diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 2a0f7a397a0..192867c3aae 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,5 +1,6 @@ Breaking Change --- +* `service/s3/s3crypto`: Package and associated encryption/decryption clients have been removed from the SDK ([#511](https://github.com/aws/aws-sdk-go-v2/pull/511)) * `aws/endpoints`: Removes `DecodeModel` and `DecodeModelOptions` from the package ([#509](https://github.com/aws/aws-sdk-go-v2/pull/509)) * `aws/external`: Removes several export constants and types ([#508](https://github.com/aws/aws-sdk-go-v2/pull/508)) * No longer exports AWS environment constants used by the external environment configuration loader diff --git a/service/s3/s3crypto/aes_cbc.go b/service/s3/s3crypto/aes_cbc.go deleted file mode 100644 index 866ceb91a3c..00000000000 --- a/service/s3/s3crypto/aes_cbc.go +++ /dev/null @@ -1,190 +0,0 @@ -package s3crypto - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "io" -) - -// AESCBC is a symmetric crypto algorithm. This algorithm -// requires a padder due to CBC needing to be of the same block -// size. AES CBC is vulnerable to Padding Oracle attacks and -// so should be avoided when possible. -type aesCBC struct { - encrypter cipher.BlockMode - decrypter cipher.BlockMode - padder Padder -} - -// newAESCBC creates a new AES CBC cipher. Expects keys to be of -// the correct size. -func newAESCBC(cd CipherData, padder Padder) (Cipher, error) { - block, err := aes.NewCipher(cd.Key) - if err != nil { - return nil, err - } - - encrypter := cipher.NewCBCEncrypter(block, cd.IV) - decrypter := cipher.NewCBCDecrypter(block, cd.IV) - - return &aesCBC{encrypter, decrypter, padder}, nil -} - -// Encrypt will encrypt the data using AES CBC by returning -// an io.Reader. The io.Reader will encrypt the data as Read -// is called. -func (c *aesCBC) Encrypt(src io.Reader) io.Reader { - reader := &cbcEncryptReader{ - encrypter: c.encrypter, - src: src, - padder: c.padder, - } - return reader -} - -type cbcEncryptReader struct { - encrypter cipher.BlockMode - src io.Reader - padder Padder - size int - buf bytes.Buffer -} - -// Read will read from our io.Reader and encrypt the data as necessary. -// Due to padding, we have to do some logic that when we encounter an -// end of file to pad properly. -func (reader *cbcEncryptReader) Read(data []byte) (int, error) { - n, err := reader.src.Read(data) - reader.size += n - blockSize := reader.encrypter.BlockSize() - reader.buf.Write(data[:n]) - - if err == io.EOF { - b := make([]byte, getSliceSize(blockSize, reader.buf.Len(), len(data))) - n, err = reader.buf.Read(b) - if err != nil && err != io.EOF { - return n, err - } - // The buffer is now empty, we can now pad the data - if reader.buf.Len() == 0 { - b, err = reader.padder.Pad(b[:n], reader.size) - if err != nil { - return n, err - } - n = len(b) - err = io.EOF - } - // We only want to encrypt if we have read anything - if n > 0 { - reader.encrypter.CryptBlocks(data, b) - } - return n, err - } - - if err != nil { - return n, err - } - - if size := reader.buf.Len(); size >= blockSize { - nBlocks := size / blockSize - if size > len(data) { - nBlocks = len(data) / blockSize - } - - if nBlocks > 0 { - b := make([]byte, nBlocks*blockSize) - n, _ = reader.buf.Read(b) - reader.encrypter.CryptBlocks(data, b[:n]) - } - } else { - n = 0 - } - return n, nil -} - -// Decrypt will decrypt the data using AES CBC -func (c *aesCBC) Decrypt(src io.Reader) io.Reader { - return &cbcDecryptReader{ - decrypter: c.decrypter, - src: src, - padder: c.padder, - } -} - -type cbcDecryptReader struct { - decrypter cipher.BlockMode - src io.Reader - padder Padder - buf bytes.Buffer -} - -// Read will read from our io.Reader and decrypt the data as necessary. -// Due to padding, we have to do some logic that when we encounter an -// end of file to pad properly. -func (reader *cbcDecryptReader) Read(data []byte) (int, error) { - n, err := reader.src.Read(data) - blockSize := reader.decrypter.BlockSize() - reader.buf.Write(data[:n]) - - if err == io.EOF { - b := make([]byte, getSliceSize(blockSize, reader.buf.Len(), len(data))) - n, err = reader.buf.Read(b) - if err != nil && err != io.EOF { - return n, err - } - // We only want to decrypt if we have read anything - if n > 0 { - reader.decrypter.CryptBlocks(data, b) - } - - if reader.buf.Len() == 0 { - b, err = reader.padder.Unpad(data[:n]) - n = len(b) - if err != nil { - return n, err - } - err = io.EOF - } - return n, err - } - - if err != nil { - return n, err - } - - if size := reader.buf.Len(); size >= blockSize { - nBlocks := size / blockSize - if size > len(data) { - nBlocks = len(data) / blockSize - } - // The last block is always padded. This will allow us to unpad - // when we receive an io.EOF error - nBlocks -= blockSize - - if nBlocks > 0 { - b := make([]byte, nBlocks*blockSize) - n, _ = reader.buf.Read(b) - reader.decrypter.CryptBlocks(data, b[:n]) - } else { - n = 0 - } - } - - return n, nil -} - -// getSliceSize will return the correct amount of bytes we need to -// read with regards to padding. -func getSliceSize(blockSize, bufSize, dataSize int) int { - size := bufSize - if bufSize > dataSize { - size = dataSize - } - size = size - (size % blockSize) - blockSize - if size <= 0 { - size = blockSize - } - - return size -} diff --git a/service/s3/s3crypto/aes_cbc_content_cipher.go b/service/s3/s3crypto/aes_cbc_content_cipher.go deleted file mode 100644 index 7346e383cd3..00000000000 --- a/service/s3/s3crypto/aes_cbc_content_cipher.go +++ /dev/null @@ -1,73 +0,0 @@ -package s3crypto - -import ( - "io" - "strings" -) - -const ( - cbcKeySize = 32 - cbcNonceSize = 16 -) - -type cbcContentCipherBuilder struct { - generator CipherDataGenerator - padder Padder -} - -// AESCBCContentCipherBuilder returns a new encryption only mode structure with a specific cipher -// for the master key -func AESCBCContentCipherBuilder(generator CipherDataGenerator, padder Padder) ContentCipherBuilder { - return cbcContentCipherBuilder{generator: generator, padder: padder} -} - -func (builder cbcContentCipherBuilder) ContentCipher() (ContentCipher, error) { - cd, err := builder.generator.GenerateCipherData(cbcKeySize, cbcNonceSize) - if err != nil { - return nil, err - } - - cd.Padder = builder.padder - return newAESCBCContentCipher(cd) -} - -// newAESCBCContentCipher will create a new aes cbc content cipher. If the cipher data's -// will set the CEK algorithm if it hasn't been set. -func newAESCBCContentCipher(cd CipherData) (ContentCipher, error) { - if len(cd.CEKAlgorithm) == 0 { - cd.CEKAlgorithm = strings.Join([]string{AESCBC, cd.Padder.Name()}, "/") - } - cipher, err := newAESCBC(cd, cd.Padder) - if err != nil { - return nil, err - } - - return &aesCBCContentCipher{ - CipherData: cd, - Cipher: cipher, - }, nil -} - -// aesCBCContentCipher will use AES CBC for the main cipher. -type aesCBCContentCipher struct { - CipherData CipherData - Cipher Cipher -} - -// EncryptContents will generate a random key and iv and encrypt the data using cbc -func (cc *aesCBCContentCipher) EncryptContents(src io.Reader) (io.Reader, error) { - return cc.Cipher.Encrypt(src), nil -} - -// DecryptContents will use the symmetric key provider to instantiate a new CBC cipher. -// We grab a decrypt reader from CBC and wrap it in a CryptoReadCloser. The only error -// expected here is when the key or iv is of invalid length. -func (cc *aesCBCContentCipher) DecryptContents(src io.ReadCloser) (io.ReadCloser, error) { - reader := cc.Cipher.Decrypt(src) - return &CryptoReadCloser{Body: src, Decrypter: reader}, nil -} - -// GetCipherData returns cipher data -func (cc aesCBCContentCipher) GetCipherData() CipherData { - return cc.CipherData -} diff --git a/service/s3/s3crypto/aes_cbc_content_cipher_test.go b/service/s3/s3crypto/aes_cbc_content_cipher_test.go deleted file mode 100644 index fe401b273dd..00000000000 --- a/service/s3/s3crypto/aes_cbc_content_cipher_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package s3crypto_test - -import ( - "testing" - - "github.com/aws/aws-sdk-go-v2/service/s3/s3crypto" -) - -func TestAESCBCBuilder(t *testing.T) { - generator := mockGenerator{} - builder := s3crypto.AESCBCContentCipherBuilder(generator, s3crypto.NoPadder) - if builder == nil { - t.Fatal(builder) - } - - _, err := builder.ContentCipher() - if err != nil { - t.Fatal(err) - } -} diff --git a/service/s3/s3crypto/aes_cbc_padder.go b/service/s3/s3crypto/aes_cbc_padder.go deleted file mode 100644 index 95a11e81844..00000000000 --- a/service/s3/s3crypto/aes_cbc_padder.go +++ /dev/null @@ -1,29 +0,0 @@ -package s3crypto - -const ( - pkcs5BlockSize = 16 -) - -var aescbcPadding = aescbcPadder{pkcs7Padder{16}} - -// AESCBCPadder is used to pad AES encrypted and decrypted data. -// Altough it uses the pkcs5Padder, it isn't following the RFC -// for PKCS5. The only reason why it is called pkcs5Padder is -// due to the Name returning PKCS5Padding. -var AESCBCPadder = Padder(aescbcPadding) - -type aescbcPadder struct { - padder pkcs7Padder -} - -func (padder aescbcPadder) Pad(b []byte, n int) ([]byte, error) { - return padder.padder.Pad(b, n) -} - -func (padder aescbcPadder) Unpad(b []byte) ([]byte, error) { - return padder.padder.Unpad(b) -} - -func (padder aescbcPadder) Name() string { - return "PKCS5Padding" -} diff --git a/service/s3/s3crypto/aes_cbc_padder_test.go b/service/s3/s3crypto/aes_cbc_padder_test.go deleted file mode 100644 index 2b1fe7d50d2..00000000000 --- a/service/s3/s3crypto/aes_cbc_padder_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package s3crypto - -import ( - "bytes" - "fmt" - "testing" -) - -func TestAESCBCPadding(t *testing.T) { - for i := 0; i < 16; i++ { - input := make([]byte, i) - expected := append(input, bytes.Repeat([]byte{byte(16 - i)}, 16-i)...) - b, err := AESCBCPadder.Pad(input, len(input)) - if err != nil { - t.Fatal("Expected error to be nil but received " + err.Error()) - } - if len(b) != len(expected) { - t.Fatal(fmt.Sprintf("Case %d: data is not of the same length", i)) - } - if bytes.Compare(b, expected) != 0 { - t.Fatal(fmt.Sprintf("Expected %v but got %v", expected, b)) - } - } -} - -func TestAESCBCUnpadding(t *testing.T) { - for i := 0; i < 16; i++ { - expected := make([]byte, i) - input := append(expected, bytes.Repeat([]byte{byte(16 - i)}, 16-i)...) - b, err := AESCBCPadder.Unpad(input) - if err != nil { - t.Fatal("Error received, was expecting nil: " + err.Error()) - } - if len(b) != len(expected) { - t.Fatal(fmt.Sprintf("Case %d: data is not of the same length", i)) - } - if bytes.Compare(b, expected) != 0 { - t.Fatal(fmt.Sprintf("Expected %v but got %v", expected, b)) - } - } -} diff --git a/service/s3/s3crypto/aes_cbc_test.go b/service/s3/s3crypto/aes_cbc_test.go deleted file mode 100644 index a160d052bbe..00000000000 --- a/service/s3/s3crypto/aes_cbc_test.go +++ /dev/null @@ -1,501 +0,0 @@ -package s3crypto - -import ( - "bytes" - "encoding/hex" - "fmt" - "io" - "testing" -) - -func TestAESCBCEncryptDecrypt(t *testing.T) { - var testCases = []struct { - key string - iv string - plaintext string - ciphertext string - decodeHex bool - padder Padder - }{ - // Test vectors from RFC 3602: https://tools.ietf.org/html/rfc3602 - { - "06a9214036b8a15b512e03d534120006", - "3dafba429d9eb430b422da802c9fac41", - "Single block msg", - "e353779c1079aeb82708942dbe77181a", - false, - NoPadder, - }, - { - "c286696d887c9aa0611bbb3e2025a45a", - "562e17996d093d28ddb3ba695a2e6f58", - "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", - "d296cd94c2cccf8a3a863028b5e1dc0a7586602d253cfff91b8266bea6d61ab1", - true, - NoPadder, - }, - { - "6c3ea0477630ce21a2ce334aa746c2cd", - "c782dc4c098c66cbd9cd27d825682c81", - "This is a 48-byte message (exactly 3 AES blocks)", - "d0a02b3836451753d493665d33f0e8862dea54cdb293abc7506939276772f8d5021c19216bad525c8579695d83ba2684", - false, - NoPadder, - }, - { - "56e47a38c5598974bc46903dba290349", - "8ce82eefbea0da3c44699ed7db51b7d9", - "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf", - "c30e32ffedc0774e6aff6af0869f71aa0f3af07a9a31a9c684db207eb0ef8e4e35907aa632c3ffdf868bb7b29d3d46ad83ce9f9a102ee99d49a53e87f4c3da55", - true, - NoPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "", - "B012949BA07D1A6DCE9DEE67274D41AB", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41", - "8A11ABA68A566132FFE04DB336621D41", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141", - "97D0896E41DFDB5CEA4A9EB70A938CFD", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141", - "8464EAD45FA2D8790E8741E32C28083F", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141", - "1E656D6E2745BA9F154FAF136B2BC73D", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141", - "0B6031C4B230DAC6BD6D3F195645B287", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141", - "5D09FEB6462BB489489A7E18FD341D9D", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141", - "85745E398F2FD1050C2CE8F8614DA369", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141", - "7BE52933970BA7B0FC6FB3FC37648205", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141", - "ED3A1E134EF36CCFE60C8123B4272F89", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141", - "C3B7C9E177E1052FC736F65FC1E74209", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141", - "C3A8B53F7F57F0B9D346FA99810A3C28", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141", - "D16B1ECE5BF00AF919E139E99775FF06", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141414141", - "B258F4DF57FFCA1EFCF8D76140F05139", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141414141", - "3CD2282DE24A2CF9E23326CC3DC9077A", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141414141", - "3010232E7C752A3B4C9EE428B4C4FE88", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217A22BC4E6D03BFD2418DD412D1ED1B31AF", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217A5427BBD4A4D50776989441370E3B5B16", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217A7FF985F55567D1B25EA40E23BB4CB1FE", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217A0835E548C7370D8F8D9925C0E6B54727", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ADC0CF1436399E67BC1122B31CB596649", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217A3D096F0DEAFF91938B82E5D404B0B065", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217AD56ABA897A355CF307CCB74226243192", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217A151284F950B1B1DBCAD6D9E7900DF4E6", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217AEF85A612514121C299A1D87116C4A182", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217A67F157569BFB4013EA3AD16DB8C69AD6", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217AF8520D191F6ACBD88B2140588B91C697", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ADD8BBAA71745669B96F2683E2F5AEC35", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217AFB2D4282817D7EC6B33EFAD7AA14A3C5", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217A459B89E7E0DAF3DA654576B60B2DA7CE", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217A65759F23F9789D05B23D5DBAA9E32036", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217A03C78FBD5E2CB08B3B6D181E23FBDE79", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA013D941FBBDE56C106C482CD022F290F", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA0645D313AC3C29B79DB1AA2E00A5B393", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA2ED0FD8048053BF22EBE501D82C4B3F1", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CAC57D706C7866A01D6E913F98AE57EE54", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CAB7FC1241FAFDFE45C4FF982D5DC1DAEF", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA7063EA296922DE8BDFD3B29D786C5F91", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA3A4603475F4AFDBFADC6E7FA908188B1", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA3365C63C2AF2A6C8FB4D0E9ED3C6FDA3", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA78BCC1874C0B7EB52645FC8F03B9C9CF", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA9B7A31397718EECB89B9E9CCCD729326", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CAB15EA8A67E9E9FADB4249710277F3D4F", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA94641D6A076193C660632CEA3F9CB02C", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CAB2170A08417BE77F0EAA9110F4790E12", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA4E30F1CD7B2256ABD57DC3DAB05376C9", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA9909B7B93D01BDAAC22D15AF34DF1EEF", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "4141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CAD97F5D1206F00E5C7225CAD81CCD4027", - true, - AESCBCPadder, - }, - { - "11111111111111111111111111111111", - "22222222222222222222222222222222", - "414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141", - "C3304FA46097CBBA59085416764A217ACEF79EE1163A2F52888F87A3979EB3CA570CBB001A0C87558906B60C884AB5F41DA97CEF2A9401BC6DD0D22A54DBAD6D", - true, - AESCBCPadder, - }, - } - - for i, testCase := range testCases { - key, _ := hex.DecodeString(testCase.key) - iv, _ := hex.DecodeString(testCase.iv) - cd := CipherData{ - Key: key, - IV: iv, - } - - cbc, err := newAESCBC(cd, testCase.padder) - if err != nil { - t.Fatal(fmt.Sprintf("Case %d: Expected no error for cipher creation, but received: %v", i, err.Error())) - } - - plaintext := []byte(testCase.plaintext) - if testCase.decodeHex { - plaintext, _ = hex.DecodeString(testCase.plaintext) - } - - cipherdata := cbc.Encrypt(bytes.NewReader(plaintext)) - ciphertext := []byte{} - b := make([]byte, 19) - err = nil - n := 0 - for err != io.EOF { - n, err = cipherdata.Read(b) - ciphertext = append(ciphertext, b[:n]...) - } - - if err != io.EOF { - t.Fatal(fmt.Sprintf("Case %d: Expected no error during io reading, but received: %v", i, err.Error())) - } - - expectedData, _ := hex.DecodeString(testCase.ciphertext) - if bytes.Compare(expectedData, ciphertext) != 0 { - t.Log("\n", ciphertext, "\n", expectedData) - t.Fatal(fmt.Sprintf("Case %d: AES CBC encryption fails. Data is not the same", i)) - } - - plaindata := cbc.Decrypt(bytes.NewReader(ciphertext)) - plaintextDecrypted := []byte{} - err = nil - for err != io.EOF { - n, err = plaindata.Read(b) - plaintextDecrypted = append(plaintextDecrypted, b[:n]...) - } - if err != io.EOF { - t.Fatal(fmt.Sprintf("Case %d: Expected no error during io reading, but received: %v", i, err.Error())) - } - - if bytes.Compare(plaintext, plaintextDecrypted) != 0 { - t.Log("\n", plaintext, "\n", plaintextDecrypted) - t.Fatal(fmt.Sprintf("Case %d: AES CBC decryption fails. Data is not the same", i)) - } - } -} diff --git a/service/s3/s3crypto/aes_gcm.go b/service/s3/s3crypto/aes_gcm.go deleted file mode 100644 index f127d34af42..00000000000 --- a/service/s3/s3crypto/aes_gcm.go +++ /dev/null @@ -1,105 +0,0 @@ -package s3crypto - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "io" - "io/ioutil" -) - -// AESGCM Symmetric encryption algorithm. Since Golang designed this -// with only TLS in mind. We have to load it all into memory meaning -// this isn't streamed. -type aesGCM struct { - aead cipher.AEAD - nonce []byte -} - -// newAESGCM creates a new AES GCM cipher. Expects keys to be of -// the correct size. -// -// Example: -// -// cd := &s3crypto.CipherData{ -// Key: key, -// "IV": iv, -// } -// cipher, err := s3crypto.newAESGCM(cd) -func newAESGCM(cd CipherData) (Cipher, error) { - block, err := aes.NewCipher(cd.Key) - if err != nil { - return nil, err - } - - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - return &aesGCM{aesgcm, cd.IV}, nil -} - -// Encrypt will encrypt the data using AES GCM -// Tag will be included as the last 16 bytes of the slice -func (c *aesGCM) Encrypt(src io.Reader) io.Reader { - reader := &gcmEncryptReader{ - encrypter: c.aead, - nonce: c.nonce, - src: src, - } - return reader -} - -type gcmEncryptReader struct { - encrypter cipher.AEAD - nonce []byte - src io.Reader - buf *bytes.Buffer -} - -func (reader *gcmEncryptReader) Read(data []byte) (int, error) { - if reader.buf == nil { - b, err := ioutil.ReadAll(reader.src) - if err != nil { - return len(b), err - } - b = reader.encrypter.Seal(b[:0], reader.nonce, b, nil) - reader.buf = bytes.NewBuffer(b) - } - - return reader.buf.Read(data) -} - -// Decrypt will decrypt the data using AES GCM -func (c *aesGCM) Decrypt(src io.Reader) io.Reader { - return &gcmDecryptReader{ - decrypter: c.aead, - nonce: c.nonce, - src: src, - } -} - -type gcmDecryptReader struct { - decrypter cipher.AEAD - nonce []byte - src io.Reader - buf *bytes.Buffer -} - -func (reader *gcmDecryptReader) Read(data []byte) (int, error) { - if reader.buf == nil { - b, err := ioutil.ReadAll(reader.src) - if err != nil { - return len(b), err - } - b, err = reader.decrypter.Open(b[:0], reader.nonce, b, nil) - if err != nil { - return len(b), err - } - - reader.buf = bytes.NewBuffer(b) - } - - return reader.buf.Read(data) -} diff --git a/service/s3/s3crypto/aes_gcm_content_cipher.go b/service/s3/s3crypto/aes_gcm_content_cipher.go deleted file mode 100644 index 6cc87d55294..00000000000 --- a/service/s3/s3crypto/aes_gcm_content_cipher.go +++ /dev/null @@ -1,68 +0,0 @@ -package s3crypto - -import ( - "io" -) - -const ( - gcmKeySize = 32 - gcmNonceSize = 12 -) - -type gcmContentCipherBuilder struct { - generator CipherDataGenerator -} - -// AESGCMContentCipherBuilder returns a new encryption only mode structure with a specific cipher -// for the master key -func AESGCMContentCipherBuilder(generator CipherDataGenerator) ContentCipherBuilder { - return gcmContentCipherBuilder{generator} -} - -func (builder gcmContentCipherBuilder) ContentCipher() (ContentCipher, error) { - cd, err := builder.generator.GenerateCipherData(gcmKeySize, gcmNonceSize) - if err != nil { - return nil, err - } - - return newAESGCMContentCipher(cd) -} - -func newAESGCMContentCipher(cd CipherData) (ContentCipher, error) { - cd.CEKAlgorithm = AESGCMNoPadding - cd.TagLength = "128" - - cipher, err := newAESGCM(cd) - if err != nil { - return nil, err - } - - return &aesGCMContentCipher{ - CipherData: cd, - Cipher: cipher, - }, nil -} - -// AESGCMContentCipher will use AES GCM for the main cipher. -type aesGCMContentCipher struct { - CipherData CipherData - Cipher Cipher -} - -// EncryptContents will generate a random key and iv and encrypt the data using cbc -func (cc *aesGCMContentCipher) EncryptContents(src io.Reader) (io.Reader, error) { - return cc.Cipher.Encrypt(src), nil -} - -// DecryptContents will use the symmetric key provider to instantiate a new GCM cipher. -// We grab a decrypt reader from gcm and wrap it in a CryptoReadCloser. The only error -// expected here is when the key or iv is of invalid length. -func (cc *aesGCMContentCipher) DecryptContents(src io.ReadCloser) (io.ReadCloser, error) { - reader := cc.Cipher.Decrypt(src) - return &CryptoReadCloser{Body: src, Decrypter: reader}, nil -} - -// GetCipherData returns cipher data -func (cc aesGCMContentCipher) GetCipherData() CipherData { - return cc.CipherData -} diff --git a/service/s3/s3crypto/aes_gcm_content_cipher_test.go b/service/s3/s3crypto/aes_gcm_content_cipher_test.go deleted file mode 100644 index 166a5f18e0f..00000000000 --- a/service/s3/s3crypto/aes_gcm_content_cipher_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package s3crypto_test - -import ( - "testing" - - "github.com/aws/aws-sdk-go-v2/service/s3/s3crypto" -) - -func TestAESGCMContentCipherBuilder(t *testing.T) { - generator := mockGenerator{} - if builder := s3crypto.AESGCMContentCipherBuilder(generator); builder == nil { - t.Error("expected non-nil value") - } -} - -func TestAESGCMContentCipherNewEncryptor(t *testing.T) { - generator := mockGenerator{} - builder := s3crypto.AESGCMContentCipherBuilder(generator) - cipher, err := builder.ContentCipher() - - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - if cipher == nil { - t.Errorf("expected non-nil vaue") - } -} diff --git a/service/s3/s3crypto/aes_gcm_test.go b/service/s3/s3crypto/aes_gcm_test.go deleted file mode 100644 index c430b280bd1..00000000000 --- a/service/s3/s3crypto/aes_gcm_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package s3crypto - -import ( - "bytes" - "encoding/hex" - "io/ioutil" - "testing" -) - -// AES GCM -func TestAES_GCM_NIST_gcmEncryptExtIV256_PTLen_128_Test_0(t *testing.T) { - iv, _ := hex.DecodeString("0d18e06c7c725ac9e362e1ce") - key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") - plaintext, _ := hex.DecodeString("2db5168e932556f8089a0622981d017d") - expected, _ := hex.DecodeString("fa4362189661d163fcd6a56d8bf0405ad636ac1bbedd5cc3ee727dc2ab4a9489") - tag, _ := hex.DecodeString("d636ac1bbedd5cc3ee727dc2ab4a9489") - aesgcmTest(t, iv, key, plaintext, expected, tag) -} - -func TestAES_GCM_NIST_gcmEncryptExtIV256_PTLen_104_Test_3(t *testing.T) { - iv, _ := hex.DecodeString("4742357c335913153ff0eb0f") - key, _ := hex.DecodeString("e5a0eb92cc2b064e1bc80891faf1fab5e9a17a9c3a984e25416720e30e6c2b21") - plaintext, _ := hex.DecodeString("8499893e16b0ba8b007d54665a") - expected, _ := hex.DecodeString("eb8e6175f1fe38eb1acf95fd5188a8b74bb74fda553e91020a23deed45") - tag, _ := hex.DecodeString("88a8b74bb74fda553e91020a23deed45") - aesgcmTest(t, iv, key, plaintext, expected, tag) -} - -func TestAES_GCM_NIST_gcmEncryptExtIV256_PTLen_256_Test_6(t *testing.T) { - iv, _ := hex.DecodeString("a291484c3de8bec6b47f525f") - key, _ := hex.DecodeString("37f39137416bafde6f75022a7a527cc593b6000a83ff51ec04871a0ff5360e4e") - plaintext, _ := hex.DecodeString("fafd94cede8b5a0730394bec68a8e77dba288d6ccaa8e1563a81d6e7ccc7fc97") - expected, _ := hex.DecodeString("44dc868006b21d49284016565ffb3979cc4271d967628bf7cdaf86db888e92e501a2b578aa2f41ec6379a44a31cc019c") - tag, _ := hex.DecodeString("01a2b578aa2f41ec6379a44a31cc019c") - aesgcmTest(t, iv, key, plaintext, expected, tag) -} - -func TestAES_GCM_NIST_gcmEncryptExtIV256_PTLen_408_Test_8(t *testing.T) { - iv, _ := hex.DecodeString("92f258071d79af3e63672285") - key, _ := hex.DecodeString("595f259c55abe00ae07535ca5d9b09d6efb9f7e9abb64605c337acbd6b14fc7e") - plaintext, _ := hex.DecodeString("a6fee33eb110a2d769bbc52b0f36969c287874f665681477a25fc4c48015c541fbe2394133ba490a34ee2dd67b898177849a91") - expected, _ := hex.DecodeString("bbca4a9e09ae9690c0f6f8d405e53dccd666aa9c5fa13c8758bc30abe1ddd1bcce0d36a1eaaaaffef20cd3c5970b9673f8a65c26ccecb9976fd6ac9c2c0f372c52c821") - tag, _ := hex.DecodeString("26ccecb9976fd6ac9c2c0f372c52c821") - aesgcmTest(t, iv, key, plaintext, expected, tag) -} - -func aesgcmTest(t *testing.T, iv, key, plaintext, expected, tag []byte) { - cd := CipherData{ - Key: key, - IV: iv, - } - gcm, err := newAESGCM(cd) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - cipherdata := gcm.Encrypt(bytes.NewReader(plaintext)) - - ciphertext, err := ioutil.ReadAll(cipherdata) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - // splitting tag and ciphertext - etag := ciphertext[len(ciphertext)-16:] - if !bytes.Equal(etag, tag) { - t.Errorf("expected tags to be equivalent") - } - if !bytes.Equal(ciphertext, expected) { - t.Errorf("expected ciphertext to be equivalent") - } - - data := gcm.Decrypt(bytes.NewReader(ciphertext)) - text, err := ioutil.ReadAll(data) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - if !bytes.Equal(plaintext, text) { - t.Errorf("expected ciphertext to be equivalent") - } -} diff --git a/service/s3/s3crypto/cipher.go b/service/s3/s3crypto/cipher.go deleted file mode 100644 index 8c95ea253d8..00000000000 --- a/service/s3/s3crypto/cipher.go +++ /dev/null @@ -1,43 +0,0 @@ -package s3crypto - -import ( - "io" -) - -// Cipher interface allows for either encryption and decryption of an object -type Cipher interface { - Encrypter - Decrypter -} - -// Encrypter interface with only the encrypt method -type Encrypter interface { - Encrypt(io.Reader) io.Reader -} - -// Decrypter interface with only the decrypt method -type Decrypter interface { - Decrypt(io.Reader) io.Reader -} - -// CryptoReadCloser handles closing of the body and allowing reads from the decrypted -// content. -type CryptoReadCloser struct { - Body io.ReadCloser - Decrypter io.Reader - isClosed bool -} - -// Close lets the CryptoReadCloser satisfy io.ReadCloser interface -func (rc *CryptoReadCloser) Close() error { - rc.isClosed = true - return rc.Body.Close() -} - -// Read lets the CryptoReadCloser satisfy io.ReadCloser interface -func (rc *CryptoReadCloser) Read(b []byte) (int, error) { - if rc.isClosed { - return 0, io.EOF - } - return rc.Decrypter.Read(b) -} diff --git a/service/s3/s3crypto/cipher_builder.go b/service/s3/s3crypto/cipher_builder.go deleted file mode 100644 index b3a0d318149..00000000000 --- a/service/s3/s3crypto/cipher_builder.go +++ /dev/null @@ -1,31 +0,0 @@ -package s3crypto - -import "io" - -// ContentCipherBuilder is a builder interface that builds -// ciphers for each request. -type ContentCipherBuilder interface { - ContentCipher() (ContentCipher, error) -} - -// ContentCipher deals with encrypting and decrypting content -type ContentCipher interface { - EncryptContents(io.Reader) (io.Reader, error) - DecryptContents(io.ReadCloser) (io.ReadCloser, error) - GetCipherData() CipherData -} - -// CipherData is used for content encryption. It is used for storing the -// metadata of the encrypted content. -type CipherData struct { - Key []byte - IV []byte - WrapAlgorithm string - CEKAlgorithm string - TagLength string - MaterialDescription MaterialDescription - // EncryptedKey should be populated when calling GenerateCipherData - EncryptedKey []byte - - Padder Padder -} diff --git a/service/s3/s3crypto/cipher_test.go b/service/s3/s3crypto/cipher_test.go deleted file mode 100644 index 8044660102f..00000000000 --- a/service/s3/s3crypto/cipher_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package s3crypto_test - -import ( - "io/ioutil" - "strings" - "testing" - - "github.com/aws/aws-sdk-go-v2/service/s3/s3crypto" -) - -func TestCryptoReadCloserRead(t *testing.T) { - expectedStr := "HELLO WORLD " - str := strings.NewReader(expectedStr) - rc := &s3crypto.CryptoReadCloser{Body: ioutil.NopCloser(str), Decrypter: str} - - b, err := ioutil.ReadAll(rc) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - if expectedStr != string(b) { - t.Errorf("expected %s, but received %s", expectedStr, string(b)) - } -} - -func TestCryptoReadCloserClose(t *testing.T) { - data := "HELLO WORLD " - expectedStr := "" - - str := strings.NewReader(data) - rc := &s3crypto.CryptoReadCloser{Body: ioutil.NopCloser(str), Decrypter: str} - rc.Close() - - b, err := ioutil.ReadAll(rc) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - if expectedStr != string(b) { - t.Errorf("expected %s, but received %s", expectedStr, string(b)) - } -} diff --git a/service/s3/s3crypto/cipher_util.go b/service/s3/s3crypto/cipher_util.go deleted file mode 100644 index c78c77c424f..00000000000 --- a/service/s3/s3crypto/cipher_util.go +++ /dev/null @@ -1,111 +0,0 @@ -package s3crypto - -import ( - "encoding/base64" - "strconv" - "strings" - - "github.com/aws/aws-sdk-go-v2/aws/awserr" -) - -func (client *DecryptionClient) contentCipherFromEnvelope(env Envelope) (ContentCipher, error) { - wrap, err := client.wrapFromEnvelope(env) - if err != nil { - return nil, err - } - - return client.cekFromEnvelope(env, wrap) -} - -func (client *DecryptionClient) wrapFromEnvelope(env Envelope) (CipherDataDecrypter, error) { - f, ok := client.WrapRegistry[env.WrapAlg] - if !ok || f == nil { - return nil, awserr.New( - "InvalidWrapAlgorithmError", - "wrap algorithm isn't supported, "+env.WrapAlg, - nil, - ) - } - return f(env) -} - -// AESGCMNoPadding is the constant value that is used to specify -// the CEK algorithm consiting of AES GCM with no padding. -const AESGCMNoPadding = "AES/GCM/NoPadding" - -// AESCBC is the string constant that signifies the AES CBC algorithm cipher. -const AESCBC = "AES/CBC" - -func (client *DecryptionClient) cekFromEnvelope(env Envelope, decrypter CipherDataDecrypter) (ContentCipher, error) { - f, ok := client.CEKRegistry[env.CEKAlg] - if !ok || f == nil { - return nil, awserr.New( - "InvalidCEKAlgorithmError", - "cek algorithm isn't supported, "+env.CEKAlg, - nil, - ) - } - - key, err := base64.StdEncoding.DecodeString(env.CipherKey) - if err != nil { - return nil, err - } - - iv, err := base64.StdEncoding.DecodeString(env.IV) - if err != nil { - return nil, err - } - key, err = decrypter.DecryptKey(key) - if err != nil { - return nil, err - } - - cd := CipherData{ - Key: key, - IV: iv, - CEKAlgorithm: env.CEKAlg, - Padder: client.getPadder(env.CEKAlg), - } - return f(cd) -} - -// getPadder will return an unpadder with checking the cek algorithm specific padder. -// If there wasn't a cek algorithm specific padder, we check the padder itself. -// We return a no unpadder, if no unpadder was found. This means any customization -// either contained padding within the cipher implementation, and to maintain -// backwards compatility we will simply not unpad anything. -func (client *DecryptionClient) getPadder(cekAlg string) Padder { - padder, ok := client.PadderRegistry[cekAlg] - if !ok { - padder, ok = client.PadderRegistry[cekAlg[strings.LastIndex(cekAlg, "/")+1:]] - if !ok { - return NoPadder - } - } - return padder -} - -func encodeMeta(reader hashReader, cd CipherData) (Envelope, error) { - iv := base64.StdEncoding.EncodeToString(cd.IV) - key := base64.StdEncoding.EncodeToString(cd.EncryptedKey) - - md5 := reader.GetValue() - contentLength := reader.GetContentLength() - - md5Str := base64.StdEncoding.EncodeToString(md5) - matdesc, err := cd.MaterialDescription.encodeDescription() - if err != nil { - return Envelope{}, err - } - - return Envelope{ - CipherKey: key, - IV: iv, - MatDesc: string(matdesc), - WrapAlg: cd.WrapAlgorithm, - CEKAlg: cd.CEKAlgorithm, - TagLen: cd.TagLength, - UnencryptedMD5: md5Str, - UnencryptedContentLen: strconv.FormatInt(contentLength, 10), - }, nil -} diff --git a/service/s3/s3crypto/cipher_util_test.go b/service/s3/s3crypto/cipher_util_test.go deleted file mode 100644 index 6a7f73a775c..00000000000 --- a/service/s3/s3crypto/cipher_util_test.go +++ /dev/null @@ -1,258 +0,0 @@ -package s3crypto - -import ( - "encoding/base64" - "encoding/hex" - "fmt" - "net/http" - "net/http/httptest" - "testing" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/internal/awstesting/unit" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func TestWrapFactory(t *testing.T) { - c := DecryptionClient{ - WrapRegistry: map[string]WrapEntry{ - KMSWrap: (kmsKeyHandler{ - kms: kms.New(unit.Config()), - }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ - AESGCMNoPadding: newAESGCMContentCipher, - }, - } - env := Envelope{ - WrapAlg: KMSWrap, - MatDesc: `{"kms_cmk_id":""}`, - } - wrap, err := c.wrapFromEnvelope(env) - w, ok := wrap.(*kmsKeyHandler) - - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - if wrap == nil { - t.Error("expected non-nil value") - } - if !ok { - t.Errorf("expected kmsKeyHandler, but received %v", *w) - } -} -func TestWrapFactoryErrorNoWrap(t *testing.T) { - c := DecryptionClient{ - WrapRegistry: map[string]WrapEntry{ - KMSWrap: (kmsKeyHandler{ - kms: kms.New(unit.Config()), - }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ - AESGCMNoPadding: newAESGCMContentCipher, - }, - } - env := Envelope{ - WrapAlg: "none", - MatDesc: `{"kms_cmk_id":""}`, - } - wrap, err := c.wrapFromEnvelope(env) - - if err == nil { - t.Error("expected error, but received none") - } - if wrap != nil { - t.Errorf("expected nil wrap value, received %v", wrap) - } -} - -func TestWrapFactoryCustomEntry(t *testing.T) { - c := DecryptionClient{ - WrapRegistry: map[string]WrapEntry{ - "custom": (kmsKeyHandler{ - kms: kms.New(unit.Config()), - }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ - AESGCMNoPadding: newAESGCMContentCipher, - }, - } - env := Envelope{ - WrapAlg: "custom", - MatDesc: `{"kms_cmk_id":""}`, - } - wrap, err := c.wrapFromEnvelope(env) - - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - if wrap == nil { - t.Errorf("expected nil wrap value, received %v", wrap) - } -} - -func TestCEKFactory(t *testing.T) { - key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") - keyB64 := base64.URLEncoding.EncodeToString(key) - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) - })) - defer ts.Close() - - cfg := unit.Config() - cfg.Retryer = aws.NoOpRetryer{} - cfg.EndpointResolver = aws.ResolveWithEndpointURL(ts.URL) - cfg.Region = "us-west-2" - - c := DecryptionClient{ - WrapRegistry: map[string]WrapEntry{ - KMSWrap: (kmsKeyHandler{ - kms: kms.New(cfg), - }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ - AESGCMNoPadding: newAESGCMContentCipher, - }, - PadderRegistry: map[string]Padder{ - NoPadder.Name(): NoPadder, - }, - } - iv, err := hex.DecodeString("0d18e06c7c725ac9e362e1ce") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - ivB64 := base64.URLEncoding.EncodeToString(iv) - - cipherKey, err := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - cipherKeyB64 := base64.URLEncoding.EncodeToString(cipherKey) - - env := Envelope{ - WrapAlg: KMSWrap, - CEKAlg: AESGCMNoPadding, - CipherKey: cipherKeyB64, - IV: ivB64, - MatDesc: `{"kms_cmk_id":""}`, - } - wrap, err := c.wrapFromEnvelope(env) - cek, err := c.cekFromEnvelope(env, wrap) - - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - if cek == nil { - t.Errorf("expected non-nil cek") - } -} - -func TestCEKFactoryNoCEK(t *testing.T) { - key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") - keyB64 := base64.URLEncoding.EncodeToString(key) - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) - })) - defer ts.Close() - - cfg := unit.Config() - cfg.Retryer = aws.NoOpRetryer{} - cfg.EndpointResolver = aws.ResolveWithEndpointURL(ts.URL) - cfg.Region = "us-west-2" - - c := DecryptionClient{ - WrapRegistry: map[string]WrapEntry{ - KMSWrap: (kmsKeyHandler{ - kms: kms.New(cfg), - }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ - AESGCMNoPadding: newAESGCMContentCipher, - }, - PadderRegistry: map[string]Padder{ - NoPadder.Name(): NoPadder, - }, - } - iv, err := hex.DecodeString("0d18e06c7c725ac9e362e1ce") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - ivB64 := base64.URLEncoding.EncodeToString(iv) - - cipherKey, err := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - cipherKeyB64 := base64.URLEncoding.EncodeToString(cipherKey) - - env := Envelope{ - WrapAlg: KMSWrap, - CEKAlg: "none", - CipherKey: cipherKeyB64, - IV: ivB64, - MatDesc: `{"kms_cmk_id":""}`, - } - wrap, err := c.wrapFromEnvelope(env) - cek, err := c.cekFromEnvelope(env, wrap) - - if err == nil { - t.Error("expected error, but received none") - } - if cek != nil { - t.Errorf("expected nil cek value, received %v", wrap) - } -} - -func TestCEKFactoryCustomEntry(t *testing.T) { - key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") - keyB64 := base64.URLEncoding.EncodeToString(key) - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) - })) - defer ts.Close() - - cfg := unit.Config() - cfg.Retryer = aws.NoOpRetryer{} - cfg.EndpointResolver = aws.ResolveWithEndpointURL(ts.URL) - cfg.Region = "us-west-2" - - c := DecryptionClient{ - WrapRegistry: map[string]WrapEntry{ - KMSWrap: (kmsKeyHandler{ - kms: kms.New(cfg), - }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ - "custom": newAESGCMContentCipher, - }, - PadderRegistry: map[string]Padder{}, - } - iv, err := hex.DecodeString("0d18e06c7c725ac9e362e1ce") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - ivB64 := base64.URLEncoding.EncodeToString(iv) - - cipherKey, err := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - cipherKeyB64 := base64.URLEncoding.EncodeToString(cipherKey) - - env := Envelope{ - WrapAlg: KMSWrap, - CEKAlg: "custom", - CipherKey: cipherKeyB64, - IV: ivB64, - MatDesc: `{"kms_cmk_id":""}`, - } - wrap, err := c.wrapFromEnvelope(env) - cek, err := c.cekFromEnvelope(env, wrap) - - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - if cek == nil { - t.Errorf("expected non-nil cek") - } -} diff --git a/service/s3/s3crypto/cust_integ_test.go b/service/s3/s3crypto/cust_integ_test.go deleted file mode 100644 index 042b59a7101..00000000000 --- a/service/s3/s3crypto/cust_integ_test.go +++ /dev/null @@ -1,262 +0,0 @@ -// +build go1.9,s3crypto_integ - -package s3crypto_test - -import ( - "bytes" - "context" - "encoding/base64" - "fmt" - "io/ioutil" - "strings" - "testing" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/internal/awstesting/integration" - "github.com/aws/aws-sdk-go-v2/service/kms" - "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/aws/aws-sdk-go-v2/service/s3/s3crypto" -) - -func TestInteg_EncryptFixtures(t *testing.T) { - ctx := context.Background() - - cfg := integration.ConfigWithDefaultRegion("us-west-2") - - const bucket = "aws-s3-shared-tests" - const version = "version_2" - - cases := []struct { - CEKAlg string - KEK, V1, V2, CEK string - }{ - { - CEKAlg: "aes_gcm", - KEK: "kms", V1: "AWS_SDK_TEST_ALIAS", V2: "us-west-2", CEK: "aes_gcm", - }, - { - CEKAlg: "aes_cbc", - KEK: "kms", V1: "AWS_SDK_TEST_ALIAS", V2: "us-west-2", CEK: "aes_cbc", - }, - } - - for _, c := range cases { - t.Run(c.CEKAlg, func(t *testing.T) { - s3Client := s3.New(cfg) - - fixtures := getFixtures(ctx, t, s3Client, c.CEKAlg, bucket) - builder, masterKey := getEncryptFixtureBuilder(ctx, t, c.KEK, c.V1, c.V2, c.CEK) - - encClient := s3crypto.NewEncryptionClient(cfg, builder) - - for caseKey, plaintext := range fixtures.Plaintexts { - _, err := encClient.PutObject(&s3.PutObjectInput{ - Bucket: aws.String(bucket), - Key: aws.String( - fmt.Sprintf("%s/%s/language_Go/ciphertext_test_case_%s", - fixtures.BaseFolder, version, caseKey), - ), - Body: bytes.NewReader(plaintext), - Metadata: map[string]string{ - "Masterkey": masterKey, - }, - }) - if err != nil { - t.Fatalf("failed to upload encrypted fixture, %v", err) - } - } - }) - } -} - -func TestInteg_DecryptFixtures(t *testing.T) { - ctx := context.Background() - - cfg := integration.ConfigWithDefaultRegion("us-west-2") - - const bucket = "aws-s3-shared-tests" - const version = "version_2" - - cases := []struct { - CEKAlg string - Lang string - }{ - {CEKAlg: "aes_cbc", Lang: "Go"}, - {CEKAlg: "aes_gcm", Lang: "Go"}, - {CEKAlg: "aes_cbc", Lang: "Java"}, - {CEKAlg: "aes_gcm", Lang: "Java"}, - } - - for _, c := range cases { - t.Run(c.CEKAlg+"-"+c.Lang, func(t *testing.T) { - decClient := s3crypto.NewDecryptionClient(cfg) - s3Client := s3.New(cfg) - - fixtures := getFixtures(ctx, t, s3Client, c.CEKAlg, bucket) - ciphertexts := decryptFixtures(ctx, t, decClient, s3Client, fixtures, bucket, c.Lang, version) - - for caseKey, ciphertext := range ciphertexts { - if e, a := len(fixtures.Plaintexts[caseKey]), len(ciphertext); e != a { - t.Errorf("expect %v text len, got %v", e, a) - } - if e, a := fixtures.Plaintexts[caseKey], ciphertext; !bytes.Equal(e, a) { - t.Errorf("expect %v text, got %v", e, a) - } - } - }) - } -} - -type testFixtures struct { - BaseFolder string - Plaintexts map[string][]byte -} - -func getFixtures(ctx context.Context, t *testing.T, s3Client *s3.S3, cekAlg, bucket string) testFixtures { - t.Helper() - - prefix := "plaintext_test_case_" - baseFolder := "crypto_tests/" + cekAlg - - out, err := s3Client.ListObjectsRequest(&s3.ListObjectsInput{ - Bucket: aws.String(bucket), - Prefix: aws.String(baseFolder + "/" + prefix), - }).Send(ctx) - if err != nil { - t.Fatalf("unable to list fixtures %v", err) - } - - plaintexts := map[string][]byte{} - for _, obj := range out.Contents { - ptObj, err := s3Client.GetObjectRequest(&s3.GetObjectInput{ - Bucket: aws.String(bucket), - Key: obj.Key, - }).Send(ctx) - if err != nil { - t.Fatalf("unable to get fixture object %s, %v", *obj.Key, err) - } - caseKey := strings.TrimPrefix(*obj.Key, baseFolder+"/"+prefix) - plaintext, err := ioutil.ReadAll(ptObj.Body) - if err != nil { - t.Fatalf("unable to read fixture object %s, %v", *obj.Key, err) - } - - plaintexts[caseKey] = plaintext - } - - return testFixtures{ - BaseFolder: baseFolder, - Plaintexts: plaintexts, - } -} - -func getEncryptFixtureBuilder(ctx context.Context, t *testing.T, kek, v1, v2, cek string, -) (builder s3crypto.ContentCipherBuilder, masterKey string) { - t.Helper() - - var handler s3crypto.CipherDataGenerator - switch kek { - case "kms": - arn, err := getAliasInformation(ctx, v1, v2) - if err != nil { - t.Fatalf("failed to get fixture alias info for %s, %v", v1, err) - } - - masterKey = base64.StdEncoding.EncodeToString([]byte(arn)) - if err != nil { - t.Fatalf("failed to encode alias's arn %v", err) - } - - kmsCfg := integration.Config() - kmsCfg.Region = v2 - - kmsSvc := kms.New(kmsCfg) - handler = s3crypto.NewKMSKeyGenerator(kmsSvc, arn) - default: - t.Fatalf("unknown fixture KEK, %v", kek) - } - - switch cek { - case "aes_gcm": - builder = s3crypto.AESGCMContentCipherBuilder(handler) - case "aes_cbc": - builder = s3crypto.AESCBCContentCipherBuilder(handler, s3crypto.AESCBCPadder) - default: - t.Fatalf("unknown fixture CEK, %v", cek) - } - - return builder, masterKey -} - -func getAliasInformation(ctx context.Context, alias, region string) (string, error) { - kmsCfg := integration.Config() - kmsCfg.Region = region - - arn := "" - svc := kms.New(kmsCfg) - - truncated := true - var marker *string - for truncated { - out, err := svc.ListAliasesRequest(&kms.ListAliasesInput{ - Marker: marker, - }).Send(ctx) - if err != nil { - return arn, err - } - for _, aliasEntry := range out.Aliases { - if *aliasEntry.AliasName == "alias/"+alias { - return *aliasEntry.AliasArn, nil - } - } - truncated = *out.Truncated - marker = out.NextMarker - } - - return "", fmt.Errorf("kms alias %s does not exist", alias) -} - -func decryptFixtures(ctx context.Context, t *testing.T, decClient *s3crypto.DecryptionClient, s3Client *s3.S3, - fixtures testFixtures, bucket, lang, version string, -) map[string][]byte { - t.Helper() - - prefix := "ciphertext_test_case_" - lang = "language_" + lang - - ciphertexts := map[string][]byte{} - for caseKey := range fixtures.Plaintexts { - cipherKey := fixtures.BaseFolder + "/" + version + "/" + lang + "/" + prefix + caseKey - - // To get metadata for encryption key - ctObj, err := s3Client.GetObjectRequest(&s3.GetObjectInput{ - Bucket: &bucket, - Key: &cipherKey, - }).Send(ctx) - if err != nil { - // TODO error? - continue - } - - // We don't support wrap, so skip it - if ctObj.Metadata["X-Amz-Wrap-Alg"] != "kms" { - continue - } - - ctObj, err = decClient.GetObject(&s3.GetObjectInput{ - Bucket: &bucket, - Key: &cipherKey, - }) - if err != nil { - t.Fatalf("failed to get encrypted object %v", err) - } - - ciphertext, err := ioutil.ReadAll(ctObj.Body) - if err != nil { - t.Fatalf("failed to read object data %v", err) - } - ciphertexts[caseKey] = ciphertext - } - - return ciphertexts -} diff --git a/service/s3/s3crypto/decryption_client.go b/service/s3/s3crypto/decryption_client.go deleted file mode 100644 index 75bc67be8eb..00000000000 --- a/service/s3/s3crypto/decryption_client.go +++ /dev/null @@ -1,144 +0,0 @@ -package s3crypto - -import ( - "context" - "strings" - - "github.com/aws/aws-sdk-go-v2/aws" - request "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/kms" - "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/aws/aws-sdk-go-v2/service/s3/s3iface" -) - -// WrapEntry is builder that return a proper key decrypter and error -type WrapEntry func(Envelope) (CipherDataDecrypter, error) - -// CEKEntry is a builder thatn returns a proper content decrypter and error -type CEKEntry func(CipherData) (ContentCipher, error) - -// DecryptionClient is an S3 crypto client. The decryption client -// will handle all get object requests from Amazon S3. -// Supported key wrapping algorithms: -// *AWS KMS -// -// Supported content ciphers: -// * AES/GCM -// * AES/CBC -type DecryptionClient struct { - S3Client s3iface.ClientAPI - // LoadStrategy is used to load the metadata either from the metadata of the object - // or from a separate file in s3. - // - // Defaults to our default load strategy. - LoadStrategy LoadStrategy - - WrapRegistry map[string]WrapEntry - CEKRegistry map[string]CEKEntry - PadderRegistry map[string]Padder -} - -// NewDecryptionClient instantiates a new S3 crypto client -// -// Example: -// cfg, err := external.LoadDefaultAWSConfig() -// svc := s3crypto.NewDecryptionClient(cfg, func(svc *s3crypto.DecryptionClient{ -// // Custom client options here -// })) -func NewDecryptionClient(cfg aws.Config, options ...func(*DecryptionClient)) *DecryptionClient { - s3client := s3.New(cfg) - client := &DecryptionClient{ - S3Client: s3client, - LoadStrategy: defaultV2LoadStrategy{ - client: s3client, - }, - WrapRegistry: map[string]WrapEntry{ - KMSWrap: (kmsKeyHandler{ - kms: kms.New(cfg), - }).decryptHandler, - }, - CEKRegistry: map[string]CEKEntry{ - AESGCMNoPadding: newAESGCMContentCipher, - strings.Join([]string{AESCBC, AESCBCPadder.Name()}, "/"): newAESCBCContentCipher, - }, - PadderRegistry: map[string]Padder{ - strings.Join([]string{AESCBC, AESCBCPadder.Name()}, "/"): AESCBCPadder, - "NoPadding": NoPadder, - }, - } - for _, option := range options { - option(client) - } - - return client -} - -// GetObjectRequest will make a request to s3 and retrieve the object. In this process -// decryption will be done. The SDK only supports V2 reads of KMS and GCM. -// -// Example: -// cfg, err := external.LoadDefaultAWSConfig() -// svc := s3crypto.NewDecryptionClient(cfg) -// req, out := svc.GetObjectRequest(&s3.GetObjectInput { -// Key: aws.String("testKey"), -// Bucket: aws.String("testBucket"), -// }) -// err := req.Send() -func (c *DecryptionClient) GetObjectRequest(input *s3.GetObjectInput) s3.GetObjectRequest { - req := c.S3Client.GetObjectRequest(input) - out := req.Data.(*s3.GetObjectOutput) - - req.Handlers.Unmarshal.PushBack(func(r *request.Request) { - env, err := c.LoadStrategy.Load(r) - if err != nil { - r.Error = err - out.Body.Close() - return - } - - // If KMS should return the correct CEK algorithm with the proper - // KMS key provider - cipher, err := c.contentCipherFromEnvelope(env) - if err != nil { - r.Error = err - out.Body.Close() - return - } - - reader, err := cipher.DecryptContents(out.Body) - if err != nil { - r.Error = err - out.Body.Close() - return - } - out.Body = reader - }) - return req -} - -// GetObject is a wrapper for GetObjectRequest -func (c *DecryptionClient) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOutput, error) { - req := c.GetObjectRequest(input) - resp, err := req.Send(context.Background()) - if err != nil { - return nil, err - } - return resp.GetObjectOutput, nil -} - -// GetObjectWithContext is a wrapper for GetObjectRequest with the additional -// context, and request options support. -// -// GetObjectWithContext is the same as GetObject with the additional support for -// Context input parameters. The Context must not be nil. A nil Context will -// cause a panic. Use the Context to add deadlining, timeouts, ect. In the future -// this may create sub-contexts for individual underlying requests. -func (c *DecryptionClient) GetObjectWithContext(ctx context.Context, input *s3.GetObjectInput, opts ...request.Option) (*s3.GetObjectOutput, error) { - req := c.GetObjectRequest(input) - req.ApplyOptions(opts...) - resp, err := req.Send(ctx) - if err != nil { - return nil, err - } - return resp.GetObjectOutput, nil -} diff --git a/service/s3/s3crypto/decryption_client_test.go b/service/s3/s3crypto/decryption_client_test.go deleted file mode 100644 index 29cd27971a8..00000000000 --- a/service/s3/s3crypto/decryption_client_test.go +++ /dev/null @@ -1,251 +0,0 @@ -package s3crypto_test - -import ( - "bytes" - "context" - "encoding/base64" - "encoding/hex" - "errors" - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/internal/awstesting" - "github.com/aws/aws-sdk-go-v2/internal/awstesting/unit" - "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/aws/aws-sdk-go-v2/service/s3/s3crypto" -) - -func TestGetObjectGCM(t *testing.T) { - key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") - keyB64 := base64.StdEncoding.EncodeToString(key) - // This is our KMS response - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) - })) - defer ts.Close() - - cfg := unit.Config() - cfg.Retryer = aws.NoOpRetryer{} - cfg.EndpointResolver = aws.ResolveWithEndpointURL(ts.URL) - cfg.Region = "us-west-2" - - c := s3crypto.NewDecryptionClient(cfg) - if c == nil { - t.Fatalf("failed to create a new S3 crypto decryption client") - } - - c.S3Client.(*s3.Client).ForcePathStyle = true - input := &s3.GetObjectInput{ - Key: aws.String("test"), - Bucket: aws.String("test"), - } - req := c.GetObjectRequest(input) - out := req.Data.(*s3.GetObjectOutput) - req.Handlers.Send.Clear() - req.Handlers.Send.PushBack(func(r *aws.Request) { - iv, err := hex.DecodeString("0d18e06c7c725ac9e362e1ce") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - b, err := hex.DecodeString("fa4362189661d163fcd6a56d8bf0405ad636ac1bbedd5cc3ee727dc2ab4a9489") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - r.HTTPResponse = &http.Response{ - StatusCode: 200, - Header: http.Header{ - http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"SpFRES0JyU8BLZSKo51SrwILK4lhtZsWiMNjgO4WmoK+joMwZPG7Hw=="}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{base64.URLEncoding.EncodeToString(iv)}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"kms_cmk_id":"arn:aws:kms:us-east-1:172259396726:key/a22a4b30-79f4-4b3d-bab4-a26d327a231b"}`}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSWrap}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{s3crypto.AESGCMNoPadding}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-tag-len"): []string{"128"}, - }, - Body: ioutil.NopCloser(bytes.NewBuffer(b)), - } - out.Metadata = make(map[string]string) - out.Metadata["x-amz-wrap-alg"] = s3crypto.KMSWrap - }) - _, err := req.Send(context.Background()) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - b, err := ioutil.ReadAll(out.Body) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - expected, err := hex.DecodeString("2db5168e932556f8089a0622981d017d") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - if !bytes.Equal(expected, b) { - t.Error("expected bytes to be equivalent") - } -} - -func TestGetObjectCBC(t *testing.T) { - key, _ := hex.DecodeString("898be9cc5004ed0fa6e117c9a3099d31") - keyB64 := base64.StdEncoding.EncodeToString(key) - // This is our KMS response - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) - })) - defer ts.Close() - - cfg := unit.Config() - cfg.Retryer = aws.NoOpRetryer{} - cfg.EndpointResolver = aws.ResolveWithEndpointURL(ts.URL) - cfg.Region = "us-west-2" - - c := s3crypto.NewDecryptionClient(cfg) - if c == nil { - t.Fatalf("failed to create a new S3 crypto decryption client") - } - - c.S3Client.(*s3.Client).ForcePathStyle = true - input := &s3.GetObjectInput{ - Key: aws.String("test"), - Bucket: aws.String("test"), - } - req := c.GetObjectRequest(input) - out := req.Data.(*s3.GetObjectOutput) - req.Handlers.Send.Clear() - req.Handlers.Send.PushBack(func(r *aws.Request) { - iv, err := hex.DecodeString("9dea7621945988f96491083849b068df") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - b, err := hex.DecodeString("e232cd6ef50047801ee681ec30f61d53cfd6b0bca02fd03c1b234baa10ea82ac9dab8b960926433a19ce6dea08677e34") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - r.HTTPResponse = &http.Response{ - StatusCode: 200, - Header: http.Header{ - http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"SpFRES0JyU8BLZSKo51SrwILK4lhtZsWiMNjgO4WmoK+joMwZPG7Hw=="}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{base64.URLEncoding.EncodeToString(iv)}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"kms_cmk_id":"arn:aws:kms:us-east-1:172259396726:key/a22a4b30-79f4-4b3d-bab4-a26d327a231b"}`}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSWrap}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{strings.Join([]string{s3crypto.AESCBC, s3crypto.AESCBCPadder.Name()}, "/")}, - }, - Body: ioutil.NopCloser(bytes.NewBuffer(b)), - } - out.Metadata = make(map[string]string) - out.Metadata["x-amz-wrap-alg"] = s3crypto.KMSWrap - }) - _, err := req.Send(context.Background()) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - b, err := ioutil.ReadAll(out.Body) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - expected, err := hex.DecodeString("0397f4f6820b1f9386f14403be5ac16e50213bd473b4874b9bcbf5f318ee686b1d") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - if !bytes.Equal(expected, b) { - t.Error("expected bytes to be equivalent") - } -} - -func TestGetObjectCBC2(t *testing.T) { - key, _ := hex.DecodeString("8d70e92489c4e6cfb12261b4d17f4b85826da687fc8742fcf9f87fadb5b4cb89") - keyB64 := base64.StdEncoding.EncodeToString(key) - // This is our KMS response - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) - })) - defer ts.Close() - - cfg := unit.Config() - cfg.Retryer = aws.NoOpRetryer{} - cfg.EndpointResolver = aws.ResolveWithEndpointURL(ts.URL) - cfg.Region = "us-west-2" - - c := s3crypto.NewDecryptionClient(cfg) - if c == nil { - t.Fatalf("failed to create a new S3 crypto decryption client") - } - - c.S3Client.(*s3.Client).ForcePathStyle = true - input := &s3.GetObjectInput{ - Key: aws.String("test"), - Bucket: aws.String("test"), - } - req := c.GetObjectRequest(input) - out := req.Data.(*s3.GetObjectOutput) - req.Handlers.Send.Clear() - req.Handlers.Send.PushBack(func(r *aws.Request) { - b, err := hex.DecodeString("fd0c71ecb7ed16a9bf42ea5f75501d416df608f190890c3b4d8897f24744cd7f9ea4a0b212e60634302450e1c5378f047ff753ccefe365d411c36339bf22e301fae4c3a6226719a4b93dc74c1af79d0296659b5d56c0892315f2c7cc30190220db1eaafae3920d6d9c65d0aa366499afc17af493454e141c6e0fbdeb6a990cb4") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - r.HTTPResponse = &http.Response{ - StatusCode: 200, - Header: http.Header{ - http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"AQEDAHikdGvcj7Gil5VqAR/JWvvPp3ue26+t2vhWy4lL2hg4mAAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDCcy43wCR0bSsnzTrAIBEIA7WdD2jxC3tCrK6TOdiEfbIN64m+UN7Velz4y0LRra5jn2U1CDClacwIpiBYuDp5ymPKO+ZqUGE0WEf20="}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{"EMMWJY8ZLcK/9FOj3iCpng=="}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"kms_cmk_id":"arn:aws:kms:us-east-1:172259396726:key/a22a4b30-79f4-4b3d-bab4-a26d327a231b"}`}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSWrap}, - http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{strings.Join([]string{s3crypto.AESCBC, s3crypto.AESCBCPadder.Name()}, "/")}, - }, - Body: ioutil.NopCloser(bytes.NewBuffer(b)), - } - out.Metadata = make(map[string]string) - out.Metadata["x-amz-wrap-alg"] = s3crypto.KMSWrap - }) - _, err := req.Send(context.Background()) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - b, err := ioutil.ReadAll(out.Body) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - expected, err := hex.DecodeString("a6ccd3482f5ce25c9ddeb69437cd0acbc0bdda2ef8696d90781de2b35704543529871b2032e68ef1c5baed1769aba8d420d1aca181341b49b8b3587a6580cdf1d809c68f06735f7735c16691f4b70c967d68fc08195b81ad71bcc4df452fd0a5799c1e1234f92f1cd929fc072167ccf9f2ac85b93170932b32") - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - if !bytes.Equal(expected, b) { - t.Error("expected bytes to be equivalent") - } -} - -func TestGetObjectWithContext(t *testing.T) { - c := s3crypto.NewDecryptionClient(unit.Config()) - - ctx := &awstesting.FakeContext{DoneCh: make(chan struct{})} - ctx.Error = fmt.Errorf("context canceled") - close(ctx.DoneCh) - - input := s3.GetObjectInput{ - Key: aws.String("test"), - Bucket: aws.String("test"), - } - _, err := c.GetObjectWithContext(ctx, &input) - if err == nil { - t.Fatalf("expected error, did not get one") - } - - var rc *aws.RequestCanceledError - if !errors.As(err, &rc) { - t.Fatalf("expect %T error, got %v", rc, err) - } - if e, a := "canceled", rc.Err.Error(); !strings.Contains(a, e) { - t.Errorf("expect %v to be in, %v", e, a) - } -} diff --git a/service/s3/s3crypto/doc.go b/service/s3/s3crypto/doc.go deleted file mode 100644 index 971cbc1a78f..00000000000 --- a/service/s3/s3crypto/doc.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -Package s3crypto is deprecated and may be removed from the future versions of the SDK. - -Package s3crypto provides encryption to S3 using KMS and AES GCM. - -Keyproviders are interfaces that handle masterkeys. Masterkeys are used to encrypt and decrypt the randomly -generated cipher keys. The SDK currently uses KMS to do this. A user does not need to provide a master key -since all that information is hidden in KMS. - -Modes are interfaces that handle content encryption and decryption. It is an abstraction layer that instantiates -the ciphers. If content is being encrypted we generate the key and iv of the cipher. For decryption, we use the -metadata stored either on the object or an instruction file object to decrypt the contents. - -Ciphers are interfaces that handle encryption and decryption of data. This may be key wrap ciphers or content -ciphers. - -Creating an S3 cryptography client - - cmkID := "" - cfg, err := external.LoadDefaultAWSConfig() - - // Create the KeyProvider - handler := s3crypto.NewKMSKeyGenerator(kms.New(cfg), cmkID) - - // Create an encryption and decryption client - // We need to pass the config here so S3 can use it. In addition, any decryption that - // occurs will use the KMS client. - svc := s3crypto.NewEncryptionClient(cfg, s3crypto.AESGCMContentCipherBuilder(handler)) - svc := s3crypto.NewDecryptionClient(cfg) - -Configuration of the S3 cryptography client - - cfg := s3crypto.EncryptionConfig{ - // Save instruction files to separate objects - SaveStrategy: NewS3SaveStrategy(cfg, ""), - // Change instruction file suffix to .example - InstructionFileSuffix: ".example", - // Set temp folder path - TempFolderPath: "/path/to/tmp/folder/", - // Any content less than the minimum file size will use memory - // instead of writing the contents to a temp file. - MinFileSize: int64(1024 * 1024 * 1024), - } - -The default SaveStrategy is to the object's header. - -The InstructionFileSuffix defaults to .instruction. Careful here though, if you do this, be sure you know -what that suffix is in grabbing data. All requests will look for fooKey.example instead of fooKey.instruction. -This suffix only affects gets and not puts. Put uses the keyprovider's suffix. - -Registration of new wrap or cek algorithms are also supported by the SDK. Let's say we want to support `AES Wrap` -and `AES CTR`. Let's assume we have already defined the functionality. - - svc := s3crypto.NewDecryptionClient(sess) - svc.WrapRegistry["AESWrap"] = NewAESWrap - svc.CEKRegistry["AES/CTR/NoPadding"] = NewAESCTR - -We have now registered these new algorithms to the decryption client. When the client calls `GetObject` and sees -the wrap as `AESWrap` then it'll use that wrap algorithm. This is also true for `AES/CTR/NoPadding`. - -For encryption adding a custom content cipher builder and key handler will allow for encryption of custom -defined ciphers. - - // Our wrap algorithm, AESWrap - handler := NewAESWrap(key, iv) - // Our content cipher builder, AESCTRContentCipherBuilder - svc := s3crypto.NewEncryptionClient(sess, NewAESCTRContentCipherBuilder(handler)) -*/ -package s3crypto diff --git a/service/s3/s3crypto/encryption_client.go b/service/s3/s3crypto/encryption_client.go deleted file mode 100644 index 4581488e4df..00000000000 --- a/service/s3/s3crypto/encryption_client.go +++ /dev/null @@ -1,153 +0,0 @@ -package s3crypto - -import ( - "context" - "encoding/hex" - "io" - - "github.com/aws/aws-sdk-go-v2/aws" - request "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/aws/aws-sdk-go-v2/service/s3/s3iface" -) - -// DefaultMinFileSize is used to check whether we want to write to a temp file -// or store the data in memory. -const DefaultMinFileSize = 1024 * 512 * 5 - -// EncryptionClient is an S3 crypto client. By default the SDK will use Authentication mode which -// will use KMS for key wrapping and AES GCM for content encryption. -// AES GCM will load all data into memory. However, the rest of the content algorithms -// do not load the entire contents into memory. -type EncryptionClient struct { - S3Client s3iface.ClientAPI - ContentCipherBuilder ContentCipherBuilder - // SaveStrategy will dictate where the envelope is saved. - // - // Defaults to the object's metadata - SaveStrategy SaveStrategy - // TempFolderPath is used to store temp files when calling PutObject. - // Temporary files are needed to compute the X-Amz-Content-Sha256 header. - TempFolderPath string - // MinFileSize is the minimum size for the content to write to a - // temporary file instead of using memory. - MinFileSize int64 -} - -// NewEncryptionClient instantiates a new S3 crypto client -// -// Example: -// cmkID := "arn:aws:kms:region:000000000000:key/00000000-0000-0000-0000-000000000000" -// cfg, err := external.LoadDefaultAWSConfig() -// handler := s3crypto.NewKMSKeyGenerator(kms.New(cfg), cmkID) -// svc := s3crypto.New(cfg, s3crypto.AESGCMContentCipherBuilder(handler)) -func NewEncryptionClient(cfg aws.Config, builder ContentCipherBuilder, options ...func(*EncryptionClient)) *EncryptionClient { - client := &EncryptionClient{ - S3Client: s3.New(cfg), - ContentCipherBuilder: builder, - SaveStrategy: HeaderV2SaveStrategy{}, - MinFileSize: DefaultMinFileSize, - } - - for _, option := range options { - option(client) - } - - return client -} - -// PutObjectRequest creates a temp file to encrypt the contents into. It then streams -// that data to S3. -// -// Example: -// cfg, err := external.LoadDefaultAWSConfig() -// svc := s3crypto.New(cfg, s3crypto.AESGCMContentCipherBuilder(handler)) -// req := svc.PutObjectRequest(&s3.PutObjectInput { -// Key: aws.String("testKey"), -// Bucket: aws.String("testBucket"), -// Body: strings.NewReader("test data"), -// }) -// err := req.Send(context.Background()) -func (c *EncryptionClient) PutObjectRequest(input *s3.PutObjectInput) s3.PutObjectRequest { - req := c.S3Client.PutObjectRequest(input) - - // Get Size of file - n, err := aws.SeekerLen(input.Body) - if err != nil { - req.Error = err - return req - } - - dst, err := getWriterStore(req.Request, c.TempFolderPath, n >= c.MinFileSize) - if err != nil { - req.Error = err - return req - } - - encryptor, err := c.ContentCipherBuilder.ContentCipher() - req.Handlers.Build.PushFront(func(r *request.Request) { - if err != nil { - r.Error = err - return - } - - md5 := newMD5Reader(input.Body) - sha := newSHA256Writer(dst) - reader, err := encryptor.EncryptContents(md5) - if err != nil { - r.Error = err - return - } - - _, err = io.Copy(sha, reader) - if err != nil { - r.Error = err - return - } - - data := encryptor.GetCipherData() - env, err := encodeMeta(md5, data) - if err != nil { - r.Error = err - return - } - - shaHex := hex.EncodeToString(sha.GetValue()) - req.HTTPRequest.Header.Set("X-Amz-Content-Sha256", shaHex) - - dst.Seek(0, io.SeekStart) - input.Body = dst - - err = c.SaveStrategy.Save(env, r) - r.Error = err - }) - - return req -} - -// PutObject is a wrapper for PutObjectRequest -func (c *EncryptionClient) PutObject(input *s3.PutObjectInput) (*s3.PutObjectOutput, error) { - req := c.PutObjectRequest(input) - resp, err := req.Send(context.Background()) - if err != nil { - return nil, err - } - return resp.PutObjectOutput, nil -} - -// PutObjectWithContext is a wrapper for PutObjectRequest with the additional -// context, and request options support. -// -// PutObjectWithContext is the same as PutObject with the additional support for -// Context input parameters. The Context must not be nil. A nil Context will -// cause a panic. Use the Context to add deadlining, timeouts, ect. In the future -// this may create sub-contexts for individual underlying requests. -func (c *EncryptionClient) PutObjectWithContext(ctx context.Context, input *s3.PutObjectInput, opts ...request.Option) (*s3.PutObjectOutput, error) { - req := c.PutObjectRequest(input) - req.ApplyOptions(opts...) - resp, err := req.Send(ctx) - if err != nil { - return nil, err - } - return resp.PutObjectOutput, nil -} diff --git a/service/s3/s3crypto/encryption_client_test.go b/service/s3/s3crypto/encryption_client_test.go deleted file mode 100644 index 73d79bdffe9..00000000000 --- a/service/s3/s3crypto/encryption_client_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package s3crypto_test - -import ( - "bytes" - "context" - "errors" - "fmt" - "io/ioutil" - "net/http" - "strings" - "testing" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/internal/awstesting" - "github.com/aws/aws-sdk-go-v2/internal/awstesting/unit" - "github.com/aws/aws-sdk-go-v2/service/kms" - "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/aws/aws-sdk-go-v2/service/s3/s3crypto" -) - -func TestDefaultConfigValues(t *testing.T) { - cfg := unit.Config() - cfg.Retryer = aws.NoOpRetryer{} - cfg.Region = "us-west-2" - - svc := kms.New(cfg) - handler := s3crypto.NewKMSKeyGenerator(svc, "testid") - - c := s3crypto.NewEncryptionClient(cfg, s3crypto.AESGCMContentCipherBuilder(handler)) - c.S3Client.(*s3.Client).ForcePathStyle = true - - if c == nil { - t.Error("expected non-vil client value") - } - if c.ContentCipherBuilder == nil { - t.Error("expected non-vil content cipher builder value") - } - if c.SaveStrategy == nil { - t.Error("expected non-vil save strategy value") - } -} - -func TestPutObject(t *testing.T) { - size := 1024 * 1024 - data := make([]byte, size) - expected := bytes.Repeat([]byte{1}, size) - generator := mockGenerator{} - cb := mockCipherBuilder{generator} - - cfg := unit.Config() - cfg.Retryer = aws.NoOpRetryer{} - cfg.Region = "us-west-2" - - c := s3crypto.NewEncryptionClient(cfg, cb) - if c == nil { - t.Fatalf("failed to create a new S3 crypto encryption client") - } - - c.S3Client.(*s3.Client).ForcePathStyle = true - input := &s3.PutObjectInput{ - Key: aws.String("test"), - Bucket: aws.String("test"), - Body: bytes.NewReader(data), - } - req := c.PutObjectRequest(input) - req.Handlers.Send.Clear() - req.Handlers.Send.PushBack(func(r *aws.Request) { - r.Error = errors.New("stop") - r.HTTPResponse = &http.Response{ - StatusCode: 200, - } - }) - _, err := req.Send(context.Background()) - if e, a := "stop", err.Error(); e != a { - t.Errorf("expected %s error, but received %s", e, a) - } - b, err := ioutil.ReadAll(req.HTTPRequest.Body) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - if !bytes.Equal(expected, b) { - t.Error("expected bytes to be equivalent, but received otherwise") - } -} - -func TestPutObjectWithContext(t *testing.T) { - generator := mockGenerator{} - cb := mockCipherBuilder{generator} - - c := s3crypto.NewEncryptionClient(unit.Config(), cb) - - ctx := &awstesting.FakeContext{DoneCh: make(chan struct{})} - ctx.Error = fmt.Errorf("context canceled") - close(ctx.DoneCh) - - input := s3.PutObjectInput{ - Bucket: aws.String("test"), - Key: aws.String("test"), - Body: bytes.NewReader([]byte{}), - } - _, err := c.PutObjectWithContext(ctx, &input) - if err == nil { - t.Fatalf("expected error, did not get one") - } - - var rc *aws.RequestCanceledError - if !errors.As(err, &rc) { - t.Fatalf("expect %T error, got %v", rc, err) - } - if e, a := "canceled", rc.Err.Error(); !strings.Contains(a, e) { - t.Errorf("expect %v to be in, %v", e, a) - } -} diff --git a/service/s3/s3crypto/envelope.go b/service/s3/s3crypto/envelope.go deleted file mode 100644 index 6e1e70a4de6..00000000000 --- a/service/s3/s3crypto/envelope.go +++ /dev/null @@ -1,37 +0,0 @@ -package s3crypto - -// DefaultInstructionKeySuffix is appended to the end of the instruction file key when -// grabbing or saving to S3 -const DefaultInstructionKeySuffix = ".instruction" - -const ( - metaHeader = "x-amz-meta" - keyV1Header = "x-amz-key" - keyV2Header = keyV1Header + "-v2" - ivHeader = "x-amz-iv" - matDescHeader = "x-amz-matdesc" - cekAlgorithmHeader = "x-amz-cek-alg" - wrapAlgorithmHeader = "x-amz-wrap-alg" - tagLengthHeader = "x-amz-tag-len" - unencryptedMD5Header = "x-amz-unencrypted-content-md5" - unencryptedContentLengthHeader = "x-amz-unencrypted-content-length" -) - -// Envelope encryption starts off by generating a random symmetric key using -// AES GCM. The SDK generates a random IV based off the encryption cipher -// chosen. The master key that was provided, whether by the user or KMS, will be used -// to encrypt the randomly generated symmetric key and base64 encode the iv. This will -// allow for decryption of that same data later. -type Envelope struct { - // IV is the randomly generated IV base64 encoded. - IV string `json:"x-amz-iv"` - // CipherKey is the randomly generated cipher key. - CipherKey string `json:"x-amz-key-v2"` - // MaterialDesc is a description to distinguish from other envelopes. - MatDesc string `json:"x-amz-matdesc"` - WrapAlg string `json:"x-amz-wrap-alg"` - CEKAlg string `json:"x-amz-cek-alg"` - TagLen string `json:"x-amz-tag-len"` - UnencryptedMD5 string `json:"x-amz-unencrypted-content-md5"` - UnencryptedContentLen string `json:"x-amz-unencrypted-content-length"` -} diff --git a/service/s3/s3crypto/hash_io.go b/service/s3/s3crypto/hash_io.go deleted file mode 100644 index cda54e63452..00000000000 --- a/service/s3/s3crypto/hash_io.go +++ /dev/null @@ -1,61 +0,0 @@ -package s3crypto - -import ( - "crypto/md5" - "crypto/sha256" - "hash" - "io" -) - -// hashReader is used for calculating SHA256 when following the sigv4 specification. -// Additionally this used for calculating the unencrypted MD5. -type hashReader interface { - GetValue() []byte - GetContentLength() int64 -} - -type sha256Writer struct { - sha256 []byte - hash hash.Hash - out io.Writer -} - -func newSHA256Writer(f io.Writer) *sha256Writer { - return &sha256Writer{hash: sha256.New(), out: f} -} -func (r *sha256Writer) Write(b []byte) (int, error) { - r.hash.Write(b) - return r.out.Write(b) -} - -func (r *sha256Writer) GetValue() []byte { - return r.hash.Sum(nil) -} - -type md5Reader struct { - contentLength int64 - hash hash.Hash - body io.Reader -} - -func newMD5Reader(body io.Reader) *md5Reader { - return &md5Reader{hash: md5.New(), body: body} -} - -func (w *md5Reader) Read(b []byte) (int, error) { - n, err := w.body.Read(b) - if err != nil && err != io.EOF { - return n, err - } - w.contentLength += int64(n) - w.hash.Write(b[:n]) - return n, err -} - -func (w *md5Reader) GetValue() []byte { - return w.hash.Sum(nil) -} - -func (w *md5Reader) GetContentLength() int64 { - return w.contentLength -} diff --git a/service/s3/s3crypto/hash_io_test.go b/service/s3/s3crypto/hash_io_test.go deleted file mode 100644 index a27daa34fa9..00000000000 --- a/service/s3/s3crypto/hash_io_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package s3crypto - -import ( - "bytes" - "encoding/hex" - "testing" -) - -// From Go stdlib encoding/sha256 test cases -func TestSHA256(t *testing.T) { - sha := newSHA256Writer(nil) - expected, _ := hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") - b := sha.GetValue() - - if !bytes.Equal(expected, b) { - t.Errorf("expected equivalent sha values, but received otherwise") - } -} - -func TestSHA256_Case2(t *testing.T) { - sha := newSHA256Writer(bytes.NewBuffer([]byte{})) - sha.Write([]byte("hello")) - expected, _ := hex.DecodeString("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824") - b := sha.GetValue() - - if !bytes.Equal(expected, b) { - t.Errorf("expected equivalent sha values, but received otherwise") - } -} diff --git a/service/s3/s3crypto/helper.go b/service/s3/s3crypto/helper.go deleted file mode 100644 index 39d0e58a253..00000000000 --- a/service/s3/s3crypto/helper.go +++ /dev/null @@ -1,70 +0,0 @@ -package s3crypto - -import ( - "errors" - "io" - "io/ioutil" - "os" - "path/filepath" - - request "github.com/aws/aws-sdk-go-v2/aws" -) - -func getWriterStore(req *request.Request, path string, useTempFile bool) (io.ReadWriteSeeker, error) { - if !useTempFile { - return &bytesReadWriteSeeker{}, nil - } - // Create temp file to be used later for calculating the SHA256 header - f, err := ioutil.TempFile(path, "") - if err != nil { - return nil, err - } - - req.Handlers.Send.PushBack(func(r *request.Request) { - // Close the temp file and cleanup - f.Close() - fpath := filepath.Join(path, f.Name()) - os.Remove(fpath) - }) - return f, nil -} - -type bytesReadWriteSeeker struct { - buf []byte - i int64 -} - -// Copied from Go stdlib bytes.Reader -func (ws *bytesReadWriteSeeker) Read(b []byte) (int, error) { - if ws.i >= int64(len(ws.buf)) { - return 0, io.EOF - } - n := copy(b, ws.buf[ws.i:]) - ws.i += int64(n) - return n, nil -} - -func (ws *bytesReadWriteSeeker) Write(b []byte) (int, error) { - ws.buf = append(ws.buf, b...) - return len(b), nil -} - -// Copied from Go stdlib bytes.Reader -func (ws *bytesReadWriteSeeker) Seek(offset int64, whence int) (int64, error) { - var abs int64 - switch whence { - case 0: - abs = offset - case 1: - abs = int64(ws.i) + offset - case 2: - abs = int64(len(ws.buf)) + offset - default: - return 0, errors.New("bytes.Reader.Seek: invalid whence") - } - if abs < 0 { - return 0, errors.New("bytes.Reader.Seek: negative position") - } - ws.i = abs - return abs, nil -} diff --git a/service/s3/s3crypto/helper_test.go b/service/s3/s3crypto/helper_test.go deleted file mode 100644 index f981992be71..00000000000 --- a/service/s3/s3crypto/helper_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package s3crypto - -import ( - "bytes" - "io" - "testing" -) - -func TestBytesReadWriteSeeker_Read(t *testing.T) { - b := &bytesReadWriteSeeker{[]byte{1, 2, 3}, 0} - expected := []byte{1, 2, 3} - buf := make([]byte, 3) - n, err := b.Read(buf) - - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - if e, a := 3, n; e != a { - t.Errorf("expected %d, but received %d", e, a) - } - - if !bytes.Equal(expected, buf) { - t.Error("expected equivalent byte slices, but received otherwise") - } -} - -func TestBytesReadWriteSeeker_Write(t *testing.T) { - b := &bytesReadWriteSeeker{} - expected := []byte{1, 2, 3} - buf := make([]byte, 3) - n, err := b.Write([]byte{1, 2, 3}) - - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - if e, a := 3, n; e != a { - t.Errorf("expected %d, but received %d", e, a) - } - - n, err = b.Read(buf) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - if e, a := 3, n; e != a { - t.Errorf("expected %d, but received %d", e, a) - } - - if !bytes.Equal(expected, buf) { - t.Error("expected equivalent byte slices, but received otherwise") - } -} - -func TestBytesReadWriteSeeker_Seek(t *testing.T) { - b := &bytesReadWriteSeeker{[]byte{1, 2, 3}, 0} - expected := []byte{2, 3} - m, err := b.Seek(1, io.SeekStart) - - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - if e, a := 1, int(m); e != a { - t.Errorf("expected %d, but received %d", e, a) - } - - buf := make([]byte, 3) - n, err := b.Read(buf) - - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - if e, a := 2, n; e != a { - t.Errorf("expected %d, but received %d", e, a) - } - - if !bytes.Equal(expected, buf[:n]) { - t.Error("expected equivalent byte slices, but received otherwise") - } -} diff --git a/service/s3/s3crypto/key_handler.go b/service/s3/s3crypto/key_handler.go deleted file mode 100644 index d17018a1762..00000000000 --- a/service/s3/s3crypto/key_handler.go +++ /dev/null @@ -1,21 +0,0 @@ -package s3crypto - -import "crypto/rand" - -// CipherDataGenerator handles generating proper key and IVs of proper size for the -// content cipher. CipherDataGenerator will also encrypt the key and store it in -// the CipherData. -type CipherDataGenerator interface { - GenerateCipherData(int, int) (CipherData, error) -} - -// CipherDataDecrypter is a handler to decrypt keys from the envelope. -type CipherDataDecrypter interface { - DecryptKey([]byte) ([]byte, error) -} - -func generateBytes(n int) []byte { - b := make([]byte, n) - rand.Read(b) - return b -} diff --git a/service/s3/s3crypto/key_handler_test.go b/service/s3/s3crypto/key_handler_test.go deleted file mode 100644 index 8e0e5ea426a..00000000000 --- a/service/s3/s3crypto/key_handler_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package s3crypto - -import ( - "testing" -) - -func TestGenerateBytes(t *testing.T) { - b := generateBytes(5) - if e, a := 5, len(b); e != a { - t.Errorf("expected %d, but received %d", e, a) - } - b = generateBytes(0) - if e, a := 0, len(b); e != a { - t.Errorf("expected %d, but received %d", e, a) - } - b = generateBytes(1024) - if e, a := 1024, len(b); e != a { - t.Errorf("expected %d, but received %d", e, a) - } -} diff --git a/service/s3/s3crypto/kms_key_handler.go b/service/s3/s3crypto/kms_key_handler.go deleted file mode 100644 index 8be65d35f17..00000000000 --- a/service/s3/s3crypto/kms_key_handler.go +++ /dev/null @@ -1,117 +0,0 @@ -package s3crypto - -import ( - "context" - - "github.com/aws/aws-sdk-go-v2/aws/awserr" - "github.com/aws/aws-sdk-go-v2/service/kms" - "github.com/aws/aws-sdk-go-v2/service/kms/kmsiface" -) - -const ( - // KMSWrap is a constant used during decryption to build a KMS key handler. - KMSWrap = "kms" -) - -// kmsKeyHandler will make calls to KMS to get the masterkey -type kmsKeyHandler struct { - kms kmsiface.ClientAPI - cmkID string - - CipherData -} - -// NewKMSKeyGenerator builds a new KMS key provider using the customer key ID and material -// description. -// -// Example: -// cfg, err := external.LoadDefaultAWSConfig() -// cmkID := "arn to key" -// matdesc := s3crypto.MaterialDescription{} -// handler := s3crypto.NewKMSKeyGenerator(kms.New(cfg), cmkID) -func NewKMSKeyGenerator(kmsClient kmsiface.ClientAPI, cmkID string) CipherDataGenerator { - return NewKMSKeyGeneratorWithMatDesc(kmsClient, cmkID, MaterialDescription{}) -} - -// NewKMSKeyGeneratorWithMatDesc builds a new KMS key provider using the customer key ID and material -// description. -// -// Example: -// cfg, err := external.LoadDefaultAWSConfig() -// cmkID := "arn to key" -// matdesc := s3crypto.MaterialDescription{} -// handler, err := s3crypto.NewKMSKeyGeneratorWithMatDesc(kms.New(cfg), cmkID, matdesc) -func NewKMSKeyGeneratorWithMatDesc(kmsClient kmsiface.ClientAPI, cmkID string, matdesc MaterialDescription) CipherDataGenerator { - if matdesc == nil { - matdesc = MaterialDescription{} - } - matdesc["kms_cmk_id"] = cmkID - - // These values are read only making them thread safe - kp := &kmsKeyHandler{ - kms: kmsClient, - cmkID: cmkID, - } - // These values are read only making them thread safe - kp.CipherData.WrapAlgorithm = KMSWrap - kp.CipherData.MaterialDescription = matdesc - return kp -} - -// decryptHandler initializes a KMS keyprovider with a material description. This -// is used with Decrypting kms content, due to the cmkID being in the material description. -func (kp kmsKeyHandler) decryptHandler(env Envelope) (CipherDataDecrypter, error) { - m := MaterialDescription{} - err := m.decodeDescription([]byte(env.MatDesc)) - if err != nil { - return nil, err - } - - cmkID, ok := m["kms_cmk_id"] - if !ok { - return nil, awserr.New("MissingCMKIDError", "Material description is missing CMK ID", nil) - } - - kp.CipherData.MaterialDescription = m - kp.cmkID = cmkID - kp.WrapAlgorithm = KMSWrap - return &kp, nil -} - -// DecryptKey makes a call to KMS to decrypt the key. -func (kp *kmsKeyHandler) DecryptKey(key []byte) ([]byte, error) { - req := kp.kms.DecryptRequest(&kms.DecryptInput{ - EncryptionContext: map[string]string(kp.CipherData.MaterialDescription), - CiphertextBlob: key, - GrantTokens: []string{}, - }) - resp, err := req.Send(context.Background()) - if err != nil { - return nil, err - } - return resp.Plaintext, nil -} - -// GenerateCipherData makes a call to KMS to generate a data key, Upon making -// the call, it also sets the encrypted key. -func (kp *kmsKeyHandler) GenerateCipherData(keySize, ivSize int) (CipherData, error) { - req := kp.kms.GenerateDataKeyRequest(&kms.GenerateDataKeyInput{ - EncryptionContext: kp.CipherData.MaterialDescription, - KeyId: &kp.cmkID, - KeySpec: kms.DataKeySpecAes256, - }) - resp, err := req.Send(context.Background()) - if err != nil { - return CipherData{}, err - } - - iv := generateBytes(ivSize) - cd := CipherData{ - Key: resp.Plaintext, - IV: iv, - WrapAlgorithm: KMSWrap, - MaterialDescription: kp.CipherData.MaterialDescription, - EncryptedKey: resp.CiphertextBlob, - } - return cd, nil -} diff --git a/service/s3/s3crypto/kms_key_handler_test.go b/service/s3/s3crypto/kms_key_handler_test.go deleted file mode 100644 index 9cf33a407c7..00000000000 --- a/service/s3/s3crypto/kms_key_handler_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package s3crypto - -import ( - "bytes" - "encoding/base64" - "encoding/hex" - "fmt" - "net/http" - "net/http/httptest" - "reflect" - "testing" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/internal/awstesting/unit" - "github.com/aws/aws-sdk-go-v2/service/kms" -) - -func TestBuildKMSEncryptHandler(t *testing.T) { - svc := kms.New(unit.Config()) - handler := NewKMSKeyGenerator(svc, "testid") - if handler == nil { - t.Error("expected non-nil handler") - } -} - -func TestBuildKMSEncryptHandlerWithMatDesc(t *testing.T) { - svc := kms.New(unit.Config()) - handler := NewKMSKeyGeneratorWithMatDesc(svc, "testid", MaterialDescription{ - "Testing": "123", - }) - if handler == nil { - t.Error("expected non-nil handler") - } - - kmsHandler := handler.(*kmsKeyHandler) - expected := MaterialDescription{ - "kms_cmk_id": "testid", - "Testing": "123", - } - - if !reflect.DeepEqual(expected, kmsHandler.CipherData.MaterialDescription) { - t.Errorf("expected %v, but received %v", expected, kmsHandler.CipherData.MaterialDescription) - } -} - -func TestKMSGenerateCipherData(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, `{"CiphertextBlob":"AQEDAHhqBCCY1MSimw8gOGcUma79cn4ANvTtQyv9iuBdbcEF1QAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDJ6IcN5E4wVbk38MNAIBEIA7oF1E3lS7FY9DkoxPc/UmJsEwHzL82zMqoLwXIvi8LQHr8If4Lv6zKqY8u0+JRgSVoqCvZDx3p8Cn6nM=","KeyId":"arn:aws:kms:us-west-2:042062605278:key/c80a5cdb-8d09-4f9f-89ee-df01b2e3870a","Plaintext":"6tmyz9JLBE2yIuU7iXpArqpDVle172WSmxjcO6GNT7E="}`) - })) - - cfg := unit.Config() - cfg.Retryer = aws.NoOpRetryer{} - cfg.EndpointResolver = aws.ResolveWithEndpointURL(ts.URL) - cfg.Region = "us-west-2" - - svc := kms.New(cfg) - handler := NewKMSKeyGenerator(svc, "testid") - - keySize := 32 - ivSize := 16 - - cd, err := handler.GenerateCipherData(keySize, ivSize) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - if keySize != len(cd.Key) { - t.Errorf("expected %d, but received %d", keySize, len(cd.Key)) - } - if ivSize != len(cd.IV) { - t.Errorf("expected %d, but received %d", ivSize, len(cd.IV)) - } -} - -func TestKMSDecrypt(t *testing.T) { - key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") - keyB64 := base64.URLEncoding.EncodeToString(key) - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) - })) - - cfg := unit.Config() - cfg.Retryer = aws.NoOpRetryer{} - cfg.EndpointResolver = aws.ResolveWithEndpointURL(ts.URL) - cfg.Region = "us-west-2" - - handler, err := (kmsKeyHandler{kms: kms.New(cfg)}).decryptHandler(Envelope{MatDesc: `{"kms_cmk_id":"test"}`}) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - plaintextKey, err := handler.DecryptKey([]byte{1, 2, 3, 4}) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - if !bytes.Equal(key, plaintextKey) { - t.Errorf("expected %v, but received %v", key, plaintextKey) - } -} - -func TestKMSDecryptBadJSON(t *testing.T) { - key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") - keyB64 := base64.URLEncoding.EncodeToString(key) - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) - })) - - cfg := unit.Config() - cfg.Retryer = aws.NoOpRetryer{} - cfg.EndpointResolver = aws.ResolveWithEndpointURL(ts.URL) - cfg.Region = "us-west-2" - - _, err := (kmsKeyHandler{kms: kms.New(cfg)}).decryptHandler(Envelope{MatDesc: `{"kms_cmk_id":"test"`}) - if err == nil { - t.Errorf("expected error, but received none") - } -} diff --git a/service/s3/s3crypto/mat_desc.go b/service/s3/s3crypto/mat_desc.go deleted file mode 100644 index 2049c1b628d..00000000000 --- a/service/s3/s3crypto/mat_desc.go +++ /dev/null @@ -1,18 +0,0 @@ -package s3crypto - -import ( - "encoding/json" -) - -// MaterialDescription is used to identify how and what master -// key has been used. -type MaterialDescription map[string]string - -func (md *MaterialDescription) encodeDescription() ([]byte, error) { - v, err := json.Marshal(&md) - return v, err -} - -func (md *MaterialDescription) decodeDescription(b []byte) error { - return json.Unmarshal(b, &md) -} diff --git a/service/s3/s3crypto/mat_desc_test.go b/service/s3/s3crypto/mat_desc_test.go deleted file mode 100644 index 3eb8a3c3a7f..00000000000 --- a/service/s3/s3crypto/mat_desc_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package s3crypto - -import ( - "reflect" - "testing" -) - -func TestEncodeMaterialDescription(t *testing.T) { - md := MaterialDescription{} - md["foo"] = "bar" - b, err := md.encodeDescription() - expected := `{"foo":"bar"}` - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - if expected != string(b) { - t.Errorf("expected %s, but received %s", expected, string(b)) - } -} -func TestDecodeMaterialDescription(t *testing.T) { - md := MaterialDescription{} - json := `{"foo":"bar"}` - err := md.decodeDescription([]byte(json)) - expected := MaterialDescription{ - "foo": "bar", - } - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - if !reflect.DeepEqual(expected, md) { - t.Error("expected material description to be equivalent, but received otherwise") - } -} diff --git a/service/s3/s3crypto/mock_test.go b/service/s3/s3crypto/mock_test.go deleted file mode 100644 index 97a31c49912..00000000000 --- a/service/s3/s3crypto/mock_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package s3crypto_test - -import ( - "bytes" - "io" - "io/ioutil" - - "github.com/aws/aws-sdk-go-v2/service/s3/s3crypto" -) - -type mockGenerator struct { -} - -func (m mockGenerator) GenerateCipherData(keySize, ivSize int) (s3crypto.CipherData, error) { - cd := s3crypto.CipherData{ - Key: make([]byte, keySize), - IV: make([]byte, ivSize), - } - return cd, nil -} - -func (m mockGenerator) EncryptKey(key []byte) ([]byte, error) { - size := len(key) - b := bytes.Repeat([]byte{1}, size) - return b, nil -} - -func (m mockGenerator) DecryptKey(key []byte) ([]byte, error) { - return make([]byte, 16), nil - -} - -type mockCipherBuilder struct { - generator s3crypto.CipherDataGenerator -} - -func (builder mockCipherBuilder) ContentCipher() (s3crypto.ContentCipher, error) { - cd, err := builder.generator.GenerateCipherData(32, 16) - if err != nil { - return nil, err - } - return &mockContentCipher{cd}, nil -} - -type mockContentCipher struct { - cd s3crypto.CipherData -} - -func (cipher *mockContentCipher) GetCipherData() s3crypto.CipherData { - return cipher.cd -} - -func (cipher *mockContentCipher) EncryptContents(src io.Reader) (io.Reader, error) { - b, err := ioutil.ReadAll(src) - if err != nil { - return nil, err - } - size := len(b) - b = bytes.Repeat([]byte{1}, size) - return bytes.NewReader(b), nil -} - -func (cipher *mockContentCipher) DecryptContents(src io.ReadCloser) (io.ReadCloser, error) { - b, err := ioutil.ReadAll(src) - if err != nil { - return nil, err - } - size := len(b) - return ioutil.NopCloser(bytes.NewReader(make([]byte, size))), nil -} diff --git a/service/s3/s3crypto/padder.go b/service/s3/s3crypto/padder.go deleted file mode 100644 index 1e3564935c5..00000000000 --- a/service/s3/s3crypto/padder.go +++ /dev/null @@ -1,35 +0,0 @@ -package s3crypto - -// Padder handles padding of crypto data -type Padder interface { - // Pad will pad the byte array. - // The second parameter is NOT how many - // bytes to pad by, but how many bytes - // have been read prior to the padding. - // This allows for streamable padding. - Pad([]byte, int) ([]byte, error) - // Unpad will unpad the byte bytes. Unpad - // methods must be constant time. - Unpad([]byte) ([]byte, error) - // Name returns the name of the padder. - // This is used when decrypting on - // instantiating new padders. - Name() string -} - -// NoPadder does not pad anything -var NoPadder = Padder(noPadder{}) - -type noPadder struct{} - -func (padder noPadder) Pad(b []byte, n int) ([]byte, error) { - return b, nil -} - -func (padder noPadder) Unpad(b []byte) ([]byte, error) { - return b, nil -} - -func (padder noPadder) Name() string { - return "NoPadding" -} diff --git a/service/s3/s3crypto/pkcs7_padder.go b/service/s3/s3crypto/pkcs7_padder.go deleted file mode 100644 index ff7e6a2ff2a..00000000000 --- a/service/s3/s3crypto/pkcs7_padder.go +++ /dev/null @@ -1,80 +0,0 @@ -package s3crypto - -// Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// -// Portions Licensed under the MIT License. Copyright (c) 2016 Carl Jackson - -import ( - "bytes" - "crypto/subtle" - - "github.com/aws/aws-sdk-go-v2/aws/awserr" -) - -const ( - pkcs7MaxPaddingSize = 255 -) - -type pkcs7Padder struct { - blockSize int -} - -// NewPKCS7Padder follows the RFC 2315: https://www.ietf.org/rfc/rfc2315.txt -// PKCS7 padding is subject to side-channel attacks and timing attacks. For -// the most secure data, use an authenticated crypto algorithm. -func NewPKCS7Padder(blockSize int) Padder { - return pkcs7Padder{blockSize} -} - -var errPKCS7Padding = awserr.New("InvalidPadding", "invalid padding", nil) - -// Pad will pad the data relative to how many bytes have been read. -// Pad follows the PKCS7 standard. -func (padder pkcs7Padder) Pad(buf []byte, n int) ([]byte, error) { - if padder.blockSize < 1 || padder.blockSize > pkcs7MaxPaddingSize { - return nil, awserr.New("InvalidBlockSize", "block size must be between 1 and 255", nil) - } - size := padder.blockSize - (n % padder.blockSize) - pad := bytes.Repeat([]byte{byte(size)}, size) - buf = append(buf, pad...) - return buf, nil -} - -// Unpad will unpad the correct amount of bytes based off -// of the PKCS7 standard -func (padder pkcs7Padder) Unpad(buf []byte) ([]byte, error) { - if len(buf) == 0 { - return nil, errPKCS7Padding - } - - // Here be dragons. We're attempting to check the padding in constant - // time. The only piece of information here which is public is len(buf). - // This code is modeled loosely after tls1_cbc_remove_padding from - // OpenSSL. - padLen := buf[len(buf)-1] - toCheck := pkcs7MaxPaddingSize - good := 1 - if toCheck > len(buf) { - toCheck = len(buf) - } - for i := 0; i < toCheck; i++ { - b := buf[len(buf)-1-i] - - outOfRange := subtle.ConstantTimeLessOrEq(int(padLen), i) - equal := subtle.ConstantTimeByteEq(padLen, b) - good &= subtle.ConstantTimeSelect(outOfRange, 1, equal) - } - - good &= subtle.ConstantTimeLessOrEq(1, int(padLen)) - good &= subtle.ConstantTimeLessOrEq(int(padLen), len(buf)) - - if good != 1 { - return nil, errPKCS7Padding - } - - return buf[:len(buf)-int(padLen)], nil -} - -func (padder pkcs7Padder) Name() string { - return "PKCS7Padding" -} diff --git a/service/s3/s3crypto/pkcs7_padder_test.go b/service/s3/s3crypto/pkcs7_padder_test.go deleted file mode 100644 index 429b9287fbc..00000000000 --- a/service/s3/s3crypto/pkcs7_padder_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package s3crypto_test - -import ( - "bytes" - "fmt" - "testing" - - "github.com/aws/aws-sdk-go-v2/service/s3/s3crypto" -) - -func padTest(size int, t *testing.T) { - padder := s3crypto.NewPKCS7Padder(size) - for i := 0; i < size; i++ { - input := make([]byte, i) - expected := append(input, bytes.Repeat([]byte{byte(size - i)}, size-i)...) - b, err := padder.Pad(input, len(input)) - if err != nil { - t.Fatal("Expected error to be nil but received " + err.Error()) - } - if len(b) != len(expected) { - t.Fatal(fmt.Sprintf("Case %d: data is not of the same length", i)) - } - if bytes.Compare(b, expected) != 0 { - t.Fatal(fmt.Sprintf("Expected %v but got %v", expected, b)) - } - } -} - -func unpadTest(size int, t *testing.T) { - padder := s3crypto.NewPKCS7Padder(size) - for i := 0; i < size; i++ { - expected := make([]byte, i) - input := append(expected, bytes.Repeat([]byte{byte(size - i)}, size-i)...) - b, err := padder.Unpad(input) - if err != nil { - t.Fatal("Error received, was expecting nil: " + err.Error()) - } - if len(b) != len(expected) { - t.Fatal(fmt.Sprintf("Case %d: data is not of the same length", i)) - } - if bytes.Compare(b, expected) != 0 { - t.Fatal(fmt.Sprintf("Expected %v but got %v", expected, b)) - } - } -} - -func TestPKCS7Padding(t *testing.T) { - padTest(10, t) - padTest(16, t) - padTest(255, t) -} - -func TestPKCS7Unpadding(t *testing.T) { - unpadTest(10, t) - unpadTest(16, t) - unpadTest(255, t) -} diff --git a/service/s3/s3crypto/strategy.go b/service/s3/s3crypto/strategy.go deleted file mode 100644 index 8076e18a47c..00000000000 --- a/service/s3/s3crypto/strategy.go +++ /dev/null @@ -1,145 +0,0 @@ -package s3crypto - -import ( - "bytes" - "context" - "encoding/json" - "io/ioutil" - "net/http" - "strings" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/aws/awserr" - "github.com/aws/aws-sdk-go-v2/service/s3" -) - -// SaveStrategy is how the data's metadata wants to be saved -type SaveStrategy interface { - Save(Envelope, *aws.Request) error -} - -// S3SaveStrategy will save the metadata to a separate instruction file in S3 -type S3SaveStrategy struct { - Client *s3.Client - InstructionFileSuffix string -} - -// Save will save the envelope contents to s3. -func (strat S3SaveStrategy) Save(env Envelope, req *aws.Request) error { - input := req.Params.(*s3.PutObjectInput) - b, err := json.Marshal(env) - if err != nil { - return err - } - - instInput := s3.PutObjectInput{ - Bucket: input.Bucket, - Body: bytes.NewReader(b), - } - - if strat.InstructionFileSuffix == "" { - instInput.Key = aws.String(*input.Key + DefaultInstructionKeySuffix) - } else { - instInput.Key = aws.String(*input.Key + strat.InstructionFileSuffix) - } - - _, err = strat.Client.PutObjectRequest(&instInput).Send(context.Background()) - return err -} - -// HeaderV2SaveStrategy will save the metadata of the crypto contents to the header of -// the object. -type HeaderV2SaveStrategy struct{} - -// Save will save the envelope to the request's header. -func (strat HeaderV2SaveStrategy) Save(env Envelope, req *aws.Request) error { - input := req.Params.(*s3.PutObjectInput) - if input.Metadata == nil { - input.Metadata = map[string]string{} - } - - input.Metadata[http.CanonicalHeaderKey(keyV2Header)] = env.CipherKey - input.Metadata[http.CanonicalHeaderKey(ivHeader)] = env.IV - input.Metadata[http.CanonicalHeaderKey(matDescHeader)] = env.MatDesc - input.Metadata[http.CanonicalHeaderKey(wrapAlgorithmHeader)] = env.WrapAlg - input.Metadata[http.CanonicalHeaderKey(cekAlgorithmHeader)] = env.CEKAlg - input.Metadata[http.CanonicalHeaderKey(unencryptedMD5Header)] = env.UnencryptedMD5 - input.Metadata[http.CanonicalHeaderKey(unencryptedContentLengthHeader)] = env.UnencryptedContentLen - - if len(env.TagLen) > 0 { - input.Metadata[http.CanonicalHeaderKey(tagLengthHeader)] = env.TagLen - } - return nil -} - -// LoadStrategy ... -type LoadStrategy interface { - Load(*aws.Request) (Envelope, error) -} - -// S3LoadStrategy will load the instruction file from s3 -type S3LoadStrategy struct { - Client *s3.Client - InstructionFileSuffix string -} - -// Load from a given instruction file suffix -func (load S3LoadStrategy) Load(req *aws.Request) (Envelope, error) { - env := Envelope{} - if load.InstructionFileSuffix == "" { - load.InstructionFileSuffix = DefaultInstructionKeySuffix - } - - input := req.Params.(*s3.GetObjectInput) - out, err := load.Client.GetObjectRequest(&s3.GetObjectInput{ - Key: aws.String(strings.Join([]string{*input.Key, load.InstructionFileSuffix}, "")), - Bucket: input.Bucket, - }).Send(context.Background()) - if err != nil { - return env, err - } - - b, err := ioutil.ReadAll(out.Body) - if err != nil { - return env, err - } - err = json.Unmarshal(b, &env) - return env, err -} - -// HeaderV2LoadStrategy will load the envelope from the metadata -type HeaderV2LoadStrategy struct{} - -// Load from a given object's header -func (load HeaderV2LoadStrategy) Load(req *aws.Request) (Envelope, error) { - env := Envelope{} - env.CipherKey = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, keyV2Header}, "-")) - env.IV = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, ivHeader}, "-")) - env.MatDesc = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, matDescHeader}, "-")) - env.WrapAlg = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, wrapAlgorithmHeader}, "-")) - env.CEKAlg = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, cekAlgorithmHeader}, "-")) - env.TagLen = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, tagLengthHeader}, "-")) - env.UnencryptedMD5 = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, unencryptedMD5Header}, "-")) - env.UnencryptedContentLen = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, unencryptedContentLengthHeader}, "-")) - return env, nil -} - -type defaultV2LoadStrategy struct { - client *s3.Client - suffix string -} - -func (load defaultV2LoadStrategy) Load(req *aws.Request) (Envelope, error) { - if value := req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, keyV2Header}, "-")); value != "" { - strat := HeaderV2LoadStrategy{} - return strat.Load(req) - } else if value = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, keyV1Header}, "-")); value != "" { - return Envelope{}, awserr.New("V1NotSupportedError", "The AWS SDK for Go does not support version 1", nil) - } - - strat := S3LoadStrategy{ - Client: load.client, - InstructionFileSuffix: load.suffix, - } - return strat.Load(req) -} diff --git a/service/s3/s3crypto/strategy_test.go b/service/s3/s3crypto/strategy_test.go deleted file mode 100644 index 49fba0afef6..00000000000 --- a/service/s3/s3crypto/strategy_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package s3crypto_test - -import ( - "reflect" - "testing" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/aws/aws-sdk-go-v2/service/s3/s3crypto" -) - -func TestHeaderV2SaveStrategy(t *testing.T) { - cases := []struct { - env s3crypto.Envelope - expected map[string]string - }{ - { - s3crypto.Envelope{ - CipherKey: "Foo", - IV: "Bar", - MatDesc: "{}", - WrapAlg: s3crypto.KMSWrap, - CEKAlg: s3crypto.AESGCMNoPadding, - TagLen: "128", - UnencryptedMD5: "hello", - UnencryptedContentLen: "0", - }, - map[string]string{ - "X-Amz-Key-V2": "Foo", - "X-Amz-Iv": "Bar", - "X-Amz-Matdesc": "{}", - "X-Amz-Wrap-Alg": s3crypto.KMSWrap, - "X-Amz-Cek-Alg": s3crypto.AESGCMNoPadding, - "X-Amz-Tag-Len": "128", - "X-Amz-Unencrypted-Content-Md5": "hello", - "X-Amz-Unencrypted-Content-Length": "0", - }, - }, - { - s3crypto.Envelope{ - CipherKey: "Foo", - IV: "Bar", - MatDesc: "{}", - WrapAlg: s3crypto.KMSWrap, - CEKAlg: s3crypto.AESGCMNoPadding, - UnencryptedMD5: "hello", - UnencryptedContentLen: "0", - }, - map[string]string{ - "X-Amz-Key-V2": "Foo", - "X-Amz-Iv": "Bar", - "X-Amz-Matdesc": "{}", - "X-Amz-Wrap-Alg": s3crypto.KMSWrap, - "X-Amz-Cek-Alg": s3crypto.AESGCMNoPadding, - "X-Amz-Unencrypted-Content-Md5": "hello", - "X-Amz-Unencrypted-Content-Length": "0", - }, - }, - } - - for _, c := range cases { - params := &s3.PutObjectInput{} - req := &aws.Request{ - Params: params, - } - strat := s3crypto.HeaderV2SaveStrategy{} - err := strat.Save(c.env, req) - if err != nil { - t.Errorf("expected no error, but received %v", err) - } - - if !reflect.DeepEqual(c.expected, params.Metadata) { - t.Errorf("expected %v, but received %v", c.expected, params.Metadata) - } - } -}