oss/crypto/aes_ctr_cipher.go (153 lines of code) (raw):
package crypto
import (
"fmt"
"io"
)
const (
aesKeySize = 32
ivSize = 16
)
// aesCtrCipherBuilder for building ContentCipher
type aesCtrCipherBuilder struct {
MasterCipher MasterCipher
}
// aesCtrCipher will use aes ctr algorithm
type aesCtrCipher struct {
CipherData CipherData
Cipher Cipher
}
// CreateAesCtrCipher creates ContentCipherBuilder
func CreateAesCtrCipher(cipher MasterCipher) ContentCipherBuilder {
return aesCtrCipherBuilder{MasterCipher: cipher}
}
// createCipherData create CipherData for encrypt object data
func (builder aesCtrCipherBuilder) createCipherData() (CipherData, error) {
var cd CipherData
var err error
err = cd.RandomKeyIv(aesKeySize, ivSize)
if err != nil {
return cd, err
}
cd.WrapAlgorithm = builder.MasterCipher.GetWrapAlgorithm()
cd.CEKAlgorithm = AesCtrAlgorithm
cd.MatDesc = builder.MasterCipher.GetMatDesc()
// EncryptedKey
cd.EncryptedKey, err = builder.MasterCipher.Encrypt(cd.Key)
if err != nil {
return cd, err
}
// EncryptedIV
cd.EncryptedIV, err = builder.MasterCipher.Encrypt(cd.IV)
if err != nil {
return cd, err
}
return cd, nil
}
// contentCipherCD is used to create ContentCipher with CipherData
func (builder aesCtrCipherBuilder) contentCipherCD(cd CipherData) (ContentCipher, error) {
cipher, err := newAesCtr(cd)
if err != nil {
return nil, err
}
return &aesCtrCipher{
CipherData: cd,
Cipher: cipher,
}, nil
}
// ContentCipher is used to create ContentCipher interface
func (builder aesCtrCipherBuilder) ContentCipher() (ContentCipher, error) {
cd, err := builder.createCipherData()
if err != nil {
return nil, err
}
return builder.contentCipherCD(cd)
}
// ContentCipherEnv is used to create a decrption ContentCipher from Envelope
func (builder aesCtrCipherBuilder) ContentCipherEnv(envelope Envelope) (ContentCipher, error) {
var cd CipherData
cd.EncryptedKey = make([]byte, len(envelope.CipherKey))
copy(cd.EncryptedKey, []byte(envelope.CipherKey))
plainKey, err := builder.MasterCipher.Decrypt([]byte(envelope.CipherKey))
if err != nil {
return nil, err
}
cd.Key = make([]byte, len(plainKey))
copy(cd.Key, plainKey)
cd.EncryptedIV = make([]byte, len(envelope.IV))
copy(cd.EncryptedIV, []byte(envelope.IV))
plainIV, err := builder.MasterCipher.Decrypt([]byte(envelope.IV))
if err != nil {
return nil, err
}
cd.IV = make([]byte, len(plainIV))
copy(cd.IV, plainIV)
cd.MatDesc = envelope.MatDesc
cd.WrapAlgorithm = envelope.WrapAlg
cd.CEKAlgorithm = envelope.CEKAlg
return builder.contentCipherCD(cd)
}
// GetMatDesc is used to get MasterCipher's MatDesc
func (builder aesCtrCipherBuilder) GetMatDesc() string {
return builder.MasterCipher.GetMatDesc()
}
// EncryptContents will generate a random key and iv and encrypt the data using ctr
func (cc *aesCtrCipher) EncryptContent(src io.Reader) (io.ReadCloser, error) {
if sr, ok := src.(io.ReadSeeker); ok {
if curr, err := sr.Seek(0, io.SeekCurrent); err == nil {
return &aesSeekEncrypter{
Body: sr,
Encrypter: nil,
Start: curr,
Offset: curr,
cc: cc,
}, nil
}
}
reader := cc.Cipher.Encrypt(src)
return &CryptoEncrypter{Body: src, Encrypter: reader}, nil
}
// DecryptContent is used to decrypt object using ctr
func (cc *aesCtrCipher) DecryptContent(src io.Reader) (io.ReadCloser, error) {
reader := cc.Cipher.Decrypt(src)
return &CryptoDecrypter{Body: src, Decrypter: reader}, nil
}
// GetCipherData is used to get cipher data information
func (cc *aesCtrCipher) GetCipherData() *CipherData {
return &(cc.CipherData)
}
// GetCipherData returns cipher data
func (cc *aesCtrCipher) GetEncryptedLen(plainTextLen int64) int64 {
// AES CTR encryption mode does not change content length
return plainTextLen
}
// GetAlignLen is used to get align length
func (cc *aesCtrCipher) GetAlignLen() int {
return len(cc.CipherData.IV)
}
// Clone is used to create a new aesCtrCipher from itself
func (cc *aesCtrCipher) Clone(cd CipherData) (ContentCipher, error) {
cipher, err := newAesCtr(cd)
if err != nil {
return nil, err
}
return &aesCtrCipher{
CipherData: cd,
Cipher: cipher,
}, nil
}
// CryptoSeekEncrypter provides close and seek method for Encrypter
type aesSeekEncrypter struct {
Body io.ReadSeeker
Encrypter io.Reader
isClosed bool
Start int64
Offset int64
cc *aesCtrCipher
}
// Close lets the CryptoSeekEncrypter satisfy io.ReadCloser interface
func (rc *aesSeekEncrypter) Close() error {
rc.isClosed = true
if closer, ok := rc.Body.(io.ReadCloser); ok {
return closer.Close()
}
return nil
}
// Read lets the CryptoSeekEncrypter satisfy io.ReadCloser interface
func (rc *aesSeekEncrypter) Read(b []byte) (int, error) {
if rc.isClosed {
return 0, io.EOF
}
if rc.Encrypter == nil {
if rc.Start != rc.Offset {
return 0, fmt.Errorf("Cant not encrypt from offset %v, must start from %v", rc.Offset, rc.Start)
}
rc.Encrypter = rc.cc.Cipher.Encrypt(rc.Body)
}
return rc.Encrypter.Read(b)
}
// Seek lets the CryptoSeekEncrypter satisfy io.Seeker interface
func (rc *aesSeekEncrypter) Seek(offset int64, whence int) (int64, error) {
off, err := rc.Body.Seek(offset, whence)
//Reset Encrypter Reader
rc.Encrypter = nil
rc.Offset = off
return off, err
}