Skip to main content

bittensor_wallet/
keypair.rs

1use base64::{engine::general_purpose, Engine as _};
2use bip39::Mnemonic;
3use schnorrkel::{PublicKey, SecretKey};
4use scrypt::{scrypt, Params as ScryptParams};
5use serde::{Deserialize, Serialize};
6use sodiumoxide::crypto::sealedbox;
7use sodiumoxide::crypto::secretbox;
8use sodiumoxide::crypto::secretbox::{Key, Nonce};
9use sodiumoxide::crypto::sign::ed25519 as sign_ed25519;
10use sp_core::crypto::{AccountId32, Ss58Codec};
11use sp_core::{ed25519, sr25519, ByteArray, Pair};
12use std::fmt;
13
14use crate::constants::{CRYPTO_ED25519, CRYPTO_SR25519};
15
16const PKCS8_HEADER: &[u8] = &[48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32];
17const PKCS8_DIVIDER: &[u8] = &[161, 35, 3, 33, 0];
18const SEC_LENGTH: usize = 64;
19const PUB_LENGTH: usize = 32;
20
21#[derive(Clone)]
22#[allow(clippy::large_enum_variant)]
23pub(crate) enum PairInner {
24    Sr25519(sr25519::Pair),
25    Ed25519(ed25519::Pair),
26}
27
28impl PairInner {
29    pub fn public_bytes(&self) -> [u8; 32] {
30        match self {
31            Self::Sr25519(p) => p.public().0,
32            Self::Ed25519(p) => p.public().0,
33        }
34    }
35
36    pub fn ss58_address(&self) -> String {
37        AccountId32::from(self.public_bytes()).to_ss58check()
38    }
39
40    pub fn sign(&self, data: &[u8]) -> Vec<u8> {
41        match self {
42            Self::Sr25519(p) => p.sign(data).0.to_vec(),
43            Self::Ed25519(p) => p.sign(data).0.to_vec(),
44        }
45    }
46
47    pub fn to_raw_vec(&self) -> Vec<u8> {
48        match self {
49            Self::Sr25519(p) => p.to_raw_vec(),
50            Self::Ed25519(p) => p.to_raw_vec(),
51        }
52    }
53
54    pub fn crypto_type(&self) -> u8 {
55        match self {
56            Self::Sr25519(_) => CRYPTO_SR25519,
57            Self::Ed25519(_) => CRYPTO_ED25519,
58        }
59    }
60}
61
62fn verify_signature(
63    crypto_type: u8,
64    public_key_bytes: &[u8; 32],
65    data: &[u8],
66    signature_bytes: &[u8],
67) -> Result<bool, String> {
68    match crypto_type {
69        CRYPTO_SR25519 => {
70            let public = sr25519::Public::from_raw(*public_key_bytes);
71            let signature = sr25519::Signature::from_slice(signature_bytes)
72                .map_err(|_| "Invalid SR25519 signature.".to_string())?;
73            Ok(sr25519::Pair::verify(&signature, data, &public))
74        }
75        CRYPTO_ED25519 => {
76            let public = ed25519::Public::from_raw(*public_key_bytes);
77            let signature = ed25519::Signature::from_slice(signature_bytes)
78                .map_err(|_| "Invalid ED25519 signature.".to_string())?;
79            Ok(ed25519::Pair::verify(&signature, data, &public))
80        }
81        _ => Err(format!("Unsupported crypto type: {}.", crypto_type)),
82    }
83}
84
85#[derive(Serialize, Deserialize, Debug)]
86struct Encoding {
87    content: Vec<String>,
88    #[serde(rename = "type")]
89    enc_type: Vec<String>,
90    version: String,
91}
92
93#[derive(Serialize, Deserialize, Debug)]
94struct Meta {
95    #[serde(rename = "genesisHash")]
96    genesis_hash: Option<String>,
97    name: String,
98    #[serde(rename = "whenCreated")]
99    when_created: Option<u64>,
100}
101
102#[derive(Serialize, Deserialize, Debug)]
103struct JsonStructure {
104    encoded: String,
105    encoding: Encoding,
106    address: String,
107    meta: Meta,
108}
109
110#[derive(Clone)]
111pub struct Keypair {
112    ss58_address: Option<String>,
113    public_key: Option<String>,
114    private_key: Option<String>,
115    ss58_format: u8,
116    seed_hex: Option<Vec<u8>>,
117    crypto_type: u8,
118    mnemonic: Option<String>,
119    pair: Option<PairInner>,
120}
121
122impl Keypair {
123    /// Returns whether this keypair has an active key pair.
124    pub fn pair_is_some(&self) -> bool {
125        self.pair.is_some()
126    }
127
128    /// Used by the Python binding setter (`keypair.crypto_type = ...`) for pub-only keypairs.
129    #[allow(dead_code)]
130    pub(crate) fn set_crypto_type_field(&mut self, crypto_type: u8) {
131        self.crypto_type = crypto_type;
132    }
133
134    /// Creates a new Keypair instance.
135    ///
136    /// ```text
137    ///     Arguments:
138    ///         ss58_address (Option<String>): Optional SS58-formatted address.
139    ///         public_key (Option<String>): Optional public key as hex string.
140    ///         private_key (Option<String>): Optional private key as hex string.
141    ///         ss58_format (u8): The SS58 format number for address encoding.
142    ///         seed_hex (Option<Vec<u8>>): Optional seed bytes.
143    ///         crypto_type (u8): The cryptographic algorithm type. 0 for ED25519, 1 for SR25519.
144    ///     Returns:
145    ///         keypair (Keypair): A new Keypair instance.
146    /// ```
147    pub fn new(
148        ss58_address: Option<String>,
149        public_key: Option<String>,
150        private_key: Option<String>,
151        ss58_format: u8,
152        seed_hex: Option<Vec<u8>>,
153        crypto_type: u8,
154    ) -> Result<Self, String> {
155        match crypto_type {
156            CRYPTO_SR25519 | CRYPTO_ED25519 => {}
157            _ => return Err(format!("Unsupported crypto type: {}.", crypto_type)),
158        }
159
160        let mut ss58_address_res = ss58_address.clone();
161        let mut public_key_res = public_key;
162
163        if let Some(private_key_str) = &private_key {
164            let private_key_bytes =
165                hex::decode(private_key_str.trim_start_matches("0x")).expect("");
166
167            let expected_len = match crypto_type {
168                CRYPTO_SR25519 => 64,
169                CRYPTO_ED25519 => 32,
170                _ => unreachable!(),
171            };
172            if private_key_bytes.len() != expected_len {
173                return Err(format!("Secret key should be {} bytes long.", expected_len));
174            }
175        }
176
177        if let Some(public_key_str) = &public_key_res {
178            let public_key_vec = hex::decode(public_key_str.trim_start_matches("0x"))
179                .map_err(|e| format!("Invalid `public_key` string: {}", e))?;
180
181            let public_key_array: [u8; 32] = public_key_vec
182                .try_into()
183                .map_err(|_| "Public key must be 32 bytes long.")?;
184
185            let account_id = AccountId32::from(public_key_array);
186            ss58_address_res = Some(account_id.to_ss58check());
187        }
188
189        if let Some(ss58_address_str) = ss58_address.clone() {
190            let (account_id, _) = AccountId32::from_ss58check_with_version(&ss58_address_str)
191                .map_err(|e| format!("Invalid SS58 address: {:?}", e))?;
192            public_key_res = Some(hex::encode(<AccountId32 as AsRef<[u8; 32]>>::as_ref(
193                &account_id,
194            )));
195        }
196
197        let kp = Keypair {
198            ss58_address: ss58_address_res,
199            public_key: public_key_res,
200            private_key,
201            ss58_format,
202            seed_hex,
203            crypto_type,
204            mnemonic: None,
205            pair: None,
206        };
207
208        if kp.public_key.is_none() {
209            return Err("No SS58 formatted address or public key provided.".to_string());
210        }
211        Ok(kp)
212    }
213
214    fn __str__(&self) -> Result<String, String> {
215        match self.ss58_address() {
216            Some(address) => Ok(format!("<Keypair (address={})>", address)),
217            None => Ok("<Keypair (address=None)>".to_string()),
218        }
219    }
220
221    fn __repr__(&self) -> Result<String, String> {
222        self.__str__()
223    }
224
225    /// Generates a new mnemonic phrase.
226    ///
227    /// ```text
228    ///     Arguments:
229    ///         n_words (usize): The number of words in the mnemonic (e.g., 12, 15, 18, 21, 24).
230    ///     Returns:
231    ///         mnemonic (String): The generated mnemonic phrase.
232    /// ```
233    pub fn generate_mnemonic(n_words: usize) -> Result<String, String> {
234        let mnemonic = Mnemonic::generate(n_words).map_err(|e| e.to_string())?;
235        Ok(mnemonic.to_string())
236    }
237
238    /// Creates a Keypair from a mnemonic phrase.
239    ///
240    /// ```text
241    ///     Arguments:
242    ///         mnemonic (str): The mnemonic phrase to create the keypair from.
243    ///         crypto_type (u8): The cryptographic algorithm type. 0 for ED25519, 1 for SR25519.
244    ///     Returns:
245    ///         keypair (Keypair): The Keypair created from the mnemonic.
246    /// ```
247    pub fn create_from_mnemonic(mnemonic: &str, crypto_type: u8) -> Result<Self, String> {
248        let (pair_inner, seed_vec) = match crypto_type {
249            CRYPTO_SR25519 => {
250                let (pair, seed) =
251                    sr25519::Pair::from_phrase(mnemonic, None).map_err(|e| e.to_string())?;
252                (PairInner::Sr25519(pair), seed.to_vec())
253            }
254            CRYPTO_ED25519 => {
255                let (pair, seed) =
256                    ed25519::Pair::from_phrase(mnemonic, None).map_err(|e| e.to_string())?;
257                (PairInner::Ed25519(pair), seed.to_vec())
258            }
259            _ => return Err(format!("Unsupported crypto type: {}.", crypto_type)),
260        };
261
262        Ok(Keypair {
263            mnemonic: Some(mnemonic.to_string()),
264            seed_hex: Some(seed_vec),
265            pair: Some(pair_inner),
266            crypto_type,
267            ..Default::default()
268        })
269    }
270
271    /// Creates a Keypair from a seed.
272    ///
273    /// ```text
274    ///     Arguments:
275    ///         seed (Vec<u8>): The seed bytes to create the keypair from.
276    ///         crypto_type (u8): The cryptographic algorithm type. 0 for ED25519, 1 for SR25519.
277    ///     Returns:
278    ///         keypair (Keypair): The Keypair created from the seed.
279    /// ```
280    pub fn create_from_seed(seed: Vec<u8>, crypto_type: u8) -> Result<Self, String> {
281        let pair_inner = match crypto_type {
282            CRYPTO_SR25519 => {
283                let pair = sr25519::Pair::from_seed_slice(&seed)
284                    .map_err(|e| format!("Failed to create SR25519 pair from seed: {}", e))?;
285                PairInner::Sr25519(pair)
286            }
287            CRYPTO_ED25519 => {
288                let pair = ed25519::Pair::from_seed_slice(&seed)
289                    .map_err(|e| format!("Failed to create ED25519 pair from seed: {}", e))?;
290                PairInner::Ed25519(pair)
291            }
292            _ => return Err(format!("Unsupported crypto type: {}.", crypto_type)),
293        };
294
295        Ok(Keypair {
296            seed_hex: Some(seed),
297            pair: Some(pair_inner),
298            crypto_type,
299            ..Default::default()
300        })
301    }
302
303    /// Creates a Keypair from a private key.
304    ///
305    /// ```text
306    ///     Arguments:
307    ///         private_key (str): The private key as hex string to create the keypair from.
308    ///         crypto_type (u8): The cryptographic algorithm type. 0 for ED25519, 1 for SR25519.
309    ///     Returns:
310    ///         keypair (Keypair): The Keypair created from the private key.
311    /// ```
312    pub fn create_from_private_key(private_key: &str, crypto_type: u8) -> Result<Self, String> {
313        let private_key_vec = hex::decode(private_key.trim_start_matches("0x"))
314            .map_err(|e| format!("Invalid `private_key` string: {}", e))?;
315
316        let pair_inner = match crypto_type {
317            CRYPTO_SR25519 => {
318                let pair = sr25519::Pair::from_seed_slice(&private_key_vec).map_err(|e| {
319                    format!("Failed to create SR25519 pair from private key: {}", e)
320                })?;
321                PairInner::Sr25519(pair)
322            }
323            CRYPTO_ED25519 => {
324                let pair = ed25519::Pair::from_seed_slice(&private_key_vec).map_err(|e| {
325                    format!("Failed to create ED25519 pair from private key: {}", e)
326                })?;
327                PairInner::Ed25519(pair)
328            }
329            _ => return Err(format!("Unsupported crypto type: {}.", crypto_type)),
330        };
331
332        Ok(Keypair {
333            pair: Some(pair_inner),
334            crypto_type,
335            ..Default::default()
336        })
337    }
338
339    /// Creates a Keypair from encrypted JSON data.
340    ///
341    /// ```text
342    ///     Arguments:
343    ///         json_data (str): The encrypted JSON data containing the keypair.
344    ///         passphrase (str): The passphrase to decrypt the JSON data.
345    ///     Returns:
346    ///         keypair (Keypair): The Keypair created from the encrypted JSON.
347    /// ```
348    pub fn create_from_encrypted_json(
349        json_data: &str,
350        passphrase: &str,
351    ) -> Result<Keypair, String> {
352        /// rust version of python .rjust
353        fn pad_right(mut data: Vec<u8>, total_len: usize, pad_byte: u8) -> Vec<u8> {
354            if data.len() < total_len {
355                let pad_len = total_len - data.len();
356                data.extend(vec![pad_byte; pad_len]);
357            }
358            data
359        }
360
361        /// Builds an SR25519 (secret, public) byte tuple. The secret is converted
362        /// from raw Ed25519 form; the public key is already SR25519 (Ristretto)
363        /// and is passed through as-is.
364        ///
365        /// ```text
366        ///     Arguments:
367        ///         secret (&[u8]): The Ed25519 private key bytes to convert to SR25519.
368        ///         pubkey (&[u8]): The SR25519 (Ristretto-compressed) public key bytes.
369        ///     Returns:
370        ///         pair ([u8; 64], [u8; 32]): A tuple of the 64-byte secret and 32-byte public key.
371        ///     Panics:
372        ///         If either input cannot be parsed by ``schnorrkel``.
373        /// ```
374        pub fn pair_from_ed25519_secret_key(secret: &[u8], pubkey: &[u8]) -> ([u8; 64], [u8; 32]) {
375            match (
376                SecretKey::from_ed25519_bytes(secret),
377                PublicKey::from_bytes(pubkey),
378            ) {
379                (Ok(s), Ok(k)) => (s.to_bytes(), k.to_bytes()),
380                _ => panic!("Invalid secret or pubkey provided."),
381            }
382        }
383
384        /// Decodes a PKCS8-encoded key pair from the provided byte slice.
385        /// Returns a tuple containing the private key and public key as vectors of bytes.
386        fn decode_pkcs8(
387            ciphertext: &[u8],
388        ) -> Result<([u8; SEC_LENGTH], [u8; PUB_LENGTH]), &'static str> {
389            let mut current_offset = 0;
390            let header = &ciphertext[current_offset..current_offset + PKCS8_HEADER.len()];
391            if header != PKCS8_HEADER {
392                return Err("Invalid Pkcs8 header found in body");
393            }
394            current_offset += PKCS8_HEADER.len();
395            let secret_key = &ciphertext[current_offset..current_offset + SEC_LENGTH];
396            let mut secret_key_array = [0u8; SEC_LENGTH];
397            secret_key_array.copy_from_slice(secret_key);
398            current_offset += SEC_LENGTH;
399            let divider = &ciphertext[current_offset..current_offset + PKCS8_DIVIDER.len()];
400            if divider != PKCS8_DIVIDER {
401                return Err("Invalid Pkcs8 divider found in body");
402            }
403            current_offset += PKCS8_DIVIDER.len();
404            let public_key = &ciphertext[current_offset..current_offset + PUB_LENGTH];
405            let mut public_key_array = [0u8; PUB_LENGTH];
406            public_key_array.copy_from_slice(public_key);
407            Ok((secret_key_array, public_key_array))
408        }
409
410        let json_data: JsonStructure = serde_json::from_str(json_data).unwrap();
411
412        if json_data.encoding.version != "3" {
413            return Err("Unsupported JSON format".to_string());
414        }
415
416        let mut encrypted = general_purpose::STANDARD
417            .decode(json_data.encoded)
418            .map_err(|e| e.to_string())?;
419
420        let password = if json_data.encoding.enc_type.contains(&"scrypt".to_string()) {
421            let salt = &encrypted[0..32];
422            let n = u32::from_le_bytes(encrypted[32..36].try_into().unwrap());
423            let p = u32::from_le_bytes(encrypted[36..40].try_into().unwrap());
424            let r = u32::from_le_bytes(encrypted[40..44].try_into().unwrap());
425            let log_n: u8 = n.ilog2() as u8;
426
427            let params = ScryptParams::new(log_n, r, p, 32).map_err(|e| e.to_string())?;
428            let mut derived_key = vec![0u8; 32];
429            scrypt(passphrase.as_bytes(), salt, &params, &mut derived_key)
430                .map_err(|e| e.to_string())?;
431            encrypted = encrypted[44..].to_vec();
432            derived_key
433        } else {
434            let mut derived_key = passphrase.as_bytes().to_vec();
435            derived_key = pad_right(derived_key, 32, 0x00);
436            derived_key
437        };
438
439        let nonce_bytes = &encrypted[0..24];
440        let nonce = Nonce::from_slice(nonce_bytes)
441            .ok_or("Invalid nonce length")
442            .map_err(|e| e.to_string())?;
443        let message = &encrypted[24..];
444
445        let key = Key::from_slice(&password).ok_or("Invalid key length")?;
446        let decrypted_data = secretbox::open(message, &nonce, &key)
447            .map_err(|_| "Failed to decrypt data".to_string())?;
448        let (private_key, public_key) =
449            decode_pkcs8(&decrypted_data).map_err(|_| "Failed to decode PKCS8 data".to_string())?;
450
451        let (secret, converted_public_key) =
452            pair_from_ed25519_secret_key(&private_key[..], &public_key[..]);
453
454        if json_data.encoding.content.iter().any(|c| c == "sr25519") {
455            assert_eq!(public_key, converted_public_key);
456            Keypair::create_from_private_key(&hex::encode(secret), CRYPTO_SR25519)
457        } else if json_data.encoding.content.iter().any(|c| c == "ed25519") {
458            let seed = &private_key[..32];
459            let pair = ed25519::Pair::from_seed_slice(seed)
460                .map_err(|e| format!("Failed to create ED25519 pair: {}", e))?;
461            if pair.public().0 != public_key {
462                return Err("ED25519 public key mismatch in JSON.".to_string());
463            }
464            Ok(Keypair {
465                pair: Some(PairInner::Ed25519(pair)),
466                crypto_type: CRYPTO_ED25519,
467                ..Default::default()
468            })
469        } else {
470            Err("Unsupported keypair type.".to_string())
471        }
472    }
473
474    /// Creates a Keypair from a URI string.
475    ///
476    /// ```text
477    ///     Arguments:
478    ///         uri (str): The URI string to create the keypair from.
479    ///         crypto_type (u8): The cryptographic algorithm type. 0 for ED25519, 1 for SR25519.
480    ///     Returns:
481    ///         keypair (Keypair): The Keypair created from the URI.
482    /// ```
483    pub fn create_from_uri(uri: &str, crypto_type: u8) -> Result<Self, String> {
484        let pair_inner = match crypto_type {
485            CRYPTO_SR25519 => {
486                let pair = sr25519::Pair::from_string(uri, None).map_err(|e| e.to_string())?;
487                PairInner::Sr25519(pair)
488            }
489            CRYPTO_ED25519 => {
490                let pair = ed25519::Pair::from_string(uri, None).map_err(|e| e.to_string())?;
491                PairInner::Ed25519(pair)
492            }
493            _ => return Err(format!("Unsupported crypto type: {}.", crypto_type)),
494        };
495
496        Ok(Keypair {
497            pair: Some(pair_inner),
498            crypto_type,
499            ..Default::default()
500        })
501    }
502
503    /// Signs data with the keypair's private key.
504    ///
505    /// ```text
506    ///     Arguments:
507    ///         data (Vec<u8>): The data to sign as bytes.
508    ///     Returns:
509    ///         signature (Vec<u8>): The signature as bytes.
510    /// ```
511    pub fn sign(&self, data: Vec<u8>) -> Result<Vec<u8>, String> {
512        let pair = self
513            .pair
514            .as_ref()
515            .ok_or_else(|| "No private key set to create signatures.".to_string())?;
516
517        Ok(pair.sign(&data))
518    }
519
520    /// Verifies a signature against data using the keypair's public key.
521    ///
522    /// ```text
523    ///     Arguments:
524    ///         data (Vec<u8>): The data that was signed as bytes.
525    ///         signature (Vec<u8>): The signature to verify as bytes.
526    ///     Returns:
527    ///         verified (bool): ``True`` if the signature is valid, ``False`` otherwise.
528    /// ```
529    pub fn verify(&self, data: Vec<u8>, signature: Vec<u8>) -> Result<bool, String> {
530        let public_key_bytes = self.public_key_bytes()?;
531        let ct = self.crypto_type();
532
533        let verified = verify_signature(ct, &public_key_bytes, &data, &signature)?;
534        if verified {
535            return Ok(true);
536        }
537
538        let wrapped_data = [b"<Bytes>", data.as_slice(), b"</Bytes>"].concat();
539        verify_signature(ct, &public_key_bytes, &wrapped_data, &signature)
540    }
541
542    /// Encrypts a message using the recipient's ED25519 public key (sealed box).
543    ///
544    /// ```text
545    ///     Arguments:
546    ///         message (&[u8]): The plaintext message to encrypt.
547    ///     Returns:
548    ///         ciphertext (Vec<u8>): The encrypted message (48 bytes longer than the input).
549    /// ```
550    pub fn encrypt(&self, message: &[u8]) -> Result<Vec<u8>, String> {
551        if self.crypto_type() != CRYPTO_ED25519 {
552            return Err("Encrypt/decrypt is only supported for ED25519 keypairs.".to_string());
553        }
554        let x25519_pk = self.ed25519_to_x25519_pk()?;
555        Ok(sealedbox::seal(message, &x25519_pk))
556    }
557
558    /// Decrypts a sealed box ciphertext using the keypair's ED25519 private key.
559    ///
560    /// ```text
561    ///     Arguments:
562    ///         ciphertext (&[u8]): The encrypted message to decrypt.
563    ///     Returns:
564    ///         plaintext (Vec<u8>): The decrypted message.
565    /// ```
566    pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, String> {
567        if self.crypto_type() != CRYPTO_ED25519 {
568            return Err("Encrypt/decrypt is only supported for ED25519 keypairs.".to_string());
569        }
570        let pair = self
571            .pair
572            .as_ref()
573            .ok_or_else(|| "Decryption requires a keypair with a private key.".to_string())?;
574        let (x25519_pk, x25519_sk) = Self::ed25519_x25519_keypair_from_pair(pair)?;
575        sealedbox::open(ciphertext, &x25519_pk, &x25519_sk)
576            .map_err(|_| "Decryption failed: invalid ciphertext or wrong key.".to_string())
577    }
578
579    fn ed25519_to_x25519_pk(&self) -> Result<sodiumoxide::crypto::box_::PublicKey, String> {
580        let pubkey_bytes = self.public_key_bytes()?;
581        let ed25519_pk = sign_ed25519::PublicKey::from_slice(&pubkey_bytes)
582            .ok_or_else(|| "No public key available for encryption.".to_string())?;
583        sign_ed25519::to_curve25519_pk(&ed25519_pk)
584            .map_err(|_| "Failed to convert ED25519 public key to X25519.".to_string())
585    }
586
587    fn ed25519_x25519_keypair_from_pair(
588        pair: &PairInner,
589    ) -> Result<
590        (
591            sodiumoxide::crypto::box_::PublicKey,
592            sodiumoxide::crypto::box_::SecretKey,
593        ),
594        String,
595    > {
596        let seed_bytes = pair.to_raw_vec();
597        let seed = sign_ed25519::Seed::from_slice(&seed_bytes)
598            .ok_or_else(|| "Failed to derive X25519 keypair for decryption.".to_string())?;
599        let (pk, sk) = sign_ed25519::keypair_from_seed(&seed);
600        let x25519_pk = sign_ed25519::to_curve25519_pk(&pk)
601            .map_err(|_| "Failed to derive X25519 keypair for decryption.".to_string())?;
602        let x25519_sk = sign_ed25519::to_curve25519_sk(&sk)
603            .map_err(|_| "Failed to derive X25519 keypair for decryption.".to_string())?;
604        Ok((x25519_pk, x25519_sk))
605    }
606
607    fn public_key_bytes(&self) -> Result<[u8; 32], String> {
608        if let Some(pair) = &self.pair {
609            Ok(pair.public_bytes())
610        } else if let Some(public_key_str) = &self.public_key {
611            let bytes = hex::decode(public_key_str.trim_start_matches("0x"))
612                .map_err(|e| format!("Invalid `public_key` string: {:?}", e))?;
613            <[u8; 32]>::try_from(bytes).map_err(|_| "Public key must be 32 bytes.".to_string())
614        } else {
615            Err("No public key or pair available.".to_string())
616        }
617    }
618
619    /// Returns the SS58 address of the keypair.
620    pub fn ss58_address(&self) -> Option<String> {
621        match &self.pair {
622            Some(pair) => Some(pair.ss58_address()),
623            None => self.ss58_address.clone(),
624        }
625    }
626
627    /// Returns the public key of the keypair as bytes.
628    pub fn public_key(&self) -> Result<Option<Vec<u8>>, String> {
629        if let Some(pair) = &self.pair {
630            Ok(Some(pair.public_bytes().to_vec()))
631        } else if let Some(public_key) = &self.public_key {
632            let public_key_vec = hex::decode(public_key.trim_start_matches("0x"))
633                .map_err(|e| format!("Invalid `public_key` string: {}", e))?;
634            Ok(Some(public_key_vec))
635        } else {
636            Ok(None)
637        }
638    }
639
640    /// Returns the SS58 format number.
641    pub fn ss58_format(&self) -> u8 {
642        self.ss58_format
643    }
644
645    /// Returns the seed bytes used to derive this keypair, if known.
646    pub fn seed_hex(&self) -> Option<Vec<u8>> {
647        self.seed_hex.clone()
648    }
649
650    /// Returns the cryptographic algorithm type.
651    /// Derives from the inner pair if present; falls back to the stored field for pub-only keypairs.
652    pub fn crypto_type(&self) -> u8 {
653        match &self.pair {
654            Some(pair) => pair.crypto_type(),
655            None => self.crypto_type,
656        }
657    }
658
659    /// Returns the mnemonic phrase of the keypair.
660    pub fn mnemonic(&self) -> Option<String> {
661        self.mnemonic.clone()
662    }
663
664    /// Returns the private key of the keypair as bytes.
665    pub fn private_key(&self) -> Result<Option<Vec<u8>>, String> {
666        match &self.pair {
667            Some(pair) => {
668                let seed = pair.to_raw_vec();
669                Ok(Some(seed))
670            }
671            None => {
672                if let Some(private_key) = &self.private_key {
673                    Ok(Some(private_key.as_bytes().to_vec()))
674                } else {
675                    Ok(None)
676                }
677            }
678        }
679    }
680}
681
682impl Default for Keypair {
683    fn default() -> Self {
684        Keypair {
685            ss58_address: None,
686            public_key: None,
687            private_key: None,
688            ss58_format: 42,
689            seed_hex: None,
690            crypto_type: CRYPTO_SR25519,
691            mnemonic: None,
692            pair: None,
693        }
694    }
695}
696
697impl fmt::Display for Keypair {
698    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
699        let address = self.ss58_address();
700        match address {
701            Some(addr) => write!(f, "<Keypair (address={})>", addr),
702            None => write!(f, "<Keypair (address=None)>"),
703        }
704    }
705}
706
707impl fmt::Debug for Keypair {
708    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
709        let address = self.ss58_address();
710        match address {
711            Some(addr) => write!(f, "<Keypair (address={})>", addr),
712            None => write!(f, "<Keypair (address=None)>"),
713        }
714    }
715}
716
717#[cfg(test)]
718mod tests {
719    use super::*;
720
721    fn test_mnemonic() -> String {
722        "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
723            .to_string()
724    }
725
726    // --- Creation tests ---
727
728    #[test]
729    fn test_sr25519_from_mnemonic_produces_valid_keypair() {
730        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
731        assert_eq!(kp.crypto_type(), CRYPTO_SR25519);
732        let pk = kp.public_key().unwrap().unwrap();
733        assert_eq!(pk.len(), 32);
734        assert!(kp.ss58_address().is_some());
735        assert!(kp.ss58_address().unwrap().starts_with('5'));
736    }
737
738    #[test]
739    fn test_ed25519_from_mnemonic_produces_valid_keypair() {
740        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
741        assert_eq!(kp.crypto_type(), CRYPTO_ED25519);
742        let pk = kp.public_key().unwrap().unwrap();
743        assert_eq!(pk.len(), 32);
744        let priv_key = kp.private_key().unwrap().unwrap();
745        assert_eq!(priv_key.len(), 32);
746        assert!(kp.ss58_address().is_some());
747        assert!(kp.ss58_address().unwrap().starts_with('5'));
748    }
749
750    #[test]
751    fn test_same_mnemonic_different_crypto_produces_different_addresses() {
752        let sr = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
753        let ed = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
754        assert_ne!(sr.ss58_address(), ed.ss58_address());
755        assert_ne!(
756            sr.public_key().unwrap().unwrap(),
757            ed.public_key().unwrap().unwrap()
758        );
759    }
760
761    #[test]
762    fn test_ed25519_from_seed_accepts_32_bytes() {
763        let result = Keypair::create_from_seed([1u8; 32].to_vec(), CRYPTO_ED25519);
764        assert!(result.is_ok());
765    }
766
767    #[test]
768    fn test_ed25519_from_seed_rejects_64_bytes() {
769        let result = Keypair::create_from_seed([1u8; 64].to_vec(), CRYPTO_ED25519);
770        assert!(result.is_err());
771    }
772
773    #[test]
774    fn test_sr25519_from_seed_accepts_32_and_64_bytes() {
775        assert!(Keypair::create_from_seed([1u8; 32].to_vec(), CRYPTO_SR25519).is_ok());
776        assert!(Keypair::create_from_seed([1u8; 64].to_vec(), CRYPTO_SR25519).is_ok());
777    }
778
779    #[test]
780    fn test_ed25519_from_uri_hard_derivation() {
781        let kp = Keypair::create_from_uri("//Alice", CRYPTO_ED25519).unwrap();
782        assert!(kp.ss58_address().is_some());
783        assert_eq!(kp.crypto_type(), CRYPTO_ED25519);
784    }
785
786    #[test]
787    fn test_invalid_crypto_type_rejected() {
788        assert!(Keypair::create_from_mnemonic(&test_mnemonic(), 2).is_err());
789        assert!(Keypair::create_from_mnemonic(&test_mnemonic(), 255).is_err());
790        assert!(Keypair::new(
791            Some("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".to_string()),
792            None,
793            None,
794            42,
795            None,
796            5
797        )
798        .is_err());
799    }
800
801    #[test]
802    fn test_sr25519_alice_known_address() {
803        let kp = Keypair::create_from_uri("//Alice", CRYPTO_SR25519).unwrap();
804        assert_eq!(
805            kp.ss58_address().unwrap(),
806            "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
807        );
808    }
809
810    // --- Sign/Verify tests ---
811
812    #[test]
813    fn test_ed25519_sign_verify_roundtrip() {
814        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
815        let sig = kp.sign(b"hello".to_vec()).unwrap();
816        assert_eq!(sig.len(), 64);
817        assert!(kp.verify(b"hello".to_vec(), sig).unwrap());
818    }
819
820    #[test]
821    fn test_ed25519_verify_wrong_data() {
822        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
823        let sig = kp.sign(b"hello".to_vec()).unwrap();
824        assert!(!kp.verify(b"world".to_vec(), sig).unwrap());
825    }
826
827    #[test]
828    fn test_ed25519_verify_bytes_wrapping() {
829        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
830        let sig = kp.sign(b"<Bytes>hello</Bytes>".to_vec()).unwrap();
831        assert!(kp.verify(b"hello".to_vec(), sig).unwrap());
832    }
833
834    #[test]
835    fn test_sr25519_sign_verify_unchanged() {
836        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
837        let sig = kp.sign(b"hello".to_vec()).unwrap();
838        assert_eq!(sig.len(), 64);
839        assert!(kp.verify(b"hello".to_vec(), sig).unwrap());
840    }
841
842    #[test]
843    fn test_cross_type_verification_fails() {
844        let sr = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
845        let ed = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
846        let sig = sr.sign(b"hello".to_vec()).unwrap();
847        assert!(!ed.verify(b"hello".to_vec(), sig).unwrap());
848    }
849
850    // --- Keypair::new() pub-only tests ---
851
852    #[test]
853    fn test_new_ed25519_pubonly_from_ss58() {
854        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
855        let addr = kp.ss58_address().unwrap();
856
857        let pub_kp =
858            Keypair::new(Some(addr.clone()), None, None, 42, None, CRYPTO_ED25519).unwrap();
859        assert_eq!(pub_kp.ss58_address().unwrap(), addr);
860        assert_eq!(pub_kp.crypto_type(), CRYPTO_ED25519);
861    }
862
863    #[test]
864    fn test_pair_is_some() {
865        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
866        assert!(kp.pair_is_some());
867
868        let pub_kp = Keypair::new(
869            Some("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".to_string()),
870            None,
871            None,
872            42,
873            None,
874            CRYPTO_SR25519,
875        )
876        .unwrap();
877        assert!(!pub_kp.pair_is_some());
878    }
879
880    // --- crypto_type getter tests ---
881
882    #[test]
883    fn test_crypto_type_derived_from_pair() {
884        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
885        assert_eq!(kp.crypto_type(), CRYPTO_ED25519);
886
887        let kp2 = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
888        assert_eq!(kp2.crypto_type(), CRYPTO_SR25519);
889    }
890
891    #[test]
892    fn test_crypto_type_from_field_for_pubonly() {
893        let pub_kp = Keypair::new(
894            Some("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY".to_string()),
895            None,
896            None,
897            42,
898            None,
899            CRYPTO_ED25519,
900        )
901        .unwrap();
902        assert_eq!(pub_kp.crypto_type(), CRYPTO_ED25519);
903    }
904
905    // --- Private key creation tests ---
906
907    #[test]
908    fn test_ed25519_from_private_key_32_bytes() {
909        let hex_key = hex::encode([1u8; 32]);
910        let kp = Keypair::create_from_private_key(&hex_key, CRYPTO_ED25519).unwrap();
911        assert_eq!(kp.crypto_type(), CRYPTO_ED25519);
912        assert!(kp.ss58_address().is_some());
913    }
914
915    #[test]
916    fn test_ed25519_from_private_key_64_bytes_rejected() {
917        let hex_key = hex::encode([1u8; 64]);
918        let result = Keypair::create_from_private_key(&hex_key, CRYPTO_ED25519);
919        assert!(result.is_err());
920    }
921
922    #[test]
923    fn test_sr25519_from_private_key_64_bytes() {
924        let hex_key = hex::encode([1u8; 64]);
925        let kp = Keypair::create_from_private_key(&hex_key, CRYPTO_SR25519).unwrap();
926        assert_eq!(kp.crypto_type(), CRYPTO_SR25519);
927        assert!(kp.ss58_address().is_some());
928    }
929
930    // --- Determinism ---
931
932    #[test]
933    fn test_ed25519_mnemonic_determinism() {
934        let kp1 = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
935        let kp2 = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
936        assert_eq!(kp1.ss58_address(), kp2.ss58_address());
937        assert_eq!(
938            kp1.public_key().unwrap().unwrap(),
939            kp2.public_key().unwrap().unwrap()
940        );
941    }
942
943    // --- Pub-only verification ---
944
945    #[test]
946    fn test_ed25519_pubonly_can_verify() {
947        let full = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
948        let sig = full.sign(b"test-message".to_vec()).unwrap();
949        let addr = full.ss58_address().unwrap();
950
951        let pub_only = Keypair::new(Some(addr), None, None, 42, None, CRYPTO_ED25519).unwrap();
952        assert!(pub_only.verify(b"test-message".to_vec(), sig).unwrap());
953    }
954
955    // --- SR25519 Bytes wrapping (mirror of ED25519 test) ---
956
957    #[test]
958    fn test_sr25519_verify_bytes_wrapping() {
959        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
960        let sig = kp.sign(b"<Bytes>hello</Bytes>".to_vec()).unwrap();
961        assert!(kp.verify(b"hello".to_vec(), sig).unwrap());
962    }
963
964    // --- Default ---
965
966    #[test]
967    fn test_default_crypto_type_is_sr25519() {
968        let kp = Keypair::default();
969        assert_eq!(kp.crypto_type(), CRYPTO_SR25519);
970    }
971
972    // --- Encrypt/Decrypt tests ---
973
974    #[test]
975    fn test_sp_core_and_sodiumoxide_same_pubkey_from_seed() {
976        let seed = [42u8; 32];
977        let sp_pair = ed25519::Pair::from_seed_slice(&seed).unwrap();
978        let sp_pubkey = sp_pair.public().0;
979
980        let sodium_seed = sign_ed25519::Seed::from_slice(&seed).unwrap();
981        let (sodium_pk, _) = sign_ed25519::keypair_from_seed(&sodium_seed);
982
983        assert_eq!(sp_pubkey, sodium_pk.0);
984    }
985
986    #[test]
987    fn test_encrypt_decrypt_roundtrip() {
988        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
989        let message = b"secret message for testing";
990        let ciphertext = kp.encrypt(message).unwrap();
991        let plaintext = kp.decrypt(&ciphertext).unwrap();
992        assert_eq!(plaintext, message);
993    }
994
995    #[test]
996    fn test_encrypt_produces_correct_length() {
997        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
998        let message = b"hello world";
999        let ciphertext = kp.encrypt(message).unwrap();
1000        assert_eq!(ciphertext.len(), message.len() + 48);
1001    }
1002
1003    #[test]
1004    fn test_encrypt_is_nondeterministic() {
1005        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
1006        let message = b"same message";
1007        let ct1 = kp.encrypt(message).unwrap();
1008        let ct2 = kp.encrypt(message).unwrap();
1009        assert_ne!(ct1, ct2);
1010    }
1011
1012    #[test]
1013    fn test_encrypt_works_on_pubonly_keypair() {
1014        let full = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
1015        let addr = full.ss58_address().unwrap();
1016        let pub_only = Keypair::new(Some(addr), None, None, 42, None, CRYPTO_ED25519).unwrap();
1017        let ciphertext = pub_only.encrypt(b"hello").unwrap();
1018        assert_eq!(ciphertext.len(), 5 + 48);
1019    }
1020
1021    #[test]
1022    fn test_decrypt_fails_on_pubonly_keypair() {
1023        let full = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
1024        let ciphertext = full.encrypt(b"hello").unwrap();
1025        let addr = full.ss58_address().unwrap();
1026        let pub_only = Keypair::new(Some(addr), None, None, 42, None, CRYPTO_ED25519).unwrap();
1027        let result = pub_only.decrypt(&ciphertext);
1028        assert!(result.is_err());
1029        assert!(result.unwrap_err().contains("private key"));
1030    }
1031
1032    #[test]
1033    fn test_encrypt_fails_on_sr25519() {
1034        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
1035        let result = kp.encrypt(b"hello");
1036        assert!(result.is_err());
1037        assert!(result.unwrap_err().contains("ED25519"));
1038    }
1039
1040    #[test]
1041    fn test_decrypt_fails_on_sr25519() {
1042        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_SR25519).unwrap();
1043        let result = kp.decrypt(b"fake ciphertext that is long enough for sealbytes padding!!");
1044        assert!(result.is_err());
1045        assert!(result.unwrap_err().contains("ED25519"));
1046    }
1047
1048    #[test]
1049    fn test_decrypt_fails_with_wrong_key() {
1050        let alice = Keypair::create_from_uri("//Alice", CRYPTO_ED25519).unwrap();
1051        let bob = Keypair::create_from_uri("//Bob", CRYPTO_ED25519).unwrap();
1052        let ciphertext = alice.encrypt(b"for alice only").unwrap();
1053        let result = bob.decrypt(&ciphertext);
1054        assert!(result.is_err());
1055    }
1056
1057    #[test]
1058    fn test_decrypt_fails_with_tampered_ciphertext() {
1059        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
1060        let mut ciphertext = kp.encrypt(b"hello").unwrap();
1061        ciphertext[10] ^= 0xFF;
1062        let result = kp.decrypt(&ciphertext);
1063        assert!(result.is_err());
1064    }
1065
1066    #[test]
1067    fn test_encrypt_empty_message() {
1068        let kp = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
1069        let ciphertext = kp.encrypt(b"").unwrap();
1070        assert_eq!(ciphertext.len(), 48);
1071        let plaintext = kp.decrypt(&ciphertext).unwrap();
1072        assert_eq!(plaintext, b"");
1073    }
1074
1075    #[test]
1076    fn test_pubonly_can_encrypt_fullkey_can_decrypt() {
1077        let full = Keypair::create_from_mnemonic(&test_mnemonic(), CRYPTO_ED25519).unwrap();
1078        let addr = full.ss58_address().unwrap();
1079        let pub_only = Keypair::new(Some(addr), None, None, 42, None, CRYPTO_ED25519).unwrap();
1080
1081        let ciphertext = pub_only.encrypt(b"encrypted by pub-only").unwrap();
1082        let plaintext = full.decrypt(&ciphertext).unwrap();
1083        assert_eq!(plaintext, b"encrypted by pub-only");
1084    }
1085}