rlwe_pke/rlwe_pke.rs
1// MIT License
2//
3// Copyright (c) 2026 Raja Lehtihet & Wael El Oraiby
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23//! Demonstrates the example RLWE-style PKE API end to end.
24//!
25//! Run with:
26//! `cargo run --example rlwe_pke`
27//!
28//! Flow:
29//! 1. Build `R_q = Z_q[x] / (x^32 + 1)`.
30//! 2. Generate keypair (`a`, `b`, `s`) with small-noise secret.
31//! 3. Encrypt a short message.
32//! 4. Decrypt and assert round-trip equality.
33
34mod common;
35
36use common::pke::{decrypt_example, encrypt_example, keygen_example};
37use core::convert::Infallible;
38use nc_polynomial::RingContext;
39use rand_core::{TryCryptoRng, TryRng};
40
41#[derive(Debug, Clone)]
42struct ExampleRng {
43 state: u64,
44}
45
46impl ExampleRng {
47 /// Creates a deterministic RNG state for reproducible example output.
48 fn new(seed: u64) -> Self {
49 Self { state: seed }
50 }
51
52 /// xorshift64* transition.
53 ///
54 /// This is a deterministic example RNG, not a production CSPRNG.
55 fn step(&mut self) -> u64 {
56 let mut x = self.state;
57 x ^= x >> 12;
58 x ^= x << 25;
59 x ^= x >> 27;
60 self.state = x;
61 x.wrapping_mul(0x2545_F491_4F6C_DD1D)
62 }
63}
64
65impl TryRng for ExampleRng {
66 type Error = Infallible;
67
68 fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
69 Ok(self.step() as u32)
70 }
71
72 fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
73 Ok(self.step())
74 }
75
76 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
77 let mut offset = 0;
78 while offset < dest.len() {
79 let word = self.step().to_le_bytes();
80 let chunk = (dest.len() - offset).min(8);
81 dest[offset..offset + chunk].copy_from_slice(&word[..chunk]);
82 offset += chunk;
83 }
84 Ok(())
85 }
86}
87
88impl TryCryptoRng for ExampleRng {}
89
90fn main() {
91 // Ring modulus polynomial `x^32 + 1`.
92 let mut modulus_poly = vec![0_u64; 32 + 1];
93 modulus_poly[0] = 1;
94 modulus_poly[32] = 1;
95 // Standard NTT-friendly prime modulus used elsewhere in the crate examples.
96 let ctx =
97 RingContext::from_parts(32, 998_244_353, &modulus_poly, 3).expect("context should build");
98
99 // Key generation randomness.
100 let mut key_rng = ExampleRng::new(0xDEAD_BEEF_1234_0001);
101 // Generates:
102 // - public key (a, b = a*s + e)
103 // - secret key (s)
104 let (public_key, secret_key) =
105 keygen_example(&ctx, 2, &mut key_rng).expect("keygen should work");
106
107 // Message payload.
108 let message = b"ok";
109 // Encryption randomness must be independent from keygen randomness.
110 let mut enc_rng = ExampleRng::new(0xFACE_CAFE_2222_0002);
111 // Produces ciphertext (u, v).
112 let ciphertext =
113 encrypt_example(&ctx, &public_key, message, 2, &mut enc_rng).expect("encrypt should work");
114
115 // Decrypt and verify.
116 let decrypted = decrypt_example(&ctx, &secret_key, &ciphertext, message.len())
117 .expect("decrypt should work");
118 assert_eq!(decrypted, message);
119
120 // Human-readable output for quick manual check.
121 println!("message={:?}", String::from_utf8_lossy(message));
122 println!("decrypted={:?}", String::from_utf8_lossy(&decrypted));
123}