1use rand_core::CryptoRng;
28
29use super::codec::{
30 CodecError, decode_message_scaled_bits_example, encode_message_scaled_bits_example,
31};
32use super::sampling::{SamplingError, sample_cbd_poly_example, sample_uniform_poly_example};
33use nc_polynomial::{PolynomialError, RingContext, RingElem};
34
35#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct ExamplePublicKey {
38 pub a: RingElem,
40 pub b: RingElem,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct ExampleSecretKey {
47 pub s: RingElem,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct ExampleCiphertext {
54 pub u: RingElem,
56 pub v: RingElem,
58}
59
60#[derive(Debug, Clone, PartialEq, Eq)]
62pub enum ExamplePkeError {
63 Sampling(SamplingError),
65 Codec(CodecError),
67 Polynomial(PolynomialError),
69 ParameterMismatch,
71}
72
73impl core::fmt::Display for ExamplePkeError {
74 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
75 match self {
76 Self::Sampling(err) => write!(f, "sampling failed: {err}"),
77 Self::Codec(err) => write!(f, "codec failed: {err}"),
78 Self::Polynomial(err) => write!(f, "polynomial arithmetic failed: {err}"),
79 Self::ParameterMismatch => write!(f, "parameters/context mismatch"),
80 }
81 }
82}
83
84impl std::error::Error for ExamplePkeError {}
85
86impl From<SamplingError> for ExamplePkeError {
87 fn from(value: SamplingError) -> Self {
88 Self::Sampling(value)
89 }
90}
91
92impl From<CodecError> for ExamplePkeError {
93 fn from(value: CodecError) -> Self {
94 Self::Codec(value)
95 }
96}
97
98impl From<PolynomialError> for ExamplePkeError {
99 fn from(value: PolynomialError) -> Self {
100 Self::Polynomial(value)
101 }
102}
103
104pub fn keygen_example<R: CryptoRng>(
106 ctx: &RingContext,
107 noise_eta: u8,
108 rng: &mut R,
109) -> Result<(ExamplePublicKey, ExampleSecretKey), ExamplePkeError> {
110 let a = sample_uniform_poly_example(ctx, rng)?;
112 let s = sample_cbd_poly_example(ctx, noise_eta, rng)?;
114 let e = sample_cbd_poly_example(ctx, noise_eta, rng)?;
116
117 let b = a.mul(&s)?.add(&e)?;
119
120 Ok((ExamplePublicKey { a, b }, ExampleSecretKey { s }))
121}
122
123pub fn encrypt_example<R: CryptoRng>(
127 ctx: &RingContext,
128 public_key: &ExamplePublicKey,
129 message: &[u8],
130 noise_eta: u8,
131 rng: &mut R,
132) -> Result<ExampleCiphertext, ExamplePkeError> {
133 ensure_element_matches_context(ctx, &public_key.a)?;
135 ensure_element_matches_context(ctx, &public_key.b)?;
136
137 let m = encode_message_scaled_bits_example(ctx, message)?;
139 let r = sample_cbd_poly_example(ctx, noise_eta, rng)?;
141 let e1 = sample_cbd_poly_example(ctx, noise_eta, rng)?;
142 let e2 = sample_cbd_poly_example(ctx, noise_eta, rng)?;
143
144 let u = public_key.a.mul(&r)?.add(&e1)?;
148 let v = public_key.b.mul(&r)?.add(&e2)?.add(&m)?;
149
150 Ok(ExampleCiphertext { u, v })
151}
152
153pub fn decrypt_example(
155 ctx: &RingContext,
156 secret_key: &ExampleSecretKey,
157 ciphertext: &ExampleCiphertext,
158 output_len_bytes: usize,
159) -> Result<Vec<u8>, ExamplePkeError> {
160 ensure_element_matches_context(ctx, &secret_key.s)?;
162 ensure_element_matches_context(ctx, &ciphertext.u)?;
163 ensure_element_matches_context(ctx, &ciphertext.v)?;
164
165 let recovered = ciphertext.v.sub(&secret_key.s.mul(&ciphertext.u)?)?;
170 Ok(decode_message_scaled_bits_example(
171 &recovered,
172 output_len_bytes,
173 )?)
174}
175
176fn ensure_element_matches_context(
177 ctx: &RingContext,
178 element: &RingElem,
179) -> Result<(), ExamplePkeError> {
180 if element.params() != ctx.params() {
182 return Err(ExamplePkeError::ParameterMismatch);
183 }
184 Ok(())
185}
186
187#[cfg(test)]
188mod tests {
189 use super::{
190 ExampleCiphertext, ExamplePkeError, ExamplePublicKey, decrypt_example, encrypt_example,
191 keygen_example,
192 };
193 use core::convert::Infallible;
194 use nc_polynomial::RingContext;
195 use rand_core::{Rng, TryCryptoRng, TryRng};
196
197 #[derive(Debug, Clone)]
198 struct TestRng {
199 state: u64,
200 }
201
202 impl TestRng {
203 fn new(seed: u64) -> Self {
204 Self { state: seed }
205 }
206
207 fn step(&mut self) -> u64 {
208 let mut x = self.state;
209 x ^= x >> 12;
210 x ^= x << 25;
211 x ^= x >> 27;
212 self.state = x;
213 x.wrapping_mul(0x2545_F491_4F6C_DD1D)
214 }
215 }
216
217 impl TryRng for TestRng {
218 type Error = Infallible;
219
220 fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
221 Ok(self.step() as u32)
222 }
223
224 fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
225 Ok(self.step())
226 }
227
228 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
229 let mut offset = 0;
230 while offset < dest.len() {
231 let word = self.step().to_le_bytes();
232 let chunk = (dest.len() - offset).min(8);
233 dest[offset..offset + chunk].copy_from_slice(&word[..chunk]);
234 offset += chunk;
235 }
236 Ok(())
237 }
238 }
239
240 impl TryCryptoRng for TestRng {}
241
242 fn ctx(max_degree: usize) -> RingContext {
243 let mut modulus_poly = vec![0_u64; max_degree + 1];
244 modulus_poly[0] = 1;
245 modulus_poly[max_degree] = 1;
246
247 RingContext::from_parts(max_degree, 998_244_353, &modulus_poly, 3)
248 .expect("context should build")
249 }
250
251 #[test]
252 fn example_pke_known_answer_vector() {
253 let ctx = ctx(16);
255
256 let mut key_rng = TestRng::new(0x1234_5678_90AB_CDEF);
257 let (pk, sk) = keygen_example(&ctx, 2, &mut key_rng).expect("keygen should work");
258
259 let message = [0xA5_u8, 0x5A];
260 let mut enc_rng = TestRng::new(0x0FED_CBA9_8765_4321);
261 let ct =
262 encrypt_example(&ctx, &pk, &message, 2, &mut enc_rng).expect("encrypt should work");
263
264 assert_eq!(
265 sk.s.trimmed_coefficients(),
266 vec![
267 998244352, 2, 0, 998244352, 0, 2, 998244352, 1, 1, 0, 998244352, 998244352, 2,
268 998244352, 1,
269 ]
270 );
271 assert_eq!(
272 pk.a.trimmed_coefficients(),
273 vec![
274 981506489, 745670232, 465336855, 4228489, 817594600, 449616339, 214586882,
275 863302342, 412683612, 739770493, 484384582, 958972188, 465458099, 901548526,
276 136656154, 185069671
277 ]
278 );
279 assert_eq!(
280 pk.b.trimmed_coefficients(),
281 vec![
282 523016218, 151678120, 905400193, 336053334, 886938703, 345066676, 653029107,
283 919882161, 719988960, 738092331, 49398785, 531018501, 299386522, 26450153,
284 202169127, 149348579
285 ]
286 );
287 assert_eq!(
288 ct.u.trimmed_coefficients(),
289 vec![
290 185825251, 992594928, 222735870, 419735116, 492318984, 871565634, 299285265,
291 894961651, 551797064, 802414206, 845696604, 713894635, 456686131, 629006526,
292 242614853, 521523065
293 ]
294 );
295 assert_eq!(
296 ct.v.trimmed_coefficients(),
297 vec![
298 342654269, 292514575, 651371554, 799848083, 605114155, 22609066, 790897921,
299 525265555, 989917947, 531922498, 781997577, 952513402, 961072975, 455278710,
300 776307615, 91714186
301 ]
302 );
303
304 let decrypted =
305 decrypt_example(&ctx, &sk, &ct, message.len()).expect("decrypt should work");
306 assert_eq!(decrypted, message);
308 }
309
310 #[test]
311 fn example_pke_encrypt_decrypt_round_trip_many_messages() {
312 let ctx = ctx(32);
313
314 for seed in 0_u64..64 {
316 let mut key_rng = TestRng::new(0xAA55_AA55_0000_0000 ^ seed);
317 let (pk, sk) = keygen_example(&ctx, 2, &mut key_rng).expect("keygen should work");
318
319 let msg_len = (seed as usize) % 5;
320 let mut msg_rng = TestRng::new(0xC0DE_CAFE_0000_0000 ^ seed);
321 let mut message = vec![0_u8; msg_len];
322 msg_rng.fill_bytes(&mut message);
323
324 let mut enc_rng = TestRng::new(0xDEAD_BEEF_0000_0000 ^ seed);
325 let ct =
326 encrypt_example(&ctx, &pk, &message, 2, &mut enc_rng).expect("encrypt should work");
327 let recovered =
328 decrypt_example(&ctx, &sk, &ct, message.len()).expect("decrypt should work");
329
330 assert_eq!(recovered, message, "seed {seed}");
331 }
332 }
333
334 #[test]
335 fn example_pke_rejects_parameter_mismatch_inputs() {
336 let ctx_a = ctx(16);
337 let ctx_b = ctx(32);
338
339 let mut rng = TestRng::new(101);
340 let (pk_a, sk_a) = keygen_example(&ctx_a, 2, &mut rng).expect("keygen should work");
341
342 let bad_pk = ExamplePublicKey {
343 a: ctx_b.one_element(),
345 b: pk_a.b.clone(),
346 };
347 let mut enc_rng = TestRng::new(102);
348 let err = encrypt_example(&ctx_a, &bad_pk, b"a", 2, &mut enc_rng)
349 .expect_err("expected mismatch error");
350 assert_eq!(err, ExamplePkeError::ParameterMismatch);
351
352 let mut good_enc_rng = TestRng::new(103);
353 let good_ct =
354 encrypt_example(&ctx_a, &pk_a, b"a", 2, &mut good_enc_rng).expect("encrypt works");
355 let bad_ct = ExampleCiphertext {
356 u: ctx_b.one_element(),
358 v: good_ct.v,
359 };
360 let err = decrypt_example(&ctx_a, &sk_a, &bad_ct, 1).expect_err("expected mismatch");
361 assert_eq!(err, ExamplePkeError::ParameterMismatch);
362 }
363
364 #[test]
365 fn example_pke_property_like_round_trips_many_seeds() {
366 let ctx = ctx(32);
367
368 for seed in 0_u64..128 {
371 let mut key_rng = TestRng::new(0x1111_2222_3333_4444 ^ seed.rotate_left(5));
372 let (pk, sk) = keygen_example(&ctx, 1 + (seed % 2) as u8, &mut key_rng)
373 .expect("keygen should work");
374
375 let msg_len = ((seed * 7) as usize) % 5;
376 let mut msg_rng = TestRng::new(0x5555_6666_7777_8888 ^ seed.rotate_left(11));
377 let mut message = vec![0_u8; msg_len];
378 msg_rng.fill_bytes(&mut message);
379
380 let mut enc_rng = TestRng::new(0x9999_AAAA_BBBB_CCCC ^ seed.rotate_left(17));
381 let ciphertext =
382 encrypt_example(&ctx, &pk, &message, 1 + (seed % 2) as u8, &mut enc_rng)
383 .expect("encrypt should work");
384 let recovered = decrypt_example(&ctx, &sk, &ciphertext, message.len())
385 .expect("decrypt should work");
386 assert_eq!(recovered, message, "seed {seed}");
387 }
388 }
389}