github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/encrypt/crypto/aes.go (about)

     1  package crypto
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/aes"
     6  	"crypto/cipher"
     7  	"crypto/rand"
     8  	"crypto/sha256"
     9  	"io"
    10  )
    11  
    12  const (
    13  	gcmTagSize           = 16 // crypto/cipher.gcmTagSize
    14  	gcmStandardNonceSize = 12 // crypto/cipher.gcmStandardNonceSize
    15  )
    16  
    17  // GCMEncrypt encrypts plaintext with key using the GCM mode.
    18  // The returned ciphertext contains the nonce, encrypted text and
    19  // the additional data authentication tag. If additional data is not
    20  // provided (as an Option), random data will be generated and used.
    21  //
    22  // GCM模式是CTR和GHASH的组合,GHASH操作定义为密文结果与密钥以及消息长度在GF(2^128)域上相乘。
    23  // GCM比CCM的优势是在于更高并行度及更好的性能。
    24  // TLS1.2标准使用的就是AES-GCM算法,并且Intel CPU提供了GHASH的硬件加速功能。
    25  func GCMEncrypt(plaintext, key []byte, opts ...Option) (ciphertext []byte, err error) {
    26  	opt := (&options{}).apply(opts...)
    27  	key = KeyPadding(key)
    28  	nonceSize := defaultInt(opt.nonceSize, gcmStandardNonceSize)
    29  	nonce := make([]byte, nonceSize)
    30  	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
    31  		return nil, err
    32  	}
    33  	block, err := aes.NewCipher(key)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	gcm, err := cipher.NewGCMWithNonceSize(block, nonceSize)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	ciphertext = gcm.Seal(nil, nonce, plaintext, opt.additionalData)
    43  	ciphertext = append(nonce, ciphertext...) //nolint:makezero
    44  	ciphertext, err = opt.encode(ciphertext)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	return ciphertext, nil
    49  }
    50  
    51  // GCMEncryptNewKey creates a new key and encrypts plaintext with the
    52  // new key using GCM mode.
    53  // The returned ciphertext contains the nonce, encrypted text and
    54  // the additional data authentication tag. If additional data is not
    55  // provided (as an Option), random data will be generated and used.
    56  func GCMEncryptNewKey(plaintext []byte, opts ...Option) (
    57  	ciphertext, key, additional []byte, err error,
    58  ) {
    59  	opt := (&options{}).apply(opts...)
    60  	keySize := defaultInt(opt.keySize, 2*aes.BlockSize)
    61  	nonceSize := defaultInt(opt.nonceSize, gcmStandardNonceSize)
    62  	additional = opt.additionalData
    63  	buflen := keySize + nonceSize
    64  	if len(additional) == 0 {
    65  		buflen += aes.BlockSize
    66  	}
    67  	buf := make([]byte, buflen)
    68  	if _, err = io.ReadFull(rand.Reader, buf); err != nil {
    69  		return nil, nil, nil, err
    70  	}
    71  	nonceEnd := keySize + nonceSize
    72  	key = buf[:keySize:keySize]
    73  	nonce := buf[keySize:nonceEnd:nonceEnd]
    74  	if len(additional) == 0 {
    75  		additional = buf[keySize+nonceSize:]
    76  	}
    77  
    78  	block, err := aes.NewCipher(key)
    79  	if err != nil {
    80  		return nil, nil, nil, err
    81  	}
    82  	gcm, err := cipher.NewGCMWithNonceSize(block, nonceSize)
    83  	if err != nil {
    84  		return nil, nil, nil, err
    85  	}
    86  
    87  	ciphertext = gcm.Seal(nil, nonce, plaintext, additional)
    88  	ciphertext = append(nonce, ciphertext...)
    89  	ciphertext, err = opt.encode(ciphertext)
    90  	if err != nil {
    91  		return nil, nil, nil, err
    92  	}
    93  	return ciphertext, key, additional, nil
    94  }
    95  
    96  // UnpackGCMCipherText unpacks cipher text returned by GCMEncrypt and
    97  // GCMEncryptNewKey into encrypted text, nonce and authentication tag.
    98  func UnpackGCMCipherText(ciphertext []byte, opts ...Option) (text, nonce, tag []byte) {
    99  	opt := (&options{}).apply(opts...)
   100  	nonceSize := defaultInt(opt.nonceSize, gcmStandardNonceSize)
   101  	tagOffset := len(ciphertext) - gcmTagSize
   102  	nonce = ciphertext[:nonceSize:nonceSize]
   103  	text = ciphertext[nonceSize:tagOffset:tagOffset]
   104  	tag = ciphertext[tagOffset:]
   105  	return
   106  }
   107  
   108  // GCMDecrypt decrypts ciphertext returned by GCMEncrypt and GCMEncryptNewKey
   109  // into plain text.
   110  func GCMDecrypt(ciphertext, key []byte, opts ...Option) (plaintext []byte, err error) {
   111  	opt := (&options{}).apply(opts...)
   112  	key = KeyPadding(key)
   113  	nonceSize := defaultInt(opt.nonceSize, gcmStandardNonceSize)
   114  	ciphertext, err = opt.decode(ciphertext)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	nonce := ciphertext[:nonceSize]
   119  	ciphertext = ciphertext[nonceSize:]
   120  	block, err := aes.NewCipher(key)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	gcm, err := cipher.NewGCMWithNonceSize(block, nonceSize)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	return gcm.Open(nil, nonce, ciphertext, opt.additionalData)
   129  }
   130  
   131  // CBCEncrypt encrypts plaintext with key using the CBC mode.
   132  // The given plaintext will be padded following the PKCS#5 standard.
   133  // The returned ciphertext contains the nonce and encrypted data.
   134  //
   135  // CBC - 密码分组链接模式,明文数据需要按分组大小对齐。
   136  func CBCEncrypt(plaintext, key []byte, opts ...Option) (ciphertext []byte, err error) {
   137  	opt := (&options{}).apply(opts...)
   138  	key = KeyPadding(key)
   139  
   140  	block, err := aes.NewCipher(key)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	blockSize := block.BlockSize()
   145  	plaintext = PKCS5Padding(plaintext, blockSize)
   146  	buf := make([]byte, blockSize+len(plaintext))
   147  	nonce := buf[:blockSize]
   148  	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
   149  		return nil, err
   150  	}
   151  	encrypter := cipher.NewCBCEncrypter(block, nonce)
   152  	encrypter.CryptBlocks(buf[len(nonce):], plaintext)
   153  	ciphertext, err = opt.encode(buf)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	return ciphertext, nil
   158  }
   159  
   160  // CBCDecrypt decrypts ciphertext returned by CBCEncrypt into plain text.
   161  func CBCDecrypt(ciphertext, key []byte, opts ...Option) (plaintext []byte, err error) {
   162  	opt := (&options{}).apply(opts...)
   163  	key = KeyPadding(key)
   164  	ciphertext, err = opt.decode(ciphertext)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	block, err := aes.NewCipher(key)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  	blockSize := block.BlockSize()
   173  	nonce := ciphertext[:blockSize]
   174  	ciphertext = ciphertext[blockSize:]
   175  	decrypter := cipher.NewCBCDecrypter(block, nonce)
   176  	plaintext = make([]byte, len(ciphertext))
   177  	decrypter.CryptBlocks(plaintext, ciphertext)
   178  	plaintext = PKCS5UnPadding(plaintext)
   179  	return plaintext, nil
   180  }
   181  
   182  // CFBEncrypt encrypts plaintext with key using the CFB mode.
   183  // The returned cipher text contains the nonce and encrypted data.
   184  //
   185  // CFB - 密文反馈模式,明文数据不需要按分组大小对齐。
   186  func CFBEncrypt(plaintext, key []byte, opts ...Option) ([]byte, error) {
   187  	opt := (&options{}).apply(opts...)
   188  	key = KeyPadding(key)
   189  
   190  	block, err := aes.NewCipher(key)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	blockSize := block.BlockSize()
   195  	buf := make([]byte, blockSize+len(plaintext))
   196  	nonce := buf[:blockSize]
   197  	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
   198  		return nil, err
   199  	}
   200  	encrypter := cipher.NewCFBEncrypter(block, nonce)
   201  	encrypter.XORKeyStream(buf[len(nonce):], plaintext)
   202  	ciphertext, err := opt.encode(buf)
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  	return ciphertext, nil
   207  }
   208  
   209  // CFBDecrypt decrypts ciphertext returned by CFBEncrypt.
   210  func CFBDecrypt(ciphertext, key []byte, opts ...Option) ([]byte, error) {
   211  	opt := (&options{}).apply(opts...)
   212  	key = KeyPadding(key)
   213  	ciphertext, err := opt.decode(ciphertext)
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  	block, err := aes.NewCipher(key)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	blockSize := block.BlockSize()
   222  	nonce := ciphertext[:blockSize]
   223  	ciphertext = ciphertext[blockSize:]
   224  	decrypter := cipher.NewCFBDecrypter(block, nonce)
   225  	plaintext := make([]byte, len(ciphertext))
   226  	decrypter.XORKeyStream(plaintext, ciphertext)
   227  	return plaintext, nil
   228  }
   229  
   230  // KeyPadding ensures a key's length is either 32, 24 or 16.
   231  // If key's length is greater than 32, it returns the first 32 bytes of key.
   232  // If key's length is not 32, 24 or 16, it appends additional data to key
   233  // using sha256.Sum(key) to make it satisfies the minimal requirement.
   234  func KeyPadding(key []byte) []byte {
   235  	length := len(key)
   236  	if length == 32 || length == 24 || length == 16 {
   237  		return key
   238  	}
   239  	if length > 32 {
   240  		return key[:32]
   241  	}
   242  	hash := sha256.Sum256(key)
   243  	switch {
   244  	case length > 24:
   245  		return append(key[:length:length], hash[:32-length]...)
   246  	case length > 16:
   247  		return append(key[:length:length], hash[:24-length]...)
   248  	default:
   249  		return append(key[:length:length], hash[:16-length]...)
   250  	}
   251  }
   252  
   253  // PKCS5Padding appends padding data to plaintext following the PKCS#5 standard.
   254  func PKCS5Padding(plaintext []byte, blockSize int) []byte {
   255  	padding := blockSize - len(plaintext)%blockSize         // 需要padding的数目
   256  	padText := bytes.Repeat([]byte{byte(padding)}, padding) // 生成填充文本
   257  	return append(plaintext, padText...)
   258  }
   259  
   260  // PKCS5UnPadding removes padding data from paddedText following the PKCS#5 standard.
   261  func PKCS5UnPadding(paddedText []byte) []byte {
   262  	length := len(paddedText)
   263  	unPadding := int(paddedText[length-1])
   264  	return paddedText[:(length - unPadding)]
   265  }
   266  
   267  func defaultInt(x, defaultValue int) int {
   268  	if x == 0 {
   269  		return defaultValue
   270  	}
   271  	return x
   272  }