diff --git a/Cargo.toml b/Cargo.toml index 06bc01055..469ed6636 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,8 @@ quickcheck = "1" quickcheck_macros = "1" serde_json = { version = "1.0" } serde = { version = "1.0", features = ["derive"] } -hex = "0.4" +hex = { version = "0.4.3", features = ["serde"] } +pqcrypto-kyber = { version = "0.7.6", default-features = false } # Benchmarking "RustCrypto" chacha20poly1305 = "0.10" @@ -70,6 +71,10 @@ harness = false name = "drbg" harness = false +[[bench]] +name = "kyber768" +harness = false + [features] hacspec = [] # TODO: #7 Use specs instead of efficient implementations rand = [] diff --git a/benches/kyber768.rs b/benches/kyber768.rs new file mode 100644 index 000000000..72b16c7af --- /dev/null +++ b/benches/kyber768.rs @@ -0,0 +1,101 @@ +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; + +use libcrux::kem::Algorithm; +use libcrux::drbg::Drbg; +use libcrux::digest; + +pub fn comparisons_key_generation(c: &mut Criterion) { + let mut drbg = Drbg::new(digest::Algorithm::Sha256).unwrap(); + let mut group = c.benchmark_group("Kyber768 Key Generation"); + + group.bench_function("libcrux reference implementation", |b| { + b.iter( + || { + let (_secret_key, _public_key) = libcrux::kem::key_gen(Algorithm::Kyber768, &mut drbg).unwrap(); + } + ) + }); + + group.bench_function("pqclean reference implementation", |b| { + b.iter(|| { + let (_public_key, _secret_key) = pqcrypto_kyber::kyber768::keypair(); + }) + }); +} + +pub fn comparisons_encapsulation(c: &mut Criterion) { + let mut group = c.benchmark_group("Kyber768 Encapsulation"); + + group.bench_function("libcrux reference implementation", |b| { + b.iter_batched( + || { + let mut drbg = Drbg::new(digest::Algorithm::Sha256).unwrap(); + let (_secret_key, public_key) = libcrux::kem::key_gen(Algorithm::Kyber768, &mut drbg).unwrap(); + + (drbg, public_key) + }, + |(mut rng, public_key)| { + let (_shared_secret, _ciphertext) = + libcrux::kem::encapsulate(Algorithm::Kyber768, &public_key, &mut rng).unwrap(); + }, + BatchSize::SmallInput, + ) + }); + + group.bench_function("pqclean reference implementation", |b| { + b.iter_batched( + || { + let (public_key, _secret_key) = pqcrypto_kyber::kyber768::keypair(); + + public_key + }, + |public_key| { + let (_shared_secret, _ciphertext) = pqcrypto_kyber::kyber768::encapsulate(&public_key); + }, + BatchSize::SmallInput, + ) + }); +} + +pub fn comparisons_decapsulation(c: &mut Criterion) { + let mut group = c.benchmark_group("Kyber768 Decapsulation"); + + group.bench_function("libcrux specification", |b| { + b.iter_batched( + || { + let mut drbg = Drbg::new(digest::Algorithm::Sha256).unwrap(); + let (secret_key, public_key) = libcrux::kem::key_gen(Algorithm::Kyber768, &mut drbg).unwrap(); + let (_shared_secret, ciphertext) = libcrux::kem::encapsulate(Algorithm::Kyber768, &public_key, &mut drbg).unwrap(); + (secret_key, ciphertext) + }, + |(secret_key, ciphertext)| { + let _shared_secret = libcrux::kem::decapsulate(Algorithm::Kyber768, &ciphertext, &secret_key); + }, + BatchSize::SmallInput, + ) + }); + + group.bench_function("pqclean reference implementation", |b| { + b.iter_batched( + || { + let (public_key, secret_key) = pqcrypto_kyber::kyber768::keypair(); + let (_shared_secret, ciphertext) = pqcrypto_kyber::kyber768::encapsulate(&public_key); + + (ciphertext, secret_key) + }, + |(ciphertext, secret_key)| { + let _shared_secret = pqcrypto_kyber::kyber768::decapsulate(&ciphertext, &secret_key); + }, + BatchSize::SmallInput, + ) + }); +} + +pub fn comparisons(c: &mut Criterion) { + comparisons_key_generation(c); + comparisons_encapsulation(c); + comparisons_decapsulation(c); +} + +criterion_group!(benches, comparisons); +criterion_main!(benches); diff --git a/specs/kyber/src/lib.rs b/specs/kyber/src/lib.rs index f45460052..5e3a7893f 100644 --- a/specs/kyber/src/lib.rs +++ b/specs/kyber/src/lib.rs @@ -28,9 +28,7 @@ pub type PublicKey = [u8; KYBER768_PUBLIC_KEY_SIZE]; pub type PrivateKey = [u8; KYBER768_SECRET_KEY_SIZE]; pub type Ciphertext = [u8; KYBER768_CIPHERTEXT_SIZE]; -pub type Enc = [u8; KYBER768_SHARED_SECRET_SIZE]; pub type SharedSecret = [u8; KYBER768_SHARED_SECRET_SIZE]; -pub type Randomness = [u8; KYBER768_KEY_GENERATION_SEED_SIZE]; #[derive(Debug)] pub struct BadRejectionSamplingRandomnessError; @@ -105,7 +103,7 @@ pub fn generate_keypair( pub fn encapsulate( public_key: PublicKey, randomness: [u8; KYBER768_SHARED_SECRET_SIZE], -) -> Result<(Ciphertext, Enc), BadRejectionSamplingRandomnessError> { +) -> Result<(Ciphertext, SharedSecret), BadRejectionSamplingRandomnessError> { let randomness_hashed = H(&randomness); let to_hash: [u8; 2 * H_DIGEST_SIZE] = randomness_hashed.push(&H(&public_key)); diff --git a/src/drbg.rs b/src/drbg.rs index a9637ef1b..c07845ff1 100644 --- a/src/drbg.rs +++ b/src/drbg.rs @@ -171,7 +171,13 @@ impl Drbg { /// Implementation of the [`RngCore`] trait for the [`Drbg`]. impl RngCore for Drbg { fn next_u32(&mut self) -> u32 { - todo!() + let mut bytes : [u8; 4] = [0; 4]; + self.generate(&mut bytes).unwrap(); + + (bytes[0] as u32) | + (bytes[1] as u32) << 8 | + (bytes[2] as u32) << 16 | + (bytes[3] as u32) << 24 } fn next_u64(&mut self) -> u64 { diff --git a/src/hpke/kem.rs b/src/hpke/kem.rs index da8a33552..a8015477b 100644 --- a/src/hpke/kem.rs +++ b/src/hpke/kem.rs @@ -243,7 +243,7 @@ fn shared_secret_from_dh(alg: KEM, mut secret: Vec) -> Vec { /// [`ValidationError`](`HpkeError::ValidationError`) as described in /// [validation](#validation-of-inputs-and-outputs). pub fn DH(alg: KEM, sk: &PrivateKeyIn, pk: &PublicKeyIn) -> Result { - match crate::ecdh::derive(kem_to_named_group(alg).into(), pk, sk) { + match crate::ecdh::derive(kem_to_named_group(alg).try_into().unwrap(), pk, sk) { Ok(secret) => HpkeBytesResult::Ok(shared_secret_from_dh(alg, secret)), Err(_) => HpkeBytesResult::Err(HpkeError::ValidationError), } @@ -444,7 +444,7 @@ pub fn DeriveKeyPair(alg: KEM, ikm: &InputKeyMaterial) -> Result for ecdh::Algorithm { - fn from(value: Algorithm) -> Self { +impl TryFrom for ecdh::Algorithm { + type Error = &'static str; + + fn try_from(value: Algorithm) -> Result { match value { - Algorithm::X25519 => ecdh::Algorithm::X25519, - Algorithm::X448 => ecdh::Algorithm::X448, - Algorithm::Secp256r1 => ecdh::Algorithm::P256, - Algorithm::Secp384r1 => ecdh::Algorithm::P384, - Algorithm::Secp521r1 => ecdh::Algorithm::P521, + Algorithm::X25519 => Ok(ecdh::Algorithm::X25519), + Algorithm::X448 => Ok(ecdh::Algorithm::X448), + Algorithm::Secp256r1 => Ok(ecdh::Algorithm::P256), + Algorithm::Secp384r1 => Ok(ecdh::Algorithm::P384), + Algorithm::Secp521r1 => Ok(ecdh::Algorithm::P521), + _ => Err("provided algorithm is not an ECDH algorithm"), } } } @@ -58,10 +73,11 @@ pub enum PublicKey { } /// Compute the public key for a private key of the given [`Algorithm`]. +/// Applicable only to X25519 and secp256r1. pub fn secret_to_public(alg: Algorithm, sk: impl AsRef<[u8]>) -> Result, Error> { match alg { Algorithm::X25519 | Algorithm::Secp256r1 => { - ecdh::secret_to_public(alg.into(), sk.as_ref()).map_err(|e| e.into()) + ecdh::secret_to_public(alg.try_into().unwrap(), sk.as_ref()).map_err(|e| e.into()) } _ => Err(Error::UnsupportedAlgorithm), } @@ -78,7 +94,18 @@ pub fn key_gen( ) -> Result<(Vec, Vec), Error> { match alg { Algorithm::X25519 | Algorithm::Secp256r1 => { - ecdh::key_gen(alg.into(), rng).map_err(|e| e.into()) + ecdh::key_gen(alg.try_into().unwrap(), rng).map_err(|e| e.into()) + } + + Algorithm::Kyber768 => { + let mut seed = [0; kyber768::KEY_GENERATION_SEED_SIZE]; + rng.try_fill_bytes(&mut seed).map_err(|_| Error::KeyGen)?; + + if let Ok((pk, sk)) = kyber768::generate_keypair(seed) { + Ok((sk.into(), pk.into())) + } else { + Err(Error::KeyGen) + } } _ => Err(Error::UnsupportedAlgorithm), } @@ -93,9 +120,23 @@ pub fn encapsulate( let (new_sk, new_pk) = key_gen(alg, rng)?; match alg { Algorithm::X25519 | Algorithm::Secp256r1 => { - let gxy = ecdh::derive(alg.into(), pk, &new_sk)?; + let gxy = ecdh::derive(alg.try_into().unwrap(), pk, &new_sk)?; Ok((gxy, new_pk)) } + + Algorithm::Kyber768 => { + let mut seed = [0; kyber768::SHARED_SECRET_SIZE]; + rng.try_fill_bytes(&mut seed).map_err(|_| Error::KeyGen)?; + + // TODO: Don't unwrap() here. See + // https://github.com/cryspen/libcrux/issues/35 + if let Ok((ct, ss)) = kyber768::encapsulate(pk.try_into().unwrap(), seed) { + Ok((ss.into(), ct.into())) + } else { + Err(Error::Encapsulate) + } + } + _ => Err(Error::UnsupportedAlgorithm), } } @@ -104,9 +145,16 @@ pub fn encapsulate( pub fn decapsulate(alg: Algorithm, ct: &[u8], sk: &[u8]) -> Result, Error> { match alg { Algorithm::X25519 | Algorithm::Secp256r1 => { - let gxy = ecdh::derive(alg.into(), ct, sk)?; + let gxy = ecdh::derive(alg.try_into().unwrap(), ct, sk)?; Ok(gxy) } + Algorithm::Kyber768 => { + // TODO: Don't unwrap() here. See + // https://github.com/cryspen/libcrux/issues/35 + let ss = kyber768::decapsulate(sk.try_into().unwrap(), ct.try_into().unwrap()); + + Ok(ss.into()) + } _ => Err(Error::UnsupportedAlgorithm), } } diff --git a/src/kem/kyber768.rs b/src/kem/kyber768.rs new file mode 100644 index 000000000..e1db45add --- /dev/null +++ b/src/kem/kyber768.rs @@ -0,0 +1,106 @@ +mod compress; +mod ind_cpa; +mod ntt; +mod parameters; +mod sampling; +mod serialize; +mod utils; + +use utils::{ArrayConversion, UpdatingArray2}; + +use parameters::{ + hash_functions::{G, H, H_DIGEST_SIZE, KDF}, + CPA_PKE_KEY_GENERATION_SEED_SIZE, CPA_PKE_MESSAGE_SIZE, CPA_PKE_PUBLIC_KEY_SIZE, + CPA_PKE_SECRET_KEY_SIZE, +}; + +pub const SHARED_SECRET_SIZE: usize = CPA_PKE_MESSAGE_SIZE; + +pub const KEY_GENERATION_SEED_SIZE: usize = CPA_PKE_KEY_GENERATION_SEED_SIZE + SHARED_SECRET_SIZE; + +pub const PUBLIC_KEY_SIZE: usize = CPA_PKE_PUBLIC_KEY_SIZE; + +pub const SECRET_KEY_SIZE: usize = + CPA_PKE_SECRET_KEY_SIZE + CPA_PKE_PUBLIC_KEY_SIZE + H_DIGEST_SIZE + SHARED_SECRET_SIZE; + +pub const CIPHERTEXT_SIZE: usize = parameters::CPA_PKE_CIPHERTEXT_SIZE; + +pub type Kyber768PublicKey = [u8; PUBLIC_KEY_SIZE]; +pub type Kyber768PrivateKey = [u8; SECRET_KEY_SIZE]; + +pub type Kyber768Ciphertext = [u8; CIPHERTEXT_SIZE]; +pub type Kyber768SharedSecret = [u8; SHARED_SECRET_SIZE]; + +#[derive(Debug)] +pub struct BadRejectionSamplingRandomnessError; + +pub fn generate_keypair( + randomness: [u8; KEY_GENERATION_SEED_SIZE], +) -> Result<(Kyber768PublicKey, Kyber768PrivateKey), BadRejectionSamplingRandomnessError> { + let ind_cpa_keypair_randomness = &randomness[0..parameters::CPA_PKE_KEY_GENERATION_SEED_SIZE]; + let implicit_rejection_value = &randomness[parameters::CPA_PKE_KEY_GENERATION_SEED_SIZE..]; + + let ind_cpa_key_pair = ind_cpa::generate_keypair(&ind_cpa_keypair_randomness.as_array())?; + + let secret_key_serialized = + ind_cpa_key_pair.serialize_secret_key(&implicit_rejection_value.as_array()); + + Ok((ind_cpa_key_pair.pk(), secret_key_serialized)) +} + +pub fn encapsulate( + public_key: Kyber768PublicKey, + randomness: [u8; SHARED_SECRET_SIZE], +) -> Result<(Kyber768Ciphertext, Kyber768SharedSecret), BadRejectionSamplingRandomnessError> { + let randomness_hashed = H(&randomness); + + let to_hash: [u8; 2 * H_DIGEST_SIZE] = randomness_hashed.push(&H(&public_key)); + + let hashed = G(&to_hash); + let (k_not, pseudorandomness) = hashed.split_at(32); + + let ciphertext = + ind_cpa::encrypt(&public_key, randomness_hashed, &pseudorandomness.as_array())?; + + let to_hash: [u8; 2 * H_DIGEST_SIZE] = k_not.push(&H(&ciphertext)); + let shared_secret: Kyber768SharedSecret = KDF(&to_hash); + + Ok((ciphertext, shared_secret)) +} + +pub fn decapsulate( + secret_key: Kyber768PrivateKey, + ciphertext: Kyber768Ciphertext, +) -> Kyber768SharedSecret { + let (ind_cpa_secret_key, secret_key) = secret_key.split_at(CPA_PKE_SECRET_KEY_SIZE); + let (ind_cpa_public_key, secret_key) = secret_key.split_at(CPA_PKE_PUBLIC_KEY_SIZE); + let (ind_cpa_public_key_hash, implicit_rejection_value) = secret_key.split_at(H_DIGEST_SIZE); + + let decrypted = ind_cpa::decrypt(&ind_cpa_secret_key.as_array(), &ciphertext); + + let to_hash: [u8; CPA_PKE_MESSAGE_SIZE + H_DIGEST_SIZE] = + decrypted.push(ind_cpa_public_key_hash); + + let hashed = G(&to_hash); + let (k_not, pseudorandomness) = hashed.split_at(32); + + let reencrypted_ciphertext = ind_cpa::encrypt( + &ind_cpa_public_key.as_array(), + decrypted, + &pseudorandomness.as_array(), + ); + + let to_hash = if let Ok(reencrypted) = reencrypted_ciphertext { + if ciphertext == reencrypted { + k_not + } else { + implicit_rejection_value + } + } else { + implicit_rejection_value + }; + assert!(to_hash.len() == 32); + let to_hash: [u8; 32 + H_DIGEST_SIZE] = to_hash.push(&H(&ciphertext)); + + KDF(&to_hash) +} diff --git a/src/kem/kyber768/compress.rs b/src/kem/kyber768/compress.rs new file mode 100644 index 000000000..0f06fe19b --- /dev/null +++ b/src/kem/kyber768/compress.rs @@ -0,0 +1,48 @@ +use crate::kem::kyber768::parameters::{self, KyberFieldElement, KyberPolynomialRingElement}; + +pub fn compress( + re: KyberPolynomialRingElement, + bits_per_compressed_coefficient: usize, +) -> KyberPolynomialRingElement { + KyberPolynomialRingElement::new( + re.coefficients() + .map(|coefficient| compress_q(coefficient, bits_per_compressed_coefficient)), + ) +} + +pub fn decompress( + re: KyberPolynomialRingElement, + bits_per_compressed_coefficient: usize, +) -> KyberPolynomialRingElement { + KyberPolynomialRingElement::new( + re.coefficients() + .map(|coefficient| decompress_q(coefficient, bits_per_compressed_coefficient)), + ) +} + +fn compress_q(fe: KyberFieldElement, to_bit_size: usize) -> KyberFieldElement { + assert!(to_bit_size <= parameters::BITS_PER_COEFFICIENT); + + let two_pow_bit_size = 2u32.pow(to_bit_size.try_into().unwrap_or_else(|_| { + panic!( + "Conversion should work since to_bit_size is never greater than {}.", + parameters::BITS_PER_COEFFICIENT + ) + })); + + let compressed = ((u32::from(fe.value) * 2 * two_pow_bit_size) + + u32::from(KyberFieldElement::MODULUS)) + / u32::from(2 * KyberFieldElement::MODULUS); + + (compressed % two_pow_bit_size).into() +} + +fn decompress_q(fe: KyberFieldElement, to_bit_size: usize) -> KyberFieldElement { + assert!(to_bit_size <= parameters::BITS_PER_COEFFICIENT); + + let decompressed = (2 * u32::from(fe.value) * u32::from(KyberFieldElement::MODULUS) + + (1 << to_bit_size)) + >> (to_bit_size + 1); + + decompressed.into() +} diff --git a/src/kem/kyber768/ind_cpa.rs b/src/kem/kyber768/ind_cpa.rs new file mode 100644 index 000000000..a09e59781 --- /dev/null +++ b/src/kem/kyber768/ind_cpa.rs @@ -0,0 +1,309 @@ +use crate::kem::kyber768::utils::{ + ArrayConversion, ArrayPadding, PanickingIntegerCasts, UpdatableArray, UpdatingArray, VecUpdate, +}; + +use crate::kem::kyber768::{ + compress::{compress, decompress}, + ntt::{ + kyber_polynomial_ring_element_mod::{invert_ntt, ntt_representation}, + *, + }, + parameters::{ + hash_functions::{G, H, PRF, XOF}, + KyberPolynomialRingElement, BITS_PER_RING_ELEMENT, COEFFICIENTS_IN_RING_ELEMENT, + CPA_PKE_CIPHERTEXT_SIZE, CPA_PKE_KEY_GENERATION_SEED_SIZE, CPA_PKE_MESSAGE_SIZE, + CPA_PKE_PUBLIC_KEY_SIZE, CPA_PKE_SECRET_KEY_SIZE, CPA_SERIALIZED_KEY_LEN, RANK, + REJECTION_SAMPLING_SEED_SIZE, T_AS_NTT_ENCODED_SIZE, VECTOR_U_COMPRESSION_FACTOR, + VECTOR_U_SIZE, VECTOR_V_COMPRESSION_FACTOR, + }, + sampling::{sample_from_binomial_distribution, sample_from_uniform_distribution}, + serialize::{deserialize_little_endian, serialize_little_endian}, + BadRejectionSamplingRandomnessError, +}; + +pub type CiphertextCpa = [u8; CPA_PKE_CIPHERTEXT_SIZE]; + +/// A Kyber key pair +pub struct KeyPair { + sk: [u8; CPA_PKE_SECRET_KEY_SIZE], + pk: [u8; CPA_PKE_PUBLIC_KEY_SIZE], +} + +impl KeyPair { + /// Creates a new [`KeyPair`]. + pub fn new(sk: [u8; CPA_PKE_SECRET_KEY_SIZE], pk: [u8; CPA_PKE_PUBLIC_KEY_SIZE]) -> Self { + Self { sk, pk } + } + + pub fn serialize_secret_key( + &self, + implicit_rejection_value: &[u8; CPA_PKE_MESSAGE_SIZE], + ) -> [u8; CPA_SERIALIZED_KEY_LEN] { + UpdatableArray::new([0u8; CPA_SERIALIZED_KEY_LEN]) + .push(&self.sk) + .push(&self.pk) + .push(&H(&self.pk)) + .push(implicit_rejection_value) + .into() + } + + pub fn pk(&self) -> [u8; 1184] { + self.pk + } +} + +fn encode_12(input: [KyberPolynomialRingElement; RANK]) -> Vec { + let mut out = Vec::new(); + for re in input.into_iter() { + out.extend_from_slice(&serialize_little_endian(re, 12)); + } + + out +} + +#[allow(non_snake_case)] +pub(crate) fn generate_keypair( + key_generation_seed: &[u8; CPA_PKE_KEY_GENERATION_SEED_SIZE], +) -> Result { + let mut prf_input: [u8; 33] = [0; 33]; + + let mut secret_as_ntt = [KyberPolynomialRingElement::ZERO; RANK]; + let mut error_as_ntt = [KyberPolynomialRingElement::ZERO; RANK]; + + // N := 0 + let mut domain_separator: u8 = 0; + + // (ρ,σ) := G(d) + let hashed = G(key_generation_seed); + let (seed_for_A, seed_for_secret_and_error) = hashed.split_at(32); + + let A_transpose = parse_a(seed_for_A.into_padded_array(), true)?; + + // for i from 0 to k−1 do + // s[i] := CBD_{η1}(PRF(σ, N)) + // N := N + 1 + // end for + // sˆ := NTT(s) + prf_input[0..seed_for_secret_and_error.len()].copy_from_slice(seed_for_secret_and_error); + + for i in 0..secret_as_ntt.len() { + prf_input[32] = domain_separator; + domain_separator += 1; + + // 2 sampling coins * 64 + let prf_output: [u8; 128] = PRF(&prf_input); + + let secret = sample_from_binomial_distribution(2, &prf_output[..]); + secret_as_ntt[i] = ntt_representation(secret); + } + + // for i from 0 to k−1 do + // e[i] := CBD_{η1}(PRF(σ, N)) + // N := N + 1 + // end for + // eˆ := NTT(e) + for i in 0..error_as_ntt.len() { + prf_input[32] = domain_separator; + domain_separator += 1; + + // 2 sampling coins * 64 + let prf_output: [u8; 128] = PRF(&prf_input); + + let error = sample_from_binomial_distribution(2, &prf_output[..]); + error_as_ntt[i] = ntt_representation(error); + } + + // tˆ := Aˆ ◦ sˆ + eˆ + let mut t_as_ntt = multiply_matrix_by_column(&A_transpose, &secret_as_ntt); + for i in 0..t_as_ntt.len() { + t_as_ntt[i] = t_as_ntt[i] + error_as_ntt[i]; + } + + // pk := (Encode_12(tˆ mod^{+}q) || ρ) + let public_key_serialized = encode_12(t_as_ntt).concat(seed_for_A); + + // sk := Encode_12(sˆ mod^{+}q) + let secret_key_serialized = encode_12(secret_as_ntt); + + Ok(KeyPair::new( + secret_key_serialized.into_array(), + public_key_serialized.into_array(), + )) +} + +#[inline(always)] +fn parse_a( + mut seed: [u8; 34], + transpose: bool, +) -> Result<[[KyberPolynomialRingElement; RANK]; RANK], BadRejectionSamplingRandomnessError> { + let mut a_transpose = [[KyberPolynomialRingElement::ZERO; RANK]; RANK]; + + for i in 0..RANK { + for j in 0..RANK { + seed[32] = i.as_u8(); + seed[33] = j.as_u8(); + + let xof_bytes: [u8; REJECTION_SAMPLING_SEED_SIZE] = XOF(&seed); + + // A[i][j] = A_transpose[j][i] + if transpose { + a_transpose[j][i] = sample_from_uniform_distribution(xof_bytes)?; + } else { + a_transpose[i][j] = sample_from_uniform_distribution(xof_bytes)?; + } + } + } + Ok(a_transpose) +} + +#[inline(always)] +fn cbd(mut prf_input: [u8; 33]) -> ([KyberPolynomialRingElement; RANK], u8) { + let mut domain_separator = 0; + let mut r_as_ntt = [KyberPolynomialRingElement::ZERO; RANK]; + for i in 0..r_as_ntt.len() { + prf_input[32] = domain_separator; + domain_separator += 1; + + // 2 sampling coins * 64 + let prf_output: [u8; 128] = PRF(&prf_input); + + let r = sample_from_binomial_distribution(2, &prf_output); + r_as_ntt[i] = ntt_representation(r); + } + (r_as_ntt, domain_separator) +} + +fn encode_and_compress_u(input: [KyberPolynomialRingElement; RANK]) -> Vec { + let mut out = Vec::new(); + for re in input.into_iter() { + out.extend_from_slice(&serialize_little_endian( + compress(re, VECTOR_U_COMPRESSION_FACTOR), + VECTOR_U_COMPRESSION_FACTOR, + )); + } + + out +} + +#[allow(non_snake_case)] +pub(crate) fn encrypt( + public_key: &[u8; CPA_PKE_PUBLIC_KEY_SIZE], + message: [u8; CPA_PKE_MESSAGE_SIZE], + randomness: &[u8; 32], +) -> Result { + // tˆ := Decode_12(pk) + let mut t_as_ntt_ring_element_bytes = public_key.chunks(BITS_PER_RING_ELEMENT / 8); + let mut t_as_ntt = [KyberPolynomialRingElement::ZERO; RANK]; + for i in 0..t_as_ntt.len() { + t_as_ntt[i] = deserialize_little_endian( + 12, + t_as_ntt_ring_element_bytes.next().expect( + "t_as_ntt_ring_element_bytes should have enough bytes to deserialize to t_as_ntt", + ), + ); + } + + // ρ := pk + 12·k·n / 8 + // for i from 0 to k−1 do + // for j from 0 to k − 1 do + // AˆT[i][j] := Parse(XOF(ρ, i, j)) + // end for + // end for + let seed = &public_key[T_AS_NTT_ENCODED_SIZE..]; + let A_transpose = parse_a(seed.into_padded_array(), false)?; + + // for i from 0 to k−1 do + // r[i] := CBD{η1}(PRF(r, N)) + // N := N + 1 + // end for + // rˆ := NTT(r) + let mut prf_input: [u8; 33] = randomness.into_padded_array(); + let (r_as_ntt, mut domain_separator) = cbd(prf_input); + + // for i from 0 to k−1 do + // e1[i] := CBD_{η2}(PRF(r,N)) + // N := N + 1 + // end for + let mut error_1 = [KyberPolynomialRingElement::ZERO; RANK]; + for i in 0..error_1.len() { + prf_input[32] = domain_separator; + domain_separator += 1; + + // 2 sampling coins * 64 + let prf_output: [u8; 128] = PRF(&prf_input); + error_1[i] = sample_from_binomial_distribution(2, &prf_output); + } + + // e_2 := CBD{η2}(PRF(r, N)) + prf_input[32] = domain_separator; + // 2 sampling coins * 64 + let prf_output: [u8; 128] = PRF(&prf_input); + let error_2 = sample_from_binomial_distribution(2, &prf_output); + + // u := NTT^{-1}(AˆT ◦ rˆ) + e_1 + let mut u = multiply_matrix_by_column(&A_transpose, &r_as_ntt).map(|r| invert_ntt(r)); + for i in 0..u.len() { + u[i] = u[i] + error_1[i]; + } + + // v := NTT^{−1}(tˆT ◦ rˆ) + e_2 + Decompress_q(Decode_1(m),1) + let message_as_ring_element = deserialize_little_endian(1, &message); + let v = invert_ntt(multiply_row_by_column(&t_as_ntt, &r_as_ntt)) + + error_2 + + decompress(message_as_ring_element, 1); + + // c_1 := Encode_{du}(Compress_q(u,d_u)) + let c1 = encode_and_compress_u(u); + + // c_2 := Encode_{dv}(Compress_q(v,d_v)) + let c2 = serialize_little_endian( + compress(v, VECTOR_V_COMPRESSION_FACTOR), + VECTOR_V_COMPRESSION_FACTOR, + ); + + let ciphertext = c1 + .into_iter() + .chain(c2.into_iter()) + .collect::>() + .as_array(); + + Ok(ciphertext) +} + +#[allow(non_snake_case)] +pub(crate) fn decrypt( + secret_key: &[u8; CPA_PKE_SECRET_KEY_SIZE], + ciphertext: &[u8; CPA_PKE_CIPHERTEXT_SIZE], +) -> [u8; CPA_PKE_MESSAGE_SIZE] { + let mut u_as_ntt = [KyberPolynomialRingElement::ZERO; RANK]; + let mut secret_as_ntt = [KyberPolynomialRingElement::ZERO; RANK]; + + // u := Decompress_q(Decode_{d_u}(c), d_u) + for (i, u_bytes) in + (0..u_as_ntt.len()).zip(ciphertext.chunks((COEFFICIENTS_IN_RING_ELEMENT * 10) / 8)) + { + let u = deserialize_little_endian(10, u_bytes); + u_as_ntt[i] = ntt_representation(decompress(u, 10)); + } + + // v := Decompress_q(Decode_{d_v}(c + d_u·k·n / 8), d_v) + let v = decompress( + deserialize_little_endian(VECTOR_V_COMPRESSION_FACTOR, &ciphertext[VECTOR_U_SIZE..]), + VECTOR_V_COMPRESSION_FACTOR, + ); + + // sˆ := Decode_12(sk) + let mut secret_as_ntt_ring_element_bytes = secret_key.chunks(BITS_PER_RING_ELEMENT / 8); + for i in 0..secret_as_ntt.len() { + secret_as_ntt[i] = deserialize_little_endian( + 12, + secret_as_ntt_ring_element_bytes.next().expect("secret_as_ntt_ring_element_bytes should have enough bytes to deserialize to secret_as_ntt"), + ); + } + + // m := Encode_1(Compress_q(v − NTT^{−1}(sˆT ◦ NTT(u)) , 1)) + let message = v - invert_ntt(multiply_row_by_column(&secret_as_ntt, &u_as_ntt)); + + // FIXME: remove conversion + serialize_little_endian(compress(message, 1), 1).as_array() +} diff --git a/src/kem/kyber768/ntt.rs b/src/kem/kyber768/ntt.rs new file mode 100644 index 000000000..635a550d6 --- /dev/null +++ b/src/kem/kyber768/ntt.rs @@ -0,0 +1,181 @@ +use crate::kem::kyber768::parameters::{KyberPolynomialRingElement, RANK}; + +use self::kyber_polynomial_ring_element_mod::ntt_multiply; + +pub(crate) mod kyber_polynomial_ring_element_mod { + use crate::kem::kyber768::utils::field::FieldElement; + + use crate::kem::kyber768::parameters::{ + self, KyberFieldElement, KyberPolynomialRingElement, COEFFICIENTS_IN_RING_ELEMENT, + }; + + /// [ pow(17, br(i), p) for 0 <= i < 128 ] + /// br(i) is the bit reversal of i regarded as a 7-bit number. + const ZETAS: [u16; 128] = [ + 1, 1729, 2580, 3289, 2642, 630, 1897, 848, 1062, 1919, 193, 797, 2786, 3260, 569, 1746, + 296, 2447, 1339, 1476, 3046, 56, 2240, 1333, 1426, 2094, 535, 2882, 2393, 2879, 1974, 821, + 289, 331, 3253, 1756, 1197, 2304, 2277, 2055, 650, 1977, 2513, 632, 2865, 33, 1320, 1915, + 2319, 1435, 807, 452, 1438, 2868, 1534, 2402, 2647, 2617, 1481, 648, 2474, 3110, 1227, 910, + 17, 2761, 583, 2649, 1637, 723, 2288, 1100, 1409, 2662, 3281, 233, 756, 2156, 3015, 3050, + 1703, 1651, 2789, 1789, 1847, 952, 1461, 2687, 939, 2308, 2437, 2388, 733, 2337, 268, 641, + 1584, 2298, 2037, 3220, 375, 2549, 2090, 1645, 1063, 319, 2773, 757, 2099, 561, 2466, 2594, + 2804, 1092, 403, 1026, 1143, 2150, 2775, 886, 1722, 1212, 1874, 1029, 2110, 2935, 885, + 2154, + ]; + + /// [ pow(17, 2 * br(i) + 1, p) for 0 <= i < 128 ] + /// br(i) is the bit reversal of i regarded as a 7-bit number. + const MOD_ROOTS: [u16; 128] = [ + 17, 3312, 2761, 568, 583, 2746, 2649, 680, 1637, 1692, 723, 2606, 2288, 1041, 1100, 2229, + 1409, 1920, 2662, 667, 3281, 48, 233, 3096, 756, 2573, 2156, 1173, 3015, 314, 3050, 279, + 1703, 1626, 1651, 1678, 2789, 540, 1789, 1540, 1847, 1482, 952, 2377, 1461, 1868, 2687, + 642, 939, 2390, 2308, 1021, 2437, 892, 2388, 941, 733, 2596, 2337, 992, 268, 3061, 641, + 2688, 1584, 1745, 2298, 1031, 2037, 1292, 3220, 109, 375, 2954, 2549, 780, 2090, 1239, + 1645, 1684, 1063, 2266, 319, 3010, 2773, 556, 757, 2572, 2099, 1230, 561, 2768, 2466, 863, + 2594, 735, 2804, 525, 1092, 2237, 403, 2926, 1026, 2303, 1143, 2186, 2150, 1179, 2775, 554, + 886, 2443, 1722, 1607, 1212, 2117, 1874, 1455, 1029, 2300, 2110, 1219, 2935, 394, 885, + 2444, 2154, 1175, + ]; + + const NTT_LAYERS: [usize; 7] = [2, 4, 8, 16, 32, 64, 128]; + + /// Use the Cooley–Tukey butterfly to compute an in-place NTT representation + /// of a `KyberPolynomialRingElement`. + /// + /// This can be seen (see [CFRG draft]) as 128 applications of the linear map CT where + /// + /// CT_i(a, b) => (a + zeta^i * b, a - zeta^i * b) mod q + /// + /// for the appropriate i. + /// + /// Because the Kyber base field has 256th roots of unity but not 512th roots + /// of unity, the resulting NTT representation is an element in: + /// + /// ```plaintext + /// Product(i = 0 to 255) F_{3329}[x] / (x^2 - zeta^{2i+1}), + /// ``` + /// + /// This is isomorphic to `F_{3329}[x] / (x^{256} + 1)` by the + /// Chinese Remainder Theorem. + /// + /// [CFRG draft]: + pub fn ntt_representation(mut re: KyberPolynomialRingElement) -> KyberPolynomialRingElement { + let mut zeta_i = 0; + for layer in NTT_LAYERS.iter().rev() { + for offset in (0..(COEFFICIENTS_IN_RING_ELEMENT - layer)).step_by(2 * layer) { + zeta_i += 1; + let zeta: KyberFieldElement = ZETAS[zeta_i].into(); + + for j in offset..offset + layer { + let t = zeta * re[j + layer]; + re[j + layer] = re[j] - t; + re[j] = re[j] + t; + } + } + } + re + } + + /// Use the Gentleman-Sande butterfly to invert, in-place, the NTT representation + /// of a `KyberPolynomialRingElement`. The inverse NTT can be computed (see [CFRG draft]) by + /// replacing CS_i by GS_j and + /// + /// ```plaintext + /// GS_j(a, b) => ( (a + b) / 2, zeta^{2*j + 1} * (a - b) / 2 ) mod q + /// ``` + /// + /// for the appropriate j. + /// + /// [CFRG draft]: https://datatracker.ietf.org/doc/draft-cfrg-schwabe-kyber/ + pub fn invert_ntt(re: KyberPolynomialRingElement) -> KyberPolynomialRingElement { + let inverse_of_2: KyberFieldElement = + KyberFieldElement::new((parameters::FIELD_MODULUS + 1) / 2); + + let mut out = KyberPolynomialRingElement::ZERO; + for i in 0..re.len() { + out[i].value = re[i].value; + } + + let mut zeta_i = COEFFICIENTS_IN_RING_ELEMENT / 2; + + for layer in NTT_LAYERS { + for offset in (0..(COEFFICIENTS_IN_RING_ELEMENT - layer)).step_by(2 * layer) { + zeta_i -= 1; + let zeta: KyberFieldElement = ZETAS[zeta_i].into(); + + for j in offset..offset + layer { + let a_minus_b = out[j + layer] - out[j]; + out[j] = inverse_of_2 * (out[j] + out[j + layer]); + out[j + layer] = inverse_of_2 * zeta * a_minus_b; + } + } + } + + out + } + + /// Two elements `a, b ∈ F_{3329}[x] / (x^2 - zeta^{2i+1})` in the Kyber NTT + /// domain: + /// + /// ```plaintext + /// a = a_0 + a_1 * x + /// b = b_0 + b_1 * x + /// ``` + /// + /// can be multiplied as follows: + /// + /// ```plaintext + /// (a_2 * x + a_1)(b_2 * x + b_1) = + /// (a_0 * b_0 + a_1 * b_1 * zeta^{2i + 1}) + (a_0 * b_1 + a_1 * b_0) * x + /// ``` + /// + /// for the appropriate i. + pub fn ntt_multiply( + left: &KyberPolynomialRingElement, + other: &KyberPolynomialRingElement, + ) -> KyberPolynomialRingElement { + let mut out = KyberPolynomialRingElement::ZERO; + + for i in (0..COEFFICIENTS_IN_RING_ELEMENT).step_by(2) { + let mod_root: KyberFieldElement = MOD_ROOTS[i / 2].into(); + + let a0_times_b0 = left[i] * other[i]; + let a1_times_b1 = left[i + 1] * other[i + 1]; + + let a0_times_b1 = left[i + 1] * other[i]; + let a1_times_b0 = left[i] * other[i + 1]; + + out[i] = a0_times_b0 + (a1_times_b1 * mod_root); + out[i + 1] = a0_times_b1 + a1_times_b0; + } + out + } +} + +pub(crate) fn multiply_matrix_by_column( + matrix: &[[KyberPolynomialRingElement; RANK]; RANK], + vector: &[KyberPolynomialRingElement; RANK], +) -> [KyberPolynomialRingElement; RANK] { + let mut result = [KyberPolynomialRingElement::ZERO; RANK]; + + for (i, row) in matrix.iter().enumerate() { + for (j, matrix_element) in row.iter().enumerate() { + let product = ntt_multiply(matrix_element, &vector[j]); + result[i] = result[i] + product; + } + } + result +} + +pub(crate) fn multiply_row_by_column( + row_vector: &[KyberPolynomialRingElement; RANK], + column_vector: &[KyberPolynomialRingElement; RANK], +) -> KyberPolynomialRingElement { + let mut result = KyberPolynomialRingElement::ZERO; + + for (row_element, column_element) in row_vector.iter().zip(column_vector.iter()) { + result = result + ntt_multiply(row_element, column_element); + } + + result +} diff --git a/src/kem/kyber768/parameters.rs b/src/kem/kyber768/parameters.rs new file mode 100644 index 000000000..9c8c3f401 --- /dev/null +++ b/src/kem/kyber768/parameters.rs @@ -0,0 +1,84 @@ +use crate::kem::kyber768::utils::{field::PrimeFieldElement, ring::PolynomialRingElement}; + +/// Field modulus: 3329 +pub(crate) const FIELD_MODULUS: u16 = 3329; + +/// Each field element needs floor(log_2(FIELD_MODULUS)) + 1 = 12 bits to represent +pub(crate) const BITS_PER_COEFFICIENT: usize = 12; + +/// Coefficients per ring element +pub(crate) const COEFFICIENTS_IN_RING_ELEMENT: usize = 256; + +/// Bits required per ring element +pub(crate) const BITS_PER_RING_ELEMENT: usize = COEFFICIENTS_IN_RING_ELEMENT * 12; + +/// Seed size for rejection sampling. +/// +/// See for some background regarding +/// this choice. +pub(crate) const REJECTION_SAMPLING_SEED_SIZE: usize = 168 * 5; + +/// Rank +pub(crate) const RANK: usize = 3; + +/// `T` NTT encoding size in bytes +pub(crate) const T_AS_NTT_ENCODED_SIZE: usize = + (RANK * COEFFICIENTS_IN_RING_ELEMENT * BITS_PER_COEFFICIENT) / 8; + +/// Compression factor for `U` +pub(crate) const VECTOR_U_COMPRESSION_FACTOR: usize = 10; + +/// Compression factor for `V` +pub(crate) const VECTOR_V_COMPRESSION_FACTOR: usize = 4; + +/// `U` encoding size in bytes +pub(crate) const VECTOR_U_SIZE: usize = + (RANK * COEFFICIENTS_IN_RING_ELEMENT * VECTOR_U_COMPRESSION_FACTOR) / 8; + +/// `V` encoding size in bytes +pub(crate) const VECTOR_V_SIZE: usize = + (COEFFICIENTS_IN_RING_ELEMENT * VECTOR_V_COMPRESSION_FACTOR) / 8; + +pub(crate) const CPA_PKE_KEY_GENERATION_SEED_SIZE: usize = 32; +pub(crate) const CPA_PKE_SECRET_KEY_SIZE: usize = + (RANK * COEFFICIENTS_IN_RING_ELEMENT * BITS_PER_COEFFICIENT) / 8; +pub(crate) const CPA_PKE_PUBLIC_KEY_SIZE: usize = T_AS_NTT_ENCODED_SIZE + 32; +pub(crate) const CPA_PKE_CIPHERTEXT_SIZE: usize = VECTOR_U_SIZE + VECTOR_V_SIZE; +pub(crate) const CPA_PKE_MESSAGE_SIZE: usize = 32; +pub(crate) const CPA_SERIALIZED_KEY_LEN: usize = CPA_PKE_SECRET_KEY_SIZE + + CPA_PKE_PUBLIC_KEY_SIZE + + hash_functions::H_DIGEST_SIZE + + CPA_PKE_MESSAGE_SIZE; + +#[allow(non_snake_case)] +pub(crate) mod hash_functions { + use crate::digest::{self, digest_size, Algorithm}; + + pub(crate) fn G(input: &[u8]) -> [u8; digest_size(Algorithm::Sha3_512)] { + crate::digest::sha3_512(input) + } + + pub(crate) const H_DIGEST_SIZE: usize = digest_size(Algorithm::Sha3_256); + pub(crate) fn H(input: &[u8]) -> [u8; H_DIGEST_SIZE] { + crate::digest::sha3_256(input) + } + + pub(crate) fn PRF(input: &[u8]) -> [u8; LEN] { + digest::shake256::(input) + } + + pub(crate) fn XOF(input: &[u8]) -> [u8; LEN] { + digest::shake128::(input) + } + + pub(crate) fn KDF(input: &[u8]) -> [u8; LEN] { + digest::shake256::(input) + } +} + +/// A Kyber field element. +pub(crate) type KyberFieldElement = PrimeFieldElement; + +/// A Kyber ring element +pub(crate) type KyberPolynomialRingElement = + PolynomialRingElement; diff --git a/src/kem/kyber768/sampling.rs b/src/kem/kyber768/sampling.rs new file mode 100644 index 000000000..a06d2a8de --- /dev/null +++ b/src/kem/kyber768/sampling.rs @@ -0,0 +1,65 @@ +use crate::kem::kyber768::{ + parameters::{self, KyberFieldElement, KyberPolynomialRingElement}, + utils::bit_vector::LittleEndianBitStream, + BadRejectionSamplingRandomnessError, +}; + +pub fn sample_from_uniform_distribution( + randomness: [u8; parameters::REJECTION_SAMPLING_SEED_SIZE], +) -> Result { + let mut sampled_coefficients: usize = 0; + let mut out: KyberPolynomialRingElement = KyberPolynomialRingElement::ZERO; + + for bytes in randomness.chunks(3) { + let b = u16::from(bytes[0]); + let b1 = u16::from(bytes[1]); + let b2 = u16::from(bytes[2]); + + let d1 = b + (256 * (b1 % 16)); + + // Integer division is flooring in Rust. + let d2 = (b1 / 16) + (16 * b2); + + if d1 < parameters::FIELD_MODULUS && sampled_coefficients < out.len() { + out[sampled_coefficients] = d1.into(); + sampled_coefficients += 1 + } + if d2 < parameters::FIELD_MODULUS && sampled_coefficients < out.len() { + out[sampled_coefficients] = d2.into(); + sampled_coefficients += 1; + } + + if sampled_coefficients == out.len() { + return Ok(out); + } + } + + Err(BadRejectionSamplingRandomnessError) +} + +pub fn sample_from_binomial_distribution( + sampling_coins: usize, + randomness: &[u8], +) -> KyberPolynomialRingElement { + assert_eq!(randomness.len(), sampling_coins * 64); + + let mut sampled: KyberPolynomialRingElement = KyberPolynomialRingElement::ZERO; + + for i in 0..sampled.len() { + let mut coin_tosses: u8 = 0; + for j in 0..sampling_coins { + coin_tosses += randomness.nth_bit(2 * i * sampling_coins + j); + } + let coin_tosses_a: KyberFieldElement = coin_tosses.into(); + + coin_tosses = 0; + for j in 0..sampling_coins { + coin_tosses += randomness.nth_bit(2 * i * sampling_coins + sampling_coins + j); + } + let coin_tosses_b: KyberFieldElement = coin_tosses.into(); + + sampled[i] = coin_tosses_a - coin_tosses_b; + } + + sampled +} diff --git a/src/kem/kyber768/serialize.rs b/src/kem/kyber768/serialize.rs new file mode 100644 index 000000000..cd9d0a7d7 --- /dev/null +++ b/src/kem/kyber768/serialize.rs @@ -0,0 +1,57 @@ +use crate::kem::kyber768::utils::{bit_vector::BitVector, ring::LittleEndianBitStream}; + +use crate::kem::kyber768::parameters::{ + KyberFieldElement, KyberPolynomialRingElement, BITS_PER_COEFFICIENT, +}; + +pub fn serialize_little_endian( + re: KyberPolynomialRingElement, + bits_per_coefficient: usize, +) -> Vec { + let out_bytes = (256 * bits_per_coefficient) / 8; + let mut serialized: Vec = Vec::with_capacity(out_bytes); + + for i in 0..out_bytes { + let mut byte_value: u8 = 0; + + for bit_pos in 0..8 { + let bit = re + .coefficients() + .nth_bit_with_coefficient_bit_size(i * 8 + bit_pos, bits_per_coefficient); + byte_value |= bit << bit_pos; + } + + serialized.push(byte_value); + } + + serialized +} + +fn field_element_from_little_endian_bit_vector(bit_vector: BitVector) -> KyberFieldElement { + let mut value: u16 = 0; + for (i, bit) in bit_vector.into_iter().enumerate() { + value |= u16::from(bit) << i; + } + + value.into() +} + +pub fn deserialize_little_endian( + bits_per_coefficient: usize, + ring_element_bytes: &[u8], +) -> KyberPolynomialRingElement { + assert!(bits_per_coefficient <= BITS_PER_COEFFICIENT); + + // FIXME: rewrite like serialization without the `BitVector`. + let ring_element_bits: BitVector = ring_element_bytes.into(); + let mut ring_element_bits = ring_element_bits.chunks(bits_per_coefficient); + + let mut deserialized = KyberPolynomialRingElement::ZERO; + + for i in 0..deserialized.len() { + deserialized[i] = + field_element_from_little_endian_bit_vector(ring_element_bits.next().unwrap()); + } + + deserialized +} diff --git a/src/kem/kyber768/utils.rs b/src/kem/kyber768/utils.rs new file mode 100644 index 000000000..52ffdbfbd --- /dev/null +++ b/src/kem/kyber768/utils.rs @@ -0,0 +1,159 @@ +pub(crate) mod bit_vector; +pub(crate) mod field; +pub(crate) mod ring; + +pub trait PanickingIntegerCasts { + fn as_u8(self) -> u8; + fn as_u16(self) -> u16; + fn as_u32(self) -> u32; +} + +impl PanickingIntegerCasts for usize { + fn as_u8(self) -> u8 { + self.try_into().unwrap() + } + + fn as_u16(self) -> u16 { + self.try_into().unwrap() + } + + fn as_u32(self) -> u32 { + self.try_into().unwrap() + } +} + +pub trait ArrayConversion { + fn as_array(&self) -> [u8; LEN]; + fn into_array(self) -> [u8; LEN]; + fn into_padded_array(&self) -> [u8; LEN]; +} + +impl ArrayConversion for Vec { + fn as_array(&self) -> [u8; LEN] { + self.clone().try_into().unwrap() + } + + fn into_array(self) -> [u8; LEN] { + self.try_into().unwrap() + } + + fn into_padded_array(&self) -> [u8; LEN] { + assert!(self.len() <= LEN); + let mut out = [0u8; LEN]; + out[0..self.len()].copy_from_slice(self); + out + } +} + +impl ArrayConversion for &[u8] { + fn as_array(&self) -> [u8; LEN] { + self.to_vec().try_into().unwrap() + } + + fn into_array(self) -> [u8; LEN] { + self.try_into().unwrap() + } + + fn into_padded_array(&self) -> [u8; LEN] { + assert!(self.len() <= LEN); + let mut out = [0u8; LEN]; + out[0..self.len()].copy_from_slice(self); + out + } +} + +pub trait ArrayPadding { + fn into_padded_array(&self) -> [u8; OLEN]; +} + +impl ArrayPadding for &[u8; LEN] { + fn into_padded_array(&self) -> [u8; OLEN] { + assert!(self.len() <= OLEN); + let mut out = [0u8; OLEN]; + out[0..self.len()].copy_from_slice(*self); + out + } +} + +pub trait ArrayUpdate { + fn update(self, start: usize, other: &[u8]) -> Self; +} + +impl ArrayUpdate for [u8; LEN] { + fn update(mut self, start: usize, other: &[u8]) -> Self { + self[start..start + other.len()].copy_from_slice(other); + self + } +} + +pub trait VecUpdate { + fn concat(self, other: &[u8]) -> Self; +} + +impl VecUpdate for Vec { + fn concat(mut self, other: &[u8]) -> Self { + self.extend_from_slice(&other); + self + } +} + +pub trait UpdatingArray { + fn push(self, other: &[u8]) -> Self; +} + +pub struct UpdatableArray { + value: [u8; LEN], + pointer: usize, +} + +impl UpdatableArray { + pub fn new(value: [u8; LEN]) -> Self { + Self { value, pointer: 0 } + } + + pub fn array(self) -> [u8; LEN] { + self.value + } +} + +impl UpdatingArray for UpdatableArray { + fn push(mut self, other: &[u8]) -> Self { + self.value[self.pointer..self.pointer + other.len()].copy_from_slice(other); + self.pointer += other.len(); + self + } +} + +impl From<[u8; LEN]> for UpdatableArray { + fn from(value: [u8; LEN]) -> Self { + Self { value, pointer: 0 } + } +} + +impl From> for [u8; LEN] { + fn from(value: UpdatableArray) -> Self { + value.value + } +} + +pub trait UpdatingArray2 { + fn push(self, other: &[u8]) -> [u8; OLEN]; +} + +impl UpdatingArray2 for [u8; LEN] { + fn push(self, other: &[u8]) -> [u8; OLEN] { + let mut out = [0u8; OLEN]; + out[0..self.len()].copy_from_slice(&self); + out[self.len()..].copy_from_slice(other); + out + } +} + +impl UpdatingArray2 for &[u8] { + fn push(self, other: &[u8]) -> [u8; OLEN] { + let mut out = [0u8; OLEN]; + out[0..self.len()].copy_from_slice(&self); + out[self.len()..].copy_from_slice(other); + out + } +} diff --git a/src/kem/kyber768/utils/bit_vector.rs b/src/kem/kyber768/utils/bit_vector.rs new file mode 100644 index 000000000..091f7eee4 --- /dev/null +++ b/src/kem/kyber768/utils/bit_vector.rs @@ -0,0 +1,100 @@ +pub struct BitVector { + bits: Vec, +} + +pub struct BitVectorChunks<'a> { + chunk_iterator: std::slice::Chunks<'a, u8>, +} + +impl BitVectorChunks<'_> { + pub fn next(&mut self) -> Option { + self.chunk_iterator.next().map(|bits| BitVector { + bits: bits.to_vec(), + }) + } +} + +impl IntoIterator for BitVector { + type Item = u8; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.bits.into_iter() + } +} + +impl From<&[u8]> for BitVector { + fn from(bytes: &[u8]) -> Self { + let mut out = Vec::with_capacity(bytes.len() * 8); + + for byte in bytes { + for j in 0..u8::BITS { + out.push((byte >> j) & 1); + } + } + + Self { bits: out } + } +} + +impl BitVector { + pub fn chunks(&self, chunk_size: usize) -> BitVectorChunks { + BitVectorChunks { + chunk_iterator: self.bits.chunks(chunk_size), + } + } +} + +pub trait LittleEndianBitStream { + fn nth_bit(&self, n: usize) -> u8; + fn iter(&self) -> LittleEndianBitStreamIter<'_>; +} + +pub struct LittleEndianBitStreamIter<'a> { + bytes: &'a [u8], + bit: usize, +} + +impl Iterator for LittleEndianBitStreamIter<'_> { + type Item = u8; + + fn next(&mut self) -> Option { + let byte_index = self.bit / 8; + if byte_index >= self.bytes.len() { + return None; + } + + let out = self.bytes.nth_bit(self.bit); + self.bit += 1; + + Some(out) + } +} + +impl LittleEndianBitStream for &[u8] { + fn nth_bit(&self, n: usize) -> u8 { + let byte = n / 8; + let byte_bit = n % 8; + (self[byte] >> byte_bit) & 1 + } + + fn iter(&self) -> LittleEndianBitStreamIter<'_> { + LittleEndianBitStreamIter { + bytes: self, + bit: 0, + } + } +} + +impl LittleEndianBitStream for Vec { + fn nth_bit(&self, n: usize) -> u8 { + self.as_slice().nth_bit(n) + } + + fn iter(&self) -> LittleEndianBitStreamIter<'_> { + LittleEndianBitStreamIter { + bytes: self, + bit: 0, + } + } +} diff --git a/src/kem/kyber768/utils/field.rs b/src/kem/kyber768/utils/field.rs new file mode 100644 index 000000000..c3f0ec499 --- /dev/null +++ b/src/kem/kyber768/utils/field.rs @@ -0,0 +1,95 @@ +use std::ops; + +pub trait FieldElement: + Copy + + Clone + + PartialEq + + From + + From + + From + + Into + + ops::Add + + ops::Sub + + ops::Mul +{ + const ZERO: Self; + + fn new(number: u16) -> Self; + fn nth_bit_little_endian(&self, n: usize) -> u8; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PrimeFieldElement { + pub value: u16, +} + +impl FieldElement for PrimeFieldElement { + const ZERO: Self = Self { value: 0 }; + + fn new(number: u16) -> Self { + Self { + value: number % MODULUS, + } + } + + fn nth_bit_little_endian(&self, n: usize) -> u8 { + ((self.value >> n) & 1) as u8 + } +} + +impl PrimeFieldElement { + pub const MODULUS: u16 = MODULUS; +} + +impl From for PrimeFieldElement { + fn from(number: u8) -> Self { + Self::new(u16::from(number)) + } +} +impl From for PrimeFieldElement { + fn from(number: u16) -> Self { + Self::new(number) + } +} +impl From> for u16 { + fn from(value: PrimeFieldElement) -> Self { + value.value + } +} +impl From for PrimeFieldElement { + fn from(number: u32) -> Self { + let remainder_as_u32 = number % u32::from(MODULUS); + + Self::new(remainder_as_u32.try_into().unwrap()) + } +} + +impl ops::Add for PrimeFieldElement { + type Output = Self; + + fn add(self, other: Self) -> Self { + let sum: u32 = u32::from(self.value) + u32::from(other.value); + + sum.into() + } +} +impl ops::Sub for PrimeFieldElement { + type Output = Self; + + fn sub(self, other: Self) -> Self { + let difference: i32 = + i32::try_from(self.value).unwrap() - i32::try_from(other.value).unwrap(); + let representative = difference.rem_euclid(MODULUS.into()); + + u16::try_from(representative).unwrap().into() + } +} +impl ops::Mul for PrimeFieldElement { + type Output = Self; + + fn mul(self, other: Self) -> Self { + let product: u32 = u32::from(self.value) * u32::from(other.value); + + product.into() + } +} diff --git a/src/kem/kyber768/utils/ring.rs b/src/kem/kyber768/utils/ring.rs new file mode 100644 index 000000000..24906f967 --- /dev/null +++ b/src/kem/kyber768/utils/ring.rs @@ -0,0 +1,160 @@ +use std::ops::{self, Index, IndexMut}; + +use crate::kem::kyber768::utils::field::FieldElement; + +pub trait LittleEndianBitStream { + fn nth_bit(&self, n: usize) -> u8; + fn nth_bit_with_coefficient_bit_size(&self, n: usize, coefficient_bit_size: usize) -> u8; + fn bits_iter(&self) -> LittleEndianBitStreamIter<'_, F, COEFFICIENTS>; + fn bits_chunks(&self, chunk_len: usize) -> LittleEndianBitStreamIter<'_, F, COEFFICIENTS>; +} + +pub struct LittleEndianBitStreamIter<'a, F: FieldElement, const COEFFICIENTS: usize> { + values: &'a [F; COEFFICIENTS], + bit: usize, + chunk_len: usize, +} + +impl LittleEndianBitStream + for &[F; COEFFICIENTS] +{ + fn nth_bit(&self, n: usize) -> u8 { + let index = n / 16; + let bit_mod = n % 16; + // eprintln!(" >>> self[{index}] >> {bit_mod}"); + ((Into::::into(self[index]) >> bit_mod) & 1) as u8 + } + + fn nth_bit_with_coefficient_bit_size(&self, n: usize, coefficient_bit_size: usize) -> u8 { + let coefficient_index = n / coefficient_bit_size; + let coefficient_bit = n % coefficient_bit_size; + + self[coefficient_index].nth_bit_little_endian(coefficient_bit) + } + + fn bits_iter(&self) -> LittleEndianBitStreamIter<'_, F, COEFFICIENTS> { + LittleEndianBitStreamIter { + values: self, + bit: 0, + chunk_len: 1, + } + } + + fn bits_chunks(&self, chunk_len: usize) -> LittleEndianBitStreamIter<'_, F, COEFFICIENTS> { + // This iterator allows setting the number of bits used to encode one limb. + // It is NOT chunking. + assert!(chunk_len <= 16); + LittleEndianBitStreamIter { + values: self, + bit: 0, + chunk_len, + } + } +} + +impl Iterator + for LittleEndianBitStreamIter<'_, F, COEFFICIENTS> +{ + type Item = Vec; + + fn next(&mut self) -> Option { + let byte_index = (self.bit + self.chunk_len) / 16; + if byte_index >= self.values.len() { + return None; + } + + let mut out = vec![0u8; self.chunk_len]; + // eprintln!( + // " >>> bits: {}-{} ({})", + // self.bit, + // self.bit + self.chunk_len, + // self.bit / 16 + // ); + for i in 0..self.chunk_len { + out[i] = self.values.nth_bit(self.bit + i); + } + self.bit += 16; + + Some(out) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PolynomialRingElement { + pub(crate) coefficients: [F; COEFFICIENTS], +} + +impl PolynomialRingElement { + pub const ZERO: Self = Self { + coefficients: [F::ZERO; COEFFICIENTS], + }; + + pub fn new(coefficients: [F; COEFFICIENTS]) -> Self { + Self { coefficients } + } + + pub fn coefficients(&self) -> &[F; COEFFICIENTS] { + &self.coefficients + } + + pub fn len(&self) -> usize { + self.coefficients.len() + } +} + +impl Index + for PolynomialRingElement +{ + type Output = F; + + fn index(&self, index: usize) -> &Self::Output { + &self.coefficients[index] + } +} + +impl IndexMut + for PolynomialRingElement +{ + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.coefficients[index] + } +} + +impl IntoIterator + for PolynomialRingElement +{ + type Item = F; + + type IntoIter = std::array::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.coefficients.into_iter() + } +} + +impl ops::Add + for PolynomialRingElement +{ + type Output = Self; + + fn add(self, other: Self) -> Self { + let mut result = PolynomialRingElement::::ZERO; + for i in 0..self.coefficients.len() { + result.coefficients[i] = self.coefficients[i] + other.coefficients[i]; + } + result + } +} +impl ops::Sub + for PolynomialRingElement +{ + type Output = Self; + + fn sub(self, other: Self) -> Self { + let mut result = PolynomialRingElement::::ZERO; + for i in 0..self.coefficients.len() { + result.coefficients[i] = self.coefficients[i] - other.coefficients[i]; + } + result + } +} diff --git a/tests/kyber768.rs b/tests/kyber768.rs new file mode 100644 index 000000000..6a7cdb8f2 --- /dev/null +++ b/tests/kyber768.rs @@ -0,0 +1,106 @@ +use libcrux::digest; +use libcrux::drbg::Drbg; +use libcrux::drbg::RngCore; +use libcrux::kem::{self, Algorithm}; + +#[test] +fn consistency() { + let mut drbg = Drbg::new(digest::Algorithm::Sha256).unwrap(); + + if let Ok((secret_key, public_key)) = kem::key_gen(Algorithm::Kyber768, &mut drbg) { + if let Ok((shared_secret, ciphertext)) = + kem::encapsulate(Algorithm::Kyber768, &public_key, &mut drbg) + { + let shared_secret_decapsulated = + kem::decapsulate(Algorithm::Kyber768, &ciphertext, &secret_key).unwrap(); + assert_eq!(shared_secret, shared_secret_decapsulated); + } + } + + // If the randomness was not enough for the rejection sampling step + // in key-generation and encapsulation, simply return without + // failing. +} + +#[test] +fn modified_ciphertext() { + let mut drbg = Drbg::new(digest::Algorithm::Sha256).unwrap(); + + let random_u32 = drbg.next_u32(); + let random_byte: u8 = (random_u32 & 0xFF).try_into().unwrap(); + + let ciphertext_position: usize = (random_u32 % 1088).try_into().unwrap(); + + if let Ok((secret_key, public_key)) = kem::key_gen(Algorithm::Kyber768, &mut drbg) { + if let Ok((shared_secret, mut ciphertext)) = + kem::encapsulate(Algorithm::Kyber768, &public_key, &mut drbg) + { + ciphertext[ciphertext_position] ^= random_byte; + let shared_secret_decapsulated = + kem::decapsulate(Algorithm::Kyber768, &ciphertext, &secret_key).unwrap(); + + assert_ne!(shared_secret, shared_secret_decapsulated); + } + } + // If the randomness was not enough for the rejection sampling step + // in key-generation and encapsulation, simply return without + // failing. +} + +#[test] +fn modified_secret_key() { + let mut drbg = Drbg::new(digest::Algorithm::Sha256).unwrap(); + + let random_u32 = drbg.next_u32(); + + let random_byte: u8 = (random_u32 & 0xFF).try_into().unwrap(); + + let secret_key_position: usize = ((random_u32 >> 8) % (2400 - 32)).try_into().unwrap(); + + if let Ok((mut secret_key, public_key)) = kem::key_gen(Algorithm::Kyber768, &mut drbg) { + if let Ok((shared_secret, ciphertext)) = + kem::encapsulate(Algorithm::Kyber768, &public_key, &mut drbg) + { + secret_key[secret_key_position] ^= random_byte; + let shared_secret_decapsulated = + kem::decapsulate(Algorithm::Kyber768, &ciphertext, &secret_key).unwrap(); + + assert_ne!(shared_secret, shared_secret_decapsulated); + } + } + // If the randomness was not enough for the rejection sampling step + // in key-generation and encapsulation, simply return without + // failing. +} + +#[test] +fn modified_ciphertext_and_implicit_rejection_value() { + let mut drbg = Drbg::new(digest::Algorithm::Sha256).unwrap(); + + let random_u32 = drbg.next_u32(); + let random_byte_for_ciphertext: u8 = (random_u32 & 0xFF).try_into().unwrap(); + let ciphertext_position: usize = ((random_u32 >> 8) % 1088).try_into().unwrap(); + + let random_u32 = drbg.next_u32(); + let random_byte_for_secret_key: u8 = (random_u32 & 0xFF).try_into().unwrap(); + let secret_key_position: usize = ((random_u32 >> 8) % 32).try_into().unwrap(); + + if let Ok((mut secret_key, public_key)) = kem::key_gen(Algorithm::Kyber768, &mut drbg) { + if let Ok((_, mut ciphertext)) = + kem::encapsulate(Algorithm::Kyber768, &public_key, &mut drbg) + { + ciphertext[ciphertext_position] ^= random_byte_for_ciphertext; + let shared_secret_decapsulated = + kem::decapsulate(Algorithm::Kyber768, &ciphertext, &secret_key).unwrap(); + + secret_key[(2400 - 32) + secret_key_position] ^= random_byte_for_secret_key; + let shared_secret_decapsulated_1 = + kem::decapsulate(Algorithm::Kyber768, &ciphertext, &secret_key).unwrap(); + + assert_ne!(shared_secret_decapsulated, shared_secret_decapsulated_1); + } + } + // If the randomness was not enough for the rejection sampling step + // in key-generation and encapsulation, simply return without + // failing. +} diff --git a/tests/kyber768_nist_kat.rs b/tests/kyber768_nist_kat.rs new file mode 100644 index 000000000..c2adf40e9 --- /dev/null +++ b/tests/kyber768_nist_kat.rs @@ -0,0 +1,68 @@ +use libcrux::kem; +use serde::Deserialize; +use serde_json; + +use libcrux::digest; + +use std::{fs::File, io::BufReader}; + +#[derive(Deserialize)] +struct Kyber768NISTKAT { + #[serde(with = "hex::serde")] + key_generation_seed: [u8; 64], + + #[serde(with = "hex::serde")] + sha3_256_hash_of_public_key: [u8; 32], + + #[serde(with = "hex::serde")] + sha3_256_hash_of_secret_key: [u8; 32], + + #[serde(with = "hex::serde")] + encapsulation_seed: [u8; 32], + + #[serde(with = "hex::serde")] + sha3_256_hash_of_ciphertext: [u8; 32], + + #[serde(with = "hex::serde")] + shared_secret: [u8; 32], +} + +#[test] +fn kyber768_known_answer_tests() { + let katfile = File::open("tests/kyber768_nist_kats.json").expect("Could not open KAT file."); + let reader = BufReader::new(katfile); + + let nist_kats: Vec = + serde_json::from_reader(reader).expect("Could not deserialize KAT file."); + + for kat in nist_kats { + let (public_key, secret_key) = kem::kyber768_generate_keypair_derand(kat.key_generation_seed).unwrap(); + + let public_key_hash = digest::sha3_256(&public_key); + for i in 0..public_key_hash.len() { + assert_eq!(public_key_hash[i], kat.sha3_256_hash_of_public_key[i]); + } + + let secret_key_hash = digest::sha3_256(&secret_key); + for i in 0..secret_key_hash.len() { + assert_eq!(secret_key_hash[i], kat.sha3_256_hash_of_secret_key[i]); + } + + let (ciphertext, shared_secret) = + kem::kyber768_encapsulate_derand(public_key, kat.encapsulation_seed).unwrap(); + + let ciphertext_hash = digest::sha3_256(&ciphertext); + for i in 0..ciphertext_hash.len() { + assert_eq!(ciphertext_hash[i], kat.sha3_256_hash_of_ciphertext[i]); + } + + for i in 0..shared_secret.len() { + assert_eq!(shared_secret[i], kat.shared_secret[i]); + } + + let shared_secret_from_decapsulate = kem::kyber768_decapsulate_derand(secret_key, ciphertext); + for i in 0..shared_secret.len() { + assert_eq!(shared_secret_from_decapsulate[i], shared_secret[i]); + } + } +} diff --git a/tests/kyber768_nist_kats.json b/tests/kyber768_nist_kats.json new file mode 100644 index 000000000..143afd4fe --- /dev/null +++ b/tests/kyber768_nist_kats.json @@ -0,0 +1,802 @@ +[ + { + "key_generation_seed": "7c9935a0b07694aa0c6d10e4db6b1add2fd81a25ccb148032dcd739936737f2d8626ed79d451140800e03b59b956f8210e556067407d13dc90fa9e8b872bfb8f", + "sha3_256_hash_of_public_key": "d4ec143b50f01423b177895edee22bb739f647ecf85f50bc25ef7b5a725dee86", + "sha3_256_hash_of_secret_key": "245bc1d8cdd4893e4c471e8fccfa7019df0fd10f2d5375f36b4af5f4222aca6a", + "encapsulation_seed": "147c03f7a5bebba406c8fae1874d7f13c80efe79a3a9a874cc09fe76f6997615", + "sha3_256_hash_of_ciphertext": "962242140e9b3492476c62847a250a5e425a41ceec123ce0158d601e7af4139e", + "shared_secret": "914cb67fe5c38e73bf74181c0ac50428dedf7750a98058f7d536708774535b29" + }, + { + "key_generation_seed": "d60b93492a1d8c1c7ba6fc0b733137f3406cee8110a93f170e7a78658af326d9003271531cf27285b8721ed5cb46853043b346a66cba6cf765f1b0eaa40bf672", + "sha3_256_hash_of_public_key": "2cedad700b675e98641bea57b936bd8befce2d5161e0ef4ef8406e70f1e2c27c", + "sha3_256_hash_of_secret_key": "0a84cc895da138b944accbef3ff1a0004b8a0d8af5d426d2b82ea4c0e585cc6a", + "encapsulation_seed": "cde797df8ce67231f6c5d15811843e01eb2ab84c7490931240822adbddd72046", + "sha3_256_hash_of_ciphertext": "5c292a0b3e0f411d1d52ea30126d6c21e83f0bb5781cb4418795d4568ded5e77", + "shared_secret": "fe8aaa6558fd8087dd7cab54b4bce50fc625a369ecace58b2ec36f3bc5bb4f5a" + }, + { + "key_generation_seed": "4b622de1350119c45a9f2e2ef3dc5df50a759d138cdfbd64c81cc7cc2f513345e82fcc97ca60ccb27bf6938c975658aeb8b4d37cffbde25d97e561f36c219ade", + "sha3_256_hash_of_public_key": "3dbc65b722a8982d058e27d409f04f744551ecde9015b62607cf67bb8ececbb8", + "sha3_256_hash_of_secret_key": "0ffced333b5d13fff22b81e66d57b6e2a6dba0285fe2a82d5537df51a8d3eac3", + "encapsulation_seed": "f43f68fbd694f0a6d307297110ecd4739876489fdf07eb9b03364e2ed0ff96e9", + "sha3_256_hash_of_ciphertext": "0d21bf3200a154ba18b488794d8fdcb17a7796b0b0cd98cedc882a55764cada4", + "shared_secret": "86435ab2aff9cea1dc653ce819721a56933841f29330869b63e36604a6ceaff2" + }, + { + "key_generation_seed": "050d58f9f757edc1e8180e3808b806f5bbb3586db3470b069826d1bb9a4efc2cde950541fd53a8a47aaa8cdfe80d928262a5ef7f8129ec3ef92f78d7cc32ef60", + "sha3_256_hash_of_public_key": "94391b7a41175a41c15cd995ebc69c83b29e4bcea6c186611dc4a79578e37f4c", + "sha3_256_hash_of_secret_key": "e3904266e186b34a397014c95f6d314cd6e1c813348b02e977d0fd21d9bb681b", + "encapsulation_seed": "ea74fbc3c546500ed684bed6fe3c496d3b86d2d6dfaf223969b942e9a8c95e85", + "sha3_256_hash_of_ciphertext": "9bdde47293bfd0a5b054c7ef58d2d9baf78aa47685941f315e771595b15c99b9", + "shared_secret": "f9a2d73f0a81b5829e7c7cad8fcf5f1ad55b384b2427c288bfbf4c29540f1db6" + }, + { + "key_generation_seed": "66b79b844e0c2adad694e0478661ac46fe6b6001f6a71ff8e2f034b1fd8833d3be2d3c64d38269a1ee8660b9a2beaeb9f5ac022e8f0a357feebfd13b06813854", + "sha3_256_hash_of_public_key": "c5dbd68b3a8c148b2e7ac049bb986e14dd1cebfa1cbf3edd6bae85a4d2dda082", + "sha3_256_hash_of_secret_key": "b3fa7958f4b7ccb68712ae948c3f08740c8b89a69e53ad4e9959234e6869d8fe", + "encapsulation_seed": "64efa87a12cb96f98b9b81a7e5128a959c74e5332aaab0444fca7b4a5e5e0216", + "sha3_256_hash_of_ciphertext": "b898e6fe8eec252f9d2b16894f9e29425514eccca8aea8ad7ffc693e57124c4b", + "shared_secret": "83e562482fcf5157c75d3d2f0a35da861689a1009104a071a7bfb10bc4d8cd02" + }, + { + "key_generation_seed": "7ec408f52c9aa723d0c41d9987682a5f4ce6c9da7cd0215af60bbaf5484ab353a08ccf451b049fd51d7a9ad77ae14a81569df8c9bd3a8f1ebea86fdcfb823082", + "sha3_256_hash_of_public_key": "62e0447f7b5ae8a806b741ca5c302230b555c3786c11f3eb43894a8f45e3f7b1", + "sha3_256_hash_of_secret_key": "1a3249c268754c86d2e02ba9d87c2b60b220bf2406b71037cfaf6b089477ffb4", + "encapsulation_seed": "8a95d71228acaa5f9ae6f9d9ca8ae55fde296463b41083a39e833e37c4c90f88", + "sha3_256_hash_of_ciphertext": "f37074f0b62febb653bc6a39f3a242c03572ec4e7b0bdac6a1a807e74044c3ec", + "shared_secret": "445b60a142d4853702a102f9cc37fdfb1d0b14a9a7e210c7d290f9402f0a2f40" + }, + { + "key_generation_seed": "c121915bfef6abdfc177dae2f5a24218f9abda2559afc6741b08e0e61ab433eb84ef52db5eaa6df8ec3a0bc5ffa730db0dde8c5f38f266d5c680a78d264a7b96", + "sha3_256_hash_of_public_key": "0c1d832af7b7282d8bd81a2237107ee60d81e28eb64d6a153ae0eaa1a25797c2", + "sha3_256_hash_of_secret_key": "fd6b5d3f120ca009871ca24552a6118917ea882f12f30dc8097f6614d9d36080", + "encapsulation_seed": "90d79d75d0bbb8921cf70d46bab497022a8e750efdc99e5f1bae653275441c7b", + "sha3_256_hash_of_ciphertext": "c68b7acf4074d1ae2ff4055dc420e5c4e808255623874082f31118c212b079c2", + "shared_secret": "71156980b8970fed7f2213594630ca825ea8eade58cc8225df8111460412b762" + }, + { + "key_generation_seed": "d86634ecf96cc2603761e284c0e36734cedec64e7ff486469e38539c71141c5a99daf37400cfe59841afc412ec97f2929dc84a6f3c36f378ee84ce3e46cd1209", + "sha3_256_hash_of_public_key": "2b757ac0425152bef72ed852ab1eb44f4359499407bb6a020ff843a31657c5fe", + "sha3_256_hash_of_secret_key": "27dbbc7918c31e9ab57808f439c4f4189cc318a62422457f4fed733be959c816", + "encapsulation_seed": "be8a32f97b9a8d596382c02fa2a0eeebc15c083e970ddaa4f2622b91d6718663", + "sha3_256_hash_of_ciphertext": "2f7dd9b6567b3c2c8be065e42e3a709339e26dfaebaa6d1d85c88e1ddc214694", + "shared_secret": "eff5e4ce44fe0d0b77cf8109d46c8a888399d1608ad9e2248181bd205a0b23d9" + }, + { + "key_generation_seed": "0610678ff4dc3128e1619f915dc192c220f8fad94da1943b90aaec401683a492da1804ddb5aa9b1c6a47a98f8505a49bae2affde5fe75e69e828e546a6771004", + "sha3_256_hash_of_public_key": "53b9d62e64f9069d9fb94ea2c0806459b201531f4fddd708d162981cc1fb3757", + "sha3_256_hash_of_secret_key": "f4b964b7ab3e09fdf3d91527da06a4d29ef28344709a41739ef56f18bd5b984b", + "encapsulation_seed": "da2cfaf69e25b2a89ff2557bbb6f69e01d8e2e7bb27a7a1ce7e40fead16f33b2", + "sha3_256_hash_of_ciphertext": "db93c9936569e81c7ed2e9c9a29ecedefddd06f793ec869f6dd0b88148e7c43c", + "shared_secret": "25c35f5858d03291c0132c970e6e072d8a7b33419b984b391a12891f878d981f" + }, + { + "key_generation_seed": "d322d56d8ef067ba1f24c92492b9c56df3a6ef54a304adc1b69913766a1ce69756047447b810cc094d400ab204cf9ae71e3afa68b88586ecb6498c68ac0e51b9", + "sha3_256_hash_of_public_key": "9cfeca12dfe978bf0b7ad7271487cf61b2b8f7c60f389f33fc18439a95bcbb63", + "sha3_256_hash_of_secret_key": "a2e37a55c9b80fb423f40585180b011f32402d0320259285b6e278df6c20ba60", + "encapsulation_seed": "511c2ab40782322c06111e144e505328c4e5bfc890a5980a2bbc44aeda4c738b", + "sha3_256_hash_of_ciphertext": "758e2189cd55f9d9fa80fbcfa433bce052cf402cd53ee060f930356f780ecd62", + "shared_secret": "f9eb1c87813c712cc3054109acb5f219fc1e29db10ff33d093a5cd67df995015" + }, + { + "key_generation_seed": "2f1d8a3bebb34540324b9485fdf3d5be3b858f544abc3fc641b5728cafab03ba8d6c42e7270ee2b77b6045385f3d175984a0e260363166c73b0c70c971644363", + "sha3_256_hash_of_public_key": "9aa64a30bed5aa8300772066ef577f79bf4813e3315a15f2c28b2665e4dc7e2f", + "sha3_256_hash_of_secret_key": "837eb6ce037f235273d7686fd9d01bea14026e0a0f5f943884f18409cc4bc70a", + "encapsulation_seed": "dca92dbec9b260dd97e8886f876862d6effc3b91fcf3fbc986cf56ab93ae79a2", + "sha3_256_hash_of_ciphertext": "8a8d7f3524c3ef9c1dd37d62969e9a829f61409b132ff5b2732286e22bd08af8", + "shared_secret": "a2b1d4028af3777be109a51fab5b7014681b0be94a7c06e8c2100565667f21a7" + }, + { + "key_generation_seed": "31beda3462627f601cbc56f3ddf4424e1529c04737ef0ef2af6d7401f653b8a1812083bfa3b670e3eaf9b443702fb6db16ac1197656bbd61a8e25ed523b8d1e5", + "sha3_256_hash_of_public_key": "241e5c7b836862d7482d507973ae3fd8dae96eec4ecebcedb68fbda75e04b401", + "sha3_256_hash_of_secret_key": "95c79c2a867b3e8a4e4e545ff626cd49893b8e87eb188ed1516b159a24736c97", + "encapsulation_seed": "57c170e691d7a914a901b9a11c62b8b569b3806427557a9dbac9faa720ec3641", + "sha3_256_hash_of_ciphertext": "731f4dcf933a93ab333df3632b48b059b0bcd6fe8956435756ea53d67b539ddc", + "shared_secret": "4a2bac4d2a30597aa5b528d0e20b1630b20a36e8d747b5ec41eeb57950375a5d" + }, + { + "key_generation_seed": "cbdff028766d558af4466ef14043a1a9cf765f7748c63cc09dceb59ab39a4e4d8e9a30597e4b52ffa87a54b83c91d12a5e9c2cd90fcac2c11b3a348240411a4c", + "sha3_256_hash_of_public_key": "6ad1d739f1598a16c608a240cd13dfaf8263d74866315e2898a3431cf19e4685", + "sha3_256_hash_of_secret_key": "1ef733faa4f2cb53cb5d8975aa6797b5f37fd918aeda02178a40584475cdf667", + "encapsulation_seed": "6b5a14e1473abf5a33d44975ca2088bd8fa6fddcb3f80e8fd5c45b9d90c24a5c", + "sha3_256_hash_of_ciphertext": "7e5fcf6be83e2dd9962f842b8f7879d6b0f61c31d0fc12cdce7eaea33bb9b0cd", + "shared_secret": "e09c8d7e5d104c0a42b47f031efe22a79ba1008efdfb80ffc7532f40fc9b3a94" + }, + { + "key_generation_seed": "4c04310bea66305c6ca8ba6b8f61ca96257a67663afc11761f13fb5c7b324b6b8aec87a9a79204cee2986867a2906eb851b734b8b22b91d6749b1a5f07c44e3b", + "sha3_256_hash_of_public_key": "9510a2a0b4fcbd414fc61aff04a8df579660d14b13c40ec0470c45f639b65a58", + "sha3_256_hash_of_secret_key": "0bcfa8078582f60e218047d0016437601da8431f34ae6da12921f53958f32819", + "encapsulation_seed": "40e593754e6eddb7f9cf176ba2d5fd1087c90ad377556d0b0f686537b1a3165e", + "sha3_256_hash_of_ciphertext": "f71047763708b3af8d9487f2b293b7e58faf7ed988642ab901eb3968b59f7aa8", + "shared_secret": "1f8d087b541b57a9d85f0737c39e73fde01db5bbe539834d8f918426a57df9e5" + }, + { + "key_generation_seed": "38a0d5f41d7dc1896efd1b45b0485634cef149828751b96087a0a6dd81b4d58aa2acf359556df4a2abaeb9dcee945829beb71185b4d6bd18b76e5668f253383a", + "sha3_256_hash_of_public_key": "cfbe9649d9d1c384baad67b91b2f3e21f2fadd6bb582a0b9cb016051dd82c75a", + "sha3_256_hash_of_secret_key": "09b118f7c4d059baf27284d127d4e85d55b84e4c92bf3127eeb318d2f5765401", + "encapsulation_seed": "c152523abd8248bed40c3827bcf0f8e8127037a55c780695e2c28ea3e041a44c", + "sha3_256_hash_of_ciphertext": "cebd6e1e4a16d53cf241c686a5516319bf7ba7ae1248368a874484e270fe56d2", + "shared_secret": "0da3b9b56fa25f4ca356d3206b99ac83fe84a09cf7fd55a33268c122a8fb51ab" + }, + { + "key_generation_seed": "97b5665676e59e3538ebadaa8cd50df1f9fda1502d9894c616a946078e56b621df05318b5f655efe36f1b678cf4b875108a18db2fa312261caf839f84bd956c5", + "sha3_256_hash_of_public_key": "a19c2c9c907b129d01cc44a95949121c39534cc98b6d105e60fe519a000cc2ae", + "sha3_256_hash_of_secret_key": "f1c00070780a7a2ac5b57ff3ff765ca75278bb661d1635cac92792f9454fe8ba", + "encapsulation_seed": "ad6466dd59f26b762fb02b19eedf5f79964da68bce0459b91c3a6ee5a7e01183", + "sha3_256_hash_of_ciphertext": "602b24bff7f990322b92fe715ed2fc790821ca44f1f83b1c1f8416dfd99ac106", + "shared_secret": "806390146332daf2ac2ce5499d2abec128137cf7db02c27fb457663c18a0d7b0" + }, + { + "key_generation_seed": "ef99224a03a85a46ef115474ec5b5d620da6795d6efcca4c9135d19958a9de62df7d92dda83e6b2ef4cce08c9134563063068a196d7b1a1a13623e48ae12528e", + "sha3_256_hash_of_public_key": "e4174b6e7542fbe80ab2bc06dfb802f691aff147ff90332d5ea739216c18d872", + "sha3_256_hash_of_secret_key": "f3f3a292f5cf01d6f7266461c9e8cd44bfc8f17e16035ab8d10af8177f389b86", + "encapsulation_seed": "1a4d5dff5847cfb48333e33bb00ca7301b144aa89dcd412ff5a3b1081d775b7f", + "sha3_256_hash_of_ciphertext": "0e184e227d7499f0a5acdadc353e8e83ad8ce17b836ba41980bc7662b0c21bcd", + "shared_secret": "5c32a0ecc8fd7e70c1b389fe25c91899c37ce4ce672e299a41c7732f4f0d03b2" + }, + { + "key_generation_seed": "b12f6fd965ea9c5b947db80fc60c83d5e232dca82e7263027c19bd62e5a6ff550f6aa3e88f7fa8a96067f8cdaeceeac90c2d0b5e277e56e9c405ec9420c30252", + "sha3_256_hash_of_public_key": "2006a70fa33ff4a65b00553734c5bd8cca0a65eb3a115d96b8aa90f8fdc5f8f4", + "sha3_256_hash_of_secret_key": "7334d4a1755e1e639b3e9eadb5996cd910b55d1de5790469f229231d3bfb1528", + "encapsulation_seed": "34f44ec2092eeaf686f2ea170591a98527cbb03a4fa9477a7aef6b41a54feeb2", + "sha3_256_hash_of_ciphertext": "cc7108f8cf530f939b4babfb6d10d61a8f1077c96fef8c2d74eceb2659a172f4", + "shared_secret": "4fc71b4d0a7cd5d80824e137b93f8356aeda6a13a55c0515aa74eef21447caca" + }, + { + "key_generation_seed": "9f52af92ca165fdc38788f2b59ba02e01c8281ff7c1e60504688043a5fe814b04f3029e1be4e1c0258c3a22ff5b50b2674cc094ba7018da2a61569845c17d26f", + "sha3_256_hash_of_public_key": "631e1de2556ae65d57e600c21e8e355a4ed586d667177ca0b7545cb5a23d669f", + "sha3_256_hash_of_secret_key": "3d4d2c680a1e6aa83861ad95043ded260e720ae80060320feffa309b4281ba3d", + "encapsulation_seed": "6250c81126572eec2da330271db36ee591f060fc7e53eeefe2e1c476c675fa33", + "sha3_256_hash_of_ciphertext": "8f755c36d1f19cf4b1d72ce3295fd7fc4b5d74165a19397bde4f8980d2cb72ee", + "shared_secret": "5a864018c4c8c17c01c5ef38d7154668d9099994d92ed2ef55f02cf6b3623fd1" + }, + { + "key_generation_seed": "851ea90fd3854cbf28fe39fb81f68e4b14345cf0d6eee7ec4ce772513df8410d1c0ec046899a777655233e4e1b5ca44e9afbdc67964bfd5d5e3dbb45e60d03cf", + "sha3_256_hash_of_public_key": "87f3829eff562789b3e19fafec92e4b5f95b45f3786f12d9c24915ca484a49ce", + "sha3_256_hash_of_secret_key": "9aa6c0546cf02085e2b3af65a7d7fd32d0f6d8080e1e7fbff6c39bcf3086ece4", + "encapsulation_seed": "35d470bcc5880872754810dfb3f2796da2fd7f397537146f6488c27804072b34", + "sha3_256_hash_of_ciphertext": "bd1de588978206a5d400f38cb7be532e93839fd59fb52dae97053163667a4895", + "shared_secret": "c8b9932975c3ef329424392da29b103f367fff38cf402b40a0299bcd2cf10fca" + }, + { + "key_generation_seed": "d304c9389cc973477f169788abcb9d511f843219d246a9b587822f422a70c2386590a2e5c7ed86cf2c5c2a898662bc9a81418720bbb632ef9cf0b845ed052d73", + "sha3_256_hash_of_public_key": "699fb2f061a75f111f4a7a60195d9045dc01716b6502cc107cbcedf122e8f619", + "sha3_256_hash_of_secret_key": "421f16805b1ceffcd64128b1296521ef812d3a8f4c5e3875a049f8de456b021a", + "encapsulation_seed": "8d667921c5db401a86fe1c35dfcf164a6bb2ab7400fd6a0b67eafd4a0ed11940", + "sha3_256_hash_of_ciphertext": "eb84a0b49f4d5e7b64227d4c1ebb34272eb4adfc61c8292f9eb035bb3a7fea71", + "shared_secret": "578e4e308f2b426a7b1ed7d80c72396a914ce77a4a7a6d16b26e4af3d59b491b" + }, + { + "key_generation_seed": "89a6e3be304a3518fb82b18ca730f0b359cd6ba90664a493fb4f8edaf965b9c3b6591121e25d64010c25a18676033e1d7278ac5f2d0b43a31f3a4156ae710465", + "sha3_256_hash_of_public_key": "d3413880d082f26986fcf452a84a8da934ed06198b290ada1789e74d9081a9e7", + "sha3_256_hash_of_secret_key": "7b546a42ffe6b65cd9c5b8857c2518f4f8e0bf835c894a68d1743691fc9aad9d", + "encapsulation_seed": "ec750b3939385a3f8df868119dc76f77ca845567ef068de6ada5478a56bc78b6", + "sha3_256_hash_of_ciphertext": "a2d61450626fbc4167a67ebc41aff6de662cd1b9d97b798b0a82adcf688ceaf2", + "shared_secret": "70080e3baab3fa55e8a308e84fe9abae780be8c09e211651f6ebf6053f8f264e" + }, + { + "key_generation_seed": "d569b935ce015c85f792f8f7fb0d83c4f53b492959361dd4f75fb764d656450176eae84d11c4528382828f7a689a0d5cff87b8ca0bba97feacb39b935a8788cb", + "sha3_256_hash_of_public_key": "e6eec2929feac2a86c9dacfa6214e2e353fda2d547c3829f5678025ff8418a1a", + "sha3_256_hash_of_secret_key": "5fac243c82807d7357a61023226a7c270525d96932162ca5c09fc8f7b9ec6cb3", + "encapsulation_seed": "74f1d52af09b12c36eb062ea7528550cb4c18a3ce8e4f4ea9fac43ae383bc925", + "sha3_256_hash_of_ciphertext": "4f7808244313ad693062e2e2a6419261801aad59bf06350dc1fac1ed50a5739b", + "shared_secret": "f804333e26c5981eb749e8e9fecbe8f8886e493cf8013a6760a1bf5d4a37200e" + }, + { + "key_generation_seed": "5cbb141c2763425c274f7404fe530d9116e08c33f9f200a20b011cf563a28990fc9ebbe336dc464489861db8253606971bd0a9008a433ed17752d04023781552", + "sha3_256_hash_of_public_key": "c74f3b7fa6e2ef8ce99508c89cf3c71d666ab065a262581a5fb01b2c9b9444fa", + "sha3_256_hash_of_secret_key": "5c6998a20960109a4c9808f8f8575697b2b8d18c44c7e9dff97585ae43e6004c", + "encapsulation_seed": "4b3a70d85f640d1a2a852fb6fe96704af56a7415a8ee4282e9207bc3a2dc116a", + "sha3_256_hash_of_ciphertext": "1ff8bc41435803d1931fc2a758d9f386083e183e2ce14394e9833a120a9d2ddf", + "shared_secret": "eb9b44605e6b8ff3a59e567c1c16c6f96b6079f8e2c4d70f706d30df82f09902" + }, + { + "key_generation_seed": "293abb6d1c207927945417cf84883ef010823e11b487ed55239e466e83696d0cff8563038aad865a817cab9ce98846ba75be9363718ecf5fea538aea90b2a558", + "sha3_256_hash_of_public_key": "7378ef967195c977d43a50d03205044006715a6a8a8263d717f40170b49e6bd0", + "sha3_256_hash_of_secret_key": "30bd5f16c3f242248a4c4cddc43508bf54535958657bda4dcf105216ddf47eb0", + "encapsulation_seed": "26e38ac804fb5b4d59ddf747715e7e6041d875f99c7b638024b4af82d622da60", + "sha3_256_hash_of_ciphertext": "4d6f027097a767ee92937a06e618dbe1a2707b974d6fe8e4fe0c72d90772d44c", + "shared_secret": "5635fed1a43168075134d0e6dd4c9cff6e85f3860b3e9f1cac06b249d62892de" + }, + { + "key_generation_seed": "74d87c7556f2671f2d666854a4d6e073e69f35421e6e1a428cccea49c37f972ce1fb7456ac0aa1b97068f452cba64ebdc138bcf5d36b0a0fada2a3b374141eb9", + "sha3_256_hash_of_public_key": "16fe956be4601573d72306a251f69bc2181253e2417e178341fd6553303ac189", + "sha3_256_hash_of_secret_key": "873c94f8bee9fe37265d5dc0c5d3bc1c706057c7efb3cd2cd5ca9ba45498d0d1", + "encapsulation_seed": "a319d2b8f114f1acd866478bcdeba6fd164dc4e37b0adfa8d8034afb3e197376", + "sha3_256_hash_of_ciphertext": "b84605835fbbbfdadef1ce96daa0dcf97d2f20310a45b4cf8eef2e4878cdc5bc", + "shared_secret": "a920df59ab127d563d7e7e96afe8075d8a7242b0ad88d72f367545fac6daa4c5" + }, + { + "key_generation_seed": "013bab0212d04ecd54b478daf72748003a25e2cb060ba6cc50bf95c292b8206b9da0c5da5f195b80fbb99c2e8b06926074f3f604b3f6195b5a5b9737876bba72", + "sha3_256_hash_of_public_key": "633bee89571e8fc16151491ea71234ab83289426559f90c67903a36e4afaa6f4", + "sha3_256_hash_of_secret_key": "3c3cff5f49a802cec693efbfc264f6a385210b1eed20f7bc5b07b51839961d14", + "encapsulation_seed": "ff646071b2509e6b75790917e08e4f0b0d9f0116ec6291c0b59eaa4b583ad830", + "sha3_256_hash_of_ciphertext": "85b2c2d105917e9c47c7f1cb5355fba0c5c90115da1ea850f8dc520980d8233e", + "shared_secret": "462f8ae0143a9173e7fb5a0b476adac03f2600fff5779cc1df9dcad9b1e7ab84" + }, + { + "key_generation_seed": "ccb073c4b90be0ad746e26fb093b60c70110bd1dcbcddb566a8cffb7b3caf80e71600a8982c350df524cde514431ded7aec23576530894bcbf0ec0bfef0bb64f", + "sha3_256_hash_of_public_key": "3217d034b472a846cd317681c0f36feea187bd40e546dc4ad69c2e67fd9d8303", + "sha3_256_hash_of_secret_key": "1503bc141825d523c9505d34f50dc0a01d7bc91cdaee6b99f4a85a24ce800496", + "encapsulation_seed": "0584270ec26f3b9818e4af074d17b2d51037cc8dfdcbe3b140fa4fed5deebc54", + "sha3_256_hash_of_ciphertext": "4a88fe4d98d08230a339829a486d94c328e7eef83df2336db6ab82da74b3ba43", + "shared_secret": "bad63432a274bec85e9c361191a3431d6f211f1fcc69173b9fb00c0517ac3b3c" + }, + { + "key_generation_seed": "2e889f44e28901e9ac7ca6b2fffcb124c8979401b17064d7e1d51a7e3c3adbfa0e145e44aae52cfc609e6f47fd7a6f6af877190ff52256d0ac5b05b89c3f449f", + "sha3_256_hash_of_public_key": "d1756ecfaeb695001ac490f36c4638151bee98d367fb7adf0e06a470844068af", + "sha3_256_hash_of_secret_key": "a21acea0fd4354eb0c78d47caaf93c9f2434f1cf2d6b2194871ccd98f9522ced", + "encapsulation_seed": "51e05c7b4ca3079781e8293f4eccebeeb2f8c8b4c59468eddb62a21bcb4ab8a3", + "sha3_256_hash_of_ciphertext": "2d8dccd1c8907f15a0f5b521a10849f836fe822729c4bbe91bdd4c631c6ad80f", + "shared_secret": "50cd9d6042e7708f347b3d187430d070f0d8712e0bf68350ab47f4b50f04962e" + }, + { + "key_generation_seed": "174aaa36410566dc15a5e62874218d7abdde0b2c0f30d877bb80b1abd5f5a0a450a7a2354f7e5cefa6f4a4e9a1c411eb9364506e9e1204a8acb3cb77fbd2c4ed", + "sha3_256_hash_of_public_key": "1b1b0a8682caf72df2e0a48513a7358edbc77a615d6be6fe2a7145be66b7c509", + "sha3_256_hash_of_secret_key": "3e214f25fbf4d1bb670a87367399e1b2a9da3491cac5a22a2c18dcc44f3f1bae", + "encapsulation_seed": "9eca0fe36c80fc5eba171c3ae66a5b1c923faa50b4521bb055e7bf51005c93df", + "sha3_256_hash_of_ciphertext": "b0e274894823b30ec50f790c7658c7b7e79d4e269c7e4a66790f635d265f72e1", + "shared_secret": "2e610015c6b440280d6f28d5e3ced35cbf65a70a171151363c4cc882d7899e0e" + }, + { + "key_generation_seed": "351fe4313e2da7fac83d509f3103caf7b4c64a4d458fefdf636785ac361a1390f072d9b5a99f9c7a0a011e4dc10f6b600d611f40bba75071e7bee61d23fd5eda", + "sha3_256_hash_of_public_key": "2c54df6e9020e1e44b11b471dea97a382a2fe8d1042565bcd51ef21cc0884d68", + "sha3_256_hash_of_secret_key": "c6bc9c9e797a02684d3ad8de47919b8d8fdbee09258d084c7a9dc963c80401ac", + "encapsulation_seed": "0c5719261caab51ae66b8c32e21c34e6d86ee4aa127d1b0195663c066497b2e9", + "sha3_256_hash_of_ciphertext": "ff01837c1bfb9790421dfff41272144b4c95498207ffb6b9a29f0b3db24da9af", + "shared_secret": "91961efe34b53285433fa9f780a04b8e47261e7a6ef77d46658e7671d800e2f2" + }, + { + "key_generation_seed": "9bc5315580207c6c16dcf3a30c48daf278de12e8c27df6733e62f799068ad23d5a4d0a8a41c4f666854e9b13673071ceb2fd61def9a850c211e7c50071b1ddad", + "sha3_256_hash_of_public_key": "bdcaf7b417da8b8933279b33068f6fda313826c2eec500b224cbe046abeb37a7", + "sha3_256_hash_of_secret_key": "c96e176b19f4135add434d0dd219024587d49fdb649bf470e84d9518bbfa2879", + "encapsulation_seed": "0e59f6f9047c784c1f00b24454aa4f1bd32c92ae7e626549972f86fab90e7e89", + "sha3_256_hash_of_ciphertext": "93199c23b5751ae8e1278d9c4792eb898d51db9b4a98dd532c5005da9c86f0e6", + "shared_secret": "e4983d6021d6c10e71f474d76650f7b5e23e02805f755f57a1012882daa77abe" + }, + { + "key_generation_seed": "d8b907b34d152ff8603b73051f772daa71eb902c47b7e2f070508269d757e02e36b817736cbc5f7b1dd6eef5fe6332fb1a598f3871e5470d440fd2ea631da28a", + "sha3_256_hash_of_public_key": "61e27e954728e2e2e230c94ff009417d7372938e2c29c38af22184eed530fa1f", + "sha3_256_hash_of_secret_key": "8baa58b1d3fab8ec5cee8841c9012506cad40bf58a677adac88f1a6400506d40", + "encapsulation_seed": "a3963ade17d69debbc358dda82c7bebe2c39d25b36813058e7a161542e3f8c2b", + "sha3_256_hash_of_ciphertext": "e9831b8bf3683bdb258faf38f7ab1772dac3851bfec7890d65fc0cdcc4bd3ed9", + "shared_secret": "807703887ad9601806d0bfd5f9d9b6cb01bee01f82500f525cf4cca6e4b21fd6" + }, + { + "key_generation_seed": "684a29e4e5480a5f2533e1526b5fac8cdf5927f3d85087c71f928c59690eb56575d12195ec32a8686d0600e45d4a7f54219b0d7a3826d193a51b9156ecf2edd6", + "sha3_256_hash_of_public_key": "672e53b28d579974d268132187e7bd72238639c6f2ca154d50d98c74096ec330", + "sha3_256_hash_of_secret_key": "4c72f0a7ef5c3274c49365cca5e6770bc709ef12bdbd4fd7c2eb5faa296cdfe8", + "encapsulation_seed": "97beafabf2c8575586487c7a80e8af5fc50f94b6051c1bc66a5ae9f66be3cea7", + "sha3_256_hash_of_ciphertext": "1892658d3ba82460b22d0b062da2a2a2e2b1756f877c718d6ab64ee2ed2312bf", + "shared_secret": "9d28091b20946b5507ae42bc4355a71acd2b5ead20d4181b22a5031ec53ca00f" + }, + { + "key_generation_seed": "d76b3573f596eb286ab5231feec7499686b13021be36cb126c7ebeb9d7030daf248c0a21ea0bb6d6f56f12300e8584d8e9a34e0e6f52227281151ae4c305fb8f", + "sha3_256_hash_of_public_key": "b86d5b13bb8b72a9fb81245ab712f0d10f0e2e09b222143c420e3f2c3acea27b", + "sha3_256_hash_of_secret_key": "c25f2e16a0e6fbf0729e5ee89fbbdd71f00ff9a1abbb00cb47f26e9989eaf678", + "encapsulation_seed": "75461decd34c50d6a094b4a64fb75e5e9479f8f9250d82bb7d729dedeb2d4b65", + "sha3_256_hash_of_ciphertext": "c4b79295e9561bf7d5143d27ed021b4e751a92b8bf22035124a5f7e2c5a13ec7", + "shared_secret": "98498206d1f4d3c94a054c3c3a6087760b210d2f3628c71ccf3f0ade9694ed7a" + }, + { + "key_generation_seed": "b87439fde81c9e39eebe7cf741c685785532c1dd23e8ef868b9ce7a541010f3d1646460817a0fce5836bdfe124a7448e7adf7b8ecc2652ac6d280e986682df71", + "sha3_256_hash_of_public_key": "85441cbd71c18717e9de7359b920a9a3bb7f32e619806f4e4718c585085be624", + "sha3_256_hash_of_secret_key": "93b65d2df33d3e3ab0d53c1d0a21f3752e2c5962f7d960b888b2a8c495b1b133", + "encapsulation_seed": "2607dcf4fd6ca1c614c21b5e37c24981c32b91c8c3e6955777da8a3f5d9c9335", + "sha3_256_hash_of_ciphertext": "869f16ec6c5c47a2ff2283f166616f16926d0fe805333e70945ad6fd1e9a701a", + "shared_secret": "c93350d1f27f771801b7c3e03a2e7672146809d37f33ba262e9e0ce7809b2187" + }, + { + "key_generation_seed": "056661b38038da4fdd7426f32a81576c73ed84843b305168a374f934e27a4e1b79238a80dcfd7c992d84b2dffa67493e669243d4fa38c46b090bdf86bc548411", + "sha3_256_hash_of_public_key": "065fb6156acaac591f1bf3ce71c4a046be8c6c55eb9a84d29569bd2b144c73e2", + "sha3_256_hash_of_secret_key": "0121afcc6aeb8be9f1c5b06d5b65cc1c03e9366ed7b85fc511d853c5eee230cc", + "encapsulation_seed": "38c89bbe7145c29e9a831c11431eb9929cb24fb4992db20737e4687d397fd732", + "sha3_256_hash_of_ciphertext": "4cc25957859ee2a08070380f4a5301601dcd4a756784c9719de99d09c12fd85e", + "shared_secret": "387e023a733bba407f35d4456a9edec3922b151c3b49c67bf3541bbee5bda18b" + }, + { + "key_generation_seed": "a1b52d871612a1c611ae0944f9e71858f35d3bd14f20e96a931720668bdf0a6b1f135cf64b6403e103afae34da038613e2853bbfc36baafa3c6a95347193f37c", + "sha3_256_hash_of_public_key": "ced77d358342759291c2bd225b0bd82d659d28a24bbc5eda8f47975b780cd129", + "sha3_256_hash_of_secret_key": "16e06287bd8d71c78f1657bbd6d5d12c22f6bad7658e68dd849d7751da950860", + "encapsulation_seed": "b2c35e33c72d90182791f0e12a0324f5b216efcab2c8da1bee025dfbe13f4152", + "sha3_256_hash_of_ciphertext": "78e964378f328246da5cf23098e6b5311f4a6dd018faed91dcc6fd5c107ab366", + "shared_secret": "4d91655d2bd77339bb81f82916a1a77329071b3919cfb35cc1d1655c2da5a2f7" + }, + { + "key_generation_seed": "952b49c803d6d6fba69f4375adce8594847a00bcae2179da49af2aed0423250262d7033947ae42ca53522a65fbafe18d3bc3e0cb66164e9a094fe4b44d8977ed", + "sha3_256_hash_of_public_key": "2fdb7c7e39ce1625c20a13a1c91aa5909d8b03b064d00877dce2415020370c72", + "sha3_256_hash_of_secret_key": "ffdb52b23a9ca4b71ec882031ebcb33a0ecc6731c13c817b24f3a06e48273778", + "encapsulation_seed": "afb7d6dc2b7eb6d84acc080c1be63c98afe7b07786b5801f716444a3e8e64800", + "sha3_256_hash_of_ciphertext": "64e3d0d521f46ff42772a91a33368013005d6dcdb72310dcfc1bc53d6b6dd524", + "shared_secret": "3d0d1ad5e2825db1f539515af2392f0d212e8166b34f3d8cf2ebe97e51785ec0" + }, + { + "key_generation_seed": "3c815e57e9233e975fa1630208aab206b71ae0db37a7a8789ac683d9f9b2d29801c8e376fdb140ee343106c093af7cb149b316ba79446ceb4e5e0cedb9b164f9", + "sha3_256_hash_of_public_key": "86bb11e7d9c1368fbba34ce3a2f169c2464ef5fbc11f73843c456467b6cdbd4e", + "sha3_256_hash_of_secret_key": "5d46659798d268f1314ad1e7c1735c480301f5877773403966e928bc3fd33d1b", + "encapsulation_seed": "28f5e9dbda122b2cf8f3754fe9e0c73a84ad4b0c093522e0b62cf815d60bbc3c", + "sha3_256_hash_of_ciphertext": "1002639fbd0211ab78f332128acecb9d75f7af4eb0d255a24ab19d7058012b99", + "shared_secret": "ff9a0d1ae64c97e4c51512b315d044c14ca34771df320ba0c16c8531d6a40d78" + }, + { + "key_generation_seed": "588760826dcfbd36d9abe6ae44a669bb3ebba6a218eab69e30f18a3bd536576e0e860576285483bb5fd36e2f944d32c4317bebc1e441470c1372046a790d79d4", + "sha3_256_hash_of_public_key": "29253478090cb4d580bc2a912645bc685061e5d4437b3811eda69c865ea9923c", + "sha3_256_hash_of_secret_key": "aadce411f3708e9727e4a7e4e198781e1ef5e8f4c4c14add1e25f5758649e265", + "encapsulation_seed": "b0d713cbef0bb1df70cbb425d1e9373e9f7790fdc7980cc96a240dfc53f1e8e2", + "sha3_256_hash_of_ciphertext": "8e8019b55e3bebe889ef989277dbeddc8632824845dca75b43144d32fea9f78d", + "shared_secret": "0e40bef57dc97b87ef89e5308f9db94fff59a475dc35ead3f2d5b6b89d24ca2d" + }, + { + "key_generation_seed": "47550e9edacb6ddce3d9ab81f6b61080dd4f2693854acb05e0ccc7a4fb6390fbf89d7d99d5c3e0d10d6ef9af054d842375f695abb28e3b8eb495100f04306e92", + "sha3_256_hash_of_public_key": "286de7dc142efe935e84b0aeebbd32d050fd9d8b008a94e59454b19ea401611d", + "sha3_256_hash_of_secret_key": "a6b53edf9efd7fa67a478456a5b6a379876c248f623ea45f4b541a8db00c524e", + "encapsulation_seed": "32bdcdb7059fe27f6409901980c080308951ffd90deffa8317b4d213a5f04495", + "sha3_256_hash_of_ciphertext": "12767f1cc2278778a66d808ae6a230428c0bd48e61fca54aad631a95e4547242", + "shared_secret": "6134219801b78f6fa5a998377643f828c19ab6fee69e7dba03c7b8e20915de13" + }, + { + "key_generation_seed": "610afb64be8cc1df288cfb016ee2f44c6c07113de7f6fee071fe0c3fe31c6215cd292e4c5f9e1a55e0489bceffb204d672a6215f4f3980a646d9f880817c52dd", + "sha3_256_hash_of_public_key": "029a2e12c3e6aa668afb5be8a82576813fac7b8e61c5a88aff94ecc2770c585e", + "sha3_256_hash_of_secret_key": "413ae41ee83e17b74ac654c2aca57abe8f8ed0409acf7cc8b301e3d6bb049cfe", + "encapsulation_seed": "4ed7c92d83bd03b2a25b567f17ae55542e2f6a4308ec0f3fe69f8ba5ae24331b", + "sha3_256_hash_of_ciphertext": "fdc158261cb75522352cf6e3208d27d0f2213a33b40522271811b2ee201683b6", + "shared_secret": "787ed075f818be6a0eae99b113dba31002097e0b85a5480003c505a40793403f" + }, + { + "key_generation_seed": "e1953800acaa85ac02a906c72cb8e8d704e8d27820345f88f71e89c1f549afcc8c64c049c6dfc0f1476cffd520b055756162f7ec94243de6b14ac0b9e5fb366c", + "sha3_256_hash_of_public_key": "e3ec3671cc7675a321af8584a0961101c04a432772431e77f5740ba3b2ef488d", + "sha3_256_hash_of_secret_key": "93bf696bf0671c3845c4b246f29701a0978eec5b49de81589009e235903061e0", + "encapsulation_seed": "060ea5d2ed1dd88144a9885e79278590821c22917b55a48920f96b53ebe0e689", + "sha3_256_hash_of_ciphertext": "b07b1e1a4fa74d847910d0769ee7a8daeeab56174051037279d371be2470c0a3", + "shared_secret": "b81e1ea69c6a6e1737c78fe18c36cfdcd26cef62deb805f22a92c49df6596c0d" + }, + { + "key_generation_seed": "c719f9b2d16399b7326ce4eca30dabefe8fdaab18e9f6df888b0a134ef355570e40771856eb77e4633504899fcb86c6a3d433d0b8d60e26f07bd61f1d4ed69bd", + "sha3_256_hash_of_public_key": "79836213a513bd4cfd42ed281304e3ee4560e4e0c60fa53781f83d5bd2bbea52", + "sha3_256_hash_of_secret_key": "65deb55fea451375ef335e7faac73917d32220fc70c95f371fdb16e712beeb26", + "encapsulation_seed": "10ef9426f8c4a13b52325c5bb4ead4596ecf2c6b5bd2d37d8350e90d4164fdd9", + "sha3_256_hash_of_ciphertext": "06bdd1883161221c5e66dfdeaf63ee8f526d63d8a0a2cd33487412b4413d51dc", + "shared_secret": "5d014fdcc992fcfcbdf3af29e8dbc9e5024f2ac41e71a3ef0ea43a063bf44e79" + }, + { + "key_generation_seed": "e9acbb774be970206c3a738e243b420805a509fa59fa902044be2f0d013650d2ded5edaec5de3bf5b4d7c2f2e18e87f499c1968993eff196753db8045e2c8ba8", + "sha3_256_hash_of_public_key": "0c2e803c2872400c49e1bb10232946ab939319e84ff32cd354dc15d082cde5a3", + "sha3_256_hash_of_secret_key": "d37f172803739d074d71a2be32125eb1ba4250128342e34b882fcba38b259248", + "encapsulation_seed": "a4bd30a64cbf29a4e290fa1cc1dfb99e68348713041e4409a1af23c5d80c15c4", + "sha3_256_hash_of_ciphertext": "0c7b5071b20477a398e4db3d83c18204c2a5dfb030f61b0d1b9434a255dedcd8", + "shared_secret": "80548d4687da93177d06d98c1e2def33fe85e770f8b871d2f74cae533f654692" + }, + { + "key_generation_seed": "c1b3cbffad4b306f9af0cdd3028876486dbe858875c9b6497fe20172a986c82b1c96249919cedc2369d8d739ab125e0d2ccb82dfebcd90240a545cdfe07511f2", + "sha3_256_hash_of_public_key": "5818ac8d7a38c781e3a0bc43d088e6d391d1d67d9639b260bb6f58a19a57150d", + "sha3_256_hash_of_secret_key": "280e4774d1b2401580216fa70fb24c2c214ac5dc7f3841710a42e14d6aa09663", + "encapsulation_seed": "f4b66a7d3b65b896dfe100b2cad24b175a1168cfd2ae11fd704b835f6bcd311a", + "sha3_256_hash_of_ciphertext": "aa39d64b106590c57b817f1958dff22fe0e3266c8d4e40a194930a4c32d2beb9", + "shared_secret": "2f2f58e23dba54d8a44ba11aa4546ee3f1819c6243e986249b7102019de3a777" + }, + { + "key_generation_seed": "ff7495b8575b5a98e4fd21fb4c3e58cbb60f14bef21aa74cf8802e3153f14807bdc370460375a778d1a31d01c42b66367ed8d9e8f84551002f552f0e52102b5d", + "sha3_256_hash_of_public_key": "172cf4f8dace8a96b8f70da966080a5e3f132873ca7544343377a99b65e8147f", + "sha3_256_hash_of_secret_key": "31136804b6c14f3a0a00a3295a5fed8d606369e64d272d432c59d7fe0ccc3e47", + "encapsulation_seed": "1d7b03d3c5eefb8ae5799dc569aa668f1bcb8c86607b089d3530cf61d6380147", + "sha3_256_hash_of_ciphertext": "9c7b7cd8cb42ab4bc9fb532405c4673f28cd41751458d7549597ed2f5d993115", + "shared_secret": "0936224ef45e29dfe9263c6237a22798e94e81a932796174271ceccb78188554" + }, + { + "key_generation_seed": "bdc3fba1c32751139fc45bacffb3ea97f26573d804a5f27a459293d95190ed8efd5a08f656a6eb8cd20679930a31caa6a6331c4b133a6838c223ef9f769f6246", + "sha3_256_hash_of_public_key": "268b6356f92c57da6dd34494b927e8764adf0ad519612ef0d1b8951e50966c2f", + "sha3_256_hash_of_secret_key": "3bf02cee24670ca40b7280d8047fa147b24c5e286dcae9c24bace9465bb19f61", + "encapsulation_seed": "554f3385b382f4a46314de37ee3885addfc5332bd4038785094e0a832e9e8c2c", + "sha3_256_hash_of_ciphertext": "12a71af24c3174eab460a2a2d2e3d27316becaae5f714e333a562dccf84b987b", + "shared_secret": "2073cea69b598292f44ebf4af3f7035e1738afb7203ad67531a7140150176e5e" + }, + { + "key_generation_seed": "447f6076a627bbc5ad7773fbfeb14b4ba9ac43a0f8b99fb6dcd5e452aa3c47ec20a7237801f470fcc2bd9fd7bea8322859b850f7882d362947432913dd068c01", + "sha3_256_hash_of_public_key": "4c6d304e0494d88d83b5e3aa5761df3b299551a24f28994d2747b2b08945bead", + "sha3_256_hash_of_secret_key": "5de91ca73756eee74da3cac78a1fb329a02f8587f212bb9bc0b29e0e654a5795", + "encapsulation_seed": "38bf0033b779edf5367d9ebc01c988af90904c560970815837380650e4749eea", + "sha3_256_hash_of_ciphertext": "b83891ce96450ac05aa2f7373675fa1dda337ab61e60098d18e8ec910d63175d", + "shared_secret": "633d9672d83a112a260b9d4c17812359b5591900a3b80424b3590b403a5dfba9" + }, + { + "key_generation_seed": "2d5df64d62cb07fe630310bb801c658dbf3d97993e68626745de39d37fbfc2b27b534537addaba4ecf14f02ab317d36cb9f0f50222ced7cf029dff8a0d3d2fd9", + "sha3_256_hash_of_public_key": "72be2f5cd569e6229f00014854633f7b278e90af4ea593411909467a03e29cfb", + "sha3_256_hash_of_secret_key": "a68ca31b91491a129af9f280cb4c60c046e7a7ccddf41c9bd98663f8512ca34b", + "encapsulation_seed": "048ea516d0ebbd9f709b47eaac66f344c571cf50f0d01c9466aa061a50b66a24", + "sha3_256_hash_of_ciphertext": "b8f3a0ec2b0827718d736ac715dcf10b514771c8b80dc27b283e6636ea598417", + "shared_secret": "7efdc40486793dcbc7c030273f8edb4178075955edbfbbf0f21b793206a172d4" + }, + { + "key_generation_seed": "25056d1b8113bb362dd979d98643d7a7ac9c4f95994c0ba060609b6d07002ff3f48a9254dd40b117941fa35a66bb50296327b725525deef70e128ca8045ec451", + "sha3_256_hash_of_public_key": "0831c75b153fa17d336a79ff6e88ddf485daf7b1b0bcf39d8df15319d52ac67e", + "sha3_256_hash_of_secret_key": "2b983d7cb50880cff761441b6a2c66b7a41642cfd2a8cc297a5df53f0ed1947f", + "encapsulation_seed": "686c921c9db1263e78ae753b1c9c2e7936b8229dca48c0942c56c6bca4f10917", + "sha3_256_hash_of_ciphertext": "df0f1e8526b97933ad6cae8d4059d2d90b292a2e084c2ef2833c89146777546b", + "shared_secret": "a33bbcff1ec8c4d3e3229aec78463ffead5b96c6c1fa857921f8907a9180af07" + }, + { + "key_generation_seed": "e4d34e12982aeeb1d62fd488d9b9e28557ed3429292239fb4f76fa9098009acae6c45c7fc62329b13c8d29844405db8ff6860de474bf727ecd19e54e6e1a141b", + "sha3_256_hash_of_public_key": "b30cedc4316b63d75b641fbad2f33241a3fc47ab8b3ee1a3ed597e5b04f77c68", + "sha3_256_hash_of_secret_key": "a49a7533c671e533deec55af218ee511c57014070e138c7059853e08c34b0a78", + "encapsulation_seed": "2387772e50059cabda53cb93ba24b19ae529496c03b36584169451525c4a0e7e", + "sha3_256_hash_of_ciphertext": "563edf14ab8095cc468c400fb06476bdcae5236e8ae7336715e8feeb7bc3969e", + "shared_secret": "bbc0b0a4782e2bb9eb19257f5b80338d1e239aa6a08710cbcba0adb974d7dc64" + }, + { + "key_generation_seed": "cd6a99396eb3539ca663a51e42063a3a262cc1c5a5fce1566f0597b52ad9fa325a3407f591791a5db4578b5972093a95bec3b8e70c1d542c9b5c9789729f8922", + "sha3_256_hash_of_public_key": "ee044dbdf6787ff038dbf9c133557169c62fc1ce2580739369aa87df00b49648", + "sha3_256_hash_of_secret_key": "9e865967f0d1e7d3f6a49f2bb623ced2a7b1408a945e02adbdca35846b70e7b9", + "encapsulation_seed": "155c29c5f0378df0cd0e847a80a07143cf7522fcd880c9229eb9feb1ce340cd2", + "sha3_256_hash_of_ciphertext": "8cc9b0e48193e9da7cb75bae6a63f83caaab5dc7ac67c5554d46c3fa1db1e647", + "shared_secret": "3eea7528610dae3539f5f81efd9beedda4d45ef0b9ff91d3d4f3561b4981d286" + }, + { + "key_generation_seed": "6c8c53ed6f65e6b2e324b84364e10de42d1c26a106d4d1c99eee79c78586fb55b9402bf02481ce4b27a52e87feb92c4399c7f2988d40e942e7496ad15ad2aa88", + "sha3_256_hash_of_public_key": "e965ac6995d525e324e8252d8e2c2da909a29b24baca8b68daa5122cb539a474", + "sha3_256_hash_of_secret_key": "91051a381626e9465fc7ab20a1944eca64be461330bda53e7d1838a74597392d", + "encapsulation_seed": "a9cb9a61a3324b1ea5afe693b32784e2871096b2ca14a11acc9577c52359a241", + "sha3_256_hash_of_ciphertext": "3d06ab179441a0fea2b05281871e6d19578a63fd1e161aa8c4b0ef7281cde2d6", + "shared_secret": "c5e2f0c1ee4f13fa8fbf2d09a78b04a5e2069aafa978598f96424acbcf41ad44" + }, + { + "key_generation_seed": "2107204cd995f1df14314d5381f8c5440f09a347502e161cffc0a2ec3dcfbc7324c3da70fe850e80aa818301d60c70f3038153866dcd5d179e22db59b8991bb4", + "sha3_256_hash_of_public_key": "a3d8a85f38cfda38c66ae39b2f9186ef7bc1e0c98e8976a6cbc6c4875d73d7fb", + "sha3_256_hash_of_secret_key": "cf7e797f8f7229a08206034737e54fe46645ab2fabdbfc8662b45a2604876b65", + "encapsulation_seed": "e99fbae8a024ebbbdcef32ce213f6aa942e3eca925e5da4c09975d773b33a175", + "sha3_256_hash_of_ciphertext": "4d2a2c762d4fc69191a3a936f0d918a9891ccb72d4a0bfd2de80d27283cbe88d", + "shared_secret": "0b642d9427caaab926dfc155993bcb41bfbfb91e6c6d1c4165d8750222cc3688" + }, + { + "key_generation_seed": "63a925685a8ac5bbd918faa33ac397d1ffbcf99135d9da7c3d6ff7aa4c50af3d3afdb8a246a56ee71465591831c371f2eb87467b0559dedd776ba063ee6d2f93", + "sha3_256_hash_of_public_key": "aa73b40dedd61e6fdaac86971965c03ab14ae69e8130426fdf830bd57d0974ce", + "sha3_256_hash_of_secret_key": "1e7f3f1e5632d1df538b564304f56689742d1f652d8d32f019b45183af68a20e", + "encapsulation_seed": "67a216f37d67f5e74f782f1badbce1cc8c80a6130aec305b421899a4faa0a6c3", + "sha3_256_hash_of_ciphertext": "4c4ecacde036488a6b2361b1b976b86e9b63294bb7df100e35dd3246d423965a", + "shared_secret": "79462708f754bf842716f900e850c228b00996b0e3801b43f6a928bfd4aa9e50" + }, + { + "key_generation_seed": "6a1aee5e708c1b47f02bdacce4f56c860f74fc7cfec1ef3b58285b1c8ad7fec2230e05b7114ff0395cc6634db1eae8258072d09c09f291e92d6620b177dc50d7", + "sha3_256_hash_of_public_key": "cf754f2ee43694865a09ca7beb0deda9b1328fd0abdf30ca5c338e27e8be04b5", + "sha3_256_hash_of_secret_key": "928592604aa44df8f2072f26e9511129f61da0b7f57acb3f6896635a9764ea87", + "encapsulation_seed": "52b19fea232c9154a3e431e9d69cda40013cf2d485c3cd027ad24e645420420b", + "sha3_256_hash_of_ciphertext": "e7c673bdc9d9dc543754e0673e9aa66ae3f4ee7b1efb5f4fdaed89794ea77fb8", + "shared_secret": "bf535eebd3721e4d832fa4c11369808a154faed4602220b4070b78900b008358" + }, + { + "key_generation_seed": "6396b328b100e4c7f4bcae69875edea1a1982421558c608c13c592bf7b5d0fef1100ced48add211a5c937b8d6079d8e271af3f949edc61f70e60453aef20dea9", + "sha3_256_hash_of_public_key": "3a842153dee9e035299d7e268c9492d71188f9fb24bdc2dd20c1ddca647a1523", + "sha3_256_hash_of_secret_key": "28ee987bc4ae5a321d2669950dbf87596fc4b35c29f192836005064aa3dadee1", + "encapsulation_seed": "64440adb05db3308b189bf999f9ee16e8ee3a6ccbe11eebf0d3ae4b172da7d2f", + "sha3_256_hash_of_ciphertext": "4fcc1ca889f0792f716e9ed77d2794b9fe34531dbfda86b01dc6088c21e5c87a", + "shared_secret": "3602269c7af9fe28ea1019208dc3ab33ba09239d435c509903ca67345717e845" + }, + { + "key_generation_seed": "a453bcacdd2b0d4646009e5ed451c3c45f08fb827ef733db3c517a9dc1af93e67a3cc8aa3239d4c52ce4c95afdeff6efbfacac10d294edc0e7cf4535059bfdba", + "sha3_256_hash_of_public_key": "da43cae3c4da51d69a57eb87094a03cd3a9c3e6b4ed864cc691a60f0509cc646", + "sha3_256_hash_of_secret_key": "b204cd1c3122b29a3d99cb77e11427fc102375699928c5a6fe816f96bb212627", + "encapsulation_seed": "c8bb46b3a7344ad170c2052fb042b5a3b62e0590562ee82577b1081f6f114d16", + "sha3_256_hash_of_ciphertext": "d60b51f94c4de5c221af1e05e7603296ed365913f90e061c686c6174aacaa891", + "shared_secret": "5e039d591cfb128e51d0804e0993084fc92b0bca9c0f2d84e24f5fea5efb3b8b" + }, + { + "key_generation_seed": "47ca2b77c5b717f423222c2730ca5cb9c856bc951d01b2b2c80bd76ccb5539b78f1481d7cab000e33fa07de8dc9627a85e76fabb4428a3376e66300cf12a0787", + "sha3_256_hash_of_public_key": "6533c524a32345eefdadc74a3c6ad7e981832797faf1068955b79f118dff9358", + "sha3_256_hash_of_secret_key": "b9dee52055b1f9a2b25a0c1be4d9f30d2ecd7c5a09f0f5294de2d49a55ac9fe0", + "encapsulation_seed": "2e2b70609f3fe029a14d09d5d659871ac776ce2797a0355f16e2eb68f5613fd1", + "sha3_256_hash_of_ciphertext": "9319ad69d886668892dfdd31b359f6abfdcac75ee600875ec47389d1d63d8c49", + "shared_secret": "eae95e643381df7a1ca1954eb0d529db88a8001b8c1ed98a4b055936bbc6c038" + }, + { + "key_generation_seed": "aaf6eb40e596a5e3e8218871e708b089240dcbe7fd3641f0e5e41e071ce49107e2f8d320ac3cb0c52efdc753282f092bc39baf4a18783a48ea031a191865eb78", + "sha3_256_hash_of_public_key": "e2f60f27da7f318eb94a74b437f8e0bc9513e9bcc38dad99c174c1d75e0145f1", + "sha3_256_hash_of_secret_key": "68eaa8143a71bd5f6df29b128781e3f2a5fbc5d20534afb223ddcc64bc767f5a", + "encapsulation_seed": "4725dd8fb314bfd8ee23731c2341dbe114606d9abe6434c471b5573e7df193bb", + "sha3_256_hash_of_ciphertext": "fca5c4beeee972f426128385dfa08f2ad7491e42bb9f414def44f0f9d3fa962d", + "shared_secret": "dfff52ca2afc33401b4f72f5e6ab5d9bc21c08a6843ffb2ced24775d786b5a0c" + }, + { + "key_generation_seed": "6500f32c93415cfdbc0bd31d78d5be95cb9060c8cfa2013955b56f8b6868b322393308641a9a4647f230201e1389624a296b55192a9819fcb19ab77c25f95445", + "sha3_256_hash_of_public_key": "d4bf608793939ecba27dff5889d4d921c583999a57e20a48085ac549573e6abf", + "sha3_256_hash_of_secret_key": "5f9a14a9c41fc228306d79417015408f31bc9c3d97579616bd68a3d3444f9bd2", + "encapsulation_seed": "818d3bb8ebfb32bf464775f7139bac0a5bddce80ec5798595992f9403002cd5d", + "sha3_256_hash_of_ciphertext": "bf911b0acdaf3ba97192e847cf8327696378bd4aee452751432763e7e531aa8c", + "shared_secret": "c870a7be3dc7bbf5836727e5bd82cf977b1332e7db276473e4029ed95204acda" + }, + { + "key_generation_seed": "7643cef2d62cc5aaeecf754653ea62294cd2208e5bf3ddeea209e3dc45373d49eac9d531a532770837a854b4f5531f6e0c8d6c10183b30d3435498c2dd142951", + "sha3_256_hash_of_public_key": "65f03add3941d22c80d50659f501f8cca1b448d84462ccb93d5f065889484bc0", + "sha3_256_hash_of_secret_key": "e4513cfd1dd2153d30d15b023421cb8e8456e6a40e612847e1713e915a29a87c", + "encapsulation_seed": "c92aa5fb91c980d9cade9ce99d4c75b2ffa7d6a6ff9bd59def1aa701f2a0992b", + "sha3_256_hash_of_ciphertext": "66d912705b35909a378354e283482cedd6e3391f78985e9e75e7639efa3268eb", + "shared_secret": "3a14474e05df3ff59c6105b1f2af004a8b8164dd049eb6929b9c9881a873ab08" + }, + { + "key_generation_seed": "f8ee95521060c03bb8dacc79f7eb7db640f545f315613a35d447a09e504cb4e13fc3d8392cb53f36ed647364a04e37278a0e0a45b720f4a75c580c9920eba98d", + "sha3_256_hash_of_public_key": "b8a3b8cf4709204a2fdb19889b0022ea655dfd58ff27e17d530510e1eef45793", + "sha3_256_hash_of_secret_key": "1f7cdadf3d4707efe1b7a6173d8f7b8a9f864ab388c3271d79ec424d9da3e896", + "encapsulation_seed": "7e8086a01dc5b3bb9eda25bcc45d27f99874841b97237968495800e007696ac5", + "sha3_256_hash_of_ciphertext": "02f5530dd9feb48f4013f496bb9ea98d70ce40f75862ca68cfd1b68e2da13a4a", + "shared_secret": "2d26f4da925e01caaeaf4a89f5ce81148a000f7a36dfee74e059a933d973ddbb" + }, + { + "key_generation_seed": "b8bd0493a882e3a49b4e0f6256fb1fea0912562fd9ba26ec3d6c9cc12c8973abd7e4b5d8021c486b9c3114d7cbbeb7cd49eba8a61bc2bcae1f1bef30a1daf76d", + "sha3_256_hash_of_public_key": "46fe6c37136273736ccb11df5b6d55debbc087de802404b72a003c5e8c809719", + "sha3_256_hash_of_secret_key": "3177ed170e84ff15fa1e744adc9ce806e431a68f15a7a026c6092bf593dec6a1", + "encapsulation_seed": "bb321ef14d44d8698df879fd52450567657f52a2df8d111185dcd7d4f30a72d4", + "sha3_256_hash_of_ciphertext": "d65323bd9209bc4e9e6b6ad73beaf3871707b3af1a5cb460000f1cfd47c823f1", + "shared_secret": "5012217b85a1e5dba62a2476d7a441411d8ecddbe2d3a291f658b7dc1d1197e9" + }, + { + "key_generation_seed": "c0407e41ddf48d333978b89bcf2db01e4613425b456249e76a6f25b8a2827bf5b2dca81e3f5f748d23c9d356a2209f6b2d60247b2e45c9808de497f64f124643", + "sha3_256_hash_of_public_key": "a074ed1f76e97d68434ba4af2af0e549204222679e9e643580c35af3cdd247ce", + "sha3_256_hash_of_secret_key": "8f9b3f631d0fb04477846ae09aea725f1cc65b2cdefe2108cdb399c36db9b487", + "encapsulation_seed": "210a423dadd899b810f011794b79aa7f860823ac1962370e791287d3a1afa384", + "sha3_256_hash_of_ciphertext": "bfcb6519e71a36787ec2f499db9c29a491e67c1a9765aa1824a454f77b1cd7b0", + "shared_secret": "0dcecac51c5261e34023d2e04146ccea10a0b39ae7edc50736127184fecfc8d8" + }, + { + "key_generation_seed": "334382d39164d1989696a2ff77b25a28af8bead9883b5365eb6fcca7c1781cc9aba5068af837be962f439f233593d193ce5e08f7d66efb3389885927b89d2523", + "sha3_256_hash_of_public_key": "26659f74fc9ec372fe18be4ed6aa28b7cd84ad1c0f0115dad011a11d20fda9ed", + "sha3_256_hash_of_secret_key": "5e3f83cb08ff80183879af9ade3631bed2a468e429ad027a5afeafd9a6f66362", + "encapsulation_seed": "bc856afe24213e3d14c3d6f9b89223bbcfb2c890722d770fa3492c1e46d1c302", + "sha3_256_hash_of_ciphertext": "e3abb3ac5b54dcad2611ca640bd314a976ae0ded1a8110dfa0cea536ead85372", + "shared_secret": "0d403f00c22fab72899d302cb536854b934446b62f9da6d9d6cf1c0f7abac2e6" + }, + { + "key_generation_seed": "6995143e8eb8a6e93840f76eec844f67d2b5f75b1839a5040337e61f9806764a0f4dff8e56f68440836a072412a30d851ace2c7c6f02d60e7a8420001a63e6c6", + "sha3_256_hash_of_public_key": "2ca3d8ad2dab1dd8a2f4320658fe6eacabf70d907920593919119cf374516336", + "sha3_256_hash_of_secret_key": "2798448395f6ae3223550e7d5255e6a605b430229f5809b6efd0683a6b9ca402", + "encapsulation_seed": "5fc00f89563e44b24cd67d0ce684effe5731619fd08e7d72e2406eb016afb66b", + "sha3_256_hash_of_ciphertext": "547cda0ec8f19690d1a8bdfb7bd8b822872ffb0c321ff096e280abe1faaa45ef", + "shared_secret": "8c0346216cc65f95d2e0caaeec4bffaaccc49132cba2dee8f2a6ce1f922e6db4" + }, + { + "key_generation_seed": "995eff7e0d195c6d0533f3dc194d47e60f9ad14696144cde694d60a95f3e96b4b28f7e7a15a005f92400ce33db073d49b53871594a88fc45e0f94207b5f0f2dc", + "sha3_256_hash_of_public_key": "de62eff56f6b49a156d065d85eaf0aa21ca229a20fa4e1372a410ab1c4ab6e7e", + "sha3_256_hash_of_secret_key": "6766cef3fe644a233caddf208074b58e6e83f8a78aecd00911c29a08f6f0b0f3", + "encapsulation_seed": "ea22a76065db4b565ee1807fbd813b43bde72b0e08407fb867c6a18995025e50", + "sha3_256_hash_of_ciphertext": "f1d6c83633f244e683ea018c1d70818cb923c2c312cf03ad81884d4de4d29cdc", + "shared_secret": "35151c059e5220d4abab9380fb0ebfa061148e6096d8f5678e4308cba0226261" + }, + { + "key_generation_seed": "3e809ec8dd0fec0d911a4e3fac20f70fbb128c5de94dc7184ca7310ae9157a98d8128601c28b1def8d393a0db283229f7c7383152a814e7cefe8ef9d9768c473", + "sha3_256_hash_of_public_key": "66f161d27dc34e1a2f4b98b14a2b221d7eae26a593bfe432487d9994cb480656", + "sha3_256_hash_of_secret_key": "2237f6cbb452d375878b82c474a7c948ff587a5f3ed02bbba1459fa7ff8ef802", + "encapsulation_seed": "e9602b34fe73ad57f4bf6ead99743d645641553a5b9b9bf2e7016629e3e9bd76", + "sha3_256_hash_of_ciphertext": "a4d6fafba699c00c8fc8e453ef1eaf47a7432e9c7f413468e126b24b458d6450", + "shared_secret": "fa4c0c7c5bbb803acd4ae91e49cb8cc659a94490e8b786cabfd9b92e949fbb0b" + }, + { + "key_generation_seed": "dbf1c465fff3d9f783bd9ee61a573715e45691147b8904439b5ffaa64f94ff7bb6d75eac6c76ced1b0a025b40a55440712ad8424672e761e9bc400d63812006f", + "sha3_256_hash_of_public_key": "7537e68ccf14e8b7e57090d8f648529dc461ca3950288879e88116acaf57b4a2", + "sha3_256_hash_of_secret_key": "bd8e44337eef01251217c4702c99232c001b33870953473d83a7486fd25484cf", + "encapsulation_seed": "f72b9080a6c051bbdb9b0abc1949034be0f89a9f73fe277ec4d4740c78d04a83", + "sha3_256_hash_of_ciphertext": "013f4b782480c4647feafa3bbfc91e84fb391e0545168d17a0a7845e5ad3f0bf", + "shared_secret": "b7a8e7b3c6d244b6b0dcc45947dc91f795ed21a5b5ad545205cd5b210df37325" + }, + { + "key_generation_seed": "1f7cfd2b70863154e8a69d1758532e86c20cfc763d67c758bd10a13b24e759b5273b38bddc18488024ec90e62a4110129a42a16d2a93c45439888e76008604c6", + "sha3_256_hash_of_public_key": "82f68b15681cca5c2852c18d6e88bcb102a059c1d21936582adb71790cc0a335", + "sha3_256_hash_of_secret_key": "fd483ddc211c5c27f453bca56158e1f8084f075a7b06f5098cc3204427bf8197", + "encapsulation_seed": "f1e5542190db8ecf4b8d617a04fd3783ad0df78bf8dab749afb57db8321d151b", + "sha3_256_hash_of_ciphertext": "3c150f0cf10ad27bca07c75961d5dee46dc8db458fa6b79ddb3de58e50433017", + "shared_secret": "70cf00481198a97f14e0870c268ca55b6ef787d130a4c32314eb7c0a531cd188" + }, + { + "key_generation_seed": "3a19577908efd37697b8edc7fdaf47d1bd3ad01a1b77faf794bee5b9c3192a6fa3729672816f3eba84c9638a79676eeac0f22c8a48e0c5d50a26ff0844c66b99", + "sha3_256_hash_of_public_key": "104fbf09445794c0ea0654f5caf70ee09d51c8386d4e1f467b10633c710ac2a4", + "sha3_256_hash_of_secret_key": "73fb93953ae666a9df1bf933ba56b8655ea9e319c0110c78d49f8480ae1aa3fd", + "encapsulation_seed": "74efa414ae171bf60b6f884cb7e5ce12028f49365daccfa23e845d551711660b", + "sha3_256_hash_of_ciphertext": "24cbafde848a7049b17fa7b911a925de796344a95eb13e0c4bcbf239c28ca964", + "shared_secret": "2e8ff9c053137ca6b6c31ce8ba7f14135a7e102c211e68eb99de12b94273f9e2" + }, + { + "key_generation_seed": "ae0f65e29f38804a6759f70f4d01e2aaff7fe1c91ebc4f892dd0de3ab2e68ea5e03ff73e02a217659f53d8c47556bf3d8c94040f630d63605e2d0f923579370c", + "sha3_256_hash_of_public_key": "0f353d6a29813d354471eb8b4c38df93939eb3b1db80ddd1cdd6558a9f2687a3", + "sha3_256_hash_of_secret_key": "8a9edd6278707108652f3a5bc244592cb7a82c24634583ed2d3eb6a176b216b8", + "encapsulation_seed": "0b4c3cffb2ba4380ead13dc0d8acad2356b448a810da1df29f264c44aab6d24f", + "sha3_256_hash_of_ciphertext": "5137ec9d55a3187cdcd24ba553853cb6182fbd07d39d288cbde158788c23270c", + "shared_secret": "ffe9448fe824ec92022890969f1fa8e2fa87e5b3e95a9a155839177cd3c8e359" + }, + { + "key_generation_seed": "6084a235f79dd093ef6d185b54e69df33dacee73a9bf2f379004421a10e3a79d9f684fb055ece19459eb464e91e126a7a6e3ed11ccee0046da234d964c985110", + "sha3_256_hash_of_public_key": "12e89c47142418c26396ef0174c02f69dc00022d56494d31af935490edee6385", + "sha3_256_hash_of_secret_key": "bc13b19f01d4cab36dac2154e0fd8fb7d2fa012596363942847f1b0bb3715f90", + "encapsulation_seed": "1c82471dcdfca3a6942061ab4f3d5bf0d197321437c706d9cccccce449447002", + "sha3_256_hash_of_ciphertext": "855b06dfc24eb530c82c7a70f12b04748027d7fa29de97d7d3b73247b16dfccc", + "shared_secret": "e3f110e7d74400cb476b0e34141a107d874986a3732ecc103d9bdfe76bd492f1" + }, + { + "key_generation_seed": "acd1c0217fad5caa4235544dd9de153ab1880ccf4c76f16f236fae4e4bfda04cf03a8abb0a5010f400ae5722a75bdf5a2f6d5b546b34d73857cb1bfc7e587aa7", + "sha3_256_hash_of_public_key": "2fac52ca60594e514333ead02cb1bfa5cd1d9ecda4a0b25ccdfc47ad3f632a85", + "sha3_256_hash_of_secret_key": "2743b7a9dd83a6b9bb5c2685f28b5629b2e31132ac64788a0929557d3449dfc0", + "encapsulation_seed": "46fe60a18124125ab93e0c578f1c02f1bd1301595013001c7f3c2fa56cde294e", + "sha3_256_hash_of_ciphertext": "d9997372e3853eb89c704d0673a526c464b1f46c27d33a445df1f6ae5a90511c", + "shared_secret": "e26737292d1a1fc6772d9c14f9d74f0f4e830c0ba04253aeea21e69830a3a360" + }, + { + "key_generation_seed": "241191401a63afa750f05662e354dddbc683c776ce3222beb83e3cf913d7ed7ca59b3bd23b49a95bc1fad20070fec930b6060bd827d742b077092e422268e15d", + "sha3_256_hash_of_public_key": "3eb856043b822df9d60b55fccb537afa3cacca9ef50433bde1dd9831e534d192", + "sha3_256_hash_of_secret_key": "398ae3423ba5c6bb05920e83e8939a104c3e4ad91647edc7db1667efe438cbfa", + "encapsulation_seed": "52fb7cb6a633fd2e83f2892bd9441b48fe59ecee6d026f5246fa7f2a5e55ee3b", + "sha3_256_hash_of_ciphertext": "dd3e23a09b448da3e15af8cccef9eec6de397b91e34e07b23171a3a93535709c", + "shared_secret": "1d8db19740e2f6ba7c8c04216cf2398fe9221b2404addfef8996a03ec72ead37" + }, + { + "key_generation_seed": "b9a6b0c05677e957d41a34ba03bd06f2a9092e31f63389397d7e70fde6409d18e99c0e7b82be89bc3c1eaee6680aa4efd394e40c2b3f30523c8117f7c26a8969", + "sha3_256_hash_of_public_key": "306aed2a804a1c9bad4ab9e59f6126ad7c8633cdd0c2dd9d4c6f639d312ed47b", + "sha3_256_hash_of_secret_key": "88b28cf6fe19424ff82fc2bb096423b71f0cb8cf985af31bc15ceb4ed18a5e62", + "encapsulation_seed": "0f81a5f97082121244403da3feeb734f6084b314b8d94beb11627aa6ad1914e9", + "sha3_256_hash_of_ciphertext": "2183395e1b660eae184599967416ea85e0ccbbc7e993b384063f1e91086b27cc", + "shared_secret": "cb0026c186440c0744c1c1c708d8fbf2b7aa1126792a7363576bcb2e64384117" + }, + { + "key_generation_seed": "28a96c71577ba00c94f99fe965bc595a26db2b3ca6ab5cf8e443cdd8462b17929c35d165453e5fcdc6f9df64526d9de698f2bd3e6bac6c7fdd86601b9ba5f4a5", + "sha3_256_hash_of_public_key": "9bb3963cc1c5cf2b2d1c6ca76226328ab765a79999ccc71fe98d5bf3b34f51b1", + "sha3_256_hash_of_secret_key": "d8c2492023fb1175a84c19b3ce20f03dd12b1c26b65176d5582c319124bc0e24", + "encapsulation_seed": "31af9345365549ea0360169ed57daf98cc5444799d4c75d9f1f5d615e9df8a91", + "sha3_256_hash_of_ciphertext": "7f8fc45748528ca80816473c46b88d268bd8c31d556cf211ef41e362e63dffd1", + "shared_secret": "f855334cde90e9e858863dbaed7bf4c45bf189861750eaa59355a3648b1ca1cb" + }, + { + "key_generation_seed": "c08ba2ef8c3a0a043afad931652d7a19e6e8cb670f840de5f1fa03309b2ca9ec5fe6141a25f7ab9f875f79e0a82d6ea5cde5a017ab637d5fdb7c42646a1d71df", + "sha3_256_hash_of_public_key": "6d029bb2121c788b5b6ead7226df664490dae362c4befb615717d81c656b3273", + "sha3_256_hash_of_secret_key": "0f2c7bd16d9289c3c27136df0cb6ebc624e80144cb92e6f0c897f58a53617ac3", + "encapsulation_seed": "774ae54093d694ef40b63b62c73e6c98295f606feb8699807eda1d030ffb996d", + "sha3_256_hash_of_ciphertext": "fdfbcedd46fbe70f8892f641c4e84f9b48d1c33ddba29e126d4236df17448ddb", + "shared_secret": "3030433313514ea95b5d6a2fdfb64d4225fc84eb70336323507aed63c5755481" + }, + { + "key_generation_seed": "0e3b30e102d707538c2671060f603bb0b8a014103f132d63b09ece07e4a4c75b11eafeca9e810796c34e8cfce9d59342884456007b01ddd12edce6d10ed87e4c", + "sha3_256_hash_of_public_key": "64c819d9bf66855f6ae70627f04da8378547e5867e2eb9759fe0971efd601c4a", + "sha3_256_hash_of_secret_key": "e85b62236d5c6c691a9076dc58bd5da80999eccc8df973c7d0e7e65d8465ea7d", + "encapsulation_seed": "9f27a47604ab5146caaf0aafe6d149424f8d66e39ba3baf5e6c73b19221b7e21", + "sha3_256_hash_of_ciphertext": "a38d236d52672c9bfb9533ad1e106544b0e52436ff622a633a1439bcf06fcf6c", + "shared_secret": "b8b9f3af55e8c616d07c7042ccc26bb4b83d20fd502ba5cc7b72310990ec50ec" + }, + { + "key_generation_seed": "2478f7d3de6041e7e5cd11c5e2ef483d1aa6218eb126444091535f6ae532fa7311136e2681df2ef881b51a092a9badbe72c9772c169808521c47149578621e28", + "sha3_256_hash_of_public_key": "db315cafbaec2f8a0142f45affff65289e826c9244ab1cb03f9f65df3e3cbcf7", + "sha3_256_hash_of_secret_key": "be98d62e4724c0d960ad4839298d4571f9871033b63bdf10d3b0e589db376ffa", + "encapsulation_seed": "90044031b7597b5e60a4f946b713e8996d0426d2cb013243d9b7d8f8ef159a0f", + "sha3_256_hash_of_ciphertext": "4b32c8d47f6ea3114114f8569dedc246e5b7ce78944273848f24eb8a5e2a3b7c", + "shared_secret": "2e4139c499a24caa334754e10c6bbddc7a0830499ca65e941af8d87ee022d483" + }, + { + "key_generation_seed": "9d405d3ebdaf35fa8722de431b669722acaaea2fd10b814310b17f78b66147d16ceb14f7662be0c42779459f69a145c0e2ce9f0bd9a0cd1bf32ed5694cc9ae32", + "sha3_256_hash_of_public_key": "c8d853e65b5b118e28b7cb6f0d5d6f282e0ea20fd72f3690a6b232b20a8a55ec", + "sha3_256_hash_of_secret_key": "7a5e854bad628be7b99f524f52a97b0959c0ee67a7a10ad24b970e6e3aeeeb80", + "encapsulation_seed": "a7a31e140891ea37d2b6424b59b1f84f89220f32dcb73e037eb912b389d34a48", + "sha3_256_hash_of_ciphertext": "24cf6d09fadeca5a23052d0239f954714bcbc47973086d778a5d2f3c47964e82", + "shared_secret": "e412f3aac1c0284d999d5a7f8344b4053d10965fcbe1638f7ef666ef29c521d2" + }, + { + "key_generation_seed": "9a86490f0615f3edf789cb0654066e9ee339cc59f968281f3b89213f83c692edfaeb2ef44d2f608621e831187ce79b2d2f4a20f1568bbe76b0d3d5af36111714", + "sha3_256_hash_of_public_key": "f69bd52cb1d071f1cc7720f949d44f66f40c917eb30f3a4b0eb519ecad2d03dc", + "sha3_256_hash_of_secret_key": "b6ef04e6acbcd1bb072d1cd28412cdb00ee40d04ce5b39442a2efd6756292167", + "encapsulation_seed": "70eb3f791faa91f1f982fa477dbcddeb2c55691c07f93b04cd31b37544c94b42", + "sha3_256_hash_of_ciphertext": "0044243b64d889050a71bbc15d8d7e619f443d0d6c3b28bf05f7811873434749", + "shared_secret": "4f9de6e4cbd9948d2de9250654d5db97ffe24cf222f68ba51d6261f02f4dc5e7" + }, + { + "key_generation_seed": "6dfd9b575872560c7bdc2732c4a28dac4db04e535eb8e402c3dffd145c09ce47a2985c1c4d203778597947d710dec806e36b0cd949fe460ef141213bfc525e5b", + "sha3_256_hash_of_public_key": "10e01965f9c196d2f5f90ce3ce8f552f8a0d76ba8f5345365392febc50560012", + "sha3_256_hash_of_secret_key": "2b5c6d5fe9b09ab5a027522e699401223ae9d304ac912f1b15f0f647dd9a0a7f", + "encapsulation_seed": "30f4095015ba88b6d969672ca3f438c395dacf7d476ea7a9e805ce932d270a13", + "sha3_256_hash_of_ciphertext": "1464bf8d08a52ae4b5b5ce35eacdbccbc845350d2586d17d1ca3ded460b36e98", + "shared_secret": "da6373247f33971b39d955418425ef1d1a233fae7fb1985c0126f541ab8d58d7" + }, + { + "key_generation_seed": "6fca9f4e384d8418075cc064c70730801bdb8249899d456a77130d5beeb3662cce7683f8a03d3cf04e46970ff7d6a12494ae12558346dfc8fd9370bf944a0102", + "sha3_256_hash_of_public_key": "7c3991fa7983d0dd6e7157cfb152538466e9d5c3998a2b8ed862162b91ca851c", + "sha3_256_hash_of_secret_key": "72e786018ae9ab8293fa51cb7ca3ff0435e7cccbd5ae02b4680b92c148590265", + "encapsulation_seed": "cf31220f44de862e1719570e1b26e897790159366a385452334fe24cdcae28ba", + "sha3_256_hash_of_ciphertext": "feb0875261b62b026e02265b31a433c75a4330a8af26d3e5f4a8fc2e6bd3a482", + "shared_secret": "037452d74a46b60f415dad3498adbac608dcabe4edc7070a358e7325c72ce76f" + }, + { + "key_generation_seed": "e58f71bf175c0550a67e00e0f7b3b7fc36bc2707bf0c93044a492626de36301a7f7054814869cf7625e45647bc1547aff288dbb90699b2ad84893f3b755d9722", + "sha3_256_hash_of_public_key": "8aacd8940ff6fc27f175342be74d48075f8ae9320cae20a41c879c27c1bf815d", + "sha3_256_hash_of_secret_key": "f7399dbf35fcc57a9bff87b0087755faa75267788cd0921b9ebc5cde8b656271", + "encapsulation_seed": "bb5e65669a44e5d5c709bafa98c16ccba6ac2c4ae923334f69a11543eda64f5d", + "sha3_256_hash_of_ciphertext": "8009fb9c48aef553011af0576568351db3776b01def04198874e1155059c3006", + "shared_secret": "2cfcf5fa2b4c0aaa85c6069616e19e6715fec913592d6b8f57eba9cb4e3b162a" + }, + { + "key_generation_seed": "e3fc575ed51513e62aba655d24cd9c8f1c6c848aaffa946c49a53ac3ea59e474d82c2f1bf2e6aebde5660fa73356982e12999d8fdafbb3cb186341d0386dead0", + "sha3_256_hash_of_public_key": "149e0b6b49fe8adba1217c2c57c83f2b8c5f1d92f319e502b184a65869214f75", + "sha3_256_hash_of_secret_key": "6dfa4d29af6a0e8413d5591339c15d2e2cfac3f502f49acca3efb53b53624666", + "encapsulation_seed": "9ddb3aa9c7905d1a438c93bcf78e3e321813580371ab4e1289e2dbf3701972c2", + "sha3_256_hash_of_ciphertext": "0d379514e14f3cc9d1ce5804db6f58868654a604177058d3f6431ff1f2be431c", + "shared_secret": "d3551ed0fd716e4887c82a6f24c5008ba80a9b5c3fc9e50f188eb224be8102c8" + }, + { + "key_generation_seed": "470b4943f0fe7fd0d8ec5185aba0d1db09d112934e4fb4787e2bbc6b88466e7b8b2809fd40008be70a6b184981101724bc3d5ec5e1956b510b82fd5ad0668a5a", + "sha3_256_hash_of_public_key": "29b1bff7f12eda28dfedfbf0ac16e27008c9fdc62c35e53b28a312bdc91c40bf", + "sha3_256_hash_of_secret_key": "762a61eb847c017ece920f51d5da7a9036ed8b835bfd7793527321ec635e2fd0", + "encapsulation_seed": "26d90b190a6c3d0d9a86cf66005154e7086749e966e7187c249ccb9329fd3b8b", + "sha3_256_hash_of_ciphertext": "a9d77d81f4a1db5985bcd6fde4b68c8f65470f2a48ecf99d6e11b6a31ae8d26d", + "shared_secret": "03139cf9f20fb6bdaa4ee906aeee834815fa924e05ed7e7e3bfb432aad944d6f" + }, + { + "key_generation_seed": "6df4385db978d27b27d2aa5e452e4152b36f097503d9581ac3390105c5727e7dc95fa08ed106ce84660e8a4c90bd2b22634e40769aa0090a101c5dddad45edc5", + "sha3_256_hash_of_public_key": "b990059e901097d00e0ebaf40c5d5dab009c66798489d357e760478ce884cce5", + "sha3_256_hash_of_secret_key": "37a044795bd330e4dc60a6d84bc6e99664d1be418b0239661d2ff16d1501573f", + "encapsulation_seed": "7db6d1a129d6123f1f805b79ad3b413012ea86aed42a05e98e7b1f32f9fbbdec", + "sha3_256_hash_of_ciphertext": "20f09e737440c30b8119448c19c43127a52070265752f2f859f110d136daf470", + "shared_secret": "f87cffe1a96bcded4fe027dc8006065d67d0190b87d805135bdafcb5edb9803c" + }, + { + "key_generation_seed": "dbacba825728444921b227cdba54446b3f6881b47be9cd02832f78b023b1bee0e15274a8e2bc08fe818b117ba28c5dfae74d54fcdf6f20052f79be333edc8dde", + "sha3_256_hash_of_public_key": "175eb63c3144108548720ce7ee0f43a9ff3f52a9924efe9f2f59318bb93c86b5", + "sha3_256_hash_of_secret_key": "1993d7639b79f5e4871a7c58a69fec50f96c1424c2c0ee030ac054ae1b88a56f", + "encapsulation_seed": "1d129b27be7384c359d04311fe5c44917d1fde4bfb57314f483ac617edd5ac49", + "sha3_256_hash_of_ciphertext": "1288de12d00d76095c0072f3f0a0058227e99e9909fba65b58b361f85a76c98a", + "shared_secret": "d2d4a23dec18fd2c413d0c64d58c1d14e19d2a18aef1cb038d14c3c2e79f6a69" + }, + { + "key_generation_seed": "690eb71fd7052b906eaec09937a8ed374e0b02afa27c2f14399932be5839fad281c38c2cb5cfafac81b96a810ab749b61806b6d54c9f8cf4bf1be0192423288f", + "sha3_256_hash_of_public_key": "9bc32a138a2fb5b6072464172abe0fd97e9eabf357c3fa5391d94a415b53abd3", + "sha3_256_hash_of_secret_key": "3db4ab1393cfc8b1c708cf8efdb1c443c975878898b60182c22af66375cba13a", + "encapsulation_seed": "bbc773ebd2df42c36ae05952d6a64c63a5dfb82ceb3ef4f8d4df3a30ec8c0467", + "sha3_256_hash_of_ciphertext": "6f991f90c37724ccf1ea02f0c90b618cd97c1ede4221d4bc369f78d03da4c560", + "shared_secret": "52e36f81dd9a23fb9bb2363c31b715106d38520a31a3304cf754a9432e757224" + }, + { + "key_generation_seed": "32e0ea9089fa928482c0770da545af1bb871a03ce38604138b0d08ea2a10ca2bc06c5bef7b6508409daf847a64c8d30d0974fd3ba7476dc76c46b458a036d884", + "sha3_256_hash_of_public_key": "7ef43a72ef04766f1e899d25c9a005009c788b5faf985123cfb3fb97975de26d", + "sha3_256_hash_of_secret_key": "77431cb18010a604d56fe5a623bed2ffd028a741f176fa09546e9a45a48caa5e", + "encapsulation_seed": "5b17a6adad541efcbf5ae4b0c0452cd2ce32e4f0f8701801c5b63e197c1fcbf4", + "sha3_256_hash_of_ciphertext": "11fd09e815bbe163a61c9d215fc7123799b7e774c8945239cac775057b8606d2", + "shared_secret": "d072cb81aff4aa5712e56f0e9567dd89f2b03488735ba4751a7f0df1c786402a" + }, + { + "key_generation_seed": "6fb2ec719f2a0dea152bf3f64b9d148f8ab8ba88f64e61f5db53e12d59f525574f797c007e4061f95c7d56cfc7ee5c49e849dde3fea8f25e7876df2a18515c34", + "sha3_256_hash_of_public_key": "2c0db43f39b672b2cd912f907cf76a0f6fda925eb2d205546431be0b37b20411", + "sha3_256_hash_of_secret_key": "09844e203f4d8fa30728ab388b9d654847febbf5c9cd939cdc11c9c9be24ce9c", + "encapsulation_seed": "61ab87659525de9656af41246f20e1dbe85c24e335e7ecf9493f46168bc14e94", + "sha3_256_hash_of_ciphertext": "19d527602842910160a13f92dbf9c610a32a5163c3f063271a759487c344cf93", + "shared_secret": "da1085cbc7452cf2ac98ca36631c6ebcfff02e60485f9e807cdb3db77bc92243" + }, + { + "key_generation_seed": "527fb88c8bd9a4d6031dad15e63878abd2b559e7e08d61f69e8e78fca964ee6ae32d432b4f9f751bde0496c580a181ffed762aa35454a02d3f1f47ee0394c89c", + "sha3_256_hash_of_public_key": "aae8e61b905723fa092fb95b839f6de3670c39ce0498c27b87d20c24e7f64e22", + "sha3_256_hash_of_secret_key": "3880f7ca8fc33575a7a6d8bb46fec86a3f12e0068630507ed245d8bc278fbe5d", + "encapsulation_seed": "eca2adc3da1fb15f34033405ec08ef2f46163df4bfcccf8842c600ce0bc2026c", + "sha3_256_hash_of_ciphertext": "9faa095c14ed0b2e38ac99ef1f7c896c8dacb9065dbe677add2960053c1a0421", + "shared_secret": "aca83f1dc628fa87b20133bed4c2eee34b98021f295ab585dfdcefc9e3c032f5" + }, + { + "key_generation_seed": "ac6fcfaeeef795b6ef9e062f02bf42975fa01e7d91ba832f74e05269a72684d05aeda108ea4d6c6bc0fb958286850422bc357ca67b83c986048e0d0087fa11ec", + "sha3_256_hash_of_public_key": "64e085f67e48f00a7a7f82963e8c67176bff839a54fa1008328c0612f98d83d3", + "sha3_256_hash_of_secret_key": "0bfbc25d9df751f4c30907095eb6d9a75ed07fa23218ad0fffc469f0e55553c2", + "encapsulation_seed": "c4f15bec2d7701339d0ade4835193bea3632edcf89e74992620d9eb623a0d0d4", + "sha3_256_hash_of_ciphertext": "bc1227424d0f6e0b6974a798f281b3f2ae089216cacf15a7c87c10aa67040720", + "shared_secret": "3cc0ef85a74184338a10ffbfad5f6d04860d51e7dacf3ee73033b70969785af8" + }, + { + "key_generation_seed": "ba2fb9318d4dbe7488057c33e95e6f054583a2800c41bb83083c330a914a12cfe63f8ffda3565c2424c89b20974b748a65a5aba75133fcb3156dfb6626a83bab", + "sha3_256_hash_of_public_key": "8dab879de09b58d0fc7ade140393ffb5343abbddabdc118fad519b14436a964c", + "sha3_256_hash_of_secret_key": "7c53072fd98ea7bd8c5e873688b1a5650fe7e11c791407ac8c118b7958cf414b", + "encapsulation_seed": "28878249e2ac2b6263422993923a0c8bd05ce56e385ed13c943b03d226856947", + "sha3_256_hash_of_ciphertext": "9013959fd6d233a5baebd70cb33e1eb99ff6b3054af1a7d863dd1203ece5f869", + "shared_secret": "1db6e99f80628e170260354ee6f3854f905d198e9669b4faab478f4b39cc2f0e" + }, + { + "key_generation_seed": "aa6dd1e5799cdf7af9c4fc632b3eb9d51d66e85c8e0a21ec98664fc51ab63c7dfda268813efab5204efa60f78bf81d320d01ac09ac06244f7afbd2d80fd356d9", + "sha3_256_hash_of_public_key": "919a696301240cd6129f66be58e19d99b0d827d9932785cd9ea3d92f7ba54463", + "sha3_256_hash_of_secret_key": "cb1d7301f15951883cc3f287d4dd8fdf5c9b7022f558dff551c2ade5f5065755", + "encapsulation_seed": "17fc65f7fbd7c75ceec421dee84dff5a8cb22764a182db17e0ebe857f54d60eb", + "sha3_256_hash_of_ciphertext": "2a0c4b9f6c8818d628045333456751d87f87821eb4496ad022d23e0d4fa3872c", + "shared_secret": "ef8de288a7ce14ccd5172a4a2f91588559e3780a75b1df329a53b1e400c4c7f5" + }, + { + "key_generation_seed": "195d6c86a3df4c21e3007d7f2768b43c74cb3060e0eca77f0a5d3271542b9a84ae77e0f9f21eabd8c0c6eea7767f4e10fde5c2d79b8400bf96b19014b457ec21", + "sha3_256_hash_of_public_key": "cb6d7232426bdbdfdacd373c9190722e7bf342825f7d829185dcc9120588fc76", + "sha3_256_hash_of_secret_key": "a85e24cc2eafdfe40d82f46471112e1359628b9955f3feae9955b48d563ac952", + "encapsulation_seed": "fa0489f3730100609488e951e6aaa15c0f193bc1dbcfcd013bc418d6c507b176", + "sha3_256_hash_of_ciphertext": "9496733c6b4cfc954325101068c40a02c4e3234cf60f1bad187ac479416f9415", + "shared_secret": "4793f705aed572ace61db13bede3900f2538eaddb904988c1f015bac605a1093" + } +]