in python3/frodokem.py [0:0]
def kem_encaps(self, pk):
"""Encapsulate against a public key to create a ciphertext and shared secret
(FrodoKEM specification, Algorithm 13)"""
# Parse pk = seedA || b
assert len(pk) == self.len_seedA_bytes + self.D * self.n * self.nbar / 8, "Incorrect public key length"
seedA = pk[0 : self.len_seedA_bytes]
b = pk[self.len_seedA_bytes:]
# 1. Choose a uniformly random key mu in {0,1}^len_mu (length in bits)
mu = self.randombytes(self.len_mu_bytes)
self.__print_intermediate_value("mu", mu)
# 2. pkh = SHAKE(pk, len_pkh)
pkh = self.shake(pk, self.len_pkh_bytes)
self.__print_intermediate_value("pkh", pkh)
# 3. seedSE || k = SHAKE(pkh || mu, len_seedSE + len_k) (length in bits)
seedSE_k = self.shake(pkh + mu, self.len_seedSE_bytes + self.len_k_bytes)
seedSE = seedSE_k[0:self.len_seedSE_bytes]
self.__print_intermediate_value("seedSE", seedSE)
k = seedSE_k[self.len_seedSE_bytes:self.len_seedSE_bytes + self.len_k_bytes]
self.__print_intermediate_value("k", k)
# 4. r = SHAKE(0x96 || seedSE, 2*mbar*n + mbar*nbar*len_chi) (length in bits)
rbytes = self.shake(bytes(b'\x96') + seedSE, (2 * self.mbar * self.n + self.mbar * self.mbar) * self.len_chi_bytes)
r = [struct.unpack_from('<H', rbytes, 2*i)[0] for i in range(2 * self.mbar * self.n + self.mbar * self.nbar)]
self.__print_intermediate_value("r", r)
# 5. S' = Frodo.SampleMatrix(r[0 .. mbar*n-1], mbar, n)
Sprime = self.sample_matrix(r[0 : self.mbar * self.n], self.mbar, self.n)
self.__print_intermediate_value("S'", Sprime)
# 6. E' = Frodo.SampleMatrix(r[mbar*n .. 2*mbar*n-1], mbar, n)
Eprime = self.sample_matrix(r[self.mbar * self.n : 2 * self.mbar * self.n], self.mbar, self.n)
self.__print_intermediate_value("E'", Eprime)
# 7. A = Frodo.Gen(seedA)
A = self.gen(seedA)
# 8. B' = S' A + E'
Bprime = self.__matrix_add(self.__matrix_mul(Sprime, A), Eprime)
self.__print_intermediate_value("B'", Bprime)
# 9. c1 = Frodo.Pack(B')
c1 = self.pack(Bprime)
self.__print_intermediate_value("c1", c1)
# 10. E'' = Frodo.SampleMatrix(r[2*mbar*n .. 2*mbar*n + mbar*nbar-1], mbar, n)
Eprimeprime = self.sample_matrix(r[2 * self.mbar * self.n : 2 * self.mbar * self.n + self.mbar * self.nbar], self.mbar, self.nbar)
self.__print_intermediate_value("E''", Eprimeprime)
# 11. B = Frodo.Unpack(b, n, nbar)
B = self.unpack(b, self.n, self.nbar)
self.__print_intermediate_value("B", B)
# 12. V = S' B + E''
V = self.__matrix_add(self.__matrix_mul(Sprime, B), Eprimeprime)
self.__print_intermediate_value("V", V)
# 13. C = V + Frodo.Encode(mu)
self.__print_intermediate_value("mu_encoded", self.encode(mu))
C = self.__matrix_add(V, self.encode(mu))
self.__print_intermediate_value("C", C)
# 14. c2 = Frodo.Pack(C)
c2 = self.pack(C)
self.__print_intermediate_value("c2", c2)
# 15. ss = SHAKE(c1 || c2 || k, len_ss)
ss = self.shake(c1 + c2 + k, self.len_ss_bytes)
ct = c1 + c2
assert len(ct) == self.len_ct_bytes
assert len(ss) == self.len_ss_bytes
return (ct, ss)