1use alloc::vec::Vec;
7use crypto_bigint::BoxedUint;
8use rand_core::CryptoRng;
9use zeroize::{Zeroize, ZeroizeOnDrop};
10
11use crate::{
12 arith::{
13 fixed_base::FixedBase,
14 monty::{Modulus, Residue},
15 sbig::{SBig, SRange},
16 },
17 error::{Error, Result},
18 params::Params,
19 rsa::tough::DfSecret,
20 transcript::Transcript,
21};
22
23const WINDOW: u32 = 4;
24const MAX_RESTARTS: usize = 64;
25
26#[derive(Clone, Debug)]
28pub struct DfStatement {
29 pub n_hat: Modulus,
30 pub t: Residue,
31 pub s1: Residue,
32 pub s2: Residue,
33}
34
35impl DfStatement {
36 pub fn new(n_hat: Modulus, t: Residue, s1: Residue, s2: Residue) -> Self {
37 Self { n_hat, t, s1, s2 }
38 }
39
40 pub fn from_uints(
42 n_hat: &BoxedUint,
43 t: &BoxedUint,
44 s1: &BoxedUint,
45 s2: &BoxedUint,
46 ) -> Result<Self> {
47 let m = Modulus::new(n_hat.clone())?;
48 let (t, s1, s2) = (m.residue(t), m.residue(s1), m.residue(s2));
49 Ok(Self {
50 n_hat: m,
51 t,
52 s1,
53 s2,
54 })
55 }
56
57 pub fn client(
59 m_hat: &BoxedUint,
60 v: &BoxedUint,
61 u1: &BoxedUint,
62 u2: &BoxedUint,
63 ) -> Result<Self> {
64 Self::from_uints(m_hat, v, u1, u2)
65 }
66
67 pub(crate) fn append_to(&self, tx: &mut Transcript) -> Result<()> {
69 let len = (self.n_hat.modulus().bits() as usize).div_ceil(8);
70 tx.append_uint("N", self.n_hat.modulus().as_ref(), len)?;
71 tx.append_residue("t", &self.n_hat, &self.t)?;
72 tx.append_residue("s1", &self.n_hat, &self.s1)?;
73 tx.append_residue("s2", &self.n_hat, &self.s2)?;
74 Ok(())
75 }
76
77 pub(crate) fn inverses(&self) -> Result<(Residue, Residue)> {
79 self.n_hat.invert(&self.t)?;
80 Ok((self.n_hat.invert(&self.s1)?, self.n_hat.invert(&self.s2)?))
81 }
82}
83
84#[derive(ZeroizeOnDrop)]
86pub struct DfWitness {
87 pub lambda1: BoxedUint,
88 pub lambda2: BoxedUint,
89}
90
91impl core::fmt::Debug for DfWitness {
92 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
93 f.write_str("DfWitness(***)")
94 }
95}
96
97impl DfWitness {
98 pub fn new(lambda1: BoxedUint, lambda2: BoxedUint) -> Self {
99 Self { lambda1, lambda2 }
100 }
101
102 pub fn from_secret(s: &DfSecret) -> Self {
103 Self {
104 lambda1: s.lambda1.clone(),
105 lambda2: s.lambda2.clone(),
106 }
107 }
108
109 pub(crate) fn as_sbig(&self, bits: u32) -> Result<(SBig, SBig)> {
111 if self.lambda1.bits() > bits || self.lambda2.bits() > bits {
112 return Err(Error::OutOfRange);
113 }
114 Ok((
115 SBig::from_uint(self.lambda1.clone()),
116 SBig::from_uint(self.lambda2.clone()),
117 ))
118 }
119}
120
121pub(crate) struct TBase {
124 fb: FixedBase,
125 shift: SBig,
126 t_inv_shift: Residue,
127}
128
129impl TBase {
130 pub(crate) fn new(m: &Modulus, t: &Residue, n: u32) -> Result<Self> {
131 let fb = FixedBase::new(m, t, n + 1, WINDOW);
132 let k =
133 SBig::from_uint(BoxedUint::one_with_precision(n.next_multiple_of(64) + 64).shl(n - 1));
134 let t_k = fb.pow_vartime(&k)?;
135 let t_inv_shift = m.invert(&t_k)?;
136 Ok(Self {
137 fb,
138 shift: k,
139 t_inv_shift,
140 })
141 }
142
143 pub(crate) fn pow_ct(&self, z: &SBig) -> Result<Residue> {
145 let mut s = z.add(&self.shift);
146 let r = self.fb.pow_ct(&s).map(|x| x.mul(&self.t_inv_shift));
147 s.zeroize();
148 r
149 }
150
151 pub(crate) fn pow_vartime(&self, z: &SBig) -> Result<Residue> {
153 Ok(self
154 .fb
155 .pow_vartime(&z.add(&self.shift))?
156 .mul(&self.t_inv_shift))
157 }
158}
159
160fn pack_bits(vals: &[u8]) -> Vec<u8> {
162 let mut out = alloc::vec![0u8; (2 * vals.len()).div_ceil(8)];
163 for (i, v) in vals.iter().enumerate() {
164 out[i / 4] |= (v & 3) << (2 * (i % 4));
165 }
166 out
167}
168
169fn unpack_bits(bytes: &[u8], r: usize) -> Result<Vec<u8>> {
170 if bytes.len() != (2 * r).div_ceil(8) {
171 return Err(Error::InvalidEncoding);
172 }
173 let out: Vec<u8> = (0..r)
174 .map(|i| (bytes[i / 4] >> (2 * (i % 4))) & 3)
175 .collect();
176 if pack_bits(&out) != bytes {
178 return Err(Error::InvalidEncoding);
179 }
180 Ok(out)
181}
182
183fn z_range(params: &Params) -> SRange {
184 SRange::pm_pow2(2 * params.ell + params.nu)
185}
186
187fn z_len(params: &Params) -> usize {
188 1 + ((2 * params.ell + params.nu - 1) as usize).div_ceil(8)
189}
190
191fn base_tx(tx: &Transcript, st: &DfStatement, params: &Params) -> Result<Transcript> {
192 let mut t = tx.clone();
193 t.append("zk", b"df");
194 st.append_to(&mut t)?;
195 t.append_u64("r", u64::from(params.r_df));
196 Ok(t)
197}
198
199fn derive_bits(t: &Transcript, a: &[Residue], st: &DfStatement) -> Result<Vec<u8>> {
200 let mut t = t.clone();
201 for ai in a {
202 t.append_residue("A", &st.n_hat, ai)?;
203 }
204 let mut ch = t.challenge("e");
205 Ok((0..a.len()).map(|_| ch.small(2) as u8).collect())
206}
207
208#[derive(Clone, Debug, PartialEq, Eq)]
210pub struct DfProof {
211 pub e: Vec<u8>,
212 pub z: Vec<SBig>,
213}
214
215impl DfProof {
216 pub fn prove<R: CryptoRng + ?Sized>(
217 rng: &mut R,
218 tx: &Transcript,
219 params: &Params,
220 st: &DfStatement,
221 w: &DfWitness,
222 ) -> Result<Self> {
223 let r = params.r_df as usize;
224 let n = 2 * params.ell + params.nu;
225 let (l1, l2) = {
226 let (a, b) = w.as_sbig(2 * params.ell)?;
227 (a, b)
228 };
229 let t0 = base_tx(tx, st, params)?;
230 let tb = TBase::new(&st.n_hat, &st.t, n)?;
231 let range = z_range(params);
232 for _ in 0..MAX_RESTARTS {
233 let mut alphas: Vec<SBig> = (0..r).map(|_| SBig::random_in(rng, range)).collect();
234 let a: Result<Vec<Residue>> = alphas.iter().map(|al| tb.pow_ct(al)).collect();
235 let a = match a {
236 Ok(a) => a,
237 Err(e) => {
238 alphas.zeroize();
239 return Err(e);
240 }
241 };
242 let e = derive_bits(&t0, &a, st)?;
243 let mut z = Vec::with_capacity(r);
244 let mut ok = true;
245 for (al, ei) in alphas.iter().zip(&e) {
246 let mut zi = al.clone();
247 if ei & 1 != 0 {
248 zi = zi.add(&l1);
249 }
250 if ei & 2 != 0 {
251 zi = zi.add(&l2);
252 }
253 ok &= zi.in_range(range);
254 z.push(zi);
255 }
256 alphas.zeroize();
257 if ok {
258 return Ok(Self { e, z });
259 }
260 z.zeroize();
261 }
262 Err(Error::OutOfRange)
263 }
264
265 pub fn verify(&self, tx: &Transcript, params: &Params, st: &DfStatement) -> Result<()> {
266 let r = params.r_df as usize;
267 let n = 2 * params.ell + params.nu;
268 if self.e.len() != r || self.z.len() != r || self.e.iter().any(|v| *v > 3) {
269 return Err(Error::InvalidEncoding);
270 }
271 let range = z_range(params);
272 if self.z.iter().any(|z| !z.in_range(range)) {
273 return Err(Error::OutOfRange);
274 }
275 let (s1i, s2i) = st.inverses()?;
276 let both = s1i.mul(&s2i);
277 let one = st.n_hat.one();
278 let tb = TBase::new(&st.n_hat, &st.t, n)?;
279 let mut a = Vec::with_capacity(r);
280 for (zi, ei) in self.z.iter().zip(&self.e) {
281 let c = match ei {
282 0 => &one,
283 1 => &s1i,
284 2 => &s2i,
285 _ => &both,
286 };
287 a.push(tb.pow_vartime(zi)?.mul(c));
288 }
289 let t0 = base_tx(tx, st, params)?;
290 if derive_bits(&t0, &a, st)? == self.e {
291 Ok(())
292 } else {
293 Err(Error::VerificationFailed)
294 }
295 }
296
297 pub fn encoded_len(params: &Params) -> usize {
298 (2 * params.r_df as usize).div_ceil(8) + params.r_df as usize * z_len(params)
299 }
300
301 pub fn encode(&self, params: &Params) -> Result<Vec<u8>> {
303 if self.e.len() != params.r_df as usize || self.z.len() != self.e.len() {
304 return Err(Error::InvalidEncoding);
305 }
306 let mut out = pack_bits(&self.e);
307 for z in &self.z {
308 out.extend_from_slice(&z.to_bytes(z_range(params))?);
309 }
310 Ok(out)
311 }
312
313 pub fn decode(bytes: &[u8], params: &Params) -> Result<Self> {
314 if bytes.len() != Self::encoded_len(params) {
315 return Err(Error::InvalidEncoding);
316 }
317 let r = params.r_df as usize;
318 let eb = (2 * r).div_ceil(8);
319 let e = unpack_bits(&bytes[..eb], r)?;
320 let z = bytes[eb..]
321 .chunks(z_len(params))
322 .map(|c| SBig::from_bytes(c, z_range(params)))
323 .collect::<Result<Vec<_>>>()?;
324 Ok(Self { e, z })
325 }
326}
327
328#[cfg(test)]
329pub(crate) mod tests {
330 use super::*;
331 use crate::{
332 params::{PARAMS_128, ParamsId},
333 rsa::tough::{ToughSpec, sample_df_with},
334 };
335 use rand_chacha::ChaCha8Rng;
336 use rand_core::SeedableRng;
337
338 pub(crate) fn small_params() -> Params {
340 Params {
341 id: ParamsId::P128,
342 ell: 16,
343 nu: 16,
344 r_df: 16,
345 r_df_star: 4,
346 ell0: 8,
347 ..PARAMS_128
348 }
349 }
350
351 pub(crate) fn small_stmt(seed: u64, p: &Params) -> (DfStatement, DfWitness, ChaCha8Rng) {
352 let mut rng = ChaCha8Rng::seed_from_u64(seed);
353 let d = sample_df_with(&mut rng, 512, ToughSpec { count: 4, bits: 64 }, p.ell).unwrap();
354 let st = DfStatement::from_uints(&d.n_hat, &d.t, &d.s1, &d.s2).unwrap();
355 (st, DfWitness::from_secret(&d.secret), rng)
356 }
357
358 #[test]
359 fn honest_roundtrip() {
360 let p = small_params();
361 let (st, w, mut rng) = small_stmt(1, &p);
362 let tx = Transcript::new("test");
363 for _ in 0..5 {
364 let pr = DfProof::prove(&mut rng, &tx, &p, &st, &w).unwrap();
365 pr.verify(&tx, &p, &st).unwrap();
366 let enc = pr.encode(&p).unwrap();
367 assert_eq!(enc.len(), DfProof::encoded_len(&p));
368 let q = DfProof::decode(&enc, &p).unwrap();
369 assert_eq!(pr, q);
370 q.verify(&tx, &p, &st).unwrap();
371 assert!(DfProof::decode(&enc[1..], &p).is_err());
372 let mut longer = enc.clone();
373 longer.push(0);
374 assert!(DfProof::decode(&longer, &p).is_err());
375 }
376 }
377
378 #[test]
379 fn tampering_fails() {
380 let p = small_params();
381 let (st, w, mut rng) = small_stmt(2, &p);
382 let tx = Transcript::new("test");
383 let pr = DfProof::prove(&mut rng, &tx, &p, &st, &w).unwrap();
384 for i in 0..pr.e.len() {
386 let mut b = pr.clone();
387 b.e[i] ^= 1;
388 assert!(b.verify(&tx, &p, &st).is_err());
389 let mut b = pr.clone();
390 b.e[i] ^= 2;
391 assert!(b.verify(&tx, &p, &st).is_err());
392 }
393 for i in 0..pr.z.len() {
395 for d in [1i64, -1] {
396 let mut b = pr.clone();
397 b.z[i] = b.z[i].add(&SBig::from_i64(d));
398 assert!(b.verify(&tx, &p, &st).is_err());
399 }
400 }
401 let n = 2 * p.ell + p.nu;
403 let mut b = pr.clone();
404 b.z[0] = SBig::from_uint(BoxedUint::one_with_precision(n + 64).shl(n - 1))
405 .add(&SBig::from_i64(1));
406 assert_eq!(b.verify(&tx, &p, &st), Err(Error::OutOfRange));
407 let mut b = pr.clone();
409 b.z.pop();
410 assert!(b.verify(&tx, &p, &st).is_err());
411 let m = &st.n_hat;
413 let one = m.one();
414 for which in 0..3 {
415 let mut s2 = st.clone();
416 match which {
417 0 => s2.t = s2.t.mul(&s2.t),
418 1 => s2.s1 = s2.s1.mul(&s2.t),
419 _ => s2.s2 = s2.s2.mul(&s2.t),
420 }
421 assert!(pr.verify(&tx, &p, &s2).is_err());
422 }
423 let mut s3 = st.clone();
425 s3.s1 = m.residue(&BoxedUint::zero_with_precision(64));
426 assert!(pr.verify(&tx, &p, &s3).is_err());
427 let _ = one;
428 let mut tx2 = Transcript::new("test");
430 tx2.append("x", b"y");
431 assert!(pr.verify(&tx2, &p, &st).is_err());
432 }
433
434 #[test]
435 fn wrong_witness_fails() {
436 let p = small_params();
437 let (st, w, mut rng) = small_stmt(3, &p);
438 let tx = Transcript::new("test");
439 let bad = DfWitness::new(
440 w.lambda1
441 .wrapping_add(BoxedUint::one_with_precision(w.lambda1.bits_precision())),
442 w.lambda2.clone(),
443 );
444 let pr = DfProof::prove(&mut rng, &tx, &p, &st, &bad).unwrap();
445 assert!(pr.verify(&tx, &p, &st).is_err());
446 let big = DfWitness::new(
448 BoxedUint::one_with_precision(128).shl(2 * p.ell),
449 w.lambda2.clone(),
450 );
451 assert!(DfProof::prove(&mut rng, &tx, &p, &st, &big).is_err());
452 }
453
454 #[test]
455 fn decode_rejects_bad() {
456 let p = small_params();
457 let (st, w, mut rng) = small_stmt(4, &p);
458 let tx = Transcript::new("test");
459 let enc = DfProof::prove(&mut rng, &tx, &p, &st, &w)
460 .unwrap()
461 .encode(&p)
462 .unwrap();
463 let eb = (2 * p.r_df as usize).div_ceil(8);
464 let mut b = enc.clone();
466 b[eb] = 2;
467 assert!(DfProof::decode(&b, &p).is_err());
468 let mut b = enc.clone();
470 b[eb] = 0;
471 b[eb + 1] = 0xff;
472 assert!(DfProof::decode(&b, &p).is_err());
473 let mut p15 = p;
475 p15.r_df = 15;
476 let mut e15 = alloc::vec![0u8; DfProof::encoded_len(&p15)];
477 e15[3] = 0x40; for i in 0..15 {
479 e15[4 + i * z_len(&p15)] = 0;
480 }
481 assert!(DfProof::decode(&e15, &p15).is_err());
482 }
483
484 #[test]
485 #[ignore]
486 fn full_size_timing() {
487 extern crate std;
488 let p = PARAMS_128;
489 let mut rng = ChaCha8Rng::seed_from_u64(9);
490 let d = crate::rsa::tough::sample_df(&mut rng, &p).unwrap();
491 let st = DfStatement::from_uints(&d.n_hat, &d.t, &d.s1, &d.s2).unwrap();
492 let w = DfWitness::from_secret(&d.secret);
493 let tx = Transcript::new("test");
494 let t0 = std::time::Instant::now();
495 let pr = DfProof::prove(&mut rng, &tx, &p, &st, &w).unwrap();
496 let t1 = std::time::Instant::now();
497 pr.verify(&tx, &p, &st).unwrap();
498 let t2 = std::time::Instant::now();
499 std::println!(
500 "df prove {:?} verify {:?} size {}",
501 t1 - t0,
502 t2 - t1,
503 pr.encode(&p).unwrap().len()
504 );
505 }
506}