auths_verifier/verifier.rs
1//! Dependency-injected [`Verifier`] for attestation and chain verification.
2
3use std::sync::Arc;
4
5use auths_crypto::CryptoProvider;
6
7use crate::clock::ClockProvider;
8use crate::core::{Attestation, DevicePublicKey, VerifiedAttestation};
9use crate::error::AttestationError;
10use crate::types::{CanonicalDid, VerificationReport};
11use crate::verify;
12use crate::witness::WitnessVerifyConfig;
13
14/// Dependency-injected verifier for attestation and chain verification.
15///
16/// Uses `Arc<dyn CryptoProvider>` and `Arc<dyn ClockProvider>` for
17/// lifetime-free sharing across async tasks and web server handler state.
18///
19/// Usage:
20/// ```ignore
21/// use std::sync::Arc;
22/// use auths_verifier::{Verifier, SystemClock};
23/// use auths_crypto::RingCryptoProvider;
24///
25/// let verifier = Verifier::native();
26/// let result = verifier.verify_with_keys(&att, &pk).await;
27/// ```
28#[derive(Clone)]
29pub struct Verifier {
30 provider: Arc<dyn CryptoProvider>,
31 clock: Arc<dyn ClockProvider>,
32}
33
34impl Verifier {
35 /// Create a `Verifier` with the given crypto provider and clock.
36 ///
37 /// Args:
38 /// * `provider`: Ed25519 crypto backend.
39 /// * `clock`: Clock provider for expiry checks.
40 pub fn new(provider: Arc<dyn CryptoProvider>, clock: Arc<dyn ClockProvider>) -> Self {
41 Self { provider, clock }
42 }
43
44 /// Create a `Verifier` using the native Ring crypto provider and system clock.
45 #[cfg(feature = "native")]
46 pub fn native() -> Self {
47 Self {
48 provider: Arc::new(auths_crypto::RingCryptoProvider),
49 clock: Arc::new(crate::clock::SystemClock),
50 }
51 }
52
53 /// Verify an attestation's signatures against the issuer's public key.
54 ///
55 /// Args:
56 /// * `att`: The attestation to verify.
57 /// * `issuer_pk`: Typed issuer public key (Ed25519 or P-256).
58 pub async fn verify_with_keys(
59 &self,
60 att: &Attestation,
61 issuer_pk: &DevicePublicKey,
62 ) -> Result<VerifiedAttestation, AttestationError> {
63 verify::verify_with_keys_at(
64 att,
65 issuer_pk,
66 self.clock.now(),
67 true,
68 self.provider.as_ref(),
69 )
70 .await?;
71 Ok(VerifiedAttestation::from_verified(att.clone()))
72 }
73
74 /// Verify an attestation against a specific point in time (skips clock-skew check).
75 ///
76 /// Args:
77 /// * `att`: The attestation to verify.
78 /// * `issuer_pk`: Typed issuer public key (Ed25519 or P-256).
79 /// * `at`: The reference timestamp for expiry evaluation.
80 pub async fn verify_at_time(
81 &self,
82 att: &Attestation,
83 issuer_pk: &DevicePublicKey,
84 at: chrono::DateTime<chrono::Utc>,
85 ) -> Result<VerifiedAttestation, AttestationError> {
86 verify::verify_with_keys_at(att, issuer_pk, at, false, self.provider.as_ref()).await?;
87 Ok(VerifiedAttestation::from_verified(att.clone()))
88 }
89
90 /// Verify an ordered attestation chain starting from a known root public key.
91 ///
92 /// Args:
93 /// * `attestations`: Ordered attestation chain (root first).
94 /// * `root_pk`: Typed root identity public key (Ed25519 or P-256).
95 pub async fn verify_chain(
96 &self,
97 attestations: &[Attestation],
98 root_pk: &DevicePublicKey,
99 ) -> Result<VerificationReport, AttestationError> {
100 verify::verify_chain_inner(
101 attestations,
102 root_pk,
103 self.provider.as_ref(),
104 self.clock.now(),
105 )
106 .await
107 }
108
109 /// Verify a chain and additionally validate witness receipts against a quorum threshold.
110 ///
111 /// Args:
112 /// * `attestations`: Ordered attestation chain (root first).
113 /// * `root_pk`: Typed root identity public key (Ed25519 or P-256).
114 /// * `witness_config`: Witness receipts and quorum threshold to validate.
115 pub async fn verify_chain_with_witnesses(
116 &self,
117 attestations: &[Attestation],
118 root_pk: &DevicePublicKey,
119 witness_config: &WitnessVerifyConfig<'_>,
120 ) -> Result<VerificationReport, AttestationError> {
121 let mut report = self.verify_chain(attestations, root_pk).await?;
122 if !report.is_valid() {
123 return Ok(report);
124 }
125
126 let quorum =
127 crate::witness::verify_witness_receipts(witness_config, self.provider.as_ref()).await;
128 if quorum.verified < quorum.required {
129 report.status = crate::types::VerificationStatus::InsufficientWitnesses {
130 required: quorum.required,
131 verified: quorum.verified,
132 };
133 report.warnings.push(format!(
134 "Witness quorum not met: {}/{} verified",
135 quorum.verified, quorum.required
136 ));
137 }
138 report.witness_quorum = Some(quorum);
139 Ok(report)
140 }
141
142 /// Verify that a specific device is authorized under a given identity.
143 ///
144 /// Args:
145 /// * `identity_did`: The DID of the authorizing identity.
146 /// * `device_did`: The device DID to check authorization for.
147 /// * `attestations`: Pool of attestations to search.
148 /// * `identity_pk`: Typed identity public key (Ed25519 or P-256).
149 pub async fn verify_device_authorization(
150 &self,
151 identity_did: &str,
152 device_did: &CanonicalDid,
153 attestations: &[Attestation],
154 identity_pk: &DevicePublicKey,
155 ) -> Result<VerificationReport, AttestationError> {
156 verify::verify_device_authorization_inner(
157 identity_did,
158 device_did,
159 attestations,
160 identity_pk,
161 self.provider.as_ref(),
162 self.clock.now(),
163 )
164 .await
165 }
166}