Skip to main content

tecdsa_core/keygen/
client.rs

1//! Client side of Protocol 2.
2use alloc::vec::Vec;
3use k256::{ProjectivePoint, Scalar};
4use rand_core::CryptoRng;
5use subtle::ConstantTimeEq;
6use zeroize::ZeroizeOnDrop;
7
8use super::{
9    ClientKeyShare, Clock, KEYGEN_TIMEOUT_MS, canonical_uint, commitment, encode_client_tuple,
10    tx_base,
11};
12use crate::{
13    curve::{decode_point, encode_point, random_nonzero_scalar},
14    error::{Error, Result},
15    messages::{KgMsg1, KgMsg2, KgMsg3},
16    paillier::PaillierPk,
17    params::Params,
18    rsa::tough::sample_df_client,
19    setup::VerifiedSetup,
20    ssid::Ssid,
21    zk::{
22        df::{DfStatement, DfWitness},
23        df_star::DfStarProof,
24        dlenc::{DlencProof, DlencStatement},
25        dlog::DlogProof,
26    },
27};
28
29/// Entry point: step 2 of the client (reacts to `KgMsg1`).
30pub struct ClientKeygen0;
31
32impl ClientKeygen0 {
33    /// Samples `x₁` and the ephemeral DF tuple, proves them and returns `KgMsg2`.
34    /// The 60-second timer starts when this returns.
35    pub fn on_msg1<R: CryptoRng + ?Sized, C: Clock>(
36        rng: &mut R,
37        clock: &C,
38        setup: &VerifiedSetup,
39        client_id: &[u8],
40        msg1: &[u8],
41    ) -> Result<(ClientKeygen1, Vec<u8>)> {
42        let params = *setup.params();
43        let m1 = KgMsg1::decode(msg1, &params)?;
44        let nonce: [u8; 32] = m1
45            .kg_nonce
46            .as_slice()
47            .try_into()
48            .map_err(|_| Error::InvalidEncoding)?;
49        let b: [u8; 32] =
50            m1.b.as_slice()
51                .try_into()
52                .map_err(|_| Error::InvalidEncoding)?;
53        let ssid = Ssid::keygen(setup.hash(), setup.server_id(), client_id, &nonce)?;
54
55        let x1 = random_nonzero_scalar(rng);
56        let x1_pub = ProjectivePoint::GENERATOR * x1;
57        let tuple = sample_df_client(rng, &params)?;
58        let client = DfStatement::client(&tuple.n_hat, &tuple.t, &tuple.s1, &tuple.s2)?;
59        let tx_c = tx_base(&params, &ssid, "C");
60        let psi_df = DfStarProof::prove(
61            rng,
62            &tx_c,
63            &params,
64            &client,
65            &DfWitness::from_secret(&tuple.secret),
66        )?;
67        // The factorization of M̂ is never needed again: destroy it right away.
68        drop(tuple);
69        let psi_dlog = DlogProof::prove(rng, &tx_c, &params, &x1_pub, &x1)?;
70
71        let [m_hat, v, u1, u2] = encode_client_tuple(&params, &client)?;
72        let msg2 = KgMsg2 {
73            x1: encode_point(&x1_pub).to_vec(),
74            m_hat,
75            v,
76            u1,
77            u2,
78            psi_df_star: psi_df.encode(&params)?,
79            psi_dlog: psi_dlog.encode(&params)?,
80        }
81        .encode(&params)?;
82        let state = ClientKeygen1 {
83            params,
84            ssid,
85            pk: setup.paillier_pk().clone(),
86            b,
87            x1,
88            x1_pub,
89            client,
90            sent_at_ms: clock.now_ms(),
91        };
92        Ok((state, msg2))
93    }
94}
95
96/// State after `KgMsg2` was sent; consumed by [`ClientKeygen1::on_msg3`].
97#[derive(ZeroizeOnDrop)]
98pub struct ClientKeygen1 {
99    #[zeroize(skip)]
100    params: Params,
101    #[zeroize(skip)]
102    ssid: Ssid,
103    #[zeroize(skip)]
104    pk: PaillierPk,
105    #[zeroize(skip)]
106    b: [u8; 32],
107    x1: Scalar,
108    #[zeroize(skip)]
109    x1_pub: ProjectivePoint,
110    #[zeroize(skip)]
111    client: DfStatement,
112    #[zeroize(skip)]
113    sent_at_ms: u64,
114}
115
116impl ClientKeygen1 {
117    /// Step 4: timer, `E ∈ Z*_{N²}`, `B = RO(ssid, S, X₂)`, `ψ_dlenc`.
118    pub fn on_msg3<C: Clock>(self, clock: &C, msg3: &[u8]) -> Result<ClientKeyShare> {
119        let params = &self.params;
120        if clock.now_ms().saturating_sub(self.sent_at_ms) > KEYGEN_TIMEOUT_MS {
121            return Err(Error::Timeout);
122        }
123        let m = KgMsg3::decode(msg3, params)?;
124        let x2_pub = decode_point(&m.x2)?;
125        if !bool::from(commitment(params, &self.ssid, &x2_pub).ct_eq(&self.b)) {
126            return Err(Error::VerificationFailed);
127        }
128        let e_len = (2 * params.n_rsa as usize).div_ceil(8);
129        let e_int = canonical_uint(&m.e, e_len, self.pk.mod_n2().modulus().as_ref())?;
130        let e_ct = self.pk.check_ciphertext(&e_int)?;
131        let psi = DlencProof::decode(&m.psi_dlenc, params)?;
132        let tx_s = tx_base(params, &self.ssid, "S");
133        let stmt = DlencStatement {
134            x_pub: &x2_pub,
135            e_ct: &e_ct,
136            pk: &self.pk,
137            client: &self.client,
138        };
139        psi.verify(&tx_s, params, &stmt)?;
140        Ok(ClientKeyShare::new(self.x1, self.x1_pub + x2_pub, m.e))
141    }
142}
143
144#[cfg(test)]
145impl ClientKeygen1 {
146    pub(crate) fn test_clone(&self) -> Self {
147        Self {
148            params: self.params,
149            ssid: self.ssid.clone(),
150            pk: self.pk.clone(),
151            b: self.b,
152            x1: self.x1,
153            x1_pub: self.x1_pub,
154            client: self.client.clone(),
155            sent_at_ms: self.sent_at_ms,
156        }
157    }
158}