crypto_multipart.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package osscrypto
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "strconv"
  7. "github.com/aliyun/aliyun-oss-go-sdk/oss"
  8. )
  9. // PartCryptoContext save encryption or decryption information
  10. type PartCryptoContext struct {
  11. ContentCipher ContentCipher
  12. DataSize int64
  13. PartSize int64
  14. }
  15. // Valid judge PartCryptoContext is valid or not
  16. func (pcc PartCryptoContext) Valid() bool {
  17. if pcc.ContentCipher == nil || pcc.DataSize == 0 || pcc.PartSize == 0 {
  18. return false
  19. }
  20. return true
  21. }
  22. // InitiateMultipartUpload initializes multipart upload for client encryption
  23. // cryptoContext.PartSize and cryptoContext.DataSize are input parameter
  24. // cryptoContext.PartSize must aligned to the secret iv length
  25. // cryptoContext.ContentCipher is output parameter
  26. // cryptoContext will be used in next API
  27. func (bucket CryptoBucket) InitiateMultipartUpload(objectKey string, cryptoContext *PartCryptoContext, options ...oss.Option) (oss.InitiateMultipartUploadResult, error) {
  28. options = bucket.AddEncryptionUaSuffix(options)
  29. var imur oss.InitiateMultipartUploadResult
  30. if cryptoContext == nil {
  31. return imur, fmt.Errorf("error,cryptoContext is nil")
  32. }
  33. if cryptoContext.PartSize <= 0 {
  34. return imur, fmt.Errorf("invalid PartCryptoContext's PartSize %d", cryptoContext.PartSize)
  35. }
  36. cc, err := bucket.ContentCipherBuilder.ContentCipher()
  37. if err != nil {
  38. return imur, err
  39. }
  40. if cryptoContext.PartSize%int64(cc.GetAlignLen()) != 0 {
  41. return imur, fmt.Errorf("PartCryptoContext's PartSize must be aligned to %d", cc.GetAlignLen())
  42. }
  43. opts := addCryptoHeaders(options, cc.GetCipherData())
  44. if cryptoContext.DataSize > 0 {
  45. opts = append(opts, oss.Meta(OssClientSideEncryptionDataSize, strconv.FormatInt(cryptoContext.DataSize, 10)))
  46. }
  47. opts = append(opts, oss.Meta(OssClientSideEncryptionPartSize, strconv.FormatInt(cryptoContext.PartSize, 10)))
  48. imur, err = bucket.Bucket.InitiateMultipartUpload(objectKey, opts...)
  49. if err == nil {
  50. cryptoContext.ContentCipher = cc
  51. }
  52. return imur, err
  53. }
  54. // UploadPart uploads parts to oss, the part data are encrypted automaticly on client side
  55. // cryptoContext is the input parameter
  56. func (bucket CryptoBucket) UploadPart(imur oss.InitiateMultipartUploadResult, reader io.Reader,
  57. partSize int64, partNumber int, cryptoContext PartCryptoContext, options ...oss.Option) (oss.UploadPart, error) {
  58. options = bucket.AddEncryptionUaSuffix(options)
  59. var uploadPart oss.UploadPart
  60. if cryptoContext.ContentCipher == nil {
  61. return uploadPart, fmt.Errorf("error,cryptoContext is nil or cryptoContext.ContentCipher is nil")
  62. }
  63. if partNumber < 1 {
  64. return uploadPart, fmt.Errorf("partNumber:%d is smaller than 1", partNumber)
  65. }
  66. if cryptoContext.PartSize%int64(cryptoContext.ContentCipher.GetAlignLen()) != 0 {
  67. return uploadPart, fmt.Errorf("PartCryptoContext's PartSize must be aligned to %d", cryptoContext.ContentCipher.GetAlignLen())
  68. }
  69. cipherData := cryptoContext.ContentCipher.GetCipherData().Clone()
  70. // caclulate iv based on part number
  71. if partNumber > 1 {
  72. cipherData.SeekIV(uint64(partNumber-1) * uint64(cryptoContext.PartSize))
  73. }
  74. // for parallel upload part
  75. partCC, _ := cryptoContext.ContentCipher.Clone(cipherData)
  76. cryptoReader, err := partCC.EncryptContent(reader)
  77. if err != nil {
  78. return uploadPart, err
  79. }
  80. request := &oss.UploadPartRequest{
  81. InitResult: &imur,
  82. Reader: cryptoReader,
  83. PartSize: partCC.GetEncryptedLen(partSize),
  84. PartNumber: partNumber,
  85. }
  86. opts := addCryptoHeaders(options, partCC.GetCipherData())
  87. if cryptoContext.DataSize > 0 {
  88. opts = append(opts, oss.Meta(OssClientSideEncryptionDataSize, strconv.FormatInt(cryptoContext.DataSize, 10)))
  89. }
  90. opts = append(opts, oss.Meta(OssClientSideEncryptionPartSize, strconv.FormatInt(cryptoContext.PartSize, 10)))
  91. result, err := bucket.Bucket.DoUploadPart(request, opts)
  92. return result.Part, err
  93. }
  94. // UploadPartFromFile uploads part from the file, the part data are encrypted automaticly on client side
  95. // cryptoContext is the input parameter
  96. func (bucket CryptoBucket) UploadPartFromFile(imur oss.InitiateMultipartUploadResult, filePath string,
  97. startPosition, partSize int64, partNumber int, cryptoContext PartCryptoContext, options ...oss.Option) (oss.UploadPart, error) {
  98. options = bucket.AddEncryptionUaSuffix(options)
  99. var uploadPart = oss.UploadPart{}
  100. if cryptoContext.ContentCipher == nil {
  101. return uploadPart, fmt.Errorf("error,cryptoContext is nil or cryptoContext.ContentCipher is nil")
  102. }
  103. if cryptoContext.PartSize%int64(cryptoContext.ContentCipher.GetAlignLen()) != 0 {
  104. return uploadPart, fmt.Errorf("PartCryptoContext's PartSize must be aligned to %d", cryptoContext.ContentCipher.GetAlignLen())
  105. }
  106. fd, err := os.Open(filePath)
  107. if err != nil {
  108. return uploadPart, err
  109. }
  110. defer fd.Close()
  111. fd.Seek(startPosition, os.SEEK_SET)
  112. if partNumber < 1 {
  113. return uploadPart, fmt.Errorf("partNumber:%d is smaller than 1", partNumber)
  114. }
  115. cipherData := cryptoContext.ContentCipher.GetCipherData().Clone()
  116. // calculate iv based on part number
  117. if partNumber > 1 {
  118. cipherData.SeekIV(uint64(partNumber-1) * uint64(cryptoContext.PartSize))
  119. }
  120. // for parallel upload part
  121. partCC, _ := cryptoContext.ContentCipher.Clone(cipherData)
  122. cryptoReader, err := partCC.EncryptContent(fd)
  123. if err != nil {
  124. return uploadPart, err
  125. }
  126. encryptedLen := partCC.GetEncryptedLen(partSize)
  127. opts := addCryptoHeaders(options, partCC.GetCipherData())
  128. if cryptoContext.DataSize > 0 {
  129. opts = append(opts, oss.Meta(OssClientSideEncryptionDataSize, strconv.FormatInt(cryptoContext.DataSize, 10)))
  130. }
  131. opts = append(opts, oss.Meta(OssClientSideEncryptionPartSize, strconv.FormatInt(cryptoContext.PartSize, 10)))
  132. request := &oss.UploadPartRequest{
  133. InitResult: &imur,
  134. Reader: cryptoReader,
  135. PartSize: encryptedLen,
  136. PartNumber: partNumber,
  137. }
  138. result, err := bucket.Bucket.DoUploadPart(request, opts)
  139. return result.Part, err
  140. }
  141. // UploadPartCopy uploads part copy
  142. func (bucket CryptoBucket) UploadPartCopy(imur oss.InitiateMultipartUploadResult, srcBucketName, srcObjectKey string,
  143. startPosition, partSize int64, partNumber int, cryptoContext PartCryptoContext, options ...oss.Option) (oss.UploadPart, error) {
  144. var uploadPart = oss.UploadPart{}
  145. return uploadPart, fmt.Errorf("CryptoBucket doesn't support UploadPartCopy")
  146. }