1use crate::core::{
14 Attestation, DevicePublicKey, VerifiedAttestation, canonicalize_attestation_data,
15};
16use crate::error::AttestationError;
17use crate::types::{ChainLink, VerificationReport, VerificationStatus};
18#[cfg(feature = "native")]
19use crate::witness::WitnessVerifyConfig;
20use auths_crypto::CryptoProvider;
21use auths_keri::{Event, compute_said, find_seal_in_kel};
22use chrono::{DateTime, Duration, Utc};
23use log::debug;
24use serde::Serialize;
25
26const MAX_SKEW_SECS: i64 = 5 * 60;
28
29#[cfg(feature = "native")]
39pub async fn verify_with_keys(
40 att: &Attestation,
41 issuer_pk: &DevicePublicKey,
42) -> Result<VerifiedAttestation, AttestationError> {
43 crate::verifier::Verifier::native()
44 .verify_with_keys(att, issuer_pk)
45 .await
46}
47
48#[cfg(feature = "native")]
55pub async fn verify_at_time(
56 att: &Attestation,
57 issuer_pk: &DevicePublicKey,
58 at: DateTime<Utc>,
59) -> Result<VerifiedAttestation, AttestationError> {
60 crate::verifier::Verifier::native()
61 .verify_at_time(att, issuer_pk, at)
62 .await
63}
64
65#[cfg(feature = "native")]
72pub async fn verify_chain_with_witnesses(
73 attestations: &[Attestation],
74 root_pk: &DevicePublicKey,
75 witness_config: &WitnessVerifyConfig<'_>,
76) -> Result<VerificationReport, AttestationError> {
77 crate::verifier::Verifier::native()
78 .verify_chain_with_witnesses(attestations, root_pk, witness_config)
79 .await
80}
81
82#[cfg(feature = "native")]
88pub async fn verify_chain(
89 attestations: &[Attestation],
90 root_pk: &DevicePublicKey,
91) -> Result<VerificationReport, AttestationError> {
92 crate::verifier::Verifier::native()
93 .verify_chain(attestations, root_pk)
94 .await
95}
96
97#[cfg(feature = "native")]
105pub async fn verify_device_authorization(
106 identity_did: &str,
107 device_did: &crate::types::CanonicalDid,
108 attestations: &[Attestation],
109 identity_pk: &DevicePublicKey,
110) -> Result<VerificationReport, AttestationError> {
111 crate::verifier::Verifier::native()
112 .verify_device_authorization(identity_did, device_did, attestations, identity_pk)
113 .await
114}
115
116use crate::types::CanonicalDid;
117
118pub fn is_device_listed(
120 identity_did: &str,
121 device_did: &CanonicalDid,
122 attestations: &[VerifiedAttestation],
123 now: DateTime<Utc>,
124) -> bool {
125 let device_did_str = device_did.to_string();
126
127 attestations.iter().any(|verified| {
128 let att = verified.inner();
129 if att.issuer != identity_did {
130 return false;
131 }
132 if att.subject.to_string() != device_did_str {
133 return false;
134 }
135 if att.is_revoked() {
136 return false;
137 }
138 if let Some(exp) = att.expires_at
139 && now > exp
140 {
141 return false;
142 }
143 true
144 })
145}
146
147#[derive(Debug, Clone, Serialize)]
157pub struct DeviceLinkVerification {
158 pub valid: bool,
160 #[serde(skip_serializing_if = "Option::is_none")]
162 pub error: Option<String>,
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub key_state: Option<auths_keri::KeyState>,
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub seal_sequence: Option<u128>,
169}
170
171impl DeviceLinkVerification {
172 fn success(key_state: auths_keri::KeyState, seal_sequence: Option<u128>) -> Self {
173 Self {
174 valid: true,
175 error: None,
176 key_state: Some(key_state),
177 seal_sequence,
178 }
179 }
180
181 fn failure(reason: impl Into<String>) -> Self {
182 Self {
183 valid: false,
184 error: Some(reason.into()),
185 key_state: None,
186 seal_sequence: None,
187 }
188 }
189}
190
191pub async fn verify_device_link(
209 events: &[Event],
210 attestation: &Attestation,
211 device_did: &str,
212 now: DateTime<Utc>,
213 provider: &dyn CryptoProvider,
214) -> DeviceLinkVerification {
215 let key_state = match auths_keri::validate_kel(events) {
216 Ok(ks) => ks,
217 Err(e) => return DeviceLinkVerification::failure(format!("KEL verification failed: {e}")),
218 };
219
220 if attestation.subject.as_str() != device_did {
221 return DeviceLinkVerification::failure(format!(
222 "Device DID mismatch: attestation subject is '{}', expected '{device_did}'",
223 attestation.subject
224 ));
225 }
226
227 let current_pk = match key_state.current_keys.first() {
228 Some(encoded) => match auths_keri::KeriPublicKey::parse(encoded.as_str()) {
229 Ok(keri_pk) => {
230 let curve = keri_pk.curve();
233 let bytes = keri_pk.into_bytes().to_vec();
234 match DevicePublicKey::try_new(curve, &bytes) {
235 Ok(dpk) => dpk,
236 Err(e) => {
237 return DeviceLinkVerification::failure(format!(
238 "Invalid current key: {e}"
239 ));
240 }
241 }
242 }
243 Err(e) => {
244 return DeviceLinkVerification::failure(format!("Invalid current key: {e}"));
245 }
246 },
247 None => return DeviceLinkVerification::failure("KEL has no current keys"),
248 };
249
250 if let Err(e) = verify_with_keys_at(attestation, ¤t_pk, now, true, provider).await {
251 return DeviceLinkVerification::failure(format!("Attestation verification failed: {e}"));
252 }
253
254 let seal_sequence = compute_attestation_seal_digest(attestation)
255 .ok()
256 .and_then(|digest| find_seal_in_kel(events, digest.as_str()));
257
258 DeviceLinkVerification::success(key_state, seal_sequence)
259}
260
261pub fn compute_attestation_seal_digest(
266 attestation: &Attestation,
267) -> Result<String, AttestationError> {
268 let canonical = canonicalize_attestation_data(&attestation.canonical_data())?;
269 let value: serde_json::Value = serde_json::from_slice(&canonical)
270 .map_err(|e| AttestationError::SerializationError(e.to_string()))?;
271 Ok(compute_said(&value)
272 .map_err(|e| AttestationError::SerializationError(e.to_string()))?
273 .into_inner())
274}
275
276pub(crate) async fn verify_with_keys_at(
281 att: &Attestation,
282 issuer_pk: &DevicePublicKey,
283 at: DateTime<Utc>,
284 check_skew: bool,
285 provider: &dyn CryptoProvider,
286) -> Result<(), AttestationError> {
287 let reference_time = at;
288
289 if let Some(revoked_at) = att.revoked_at
291 && revoked_at <= reference_time
292 {
293 return Err(AttestationError::AttestationRevoked);
294 }
295
296 if let Some(exp) = att.expires_at
298 && reference_time > exp
299 {
300 return Err(AttestationError::AttestationExpired {
301 at: exp.to_rfc3339(),
302 });
303 }
304
305 if check_skew
307 && let Some(ts) = att.timestamp
308 && ts > reference_time + Duration::seconds(MAX_SKEW_SECS)
309 {
310 return Err(AttestationError::TimestampInFuture {
311 at: ts.to_rfc3339(),
312 });
313 }
314
315 let canonical_json_bytes = canonicalize_attestation_data(&att.canonical_data())?;
317 let data_to_verify = canonical_json_bytes.as_slice();
318 debug!(
319 "(Verify) Canonical data: {}",
320 String::from_utf8_lossy(&canonical_json_bytes)
321 );
322
323 if !att.identity_signature.is_empty() {
325 verify_signature_by_curve(
326 issuer_pk,
327 data_to_verify,
328 att.identity_signature.as_bytes(),
329 provider,
330 SignatureRole::Issuer,
331 )
332 .await?;
333 debug!("(Verify) Issuer signature verified successfully.");
334 } else {
335 debug!(
336 "(Verify) No identity signature present (device-only attestation), skipping issuer check."
337 );
338 }
339
340 verify_signature_by_curve(
342 &att.device_public_key,
343 data_to_verify,
344 att.device_signature.as_bytes(),
345 provider,
346 SignatureRole::Device,
347 )
348 .await?;
349 debug!("(Verify) Device signature verified successfully.");
350
351 Ok(())
352}
353
354#[derive(Clone, Copy)]
356enum SignatureRole {
357 Issuer,
358 Device,
359}
360
361async fn verify_signature_by_curve(
368 pk: &DevicePublicKey,
369 message: &[u8],
370 signature: &[u8],
371 provider: &dyn CryptoProvider,
372 role: SignatureRole,
373) -> Result<(), AttestationError> {
374 let map_err = |e: String| match role {
375 SignatureRole::Issuer => AttestationError::IssuerSignatureFailed(e),
376 SignatureRole::Device => AttestationError::DeviceSignatureFailed(e),
377 };
378
379 pk.verify(message, signature, provider)
380 .await
381 .map_err(|e| map_err(e.to_string()))
382}
383
384pub(crate) async fn verify_chain_inner(
385 attestations: &[Attestation],
386 root_pk: &DevicePublicKey,
387 provider: &dyn CryptoProvider,
388 now: DateTime<Utc>,
389) -> Result<VerificationReport, AttestationError> {
390 if attestations.is_empty() {
391 return Ok(VerificationReport::with_status(
392 VerificationStatus::BrokenChain {
393 missing_link: "empty chain".to_string(),
394 },
395 vec![],
396 ));
397 }
398
399 let mut chain_links: Vec<ChainLink> = Vec::with_capacity(attestations.len());
400
401 let first_att = &attestations[0];
402 match verify_single_attestation(first_att, root_pk, 0, provider, now).await {
403 Ok(link) => chain_links.push(link),
404 Err((status, link)) => {
405 chain_links.push(link);
406 return Ok(VerificationReport::with_status(status, chain_links));
407 }
408 }
409
410 for (idx, att) in attestations.iter().enumerate().skip(1) {
411 let prev_att = &attestations[idx - 1];
412
413 if att.issuer.as_str() != prev_att.subject.as_str() {
414 let link = ChainLink::invalid(
415 att.issuer.to_string(),
416 att.subject.to_string(),
417 format!(
418 "Chain broken: expected issuer '{}', got '{}'",
419 prev_att.subject, att.issuer
420 ),
421 );
422 chain_links.push(link);
423 return Ok(VerificationReport::with_status(
424 VerificationStatus::BrokenChain {
425 missing_link: format!(
426 "Issuer mismatch at step {}: expected '{}', got '{}'",
427 idx, prev_att.subject, att.issuer
428 ),
429 },
430 chain_links,
431 ));
432 }
433
434 let issuer_pk = &prev_att.device_public_key;
435
436 match verify_single_attestation(att, issuer_pk, idx, provider, now).await {
437 Ok(link) => chain_links.push(link),
438 Err((status, link)) => {
439 chain_links.push(link);
440 return Ok(VerificationReport::with_status(status, chain_links));
441 }
442 }
443 }
444
445 Ok(VerificationReport::valid(chain_links))
446}
447
448pub(crate) async fn verify_device_authorization_inner(
449 identity_did: &str,
450 device_did: &CanonicalDid,
451 attestations: &[Attestation],
452 identity_pk: &DevicePublicKey,
453 provider: &dyn CryptoProvider,
454 now: DateTime<Utc>,
455) -> Result<VerificationReport, AttestationError> {
456 let device_did_str = device_did.to_string();
457
458 let matching: Vec<&Attestation> = attestations
459 .iter()
460 .filter(|a| a.issuer == identity_did && a.subject.to_string() == device_did_str)
461 .collect();
462
463 if matching.is_empty() {
464 return Ok(VerificationReport::with_status(
465 VerificationStatus::BrokenChain {
466 missing_link: format!(
467 "No attestation found for device {} under {}",
468 device_did_str, identity_did
469 ),
470 },
471 vec![],
472 ));
473 }
474
475 match verify_single_attestation(matching[0], identity_pk, 0, provider, now).await {
476 Ok(link) => Ok(VerificationReport::valid(vec![link])),
477 Err((status, link)) => Ok(VerificationReport::with_status(status, vec![link])),
478 }
479}
480
481async fn verify_single_attestation(
482 att: &Attestation,
483 issuer_pk: &DevicePublicKey,
484 step: usize,
485 provider: &dyn CryptoProvider,
486 now: DateTime<Utc>,
487) -> Result<ChainLink, (VerificationStatus, ChainLink)> {
488 let issuer = att.issuer.to_string();
489 let subject = att.subject.to_string();
490
491 if att.is_revoked() {
492 return Err((
493 VerificationStatus::Revoked { at: att.revoked_at },
494 ChainLink::invalid(issuer, subject, "Attestation revoked".to_string()),
495 ));
496 }
497
498 if let Some(exp) = att.expires_at
499 && now > exp
500 {
501 return Err((
502 VerificationStatus::Expired { at: exp },
503 ChainLink::invalid(
504 issuer,
505 subject,
506 format!("Attestation expired on {}", exp.to_rfc3339()),
507 ),
508 ));
509 }
510
511 match verify_with_keys_at(att, issuer_pk, now, true, provider).await {
512 Ok(()) => Ok(ChainLink::valid(issuer, subject)),
513 Err(e) => Err((
514 VerificationStatus::InvalidSignature { step },
515 ChainLink::invalid(issuer, subject, e.to_string()),
516 )),
517 }
518}
519
520#[cfg(all(test, not(target_arch = "wasm32")))]
521#[allow(clippy::unwrap_used, clippy::expect_used, clippy::disallowed_methods)]
522mod tests {
523 use super::*;
524 use crate::clock::ClockProvider;
525 use crate::core::{Ed25519PublicKey, Ed25519Signature, ResourceId};
526 use crate::types::CanonicalDid;
527 use crate::verifier::Verifier;
528 use auths_crypto::RingCryptoProvider;
529 use auths_crypto::testing::create_test_keypair;
530 use auths_keri::Said;
531 use chrono::{DateTime, Duration, TimeZone, Utc};
532 use ring::signature::{Ed25519KeyPair, KeyPair};
533 use std::sync::Arc;
534
535 fn ed(pk: &[u8]) -> DevicePublicKey {
537 DevicePublicKey::try_new(auths_crypto::CurveType::Ed25519, pk).unwrap()
538 }
539
540 fn ed25519_did(pk: &[u8; 32]) -> String {
542 CanonicalDid::from_public_key_did_key(pk, auths_crypto::CurveType::Ed25519).to_string()
543 }
544
545 struct TestClock(DateTime<Utc>);
546 impl ClockProvider for TestClock {
547 fn now(&self) -> DateTime<Utc> {
548 self.0
549 }
550 }
551
552 fn fixed_now() -> DateTime<Utc> {
553 Utc.with_ymd_and_hms(2025, 1, 1, 0, 0, 0).unwrap()
554 }
555
556 fn test_verifier() -> Verifier {
557 Verifier::new(
558 Arc::new(RingCryptoProvider),
559 Arc::new(TestClock(fixed_now())),
560 )
561 }
562
563 fn create_signed_attestation(
565 issuer_kp: &Ed25519KeyPair,
566 device_kp: &Ed25519KeyPair,
567 issuer_did: &str,
568 subject_did: &str,
569 revoked_at: Option<DateTime<Utc>>,
570 expires_at: Option<DateTime<Utc>>,
571 ) -> Attestation {
572 let device_pk: [u8; 32] = device_kp.public_key().as_ref().try_into().unwrap();
573
574 let mut att = Attestation {
575 version: 1,
576 rid: ResourceId::new("test-rid"),
577 issuer: CanonicalDid::new_unchecked(issuer_did),
578 subject: CanonicalDid::new_unchecked(subject_did),
579 device_public_key: Ed25519PublicKey::from_bytes(device_pk).into(),
580 identity_signature: Ed25519Signature::empty(),
581 device_signature: Ed25519Signature::empty(),
582 revoked_at,
583 expires_at,
584 timestamp: Some(fixed_now()),
585 note: None,
586 payload: None,
587 delegated_by: None,
588 signer_type: None,
589 environment_claim: None,
590 commit_sha: None,
591 commit_message: None,
592 author: None,
593 oidc_binding: None,
594 };
595
596 let canonical_bytes = canonicalize_attestation_data(&att.canonical_data()).unwrap();
597
598 att.identity_signature =
599 Ed25519Signature::try_from_slice(issuer_kp.sign(&canonical_bytes).as_ref()).unwrap();
600 att.device_signature =
601 Ed25519Signature::try_from_slice(device_kp.sign(&canonical_bytes).as_ref()).unwrap();
602
603 att
604 }
605
606 #[tokio::test]
607 async fn verify_chain_empty_returns_broken_chain() {
608 let result = test_verifier()
609 .verify_chain(&[], &ed(&[0u8; 32]))
610 .await
611 .unwrap();
612 assert!(!result.is_valid());
613 match result.status {
614 VerificationStatus::BrokenChain { missing_link } => {
615 assert_eq!(missing_link, "empty chain");
616 }
617 _ => panic!("Expected BrokenChain status"),
618 }
619 assert!(result.chain.is_empty());
620 }
621
622 #[tokio::test]
623 async fn verify_chain_single_valid_attestation() {
624 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
625 let root_did = ed25519_did(&root_pk);
626 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
627 let device_did = ed25519_did(&device_pk);
628
629 let att = create_signed_attestation(
630 &root_kp,
631 &device_kp,
632 &root_did,
633 &device_did,
634 None,
635 Some(fixed_now() + Duration::days(365)),
636 );
637
638 let result = test_verifier()
639 .verify_chain(&[att], &ed(&root_pk))
640 .await
641 .unwrap();
642 assert!(result.is_valid());
643 assert_eq!(result.chain.len(), 1);
644 assert!(result.chain[0].valid);
645 }
646
647 #[tokio::test]
648 async fn verify_chain_revoked_attestation_returns_revoked() {
649 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
650 let root_did = ed25519_did(&root_pk);
651 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
652 let device_did = ed25519_did(&device_pk);
653
654 let att = create_signed_attestation(
655 &root_kp,
656 &device_kp,
657 &root_did,
658 &device_did,
659 Some(fixed_now()),
660 Some(fixed_now() + Duration::days(365)),
661 );
662
663 let result = test_verifier()
664 .verify_chain(&[att], &ed(&root_pk))
665 .await
666 .unwrap();
667 assert!(!result.is_valid());
668 match result.status {
669 VerificationStatus::Revoked { .. } => {}
670 _ => panic!("Expected Revoked status, got {:?}", result.status),
671 }
672 }
673
674 #[tokio::test]
675 async fn verify_chain_expired_attestation_returns_expired() {
676 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
677 let root_did = ed25519_did(&root_pk);
678 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
679 let device_did = ed25519_did(&device_pk);
680
681 let att = create_signed_attestation(
682 &root_kp,
683 &device_kp,
684 &root_did,
685 &device_did,
686 None,
687 Some(fixed_now() - Duration::days(1)),
688 );
689
690 let result = test_verifier()
691 .verify_chain(&[att], &ed(&root_pk))
692 .await
693 .unwrap();
694 assert!(!result.is_valid());
695 match result.status {
696 VerificationStatus::Expired { .. } => {}
697 _ => panic!("Expected Expired status, got {:?}", result.status),
698 }
699 }
700
701 #[tokio::test]
702 async fn verify_chain_invalid_signature_returns_invalid_signature() {
703 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
704 let root_did = ed25519_did(&root_pk);
705 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
706 let device_did = ed25519_did(&device_pk);
707
708 let mut att = create_signed_attestation(
709 &root_kp,
710 &device_kp,
711 &root_did,
712 &device_did,
713 None,
714 Some(fixed_now() + Duration::days(365)),
715 );
716 let mut tampered = *att.identity_signature.as_bytes();
717 tampered[0] ^= 0xFF;
718 att.identity_signature = Ed25519Signature::from_bytes(tampered);
719
720 let result = test_verifier()
721 .verify_chain(&[att], &ed(&root_pk))
722 .await
723 .unwrap();
724 assert!(!result.is_valid());
725 match result.status {
726 VerificationStatus::InvalidSignature { step } => {
727 assert_eq!(step, 0);
728 }
729 _ => panic!("Expected InvalidSignature status, got {:?}", result.status),
730 }
731 }
732
733 #[tokio::test]
734 async fn verify_chain_broken_link_returns_broken_chain() {
735 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
736 let root_did = ed25519_did(&root_pk);
737 let (device1_kp, device1_pk) = create_test_keypair(&[2u8; 32]);
738 let device1_did = ed25519_did(&device1_pk);
739 let (device2_kp, device2_pk) = create_test_keypair(&[3u8; 32]);
740 let device2_did = ed25519_did(&device2_pk);
741
742 let att1 = create_signed_attestation(
743 &root_kp,
744 &device1_kp,
745 &root_did,
746 &device1_did,
747 None,
748 Some(fixed_now() + Duration::days(365)),
749 );
750 let att2 = create_signed_attestation(
751 &device1_kp,
752 &device2_kp,
753 &root_did, &device2_did,
755 None,
756 Some(fixed_now() + Duration::days(365)),
757 );
758
759 let result = test_verifier()
760 .verify_chain(&[att1, att2], &ed(&root_pk))
761 .await
762 .unwrap();
763 assert!(!result.is_valid());
764 match result.status {
765 VerificationStatus::BrokenChain { missing_link } => {
766 assert!(missing_link.contains("Issuer mismatch"));
767 }
768 _ => panic!("Expected BrokenChain status, got {:?}", result.status),
769 }
770 }
771
772 #[tokio::test]
773 async fn verify_chain_valid_three_level_chain() {
774 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
775 let root_did = ed25519_did(&root_pk);
776 let (identity_kp, identity_pk) = create_test_keypair(&[2u8; 32]);
777 let identity_did = ed25519_did(&identity_pk);
778 let (device_kp, device_pk) = create_test_keypair(&[3u8; 32]);
779 let device_did = ed25519_did(&device_pk);
780
781 let att1 = create_signed_attestation(
782 &root_kp,
783 &identity_kp,
784 &root_did,
785 &identity_did,
786 None,
787 Some(fixed_now() + Duration::days(365)),
788 );
789 let att2 = create_signed_attestation(
790 &identity_kp,
791 &device_kp,
792 &identity_did,
793 &device_did,
794 None,
795 Some(fixed_now() + Duration::days(365)),
796 );
797
798 let result = test_verifier()
799 .verify_chain(&[att1, att2], &ed(&root_pk))
800 .await
801 .unwrap();
802 assert!(result.is_valid());
803 assert_eq!(result.chain.len(), 2);
804 assert!(result.chain[0].valid);
805 assert!(result.chain[1].valid);
806 }
807
808 #[tokio::test]
809 async fn verify_chain_revoked_intermediate_returns_revoked() {
810 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
811 let root_did = ed25519_did(&root_pk);
812 let (identity_kp, identity_pk) = create_test_keypair(&[2u8; 32]);
813 let identity_did = ed25519_did(&identity_pk);
814 let (device_kp, device_pk) = create_test_keypair(&[3u8; 32]);
815 let device_did = ed25519_did(&device_pk);
816
817 let att1 = create_signed_attestation(
818 &root_kp,
819 &identity_kp,
820 &root_did,
821 &identity_did,
822 None,
823 Some(fixed_now() + Duration::days(365)),
824 );
825 let att2 = create_signed_attestation(
826 &identity_kp,
827 &device_kp,
828 &identity_did,
829 &device_did,
830 Some(fixed_now()),
831 Some(fixed_now() + Duration::days(365)),
832 );
833
834 let result = test_verifier()
835 .verify_chain(&[att1, att2], &ed(&root_pk))
836 .await
837 .unwrap();
838 assert!(!result.is_valid());
839 match result.status {
840 VerificationStatus::Revoked { .. } => {}
841 _ => panic!("Expected Revoked status, got {:?}", result.status),
842 }
843 assert_eq!(result.chain.len(), 2);
844 assert!(result.chain[0].valid);
845 assert!(!result.chain[1].valid);
846 }
847
848 #[tokio::test]
849 async fn verify_at_time_valid_before_expiration() {
850 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
851 let root_did = ed25519_did(&root_pk);
852 let (device_kp, _) = create_test_keypair(&[2u8; 32]);
853 let device_did = ed25519_did(&root_pk);
854
855 let expires = fixed_now() + Duration::days(30);
856 let att = create_signed_attestation(
857 &root_kp,
858 &device_kp,
859 &root_did,
860 &device_did,
861 None,
862 Some(expires),
863 );
864
865 let verification_time = fixed_now() + Duration::days(10);
866 let result = test_verifier()
867 .verify_at_time(&att, &ed(&root_pk), verification_time)
868 .await;
869 assert!(result.is_ok());
870 }
871
872 #[tokio::test]
873 async fn verify_at_time_expired_after_expiration() {
874 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
875 let root_did = ed25519_did(&root_pk);
876 let (device_kp, _) = create_test_keypair(&[2u8; 32]);
877 let device_did = ed25519_did(&root_pk);
878
879 let expires = fixed_now() + Duration::days(30);
880 let att = create_signed_attestation(
881 &root_kp,
882 &device_kp,
883 &root_did,
884 &device_did,
885 None,
886 Some(expires),
887 );
888
889 let verification_time = fixed_now() + Duration::days(60);
890 let result = test_verifier()
891 .verify_at_time(&att, &ed(&root_pk), verification_time)
892 .await;
893 assert!(result.is_err());
894 assert!(result.unwrap_err().to_string().contains("expired"));
895 }
896
897 #[tokio::test]
898 async fn verify_at_time_signature_always_checked() {
899 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
900 let root_did = ed25519_did(&root_pk);
901 let (device_kp, _) = create_test_keypair(&[2u8; 32]);
902 let device_did = ed25519_did(&root_pk);
903
904 let mut att = create_signed_attestation(
905 &root_kp,
906 &device_kp,
907 &root_did,
908 &device_did,
909 None,
910 Some(fixed_now() + Duration::days(365)),
911 );
912 let mut tampered = *att.identity_signature.as_bytes();
913 tampered[0] ^= 0xFF;
914 att.identity_signature = Ed25519Signature::from_bytes(tampered);
915
916 let verification_time = fixed_now() - Duration::days(10);
917 let result = test_verifier()
918 .verify_at_time(&att, &ed(&root_pk), verification_time)
919 .await;
920 assert!(result.is_err());
921 assert!(
922 result
923 .unwrap_err()
924 .to_string()
925 .contains("signature verification failed")
926 );
927 }
928
929 #[tokio::test]
930 async fn verify_at_time_with_past_time_skips_skew_check() {
931 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
932 let root_did = ed25519_did(&root_pk);
933 let (device_kp, _) = create_test_keypair(&[2u8; 32]);
934 let device_did = ed25519_did(&root_pk);
935
936 let att = create_signed_attestation(
937 &root_kp,
938 &device_kp,
939 &root_did,
940 &device_did,
941 None,
942 Some(fixed_now() + Duration::days(365)),
943 );
944
945 let verification_time = fixed_now() - Duration::days(30);
946 let result = test_verifier()
947 .verify_at_time(&att, &ed(&root_pk), verification_time)
948 .await;
949 assert!(result.is_ok());
950 }
951
952 #[tokio::test]
953 async fn verify_with_keys_still_works() {
954 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
955 let root_did = ed25519_did(&root_pk);
956 let (device_kp, _) = create_test_keypair(&[2u8; 32]);
957 let device_did = ed25519_did(&root_pk);
958
959 let att = create_signed_attestation(
960 &root_kp,
961 &device_kp,
962 &root_did,
963 &device_did,
964 None,
965 Some(fixed_now() + Duration::days(365)),
966 );
967
968 let result = test_verifier().verify_with_keys(&att, &ed(&root_pk)).await;
969 assert!(result.is_ok());
970 }
971
972 fn verified(att: Attestation) -> VerifiedAttestation {
974 VerifiedAttestation::dangerous_from_unchecked(att)
975 }
976
977 #[test]
978 fn is_device_listed_returns_true_for_valid_attestation() {
979 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
980 let root_did = ed25519_did(&root_pk);
981 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
982 let device_did_str = ed25519_did(&device_pk);
983 let device_did = CanonicalDid::new_unchecked(&device_did_str);
984
985 let att = create_signed_attestation(
986 &root_kp,
987 &device_kp,
988 &root_did,
989 &device_did_str,
990 None,
991 Some(fixed_now() + Duration::days(365)),
992 );
993
994 assert!(is_device_listed(
995 &root_did,
996 &device_did,
997 &[verified(att)],
998 fixed_now()
999 ));
1000 }
1001
1002 #[test]
1003 fn is_device_listed_returns_false_for_no_attestations() {
1004 let (_, root_pk) = create_test_keypair(&[1u8; 32]);
1005 let root_did = ed25519_did(&root_pk);
1006 let (_, device_pk) = create_test_keypair(&[2u8; 32]);
1007 let device_did_str = ed25519_did(&device_pk);
1008 let device_did = CanonicalDid::new_unchecked(&device_did_str);
1009
1010 assert!(!is_device_listed(&root_did, &device_did, &[], fixed_now()));
1011 }
1012
1013 #[test]
1014 fn is_device_listed_returns_false_for_expired_attestation() {
1015 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1016 let root_did = ed25519_did(&root_pk);
1017 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1018 let device_did_str = ed25519_did(&device_pk);
1019 let device_did = CanonicalDid::new_unchecked(&device_did_str);
1020
1021 let att = create_signed_attestation(
1022 &root_kp,
1023 &device_kp,
1024 &root_did,
1025 &device_did_str,
1026 None,
1027 Some(fixed_now() - Duration::days(1)),
1028 );
1029
1030 assert!(!is_device_listed(
1031 &root_did,
1032 &device_did,
1033 &[verified(att)],
1034 fixed_now()
1035 ));
1036 }
1037
1038 #[test]
1039 fn is_device_listed_returns_false_for_revoked_attestation() {
1040 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1041 let root_did = ed25519_did(&root_pk);
1042 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1043 let device_did_str = ed25519_did(&device_pk);
1044 let device_did = CanonicalDid::new_unchecked(&device_did_str);
1045
1046 let att = create_signed_attestation(
1047 &root_kp,
1048 &device_kp,
1049 &root_did,
1050 &device_did_str,
1051 Some(fixed_now()),
1052 Some(fixed_now() + Duration::days(365)),
1053 );
1054
1055 assert!(!is_device_listed(
1056 &root_did,
1057 &device_did,
1058 &[verified(att)],
1059 fixed_now()
1060 ));
1061 }
1062
1063 #[test]
1064 fn is_device_listed_returns_true_if_one_valid_among_many() {
1065 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1066 let root_did = ed25519_did(&root_pk);
1067 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1068 let device_did_str = ed25519_did(&device_pk);
1069 let device_did = CanonicalDid::new_unchecked(&device_did_str);
1070
1071 let att_expired = verified(create_signed_attestation(
1072 &root_kp,
1073 &device_kp,
1074 &root_did,
1075 &device_did_str,
1076 None,
1077 Some(fixed_now() - Duration::days(1)),
1078 ));
1079 let att_revoked = verified(create_signed_attestation(
1080 &root_kp,
1081 &device_kp,
1082 &root_did,
1083 &device_did_str,
1084 Some(fixed_now()),
1085 Some(fixed_now() + Duration::days(365)),
1086 ));
1087 let att_valid = verified(create_signed_attestation(
1088 &root_kp,
1089 &device_kp,
1090 &root_did,
1091 &device_did_str,
1092 None,
1093 Some(fixed_now() + Duration::days(365)),
1094 ));
1095
1096 assert!(is_device_listed(
1097 &root_did,
1098 &device_did,
1099 &[att_expired, att_revoked, att_valid],
1100 fixed_now()
1101 ));
1102 }
1103
1104 #[test]
1105 fn is_device_listed_returns_false_for_wrong_identity() {
1106 let (_, root_pk) = create_test_keypair(&[1u8; 32]);
1107 let root_did = ed25519_did(&root_pk);
1108 let (other_kp, other_pk) = create_test_keypair(&[3u8; 32]);
1109 let other_did = ed25519_did(&other_pk);
1110 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1111 let device_did_str = ed25519_did(&device_pk);
1112 let device_did = CanonicalDid::new_unchecked(&device_did_str);
1113
1114 let att = create_signed_attestation(
1115 &other_kp,
1116 &device_kp,
1117 &other_did,
1118 &device_did_str,
1119 None,
1120 Some(fixed_now() + Duration::days(365)),
1121 );
1122 assert!(!is_device_listed(
1123 &root_did,
1124 &device_did,
1125 &[verified(att)],
1126 fixed_now()
1127 ));
1128 }
1129
1130 #[test]
1131 fn is_device_listed_returns_false_for_wrong_device() {
1132 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1133 let root_did = ed25519_did(&root_pk);
1134 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1135 let device_did_str = ed25519_did(&device_pk);
1136 let (_, other_device_pk) = create_test_keypair(&[4u8; 32]);
1137 let other_device_did_str = ed25519_did(&other_device_pk);
1138 let other_device_did = CanonicalDid::new_unchecked(&other_device_did_str);
1139
1140 let att = create_signed_attestation(
1141 &root_kp,
1142 &device_kp,
1143 &root_did,
1144 &device_did_str,
1145 None,
1146 Some(fixed_now() + Duration::days(365)),
1147 );
1148 assert!(!is_device_listed(
1149 &root_did,
1150 &other_device_did,
1151 &[verified(att)],
1152 fixed_now()
1153 ));
1154 }
1155
1156 fn create_signed_attestation_with_delegation(
1158 issuer_kp: &Ed25519KeyPair,
1159 device_kp: &Ed25519KeyPair,
1160 issuer_did: &str,
1161 subject_did: &str,
1162 delegated_by: Option<CanonicalDid>,
1163 ) -> Attestation {
1164 let device_pk: [u8; 32] = device_kp.public_key().as_ref().try_into().unwrap();
1165
1166 let mut att = Attestation {
1167 version: 1,
1168 rid: ResourceId::new("test-rid"),
1169 issuer: CanonicalDid::new_unchecked(issuer_did),
1170 subject: CanonicalDid::new_unchecked(subject_did),
1171 device_public_key: Ed25519PublicKey::from_bytes(device_pk).into(),
1172 identity_signature: Ed25519Signature::empty(),
1173 device_signature: Ed25519Signature::empty(),
1174 revoked_at: None,
1175 expires_at: Some(fixed_now() + Duration::days(365)),
1176 timestamp: Some(fixed_now()),
1177 note: None,
1178 payload: None,
1179 delegated_by,
1180 signer_type: None,
1181 environment_claim: None,
1182 commit_sha: None,
1183 commit_message: None,
1184 author: None,
1185 oidc_binding: None,
1186 };
1187
1188 let canonical_bytes = canonicalize_attestation_data(&att.canonical_data()).unwrap();
1189
1190 att.identity_signature =
1191 Ed25519Signature::try_from_slice(issuer_kp.sign(&canonical_bytes).as_ref()).unwrap();
1192 att.device_signature =
1193 Ed25519Signature::try_from_slice(device_kp.sign(&canonical_bytes).as_ref()).unwrap();
1194
1195 att
1196 }
1197
1198 #[tokio::test]
1199 async fn verify_attestation_rejects_tampered_delegated_by() {
1200 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1201 let root_did = ed25519_did(&root_pk);
1202 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1203 let device_did = ed25519_did(&device_pk);
1204
1205 let mut att = create_signed_attestation_with_delegation(
1206 &root_kp,
1207 &device_kp,
1208 &root_did,
1209 &device_did,
1210 Some(CanonicalDid::new_unchecked("did:keri:Eadmin")),
1211 );
1212 assert!(
1213 test_verifier()
1214 .verify_with_keys(&att, &ed(&root_pk))
1215 .await
1216 .is_ok()
1217 );
1218
1219 att.delegated_by = Some(CanonicalDid::new_unchecked("did:keri:Eattacker"));
1220 let result = test_verifier().verify_with_keys(&att, &ed(&root_pk)).await;
1221 assert!(
1222 result.is_err(),
1223 "Attestation should reject tampered delegated_by"
1224 );
1225 }
1226
1227 #[tokio::test]
1228 async fn verify_attestation_valid_with_delegation() {
1229 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1230 let root_did = ed25519_did(&root_pk);
1231 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1232 let device_did = ed25519_did(&device_pk);
1233
1234 let att = create_signed_attestation_with_delegation(
1235 &root_kp,
1236 &device_kp,
1237 &root_did,
1238 &device_did,
1239 Some(CanonicalDid::new_unchecked("did:keri:Eadmin")),
1240 );
1241
1242 let result = test_verifier().verify_with_keys(&att, &ed(&root_pk)).await;
1243 assert!(
1244 result.is_ok(),
1245 "Attestation with delegation should verify: {:?}",
1246 result.err()
1247 );
1248 }
1249
1250 fn create_signed_attestation_with_timestamp(
1251 issuer_kp: &Ed25519KeyPair,
1252 device_kp: &Ed25519KeyPair,
1253 issuer_did: &str,
1254 subject_did: &str,
1255 timestamp: Option<DateTime<Utc>>,
1256 ) -> Attestation {
1257 let device_pk: [u8; 32] = device_kp.public_key().as_ref().try_into().unwrap();
1258
1259 let mut att = Attestation {
1260 version: 1,
1261 rid: ResourceId::new("test-rid"),
1262 issuer: CanonicalDid::new_unchecked(issuer_did),
1263 subject: CanonicalDid::new_unchecked(subject_did),
1264 device_public_key: Ed25519PublicKey::from_bytes(device_pk).into(),
1265 identity_signature: Ed25519Signature::empty(),
1266 device_signature: Ed25519Signature::empty(),
1267 revoked_at: None,
1268 expires_at: Some(fixed_now() + Duration::days(365)),
1269 timestamp,
1270 note: None,
1271 payload: None,
1272 delegated_by: None,
1273 signer_type: None,
1274 environment_claim: None,
1275 commit_sha: None,
1276 commit_message: None,
1277 author: None,
1278 oidc_binding: None,
1279 };
1280
1281 let canonical_bytes = canonicalize_attestation_data(&att.canonical_data()).unwrap();
1282
1283 att.identity_signature =
1284 Ed25519Signature::try_from_slice(issuer_kp.sign(&canonical_bytes).as_ref()).unwrap();
1285 att.device_signature =
1286 Ed25519Signature::try_from_slice(device_kp.sign(&canonical_bytes).as_ref()).unwrap();
1287
1288 att
1289 }
1290
1291 #[tokio::test]
1292 async fn verify_attestation_created_1_hour_ago_succeeds() {
1293 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1294 let root_did = ed25519_did(&root_pk);
1295 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1296 let device_did = ed25519_did(&device_pk);
1297
1298 let one_hour_ago = fixed_now() - Duration::hours(1);
1299 let att = create_signed_attestation_with_timestamp(
1300 &root_kp,
1301 &device_kp,
1302 &root_did,
1303 &device_did,
1304 Some(one_hour_ago),
1305 );
1306
1307 let result = test_verifier().verify_with_keys(&att, &ed(&root_pk)).await;
1308 assert!(
1309 result.is_ok(),
1310 "Attestation created 1 hour ago should verify: {:?}",
1311 result.err()
1312 );
1313 }
1314
1315 #[tokio::test]
1316 async fn verify_attestation_created_30_days_ago_succeeds() {
1317 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1318 let root_did = ed25519_did(&root_pk);
1319 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1320 let device_did = ed25519_did(&device_pk);
1321
1322 let thirty_days_ago = fixed_now() - Duration::days(30);
1323 let att = create_signed_attestation_with_timestamp(
1324 &root_kp,
1325 &device_kp,
1326 &root_did,
1327 &device_did,
1328 Some(thirty_days_ago),
1329 );
1330
1331 let result = test_verifier().verify_with_keys(&att, &ed(&root_pk)).await;
1332 assert!(
1333 result.is_ok(),
1334 "Attestation created 30 days ago should verify: {:?}",
1335 result.err()
1336 );
1337 }
1338
1339 #[tokio::test]
1340 async fn verify_attestation_with_future_timestamp_fails() {
1341 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1342 let root_did = ed25519_did(&root_pk);
1343 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1344 let device_did = ed25519_did(&device_pk);
1345
1346 let ten_minutes_future = fixed_now() + Duration::minutes(10);
1347 let att = create_signed_attestation_with_timestamp(
1348 &root_kp,
1349 &device_kp,
1350 &root_did,
1351 &device_did,
1352 Some(ten_minutes_future),
1353 );
1354
1355 let result = test_verifier().verify_with_keys(&att, &ed(&root_pk)).await;
1356 assert!(
1357 result.is_err(),
1358 "Attestation with future timestamp should fail"
1359 );
1360 let err_msg = result.unwrap_err().to_string();
1361 assert!(
1362 err_msg.contains("in the future"),
1363 "Error should mention 'in the future': {}",
1364 err_msg
1365 );
1366 }
1367
1368 #[tokio::test]
1369 async fn verify_device_authorization_returns_valid_for_signed_attestation() {
1370 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1371 let root_did = ed25519_did(&root_pk);
1372 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1373 let device_did_str = ed25519_did(&device_pk);
1374 let device_did = CanonicalDid::new_unchecked(&device_did_str);
1375
1376 let att = create_signed_attestation(
1377 &root_kp,
1378 &device_kp,
1379 &root_did,
1380 &device_did_str,
1381 None,
1382 Some(fixed_now() + Duration::days(365)),
1383 );
1384
1385 let result = test_verifier()
1386 .verify_device_authorization(&root_did, &device_did, &[att], &ed(&root_pk))
1387 .await;
1388 assert!(result.is_ok());
1389 let report = result.unwrap();
1390 assert!(report.is_valid());
1391 assert_eq!(report.chain.len(), 1);
1392 assert!(report.chain[0].valid);
1393 }
1394
1395 #[tokio::test]
1396 async fn verify_device_authorization_returns_broken_chain_for_no_attestations() {
1397 let (_, root_pk) = create_test_keypair(&[1u8; 32]);
1398 let root_did = ed25519_did(&root_pk);
1399 let (_, device_pk) = create_test_keypair(&[2u8; 32]);
1400 let device_did_str = ed25519_did(&device_pk);
1401 let device_did = CanonicalDid::new_unchecked(&device_did_str);
1402
1403 let result = test_verifier()
1404 .verify_device_authorization(&root_did, &device_did, &[], &ed(&root_pk))
1405 .await;
1406 assert!(result.is_ok());
1407 let report = result.unwrap();
1408 assert!(!report.is_valid());
1409 match report.status {
1410 VerificationStatus::BrokenChain { missing_link } => {
1411 assert!(missing_link.contains("No attestation found"));
1412 }
1413 _ => panic!("Expected BrokenChain status, got {:?}", report.status),
1414 }
1415 }
1416
1417 #[tokio::test]
1418 async fn verify_device_authorization_fails_for_forged_signature() {
1419 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1420 let root_did = ed25519_did(&root_pk);
1421 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1422 let device_did_str = ed25519_did(&device_pk);
1423 let device_did = CanonicalDid::new_unchecked(&device_did_str);
1424
1425 let mut att = create_signed_attestation(
1426 &root_kp,
1427 &device_kp,
1428 &root_did,
1429 &device_did_str,
1430 None,
1431 Some(fixed_now() + Duration::days(365)),
1432 );
1433 let mut tampered = *att.identity_signature.as_bytes();
1434 tampered[0] ^= 0xFF;
1435 att.identity_signature = Ed25519Signature::from_bytes(tampered);
1436
1437 let result = test_verifier()
1438 .verify_device_authorization(&root_did, &device_did, &[att], &ed(&root_pk))
1439 .await;
1440 assert!(result.is_ok());
1441 let report = result.unwrap();
1442 assert!(!report.is_valid());
1443 match report.status {
1444 VerificationStatus::InvalidSignature { step } => assert_eq!(step, 0),
1445 _ => panic!("Expected InvalidSignature status, got {:?}", report.status),
1446 }
1447 }
1448
1449 #[tokio::test]
1450 async fn verify_device_authorization_fails_for_wrong_issuer_key() {
1451 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1452 let root_did = ed25519_did(&root_pk);
1453 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1454 let device_did_str = ed25519_did(&device_pk);
1455 let device_did = CanonicalDid::new_unchecked(&device_did_str);
1456 let (_, wrong_pk) = create_test_keypair(&[99u8; 32]);
1457
1458 let att = create_signed_attestation(
1459 &root_kp,
1460 &device_kp,
1461 &root_did,
1462 &device_did_str,
1463 None,
1464 Some(fixed_now() + Duration::days(365)),
1465 );
1466
1467 let result = test_verifier()
1468 .verify_device_authorization(&root_did, &device_did, &[att], &ed(&wrong_pk))
1469 .await;
1470 assert!(result.is_ok());
1471 let report = result.unwrap();
1472 assert!(!report.is_valid());
1473 match report.status {
1474 VerificationStatus::InvalidSignature { .. } => {}
1475 _ => panic!("Expected InvalidSignature status, got {:?}", report.status),
1476 }
1477 }
1478
1479 #[tokio::test]
1480 async fn verify_device_authorization_checks_expiry_and_revocation() {
1481 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1482 let root_did = ed25519_did(&root_pk);
1483 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1484 let device_did_str = ed25519_did(&device_pk);
1485 let device_did = CanonicalDid::new_unchecked(&device_did_str);
1486
1487 let att_expired = create_signed_attestation(
1488 &root_kp,
1489 &device_kp,
1490 &root_did,
1491 &device_did_str,
1492 None,
1493 Some(fixed_now() - Duration::days(1)),
1494 );
1495 let result = test_verifier()
1496 .verify_device_authorization(&root_did, &device_did, &[att_expired], &ed(&root_pk))
1497 .await;
1498 assert!(result.is_ok());
1499 let report = result.unwrap();
1500 assert!(!report.is_valid());
1501 match report.status {
1502 VerificationStatus::Expired { .. } => {}
1503 _ => panic!("Expected Expired status, got {:?}", report.status),
1504 }
1505
1506 let att_revoked = create_signed_attestation(
1507 &root_kp,
1508 &device_kp,
1509 &root_did,
1510 &device_did_str,
1511 Some(fixed_now()),
1512 Some(fixed_now() + Duration::days(365)),
1513 );
1514 let result = test_verifier()
1515 .verify_device_authorization(&root_did, &device_did, &[att_revoked], &ed(&root_pk))
1516 .await;
1517 assert!(result.is_ok());
1518 let report = result.unwrap();
1519 assert!(!report.is_valid());
1520 match report.status {
1521 VerificationStatus::Revoked { .. } => {}
1522 _ => panic!("Expected Revoked status, got {:?}", report.status),
1523 }
1524 }
1525
1526 fn create_witness_receipt(
1527 witness_kp: &Ed25519KeyPair,
1528 witness_did: &str,
1529 event_said: &str,
1530 seq: u128,
1531 ) -> auths_keri::witness::SignedReceipt {
1532 let receipt = auths_keri::witness::Receipt {
1533 v: auths_keri::VersionString::placeholder(),
1534 t: auths_keri::witness::ReceiptTag,
1535 d: Said::new_unchecked(event_said.to_string()),
1536 i: auths_keri::Prefix::new_unchecked(witness_did.to_string()),
1537 s: auths_keri::KeriSequence::new(seq),
1538 };
1539 let payload = serde_json::to_vec(&receipt).unwrap();
1540 let sig = witness_kp.sign(&payload).as_ref().to_vec();
1541 auths_keri::witness::SignedReceipt {
1542 receipt,
1543 signature: sig,
1544 }
1545 }
1546
1547 #[tokio::test]
1548 async fn verify_chain_with_witnesses_valid() {
1549 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1550 let root_did = ed25519_did(&root_pk);
1551 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1552 let device_did = ed25519_did(&device_pk);
1553
1554 let att = create_signed_attestation(
1555 &root_kp,
1556 &device_kp,
1557 &root_did,
1558 &device_did,
1559 None,
1560 Some(fixed_now() + Duration::days(365)),
1561 );
1562
1563 let (w1_kp, w1_pk) = create_test_keypair(&[10u8; 32]);
1564 let (w2_kp, w2_pk) = create_test_keypair(&[20u8; 32]);
1565
1566 let r1 = create_witness_receipt(&w1_kp, "did:key:w1", "EEvent1", 1);
1567 let r2 = create_witness_receipt(&w2_kp, "did:key:w2", "EEvent1", 1);
1568
1569 let witness_keys = vec![
1570 ("did:key:w1".into(), w1_pk.to_vec()),
1571 ("did:key:w2".into(), w2_pk.to_vec()),
1572 ];
1573
1574 let config = crate::witness::WitnessVerifyConfig {
1575 receipts: &[r1, r2],
1576 witness_keys: &witness_keys,
1577 threshold: 2,
1578 };
1579
1580 let report = test_verifier()
1581 .verify_chain_with_witnesses(&[att], &ed(&root_pk), &config)
1582 .await
1583 .unwrap();
1584 assert!(report.is_valid());
1585 assert!(report.witness_quorum.is_some());
1586 let quorum = report.witness_quorum.unwrap();
1587 assert_eq!(quorum.required, 2);
1588 assert_eq!(quorum.verified, 2);
1589 }
1590
1591 #[tokio::test]
1592 async fn verify_chain_with_witnesses_chain_fails() {
1593 let (w1_kp, w1_pk) = create_test_keypair(&[10u8; 32]);
1594 let r1 = create_witness_receipt(&w1_kp, "did:key:w1", "EEvent1", 1);
1595 let witness_keys = vec![("did:key:w1".into(), w1_pk.to_vec())];
1596 let config = crate::witness::WitnessVerifyConfig {
1597 receipts: &[r1],
1598 witness_keys: &witness_keys,
1599 threshold: 1,
1600 };
1601
1602 let report = test_verifier()
1603 .verify_chain_with_witnesses(&[], &ed(&[0u8; 32]), &config)
1604 .await
1605 .unwrap();
1606 assert!(!report.is_valid());
1607 match report.status {
1608 VerificationStatus::BrokenChain { .. } => {}
1609 _ => panic!("Expected BrokenChain, got {:?}", report.status),
1610 }
1611 assert!(report.witness_quorum.is_none());
1612 }
1613
1614 #[tokio::test]
1615 async fn verify_chain_with_witnesses_quorum_fails() {
1616 let (root_kp, root_pk) = create_test_keypair(&[1u8; 32]);
1617 let root_did = ed25519_did(&root_pk);
1618 let (device_kp, device_pk) = create_test_keypair(&[2u8; 32]);
1619 let device_did = ed25519_did(&device_pk);
1620
1621 let att = create_signed_attestation(
1622 &root_kp,
1623 &device_kp,
1624 &root_did,
1625 &device_did,
1626 None,
1627 Some(fixed_now() + Duration::days(365)),
1628 );
1629
1630 let (w1_kp, w1_pk) = create_test_keypair(&[10u8; 32]);
1631 let r1 = create_witness_receipt(&w1_kp, "did:key:w1", "EEvent1", 1);
1632 let witness_keys = vec![("did:key:w1".into(), w1_pk.to_vec())];
1633 let config = crate::witness::WitnessVerifyConfig {
1634 receipts: &[r1],
1635 witness_keys: &witness_keys,
1636 threshold: 2,
1637 };
1638
1639 let report = test_verifier()
1640 .verify_chain_with_witnesses(&[att], &ed(&root_pk), &config)
1641 .await
1642 .unwrap();
1643 assert!(!report.is_valid());
1644 match report.status {
1645 VerificationStatus::InsufficientWitnesses { required, verified } => {
1646 assert_eq!(required, 2);
1647 assert_eq!(verified, 1);
1648 }
1649 _ => panic!("Expected InsufficientWitnesses, got {:?}", report.status),
1650 }
1651 assert!(report.witness_quorum.is_some());
1652 assert!(!report.warnings.is_empty());
1653 }
1654
1655 #[tokio::test]
1661 async fn verify_p256_identity_signed_attestation() {
1662 use auths_crypto::RingCryptoProvider;
1663
1664 let (p256_seed, p256_pk_bytes) = RingCryptoProvider::p256_generate().unwrap();
1666 assert_eq!(
1667 p256_pk_bytes.len(),
1668 33,
1669 "P-256 compressed pubkey is 33 bytes"
1670 );
1671 let seed_arr: [u8; 32] = *p256_seed.as_bytes();
1672
1673 let issuer_did = "did:keri:Etest-p256-identity";
1676
1677 let (device_kp, device_pk) = create_test_keypair(&[42u8; 32]);
1679 let device_did = ed25519_did(&device_pk);
1680
1681 let mut att = Attestation {
1682 version: 1,
1683 rid: ResourceId::new("test-rid"),
1684 issuer: CanonicalDid::new_unchecked(issuer_did),
1685 subject: CanonicalDid::new_unchecked(&device_did),
1686 device_public_key: Ed25519PublicKey::from_bytes(device_pk).into(),
1687 identity_signature: Ed25519Signature::empty(),
1688 device_signature: Ed25519Signature::empty(),
1689 revoked_at: None,
1690 expires_at: Some(fixed_now() + Duration::days(365)),
1691 timestamp: Some(fixed_now()),
1692 note: None,
1693 payload: None,
1694 delegated_by: None,
1695 signer_type: None,
1696 environment_claim: None,
1697 commit_sha: None,
1698 commit_message: None,
1699 author: None,
1700 oidc_binding: None,
1701 };
1702
1703 let canonical_bytes = canonicalize_attestation_data(&att.canonical_data()).unwrap();
1704
1705 let p256_sig = RingCryptoProvider::p256_sign(&seed_arr, &canonical_bytes).unwrap();
1707 assert_eq!(p256_sig.len(), 64);
1709 att.identity_signature = Ed25519Signature::try_from_slice(&p256_sig).unwrap();
1710
1711 att.device_signature =
1713 Ed25519Signature::try_from_slice(device_kp.sign(&canonical_bytes).as_ref()).unwrap();
1714
1715 let issuer_dpk =
1716 DevicePublicKey::try_new(auths_crypto::CurveType::P256, &p256_pk_bytes).unwrap();
1717
1718 let result = test_verifier().verify_with_keys(&att, &issuer_dpk).await;
1719 assert!(
1720 result.is_ok(),
1721 "P-256-signed attestation should verify with a P-256 DevicePublicKey: {:?}",
1722 result.err()
1723 );
1724 }
1725}