-
Notifications
You must be signed in to change notification settings - Fork 629
Add support for ED25519ph for sign/verify(-blob) commands #3479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7f0717d
260978c
1046eea
e01c68c
83c0cb5
0ae10be
7a48924
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,10 @@ package sign | |
|
||
import ( | ||
"context" | ||
"crypto" | ||
"crypto/ecdsa" | ||
"crypto/ed25519" | ||
"crypto/rsa" | ||
"crypto/sha256" | ||
"crypto/x509" | ||
"encoding/base64" | ||
|
@@ -39,9 +43,28 @@ 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" | ||
) | ||
|
||
func getHashAlgorithmFromSignerVerifier(sv *SignerVerifier) (crypto.Hash, error) { | ||
publicKey, err := sv.SignerVerifier.PublicKey() | ||
if err != nil { | ||
return crypto.Hash(0), err | ||
} | ||
|
||
switch publicKey.(type) { | ||
case *ecdsa.PublicKey: | ||
return crypto.SHA256, nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe for a later PR to keep this smaller in scope - Should this be based on curve too? This would be a change from current behavior, since I believe we're using sha256 regardless of curve. This might be an unexpected change for custom verifiers though who likely have always assumed sha256. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I agree, there should be more flexibility here. |
||
case *rsa.PublicKey: | ||
return crypto.SHA256, nil | ||
case ed25519.PublicKey: | ||
return crypto.SHA512, nil | ||
default: | ||
return crypto.Hash(0), fmt.Errorf("unsupported public key type") | ||
} | ||
} | ||
Comment on lines
+50
to
+66
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this would be solved with sigstore/sigstore#860 . Shall I add a reference to this issue in the code (and back-ref)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would you be interested in taking a look at the failing test in sigstore/sigstore#1426? I think it's just a failure due to a mock not having an expectation met. We could get that merged in then to use here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't sigstore/sigstore#1426 just about the KMS SignerVerifier? I think we'd need that |
||
|
||
// nolint | ||
func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string, b64 bool, outputSignature string, outputCertificate string, tlogUpload bool) ([]byte, error) { | ||
var payload internal.HashReader | ||
|
@@ -50,27 +73,43 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string | |
ctx, cancel := context.WithTimeout(context.Background(), ro.Timeout) | ||
defer cancel() | ||
|
||
svOptions := []signature.LoadOption{ | ||
signatureoptions.WithHash(crypto.SHA256), | ||
} | ||
// Use ED25519 pre-hashed version only when uploading to tlog to maintain | ||
// backwards compatibility. When self-managed keys are used this keeps the | ||
// behavior consistent with older cosign clients, which will still be able | ||
// to verify the newer signatures. | ||
if tlogUpload { | ||
svOptions = append(svOptions, signatureoptions.WithED25519ph()) | ||
} | ||
|
||
sv, err := signerFromKeyOptsWithSVOpts(ctx, "", "", ko, svOptions...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer sv.Close() | ||
|
||
hashAlgorithm, err := getHashAlgorithmFromSignerVerifier(sv) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if payloadPath == "-" { | ||
payload = internal.NewHashReader(os.Stdin, sha256.New()) | ||
payload = internal.NewHashReader(os.Stdin, hashAlgorithm) | ||
} 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()) | ||
payload = internal.NewHashReader(f, hashAlgorithm) | ||
} | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
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 { | ||
return nil, fmt.Errorf("signing blob: %w", err) | ||
|
@@ -135,7 +174,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 | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,7 @@ import ( | |
sigs "github.com/sigstore/cosign/v2/pkg/signature" | ||
"github.com/sigstore/sigstore/pkg/cryptoutils" | ||
"github.com/sigstore/sigstore/pkg/signature" | ||
signatureoptions "github.com/sigstore/sigstore/pkg/signature/options" | ||
"github.com/sigstore/sigstore/pkg/signature/payload" | ||
) | ||
|
||
|
@@ -182,11 +183,16 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { | |
} | ||
} | ||
|
||
svOpts := []signature.LoadOption{ | ||
signatureoptions.WithHash(crypto.SHA256), | ||
signatureoptions.WithED25519ph(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need the trial verification you mentioned in the comment, verifying both as ed25519 and ed25519ph? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this stage, yes, I think that's needed. However, once we add support for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I realize now that you might be saying there is no trial verification in the PR, but this is already implemented in https://github.com/sigstore/cosign/pull/3479/files#diff-8a85c8e688d61e16b8af8e09832ed2bef89c1163b0e9601a8363c782c387c006R272-R298 . Was that what you were referring to or did I misinterpret your messages? |
||
} | ||
|
||
// Keys are optional! | ||
var pubKey signature.Verifier | ||
switch { | ||
case keyRef != "": | ||
pubKey, err = sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, c.HashAlgorithm) | ||
pubKey, err = sigs.PublicKeyFromKeyRefWithOpts(ctx, keyRef, svOpts...) | ||
if err != nil { | ||
return fmt.Errorf("loading public key: %w", err) | ||
} | ||
|
@@ -220,7 +226,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { | |
if err != nil { | ||
return fmt.Errorf("getting Fulcio intermediates: %w", err) | ||
} | ||
pubKey, err = cosign.ValidateAndUnpackCert(cert, co) | ||
pubKey, err = cosign.ValidateAndUnpackCertWithOpts(cert, co, cosign.WithSignerVerifierOptions(svOpts...)) | ||
if err != nil { | ||
return err | ||
} | ||
|
@@ -230,7 +236,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { | |
if err != nil { | ||
return err | ||
} | ||
pubKey, err = cosign.ValidateAndUnpackCertWithChain(cert, chain, co) | ||
pubKey, err = cosign.ValidateAndUnpackCertWithOpts(cert, co, cosign.WithChain(chain), cosign.WithSignerVerifierOptions(svOpts...)) | ||
if err != nil { | ||
return err | ||
} | ||
|
@@ -267,7 +273,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { | |
|
||
for _, img := range images { | ||
if c.LocalImage { | ||
verified, bundleVerified, err := cosign.VerifyLocalImageSignatures(ctx, img, co) | ||
verified, bundleVerified, err := cosign.VerifyLocalImageSignaturesWithOpts(ctx, img, co, svOpts...) | ||
if err != nil { | ||
return err | ||
} | ||
|
@@ -283,7 +289,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { | |
return fmt.Errorf("resolving attachment type %s for image %s: %w", c.Attachment, img, err) | ||
} | ||
|
||
verified, bundleVerified, err := cosign.VerifyImageSignatures(ctx, ref, co) | ||
verified, bundleVerified, err := cosign.VerifyImageSignaturesWithOpts(ctx, ref, co, svOpts...) | ||
if err != nil { | ||
return cosignError.WrapError(err) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To confirm, this would mean that self-managed ed25519 keys would now generate signatures with the pre-hashed variant, correct? This would mean that a verifier using an older Cosign version could not verify a signature from the latest Cosign version. Could we set this only when
tlogupload
(or the equivalent config) is true? This preserves the same signing behavior then for self-managed keys with sigs not being uploaded to Rekor.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented!