1use crate::error::IdentityError;
27use crate::{P2PError, Result};
28use saorsa_pqc::HkdfSha3_256;
29use saorsa_pqc::api::sig::{MlDsa, MlDsaVariant};
30use saorsa_pqc::api::traits::Kdf;
31use serde::{Deserialize, Serialize};
32use std::fmt;
33
34use crate::quantum_crypto::saorsa_transport_integration::{
36 MlDsaPublicKey, MlDsaSecretKey, MlDsaSignature,
37};
38
39pub use super::peer_id::{PEER_ID_BYTE_LEN, PeerId, PeerIdParseError};
41
42pub fn peer_id_from_public_key(public_key: &MlDsaPublicKey) -> PeerId {
48 let hash = blake3::hash(public_key.as_bytes());
49 PeerId(*hash.as_bytes())
50}
51
52const ML_DSA_PUB_KEY_LEN: usize = 1952;
54
55pub fn peer_id_from_public_key_bytes(bytes: &[u8]) -> Result<PeerId> {
62 if bytes.len() != ML_DSA_PUB_KEY_LEN {
63 return Err(P2PError::Identity(IdentityError::InvalidFormat(
64 "Invalid ML-DSA public key length".to_string().into(),
65 )));
66 }
67
68 let public_key = MlDsaPublicKey::from_bytes(bytes).map_err(|e| {
69 IdentityError::InvalidFormat(format!("Invalid ML-DSA public key: {:?}", e).into())
70 })?;
71
72 Ok(peer_id_from_public_key(&public_key))
73}
74
75#[derive(Clone)]
77pub struct PublicNodeIdentity {
78 public_key: MlDsaPublicKey,
80 peer_id: PeerId,
82}
83
84impl PublicNodeIdentity {
85 pub fn peer_id(&self) -> &PeerId {
87 &self.peer_id
88 }
89
90 pub fn public_key(&self) -> &MlDsaPublicKey {
92 &self.public_key
93 }
94
95 }
97
98pub struct NodeIdentity {
102 secret_key: MlDsaSecretKey,
104 public_key: MlDsaPublicKey,
106 peer_id: PeerId,
108}
109
110impl fmt::Debug for NodeIdentity {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 f.debug_struct("NodeIdentity")
113 .field("peer_id", &self.peer_id)
114 .field("secret_key", &"[REDACTED]")
115 .finish()
116 }
117}
118
119impl NodeIdentity {
120 pub fn generate() -> Result<Self> {
122 let (public_key, secret_key) =
124 crate::quantum_crypto::generate_ml_dsa_keypair().map_err(|e| {
125 P2PError::Identity(IdentityError::InvalidFormat(
126 format!("Failed to generate ML-DSA key pair: {}", e).into(),
127 ))
128 })?;
129
130 let peer_id = peer_id_from_public_key(&public_key);
131
132 Ok(Self {
133 secret_key,
134 public_key,
135 peer_id,
136 })
137 }
138
139 pub fn from_seed(seed: &[u8; 32]) -> Result<Self> {
141 let mut xi = [0u8; 32];
143 HkdfSha3_256::derive(seed, None, b"saorsa-node-identity-seed", &mut xi).map_err(|_| {
144 P2PError::Identity(IdentityError::InvalidFormat("HKDF expand failed".into()))
145 })?;
146
147 let dsa = MlDsa::new(MlDsaVariant::MlDsa65);
149 let (pk, sk) = dsa.generate_keypair_from_seed(&xi);
150
151 let public_key = MlDsaPublicKey::from_bytes(&pk.to_bytes()).map_err(|e| {
152 P2PError::Identity(IdentityError::InvalidFormat(
153 format!("Invalid ML-DSA public key bytes: {e}").into(),
154 ))
155 })?;
156 let secret_key = MlDsaSecretKey::from_bytes(&sk.to_bytes()).map_err(|e| {
157 P2PError::Identity(IdentityError::InvalidFormat(
158 format!("Invalid ML-DSA secret key bytes: {e}").into(),
159 ))
160 })?;
161
162 let peer_id = peer_id_from_public_key(&public_key);
163
164 Ok(Self {
165 secret_key,
166 public_key,
167 peer_id,
168 })
169 }
170
171 pub fn peer_id(&self) -> &PeerId {
173 &self.peer_id
174 }
175
176 pub fn public_key(&self) -> &MlDsaPublicKey {
178 &self.public_key
179 }
180
181 pub fn secret_key_bytes(&self) -> &[u8] {
185 self.secret_key.as_bytes()
186 }
187
188 pub fn secret_key(&self) -> &MlDsaSecretKey {
194 &self.secret_key
195 }
196
197 pub fn sign(&self, message: &[u8]) -> Result<MlDsaSignature> {
199 crate::quantum_crypto::ml_dsa_sign(&self.secret_key, message).map_err(|e| {
200 P2PError::Identity(IdentityError::InvalidFormat(
201 format!("ML-DSA signing failed: {:?}", e).into(),
202 ))
203 })
204 }
205
206 pub fn verify(&self, message: &[u8], signature: &MlDsaSignature) -> Result<bool> {
208 crate::quantum_crypto::ml_dsa_verify(&self.public_key, message, signature).map_err(|e| {
209 P2PError::Identity(IdentityError::InvalidFormat(
210 format!("ML-DSA verification failed: {:?}", e).into(),
211 ))
212 })
213 }
214
215 pub fn to_public(&self) -> PublicNodeIdentity {
217 PublicNodeIdentity {
218 public_key: self.public_key.clone(),
219 peer_id: self.peer_id,
220 }
221 }
222}
223
224impl NodeIdentity {
225 pub fn from_secret_key(_secret_key: MlDsaSecretKey) -> Result<Self> {
229 Err(P2PError::Identity(IdentityError::InvalidFormat(
230 "Creating identity from secret key alone is not supported"
231 .to_string()
232 .into(),
233 )))
234 }
235}
236
237impl NodeIdentity {
238 pub async fn save_to_file(&self, path: &std::path::Path) -> Result<()> {
240 use tokio::fs;
241 let data = self.export();
242 let json = serde_json::to_string_pretty(&data).map_err(|e| {
243 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
244 format!("Failed to serialize identity: {}", e).into(),
245 ))
246 })?;
247
248 if let Some(parent) = path.parent() {
249 fs::create_dir_all(parent).await.map_err(|e| {
250 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
251 format!("Failed to create directory: {}", e).into(),
252 ))
253 })?;
254 }
255
256 tokio::fs::write(path, json).await.map_err(|e| {
257 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
258 format!("Failed to write identity file: {}", e).into(),
259 ))
260 })?;
261 Ok(())
262 }
263
264 pub async fn load_from_file(path: &std::path::Path) -> Result<Self> {
266 let json = tokio::fs::read_to_string(path).await.map_err(|e| {
267 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
268 format!("Failed to read identity file: {}", e).into(),
269 ))
270 })?;
271 let data: IdentityData = serde_json::from_str(&json).map_err(|e| {
272 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
273 format!("Failed to deserialize identity: {}", e).into(),
274 ))
275 })?;
276 Self::import(&data)
277 }
278}
279
280#[derive(Serialize, Deserialize)]
282pub struct IdentityData {
283 pub secret_key: Vec<u8>,
285 pub public_key: Vec<u8>,
287}
288
289impl NodeIdentity {
290 pub fn export(&self) -> IdentityData {
292 IdentityData {
293 secret_key: self.secret_key.as_bytes().to_vec(),
294 public_key: self.public_key.as_bytes().to_vec(),
295 }
296 }
297
298 pub fn import(data: &IdentityData) -> Result<Self> {
300 let secret_key =
302 crate::quantum_crypto::saorsa_transport_integration::MlDsaSecretKey::from_bytes(
303 &data.secret_key,
304 )
305 .map_err(|e| {
306 P2PError::Identity(IdentityError::InvalidFormat(
307 format!("Invalid ML-DSA secret key: {e}").into(),
308 ))
309 })?;
310 let public_key =
311 crate::quantum_crypto::saorsa_transport_integration::MlDsaPublicKey::from_bytes(
312 &data.public_key,
313 )
314 .map_err(|e| {
315 P2PError::Identity(IdentityError::InvalidFormat(
316 format!("Invalid ML-DSA public key: {e}").into(),
317 ))
318 })?;
319
320 let peer_id = peer_id_from_public_key(&public_key);
321
322 Ok(Self {
323 secret_key,
324 public_key,
325 peer_id,
326 })
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333
334 #[test]
335 fn test_peer_id_generation() {
336 let (public_key, _secret_key) = crate::quantum_crypto::generate_ml_dsa_keypair()
337 .expect("ML-DSA key generation should succeed");
338 let peer_id = peer_id_from_public_key(&public_key);
339
340 assert_eq!(peer_id.to_bytes().len(), 32);
342
343 let peer_id2 = peer_id_from_public_key(&public_key);
345 assert_eq!(peer_id, peer_id2);
346 }
347
348 #[test]
349 fn test_xor_distance() {
350 let id1 = PeerId([0u8; 32]);
351 let mut id2_bytes = [0u8; 32];
352 id2_bytes[0] = 0xFF;
353 let id2 = PeerId(id2_bytes);
354
355 let distance = id1.xor_distance(&id2);
356 assert_eq!(distance[0], 0xFF);
357 for byte in &distance[1..] {
358 assert_eq!(*byte, 0);
359 }
360 }
361
362 #[test]
363 fn test_proof_of_work() {
364 }
366
367 #[test]
368 fn test_identity_generation() {
369 let identity = NodeIdentity::generate().expect("Identity generation should succeed");
370
371 let message = b"Hello, P2P!";
373 let signature = identity.sign(message).unwrap();
374 assert!(identity.verify(message, &signature).unwrap());
375
376 assert!(!identity.verify(b"Wrong message", &signature).unwrap());
378 }
379
380 #[test]
381 fn test_deterministic_generation() {
382 let seed = [0x42; 32];
383 let identity1 = NodeIdentity::from_seed(&seed).expect("Identity from seed should succeed");
384 let identity2 = NodeIdentity::from_seed(&seed).expect("Identity from seed should succeed");
385
386 assert_eq!(identity1.peer_id, identity2.peer_id);
388 assert_eq!(
389 identity1.public_key().as_bytes(),
390 identity2.public_key().as_bytes()
391 );
392 }
393
394 #[test]
395 fn test_identity_persistence() {
396 let identity = NodeIdentity::generate().expect("Identity generation should succeed");
397
398 let data = identity.export();
400
401 let imported = NodeIdentity::import(&data).expect("Import should succeed with valid data");
403
404 assert_eq!(identity.peer_id, imported.peer_id);
406 assert_eq!(
407 identity.public_key().as_bytes(),
408 imported.public_key().as_bytes()
409 );
410
411 let message = b"Test message";
413 let signature = imported.sign(message);
414 assert!(identity.verify(message, &signature.unwrap()).unwrap());
415 }
416
417 #[test]
418 fn test_peer_id_display_full_hex() {
419 let id = PeerId([0xAB; 32]);
420 let display = format!("{}", id);
421 assert_eq!(display.len(), 64);
422 assert_eq!(display, "ab".repeat(32));
423 }
424
425 #[test]
426 fn test_peer_id_ord() {
427 let a = PeerId([0x00; 32]);
428 let b = PeerId([0xFF; 32]);
429 assert!(a < b);
430 }
431
432 #[test]
433 fn test_peer_id_from_str() {
434 let hex = "ab".repeat(32);
435 let id: PeerId = hex.parse().expect("should parse valid hex");
436 assert_eq!(id.0, [0xAB; 32]);
437 }
438
439 #[test]
440 fn test_peer_id_json_roundtrip() {
441 let id = PeerId([0xAB; 32]);
442 let json = serde_json::to_string(&id).expect("serialize");
443 assert_eq!(json, format!("\"{}\"", "ab".repeat(32)));
444 let deserialized: PeerId = serde_json::from_str(&json).expect("deserialize");
445 assert_eq!(id, deserialized);
446 }
447
448 #[test]
449 fn test_peer_id_postcard_roundtrip() {
450 let id = PeerId([0xAB; 32]);
451 let bytes = postcard::to_stdvec(&id).expect("serialize");
452 let deserialized: PeerId = postcard::from_bytes(&bytes).expect("deserialize");
453 assert_eq!(id, deserialized);
454 }
455}