Skip to content

Commit 20a493e

Browse files
author
Ahmed
committed
ntru: Implement KEM
Signed-off-by: Ahmed <>
1 parent 7405b00 commit 20a493e

2 files changed

Lines changed: 130 additions & 0 deletions

File tree

ntru/src/kem.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
use crate::encoded::AsymEnc;
2+
use crate::hashes::{hash_prefix, HashOps};
3+
use alloc::vec::Vec;
4+
use hybrid_array::{typenum::Unsigned, Array};
5+
use rand_core::CryptoRngCore;
6+
pub struct PublicKey<Params: AsymEnc>(pub Array<u8, Params::PublicKeyBytes>);
7+
8+
pub struct SecretKey<Params: AsymEnc> {
9+
sk: Array<u8, Params::SecretKeyBytes>,
10+
pk: Array<u8, Params::PublicKeyBytes>,
11+
rand: Array<u8, Params::InputsBytes>,
12+
digest: [u8; 32],
13+
}
14+
pub struct CihpherText<Params: AsymEnc> {
15+
c: Array<u8, Params::CipherTextBytes>,
16+
cache: [u8; 32],
17+
}
18+
19+
impl<T: AsymEnc> CihpherText<T> {
20+
#[must_use]
21+
pub fn to_bytes(&self) -> Vec<u8> {
22+
let mut bytes = Vec::new();
23+
bytes.extend_from_slice(&self.c);
24+
bytes.extend_from_slice(&self.cache);
25+
bytes
26+
}
27+
#[must_use]
28+
pub fn from_bytes(bytes: &[u8]) -> Self {
29+
let c = bytes[..T::CipherTextBytes::USIZE].try_into().unwrap();
30+
let cache = bytes[T::CipherTextBytes::USIZE..].try_into().unwrap();
31+
CihpherText { c, cache }
32+
}
33+
}
34+
35+
impl<T: AsymEnc> SecretKey<T> {
36+
#[must_use]
37+
pub fn to_bytes(&self) -> Vec<u8> {
38+
let mut bytes = Vec::new();
39+
bytes.extend_from_slice(&self.sk);
40+
bytes.extend_from_slice(&self.pk);
41+
bytes.extend_from_slice(&self.rand);
42+
bytes.extend_from_slice(&self.digest);
43+
bytes
44+
}
45+
#[must_use]
46+
pub fn from_bytes(bytes: &[u8]) -> Self {
47+
let mut start = 0;
48+
let sk = bytes[start..T::SecretKeyBytes::USIZE].try_into().unwrap();
49+
start += T::SecretKeyBytes::USIZE;
50+
let pk = bytes[start..start + T::PublicKeyBytes::USIZE]
51+
.try_into()
52+
.unwrap();
53+
start += T::PublicKeyBytes::USIZE;
54+
let rand = bytes[start..start + T::InputsBytes::USIZE]
55+
.try_into()
56+
.unwrap();
57+
start += T::InputsBytes::USIZE;
58+
let digest = bytes[start..start + 32].try_into().unwrap();
59+
SecretKey {
60+
sk,
61+
pk,
62+
rand,
63+
digest,
64+
}
65+
}
66+
}
67+
pub fn key_gen<Params: AsymEnc + AsymEnc>(
68+
rng: &mut impl CryptoRngCore,
69+
) -> (SecretKey<Params>, PublicKey<Params>) {
70+
let (sk, pk) = Params::key_gen(rng);
71+
let mut rand = Array::default();
72+
rng.fill_bytes(&mut rand);
73+
let digest = hash_prefix(4, &pk);
74+
(
75+
SecretKey {
76+
sk,
77+
pk: pk.clone(),
78+
rand,
79+
digest,
80+
},
81+
PublicKey(pk),
82+
)
83+
}
84+
fn hide<Params: AsymEnc + AsymEnc + HashOps>(
85+
r: &Params::Inputs,
86+
pk: &Array<u8, Params::PublicKeyBytes>,
87+
cache: &[u8; 32],
88+
) -> (CihpherText<Params>, Array<u8, Params::InputsBytes>) {
89+
let mut r_enc = Array::default();
90+
Params::inputs_encode(r, &mut r_enc);
91+
let c = Params::encrypt(r, pk);
92+
let cache = Params::hash_confirm::<Params>(&r_enc, cache);
93+
(CihpherText { c, cache }, r_enc)
94+
}
95+
96+
pub fn encap<Params: AsymEnc + AsymEnc + HashOps>(
97+
rng: &mut impl CryptoRngCore,
98+
pk: &PublicKey<Params>,
99+
) -> (CihpherText<Params>, [u8; 32]) {
100+
let r = Params::inputs_random(rng);
101+
let cache = hash_prefix(4, &pk.0);
102+
let (c, r_enc) = hide::<Params>(&r, &pk.0, &cache);
103+
let parts: &[&[u8]] = &[&r_enc, &c.c, &c.cache];
104+
let k = Params::hash_session(1, parts);
105+
(c, k)
106+
}
107+
108+
fn ciphertext_diff_mask(c: &[u8], c2: &[u8]) -> i32 {
109+
debug_assert_eq!(c.len(), c2.len());
110+
let mut differentbits = 0u16;
111+
for i in 0..c.len() {
112+
differentbits |= (c[i] ^ c2[i]) as u16;
113+
}
114+
(1 & ((differentbits as i32 - 1) >> 8)) - 1
115+
}
116+
117+
pub fn decap<Params: AsymEnc + HashOps>(
118+
c: &CihpherText<Params>,
119+
sk: &SecretKey<Params>,
120+
) -> [u8; 32] {
121+
let r = Params::decrypt(&c.c, &sk.sk);
122+
let (cnew, mut r_enc) = hide::<Params>(&r, &sk.pk, &sk.digest);
123+
let mask = ciphertext_diff_mask(&c.c, &cnew.c) as u8;
124+
for i in 0..Params::InputsBytes::USIZE {
125+
r_enc[i] ^= (mask as u8) & (r_enc[i] ^ sk.rand[i]);
126+
}
127+
let parts: &[&[u8]] = &[&r_enc, &c.c, &c.cache];
128+
Params::hash_session(mask.wrapping_add(1), parts)
129+
}

ntru/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub mod const_time;
2323
mod core;
2424
pub mod encoded;
2525
pub mod hashes;
26+
pub mod kem;
2627
pub mod params;
2728
use hybrid_array::sizes::{U1013, U1277, U653, U761, U857, U953};
2829
use params::{Lpr, Streamlined};

0 commit comments

Comments
 (0)