1use crate::error::IdentityError;
27use crate::{P2PError, Result};
28use serde::{Deserialize, Serialize};
29use sha2::{Digest, Sha256};
30use std::fmt;
31
32use crate::quantum_crypto::ant_quic_integration::{MlDsaPublicKey, MlDsaSecretKey, MlDsaSignature};
34
35#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
39pub struct NodeId(pub [u8; 32]);
40
41impl NodeId {
42 pub fn from_public_key(public_key: &MlDsaPublicKey) -> Self {
44 let mut hasher = Sha256::new();
45 hasher.update(public_key.as_bytes());
46 let hash = hasher.finalize();
47 let mut id = [0u8; 32];
48 id.copy_from_slice(&hash);
49 Self(id)
50 }
51
52 pub fn to_bytes(&self) -> &[u8; 32] {
54 &self.0
55 }
56
57 pub fn xor_distance(&self, other: &NodeId) -> [u8; 32] {
59 let mut distance = [0u8; 32];
60 for (i, out) in distance.iter_mut().enumerate() {
61 *out = self.0[i] ^ other.0[i];
62 }
63 distance
64 }
65
66 pub fn from_public_key_bytes(bytes: &[u8]) -> Result<Self> {
68 if bytes.len() != 1952 {
70 return Err(P2PError::Identity(IdentityError::InvalidFormat(
71 "Invalid ML-DSA public key length".to_string().into(),
72 )));
73 }
74
75 let public_key = MlDsaPublicKey::from_bytes(bytes).map_err(|e| {
77 IdentityError::InvalidFormat(format!("Invalid ML-DSA public key: {:?}", e).into())
78 })?;
79
80 Ok(NodeId::from_public_key(&public_key))
81 }
82
83 pub fn from_bytes(bytes: [u8; 32]) -> Self {
85 Self(bytes)
86 }
87}
88
89impl fmt::Display for NodeId {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 write!(f, "{}", hex::encode(&self.0[..8])) }
93}
94
95#[derive(Clone)]
97pub struct PublicNodeIdentity {
98 public_key: MlDsaPublicKey,
100 node_id: NodeId,
102}
103
104impl PublicNodeIdentity {
105 pub fn node_id(&self) -> &NodeId {
107 &self.node_id
108 }
109
110 pub fn public_key(&self) -> &MlDsaPublicKey {
112 &self.public_key
113 }
114
115 }
117
118pub struct NodeIdentity {
120 secret_key: MlDsaSecretKey,
122 public_key: MlDsaPublicKey,
124 node_id: NodeId,
126}
127
128impl NodeIdentity {
129 pub fn generate() -> Result<Self> {
131 let (public_key, secret_key) =
133 crate::quantum_crypto::generate_ml_dsa_keypair().map_err(|e| {
134 P2PError::Identity(IdentityError::InvalidFormat(
135 format!("Failed to generate ML-DSA key pair: {}", e).into(),
136 ))
137 })?;
138
139 let node_id = NodeId::from_public_key(&public_key);
140
141 crate::quantum_crypto::ant_quic_integration::register_debug_ml_dsa_keypair(
142 &secret_key,
143 &public_key,
144 );
145
146 Ok(Self {
147 secret_key,
148 public_key,
149 node_id,
150 })
151 }
152
153 pub fn to_user_id(&self) -> crate::peer_record::UserId {
155 crate::peer_record::UserId::from_bytes(self.node_id.0)
156 }
157
158 pub fn from_seed(seed: &[u8; 32]) -> Result<Self> {
160 use saorsa_pqc::{HkdfSha3_256, api::traits::Kdf};
162
163 const ML_DSA_PUB_LEN: usize = 1952;
165 const ML_DSA_SEC_LEN: usize = 4032;
166
167 let mut derived = vec![0u8; ML_DSA_PUB_LEN + ML_DSA_SEC_LEN];
168 HkdfSha3_256::derive(seed, None, b"saorsa-node-identity-seed", &mut derived).map_err(
169 |_| P2PError::Identity(IdentityError::InvalidFormat("HKDF expand failed".into())),
170 )?;
171
172 let pub_bytes = &derived[..ML_DSA_PUB_LEN];
173 let sec_bytes = &derived[ML_DSA_PUB_LEN..];
174
175 let public_key =
177 crate::quantum_crypto::ant_quic_integration::MlDsaPublicKey::from_bytes(pub_bytes)
178 .map_err(|e| {
179 P2PError::Identity(IdentityError::InvalidFormat(
180 format!("Invalid ML-DSA public key bytes: {e}").into(),
181 ))
182 })?;
183 let secret_key =
184 crate::quantum_crypto::ant_quic_integration::MlDsaSecretKey::from_bytes(sec_bytes)
185 .map_err(|e| {
186 P2PError::Identity(IdentityError::InvalidFormat(
187 format!("Invalid ML-DSA secret key bytes: {e}").into(),
188 ))
189 })?;
190
191 let node_id = NodeId::from_public_key(&public_key);
192
193 crate::quantum_crypto::ant_quic_integration::register_debug_ml_dsa_keypair(
194 &secret_key,
195 &public_key,
196 );
197
198 Ok(Self {
199 secret_key,
200 public_key,
201 node_id,
202 })
203 }
204
205 pub fn node_id(&self) -> &NodeId {
207 &self.node_id
208 }
209
210 pub fn public_key(&self) -> &MlDsaPublicKey {
212 &self.public_key
213 }
214
215 pub fn secret_key_bytes(&self) -> &[u8] {
219 self.secret_key.as_bytes()
220 }
221
222 pub fn sign(&self, message: &[u8]) -> Result<MlDsaSignature> {
224 crate::quantum_crypto::ml_dsa_sign(&self.secret_key, message).map_err(|e| {
225 P2PError::Identity(IdentityError::InvalidFormat(
226 format!("ML-DSA signing failed: {:?}", e).into(),
227 ))
228 })
229 }
230
231 pub fn verify(&self, message: &[u8], signature: &MlDsaSignature) -> Result<bool> {
233 crate::quantum_crypto::ml_dsa_verify(&self.public_key, message, signature).map_err(|e| {
234 P2PError::Identity(IdentityError::InvalidFormat(
235 format!("ML-DSA verification failed: {:?}", e).into(),
236 ))
237 })
238 }
239
240 pub fn to_public(&self) -> PublicNodeIdentity {
242 PublicNodeIdentity {
243 public_key: self.public_key.clone(),
244 node_id: self.node_id.clone(),
245 }
246 }
247}
248
249impl NodeIdentity {
250 pub fn from_secret_key(_secret_key: MlDsaSecretKey) -> Result<Self> {
254 Err(P2PError::Identity(IdentityError::InvalidFormat(
255 "Creating identity from secret key alone is not supported"
256 .to_string()
257 .into(),
258 )))
259 }
260}
261
262impl NodeIdentity {
267 #[must_use]
292 pub fn to_entangled_id(
293 &self,
294 binary_hash: &[u8; 32],
295 nonce: u64,
296 ) -> crate::attestation::EntangledId {
297 crate::attestation::EntangledId::derive(&self.public_key, binary_hash, nonce)
298 }
299
300 #[must_use]
314 pub fn verify_entangled_id(&self, entangled_id: &crate::attestation::EntangledId) -> bool {
315 entangled_id.verify(&self.public_key)
316 }
317
318 #[must_use]
331 pub fn verify_entangled_id_with_binary(
332 &self,
333 entangled_id: &crate::attestation::EntangledId,
334 binary_hash: &[u8; 32],
335 ) -> bool {
336 entangled_id.verify_with_binary(&self.public_key, binary_hash)
337 }
338}
339
340impl NodeIdentity {
341 pub async fn save_to_file(&self, path: &std::path::Path) -> Result<()> {
343 use tokio::fs;
344 let data = self.export();
345 let json = serde_json::to_string_pretty(&data).map_err(|e| {
346 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
347 format!("Failed to serialize identity: {}", e).into(),
348 ))
349 })?;
350
351 if let Some(parent) = path.parent() {
352 fs::create_dir_all(parent).await.map_err(|e| {
353 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
354 format!("Failed to create directory: {}", e).into(),
355 ))
356 })?;
357 }
358
359 tokio::fs::write(path, json).await.map_err(|e| {
360 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
361 format!("Failed to write identity file: {}", e).into(),
362 ))
363 })?;
364 Ok(())
365 }
366
367 pub async fn load_from_file(path: &std::path::Path) -> Result<Self> {
369 let json = tokio::fs::read_to_string(path).await.map_err(|e| {
370 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
371 format!("Failed to read identity file: {}", e).into(),
372 ))
373 })?;
374 let data: IdentityData = serde_json::from_str(&json).map_err(|e| {
375 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
376 format!("Failed to deserialize identity: {}", e).into(),
377 ))
378 })?;
379 Self::import(&data)
380 }
381}
382
383#[derive(Serialize, Deserialize)]
385pub struct IdentityData {
386 pub secret_key: Vec<u8>,
388 pub public_key: Vec<u8>,
390}
391
392impl NodeIdentity {
393 pub fn export(&self) -> IdentityData {
395 IdentityData {
396 secret_key: self.secret_key.as_bytes().to_vec(),
397 public_key: self.public_key.as_bytes().to_vec(),
398 }
399 }
400
401 pub fn import(data: &IdentityData) -> Result<Self> {
403 let secret_key = crate::quantum_crypto::ant_quic_integration::MlDsaSecretKey::from_bytes(
405 &data.secret_key,
406 )
407 .map_err(|e| {
408 P2PError::Identity(IdentityError::InvalidFormat(
409 format!("Invalid ML-DSA secret key: {e}").into(),
410 ))
411 })?;
412 let public_key = crate::quantum_crypto::ant_quic_integration::MlDsaPublicKey::from_bytes(
413 &data.public_key,
414 )
415 .map_err(|e| {
416 P2PError::Identity(IdentityError::InvalidFormat(
417 format!("Invalid ML-DSA public key: {e}").into(),
418 ))
419 })?;
420
421 let node_id = NodeId::from_public_key(&public_key);
422
423 crate::quantum_crypto::ant_quic_integration::register_debug_ml_dsa_keypair(
424 &secret_key,
425 &public_key,
426 );
427
428 Ok(Self {
429 secret_key,
430 public_key,
431 node_id,
432 })
433 }
434}
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439
440 #[test]
441 fn test_node_id_generation() {
442 let (public_key, _secret_key) = crate::quantum_crypto::generate_ml_dsa_keypair()
443 .expect("ML-DSA key generation should succeed");
444 let node_id = NodeId::from_public_key(&public_key);
445
446 assert_eq!(node_id.to_bytes().len(), 32);
448
449 let node_id2 = NodeId::from_public_key(&public_key);
451 assert_eq!(node_id, node_id2);
452 }
453
454 #[test]
455 fn test_xor_distance() {
456 let id1 = NodeId([0u8; 32]);
457 let mut id2_bytes = [0u8; 32];
458 id2_bytes[0] = 0xFF;
459 let id2 = NodeId(id2_bytes);
460
461 let distance = id1.xor_distance(&id2);
462 assert_eq!(distance[0], 0xFF);
463 for byte in &distance[1..] {
464 assert_eq!(*byte, 0);
465 }
466 }
467
468 #[test]
469 fn test_proof_of_work() {
470 }
472
473 #[test]
474 fn test_identity_generation() {
475 let identity = NodeIdentity::generate().expect("Identity generation should succeed");
476
477 let message = b"Hello, P2P!";
479 let signature = identity.sign(message).unwrap();
480 assert!(identity.verify(message, &signature).unwrap());
481
482 assert!(!identity.verify(b"Wrong message", &signature).unwrap());
484 }
485
486 #[test]
487 fn test_deterministic_generation() {
488 let seed = [0x42; 32];
489 let identity1 = NodeIdentity::from_seed(&seed).expect("Identity from seed should succeed");
490 let identity2 = NodeIdentity::from_seed(&seed).expect("Identity from seed should succeed");
491
492 assert_eq!(identity1.node_id, identity2.node_id);
494 assert_eq!(
495 identity1.public_key().as_bytes(),
496 identity2.public_key().as_bytes()
497 );
498 }
499
500 #[test]
501 fn test_identity_persistence() {
502 let identity = NodeIdentity::generate().expect("Identity generation should succeed");
503
504 let data = identity.export();
506
507 let imported = NodeIdentity::import(&data).expect("Import should succeed with valid data");
509
510 assert_eq!(identity.node_id, imported.node_id);
512 assert_eq!(
513 identity.public_key().as_bytes(),
514 imported.public_key().as_bytes()
515 );
516
517 let message = b"Test message";
519 let signature = imported.sign(message);
520 assert!(identity.verify(message, &signature.unwrap()).unwrap());
521 }
522
523 #[test]
528 fn test_entangled_id_creation() {
529 let identity = NodeIdentity::generate().expect("Identity generation should succeed");
530 let binary_hash = [0x42u8; 32];
531 let nonce = 12345u64;
532
533 let entangled_id = identity.to_entangled_id(&binary_hash, nonce);
534
535 assert_ne!(
537 entangled_id.id(),
538 identity.node_id().to_bytes(),
539 "Entangled ID should differ from plain NodeId"
540 );
541
542 let entangled_id2 = identity.to_entangled_id(&binary_hash, nonce);
544 assert_eq!(entangled_id.id(), entangled_id2.id());
545 }
546
547 #[test]
548 fn test_entangled_id_verification() {
549 let identity = NodeIdentity::generate().expect("Identity generation should succeed");
550 let binary_hash = [0x42u8; 32];
551 let nonce = 12345u64;
552
553 let entangled_id = identity.to_entangled_id(&binary_hash, nonce);
554
555 assert!(
557 identity.verify_entangled_id(&entangled_id),
558 "Entangled ID should verify against its creating identity"
559 );
560 }
561
562 #[test]
563 fn test_entangled_id_rejects_different_identity() {
564 let identity1 = NodeIdentity::generate().expect("Identity generation should succeed");
565 let identity2 = NodeIdentity::generate().expect("Identity generation should succeed");
566 let binary_hash = [0x42u8; 32];
567 let nonce = 12345u64;
568
569 let entangled_id = identity1.to_entangled_id(&binary_hash, nonce);
570
571 assert!(
573 !identity2.verify_entangled_id(&entangled_id),
574 "Entangled ID should NOT verify against a different identity"
575 );
576 }
577
578 #[test]
579 fn test_entangled_id_with_binary_verification() {
580 let identity = NodeIdentity::generate().expect("Identity generation should succeed");
581 let binary_hash = [0x42u8; 32];
582 let wrong_binary_hash = [0x43u8; 32];
583 let nonce = 12345u64;
584
585 let entangled_id = identity.to_entangled_id(&binary_hash, nonce);
586
587 assert!(
589 identity.verify_entangled_id_with_binary(&entangled_id, &binary_hash),
590 "Should verify with correct binary hash"
591 );
592
593 assert!(
595 !identity.verify_entangled_id_with_binary(&entangled_id, &wrong_binary_hash),
596 "Should NOT verify with wrong binary hash"
597 );
598 }
599
600 #[test]
601 fn test_entangled_id_to_node_id_consistency() {
602 let identity = NodeIdentity::generate().expect("Identity generation should succeed");
603 let binary_hash = [0x42u8; 32];
604 let nonce = 12345u64;
605
606 let entangled_id = identity.to_entangled_id(&binary_hash, nonce);
607 let derived_node_id = entangled_id.to_node_id();
608
609 assert_eq!(
611 derived_node_id.to_bytes(),
612 entangled_id.id(),
613 "NodeId derived from EntangledId should have same bytes"
614 );
615 }
616}