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 }