diff --git a/cmd/encryption-v1.go b/cmd/encryption-v1.go index f5708eb0b..8069da222 100644 --- a/cmd/encryption-v1.go +++ b/cmd/encryption-v1.go @@ -623,9 +623,9 @@ func DecryptBlocksRequest(client io.Writer, r *http.Request, startOffset, length var partStartOffset = startOffset // Skip parts until final offset maps to a particular part offset. for i, part := range objInfo.Parts { - decryptedSize, err := decryptedSize(part.Size) + decryptedSize, err := sio.DecryptedSize(uint64(part.Size)) if err != nil { - return nil, -1, -1, err + return nil, -1, -1, errObjectTampered } partStartIndex = i @@ -633,12 +633,12 @@ func DecryptBlocksRequest(client io.Writer, r *http.Request, startOffset, length // Offset is smaller than size we have reached the // proper part offset, break out we start from // this part index. - if partStartOffset < decryptedSize { + if partStartOffset < int64(decryptedSize) { break } // Continue to look for next part. - partStartOffset -= decryptedSize + partStartOffset -= int64(decryptedSize) } startSeqNum := partStartOffset / sseDAREPackageBlockSize @@ -734,20 +734,6 @@ func (li *ListPartsInfo) IsEncrypted() bool { return false } -func decryptedSize(encryptedSize int64) (int64, error) { - if encryptedSize == 0 { - return encryptedSize, nil - } - size := (encryptedSize / (sseDAREPackageBlockSize + sseDAREPackageMetaSize)) * sseDAREPackageBlockSize - if mod := encryptedSize % (sseDAREPackageBlockSize + sseDAREPackageMetaSize); mod > 0 { - if mod < sseDAREPackageMetaSize+1 { - return -1, errObjectTampered // object is not 0 size but smaller than the smallest valid encrypted object - } - size += mod - sseDAREPackageMetaSize - } - return size, nil -} - // DecryptedSize returns the size of the object after decryption in bytes. // It returns an error if the object is not encrypted or marked as encrypted // but has an invalid size. @@ -756,19 +742,22 @@ func (o *ObjectInfo) DecryptedSize() (int64, error) { if !o.IsEncrypted() { panic("cannot compute decrypted size of an object which is not encrypted") } - - return decryptedSize(o.Size) + size, err := sio.DecryptedSize(uint64(o.Size)) + if err != nil { + err = errObjectTampered // assign correct error type + } + return int64(size), err } // EncryptedSize returns the size of the object after encryption. // An encrypted object is always larger than a plain object // except for zero size objects. func (o *ObjectInfo) EncryptedSize() int64 { - size := (o.Size / sseDAREPackageBlockSize) * (sseDAREPackageBlockSize + sseDAREPackageMetaSize) - if mod := o.Size % (sseDAREPackageBlockSize); mod > 0 { - size += mod + sseDAREPackageMetaSize + size, err := sio.EncryptedSize(uint64(o.Size)) + if err != nil { + panic(err) // Since AWS S3 allows parts to be 5GB at most this cannot happen - sio max. size is 256 TB } - return size + return int64(size) } // DecryptCopyObjectInfo tries to decrypt the provided object if it is encrypted. diff --git a/cmd/encryption-v1_test.go b/cmd/encryption-v1_test.go index 0cc2f74c6..283d4a388 100644 --- a/cmd/encryption-v1_test.go +++ b/cmd/encryption-v1_test.go @@ -275,59 +275,6 @@ func TestParseSSECopyCustomerRequest(t *testing.T) { } } -var encryptedSizeTests = []struct { - size, encsize int64 -}{ - {size: 0, encsize: 0}, // 0 - {size: 1, encsize: 33}, // 1 - {size: 1024, encsize: 1024 + 32}, // 2 - {size: 2 * sseDAREPackageBlockSize, encsize: 2 * (sseDAREPackageBlockSize + 32)}, // 3 - {size: 100*sseDAREPackageBlockSize + 1, encsize: 100*(sseDAREPackageBlockSize+32) + 33}, // 4 - {size: sseDAREPackageBlockSize + 1, encsize: (sseDAREPackageBlockSize + 32) + 33}, // 5 - {size: 5 * 1024 * 1024 * 1024, encsize: 81920 * (sseDAREPackageBlockSize + 32)}, // 6 -} - -func TestEncryptedSize(t *testing.T) { - for i, test := range encryptedSizeTests { - objInfo := ObjectInfo{Size: test.size} - if size := objInfo.EncryptedSize(); test.encsize != size { - t.Errorf("Test %d: got encrypted size: #%d want: #%d", i, size, test.encsize) - } - } -} - -var decryptSSECustomerObjectInfoTests = []struct { - encsize, size int64 - err error -}{ - {encsize: 0, size: 0, err: nil}, // 0 - {encsize: 33, size: 1, err: nil}, // 1 - {encsize: 1024 + 32, size: 1024, err: nil}, // 2 - {encsize: 2 * (sseDAREPackageBlockSize + 32), size: 2 * sseDAREPackageBlockSize, err: nil}, // 3 - {encsize: 100*(sseDAREPackageBlockSize+32) + 33, size: 100*sseDAREPackageBlockSize + 1, err: nil}, // 4 - {encsize: (sseDAREPackageBlockSize + 32) + 33, size: sseDAREPackageBlockSize + 1, err: nil}, // 5 - {encsize: 81920 * (sseDAREPackageBlockSize + 32), size: 5 * 1024 * 1024 * 1024, err: nil}, // 6 - {encsize: 0, size: 0, err: nil}, // 7 - {encsize: sseDAREPackageBlockSize + 32 + 31, size: 0, err: errObjectTampered}, // 8 -} - -func TestDecryptedSize(t *testing.T) { - for i, test := range decryptSSECustomerObjectInfoTests { - objInfo := ObjectInfo{Size: test.encsize} - objInfo.UserDefined = map[string]string{ - ServerSideEncryptionSealAlgorithm: SSESealAlgorithmDareSha256, - } - - size, err := objInfo.DecryptedSize() - if err != test.err || (size != test.size && err == nil) { - t.Errorf("Test %d: decryption returned: %v want: %v", i, err, test.err) - } - if err == nil && size != test.size { - t.Errorf("Test %d: got decrypted size: #%d want: #%d", i, size, test.size) - } - } -} - var encryptRequestTests = []struct { header map[string]string metadata map[string]string diff --git a/vendor/github.com/minio/sio/sio.go b/vendor/github.com/minio/sio/sio.go index 5e043a4a3..1d60803f5 100644 --- a/vendor/github.com/minio/sio/sio.go +++ b/vendor/github.com/minio/sio/sio.go @@ -48,6 +48,9 @@ const ( maxPayloadSize = 1 << 16 tagSize = 16 maxPackageSize = headerSize + maxPayloadSize + tagSize + + maxDecryptedSize = 1 << 48 + maxEncryptedSize = maxDecryptedSize + ((headerSize + tagSize) * 1 << 32) ) var newAesGcm = func(key []byte) (cipher.AEAD, error) { @@ -68,6 +71,7 @@ var ( errUnsupportedCipher = errors.New("sio: unsupported cipher suite") errInvalidPayloadSize = errors.New("sio: invalid payload size") errTagMismatch = errors.New("sio: authentication failed") + errUnexpectedSize = errors.New("sio: size is too large for DARE") // Version 1.0 specific errPackageOutOfOrder = errors.New("sio: sequence number mismatch") @@ -115,6 +119,41 @@ type Config struct { PayloadSize int } +// EncryptedSize computes the size of an encrypted data stream +// from the plaintext size. It is the inverse of DecryptedSize(). +// +// EncryptedSize returns an error if the provided size is to large. +func EncryptedSize(size uint64) (uint64, error) { + if size > maxDecryptedSize { + return 0, errUnexpectedSize + } + + encSize := (size / maxPayloadSize) * maxPackageSize + if mod := size % maxPayloadSize; mod > 0 { + encSize += mod + (headerSize + tagSize) + } + return encSize, nil +} + +// DecryptedSize computes the size of a decrypted data stream +// from the encrypted stream size. It is the inverse of EncryptedSize(). +// +// DecryptedSize returns an error if the provided size is to large +// or if the provided size is an invalid encrypted stream size. +func DecryptedSize(size uint64) (uint64, error) { + if size > maxEncryptedSize { + return 0, errUnexpectedSize + } + decSize := (size / maxPackageSize) * maxPayloadSize + if mod := size % maxPackageSize; mod > 0 { + if mod <= headerSize+tagSize { + return 0, errors.New("sio: size is not valid") // last package is not valid + } + decSize += mod - (headerSize + tagSize) + } + return decSize, nil +} + // Encrypt reads from src until it encounters an io.EOF and encrypts all received // data. The encrypted data is written to dst. It returns the number of bytes // encrypted and the first error encountered while encrypting, if any. diff --git a/vendor/github.com/minio/sio/writer-v1.go b/vendor/github.com/minio/sio/writer-v1.go index 590eea3d7..64eb811b2 100644 --- a/vendor/github.com/minio/sio/writer-v1.go +++ b/vendor/github.com/minio/sio/writer-v1.go @@ -192,7 +192,7 @@ func flush(w io.Writer, p []byte) error { if err != nil { return err } - if n != len(p) { // not neccasary if the w follows the io.Writer doc *precisly* + if n != len(p) { // not neccasary if the w follows the io.Writer doc *precisely* return io.ErrShortWrite } return nil diff --git a/vendor/github.com/minio/sio/writer-v2.go b/vendor/github.com/minio/sio/writer-v2.go index 4c349cd8e..e8b3dbdda 100644 --- a/vendor/github.com/minio/sio/writer-v2.go +++ b/vendor/github.com/minio/sio/writer-v2.go @@ -80,7 +80,7 @@ func (w *encWriterV20) Write(p []byte) (n int, err error) { } func (w *encWriterV20) Close() error { - if w.offset > 0 { // true if at least one Write call happend + if w.offset > 0 { // true if at least one Write call happened w.SealFinal(w.buffer, w.buffer[headerSize:headerSize+w.offset]) if err := flush(w.dst, w.buffer[:headerSize+w.offset+tagSize]); err != nil { // write to underlying io.Writer return err diff --git a/vendor/vendor.json b/vendor/vendor.json index 367d40e00..8415fdf06 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -446,10 +446,10 @@ "revisionTime": "2017-08-28T17:39:33Z" }, { - "checksumSHA1": "pvBHvHfubwxgvXNz35PDMbBWY9s=", + "checksumSHA1": "LLNO4saSnirHPhxJQ6nk2E0VMKs=", "path": "github.com/minio/sio", - "revision": "83dd737d26dbcf4847f48ecec77c2c13f739eb25", - "revisionTime": "2018-03-01T21:58:29Z" + "revision": "b421622190be1ffb4569c7303ed1b7bef201a7c3", + "revisionTime": "2018-03-15T10:40:56Z" }, { "checksumSHA1": "V/quM7+em2ByJbWBLOsEwnY3j/Q=",