Skip to main content

near_kit/types/
key.rs

1//! Cryptographic key types for NEAR.
2
3use std::fmt::{self, Debug, Display};
4use std::str::FromStr;
5
6use bip39::Mnemonic;
7use borsh::{BorshDeserialize, BorshSerialize};
8use ed25519_dalek::{Signer as _, SigningKey, VerifyingKey};
9use k256::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
10use rand::rngs::OsRng;
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12use sha2::Digest;
13use slipped10::{BIP32Path, Curve};
14
15use crate::error::{ParseKeyError, SignerError};
16
17/// Key type identifier.
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
19#[repr(u8)]
20pub enum KeyType {
21    /// Ed25519 key (most common).
22    Ed25519 = 0,
23    /// Secp256k1 key (for Ethereum compatibility).
24    Secp256k1 = 1,
25}
26
27impl KeyType {
28    /// Get the string prefix for this key type.
29    pub fn as_str(&self) -> &'static str {
30        match self {
31            KeyType::Ed25519 => "ed25519",
32            KeyType::Secp256k1 => "secp256k1",
33        }
34    }
35
36    /// Get the expected public key length in bytes.
37    pub fn public_key_len(&self) -> usize {
38        match self {
39            KeyType::Ed25519 => 32,
40            KeyType::Secp256k1 => 64, // Uncompressed, no prefix — matches nearcore
41        }
42    }
43
44    /// Get the expected signature length in bytes.
45    pub fn signature_len(&self) -> usize {
46        match self {
47            KeyType::Ed25519 => 64,
48            KeyType::Secp256k1 => 65,
49        }
50    }
51}
52
53impl TryFrom<u8> for KeyType {
54    type Error = ParseKeyError;
55
56    fn try_from(value: u8) -> Result<Self, Self::Error> {
57        match value {
58            0 => Ok(KeyType::Ed25519),
59            1 => Ok(KeyType::Secp256k1),
60            _ => Err(ParseKeyError::UnknownKeyType(value.to_string())),
61        }
62    }
63}
64
65/// Ed25519 or Secp256k1 public key.
66///
67/// Stored as fixed-size arrays matching nearcore's representation:
68/// - Ed25519: 32-byte compressed point
69/// - Secp256k1: 64-byte uncompressed point (x, y coordinates, no `0x04` prefix)
70#[derive(Clone, PartialEq, Eq, Hash)]
71pub enum PublicKey {
72    /// Ed25519 public key (32 bytes).
73    Ed25519([u8; 32]),
74    /// Secp256k1 public key (64 bytes, uncompressed without prefix).
75    ///
76    /// This matches nearcore's representation: raw x,y coordinates
77    /// without the `0x04` uncompressed point prefix.
78    Secp256k1([u8; 64]),
79}
80
81impl PublicKey {
82    /// Create an Ed25519 public key from raw 32 bytes.
83    pub fn ed25519_from_bytes(bytes: [u8; 32]) -> Self {
84        Self::Ed25519(bytes)
85    }
86
87    /// Create a Secp256k1 public key from raw 64-byte uncompressed coordinates.
88    ///
89    /// The 64 bytes are the raw x,y coordinates without the `0x04` prefix,
90    /// matching nearcore's format.
91    ///
92    /// Validates that the point is on the secp256k1 curve.
93    ///
94    /// # Panics
95    ///
96    /// Panics if the bytes do not represent a valid point on the secp256k1 curve.
97    pub fn secp256k1_from_bytes(bytes: [u8; 64]) -> Self {
98        // Validate the point is on the curve by constructing full uncompressed encoding
99        let mut uncompressed = [0u8; 65];
100        uncompressed[0] = 0x04;
101        uncompressed[1..].copy_from_slice(&bytes);
102        let encoded = k256::EncodedPoint::from_bytes(uncompressed.as_ref())
103            .expect("invalid secp256k1 SEC1 encoding");
104        let point = k256::AffinePoint::from_encoded_point(&encoded);
105        assert!(bool::from(point.is_some()), "invalid secp256k1 curve point");
106
107        Self::Secp256k1(bytes)
108    }
109
110    /// Create a Secp256k1 public key from a compressed 33-byte SEC1 encoding.
111    ///
112    /// The key is validated and stored internally in uncompressed (64-byte) format
113    /// matching nearcore's representation.
114    ///
115    /// # Panics
116    ///
117    /// Panics if the bytes do not represent a valid point on the secp256k1 curve.
118    pub fn secp256k1_from_compressed(bytes: [u8; 33]) -> Self {
119        let encoded = k256::EncodedPoint::from_bytes(bytes.as_ref())
120            .expect("invalid secp256k1 SEC1 encoding");
121        let point = k256::AffinePoint::from_encoded_point(&encoded);
122        let point: k256::AffinePoint = Option::from(point).expect("invalid secp256k1 curve point");
123
124        // Re-encode as uncompressed (65 bytes with 0x04 prefix)
125        let uncompressed = point.to_encoded_point(false);
126        let uncompressed_bytes: &[u8] = uncompressed.as_bytes();
127        assert_eq!(uncompressed_bytes.len(), 65);
128        assert_eq!(uncompressed_bytes[0], 0x04);
129
130        // Store without the 0x04 prefix (64 bytes)
131        let mut result = [0u8; 64];
132        result.copy_from_slice(&uncompressed_bytes[1..]);
133        Self::Secp256k1(result)
134    }
135
136    /// Create a Secp256k1 public key from an uncompressed 65-byte SEC1 encoding
137    /// (with `0x04` prefix).
138    ///
139    /// The key is validated and stored internally without the prefix (64 bytes),
140    /// matching nearcore's format.
141    ///
142    /// # Panics
143    ///
144    /// Panics if the bytes do not represent a valid point on the secp256k1 curve.
145    pub fn secp256k1_from_uncompressed(bytes: [u8; 65]) -> Self {
146        let encoded = k256::EncodedPoint::from_bytes(bytes.as_ref())
147            .expect("invalid secp256k1 SEC1 encoding");
148        let point = k256::AffinePoint::from_encoded_point(&encoded);
149        assert!(bool::from(point.is_some()), "invalid secp256k1 curve point");
150
151        // Store without the 0x04 prefix (64 bytes)
152        let mut result = [0u8; 64];
153        result.copy_from_slice(&bytes[1..]);
154        Self::Secp256k1(result)
155    }
156
157    /// Get the key type.
158    pub fn key_type(&self) -> KeyType {
159        match self {
160            Self::Ed25519(_) => KeyType::Ed25519,
161            Self::Secp256k1(_) => KeyType::Secp256k1,
162        }
163    }
164
165    /// Get the raw key bytes as a slice.
166    pub fn as_bytes(&self) -> &[u8] {
167        match self {
168            Self::Ed25519(bytes) => bytes.as_slice(),
169            Self::Secp256k1(bytes) => bytes.as_slice(),
170        }
171    }
172
173    /// Get the key data as a fixed-size array for Ed25519 keys.
174    pub fn as_ed25519_bytes(&self) -> Option<&[u8; 32]> {
175        match self {
176            Self::Ed25519(bytes) => Some(bytes),
177            _ => None,
178        }
179    }
180
181    /// Get the key data as a fixed-size array for Secp256k1 keys
182    /// (64-byte uncompressed, no prefix).
183    pub fn as_secp256k1_bytes(&self) -> Option<&[u8; 64]> {
184        match self {
185            Self::Secp256k1(bytes) => Some(bytes),
186            _ => None,
187        }
188    }
189}
190
191impl FromStr for PublicKey {
192    type Err = ParseKeyError;
193
194    fn from_str(s: &str) -> Result<Self, Self::Err> {
195        let (key_type, data_str) = s.split_once(':').ok_or(ParseKeyError::InvalidFormat)?;
196
197        let key_type = match key_type {
198            "ed25519" => KeyType::Ed25519,
199            "secp256k1" => KeyType::Secp256k1,
200            other => return Err(ParseKeyError::UnknownKeyType(other.to_string())),
201        };
202
203        let data = bs58::decode(data_str)
204            .into_vec()
205            .map_err(|e| ParseKeyError::InvalidBase58(e.to_string()))?;
206
207        if data.len() != key_type.public_key_len() {
208            return Err(ParseKeyError::InvalidLength {
209                expected: key_type.public_key_len(),
210                actual: data.len(),
211            });
212        }
213
214        match key_type {
215            KeyType::Ed25519 => {
216                let bytes: [u8; 32] = data
217                    .as_slice()
218                    .try_into()
219                    .map_err(|_| ParseKeyError::InvalidCurvePoint)?;
220                VerifyingKey::from_bytes(&bytes).map_err(|_| ParseKeyError::InvalidCurvePoint)?;
221                Ok(Self::Ed25519(bytes))
222            }
223            KeyType::Secp256k1 => {
224                // Data is 64 bytes (uncompressed, no prefix). Add 0x04 prefix for validation.
225                let mut uncompressed = [0u8; 65];
226                uncompressed[0] = 0x04;
227                uncompressed[1..].copy_from_slice(&data);
228                let encoded = k256::EncodedPoint::from_bytes(uncompressed)
229                    .map_err(|_| ParseKeyError::InvalidCurvePoint)?;
230                let point = k256::AffinePoint::from_encoded_point(&encoded);
231                if point.is_none().into() {
232                    return Err(ParseKeyError::InvalidCurvePoint);
233                }
234                let bytes: [u8; 64] = data
235                    .as_slice()
236                    .try_into()
237                    .map_err(|_| ParseKeyError::InvalidCurvePoint)?;
238                Ok(Self::Secp256k1(bytes))
239            }
240        }
241    }
242}
243
244impl TryFrom<&str> for PublicKey {
245    type Error = ParseKeyError;
246
247    fn try_from(s: &str) -> Result<Self, Self::Error> {
248        s.parse()
249    }
250}
251
252impl Display for PublicKey {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        write!(
255            f,
256            "{}:{}",
257            self.key_type().as_str(),
258            bs58::encode(self.as_bytes()).into_string()
259        )
260    }
261}
262
263impl Debug for PublicKey {
264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265        write!(f, "PublicKey({})", self)
266    }
267}
268
269impl Serialize for PublicKey {
270    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
271        s.serialize_str(&self.to_string())
272    }
273}
274
275impl<'de> Deserialize<'de> for PublicKey {
276    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
277        let s: String = serde::Deserialize::deserialize(d)?;
278        s.parse().map_err(serde::de::Error::custom)
279    }
280}
281
282impl BorshSerialize for PublicKey {
283    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
284        borsh::BorshSerialize::serialize(&(self.key_type() as u8), writer)?;
285        writer.write_all(self.as_bytes())?;
286        Ok(())
287    }
288}
289
290impl BorshDeserialize for PublicKey {
291    fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
292        let key_type_byte = u8::deserialize_reader(reader)?;
293        let key_type = KeyType::try_from(key_type_byte)
294            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
295
296        match key_type {
297            KeyType::Ed25519 => {
298                let mut bytes = [0u8; 32];
299                reader.read_exact(&mut bytes)?;
300                VerifyingKey::from_bytes(&bytes).map_err(|_| {
301                    std::io::Error::new(
302                        std::io::ErrorKind::InvalidData,
303                        "invalid ed25519 curve point",
304                    )
305                })?;
306                Ok(Self::Ed25519(bytes))
307            }
308            KeyType::Secp256k1 => {
309                let mut bytes = [0u8; 64];
310                reader.read_exact(&mut bytes)?;
311                // Validate point is on curve
312                let mut uncompressed = [0u8; 65];
313                uncompressed[0] = 0x04;
314                uncompressed[1..].copy_from_slice(&bytes);
315                let encoded = k256::EncodedPoint::from_bytes(uncompressed).map_err(|_| {
316                    std::io::Error::new(
317                        std::io::ErrorKind::InvalidData,
318                        "invalid secp256k1 encoding",
319                    )
320                })?;
321                let point = k256::AffinePoint::from_encoded_point(&encoded);
322                if point.is_none().into() {
323                    return Err(std::io::Error::new(
324                        std::io::ErrorKind::InvalidData,
325                        "invalid secp256k1 curve point",
326                    ));
327                }
328                Ok(Self::Secp256k1(bytes))
329            }
330        }
331    }
332}
333
334/// Default BIP-32 HD derivation path for NEAR keys.
335/// NEAR uses coin type 397 per SLIP-44.
336pub const DEFAULT_HD_PATH: &str = "m/44'/397'/0'";
337
338/// Default number of words in generated seed phrases.
339pub const DEFAULT_WORD_COUNT: usize = 12;
340
341/// Ed25519 or Secp256k1 secret key.
342#[derive(Clone)]
343pub enum SecretKey {
344    /// Ed25519 secret key (32-byte seed).
345    Ed25519([u8; 32]),
346    /// Secp256k1 secret key (32-byte scalar).
347    Secp256k1([u8; 32]),
348}
349
350impl SecretKey {
351    /// Generate a new random Ed25519 key pair.
352    pub fn generate_ed25519() -> Self {
353        let signing_key = SigningKey::generate(&mut OsRng);
354        Self::Ed25519(signing_key.to_bytes())
355    }
356
357    /// Create an Ed25519 secret key from raw 32 bytes.
358    pub fn ed25519_from_bytes(bytes: [u8; 32]) -> Self {
359        Self::Ed25519(bytes)
360    }
361
362    /// Generate a new random Secp256k1 key pair.
363    pub fn generate_secp256k1() -> Self {
364        let secret_key = k256::SecretKey::random(&mut OsRng);
365        let mut bytes = [0u8; 32];
366        bytes.copy_from_slice(&secret_key.to_bytes());
367        Self::Secp256k1(bytes)
368    }
369
370    /// Create a Secp256k1 secret key from raw 32 bytes.
371    ///
372    /// Validates that the bytes represent a valid secp256k1 scalar
373    /// (non-zero and less than the curve order).
374    pub fn secp256k1_from_bytes(bytes: [u8; 32]) -> Result<Self, ParseKeyError> {
375        k256::SecretKey::from_bytes((&bytes).into()).map_err(|_| ParseKeyError::InvalidScalar)?;
376        Ok(Self::Secp256k1(bytes))
377    }
378
379    /// Get the key type.
380    pub fn key_type(&self) -> KeyType {
381        match self {
382            Self::Ed25519(_) => KeyType::Ed25519,
383            Self::Secp256k1(_) => KeyType::Secp256k1,
384        }
385    }
386
387    /// Get the raw key bytes as a slice.
388    pub fn as_bytes(&self) -> &[u8] {
389        match self {
390            Self::Ed25519(bytes) => bytes.as_slice(),
391            Self::Secp256k1(bytes) => bytes.as_slice(),
392        }
393    }
394
395    /// Derive the public key.
396    pub fn public_key(&self) -> PublicKey {
397        match self {
398            Self::Ed25519(bytes) => {
399                let signing_key = SigningKey::from_bytes(bytes);
400                let verifying_key = signing_key.verifying_key();
401                PublicKey::Ed25519(verifying_key.to_bytes())
402            }
403            Self::Secp256k1(bytes) => {
404                let secret_key =
405                    k256::SecretKey::from_bytes(bytes.into()).expect("invalid secp256k1 key");
406                let public_key = secret_key.public_key();
407                // Get uncompressed encoding (65 bytes with 0x04 prefix)
408                let uncompressed = public_key.to_encoded_point(false);
409                let uncompressed_bytes: &[u8] = uncompressed.as_bytes();
410                assert_eq!(uncompressed_bytes.len(), 65);
411                // Store without the 0x04 prefix (64 bytes)
412                let mut result = [0u8; 64];
413                result.copy_from_slice(&uncompressed_bytes[1..]);
414                PublicKey::Secp256k1(result)
415            }
416        }
417    }
418
419    /// Sign a message.
420    pub fn sign(&self, message: &[u8]) -> Signature {
421        match self {
422            Self::Ed25519(bytes) => {
423                let signing_key = SigningKey::from_bytes(bytes);
424                let signature = signing_key.sign(message);
425                Signature::Ed25519(signature.to_bytes())
426            }
427            Self::Secp256k1(bytes) => {
428                let signing_key = k256::ecdsa::SigningKey::from_bytes(bytes.into())
429                    .expect("invalid secp256k1 key");
430
431                // NEAR protocol: hash message with SHA-256 before signing
432                let hash = sha2::Sha256::digest(message);
433                let (signature, recovery_id) = signing_key
434                    .sign_prehash_recoverable(&hash)
435                    .expect("secp256k1 signing failed");
436
437                // NEAR format: [r (32) | s (32) | v (1)]
438                let mut sig_bytes = [0u8; 65];
439                sig_bytes[..64].copy_from_slice(&signature.to_bytes());
440                sig_bytes[64] = recovery_id.to_byte();
441
442                Signature::Secp256k1(sig_bytes)
443            }
444        }
445    }
446
447    // ========================================================================
448    // Seed Phrase / Mnemonic Support
449    // ========================================================================
450
451    /// Derive an Ed25519 secret key from a BIP-39 seed phrase.
452    ///
453    /// Uses SLIP-10 derivation with the default NEAR HD path (`m/44'/397'/0'`).
454    ///
455    /// # Arguments
456    ///
457    /// * `phrase` - BIP-39 mnemonic phrase (12, 15, 18, 21, or 24 words)
458    ///
459    /// # Example
460    ///
461    /// ```rust
462    /// use near_kit::SecretKey;
463    ///
464    /// // Valid BIP-39 mnemonic (all zeros entropy)
465    /// let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
466    /// let secret_key = SecretKey::from_seed_phrase(phrase).unwrap();
467    /// ```
468    pub fn from_seed_phrase(phrase: impl AsRef<str>) -> Result<Self, SignerError> {
469        Self::from_seed_phrase_with_path(phrase, DEFAULT_HD_PATH)
470    }
471
472    /// Derive an Ed25519 secret key from a BIP-39 seed phrase with custom HD path.
473    ///
474    /// Uses SLIP-10 derivation for Ed25519 keys. Only hardened derivation paths
475    /// are supported (all path components must use `'` suffix).
476    ///
477    /// # Arguments
478    ///
479    /// * `phrase` - BIP-39 mnemonic phrase (12, 15, 18, 21, or 24 words)
480    /// * `hd_path` - BIP-32 derivation path (e.g., `"m/44'/397'/0'"`)
481    ///
482    /// # Example
483    ///
484    /// ```rust
485    /// use near_kit::SecretKey;
486    ///
487    /// let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
488    /// let secret_key = SecretKey::from_seed_phrase_with_path(phrase, "m/44'/397'/1'").unwrap();
489    /// ```
490    pub fn from_seed_phrase_with_path(
491        phrase: impl AsRef<str>,
492        hd_path: impl AsRef<str>,
493    ) -> Result<Self, SignerError> {
494        Self::from_seed_phrase_with_path_and_passphrase(phrase, hd_path, None)
495    }
496
497    /// Derive an Ed25519 secret key from a BIP-39 seed phrase with passphrase.
498    ///
499    /// The passphrase provides additional entropy for seed generation (BIP-39 feature).
500    /// An empty passphrase is equivalent to no passphrase.
501    ///
502    /// # Arguments
503    ///
504    /// * `phrase` - BIP-39 mnemonic phrase
505    /// * `hd_path` - BIP-32 derivation path
506    /// * `passphrase` - Optional passphrase for additional entropy
507    ///
508    /// # Example
509    ///
510    /// ```rust
511    /// use near_kit::SecretKey;
512    ///
513    /// let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
514    /// let secret_key = SecretKey::from_seed_phrase_with_path_and_passphrase(
515    ///     phrase,
516    ///     "m/44'/397'/0'",
517    ///     Some("my-passphrase")
518    /// ).unwrap();
519    /// ```
520    pub fn from_seed_phrase_with_path_and_passphrase(
521        phrase: impl AsRef<str>,
522        hd_path: impl AsRef<str>,
523        passphrase: Option<&str>,
524    ) -> Result<Self, SignerError> {
525        // Normalize and parse mnemonic
526        let normalized = phrase
527            .as_ref()
528            .trim()
529            .to_lowercase()
530            .split_whitespace()
531            .collect::<Vec<_>>()
532            .join(" ");
533
534        let mnemonic: Mnemonic = normalized
535            .parse()
536            .map_err(|_| SignerError::InvalidSeedPhrase)?;
537
538        // Convert mnemonic to seed (64 bytes)
539        let seed = mnemonic.to_seed(passphrase.unwrap_or(""));
540
541        // Parse HD path
542        let path: BIP32Path = hd_path
543            .as_ref()
544            .parse()
545            .map_err(|e| SignerError::KeyDerivationFailed(format!("Invalid HD path: {}", e)))?;
546
547        // Derive key using SLIP-10 for Ed25519
548        let derived =
549            slipped10::derive_key_from_path(&seed, Curve::Ed25519, &path).map_err(|e| {
550                SignerError::KeyDerivationFailed(format!("SLIP-10 derivation failed: {:?}", e))
551            })?;
552
553        Ok(Self::ed25519_from_bytes(derived.key))
554    }
555
556    /// Generate a new random seed phrase and derive the corresponding secret key.
557    ///
558    /// Returns both the seed phrase (for backup) and the derived secret key.
559    /// Uses 12 words by default and the standard NEAR HD path.
560    ///
561    /// # Example
562    ///
563    /// ```rust
564    /// use near_kit::SecretKey;
565    ///
566    /// let (phrase, secret_key) = SecretKey::generate_with_seed_phrase().unwrap();
567    /// println!("Backup your seed phrase: {}", phrase);
568    /// ```
569    pub fn generate_with_seed_phrase() -> Result<(String, Self), SignerError> {
570        Self::generate_with_seed_phrase_custom(DEFAULT_WORD_COUNT, DEFAULT_HD_PATH, None)
571    }
572
573    /// Generate a new random seed phrase with custom word count.
574    ///
575    /// # Arguments
576    ///
577    /// * `word_count` - Number of words (12, 15, 18, 21, or 24)
578    ///
579    /// # Example
580    ///
581    /// ```rust
582    /// use near_kit::SecretKey;
583    ///
584    /// let (phrase, secret_key) = SecretKey::generate_with_seed_phrase_words(24).unwrap();
585    /// assert_eq!(phrase.split_whitespace().count(), 24);
586    /// ```
587    pub fn generate_with_seed_phrase_words(
588        word_count: usize,
589    ) -> Result<(String, Self), SignerError> {
590        Self::generate_with_seed_phrase_custom(word_count, DEFAULT_HD_PATH, None)
591    }
592
593    /// Generate a new random seed phrase with full customization.
594    ///
595    /// # Arguments
596    ///
597    /// * `word_count` - Number of words (12, 15, 18, 21, or 24)
598    /// * `hd_path` - BIP-32 derivation path
599    /// * `passphrase` - Optional passphrase for additional entropy
600    pub fn generate_with_seed_phrase_custom(
601        word_count: usize,
602        hd_path: impl AsRef<str>,
603        passphrase: Option<&str>,
604    ) -> Result<(String, Self), SignerError> {
605        let phrase = generate_seed_phrase(word_count)?;
606        let secret_key =
607            Self::from_seed_phrase_with_path_and_passphrase(&phrase, hd_path, passphrase)?;
608        Ok((phrase, secret_key))
609    }
610}
611
612impl FromStr for SecretKey {
613    type Err = ParseKeyError;
614
615    fn from_str(s: &str) -> Result<Self, Self::Err> {
616        let (key_type, data_str) = s.split_once(':').ok_or(ParseKeyError::InvalidFormat)?;
617
618        let key_type = match key_type {
619            "ed25519" => KeyType::Ed25519,
620            "secp256k1" => KeyType::Secp256k1,
621            other => return Err(ParseKeyError::UnknownKeyType(other.to_string())),
622        };
623
624        let data = bs58::decode(data_str)
625            .into_vec()
626            .map_err(|e| ParseKeyError::InvalidBase58(e.to_string()))?;
627
628        // For ed25519, the secret key might be 32 bytes (seed) or 64 bytes (expanded)
629        // For secp256k1, it must be 32 bytes
630        let valid_len = match key_type {
631            KeyType::Ed25519 => data.len() == 32 || data.len() == 64,
632            KeyType::Secp256k1 => data.len() == 32,
633        };
634        if !valid_len {
635            return Err(ParseKeyError::InvalidLength {
636                expected: 32,
637                actual: data.len(),
638            });
639        }
640
641        // Take first 32 bytes if 64-byte expanded key
642        let bytes: [u8; 32] = data[..32]
643            .try_into()
644            .map_err(|_| ParseKeyError::InvalidFormat)?;
645
646        match key_type {
647            KeyType::Ed25519 => Ok(Self::Ed25519(bytes)),
648            KeyType::Secp256k1 => {
649                // Validate secp256k1 scalar (non-zero and < curve order)
650                k256::SecretKey::from_bytes((&bytes).into())
651                    .map_err(|_| ParseKeyError::InvalidScalar)?;
652                Ok(Self::Secp256k1(bytes))
653            }
654        }
655    }
656}
657
658impl TryFrom<&str> for SecretKey {
659    type Error = ParseKeyError;
660
661    fn try_from(s: &str) -> Result<Self, Self::Error> {
662        s.parse()
663    }
664}
665
666impl Display for SecretKey {
667    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
668        write!(
669            f,
670            "{}:{}",
671            self.key_type().as_str(),
672            bs58::encode(self.as_bytes()).into_string()
673        )
674    }
675}
676
677impl Debug for SecretKey {
678    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
679        write!(f, "SecretKey({}:***)", self.key_type().as_str())
680    }
681}
682
683/// Cryptographic signature.
684#[derive(Clone, PartialEq, Eq)]
685pub enum Signature {
686    /// Ed25519 signature (64 bytes).
687    Ed25519([u8; 64]),
688    /// Secp256k1 signature (65 bytes: `[r (32) | s (32) | v (1)]`).
689    Secp256k1([u8; 65]),
690}
691
692impl Signature {
693    /// Create an Ed25519 signature from raw 64 bytes.
694    pub fn ed25519_from_bytes(bytes: [u8; 64]) -> Self {
695        Self::Ed25519(bytes)
696    }
697
698    /// Create a Secp256k1 signature from raw 65 bytes.
699    ///
700    /// The format is `[r (32 bytes) | s (32 bytes) | v (1 byte recovery id)]`,
701    /// matching the NEAR protocol's secp256k1 signature format.
702    pub fn secp256k1_from_bytes(bytes: [u8; 65]) -> Self {
703        Self::Secp256k1(bytes)
704    }
705
706    /// Get the key type.
707    pub fn key_type(&self) -> KeyType {
708        match self {
709            Self::Ed25519(_) => KeyType::Ed25519,
710            Self::Secp256k1(_) => KeyType::Secp256k1,
711        }
712    }
713
714    /// Get the raw signature bytes.
715    pub fn as_bytes(&self) -> &[u8] {
716        match self {
717            Self::Ed25519(bytes) => bytes.as_slice(),
718            Self::Secp256k1(bytes) => bytes.as_slice(),
719        }
720    }
721
722    /// Verify this signature against a message and public key.
723    pub fn verify(&self, message: &[u8], public_key: &PublicKey) -> bool {
724        match (self, public_key) {
725            (Self::Ed25519(sig_bytes), PublicKey::Ed25519(pk_bytes)) => {
726                let Ok(verifying_key) = VerifyingKey::from_bytes(pk_bytes) else {
727                    return false;
728                };
729                let signature = ed25519_dalek::Signature::from_bytes(sig_bytes);
730                verifying_key.verify_strict(message, &signature).is_ok()
731            }
732            (Self::Secp256k1(sig_bytes), PublicKey::Secp256k1(pk_bytes)) => {
733                // Reconstruct the 65-byte uncompressed key with 0x04 prefix for verification
734                let mut uncompressed = [0u8; 65];
735                uncompressed[0] = 0x04;
736                uncompressed[1..].copy_from_slice(pk_bytes);
737                let Ok(verifying_key) = k256::ecdsa::VerifyingKey::from_sec1_bytes(&uncompressed)
738                else {
739                    return false;
740                };
741
742                // Validate recovery id byte is in expected range (0..=3)
743                let v = sig_bytes[64];
744                if v > 3 {
745                    return false;
746                }
747
748                let Ok(signature) = k256::ecdsa::Signature::from_bytes((&sig_bytes[..64]).into())
749                else {
750                    return false;
751                };
752
753                // NEAR protocol: verify against SHA-256 hash of the message
754                let hash = sha2::Sha256::digest(message);
755                use k256::ecdsa::signature::hazmat::PrehashVerifier;
756                verifying_key.verify_prehash(&hash, &signature).is_ok()
757            }
758            // Mismatched key types
759            _ => false,
760        }
761    }
762}
763
764impl FromStr for Signature {
765    type Err = ParseKeyError;
766
767    fn from_str(s: &str) -> Result<Self, Self::Err> {
768        let (key_type, data_str) = s.split_once(':').ok_or(ParseKeyError::InvalidFormat)?;
769
770        let key_type = match key_type {
771            "ed25519" => KeyType::Ed25519,
772            "secp256k1" => KeyType::Secp256k1,
773            other => return Err(ParseKeyError::UnknownKeyType(other.to_string())),
774        };
775
776        let data = bs58::decode(data_str)
777            .into_vec()
778            .map_err(|e| ParseKeyError::InvalidBase58(e.to_string()))?;
779
780        if data.len() != key_type.signature_len() {
781            return Err(ParseKeyError::InvalidLength {
782                expected: key_type.signature_len(),
783                actual: data.len(),
784            });
785        }
786
787        match key_type {
788            KeyType::Ed25519 => {
789                let bytes: [u8; 64] = data
790                    .as_slice()
791                    .try_into()
792                    .map_err(|_| ParseKeyError::InvalidFormat)?;
793                Ok(Self::Ed25519(bytes))
794            }
795            KeyType::Secp256k1 => {
796                let bytes: [u8; 65] = data
797                    .as_slice()
798                    .try_into()
799                    .map_err(|_| ParseKeyError::InvalidFormat)?;
800                Ok(Self::Secp256k1(bytes))
801            }
802        }
803    }
804}
805
806impl Display for Signature {
807    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
808        write!(
809            f,
810            "{}:{}",
811            self.key_type().as_str(),
812            bs58::encode(self.as_bytes()).into_string()
813        )
814    }
815}
816
817impl Debug for Signature {
818    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
819        write!(f, "Signature({})", self)
820    }
821}
822
823impl Serialize for Signature {
824    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
825        s.serialize_str(&self.to_string())
826    }
827}
828
829impl<'de> Deserialize<'de> for Signature {
830    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
831        let s: String = serde::Deserialize::deserialize(d)?;
832        s.parse().map_err(serde::de::Error::custom)
833    }
834}
835
836impl BorshSerialize for Signature {
837    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
838        borsh::BorshSerialize::serialize(&(self.key_type() as u8), writer)?;
839        writer.write_all(self.as_bytes())?;
840        Ok(())
841    }
842}
843
844impl BorshDeserialize for Signature {
845    fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
846        let key_type_byte = u8::deserialize_reader(reader)?;
847        let key_type = KeyType::try_from(key_type_byte)
848            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
849
850        match key_type {
851            KeyType::Ed25519 => {
852                let mut bytes = [0u8; 64];
853                reader.read_exact(&mut bytes)?;
854                Ok(Self::Ed25519(bytes))
855            }
856            KeyType::Secp256k1 => {
857                let mut bytes = [0u8; 65];
858                reader.read_exact(&mut bytes)?;
859                Ok(Self::Secp256k1(bytes))
860            }
861        }
862    }
863}
864
865// ============================================================================
866// Seed Phrase Generation
867// ============================================================================
868
869/// Generate a random BIP-39 seed phrase.
870///
871/// # Arguments
872///
873/// * `word_count` - Number of words (12, 15, 18, 21, or 24)
874///
875/// # Example
876///
877/// ```rust
878/// use near_kit::generate_seed_phrase;
879///
880/// let phrase = generate_seed_phrase(12).unwrap();
881/// assert_eq!(phrase.split_whitespace().count(), 12);
882/// ```
883pub fn generate_seed_phrase(word_count: usize) -> Result<String, SignerError> {
884    use rand::RngCore;
885
886    // Word count to entropy bytes: 12->16, 15->20, 18->24, 21->28, 24->32
887    let entropy_bytes = match word_count {
888        12 => 16,
889        15 => 20,
890        18 => 24,
891        21 => 28,
892        24 => 32,
893        _ => {
894            return Err(SignerError::KeyDerivationFailed(format!(
895                "Invalid word count: {}. Must be 12, 15, 18, 21, or 24",
896                word_count
897            )));
898        }
899    };
900
901    let mut entropy = vec![0u8; entropy_bytes];
902    OsRng.fill_bytes(&mut entropy);
903
904    let mnemonic = Mnemonic::from_entropy(&entropy).map_err(|e| {
905        SignerError::KeyDerivationFailed(format!("Failed to generate mnemonic: {}", e))
906    })?;
907
908    Ok(mnemonic.to_string())
909}
910
911// ============================================================================
912// KeyPair
913// ============================================================================
914
915/// A cryptographic key pair (secret key + public key).
916///
917/// This is a convenience type that bundles a [`SecretKey`] with its derived
918/// [`PublicKey`] for situations where you need both (e.g., creating accounts,
919/// adding access keys).
920///
921/// # Example
922///
923/// ```rust
924/// use near_kit::KeyPair;
925///
926/// // Generate a random Ed25519 key pair
927/// let keypair = KeyPair::random();
928/// println!("Public key: {}", keypair.public_key);
929/// println!("Secret key: {}", keypair.secret_key);
930///
931/// // Use with account creation
932/// // near.transaction("new.alice.testnet")
933/// //     .create_account()
934/// //     .add_full_access_key(keypair.public_key)
935/// //     .send()
936/// //     .await?;
937/// ```
938#[derive(Clone)]
939pub struct KeyPair {
940    /// The secret (private) key.
941    pub secret_key: SecretKey,
942    /// The public key derived from the secret key.
943    pub public_key: PublicKey,
944}
945
946impl KeyPair {
947    /// Generate a random Ed25519 key pair.
948    ///
949    /// This is the most common key type for NEAR.
950    ///
951    /// # Example
952    ///
953    /// ```rust
954    /// use near_kit::KeyPair;
955    ///
956    /// let keypair = KeyPair::random();
957    /// ```
958    pub fn random() -> Self {
959        Self::random_ed25519()
960    }
961
962    /// Generate a random Ed25519 key pair.
963    ///
964    /// # Example
965    ///
966    /// ```rust
967    /// use near_kit::KeyPair;
968    ///
969    /// let keypair = KeyPair::random_ed25519();
970    /// assert!(keypair.public_key.to_string().starts_with("ed25519:"));
971    /// ```
972    pub fn random_ed25519() -> Self {
973        let secret_key = SecretKey::generate_ed25519();
974        let public_key = secret_key.public_key();
975        Self {
976            secret_key,
977            public_key,
978        }
979    }
980
981    /// Generate a random Secp256k1 key pair.
982    ///
983    /// # Example
984    ///
985    /// ```rust
986    /// use near_kit::KeyPair;
987    ///
988    /// let keypair = KeyPair::random_secp256k1();
989    /// assert!(keypair.public_key.to_string().starts_with("secp256k1:"));
990    /// ```
991    pub fn random_secp256k1() -> Self {
992        let secret_key = SecretKey::generate_secp256k1();
993        let public_key = secret_key.public_key();
994        Self {
995            secret_key,
996            public_key,
997        }
998    }
999
1000    /// Create a key pair from an existing secret key.
1001    ///
1002    /// # Example
1003    ///
1004    /// ```rust
1005    /// use near_kit::{KeyPair, SecretKey};
1006    ///
1007    /// let secret_key: SecretKey = "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr".parse().unwrap();
1008    /// let keypair = KeyPair::from_secret_key(secret_key);
1009    /// ```
1010    pub fn from_secret_key(secret_key: SecretKey) -> Self {
1011        let public_key = secret_key.public_key();
1012        Self {
1013            secret_key,
1014            public_key,
1015        }
1016    }
1017
1018    /// Create a key pair from a seed phrase using the default NEAR HD path.
1019    ///
1020    /// # Example
1021    ///
1022    /// ```rust
1023    /// use near_kit::KeyPair;
1024    ///
1025    /// let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
1026    /// let keypair = KeyPair::from_seed_phrase(phrase).unwrap();
1027    /// ```
1028    pub fn from_seed_phrase(phrase: impl AsRef<str>) -> Result<Self, SignerError> {
1029        let secret_key = SecretKey::from_seed_phrase(phrase)?;
1030        Ok(Self::from_secret_key(secret_key))
1031    }
1032
1033    /// Generate a new random key pair with a seed phrase for backup.
1034    ///
1035    /// Returns the seed phrase (for backup) and the key pair.
1036    ///
1037    /// # Example
1038    ///
1039    /// ```rust
1040    /// use near_kit::KeyPair;
1041    ///
1042    /// let (phrase, keypair) = KeyPair::random_with_seed_phrase().unwrap();
1043    /// println!("Backup your seed phrase: {}", phrase);
1044    /// println!("Public key: {}", keypair.public_key);
1045    /// ```
1046    pub fn random_with_seed_phrase() -> Result<(String, Self), SignerError> {
1047        let (phrase, secret_key) = SecretKey::generate_with_seed_phrase()?;
1048        Ok((phrase, Self::from_secret_key(secret_key)))
1049    }
1050}
1051
1052impl std::fmt::Debug for KeyPair {
1053    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1054        f.debug_struct("KeyPair")
1055            .field("public_key", &self.public_key)
1056            .field("secret_key", &"***")
1057            .finish()
1058    }
1059}
1060
1061#[cfg(test)]
1062mod tests {
1063    use super::*;
1064
1065    #[test]
1066    fn test_generate_and_sign() {
1067        let secret = SecretKey::generate_ed25519();
1068        let public = secret.public_key();
1069        let message = b"hello world";
1070
1071        let signature = secret.sign(message);
1072        assert!(signature.verify(message, &public));
1073        assert!(!signature.verify(b"wrong message", &public));
1074    }
1075
1076    #[test]
1077    fn test_public_key_roundtrip() {
1078        let secret = SecretKey::generate_ed25519();
1079        let public = secret.public_key();
1080        let s = public.to_string();
1081        let parsed: PublicKey = s.parse().unwrap();
1082        assert_eq!(public, parsed);
1083    }
1084
1085    #[test]
1086    fn test_secret_key_roundtrip() {
1087        let secret = SecretKey::generate_ed25519();
1088        let s = secret.to_string();
1089        let parsed: SecretKey = s.parse().unwrap();
1090        assert_eq!(secret.public_key(), parsed.public_key());
1091    }
1092
1093    #[test]
1094    fn test_ed25519_secret_key_64_byte_expanded_form() {
1095        // Generate a key, get its 32-byte seed, then construct a 64-byte expanded form
1096        // (seed || public_key) which near-cli sometimes stores
1097        let secret = SecretKey::generate_ed25519();
1098        let public = secret.public_key();
1099        let seed_bytes = secret.as_bytes();
1100
1101        // Construct 64-byte expanded key: seed (32) + public key bytes (32)
1102        let mut expanded = Vec::with_capacity(64);
1103        expanded.extend_from_slice(seed_bytes);
1104        expanded.extend_from_slice(public.as_bytes());
1105        let expanded_b58 = bs58::encode(&expanded).into_string();
1106        let expanded_str = format!("ed25519:{}", expanded_b58);
1107
1108        // Parse the 64-byte form — should succeed and produce same public key
1109        let parsed: SecretKey = expanded_str.parse().unwrap();
1110        assert_eq!(parsed.public_key(), public);
1111
1112        // Re-serializing yields the 32-byte seed form
1113        let reserialized = parsed.to_string();
1114        assert_eq!(reserialized, secret.to_string());
1115    }
1116
1117    // ========================================================================
1118    // Seed Phrase Tests
1119    // ========================================================================
1120
1121    #[test]
1122    fn test_generate_seed_phrase_12_words() {
1123        let phrase = generate_seed_phrase(12).unwrap();
1124        assert_eq!(phrase.split_whitespace().count(), 12);
1125    }
1126
1127    #[test]
1128    fn test_generate_seed_phrase_24_words() {
1129        let phrase = generate_seed_phrase(24).unwrap();
1130        assert_eq!(phrase.split_whitespace().count(), 24);
1131    }
1132
1133    #[test]
1134    fn test_generate_seed_phrase_invalid_word_count() {
1135        let result = generate_seed_phrase(13);
1136        assert!(result.is_err());
1137    }
1138
1139    // Valid BIP-39 test vector (from official test vectors)
1140    const TEST_PHRASE: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
1141
1142    #[test]
1143    fn test_from_seed_phrase_known_vector() {
1144        // Test vector: known seed phrase should produce consistent key
1145        let secret_key = SecretKey::from_seed_phrase(TEST_PHRASE).unwrap();
1146
1147        // Same phrase should always produce the same key
1148        let secret_key2 = SecretKey::from_seed_phrase(TEST_PHRASE).unwrap();
1149        assert_eq!(secret_key.public_key(), secret_key2.public_key());
1150    }
1151
1152    #[test]
1153    fn test_from_seed_phrase_whitespace_normalization() {
1154        let phrase1 = TEST_PHRASE;
1155        let phrase2 = "  abandon   abandon  abandon abandon abandon abandon abandon abandon abandon abandon abandon about  ";
1156        let phrase3 = "ABANDON ABANDON ABANDON ABANDON ABANDON ABANDON ABANDON ABANDON ABANDON ABANDON ABANDON ABOUT";
1157
1158        let key1 = SecretKey::from_seed_phrase(phrase1).unwrap();
1159        let key2 = SecretKey::from_seed_phrase(phrase2).unwrap();
1160        let key3 = SecretKey::from_seed_phrase(phrase3).unwrap();
1161
1162        assert_eq!(key1.public_key(), key2.public_key());
1163        assert_eq!(key1.public_key(), key3.public_key());
1164    }
1165
1166    #[test]
1167    fn test_from_seed_phrase_invalid() {
1168        let result = SecretKey::from_seed_phrase("invalid words that are not a mnemonic");
1169        assert!(result.is_err());
1170    }
1171
1172    #[test]
1173    fn test_from_seed_phrase_different_paths() {
1174        let key1 = SecretKey::from_seed_phrase_with_path(TEST_PHRASE, "m/44'/397'/0'").unwrap();
1175        let key2 = SecretKey::from_seed_phrase_with_path(TEST_PHRASE, "m/44'/397'/1'").unwrap();
1176
1177        // Different paths should produce different keys
1178        assert_ne!(key1.public_key(), key2.public_key());
1179    }
1180
1181    #[test]
1182    fn test_from_seed_phrase_with_passphrase() {
1183        let key_no_pass = SecretKey::from_seed_phrase_with_path_and_passphrase(
1184            TEST_PHRASE,
1185            DEFAULT_HD_PATH,
1186            None,
1187        )
1188        .unwrap();
1189
1190        let key_with_pass = SecretKey::from_seed_phrase_with_path_and_passphrase(
1191            TEST_PHRASE,
1192            DEFAULT_HD_PATH,
1193            Some("my-password"),
1194        )
1195        .unwrap();
1196
1197        // Passphrase should produce different key
1198        assert_ne!(key_no_pass.public_key(), key_with_pass.public_key());
1199    }
1200
1201    #[test]
1202    fn test_generate_with_seed_phrase() {
1203        let (phrase, secret_key) = SecretKey::generate_with_seed_phrase().unwrap();
1204
1205        // Phrase should be 12 words
1206        assert_eq!(phrase.split_whitespace().count(), 12);
1207
1208        // Re-deriving from the phrase should produce the same key
1209        let derived = SecretKey::from_seed_phrase(&phrase).unwrap();
1210        assert_eq!(secret_key.public_key(), derived.public_key());
1211    }
1212
1213    #[test]
1214    fn test_generate_with_seed_phrase_24_words() {
1215        let (phrase, secret_key) = SecretKey::generate_with_seed_phrase_words(24).unwrap();
1216
1217        assert_eq!(phrase.split_whitespace().count(), 24);
1218
1219        let derived = SecretKey::from_seed_phrase(&phrase).unwrap();
1220        assert_eq!(secret_key.public_key(), derived.public_key());
1221    }
1222
1223    #[test]
1224    fn test_seed_phrase_key_can_sign() {
1225        let secret_key = SecretKey::from_seed_phrase(TEST_PHRASE).unwrap();
1226
1227        let message = b"test message";
1228        let signature = secret_key.sign(message);
1229        let public_key = secret_key.public_key();
1230
1231        assert!(signature.verify(message, &public_key));
1232    }
1233
1234    // ========================================================================
1235    // Curve Point Validation Tests
1236    // ========================================================================
1237
1238    #[test]
1239    fn test_secp256k1_invalid_curve_point_rejected() {
1240        // This is the invalid key from the NEAR SDK docs that was identified as not being
1241        // on the secp256k1 curve. See: https://github.com/near/near-sdk-rs/pull/1469
1242        let invalid_key = "secp256k1:qMoRgcoXai4mBPsdbHi1wfyxF9TdbPCF4qSDQTRP3TfescSRoUdSx6nmeQoN3aiwGzwMyGXAb1gUjBTv5AY8DXj";
1243        let result: Result<PublicKey, _> = invalid_key.parse();
1244        assert!(result.is_err());
1245        assert!(matches!(
1246            result.unwrap_err(),
1247            ParseKeyError::InvalidCurvePoint | ParseKeyError::InvalidLength { .. }
1248        ));
1249    }
1250
1251    #[test]
1252    fn test_secp256k1_valid_curve_point_accepted() {
1253        // Valid secp256k1 key from near-sdk-js (verified to be on the curve)
1254        // This key is 64 bytes (uncompressed without prefix) — matches our new format
1255        let valid_key = "secp256k1:5r22SrjrDvgY3wdQsnjgxkeAbU1VcM71FYvALEQWihjM3Xk4Be1CpETTqFccChQr4iJwDroSDVmgaWZv2AcXvYeL";
1256        let result: Result<PublicKey, _> = valid_key.parse();
1257        // This key is now parseable because we expect 64-byte uncompressed format
1258        assert!(result.is_ok());
1259    }
1260
1261    #[test]
1262    fn test_ed25519_valid_key_accepted() {
1263        // Valid ed25519 public key
1264        let valid_key = "ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp";
1265        let result: Result<PublicKey, _> = valid_key.parse();
1266        assert!(result.is_ok());
1267    }
1268
1269    #[test]
1270    fn test_ed25519_invalid_curve_point_rejected() {
1271        // The high bit of the last byte being set with an invalid x-coordinate recovery
1272        // should produce an invalid point. Specifically, a y-coordinate that when
1273        // the x is computed results in a non-square (no valid x exists).
1274        // This specific byte sequence has been verified to fail ed25519 decompression.
1275        //
1276        // Note: ed25519_dalek may accept many byte patterns as valid curve points.
1277        // Ed25519 point decompression is very permissive - most 32-byte sequences
1278        // decode to valid points.
1279        let invalid_bytes = [
1280            0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1281            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1282            0xFF, 0xFF, 0xFF, 0x7F,
1283        ];
1284        let encoded = bs58::encode(&invalid_bytes).into_string();
1285        let invalid_key = format!("ed25519:{}", encoded);
1286        let result: Result<PublicKey, _> = invalid_key.parse();
1287        if let Err(err) = result {
1288            assert!(matches!(err, ParseKeyError::InvalidCurvePoint));
1289        } else {
1290            // If ed25519_dalek accepts this, we should skip this test case
1291            eprintln!(
1292                "Note: ed25519 point decompression accepted test bytes - validation may be too lenient"
1293            );
1294        }
1295    }
1296
1297    #[test]
1298    fn test_borsh_deserialize_validates_curve_point() {
1299        use borsh::BorshDeserialize;
1300
1301        // Test with secp256k1 since ed25519 validation is very lenient
1302        // Use invalid secp256k1 bytes (all zeros is definitely not on the curve)
1303        let mut invalid_bytes = vec![1u8]; // KeyType::Secp256k1
1304        invalid_bytes.extend_from_slice(&[0u8; 64]); // Invalid curve point (64 bytes now)
1305
1306        let result = PublicKey::try_from_slice(&invalid_bytes);
1307        assert!(result.is_err());
1308    }
1309
1310    #[test]
1311    fn test_signature_from_str_roundtrip() {
1312        let sig_str = "ed25519:3s1dvMqNDCByoMnDnkhB4GPjTSXCRt4nt3Af5n1RX8W7aJ2FC6MfRf5BNXZ52EBifNJnNVBsGvke6GRYuaEYJXt5";
1313        let sig: Signature = sig_str.parse().unwrap();
1314        assert_eq!(sig.key_type(), KeyType::Ed25519);
1315        assert_eq!(sig.as_bytes().len(), 64);
1316        assert_eq!(sig.to_string(), sig_str);
1317    }
1318
1319    #[test]
1320    fn test_signature_from_str_invalid_format() {
1321        assert!("no_colon".parse::<Signature>().is_err());
1322        assert!("unknown:abc".parse::<Signature>().is_err());
1323        assert!("ed25519:invalid!!!".parse::<Signature>().is_err());
1324        assert!("ed25519:AAAA".parse::<Signature>().is_err()); // too short
1325    }
1326
1327    #[test]
1328    fn test_signature_serde_roundtrip() {
1329        let sig_str = "ed25519:3s1dvMqNDCByoMnDnkhB4GPjTSXCRt4nt3Af5n1RX8W7aJ2FC6MfRf5BNXZ52EBifNJnNVBsGvke6GRYuaEYJXt5";
1330        let sig: Signature = sig_str.parse().unwrap();
1331        let json = serde_json::to_value(&sig).unwrap();
1332        assert_eq!(json.as_str().unwrap(), sig_str);
1333        let parsed: Signature = serde_json::from_value(json).unwrap();
1334        assert_eq!(sig, parsed);
1335    }
1336
1337    // ========================================================================
1338    // Secp256k1 Tests
1339    // ========================================================================
1340
1341    #[test]
1342    fn test_secp256k1_generate_and_sign_verify() {
1343        let secret = SecretKey::generate_secp256k1();
1344        let public = secret.public_key();
1345        let message = b"hello world";
1346
1347        assert_eq!(secret.key_type(), KeyType::Secp256k1);
1348        assert_eq!(public.key_type(), KeyType::Secp256k1);
1349
1350        let signature = secret.sign(message);
1351        assert_eq!(signature.key_type(), KeyType::Secp256k1);
1352        assert_eq!(signature.as_bytes().len(), 65);
1353
1354        assert!(signature.verify(message, &public));
1355        assert!(!signature.verify(b"wrong message", &public));
1356    }
1357
1358    #[test]
1359    fn test_secp256k1_public_key_is_64_bytes() {
1360        let secret = SecretKey::generate_secp256k1();
1361        let public = secret.public_key();
1362
1363        // Public key should be 64 bytes (uncompressed without prefix)
1364        let pk_bytes = public.as_secp256k1_bytes().unwrap();
1365        assert_eq!(pk_bytes.len(), 64);
1366    }
1367
1368    #[test]
1369    fn test_secp256k1_secret_key_to_public_key_derivation() {
1370        // Deterministic: same secret key bytes should always produce the same public key
1371        let bytes = [42u8; 32];
1372        let sk1 = SecretKey::secp256k1_from_bytes(bytes).unwrap();
1373        let sk2 = SecretKey::secp256k1_from_bytes(bytes).unwrap();
1374        assert_eq!(sk1.public_key(), sk2.public_key());
1375
1376        // Different secret keys produce different public keys
1377        let bytes2 = [43u8; 32];
1378        let sk3 = SecretKey::secp256k1_from_bytes(bytes2).unwrap();
1379        assert_ne!(sk1.public_key(), sk3.public_key());
1380    }
1381
1382    #[test]
1383    fn test_secp256k1_public_key_string_roundtrip() {
1384        let secret = SecretKey::generate_secp256k1();
1385        let public = secret.public_key();
1386        let s = public.to_string();
1387        assert!(s.starts_with("secp256k1:"));
1388        let parsed: PublicKey = s.parse().unwrap();
1389        assert_eq!(public, parsed);
1390    }
1391
1392    #[test]
1393    fn test_secp256k1_secret_key_string_roundtrip() {
1394        let secret = SecretKey::generate_secp256k1();
1395        let s = secret.to_string();
1396        assert!(s.starts_with("secp256k1:"));
1397        let parsed: SecretKey = s.parse().unwrap();
1398        assert_eq!(secret.public_key(), parsed.public_key());
1399    }
1400
1401    #[test]
1402    fn test_secp256k1_keypair_random() {
1403        let keypair = KeyPair::random_secp256k1();
1404        assert_eq!(keypair.public_key.key_type(), KeyType::Secp256k1);
1405        assert_eq!(keypair.secret_key.key_type(), KeyType::Secp256k1);
1406        assert!(keypair.public_key.to_string().starts_with("secp256k1:"));
1407
1408        // Sign/verify through keypair
1409        let message = b"keypair test";
1410        let signature = keypair.secret_key.sign(message);
1411        assert!(signature.verify(message, &keypair.public_key));
1412    }
1413
1414    #[test]
1415    fn test_secp256k1_cross_type_verify_fails() {
1416        // Ed25519 signature should not verify against secp256k1 key
1417        let ed_secret = SecretKey::generate_ed25519();
1418        let secp_secret = SecretKey::generate_secp256k1();
1419
1420        let message = b"cross type test";
1421        let ed_sig = ed_secret.sign(message);
1422        let secp_sig = secp_secret.sign(message);
1423
1424        assert!(!ed_sig.verify(message, &secp_secret.public_key()));
1425        assert!(!secp_sig.verify(message, &ed_secret.public_key()));
1426    }
1427
1428    #[test]
1429    fn test_secp256k1_invalid_scalar_rejected() {
1430        // Zero scalar is invalid for secp256k1
1431        let zero_bytes = [0u8; 32];
1432        let result = SecretKey::secp256k1_from_bytes(zero_bytes);
1433        assert!(result.is_err());
1434        assert!(matches!(result.unwrap_err(), ParseKeyError::InvalidScalar));
1435    }
1436
1437    #[test]
1438    fn test_secp256k1_invalid_scalar_rejected_from_str() {
1439        // Construct a secp256k1 secret key string with zero bytes (invalid scalar)
1440        let zero_bytes = [0u8; 32];
1441        let encoded = bs58::encode(&zero_bytes).into_string();
1442        let key_str = format!("secp256k1:{}", encoded);
1443        let result: Result<SecretKey, _> = key_str.parse();
1444        assert!(result.is_err());
1445        assert!(matches!(result.unwrap_err(), ParseKeyError::InvalidScalar));
1446    }
1447
1448    #[test]
1449    fn test_secp256k1_invalid_recovery_id_rejected() {
1450        let secret = SecretKey::generate_secp256k1();
1451        let public = secret.public_key();
1452        let message = b"test message";
1453        let signature = secret.sign(message);
1454
1455        // Create a tampered signature with invalid recovery id
1456        if let Signature::Secp256k1(mut sig_bytes) = signature.clone() {
1457            sig_bytes[64] = 4; // valid range is 0..=3
1458            let tampered = Signature::Secp256k1(sig_bytes);
1459            assert!(!tampered.verify(message, &public));
1460
1461            sig_bytes[64] = 255;
1462            let tampered = Signature::Secp256k1(sig_bytes);
1463            assert!(!tampered.verify(message, &public));
1464        } else {
1465            panic!("Expected Secp256k1 signature");
1466        }
1467    }
1468
1469    #[test]
1470    fn test_secp256k1_from_compressed() {
1471        let secret = SecretKey::generate_secp256k1();
1472        let public = secret.public_key();
1473
1474        // Get the uncompressed bytes and create a compressed version
1475        let pk_bytes = public.as_secp256k1_bytes().unwrap();
1476        let mut uncompressed = [0u8; 65];
1477        uncompressed[0] = 0x04;
1478        uncompressed[1..].copy_from_slice(pk_bytes);
1479        let encoded =
1480            k256::EncodedPoint::from_bytes(uncompressed.as_ref()).expect("valid encoded point");
1481        let point = k256::AffinePoint::from_encoded_point(&encoded).expect("valid point on curve");
1482        let compressed = point.to_encoded_point(true);
1483        let compressed_bytes: [u8; 33] = compressed
1484            .as_bytes()
1485            .try_into()
1486            .expect("compressed should be 33 bytes");
1487
1488        // Reconstruct from compressed form
1489        let public2 = PublicKey::secp256k1_from_compressed(compressed_bytes);
1490        assert_eq!(public, public2);
1491    }
1492
1493    #[test]
1494    fn test_ed25519_borsh_roundtrip() {
1495        use borsh::BorshDeserialize;
1496
1497        let secret = SecretKey::generate_ed25519();
1498        let public = secret.public_key();
1499        let serialized = borsh::to_vec(&public).unwrap();
1500        // Ed25519: 1 byte key type + 32 bytes key data
1501        assert_eq!(serialized.len(), 33);
1502        assert_eq!(serialized[0], 0); // Ed25519
1503        let deserialized = PublicKey::try_from_slice(&serialized).unwrap();
1504        assert_eq!(public, deserialized);
1505    }
1506
1507    #[test]
1508    fn test_secp256k1_borsh_roundtrip() {
1509        use borsh::BorshDeserialize;
1510
1511        let secret = SecretKey::generate_secp256k1();
1512        let public = secret.public_key();
1513        let serialized = borsh::to_vec(&public).unwrap();
1514        // Secp256k1: 1 byte key type + 64 bytes key data
1515        assert_eq!(serialized.len(), 65);
1516        assert_eq!(serialized[0], 1); // Secp256k1
1517        let deserialized = PublicKey::try_from_slice(&serialized).unwrap();
1518        assert_eq!(public, deserialized);
1519    }
1520
1521    #[test]
1522    fn test_signature_borsh_roundtrip() {
1523        use borsh::BorshDeserialize;
1524
1525        let secret = SecretKey::generate_ed25519();
1526        let sig = secret.sign(b"test");
1527        let serialized = borsh::to_vec(&sig).unwrap();
1528        assert_eq!(serialized.len(), 65); // 1 + 64
1529        let deserialized = Signature::try_from_slice(&serialized).unwrap();
1530        assert_eq!(sig, deserialized);
1531    }
1532
1533    #[test]
1534    fn test_enum_variants_match_expected_types() {
1535        let ed_secret = SecretKey::generate_ed25519();
1536        let ed_public = ed_secret.public_key();
1537
1538        assert!(matches!(ed_public, PublicKey::Ed25519(_)));
1539        assert!(matches!(ed_secret, SecretKey::Ed25519(_)));
1540
1541        let secp_secret = SecretKey::generate_secp256k1();
1542        let secp_public = secp_secret.public_key();
1543
1544        assert!(matches!(secp_public, PublicKey::Secp256k1(_)));
1545        assert!(matches!(secp_secret, SecretKey::Secp256k1(_)));
1546    }
1547}