1package s3crypto
2
3import (
4	"github.com/aws/aws-sdk-go/aws"
5	"github.com/aws/aws-sdk-go/aws/client"
6	"github.com/aws/aws-sdk-go/aws/request"
7	"github.com/aws/aws-sdk-go/service/s3"
8	"github.com/aws/aws-sdk-go/service/s3/s3iface"
9)
10
11// DecryptionClientV2 is an S3 crypto client. The decryption client
12// will handle all get object requests from Amazon S3.
13// Supported key wrapping algorithms:
14//	* AWS KMS
15//	* AWS KMS + Context
16//
17// Supported content ciphers:
18//	* AES/GCM
19//	* AES/CBC
20type DecryptionClientV2 struct {
21	options DecryptionClientOptions
22}
23
24// DecryptionClientOptions is the configuration options for DecryptionClientV2.
25type DecryptionClientOptions struct {
26	S3Client s3iface.S3API
27	// LoadStrategy is used to load the metadata either from the metadata of the object
28	// or from a separate file in s3.
29	//
30	// Defaults to our default load strategy.
31	LoadStrategy LoadStrategy
32
33	CryptoRegistry *CryptoRegistry
34}
35
36// NewDecryptionClientV2 instantiates a new DecryptionClientV2. The NewDecryptionClientV2 must be configured with the
37// desired key wrapping and content encryption algorithms that are required to be read by the client. These algorithms
38// are registered by providing the client a CryptoRegistry that has been constructed with the desired configuration.
39// NewDecryptionClientV2 will return an error if no key wrapping or content encryption algorithms have been provided.
40//
41// Example:
42//	sess := session.Must(session.NewSession())
43//	cr := s3crypto.NewCryptoRegistry()
44// 	if err := s3crypto.RegisterKMSContextWrapWithAnyCMK(cr, kms.New(sess)); err != nil {
45//		panic(err) // handle error
46//	}
47//	if err := s3crypto.RegisterAESGCMContentCipher(cr); err != nil {
48//		panic(err) // handle error
49//	}
50//	svc, err := s3crypto.NewDecryptionClientV2(sess, cr, func(o *s3crypto.DecryptionClientOptions) {
51//		// Custom client options here
52//	})
53//	if err != nil {
54//		panic(err) // handle error
55//	}
56func NewDecryptionClientV2(
57	prov client.ConfigProvider, cryptoRegistry *CryptoRegistry,
58	options ...func(clientOptions *DecryptionClientOptions),
59) (*DecryptionClientV2, error) {
60	s3client := s3.New(prov)
61
62	s3client.Handlers.Build.PushBack(func(r *request.Request) {
63		request.AddToUserAgent(r, "S3CryptoV2")
64	})
65
66	clientOptions := &DecryptionClientOptions{
67		S3Client: s3client,
68		LoadStrategy: defaultV2LoadStrategy{
69			client: s3client,
70		},
71		CryptoRegistry: cryptoRegistry,
72	}
73	for _, option := range options {
74		option(clientOptions)
75	}
76
77	if err := cryptoRegistry.valid(); err != nil {
78		return nil, err
79	}
80
81	return &DecryptionClientV2{options: *clientOptions}, nil
82}
83
84// GetObjectRequest will make a request to s3 and retrieve the object. In this process
85// decryption will be done. The SDK only supports V2 reads of KMS and GCM.
86//
87// Example:
88//	req, out := svc.GetObjectRequest(&s3.GetObjectInput {
89//	  Key: aws.String("testKey"),
90//	  Bucket: aws.String("testBucket"),
91//	})
92//	err := req.Send()
93func (c *DecryptionClientV2) GetObjectRequest(input *s3.GetObjectInput) (*request.Request, *s3.GetObjectOutput) {
94	return getObjectRequest(c.options, input)
95}
96
97// GetObject is a wrapper for GetObjectRequest
98func (c *DecryptionClientV2) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOutput, error) {
99	req, out := getObjectRequest(c.options, input)
100	return out, req.Send()
101}
102
103// GetObjectWithContext is a wrapper for GetObjectRequest with the additional
104// context, and request options support.
105//
106// GetObjectWithContext is the same as GetObject with the additional support for
107// Context input parameters. The Context must not be nil. A nil Context will
108// cause a panic. Use the Context to add deadlining, timeouts, etc. In the future
109// this may create sub-contexts for individual underlying requests.
110func (c *DecryptionClientV2) GetObjectWithContext(ctx aws.Context, input *s3.GetObjectInput, opts ...request.Option) (*s3.GetObjectOutput, error) {
111	req, out := getObjectRequest(c.options, input)
112	req.SetContext(ctx)
113	req.ApplyOptions(opts...)
114	return out, req.Send()
115}
116