From c2a12162acc031bbfd45fb3455b86f33bcac9b61 Mon Sep 17 00:00:00 2001 From: xvzcf Date: Tue, 15 Feb 2022 12:56:42 +0100 Subject: [PATCH 1/5] Addressing more review comments. --- README.md | 1 + kem/frodo/frodo640shake/frodo.go | 244 ++++++++---------------- kem/frodo/frodo640shake/matrix_shake.go | 23 +-- kem/frodo/frodo640shake/noise.go | 8 + kem/frodo/frodo640shake/util.go | 4 +- 5 files changed, 100 insertions(+), 180 deletions(-) diff --git a/README.md b/README.md index d6060b066..8f2fa0a42 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ go get -u github.com/cloudflare/circl - [SIDH/SIKE](https://sike.org/): Supersingular Key Encapsulation with rimes p434, p503, p751 - [CSIDH](https://csidh.isogeny.org/): Post-Quantum Commutative Group Action - [Kyber](https://pq-crystals.org/kyber/) KEM: modes 512, 768, 1024 + - [FrodoKEM](https://frodokem.org/) KEM: modes 640-SHAKE #### Post-Quantum Public-Key Encryption - [Kyber](https://pq-crystals.org/kyber/) PKE: modes 512, 768, 1024 diff --git a/kem/frodo/frodo640shake/frodo.go b/kem/frodo/frodo640shake/frodo.go index a0d4391c7..f9b438910 100644 --- a/kem/frodo/frodo640shake/frodo.go +++ b/kem/frodo/frodo640shake/frodo.go @@ -1,4 +1,5 @@ // Package frodo640shake implements the variant FrodoKEM-640 with SHAKE. +// Multi-dimensional arrays are stored in 1-dimensional arrays in row-major order. package frodo640shake import ( @@ -12,13 +13,19 @@ import ( ) const ( - paramN = 640 - paramNbar = 8 - logQ = 15 - logQMask = ((1 << logQ) - 1) - seedASize = 16 - pkHashSize = 16 - extractedBits = 2 + paramN = 640 + + // Denoted by 'mbar' in the FrodoKEM spec. + paramNbar = 8 + + logQ = 15 + logQMask = ((1 << logQ) - 1) + seedASize = 16 + pkHashSize = 16 + + // Denoted by 'B' in the FrodoKEM spec. + extractedBits = 2 + messageSize = 16 matrixBpPackedSize = (logQ * (paramN * paramNbar)) / 8 ) @@ -53,15 +60,19 @@ type PublicKey struct { type PrivateKey struct { hashInputIfDecapsFail [SharedKeySize]byte pk *PublicKey - matrixS [paramN * paramNbar]uint16 - hpk [pkHashSize]byte // H(packed(pk)) + + // matrixS stores transpose(S) + matrixS [paramN * paramNbar]uint16 + + // H(packed(pk)) + hpk [pkHashSize]byte } // NewKeyFromSeed derives a public/private keypair deterministically // from the given seed. // // Panics if seed is not of length KeySeedSize. -func newKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey, error) { +func newKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { if len(seed) != KeySeedSize { panic("seed must be of length KeySeedSize") } @@ -69,69 +80,49 @@ func newKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey, error) { var sk PrivateKey var pk PublicKey - var shakeInputForSE [1 + SharedKeySize]byte - - var SE [2 * paramN * paramNbar]uint16 - var byteSE [2 * len(SE)]byte - S := SE[0 : paramN*paramNbar] - E := SE[paramN*paramNbar : 2*paramN*paramNbar] + var E [paramN * paramNbar]uint16 + var byteSE [2 * (len(sk.matrixS) + len(E))]byte var A [paramN * paramN]uint16 // Generate the secret value s, and the seed for S, E, and A. Add seedA to the public key shake128 := sha3.NewShake128() - _, err := shake128.Write(seed[2*SharedKeySize:]) - if err != nil { - return nil, nil, err - } - _, err = shake128.Read(pk.seedA[:]) - if err != nil { - return nil, nil, err - } - - // Generate S,E, and A, and compute B = A*S + E. - shakeInputForSE[0] = 0x5F - copy(shakeInputForSE[1:], seed[SharedKeySize:2*SharedKeySize]) + _, _ = shake128.Write(seed[2*SharedKeySize:]) + _, _ = shake128.Read(pk.seedA[:]) shake128.Reset() - _, err = shake128.Write(shakeInputForSE[:]) - if err != nil { - return nil, nil, err - } - _, err = shake128.Read(byteSE[:]) - if err != nil { - return nil, nil, err - } - for i := range SE { - SE[i] = uint16(byteSE[i*2]) | (uint16(byteSE[(i*2)+1]) << 8) + _, _ = shake128.Write([]byte{0x5F}) + _, _ = shake128.Write(seed[SharedKeySize : 2*SharedKeySize]) + _, _ = shake128.Read(byteSE[:]) + + i := 0 + for i < len(sk.matrixS) { + sk.matrixS[i] = uint16(byteSE[i*2]) | (uint16(byteSE[(i*2)+1]) << 8) + i++ } - sample(SE[:]) + sample(sk.matrixS[:]) - err = expandSeedIntoA(&A, &pk.seedA, &shake128) - if err != nil { - return nil, nil, err + for j := range E { + E[j] = uint16(byteSE[i*2]) | (uint16(byteSE[(i*2)+1]) << 8) + i++ } - mulAddASPlusE(&pk.matrixB, &A, S[:], E[:]) + sample(E[:]) + + expandSeedIntoA(&A, &pk.seedA, &shake128) + mulAddASPlusE(&pk.matrixB, &A, sk.matrixS[:], E[:]) // Populate the private key copy(sk.hashInputIfDecapsFail[:], seed[0:SharedKeySize]) sk.pk = &pk - copy(sk.matrixS[:], S[:]) // Add H(pk) to the private key shake128.Reset() var ppk [PublicKeySize]byte pk.Pack(ppk[:]) - _, err = shake128.Write(ppk[:]) - if err != nil { - return nil, nil, err - } - _, err = shake128.Read(sk.hpk[:]) - if err != nil { - return nil, nil, err - } + _, _ = shake128.Write(ppk[:]) + _, _ = shake128.Read(sk.hpk[:]) - return &pk, &sk, nil + return &pk, &sk } // GenerateKeyPair generates public and private keys using entropy from rand. @@ -145,7 +136,7 @@ func generateKeyPair(rand io.Reader) (*PublicKey, *PrivateKey, error) { if err != nil { return nil, nil, err } - pk, sk, err := newKeyFromSeed(seed[:]) + pk, sk := newKeyFromSeed(seed[:]) return pk, sk, err } @@ -157,13 +148,10 @@ func generateKeyPair(rand io.Reader) (*PublicKey, *PrivateKey, error) { // and EncapsulationSeedSize respectively. // // seed may be nil, in which case crypto/rand.Reader is used to generate one. -func (pk *PublicKey) EncapsulateTo(ct []byte, ss []byte, seed []byte) error { +func (pk *PublicKey) EncapsulateTo(ct []byte, ss []byte, seed []byte) { if seed == nil { seed = make([]byte, EncapsulationSeedSize) - _, err := cryptoRand.Read(seed[:]) - if err != nil { - return err - } + _, _ = cryptoRand.Read(seed[:]) } if len(seed) != EncapsulationSeedSize { panic("seed must be of length EncapsulationSeedSize") @@ -177,8 +165,6 @@ func (pk *PublicKey) EncapsulateTo(ct []byte, ss []byte, seed []byte) error { var G2out [2 * SharedKeySize]byte - var shakeInputForSpEpEpp [1 + SharedKeySize]byte - var SpEpEpp [(paramN * paramNbar) + (paramN * paramNbar) + (paramNbar * paramNbar)]uint16 var byteSpEpEpp [2 * len(SpEpEpp)]byte Sp := SpEpEpp[:paramN*paramNbar] @@ -194,65 +180,41 @@ func (pk *PublicKey) EncapsulateTo(ct []byte, ss []byte, seed []byte) error { var hpk [pkHashSize]byte - mu := seed[:messageSize] + var mu [messageSize]byte + copy(mu[:], seed[:messageSize]) // compute hpk = G_1(packed(pk)) shake128 := sha3.NewShake128() var ppk [PublicKeySize]byte pk.Pack(ppk[:]) - _, err := shake128.Write(ppk[:]) - if err != nil { - return err - } - _, err = shake128.Read(hpk[:]) - if err != nil { - return err - } + _, _ = shake128.Write(ppk[:]) + _, _ = shake128.Read(hpk[:]) // compute (seedSE || k) = G_2(hpk || mu) shake128.Reset() - _, err = shake128.Write(hpk[:]) - if err != nil { - return err - } - _, err = shake128.Write(mu[:]) - if err != nil { - return err - } - _, err = shake128.Read(G2out[:]) - if err != nil { - return err - } + _, _ = shake128.Write(hpk[:]) + _, _ = shake128.Write(mu[:]) + _, _ = shake128.Read(G2out[:]) // Generate Sp, Ep, Epp, and A, and compute: // Bp = Sp*A + Ep // V = Sp*B + Epp - shakeInputForSpEpEpp[0] = 0x96 - copy(shakeInputForSpEpEpp[1:], G2out[:SharedKeySize]) shake128.Reset() - _, err = shake128.Write(shakeInputForSpEpEpp[:]) - if err != nil { - return err - } - _, err = shake128.Read(byteSpEpEpp[:]) - if err != nil { - return err - } + shake128.Write([]byte{0x96}) + shake128.Write(G2out[:SharedKeySize]) + _, _ = shake128.Read(byteSpEpEpp[:]) for i := range SpEpEpp { SpEpEpp[i] = uint16(byteSpEpEpp[i*2]) | (uint16(byteSpEpEpp[(i*2)+1]) << 8) } sample(SpEpEpp[:]) - err = expandSeedIntoA(&A, &pk.seedA, &shake128) - if err != nil { - return err - } + expandSeedIntoA(&A, &pk.seedA, &shake128) mulAddSAPlusE(&Bp, Sp, &A, Ep) mulAddSBPlusE(&V, Sp, &pk.matrixB, Epp) // Encode mu, and compute C = V + enc(mu) (mod q) - encodeMessage(C[:], mu[:]) + encodeMessage(&C, &mu) add(&C, &V, &C) // Prepare the ciphertext @@ -261,19 +223,9 @@ func (pk *PublicKey) EncapsulateTo(ct []byte, ss []byte, seed []byte) error { // Compute ss = F(ct||k) shake128.Reset() - _, err = shake128.Write(ct[:]) - if err != nil { - return err - } - _, err = shake128.Write(G2out[SharedKeySize:]) - if err != nil { - return err - } - _, err = shake128.Read(ss[:]) - if err != nil { - return err - } - return nil + _, _ = shake128.Write(ct[:]) + _, _ = shake128.Write(G2out[SharedKeySize:]) + _, _ = shake128.Read(ss[:]) } // DecapsulateTo computes the shared key that is encapsulated in ct @@ -281,7 +233,7 @@ func (pk *PublicKey) EncapsulateTo(ct []byte, ss []byte, seed []byte) error { // // Panics if ct or ss are not of length CiphertextSize and SharedKeySize // respectively. -func (sk *PrivateKey) DecapsulateTo(ss, ct []byte) error { +func (sk *PrivateKey) DecapsulateTo(ss, ct []byte) { if len(ct) != CiphertextSize { panic("ct must be of length CiphertextSize") } @@ -296,8 +248,6 @@ func (sk *PrivateKey) DecapsulateTo(ss, ct []byte) error { var CC [paramNbar * paramNbar]uint16 var BBp [paramN * paramNbar]uint16 - var shakeInputForSEprime [1 + SharedKeySize]byte - var SpEpEpp [(paramN * paramNbar) + (paramN * paramNbar) + (paramNbar * paramNbar)]uint16 var byteSpEpEpp [2 * len(SpEpEpp)]byte Sp := SpEpEpp[:paramN*paramNbar] @@ -317,46 +267,26 @@ func (sk *PrivateKey) DecapsulateTo(ss, ct []byte) error { mulBS(&W, &Bp, &sk.matrixS) sub(&W, &C, &W) - decodeMessage(muprime[:], W[:]) + decodeMessage(&muprime, &W) // Generate (seedSE' || k') = G_2(hpk || mu') shake128 := sha3.NewShake128() - _, err := shake128.Write(sk.hpk[:]) - if err != nil { - return err - } - _, err = shake128.Write(muprime[:]) - if err != nil { - return err - } - _, err = shake128.Read(G2out[:]) - if err != nil { - return err - } + _, _ = shake128.Write(sk.hpk[:]) + _, _ = shake128.Write(muprime[:]) + _, _ = shake128.Read(G2out[:]) // Generate Sp, Ep, Epp, A, and compute BBp = Sp*A + Ep. - shakeInputForSEprime[0] = 0x96 - copy(shakeInputForSEprime[1:], G2out[0:SharedKeySize]) - shake128.Reset() - _, err = shake128.Write(shakeInputForSEprime[:]) - if err != nil { - return err - } - _, err = shake128.Read(byteSpEpEpp[:]) - if err != nil { - return err - } + _, _ = shake128.Write([]byte{0x96}) + _, _ = shake128.Write(G2out[:SharedKeySize]) + _, _ = shake128.Read(byteSpEpEpp[:]) for i := range SpEpEpp { SpEpEpp[i] = uint16(byteSpEpEpp[i*2]) | (uint16(byteSpEpEpp[(i*2)+1]) << 8) } sample(SpEpEpp[:]) - err = expandSeedIntoA(&A, &sk.pk.seedA, &shake128) - if err != nil { - return err - } + expandSeedIntoA(&A, &sk.pk.seedA, &shake128) mulAddSAPlusE(&BBp, Sp[:], &A, Ep[:]) // Reduce BBp modulo q @@ -368,7 +298,7 @@ func (sk *PrivateKey) DecapsulateTo(ss, ct []byte) error { mulAddSBPlusE(&W, Sp, &sk.pk.matrixB, Epp) // Encode mu, and compute CC = W + enc(mu') (mod q) - encodeMessage(CC[:], muprime[:]) + encodeMessage(&CC, &muprime) add(&CC, &W, &CC) // Prepare input to F @@ -382,19 +312,9 @@ func (sk *PrivateKey) DecapsulateTo(ss, ct []byte) error { subtle.ConstantTimeCopy(selector, kprime[:], sk.hashInputIfDecapsFail[:]) shake128.Reset() - _, err = shake128.Write(ct[:]) - if err != nil { - return err - } - _, err = shake128.Write(kprime[:]) - if err != nil { - return err - } - _, err = shake128.Read(ss[:]) - if err != nil { - return err - } - return nil + _, _ = shake128.Write(ct[:]) + _, _ = shake128.Write(kprime[:]) + _, _ = shake128.Read(ss[:]) } // Packs sk to buf. @@ -550,11 +470,7 @@ func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { if len(seed) != KeySeedSize { panic(kem.ErrSeedSize) } - pk, sk, err := newKeyFromSeed(seed[:]) - if err != nil { - panic(err) - } - return pk, sk + return newKeyFromSeed(seed[:]) } func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { @@ -565,7 +481,7 @@ func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { if !ok { return nil, nil, kem.ErrTypeMismatch } - err = pub.EncapsulateTo(ct, ss, nil) + pub.EncapsulateTo(ct, ss, nil) return } @@ -582,7 +498,7 @@ func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) ( if !ok { return nil, nil, kem.ErrTypeMismatch } - err = pub.EncapsulateTo(ct, ss, seed) + pub.EncapsulateTo(ct, ss, seed) return } @@ -596,8 +512,8 @@ func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) { return nil, kem.ErrTypeMismatch } ss := make([]byte, SharedKeySize) - err := priv.DecapsulateTo(ss, ct) - return ss, err + priv.DecapsulateTo(ss, ct) + return ss, nil } func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) { diff --git a/kem/frodo/frodo640shake/matrix_shake.go b/kem/frodo/frodo640shake/matrix_shake.go index 2ea7283c1..8ec944349 100644 --- a/kem/frodo/frodo640shake/matrix_shake.go +++ b/kem/frodo/frodo640shake/matrix_shake.go @@ -4,7 +4,7 @@ import ( "github.com/cloudflare/circl/internal/sha3" ) -func expandSeedIntoA(A *[paramN * paramN]uint16, seed *[seedASize]byte, xof *sha3.State) error { +func expandSeedIntoA(A *[paramN * paramN]uint16, seed *[seedASize]byte, xof *sha3.State) { var ARow [paramN * 2]byte var seedSeparated [2 + seedASize]byte @@ -15,20 +15,15 @@ func expandSeedIntoA(A *[paramN * paramN]uint16, seed *[seedASize]byte, xof *sha seedSeparated[1] = byte(i >> 8) xof.Reset() - _, err := xof.Write(seedSeparated[:]) - if err != nil { - return err - } - _, err = xof.Read(ARow[:]) - if err != nil { - return err - } + _, _ = xof.Write(seedSeparated[:]) + _, _ = xof.Read(ARow[:]) for j := 0; j < paramN; j++ { + // No need to reduce modulo 2^15, extra bits are removed + // later on via packing or explicit reduction. A[(i*paramN)+j] = uint16(ARow[j*2]) | (uint16(ARow[(j*2)+1]) << 8) } } - return nil } func mulAddASPlusE(out *[paramN * paramNbar]uint16, A *[paramN * paramN]uint16, s []uint16, e []uint16) { @@ -40,8 +35,8 @@ func mulAddASPlusE(out *[paramN * paramNbar]uint16, A *[paramN * paramN]uint16, for j := 0; j < paramN; j++ { sum += A[i*paramN+j] * s[k*paramN+j] } - // Adding e. No need to reduce modulo 2^15, extra bits are taken - // care of during packing later on. + // No need to reduce modulo 2^15, extra bits are removed + // later on via packing or explicit reduction. out[i*paramNbar+k] += sum } } @@ -56,8 +51,8 @@ func mulAddSAPlusE(out *[paramNbar * paramN]uint16, s []uint16, A *[paramN * par for j := 0; j < paramN; j++ { sum += A[j*paramN+i] * s[k*paramN+j] } - // Adding e. No need to reduce modulo 2^15, extra bits are taken - // care of during packing later on. + // No need to reduce modulo 2^15, extra bits are removed + // later on via packing or explicit reduction. out[k*paramN+i] += sum } } diff --git a/kem/frodo/frodo640shake/noise.go b/kem/frodo/frodo640shake/noise.go index e0a47107a..934ed9e43 100644 --- a/kem/frodo/frodo640shake/noise.go +++ b/kem/frodo/frodo640shake/noise.go @@ -4,6 +4,8 @@ const cdfTableLen = 13 var cdfTable [cdfTableLen]uint16 = [cdfTableLen]uint16{4643, 13363, 20579, 25843, 29227, 31145, 32103, 32525, 32689, 32745, 32762, 32766, 32767} +// Take a uniformly distributed sample, and produce a sample in the FrodoKEM +// discrete Gaussian distribution using inverse transform sampling. func sample(sampled []uint16) { for i := 0; i < len(sampled); i++ { var gaussianSample uint16 = 0 @@ -13,6 +15,12 @@ func sample(sampled []uint16) { for j := 0; j < cdfTableLen-1; j++ { gaussianSample += (cdfTable[j] - unifSample) >> 15 } + // If sign = 1, -sign = 0xFFFF and the bits of gaussianSample + // are flipped. Since gaussianSample is uint16, we have: + // + // flippedBits(gaussianSample) + 1 ≡ -gaussianSample (mod 2^16), + // + // and so the sign of gaussianSample is flipped. sampled[i] = ((-sign) ^ gaussianSample) + sign } } diff --git a/kem/frodo/frodo640shake/util.go b/kem/frodo/frodo640shake/util.go index c4e7ab377..ab2f52aa4 100644 --- a/kem/frodo/frodo640shake/util.go +++ b/kem/frodo/frodo640shake/util.go @@ -79,7 +79,7 @@ func unpack(out []uint16, in []byte) { } } -func encodeMessage(out []uint16, msg []byte) { +func encodeMessage(out *[paramNbar * paramNbar]uint16, msg *[messageSize]byte) { extractedBitsMask := uint16((1 << extractedBits) - 1) outPos := 0 @@ -94,7 +94,7 @@ func encodeMessage(out []uint16, msg []byte) { } } -func decodeMessage(out []byte, msg []uint16) { +func decodeMessage(out *[messageSize]byte, msg *[paramNbar * paramNbar]uint16) { extractedBitsMask := uint16((1 << extractedBits) - 1) msgPos := 0 From dff96e10a92d3ab2071c4a4acec7982f44b906f6 Mon Sep 17 00:00:00 2001 From: Goutam Tamvada Date: Tue, 15 Mar 2022 16:22:15 +0100 Subject: [PATCH 2/5] Rewrote pack and unpack(). --- kem/frodo/frodo640shake/util.go | 107 ++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 47 deletions(-) diff --git a/kem/frodo/frodo640shake/util.go b/kem/frodo/frodo640shake/util.go index ab2f52aa4..1f63794d4 100644 --- a/kem/frodo/frodo640shake/util.go +++ b/kem/frodo/frodo640shake/util.go @@ -20,62 +20,75 @@ func sub(out *[paramNbar * paramNbar]uint16, lhs *[paramNbar * paramNbar]uint16, } func pack(out []byte, in []uint16) { - outBitsFree := uint(0) - inBitsLeft := uint(0) - var inElem uint16 - - j := -1 - for i := 0; i < len(in); { - if outBitsFree == 0 { - outBitsFree = 8 - j += 1 - out[j] = 0 - } + logQMask := uint16((1 << 15) - 1) + j := 0 + for i := 0; (i * 8) < len(in); i++ { + in0 := in[i*8] & logQMask + in1 := in[(i*8)+1] & logQMask + in2 := in[(i*8)+2] & logQMask + in3 := in[(i*8)+3] & logQMask + in4 := in[(i*8)+4] & logQMask + in5 := in[(i*8)+5] & logQMask + in6 := in[(i*8)+6] & logQMask + in7 := in[(i*8)+7] & logQMask - if inBitsLeft == 0 { - inElem = in[i] - inBitsLeft = logQ - i += 1 - } + out[j] |= byte(in0 >> 7) + out[j+1] = (byte(in0&0x7F) << 1) | byte(in1>>14) - toCopy := min(outBitsFree, inBitsLeft) - outBitsFree -= toCopy - inBitsLeft -= toCopy + out[j+2] = byte(in1 >> 6) + out[j+3] = (byte(in1&0x3F) << 2) | byte(in2>>13) - var mask uint16 = (1 << toCopy) - 1 + out[j+4] = byte(in2 >> 5) + out[j+5] = (byte(in2&0x1F) << 3) | byte(in3>>12) - out[j] |= byte((inElem>>inBitsLeft)&mask) << outBitsFree - } + out[j+6] = byte(in3 >> 4) + out[j+7] = (byte(in3&0x0F) << 4) | byte(in4>>11) - out[j+1] = byte(inElem) -} + out[j+8] = byte(in4 >> 3) + out[j+9] = (byte(in4&0x07) << 5) | byte(in5>>10) -func unpack(out []uint16, in []byte) { - var outBitsFree uint = 0 - var inBitsLeft uint = 0 - var inByte byte - - j := -1 - for i := 0; i < len(in); { - if outBitsFree == 0 { - outBitsFree = logQ - j += 1 - out[j] = 0 - } + out[j+10] = byte(in5 >> 2) + out[j+11] = (byte(in5&0x03) << 6) | byte(in6>>9) - if inBitsLeft == 0 { - inByte = in[i] - inBitsLeft = 8 - i += 1 - } - - toCopy := min(outBitsFree, inBitsLeft) - outBitsFree -= toCopy - inBitsLeft -= toCopy + out[j+12] = byte(in6 >> 1) + out[j+13] = (byte(in6&0x01) << 7) | byte(in7>>8) - mask := byte((1 << toCopy) - 1) + out[j+14] = byte(in7) + j += 15 + } +} - out[j] |= uint16((inByte>>inBitsLeft)&mask) << outBitsFree +func unpack(out []uint16, in []byte) { + j := 0 + for i := 0; (i * 15) < len(in); i++ { + in0 := in[i*15] + in1 := in[(i*15)+1] + in2 := in[(i*15)+2] + in3 := in[(i*15)+3] + in4 := in[(i*15)+4] + in5 := in[(i*15)+5] + in6 := in[(i*15)+6] + in7 := in[(i*15)+7] + in8 := in[(i*15)+8] + in9 := in[(i*15)+9] + in10 := in[(i*15)+10] + in11 := in[(i*15)+11] + in12 := in[(i*15)+12] + in13 := in[(i*15)+13] + in14 := in[(i*15)+14] + + out[j] = (uint16(in0) << 7) | (uint16(in1&0xFE) >> 1) + out[j+1] = (uint16(in1&0x1) << 14) | (uint16(in2) << 6) | (uint16(in3&0xFC) >> 2) + + out[j+2] = (uint16(in3&0x03) << 13) | (uint16(in4) << 5) | (uint16(in5&0xF8) >> 3) + out[j+3] = (uint16(in5&0x07) << 12) | (uint16(in6) << 4) | (uint16(in7&0xF0) >> 4) + + out[j+4] = (uint16(in7&0x0F) << 11) | (uint16(in8) << 3) | (uint16(in9&0xE0) >> 5) + out[j+5] = (uint16(in9&0x1F) << 10) | (uint16(in10) << 2) | (uint16(in11&0xC0) >> 6) + + out[j+6] = (uint16(in11&0x3F) << 9) | (uint16(in12) << 1) | (uint16(in13&0x80) >> 7) + out[j+7] = (uint16(in13&0x7F) << 8) | uint16(in14) + j += 8 } } From 1d5f014032a42697c2d1cb0e79d870ec1d1aa06f Mon Sep 17 00:00:00 2001 From: Goutam Tamvada Date: Tue, 15 Mar 2022 23:17:20 +0100 Subject: [PATCH 3/5] golangci-lint --- kem/frodo/frodo640shake/frodo.go | 4 ++-- kem/frodo/frodo640shake/util.go | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/kem/frodo/frodo640shake/frodo.go b/kem/frodo/frodo640shake/frodo.go index f9b438910..f8d2ad942 100644 --- a/kem/frodo/frodo640shake/frodo.go +++ b/kem/frodo/frodo640shake/frodo.go @@ -200,8 +200,8 @@ func (pk *PublicKey) EncapsulateTo(ct []byte, ss []byte, seed []byte) { // Bp = Sp*A + Ep // V = Sp*B + Epp shake128.Reset() - shake128.Write([]byte{0x96}) - shake128.Write(G2out[:SharedKeySize]) + _, _ = shake128.Write([]byte{0x96}) + _, _ = shake128.Write(G2out[:SharedKeySize]) _, _ = shake128.Read(byteSpEpEpp[:]) for i := range SpEpEpp { SpEpEpp[i] = uint16(byteSpEpEpp[i*2]) | (uint16(byteSpEpEpp[(i*2)+1]) << 8) diff --git a/kem/frodo/frodo640shake/util.go b/kem/frodo/frodo640shake/util.go index 1f63794d4..24c43fc38 100644 --- a/kem/frodo/frodo640shake/util.go +++ b/kem/frodo/frodo640shake/util.go @@ -1,12 +1,5 @@ package frodo640shake -func min(x uint, y uint) uint { - if x < y { - return x - } - return y -} - func add(out *[paramNbar * paramNbar]uint16, lhs *[paramNbar * paramNbar]uint16, rhs *[paramNbar * paramNbar]uint16) { for i := 0; i < len(out); i++ { out[i] = (lhs[i] + rhs[i]) & logQMask From 6b8f1f2659e5b92eadec2d8e51da99876557eb8c Mon Sep 17 00:00:00 2001 From: Goutam Tamvada Date: Wed, 16 Mar 2022 11:38:26 +0100 Subject: [PATCH 4/5] Added some type aliases. --- kem/frodo/frodo640shake/frodo.go | 45 +++++++++++++++---------- kem/frodo/frodo640shake/matrix_shake.go | 14 +++----- kem/frodo/frodo640shake/util.go | 13 ++++--- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/kem/frodo/frodo640shake/frodo.go b/kem/frodo/frodo640shake/frodo.go index f8d2ad942..a6fd07721 100644 --- a/kem/frodo/frodo640shake/frodo.go +++ b/kem/frodo/frodo640shake/frodo.go @@ -31,8 +31,9 @@ const ( ) const ( - // Size of seed for NewKeyFromSeed - KeySeedSize = 2*SharedKeySize + seedASize + // Size of seed for NewKeyFromSeed. + // = len(s) + len(seedSE) + len(z). + KeySeedSize = SharedKeySize + SharedKeySize + 16 // Size of seed for EncapsulateTo. EncapsulationSeedSize = 16 @@ -50,10 +51,17 @@ const ( PrivateKeySize = 19888 ) +type ( + nByNU16 [paramN * paramN]uint16 + nByNbarU16 [paramN * paramNbar]uint16 + nbarByNU16 [paramNbar * paramN]uint16 + nbarByNbarU16 [paramNbar * paramNbar]uint16 +) + // Type of a FrodoKEM-640-SHAKE public key type PublicKey struct { seedA [seedASize]byte - matrixB [paramN * paramNbar]uint16 + matrixB nByNbarU16 } // Type of a FrodoKEM-640-SHAKE private key @@ -62,7 +70,7 @@ type PrivateKey struct { pk *PublicKey // matrixS stores transpose(S) - matrixS [paramN * paramNbar]uint16 + matrixS nByNbarU16 // H(packed(pk)) hpk [pkHashSize]byte @@ -80,10 +88,10 @@ func newKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { var sk PrivateKey var pk PublicKey - var E [paramN * paramNbar]uint16 + var E nByNbarU16 var byteSE [2 * (len(sk.matrixS) + len(E))]byte - var A [paramN * paramN]uint16 + var A nByNU16 // Generate the secret value s, and the seed for S, E, and A. Add seedA to the public key shake128 := sha3.NewShake128() @@ -109,7 +117,7 @@ func newKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { sample(E[:]) expandSeedIntoA(&A, &pk.seedA, &shake128) - mulAddASPlusE(&pk.matrixB, &A, sk.matrixS[:], E[:]) + mulAddASPlusE(&pk.matrixB, &A, &sk.matrixS, &E) // Populate the private key copy(sk.hashInputIfDecapsFail[:], seed[0:SharedKeySize]) @@ -171,12 +179,12 @@ func (pk *PublicKey) EncapsulateTo(ct []byte, ss []byte, seed []byte) { Ep := SpEpEpp[paramN*paramNbar : 2*paramN*paramNbar] Epp := SpEpEpp[2*paramN*paramNbar:] - var Bp [paramN * paramNbar]uint16 + var Bp nbarByNU16 - var V [paramNbar * paramNbar]uint16 - var C [paramNbar * paramNbar]uint16 + var V nbarByNbarU16 + var C nbarByNbarU16 - var A [paramN * paramN]uint16 + var A nByNU16 var hpk [pkHashSize]byte @@ -241,12 +249,12 @@ func (sk *PrivateKey) DecapsulateTo(ss, ct []byte) { panic("ss must be of length SharedKeySize") } - var Bp [paramN * paramNbar]uint16 - var C [paramNbar * paramNbar]uint16 + var Bp nbarByNU16 + var C nbarByNbarU16 - var W [paramNbar * paramNbar]uint16 - var CC [paramNbar * paramNbar]uint16 - var BBp [paramN * paramNbar]uint16 + var W nbarByNbarU16 + var CC nbarByNbarU16 + var BBp nbarByNU16 var SpEpEpp [(paramN * paramNbar) + (paramN * paramNbar) + (paramNbar * paramNbar)]uint16 var byteSpEpEpp [2 * len(SpEpEpp)]byte @@ -254,7 +262,7 @@ func (sk *PrivateKey) DecapsulateTo(ss, ct []byte) { Ep := SpEpEpp[paramN*paramNbar : 2*paramN*paramNbar] Epp := SpEpEpp[2*paramN*paramNbar:] - var A [paramN * paramN]uint16 + var A nByNU16 var muprime [messageSize]byte var G2out [2 * SharedKeySize]byte @@ -486,7 +494,8 @@ func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) { } func (*scheme) EncapsulateDeterministically(pk kem.PublicKey, seed []byte) ( - ct, ss []byte, err error) { + ct, ss []byte, err error, +) { if len(seed) != EncapsulationSeedSize { return nil, nil, kem.ErrSeedSize } diff --git a/kem/frodo/frodo640shake/matrix_shake.go b/kem/frodo/frodo640shake/matrix_shake.go index 8ec944349..d41a17e75 100644 --- a/kem/frodo/frodo640shake/matrix_shake.go +++ b/kem/frodo/frodo640shake/matrix_shake.go @@ -4,7 +4,7 @@ import ( "github.com/cloudflare/circl/internal/sha3" ) -func expandSeedIntoA(A *[paramN * paramN]uint16, seed *[seedASize]byte, xof *sha3.State) { +func expandSeedIntoA(A *nByNU16, seed *[seedASize]byte, xof *sha3.State) { var ARow [paramN * 2]byte var seedSeparated [2 + seedASize]byte @@ -26,12 +26,10 @@ func expandSeedIntoA(A *[paramN * paramN]uint16, seed *[seedASize]byte, xof *sha } } -func mulAddASPlusE(out *[paramN * paramNbar]uint16, A *[paramN * paramN]uint16, s []uint16, e []uint16) { - copy(out[:], e) - +func mulAddASPlusE(out *nByNbarU16, A *nByNU16, s *nByNbarU16, e *nByNbarU16) { for i := 0; i < paramN; i++ { for k := 0; k < paramNbar; k++ { - sum := uint16(0) + sum := e[i*paramNbar+k] for j := 0; j < paramN; j++ { sum += A[i*paramN+j] * s[k*paramN+j] } @@ -42,12 +40,10 @@ func mulAddASPlusE(out *[paramN * paramNbar]uint16, A *[paramN * paramN]uint16, } } -func mulAddSAPlusE(out *[paramNbar * paramN]uint16, s []uint16, A *[paramN * paramN]uint16, e []uint16) { - copy(out[:], e) - +func mulAddSAPlusE(out *nbarByNU16, s []uint16, A *nByNU16, e []uint16) { for i := 0; i < paramN; i++ { for k := 0; k < paramNbar; k++ { - var sum uint16 = 0 + sum := e[k*paramN+i] for j := 0; j < paramN; j++ { sum += A[j*paramN+i] * s[k*paramN+j] } diff --git a/kem/frodo/frodo640shake/util.go b/kem/frodo/frodo640shake/util.go index 24c43fc38..b060a6733 100644 --- a/kem/frodo/frodo640shake/util.go +++ b/kem/frodo/frodo640shake/util.go @@ -1,19 +1,18 @@ package frodo640shake -func add(out *[paramNbar * paramNbar]uint16, lhs *[paramNbar * paramNbar]uint16, rhs *[paramNbar * paramNbar]uint16) { +func add(out *nbarByNbarU16, lhs *nbarByNbarU16, rhs *nbarByNbarU16) { for i := 0; i < len(out); i++ { out[i] = (lhs[i] + rhs[i]) & logQMask } } -func sub(out *[paramNbar * paramNbar]uint16, lhs *[paramNbar * paramNbar]uint16, rhs *[paramNbar * paramNbar]uint16) { +func sub(out *nbarByNbarU16, lhs *nbarByNbarU16, rhs *nbarByNbarU16) { for i := 0; i < len(out); i++ { out[i] = (lhs[i] - rhs[i]) & logQMask } } func pack(out []byte, in []uint16) { - logQMask := uint16((1 << 15) - 1) j := 0 for i := 0; (i * 8) < len(in); i++ { in0 := in[i*8] & logQMask @@ -85,7 +84,7 @@ func unpack(out []uint16, in []byte) { } } -func encodeMessage(out *[paramNbar * paramNbar]uint16, msg *[messageSize]byte) { +func encodeMessage(out *nbarByNbarU16, msg *[messageSize]byte) { extractedBitsMask := uint16((1 << extractedBits) - 1) outPos := 0 @@ -100,7 +99,7 @@ func encodeMessage(out *[paramNbar * paramNbar]uint16, msg *[messageSize]byte) { } } -func decodeMessage(out *[messageSize]byte, msg *[paramNbar * paramNbar]uint16) { +func decodeMessage(out *[messageSize]byte, msg *nbarByNbarU16) { extractedBitsMask := uint16((1 << extractedBits) - 1) msgPos := 0 @@ -115,7 +114,7 @@ func decodeMessage(out *[messageSize]byte, msg *[paramNbar * paramNbar]uint16) { } } -func mulAddSBPlusE(out *[paramNbar * paramNbar]uint16, s []uint16, b *[paramN * paramNbar]uint16, e []uint16) { +func mulAddSBPlusE(out *nbarByNbarU16, s []uint16, b *nByNbarU16, e []uint16) { // Multiply by s on the left // Inputs: b (N x N_BAR), s (N_BAR x N), e (N_BAR x N_BAR) // Output: out = s*b + e (N_BAR x N_BAR) @@ -131,7 +130,7 @@ func mulAddSBPlusE(out *[paramNbar * paramNbar]uint16, s []uint16, b *[paramN * } } -func mulBS(out *[paramNbar * paramNbar]uint16, b *[paramNbar * paramN]uint16, s *[paramN * paramNbar]uint16) { +func mulBS(out *nbarByNbarU16, b *nbarByNU16, s *nByNbarU16) { for i := 0; i < paramNbar; i++ { for j := 0; j < paramNbar; j++ { out[i*paramNbar+j] = 0 From fead45de45c356e948993a86f1d9707c8c1a124f Mon Sep 17 00:00:00 2001 From: Goutam Tamvada Date: Wed, 16 Mar 2022 12:24:35 +0100 Subject: [PATCH 5/5] Moved a comment. --- kem/frodo/frodo640shake/frodo.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kem/frodo/frodo640shake/frodo.go b/kem/frodo/frodo640shake/frodo.go index a6fd07721..b2f2a2bb8 100644 --- a/kem/frodo/frodo640shake/frodo.go +++ b/kem/frodo/frodo640shake/frodo.go @@ -1,5 +1,4 @@ // Package frodo640shake implements the variant FrodoKEM-640 with SHAKE. -// Multi-dimensional arrays are stored in 1-dimensional arrays in row-major order. package frodo640shake import ( @@ -51,6 +50,8 @@ const ( PrivateKeySize = 19888 ) +// Multi-dimensional arrays are stored in 1-dimensional arrays in +// row-major order. type ( nByNU16 [paramN * paramN]uint16 nByNbarU16 [paramN * paramNbar]uint16