feat(KMS): add BsrKeys type and req. receiver functions

This commit is contained in:
Jim authored and Timothy Messier committed 2023-05-31 12:18:09 +00:00
1 parent 98d3e59372
commit aa10d1dbfe
9 files changed
+2110

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package is
import "reflect"
// Nil checks if the interface is nil
func Nil(i any) bool {
if i == nil {
return true
}
switch reflect.TypeOf(i).Kind() {
case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Slice:
return reflect.ValueOf(i).IsNil()
}
return false
}
+32
View File
@@ -0,0 +1,32 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package kms
import (
"errors"
)
// ErrUnknown represents an unknown error
var ErrUnknown = errors.New("unknown error")
// ErrInvalidParameter represents an invalid parameter error
var ErrInvalidParameter = errors.New("invalid parameter")
// ErrGenKey represents a key gen error
var ErrGenKey = errors.New("error occurred during key generation")
// ErrEncrypt represents an encryption error
var ErrEncrypt = errors.New("error occurred during encrypt")
// ErrDecrypt represents a decryption error
var ErrDecrypt = errors.New("error occurred during decrypt")
// ErrEncode represents an encoding error
var ErrEncode = errors.New("error occurred during encode")
// ErrDecode represents a decoding error
var ErrDecode = errors.New("error occurred during decode")
// ErrSign represents a signing error
var ErrSign = errors.New("error occurred during signing")
+467
View File
@@ -0,0 +1,467 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package kms
import (
"bytes"
"context"
"crypto/ed25519"
"crypto/rand"
"fmt"
"sync"
"github.com/hashicorp/boundary/internal/bsr/internal/is"
"github.com/hashicorp/boundary/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
}
// 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"
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 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)
}
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, ed25519.PublicKey(k.PubKey.Key), bsrKeyWrapper, crypto.WithMarshaledSigInfo())
if err != nil {
return false, fmt.Errorf("%s: error signing pub key %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 k.PubKeyBsrSignature.KeyInfo.KeyId != bsrKeyId {
return false, fmt.Errorf("%s: pub signature key id %q doesn't match verifying key id %q: %w", op, k.PubKeyBsrSignature.KeyInfo.KeyId, bsrKeyId, ErrInvalidParameter)
}
if bytes.Compare(k.PubKeyBsrSignature.Signature, sigInfo.Signature) == 0 {
return true, nil
}
return false, nil
}
// 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)
}
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 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)
}
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
}
// 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
}
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,
)
}
File diff suppressed because it is too large. Load diff
+24
View File
@@ -0,0 +1,24 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package kms
import (
"crypto/rand"
"testing"
"github.com/stretchr/testify/assert"
)
// Test_GetOpts provides unit tests for GetOpts and all the options
func Test_GetOpts(t *testing.T) {
t.Parallel()
t.Run("WithRandomReader", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithRandomReader(rand.Reader))
testOpts := getDefaultOptions()
testOpts.withRandomReader = rand.Reader
assert.Equal(opts, testOpts)
})
}
+55
View File
@@ -0,0 +1,55 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package kms
import (
"io"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
)
// getOpts - iterate the inbound Options and return a struct
func getOpts(opt ...Option) options {
opts := getDefaultOptions()
for _, o := range opt {
o(&opts)
}
return opts
}
// Option - how Options are passed as arguments
type Option func(*options)
// options = how options are represented
type options struct {
withBsrWrapper wrapping.Wrapper
withRandomReader io.Reader
withAlreadyLocked bool
}
func getDefaultOptions() options {
return options{}
}
// WithBsrWrapper sets the external Bsr wrapper for a KMS
func WithBsrWrapper(w wrapping.Wrapper) Option {
return func(o *options) {
o.withBsrWrapper = w
}
}
// WithRandomReader(...) option allows an optional random reader to be
// provided. By default the reader from crypto/rand will be used.
func WithRandomReader(randomReader io.Reader) Option {
return func(o *options) {
o.withRandomReader = randomReader
}
}
// not exported... just an internal option
func withAlreadyLocked(isLockedAlready bool) Option {
return func(o *options) {
o.withAlreadyLocked = isLockedAlready
}
}
+135
View File
@@ -0,0 +1,135 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package kms
import (
"context"
"crypto/rand"
"encoding/base64"
"io"
"testing"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"github.com/hashicorp/go-kms-wrapping/v2/aead"
)
// TestWrapper initializes an AEAD wrapping.Wrapper for testing
func TestWrapper(t testing.TB) wrapping.Wrapper {
t.Helper()
rootKey := make([]byte, 32)
n, err := rand.Read(rootKey)
if err != nil {
t.Fatal(err)
}
if n != 32 {
t.Fatal(n)
}
root := aead.NewWrapper()
_, err = root.SetConfig(context.Background(), wrapping.WithKeyId(base64.StdEncoding.EncodeToString(rootKey)))
if err != nil {
t.Fatal(err)
}
if err := root.SetAesGcmKeyBytes(rootKey); err != nil {
t.Fatal(err)
}
return root
}
// MockReader provides a mock reader for testing
type MockReader struct {
readCount int
// WithMockReadOn determines which read attempt the mock read results should
// be returned on.
WithMockReadOn int
// WithError specifies a mock read result of the specified error
WithError error
// WithBytesRead specifies a mock read result of the specified bytes read
WithBytesRead int
// Reader is the underlying reader
Reader io.Reader
}
// Read implements the mock read operation. Mock values supported: WithBytesRead, WithMockReadOn
func (m *MockReader) Read(p []byte) (n int, err error) {
m.readCount++
if m.readCount == m.WithMockReadOn {
_, _ = m.Reader.Read(p)
return m.WithBytesRead, m.WithError
}
return m.Reader.Read(p)
}
type MockWrapper struct {
// Wrapper is the underlying wrapping.Wrapper which is used to provide the
// mock's default behavior
Wrapper wrapping.Wrapper
encryptCount int
// WithEncryptErrorOn determines which encrypt attempt the mock encrypt
// error should be returned on.
WithEncryptErrorOn int
// EncryptErr is a mock value to return for the Encrypt(...) operation
EncryptErr error
// DecryptErr is a mock value to return for the Decrypt(...) operation
DecryptErr error
// KeyIdErr is a mock value to return for the KeyId(...) operation
KeyIdErr error
// KeyIdReturned is a mock value to return for the KeyId(...) operation
KeyIdReturned string
keyBytesCount int
// WithKeyBytesErrorOn determines which key bytes attempt the mock key bytes
// error should be returned on.
WithKeyBytesErrorOn int
}
// Type of the wrapper. No mock values supported
func (w *MockWrapper) Type(ctx context.Context) (wrapping.WrapperType, error) {
return w.Wrapper.Type(ctx)
}
// KeyId is the id of the key currently used for encryption operations. Mock
// values supported: KeyIdErr, KeyIdReturned
func (w *MockWrapper) KeyId(ctx context.Context) (string, error) { // nolint
switch {
case w.KeyIdErr != nil:
return "", w.KeyIdErr
case w.KeyIdReturned != "":
return w.KeyIdReturned, nil
default:
return w.Wrapper.KeyId(ctx)
}
}
// SetConfig applies the given options to a wrapper and returns
// configuration information. No mock values supported.
func (w *MockWrapper) SetConfig(ctx context.Context, options ...wrapping.Option) (*wrapping.WrapperConfig, error) {
return w.Wrapper.SetConfig(ctx, options...)
}
// Decrypt decrypts the given byte slice and stores the resulting information in
// the returned byte slice. Mock values supported: DecryptErr
func (w *MockWrapper) Decrypt(ctx context.Context, ciphertext *wrapping.BlobInfo, options ...wrapping.Option) ([]byte, error) {
if w.DecryptErr != nil {
return nil, w.DecryptErr
}
return w.Wrapper.Decrypt(ctx, ciphertext, options...)
}
// Encrypt encrypts the given byte slice. Mock values supported: EncryptErr and WithEncryptErrorOn
func (w *MockWrapper) Encrypt(ctx context.Context, plaintext []byte, options ...wrapping.Option) (*wrapping.BlobInfo, error) {
w.encryptCount++
if w.encryptCount == w.WithEncryptErrorOn {
return nil, w.EncryptErr
}
return w.Wrapper.Encrypt(ctx, plaintext, options...)
}
+2
View File
@@ -77,6 +77,8 @@ const (
Decode Code = 303 // Decode represents an error occurred during the underlying decoding/unmarshaling process
GenKey Code = 304 // GenKey represents an error occurred during the underlying key generation process
GenCert Code = 305 // GenCert represents an error occurred during the underlying certificate generation process
Sign Code = 306 // Sign represents an error occurred during the underlying signing process
Verify Code = 307 // Verify represents an error occurred during the underlying verification process
// General system errors are reserved Codes 400-599 and align with http
// client and server error codes
+8
View File
@@ -143,6 +143,14 @@ var errorCodeInfo = map[Code]Info{
Message: "error occurred during certification generation",
Kind: Encryption,
},
Sign: {
Message: "error occurred during signing",
Kind: Encryption,
},
Verify: {
Message: "error occurred during verification",
Kind: Encryption,
},
Internal: {
Message: "internal error",
Kind: Other,