ndn_cert/challenge/mod.rs
1//! Pluggable challenge framework for NDNCERT.
2
3pub mod email;
4pub mod pin;
5pub mod possession;
6pub mod token;
7pub mod yubikey;
8
9use std::{future::Future, pin::Pin};
10
11use crate::{error::CertError, protocol::CertRequest};
12
13/// Opaque per-challenge state stored by the CA between request steps.
14#[derive(Debug, Clone)]
15pub struct ChallengeState {
16 pub challenge_type: String,
17 pub data: serde_json::Value,
18}
19
20/// Outcome returned by [`ChallengeHandler::verify`].
21pub enum ChallengeOutcome {
22 /// Challenge passed — proceed to issue the certificate.
23 Approved,
24 /// Challenge requires another round (e.g. email: code was sent, awaiting submission).
25 ///
26 /// The CA returns this with updated state; the client submits another CHALLENGE
27 /// request with the next parameters.
28 Pending {
29 /// Human-readable status for the client (e.g. "Code sent to user@example.com").
30 status_message: String,
31 /// How many more attempts the client may make before the request is rejected.
32 remaining_tries: u8,
33 /// Seconds remaining before the challenge expires.
34 remaining_time_secs: u32,
35 /// Updated challenge state to store for the next round.
36 next_state: ChallengeState,
37 },
38 /// Challenge failed — reject the request with this reason.
39 Denied(String),
40}
41
42/// A pluggable challenge handler for the NDNCERT CA.
43pub trait ChallengeHandler: Send + Sync {
44 /// The challenge type identifier (e.g. `"possession"`, `"token"`, `"pin"`, `"email"`).
45 fn challenge_type(&self) -> &'static str;
46
47 /// Prepare initial challenge state for a new enrollment request.
48 ///
49 /// Called on the first CHALLENGE request for a given enrollment. The returned
50 /// [`ChallengeState`] is stored and passed back to [`verify`](Self::verify).
51 fn begin<'a>(
52 &'a self,
53 req: &'a CertRequest,
54 ) -> Pin<Box<dyn Future<Output = Result<ChallengeState, CertError>> + Send + 'a>>;
55
56 /// Verify the client's challenge response.
57 ///
58 /// May return [`ChallengeOutcome::Pending`] to indicate another round is needed
59 /// (e.g. the email was sent and the client must submit the code). The CA will
60 /// store `next_state` and call `verify` again on the next CHALLENGE request.
61 fn verify<'a>(
62 &'a self,
63 state: &'a ChallengeState,
64 parameters: &'a serde_json::Map<String, serde_json::Value>,
65 ) -> Pin<Box<dyn Future<Output = Result<ChallengeOutcome, CertError>> + Send + 'a>>;
66}