Skip to content

Commit dc4f868

Browse files
author
Ahmed
committed
ntru: Add encoding layer for streamlined ntru
Signed-off-by: Ahmed <>
1 parent 0b60b12 commit dc4f868

4 files changed

Lines changed: 287 additions & 2 deletions

File tree

ntru/src/encoded/encoding.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#![allow(non_snake_case)]
2+
3+
use crate::const_time::{u32_divmod_u14, u32_mod_u14};
4+
use alloc::vec;
5+
pub fn encode(R: &[u16], M: &[u16], out: &mut [u8]) {
6+
if M.is_empty() {
7+
return;
8+
}
9+
if M.len() == 1 {
10+
let mut r = R[0];
11+
let mut m = M[0];
12+
let mut i = 0;
13+
while m > 1 {
14+
out[i] = r as u8;
15+
i += 1;
16+
r >>= 8;
17+
m = m.wrapping_add(255) >> 8;
18+
}
19+
return;
20+
}
21+
let mut R2 = vec![0u16; (M.len() + 1) / 2];
22+
let mut M2 = vec![0u16; (M.len() + 1) / 2];
23+
let mut idx = 0;
24+
let mut i = 0;
25+
while i < M.len() - 1 {
26+
let m0 = M[i] as u32;
27+
let mut r = R[i] as u32 + R[i + 1] as u32 * m0;
28+
let mut m = M[i + 1] as u32 * m0;
29+
while m >= 16384 {
30+
out[idx] = r as u8;
31+
idx += 1;
32+
r >>= 8;
33+
m = m.wrapping_add(255) >> 8;
34+
}
35+
R2[i / 2] = r as u16;
36+
M2[i / 2] = m as u16;
37+
i += 2;
38+
}
39+
if i < M.len() {
40+
R2[i / 2] = R[i];
41+
M2[i / 2] = M[i];
42+
}
43+
encode(&R2, &M2, &mut out[idx..]);
44+
}
45+
46+
pub fn decode(S: &[u8], M: &[u16], out: &mut [u16]) {
47+
if M.is_empty() {
48+
return;
49+
}
50+
if M.len() == 1 {
51+
if M[0] == 1 {
52+
out[0] = 0;
53+
} else if M[0] < 256 {
54+
out[0] = u32_mod_u14(S[0] as u32, M[0]);
55+
} else {
56+
out[0] = u32_mod_u14(S[0] as u32 + ((S[1] as u16) << 8) as u32, M[0]);
57+
}
58+
return;
59+
}
60+
let mut R2 = vec![0u16; (M.len() + 1) / 2];
61+
let mut M2 = vec![0u16; (M.len() + 1) / 2];
62+
let mut bottomr = vec![0u16; M.len() / 2];
63+
let mut bottomt = vec![0u32; M.len() / 2];
64+
let mut i = 0;
65+
let mut s_idx = 0;
66+
while i < M.len() - 1 {
67+
let m = M[i] as u32 * M[i + 1] as u32;
68+
if m > 256 * 16383 {
69+
bottomt[i / 2] = 256 * 256;
70+
bottomr[i / 2] = S[s_idx] as u16 + 256 * S[s_idx + 1] as u16;
71+
s_idx += 2;
72+
M2[i / 2] = ((((m + 255) >> 8) + 255) >> 8) as u16;
73+
} else if m >= 16384 {
74+
bottomt[i / 2] = 256;
75+
bottomr[i / 2] = S[s_idx] as u16;
76+
s_idx += 1;
77+
M2[i / 2] = ((m + 255) >> 8) as u16;
78+
} else {
79+
bottomt[i / 2] = 1;
80+
bottomr[i / 2] = 0;
81+
M2[i / 2] = m as u16;
82+
}
83+
i += 2;
84+
}
85+
if i < M.len() {
86+
M2[i / 2] = M[i];
87+
}
88+
decode(&S[s_idx..], &M2, &mut R2);
89+
let mut i = 0;
90+
let mut out_idx = 0;
91+
while i < M.len() - 1 {
92+
let mut r = bottomr[i / 2] as u32;
93+
r += bottomt[i / 2] * R2[i / 2] as u32;
94+
let (r1, r0) = u32_divmod_u14(r, M[i]);
95+
let r1 = u32_mod_u14(r1, M[i + 1]);
96+
out[out_idx] = r0;
97+
out[out_idx + 1] = r1;
98+
out_idx += 2;
99+
i += 2;
100+
}
101+
if i < M.len() {
102+
out[out_idx] = R2[i / 2];
103+
}
104+
}

ntru/src/encoded/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
mod encoding;
2+
mod streamlined;
3+
4+
use crate::params::NtruCommon;
5+
use hybrid_array::Array;
6+
use rand_core::CryptoRngCore;
7+
8+
pub trait AsymEnc: NtruCommon + Sized {
9+
type Inputs;
10+
fn key_gen(
11+
rng: &mut impl CryptoRngCore,
12+
) -> (
13+
Array<u8, Self::SecretKeyBytes>,
14+
Array<u8, Self::PublicKeyBytes>,
15+
);
16+
fn decrypt(
17+
c: &Array<u8, Self::CipherTextBytes>,
18+
sk: &Array<u8, Self::SecretKeyBytes>,
19+
) -> Self::Inputs;
20+
fn encrypt(
21+
r: &Self::Inputs,
22+
pk: &Array<u8, Self::PublicKeyBytes>,
23+
) -> Array<u8, Self::CipherTextBytes>;
24+
fn inputs_encode(f: &Self::Inputs, out: &mut [u8]);
25+
fn inputs_random(rng: &mut impl CryptoRngCore) -> Self::Inputs;
26+
}

ntru/src/encoded/streamlined.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
use super::{
2+
encoding::{decode, encode},
3+
AsymEnc,
4+
};
5+
use crate::{
6+
algebra::{f3::Small, fq::Fq, r3::R3, rq::Rq},
7+
core::streamlined,
8+
params::{NtruCommon, Streamlined, StreamlinedNtru},
9+
};
10+
use hybrid_array::Array;
11+
use hybrid_array::{typenum::Unsigned, ArraySize};
12+
use rand_core::CryptoRngCore;
13+
14+
#[allow(non_snake_case)]
15+
fn rq_encode<Params: NtruCommon>(r: &Rq<Params>, out: &mut [u8]) {
16+
let mut R: Array<u16, Params::P> = Array::default();
17+
let mut M: Array<u16, Params::P> = Array::default();
18+
for i in 0..Params::P::USIZE {
19+
R[i] = (*r.0[i] as u16).wrapping_add(Params::Q12);
20+
}
21+
for i in 0..Params::P::USIZE {
22+
M[i] = Params::Q;
23+
}
24+
encode(&R, &M, out);
25+
}
26+
#[allow(non_snake_case)]
27+
fn rq_decode<Params: NtruCommon + StreamlinedNtru>(
28+
s: &Array<u8, Params::PublicKeyBytes>,
29+
) -> Rq<Params> {
30+
let mut R: Array<u16, Params::P> = Array::default();
31+
let mut M: Array<u16, Params::P> = Array::default();
32+
for i in 0..Params::P::USIZE {
33+
M[i] = Params::Q;
34+
}
35+
decode(s, &M, &mut R);
36+
let mut r = Rq::<Params>::default();
37+
for i in 0..Params::P::USIZE {
38+
r.0[i] = Fq::new_i16(R[i].wrapping_sub(Params::Q12) as i16);
39+
}
40+
r
41+
}
42+
#[allow(non_snake_case)]
43+
fn rounded_encode<Params: NtruCommon + StreamlinedNtru>(
44+
r: &Rq<Params>,
45+
) -> Array<u8, Params::CipherTextBytes> {
46+
let mut R: Array<u16, Params::P> = Array::default();
47+
let mut M: Array<u16, Params::P> = Array::default();
48+
let mut out = Array::default();
49+
for i in 0..Params::P::USIZE {
50+
R[i] = ((*r.0[i] as u32)
51+
.wrapping_add(Params::Q12 as u32)
52+
.wrapping_mul(10923)
53+
>> 15) as u16;
54+
}
55+
for i in 0..Params::P::USIZE {
56+
M[i] = (Params::Q + 2) / 3;
57+
}
58+
encode(&R, &M, &mut out);
59+
out
60+
}
61+
#[allow(non_snake_case)]
62+
fn rounded_decode<Params: NtruCommon + StreamlinedNtru>(
63+
s: &Array<u8, Params::CipherTextBytes>,
64+
) -> Rq<Params> {
65+
let mut R: Array<u16, Params::P> = Array::default();
66+
let mut M: Array<u16, Params::P> = Array::default();
67+
for i in 0..Params::P::USIZE {
68+
M[i] = (Params::Q + 2) / 3;
69+
}
70+
decode(s, &M, &mut R);
71+
let mut r = Rq::<Params>::default();
72+
for i in 0..Params::P::USIZE {
73+
r.0[i] = Fq::new_i16(R[i].wrapping_mul(3).wrapping_sub(Params::Q12) as i16);
74+
}
75+
r
76+
}
77+
78+
fn small_encode<Params: NtruCommon>(f: &R3<Params>, out: &mut [u8]) {
79+
let mut i = 0;
80+
let mut x;
81+
for _ in 0..Params::P::USIZE / 4 {
82+
x = (*f.0[i] + 1) as u8;
83+
x += ((*f.0[i + 1] + 1) as u8) << 2;
84+
x += ((*f.0[i + 2] + 1) as u8) << 4;
85+
x += ((*f.0[i + 3] + 1) as u8) << 6;
86+
out[i / 4] = x;
87+
i += 4;
88+
}
89+
x = (*f.0[i] + 1) as u8;
90+
out[i / 4] = x;
91+
}
92+
93+
fn small_decode<Params: NtruCommon>(input: &[u8]) -> R3<Params> {
94+
let mut r = R3::default();
95+
for (i, x) in input.iter().enumerate().take(Params::P::USIZE / 4) {
96+
let mut x = *x;
97+
r.0[i * 4] = Small::new_i8((x & 3) as i8 - 1);
98+
x >>= 2;
99+
r.0[i * 4 + 1] = Small::new_i8((x & 3) as i8 - 1);
100+
x >>= 2;
101+
r.0[i * 4 + 2] = Small::new_i8((x & 3) as i8 - 1);
102+
x >>= 2;
103+
r.0[i * 4 + 3] = Small::new_i8((x & 3) as i8 - 1);
104+
}
105+
let x = input[Params::P::USIZE / 4];
106+
r.0[Params::P::USIZE - 1] = Small::new_i8((x & 3) as i8 - 1);
107+
r
108+
}
109+
110+
impl<P> AsymEnc for Streamlined<P>
111+
where
112+
P: ArraySize,
113+
Streamlined<P>: NtruCommon + StreamlinedNtru + Sized,
114+
{
115+
type Inputs = R3<Self>;
116+
fn decrypt(
117+
c: &Array<u8, Self::CipherTextBytes>,
118+
sk: &Array<u8, Self::SecretKeyBytes>,
119+
) -> R3<Self> {
120+
let f = small_decode(&sk[..Self::InputsBytes::USIZE]);
121+
let v = small_decode(&sk[Self::InputsBytes::USIZE..]);
122+
let c = rounded_decode(c);
123+
streamlined::decrypt(&c, &f, &v)
124+
}
125+
fn key_gen(
126+
rng: &mut impl CryptoRngCore,
127+
) -> (
128+
Array<u8, Self::SecretKeyBytes>,
129+
Array<u8, Self::PublicKeyBytes>,
130+
) {
131+
let (h, f, v) = streamlined::key_gen::<Self>(rng);
132+
let mut pk: Array<u8, Self::PublicKeyBytes> = Array::default();
133+
let mut sk: Array<u8, Self::SecretKeyBytes> = Array::default();
134+
rq_encode(&h, &mut pk);
135+
small_encode(&f, &mut sk[..Self::InputsBytes::USIZE]);
136+
small_encode(&v, &mut sk[Self::InputsBytes::USIZE..]);
137+
(sk, pk)
138+
}
139+
fn encrypt(
140+
r: &R3<Self>,
141+
pk: &Array<u8, Self::PublicKeyBytes>,
142+
) -> Array<u8, Self::CipherTextBytes> {
143+
let h = rq_decode(pk);
144+
let c = streamlined::encrypt(r, &h);
145+
rounded_encode(&c)
146+
}
147+
fn inputs_encode(f: &R3<Self>, out: &mut [u8]) {
148+
small_encode(f, out);
149+
}
150+
151+
fn inputs_random(rng: &mut impl CryptoRngCore) -> R3<Self> {
152+
R3::short_random(rng)
153+
}
154+
}

ntru/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
clippy::many_single_char_names,
1717
clippy::similar_names,
1818
)]
19+
extern crate alloc;
1920

2021
mod algebra;
2122
pub mod const_time;
22-
pub mod core;
23+
mod core;
24+
pub mod encoded;
2325
pub mod params;
24-
2526
use hybrid_array::sizes::{U1013, U1277, U653, U761, U857, U953};
2627
use params::{Lpr, Streamlined};
2728

0 commit comments

Comments
 (0)