wamu_core/
identity_challenge.rs

1//! Identity challenge implementation.
2//!
3//! Ref: <https://wamu.tech/specification#identity-challenge>.
4
5use crate::crypto::{Random32Bytes, Signature, VerifyingKey};
6use crate::errors::CryptoError;
7use crate::traits::IdentityProvider;
8use crate::{crypto, utils};
9
10/// Returns a challenge fragment for initiating an identity challenge.
11///
12/// Ref: <https://wamu.tech/specification#identity-challenge-initiation>.
13pub fn initiate() -> Random32Bytes {
14    Random32Bytes::generate()
15}
16
17/// Given a list of identity challenge fragments and an identity provider, returns the response signature for an identity challenge.
18///
19/// Ref: <https://wamu.tech/specification#identity-challenge-response>.
20pub fn respond(
21    challenge_fragments: &[Random32Bytes],
22    identity_provider: &impl IdentityProvider,
23) -> Signature {
24    identity_provider.sign(&challenge_message_bytes(challenge_fragments))
25}
26
27/// Given an identity challenge response signature, a list of identity challenge fragments and
28/// a verifying key for challenged party,
29/// returns an `Ok` result for valid identity challenge response signature, or an appropriate `Err` result otherwise.
30///
31/// Ref: <https://wamu.tech/specification#identity-challenge-verification>.
32pub fn verify(
33    signature: &Signature,
34    challenge_fragments: &[Random32Bytes],
35    verifying_key: &VerifyingKey,
36) -> Result<(), CryptoError> {
37    crypto::verify_signature(
38        verifying_key,
39        &challenge_message_bytes(challenge_fragments),
40        signature,
41    )
42}
43
44/// Returns sign-able message bytes for the identity challenge fragments.
45fn challenge_message_bytes(challenge_fragments: &[Random32Bytes]) -> Vec<u8> {
46    // Sort the challenge fragments so that we always get the same challenge regardless of order of receiving challenges.
47    let mut sorted_challenge_fragments = challenge_fragments.to_owned();
48    sorted_challenge_fragments.sort();
49    utils::prefix_message_bytes(&sorted_challenge_fragments.iter().fold(
50        Vec::<u8>::new(),
51        |mut acc, n| {
52            acc.append(&mut n.to_be_bytes().to_vec());
53            acc
54        },
55    ))
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61    use crate::test_utils::MockECDSAIdentityProvider;
62    use crypto_bigint::U256;
63
64    #[test]
65    fn identity_challenge_works() {
66        // Generates identity provider.
67        let identity_provider = MockECDSAIdentityProvider::generate();
68
69        // Generates identity challenge fragments.
70        let challenge_fragments: Vec<Random32Bytes> = (0..5).map(|_| initiate()).collect();
71
72        for (actual_signer, fragments_to_sign, fragments_to_verify, expected_result) in [
73            // Valid response should be accepted.
74            (
75                &identity_provider,
76                &challenge_fragments,
77                &challenge_fragments,
78                Ok(()),
79            ),
80            // Response from the wrong signer should be rejected.
81            (
82                &MockECDSAIdentityProvider::generate(),
83                &challenge_fragments,
84                &challenge_fragments,
85                Err(CryptoError::InvalidSignature),
86            ),
87            // Response signing the wrong challenge fragments should be rejected.
88            (
89                &identity_provider,
90                &(0..3u8)
91                    .map(|n| Random32Bytes::from(U256::from(n)))
92                    .collect(),
93                &challenge_fragments,
94                Err(CryptoError::InvalidSignature),
95            ),
96        ] {
97            // Generates an identity challenge response using the "actual signer" and "signing challenge fragments" for this test case.
98            let challenge_response = respond(fragments_to_sign, actual_signer);
99
100            // Verifies identity challenge response using the challenged identity provider and "verification challenge fragments" for this test case.
101            let result = verify(
102                &challenge_response,
103                fragments_to_verify,
104                &identity_provider.verifying_key(),
105            );
106
107            // Verifies expected result.
108            assert_eq!(result, expected_result);
109        }
110    }
111}