github.com/trustbloc/kms-go@v1.1.2/crypto/tinkcrypto/primitive/aead/subtle/aes_cbc.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package subtle
     8  
     9  import (
    10  	"bytes"
    11  	"crypto/aes"
    12  	"crypto/cipher"
    13  	"errors"
    14  	"fmt"
    15  
    16  	"github.com/google/tink/go/subtle/random"
    17  )
    18  
    19  const (
    20  	// AESCBCIVSize is the IV size that this implementation supports.
    21  	AESCBCIVSize = 16
    22  )
    23  
    24  // AESCBC is an implementation of AEAD interface.
    25  type AESCBC struct {
    26  	Key []byte
    27  }
    28  
    29  // NewAESCBC returns an AESCBC instance.
    30  // The key argument should be the AES key, either 16, 24 or 32 bytes to select
    31  // AES-128, AES-192 or AES-256.
    32  func NewAESCBC(key []byte) (*AESCBC, error) {
    33  	keySize := uint32(len(key))
    34  	if err := ValidateAESKeySize(keySize); err != nil {
    35  		return nil, fmt.Errorf("aes_cbc: NewAESCBC() %w", err)
    36  	}
    37  
    38  	return &AESCBC{Key: key}, nil
    39  }
    40  
    41  // Encrypt encrypts plaintext using AES in CTR mode.
    42  // The resulting ciphertext consists of two parts:
    43  // (1) the IV used for encryption and (2) the actual ciphertext.
    44  func (a *AESCBC) Encrypt(plaintext []byte) ([]byte, error) {
    45  	plainTextSize := len(plaintext)
    46  	if plainTextSize > maxInt-AESCBCIVSize {
    47  		return nil, errors.New("aes_cbc: plaintext too long")
    48  	}
    49  
    50  	iv := a.newIV()
    51  
    52  	cbc, err := newCipher(a.Key, iv, false)
    53  	if err != nil {
    54  		return nil, fmt.Errorf("aes_cbc: Encrypt() %w", err)
    55  	}
    56  
    57  	ciphertext := make([]byte, AESCBCIVSize+plainTextSize)
    58  	if n := copy(ciphertext, iv); n != AESCBCIVSize {
    59  		return nil, fmt.Errorf("aes_cbc: failed to copy IV (copied %d/%d bytes)", n, AESCBCIVSize)
    60  	}
    61  
    62  	if n := copy(ciphertext[AESCBCIVSize:], plaintext); n != plainTextSize {
    63  		return nil, fmt.Errorf("aes_cbc: failed to copy plaintext (copied %d/%d bytes)", n, plainTextSize)
    64  	}
    65  
    66  	ciphertext = Pad(ciphertext, plainTextSize, cbc.BlockSize())
    67  
    68  	cbc.CryptBlocks(ciphertext[AESCBCIVSize:], ciphertext[AESCBCIVSize:])
    69  
    70  	return ciphertext, nil
    71  }
    72  
    73  // Decrypt decrypts ciphertext.
    74  func (a *AESCBC) Decrypt(ciphertext []byte) ([]byte, error) {
    75  	ciphertextSize := len(ciphertext)
    76  	if ciphertextSize < AESCBCIVSize {
    77  		return nil, errors.New("aes_cbc: ciphertext too short")
    78  	}
    79  
    80  	iv := ciphertext[:AESCBCIVSize]
    81  
    82  	cbc, err := newCipher(a.Key, iv, true)
    83  	if err != nil {
    84  		return nil, fmt.Errorf("aes_cbc: Decrypt() %w", err)
    85  	}
    86  
    87  	blockSize := cbc.BlockSize()
    88  
    89  	if len(ciphertext[AESCBCIVSize:])%blockSize > 0 {
    90  		return nil, errors.New("aes_cbc: invalid ciphertext padding")
    91  	}
    92  
    93  	plaintext := make([]byte, ciphertextSize-AESCBCIVSize)
    94  	cbc.CryptBlocks(plaintext, ciphertext[AESCBCIVSize:])
    95  
    96  	if len(plaintext) == 0 {
    97  		return plaintext, nil
    98  	}
    99  
   100  	return Unpad(plaintext), nil
   101  }
   102  
   103  // newIV creates a new IV for encryption.
   104  func (a *AESCBC) newIV() []byte {
   105  	return random.GetRandomBytes(uint32(AESCBCIVSize))
   106  }
   107  
   108  // newCipher creates a new AES-CBC cipher using the given key, IV and the crypto library.
   109  func newCipher(key, iv []byte, decrypt bool) (cipher.BlockMode, error) {
   110  	block, err := aes.NewCipher(key)
   111  	if err != nil {
   112  		return nil, fmt.Errorf("aes_cbc: failed to create block cipher, error: %w", err)
   113  	}
   114  
   115  	// IV size must be at least BlockSize.
   116  	if len(iv) < aes.BlockSize {
   117  		return nil, errors.New("aes_cbc: invalid iv size")
   118  	}
   119  
   120  	if decrypt {
   121  		return cipher.NewCBCDecrypter(block, iv), nil
   122  	}
   123  
   124  	return cipher.NewCBCEncrypter(block, iv), nil
   125  }
   126  
   127  // Pad text to blockSize.
   128  func Pad(text []byte, originalTextSize, blockSize int) []byte {
   129  	// pad to block size if needed. The value of missing is between 0 and blockSize.
   130  	missing := blockSize - (originalTextSize % blockSize)
   131  	if missing > 0 {
   132  		text = append(text, bytes.Repeat([]byte{byte(missing)}, missing)...)
   133  
   134  		return text
   135  	}
   136  
   137  	// return original text if missing =< 0
   138  	return text
   139  }
   140  
   141  // Unpad a padded text of blockSize.
   142  func Unpad(text []byte) []byte {
   143  	last := text[len(text)-1]
   144  	count := int(last)
   145  
   146  	// check for padding, count is the padding value.
   147  	if count > 0 {
   148  		padding := bytes.Repeat([]byte{last}, count)
   149  		if bytes.HasSuffix(text, padding) {
   150  			// padding was found, trim it and return remaining plaintext.
   151  			return text[:len(text)-len(padding)]
   152  		}
   153  	}
   154  
   155  	// count is <= 0 or text has no padding, return text as is.
   156  	return text
   157  }