Skip to content

Commit

Permalink
add client method to generate TLE struct (#1498)
Browse files Browse the repository at this point in the history
* add client method to generate TLE protobuf

Signed-off-by: Bob Callaway <bcallaway@google.com>

* go mod tidy

Signed-off-by: Bob Callaway <bcallaway@google.com>

* remove CLI usage

Signed-off-by: Bob Callaway <bcallaway@google.com>

* add unit tests

Signed-off-by: Bob Callaway <bcallaway@google.com>

* fix lint issues

Signed-off-by: Bob Callaway <bcallaway@google.com>

* add back CLI output option for rekor-cli get

Signed-off-by: Bob Callaway <bcallaway@google.com>

* fix func comment

Signed-off-by: Bob Callaway <bcallaway@google.com>

---------

Signed-off-by: Bob Callaway <bcallaway@google.com>
  • Loading branch information
bobcallaway authored May 25, 2023
1 parent 161a796 commit 795a236
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 1 deletion.
13 changes: 13 additions & 0 deletions cmd/rekor-cli/app/format/wrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import (
"encoding/json"
"fmt"

rekor_pb "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1"

"github.com/sigstore/rekor/pkg/client"
"github.com/sigstore/rekor/pkg/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -46,6 +49,16 @@ func WrapCmd(f formatCmd) CobraCmd {
}
case "json":
fmt.Println(toJSON(obj))
case "tle":
if tle, ok := obj.(*rekor_pb.TransparencyLogEntry); ok {
json, err := client.MarshalTLEToJSON(tle)
if err != nil {
log.CliLogger.Fatalf("error converting to transparency log entry: %v", err)
}
fmt.Println(string(json))
} else {
log.CliLogger.Fatal("unable to print transparency log entry")
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions cmd/rekor-cli/app/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ func compareEntryUUIDs(requestEntryUUID string, responseEntryUUID string) error
}

func parseEntry(uuid string, e models.LogEntryAnon) (interface{}, error) {
if viper.GetString("format") == "tle" {
return client.GenerateTransparencyLogEntry(e)
}

b, err := base64.StdEncoding.DecodeString(e.Body.(string))
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion cmd/rekor-cli/app/pflags.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func initializePFlagMap() {
},
formatFlag: func() pflag.Value {
// this validates the output format requested
return valueFactory(formatFlag, validateString("required,oneof=json default"), "")
return valueFactory(formatFlag, validateString("required,oneof=json default tle"), "")
},
timeoutFlag: func() pflag.Value {
// this validates the timeout is >= 0
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ require (
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-retryablehttp v0.7.2
github.com/redis/go-redis/v9 v9.0.4
github.com/sigstore/protobuf-specs v0.1.0
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1845,6 +1845,8 @@ github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/sigstore/protobuf-specs v0.1.0 h1:X0l/E2C2c79t/rI/lmSu8WAoKWsQtMqDzAMiDdEMGr8=
github.com/sigstore/protobuf-specs v0.1.0/go.mod h1:5shUCxf82hGnjUEFVWiktcxwzdtn6EfeeJssxZ5Q5HE=
github.com/sigstore/sigstore v1.6.4 h1:jH4AzR7qlEH/EWzm+opSpxCfuUcjHL+LJPuQE7h40WE=
github.com/sigstore/sigstore v1.6.4/go.mod h1:pjR64lBxnjoSrAr+Ydye/FV73IfrgtoYlAI11a8xMfA=
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
Expand Down
76 changes: 76 additions & 0 deletions pkg/client/rekor_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
package client

import (
"bytes"
"crypto/tls"
"encoding/base64"
"encoding/hex"
"fmt"
"net/http"
"net/url"

Expand All @@ -24,8 +28,13 @@ import (
"github.com/go-openapi/strfmt"
"github.com/hashicorp/go-cleanhttp"
retryablehttp "github.com/hashicorp/go-retryablehttp"
rekor_pb_common "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
rekor_pb "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1"
"github.com/sigstore/rekor/pkg/generated/client"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/rekor/pkg/util"
"google.golang.org/protobuf/encoding/protojson"
)

func GetRekorClient(rekorServerURL string, opts ...Option) (*client.Rekor, error) {
Expand Down Expand Up @@ -64,3 +73,70 @@ func GetRekorClient(rekorServerURL string, opts ...Option) (*client.Rekor, error
registry.Add("signedCheckpoint", &util.SignedNote{}, util.SignedCheckpointValidator)
return client.New(rt, registry), nil
}

// GenerateTransparencyLogEntry returns a sigstore/protobuf-specs compliant message containing a
// TransparencyLogEntry as defined at https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_rekor.proto
func GenerateTransparencyLogEntry(anon models.LogEntryAnon) (*rekor_pb.TransparencyLogEntry, error) {
logIDHash, err := hex.DecodeString(*anon.LogID)
if err != nil {
return nil, fmt.Errorf("decoding logID string: %w", err)
}

rootHash, err := hex.DecodeString(*anon.Verification.InclusionProof.RootHash)
if err != nil {
return nil, fmt.Errorf("decoding inclusion proof root hash: %w", err)
}

inclusionProofHashes := make([][]byte, len(anon.Verification.InclusionProof.Hashes))
for i, hash := range anon.Verification.InclusionProof.Hashes {
hashBytes, err := hex.DecodeString(hash)
if err != nil {
return nil, fmt.Errorf("decoding inclusion proof hash: %w", err)
}
inclusionProofHashes[i] = hashBytes
}

b, err := base64.StdEncoding.DecodeString(anon.Body.(string))
if err != nil {
return nil, fmt.Errorf("base64 decoding body: %w", err)
}

pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer())
if err != nil {
return nil, err
}
eimpl, err := types.UnmarshalEntry(pe)
if err != nil {
return nil, err
}

return &rekor_pb.TransparencyLogEntry{
LogIndex: *anon.LogIndex,
LogId: &rekor_pb_common.LogId{
KeyId: logIDHash,
},
KindVersion: &rekor_pb.KindVersion{
Kind: pe.Kind(),
Version: eimpl.APIVersion(),
},
IntegratedTime: *anon.IntegratedTime,
InclusionPromise: &rekor_pb.InclusionPromise{
SignedEntryTimestamp: anon.Verification.SignedEntryTimestamp,
},
InclusionProof: &rekor_pb.InclusionProof{
LogIndex: *anon.LogIndex,
RootHash: rootHash,
TreeSize: *anon.Verification.InclusionProof.TreeSize,
Hashes: inclusionProofHashes,
Checkpoint: &rekor_pb.Checkpoint{
Envelope: *anon.Verification.InclusionProof.Checkpoint,
},
},
CanonicalizedBody: b, // we don't call eimpl.Canonicalize in the case that the logic is different in this caller vs when it was persisted in the log
}, nil
}

// MarshalTLEToJSON marshals a TransparencyLogEntry message to JSON according to the protobuf JSON encoding rules
func MarshalTLEToJSON(tle *rekor_pb.TransparencyLogEntry) ([]byte, error) {
return protojson.Marshal(tle)
}
Loading

0 comments on commit 795a236

Please sign in to comment.