1// Copyright 2022 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14// 15//////////////////////////////////////////////////////////////////////////////// 16 17package aead 18 19import ( 20 "bytes" 21 "crypto/aes" 22 "crypto/cipher" 23 "errors" 24 "fmt" 25) 26 27// TODO(b/201070904): Rename to AESGCMInsecureNonce and simplify by getting rid 28// of the prependIV bool. 29 30const ( 31 // aesGCMMaxPlaintextSize is the maximum plaintext size defined by RFC 5116. 32 aesGCMMaxPlaintextSize = (1 << 36) - 31 33 34 intSize = 32 << (^uint(0) >> 63) // 32 or 64 35 maxInt = 1<<(intSize-1) - 1 36 maxIntPlaintextSize = maxInt - AESGCMIVSize - AESGCMTagSize 37 38 minNoIVCiphertextSize = AESGCMTagSize 39 minPrependIVCiphertextSize = AESGCMIVSize + AESGCMTagSize 40) 41 42// AESGCMInsecureIV is an insecure implementation of the AEAD interface that 43// permits the user to set the IV. 44type AESGCMInsecureIV struct { 45 Key []byte 46 prependIV bool 47} 48 49// NewAESGCMInsecureIV returns an AESGCMInsecureIV instance, where key is the 50// AES key with length 16 bytes (AES-128) or 32 bytes (AES-256). 51// 52// If prependIV is true, both the ciphertext returned from Encrypt and passed 53// into Decrypt are prefixed with the IV. 54func NewAESGCMInsecureIV(key []byte, prependIV bool) (*AESGCMInsecureIV, error) { 55 keySize := uint32(len(key)) 56 if err := ValidateAESKeySize(keySize); err != nil { 57 return nil, fmt.Errorf("invalid AES key size: %s", err) 58 } 59 return &AESGCMInsecureIV{ 60 Key: key, 61 prependIV: prependIV, 62 }, nil 63} 64 65// Encrypt encrypts plaintext with iv as the initialization vector and 66// associatedData as associated data. 67// 68// If prependIV is true, the returned ciphertext contains both the IV used for 69// encryption and the actual ciphertext. 70// If false, the returned ciphertext contains only the actual ciphertext. 71// 72// Note: The crypto library's AES-GCM implementation always returns the 73// ciphertext with an AESGCMTagSize (16-byte) tag. 74func (i *AESGCMInsecureIV) Encrypt(iv, plaintext, associatedData []byte) ([]byte, error) { 75 if got, want := len(iv), AESGCMIVSize; got != want { 76 return nil, fmt.Errorf("unexpected IV size: got %d, want %d", got, want) 77 } 78 // Seal() checks plaintext length, but this duplicated check avoids panic. 79 var maxPlaintextSize uint64 = maxIntPlaintextSize 80 if maxIntPlaintextSize > aesGCMMaxPlaintextSize { 81 maxPlaintextSize = aesGCMMaxPlaintextSize 82 } 83 if uint64(len(plaintext)) > maxPlaintextSize { 84 return nil, fmt.Errorf("plaintext too long: got %d", len(plaintext)) 85 } 86 87 cipher, err := i.newCipher() 88 if err != nil { 89 return nil, err 90 } 91 if !i.prependIV { 92 return cipher.Seal(nil, iv, plaintext, associatedData), nil 93 } 94 // Make the capacity of dst large enough so that both the IV and the ciphertext fit inside. 95 dst := make([]byte, 0, AESGCMIVSize+len(plaintext)+cipher.Overhead()) 96 dst = append(dst, iv...) 97 // Seal appends the ciphertext to dst. So the final output is: iv || ciphertext. 98 return cipher.Seal(dst, iv, plaintext, associatedData), nil 99} 100 101// Decrypt decrypts ciphertext with iv as the initialization vector and 102// associatedData as associated data. 103// 104// If prependIV is true, the iv argument and the first AESGCMIVSize bytes of 105// ciphertext must be equal. The ciphertext argument is as follows: 106// 107// | iv | actual ciphertext | tag | 108// 109// If false, the ciphertext argument is as follows: 110// 111// | actual ciphertext | tag | 112func (i *AESGCMInsecureIV) Decrypt(iv, ciphertext, associatedData []byte) ([]byte, error) { 113 if len(iv) != AESGCMIVSize { 114 return nil, fmt.Errorf("unexpected IV size: got %d, want %d", len(iv), AESGCMIVSize) 115 } 116 117 var actualCiphertext []byte 118 if i.prependIV { 119 if len(ciphertext) < minPrependIVCiphertextSize { 120 return nil, fmt.Errorf("ciphertext too short: got %d, want >= %d", len(ciphertext), minPrependIVCiphertextSize) 121 } 122 if !bytes.Equal(iv, ciphertext[:AESGCMIVSize]) { 123 return nil, fmt.Errorf("unequal IVs: iv argument %x, ct prefix %x", iv, ciphertext[:AESGCMIVSize]) 124 } 125 actualCiphertext = ciphertext[AESGCMIVSize:] 126 } else { 127 if len(ciphertext) < minNoIVCiphertextSize { 128 return nil, fmt.Errorf("ciphertext too short: got %d, want >= %d", len(ciphertext), minNoIVCiphertextSize) 129 } 130 actualCiphertext = ciphertext 131 } 132 133 cipher, err := i.newCipher() 134 if err != nil { 135 return nil, err 136 } 137 plaintext, err := cipher.Open(nil, iv, actualCiphertext, associatedData) 138 if err != nil { 139 return nil, err 140 } 141 return plaintext, nil 142} 143 144// newCipher creates a new AES-GCM cipher using the given key and the crypto 145// library. 146func (i *AESGCMInsecureIV) newCipher() (cipher.AEAD, error) { 147 aesCipher, err := aes.NewCipher(i.Key) 148 if err != nil { 149 return nil, errors.New("failed to initialize cipher") 150 } 151 ret, err := cipher.NewGCM(aesCipher) 152 if err != nil { 153 return nil, errors.New("failed to initialize cipher") 154 } 155 return ret, nil 156} 157