1use crate::sparql_ast::*;
7use crate::NQuin;
8
9#[repr(C)]
20#[derive(Debug, Clone, Copy)]
21pub struct DidResolutionPointer {
22 pub did: u64,
23 pub endpoint_url: u64,
24 pub verification_method: u64,
25 pub expires: u64,
26}
27
28#[derive(Debug, Clone)]
34pub struct DidResolutionResult {
35 pub did: String,
37 pub endpoint_url: String,
39 pub verification_method: String,
41 pub resolved_at: u64,
43}
44
45#[repr(C)]
47#[derive(Debug, Clone, Copy)]
48pub struct DidVerificationResult {
49 pub did: u64,
50 pub valid: bool,
51 pub algorithm: u8,
52}
53
54#[repr(C)]
56#[derive(Debug, Clone, Copy)]
57pub struct DidPermissionResult {
58 pub did: u64,
59 pub graph: u64,
60 pub has_permission: bool,
61 pub permission_type: u8, }
63
64#[repr(C)]
66#[derive(Debug, Clone, Copy)]
67pub struct DidCacheEntry {
68 pub did: u64,
69 pub resolution: DidResolutionPointer,
70 pub timestamp: u64,
71 pub ttl: u32,
72}
73
74pub struct SparqlDidHandler<'a> {
76 pub quins: &'a [NQuin],
77 pub did_cache: [Option<DidCacheEntry>; 32],
78 pub cache_count: u8,
79}
80
81impl<'a> SparqlDidHandler<'a> {
82 pub fn new(quins: &'a [NQuin]) -> Self {
83 Self {
84 quins,
85 did_cache: [None; 32],
86 cache_count: 0,
87 }
88 }
89
90 pub fn resolve_did(&mut self, did: u64) -> Result<DidResolutionPointer, String> {
99 for i in 0..self.cache_count as usize {
101 if let Some(entry) = self.did_cache[i] {
102 if entry.did == did {
103 let now = self.current_timestamp();
104 if now.saturating_sub(entry.timestamp) < (entry.ttl as u64) * 1000 {
107 return Ok(entry.resolution);
108 }
109 }
110 }
111 }
112
113 let id_bytes = (did & 0x0FFF_FFFF_FFFF_FFFF).to_le_bytes();
117 let endpoint_url = fnv1a_tagged(b"did:endpoint:", &id_bytes);
118 let verification_method = fnv1a_tagged(b"did:vm:", &id_bytes);
119
120 let resolution = DidResolutionPointer {
121 did,
122 endpoint_url,
123 verification_method,
124 expires: self.current_timestamp() + 3600000, };
126
127 if self.cache_count < 32 {
129 let entry = DidCacheEntry {
130 did,
131 resolution,
132 timestamp: self.current_timestamp(),
133 ttl: 3600,
134 };
135 self.did_cache[self.cache_count as usize] = Some(entry);
136 self.cache_count += 1;
137 }
138
139 Ok(resolution)
140 }
141
142 pub fn verify_signature(
144 &self,
145 _did: u64,
146 signature: &[u8],
147 data: &[u8],
148 ) -> Result<DidVerificationResult, String> {
149 if signature.len() != 64 {
150 return Err("Invalid signature length".to_string());
151 }
152
153 let _ = (signature, data);
154
155 #[cfg(feature = "interop-crypto")]
156 {
157 use ed25519_dalek::{Signature, Verifier, VerifyingKey};
158
159 let mut sig_bytes = [0u8; 64];
160 sig_bytes.copy_from_slice(signature);
161 if let Ok(sig) = Signature::from_bytes(&sig_bytes) {
162 if data.len() > 32 {
165 let mut pk_bytes = [0u8; 32];
166 pk_bytes.copy_from_slice(&data[0..32]);
167 if let Ok(verifying_key) = VerifyingKey::from_bytes(&pk_bytes) {
168 let valid = verifying_key.verify(&data[32..], &sig).is_ok();
169 if valid {
170 return Ok(DidVerificationResult {
171 did: _did,
172 valid: true,
173 algorithm: 1, });
175 }
176 }
177 }
178 }
179 }
180
181 Err(
184 "did:verify fast-path failed: no resolvable verification key is provisioned \
185 in the SPARQL read-side shim. Verify via the identity/key-vault layer \
186 (KeyVault::verify_signature)."
187 .to_string(),
188 )
189 }
190
191 pub fn check_permission(
193 &self,
194 did: u64,
195 graph: u64,
196 permission_type: u8,
197 ) -> Result<DidPermissionResult, String> {
198 if (did & 0x8000000000000000) == 0 {
200 return Err("Invalid DID: missing 0x8 prefix".to_string());
201 }
202
203 let _ = (graph, permission_type);
204
205 Err(
210 "did:permission is not available in the SPARQL query layer: \
211 access-control decisions must be evaluated against the policy graph by \
212 the governance layer, not granted unconditionally here."
213 .to_string(),
214 )
215 }
216
217 pub fn authenticate_did(
219 &self,
220 did: u64,
221 auth_method: u8,
222 _auth_payload: &[u8],
223 ) -> Result<bool, String> {
224 if (did & 0x8000000000000000) == 0 {
226 return Err("Invalid DID: missing 0x8 prefix".to_string());
227 }
228
229 let _ = auth_method;
230
231 Err("did:auth is not available in the SPARQL query layer: \
237 the authentication proof is stripped at this boundary and cannot be \
238 verified here. Authenticate via the identity/key-vault layer."
239 .to_string())
240 }
241
242 fn current_timestamp(&self) -> u64 {
245 std::time::SystemTime::now()
246 .duration_since(std::time::UNIX_EPOCH)
247 .map(|d| d.as_millis() as u64)
248 .unwrap_or(0)
249 }
250
251 pub fn sign_with_did(&self, did: u64, data: &[u8]) -> Result<Vec<u8>, String> {
253 if (did & 0x8000000000000000) == 0 {
255 return Err("Invalid DID: missing 0x8 prefix".to_string());
256 }
257
258 let _ = data;
268 Err("did:sign is not available in the SPARQL query layer: \
269 no private key is provisioned here. Sign via the identity/key-vault \
270 layer (WebizenIdentityManager / CryptographicLibrary::sign_data)."
271 .to_string())
272 }
273
274 pub fn invalidate_cache(&mut self, did: u64) {
276 for i in 0..self.cache_count as usize {
277 if let Some(entry) = self.did_cache[i] {
278 if entry.did == did {
279 self.did_cache[i] = None;
280 for j in i..self.cache_count as usize - 1 {
282 self.did_cache[j] = self.did_cache[j + 1];
283 }
284 self.cache_count -= 1;
285 return;
286 }
287 }
288 }
289 }
290}
291
292impl<'a> Default for SparqlDidHandler<'a> {
293 fn default() -> Self {
294 Self::new(&[])
295 }
296}
297
298pub fn did_resolve(args: &[u64], quins: &[NQuin], result: &mut BindingRow) -> bool {
308 if args.is_empty() {
309 return false;
310 }
311 let did = args[0];
312
313 let mut handler = SparqlDidHandler::new(quins);
314 match handler.resolve_did(did) {
315 Ok(resolution) => {
316 result.slots[0] = Some(resolution.endpoint_url);
317 result.slots[1] = Some(resolution.verification_method);
318 true
319 }
320 Err(_) => false,
321 }
322}
323
324pub fn did_verify(args: &[u64], quins: &[NQuin], result: &mut BindingRow) -> bool {
326 if args.len() < 3 {
327 return false;
328 }
329 let did = args[0];
330 let signature_ptr = args[1];
331 let data_ptr = args[2];
332
333 let handler = SparqlDidHandler::new(quins);
334 let _ = (signature_ptr, data_ptr);
340 let signature: &[u8] = &[];
341 let data: &[u8] = &[];
342
343 match handler.verify_signature(did, signature, data) {
344 Ok(verification) => {
345 result.slots[0] = Some(if verification.valid { 1 } else { 0 });
346 result.slots[1] = Some(verification.algorithm as u64);
347 true
348 }
349 Err(_) => false,
350 }
351}
352
353pub fn did_auth(args: &[u64], quins: &[NQuin], result: &mut BindingRow) -> bool {
355 if args.len() < 2 {
356 return false;
357 }
358 let did = args[0];
359 let auth_method = args[1] as u8;
360
361 let handler = SparqlDidHandler::new(quins);
362 let auth_payload: &[u8] = &[];
366
367 match handler.authenticate_did(did, auth_method, auth_payload) {
368 Ok(valid) => {
369 result.slots[0] = Some(if valid { 1 } else { 0 });
370 true
371 }
372 Err(_) => false,
373 }
374}
375
376pub fn did_sign(args: &[u64], quins: &[NQuin], result: &mut BindingRow) -> bool {
378 if args.len() < 2 {
379 return false;
380 }
381 let did = args[0];
382 let data_ptr = args[1];
383
384 let handler = SparqlDidHandler::new(quins);
385 let _ = data_ptr;
388 let data: &[u8] = &[];
389
390 match handler.sign_with_did(did, data) {
391 Ok(_signature) => {
392 result.slots[0] = Some(1); true
394 }
395 Err(_) => false,
396 }
397}
398
399pub fn did_permission(args: &[u64], quins: &[NQuin], result: &mut BindingRow) -> bool {
401 if args.len() < 3 {
402 return false;
403 }
404 let did = args[0];
405 let graph = args[1];
406 let permission_type = args[2] as u8;
407
408 let handler = SparqlDidHandler::new(quins);
409 match handler.check_permission(did, graph, permission_type) {
410 Ok(permission) => {
411 result.slots[0] = Some(if permission.has_permission { 1 } else { 0 });
412 result.slots[1] = Some(permission.permission_type as u64);
413 true
414 }
415 Err(_) => false,
416 }
417}
418
419#[inline]
426fn fnv1a_tagged(tag: &[u8], payload: &[u8]) -> u64 {
427 let mut hash: u64 = 0xcbf29ce484222325;
428 for &b in tag {
429 hash ^= b as u64;
430 hash = hash.wrapping_mul(0x100000001b3);
431 }
432 for &b in payload {
433 hash ^= b as u64;
434 hash = hash.wrapping_mul(0x100000001b3);
435 }
436 hash & 0x0FFF_FFFF_FFFF_FFFF
437}
438
439pub struct DIDResolver;
455
456impl DIDResolver {
457 pub fn new() -> Self {
459 Self
460 }
461
462 pub fn resolve(&self, did: &str) -> Result<DidResolutionResult, String> {
467 self.resolve_did(did)
468 }
469
470 pub fn resolve_did(&self, did: &str) -> Result<DidResolutionResult, String> {
472 let resolved_at = current_epoch_millis();
473
474 if let Some(rest) = did.strip_prefix("did:q42:") {
475 if rest.is_empty() {
476 return Err("did:q42 resolution failed: empty identifier".to_string());
477 }
478 let pointer = crate::identifier::parse_did_q42(did.as_bytes())
482 .map_err(|e| format!("did:q42 resolution failed: {:?}", e))?;
483 let id_hex = format!("{:015x}", pointer & 0x0FFF_FFFF_FFFF_FFFF);
484 Ok(DidResolutionResult {
485 did: did.to_string(),
486 endpoint_url: format!("https://q42.network/agents/{}", id_hex),
487 verification_method: format!("{}#q42-key", did),
488 resolved_at,
489 })
490 } else if let Some(rest) = did.strip_prefix("did:web:") {
491 if rest.is_empty() {
492 return Err("did:web resolution failed: empty domain".to_string());
493 }
494 let domain_path = rest.replace(':', "/");
497 Ok(DidResolutionResult {
498 did: did.to_string(),
499 endpoint_url: format!("https://{}/.well-known/did.json", domain_path),
500 verification_method: format!("{}#key-1", did),
501 resolved_at,
502 })
503 } else if let Some(rest) = did.strip_prefix("did:key:") {
504 if rest.is_empty() {
505 return Err("did:key resolution failed: empty key".to_string());
506 }
507 Ok(DidResolutionResult {
511 did: did.to_string(),
512 endpoint_url: format!("https://dev.uniresolver.io/1.0/identifiers/{}", did),
513 verification_method: did.to_string(),
514 resolved_at,
515 })
516 } else if did.starts_with("did:") {
517 let method = did
519 .get(4..)
520 .and_then(|s| s.split(':').next())
521 .unwrap_or("unknown");
522 Err(format!(
523 "DID resolution failed: unsupported DID method '{}'. \
524 Supported methods: did:q42, did:web, did:key.",
525 method
526 ))
527 } else {
528 Err(format!(
529 "DID resolution failed: '{}' is not a valid DID (must start with 'did:')",
530 did
531 ))
532 }
533 }
534
535 pub fn verify_authentication_signature(
547 &self,
548 did: &str,
549 signature: &[u8],
550 payload: &[u8],
551 ) -> Result<bool, String> {
552 self.resolve_did(did)?;
554
555 if signature.is_empty() {
556 return Err("DID signature verification failed: empty signature".to_string());
557 }
558 if payload.is_empty() {
559 return Err("DID signature verification failed: empty payload".to_string());
560 }
561
562 Err("did:verify is not available in the SPARQL query layer: \
567 no resolvable verification key is provisioned here. Verify via the \
568 identity/key-vault layer (KeyVault::verify_signature / \
569 WebizenIdentityRegistry::verify_signature)."
570 .to_string())
571 }
572}
573
574impl Default for DIDResolver {
575 fn default() -> Self {
576 Self::new()
577 }
578}
579
580fn current_epoch_millis() -> u64 {
587 use std::time::{SystemTime, UNIX_EPOCH};
588 SystemTime::now()
589 .duration_since(UNIX_EPOCH)
590 .map(|d| d.as_millis() as u64)
591 .unwrap_or(0)
592}
593
594#[cfg(test)]
595mod tests {
596 use super::*;
597
598 #[test]
599 fn test_did_handler_creation() {
600 let quins = vec![];
601 let handler = SparqlDidHandler::new(&quins);
602 assert_eq!(handler.cache_count, 0);
603 }
604
605 #[test]
606 fn test_resolve_did() {
607 let quins = vec![];
608 let mut handler = SparqlDidHandler::new(&quins);
609
610 let result = handler.resolve_did(0x8000000000000001); assert!(result.is_ok());
612 assert_eq!(result.unwrap().did, 0x8000000000000001);
613 }
614
615 #[test]
616 fn test_verify_signature_fails_closed() {
617 let quins = vec![];
621 let handler = SparqlDidHandler::new(&quins);
622
623 let signature = &[0u8; 64];
624 let data = &[0u8; 256];
625
626 let result = handler.verify_signature(0x8000000000000001, signature, data);
627 assert!(
628 result.is_err(),
629 "verify_signature must fail closed, not return valid"
630 );
631 }
632
633 #[test]
634 fn test_check_permission_fails_closed() {
635 let quins = vec![];
637 let handler = SparqlDidHandler::new(&quins);
638
639 let result = handler.check_permission(0x8000000000000001, 123, 0);
640 assert!(
641 result.is_err(),
642 "check_permission must fail closed, not grant access"
643 );
644 }
645
646 #[test]
647 fn test_authenticate_did_fails_closed() {
648 let quins = vec![];
650 let handler = SparqlDidHandler::new(&quins);
651
652 let result = handler.authenticate_did(0x8000000000000001, 1, &[0u8; 256]);
653 assert!(
654 result.is_err(),
655 "authenticate_did must fail closed, not authenticate all"
656 );
657 }
658
659 #[test]
662 fn test_resolver_did_q42_produces_valid_url() {
663 let resolver = DIDResolver::new();
664 let did = "did:q42:z6MkpTHR8VNs";
665 let result = resolver.resolve(did).expect("did:q42 must resolve");
666
667 assert_eq!(result.did, did);
668 assert!(
670 result
671 .endpoint_url
672 .starts_with("https://q42.network/agents/"),
673 "endpoint_url should be a q42.network agent URL, got '{}'",
674 result.endpoint_url
675 );
676 let agent_id = result
678 .endpoint_url
679 .strip_prefix("https://q42.network/agents/")
680 .unwrap();
681 assert!(!agent_id.is_empty(), "agent id must not be empty");
682 assert!(
683 agent_id.chars().all(|c| c.is_ascii_hexdigit()),
684 "agent id must be hex, got '{}'",
685 agent_id
686 );
687 assert_eq!(result.verification_method, format!("{}#q42-key", did));
688 assert!(result.resolved_at > 0, "resolved_at must be set");
689 }
690
691 #[test]
692 fn test_resolver_did_q42_is_deterministic() {
693 let resolver = DIDResolver::new();
694 let did = "did:q42:z6MkpTHR8VNs";
695 let a = resolver.resolve(did).unwrap();
696 let b = resolver.resolve(did).unwrap();
697 assert_eq!(
698 a.endpoint_url, b.endpoint_url,
699 "resolution must be deterministic"
700 );
701 assert_eq!(a.verification_method, b.verification_method);
702 }
703
704 #[test]
705 fn test_resolver_did_q42_distinct_payloads_distinct_urls() {
706 let resolver = DIDResolver::new();
707 let a = resolver.resolve("did:q42:z6MkpTHR8VNs").unwrap();
708 let b = resolver.resolve("did:q42:z6MkpTHR8VNt").unwrap();
709 assert_ne!(
710 a.endpoint_url, b.endpoint_url,
711 "distinct DIDs must resolve to distinct URLs"
712 );
713 }
714
715 #[test]
716 fn test_resolver_did_web_maps_to_well_known() {
717 let resolver = DIDResolver::new();
718 let did = "did:web:example.com";
719 let result = resolver.resolve(did).expect("did:web must resolve");
720
721 assert_eq!(result.did, did);
722 assert_eq!(
723 result.endpoint_url, "https://example.com/.well-known/did.json",
724 "did:web must map to the standard well-known DID document URL"
725 );
726 assert_eq!(result.verification_method, "did:web:example.com#key-1");
727 }
728
729 #[test]
730 fn test_resolver_did_web_with_path_components() {
731 let resolver = DIDResolver::new();
733 let did = "did:web:example.com:users:alice";
734 let result = resolver.resolve(did).unwrap();
735 assert_eq!(
736 result.endpoint_url,
737 "https://example.com/users/alice/.well-known/did.json"
738 );
739 }
740
741 #[test]
742 fn test_resolver_did_key() {
743 let resolver = DIDResolver::new();
744 let did = "did:key:z6MkhaXgBZDvotDkL5v7wB9QkN8eYfH2";
745 let result = resolver.resolve(did).expect("did:key must resolve");
746
747 assert_eq!(result.did, did);
748 assert_eq!(
750 result.endpoint_url,
751 format!("https://dev.uniresolver.io/1.0/identifiers/{}", did)
752 );
753 assert_eq!(result.verification_method, did);
755 }
756
757 #[test]
758 fn test_resolver_unknown_method_returns_error() {
759 let resolver = DIDResolver::new();
760 let result = resolver.resolve("did:foo:bar");
761 assert!(result.is_err(), "unknown DID method must error");
762 let err = result.unwrap_err();
763 assert!(
764 err.contains("unsupported DID method"),
765 "error should mention unsupported method, got: {}",
766 err
767 );
768 assert!(err.contains("foo"), "error should name the method 'foo'");
769 }
770
771 #[test]
772 fn test_resolver_non_did_returns_error() {
773 let resolver = DIDResolver::new();
774 let result = resolver.resolve("https://example.com/not-a-did");
775 assert!(result.is_err(), "non-DID input must error");
776 assert!(result.unwrap_err().contains("not a valid DID"));
777 }
778
779 #[test]
780 fn test_resolver_empty_q42_payload_errors() {
781 let resolver = DIDResolver::new();
782 assert!(resolver.resolve("did:q42:").is_err());
783 }
784
785 #[test]
786 fn test_resolver_empty_web_domain_errors() {
787 let resolver = DIDResolver::new();
788 assert!(resolver.resolve("did:web:").is_err());
789 }
790
791 #[test]
792 fn test_resolver_empty_key_errors() {
793 let resolver = DIDResolver::new();
794 assert!(resolver.resolve("did:key:").is_err());
795 }
796
797 #[test]
798 fn test_resolver_resolve_delegates_to_resolve_did() {
799 let resolver = DIDResolver::new();
801 let did = "did:web:example.com";
802 let a = resolver.resolve(did).unwrap();
803 let b = resolver.resolve_did(did).unwrap();
804 assert_eq!(a.did, b.did);
805 assert_eq!(a.endpoint_url, b.endpoint_url);
806 assert_eq!(a.verification_method, b.verification_method);
807 }
808
809 #[test]
810 fn test_verify_authentication_signature_fails_closed() {
811 let resolver = DIDResolver::new();
814 let did = "did:q42:z6MkpTHR8VNs";
815 let result = resolver.verify_authentication_signature(did, &[1u8; 64], &[2u8; 32]);
816 assert!(result.is_err(), "must fail closed, not return valid");
817 assert!(result.unwrap_err().contains("identity/key-vault layer"));
818 }
819
820 #[test]
821 fn test_verify_authentication_signature_rejects_empty_signature() {
822 let resolver = DIDResolver::new();
823 let result =
824 resolver.verify_authentication_signature("did:q42:z6MkpTHR8VNs", &[], &[2u8; 32]);
825 assert!(result.is_err());
826 assert!(result.unwrap_err().contains("empty signature"));
827 }
828
829 #[test]
830 fn test_verify_authentication_signature_rejects_bad_did() {
831 let resolver = DIDResolver::new();
832 let result =
834 resolver.verify_authentication_signature("did:foo:bar", &[1u8; 64], &[2u8; 32]);
835 assert!(result.is_err());
836 }
837
838 #[test]
841 fn test_resolve_did_pointer_is_not_xor_garbage() {
842 let quins = vec![];
846 let mut handler = SparqlDidHandler::new(&quins);
847 let did = 0x8000000000000001;
848 let result = handler.resolve_did(did).unwrap();
849
850 assert_ne!(result.endpoint_url, did ^ 0xDEADBEEF);
852 assert_ne!(result.verification_method, did ^ 0xCAFEBABE);
853 assert_ne!(result.endpoint_url, result.verification_method);
855 let again = handler.resolve_did(did).unwrap_or_else(|_| {
857 handler.invalidate_cache(did);
858 handler.resolve_did(did).unwrap()
859 });
860 assert_eq!(result.endpoint_url, again.endpoint_url);
862 assert_eq!(result.endpoint_url >> 60, 0);
864 assert_eq!(result.verification_method >> 60, 0);
865 }
866}