identity.rs (4349B)
1 use std::{ 2 collections::BTreeMap, 3 sync::{Mutex as StdMutex, OnceLock}, 4 }; 5 6 use anyhow::Result; 7 use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; 8 9 use crate::app::{GossipEnvelope, NETWORK_ID}; 10 11 use super::GossipNetwork; 12 13 static NODE_SIGNING_KEYS: OnceLock<StdMutex<BTreeMap<String, SigningKey>>> = OnceLock::new(); 14 15 pub(super) fn new_node_id() -> String { 16 let mut bytes = [0_u8; 32]; 17 getrandom::getrandom(&mut bytes).expect("secure randomness unavailable for p2p node id"); 18 let signing_key = SigningKey::from_bytes(&bytes); 19 let node_id = hex_encode(&signing_key.verifying_key().to_bytes()); 20 node_signing_keys() 21 .lock() 22 .expect("node signing key registry mutex poisoned") 23 .insert(node_id.clone(), signing_key); 24 node_id 25 } 26 27 fn node_signing_keys() -> &'static StdMutex<BTreeMap<String, SigningKey>> { 28 NODE_SIGNING_KEYS.get_or_init(|| StdMutex::new(BTreeMap::new())) 29 } 30 31 pub(super) fn hex_encode(bytes: &[u8]) -> String { 32 const HEX: &[u8; 16] = b"0123456789abcdef"; 33 let mut encoded = String::with_capacity(bytes.len() * 2); 34 for byte in bytes { 35 encoded.push(HEX[(byte >> 4) as usize] as char); 36 encoded.push(HEX[(byte & 0x0f) as usize] as char); 37 } 38 encoded 39 } 40 41 pub(super) fn decode_hex_array<const N: usize>(value: &str) -> Result<[u8; N]> { 42 if value.len() != N * 2 { 43 anyhow::bail!("hex value has {} chars, expected {}", value.len(), N * 2); 44 } 45 let mut bytes = [0_u8; N]; 46 for (index, chunk) in value.as_bytes().chunks_exact(2).enumerate() { 47 let high = hex_nibble(chunk[0])?; 48 let low = hex_nibble(chunk[1])?; 49 bytes[index] = (high << 4) | low; 50 } 51 Ok(bytes) 52 } 53 54 pub(super) fn hex_nibble(byte: u8) -> Result<u8> { 55 match byte { 56 b'0'..=b'9' => Ok(byte - b'0'), 57 b'a'..=b'f' => Ok(byte - b'a' + 10), 58 b'A'..=b'F' => Ok(byte - b'A' + 10), 59 _ => anyhow::bail!("invalid hex digit"), 60 } 61 } 62 63 pub(super) fn new_verification_nonce() -> String { 64 let mut bytes = [0_u8; 32]; 65 getrandom::getrandom(&mut bytes) 66 .expect("secure randomness unavailable for p2p verification nonce"); 67 hex_encode(&bytes) 68 } 69 70 pub(super) fn peer_verification_payload(address: &str, nonce: &str, node_id: &str) -> String { 71 format!("iuna-peer-verification:v1:{NETWORK_ID}:{node_id}:{address}:{nonce}") 72 } 73 74 pub(super) fn peer_verification_response( 75 network: &GossipNetwork, 76 address: &str, 77 nonce: &str, 78 ) -> Option<GossipEnvelope> { 79 peer_verification_response_for_node_id(&network.inner.node_id, address, nonce) 80 } 81 82 pub(super) fn peer_verification_response_for_node_id( 83 node_id: &str, 84 address: &str, 85 nonce: &str, 86 ) -> Option<GossipEnvelope> { 87 let keys = node_signing_keys() 88 .lock() 89 .expect("node signing key registry mutex poisoned"); 90 let signing_key = keys.get(node_id)?; 91 let payload = peer_verification_payload(address, nonce, node_id); 92 let signature: Signature = signing_key.sign(payload.as_bytes()); 93 Some(GossipEnvelope::PeerVerificationResponse { 94 address: address.to_string(), 95 nonce: nonce.to_string(), 96 node_id: node_id.to_string(), 97 signature: hex_encode(&signature.to_bytes()), 98 }) 99 } 100 101 pub(super) fn peer_verification_response_is_valid( 102 response_address: &str, 103 response_nonce: &str, 104 response_node_id: &str, 105 signature: &str, 106 expected_address: &str, 107 expected_nonce: &str, 108 expected_node_id: &str, 109 ) -> bool { 110 if response_address != expected_address 111 || response_nonce != expected_nonce 112 || response_node_id != expected_node_id 113 { 114 return false; 115 } 116 let public_key = match decode_hex_array::<32>(response_node_id) { 117 Ok(public_key) => public_key, 118 Err(_) => return false, 119 }; 120 let signature = match decode_hex_array::<64>(signature) { 121 Ok(signature) => Signature::from_bytes(&signature), 122 Err(_) => return false, 123 }; 124 let verifying_key = match VerifyingKey::from_bytes(&public_key) { 125 Ok(verifying_key) => verifying_key, 126 Err(_) => return false, 127 }; 128 verifying_key 129 .verify( 130 peer_verification_payload(expected_address, expected_nonce, expected_node_id) 131 .as_bytes(), 132 &signature, 133 ) 134 .is_ok() 135 }