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

4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. package coscrypto_test
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/aes"
  6. "crypto/cipher"
  7. "crypto/md5"
  8. "crypto/rand"
  9. "encoding/base64"
  10. "fmt"
  11. "io"
  12. "io/ioutil"
  13. mgit.ouxuan.net/tommy/cos-go-sdk-v5
  14. "net/http"
  15. "net/url"
  16. "os"
  17. "sort"
  18. "sync"
  19. "time"
  20. "git.ouxuan.net/tommy/cos-go-sdk-v5"
  21. "github.com/stretchr/testify/assert"
  22. coscrypto "git.ouxuan.net/tommy/cos-go-sdk-v5/crypto"
  23. )
  24. func (s *CosTestSuite) TestMultiUpload_Normal() {
  25. name := "test/ObjectPut" + time.Now().Format(time.RFC3339)
  26. contentLength := int64(1024*1024*10 + 1)
  27. originData := make([]byte, contentLength)
  28. _, err := rand.Read(originData)
  29. cryptoCtx := coscrypto.CryptoContext{
  30. DataSize: contentLength,
  31. PartSize: (contentLength / 16 / 3) * 16,
  32. }
  33. v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx)
  34. assert.Nil(s.T(), err, "Init Failed")
  35. chunks, _, err := cos.SplitSizeIntoChunks(contentLength, cryptoCtx.PartSize)
  36. assert.Nil(s.T(), err, "Split Failed")
  37. optcom := &cos.CompleteMultipartUploadOptions{}
  38. for _, chunk := range chunks {
  39. opt := &cos.ObjectUploadPartOptions{
  40. ContentLength: chunk.Size,
  41. }
  42. f := bytes.NewReader(originData[chunk.OffSet : chunk.OffSet+chunk.Size])
  43. resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chunk.Number, io.LimitReader(f, chunk.Size), opt, &cryptoCtx)
  44. assert.Nil(s.T(), err, "UploadPart failed")
  45. optcom.Parts = append(optcom.Parts, cos.Object{
  46. PartNumber: chunk.Number, ETag: resp.Header.Get("ETag"),
  47. })
  48. }
  49. _, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom)
  50. assert.Nil(s.T(), err, "Complete Failed")
  51. resp, err := s.CClient.Object.Get(context.Background(), name, nil)
  52. assert.Nil(s.T(), err, "GetObject Failed")
  53. defer resp.Body.Close()
  54. decryptedData, _ := ioutil.ReadAll(resp.Body)
  55. assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")
  56. _, err = s.CClient.Object.Delete(context.Background(), name)
  57. assert.Nil(s.T(), err, "DeleteObject Failed")
  58. }
  59. func (s *CosTestSuite) TestMultiUpload_DecryptWithKey() {
  60. name := "test/ObjectPut" + time.Now().Format(time.RFC3339)
  61. contentLength := int64(1024*1024*10 + 1)
  62. originData := make([]byte, contentLength)
  63. _, err := rand.Read(originData)
  64. f := bytes.NewReader(originData)
  65. // 分块上传
  66. cryptoCtx := coscrypto.CryptoContext{
  67. DataSize: contentLength,
  68. PartSize: (contentLength / 16 / 3) * 16,
  69. }
  70. v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx)
  71. assert.Nil(s.T(), err, "Init Failed")
  72. chunks, _, err := cos.SplitSizeIntoChunks(contentLength, cryptoCtx.PartSize)
  73. assert.Nil(s.T(), err, "Split Failed")
  74. optcom := &cos.CompleteMultipartUploadOptions{}
  75. for _, chunk := range chunks {
  76. opt := &cos.ObjectUploadPartOptions{
  77. ContentLength: chunk.Size,
  78. Listener: &cos.DefaultProgressListener{},
  79. }
  80. resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chunk.Number, io.LimitReader(f, chunk.Size), opt, &cryptoCtx)
  81. assert.Nil(s.T(), err, "UploadPart failed")
  82. optcom.Parts = append(optcom.Parts, cos.Object{
  83. PartNumber: chunk.Number, ETag: resp.Header.Get("ETag"),
  84. })
  85. }
  86. _, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom)
  87. assert.Nil(s.T(), err, "Complete Failed")
  88. // 正常读取
  89. resp, err := s.Client.Object.Get(context.Background(), name, nil)
  90. assert.Nil(s.T(), err, "GetObject Failed")
  91. defer resp.Body.Close()
  92. encryptedData, _ := ioutil.ReadAll(resp.Body)
  93. assert.NotEqual(s.T(), bytes.Compare(encryptedData, originData), 0, "encryptedData == originData")
  94. // 获取解密信息
  95. resp, err = s.CClient.Object.Head(context.Background(), name, nil)
  96. assert.Nil(s.T(), err, "HeadObject Failed")
  97. cipherKey := resp.Header.Get(coscrypto.COSClientSideEncryptionKey)
  98. cipherKeybs, err := base64.StdEncoding.DecodeString(cipherKey)
  99. assert.Nil(s.T(), err, "base64 Decode Failed")
  100. cipherIV := resp.Header.Get(coscrypto.COSClientSideEncryptionStart)
  101. cipherIVbs, err := base64.StdEncoding.DecodeString(cipherIV)
  102. assert.Nil(s.T(), err, "base64 Decode Failed")
  103. key, err := s.Master.Decrypt(cipherKeybs)
  104. assert.Nil(s.T(), err, "Master Decrypt Failed")
  105. iv, err := s.Master.Decrypt(cipherIVbs)
  106. assert.Nil(s.T(), err, "Master Decrypt Failed")
  107. // 手动解密
  108. block, err := aes.NewCipher(key)
  109. assert.Nil(s.T(), err, "NewCipher Failed")
  110. decrypter := cipher.NewCTR(block, iv)
  111. decryptedData := make([]byte, len(originData))
  112. decrypter.XORKeyStream(decryptedData, encryptedData)
  113. assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")
  114. _, err = s.CClient.Object.Delete(context.Background(), name)
  115. assert.Nil(s.T(), err, "DeleteObject Failed")
  116. }
  117. func (s *CosTestSuite) TestMultiUpload_PutFromFile() {
  118. name := "test/ObjectPut" + time.Now().Format(time.RFC3339)
  119. filepath := "tmpfile" + time.Now().Format(time.RFC3339)
  120. newfile, err := os.Create(filepath)
  121. assert.Nil(s.T(), err, "Create File Failed")
  122. defer os.Remove(filepath)
  123. contentLength := int64(1024*1024*10 + 1)
  124. originData := make([]byte, contentLength)
  125. _, err = rand.Read(originData)
  126. newfile.Write(originData)
  127. newfile.Close()
  128. m := md5.New()
  129. m.Write(originData)
  130. contentMD5 := m.Sum(nil)
  131. cryptoCtx := coscrypto.CryptoContext{
  132. DataSize: contentLength,
  133. PartSize: (contentLength / 16 / 3) * 16,
  134. }
  135. v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx)
  136. assert.Nil(s.T(), err, "Init Failed")
  137. _, chunks, _, err := cos.SplitFileIntoChunks(filepath, cryptoCtx.PartSize)
  138. assert.Nil(s.T(), err, "Split Failed")
  139. optcom := &cos.CompleteMultipartUploadOptions{}
  140. var wg sync.WaitGroup
  141. var mtx sync.Mutex
  142. for _, chunk := range chunks {
  143. wg.Add(1)
  144. go func(chk cos.Chunk) {
  145. defer wg.Done()
  146. fd, err := os.Open(filepath)
  147. assert.Nil(s.T(), err, "Open File Failed")
  148. opt := &cos.ObjectUploadPartOptions{
  149. ContentLength: chk.Size,
  150. }
  151. fd.Seek(chk.OffSet, os.SEEK_SET)
  152. resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chk.Number, io.LimitReader(fd, chk.Size), opt, &cryptoCtx)
  153. assert.Nil(s.T(), err, "UploadPart failed")
  154. mtx.Lock()
  155. optcom.Parts = append(optcom.Parts, cos.Object{
  156. PartNumber: chk.Number, ETag: resp.Header.Get("ETag"),
  157. })
  158. mtx.Unlock()
  159. }(chunk)
  160. }
  161. wg.Wait()
  162. sort.Sort(cos.ObjectList(optcom.Parts))
  163. _, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom)
  164. assert.Nil(s.T(), err, "Complete Failed")
  165. downfile := "downfile" + time.Now().Format(time.RFC3339)
  166. _, err = s.CClient.Object.GetToFile(context.Background(), name, downfile, nil)
  167. assert.Nil(s.T(), err, "GetObject Failed")
  168. m = md5.New()
  169. fd, err := os.Open(downfile)
  170. assert.Nil(s.T(), err, "Open File Failed")
  171. defer os.Remove(downfile)
  172. defer fd.Close()
  173. io.Copy(m, fd)
  174. downContentMD5 := m.Sum(nil)
  175. assert.Equal(s.T(), bytes.Compare(contentMD5, downContentMD5), 0, "decryptData != originData")
  176. _, err = s.CClient.Object.Delete(context.Background(), name)
  177. assert.Nil(s.T(), err, "DeleteObject Failed")
  178. }
  179. func (s *CosTestSuite) TestMultiUpload_GetWithRange() {
  180. name := "test/ObjectPut" + time.Now().Format(time.RFC3339)
  181. filepath := "tmpfile" + time.Now().Format(time.RFC3339)
  182. newfile, err := os.Create(filepath)
  183. assert.Nil(s.T(), err, "Create File Failed")
  184. defer os.Remove(filepath)
  185. contentLength := int64(1024*1024*10 + 1)
  186. originData := make([]byte, contentLength)
  187. _, err = rand.Read(originData)
  188. newfile.Write(originData)
  189. newfile.Close()
  190. m := md5.New()
  191. m.Write(originData)
  192. contentMD5 := m.Sum(nil)
  193. cryptoCtx := coscrypto.CryptoContext{
  194. DataSize: contentLength,
  195. PartSize: (contentLength / 16 / 3) * 16,
  196. }
  197. iniopt := &cos.InitiateMultipartUploadOptions{
  198. &cos.ACLHeaderOptions{
  199. XCosACL: "private",
  200. },
  201. &cos.ObjectPutHeaderOptions{
  202. ContentMD5: base64.StdEncoding.EncodeToString(contentMD5),
  203. XCosMetaXXX: &http.Header{},
  204. },
  205. }
  206. iniopt.XCosMetaXXX.Add("x-cos-meta-isEncrypted", "true")
  207. v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, iniopt, &cryptoCtx)
  208. assert.Nil(s.T(), err, "Init Failed")
  209. _, chunks, _, err := cos.SplitFileIntoChunks(filepath, cryptoCtx.PartSize)
  210. assert.Nil(s.T(), err, "Split Failed")
  211. optcom := &cos.CompleteMultipartUploadOptions{}
  212. var wg sync.WaitGroup
  213. var mtx sync.Mutex
  214. for _, chunk := range chunks {
  215. wg.Add(1)
  216. go func(chk cos.Chunk) {
  217. defer wg.Done()
  218. fd, err := os.Open(filepath)
  219. assert.Nil(s.T(), err, "Open File Failed")
  220. opt := &cos.ObjectUploadPartOptions{
  221. ContentLength: chk.Size,
  222. }
  223. fd.Seek(chk.OffSet, os.SEEK_SET)
  224. resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chk.Number, io.LimitReader(fd, chk.Size), opt, &cryptoCtx)
  225. assert.Nil(s.T(), err, "UploadPart failed")
  226. mtx.Lock()
  227. optcom.Parts = append(optcom.Parts, cos.Object{
  228. PartNumber: chk.Number, ETag: resp.Header.Get("ETag"),
  229. })
  230. mtx.Unlock()
  231. }(chunk)
  232. }
  233. wg.Wait()
  234. sort.Sort(cos.ObjectList(optcom.Parts))
  235. _, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom)
  236. assert.Nil(s.T(), err, "Complete Failed")
  237. // Range解密读取
  238. for i := 0; i < 10; i++ {
  239. math_rand.Seed(time.Now().UnixNano())
  240. rangeStart := math_rand.Int63n(contentLength)
  241. rangeEnd := rangeStart + math_rand.Int63n(contentLength-rangeStart)
  242. if rangeEnd == rangeStart || rangeStart >= contentLength-1 {
  243. continue
  244. }
  245. opt := &cos.ObjectGetOptions{
  246. Range: fmt.Sprintf("bytes=%v-%v", rangeStart, rangeEnd),
  247. Listener: &cos.DefaultProgressListener{},
  248. }
  249. resp, err := s.CClient.Object.Get(context.Background(), name, opt)
  250. assert.Nil(s.T(), err, "GetObject Failed")
  251. defer resp.Body.Close()
  252. decryptedData, _ := ioutil.ReadAll(resp.Body)
  253. assert.Equal(s.T(), bytes.Compare(originData[rangeStart:rangeEnd+1], decryptedData), 0, "decryptData != originData")
  254. }
  255. opt := &cos.ObjectGetOptions{
  256. Listener: &cos.DefaultProgressListener{},
  257. }
  258. resp, err := s.CClient.Object.Get(context.Background(), name, opt)
  259. assert.Nil(s.T(), err, "GetObject Failed")
  260. assert.Equal(s.T(), resp.Header.Get("x-cos-meta-isEncrypted"), "true", "meta data isn't consistent")
  261. assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionCekAlg), coscrypto.AesCtrAlgorithm, "meta data isn't consistent")
  262. assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionWrapAlg), coscrypto.CosKmsCryptoWrap, "meta data isn't consistent")
  263. assert.Equal(s.T(), resp.Header.Get(coscrypto.COSClientSideEncryptionUnencryptedContentMD5), base64.StdEncoding.EncodeToString(contentMD5), "meta data isn't consistent")
  264. defer resp.Body.Close()
  265. decryptedData, _ := ioutil.ReadAll(resp.Body)
  266. assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")
  267. _, err = s.CClient.Object.Delete(context.Background(), name)
  268. assert.Nil(s.T(), err, "DeleteObject Failed")
  269. }
  270. func (s *CosTestSuite) TestMultiUpload_GetWithNewClient() {
  271. name := "test/ObjectPut" + time.Now().Format(time.RFC3339)
  272. contentLength := int64(1024*1024*10 + 1)
  273. originData := make([]byte, contentLength)
  274. _, err := rand.Read(originData)
  275. cryptoCtx := coscrypto.CryptoContext{
  276. DataSize: contentLength,
  277. PartSize: (contentLength / 16 / 3) * 16,
  278. }
  279. v, _, err := s.CClient.Object.InitiateMultipartUpload(context.Background(), name, nil, &cryptoCtx)
  280. assert.Nil(s.T(), err, "Init Failed")
  281. chunks, _, err := cos.SplitSizeIntoChunks(contentLength, cryptoCtx.PartSize)
  282. assert.Nil(s.T(), err, "Split Failed")
  283. optcom := &cos.CompleteMultipartUploadOptions{}
  284. for _, chunk := range chunks {
  285. opt := &cos.ObjectUploadPartOptions{
  286. ContentLength: chunk.Size,
  287. }
  288. f := bytes.NewReader(originData[chunk.OffSet : chunk.OffSet+chunk.Size])
  289. resp, err := s.CClient.Object.UploadPart(context.Background(), name, v.UploadID, chunk.Number, io.LimitReader(f, chunk.Size), opt, &cryptoCtx)
  290. assert.Nil(s.T(), err, "UploadPart failed")
  291. optcom.Parts = append(optcom.Parts, cos.Object{
  292. PartNumber: chunk.Number, ETag: resp.Header.Get("ETag"),
  293. })
  294. }
  295. _, _, err = s.CClient.Object.CompleteMultipartUpload(context.Background(), name, v.UploadID, optcom)
  296. assert.Nil(s.T(), err, "Complete Failed")
  297. u, _ := url.Parse("https://" + kBucket + ".cos." + kRegion + ".myqcloud.com")
  298. b := &cos.BaseURL{BucketURL: u}
  299. c := cos.NewClient(b, &http.Client{
  300. Transport: &cos.AuthorizationTransport{
  301. SecretID: os.Getenv("COS_SECRETID"),
  302. SecretKey: os.Getenv("COS_SECRETKEY"),
  303. },
  304. })
  305. {
  306. // 使用不同的MatDesc客户端读取, 期待错误
  307. material := make(map[string]string)
  308. material["desc"] = "cos crypto suite test 2"
  309. kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), kRegion)
  310. master, _ := coscrypto.CreateMasterKMS(kmsclient, os.Getenv("KMSID"), material)
  311. client := coscrypto.NewCryptoClient(c, master)
  312. resp, err := client.Object.Get(context.Background(), name, nil)
  313. assert.Nil(s.T(), resp, "Get Object Failed")
  314. assert.NotNil(s.T(), err, "Get Object Failed")
  315. }
  316. {
  317. // 使用相同的MatDesc客户端读取, 但KMSID不一样,期待正确,kms解密是不需要KMSID
  318. material := make(map[string]string)
  319. material["desc"] = "cos crypto suite test"
  320. kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), kRegion)
  321. master, _ := coscrypto.CreateMasterKMS(kmsclient, "KMSID", material)
  322. client := coscrypto.NewCryptoClient(c, master)
  323. resp, err := client.Object.Get(context.Background(), name, nil)
  324. assert.Nil(s.T(), err, "Get Object Failed")
  325. defer resp.Body.Close()
  326. decryptedData, _ := ioutil.ReadAll(resp.Body)
  327. assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")
  328. }
  329. {
  330. // 使用相同的MatDesc客户端读取, 地域不一样,期待错误
  331. material := make(map[string]string)
  332. material["desc"] = "cos crypto suite test"
  333. diffRegion := "ap-shanghai"
  334. if diffRegion == kRegion {
  335. diffRegion = "ap-guangzhou"
  336. }
  337. kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), diffRegion)
  338. master, _ := coscrypto.CreateMasterKMS(kmsclient, "KMSID", material)
  339. client := coscrypto.NewCryptoClient(c, master)
  340. resp, err := client.Object.Get(context.Background(), name, nil)
  341. assert.Nil(s.T(), resp, "Get Object Failed")
  342. assert.NotNil(s.T(), err, "Get Object Failed")
  343. }
  344. {
  345. // 使用相同的MatDesc和KMSID客户端读取, 期待正确
  346. material := make(map[string]string)
  347. material["desc"] = "cos crypto suite test"
  348. kmsclient, _ := coscrypto.NewKMSClient(c.GetCredential(), kRegion)
  349. master, _ := coscrypto.CreateMasterKMS(kmsclient, os.Getenv("KMSID"), material)
  350. client := coscrypto.NewCryptoClient(c, master)
  351. resp, err := client.Object.Get(context.Background(), name, nil)
  352. assert.Nil(s.T(), err, "Get Object Failed")
  353. defer resp.Body.Close()
  354. decryptedData, _ := ioutil.ReadAll(resp.Body)
  355. assert.Equal(s.T(), bytes.Compare(originData, decryptedData), 0, "decryptData != originData")
  356. }
  357. _, err = s.CClient.Object.Delete(context.Background(), name)
  358. assert.Nil(s.T(), err, "DeleteObject Failed")
  359. }