Rename root, api and sdk module paths (imports, go.mod, go_package, ldflags, build scripts, buf configs) and regenerate protobuf/gateway code with the project's pinned toolchain. Wire identifiers unchanged. api gets a local replace for sdk; root/api/sdk require v0.1.0. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
533 lines
21 KiB
Go
533 lines
21 KiB
Go
// Copyright IBM Corp. 2020, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package kms
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ed25519"
|
|
"crypto/rand"
|
|
"crypto/subtle"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/bsr/internal/is"
|
|
"repo.anhonesthost.net/CyberCoveLLC/threshold/internal/util"
|
|
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
|
|
wrappingEd "github.com/hashicorp/go-kms-wrapping/v2/ed25519"
|
|
|
|
"github.com/hashicorp/go-kms-wrapping/v2/aead"
|
|
"github.com/hashicorp/go-kms-wrapping/v2/extras/crypto"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
// Keys are the keys required/associated with a BSR for crypto operations.
|
|
//
|
|
// Operations on this type are concurrently safe.
|
|
type Keys struct {
|
|
// WrappedBsrKey is the "bsr key" which is a wrapped AES-GCM key
|
|
WrappedBsrKey *wrapping.KeyInfo
|
|
|
|
// WrappedPrivKey is the BSR's wrapped ed25519.PrivateKey
|
|
WrappedPrivKey *wrapping.KeyInfo
|
|
|
|
// BsrKey is the plaintext "bsr key" which is an AES-GCM key
|
|
BsrKey *wrapping.KeyInfo
|
|
|
|
// PrivKey is the BSR's plaintext ed25519.PrivateKey
|
|
PrivKey *wrapping.KeyInfo
|
|
|
|
// PubKey is the BSR's plaintext ed25519.PublicKey
|
|
PubKey *wrapping.KeyInfo
|
|
|
|
// PubKeySelfSignature is a self-signature of the BSR's plaintext
|
|
// ed25519.PublicKey created with its ed25519.PrivateKey
|
|
PubKeySelfSignature *wrapping.SigInfo
|
|
|
|
// PubKeyBsrSignature is a signature of the BSR's plaintext ed25519.Public
|
|
// key created with the BsrKey (AES-GCM)
|
|
PubKeyBsrSignature *wrapping.SigInfo
|
|
|
|
l sync.RWMutex
|
|
}
|
|
|
|
// WrappedKeys contains the wrapped BSR and priv keys
|
|
type WrappedKeys struct {
|
|
WrappedBsrKey *wrapping.KeyInfo
|
|
WrappedPrivKey *wrapping.KeyInfo
|
|
}
|
|
|
|
// Unwrapped keys contains the unwrapped BSR and priv keys
|
|
type UnwrappedKeys struct {
|
|
BsrKey *wrapping.KeyInfo
|
|
PrivKey *wrapping.KeyInfo
|
|
}
|
|
|
|
// KeyUnwrapCallbackFunc is used by OpenSession to unwrap BSR and private keys
|
|
type KeyUnwrapCallbackFunc func(WrappedKeys) (UnwrappedKeys, error)
|
|
|
|
// CreateKeys creates new bsr keys, wrapping and signing keys as required
|
|
// using the provided bsrWrapper. Supported options: WithRandomReader
|
|
func CreateKeys(ctx context.Context, bsrWrapper wrapping.Wrapper, sessionId string, opt ...Option) (*Keys, error) {
|
|
const op = "kms.CreateBsrKeys"
|
|
switch {
|
|
case sessionId == "":
|
|
return nil, fmt.Errorf("%s: missing session id: %w", op, ErrInvalidParameter)
|
|
case is.Nil(bsrWrapper):
|
|
return nil, fmt.Errorf("%s: missing external bsr wrapper: %w", op, ErrInvalidParameter)
|
|
}
|
|
|
|
opts := getOpts(opt...)
|
|
|
|
if opts.withRandomReader == nil {
|
|
opts.withRandomReader = rand.Reader
|
|
}
|
|
|
|
retKeys := &Keys{}
|
|
|
|
bsrKey := make([]byte, 32)
|
|
n, err := opts.withRandomReader.Read(bsrKey)
|
|
switch {
|
|
case err != nil:
|
|
return nil, fmt.Errorf("%s: error reading random bytes for bsr: %w", op, ErrGenKey)
|
|
case n != 32:
|
|
return nil, fmt.Errorf("%s: wanted 32 bytes and got %d: %w", op, n, ErrGenKey)
|
|
}
|
|
bsrBlob, err := bsrWrapper.Encrypt(ctx, bsrKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: unable to encrypt bsr key: %w", op, ErrEncrypt)
|
|
}
|
|
marshaledBsrBlob, err := proto.Marshal(bsrBlob)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: unable to marshal bsr key %w: %w", op, err, ErrEncode)
|
|
}
|
|
retKeys.WrappedBsrKey = &wrapping.KeyInfo{
|
|
KeyId: sessionId,
|
|
WrappedKey: marshaledBsrBlob,
|
|
KeyType: wrapping.KeyType_Aes256,
|
|
KeyEncoding: wrapping.KeyEncoding_Bytes,
|
|
KeyPurposes: []wrapping.KeyPurpose{wrapping.KeyPurpose_Sign, wrapping.KeyPurpose_Encrypt, wrapping.KeyPurpose_Decrypt},
|
|
}
|
|
retKeys.BsrKey = &wrapping.KeyInfo{
|
|
KeyId: sessionId,
|
|
Key: bsrKey,
|
|
KeyType: wrapping.KeyType_Aes256,
|
|
KeyEncoding: wrapping.KeyEncoding_Bytes,
|
|
KeyPurposes: []wrapping.KeyPurpose{wrapping.KeyPurpose_Sign, wrapping.KeyPurpose_Encrypt, wrapping.KeyPurpose_Decrypt},
|
|
}
|
|
|
|
pub, priv, err := ed25519.GenerateKey(opts.withRandomReader)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: unable to generate bsr ed25519 key-pair %w: %w", op, err, ErrGenKey)
|
|
}
|
|
privBlob, err := bsrWrapper.Encrypt(ctx, priv)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: unable to encrypt bsr ed25519 priv key %w: %w", op, err, ErrEncrypt)
|
|
}
|
|
marshaledPrivBlob, err := proto.Marshal(privBlob)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: unable to marshal bsr ed25519 priv key %w: %w", op, err, ErrEncode)
|
|
}
|
|
retKeys.WrappedPrivKey = &wrapping.KeyInfo{
|
|
KeyId: sessionId,
|
|
WrappedKey: marshaledPrivBlob,
|
|
KeyType: wrapping.KeyType_Ed25519,
|
|
KeyEncoding: wrapping.KeyEncoding_Bytes,
|
|
KeyPurposes: []wrapping.KeyPurpose{wrapping.KeyPurpose_Sign},
|
|
}
|
|
retKeys.PrivKey = &wrapping.KeyInfo{
|
|
KeyId: sessionId,
|
|
Key: priv,
|
|
KeyType: wrapping.KeyType_Ed25519,
|
|
KeyEncoding: wrapping.KeyEncoding_Bytes,
|
|
KeyPurposes: []wrapping.KeyPurpose{wrapping.KeyPurpose_Sign},
|
|
}
|
|
retKeys.PubKey = &wrapping.KeyInfo{
|
|
KeyId: sessionId,
|
|
Key: pub,
|
|
KeyType: wrapping.KeyType_Ed25519,
|
|
KeyEncoding: wrapping.KeyEncoding_Bytes,
|
|
KeyPurposes: []wrapping.KeyPurpose{wrapping.KeyPurpose_Verify},
|
|
}
|
|
privKeySigner, err := wrappingEd.NewSigner(ctx, wrappingEd.WithPrivKey(priv), wrapping.WithKeyId(sessionId))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
retKeys.PubKeySelfSignature, err = privKeySigner.Sign(ctx, pub)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: %w: %w", op, err, ErrSign)
|
|
}
|
|
|
|
bsrKeyWrapper := aead.NewWrapper()
|
|
if _, err := bsrKeyWrapper.SetConfig(ctx, wrapping.WithKeyId(sessionId)); err != nil {
|
|
return nil, fmt.Errorf("%s: setting config on aead wrapper %w: %w", op, err, ErrUnknown)
|
|
}
|
|
if err := bsrKeyWrapper.SetAesGcmKeyBytes(bsrKey); err != nil {
|
|
return nil, fmt.Errorf("%s: error setting key bytes on aead wrapper %w: %w", op, err, ErrUnknown)
|
|
}
|
|
marshaledSig, err := crypto.HmacSha256(ctx, pub, bsrKeyWrapper, crypto.WithMarshaledSigInfo())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: error signing pub key with bsr key %w: %w", op, err, ErrSign)
|
|
}
|
|
retKeys.PubKeyBsrSignature = &wrapping.SigInfo{}
|
|
if err := proto.Unmarshal([]byte(marshaledSig), retKeys.PubKeyBsrSignature); err != nil {
|
|
return nil, fmt.Errorf("%s: unable to unmarshal sig info %w: %w", op, err, ErrEncode)
|
|
}
|
|
|
|
return retKeys, nil
|
|
}
|
|
|
|
// UnwrapBsrKey will unwrap the bsr key (k.WrappedBsrKey which is an AES-GCM)
|
|
// using the provide bsr kms. The k.BsrKey will be set to the unwrapped key
|
|
// key and returned in the form of an aead.Wrapper
|
|
//
|
|
// This is a concurrently safe operation.
|
|
func (k *Keys) UnwrapBsrKey(ctx context.Context, bsrWrapper wrapping.Wrapper, opt ...Option) (*aead.Wrapper, error) {
|
|
const op = "kms.(BsrKeys).UnwrapBsrKey"
|
|
if k == nil {
|
|
return nil, fmt.Errorf("%s: nil bsr keys: %w", op, ErrInvalidParameter)
|
|
}
|
|
opts := getOpts(opt...)
|
|
|
|
// support just using an existing lock...
|
|
if !opts.withAlreadyLocked {
|
|
k.l.Lock()
|
|
defer k.l.Unlock()
|
|
}
|
|
|
|
switch {
|
|
case util.IsNil(bsrWrapper):
|
|
return nil, fmt.Errorf("%s: missing bsr wrapper: %w", op, ErrInvalidParameter)
|
|
case k.WrappedBsrKey == nil:
|
|
return nil, fmt.Errorf("%s: missing wrapped bsr key: %w", op, ErrInvalidParameter)
|
|
case k.WrappedBsrKey.KeyId == "":
|
|
return nil, fmt.Errorf("%s: missing wrapped bsr key id: %w", op, ErrInvalidParameter)
|
|
case k.WrappedBsrKey.WrappedKey == nil:
|
|
return nil, fmt.Errorf("%s: missing wrapped bsr key bytes: %w", op, ErrInvalidParameter)
|
|
case k.WrappedBsrKey.KeyType != wrapping.KeyType_Aes256:
|
|
return nil, keyTypeError(ctx, op, wrapping.KeyType_Aes256, k.WrappedBsrKey.KeyType)
|
|
case k.WrappedBsrKey.KeyEncoding != wrapping.KeyEncoding_Bytes:
|
|
return nil, keyEncodingError(ctx, op, wrapping.KeyEncoding_Bytes, k.WrappedBsrKey.KeyEncoding)
|
|
}
|
|
|
|
var blob wrapping.BlobInfo
|
|
|
|
if err := proto.Unmarshal(k.WrappedBsrKey.WrappedKey, &blob); err != nil {
|
|
return nil, fmt.Errorf("%s: error unmarshaling wrapped bsr key %w: %w", op, err, ErrDecode)
|
|
}
|
|
|
|
keyBytes, err := bsrWrapper.Decrypt(ctx, &blob)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: error decrypting wrapped bsr key %w: %w", op, err, ErrDecrypt)
|
|
}
|
|
|
|
bsrKeyWrapper := aead.NewWrapper()
|
|
if _, err := bsrKeyWrapper.SetConfig(ctx, wrapping.WithKeyId(k.WrappedBsrKey.KeyId)); err != nil {
|
|
return nil, fmt.Errorf("%s: error setting config on aead wrapper %w: %w", op, err, ErrUnknown)
|
|
}
|
|
if err := bsrKeyWrapper.SetAesGcmKeyBytes(keyBytes); err != nil {
|
|
return nil, fmt.Errorf("%s: error setting key bytes on aead wrapper %w: %w", op, err, ErrUnknown)
|
|
}
|
|
|
|
k.BsrKey = &wrapping.KeyInfo{
|
|
Key: keyBytes,
|
|
KeyId: k.WrappedBsrKey.KeyId,
|
|
KeyType: k.WrappedBsrKey.KeyType,
|
|
KeyPurposes: k.WrappedBsrKey.KeyPurposes,
|
|
KeyEncoding: wrapping.KeyEncoding_Bytes,
|
|
}
|
|
|
|
return bsrKeyWrapper, nil
|
|
}
|
|
|
|
// UnwrapPrivKey will unwrap the priv key (k.WrappedPrivKey which is an
|
|
// ed25519.PrivateKey) using the provide bsr kms. The k.PrivKey will be set to
|
|
// the unwrapped key and returned in the form of an ed25519.PrivateKey
|
|
//
|
|
// This is a concurrently safe operation.
|
|
func (k *Keys) UnwrapPrivKey(ctx context.Context, bsrWrapper wrapping.Wrapper) (ed25519.PrivateKey, error) {
|
|
const op = "kms.(BsrKeys).UnwrapPrivKey"
|
|
if k == nil {
|
|
return nil, fmt.Errorf("%s: nil bsr keys: %w", op, ErrInvalidParameter)
|
|
}
|
|
k.l.Lock()
|
|
defer k.l.Unlock()
|
|
|
|
switch {
|
|
case util.IsNil(bsrWrapper):
|
|
return nil, fmt.Errorf("%s: missing bsr wrapper: %w", op, ErrInvalidParameter)
|
|
case k.WrappedPrivKey == nil:
|
|
return nil, fmt.Errorf("%s: missing wrapped priv key: %w", op, ErrInvalidParameter)
|
|
case k.WrappedPrivKey.KeyId == "":
|
|
return nil, fmt.Errorf("%s: missing wrapped priv key id: %w", op, ErrInvalidParameter)
|
|
case k.WrappedPrivKey.WrappedKey == nil:
|
|
return nil, fmt.Errorf("%s: missing wrapped priv key bytes: %w", op, ErrInvalidParameter)
|
|
case k.WrappedPrivKey.KeyType != wrapping.KeyType_Ed25519:
|
|
return nil, keyTypeError(ctx, op, wrapping.KeyType_Ed25519, k.WrappedPrivKey.KeyType)
|
|
case k.WrappedPrivKey.KeyEncoding != wrapping.KeyEncoding_Bytes:
|
|
return nil, keyEncodingError(ctx, op, wrapping.KeyEncoding_Bytes, k.WrappedPrivKey.WrappedKeyEncoding)
|
|
}
|
|
var privKeyBlob wrapping.BlobInfo
|
|
if err := proto.Unmarshal(k.WrappedPrivKey.WrappedKey, &privKeyBlob); err != nil {
|
|
return nil, fmt.Errorf("%s: error unmarshaling wrapped priv key %w: %w", op, err, ErrDecode)
|
|
}
|
|
privKeyBytes, err := bsrWrapper.Decrypt(ctx, &privKeyBlob)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: error decrypting wrapped priv key %w: %w", op, err, ErrDecrypt)
|
|
}
|
|
if len(privKeyBytes) != ed25519.PrivateKeySize {
|
|
return nil, fmt.Errorf("%s: priv key must be %d bytes; got %d: %w", op, ed25519.PrivateKeySize, len(privKeyBytes), ErrInvalidParameter)
|
|
}
|
|
|
|
k.PrivKey = &wrapping.KeyInfo{
|
|
Key: privKeyBytes,
|
|
KeyId: k.WrappedPrivKey.KeyId,
|
|
KeyType: k.WrappedPrivKey.KeyType,
|
|
KeyPurposes: k.WrappedPrivKey.KeyPurposes,
|
|
KeyEncoding: wrapping.KeyEncoding_Bytes,
|
|
}
|
|
|
|
return ed25519.PrivateKey(privKeyBytes), nil
|
|
}
|
|
|
|
// VerifyPubKeyBsrSignature will verify the pub key signature created with the
|
|
// bsr key. It will first try to use k.BsrKey, if that's not available it will
|
|
// attempt to unwrap k.WrappedBsrKey and use it for verification. Supported
|
|
// options: WithBsrWrapper which is required if using k.WrappedBsrKey to verify
|
|
// the signature.
|
|
//
|
|
// This is a concurrently safe operation.
|
|
func (k *Keys) VerifyPubKeyBsrSignature(ctx context.Context, opt ...Option) (bool, error) {
|
|
const op = "kms.(BsrKeys).VerifyPubKeyBsrSignature"
|
|
switch {
|
|
case k == nil:
|
|
return false, fmt.Errorf("%s: nil bsr keys: %w", op, ErrInvalidParameter)
|
|
case k.PubKey == nil:
|
|
return false, fmt.Errorf("%s: missing pub key: %w", op, ErrInvalidParameter)
|
|
case k.PubKeyBsrSignature == nil:
|
|
return false, fmt.Errorf("%s: missing pub key signature: %w", op, ErrInvalidParameter)
|
|
}
|
|
ok, err := k.VerifySignatureWithBsrKey(ctx, k.PubKeyBsrSignature, ed25519.PublicKey(k.PubKey.Key), opt...)
|
|
if err != nil {
|
|
return ok, fmt.Errorf("%s: %w", op, err)
|
|
}
|
|
return ok, err
|
|
}
|
|
|
|
// VerifyPubKeySelfSignature will verify the self-signed pub key signature using
|
|
// k.PubKey. Note: this will tell you the signature is correct, but not if the
|
|
// public key is the right key; you want to use VerifyPubKeyBsrSignature(...)
|
|
// for that.
|
|
//
|
|
// This is a concurrently safe operation.
|
|
func (k *Keys) VerifyPubKeySelfSignature(ctx context.Context) (bool, error) {
|
|
const op = "kms.(BsrKeys).VerifyPubKeySelfSignature"
|
|
if k == nil {
|
|
return false, fmt.Errorf("%s: bsr keys: %w", op, ErrInvalidParameter)
|
|
}
|
|
|
|
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 k.PubKeySelfSignature == nil:
|
|
return false, fmt.Errorf("%s: missing pub key self signature: %w", op, ErrInvalidParameter)
|
|
case k.PubKeySelfSignature.KeyInfo.KeyId != k.PubKey.KeyId:
|
|
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)
|
|
}
|
|
|
|
return k.VerifySignatureWithPubKey(ctx, k.PubKeySelfSignature, k.PubKey.Key)
|
|
}
|
|
|
|
// SignWithPrivKey will sign the msg with the BsrKeys.PrivKey (aka the BSR's
|
|
// ed25519.PrivateKey). Typical usage is to sign the BSR's checksums file.
|
|
//
|
|
// This is a concurrently safe operation.
|
|
func (k *Keys) SignWithPrivKey(ctx context.Context, msg []byte) (*wrapping.SigInfo, error) {
|
|
const op = "kms.(BsrKeys).SignWithPrivKey"
|
|
if k == nil {
|
|
return nil, fmt.Errorf("%s: nil bsr keys: %w", op, ErrInvalidParameter)
|
|
}
|
|
k.l.RLock()
|
|
defer k.l.RUnlock()
|
|
|
|
switch {
|
|
case msg == nil:
|
|
return nil, fmt.Errorf("%s: missing msg: %w", op, ErrInvalidParameter)
|
|
case k.PrivKey == nil:
|
|
return nil, fmt.Errorf("%s: missing priv key: %w", op, ErrInvalidParameter)
|
|
case k.PrivKey.KeyId == "":
|
|
return nil, fmt.Errorf("%s: missing priv key id: %w", op, ErrInvalidParameter)
|
|
case k.PrivKey.Key == nil:
|
|
return nil, fmt.Errorf("%s: missing priv key bytes: %w", op, ErrInvalidParameter)
|
|
case k.PrivKey.KeyType != wrapping.KeyType_Ed25519:
|
|
return nil, keyTypeError(ctx, op, wrapping.KeyType_Ed25519, k.PrivKey.KeyType)
|
|
case k.PrivKey.KeyEncoding != wrapping.KeyEncoding_Bytes:
|
|
return nil, keyEncodingError(ctx, op, wrapping.KeyEncoding_Bytes, k.PrivKey.KeyEncoding)
|
|
case len(k.PrivKey.Key) != ed25519.PrivateKeySize:
|
|
return nil, fmt.Errorf("%s: expected priv key with %d bytes and got %d: %w", op, ed25519.PrivateKeySize, len(k.PrivKey.Key), ErrInvalidParameter)
|
|
}
|
|
|
|
s, err := wrappingEd.NewSigner(ctx,
|
|
wrappingEd.WithPrivKey(k.PrivKey.Key),
|
|
wrapping.WithKeyId(k.PrivKey.KeyId),
|
|
wrapping.WithKeyEncoding(k.PrivKey.KeyEncoding),
|
|
wrapping.WithKeyPurposes(k.PrivKey.KeyPurposes...),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: unable to create priv key signer: %w", op, err)
|
|
}
|
|
si, err := s.Sign(ctx, msg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s: %w: %w", op, err, ErrSign)
|
|
}
|
|
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 {
|
|
return false, fmt.Errorf("%s: nil bsr keys: %w", op, ErrInvalidParameter)
|
|
}
|
|
// needs a write lock since it may need to UnwrapBsrKey(...)
|
|
k.l.Lock()
|
|
defer k.l.Unlock()
|
|
|
|
switch {
|
|
case k.BsrKey == nil && k.WrappedBsrKey == nil:
|
|
return false, fmt.Errorf("%s: missing bsr key and wrapped bsr key: %w", op, ErrInvalidParameter)
|
|
case k.BsrKey != nil && k.BsrKey.Key == nil:
|
|
return false, fmt.Errorf("%s: missing bsr key bytes: %w", op, ErrInvalidParameter)
|
|
case k.WrappedBsrKey != nil && k.WrappedBsrKey.WrappedKey == nil:
|
|
return false, fmt.Errorf("%s: missing wrapped bsr key bytes: %w", op, ErrInvalidParameter)
|
|
case len(msg) == 0:
|
|
return false, fmt.Errorf("%s: missing msg: %w", op, ErrInvalidParameter)
|
|
case sig == nil:
|
|
return false, fmt.Errorf("%s: missing signature: %w", op, ErrInvalidParameter)
|
|
case sig.KeyInfo == nil:
|
|
return false, fmt.Errorf("%s: missing signature key info: %w", op, ErrInvalidParameter)
|
|
case len(sig.Signature) == 0:
|
|
return false, fmt.Errorf("%s: signature is empty: %w", op, ErrInvalidParameter)
|
|
}
|
|
|
|
opts := getOpts(opt...)
|
|
var bsrKeyWrapper *aead.Wrapper
|
|
switch {
|
|
case k.BsrKey != nil:
|
|
switch {
|
|
case k.BsrKey.KeyType != wrapping.KeyType_Aes256:
|
|
return false, keyTypeError(ctx, op, wrapping.KeyType_Aes256, k.BsrKey.KeyType)
|
|
case k.BsrKey.KeyEncoding != wrapping.KeyEncoding_Bytes:
|
|
return false, keyEncodingError(ctx, op, wrapping.KeyEncoding_Bytes, k.BsrKey.KeyEncoding)
|
|
}
|
|
bsrKeyWrapper = aead.NewWrapper()
|
|
if _, err := bsrKeyWrapper.SetConfig(ctx, wrapping.WithKeyId(k.BsrKey.KeyId)); err != nil {
|
|
return false, fmt.Errorf("%s: error setting config on bsr key wrapper %w: %w", op, err, ErrUnknown)
|
|
}
|
|
if err := bsrKeyWrapper.SetAesGcmKeyBytes(k.BsrKey.Key); err != nil {
|
|
return false, fmt.Errorf("%s: error setting key bytes on bsr key wrapper %w: %w", op, err, ErrUnknown)
|
|
}
|
|
case k.WrappedBsrKey != nil:
|
|
switch {
|
|
case util.IsNil(opts.withBsrWrapper):
|
|
return false, fmt.Errorf("%s: missing bsr wrapper: %w", op, ErrInvalidParameter)
|
|
case k.WrappedBsrKey.KeyType != wrapping.KeyType_Aes256:
|
|
return false, keyTypeError(ctx, op, wrapping.KeyType_Aes256, k.WrappedBsrKey.KeyType)
|
|
case k.WrappedBsrKey.KeyEncoding != wrapping.KeyEncoding_Bytes:
|
|
return false, keyEncodingError(ctx, op, wrapping.KeyEncoding_Bytes, k.WrappedBsrKey.KeyEncoding)
|
|
}
|
|
var err error
|
|
if bsrKeyWrapper, err = k.UnwrapBsrKey(ctx, opts.withBsrWrapper, withAlreadyLocked(true)); err != nil {
|
|
return false, fmt.Errorf("%s: error unwrapping bsr key: %w", op, err)
|
|
}
|
|
default:
|
|
return false, fmt.Errorf("%s: missing bsr key and wrapped bsr key: %w", op, ErrInvalidParameter)
|
|
}
|
|
marshaledSigInfo, err := crypto.HmacSha256(ctx, msg, bsrKeyWrapper, crypto.WithMarshaledSigInfo())
|
|
if err != nil {
|
|
return false, fmt.Errorf("%s: error signing msg %w: %w", op, err, ErrSign)
|
|
}
|
|
var sigInfo wrapping.SigInfo
|
|
if err = proto.Unmarshal([]byte(marshaledSigInfo), &sigInfo); err != nil {
|
|
return false, fmt.Errorf("%s: error unmarshaling sig info %w: %w", op, err, ErrDecode)
|
|
}
|
|
bsrKeyId, err := bsrKeyWrapper.KeyId(ctx)
|
|
if err != nil {
|
|
return false, fmt.Errorf("%s: error getting bsr key id %w: %w", op, err, ErrUnknown)
|
|
}
|
|
if sig.KeyInfo.KeyId != bsrKeyId {
|
|
return false, fmt.Errorf("%s: signature key id %q doesn't match verifying key id %q: %w", op, k.PubKeyBsrSignature.KeyInfo.KeyId, bsrKeyId, ErrInvalidParameter)
|
|
}
|
|
if subtle.ConstantTimeCompare(sig.Signature, sigInfo.Signature) == 1 {
|
|
return true, nil
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func keyTypeError(ctx context.Context, fromOp string, want wrapping.KeyType, got wrapping.KeyType) error {
|
|
return fmt.Errorf(
|
|
"%s: unexpected key type %q; expected %q: %w",
|
|
fromOp,
|
|
wrapping.KeyType_name[int32(got)],
|
|
wrapping.KeyType_name[int32(want)],
|
|
ErrInvalidParameter,
|
|
)
|
|
}
|
|
|
|
func keyEncodingError(ctx context.Context, fromOp string, want wrapping.KeyEncoding, got wrapping.KeyEncoding) error {
|
|
return fmt.Errorf(
|
|
"%s: unexpected key encoding %q; expected %q: %w",
|
|
fromOp,
|
|
wrapping.KeyEncoding_name[int32(got)],
|
|
wrapping.KeyEncoding_name[int32(want)],
|
|
ErrInvalidParameter,
|
|
)
|
|
}
|