Skip to main content

aptos_sdk/crypto/
secp256k1.rs

1//! Secp256k1 ECDSA signature scheme implementation.
2//!
3//! Secp256k1 is the same elliptic curve used by Bitcoin and Ethereum.
4//!
5//! # Security: Signature Malleability
6//!
7//! This implementation enforces **low-S only** signatures to match the Aptos
8//! blockchain's on-chain verification, which rejects high-S signatures to
9//! prevent signature malleability attacks.
10//!
11//! - **Signing** always produces low-S signatures (the `k256` crate normalizes
12//!   by default).
13//! - **Parsing** (`from_bytes`, `from_hex`) rejects high-S signatures.
14//! - **Verification** also rejects high-S signatures as a defense-in-depth
15//!   measure.
16
17use crate::crypto::traits::{PublicKey, Signature, Signer, Verifier};
18use crate::error::{AptosError, AptosResult};
19use k256::ecdsa::{
20    Signature as K256Signature, SigningKey, VerifyingKey, signature::Signer as K256Signer,
21    signature::Verifier as K256Verifier,
22};
23use serde::{Deserialize, Serialize};
24use std::fmt;
25use zeroize::Zeroize;
26
27/// Secp256k1 private key length in bytes.
28pub const SECP256K1_PRIVATE_KEY_LENGTH: usize = 32;
29/// Secp256k1 public key length in bytes (compressed).
30pub const SECP256K1_PUBLIC_KEY_LENGTH: usize = 33;
31/// Secp256k1 uncompressed public key length in bytes.
32#[allow(dead_code)] // Public API constant
33pub const SECP256K1_PUBLIC_KEY_UNCOMPRESSED_LENGTH: usize = 65;
34/// Secp256k1 signature length in bytes (DER encoded max).
35pub const SECP256K1_SIGNATURE_LENGTH: usize = 64;
36
37/// A Secp256k1 ECDSA private key.
38///
39/// The private key is zeroized when dropped.
40#[derive(Clone, Zeroize)]
41#[zeroize(drop)]
42pub struct Secp256k1PrivateKey {
43    #[zeroize(skip)]
44    #[allow(unused)] // Field is used; lint false positive from Zeroize derive
45    inner: SigningKey,
46}
47
48impl Secp256k1PrivateKey {
49    /// Generates a new random Secp256k1 private key.
50    pub fn generate() -> Self {
51        let signing_key = SigningKey::random(&mut rand::rngs::OsRng);
52        Self { inner: signing_key }
53    }
54
55    /// Creates a private key from raw bytes.
56    ///
57    /// # Errors
58    ///
59    /// Returns [`AptosError::InvalidPrivateKey`] if:
60    /// - The byte slice length is not exactly 32 bytes
61    /// - The bytes do not represent a valid Secp256k1 private key
62    pub fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
63        if bytes.len() != SECP256K1_PRIVATE_KEY_LENGTH {
64            return Err(AptosError::InvalidPrivateKey(format!(
65                "expected {} bytes, got {}",
66                SECP256K1_PRIVATE_KEY_LENGTH,
67                bytes.len()
68            )));
69        }
70        let signing_key = SigningKey::from_slice(bytes)
71            .map_err(|e| AptosError::InvalidPrivateKey(e.to_string()))?;
72        Ok(Self { inner: signing_key })
73    }
74
75    /// Creates a private key from a hex string.
76    ///
77    /// # Errors
78    ///
79    /// Returns [`AptosError::Hex`] if the hex string is invalid.
80    /// Returns [`AptosError::InvalidPrivateKey`] if the decoded bytes are not exactly 32 bytes or do not represent a valid Secp256k1 private key.
81    pub fn from_hex(hex_str: &str) -> AptosResult<Self> {
82        let bytes = const_hex::decode(hex_str)?;
83        Self::from_bytes(&bytes)
84    }
85
86    /// Creates a private key from AIP-80 format string.
87    ///
88    /// AIP-80 format: `secp256k1-priv-0x{hex_bytes}`
89    ///
90    /// # Errors
91    ///
92    /// Returns an error if the format is invalid or the key bytes are invalid.
93    pub fn from_aip80(s: &str) -> AptosResult<Self> {
94        const PREFIX: &str = "secp256k1-priv-";
95        if let Some(hex_part) = s.strip_prefix(PREFIX) {
96            Self::from_hex(hex_part)
97        } else {
98            Err(AptosError::InvalidPrivateKey(format!(
99                "invalid AIP-80 format: expected prefix '{PREFIX}'"
100            )))
101        }
102    }
103
104    /// Returns the private key as bytes.
105    pub fn to_bytes(&self) -> [u8; SECP256K1_PRIVATE_KEY_LENGTH] {
106        self.inner.to_bytes().into()
107    }
108
109    /// Returns the private key as a hex string.
110    pub fn to_hex(&self) -> String {
111        const_hex::encode_prefixed(self.inner.to_bytes())
112    }
113
114    /// Returns the private key in AIP-80 format.
115    ///
116    /// AIP-80 format: `secp256k1-priv-0x{hex_bytes}`
117    pub fn to_aip80(&self) -> String {
118        format!("secp256k1-priv-{}", self.to_hex())
119    }
120
121    /// Returns the corresponding public key.
122    pub fn public_key(&self) -> Secp256k1PublicKey {
123        Secp256k1PublicKey {
124            inner: *self.inner.verifying_key(),
125        }
126    }
127
128    /// Signs a message (pre-hashed with SHA256) and returns a low-S signature.
129    ///
130    /// The `k256` crate produces low-S signatures by default. An additional
131    /// normalization step is included as defense-in-depth to guarantee Aptos
132    /// on-chain compatibility.
133    pub fn sign(&self, message: &[u8]) -> Secp256k1Signature {
134        // Hash the message with SHA256 first (standard for ECDSA)
135        let hash = crate::crypto::sha2_256(message);
136        let signature: K256Signature = self.inner.sign(&hash);
137        // SECURITY: Ensure low-S (defense-in-depth; k256 should already do this)
138        let normalized = signature.normalize_s().unwrap_or(signature);
139        Secp256k1Signature { inner: normalized }
140    }
141
142    /// Signs a pre-hashed message directly and returns a low-S signature.
143    ///
144    /// The `k256` crate produces low-S signatures by default. An additional
145    /// normalization step is included as defense-in-depth.
146    pub fn sign_prehashed(&self, hash: &[u8; 32]) -> Secp256k1Signature {
147        let signature: K256Signature = self.inner.sign(hash);
148        // SECURITY: Ensure low-S (defense-in-depth; k256 should already do this)
149        let normalized = signature.normalize_s().unwrap_or(signature);
150        Secp256k1Signature { inner: normalized }
151    }
152}
153
154impl Signer for Secp256k1PrivateKey {
155    type Signature = Secp256k1Signature;
156
157    fn sign(&self, message: &[u8]) -> Secp256k1Signature {
158        Secp256k1PrivateKey::sign(self, message)
159    }
160
161    fn public_key(&self) -> Secp256k1PublicKey {
162        Secp256k1PrivateKey::public_key(self)
163    }
164}
165
166impl fmt::Debug for Secp256k1PrivateKey {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        write!(f, "Secp256k1PrivateKey([REDACTED])")
169    }
170}
171
172/// A Secp256k1 ECDSA public key.
173#[derive(Clone, Copy, PartialEq, Eq)]
174pub struct Secp256k1PublicKey {
175    inner: VerifyingKey,
176}
177
178impl Secp256k1PublicKey {
179    /// Creates a public key from compressed bytes (33 bytes).
180    ///
181    /// # Errors
182    ///
183    /// Returns [`AptosError::InvalidPublicKey`] if the bytes do not represent a valid Secp256k1 compressed public key.
184    pub fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
185        let verifying_key = VerifyingKey::from_sec1_bytes(bytes)
186            .map_err(|e| AptosError::InvalidPublicKey(e.to_string()))?;
187        Ok(Self {
188            inner: verifying_key,
189        })
190    }
191
192    /// Creates a public key from a hex string.
193    ///
194    /// # Errors
195    ///
196    /// Returns [`AptosError::Hex`] if the hex string is invalid.
197    /// Returns [`AptosError::InvalidPublicKey`] if the decoded bytes do not represent a valid Secp256k1 compressed public key.
198    pub fn from_hex(hex_str: &str) -> AptosResult<Self> {
199        let bytes = const_hex::decode(hex_str)?;
200        Self::from_bytes(&bytes)
201    }
202
203    /// Creates a public key from AIP-80 format string.
204    ///
205    /// AIP-80 format: `secp256k1-pub-0x{hex_bytes}`
206    ///
207    /// # Errors
208    ///
209    /// Returns an error if the format is invalid or the key bytes are invalid.
210    pub fn from_aip80(s: &str) -> AptosResult<Self> {
211        const PREFIX: &str = "secp256k1-pub-";
212        if let Some(hex_part) = s.strip_prefix(PREFIX) {
213            Self::from_hex(hex_part)
214        } else {
215            Err(AptosError::InvalidPublicKey(format!(
216                "invalid AIP-80 format: expected prefix '{PREFIX}'"
217            )))
218        }
219    }
220
221    /// Returns the public key as compressed bytes (33 bytes).
222    pub fn to_bytes(&self) -> Vec<u8> {
223        self.inner.to_sec1_bytes().to_vec()
224    }
225
226    /// Returns the public key as uncompressed bytes (65 bytes).
227    pub fn to_uncompressed_bytes(&self) -> Vec<u8> {
228        #[allow(unused_imports)]
229        use k256::elliptic_curve::sec1::ToEncodedPoint;
230        self.inner.to_encoded_point(false).as_bytes().to_vec()
231    }
232
233    /// Returns the public key as a hex string (compressed format).
234    pub fn to_hex(&self) -> String {
235        const_hex::encode_prefixed(self.to_bytes())
236    }
237
238    /// Returns the public key in AIP-80 format (compressed).
239    ///
240    /// AIP-80 format: `secp256k1-pub-0x{hex_bytes}`
241    pub fn to_aip80(&self) -> String {
242        format!("secp256k1-pub-{}", self.to_hex())
243    }
244
245    /// Verifies a signature against a message.
246    ///
247    /// # Security
248    ///
249    /// Rejects high-S signatures before verification, matching Aptos on-chain
250    /// behavior. This is a defense-in-depth check; signatures created through
251    /// this SDK's `from_bytes` are already guaranteed to be low-S.
252    ///
253    /// # Errors
254    ///
255    /// Returns [`AptosError::SignatureVerificationFailed`] if the signature has
256    /// a high-S value, is invalid, or does not match the message.
257    pub fn verify(&self, message: &[u8], signature: &Secp256k1Signature) -> AptosResult<()> {
258        // SECURITY: Reject high-S signatures (matches aptos-core behavior)
259        if signature.inner.normalize_s().is_some() {
260            return Err(AptosError::SignatureVerificationFailed);
261        }
262        let hash = crate::crypto::sha2_256(message);
263        self.inner
264            .verify(&hash, &signature.inner)
265            .map_err(|_| AptosError::SignatureVerificationFailed)
266    }
267
268    /// Verifies a signature against a pre-hashed message.
269    ///
270    /// # Security
271    ///
272    /// Rejects high-S signatures before verification, matching Aptos on-chain
273    /// behavior.
274    ///
275    /// # Errors
276    ///
277    /// Returns [`AptosError::SignatureVerificationFailed`] if the signature has
278    /// a high-S value, is invalid, or does not match the hash.
279    pub fn verify_prehashed(
280        &self,
281        hash: &[u8; 32],
282        signature: &Secp256k1Signature,
283    ) -> AptosResult<()> {
284        // SECURITY: Reject high-S signatures (matches aptos-core behavior)
285        if signature.inner.normalize_s().is_some() {
286            return Err(AptosError::SignatureVerificationFailed);
287        }
288        self.inner
289            .verify(hash, &signature.inner)
290            .map_err(|_| AptosError::SignatureVerificationFailed)
291    }
292
293    /// Derives the account address for this public key.
294    ///
295    /// Uses the `SingleKey` authentication scheme (`scheme_id` = 2):
296    /// `auth_key = SHA3-256(BCS(AnyPublicKey::Secp256k1) || 0x02)`
297    ///
298    /// Where `BCS(AnyPublicKey::Secp256k1)` = `0x01 || ULEB128(65) || uncompressed_public_key`
299    pub fn to_address(&self) -> crate::types::AccountAddress {
300        // BCS format: variant_byte || ULEB128(length) || uncompressed_public_key
301        let uncompressed = self.to_uncompressed_bytes();
302        let mut bcs_bytes = Vec::with_capacity(1 + 1 + uncompressed.len());
303        bcs_bytes.push(0x01); // Secp256k1 variant
304        bcs_bytes.push(65); // ULEB128(65) = 0x41, but 65 < 128 so it's just 65
305        bcs_bytes.extend_from_slice(&uncompressed);
306        crate::crypto::derive_address(&bcs_bytes, crate::crypto::SINGLE_KEY_SCHEME)
307    }
308}
309
310impl PublicKey for Secp256k1PublicKey {
311    const LENGTH: usize = SECP256K1_PUBLIC_KEY_LENGTH;
312
313    fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
314        Secp256k1PublicKey::from_bytes(bytes)
315    }
316
317    fn to_bytes(&self) -> Vec<u8> {
318        Secp256k1PublicKey::to_bytes(self)
319    }
320}
321
322impl Verifier for Secp256k1PublicKey {
323    type Signature = Secp256k1Signature;
324
325    fn verify(&self, message: &[u8], signature: &Secp256k1Signature) -> AptosResult<()> {
326        Secp256k1PublicKey::verify(self, message, signature)
327    }
328}
329
330impl fmt::Debug for Secp256k1PublicKey {
331    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332        write!(f, "Secp256k1PublicKey({})", self.to_hex())
333    }
334}
335
336impl fmt::Display for Secp256k1PublicKey {
337    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338        write!(f, "{}", self.to_hex())
339    }
340}
341
342impl Serialize for Secp256k1PublicKey {
343    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
344    where
345        S: serde::Serializer,
346    {
347        if serializer.is_human_readable() {
348            serializer.serialize_str(&self.to_hex())
349        } else {
350            serializer.serialize_bytes(&self.to_bytes())
351        }
352    }
353}
354
355impl<'de> Deserialize<'de> for Secp256k1PublicKey {
356    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
357    where
358        D: serde::Deserializer<'de>,
359    {
360        if deserializer.is_human_readable() {
361            let s = String::deserialize(deserializer)?;
362            Self::from_hex(&s).map_err(serde::de::Error::custom)
363        } else {
364            let bytes = Vec::<u8>::deserialize(deserializer)?;
365            Self::from_bytes(&bytes).map_err(serde::de::Error::custom)
366        }
367    }
368}
369
370/// A Secp256k1 ECDSA signature.
371#[derive(Clone, Copy, PartialEq, Eq)]
372pub struct Secp256k1Signature {
373    inner: K256Signature,
374}
375
376impl Secp256k1Signature {
377    /// Creates a signature from raw bytes (64 bytes, r || s).
378    ///
379    /// # Security
380    ///
381    /// Rejects high-S signatures to match Aptos on-chain verification behavior.
382    /// The Aptos VM only accepts low-S (canonical) ECDSA signatures to prevent
383    /// signature malleability attacks.
384    ///
385    /// # Errors
386    ///
387    /// Returns [`AptosError::InvalidSignature`] if:
388    /// - The byte slice length is not exactly 64 bytes
389    /// - The bytes do not represent a valid Secp256k1 signature
390    /// - The signature has a high-S value (not canonical)
391    pub fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
392        if bytes.len() != SECP256K1_SIGNATURE_LENGTH {
393            return Err(AptosError::InvalidSignature(format!(
394                "expected {} bytes, got {}",
395                SECP256K1_SIGNATURE_LENGTH,
396                bytes.len()
397            )));
398        }
399        let signature = K256Signature::from_slice(bytes)
400            .map_err(|e| AptosError::InvalidSignature(e.to_string()))?;
401        // SECURITY: Reject high-S signatures. Aptos on-chain verification only
402        // accepts low-S (canonical) signatures to prevent malleability.
403        // normalize_s() returns Some(_) if the signature was high-S.
404        if signature.normalize_s().is_some() {
405            return Err(AptosError::InvalidSignature(
406                "high-S signature rejected: Aptos requires low-S (canonical) ECDSA signatures"
407                    .into(),
408            ));
409        }
410        Ok(Self { inner: signature })
411    }
412
413    /// Creates a signature from a hex string.
414    ///
415    /// # Errors
416    ///
417    /// Returns [`AptosError::Hex`] if the hex string is invalid.
418    /// Returns [`AptosError::InvalidSignature`] if the decoded bytes are not exactly 64 bytes or do not represent a valid Secp256k1 signature.
419    pub fn from_hex(hex_str: &str) -> AptosResult<Self> {
420        let bytes = const_hex::decode(hex_str)?;
421        Self::from_bytes(&bytes)
422    }
423
424    /// Returns the signature as bytes (64 bytes, r || s).
425    pub fn to_bytes(&self) -> [u8; SECP256K1_SIGNATURE_LENGTH] {
426        self.inner.to_bytes().into()
427    }
428
429    /// Returns the signature as a hex string.
430    pub fn to_hex(&self) -> String {
431        const_hex::encode_prefixed(self.to_bytes())
432    }
433}
434
435impl Signature for Secp256k1Signature {
436    type PublicKey = Secp256k1PublicKey;
437    const LENGTH: usize = SECP256K1_SIGNATURE_LENGTH;
438
439    fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
440        Secp256k1Signature::from_bytes(bytes)
441    }
442
443    fn to_bytes(&self) -> Vec<u8> {
444        self.inner.to_bytes().to_vec()
445    }
446}
447
448impl fmt::Debug for Secp256k1Signature {
449    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
450        write!(f, "Secp256k1Signature({})", self.to_hex())
451    }
452}
453
454impl fmt::Display for Secp256k1Signature {
455    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456        write!(f, "{}", self.to_hex())
457    }
458}
459
460impl Serialize for Secp256k1Signature {
461    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
462    where
463        S: serde::Serializer,
464    {
465        if serializer.is_human_readable() {
466            serializer.serialize_str(&self.to_hex())
467        } else {
468            serializer.serialize_bytes(&self.to_bytes())
469        }
470    }
471}
472
473impl<'de> Deserialize<'de> for Secp256k1Signature {
474    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
475    where
476        D: serde::Deserializer<'de>,
477    {
478        if deserializer.is_human_readable() {
479            let s = String::deserialize(deserializer)?;
480            Self::from_hex(&s).map_err(serde::de::Error::custom)
481        } else {
482            let bytes = Vec::<u8>::deserialize(deserializer)?;
483            Self::from_bytes(&bytes).map_err(serde::de::Error::custom)
484        }
485    }
486}
487
488#[cfg(test)]
489mod tests {
490    use super::*;
491
492    #[test]
493    fn test_generate_and_sign() {
494        let private_key = Secp256k1PrivateKey::generate();
495        let message = b"hello world";
496        let signature = private_key.sign(message);
497
498        let public_key = private_key.public_key();
499        assert!(public_key.verify(message, &signature).is_ok());
500    }
501
502    #[test]
503    fn test_wrong_message_fails() {
504        let private_key = Secp256k1PrivateKey::generate();
505        let message = b"hello world";
506        let wrong_message = b"hello world!";
507        let signature = private_key.sign(message);
508
509        let public_key = private_key.public_key();
510        assert!(public_key.verify(wrong_message, &signature).is_err());
511    }
512
513    #[test]
514    fn test_from_bytes_roundtrip() {
515        let private_key = Secp256k1PrivateKey::generate();
516        let bytes = private_key.to_bytes();
517        let restored = Secp256k1PrivateKey::from_bytes(&bytes).unwrap();
518        assert_eq!(private_key.to_bytes(), restored.to_bytes());
519    }
520
521    #[test]
522    fn test_public_key_compressed() {
523        let private_key = Secp256k1PrivateKey::generate();
524        let public_key = private_key.public_key();
525
526        // Compressed should be 33 bytes
527        assert_eq!(public_key.to_bytes().len(), 33);
528
529        // Uncompressed should be 65 bytes
530        assert_eq!(public_key.to_uncompressed_bytes().len(), 65);
531    }
532
533    #[test]
534    fn test_public_key_from_bytes_roundtrip() {
535        let private_key = Secp256k1PrivateKey::generate();
536        let public_key = private_key.public_key();
537        let bytes = public_key.to_bytes();
538        let restored = Secp256k1PublicKey::from_bytes(&bytes).unwrap();
539        assert_eq!(public_key.to_bytes(), restored.to_bytes());
540    }
541
542    #[test]
543    fn test_signature_from_bytes_roundtrip() {
544        let private_key = Secp256k1PrivateKey::generate();
545        let signature = private_key.sign(b"test");
546        let bytes = signature.to_bytes();
547        let restored = Secp256k1Signature::from_bytes(&bytes).unwrap();
548        assert_eq!(signature.to_bytes(), restored.to_bytes());
549    }
550
551    #[test]
552    fn test_hex_roundtrip() {
553        let private_key = Secp256k1PrivateKey::generate();
554        let hex = private_key.to_hex();
555        let restored = Secp256k1PrivateKey::from_hex(&hex).unwrap();
556        assert_eq!(private_key.to_bytes(), restored.to_bytes());
557    }
558
559    #[test]
560    fn test_public_key_hex_roundtrip() {
561        let private_key = Secp256k1PrivateKey::generate();
562        let public_key = private_key.public_key();
563        let hex = public_key.to_hex();
564        let restored = Secp256k1PublicKey::from_hex(&hex).unwrap();
565        assert_eq!(public_key.to_bytes(), restored.to_bytes());
566    }
567
568    #[test]
569    fn test_signature_hex_roundtrip() {
570        let private_key = Secp256k1PrivateKey::generate();
571        let signature = private_key.sign(b"test");
572        let hex = signature.to_hex();
573        let restored = Secp256k1Signature::from_hex(&hex).unwrap();
574        assert_eq!(signature.to_bytes(), restored.to_bytes());
575    }
576
577    #[test]
578    fn test_invalid_private_key_bytes() {
579        let bytes = vec![0u8; 16]; // Wrong length
580        let result = Secp256k1PrivateKey::from_bytes(&bytes);
581        assert!(result.is_err());
582    }
583
584    #[test]
585    fn test_invalid_public_key_bytes() {
586        let bytes = vec![0u8; 16]; // Wrong length
587        let result = Secp256k1PublicKey::from_bytes(&bytes);
588        assert!(result.is_err());
589    }
590
591    #[test]
592    fn test_invalid_signature_bytes() {
593        let bytes = vec![0u8; 16]; // Wrong length
594        let result = Secp256k1Signature::from_bytes(&bytes);
595        assert!(result.is_err());
596    }
597
598    #[test]
599    fn test_high_s_signature_rejected() {
600        use k256::elliptic_curve::ops::Neg;
601
602        // Sign a message (produces low-S)
603        let private_key = Secp256k1PrivateKey::generate();
604        let signature = private_key.sign(b"test message");
605
606        // Construct high-S by negating the S component: S' = n - S
607        let low_s_sig = k256::ecdsa::Signature::from_slice(&signature.to_bytes()).unwrap();
608        let (r, s) = low_s_sig.split_scalars();
609        let neg_s = s.neg();
610        let high_s_sig = k256::ecdsa::Signature::from_scalars(r, neg_s).unwrap();
611        // Confirm it really is high-S (normalize_s returns Some for high-S)
612        assert!(
613            high_s_sig.normalize_s().is_some(),
614            "constructed signature should be high-S"
615        );
616        let high_s_bytes = high_s_sig.to_bytes();
617
618        // from_bytes should reject high-S
619        let result = Secp256k1Signature::from_bytes(&high_s_bytes);
620        assert!(result.is_err(), "high-S signature should be rejected");
621        assert!(
622            result
623                .unwrap_err()
624                .to_string()
625                .contains("high-S signature rejected"),
626            "error message should mention high-S rejection"
627        );
628
629        // Verify should also reject high-S (defense-in-depth via inner field)
630        let sig_with_high_s = Secp256k1Signature { inner: high_s_sig };
631        let public_key = private_key.public_key();
632        let result = public_key.verify(b"test message", &sig_with_high_s);
633        assert!(result.is_err(), "verify should reject high-S signature");
634    }
635
636    #[test]
637    fn test_signing_always_produces_low_s() {
638        // Run multiple iterations to increase confidence
639        for _ in 0..20 {
640            let private_key = Secp256k1PrivateKey::generate();
641            let signature = private_key.sign(b"test low-s");
642            // normalize_s returns None if already low-S
643            assert!(
644                signature.inner.normalize_s().is_none(),
645                "signing must always produce low-S signatures"
646            );
647        }
648    }
649
650    #[test]
651    fn test_json_serialization_public_key() {
652        let private_key = Secp256k1PrivateKey::generate();
653        let public_key = private_key.public_key();
654        let json = serde_json::to_string(&public_key).unwrap();
655        let restored: Secp256k1PublicKey = serde_json::from_str(&json).unwrap();
656        assert_eq!(public_key.to_bytes(), restored.to_bytes());
657    }
658
659    #[test]
660    fn test_json_serialization_signature() {
661        let private_key = Secp256k1PrivateKey::generate();
662        let signature = private_key.sign(b"test");
663        let json = serde_json::to_string(&signature).unwrap();
664        let restored: Secp256k1Signature = serde_json::from_str(&json).unwrap();
665        assert_eq!(signature.to_bytes(), restored.to_bytes());
666    }
667
668    #[test]
669    fn test_key_lengths() {
670        assert_eq!(Secp256k1PublicKey::LENGTH, SECP256K1_PUBLIC_KEY_LENGTH);
671        assert_eq!(Secp256k1Signature::LENGTH, SECP256K1_SIGNATURE_LENGTH);
672    }
673
674    #[test]
675    fn test_display_debug() {
676        let private_key = Secp256k1PrivateKey::generate();
677        let public_key = private_key.public_key();
678        let signature = private_key.sign(b"test");
679
680        // Debug should contain type name
681        assert!(format!("{public_key:?}").contains("Secp256k1PublicKey"));
682        assert!(format!("{signature:?}").contains("Secp256k1Signature"));
683
684        // Display should show hex
685        assert!(format!("{public_key}").starts_with("0x"));
686        assert!(format!("{signature}").starts_with("0x"));
687    }
688
689    #[test]
690    fn test_private_key_aip80_roundtrip() {
691        let private_key = Secp256k1PrivateKey::generate();
692        let aip80 = private_key.to_aip80();
693
694        // Should have correct prefix
695        assert!(aip80.starts_with("secp256k1-priv-0x"));
696
697        // Should roundtrip correctly
698        let restored = Secp256k1PrivateKey::from_aip80(&aip80).unwrap();
699        assert_eq!(private_key.to_bytes(), restored.to_bytes());
700    }
701
702    #[test]
703    fn test_private_key_aip80_format() {
704        let bytes = [0x01; 32];
705        let private_key = Secp256k1PrivateKey::from_bytes(&bytes).unwrap();
706        let aip80 = private_key.to_aip80();
707
708        // Expected format: secp256k1-priv-0x0101...01
709        let expected = format!("secp256k1-priv-0x{}", "01".repeat(32));
710        assert_eq!(aip80, expected);
711    }
712
713    #[test]
714    fn test_private_key_aip80_invalid_prefix() {
715        let result = Secp256k1PrivateKey::from_aip80("ed25519-priv-0x01");
716        assert!(result.is_err());
717    }
718
719    #[test]
720    fn test_public_key_aip80_roundtrip() {
721        let private_key = Secp256k1PrivateKey::generate();
722        let public_key = private_key.public_key();
723        let aip80 = public_key.to_aip80();
724
725        // Should have correct prefix
726        assert!(aip80.starts_with("secp256k1-pub-0x"));
727
728        // Should roundtrip correctly
729        let restored = Secp256k1PublicKey::from_aip80(&aip80).unwrap();
730        assert_eq!(public_key.to_bytes(), restored.to_bytes());
731    }
732
733    #[test]
734    fn test_public_key_aip80_invalid_prefix() {
735        let result = Secp256k1PublicKey::from_aip80("ed25519-pub-0x01");
736        assert!(result.is_err());
737    }
738
739    #[test]
740    fn test_signer_trait() {
741        use crate::crypto::traits::Signer;
742
743        let private_key = Secp256k1PrivateKey::generate();
744        let message = b"trait test";
745
746        let signature = Signer::sign(&private_key, message);
747        let public_key = Signer::public_key(&private_key);
748
749        assert!(public_key.verify(message, &signature).is_ok());
750    }
751
752    #[test]
753    fn test_verifier_trait() {
754        use crate::crypto::traits::Verifier;
755
756        let private_key = Secp256k1PrivateKey::generate();
757        let public_key = private_key.public_key();
758        let message = b"verifier test";
759        let signature = private_key.sign(message);
760
761        assert!(Verifier::verify(&public_key, message, &signature).is_ok());
762    }
763
764    #[test]
765    fn test_public_key_trait() {
766        use crate::crypto::traits::PublicKey;
767
768        let private_key = Secp256k1PrivateKey::generate();
769        let public_key = private_key.public_key();
770        let bytes = PublicKey::to_bytes(&public_key);
771        let restored = Secp256k1PublicKey::from_bytes(&bytes).unwrap();
772        assert_eq!(public_key.to_bytes(), restored.to_bytes());
773    }
774
775    #[test]
776    fn test_signature_trait() {
777        use crate::crypto::traits::Signature;
778
779        let private_key = Secp256k1PrivateKey::generate();
780        let signature = private_key.sign(b"test");
781        let bytes = Signature::to_bytes(&signature);
782        let restored = Secp256k1Signature::from_bytes(&bytes).unwrap();
783        assert_eq!(signature.to_bytes(), restored.to_bytes());
784    }
785
786    #[test]
787    fn test_private_key_debug() {
788        let private_key = Secp256k1PrivateKey::generate();
789        let debug = format!("{private_key:?}");
790        assert!(debug.contains("REDACTED"));
791        assert!(!debug.contains(&private_key.to_hex()));
792    }
793
794    #[test]
795    fn test_address_derivation() {
796        let private_key = Secp256k1PrivateKey::generate();
797        let public_key = private_key.public_key();
798        let address = public_key.to_address();
799
800        // Address should not be zero
801        assert!(!address.is_zero());
802
803        // Same public key should derive same address
804        let address2 = public_key.to_address();
805        assert_eq!(address, address2);
806    }
807
808    #[test]
809    fn test_uncompressed_bytes() {
810        let private_key = Secp256k1PrivateKey::generate();
811        let public_key = private_key.public_key();
812
813        // Uncompressed should be 65 bytes (0x04 prefix + 64 bytes)
814        let uncompressed = public_key.to_uncompressed_bytes();
815        assert_eq!(uncompressed.len(), 65);
816        assert_eq!(uncompressed[0], 0x04); // Uncompressed point prefix
817    }
818
819    #[test]
820    fn test_private_key_clone() {
821        let private_key = Secp256k1PrivateKey::generate();
822        let cloned = private_key.clone();
823        assert_eq!(private_key.to_bytes(), cloned.to_bytes());
824    }
825
826    #[test]
827    fn test_public_key_equality() {
828        let private_key = Secp256k1PrivateKey::generate();
829        let pk1 = private_key.public_key();
830        let pk2 = private_key.public_key();
831        assert_eq!(pk1, pk2);
832
833        let different = Secp256k1PrivateKey::generate().public_key();
834        assert_ne!(pk1, different);
835    }
836
837    #[test]
838    fn test_signature_equality() {
839        let private_key = Secp256k1PrivateKey::generate();
840        let sig1 = private_key.sign(b"test");
841        let sig2 = private_key.sign(b"test");
842        // Note: ECDSA signatures may have randomness, so they might not be equal
843        // But they should both verify
844        let public_key = private_key.public_key();
845        assert!(public_key.verify(b"test", &sig1).is_ok());
846        assert!(public_key.verify(b"test", &sig2).is_ok());
847    }
848}