wamu_core/
identity_rotation.rs1use crate::crypto::{Random32Bytes, VerifyingKey};
6use crate::errors::{Error, IdentityAuthedRequestError};
7use crate::payloads::{IdentityAuthedRequestPayload, IdentityRotationChallengeResponsePayload};
8use crate::share::{SigningShare, SubShare};
9use crate::traits::IdentityProvider;
10use crate::{identity_authed_request, identity_challenge, share_split_reconstruct, wrappers};
11
12const IDENTITY_ROTATION: &str = "identity-rotation";
13
14pub fn initiate(identity_provider: &impl IdentityProvider) -> IdentityAuthedRequestPayload {
16 identity_authed_request::initiate(IDENTITY_ROTATION, identity_provider)
17}
18
19pub fn verify_request_and_initiate_challenge(
23 request: &IdentityAuthedRequestPayload,
24 verified_parties: &[VerifyingKey],
25) -> Result<Random32Bytes, IdentityAuthedRequestError> {
26 wrappers::verify_identity_authed_request_and_initiate_challenge(
27 IDENTITY_ROTATION,
28 request,
29 verified_parties,
30 )
31}
32
33pub fn challenge_response(
37 challenge_fragments: &[Random32Bytes],
38 current_identity_provider: &impl IdentityProvider,
39 new_identity_provider: &impl IdentityProvider,
40) -> IdentityRotationChallengeResponsePayload {
41 IdentityRotationChallengeResponsePayload {
42 new_verifying_key: new_identity_provider.verifying_key(),
43 current_signature: identity_challenge::respond(
44 challenge_fragments,
45 current_identity_provider,
46 ),
47 new_signature: identity_challenge::respond(challenge_fragments, new_identity_provider),
48 }
49}
50
51pub fn verify_challenge_response(
55 response: &IdentityRotationChallengeResponsePayload,
56 challenge_fragments: &[Random32Bytes],
57 verifying_key: &VerifyingKey,
58) -> Result<(), Error> {
59 identity_challenge::verify(
61 &response.current_signature,
62 challenge_fragments,
63 verifying_key,
64 )?;
65 Ok(identity_challenge::verify(
67 &response.new_signature,
68 challenge_fragments,
69 &response.new_verifying_key,
70 )?)
71}
72
73pub fn rotate_signing_and_sub_share(
77 signing_share: &SigningShare,
78 sub_share_b: &SubShare,
79 current_identity_provider: &impl IdentityProvider,
80 new_identity_provider: &impl IdentityProvider,
81) -> Result<(SigningShare, SubShare), Error> {
82 let secret_share = share_split_reconstruct::reconstruct(
83 signing_share,
84 sub_share_b,
85 current_identity_provider,
86 )?;
87 share_split_reconstruct::split(&secret_share, new_identity_provider)
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::crypto::Random32Bytes;
94 use crate::errors::CryptoError;
95 use crate::share::SecretShare;
96 use crate::test_utils::MockECDSAIdentityProvider;
97 use crypto_bigint::U256;
98
99 #[test]
100 fn identity_rotation_works() {
101 let current_identity_provider = MockECDSAIdentityProvider::generate();
103
104 let secret_share = SecretShare::from(Random32Bytes::generate());
106
107 let (current_signing_share, current_sub_share_b) =
109 share_split_reconstruct::split(&secret_share, ¤t_identity_provider).unwrap();
110
111 let new_identity_provider = MockECDSAIdentityProvider::generate();
113
114 let init_payload = initiate(¤t_identity_provider);
116
117 let init_results: Vec<Result<Random32Bytes, IdentityAuthedRequestError>> = (0..5)
119 .map(|_| {
120 verify_request_and_initiate_challenge(
121 &init_payload,
122 &[current_identity_provider.verifying_key()],
123 )
124 })
125 .collect();
126
127 assert!(!init_results.iter().any(|result| result.is_err()));
129
130 let challenge_fragments: Vec<Random32Bytes> = init_results
132 .into_iter()
133 .map(|result| result.unwrap())
134 .collect();
135
136 for (
137 actual_current_signer,
138 fragments_to_sign,
139 fragments_to_verify,
140 expected_challenge_result,
141 ) in [
142 (
144 ¤t_identity_provider,
145 &challenge_fragments,
146 &challenge_fragments,
147 Ok(()),
148 ),
149 (
151 &MockECDSAIdentityProvider::generate(),
152 &challenge_fragments,
153 &challenge_fragments,
154 Err(Error::Crypto(CryptoError::InvalidSignature)),
155 ),
156 (
158 ¤t_identity_provider,
159 &(0..3u8)
160 .map(|n| Random32Bytes::from(U256::from(n)))
161 .collect(),
162 &challenge_fragments,
163 Err(Error::Crypto(CryptoError::InvalidSignature)),
164 ),
165 ] {
166 let challenge_payload = challenge_response(
168 fragments_to_sign,
169 actual_current_signer,
170 &new_identity_provider,
171 );
172
173 let challenge_result = verify_challenge_response(
175 &challenge_payload,
176 fragments_to_verify,
177 ¤t_identity_provider.verifying_key(),
178 );
179
180 assert_eq!(challenge_result, expected_challenge_result);
182 }
183
184 let (new_signing_share, new_sub_share_b) = rotate_signing_and_sub_share(
186 ¤t_signing_share,
187 ¤t_sub_share_b,
188 ¤t_identity_provider,
189 &new_identity_provider,
190 )
191 .unwrap();
192
193 let reconstructed_secret_share = share_split_reconstruct::reconstruct(
195 &new_signing_share,
196 &new_sub_share_b,
197 &new_identity_provider,
198 )
199 .unwrap();
200
201 assert_eq!(
203 &reconstructed_secret_share.to_be_bytes(),
204 &secret_share.to_be_bytes()
205 );
206 }
207}