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
24#[derive(Clone)]
25pub struct Ed25519PrivateKey {
26    inner: ed25519_dalek::SigningKey,
27}
28
29#[derive(Clone)]
30pub struct Ed25519PublicKey {
31    inner: ed25519_dalek::VerifyingKey,
32}
33
34impl Ed25519PrivateKey {
35    pub fn from_bytes(seed: &[u8; 32]) -> Self {
36        Ed25519PrivateKey {
37            inner: ed25519_dalek::SigningKey::from_bytes(seed),
38        }
39    }
40
41    pub fn generate(rng: &mut dyn Rng) -> Self {
42        let mut seed = [0u8; 32];
43        rng.fill_bytes(&mut seed);
44        Self::from_bytes(&seed)
45    }
46
47    pub fn private_bytes(&self) -> [u8; 32] {
48        self.inner.to_bytes()
49    }
50
51    pub fn public_key(&self) -> Ed25519PublicKey {
52        Ed25519PublicKey {
53            inner: self.inner.verifying_key(),
54        }
55    }
56
57    pub fn sign(&self, message: &[u8]) -> [u8; 64] {
58        #[cfg(all(
59            feature = "ed25519-aws-lc",
60            any(target_arch = "x86_64", target_arch = "aarch64")
61        ))]
62        if let Some(signature) = self.sign_aws_lc(message) {
63            return signature;
64        }
65        self.inner.sign(message).to_bytes()
66    }
67
68    /// The same signature from aws-lc, which signs in about half the time
69    /// (Ed25519 signatures are deterministic, so the bytes are identical).
70    #[cfg(all(
71        feature = "ed25519-aws-lc",
72        any(target_arch = "x86_64", target_arch = "aarch64")
73    ))]
74    fn sign_aws_lc(&self, message: &[u8]) -> Option<[u8; 64]> {
75        crate::aws_lc_init();
76        // aws-lc's private key form: the seed, then the public key.
77        let mut key = zeroize::Zeroizing::new([0u8; 64]);
78        key[..32].copy_from_slice(self.inner.as_bytes());
79        key[32..].copy_from_slice(self.inner.verifying_key().as_bytes());
80        let mut signature = [0u8; 64];
81        // SAFETY: `signature` and `key` are 64 bytes and `message` is
82        // `message.len()` bytes, as the function takes.
83        let ok = unsafe {
84            aws_lc_sys::ED25519_sign(
85                signature.as_mut_ptr(),
86                message.as_ptr(),
87                message.len(),
88                key.as_ptr(),
89            )
90        };
91        (ok == 1).then_some(signature)
92    }
93}
94
95impl Ed25519PublicKey {
96    /// Decode a compressed Ed25519 public key.
97    ///
98    /// Not every 32-byte string is a valid point encoding; such input
99    /// returns `Ed25519Error::InvalidPublicKey`.
100    pub fn from_bytes(data: &[u8; 32]) -> Result<Self, Ed25519Error> {
101        ed25519_dalek::VerifyingKey::from_bytes(data)
102            .map(|inner| Ed25519PublicKey { inner })
103            .map_err(|_| Ed25519Error::InvalidPublicKey)
104    }
105
106    pub fn public_bytes(&self) -> [u8; 32] {
107        self.inner.to_bytes()
108    }
109
110    pub fn verify(&self, signature: &[u8; 64], message: &[u8]) -> bool {
111        let sig = ed25519_dalek::Signature::from_bytes(signature);
112        self.inner.verify(message, &sig).is_ok()
113    }
114}
115
116/// Check several signatures at once: true only when every one is valid.
117///
118/// One multiscalar multiplication covers the whole batch, so a batch costs
119/// a fraction of checking each signature with [`Ed25519PublicKey::verify`].
120/// A false result does not say which signature failed; check them one by
121/// one to find out. The batch equation is the cofactored one, so a
122/// signature crafted with small-order components can pass here and fail
123/// [`Ed25519PublicKey::verify`]: only its signer can make one, and an honest
124/// signature passes both.
125#[cfg(feature = "ed25519-batch")]
126pub fn verify_batch(items: &[(&Ed25519PublicKey, &[u8; 64], &[u8])]) -> bool {
127    use alloc::vec::Vec;
128    let messages: Vec<&[u8]> = items.iter().map(|(_, _, message)| *message).collect();
129    let signatures: Vec<ed25519_dalek::Signature> = items
130        .iter()
131        .map(|(_, signature, _)| ed25519_dalek::Signature::from_bytes(signature))
132        .collect();
133    let keys: Vec<ed25519_dalek::VerifyingKey> =
134        items.iter().map(|(key, _, _)| key.inner).collect();
135    ed25519_dalek::verify_batch(&messages, &signatures, &keys).is_ok()
136}
137
138#[cfg(test)]
139mod tests {
140
141    #[cfg(all(
142        feature = "ed25519-aws-lc",
143        any(target_arch = "x86_64", target_arch = "aarch64")
144    ))]
145    #[test]
146    fn aws_lc_signs_the_same_bytes_as_dalek() {
147        use ed25519_dalek::Signer;
148        for seed in 0u8..32 {
149            let key = Ed25519PrivateKey::from_bytes(&[seed.wrapping_mul(37).wrapping_add(1); 32]);
150            for len in [0usize, 1, 32, 64, 500] {
151                let message: Vec<u8> = (0..len).map(|i| (i as u8) ^ seed).collect();
152                let expected = key.inner.sign(&message).to_bytes();
153                assert_eq!(key.sign_aws_lc(&message), Some(expected));
154                assert_eq!(key.sign(&message), expected);
155                assert!(key.public_key().verify(&expected, &message));
156            }
157        }
158    }
159
160    use super::*;
161
162    #[cfg(feature = "ed25519-batch")]
163    #[test]
164    fn batch_accepts_valid_signatures_and_rejects_any_bad_one() {
165        let keys: alloc::vec::Vec<Ed25519PrivateKey> = (0u8..8)
166            .map(|i| Ed25519PrivateKey::from_bytes(&[i + 1; 32]))
167            .collect();
168        let messages: alloc::vec::Vec<[u8; 32]> = (0u8..8).map(|i| [i; 32]).collect();
169        let publics: alloc::vec::Vec<Ed25519PublicKey> =
170            keys.iter().map(|key| key.public_key()).collect();
171        let mut signatures: alloc::vec::Vec<[u8; 64]> = keys
172            .iter()
173            .zip(&messages)
174            .map(|(key, message)| key.sign(message))
175            .collect();
176        let items = |signatures: &[[u8; 64]]| -> bool {
177            let batch: alloc::vec::Vec<(&Ed25519PublicKey, &[u8; 64], &[u8])> = publics
178                .iter()
179                .zip(signatures)
180                .zip(&messages)
181                .map(|((key, signature), message)| (key, signature, &message[..]))
182                .collect();
183            verify_batch(&batch)
184        };
185        assert!(items(&signatures));
186        assert!(verify_batch(&[]));
187        signatures[5][0] ^= 1;
188        assert!(!items(&signatures));
189    }
190
191    struct Rfc8032Vector {
192        seed: &'static str,
193        public_key: &'static str,
194        message: &'static str,
195        signature: &'static str,
196    }
197
198    const RFC8032_VECTORS: &[Rfc8032Vector] = &[
199        Rfc8032Vector {
200            seed: "9d61b19deffd5a60ba844af492ec2cc4\
201                   4449c5697b326919703bac031cae7f60",
202            public_key: "d75a980182b10ab7d54bfed3c964073a\
203                         0ee172f3daa62325af021a68f707511a",
204            message: "",
205            signature: "e5564300c360ac729086e2cc806e828a\
206                        84877f1eb8e5d974d873e06522490155\
207                        5fb8821590a33bacc61e39701cf9b46b\
208                        d25bf5f0595bbe24655141438e7a100b",
209        },
210        Rfc8032Vector {
211            seed: "4ccd089b28ff96da9db6c346ec114e0f\
212                   5b8a319f35aba624da8cf6ed4fb8a6fb",
213            public_key: "3d4017c3e843895a92b70aa74d1b7ebc\
214                         9c982ccf2ec4968cc0cd55f12af4660c",
215            message: "72",
216            signature: "92a009a9f0d4cab8720e820b5f642540\
217                        a2b27b5416503f8fb3762223ebdb69da\
218                        085ac1e43e15996e458f3613d0f11d8c\
219                        387b2eaeb4302aeeb00d291612bb0c00",
220        },
221        Rfc8032Vector {
222            seed: "c5aa8df43f9f837bedb7442f31dcb7b1\
223                   66d38535076f094b85ce3a2e0b4458f7",
224            public_key: "fc51cd8e6218a1a38da47ed00230f058\
225                         0816ed13ba3303ac5deb911548908025",
226            message: "af82",
227            signature: "6291d657deec24024827e69c3abe01a3\
228                        0ce548a284743a445e3680d7db5ac3ac\
229                        18ff9b538d16f290ae67f760984dc659\
230                        4a7c15e9716ed28dc027beceea1ec40a",
231        },
232    ];
233
234    #[test]
235    fn test_ed25519_sign_verify_roundtrip() {
236        let seed = [42u8; 32];
237        let key = Ed25519PrivateKey::from_bytes(&seed);
238        let pubkey = key.public_key();
239        let msg = b"Hello, Ed25519!";
240        let sig = key.sign(msg);
241        assert!(pubkey.verify(&sig, msg));
242    }
243
244    #[test]
245    fn test_ed25519_verify_tampered() {
246        let seed = [42u8; 32];
247        let key = Ed25519PrivateKey::from_bytes(&seed);
248        let pubkey = key.public_key();
249        let msg = b"Hello, Ed25519!";
250        let sig = key.sign(msg);
251        assert!(!pubkey.verify(&sig, b"Hello, Ed25519?"));
252    }
253
254    #[test]
255    fn test_ed25519_pubkey_deterministic() {
256        let seed = [1u8; 32];
257        let key1 = Ed25519PrivateKey::from_bytes(&seed);
258        let key2 = Ed25519PrivateKey::from_bytes(&seed);
259        assert_eq!(
260            key1.public_key().public_bytes(),
261            key2.public_key().public_bytes()
262        );
263    }
264
265    #[test]
266    fn test_ed25519_rfc8032_known_answers() {
267        for vector in RFC8032_VECTORS {
268            let seed: [u8; 32] = decode_hex(vector.seed).try_into().unwrap();
269            let expected_public: [u8; 32] = decode_hex(vector.public_key).try_into().unwrap();
270            let message = decode_hex(vector.message);
271            let expected_signature: [u8; 64] = decode_hex(vector.signature).try_into().unwrap();
272            let key = Ed25519PrivateKey::from_bytes(&seed);
273
274            assert_eq!(key.private_bytes(), seed);
275            assert_eq!(key.public_key().public_bytes(), expected_public);
276            assert_eq!(key.sign(&message), expected_signature);
277            assert!(key.public_key().verify(&expected_signature, &message));
278            assert!(Ed25519PublicKey::from_bytes(&expected_public)
279                .unwrap()
280                .verify(&expected_signature, &message));
281        }
282    }
283
284    #[test]
285    fn test_ed25519_rejects_signature_and_message_mutations() {
286        let key = Ed25519PrivateKey::from_bytes(&[0x42; 32]);
287        let public = key.public_key();
288        let message = b"Reticulum signature compatibility";
289        let signature = key.sign(message);
290
291        for index in [0, 31, 32, 63] {
292            let mut mutated = signature;
293            mutated[index] ^= 0x80;
294            assert!(!public.verify(&mutated, message));
295        }
296
297        let mut mutated_message = *b"Reticulum signature compatibility";
298        mutated_message[0] ^= 1;
299        assert!(!public.verify(&signature, &mutated_message));
300    }
301
302    #[test]
303    fn test_ed25519_rejects_noncanonical_scalar() {
304        let key = Ed25519PrivateKey::from_bytes(&[0x24; 32]);
305        let public = key.public_key();
306        let message = b"noncanonical signature scalar";
307        let mut signature = key.sign(message);
308
309        signature[32..].copy_from_slice(&[
310            0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9,
311            0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
312            0x00, 0x00, 0x00, 0x10,
313        ]);
314
315        assert!(!public.verify(&signature, message));
316    }
317
318    #[test]
319    fn test_ed25519_invalid_public_key_bytes_return_error() {
320        // y = 2 has no corresponding x on the curve, so this is not a valid
321        // compressed point encoding.
322        let mut invalid = [0u8; 32];
323        invalid[0] = 2;
324        assert!(matches!(
325            Ed25519PublicKey::from_bytes(&invalid),
326            Err(Ed25519Error::InvalidPublicKey)
327        ));
328    }
329
330    #[test]
331    fn test_ed25519_valid_public_key_bytes_roundtrip() {
332        let public = Ed25519PrivateKey::from_bytes(&[7; 32]).public_key();
333        let bytes = public.public_bytes();
334        let decoded = Ed25519PublicKey::from_bytes(&bytes).unwrap();
335        assert_eq!(decoded.public_bytes(), bytes);
336    }
337
338    fn decode_hex(hex: &str) -> alloc::vec::Vec<u8> {
339        let compact: alloc::string::String = hex.chars().filter(|c| !c.is_whitespace()).collect();
340        (0..compact.len())
341            .step_by(2)
342            .map(|index| u8::from_str_radix(&compact[index..index + 2], 16).unwrap())
343            .collect()
344    }
345}