mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
fix object rebinding SSE-C security guarantee violation (#6121)
This commit fixes a weakness of the key-encryption-key derivation for SSE-C encrypted objects. Before this change the key-encryption-key was not bound to / didn't depend on the object path. This allows an attacker to repalce objects - encrypted with the same client-key - with each other. This change fixes this issue by updating the key-encryption-key derivation to include: - the domain (in this case SSE-C) - a canonical object path representation - the encryption & key derivation algorithm Changing the object path now causes the KDF to derive a different key-encryption-key such that the object-key unsealing fails. Including the domain (SSE-C) and encryption & key derivation algorithm is not directly neccessary for this fix. However, both will be included for the SSE-S3 KDF. So they are included here to avoid updating the KDF again when we add SSE-S3. The leagcy KDF 'DARE-SHA256' is only used for existing objects and never for new objects / key rotation.
This commit is contained in:
parent
4ddc222f46
commit
b181a693fb
@ -623,7 +623,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
|
|||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
reader, err = newEncryptReader(hashReader, key, metadata)
|
reader, err = newEncryptReader(hashReader, key, bucket, object, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
return
|
return
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
// Input: ClientKey, bucket, object, metadata, object_data
|
// Input: ClientKey, bucket, object, metadata, object_data
|
||||||
// - IV := Random({0,1}²⁵⁶)
|
// - IV := Random({0,1}²⁵⁶)
|
||||||
// - ObjectKey := SHA256(ClientKey || Random({0,1}²⁵⁶))
|
// - ObjectKey := SHA256(ClientKey || Random({0,1}²⁵⁶))
|
||||||
// - KeyEncKey := HMAC-SHA256(ClientKey, IV || bucket || object)
|
// - KeyEncKey := HMAC-SHA256(ClientKey, IV || 'SSE-C' || 'DAREv2-HMAC-SHA256' || bucket || '/' || object)
|
||||||
// - SealedKey := DAREv2_Enc(KeyEncKey, ObjectKey)
|
// - SealedKey := DAREv2_Enc(KeyEncKey, ObjectKey)
|
||||||
// - enc_object_data := DAREv2_Enc(ObjectKey, object_data)
|
// - enc_object_data := DAREv2_Enc(ObjectKey, object_data)
|
||||||
// - metadata <- IV
|
// - metadata <- IV
|
||||||
@ -43,7 +43,7 @@
|
|||||||
// Input: ClientKey, bucket, object, metadata, enc_object_data
|
// Input: ClientKey, bucket, object, metadata, enc_object_data
|
||||||
// - IV <- metadata
|
// - IV <- metadata
|
||||||
// - SealedKey <- metadata
|
// - SealedKey <- metadata
|
||||||
// - KeyEncKey := HMAC-SHA256(ClientKey, IV || bucket || object)
|
// - KeyEncKey := HMAC-SHA256(ClientKey, IV || 'SSE-C' || 'DAREv2-HMAC-SHA256' || bucket || '/' || object)
|
||||||
// - ObjectKey := DAREv2_Dec(KeyEncKey, SealedKey)
|
// - ObjectKey := DAREv2_Dec(KeyEncKey, SealedKey)
|
||||||
// - object_data := DAREv2_Dec(ObjectKey, enc_object_data)
|
// - object_data := DAREv2_Dec(ObjectKey, enc_object_data)
|
||||||
// Output: object_data
|
// Output: object_data
|
||||||
@ -64,7 +64,7 @@
|
|||||||
// Input: MasterKey, bucket, object, metadata, object_data
|
// Input: MasterKey, bucket, object, metadata, object_data
|
||||||
// - IV := Random({0,1}²⁵⁶)
|
// - IV := Random({0,1}²⁵⁶)
|
||||||
// - ObjectKey := SHA256(MasterKey || Random({0,1}²⁵⁶))
|
// - ObjectKey := SHA256(MasterKey || Random({0,1}²⁵⁶))
|
||||||
// - KeyEncKey := HMAC-SHA256(MasterKey, IV || bucket || object)
|
// - KeyEncKey := HMAC-SHA256(MasterKey, IV || 'SSE-S3' || 'DAREv2-HMAC-SHA256' || bucket || '/' || object)
|
||||||
// - SealedKey := DAREv2_Enc(KeyEncKey, ObjectKey)
|
// - SealedKey := DAREv2_Enc(KeyEncKey, ObjectKey)
|
||||||
// - enc_object_data := DAREv2_Enc(ObjectKey, object_data)
|
// - enc_object_data := DAREv2_Enc(ObjectKey, object_data)
|
||||||
// - metadata <- IV
|
// - metadata <- IV
|
||||||
@ -75,7 +75,7 @@
|
|||||||
// Input: MasterKey, bucket, object, metadata, enc_object_data
|
// Input: MasterKey, bucket, object, metadata, enc_object_data
|
||||||
// - IV <- metadata
|
// - IV <- metadata
|
||||||
// - SealedKey <- metadata
|
// - SealedKey <- metadata
|
||||||
// - KeyEncKey := HMAC-SHA256(MasterKey, IV || bucket || object)
|
// - KeyEncKey := HMAC-SHA256(MasterKey, IV || 'SSE-S3' || 'DAREv2-HMAC-SHA256' || bucket || '/' || object)
|
||||||
// - ObjectKey := DAREv2_Dec(KeyEncKey, SealedKey)
|
// - ObjectKey := DAREv2_Dec(KeyEncKey, SealedKey)
|
||||||
// - object_data := DAREv2_Dec(ObjectKey, enc_object_data)
|
// - object_data := DAREv2_Dec(ObjectKey, enc_object_data)
|
||||||
// Output: object_data
|
// Output: object_data
|
||||||
@ -92,7 +92,7 @@
|
|||||||
// - Key, EncKey := Generate(KeyID)
|
// - Key, EncKey := Generate(KeyID)
|
||||||
// - IV := Random({0,1}²⁵⁶)
|
// - IV := Random({0,1}²⁵⁶)
|
||||||
// - ObjectKey := SHA256(Key, Random({0,1}²⁵⁶))
|
// - ObjectKey := SHA256(Key, Random({0,1}²⁵⁶))
|
||||||
// - KeyEncKey := HMAC-SHA256(Key, IV || bucket || object)
|
// - KeyEncKey := HMAC-SHA256(Key, IV || 'SSE-S3' || 'DAREv2-HMAC-SHA256' || bucket || '/' || object)
|
||||||
// - SealedKey := DAREv2_Enc(KeyEncKey, ObjectKey)
|
// - SealedKey := DAREv2_Enc(KeyEncKey, ObjectKey)
|
||||||
// - enc_object_data := DAREv2_Enc(ObjectKey, object_data)
|
// - enc_object_data := DAREv2_Enc(ObjectKey, object_data)
|
||||||
// - metadata <- IV
|
// - metadata <- IV
|
||||||
@ -108,7 +108,7 @@
|
|||||||
// - IV <- metadata
|
// - IV <- metadata
|
||||||
// - SealedKey <- metadata
|
// - SealedKey <- metadata
|
||||||
// - Key := Unseal(KeyID, EncKey)
|
// - Key := Unseal(KeyID, EncKey)
|
||||||
// - KeyEncKey := HMAC-SHA256(Key, IV || bucket || object)
|
// - KeyEncKey := HMAC-SHA256(Key, IV || 'SSE-S3' || 'DAREv2-HMAC-SHA256' || bucket || '/' || object)
|
||||||
// - ObjectKey := DAREv2_Dec(KeyEncKey, SealedKey)
|
// - ObjectKey := DAREv2_Dec(KeyEncKey, SealedKey)
|
||||||
// - object_data := DAREv2_Dec(ObjectKey, enc_object_data)
|
// - object_data := DAREv2_Dec(ObjectKey, enc_object_data)
|
||||||
// Output: object_data
|
// Output: object_data
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
@ -144,9 +145,20 @@ const (
|
|||||||
ServerSideEncryptionSealedKey = ReservedMetadataPrefix + "Server-Side-Encryption-Sealed-Key"
|
ServerSideEncryptionSealedKey = ReservedMetadataPrefix + "Server-Side-Encryption-Sealed-Key"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SSESealAlgorithmDareSha256 specifies DARE as authenticated en/decryption scheme and SHA256 as cryptographic
|
const (
|
||||||
// hash function.
|
// SSESealAlgorithmDareSha256 specifies DARE as authenticated en/decryption scheme and SHA256 as cryptographic
|
||||||
const SSESealAlgorithmDareSha256 = "DARE-SHA256"
|
// hash function. The key derivation of DARE-SHA256 is not optimal and does not include the object path.
|
||||||
|
// It is considered legacy and should not be used anymore.
|
||||||
|
SSESealAlgorithmDareSha256 = "DARE-SHA256"
|
||||||
|
|
||||||
|
// SSESealAlgorithmDareV2HmacSha256 specifies DAREv2 as authenticated en/decryption scheme and SHA256 as cryptographic
|
||||||
|
// hash function for the HMAC PRF.
|
||||||
|
SSESealAlgorithmDareV2HmacSha256 = "DAREv2-HMAC-SHA256"
|
||||||
|
|
||||||
|
// SSEDomain specifies the domain for the derived key - in this case the
|
||||||
|
// key should be used for SSE-C.
|
||||||
|
SSEDomain = "SSE-C"
|
||||||
|
)
|
||||||
|
|
||||||
// hasSSECustomerHeader returns true if the given HTTP header
|
// hasSSECustomerHeader returns true if the given HTTP header
|
||||||
// contains server-side-encryption with customer provided key fields.
|
// contains server-side-encryption with customer provided key fields.
|
||||||
@ -250,10 +262,11 @@ func ParseSSECustomerHeader(header http.Header) (key []byte, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This function rotates old to new key.
|
// This function rotates old to new key.
|
||||||
func rotateKey(oldKey []byte, newKey []byte, metadata map[string]string) error {
|
func rotateKey(oldKey []byte, newKey []byte, bucket, object string, metadata map[string]string) error {
|
||||||
delete(metadata, SSECustomerKey) // make sure we do not save the key by accident
|
delete(metadata, SSECustomerKey) // make sure we do not save the key by accident
|
||||||
|
|
||||||
if metadata[ServerSideEncryptionSealAlgorithm] != SSESealAlgorithmDareSha256 { // currently DARE-SHA256 is the only option
|
algorithm := metadata[ServerSideEncryptionSealAlgorithm]
|
||||||
|
if algorithm != SSESealAlgorithmDareSha256 && algorithm != SSESealAlgorithmDareV2HmacSha256 {
|
||||||
return errObjectTampered
|
return errObjectTampered
|
||||||
}
|
}
|
||||||
iv, err := base64.StdEncoding.DecodeString(metadata[ServerSideEncryptionIV])
|
iv, err := base64.StdEncoding.DecodeString(metadata[ServerSideEncryptionIV])
|
||||||
@ -265,14 +278,33 @@ func rotateKey(oldKey []byte, newKey []byte, metadata map[string]string) error {
|
|||||||
return errObjectTampered
|
return errObjectTampered
|
||||||
}
|
}
|
||||||
|
|
||||||
sha := sha256.New() // derive key encryption key
|
var (
|
||||||
sha.Write(oldKey)
|
minDAREVersion byte
|
||||||
sha.Write(iv)
|
keyEncryptionKey [32]byte
|
||||||
keyEncryptionKey := sha.Sum(nil)
|
)
|
||||||
|
switch algorithm {
|
||||||
|
default:
|
||||||
|
return errObjectTampered
|
||||||
|
case SSESealAlgorithmDareSha256: // legacy key-encryption-key derivation
|
||||||
|
minDAREVersion = sio.Version10
|
||||||
|
sha := sha256.New()
|
||||||
|
sha.Write(oldKey)
|
||||||
|
sha.Write(iv)
|
||||||
|
sha.Sum(keyEncryptionKey[:0])
|
||||||
|
case SSESealAlgorithmDareV2HmacSha256: // key-encryption-key derivation - See: crypto/doc.go
|
||||||
|
minDAREVersion = sio.Version20
|
||||||
|
mac := hmac.New(sha256.New, oldKey)
|
||||||
|
mac.Write(iv)
|
||||||
|
mac.Write([]byte(SSEDomain))
|
||||||
|
mac.Write([]byte(SSESealAlgorithmDareV2HmacSha256))
|
||||||
|
mac.Write([]byte(path.Join(bucket, object)))
|
||||||
|
mac.Sum(keyEncryptionKey[:0])
|
||||||
|
}
|
||||||
|
|
||||||
objectEncryptionKey := bytes.NewBuffer(nil) // decrypt object encryption key
|
objectEncryptionKey := bytes.NewBuffer(nil) // decrypt object encryption key
|
||||||
n, err := sio.Decrypt(objectEncryptionKey, bytes.NewReader(sealedKey), sio.Config{
|
n, err := sio.Decrypt(objectEncryptionKey, bytes.NewReader(sealedKey), sio.Config{
|
||||||
Key: keyEncryptionKey,
|
MinVersion: minDAREVersion,
|
||||||
|
Key: keyEncryptionKey[:],
|
||||||
})
|
})
|
||||||
if n != 32 || err != nil { // Either the provided key does not match or the object was tampered.
|
if n != 32 || err != nil { // Either the provided key does not match or the object was tampered.
|
||||||
if subtle.ConstantTimeCompare(oldKey, newKey) == 1 {
|
if subtle.ConstantTimeCompare(oldKey, newKey) == 1 {
|
||||||
@ -280,46 +312,34 @@ func rotateKey(oldKey []byte, newKey []byte, metadata map[string]string) error {
|
|||||||
}
|
}
|
||||||
return errSSEKeyMismatch // To provide strict AWS S3 compatibility we return: access denied.
|
return errSSEKeyMismatch // To provide strict AWS S3 compatibility we return: access denied.
|
||||||
}
|
}
|
||||||
if subtle.ConstantTimeCompare(oldKey, newKey) == 1 {
|
if subtle.ConstantTimeCompare(oldKey, newKey) == 1 && algorithm != SSESealAlgorithmDareSha256 {
|
||||||
return nil // we don't need to rotate keys if newKey == oldKey
|
return nil // we don't need to rotate keys if newKey == oldKey but we may have to upgrade KDF algorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
nonce := make([]byte, 32) // generate random values for key derivation
|
mac := hmac.New(sha256.New, newKey) // key-encryption-key derivation - See: crypto/doc.go
|
||||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
mac.Write(iv)
|
||||||
return err
|
mac.Write([]byte(SSEDomain))
|
||||||
}
|
mac.Write([]byte(SSESealAlgorithmDareV2HmacSha256))
|
||||||
|
mac.Write([]byte(path.Join(bucket, object)))
|
||||||
niv := sha256.Sum256(nonce[:]) // derive key encryption key
|
mac.Sum(keyEncryptionKey[:0])
|
||||||
sha = sha256.New()
|
|
||||||
sha.Write(newKey)
|
|
||||||
sha.Write(niv[:])
|
|
||||||
keyEncryptionKey = sha.Sum(nil)
|
|
||||||
|
|
||||||
sealedKeyW := bytes.NewBuffer(nil) // sealedKey := 16 byte header + 32 byte payload + 16 byte tag
|
sealedKeyW := bytes.NewBuffer(nil) // sealedKey := 16 byte header + 32 byte payload + 16 byte tag
|
||||||
n, err = sio.Encrypt(sealedKeyW, bytes.NewReader(objectEncryptionKey.Bytes()), sio.Config{
|
n, err = sio.Encrypt(sealedKeyW, bytes.NewReader(objectEncryptionKey.Bytes()), sio.Config{
|
||||||
Key: keyEncryptionKey,
|
Key: keyEncryptionKey[:],
|
||||||
})
|
})
|
||||||
if n != 64 || err != nil {
|
if n != 64 || err != nil {
|
||||||
return errors.New("failed to seal object encryption key") // if this happens there's a bug in the code (may panic ?)
|
return errors.New("failed to seal object encryption key") // if this happens there's a bug in the code (may panic ?)
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata[ServerSideEncryptionIV] = base64.StdEncoding.EncodeToString(niv[:])
|
metadata[ServerSideEncryptionIV] = base64.StdEncoding.EncodeToString(iv[:])
|
||||||
metadata[ServerSideEncryptionSealAlgorithm] = SSESealAlgorithmDareSha256
|
metadata[ServerSideEncryptionSealAlgorithm] = SSESealAlgorithmDareV2HmacSha256
|
||||||
metadata[ServerSideEncryptionSealedKey] = base64.StdEncoding.EncodeToString(sealedKeyW.Bytes())
|
metadata[ServerSideEncryptionSealedKey] = base64.StdEncoding.EncodeToString(sealedKeyW.Bytes())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newEncryptMetadata(key []byte, metadata map[string]string) ([]byte, error) {
|
func newEncryptMetadata(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) {
|
||||||
delete(metadata, SSECustomerKey) // make sure we do not save the key by accident
|
delete(metadata, SSECustomerKey) // make sure we do not save the key by accident
|
||||||
|
// See crypto/doc.go for detailed description
|
||||||
// security notice:
|
|
||||||
// - If the first 32 bytes of the random value are ever repeated under the same client-provided
|
|
||||||
// key the encrypted object will not be tamper-proof. [ P(coll) ~= 1 / 2^(256 / 2)]
|
|
||||||
// - If the last 32 bytes of the random value are ever repeated under the same client-provided
|
|
||||||
// key an adversary may be able to extract the object encryption key. This depends on the
|
|
||||||
// authenticated en/decryption scheme. The DARE format will generate an 8 byte nonce which must
|
|
||||||
// be repeated in addition to reveal the object encryption key.
|
|
||||||
// [ P(coll) ~= 1 / 2^((256 + 64) / 2) ]
|
|
||||||
nonce := make([]byte, 32+SSEIVSize) // generate random values for key derivation
|
nonce := make([]byte, 32+SSEIVSize) // generate random values for key derivation
|
||||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -329,11 +349,13 @@ func newEncryptMetadata(key []byte, metadata map[string]string) ([]byte, error)
|
|||||||
sha.Write(nonce[:32])
|
sha.Write(nonce[:32])
|
||||||
objectEncryptionKey := sha.Sum(nil)
|
objectEncryptionKey := sha.Sum(nil)
|
||||||
|
|
||||||
iv := sha256.Sum256(nonce[32:]) // derive key encryption key
|
iv := sha256.Sum256(nonce[32:]) // key-encryption-key derivation - See: crypto/doc.go
|
||||||
sha = sha256.New()
|
mac := hmac.New(sha256.New, key)
|
||||||
sha.Write(key)
|
mac.Write(iv[:])
|
||||||
sha.Write(iv[:])
|
mac.Write([]byte(SSEDomain))
|
||||||
keyEncryptionKey := sha.Sum(nil)
|
mac.Write([]byte(SSESealAlgorithmDareV2HmacSha256))
|
||||||
|
mac.Write([]byte(path.Join(bucket, object)))
|
||||||
|
keyEncryptionKey := mac.Sum(nil)
|
||||||
|
|
||||||
sealedKey := bytes.NewBuffer(nil) // sealedKey := 16 byte header + 32 byte payload + 16 byte tag
|
sealedKey := bytes.NewBuffer(nil) // sealedKey := 16 byte header + 32 byte payload + 16 byte tag
|
||||||
n, err := sio.Encrypt(sealedKey, bytes.NewReader(objectEncryptionKey), sio.Config{
|
n, err := sio.Encrypt(sealedKey, bytes.NewReader(objectEncryptionKey), sio.Config{
|
||||||
@ -344,14 +366,14 @@ func newEncryptMetadata(key []byte, metadata map[string]string) ([]byte, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
metadata[ServerSideEncryptionIV] = base64.StdEncoding.EncodeToString(iv[:])
|
metadata[ServerSideEncryptionIV] = base64.StdEncoding.EncodeToString(iv[:])
|
||||||
metadata[ServerSideEncryptionSealAlgorithm] = SSESealAlgorithmDareSha256
|
metadata[ServerSideEncryptionSealAlgorithm] = SSESealAlgorithmDareV2HmacSha256
|
||||||
metadata[ServerSideEncryptionSealedKey] = base64.StdEncoding.EncodeToString(sealedKey.Bytes())
|
metadata[ServerSideEncryptionSealedKey] = base64.StdEncoding.EncodeToString(sealedKey.Bytes())
|
||||||
|
|
||||||
return objectEncryptionKey, nil
|
return objectEncryptionKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newEncryptReader(content io.Reader, key []byte, metadata map[string]string) (io.Reader, error) {
|
func newEncryptReader(content io.Reader, key []byte, bucket, object string, metadata map[string]string) (io.Reader, error) {
|
||||||
objectEncryptionKey, err := newEncryptMetadata(key, metadata)
|
objectEncryptionKey, err := newEncryptMetadata(key, bucket, object, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -367,29 +389,26 @@ func newEncryptReader(content io.Reader, key []byte, metadata map[string]string)
|
|||||||
// EncryptRequest takes the client provided content and encrypts the data
|
// EncryptRequest takes the client provided content and encrypts the data
|
||||||
// with the client provided key. It also marks the object as client-side-encrypted
|
// with the client provided key. It also marks the object as client-side-encrypted
|
||||||
// and sets the correct headers.
|
// and sets the correct headers.
|
||||||
func EncryptRequest(content io.Reader, r *http.Request, metadata map[string]string) (io.Reader, error) {
|
func EncryptRequest(content io.Reader, r *http.Request, bucket, object string, metadata map[string]string) (io.Reader, error) {
|
||||||
key, err := ParseSSECustomerRequest(r)
|
key, err := ParseSSECustomerRequest(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newEncryptReader(content, key, metadata)
|
return newEncryptReader(content, key, bucket, object, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecryptCopyRequest decrypts the object with the client provided key. It also removes
|
// DecryptCopyRequest decrypts the object with the client provided key. It also removes
|
||||||
// the client-side-encryption metadata from the object and sets the correct headers.
|
// the client-side-encryption metadata from the object and sets the correct headers.
|
||||||
func DecryptCopyRequest(client io.Writer, r *http.Request, metadata map[string]string) (io.WriteCloser, error) {
|
func DecryptCopyRequest(client io.Writer, r *http.Request, bucket, object string, metadata map[string]string) (io.WriteCloser, error) {
|
||||||
key, err := ParseSSECopyCustomerRequest(r)
|
key, err := ParseSSECopyCustomerRequest(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
delete(metadata, SSECopyCustomerKey) // make sure we do not save the key by accident
|
delete(metadata, SSECopyCustomerKey) // make sure we do not save the key by accident
|
||||||
return newDecryptWriter(client, key, 0, metadata)
|
return newDecryptWriter(client, key, bucket, object, 0, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decryptObjectInfo(key []byte, metadata map[string]string) ([]byte, error) {
|
func decryptObjectInfo(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) {
|
||||||
if metadata[ServerSideEncryptionSealAlgorithm] != SSESealAlgorithmDareSha256 { // currently DARE-SHA256 is the only option
|
|
||||||
return nil, errObjectTampered
|
|
||||||
}
|
|
||||||
iv, err := base64.StdEncoding.DecodeString(metadata[ServerSideEncryptionIV])
|
iv, err := base64.StdEncoding.DecodeString(metadata[ServerSideEncryptionIV])
|
||||||
if err != nil || len(iv) != SSEIVSize {
|
if err != nil || len(iv) != SSEIVSize {
|
||||||
return nil, errObjectTampered
|
return nil, errObjectTampered
|
||||||
@ -399,14 +418,33 @@ func decryptObjectInfo(key []byte, metadata map[string]string) ([]byte, error) {
|
|||||||
return nil, errObjectTampered
|
return nil, errObjectTampered
|
||||||
}
|
}
|
||||||
|
|
||||||
sha := sha256.New() // derive key encryption key
|
var (
|
||||||
sha.Write(key)
|
minDAREVersion byte
|
||||||
sha.Write(iv)
|
keyEncryptionKey [32]byte
|
||||||
keyEncryptionKey := sha.Sum(nil)
|
)
|
||||||
|
switch algorithm := metadata[ServerSideEncryptionSealAlgorithm]; algorithm {
|
||||||
|
default:
|
||||||
|
return nil, errObjectTampered
|
||||||
|
case SSESealAlgorithmDareSha256: // legacy key-encryption-key derivation
|
||||||
|
minDAREVersion = sio.Version10
|
||||||
|
sha := sha256.New()
|
||||||
|
sha.Write(key)
|
||||||
|
sha.Write(iv)
|
||||||
|
sha.Sum(keyEncryptionKey[:0])
|
||||||
|
case SSESealAlgorithmDareV2HmacSha256: // key-encryption-key derivation - See: crypto/doc.go
|
||||||
|
minDAREVersion = sio.Version20
|
||||||
|
mac := hmac.New(sha256.New, key)
|
||||||
|
mac.Write(iv)
|
||||||
|
mac.Write([]byte(SSEDomain))
|
||||||
|
mac.Write([]byte(SSESealAlgorithmDareV2HmacSha256))
|
||||||
|
mac.Write([]byte(path.Join(bucket, object)))
|
||||||
|
mac.Sum(keyEncryptionKey[:0])
|
||||||
|
}
|
||||||
|
|
||||||
objectEncryptionKey := bytes.NewBuffer(nil) // decrypt object encryption key
|
objectEncryptionKey := bytes.NewBuffer(nil) // decrypt object encryption key
|
||||||
n, err := sio.Decrypt(objectEncryptionKey, bytes.NewReader(sealedKey), sio.Config{
|
n, err := sio.Decrypt(objectEncryptionKey, bytes.NewReader(sealedKey), sio.Config{
|
||||||
Key: keyEncryptionKey,
|
MinVersion: minDAREVersion,
|
||||||
|
Key: keyEncryptionKey[:],
|
||||||
})
|
})
|
||||||
if n != 32 || err != nil {
|
if n != 32 || err != nil {
|
||||||
// Either the provided key does not match or the object was tampered.
|
// Either the provided key does not match or the object was tampered.
|
||||||
@ -416,11 +454,10 @@ func decryptObjectInfo(key []byte, metadata map[string]string) ([]byte, error) {
|
|||||||
return objectEncryptionKey.Bytes(), nil
|
return objectEncryptionKey.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDecryptWriter(client io.Writer, key []byte, seqNumber uint32, metadata map[string]string) (io.WriteCloser, error) {
|
func newDecryptWriter(client io.Writer, key []byte, bucket, object string, seqNumber uint32, metadata map[string]string) (io.WriteCloser, error) {
|
||||||
objectEncryptionKey, err := decryptObjectInfo(key, metadata)
|
objectEncryptionKey, err := decryptObjectInfo(key, bucket, object, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
||||||
}
|
}
|
||||||
return newDecryptWriterWithObjectKey(client, objectEncryptionKey, seqNumber, metadata)
|
return newDecryptWriterWithObjectKey(client, objectEncryptionKey, seqNumber, metadata)
|
||||||
}
|
}
|
||||||
@ -443,19 +480,19 @@ func newDecryptWriterWithObjectKey(client io.Writer, objectEncryptionKey []byte,
|
|||||||
|
|
||||||
// DecryptRequestWithSequenceNumber decrypts the object with the client provided key. It also removes
|
// DecryptRequestWithSequenceNumber decrypts the object with the client provided key. It also removes
|
||||||
// the client-side-encryption metadata from the object and sets the correct headers.
|
// the client-side-encryption metadata from the object and sets the correct headers.
|
||||||
func DecryptRequestWithSequenceNumber(client io.Writer, r *http.Request, seqNumber uint32, metadata map[string]string) (io.WriteCloser, error) {
|
func DecryptRequestWithSequenceNumber(client io.Writer, r *http.Request, bucket, object string, seqNumber uint32, metadata map[string]string) (io.WriteCloser, error) {
|
||||||
key, err := ParseSSECustomerRequest(r)
|
key, err := ParseSSECustomerRequest(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
delete(metadata, SSECustomerKey) // make sure we do not save the key by accident
|
delete(metadata, SSECustomerKey) // make sure we do not save the key by accident
|
||||||
return newDecryptWriter(client, key, seqNumber, metadata)
|
return newDecryptWriter(client, key, bucket, object, seqNumber, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecryptRequest decrypts the object with the client provided key. It also removes
|
// DecryptRequest decrypts the object with the client provided key. It also removes
|
||||||
// the client-side-encryption metadata from the object and sets the correct headers.
|
// the client-side-encryption metadata from the object and sets the correct headers.
|
||||||
func DecryptRequest(client io.Writer, r *http.Request, metadata map[string]string) (io.WriteCloser, error) {
|
func DecryptRequest(client io.Writer, r *http.Request, bucket, object string, metadata map[string]string) (io.WriteCloser, error) {
|
||||||
return DecryptRequestWithSequenceNumber(client, r, 0, metadata)
|
return DecryptRequestWithSequenceNumber(client, r, bucket, object, 0, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecryptBlocksWriter - decrypts multipart parts, while implementing a io.Writer compatible interface.
|
// DecryptBlocksWriter - decrypts multipart parts, while implementing a io.Writer compatible interface.
|
||||||
@ -469,9 +506,10 @@ type DecryptBlocksWriter struct {
|
|||||||
// Current part index
|
// Current part index
|
||||||
partIndex int
|
partIndex int
|
||||||
// Parts information
|
// Parts information
|
||||||
parts []objectPartInfo
|
parts []objectPartInfo
|
||||||
req *http.Request
|
req *http.Request
|
||||||
metadata map[string]string
|
bucket, object string
|
||||||
|
metadata map[string]string
|
||||||
|
|
||||||
partEncRelOffset int64
|
partEncRelOffset int64
|
||||||
|
|
||||||
@ -499,7 +537,7 @@ func (w *DecryptBlocksWriter) buildDecrypter(partID int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
objectEncryptionKey, err := decryptObjectInfo(key, m)
|
objectEncryptionKey, err := decryptObjectInfo(key, w.bucket, w.object, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -594,14 +632,14 @@ func (w *DecryptBlocksWriter) Close() error {
|
|||||||
// DecryptAllBlocksCopyRequest - setup a struct which can decrypt many concatenated encrypted data
|
// DecryptAllBlocksCopyRequest - setup a struct which can decrypt many concatenated encrypted data
|
||||||
// parts information helps to know the boundaries of each encrypted data block, this function decrypts
|
// parts information helps to know the boundaries of each encrypted data block, this function decrypts
|
||||||
// all parts starting from part-1.
|
// all parts starting from part-1.
|
||||||
func DecryptAllBlocksCopyRequest(client io.Writer, r *http.Request, objInfo ObjectInfo) (io.WriteCloser, int64, error) {
|
func DecryptAllBlocksCopyRequest(client io.Writer, r *http.Request, bucket, object string, objInfo ObjectInfo) (io.WriteCloser, int64, error) {
|
||||||
w, _, size, err := DecryptBlocksRequest(client, r, 0, objInfo.Size, objInfo, true)
|
w, _, size, err := DecryptBlocksRequest(client, r, bucket, object, 0, objInfo.Size, objInfo, true)
|
||||||
return w, size, err
|
return w, size, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecryptBlocksRequest - setup a struct which can decrypt many concatenated encrypted data
|
// DecryptBlocksRequest - setup a struct which can decrypt many concatenated encrypted data
|
||||||
// parts information helps to know the boundaries of each encrypted data block.
|
// parts information helps to know the boundaries of each encrypted data block.
|
||||||
func DecryptBlocksRequest(client io.Writer, r *http.Request, startOffset, length int64, objInfo ObjectInfo, copySource bool) (io.WriteCloser, int64, int64, error) {
|
func DecryptBlocksRequest(client io.Writer, r *http.Request, bucket, object string, startOffset, length int64, objInfo ObjectInfo, copySource bool) (io.WriteCloser, int64, int64, error) {
|
||||||
seqNumber, encStartOffset, encLength := getEncryptedStartOffset(startOffset, length)
|
seqNumber, encStartOffset, encLength := getEncryptedStartOffset(startOffset, length)
|
||||||
|
|
||||||
// Encryption length cannot be bigger than the file size, if it is
|
// Encryption length cannot be bigger than the file size, if it is
|
||||||
@ -614,9 +652,9 @@ func DecryptBlocksRequest(client io.Writer, r *http.Request, startOffset, length
|
|||||||
var writer io.WriteCloser
|
var writer io.WriteCloser
|
||||||
var err error
|
var err error
|
||||||
if copySource {
|
if copySource {
|
||||||
writer, err = DecryptCopyRequest(client, r, objInfo.UserDefined)
|
writer, err = DecryptCopyRequest(client, r, bucket, object, objInfo.UserDefined)
|
||||||
} else {
|
} else {
|
||||||
writer, err = DecryptRequestWithSequenceNumber(client, r, seqNumber, objInfo.UserDefined)
|
writer, err = DecryptRequestWithSequenceNumber(client, r, bucket, object, seqNumber, objInfo.UserDefined)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
@ -656,6 +694,8 @@ func DecryptBlocksRequest(client io.Writer, r *http.Request, startOffset, length
|
|||||||
parts: objInfo.Parts,
|
parts: objInfo.Parts,
|
||||||
partIndex: partStartIndex,
|
partIndex: partStartIndex,
|
||||||
req: r,
|
req: r,
|
||||||
|
bucket: bucket,
|
||||||
|
object: object,
|
||||||
customerKeyHeader: r.Header.Get(SSECustomerKey),
|
customerKeyHeader: r.Header.Get(SSECustomerKey),
|
||||||
copySource: copySource,
|
copySource: copySource,
|
||||||
}
|
}
|
||||||
|
@ -308,7 +308,7 @@ func TestEncryptRequest(t *testing.T) {
|
|||||||
for k, v := range test.header {
|
for k, v := range test.header {
|
||||||
req.Header.Set(k, v)
|
req.Header.Set(k, v)
|
||||||
}
|
}
|
||||||
_, err := EncryptRequest(content, req, test.metadata)
|
_, err := EncryptRequest(content, req, "bucket", "object", test.metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %d: Failed to encrypt request: %v", i, err)
|
t.Fatalf("Test %d: Failed to encrypt request: %v", i, err)
|
||||||
}
|
}
|
||||||
@ -328,11 +328,14 @@ func TestEncryptRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var decryptRequestTests = []struct {
|
var decryptRequestTests = []struct {
|
||||||
header map[string]string
|
bucket, object string
|
||||||
metadata map[string]string
|
header map[string]string
|
||||||
shouldFail bool
|
metadata map[string]string
|
||||||
|
shouldFail bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
bucket: "bucket",
|
||||||
|
object: "object",
|
||||||
header: map[string]string{
|
header: map[string]string{
|
||||||
SSECustomerAlgorithm: "AES256",
|
SSECustomerAlgorithm: "AES256",
|
||||||
SSECustomerKey: "MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ=",
|
SSECustomerKey: "MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ=",
|
||||||
@ -346,6 +349,23 @@ var decryptRequestTests = []struct {
|
|||||||
shouldFail: false,
|
shouldFail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
bucket: "bucket",
|
||||||
|
object: "object",
|
||||||
|
header: map[string]string{
|
||||||
|
SSECustomerAlgorithm: "AES256",
|
||||||
|
SSECustomerKey: "MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ=",
|
||||||
|
SSECustomerKeyMD5: "7PpPLAK26ONlVUGOWlusfg==",
|
||||||
|
},
|
||||||
|
metadata: map[string]string{
|
||||||
|
ServerSideEncryptionSealAlgorithm: SSESealAlgorithmDareV2HmacSha256,
|
||||||
|
ServerSideEncryptionIV: "qEqmsONcorqlcZXJxaw32H04eyXyXwUgjHzlhkaIYrU=",
|
||||||
|
ServerSideEncryptionSealedKey: "IAAfAIM14ugTGcM/dIrn4iQMrkl1sjKyeBQ8FBEvRebYj8vWvxG+0cJRpC6NXRU1wJN50JaUOATjO7kz0wZ2mA==",
|
||||||
|
},
|
||||||
|
shouldFail: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
bucket: "bucket",
|
||||||
|
object: "object",
|
||||||
header: map[string]string{
|
header: map[string]string{
|
||||||
SSECustomerAlgorithm: "AES256",
|
SSECustomerAlgorithm: "AES256",
|
||||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||||
@ -359,6 +379,8 @@ var decryptRequestTests = []struct {
|
|||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
bucket: "bucket",
|
||||||
|
object: "object",
|
||||||
header: map[string]string{
|
header: map[string]string{
|
||||||
SSECustomerAlgorithm: "AES256",
|
SSECustomerAlgorithm: "AES256",
|
||||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||||
@ -372,6 +394,8 @@ var decryptRequestTests = []struct {
|
|||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
bucket: "bucket",
|
||||||
|
object: "object",
|
||||||
header: map[string]string{
|
header: map[string]string{
|
||||||
SSECustomerAlgorithm: "AES256",
|
SSECustomerAlgorithm: "AES256",
|
||||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||||
@ -384,21 +408,39 @@ var decryptRequestTests = []struct {
|
|||||||
},
|
},
|
||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
bucket: "bucket",
|
||||||
|
object: "object-2",
|
||||||
|
header: map[string]string{
|
||||||
|
SSECustomerAlgorithm: "AES256",
|
||||||
|
SSECustomerKey: "MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ=",
|
||||||
|
SSECustomerKeyMD5: "7PpPLAK26ONlVUGOWlusfg==",
|
||||||
|
},
|
||||||
|
metadata: map[string]string{
|
||||||
|
ServerSideEncryptionSealAlgorithm: SSESealAlgorithmDareV2HmacSha256,
|
||||||
|
ServerSideEncryptionIV: "qEqmsONcorqlcZXJxaw32H04eyXyXwUgjHzlhkaIYrU=",
|
||||||
|
ServerSideEncryptionSealedKey: "IAAfAIM14ugTGcM/dIrn4iQMrkl1sjKyeBQ8FBEvRebYj8vWvxG+0cJRpC6NXRU1wJN50JaUOATjO7kz0wZ2mA==",
|
||||||
|
},
|
||||||
|
shouldFail: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecryptRequest(t *testing.T) {
|
func TestDecryptRequest(t *testing.T) {
|
||||||
defer func(flag bool) { globalIsSSL = flag }(globalIsSSL)
|
defer func(flag bool) { globalIsSSL = flag }(globalIsSSL)
|
||||||
globalIsSSL = true
|
globalIsSSL = true
|
||||||
for i, test := range decryptRequestTests {
|
for i, test := range decryptRequestTests[1:] {
|
||||||
client := bytes.NewBuffer(nil)
|
client := bytes.NewBuffer(nil)
|
||||||
req := &http.Request{Header: http.Header{}}
|
req := &http.Request{Header: http.Header{}}
|
||||||
for k, v := range test.header {
|
for k, v := range test.header {
|
||||||
req.Header.Set(k, v)
|
req.Header.Set(k, v)
|
||||||
}
|
}
|
||||||
_, err := DecryptRequest(client, req, test.metadata)
|
_, err := DecryptRequest(client, req, test.bucket, test.object, test.metadata)
|
||||||
if err != nil && !test.shouldFail {
|
if err != nil && !test.shouldFail {
|
||||||
t.Fatalf("Test %d: Failed to encrypt request: %v", i, err)
|
t.Fatalf("Test %d: Failed to encrypt request: %v", i, err)
|
||||||
}
|
}
|
||||||
|
if err == nil && test.shouldFail {
|
||||||
|
t.Fatalf("Test %d: should fail but passed", i)
|
||||||
|
}
|
||||||
if key, ok := test.metadata[SSECustomerKey]; ok {
|
if key, ok := test.metadata[SSECustomerKey]; ok {
|
||||||
t.Errorf("Test %d: Client provided key survived in metadata - key: %s", i, key)
|
t.Errorf("Test %d: Client provided key survived in metadata - key: %s", i, key)
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
// additionally also skipping mod(offset)64KiB boundaries.
|
// additionally also skipping mod(offset)64KiB boundaries.
|
||||||
writer = ioutil.LimitedWriter(writer, startOffset%(64*1024), length)
|
writer = ioutil.LimitedWriter(writer, startOffset%(64*1024), length)
|
||||||
|
|
||||||
writer, startOffset, length, err = DecryptBlocksRequest(writer, r, startOffset, length, objInfo, false)
|
writer, startOffset, length, err = DecryptBlocksRequest(writer, r, bucket, object, startOffset, length, objInfo, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
return
|
return
|
||||||
@ -270,7 +270,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
writeErrorResponse(w, apiErr, r.URL)
|
writeErrorResponse(w, apiErr, r.URL)
|
||||||
return
|
return
|
||||||
} else if encrypted {
|
} else if encrypted {
|
||||||
if _, err = DecryptRequest(w, r, objInfo.UserDefined); err != nil {
|
if _, err = DecryptRequest(w, r, bucket, object, objInfo.UserDefined); err != nil {
|
||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -463,7 +463,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
for k, v := range srcInfo.UserDefined {
|
for k, v := range srcInfo.UserDefined {
|
||||||
encMetadata[k] = v
|
encMetadata[k] = v
|
||||||
}
|
}
|
||||||
if err = rotateKey(oldKey, newKey, encMetadata); err != nil {
|
if err = rotateKey(oldKey, newKey, srcBucket, srcObject, encMetadata); err != nil {
|
||||||
pipeWriter.CloseWithError(err)
|
pipeWriter.CloseWithError(err)
|
||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
return
|
return
|
||||||
@ -475,7 +475,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
if sseCopyC {
|
if sseCopyC {
|
||||||
// Source is encrypted make sure to save the encrypted size.
|
// Source is encrypted make sure to save the encrypted size.
|
||||||
writer = ioutil.LimitedWriter(writer, 0, srcInfo.Size)
|
writer = ioutil.LimitedWriter(writer, 0, srcInfo.Size)
|
||||||
writer, srcInfo.Size, err = DecryptAllBlocksCopyRequest(writer, r, srcInfo)
|
writer, srcInfo.Size, err = DecryptAllBlocksCopyRequest(writer, r, srcBucket, srcObject, srcInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pipeWriter.CloseWithError(err)
|
pipeWriter.CloseWithError(err)
|
||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
@ -490,7 +490,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sseC {
|
if sseC {
|
||||||
reader, err = newEncryptReader(pipeReader, newKey, encMetadata)
|
reader, err = newEncryptReader(reader, newKey, dstBucket, dstObject, encMetadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pipeWriter.CloseWithError(err)
|
pipeWriter.CloseWithError(err)
|
||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
@ -789,7 +789,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
|
|
||||||
if objectAPI.IsEncryptionSupported() {
|
if objectAPI.IsEncryptionSupported() {
|
||||||
if hasSSECustomerHeader(r.Header) && !hasSuffix(object, slashSeparator) { // handle SSE-C requests
|
if hasSSECustomerHeader(r.Header) && !hasSuffix(object, slashSeparator) { // handle SSE-C requests
|
||||||
reader, err = EncryptRequest(hashReader, r, metadata)
|
reader, err = EncryptRequest(hashReader, r, bucket, object, metadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
return
|
return
|
||||||
@ -888,7 +888,7 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
|
|||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = newEncryptMetadata(key, encMetadata)
|
_, err = newEncryptMetadata(key, bucket, object, encMetadata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
return
|
return
|
||||||
@ -1056,7 +1056,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
|
|||||||
// Response writer should be limited early on for decryption upto required length,
|
// Response writer should be limited early on for decryption upto required length,
|
||||||
// additionally also skipping mod(offset)64KiB boundaries.
|
// additionally also skipping mod(offset)64KiB boundaries.
|
||||||
writer = ioutil.LimitedWriter(writer, startOffset%(64*1024), length)
|
writer = ioutil.LimitedWriter(writer, startOffset%(64*1024), length)
|
||||||
writer, startOffset, length, err = DecryptBlocksRequest(pipeWriter, r, startOffset, length, srcInfo, true)
|
writer, startOffset, length, err = DecryptBlocksRequest(writer, r, srcBucket, srcObject, startOffset, length, srcInfo, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pipeWriter.CloseWithError(err)
|
pipeWriter.CloseWithError(err)
|
||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
@ -1078,14 +1078,14 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
|
|||||||
|
|
||||||
// Calculating object encryption key
|
// Calculating object encryption key
|
||||||
var objectEncryptionKey []byte
|
var objectEncryptionKey []byte
|
||||||
objectEncryptionKey, err = decryptObjectInfo(key, li.UserDefined)
|
objectEncryptionKey, err = decryptObjectInfo(key, dstBucket, dstObject, li.UserDefined)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pipeWriter.CloseWithError(err)
|
pipeWriter.CloseWithError(err)
|
||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reader, err = sio.EncryptReader(pipeReader, sio.Config{Key: objectEncryptionKey})
|
reader, err = sio.EncryptReader(reader, sio.Config{Key: objectEncryptionKey})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pipeWriter.CloseWithError(err)
|
pipeWriter.CloseWithError(err)
|
||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
@ -1281,7 +1281,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
|
|||||||
|
|
||||||
// Calculating object encryption key
|
// Calculating object encryption key
|
||||||
var objectEncryptionKey []byte
|
var objectEncryptionKey []byte
|
||||||
objectEncryptionKey, err = decryptObjectInfo(key, li.UserDefined)
|
objectEncryptionKey, err = decryptObjectInfo(key, bucket, object, li.UserDefined)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user