dkls23/
key_import.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::{CryptoRng, RngCore};
6use sl_mpc_mate::math::Polynomial;
7
8use crate::keygen::key_refresh::KeyshareForRefresh;
9
10/// Create ecdsa keyshares from a singleton private key.
11pub fn ecdsa_secret_shares<T: RngCore + CryptoRng>(
12    threshold: u8,
13    rank_list: Vec<u8>,
14    private_key: &NonZeroScalar,
15    root_chain_code: [u8; 32],
16    skip: Option<&[u8]>,
17    rng: &mut T,
18) -> Vec<KeyshareForRefresh> {
19    let lost = skip.unwrap_or(&[]).to_vec();
20
21    let private_key: &Scalar = private_key;
22    let public_key = ProjectivePoint::GENERATOR * private_key;
23
24    // u_i_k
25    let mut polynomial =
26        Polynomial::<ProjectivePoint>::random(rng, threshold as usize - 1);
27
28    polynomial.set_constant(*private_key); // making a copy of private key
29
30    let x_i_list = (0..rank_list.len())
31        .map(|i| NonZeroScalar::from_uint((i as u64 + 1).into()).unwrap())
32        .collect::<Vec<_>>();
33
34    let shares = rank_list
35        .iter()
36        .zip(&x_i_list)
37        .enumerate()
38        .filter(|&(i, _)| !lost.contains(&(i as u8)))
39        .map(|(i, (&n_i, x_i))| {
40            let s_i: Scalar = polynomial.derivative_at(n_i as usize, x_i);
41
42            KeyshareForRefresh::new(
43                rank_list.clone(),
44                threshold,
45                public_key,
46                root_chain_code,
47                Some(s_i),
48                Some(x_i_list.clone()),
49                lost.clone(),
50                i as u8,
51            )
52        })
53        .collect();
54
55    // FIXME: it should be Zeroize
56    polynomial.reset_contant();
57
58    shares
59}
60
61#[cfg(test)]
62mod tests {
63    use std::sync::Arc;
64
65    use k256::{
66        elliptic_curve::sec1::ToEncodedPoint, NonZeroScalar, ProjectivePoint,
67    };
68    use rand::Rng;
69    use sl_mpc_mate::coord::SimpleMessageRelay;
70    use tokio::task::JoinSet;
71
72    use crate::{
73        key_import::ecdsa_secret_shares,
74        keygen::{
75            key_refresh::{run, setup_key_refresh, KeyshareForRefresh},
76            Keyshare,
77        },
78        sign::setup_dsg,
79    };
80
81    async fn refresh(
82        keyshares: Vec<KeyshareForRefresh>,
83    ) -> Vec<Arc<Keyshare>> {
84        let coord = SimpleMessageRelay::new();
85        let mut parties = JoinSet::new();
86
87        for (setup, seed, share) in
88            setup_key_refresh(2, 3, Some(&[0, 0, 0]), keyshares)
89        {
90            parties.spawn(run(setup, seed, coord.connect(), share));
91        }
92
93        let mut new_shares = vec![];
94        while let Some(fini) = parties.join_next().await {
95            let fini = fini.unwrap();
96
97            if let Err(ref err) = fini {
98                println!("error {}", err);
99            }
100
101            assert!(fini.is_ok());
102
103            let new_share = fini.unwrap();
104            new_shares.push(Arc::new(new_share));
105        }
106
107        new_shares
108    }
109
110    #[tokio::test(flavor = "multi_thread")]
111    async fn import_key() {
112        let mut rng = rand::thread_rng();
113        let private_key = NonZeroScalar::random(&mut rng);
114        let original_pubkey = ProjectivePoint::GENERATOR * *private_key;
115
116        let keyshares = ecdsa_secret_shares(
117            2,
118            vec![0, 0, 0],
119            &private_key,
120            rng.gen(),
121            None,
122            &mut rng,
123        );
124        let original_chain_code = keyshares[0].root_chain_code;
125
126        let mut new_shares = refresh(keyshares).await;
127
128        let pubkey = new_shares[0].public_key;
129        let new_chain_code = new_shares[0].root_chain_code;
130        // Check if the chain code is not the default chain code
131        assert_ne!(new_chain_code, [0; 32]);
132
133        // Check if the public key is the same as the original public key after refreshing
134        // keyshares
135        assert_eq!(pubkey, original_pubkey.to_encoded_point(true).as_bytes());
136        assert_eq!(new_chain_code, original_chain_code);
137
138        // sign with party_0 and party_1 new key_shares
139        let coord = SimpleMessageRelay::new();
140
141        new_shares.sort_by_key(|share| share.party_id);
142        let subset = &new_shares[0..2_usize];
143
144        let mut parties: JoinSet<Result<_, _>> = JoinSet::new();
145        for (setup, seed) in setup_dsg(None, subset, "m") {
146            parties.spawn(crate::sign::run(setup, seed, coord.connect()));
147        }
148
149        while let Some(fini) = parties.join_next().await {
150            let fini = fini.unwrap();
151
152            if let Err(ref err) = fini {
153                println!("error {err:?}");
154            }
155            let _fini = fini.unwrap();
156        }
157    }
158
159    // this is the same test as above but it uses first 2 shares returned by
160    // `ecdsa_secret_shares()` and runs key recovery protocol.
161    #[tokio::test(flavor = "multi_thread")]
162    async fn import_key_2x3() {
163        let mut rng = rand::thread_rng();
164        let private_key = NonZeroScalar::random(&mut rng);
165        let original_pubkey = ProjectivePoint::GENERATOR * *private_key;
166
167        let mut keyshares = ecdsa_secret_shares(
168            2,
169            vec![0, 0, 0],
170            &private_key,
171            rng.gen(),
172            Some(&[2]), // skip party-id 2
173            &mut rng,
174        );
175        let original_chain_code = keyshares[0].root_chain_code;
176
177        keyshares.push(KeyshareForRefresh::from_lost_keyshare(
178            vec![0; 3],
179            2,
180            original_pubkey,
181            vec![2],
182            2,
183        ));
184
185        let new_shares = refresh(keyshares).await;
186
187        let pubkey = new_shares[0].public_key;
188        let new_chain_code = new_shares[0].root_chain_code;
189        // Check if the chain code is not the default chain code
190        assert_ne!(new_chain_code, [0; 32]);
191
192        // Check if the public key is the same as the original public
193        // key after refreshing keyshares
194        assert_eq!(pubkey, original_pubkey.to_encoded_point(true).as_bytes());
195        assert_eq!(new_chain_code, original_chain_code);
196
197        let coord = SimpleMessageRelay::new();
198        let subset = &new_shares[0..2_usize];
199
200        let mut parties: JoinSet<Result<_, _>> = JoinSet::new();
201        for (setup, seed) in setup_dsg(None, subset, "m") {
202            parties.spawn(crate::sign::run(setup, seed, coord.connect()));
203        }
204
205        while let Some(fini) = parties.join_next().await {
206            let fini = fini.unwrap();
207
208            if let Err(ref err) = fini {
209                println!("error {err:?}");
210            }
211            let _fini = fini.unwrap();
212        }
213    }
214}