Skip to main content

pot_o_extensions/
peer_auth.rs

1use 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}