rns_crypto/ed25519/
mod.rs1use core::fmt;
2
3use crate::Rng;
4use ed25519_dalek::Signer;
5use ed25519_dalek::Verifier;
6
7#[derive(Debug, PartialEq)]
8pub enum Ed25519Error {
9 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 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 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}