diff --git a/cmd/bucket-metadata.go b/cmd/bucket-metadata.go index d0132c1c9..100c3836e 100644 --- a/cmd/bucket-metadata.go +++ b/cmd/bucket-metadata.go @@ -555,7 +555,7 @@ func encryptBucketMetadata(ctx context.Context, bucket string, input []byte, kms outbuf := bytes.NewBuffer(nil) objectKey := crypto.GenerateKey(key.Plaintext, rand.Reader) sealedKey := objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, "") - crypto.S3.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey) + crypto.S3.CreateMetadata(metadata, key, sealedKey) _, err = sio.Encrypt(outbuf, bytes.NewBuffer(input), sio.Config{Key: objectKey[:], MinVersion: sio.Version20, CipherSuites: fips.DARECiphers()}) if err != nil { return output, metabytes, err diff --git a/cmd/encryption-v1.go b/cmd/encryption-v1.go index 9935ff9bd..5569b6379 100644 --- a/cmd/encryption-v1.go +++ b/cmd/encryption-v1.go @@ -293,7 +293,7 @@ func rotateKey(ctx context.Context, oldKey []byte, newKeyID string, newKey []byt return err } sealedKey = objectKey.Seal(newKey.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object) - crypto.S3.CreateMetadata(metadata, newKey.KeyID, newKey.Ciphertext, sealedKey) + crypto.S3.CreateMetadata(metadata, newKey, sealedKey) return nil case crypto.S3KMS: if GlobalKMS == nil { @@ -333,7 +333,7 @@ func rotateKey(ctx context.Context, oldKey []byte, newKeyID string, newKey []byt } sealedKey := objectKey.Seal(newKey.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3KMS.String(), bucket, object) - crypto.S3KMS.CreateMetadata(metadata, newKey.KeyID, newKey.Ciphertext, sealedKey, cryptoCtx) + crypto.S3KMS.CreateMetadata(metadata, newKey, sealedKey, cryptoCtx) return nil case crypto.SSEC: sealedKey, err := crypto.SSEC.ParseMetadata(metadata) @@ -376,7 +376,7 @@ func newEncryptMetadata(ctx context.Context, kind crypto.Type, keyID string, key objectKey := crypto.GenerateKey(key.Plaintext, rand.Reader) sealedKey = objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object) - crypto.S3.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey) + crypto.S3.CreateMetadata(metadata, key, sealedKey) return objectKey, nil case crypto.S3KMS: if GlobalKMS == nil { @@ -409,7 +409,7 @@ func newEncryptMetadata(ctx context.Context, kind crypto.Type, keyID string, key objectKey := crypto.GenerateKey(key.Plaintext, rand.Reader) sealedKey = objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3KMS.String(), bucket, object) - crypto.S3KMS.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey, cryptoCtx) + crypto.S3KMS.CreateMetadata(metadata, key, sealedKey, cryptoCtx) return objectKey, nil case crypto.SSEC: objectKey := crypto.GenerateKey(key, rand.Reader) diff --git a/cmd/kms-handlers.go b/cmd/kms-handlers.go index ce5017c1f..ba6f9ef5a 100644 --- a/cmd/kms-handlers.go +++ b/cmd/kms-handlers.go @@ -280,6 +280,7 @@ func (a kmsAPIHandlers) KMSKeyStatusHandler(w http.ResponseWriter, r *http.Reque // 2. Verify that we can indeed decrypt the (encrypted) key decryptedKey, err := GlobalKMS.Decrypt(ctx, &kms.DecryptRequest{ Name: key.KeyID, + Version: key.Version, Ciphertext: key.Ciphertext, AssociatedData: kmsContext, }) diff --git a/cmd/metacache-stream.go b/cmd/metacache-stream.go index 0925683d5..cf61895fe 100644 --- a/cmd/metacache-stream.go +++ b/cmd/metacache-stream.go @@ -758,7 +758,7 @@ func (r *metacacheReader) Close() error { return nil } -// metacacheBlockWriter collects blocks and provides a callaback to store them. +// metacacheBlockWriter collects blocks and provides a callback to store them. type metacacheBlockWriter struct { wg sync.WaitGroup streamErr error diff --git a/go.mod b/go.mod index ddac6e94a..7e3ad2606 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,7 @@ require ( github.com/minio/dnscache v0.1.1 github.com/minio/dperf v0.6.3 github.com/minio/highwayhash v1.0.3 - github.com/minio/kms-go/kes v0.3.1 + github.com/minio/kms-go/kes v0.3.2-0.20250505160844-240efef6bb74 github.com/minio/kms-go/kms v0.5.1-0.20250225090116-4e64ce8d0f35 github.com/minio/madmin-go/v3 v3.0.109 github.com/minio/minio-go/v7 v7.0.91 diff --git a/go.sum b/go.sum index b43a2a370..3a59a6f4b 100644 --- a/go.sum +++ b/go.sum @@ -436,8 +436,8 @@ github.com/minio/filepath v1.0.0/go.mod h1:/nRZA2ldl5z6jT9/KQuvZcQlxZIMQoFFQPvEX github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= -github.com/minio/kms-go/kes v0.3.1 h1:K3sPFAvFbJx33XlCTUBnQo8JRmSZyDvT6T2/MQ2iC3A= -github.com/minio/kms-go/kes v0.3.1/go.mod h1:Q9Ct0KUAuN9dH0hSVa0eva45Jg99cahbZpPxeqR9rOQ= +github.com/minio/kms-go/kes v0.3.2-0.20250505160844-240efef6bb74 h1:iUY0/rQ66zmowoB94yGBTk3I2ljOoD5jSIKwkhxkwe4= +github.com/minio/kms-go/kes v0.3.2-0.20250505160844-240efef6bb74/go.mod h1:Q9Ct0KUAuN9dH0hSVa0eva45Jg99cahbZpPxeqR9rOQ= github.com/minio/kms-go/kms v0.5.1-0.20250225090116-4e64ce8d0f35 h1:ISNz42SPD+heeHhpl9bwMRRusPTCsbYKd1YoED265E0= github.com/minio/kms-go/kms v0.5.1-0.20250225090116-4e64ce8d0f35/go.mod h1:JFQu2srrnWxMn6KcwS5347oTwNKW7nkewgBlrodjF9k= github.com/minio/madmin-go/v3 v3.0.109 h1:hRHlJ6yaIB3tlIj5mz9L9mGcyLC37S9qL1WtFrRtyQ0= diff --git a/internal/config/crypto.go b/internal/config/crypto.go index ecacdbca5..14e2703d6 100644 --- a/internal/config/crypto.go +++ b/internal/config/crypto.go @@ -92,6 +92,7 @@ func Encrypt(k *kms.KMS, plaintext io.Reader, ctx kms.Context) (io.Reader, error json := jsoniter.ConfigCompatibleWithStandardLibrary metadata, err := json.Marshal(encryptedObject{ KeyID: key.KeyID, + Version: key.Version, KMSKey: key.Ciphertext, Algorithm: algorithm, Nonce: nonce, @@ -151,6 +152,7 @@ func Decrypt(k *kms.KMS, ciphertext io.Reader, associatedData kms.Context) (io.R key, err := k.Decrypt(context.TODO(), &kms.DecryptRequest{ Name: metadata.KeyID, + Version: metadata.Version, Ciphertext: metadata.KMSKey, AssociatedData: associatedData, }) @@ -168,8 +170,9 @@ func Decrypt(k *kms.KMS, ciphertext io.Reader, associatedData kms.Context) (io.R } type encryptedObject struct { - KeyID string `json:"keyid"` - KMSKey []byte `json:"kmskey"` + KeyID string `json:"keyid"` + Version string `json:"version"` + KMSKey []byte `json:"kmskey"` Algorithm sio.Algorithm `json:"algorithm"` Nonce []byte `json:"nonce"` diff --git a/internal/crypto/metadata.go b/internal/crypto/metadata.go index 43e8cfe4f..0fb03b836 100644 --- a/internal/crypto/metadata.go +++ b/internal/crypto/metadata.go @@ -48,6 +48,12 @@ const ( // the KMS. MetaDataEncryptionKey = "X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key" + // MetaKeyVersion is the version of the KMS master key used to generate/encrypt + // the data encryption key (DEK). When a MinKMS master key is "rotated", it + // adds another key version. MinIO has to remember which key version has been + // used to encrypt an object. + MetaKeyVersion = "X-Minio-Internal-Server-Side-Encryption-S3-Kms-Key-Version" + // MetaSsecCRC is the encrypted checksum of the SSE-C encrypted object. MetaSsecCRC = "X-Minio-Replication-Ssec-Crc" @@ -108,6 +114,7 @@ func RemoveInternalEntries(metadata map[string]string) { delete(metadata, MetaSealedKeyS3) delete(metadata, MetaSealedKeyKMS) delete(metadata, MetaKeyID) + delete(metadata, MetaKeyVersion) delete(metadata, MetaDataEncryptionKey) delete(metadata, MetaSsecCRC) } @@ -150,6 +157,9 @@ func IsEncrypted(metadata map[string]string) (Type, bool) { if _, ok := metadata[MetaKeyID]; ok { return nil, true } + if _, ok := metadata[MetaKeyVersion]; ok { + return nil, true + } if _, ok := metadata[MetaDataEncryptionKey]; ok { return nil, true } diff --git a/internal/crypto/metadata_test.go b/internal/crypto/metadata_test.go index 612cf19c2..566dad7f0 100644 --- a/internal/crypto/metadata_test.go +++ b/internal/crypto/metadata_test.go @@ -23,6 +23,7 @@ import ( "encoding/hex" "testing" + "github.com/minio/minio/internal/kms" "github.com/minio/minio/internal/logger" ) @@ -306,7 +307,7 @@ var s3CreateMetadataTests = []struct { SealedDataKey []byte SealedKey SealedKey }{ - {KeyID: "", SealedDataKey: nil, SealedKey: SealedKey{Algorithm: SealAlgorithm}}, + {KeyID: "foo", SealedDataKey: make([]byte, 32), SealedKey: SealedKey{Algorithm: SealAlgorithm}}, {KeyID: "my-minio-key", SealedDataKey: make([]byte, 48), SealedKey: SealedKey{Algorithm: SealAlgorithm}}, {KeyID: "cafebabe", SealedDataKey: make([]byte, 48), SealedKey: SealedKey{Algorithm: SealAlgorithm}}, {KeyID: "deadbeef", SealedDataKey: make([]byte, 32), SealedKey: SealedKey{IV: [32]byte{0xf7}, Key: [64]byte{0xea}, Algorithm: SealAlgorithm}}, @@ -316,7 +317,7 @@ func TestS3CreateMetadata(t *testing.T) { defer func(l bool) { logger.DisableLog = l }(logger.DisableLog) logger.DisableLog = true for i, test := range s3CreateMetadataTests { - metadata := S3.CreateMetadata(nil, test.KeyID, test.SealedDataKey, test.SealedKey) + metadata := S3.CreateMetadata(nil, kms.DEK{KeyID: test.KeyID, Ciphertext: test.SealedDataKey}, test.SealedKey) keyID, kmsKey, sealedKey, err := S3.ParseMetadata(metadata) if err != nil { t.Errorf("Test %d: failed to parse metadata: %v", i, err) @@ -344,7 +345,7 @@ func TestS3CreateMetadata(t *testing.T) { t.Errorf("Expected '%s' panic for invalid seal algorithm but got '%s'", logger.ErrCritical, err) } }() - _ = S3.CreateMetadata(nil, "", []byte{}, SealedKey{Algorithm: InsecureSealAlgorithm}) + _ = S3.CreateMetadata(nil, kms.DEK{}, SealedKey{Algorithm: InsecureSealAlgorithm}) } var ssecCreateMetadataTests = []struct { diff --git a/internal/crypto/sse-kms.go b/internal/crypto/sse-kms.go index dd0aa46a2..fc80366a3 100644 --- a/internal/crypto/sse-kms.go +++ b/internal/crypto/sse-kms.go @@ -136,20 +136,15 @@ func (s3 ssekms) UnsealObjectKey(k *kms.KMS, metadata map[string]string, bucket, // the modified metadata. If the keyID and the kmsKey is not empty it encodes // both into the metadata as well. It allocates a new metadata map if metadata // is nil. -func (ssekms) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey, ctx kms.Context) map[string]string { +func (ssekms) CreateMetadata(metadata map[string]string, dek kms.DEK, sealedKey SealedKey, ctx kms.Context) map[string]string { if sealedKey.Algorithm != SealAlgorithm { logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm)) } - - // There are two possibilities: - // - We use a KMS -> There must be non-empty key ID and a KMS data key. - // - We use a K/V -> There must be no key ID and no KMS data key. - // Otherwise, the caller has passed an invalid argument combination. - if keyID == "" && len(kmsKey) != 0 { - logger.CriticalIf(context.Background(), errors.New("The key ID must not be empty if a KMS data key is present")) + if dek.KeyID == "" { + logger.CriticalIf(context.Background(), errors.New("The key ID must not be empty")) } - if keyID != "" && len(kmsKey) == 0 { - logger.CriticalIf(context.Background(), errors.New("The KMS data key must not be empty if a key ID is present")) + if len(dek.Ciphertext) == 0 { + logger.CriticalIf(context.Background(), errors.New("The DEK must not be empty")) } if metadata == nil { @@ -159,13 +154,18 @@ func (ssekms) CreateMetadata(metadata map[string]string, keyID string, kmsKey [] metadata[MetaAlgorithm] = sealedKey.Algorithm metadata[MetaIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:]) metadata[MetaSealedKeyKMS] = base64.StdEncoding.EncodeToString(sealedKey.Key[:]) + metadata[MetaKeyID] = dek.KeyID + metadata[MetaDataEncryptionKey] = base64.StdEncoding.EncodeToString(dek.Ciphertext) + if len(ctx) > 0 { - b, _ := ctx.MarshalText() + b, err := ctx.MarshalText() + if err != nil { + logger.CriticalIf(context.Background(), Errorf("crypto: failed to marshal KMS context: %v", err)) + } metadata[MetaContext] = base64.StdEncoding.EncodeToString(b) } - if len(kmsKey) > 0 && keyID != "" { // We use a KMS -> Store key ID and sealed KMS data key. - metadata[MetaKeyID] = keyID - metadata[MetaDataEncryptionKey] = base64.StdEncoding.EncodeToString(kmsKey) + if dek.Version != "" { + metadata[MetaKeyVersion] = dek.Version } return metadata } diff --git a/internal/crypto/sse-s3.go b/internal/crypto/sse-s3.go index ce34d5a4f..28bf3ccdb 100644 --- a/internal/crypto/sse-s3.go +++ b/internal/crypto/sse-s3.go @@ -119,20 +119,15 @@ func (s3 sses3) UnsealObjectKeys(ctx context.Context, k *kms.KMS, metadata []map // the modified metadata. If the keyID and the kmsKey is not empty it encodes // both into the metadata as well. It allocates a new metadata map if metadata // is nil. -func (sses3) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string { +func (sses3) CreateMetadata(metadata map[string]string, dek kms.DEK, sealedKey SealedKey) map[string]string { if sealedKey.Algorithm != SealAlgorithm { logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm)) } - - // There are two possibilities: - // - We use a KMS -> There must be non-empty key ID and a KMS data key. - // - We use a K/V -> There must be no key ID and no KMS data key. - // Otherwise, the caller has passed an invalid argument combination. - if keyID == "" && len(kmsKey) != 0 { - logger.CriticalIf(context.Background(), errors.New("The key ID must not be empty if a KMS data key is present")) + if dek.KeyID == "" { + logger.CriticalIf(context.Background(), errors.New("The key ID must not be empty")) } - if keyID != "" && len(kmsKey) == 0 { - logger.CriticalIf(context.Background(), errors.New("The KMS data key must not be empty if a key ID is present")) + if len(dek.Ciphertext) == 0 { + logger.CriticalIf(context.Background(), errors.New("The DEK must not be empty")) } if metadata == nil { @@ -142,9 +137,10 @@ func (sses3) CreateMetadata(metadata map[string]string, keyID string, kmsKey []b metadata[MetaAlgorithm] = sealedKey.Algorithm metadata[MetaIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:]) metadata[MetaSealedKeyS3] = base64.StdEncoding.EncodeToString(sealedKey.Key[:]) - if len(kmsKey) > 0 && keyID != "" { // We use a KMS -> Store key ID and sealed KMS data key. - metadata[MetaKeyID] = keyID - metadata[MetaDataEncryptionKey] = base64.StdEncoding.EncodeToString(kmsKey) + metadata[MetaKeyID] = dek.KeyID + metadata[MetaDataEncryptionKey] = base64.StdEncoding.EncodeToString(dek.Ciphertext) + if dek.Version != "" { + metadata[MetaKeyVersion] = dek.Version } return metadata } diff --git a/internal/kms/conn.go b/internal/kms/conn.go index c63fcb6cb..b63fe2140 100644 --- a/internal/kms/conn.go +++ b/internal/kms/conn.go @@ -19,11 +19,8 @@ package kms import ( "context" - "encoding" - "encoding/json" "strconv" - jsoniter "github.com/json-iterator/go" "github.com/minio/madmin-go/v3" ) @@ -121,47 +118,7 @@ type Status struct { // storage. type DEK struct { KeyID string // Name of the master key - Version int // Version of the master key (MinKMS only) + Version string // Version of the master key Plaintext []byte // Paintext of the data encryption key Ciphertext []byte // Ciphertext of the data encryption key } - -var ( - _ encoding.TextMarshaler = (*DEK)(nil) - _ encoding.TextUnmarshaler = (*DEK)(nil) -) - -// MarshalText encodes the DEK's key ID and ciphertext -// as JSON. -func (d DEK) MarshalText() ([]byte, error) { - type JSON struct { - KeyID string `json:"keyid"` - Version uint32 `json:"version,omitempty"` - Ciphertext []byte `json:"ciphertext"` - } - return json.Marshal(JSON{ - KeyID: d.KeyID, - Version: uint32(d.Version), - Ciphertext: d.Ciphertext, - }) -} - -// UnmarshalText tries to decode text as JSON representation -// of a DEK and sets DEK's key ID and ciphertext to the -// decoded values. -// -// It sets DEK's plaintext to nil. -func (d *DEK) UnmarshalText(text []byte) error { - type JSON struct { - KeyID string `json:"keyid"` - Version uint32 `json:"version"` - Ciphertext []byte `json:"ciphertext"` - } - var v JSON - json := jsoniter.ConfigCompatibleWithStandardLibrary - if err := json.Unmarshal(text, &v); err != nil { - return err - } - d.KeyID, d.Version, d.Plaintext, d.Ciphertext = v.KeyID, int(v.Version), nil, v.Ciphertext - return nil -} diff --git a/internal/kms/dek_test.go b/internal/kms/dek_test.go deleted file mode 100644 index 12ab164d5..000000000 --- a/internal/kms/dek_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2015-2021 MinIO, Inc. -// -// This file is part of MinIO Object Storage stack -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -package kms - -import ( - "bytes" - "encoding/base64" - "testing" -) - -var dekEncodeDecodeTests = []struct { - Key DEK -}{ - { - Key: DEK{}, - }, - { - Key: DEK{ - Plaintext: nil, - Ciphertext: mustDecodeB64("eyJhZWFkIjoiQUVTLTI1Ni1HQ00tSE1BQy1TSEEtMjU2IiwiaXYiOiJ3NmhLUFVNZXVtejZ5UlVZL29pTFVBPT0iLCJub25jZSI6IktMSEU3UE1jRGo2N2UweHkiLCJieXRlcyI6Ik1wUkhjQWJaTzZ1Sm5lUGJGcnpKTkxZOG9pdkxwTmlUcTNLZ0hWdWNGYkR2Y0RlbEh1c1lYT29zblJWVTZoSXIifQ=="), - }, - }, - { - Key: DEK{ - Plaintext: mustDecodeB64("GM2UvLXp/X8lzqq0mibFC0LayDCGlmTHQhYLj7qAy7Q="), - Ciphertext: mustDecodeB64("eyJhZWFkIjoiQUVTLTI1Ni1HQ00tSE1BQy1TSEEtMjU2IiwiaXYiOiJ3NmhLUFVNZXVtejZ5UlVZL29pTFVBPT0iLCJub25jZSI6IktMSEU3UE1jRGo2N2UweHkiLCJieXRlcyI6Ik1wUkhjQWJaTzZ1Sm5lUGJGcnpKTkxZOG9pdkxwTmlUcTNLZ0hWdWNGYkR2Y0RlbEh1c1lYT29zblJWVTZoSXIifQ=="), - }, - }, - { - Key: DEK{ - Version: 3, - Plaintext: mustDecodeB64("GM2UvLXp/X8lzqq0mibFC0LayDCGlmTHQhYLj7qAy7Q="), - Ciphertext: mustDecodeB64("eyJhZWFkIjoiQUVTLTI1Ni1HQ00tSE1BQy1TSEEtMjU2IiwiaXYiOiJ3NmhLUFVNZXVtejZ5UlVZL29pTFVBPT0iLCJub25jZSI6IktMSEU3UE1jRGo2N2UweHkiLCJieXRlcyI6Ik1wUkhjQWJaTzZ1Sm5lUGJGcnpKTkxZOG9pdkxwTmlUcTNLZ0hWdWNGYkR2Y0RlbEh1c1lYT29zblJWVTZoSXIifQ=="), - }, - }, -} - -func TestEncodeDecodeDEK(t *testing.T) { - for i, test := range dekEncodeDecodeTests { - text, err := test.Key.MarshalText() - if err != nil { - t.Fatalf("Test %d: failed to marshal DEK: %v", i, err) - } - - var key DEK - if err = key.UnmarshalText(text); err != nil { - t.Fatalf("Test %d: failed to unmarshal DEK: %v", i, err) - } - if key.Plaintext != nil { - t.Fatalf("Test %d: unmarshaled DEK contains non-nil plaintext", i) - } - if !bytes.Equal(key.Ciphertext, test.Key.Ciphertext) { - t.Fatalf("Test %d: ciphertext mismatch: got %x - want %x", i, key.Ciphertext, test.Key.Ciphertext) - } - } -} - -func mustDecodeB64(s string) []byte { - b, err := base64.StdEncoding.DecodeString(s) - if err != nil { - panic(err) - } - return b -} diff --git a/internal/kms/kes.go b/internal/kms/kes.go index 8bd387c27..27b641a95 100644 --- a/internal/kms/kes.go +++ b/internal/kms/kes.go @@ -206,6 +206,7 @@ func (c *kesConn) GenerateKey(ctx context.Context, req *GenerateKeyRequest) (DEK KeyID: name, Plaintext: dek.Plaintext, Ciphertext: dek.Ciphertext, + Version: dek.Version, }, nil } @@ -235,7 +236,7 @@ func (c *kesConn) Decrypt(ctx context.Context, req *DecryptRequest) ([]byte, err return nil, err } - plaintext, err := c.client.Decrypt(context.Background(), req.Name, req.Ciphertext, aad) + plaintext, err := c.client.Decrypt(ctx, req.Name, req.Version, req.Ciphertext, aad) if err != nil { if errors.Is(err, kes.ErrKeyNotFound) { return nil, ErrKeyNotFound diff --git a/internal/kms/kms.go b/internal/kms/kms.go index 414d3795f..bc4d4d844 100644 --- a/internal/kms/kms.go +++ b/internal/kms/kms.go @@ -22,6 +22,7 @@ import ( "errors" "net/http" "slices" + "strconv" "sync/atomic" "time" @@ -90,7 +91,7 @@ type DecryptRequest struct { // Version is the version of the master used for // decryption. If empty, the latest key version // is used. - Version int + Version string // Ciphertext is the encrypted data that gets // decrypted. @@ -383,7 +384,7 @@ func (c *kmsConn) GenerateKey(ctx context.Context, req *GenerateKeyRequest) (DEK return DEK{ KeyID: name, - Version: resp[0].Version, + Version: strconv.Itoa(resp[0].Version), Plaintext: resp[0].Plaintext, Ciphertext: resp[0].Ciphertext, }, nil @@ -395,9 +396,17 @@ func (c *kmsConn) Decrypt(ctx context.Context, req *DecryptRequest) ([]byte, err return nil, err } + version := 1 + if req.Version != "" { + if version, err = strconv.Atoi(req.Version); err != nil { + return nil, err + } + } + ciphertext, _ := parseCiphertext(req.Ciphertext) resp, err := c.client.Decrypt(ctx, c.enclave, &kms.DecryptRequest{ Name: req.Name, + Version: version, Ciphertext: ciphertext, AssociatedData: aad, }) diff --git a/internal/kms/secret-key.go b/internal/kms/secret-key.go index 8db53bd55..6942c11ce 100644 --- a/internal/kms/secret-key.go +++ b/internal/kms/secret-key.go @@ -154,7 +154,6 @@ func (s secretKey) GenerateKey(_ context.Context, req *GenerateKeyRequest) (DEK, ciphertext = append(ciphertext, random...) return DEK{ KeyID: req.Name, - Version: 0, Plaintext: plaintext, Ciphertext: ciphertext, }, nil diff --git a/internal/kms/stub.go b/internal/kms/stub.go index 2df1e9d8b..0ffbf5e2e 100644 --- a/internal/kms/stub.go +++ b/internal/kms/stub.go @@ -103,7 +103,7 @@ func (s StubKMS) GenerateKey(_ context.Context, req *GenerateKeyRequest) (DEK, e } return DEK{ KeyID: req.Name, - Version: 0, + Version: "0", Plaintext: []byte("stubplaincharswhichare32bytelong"), Ciphertext: []byte("stubplaincharswhichare32bytelong"), }, nil