Skip to main content

vodozemac/types/
ed25519.rs

1// Copyright 2021 Denis Kasak, Damir Jelić
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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/// Error type describing signature verification failures.
33#[derive(Debug, Error)]
34pub enum SignatureError {
35    /// The signature wasn't valid base64.
36    #[error("The signature couldn't be decoded: {0}")]
37    Base64(#[from] base64::DecodeError),
38    /// The signature failed to be verified.
39    #[error("The signature was invalid: {0}")]
40    Signature(#[from] ed25519_dalek::SignatureError),
41}
42
43/// A struct collecting both a public, and a secret, Ed25519 key.
44#[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    /// Create a new, random, `Ed25519Keypair`.
129    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    /// Get the public Ed25519 key of this keypair.
182    pub const fn public_key(&self) -> Ed25519PublicKey {
183        self.public_key
184    }
185
186    /// Sign the given message with our secret key.
187    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/// An Ed25519 secret key, used to create digital signatures.
199#[derive(Deserialize, Serialize)]
200#[serde(transparent)]
201pub struct Ed25519SecretKey(Box<SigningKey>);
202
203impl Ed25519SecretKey {
204    /// The number of bytes a Ed25519 secret key has.
205    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    /// Create a new random `Ed25519SecretKey`.
211    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    /// Get the byte representation of the secret key.
220    ///
221    /// **Warning**: This creates a copy of the key which won't be zeroized, the
222    /// caller of the method needs to make sure to zeroize the returned array.
223    pub fn to_bytes(&self) -> Box<[u8; 32]> {
224        Box::new(self.0.to_bytes())
225    }
226
227    /// Try to create a `Ed25519SecretKey` from a slice of bytes.
228    pub fn from_slice(bytes: &[u8; 32]) -> Self {
229        Self(Box::new(SigningKey::from_bytes(bytes)))
230    }
231
232    /// Convert the secret key to a base64 encoded string.
233    ///
234    /// This can be useful if the secret key needs to be sent over the network
235    /// or persisted.
236    ///
237    /// **Warning**: The string should be zeroized after it has been used,
238    /// otherwise an unintentional copy of the key might exist in memory.
239    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    /// Try to create a `Ed25519SecretKey` from a base64 encoded string.
249    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            // Ed25519 secret keys can sometimes be encoded with padding, don't ask me why.
258            // This means that if the unpadded decoding fails, we have to attempt the padded
259            // one.
260            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 mut key_bytes = [0u8; 32];
267            key_bytes.copy_from_slice(&bytes);
268            let key = Self::from_slice(&key_bytes);
269
270            bytes.zeroize();
271            key_bytes.zeroize();
272
273            Ok(key)
274        }
275    }
276
277    /// Get the public key that matches this `Ed25519SecretKey`.
278    pub fn public_key(&self) -> Ed25519PublicKey {
279        Ed25519PublicKey(self.0.verifying_key())
280    }
281
282    /// Sign the given slice of bytes with this `Ed25519SecretKey`.
283    ///
284    /// The signature can be verified using the public key.
285    ///
286    /// # Examples
287    ///
288    /// ```
289    /// use vodozemac::{Ed25519SecretKey, Ed25519PublicKey};
290    ///
291    /// let secret = Ed25519SecretKey::new();
292    /// let message = "It's dangerous to go alone";
293    ///
294    /// let signature = secret.sign(message.as_bytes());
295    ///
296    /// let public_key = secret.public_key();
297    ///
298    /// public_key.verify(message.as_bytes(), &signature).expect("The signature has to be valid");
299    /// ```
300    pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
301        Ed25519Signature(self.0.sign(message))
302    }
303}
304
305impl Default for Ed25519SecretKey {
306    fn default() -> Self {
307        Self::new()
308    }
309}
310
311#[derive(Serialize, Deserialize)]
312enum SecretKeys {
313    Normal(Box<SigningKey>),
314    Expanded(Box<ExpandedSecretKey>),
315}
316
317impl SecretKeys {
318    fn public_key(&self) -> Ed25519PublicKey {
319        match &self {
320            SecretKeys::Normal(k) => Ed25519PublicKey(k.verifying_key()),
321            SecretKeys::Expanded(k) => k.public_key(),
322        }
323    }
324
325    fn sign(&self, message: &[u8]) -> Ed25519Signature {
326        let signature = match &self {
327            SecretKeys::Normal(k) => k.sign(message),
328            SecretKeys::Expanded(k) => k.sign(message),
329        };
330
331        Ed25519Signature(signature)
332    }
333}
334
335/// An Ed25519 public key, used to verify digital signatures.
336#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
337#[serde(transparent)]
338pub struct Ed25519PublicKey(VerifyingKey);
339
340impl Ed25519PublicKey {
341    /// The number of bytes a Ed25519 public key has.
342    pub const LENGTH: usize = PUBLIC_KEY_LENGTH;
343
344    const BASE64_LENGTH: usize = 43;
345    const PADDED_BASE64_LENGTH: usize = 44;
346
347    /// Try to create a `Ed25519PublicKey` from a slice of bytes.
348    pub fn from_slice(bytes: &[u8; 32]) -> Result<Self, crate::KeyError> {
349        Ok(Self(VerifyingKey::from_bytes(bytes).map_err(SignatureError::from)?))
350    }
351
352    /// View this public key as a byte array.
353    pub fn as_bytes(&self) -> &[u8; Self::LENGTH] {
354        self.0.as_bytes()
355    }
356
357    /// Instantiate a Ed25519PublicKey public key from an unpadded base64
358    /// representation.
359    pub fn from_base64(input: &str) -> Result<Self, crate::KeyError> {
360        if input.len() != Self::BASE64_LENGTH && input.len() != Self::PADDED_BASE64_LENGTH {
361            Err(crate::KeyError::InvalidKeyLength {
362                key_type: "Ed25519",
363                expected_length: Self::LENGTH,
364                length: decoded_len_estimate(input.len()),
365            })
366        } else {
367            let mut bytes = base64_decode(input)?;
368            let mut key_bytes = [0u8; 32];
369
370            key_bytes.copy_from_slice(&bytes);
371            let key = Self::from_slice(&key_bytes);
372
373            bytes.zeroize();
374            key_bytes.zeroize();
375
376            key
377        }
378    }
379
380    /// Serialize a Ed25519PublicKey public key to an unpadded base64
381    /// representation.
382    pub fn to_base64(&self) -> String {
383        base64_encode(self.as_bytes())
384    }
385
386    /// Verify that the provided signature for a given message has been signed
387    /// by the private key matching this public one.
388    #[cfg(not(fuzzing))]
389    pub fn verify(
390        &self,
391        message: &[u8],
392        signature: &Ed25519Signature,
393    ) -> Result<(), SignatureError> {
394        Ok(self.0.verify_strict(message, &signature.0)?)
395    }
396
397    #[cfg(fuzzing)]
398    #[allow(missing_docs)]
399    pub fn verify(
400        &self,
401        _message: &[u8],
402        _signature: &Ed25519Signature,
403    ) -> Result<(), SignatureError> {
404        Ok(())
405    }
406}
407
408impl Display for Ed25519PublicKey {
409    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410        write!(f, "{}", self.to_base64())
411    }
412}
413
414impl std::fmt::Debug for Ed25519PublicKey {
415    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
416        let s = format!("ed25519:{self}");
417        <str as std::fmt::Debug>::fmt(&s, f)
418    }
419}
420
421/// An Ed25519 digital signature, can be used to verify the authenticity of a
422/// message.
423#[derive(Clone, Copy, PartialEq, Eq)]
424pub struct Ed25519Signature(pub(crate) Signature);
425
426impl Ed25519Signature {
427    /// The number of bytes a Ed25519 signature has.
428    pub const LENGTH: usize = SIGNATURE_LENGTH;
429
430    /// Try to create a `Ed25519Signature` from a slice of bytes.
431    pub fn from_slice(bytes: &[u8]) -> Result<Self, SignatureError> {
432        Ok(Self(Signature::try_from(bytes)?))
433    }
434
435    /// Try to create a `Ed25519Signature` from an unpadded base64
436    /// representation.
437    pub fn from_base64(signature: &str) -> Result<Self, SignatureError> {
438        Ok(Self(Signature::try_from(base64_decode(signature)?.as_slice())?))
439    }
440
441    /// Serialize an `Ed25519Signature` to an unpadded base64 representation.
442    pub fn to_base64(&self) -> String {
443        base64_encode(self.0.to_bytes())
444    }
445
446    /// Convert the `Ed25519Signature` to a byte array.
447    pub fn to_bytes(&self) -> [u8; Self::LENGTH] {
448        self.0.to_bytes()
449    }
450}
451
452impl Display for Ed25519Signature {
453    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
454        write!(f, "{}", self.to_base64())
455    }
456}
457
458impl std::fmt::Debug for Ed25519Signature {
459    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
460        let s = format!("ed25519:{self}");
461        <str as std::fmt::Debug>::fmt(&s, f)
462    }
463}
464
465impl Clone for Ed25519Keypair {
466    fn clone(&self) -> Self {
467        let secret_key: SecretKeys = match &self.secret_key {
468            SecretKeys::Normal(k) => SecretKeys::Normal(k.clone()),
469            SecretKeys::Expanded(k) => SecretKeys::Expanded(k.clone()),
470        };
471
472        Self { secret_key, public_key: self.public_key }
473    }
474}
475
476impl From<Ed25519Keypair> for Ed25519KeypairPickle {
477    fn from(key: Ed25519Keypair) -> Self {
478        Self(key.secret_key)
479    }
480}
481
482impl From<SigningKey> for SecretKeys {
483    fn from(key: SigningKey) -> Self {
484        Self::Normal(Box::new(key))
485    }
486}
487
488impl From<ExpandedSecretKey> for SecretKeys {
489    fn from(key: ExpandedSecretKey) -> Self {
490        Self::Expanded(Box::new(key))
491    }
492}
493
494#[derive(Serialize, Deserialize)]
495#[serde(transparent)]
496pub struct Ed25519KeypairPickle(SecretKeys);
497
498impl From<Ed25519KeypairPickle> for Ed25519Keypair {
499    fn from(pickle: Ed25519KeypairPickle) -> Self {
500        let secret_key = pickle.0;
501        let public_key = secret_key.public_key();
502
503        Self { secret_key, public_key }
504    }
505}
506
507#[cfg(test)]
508mod tests {
509    use assert_matches2::assert_matches;
510
511    use super::ExpandedSecretKey;
512    use crate::{Ed25519Keypair, Ed25519PublicKey, Ed25519SecretKey, Ed25519Signature, KeyError};
513
514    #[test]
515    fn byte_decoding_roundtrip_succeeds_for_secret_key() {
516        let bytes = *b"oooooooooooooooooooooooooooooooo";
517        let key = Ed25519SecretKey::from_slice(&bytes);
518        assert_eq!(*(key.to_bytes()), bytes);
519    }
520
521    #[test]
522    fn base64_decoding_incorrect_num_of_bytes_fails_for_secret_key() {
523        assert!(matches!(
524            Ed25519SecretKey::from_base64("foo"),
525            Err(KeyError::InvalidKeyLength { .. })
526        ));
527    }
528
529    #[test]
530    fn unpadded_base64_decoding_roundtrip_succeeds_for_secret_key() {
531        let base64 = "MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE";
532        let key = Ed25519SecretKey::from_base64(base64).expect("Should decode key from base64");
533        assert_eq!(key.to_base64(), base64);
534    }
535
536    #[test]
537    fn padded_base64_decoding_roundtrip_succeeds_for_secret_key() {
538        let base64 = "MTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTE=";
539        let key = Ed25519SecretKey::from_base64(base64).expect("Should decode key from base64");
540        assert_eq!(key.to_base64(), base64.trim_end_matches('='));
541    }
542
543    #[test]
544    fn byte_decoding_roundtrip_succeeds_for_public_key() {
545        let bytes = *b"oooooooooooooooooooooooooooooooo";
546        let key = Ed25519PublicKey::from_slice(&bytes).expect("Should decode key from bytes");
547        assert_eq!(key.as_bytes(), &bytes);
548    }
549
550    #[test]
551    fn base64_decoding_incorrect_num_of_bytes_fails_for_public_key() {
552        assert_matches!(
553            Ed25519PublicKey::from_base64("foo"),
554            Err(KeyError::InvalidKeyLength { .. })
555        );
556    }
557
558    #[test]
559    fn unpadded_base64_decoding_roundtrip_succeeds_for_public_key() {
560        let base64 = "b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28";
561        let key = Ed25519PublicKey::from_base64(base64).expect("Should decode key from base64");
562        assert_eq!(key.to_base64(), base64);
563    }
564
565    #[test]
566    fn padded_base64_decoding_roundtrip_succeeds_for_public_key() {
567        let base64 = "b29vb29vb29vb29vb29vb29vb29vb29vb29vb29vb28=";
568        let key = Ed25519PublicKey::from_base64(base64).expect("Should decode key from base64");
569        assert_eq!(key.to_base64(), base64.trim_end_matches('='));
570    }
571
572    #[test]
573    fn verifying_valid_signature_succeeds() {
574        let key_pair = Ed25519Keypair::new();
575        let signature = key_pair.secret_key.sign(b"foo");
576        key_pair.public_key().verify(b"foo", &signature).expect("Should verify valid signature");
577    }
578
579    #[test]
580    fn verifying_invalid_signature_fails() {
581        let key_pair = Ed25519Keypair::new();
582        let signature = key_pair.secret_key.sign(b"foo");
583        key_pair
584            .public_key()
585            .verify(b"bar", &signature)
586            .expect_err("Should reject invalid signature");
587    }
588
589    #[test]
590    #[cfg(feature = "libolm-compat")]
591    fn can_only_expand_secret_key_once() {
592        let key_pair = Ed25519Keypair::new();
593        assert!(matches!(key_pair.secret_key, crate::types::ed25519::SecretKeys::Normal(_)));
594
595        let expanded_key = key_pair.expanded_secret_key();
596        let expanded_key_pair = Ed25519Keypair::from_expanded_key(&expanded_key).unwrap();
597        assert!(matches!(
598            expanded_key_pair.secret_key,
599            crate::types::ed25519::SecretKeys::Expanded(_)
600        ));
601        assert_eq!(expanded_key_pair.public_key(), key_pair.public_key());
602
603        let reexpanded_key = expanded_key_pair.expanded_secret_key();
604        assert_eq!(reexpanded_key, expanded_key);
605    }
606
607    #[test]
608    fn serialization_roundtrip_succeeds() {
609        let bytes = b"9999999999999999999999999999999999999999999999999999999999999999";
610        let key = ExpandedSecretKey::from_bytes(bytes);
611        let serialized = serde_json::to_value(key).expect("Should serialize key");
612        let deserialized = serde_json::from_value::<ExpandedSecretKey>(serialized)
613            .expect("Should deserialize key");
614        assert_eq!(deserialized.as_bytes(), bytes);
615    }
616
617    #[test]
618    fn deserializing_from_invalid_length_fails() {
619        let serialized = serde_json::to_value(b"foo").expect("Should serialize key");
620        let deserialized = serde_json::from_value::<ExpandedSecretKey>(serialized);
621        assert!(deserialized.is_err());
622    }
623
624    #[test]
625    fn unexpanded_key_roundtrip_succeeds() {
626        let key_pair = Ed25519Keypair::new();
627
628        let unexpanded_key = key_pair.unexpanded_secret_key().expect("Should have unexpanded key");
629        let recovered_key_pair = Ed25519Keypair::from_unexpanded_key(unexpanded_key.as_ref())
630            .expect("Should create new keypair");
631
632        assert_eq!(key_pair.public_key().to_base64(), recovered_key_pair.public_key().to_base64());
633    }
634
635    #[test]
636    fn snapshot_public_key_debug() {
637        let public_key = Ed25519PublicKey::from_slice(&[0; 32])
638            .expect("We should be able to create a public key from zero bytes");
639
640        insta::assert_debug_snapshot!(public_key)
641    }
642
643    #[test]
644    fn snapshot_signature_debug() {
645        let signature = Ed25519Signature::from_slice(&[0; 64])
646            .expect("We should be able to create a public key from zero bytes");
647
648        insta::assert_debug_snapshot!(signature)
649    }
650}