crypto_multipart.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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. var imur oss.InitiateMultipartUploadResult
  29. if cryptoContext == nil {
  30. return imur, fmt.Errorf("error,cryptoContext is nil")
  31. }
  32. if cryptoContext.PartSize <= 0 {
  33. return imur, fmt.Errorf("invalid PartCryptoContext's PartSize %d", cryptoContext.PartSize)
  34. }
  35. cc, err := bucket.ContentCipherBuilder.ContentCipher()
  36. if err != nil {
  37. return imur, err
  38. }
  39. if cryptoContext.PartSize%int64(cc.GetAlignLen()) != 0 {
  40. return imur, fmt.Errorf("PartCryptoContext's PartSize must be aligned to %d", cc.GetAlignLen())
  41. }
  42. opts := addCryptoHeaders(options, cc.GetCipherData())
  43. if cryptoContext.DataSize > 0 {
  44. opts = append(opts, oss.Meta(OssClientSideEncryptionDataSize, strconv.FormatInt(cryptoContext.DataSize, 10)))
  45. }
  46. opts = append(opts, oss.Meta(OssClientSideEncryptionPartSize, strconv.FormatInt(cryptoContext.PartSize, 10)))
  47. imur, err = bucket.Bucket.InitiateMultipartUpload(objectKey, opts...)
  48. if err == nil {
  49. cryptoContext.ContentCipher = cc
  50. }
  51. return imur, err
  52. }
  53. // UploadPart uploads parts to oss, the part data are encrypted automaticly on client side
  54. // cryptoContext is the input parameter
  55. func (bucket CryptoBucket) UploadPart(imur oss.InitiateMultipartUploadResult, reader io.Reader,
  56. partSize int64, partNumber int, cryptoContext PartCryptoContext, options ...oss.Option) (oss.UploadPart, error) {
  57. var uploadPart oss.UploadPart
  58. if cryptoContext.ContentCipher == nil {
  59. return uploadPart, fmt.Errorf("error,cryptoContext is nil or cryptoContext.ContentCipher is nil")
  60. }
  61. if partNumber < 1 {
  62. return uploadPart, fmt.Errorf("partNumber:%d is smaller than 1", partNumber)
  63. }
  64. if cryptoContext.PartSize%int64(cryptoContext.ContentCipher.GetAlignLen()) != 0 {
  65. return uploadPart, fmt.Errorf("PartCryptoContext's PartSize must be aligned to %d", cryptoContext.ContentCipher.GetAlignLen())
  66. }
  67. cipherData := cryptoContext.ContentCipher.GetCipherData().Clone()
  68. // caclulate iv based on part number
  69. if partNumber > 1 {
  70. cipherData.SeekIV(uint64(partNumber-1) * uint64(cryptoContext.PartSize))
  71. }
  72. // for parallel upload part
  73. partCC, _ := cryptoContext.ContentCipher.Clone(cipherData)
  74. cryptoReader, err := partCC.EncryptContent(reader)
  75. if err != nil {
  76. return uploadPart, err
  77. }
  78. request := &oss.UploadPartRequest{
  79. InitResult: &imur,
  80. Reader: cryptoReader,
  81. PartSize: partCC.GetEncryptedLen(partSize),
  82. PartNumber: partNumber,
  83. }
  84. opts := addCryptoHeaders(options, partCC.GetCipherData())
  85. if cryptoContext.DataSize >= 0 {
  86. opts = append(opts, oss.Meta(OssClientSideEncryptionDataSize, strconv.FormatInt(cryptoContext.DataSize, 10)))
  87. }
  88. opts = append(opts, oss.Meta(OssClientSideEncryptionPartSize, strconv.FormatInt(cryptoContext.PartSize, 10)))
  89. result, err := bucket.Bucket.DoUploadPart(request, opts)
  90. return result.Part, err
  91. }
  92. // UploadPartFromFile uploads part from the file, the part data are encrypted automaticly on client side
  93. // cryptoContext is the input parameter
  94. func (bucket CryptoBucket) UploadPartFromFile(imur oss.InitiateMultipartUploadResult, filePath string,
  95. startPosition, partSize int64, partNumber int, cryptoContext PartCryptoContext, options ...oss.Option) (oss.UploadPart, error) {
  96. var uploadPart = oss.UploadPart{}
  97. if cryptoContext.ContentCipher == nil {
  98. return uploadPart, fmt.Errorf("error,cryptoContext is nil or cryptoContext.ContentCipher is nil")
  99. }
  100. if cryptoContext.PartSize%int64(cryptoContext.ContentCipher.GetAlignLen()) != 0 {
  101. return uploadPart, fmt.Errorf("PartCryptoContext's PartSize must be aligned to %d", cryptoContext.ContentCipher.GetAlignLen())
  102. }
  103. fd, err := os.Open(filePath)
  104. if err != nil {
  105. return uploadPart, err
  106. }
  107. defer fd.Close()
  108. fd.Seek(startPosition, os.SEEK_SET)
  109. if partNumber < 1 {
  110. return uploadPart, fmt.Errorf("partNumber:%d is smaller than 1", partNumber)
  111. }
  112. cipherData := cryptoContext.ContentCipher.GetCipherData().Clone()
  113. // calculate iv based on part number
  114. if partNumber > 1 {
  115. cipherData.SeekIV(uint64(partNumber-1) * uint64(cryptoContext.PartSize))
  116. }
  117. // for parallel upload part
  118. partCC, _ := cryptoContext.ContentCipher.Clone(cipherData)
  119. cryptoReader, err := partCC.EncryptContent(fd)
  120. if err != nil {
  121. return uploadPart, err
  122. }
  123. encryptedLen := partCC.GetEncryptedLen(partSize)
  124. opts := addCryptoHeaders(options, partCC.GetCipherData())
  125. if cryptoContext.DataSize >= 0 {
  126. opts = append(opts, oss.Meta(OssClientSideEncryptionDataSize, strconv.FormatInt(cryptoContext.DataSize, 10)))
  127. }
  128. opts = append(opts, oss.Meta(OssClientSideEncryptionPartSize, strconv.FormatInt(cryptoContext.PartSize, 10)))
  129. request := &oss.UploadPartRequest{
  130. InitResult: &imur,
  131. Reader: cryptoReader,
  132. PartSize: encryptedLen,
  133. PartNumber: partNumber,
  134. }
  135. result, err := bucket.Bucket.DoUploadPart(request, opts)
  136. return result.Part, err
  137. }
  138. // UploadPartCopy uploads part copy
  139. func (bucket CryptoBucket) UploadPartCopy(imur oss.InitiateMultipartUploadResult, srcBucketName, srcObjectKey string,
  140. startPosition, partSize int64, partNumber int, cryptoContext PartCryptoContext, options ...oss.Option) (oss.UploadPart, error) {
  141. var uploadPart = oss.UploadPart{}
  142. return uploadPart, fmt.Errorf("CryptoBucket doesn't support UploadPartCopy")
  143. }