strategy.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. package s3crypto
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "io/ioutil"
  6. "net/http"
  7. "strings"
  8. "github.com/aws/aws-sdk-go/aws"
  9. "github.com/aws/aws-sdk-go/aws/awserr"
  10. "github.com/aws/aws-sdk-go/aws/request"
  11. "github.com/aws/aws-sdk-go/service/s3"
  12. )
  13. // SaveStrategy is how the data's metadata wants to be saved
  14. type SaveStrategy interface {
  15. Save(Envelope, *request.Request) error
  16. }
  17. // S3SaveStrategy will save the metadata to a separate instruction file in S3
  18. type S3SaveStrategy struct {
  19. Client *s3.S3
  20. InstructionFileSuffix string
  21. }
  22. // Save will save the envelope contents to s3.
  23. func (strat S3SaveStrategy) Save(env Envelope, req *request.Request) error {
  24. input := req.Params.(*s3.PutObjectInput)
  25. b, err := json.Marshal(env)
  26. if err != nil {
  27. return err
  28. }
  29. instInput := s3.PutObjectInput{
  30. Bucket: input.Bucket,
  31. Body: bytes.NewReader(b),
  32. }
  33. if strat.InstructionFileSuffix == "" {
  34. instInput.Key = aws.String(*input.Key + DefaultInstructionKeySuffix)
  35. } else {
  36. instInput.Key = aws.String(*input.Key + strat.InstructionFileSuffix)
  37. }
  38. _, err = strat.Client.PutObject(&instInput)
  39. return err
  40. }
  41. // HeaderV2SaveStrategy will save the metadata of the crypto contents to the header of
  42. // the object.
  43. type HeaderV2SaveStrategy struct{}
  44. // Save will save the envelope to the request's header.
  45. func (strat HeaderV2SaveStrategy) Save(env Envelope, req *request.Request) error {
  46. input := req.Params.(*s3.PutObjectInput)
  47. if input.Metadata == nil {
  48. input.Metadata = map[string]*string{}
  49. }
  50. input.Metadata[http.CanonicalHeaderKey(keyV2Header)] = &env.CipherKey
  51. input.Metadata[http.CanonicalHeaderKey(ivHeader)] = &env.IV
  52. input.Metadata[http.CanonicalHeaderKey(matDescHeader)] = &env.MatDesc
  53. input.Metadata[http.CanonicalHeaderKey(wrapAlgorithmHeader)] = &env.WrapAlg
  54. input.Metadata[http.CanonicalHeaderKey(cekAlgorithmHeader)] = &env.CEKAlg
  55. input.Metadata[http.CanonicalHeaderKey(tagLengthHeader)] = &env.TagLen
  56. input.Metadata[http.CanonicalHeaderKey(unencryptedMD5Header)] = &env.UnencryptedMD5
  57. input.Metadata[http.CanonicalHeaderKey(unencryptedContentLengthHeader)] = &env.UnencryptedContentLen
  58. return nil
  59. }
  60. // LoadStrategy ...
  61. type LoadStrategy interface {
  62. Load(*request.Request) (Envelope, error)
  63. }
  64. // S3LoadStrategy will load the instruction file from s3
  65. type S3LoadStrategy struct {
  66. Client *s3.S3
  67. InstructionFileSuffix string
  68. }
  69. // Load from a given instruction file suffix
  70. func (load S3LoadStrategy) Load(req *request.Request) (Envelope, error) {
  71. env := Envelope{}
  72. if load.InstructionFileSuffix == "" {
  73. load.InstructionFileSuffix = DefaultInstructionKeySuffix
  74. }
  75. input := req.Params.(*s3.GetObjectInput)
  76. out, err := load.Client.GetObject(&s3.GetObjectInput{
  77. Key: aws.String(strings.Join([]string{*input.Key, load.InstructionFileSuffix}, "")),
  78. Bucket: input.Bucket,
  79. })
  80. if err != nil {
  81. return env, err
  82. }
  83. b, err := ioutil.ReadAll(out.Body)
  84. if err != nil {
  85. return env, err
  86. }
  87. err = json.Unmarshal(b, &env)
  88. return env, err
  89. }
  90. // HeaderV2LoadStrategy will load the envelope from the metadata
  91. type HeaderV2LoadStrategy struct{}
  92. // Load from a given object's header
  93. func (load HeaderV2LoadStrategy) Load(req *request.Request) (Envelope, error) {
  94. env := Envelope{}
  95. env.CipherKey = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, keyV2Header}, "-"))
  96. env.IV = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, ivHeader}, "-"))
  97. env.MatDesc = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, matDescHeader}, "-"))
  98. env.WrapAlg = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, wrapAlgorithmHeader}, "-"))
  99. env.CEKAlg = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, cekAlgorithmHeader}, "-"))
  100. env.TagLen = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, tagLengthHeader}, "-"))
  101. env.UnencryptedMD5 = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, unencryptedMD5Header}, "-"))
  102. env.UnencryptedContentLen = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, unencryptedContentLengthHeader}, "-"))
  103. return env, nil
  104. }
  105. type defaultV2LoadStrategy struct {
  106. client *s3.S3
  107. suffix string
  108. }
  109. func (load defaultV2LoadStrategy) Load(req *request.Request) (Envelope, error) {
  110. if value := req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, keyV2Header}, "-")); value != "" {
  111. strat := HeaderV2LoadStrategy{}
  112. return strat.Load(req)
  113. } else if value = req.HTTPResponse.Header.Get(strings.Join([]string{metaHeader, keyV1Header}, "-")); value != "" {
  114. return Envelope{}, awserr.New("V1NotSupportedError", "The AWS SDK for Go does not support version 1", nil)
  115. }
  116. strat := S3LoadStrategy{
  117. Client: load.client,
  118. InstructionFileSuffix: load.suffix,
  119. }
  120. return strat.Load(req)
  121. }