1#![allow(clippy::disallowed_methods)]
3#![deny(
4 clippy::print_stdout,
5 clippy::print_stderr,
6 clippy::exit,
7 clippy::dbg_macro
8)]
9#![deny(rustdoc::broken_intra_doc_links)]
10#![warn(clippy::too_many_lines, clippy::cognitive_complexity)]
11#![warn(missing_docs)]
12pub mod action;
51pub mod clock;
52pub mod commit;
53pub mod commit_error;
54pub mod commit_kel;
55pub mod contract;
57pub mod core;
58pub mod credential;
59pub mod duplicity;
60pub mod error;
61#[cfg(feature = "ffi")]
63pub mod ffi;
64pub mod presentation;
65mod software_verify;
66pub mod ssh_sig;
67pub mod types;
68pub mod verifier;
69pub mod verify;
70#[cfg(feature = "wasm")]
72pub mod wasm;
73pub mod witness;
74
75pub use types::{
77 AssuranceLevel, AssuranceLevelParseError, CanonicalDid, ChainLink, DidConversionError,
78 DidParseError, IdentityDID, VerificationReport, VerificationStatus, signer_hex_to_did,
79 validate_did,
80};
81
82pub use action::ActionEnvelope;
84
85pub use core::{
87 ATTESTATION_VERSION, Attestation, Capability, CapabilityError, CommitOid, CommitOidError,
88 DevicePublicKey, EcdsaP256Error, EcdsaP256PublicKey, EcdsaP256Signature, Ed25519KeyError,
89 Ed25519PublicKey, Ed25519Signature, IdentityBundle, InvalidKeyError, MAX_ATTESTATION_JSON_SIZE,
90 MAX_JSON_BATCH_SIZE, OidcBinding, PolicyId, PublicKeyDecodeError, PublicKeyHex,
91 PublicKeyHexError, ResourceId, Role, RoleParseError, SignatureAlgorithm, SignatureLengthError,
92 SignatureVerifyError, ThresholdPolicy, TypedSignature, VerifiedAttestation,
93 decode_public_key_bytes, decode_public_key_hex,
94};
95
96#[cfg(any(test, feature = "test-utils"))]
98pub use testing::AttestationBuilder;
99
100#[cfg(any(test, feature = "test-utils"))]
101pub use testing::MockClock;
102
103pub use commit_error::CommitVerificationError;
105pub use error::{AttestationError, AuthsErrorInfo};
106
107pub use verifier::Verifier;
109
110#[cfg(feature = "native")]
112pub use verify::{
113 verify_at_time, verify_chain, verify_chain_with_witnesses, verify_device_authorization,
114 verify_with_keys,
115};
116
117pub use verify::{
119 DeviceLinkVerification, compute_attestation_seal_digest, is_device_listed, verify_device_link,
120};
121
122pub use witness::{SignedReceipt, WitnessQuorum, WitnessReceiptResult, WitnessVerifyConfig};
124
125pub use auths_keri::{
127 Event as KeriEvent, IcpEvent as KeriIcpEvent, IxnEvent as KeriIxnEvent, KeriTypeError, Prefix,
128 RotEvent as KeriRotEvent, Said, Seal as KeriSeal, ValidationError, compute_said,
129 find_seal_in_kel, parse_kel_json,
130};
131
132pub use commit::VerifiedCommit;
134pub use commit_kel::{
135 ANCHOR_SEQ_TRAILER, CommitVerdict, SCOPE_TRAILER, VerifierWitnessPolicy, WitnessGateStatus,
136 WitnessedVerdict, anchor_seq_trailer, scope_trailer, verify_commit_against_kel,
137 verify_commit_against_kel_scoped, verify_commit_against_kel_witnessed,
138};
139pub use ssh_sig::{SshKeyType, SshSigEnvelope};
140
141pub use credential::{
145 CredentialVerdict, LifecycleEvent, SignedAcdc, verify_credential, verify_credential_sync,
146};
147
148pub use presentation::{
151 PresentationBinding, PresentationEnvelope, PresentationVerdict, verify_presentation,
152 verify_presentation_sync,
153};
154
155pub use contract::{SCHEMA_VERSION, verify_credential_json, verify_presentation_json};
158
159pub use auths_crypto::CryptoProvider;
161pub use auths_crypto::Hash256;
162
163pub use clock::{ClockProvider, SystemClock};
165
166#[cfg(any(test, feature = "test-utils"))]
168pub mod testing;
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use chrono::{TimeZone, Utc};
174
175 fn fixed_ts() -> chrono::DateTime<Utc> {
176 Utc.with_ymd_and_hms(2025, 1, 1, 0, 0, 0).unwrap()
177 }
178
179 #[test]
180 fn verification_report_is_valid_returns_true_for_valid_status() {
181 let report = VerificationReport::valid(vec![]);
182 assert!(report.is_valid());
183 }
184
185 #[test]
186 fn verification_report_is_valid_returns_false_for_expired_status() {
187 let report =
188 VerificationReport::with_status(VerificationStatus::Expired { at: fixed_ts() }, vec![]);
189 assert!(!report.is_valid());
190 }
191
192 #[test]
193 fn verification_report_is_valid_returns_false_for_revoked_status() {
194 let report = VerificationReport::with_status(
195 VerificationStatus::Revoked {
196 at: Some(fixed_ts()),
197 },
198 vec![],
199 );
200 assert!(!report.is_valid());
201 }
202
203 #[test]
204 fn verification_report_is_valid_returns_false_for_invalid_signature() {
205 let report = VerificationReport::with_status(
206 VerificationStatus::InvalidSignature { step: 0 },
207 vec![],
208 );
209 assert!(!report.is_valid());
210 }
211
212 #[test]
213 fn verification_report_is_valid_returns_false_for_broken_chain() {
214 let report = VerificationReport::with_status(
215 VerificationStatus::BrokenChain {
216 missing_link: "test".to_string(),
217 },
218 vec![],
219 );
220 assert!(!report.is_valid());
221 }
222
223 #[test]
224 fn verification_report_serializes_to_expected_json() {
225 let chain = vec![
226 ChainLink::valid(
227 "did:key:issuer1".to_string(),
228 "did:key:subject1".to_string(),
229 ),
230 ChainLink::invalid(
231 "did:key:issuer2".to_string(),
232 "did:key:subject2".to_string(),
233 "signature mismatch".to_string(),
234 ),
235 ];
236
237 let report = VerificationReport {
238 status: VerificationStatus::InvalidSignature { step: 1 },
239 chain,
240 warnings: vec!["Key expires soon".to_string()],
241 witness_quorum: None,
242 anchored: None,
243 duplicity_warning: None,
244 };
245
246 let json = serde_json::to_string(&report).expect("serialization failed");
247
248 let parsed: serde_json::Value = serde_json::from_str(&json).expect("parse failed");
250
251 assert_eq!(parsed["status"]["type"], "InvalidSignature");
253 assert_eq!(parsed["status"]["step"], 1);
254
255 assert_eq!(parsed["chain"].as_array().unwrap().len(), 2);
257 assert_eq!(parsed["chain"][0]["issuer"], "did:key:issuer1");
258 assert_eq!(parsed["chain"][0]["valid"], true);
259 assert_eq!(parsed["chain"][1]["valid"], false);
260 assert_eq!(parsed["chain"][1]["error"], "signature mismatch");
261
262 assert_eq!(parsed["warnings"][0], "Key expires soon");
264 }
265
266 #[test]
267 fn verification_status_valid_serializes_correctly() {
268 let status = VerificationStatus::Valid;
269 let json = serde_json::to_string(&status).unwrap();
270 assert_eq!(json, r#"{"type":"Valid"}"#);
271 }
272
273 #[test]
274 fn verification_status_expired_serializes_with_timestamp() {
275 let status = VerificationStatus::Expired {
276 at: chrono::DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z")
277 .unwrap()
278 .with_timezone(&Utc),
279 };
280 let json = serde_json::to_string(&status).unwrap();
281 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
282 assert_eq!(parsed["type"], "Expired");
283 assert_eq!(parsed["at"], "2024-01-01T00:00:00Z");
284 }
285
286 #[test]
287 fn verification_status_revoked_serializes_with_optional_timestamp() {
288 let status = VerificationStatus::Revoked {
290 at: Some(
291 chrono::DateTime::parse_from_rfc3339("2024-06-15T12:00:00Z")
292 .unwrap()
293 .with_timezone(&Utc),
294 ),
295 };
296 let json = serde_json::to_string(&status).unwrap();
297 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
298 assert_eq!(parsed["type"], "Revoked");
299 assert_eq!(parsed["at"], "2024-06-15T12:00:00Z");
300
301 let status = VerificationStatus::Revoked { at: None };
303 let json = serde_json::to_string(&status).unwrap();
304 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
305 assert_eq!(parsed["type"], "Revoked");
306 assert!(parsed["at"].is_null());
307 }
308
309 #[test]
310 fn chain_link_helpers_work() {
311 let valid = ChainLink::valid("issuer".to_string(), "subject".to_string());
312 assert!(valid.valid);
313 assert!(valid.error.is_none());
314
315 let invalid = ChainLink::invalid(
316 "issuer".to_string(),
317 "subject".to_string(),
318 "error".to_string(),
319 );
320 assert!(!invalid.valid);
321 assert_eq!(invalid.error, Some("error".to_string()));
322 }
323}