Skip to main content

rns_crypto/ed25519/
mod.rs

1use core::fmt;
2
3use crate::Rng;
4use ed25519_dalek::Signer;
5use ed25519_dalek::Verifier;
6
7#[derive(Debug, PartialEq)]
8pub enum Ed25519Error {
9    /// The 32 bytes do not decode to a valid compressed Edwards point.
10    InvalidPublicKey,
11}
12
13impl fmt::Display for Ed25519Error {
14    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15        match self {
16            Ed25519Error::InvalidPublicKey => write!(f, "Invalid Ed25519 public key bytes"),
17        }
18    }
19}
20
21#[cfg(feature = "std")]
22impl std::error::Error for Ed25519Error {}
23
24pub struct Ed25519PrivateKey {
25    inner: ed25519_dalek::SigningKey,
26}
27
28pub struct Ed25519PublicKey {
29    inner: ed25519_dalek::VerifyingKey,
30}
31
32impl Ed25519PrivateKey {
33    pub fn from_bytes(seed: &[u8; 32]) -> Self {
34        Ed25519PrivateKey {
35            inner: ed25519_dalek::SigningKey::from_bytes(seed),
36        }
37    }
38
39    pub fn generate(rng: &mut dyn Rng) -> Self {
40        let mut seed = [0u8; 32];
41        rng.fill_bytes(&mut seed);
42        Self::from_bytes(&seed)
43    }
44
45    pub fn private_bytes(&self) -> [u8; 32] {
46        self.inner.to_bytes()
47    }
48
49    pub fn public_key(&self) -> Ed25519PublicKey {
50        Ed25519PublicKey {
51            inner: self.inner.verifying_key(),
52        }
53    }
54
55    pub fn sign(&self, message: &[u8]) -> [u8; 64] {
56        self.inner.sign(message).to_bytes()
57    }
58}
59
60impl Ed25519PublicKey {
61    /// Decode a compressed Ed25519 public key.
62    ///
63    /// Not every 32-byte string is a valid point encoding; such input
64    /// returns `Ed25519Error::InvalidPublicKey`.
65    pub fn from_bytes(data: &[u8; 32]) -> Result<Self, Ed25519Error> {
66        ed25519_dalek::VerifyingKey::from_bytes(data)
67            .map(|inner| Ed25519PublicKey { inner })
68            .map_err(|_| Ed25519Error::InvalidPublicKey)
69    }
70
71    pub fn public_bytes(&self) -> [u8; 32] {
72        self.inner.to_bytes()
73    }
74
75    pub fn verify(&self, signature: &[u8; 64], message: &[u8]) -> bool {
76        let sig = ed25519_dalek::Signature::from_bytes(signature);
77        self.inner.verify(message, &sig).is_ok()
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    struct Rfc8032Vector {
86        seed: &'static str,
87        public_key: &'static str,
88        message: &'static str,
89        signature: &'static str,
90    }
91
92    const RFC8032_VECTORS: &[Rfc8032Vector] = &[
93        Rfc8032Vector {
94            seed: "9d61b19deffd5a60ba844af492ec2cc4\
95                   4449c5697b326919703bac031cae7f60",
96            public_key: "d75a980182b10ab7d54bfed3c964073a\
97                         0ee172f3daa62325af021a68f707511a",
98            message: "",
99            signature: "e5564300c360ac729086e2cc806e828a\
100                        84877f1eb8e5d974d873e06522490155\
101                        5fb8821590a33bacc61e39701cf9b46b\
102                        d25bf5f0595bbe24655141438e7a100b",
103        },
104        Rfc8032Vector {
105            seed: "4ccd089b28ff96da9db6c346ec114e0f\
106                   5b8a319f35aba624da8cf6ed4fb8a6fb",
107            public_key: "3d4017c3e843895a92b70aa74d1b7ebc\
108                         9c982ccf2ec4968cc0cd55f12af4660c",
109            message: "72",
110            signature: "92a009a9f0d4cab8720e820b5f642540\
111                        a2b27b5416503f8fb3762223ebdb69da\
112                        085ac1e43e15996e458f3613d0f11d8c\
113                        387b2eaeb4302aeeb00d291612bb0c00",
114        },
115        Rfc8032Vector {
116            seed: "c5aa8df43f9f837bedb7442f31dcb7b1\
117                   66d38535076f094b85ce3a2e0b4458f7",
118            public_key: "fc51cd8e6218a1a38da47ed00230f058\
119                         0816ed13ba3303ac5deb911548908025",
120            message: "af82",
121            signature: "6291d657deec24024827e69c3abe01a3\
122                        0ce548a284743a445e3680d7db5ac3ac\
123                        18ff9b538d16f290ae67f760984dc659\
124                        4a7c15e9716ed28dc027beceea1ec40a",
125        },
126    ];
127
128    #[test]
129    fn test_ed25519_sign_verify_roundtrip() {
130        let seed = [42u8; 32];
131        let key = Ed25519PrivateKey::from_bytes(&seed);
132        let pubkey = key.public_key();
133        let msg = b"Hello, Ed25519!";
134        let sig = key.sign(msg);
135        assert!(pubkey.verify(&sig, msg));
136    }
137
138    #[test]
139    fn test_ed25519_verify_tampered() {
140        let seed = [42u8; 32];
141        let key = Ed25519PrivateKey::from_bytes(&seed);
142        let pubkey = key.public_key();
143        let msg = b"Hello, Ed25519!";
144        let sig = key.sign(msg);
145        assert!(!pubkey.verify(&sig, b"Hello, Ed25519?"));
146    }
147
148    #[test]
149    fn test_ed25519_pubkey_deterministic() {
150        let seed = [1u8; 32];
151        let key1 = Ed25519PrivateKey::from_bytes(&seed);
152        let key2 = Ed25519PrivateKey::from_bytes(&seed);
153        assert_eq!(
154            key1.public_key().public_bytes(),
155            key2.public_key().public_bytes()
156        );
157    }
158
159    #[test]
160    fn test_ed25519_rfc8032_known_answers() {
161        for vector in RFC8032_VECTORS {
162            let seed: [u8; 32] = decode_hex(vector.seed).try_into().unwrap();
163            let expected_public: [u8; 32] = decode_hex(vector.public_key).try_into().unwrap();
164            let message = decode_hex(vector.message);
165            let expected_signature: [u8; 64] = decode_hex(vector.signature).try_into().unwrap();
166            let key = Ed25519PrivateKey::from_bytes(&seed);
167
168            assert_eq!(key.private_bytes(), seed);
169            assert_eq!(key.public_key().public_bytes(), expected_public);
170            assert_eq!(key.sign(&message), expected_signature);
171            assert!(key.public_key().verify(&expected_signature, &message));
172            assert!(Ed25519PublicKey::from_bytes(&expected_public)
173                .unwrap()
174                .verify(&expected_signature, &message));
175        }
176    }
177
178    #[test]
179    fn test_ed25519_rejects_signature_and_message_mutations() {
180        let key = Ed25519PrivateKey::from_bytes(&[0x42; 32]);
181        let public = key.public_key();
182        let message = b"Reticulum signature compatibility";
183        let signature = key.sign(message);
184
185        for index in [0, 31, 32, 63] {
186            let mut mutated = signature;
187            mutated[index] ^= 0x80;
188            assert!(!public.verify(&mutated, message));
189        }
190
191        let mut mutated_message = *b"Reticulum signature compatibility";
192        mutated_message[0] ^= 1;
193        assert!(!public.verify(&signature, &mutated_message));
194    }
195
196    #[test]
197    fn test_ed25519_rejects_noncanonical_scalar() {
198        let key = Ed25519PrivateKey::from_bytes(&[0x24; 32]);
199        let public = key.public_key();
200        let message = b"noncanonical signature scalar";
201        let mut signature = key.sign(message);
202
203        signature[32..].copy_from_slice(&[
204            0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9,
205            0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
206            0x00, 0x00, 0x00, 0x10,
207        ]);
208
209        assert!(!public.verify(&signature, message));
210    }
211
212    #[test]
213    fn test_ed25519_invalid_public_key_bytes_return_error() {
214        // y = 2 has no corresponding x on the curve, so this is not a valid
215        // compressed point encoding.
216        let mut invalid = [0u8; 32];
217        invalid[0] = 2;
218        assert!(matches!(
219            Ed25519PublicKey::from_bytes(&invalid),
220            Err(Ed25519Error::InvalidPublicKey)
221        ));
222    }
223
224    #[test]
225    fn test_ed25519_valid_public_key_bytes_roundtrip() {
226        let public = Ed25519PrivateKey::from_bytes(&[7; 32]).public_key();
227        let bytes = public.public_bytes();
228        let decoded = Ed25519PublicKey::from_bytes(&bytes).unwrap();
229        assert_eq!(decoded.public_bytes(), bytes);
230    }
231
232    fn decode_hex(hex: &str) -> alloc::vec::Vec<u8> {
233        let compact: alloc::string::String = hex.chars().filter(|c| !c.is_whitespace()).collect();
234        (0..compact.len())
235            .step_by(2)
236            .map(|index| u8::from_str_radix(&compact[index..index + 2], 16).unwrap())
237            .collect()
238    }
239}