// Code generated from kyber512/internal/cpapke.go by gen.go package internal import ( "github.com/cloudflare/circl/internal/sha3" "github.com/cloudflare/circl/pke/kyber/internal/common" ) // A Kyber.CPAPKE private key. type PrivateKey struct { sh Vec // NTT(s), normalized } // A Kyber.CPAPKE public key. type PublicKey struct { rho [32]byte // ρ, the seed for the matrix A th Vec // NTT(t), normalized // cached values aT Mat // the matrix Aᵀ } // Packs the private key to buf. func (sk *PrivateKey) Pack(buf []byte) { sk.sh.Pack(buf) } // Unpacks the private key from buf. func (sk *PrivateKey) Unpack(buf []byte) { sk.sh.Unpack(buf) sk.sh.Normalize() } // Packs the public key to buf. func (pk *PublicKey) Pack(buf []byte) { pk.th.Pack(buf) copy(buf[K*common.PolySize:], pk.rho[:]) } // Unpacks the public key from buf. func (pk *PublicKey) Unpack(buf []byte) { pk.th.Unpack(buf) pk.th.Normalize() copy(pk.rho[:], buf[K*common.PolySize:]) pk.aT.Derive(&pk.rho, true) } // Derives a new Kyber.CPAPKE keypair from the given seed. func NewKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) { var pk PublicKey var sk PrivateKey var expandedSeed [64]byte h := sha3.New512() _, _ = h.Write(seed) // This writes hash into expandedSeed. Yes, this is idiomatic Go. _, _ = h.Read(expandedSeed[:]) copy(pk.rho[:], expandedSeed[:32]) sigma := expandedSeed[32:] // σ, the noise seed pk.aT.Derive(&pk.rho, false) // Expand ρ to matrix A; we'll transpose later var eh Vec sk.sh.DeriveNoise(sigma, 0, Eta1) // Sample secret vector s sk.sh.NTT() sk.sh.Normalize() eh.DeriveNoise(sigma, K, Eta1) // Sample blind e eh.NTT() // Next, we compute t = A s + e. for i := 0; i < K; i++ { // Note that coefficients of s are bounded by q and those of A // are bounded by 4.5q and so their product is bounded by 2¹⁵q // as required for multiplication. PolyDotHat(&pk.th[i], &pk.aT[i], &sk.sh) // A and s were not in Montgomery form, so the Montgomery // multiplications in the inner product added a factor R⁻¹ which // we'll cancel out now. This will also ensure the coefficients of // t are bounded in absolute value by q. pk.th[i].ToMont() } pk.th.Add(&pk.th, &eh) // bounded by 8q. pk.th.Normalize() pk.aT.Transpose() return &pk, &sk } // Decrypts ciphertext ct meant for private key sk to plaintext pt. func (sk *PrivateKey) DecryptTo(pt, ct []byte) { var u Vec var v, m common.Poly u.Decompress(ct, DU) v.Decompress(ct[K*compressedPolySize(DU):], DV) // Compute m = v - u.NTT() PolyDotHat(&m, &sk.sh, &u) m.BarrettReduce() m.InvNTT() m.Sub(&v, &m) m.Normalize() // Compress polynomial m to original message m.CompressMessageTo(pt) } // Encrypts message pt for the public key to ciphertext ct using randomness // from seed. // // seed has to be of length SeedSize, pt of PlaintextSize and ct of // CiphertextSize. func (pk *PublicKey) EncryptTo(ct, pt, seed []byte) { var rh, e1, u Vec var e2, v, m common.Poly // Sample r, e₁ and e₂ from B_η rh.DeriveNoise(seed, 0, Eta1) rh.NTT() rh.BarrettReduce() e1.DeriveNoise(seed, K, common.Eta2) e2.DeriveNoise(seed, 2*K, common.Eta2) // Next we compute u = Aᵀ r + e₁. First Aᵀ. for i := 0; i < K; i++ { // Note that coefficients of r are bounded by q and those of Aᵀ // are bounded by 4.5q and so their product is bounded by 2¹⁵q // as required for multiplication. PolyDotHat(&u[i], &pk.aT[i], &rh) } u.BarrettReduce() // Aᵀ and r were not in Montgomery form, so the Montgomery // multiplications in the inner product added a factor R⁻¹ which // the InvNTT cancels out. u.InvNTT() u.Add(&u, &e1) // u = Aᵀ r + e₁ // Next compute v = + e₂ + Decompress_q(m, 1). PolyDotHat(&v, &pk.th, &rh) v.BarrettReduce() v.InvNTT() m.DecompressMessage(pt) v.Add(&v, &m) v.Add(&v, &e2) // v = + e₂ + Decompress_q(m, 1) // Pack ciphertext u.Normalize() v.Normalize() u.CompressTo(ct, DU) v.CompressTo(ct[K*compressedPolySize(DU):], DV) } // Returns whether sk equals other. func (sk *PrivateKey) Equal(other *PrivateKey) bool { ret := int16(0) for i := 0; i < K; i++ { for j := 0; j < common.N; j++ { ret |= sk.sh[i][j] ^ other.sh[i][j] } } return ret == 0 }