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