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