// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. // Package gaes provides useful API for AES encryption/decryption algorithms. package gaes import ( "bytes" "crypto/aes" "crypto/cipher" "errors" ) var ( // IVDefaultValue is the default value for IV. // This can be changed globally. IVDefaultValue = "I Love Go Frame!" ) // Encrypt is alias of EncryptCBC. func Encrypt(plainText []byte, key []byte, iv ...[]byte) ([]byte, error) { return EncryptCBC(plainText, key, iv...) } // Decrypt is alias of DecryptCBC. func Decrypt(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) { return DecryptCBC(cipherText, key, iv...) } // EncryptCBC encrypts using CBC mode. // Note that the key must be 16/24/32 bit length. // The parameter <iv> initialization vector is unnecessary. func EncryptCBC(plainText []byte, key []byte, iv ...[]byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } blockSize := block.BlockSize() plainText = PKCS5Padding(plainText, blockSize) ivValue := ([]byte)(nil) if len(iv) > 0 { ivValue = iv[0] } else { ivValue = []byte(IVDefaultValue) } blockMode := cipher.NewCBCEncrypter(block, ivValue) cipherText := make([]byte, len(plainText)) blockMode.CryptBlocks(cipherText, plainText) return cipherText, nil } // DecryptCBC decrypts <cipherText> using CBC mode. // Note that the key must be 16/24/32 bit length. // The parameter <iv> initialization vector is unnecessary. func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } blockSize := block.BlockSize() if len(cipherText) < blockSize { return nil, errors.New("cipherText too short") } ivValue := ([]byte)(nil) if len(iv) > 0 { ivValue = iv[0] } else { ivValue = []byte(IVDefaultValue) } if len(cipherText)%blockSize != 0 { return nil, errors.New("cipherText is not a multiple of the block size") } blockModel := cipher.NewCBCDecrypter(block, ivValue) plainText := make([]byte, len(cipherText)) blockModel.CryptBlocks(plainText, cipherText) plainText, e := PKCS5UnPadding(plainText, blockSize) if e != nil { return nil, e } return plainText, nil } func PKCS5Padding(src []byte, blockSize int) []byte { padding := blockSize - len(src)%blockSize padtext := bytes.Repeat([]byte{byte(padding)}, padding) return append(src, padtext...) } func PKCS5UnPadding(src []byte, blockSize int) ([]byte, error) { length := len(src) if blockSize <= 0 { return nil, errors.New("invalid blocklen") } if length%blockSize != 0 || length == 0 { return nil, errors.New("invalid data len") } unpadding := int(src[length-1]) if unpadding > blockSize || unpadding == 0 { return nil, errors.New("invalid padding") } padding := src[length-unpadding:] for i := 0; i < unpadding; i++ { if padding[i] != byte(unpadding) { return nil, errors.New("invalid padding") } } return src[:(length - unpadding)], nil } // EncryptCFB encrypts <plainText> using CFB mode. // Note that the key must be 16/24/32 bit length. // The parameter <iv> initialization vector is unnecessary. func EncryptCFB(plainText []byte, key []byte, padding *int, iv ...[]byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } blockSize := block.BlockSize() plainText, *padding = ZeroPadding(plainText, blockSize) ivValue := ([]byte)(nil) if len(iv) > 0 { ivValue = iv[0] } else { ivValue = []byte(IVDefaultValue) } stream := cipher.NewCFBEncrypter(block, ivValue) cipherText := make([]byte, len(plainText)) stream.XORKeyStream(cipherText, plainText) return cipherText, nil } // DecryptCFB decrypts <plainText> using CFB mode. // Note that the key must be 16/24/32 bit length. // The parameter <iv> initialization vector is unnecessary. func DecryptCFB(cipherText []byte, key []byte, unPadding int, iv ...[]byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } if len(cipherText) < aes.BlockSize { return nil, errors.New("cipherText too short") } ivValue := ([]byte)(nil) if len(iv) > 0 { ivValue = iv[0] } else { ivValue = []byte(IVDefaultValue) } stream := cipher.NewCFBDecrypter(block, ivValue) plainText := make([]byte, len(cipherText)) stream.XORKeyStream(plainText, cipherText) plainText = ZeroUnPadding(plainText, unPadding) return plainText, nil } func ZeroPadding(cipherText []byte, blockSize int) ([]byte, int) { padding := blockSize - len(cipherText)%blockSize padText := bytes.Repeat([]byte{byte(0)}, padding) return append(cipherText, padText...), padding } func ZeroUnPadding(plaintext []byte, unPadding int) []byte { length := len(plaintext) return plaintext[:(length - unPadding)] }