// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package subtle import ( "crypto/aes" "crypto/cipher" "fmt" // Placeholder for internal crypto/cipher allowlist, please ignore. "github.com/google/tink/go/subtle/random" ) const ( // AESCTRMinIVSize is the minimum IV size that this implementation supports. AESCTRMinIVSize = 12 ) // AESCTR is an implementation of AEAD interface. type AESCTR struct { Key []byte IVSize int } // NewAESCTR returns an AESCTR instance. // The key argument should be the AES key, either 16 or 32 bytes to select // AES-128 or AES-256. // ivSize specifies the size of the IV in bytes. func NewAESCTR(key []byte, ivSize int) (*AESCTR, error) { keySize := uint32(len(key)) if err := ValidateAESKeySize(keySize); err != nil { return nil, fmt.Errorf("aes_ctr: %s", err) } if ivSize < AESCTRMinIVSize || ivSize > aes.BlockSize { return nil, fmt.Errorf("aes_ctr: invalid IV size: %d", ivSize) } return &AESCTR{Key: key, IVSize: ivSize}, nil } // Encrypt encrypts plaintext using AES in CTR mode. // The resulting ciphertext consists of two parts: // (1) the IV used for encryption and (2) the actual ciphertext. func (a *AESCTR) Encrypt(plaintext []byte) ([]byte, error) { if len(plaintext) > maxInt-a.IVSize { return nil, fmt.Errorf("aes_ctr: plaintext too long") } iv := a.newIV() stream, err := newCipher(a.Key, iv) if err != nil { return nil, err } ciphertext := make([]byte, a.IVSize+len(plaintext)) if n := copy(ciphertext, iv); n != a.IVSize { return nil, fmt.Errorf("aes_ctr: failed to copy IV (copied %d/%d bytes)", n, a.IVSize) } stream.XORKeyStream(ciphertext[a.IVSize:], plaintext) return ciphertext, nil } // Decrypt decrypts ciphertext. func (a *AESCTR) Decrypt(ciphertext []byte) ([]byte, error) { if len(ciphertext) < a.IVSize { return nil, fmt.Errorf("aes_ctr: ciphertext too short") } iv := ciphertext[:a.IVSize] stream, err := newCipher(a.Key, iv) if err != nil { return nil, err } plaintext := make([]byte, len(ciphertext)-a.IVSize) stream.XORKeyStream(plaintext, ciphertext[a.IVSize:]) return plaintext, nil } // newIV creates a new IV for encryption. func (a *AESCTR) newIV() []byte { return random.GetRandomBytes(uint32(a.IVSize)) } // newCipher creates a new AES-CTR cipher using the given key, IV and the crypto library. func newCipher(key, iv []byte) (cipher.Stream, error) { block, err := aes.NewCipher(key) if err != nil { return nil, fmt.Errorf("aes_ctr: failed to create block cipher, error: %v", err) } // If the IV is less than BlockSize bytes we need to pad it with zeros // otherwise NewCTR will panic. if len(iv) < aes.BlockSize { paddedIV := make([]byte, aes.BlockSize) if n := copy(paddedIV, iv); n != len(iv) { return nil, fmt.Errorf("aes_ctr: failed to pad IV") } return cipher.NewCTR(block, paddedIV), nil } return cipher.NewCTR(block, iv), nil }