classic_mceliece_rust/
encrypt.rs

1//! Encryption function to compute error vector and syndrome to get ciphertext
2
3use crate::{
4    api::CRYPTO_CIPHERTEXTBYTES,
5    macros::sub,
6    params::{PK_NROWS, PK_ROW_BYTES, SYND_BYTES, SYS_N, SYS_T},
7    util::load_gf,
8};
9use rand::{CryptoRng, RngCore};
10
11/// Takes two 16-bit integers and determines whether they are equal (u8::MAX) or different (0)
12fn same_mask_u8(x: u16, y: u16) -> u8 {
13    let mut mask = (x ^ y) as u32;
14    mask = mask.wrapping_sub(1);
15    mask = mask.wrapping_shr(31);
16    mask = 0u32.wrapping_sub(mask);
17
18    (mask & 0xFF) as u8 // ∈ {0, u8::MAX}
19}
20
21/// Generation of `e`, an error vector of weight `t`.
22/// Does not take any input arguments.
23/// If generation of pseudo-random numbers fails, an error is returned.
24#[cfg(not(any(feature = "mceliece8192128", feature = "mceliece8192128f")))]
25fn gen_e<R: CryptoRng + RngCore>(e: &mut [u8; SYS_N / 8], rng: &mut R) {
26    let mut ind = [0u16; SYS_T];
27    let mut val = [0u8; SYS_T];
28
29    loop {
30        let mut bytes = [0u8; SYS_T * 4];
31        rng.fill_bytes(&mut bytes);
32
33        let mut nums = [0u16; SYS_T * 2];
34        for (i, chunk) in bytes.chunks(2).enumerate() {
35            nums[i] = load_gf(sub!(chunk, 0, 2));
36        }
37
38        // moving and counting indices in the correct range
39
40        let mut count = 0;
41        for itr_num in nums.iter() {
42            if count >= SYS_T {
43                break;
44            }
45            if *itr_num < SYS_N as u16 {
46                ind[count] = *itr_num;
47                count += 1;
48            }
49        }
50
51        if count < SYS_T {
52            continue;
53        }
54
55        // check for repetition
56
57        let mut eq = 0;
58
59        for i in 1..SYS_T {
60            for j in 0..i {
61                if ind[i] == ind[j] {
62                    eq = 1;
63                }
64            }
65        }
66
67        if eq == 0 {
68            break;
69        }
70    }
71
72    for j in 0..SYS_T {
73        val[j] = 1 << (ind[j] & 7);
74    }
75
76    for (i, itr_e) in e.iter_mut().enumerate() {
77        *itr_e = 0;
78
79        for j in 0..SYS_T {
80            let mask: u8 = same_mask_u8(i as u16, ind[j] >> 3);
81
82            *itr_e |= val[j] & mask;
83        }
84    }
85}
86
87/// Generation of `e`, an error vector of weight `t`.
88/// Does not take any input arguments.
89/// If generation of pseudo-random numbers fails, an error is returned.
90#[cfg(any(feature = "mceliece8192128", feature = "mceliece8192128f"))]
91fn gen_e<R: CryptoRng + RngCore>(e: &mut [u8], rng: &mut R) {
92    let mut ind = [0u16; SYS_T];
93    let mut bytes = [0u8; SYS_T * 2];
94    let mut val = [0u8; SYS_T];
95
96    loop {
97        rng.fill_bytes(&mut bytes);
98
99        for (i, chunk) in bytes.chunks(2).enumerate() {
100            ind[i] = load_gf(sub!(chunk, 0, 2));
101        }
102
103        // check for repetition
104
105        let mut eq = 0;
106
107        for i in 1..SYS_T {
108            for j in 0..i {
109                if ind[i] == ind[j] {
110                    eq = 1;
111                }
112            }
113        }
114
115        if eq == 0 {
116            break;
117        }
118    }
119
120    for j in 0..SYS_T {
121        val[j] = 1 << (ind[j] & 7);
122    }
123
124    for i in 0..SYS_N / 8 {
125        e[i] = 0;
126
127        for j in 0..SYS_T {
128            let mask: u8 = same_mask_u8(i as u16, ind[j] >> 3);
129
130            e[i] |= val[j] & mask;
131        }
132    }
133}
134
135/// Syndrome computation.
136///
137/// Computes syndrome `s` based on public key `pk` and error vector `e`.
138#[cfg(any(feature = "mceliece6960119", feature = "mceliece6960119f"))]
139fn syndrome(
140    s: &mut [u8; (PK_NROWS + 7) / 8],
141    pk: &[u8; PK_NROWS * PK_ROW_BYTES],
142    e: &[u8; SYS_N / 8],
143) {
144    let mut row = [0u8; SYS_N / 8];
145
146    let mut pk_segment = &pk[..];
147    let tail = PK_NROWS % 8;
148
149    s[0..SYND_BYTES].fill(0);
150
151    for i in 0..PK_NROWS {
152        row[0..SYS_N / 8].fill(0);
153
154        for j in 0..PK_ROW_BYTES {
155            row[SYS_N / 8 - PK_ROW_BYTES + j] = pk_segment[j];
156        }
157
158        for j in ((SYS_N / 8 - PK_ROW_BYTES)..SYS_N / 8).rev() {
159            row[j] = (row[j] << tail) | (row[j - 1] >> (8 - tail));
160        }
161
162        row[i / 8] |= 1 << (i % 8);
163
164        let mut b = 0u8;
165        for j in 0..SYS_N / 8 {
166            b ^= row[j] & e[j];
167        }
168
169        b ^= b >> 4;
170        b ^= b >> 2;
171        b ^= b >> 1;
172        b &= 1;
173
174        s[i / 8] |= b << (i % 8);
175
176        pk_segment = &pk_segment[PK_ROW_BYTES..];
177    }
178}
179
180/// Syndrome computation.
181///
182/// Computes syndrome `s` based on public key `pk` and error vector `e`.
183#[cfg(not(any(feature = "mceliece6960119", feature = "mceliece6960119f")))]
184fn syndrome(
185    s: &mut [u8; PK_NROWS.div_ceil(8)],
186    pk: &[u8; PK_NROWS * PK_ROW_BYTES],
187    e: &[u8; SYS_N / 8],
188) {
189    let mut row = [0u8; SYS_N / 8];
190
191    let mut pk_segment = &pk[..];
192
193    s[0..SYND_BYTES].fill(0);
194
195    for i in 0..PK_NROWS {
196        row[0..SYS_N / 8].fill(0);
197
198        for j in 0..PK_ROW_BYTES {
199            row[SYS_N / 8 - PK_ROW_BYTES + j] = pk_segment[j];
200        }
201
202        row[i / 8] |= 1 << (i % 8);
203
204        let mut b = 0u8;
205        for j in 0..SYS_N / 8 {
206            b ^= row[j] & e[j];
207        }
208
209        b ^= b >> 4;
210        b ^= b >> 2;
211        b ^= b >> 1;
212        b &= 1;
213
214        s[i / 8] |= b << (i % 8);
215
216        pk_segment = &pk_segment[PK_ROW_BYTES..];
217    }
218}
219
220/// Encryption routine.
221/// Takes a public key `pk` to compute error vector `e` and syndrome `s`.
222pub(crate) fn encrypt<R: CryptoRng + RngCore>(
223    s: &mut [u8; CRYPTO_CIPHERTEXTBYTES],
224    pk: &[u8; PK_NROWS * PK_ROW_BYTES],
225    e: &mut [u8; SYS_N / 8],
226    rng: &mut R,
227) {
228    gen_e(e, rng);
229    syndrome(sub!(mut s, 0, PK_NROWS.div_ceil(8)), pk, e);
230}
231
232#[cfg(test)]
233mod tests {
234    #[cfg(feature = "mceliece8192128f")]
235    use super::*;
236    #[cfg(feature = "mceliece8192128f")]
237    use crate::api::CRYPTO_CIPHERTEXTBYTES;
238    #[cfg(feature = "mceliece8192128f")]
239    use crate::api::CRYPTO_PUBLICKEYBYTES;
240    #[cfg(feature = "mceliece8192128f")]
241    use crate::nist_aes_rng::AesState;
242    #[cfg(feature = "mceliece8192128f")]
243    use crate::test_utils::TestData;
244
245    #[test]
246    #[cfg(feature = "mceliece8192128f")]
247    fn test_encrypt() {
248        let entropy_input = [
249            6, 21, 80, 35, 77, 21, 140, 94, 201, 85, 149, 254, 4, 239, 122, 37, 118, 127, 46, 36,
250            204, 43, 196, 121, 208, 157, 134, 220, 154, 188, 253, 231, 5, 106, 140, 38, 111, 158,
251            249, 126, 208, 133, 65, 219, 210, 225, 255, 161,
252        ];
253
254        let mut rng_state = AesState::new();
255        rng_state.randombytes_init(entropy_input);
256
257        let mut second_seed = [0u8; 33];
258        second_seed[0] = 64;
259
260        rng_state.fill_bytes(&mut second_seed[1..]);
261
262        let mut e = [0u8; SYS_N / 8];
263
264        let mut c = [0u8; CRYPTO_CIPHERTEXTBYTES];
265        let mut pk = TestData::new().u8vec("mceliece8192128f_pk1");
266
267        let compare_ct = TestData::new().u8vec("mceliece8192128f_encrypt_ct");
268        assert_eq!(compare_ct.len(), CRYPTO_CIPHERTEXTBYTES);
269
270        encrypt(
271            &mut c,
272            sub!(mut pk, 0, CRYPTO_PUBLICKEYBYTES),
273            sub!(mut e, 0, SYS_N / 8),
274            &mut rng_state,
275        );
276
277        assert_eq!(compare_ct, c);
278    }
279}