Skip to content

Commit

Permalink
Add ability for verify-blob to find signing cert in transparency log (#…
Browse files Browse the repository at this point in the history
…991)

- Uses the first tlog entry it finds
- Verifies provided cert / public key. If one isn't provided by the
user, it will find it in the tlog (if experimental feature flag is
enabled)

Authored-by: Dennis Leon <[email protected]>
Signed-off-by: Dennis Leon <[email protected]>
  • Loading branch information
DennisDenuto authored Nov 3, 2021
1 parent 6573dcd commit 5deaca0
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 26 deletions.
122 changes: 99 additions & 23 deletions cmd/cosign/cli/verify/verify_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import (
"io"
"os"

"github.com/go-openapi/runtime"
"github.com/pkg/errors"

"github.com/sigstore/cosign/cmd/cosign/cli/fulcio"
"github.com/sigstore/cosign/cmd/cosign/cli/options"
"github.com/sigstore/cosign/cmd/cosign/cli/sign"
Expand All @@ -37,6 +37,9 @@ import (
"github.com/sigstore/cosign/pkg/cosign/pivkey"
sigs "github.com/sigstore/cosign/pkg/signature"
rekorClient "github.com/sigstore/rekor/pkg/client"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/rekor/pkg/types"
rekord "github.com/sigstore/rekor/pkg/types/rekord/v0.0.1"
"github.com/sigstore/sigstore/pkg/cryptoutils"
sigstoresigs "github.com/sigstore/sigstore/pkg/signature"
signatureoptions "github.com/sigstore/sigstore/pkg/signature/options"
Expand All @@ -53,10 +56,36 @@ func VerifyBlobCmd(ctx context.Context, ko sign.KeyOpts, certRef, sigRef, blobRe
var err error
var cert *x509.Certificate

if !options.OneOf(ko.KeyRef, ko.Sk, certRef) {
if !options.OneOf(ko.KeyRef, ko.Sk, certRef) && !options.EnableExperimental() {
return &options.PubKeyParseError{}
}

var b64sig string
targetSig, err := blob.LoadFileOrURL(sigRef)
if err != nil {
if !os.IsNotExist(err) {
// ignore if file does not exist, it can be a base64 encoded string as well
return err
}
targetSig = []byte(sigRef)
}

if isb64(targetSig) {
b64sig = string(targetSig)
} else {
b64sig = base64.StdEncoding.EncodeToString(targetSig)
}

var blobBytes []byte
if blobRef == "-" {
blobBytes, err = io.ReadAll(os.Stdin)
} else {
blobBytes, err = blob.LoadFileOrURL(blobRef)
}
if err != nil {
return err
}

// Keys are optional!
switch {
case ko.KeyRef != "":
Expand Down Expand Up @@ -92,32 +121,35 @@ func VerifyBlobCmd(ctx context.Context, ko sign.KeyOpts, certRef, sigRef, blobRe
if err != nil {
return err
}
}
case options.EnableExperimental():
rClient, err := rekorClient.GetRekorClient(ko.RekorURL)
if err != nil {
return err
}

var b64sig string
targetSig, err := blob.LoadFileOrURL(sigRef)
if err != nil {
if !os.IsNotExist(err) {
// ignore if file does not exist, it can be a base64 encoded string as well
uuids, err := cosign.FindTLogEntriesByPayload(rClient, blobBytes)
if err != nil {
return err
}
targetSig = []byte(sigRef)
}

if isb64(targetSig) {
b64sig = string(targetSig)
} else {
b64sig = base64.StdEncoding.EncodeToString(targetSig)
}
if len(uuids) == 0 {
return errors.New("could not find a tlog entry for provided blob")
}

var blobBytes []byte
if blobRef == "-" {
blobBytes, err = io.ReadAll(os.Stdin)
} else {
blobBytes, err = blob.LoadFileOrURL(blobRef)
}
if err != nil {
return err
tlogEntry, err := cosign.GetTlogEntry(rClient, uuids[0])
if err != nil {
return err
}

certs, err := extractCerts(tlogEntry)
if err != nil {
return err
}
cert = certs[0]
pubKey, err = sigstoresigs.LoadECDSAVerifier(cert.PublicKey.(*ecdsa.PublicKey), crypto.SHA256)
if err != nil {
return err
}
}

sig, err := base64.StdEncoding.DecodeString(b64sig)
Expand Down Expand Up @@ -165,3 +197,47 @@ func VerifyBlobCmd(ctx context.Context, ko sign.KeyOpts, certRef, sigRef, blobRe

return nil
}

func extractCerts(e *models.LogEntryAnon) ([]*x509.Certificate, error) {
b, err := base64.StdEncoding.DecodeString(e.Body.(string))
if err != nil {
return nil, err
}

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

eimpl, err := types.NewEntry(pe)
if err != nil {
return nil, err
}

var v001Entry *rekord.V001Entry
var ok bool
if v001Entry, ok = eimpl.(*rekord.V001Entry); !ok {
return nil, errors.New("unexpected tlog entry type")
}

publicKeyB64, err := v001Entry.RekordObj.Signature.PublicKey.Content.MarshalText()
if err != nil {
return nil, err
}

publicKey, err := base64.StdEncoding.DecodeString(string(publicKeyB64))
if err != nil {
return nil, err
}

certs, err := cryptoutils.UnmarshalCertificatesFromPEM(publicKey)
if err != nil {
return nil, err
}

if len(certs) == 0 {
return nil, errors.New("no certs found in pem tlog")
}

return certs, err
}
21 changes: 19 additions & 2 deletions pkg/cosign/tlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package cosign
import (
"bytes"
"context"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
Expand All @@ -27,9 +28,10 @@ import (
"github.com/google/trillian/merkle/logverifier"
"github.com/google/trillian/merkle/rfc6962/hasher"
"github.com/pkg/errors"

"github.com/sigstore/cosign/pkg/cosign/tuf"
"github.com/sigstore/cosign/pkg/oci"
"github.com/sigstore/rekor/pkg/generated/client/index"

"github.com/sigstore/rekor/pkg/generated/client"
"github.com/sigstore/rekor/pkg/generated/client/entries"
"github.com/sigstore/rekor/pkg/generated/client/pubkey"
Expand Down Expand Up @@ -124,7 +126,7 @@ func rekorEntry(payload, signature, pubKey []byte) rekord_v001.V001Entry {
}
}

func getTlogEntry(rekorClient *client.Rekor, uuid string) (*models.LogEntryAnon, error) {
func GetTlogEntry(rekorClient *client.Rekor, uuid string) (*models.LogEntryAnon, error) {
params := entries.NewGetLogEntryByUUIDParams()
params.SetEntryUUID(uuid)
resp, err := rekorClient.Entries.GetLogEntryByUUID(params)
Expand Down Expand Up @@ -177,6 +179,21 @@ func FindTlogEntry(rekorClient *client.Rekor, b64Sig string, payload, pubKey []b
return uuid, *verifiedEntry.Verification.InclusionProof.LogIndex, nil
}

func FindTLogEntriesByPayload(rekorClient *client.Rekor, payload []byte) (uuids []string, err error) {
params := index.NewSearchIndexParams()
params.Query = &models.SearchIndex{}

h := sha256.New()
h.Write(payload)
params.Query.Hash = fmt.Sprintf("sha256:%s", strings.ToLower(hex.EncodeToString(h.Sum(nil))))

searchIndex, err := rekorClient.Index.SearchIndex(params)
if err != nil {
return nil, err
}
return searchIndex.GetPayload(), nil
}

func verifyTLogEntry(rekorClient *client.Rekor, uuid string) (*models.LogEntryAnon, error) {
params := entries.NewGetLogEntryByUUIDParams()
params.EntryUUID = uuid
Expand Down
2 changes: 1 addition & 1 deletion pkg/cosign/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ func Verify(ctx context.Context, signedImgRef name.Reference, accessor Accessor,
// if we have a cert, we should check expiry
// The IntegratedTime verified in VerifyTlog
if cert != nil {
e, err := getTlogEntry(rekorClient, uuid)
e, err := GetTlogEntry(rekorClient, uuid)
if err != nil {
return err
}
Expand Down

0 comments on commit 5deaca0

Please sign in to comment.