1use std::fmt::Display;
16
17use base64::decoded_len_estimate;
18use base64ct::Encoding;
19use curve25519_dalek::EdwardsPoint;
20use ed25519_dalek::{
21 PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH, Signature, Signer, SigningKey, VerifyingKey,
22};
23use rand::thread_rng;
24use serde::{Deserialize, Deserializer, Serialize, Serializer};
25use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
26use sha2::Sha512;
27use thiserror::Error;
28use zeroize::Zeroize;
29
30use crate::utilities::{base64_decode, base64_encode};
31
32#[derive(Debug, Error)]
34pub enum SignatureError {
35 #[error("The signature couldn't be decoded: {0}")]
37 Base64(#[from] base64::DecodeError),
38 #[error("The signature was invalid: {0}")]
40 Signature(#[from] ed25519_dalek::SignatureError),
41}
42
43#[derive(Deserialize, Serialize)]
45#[serde(try_from = "Ed25519KeypairPickle")]
46#[serde(into = "Ed25519KeypairPickle")]
47pub struct Ed25519Keypair {
48 secret_key: SecretKeys,
49 public_key: Ed25519PublicKey,
50}
51
52struct ExpandedSecretKey {
53 source: Box<[u8; 64]>,
54 inner: Box<ed25519_dalek::hazmat::ExpandedSecretKey>,
55}
56
57impl ExpandedSecretKey {
58 fn from_bytes(bytes: &[u8; 64]) -> Self {
59 let mut source = Box::new([0u8; 64]);
60 source.copy_from_slice(bytes);
61
62 Self { source, inner: ed25519_dalek::hazmat::ExpandedSecretKey::from_bytes(bytes).into() }
63 }
64
65 const fn as_bytes(&self) -> &[u8; 64] {
66 &self.source
67 }
68
69 fn sign(&self, message: &[u8]) -> Signature {
70 ed25519_dalek::hazmat::raw_sign::<Sha512>(&self.inner, message, &self.public_key().0)
71 }
72
73 fn public_key(&self) -> Ed25519PublicKey {
74 let point = EdwardsPoint::mul_base(&self.inner.scalar);
75 Ed25519PublicKey(VerifyingKey::from(point))
76 }
77}
78
79impl Clone for ExpandedSecretKey {
80 fn clone(&self) -> Self {
81 let source = self.source.clone();
82 Self {
83 source,
84 inner: ed25519_dalek::hazmat::ExpandedSecretKey::from_bytes(&self.source).into(),
85 }
86 }
87}
88
89impl Serialize for ExpandedSecretKey {
90 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
91 where
92 S: Serializer,
93 {
94 let bytes = self.as_bytes();
95 SerdeBytes::new(bytes).serialize(serializer)
96 }
97}
98
99impl<'d> Deserialize<'d> for ExpandedSecretKey {
100 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
101 where
102 D: Deserializer<'d>,
103 {
104 let mut bytes = <SerdeByteBuf>::deserialize(deserializer)?;
105 let length = bytes.len();
106
107 if bytes.len() != 64 {
108 bytes.zeroize();
109
110 Err(serde::de::Error::custom(format!(
111 "Invalid secret key length: expected 64 bytes, got {length}"
112 )))
113 } else {
114 let mut slice = [0u8; 64];
115 slice.copy_from_slice(&bytes);
116
117 let ret = ExpandedSecretKey::from_bytes(&slice);
118
119 slice.zeroize();
120 bytes.zeroize();
121
122 Ok(ret)
123 }
124 }
125}
126
127impl Ed25519Keypair {
128 pub fn new() -> Self {
130 let mut rng = thread_rng();
131 let signing_key = SigningKey::generate(&mut rng);
132
133 Self {
134 public_key: Ed25519PublicKey(signing_key.verifying_key()),
135 secret_key: signing_key.into(),
136 }
137 }
138
139 pub(crate) fn from_unexpanded_key(secret_key: &[u8; 32]) -> Result<Self, crate::KeyError> {
140 let secret_key = SigningKey::from_bytes(secret_key);
141 let public_key = secret_key.verifying_key();
142 Ok(Self { secret_key: secret_key.into(), public_key: Ed25519PublicKey(public_key) })
143 }
144
145 pub(crate) fn unexpanded_secret_key(&self) -> Option<Box<[u8; 32]>> {
146 match &self.secret_key {
147 SecretKeys::Normal(k) => Some(Box::new(k.to_bytes())),
148 SecretKeys::Expanded(_) => None,
149 }
150 }
151
152 #[cfg(feature = "libolm-compat")]
153 pub(crate) fn from_expanded_key(secret_key: &[u8; 64]) -> Result<Self, crate::KeyError> {
154 let secret_key = ExpandedSecretKey::from_bytes(secret_key);
155 let public_key = secret_key.public_key();
156
157 Ok(Self { secret_key: secret_key.into(), public_key })
158 }
159
160 #[cfg(feature = "libolm-compat")]
161 pub(crate) fn expanded_secret_key(&self) -> Box<[u8; 64]> {
162 use sha2::Digest;
163
164 let mut expanded = Box::new([0u8; 64]);
165
166 match &self.secret_key {
167 SecretKeys::Normal(k) => {
168 let mut k = k.to_bytes();
169 Sha512::new().chain_update(k).finalize_into(expanded.as_mut_slice().into());
170 k.zeroize();
171 }
172 SecretKeys::Expanded(k) => expanded.copy_from_slice(k.as_bytes()),
173 }
174
175 expanded
176 }
177
178 pub const fn public_key(&self) -> Ed25519PublicKey {
180 self.public_key
181 }
182
183 pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
185 self.secret_key.sign(message)
186 }
187}
188
189impl Default for Ed25519Keypair {
190 fn default() -> Self {
191 Self::new()
192 }
193}
194
195#[derive(Deserialize, Serialize)]
197#[serde(transparent)]
198pub struct Ed25519SecretKey(Box<SigningKey>);
199
200impl Ed25519SecretKey {
201 pub const LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH;
203
204 const BASE64_LENGTH: usize = 43;
205 const PADDED_BASE64_LENGTH: usize = 44;
206
207 pub fn new() -> Self {
209 let mut rng = thread_rng();
210 let signing_key = SigningKey::generate(&mut rng);
211 let key = Box::new(signing_key);
212
213 Self(key)
214 }
215
216 pub fn to_bytes(&self) -> Box<[u8; 32]> {
221 Box::new(self.0.to_bytes())
222 }
223
224 pub fn from_slice(bytes: &[u8; 32]) -> Self {
226 Self(Box::new(SigningKey::from_bytes(bytes)))
227 }
228
229 pub fn to_base64(&self) -> String {
237 let mut bytes = self.to_bytes();
238 let ret = base64ct::Base64Unpadded::encode_string(bytes.as_ref());
239
240 bytes.zeroize();
241
242 ret
243 }
244
245 pub fn from_base64(input: &str) -> Result<Self, crate::KeyError> {
247 if input.len() != Self::BASE64_LENGTH && input.len() != Self::PADDED_BASE64_LENGTH {
248 Err(crate::KeyError::InvalidKeyLength {
249 key_type: "Ed25519",
250 expected_length: ed25519_dalek::SECRET_KEY_LENGTH,
251 length: decoded_len_estimate(input.len()),
252 })
253 } else {
254 let mut bytes = if let Ok(bytes) = base64ct::Base64Unpadded::decode_vec(input) {
258 bytes
259 } else {
260 base64ct::Base64::decode_vec(input)?
261 };
262
263 let mut key_bytes = [0u8; 32];
264 key_bytes.copy_from_slice(&bytes);
265 let key = Self::from_slice(&key_bytes);
266
267 bytes.zeroize();
268 key_bytes.zeroize();
269
270 Ok(key)
271 }
272 }
273
274 pub fn public_key(&self) -> Ed25519PublicKey {
276 Ed25519PublicKey(self.0.verifying_key())
277 }
278
279 pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
298 Ed25519Signature(self.0.sign(message))
299 }
300}
301
302impl Default for Ed25519SecretKey {
303 fn default() -> Self {
304 Self::new()
305 }
306}
307
308#[derive(Serialize, Deserialize)]
309enum SecretKeys {
310 Normal(Box<SigningKey>),
311 Expanded(Box<ExpandedSecretKey>),
312}
313
314impl SecretKeys {
315 fn public_key(&self) -> Ed25519PublicKey {
316 match &self {
317 SecretKeys::Normal(k) => Ed25519PublicKey(k.verifying_key()),
318 SecretKeys::Expanded(k) => k.public_key(),
319 }
320 }
321
322 fn sign(&self, message: &[u8]) -> Ed25519Signature {
323 let signature = match &self {
324 SecretKeys::Normal(k) => k.sign(message),
325 SecretKeys::Expanded(k) => k.sign(message),
326 };
327
328 Ed25519Signature(signature)
329 }
330}
331
332#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
334#[serde(transparent)]
335pub struct Ed25519PublicKey(VerifyingKey);
336
337impl Ed25519PublicKey {
338 pub const LENGTH: usize = PUBLIC_KEY_LENGTH;
340
341 const BASE64_LENGTH: usize = 43;
342 const PADDED_BASE64_LENGTH: usize = 44;
343
344 pub fn from_slice(bytes: &[u8; 32]) -> Result<Self, crate::KeyError> {
346 Ok(Self(VerifyingKey::from_bytes(bytes).map_err(SignatureError::from)?))
347 }
348
349 pub fn as_bytes(&self) -> &[u8; Self::LENGTH] {
351 self.0.as_bytes()
352 }
353
354 pub fn from_base64(input: &str) -> Result<Self, crate::KeyError> {
357 if input.len() != Self::BASE64_LENGTH && input.len() != Self::PADDED_BASE64_LENGTH {
358 Err(crate::KeyError::InvalidKeyLength {
359 key_type: "Ed25519",
360 expected_length: Self::LENGTH,
361 length: decoded_len_estimate(input.len()),
362 })
363 } else {
364 let mut bytes = base64_decode(input)?;
365 let mut key_bytes = [0u8; 32];
366
367 key_bytes.copy_from_slice(&bytes);
368 let key = Self::from_slice(&key_bytes);
369
370 bytes.zeroize();
371 key_bytes.zeroize();
372
373 key
374 }
375 }
376
377 pub fn to_base64(&self) -> String {
380 base64_encode(self.as_bytes())
381 }
382
383 #[cfg(not(fuzzing))]
386 pub fn verify(
387 &self,
388 message: &[u8],
389 signature: &Ed25519Signature,
390 ) -> Result<(), SignatureError> {
391 Ok(self.0.verify_strict(message, &signature.0)?)
392 }
393
394 #[cfg(fuzzing)]
395 #[allow(missing_docs)]
396 pub fn verify(
397 &self,
398 _message: &[u8],
399 _signature: &Ed25519Signature,
400 ) -> Result<(), SignatureError> {
401 Ok(())
402 }
403}
404
405impl Display for Ed25519PublicKey {
406 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
407 write!(f, "{}", self.to_base64())
408 }
409}
410
411impl std::fmt::Debug for Ed25519PublicKey {
412 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
413 let s = format!("ed25519:{self}");
414 <str as std::fmt::Debug>::fmt(&s, f)
415 }
416}
417
418#[derive(Clone, Copy, PartialEq, Eq)]
421pub struct Ed25519Signature(pub(crate) Signature);
422
423impl Ed25519Signature {
424 pub const LENGTH: usize = SIGNATURE_LENGTH;
426
427 pub fn from_slice(bytes: &[u8]) -> Result<Self, SignatureError> {
429 Ok(Self(Signature::try_from(bytes)?))
430 }
431
432 pub fn from_base64(signature: &str) -> Result<Self, SignatureError> {
435 Ok(Self(Signature::try_from(base64_decode(signature)?.as_slice())?))
436 }
437
438 pub fn to_base64(&self) -> String {
440 base64_encode(self.0.to_bytes())
441 }
442
443 pub fn to_bytes(&self) -> [u8; Self::LENGTH] {
445 self.0.to_bytes()
446 }
447}
448
449impl Display for Ed25519Signature {
450 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
451 write!(f, "{}", self.to_base64())
452 }
453}
454
455impl std::fmt::Debug for Ed25519Signature {
456 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
457 let s = format!("ed25519:{self}");
458 <str as std::fmt::Debug>::fmt(&s, f)
459 }
460}
461
462impl Clone for Ed25519Keypair {
463 fn clone(&self) -> Self {
464 let secret_key: SecretKeys = match &self.secret_key {
465 SecretKeys::Normal(k) => SecretKeys::Normal(k.clone()),
466 SecretKeys::Expanded(k) => SecretKeys::Expanded(k.clone()),
467 };
468
469 Self { secret_key, public_key: self.public_key }
470 }
471}
472
473impl From<Ed25519Keypair> for Ed25519KeypairPickle {
474 fn from(key: Ed25519Keypair) -> Self {
475 Self(key.secret_key)
476 }
477}
478
479impl From<SigningKey> for SecretKeys {
480 fn from(key: SigningKey) -> Self {
481 Self::Normal(Box::new(key))
482 }
483}
484
485impl From<ExpandedSecretKey> for SecretKeys {
486 fn from(key: ExpandedSecretKey) -> Self {
487 Self::Expanded(Box::new(key))
488 }
489}
490
491#[derive(Serialize, Deserialize)]
492#[serde(transparent)]
493pub struct Ed25519KeypairPickle(SecretKeys);
494
495impl From<Ed25519KeypairPickle> for Ed25519Keypair {
496 fn from(pickle: Ed25519KeypairPickle) -> Self {
497 let secret_key = pickle.0;
498 let public_key = secret_key.public_key();
499
500 Self { secret_key, public_key }
501 }
502}
503
504#[cfg(test)]
505mod tests {
506 use assert_matches2::assert_matches;
507
508 use super::ExpandedSecretKey;
509 use crate::{Ed25519Keypair, Ed25519PublicKey, Ed25519SecretKey, Ed25519Signature, KeyError};
510
511 #[test]
512 fn byte_decoding_roundtrip_succeeds_for_secret_key() {
513 let bytes = *b"oooooooooooooooooooooooooooooooo";
514 let key = Ed25519SecretKey::from_slice(&bytes);
515 assert_eq!(*(key.to_bytes()), bytes);
516 }
517
518 #[test]
519 fn base64_decoding_incorrect_num_of_bytes_fails_for_secret_key() {
520 assert!(matches!(
521 Ed25519SecretKey::from_base64("foo"),
522 Err(KeyError::InvalidKeyLength { .. })
523 ));
524 }
525
526 #[test]
527 fn unpadded_base64_decoding_roundtrip_succeeds_for_secret_key() {
528 let base64 = "MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE";
529 let key = Ed25519SecretKey::from_base64(base64).expect("Should decode key from base64");
530 assert_eq!(key.to_base64(), base64);
531 }
532
533 #[test]
534 fn padded_base64_decoding_roundtrip_succeeds_for_secret_key() {
535 let base64 = "MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE=";
536 let key = Ed25519SecretKey::from_base64(base64).expect("Should decode key from base64");
537 assert_eq!(key.to_base64(), base64.trim_end_matches('='));
538 }
539
540 #[test]
541 fn byte_decoding_roundtrip_succeeds_for_public_key() {
542 let bytes = *b"oooooooooooooooooooooooooooooooo";
543 let key = Ed25519PublicKey::from_slice(&bytes).expect("Should decode key from bytes");
544 assert_eq!(key.as_bytes(), &bytes);
545 }
546
547 #[test]
548 fn base64_decoding_incorrect_num_of_bytes_fails_for_public_key() {
549 assert_matches!(
550 Ed25519PublicKey::from_base64("foo"),
551 Err(KeyError::InvalidKeyLength { .. })
552 );
553 }
554
555 #[test]
556 fn unpadded_base64_decoding_roundtrip_succeeds_for_public_key() {
557 let base64 = "b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28";
558 let key = Ed25519PublicKey::from_base64(base64).expect("Should decode key from base64");
559 assert_eq!(key.to_base64(), base64);
560 }
561
562 #[test]
563 fn padded_base64_decoding_roundtrip_succeeds_for_public_key() {
564 let base64 = "b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28=";
565 let key = Ed25519PublicKey::from_base64(base64).expect("Should decode key from base64");
566 assert_eq!(key.to_base64(), base64.trim_end_matches('='));
567 }
568
569 #[test]
570 fn verifying_valid_signature_succeeds() {
571 let key_pair = Ed25519Keypair::new();
572 let signature = key_pair.secret_key.sign(b"foo");
573 key_pair.public_key().verify(b"foo", &signature).expect("Should verify valid signature");
574 }
575
576 #[test]
577 fn verifying_invalid_signature_fails() {
578 let key_pair = Ed25519Keypair::new();
579 let signature = key_pair.secret_key.sign(b"foo");
580 key_pair
581 .public_key()
582 .verify(b"bar", &signature)
583 .expect_err("Should reject invalid signature");
584 }
585
586 #[test]
587 #[cfg(feature = "libolm-compat")]
588 fn can_only_expand_secret_key_once() {
589 let key_pair = Ed25519Keypair::new();
590 assert!(matches!(key_pair.secret_key, crate::types::ed25519::SecretKeys::Normal(_)));
591
592 let expanded_key = key_pair.expanded_secret_key();
593 let expanded_key_pair = Ed25519Keypair::from_expanded_key(&expanded_key).unwrap();
594 assert!(matches!(
595 expanded_key_pair.secret_key,
596 crate::types::ed25519::SecretKeys::Expanded(_)
597 ));
598 assert_eq!(expanded_key_pair.public_key(), key_pair.public_key());
599
600 let reexpanded_key = expanded_key_pair.expanded_secret_key();
601 assert_eq!(reexpanded_key, expanded_key);
602 }
603
604 #[test]
605 fn serialization_roundtrip_succeeds() {
606 let bytes = b"9999999999999999999999999999999999999999999999999999999999999999";
607 let key = ExpandedSecretKey::from_bytes(bytes);
608 let serialized = serde_json::to_value(key).expect("Should serialize key");
609 let deserialized = serde_json::from_value::<ExpandedSecretKey>(serialized)
610 .expect("Should deserialize key");
611 assert_eq!(deserialized.as_bytes(), bytes);
612 }
613
614 #[test]
615 fn deserializing_from_invalid_length_fails() {
616 let serialized = serde_json::to_value(b"foo").expect("Should serialize key");
617 let deserialized = serde_json::from_value::<ExpandedSecretKey>(serialized);
618 assert!(deserialized.is_err());
619 }
620
621 #[test]
622 fn unexpanded_key_roundtrip_succeeds() {
623 let key_pair = Ed25519Keypair::new();
624
625 let unexpanded_key = key_pair.unexpanded_secret_key().expect("Should have unexpanded key");
626 let recovered_key_pair = Ed25519Keypair::from_unexpanded_key(unexpanded_key.as_ref())
627 .expect("Should create new keypair");
628
629 assert_eq!(key_pair.public_key().to_base64(), recovered_key_pair.public_key().to_base64());
630 }
631
632 #[test]
633 fn snapshot_public_key_debug() {
634 let public_key = Ed25519PublicKey::from_slice(&[0; 32])
635 .expect("We should be able to create a public key from zero bytes");
636
637 insta::assert_debug_snapshot!(public_key)
638 }
639
640 #[test]
641 fn snapshot_signature_debug() {
642 let signature = Ed25519Signature::from_slice(&[0; 64])
643 .expect("We should be able to create a public key from zero bytes");
644
645 insta::assert_debug_snapshot!(signature)
646 }
647}