Skip to main content

tecdsa_core/sign/
client.rs

1//! Client side of Protocol 3.
2use alloc::vec::Vec;
3use crypto_bigint::{BoxedUint, NonZero};
4use k256::{ProjectivePoint, Scalar, ecdsa::VerifyingKey};
5use rand_core::CryptoRng;
6
7use super::{
8    Signature, decode_signature, digest_scalar, ecdsa_verify, hedged_nonce, nonce_challenge,
9    tx_base,
10};
11use crate::{
12    arith::{
13        monty::Residue,
14        sbig::{SBig, SRange, resized},
15    },
16    curve::{decode_point, encode_point, encode_scalar, order, x_coord},
17    error::{Error, Result},
18    hd::HdPath,
19    keygen::{ClientKeyShare, canonical_uint},
20    messages::{SignMsg1, SignMsg2, SignMsg3},
21    params::Params,
22    setup::VerifiedSetup,
23    ssid::Ssid,
24    transcript::{uint_from_be, uint_to_be},
25    zk::sig::{SigProof, SigStatement, SigWitness},
26};
27
28const MAX_RESAMPLE: usize = 64;
29
30/// Client side of signing (a single step: `SignMsg1` in, `SignMsg2` out).
31pub struct ClientSign;
32
33fn scalar_to_sbig(s: &Scalar) -> Result<SBig> {
34    Ok(SBig::from_uint(uint_from_be(&encode_scalar(s), 256)?))
35}
36
37/// `⌊2^bits / q⌋` as the size of the interval the masking multiplier is drawn from.
38fn mask_interval(bits: u32) -> Result<BoxedUint> {
39    let q = order();
40    let prec = bits.max(q.bits_precision()) + 64;
41    let num = BoxedUint::one_with_precision(prec).shl(bits);
42    let qnz = Option::<NonZero<BoxedUint>>::from(resized(&q, prec).to_nz())
43        .ok_or(Error::InvalidModulus)?;
44    Ok(num.div_rem_vartime(&qnz).0)
45}
46
47impl ClientSign {
48    /// Verifies `SignMsg1`, computes `S` and `ψ_sig`, returns `SignMsg2` (envelope bytes).
49    /// `setup` should have its fixed-base tables built (`with_tables`) to meet the time budget.
50    pub fn respond<R: CryptoRng + ?Sized>(
51        rng: &mut R,
52        setup: &VerifiedSetup,
53        share: &ClientKeyShare,
54        client_id: &[u8],
55        digest: &[u8; 32],
56        msg1: &[u8],
57        hd: Option<&HdPath>,
58    ) -> Result<Vec<u8>> {
59        let params = setup.params();
60        super::check_digest(digest)?;
61        let m1 = SignMsg1::decode(msg1, params)?;
62        let r2 = decode_point(&m1.r2)?;
63        let y = decode_point(&m1.y)?;
64        let (x_pub, delta) = super::signing_key(share.public_key(), hd)?;
65        let x1 = *share.secret() + delta; // shifted client share
66        if r2 * x1 != y {
67            return Err(Error::VerificationFailed);
68        }
69        let ssid = Ssid::sign(
70            setup.hash(),
71            setup.server_id(),
72            client_id,
73            &share.key_id(),
74            digest,
75            &encode_point(&r2),
76            &encode_point(&x_pub),
77        )?;
78
79        let mut ctx = Vec::with_capacity(96);
80        ctx.extend_from_slice(&ssid.encode());
81        let k1 = hedged_nonce(rng, "k1", &x1, &ctx);
82        let r1 = ProjectivePoint::GENERATOR * k1;
83        let r_pt = r2 * k1;
84        let e = nonce_challenge(params, &ssid, &x_pub, &r1, &r_pt, digest);
85        let r = x_coord(&((r2 + ProjectivePoint::GENERATOR * e) * k1));
86        let m = digest_scalar(digest);
87        let k1_inv = Option::<Scalar>::from(k256::elliptic_curve::Field::invert(&k1))
88            .ok_or(Error::VerificationFailed)?;
89        let t1 = scalar_to_sbig(&(k1_inv * (m + r * x1)))?;
90        let t2 = scalar_to_sbig(&(k1_inv * r))?;
91        let q = SBig::from_uint(order());
92
93        // u = [k₁⁻¹(m + r·x₁)]_q + µ·q, v = [k₁⁻¹·r]_q + µ′·q (resample on the negligible overflow).
94        let (size_a, size_b) = (mask_interval(params.n_a())?, mask_interval(params.n_b())?);
95        let (a_range, b_range) = (SRange::pm_pow2(params.n_a()), SRange::pm_pow2(params.n_b()));
96        let (mut u, mut v) = (SBig::zero(64), SBig::zero(64));
97        for i in 0..MAX_RESAMPLE {
98            u = t1.add(&SBig::random_in_size(rng, &size_a)?.mul(&q));
99            v = t2.add(&SBig::random_in_size(rng, &size_b)?.mul(&q));
100            if u.in_range(a_range) && v.in_range(b_range) {
101                break;
102            }
103            if i + 1 == MAX_RESAMPLE {
104                return Err(Error::OutOfRange);
105            }
106        }
107        let lambda0 = SBig::random_in(rng, SRange::pm_pow2(params.n_lambda0()));
108
109        // S = enc_N(u; ρ^{λ₀}) · E^v mod N²
110        let pk = setup.paillier_pk();
111        let s_len = (2 * params.n_rsa as usize).div_ceil(8);
112        let e_int = canonical_uint(share.e_bytes(), s_len, pk.mod_n2().modulus().as_ref())?;
113        let e_ct = pk.check_ciphertext(&e_int)?;
114        let rho_pow: Residue = match setup.rho_table() {
115            Some(t) if t.max_bits() >= params.n_lambda0() => t.pow_ct(&lambda0)?,
116            _ => pk.mod_n2().pow_ct(pk.rho(), &lambda0, params.n_lambda0())?,
117        };
118        let s_ct = pk
119            .g_pow(&u)
120            .mul(&rho_pow)
121            .mul(&pk.scale(&e_ct, &v, params.n_b())?);
122
123        let stmt = SigStatement {
124            h: setup.h(),
125            f: setup.f(),
126            pk,
127            df: setup.df(),
128            e_ct: &e_ct,
129            s_ct: &s_ct,
130            rho_table: setup.rho_table(),
131        };
132        let psi = SigProof::prove(
133            rng,
134            &tx_base(params, &ssid, "C"),
135            params,
136            &stmt,
137            &SigWitness {
138                a: u,
139                b: v,
140                lambda0,
141            },
142        )?;
143        SignMsg2 {
144            r1: encode_point(&r1).to_vec(),
145            r: encode_point(&r_pt).to_vec(),
146            s_ct: uint_to_be(&s_ct.retrieve(), s_len)?,
147            psi_sig: psi.encode(params)?,
148        }
149        .encode(params)
150    }
151
152    /// Optional `SignMsg3`: checks the server's result locally against `X` and the digest.
153    pub fn verify_final(
154        params: &Params,
155        share: &ClientKeyShare,
156        digest: &[u8; 32],
157        msg3: &[u8],
158        hd: Option<&HdPath>,
159    ) -> Result<Signature> {
160        let m = SignMsg3::decode(msg3, params)?;
161        let sig = decode_signature(&m.r, &m.s, m.recid)?;
162        let (x_sign, _) = super::signing_key(share.public_key(), hd)?;
163        ecdsa_verify(&x_sign, digest, &sig)?;
164        // The recovery id must point back to the same key.
165        let vk = k256::ecdsa::VerifyingKey::recover_from_prehash(
166            digest,
167            &sig.to_ecdsa()?,
168            sig.recovery_id()?,
169        )
170        .map_err(|_| Error::VerificationFailed)?;
171        if VerifyingKey::from_affine(x_sign.to_affine()).ok() != Some(vk) {
172            return Err(Error::VerificationFailed);
173        }
174        Ok(sig)
175    }
176}