github.com/wangyougui/gf/v2@v2.6.5/crypto/gaes/gaes.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/wangyougui/gf.
     6  
     7  // Package gaes provides useful API for AES encryption/decryption algorithms.
     8  package gaes
     9  
    10  import (
    11  	"bytes"
    12  	"crypto/aes"
    13  	"crypto/cipher"
    14  	"fmt"
    15  
    16  	"github.com/wangyougui/gf/v2/errors/gcode"
    17  	"github.com/wangyougui/gf/v2/errors/gerror"
    18  )
    19  
    20  const (
    21  	// IVDefaultValue is the default value for IV.
    22  	IVDefaultValue = "I Love Go Frame!"
    23  )
    24  
    25  // Encrypt is alias of EncryptCBC.
    26  func Encrypt(plainText []byte, key []byte, iv ...[]byte) ([]byte, error) {
    27  	return EncryptCBC(plainText, key, iv...)
    28  }
    29  
    30  // Decrypt is alias of DecryptCBC.
    31  func Decrypt(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) {
    32  	return DecryptCBC(cipherText, key, iv...)
    33  }
    34  
    35  // EncryptCBC encrypts `plainText` using CBC mode.
    36  // Note that the key must be 16/24/32 bit length.
    37  // The parameter `iv` initialization vector is unnecessary.
    38  func EncryptCBC(plainText []byte, key []byte, iv ...[]byte) ([]byte, error) {
    39  	block, err := aes.NewCipher(key)
    40  	if err != nil {
    41  		err = gerror.WrapCodef(gcode.CodeInvalidParameter, err, `aes.NewCipher failed for key "%s"`, key)
    42  		return nil, err
    43  	}
    44  	blockSize := block.BlockSize()
    45  	plainText = PKCS7Padding(plainText, blockSize)
    46  	ivValue := ([]byte)(nil)
    47  	if len(iv) > 0 {
    48  		ivValue = iv[0]
    49  	} else {
    50  		ivValue = []byte(IVDefaultValue)
    51  	}
    52  	blockMode := cipher.NewCBCEncrypter(block, ivValue)
    53  	cipherText := make([]byte, len(plainText))
    54  	blockMode.CryptBlocks(cipherText, plainText)
    55  
    56  	return cipherText, nil
    57  }
    58  
    59  // DecryptCBC decrypts `cipherText` using CBC mode.
    60  // Note that the key must be 16/24/32 bit length.
    61  // The parameter `iv` initialization vector is unnecessary.
    62  func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) {
    63  	block, err := aes.NewCipher(key)
    64  	if err != nil {
    65  		err = gerror.WrapCodef(gcode.CodeInvalidParameter, err, `aes.NewCipher failed for key "%s"`, key)
    66  		return nil, err
    67  	}
    68  	blockSize := block.BlockSize()
    69  	if len(cipherText) < blockSize {
    70  		return nil, gerror.NewCode(gcode.CodeInvalidParameter, "cipherText too short")
    71  	}
    72  	ivValue := ([]byte)(nil)
    73  	if len(iv) > 0 {
    74  		ivValue = iv[0]
    75  	} else {
    76  		ivValue = []byte(IVDefaultValue)
    77  	}
    78  	if len(cipherText)%blockSize != 0 {
    79  		return nil, gerror.NewCode(gcode.CodeInvalidParameter, "cipherText is not a multiple of the block size")
    80  	}
    81  	blockModel := cipher.NewCBCDecrypter(block, ivValue)
    82  	plainText := make([]byte, len(cipherText))
    83  	blockModel.CryptBlocks(plainText, cipherText)
    84  	plainText, e := PKCS7UnPadding(plainText, blockSize)
    85  	if e != nil {
    86  		return nil, e
    87  	}
    88  	return plainText, nil
    89  }
    90  
    91  // PKCS5Padding applies PKCS#5 padding to the source byte slice to match the given block size.
    92  //
    93  // If the block size is not provided, it defaults to 8.
    94  func PKCS5Padding(src []byte, blockSize ...int) []byte {
    95  	blockSizeTemp := 8
    96  	if len(blockSize) > 0 {
    97  		blockSizeTemp = blockSize[0]
    98  	}
    99  	return PKCS7Padding(src, blockSizeTemp)
   100  }
   101  
   102  // PKCS5UnPadding removes PKCS#5 padding from the source byte slice based on the given block size.
   103  //
   104  // If the block size is not provided, it defaults to 8.
   105  func PKCS5UnPadding(src []byte, blockSize ...int) ([]byte, error) {
   106  	blockSizeTemp := 8
   107  	if len(blockSize) > 0 {
   108  		blockSizeTemp = blockSize[0]
   109  	}
   110  	return PKCS7UnPadding(src, blockSizeTemp)
   111  }
   112  
   113  // PKCS7Padding applies PKCS#7 padding to the source byte slice to match the given block size.
   114  func PKCS7Padding(src []byte, blockSize int) []byte {
   115  	padding := blockSize - len(src)%blockSize
   116  	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
   117  	return append(src, padtext...)
   118  }
   119  
   120  // PKCS7UnPadding removes PKCS#7 padding from the source byte slice based on the given block size.
   121  func PKCS7UnPadding(src []byte, blockSize int) ([]byte, error) {
   122  	length := len(src)
   123  	if blockSize <= 0 {
   124  		return nil, gerror.NewCode(gcode.CodeInvalidParameter, fmt.Sprintf("invalid blockSize: %d", blockSize))
   125  	}
   126  
   127  	if length%blockSize != 0 || length == 0 {
   128  		return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid data len")
   129  	}
   130  
   131  	unpadding := int(src[length-1])
   132  	if unpadding > blockSize || unpadding == 0 {
   133  		return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid unpadding")
   134  	}
   135  
   136  	padding := src[length-unpadding:]
   137  	for i := 0; i < unpadding; i++ {
   138  		if padding[i] != byte(unpadding) {
   139  			return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid padding")
   140  		}
   141  	}
   142  
   143  	return src[:(length - unpadding)], nil
   144  }
   145  
   146  // EncryptCFB encrypts `plainText` using CFB mode.
   147  // Note that the key must be 16/24/32 bit length.
   148  // The parameter `iv` initialization vector is unnecessary.
   149  func EncryptCFB(plainText []byte, key []byte, padding *int, iv ...[]byte) ([]byte, error) {
   150  	block, err := aes.NewCipher(key)
   151  	if err != nil {
   152  		err = gerror.WrapCodef(gcode.CodeInvalidParameter, err, `aes.NewCipher failed for key "%s"`, key)
   153  		return nil, err
   154  	}
   155  	blockSize := block.BlockSize()
   156  	plainText, *padding = ZeroPadding(plainText, blockSize)
   157  	ivValue := ([]byte)(nil)
   158  	if len(iv) > 0 {
   159  		ivValue = iv[0]
   160  	} else {
   161  		ivValue = []byte(IVDefaultValue)
   162  	}
   163  	stream := cipher.NewCFBEncrypter(block, ivValue)
   164  	cipherText := make([]byte, len(plainText))
   165  	stream.XORKeyStream(cipherText, plainText)
   166  	return cipherText, nil
   167  }
   168  
   169  // DecryptCFB decrypts `plainText` using CFB mode.
   170  // Note that the key must be 16/24/32 bit length.
   171  // The parameter `iv` initialization vector is unnecessary.
   172  func DecryptCFB(cipherText []byte, key []byte, unPadding int, iv ...[]byte) ([]byte, error) {
   173  	block, err := aes.NewCipher(key)
   174  	if err != nil {
   175  		err = gerror.WrapCodef(gcode.CodeInvalidParameter, err, `aes.NewCipher failed for key "%s"`, key)
   176  		return nil, err
   177  	}
   178  	if len(cipherText) < aes.BlockSize {
   179  		return nil, gerror.NewCode(gcode.CodeInvalidParameter, "cipherText too short")
   180  	}
   181  	ivValue := ([]byte)(nil)
   182  	if len(iv) > 0 {
   183  		ivValue = iv[0]
   184  	} else {
   185  		ivValue = []byte(IVDefaultValue)
   186  	}
   187  	stream := cipher.NewCFBDecrypter(block, ivValue)
   188  	plainText := make([]byte, len(cipherText))
   189  	stream.XORKeyStream(plainText, cipherText)
   190  	plainText = ZeroUnPadding(plainText, unPadding)
   191  	return plainText, nil
   192  }
   193  
   194  func ZeroPadding(cipherText []byte, blockSize int) ([]byte, int) {
   195  	padding := blockSize - len(cipherText)%blockSize
   196  	padText := bytes.Repeat([]byte{byte(0)}, padding)
   197  	return append(cipherText, padText...), padding
   198  }
   199  
   200  func ZeroUnPadding(plaintext []byte, unPadding int) []byte {
   201  	length := len(plaintext)
   202  	return plaintext[:(length - unPadding)]
   203  }