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