1package s3crypto 2 3import ( 4 "strings" 5 6 "github.com/aws/aws-sdk-go/aws" 7 "github.com/aws/aws-sdk-go/aws/client" 8 "github.com/aws/aws-sdk-go/aws/request" 9 "github.com/aws/aws-sdk-go/service/kms" 10 "github.com/aws/aws-sdk-go/service/s3" 11 "github.com/aws/aws-sdk-go/service/s3/s3iface" 12) 13 14// WrapEntry is builder that return a proper key decrypter and error 15type WrapEntry func(Envelope) (CipherDataDecrypter, error) 16 17// CEKEntry is a builder thatn returns a proper content decrypter and error 18type CEKEntry func(CipherData) (ContentCipher, error) 19 20// DecryptionClient is an S3 crypto client. The decryption client 21// will handle all get object requests from Amazon S3. 22// Supported key wrapping algorithms: 23// *AWS KMS 24// 25// Supported content ciphers: 26// * AES/GCM 27// * AES/CBC 28type DecryptionClient struct { 29 S3Client s3iface.S3API 30 // LoadStrategy is used to load the metadata either from the metadata of the object 31 // or from a separate file in s3. 32 // 33 // Defaults to our default load strategy. 34 LoadStrategy LoadStrategy 35 36 WrapRegistry map[string]WrapEntry 37 CEKRegistry map[string]CEKEntry 38 PadderRegistry map[string]Padder 39} 40 41// NewDecryptionClient instantiates a new S3 crypto client 42// 43// Example: 44// sess := session.New() 45// svc := s3crypto.NewDecryptionClient(sess, func(svc *s3crypto.DecryptionClient{ 46// // Custom client options here 47// })) 48func NewDecryptionClient(prov client.ConfigProvider, options ...func(*DecryptionClient)) *DecryptionClient { 49 s3client := s3.New(prov) 50 client := &DecryptionClient{ 51 S3Client: s3client, 52 LoadStrategy: defaultV2LoadStrategy{ 53 client: s3client, 54 }, 55 WrapRegistry: map[string]WrapEntry{ 56 KMSWrap: (kmsKeyHandler{ 57 kms: kms.New(prov), 58 }).decryptHandler, 59 }, 60 CEKRegistry: map[string]CEKEntry{ 61 AESGCMNoPadding: newAESGCMContentCipher, 62 strings.Join([]string{AESCBC, AESCBCPadder.Name()}, "/"): newAESCBCContentCipher, 63 }, 64 PadderRegistry: map[string]Padder{ 65 strings.Join([]string{AESCBC, AESCBCPadder.Name()}, "/"): AESCBCPadder, 66 "NoPadding": NoPadder, 67 }, 68 } 69 for _, option := range options { 70 option(client) 71 } 72 73 return client 74} 75 76// GetObjectRequest will make a request to s3 and retrieve the object. In this process 77// decryption will be done. The SDK only supports V2 reads of KMS and GCM. 78// 79// Example: 80// sess := session.New() 81// svc := s3crypto.NewDecryptionClient(sess) 82// req, out := svc.GetObjectRequest(&s3.GetObjectInput { 83// Key: aws.String("testKey"), 84// Bucket: aws.String("testBucket"), 85// }) 86// err := req.Send() 87func (c *DecryptionClient) GetObjectRequest(input *s3.GetObjectInput) (*request.Request, *s3.GetObjectOutput) { 88 req, out := c.S3Client.GetObjectRequest(input) 89 req.Handlers.Unmarshal.PushBack(func(r *request.Request) { 90 env, err := c.LoadStrategy.Load(r) 91 if err != nil { 92 r.Error = err 93 out.Body.Close() 94 return 95 } 96 97 // If KMS should return the correct CEK algorithm with the proper 98 // KMS key provider 99 cipher, err := c.contentCipherFromEnvelope(env) 100 if err != nil { 101 r.Error = err 102 out.Body.Close() 103 return 104 } 105 106 reader, err := cipher.DecryptContents(out.Body) 107 if err != nil { 108 r.Error = err 109 out.Body.Close() 110 return 111 } 112 out.Body = reader 113 }) 114 return req, out 115} 116 117// GetObject is a wrapper for GetObjectRequest 118func (c *DecryptionClient) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOutput, error) { 119 req, out := c.GetObjectRequest(input) 120 return out, req.Send() 121} 122 123// GetObjectWithContext is a wrapper for GetObjectRequest with the additional 124// context, and request options support. 125// 126// GetObjectWithContext is the same as GetObject with the additional support for 127// Context input parameters. The Context must not be nil. A nil Context will 128// cause a panic. Use the Context to add deadlining, timeouts, etc. In the future 129// this may create sub-contexts for individual underlying requests. 130func (c *DecryptionClient) GetObjectWithContext(ctx aws.Context, input *s3.GetObjectInput, opts ...request.Option) (*s3.GetObjectOutput, error) { 131 req, out := c.GetObjectRequest(input) 132 req.SetContext(ctx) 133 req.ApplyOptions(opts...) 134 return out, req.Send() 135} 136