mirror of
https://github.com/minio/minio.git
synced 2025-11-12 06:50:17 -05:00
introduce new package pkg/kms (#12019)
This commit introduces a new package `pkg/kms`. It contains basic types and functions to interact with various KMS implementations. This commit also moves KMS-related code from `cmd/crypto` to `pkg/kms`. Now, it is possible to implement a KMS-based config data encryption in the `pkg/config` package.
This commit is contained in:
committed by
GitHub
parent
1456f9f090
commit
885c170a64
@@ -32,6 +32,7 @@ import (
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
xhttp "github.com/minio/minio/cmd/http"
|
||||
"github.com/minio/minio/pkg/kms"
|
||||
xnet "github.com/minio/minio/pkg/net"
|
||||
)
|
||||
|
||||
@@ -155,22 +156,12 @@ func NewKes(cfg KesConfig) (KMS, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DefaultKeyID returns the default key ID that should be
|
||||
// used for SSE-S3 or SSE-KMS when the S3 client does not
|
||||
// provide an explicit key ID.
|
||||
func (kes *kesService) DefaultKeyID() string {
|
||||
return kes.defaultKeyID
|
||||
}
|
||||
|
||||
// Info returns some information about the KES,
|
||||
// configuration - like the endpoint or authentication
|
||||
// method.
|
||||
func (kes *kesService) Info() KMSInfo {
|
||||
return KMSInfo{
|
||||
Endpoints: kes.endpoints,
|
||||
Name: kes.DefaultKeyID(),
|
||||
AuthType: "TLS",
|
||||
}
|
||||
func (kes *kesService) Stat() (kms.Status, error) {
|
||||
return kms.Status{
|
||||
Name: "KES",
|
||||
Endpoints: kes.endpoints,
|
||||
DefaultKey: kes.defaultKeyID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateKey tries to create a new master key with the given keyID.
|
||||
@@ -180,22 +171,24 @@ func (kes *kesService) CreateKey(keyID string) error { return kes.client.CreateK
|
||||
// and a sealed version of this plaintext key encrypted using the
|
||||
// named key referenced by keyID. It also binds the generated key
|
||||
// cryptographically to the provided context.
|
||||
func (kes *kesService) GenerateKey(keyID string, ctx Context) (key [32]byte, sealedKey []byte, err error) {
|
||||
func (kes *kesService) GenerateKey(keyID string, ctx Context) (kms.DEK, error) {
|
||||
if keyID == "" {
|
||||
keyID = kes.defaultKeyID
|
||||
}
|
||||
context, err := ctx.MarshalText()
|
||||
if err != nil {
|
||||
return key, nil, err
|
||||
return kms.DEK{}, err
|
||||
}
|
||||
|
||||
var plainKey []byte
|
||||
plainKey, sealedKey, err = kes.client.GenerateDataKey(keyID, context)
|
||||
plaintext, ciphertext, err := kes.client.GenerateDataKey(keyID, context)
|
||||
if err != nil {
|
||||
return key, nil, err
|
||||
return kms.DEK{}, err
|
||||
}
|
||||
if len(plainKey) != len(key) {
|
||||
return key, nil, Errorf("crypto: received invalid plaintext key size from KMS")
|
||||
}
|
||||
copy(key[:], plainKey)
|
||||
return key, sealedKey, nil
|
||||
return kms.DEK{
|
||||
KeyID: keyID,
|
||||
Plaintext: plaintext,
|
||||
Ciphertext: ciphertext,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UnsealKey returns the decrypted sealedKey as plaintext key.
|
||||
@@ -205,22 +198,12 @@ func (kes *kesService) GenerateKey(keyID string, ctx Context) (key [32]byte, sea
|
||||
//
|
||||
// The context must be same context as the one provided while
|
||||
// generating the plaintext key / sealedKey.
|
||||
func (kes *kesService) UnsealKey(keyID string, sealedKey []byte, ctx Context) (key [32]byte, err error) {
|
||||
func (kes *kesService) DecryptKey(keyID string, ciphertext []byte, ctx Context) ([]byte, error) {
|
||||
context, err := ctx.MarshalText()
|
||||
if err != nil {
|
||||
return key, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var plainKey []byte
|
||||
plainKey, err = kes.client.DecryptDataKey(keyID, sealedKey, context)
|
||||
if err != nil {
|
||||
return key, err
|
||||
}
|
||||
if len(plainKey) != len(key) {
|
||||
return key, Errorf("crypto: received invalid plaintext key size from KMS")
|
||||
}
|
||||
copy(key[:], plainKey)
|
||||
return key, nil
|
||||
return kes.client.DecryptDataKey(keyID, ciphertext, context)
|
||||
}
|
||||
|
||||
// kesClient implements the bare minimum functionality needed for
|
||||
|
||||
@@ -37,7 +37,7 @@ type ObjectKey [32]byte
|
||||
// GenerateKey generates a unique ObjectKey from a 256 bit external key
|
||||
// and a source of randomness. If random is nil the default PRNG of the
|
||||
// system (crypto/rand) is used.
|
||||
func GenerateKey(extKey [32]byte, random io.Reader) (key ObjectKey) {
|
||||
func GenerateKey(extKey []byte, random io.Reader) (key ObjectKey) {
|
||||
if random == nil {
|
||||
random = rand.Reader
|
||||
}
|
||||
@@ -46,7 +46,7 @@ func GenerateKey(extKey [32]byte, random io.Reader) (key ObjectKey) {
|
||||
logger.CriticalIf(context.Background(), errOutOfEntropy)
|
||||
}
|
||||
sha := sha256.New()
|
||||
sha.Write(extKey[:])
|
||||
sha.Write(extKey)
|
||||
sha.Write(nonce[:])
|
||||
sha.Sum(key[:0])
|
||||
return key
|
||||
@@ -76,7 +76,7 @@ type SealedKey struct {
|
||||
// Seal encrypts the ObjectKey using the 256 bit external key and IV. The sealed
|
||||
// key is also cryptographically bound to the object's path (bucket/object) and the
|
||||
// domain (SSE-C or SSE-S3).
|
||||
func (key ObjectKey) Seal(extKey, iv [32]byte, domain, bucket, object string) SealedKey {
|
||||
func (key ObjectKey) Seal(extKey []byte, iv [32]byte, domain, bucket, object string) SealedKey {
|
||||
var (
|
||||
sealingKey [32]byte
|
||||
encryptedKey bytes.Buffer
|
||||
@@ -101,7 +101,7 @@ func (key ObjectKey) Seal(extKey, iv [32]byte, domain, bucket, object string) Se
|
||||
// Unseal decrypts a sealed key using the 256 bit external key. Since the sealed key
|
||||
// may be cryptographically bound to the object's path the same bucket/object as during sealing
|
||||
// must be provided. On success the ObjectKey contains the decrypted sealed key.
|
||||
func (key *ObjectKey) Unseal(extKey [32]byte, sealedKey SealedKey, domain, bucket, object string) error {
|
||||
func (key *ObjectKey) Unseal(extKey []byte, sealedKey SealedKey, domain, bucket, object string) error {
|
||||
var (
|
||||
unsealConfig sio.Config
|
||||
)
|
||||
|
||||
@@ -53,7 +53,7 @@ func TestGenerateKey(t *testing.T) {
|
||||
i, test := i, test
|
||||
func() {
|
||||
defer recoverTest(i, test.ShouldPass, t)
|
||||
key := GenerateKey(test.ExtKey, test.Random)
|
||||
key := GenerateKey(test.ExtKey[:], test.Random)
|
||||
if [32]byte(key) == [32]byte{} {
|
||||
t.Errorf("Test %d: generated key is zero key", i) // check that we generate random and unique key
|
||||
}
|
||||
@@ -125,9 +125,9 @@ var sealUnsealKeyTests = []struct {
|
||||
|
||||
func TestSealUnsealKey(t *testing.T) {
|
||||
for i, test := range sealUnsealKeyTests {
|
||||
key := GenerateKey(test.SealExtKey, rand.Reader)
|
||||
sealedKey := key.Seal(test.SealExtKey, test.SealIV, test.SealDomain, test.SealBucket, test.SealObject)
|
||||
if err := key.Unseal(test.UnsealExtKey, sealedKey, test.UnsealDomain, test.UnsealBucket, test.UnsealObject); err == nil && !test.ShouldPass {
|
||||
key := GenerateKey(test.SealExtKey[:], rand.Reader)
|
||||
sealedKey := key.Seal(test.SealExtKey[:], test.SealIV, test.SealDomain, test.SealBucket, test.SealObject)
|
||||
if err := key.Unseal(test.UnsealExtKey[:], sealedKey, test.UnsealDomain, test.UnsealBucket, test.UnsealObject); err == nil && !test.ShouldPass {
|
||||
t.Errorf("Test %d should fail but passed successfully", i)
|
||||
} else if err != nil && test.ShouldPass {
|
||||
t.Errorf("Test %d should pass put failed: %v", i, err)
|
||||
@@ -136,10 +136,10 @@ func TestSealUnsealKey(t *testing.T) {
|
||||
|
||||
// Test legacy InsecureSealAlgorithm
|
||||
var extKey, iv [32]byte
|
||||
key := GenerateKey(extKey, rand.Reader)
|
||||
sealedKey := key.Seal(extKey, iv, "SSE-S3", "bucket", "object")
|
||||
key := GenerateKey(extKey[:], rand.Reader)
|
||||
sealedKey := key.Seal(extKey[:], iv, "SSE-S3", "bucket", "object")
|
||||
sealedKey.Algorithm = InsecureSealAlgorithm
|
||||
if err := key.Unseal(extKey, sealedKey, "SSE-S3", "bucket", "object"); err == nil {
|
||||
if err := key.Unseal(extKey[:], sealedKey, "SSE-S3", "bucket", "object"); err == nil {
|
||||
t.Errorf("'%s' test succeeded but it should fail because the legacy algorithm was used", sealedKey.Algorithm)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,166 +22,86 @@ import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/kms"
|
||||
"github.com/minio/sio"
|
||||
)
|
||||
|
||||
// Context is a list of key-value pairs cryptographically
|
||||
// associated with a certain object.
|
||||
type Context map[string]string
|
||||
|
||||
// MarshalText returns a canonical text representation of
|
||||
// the Context.
|
||||
|
||||
// MarshalText sorts the context keys and writes the sorted
|
||||
// key-value pairs as canonical JSON object. The sort order
|
||||
// is based on the un-escaped keys.
|
||||
func (c Context) MarshalText() ([]byte, error) {
|
||||
if len(c) == 0 {
|
||||
return []byte{'{', '}'}, nil
|
||||
}
|
||||
|
||||
// Pre-allocate a buffer - 128 bytes is an arbitrary
|
||||
// heuristic value that seems like a good starting size.
|
||||
var b = bytes.NewBuffer(make([]byte, 0, 128))
|
||||
if len(c) == 1 {
|
||||
for k, v := range c {
|
||||
b.WriteString(`{"`)
|
||||
EscapeStringJSON(b, k)
|
||||
b.WriteString(`":"`)
|
||||
EscapeStringJSON(b, v)
|
||||
b.WriteString(`"}`)
|
||||
}
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
sortedKeys := make([]string, 0, len(c))
|
||||
for k := range c {
|
||||
sortedKeys = append(sortedKeys, k)
|
||||
}
|
||||
sort.Strings(sortedKeys)
|
||||
|
||||
b.WriteByte('{')
|
||||
for i, k := range sortedKeys {
|
||||
b.WriteByte('"')
|
||||
EscapeStringJSON(b, k)
|
||||
b.WriteString(`":"`)
|
||||
EscapeStringJSON(b, c[k])
|
||||
b.WriteByte('"')
|
||||
if i < len(sortedKeys)-1 {
|
||||
b.WriteByte(',')
|
||||
}
|
||||
}
|
||||
b.WriteByte('}')
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
type Context = kms.Context
|
||||
|
||||
// KMS represents an active and authenticted connection
|
||||
// to a Key-Management-Service. It supports generating
|
||||
// data key generation and unsealing of KMS-generated
|
||||
// data keys.
|
||||
type KMS interface {
|
||||
// DefaultKeyID returns the default master key ID. It should be
|
||||
// used for SSE-S3 and whenever a S3 client requests SSE-KMS but
|
||||
// does not specify an explicit SSE-KMS key ID.
|
||||
DefaultKeyID() string
|
||||
|
||||
// CreateKey creates a new master key with the given key ID
|
||||
// at the KMS.
|
||||
CreateKey(keyID string) error
|
||||
|
||||
// GenerateKey generates a new random data key using
|
||||
// the master key referenced by the keyID. It returns
|
||||
// the plaintext key and the sealed plaintext key
|
||||
// on success.
|
||||
//
|
||||
// The context is cryptographically bound to the
|
||||
// generated key. The same context must be provided
|
||||
// again to unseal the generated key.
|
||||
GenerateKey(keyID string, context Context) (key [32]byte, sealedKey []byte, err error)
|
||||
|
||||
// UnsealKey unseals the sealedKey using the master key
|
||||
// referenced by the keyID. The provided context must
|
||||
// match the context used to generate the sealed key.
|
||||
UnsealKey(keyID string, sealedKey []byte, context Context) (key [32]byte, err error)
|
||||
|
||||
// Info returns descriptive information about the KMS,
|
||||
// like the default key ID and authentication method.
|
||||
Info() KMSInfo
|
||||
}
|
||||
type KMS = kms.KMS
|
||||
|
||||
type masterKeyKMS struct {
|
||||
keyID string
|
||||
masterKey [32]byte
|
||||
}
|
||||
|
||||
// KMSInfo contains some describing information about
|
||||
// the KMS.
|
||||
type KMSInfo struct {
|
||||
Endpoints []string
|
||||
Name string
|
||||
AuthType string
|
||||
}
|
||||
|
||||
// NewMasterKey returns a basic KMS implementation from a single 256 bit master key.
|
||||
//
|
||||
// The KMS accepts any keyID but binds the keyID and context cryptographically
|
||||
// to the generated keys.
|
||||
func NewMasterKey(keyID string, key [32]byte) KMS { return &masterKeyKMS{keyID: keyID, masterKey: key} }
|
||||
|
||||
func (kms *masterKeyKMS) DefaultKeyID() string {
|
||||
return kms.keyID
|
||||
func (m *masterKeyKMS) Stat() (kms.Status, error) {
|
||||
return kms.Status{
|
||||
Name: "MasterKey",
|
||||
DefaultKey: m.keyID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (kms *masterKeyKMS) CreateKey(keyID string) error {
|
||||
func (m *masterKeyKMS) CreateKey(keyID string) error {
|
||||
return errors.New("crypto: creating keys is not supported by a static master key")
|
||||
}
|
||||
|
||||
func (kms *masterKeyKMS) GenerateKey(keyID string, ctx Context) (key [32]byte, sealedKey []byte, err error) {
|
||||
if _, err = io.ReadFull(rand.Reader, key[:]); err != nil {
|
||||
func (m *masterKeyKMS) GenerateKey(keyID string, ctx Context) (kms.DEK, error) {
|
||||
if keyID == "" {
|
||||
keyID = m.keyID
|
||||
}
|
||||
|
||||
var key [32]byte
|
||||
if _, err := io.ReadFull(rand.Reader, key[:]); err != nil {
|
||||
logger.CriticalIf(context.Background(), errOutOfEntropy)
|
||||
}
|
||||
|
||||
var (
|
||||
buffer bytes.Buffer
|
||||
derivedKey = kms.deriveKey(keyID, ctx)
|
||||
derivedKey = m.deriveKey(keyID, ctx)
|
||||
)
|
||||
if n, err := sio.Encrypt(&buffer, bytes.NewReader(key[:]), sio.Config{Key: derivedKey[:]}); err != nil || n != 64 {
|
||||
logger.CriticalIf(context.Background(), errors.New("KMS: unable to encrypt data key"))
|
||||
}
|
||||
sealedKey = buffer.Bytes()
|
||||
return key, sealedKey, nil
|
||||
return kms.DEK{
|
||||
KeyID: m.keyID,
|
||||
Plaintext: key[:],
|
||||
Ciphertext: buffer.Bytes(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// KMS is configured directly using master key
|
||||
func (kms *masterKeyKMS) Info() (info KMSInfo) {
|
||||
return KMSInfo{
|
||||
Endpoints: []string{},
|
||||
Name: "",
|
||||
AuthType: "master-key",
|
||||
}
|
||||
}
|
||||
func (m *masterKeyKMS) DecryptKey(keyID string, sealedKey []byte, ctx Context) ([]byte, error) {
|
||||
var derivedKey = m.deriveKey(keyID, ctx)
|
||||
|
||||
func (kms *masterKeyKMS) UnsealKey(keyID string, sealedKey []byte, ctx Context) (key [32]byte, err error) {
|
||||
var (
|
||||
derivedKey = kms.deriveKey(keyID, ctx)
|
||||
)
|
||||
var key [32]byte
|
||||
out, err := sio.DecryptBuffer(key[:0], sealedKey, sio.Config{Key: derivedKey[:]})
|
||||
if err != nil || len(out) != 32 {
|
||||
return key, err // TODO(aead): upgrade sio to use sio.Error
|
||||
return nil, err // TODO(aead): upgrade sio to use sio.Error
|
||||
}
|
||||
return key, nil
|
||||
return key[:], nil
|
||||
}
|
||||
|
||||
func (kms *masterKeyKMS) deriveKey(keyID string, context Context) (key [32]byte) {
|
||||
func (m *masterKeyKMS) deriveKey(keyID string, context Context) (key [32]byte) {
|
||||
if context == nil {
|
||||
context = Context{}
|
||||
}
|
||||
ctxBytes, _ := context.MarshalText()
|
||||
|
||||
mac := hmac.New(sha256.New, kms.masterKey[:])
|
||||
mac := hmac.New(sha256.New, m.masterKey[:])
|
||||
mac.Write([]byte(keyID))
|
||||
mac.Write(ctxBytes)
|
||||
mac.Sum(key[:0])
|
||||
|
||||
@@ -43,18 +43,18 @@ func TestMasterKeyKMS(t *testing.T) {
|
||||
for i, test := range masterKeyKMSTests {
|
||||
kms := NewMasterKey(test.GenKeyID, [32]byte{})
|
||||
|
||||
key, sealedKey, err := kms.GenerateKey(test.GenKeyID, test.GenContext)
|
||||
key, err := kms.GenerateKey(test.GenKeyID, test.GenContext)
|
||||
if err != nil {
|
||||
t.Errorf("Test %d: KMS failed to generate key: %v", i, err)
|
||||
}
|
||||
unsealedKey, err := kms.UnsealKey(test.UnsealKeyID, sealedKey, test.UnsealContext)
|
||||
unsealedKey, err := kms.DecryptKey(test.UnsealKeyID, key.Ciphertext, test.UnsealContext)
|
||||
if err != nil && !test.ShouldFail {
|
||||
t.Errorf("Test %d: KMS failed to unseal the generated key: %v", i, err)
|
||||
}
|
||||
if err == nil && test.ShouldFail {
|
||||
t.Errorf("Test %d: KMS unsealed the generated key successfully but should have failed", i)
|
||||
}
|
||||
if !test.ShouldFail && !bytes.Equal(key[:], unsealedKey[:]) {
|
||||
if !test.ShouldFail && !bytes.Equal(key.Plaintext, unsealedKey[:]) {
|
||||
t.Errorf("Test %d: The generated and unsealed key differ", i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +51,11 @@ func TestParseMasterKey(t *testing.T) {
|
||||
if !tt.success && err == nil {
|
||||
t.Error("Unexpected failure")
|
||||
}
|
||||
if err == nil && kms.DefaultKeyID() != tt.expectedKeyID {
|
||||
t.Errorf("Expected keyID %s, got %s", tt.expectedKeyID, kms.DefaultKeyID())
|
||||
if kms != nil {
|
||||
stat, _ := kms.Stat()
|
||||
if err == nil && stat.DefaultKey != tt.expectedKeyID {
|
||||
t.Errorf("Expected keyID %s, got %s", tt.expectedKeyID, stat.DefaultKey)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func (s3 ssec) UnsealObjectKey(h http.Header, metadata map[string]string, bucket
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return unsealObjectKey(clientKey, metadata, bucket, object)
|
||||
return unsealObjectKey(clientKey[:], metadata, bucket, object)
|
||||
}
|
||||
|
||||
// CreateMetadata encodes the sealed key into the metadata
|
||||
|
||||
@@ -96,11 +96,11 @@ func (s3 ssekms) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, ob
|
||||
if _, ok := ctx[bucket]; !ok {
|
||||
ctx[bucket] = path.Join(bucket, object)
|
||||
}
|
||||
unsealKey, err := kms.UnsealKey(keyID, kmsKey, ctx)
|
||||
unsealKey, err := kms.DecryptKey(keyID, kmsKey, ctx)
|
||||
if err != nil {
|
||||
return key, err
|
||||
}
|
||||
err = key.Unseal(unsealKey, sealedKey, s3.String(), bucket, object)
|
||||
err = key.Unseal(unsealKey[:], sealedKey, s3.String(), bucket, object)
|
||||
return key, err
|
||||
}
|
||||
|
||||
|
||||
@@ -73,11 +73,11 @@ func (s3 sses3) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, obj
|
||||
if err != nil {
|
||||
return key, err
|
||||
}
|
||||
unsealKey, err := kms.UnsealKey(keyID, kmsKey, Context{bucket: path.Join(bucket, object)})
|
||||
unsealKey, err := kms.DecryptKey(keyID, kmsKey, Context{bucket: path.Join(bucket, object)})
|
||||
if err != nil {
|
||||
return key, err
|
||||
}
|
||||
err = key.Unseal(unsealKey, sealedKey, s3.String(), bucket, object)
|
||||
err = key.Unseal(unsealKey[:], sealedKey, s3.String(), bucket, object)
|
||||
return key, err
|
||||
}
|
||||
|
||||
|
||||
@@ -77,12 +77,12 @@ func (sse ssecCopy) UnsealObjectKey(h http.Header, metadata map[string]string, b
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return unsealObjectKey(clientKey, metadata, bucket, object)
|
||||
return unsealObjectKey(clientKey[:], metadata, bucket, object)
|
||||
}
|
||||
|
||||
// unsealObjectKey decrypts and returns the sealed object key
|
||||
// from the metadata using the SSE-C client key.
|
||||
func unsealObjectKey(clientKey [32]byte, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
||||
func unsealObjectKey(clientKey []byte, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
|
||||
sealedKey, err := SSEC.ParseMetadata(metadata)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"time"
|
||||
|
||||
vault "github.com/hashicorp/vault/api"
|
||||
"github.com/minio/minio/pkg/kms"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -190,22 +191,15 @@ func (v *vaultService) authenticate() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// DefaultKeyID returns the default key ID that should be
|
||||
// used for SSE-S3 or SSE-KMS when the S3 client does not
|
||||
// provide an explicit key ID.
|
||||
func (v *vaultService) DefaultKeyID() string {
|
||||
return v.config.Key.Name
|
||||
}
|
||||
|
||||
// Info returns some information about the Vault,
|
||||
// configuration - like the endpoints or authentication
|
||||
// method.
|
||||
func (v *vaultService) Info() KMSInfo {
|
||||
return KMSInfo{
|
||||
Endpoints: []string{v.config.Endpoint},
|
||||
Name: v.DefaultKeyID(),
|
||||
AuthType: v.config.Auth.Type,
|
||||
}
|
||||
func (v *vaultService) Stat() (kms.Status, error) {
|
||||
return kms.Status{
|
||||
Endpoints: []string{v.config.Endpoint},
|
||||
Name: "Hashicorp Vault",
|
||||
DefaultKey: v.config.Key.Name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateKey is a stub that exists such that the Vault
|
||||
@@ -222,10 +216,13 @@ func (v *vaultService) CreateKey(keyID string) error {
|
||||
// and a sealed version of this plaintext key encrypted using the
|
||||
// named key referenced by keyID. It also binds the generated key
|
||||
// cryptographically to the provided context.
|
||||
func (v *vaultService) GenerateKey(keyID string, ctx Context) (key [32]byte, sealedKey []byte, err error) {
|
||||
func (v *vaultService) GenerateKey(keyID string, ctx Context) (kms.DEK, error) {
|
||||
if keyID == "" {
|
||||
keyID = v.config.Key.Name
|
||||
}
|
||||
context, err := ctx.MarshalText()
|
||||
if err != nil {
|
||||
return key, sealedKey, err
|
||||
return kms.DEK{}, err
|
||||
}
|
||||
|
||||
payload := map[string]interface{}{
|
||||
@@ -233,24 +230,27 @@ func (v *vaultService) GenerateKey(keyID string, ctx Context) (key [32]byte, sea
|
||||
}
|
||||
s, err := v.client.Logical().Write(fmt.Sprintf("/transit/datakey/plaintext/%s", keyID), payload)
|
||||
if err != nil {
|
||||
return key, sealedKey, Errorf("crypto: client error %w", err)
|
||||
return kms.DEK{}, Errorf("crypto: client error %w", err)
|
||||
}
|
||||
sealKey, ok := s.Data["ciphertext"].(string)
|
||||
if !ok {
|
||||
return key, sealedKey, Errorf("crypto: incorrect 'ciphertext' key type %v", s.Data["ciphertext"])
|
||||
return kms.DEK{}, Errorf("crypto: incorrect 'ciphertext' key type %v", s.Data["ciphertext"])
|
||||
}
|
||||
|
||||
plainKeyB64, ok := s.Data["plaintext"].(string)
|
||||
if !ok {
|
||||
return key, sealedKey, Errorf("crypto: incorrect 'plaintext' key type %v", s.Data["plaintext"])
|
||||
return kms.DEK{}, Errorf("crypto: incorrect 'plaintext' key type %v", s.Data["plaintext"])
|
||||
}
|
||||
|
||||
plainKey, err := base64.StdEncoding.DecodeString(plainKeyB64)
|
||||
if err != nil {
|
||||
return key, sealedKey, Errorf("crypto: invalid base64 key %w", err)
|
||||
return kms.DEK{}, Errorf("crypto: invalid base64 key %w", err)
|
||||
}
|
||||
copy(key[:], plainKey)
|
||||
return key, []byte(sealKey), nil
|
||||
return kms.DEK{
|
||||
KeyID: keyID,
|
||||
Plaintext: plainKey,
|
||||
Ciphertext: []byte(sealKey),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UnsealKey returns the decrypted sealedKey as plaintext key.
|
||||
@@ -260,10 +260,10 @@ func (v *vaultService) GenerateKey(keyID string, ctx Context) (key [32]byte, sea
|
||||
//
|
||||
// The context must be same context as the one provided while
|
||||
// generating the plaintext key / sealedKey.
|
||||
func (v *vaultService) UnsealKey(keyID string, sealedKey []byte, ctx Context) (key [32]byte, err error) {
|
||||
func (v *vaultService) DecryptKey(keyID string, sealedKey []byte, ctx Context) ([]byte, error) {
|
||||
context, err := ctx.MarshalText()
|
||||
if err != nil {
|
||||
return key, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payload := map[string]interface{}{
|
||||
@@ -273,18 +273,17 @@ func (v *vaultService) UnsealKey(keyID string, sealedKey []byte, ctx Context) (k
|
||||
|
||||
s, err := v.client.Logical().Write(fmt.Sprintf("/transit/decrypt/%s", keyID), payload)
|
||||
if err != nil {
|
||||
return key, Errorf("crypto: client error %w", err)
|
||||
return nil, Errorf("crypto: client error %w", err)
|
||||
}
|
||||
|
||||
base64Key, ok := s.Data["plaintext"].(string)
|
||||
if !ok {
|
||||
return key, Errorf("crypto: incorrect 'plaintext' key type %v", s.Data["plaintext"])
|
||||
return nil, Errorf("crypto: incorrect 'plaintext' key type %v", s.Data["plaintext"])
|
||||
}
|
||||
|
||||
plainKey, err := base64.StdEncoding.DecodeString(base64Key)
|
||||
if err != nil {
|
||||
return key, Errorf("crypto: invalid base64 key %w", err)
|
||||
return nil, Errorf("crypto: invalid base64 key %w", err)
|
||||
}
|
||||
copy(key[:], plainKey)
|
||||
return key, nil
|
||||
return plainKey, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user