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  }