github.com/aavshr/aws-sdk-go@v1.41.3/service/s3/s3crypto/shared_client.go (about)

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