gf/crypto/gaes/gaes.go

204 lines
6.4 KiB
Go

// Copyright GoFrame Author(https://goframe.org). 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"
"fmt"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
)
const (
// IVDefaultValue is the default value for IV.
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 `plainText` 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 {
err = gerror.WrapCodef(gcode.CodeInvalidParameter, err, `aes.NewCipher failed for key "%s"`, key)
return nil, err
}
blockSize := block.BlockSize()
plainText = PKCS7Padding(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 {
err = gerror.WrapCodef(gcode.CodeInvalidParameter, err, `aes.NewCipher failed for key "%s"`, key)
return nil, err
}
blockSize := block.BlockSize()
if len(cipherText) < blockSize {
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "cipherText too short")
}
ivValue := ([]byte)(nil)
if len(iv) > 0 {
ivValue = iv[0]
} else {
ivValue = []byte(IVDefaultValue)
}
if len(cipherText)%blockSize != 0 {
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "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 := PKCS7UnPadding(plainText, blockSize)
if e != nil {
return nil, e
}
return plainText, nil
}
// PKCS5Padding applies PKCS#5 padding to the source byte slice to match the given block size.
//
// If the block size is not provided, it defaults to 8.
func PKCS5Padding(src []byte, blockSize ...int) []byte {
blockSizeTemp := 8
if len(blockSize) > 0 {
blockSizeTemp = blockSize[0]
}
return PKCS7Padding(src, blockSizeTemp)
}
// PKCS5UnPadding removes PKCS#5 padding from the source byte slice based on the given block size.
//
// If the block size is not provided, it defaults to 8.
func PKCS5UnPadding(src []byte, blockSize ...int) ([]byte, error) {
blockSizeTemp := 8
if len(blockSize) > 0 {
blockSizeTemp = blockSize[0]
}
return PKCS7UnPadding(src, blockSizeTemp)
}
// PKCS7Padding applies PKCS#7 padding to the source byte slice to match the given block size.
func PKCS7Padding(src []byte, blockSize int) []byte {
padding := blockSize - len(src)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}
// PKCS7UnPadding removes PKCS#7 padding from the source byte slice based on the given block size.
func PKCS7UnPadding(src []byte, blockSize int) ([]byte, error) {
length := len(src)
if blockSize <= 0 {
return nil, gerror.NewCode(gcode.CodeInvalidParameter, fmt.Sprintf("invalid blockSize: %d", blockSize))
}
if length%blockSize != 0 || length == 0 {
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid data len")
}
unpadding := int(src[length-1])
if unpadding > blockSize || unpadding == 0 {
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid unpadding")
}
padding := src[length-unpadding:]
for i := 0; i < unpadding; i++ {
if padding[i] != byte(unpadding) {
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "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 {
err = gerror.WrapCodef(gcode.CodeInvalidParameter, err, `aes.NewCipher failed for key "%s"`, key)
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 {
err = gerror.WrapCodef(gcode.CodeInvalidParameter, err, `aes.NewCipher failed for key "%s"`, key)
return nil, err
}
if len(cipherText) < aes.BlockSize {
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "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)]
}