1package s3crypto 2 3import ( 4 "encoding/base64" 5 "encoding/hex" 6 "io" 7 "strings" 8 9 "github.com/aws/aws-sdk-go/aws" 10 "github.com/aws/aws-sdk-go/aws/awserr" 11 "github.com/aws/aws-sdk-go/aws/request" 12 "github.com/aws/aws-sdk-go/internal/sdkio" 13 "github.com/aws/aws-sdk-go/service/s3" 14) 15 16func putObjectRequest(c EncryptionClientOptions, input *s3.PutObjectInput) (*request.Request, *s3.PutObjectOutput) { 17 req, out := c.S3Client.PutObjectRequest(input) 18 19 // Get Size of file 20 n, err := aws.SeekerLen(input.Body) 21 if err != nil { 22 req.Error = err 23 return req, out 24 } 25 26 dst, err := getWriterStore(req, c.TempFolderPath, n >= c.MinFileSize) 27 if err != nil { 28 req.Error = err 29 return req, out 30 } 31 32 req.Handlers.Build.PushFront(func(r *request.Request) { 33 if err != nil { 34 r.Error = err 35 return 36 } 37 var encryptor ContentCipher 38 if v, ok := c.ContentCipherBuilder.(ContentCipherBuilderWithContext); ok { 39 encryptor, err = v.ContentCipherWithContext(r.Context()) 40 } else { 41 encryptor, err = c.ContentCipherBuilder.ContentCipher() 42 } 43 if err != nil { 44 r.Error = err 45 return 46 } 47 48 md5 := newMD5Reader(input.Body) 49 sha := newSHA256Writer(dst) 50 reader, err := encryptor.EncryptContents(md5) 51 if err != nil { 52 r.Error = err 53 return 54 } 55 56 _, err = io.Copy(sha, reader) 57 if err != nil { 58 r.Error = err 59 return 60 } 61 62 data := encryptor.GetCipherData() 63 env, err := encodeMeta(md5, data) 64 if err != nil { 65 r.Error = err 66 return 67 } 68 69 shaHex := hex.EncodeToString(sha.GetValue()) 70 req.HTTPRequest.Header.Set("X-Amz-Content-Sha256", shaHex) 71 72 dst.Seek(0, sdkio.SeekStart) 73 input.Body = dst 74 75 err = c.SaveStrategy.Save(env, r) 76 r.Error = err 77 }) 78 79 return req, out 80} 81 82func putObject(options EncryptionClientOptions, input *s3.PutObjectInput) (*s3.PutObjectOutput, error) { 83 req, out := putObjectRequest(options, input) 84 return out, req.Send() 85} 86 87func putObjectWithContext(options EncryptionClientOptions, ctx aws.Context, input *s3.PutObjectInput, opts ...request.Option) (*s3.PutObjectOutput, error) { 88 req, out := putObjectRequest(options, input) 89 req.SetContext(ctx) 90 req.ApplyOptions(opts...) 91 return out, req.Send() 92} 93 94func getObjectRequest(options DecryptionClientOptions, input *s3.GetObjectInput) (*request.Request, *s3.GetObjectOutput) { 95 req, out := options.S3Client.GetObjectRequest(input) 96 req.Handlers.Unmarshal.PushBack(func(r *request.Request) { 97 env, err := options.LoadStrategy.Load(r) 98 if err != nil { 99 r.Error = err 100 out.Body.Close() 101 return 102 } 103 104 // If KMS should return the correct CEK algorithm with the proper 105 // KMS key provider 106 cipher, err := contentCipherFromEnvelope(options, r.Context(), env) 107 if err != nil { 108 r.Error = err 109 out.Body.Close() 110 return 111 } 112 113 reader, err := cipher.DecryptContents(out.Body) 114 if err != nil { 115 r.Error = err 116 out.Body.Close() 117 return 118 } 119 out.Body = reader 120 }) 121 return req, out 122} 123 124func getObject(options DecryptionClientOptions, input *s3.GetObjectInput) (*s3.GetObjectOutput, error) { 125 req, out := getObjectRequest(options, input) 126 return out, req.Send() 127} 128 129func getObjectWithContext(options DecryptionClientOptions, ctx aws.Context, input *s3.GetObjectInput, opts ...request.Option) (*s3.GetObjectOutput, error) { 130 req, out := getObjectRequest(options, input) 131 req.SetContext(ctx) 132 req.ApplyOptions(opts...) 133 return out, req.Send() 134} 135 136func contentCipherFromEnvelope(options DecryptionClientOptions, ctx aws.Context, env Envelope) (ContentCipher, error) { 137 wrap, err := wrapFromEnvelope(options, env) 138 if err != nil { 139 return nil, err 140 } 141 142 return cekFromEnvelope(options, ctx, env, wrap) 143} 144 145func wrapFromEnvelope(options DecryptionClientOptions, env Envelope) (CipherDataDecrypter, error) { 146 f, ok := options.WrapRegistry[env.WrapAlg] 147 if !ok || f == nil { 148 return nil, awserr.New( 149 "InvalidWrapAlgorithmError", 150 "wrap algorithm isn't supported, "+env.WrapAlg, 151 nil, 152 ) 153 } 154 return f(env) 155} 156 157func cekFromEnvelope(options DecryptionClientOptions, ctx aws.Context, env Envelope, decrypter CipherDataDecrypter) (ContentCipher, error) { 158 f, ok := options.CEKRegistry[env.CEKAlg] 159 if !ok || f == nil { 160 return nil, awserr.New( 161 "InvalidCEKAlgorithmError", 162 "cek algorithm isn't supported, "+env.CEKAlg, 163 nil, 164 ) 165 } 166 167 key, err := base64.StdEncoding.DecodeString(env.CipherKey) 168 if err != nil { 169 return nil, err 170 } 171 172 iv, err := base64.StdEncoding.DecodeString(env.IV) 173 if err != nil { 174 return nil, err 175 } 176 177 if d, ok := decrypter.(CipherDataDecrypterWithContext); ok { 178 key, err = d.DecryptKeyWithContext(ctx, key) 179 } else { 180 key, err = decrypter.DecryptKey(key) 181 } 182 183 if err != nil { 184 return nil, err 185 } 186 187 cd := CipherData{ 188 Key: key, 189 IV: iv, 190 CEKAlgorithm: env.CEKAlg, 191 Padder: getPadder(options, env.CEKAlg), 192 } 193 return f(cd) 194} 195 196// getPadder will return an unpadder with checking the cek algorithm specific padder. 197// If there wasn't a cek algorithm specific padder, we check the padder itself. 198// We return a no unpadder, if no unpadder was found. This means any customization 199// either contained padding within the cipher implementation, and to maintain 200// backwards compatility we will simply not unpad anything. 201func getPadder(options DecryptionClientOptions, cekAlg string) Padder { 202 padder, ok := options.PadderRegistry[cekAlg] 203 if !ok { 204 padder, ok = options.PadderRegistry[cekAlg[strings.LastIndex(cekAlg, "/")+1:]] 205 if !ok { 206 return NoPadder 207 } 208 } 209 return padder 210} 211