Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b1c0f19
Remove SHA256 assumption in sign-blob/verify-blob
ret2libc Mar 4, 2025
180d536
Use Ed25519ph by default
ret2libc Mar 5, 2025
bb0a4bd
tests: add e2e test for sign-blob/verify-blob with non-default keys
ret2libc Mar 5, 2025
260444c
Update sigstore/sigstore to v1.9.1
ret2libc Mar 12, 2025
45f0289
REMOVE ME: Update to trail-of-forks sigstore-go
ret2libc Mar 12, 2025
2040109
fix tests
ret2libc Mar 12, 2025
00d769a
fix e2e tests
ret2libc Mar 12, 2025
acfa016
Use PureED25519 if no tlog upload
ret2libc Mar 13, 2025
8234ba5
fulcio: add comment about adapter
ret2libc Mar 13, 2025
1bc2269
test/e2e_test: use rekorUrl/fulcioUrl
ret2libc Mar 13, 2025
1f86890
REMOVE ME: update sigsto-go
ret2libc Apr 2, 2025
ab1b104
Merge remote-tracking branch 'origin/main' into use-load-options
ret2libc Apr 2, 2025
2961ce1
REMOVE ME: update sigstore-go
ret2libc Apr 2, 2025
dd4c926
test: try to fix e2e tests
ret2libc Apr 4, 2025
1b82e30
test/e2e_test: correctly name subtests
ret2libc Apr 4, 2025
3a8c7e2
fix e2e tests
ret2libc Apr 7, 2025
d6ef62b
fix lint
ret2libc Apr 7, 2025
208c3d5
fix lint 2
ret2libc Apr 7, 2025
c9ec1b7
extract GetDefaultLoadOptions function
ret2libc Apr 8, 2025
b7ab2fb
Merge remote-tracking branch 'origin/main' into use-load-options
ret2libc Apr 8, 2025
f0dd4d1
Merge remote-tracking branch 'origin/main' into use-load-options
ret2libc Jul 1, 2025
b4ea782
add a confirmation msg for old bundle format
ret2libc Jul 1, 2025
590c216
Merge remote-tracking branch 'origin/main' into use-load-options
ret2libc Aug 26, 2025
126a039
fix merging, payload needs to be initialized earlier
ret2libc Aug 27, 2025
c4f824e
Provide a default SHA256 payload for signingConfig payload
ret2libc Aug 28, 2025
3fb2bb7
fix lint
ret2libc Aug 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Remove SHA256 assumption in sign-blob/verify-blob
Signed-off-by: Riccardo Schirone <riccardo.schirone@trailofbits.com>
  • Loading branch information
ret2libc committed Mar 13, 2025
commit b1c0f19caa9a432efcc3d1e63b2cec69292d3b8c
6 changes: 5 additions & 1 deletion cmd/cosign/cli/fulcio/fulcio.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ type Signer struct {
}

func NewSigner(ctx context.Context, ko options.KeyOpts, signer signature.SignerVerifier) (*Signer, error) {
return NewSignerWithAdapter(ctx, ko, signer, signer)
}

func NewSignerWithAdapter(ctx context.Context, ko options.KeyOpts, signer signature.SignerVerifier, fulcioSigner signature.SignerVerifier) (*Signer, error) {
Comment thread
ret2libc marked this conversation as resolved.
fClient, err := NewClient(ko.FulcioURL)
if err != nil {
return nil, fmt.Errorf("creating Fulcio client: %w", err)
Expand Down Expand Up @@ -167,7 +171,7 @@ func NewSigner(ctx context.Context, ko options.KeyOpts, signer signature.SignerV
}
flow = flowNormal
}
Resp, err := GetCert(ctx, signer, idToken, flow, ko.OIDCIssuer, ko.OIDCClientID, ko.OIDCClientSecret, ko.OIDCRedirectURL, fClient) // TODO, use the chain.
Resp, err := GetCert(ctx, fulcioSigner, idToken, flow, ko.OIDCIssuer, ko.OIDCClientID, ko.OIDCClientSecret, ko.OIDCRedirectURL, fClient) // TODO, use the chain.
if err != nil {
return nil, fmt.Errorf("retrieving cert: %w", err)
}
Expand Down
6 changes: 5 additions & 1 deletion cmd/cosign/cli/fulcio/fulcioverifier/fulcioverifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ import (
)

func NewSigner(ctx context.Context, ko options.KeyOpts, signer signature.SignerVerifier) (*fulcio.Signer, error) {
fs, err := fulcio.NewSigner(ctx, ko, signer)
return NewSignerWithAdapter(ctx, ko, signer, signer)
}

func NewSignerWithAdapter(ctx context.Context, ko options.KeyOpts, signer signature.SignerVerifier, fulcioSigner signature.SignerVerifier) (*fulcio.Signer, error) {
fs, err := fulcio.NewSignerWithAdapter(ctx, ko, signer, fulcioSigner)
if err != nil {
return nil, err
}
Expand Down
31 changes: 29 additions & 2 deletions cmd/cosign/cli/sign/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,18 +536,45 @@ func signerFromNewKey() (*SignerVerifier, error) {
}, nil
}

// adaptSignerVerifierToFulcio adapts, if necessary, the SignerVerifier to be
// used to interact with Fulcio.
//
// This is needed in particular for ED25519 keys with the pre-hashed version of
// the algorithm, which is not supported by Fulcio. This function creates a
// ED25519 SignerVerifier based on that instead.
func adaptSignerVerifierToFulcio(sv *SignerVerifier) (*SignerVerifier, error) {
Comment thread
ret2libc marked this conversation as resolved.
if ed25519phSV, ok := sv.SignerVerifier.(*signature.ED25519phSignerVerifier); ok {
signerVerifier, err := ed25519phSV.ToED25519SignerVerifier()
if err != nil {
return nil, err
}

return &SignerVerifier{
SignerVerifier: signerVerifier,
Cert: sv.Cert,
Chain: sv.Chain,
}, nil
}
return sv, nil
}

func keylessSigner(ctx context.Context, ko options.KeyOpts, sv *SignerVerifier) (*SignerVerifier, error) {
var (
k *fulcio.Signer
err error
)

fulcioSV, err := adaptSignerVerifierToFulcio(sv)
if err != nil {
return nil, fmt.Errorf("adapting signer verifier to Fulcio: %w", err)
}

if ko.InsecureSkipFulcioVerify {
if k, err = fulcio.NewSigner(ctx, ko, sv); err != nil {
if k, err = fulcio.NewSignerWithAdapter(ctx, ko, sv, fulcioSV); err != nil {
return nil, fmt.Errorf("getting key from Fulcio: %w", err)
}
} else {
if k, err = fulcioverifier.NewSigner(ctx, ko, sv); err != nil {
if k, err = fulcioverifier.NewSignerWithAdapter(ctx, ko, sv, fulcioSV); err != nil {
return nil, fmt.Errorf("getting key from Fulcio: %w", err)
}
}
Expand Down
54 changes: 44 additions & 10 deletions cmd/cosign/cli/sign/sign_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package sign

import (
"context"
"crypto"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
Expand All @@ -39,6 +40,7 @@ import (
protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
"github.com/sigstore/rekor/pkg/generated/models"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"github.com/sigstore/sigstore/pkg/signature"
signatureoptions "github.com/sigstore/sigstore/pkg/signature/options"
)

Expand All @@ -50,26 +52,31 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string
ctx, cancel := context.WithTimeout(context.Background(), ro.Timeout)
defer cancel()

sv, err := SignerFromKeyOpts(ctx, "", "", ko)
if err != nil {
return nil, err
}
defer sv.Close()

hashFunction, err := getHashFunction(sv)
if err != nil {
return nil, err
}

if payloadPath == "-" {
Comment thread
ret2libc marked this conversation as resolved.
Outdated
payload = internal.NewHashReader(os.Stdin, sha256.New())
payload = internal.NewHashReader(os.Stdin, hashFunction)
} else {
ui.Infof(ctx, "Using payload from: %s", payloadPath)
f, err := os.Open(filepath.Clean(payloadPath))
defer f.Close()
if err != nil {
return nil, err
}
payload = internal.NewHashReader(f, sha256.New())
}
if err != nil {
return nil, err
payload = internal.NewHashReader(f, hashFunction)
}

sv, err := SignerFromKeyOpts(ctx, "", "", ko)
if err != nil {
return nil, err
}
defer sv.Close()

sig, err := sv.SignMessage(&payload, signatureoptions.WithContext(ctx))
if err != nil {
Expand Down Expand Up @@ -135,7 +142,7 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string
if err != nil {
return nil, err
}
rekorEntry, err = cosign.TLogUpload(ctx, rekorClient, sig, &payload, rekorBytes)
rekorEntry, err = cosign.TLogUploadWithCustomHash(ctx, rekorClient, sig, &payload, rekorBytes)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -179,7 +186,7 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string
bundle.Content = &protobundle.Bundle_MessageSignature{
MessageSignature: &protocommon.MessageSignature{
MessageDigest: &protocommon.HashOutput{
Algorithm: protocommon.HashAlgorithm_SHA2_256,
Algorithm: hashFuncToProtoBundle(payload.HashFunc()),
Digest: digest,
},
Signature: sig,
Expand Down Expand Up @@ -263,3 +270,30 @@ func extractCertificate(ctx context.Context, sv *SignerVerifier) ([]byte, error)
}
return nil, nil
}

func getHashFunction(sv *SignerVerifier) (crypto.Hash, error) {
pubKey, err := sv.PublicKey()
if err != nil {
return crypto.Hash(0), fmt.Errorf("error getting public key: %w", err)
}

// TODO: Ideally the SignerVerifier should have a method to get the hash function
algo, err := signature.GetDefaultAlgorithmDetails(pubKey)
if err != nil {
return crypto.Hash(0), fmt.Errorf("error getting default algorithm details: %w", err)
}
return algo.GetHashType(), nil
}

func hashFuncToProtoBundle(hashFunc crypto.Hash) protocommon.HashAlgorithm {
switch hashFunc {
case crypto.SHA256:
return protocommon.HashAlgorithm_SHA2_256
case crypto.SHA384:
return protocommon.HashAlgorithm_SHA2_384
case crypto.SHA512:
return protocommon.HashAlgorithm_SHA2_512
default:
return protocommon.HashAlgorithm_HASH_ALGORITHM_UNSPECIFIED
}
}
3 changes: 1 addition & 2 deletions cmd/cosign/cli/verify/verify_blob_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package verify
import (
"context"
"crypto"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
Expand Down Expand Up @@ -169,7 +168,7 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st
return err
}

payload = internal.NewHashReader(f, sha256.New())
payload = internal.NewHashReader(f, crypto.SHA256)
if _, err := io.ReadAll(&payload); err != nil {
return err
}
Expand Down
17 changes: 12 additions & 5 deletions internal/pkg/cosign/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package cosign

import (
"crypto"
"errors"
"hash"
"io"
Expand All @@ -39,14 +40,17 @@ func FileExists(filename string) (bool, error) {

// HashReader hashes while it reads.
type HashReader struct {
r io.Reader
h hash.Hash
r io.Reader
h hash.Hash
ch crypto.Hash
}

func NewHashReader(r io.Reader, h hash.Hash) HashReader {
func NewHashReader(r io.Reader, ch crypto.Hash) HashReader {
h := ch.New()
return HashReader{
r: io.TeeReader(r, h),
h: h,
r: io.TeeReader(r, h),
h: h,
ch: ch,
}
}

Expand All @@ -67,3 +71,6 @@ func (h *HashReader) BlockSize() int { return h.h.BlockSize() }

// Write implements hash.Hash
func (h *HashReader) Write(p []byte) (int, error) { return 0, errors.New("not implemented") } //nolint: revive

// HashFunc implements cosign.NamedHash
func (h *HashReader) HashFunc() crypto.Hash { return h.ch }
11 changes: 1 addition & 10 deletions pkg/cosign/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,14 +227,5 @@ func LoadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) {
if err != nil {
return nil, fmt.Errorf("parsing private key: %w", err)
}
switch pk := pk.(type) {
case *rsa.PrivateKey:
return signature.LoadRSAPKCS1v15SignerVerifier(pk, crypto.SHA256)
case *ecdsa.PrivateKey:
return signature.LoadECDSASignerVerifier(pk, crypto.SHA256)
case ed25519.PrivateKey:
return signature.LoadED25519SignerVerifier(pk)
default:
return nil, errors.New("unsupported key type")
}
return signature.LoadSignerVerifierFromPrivateKey(pk)
}
63 changes: 55 additions & 8 deletions pkg/cosign/tlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,36 @@ import (
// This is the rekor transparency log public key target name
var rekorTargetStr = `rekor.pub`

type NamedHash interface {
hash.Hash
crypto.SignerOpts
}

type CryptoNamedHash struct {
hash.Hash
hashType crypto.Hash
}

func (h CryptoNamedHash) HashFunc() crypto.Hash {
return h.hashType
}

func NewCryptoNamedHash(hashType crypto.Hash) NamedHash {
return CryptoNamedHash{Hash: hashType.New(), hashType: hashType}
}

type SHA256NamedHash struct {
hash.Hash
}

func (h SHA256NamedHash) HashFunc() crypto.Hash {
return crypto.SHA256
}

func WrapSHA256Hash(hash hash.Hash) NamedHash {
return SHA256NamedHash{Hash: hash}
}

// TransparencyLogPubKey contains the ECDSA verification key and the current status
// of the key according to TUF metadata, whether it's active or expired.
type TransparencyLogPubKey struct {
Expand Down Expand Up @@ -170,7 +200,14 @@ func rekorPubsFromClient(rekorClient *client.Rekor) (*TrustedTransparencyLogPubK

// TLogUpload will upload the signature, public key and payload to the transparency log.
func TLogUpload(ctx context.Context, rekorClient *client.Rekor, signature []byte, sha256CheckSum hash.Hash, pemBytes []byte) (*models.LogEntryAnon, error) {
re := rekorEntry(sha256CheckSum, signature, pemBytes)
cryptoChecksum := WrapSHA256Hash(sha256CheckSum)
return TLogUploadWithCustomHash(ctx, rekorClient, signature, cryptoChecksum, pemBytes)
}

// TLogUploadWithCustomHash will upload the signature, public key and payload to
// the transparency log. Clients can use this to specify a custom hash function.
func TLogUploadWithCustomHash(ctx context.Context, rekorClient *client.Rekor, signature []byte, checksum NamedHash, pemBytes []byte) (*models.LogEntryAnon, error) {
re := rekorEntry(checksum, signature, pemBytes)
returnVal := models.Hashedrekord{
APIVersion: swag.String(re.APIVersion()),
Spec: re.HashedRekordObj,
Expand Down Expand Up @@ -229,16 +266,26 @@ func doUpload(ctx context.Context, rekorClient *client.Rekor, pe models.Proposed
return nil, errors.New("bad response from server")
}

func rekorEntry(sha256CheckSum hash.Hash, signature, pubKey []byte) hashedrekord_v001.V001Entry {
// TODO: Signatures created on a digest using a hash algorithm other than SHA256 will fail
// upload right now. Plumb information on the hash algorithm used when signing from the
// SignerVerifier to use for the HashedRekordObj.Data.Hash.Algorithm.
func rekorEntryHashAlgorithm(checksum crypto.SignerOpts) string {
switch checksum.HashFunc() {
case crypto.SHA256:
return models.HashedrekordV001SchemaDataHashAlgorithmSha256
case crypto.SHA384:
return models.HashedrekordV001SchemaDataHashAlgorithmSha384
case crypto.SHA512:
return models.HashedrekordV001SchemaDataHashAlgorithmSha512
default:
return models.HashedrekordV001SchemaDataHashAlgorithmSha256
}
}

func rekorEntry(checksum NamedHash, signature, pubKey []byte) hashedrekord_v001.V001Entry {
return hashedrekord_v001.V001Entry{
HashedRekordObj: models.HashedrekordV001Schema{
Data: &models.HashedrekordV001SchemaData{
Hash: &models.HashedrekordV001SchemaDataHash{
Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256),
Value: swag.String(hex.EncodeToString(sha256CheckSum.Sum(nil))),
Algorithm: swag.String(rekorEntryHashAlgorithm(checksum)),
Value: swag.String(hex.EncodeToString(checksum.Sum(nil))),
},
},
Signature: &models.HashedrekordV001SchemaSignature{
Expand Down Expand Up @@ -391,7 +438,7 @@ func proposedEntries(b64Sig string, payload, pubKey []byte) ([]models.ProposedEn
}
proposedEntry = []models.ProposedEntry{dsseEntry, intotoEntry}
} else {
sha256CheckSum := sha256.New()
sha256CheckSum := NewCryptoNamedHash(crypto.SHA256)
if _, err := sha256CheckSum.Write(payload); err != nil {
return nil, err
}
Expand Down