diff --git a/byteops.go b/byteops.go index ae4e28b..cfa5373 100644 --- a/byteops.go +++ b/byteops.go @@ -3,6 +3,7 @@ package kyberk2so +// byteopsLoad32 returns a 32-bit unsigned integer loaded from byte x. func byteopsLoad32(x []byte) uint32 { var r uint32 r = uint32(x[0]) @@ -12,6 +13,9 @@ func byteopsLoad32(x []byte) uint32 { return r } +// byteopsCbd computers a polynomial with coefficients distributed +// according to a centered binomial distribution with parameter paramsETA, +// given an array of uniformly random bytes. func byteopsCbd(buf []byte) poly { var t, d uint32 var a, b int16 @@ -22,13 +26,15 @@ func byteopsCbd(buf []byte) poly { d = d + ((t >> 1) & 0x55555555) for j := 0; j < 8; j++ { a = int16((d >> (4*j + 0)) & 0x3) - b = int16((d >> (4*j + 2)) & 0x3) + b = int16((d >> (4*j + paramsETA)) & 0x3) r[8*i+j] = a - b } } return r } +// byteopsMontgomeryReduce computes a Montgomery reduction; given +// a 32-bit integer `a`, returns `a * R^-1 mod Q` where `R=2^16`. func byteopsMontgomeryReduce(a int32) int16 { u := int16(a * int32(paramsQinv)) t := int32(u) * int32(paramsQ) @@ -37,6 +43,9 @@ func byteopsMontgomeryReduce(a int32) int16 { return int16(t) } +// byteopsBarrettReduce computes a Barrett reduction; given +// a 16-bit integer `a`, returns a 16-bit integer congruent to +// `a mod Q` in {0,...,Q}. func byteopsBarrettReduce(a int16) int16 { var t int16 var v int16 = int16(((uint32(1) << 26) + uint32(paramsQ/2)) / uint32(paramsQ)) @@ -45,6 +54,7 @@ func byteopsBarrettReduce(a int16) int16 { return a - t } +// byteopsCSubQ conditionally subtracts Q from a. func byteopsCSubQ(a int16) int16 { a = a - int16(paramsQ) a = a + ((a >> 15) & int16(paramsQ)) diff --git a/indcpa.go b/indcpa.go index 5f173e9..4034db3 100644 --- a/indcpa.go +++ b/indcpa.go @@ -9,56 +9,72 @@ import ( "golang.org/x/crypto/sha3" ) +// indcpaPackPublicKey serializes the public key as a concatenation of the +// serialized vector of polynomials of the public key, and the public seed +// used to generate the matrix `A`. func indcpaPackPublicKey(publicKey polyvec, seed []byte, paramsK int) []byte { return append(polyvecToBytes(publicKey, paramsK), seed...) } +// indcpaUnpackPublicKey de-serializes the public key from a byte array +// and represents the approximate inverse of indcpaPackPublicKey. func indcpaUnpackPublicKey(packedPublicKey []byte, paramsK int) (polyvec, []byte) { switch paramsK { case 2: - publicKeyPolyvec := polyvecFromBytes(packedPublicKey[:paramsPolyvecBytesK2], paramsK) - seed := packedPublicKey[paramsPolyvecBytesK2:] + publicKeyPolyvec := polyvecFromBytes(packedPublicKey[:paramsPolyvecBytesK512], paramsK) + seed := packedPublicKey[paramsPolyvecBytesK512:] return publicKeyPolyvec, seed case 3: - publicKeyPolyvec := polyvecFromBytes(packedPublicKey[:paramsPolyvecBytesK3], paramsK) - seed := packedPublicKey[paramsPolyvecBytesK3:] + publicKeyPolyvec := polyvecFromBytes(packedPublicKey[:paramsPolyvecBytesK768], paramsK) + seed := packedPublicKey[paramsPolyvecBytesK768:] return publicKeyPolyvec, seed default: - publicKeyPolyvec := polyvecFromBytes(packedPublicKey[:paramsPolyvecBytesK4], paramsK) - seed := packedPublicKey[paramsPolyvecBytesK4:] + publicKeyPolyvec := polyvecFromBytes(packedPublicKey[:paramsPolyvecBytesK1024], paramsK) + seed := packedPublicKey[paramsPolyvecBytesK1024:] return publicKeyPolyvec, seed } } +// indcpaPackPrivateKey serializes the private key. func indcpaPackPrivateKey(privateKey polyvec, paramsK int) []byte { return polyvecToBytes(privateKey, paramsK) } +// indcpaUnpackPrivateKey de-serializes the private key and represents +// the inverse of indcpaPackPrivateKey. func indcpaUnpackPrivateKey(packedPrivateKey []byte, paramsK int) polyvec { return polyvecFromBytes(packedPrivateKey, paramsK) } +// indcpaPackCiphertext serializes the ciphertext as a concatenation of +// the compressed and serialized vector of polynomials `b` and the +// compressed and serialized polynomial `v`. func indcpaPackCiphertext(b polyvec, v poly, paramsK int) []byte { return append(polyvecCompress(b, paramsK), polyCompress(v, paramsK)...) } +// indcpaUnpackCiphertext de-serializes and decompresses the ciphertext +// from a byte array, and represents the approximate inverse of +// indcpaPackCiphertext. func indcpaUnpackCiphertext(c []byte, paramsK int) (polyvec, poly) { switch paramsK { case 2: - b := polyvecDecompress(c[:paramsPolyvecCompressedBytesK2], paramsK) - v := polyDecompress(c[paramsPolyvecCompressedBytesK2:], paramsK) + b := polyvecDecompress(c[:paramsPolyvecCompressedBytesK512], paramsK) + v := polyDecompress(c[paramsPolyvecCompressedBytesK512:], paramsK) return b, v case 3: - b := polyvecDecompress(c[:paramsPolyvecCompressedBytesK3], paramsK) - v := polyDecompress(c[paramsPolyvecCompressedBytesK3:], paramsK) + b := polyvecDecompress(c[:paramsPolyvecCompressedBytesK768], paramsK) + v := polyDecompress(c[paramsPolyvecCompressedBytesK768:], paramsK) return b, v default: - b := polyvecDecompress(c[:paramsPolyvecCompressedBytesK4], paramsK) - v := polyDecompress(c[paramsPolyvecCompressedBytesK4:], paramsK) + b := polyvecDecompress(c[:paramsPolyvecCompressedBytesK1024], paramsK) + v := polyDecompress(c[paramsPolyvecCompressedBytesK1024:], paramsK) return b, v } } +// indcpaRejUniform runs rejection sampling on uniform random bytes +// to generate uniform random integers modulo `Q`. func indcpaRejUniform(buf []byte, bufl int) (poly, int) { var r poly var val uint16 @@ -76,6 +92,9 @@ func indcpaRejUniform(buf []byte, bufl int) (poly, int) { return r, ctr } +// indcpaGenMatrix deterministically generates a matrix `A` (or the transpose of `A`) +// from a seed. Entries of the matrix are polynomials that look uniformly random. +// Performs rejection sampling on the output of an extendable-output function (XOF). func indcpaGenMatrix(seed []byte, transposed bool, paramsK int) ([]polyvec, error) { r := make([]polyvec, paramsK) buf := make([]byte, 4*168) @@ -115,12 +134,17 @@ func indcpaGenMatrix(seed []byte, transposed bool, paramsK int) ([]polyvec, erro return r, nil } +// indcpaPrf provides a pseudo-random function (PRF) which returns +// a byte array of length `l`, using the provided key and nonce +// to instantiate the PRF's underlying hash function. func indcpaPrf(l int, key []byte, nonce byte) []byte { hash := make([]byte, l) sha3.ShakeSum256(hash, append(key, nonce)) return hash } +// indcpaKeypair generates public and private keys for the CPA-secure +// public-key encryption scheme underlying Kyber. func indcpaKeypair(paramsK int) ([]byte, []byte, error) { skpv := polyvecNew(paramsK) pkpv := polyvecNew(paramsK) @@ -161,6 +185,8 @@ func indcpaKeypair(paramsK int) ([]byte, []byte, error) { return indcpaPackPrivateKey(skpv, paramsK), indcpaPackPublicKey(pkpv, publicSeed, paramsK), nil } +// indcpaEncrypt is the encryption function of the CPA-secure +// public-key encryption scheme underlying Kyber. func indcpaEncrypt(m []byte, publicKey []byte, coins []byte, paramsK int) ([]byte, error) { sp := polyvecNew(paramsK) ep := polyvecNew(paramsK) @@ -189,6 +215,8 @@ func indcpaEncrypt(m []byte, publicKey []byte, coins []byte, paramsK int) ([]byt return indcpaPackCiphertext(bp, polyReduce(v), paramsK), nil } +// indcpaDecrypt is the decryption function of the CPA-secure +// public-key encryption scheme underlying Kyber. func indcpaDecrypt(c []byte, privateKey []byte, paramsK int) []byte { bp, v := indcpaUnpackCiphertext(c, paramsK) privateKeyPolyvec := indcpaUnpackPrivateKey(privateKey, paramsK) diff --git a/kem.go b/kem.go index 3697de3..e703515 100644 --- a/kem.go +++ b/kem.go @@ -183,9 +183,9 @@ func KemDecrypt512( const paramsK int = 2 var sharedSecretFixedLength [KyberSSBytes]byte sharedSecret := make([]byte, KyberSSBytes) - indcpaPrivateKey := privateKey[:paramsIndcpaSecretKeyBytesK2] - pki := paramsIndcpaSecretKeyBytesK2 + paramsIndcpaPublicKeyBytesK2 - publicKey := privateKey[paramsIndcpaSecretKeyBytesK2:pki] + indcpaPrivateKey := privateKey[:paramsIndcpaSecretKeyBytesK512] + pki := paramsIndcpaSecretKeyBytesK512 + paramsIndcpaPublicKeyBytesK512 + publicKey := privateKey[paramsIndcpaSecretKeyBytesK512:pki] buf := indcpaDecrypt(ciphertext[:], indcpaPrivateKey, paramsK) ski := Kyber512SKBytes - 2*paramsSymBytes kr := sha3.Sum512(append(buf, privateKey[ski:ski+paramsSymBytes]...)) @@ -212,9 +212,9 @@ func KemDecrypt768( const paramsK int = 3 var sharedSecretFixedLength [KyberSSBytes]byte sharedSecret := make([]byte, KyberSSBytes) - indcpaPrivateKey := privateKey[:paramsIndcpaSecretKeyBytesK3] - pki := paramsIndcpaSecretKeyBytesK3 + paramsIndcpaPublicKeyBytesK3 - publicKey := privateKey[paramsIndcpaSecretKeyBytesK3:pki] + indcpaPrivateKey := privateKey[:paramsIndcpaSecretKeyBytesK768] + pki := paramsIndcpaSecretKeyBytesK768 + paramsIndcpaPublicKeyBytesK768 + publicKey := privateKey[paramsIndcpaSecretKeyBytesK768:pki] buf := indcpaDecrypt(ciphertext[:], indcpaPrivateKey, paramsK) ski := Kyber768SKBytes - 2*paramsSymBytes kr := sha3.Sum512(append(buf, privateKey[ski:ski+paramsSymBytes]...)) @@ -241,9 +241,9 @@ func KemDecrypt1024( const paramsK int = 4 var sharedSecretFixedLength [KyberSSBytes]byte sharedSecret := make([]byte, KyberSSBytes) - indcpaPrivateKey := privateKey[:paramsIndcpaSecretKeyBytesK4] - pki := paramsIndcpaSecretKeyBytesK4 + paramsIndcpaPublicKeyBytesK4 - publicKey := privateKey[paramsIndcpaSecretKeyBytesK4:pki] + indcpaPrivateKey := privateKey[:paramsIndcpaSecretKeyBytesK1024] + pki := paramsIndcpaSecretKeyBytesK1024 + paramsIndcpaPublicKeyBytesK1024 + publicKey := privateKey[paramsIndcpaSecretKeyBytesK1024:pki] buf := indcpaDecrypt(ciphertext[:], indcpaPrivateKey, paramsK) ski := Kyber1024SKBytes - 2*paramsSymBytes kr := sha3.Sum512(append(buf, privateKey[ski:ski+paramsSymBytes]...)) diff --git a/ntt.go b/ntt.go index 4050d32..a145ff4 100644 --- a/ntt.go +++ b/ntt.go @@ -29,10 +29,14 @@ var nttZetasInv [128]int16 = [128]int16{ 3127, 3042, 1907, 1836, 1517, 359, 758, 1441, } +// nttFqMul performs multiplication followed by Montgomery reduction +// and returns a 16-bit integer congruent to `a*b*R^{-1} mod Q`. func nttFqMul(a int16, b int16) int16 { return byteopsMontgomeryReduce(int32(a) * int32(b)) } +// ntt performs an inplace number-theoretic transform (NTT) in `Rq`. +// The input is in standard order, the output is in bit-reversed order. func ntt(r poly) poly { j := 0 k := 1 @@ -50,6 +54,9 @@ func ntt(r poly) poly { return r } +// nttInv performs an inplace inverse number-theoretic transform (NTT) +// in `Rq` and multiplication by Montgomery factor 2^16. +// The input is in bit-reversed order, the output is in standard order. func nttInv(r poly) poly { j := 0 k := 0 @@ -71,6 +78,9 @@ func nttInv(r poly) poly { return r } +// nttBaseMul performs the multiplication of polynomials +// in `Zq[X]/(X^2-zeta)`. Used for multiplication of elements +// in `Rq` in the number-theoretic transformation domain. func nttBaseMul( a0 int16, a1 int16, b0 int16, b1 int16, diff --git a/params.go b/params.go index 084c3f7..a3aa712 100644 --- a/params.go +++ b/params.go @@ -9,48 +9,48 @@ const paramsQinv int = 62209 const paramsETA int = 2 const paramsSymBytes int = 32 const paramsPolyBytes int = 384 -const paramsPolyvecBytesK2 int = 2 * paramsPolyBytes -const paramsPolyvecBytesK3 int = 3 * paramsPolyBytes -const paramsPolyvecBytesK4 int = 4 * paramsPolyBytes -const paramsPolyCompressedBytesK2 int = 96 -const paramsPolyCompressedBytesK3 int = 128 -const paramsPolyCompressedBytesK4 int = 160 -const paramsPolyvecCompressedBytesK2 int = 2 * 320 -const paramsPolyvecCompressedBytesK3 int = 3 * 320 -const paramsPolyvecCompressedBytesK4 int = 4 * 352 -const paramsIndcpaPublicKeyBytesK2 int = paramsPolyvecBytesK2 + paramsSymBytes -const paramsIndcpaPublicKeyBytesK3 int = paramsPolyvecBytesK3 + paramsSymBytes -const paramsIndcpaPublicKeyBytesK4 int = paramsPolyvecBytesK4 + paramsSymBytes -const paramsIndcpaSecretKeyBytesK2 int = 2 * paramsPolyBytes -const paramsIndcpaSecretKeyBytesK3 int = 3 * paramsPolyBytes -const paramsIndcpaSecretKeyBytesK4 int = 4 * paramsPolyBytes +const paramsPolyvecBytesK512 int = 2 * paramsPolyBytes +const paramsPolyvecBytesK768 int = 3 * paramsPolyBytes +const paramsPolyvecBytesK1024 int = 4 * paramsPolyBytes +const paramsPolyCompressedBytesK512 int = 96 +const paramsPolyCompressedBytesK768 int = 128 +const paramsPolyCompressedBytesK1024 int = 160 +const paramsPolyvecCompressedBytesK512 int = 2 * 320 +const paramsPolyvecCompressedBytesK768 int = 3 * 320 +const paramsPolyvecCompressedBytesK1024 int = 4 * 352 +const paramsIndcpaPublicKeyBytesK512 int = paramsPolyvecBytesK512 + paramsSymBytes +const paramsIndcpaPublicKeyBytesK768 int = paramsPolyvecBytesK768 + paramsSymBytes +const paramsIndcpaPublicKeyBytesK1024 int = paramsPolyvecBytesK1024 + paramsSymBytes +const paramsIndcpaSecretKeyBytesK512 int = 2 * paramsPolyBytes +const paramsIndcpaSecretKeyBytesK768 int = 3 * paramsPolyBytes +const paramsIndcpaSecretKeyBytesK1024 int = 4 * paramsPolyBytes // Kyber512SKBytes is a constant representing the byte length of private keys in Kyber-512. -const Kyber512SKBytes int = paramsPolyvecBytesK2 + ((paramsPolyvecBytesK2 + paramsSymBytes) + 2*paramsSymBytes) +const Kyber512SKBytes int = paramsPolyvecBytesK512 + ((paramsPolyvecBytesK512 + paramsSymBytes) + 2*paramsSymBytes) // Kyber768SKBytes is a constant representing the byte length of private keys in Kyber-768. -const Kyber768SKBytes int = paramsPolyvecBytesK3 + ((paramsPolyvecBytesK3 + paramsSymBytes) + 2*paramsSymBytes) +const Kyber768SKBytes int = paramsPolyvecBytesK768 + ((paramsPolyvecBytesK768 + paramsSymBytes) + 2*paramsSymBytes) // Kyber1024SKBytes is a constant representing the byte length of private keys in Kyber-1024. -const Kyber1024SKBytes int = paramsPolyvecBytesK4 + ((paramsPolyvecBytesK4 + paramsSymBytes) + 2*paramsSymBytes) +const Kyber1024SKBytes int = paramsPolyvecBytesK1024 + ((paramsPolyvecBytesK1024 + paramsSymBytes) + 2*paramsSymBytes) // Kyber512PKBytes is a constant representing the byte length of public keys in Kyber-512. -const Kyber512PKBytes int = paramsPolyvecBytesK2 + paramsSymBytes +const Kyber512PKBytes int = paramsPolyvecBytesK512 + paramsSymBytes // Kyber768PKBytes is a constant representing the byte length of public keys in Kyber-768. -const Kyber768PKBytes int = paramsPolyvecBytesK3 + paramsSymBytes +const Kyber768PKBytes int = paramsPolyvecBytesK768 + paramsSymBytes // Kyber1024PKBytes is a constant representing the byte length of public keys in Kyber-1024. -const Kyber1024PKBytes int = paramsPolyvecBytesK4 + paramsSymBytes +const Kyber1024PKBytes int = paramsPolyvecBytesK1024 + paramsSymBytes // Kyber512CTBytes is a constant representing the byte length of ciphertexts in Kyber-512. -const Kyber512CTBytes int = paramsPolyvecCompressedBytesK2 + paramsPolyCompressedBytesK2 +const Kyber512CTBytes int = paramsPolyvecCompressedBytesK512 + paramsPolyCompressedBytesK512 // Kyber768CTBytes is a constant representing the byte length of ciphertexts in Kyber-768. -const Kyber768CTBytes int = paramsPolyvecCompressedBytesK3 + paramsPolyCompressedBytesK3 +const Kyber768CTBytes int = paramsPolyvecCompressedBytesK768 + paramsPolyCompressedBytesK768 // Kyber1024CTBytes is a constant representing the byte length of ciphertexts in Kyber-1024. -const Kyber1024CTBytes int = paramsPolyvecCompressedBytesK4 + paramsPolyCompressedBytesK4 +const Kyber1024CTBytes int = paramsPolyvecCompressedBytesK1024 + paramsPolyCompressedBytesK1024 // KyberSSBytes is a constant representing the byte length of shared secrets in Kyber. const KyberSSBytes int = 32 diff --git a/poly.go b/poly.go index 9006bfd..0d9e8ab 100644 --- a/poly.go +++ b/poly.go @@ -4,21 +4,16 @@ package kyberk2so type poly [paramsPolyBytes]int16 - type polyvec []poly -func polyvecNew(paramsK int) polyvec { - var pv polyvec = make([]poly, paramsK) - return pv -} - +// polyCompress lossily compresses and subsequently serializes a polynomial. func polyCompress(a poly, paramsK int) []byte { t := make([]byte, 8) a = polyCSubQ(a) rr := 0 switch paramsK { case 2: - r := make([]byte, paramsPolyCompressedBytesK2) + r := make([]byte, paramsPolyCompressedBytesK512) for i := 0; i < paramsN/8; i++ { for j := 0; j < 8; j++ { t[j] = byte(((uint16(a[8*i+j])<<3)+uint16(paramsQ)/2)/uint16(paramsQ)) & 7 @@ -30,7 +25,7 @@ func polyCompress(a poly, paramsK int) []byte { } return r case 3: - r := make([]byte, paramsPolyCompressedBytesK3) + r := make([]byte, paramsPolyCompressedBytesK768) for i := 0; i < paramsN/8; i++ { for j := 0; j < 8; j++ { t[j] = byte(((uint16(a[8*i+j])<<4)+uint16(paramsQ/2))/uint16(paramsQ)) & 15 @@ -43,7 +38,7 @@ func polyCompress(a poly, paramsK int) []byte { } return r default: - r := make([]byte, paramsPolyCompressedBytesK4) + r := make([]byte, paramsPolyCompressedBytesK1024) for i := 0; i < paramsN/8; i++ { for j := 0; j < 8; j++ { t[j] = byte(((uint32(a[8*i+j])<<5)+uint32(paramsQ/2))/uint32(paramsQ)) & 31 @@ -59,6 +54,10 @@ func polyCompress(a poly, paramsK int) []byte { } } +// polyDecompress de-serializes and subsequently decompresses a polynomial, +// representing the approximate inverse of polyCompress. +// Note that compression is lossy, and thus decompression will not match the +// original input. func polyDecompress(a []byte, paramsK int) poly { var r poly t := make([]byte, 8) @@ -104,6 +103,7 @@ func polyDecompress(a []byte, paramsK int) poly { return r } +// polyToBytes serializes a polynomial into an array of bytes. func polyToBytes(a poly) []byte { var t0, t1 uint16 r := make([]byte, paramsPolyBytes) @@ -118,6 +118,8 @@ func polyToBytes(a poly) []byte { return r } +// polyFromBytes de-serialises an array of bytes into a polynomial, +// and represents the inverse of polyToBytes. func polyFromBytes(a []byte) poly { var r poly for i := 0; i < paramsN/2; i++ { @@ -127,6 +129,7 @@ func polyFromBytes(a []byte) poly { return r } +// polyFromMsg converts a 32-byte message to a polynomial. func polyFromMsg(msg []byte) poly { var r poly var mask int16 @@ -139,6 +142,8 @@ func polyFromMsg(msg []byte) poly { return r } +// polyToMsg converts a polynomial to a 32-byte message +// and represents the inverse of polyFromMsg. func polyToMsg(a poly) []byte { msg := make([]byte, paramsSymBytes) var t uint16 @@ -153,20 +158,31 @@ func polyToMsg(a poly) []byte { return msg } +// polyGetNoise samples a polynomial deterministically from a seed +// and nonce, with the output polynomial being close to a centered +// binomial distribution with parameter paramsETA = 2. func polyGetNoise(seed []byte, nonce byte) poly { l := paramsETA * paramsN / 4 p := indcpaPrf(l, seed, nonce) return byteopsCbd(p) } +// polyNtt computes a negacyclic number-theoretic transform (NTT) of +// a polynomial in-place; the input is assumed to be in normal order, +// while the output is in bit-reversed order. func polyNtt(r poly) poly { return polyReduce(ntt(r)) } +// polyInvNttToMont computes the inverse of a negacyclic number-theoretic +// transform (NTT) of a polynomial in-place; the input is assumed to be in +// bit-reversed order, while the output is in normal order. func polyInvNttToMont(r poly) poly { return nttInv(r) } +// polyBaseMulMontgomery performs the multiplication of two polynomials +// in the number-theoretic transform (NTT) domain. func polyBaseMulMontgomery(a poly, b poly) poly { for i := 0; i < paramsN/4; i++ { rx := nttBaseMul( @@ -187,6 +203,8 @@ func polyBaseMulMontgomery(a poly, b poly) poly { return a } +// polyToMont performs the in-place conversion of all coefficients +// of a polynomial from the normal domain to the Montgomery domain. func polyToMont(r poly) poly { var f int16 = int16((uint64(1) << 32) % uint64(paramsQ)) for i := 0; i < paramsN; i++ { @@ -195,6 +213,7 @@ func polyToMont(r poly) poly { return r } +// polyReduce applies Barrett reduction to all coefficients of a polynomial. func polyReduce(r poly) poly { for i := 0; i < paramsN; i++ { r[i] = byteopsBarrettReduce(r[i]) @@ -202,6 +221,8 @@ func polyReduce(r poly) poly { return r } +// polyCSubQ applies the conditional subtraction of `Q` to each coefficient +// of a polynomial. func polyCSubQ(r poly) poly { for i := 0; i < paramsN; i++ { r[i] = byteopsCSubQ(r[i]) @@ -209,6 +230,7 @@ func polyCSubQ(r poly) poly { return r } +// polyAdd adds two polynomials. func polyAdd(a poly, b poly) poly { for i := 0; i < paramsN; i++ { a[i] = a[i] + b[i] @@ -216,6 +238,7 @@ func polyAdd(a poly, b poly) poly { return a } +// polySub subtracts two polynomials. func polySub(a poly, b poly) poly { for i := 0; i < paramsN; i++ { a[i] = a[i] - b[i] @@ -223,17 +246,24 @@ func polySub(a poly, b poly) poly { return a } +// polyvecNew instantiates a new vector of polynomials. +func polyvecNew(paramsK int) polyvec { + var pv polyvec = make([]poly, paramsK) + return pv +} + +// polyvecCompress lossily compresses and serializes a vector of polynomials. func polyvecCompress(a polyvec, paramsK int) []byte { var r []byte polyvecCSubQ(a, paramsK) rr := 0 switch paramsK { case 2: - r = make([]byte, paramsPolyvecCompressedBytesK2) + r = make([]byte, paramsPolyvecCompressedBytesK512) case 3: - r = make([]byte, paramsPolyvecCompressedBytesK3) + r = make([]byte, paramsPolyvecCompressedBytesK768) case 4: - r = make([]byte, paramsPolyvecCompressedBytesK4) + r = make([]byte, paramsPolyvecCompressedBytesK1024) } switch paramsK { case 2, 3: @@ -277,6 +307,9 @@ func polyvecCompress(a polyvec, paramsK int) []byte { } } +// polyvecDecompress de-serializes and decompresses a vector of polynomials and +// represents the approximate inverse of polyvecCompress. Since compression is lossy, +// the results of decompression will may not match the original vector of polynomials. func polyvecDecompress(a []byte, paramsK int) polyvec { r := polyvecNew(paramsK) aa := 0 @@ -317,6 +350,7 @@ func polyvecDecompress(a []byte, paramsK int) polyvec { return r } +// polyvecToBytes serializes a vector of polynomials. func polyvecToBytes(a polyvec, paramsK int) []byte { r := []byte{} for i := 0; i < paramsK; i++ { @@ -325,6 +359,7 @@ func polyvecToBytes(a polyvec, paramsK int) []byte { return r } +// polyvecFromBytes deserializes a vector of polynomials. func polyvecFromBytes(a []byte, paramsK int) polyvec { r := polyvecNew(paramsK) for i := 0; i < paramsK; i++ { @@ -335,18 +370,25 @@ func polyvecFromBytes(a []byte, paramsK int) polyvec { return r } +// polyvecNtt applies forward number-theoretic transforms (NTT) +// to all elements of a vector of polynomials. func polyvecNtt(r polyvec, paramsK int) { for i := 0; i < paramsK; i++ { r[i] = polyNtt(r[i]) } } +// polyvecInvNttToMont applies the inverse number-theoretic transform (NTT) +// to all elements of a vector of polynomials and multiplies by Montgomery +// factor `2^16`. func polyvecInvNttToMont(r polyvec, paramsK int) { for i := 0; i < paramsK; i++ { r[i] = polyInvNttToMont(r[i]) } } +// polyvecPointWiseAccMontgomery pointwise-multiplies elements of polynomial-vectors +// `a` and `b`, accumulates the results into `r`, and then multiplies by `2^-16`. func polyvecPointWiseAccMontgomery(a polyvec, b polyvec, paramsK int) poly { r := polyBaseMulMontgomery(a[0], b[0]) for i := 1; i < paramsK; i++ { @@ -356,18 +398,23 @@ func polyvecPointWiseAccMontgomery(a polyvec, b polyvec, paramsK int) poly { return polyReduce(r) } +// polyvecReduce applies Barrett reduction to each coefficient of each element +// of a vector of polynomials. func polyvecReduce(r polyvec, paramsK int) { for i := 0; i < paramsK; i++ { r[i] = polyReduce(r[i]) } } +// polyvecCSubQ applies the conditional subtraction of `Q` to each coefficient +// of each element of a vector of polynomials. func polyvecCSubQ(r polyvec, paramsK int) { for i := 0; i < paramsK; i++ { r[i] = polyCSubQ(r[i]) } } +// polyvecAdd adds two vectors of polynomials. func polyvecAdd(a polyvec, b polyvec, paramsK int) { for i := 0; i < paramsK; i++ { a[i] = polyAdd(a[i], b[i])