ndn_cert/challenge/
possession.rs1use std::{future::Future, pin::Pin, sync::Arc};
11
12use base64::Engine;
13use ndn_security::{Certificate, Ed25519Verifier, Verifier, VerifyOutcome};
14
15use crate::{
16 challenge::{ChallengeHandler, ChallengeOutcome, ChallengeState},
17 error::CertError,
18 protocol::CertRequest,
19};
20
21pub struct PossessionChallenge {
29 trusted_certs: Arc<Vec<Certificate>>,
30}
31
32impl PossessionChallenge {
33 pub fn new(trusted_certs: Vec<Certificate>) -> Self {
35 Self {
36 trusted_certs: Arc::new(trusted_certs),
37 }
38 }
39}
40
41impl ChallengeHandler for PossessionChallenge {
42 fn challenge_type(&self) -> &'static str {
43 "possession"
44 }
45
46 fn begin<'a>(
47 &'a self,
48 req: &'a CertRequest,
49 ) -> Pin<Box<dyn Future<Output = Result<ChallengeState, CertError>> + Send + 'a>> {
50 let nonce = req.name.clone();
52 Box::pin(async move {
53 Ok(ChallengeState {
54 challenge_type: "possession".to_string(),
55 data: serde_json::json!({ "nonce": nonce }),
56 })
57 })
58 }
59
60 fn verify<'a>(
61 &'a self,
62 state: &'a ChallengeState,
63 parameters: &'a serde_json::Map<String, serde_json::Value>,
64 ) -> Pin<Box<dyn Future<Output = Result<ChallengeOutcome, CertError>> + Send + 'a>> {
65 let cert_name_str = parameters
66 .get("cert_name")
67 .and_then(|v| v.as_str())
68 .map(str::to_string);
69 let signature_b64 = parameters
70 .get("signature")
71 .and_then(|v| v.as_str())
72 .map(str::to_string);
73 let nonce = state
74 .data
75 .get("nonce")
76 .and_then(|v| v.as_str())
77 .unwrap_or("")
78 .to_string();
79 let trusted = self.trusted_certs.clone();
80
81 Box::pin(async move {
82 let cert_name_str = cert_name_str
83 .ok_or_else(|| CertError::InvalidRequest("missing 'cert_name'".to_string()))?;
84 let signature_b64 = signature_b64
85 .ok_or_else(|| CertError::InvalidRequest("missing 'signature'".to_string()))?;
86
87 let sig_bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD
88 .decode(&signature_b64)
89 .map_err(|_| CertError::InvalidRequest("invalid base64 signature".to_string()))?;
90
91 let cert = trusted.iter().find(|c| c.name.to_string() == cert_name_str);
93 let cert = match cert {
94 Some(c) => c,
95 None => {
96 return Ok(ChallengeOutcome::Denied(format!(
97 "certificate not trusted: {cert_name_str}"
98 )));
99 }
100 };
101
102 let outcome = Ed25519Verifier
105 .verify(nonce.as_bytes(), &sig_bytes, &cert.public_key)
106 .await
107 .map_err(CertError::Security)?;
108
109 match outcome {
110 VerifyOutcome::Valid => Ok(ChallengeOutcome::Approved),
111 VerifyOutcome::Invalid => Ok(ChallengeOutcome::Denied(
112 "signature verification failed".to_string(),
113 )),
114 }
115 })
116 }
117}