use crate::crypto::{Random32Bytes, VerifyingKey};
use crate::errors::{Error, IdentityAuthedRequestError};
use crate::payloads::{IdentityAuthedRequestPayload, IdentityRotationChallengeResponsePayload};
use crate::share::{SigningShare, SubShare};
use crate::traits::IdentityProvider;
use crate::{identity_authed_request, identity_challenge, share_split_reconstruct, wrappers};
const IDENTITY_ROTATION: &str = "identity-rotation";
pub fn initiate(identity_provider: &impl IdentityProvider) -> IdentityAuthedRequestPayload {
identity_authed_request::initiate(IDENTITY_ROTATION, identity_provider)
}
pub fn verify_request_and_initiate_challenge(
request: &IdentityAuthedRequestPayload,
verified_parties: &[VerifyingKey],
) -> Result<Random32Bytes, IdentityAuthedRequestError> {
wrappers::verify_identity_authed_request_and_initiate_challenge(
IDENTITY_ROTATION,
request,
verified_parties,
)
}
pub fn challenge_response(
challenge_fragments: &[Random32Bytes],
current_identity_provider: &impl IdentityProvider,
new_identity_provider: &impl IdentityProvider,
) -> IdentityRotationChallengeResponsePayload {
IdentityRotationChallengeResponsePayload {
new_verifying_key: new_identity_provider.verifying_key(),
current_signature: identity_challenge::respond(
challenge_fragments,
current_identity_provider,
),
new_signature: identity_challenge::respond(challenge_fragments, new_identity_provider),
}
}
pub fn verify_challenge_response(
response: &IdentityRotationChallengeResponsePayload,
challenge_fragments: &[Random32Bytes],
verifying_key: &VerifyingKey,
) -> Result<(), Error> {
identity_challenge::verify(
&response.current_signature,
challenge_fragments,
verifying_key,
)?;
Ok(identity_challenge::verify(
&response.new_signature,
challenge_fragments,
&response.new_verifying_key,
)?)
}
pub fn rotate_signing_and_sub_share(
signing_share: &SigningShare,
sub_share_b: &SubShare,
current_identity_provider: &impl IdentityProvider,
new_identity_provider: &impl IdentityProvider,
) -> Result<(SigningShare, SubShare), Error> {
let secret_share = share_split_reconstruct::reconstruct(
signing_share,
sub_share_b,
current_identity_provider,
)?;
share_split_reconstruct::split(&secret_share, new_identity_provider)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::crypto::Random32Bytes;
use crate::errors::CryptoError;
use crate::share::SecretShare;
use crate::test_utils::MockECDSAIdentityProvider;
use crypto_bigint::U256;
#[test]
fn identity_rotation_works() {
let current_identity_provider = MockECDSAIdentityProvider::generate();
let secret_share = SecretShare::from(Random32Bytes::generate());
let (current_signing_share, current_sub_share_b) =
share_split_reconstruct::split(&secret_share, ¤t_identity_provider).unwrap();
let new_identity_provider = MockECDSAIdentityProvider::generate();
let init_payload = initiate(¤t_identity_provider);
let init_results: Vec<Result<Random32Bytes, IdentityAuthedRequestError>> = (0..5)
.map(|_| {
verify_request_and_initiate_challenge(
&init_payload,
&[current_identity_provider.verifying_key()],
)
})
.collect();
assert!(!init_results.iter().any(|result| result.is_err()));
let challenge_fragments: Vec<Random32Bytes> = init_results
.into_iter()
.map(|result| result.unwrap())
.collect();
for (
actual_current_signer,
fragments_to_sign,
fragments_to_verify,
expected_challenge_result,
) in [
(
¤t_identity_provider,
&challenge_fragments,
&challenge_fragments,
Ok(()),
),
(
&MockECDSAIdentityProvider::generate(),
&challenge_fragments,
&challenge_fragments,
Err(Error::Crypto(CryptoError::InvalidSignature)),
),
(
¤t_identity_provider,
&(0..3u8)
.map(|n| Random32Bytes::from(U256::from(n)))
.collect(),
&challenge_fragments,
Err(Error::Crypto(CryptoError::InvalidSignature)),
),
] {
let challenge_payload = challenge_response(
fragments_to_sign,
actual_current_signer,
&new_identity_provider,
);
let challenge_result = verify_challenge_response(
&challenge_payload,
fragments_to_verify,
¤t_identity_provider.verifying_key(),
);
assert_eq!(challenge_result, expected_challenge_result);
}
let (new_signing_share, new_sub_share_b) = rotate_signing_and_sub_share(
¤t_signing_share,
¤t_sub_share_b,
¤t_identity_provider,
&new_identity_provider,
)
.unwrap();
let reconstructed_secret_share = share_split_reconstruct::reconstruct(
&new_signing_share,
&new_sub_share_b,
&new_identity_provider,
)
.unwrap();
assert_eq!(
&reconstructed_secret_share.to_be_bytes(),
&secret_share.to_be_bytes()
);
}
}