1use crate::error::IdentityError;
22use crate::{P2PError, Result};
23use saorsa_pqc::HkdfSha3_256;
24use saorsa_pqc::api::sig::{MlDsa, MlDsaVariant};
25use saorsa_pqc::api::traits::Kdf;
26use serde::{Deserialize, Serialize};
27use std::fmt;
28
29use crate::quantum_crypto::saorsa_transport_integration::{
31 MlDsaPublicKey, MlDsaSecretKey, MlDsaSignature,
32};
33
34pub use super::peer_id::{PEER_ID_BYTE_LEN, PeerId, PeerIdParseError};
36
37pub fn peer_id_from_public_key(public_key: &MlDsaPublicKey) -> PeerId {
43 let hash = blake3::hash(public_key.as_bytes());
44 PeerId(*hash.as_bytes())
45}
46
47const ML_DSA_PUB_KEY_LEN: usize = 1952;
49
50pub fn peer_id_from_public_key_bytes(bytes: &[u8]) -> Result<PeerId> {
57 if bytes.len() != ML_DSA_PUB_KEY_LEN {
58 return Err(P2PError::Identity(IdentityError::InvalidFormat(
59 "Invalid ML-DSA public key length".to_string().into(),
60 )));
61 }
62
63 let public_key = MlDsaPublicKey::from_bytes(bytes).map_err(|e| {
64 IdentityError::InvalidFormat(format!("Invalid ML-DSA public key: {:?}", e).into())
65 })?;
66
67 Ok(peer_id_from_public_key(&public_key))
68}
69
70#[cfg(feature = "native")]
77pub(crate) fn peer_id_from_public_key_spki(spki_bytes: &[u8]) -> Result<PeerId> {
78 let public_key =
79 saorsa_transport::crypto::raw_public_keys::pqc::extract_public_key_from_spki(spki_bytes)
80 .map_err(|e| {
81 P2PError::Identity(IdentityError::InvalidFormat(
82 format!("Invalid ML-DSA SubjectPublicKeyInfo: {e}").into(),
83 ))
84 })?;
85
86 peer_id_from_public_key_bytes(public_key.as_bytes())
87}
88
89#[derive(Clone)]
91pub struct PublicNodeIdentity {
92 public_key: MlDsaPublicKey,
94 peer_id: PeerId,
96}
97
98impl PublicNodeIdentity {
99 pub fn peer_id(&self) -> &PeerId {
101 &self.peer_id
102 }
103
104 pub fn public_key(&self) -> &MlDsaPublicKey {
106 &self.public_key
107 }
108
109 }
111
112pub struct NodeIdentity {
116 secret_key: MlDsaSecretKey,
118 public_key: MlDsaPublicKey,
120 peer_id: PeerId,
122}
123
124impl fmt::Debug for NodeIdentity {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 f.debug_struct("NodeIdentity")
127 .field("peer_id", &self.peer_id)
128 .field("secret_key", &"[REDACTED]")
129 .finish()
130 }
131}
132
133impl NodeIdentity {
134 pub fn generate() -> Result<Self> {
136 let (public_key, secret_key) =
138 crate::quantum_crypto::generate_ml_dsa_keypair().map_err(|e| {
139 P2PError::Identity(IdentityError::InvalidFormat(
140 format!("Failed to generate ML-DSA key pair: {}", e).into(),
141 ))
142 })?;
143
144 let peer_id = peer_id_from_public_key(&public_key);
145
146 Ok(Self {
147 secret_key,
148 public_key,
149 peer_id,
150 })
151 }
152
153 pub fn from_seed(seed: &[u8; 32]) -> Result<Self> {
155 let mut xi = [0u8; 32];
157 HkdfSha3_256::derive(seed, None, b"saorsa-node-identity-seed", &mut xi).map_err(|_| {
158 P2PError::Identity(IdentityError::InvalidFormat("HKDF expand failed".into()))
159 })?;
160
161 let dsa = MlDsa::new(MlDsaVariant::MlDsa65);
163 let (pk, sk) = dsa.generate_keypair_from_seed(&xi);
164
165 let public_key = MlDsaPublicKey::from_bytes(&pk.to_bytes()).map_err(|e| {
166 P2PError::Identity(IdentityError::InvalidFormat(
167 format!("Invalid ML-DSA public key bytes: {e}").into(),
168 ))
169 })?;
170 let secret_key = MlDsaSecretKey::from_bytes(&sk.to_bytes()).map_err(|e| {
171 P2PError::Identity(IdentityError::InvalidFormat(
172 format!("Invalid ML-DSA secret key bytes: {e}").into(),
173 ))
174 })?;
175
176 let peer_id = peer_id_from_public_key(&public_key);
177
178 Ok(Self {
179 secret_key,
180 public_key,
181 peer_id,
182 })
183 }
184
185 pub fn peer_id(&self) -> &PeerId {
187 &self.peer_id
188 }
189
190 pub fn public_key(&self) -> &MlDsaPublicKey {
192 &self.public_key
193 }
194
195 pub fn secret_key_bytes(&self) -> &[u8] {
199 self.secret_key.as_bytes()
200 }
201
202 pub fn secret_key(&self) -> &MlDsaSecretKey {
208 &self.secret_key
209 }
210
211 pub fn sign(&self, message: &[u8]) -> Result<MlDsaSignature> {
213 crate::quantum_crypto::ml_dsa_sign(&self.secret_key, message).map_err(|e| {
214 P2PError::Identity(IdentityError::InvalidFormat(
215 format!("ML-DSA signing failed: {:?}", e).into(),
216 ))
217 })
218 }
219
220 pub fn verify(&self, message: &[u8], signature: &MlDsaSignature) -> Result<bool> {
222 crate::quantum_crypto::ml_dsa_verify(&self.public_key, message, signature).map_err(|e| {
223 P2PError::Identity(IdentityError::InvalidFormat(
224 format!("ML-DSA verification failed: {:?}", e).into(),
225 ))
226 })
227 }
228
229 pub fn to_public(&self) -> PublicNodeIdentity {
231 PublicNodeIdentity {
232 public_key: self.public_key.clone(),
233 peer_id: self.peer_id,
234 }
235 }
236}
237
238impl NodeIdentity {
239 pub fn from_secret_key(_secret_key: MlDsaSecretKey) -> Result<Self> {
243 Err(P2PError::Identity(IdentityError::InvalidFormat(
244 "Creating identity from secret key alone is not supported"
245 .to_string()
246 .into(),
247 )))
248 }
249}
250
251impl NodeIdentity {
252 #[cfg(feature = "native")]
254 pub async fn save_to_file(&self, path: &std::path::Path) -> Result<()> {
255 use tokio::fs;
256 let data = self.export();
257 let json = serde_json::to_string_pretty(&data).map_err(|e| {
258 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
259 format!("Failed to serialize identity: {}", e).into(),
260 ))
261 })?;
262
263 if let Some(parent) = path.parent() {
264 fs::create_dir_all(parent).await.map_err(|e| {
265 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
266 format!("Failed to create directory: {}", e).into(),
267 ))
268 })?;
269 }
270
271 tokio::fs::write(path, json).await.map_err(|e| {
272 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
273 format!("Failed to write identity file: {}", e).into(),
274 ))
275 })?;
276 Ok(())
277 }
278
279 #[cfg(feature = "native")]
281 pub async fn load_from_file(path: &std::path::Path) -> Result<Self> {
282 let json = tokio::fs::read_to_string(path).await.map_err(|e| {
283 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
284 format!("Failed to read identity file: {}", e).into(),
285 ))
286 })?;
287 let data: IdentityData = serde_json::from_str(&json).map_err(|e| {
288 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
289 format!("Failed to deserialize identity: {}", e).into(),
290 ))
291 })?;
292 Self::import(&data)
293 }
294}
295
296#[derive(Serialize, Deserialize)]
298pub struct IdentityData {
299 pub secret_key: Vec<u8>,
301 pub public_key: Vec<u8>,
303}
304
305impl NodeIdentity {
306 pub fn export(&self) -> IdentityData {
308 IdentityData {
309 secret_key: self.secret_key.as_bytes().to_vec(),
310 public_key: self.public_key.as_bytes().to_vec(),
311 }
312 }
313
314 pub fn import(data: &IdentityData) -> Result<Self> {
316 let secret_key =
318 crate::quantum_crypto::saorsa_transport_integration::MlDsaSecretKey::from_bytes(
319 &data.secret_key,
320 )
321 .map_err(|e| {
322 P2PError::Identity(IdentityError::InvalidFormat(
323 format!("Invalid ML-DSA secret key: {e}").into(),
324 ))
325 })?;
326 let public_key =
327 crate::quantum_crypto::saorsa_transport_integration::MlDsaPublicKey::from_bytes(
328 &data.public_key,
329 )
330 .map_err(|e| {
331 P2PError::Identity(IdentityError::InvalidFormat(
332 format!("Invalid ML-DSA public key: {e}").into(),
333 ))
334 })?;
335
336 let peer_id = peer_id_from_public_key(&public_key);
337
338 Ok(Self {
339 secret_key,
340 public_key,
341 peer_id,
342 })
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[cfg(feature = "native")]
351 fn ml_dsa_65_spki(public_key: &[u8]) -> Vec<u8> {
352 const OID: [u8; 9] = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x12];
353 let bit_string_len = public_key.len() + 1;
354 let algorithm_len = 2 + OID.len();
355 let algorithm_total_len = 2 + algorithm_len;
356 let bit_string_total_len = 4 + bit_string_len;
357 let outer_len = algorithm_total_len + bit_string_total_len;
358
359 let mut encoded = Vec::with_capacity(4 + outer_len);
360 encoded.extend_from_slice(&[
361 0x30,
362 0x82,
363 (outer_len >> 8) as u8,
364 outer_len as u8,
365 0x30,
366 algorithm_len as u8,
367 0x06,
368 OID.len() as u8,
369 ]);
370 encoded.extend_from_slice(&OID);
371 encoded.extend_from_slice(&[
372 0x03,
373 0x82,
374 (bit_string_len >> 8) as u8,
375 bit_string_len as u8,
376 0x00,
377 ]);
378 encoded.extend_from_slice(public_key);
379 encoded
380 }
381
382 #[test]
383 fn test_peer_id_generation() {
384 let (public_key, _secret_key) = crate::quantum_crypto::generate_ml_dsa_keypair()
385 .expect("ML-DSA key generation should succeed");
386 let peer_id = peer_id_from_public_key(&public_key);
387
388 assert_eq!(peer_id.to_bytes().len(), 32);
390
391 let peer_id2 = peer_id_from_public_key(&public_key);
393 assert_eq!(peer_id, peer_id2);
394 }
395
396 #[test]
397 #[cfg(feature = "native")]
398 fn transport_spki_derives_same_peer_id_as_raw_key() {
399 let (public_key, _secret_key) = crate::quantum_crypto::generate_ml_dsa_keypair()
400 .expect("ML-DSA key generation should succeed");
401 let spki = ml_dsa_65_spki(public_key.as_bytes());
402
403 let from_spki =
404 peer_id_from_public_key_spki(&spki).expect("valid ML-DSA SPKI should parse");
405
406 assert_eq!(from_spki, peer_id_from_public_key(&public_key));
407 }
408
409 #[test]
410 #[cfg(feature = "native")]
411 fn transport_spki_rejects_wrong_algorithm() {
412 let (public_key, _secret_key) = crate::quantum_crypto::generate_ml_dsa_keypair()
413 .expect("ML-DSA key generation should succeed");
414 let mut spki = ml_dsa_65_spki(public_key.as_bytes());
415 let oid_last_byte = 16;
416 spki[oid_last_byte] = 0x11;
417
418 assert!(peer_id_from_public_key_spki(&spki).is_err());
419 }
420
421 #[test]
422 #[cfg(feature = "native")]
423 fn transport_spki_rejects_raw_public_key_bytes() {
424 let (public_key, _secret_key) = crate::quantum_crypto::generate_ml_dsa_keypair()
425 .expect("ML-DSA key generation should succeed");
426
427 assert!(peer_id_from_public_key_spki(public_key.as_bytes()).is_err());
428 }
429
430 #[test]
431 fn test_xor_distance() {
432 let id1 = PeerId([0u8; 32]);
433 let mut id2_bytes = [0u8; 32];
434 id2_bytes[0] = 0xFF;
435 let id2 = PeerId(id2_bytes);
436
437 let distance = id1.xor_distance(&id2);
438 assert_eq!(distance[0], 0xFF);
439 for byte in &distance[1..] {
440 assert_eq!(*byte, 0);
441 }
442 }
443
444 #[test]
445 fn test_proof_of_work() {
446 }
448
449 #[test]
450 fn test_identity_generation() {
451 let identity = NodeIdentity::generate().expect("Identity generation should succeed");
452
453 let message = b"Hello, P2P!";
455 let signature = identity.sign(message).unwrap();
456 assert!(identity.verify(message, &signature).unwrap());
457
458 assert!(!identity.verify(b"Wrong message", &signature).unwrap());
460 }
461
462 #[test]
463 fn test_deterministic_generation() {
464 let seed = [0x42; 32];
465 let identity1 = NodeIdentity::from_seed(&seed).expect("Identity from seed should succeed");
466 let identity2 = NodeIdentity::from_seed(&seed).expect("Identity from seed should succeed");
467
468 assert_eq!(identity1.peer_id, identity2.peer_id);
470 assert_eq!(
471 identity1.public_key().as_bytes(),
472 identity2.public_key().as_bytes()
473 );
474 }
475
476 #[test]
477 fn test_identity_persistence() {
478 let identity = NodeIdentity::generate().expect("Identity generation should succeed");
479
480 let data = identity.export();
482
483 let imported = NodeIdentity::import(&data).expect("Import should succeed with valid data");
485
486 assert_eq!(identity.peer_id, imported.peer_id);
488 assert_eq!(
489 identity.public_key().as_bytes(),
490 imported.public_key().as_bytes()
491 );
492
493 let message = b"Test message";
495 let signature = imported.sign(message);
496 assert!(identity.verify(message, &signature.unwrap()).unwrap());
497 }
498
499 #[test]
500 fn test_peer_id_display_full_hex() {
501 let id = PeerId([0xAB; 32]);
502 let display = format!("{}", id);
503 assert_eq!(display.len(), 64);
504 assert_eq!(display, "ab".repeat(32));
505 }
506
507 #[test]
508 fn test_peer_id_ord() {
509 let a = PeerId([0x00; 32]);
510 let b = PeerId([0xFF; 32]);
511 assert!(a < b);
512 }
513
514 #[test]
515 fn test_peer_id_from_str() {
516 let hex = "ab".repeat(32);
517 let id: PeerId = hex.parse().expect("should parse valid hex");
518 assert_eq!(id.0, [0xAB; 32]);
519 }
520
521 #[test]
522 fn test_peer_id_json_roundtrip() {
523 let id = PeerId([0xAB; 32]);
524 let json = serde_json::to_string(&id).expect("serialize");
525 assert_eq!(json, format!("\"{}\"", "ab".repeat(32)));
526 let deserialized: PeerId = serde_json::from_str(&json).expect("deserialize");
527 assert_eq!(id, deserialized);
528 }
529
530 #[test]
531 fn test_peer_id_postcard_roundtrip() {
532 let id = PeerId([0xAB; 32]);
533 let bytes = postcard::to_stdvec(&id).expect("serialize");
534 let deserialized: PeerId = postcard::from_bytes(&bytes).expect("deserialize");
535 assert_eq!(id, deserialized);
536 }
537}