1use std::collections::BTreeMap;
2use std::fmt;
3
4use anp::authentication::{
5 complete_http_signature_headers, complete_legacy_did_wba_auth_header, find_verification_method,
6 prepare_http_signature_headers, prepare_legacy_did_wba_auth_header,
7};
8use anp::proof::{
9 complete_object_proof, complete_rfc9421_origin_proof, complete_w3c_proof, prepare_object_proof,
10 prepare_rfc9421_origin_proof, prepare_w3c_proof, ProofGenerationOptions, Rfc9421OriginProof,
11 Rfc9421OriginProofGenerationOptions,
12};
13use zeroize::{Zeroize, Zeroizing};
14
15use crate::secret::SecretBytes;
16use crate::{DidError, DidIdentity, DidResult, KeyMetadata, KeyRole, KeyState};
17
18pub struct SharedSecret {
19 bytes: Zeroizing<[u8; 32]>,
20}
21
22impl SharedSecret {
23 pub fn as_bytes(&self) -> &[u8; 32] {
24 &self.bytes
25 }
26}
27
28impl fmt::Debug for SharedSecret {
29 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
30 formatter
31 .debug_struct("SharedSecret")
32 .field("value", &"[REDACTED]")
33 .finish()
34 }
35}
36
37impl DidIdentity {
38 pub fn sign_pending_root_object_proof(
39 &self,
40 kid: &str,
41 document: &serde_json::Value,
42 issuer_did: &str,
43 created: Option<String>,
44 ) -> DidResult<serde_json::Value> {
45 let current = crate::registry::read_identity(self.runtime().root(), self.identity_id())?;
46 if current.generation != self.record().generation {
47 return Err(DidError::Conflict);
48 }
49 if current.state != crate::IdentityState::Active
50 || current.root_capability != crate::RootCapabilityState::Pending
51 || current.pending_root_transfer.is_none()
52 {
53 return Err(DidError::RootCapabilityUnavailable);
54 }
55 let kid = crate::input::canonicalize_kid(¤t.did, kid)?;
56 let metadata = current
57 .keys
58 .iter()
59 .find(|metadata| metadata.kid == kid)
60 .ok_or(DidError::KeyNotFound)?;
61 if metadata.role != KeyRole::RootControl
62 || metadata.origin != crate::KeyOrigin::Managed
63 || metadata.state != KeyState::Pending
64 || metadata.material_erased
65 {
66 return Err(DidError::RootCapabilityUnavailable);
67 }
68 let method = find_verification_method(self.document(), &metadata.kid)
69 .ok_or(DidError::InvalidIdentity)?;
70 let public = anp::authentication::extract_public_key(&method)
71 .map_err(|_| DidError::InvalidPublicKey)?;
72 let prepared = prepare_object_proof(document, &public, &metadata.kid, issuer_did, created)
73 .map_err(|_| DidError::Crypto)?;
74 let signature = self.sign_prepared(metadata, prepared.signing_input())?;
75 complete_object_proof(prepared, &signature).map_err(|_| DidError::Crypto)
76 }
77
78 pub fn sign_pending_enrollment(
79 &self,
80 enrollment_id: &str,
81 kid: &str,
82 message: &[u8],
83 ) -> DidResult<Vec<u8>> {
84 let record = self.current_pending_enrollment(enrollment_id)?;
85 let local = record
86 .local_authorization
87 .as_ref()
88 .ok_or(DidError::InvalidIdentity)?;
89 let kid = crate::input::canonicalize_kid(&record.did, kid)?;
90 if kid != local.signing_kid {
91 return Err(DidError::KeyRoleViolation);
92 }
93 let metadata = record
94 .keys
95 .iter()
96 .find(|metadata| metadata.kid == kid)
97 .ok_or(DidError::KeyNotFound)?;
98 if metadata.role != KeyRole::DeviceSigning
99 || metadata.origin != crate::KeyOrigin::Managed
100 || metadata.state != KeyState::Pending
101 || metadata.material_erased
102 {
103 return Err(DidError::KeyNotUsable);
104 }
105 self.sign_prepared(metadata, message)
106 }
107
108 pub fn ecdh_pending_enrollment(
109 &self,
110 enrollment_id: &str,
111 kid: &str,
112 peer_public: &[u8],
113 ) -> DidResult<SharedSecret> {
114 let record = self.current_pending_enrollment(enrollment_id)?;
115 let local = record
116 .local_authorization
117 .as_ref()
118 .ok_or(DidError::InvalidIdentity)?;
119 let kid = crate::input::canonicalize_kid(&record.did, kid)?;
120 if kid != local.e2ee_kid {
121 return Err(DidError::KeyRoleViolation);
122 }
123 let metadata = record
124 .keys
125 .iter()
126 .find(|metadata| metadata.kid == kid)
127 .ok_or(DidError::KeyNotFound)?;
128 if metadata.role != KeyRole::E2eeAgreement
129 || metadata.origin != crate::KeyOrigin::Managed
130 || metadata.state != KeyState::Pending
131 || metadata.material_erased
132 {
133 return Err(DidError::KeyNotUsable);
134 }
135 let peer: [u8; 32] = peer_public
136 .try_into()
137 .map_err(|_| DidError::InvalidPeerKey)?;
138 let secret = self.load_managed_secret(metadata)?;
139 let private = x25519_private_key(&secret)?;
140 let shared = private.diffie_hellman(&x25519_dalek::PublicKey::from(peer));
141 let bytes = Zeroizing::new(shared.to_bytes());
142 if bytes.iter().all(|byte| *byte == 0) {
143 return Err(DidError::InvalidPeerKey);
144 }
145 Ok(SharedSecret { bytes })
146 }
147
148 pub fn sign(&self, kid: &str, message: &[u8]) -> DidResult<Vec<u8>> {
149 self.require_operational()?;
150 let metadata = self.managed_key_metadata(kid)?;
151 require_active(metadata)?;
152 if !matches!(
153 metadata.role,
154 KeyRole::DeviceSigning | KeyRole::RequestSigning | KeyRole::E2eeSigning
155 ) {
156 return Err(DidError::KeyRoleViolation);
157 }
158 let secret = self.load_managed_secret(metadata)?;
159 let signing_key = ed25519_signing_key(&secret)?;
160 use ed25519_dalek::Signer;
161 Ok(signing_key.sign(message).to_bytes().to_vec())
162 }
163
164 pub fn sign_device_assertion(&self, kid: &str, message: &[u8]) -> DidResult<Vec<u8>> {
165 self.require_operational()?;
166 let metadata = self.managed_key_metadata(kid)?;
167 require_active(metadata)?;
168 if metadata.role != KeyRole::DeviceSigning {
169 return Err(DidError::KeyRoleViolation);
170 }
171 self.sign_prepared(metadata, message)
172 }
173
174 pub fn sign_object_proof(
175 &self,
176 kid: &str,
177 document: &serde_json::Value,
178 issuer_did: &str,
179 created: Option<String>,
180 ) -> DidResult<serde_json::Value> {
181 let metadata = self.device_signing_metadata(kid)?;
182 let method = find_verification_method(self.document(), &metadata.kid)
183 .ok_or(DidError::InvalidIdentity)?;
184 let public = anp::authentication::extract_public_key(&method)
185 .map_err(|_| DidError::InvalidPublicKey)?;
186 let prepared = prepare_object_proof(document, &public, &metadata.kid, issuer_did, created)
187 .map_err(|_| DidError::Crypto)?;
188 let signature = self.sign_prepared(metadata, prepared.signing_input())?;
189 complete_object_proof(prepared, &signature).map_err(|_| DidError::Crypto)
190 }
191
192 pub fn sign_document_proof(
193 &self,
194 document: &serde_json::Value,
195 verification_method: &str,
196 options: ProofGenerationOptions,
197 ) -> DidResult<serde_json::Value> {
198 self.require_operational()?;
199 if self.root_capability() != crate::RootCapabilityState::Active {
200 return Err(DidError::RootCapabilityUnavailable);
201 }
202 let metadata = self.managed_key_metadata(verification_method)?;
203 require_active(metadata)?;
204 if metadata.role != KeyRole::RootControl {
205 return Err(DidError::KeyRoleViolation);
206 }
207 let method = find_verification_method(self.document(), &metadata.kid)
208 .ok_or(DidError::InvalidIdentity)?;
209 let public = anp::authentication::extract_public_key(&method)
210 .map_err(|_| DidError::InvalidPublicKey)?;
211 let prepared = prepare_w3c_proof(document, &public, &metadata.kid, options)
212 .map_err(|_| DidError::Crypto)?;
213 let signature = self.sign_prepared(metadata, prepared.signing_input())?;
214 complete_w3c_proof(prepared, &signature).map_err(|_| DidError::Crypto)
215 }
216
217 pub fn sign_origin_proof(
218 &self,
219 method: &str,
220 meta: &serde_json::Value,
221 body: &serde_json::Value,
222 kid: &str,
223 options: Rfc9421OriginProofGenerationOptions,
224 ) -> DidResult<Rfc9421OriginProof> {
225 let metadata = self.request_signing_metadata(kid)?;
226 let verification_method = find_verification_method(self.document(), &metadata.kid)
227 .ok_or(DidError::InvalidIdentity)?;
228 let public = anp::authentication::extract_public_key(&verification_method)
229 .map_err(|_| DidError::InvalidPublicKey)?;
230 let prepared =
231 prepare_rfc9421_origin_proof(method, meta, body, &public, &metadata.kid, options)
232 .map_err(|_| DidError::Crypto)?;
233 let signature = self.sign_prepared(metadata, prepared.signing_input())?;
234 complete_rfc9421_origin_proof(prepared, &signature).map_err(|_| DidError::Crypto)
235 }
236
237 pub fn verify(&self, kid: &str, message: &[u8], signature: &[u8]) -> DidResult<()> {
238 let metadata = self.key_metadata(kid)?;
239 if metadata.state == KeyState::Revoked {
240 return Err(DidError::KeyNotUsable);
241 }
242 if metadata.role == KeyRole::E2eeAgreement {
243 return Err(DidError::KeyRoleViolation);
244 }
245 let method = find_verification_method(self.document(), &metadata.kid)
246 .ok_or(DidError::InvalidIdentity)?;
247 let public_key = anp::authentication::extract_public_key(&method)
248 .map_err(|_| DidError::InvalidPublicKey)?;
249 public_key
250 .verify_message(message, signature)
251 .map_err(|_| DidError::VerificationFailed)
252 }
253
254 pub fn ecdh(&self, kid: &str, peer_public: &[u8]) -> DidResult<SharedSecret> {
255 self.require_operational()?;
256 let metadata = self.managed_key_metadata(kid)?;
257 require_active(metadata)?;
258 if metadata.role != KeyRole::E2eeAgreement {
259 return Err(DidError::KeyRoleViolation);
260 }
261 let peer: [u8; 32] = peer_public
262 .try_into()
263 .map_err(|_| DidError::InvalidPeerKey)?;
264 let secret = self.load_managed_secret(metadata)?;
265 let private = x25519_private_key(&secret)?;
266 let shared = private.diffie_hellman(&x25519_dalek::PublicKey::from(peer));
267 let bytes = Zeroizing::new(shared.to_bytes());
268 if bytes.iter().all(|byte| *byte == 0) {
269 return Err(DidError::InvalidPeerKey);
270 }
271 Ok(SharedSecret { bytes })
272 }
273
274 pub fn legacy_did_wba_header(
275 &self,
276 kid: &str,
277 service_domain: &str,
278 version: &str,
279 ) -> DidResult<String> {
280 let metadata = self.request_signing_metadata(kid)?;
281 let prepared = prepare_legacy_did_wba_auth_header(
282 self.document(),
283 service_domain,
284 version,
285 &metadata.kid,
286 )
287 .map_err(|_| DidError::InvalidIdentity)?;
288 let signature = self.sign_prepared(metadata, prepared.signing_input())?;
289 complete_legacy_did_wba_auth_header(prepared, &signature).map_err(|_| DidError::Crypto)
290 }
291
292 pub fn http_signature_headers(
293 &self,
294 kid: &str,
295 request_url: &str,
296 request_method: &str,
297 headers: Option<&BTreeMap<String, String>>,
298 body: Option<&[u8]>,
299 ) -> DidResult<BTreeMap<String, String>> {
300 self.http_signature_headers_with_options(
301 kid,
302 request_url,
303 request_method,
304 headers,
305 body,
306 crate::HttpSignatureOptions::default(),
307 )
308 }
309
310 pub fn http_signature_headers_with_options(
311 &self,
312 kid: &str,
313 request_url: &str,
314 request_method: &str,
315 headers: Option<&BTreeMap<String, String>>,
316 body: Option<&[u8]>,
317 mut options: crate::HttpSignatureOptions,
318 ) -> DidResult<BTreeMap<String, String>> {
319 let metadata = self.request_signing_metadata(kid)?;
320 options.keyid = Some(metadata.kid.clone());
321 let prepared = prepare_http_signature_headers(
322 self.document(),
323 request_url,
324 request_method,
325 headers,
326 body,
327 options,
328 )
329 .map_err(|_| DidError::InvalidIdentity)?;
330 let signature = self.sign_prepared(metadata, prepared.signing_input())?;
331 complete_http_signature_headers(prepared, &signature).map_err(|_| DidError::Crypto)
332 }
333
334 fn request_signing_metadata(&self, kid: &str) -> DidResult<&KeyMetadata> {
335 self.require_operational()?;
336 let metadata = self.managed_key_metadata(kid)?;
337 require_active(metadata)?;
338 if !matches!(
339 metadata.role,
340 KeyRole::DeviceSigning | KeyRole::RequestSigning
341 ) {
342 return Err(DidError::KeyRoleViolation);
343 }
344 Ok(metadata)
345 }
346
347 fn device_signing_metadata(&self, kid: &str) -> DidResult<&KeyMetadata> {
348 self.require_operational()?;
349 let metadata = self.managed_key_metadata(kid)?;
350 require_active(metadata)?;
351 if metadata.role != KeyRole::DeviceSigning {
352 return Err(DidError::KeyRoleViolation);
353 }
354 Ok(metadata)
355 }
356
357 fn sign_prepared(&self, metadata: &KeyMetadata, message: &[u8]) -> DidResult<Vec<u8>> {
358 let secret = self.load_managed_secret(metadata)?;
359 let signing_key = ed25519_signing_key(&secret)?;
360 use ed25519_dalek::Signer;
361 Ok(signing_key.sign(message).to_bytes().to_vec())
362 }
363
364 fn require_operational(&self) -> DidResult<()> {
365 let current = crate::registry::read_identity(self.runtime().root(), self.identity_id())?;
366 if current.generation != self.record().generation {
367 return Err(DidError::Conflict);
368 }
369 if current.state != crate::IdentityState::Active {
370 return Err(DidError::KeyNotUsable);
371 }
372 Ok(())
373 }
374
375 fn current_pending_enrollment(
376 &self,
377 enrollment_id: &str,
378 ) -> DidResult<crate::registry::IdentityRecord> {
379 let current = crate::registry::read_identity(self.runtime().root(), self.identity_id())?;
380 if current.generation != self.record().generation {
381 return Err(DidError::Conflict);
382 }
383 if current.state != crate::IdentityState::Enrolling
384 || current
385 .pending_enrollment
386 .as_ref()
387 .is_none_or(|pending| pending.enrollment_id != enrollment_id)
388 {
389 return Err(DidError::KeyNotUsable);
390 }
391 Ok(current)
392 }
393}
394
395fn require_active(metadata: &KeyMetadata) -> DidResult<()> {
396 if metadata.state != KeyState::Active {
397 return Err(DidError::KeyNotUsable);
398 }
399 Ok(())
400}
401
402fn ed25519_signing_key(secret: &SecretBytes) -> DidResult<ed25519_dalek::SigningKey> {
403 if secret.expose().len() != 32 {
404 return Err(DidError::InvalidIdentity);
405 }
406 let mut bytes = Zeroizing::new([0_u8; 32]);
407 bytes.copy_from_slice(secret.expose());
408 Ok(ed25519_dalek::SigningKey::from_bytes(&bytes))
409}
410
411fn x25519_private_key(secret: &SecretBytes) -> DidResult<x25519_dalek::StaticSecret> {
412 if secret.expose().len() != 32 {
413 return Err(DidError::InvalidIdentity);
414 }
415 let mut bytes = [0_u8; 32];
416 bytes.copy_from_slice(secret.expose());
417 let private = x25519_dalek::StaticSecret::from(bytes);
418 bytes.zeroize();
419 Ok(private)
420}
421
422#[cfg(test)]
423mod tests {
424 use anp::authentication::{verify_auth_header_signature, verify_http_message_signature};
425
426 use super::*;
427 use crate::{Capabilities, DidCreateSpec, DidProfile, DidStore, ManagedKeySpec};
428
429 #[test]
430 fn crypto_ops_enforce_roles_and_interoperate_with_anp_verifiers() {
431 let alice_root = tempfile::tempdir().unwrap();
432 let bob_root = tempfile::tempdir().unwrap();
433 let mut alice_store =
434 DidStore::initialize_injected(alice_root.path(), "host", [21_u8; 32]).unwrap();
435 let mut bob_store =
436 DidStore::initialize_injected(bob_root.path(), "host", [22_u8; 32]).unwrap();
437 let alice = alice_store.create_identity(spec("alice")).unwrap();
438 let bob = bob_store.create_identity(spec("bob")).unwrap();
439 let message = b"message-to-sign";
440
441 let signature = alice.sign("#request", message).unwrap();
442 alice.verify("#request", message, &signature).unwrap();
443 let device_signature = alice.sign_device_assertion("#device", message).unwrap();
444 alice.verify("#device", message, &device_signature).unwrap();
445 assert_eq!(
446 alice.sign_device_assertion("#request", message),
447 Err(DidError::KeyRoleViolation)
448 );
449 assert_eq!(
450 alice.sign_device_assertion("#e2ee-signing", message),
451 Err(DidError::KeyRoleViolation)
452 );
453 let object = serde_json::json!({"operation": "device.assertion"});
454 let signed_object = alice
455 .sign_object_proof("#device", &object, alice.did(), None)
456 .unwrap();
457 anp::proof::verify_object_proof(&signed_object, alice.did(), alice.document()).unwrap();
458 assert_eq!(
459 alice.sign_object_proof("#request", &object, alice.did(), None),
460 Err(DidError::KeyRoleViolation)
461 );
462 let mut unsigned_document = alice.document().clone();
463 unsigned_document.as_object_mut().unwrap().remove("proof");
464 let signed_document = alice
465 .sign_document_proof(
466 &unsigned_document,
467 &format!("{}#root", alice.did()),
468 ProofGenerationOptions {
469 proof_purpose: Some("assertionMethod".to_string()),
470 proof_type: Some(anp::proof::PROOF_TYPE_DATA_INTEGRITY.to_string()),
471 cryptosuite: Some(anp::proof::CRYPTOSUITE_EDDSA_JCS_2022.to_string()),
472 domain: Some("example.com".to_string()),
473 ..ProofGenerationOptions::default()
474 },
475 )
476 .unwrap();
477 assert!(anp::authentication::validate_did_document_binding(
478 &signed_document,
479 true
480 ));
481 assert_eq!(
482 alice.verify("#request", b"tampered", &signature),
483 Err(DidError::VerificationFailed)
484 );
485 assert_eq!(
486 alice.sign("#root", message),
487 Err(DidError::KeyRoleViolation)
488 );
489 assert_eq!(
490 alice.sign("#agreement", message),
491 Err(DidError::KeyRoleViolation)
492 );
493 assert_eq!(
494 alice.sign(&format!("{}#request", bob.did()), message),
495 Err(DidError::ForeignKid)
496 );
497
498 let alice_agreement = public_x25519(&alice, "#agreement");
499 let bob_agreement = public_x25519(&bob, "#agreement");
500 assert_eq!(
501 alice.public_key_bytes("#agreement").unwrap(),
502 alice_agreement
503 );
504 assert_eq!(
505 alice.public_key_bytes("#missing"),
506 Err(DidError::KeyNotFound)
507 );
508 let alice_shared = alice.ecdh("#agreement", &bob_agreement).unwrap();
509 let bob_shared = bob.ecdh("#agreement", &alice_agreement).unwrap();
510 assert_eq!(alice_shared.as_bytes(), bob_shared.as_bytes());
511 assert_eq!(
512 alice.ecdh("#agreement", &[0_u8; 31]).err(),
513 Some(DidError::InvalidPeerKey)
514 );
515 assert_eq!(
516 alice.ecdh("#agreement", &[0_u8; 32]).err(),
517 Some(DidError::InvalidPeerKey)
518 );
519 let mut low_order = [0_u8; 32];
520 low_order[0] = 1;
521 assert_eq!(
522 alice.ecdh("#agreement", &low_order).err(),
523 Some(DidError::InvalidPeerKey)
524 );
525
526 let legacy = alice
527 .legacy_did_wba_header("#request", "api.example.com", "1.1")
528 .unwrap();
529 verify_auth_header_signature(&legacy, alice.document(), "api.example.com").unwrap();
530 let http = alice
531 .http_signature_headers(
532 "#request",
533 "https://api.example.com/messages",
534 "POST",
535 None,
536 Some(br#"{"message":"hello"}"#),
537 )
538 .unwrap();
539 verify_http_message_signature(
540 alice.document(),
541 "POST",
542 "https://api.example.com/messages",
543 &http,
544 Some(br#"{"message":"hello"}"#),
545 )
546 .unwrap();
547 let challenged = alice
548 .http_signature_headers_with_options(
549 "#device",
550 "https://api.example.com/messages",
551 "POST",
552 None,
553 Some(br#"{"message":"hello"}"#),
554 crate::HttpSignatureOptions {
555 keyid: Some("did:example:ignored#foreign".to_string()),
556 nonce: Some("challenge-123".to_string()),
557 created: Some(1_700_000_000),
558 expires: Some(1_700_000_300),
559 covered_components: Some(vec![
560 "@method".to_string(),
561 "@target-uri".to_string(),
562 "content-digest".to_string(),
563 ]),
564 },
565 )
566 .unwrap();
567 let signature_input = challenged.get("Signature-Input").unwrap();
568 assert!(signature_input.contains("nonce=\"challenge-123\""));
569 assert!(signature_input.contains("created=1700000000"));
570 assert!(signature_input.contains("expires=1700000300"));
571 assert!(signature_input.contains(&format!("keyid=\"{}#device\"", alice.did())));
572 verify_http_message_signature(
573 alice.document(),
574 "POST",
575 "https://api.example.com/messages",
576 &challenged,
577 Some(br#"{"message":"hello"}"#),
578 )
579 .unwrap();
580 }
581
582 fn spec(name: &str) -> DidCreateSpec {
583 DidCreateSpec {
584 profile: DidProfile::E1,
585 domain: "example.com".to_string(),
586 port: None,
587 path_segments: vec!["agents".to_string(), name.to_string()],
588 capabilities: Capabilities { did_wba: true },
589 managed_keys: vec![
590 managed("root", KeyRole::RootControl),
591 managed("device", KeyRole::DeviceSigning),
592 managed("request", KeyRole::RequestSigning),
593 managed("e2ee-signing", KeyRole::E2eeSigning),
594 managed("agreement", KeyRole::E2eeAgreement),
595 ],
596 external_keys: Vec::new(),
597 services: Vec::new(),
598 agent_description_url: None,
599 extensions: Vec::new(),
600 }
601 }
602
603 fn managed(fragment: &str, role: KeyRole) -> ManagedKeySpec {
604 ManagedKeySpec {
605 fragment: fragment.to_string(),
606 role,
607 }
608 }
609
610 fn public_x25519(identity: &DidIdentity, kid: &str) -> [u8; 32] {
611 let kid = identity.key_metadata(kid).unwrap().kid.clone();
612 let method = find_verification_method(identity.document(), &kid).unwrap();
613 match anp::authentication::extract_public_key(&method).unwrap() {
614 anp::PublicKeyMaterial::X25519(bytes) => bytes,
615 _ => panic!("expected X25519 public key"),
616 }
617 }
618}