crypto_bucket.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. package osscrypto
  2. import (
  3. "encoding/base64"
  4. "encoding/json"
  5. "fmt"
  6. "hash"
  7. "hash/crc64"
  8. "io"
  9. "net/http"
  10. "os"
  11. "strconv"
  12. kms "github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
  13. "github.com/aliyun/aliyun-oss-go-sdk/oss"
  14. )
  15. // MasterCipherManager is interface for getting master key with MatDesc(material desc)
  16. // If you may use different master keys for encrypting and decrypting objects,each master
  17. // key must have a unique, non-emtpy, unalterable MatDesc(json string format) and you must provide this interface
  18. // If you always use the same master key for encrypting and decrypting objects, MatDesc
  19. // can be empty and you don't need to provide this interface
  20. //
  21. // matDesc map[string]string:is converted by matDesc json string
  22. // return: []string the secret key information,such as {"rsa-public-key","rsa-private-key"} or {"non-rsa-key"}
  23. type MasterCipherManager interface {
  24. GetMasterKey(matDesc map[string]string) ([]string, error)
  25. }
  26. // ExtraCipherBuilder is interface for creating a decrypt ContentCipher with Envelope
  27. // If the objects you need to decrypt are neither encrypted with ContentCipherBuilder
  28. // you provided, nor encrypted with rsa and ali kms master keys, you must provide this interface
  29. //
  30. // ContentCipher the interface used to decrypt objects
  31. type ExtraCipherBuilder interface {
  32. GetDecryptCipher(envelope Envelope, cm MasterCipherManager) (ContentCipher, error)
  33. }
  34. // CryptoBucketOption CryptoBucket option such as SetAliKmsClient, SetMasterCipherManager, SetDecryptCipherManager.
  35. type CryptoBucketOption func(*CryptoBucket)
  36. // SetAliKmsClient set field AliKmsClient of CryptoBucket
  37. // If the objects you need to decrypt are encrypted with ali kms master key,but not with ContentCipherBuilder
  38. // you provided, you must provide this interface
  39. func SetAliKmsClient(client *kms.Client) CryptoBucketOption {
  40. return func(bucket *CryptoBucket) {
  41. bucket.AliKmsClient = client
  42. }
  43. }
  44. // SetMasterCipherManager set field MasterCipherManager of CryptoBucket
  45. func SetMasterCipherManager(manager MasterCipherManager) CryptoBucketOption {
  46. return func(bucket *CryptoBucket) {
  47. bucket.MasterCipherManager = manager
  48. }
  49. }
  50. // SetExtraCipherBuilder set field ExtraCipherBuilder of CryptoBucket
  51. func SetExtraCipherBuilder(extraBuilder ExtraCipherBuilder) CryptoBucketOption {
  52. return func(bucket *CryptoBucket) {
  53. bucket.ExtraCipherBuilder = extraBuilder
  54. }
  55. }
  56. // DefaultExtraCipherBuilder is Default implementation of the ExtraCipherBuilder for rsa and kms master keys
  57. type DefaultExtraCipherBuilder struct {
  58. AliKmsClient *kms.Client
  59. }
  60. // GetDecryptCipher is used to get ContentCipher for decrypt object
  61. func (decb *DefaultExtraCipherBuilder) GetDecryptCipher(envelope Envelope, cm MasterCipherManager) (ContentCipher, error) {
  62. if cm == nil {
  63. return nil, fmt.Errorf("DefaultExtraCipherBuilder GetDecryptCipher error,MasterCipherManager is nil")
  64. }
  65. if envelope.CEKAlg != AesCtrAlgorithm {
  66. return nil, fmt.Errorf("DefaultExtraCipherBuilder GetDecryptCipher error,not supported content algorithm %s", envelope.CEKAlg)
  67. }
  68. if envelope.WrapAlg != RsaCryptoWrap && envelope.WrapAlg != KmsAliCryptoWrap {
  69. return nil, fmt.Errorf("DefaultExtraCipherBuilder GetDecryptCipher error,not supported envelope wrap algorithm %s", envelope.WrapAlg)
  70. }
  71. matDesc := make(map[string]string)
  72. if envelope.MatDesc != "" {
  73. err := json.Unmarshal([]byte(envelope.MatDesc), &matDesc)
  74. if err != nil {
  75. return nil, err
  76. }
  77. }
  78. masterKeys, err := cm.GetMasterKey(matDesc)
  79. if err != nil {
  80. return nil, err
  81. }
  82. var contentCipher ContentCipher
  83. if envelope.WrapAlg == RsaCryptoWrap {
  84. // for rsa master key
  85. if len(masterKeys) != 2 {
  86. return nil, fmt.Errorf("rsa keys count must be 2,now is %d", len(masterKeys))
  87. }
  88. rsaCipher, err := CreateMasterRsa(matDesc, masterKeys[0], masterKeys[1])
  89. if err != nil {
  90. return nil, err
  91. }
  92. aesCtrBuilder := CreateAesCtrCipher(rsaCipher)
  93. contentCipher, err = aesCtrBuilder.ContentCipherEnv(envelope)
  94. } else if envelope.WrapAlg == KmsAliCryptoWrap {
  95. // for kms master key
  96. if len(masterKeys) != 1 {
  97. return nil, fmt.Errorf("non-rsa keys count must be 1,now is %d", len(masterKeys))
  98. }
  99. if decb.AliKmsClient == nil {
  100. return nil, fmt.Errorf("aliyun kms client is nil")
  101. }
  102. kmsCipher, err := CreateMasterAliKms(matDesc, masterKeys[0], decb.AliKmsClient)
  103. if err != nil {
  104. return nil, err
  105. }
  106. aesCtrBuilder := CreateAesCtrCipher(kmsCipher)
  107. contentCipher, err = aesCtrBuilder.ContentCipherEnv(envelope)
  108. } else {
  109. // to do
  110. // for master keys which are neither rsa nor kms
  111. }
  112. return contentCipher, err
  113. }
  114. // CryptoBucket implements the operations for encrypting and decrypting objects
  115. // ContentCipherBuilder is used to encrypt and decrypt objects by default
  116. // when the object's MatDesc which you want to decrypt is emtpy or same to the
  117. // master key's MatDesc you provided in ContentCipherBuilder, sdk try to
  118. // use ContentCipherBuilder to decrypt
  119. type CryptoBucket struct {
  120. oss.Bucket
  121. ContentCipherBuilder ContentCipherBuilder
  122. ExtraCipherBuilder ExtraCipherBuilder
  123. MasterCipherManager MasterCipherManager
  124. AliKmsClient *kms.Client
  125. }
  126. // GetCryptoBucket create a client encyrption bucket
  127. func GetCryptoBucket(client *oss.Client, bucketName string, builder ContentCipherBuilder,
  128. options ...CryptoBucketOption) (*CryptoBucket, error) {
  129. var cryptoBucket CryptoBucket
  130. cryptoBucket.Client = *client
  131. cryptoBucket.BucketName = bucketName
  132. cryptoBucket.ContentCipherBuilder = builder
  133. for _, option := range options {
  134. option(&cryptoBucket)
  135. }
  136. if cryptoBucket.ExtraCipherBuilder == nil {
  137. cryptoBucket.ExtraCipherBuilder = &DefaultExtraCipherBuilder{AliKmsClient: cryptoBucket.AliKmsClient}
  138. }
  139. return &cryptoBucket, nil
  140. }
  141. // PutObject creates a new object and encyrpt it on client side when uploading to oss
  142. func (bucket CryptoBucket) PutObject(objectKey string, reader io.Reader, options ...oss.Option) error {
  143. options = bucket.AddEncryptionUaSuffix(options)
  144. cc, err := bucket.ContentCipherBuilder.ContentCipher()
  145. if err != nil {
  146. return err
  147. }
  148. cryptoReader, err := cc.EncryptContent(reader)
  149. if err != nil {
  150. return err
  151. }
  152. var request *oss.PutObjectRequest
  153. srcLen, err := oss.GetReaderLen(reader)
  154. if err != nil {
  155. request = &oss.PutObjectRequest{
  156. ObjectKey: objectKey,
  157. Reader: cryptoReader,
  158. }
  159. } else {
  160. encryptedLen := cc.GetEncryptedLen(srcLen)
  161. request = &oss.PutObjectRequest{
  162. ObjectKey: objectKey,
  163. Reader: oss.LimitReadCloser(cryptoReader, encryptedLen),
  164. }
  165. }
  166. opts := addCryptoHeaders(options, cc.GetCipherData())
  167. resp, err := bucket.DoPutObject(request, opts)
  168. if err != nil {
  169. return err
  170. }
  171. defer resp.Body.Close()
  172. return err
  173. }
  174. // GetObject downloads the object from oss
  175. // If the object is encrypted, sdk decrypt it automaticly
  176. func (bucket CryptoBucket) GetObject(objectKey string, options ...oss.Option) (io.ReadCloser, error) {
  177. options = bucket.AddEncryptionUaSuffix(options)
  178. result, err := bucket.DoGetObject(&oss.GetObjectRequest{ObjectKey: objectKey}, options)
  179. if err != nil {
  180. return nil, err
  181. }
  182. return result.Response, nil
  183. }
  184. // GetObjectToFile downloads the object from oss to local file
  185. // If the object is encrypted, sdk decrypt it automaticly
  186. func (bucket CryptoBucket) GetObjectToFile(objectKey, filePath string, options ...oss.Option) error {
  187. options = bucket.AddEncryptionUaSuffix(options)
  188. tempFilePath := filePath + oss.TempFileSuffix
  189. // Calls the API to actually download the object. Returns the result instance.
  190. result, err := bucket.DoGetObject(&oss.GetObjectRequest{ObjectKey: objectKey}, options)
  191. if err != nil {
  192. return err
  193. }
  194. defer result.Response.Close()
  195. // If the local file does not exist, create a new one. If it exists, overwrite it.
  196. fd, err := os.OpenFile(tempFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, oss.FilePermMode)
  197. if err != nil {
  198. return err
  199. }
  200. // Copy the data to the local file path.
  201. _, err = io.Copy(fd, result.Response.Body)
  202. fd.Close()
  203. if err != nil {
  204. return err
  205. }
  206. // Compares the CRC value
  207. hasRange, _, _ := oss.IsOptionSet(options, oss.HTTPHeaderRange)
  208. encodeOpt, _ := oss.FindOption(options, oss.HTTPHeaderAcceptEncoding, nil)
  209. acceptEncoding := ""
  210. if encodeOpt != nil {
  211. acceptEncoding = encodeOpt.(string)
  212. }
  213. if bucket.GetConfig().IsEnableCRC && !hasRange && acceptEncoding != "gzip" {
  214. result.Response.ClientCRC = result.ClientCRC.Sum64()
  215. err = oss.CheckCRC(result.Response, "GetObjectToFile")
  216. if err != nil {
  217. os.Remove(tempFilePath)
  218. return err
  219. }
  220. }
  221. return os.Rename(tempFilePath, filePath)
  222. }
  223. // DoGetObject is the actual API that gets the encrypted or not encrypted object.
  224. // It's the internal function called by other public APIs.
  225. func (bucket CryptoBucket) DoGetObject(request *oss.GetObjectRequest, options []oss.Option) (*oss.GetObjectResult, error) {
  226. options = bucket.AddEncryptionUaSuffix(options)
  227. // first,we must head object
  228. metaInfo, err := bucket.GetObjectDetailedMeta(request.ObjectKey)
  229. if err != nil {
  230. return nil, err
  231. }
  232. isEncryptedObj := isEncryptedObject(metaInfo)
  233. if !isEncryptedObj {
  234. return bucket.Bucket.DoGetObject(request, options)
  235. }
  236. envelope, err := getEnvelopeFromHeader(metaInfo)
  237. if err != nil {
  238. return nil, err
  239. }
  240. if !isValidContentAlg(envelope.CEKAlg) {
  241. return nil, fmt.Errorf("not supported content algorithm %s,object:%s", envelope.CEKAlg, request.ObjectKey)
  242. }
  243. if !envelope.IsValid() {
  244. return nil, fmt.Errorf("getEnvelopeFromHeader error,object:%s", request.ObjectKey)
  245. }
  246. // use ContentCipherBuilder to decrpt object by default
  247. encryptMatDesc := bucket.ContentCipherBuilder.GetMatDesc()
  248. var cc ContentCipher
  249. err = nil
  250. if envelope.MatDesc == encryptMatDesc {
  251. cc, err = bucket.ContentCipherBuilder.ContentCipherEnv(envelope)
  252. } else {
  253. cc, err = bucket.ExtraCipherBuilder.GetDecryptCipher(envelope, bucket.MasterCipherManager)
  254. }
  255. if err != nil {
  256. return nil, fmt.Errorf("%s,object:%s", err.Error(), request.ObjectKey)
  257. }
  258. discardFrontAlignLen := int64(0)
  259. uRange, err := oss.GetRangeConfig(options)
  260. if err != nil {
  261. return nil, err
  262. }
  263. if uRange != nil && uRange.HasStart {
  264. // process range to align key size
  265. adjustStart := adjustRangeStart(uRange.Start, cc)
  266. discardFrontAlignLen = uRange.Start - adjustStart
  267. if discardFrontAlignLen > 0 {
  268. uRange.Start = adjustStart
  269. options = oss.DeleteOption(options, oss.HTTPHeaderRange)
  270. options = append(options, oss.NormalizedRange(oss.GetRangeString(*uRange)))
  271. }
  272. // seek iv
  273. cipherData := cc.GetCipherData().Clone()
  274. cipherData.SeekIV(uint64(adjustStart))
  275. cc, _ = cc.Clone(cipherData)
  276. }
  277. params, _ := oss.GetRawParams(options)
  278. resp, err := bucket.Do("GET", request.ObjectKey, params, options, nil, nil)
  279. if err != nil {
  280. return nil, err
  281. }
  282. result := &oss.GetObjectResult{
  283. Response: resp,
  284. }
  285. // CRC
  286. var crcCalc hash.Hash64
  287. hasRange, _, _ := oss.IsOptionSet(options, oss.HTTPHeaderRange)
  288. if bucket.GetConfig().IsEnableCRC && !hasRange {
  289. crcCalc = crc64.New(oss.CrcTable())
  290. result.ServerCRC = resp.ServerCRC
  291. result.ClientCRC = crcCalc
  292. }
  293. // Progress
  294. listener := oss.GetProgressListener(options)
  295. contentLen, _ := strconv.ParseInt(resp.Headers.Get(oss.HTTPHeaderContentLength), 10, 64)
  296. resp.Body = oss.TeeReader(resp.Body, crcCalc, contentLen, listener, nil)
  297. resp.Body, err = cc.DecryptContent(resp.Body)
  298. if err == nil && discardFrontAlignLen > 0 {
  299. resp.Body = &oss.DiscardReadCloser{
  300. RC: resp.Body,
  301. Discard: int(discardFrontAlignLen)}
  302. }
  303. return result, err
  304. }
  305. // PutObjectFromFile creates a new object from the local file
  306. // the object will be encrypted automaticly on client side when uploaded to oss
  307. func (bucket CryptoBucket) PutObjectFromFile(objectKey, filePath string, options ...oss.Option) error {
  308. options = bucket.AddEncryptionUaSuffix(options)
  309. fd, err := os.Open(filePath)
  310. if err != nil {
  311. return err
  312. }
  313. defer fd.Close()
  314. opts := oss.AddContentType(options, filePath, objectKey)
  315. cc, err := bucket.ContentCipherBuilder.ContentCipher()
  316. if err != nil {
  317. return err
  318. }
  319. cryptoReader, err := cc.EncryptContent(fd)
  320. if err != nil {
  321. return err
  322. }
  323. var request *oss.PutObjectRequest
  324. srcLen, err := oss.GetReaderLen(fd)
  325. if err != nil {
  326. request = &oss.PutObjectRequest{
  327. ObjectKey: objectKey,
  328. Reader: cryptoReader,
  329. }
  330. } else {
  331. encryptedLen := cc.GetEncryptedLen(srcLen)
  332. request = &oss.PutObjectRequest{
  333. ObjectKey: objectKey,
  334. Reader: oss.LimitReadCloser(cryptoReader, encryptedLen),
  335. }
  336. }
  337. opts = addCryptoHeaders(opts, cc.GetCipherData())
  338. resp, err := bucket.DoPutObject(request, opts)
  339. if err != nil {
  340. return err
  341. }
  342. defer resp.Body.Close()
  343. return nil
  344. }
  345. // AppendObject please refer to Bucket.AppendObject
  346. func (bucket CryptoBucket) AppendObject(objectKey string, reader io.Reader, appendPosition int64, options ...oss.Option) (int64, error) {
  347. return 0, fmt.Errorf("CryptoBucket doesn't support AppendObject")
  348. }
  349. // DoAppendObject please refer to Bucket.DoAppendObject
  350. func (bucket CryptoBucket) DoAppendObject(request *oss.AppendObjectRequest, options []oss.Option) (*oss.AppendObjectResult, error) {
  351. return nil, fmt.Errorf("CryptoBucket doesn't support DoAppendObject")
  352. }
  353. // PutObjectWithURL please refer to Bucket.PutObjectWithURL
  354. func (bucket CryptoBucket) PutObjectWithURL(signedURL string, reader io.Reader, options ...oss.Option) error {
  355. return fmt.Errorf("CryptoBucket doesn't support PutObjectWithURL")
  356. }
  357. // PutObjectFromFileWithURL please refer to Bucket.PutObjectFromFileWithURL
  358. func (bucket CryptoBucket) PutObjectFromFileWithURL(signedURL, filePath string, options ...oss.Option) error {
  359. return fmt.Errorf("CryptoBucket doesn't support PutObjectFromFileWithURL")
  360. }
  361. // DoPutObjectWithURL please refer to Bucket.DoPutObjectWithURL
  362. func (bucket CryptoBucket) DoPutObjectWithURL(signedURL string, reader io.Reader, options []oss.Option) (*oss.Response, error) {
  363. return nil, fmt.Errorf("CryptoBucket doesn't support DoPutObjectWithURL")
  364. }
  365. // GetObjectWithURL please refer to Bucket.GetObjectWithURL
  366. func (bucket CryptoBucket) GetObjectWithURL(signedURL string, options ...oss.Option) (io.ReadCloser, error) {
  367. return nil, fmt.Errorf("CryptoBucket doesn't support GetObjectWithURL")
  368. }
  369. // GetObjectToFileWithURL please refer to Bucket.GetObjectToFileWithURL
  370. func (bucket CryptoBucket) GetObjectToFileWithURL(signedURL, filePath string, options ...oss.Option) error {
  371. return fmt.Errorf("CryptoBucket doesn't support GetObjectToFileWithURL")
  372. }
  373. // DoGetObjectWithURL please refer to Bucket.DoGetObjectWithURL
  374. func (bucket CryptoBucket) DoGetObjectWithURL(signedURL string, options []oss.Option) (*oss.GetObjectResult, error) {
  375. return nil, fmt.Errorf("CryptoBucket doesn't support DoGetObjectWithURL")
  376. }
  377. // ProcessObject please refer to Bucket.ProcessObject
  378. func (bucket CryptoBucket) ProcessObject(objectKey string, process string, options ...oss.Option) (oss.ProcessObjectResult, error) {
  379. var out oss.ProcessObjectResult
  380. return out, fmt.Errorf("CryptoBucket doesn't support ProcessObject")
  381. }
  382. func (bucket CryptoBucket) AddEncryptionUaSuffix(options []oss.Option) []oss.Option {
  383. var outOption []oss.Option
  384. bSet, _, _ := oss.IsOptionSet(options, oss.HTTPHeaderUserAgent)
  385. if bSet || bucket.Client.Config.UserSetUa {
  386. outOption = options
  387. return outOption
  388. }
  389. outOption = append(options, oss.UserAgentHeader(bucket.Client.Config.UserAgent+"/"+EncryptionUaSuffix))
  390. return outOption
  391. }
  392. // isEncryptedObject judge the object is encrypted or not
  393. func isEncryptedObject(headers http.Header) bool {
  394. encrptedKey := headers.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionKey)
  395. return len(encrptedKey) > 0
  396. }
  397. // addCryptoHeaders save Envelope information in oss meta
  398. func addCryptoHeaders(options []oss.Option, cd *CipherData) []oss.Option {
  399. opts := []oss.Option{}
  400. // convert content-md5
  401. md5Option, _ := oss.FindOption(options, oss.HTTPHeaderContentMD5, nil)
  402. if md5Option != nil {
  403. opts = append(opts, oss.Meta(OssClientSideEncryptionUnencryptedContentMD5, md5Option.(string)))
  404. options = oss.DeleteOption(options, oss.HTTPHeaderContentMD5)
  405. }
  406. // convert content-length
  407. lenOption, _ := oss.FindOption(options, oss.HTTPHeaderContentLength, nil)
  408. if lenOption != nil {
  409. opts = append(opts, oss.Meta(OssClientSideEncryptionUnencryptedContentLength, lenOption.(string)))
  410. options = oss.DeleteOption(options, oss.HTTPHeaderContentLength)
  411. }
  412. opts = append(opts, options...)
  413. // matDesc
  414. if cd.MatDesc != "" {
  415. opts = append(opts, oss.Meta(OssClientSideEncryptionMatDesc, cd.MatDesc))
  416. }
  417. // encrypted key
  418. strEncryptedKey := base64.StdEncoding.EncodeToString(cd.EncryptedKey)
  419. opts = append(opts, oss.Meta(OssClientSideEncryptionKey, strEncryptedKey))
  420. // encrypted iv
  421. strEncryptedIV := base64.StdEncoding.EncodeToString(cd.EncryptedIV)
  422. opts = append(opts, oss.Meta(OssClientSideEncryptionStart, strEncryptedIV))
  423. // wrap alg
  424. opts = append(opts, oss.Meta(OssClientSideEncryptionWrapAlg, cd.WrapAlgorithm))
  425. // cek alg
  426. opts = append(opts, oss.Meta(OssClientSideEncryptionCekAlg, cd.CEKAlgorithm))
  427. return opts
  428. }
  429. func getEnvelopeFromHeader(header http.Header) (Envelope, error) {
  430. var envelope Envelope
  431. envelope.IV = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionStart)
  432. decodedIV, err := base64.StdEncoding.DecodeString(envelope.IV)
  433. if err != nil {
  434. return envelope, err
  435. }
  436. envelope.IV = string(decodedIV)
  437. envelope.CipherKey = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionKey)
  438. decodedKey, err := base64.StdEncoding.DecodeString(envelope.CipherKey)
  439. if err != nil {
  440. return envelope, err
  441. }
  442. envelope.CipherKey = string(decodedKey)
  443. envelope.MatDesc = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionMatDesc)
  444. envelope.WrapAlg = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionWrapAlg)
  445. envelope.CEKAlg = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionCekAlg)
  446. envelope.UnencryptedMD5 = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionUnencryptedContentMD5)
  447. envelope.UnencryptedContentLen = header.Get(oss.HTTPHeaderOssMetaPrefix + OssClientSideEncryptionUnencryptedContentLength)
  448. return envelope, err
  449. }
  450. func isValidContentAlg(algName string) bool {
  451. // now content encyrption only support aec/ctr algorithm
  452. return algName == AesCtrAlgorithm
  453. }
  454. func adjustRangeStart(start int64, cc ContentCipher) int64 {
  455. alignLen := int64(cc.GetAlignLen())
  456. return (start / alignLen) * alignLen
  457. }