mirror of
https://github.com/minio/minio.git
synced 2025-02-03 09:55:59 -05:00
improve multipart decryption (#20324)
This commit simplifies and optimizes the decryption of large (multipart) objects. This PR does two things: - Re-write the init logic for the decryption reader - Reduce the number of OEK decryptions Before, the init logic copied some SSE HTTP request headers to parse them later. This is simplified to parsing them right away. This removes some fields from the decryption reader struct. Further, the decryption reader decrypted the OEK using the client-provided key (SSE-C) or the KMS (SSE-S3 / SSE-KMS) for each part. This is redundant since the OEK is the same for all parts. In particular, a KMS call might be a network request. Now, the OEK is decrypted once for the entire multipart object. This should improve latency when reading encrypted multipart objects and reduce requests to the KMS. Signed-off-by: Andreas Auernhammer <github@aead.dev>
This commit is contained in:
parent
006cacfefb
commit
2d67c26794
@ -601,28 +601,44 @@ func DecryptBlocksRequestR(inputReader io.Reader, h http.Header, seqNumber uint3
|
|||||||
partEncRelOffset := int64(seqNumber) * (SSEDAREPackageBlockSize + SSEDAREPackageMetaSize)
|
partEncRelOffset := int64(seqNumber) * (SSEDAREPackageBlockSize + SSEDAREPackageMetaSize)
|
||||||
|
|
||||||
w := &DecryptBlocksReader{
|
w := &DecryptBlocksReader{
|
||||||
reader: inputReader,
|
reader: inputReader,
|
||||||
startSeqNum: seqNumber,
|
startSeqNum: seqNumber,
|
||||||
partDecRelOffset: partDecRelOffset,
|
partDecRelOffset: partDecRelOffset,
|
||||||
partEncRelOffset: partEncRelOffset,
|
partEncRelOffset: partEncRelOffset,
|
||||||
parts: oi.Parts,
|
parts: oi.Parts,
|
||||||
partIndex: partStart,
|
partIndex: partStart,
|
||||||
header: h,
|
|
||||||
bucket: bucket,
|
|
||||||
object: object,
|
|
||||||
customerKeyHeader: h.Get(xhttp.AmzServerSideEncryptionCustomerKey),
|
|
||||||
copySource: copySource,
|
|
||||||
metadata: cloneMSS(oi.UserDefined),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.copySource {
|
// In case of SSE-C, we have to decrypt the OEK using the client-provided key.
|
||||||
w.customerKeyHeader = h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKey)
|
// 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 {
|
if err := w.buildDecrypter(w.parts[w.partIndex].Number); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return w, nil
|
return w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,48 +654,17 @@ type DecryptBlocksReader struct {
|
|||||||
// Current part index
|
// Current part index
|
||||||
partIndex int
|
partIndex int
|
||||||
// Parts information
|
// Parts information
|
||||||
parts []ObjectPartInfo
|
parts []ObjectPartInfo
|
||||||
header http.Header
|
|
||||||
bucket, object string
|
|
||||||
metadata map[string]string
|
|
||||||
|
|
||||||
|
objectEncryptionKey []byte
|
||||||
partDecRelOffset, partEncRelOffset int64
|
partDecRelOffset, partEncRelOffset int64
|
||||||
|
|
||||||
copySource bool
|
|
||||||
// Customer Key
|
|
||||||
customerKeyHeader string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DecryptBlocksReader) buildDecrypter(partID int) error {
|
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
|
var partIDbin [4]byte
|
||||||
binary.LittleEndian.PutUint32(partIDbin[:], uint32(partID)) // marshal part ID
|
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[:])
|
mac.Write(partIDbin[:])
|
||||||
partEncryptionKey := mac.Sum(nil)
|
partEncryptionKey := mac.Sum(nil)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user