1// +build go1.7 2 3package s3crypto_test 4 5import ( 6 "bytes" 7 "encoding/hex" 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 "net/http/httptest" 12 "strings" 13 "testing" 14 15 "github.com/aws/aws-sdk-go/aws" 16 "github.com/aws/aws-sdk-go/aws/request" 17 "github.com/aws/aws-sdk-go/awstesting/unit" 18 "github.com/aws/aws-sdk-go/service/kms" 19 "github.com/aws/aws-sdk-go/service/s3" 20 "github.com/aws/aws-sdk-go/service/s3/s3crypto" 21) 22 23func TestDecryptionClientV2_CheckDeprecatedFeatures(t *testing.T) { 24 // AES/GCM/NoPadding with kms+context => allowed 25 builder := s3crypto.AESGCMContentCipherBuilderV2(s3crypto.NewKMSContextKeyGenerator(kms.New(unit.Session), "cmkID", s3crypto.MaterialDescription{})) 26 _, err := s3crypto.NewEncryptionClientV2(unit.Session, builder) 27 if err != nil { 28 t.Errorf("expected no error, got %v", err) 29 } 30 31 // AES/GCM/NoPadding with kms => not allowed 32 builder = s3crypto.AESGCMContentCipherBuilder(s3crypto.NewKMSKeyGenerator(kms.New(unit.Session), "cmkID")) 33 _, err = s3crypto.NewEncryptionClientV2(unit.Session, builder) 34 if err == nil { 35 t.Error("expected error, but got nil") 36 } 37 38 // AES/CBC/PKCS5Padding with kms => not allowed 39 builder = s3crypto.AESCBCContentCipherBuilder(s3crypto.NewKMSKeyGenerator(kms.New(unit.Session), "cmkID"), s3crypto.NewPKCS7Padder(128)) 40 _, err = s3crypto.NewEncryptionClientV2(unit.Session, builder) 41 if err == nil { 42 t.Error("expected error, but got nil") 43 } 44} 45 46func TestDecryptionClientV2_GetObject(t *testing.T) { 47 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 48 fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, "hJUv7S6K2cHF64boS9ixHX0TZAjBZLT4ZpEO4XxkGnY=", `"}`)) 49 })) 50 defer ts.Close() 51 52 kmsClient := kms.New(unit.Session.Copy(&aws.Config{Endpoint: &ts.URL})) 53 54 cr := s3crypto.NewCryptoRegistry() 55 if err := s3crypto.RegisterKMSContextWrapWithAnyCMK(cr, kmsClient); err != nil { 56 t.Fatalf("expected no error, got %v", err) 57 } 58 if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil { 59 t.Fatalf("expected no error, got %v", err) 60 } 61 62 c, err := s3crypto.NewDecryptionClientV2(unit.Session.Copy(), cr) 63 if err != nil { 64 t.Fatalf("expected no error, got %v", err) 65 } 66 67 input := &s3.GetObjectInput{ 68 Bucket: aws.String("test"), 69 Key: aws.String("test"), 70 } 71 72 req, out := c.GetObjectRequest(input) 73 req.Handlers.Send.Clear() 74 req.Handlers.Send.PushBack(func(r *request.Request) { 75 b, err := hex.DecodeString("6b134eb7a353131de92faff64f594b2794e3544e31776cca26fe3bbeeffc68742d1007234f11c6670522602326868e29f37e9d2678f1614ec1a2418009b9772100929aadbed9a21a") 76 if err != nil { 77 t.Errorf("expected no error, but received %v", err) 78 } 79 80 r.HTTPResponse = &http.Response{ 81 StatusCode: 200, 82 Header: http.Header{ 83 http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"PsuclPnlo2O0MQoov6kL1TBlaZG6oyNwWuAqmAgq7g8b9ZeeORi3VTMg624FU9jx"}, 84 http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{"dqqlq2dRVSQ5hFRb"}, 85 http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"aws:x-amz-cek-alg":"AES/GCM/NoPadding"}`}, 86 http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSContextWrap}, 87 http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{"AES/GCM/NoPadding"}, 88 }, 89 Body: ioutil.NopCloser(bytes.NewBuffer(b)), 90 } 91 }) 92 err = req.Send() 93 if err != nil { 94 t.Fatalf("expected no error, got %v", err) 95 } 96 97 actual, err := ioutil.ReadAll(out.Body) 98 if err != nil { 99 t.Fatalf("expected no error, got %v", err) 100 } 101 102 expected, err := hex.DecodeString("af150d7156bf5b3f5c461e5c6ac820acc5a33aab7085d920666c250ff251209d5a4029b3bd78250fab6e11aed52fae948d407056a9519b68") 103 if err != nil { 104 t.Fatalf("expected no error, got %v", err) 105 } 106 107 if bytes.Compare(expected, actual) != 0 { 108 t.Fatalf("expected content to match but it did not") 109 } 110} 111 112func TestDecryptionClientV2_GetObject_V1Interop_KMS_AESCBC(t *testing.T) { 113 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 114 fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, "7ItX9CTGNWWegC62RlaNu6EJ3+J9yGO7yAqDNU4CdeA=", `"}`)) 115 })) 116 defer ts.Close() 117 118 kmsClient := kms.New(unit.Session.Copy(&aws.Config{Endpoint: &ts.URL})) 119 120 cr := s3crypto.NewCryptoRegistry() 121 if err := s3crypto.RegisterKMSWrapWithAnyCMK(cr, kmsClient); err != nil { 122 t.Fatalf("expected no error, got %v", err) 123 } 124 if err := s3crypto.RegisterAESCBCContentCipher(cr, s3crypto.AESCBCPadder); err != nil { 125 t.Fatalf("expected no error, got %v", err) 126 } 127 128 c, err := s3crypto.NewDecryptionClientV2(unit.Session.Copy(), cr) 129 if err != nil { 130 t.Fatalf("expected no error, got %v", err) 131 } 132 133 input := &s3.GetObjectInput{ 134 Bucket: aws.String("test"), 135 Key: aws.String("test"), 136 } 137 138 req, out := c.GetObjectRequest(input) 139 req.Handlers.Send.Clear() 140 req.Handlers.Send.PushBack(func(r *request.Request) { 141 b, err := hex.DecodeString("6f4f413a357a3c3a12289442fb835c5e4ecc8db1d86d3d1eab906ce07e1ad772180b2e9ec49c3fc667d8aceea8c46da6bb9738251a8e36241a473ad820f99c701906bac1f48578d5392e928889bbb1d9") 142 if err != nil { 143 t.Errorf("expected no error, but received %v", err) 144 } 145 146 r.HTTPResponse = &http.Response{ 147 StatusCode: 200, 148 Header: http.Header{ 149 http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"/nJlgMtxMNk2ErKLLrLp3H7A7aQyJcJOClE2ldAIIFNZU4OhUMc1mMCHdIEC8fby"}, 150 http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{"adO9U7pcEHxUTaguIkho9g=="}, 151 http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"kms_cmk_id":"test-key-id"}`}, 152 http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSWrap}, 153 http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{"AES/CBC/PKCS5Padding"}, 154 }, 155 Body: ioutil.NopCloser(bytes.NewBuffer(b)), 156 } 157 }) 158 err = req.Send() 159 if err != nil { 160 t.Fatalf("expected no error, got %v", err) 161 } 162 163 actual, err := ioutil.ReadAll(out.Body) 164 if err != nil { 165 t.Fatalf("expected no error, got %v", err) 166 } 167 168 expected, err := hex.DecodeString("a716e018ffecf4bb94d4352082af4662612d9c225efed6f389bf1f6f0447a9bce80cc712d7e66ee5e1c086af38e607ead351fd2c1a0247878e693ada73bd580b") 169 if err != nil { 170 t.Fatalf("expected no error, got %v", err) 171 } 172 173 if bytes.Compare(expected, actual) != 0 { 174 t.Fatalf("expected content to match but it did not") 175 } 176} 177 178func TestDecryptionClientV2_GetObject_V1Interop_KMS_AESGCM(t *testing.T) { 179 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 180 fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, "Hrjrkkt/vQwMYtqvK6+MiXh3xiMvviL1Ks7w2mgsJgU=", `"}`)) 181 })) 182 defer ts.Close() 183 184 kmsClient := kms.New(unit.Session.Copy(&aws.Config{Endpoint: &ts.URL})) 185 186 cr := s3crypto.NewCryptoRegistry() 187 if err := s3crypto.RegisterKMSWrapWithAnyCMK(cr, kmsClient); err != nil { 188 t.Fatalf("expected no error, got %v", err) 189 } 190 if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil { 191 t.Fatalf("expected no error, got %v", err) 192 } 193 194 c, err := s3crypto.NewDecryptionClientV2(unit.Session.Copy(), cr) 195 if err != nil { 196 t.Fatalf("expected no error, got %v", err) 197 } 198 199 input := &s3.GetObjectInput{ 200 Bucket: aws.String("test"), 201 Key: aws.String("test"), 202 } 203 204 req, out := c.GetObjectRequest(input) 205 req.Handlers.Send.Clear() 206 req.Handlers.Send.PushBack(func(r *request.Request) { 207 b, err := hex.DecodeString("6370a90b9a118301c2160c23a90d96146761276acdcfa92e6cbcb783abdc2e1813891506d6850754ef87ed2ac3bf570dd5c9da9492b7769ae1e639d073d688bd284815404ce2648a") 208 if err != nil { 209 t.Errorf("expected no error, but received %v", err) 210 } 211 212 r.HTTPResponse = &http.Response{ 213 StatusCode: 200, 214 Header: http.Header{ 215 http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"/7tu/RFXZU1UFwRzzf11IdF3b1wBxBZhnUMjVYHKKr5DjAHS602GvXt4zYcx/MJo"}, 216 http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{"8Rlvyy8AoYj8v579"}, 217 http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"kms_cmk_id":"test-key-id"}`}, 218 http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSWrap}, 219 http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{"AES/GCM/NoPadding"}, 220 }, 221 Body: ioutil.NopCloser(bytes.NewBuffer(b)), 222 } 223 }) 224 err = req.Send() 225 if err != nil { 226 t.Fatalf("expected no error, got %v", err) 227 } 228 229 actual, err := ioutil.ReadAll(out.Body) 230 if err != nil { 231 t.Fatalf("expected no error, got %v", err) 232 } 233 234 expected, err := hex.DecodeString("75f6805afa7d7be4f56c5906adc27a5959158bf4af6e7c7e12bda3458300f6b1c8daaf9a5949f7a6bdbb8a9c072de05bf0541633421f42f8") 235 if err != nil { 236 t.Fatalf("expected no error, got %v", err) 237 } 238 239 if bytes.Compare(expected, actual) != 0 { 240 t.Fatalf("expected content to match but it did not") 241 } 242} 243 244func TestDecryptionClientV2_GetObject_OnlyDecryptsRegisteredAlgorithms(t *testing.T) { 245 dataHandler := func(r *request.Request) { 246 b, err := hex.DecodeString("1bd0271b25951fdef3dbe51a9b7af85f66b311e091aa10a346655068f657b9da9acc0843ea0522b0d1ae4a25a31b13605dd1ac5d002db8965d9d4652fd602693") 247 if err != nil { 248 t.Errorf("expected no error, but received %v", err) 249 } 250 251 r.HTTPResponse = &http.Response{ 252 StatusCode: 200, 253 Header: http.Header{ 254 http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"gNuYjzkLTzfhOcIX9h1l8jApWcAAQqzlryOE166kdDojaHH/+7cCqR5HU8Bpxmij"}, 255 http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{"Vmauu+TMEgaXa26ObqpARA=="}, 256 http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"kms_cmk_id":"test-key-id"}`}, 257 http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSWrap}, 258 http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{"AES/CBC/PKCS5Padding"}, 259 }, 260 Body: ioutil.NopCloser(bytes.NewBuffer(b)), 261 } 262 } 263 264 cases := map[string]struct { 265 Client *s3crypto.DecryptionClientV2 266 WantErr string 267 }{ 268 "unsupported wrap": { 269 Client: func() *s3crypto.DecryptionClientV2 { 270 cr := s3crypto.NewCryptoRegistry() 271 if err := s3crypto.RegisterKMSContextWrapWithAnyCMK(cr, kms.New(unit.Session.Copy())); err != nil { 272 t.Fatalf("expected no error, got %v", err) 273 } 274 if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil { 275 t.Fatalf("expected no error, got %v", err) 276 } 277 278 c, err := s3crypto.NewDecryptionClientV2(unit.Session.Copy(), cr) 279 if err != nil { 280 t.Fatalf("expected no error, got %v", err) 281 } 282 return c 283 }(), 284 WantErr: "wrap algorithm isn't supported, kms", 285 }, 286 "unsupported cek": { 287 Client: func() *s3crypto.DecryptionClientV2 { 288 cr := s3crypto.NewCryptoRegistry() 289 if err := s3crypto.RegisterKMSWrapWithAnyCMK(cr, kms.New(unit.Session.Copy())); err != nil { 290 t.Fatalf("expected no error, got %v", err) 291 } 292 if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil { 293 t.Fatalf("expected no error, got %v", err) 294 } 295 c, err := s3crypto.NewDecryptionClientV2(unit.Session.Copy(), cr) 296 if err != nil { 297 t.Fatalf("expected no error, got %v", err) 298 } 299 return c 300 }(), 301 WantErr: "cek algorithm isn't supported, AES/CBC/PKCS5Padding", 302 }, 303 } 304 305 for name, tt := range cases { 306 t.Run(name, func(t *testing.T) { 307 input := &s3.GetObjectInput{ 308 Bucket: aws.String("test"), 309 Key: aws.String("test"), 310 } 311 req, _ := tt.Client.GetObjectRequest(input) 312 req.Handlers.Send.Clear() 313 req.Handlers.Send.PushBack(dataHandler) 314 err := req.Send() 315 if err == nil { 316 t.Fatalf("expected error, got none") 317 } 318 if e, a := tt.WantErr, err.Error(); !strings.Contains(a, e) { 319 t.Errorf("expected %v, got %v", e, a) 320 } 321 }) 322 } 323} 324 325func TestDecryptionClientV2_CheckValidCryptoRegistry(t *testing.T) { 326 cr := s3crypto.NewCryptoRegistry() 327 _, err := s3crypto.NewDecryptionClientV2(unit.Session.Copy(), cr) 328 if err == nil { 329 t.Fatal("expected error, got none") 330 } 331 if e, a := "at least one key wrapping algorithms must be provided", err.Error(); !strings.Contains(a, e) { 332 t.Fatalf("expected %v, got %v", e, a) 333 } 334} 335