prepare SSE-S3 metadata parsing for K/V data key store (#8259)

This commit allows the MinIO server to parse the metadata if:
 - either the `X-Minio-Internal-Server-Side-Encryption-S3-Key-Id`
   and the `X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key`
   entries are present.
 - or *both* headers are not present.

This is in service to support a K/V data key store.
master
Andreas Auernhammer 5 years ago committed by kannappanr
parent 8dc897b5f5
commit e34369c860
  1. 46
      cmd/crypto/metadata.go
  2. 6
      cmd/crypto/metadata_test.go

@ -17,6 +17,7 @@ package crypto
import ( import (
"context" "context"
"encoding/base64" "encoding/base64"
"errors"
"fmt" "fmt"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
@ -119,28 +120,46 @@ func CreateMultipartMetadata(metadata map[string]string) map[string]string {
return metadata return metadata
} }
// CreateMetadata encodes the keyID, the sealed kms data key and the sealed key // CreateMetadata encodes the sealed object key into the metadata and returns
// into the metadata and returns the modified metadata. It allocates a new // the modified metadata. If the keyID and the kmsKey is not empty it encodes
// metadata map if metadata is nil. // both into the metadata as well. It allocates a new metadata map if metadata
// is nil.
func (s3) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string { func (s3) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string {
if sealedKey.Algorithm != SealAlgorithm { if sealedKey.Algorithm != SealAlgorithm {
logger.CriticalIf(context.Background(), fmt.Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm)) logger.CriticalIf(context.Background(), fmt.Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm))
} }
// There are two possibilites:
// - 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 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 metadata == nil { if metadata == nil {
metadata = map[string]string{} metadata = map[string]string{}
} }
metadata[S3KMSKeyID] = keyID
metadata[SSESealAlgorithm] = sealedKey.Algorithm metadata[SSESealAlgorithm] = sealedKey.Algorithm
metadata[SSEIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:]) metadata[SSEIV] = base64.StdEncoding.EncodeToString(sealedKey.IV[:])
metadata[S3SealedKey] = base64.StdEncoding.EncodeToString(sealedKey.Key[:]) metadata[S3SealedKey] = base64.StdEncoding.EncodeToString(sealedKey.Key[:])
if len(kmsKey) > 0 && keyID != "" { // We use a KMS -> Store key ID and sealed KMS data key.
metadata[S3KMSKeyID] = keyID
metadata[S3KMSSealedKey] = base64.StdEncoding.EncodeToString(kmsKey) metadata[S3KMSSealedKey] = base64.StdEncoding.EncodeToString(kmsKey)
}
return metadata return metadata
} }
// ParseMetadata extracts all SSE-S3 related values from the object metadata // ParseMetadata extracts all SSE-S3 related values from the object metadata
// and checks whether they are well-formed. It returns the KMS key-ID, the // and checks whether they are well-formed. It returns the sealed object key
// sealed KMS key and the sealed object key on success. // on success. If the metadata contains both, a KMS master key ID and a sealed
// KMS data key it returns both. If the metadata does not contain neither a
// KMS master key ID nor a sealed KMS data key it returns an empty keyID and
// KMS data key. Otherwise, it returns an error.
func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte, sealedKey SealedKey, err error) { func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte, sealedKey SealedKey, err error) {
// Extract all required values from object metadata // Extract all required values from object metadata
b64IV, ok := metadata[SSEIV] b64IV, ok := metadata[SSEIV]
@ -155,12 +174,17 @@ func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte
if !ok { if !ok {
return keyID, kmsKey, sealedKey, Error{"The object metadata is missing the internal sealed key for SSE-S3"} return keyID, kmsKey, sealedKey, Error{"The object metadata is missing the internal sealed key for SSE-S3"}
} }
keyID, ok = metadata[S3KMSKeyID]
if !ok { // There are two possibilites:
// - We use a KMS -> There must be a 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 metadata is corrupted.
keyID, idPresent := metadata[S3KMSKeyID]
b64KMSSealedKey, kmsKeyPresent := metadata[S3KMSSealedKey]
if !idPresent && kmsKeyPresent {
return keyID, kmsKey, sealedKey, Error{"The object metadata is missing the internal KMS key-ID for SSE-S3"} return keyID, kmsKey, sealedKey, Error{"The object metadata is missing the internal KMS key-ID for SSE-S3"}
} }
b64KMSSealedKey, ok := metadata[S3KMSSealedKey] if idPresent && !kmsKeyPresent {
if !ok {
return keyID, kmsKey, sealedKey, Error{"The object metadata is missing the internal sealed KMS data key for SSE-S3"} return keyID, kmsKey, sealedKey, Error{"The object metadata is missing the internal sealed KMS data key for SSE-S3"}
} }
@ -176,10 +200,12 @@ func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte
if err != nil || len(encryptedKey) != 64 { if err != nil || len(encryptedKey) != 64 {
return keyID, kmsKey, sealedKey, Error{"The internal sealed key for SSE-S3 is invalid"} return keyID, kmsKey, sealedKey, Error{"The internal sealed key for SSE-S3 is invalid"}
} }
if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key.
kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey) kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey)
if err != nil { if err != nil {
return keyID, kmsKey, sealedKey, Error{"The internal sealed KMS data key for SSE-S3 is invalid"} return keyID, kmsKey, sealedKey, Error{"The internal sealed KMS data key for SSE-S3 is invalid"}
} }
}
sealedKey.Algorithm = algorithm sealedKey.Algorithm = algorithm
copy(sealedKey.IV[:], iv) copy(sealedKey.IV[:], iv)

@ -130,7 +130,7 @@ var s3ParseMetadataTests = []struct {
}, // 2 }, // 2
{ {
ExpectedErr: Error{"The object metadata is missing the internal KMS key-ID for SSE-S3"}, ExpectedErr: Error{"The object metadata is missing the internal KMS key-ID for SSE-S3"},
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: ""}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSSealedKey: "IAAF0b=="}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
}, // 3 }, // 3
{ {
ExpectedErr: Error{"The object metadata is missing the internal sealed KMS data key for SSE-S3"}, ExpectedErr: Error{"The object metadata is missing the internal sealed KMS data key for SSE-S3"},
@ -287,7 +287,9 @@ var s3CreateMetadataTests = []struct {
SealedDataKey []byte SealedDataKey []byte
SealedKey SealedKey SealedKey SealedKey
}{ }{
{KeyID: "", SealedDataKey: make([]byte, 48), SealedKey: SealedKey{Algorithm: SealAlgorithm}},
{KeyID: "", SealedDataKey: nil, 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: "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}}, {KeyID: "deadbeef", SealedDataKey: make([]byte, 32), SealedKey: SealedKey{IV: [32]byte{0xf7}, Key: [64]byte{0xea}, Algorithm: SealAlgorithm}},
} }

Loading…
Cancel
Save