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