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}