You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

392 lines
15 KiB

package coscrypto_test
import (
"bytes"
"context"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"io/ioutil"
mgit.ouxuan.net/tommy/cos-go-sdk-v5
"net/http"
"net/url"
"os"
"sort"
"sync"
"time"
"git.ouxuan.net/tommy/cos-go-sdk-v5"
"github.com/stretchr/testify/assert"
coscrypto "git.ouxuan.net/tommy/cos-go-sdk-v5/crypto"
)
func (s *CosTestSuite) TestMultiUpload_Normal() {
name := "test/ObjectPut" + time.Now().Format(time.RFC3339)
contentLength := int64(1024*1024*10 + 1)
originData := make([]byte, contentLength)
_, err := rand.Read(originData)
cryptoCtx := coscrypto.CryptoContext{
DataSize: contentLength,
PartSize: (contentLength / 16 / 3) * 16,
}
v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx)
assert.Nil(s.T(), err, "Init Failed")
chunks, _, err := cos.SplitSizeIntoChunks(contentLength, cryptoCtx.PartSize)
assert.Nil(s.T(), err, "Split Failed")
optcom := &cos.CompleteMultipartUploadOptions{}
for _, chunk := range chunks {
opt := &cos.ObjectUploadPartOptions{
ContentLength: chunk.Size,
}
f := bytes.NewReader(originData[chunk.OffSet : chunk.OffSet+chunk.Size])
resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chunk.Number, io.LimitReader(f, chunk.Size), opt, &cryptoCtx)
assert.Nil(s.T(), err, "UploadPart failed")
optcom.Parts = append(optcom.Parts, cos.Object{
PartNumber: chunk.Number, ETag: resp.Header.Get("ETag"),
})
}
_, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom)
assert.Nil(s.T(), err, "Complete Failed")
resp, err := s.CClient.Object.Get(context.Background(), name, nil)
assert.Nil(s.T(), err, "GetObject Failed")
defer resp.Body.Close()
decryptedData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")
_, err = s.CClient.Object.Delete(context.Background(), name)
assert.Nil(s.T(), err, "DeleteObject Failed")
}
func (s *CosTestSuite) TestMultiUpload_DecryptWithKey() {
name := "test/ObjectPut" + time.Now().Format(time.RFC3339)
contentLength := int64(1024*1024*10 + 1)
originData := make([]byte, contentLength)
_, err := rand.Read(originData)
f := bytes.NewReader(originData)
// 分块上传
cryptoCtx := coscrypto.CryptoContext{
DataSize: contentLength,
PartSize: (contentLength / 16 / 3) * 16,
}
v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx)
assert.Nil(s.T(), err, "Init Failed")
chunks, _, err := cos.SplitSizeIntoChunks(contentLength, cryptoCtx.PartSize)
assert.Nil(s.T(), err, "Split Failed")
optcom := &cos.CompleteMultipartUploadOptions{}
for _, chunk := range chunks {
opt := &cos.ObjectUploadPartOptions{
ContentLength: chunk.Size,
Listener: &cos.DefaultProgressListener{},
}
resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chunk.Number, io.LimitReader(f, chunk.Size), opt, &cryptoCtx)
assert.Nil(s.T(), err, "UploadPart failed")
optcom.Parts = append(optcom.Parts, cos.Object{
PartNumber: chunk.Number, ETag: resp.Header.Get("ETag"),
})
}
_, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom)
assert.Nil(s.T(), err, "Complete Failed")
// 正常读取
resp, err := s.Client.Object.Get(context.Background(), name, nil)
assert.Nil(s.T(), err, "GetObject Failed")
defer resp.Body.Close()
encryptedData, _ := ioutil.ReadAll(resp.Body)
assert.NotEqual(s.T(), bytes.Compare(encryptedData, originData), 0, "encryptedData == originData")
// 获取解密信息
resp, err = s.CClient.Object.Head(context.Background(), name, nil)
assert.Nil(s.T(), err, "HeadObject Failed")
cipherKey := resp.Header.Get(coscrypto.COSClientSideEncryptionKey)
cipherKeybs, err := base64.StdEncoding.DecodeString(cipherKey)
assert.Nil(s.T(), err, "base64 Decode Failed")
cipherIV := resp.Header.Get(coscrypto.COSClientSideEncryptionStart)
cipherIVbs, err := base64.StdEncoding.DecodeString(cipherIV)
assert.Nil(s.T(), err, "base64 Decode Failed")
key, err := s.Master.Decrypt(cipherKeybs)
assert.Nil(s.T(), err, "Master Decrypt Failed")
iv, err := s.Master.Decrypt(cipherIVbs)
assert.Nil(s.T(), err, "Master Decrypt Failed")
// 手动解密
block, err := aes.NewCipher(key)
assert.Nil(s.T(), err, "NewCipher Failed")
decrypter := cipher.NewCTR(block, iv)
decryptedData := make([]byte, len(originData))
decrypter.XORKeyStream(decryptedData, encryptedData)
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")
_, err = s.CClient.Object.Delete(context.Background(), name)
assert.Nil(s.T(), err, "DeleteObject Failed")
}
func (s *CosTestSuite) TestMultiUpload_PutFromFile() {
name := "test/ObjectPut" + time.Now().Format(time.RFC3339)
filepath := "tmpfile" + time.Now().Format(time.RFC3339)
newfile, err := os.Create(filepath)
assert.Nil(s.T(), err, "Create File Failed")
defer os.Remove(filepath)
contentLength := int64(1024*1024*10 + 1)
originData := make([]byte, contentLength)
_, err = rand.Read(originData)
newfile.Write(originData)
newfile.Close()
m := md5.New()
m.Write(originData)
contentMD5 := m.Sum(nil)
cryptoCtx := coscrypto.CryptoContext{
DataSize: contentLength,
PartSize: (contentLength / 16 / 3) * 16,
}
v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx)
assert.Nil(s.T(), err, "Init Failed")
_, chunks, _, err := cos.SplitFileIntoChunks(filepath, cryptoCtx.PartSize)
assert.Nil(s.T(), err, "Split Failed")
optcom := &cos.CompleteMultipartUploadOptions{}
var wg sync.WaitGroup
var mtx sync.Mutex
for _, chunk := range chunks {
wg.Add(1)
go func(chk cos.Chunk) {
defer wg.Done()
fd, err := os.Open(filepath)
assert.Nil(s.T(), err, "Open File Failed")
opt := &cos.ObjectUploadPartOptions{
ContentLength: chk.Size,
}
fd.Seek(chk.OffSet, os.SEEK_SET)
resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chk.Number, io.LimitReader(fd, chk.Size), opt, &cryptoCtx)
assert.Nil(s.T(), err, "UploadPart failed")
mtx.Lock()
optcom.Parts = append(optcom.Parts, cos.Object{
PartNumber: chk.Number, ETag: resp.Header.Get("ETag"),
})
mtx.Unlock()
}(chunk)
}
wg.Wait()
sort.Sort(cos.ObjectList(optcom.Parts))
_, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom)
assert.Nil(s.T(), err, "Complete Failed")
downfile := "downfile" + time.Now().Format(time.RFC3339)
_, err = s.CClient.Object.GetToFile(context.Background(), name, downfile, nil)
assert.Nil(s.T(), err, "GetObject Failed")
m = md5.New()
fd, err := os.Open(downfile)
assert.Nil(s.T(), err, "Open File Failed")
defer os.Remove(downfile)
defer fd.Close()
io.Copy(m, fd)
downContentMD5 := m.Sum(nil)
assert.Equal(s.T(), bytes.Compare(contentMD5, downContentMD5), 0, "decryptData != originData")
_, err = s.CClient.Object.Delete(context.Background(), name)
assert.Nil(s.T(), err, "DeleteObject Failed")
}
func (s *CosTestSuite) TestMultiUpload_GetWithRange() {
name := "test/ObjectPut" + time.Now().Format(time.RFC3339)
filepath := "tmpfile" + time.Now().Format(time.RFC3339)
newfile, err := os.Create(filepath)
assert.Nil(s.T(), err, "Create File Failed")
defer os.Remove(filepath)
contentLength := int64(1024*1024*10 + 1)
originData := make([]byte, contentLength)
_, err = rand.Read(originData)
newfile.Write(originData)
newfile.Close()
m := md5.New()
m.Write(originData)
contentMD5 := m.Sum(nil)
cryptoCtx := coscrypto.CryptoContext{
DataSize: contentLength,
PartSize: (contentLength / 16 / 3) * 16,
}
iniopt := &cos.InitiateMultipartUploadOptions{
&cos.ACLHeaderOptions{
XCosACL: "private",
},
&cos.ObjectPutHeaderOptions{
ContentMD5: base64.StdEncoding.EncodeToString(contentMD5),
XCosMetaXXX: &http.Header{},
},
}
iniopt.XCosMetaXXX.Add("x-cos-meta-isEncrypted", "true")
v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, iniopt, &cryptoCtx)
assert.Nil(s.T(), err, "Init Failed")
_, chunks, _, err := cos.SplitFileIntoChunks(filepath, cryptoCtx.PartSize)
assert.Nil(s.T(), err, "Split Failed")
optcom := &cos.CompleteMultipartUploadOptions{}
var wg sync.WaitGroup
var mtx sync.Mutex
for _, chunk := range chunks {
wg.Add(1)
go func(chk cos.Chunk) {
defer wg.Done()
fd, err := os.Open(filepath)
assert.Nil(s.T(), err, "Open File Failed")
opt := &cos.ObjectUploadPartOptions{
ContentLength: chk.Size,
}
fd.Seek(chk.OffSet, os.SEEK_SET)
resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chk.Number, io.LimitReader(fd, chk.Size), opt, &cryptoCtx)
assert.Nil(s.T(), err, "UploadPart failed")
mtx.Lock()
optcom.Parts = append(optcom.Parts, cos.Object{
PartNumber: chk.Number, ETag: resp.Header.Get("ETag"),
})
mtx.Unlock()
}(chunk)
}
wg.Wait()
sort.Sort(cos.ObjectList(optcom.Parts))
_, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom)
assert.Nil(s.T(), err, "Complete Failed")
// Range解密读取
for i := 0; i < 10; i++ {
math_rand.Seed(time.Now().UnixNano())
rangeStart := math_rand.Int63n(contentLength)
rangeEnd := rangeStart + math_rand.Int63n(contentLength-rangeStart)
if rangeEnd == rangeStart || rangeStart >= contentLength-1 {
continue
}
opt := &cos.ObjectGetOptions{
Range: fmt.Sprintf("bytes=%v-%v", rangeStart, rangeEnd),
Listener: &cos.DefaultProgressListener{},
}
resp, err := s.CClient.Object.Get(context.Background(), name, opt)
assert.Nil(s.T(), err, "GetObject Failed")
defer resp.Body.Close()
decryptedData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), bytes.Compare(originData[rangeStart:rangeEnd+1], decryptedData), 0, "decryptData != originData")
}
opt := &cos.ObjectGetOptions{
Listener: &cos.DefaultProgressListener{},
}
resp, err := s.CClient.Object.Get(context.Background(), name, opt)
assert.Nil(s.T(), err, "GetObject Failed")
assert.Equal(s.T(), resp.Header.Get("x-cos-meta-isEncrypted"), "true", "meta data isn't consistent")
assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionCekAlg), coscrypto.AesCtrAlgorithm, "meta data isn't consistent")
assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionWrapAlg), coscrypto.CosKmsCryptoWrap, "meta data isn't consistent")
assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionUnencryptedContentMD5), base64.StdEncoding.EncodeToString(contentMD5), "meta data isn't consistent")
defer resp.Body.Close()
decryptedData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")
_, err = s.CClient.Object.Delete(context.Background(), name)
assert.Nil(s.T(), err, "DeleteObject Failed")
}
func (s *CosTestSuite) TestMultiUpload_GetWithNewClient() {
name := "test/ObjectPut" + time.Now().Format(time.RFC3339)
contentLength := int64(1024*1024*10 + 1)
originData := make([]byte, contentLength)
_, err := rand.Read(originData)
cryptoCtx := coscrypto.CryptoContext{
DataSize: contentLength,
PartSize: (contentLength / 16 / 3) * 16,
}
v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx)
assert.Nil(s.T(), err, "Init Failed")
chunks, _, err := cos.SplitSizeIntoChunks(contentLength, cryptoCtx.PartSize)
assert.Nil(s.T(), err, "Split Failed")
optcom := &cos.CompleteMultipartUploadOptions{}
for _, chunk := range chunks {
opt := &cos.ObjectUploadPartOptions{
ContentLength: chunk.Size,
}
f := bytes.NewReader(originData[chunk.OffSet : chunk.OffSet+chunk.Size])
resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chunk.Number, io.LimitReader(f, chunk.Size), opt, &cryptoCtx)
assert.Nil(s.T(), err, "UploadPart failed")
optcom.Parts = append(optcom.Parts, cos.Object{
PartNumber: chunk.Number, ETag: resp.Header.Get("ETag"),
})
}
_, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom)
assert.Nil(s.T(), err, "Complete Failed")
u, _ := url.Parse("https://" + kBucket + ".cos." + kRegion + ".myqcloud.com")
b := &cos.BaseURL{BucketURL: u}
c := cos.NewClient(b, &http.Client{
Transport: &cos.AuthorizationTransport{
SecretID: os.Getenv("COS_SECRETID"),
SecretKey: os.Getenv("COS_SECRETKEY"),
},
})
{
// 使用不同的MatDesc客户端读取, 期待错误
material := make(map[string]string)
material["desc"] = "cos crypto suite test 2"
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), kRegion)
master, _ := coscrypto.CreateMasterKMS(kmsclient, os.Getenv("KMSID"), material)
client := coscrypto.NewCryptoClient(c, master)
resp, err := client.Object.Get(context.Background(), name, nil)
assert.Nil(s.T(), resp, "Get Object Failed")
assert.NotNil(s.T(), err, "Get Object Failed")
}
{
// 使用相同的MatDesc客户端读取, 但KMSID不一样,期待正确,kms解密是不需要KMSID
material := make(map[string]string)
material["desc"] = "cos crypto suite test"
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), kRegion)
master, _ := coscrypto.CreateMasterKMS(kmsclient, "KMSID", material)
client := coscrypto.NewCryptoClient(c, master)
resp, err := client.Object.Get(context.Background(), name, nil)
assert.Nil(s.T(), err, "Get Object Failed")
defer resp.Body.Close()
decryptedData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")
}
{
// 使用相同的MatDesc客户端读取, 地域不一样,期待错误
material := make(map[string]string)
material["desc"] = "cos crypto suite test"
diffRegion := "ap-shanghai"
if diffRegion == kRegion {
diffRegion = "ap-guangzhou"
}
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), diffRegion)
master, _ := coscrypto.CreateMasterKMS(kmsclient, "KMSID", material)
client := coscrypto.NewCryptoClient(c, master)
resp, err := client.Object.Get(context.Background(), name, nil)
assert.Nil(s.T(), resp, "Get Object Failed")
assert.NotNil(s.T(), err, "Get Object Failed")
}
{
// 使用相同的MatDesc和KMSID客户端读取, 期待正确
material := make(map[string]string)
material["desc"] = "cos crypto suite test"
kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), kRegion)
master, _ := coscrypto.CreateMasterKMS(kmsclient, os.Getenv("KMSID"), material)
client := coscrypto.NewCryptoClient(c, master)
resp, err := client.Object.Get(context.Background(), name, nil)
assert.Nil(s.T(), err, "Get Object Failed")
defer resp.Body.Close()
decryptedData, _ := ioutil.ReadAll(resp.Body)
assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")
}
_, err = s.CClient.Object.Delete(context.Background(), name)
assert.Nil(s.T(), err, "DeleteObject Failed")
}