@@ -3,11 +3,13 @@ package object
33import (
44 "bytes"
55 "context"
6+ "crypto/md5" //nolint:gosec
67 "encoding/base64"
78 "fmt"
89 "os"
910 "strings"
1011
12+ "github.com/aws/aws-sdk-go-v2/aws"
1113 "github.com/aws/aws-sdk-go-v2/service/s3"
1214 s3Types "github.com/aws/aws-sdk-go-v2/service/s3/types"
1315 "github.com/hashicorp/go-cty/cty"
@@ -105,6 +107,13 @@ func ResourceObject() *schema.Resource {
105107 string (s3Types .ObjectCannedACLPublicRead ),
106108 }, false ),
107109 },
110+ "sse_customer_key" : {
111+ Type : schema .TypeString ,
112+ Optional : true ,
113+ Sensitive : true ,
114+ Description : "Customer's encryption keys to encrypt data (SSE-C)" ,
115+ ValidateFunc : validation .StringLenBetween (32 , 32 ),
116+ },
108117 "region" : regional .Schema (),
109118 "project_id" : account .ProjectIDSchema (),
110119 },
@@ -148,6 +157,16 @@ func resourceObjectCreate(ctx context.Context, d *schema.ResourceData, m interfa
148157 req .ACL = s3Types .ObjectCannedACL (* visibilityStr )
149158 }
150159
160+ if encryptionKeyStr , ok := d .GetOk ("sse_customer_key" ); ok {
161+ digestMD5 , encryption , err := EncryptCustomerKey (encryptionKeyStr .(string ))
162+ if err != nil {
163+ return diag .FromErr (err )
164+ }
165+ req .SSECustomerAlgorithm = scw .StringPtr ("AES256" )
166+ req .SSECustomerKeyMD5 = & digestMD5
167+ req .SSECustomerKey = encryption
168+ }
169+
151170 if filePath , hasFile := d .GetOk ("file" ); hasFile {
152171 file , err := os .Open (filePath .(string ))
153172 if err != nil {
@@ -192,6 +211,19 @@ func resourceObjectCreate(ctx context.Context, d *schema.ResourceData, m interfa
192211 return resourceObjectRead (ctx , d , m )
193212}
194213
214+ func EncryptCustomerKey (encryptionKeyStr string ) (string , * string , error ) {
215+ encryptionKey := []byte (encryptionKeyStr )
216+ h := md5 .New () //nolint:gosec
217+ _ , err := h .Write (encryptionKey )
218+ if err != nil {
219+ return "" , nil , err
220+ }
221+ digest := h .Sum (nil )
222+ digestMD5 := base64 .StdEncoding .EncodeToString (digest )
223+ encryption := aws .String (base64 .StdEncoding .EncodeToString (encryptionKey ))
224+ return digestMD5 , encryption , nil
225+ }
226+
195227func resourceObjectUpdate (ctx context.Context , d * schema.ResourceData , m interface {}) diag.Diagnostics {
196228 s3Client , region , key , bucket , err := s3ClientWithRegionAndNestedName (ctx , d , m , d .Id ())
197229 if err != nil {
@@ -212,7 +244,15 @@ func resourceObjectUpdate(ctx context.Context, d *schema.ResourceData, m interfa
212244 Metadata : types .ExpandMapStringString (d .Get ("metadata" )),
213245 ACL : s3Types .ObjectCannedACL (d .Get ("visibility" ).(string )),
214246 }
215-
247+ if encryptionKey , ok := d .GetOk ("sse_customer_key" ); ok {
248+ digestMD5 , encryption , err := EncryptCustomerKey (encryptionKey .(string ))
249+ if err != nil {
250+ return diag .FromErr (err )
251+ }
252+ req .SSECustomerAlgorithm = scw .StringPtr ("AES256" )
253+ req .SSECustomerKeyMD5 = & digestMD5
254+ req .SSECustomerKey = encryption
255+ }
216256 if filePath , hasFile := d .GetOk ("file" ); hasFile {
217257 file , err := os .Open (filePath .(string ))
218258 if err != nil {
@@ -224,14 +264,24 @@ func resourceObjectUpdate(ctx context.Context, d *schema.ResourceData, m interfa
224264 }
225265 _ , err = s3Client .PutObject (ctx , req )
226266 } else {
227- _ , err = s3Client . CopyObject ( ctx , & s3.CopyObjectInput {
267+ req := & s3.CopyObjectInput {
228268 Bucket : types .ExpandStringPtr (bucketUpdated ),
229269 Key : types .ExpandStringPtr (keyUpdated ),
230270 StorageClass : s3Types .StorageClass (d .Get ("storage_class" ).(string )),
231271 CopySource : scw .StringPtr (fmt .Sprintf ("%s/%s" , bucket , key )),
232272 Metadata : types .ExpandMapStringString (d .Get ("metadata" )),
233273 ACL : s3Types .ObjectCannedACL (d .Get ("visibility" ).(string )),
234- })
274+ }
275+ if encryptionKey , ok := d .GetOk ("sse_customer_key" ); ok {
276+ digestMD5 , encryption , err := EncryptCustomerKey (encryptionKey .(string ))
277+ if err != nil {
278+ return diag .FromErr (err )
279+ }
280+ req .CopySourceSSECustomerAlgorithm = scw .StringPtr ("AES256" )
281+ req .CopySourceSSECustomerKeyMD5 = & digestMD5
282+ req .CopySourceSSECustomerKey = encryption
283+ }
284+ _ , err = s3Client .CopyObject (ctx , req )
235285 }
236286 if err != nil {
237287 return diag .FromErr (err )
@@ -274,10 +324,17 @@ func resourceObjectRead(ctx context.Context, d *schema.ResourceData, m interface
274324 ctx , cancel := context .WithTimeout (ctx , d .Timeout (schema .TimeoutRead ))
275325 defer cancel ()
276326
277- obj , err := s3Client . HeadObject ( ctx , & s3.HeadObjectInput {
327+ req := & s3.HeadObjectInput {
278328 Bucket : types .ExpandStringPtr (bucket ),
279329 Key : types .ExpandStringPtr (key ),
280- })
330+ }
331+
332+ if encryption , ok := d .GetOk ("sse_customer_key" ); ok {
333+ req .SSECustomerKey = aws .String (base64 .StdEncoding .EncodeToString ([]byte (encryption .(string ))))
334+ req .SSECustomerAlgorithm = scw .StringPtr ("AES256" )
335+ }
336+
337+ obj , err := s3Client .HeadObject (ctx , req )
281338 if err != nil {
282339 return diag .FromErr (err )
283340 }
0 commit comments