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;
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/// Error type describing signature verification failures.
35#[derive(Debug, Error)]
36pub enum SignatureError {
37    /// The signature wasn't valid base64.
38    #[error("The signature couldn't be decoded: {0}")]
39    Base64(#[from] base64::DecodeError),
40    /// The signature failed to be verified.
41    #[error("The signature was invalid: {0}")]
42    Signature(#[from] ed25519_dalek::SignatureError),
43}
44
45/// A struct collecting both a public, and a secret, Ed25519 key.
46#[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    /// Create a new, random, `Ed25519Keypair`.
131    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    /// Get the public Ed25519 key of this keypair.
181    pub const fn public_key(&self) -> Ed25519PublicKey {
182        self.public_key
183    }
184
185    /// Sign the given message with our secret key.
186    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/// An Ed25519 secret key, used to create digital signatures.
198#[derive(Deserialize, Serialize)]
199#[serde(transparent)]
200pub struct Ed25519SecretKey(Box<SigningKey>);
201
202impl Ed25519SecretKey {
203    /// The number of bytes a Ed25519 secret key has.
204    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    /// Create a new random `Ed25519SecretKey`.
210    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    /// Get the byte representation of the secret key.
219    ///
220    /// **Warning**: This creates a copy of the key which won't be zeroized, the
221    /// caller of the method needs to make sure to zeroize the returned array.
222    pub fn to_bytes(&self) -> Box<[u8; 32]> {
223        Box::new(self.0.to_bytes())
224    }
225
226    /// Try to create a `Ed25519SecretKey` from a slice of bytes.
227    pub fn from_slice(bytes: &[u8; 32]) -> Self {
228        Self(Box::new(SigningKey::from_bytes(bytes)))
229    }
230
231    /// Convert the secret key to a base64 encoded string.
232    ///
233    /// This can be useful if the secret key needs to be sent over the network
234    /// or persisted.
235    ///
236    /// **Warning**: The string should be zeroized after it has been used,
237    /// otherwise an unintentional copy of the key might exist in memory.
238    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    /// Try to create a `Ed25519SecretKey` from a base64 encoded string.
248    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            // Ed25519 secret keys can sometimes be encoded with padding, don't ask me why.
257            // This means that if the unpadded decoding fails, we have to attempt the padded
258            // one.
259            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    /// Get the public key that matches this `Ed25519SecretKey`.
277    pub fn public_key(&self) -> Ed25519PublicKey {
278        Ed25519PublicKey(self.0.verifying_key())
279    }
280
281    /// Sign the given slice of bytes with this `Ed25519SecretKey`.
282    ///
283    /// The signature can be verified using the public key.
284    ///
285    /// # Examples
286    ///
287    /// ```
288    /// use vodozemac::{Ed25519SecretKey, Ed25519PublicKey};
289    ///
290    /// let secret = Ed25519SecretKey::new();
291    /// let message = "It's dangerous to go alone";
292    ///
293    /// let signature = secret.sign(message.as_bytes());
294    ///
295    /// let public_key = secret.public_key();
296    ///
297    /// public_key.verify(message.as_bytes(), &signature).expect("The signature has to be valid");
298    /// ```
299    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/// An Ed25519 public key, used to verify digital signatures.
335#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
336#[serde(transparent)]
337pub struct Ed25519PublicKey(VerifyingKey);
338
339impl Ed25519PublicKey {
340    /// The number of bytes a Ed25519 public key has.
341    pub const LENGTH: usize = PUBLIC_KEY_LENGTH;
342
343    const BASE64_LENGTH: usize = 43;
344    const PADDED_BASE64_LENGTH: usize = 44;
345
346    /// Try to create a `Ed25519PublicKey` from a slice of bytes.
347    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    /// View this public key as a byte array.
352    pub fn as_bytes(&self) -> &[u8; Self::LENGTH] {
353        self.0.as_bytes()
354    }
355
356    /// Instantiate a Ed25519PublicKey public key from an unpadded base64
357    /// representation.
358    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    /// Serialize a Ed25519PublicKey public key to an unpadded base64
380    /// representation.
381    pub fn to_base64(&self) -> String {
382        base64_encode(self.as_bytes())
383    }
384
385    /// Verify that the provided signature for a given message has been signed
386    /// by the private key matching this public one.
387    ///
388    /// By default this performs an [RFC8032] compatible signature check. A
389    /// stricter version of the signature check can be enabled with the
390    /// `strict-signatures` feature flag.
391    ///
392    /// The stricter variant is compatible with libsodium 0.16 and under the
393    /// hood uses the [`ed25519_dalek::PublicKey::verify_strict()`] method.
394    ///
395    /// For more info, see the ed25519_dalek [README] and [this] post.
396    ///
397    /// [RFC8032]: https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.7
398    /// [README]: https://github.com/dalek-cryptography/ed25519-dalek#a-note-on-signature-malleability
399    /// [this]: https://hdevalence.ca/blog/2020-10-04-its-25519am
400    #[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/// An Ed25519 digital signature, can be used to verify the authenticity of a
438/// message.
439#[derive(Clone, Copy, PartialEq, Eq)]
440pub struct Ed25519Signature(pub(crate) Signature);
441
442impl Ed25519Signature {
443    /// The number of bytes a Ed25519 signature has.
444    pub const LENGTH: usize = SIGNATURE_LENGTH;
445
446    /// Try to create a `Ed25519Signature` from a slice of bytes.
447    pub fn from_slice(bytes: &[u8]) -> Result<Self, SignatureError> {
448        Ok(Self(Signature::try_from(bytes)?))
449    }
450
451    /// Try to create a `Ed25519Signature` from an unpadded base64
452    /// representation.
453    pub fn from_base64(signature: &str) -> Result<Self, SignatureError> {
454        Ok(Self(Signature::try_from(base64_decode(signature)?.as_slice())?))
455    }
456
457    /// Serialize an `Ed25519Signature` to an unpadded base64 representation.
458    pub fn to_base64(&self) -> String {
459        base64_encode(self.0.to_bytes())
460    }
461
462    /// Convert the `Ed25519Signature` to a byte array.
463    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}