crypto: add support for decrypting SSE-KMS metadata (#11415)

This commit refactors the SSE implementation and add
S3-compatible SSE-KMS context handling.

SSE-KMS differs from SSE-S3 in two main aspects:
 1. The client can request a particular key and
    specify a KMS context as part of the request.
 2. The ETag of an SSE-KMS encrypted object is not
    the MD5 sum of the object content.

This commit only focuses on the 1st aspect.

A client can send an optional SSE context when using
SSE-KMS. This context is remembered by the S3 server
such that the client does not have to specify the
context again (during multipart PUT / GET / HEAD ...).
The crypto. context also includes the bucket/object
name to prevent renaming objects at the backend.

Now, AWS S3 behaves as following:
 - If the user does not provide a SSE-KMS context
   it does not store one - resp. does not include
   the SSE-KMS context header in the response (e.g. HEAD).
 - If the user specifies a SSE-KMS context without
   the bucket/object name then AWS stores the exact
   context the client provided but adds the bucket/object
   name internally. The response contains the KMS context
   without the bucket/object name.
 - If the user specifies a SSE-KMS context with
   the bucket/object name then AWS again stores the exact
   context provided by the client. The response contains
   the KMS context with the bucket/object name.

This commit implements this behavior w.r.t. SSE-KMS.
However, as of now, no such object can be created since
the server rejects SSE-KMS encryption requests.

This commit is one stepping stone for SSE-KMS support.

Co-authored-by: Harshavardhana <harsha@minio.io>
This commit is contained in:
Andreas Auernhammer 2021-02-04 00:19:08 +01:00 committed by GitHub
parent f71e192343
commit 871b450dbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 142 additions and 139 deletions

View File

@ -44,6 +44,15 @@ const (
// MetaDataEncryptionKey is the sealed data encryption key (DEK) received from // MetaDataEncryptionKey is the sealed data encryption key (DEK) received from
// the KMS. // the KMS.
MetaDataEncryptionKey = "X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key" MetaDataEncryptionKey = "X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key"
// MetaContext is the KMS context provided by a client when encrypting an
// object with SSE-KMS. A client may not send a context in which case the
// MetaContext will not be present.
// MetaContext only contains the bucket/object name if the client explicitly
// added it. However, when decrypting an object the bucket/object name must
// be part of the object. Therefore, the bucket/object name must be added
// to the context, if not present, whenever a decryption is performed.
MetaContext = "X-Minio-Internal-Server-Side-Encryption-Context"
) )
// IsMultiPart returns true if the object metadata indicates // IsMultiPart returns true if the object metadata indicates
@ -109,26 +118,35 @@ func IsSourceEncrypted(metadata map[string]string) bool {
// //
// IsEncrypted only checks whether the metadata contains at least // IsEncrypted only checks whether the metadata contains at least
// one entry indicating SSE-C or SSE-S3. // one entry indicating SSE-C or SSE-S3.
func IsEncrypted(metadata map[string]string) bool { func IsEncrypted(metadata map[string]string) (Type, bool) {
if _, ok := metadata[MetaIV]; ok { if S3KMS.IsEncrypted(metadata) {
return true return S3KMS, true
}
if _, ok := metadata[MetaAlgorithm]; ok {
return true
}
if IsMultiPart(metadata) {
return true
} }
if S3.IsEncrypted(metadata) { if S3.IsEncrypted(metadata) {
return true return S3, true
} }
if SSEC.IsEncrypted(metadata) { if SSEC.IsEncrypted(metadata) {
return true return SSEC, true
} }
if S3KMS.IsEncrypted(metadata) { if IsMultiPart(metadata) {
return true return nil, true
} }
return false if _, ok := metadata[MetaIV]; ok {
return nil, true
}
if _, ok := metadata[MetaAlgorithm]; ok {
return nil, true
}
if _, ok := metadata[MetaKeyID]; ok {
return nil, true
}
if _, ok := metadata[MetaDataEncryptionKey]; ok {
return nil, true
}
if _, ok := metadata[MetaContext]; ok {
return nil, true
}
return nil, false
} }
// CreateMultipartMetadata adds the multipart flag entry to metadata // CreateMultipartMetadata adds the multipart flag entry to metadata

View File

@ -59,7 +59,7 @@ var isEncryptedTests = []struct {
func TestIsEncrypted(t *testing.T) { func TestIsEncrypted(t *testing.T) {
for i, test := range isEncryptedTests { for i, test := range isEncryptedTests {
if isEncrypted := IsEncrypted(test.Metadata); isEncrypted != test.Encrypted { if _, isEncrypted := IsEncrypted(test.Metadata); isEncrypted != test.Encrypted {
t.Errorf("Test %d: got '%v' - want '%v'", i, isEncrypted, test.Encrypted) t.Errorf("Test %d: got '%v' - want '%v'", i, isEncrypted, test.Encrypted)
} }
} }
@ -74,8 +74,8 @@ var s3IsEncryptedTests = []struct {
{Encrypted: false, Metadata: map[string]string{MetaAlgorithm: ""}}, // 2 {Encrypted: false, Metadata: map[string]string{MetaAlgorithm: ""}}, // 2
{Encrypted: false, Metadata: map[string]string{MetaSealedKeySSEC: ""}}, // 3 {Encrypted: false, Metadata: map[string]string{MetaSealedKeySSEC: ""}}, // 3
{Encrypted: true, Metadata: map[string]string{MetaSealedKeyS3: ""}}, // 4 {Encrypted: true, Metadata: map[string]string{MetaSealedKeyS3: ""}}, // 4
{Encrypted: true, Metadata: map[string]string{MetaKeyID: ""}}, // 5 {Encrypted: false, Metadata: map[string]string{MetaKeyID: ""}}, // 5
{Encrypted: true, Metadata: map[string]string{MetaDataEncryptionKey: ""}}, // 6 {Encrypted: false, Metadata: map[string]string{MetaDataEncryptionKey: ""}}, // 6
{Encrypted: false, Metadata: map[string]string{"": ""}}, // 7 {Encrypted: false, Metadata: map[string]string{"": ""}}, // 7
{Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8 {Encrypted: false, Metadata: map[string]string{"X-Minio-Internal-Server-Side-Encryption": ""}}, // 8
} }

View File

@ -82,12 +82,6 @@ func (ssekms) IsEncrypted(metadata map[string]string) bool {
if _, ok := metadata[MetaSealedKeyKMS]; ok { if _, ok := metadata[MetaSealedKeyKMS]; ok {
return true return true
} }
if _, ok := metadata[MetaKeyID]; ok {
return true
}
if _, ok := metadata[MetaDataEncryptionKey]; ok {
return true
}
return false return false
} }
@ -95,11 +89,14 @@ func (ssekms) IsEncrypted(metadata map[string]string) bool {
// from the metadata using KMS and returns the decrypted object // from the metadata using KMS and returns the decrypted object
// key. // key.
func (s3 ssekms) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, object string) (key ObjectKey, err error) { func (s3 ssekms) UnsealObjectKey(kms KMS, metadata map[string]string, bucket, object string) (key ObjectKey, err error) {
keyID, kmsKey, sealedKey, err := s3.ParseMetadata(metadata) keyID, kmsKey, sealedKey, ctx, err := s3.ParseMetadata(metadata)
if err != nil { if err != nil {
return key, err return key, err
} }
unsealKey, err := kms.UnsealKey(keyID, kmsKey, Context{bucket: path.Join(bucket, object)}) if _, ok := ctx[bucket]; !ok {
ctx[bucket] = path.Join(bucket, object)
}
unsealKey, err := kms.UnsealKey(keyID, kmsKey, ctx)
if err != nil { if err != nil {
return key, err return key, err
} }
@ -147,19 +144,19 @@ func (ssekms) CreateMetadata(metadata map[string]string, keyID string, kmsKey []
// KMS data key it returns both. If the metadata does not contain neither a // 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 master key ID nor a sealed KMS data key it returns an empty keyID and
// KMS data key. Otherwise, it returns an error. // KMS data key. Otherwise, it returns an error.
func (ssekms) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte, sealedKey SealedKey, err error) { func (ssekms) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte, sealedKey SealedKey, ctx Context, err error) {
// Extract all required values from object metadata // Extract all required values from object metadata
b64IV, ok := metadata[MetaIV] b64IV, ok := metadata[MetaIV]
if !ok { if !ok {
return keyID, kmsKey, sealedKey, errMissingInternalIV return keyID, kmsKey, sealedKey, ctx, errMissingInternalIV
} }
algorithm, ok := metadata[MetaAlgorithm] algorithm, ok := metadata[MetaAlgorithm]
if !ok { if !ok {
return keyID, kmsKey, sealedKey, errMissingInternalSealAlgorithm return keyID, kmsKey, sealedKey, ctx, errMissingInternalSealAlgorithm
} }
b64SealedKey, ok := metadata[MetaSealedKeyKMS] b64SealedKey, ok := metadata[MetaSealedKeyKMS]
if !ok { if !ok {
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-S3") return keyID, kmsKey, sealedKey, ctx, Errorf("The object metadata is missing the internal sealed key for SSE-S3")
} }
// There are two possibilites: // There are two possibilites:
@ -169,33 +166,44 @@ func (ssekms) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []
keyID, idPresent := metadata[MetaKeyID] keyID, idPresent := metadata[MetaKeyID]
b64KMSSealedKey, kmsKeyPresent := metadata[MetaDataEncryptionKey] b64KMSSealedKey, kmsKeyPresent := metadata[MetaDataEncryptionKey]
if !idPresent && kmsKeyPresent { if !idPresent && kmsKeyPresent {
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3") return keyID, kmsKey, sealedKey, ctx, Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3")
} }
if idPresent && !kmsKeyPresent { if idPresent && !kmsKeyPresent {
return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3") return keyID, kmsKey, sealedKey, ctx, Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3")
} }
// Check whether all extracted values are well-formed // Check whether all extracted values are well-formed
iv, err := base64.StdEncoding.DecodeString(b64IV) iv, err := base64.StdEncoding.DecodeString(b64IV)
if err != nil || len(iv) != 32 { if err != nil || len(iv) != 32 {
return keyID, kmsKey, sealedKey, errInvalidInternalIV return keyID, kmsKey, sealedKey, ctx, errInvalidInternalIV
} }
if algorithm != SealAlgorithm { if algorithm != SealAlgorithm {
return keyID, kmsKey, sealedKey, errInvalidInternalSealAlgorithm return keyID, kmsKey, sealedKey, ctx, errInvalidInternalSealAlgorithm
} }
encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey) encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey)
if err != nil || len(encryptedKey) != 64 { if err != nil || len(encryptedKey) != 64 {
return keyID, kmsKey, sealedKey, Errorf("The internal sealed key for SSE-S3 is invalid") return keyID, kmsKey, sealedKey, ctx, Errorf("The internal sealed key for SSE-KMS is invalid")
} }
if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key. 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, Errorf("The internal sealed KMS data key for SSE-S3 is invalid") return keyID, kmsKey, sealedKey, ctx, Errorf("The internal sealed KMS data key for SSE-KMS is invalid")
}
}
b64Ctx, ok := metadata[MetaContext]
if ok {
b, err := base64.StdEncoding.DecodeString(b64Ctx)
if err != nil {
return keyID, kmsKey, sealedKey, ctx, Errorf("The internal KMS context is not base64-encoded")
}
var json = jsoniter.ConfigCompatibleWithStandardLibrary
if err = json.Unmarshal(b, ctx); err != nil {
return keyID, kmsKey, sealedKey, ctx, Errorf("The internal sealed KMS context is invalid")
} }
} }
sealedKey.Algorithm = algorithm sealedKey.Algorithm = algorithm
copy(sealedKey.IV[:], iv) copy(sealedKey.IV[:], iv)
copy(sealedKey.Key[:], encryptedKey) copy(sealedKey.Key[:], encryptedKey)
return keyID, kmsKey, sealedKey, nil return keyID, kmsKey, sealedKey, ctx, nil
} }

View File

@ -62,12 +62,6 @@ func (sses3) IsEncrypted(metadata map[string]string) bool {
if _, ok := metadata[MetaSealedKeyS3]; ok { if _, ok := metadata[MetaSealedKeyS3]; ok {
return true return true
} }
if _, ok := metadata[MetaKeyID]; ok {
return true
}
if _, ok := metadata[MetaDataEncryptionKey]; ok {
return true
}
return false return false
} }

View File

@ -173,7 +173,11 @@ func backendDownError(err error) bool {
// IsCacheable returns if the object should be saved in the cache. // IsCacheable returns if the object should be saved in the cache.
func (o ObjectInfo) IsCacheable() bool { func (o ObjectInfo) IsCacheable() bool {
return !crypto.IsEncrypted(o.UserDefined) || globalCacheKMS != nil if globalCacheKMS != nil {
return true
}
_, ok := crypto.IsEncrypted(o.UserDefined)
return !ok
} }
// reads file cached on disk from offset upto length // reads file cached on disk from offset upto length

View File

@ -247,56 +247,45 @@ func EncryptRequest(content io.Reader, r *http.Request, bucket, object string, m
} }
func decryptObjectInfo(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) { func decryptObjectInfo(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) {
switch { switch kind, _ := crypto.IsEncrypted(metadata); kind {
default: case crypto.S3:
return nil, errObjectTampered var KMS crypto.KMS = GlobalKMS
case crypto.S3.IsEncrypted(metadata) && isCacheEncrypted(metadata): if isCacheEncrypted(metadata) {
if globalCacheKMS == nil { KMS = globalCacheKMS
}
if KMS == nil {
return nil, errKMSNotConfigured return nil, errKMSNotConfigured
} }
keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(metadata) objectKey, err := crypto.S3.UnsealObjectKey(KMS, metadata, bucket, object)
if err != nil { if err != nil {
return nil, err return nil, err
} }
extKey, err := globalCacheKMS.UnsealKey(keyID, kmsKey, crypto.Context{bucket: path.Join(bucket, object)})
if err != nil {
return nil, err
}
var objectKey crypto.ObjectKey
if err = objectKey.Unseal(extKey, sealedKey, crypto.S3.String(), bucket, object); err != nil {
return nil, err
}
return objectKey[:], nil return objectKey[:], nil
case crypto.S3.IsEncrypted(metadata): case crypto.S3KMS:
if GlobalKMS == nil { if GlobalKMS == nil {
return nil, errKMSNotConfigured return nil, errKMSNotConfigured
} }
keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(metadata) objectKey, err := crypto.S3KMS.UnsealObjectKey(GlobalKMS, metadata, bucket, object)
if err != nil { if err != nil {
return nil, err return nil, err
} }
extKey, err := GlobalKMS.UnsealKey(keyID, kmsKey, crypto.Context{bucket: path.Join(bucket, object)})
if err != nil {
return nil, err
}
var objectKey crypto.ObjectKey
if err = objectKey.Unseal(extKey, sealedKey, crypto.S3.String(), bucket, object); err != nil {
return nil, err
}
return objectKey[:], nil return objectKey[:], nil
case crypto.SSEC.IsEncrypted(metadata): case crypto.SSEC:
var extKey [32]byte
copy(extKey[:], key)
sealedKey, err := crypto.SSEC.ParseMetadata(metadata) sealedKey, err := crypto.SSEC.ParseMetadata(metadata)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var objectKey crypto.ObjectKey var (
objectKey crypto.ObjectKey
extKey [32]byte
)
copy(extKey[:], key)
if err = objectKey.Unseal(extKey, sealedKey, crypto.SSEC.String(), bucket, object); err != nil { if err = objectKey.Unseal(extKey, sealedKey, crypto.SSEC.String(), bucket, object); err != nil {
return nil, err return nil, err
} }
return objectKey[:], nil return objectKey[:], nil
default:
return nil, errObjectTampered
} }
} }
@ -516,7 +505,7 @@ func (d *DecryptBlocksReader) Read(p []byte) (int, error) {
// It returns an error if the object is not encrypted or marked as encrypted // It returns an error if the object is not encrypted or marked as encrypted
// but has an invalid size. // but has an invalid size.
func (o *ObjectInfo) DecryptedSize() (int64, error) { func (o *ObjectInfo) DecryptedSize() (int64, error) {
if !crypto.IsEncrypted(o.UserDefined) { if _, ok := crypto.IsEncrypted(o.UserDefined); !ok {
return 0, errors.New("Cannot compute decrypted size of an unencrypted object") return 0, errors.New("Cannot compute decrypted size of an unencrypted object")
} }
if !isEncryptedMultipart(*o) { if !isEncryptedMultipart(*o) {
@ -649,7 +638,7 @@ func tryDecryptETag(key []byte, encryptedETag string, ssec bool) string {
// requested range starts, along with the DARE sequence number within // requested range starts, along with the DARE sequence number within
// that part. For single part objects, the partStart will be 0. // that part. For single part objects, the partStart will be 0.
func (o *ObjectInfo) GetDecryptedRange(rs *HTTPRangeSpec) (encOff, encLength, skipLen int64, seqNumber uint32, partStart int, err error) { func (o *ObjectInfo) GetDecryptedRange(rs *HTTPRangeSpec) (encOff, encLength, skipLen int64, seqNumber uint32, partStart int, err error) {
if !crypto.IsEncrypted(o.UserDefined) { if _, ok := crypto.IsEncrypted(o.UserDefined); !ok {
err = errors.New("Object is not encrypted") err = errors.New("Object is not encrypted")
return return
} }
@ -796,7 +785,7 @@ func DecryptObjectInfo(info *ObjectInfo, r *http.Request) (encrypted bool, err e
} }
} }
encrypted = crypto.IsEncrypted(info.UserDefined) _, encrypted = crypto.IsEncrypted(info.UserDefined)
if !encrypted && crypto.SSEC.IsRequested(headers) && r.Header.Get(xhttp.AmzCopySource) == "" { if !encrypted && crypto.SSEC.IsRequested(headers) && r.Header.Get(xhttp.AmzCopySource) == "" {
return false, errInvalidEncryptionParameters return false, errInvalidEncryptionParameters
} }
@ -818,7 +807,7 @@ func DecryptObjectInfo(info *ObjectInfo, r *http.Request) (encrypted bool, err e
return encrypted, err return encrypted, err
} }
if crypto.IsEncrypted(info.UserDefined) && !crypto.IsMultiPart(info.UserDefined) { if _, ok := crypto.IsEncrypted(info.UserDefined); ok && !crypto.IsMultiPart(info.UserDefined) {
info.ETag = getDecryptedETag(headers, *info, false) info.ETag = getDecryptedETag(headers, *info, false)
} }
} }

View File

@ -129,7 +129,7 @@ func TestDecryptObjectInfo(t *testing.T) {
for i, test := range decryptObjectInfoTests { for i, test := range decryptObjectInfoTests {
if encrypted, err := DecryptObjectInfo(&test.info, test.request); err != test.expErr { if encrypted, err := DecryptObjectInfo(&test.info, test.request); err != test.expErr {
t.Errorf("Test %d: Decryption returned wrong error code: got %d , want %d", i, err, test.expErr) t.Errorf("Test %d: Decryption returned wrong error code: got %d , want %d", i, err, test.expErr)
} else if enc := crypto.IsEncrypted(test.info.UserDefined); encrypted && enc != encrypted { } else if _, enc := crypto.IsEncrypted(test.info.UserDefined); encrypted && enc != encrypted {
t.Errorf("Test %d: Decryption thinks object is encrypted but it is not", i) t.Errorf("Test %d: Decryption thinks object is encrypted but it is not", i)
} else if !encrypted && enc != encrypted { } else if !encrypted && enc != encrypted {
t.Errorf("Test %d: Decryption thinks object is not encrypted but it is", i) t.Errorf("Test %d: Decryption thinks object is not encrypted but it is", i)

View File

@ -404,7 +404,7 @@ func (o ObjectInfo) IsCompressedOK() (bool, error) {
// GetActualETag - returns the actual etag of the stored object // GetActualETag - returns the actual etag of the stored object
// decrypts SSE objects. // decrypts SSE objects.
func (o ObjectInfo) GetActualETag(h http.Header) string { func (o ObjectInfo) GetActualETag(h http.Header) string {
if !crypto.IsEncrypted(o.UserDefined) { if _, ok := crypto.IsEncrypted(o.UserDefined); !ok {
return o.ETag return o.ETag
} }
return getDecryptedETag(h, o, false) return getDecryptedETag(h, o, false)
@ -423,7 +423,7 @@ func (o ObjectInfo) GetActualSize() (int64, error) {
} }
return size, nil return size, nil
} }
if crypto.IsEncrypted(o.UserDefined) { if _, ok := crypto.IsEncrypted(o.UserDefined); ok {
return o.DecryptedSize() return o.DecryptedSize()
} }
@ -600,7 +600,7 @@ func NewGetObjectReader(rs *HTTPRangeSpec, oi ObjectInfo, opts ObjectOptions, cl
} }
}() }()
isEncrypted := crypto.IsEncrypted(oi.UserDefined) _, isEncrypted := crypto.IsEncrypted(oi.UserDefined)
isCompressed, err := oi.IsCompressedOK() isCompressed, err := oi.IsCompressedOK()
if err != nil { if err != nil {
return nil, 0, 0, err return nil, 0, 0, err

View File

@ -265,19 +265,17 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
// Set encryption response headers // Set encryption response headers
if objectAPI.IsEncryptionSupported() { if objectAPI.IsEncryptionSupported() {
if crypto.IsEncrypted(objInfo.UserDefined) { switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
switch { case crypto.S3:
case crypto.S3.IsEncrypted(objInfo.UserDefined): w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) case crypto.SSEC:
case crypto.SSEC.IsEncrypted(objInfo.UserDefined): // Validate the SSE-C Key set in the header.
// Validate the SSE-C Key set in the header. if _, err = crypto.SSEC.UnsealObjectKey(r.Header, objInfo.UserDefined, bucket, object); err != nil {
if _, err = crypto.SSEC.UnsealObjectKey(r.Header, objInfo.UserDefined, bucket, object); err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) return
return
}
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
} }
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
} }
} }
@ -450,14 +448,12 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
// Set encryption response headers // Set encryption response headers
if objectAPI.IsEncryptionSupported() { if objectAPI.IsEncryptionSupported() {
if crypto.IsEncrypted(objInfo.UserDefined) { switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
switch { case crypto.S3:
case crypto.S3.IsEncrypted(objInfo.UserDefined): w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) case crypto.SSEC:
case crypto.SSEC.IsEncrypted(objInfo.UserDefined): w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm)) w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
}
} }
} }
@ -654,19 +650,17 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
// Set encryption response headers // Set encryption response headers
if objectAPI.IsEncryptionSupported() { if objectAPI.IsEncryptionSupported() {
if crypto.IsEncrypted(objInfo.UserDefined) { switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
switch { case crypto.S3:
case crypto.S3.IsEncrypted(objInfo.UserDefined): w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) case crypto.SSEC:
case crypto.SSEC.IsEncrypted(objInfo.UserDefined): // Validate the SSE-C Key set in the header.
// Validate the SSE-C Key set in the header. if _, err = crypto.SSEC.UnsealObjectKey(r.Header, objInfo.UserDefined, bucket, object); err != nil {
if _, err = crypto.SSEC.UnsealObjectKey(r.Header, objInfo.UserDefined, bucket, object); err != nil { writeErrorResponseHeadersOnly(w, toAPIError(ctx, err))
writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) return
return
}
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
} }
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
} }
} }
@ -1047,7 +1041,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
var encMetadata = make(map[string]string) var encMetadata = make(map[string]string)
if objectAPI.IsEncryptionSupported() { if objectAPI.IsEncryptionSupported() {
// Encryption parameters not applicable for this object. // Encryption parameters not applicable for this object.
if !crypto.IsEncrypted(srcInfo.UserDefined) && crypto.SSECopy.IsRequested(r.Header) { if _, ok := crypto.IsEncrypted(srcInfo.UserDefined); ok && crypto.SSECopy.IsRequested(r.Header) {
writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidEncryptionParameters), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, toAPIError(ctx, errInvalidEncryptionParameters), r.URL, guessIsBrowserReq(r))
return return
} }
@ -1584,13 +1578,13 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
return return
} }
switch { switch kind, encrypted := crypto.IsEncrypted(objInfo.UserDefined); {
case crypto.IsEncrypted(objInfo.UserDefined): case encrypted:
switch { switch kind {
case crypto.S3.IsEncrypted(objInfo.UserDefined): case crypto.S3:
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
objInfo.ETag, _ = DecryptETag(objectEncryptionKey, ObjectInfo{ETag: objInfo.ETag}) objInfo.ETag, _ = DecryptETag(objectEncryptionKey, ObjectInfo{ETag: objInfo.ETag})
case crypto.SSEC.IsEncrypted(objInfo.UserDefined): case crypto.SSEC:
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm)) w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5)) w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
@ -1912,7 +1906,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
srcInfo := gr.ObjInfo srcInfo := gr.ObjInfo
actualPartSize := srcInfo.Size actualPartSize := srcInfo.Size
if crypto.IsEncrypted(srcInfo.UserDefined) { if _, ok := crypto.IsEncrypted(srcInfo.UserDefined); ok {
actualPartSize, err = srcInfo.GetActualSize() actualPartSize, err = srcInfo.GetActualSize()
if err != nil { if err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
@ -2010,7 +2004,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
rawReader := srcInfo.Reader rawReader := srcInfo.Reader
pReader := NewPutObjReader(rawReader, nil, nil) pReader := NewPutObjReader(rawReader, nil, nil)
isEncrypted := crypto.IsEncrypted(mi.UserDefined) _, isEncrypted := crypto.IsEncrypted(mi.UserDefined)
var objectEncryptionKey crypto.ObjectKey var objectEncryptionKey crypto.ObjectKey
if objectAPI.IsEncryptionSupported() && isEncrypted { if objectAPI.IsEncryptionSupported() && isEncrypted {
if !crypto.SSEC.IsRequested(r.Header) && crypto.SSEC.IsEncrypted(mi.UserDefined) { if !crypto.SSEC.IsRequested(r.Header) && crypto.SSEC.IsEncrypted(mi.UserDefined) {
@ -2250,7 +2244,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
rawReader := hashReader rawReader := hashReader
pReader := NewPutObjReader(rawReader, nil, nil) pReader := NewPutObjReader(rawReader, nil, nil)
isEncrypted := crypto.IsEncrypted(mi.UserDefined) _, isEncrypted := crypto.IsEncrypted(mi.UserDefined)
var objectEncryptionKey crypto.ObjectKey var objectEncryptionKey crypto.ObjectKey
if objectAPI.IsEncryptionSupported() && isEncrypted { if objectAPI.IsEncryptionSupported() && isEncrypted {
if !crypto.SSEC.IsRequested(r.Header) && crypto.SSEC.IsEncrypted(mi.UserDefined) { if !crypto.SSEC.IsRequested(r.Header) && crypto.SSEC.IsEncrypted(mi.UserDefined) {
@ -2416,7 +2410,7 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht
} }
var ssec bool var ssec bool
if objectAPI.IsEncryptionSupported() && crypto.IsEncrypted(listPartsInfo.UserDefined) { if _, ok := crypto.IsEncrypted(listPartsInfo.UserDefined); ok && objectAPI.IsEncryptionSupported() {
var key []byte var key []byte
if crypto.SSEC.IsEncrypted(listPartsInfo.UserDefined) { if crypto.SSEC.IsEncrypted(listPartsInfo.UserDefined) {
ssec = true ssec = true
@ -2580,7 +2574,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return return
} }
if crypto.IsEncrypted(mi.UserDefined) { if _, ok := crypto.IsEncrypted(mi.UserDefined); ok {
var key []byte var key []byte
isEncrypted = true isEncrypted = true
ssec = crypto.SSEC.IsEncrypted(mi.UserDefined) ssec = crypto.SSEC.IsEncrypted(mi.UserDefined)

View File

@ -1289,14 +1289,12 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
return return
} }
if objectAPI.IsEncryptionSupported() { if objectAPI.IsEncryptionSupported() {
if crypto.IsEncrypted(objInfo.UserDefined) { switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
switch { case crypto.S3:
case crypto.S3.IsEncrypted(objInfo.UserDefined): w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) case crypto.SSEC:
case crypto.SSEC.IsRequested(r.Header): w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm)) w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
}
} }
} }
if mustReplicate { if mustReplicate {
@ -1448,14 +1446,12 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
// Set encryption response headers // Set encryption response headers
if objectAPI.IsEncryptionSupported() { if objectAPI.IsEncryptionSupported() {
if crypto.IsEncrypted(objInfo.UserDefined) { switch kind, _ := crypto.IsEncrypted(objInfo.UserDefined); kind {
switch { case crypto.S3:
case crypto.S3.IsEncrypted(objInfo.UserDefined): w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES)
w.Header().Set(xhttp.AmzServerSideEncryption, xhttp.AmzEncryptionAES) case crypto.SSEC:
case crypto.SSEC.IsEncrypted(objInfo.UserDefined): w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm))
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerAlgorithm, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerAlgorithm)) w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
w.Header().Set(xhttp.AmzServerSideEncryptionCustomerKeyMD5, r.Header.Get(xhttp.AmzServerSideEncryptionCustomerKeyMD5))
}
} }
} }