dkls23/
key_export.rs

1// Copyright (c) Silence Laboratories Pte. Ltd. All Rights Reserved.
2// This software is licensed under the Silence Laboratories License Agreement.
3
4use k256::{NonZeroScalar, ProjectivePoint, Scalar};
5use rand::prelude::*;
6use x25519_dalek::{PublicKey, ReusableSecret};
7use zeroize::Zeroizing;
8
9use sl_mpc_mate::{
10    coord::*,
11    math::birkhoff_coeffs,
12    message::{MessageTag, MsgId, MESSAGE_HEADER_SIZE},
13};
14
15/// Tag of an enrypted keyshare message
16pub const KEYSHARE_EXPORT_TAG: MessageTag = MessageTag::tag(1);
17
18/// Receiver of an encrypted keyshare.
19const EXPORTED_KEYSHARE_RECEIVER: usize = 0;
20
21use crate::{
22    keygen::Keyshare,
23    pairs::Pairs,
24    proto::{
25        check_abort, decode_scalar, tags::*, EncryptedMessage,
26        EncryptionScheme, ScalarBytes,
27    },
28    setup::{
29        KeyExportReceiverSetupMessage, KeyExporterSetupMessage,
30        ProtocolParticipant, ABORT_MESSAGE_TAG,
31    },
32    sign::get_lagrange_coeff_list,
33};
34
35#[derive(Debug, thiserror::Error)]
36#[allow(missing_docs)]
37/// Distributed key generation errors
38pub enum KeyExportError {
39    #[error(
40        "Error while deserializing message or invalid message data length"
41    )]
42    InvalidMessage,
43
44    #[error("Public key mismatch after combining keyshares")]
45    PublicKeyMismatch,
46
47    /// Missing message
48    #[error("Missing message")]
49    MissingMessage,
50
51    /// We can't a send message
52    #[error("Send message")]
53    SendMessage,
54
55    /// Some party decided to not participate in the protocol.
56    #[error("Abort protocol by party {0}")]
57    AbortProtocol(usize),
58}
59
60impl From<MessageSendError> for KeyExportError {
61    fn from(_err: MessageSendError) -> Self {
62        KeyExportError::SendMessage
63    }
64}
65
66impl From<Error> for KeyExportError {
67    fn from(err: Error) -> Self {
68        match err {
69            Error::Abort(p) => KeyExportError::AbortProtocol(p as _),
70            Error::Recv => KeyExportError::MissingMessage,
71            Error::Send => KeyExportError::SendMessage,
72            Error::InvalidMessage => KeyExportError::InvalidMessage,
73        }
74    }
75}
76
77/// Helper method to combine the secret shares into the private key
78/// You can use all the shares or the threshold number of shares to
79/// combine the private key
80///
81/// # Arguments
82///
83/// * `x_i_list` - List of (x_i, rank_i) pairs
84///
85/// * `s_i_list` - List of s_i (secret shares of the parties)
86///
87pub fn combine_shares(
88    x_i_list: &[(NonZeroScalar, usize)],
89    s_i_list: &[Scalar],
90    public_key: &ProjectivePoint,
91) -> Option<Scalar> {
92    if s_i_list.len() != x_i_list.len() {
93        return None;
94    }
95
96    let is_lagrange = x_i_list.iter().all(|&(_, rank)| rank == 0);
97
98    let s = if is_lagrange {
99        get_lagrange_coeff_list(x_i_list, |(x, _)| x)
100            .zip(s_i_list)
101            .map(|(c, s_i)| c * s_i)
102            .sum()
103    } else {
104        birkhoff_coeffs(x_i_list)
105            .into_iter()
106            .zip(s_i_list)
107            .map(|(c, s_i)| c * s_i)
108            .sum()
109    };
110
111    let calculated_public_key = ProjectivePoint::GENERATOR * s;
112
113    (public_key == &calculated_public_key).then_some(s)
114}
115
116/// Export keyshare.
117///
118/// Encrypt `s_i` and send as a P2P message to an a party combining keyshares.
119///
120pub fn export_keyshare<S, R>(mut rng: R, setup: &S) -> Option<Vec<u8>>
121where
122    S: KeyExporterSetupMessage<PublicKey, Keyshare>,
123    R: RngCore + CryptoRng,
124{
125    let mut scheme = crate::proto::Scheme::new(&mut rng);
126
127    scheme
128        .receiver_public_key(0, setup.receiver_public_key().as_bytes())
129        .ok()?;
130
131    let pub_key = scheme.public_key();
132
133    let mut msg = EncryptedMessage::<ScalarBytes>::new_with_ad(
134        &setup.msg_id(Some(EXPORTED_KEYSHARE_RECEIVER), KEYSHARE_EXPORT_TAG),
135        setup.message_ttl().as_secs() as u32,
136        0,
137        pub_key.len(),
138        1,
139        &scheme,
140    );
141
142    let (payload, trailer, ad) = msg.payload_with_ad(&scheme);
143
144    payload.copy_from_slice(&setup.keyshare().s_i);
145    trailer[0] = setup.keyshare().party_id;
146    ad.copy_from_slice(pub_key);
147
148    msg.encrypt(&mut scheme, 0)
149}
150
151/// Decrypt share encrypted by `export_keyshare()`
152pub fn decrypt_share(
153    mut msg: Zeroizing<Vec<u8>>,
154    enc_key: &ReusableSecret,
155) -> Option<(Scalar, u8)> {
156    let mut scheme = crate::proto::Scheme::from_secret(enc_key.clone());
157
158    let enc_pub_key = msg
159        .get(MESSAGE_HEADER_SIZE..)
160        .and_then(|msg| msg.get(0..32))?;
161
162    scheme.receiver_public_key(0, enc_pub_key).ok()?;
163
164    let (s_i, pid, _) = EncryptedMessage::<[u8; 32]>::decrypt_with_ad(
165        &mut msg, 32, 1, &scheme, 0,
166    )?;
167
168    let party_id = pid[0];
169
170    let s_i = decode_scalar(s_i)?;
171
172    Some((s_i, party_id))
173}
174
175/// Receive exported key shares, combine them and calculate private key.
176pub async fn receive_keyshares<S, R>(
177    setup: S,
178    relay: R,
179) -> Result<Scalar, KeyExportError>
180where
181    S: KeyExportReceiverSetupMessage<ReusableSecret>,
182    R: Relay,
183{
184    let share = setup.keyshare();
185
186    let mut relay = FilteredMsgRelay::new(relay);
187
188    relay.ask_messages(&setup, ABORT_MESSAGE_TAG, false).await?;
189
190    relay
191        .ask_messages(&setup, KEYSHARE_EXPORT_TAG, true)
192        .await?;
193
194    let pk = setup.keyshare().public_key();
195
196    let rank_list = setup.keyshare().rank_list();
197    let x_i_list = setup.keyshare().x_i_list();
198
199    let mut x_i_list_2 = Pairs::new_with_item(
200        share.party_id as usize,
201        (
202            x_i_list[share.party_id as usize],
203            rank_list[share.party_id as usize] as usize,
204        ),
205    );
206    let mut s_i_list =
207        Pairs::new_with_item(share.party_id as usize, share.s_i());
208
209    let mut round = Round::new(
210        setup.total_participants() - 1,
211        KEYSHARE_EXPORT_TAG,
212        &mut relay,
213    );
214
215    while let Some((msg, party_idx, is_abort)) = round.recv().await? {
216        if is_abort {
217            check_abort(
218                &setup,
219                &msg,
220                party_idx,
221                KeyExportError::AbortProtocol,
222            )?;
223            round.put_back(&msg, ABORT_MESSAGE_TAG, party_idx);
224            continue;
225        }
226
227        let msg = Zeroizing::new(msg);
228
229        let (s_i, party_id) =
230            decrypt_share(msg, setup.receiver_private_key())
231                .ok_or(KeyExportError::InvalidMessage)?;
232
233        let x_j = x_i_list
234            .get(party_id as usize)
235            .ok_or(KeyExportError::InvalidMessage)?;
236        let rank_j = rank_list
237            .get(party_id as usize)
238            .ok_or(KeyExportError::InvalidMessage)?;
239        x_i_list_2.push(party_id as usize, (*x_j, *rank_j as usize));
240        s_i_list.push(party_id as usize, s_i);
241    }
242
243    let private_key =
244        combine_shares(&x_i_list_2.remove_ids(), &s_i_list.remove_ids(), &pk)
245            .ok_or(KeyExportError::PublicKeyMismatch)?;
246
247    Ok(private_key)
248}
249
250/// Generate message receiver map.
251///
252/// Call the passed closure for each pair (msg_id, receiver)
253///
254pub fn message_receivers<S, F>(setup: &S, mut msg_receiver: F)
255where
256    S: ProtocolParticipant,
257    F: FnMut(MsgId, &S::MessageVerifier),
258{
259    setup.all_other_parties().for_each(|p| {
260        let vk = setup.verifier(p);
261
262        msg_receiver(setup.msg_id(None, ABORT_MESSAGE_TAG), vk);
263        msg_receiver(setup.msg_id(Some(p), KEYSHARE_EXPORT_TAG), vk);
264    })
265}
266
267#[cfg(test)]
268mod tests {
269    use k256::{NonZeroScalar, ProjectivePoint};
270    use rand::seq::SliceRandom;
271    use x25519_dalek::ReusableSecret;
272
273    use sl_mpc_mate::{coord::SimpleMessageRelay, message::InstanceId};
274
275    use crate::{
276        key_import::ecdsa_secret_shares,
277        keygen::utils::gen_keyshares,
278        setup::{
279            key_export::{
280                exporter::KeyExporter, receiver::KeyExportReceiver,
281            },
282            NoSigningKey, NoVerifyingKey,
283        },
284    };
285
286    use super::{
287        combine_shares, export_keyshare, receive_keyshares, PublicKey,
288    };
289
290    #[test]
291    fn test_combine() {
292        const T: u8 = 5;
293        const N: usize = 9;
294
295        let mut rng = rand::thread_rng();
296
297        let private_key = NonZeroScalar::random(&mut rng);
298        let public_key = ProjectivePoint::GENERATOR * *private_key;
299
300        let root_chain_code = [1u8; 32];
301
302        let shares = ecdsa_secret_shares(
303            T,
304            vec![0; N],
305            &private_key,
306            root_chain_code,
307            None,
308            &mut rng,
309        );
310
311        let s_i_list =
312            shares.iter().map(|s| s.s_i.unwrap()).collect::<Vec<_>>();
313
314        let x_i_list = shares[0]
315            .x_i_list
316            .clone()
317            .unwrap()
318            .into_iter()
319            .map(|x_i| (x_i, 0))
320            .collect::<Vec<_>>();
321
322        for t in T as usize..=N {
323            let recovered_private_key =
324                combine_shares(&x_i_list[..t], &s_i_list[..t], &public_key)
325                    .unwrap();
326
327            assert_eq!(recovered_private_key, *private_key);
328        }
329    }
330
331    #[tokio::test(flavor = "multi_thread")]
332    async fn parties() {
333        let mut rng = rand::thread_rng();
334
335        const T: u8 = 3;
336        const N: u8 = 5;
337
338        let mut shares = gen_keyshares(T, N, None).await;
339        let pk = shares[0].public_key();
340
341        let inst = rand::random();
342
343        let vk: Vec<NoVerifyingKey> =
344            (0..N as usize).map(NoVerifyingKey::new).collect();
345
346        for _ in 0..10 {
347            // let's try 10 different permutation of shares.
348
349            // We should be able to export key using any subset of
350            //        t in (T..=N) of keyshares
351            shares.shuffle(&mut rng);
352
353            for t in T..=N {
354                let enc_key = ReusableSecret::random_from_rng(&mut rng);
355                let enc_pub_key = PublicKey::from(&enc_key);
356
357                let msgs = (1..t)
358                    .map(|party_id| {
359                        let setup = KeyExporter::new(
360                            InstanceId::new(inst),
361                            NoSigningKey,
362                            party_id as _,
363                            vk[..t as usize].to_vec(),
364                            shares[party_id as usize].clone(),
365                            enc_pub_key,
366                        );
367
368                        export_keyshare(&mut rng, &setup).unwrap()
369                    })
370                    .collect::<Vec<_>>();
371
372                let relay = SimpleMessageRelay::new();
373
374                for msg in msgs {
375                    relay.send(msg);
376                }
377
378                let recv = <KeyExportReceiver>::new(
379                    InstanceId::new(inst),
380                    NoSigningKey,
381                    0,
382                    vk[..t as usize].to_vec(),
383                    shares[0].clone(),
384                    enc_key,
385                );
386
387                let sk =
388                    receive_keyshares(recv, relay.connect()).await.unwrap();
389
390                assert_eq!(ProjectivePoint::GENERATOR * sk, pk);
391            }
392        }
393    }
394}