Skip to main content

tecdsa_core/
setup.rs

1//! Server setup and client verification (paper Algorithm 4, spec §6.1 FR-S1..S8).
2//!
3//! `h, f` are derived from `aux = setup_id ‖ u32be(len(server_id)) ‖ server_id`; the server
4//! draws `setup_id` at random. `sid = SHA-256(canonical SetupData bytes)` is the value the
5//! application pins (FR-S8).
6use alloc::vec::Vec;
7use core::cmp::Ordering;
8use crypto_bigint::{BoxedUint, ConcatenatingMul, Gcd, Integer, NonZero, RandomMod};
9use k256::ProjectivePoint;
10use rand_core::CryptoRng;
11use sha2::{Digest, Sha256};
12use zeroize::{ZeroizeOnDrop, Zeroizing};
13
14use crate::{
15    arith::{
16        fixed_base::FixedBase,
17        monty::Modulus,
18        sbig::{SBig, resized},
19    },
20    curve::{decode_point, derive_h_f, encode_point, is_identity},
21    df::DfParams,
22    error::{Error, Result},
23    paillier::{PaillierPk, PaillierSk},
24    params::Params,
25    rsa::tough::{sample_df, sample_rsa},
26    transcript::{Transcript, uint_from_be, uint_to_be},
27    zk::{
28        df::{DfProof, DfStatement, DfWitness},
29        fac::{FacParams, FacProof, FacWitness},
30        modulus::{ModProof, ModWitness},
31    },
32};
33
34/// Canonical format version (first byte of the encoding).
35pub const FORMAT_VERSION: u8 = 0x01;
36/// Maximal length of the server identifier.
37pub const MAX_SERVER_ID_LEN: usize = 256;
38/// Window of the fixed-base table for `ρ`.
39pub const RHO_TABLE_WINDOW: u32 = 4;
40/// Window of the fixed-base tables for `t, s₁, s₂`.
41pub const DF_TABLE_WINDOW: u32 = 3;
42
43/// Public setup of the server (spec §6.1).
44#[derive(Clone, Debug)]
45pub struct SetupData {
46    pub params: Params,
47    pub fac: FacParams,
48    pub setup_id: [u8; 32],
49    pub server_id: Vec<u8>,
50    pub n: BoxedUint,
51    pub n_hat: BoxedUint,
52    pub rho0: BoxedUint,
53    pub rho: BoxedUint,
54    pub t: BoxedUint,
55    pub s1: BoxedUint,
56    pub s2: BoxedUint,
57    pub h: ProjectivePoint,
58    pub f: ProjectivePoint,
59    pub pi_mod: ModProof,
60    pub pi_fac: FacProof,
61    pub pi_df: DfProof,
62}
63
64/// Secret output of the server setup. Zeroized on drop (the key zeroizes itself).
65#[derive(ZeroizeOnDrop)]
66pub struct ServerSetupSecret {
67    #[zeroize(skip)]
68    sk: PaillierSk,
69}
70
71impl core::fmt::Debug for ServerSetupSecret {
72    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
73        f.write_str("ServerSetupSecret(***)")
74    }
75}
76
77/// Version byte of the exported server setup secret.
78const SECRET_VERSION: u8 = 0x01;
79
80impl ServerSetupSecret {
81    pub fn paillier_sk(&self) -> &PaillierSk {
82        &self.sk
83    }
84
85    /// Explicit export for a `SecretStore`: `version ‖ p ‖ q`, each `n_rsa/16` bytes.
86    pub fn export_secret(&self, params: &Params) -> Result<Zeroizing<Vec<u8>>> {
87        let len = nb(params) / 2;
88        let (p, q) = self.sk.factors();
89        let mut out = Zeroizing::new(Vec::with_capacity(1 + 2 * len));
90        out.push(SECRET_VERSION);
91        out.extend_from_slice(&Zeroizing::new(uint_to_be(p, len)?));
92        out.extend_from_slice(&Zeroizing::new(uint_to_be(q, len)?));
93        Ok(out)
94    }
95
96    /// Restores the secret exported by [`Self::export_secret`]; rejects it unless `p·q` is the
97    /// Paillier modulus `N` of `data`, so a secret cannot be paired with a foreign setup.
98    pub fn import_secret(bytes: &[u8], data: &SetupData) -> Result<Self> {
99        let len = nb(&data.params) / 2;
100        if bytes.len() != 1 + 2 * len || bytes[0] != SECRET_VERSION {
101            return Err(Error::InvalidEncoding);
102        }
103        let bits = len as u32 * 8;
104        let p = uint_from_be(&bytes[1..1 + len], bits)?;
105        let q = uint_from_be(&bytes[1 + len..], bits)?;
106        let sk = PaillierSk::new(p, q)?;
107        let (p, q) = sk.factors();
108        let n = p.concatenating_mul(q);
109        let bits = n.bits_precision().max(data.n.bits_precision());
110        if resized(&n, bits).cmp_vartime(resized(&data.n, bits)) != Ordering::Equal {
111            return Err(Error::InvalidEncoding);
112        }
113        Ok(Self { sk })
114    }
115}
116
117fn nb(params: &Params) -> usize {
118    (params.n_rsa as usize).div_ceil(8)
119}
120
121fn lt(a: &BoxedUint, b: &BoxedUint) -> bool {
122    let bits = a.bits_precision().max(b.bits_precision());
123    resized(a, bits).cmp_vartime(resized(b, bits)) == Ordering::Less
124}
125
126fn is_unit(a: &BoxedUint, n: &BoxedUint) -> bool {
127    let bits = a.bits_precision().max(n.bits_precision());
128    bool::from(resized(a, bits).gcd_vartime(&resized(n, bits)).is_one())
129}
130
131fn aux(setup_id: &[u8; 32], server_id: &[u8]) -> Vec<u8> {
132    let mut a = Vec::with_capacity(36 + server_id.len());
133    a.extend_from_slice(setup_id);
134    a.extend_from_slice(&(server_id.len() as u32).to_be_bytes());
135    a.extend_from_slice(server_id);
136    a
137}
138
139/// Base transcript shared by the three proofs.
140fn base_tx(params: &Params, setup_id: &[u8; 32], server_id: &[u8]) -> Transcript {
141    let mut tx = Transcript::new("setup");
142    tx.append("params_id", &[params.id as u8]);
143    tx.append("setup_id", setup_id);
144    tx.append("server_id", server_id);
145    tx
146}
147
148/// `ρ0^{2N} mod N²`.
149fn rho_of(n: &BoxedUint, rho0: &BoxedUint) -> Result<BoxedUint> {
150    let n2 = Modulus::new(n.concatenating_mul_self())?;
151    let exp = resized(n, n.bits_precision() + 64);
152    let exp = exp.wrapping_add(&exp);
153    let r = n2.pow_vartime(&n2.residue(rho0), &SBig::from_uint(exp))?;
154    Ok(r.retrieve())
155}
156
157trait SquareExt {
158    fn concatenating_mul_self(&self) -> BoxedUint;
159}
160impl SquareExt for BoxedUint {
161    fn concatenating_mul_self(&self) -> BoxedUint {
162        crypto_bigint::ConcatenatingMul::concatenating_mul(self, self)
163    }
164}
165
166/// Server setup with the standard Π_fac group.
167pub fn server_setup<R: CryptoRng + ?Sized>(
168    rng: &mut R,
169    params: &Params,
170    setup_id: [u8; 32],
171    server_id: &[u8],
172) -> Result<(SetupData, ServerSetupSecret)> {
173    server_setup_with(rng, params, &FacParams::standard()?, setup_id, server_id)
174}
175
176/// Server setup with an explicit Π_fac group (tests).
177pub fn server_setup_with<R: CryptoRng + ?Sized>(
178    rng: &mut R,
179    params: &Params,
180    fac: &FacParams,
181    setup_id: [u8; 32],
182    server_id: &[u8],
183) -> Result<(SetupData, ServerSetupSecret)> {
184    if server_id.len() > MAX_SERVER_ID_LEN {
185        return Err(Error::OutOfRange);
186    }
187    let tx = base_tx(params, &setup_id, server_id);
188    // FR-S1, FR-S2
189    let rsa = sample_rsa(rng, params).map_err(|_| Error::InvalidModulus)?;
190    let n = rsa.n.clone();
191    let (p1, p2) = (rsa.secret.p1.p(), rsa.secret.p2.p());
192    let pi_mod = ModProof::prove(rng, &tx, params, &n, &ModWitness { p1, p2 })?;
193    let pi_fac = FacProof::prove(rng, &tx, params, fac, &n, &FacWitness { p: p1, q: p2 })?;
194    let sk = PaillierSk::new(p1.clone(), p2.clone())?;
195    // FR-S3
196    let nz = Option::<NonZero<BoxedUint>>::from(n.to_nz()).ok_or(Error::InvalidModulus)?;
197    let rho0 = loop {
198        let r = BoxedUint::random_mod_vartime(rng, &nz);
199        if is_unit(&r, &n) {
200            break r;
201        }
202    };
203    let rho = rho_of(&n, &rho0)?;
204    // FR-S4: the tuple (with its factorization and λ's) is zeroized when dropped.
205    let df = sample_df(rng, params).map_err(|_| Error::InvalidModulus)?;
206    let st = DfStatement::from_uints(&df.n_hat, &df.t, &df.s1, &df.s2)?;
207    let pi_df = DfProof::prove(rng, &tx, params, &st, &DfWitness::from_secret(&df.secret))?;
208    // FR-S5
209    let (h, f) = derive_h_f(&aux(&setup_id, server_id))?;
210    let data = SetupData {
211        params: *params,
212        fac: fac.clone(),
213        setup_id,
214        server_id: server_id.to_vec(),
215        n,
216        n_hat: df.n_hat.clone(),
217        rho0,
218        rho,
219        t: df.t.clone(),
220        s1: df.s1.clone(),
221        s2: df.s2.clone(),
222        h,
223        f,
224        pi_mod,
225        pi_fac,
226        pi_df,
227    };
228    Ok((data, ServerSetupSecret { sk }))
229}
230
231struct Reader<'a> {
232    b: &'a [u8],
233    pos: usize,
234}
235
236impl<'a> Reader<'a> {
237    fn take(&mut self, len: usize) -> Result<&'a [u8]> {
238        let end = self.pos.checked_add(len).ok_or(Error::InvalidEncoding)?;
239        let s = self.b.get(self.pos..end).ok_or(Error::InvalidEncoding)?;
240        self.pos = end;
241        Ok(s)
242    }
243}
244
245impl SetupData {
246    /// `aux` seed of `h, f`.
247    pub fn aux(&self) -> Vec<u8> {
248        aux(&self.setup_id, &self.server_id)
249    }
250
251    /// Paillier public key `(N, ρ)`.
252    pub fn paillier_pk(&self) -> Result<PaillierPk> {
253        PaillierPk::new(self.n.clone(), &self.rho)
254    }
255
256    /// Canonical encoding (spec §10.2).
257    pub fn to_bytes(&self) -> Result<Vec<u8>> {
258        let p = &self.params;
259        let len = nb(p);
260        if self.server_id.len() > MAX_SERVER_ID_LEN {
261            return Err(Error::OutOfRange);
262        }
263        let mut out = Vec::new();
264        out.push(FORMAT_VERSION);
265        out.extend_from_slice(&self.setup_id);
266        out.extend_from_slice(&(self.server_id.len() as u16).to_be_bytes());
267        out.extend_from_slice(&self.server_id);
268        for v in [&self.n, &self.n_hat, &self.rho0] {
269            out.extend_from_slice(&uint_to_be(v, len)?);
270        }
271        out.extend_from_slice(&uint_to_be(&self.rho, 2 * len)?);
272        for v in [&self.t, &self.s1, &self.s2] {
273            out.extend_from_slice(&uint_to_be(v, len)?);
274        }
275        for pt in [&self.h, &self.f] {
276            if is_identity(pt) {
277                return Err(Error::InvalidEncoding);
278            }
279            out.extend_from_slice(&encode_point(pt));
280        }
281        out.extend_from_slice(&self.pi_mod.encode(p.n_rsa)?);
282        out.extend_from_slice(&self.pi_fac.encode(p, &self.fac, p.n_rsa)?);
283        out.extend_from_slice(&self.pi_df.encode(p)?);
284        Ok(out)
285    }
286
287    /// `sid = SHA-256(to_bytes())`.
288    pub fn hash(&self) -> Result<[u8; 32]> {
289        Ok(Sha256::digest(self.to_bytes()?).into())
290    }
291
292    /// Strict decoding with the standard Π_fac group.
293    pub fn from_bytes(bytes: &[u8], params: &Params) -> Result<Self> {
294        Self::from_bytes_with(bytes, params, &FacParams::standard()?)
295    }
296
297    pub fn from_bytes_with(bytes: &[u8], params: &Params, fac: &FacParams) -> Result<Self> {
298        let bad = Error::InvalidEncoding;
299        let len = nb(params);
300        let mut r = Reader { b: bytes, pos: 0 };
301        if r.take(1)?[0] != FORMAT_VERSION {
302            return Err(bad);
303        }
304        let setup_id: [u8; 32] = r.take(32)?.try_into().map_err(|_| bad)?;
305        let sl = u16::from_be_bytes(r.take(2)?.try_into().map_err(|_| bad)?) as usize;
306        if sl > MAX_SERVER_ID_LEN {
307            return Err(bad);
308        }
309        let server_id = r.take(sl)?.to_vec();
310        let fixed = 8 * len + 66;
311        let (ml, fl, dl) = (
312            ModProof::encoded_len(params, params.n_rsa),
313            FacProof::encoded_len(params, fac, params.n_rsa),
314            DfProof::encoded_len(params),
315        );
316        if bytes.len() != r.pos + fixed + ml + fl + dl {
317            return Err(bad);
318        }
319        let mut uint = |l: usize| uint_from_be(r.take(l)?, params.n_rsa);
320        let n = uint(len)?;
321        let n_hat = uint(len)?;
322        let rho0 = uint(len)?;
323        let rho = uint(2 * len)?;
324        let t = uint(len)?;
325        let s1 = uint(len)?;
326        let s2 = uint(len)?;
327        let h = decode_point(r.take(33)?)?;
328        let f = decode_point(r.take(33)?)?;
329        let pi_mod = ModProof::decode(r.take(ml)?, params, params.n_rsa)?;
330        let pi_fac = FacProof::decode(r.take(fl)?, params, fac, params.n_rsa)?;
331        let pi_df = DfProof::decode(r.take(dl)?, params)?;
332        let data = Self {
333            params: *params,
334            fac: fac.clone(),
335            setup_id,
336            server_id,
337            n,
338            n_hat,
339            rho0,
340            rho,
341            t,
342            s1,
343            s2,
344            h,
345            f,
346            pi_mod,
347            pi_fac,
348            pi_df,
349        };
350        data.check_basic()?;
351        Ok(data)
352    }
353
354    /// Cheap structural checks: lengths, sizes, ranges, units.
355    fn check_basic(&self) -> Result<()> {
356        let fail = Error::VerificationFailed;
357        let p = &self.params;
358        if self.server_id.len() > MAX_SERVER_ID_LEN {
359            return Err(fail);
360        }
361        for m in [&self.n, &self.n_hat] {
362            if m.bits() != p.n_rsa || !bool::from(m.is_odd()) {
363                return Err(fail);
364            }
365        }
366        if !lt(&self.rho0, &self.n) || !is_unit(&self.rho0, &self.n) {
367            return Err(fail);
368        }
369        let n2 = self.n.concatenating_mul_self();
370        if !lt(&self.rho, &n2) || !is_unit(&self.rho, &self.n) {
371            return Err(fail);
372        }
373        for v in [&self.t, &self.s1, &self.s2] {
374            if !lt(v, &self.n_hat) || !is_unit(v, &self.n_hat) {
375                return Err(fail);
376            }
377        }
378        Ok(())
379    }
380
381    /// Client verification (FR-S7) with the standard Π_fac group.
382    pub fn verify(&self, params: &Params) -> Result<VerifiedSetup> {
383        self.verify_with(params, &FacParams::standard()?)
384    }
385
386    pub fn verify_with(&self, params: &Params, fac: &FacParams) -> Result<VerifiedSetup> {
387        let fail = Error::VerificationFailed;
388        if self.params != *params {
389            return Err(fail);
390        }
391        self.check_basic()?;
392        let (h, f) = derive_h_f(&self.aux()).map_err(|_| fail)?;
393        if is_identity(&self.h) || is_identity(&self.f) || self.h != h || self.f != f || h == f {
394            return Err(fail);
395        }
396        let want = rho_of(&self.n, &self.rho0)?;
397        let bits = want.bits_precision().max(self.rho.bits_precision());
398        if resized(&want, bits) != resized(&self.rho, bits) {
399            return Err(fail);
400        }
401        let tx = base_tx(params, &self.setup_id, &self.server_id);
402        self.pi_fac.verify(&tx, params, fac, &self.n)?;
403        let st = DfStatement::from_uints(&self.n_hat, &self.t, &self.s1, &self.s2)?;
404        self.pi_df.verify(&tx, params, &st)?;
405        self.pi_mod.verify(&tx, params, &self.n, params.n_rsa)?;
406        let hash = self.hash()?;
407        let pk = self.paillier_pk()?;
408        let df = DfParams::new(st.n_hat, st.t, st.s1, st.s2);
409        Ok(VerifiedSetup {
410            pk,
411            df,
412            h,
413            f,
414            hash,
415            server_id: self.server_id.clone(),
416            params: *params,
417            rho_table: None,
418        })
419    }
420}
421
422impl SetupData {
423    /// For the server's *own* data: builds the verified view without re-checking the proofs.
424    /// Never use this on data received from another party.
425    pub fn assume_verified(&self, params: &Params) -> Result<VerifiedSetup> {
426        if self.params != *params {
427            return Err(Error::VerificationFailed);
428        }
429        self.check_basic()?;
430        let st = DfStatement::from_uints(&self.n_hat, &self.t, &self.s1, &self.s2)?;
431        Ok(VerifiedSetup {
432            pk: self.paillier_pk()?,
433            df: DfParams::new(st.n_hat, st.t, st.s1, st.s2),
434            h: self.h,
435            f: self.f,
436            hash: self.hash()?,
437            server_id: self.server_id.clone(),
438            params: *params,
439            rho_table: None,
440        })
441    }
442}
443
444/// Setup that passed FR-S7.
445pub struct VerifiedSetup {
446    pk: PaillierPk,
447    df: DfParams,
448    h: ProjectivePoint,
449    f: ProjectivePoint,
450    hash: [u8; 32],
451    server_id: Vec<u8>,
452    params: Params,
453    rho_table: Option<FixedBase>,
454}
455
456impl VerifiedSetup {
457    /// Identifier of the server this setup belongs to (part of every ssid).
458    pub fn server_id(&self) -> &[u8] {
459        &self.server_id
460    }
461
462    pub fn paillier_pk(&self) -> &PaillierPk {
463        &self.pk
464    }
465    pub fn df(&self) -> &DfParams {
466        &self.df
467    }
468    pub fn h(&self) -> &ProjectivePoint {
469        &self.h
470    }
471    pub fn f(&self) -> &ProjectivePoint {
472        &self.f
473    }
474    /// `sid = SHA-256(SetupData)`; the value pinned by the application (FR-S8).
475    pub fn hash(&self) -> [u8; 32] {
476        self.hash
477    }
478    pub fn params(&self) -> &Params {
479        &self.params
480    }
481    pub fn rho_table(&self) -> Option<&FixedBase> {
482        self.rho_table.as_ref()
483    }
484
485    /// Fixed-base tables: `ρ` for exponents below `2^{n_λ0}` with `rho_window`, and
486    /// `t, s₁, s₂` for exponents below `2^{df_bits}` with `df_window`.
487    pub fn with_tables_custom(mut self, rho_window: u32, df_bits: u32, df_window: u32) -> Self {
488        let rho = self.pk.rho().clone();
489        self.rho_table = Some(FixedBase::new(
490            self.pk.mod_n2(),
491            &rho,
492            self.params.n_lambda0(),
493            rho_window,
494        ));
495        self.df = self.df.with_tables(df_bits, df_window);
496        self
497    }
498
499    /// Default tables sized for the exponents of Π_sig and signing (mask widths include ε):
500    /// `ρ`: `n_λ0 + ε` bits (window 4), `t`: `n_tρ + ε`, `s₁`: `n_a + ε`, `s₂`: `n_b + ε` (window 3).
501    pub fn with_tables(mut self) -> Self {
502        let p = self.params;
503        let eps = p.eps();
504        self.rho_table = Some(FixedBase::new(
505            self.pk.mod_n2(),
506            self.pk.rho(),
507            p.n_lambda0() + eps,
508            RHO_TABLE_WINDOW,
509        ));
510        self.df = self.df.with_tables_sized(
511            [p.n_t_rho + eps, p.n_a() + eps, p.n_b() + eps],
512            DF_TABLE_WINDOW,
513        );
514        self
515    }
516
517    /// Total memory of the tables in bytes.
518    pub fn tables_size_bytes(&self) -> usize {
519        self.rho_table.as_ref().map_or(0, FixedBase::size_bytes) + self.df.tables_size_bytes()
520    }
521}
522
523#[cfg(test)]
524mod tests {
525    extern crate std;
526    use super::*;
527    use crate::{
528        params::{PARAMS_128, ParamsId},
529        rsa::tough::{ToughSpec, sample_tough_prime},
530        zk::{fac::FacParams, modulus::ModRep},
531    };
532    use rand_chacha::ChaCha8Rng;
533    use rand_core::SeedableRng;
534    use std::{sync::OnceLock, time::Instant};
535
536    fn small_params() -> Params {
537        Params {
538            id: ParamsId::P128,
539            ell: 16,
540            nu: 16,
541            n_rsa: 512,
542            r_mod: 8,
543            r_df: 16,
544            r_df_star: 4,
545            tough_count: 4,
546            tough_bits: 64,
547            ..PARAMS_128
548        }
549    }
550
551    struct Fx {
552        fac: FacParams,
553        data: SetupData,
554        other: SetupData,
555        sk: ServerSetupSecret,
556    }
557
558    fn fx() -> &'static Fx {
559        static F: OnceLock<Fx> = OnceLock::new();
560        F.get_or_init(|| {
561            let p = small_params();
562            let mut rng = ChaCha8Rng::seed_from_u64(1);
563            let d_bits = p.n_rsa + 2 * p.ell + 2 * p.nu + 4;
564            let d = sample_tough_prime(&mut rng, d_bits, 7, ToughSpec::SAFE).unwrap();
565            let fac = FacParams::new(d.p().clone()).unwrap();
566            let (data, sk) = server_setup_with(&mut rng, &p, &fac, [7u8; 32], b"server-A").unwrap();
567            let (other, _) = server_setup_with(&mut rng, &p, &fac, [9u8; 32], b"server-B").unwrap();
568            Fx {
569                fac,
570                data,
571                other,
572                sk,
573            }
574        })
575    }
576
577    fn ok(d: &SetupData) -> bool {
578        d.verify_with(&small_params(), &fx().fac).is_ok()
579    }
580
581    #[test]
582    fn server_secret_export_roundtrip_and_binding() {
583        let f = fx();
584        let p = small_params();
585        let bytes = f.sk.export_secret(&p).unwrap();
586        let back = ServerSetupSecret::import_secret(&bytes, &f.data).unwrap();
587        let pk = f.data.paillier_pk().unwrap();
588        let c = pk.g_pow(&SBig::from_i64(777));
589        let m = back.paillier_sk().dec(&c).unwrap();
590        assert_eq!(resized(&m, 64), BoxedUint::from(777u64));
591        // a secret does not fit another setup
592        assert!(ServerSetupSecret::import_secret(&bytes, &f.other).is_err());
593        // malformed encodings
594        assert!(ServerSetupSecret::import_secret(&bytes[1..], &f.data).is_err());
595        let mut bad = bytes.to_vec();
596        bad[0] ^= 1;
597        assert!(ServerSetupSecret::import_secret(&bad, &f.data).is_err());
598        let mut bad = bytes.to_vec();
599        *bad.last_mut().unwrap() ^= 2;
600        assert!(ServerSetupSecret::import_secret(&bad, &f.data).is_err());
601    }
602
603    #[test]
604    fn honest_setup_verifies() {
605        let f = fx();
606        let v = f.data.verify_with(&small_params(), &f.fac).unwrap();
607        assert_eq!(v.hash(), f.data.hash().unwrap());
608        assert_eq!(v.paillier_pk().n(), &f.data.n);
609        assert_eq!(
610            f.data.verify_with(&small_params(), &f.fac).unwrap().hash(),
611            v.hash()
612        );
613        assert_eq!(std::format!("{:?}", f.sk), "ServerSetupSecret(***)");
614        // decryption works with the secret key
615        let pk = f.data.paillier_pk().unwrap();
616        let c = pk.g_pow(&SBig::from_i64(12345));
617        let m = f.sk.paillier_sk().dec(&c).unwrap();
618        assert_eq!(resized(&m, 64), BoxedUint::from(12345u64));
619        let v = v.with_tables();
620        assert!(v.rho_table().is_some() && v.tables_size_bytes() > 0);
621        // wrong params rejected
622        let mut p2 = small_params();
623        p2.r_mod = 9;
624        assert!(f.data.verify_with(&p2, &f.fac).is_err());
625    }
626
627    #[test]
628    fn roundtrip_and_strict_length() {
629        let f = fx();
630        let p = small_params();
631        let bytes = f.data.to_bytes().unwrap();
632        let back = SetupData::from_bytes_with(&bytes, &p, &f.fac).unwrap();
633        assert_eq!(back.to_bytes().unwrap(), bytes);
634        assert!(ok(&back));
635        assert!(SetupData::from_bytes_with(&bytes[1..], &p, &f.fac).is_err());
636        assert!(SetupData::from_bytes_with(&bytes[..bytes.len() - 1], &p, &f.fac).is_err());
637        let mut long = bytes.clone();
638        long.push(0);
639        assert!(SetupData::from_bytes_with(&long, &p, &f.fac).is_err());
640        assert!(SetupData::from_bytes_with(&[], &p, &f.fac).is_err());
641    }
642
643    #[test]
644    fn byte_flip_in_every_field_rejected() {
645        let f = fx();
646        let p = small_params();
647        let bytes = f.data.to_bytes().unwrap();
648        let len = nb(&p);
649        let sl = f.data.server_id.len();
650        let mut regions: Vec<(usize, usize)> = std::vec![(0, 1), (1, 32), (33, 2), (35, sl)];
651        let mut pos = 35 + sl;
652        for l in [len, len, len, 2 * len, len, len, len, 33, 33] {
653            regions.push((pos, l));
654            pos += l;
655        }
656        for l in [
657            ModProof::encoded_len(&p, p.n_rsa),
658            FacProof::encoded_len(&p, &f.fac, p.n_rsa),
659            DfProof::encoded_len(&p),
660        ] {
661            regions.push((pos, l));
662            pos += l;
663        }
664        assert_eq!(pos, bytes.len());
665        let orig = f.data.hash().unwrap();
666        for (start, l) in regions {
667            for off in [0, l / 2, l - 1] {
668                for bit in [0x01u8, 0x80] {
669                    let mut b = bytes.clone();
670                    b[start + off] ^= bit;
671                    match SetupData::from_bytes_with(&b, &p, &f.fac) {
672                        Err(_) => {}
673                        Ok(d) => {
674                            assert!(!ok(&d), "flip at {} accepted", start + off);
675                            assert_ne!(d.hash().unwrap(), orig);
676                        }
677                    }
678                }
679            }
680        }
681    }
682
683    #[test]
684    fn field_tampering_rejected() {
685        let f = fx();
686        let one = |b: &BoxedUint| {
687            let bits = b.bits_precision();
688            b.wrapping_add(BoxedUint::one_with_precision(bits))
689        };
690        let orig = f.data.hash().unwrap();
691        let mut cases: Vec<SetupData> = Vec::new();
692        let mut d = f.data.clone();
693        core::mem::swap(&mut d.h, &mut d.f);
694        cases.push(d);
695        let mut d = f.data.clone();
696        d.h += d.f;
697        cases.push(d);
698        let mut d = f.data.clone();
699        d.rho = one(&d.rho);
700        cases.push(d);
701        let mut d = f.data.clone();
702        d.rho0 = one(&d.rho0);
703        cases.push(d);
704        let mut d = f.data.clone();
705        d.t = one(&d.t);
706        cases.push(d);
707        let mut d = f.data.clone();
708        d.s1 = d.s2.clone();
709        cases.push(d);
710        let mut d = f.data.clone();
711        d.s2 = BoxedUint::zero_with_precision(512);
712        cases.push(d);
713        // N̂ of the wrong size / N swapped for N̂ / other server's moduli
714        let mut d = f.data.clone();
715        d.n_hat = d.n_hat.shr_vartime(1).unwrap();
716        cases.push(d);
717        let mut d = f.data.clone();
718        d.n = d.n_hat.clone();
719        cases.push(d);
720        let mut d = f.data.clone();
721        d.n = f.other.n.clone();
722        cases.push(d);
723        // proofs / ids from another setup
724        let mut d = f.data.clone();
725        d.pi_mod = f.other.pi_mod.clone();
726        cases.push(d);
727        let mut d = f.data.clone();
728        d.pi_fac = f.other.pi_fac.clone();
729        cases.push(d);
730        let mut d = f.data.clone();
731        d.pi_df = f.other.pi_df.clone();
732        cases.push(d);
733        let mut d = f.data.clone();
734        d.server_id = f.other.server_id.clone();
735        cases.push(d);
736        let mut d = f.data.clone();
737        d.setup_id = f.other.setup_id;
738        cases.push(d);
739        let mut d = f.data.clone();
740        d.server_id = std::vec![0u8; 257];
741        cases.push(d);
742        // proof field edits
743        let mut d = f.data.clone();
744        d.pi_mod.reps[0] = ModRep {
745            a: !d.pi_mod.reps[0].a,
746            ..d.pi_mod.reps[0].clone()
747        };
748        cases.push(d);
749        let mut d = f.data.clone();
750        d.pi_df.e[0] ^= 1;
751        cases.push(d);
752        let mut d = f.data.clone();
753        d.pi_fac.w = one(&d.pi_fac.w);
754        cases.push(d);
755        for (i, d) in cases.iter().enumerate() {
756            assert!(!ok(d), "case {i} accepted");
757            if let Ok(h) = d.hash() {
758                assert_ne!(h, orig, "case {i}");
759            }
760        }
761        assert!(ok(&f.data));
762    }
763
764    #[test]
765    fn server_id_limit() {
766        let p = small_params();
767        let mut rng = ChaCha8Rng::seed_from_u64(5);
768        let r = server_setup_with(&mut rng, &p, &fx().fac, [0; 32], &[0u8; 257]);
769        assert!(r.is_err());
770    }
771
772    #[test]
773    #[ignore]
774    fn full_size_setup() {
775        let mut rng = ChaCha8Rng::seed_from_u64(99);
776        let t0 = Instant::now();
777        let (data, _sk) = server_setup(&mut rng, &PARAMS_128, [1u8; 32], b"example.org").unwrap();
778        std::println!("server setup: {:?}", t0.elapsed());
779        let bytes = data.to_bytes().unwrap();
780        std::println!("setup size: {} bytes", bytes.len());
781        let data = SetupData::from_bytes(&bytes, &PARAMS_128).unwrap();
782        let fac = FacParams::standard().unwrap();
783        let t0 = Instant::now();
784        data.check_basic().unwrap();
785        std::println!("check_basic: {:?}", t0.elapsed());
786        let t0 = Instant::now();
787        let want = rho_of(&data.n, &data.rho0).unwrap();
788        std::println!("rho check: {:?} {}", t0.elapsed(), want.bits());
789        let tx = base_tx(&PARAMS_128, &data.setup_id, &data.server_id);
790        let t0 = Instant::now();
791        data.pi_fac.verify(&tx, &PARAMS_128, &fac, &data.n).unwrap();
792        std::println!("fac verify: {:?}", t0.elapsed());
793        let st = DfStatement::from_uints(&data.n_hat, &data.t, &data.s1, &data.s2).unwrap();
794        let t0 = Instant::now();
795        data.pi_df.verify(&tx, &PARAMS_128, &st).unwrap();
796        std::println!("df verify: {:?}", t0.elapsed());
797        let t0 = Instant::now();
798        data.pi_mod
799            .verify(&tx, &PARAMS_128, &data.n, PARAMS_128.n_rsa)
800            .unwrap();
801        std::println!("mod verify: {:?}", t0.elapsed());
802        let t0 = Instant::now();
803        let v = data.verify(&PARAMS_128).unwrap();
804        std::println!("client verify total: {:?}", t0.elapsed());
805        let t0 = Instant::now();
806        let v = v.with_tables();
807        std::println!(
808            "tables: {:?}, {} bytes (rho {}, df {})",
809            t0.elapsed(),
810            v.tables_size_bytes(),
811            v.rho_table().map_or(0, FixedBase::size_bytes),
812            v.df().tables_size_bytes()
813        );
814        assert_eq!(v.hash(), data.hash().unwrap());
815    }
816}