mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-07-22 10:22:50 +02:00
289 lines
10 KiB
Rust
289 lines
10 KiB
Rust
#[cfg(not(feature = "KATs"))]
|
|
use crate::rng::randombytes;
|
|
use crate::{params::*, poly::*, polyvec::*, symmetric::*, CryptoRng, RngCore};
|
|
|
|
// Name: pack_pk
|
|
//
|
|
// Description: Serialize the public key as concatenation of the
|
|
// serialized vector of polynomials pk
|
|
// and the public seed used to generate the matrix A.
|
|
//
|
|
// Arguments: [u8] r: the output serialized public key
|
|
// const poly *pk: the input public-key polynomial
|
|
// const [u8] seed: the input public seed
|
|
fn pack_pk(r: &mut [u8], pk: &mut Polyvec, seed: &[u8]) {
|
|
const END: usize = KYBER_SYMBYTES + KYBER_POLYVECBYTES;
|
|
polyvec_tobytes(r, pk);
|
|
r[KYBER_POLYVECBYTES..END].copy_from_slice(&seed[..KYBER_SYMBYTES]);
|
|
}
|
|
|
|
// Name: unpack_pk
|
|
//
|
|
// Description: De-serialize public key from a byte array;
|
|
// approximate inverse of pack_pk
|
|
//
|
|
// Arguments: - Polyvec pk: output public-key vector of polynomials
|
|
// - [u8] seed: output seed to generate matrix A
|
|
// - const [u8] packedpk: input serialized public key
|
|
fn unpack_pk(pk: &mut Polyvec, seed: &mut [u8], packedpk: &[u8]) {
|
|
const END: usize = KYBER_SYMBYTES + KYBER_POLYVECBYTES;
|
|
polyvec_frombytes(pk, packedpk);
|
|
seed[..KYBER_SYMBYTES].copy_from_slice(&packedpk[KYBER_POLYVECBYTES..END]);
|
|
}
|
|
|
|
// Name: pack_sk
|
|
//
|
|
// Description: Serialize the secret key
|
|
//
|
|
// Arguments: - [u8] r: output serialized secret key
|
|
// - const Polyvec sk: input vector of polynomials (secret key)
|
|
fn pack_sk(r: &mut [u8], sk: &mut Polyvec) {
|
|
polyvec_tobytes(r, sk);
|
|
}
|
|
|
|
// Name: unpack_sk
|
|
//
|
|
// Description: De-serialize the secret key, inverse of pack_sk
|
|
//
|
|
// Arguments: - Polyvec sk: output vector of polynomials (secret key)
|
|
// - const [u8] packedsk: input serialized secret key
|
|
fn unpack_sk(sk: &mut Polyvec, packedsk: &[u8]) {
|
|
polyvec_frombytes(sk, packedsk);
|
|
}
|
|
|
|
// Name: pack_ciphertext
|
|
//
|
|
// Description: Serialize the ciphertext as concatenation of the
|
|
// compressed and serialized vector of polynomials b
|
|
// and the compressed and serialized polynomial v
|
|
//
|
|
// Arguments: [u8] r: the output serialized ciphertext
|
|
// const poly *pk: the input vector of polynomials b
|
|
// const [u8] seed: the input polynomial v
|
|
fn pack_ciphertext(r: &mut [u8], b: &mut Polyvec, v: Poly) {
|
|
polyvec_compress(r, *b);
|
|
poly_compress(&mut r[KYBER_POLYVECCOMPRESSEDBYTES..], v);
|
|
}
|
|
|
|
// Name: unpack_ciphertext
|
|
//
|
|
// Description: De-serialize and decompress ciphertext from a byte array;
|
|
// approximate inverse of pack_ciphertext
|
|
//
|
|
// Arguments: - Polyvec b: output vector of polynomials b
|
|
// - poly *v: output polynomial v
|
|
// - const [u8] c: input serialized ciphertext
|
|
fn unpack_ciphertext(b: &mut Polyvec, v: &mut Poly, c: &[u8]) {
|
|
polyvec_decompress(b, c);
|
|
poly_decompress(v, &c[KYBER_POLYVECCOMPRESSEDBYTES..]);
|
|
}
|
|
|
|
// Name: rej_uniform
|
|
//
|
|
// Description: Run rejection sampling on uniform random bytes to generate
|
|
// uniform random integers mod q
|
|
//
|
|
// Arguments: - i16 *r: output buffer
|
|
// - usize len: requested number of 16-bit integers (uniform mod q)
|
|
// - const [u8] buf: input buffer (assumed to be uniform random bytes)
|
|
// - usize buflen: length of input buffer in bytes
|
|
//
|
|
// Returns number of sampled 16-bit integers (at most len)
|
|
fn rej_uniform(r: &mut [i16], len: usize, buf: &[u8], buflen: usize) -> usize {
|
|
let (mut ctr, mut pos) = (0usize, 0usize);
|
|
let (mut val0, mut val1);
|
|
|
|
while ctr < len && pos + 3 <= buflen {
|
|
val0 = ((buf[pos + 0] >> 0) as u16 | (buf[pos + 1] as u16) << 8) & 0xFFF;
|
|
val1 = ((buf[pos + 1] >> 4) as u16 | (buf[pos + 2] as u16) << 4) & 0xFFF;
|
|
pos += 3;
|
|
|
|
if val0 < KYBER_Q as u16 {
|
|
r[ctr] = val0 as i16;
|
|
ctr += 1;
|
|
}
|
|
if ctr < len && val1 < KYBER_Q as u16 {
|
|
r[ctr] = val1 as i16;
|
|
ctr += 1;
|
|
}
|
|
}
|
|
ctr
|
|
}
|
|
|
|
fn gen_a(a: &mut [Polyvec], b: &[u8]) {
|
|
gen_matrix(a, b, false);
|
|
}
|
|
|
|
fn gen_at(a: &mut [Polyvec], b: &[u8]) {
|
|
gen_matrix(a, b, true);
|
|
}
|
|
|
|
// Name: gen_matrix
|
|
//
|
|
// Description: Deterministically generate matrix A (or the transpose of A)
|
|
// from a seed. Entries of the matrix are polynomials that look
|
|
// uniformly random. Performs rejection sampling on output of
|
|
// a XOF
|
|
//
|
|
// Arguments: - Polyvec a: ouptput matrix A
|
|
// - const [u8] seed: input seed
|
|
// - bool transposed: boolean deciding whether A or A^T is generated
|
|
fn gen_matrix(a: &mut [Polyvec], seed: &[u8], transposed: bool) {
|
|
let mut ctr;
|
|
// 530 is expected number of required bytes
|
|
const GEN_MATRIX_NBLOCKS: usize = (12 * KYBER_N / 8 * (1 << 12) / KYBER_Q + XOF_BLOCKBYTES) / XOF_BLOCKBYTES;
|
|
const BUFLEN: usize = GEN_MATRIX_NBLOCKS * XOF_BLOCKBYTES;
|
|
let mut buf = [0u8; BUFLEN + 2];
|
|
let mut off: usize;
|
|
let mut state = XofState::new();
|
|
|
|
for i in 0..KYBER_K {
|
|
for j in 0..KYBER_K {
|
|
if transposed {
|
|
xof_absorb(&mut state, seed, i as u8, j as u8);
|
|
} else {
|
|
xof_absorb(&mut state, seed, j as u8, i as u8);
|
|
}
|
|
xof_squeezeblocks(&mut buf, GEN_MATRIX_NBLOCKS, &mut state);
|
|
ctr = rej_uniform(&mut a[i].vec[j].coeffs, KYBER_N, &buf, BUFLEN);
|
|
|
|
while ctr < KYBER_N {
|
|
off = BUFLEN % 3;
|
|
for k in 0..off {
|
|
buf[k] = buf[BUFLEN - off + k];
|
|
}
|
|
xof_squeezeblocks(&mut buf[off..], 1, &mut state);
|
|
ctr += rej_uniform(&mut a[i].vec[j].coeffs[ctr..], KYBER_N - ctr, &buf, BUFLEN);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Name: indcpa_keypair
|
|
//
|
|
// Description: Generates public and private key for the CPA-secure
|
|
// public-key encryption scheme underlying Kyber
|
|
//
|
|
// Arguments: - [u8] pk: output public key (length KYBER_INDCPA_PUBLICKEYBYTES)
|
|
// - [u8] sk: output private key (length KYBER_INDCPA_SECRETKEYBYTES)
|
|
pub fn indcpa_keypair<R>(pk: &mut [u8], sk: &mut [u8], _seed: Option<(&[u8], &[u8])>, _rng: &mut R)
|
|
where
|
|
R: CryptoRng + RngCore,
|
|
{
|
|
let mut a = [Polyvec::new(); KYBER_K];
|
|
let (mut e, mut pkpv, mut skpv) = (Polyvec::new(), Polyvec::new(), Polyvec::new());
|
|
let mut nonce = 0u8;
|
|
let mut buf = [0u8; 2 * KYBER_SYMBYTES];
|
|
let mut randbuf = [0u8; 2 * KYBER_SYMBYTES];
|
|
|
|
#[cfg(not(feature = "KATs"))]
|
|
randombytes(&mut randbuf, KYBER_SYMBYTES, _rng);
|
|
|
|
// Use rng seed for test vectors
|
|
#[cfg(feature = "KATs")]
|
|
randbuf[..KYBER_SYMBYTES].copy_from_slice(&_seed.expect("KAT feature only for testing").0);
|
|
|
|
hash_g(&mut buf, &randbuf, KYBER_SYMBYTES);
|
|
|
|
let (publicseed, noiseseed) = buf.split_at(KYBER_SYMBYTES);
|
|
gen_a(&mut a, publicseed);
|
|
|
|
for i in 0..KYBER_K {
|
|
poly_getnoise_eta1(&mut skpv.vec[i], noiseseed, nonce);
|
|
nonce += 1;
|
|
}
|
|
for i in 0..KYBER_K {
|
|
poly_getnoise_eta1(&mut e.vec[i], noiseseed, nonce);
|
|
nonce += 1;
|
|
}
|
|
|
|
polyvec_ntt(&mut skpv);
|
|
polyvec_ntt(&mut e);
|
|
|
|
// matrix-vector multiplication
|
|
for i in 0..KYBER_K {
|
|
polyvec_basemul_acc_montgomery(&mut pkpv.vec[i], &a[i], &skpv);
|
|
poly_frommont(&mut pkpv.vec[i]);
|
|
}
|
|
polyvec_add(&mut pkpv, &e);
|
|
polyvec_reduce(&mut pkpv);
|
|
|
|
pack_sk(sk, &mut skpv);
|
|
pack_pk(pk, &mut pkpv, publicseed);
|
|
}
|
|
|
|
// Name: indcpa_enc
|
|
//
|
|
// Description: Encryption function of the CPA-secure
|
|
// public-key encryption scheme underlying Kyber.
|
|
//
|
|
// Arguments: - [u8] c: output ciphertext (length KYBER_INDCPA_BYTES)
|
|
// - const [u8] m: input message (length KYBER_INDCPA_MSGBYTES)
|
|
// - const [u8] pk: input public key (length KYBER_INDCPA_PUBLICKEYBYTES)
|
|
// - const [u8] coin: input random coins used as seed (length KYBER_SYMBYTES)
|
|
// to deterministically generate all randomness
|
|
pub fn indcpa_enc(c: &mut [u8], m: &[u8], pk: &[u8], coins: &[u8]) {
|
|
let mut at = [Polyvec::new(); KYBER_K];
|
|
let (mut sp, mut pkpv, mut ep, mut b) = (Polyvec::new(), Polyvec::new(), Polyvec::new(), Polyvec::new());
|
|
let (mut v, mut k, mut epp) = (Poly::new(), Poly::new(), Poly::new());
|
|
let mut seed = [0u8; KYBER_SYMBYTES];
|
|
let mut nonce = 0u8;
|
|
|
|
unpack_pk(&mut pkpv, &mut seed, pk);
|
|
poly_frommsg(&mut k, m);
|
|
gen_at(&mut at, &seed);
|
|
|
|
for i in 0..KYBER_K {
|
|
poly_getnoise_eta1(&mut sp.vec[i], coins, nonce);
|
|
nonce += 1;
|
|
}
|
|
for i in 0..KYBER_K {
|
|
poly_getnoise_eta2(&mut ep.vec[i], coins, nonce);
|
|
nonce += 1;
|
|
}
|
|
poly_getnoise_eta2(&mut epp, coins, nonce);
|
|
|
|
polyvec_ntt(&mut sp);
|
|
|
|
// matrix-vector multiplication
|
|
for i in 0..KYBER_K {
|
|
polyvec_basemul_acc_montgomery(&mut b.vec[i], &at[i], &sp);
|
|
}
|
|
|
|
polyvec_basemul_acc_montgomery(&mut v, &pkpv, &sp);
|
|
polyvec_invntt_tomont(&mut b);
|
|
poly_invntt_tomont(&mut v);
|
|
|
|
polyvec_add(&mut b, &ep);
|
|
poly_add(&mut v, &epp);
|
|
poly_add(&mut v, &k);
|
|
polyvec_reduce(&mut b);
|
|
poly_reduce(&mut v);
|
|
|
|
pack_ciphertext(c, &mut b, v);
|
|
}
|
|
|
|
// Name: indcpa_dec
|
|
//
|
|
// Description: Decryption function of the CPA-secure
|
|
// public-key encryption scheme underlying Kyber.
|
|
//
|
|
// Arguments: - [u8] m: output decrypted message (of length KYBER_INDCPA_MSGBYTES)
|
|
// - const [u8] c: input ciphertext (of length KYBER_INDCPA_BYTES)
|
|
// - const [u8] sk: input secret key (of length KYBER_INDCPA_SECRETKEYBYTES)
|
|
pub fn indcpa_dec(m: &mut [u8], c: &[u8], sk: &[u8]) {
|
|
let (mut b, mut skpv) = (Polyvec::new(), Polyvec::new());
|
|
let (mut v, mut mp) = (Poly::new(), Poly::new());
|
|
|
|
unpack_ciphertext(&mut b, &mut v, c);
|
|
unpack_sk(&mut skpv, sk);
|
|
|
|
polyvec_ntt(&mut b);
|
|
polyvec_basemul_acc_montgomery(&mut mp, &skpv, &b);
|
|
poly_invntt_tomont(&mut mp);
|
|
|
|
poly_sub(&mut mp, &v);
|
|
poly_reduce(&mut mp);
|
|
|
|
poly_tomsg(m, mp);
|
|
}
|