Skip to main content

aptos_sdk/crypto/
secp256r1.rs

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