Skip to main content

miden_crypto/dsa/eddsa_25519_sha512/
mod.rs

1//! Ed25519 (EdDSA) signature implementation using Curve25519 and SHA-512 to hash
2//! the messages when signing.
3
4use alloc::{string::ToString, vec::Vec};
5use core::fmt;
6
7use der::{Decode, asn1::BitStringRef};
8use ed25519_dalek::{Signer, Verifier};
9use miden_crypto_derive::{SilentDebug, SilentDisplay};
10use rand::CryptoRng;
11use thiserror::Error;
12
13use crate::{
14    Felt, SequentialCommit, Word,
15    ecdh::x25519::{EphemeralPublicKey, SharedSecret},
16    utils::{
17        ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
18        bytes_to_packed_u32_elements, read_sensitive_array,
19        zeroize::{Zeroize, ZeroizeOnDrop},
20    },
21};
22
23mod tests;
24
25// CONSTANTS
26// ================================================================================================
27
28/// Length of secret key in bytes
29const SECRET_KEY_BYTES: usize = 32;
30/// Length of public key in bytes
31pub(crate) const PUBLIC_KEY_BYTES: usize = 32;
32/// Length of signature in bytes
33const SIGNATURE_BYTES: usize = 64;
34
35// SECRET KEY
36// ================================================================================================
37
38/// Secret key for EdDSA (Ed25519) signature verification over Curve25519.
39#[derive(Clone, SilentDebug, SilentDisplay)]
40#[cfg_attr(
41    all(feature = "arbitrary", test),
42    miden_test_serialization_macros::serialization_test
43)]
44struct SecretKey {
45    inner: ed25519_dalek::SigningKey,
46}
47
48impl SecretKey {
49    /// Generates a new secret key using RNG.
50    fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
51        let mut seed = [0u8; SECRET_KEY_BYTES];
52        rng.fill_bytes(&mut seed);
53
54        let inner = ed25519_dalek::SigningKey::from_bytes(&seed);
55
56        // Zeroize the seed to prevent leaking secret material
57        seed.zeroize();
58
59        Self { inner }
60    }
61
62    /// Gets the corresponding public key for this secret key.
63    fn public_key(&self) -> PublicKey {
64        PublicKey { inner: self.inner.verifying_key() }
65    }
66
67    /// Signs a message (Word) with this secret key.
68    fn sign(&self, message: Word) -> Signature {
69        let message_bytes: [u8; 32] = message.into();
70        let sig = self.inner.sign(&message_bytes);
71        Signature { inner: sig }
72    }
73
74    /// Computes a Diffie-Hellman shared secret from this secret key and the ephemeral public key
75    /// generated by the other party.
76    fn get_shared_secret(&self, pk_e: &EphemeralPublicKey) -> SharedSecret {
77        let shared = self.to_x25519().diffie_hellman(&pk_e.inner);
78        SharedSecret::new(shared)
79    }
80
81    /// Converts this Ed25519 secret key into an [`x25519_dalek::StaticSecret`].
82    ///
83    /// This conversion allows using the same underlying scalar from the Ed25519 secret key
84    /// for X25519 Diffie-Hellman key exchange. The returned `StaticSecret` can then be used
85    /// in key agreement protocols to establish a shared secret with another party's
86    /// X25519 public key.
87    fn to_x25519(&self) -> x25519_dalek::StaticSecret {
88        let mut scalar_bytes = self.inner.to_scalar_bytes();
89        let static_secret = x25519_dalek::StaticSecret::from(scalar_bytes);
90
91        // Zeroize the temporary scalar bytes
92        scalar_bytes.zeroize();
93
94        static_secret
95    }
96}
97
98// SAFETY: The inner `ed25519_dalek::SigningKey` already implements `ZeroizeOnDrop`,
99// which ensures that the secret key material is securely zeroized when dropped.
100impl ZeroizeOnDrop for SecretKey {}
101
102impl PartialEq for SecretKey {
103    fn eq(&self, other: &Self) -> bool {
104        use subtle::ConstantTimeEq;
105        self.inner.to_bytes().ct_eq(&other.inner.to_bytes()).into()
106    }
107}
108
109impl Eq for SecretKey {}
110
111// SIGNING KEY
112// ================================================================================================
113
114/// A secret key for EdDSA (Ed25519) signature verification over Curve25519.
115#[derive(Clone, Eq, PartialEq, SilentDebug, SilentDisplay)]
116#[cfg_attr(
117    all(feature = "arbitrary", test),
118    miden_test_serialization_macros::serialization_test
119)] // Safe as SecretKey has const-time eq
120pub struct SigningKey(SecretKey);
121
122impl SigningKey {
123    /// Generates a new random signing key using the OS random number generator.
124    ///
125    /// This is cryptographically secure as long as [`rand::rng`] remains so.
126    #[cfg(feature = "std")]
127    #[allow(clippy::new_without_default)]
128    pub fn new() -> Self {
129        let mut rng = rand::rng();
130        Self::with_rng(&mut rng)
131    }
132
133    /// Generates a new secret key using RNG.
134    pub fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
135        Self(SecretKey::with_rng(rng))
136    }
137
138    /// Gets the corresponding public key for this secret key.
139    pub fn public_key(&self) -> PublicKey {
140        self.0.public_key()
141    }
142
143    /// Signs a message (Word) with this secret key.
144    pub fn sign(&self, message: Word) -> Signature {
145        self.0.sign(message)
146    }
147}
148
149impl From<SecretKey> for SigningKey {
150    fn from(secret_key: SecretKey) -> Self {
151        Self(secret_key)
152    }
153}
154
155// SAFETY: The inner `SecretKey` already implements `ZeroizeOnDrop` which ensures that the secret
156// key material is securely zeroized when dropped.
157impl ZeroizeOnDrop for SigningKey {}
158
159impl Serializable for SigningKey {
160    fn write_into<W: ByteWriter>(&self, target: &mut W) {
161        self.0.write_into(target);
162    }
163}
164
165impl Deserializable for SigningKey {
166    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
167        Ok(Self(SecretKey::read_from(source)?))
168    }
169}
170
171// KEY EXCHANGE KEY
172// ================================================================================================
173
174/// A key for ECDH key exchange over Curve25519
175#[derive(Clone, Eq, PartialEq, SilentDebug, SilentDisplay)]
176#[cfg_attr(
177    all(feature = "arbitrary", test),
178    miden_test_serialization_macros::serialization_test
179)] // Safe as SecretKey has const-time eq
180pub struct KeyExchangeKey(SecretKey);
181
182impl KeyExchangeKey {
183    /// Generates a new random key exchange key using the OS random number generator.
184    ///
185    /// This is cryptographically secure as long as [`rand::rng`] remains so.
186    #[cfg(feature = "std")]
187    #[allow(clippy::new_without_default)]
188    pub fn new() -> Self {
189        let mut rng = rand::rng();
190        Self::with_rng(&mut rng)
191    }
192
193    /// Generates a new secret key using RNG.
194    pub fn with_rng<R: CryptoRng>(rng: &mut R) -> Self {
195        Self(SecretKey::with_rng(rng))
196    }
197
198    /// Gets the corresponding public key for this secret key.
199    pub fn public_key(&self) -> PublicKey {
200        self.0.public_key()
201    }
202
203    /// Computes a Diffie-Hellman shared secret from this secret key and the ephemeral public key
204    /// generated by the other party.
205    pub fn get_shared_secret(&self, pk_e: EphemeralPublicKey) -> SharedSecret {
206        self.0.get_shared_secret(&pk_e)
207    }
208}
209
210impl From<SecretKey> for KeyExchangeKey {
211    fn from(secret_key: SecretKey) -> Self {
212        Self(secret_key)
213    }
214}
215
216// SAFETY: The inner `SecretKey` already implements `ZeroizeOnDrop` which ensures that the secret
217// key material is securely zeroized when dropped.
218impl ZeroizeOnDrop for KeyExchangeKey {}
219
220impl Serializable for KeyExchangeKey {
221    fn write_into<W: ByteWriter>(&self, target: &mut W) {
222        self.0.write_into(target);
223    }
224}
225
226impl Deserializable for KeyExchangeKey {
227    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
228        Ok(Self(SecretKey::read_from(source)?))
229    }
230}
231
232// PUBLIC KEY
233// ================================================================================================
234
235#[derive(Debug, Clone, PartialEq, Eq)]
236#[cfg_attr(
237    all(feature = "arbitrary", test),
238    miden_test_serialization_macros::serialization_test
239)]
240pub struct PublicKey {
241    pub(crate) inner: ed25519_dalek::VerifyingKey,
242}
243
244impl PublicKey {
245    /// Returns a commitment to the public key using Eidos.
246    ///
247    /// The commitment is computed by first converting the public key to field elements (4 bytes
248    /// per element), and then computing a sequential hash of the elements.
249    pub fn to_commitment(&self) -> Word {
250        <Self as SequentialCommit>::to_commitment(self)
251    }
252
253    /// Verifies a signature against this public key and message.
254    pub fn verify(&self, message: Word, signature: &Signature) -> bool {
255        let message_bytes: [u8; 32] = message.into();
256        self.inner.verify(&message_bytes, &signature.inner).is_ok()
257    }
258
259    /// Computes the Ed25519 challenge hash from a message and signature.
260    ///
261    /// This method computes the 64-byte hash `SHA-512(R || A || message)` where:
262    /// - `R` is the signature's R component (first 32 bytes)
263    /// - `A` is the public key
264    /// - `message` is the message bytes
265    ///
266    /// The resulting 64-byte hash can be passed to `verify_with_unchecked_k()` which will
267    /// reduce it modulo the curve order L to produce the challenge scalar.
268    ///
269    /// # Use Case
270    ///
271    /// This method is useful when you want to separate the hashing phase from the
272    /// elliptic curve verification phase. You can:
273    /// 1. Compute the hash using this method (hashing phase)
274    /// 2. Verify using `verify_with_unchecked_k(hash, signature)` (EC phase)
275    ///
276    /// This is equivalent to calling `verify()` directly, but allows the two phases
277    /// to be executed separately or in different environments.
278    ///
279    /// # Arguments
280    /// * `message` - The message that was signed
281    /// * `signature` - The signature to compute the challenge hash from
282    ///
283    /// # Returns
284    /// A 64-byte hash that will be reduced modulo L in `verify_with_unchecked_k()`
285    ///
286    /// # Example
287    /// ```ignore
288    /// let k_hash = public_key.compute_challenge_k(message, &signature);
289    /// let is_valid = public_key.verify_with_unchecked_k(k_hash, &signature).is_ok();
290    /// // is_valid should equal public_key.verify(message, &signature)
291    /// ```
292    ///
293    /// # Not Ed25519ph / RFC 8032 Prehash
294    ///
295    /// This helper reproduces the *standard* Ed25519 challenge `H(R || A || M)` used when verifying
296    /// signatures. It does **not** implement the RFC 8032 Ed25519ph variant, which prepends a
297    /// domain separation string and optional context before hashing. Callers that require the
298    /// Ed25519ph flavour must implement the additional domain separation logic themselves.
299    pub fn compute_challenge_k(&self, message: Word, signature: &Signature) -> [u8; 64] {
300        use sha2::Digest;
301
302        let message_bytes: [u8; 32] = message.into();
303        let sig_bytes = signature.inner.to_bytes();
304        let r_bytes = &sig_bytes[0..32];
305
306        // Compute SHA-512(R || A || message)
307        let mut hasher = sha2::Sha512::new();
308        hasher.update(r_bytes);
309        hasher.update(self.inner.to_bytes());
310        hasher.update(message_bytes);
311        let k_hash = hasher.finalize();
312
313        k_hash.into()
314    }
315
316    /// Verifies a signature using a pre-computed challenge hash.
317    ///
318    /// # ⚠️ CRITICAL SECURITY WARNING ⚠️
319    ///
320    /// **THIS METHOD IS EXTREMELY DANGEROUS AND EASY TO MISUSE.**
321    ///
322    /// This method bypasses the standard Ed25519 verification process by accepting a pre-computed
323    /// challenge hash instead of computing it from the message. This breaks Ed25519's
324    /// security properties in the following ways:
325    ///
326    /// ## Security Risks:
327    ///
328    /// 1. **Signature Forgery**: An attacker who can control the hash value can forge signatures
329    ///    for arbitrary messages without knowing the private key.
330    ///
331    /// 2. **Breaks Message Binding**: Standard Ed25519 cryptographically binds the signature to the
332    ///    message via the hash `H(R || A || message)`. Accepting arbitrary hashes breaks this
333    ///    binding.
334    ///
335    /// 3. **Bypasses Standard Protocol**: If the hash is not computed correctly as `SHA-512(R || A
336    ///    || message)`, this method bypasses standard Ed25519 verification and the signature will
337    ///    not be compatible with Ed25519 semantics.
338    ///
339    /// ## When This Might Be Used:
340    ///
341    /// This method is only appropriate in very specific scenarios where:
342    /// - You have a trusted computation environment that computes the hash correctly as `SHA-512(R
343    ///   || A || message)` (see `compute_challenge_k()`)
344    /// - You need to separate the hashing phase from the EC verification phase (e.g., for different
345    ///   execution environments or performance optimization)
346    /// - You fully understand the security implications and have a threat model that accounts for
347    ///   them
348    ///
349    /// When the hash is computed correctly, this method implements standard Ed25519 verification.
350    ///
351    /// ## Standard Usage:
352    ///
353    /// For normal Ed25519 verification, use `verify()` instead.
354    ///
355    /// ## Performance
356    ///
357    /// This helper decompresses the signature's `R` component before performing group arithmetic
358    /// and reuses the cached Edwards form of the public key. Expect it to be slower than
359    /// calling `verify()` directly.
360    ///
361    /// # Arguments
362    /// * `k_hash` - A 64-byte hash (typically computed as `SHA-512(R || A || message)`)
363    /// * `signature` - The signature to verify
364    ///
365    /// # Returns
366    /// `Ok(())` if the verification equation `[s]B = R + [k]A` holds, or an error describing why
367    /// the verification failed.
368    ///
369    /// # Warning
370    /// Do NOT use this method unless you fully understand Ed25519's cryptographic properties,
371    /// have a specific need for this low-level operation, and are feeding it the exact
372    /// `SHA-512(R || A || message)` output (without the Ed25519ph domain separation string).
373    pub fn verify_with_unchecked_k(
374        &self,
375        k_hash: [u8; 64],
376        signature: &Signature,
377    ) -> Result<(), UncheckedVerificationError> {
378        use curve25519_dalek::{
379            edwards::{CompressedEdwardsY, EdwardsPoint},
380            scalar::Scalar,
381        };
382
383        // Reduce the 64-byte hash modulo L to get the challenge scalar
384        let k_scalar = Scalar::from_bytes_mod_order_wide(&k_hash);
385
386        // Extract signature components: R (first 32 bytes) and s (second 32 bytes)
387        let sig_bytes = signature.inner.to_bytes();
388        let r_bytes: [u8; 32] =
389            sig_bytes[..32].try_into().expect("signature R component is exactly 32 bytes");
390        let s_bytes: [u8; 32] =
391            sig_bytes[32..].try_into().expect("signature s component is exactly 32 bytes");
392
393        // RFC 8032 requires s to be canonical; reject non-canonical scalars to avoid malleability.
394        let s_candidate = Scalar::from_canonical_bytes(s_bytes);
395        if s_candidate.is_none().into() {
396            return Err(UncheckedVerificationError::NonCanonicalScalar);
397        }
398        let s_scalar = s_candidate.unwrap();
399
400        let r_compressed = CompressedEdwardsY(r_bytes);
401        let Some(r_point) = r_compressed.decompress() else {
402            return Err(UncheckedVerificationError::InvalidSignaturePoint);
403        };
404
405        let a_point = self.inner.to_edwards();
406
407        // Match the stricter ed25519-dalek semantics by rejecting small-order inputs instead of
408        // multiplying the whole equation by the cofactor. dalek leaves this check opt-in via
409        // `verify_strict()`; we enforce it here to guard this hazmat API against torsion exploits.
410        if r_point.is_small_order() {
411            return Err(UncheckedVerificationError::SmallOrderSignature);
412        }
413        if a_point.is_small_order() {
414            return Err(UncheckedVerificationError::SmallOrderPublicKey);
415        }
416
417        // Compute the verification equation: -[k]A + [s]B == R, mirroring dalek's raw_verify.
418        // Small-order points are rejected above and hence no need for multiplication by co-factor
419        let minus_a = -a_point;
420        let expected_r =
421            EdwardsPoint::vartime_double_scalar_mul_basepoint(&k_scalar, &minus_a, &s_scalar)
422                .compress();
423
424        if expected_r == r_compressed {
425            Ok(())
426        } else {
427            Err(UncheckedVerificationError::EquationMismatch)
428        }
429    }
430
431    /// Convert to a X25519 public key which can be used in a DH key exchange protocol.
432    ///
433    /// # ⚠️ Security Warning
434    ///
435    /// **Do not reuse the same secret key for both Ed25519 signatures and X25519 key exchange.**
436    /// This conversion is primarily intended for sealed box primitives where an Ed25519 public key
437    /// is used to generate the shared key for encryption given an ephemeral X25519 key pair.
438    ///
439    /// In all other uses, prefer generating dedicated X25519 keys directly.
440    pub(crate) fn to_x25519(&self) -> x25519_dalek::PublicKey {
441        let mont_point = self.inner.to_montgomery();
442        x25519_dalek::PublicKey::from(mont_point.to_bytes())
443    }
444}
445
446impl SequentialCommit for PublicKey {
447    type Commitment = Word;
448
449    fn to_elements(&self) -> Vec<Felt> {
450        bytes_to_packed_u32_elements(&self.to_bytes())
451    }
452}
453
454#[derive(Debug, Error)]
455pub enum PublicKeyError {
456    #[error("Could not verify with given public key and signature")]
457    VerificationFailed,
458}
459
460/// Errors that can arise when invoking [`PublicKey::verify_with_unchecked_k`].
461#[derive(Debug, Error)]
462pub enum UncheckedVerificationError {
463    #[error("challenge scalar is not canonical")]
464    NonCanonicalScalar,
465    #[error("signature R component failed to decompress")]
466    InvalidSignaturePoint,
467    #[error("small-order component detected in signature R")]
468    SmallOrderSignature,
469    #[error("small-order component detected in public key")]
470    SmallOrderPublicKey,
471    #[error("verification equation was not satisfied")]
472    EquationMismatch,
473}
474
475// SIGNATURE
476// ================================================================================================
477
478/// EdDSA (Ed25519) signature
479#[derive(Debug, Clone, PartialEq, Eq)]
480#[cfg_attr(
481    all(feature = "arbitrary", test),
482    miden_test_serialization_macros::serialization_test
483)]
484pub struct Signature {
485    inner: ed25519_dalek::Signature,
486}
487
488impl Signature {
489    /// Verify against (message, public key).
490    pub fn verify(&self, message: Word, pub_key: &PublicKey) -> bool {
491        pub_key.verify(message, self)
492    }
493
494    /// Creates a signature from a DER-encoded BIT STRING (RFC 8410 §6) or raw bytes.
495    ///
496    /// Accepts either:
497    /// - A raw 64-byte Ed25519 signature.
498    /// - A DER BIT STRING wrapping a 64-byte Ed25519 signature.
499    pub fn from_der(bytes: &[u8]) -> Result<Self, DeserializationError> {
500        if bytes.len() == SIGNATURE_BYTES {
501            let inner = ed25519_dalek::Signature::from_bytes(
502                bytes.try_into().expect("length verified above"),
503            );
504            return Ok(Self { inner });
505        }
506
507        let bit_string = BitStringRef::from_der(bytes)
508            .map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
509
510        let raw = bit_string.as_bytes().ok_or_else(|| {
511            DeserializationError::InvalidValue("BIT STRING has non-zero unused bits".into())
512        })?;
513
514        let sig_bytes: &[u8; SIGNATURE_BYTES] = raw.try_into().map_err(|e| {
515            DeserializationError::InvalidValue(alloc::format!(
516                "expected {SIGNATURE_BYTES} signature bytes, got {}: {e}",
517                raw.len()
518            ))
519        })?;
520
521        Ok(Self {
522            inner: ed25519_dalek::Signature::from_bytes(sig_bytes),
523        })
524    }
525}
526
527// SERIALIZATION / DESERIALIZATION
528// ================================================================================================
529
530impl Serializable for SecretKey {
531    fn write_into<W: ByteWriter>(&self, target: &mut W) {
532        target.write_bytes(&self.inner.to_bytes());
533    }
534}
535
536impl Deserializable for SecretKey {
537    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
538        let bytes = read_sensitive_array::<SECRET_KEY_BYTES, _>(source)?;
539        let inner = ed25519_dalek::SigningKey::from_bytes(&bytes);
540
541        Ok(Self { inner })
542    }
543}
544
545impl Serializable for PublicKey {
546    fn write_into<W: ByteWriter>(&self, target: &mut W) {
547        target.write_bytes(&self.inner.to_bytes());
548    }
549}
550
551impl Deserializable for PublicKey {
552    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
553        let bytes: [u8; PUBLIC_KEY_BYTES] = source.read_array()?;
554        let inner = ed25519_dalek::VerifyingKey::from_bytes(&bytes).map_err(|_| {
555            DeserializationError::InvalidValue("Invalid Ed25519 public key".to_string())
556        })?;
557        Ok(Self { inner })
558    }
559}
560
561impl fmt::Display for PublicKey {
562    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
563        crate::utils::write_hex(f, &self.to_bytes())
564    }
565}
566
567impl Serializable for Signature {
568    fn write_into<W: ByteWriter>(&self, target: &mut W) {
569        target.write_bytes(&self.inner.to_bytes())
570    }
571}
572
573impl Deserializable for Signature {
574    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
575        let bytes: [u8; SIGNATURE_BYTES] = source.read_array()?;
576        let inner = ed25519_dalek::Signature::from_bytes(&bytes);
577        Ok(Self { inner })
578    }
579}
580
581impl fmt::Display for Signature {
582    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583        crate::utils::write_hex(f, &self.to_bytes())
584    }
585}
586
587// ARBITRARY (proptest)
588// ================================================================================================
589
590#[cfg(any(test, feature = "arbitrary"))]
591mod key_arbitrary {
592    use proptest::prelude::*;
593
594    use super::{
595        KeyExchangeKey, PublicKey, SECRET_KEY_BYTES, SIGNATURE_BYTES, SecretKey, Signature,
596        SigningKey,
597    };
598
599    impl Arbitrary for SecretKey {
600        type Parameters = ();
601        type Strategy = BoxedStrategy<Self>;
602
603        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
604            // An Ed25519 secret key is an unconstrained 32-byte seed.
605            any::<[u8; SECRET_KEY_BYTES]>()
606                .prop_map(|bytes| Self {
607                    inner: ed25519_dalek::SigningKey::from_bytes(&bytes),
608                })
609                .boxed()
610        }
611    }
612
613    impl Arbitrary for SigningKey {
614        type Parameters = ();
615        type Strategy = BoxedStrategy<Self>;
616
617        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
618            any::<SecretKey>().prop_map(Self).boxed()
619        }
620    }
621
622    impl Arbitrary for KeyExchangeKey {
623        type Parameters = ();
624        type Strategy = BoxedStrategy<Self>;
625
626        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
627            any::<SecretKey>().prop_map(Self).boxed()
628        }
629    }
630
631    impl Arbitrary for PublicKey {
632        type Parameters = ();
633        type Strategy = BoxedStrategy<Self>;
634
635        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
636            // The reader decompresses the curve point, so derive the public key from a generated
637            // secret key - valid by construction.
638            any::<SigningKey>()
639                .prop_map(|signing_key| Self {
640                    inner: signing_key.0.inner.verifying_key(),
641                })
642                .boxed()
643        }
644    }
645
646    impl Arbitrary for Signature {
647        type Parameters = ();
648        type Strategy = BoxedStrategy<Self>;
649
650        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
651            // from_bytes is infallible: any 64 bytes are a wire-valid signature.
652            any::<[u8; SIGNATURE_BYTES]>()
653                .prop_map(|bytes| Self {
654                    inner: ed25519_dalek::Signature::from_bytes(&bytes),
655                })
656                .boxed()
657        }
658    }
659}