pot_o_extensions/
peer_auth.rs1use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};
2
3pub struct NodeIdentity {
4 signing_key: SigningKey,
5 verifying_key: VerifyingKey,
6 node_id: String,
7}
8
9impl NodeIdentity {
10 pub fn new(node_id: String) -> Self {
11 let signing_key = SigningKey::generate(&mut rand::rngs::OsRng);
12 let verifying_key = signing_key.verifying_key();
13 Self {
14 signing_key,
15 verifying_key,
16 node_id,
17 }
18 }
19
20 pub fn from_bytes(node_id: String, secret_bytes: [u8; 32]) -> Self {
21 let signing_key = SigningKey::from_bytes(&secret_bytes);
22 let verifying_key = signing_key.verifying_key();
23 Self {
24 signing_key,
25 verifying_key,
26 node_id,
27 }
28 }
29
30 pub fn sign_message(&self, message: &[u8]) -> Vec<u8> {
31 self.signing_key.sign(message).to_bytes().to_vec()
32 }
33
34 pub fn node_id(&self) -> &str {
35 &self.node_id
36 }
37
38 pub fn public_key_bytes(&self) -> [u8; 32] {
39 self.verifying_key.to_bytes()
40 }
41}
42
43pub fn verify_peer_signature(peer_pubkey: &[u8; 32], message: &[u8], signature: &[u8]) -> bool {
44 if signature.len() != 64 {
45 return false;
46 }
47 let Ok(verifying_key) = VerifyingKey::from_bytes(peer_pubkey) else {
48 return false;
49 };
50 let sig_bytes: &[u8; 64] = match signature.try_into() {
51 Ok(b) => b,
52 Err(_) => return false,
53 };
54 let sig = Signature::from_bytes(sig_bytes);
55 verifying_key.verify_strict(message, &sig).is_ok()
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn test_sign_and_verify() {
64 let identity = NodeIdentity::new("test-node".to_string());
65 let message = b"hello world";
66 let signature = identity.sign_message(message);
67 assert!(verify_peer_signature(
68 &identity.public_key_bytes(),
69 message,
70 &signature
71 ));
72 }
73
74 #[test]
75 fn test_verify_rejects_tampered_message() {
76 let identity = NodeIdentity::new("test-node".to_string());
77 let signature = identity.sign_message(b"hello");
78 assert!(!verify_peer_signature(
79 &identity.public_key_bytes(),
80 b"world",
81 &signature
82 ));
83 }
84
85 #[test]
86 fn test_from_bytes_roundtrip() {
87 let identity = NodeIdentity::new("node-a".to_string());
88 let secret_bytes = identity.signing_key.to_bytes();
89 let restored = NodeIdentity::from_bytes("node-a".to_string(), secret_bytes);
90 assert_eq!(restored.public_key_bytes(), identity.public_key_bytes());
91 let msg = b"test message";
92 let sig = restored.sign_message(msg);
93 assert!(verify_peer_signature(
94 &identity.public_key_bytes(),
95 msg,
96 &sig
97 ));
98 }
99
100 #[test]
101 fn test_verify_rejects_wrong_key() {
102 let identity_a = NodeIdentity::new("a".to_string());
103 let identity_b = NodeIdentity::new("b".to_string());
104 let msg = b"hello";
105 let sig = identity_a.sign_message(msg);
106 assert!(!verify_peer_signature(
107 &identity_b.public_key_bytes(),
108 msg,
109 &sig
110 ));
111 }
112
113 #[test]
114 fn test_verify_rejects_short_signature() {
115 let identity = NodeIdentity::new("test".to_string());
116 assert!(!verify_peer_signature(
117 &identity.public_key_bytes(),
118 b"msg",
119 &[0u8; 32]
120 ));
121 }
122}