package symm import ( "crypto/aes" "crypto/cipher" "crypto/rand" "errors" "io" "b612.me/starcrypto/ccm" "b612.me/starcrypto/paddingx" ) const ( PKCS5PADDING = paddingx.PKCS5 PKCS7PADDING = paddingx.PKCS7 ZEROPADDING = paddingx.ZERO ANSIX923PADDING = paddingx.ANSIX923 ) var ( ErrInvalidGCMNonceLength = errors.New("gcm nonce length must be 12 bytes") ErrInvalidCCMNonceLength = errors.New("ccm nonce length must be 12 bytes") ) const ( aeadCCMTagSize = 16 aeadCCMNonceSize = 12 ) func EncryptAes(data, key, iv []byte, mode, paddingType string) ([]byte, error) { normalizedMode := normalizeCipherMode(mode) if normalizedMode == "" { normalizedMode = MODEGCM } if normalizedMode == MODEGCM { return EncryptAesGCM(data, key, iv, nil) } if normalizedMode == MODECCM { return EncryptAesCCM(data, key, iv, nil) } block, err := aes.NewCipher(key) if err != nil { return nil, err } return encryptWithBlockMode(block, data, iv, normalizedMode, paddingType, PKCS7PADDING) } func DecryptAes(src, key, iv []byte, mode, paddingType string) ([]byte, error) { normalizedMode := normalizeCipherMode(mode) if normalizedMode == "" { normalizedMode = MODEGCM } if normalizedMode == MODEGCM { return DecryptAesGCM(src, key, iv, nil) } if normalizedMode == MODECCM { return DecryptAesCCM(src, key, iv, nil) } block, err := aes.NewCipher(key) if err != nil { return nil, err } return decryptWithBlockMode(block, src, iv, normalizedMode, paddingType, PKCS7PADDING) } func EncryptAesStream(dst io.Writer, src io.Reader, key, iv []byte, mode, paddingType string) error { normalizedMode := normalizeCipherMode(mode) if normalizedMode == "" { normalizedMode = MODEGCM } if normalizedMode == MODEGCM { return EncryptAesGCMStream(dst, src, key, iv, nil) } if normalizedMode == MODECCM { return EncryptAesCCMStream(dst, src, key, iv, nil) } block, err := aes.NewCipher(key) if err != nil { return err } return encryptWithBlockModeStream(block, dst, src, iv, normalizedMode, paddingType, PKCS7PADDING) } func DecryptAesStream(dst io.Writer, src io.Reader, key, iv []byte, mode, paddingType string) error { normalizedMode := normalizeCipherMode(mode) if normalizedMode == "" { normalizedMode = MODEGCM } if normalizedMode == MODEGCM { return DecryptAesGCMStream(dst, src, key, iv, nil) } if normalizedMode == MODECCM { return DecryptAesCCMStream(dst, src, key, iv, nil) } block, err := aes.NewCipher(key) if err != nil { return err } return decryptWithBlockModeStream(block, dst, src, iv, normalizedMode, paddingType, PKCS7PADDING) } func EncryptAesWithOptions(data, key []byte, opts *CipherOptions) ([]byte, error) { cfg := normalizeCipherOptions(opts) mode := normalizeCipherMode(cfg.Mode) if mode == "" { mode = MODEGCM } if mode == MODEGCM { return EncryptAesGCM(data, key, nonceFromOptions(cfg), cfg.AAD) } if mode == MODECCM { return EncryptAesCCM(data, key, nonceFromOptions(cfg), cfg.AAD) } return EncryptAes(data, key, cfg.IV, mode, cfg.Padding) } func DecryptAesWithOptions(src, key []byte, opts *CipherOptions) ([]byte, error) { cfg := normalizeCipherOptions(opts) mode := normalizeCipherMode(cfg.Mode) if mode == "" { mode = MODEGCM } if mode == MODEGCM { return DecryptAesGCM(src, key, nonceFromOptions(cfg), cfg.AAD) } if mode == MODECCM { return DecryptAesCCM(src, key, nonceFromOptions(cfg), cfg.AAD) } return DecryptAes(src, key, cfg.IV, mode, cfg.Padding) } func EncryptAesStreamWithOptions(dst io.Writer, src io.Reader, key []byte, opts *CipherOptions) error { cfg := normalizeCipherOptions(opts) mode := normalizeCipherMode(cfg.Mode) if mode == "" { mode = MODEGCM } if mode == MODEGCM { return EncryptAesGCMStream(dst, src, key, nonceFromOptions(cfg), cfg.AAD) } if mode == MODECCM { return EncryptAesCCMStream(dst, src, key, nonceFromOptions(cfg), cfg.AAD) } return EncryptAesStream(dst, src, key, cfg.IV, mode, cfg.Padding) } func DecryptAesStreamWithOptions(dst io.Writer, src io.Reader, key []byte, opts *CipherOptions) error { cfg := normalizeCipherOptions(opts) mode := normalizeCipherMode(cfg.Mode) if mode == "" { mode = MODEGCM } if mode == MODEGCM { return DecryptAesGCMStream(dst, src, key, nonceFromOptions(cfg), cfg.AAD) } if mode == MODECCM { return DecryptAesCCMStream(dst, src, key, nonceFromOptions(cfg), cfg.AAD) } return DecryptAesStream(dst, src, key, cfg.IV, mode, cfg.Padding) } func EncryptAesGCM(plain, key, nonce, aad []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } if len(nonce) != gcm.NonceSize() { return nil, ErrInvalidGCMNonceLength } return gcm.Seal(nil, nonce, plain, aad), nil } func DecryptAesGCM(ciphertext, key, nonce, aad []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } if len(nonce) != gcm.NonceSize() { return nil, ErrInvalidGCMNonceLength } return gcm.Open(nil, nonce, ciphertext, aad) } func newAesCCM(key []byte) (cipher.AEAD, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } return ccm.NewCCM(block, aeadCCMTagSize, aeadCCMNonceSize) } func EncryptAesCCM(plain, key, nonce, aad []byte) ([]byte, error) { aead, err := newAesCCM(key) if err != nil { return nil, err } if len(nonce) != aead.NonceSize() { return nil, ErrInvalidCCMNonceLength } return aead.Seal(nil, nonce, plain, aad), nil } func DecryptAesCCM(ciphertext, key, nonce, aad []byte) ([]byte, error) { aead, err := newAesCCM(key) if err != nil { return nil, err } if len(nonce) != aead.NonceSize() { return nil, ErrInvalidCCMNonceLength } return aead.Open(nil, nonce, ciphertext, aad) } func EncryptAesCCMChunk(plain, key, nonce, aad []byte, chunkIndex uint64) ([]byte, error) { aead, err := newAesCCM(key) if err != nil { return nil, err } if len(nonce) != aead.NonceSize() { return nil, ErrInvalidCCMNonceLength } return encryptCCMChunk(aead, plain, nonce, aad, chunkIndex), nil } func DecryptAesCCMChunk(ciphertext, key, nonce, aad []byte, chunkIndex uint64) ([]byte, error) { aead, err := newAesCCM(key) if err != nil { return nil, err } if len(nonce) != aead.NonceSize() { return nil, ErrInvalidCCMNonceLength } return decryptCCMChunk(aead, ciphertext, nonce, aad, chunkIndex) } func EncryptAesCCMStream(dst io.Writer, src io.Reader, key, nonce, aad []byte) error { aead, err := newAesCCM(key) if err != nil { return err } if len(nonce) != aead.NonceSize() { return ErrInvalidCCMNonceLength } return encryptCCMChunkedStream(dst, src, aead, nonce, aad) } func DecryptAesCCMStream(dst io.Writer, src io.Reader, key, nonce, aad []byte) error { aead, err := newAesCCM(key) if err != nil { return err } if len(nonce) != aead.NonceSize() { return ErrInvalidCCMNonceLength } return decryptCCMChunkedOrLegacyStream(dst, src, aead, nonce, aad) } func EncryptAesGCMChunk(plain, key, nonce, aad []byte, chunkIndex uint64) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } if len(nonce) != gcm.NonceSize() { return nil, ErrInvalidGCMNonceLength } return encryptGCMChunk(gcm, plain, nonce, aad, chunkIndex), nil } func DecryptAesGCMChunk(ciphertext, key, nonce, aad []byte, chunkIndex uint64) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } if len(nonce) != gcm.NonceSize() { return nil, ErrInvalidGCMNonceLength } return decryptGCMChunk(gcm, ciphertext, nonce, aad, chunkIndex) } func EncryptAesGCMStream(dst io.Writer, src io.Reader, key, nonce, aad []byte) error { block, err := aes.NewCipher(key) if err != nil { return err } gcm, err := cipher.NewGCM(block) if err != nil { return err } if len(nonce) != gcm.NonceSize() { return ErrInvalidGCMNonceLength } return encryptGCMChunkedStream(dst, src, gcm, nonce, aad) } func DecryptAesGCMStream(dst io.Writer, src io.Reader, key, nonce, aad []byte) error { block, err := aes.NewCipher(key) if err != nil { return err } gcm, err := cipher.NewGCM(block) if err != nil { return err } if len(nonce) != gcm.NonceSize() { return ErrInvalidGCMNonceLength } return decryptGCMChunkedOrLegacyStream(dst, src, gcm, nonce, aad) } func EncryptAesECB(data, key []byte, paddingType string) ([]byte, error) { return EncryptAes(data, key, nil, MODEECB, paddingType) } func DecryptAesECB(src, key []byte, paddingType string) ([]byte, error) { return DecryptAes(src, key, nil, MODEECB, paddingType) } func EncryptAesCBC(data, key, iv []byte, paddingType string) ([]byte, error) { return EncryptAes(data, key, iv, MODECBC, paddingType) } func DecryptAesCBC(src, key, iv []byte, paddingType string) ([]byte, error) { return DecryptAes(src, key, iv, MODECBC, paddingType) } func EncryptAesCFB(data, key, iv []byte) ([]byte, error) { return EncryptAes(data, key, iv, MODECFB, "") } func DecryptAesCFB(src, key, iv []byte) ([]byte, error) { return DecryptAes(src, key, iv, MODECFB, "") } func EncryptAesOFB(data, key, iv []byte) ([]byte, error) { return EncryptAes(data, key, iv, MODEOFB, "") } func DecryptAesOFB(src, key, iv []byte) ([]byte, error) { return DecryptAes(src, key, iv, MODEOFB, "") } func EncryptAesCTR(data, key, iv []byte) ([]byte, error) { return EncryptAes(data, key, iv, MODECTR, "") } func DecryptAesCTR(src, key, iv []byte) ([]byte, error) { return DecryptAes(src, key, iv, MODECTR, "") } func EncryptAesCTRAt(data, key, iv []byte, offset int64) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } return xorCTRAtOffset(block, data, iv, offset) } func DecryptAesCTRAt(src, key, iv []byte, offset int64) ([]byte, error) { return EncryptAesCTRAt(src, key, iv, offset) } func EncryptAesECBStream(dst io.Writer, src io.Reader, key []byte, paddingType string) error { return EncryptAesStream(dst, src, key, nil, MODEECB, paddingType) } func DecryptAesECBStream(dst io.Writer, src io.Reader, key []byte, paddingType string) error { return DecryptAesStream(dst, src, key, nil, MODEECB, paddingType) } func EncryptAesCBCStream(dst io.Writer, src io.Reader, key, iv []byte, paddingType string) error { return EncryptAesStream(dst, src, key, iv, MODECBC, paddingType) } func DecryptAesCBCStream(dst io.Writer, src io.Reader, key, iv []byte, paddingType string) error { return DecryptAesStream(dst, src, key, iv, MODECBC, paddingType) } func EncryptAesCFBStream(dst io.Writer, src io.Reader, key, iv []byte) error { return EncryptAesStream(dst, src, key, iv, MODECFB, "") } func DecryptAesCFBStream(dst io.Writer, src io.Reader, key, iv []byte) error { return DecryptAesStream(dst, src, key, iv, MODECFB, "") } func EncryptAesOFBStream(dst io.Writer, src io.Reader, key, iv []byte) error { return EncryptAesStream(dst, src, key, iv, MODEOFB, "") } func DecryptAesOFBStream(dst io.Writer, src io.Reader, key, iv []byte) error { return DecryptAesStream(dst, src, key, iv, MODEOFB, "") } func EncryptAesCTRStream(dst io.Writer, src io.Reader, key, iv []byte) error { return EncryptAesStream(dst, src, key, iv, MODECTR, "") } func DecryptAesCTRStream(dst io.Writer, src io.Reader, key, iv []byte) error { return DecryptAesStream(dst, src, key, iv, MODECTR, "") } func CustomEncryptAesCFB(origData, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } encrypted := make([]byte, aes.BlockSize+len(origData)) iv := encrypted[:block.BlockSize()] if _, err := io.ReadFull(rand.Reader, iv); err != nil { return nil, err } body, err := EncryptAesCFB(origData, key, iv) if err != nil { return nil, err } copy(encrypted[block.BlockSize():], body) return encrypted, nil } func CustomDecryptAesCFB(encrypted, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } if len(encrypted) < block.BlockSize() { return nil, errors.New("ciphertext too short") } iv := encrypted[:block.BlockSize()] return DecryptAesCFB(encrypted[block.BlockSize():], key, iv) } func CustomEncryptAesCFBNoBlock(origData, key, iv []byte) ([]byte, error) { return EncryptAesCFB(origData, key, iv) } func CustomDecryptAesCFBNoBlock(encrypted, key, iv []byte) ([]byte, error) { return DecryptAesCFB(encrypted, key, iv) } func PKCS5Padding(cipherText []byte, blockSize int) []byte { out, _ := paddingx.Pad(cipherText, blockSize, PKCS5PADDING) return out } func PKCS5Trimming(encrypted []byte) []byte { if len(encrypted) == 0 { return nil } padding := int(encrypted[len(encrypted)-1]) if padding <= 0 || padding > len(encrypted) { return nil } for i := len(encrypted) - padding; i < len(encrypted); i++ { if int(encrypted[i]) != padding { return nil } } return encrypted[:len(encrypted)-padding] } func PKCS7Padding(cipherText []byte, blockSize int) []byte { out, _ := paddingx.Pad(cipherText, blockSize, PKCS7PADDING) return out } func PKCS7Trimming(encrypted []byte, blockSize int) []byte { out, err := paddingx.Unpad(encrypted, blockSize, PKCS7PADDING) if err != nil { return nil } return out } func EncryptAesCFB8(data, key, iv []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } return encryptCFB8(block, data, iv) } func DecryptAesCFB8(src, key, iv []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } return decryptCFB8(block, src, iv) } func EncryptAesCFB8Stream(dst io.Writer, src io.Reader, key, iv []byte) error { block, err := aes.NewCipher(key) if err != nil { return err } return encryptCFB8Stream(block, dst, src, iv, false) } func DecryptAesCFB8Stream(dst io.Writer, src io.Reader, key, iv []byte) error { block, err := aes.NewCipher(key) if err != nil { return err } return encryptCFB8Stream(block, dst, src, iv, true) } func DecryptAesECBBlocks(src, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } return decryptECBBlocks(block, src) } // DecryptAesCBCFromSecondBlock decrypts a CBC ciphertext segment that starts from block 2 or later. // prevCipherBlock must be the previous ciphertext block. For data from block 2, pass block 1 as prevCipherBlock. func DecryptAesCBCFromSecondBlock(src, key, prevCipherBlock []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } return decryptCBCFromSecondBlock(block, src, prevCipherBlock) } // DecryptAesCFBFromSecondBlock decrypts a CFB ciphertext segment that starts from block 2 or later. // prevCipherBlock must be the previous ciphertext block. For data from block 2, pass block 1 as prevCipherBlock. func DecryptAesCFBFromSecondBlock(src, key, prevCipherBlock []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } return decryptCFBFromSecondBlock(block, src, prevCipherBlock) }