|
|
@ -32,7 +32,6 @@ import ( |
|
|
|
"github.com/minio/minio-go/v6/pkg/encrypt" |
|
|
|
"github.com/minio/minio-go/v6/pkg/encrypt" |
|
|
|
"github.com/minio/minio/cmd/crypto" |
|
|
|
"github.com/minio/minio/cmd/crypto" |
|
|
|
"github.com/minio/minio/cmd/logger" |
|
|
|
"github.com/minio/minio/cmd/logger" |
|
|
|
"github.com/minio/minio/pkg/ioutil" |
|
|
|
|
|
|
|
sha256 "github.com/minio/sha256-simd" |
|
|
|
sha256 "github.com/minio/sha256-simd" |
|
|
|
"github.com/minio/sio" |
|
|
|
"github.com/minio/sio" |
|
|
|
) |
|
|
|
) |
|
|
@ -64,17 +63,6 @@ const ( |
|
|
|
|
|
|
|
|
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
|
|
|
// SSESealAlgorithmDareSha256 specifies DARE as authenticated en/decryption scheme and SHA256 as cryptographic
|
|
|
|
|
|
|
|
// 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" |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// hasServerSideEncryptionHeader returns true if the given HTTP header
|
|
|
|
// hasServerSideEncryptionHeader returns true if the given HTTP header
|
|
|
|
// contains server-side-encryption.
|
|
|
|
// contains server-side-encryption.
|
|
|
|
func hasServerSideEncryptionHeader(header http.Header) bool { |
|
|
|
func hasServerSideEncryptionHeader(header http.Header) bool { |
|
|
@ -597,236 +585,6 @@ func (d *DecryptBlocksReader) Read(p []byte) (int, error) { |
|
|
|
return len(p), nil |
|
|
|
return len(p), nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// DecryptBlocksWriter - decrypts multipart parts, while implementing
|
|
|
|
|
|
|
|
// a io.Writer compatible interface.
|
|
|
|
|
|
|
|
type DecryptBlocksWriter struct { |
|
|
|
|
|
|
|
// Original writer where the plain data will be written
|
|
|
|
|
|
|
|
writer io.Writer |
|
|
|
|
|
|
|
// Current decrypter for the current encrypted data block
|
|
|
|
|
|
|
|
decrypter io.WriteCloser |
|
|
|
|
|
|
|
// Start sequence number
|
|
|
|
|
|
|
|
startSeqNum uint32 |
|
|
|
|
|
|
|
// Current part index
|
|
|
|
|
|
|
|
partIndex int |
|
|
|
|
|
|
|
// Parts information
|
|
|
|
|
|
|
|
parts []ObjectPartInfo |
|
|
|
|
|
|
|
req *http.Request |
|
|
|
|
|
|
|
bucket, object string |
|
|
|
|
|
|
|
metadata map[string]string |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
partEncRelOffset int64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
copySource bool |
|
|
|
|
|
|
|
// Customer Key
|
|
|
|
|
|
|
|
customerKeyHeader string |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (w *DecryptBlocksWriter) buildDecrypter(partID int) error { |
|
|
|
|
|
|
|
m := make(map[string]string) |
|
|
|
|
|
|
|
for k, v := range w.metadata { |
|
|
|
|
|
|
|
m[k] = v |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Initialize the first decrypter, new decrypters will be initialized in Write() operation as needed.
|
|
|
|
|
|
|
|
var key []byte |
|
|
|
|
|
|
|
var err error |
|
|
|
|
|
|
|
if w.copySource { |
|
|
|
|
|
|
|
if crypto.SSEC.IsEncrypted(w.metadata) { |
|
|
|
|
|
|
|
w.req.Header.Set(crypto.SSECopyKey, w.customerKeyHeader) |
|
|
|
|
|
|
|
key, err = ParseSSECopyCustomerRequest(w.req.Header, w.metadata) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if crypto.SSEC.IsEncrypted(w.metadata) { |
|
|
|
|
|
|
|
w.req.Header.Set(crypto.SSECKey, w.customerKeyHeader) |
|
|
|
|
|
|
|
key, err = ParseSSECustomerRequest(w.req) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
objectEncryptionKey, err := decryptObjectInfo(key, w.bucket, w.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.Write(partIDbin[:]) |
|
|
|
|
|
|
|
partEncryptionKey := mac.Sum(nil) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// make sure to provide a NopCloser such that a Close
|
|
|
|
|
|
|
|
// on sio.decryptWriter doesn't close the underlying writer's
|
|
|
|
|
|
|
|
// close which perhaps can close the stream prematurely.
|
|
|
|
|
|
|
|
decrypter, err := newDecryptWriterWithObjectKey(ioutil.NopCloser(w.writer), partEncryptionKey, w.startSeqNum, m) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if w.decrypter != nil { |
|
|
|
|
|
|
|
// Pro-actively close the writer such that any pending buffers
|
|
|
|
|
|
|
|
// are flushed already before we allocate a new decrypter.
|
|
|
|
|
|
|
|
err = w.decrypter.Close() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w.decrypter = decrypter |
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (w *DecryptBlocksWriter) Write(p []byte) (int, error) { |
|
|
|
|
|
|
|
var err error |
|
|
|
|
|
|
|
var n1 int |
|
|
|
|
|
|
|
if int64(len(p)) < w.parts[w.partIndex].Size-w.partEncRelOffset { |
|
|
|
|
|
|
|
n1, err = w.decrypter.Write(p) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return 0, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
w.partEncRelOffset += int64(n1) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
n1, err = w.decrypter.Write(p[:w.parts[w.partIndex].Size-w.partEncRelOffset]) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return 0, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// We should now proceed to next part, reset all values appropriately.
|
|
|
|
|
|
|
|
w.partEncRelOffset = 0 |
|
|
|
|
|
|
|
w.startSeqNum = 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w.partIndex++ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = w.buildDecrypter(w.partIndex + 1) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return 0, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
n1, err = w.decrypter.Write(p[n1:]) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return 0, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w.partEncRelOffset += int64(n1) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return len(p), nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Close closes the LimitWriter. It behaves like io.Closer.
|
|
|
|
|
|
|
|
func (w *DecryptBlocksWriter) Close() error { |
|
|
|
|
|
|
|
if w.decrypter != nil { |
|
|
|
|
|
|
|
err := w.decrypter.Close() |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if closer, ok := w.writer.(io.Closer); ok { |
|
|
|
|
|
|
|
return closer.Close() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
// all parts starting from part-1.
|
|
|
|
|
|
|
|
func DecryptAllBlocksCopyRequest(client io.Writer, r *http.Request, bucket, object string, objInfo ObjectInfo) (io.WriteCloser, int64, error) { |
|
|
|
|
|
|
|
w, _, size, err := DecryptBlocksRequest(client, r, bucket, object, 0, objInfo.Size, objInfo, true) |
|
|
|
|
|
|
|
return w, size, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DecryptBlocksRequest - setup a struct which can decrypt many concatenated encrypted data
|
|
|
|
|
|
|
|
// parts information helps to know the boundaries of each encrypted data block.
|
|
|
|
|
|
|
|
func DecryptBlocksRequest(client io.Writer, r *http.Request, bucket, object string, startOffset, length int64, objInfo ObjectInfo, copySource bool) (io.WriteCloser, int64, int64, error) { |
|
|
|
|
|
|
|
var seqNumber uint32 |
|
|
|
|
|
|
|
var encStartOffset, encLength int64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if !isEncryptedMultipart(objInfo) { |
|
|
|
|
|
|
|
seqNumber, encStartOffset, encLength = getEncryptedSinglePartOffsetLength(startOffset, length, objInfo) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var writer io.WriteCloser |
|
|
|
|
|
|
|
var err error |
|
|
|
|
|
|
|
if copySource { |
|
|
|
|
|
|
|
writer, err = DecryptCopyRequest(client, r, bucket, object, objInfo.UserDefined) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
writer, err = DecryptRequestWithSequenceNumber(client, r, bucket, object, seqNumber, objInfo.UserDefined) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, 0, 0, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return writer, encStartOffset, encLength, nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_, encStartOffset, encLength = getEncryptedMultipartsOffsetLength(startOffset, length, objInfo) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var partStartIndex int |
|
|
|
|
|
|
|
var partStartOffset = startOffset |
|
|
|
|
|
|
|
// Skip parts until final offset maps to a particular part offset.
|
|
|
|
|
|
|
|
for i, part := range objInfo.Parts { |
|
|
|
|
|
|
|
decryptedSize, err := sio.DecryptedSize(uint64(part.Size)) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, -1, -1, errObjectTampered |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
partStartIndex = i |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Offset is smaller than size we have reached the
|
|
|
|
|
|
|
|
// proper part offset, break out we start from
|
|
|
|
|
|
|
|
// this part index.
|
|
|
|
|
|
|
|
if partStartOffset < int64(decryptedSize) { |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Continue to look for next part.
|
|
|
|
|
|
|
|
partStartOffset -= int64(decryptedSize) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
startSeqNum := partStartOffset / SSEDAREPackageBlockSize |
|
|
|
|
|
|
|
partEncRelOffset := int64(startSeqNum) * (SSEDAREPackageBlockSize + SSEDAREPackageMetaSize) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w := &DecryptBlocksWriter{ |
|
|
|
|
|
|
|
writer: client, |
|
|
|
|
|
|
|
startSeqNum: uint32(startSeqNum), |
|
|
|
|
|
|
|
partEncRelOffset: partEncRelOffset, |
|
|
|
|
|
|
|
parts: objInfo.Parts, |
|
|
|
|
|
|
|
partIndex: partStartIndex, |
|
|
|
|
|
|
|
req: r, |
|
|
|
|
|
|
|
bucket: bucket, |
|
|
|
|
|
|
|
object: object, |
|
|
|
|
|
|
|
customerKeyHeader: r.Header.Get(crypto.SSECKey), |
|
|
|
|
|
|
|
copySource: copySource, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w.metadata = map[string]string{} |
|
|
|
|
|
|
|
// Copy encryption metadata for internal use.
|
|
|
|
|
|
|
|
for k, v := range objInfo.UserDefined { |
|
|
|
|
|
|
|
w.metadata[k] = v |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Purge all the encryption headers.
|
|
|
|
|
|
|
|
delete(objInfo.UserDefined, crypto.SSEIV) |
|
|
|
|
|
|
|
delete(objInfo.UserDefined, crypto.SSESealAlgorithm) |
|
|
|
|
|
|
|
delete(objInfo.UserDefined, crypto.SSECSealedKey) |
|
|
|
|
|
|
|
delete(objInfo.UserDefined, crypto.SSEMultipart) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if crypto.S3.IsEncrypted(objInfo.UserDefined) { |
|
|
|
|
|
|
|
delete(objInfo.UserDefined, crypto.S3SealedKey) |
|
|
|
|
|
|
|
delete(objInfo.UserDefined, crypto.S3KMSKeyID) |
|
|
|
|
|
|
|
delete(objInfo.UserDefined, crypto.S3KMSSealedKey) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if w.copySource { |
|
|
|
|
|
|
|
w.customerKeyHeader = r.Header.Get(crypto.SSECopyKey) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err := w.buildDecrypter(w.parts[w.partIndex].Number); err != nil { |
|
|
|
|
|
|
|
return nil, 0, 0, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return w, encStartOffset, encLength, nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// getEncryptedMultipartsOffsetLength - fetch sequence number, encrypted start offset and encrypted length.
|
|
|
|
// getEncryptedMultipartsOffsetLength - fetch sequence number, encrypted start offset and encrypted length.
|
|
|
|
func getEncryptedMultipartsOffsetLength(offset, length int64, obj ObjectInfo) (uint32, int64, int64) { |
|
|
|
func getEncryptedMultipartsOffsetLength(offset, length int64, obj ObjectInfo) (uint32, int64, int64) { |
|
|
|
// Calculate encrypted offset of a multipart object
|
|
|
|
// Calculate encrypted offset of a multipart object
|
|
|
|