diff --git a/cmd/encryption-v1.go b/cmd/encryption-v1.go index da9e97bc9..3b93492b1 100644 --- a/cmd/encryption-v1.go +++ b/cmd/encryption-v1.go @@ -601,28 +601,44 @@ func DecryptBlocksRequestR(inputReader io.Reader, h http.Header, seqNumber uint3 partEncRelOffset := int64(seqNumber) * (SSEDAREPackageBlockSize + SSEDAREPackageMetaSize) w := &DecryptBlocksReader{ - reader: inputReader, - startSeqNum: seqNumber, - partDecRelOffset: partDecRelOffset, - partEncRelOffset: partEncRelOffset, - parts: oi.Parts, - partIndex: partStart, - header: h, - bucket: bucket, - object: object, - customerKeyHeader: h.Get(xhttp.AmzServerSideEncryptionCustomerKey), - copySource: copySource, - metadata: cloneMSS(oi.UserDefined), + reader: inputReader, + startSeqNum: seqNumber, + partDecRelOffset: partDecRelOffset, + partEncRelOffset: partEncRelOffset, + parts: oi.Parts, + partIndex: partStart, } - if w.copySource { - w.customerKeyHeader = h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKey) + // In case of SSE-C, we have to decrypt the OEK using the client-provided key. + // In case of a SSE-C server-side copy, the client might provide two keys, + // one for the source and one for the target. This reader is the source. + var ssecClientKey []byte + if crypto.SSEC.IsEncrypted(oi.UserDefined) { + if copySource && crypto.SSECopy.IsRequested(h) { + key, err := crypto.SSECopy.ParseHTTP(h) + if err != nil { + return nil, err + } + ssecClientKey = key[:] + } else { + key, err := crypto.SSEC.ParseHTTP(h) + if err != nil { + return nil, err + } + ssecClientKey = key[:] + } } + // Decrypt the OEK once and reuse it for all subsequent parts. + objectEncryptionKey, err := decryptObjectMeta(ssecClientKey, bucket, object, oi.UserDefined) + if err != nil { + return nil, err + } + w.objectEncryptionKey = objectEncryptionKey + if err := w.buildDecrypter(w.parts[w.partIndex].Number); err != nil { return nil, err } - return w, nil } @@ -638,48 +654,17 @@ type DecryptBlocksReader struct { // Current part index partIndex int // Parts information - parts []ObjectPartInfo - header http.Header - bucket, object string - metadata map[string]string + parts []ObjectPartInfo + objectEncryptionKey []byte partDecRelOffset, partEncRelOffset int64 - - copySource bool - // Customer Key - customerKeyHeader string } func (d *DecryptBlocksReader) buildDecrypter(partID int) error { - m := cloneMSS(d.metadata) - // Initialize the first decrypter; new decrypters will be - // initialized in Read() operation as needed. - var key []byte - var err error - if d.copySource { - if crypto.SSEC.IsEncrypted(d.metadata) { - d.header.Set(xhttp.AmzServerSideEncryptionCopyCustomerKey, d.customerKeyHeader) - key, err = ParseSSECopyCustomerRequest(d.header, d.metadata) - } - } else { - if crypto.SSEC.IsEncrypted(d.metadata) { - d.header.Set(xhttp.AmzServerSideEncryptionCustomerKey, d.customerKeyHeader) - key, err = ParseSSECustomerHeader(d.header) - } - } - if err != nil { - return err - } - - objectEncryptionKey, err := decryptObjectMeta(key, d.bucket, d.object, m) - if err != nil { - return err - } - var partIDbin [4]byte binary.LittleEndian.PutUint32(partIDbin[:], uint32(partID)) // marshal part ID - mac := hmac.New(sha256.New, objectEncryptionKey) // derive part encryption key from part ID and object key + mac := hmac.New(sha256.New, d.objectEncryptionKey) // derive part encryption key from part ID and object key mac.Write(partIDbin[:]) partEncryptionKey := mac.Sum(nil)