diff --git a/internal/bsr/internal/sign/sign_test.go b/internal/bsr/internal/sign/sign_test.go index efa50fb97..134bcc5d6 100644 --- a/internal/bsr/internal/sign/sign_test.go +++ b/internal/bsr/internal/sign/sign_test.go @@ -177,6 +177,10 @@ func TestFile(t *testing.T) { err = proto.Unmarshal(tc.sig.Bytes(), sigInfo) require.NoError(t, err) + verified, err := keys.VerifySignatureWithPubKey(ctx, sigInfo, []byte(tc.write)) + require.NoError(t, err) + require.True(t, verified) + want, err := keys.SignWithPrivKey(ctx, []byte(tc.write)) require.NoError(t, err) assert.Empty(t, diff --git a/internal/bsr/kms/keys.go b/internal/bsr/kms/keys.go index b07b6bf61..ae05ae5e4 100644 --- a/internal/bsr/kms/keys.go +++ b/internal/bsr/kms/keys.go @@ -325,8 +325,6 @@ func (k *Keys) VerifyPubKeySelfSignature(ctx context.Context) (bool, error) { if k == nil { return false, fmt.Errorf("%s: bsr keys: %w", op, ErrInvalidParameter) } - k.l.RLock() - defer k.l.RUnlock() switch { case k.PubKey == nil: @@ -345,16 +343,7 @@ func (k *Keys) VerifyPubKeySelfSignature(ctx context.Context) (bool, error) { return false, fmt.Errorf("%s: pub self signature key id %q doesn't match verifying pub key id %q: %w", op, k.PubKeySelfSignature.KeyInfo.KeyId, k.PubKey.KeyId, ErrInvalidParameter) } - verifier, err := wrappingEd.NewVerifier(ctx, wrappingEd.WithPubKey(k.PubKey.Key)) - if err != nil { - return false, fmt.Errorf("%s: %w", op, err) - } - - ok, err := verifier.Verify(ctx, k.PubKey.Key, k.PubKeySelfSignature) - if err != nil { - return false, fmt.Errorf("%s: %w", op, err) - } - return ok, nil + return k.VerifySignatureWithPubKey(ctx, k.PubKeySelfSignature, k.PubKey.Key) } // SignWithPrivKey will sign the msg with the BsrKeys.PrivKey (aka the BSR's @@ -402,6 +391,46 @@ func (k *Keys) SignWithPrivKey(ctx context.Context, msg []byte) (*wrapping.SigIn return si, nil } +// VerifySignatureWithPubKey will verify a signature using the k.PubKey. +// +// This is a concurrently safe operation. +func (k *Keys) VerifySignatureWithPubKey(ctx context.Context, sig *wrapping.SigInfo, msg []byte) (bool, error) { + const op = "kms.(BsrKeys).VerifySignatureWithPubKey" + if k == nil { + return false, fmt.Errorf("%s: bsr keys: %w", op, ErrInvalidParameter) + } + k.l.RLock() + defer k.l.RUnlock() + + switch { + case k.PubKey == nil: + return false, fmt.Errorf("%s: missing pub key: %w", op, ErrInvalidParameter) + case k.PubKey.Key == nil: + return false, fmt.Errorf("%s: missing pub key bytes: %w", op, ErrInvalidParameter) + case k.PubKey.KeyType != wrapping.KeyType_Ed25519: + return false, keyTypeError(ctx, op, wrapping.KeyType_Ed25519, k.PubKey.KeyType) + case k.PubKey.KeyEncoding != wrapping.KeyEncoding_Bytes: + return false, keyEncodingError(ctx, op, wrapping.KeyEncoding_Bytes, k.PubKey.KeyEncoding) + case len(k.PubKey.Key) != ed25519.PublicKeySize: + return false, fmt.Errorf("%s: expected public key with %d bytes and got %d: %w", op, ed25519.PublicKeySize, len(k.PubKey.Key), ErrInvalidParameter) + case sig == nil: + return false, fmt.Errorf("%s: missing signature info: %w", op, ErrInvalidParameter) + case msg == nil: + return false, fmt.Errorf("%s: missing message: %w", op, ErrInvalidParameter) + } + + verifier, err := wrappingEd.NewVerifier(ctx, wrappingEd.WithPubKey(k.PubKey.Key)) + if err != nil { + return false, fmt.Errorf("%s: %w", op, err) + } + + ok, err := verifier.Verify(ctx, msg, sig) + if err != nil { + return false, fmt.Errorf("%s: %w", op, err) + } + return ok, nil +} + func (k *Keys) VerifySignatureWithBsrKey(ctx context.Context, sig *wrapping.SigInfo, msg []byte, opt ...Option) (bool, error) { const op = "kms.(BsrKeys).VerifySignatureWithBsrKey" if k == nil { diff --git a/internal/bsr/kms/keys_test.go b/internal/bsr/kms/keys_test.go index 5b797f40e..4865248fe 100644 --- a/internal/bsr/kms/keys_test.go +++ b/internal/bsr/kms/keys_test.go @@ -1206,6 +1206,133 @@ func TestBsrKeys_VerifyPubKeySelfSignature(t *testing.T) { } } +func TestBsrKeys_VerifySignatureWithPubKey(t *testing.T) { + t.Parallel() + testCtx := context.Background() + + keys, err := kms.CreateKeys(testCtx, kms.TestWrapper(t), "session") + require.NoError(t, err) + + message := "this is a super secret message!" + sig, err := keys.SignWithPrivKey(testCtx, []byte(message)) + require.NoError(t, err) + require.NotNil(t, sig) + badMessage := "bad message###oh nooo" + + tests := []struct { + name string + bsrKeys *kms.Keys + verifyMessage string + opt []kms.Option + want bool + wantErr bool + wantErrMatch error + wantErrContains string + }{ + { + name: "success", + bsrKeys: keys, + verifyMessage: message, + want: true, + }, + { + name: "message-not-verified", + bsrKeys: keys, + verifyMessage: badMessage, + want: false, + }, + { + name: "missing-pub-key", + bsrKeys: &kms.Keys{ + PubKeySelfSignature: keys.PubKeySelfSignature, + }, + wantErr: true, + wantErrMatch: kms.ErrInvalidParameter, + wantErrContains: "missing pub key", + }, + + { + name: "missing-pub-key-bytes", + bsrKeys: &kms.Keys{ + PubKey: &wrapping.KeyInfo{ + KeyId: keys.PubKey.KeyId, + KeyType: keys.PubKey.KeyType, + KeyEncoding: keys.PubKey.KeyEncoding, + }, + PubKeySelfSignature: keys.PubKeySelfSignature, + }, + wantErr: true, + wantErrMatch: kms.ErrInvalidParameter, + wantErrContains: "missing pub key bytes", + }, + { + name: "invalid-key-type", + bsrKeys: &kms.Keys{ + PubKey: &wrapping.KeyInfo{ + Key: keys.PubKey.Key, + KeyId: keys.PubKey.KeyId, + KeyType: wrapping.KeyType_Aes256, + KeyEncoding: keys.PubKey.KeyEncoding, + }, + PubKeySelfSignature: keys.PubKeySelfSignature, + }, + wantErr: true, + wantErrMatch: kms.ErrInvalidParameter, + wantErrContains: "unexpected key type \"Aes256\"; expected \"Ed25519\"", + }, + { + name: "invalid-key-encoding", + bsrKeys: &kms.Keys{ + PubKey: &wrapping.KeyInfo{ + Key: keys.PubKey.Key, + KeyId: keys.PubKey.KeyId, + KeyType: keys.PubKey.KeyType, + KeyEncoding: wrapping.KeyEncoding_Pkcs8, + }, + PubKeySelfSignature: keys.PubKeySelfSignature, + }, + wantErr: true, + wantErrMatch: kms.ErrInvalidParameter, + wantErrContains: "unexpected key encoding \"Pkcs8\"; expected \"Bytes\"", + }, + { + name: "bad-pub-key", + bsrKeys: &kms.Keys{ + PubKey: &wrapping.KeyInfo{ + Key: []byte("bad-pub-key"), + KeyId: keys.PubKey.KeyId, + KeyType: keys.PubKey.KeyType, + KeyEncoding: keys.PubKey.KeyEncoding, + }, + PubKeySelfSignature: keys.PubKeySelfSignature, + }, + wantErr: true, + wantErrMatch: kms.ErrInvalidParameter, + wantErrContains: "expected public key with 32 bytes and got 11", + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + + got, err := tc.bsrKeys.VerifySignatureWithPubKey(testCtx, sig, []byte(tc.verifyMessage)) + if tc.wantErr { + require.Error(err) + assert.Empty(got) + if tc.wantErrMatch != nil { + assert.ErrorIsf(err, tc.wantErrMatch, "expected %q and got err: %+v", tc.wantErrMatch, err) + } + if tc.wantErrContains != "" { + assert.Contains(err.Error(), tc.wantErrContains) + } + return + } + require.NoError(err) + assert.Equal(tc.want, got) + }) + } +} + func TestBsrKeys_SignWithPrivKey(t *testing.T) { t.Parallel() testCtx := context.Background()