1*e7b1675dSTing-Kang Chang// Copyright 2020 Google LLC 2*e7b1675dSTing-Kang Chang// 3*e7b1675dSTing-Kang Chang// Licensed under the Apache License, Version 2.0 (the "License"); 4*e7b1675dSTing-Kang Chang// you may not use this file except in compliance with the License. 5*e7b1675dSTing-Kang Chang// You may obtain a copy of the License at 6*e7b1675dSTing-Kang Chang// 7*e7b1675dSTing-Kang Chang// http://www.apache.org/licenses/LICENSE-2.0 8*e7b1675dSTing-Kang Chang// 9*e7b1675dSTing-Kang Chang// Unless required by applicable law or agreed to in writing, software 10*e7b1675dSTing-Kang Chang// distributed under the License is distributed on an "AS IS" BASIS, 11*e7b1675dSTing-Kang Chang// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*e7b1675dSTing-Kang Chang// See the License for the specific language governing permissions and 13*e7b1675dSTing-Kang Chang// limitations under the License. 14*e7b1675dSTing-Kang Chang// 15*e7b1675dSTing-Kang Chang//////////////////////////////////////////////////////////////////////////////// 16*e7b1675dSTing-Kang Chang 17*e7b1675dSTing-Kang Chang// Package subtle provides subtle implementations of the DeterministicAEAD 18*e7b1675dSTing-Kang Chang// primitive. 19*e7b1675dSTing-Kang Changpackage subtle 20*e7b1675dSTing-Kang Chang 21*e7b1675dSTing-Kang Changimport ( 22*e7b1675dSTing-Kang Chang "crypto/aes" 23*e7b1675dSTing-Kang Chang "crypto/cipher" 24*e7b1675dSTing-Kang Chang "crypto/subtle" 25*e7b1675dSTing-Kang Chang "errors" 26*e7b1675dSTing-Kang Chang "fmt" 27*e7b1675dSTing-Kang Chang "math" 28*e7b1675dSTing-Kang Chang 29*e7b1675dSTing-Kang Chang // Placeholder for internal crypto/cipher allowlist, please ignore. 30*e7b1675dSTing-Kang Chang // Placeholder for internal crypto/subtle allowlist, please ignore. 31*e7b1675dSTing-Kang Chang) 32*e7b1675dSTing-Kang Chang 33*e7b1675dSTing-Kang Chang// AESSIV is an implementation of AES-SIV-CMAC as defined in 34*e7b1675dSTing-Kang Chang// https://tools.ietf.org/html/rfc5297. 35*e7b1675dSTing-Kang Chang// 36*e7b1675dSTing-Kang Chang// AESSIV implements a deterministic encryption with associated data (i.e. the 37*e7b1675dSTing-Kang Chang// DeterministicAEAD interface). Hence the implementation below is restricted 38*e7b1675dSTing-Kang Chang// to one AD component. 39*e7b1675dSTing-Kang Chang// 40*e7b1675dSTing-Kang Chang// Security Note: 41*e7b1675dSTing-Kang Chang// 42*e7b1675dSTing-Kang Chang// Chatterjee, Menezes and Sarkar analyze AES-SIV in Section 5.1 of 43*e7b1675dSTing-Kang Chang// https://www.math.uwaterloo.ca/~ajmeneze/publications/tightness.pdf 44*e7b1675dSTing-Kang Chang// 45*e7b1675dSTing-Kang Chang// Their analysis shows that AES-SIV is susceptible to an attack in 46*e7b1675dSTing-Kang Chang// a multi-user setting. Concretely, if an attacker knows the encryption 47*e7b1675dSTing-Kang Chang// of a message m encrypted and authenticated with k different keys, 48*e7b1675dSTing-Kang Chang// then it is possible to find one of the MAC keys in time 2^b / k 49*e7b1675dSTing-Kang Chang// where b is the size of the MAC key. A consequence of this attack 50*e7b1675dSTing-Kang Chang// is that 128-bit MAC keys give unsufficient security. 51*e7b1675dSTing-Kang Chang// Since 192-bit AES keys are not supported by tink for voodoo reasons 52*e7b1675dSTing-Kang Chang// and RFC 5297 only supports same size encryption and MAC keys this 53*e7b1675dSTing-Kang Chang// implies that keys must be 64 bytes (2*256 bits) long. 54*e7b1675dSTing-Kang Changtype AESSIV struct { 55*e7b1675dSTing-Kang Chang K1 []byte 56*e7b1675dSTing-Kang Chang K2 []byte 57*e7b1675dSTing-Kang Chang CmacK1 []byte 58*e7b1675dSTing-Kang Chang CmacK2 []byte 59*e7b1675dSTing-Kang Chang Cipher cipher.Block 60*e7b1675dSTing-Kang Chang} 61*e7b1675dSTing-Kang Chang 62*e7b1675dSTing-Kang Changconst ( 63*e7b1675dSTing-Kang Chang // AESSIVKeySize is the key size in bytes. 64*e7b1675dSTing-Kang Chang AESSIVKeySize = 64 65*e7b1675dSTing-Kang Chang 66*e7b1675dSTing-Kang Chang intSize = 32 << (^uint(0) >> 63) // 32 or 64 67*e7b1675dSTing-Kang Chang maxInt = 1<<(intSize-1) - 1 68*e7b1675dSTing-Kang Chang) 69*e7b1675dSTing-Kang Chang 70*e7b1675dSTing-Kang Chang// NewAESSIV returns an AESSIV instance. 71*e7b1675dSTing-Kang Changfunc NewAESSIV(key []byte) (*AESSIV, error) { 72*e7b1675dSTing-Kang Chang if len(key) != AESSIVKeySize { 73*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("aes_siv: invalid key size %d", len(key)) 74*e7b1675dSTing-Kang Chang } 75*e7b1675dSTing-Kang Chang 76*e7b1675dSTing-Kang Chang k1 := key[:32] 77*e7b1675dSTing-Kang Chang k2 := key[32:] 78*e7b1675dSTing-Kang Chang c, err := aes.NewCipher(k1) 79*e7b1675dSTing-Kang Chang if err != nil { 80*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("aes_siv: aes.NewCipher(%s) failed, %v", k1, err) 81*e7b1675dSTing-Kang Chang } 82*e7b1675dSTing-Kang Chang 83*e7b1675dSTing-Kang Chang block := make([]byte, aes.BlockSize) 84*e7b1675dSTing-Kang Chang c.Encrypt(block, block) 85*e7b1675dSTing-Kang Chang multiplyByX(block) 86*e7b1675dSTing-Kang Chang cmacK1 := make([]byte, aes.BlockSize) 87*e7b1675dSTing-Kang Chang copy(cmacK1, block) 88*e7b1675dSTing-Kang Chang multiplyByX(block) 89*e7b1675dSTing-Kang Chang cmacK2 := make([]byte, aes.BlockSize) 90*e7b1675dSTing-Kang Chang copy(cmacK2, block) 91*e7b1675dSTing-Kang Chang 92*e7b1675dSTing-Kang Chang return &AESSIV{ 93*e7b1675dSTing-Kang Chang K1: k1, 94*e7b1675dSTing-Kang Chang K2: k2, 95*e7b1675dSTing-Kang Chang CmacK1: cmacK1, 96*e7b1675dSTing-Kang Chang CmacK2: cmacK2, 97*e7b1675dSTing-Kang Chang Cipher: c, 98*e7b1675dSTing-Kang Chang }, nil 99*e7b1675dSTing-Kang Chang} 100*e7b1675dSTing-Kang Chang 101*e7b1675dSTing-Kang Chang// multiplyByX multiplies an element in GF(2^128) by its generator. 102*e7b1675dSTing-Kang Chang// 103*e7b1675dSTing-Kang Chang// This function is incorrectly named "doubling" in section 2.3 of RFC 5297. 104*e7b1675dSTing-Kang Changfunc multiplyByX(block []byte) { 105*e7b1675dSTing-Kang Chang carry := int(block[0] >> 7) 106*e7b1675dSTing-Kang Chang for i := 0; i < aes.BlockSize-1; i++ { 107*e7b1675dSTing-Kang Chang block[i] = (block[i] << 1) | (block[i+1] >> 7) 108*e7b1675dSTing-Kang Chang } 109*e7b1675dSTing-Kang Chang 110*e7b1675dSTing-Kang Chang block[aes.BlockSize-1] = (block[aes.BlockSize-1] << 1) ^ byte(subtle.ConstantTimeSelect(carry, 0x87, 0x00)) 111*e7b1675dSTing-Kang Chang} 112*e7b1675dSTing-Kang Chang 113*e7b1675dSTing-Kang Chang// EncryptDeterministically deterministically encrypts plaintext with associatedData. 114*e7b1675dSTing-Kang Changfunc (asc *AESSIV) EncryptDeterministically(plaintext, associatedData []byte) ([]byte, error) { 115*e7b1675dSTing-Kang Chang if len(plaintext) > maxInt-aes.BlockSize { 116*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("aes_siv: plaintext too long") 117*e7b1675dSTing-Kang Chang } 118*e7b1675dSTing-Kang Chang siv := make([]byte, aes.BlockSize) 119*e7b1675dSTing-Kang Chang asc.s2v(plaintext, associatedData, siv) 120*e7b1675dSTing-Kang Chang 121*e7b1675dSTing-Kang Chang ct := make([]byte, len(plaintext)+aes.BlockSize) 122*e7b1675dSTing-Kang Chang copy(ct[:aes.BlockSize], siv) 123*e7b1675dSTing-Kang Chang if err := asc.ctrCrypt(siv, plaintext, ct[aes.BlockSize:]); err != nil { 124*e7b1675dSTing-Kang Chang return nil, err 125*e7b1675dSTing-Kang Chang } 126*e7b1675dSTing-Kang Chang 127*e7b1675dSTing-Kang Chang return ct, nil 128*e7b1675dSTing-Kang Chang} 129*e7b1675dSTing-Kang Chang 130*e7b1675dSTing-Kang Chang// DecryptDeterministically deterministically decrypts ciphertext with associatedData. 131*e7b1675dSTing-Kang Changfunc (asc *AESSIV) DecryptDeterministically(ciphertext, associatedData []byte) ([]byte, error) { 132*e7b1675dSTing-Kang Chang if len(ciphertext) < aes.BlockSize { 133*e7b1675dSTing-Kang Chang return nil, errors.New("aes_siv: ciphertext is too short") 134*e7b1675dSTing-Kang Chang } 135*e7b1675dSTing-Kang Chang 136*e7b1675dSTing-Kang Chang pt := make([]byte, len(ciphertext)-aes.BlockSize) 137*e7b1675dSTing-Kang Chang siv := ciphertext[:aes.BlockSize] 138*e7b1675dSTing-Kang Chang asc.ctrCrypt(siv, ciphertext[aes.BlockSize:], pt) 139*e7b1675dSTing-Kang Chang s2v := make([]byte, aes.BlockSize) 140*e7b1675dSTing-Kang Chang asc.s2v(pt, associatedData, s2v) 141*e7b1675dSTing-Kang Chang 142*e7b1675dSTing-Kang Chang diff := byte(0) 143*e7b1675dSTing-Kang Chang for i := 0; i < aes.BlockSize; i++ { 144*e7b1675dSTing-Kang Chang diff |= siv[i] ^ s2v[i] 145*e7b1675dSTing-Kang Chang } 146*e7b1675dSTing-Kang Chang if diff != 0 { 147*e7b1675dSTing-Kang Chang return nil, errors.New("aes_siv: invalid ciphertext") 148*e7b1675dSTing-Kang Chang } 149*e7b1675dSTing-Kang Chang 150*e7b1675dSTing-Kang Chang return pt, nil 151*e7b1675dSTing-Kang Chang} 152*e7b1675dSTing-Kang Chang 153*e7b1675dSTing-Kang Chang// ctrCrypt encrypts (or decrypts) the bytes in in using an SIV and writes the 154*e7b1675dSTing-Kang Chang// result to out. 155*e7b1675dSTing-Kang Changfunc (asc *AESSIV) ctrCrypt(siv, in, out []byte) error { 156*e7b1675dSTing-Kang Chang // siv might be used outside of ctrCrypt(), so making a copy of it. 157*e7b1675dSTing-Kang Chang iv := make([]byte, aes.BlockSize) 158*e7b1675dSTing-Kang Chang copy(iv, siv) 159*e7b1675dSTing-Kang Chang iv[8] &= 0x7f 160*e7b1675dSTing-Kang Chang iv[12] &= 0x7f 161*e7b1675dSTing-Kang Chang 162*e7b1675dSTing-Kang Chang c, err := aes.NewCipher(asc.K2) 163*e7b1675dSTing-Kang Chang if err != nil { 164*e7b1675dSTing-Kang Chang return fmt.Errorf("aes_siv: aes.NewCipher(%s) failed, %v", asc.K2, err) 165*e7b1675dSTing-Kang Chang } 166*e7b1675dSTing-Kang Chang 167*e7b1675dSTing-Kang Chang steam := cipher.NewCTR(c, iv) 168*e7b1675dSTing-Kang Chang steam.XORKeyStream(out, in) 169*e7b1675dSTing-Kang Chang return nil 170*e7b1675dSTing-Kang Chang} 171*e7b1675dSTing-Kang Chang 172*e7b1675dSTing-Kang Chang// s2v is a Pseudo-Random Function (PRF) construction: 173*e7b1675dSTing-Kang Chang// https://tools.ietf.org/html/rfc5297. 174*e7b1675dSTing-Kang Changfunc (asc *AESSIV) s2v(msg, ad, siv []byte) { 175*e7b1675dSTing-Kang Chang block := make([]byte, aes.BlockSize) 176*e7b1675dSTing-Kang Chang asc.cmac(block, block) 177*e7b1675dSTing-Kang Chang multiplyByX(block) 178*e7b1675dSTing-Kang Chang 179*e7b1675dSTing-Kang Chang adMac := make([]byte, aes.BlockSize) 180*e7b1675dSTing-Kang Chang asc.cmac(ad, adMac) 181*e7b1675dSTing-Kang Chang xorBlock(adMac, block) 182*e7b1675dSTing-Kang Chang 183*e7b1675dSTing-Kang Chang if len(msg) >= aes.BlockSize { 184*e7b1675dSTing-Kang Chang asc.cmacLong(msg, block, siv) 185*e7b1675dSTing-Kang Chang } else { 186*e7b1675dSTing-Kang Chang multiplyByX(block) 187*e7b1675dSTing-Kang Chang for i := 0; i < len(msg); i++ { 188*e7b1675dSTing-Kang Chang block[i] ^= msg[i] 189*e7b1675dSTing-Kang Chang } 190*e7b1675dSTing-Kang Chang block[len(msg)] ^= 0x80 191*e7b1675dSTing-Kang Chang asc.cmac(block, siv) 192*e7b1675dSTing-Kang Chang } 193*e7b1675dSTing-Kang Chang} 194*e7b1675dSTing-Kang Chang 195*e7b1675dSTing-Kang Chang// cmacLong computes CMAC(XorEnd(data, last)), where XorEnd xors the bytes in 196*e7b1675dSTing-Kang Chang// last to the last bytes in data. 197*e7b1675dSTing-Kang Chang// 198*e7b1675dSTing-Kang Chang// The size of the data must be at least 16 bytes. 199*e7b1675dSTing-Kang Changfunc (asc *AESSIV) cmacLong(data, last, mac []byte) { 200*e7b1675dSTing-Kang Chang block := make([]byte, aes.BlockSize) 201*e7b1675dSTing-Kang Chang copy(block, data[:aes.BlockSize]) 202*e7b1675dSTing-Kang Chang 203*e7b1675dSTing-Kang Chang idx := aes.BlockSize 204*e7b1675dSTing-Kang Chang for aes.BlockSize <= len(data)-idx { 205*e7b1675dSTing-Kang Chang asc.Cipher.Encrypt(block, block) 206*e7b1675dSTing-Kang Chang xorBlock(data[idx:idx+aes.BlockSize], block) 207*e7b1675dSTing-Kang Chang idx += aes.BlockSize 208*e7b1675dSTing-Kang Chang } 209*e7b1675dSTing-Kang Chang 210*e7b1675dSTing-Kang Chang remaining := len(data) - idx 211*e7b1675dSTing-Kang Chang for i := 0; i < aes.BlockSize-remaining; i++ { 212*e7b1675dSTing-Kang Chang block[remaining+i] ^= last[i] 213*e7b1675dSTing-Kang Chang } 214*e7b1675dSTing-Kang Chang if remaining == 0 { 215*e7b1675dSTing-Kang Chang xorBlock(asc.CmacK1, block) 216*e7b1675dSTing-Kang Chang } else { 217*e7b1675dSTing-Kang Chang asc.Cipher.Encrypt(block, block) 218*e7b1675dSTing-Kang Chang for i := 0; i < remaining; i++ { 219*e7b1675dSTing-Kang Chang block[i] ^= last[aes.BlockSize-remaining+i] 220*e7b1675dSTing-Kang Chang block[i] ^= data[idx+i] 221*e7b1675dSTing-Kang Chang } 222*e7b1675dSTing-Kang Chang block[remaining] ^= 0x80 223*e7b1675dSTing-Kang Chang xorBlock(asc.CmacK2, block) 224*e7b1675dSTing-Kang Chang } 225*e7b1675dSTing-Kang Chang 226*e7b1675dSTing-Kang Chang asc.Cipher.Encrypt(mac, block) 227*e7b1675dSTing-Kang Chang} 228*e7b1675dSTing-Kang Chang 229*e7b1675dSTing-Kang Chang// cmac computes a CMAC of some data. 230*e7b1675dSTing-Kang Changfunc (asc *AESSIV) cmac(data, mac []byte) { 231*e7b1675dSTing-Kang Chang numBs := int(math.Ceil(float64(len(data)) / aes.BlockSize)) 232*e7b1675dSTing-Kang Chang if numBs == 0 { 233*e7b1675dSTing-Kang Chang numBs = 1 234*e7b1675dSTing-Kang Chang } 235*e7b1675dSTing-Kang Chang lastBSize := len(data) - (numBs-1)*aes.BlockSize 236*e7b1675dSTing-Kang Chang 237*e7b1675dSTing-Kang Chang block := make([]byte, aes.BlockSize) 238*e7b1675dSTing-Kang Chang idx := 0 239*e7b1675dSTing-Kang Chang for i := 0; i < numBs-1; i++ { 240*e7b1675dSTing-Kang Chang xorBlock(data[idx:idx+aes.BlockSize], block) 241*e7b1675dSTing-Kang Chang asc.Cipher.Encrypt(block, block) 242*e7b1675dSTing-Kang Chang idx += aes.BlockSize 243*e7b1675dSTing-Kang Chang } 244*e7b1675dSTing-Kang Chang for j := 0; j < lastBSize; j++ { 245*e7b1675dSTing-Kang Chang block[j] ^= data[idx+j] 246*e7b1675dSTing-Kang Chang } 247*e7b1675dSTing-Kang Chang 248*e7b1675dSTing-Kang Chang if lastBSize == aes.BlockSize { 249*e7b1675dSTing-Kang Chang xorBlock(asc.CmacK1, block) 250*e7b1675dSTing-Kang Chang } else { 251*e7b1675dSTing-Kang Chang block[lastBSize] ^= 0x80 252*e7b1675dSTing-Kang Chang xorBlock(asc.CmacK2, block) 253*e7b1675dSTing-Kang Chang } 254*e7b1675dSTing-Kang Chang 255*e7b1675dSTing-Kang Chang asc.Cipher.Encrypt(mac, block) 256*e7b1675dSTing-Kang Chang} 257*e7b1675dSTing-Kang Chang 258*e7b1675dSTing-Kang Chang// xorBlock sets block[i] = x[i] ^ block[i]. 259*e7b1675dSTing-Kang Changfunc xorBlock(x, block []byte) { 260*e7b1675dSTing-Kang Chang for i := 0; i < aes.BlockSize; i++ { 261*e7b1675dSTing-Kang Chang block[i] ^= x[i] 262*e7b1675dSTing-Kang Chang } 263*e7b1675dSTing-Kang Chang} 264