// 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/subtle" "encoding/binary" "fmt" "math" // Placeholder for internal crypto/cipher allowlist, please ignore. // Placeholder for internal crypto/subtle allowlist, please ignore. // to allow import of "crypto/subte" "github.com/google/tink/go/subtle/random" ) const ( // AESGCMSIVNonceSize is the acceptable IV size defined by RFC 8452. AESGCMSIVNonceSize = 12 // aesgcmsivBlockSize is the block size that AES-GCM-SIV uses. This is the // size for the tag, the KDF etc. // Note: this value is the same as AES block size. aesgcmsivBlockSize = 16 // aesgcmsivTagSize is the byte-length of the authentication tag produced by // AES-GCM-SIV. aesgcmsivTagSize = aesgcmsivBlockSize // aesgcmsivPolyvalSize is the byte-length of result produced by the // POLYVAL function. aesgcmsivPolyvalSize = aesgcmsivBlockSize ) // AESGCMSIV is an implementation of AEAD interface. type AESGCMSIV struct { Key []byte } // NewAESGCMSIV returns an AESGCMSIV instance. // The key argument should be the AES key, either 16 or 32 bytes to select // AES-128 or AES-256. func NewAESGCMSIV(key []byte) (*AESGCMSIV, error) { keySize := uint32(len(key)) if err := ValidateAESKeySize(keySize); err != nil { return nil, fmt.Errorf("aes_gcm_siv: %s", err) } return &AESGCMSIV{Key: key}, nil } // Encrypt encrypts plaintext with associatedData. // // The resulting ciphertext consists of three parts: // (1) the Nonce used for encryption // (2) the actual ciphertext // (3) the authentication tag. func (a *AESGCMSIV) Encrypt(plaintext, associatedData []byte) ([]byte, error) { if len(plaintext) > math.MaxInt32-AESGCMSIVNonceSize-aesgcmsivTagSize { return nil, fmt.Errorf("aes_gcm_siv: plaintext too long") } if len(associatedData) > math.MaxInt32 { return nil, fmt.Errorf("aes_gcm_siv: associatedData too long") } nonce := random.GetRandomBytes(uint32(AESGCMSIVNonceSize)) authKey, encKey, err := a.deriveKeys(nonce) if err != nil { return nil, err } polyval, err := a.computePolyval(authKey, plaintext, associatedData) if err != nil { return nil, err } tag, err := a.computeTag(polyval, nonce, encKey) if err != nil { return nil, err } ct, err := a.aesCTR(encKey, tag, plaintext) if err != nil { return nil, err } ret := make([]byte, 0, AESGCMSIVNonceSize+aesgcmsivTagSize+len(plaintext)) ret = append(ret, nonce...) ret = append(ret, ct...) ret = append(ret, tag...) return ret, nil } // Decrypt decrypts ciphertext with associatedData. func (a *AESGCMSIV) Decrypt(ciphertext, associatedData []byte) ([]byte, error) { if len(ciphertext) < AESGCMSIVNonceSize+aesgcmsivTagSize { return nil, fmt.Errorf("aes_gcm_siv: ciphertext too short") } if len(ciphertext) > math.MaxInt32 { return nil, fmt.Errorf("aes_gcm_siv: ciphertext too long") } if len(associatedData) > math.MaxInt32 { return nil, fmt.Errorf("aes_gcm_siv: associatedData too long") } nonce := ciphertext[:AESGCMSIVNonceSize] tag := ciphertext[len(ciphertext)-aesgcmsivTagSize:] ciphertext = ciphertext[AESGCMSIVNonceSize : len(ciphertext)-aesgcmsivTagSize] authKey, encKey, err := a.deriveKeys(nonce) if err != nil { return nil, err } pt, err := a.aesCTR(encKey, tag, ciphertext) if err != nil { return nil, err } polyval, err := a.computePolyval(authKey, pt, associatedData) if err != nil { return nil, err } expectedTag, err := a.computeTag(polyval, nonce, encKey) if err != nil { return nil, err } if subtle.ConstantTimeCompare(expectedTag, tag) != 1 { return nil, fmt.Errorf("aes_gcm_siv: message authentication failure") } return pt, nil } // The KDF as described by the RFC #8452. This uses the AES-GCM-SIV key and // nonce to generate the authentication key and the encryption key. func (a *AESGCMSIV) deriveKeys(nonce []byte) ([]byte, []byte, error) { if len(nonce) != AESGCMSIVNonceSize { return nil, nil, fmt.Errorf("aes_gcm_siv: invalid nonce size") } nonceBlock := make([]byte, aesgcmsivBlockSize) copy(nonceBlock[aesgcmsivBlockSize-AESGCMSIVNonceSize:], nonce) block, err := aes.NewCipher(a.Key) if err != nil { return nil, nil, fmt.Errorf("aes_gcm_siv: failed to create block cipher, error: %v", err) } encBlock := make([]byte, block.BlockSize()) kdfAes := func(counter uint32, dst []byte) { binary.LittleEndian.PutUint32(nonceBlock[:4], counter) block.Encrypt(encBlock, nonceBlock) copy(dst, encBlock[0:8]) } authKey := make([]byte, aesgcmsivBlockSize) kdfAes(0, authKey[0:8]) kdfAes(1, authKey[8:16]) encKey := make([]byte, len(a.Key)) kdfAes(2, encKey[0:8]) kdfAes(3, encKey[8:16]) if len(a.Key) == 32 { kdfAes(4, encKey[16:24]) kdfAes(5, encKey[24:32]) } return authKey, encKey, nil } func (a *AESGCMSIV) computePolyval(authKey, pt, ad []byte) ([]byte, error) { lengthBlock := make([]byte, aesgcmsivBlockSize) binary.LittleEndian.PutUint64(lengthBlock[:8], uint64(len(ad))*8) binary.LittleEndian.PutUint64(lengthBlock[8:], uint64(len(pt))*8) p, err := NewPolyval(authKey) if err != nil { return nil, fmt.Errorf("aes_gcm_siv: failed to create polyval, error: %v", err) } p.Update(ad) p.Update(pt) p.Update(lengthBlock) polyval := p.Finish() return polyval[:], nil } func (a *AESGCMSIV) computeTag(polyval, nonce, encKey []byte) ([]byte, error) { if len(polyval) != aesgcmsivPolyvalSize { return nil, fmt.Errorf("aes_gcm_siv: polyval returned invalid sized response") } for i, val := range nonce { polyval[i] ^= val } polyval[aesgcmsivPolyvalSize-1] &= 0x7f block, err := aes.NewCipher(encKey) if err != nil { return nil, fmt.Errorf("aes_gcm_siv: failed to create block cipher, error: %v", err) } tag := make([]byte, aesgcmsivTagSize) block.Encrypt(tag, polyval) return tag, nil } // aesCTR implements the AES-CTR operation in AES-GCM-SIV. // Note that RFC 8452 defines AES-CTR differently compared to standard AES // in CTR mode: the way they increment the counter block is completely different. func (a *AESGCMSIV) aesCTR(key, tag, in []byte) ([]byte, error) { if len(tag) != aesgcmsivTagSize { return nil, fmt.Errorf("aes_gcm_siv: incorrect IV size for stream cipher") } block, err := aes.NewCipher(key) if err != nil { return nil, fmt.Errorf( "aes_gcm_siv: failed to create block cipher, error: %v", err) } counter := make([]byte, aesgcmsivBlockSize) copy(counter, tag) counter[aesgcmsivBlockSize-1] |= 0x80 counterInc := binary.LittleEndian.Uint32(counter[0:4]) output := make([]byte, len(in)) outputIdx := 0 keystreamBlock := make([]byte, block.BlockSize()) for len(in) > 0 { block.Encrypt(keystreamBlock, counter) counterInc++ binary.LittleEndian.PutUint32(counter[0:4], counterInc) n := xorBytes(output[outputIdx:], in, keystreamBlock) outputIdx += n in = in[n:] } return output, nil } // It would have been better to call xorBytes function defined in // "crypto/cipher/xor_*.go" to make use of the architechture optimisations. func xorBytes(dst, a, b []byte) int { n := len(a) if len(b) < n { n = len(b) } if n == 0 { return 0 } for i := 0; i < n; i++ { dst[i] = a[i] ^ b[i] } return n }