package coscrypto import ( "crypto/rand" "encoding/binary" "fmt" "io" math_rand "math/rand" "time" ) const ( COSClientSideEncryptionKey string = "x-cos-meta-client-side-encryption-key" COSClientSideEncryptionStart = "x-cos-meta-client-side-encryption-start" COSClientSideEncryptionCekAlg = "x-cos-meta-client-side-encryption-cek-alg" COSClientSideEncryptionWrapAlg = "x-cos-meta-client-side-encryption-wrap-alg" COSClientSideEncryptionMatDesc = "x-cos-meta-client-side-encryption-matdesc" COSClientSideEncryptionUnencryptedContentLength = "x-cos-meta-client-side-encryption-unencrypted-content-length" COSClientSideEncryptionUnencryptedContentMD5 = "x-cos-meta-client-side-encryption-unencrypted-content-md5" COSClientSideEncryptionDataSize = "x-cos-meta-client-side-encryption-data-size" COSClientSideEncryptionPartSize = "x-cos-meta-client-side-encryption-part-size" UserAgent = "User-Agent" ) const ( CosKmsCryptoWrap = "KMS/TencentCloud" AesCtrAlgorithm = "AES/CTR/NoPadding" EncryptionUaSuffix = "COSEncryptionClient" ) type MasterCipher interface { Encrypt([]byte) ([]byte, error) Decrypt([]byte) ([]byte, error) GetWrapAlgorithm() string GetMatDesc() string } type ContentCipherBuilder interface { ContentCipher() (ContentCipher, error) ContentCipherEnv(Envelope) (ContentCipher, error) GetMatDesc() string } type ContentCipher interface { EncryptContent(io.Reader) (io.ReadCloser, error) DecryptContent(io.Reader) (io.ReadCloser, error) Clone(cd CipherData) (ContentCipher, error) GetEncryptedLen(int64) int64 GetCipherData() *CipherData GetAlignLen() int } type Envelope struct { IV string CipherKey string MatDesc string WrapAlg string CEKAlg string UnencryptedMD5 string UnencryptedContentLen string } func (el Envelope) IsValid() bool { return len(el.IV) > 0 && len(el.CipherKey) > 0 && len(el.WrapAlg) > 0 && len(el.CEKAlg) > 0 } func (el Envelope) String() string { return fmt.Sprintf("IV=%s&CipherKey=%s&WrapAlg=%s&CEKAlg=%s", el.IV, el.CipherKey, el.WrapAlg, el.CEKAlg) } type CipherData struct { IV []byte Key []byte MatDesc string WrapAlgorithm string CEKAlgorithm string EncryptedIV []byte EncryptedKey []byte } func (cd *CipherData) RandomKeyIv(keyLen int, ivLen int) error { math_rand.Seed(time.Now().UnixNano()) // Key cd.Key = make([]byte, keyLen) if _, err := io.ReadFull(rand.Reader, cd.Key); err != nil { return err } // sizeof uint64 if ivLen < 8 { return fmt.Errorf("ivLen:%d less than 8", ivLen) } // IV: | nonce: 8 bytes | Serial number: 8 bytes | cd.IV = make([]byte, ivLen) if _, err := io.ReadFull(rand.Reader, cd.IV[0:ivLen-8]); err != nil { return err } // only use 4 byte,in order not to overflow when SeekIV() randNumber := math_rand.Uint32() cd.SetIV(uint64(randNumber)) return nil } func (cd *CipherData) SetIV(iv uint64) { ivLen := len(cd.IV) binary.BigEndian.PutUint64(cd.IV[ivLen-8:], iv) } func (cd *CipherData) GetIV() uint64 { ivLen := len(cd.IV) return binary.BigEndian.Uint64(cd.IV[ivLen-8:]) } func (cd *CipherData) SeekIV(startPos uint64) { cd.SetIV(cd.GetIV() + startPos/uint64(len(cd.IV))) } func (cd *CipherData) Clone() CipherData { var cloneCd CipherData cloneCd = *cd cloneCd.Key = make([]byte, len(cd.Key)) copy(cloneCd.Key, cd.Key) cloneCd.IV = make([]byte, len(cd.IV)) copy(cloneCd.IV, cd.IV) cloneCd.EncryptedIV = make([]byte, len(cd.EncryptedIV)) copy(cloneCd.EncryptedIV, cd.EncryptedIV) cloneCd.EncryptedKey = make([]byte, len(cd.EncryptedKey)) copy(cloneCd.EncryptedKey, cd.EncryptedKey) return cloneCd }