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::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 = 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 std::ops::DerefMut;
163
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.deref_mut().into());
172
173 k.zeroize();
174 }
175 SecretKeys::Expanded(k) => expanded.copy_from_slice(k.as_bytes()),
176 }
177
178 expanded
179 }
180
181 pub const fn public_key(&self) -> Ed25519PublicKey {
183 self.public_key
184 }
185
186 pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
188 self.secret_key.sign(message)
189 }
190}
191
192impl Default for Ed25519Keypair {
193 fn default() -> Self {
194 Self::new()
195 }
196}
197
198#[derive(Deserialize, Serialize)]
200#[serde(transparent)]
201pub struct Ed25519SecretKey(Box<SigningKey>);
202
203impl Ed25519SecretKey {
204 pub const LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH;
206
207 const BASE64_LENGTH: usize = 43;
208 const PADDED_BASE64_LENGTH: usize = 44;
209
210 pub fn new() -> Self {
212 let mut rng = rng();
213 let signing_key = SigningKey::generate(&mut rng);
214 let key = Box::new(signing_key);
215
216 Self(key)
217 }
218
219 pub fn to_bytes(&self) -> Box<[u8; 32]> {
224 Box::new(self.0.to_bytes())
225 }
226
227 pub fn from_slice(bytes: &[u8; 32]) -> Self {
229 Self(Box::new(SigningKey::from_bytes(bytes)))
230 }
231
232 pub fn to_base64(&self) -> String {
240 let mut bytes = self.to_bytes();
241 let ret = base64ct::Base64Unpadded::encode_string(bytes.as_ref());
242
243 bytes.zeroize();
244
245 ret
246 }
247
248 pub fn from_base64(input: &str) -> Result<Self, crate::KeyError> {
250 if input.len() != Self::BASE64_LENGTH && input.len() != Self::PADDED_BASE64_LENGTH {
251 Err(crate::KeyError::InvalidKeyLength {
252 key_type: "Ed25519",
253 expected_length: ed25519_dalek::SECRET_KEY_LENGTH,
254 length: decoded_len_estimate(input.len()),
255 })
256 } else {
257 let mut bytes = if let Ok(bytes) = base64ct::Base64Unpadded::decode_vec(input) {
261 bytes
262 } else {
263 base64ct::Base64::decode_vec(input)?
264 };
265
266 let result = if bytes.len() == ed25519_dalek::SECRET_KEY_LENGTH {
267 let mut key_bytes = [0u8; ed25519_dalek::SECRET_KEY_LENGTH];
268 key_bytes.copy_from_slice(&bytes);
269 let key = Self::from_slice(&key_bytes);
270 key_bytes.zeroize();
271
272 Ok(key)
273 } else {
274 Err(crate::KeyError::InvalidKeyLength {
275 key_type: "Ed25519",
276 expected_length: ed25519_dalek::SECRET_KEY_LENGTH,
277 length: bytes.len(),
278 })
279 };
280
281 bytes.zeroize();
282 result
283 }
284 }
285
286 pub fn public_key(&self) -> Ed25519PublicKey {
288 Ed25519PublicKey(self.0.verifying_key())
289 }
290
291 pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
310 Ed25519Signature(self.0.sign(message))
311 }
312}
313
314impl Default for Ed25519SecretKey {
315 fn default() -> Self {
316 Self::new()
317 }
318}
319
320#[derive(Serialize, Deserialize)]
321enum SecretKeys {
322 Normal(Box<SigningKey>),
323 Expanded(Box<ExpandedSecretKey>),
324}
325
326impl SecretKeys {
327 fn public_key(&self) -> Ed25519PublicKey {
328 match &self {
329 SecretKeys::Normal(k) => Ed25519PublicKey(k.verifying_key()),
330 SecretKeys::Expanded(k) => k.public_key(),
331 }
332 }
333
334 fn sign(&self, message: &[u8]) -> Ed25519Signature {
335 let signature = match &self {
336 SecretKeys::Normal(k) => k.sign(message),
337 SecretKeys::Expanded(k) => k.sign(message),
338 };
339
340 Ed25519Signature(signature)
341 }
342}
343
344#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
346#[serde(transparent)]
347pub struct Ed25519PublicKey(VerifyingKey);
348
349impl Ed25519PublicKey {
350 pub const LENGTH: usize = PUBLIC_KEY_LENGTH;
352
353 const BASE64_LENGTH: usize = 43;
354 const PADDED_BASE64_LENGTH: usize = 44;
355
356 pub fn from_slice(bytes: &[u8; 32]) -> Result<Self, crate::KeyError> {
358 Ok(Self(VerifyingKey::from_bytes(bytes).map_err(SignatureError::from)?))
359 }
360
361 pub fn as_bytes(&self) -> &[u8; Self::LENGTH] {
363 self.0.as_bytes()
364 }
365
366 pub fn from_base64(input: &str) -> Result<Self, crate::KeyError> {
369 if input.len() != Self::BASE64_LENGTH && input.len() != Self::PADDED_BASE64_LENGTH {
370 Err(crate::KeyError::InvalidKeyLength {
371 key_type: "Ed25519",
372 expected_length: Self::LENGTH,
373 length: decoded_len_estimate(input.len()),
374 })
375 } else {
376 let mut bytes = base64_decode(input)?;
377 let mut key_bytes = [0u8; Self::LENGTH];
378
379 if bytes.len() == Self::LENGTH {
380 key_bytes.copy_from_slice(&bytes);
381 let key = Self::from_slice(&key_bytes);
382
383 bytes.zeroize();
384 key_bytes.zeroize();
385
386 key
387 } else {
388 Err(crate::KeyError::InvalidKeyLength {
389 key_type: "Ed25519",
390 expected_length: Self::LENGTH,
391 length: bytes.len(),
392 })
393 }
394 }
395 }
396
397 pub fn to_base64(&self) -> String {
400 base64_encode(self.as_bytes())
401 }
402
403 #[cfg(not(fuzzing))]
406 pub fn verify(
407 &self,
408 message: &[u8],
409 signature: &Ed25519Signature,
410 ) -> Result<(), SignatureError> {
411 Ok(self.0.verify_strict(message, &signature.0)?)
412 }
413
414 #[cfg(fuzzing)]
416 pub fn verify(
417 &self,
418 _message: &[u8],
419 _signature: &Ed25519Signature,
420 ) -> Result<(), SignatureError> {
421 Ok(())
422 }
423}
424
425impl Display for Ed25519PublicKey {
426 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427 write!(f, "{}", self.to_base64())
428 }
429}
430
431impl std::fmt::Debug for Ed25519PublicKey {
432 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
433 let s = format!("ed25519:{self}");
434 <str as std::fmt::Debug>::fmt(&s, f)
435 }
436}
437
438#[derive(Clone, Copy, PartialEq, Eq)]
441pub struct Ed25519Signature(pub(crate) Signature);
442
443impl Ed25519Signature {
444 pub const LENGTH: usize = SIGNATURE_LENGTH;
446
447 pub fn from_slice(bytes: &[u8]) -> Result<Self, SignatureError> {
449 Ok(Self(Signature::try_from(bytes)?))
450 }
451
452 pub fn from_base64(signature: &str) -> Result<Self, SignatureError> {
455 Ok(Self(Signature::try_from(base64_decode(signature)?.as_slice())?))
456 }
457
458 pub fn to_base64(&self) -> String {
460 base64_encode(self.0.to_bytes())
461 }
462
463 pub fn to_bytes(&self) -> [u8; Self::LENGTH] {
465 self.0.to_bytes()
466 }
467}
468
469impl Display for Ed25519Signature {
470 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
471 write!(f, "{}", self.to_base64())
472 }
473}
474
475impl std::fmt::Debug for Ed25519Signature {
476 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
477 let s = format!("ed25519:{self}");
478 <str as std::fmt::Debug>::fmt(&s, f)
479 }
480}
481
482impl Clone for Ed25519Keypair {
483 fn clone(&self) -> Self {
484 let secret_key: SecretKeys = match &self.secret_key {
485 SecretKeys::Normal(k) => SecretKeys::Normal(k.clone()),
486 SecretKeys::Expanded(k) => SecretKeys::Expanded(k.clone()),
487 };
488
489 Self { secret_key, public_key: self.public_key }
490 }
491}
492
493impl From<Ed25519Keypair> for Ed25519KeypairPickle {
494 fn from(key: Ed25519Keypair) -> Self {
495 Self(key.secret_key)
496 }
497}
498
499impl From<SigningKey> for SecretKeys {
500 fn from(key: SigningKey) -> Self {
501 Self::Normal(Box::new(key))
502 }
503}
504
505impl From<ExpandedSecretKey> for SecretKeys {
506 fn from(key: ExpandedSecretKey) -> Self {
507 Self::Expanded(Box::new(key))
508 }
509}
510
511#[derive(Serialize, Deserialize)]
512#[serde(transparent)]
513pub struct Ed25519KeypairPickle(SecretKeys);
514
515impl From<Ed25519KeypairPickle> for Ed25519Keypair {
516 fn from(pickle: Ed25519KeypairPickle) -> Self {
517 let secret_key = pickle.0;
518 let public_key = secret_key.public_key();
519
520 Self { secret_key, public_key }
521 }
522}
523
524#[cfg(test)]
525mod tests {
526 use std::assert_matches;
527
528 use super::ExpandedSecretKey;
529 use crate::{Ed25519Keypair, Ed25519PublicKey, Ed25519SecretKey, Ed25519Signature, KeyError};
530
531 #[test]
532 fn byte_decoding_roundtrip_succeeds_for_secret_key() {
533 let bytes = *b"oooooooooooooooooooooooooooooooo";
534 let key = Ed25519SecretKey::from_slice(&bytes);
535 assert_eq!(*(key.to_bytes()), bytes);
536 }
537
538 #[test]
539 fn base_64_decoding_too_long_unpadded_secret_key_fails() {
540 let base64 = "MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEE";
541 assert!(matches!(
542 Ed25519SecretKey::from_base64(base64),
543 Err(KeyError::InvalidKeyLength { expected_length: 32, length: 33, .. })
544 ));
545 }
546
547 #[test]
548 fn base_64_decoding_too_short_unpadded_secret_key_fails() {
549 let base64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
550 assert!(matches!(
551 Ed25519SecretKey::from_base64(base64),
552 Err(KeyError::InvalidKeyLength { expected_length: 32, length: 31, .. })
553 ));
554 }
555
556 #[test]
557 fn base64_decoding_incorrect_num_of_bytes_fails_for_secret_key() {
558 assert!(matches!(
559 Ed25519SecretKey::from_base64("foo"),
560 Err(KeyError::InvalidKeyLength { .. })
561 ));
562 }
563
564 #[test]
565 fn unpadded_base64_decoding_roundtrip_succeeds_for_secret_key() {
566 let base64 = "MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE";
567 let key = Ed25519SecretKey::from_base64(base64).expect("Should decode key from base64");
568 assert_eq!(key.to_base64(), base64);
569 }
570
571 #[test]
572 fn padded_base64_decoding_roundtrip_succeeds_for_secret_key() {
573 let base64 = "MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE=";
574 let key = Ed25519SecretKey::from_base64(base64).expect("Should decode key from base64");
575 assert_eq!(key.to_base64(), base64.trim_end_matches('='));
576 }
577
578 #[test]
579 fn byte_decoding_roundtrip_succeeds_for_public_key() {
580 let bytes = *b"oooooooooooooooooooooooooooooooo";
581 let key = Ed25519PublicKey::from_slice(&bytes).expect("Should decode key from bytes");
582 assert_eq!(key.as_bytes(), &bytes);
583 }
584
585 #[test]
586 fn base64_decoding_incorrect_num_of_bytes_fails_for_public_key() {
587 assert_matches!(
588 Ed25519PublicKey::from_base64("foo"),
589 Err(KeyError::InvalidKeyLength { .. })
590 );
591 }
592
593 #[test]
594 fn base_64_decoding_too_long_unpadded_public_key_fails() {
595 let base64 = "b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb288";
596 assert_matches!(
597 Ed25519PublicKey::from_base64(base64),
598 Err(KeyError::InvalidKeyLength { expected_length: 32, length: 33, .. })
599 )
600 }
601
602 #[test]
603 fn base_64_decoding_too_short_unpadded_public_key_fails() {
604 let base64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
605 assert_matches!(
606 Ed25519PublicKey::from_base64(base64),
607 Err(KeyError::InvalidKeyLength { expected_length: 32, length: 31, .. })
608 )
609 }
610
611 #[test]
612 fn unpadded_base64_decoding_roundtrip_succeeds_for_public_key() {
613 let base64 = "b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28";
614 let key = Ed25519PublicKey::from_base64(base64).expect("Should decode key from base64");
615 assert_eq!(key.to_base64(), base64);
616 }
617
618 #[test]
619 fn padded_base64_decoding_roundtrip_succeeds_for_public_key() {
620 let base64 = "b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28=";
621 let key = Ed25519PublicKey::from_base64(base64).expect("Should decode key from base64");
622 assert_eq!(key.to_base64(), base64.trim_end_matches('='));
623 }
624
625 #[test]
626 fn verifying_valid_signature_succeeds() {
627 let key_pair = Ed25519Keypair::new();
628 let signature = key_pair.secret_key.sign(b"foo");
629 key_pair.public_key().verify(b"foo", &signature).expect("Should verify valid signature");
630 }
631
632 #[test]
633 fn verifying_invalid_signature_fails() {
634 let key_pair = Ed25519Keypair::new();
635 let signature = key_pair.secret_key.sign(b"foo");
636 key_pair
637 .public_key()
638 .verify(b"bar", &signature)
639 .expect_err("Should reject invalid signature");
640 }
641
642 #[test]
643 #[cfg(feature = "libolm-compat")]
644 fn can_only_expand_secret_key_once() {
645 let key_pair = Ed25519Keypair::new();
646 assert!(matches!(key_pair.secret_key, crate::types::ed25519::SecretKeys::Normal(_)));
647
648 let expanded_key = key_pair.expanded_secret_key();
649 let expanded_key_pair = Ed25519Keypair::from_expanded_key(&expanded_key).unwrap();
650 assert!(matches!(
651 expanded_key_pair.secret_key,
652 crate::types::ed25519::SecretKeys::Expanded(_)
653 ));
654 assert_eq!(expanded_key_pair.public_key(), key_pair.public_key());
655
656 let reexpanded_key = expanded_key_pair.expanded_secret_key();
657 assert_eq!(reexpanded_key, expanded_key);
658 }
659
660 #[test]
661 fn serialization_roundtrip_succeeds() {
662 let bytes = b"9999999999999999999999999999999999999999999999999999999999999999";
663 let key = ExpandedSecretKey::from_bytes(bytes);
664 let serialized = serde_json::to_value(key).expect("Should serialize key");
665 let deserialized = serde_json::from_value::<ExpandedSecretKey>(serialized)
666 .expect("Should deserialize key");
667 assert_eq!(deserialized.as_bytes(), bytes);
668 }
669
670 #[test]
671 fn deserializing_from_invalid_length_fails() {
672 let serialized = serde_json::to_value(b"foo").expect("Should serialize key");
673 let deserialized = serde_json::from_value::<ExpandedSecretKey>(serialized);
674 assert!(deserialized.is_err());
675 }
676
677 #[test]
678 fn unexpanded_key_roundtrip_succeeds() {
679 let key_pair = Ed25519Keypair::new();
680
681 let unexpanded_key = key_pair.unexpanded_secret_key().expect("Should have unexpanded key");
682 let recovered_key_pair = Ed25519Keypair::from_unexpanded_key(unexpanded_key.as_ref())
683 .expect("Should create new keypair");
684
685 assert_eq!(key_pair.public_key().to_base64(), recovered_key_pair.public_key().to_base64());
686 }
687
688 #[test]
689 fn snapshot_public_key_debug() {
690 let public_key = Ed25519PublicKey::from_slice(&[0; 32])
691 .expect("We should be able to create a public key from zero bytes");
692
693 insta::assert_debug_snapshot!(public_key)
694 }
695
696 #[test]
697 fn snapshot_signature_debug() {
698 let signature = Ed25519Signature::from_slice(&[0; 64])
699 .expect("We should be able to create a public key from zero bytes");
700
701 insta::assert_debug_snapshot!(signature)
702 }
703}