From da9f0e324eb3e5183f8047bbdff03c911c5df635 Mon Sep 17 00:00:00 2001 From: Andreas Auernhammer Date: Fri, 6 Apr 2018 23:15:23 +0200 Subject: [PATCH] return AWS S3 compatible error for invalid but equal keys during key rotation (#5783) This change let the server return the S3 error for a key rotation if the source key is not valid but equal to the destination key. This change also fixes the SSE-C error messages since AWS returns error messages ending with a '.'. Fixes #5625 --- cmd/api-errors.go | 22 +++++++++++++++------- cmd/encryption-v1.go | 32 +++++++++++++++++--------------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/cmd/api-errors.go b/cmd/api-errors.go index 10331f4af..9fe818786 100644 --- a/cmd/api-errors.go +++ b/cmd/api-errors.go @@ -138,6 +138,7 @@ const ( ErrMissingSSECustomerKey ErrMissingSSECustomerKeyMD5 ErrSSECustomerKeyMD5Mismatch + ErrInvalidSSECustomerParameters // Bucket notification related errors. ErrEventNotification @@ -629,7 +630,7 @@ var errorCodeResponse = map[APIErrorCode]APIError{ }, ErrInsecureSSECustomerRequest: { Code: "InvalidRequest", - Description: errInsecureSSERequest.Error(), + Description: "Requests specifying Server Side Encryption with Customer provided keys must be made over a secure connection.", HTTPStatusCode: http.StatusBadRequest, }, ErrSSEMultipartEncrypted: { @@ -639,7 +640,7 @@ var errorCodeResponse = map[APIErrorCode]APIError{ }, ErrSSEEncryptedObject: { Code: "InvalidRequest", - Description: errEncryptedObject.Error(), + Description: "The object was stored using a form of Server Side Encryption. The correct parameters must be provided to retrieve the object.", HTTPStatusCode: http.StatusBadRequest, }, ErrInvalidEncryptionParameters: { @@ -649,27 +650,32 @@ var errorCodeResponse = map[APIErrorCode]APIError{ }, ErrInvalidSSECustomerAlgorithm: { Code: "InvalidArgument", - Description: errInvalidSSEAlgorithm.Error(), + Description: "Requests specifying Server Side Encryption with Customer provided keys must provide a valid encryption algorithm.", HTTPStatusCode: http.StatusBadRequest, }, ErrInvalidSSECustomerKey: { Code: "InvalidArgument", - Description: errInvalidSSEKey.Error(), + Description: "The secret key was invalid for the specified algorithm.", HTTPStatusCode: http.StatusBadRequest, }, ErrMissingSSECustomerKey: { Code: "InvalidArgument", - Description: errMissingSSEKey.Error(), + Description: "Requests specifying Server Side Encryption with Customer provided keys must provide an appropriate secret key.", HTTPStatusCode: http.StatusBadRequest, }, ErrMissingSSECustomerKeyMD5: { Code: "InvalidArgument", - Description: errMissingSSEKeyMD5.Error(), + Description: "Requests specifying Server Side Encryption with Customer provided keys must provide the client calculated MD5 of the secret key.", HTTPStatusCode: http.StatusBadRequest, }, ErrSSECustomerKeyMD5Mismatch: { Code: "InvalidArgument", - Description: errSSEKeyMD5Mismatch.Error(), + Description: "The calculated MD5 hash of the key did not match the hash that was provided.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrInvalidSSECustomerParameters: { + Code: "InvalidArgument", + Description: "The provided encryption parameters did not match the ones used originally.", HTTPStatusCode: http.StatusBadRequest, }, @@ -884,6 +890,8 @@ func toAPIErrorCode(err error) (apiErr APIErrorCode) { return ErrObjectTampered case errEncryptedObject: return ErrSSEEncryptedObject + case errInvalidSSEParameters: + return ErrInvalidSSECustomerParameters case errSSEKeyMismatch: return ErrAccessDenied // no access without correct key } diff --git a/cmd/encryption-v1.go b/cmd/encryption-v1.go index 8069da222..191c5f3fe 100644 --- a/cmd/encryption-v1.go +++ b/cmd/encryption-v1.go @@ -35,14 +35,15 @@ import ( var ( // AWS errors for invalid SSE-C requests. - errInsecureSSERequest = errors.New("Requests specifying Server Side Encryption with Customer provided keys must be made over a secure connection") - errEncryptedObject = errors.New("The object was stored using a form of Server Side Encryption. The correct parameters must be provided to retrieve the object") - errInvalidSSEAlgorithm = errors.New("Requests specifying Server Side Encryption with Customer provided keys must provide a valid encryption algorithm") - errMissingSSEKey = errors.New("Requests specifying Server Side Encryption with Customer provided keys must provide an appropriate secret key") - errInvalidSSEKey = errors.New("The secret key was invalid for the specified algorithm") - errMissingSSEKeyMD5 = errors.New("Requests specifying Server Side Encryption with Customer provided keys must provide the client calculated MD5 of the secret key") - errSSEKeyMD5Mismatch = errors.New("The calculated MD5 hash of the key did not match the hash that was provided") - errSSEKeyMismatch = errors.New("The client provided key does not match the key provided when the object was encrypted") // this msg is not shown to the client + errInsecureSSERequest = errors.New("SSE-C requests require TLS connections") + errEncryptedObject = errors.New("The object was stored using a form of SSE") + errInvalidSSEAlgorithm = errors.New("The SSE-C algorithm is not valid") + errMissingSSEKey = errors.New("The SSE-C request is missing the customer key") + errInvalidSSEKey = errors.New("The SSE-C key is invalid") + errMissingSSEKeyMD5 = errors.New("The SSE-C request is missing the customer key MD5") + errSSEKeyMD5Mismatch = errors.New("The key MD5 does not match the SSE-C key") + errSSEKeyMismatch = errors.New("The SSE-C key is not correct") // access denied + errInvalidSSEParameters = errors.New("The SSE-C key for key-rotation is not correct") // special access denied // Additional Minio errors for SSE-C requests. errObjectTampered = errors.New("The requested object was modified and may be compromised") @@ -247,9 +248,6 @@ func ParseSSECustomerHeader(header http.Header) (key []byte, err error) { // This function rotates old to new key. func rotateKey(oldKey []byte, newKey []byte, metadata map[string]string) error { - if subtle.ConstantTimeCompare(oldKey, newKey) == 1 { - return nil - } delete(metadata, SSECustomerKey) // make sure we do not save the key by accident if metadata[ServerSideEncryptionSealAlgorithm] != SSESealAlgorithmDareSha256 { // currently DARE-SHA256 is the only option @@ -273,10 +271,14 @@ func rotateKey(oldKey []byte, newKey []byte, metadata map[string]string) error { n, err := sio.Decrypt(objectEncryptionKey, bytes.NewReader(sealedKey), sio.Config{ Key: keyEncryptionKey, }) - if n != 32 || err != nil { - // Either the provided key does not match or the object was tampered. - // To provide strict AWS S3 compatibility we return: access denied. - return errSSEKeyMismatch + if n != 32 || err != nil { // Either the provided key does not match or the object was tampered. + if subtle.ConstantTimeCompare(oldKey, newKey) == 1 { + return errInvalidSSEParameters // AWS returns special error for equal but invalid keys. + } + return errSSEKeyMismatch // To provide strict AWS S3 compatibility we return: access denied. + } + if subtle.ConstantTimeCompare(oldKey, newKey) == 1 { + return nil // we don't need to rotate keys if newKey == oldKey } nonce := make([]byte, 32) // generate random values for key derivation