Skip to main content

aptos_sdk/crypto/
ed25519.rs

1//! Ed25519 signature scheme implementation.
2//!
3//! Ed25519 is the default and most commonly used signature scheme on Aptos.
4
5use crate::crypto::traits::{PublicKey, Signature, Signer, Verifier};
6use crate::error::{AptosError, AptosResult};
7use ed25519_dalek::{Signer as DalekSigner, Verifier as DalekVerifier};
8use serde::{Deserialize, Serialize};
9use std::fmt;
10use zeroize::Zeroize;
11
12/// Ed25519 private key length in bytes.
13pub const ED25519_PRIVATE_KEY_LENGTH: usize = 32;
14/// Ed25519 public key length in bytes.
15pub const ED25519_PUBLIC_KEY_LENGTH: usize = 32;
16/// Ed25519 signature length in bytes.
17pub const ED25519_SIGNATURE_LENGTH: usize = 64;
18
19/// An Ed25519 private key.
20///
21/// The private key is zeroized when dropped to prevent sensitive
22/// data from remaining in memory.
23///
24/// # Example
25///
26/// ```rust
27/// use aptos_sdk::crypto::{Ed25519PrivateKey, Signer};
28///
29/// // Generate a random key
30/// let private_key = Ed25519PrivateKey::generate();
31///
32/// // Sign a message
33/// let signature = private_key.sign(b"hello");
34///
35/// // Get the public key
36/// let public_key = private_key.public_key();
37/// ```
38#[derive(Clone, Zeroize)]
39#[zeroize(drop)]
40pub struct Ed25519PrivateKey {
41    #[zeroize(skip)]
42    #[allow(unused)] // Field is used; lint false positive from Zeroize derive
43    inner: ed25519_dalek::SigningKey,
44}
45
46impl Ed25519PrivateKey {
47    /// Generates a new random Ed25519 private key.
48    pub fn generate() -> Self {
49        let mut csprng = rand::rngs::OsRng;
50        let signing_key = ed25519_dalek::SigningKey::generate(&mut csprng);
51        Self { inner: signing_key }
52    }
53
54    /// Creates a private key from raw bytes.
55    ///
56    /// # Errors
57    ///
58    /// Returns [`AptosError::InvalidPrivateKey`] if:
59    /// - The byte slice length is not exactly 32 bytes
60    pub fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
61        if bytes.len() != ED25519_PRIVATE_KEY_LENGTH {
62            return Err(AptosError::InvalidPrivateKey(format!(
63                "expected {} bytes, got {}",
64                ED25519_PRIVATE_KEY_LENGTH,
65                bytes.len()
66            )));
67        }
68        let mut key_bytes = [0u8; ED25519_PRIVATE_KEY_LENGTH];
69        key_bytes.copy_from_slice(bytes);
70        let signing_key = ed25519_dalek::SigningKey::from_bytes(&key_bytes);
71        // SECURITY: Zeroize the temporary buffer that held private key material
72        zeroize::Zeroize::zeroize(&mut key_bytes);
73        Ok(Self { inner: signing_key })
74    }
75
76    /// Creates a private key from a hex string.
77    ///
78    /// # Errors
79    ///
80    /// Returns [`AptosError::Hex`] if the hex string is invalid.
81    /// Returns [`AptosError::InvalidPrivateKey`] if the decoded bytes are not exactly 32 bytes.
82    pub fn from_hex(hex_str: &str) -> AptosResult<Self> {
83        let bytes = const_hex::decode(hex_str)?;
84        Self::from_bytes(&bytes)
85    }
86
87    /// Creates a private key from AIP-80 format string.
88    ///
89    /// AIP-80 format: `ed25519-priv-0x{hex_bytes}`
90    ///
91    /// # Errors
92    ///
93    /// Returns an error if the format is invalid or the key bytes are invalid.
94    ///
95    /// # Example
96    ///
97    /// ```rust
98    /// use aptos_sdk::crypto::Ed25519PrivateKey;
99    ///
100    /// let key = Ed25519PrivateKey::from_aip80(
101    ///     "ed25519-priv-0x0000000000000000000000000000000000000000000000000000000000000001"
102    /// ).unwrap();
103    /// ```
104    pub fn from_aip80(s: &str) -> AptosResult<Self> {
105        const PREFIX: &str = "ed25519-priv-";
106        if let Some(hex_part) = s.strip_prefix(PREFIX) {
107            Self::from_hex(hex_part)
108        } else {
109            Err(AptosError::InvalidPrivateKey(format!(
110                "invalid AIP-80 format: expected prefix '{PREFIX}'"
111            )))
112        }
113    }
114
115    /// Returns the private key as bytes.
116    ///
117    /// **Warning**: Handle the returned bytes carefully to avoid leaking
118    /// sensitive key material.
119    pub fn to_bytes(&self) -> [u8; ED25519_PRIVATE_KEY_LENGTH] {
120        self.inner.to_bytes()
121    }
122
123    /// Returns the private key as a hex string.
124    pub fn to_hex(&self) -> String {
125        const_hex::encode_prefixed(self.inner.to_bytes())
126    }
127
128    /// Returns the private key in AIP-80 format.
129    ///
130    /// AIP-80 format: `ed25519-priv-0x{hex_bytes}`
131    ///
132    /// # Example
133    ///
134    /// ```rust
135    /// use aptos_sdk::crypto::Ed25519PrivateKey;
136    ///
137    /// let key = Ed25519PrivateKey::generate();
138    /// let aip80 = key.to_aip80();
139    /// assert!(aip80.starts_with("ed25519-priv-0x"));
140    /// ```
141    pub fn to_aip80(&self) -> String {
142        format!("ed25519-priv-{}", self.to_hex())
143    }
144
145    /// Returns the corresponding public key.
146    pub fn public_key(&self) -> Ed25519PublicKey {
147        Ed25519PublicKey {
148            inner: self.inner.verifying_key(),
149        }
150    }
151
152    /// Signs a message and returns the signature.
153    pub fn sign(&self, message: &[u8]) -> Ed25519Signature {
154        let signature = self.inner.sign(message);
155        Ed25519Signature { inner: signature }
156    }
157}
158
159impl Signer for Ed25519PrivateKey {
160    type Signature = Ed25519Signature;
161
162    fn sign(&self, message: &[u8]) -> Ed25519Signature {
163        Ed25519PrivateKey::sign(self, message)
164    }
165
166    fn public_key(&self) -> Ed25519PublicKey {
167        Ed25519PrivateKey::public_key(self)
168    }
169}
170
171impl fmt::Debug for Ed25519PrivateKey {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        write!(f, "Ed25519PrivateKey([REDACTED])")
174    }
175}
176
177/// An Ed25519 public key.
178///
179/// # Example
180///
181/// ```rust
182/// use aptos_sdk::crypto::{Ed25519PrivateKey, Ed25519PublicKey, Signer, Verifier};
183///
184/// let private_key = Ed25519PrivateKey::generate();
185/// let public_key = private_key.public_key();
186///
187/// let message = b"hello";
188/// let signature = private_key.sign(message);
189///
190/// assert!(public_key.verify(message, &signature).is_ok());
191/// ```
192#[derive(Clone, Copy, PartialEq, Eq)]
193pub struct Ed25519PublicKey {
194    inner: ed25519_dalek::VerifyingKey,
195}
196
197impl Ed25519PublicKey {
198    /// Creates a public key from raw bytes.
199    ///
200    /// # Errors
201    ///
202    /// Returns [`AptosError::InvalidPublicKey`] if:
203    /// - The byte slice length is not exactly 32 bytes
204    /// - The bytes do not represent a valid Ed25519 public key
205    pub fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
206        if bytes.len() != ED25519_PUBLIC_KEY_LENGTH {
207            return Err(AptosError::InvalidPublicKey(format!(
208                "expected {} bytes, got {}",
209                ED25519_PUBLIC_KEY_LENGTH,
210                bytes.len()
211            )));
212        }
213        let mut key_bytes = [0u8; ED25519_PUBLIC_KEY_LENGTH];
214        key_bytes.copy_from_slice(bytes);
215        let verifying_key = ed25519_dalek::VerifyingKey::from_bytes(&key_bytes)
216            .map_err(|e| AptosError::InvalidPublicKey(e.to_string()))?;
217        Ok(Self {
218            inner: verifying_key,
219        })
220    }
221
222    /// Creates a public key from a hex string.
223    ///
224    /// # Errors
225    ///
226    /// Returns [`AptosError::Hex`] if the hex string is invalid.
227    /// Returns [`AptosError::InvalidPublicKey`] if the decoded bytes are not exactly 32 bytes or do not represent a valid Ed25519 public key.
228    pub fn from_hex(hex_str: &str) -> AptosResult<Self> {
229        let bytes = const_hex::decode(hex_str)?;
230        Self::from_bytes(&bytes)
231    }
232
233    /// Creates a public key from AIP-80 format string.
234    ///
235    /// AIP-80 format: `ed25519-pub-0x{hex_bytes}`
236    ///
237    /// # Errors
238    ///
239    /// Returns an error if the format is invalid or the key bytes are invalid.
240    pub fn from_aip80(s: &str) -> AptosResult<Self> {
241        const PREFIX: &str = "ed25519-pub-";
242        if let Some(hex_part) = s.strip_prefix(PREFIX) {
243            Self::from_hex(hex_part)
244        } else {
245            Err(AptosError::InvalidPublicKey(format!(
246                "invalid AIP-80 format: expected prefix '{PREFIX}'"
247            )))
248        }
249    }
250
251    /// Returns the public key as bytes.
252    pub fn to_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_LENGTH] {
253        self.inner.to_bytes()
254    }
255
256    /// Returns the public key as a hex string.
257    pub fn to_hex(&self) -> String {
258        const_hex::encode_prefixed(self.inner.to_bytes())
259    }
260
261    /// Returns the public key in AIP-80 format.
262    ///
263    /// AIP-80 format: `ed25519-pub-0x{hex_bytes}`
264    pub fn to_aip80(&self) -> String {
265        format!("ed25519-pub-{}", self.to_hex())
266    }
267
268    /// Verifies a signature against a message.
269    ///
270    /// # Errors
271    ///
272    /// Returns [`AptosError::SignatureVerificationFailed`] if the signature is invalid or does not match the message.
273    pub fn verify(&self, message: &[u8], signature: &Ed25519Signature) -> AptosResult<()> {
274        self.inner
275            .verify(message, &signature.inner)
276            .map_err(|_| AptosError::SignatureVerificationFailed)
277    }
278
279    /// Derives the account address for this public key.
280    ///
281    /// Uses the Ed25519 single-key scheme (scheme byte 0).
282    pub fn to_address(&self) -> crate::types::AccountAddress {
283        crate::crypto::derive_address(&self.to_bytes(), crate::crypto::ED25519_SCHEME)
284    }
285
286    /// Derives the authentication key for this public key.
287    pub fn to_authentication_key(&self) -> [u8; 32] {
288        crate::crypto::derive_authentication_key(&self.to_bytes(), crate::crypto::ED25519_SCHEME)
289    }
290}
291
292impl PublicKey for Ed25519PublicKey {
293    const LENGTH: usize = ED25519_PUBLIC_KEY_LENGTH;
294
295    fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
296        Ed25519PublicKey::from_bytes(bytes)
297    }
298
299    fn to_bytes(&self) -> Vec<u8> {
300        self.inner.to_bytes().to_vec()
301    }
302}
303
304impl Verifier for Ed25519PublicKey {
305    type Signature = Ed25519Signature;
306
307    fn verify(&self, message: &[u8], signature: &Ed25519Signature) -> AptosResult<()> {
308        Ed25519PublicKey::verify(self, message, signature)
309    }
310}
311
312impl fmt::Debug for Ed25519PublicKey {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        write!(f, "Ed25519PublicKey({})", self.to_hex())
315    }
316}
317
318impl fmt::Display for Ed25519PublicKey {
319    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320        write!(f, "{}", self.to_hex())
321    }
322}
323
324impl Serialize for Ed25519PublicKey {
325    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
326    where
327        S: serde::Serializer,
328    {
329        if serializer.is_human_readable() {
330            serializer.serialize_str(&self.to_hex())
331        } else {
332            serializer.serialize_bytes(&self.inner.to_bytes())
333        }
334    }
335}
336
337impl<'de> Deserialize<'de> for Ed25519PublicKey {
338    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
339    where
340        D: serde::Deserializer<'de>,
341    {
342        if deserializer.is_human_readable() {
343            let s = String::deserialize(deserializer)?;
344            Self::from_hex(&s).map_err(serde::de::Error::custom)
345        } else {
346            let bytes = <[u8; ED25519_PUBLIC_KEY_LENGTH]>::deserialize(deserializer)?;
347            Self::from_bytes(&bytes).map_err(serde::de::Error::custom)
348        }
349    }
350}
351
352/// An Ed25519 signature.
353#[derive(Clone, Copy, PartialEq, Eq)]
354pub struct Ed25519Signature {
355    inner: ed25519_dalek::Signature,
356}
357
358impl Ed25519Signature {
359    /// Creates a signature from raw bytes.
360    ///
361    /// Only the length is validated here. `ed25519-dalek` 2.x parses the 64
362    /// bytes into an `(R, s)` pair without checking that they form a
363    /// cryptographically valid signature; whether the signature actually
364    /// verifies against a given key and message is determined later at
365    /// verification time (see [`Ed25519PublicKey::verify`]).
366    ///
367    /// # Errors
368    ///
369    /// Returns [`AptosError::InvalidSignature`] if the byte slice length is not
370    /// exactly 64 bytes.
371    pub fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
372        if bytes.len() != ED25519_SIGNATURE_LENGTH {
373            return Err(AptosError::InvalidSignature(format!(
374                "expected {} bytes, got {}",
375                ED25519_SIGNATURE_LENGTH,
376                bytes.len()
377            )));
378        }
379        let signature = ed25519_dalek::Signature::from_slice(bytes)
380            .map_err(|e| AptosError::InvalidSignature(e.to_string()))?;
381        Ok(Self { inner: signature })
382    }
383
384    /// Creates a signature from a hex string.
385    ///
386    /// # Errors
387    ///
388    /// Returns [`AptosError::Hex`] if the hex string is invalid.
389    /// Returns [`AptosError::InvalidSignature`] if the decoded bytes are not exactly 64 bytes.
390    /// (As with [`Self::from_bytes`], only the length is validated; cryptographic validity is
391    /// checked at verification time.)
392    pub fn from_hex(hex_str: &str) -> AptosResult<Self> {
393        let bytes = const_hex::decode(hex_str)?;
394        Self::from_bytes(&bytes)
395    }
396
397    /// Returns the signature as bytes.
398    pub fn to_bytes(&self) -> [u8; ED25519_SIGNATURE_LENGTH] {
399        self.inner.to_bytes()
400    }
401
402    /// Returns the signature as a hex string.
403    pub fn to_hex(&self) -> String {
404        const_hex::encode_prefixed(self.inner.to_bytes())
405    }
406}
407
408impl Signature for Ed25519Signature {
409    type PublicKey = Ed25519PublicKey;
410    const LENGTH: usize = ED25519_SIGNATURE_LENGTH;
411
412    fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
413        Ed25519Signature::from_bytes(bytes)
414    }
415
416    fn to_bytes(&self) -> Vec<u8> {
417        self.inner.to_bytes().to_vec()
418    }
419}
420
421impl fmt::Debug for Ed25519Signature {
422    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423        write!(f, "Ed25519Signature({})", self.to_hex())
424    }
425}
426
427impl fmt::Display for Ed25519Signature {
428    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429        write!(f, "{}", self.to_hex())
430    }
431}
432
433impl Serialize for Ed25519Signature {
434    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
435    where
436        S: serde::Serializer,
437    {
438        if serializer.is_human_readable() {
439            serializer.serialize_str(&self.to_hex())
440        } else {
441            serializer.serialize_bytes(&self.inner.to_bytes())
442        }
443    }
444}
445
446impl<'de> Deserialize<'de> for Ed25519Signature {
447    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
448    where
449        D: serde::Deserializer<'de>,
450    {
451        if deserializer.is_human_readable() {
452            let s = String::deserialize(deserializer)?;
453            Self::from_hex(&s).map_err(serde::de::Error::custom)
454        } else {
455            let bytes = Vec::<u8>::deserialize(deserializer)?;
456            Self::from_bytes(&bytes).map_err(serde::de::Error::custom)
457        }
458    }
459}
460
461#[cfg(test)]
462mod tests {
463    use super::*;
464
465    #[test]
466    fn test_generate_and_sign() {
467        let private_key = Ed25519PrivateKey::generate();
468        let message = b"hello world";
469        let signature = private_key.sign(message);
470
471        let public_key = private_key.public_key();
472        assert!(public_key.verify(message, &signature).is_ok());
473    }
474
475    #[test]
476    fn test_wrong_message_fails() {
477        let private_key = Ed25519PrivateKey::generate();
478        let message = b"hello world";
479        let wrong_message = b"hello world!";
480        let signature = private_key.sign(message);
481
482        let public_key = private_key.public_key();
483        assert!(public_key.verify(wrong_message, &signature).is_err());
484    }
485
486    #[test]
487    fn test_from_bytes_roundtrip() {
488        let private_key = Ed25519PrivateKey::generate();
489        let bytes = private_key.to_bytes();
490        let restored = Ed25519PrivateKey::from_bytes(&bytes).unwrap();
491        assert_eq!(private_key.to_bytes(), restored.to_bytes());
492    }
493
494    #[test]
495    fn test_from_hex_roundtrip() {
496        let private_key = Ed25519PrivateKey::generate();
497        let hex = private_key.to_hex();
498        let restored = Ed25519PrivateKey::from_hex(&hex).unwrap();
499        assert_eq!(private_key.to_bytes(), restored.to_bytes());
500    }
501
502    #[test]
503    fn test_public_key_serialization() {
504        let private_key = Ed25519PrivateKey::generate();
505        let public_key = private_key.public_key();
506
507        let json = serde_json::to_string(&public_key).unwrap();
508        let restored: Ed25519PublicKey = serde_json::from_str(&json).unwrap();
509        assert_eq!(public_key, restored);
510    }
511
512    #[test]
513    fn test_address_derivation() {
514        let private_key = Ed25519PrivateKey::generate();
515        let public_key = private_key.public_key();
516        let address = public_key.to_address();
517
518        // Address should not be zero
519        assert!(!address.is_zero());
520
521        // Same public key should always derive same address
522        let address2 = public_key.to_address();
523        assert_eq!(address, address2);
524    }
525
526    #[test]
527    fn test_private_key_aip80_roundtrip() {
528        let private_key = Ed25519PrivateKey::generate();
529        let aip80 = private_key.to_aip80();
530
531        // Should have correct prefix
532        assert!(aip80.starts_with("ed25519-priv-0x"));
533
534        // Should roundtrip correctly
535        let restored = Ed25519PrivateKey::from_aip80(&aip80).unwrap();
536        assert_eq!(private_key.to_bytes(), restored.to_bytes());
537    }
538
539    #[test]
540    fn test_private_key_aip80_format() {
541        let bytes = [0x01; 32];
542        let private_key = Ed25519PrivateKey::from_bytes(&bytes).unwrap();
543        let aip80 = private_key.to_aip80();
544
545        // Expected format: ed25519-priv-0x0101...01
546        let expected = format!("ed25519-priv-0x{}", "01".repeat(32));
547        assert_eq!(aip80, expected);
548    }
549
550    #[test]
551    fn test_private_key_aip80_invalid_prefix() {
552        let result = Ed25519PrivateKey::from_aip80("secp256k1-priv-0x01");
553        assert!(result.is_err());
554    }
555
556    #[test]
557    fn test_public_key_aip80_roundtrip() {
558        let private_key = Ed25519PrivateKey::generate();
559        let public_key = private_key.public_key();
560        let aip80 = public_key.to_aip80();
561
562        // Should have correct prefix
563        assert!(aip80.starts_with("ed25519-pub-0x"));
564
565        // Should roundtrip correctly
566        let restored = Ed25519PublicKey::from_aip80(&aip80).unwrap();
567        assert_eq!(public_key.to_bytes(), restored.to_bytes());
568    }
569
570    #[test]
571    fn test_public_key_aip80_invalid_prefix() {
572        let result = Ed25519PublicKey::from_aip80("secp256k1-pub-0x01");
573        assert!(result.is_err());
574    }
575
576    #[test]
577    fn test_invalid_private_key_bytes_length() {
578        let bytes = vec![0u8; 16]; // Wrong length
579        let result = Ed25519PrivateKey::from_bytes(&bytes);
580        assert!(result.is_err());
581    }
582
583    #[test]
584    fn test_invalid_public_key_bytes_length() {
585        let bytes = vec![0u8; 16]; // Wrong length
586        let result = Ed25519PublicKey::from_bytes(&bytes);
587        assert!(result.is_err());
588    }
589
590    #[test]
591    fn test_invalid_signature_bytes_length() {
592        let bytes = vec![0u8; 32]; // Wrong length
593        let result = Ed25519Signature::from_bytes(&bytes);
594        assert!(result.is_err());
595    }
596
597    #[test]
598    fn test_public_key_from_hex_roundtrip() {
599        let private_key = Ed25519PrivateKey::generate();
600        let public_key = private_key.public_key();
601        let hex = public_key.to_hex();
602        let restored = Ed25519PublicKey::from_hex(&hex).unwrap();
603        assert_eq!(public_key, restored);
604    }
605
606    #[test]
607    fn test_signature_from_hex_roundtrip() {
608        let private_key = Ed25519PrivateKey::generate();
609        let signature = private_key.sign(b"test");
610        let hex = signature.to_hex();
611        let restored = Ed25519Signature::from_hex(&hex).unwrap();
612        assert_eq!(signature.to_bytes(), restored.to_bytes());
613    }
614
615    #[test]
616    fn test_public_key_bytes_roundtrip() {
617        let private_key = Ed25519PrivateKey::generate();
618        let public_key = private_key.public_key();
619        let bytes = public_key.to_bytes();
620        let restored = Ed25519PublicKey::from_bytes(&bytes).unwrap();
621        assert_eq!(public_key, restored);
622    }
623
624    #[test]
625    fn test_signature_bytes_roundtrip() {
626        let private_key = Ed25519PrivateKey::generate();
627        let signature = private_key.sign(b"test");
628        let bytes = signature.to_bytes();
629        let restored = Ed25519Signature::from_bytes(&bytes).unwrap();
630        assert_eq!(signature.to_bytes(), restored.to_bytes());
631    }
632
633    #[test]
634    fn test_private_key_debug() {
635        let private_key = Ed25519PrivateKey::generate();
636        let debug = format!("{private_key:?}");
637        assert!(debug.contains("REDACTED"));
638        assert!(!debug.contains(&private_key.to_hex()));
639    }
640
641    #[test]
642    fn test_public_key_debug() {
643        let private_key = Ed25519PrivateKey::generate();
644        let public_key = private_key.public_key();
645        let debug = format!("{public_key:?}");
646        assert!(debug.contains("Ed25519PublicKey"));
647    }
648
649    #[test]
650    fn test_public_key_display() {
651        let private_key = Ed25519PrivateKey::generate();
652        let public_key = private_key.public_key();
653        let display = format!("{public_key}");
654        assert!(display.starts_with("0x"));
655    }
656
657    #[test]
658    fn test_signature_debug() {
659        let private_key = Ed25519PrivateKey::generate();
660        let signature = private_key.sign(b"test");
661        let debug = format!("{signature:?}");
662        assert!(debug.contains("Ed25519Signature"));
663    }
664
665    #[test]
666    fn test_signature_display() {
667        let private_key = Ed25519PrivateKey::generate();
668        let signature = private_key.sign(b"test");
669        let display = format!("{signature}");
670        assert!(display.starts_with("0x"));
671    }
672
673    #[test]
674    fn test_signer_trait() {
675        use crate::crypto::traits::Signer;
676
677        let private_key = Ed25519PrivateKey::generate();
678        let message = b"trait test";
679
680        let signature = Signer::sign(&private_key, message);
681        let public_key = Signer::public_key(&private_key);
682
683        assert!(public_key.verify(message, &signature).is_ok());
684    }
685
686    #[test]
687    fn test_verifier_trait() {
688        use crate::crypto::traits::Verifier;
689
690        let private_key = Ed25519PrivateKey::generate();
691        let public_key = private_key.public_key();
692        let message = b"verifier test";
693        let signature = private_key.sign(message);
694
695        assert!(Verifier::verify(&public_key, message, &signature).is_ok());
696    }
697
698    #[test]
699    fn test_public_key_trait() {
700        use crate::crypto::traits::PublicKey;
701
702        let private_key = Ed25519PrivateKey::generate();
703        let public_key = private_key.public_key();
704        let bytes = PublicKey::to_bytes(&public_key);
705        let restored = Ed25519PublicKey::from_bytes(&bytes).unwrap();
706        assert_eq!(public_key, restored);
707    }
708
709    #[test]
710    fn test_signature_trait() {
711        use crate::crypto::traits::Signature;
712
713        let private_key = Ed25519PrivateKey::generate();
714        let signature = private_key.sign(b"test");
715        let bytes = Signature::to_bytes(&signature);
716        let restored = Ed25519Signature::from_bytes(&bytes).unwrap();
717        assert_eq!(signature.to_bytes(), restored.to_bytes());
718    }
719
720    #[test]
721    fn test_authentication_key() {
722        let private_key = Ed25519PrivateKey::generate();
723        let public_key = private_key.public_key();
724        let auth_key = public_key.to_authentication_key();
725        assert_eq!(auth_key.len(), 32);
726    }
727
728    #[test]
729    fn test_signature_json_serialization() {
730        let private_key = Ed25519PrivateKey::generate();
731        let signature = private_key.sign(b"test");
732
733        let json = serde_json::to_string(&signature).unwrap();
734        let restored: Ed25519Signature = serde_json::from_str(&json).unwrap();
735        assert_eq!(signature.to_bytes(), restored.to_bytes());
736    }
737
738    #[test]
739    fn test_private_key_clone() {
740        let private_key = Ed25519PrivateKey::generate();
741        let cloned = private_key.clone();
742        assert_eq!(private_key.to_bytes(), cloned.to_bytes());
743    }
744
745    #[test]
746    fn test_public_key_equality() {
747        let private_key = Ed25519PrivateKey::generate();
748        let pk1 = private_key.public_key();
749        let pk2 = private_key.public_key();
750        assert_eq!(pk1, pk2);
751
752        let different = Ed25519PrivateKey::generate().public_key();
753        assert_ne!(pk1, different);
754    }
755
756    /// The documented drop-clearing guarantee relies on the inner
757    /// `ed25519_dalek::SigningKey` zeroizing its secret on drop. This
758    /// compile-time assertion pins that contract: if the underlying type ever
759    /// stopped implementing `ZeroizeOnDrop`, the docs in `crypto/mod.rs` and on
760    /// `Ed25519PrivateKey` would become false and this test would fail to build.
761    #[test]
762    fn test_inner_key_zeroizes_on_drop() {
763        fn assert_zeroize_on_drop<T: zeroize::ZeroizeOnDrop>() {}
764        assert_zeroize_on_drop::<ed25519_dalek::SigningKey>();
765    }
766}