1use fips204::ml_dsa_65;
13use fips204::traits::{SerDes, Signer, Verifier};
14use serde::{Deserialize, Serialize};
15use serde_bytes;
16use sha3::{Digest, Sha3_512};
17use std::collections::HashMap;
18use std::sync::{Arc, Mutex};
19
20pub const ML_DSA_SECURITY_LEVEL: usize = 192; pub const ML_DSA_PRIVATE_KEY_SIZE: usize = ml_dsa_65::SK_LEN; pub const ML_DSA_PUBLIC_KEY_SIZE: usize = ml_dsa_65::PK_LEN; pub const ML_DSA_SIGNATURE_SIZE: usize = ml_dsa_65::SIG_LEN; pub struct MlDsaSigner {
28 private_key: MlDsaPrivateKey,
29 public_key: MlDsaPublicKey,
30 key_id: Option<String>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct MlDsaPrivateKey {
36 #[serde(with = "serde_bytes")]
37 pub sk_bytes: Vec<u8>,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct MlDsaPublicKey {
43 #[serde(with = "serde_bytes")]
44 pub pk_bytes: Vec<u8>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct MlDsaSignature {
50 #[serde(with = "serde_bytes")]
51 pub sig_bytes: Vec<u8>,
52}
53
54pub struct MlDsaKeyManager {
56 keys: HashMap<String, Arc<Mutex<MlDsaSigner>>>,
57 default_key: Option<String>,
58 key_rotation_policy: KeyRotationPolicy,
59}
60
61#[derive(Debug, Clone)]
63pub struct KeyRotationPolicy {
64 pub rotation_interval: u64, pub max_signatures: u64,
66 pub quantum_resistance_threshold: f64,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct CryptoContext {
72 pub domain: String,
73 pub purpose: String,
74 pub timestamp: u64,
75 pub nonce: [u8; 32],
76}
77
78pub struct FiduciaryCrypto {
80 key_manager: Arc<Mutex<MlDsaKeyManager>>,
81 context_manager: ContextManager,
82 compliance_checker: ComplianceChecker,
83}
84
85pub struct ContextManager {
87 active_contexts: HashMap<String, CryptoContext>,
88 context_cache: Vec<CryptoContext>,
89 max_cache_size: usize,
90}
91
92pub struct ComplianceChecker {
94 quantum_resistance_threshold: f64,
95 fiduciary_standards: FiduciaryStandards,
96 audit_log: Vec<AuditEntry>,
97}
98
99#[derive(Debug, Clone)]
101pub struct FiduciaryStandards {
102 pub min_security_level: usize,
103 pub quantum_resistance_required: bool,
104 pub audit_trail_required: bool,
105 pub key_escrow_required: bool,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct AuditEntry {
111 pub timestamp: u64,
112 pub operation: String,
113 pub key_id: Option<String>,
114 pub context: Option<String>,
115 pub success: bool,
116 pub details: String,
117}
118
119impl MlDsaSigner {
120 pub fn generate_keypair() -> Result<(MlDsaPrivateKey, MlDsaPublicKey), MlDsaError> {
122 let (pk, sk) =
123 ml_dsa_65::try_keygen().map_err(|e| MlDsaError::KeyGenerationFailed(e.to_string()))?;
124 let private_key = MlDsaPrivateKey {
125 sk_bytes: sk.into_bytes().to_vec(),
126 };
127 let public_key = MlDsaPublicKey {
128 pk_bytes: pk.into_bytes().to_vec(),
129 };
130 Ok((private_key, public_key))
131 }
132
133 pub fn from_keypair(private_key: MlDsaPrivateKey, public_key: MlDsaPublicKey) -> Self {
135 Self {
136 private_key,
137 public_key,
138 key_id: None,
139 }
140 }
141
142 pub fn sign(
148 &self,
149 message: &[u8],
150 context: &CryptoContext,
151 ) -> Result<MlDsaSignature, MlDsaError> {
152 Self::sign_with_secret(&self.private_key.sk_bytes, message, context)
153 }
154
155 pub fn verify(
157 &self,
158 message: &[u8],
159 signature: &MlDsaSignature,
160 context: &CryptoContext,
161 ) -> Result<bool, MlDsaError> {
162 Self::verify_with_public(&self.public_key.pk_bytes, message, signature, context)
163 }
164
165 pub fn public_key(&self) -> &MlDsaPublicKey {
167 &self.public_key
168 }
169
170 pub fn key_id(&self) -> Option<&str> {
172 self.key_id.as_deref()
173 }
174
175 pub fn set_key_id(&mut self, key_id: String) {
177 self.key_id = Some(key_id);
178 }
179
180 fn derive_ctx(context: &CryptoContext) -> Vec<u8> {
187 let mut hasher = Sha3_512::new();
188 hasher.update(context.domain.as_bytes());
189 hasher.update(context.purpose.as_bytes());
190 hasher.update(&context.timestamp.to_be_bytes());
191 hasher.update(&context.nonce);
192 hasher.finalize().to_vec() }
194
195 pub fn sign_with_secret(
197 sk_bytes: &[u8],
198 message: &[u8],
199 context: &CryptoContext,
200 ) -> Result<MlDsaSignature, MlDsaError> {
201 let sk_arr: [u8; ml_dsa_65::SK_LEN] = sk_bytes.try_into().map_err(|_| {
202 MlDsaError::SignatureGenerationFailed(format!(
203 "secret key must be {} bytes",
204 ml_dsa_65::SK_LEN
205 ))
206 })?;
207 let sk = ml_dsa_65::PrivateKey::try_from_bytes(sk_arr)
208 .map_err(|e| MlDsaError::SignatureGenerationFailed(e.to_string()))?;
209 let ctx = Self::derive_ctx(context);
210 let sig = sk
211 .try_sign(message, &ctx)
212 .map_err(|e| MlDsaError::SignatureGenerationFailed(e.to_string()))?;
213 Ok(MlDsaSignature {
214 sig_bytes: sig.to_vec(),
215 })
216 }
217
218 pub fn verify_with_public(
220 pk_bytes: &[u8],
221 message: &[u8],
222 signature: &MlDsaSignature,
223 context: &CryptoContext,
224 ) -> Result<bool, MlDsaError> {
225 let pk_arr: [u8; ml_dsa_65::PK_LEN] = pk_bytes.try_into().map_err(|_| {
226 MlDsaError::SignatureVerificationFailed(format!(
227 "public key must be {} bytes",
228 ml_dsa_65::PK_LEN
229 ))
230 })?;
231 let pk = ml_dsa_65::PublicKey::try_from_bytes(pk_arr)
232 .map_err(|e| MlDsaError::SignatureVerificationFailed(e.to_string()))?;
233 let sig_arr: [u8; ml_dsa_65::SIG_LEN] =
234 signature.sig_bytes.as_slice().try_into().map_err(|_| {
235 MlDsaError::SignatureVerificationFailed(format!(
236 "signature must be {} bytes",
237 ml_dsa_65::SIG_LEN
238 ))
239 })?;
240 let ctx = Self::derive_ctx(context);
241 Ok(pk.verify(message, &sig_arr, &ctx))
242 }
243
244 fn secure_random(buf: &mut [u8]) -> Result<(), MlDsaError> {
246 let mut offset = 0;
247 while offset + 32 <= buf.len() {
248 let chunk: [u8; 32] = rand::random();
249 buf[offset..offset + 32].copy_from_slice(&chunk);
250 offset += 32;
251 }
252 if offset < buf.len() {
253 let remaining = buf.len() - offset;
254 let tail: [u8; 32] = rand::random();
255 buf[offset..].copy_from_slice(&tail[..remaining]);
256 }
257 Ok(())
258 }
259}
260
261impl MlDsaKeyManager {
262 pub fn new() -> Self {
264 Self {
265 keys: HashMap::new(),
266 default_key: None,
267 key_rotation_policy: KeyRotationPolicy {
268 rotation_interval: 86400 * 30, max_signatures: 1000000,
270 quantum_resistance_threshold: 0.95,
271 },
272 }
273 }
274
275 pub fn generate_key(&mut self, key_id: String) -> Result<(), MlDsaError> {
277 let (private_key, public_key) = MlDsaSigner::generate_keypair()?;
278 let mut signer = MlDsaSigner::from_keypair(private_key, public_key);
279 signer.set_key_id(key_id.clone());
280
281 let signer_arc = Arc::new(Mutex::new(signer));
282 self.keys.insert(key_id.clone(), signer_arc);
283
284 if self.default_key.is_none() {
286 self.default_key = Some(key_id);
287 }
288
289 Ok(())
290 }
291
292 pub fn get_signer(&self, key_id: &str) -> Option<Arc<Mutex<MlDsaSigner>>> {
294 self.keys.get(key_id).cloned()
295 }
296
297 pub fn get_default_signer(&self) -> Option<Arc<Mutex<MlDsaSigner>>> {
299 self.default_key
300 .as_ref()
301 .and_then(|key_id| self.get_signer(key_id))
302 }
303
304 pub fn list_keys(&self) -> Vec<String> {
306 self.keys.keys().cloned().collect()
307 }
308
309 pub fn remove_key(&mut self, key_id: &str) -> Result<(), MlDsaError> {
311 self.keys.remove(key_id);
312
313 if self.default_key.as_ref() == Some(&key_id.to_string()) {
315 self.default_key = self.keys.keys().next().cloned();
316 }
317
318 Ok(())
319 }
320
321 pub fn should_rotate_key(
325 &self,
326 _key_id: &str,
327 signature_count: u64,
328 key_age_seconds: u64,
329 ) -> bool {
330 signature_count >= self.key_rotation_policy.max_signatures
331 || key_age_seconds >= self.key_rotation_policy.rotation_interval
332 }
333
334 pub fn rotation_policy(&self) -> &KeyRotationPolicy {
336 &self.key_rotation_policy
337 }
338}
339
340impl ContextManager {
341 pub fn new() -> Self {
343 Self {
344 active_contexts: HashMap::new(),
345 context_cache: Vec::new(),
346 max_cache_size: 1000,
347 }
348 }
349
350 pub fn create_context(
352 &mut self,
353 domain: String,
354 purpose: String,
355 ) -> Result<CryptoContext, MlDsaError> {
356 let context = CryptoContext {
357 domain,
358 purpose,
359 timestamp: std::time::SystemTime::now()
360 .duration_since(std::time::UNIX_EPOCH)
361 .unwrap()
362 .as_secs(),
363 nonce: Self::generate_nonce(),
364 };
365
366 self.context_cache.push(context.clone());
368
369 if self.context_cache.len() > self.max_cache_size {
371 self.context_cache.remove(0);
372 }
373
374 Ok(context)
375 }
376
377 pub fn get_context(&self, context_id: &str) -> Option<&CryptoContext> {
379 self.active_contexts.get(context_id)
380 }
381
382 fn generate_nonce() -> [u8; 32] {
384 let mut nonce = [0u8; 32];
385 MlDsaSigner::secure_random(&mut nonce).unwrap_or(());
386 nonce
387 }
388}
389
390impl ComplianceChecker {
391 pub fn new() -> Self {
393 Self {
394 quantum_resistance_threshold: 0.95,
395 fiduciary_standards: FiduciaryStandards {
396 min_security_level: 128,
397 quantum_resistance_required: true,
398 audit_trail_required: true,
399 key_escrow_required: false,
400 },
401 audit_log: Vec::new(),
402 }
403 }
404
405 pub fn check_compliance(
407 &mut self,
408 operation: &str,
409 key_id: Option<&str>,
410 ) -> Result<bool, MlDsaError> {
411 let timestamp = std::time::SystemTime::now()
412 .duration_since(std::time::UNIX_EPOCH)
413 .unwrap()
414 .as_secs();
415
416 let entry = AuditEntry {
417 timestamp,
418 operation: operation.to_string(),
419 key_id: key_id.map(|s| s.to_string()),
420 context: None,
421 success: true,
422 details: "Compliance check passed".to_string(),
423 };
424
425 self.audit_log.push(entry);
426
427 Ok(true)
428 }
429
430 pub fn get_audit_log(&self) -> &[AuditEntry] {
432 &self.audit_log
433 }
434
435 pub fn clear_audit_log(&mut self) {
437 self.audit_log.clear();
438 }
439
440 pub fn check_quantum_readiness(&self) -> bool {
443 let ml_dsa_security = ML_DSA_SECURITY_LEVEL as f64 / 256.0;
444 ml_dsa_security >= self.quantum_resistance_threshold
445 && self.fiduciary_standards.quantum_resistance_required
446 }
447
448 pub fn fiduciary_standards(&self) -> &FiduciaryStandards {
450 &self.fiduciary_standards
451 }
452
453 pub fn quantum_resistance_threshold(&self) -> f64 {
455 self.quantum_resistance_threshold
456 }
457}
458
459impl FiduciaryCrypto {
460 pub fn new() -> Self {
462 Self {
463 key_manager: Arc::new(Mutex::new(MlDsaKeyManager::new())),
464 context_manager: ContextManager::new(),
465 compliance_checker: ComplianceChecker::new(),
466 }
467 }
468
469 pub fn generate_key(&mut self, key_id: String) -> Result<(), MlDsaError> {
471 let mut key_manager = self.key_manager.lock().unwrap();
472 key_manager.generate_key(key_id)
473 }
474
475 pub fn sign(
481 &self,
482 message: &[u8],
483 key_id: Option<&str>,
484 domain: String,
485 purpose: String,
486 ) -> Result<MlDsaSignature, MlDsaError> {
487 let key_manager = self.key_manager.lock().unwrap();
488 let signer_arc = if let Some(kid) = key_id {
489 key_manager
490 .get_signer(kid)
491 .ok_or_else(|| MlDsaError::KeyNotFound(kid.to_string()))?
492 } else {
493 key_manager
494 .get_default_signer()
495 .ok_or_else(|| MlDsaError::NoDefaultKey)?
496 };
497 let signer = signer_arc.lock().unwrap();
498
499 let context = CryptoContext {
500 domain,
501 purpose,
502 timestamp: 0,
503 nonce: [0u8; 32],
504 };
505
506 signer.sign(message, &context)
507 }
508
509 pub fn verify(
511 &self,
512 message: &[u8],
513 signature: &MlDsaSignature,
514 key_id: Option<&str>,
515 domain: String,
516 purpose: String,
517 ) -> Result<bool, MlDsaError> {
518 let key_manager = self.key_manager.lock().unwrap();
519 let signer_arc = if let Some(kid) = key_id {
520 key_manager
521 .get_signer(kid)
522 .ok_or_else(|| MlDsaError::KeyNotFound(kid.to_string()))?
523 } else {
524 key_manager
525 .get_default_signer()
526 .ok_or_else(|| MlDsaError::NoDefaultKey)?
527 };
528 let signer = signer_arc.lock().unwrap();
529
530 let context = CryptoContext {
531 domain,
532 purpose,
533 timestamp: 0,
534 nonce: [0u8; 32],
535 };
536
537 signer.verify(message, signature, &context)
538 }
539
540 pub fn hash_token(&self, token: &[u8]) -> Result<[u8; 32], MlDsaError> {
542 let mut hasher = Sha3_512::new();
543 hasher.update(token);
544 let digest = hasher.finalize();
545 let mut out = [0u8; 32];
546 out.copy_from_slice(&digest[..32]);
547 Ok(out)
548 }
549
550 pub fn list_keys(&self) -> Vec<String> {
552 let key_manager = self.key_manager.lock().unwrap();
553 key_manager.list_keys()
554 }
555
556 pub fn get_audit_log(&self) -> Vec<AuditEntry> {
558 let compliance_checker = &self.compliance_checker;
559 compliance_checker.get_audit_log().to_vec()
560 }
561
562 pub fn sign_with_managed_context(
566 &mut self,
567 message: &[u8],
568 key_id: Option<&str>,
569 domain: String,
570 purpose: String,
571 ) -> Result<(MlDsaSignature, CryptoContext), MlDsaError> {
572 let context = self.context_manager.create_context(domain, purpose)?;
573 let key_manager = self.key_manager.lock().unwrap();
574 let signer_arc = if let Some(kid) = key_id {
575 key_manager
576 .get_signer(kid)
577 .ok_or_else(|| MlDsaError::KeyNotFound(kid.to_string()))?
578 } else {
579 key_manager
580 .get_default_signer()
581 .ok_or_else(|| MlDsaError::NoDefaultKey)?
582 };
583 let signer = signer_arc.lock().unwrap();
584 let sig = signer.sign(message, &context)?;
585 Ok((sig, context))
586 }
587
588 pub fn should_rotate_key(
590 &self,
591 key_id: &str,
592 signature_count: u64,
593 key_age_seconds: u64,
594 ) -> bool {
595 let key_manager = self.key_manager.lock().unwrap();
596 key_manager.should_rotate_key(key_id, signature_count, key_age_seconds)
597 }
598
599 pub fn check_quantum_readiness(&self) -> bool {
601 self.compliance_checker.check_quantum_readiness()
602 }
603
604 pub fn context_manager(&self) -> &ContextManager {
606 &self.context_manager
607 }
608}
609
610#[derive(Debug, Clone)]
612pub enum MlDsaError {
613 KeyGenerationFailed(String),
614 KeyNotFound(String),
615 NoDefaultKey,
616 SignatureGenerationFailed(String),
617 SignatureVerificationFailed(String),
618 InvalidContext(String),
619 ComplianceError(String),
620 RandomGenerationError(String),
621}
622
623impl std::fmt::Display for MlDsaError {
624 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
625 match self {
626 MlDsaError::KeyGenerationFailed(msg) => write!(f, "Key generation failed: {}", msg),
627 MlDsaError::KeyNotFound(msg) => write!(f, "Key not found: {}", msg),
628 MlDsaError::NoDefaultKey => write!(f, "No default key available"),
629 MlDsaError::SignatureGenerationFailed(msg) => {
630 write!(f, "Signature generation failed: {}", msg)
631 }
632 MlDsaError::SignatureVerificationFailed(msg) => {
633 write!(f, "Signature verification failed: {}", msg)
634 }
635 MlDsaError::InvalidContext(msg) => write!(f, "Invalid context: {}", msg),
636 MlDsaError::ComplianceError(msg) => write!(f, "Compliance error: {}", msg),
637 MlDsaError::RandomGenerationError(msg) => write!(f, "Random generation error: {}", msg),
638 }
639 }
640}
641
642impl std::error::Error for MlDsaError {}
643
644const P_VC_PROOF_MLDSA: u64 = crate::q_hash("vc:proof/mldsa");
648const P_VC_PROOF_MLDSA_FRAG: u64 = crate::q_hash("vc:proof/mldsa/frag");
650
651#[derive(Debug, Clone)]
654pub struct MlDsaVcProof {
655 pub head_quin: crate::NQuin,
656 pub fragment_quins: Vec<crate::NQuin>,
657}
658
659impl MlDsaVcProof {
660 pub fn issue_vc_mldsa(
663 claim_quins: &[crate::NQuin],
664 issuer_sk: &[u8],
665 issuer_did_hash: u64,
666 context: &CryptoContext,
667 ) -> Result<Self, MlDsaError> {
668 let claim_bytes = Self::serialize_claims(claim_quins);
670
671 let signature = MlDsaSigner::sign_with_secret(issuer_sk, &claim_bytes, context)?;
673
674 let sig_bytes = signature.sig_bytes;
676 let total_len = sig_bytes.len();
677 let fragment_count = (total_len + 7) / 8; let mut fragment_quins = Vec::with_capacity(fragment_count);
680
681 for i in 0..fragment_count {
682 let start = i * 8;
683 let end = (start + 8).min(total_len);
684 let chunk = &sig_bytes[start..end];
685
686 let mut object: u64 = 0;
688 for (j, &byte) in chunk.iter().enumerate() {
689 object |= (byte as u64) << (j * 8);
690 }
691
692 let metadata = (i as u64) << 32 | (fragment_count as u64);
693 let parity = crate::NQuin::calculate_parity(
694 issuer_did_hash,
695 P_VC_PROOF_MLDSA_FRAG,
696 object,
697 issuer_did_hash,
698 metadata,
699 );
700 let fragment = crate::NQuin {
701 subject: issuer_did_hash,
702 predicate: P_VC_PROOF_MLDSA_FRAG,
703 object,
704 context: issuer_did_hash,
705 metadata,
706 parity,
707 };
708
709 fragment_quins.push(fragment);
710 }
711
712 let head_object = ((total_len as u64) << 32) | (fragment_count as u64);
713 let head_metadata = std::time::SystemTime::now()
714 .duration_since(std::time::UNIX_EPOCH)
715 .unwrap()
716 .as_secs();
717 let head_parity = crate::NQuin::calculate_parity(
718 issuer_did_hash,
719 P_VC_PROOF_MLDSA,
720 head_object,
721 issuer_did_hash,
722 head_metadata,
723 );
724 let head = crate::NQuin {
725 subject: issuer_did_hash,
726 predicate: P_VC_PROOF_MLDSA,
727 object: head_object,
728 context: issuer_did_hash,
729 metadata: head_metadata,
730 parity: head_parity,
731 };
732
733 Ok(Self {
734 head_quin: head,
735 fragment_quins,
736 })
737 }
738
739 pub fn verify_vc_mldsa(
741 &self,
742 claim_quins: &[crate::NQuin],
743 issuer_pk: &[u8],
744 context: &CryptoContext,
745 ) -> Result<bool, MlDsaError> {
746 let total_len = (self.head_quin.object >> 32) as usize;
747 let expected_fragments = (self.head_quin.object & 0xFFFF_FFFF) as usize;
748 if expected_fragments != self.fragment_quins.len() {
749 return Ok(false);
750 }
751
752 let mut ordered = self.fragment_quins.clone();
753 ordered.sort_by_key(|fragment| fragment.metadata >> 32);
754
755 let mut signature_bytes = Vec::with_capacity(total_len);
756 for fragment in &ordered {
757 let fragment_index = (fragment.metadata >> 32) as usize;
758 let fragment_count = (fragment.metadata & 0xFFFF_FFFF) as usize;
759 if fragment_count != expected_fragments || fragment_index >= expected_fragments {
760 return Ok(false);
761 }
762
763 let start = fragment_index * 8;
764 let chunk_len = 8.min(total_len.saturating_sub(start));
765 for j in 0..chunk_len {
766 let byte = ((fragment.object >> (j * 8)) & 0xFF) as u8;
767 signature_bytes.push(byte);
768 }
769 }
770
771 if signature_bytes.len() != total_len {
772 return Ok(false);
773 }
774
775 let signature = MlDsaSignature {
777 sig_bytes: signature_bytes,
778 };
779
780 let claim_bytes = Self::serialize_claims(claim_quins);
782
783 MlDsaSigner::verify_with_public(issuer_pk, &claim_bytes, &signature, context)
785 }
786
787 fn serialize_claims(claims: &[crate::NQuin]) -> Vec<u8> {
789 let mut bytes = Vec::new();
791 for quin in claims {
792 bytes.extend_from_slice(unsafe {
793 std::slice::from_raw_parts(
794 quin as *const _ as *const u8,
795 std::mem::size_of::<crate::NQuin>(),
796 )
797 });
798 }
799 bytes
800 }
801}
802
803#[cfg(feature = "interop-crypto")]
805use secp256k1::{ecdsa, Message, PublicKey, Secp256k1, SecretKey};
806
807#[cfg(feature = "interop-crypto")]
809#[derive(Debug, Clone)]
810pub struct InteropEcdsaSigner {
811 secret_key: Option<Vec<u8>>,
812 public_key: Option<Vec<u8>>,
813 key_id: Option<String>,
814}
815
816#[cfg(feature = "interop-crypto")]
817#[derive(Debug, Clone, Serialize, Deserialize)]
818pub struct InteropEcdsaSignature {
819 #[serde(with = "serde_bytes")]
820 pub sig_bytes: Vec<u8>,
821}
822
823#[cfg(feature = "interop-crypto")]
824impl InteropEcdsaSigner {
825 pub fn generate() -> Result<Self, MlDsaError> {
827 let secp = Secp256k1::new();
830 let mut rng = secp256k1::rand::rng();
831 let (secret_key, public_key) = secp.generate_keypair(&mut rng);
832
833 Ok(Self {
834 secret_key: Some(secret_key.secret_bytes().to_vec()),
835 public_key: Some(public_key.serialize().to_vec()),
836 key_id: None,
837 })
838 }
839
840 pub fn from_secret_key(sk_bytes: &[u8]) -> Result<Self, MlDsaError> {
842 let secp = Secp256k1::new();
843 let secret_key = SecretKey::from_slice(sk_bytes)
844 .map_err(|e| MlDsaError::SignatureGenerationFailed(e.to_string()))?;
845 let public_key = PublicKey::from_secret_key(&secp, &secret_key);
846
847 Ok(Self {
848 secret_key: Some(sk_bytes.to_vec()),
849 public_key: Some(public_key.serialize().to_vec()),
850 key_id: None,
851 })
852 }
853
854 pub fn from_public_key(pk_bytes: &[u8]) -> Result<Self, MlDsaError> {
856 let secp = Secp256k1::new();
857 let public_key = PublicKey::from_slice(pk_bytes)
858 .map_err(|e| MlDsaError::SignatureVerificationFailed(e.to_string()))?;
859 Ok(Self {
860 secret_key: None,
861 public_key: Some(public_key.serialize().to_vec()),
862 key_id: None,
863 })
864 }
865
866 pub fn export_secret_key(&self) -> Result<Vec<u8>, MlDsaError> {
868 self.secret_key
869 .clone()
870 .ok_or_else(|| MlDsaError::KeyGenerationFailed("No secret key available".to_string()))
871 }
872
873 pub fn sign(&self, message: &[u8]) -> Result<InteropEcdsaSignature, MlDsaError> {
875 let secp = Secp256k1::new();
876 let secret_key = self
877 .secret_key
878 .as_ref()
879 .ok_or_else(|| MlDsaError::SignatureGenerationFailed("No secret key".to_string()))?;
880 let sk = SecretKey::from_slice(secret_key)
881 .map_err(|e| MlDsaError::SignatureGenerationFailed(e.to_string()))?;
882
883 let msg = Message::from_digest_slice(message)
884 .map_err(|e| MlDsaError::SignatureGenerationFailed(e.to_string()))?;
885
886 let sig = secp.sign_ecdsa(msg, &sk);
887
888 Ok(InteropEcdsaSignature {
889 sig_bytes: sig.serialize_compact().to_vec(),
890 })
891 }
892
893 pub fn verify(
895 &self,
896 message: &[u8],
897 signature: &InteropEcdsaSignature,
898 ) -> Result<bool, MlDsaError> {
899 let secp = Secp256k1::new();
900 let public_key = self
901 .public_key
902 .as_ref()
903 .ok_or_else(|| MlDsaError::SignatureVerificationFailed("No public key".to_string()))?;
904 let pk = PublicKey::from_slice(public_key)
905 .map_err(|e| MlDsaError::SignatureVerificationFailed(e.to_string()))?;
906
907 let msg = Message::from_digest_slice(message)
908 .map_err(|e| MlDsaError::SignatureVerificationFailed(e.to_string()))?;
909
910 let sig = ecdsa::Signature::from_compact(&signature.sig_bytes)
911 .map_err(|e| MlDsaError::SignatureVerificationFailed(e.to_string()))?;
912
913 Ok(secp.verify_ecdsa(msg, &sig, &pk).is_ok())
914 }
915
916 pub fn public_key(&self) -> Option<&[u8]> {
918 self.public_key.as_deref()
919 }
920}
921
922#[cfg(test)]
923mod tests {
924 use super::*;
925
926 #[test]
927 fn test_key_generation() {
928 let (private_key, public_key) = MlDsaSigner::generate_keypair().unwrap();
929
930 assert_eq!(private_key.sk_bytes.len(), ML_DSA_PRIVATE_KEY_SIZE);
932 assert_eq!(public_key.pk_bytes.len(), ML_DSA_PUBLIC_KEY_SIZE);
933 }
934
935 #[test]
936 fn test_sign_verify_rejects_tampered_message() {
937 let (private_key, public_key) = MlDsaSigner::generate_keypair().unwrap();
938 let signer = MlDsaSigner::from_keypair(private_key, public_key);
939 let context = CryptoContext {
940 domain: "test".to_string(),
941 purpose: "auth".to_string(),
942 timestamp: 42,
943 nonce: [7u8; 32],
944 };
945 let sig = signer.sign(b"genuine message", &context).unwrap();
946 assert!(!signer.verify(b"forged message", &sig, &context).unwrap());
948 let other_ctx = CryptoContext {
950 purpose: "other".to_string(),
951 ..context.clone()
952 };
953 assert!(!signer.verify(b"genuine message", &sig, &other_ctx).unwrap());
954 }
955
956 #[test]
957 fn test_sign_verify() {
958 let (private_key, public_key) = MlDsaSigner::generate_keypair().unwrap();
959 let signer = MlDsaSigner::from_keypair(private_key, public_key);
960
961 let message = b"Hello, QualiaDB!";
962 let context = CryptoContext {
963 domain: "test".to_string(),
964 purpose: "authentication".to_string(),
965 timestamp: 1234567890,
966 nonce: [
967 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
968 24, 25, 26, 27, 28, 29, 30, 31, 32,
969 ],
970 };
971
972 let signature = signer.sign(message, &context).unwrap();
973 let is_valid = signer.verify(message, &signature, &context).unwrap();
974
975 assert!(is_valid);
976 }
977
978 #[test]
979 fn test_key_manager() {
980 let mut key_manager = MlDsaKeyManager::new();
981
982 key_manager.generate_key("test_key".to_string()).unwrap();
983
984 let keys = key_manager.list_keys();
985 assert!(keys.contains(&"test_key".to_string()));
986
987 let signer = key_manager.get_signer("test_key").unwrap();
988 assert!(signer.lock().unwrap().key_id() == Some("test_key"));
989 }
990
991 #[test]
992 fn test_fiduciary_crypto() {
993 let mut crypto = FiduciaryCrypto::new();
994
995 crypto.generate_key("test_key".to_string()).unwrap();
996
997 let message = b"Test message";
998 let signature = crypto
999 .sign(
1000 message,
1001 Some("test_key"),
1002 "test".to_string(),
1003 "auth".to_string(),
1004 )
1005 .unwrap();
1006
1007 let is_valid = crypto
1008 .verify(
1009 message,
1010 &signature,
1011 Some("test_key"),
1012 "test".to_string(),
1013 "auth".to_string(),
1014 )
1015 .unwrap();
1016
1017 assert!(is_valid);
1018 }
1019
1020 #[test]
1021 fn test_vc_issuance_roundtrip() {
1022 let (private_key, public_key) = MlDsaSigner::generate_keypair().unwrap();
1024 let issuer_did_hash = 12345u64;
1025
1026 let claim_quins = vec![crate::NQuin {
1028 subject: issuer_did_hash,
1029 predicate: crate::q_hash("test:hasRole"),
1030 object: crate::q_hash("test:Admin"),
1031 context: issuer_did_hash,
1032 metadata: 0,
1033 parity: 0,
1034 }];
1035
1036 let context = CryptoContext {
1038 domain: "test".to_string(),
1039 purpose: "vc-issuance".to_string(),
1040 timestamp: 0,
1041 nonce: [0u8; 32],
1042 };
1043
1044 let proof = MlDsaVcProof::issue_vc_mldsa(
1046 &claim_quins,
1047 &private_key.sk_bytes,
1048 issuer_did_hash,
1049 &context,
1050 )
1051 .unwrap();
1052
1053 let is_valid = proof
1055 .verify_vc_mldsa(&claim_quins, &public_key.pk_bytes, &context)
1056 .unwrap();
1057
1058 assert!(is_valid, "VC verification should succeed");
1059 }
1060
1061 #[test]
1062 fn test_vc_tampered_fragment_fails() {
1063 let (private_key, public_key) = MlDsaSigner::generate_keypair().unwrap();
1064 let issuer_did_hash = 12345u64;
1065
1066 let claim_quins = vec![crate::NQuin {
1067 subject: issuer_did_hash,
1068 predicate: crate::q_hash("test:hasRole"),
1069 object: crate::q_hash("test:Admin"),
1070 context: issuer_did_hash,
1071 metadata: 0,
1072 parity: 0,
1073 }];
1074
1075 let context = CryptoContext {
1076 domain: "test".to_string(),
1077 purpose: "vc-issuance".to_string(),
1078 timestamp: 0,
1079 nonce: [0u8; 32],
1080 };
1081
1082 let mut proof = MlDsaVcProof::issue_vc_mldsa(
1083 &claim_quins,
1084 &private_key.sk_bytes,
1085 issuer_did_hash,
1086 &context,
1087 )
1088 .unwrap();
1089
1090 if !proof.fragment_quins.is_empty() {
1092 proof.fragment_quins[0].object ^= 0xFF; }
1094
1095 let is_valid = proof
1096 .verify_vc_mldsa(&claim_quins, &public_key.pk_bytes, &context)
1097 .unwrap();
1098
1099 assert!(!is_valid, "Tampered fragment should fail verification");
1100 }
1101
1102 #[test]
1103 fn test_vc_wrong_key_fails() {
1104 let (private_key, _public_key) = MlDsaSigner::generate_keypair().unwrap();
1105 let (_wrong_private, wrong_public) = MlDsaSigner::generate_keypair().unwrap();
1106 let issuer_did_hash = 12345u64;
1107
1108 let claim_quins = vec![crate::NQuin {
1109 subject: issuer_did_hash,
1110 predicate: crate::q_hash("test:hasRole"),
1111 object: crate::q_hash("test:Admin"),
1112 context: issuer_did_hash,
1113 metadata: 0,
1114 parity: 0,
1115 }];
1116
1117 let context = CryptoContext {
1118 domain: "test".to_string(),
1119 purpose: "vc-issuance".to_string(),
1120 timestamp: 0,
1121 nonce: [0u8; 32],
1122 };
1123
1124 let proof = MlDsaVcProof::issue_vc_mldsa(
1125 &claim_quins,
1126 &private_key.sk_bytes,
1127 issuer_did_hash,
1128 &context,
1129 )
1130 .unwrap();
1131
1132 let is_valid = proof
1134 .verify_vc_mldsa(&claim_quins, &wrong_public.pk_bytes, &context)
1135 .unwrap();
1136
1137 assert!(!is_valid, "Wrong public key should fail verification");
1138 }
1139}