1use alloc::vec::Vec;
12use crypto_bigint::BoxedUint;
13use k256::{ProjectivePoint, Scalar};
14use rand_core::CryptoRng;
15use zeroize::{Zeroize, ZeroizeOnDrop};
16
17use super::df::DfStatement;
18use crate::{
19 arith::{
20 monty::Residue,
21 multiexp::multiexp_vartime,
22 sbig::{SBig, SRange, resized},
23 },
24 curve::{encode_point, is_identity},
25 error::{Error, Result},
26 paillier::PaillierPk,
27 params::Params,
28 transcript::{Transcript, uint_to_be},
29};
30
31const MAX_ATTEMPTS: u32 = 64;
32
33pub struct DlencStatement<'a> {
35 pub x_pub: &'a ProjectivePoint,
37 pub e_ct: &'a Residue,
39 pub pk: &'a PaillierPk,
40 pub client: &'a DfStatement,
42}
43
44#[derive(ZeroizeOnDrop)]
46pub struct DlencWitness {
47 pub x: SBig,
48 pub beta: SBig,
49}
50
51impl core::fmt::Debug for DlencWitness {
52 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53 f.write_str("DlencWitness(***)")
54 }
55}
56
57impl DlencWitness {
58 pub fn new(x: SBig, beta: SBig) -> Self {
59 Self { x, beta }
60 }
61}
62
63#[derive(Clone, Debug, PartialEq, Eq)]
65pub struct DlencProof {
66 pub p_tilde: BoxedUint,
68 pub e: SBig,
69 pub z1: SBig,
70 pub z2: SBig,
71 pub z3: SBig,
72}
73
74fn e_range(p: &Params) -> SRange {
75 SRange::pm_pow2(p.ell)
76}
77fn z1_range(p: &Params) -> SRange {
78 SRange::pm_pow2(p.n_x() + p.eps())
79}
80fn z2_range(p: &Params) -> SRange {
81 SRange::pm_pow2(p.n_lambda() + p.eps())
82}
83fn mu_range(p: &Params) -> SRange {
84 SRange::pm_pow2(p.n_client + p.nu)
85}
86fn z3_range(p: &Params) -> SRange {
87 SRange::pm_pow2(p.n_client + p.nu + p.eps())
88}
89fn sbig_len(r: SRange) -> usize {
90 1 + (r.bits().saturating_sub(1) as usize).div_ceil(8).max(1)
91}
92fn p_len(p: &Params) -> usize {
93 (p.n_client as usize).div_ceil(8)
94}
95
96fn check_params(p: &Params) -> Result<()> {
97 if p.ell == 0 || p.ell > 200 || p.n_client == 0 {
99 return Err(Error::OutOfRange);
100 }
101 Ok(())
102}
103
104fn sbig_to_scalar(s: &SBig) -> Scalar {
106 let mut acc = Scalar::ZERO;
107 let c256 = Scalar::from(256u64);
108 for b in s.mag().to_be_bytes().iter() {
109 acc = acc * c256 + Scalar::from(u64::from(*b));
110 }
111 if bool::from(s.is_neg()) { -acc } else { acc }
112}
113
114fn lt(a: &BoxedUint, b: &BoxedUint) -> bool {
115 let bits = a.bits_precision().max(b.bits_precision());
116 resized(a, bits).cmp_vartime(resized(b, bits)) == core::cmp::Ordering::Less
117}
118
119fn base_tx(tx: &Transcript, st: &DlencStatement<'_>, params: &Params) -> Result<Transcript> {
120 let mut t = tx.clone();
121 t.append("zk", b"dlenc");
122 t.append_u64("n_x", u64::from(params.n_x()));
123 t.append_u64("n_l", u64::from(params.n_lambda()));
124 t.append("X", &encode_point(st.x_pub));
125 let n = st.pk.n();
126 t.append_uint("N", n, (n.bits() as usize).div_ceil(8))?;
127 t.append_residue("rho", st.pk.mod_n2(), st.pk.rho())?;
128 t.append_residue("E", st.pk.mod_n2(), st.e_ct)?;
129 st.client.append_to(&mut t)?;
130 Ok(t)
131}
132
133fn derive_e(
134 t: &Transcript,
135 st: &DlencStatement<'_>,
136 p_tilde: &Residue,
137 a: &ProjectivePoint,
138 b: &Residue,
139 d: &Residue,
140 params: &Params,
141) -> Result<SBig> {
142 let mut t = t.clone();
143 t.append_residue("P", &st.client.n_hat, p_tilde)?;
144 t.append("A", &encode_point(a));
145 t.append_residue("B", &st.client.n_hat, b)?;
146 t.append_residue("D", st.pk.mod_n2(), d)?;
147 Ok(t.challenge("e").pm_pow2(params.ell))
148}
149
150fn check_statement(st: &DlencStatement<'_>, params: &Params) -> Result<()> {
152 check_params(params)?;
153 if is_identity(st.x_pub) || st.client.n_hat.modulus().bits() > params.n_client {
154 return Err(Error::OutOfRange);
155 }
156 st.pk.check_ciphertext(&st.e_ct.retrieve())?;
157 Ok(())
158}
159
160impl DlencProof {
161 pub fn prove<R: CryptoRng + ?Sized>(
162 rng: &mut R,
163 tx: &Transcript,
164 params: &Params,
165 st: &DlencStatement<'_>,
166 w: &DlencWitness,
167 ) -> Result<Self> {
168 check_statement(st, params)?;
169 if !w.x.in_range(SRange::pm_pow2(params.n_x()))
170 || !w.beta.in_range(SRange::pm_pow2(params.n_lambda()))
171 {
172 return Err(Error::OutOfRange);
173 }
174 let m = &st.client.n_hat;
175 let (u1, u2, v) = (&st.client.s1, &st.client.s2, &st.client.t);
176 let t0 = base_tx(tx, st, params)?;
177 let (r1, r2, r3) = (z1_range(params), z2_range(params), z3_range(params));
178 let mut mu = SBig::random_in(rng, mu_range(params));
179 let p_tilde = m
180 .pow_ct(u1, &w.x, params.n_x())
181 .and_then(|a| {
182 let b = m.pow_ct(u2, &w.beta, params.n_lambda())?;
183 let c = m.pow_ct(v, &mu, mu_range(params).bits())?;
184 Ok(a.mul(&b).mul(&c))
185 })
186 .inspect_err(|_| mu.zeroize())?;
187
188 for _ in 0..MAX_ATTEMPTS {
189 let mut alpha = SBig::random_in(rng, r1);
190 let mut lam = SBig::random_in(rng, r2);
191 let mut mu2 = SBig::random_in(rng, r3);
192 let first = (|| -> Result<(ProjectivePoint, Residue, Residue)> {
193 let a = ProjectivePoint::GENERATOR * sbig_to_scalar(&alpha);
194 let b = m
195 .pow_ct(u1, &alpha, r1.bits())?
196 .mul(&m.pow_ct(u2, &lam, r2.bits())?)
197 .mul(&m.pow_ct(v, &mu2, r3.bits())?);
198 let d = st.pk.enc(&alpha, &lam, r2.bits())?;
199 Ok((a, b, d))
200 })();
201 let (a, b, d) = match first {
202 Ok(f) => f,
203 Err(e) => {
204 alpha.zeroize();
205 lam.zeroize();
206 mu2.zeroize();
207 mu.zeroize();
208 return Err(e);
209 }
210 };
211 let e = derive_e(&t0, st, &p_tilde, &a, &b, &d, params)?;
212 let z1 = alpha.add(&e.mul(&w.x));
213 let z2 = lam.add(&e.mul(&w.beta));
214 let z3 = mu2.add(&e.mul(&mu));
215 alpha.zeroize();
216 lam.zeroize();
217 mu2.zeroize();
218 if z1.in_range(r1) && z2.in_range(r2) && z3.in_range(r3) {
219 mu.zeroize();
220 return Ok(Self {
221 p_tilde: p_tilde.retrieve(),
222 e,
223 z1,
224 z2,
225 z3,
226 });
227 }
228 }
229 mu.zeroize();
230 Err(Error::OutOfRange)
231 }
232
233 pub fn verify(&self, tx: &Transcript, params: &Params, st: &DlencStatement<'_>) -> Result<()> {
234 check_statement(st, params).map_err(|_| Error::VerificationFailed)?;
235 if !self.e.in_range(e_range(params))
236 || !self.z1.in_range(z1_range(params))
237 || !self.z2.in_range(z2_range(params))
238 || !self.z3.in_range(z3_range(params))
239 {
240 return Err(Error::OutOfRange);
241 }
242 let m = &st.client.n_hat;
243 if !lt(&self.p_tilde, m.modulus().as_ref()) {
244 return Err(Error::OutOfRange);
245 }
246 let p_t = m.residue(&self.p_tilde);
247 m.invert(&p_t).map_err(|_| Error::VerificationFailed)?;
248 let neg_e = self.e.negate();
249 let a = ProjectivePoint::GENERATOR * sbig_to_scalar(&self.z1)
251 - *st.x_pub * sbig_to_scalar(&self.e);
252 if is_identity(&a) {
253 return Err(Error::VerificationFailed);
254 }
255 let b = multiexp_vartime(m, &[(&st.client.s1, &self.z1), (&st.client.s2, &self.z2)])?.mul(
257 &multiexp_vartime(m, &[(&st.client.t, &self.z3), (&p_t, &neg_e)])?,
258 );
259 let d = st.pk.g_pow(&self.z1).mul(&multiexp_vartime(
261 st.pk.mod_n2(),
262 &[(st.pk.rho(), &self.z2), (st.e_ct, &neg_e)],
263 )?);
264 let t0 = base_tx(tx, st, params)?;
265 if derive_e(&t0, st, &p_t, &a, &b, &d, params)? == self.e {
266 Ok(())
267 } else {
268 Err(Error::VerificationFailed)
269 }
270 }
271
272 pub fn encoded_len(params: &Params) -> usize {
273 p_len(params)
274 + sbig_len(e_range(params))
275 + sbig_len(z1_range(params))
276 + sbig_len(z2_range(params))
277 + sbig_len(z3_range(params))
278 }
279
280 pub fn encode(&self, params: &Params) -> Result<Vec<u8>> {
282 check_params(params)?;
283 let mut out = uint_to_be(&self.p_tilde, p_len(params))?;
284 out.extend_from_slice(&self.e.to_bytes(e_range(params))?);
285 out.extend_from_slice(&self.z1.to_bytes(z1_range(params))?);
286 out.extend_from_slice(&self.z2.to_bytes(z2_range(params))?);
287 out.extend_from_slice(&self.z3.to_bytes(z3_range(params))?);
288 Ok(out)
289 }
290
291 pub fn decode(bytes: &[u8], params: &Params) -> Result<Self> {
292 check_params(params)?;
293 if bytes.len() != Self::encoded_len(params) {
294 return Err(Error::InvalidEncoding);
295 }
296 let (pb, rest) = bytes.split_at(p_len(params));
297 let p_tilde = crate::transcript::uint_from_be(pb, params.n_client)?;
298 let (eb, rest) = rest.split_at(sbig_len(e_range(params)));
299 let (z1b, rest) = rest.split_at(sbig_len(z1_range(params)));
300 let (z2b, z3b) = rest.split_at(sbig_len(z2_range(params)));
301 Ok(Self {
302 p_tilde,
303 e: SBig::from_bytes(eb, e_range(params))?,
304 z1: SBig::from_bytes(z1b, z1_range(params))?,
305 z2: SBig::from_bytes(z2b, z2_range(params))?,
306 z3: SBig::from_bytes(z3b, z3_range(params))?,
307 })
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 #![allow(clippy::unwrap_used)]
314 extern crate std;
315 use super::*;
316 use crate::{
317 arith::monty::Modulus,
318 params::{PARAMS_128, ParamsId},
319 rsa::tough::{ToughSpec, sample_df_with, sample_rsa_with},
320 };
321 use crypto_bigint::RandomBits;
322 use rand_chacha::ChaCha8Rng;
323 use rand_core::SeedableRng;
324
325 fn small_params() -> Params {
326 Params {
327 id: ParamsId::P128,
328 ell: 16,
329 nu: 16,
330 nu_leak: 16,
331 n_client: 512,
332 ..PARAMS_128
333 }
334 }
335
336 struct Fx {
337 p: Params,
338 pk: PaillierPk,
339 client: DfStatement,
340 x_pub: ProjectivePoint,
341 e_ct: Residue,
342 w: DlencWitness,
343 }
344
345 fn make_pk(rng: &mut ChaCha8Rng, n: BoxedUint) -> PaillierPk {
346 let n2 = crypto_bigint::ConcatenatingMul::concatenating_mul(&n, &n);
347 let m2 = Modulus::new(n2).unwrap();
348 let r = m2.residue(&BoxedUint::random_bits(rng, 1000));
349 PaillierPk::new(n, &r.square().retrieve()).unwrap()
350 }
351
352 fn fixture(
353 seed: u64,
354 p: Params,
355 n_bits: u32,
356 client: Option<DfStatement>,
357 neg: bool,
358 ) -> (Fx, ChaCha8Rng) {
359 let mut rng = ChaCha8Rng::seed_from_u64(seed);
360 let spec = ToughSpec { count: 4, bits: 64 };
361 let client = client.unwrap_or_else(|| {
362 let d = sample_df_with(&mut rng, p.n_client, spec, p.ell).unwrap();
363 DfStatement::from_uints(&d.n_hat, &d.t, &d.s1, &d.s2).unwrap()
364 });
365 let spec_n = if n_bits == p.n_rsa {
366 ToughSpec::from_params(&p)
367 } else {
368 spec
369 };
370 let n = sample_rsa_with(&mut rng, n_bits, spec_n).unwrap().n;
371 let pk = make_pk(&mut rng, n);
372 let x = SBig::random_in(&mut rng, SRange::pm_pow2(p.n_x()));
373 let x = if neg && !bool::from(x.is_neg()) {
374 x.negate()
375 } else {
376 x
377 };
378 let beta = SBig::random_in(&mut rng, SRange::pm_pow2(p.n_lambda()));
379 let x_pub = ProjectivePoint::GENERATOR * sbig_to_scalar(&x);
380 let e_ct = pk.enc(&x, &beta, p.n_lambda()).unwrap();
381 (
382 Fx {
383 p,
384 pk,
385 client,
386 x_pub,
387 e_ct,
388 w: DlencWitness::new(x, beta),
389 },
390 rng,
391 )
392 }
393
394 impl Fx {
395 fn st(&self) -> DlencStatement<'_> {
396 DlencStatement {
397 x_pub: &self.x_pub,
398 e_ct: &self.e_ct,
399 pk: &self.pk,
400 client: &self.client,
401 }
402 }
403 }
404
405 fn small(seed: u64) -> (Fx, ChaCha8Rng) {
406 fixture(seed, small_params(), 512, None, false)
407 }
408
409 #[test]
410 fn honest_roundtrip() {
411 for (seed, neg) in [(1u64, false), (2, true)] {
412 let (f, mut rng) = fixture(seed, small_params(), 512, None, neg);
413 let tx = Transcript::new("test");
414 for _ in 0..4 {
415 let pr = DlencProof::prove(&mut rng, &tx, &f.p, &f.st(), &f.w).unwrap();
416 pr.verify(&tx, &f.p, &f.st()).unwrap();
417 let enc = pr.encode(&f.p).unwrap();
418 assert_eq!(enc.len(), DlencProof::encoded_len(&f.p));
419 let q = DlencProof::decode(&enc, &f.p).unwrap();
420 assert_eq!(pr, q);
421 q.verify(&tx, &f.p, &f.st()).unwrap();
422 assert!(DlencProof::decode(&enc[1..], &f.p).is_err());
423 let mut longer = enc.clone();
424 longer.push(0);
425 assert!(DlencProof::decode(&longer, &f.p).is_err());
426 }
427 }
428 }
429
430 #[test]
431 fn tampering_fails() {
432 let (f, mut rng) = small(3);
433 let tx = Transcript::new("test");
434 let pr = DlencProof::prove(&mut rng, &tx, &f.p, &f.st(), &f.w).unwrap();
435 let one = SBig::from_i64(1);
436 for d in [one.clone(), one.negate()] {
437 let mut b = pr.clone();
438 b.e = b.e.add(&d);
439 assert!(b.verify(&tx, &f.p, &f.st()).is_err());
440 let mut b = pr.clone();
441 b.z1 = b.z1.add(&d);
442 assert!(b.verify(&tx, &f.p, &f.st()).is_err());
443 let mut b = pr.clone();
444 b.z2 = b.z2.add(&d);
445 assert!(b.verify(&tx, &f.p, &f.st()).is_err());
446 let mut b = pr.clone();
447 b.z3 = b.z3.add(&d);
448 assert!(b.verify(&tx, &f.p, &f.st()).is_err());
449 }
450 let mut b = pr.clone();
452 b.p_tilde = b
453 .p_tilde
454 .wrapping_add(BoxedUint::one_with_precision(b.p_tilde.bits_precision()));
455 assert!(b.verify(&tx, &f.p, &f.st()).is_err());
456 let mut b = pr.clone();
457 b.p_tilde = BoxedUint::zero_with_precision(64);
458 assert!(b.verify(&tx, &f.p, &f.st()).is_err());
459 let mut b = pr.clone();
460 b.p_tilde = pr.p_tilde.wrapping_add(f.client.n_hat.modulus().as_ref());
461 assert_eq!(b.verify(&tx, &f.p, &f.st()), Err(Error::OutOfRange));
462 for which in 0..3 {
464 let n = match which {
465 0 => f.p.n_x() + f.p.eps(),
466 1 => f.p.n_lambda() + f.p.eps(),
467 _ => f.p.n_client + f.p.nu + f.p.eps(),
468 };
469 let edge = SBig::from_uint(BoxedUint::one_with_precision(n + 64).shl(n - 1));
470 let mut b = pr.clone();
471 match which {
472 0 => b.z1 = edge.clone(),
473 1 => b.z2 = edge.clone(),
474 _ => b.z3 = edge.clone(),
475 }
476 assert_eq!(b.verify(&tx, &f.p, &f.st()), Err(Error::VerificationFailed));
477 match which {
478 0 => b.z1 = edge.add(&one),
479 1 => b.z2 = edge.add(&one),
480 _ => b.z3 = edge.add(&one),
481 }
482 assert_eq!(b.verify(&tx, &f.p, &f.st()), Err(Error::OutOfRange));
483 }
484 let mut b = pr.clone();
485 b.e = SBig::from_uint(BoxedUint::one_with_precision(128).shl(f.p.ell - 1)).add(&one);
486 assert_eq!(b.verify(&tx, &f.p, &f.st()), Err(Error::OutOfRange));
487 let other = f.x_pub + ProjectivePoint::GENERATOR;
489 let st = DlencStatement {
490 x_pub: &other,
491 ..f.st()
492 };
493 assert!(pr.verify(&tx, &f.p, &st).is_err());
494 let ident = ProjectivePoint::IDENTITY;
495 let st = DlencStatement {
496 x_pub: &ident,
497 ..f.st()
498 };
499 assert!(pr.verify(&tx, &f.p, &st).is_err());
500 let e2 = f.e_ct.mul(&f.pk.g_pow(&one));
501 let st = DlencStatement {
502 e_ct: &e2,
503 ..f.st()
504 };
505 assert!(pr.verify(&tx, &f.p, &st).is_err());
506 for which in 0..4 {
508 let mut c = f.client.clone();
509 match which {
510 0 => c.t = c.t.mul(&c.t),
511 1 => c.s1 = c.s1.mul(&c.t),
512 2 => c.s2 = c.s2.mul(&c.t),
513 _ => c.s1 = c.s2.clone(),
514 }
515 let st = DlencStatement {
516 client: &c,
517 ..f.st()
518 };
519 assert!(pr.verify(&tx, &f.p, &st).is_err());
520 }
521 let pk2 = make_pk(&mut rng, f.pk.n().clone());
523 let st = DlencStatement { pk: &pk2, ..f.st() };
524 assert!(pr.verify(&tx, &f.p, &st).is_err());
525 let mut tx2 = Transcript::new("test");
527 tx2.append("x", b"y");
528 assert!(pr.verify(&tx2, &f.p, &f.st()).is_err());
529 }
530
531 #[test]
532 fn wrong_witness_fails() {
533 let (f, mut rng) = small(4);
534 let tx = Transcript::new("test");
535 let one = SBig::from_i64(1);
536 let bad = DlencWitness::new(f.w.x.add(&one), f.w.beta.clone());
537 let pr = DlencProof::prove(&mut rng, &tx, &f.p, &f.st(), &bad).unwrap();
538 assert!(pr.verify(&tx, &f.p, &f.st()).is_err());
539 let bad = DlencWitness::new(f.w.x.clone(), f.w.beta.add(&one));
540 let pr = DlencProof::prove(&mut rng, &tx, &f.p, &f.st(), &bad).unwrap();
541 assert!(pr.verify(&tx, &f.p, &f.st()).is_err());
542 let big = SBig::from_uint(BoxedUint::one_with_precision(512).shl(f.p.n_x()));
544 let bad = DlencWitness::new(big, f.w.beta.clone());
545 assert!(DlencProof::prove(&mut rng, &tx, &f.p, &f.st(), &bad).is_err());
546 }
547
548 #[test]
549 fn bad_ciphertext_and_rho() {
550 let (f, mut rng) = small(5);
551 let tx = Transcript::new("test");
552 let pr = DlencProof::prove(&mut rng, &tx, &f.p, &f.st(), &f.w).unwrap();
553 let pk2 = make_pk(&mut rng, f.pk.n().clone());
555 let e2 = pk2.enc(&f.w.x, &f.w.beta, f.p.n_lambda()).unwrap();
556 let st = DlencStatement {
557 e_ct: &e2,
558 ..f.st()
559 };
560 assert!(pr.verify(&tx, &f.p, &st).is_err());
561 assert!(
562 DlencProof::prove(&mut rng, &tx, &f.p, &st, &f.w)
563 .unwrap()
564 .verify(&tx, &f.p, &st)
565 .is_err()
566 );
567 let e3 = f.pk.mod_n2().residue(f.pk.n());
569 let st = DlencStatement {
570 e_ct: &e3,
571 ..f.st()
572 };
573 assert!(pr.verify(&tx, &f.p, &st).is_err());
574 assert!(DlencProof::prove(&mut rng, &tx, &f.p, &st, &f.w).is_err());
575 let e4 = f.pk.mod_n2().residue(&BoxedUint::zero_with_precision(64));
576 let st = DlencStatement {
577 e_ct: &e4,
578 ..f.st()
579 };
580 assert!(pr.verify(&tx, &f.p, &st).is_err());
581 }
582
583 #[test]
584 fn decode_rejects_bad() {
585 let (f, mut rng) = small(6);
586 let tx = Transcript::new("test");
587 let p = f.p;
588 let enc = DlencProof::prove(&mut rng, &tx, &p, &f.st(), &f.w)
589 .unwrap()
590 .encode(&p)
591 .unwrap();
592 let o = p_len(&p);
593 let mut b = enc.clone();
595 b[o] = 2;
596 assert!(DlencProof::decode(&b, &p).is_err());
597 let el = sbig_len(e_range(&p));
599 let mut b = enc.clone();
600 b[o] = 1;
601 for v in b[o + 1..o + el].iter_mut() {
602 *v = 0;
603 }
604 assert!(DlencProof::decode(&b, &p).is_err());
605 let mut b = enc.clone();
607 b[o + el] = 0;
608 b[o + el + 1] = 0xff;
609 assert!(DlencProof::decode(&b, &p).is_err());
610 }
611
612 #[test]
613 #[ignore]
614 fn full_size_timing() {
615 let p = PARAMS_128;
616 let mut rng = ChaCha8Rng::seed_from_u64(9);
617 let d = crate::rsa::tough::sample_df_client(&mut rng, &p).unwrap();
618 let client = DfStatement::client(&d.n_hat, &d.t, &d.s1, &d.s2).unwrap();
619 let (f, mut rng) = fixture(10, p, p.n_rsa, Some(client), true);
620 let tx = Transcript::new("test");
621 let t0 = std::time::Instant::now();
622 let pr = DlencProof::prove(&mut rng, &tx, &p, &f.st(), &f.w).unwrap();
623 let t1 = std::time::Instant::now();
624 pr.verify(&tx, &p, &f.st()).unwrap();
625 let t2 = std::time::Instant::now();
626 let enc = pr.encode(&p).unwrap();
627 std::println!(
628 "dlenc prove {:?} verify {:?} size {} (P {} e {} z1 {} z2 {} z3 {})",
629 t1 - t0,
630 t2 - t1,
631 enc.len(),
632 p_len(&p),
633 sbig_len(e_range(&p)),
634 sbig_len(z1_range(&p)),
635 sbig_len(z2_range(&p)),
636 sbig_len(z3_range(&p)),
637 );
638 }
639}