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 Changpackage subtle 18*e7b1675dSTing-Kang Chang 19*e7b1675dSTing-Kang Changimport ( 20*e7b1675dSTing-Kang Chang "encoding/binary" 21*e7b1675dSTing-Kang Chang "fmt" 22*e7b1675dSTing-Kang Chang) 23*e7b1675dSTing-Kang Chang 24*e7b1675dSTing-Kang Changconst ( 25*e7b1675dSTing-Kang Chang // PolyvalBlockSize is the block size (in bytes) that POLYVAL uses. 26*e7b1675dSTing-Kang Chang PolyvalBlockSize = 16 27*e7b1675dSTing-Kang Chang 28*e7b1675dSTing-Kang Chang u32Sel0 uint32 = 0x11111111 29*e7b1675dSTing-Kang Chang u32Sel1 uint32 = 0x22222222 30*e7b1675dSTing-Kang Chang u32Sel2 uint32 = 0x44444444 31*e7b1675dSTing-Kang Chang u32Sel3 uint32 = 0x88888888 32*e7b1675dSTing-Kang Chang 33*e7b1675dSTing-Kang Chang u64Sel0 uint64 = 0x1111111111111111 34*e7b1675dSTing-Kang Chang u64Sel1 uint64 = 0x2222222222222222 35*e7b1675dSTing-Kang Chang u64Sel2 uint64 = 0x4444444444444444 36*e7b1675dSTing-Kang Chang u64Sel3 uint64 = 0x8888888888888888 37*e7b1675dSTing-Kang Chang) 38*e7b1675dSTing-Kang Chang 39*e7b1675dSTing-Kang Chang// Polyval (RFC 8452) is a universal hash function which operates on GF(2^128) 40*e7b1675dSTing-Kang Chang// and can be used for constructing a Message Authentication Code (MAC). 41*e7b1675dSTing-Kang Chang// See Section 3 of go/rfc/8452 for definition. 42*e7b1675dSTing-Kang Changtype Polyval interface { 43*e7b1675dSTing-Kang Chang // update the accumulator in the object with the blocks from data. If data 44*e7b1675dSTing-Kang Chang // is not a multiple of 16 bytes, it is automatically zero padded. 45*e7b1675dSTing-Kang Chang Update(data []byte) 46*e7b1675dSTing-Kang Chang 47*e7b1675dSTing-Kang Chang // finish completes the polyval computation and returns the result. 48*e7b1675dSTing-Kang Chang Finish() [PolyvalBlockSize]byte 49*e7b1675dSTing-Kang Chang} 50*e7b1675dSTing-Kang Chang 51*e7b1675dSTing-Kang Chang// fieldElement represents a value in GF(2^128). 52*e7b1675dSTing-Kang Chang// In order to reflect the Polyval standard and make binary.LittleEndian suitable 53*e7b1675dSTing-Kang Chang// for marshaling these values, the bits are stored in little endian order. 54*e7b1675dSTing-Kang Chang// For example: 55*e7b1675dSTing-Kang Chang// the coefficient of x^0 can be obtained by v.lo & 1. 56*e7b1675dSTing-Kang Chang// the coefficient of x^63 can be obtained by v.lo >> 63. 57*e7b1675dSTing-Kang Chang// the coefficient of x^64 can be obtained by v.hi & 1. 58*e7b1675dSTing-Kang Chang// the coefficient of x^127 can be obtained by v.hi >> 63. 59*e7b1675dSTing-Kang Changtype fieldElement struct { 60*e7b1675dSTing-Kang Chang lo, hi uint64 61*e7b1675dSTing-Kang Chang} 62*e7b1675dSTing-Kang Chang 63*e7b1675dSTing-Kang Chang// polyval implements the POLYVAL function as defined by go/rfc/8452. 64*e7b1675dSTing-Kang Changtype polyval struct { 65*e7b1675dSTing-Kang Chang key fieldElement 66*e7b1675dSTing-Kang Chang acc fieldElement 67*e7b1675dSTing-Kang Chang} 68*e7b1675dSTing-Kang Chang 69*e7b1675dSTing-Kang Chang// Assert that polyval implements Polyval interface 70*e7b1675dSTing-Kang Changvar _ Polyval = (*polyval)(nil) 71*e7b1675dSTing-Kang Chang 72*e7b1675dSTing-Kang Chang// mul32 multiplies two 32 bit polynomials in GF(2^128) using Karatsuba multiplication. 73*e7b1675dSTing-Kang Changfunc mul32(a uint32, b uint32) uint64 { 74*e7b1675dSTing-Kang Chang a0 := uint64(a & u32Sel0) 75*e7b1675dSTing-Kang Chang a1 := uint64(a & u32Sel1) 76*e7b1675dSTing-Kang Chang a2 := uint64(a & u32Sel2) 77*e7b1675dSTing-Kang Chang a3 := uint64(a & u32Sel3) 78*e7b1675dSTing-Kang Chang 79*e7b1675dSTing-Kang Chang b0 := uint64(b & u32Sel0) 80*e7b1675dSTing-Kang Chang b1 := uint64(b & u32Sel1) 81*e7b1675dSTing-Kang Chang b2 := uint64(b & u32Sel2) 82*e7b1675dSTing-Kang Chang b3 := uint64(b & u32Sel3) 83*e7b1675dSTing-Kang Chang 84*e7b1675dSTing-Kang Chang c0 := (a0 * b0) ^ (a1 * b3) ^ (a2 * b2) ^ (a3 * b1) 85*e7b1675dSTing-Kang Chang c1 := (a0 * b1) ^ (a1 * b0) ^ (a2 * b3) ^ (a3 * b2) 86*e7b1675dSTing-Kang Chang c2 := (a0 * b2) ^ (a1 * b1) ^ (a2 * b0) ^ (a3 * b3) 87*e7b1675dSTing-Kang Chang c3 := (a0 * b3) ^ (a1 * b2) ^ (a2 * b1) ^ (a3 * b0) 88*e7b1675dSTing-Kang Chang 89*e7b1675dSTing-Kang Chang return (c0 & u64Sel0) | (c1 & u64Sel1) | (c2 & u64Sel2) | (c3 & u64Sel3) 90*e7b1675dSTing-Kang Chang} 91*e7b1675dSTing-Kang Chang 92*e7b1675dSTing-Kang Chang// mul64 multiplies two 64 bit polynomials in GF(2^128) using Karatsuba multiplication. 93*e7b1675dSTing-Kang Changfunc mul64(a uint64, b uint64) fieldElement { 94*e7b1675dSTing-Kang Chang a0 := uint32(a & 0xffffffff) 95*e7b1675dSTing-Kang Chang a1 := uint32(a >> 32) 96*e7b1675dSTing-Kang Chang 97*e7b1675dSTing-Kang Chang b0 := uint32(b & 0xffffffff) 98*e7b1675dSTing-Kang Chang b1 := uint32(b >> 32) 99*e7b1675dSTing-Kang Chang 100*e7b1675dSTing-Kang Chang lo := mul32(a0, b0) 101*e7b1675dSTing-Kang Chang hi := mul32(a1, b1) 102*e7b1675dSTing-Kang Chang mid := mul32(a0^a1, b0^b1) ^ lo ^ hi 103*e7b1675dSTing-Kang Chang 104*e7b1675dSTing-Kang Chang return fieldElement{lo: lo ^ (mid << 32), hi: hi ^ (mid >> 32)} 105*e7b1675dSTing-Kang Chang} 106*e7b1675dSTing-Kang Chang 107*e7b1675dSTing-Kang Chang// polyvalDot implements the dot operation defined by go/rfc/8452. 108*e7b1675dSTing-Kang Chang// dot(a, b) = a * b * x^-128. 109*e7b1675dSTing-Kang Chang// The value of the field element x^-128 is equal to x^127 + x^124 + x^121 + x^114 + 1. 110*e7b1675dSTing-Kang Chang// The result of this multiplication, dot(a, b), is another field element. 111*e7b1675dSTing-Kang Chang// The implementation here is inspired from BoringSSL's implementation of gcm_polyval_nohw(). 112*e7b1675dSTing-Kang Chang// Ref: https://boringssl.googlesource.com/boringssl/+/master/crypto/fipsmodule/modes/gcm_nohw.c 113*e7b1675dSTing-Kang Changfunc polyvalDot(a fieldElement, b fieldElement) fieldElement { 114*e7b1675dSTing-Kang Chang // Karatsuba multiplication. The product of |a| and |b| is stored in |r0| and |r1| 115*e7b1675dSTing-Kang Chang // Note there is no byte or bit reversal because we are evaluating POLYVAL. 116*e7b1675dSTing-Kang Chang r0 := mul64(a.lo, b.lo) 117*e7b1675dSTing-Kang Chang r1 := mul64(a.hi, b.hi) 118*e7b1675dSTing-Kang Chang 119*e7b1675dSTing-Kang Chang mid := mul64(a.lo^a.hi, b.lo^b.hi) 120*e7b1675dSTing-Kang Chang mid.lo ^= r0.lo ^ r1.lo 121*e7b1675dSTing-Kang Chang mid.hi ^= r0.hi ^ r1.hi 122*e7b1675dSTing-Kang Chang 123*e7b1675dSTing-Kang Chang r1.lo ^= mid.hi 124*e7b1675dSTing-Kang Chang r0.hi ^= mid.lo 125*e7b1675dSTing-Kang Chang 126*e7b1675dSTing-Kang Chang // Now we multiply our 256-bit result by x^-128 and reduce. 127*e7b1675dSTing-Kang Chang // |r1| shifts into position and we must multiply |r0| by x^-128. We have: 128*e7b1675dSTing-Kang Chang // 129*e7b1675dSTing-Kang Chang // 1 = x^121 + x^126 + x^127 + x^128 130*e7b1675dSTing-Kang Chang // x^-128 = x^-7 + x^-2 + x^-1 + 1 131*e7b1675dSTing-Kang Chang // 132*e7b1675dSTing-Kang Chang // This is the GHASH reduction step, but with bits flowing in reverse. 133*e7b1675dSTing-Kang Chang // The x^-7, x^-2, and x^-1 terms shift bits past x^0, which would require 134*e7b1675dSTing-Kang Chang // another reduction steps. Instead, we gather the excess bits, incorporate 135*e7b1675dSTing-Kang Chang // them into |r0| and reduce once. 136*e7b1675dSTing-Kang Chang // Ref: slides 17-19 of https://crypto.stanford.edu/RealWorldCrypto/slides/gueron.pdf. 137*e7b1675dSTing-Kang Chang r0.hi ^= (r0.lo << 63) ^ (r0.lo << 62) ^ (r0.lo << 57) 138*e7b1675dSTing-Kang Chang 139*e7b1675dSTing-Kang Chang // 1 140*e7b1675dSTing-Kang Chang r1.lo ^= r0.lo 141*e7b1675dSTing-Kang Chang r1.hi ^= r0.hi 142*e7b1675dSTing-Kang Chang 143*e7b1675dSTing-Kang Chang // x^-1 144*e7b1675dSTing-Kang Chang r1.lo ^= r0.lo >> 1 145*e7b1675dSTing-Kang Chang r1.lo ^= r0.hi << 63 146*e7b1675dSTing-Kang Chang r1.hi ^= r0.hi >> 1 147*e7b1675dSTing-Kang Chang 148*e7b1675dSTing-Kang Chang // x^-2 149*e7b1675dSTing-Kang Chang r1.lo ^= r0.lo >> 2 150*e7b1675dSTing-Kang Chang r1.lo ^= r0.hi << 62 151*e7b1675dSTing-Kang Chang r1.hi ^= r0.hi >> 2 152*e7b1675dSTing-Kang Chang 153*e7b1675dSTing-Kang Chang // x^-7 154*e7b1675dSTing-Kang Chang r1.lo ^= r0.lo >> 7 155*e7b1675dSTing-Kang Chang r1.lo ^= r0.hi << 57 156*e7b1675dSTing-Kang Chang r1.hi ^= r0.hi >> 7 157*e7b1675dSTing-Kang Chang 158*e7b1675dSTing-Kang Chang return r1 159*e7b1675dSTing-Kang Chang} 160*e7b1675dSTing-Kang Chang 161*e7b1675dSTing-Kang Chang// NewPolyval returns a Polyval instance. 162*e7b1675dSTing-Kang Changfunc NewPolyval(key []byte) (Polyval, error) { 163*e7b1675dSTing-Kang Chang if len(key) != PolyvalBlockSize { 164*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("polyval: Invalid key size: %d", len(key)) 165*e7b1675dSTing-Kang Chang } 166*e7b1675dSTing-Kang Chang 167*e7b1675dSTing-Kang Chang return &polyval{ 168*e7b1675dSTing-Kang Chang key: fieldElement{ 169*e7b1675dSTing-Kang Chang lo: binary.LittleEndian.Uint64(key[:8]), 170*e7b1675dSTing-Kang Chang hi: binary.LittleEndian.Uint64(key[8:]), 171*e7b1675dSTing-Kang Chang }, 172*e7b1675dSTing-Kang Chang }, nil 173*e7b1675dSTing-Kang Chang} 174*e7b1675dSTing-Kang Chang 175*e7b1675dSTing-Kang Changfunc (p *polyval) Update(data []byte) { 176*e7b1675dSTing-Kang Chang var block []byte 177*e7b1675dSTing-Kang Chang for len(data) > 0 { 178*e7b1675dSTing-Kang Chang if len(data) >= PolyvalBlockSize { 179*e7b1675dSTing-Kang Chang block = data[:PolyvalBlockSize] 180*e7b1675dSTing-Kang Chang data = data[PolyvalBlockSize:] 181*e7b1675dSTing-Kang Chang } else { 182*e7b1675dSTing-Kang Chang var partialBlock [PolyvalBlockSize]byte 183*e7b1675dSTing-Kang Chang copy(partialBlock[:], data) 184*e7b1675dSTing-Kang Chang block = partialBlock[:] 185*e7b1675dSTing-Kang Chang data = data[len(data):] 186*e7b1675dSTing-Kang Chang } 187*e7b1675dSTing-Kang Chang 188*e7b1675dSTing-Kang Chang p.acc.lo ^= binary.LittleEndian.Uint64(block[:8]) 189*e7b1675dSTing-Kang Chang p.acc.hi ^= binary.LittleEndian.Uint64(block[8:]) 190*e7b1675dSTing-Kang Chang p.acc = polyvalDot(p.acc, p.key) 191*e7b1675dSTing-Kang Chang } 192*e7b1675dSTing-Kang Chang} 193*e7b1675dSTing-Kang Chang 194*e7b1675dSTing-Kang Changfunc (p *polyval) Finish() (hash [PolyvalBlockSize]byte) { 195*e7b1675dSTing-Kang Chang binary.LittleEndian.PutUint64(hash[:8], p.acc.lo) 196*e7b1675dSTing-Kang Chang binary.LittleEndian.PutUint64(hash[8:], p.acc.hi) 197*e7b1675dSTing-Kang Chang return 198*e7b1675dSTing-Kang Chang} 199