Skip to main content

multi_party_schnorr/keygen/
refresh.rs

1// Copyright (c) Silence Laboratories Pte. Ltd. All Rights Reserved.
2// This software is licensed under the Silence Laboratories License Agreement.
3
4use elliptic_curve::{group::GroupEncoding, Group};
5use ff::Field;
6
7use crate::common::traits::GroupElem;
8
9#[cfg(feature = "serde")]
10use crate::common::{ser::Serializable, utils::serde_point};
11
12#[cfg_attr(
13    feature = "serde",
14    derive(serde::Serialize, serde::Deserialize),
15    serde(bound(
16        serialize = "G: Group + GroupEncoding, G::Scalar: Serializable",
17        deserialize = "G: Group + GroupEncoding, G::Scalar: Serializable"
18    ))
19)]
20pub struct KeyRefreshData<G>
21where
22    G: Group + GroupEncoding,
23{
24    /// Party id of the key share
25    pub party_id: u8,
26    pub threshold: u8,
27    pub total_parties: u8,
28    /// Additive share of participant_i (after interpolation)
29    /// \sum_{i=0}^{n-1} s_i_0 = private_key
30    /// s_i_0 can be equal to Zero in case when participant lost their key_share
31    /// and wants to recover it during key_refresh
32    pub(crate) s_i_0: G::Scalar,
33
34    /// list of participants ids who lost their key_shares
35    /// should be in range [0, n-1]
36    pub(crate) lost_keyshare_party_ids: Vec<u8>,
37
38    /// expected public key for key_refresh
39    #[cfg_attr(feature = "serde", serde(with = "serde_point"))]
40    pub expected_public_key: G,
41    pub root_chain_code: [u8; 32],
42}
43
44impl<G> KeyRefreshData<G>
45where
46    G: GroupElem,
47{
48    /// Create a new KeyRefreshData
49    pub fn recovery_data_for_lost(
50        lost_keyshare_party_ids: Vec<u8>,
51        expected_public_key: G,
52        party_id: u8,
53        threshold: u8,
54        total_parties: u8,
55    ) -> Self {
56        KeyRefreshData {
57            threshold,
58            total_parties,
59            party_id,
60            s_i_0: <G::Scalar as Field>::ZERO,
61            lost_keyshare_party_ids,
62            expected_public_key,
63            root_chain_code: [0; 32],
64        }
65    }
66
67    pub fn make_refresh_data_migrate(
68        party_id: u8,
69        threshold: u8,
70        total_parties: u8,
71        s_i_0: G::Scalar,
72        expected_public_key: G,
73        root_chain_code: [u8; 32],
74    ) -> Self {
75        KeyRefreshData {
76            threshold,
77            total_parties,
78            party_id,
79            s_i_0,
80            lost_keyshare_party_ids: vec![],
81            expected_public_key,
82            root_chain_code,
83        }
84    }
85
86    /// Get the private scalar (s_i) (not shamir secret)
87    pub fn s_i(&self) -> &G::Scalar {
88        &self.s_i_0
89    }
90
91    /// Get the list of lost party ids
92    pub fn lost_party_ids(&self) -> &[u8] {
93        &self.lost_keyshare_party_ids
94    }
95}
96
97#[cfg(test)]
98mod test {
99    #[cfg(any(feature = "eddsa", feature = "taproot"))]
100    use crate::{
101        common::utils::support::run_keygen,
102        keygen::utils::{run_import, run_recovery, run_refresh},
103    };
104
105    #[cfg(feature = "eddsa")]
106    use curve25519_dalek::EdwardsPoint;
107
108    #[cfg(feature = "taproot")]
109    use k256::ProjectivePoint;
110
111    #[cfg(feature = "eddsa")]
112    #[test]
113    fn refresh_curve25519() {
114        let _ = run_refresh::<3, 5, EdwardsPoint>();
115        let _ = run_refresh::<2, 3, EdwardsPoint>();
116        let _ = run_refresh::<5, 10, EdwardsPoint>();
117        let _ = run_refresh::<9, 20, EdwardsPoint>();
118    }
119
120    #[cfg(feature = "eddsa")]
121    #[test]
122    fn key_import_curve25519() {
123        let _ = run_import::<3, 5, EdwardsPoint>();
124        let _ = run_import::<2, 3, EdwardsPoint>();
125        let _ = run_import::<5, 10, EdwardsPoint>();
126        let _ = run_import::<9, 20, EdwardsPoint>();
127    }
128
129    #[cfg(feature = "eddsa")]
130    #[test]
131    fn recovery_curve25519() {
132        let keyshares = run_keygen::<3, 5, EdwardsPoint>();
133        run_recovery::<3, 5, EdwardsPoint>(&keyshares, vec![0]).unwrap();
134        run_recovery::<3, 5, EdwardsPoint>(&keyshares, vec![1, 2]).unwrap();
135        run_recovery::<3, 5, EdwardsPoint>(&keyshares, vec![3, 4]).unwrap();
136        run_recovery::<3, 5, EdwardsPoint>(&keyshares, vec![2, 3]).unwrap();
137        run_recovery::<3, 5, EdwardsPoint>(&keyshares, vec![4, 1]).unwrap();
138    }
139
140    #[cfg(feature = "eddsa")]
141    #[test]
142    #[should_panic(expected = "Error during key refresh or recovery protocol")]
143    fn recovery_invalid_curve25519() {
144        let keyshares = run_keygen::<3, 5, EdwardsPoint>();
145        if let Err(e) = run_recovery::<3, 5, EdwardsPoint>(&keyshares, vec![1, 2, 3]) {
146            panic!("{}", e);
147        }
148    }
149
150    #[cfg(feature = "taproot")]
151    #[test]
152    fn refresh_taproot() {
153        let _ = run_refresh::<3, 5, ProjectivePoint>();
154        let _ = run_refresh::<2, 3, ProjectivePoint>();
155        let _ = run_refresh::<5, 10, ProjectivePoint>();
156        let _ = run_refresh::<9, 20, ProjectivePoint>();
157    }
158
159    #[cfg(feature = "taproot")]
160    #[test]
161    fn recovery_taproot() {
162        let keyshares = run_keygen::<3, 5, ProjectivePoint>();
163        run_recovery::<3, 5, ProjectivePoint>(&keyshares, vec![0]).unwrap();
164        run_recovery::<3, 5, ProjectivePoint>(&keyshares, vec![1, 2]).unwrap();
165        run_recovery::<3, 5, ProjectivePoint>(&keyshares, vec![3, 4]).unwrap();
166        run_recovery::<3, 5, ProjectivePoint>(&keyshares, vec![2, 3]).unwrap();
167        run_recovery::<3, 5, ProjectivePoint>(&keyshares, vec![4, 1]).unwrap();
168    }
169
170    #[cfg(feature = "taproot")]
171    #[test]
172    #[should_panic(expected = "Error during key refresh or recovery protocol")]
173    fn recovery_invalid_taproot() {
174        let keyshares = run_keygen::<3, 5, ProjectivePoint>();
175        if let Err(e) = run_recovery::<3, 5, ProjectivePoint>(&keyshares, vec![1, 2, 3]) {
176            panic!("{}", e);
177        }
178    }
179
180    #[cfg(feature = "taproot")]
181    #[test]
182    fn key_import_taproot() {
183        let _ = run_import::<3, 5, ProjectivePoint>();
184        let _ = run_import::<2, 3, ProjectivePoint>();
185        let _ = run_import::<5, 10, ProjectivePoint>();
186        let _ = run_import::<9, 20, ProjectivePoint>();
187    }
188}