everscale_crypto/ed25519/
mod.rs

1use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint};
2use curve25519_dalek::scalar::Scalar;
3use rand::Rng;
4use sha2::{Digest, Sha512};
5
6/// Ed25519 key pair
7#[derive(Copy, Clone)]
8pub struct KeyPair {
9    pub secret_key: ExpandedSecretKey,
10    pub public_key: PublicKey,
11}
12
13impl KeyPair {
14    /// Generates new Ed25519 key pair
15    #[inline(always)]
16    pub fn generate(rng: &mut impl Rng) -> Self {
17        Self::from(&SecretKey::generate(rng))
18    }
19
20    /// Signs a serialized TL representation of data
21    #[inline(always)]
22    #[cfg(feature = "tl-proto")]
23    pub fn sign<T: tl_proto::TlWrite>(&self, data: T) -> [u8; 64] {
24        self.secret_key.sign(data, &self.public_key)
25    }
26
27    /// Signs raw bytes
28    #[inline(always)]
29    pub fn sign_raw(&self, data: &[u8]) -> [u8; 64] {
30        self.secret_key.sign_raw(data, &self.public_key)
31    }
32
33    /// Computes shared secret using x25519
34    #[inline(always)]
35    pub fn compute_shared_secret(&self, other_public_key: &PublicKey) -> [u8; 32] {
36        self.secret_key.compute_shared_secret(other_public_key)
37    }
38}
39
40impl rand::distributions::Distribution<KeyPair> for rand::distributions::Standard {
41    #[inline]
42    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> KeyPair {
43        let secret_key = rng.gen::<SecretKey>();
44
45        KeyPair {
46            secret_key: ExpandedSecretKey::from(&secret_key),
47            public_key: PublicKey::from(&secret_key),
48        }
49    }
50}
51
52impl From<ExpandedSecretKey> for KeyPair {
53    fn from(secret_key: ExpandedSecretKey) -> Self {
54        let public_key = PublicKey::from(&secret_key);
55        Self {
56            secret_key,
57            public_key,
58        }
59    }
60}
61
62impl From<&'_ SecretKey> for KeyPair {
63    fn from(secret_key: &SecretKey) -> Self {
64        let secret_key = secret_key.expand();
65        let public_key = PublicKey::from(&secret_key);
66        Self {
67            secret_key,
68            public_key,
69        }
70    }
71}
72
73/// Ed25519 public key
74#[derive(Copy, Clone)]
75pub struct PublicKey {
76    compressed: CompressedEdwardsY,
77    neg_point: EdwardsPoint,
78}
79
80impl PublicKey {
81    /// Tries to create public key from
82    #[inline(always)]
83    pub fn from_bytes(bytes: [u8; 32]) -> Option<Self> {
84        let compressed = CompressedEdwardsY(bytes);
85        let point = compressed.decompress()?;
86        Some(PublicKey {
87            compressed,
88            neg_point: -point,
89        })
90    }
91
92    #[inline(always)]
93    #[cfg(feature = "tl-proto")]
94    pub fn from_tl(tl: crate::tl::PublicKey<'_>) -> Option<Self> {
95        match tl {
96            crate::tl::PublicKey::Ed25519 { key } => Self::from_bytes(*key),
97            _ => None,
98        }
99    }
100
101    #[inline(always)]
102    #[cfg(feature = "tl-proto")]
103    pub fn as_tl(&'_ self) -> crate::tl::PublicKey<'_> {
104        crate::tl::PublicKey::Ed25519 {
105            key: self.compressed.as_bytes(),
106        }
107    }
108
109    #[inline(always)]
110    pub fn to_bytes(&self) -> [u8; 32] {
111        self.compressed.to_bytes()
112    }
113
114    #[inline(always)]
115    pub fn as_bytes(&'_ self) -> &'_ [u8; 32] {
116        self.compressed.as_bytes()
117    }
118
119    /// Verifies message signature using its TL representation
120    ///
121    /// NOTE: `[u8]` is representation differently in TL. Use [PublicKey::verify_raw] if
122    /// you need to verify raw bytes signature
123    #[cfg(feature = "tl-proto")]
124    pub fn verify<T: tl_proto::TlWrite>(&self, message: T, signature: &[u8; 64]) -> bool {
125        let target_r = CompressedEdwardsY(signature[..32].try_into().unwrap());
126        let s = match check_scalar(signature[32..].try_into().unwrap()) {
127            Some(s) => s,
128            None => return false,
129        };
130
131        let mut h = Sha512::new();
132        h.update(target_r.as_bytes());
133        h.update(self.compressed.as_bytes());
134        tl_proto::HashWrapper(message).update_hasher(&mut h);
135
136        let k = Scalar::from_bytes_mod_order_wide(&h.finalize().into());
137        let r = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &self.neg_point, &s);
138
139        r.compress() == target_r
140    }
141
142    /// Verifies message signature as it is
143    pub fn verify_raw(&self, message: &[u8], signature: &[u8; 64]) -> bool {
144        let target_r = CompressedEdwardsY(signature[..32].try_into().unwrap());
145        let s = match check_scalar(signature[32..].try_into().unwrap()) {
146            Some(s) => s,
147            None => return false,
148        };
149
150        let mut h = Sha512::new();
151        h.update(target_r.as_bytes());
152        h.update(self.compressed.as_bytes());
153        h.update(message);
154
155        let k = Scalar::from_bytes_mod_order_wide(&h.finalize().into());
156        let r = EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &self.neg_point, &s);
157
158        r.compress() == target_r
159    }
160
161    #[inline(always)]
162    fn from_scalar(bits: [u8; 32]) -> PublicKey {
163        let point = EdwardsPoint::mul_base_clamped(bits);
164        let compressed = point.compress();
165        Self {
166            compressed,
167            neg_point: -point,
168        }
169    }
170}
171
172impl From<&'_ SecretKey> for PublicKey {
173    fn from(secret_key: &SecretKey) -> Self {
174        let mut h = Sha512::new();
175        h.update(secret_key.0.as_slice());
176        let hash: [u8; 64] = h.finalize().into();
177        Self::from_scalar(hash[..32].try_into().unwrap())
178    }
179}
180
181impl From<&'_ ExpandedSecretKey> for PublicKey {
182    fn from(expanded_secret_key: &ExpandedSecretKey) -> Self {
183        Self::from_scalar(expanded_secret_key.key_bytes)
184    }
185}
186
187impl AsRef<[u8; 32]> for PublicKey {
188    fn as_ref(&self) -> &[u8; 32] {
189        self.as_bytes()
190    }
191}
192
193impl PartialEq for PublicKey {
194    #[inline(always)]
195    fn eq(&self, other: &Self) -> bool {
196        self.compressed.eq(&other.compressed)
197    }
198}
199
200impl Eq for PublicKey {}
201
202impl std::fmt::Display for PublicKey {
203    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
204        let mut output = [0u8; 64];
205        hex::encode_to_slice(self.compressed.as_bytes(), &mut output).ok();
206
207        // SAFETY: output is guaranteed to contain only [0-9a-f]
208        let output = unsafe { std::str::from_utf8_unchecked(&output) };
209        f.write_str(output)
210    }
211}
212
213impl std::fmt::Debug for PublicKey {
214    #[inline]
215    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
216        std::fmt::Display::fmt(self, f)
217    }
218}
219
220#[cfg(feature = "serde")]
221impl serde::Serialize for PublicKey {
222    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
223    where
224        S: serde::Serializer,
225    {
226        if serializer.is_human_readable() {
227            serializer.collect_str(self)
228        } else {
229            self.as_bytes().serialize(serializer)
230        }
231    }
232}
233
234#[cfg(feature = "serde")]
235impl<'de> serde::Deserialize<'de> for PublicKey {
236    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
237    where
238        D: serde::Deserializer<'de>,
239    {
240        use serde::de::{Error, Visitor};
241
242        struct BytesVisitor;
243
244        impl Visitor<'_> for BytesVisitor {
245            type Value = [u8; 32];
246
247            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248                formatter.write_str("hex-encoded public key")
249            }
250
251            fn visit_str<E: Error>(self, value: &str) -> Result<Self::Value, E> {
252                let mut result = [0; 32];
253                match hex::decode_to_slice(value, &mut result) {
254                    Ok(()) => Ok(result),
255                    Err(_) => Err(Error::invalid_value(
256                        serde::de::Unexpected::Str(value),
257                        &self,
258                    )),
259                }
260            }
261        }
262
263        let bytes = if deserializer.is_human_readable() {
264            deserializer.deserialize_str(BytesVisitor)
265        } else {
266            <[u8; 32]>::deserialize(deserializer)
267        }?;
268
269        Self::from_bytes(bytes).ok_or_else(|| Error::custom("invalid public key"))
270    }
271}
272
273#[derive(Copy, Clone)]
274pub struct ExpandedSecretKey {
275    key: Scalar,
276    key_bytes: [u8; 32],
277    nonce: [u8; 32],
278}
279
280impl ExpandedSecretKey {
281    #[inline(always)]
282    pub fn nonce(&'_ self) -> &'_ [u8; 32] {
283        &self.nonce
284    }
285
286    #[cfg(feature = "tl-proto")]
287    pub fn sign<T: tl_proto::TlWrite>(&self, message: T, public_key: &PublicKey) -> [u8; 64] {
288        #![allow(non_snake_case)]
289
290        let message = tl_proto::HashWrapper(message);
291
292        let mut h = Sha512::new();
293        h.update(self.nonce.as_slice());
294        message.update_hasher(&mut h);
295
296        let r = Scalar::from_bytes_mod_order_wide(&h.finalize().into());
297        let R = EdwardsPoint::mul_base(&r).compress();
298
299        h = Sha512::new();
300        h.update(R.as_bytes());
301        h.update(public_key.as_bytes());
302        message.update_hasher(&mut h);
303
304        let k = Scalar::from_bytes_mod_order_wide(&h.finalize().into());
305        let s = (k * self.key) + r;
306
307        let mut result = [0u8; 64];
308        result[..32].copy_from_slice(R.as_bytes().as_slice());
309        result[32..].copy_from_slice(s.as_bytes().as_slice());
310        result
311    }
312
313    pub fn sign_raw(&self, message: &[u8], public_key: &PublicKey) -> [u8; 64] {
314        #![allow(non_snake_case)]
315
316        let mut h = Sha512::new();
317        h.update(self.nonce.as_slice());
318        h.update(message);
319
320        let r = Scalar::from_bytes_mod_order_wide(&h.finalize().into());
321        let R = EdwardsPoint::mul_base(&r).compress();
322
323        h = Sha512::new();
324        h.update(R.as_bytes());
325        h.update(public_key.as_bytes());
326        h.update(message);
327
328        let k = Scalar::from_bytes_mod_order_wide(&h.finalize().into());
329        let s = (k * self.key) + r;
330
331        let mut result = [0u8; 64];
332        result[..32].copy_from_slice(R.as_bytes().as_slice());
333        result[32..].copy_from_slice(s.as_bytes().as_slice());
334        result
335    }
336
337    #[inline(always)]
338    pub fn compute_shared_secret(&self, other_public_key: &PublicKey) -> [u8; 32] {
339        let point = (-other_public_key.neg_point).to_montgomery();
340        (point * self.key).to_bytes()
341    }
342}
343
344impl From<&'_ SecretKey> for ExpandedSecretKey {
345    fn from(secret_key: &SecretKey) -> Self {
346        let mut h = Sha512::new();
347        h.update(secret_key.0.as_slice());
348        let hash: [u8; 64] = h.finalize().into();
349
350        let lower: [u8; 32] = hash[..32].try_into().unwrap();
351        let nonce: [u8; 32] = hash[32..].try_into().unwrap();
352
353        let key_bytes = curve25519_dalek::scalar::clamp_integer(lower);
354
355        Self {
356            key: Scalar::from_bytes_mod_order(key_bytes),
357            key_bytes,
358            nonce,
359        }
360    }
361}
362
363#[derive(Copy, Clone)]
364pub struct SecretKey([u8; 32]);
365
366impl SecretKey {
367    #[inline(always)]
368    pub fn from_bytes(bytes: [u8; 32]) -> Self {
369        Self(bytes)
370    }
371
372    #[inline(always)]
373    pub fn to_bytes(&self) -> [u8; 32] {
374        self.0
375    }
376
377    #[inline(always)]
378    pub fn as_bytes(&'_ self) -> &'_ [u8; 32] {
379        &self.0
380    }
381
382    pub fn generate(rng: &mut impl Rng) -> Self {
383        Self(rng.gen())
384    }
385
386    #[inline(always)]
387    pub fn expand(&self) -> ExpandedSecretKey {
388        ExpandedSecretKey::from(self)
389    }
390}
391
392impl rand::distributions::Distribution<SecretKey> for rand::distributions::Standard {
393    #[inline]
394    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> SecretKey {
395        SecretKey(rng.gen())
396    }
397}
398
399#[inline(always)]
400fn check_scalar(bytes: [u8; 32]) -> Option<Scalar> {
401    Scalar::from_canonical_bytes(bytes).into()
402}
403
404#[cfg(test)]
405mod tests {
406    use super::*;
407
408    #[test]
409    fn correct_signature() {
410        let secret = SecretKey::from_bytes([
411            99, 87, 207, 105, 199, 108, 51, 89, 172, 108, 232, 48, 240, 147, 49, 155, 145, 60, 66,
412            55, 98, 149, 119, 0, 251, 19, 132, 69, 151, 132, 184, 53,
413        ]);
414
415        let pubkey = PublicKey::from(&secret);
416        assert_eq!(
417            pubkey.to_bytes(),
418            [
419                75, 54, 96, 93, 16, 21, 8, 159, 230, 42, 68, 148, 54, 18, 251, 196, 205, 254, 252,
420                114, 76, 87, 204, 218, 132, 26, 196, 181, 191, 188, 115, 123
421            ]
422        );
423        println!("{pubkey:?}");
424
425        let data = b"hello world";
426
427        let extended = ExpandedSecretKey::from(&secret);
428        let signature = extended.sign(data, &pubkey);
429        assert_eq!(
430            signature,
431            [
432                76, 51, 131, 27, 77, 188, 20, 26, 229, 121, 93, 100, 10, 166, 183, 121, 12, 48, 17,
433                239, 115, 184, 50, 162, 103, 228, 3, 136, 213, 165, 246, 113, 220, 84, 255, 136,
434                251, 141, 229, 52, 236, 249, 135, 182, 242, 198, 171, 1, 194, 148, 164, 8, 131,
435                253, 205, 112, 112, 145, 6, 225, 71, 78, 138, 1
436            ]
437        );
438
439        assert!(pubkey.verify(data, &signature))
440    }
441
442    #[test]
443    fn verify_with_different_key() {
444        let first = SecretKey::generate(&mut rand::thread_rng());
445        let first_pubkey = PublicKey::from(&first);
446
447        let second = SecretKey::generate(&mut rand::thread_rng());
448        let second_pubkey = PublicKey::from(&second);
449
450        let data = b"hello world";
451
452        let extended = ExpandedSecretKey::from(&first);
453        let signature = extended.sign(data, &first_pubkey);
454
455        assert!(!second_pubkey.verify(data, &signature))
456    }
457
458    #[test]
459    fn correct_shared_secret() {
460        let first = ExpandedSecretKey::from(&SecretKey::from_bytes([
461            215, 30, 117, 171, 183, 9, 171, 48, 212, 45, 10, 198, 14, 66, 109, 80, 163, 180, 194,
462            66, 82, 184, 13, 48, 240, 102, 40, 110, 156, 5, 13, 143,
463        ]));
464        let first_pubkey = PublicKey::from(&first);
465
466        let second = ExpandedSecretKey::from(&SecretKey::from_bytes([
467            181, 115, 13, 55, 26, 150, 138, 43, 66, 28, 162, 50, 0, 133, 120, 24, 20, 142, 183, 60,
468            159, 53, 200, 97, 14, 123, 63, 249, 222, 211, 186, 99,
469        ]));
470        let second_pubkey = PublicKey::from(&second);
471
472        let first_shared_key = first.compute_shared_secret(&second_pubkey);
473        let second_shared_key = second.compute_shared_secret(&first_pubkey);
474
475        assert_eq!(
476            first_shared_key,
477            [
478                30, 243, 238, 65, 216, 53, 237, 172, 6, 120, 204, 220, 34, 163, 18, 28, 181, 245,
479                215, 233, 98, 0, 87, 11, 85, 6, 41, 130, 140, 95, 66, 72
480            ]
481        );
482        assert_eq!(first_shared_key, second_shared_key);
483    }
484
485    #[test]
486    fn same_shared_secret() {
487        let first = ExpandedSecretKey::from(&SecretKey::generate(&mut rand::thread_rng()));
488        let first_pubkey = PublicKey::from(&first);
489
490        let second = ExpandedSecretKey::from(&SecretKey::generate(&mut rand::thread_rng()));
491        let second_pubkey = PublicKey::from(&second);
492
493        let first_shared_key = first.compute_shared_secret(&second_pubkey);
494        let second_shared_key = second.compute_shared_secret(&first_pubkey);
495
496        assert_eq!(first_shared_key, second_shared_key);
497    }
498
499    #[test]
500    fn shared_secret_on_self() {
501        let secret = SecretKey::generate(&mut rand::thread_rng());
502        let pubkey = PublicKey::from(&secret);
503
504        let shared = ExpandedSecretKey::from(&secret).compute_shared_secret(&pubkey);
505        assert_ne!(secret.as_bytes(), &shared);
506    }
507}