1use curve25519_dalek::edwards::{CompressedEdwardsY, EdwardsPoint};
2use curve25519_dalek::scalar::Scalar;
3use rand::Rng;
4use sha2::{Digest, Sha512};
5
6#[derive(Copy, Clone)]
8pub struct KeyPair {
9 pub secret_key: ExpandedSecretKey,
10 pub public_key: PublicKey,
11}
12
13impl KeyPair {
14 #[inline(always)]
16 pub fn generate(rng: &mut impl Rng) -> Self {
17 Self::from(&SecretKey::generate(rng))
18 }
19
20 #[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 #[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 #[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#[derive(Copy, Clone)]
75pub struct PublicKey {
76 compressed: CompressedEdwardsY,
77 neg_point: EdwardsPoint,
78}
79
80impl PublicKey {
81 #[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 #[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 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 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}