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 sign(&self, message: &[u8]) -> Result<MlDsaSignature> {
190 crate::quantum_crypto::ml_dsa_sign(&self.secret_key, message).map_err(|e| {
191 P2PError::Identity(IdentityError::InvalidFormat(
192 format!("ML-DSA signing failed: {:?}", e).into(),
193 ))
194 })
195 }
196
197 pub fn verify(&self, message: &[u8], signature: &MlDsaSignature) -> Result<bool> {
199 crate::quantum_crypto::ml_dsa_verify(&self.public_key, message, signature).map_err(|e| {
200 P2PError::Identity(IdentityError::InvalidFormat(
201 format!("ML-DSA verification failed: {:?}", e).into(),
202 ))
203 })
204 }
205
206 pub fn to_public(&self) -> PublicNodeIdentity {
208 PublicNodeIdentity {
209 public_key: self.public_key.clone(),
210 peer_id: self.peer_id,
211 }
212 }
213}
214
215impl NodeIdentity {
216 pub fn from_secret_key(_secret_key: MlDsaSecretKey) -> Result<Self> {
220 Err(P2PError::Identity(IdentityError::InvalidFormat(
221 "Creating identity from secret key alone is not supported"
222 .to_string()
223 .into(),
224 )))
225 }
226}
227
228impl NodeIdentity {
229 pub async fn save_to_file(&self, path: &std::path::Path) -> Result<()> {
231 use tokio::fs;
232 let data = self.export();
233 let json = serde_json::to_string_pretty(&data).map_err(|e| {
234 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
235 format!("Failed to serialize identity: {}", e).into(),
236 ))
237 })?;
238
239 if let Some(parent) = path.parent() {
240 fs::create_dir_all(parent).await.map_err(|e| {
241 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
242 format!("Failed to create directory: {}", e).into(),
243 ))
244 })?;
245 }
246
247 tokio::fs::write(path, json).await.map_err(|e| {
248 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
249 format!("Failed to write identity file: {}", e).into(),
250 ))
251 })?;
252 Ok(())
253 }
254
255 pub async fn load_from_file(path: &std::path::Path) -> Result<Self> {
257 let json = tokio::fs::read_to_string(path).await.map_err(|e| {
258 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
259 format!("Failed to read identity file: {}", e).into(),
260 ))
261 })?;
262 let data: IdentityData = serde_json::from_str(&json).map_err(|e| {
263 P2PError::Identity(crate::error::IdentityError::InvalidFormat(
264 format!("Failed to deserialize identity: {}", e).into(),
265 ))
266 })?;
267 Self::import(&data)
268 }
269}
270
271#[derive(Serialize, Deserialize)]
273pub struct IdentityData {
274 pub secret_key: Vec<u8>,
276 pub public_key: Vec<u8>,
278}
279
280impl NodeIdentity {
281 pub fn export(&self) -> IdentityData {
283 IdentityData {
284 secret_key: self.secret_key.as_bytes().to_vec(),
285 public_key: self.public_key.as_bytes().to_vec(),
286 }
287 }
288
289 pub fn import(data: &IdentityData) -> Result<Self> {
291 let secret_key =
293 crate::quantum_crypto::saorsa_transport_integration::MlDsaSecretKey::from_bytes(
294 &data.secret_key,
295 )
296 .map_err(|e| {
297 P2PError::Identity(IdentityError::InvalidFormat(
298 format!("Invalid ML-DSA secret key: {e}").into(),
299 ))
300 })?;
301 let public_key =
302 crate::quantum_crypto::saorsa_transport_integration::MlDsaPublicKey::from_bytes(
303 &data.public_key,
304 )
305 .map_err(|e| {
306 P2PError::Identity(IdentityError::InvalidFormat(
307 format!("Invalid ML-DSA public key: {e}").into(),
308 ))
309 })?;
310
311 let peer_id = peer_id_from_public_key(&public_key);
312
313 Ok(Self {
314 secret_key,
315 public_key,
316 peer_id,
317 })
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324
325 #[test]
326 fn test_peer_id_generation() {
327 let (public_key, _secret_key) = crate::quantum_crypto::generate_ml_dsa_keypair()
328 .expect("ML-DSA key generation should succeed");
329 let peer_id = peer_id_from_public_key(&public_key);
330
331 assert_eq!(peer_id.to_bytes().len(), 32);
333
334 let peer_id2 = peer_id_from_public_key(&public_key);
336 assert_eq!(peer_id, peer_id2);
337 }
338
339 #[test]
340 fn test_xor_distance() {
341 let id1 = PeerId([0u8; 32]);
342 let mut id2_bytes = [0u8; 32];
343 id2_bytes[0] = 0xFF;
344 let id2 = PeerId(id2_bytes);
345
346 let distance = id1.xor_distance(&id2);
347 assert_eq!(distance[0], 0xFF);
348 for byte in &distance[1..] {
349 assert_eq!(*byte, 0);
350 }
351 }
352
353 #[test]
354 fn test_proof_of_work() {
355 }
357
358 #[test]
359 fn test_identity_generation() {
360 let identity = NodeIdentity::generate().expect("Identity generation should succeed");
361
362 let message = b"Hello, P2P!";
364 let signature = identity.sign(message).unwrap();
365 assert!(identity.verify(message, &signature).unwrap());
366
367 assert!(!identity.verify(b"Wrong message", &signature).unwrap());
369 }
370
371 #[test]
372 fn test_deterministic_generation() {
373 let seed = [0x42; 32];
374 let identity1 = NodeIdentity::from_seed(&seed).expect("Identity from seed should succeed");
375 let identity2 = NodeIdentity::from_seed(&seed).expect("Identity from seed should succeed");
376
377 assert_eq!(identity1.peer_id, identity2.peer_id);
379 assert_eq!(
380 identity1.public_key().as_bytes(),
381 identity2.public_key().as_bytes()
382 );
383 }
384
385 #[test]
386 fn test_identity_persistence() {
387 let identity = NodeIdentity::generate().expect("Identity generation should succeed");
388
389 let data = identity.export();
391
392 let imported = NodeIdentity::import(&data).expect("Import should succeed with valid data");
394
395 assert_eq!(identity.peer_id, imported.peer_id);
397 assert_eq!(
398 identity.public_key().as_bytes(),
399 imported.public_key().as_bytes()
400 );
401
402 let message = b"Test message";
404 let signature = imported.sign(message);
405 assert!(identity.verify(message, &signature.unwrap()).unwrap());
406 }
407
408 #[test]
409 fn test_peer_id_display_full_hex() {
410 let id = PeerId([0xAB; 32]);
411 let display = format!("{}", id);
412 assert_eq!(display.len(), 64);
413 assert_eq!(display, "ab".repeat(32));
414 }
415
416 #[test]
417 fn test_peer_id_ord() {
418 let a = PeerId([0x00; 32]);
419 let b = PeerId([0xFF; 32]);
420 assert!(a < b);
421 }
422
423 #[test]
424 fn test_peer_id_from_str() {
425 let hex = "ab".repeat(32);
426 let id: PeerId = hex.parse().expect("should parse valid hex");
427 assert_eq!(id.0, [0xAB; 32]);
428 }
429
430 #[test]
431 fn test_peer_id_json_roundtrip() {
432 let id = PeerId([0xAB; 32]);
433 let json = serde_json::to_string(&id).expect("serialize");
434 assert_eq!(json, format!("\"{}\"", "ab".repeat(32)));
435 let deserialized: PeerId = serde_json::from_str(&json).expect("deserialize");
436 assert_eq!(id, deserialized);
437 }
438
439 #[test]
440 fn test_peer_id_postcard_roundtrip() {
441 let id = PeerId([0xAB; 32]);
442 let bytes = postcard::to_stdvec(&id).expect("serialize");
443 let deserialized: PeerId = postcard::from_bytes(&bytes).expect("deserialize");
444 assert_eq!(id, deserialized);
445 }
446}