/* Copyright (c) 2023, Google Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ package kyber // This code is ported from kyber.c. import ( "crypto/subtle" "golang.org/x/crypto/sha3" "io" ) const( CiphertextSize = 1088 PublicKeySize = 1184 PrivateKeySize = 2400 ) const ( degree = 256 rank = 3 prime = 3329 log2Prime = 12 halfPrime = (prime - 1) / 2 du = 10 dv = 4 inverseDegree = 3303 encodedVectorSize = log2Prime * degree / 8 * rank compressedVectorSize = du * rank * degree / 8 barrettMultiplier = 5039 barrettShift = 24 ) func reduceOnce(x uint16) uint16 { if x >= 2*prime { panic("reduce_once: value out of range") } subtracted := x - prime mask := 0 - (subtracted >> 15) return (mask & x) | (^mask & subtracted) } func reduce(x uint32) uint16 { if x >= prime+2*prime*prime { panic("reduce: value out of range") } product := uint64(x) * barrettMultiplier quotient := uint32(product >> barrettShift) remainder := uint32(x) - quotient*prime return reduceOnce(uint16(remainder)) } // lt returns 0xff..f if a < b and 0 otherwise func lt(a, b uint32) uint32 { return uint32(0 - int32(a^((a^b)|((a-b)^a)))>>31) } // Compresses (lossily) an input |x| mod 3329 into |bits| many bits by grouping // numbers close to each other together. The formula used is // round(2^|bits|/prime*x) mod 2^|bits|. // Uses Barrett reduction to achieve constant time. Since we need both the // remainder (for rounding) and the quotient (as the result), we cannot use // |reduce| here, but need to do the Barrett reduction directly. func compress(x uint16, bits int) uint16 { product := uint32(x) << bits quotient := uint32((uint64(product) * barrettMultiplier) >> barrettShift) remainder := product - quotient*prime // Adjust the quotient to round correctly: // 0 <= remainder <= halfPrime round to 0 // halfPrime < remainder <= prime + halfPrime round to 1 // prime + halfPrime < remainder < 2 * prime round to 2 quotient += 1 & lt(halfPrime, remainder) quotient += 1 & lt(prime+halfPrime, remainder) return uint16(quotient) & ((1 << bits) - 1) } func decompress(x uint16, bits int) uint16 { product := uint32(x) * prime power := uint32(1) << bits // This is |product| % power, since |power| is a power of 2. remainder := product & (power - 1) // This is |product| / power, since |power| is a power of 2. lower := product >> bits // The rounding logic works since the first half of numbers mod |power| have a // 0 as first bit, and the second half has a 1 as first bit, since |power| is // a power of 2. As a 12 bit number, |remainder| is always positive, so we // will shift in 0s for a right shift. return uint16(lower + (remainder >> (bits - 1))) } type scalar [degree]uint16 func (s *scalar) zero() { for i := range s { s[i] = 0 } } // This bit of Python will be referenced in some of the following comments: // // p = 3329 // // def bitreverse(i): // ret = 0 // for n in range(7): // bit = i & 1 // ret <<= 1 // ret |= bit // i >>= 1 // return ret // kNTTRoots = [pow(17, bitreverse(i), p) for i in range(128)] var nttRoots = [128]uint16{ 1, 1729, 2580, 3289, 2642, 630, 1897, 848, 1062, 1919, 193, 797, 2786, 3260, 569, 1746, 296, 2447, 1339, 1476, 3046, 56, 2240, 1333, 1426, 2094, 535, 2882, 2393, 2879, 1974, 821, 289, 331, 3253, 1756, 1197, 2304, 2277, 2055, 650, 1977, 2513, 632, 2865, 33, 1320, 1915, 2319, 1435, 807, 452, 1438, 2868, 1534, 2402, 2647, 2617, 1481, 648, 2474, 3110, 1227, 910, 17, 2761, 583, 2649, 1637, 723, 2288, 1100, 1409, 2662, 3281, 233, 756, 2156, 3015, 3050, 1703, 1651, 2789, 1789, 1847, 952, 1461, 2687, 939, 2308, 2437, 2388, 733, 2337, 268, 641, 1584, 2298, 2037, 3220, 375, 2549, 2090, 1645, 1063, 319, 2773, 757, 2099, 561, 2466, 2594, 2804, 1092, 403, 1026, 1143, 2150, 2775, 886, 1722, 1212, 1874, 1029, 2110, 2935, 885, 2154, } func (s *scalar) ntt() { offset := degree for step := 1; step < degree/2; step <<= 1 { offset >>= 1 k := 0 for i := 0; i < step; i++ { stepRoot := uint32(nttRoots[i+step]) for j := k; j < k+offset; j++ { odd := reduce(stepRoot * uint32(s[j+offset])) even := s[j] s[j] = reduceOnce(odd + even) s[j+offset] = reduceOnce(even - odd + prime) } k += 2 * offset } } } // kInverseNTTRoots = [pow(17, -bitreverse(i), p) for i in range(128)] var inverseNTTRoots = [128]uint16{ 1, 1600, 40, 749, 2481, 1432, 2699, 687, 1583, 2760, 69, 543, 2532, 3136, 1410, 2267, 2508, 1355, 450, 936, 447, 2794, 1235, 1903, 1996, 1089, 3273, 283, 1853, 1990, 882, 3033, 2419, 2102, 219, 855, 2681, 1848, 712, 682, 927, 1795, 461, 1891, 2877, 2522, 1894, 1010, 1414, 2009, 3296, 464, 2697, 816, 1352, 2679, 1274, 1052, 1025, 2132, 1573, 76, 2998, 3040, 1175, 2444, 394, 1219, 2300, 1455, 2117, 1607, 2443, 554, 1179, 2186, 2303, 2926, 2237, 525, 735, 863, 2768, 1230, 2572, 556, 3010, 2266, 1684, 1239, 780, 2954, 109, 1292, 1031, 1745, 2688, 3061, 992, 2596, 941, 892, 1021, 2390, 642, 1868, 2377, 1482, 1540, 540, 1678, 1626, 279, 314, 1173, 2573, 3096, 48, 667, 1920, 2229, 1041, 2606, 1692, 680, 2746, 568, 3312, } func (s *scalar) inverseNTT() { step := degree / 2 for offset := 2; offset < degree; offset <<= 1 { step >>= 1 k := 0 for i := 0; i < step; i++ { stepRoot := uint32(inverseNTTRoots[i+step]) for j := k; j < k+offset; j++ { odd := s[j+offset] even := s[j] s[j] = reduceOnce(odd + even) s[j+offset] = reduce(stepRoot * uint32(even-odd+prime)) } k += 2 * offset } } for i := range s { s[i] = reduce(uint32(s[i]) * inverseDegree) } } func (s *scalar) add(b *scalar) { for i := range s { s[i] = reduceOnce(s[i] + b[i]) } } func (s *scalar) sub(b *scalar) { for i := range s { s[i] = reduceOnce(s[i] - b[i] + prime) } } // kModRoots = [pow(17, 2*bitreverse(i) + 1, p) for i in range(128)] var modRoots = [128]uint16{ 17, 3312, 2761, 568, 583, 2746, 2649, 680, 1637, 1692, 723, 2606, 2288, 1041, 1100, 2229, 1409, 1920, 2662, 667, 3281, 48, 233, 3096, 756, 2573, 2156, 1173, 3015, 314, 3050, 279, 1703, 1626, 1651, 1678, 2789, 540, 1789, 1540, 1847, 1482, 952, 2377, 1461, 1868, 2687, 642, 939, 2390, 2308, 1021, 2437, 892, 2388, 941, 733, 2596, 2337, 992, 268, 3061, 641, 2688, 1584, 1745, 2298, 1031, 2037, 1292, 3220, 109, 375, 2954, 2549, 780, 2090, 1239, 1645, 1684, 1063, 2266, 319, 3010, 2773, 556, 757, 2572, 2099, 1230, 561, 2768, 2466, 863, 2594, 735, 2804, 525, 1092, 2237, 403, 2926, 1026, 2303, 1143, 2186, 2150, 1179, 2775, 554, 886, 2443, 1722, 1607, 1212, 2117, 1874, 1455, 1029, 2300, 2110, 1219, 2935, 394, 885, 2444, 2154, 1175, } func (s *scalar) mult(a, b *scalar) { for i := 0; i < degree/2; i++ { realReal := uint32(a[2*i]) * uint32(b[2*i]) imgImg := uint32(a[2*i+1]) * uint32(b[2*i+1]) realImg := uint32(a[2*i]) * uint32(b[2*i+1]) imgReal := uint32(a[2*i+1]) * uint32(b[2*i]) s[2*i] = reduce(realReal + uint32(reduce(imgImg))*uint32(modRoots[i])) s[2*i+1] = reduce(imgReal + realImg) } } func (s *scalar) innerProduct(left, right *vector) { s.zero() var product scalar for i := range left { product.mult(&left[i], &right[i]) s.add(&product) } } func (s *scalar) fromKeccakVartime(keccak io.Reader) { var buf [3]byte for i := 0; i < len(s); { keccak.Read(buf[:]) d1 := uint16(buf[0]) + 256*uint16(buf[1]%16) d2 := uint16(buf[1])/16 + 16*uint16(buf[2]) if d1 < prime { s[i] = d1 i++ } if d2 < prime && i < len(s) { s[i] = d2 i++ } } } func (s *scalar) centeredBinomialEta2(input *[33]byte) { var entropy [128]byte sha3.ShakeSum256(entropy[:], input[:]) for i := 0; i < len(s); i += 2 { b := uint16(entropy[i/2]) value := uint16(prime) value += (b & 1) + ((b >> 1) & 1) value -= ((b >> 2) & 1) + ((b >> 3) & 1) s[i] = reduceOnce(value) b >>= 4 value = prime value += (b & 1) + ((b >> 1) & 1) value -= ((b >> 2) & 1) + ((b >> 3) & 1) s[i+1] = reduceOnce(value) } } var masks = [8]uint16{0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff} func (s *scalar) encode(out []byte, bits int) []byte { var outByte byte outByteBits := 0 for i := range s { element := s[i] elementBitsDone := 0 for elementBitsDone < bits { chunkBits := bits - elementBitsDone outBitsRemaining := 8 - outByteBits if chunkBits >= outBitsRemaining { chunkBits = outBitsRemaining outByte |= byte(element&masks[chunkBits-1]) << outByteBits out[0] = outByte out = out[1:] outByteBits = 0 outByte = 0 } else { outByte |= byte(element&masks[chunkBits-1]) << outByteBits outByteBits += chunkBits } elementBitsDone += chunkBits element >>= chunkBits } } if outByteBits > 0 { out[0] = outByte out = out[1:] } return out } func (s *scalar) decode(in []byte, bits int) ([]byte, bool) { var inByte byte inByteBitsLeft := 0 for i := range s { var element uint16 elementBitsDone := 0 for elementBitsDone < bits { if inByteBitsLeft == 0 { inByte = in[0] in = in[1:] inByteBitsLeft = 8 } chunkBits := bits - elementBitsDone if chunkBits > inByteBitsLeft { chunkBits = inByteBitsLeft } element |= (uint16(inByte) & masks[chunkBits-1]) << elementBitsDone inByteBitsLeft -= chunkBits inByte >>= chunkBits elementBitsDone += chunkBits } if element >= prime { return nil, false } s[i] = element } return in, true } func (s *scalar) compress(bits int) { for i := range s { s[i] = compress(s[i], bits) } } func (s *scalar) decompress(bits int) { for i := range s { s[i] = decompress(s[i], bits) } } type vector [rank]scalar func (v *vector) zero() { for i := range v { v[i].zero() } } func (v *vector) ntt() { for i := range v { v[i].ntt() } } func (v *vector) inverseNTT() { for i := range v { v[i].inverseNTT() } } func (v *vector) add(b *vector) { for i := range v { v[i].add(&b[i]) } } func (out *vector) mult(m *matrix, v *vector) { out.zero() var product scalar for i := 0; i < rank; i++ { for j := 0; j < rank; j++ { product.mult(&m[i][j], &v[j]) out[i].add(&product) } } } func (out *vector) multTranspose(m *matrix, v *vector) { out.zero() var product scalar for i := 0; i < rank; i++ { for j := 0; j < rank; j++ { product.mult(&m[j][i], &v[j]) out[i].add(&product) } } } func (v *vector) generateSecretEta2(counter *byte, seed *[32]byte) { var input [33]byte copy(input[:], seed[:]) for i := range v { input[32] = *counter *counter++ v[i].centeredBinomialEta2(&input) } } func (v *vector) encode(out []byte, bits int) []byte { for i := range v { out = v[i].encode(out, bits) } return out } func (v *vector) decode(out []byte, bits int) ([]byte, bool) { var ok bool for i := range v { out, ok = v[i].decode(out, bits) if !ok { return nil, false } } return out, true } func (v *vector) compress(bits int) { for i := range v { v[i].compress(bits) } } func (v *vector) decompress(bits int) { for i := range v { v[i].decompress(bits) } } type matrix [rank][rank]scalar func (m *matrix) expand(rho *[32]byte) { shake := sha3.NewShake128() var input [34]byte copy(input[:], rho[:]) for i := 0; i < rank; i++ { for j := 0; j < rank; j++ { input[32] = byte(i) input[33] = byte(j) shake.Reset() shake.Write(input[:]) m[i][j].fromKeccakVartime(shake) } } } type PublicKey struct { t vector rho [32]byte publicKeyHash [32]byte m matrix } func UnmarshalPublicKey(data *[PublicKeySize]byte) (*PublicKey, bool) { var ret PublicKey ret.publicKeyHash = sha3.Sum256(data[:]) in, ok := ret.t.decode(data[:], log2Prime) if !ok { return nil, false } copy(ret.rho[:], in) ret.m.expand(&ret.rho) return &ret, true } func (pub *PublicKey) Marshal() *[PublicKeySize]byte { var ret [PublicKeySize]byte out := pub.t.encode(ret[:], log2Prime) copy(out, pub.rho[:]) return &ret } func (pub *PublicKey) encryptCPA(message, entropy *[32]byte) *[CiphertextSize]byte { var counter uint8 var secret, error vector secret.generateSecretEta2(&counter, entropy) error.generateSecretEta2(&counter, entropy) secret.ntt() var input [33]byte copy(input[:], entropy[:]) input[32] = counter var scalarError scalar scalarError.centeredBinomialEta2(&input) var u vector u.mult(&pub.m, &secret) u.inverseNTT() u.add(&error) var v scalar v.innerProduct(&pub.t, &secret) v.inverseNTT() v.add(&scalarError) out := make([]byte, CiphertextSize) var expandedMessage scalar expandedMessage.decode(message[:], 1) expandedMessage.decompress(1) v.add(&expandedMessage) u.compress(du) it := u.encode(out, du) v.compress(dv) v.encode(it, dv) return (*[CiphertextSize]byte)(out) } func (pub *PublicKey) Encap(outSharedSecret []byte, entropy *[32]byte) *[CiphertextSize]byte { var input [64]byte copy(input[:], entropy[:]) copy(input[32:], pub.publicKeyHash[:]) prekeyAndRandomness := sha3.Sum512(input[:]) ciphertext := pub.encryptCPA(entropy, (*[32]byte)(prekeyAndRandomness[32:])) ciphertextHash := sha3.Sum256(ciphertext[:]) copy(prekeyAndRandomness[32:], ciphertextHash[:]) sha3.ShakeSum256(outSharedSecret, prekeyAndRandomness[:]) return ciphertext } type PrivateKey struct { PublicKey s vector foFailureSecret [32]byte } func NewPrivateKey(entropy *[64]byte) (*PrivateKey, *[PublicKeySize]byte) { hashed := sha3.Sum512(entropy[:32]) rho := (*[32]byte)(hashed[:32]) sigma := (*[32]byte)(hashed[32:]) ret := new(PrivateKey) copy(ret.foFailureSecret[:], entropy[32:]) copy(ret.rho[:], rho[:]) ret.m.expand(rho) counter := uint8(0) ret.s.generateSecretEta2(&counter, sigma) ret.s.ntt() var error vector error.generateSecretEta2(&counter, sigma) error.ntt() ret.t.multTranspose(&ret.m, &ret.s) ret.t.add(&error) marshalledPublicKey := ret.PublicKey.Marshal() ret.publicKeyHash = sha3.Sum256(marshalledPublicKey[:]) return ret, marshalledPublicKey } func (priv *PrivateKey) decryptCPA(ciphertext *[CiphertextSize]byte) [32]byte { var u vector u.decode(ciphertext[:], du) u.decompress(du) u.ntt() var v scalar v.decode(ciphertext[compressedVectorSize:], dv) v.decompress(dv) var mask scalar mask.innerProduct(&priv.s, &u) mask.inverseNTT() v.sub(&mask) v.compress(1) var out [32]byte v.encode(out[:], 1) return out } func (priv *PrivateKey) Decap(outSharedSecret []byte, ciphertext *[CiphertextSize]byte) { decrypted := priv.decryptCPA(ciphertext) h := sha3.New512() h.Write(decrypted[:]) h.Write(priv.publicKeyHash[:]) prekeyAndRandomness := h.Sum(nil) expectedCiphertext := priv.encryptCPA(&decrypted, (*[32]byte)(prekeyAndRandomness[32:])) equal := subtle.ConstantTimeCompare(ciphertext[:], expectedCiphertext[:]) var secret [32]byte for i := range secret { secret[i] = byte(subtle.ConstantTimeSelect(equal, int(prekeyAndRandomness[i]), int(priv.foFailureSecret[i]))) } ciphertextHash := sha3.Sum256(ciphertext[:]) shake := sha3.NewShake256() shake.Write(secret[:]) shake.Write(ciphertextHash[:]) shake.Read(outSharedSecret) } func (priv *PrivateKey) Marshal() *[PrivateKeySize]byte { var ret [PrivateKeySize]byte out := priv.s.encode(ret[:], log2Prime) publicKey := priv.PublicKey.Marshal() n := copy(out, publicKey[:]) out = out[n:] n = copy(out, priv.publicKeyHash[:]) out = out[n:] copy(out, priv.foFailureSecret[:]) return &ret }