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