1package s3crypto
2
3import (
4	"github.com/aws/aws-sdk-go/aws"
5	"github.com/aws/aws-sdk-go/aws/awserr"
6	"github.com/aws/aws-sdk-go/aws/client"
7	"github.com/aws/aws-sdk-go/aws/request"
8	"github.com/aws/aws-sdk-go/service/s3"
9	"github.com/aws/aws-sdk-go/service/s3/s3iface"
10)
11
12// DefaultMinFileSize is used to check whether we want to write to a temp file
13// or store the data in memory.
14const DefaultMinFileSize = 1024 * 512 * 5
15
16// EncryptionClient is an S3 crypto client. By default the SDK will use Authentication mode which
17// will use KMS for key wrapping and AES GCM for content encryption.
18// AES GCM will load all data into memory. However, the rest of the content algorithms
19// do not load the entire contents into memory.
20//
21// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
22type EncryptionClient struct {
23	S3Client             s3iface.S3API
24	ContentCipherBuilder ContentCipherBuilder
25	// SaveStrategy will dictate where the envelope is saved.
26	//
27	// Defaults to the object's metadata
28	SaveStrategy SaveStrategy
29	// TempFolderPath is used to store temp files when calling PutObject.
30	// Temporary files are needed to compute the X-Amz-Content-Sha256 header.
31	TempFolderPath string
32	// MinFileSize is the minimum size for the content to write to a
33	// temporary file instead of using memory.
34	MinFileSize int64
35}
36
37func validateV1EncryptionClientConstruction(c *EncryptionClient) error {
38	builder, ok := c.ContentCipherBuilder.(compatibleEncryptionFixture)
39	if !ok {
40		return nil
41	}
42
43	err := builder.isEncryptionVersionCompatible(v1ClientVersion)
44	if err != nil {
45		return awserr.New(clientConstructionErrorCode, "invalid client configuration", err)
46	}
47	return nil
48}
49
50// NewEncryptionClient instantiates a new S3 crypto client
51//
52// Example:
53//	cmkID := "arn:aws:kms:region:000000000000:key/00000000-0000-0000-0000-000000000000"
54//  sess := session.Must(session.NewSession())
55//	handler := s3crypto.NewKMSKeyGenerator(kms.New(sess), cmkID)
56//	svc := s3crypto.NewEncryptionClient(sess, s3crypto.AESGCMContentCipherBuilder(handler))
57//
58// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
59func NewEncryptionClient(prov client.ConfigProvider, builder ContentCipherBuilder, options ...func(*EncryptionClient)) *EncryptionClient {
60	s3client := s3.New(prov)
61
62	s3client.Handlers.Build.PushBack(func(r *request.Request) {
63		request.AddToUserAgent(r, "S3CryptoV1n")
64	})
65
66	client := &EncryptionClient{
67		S3Client:             s3client,
68		ContentCipherBuilder: builder,
69		SaveStrategy:         HeaderV2SaveStrategy{},
70		MinFileSize:          DefaultMinFileSize,
71	}
72
73	for _, option := range options {
74		option(client)
75	}
76
77	return client
78}
79
80// PutObjectRequest creates a temp file to encrypt the contents into. It then streams
81// that data to S3.
82//
83// Example:
84//	svc := s3crypto.NewEncryptionClient(session.Must(session.NewSession()), s3crypto.AESGCMContentCipherBuilder(handler))
85//	req, out := svc.PutObjectRequest(&s3.PutObjectInput {
86//	  Key: aws.String("testKey"),
87//	  Bucket: aws.String("testBucket"),
88//	  Body: strings.NewReader("test data"),
89//	})
90//	err := req.Send()
91//
92// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
93func (c *EncryptionClient) PutObjectRequest(input *s3.PutObjectInput) (*request.Request, *s3.PutObjectOutput) {
94	req, out := putObjectRequest(c.getClientOptions(), input)
95	if err := validateV1EncryptionClientConstruction(c); err != nil {
96		errHandler := setReqError(err)
97		req.Error = err
98		req.Handlers.Build.Clear()
99		req.Handlers.Send.Clear()
100		req.Handlers.Validate.PushFront(errHandler)
101		req.Handlers.Build.PushFront(errHandler)
102		req.Handlers.Send.PushFront(errHandler)
103	}
104	return req, out
105}
106
107func setReqError(err error) func(*request.Request) {
108	return func(r *request.Request) {
109		r.Error = err
110	}
111}
112
113// PutObject is a wrapper for PutObjectRequest
114//
115// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
116func (c *EncryptionClient) PutObject(input *s3.PutObjectInput) (*s3.PutObjectOutput, error) {
117	if err := validateV1EncryptionClientConstruction(c); err != nil {
118		return nil, err
119	}
120	return putObject(c.getClientOptions(), input)
121}
122
123// PutObjectWithContext is a wrapper for PutObjectRequest with the additional
124// context, and request options support.
125//
126// PutObjectWithContext is the same as PutObject 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.
130// PutObject is a wrapper for PutObjectRequest
131//
132// deprecated: This feature is in maintenance mode, no new updates will be released. Please see https://docs.aws.amazon.com/general/latest/gr/aws_sdk_cryptography.html for more information.
133func (c *EncryptionClient) PutObjectWithContext(ctx aws.Context, input *s3.PutObjectInput, opts ...request.Option) (*s3.PutObjectOutput, error) {
134	if err := validateV1EncryptionClientConstruction(c); err != nil {
135		return nil, err
136	}
137	return putObjectWithContext(c.getClientOptions(), ctx, input, opts...)
138}
139
140func (c *EncryptionClient) getClientOptions() EncryptionClientOptions {
141	return EncryptionClientOptions{
142		S3Client:             c.S3Client,
143		ContentCipherBuilder: c.ContentCipherBuilder,
144		SaveStrategy:         c.SaveStrategy,
145		TempFolderPath:       c.TempFolderPath,
146		MinFileSize:          c.MinFileSize,
147	}
148}
149