1// +build go1.7 2 3package s3crypto 4 5import ( 6 "bytes" 7 "encoding/hex" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "net/http" 12 "net/http/httptest" 13 "reflect" 14 "strings" 15 "testing" 16 17 "github.com/aws/aws-sdk-go/aws" 18 "github.com/aws/aws-sdk-go/aws/request" 19 "github.com/aws/aws-sdk-go/aws/session" 20 "github.com/aws/aws-sdk-go/awstesting/unit" 21 "github.com/aws/aws-sdk-go/service/kms" 22 "github.com/aws/aws-sdk-go/service/s3" 23) 24 25func sessionWithLogCheck(message string) (*session.Session, *bool) { 26 gotWarning := false 27 28 u := unit.Session.Copy(&aws.Config{Logger: aws.LoggerFunc(func(i ...interface{}) { 29 if len(i) == 0 { 30 return 31 } 32 s, ok := i[0].(string) 33 if !ok { 34 return 35 } 36 if s == message { 37 gotWarning = true 38 } 39 })}) 40 41 return u, &gotWarning 42} 43 44func TestNewEncryptionClientV2(t *testing.T) { 45 tUnit, gotWarning := sessionWithLogCheck(customTypeWarningMessage) 46 47 mcb := AESGCMContentCipherBuilderV2(NewKMSContextKeyGenerator(nil, "id", nil)) 48 v2, err := NewEncryptionClientV2(tUnit, mcb) 49 if err != nil { 50 t.Fatalf("expected no error, got %v", err) 51 } 52 if v2 == nil { 53 t.Fatal("expected client to not be nil") 54 } 55 56 if *gotWarning { 57 t.Errorf("expected no warning for aws provided custom cipher builder") 58 } 59 60 if !reflect.DeepEqual(mcb, v2.options.ContentCipherBuilder) { 61 t.Errorf("content cipher builder did not match provided value") 62 } 63 64 _, ok := v2.options.SaveStrategy.(HeaderV2SaveStrategy) 65 if !ok { 66 t.Errorf("expected default save strategy to be s3 header strategy") 67 } 68 69 if v2.options.S3Client == nil { 70 t.Errorf("expected s3 client not be nil") 71 } 72 73 if e, a := DefaultMinFileSize, v2.options.MinFileSize; int64(e) != a { 74 t.Errorf("expected %v, got %v", e, a) 75 } 76 77 if e, a := "", v2.options.TempFolderPath; e != a { 78 t.Errorf("expected %v, got %v", e, a) 79 } 80} 81 82func TestNewEncryptionClientV2_NonDefaults(t *testing.T) { 83 tUnit, gotWarning := sessionWithLogCheck(customTypeWarningMessage) 84 85 s3Client := s3.New(tUnit) 86 87 mcb := mockCipherBuilderV2{} 88 v2, err := NewEncryptionClientV2(tUnit, nil, func(clientOptions *EncryptionClientOptions) { 89 clientOptions.S3Client = s3Client 90 clientOptions.ContentCipherBuilder = mcb 91 clientOptions.TempFolderPath = "/mock/path" 92 clientOptions.MinFileSize = 42 93 clientOptions.SaveStrategy = S3SaveStrategy{} 94 }) 95 if err != nil { 96 t.Fatalf("expected no error, got %v", err) 97 } 98 if v2 == nil { 99 t.Fatal("expected client to not be nil") 100 } 101 102 if !*gotWarning { 103 t.Errorf("expected warning for custom provided content cipher builder") 104 } 105 106 if !reflect.DeepEqual(mcb, v2.options.ContentCipherBuilder) { 107 t.Errorf("content cipher builder did not match provided value") 108 } 109 110 _, ok := v2.options.SaveStrategy.(S3SaveStrategy) 111 if !ok { 112 t.Errorf("expected default save strategy to be s3 header strategy") 113 } 114 115 if v2.options.S3Client != s3Client { 116 t.Errorf("expected s3 client not be nil") 117 } 118 119 if e, a := 42, v2.options.MinFileSize; int64(e) != a { 120 t.Errorf("expected %v, got %v", e, a) 121 } 122 123 if e, a := "/mock/path", v2.options.TempFolderPath; e != a { 124 t.Errorf("expected %v, got %v", e, a) 125 } 126} 127 128// cdgWithStaticTestIV is a test structure that wraps a CipherDataGeneratorWithCEKAlg and stubs in a static IV 129// so that encryption tests can be guaranteed to be consistent. 130type cdgWithStaticTestIV struct { 131 IV []byte 132 CipherDataGeneratorWithCEKAlg 133} 134 135// isAWSFixture will avoid the warning log message when doing tests that need to mock the IV 136func (k cdgWithStaticTestIV) isAWSFixture() bool { 137 return true 138} 139 140func (k cdgWithStaticTestIV) GenerateCipherDataWithCEKAlg(ctx aws.Context, keySize, ivSize int, cekAlg string) (CipherData, error) { 141 cipherData, err := k.CipherDataGeneratorWithCEKAlg.GenerateCipherDataWithCEKAlg(ctx, keySize, ivSize, cekAlg) 142 if err == nil { 143 cipherData.IV = k.IV 144 } 145 return cipherData, err 146} 147 148func TestEncryptionClientV2_PutObject_KMSCONTEXT_AESGCM(t *testing.T) { 149 ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { 150 fmt.Fprintln(writer, `{"CiphertextBlob":"8gSzlk7giyfFbLPUVgoVjvQebI1827jp8lDkO+n2chsiSoegx1sjm8NdPk0Bl70I","KeyId":"test-key-id","Plaintext":"lP6AbIQTmptyb/+WQq+ubDw+w7na0T1LGSByZGuaono="}`) 151 })) 152 153 sess := unit.Session.Copy() 154 kmsClient := kms.New(sess.Copy(&aws.Config{Endpoint: &ts.URL})) 155 156 var md MaterialDescription 157 iv, _ := hex.DecodeString("ae325acae2bfd5b9c3d0b813") 158 kmsWithStaticIV := cdgWithStaticTestIV{ 159 IV: iv, 160 CipherDataGeneratorWithCEKAlg: NewKMSContextKeyGenerator(kmsClient, "test-key-id", md), 161 } 162 contentCipherBuilderV2 := AESGCMContentCipherBuilderV2(kmsWithStaticIV) 163 client, err := NewEncryptionClientV2(sess, contentCipherBuilderV2) 164 if err != nil { 165 t.Fatalf("expected no error, got %v", err) 166 } 167 168 req, _ := client.PutObjectRequest(&s3.PutObjectInput{ 169 Bucket: aws.String("test-bucket"), 170 Key: aws.String("test-key"), 171 Body: func() io.ReadSeeker { 172 content, _ := hex.DecodeString("8f2c59c6dbfcacf356f3da40788cbde67ca38161a4702cbcf757af663e1c24a600001b2f500417dbf5a050f57db6737422b2ed6a44c75e0d") 173 return bytes.NewReader(content) 174 }(), 175 }) 176 177 req.Handlers.Send.Clear() 178 req.Handlers.Send.PushFront(func(r *request.Request) { 179 all, err := ioutil.ReadAll(r.Body) 180 if err != nil { 181 t.Fatalf("expected no error, got %v", err) 182 } 183 184 expected, _ := hex.DecodeString("4cd8e95a1c9b8b19640e02838b02c8c09e66250703a602956695afbc23cbb8647d51645955ab63b89733d0766f9a264adb88571b1d467b734ff72eb73d31de9a83670d59688c54ea") 185 186 if !bytes.Equal(all, expected) { 187 t.Error("encrypted bytes did not match expected") 188 } 189 190 req.HTTPResponse = &http.Response{ 191 Status: http.StatusText(200), 192 StatusCode: http.StatusOK, 193 Body: aws.ReadSeekCloser(bytes.NewReader([]byte{})), 194 } 195 }) 196 err = req.Send() 197 if err != nil { 198 t.Errorf("expected no error, got %v", err) 199 } 200} 201 202func TestNewEncryptionClientV2_FailsOnIncompatibleFixtures(t *testing.T) { 203 sess := unit.Session.Copy() 204 _, err := NewEncryptionClientV2(sess, AESGCMContentCipherBuilder(NewKMSKeyGenerator(kms.New(sess), "cmkId"))) 205 if err == nil { 206 t.Fatal("expected to fail, but got nil") 207 } 208 if !strings.Contains(err.Error(), "attempted to use deprecated or incompatible cipher builder") { 209 t.Errorf("expected to get error for using dperecated cipher builder") 210 } 211} 212