Skip to content

Commit

Permalink
fix: verify signer hash algorithm
Browse files Browse the repository at this point in the history
Work around the fact that we can't obtain the hash algorithm used by a
signature.Signer by detecting when an algorithm other than SHA256 is
used.
  • Loading branch information
tri-adam committed Dec 12, 2022
1 parent 5608577 commit 0628e41
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 42 deletions.
42 changes: 35 additions & 7 deletions pkg/integrity/dsse.go
Expand Up @@ -26,22 +26,50 @@ type dsseEncoder struct {
payloadType string
}

// signerUsesHash verifies if s signs using algorithm h.
//
// The Signer interface does not provide a way to determine the hash algorithm being used, so we
// have to work around that.
func signerUsesHash(s signature.Signer, h crypto.Hash) bool {
msg := []byte{0}

// Generate a signature using the opaque hash algorithm in s.
sig, err := s.SignMessage(bytes.NewReader(msg))
if err != nil {
return false
}

pub, err := s.PublicKey()
if err != nil {
return false
}

// Create a Verifier that uses algorithm h.
sv, err := signature.LoadVerifier(pub, h)
if err != nil {
return false
}

// Test whether we can verify the signature. If so, the signer uses the requested algorithm.
err = sv.VerifySignature(bytes.NewReader(sig), bytes.NewReader(msg))
return err == nil
}

// newDSSEEncoder returns an encoder that signs messages in DSSE format according to opts, with key
// material from ss. SHA256 is used as the hash algorithm, unless overridden by opts.
func newDSSEEncoder(ss []signature.Signer, opts ...signature.SignOption) (*dsseEncoder, error) {
var so crypto.SignerOpts
var so crypto.SignerOpts = crypto.SHA256
for _, opt := range opts {
opt.ApplyCryptoSignerOpts(&so)
}

// If SignerOpts not explicitly supplied, set default hash algorithm.
if so == nil {
so = crypto.SHA256
opts = append(opts, options.WithCryptoSignerOpts(so))
}

dss := make([]dsse.SignVerifier, 0, len(ss))
for _, s := range ss {
// Verify signer uses SHA256 to ensure default hash algorithm is SHA256.
if !signerUsesHash(s, crypto.SHA256) {
return nil, errHashUnsupported
}

ds, err := newDSSESigner(s, opts...)
if err != nil {
return nil, err
Expand Down
52 changes: 19 additions & 33 deletions pkg/integrity/dsse_test.go
Expand Up @@ -25,10 +25,16 @@ func Test_dsseEncoder_signMessage(t *testing.T) {
tests := []struct {
name string
signers []signature.Signer
signOpts []signature.SignOption
wantErr bool
wantErr error
wantHash crypto.Hash
}{
{
name: "HashUnsupported",
signers: []signature.Signer{
getTestSigner(t, "rsa-private.pem", crypto.SHA384),
},
wantErr: errHashUnsupported,
},
{
name: "Multi_SHA256",
signers: []signature.Signer{
Expand All @@ -51,44 +57,24 @@ func Test_dsseEncoder_signMessage(t *testing.T) {
},
wantHash: crypto.SHA256,
},
{
name: "RSA_SHA384",
signers: []signature.Signer{
getTestSigner(t, "rsa-private.pem", crypto.SHA384),
},
signOpts: []signature.SignOption{
options.WithCryptoSignerOpts(crypto.SHA384),
},
wantHash: crypto.SHA384,
},
{
name: "RSA_SHA512",
signers: []signature.Signer{
getTestSigner(t, "rsa-private.pem", crypto.SHA512),
},
signOpts: []signature.SignOption{
options.WithCryptoSignerOpts(crypto.SHA512),
},
wantHash: crypto.SHA512,
},
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
b := bytes.Buffer{}

en, err := newDSSEEncoder(tt.signers, tt.signOpts...)
if err != nil {
t.Fatal(err)
}

ht, err := en.signMessage(&b, strings.NewReader(testMessage))
if got, want := err, tt.wantErr; (got != nil) != want {
en, err := newDSSEEncoder(tt.signers)
if got, want := err, tt.wantErr; !errors.Is(got, want) {
t.Fatalf("got error %v, wantErr %v", got, want)
}

if err == nil {
ht, err := en.signMessage(&b, strings.NewReader(testMessage))
if err != nil {
t.Error(err)
}

if got, want := ht, tt.wantHash; got != want {
t.Errorf("got hash %v, want %v", got, want)
}
Expand Down Expand Up @@ -297,10 +283,10 @@ func Test_dsseDecoder_verifyMessage(t *testing.T) {
{
name: "RSA_SHA384",
signers: []signature.Signer{
getTestSigner(t, "rsa-private.pem", crypto.SHA384),
getTestSigner(t, "rsa-private.pem", crypto.SHA256),
},
signOpts: []signature.SignOption{
options.WithCryptoSignerOpts(crypto.SHA384),
options.WithCryptoSignerOpts(crypto.SHA384), // Override hash.
},
de: newDSSEDecoder(
getTestVerifier(t, "rsa-public.pem", crypto.SHA384),
Expand All @@ -313,10 +299,10 @@ func Test_dsseDecoder_verifyMessage(t *testing.T) {
{
name: "RSA_SHA512",
signers: []signature.Signer{
getTestSigner(t, "rsa-private.pem", crypto.SHA512),
getTestSigner(t, "rsa-private.pem", crypto.SHA256),
},
signOpts: []signature.SignOption{
options.WithCryptoSignerOpts(crypto.SHA512),
options.WithCryptoSignerOpts(crypto.SHA512), // Override hash.
},
de: newDSSEDecoder(
getTestVerifier(t, "rsa-public.pem", crypto.SHA512),
Expand Down

This file was deleted.

This file was deleted.

0 comments on commit 0628e41

Please sign in to comment.