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::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/// 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 = 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    /// Get the public Ed25519 key of this keypair.
179    pub const fn public_key(&self) -> Ed25519PublicKey {
180        self.public_key
181    }
182
183    /// Sign the given message with our secret key.
184    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/// An Ed25519 secret key, used to create digital signatures.
196#[derive(Deserialize, Serialize)]
197#[serde(transparent)]
198pub struct Ed25519SecretKey(Box<SigningKey>);
199
200impl Ed25519SecretKey {
201    /// The number of bytes a Ed25519 secret key has.
202    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    /// Create a new random `Ed25519SecretKey`.
208    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    /// Get the byte representation of the secret key.
217    ///
218    /// **Warning**: This creates a copy of the key which won't be zeroized, the
219    /// caller of the method needs to make sure to zeroize the returned array.
220    pub fn to_bytes(&self) -> Box<[u8; 32]> {
221        Box::new(self.0.to_bytes())
222    }
223
224    /// Try to create a `Ed25519SecretKey` from a slice of bytes.
225    pub fn from_slice(bytes: &[u8; 32]) -> Self {
226        Self(Box::new(SigningKey::from_bytes(bytes)))
227    }
228
229    /// Convert the secret key to a base64 encoded string.
230    ///
231    /// This can be useful if the secret key needs to be sent over the network
232    /// or persisted.
233    ///
234    /// **Warning**: The string should be zeroized after it has been used,
235    /// otherwise an unintentional copy of the key might exist in memory.
236    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    /// Try to create a `Ed25519SecretKey` from a base64 encoded string.
246    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            // Ed25519 secret keys can sometimes be encoded with padding, don't ask me why.
255            // This means that if the unpadded decoding fails, we have to attempt the padded
256            // one.
257            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    /// Get the public key that matches this `Ed25519SecretKey`.
275    pub fn public_key(&self) -> Ed25519PublicKey {
276        Ed25519PublicKey(self.0.verifying_key())
277    }
278
279    /// Sign the given slice of bytes with this `Ed25519SecretKey`.
280    ///
281    /// The signature can be verified using the public key.
282    ///
283    /// # Examples
284    ///
285    /// ```
286    /// use vodozemac::{Ed25519SecretKey, Ed25519PublicKey};
287    ///
288    /// let secret = Ed25519SecretKey::new();
289    /// let message = "It's dangerous to go alone";
290    ///
291    /// let signature = secret.sign(message.as_bytes());
292    ///
293    /// let public_key = secret.public_key();
294    ///
295    /// public_key.verify(message.as_bytes(), &signature).expect("The signature has to be valid");
296    /// ```
297    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/// An Ed25519 public key, used to verify digital signatures.
333#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
334#[serde(transparent)]
335pub struct Ed25519PublicKey(VerifyingKey);
336
337impl Ed25519PublicKey {
338    /// The number of bytes a Ed25519 public key has.
339    pub const LENGTH: usize = PUBLIC_KEY_LENGTH;
340
341    const BASE64_LENGTH: usize = 43;
342    const PADDED_BASE64_LENGTH: usize = 44;
343
344    /// Try to create a `Ed25519PublicKey` from a slice of bytes.
345    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    /// View this public key as a byte array.
350    pub fn as_bytes(&self) -> &[u8; Self::LENGTH] {
351        self.0.as_bytes()
352    }
353
354    /// Instantiate a Ed25519PublicKey public key from an unpadded base64
355    /// representation.
356    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    /// Serialize a Ed25519PublicKey public key to an unpadded base64
378    /// representation.
379    pub fn to_base64(&self) -> String {
380        base64_encode(self.as_bytes())
381    }
382
383    /// Verify that the provided signature for a given message has been signed
384    /// by the private key matching this public one.
385    #[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/// An Ed25519 digital signature, can be used to verify the authenticity of a
419/// message.
420#[derive(Clone, Copy, PartialEq, Eq)]
421pub struct Ed25519Signature(pub(crate) Signature);
422
423impl Ed25519Signature {
424    /// The number of bytes a Ed25519 signature has.
425    pub const LENGTH: usize = SIGNATURE_LENGTH;
426
427    /// Try to create a `Ed25519Signature` from a slice of bytes.
428    pub fn from_slice(bytes: &[u8]) -> Result<Self, SignatureError> {
429        Ok(Self(Signature::try_from(bytes)?))
430    }
431
432    /// Try to create a `Ed25519Signature` from an unpadded base64
433    /// representation.
434    pub fn from_base64(signature: &str) -> Result<Self, SignatureError> {
435        Ok(Self(Signature::try_from(base64_decode(signature)?.as_slice())?))
436    }
437
438    /// Serialize an `Ed25519Signature` to an unpadded base64 representation.
439    pub fn to_base64(&self) -> String {
440        base64_encode(self.0.to_bytes())
441    }
442
443    /// Convert the `Ed25519Signature` to a byte array.
444    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}