1#![deny(
2 clippy::print_stdout,
3 clippy::print_stderr,
4 clippy::exit,
5 clippy::dbg_macro
6)]
7#![deny(clippy::disallowed_methods)]
8#![deny(rustdoc::broken_intra_doc_links)]
9#![warn(clippy::too_many_lines, clippy::cognitive_complexity)]
10#![warn(missing_docs)]
11pub mod clock;
52pub mod core;
53pub mod error;
54#[cfg(feature = "ffi")]
56pub mod ffi;
57pub mod keri;
58pub mod types;
59pub mod verifier;
60pub mod verify;
61#[cfg(feature = "wasm")]
63pub mod wasm;
64pub mod witness;
65
66pub use types::{ChainLink, DeviceDID, IdentityDID, VerificationReport, VerificationStatus};
68
69pub use core::{
71 Capability, CapabilityError, Ed25519KeyError, Ed25519PublicKey, Ed25519Signature,
72 IdentityBundle, MAX_ATTESTATION_JSON_SIZE, MAX_JSON_BATCH_SIZE, ResourceId, Role,
73 RoleParseError, SignatureLengthError, ThresholdPolicy, VerifiedAttestation,
74};
75
76pub use error::{AttestationError, AuthsErrorInfo};
78
79pub use verifier::Verifier;
81
82#[cfg(feature = "native")]
84pub use verify::{
85 verify_at_time, verify_chain, verify_chain_with_capability, verify_chain_with_witnesses,
86 verify_device_authorization, verify_with_capability, verify_with_keys,
87};
88
89pub use verify::{
91 DeviceLinkVerification, compute_attestation_seal_digest, did_to_ed25519, is_device_listed,
92 verify_device_link,
93};
94
95pub use witness::{WitnessQuorum, WitnessReceipt, WitnessReceiptResult, WitnessVerifyConfig};
97
98pub use keri::{
100 IcpEvent as KeriIcpEvent, IxnEvent as KeriIxnEvent, KeriEvent, KeriKeyState, KeriTypeError,
101 KeriVerifyError, Prefix, RotEvent as KeriRotEvent, Said, Seal as KeriSeal, compute_said,
102 find_seal_in_kel, parse_kel_json, verify_kel,
103};
104
105pub use auths_crypto::CryptoProvider;
107
108pub use clock::{ClockProvider, SystemClock};
110
111#[cfg(any(test, feature = "test-utils"))]
113pub mod testing;
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use chrono::{TimeZone, Utc};
119
120 fn fixed_ts() -> chrono::DateTime<Utc> {
121 Utc.with_ymd_and_hms(2025, 1, 1, 0, 0, 0).unwrap()
122 }
123
124 #[test]
125 fn verification_report_is_valid_returns_true_for_valid_status() {
126 let report = VerificationReport::valid(vec![]);
127 assert!(report.is_valid());
128 }
129
130 #[test]
131 fn verification_report_is_valid_returns_false_for_expired_status() {
132 let report =
133 VerificationReport::with_status(VerificationStatus::Expired { at: fixed_ts() }, vec![]);
134 assert!(!report.is_valid());
135 }
136
137 #[test]
138 fn verification_report_is_valid_returns_false_for_revoked_status() {
139 let report = VerificationReport::with_status(
140 VerificationStatus::Revoked {
141 at: Some(fixed_ts()),
142 },
143 vec![],
144 );
145 assert!(!report.is_valid());
146 }
147
148 #[test]
149 fn verification_report_is_valid_returns_false_for_invalid_signature() {
150 let report = VerificationReport::with_status(
151 VerificationStatus::InvalidSignature { step: 0 },
152 vec![],
153 );
154 assert!(!report.is_valid());
155 }
156
157 #[test]
158 fn verification_report_is_valid_returns_false_for_broken_chain() {
159 let report = VerificationReport::with_status(
160 VerificationStatus::BrokenChain {
161 missing_link: "test".to_string(),
162 },
163 vec![],
164 );
165 assert!(!report.is_valid());
166 }
167
168 #[test]
169 fn verification_report_serializes_to_expected_json() {
170 let chain = vec![
171 ChainLink::valid(
172 "did:key:issuer1".to_string(),
173 "did:key:subject1".to_string(),
174 ),
175 ChainLink::invalid(
176 "did:key:issuer2".to_string(),
177 "did:key:subject2".to_string(),
178 "signature mismatch".to_string(),
179 ),
180 ];
181
182 let report = VerificationReport {
183 status: VerificationStatus::InvalidSignature { step: 1 },
184 chain,
185 warnings: vec!["Key expires soon".to_string()],
186 witness_quorum: None,
187 };
188
189 let json = serde_json::to_string(&report).expect("serialization failed");
190
191 let parsed: serde_json::Value = serde_json::from_str(&json).expect("parse failed");
193
194 assert_eq!(parsed["status"]["type"], "InvalidSignature");
196 assert_eq!(parsed["status"]["step"], 1);
197
198 assert_eq!(parsed["chain"].as_array().unwrap().len(), 2);
200 assert_eq!(parsed["chain"][0]["issuer"], "did:key:issuer1");
201 assert_eq!(parsed["chain"][0]["valid"], true);
202 assert_eq!(parsed["chain"][1]["valid"], false);
203 assert_eq!(parsed["chain"][1]["error"], "signature mismatch");
204
205 assert_eq!(parsed["warnings"][0], "Key expires soon");
207 }
208
209 #[test]
210 fn verification_status_valid_serializes_correctly() {
211 let status = VerificationStatus::Valid;
212 let json = serde_json::to_string(&status).unwrap();
213 assert_eq!(json, r#"{"type":"Valid"}"#);
214 }
215
216 #[test]
217 fn verification_status_expired_serializes_with_timestamp() {
218 let status = VerificationStatus::Expired {
219 at: chrono::DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z")
220 .unwrap()
221 .with_timezone(&Utc),
222 };
223 let json = serde_json::to_string(&status).unwrap();
224 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
225 assert_eq!(parsed["type"], "Expired");
226 assert_eq!(parsed["at"], "2024-01-01T00:00:00Z");
227 }
228
229 #[test]
230 fn verification_status_revoked_serializes_with_optional_timestamp() {
231 let status = VerificationStatus::Revoked {
233 at: Some(
234 chrono::DateTime::parse_from_rfc3339("2024-06-15T12:00:00Z")
235 .unwrap()
236 .with_timezone(&Utc),
237 ),
238 };
239 let json = serde_json::to_string(&status).unwrap();
240 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
241 assert_eq!(parsed["type"], "Revoked");
242 assert_eq!(parsed["at"], "2024-06-15T12:00:00Z");
243
244 let status = VerificationStatus::Revoked { at: None };
246 let json = serde_json::to_string(&status).unwrap();
247 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
248 assert_eq!(parsed["type"], "Revoked");
249 assert!(parsed["at"].is_null());
250 }
251
252 #[test]
253 fn chain_link_helpers_work() {
254 let valid = ChainLink::valid("issuer".to_string(), "subject".to_string());
255 assert!(valid.valid);
256 assert!(valid.error.is_none());
257
258 let invalid = ChainLink::invalid(
259 "issuer".to_string(),
260 "subject".to_string(),
261 "error".to_string(),
262 );
263 assert!(!invalid.valid);
264 assert_eq!(invalid.error, Some("error".to_string()));
265 }
266}