1package s3crypto_test 2 3import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/hex" 7 "fmt" 8 "io/ioutil" 9 "net/http" 10 "net/http/httptest" 11 "strings" 12 "testing" 13 14 "github.com/aws/aws-sdk-go/aws" 15 "github.com/aws/aws-sdk-go/aws/awserr" 16 "github.com/aws/aws-sdk-go/aws/request" 17 "github.com/aws/aws-sdk-go/awstesting" 18 "github.com/aws/aws-sdk-go/awstesting/unit" 19 "github.com/aws/aws-sdk-go/service/s3" 20 "github.com/aws/aws-sdk-go/service/s3/s3crypto" 21) 22 23func TestGetObjectGCM(t *testing.T) { 24 key, _ := hex.DecodeString("31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22") 25 keyB64 := base64.StdEncoding.EncodeToString(key) 26 // This is our KMS response 27 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 28 fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) 29 })) 30 defer ts.Close() 31 32 sess := unit.Session.Copy(&aws.Config{ 33 MaxRetries: aws.Int(0), 34 Endpoint: aws.String(ts.URL), 35 DisableSSL: aws.Bool(true), 36 S3ForcePathStyle: aws.Bool(true), 37 Region: aws.String("us-west-2"), 38 }) 39 40 c := s3crypto.NewDecryptionClient(sess) 41 if c == nil { 42 t.Error("expected non-nil value") 43 } 44 input := &s3.GetObjectInput{ 45 Key: aws.String("test"), 46 Bucket: aws.String("test"), 47 } 48 req, out := c.GetObjectRequest(input) 49 req.Handlers.Send.Clear() 50 req.Handlers.Send.PushBack(func(r *request.Request) { 51 iv, err := hex.DecodeString("0d18e06c7c725ac9e362e1ce") 52 if err != nil { 53 t.Errorf("expected no error, but received %v", err) 54 } 55 56 b, err := hex.DecodeString("fa4362189661d163fcd6a56d8bf0405ad636ac1bbedd5cc3ee727dc2ab4a9489") 57 if err != nil { 58 t.Errorf("expected no error, but received %v", err) 59 } 60 61 r.HTTPResponse = &http.Response{ 62 StatusCode: 200, 63 Header: http.Header{ 64 http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"SpFRES0JyU8BLZSKo51SrwILK4lhtZsWiMNjgO4WmoK+joMwZPG7Hw=="}, 65 http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{base64.URLEncoding.EncodeToString(iv)}, 66 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"}`}, 67 http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSWrap}, 68 http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{s3crypto.AESGCMNoPadding}, 69 http.CanonicalHeaderKey("x-amz-meta-x-amz-tag-len"): []string{"128"}, 70 }, 71 Body: ioutil.NopCloser(bytes.NewBuffer(b)), 72 } 73 }) 74 err := req.Send() 75 if err != nil { 76 t.Errorf("expected no error, but received %v", err) 77 } 78 b, err := ioutil.ReadAll(out.Body) 79 if err != nil { 80 t.Errorf("expected no error, but received %v", err) 81 } 82 expected, err := hex.DecodeString("2db5168e932556f8089a0622981d017d") 83 if err != nil { 84 t.Errorf("expected no error, but received %v", err) 85 } 86 87 if !bytes.Equal(expected, b) { 88 t.Error("expected bytes to be equivalent") 89 } 90} 91 92func TestGetObjectCBC(t *testing.T) { 93 key, _ := hex.DecodeString("898be9cc5004ed0fa6e117c9a3099d31") 94 keyB64 := base64.StdEncoding.EncodeToString(key) 95 // This is our KMS response 96 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 97 fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) 98 })) 99 defer ts.Close() 100 101 sess := unit.Session.Copy(&aws.Config{ 102 MaxRetries: aws.Int(0), 103 Endpoint: aws.String(ts.URL), 104 DisableSSL: aws.Bool(true), 105 S3ForcePathStyle: aws.Bool(true), 106 Region: aws.String("us-west-2"), 107 }) 108 109 c := s3crypto.NewDecryptionClient(sess) 110 if c == nil { 111 t.Error("expected non-nil value") 112 } 113 input := &s3.GetObjectInput{ 114 Key: aws.String("test"), 115 Bucket: aws.String("test"), 116 } 117 req, out := c.GetObjectRequest(input) 118 req.Handlers.Send.Clear() 119 req.Handlers.Send.PushBack(func(r *request.Request) { 120 iv, err := hex.DecodeString("9dea7621945988f96491083849b068df") 121 if err != nil { 122 t.Errorf("expected no error, but received %v", err) 123 } 124 b, err := hex.DecodeString("e232cd6ef50047801ee681ec30f61d53cfd6b0bca02fd03c1b234baa10ea82ac9dab8b960926433a19ce6dea08677e34") 125 if err != nil { 126 t.Errorf("expected no error, but received %v", err) 127 } 128 129 r.HTTPResponse = &http.Response{ 130 StatusCode: 200, 131 Header: http.Header{ 132 http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"SpFRES0JyU8BLZSKo51SrwILK4lhtZsWiMNjgO4WmoK+joMwZPG7Hw=="}, 133 http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{base64.URLEncoding.EncodeToString(iv)}, 134 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"}`}, 135 http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSWrap}, 136 http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{strings.Join([]string{s3crypto.AESCBC, s3crypto.AESCBCPadder.Name()}, "/")}, 137 }, 138 Body: ioutil.NopCloser(bytes.NewBuffer(b)), 139 } 140 }) 141 err := req.Send() 142 if err != nil { 143 t.Errorf("expected no error, but received %v", err) 144 } 145 b, err := ioutil.ReadAll(out.Body) 146 if err != nil { 147 t.Errorf("expected no error, but received %v", err) 148 } 149 expected, err := hex.DecodeString("0397f4f6820b1f9386f14403be5ac16e50213bd473b4874b9bcbf5f318ee686b1d") 150 if err != nil { 151 t.Errorf("expected no error, but received %v", err) 152 } 153 154 if !bytes.Equal(expected, b) { 155 t.Error("expected bytes to be equivalent") 156 } 157} 158 159func TestGetObjectCBC2(t *testing.T) { 160 key, _ := hex.DecodeString("8d70e92489c4e6cfb12261b4d17f4b85826da687fc8742fcf9f87fadb5b4cb89") 161 keyB64 := base64.StdEncoding.EncodeToString(key) 162 // This is our KMS response 163 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 164 fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, keyB64, `"}`)) 165 })) 166 defer ts.Close() 167 168 sess := unit.Session.Copy(&aws.Config{ 169 MaxRetries: aws.Int(0), 170 Endpoint: aws.String(ts.URL), 171 DisableSSL: aws.Bool(true), 172 S3ForcePathStyle: aws.Bool(true), 173 Region: aws.String("us-west-2"), 174 }) 175 176 c := s3crypto.NewDecryptionClient(sess) 177 if c == nil { 178 t.Error("expected non-nil value") 179 } 180 input := &s3.GetObjectInput{ 181 Key: aws.String("test"), 182 Bucket: aws.String("test"), 183 } 184 req, out := c.GetObjectRequest(input) 185 req.Handlers.Send.Clear() 186 req.Handlers.Send.PushBack(func(r *request.Request) { 187 b, err := hex.DecodeString("fd0c71ecb7ed16a9bf42ea5f75501d416df608f190890c3b4d8897f24744cd7f9ea4a0b212e60634302450e1c5378f047ff753ccefe365d411c36339bf22e301fae4c3a6226719a4b93dc74c1af79d0296659b5d56c0892315f2c7cc30190220db1eaafae3920d6d9c65d0aa366499afc17af493454e141c6e0fbdeb6a990cb4") 188 if err != nil { 189 t.Errorf("expected no error, but received %v", err) 190 } 191 192 r.HTTPResponse = &http.Response{ 193 StatusCode: 200, 194 Header: http.Header{ 195 http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"AQEDAHikdGvcj7Gil5VqAR/JWvvPp3ue26+t2vhWy4lL2hg4mAAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDCcy43wCR0bSsnzTrAIBEIA7WdD2jxC3tCrK6TOdiEfbIN64m+UN7Velz4y0LRra5jn2U1CDClacwIpiBYuDp5ymPKO+ZqUGE0WEf20="}, 196 http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{"EMMWJY8ZLcK/9FOj3iCpng=="}, 197 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"}`}, 198 http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSWrap}, 199 http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{strings.Join([]string{s3crypto.AESCBC, s3crypto.AESCBCPadder.Name()}, "/")}, 200 }, 201 Body: ioutil.NopCloser(bytes.NewBuffer(b)), 202 } 203 }) 204 err := req.Send() 205 if err != nil { 206 t.Errorf("expected no error, but received %v", err) 207 } 208 b, err := ioutil.ReadAll(out.Body) 209 if err != nil { 210 t.Errorf("expected no error, but received %v", err) 211 } 212 expected, err := hex.DecodeString("a6ccd3482f5ce25c9ddeb69437cd0acbc0bdda2ef8696d90781de2b35704543529871b2032e68ef1c5baed1769aba8d420d1aca181341b49b8b3587a6580cdf1d809c68f06735f7735c16691f4b70c967d68fc08195b81ad71bcc4df452fd0a5799c1e1234f92f1cd929fc072167ccf9f2ac85b93170932b32") 213 if err != nil { 214 t.Errorf("expected no error, but received %v", err) 215 } 216 217 if !bytes.Equal(expected, b) { 218 t.Error("expected bytes to be equivalent") 219 } 220} 221 222func TestGetObjectWithContext(t *testing.T) { 223 c := s3crypto.NewDecryptionClient(unit.Session) 224 225 ctx := &awstesting.FakeContext{DoneCh: make(chan struct{})} 226 ctx.Error = fmt.Errorf("context canceled") 227 close(ctx.DoneCh) 228 229 input := s3.GetObjectInput{ 230 Key: aws.String("test"), 231 Bucket: aws.String("test"), 232 } 233 _, err := c.GetObjectWithContext(ctx, &input) 234 if err == nil { 235 t.Fatalf("expected error, did not get one") 236 } 237 aerr := err.(awserr.Error) 238 if e, a := request.CanceledErrorCode, aerr.Code(); e != a { 239 t.Errorf("expected error code %q, got %q", e, a) 240 } 241 if e, a := "canceled", aerr.Message(); !strings.Contains(a, e) { 242 t.Errorf("expected error message to contain %q, but did not %q", e, a) 243 } 244} 245 246func TestDecryptionClient_GetObject_V2Artifact(t *testing.T) { 247 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 248 fmt.Fprintln(w, fmt.Sprintf("%s%s%s", `{"KeyId":"test-key-id","Plaintext":"`, "hJUv7S6K2cHF64boS9ixHX0TZAjBZLT4ZpEO4XxkGnY=", `"}`)) 249 })) 250 defer ts.Close() 251 252 c := s3crypto.NewDecryptionClient(unit.Session.Copy(&aws.Config{Endpoint: &ts.URL})) 253 254 input := &s3.GetObjectInput{ 255 Bucket: aws.String("test"), 256 Key: aws.String("test"), 257 } 258 259 req, out := c.GetObjectRequest(input) 260 req.Handlers.Send.Clear() 261 req.Handlers.Send.PushBack(func(r *request.Request) { 262 b, err := hex.DecodeString("6b134eb7a353131de92faff64f594b2794e3544e31776cca26fe3bbeeffc68742d1007234f11c6670522602326868e29f37e9d2678f1614ec1a2418009b9772100929aadbed9a21a") 263 if err != nil { 264 t.Errorf("expected no error, but received %v", err) 265 } 266 267 r.HTTPResponse = &http.Response{ 268 StatusCode: 200, 269 Header: http.Header{ 270 http.CanonicalHeaderKey("x-amz-meta-x-amz-key-v2"): []string{"PsuclPnlo2O0MQoov6kL1TBlaZG6oyNwWuAqmAgq7g8b9ZeeORi3VTMg624FU9jx"}, 271 http.CanonicalHeaderKey("x-amz-meta-x-amz-iv"): []string{"dqqlq2dRVSQ5hFRb"}, 272 http.CanonicalHeaderKey("x-amz-meta-x-amz-matdesc"): []string{`{"aws:x-amz-cek-alg": "AES/GCM/NoPadding"}`}, 273 http.CanonicalHeaderKey("x-amz-meta-x-amz-wrap-alg"): []string{s3crypto.KMSContextWrap}, 274 http.CanonicalHeaderKey("x-amz-meta-x-amz-cek-alg"): []string{"AES/GCM/NoPadding"}, 275 }, 276 Body: ioutil.NopCloser(bytes.NewBuffer(b)), 277 } 278 }) 279 err := req.Send() 280 if err != nil { 281 t.Fatalf("expected no error, got %v", err) 282 } 283 284 actual, err := ioutil.ReadAll(out.Body) 285 if err != nil { 286 t.Fatalf("expected no error, got %v", err) 287 } 288 289 expected, err := hex.DecodeString("af150d7156bf5b3f5c461e5c6ac820acc5a33aab7085d920666c250ff251209d5a4029b3bd78250fab6e11aed52fae948d407056a9519b68") 290 if err != nil { 291 t.Fatalf("expected no error, got %v", err) 292 } 293 294 if bytes.Compare(expected, actual) != 0 { 295 t.Fatalf("expected content to match but it did not") 296 } 297} 298