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