1#[cfg(not(feature = "std"))]
47use alloc::vec::Vec;
48
49use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
50use rand_core::OsRng;
51
52use crate::NodeId;
53
54#[derive(Debug, Clone, PartialEq, Eq)]
56pub enum IdentityError {
57 InvalidSignature,
59 InvalidPublicKey,
61 InvalidPrivateKey,
63 SerializationError,
65}
66
67impl core::fmt::Display for IdentityError {
68 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69 match self {
70 Self::InvalidSignature => write!(f, "invalid signature"),
71 Self::InvalidPublicKey => write!(f, "invalid public key"),
72 Self::InvalidPrivateKey => write!(f, "invalid private key"),
73 Self::SerializationError => write!(f, "serialization error"),
74 }
75 }
76}
77
78#[cfg(feature = "std")]
79impl std::error::Error for IdentityError {}
80
81pub struct DeviceIdentity {
86 signing_key: SigningKey,
88}
89
90impl DeviceIdentity {
91 pub fn generate() -> Self {
95 let signing_key = SigningKey::generate(&mut OsRng);
96 Self { signing_key }
97 }
98
99 pub fn from_private_key(private_key: &[u8; 32]) -> Result<Self, IdentityError> {
108 let signing_key = SigningKey::from_bytes(private_key);
109 Ok(Self { signing_key })
110 }
111
112 pub fn private_key_bytes(&self) -> [u8; 32] {
117 self.signing_key.to_bytes()
118 }
119
120 pub fn public_key(&self) -> [u8; 32] {
124 self.signing_key.verifying_key().to_bytes()
125 }
126
127 pub fn verifying_key(&self) -> VerifyingKey {
129 self.signing_key.verifying_key()
130 }
131
132 pub fn node_id(&self) -> NodeId {
140 let public_key = self.public_key();
141 let hash = blake3::hash(&public_key);
142 let hash_bytes = hash.as_bytes();
143
144 let id = u32::from_le_bytes([hash_bytes[0], hash_bytes[1], hash_bytes[2], hash_bytes[3]]);
146
147 NodeId::new(id)
148 }
149
150 pub fn sign(&self, message: &[u8]) -> [u8; 64] {
158 let signature = self.signing_key.sign(message);
159 signature.to_bytes()
160 }
161
162 pub fn verify(&self, message: &[u8], signature: &[u8; 64]) -> bool {
171 let sig = Signature::from_bytes(signature);
172 self.signing_key
173 .verifying_key()
174 .verify(message, &sig)
175 .is_ok()
176 }
177
178 pub fn create_attestation(&self, timestamp_ms: u64) -> IdentityAttestation {
183 let node_id = self.node_id();
184 let public_key = self.public_key();
185
186 let mut message = Vec::with_capacity(4 + 32 + 8);
188 message.extend_from_slice(&node_id.as_u32().to_le_bytes());
189 message.extend_from_slice(&public_key);
190 message.extend_from_slice(×tamp_ms.to_le_bytes());
191
192 let signature = self.sign(&message);
193
194 IdentityAttestation {
195 node_id,
196 public_key,
197 timestamp_ms,
198 signature,
199 }
200 }
201}
202
203impl Clone for DeviceIdentity {
204 fn clone(&self) -> Self {
205 Self {
206 signing_key: SigningKey::from_bytes(&self.signing_key.to_bytes()),
207 }
208 }
209}
210
211impl core::fmt::Debug for DeviceIdentity {
212 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
213 f.debug_struct("DeviceIdentity")
214 .field("node_id", &self.node_id())
215 .field("public_key", &hex_short(&self.public_key()))
216 .field("private_key", &"[REDACTED]")
217 .finish()
218 }
219}
220
221#[derive(Debug, Clone, PartialEq, Eq)]
225pub struct IdentityAttestation {
226 pub node_id: NodeId,
228 pub public_key: [u8; 32],
230 pub timestamp_ms: u64,
232 pub signature: [u8; 64],
234}
235
236impl IdentityAttestation {
237 pub fn verify(&self) -> bool {
245 let hash = blake3::hash(&self.public_key);
247 let hash_bytes = hash.as_bytes();
248 let expected_id =
249 u32::from_le_bytes([hash_bytes[0], hash_bytes[1], hash_bytes[2], hash_bytes[3]]);
250
251 if self.node_id.as_u32() != expected_id {
252 return false;
253 }
254
255 let verifying_key = match VerifyingKey::from_bytes(&self.public_key) {
257 Ok(k) => k,
258 Err(_) => return false,
259 };
260
261 let signature = Signature::from_bytes(&self.signature);
262
263 let mut message = Vec::with_capacity(4 + 32 + 8);
265 message.extend_from_slice(&self.node_id.as_u32().to_le_bytes());
266 message.extend_from_slice(&self.public_key);
267 message.extend_from_slice(&self.timestamp_ms.to_le_bytes());
268
269 verifying_key.verify(&message, &signature).is_ok()
270 }
271
272 pub fn encode(&self) -> Vec<u8> {
276 let mut buf = Vec::with_capacity(108);
277 buf.extend_from_slice(&self.node_id.as_u32().to_le_bytes());
278 buf.extend_from_slice(&self.public_key);
279 buf.extend_from_slice(&self.timestamp_ms.to_le_bytes());
280 buf.extend_from_slice(&self.signature);
281 buf
282 }
283
284 pub fn decode(data: &[u8]) -> Option<Self> {
288 if data.len() != 108 {
289 return None;
290 }
291
292 let node_id = NodeId::new(u32::from_le_bytes([data[0], data[1], data[2], data[3]]));
293
294 let mut public_key = [0u8; 32];
295 public_key.copy_from_slice(&data[4..36]);
296
297 let timestamp_ms = u64::from_le_bytes([
298 data[36], data[37], data[38], data[39], data[40], data[41], data[42], data[43],
299 ]);
300
301 let mut signature = [0u8; 64];
302 signature.copy_from_slice(&data[44..108]);
303
304 Some(Self {
305 node_id,
306 public_key,
307 timestamp_ms,
308 signature,
309 })
310 }
311}
312
313pub fn verify_signature(public_key: &[u8; 32], message: &[u8], signature: &[u8; 64]) -> bool {
317 let verifying_key = match VerifyingKey::from_bytes(public_key) {
318 Ok(k) => k,
319 Err(_) => return false,
320 };
321
322 let sig = Signature::from_bytes(signature);
323
324 verifying_key.verify(message, &sig).is_ok()
325}
326
327pub fn node_id_from_public_key(public_key: &[u8; 32]) -> NodeId {
331 let hash = blake3::hash(public_key);
332 let hash_bytes = hash.as_bytes();
333
334 let id = u32::from_le_bytes([hash_bytes[0], hash_bytes[1], hash_bytes[2], hash_bytes[3]]);
335
336 NodeId::new(id)
337}
338
339fn hex_short(bytes: &[u8]) -> String {
341 if bytes.len() <= 4 {
342 hex::encode(bytes)
343 } else {
344 format!(
345 "{}..{}",
346 hex::encode(&bytes[..2]),
347 hex::encode(&bytes[bytes.len() - 2..])
348 )
349 }
350}
351
352mod hex {
354 pub fn encode(bytes: &[u8]) -> String {
355 bytes.iter().map(|b| format!("{:02x}", b)).collect()
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362
363 #[test]
364 fn test_generate_identity() {
365 let identity = DeviceIdentity::generate();
366
367 assert_ne!(identity.node_id().as_u32(), 0);
369
370 assert_eq!(identity.public_key().len(), 32);
372 }
373
374 #[test]
375 fn test_identity_from_private_key() {
376 let identity1 = DeviceIdentity::generate();
377 let private_key = identity1.private_key_bytes();
378
379 let identity2 = DeviceIdentity::from_private_key(&private_key).unwrap();
380
381 assert_eq!(identity1.public_key(), identity2.public_key());
383 assert_eq!(identity1.node_id(), identity2.node_id());
384 }
385
386 #[test]
387 fn test_node_id_deterministic() {
388 let identity = DeviceIdentity::generate();
389
390 let id1 = identity.node_id();
392 let id2 = identity.node_id();
393 assert_eq!(id1, id2);
394 }
395
396 #[test]
397 fn test_different_identities_different_node_ids() {
398 let identity1 = DeviceIdentity::generate();
399 let identity2 = DeviceIdentity::generate();
400
401 assert_ne!(identity1.node_id(), identity2.node_id());
404 }
405
406 #[test]
407 fn test_sign_verify() {
408 let identity = DeviceIdentity::generate();
409 let message = b"Test message for signing";
410
411 let signature = identity.sign(message);
412 assert!(identity.verify(message, &signature));
413 }
414
415 #[test]
416 fn test_verify_wrong_message() {
417 let identity = DeviceIdentity::generate();
418 let message = b"Original message";
419 let wrong_message = b"Wrong message";
420
421 let signature = identity.sign(message);
422 assert!(!identity.verify(wrong_message, &signature));
423 }
424
425 #[test]
426 fn test_verify_wrong_key() {
427 let identity1 = DeviceIdentity::generate();
428 let identity2 = DeviceIdentity::generate();
429 let message = b"Test message";
430
431 let signature = identity1.sign(message);
432 assert!(!identity2.verify(message, &signature));
433 }
434
435 #[test]
436 fn test_attestation_create_verify() {
437 let identity = DeviceIdentity::generate();
438 let timestamp = 1705680000000u64; let attestation = identity.create_attestation(timestamp);
441
442 assert!(attestation.verify());
443 assert_eq!(attestation.node_id, identity.node_id());
444 assert_eq!(attestation.public_key, identity.public_key());
445 assert_eq!(attestation.timestamp_ms, timestamp);
446 }
447
448 #[test]
449 fn test_attestation_encode_decode() {
450 let identity = DeviceIdentity::generate();
451 let attestation = identity.create_attestation(1705680000000);
452
453 let encoded = attestation.encode();
454 assert_eq!(encoded.len(), 108);
455
456 let decoded = IdentityAttestation::decode(&encoded).unwrap();
457 assert_eq!(decoded, attestation);
458 assert!(decoded.verify());
459 }
460
461 #[test]
462 fn test_attestation_tampered() {
463 let identity = DeviceIdentity::generate();
464 let mut attestation = identity.create_attestation(1705680000000);
465
466 attestation.timestamp_ms += 1;
468
469 assert!(!attestation.verify());
471 }
472
473 #[test]
474 fn test_node_id_from_public_key() {
475 let identity = DeviceIdentity::generate();
476 let public_key = identity.public_key();
477
478 let derived_id = node_id_from_public_key(&public_key);
479 assert_eq!(derived_id, identity.node_id());
480 }
481
482 #[test]
483 fn test_verify_signature_utility() {
484 let identity = DeviceIdentity::generate();
485 let message = b"Test with utility function";
486
487 let signature = identity.sign(message);
488 let public_key = identity.public_key();
489
490 assert!(verify_signature(&public_key, message, &signature));
491 }
492
493 #[test]
494 fn test_identity_clone() {
495 let identity1 = DeviceIdentity::generate();
496 let identity2 = identity1.clone();
497
498 assert_eq!(identity1.public_key(), identity2.public_key());
499 assert_eq!(identity1.node_id(), identity2.node_id());
500
501 let message = b"Clone test";
503 let sig1 = identity1.sign(message);
504 let sig2 = identity2.sign(message);
505
506 assert!(identity1.verify(message, &sig2));
507 assert!(identity2.verify(message, &sig1));
508 }
509
510 #[test]
511 fn test_debug_redacts_private_key() {
512 let identity = DeviceIdentity::generate();
513 let debug_str = format!("{:?}", identity);
514
515 assert!(debug_str.contains("REDACTED"));
516 assert!(debug_str.contains("node_id"));
517 }
518}