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