@ -312,6 +312,155 @@ func newDecryptWriterWithObjectKey(client io.Writer, objectEncryptionKey []byte,
return writer , nil
}
// Adding support for reader based interface
// DecryptRequestWithSequenceNumberR - same as
// DecryptRequestWithSequenceNumber but with a reader
func DecryptRequestWithSequenceNumberR ( client io . Reader , r * http . Request , bucket , object string , seqNumber uint32 , metadata map [ string ] string ) ( io . Reader , error ) {
if crypto . S3 . IsEncrypted ( metadata ) {
return newDecryptReader ( client , nil , bucket , object , seqNumber , metadata )
}
key , err := ParseSSECustomerRequest ( r )
if err != nil {
return nil , err
}
delete ( metadata , crypto . SSECKey ) // make sure we do not save the key by accident
return newDecryptReader ( client , key , bucket , object , seqNumber , metadata )
}
// DecryptCopyRequestR - same as DecryptCopyRequest, but with a
// Reader
func DecryptCopyRequestR ( client io . Reader , r * http . Request , bucket , object string , metadata map [ string ] string ) ( io . Reader , error ) {
var (
key [ ] byte
err error
)
if crypto . SSECopy . IsRequested ( r . Header ) {
key , err = ParseSSECopyCustomerRequest ( r , metadata )
if err != nil {
return nil , err
}
}
delete ( metadata , crypto . SSECopyKey ) // make sure we do not save the key by accident
return newDecryptReader ( client , key , bucket , object , 0 , metadata )
}
func newDecryptReader ( client io . Reader , key [ ] byte , bucket , object string , seqNumber uint32 , metadata map [ string ] string ) ( io . Reader , error ) {
objectEncryptionKey , err := decryptObjectInfo ( key , bucket , object , metadata )
if err != nil {
return nil , err
}
return newDecryptReaderWithObjectKey ( client , objectEncryptionKey , seqNumber , metadata )
}
func newDecryptReaderWithObjectKey ( client io . Reader , objectEncryptionKey [ ] byte , seqNumber uint32 , metadata map [ string ] string ) ( io . Reader , error ) {
reader , err := sio . DecryptReader ( client , sio . Config {
Key : objectEncryptionKey ,
SequenceNumber : seqNumber ,
} )
if err != nil {
return nil , crypto . ErrInvalidCustomerKey
}
delete ( metadata , crypto . SSEIV )
delete ( metadata , crypto . SSESealAlgorithm )
delete ( metadata , crypto . SSECSealedKey )
delete ( metadata , crypto . SSEMultipart )
delete ( metadata , crypto . S3SealedKey )
delete ( metadata , crypto . S3KMSSealedKey )
delete ( metadata , crypto . S3KMSKeyID )
return reader , nil
}
// DecryptBlocksRequestR - same as DecryptBlocksRequest but with a
// reader
func DecryptBlocksRequestR ( client io . Reader , r * http . Request , bucket , object string , startOffset , length int64 , objInfo ObjectInfo , copySource bool ) ( io . Reader , int64 , int64 , error ) {
var seqNumber uint32
var encStartOffset , encLength int64
if len ( objInfo . Parts ) == 0 || ! crypto . IsMultiPart ( objInfo . UserDefined ) {
seqNumber , encStartOffset , encLength = getEncryptedSinglePartOffsetLength ( startOffset , length , objInfo )
var reader io . Reader
var err error
if copySource {
reader , err = DecryptCopyRequestR ( client , r , bucket , object , objInfo . UserDefined )
} else {
reader , err = DecryptRequestWithSequenceNumberR ( client , r , bucket , object , seqNumber , objInfo . UserDefined )
}
if err != nil {
return nil , 0 , 0 , err
}
return reader , encStartOffset , encLength , nil
}
seqNumber , 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 := & DecryptBlocksReader {
reader : 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
}
// 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.
func DecryptRequestWithSequenceNumber ( client io . Writer , r * http . Request , bucket , object string , seqNumber uint32 , metadata map [ string ] string ) ( io . WriteCloser , error ) {
@ -333,6 +482,123 @@ func DecryptRequest(client io.Writer, r *http.Request, bucket, object string, me
return DecryptRequestWithSequenceNumber ( client , r , bucket , object , 0 , metadata )
}
// DecryptBlocksReader - decrypts multipart parts, while implementing
// a io.Reader compatible interface.
type DecryptBlocksReader struct {
// Source of the encrypted content that will be decrypted
reader io . Reader
// Current decrypter for the current encrypted data block
decrypter io . Reader
// 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 ( d * DecryptBlocksReader ) buildDecrypter ( partID int ) error {
m := make ( map [ string ] string )
for k , v := range d . metadata {
m [ k ] = v
}
// 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 . req . Header . Set ( crypto . SSECopyKey , d . customerKeyHeader )
key , err = ParseSSECopyCustomerRequest ( d . req , d . metadata )
}
} else {
if crypto . SSEC . IsEncrypted ( d . metadata ) {
d . req . Header . Set ( crypto . SSECKey , d . customerKeyHeader )
key , err = ParseSSECustomerRequest ( d . req )
}
}
if err != nil {
return err
}
objectEncryptionKey , err := decryptObjectInfo ( 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 . Write ( partIDbin [ : ] )
partEncryptionKey := mac . Sum ( nil )
// make sure we do not save the key by accident
if d . copySource {
delete ( m , crypto . SSECopyKey )
} else {
delete ( m , crypto . SSECKey )
}
// 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 := newDecryptReaderWithObjectKey ( d . reader , partEncryptionKey , d . startSeqNum , m )
if err != nil {
return err
}
d . decrypter = decrypter
return nil
}
func ( d * DecryptBlocksReader ) Read ( p [ ] byte ) ( int , error ) {
var err error
var n1 int
if int64 ( len ( p ) ) < d . parts [ d . partIndex ] . Size - d . partEncRelOffset {
n1 , err = d . decrypter . Read ( p )
if err != nil {
return 0 , err
}
d . partEncRelOffset += int64 ( n1 )
} else {
n1 , err = d . decrypter . Read ( p [ : d . parts [ d . partIndex ] . Size - d . partEncRelOffset ] )
if err != nil {
return 0 , err
}
// We should now proceed to next part, reset all
// values appropriately.
d . partEncRelOffset = 0
d . startSeqNum = 0
d . partIndex ++
err = d . buildDecrypter ( d . partIndex + 1 )
if err != nil {
return 0 , err
}
n1 , err = d . decrypter . Read ( p [ n1 : ] )
if err != nil {
return 0 , err
}
d . partEncRelOffset += int64 ( n1 )
}
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