wamu_core/
identity_authed_request.rs1use crate::crypto::VerifyingKey;
6use crate::errors::{Error, IdentityAuthedRequestError};
7use crate::payloads::IdentityAuthedRequestPayload;
8use crate::traits::IdentityProvider;
9use crate::{crypto, utils};
10
11const EXPIRY_TIMEOUT: u64 = 60 * 60; const FUTURE_TIMESTAMP_TOLERANCE: u64 = 5 * 60; pub fn initiate(
21 command: &'static str,
22 identity_provider: &impl IdentityProvider,
23) -> IdentityAuthedRequestPayload {
24 let timestamp = utils::unix_timestamp();
25 let signature = identity_provider.sign(&command_message_bytes(command, timestamp));
26
27 IdentityAuthedRequestPayload {
28 command,
29 verifying_key: identity_provider.verifying_key(),
30 timestamp,
31 signature,
32 }
33}
34
35pub fn verify(
40 request: &IdentityAuthedRequestPayload,
41 verified_parties: &[VerifyingKey],
42) -> Result<(), IdentityAuthedRequestError> {
43 if !verified_parties.contains(&request.verifying_key) {
44 Err(IdentityAuthedRequestError::Unauthorized(
46 Error::UnauthorizedParty,
47 ))
48 } else if request.timestamp + EXPIRY_TIMEOUT < utils::unix_timestamp() {
49 Err(IdentityAuthedRequestError::Expired)
51 } else if utils::unix_timestamp() + FUTURE_TIMESTAMP_TOLERANCE < request.timestamp {
52 Err(IdentityAuthedRequestError::InvalidTimestamp)
54 } else {
55 Ok(crypto::verify_signature(
57 &request.verifying_key,
58 &command_message_bytes(request.command, request.timestamp),
59 &request.signature,
60 )?)
61 }
62}
63
64fn command_message_bytes(command: &str, timestamp: u64) -> Vec<u8> {
66 utils::prefix_message_bytes(format!("{}{}", command, timestamp).as_bytes())
67}
68
69#[cfg(test)]
70mod test {
71 use super::*;
72 use crate::errors::CryptoError;
73 use crate::test_utils::MockECDSAIdentityProvider;
74
75 #[test]
76 fn identity_authed_request_initiation_and_verification_works() {
77 let identity_provider = MockECDSAIdentityProvider::generate();
79
80 let payload = initiate("command", &identity_provider);
82
83 for (verified_parties, timestamp_modification, signature_modification, expected_result) in [
84 (vec![identity_provider.verifying_key()], None, None, Ok(())),
86 (
88 vec![],
89 None,
90 None,
91 Err(IdentityAuthedRequestError::Unauthorized(
92 Error::UnauthorizedParty,
93 )),
94 ),
95 (
97 vec![identity_provider.verifying_key()],
98 Some(-(EXPIRY_TIMEOUT as i64 + 1)),
99 None,
100 Err(IdentityAuthedRequestError::Expired),
101 ),
102 (
104 vec![identity_provider.verifying_key()],
105 Some(FUTURE_TIMESTAMP_TOLERANCE as i64 + 1),
106 None,
107 Err(IdentityAuthedRequestError::InvalidTimestamp),
108 ),
109 (
111 vec![identity_provider.verifying_key()],
112 None,
113 Some(identity_provider.sign(b"Hello, world!")),
114 Err(IdentityAuthedRequestError::Unauthorized(Error::Crypto(
115 CryptoError::InvalidSignature,
116 ))),
117 ),
118 ] {
119 let mut modified_payload = payload.clone();
121
122 if let Some(delta) = timestamp_modification {
124 modified_payload.timestamp = (modified_payload.timestamp as i64 + delta) as u64;
125 }
126
127 if let Some(modified_signature) = signature_modification {
129 modified_payload.signature = modified_signature;
130 }
131
132 let result = verify(&modified_payload, &verified_parties);
134
135 assert_eq!(result, expected_result);
137 }
138 }
139}