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::secretbox;
7use sodiumoxide::crypto::secretbox::{Key, Nonce};
8use sp_core::crypto::Ss58Codec;
9use sp_core::{sr25519, ByteArray, Pair};
10use std::fmt;
11
12const PKCS8_HEADER: &[u8] = &[48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32];
13const PKCS8_DIVIDER: &[u8] = &[161, 35, 3, 33, 0];
14const SEC_LENGTH: usize = 64;
15const PUB_LENGTH: usize = 32;
16
17#[derive(Serialize, Deserialize, Debug)]
18struct Encoding {
19    content: Vec<String>,
20    #[serde(rename = "type")]
21    enc_type: Vec<String>,
22    version: String,
23}
24
25#[derive(Serialize, Deserialize, Debug)]
26struct Meta {
27    #[serde(rename = "genesisHash")]
28    genesis_hash: Option<String>,
29    name: String,
30    #[serde(rename = "whenCreated")]
31    when_created: Option<u64>,
32}
33
34#[derive(Serialize, Deserialize, Debug)]
35struct JsonStructure {
36    encoded: String,
37    encoding: Encoding,
38    address: String,
39    meta: Meta,
40}
41
42#[derive(Clone)]
43pub struct Keypair {
44    ss58_address: Option<String>,
45    public_key: Option<String>,
46    private_key: Option<String>,
47    ss58_format: u8,
48    seed_hex: Option<Vec<u8>>,
49    crypto_type: u8,
50    mnemonic: Option<String>,
51    pair: Option<sr25519::Pair>,
52}
53
54impl Keypair {
55    /// Creates a new Keypair instance.
56    ///
57    ///     Arguments:
58    ///         ss58_address (Option<String>): Optional SS58-formatted address.
59    ///         public_key (Option<String>): Optional public key as hex string.
60    ///         private_key (Option<String>): Optional private key as hex string.
61    ///         ss58_format (u8): The SS58 format number for address encoding.
62    ///         seed_hex (Option<Vec<u8>>): Optional seed bytes.
63    ///         crypto_type (u8): The cryptographic algorithm type (1 for SR25519).
64    ///     Returns:
65    ///         keypair (Keypair): A new Keypair instance.
66    pub fn new(
67        ss58_address: Option<String>,
68        public_key: Option<String>,
69        private_key: Option<String>,
70        ss58_format: u8,
71        seed_hex: Option<Vec<u8>>,
72        crypto_type: u8,
73    ) -> Result<Self, String> {
74        if crypto_type != 1 {
75            return Err(format!("Unsupported crypto type: {}.", crypto_type));
76        }
77
78        let mut ss58_address_res = ss58_address.clone();
79        let mut public_key_res = public_key;
80
81        if let Some(private_key_str) = &private_key {
82            let private_key_bytes =
83                hex::decode(private_key_str.trim_start_matches("0x")).expect("");
84
85            if private_key_bytes.len() != 64 {
86                return Err("Secret key should be 64 bytes long.".to_string());
87            }
88
89            // TODO: add logic creation pair from private key
90        }
91
92        // if public_key is passed
93        if let Some(public_key_str) = &public_key_res {
94            let public_key_vec = hex::decode(public_key_str.trim_start_matches("0x"))
95                .map_err(|e| format!("Invalid `public_key` string: {}", e))?;
96
97            let public_key_array: [u8; 32] = public_key_vec
98                .try_into()
99                .map_err(|_| "Public key must be 32 bytes long.")?;
100
101            let public_key = sr25519::Public::from_raw(public_key_array);
102
103            ss58_address_res = Option::from(public_key.to_ss58check());
104        }
105
106        // If ss58_address is passed, decode the public key
107        if let Some(ss58_address_str) = ss58_address.clone() {
108            let public_key = sr25519::Public::from_ss58check(&ss58_address_str)
109                .map_err(|e| format!("Invalid SS58 address: {}", e))?;
110
111            public_key_res = Some(hex::encode(public_key.to_raw()));
112        }
113
114        let kp = Keypair {
115            ss58_address: ss58_address_res,
116            public_key: public_key_res,
117            private_key,
118            ss58_format,
119            seed_hex,
120            crypto_type,
121            mnemonic: None,
122            pair: None,
123        };
124
125        // If public_key is missing (ss58_address wasn't created), return an error
126        if kp.public_key.is_none() {
127            return Err("No SS58 formatted address or public key provided.".to_string());
128        }
129        Ok(kp)
130    }
131
132    fn __str__(&self) -> Result<String, String> {
133        match self.ss58_address() {
134            Some(address) => Ok(format!("<Keypair (address={})>", address)),
135            None => Ok("<Keypair (address=None)>".to_string()),
136        }
137    }
138
139    fn __repr__(&self) -> Result<String, String> {
140        self.__str__()
141    }
142
143    /// Generates a new mnemonic phrase.
144    ///
145    ///     Arguments:
146    ///         n_words (usize): The number of words in the mnemonic (e.g., 12, 15, 18, 21, 24).
147    ///     Returns:
148    ///         mnemonic (String): The generated mnemonic phrase.
149    pub fn generate_mnemonic(n_words: usize) -> Result<String, String> {
150        let mnemonic = Mnemonic::generate(n_words).map_err(|e| e.to_string())?;
151        Ok(mnemonic.to_string())
152    }
153
154    /// Creates a Keypair from a mnemonic phrase.
155    ///
156    ///     Arguments:
157    ///         mnemonic (str): The mnemonic phrase to create the keypair from.
158    ///     Returns:
159    ///         keypair (Keypair): The Keypair created from the mnemonic.
160    pub fn create_from_mnemonic(mnemonic: &str) -> Result<Self, String> {
161        let (pair, seed_vec) =
162            sr25519::Pair::from_phrase(mnemonic, None).map_err(|e| e.to_string())?;
163
164        let kp = Keypair {
165            mnemonic: Some(mnemonic.to_string()),
166            seed_hex: Some(seed_vec.to_vec()),
167            pair: Some(pair),
168            ..Default::default()
169        };
170        Ok(kp)
171    }
172
173    /// Creates a Keypair from a seed.
174    ///
175    ///     Arguments:
176    ///         seed (Vec<u8>): The seed bytes to create the keypair from.
177    ///     Returns:
178    ///         keypair (Keypair): The Keypair created from the seed.
179    pub fn create_from_seed(seed: Vec<u8>) -> Result<Self, String> {
180        let pair = sr25519::Pair::from_seed_slice(&seed)
181            .map_err(|e| format!("Failed to create pair from seed: {}", e))?;
182
183        let kp = Keypair {
184            seed_hex: Some(seed.to_vec()),
185            pair: Some(pair),
186            ..Default::default()
187        };
188        Ok(kp)
189    }
190
191    /// Creates a Keypair from a private key.
192    ///
193    ///     Arguments:
194    ///         private_key (str): The private key as hex string to create the keypair from.
195    ///     Returns:
196    ///         keypair (Keypair): The Keypair created from the private key.
197    pub fn create_from_private_key(private_key: &str) -> Result<Self, String> {
198        let private_key_vec = hex::decode(private_key.trim_start_matches("0x"))
199            .map_err(|e| format!("Invalid `private_key` string: {}", e))?;
200
201        let pair = sr25519::Pair::from_seed_slice(&private_key_vec)
202            .map_err(|e| format!("Failed to create pair from private key: {}", e))?;
203
204        let kp = Keypair {
205            pair: Some(pair),
206            ..Default::default()
207        };
208        Ok(kp)
209    }
210
211    /// Creates a Keypair from encrypted JSON data.
212    ///
213    ///     Arguments:
214    ///         json_data (str): The encrypted JSON data containing the keypair.
215    ///         passphrase (str): The passphrase to decrypt the JSON data.
216    ///     Returns:
217    ///         keypair (Keypair): The Keypair created from the encrypted JSON.
218    pub fn create_from_encrypted_json(
219        json_data: &str,
220        passphrase: &str,
221    ) -> Result<Keypair, String> {
222        /// rust version of python .rjust
223        fn pad_right(mut data: Vec<u8>, total_len: usize, pad_byte: u8) -> Vec<u8> {
224            if data.len() < total_len {
225                let pad_len = total_len - data.len();
226                data.extend(vec![pad_byte; pad_len]);
227            }
228            data
229        }
230
231        pub fn pair_from_ed25519_secret_key(secret: &[u8], pubkey: &[u8]) -> ([u8; 64], [u8; 32]) {
232            match (
233                SecretKey::from_ed25519_bytes(secret),
234                PublicKey::from_bytes(pubkey),
235            ) {
236                (Ok(s), Ok(k)) => (s.to_bytes(), k.to_bytes()),
237                _ => panic!("Invalid secret or pubkey provided."),
238            }
239        }
240
241        /// Decodes a PKCS8-encoded key pair from the provided byte slice.
242        /// Returns a tuple containing the private key and public key as vectors of bytes.
243        fn decode_pkcs8(
244            ciphertext: &[u8],
245        ) -> Result<([u8; SEC_LENGTH], [u8; PUB_LENGTH]), &'static str> {
246            let mut current_offset = 0;
247            let header = &ciphertext[current_offset..current_offset + PKCS8_HEADER.len()];
248            if header != PKCS8_HEADER {
249                return Err("Invalid Pkcs8 header found in body");
250            }
251            current_offset += PKCS8_HEADER.len();
252            let secret_key = &ciphertext[current_offset..current_offset + SEC_LENGTH];
253            let mut secret_key_array = [0u8; SEC_LENGTH];
254            secret_key_array.copy_from_slice(secret_key);
255            current_offset += SEC_LENGTH;
256            let divider = &ciphertext[current_offset..current_offset + PKCS8_DIVIDER.len()];
257            if divider != PKCS8_DIVIDER {
258                return Err("Invalid Pkcs8 divider found in body");
259            }
260            current_offset += PKCS8_DIVIDER.len();
261            let public_key = &ciphertext[current_offset..current_offset + PUB_LENGTH];
262            let mut public_key_array = [0u8; PUB_LENGTH];
263            public_key_array.copy_from_slice(public_key);
264            Ok((secret_key_array, public_key_array))
265        }
266
267        let json_data: JsonStructure = serde_json::from_str(json_data).unwrap();
268
269        if json_data.encoding.version != "3" {
270            return Err("Unsupported JSON format".to_string());
271        }
272
273        let mut encrypted = general_purpose::STANDARD
274            .decode(json_data.encoded)
275            .map_err(|e| e.to_string())?;
276
277        let password = if json_data.encoding.enc_type.contains(&"scrypt".to_string()) {
278            let salt = &encrypted[0..32];
279            let n = u32::from_le_bytes(encrypted[32..36].try_into().unwrap());
280            let p = u32::from_le_bytes(encrypted[36..40].try_into().unwrap());
281            let r = u32::from_le_bytes(encrypted[40..44].try_into().unwrap());
282            let log_n: u8 = n.ilog2() as u8;
283
284            let params = ScryptParams::new(log_n, r, p, 32).map_err(|e| e.to_string())?;
285            let mut derived_key = vec![0u8; 32];
286            scrypt(passphrase.as_bytes(), salt, &params, &mut derived_key)
287                .map_err(|e| e.to_string())?;
288            encrypted = encrypted[44..].to_vec();
289            derived_key
290        } else {
291            let mut derived_key = passphrase.as_bytes().to_vec();
292            derived_key = pad_right(derived_key, 32, 0x00);
293            derived_key
294        };
295
296        let nonce_bytes = &encrypted[0..24];
297        let nonce = Nonce::from_slice(nonce_bytes)
298            .ok_or("Invalid nonce length")
299            .map_err(|e| e.to_string())?;
300        let message = &encrypted[24..];
301
302        let key = Key::from_slice(&password).ok_or("Invalid key length")?;
303        let decrypted_data = secretbox::open(message, &nonce, &key)
304            .map_err(|_| "Failed to decrypt data".to_string())?;
305        let (private_key, public_key) =
306            decode_pkcs8(&decrypted_data).map_err(|_| "Failed to decode PKCS8 data".to_string())?;
307
308        let (secret, converted_public_key) =
309            pair_from_ed25519_secret_key(&private_key[..], &public_key[..]);
310
311        let keypair = match json_data.encoding.content.iter().any(|c| c == "sr25519") {
312            true => {
313                assert_eq!(public_key, converted_public_key);
314                Keypair::create_from_private_key(&hex::encode(secret))
315            }
316            _ => return Err("Unsupported keypair type.".to_string()),
317        };
318
319        keypair
320    }
321
322    /// Creates a Keypair from a URI string.
323    ///
324    ///     Arguments:
325    ///         uri (str): The URI string to create the keypair from.
326    ///     Returns:
327    ///         keypair (Keypair): The Keypair created from the URI.
328    pub fn create_from_uri(uri: &str) -> Result<Self, String> {
329        let pair = Pair::from_string(uri, None).map_err(|e| e.to_string())?;
330
331        let kp = Keypair {
332            pair: Some(pair),
333            ..Default::default()
334        };
335        Ok(kp)
336    }
337
338    /// Signs data with the keypair's private key.
339    ///
340    ///     Arguments:
341    ///         data (Vec<u8>): The data to sign as bytes.
342    ///     Returns:
343    ///         signature (Vec<u8>): The signature as bytes.
344    pub fn sign(&self, data: Vec<u8>) -> Result<Vec<u8>, String> {
345        // Check if private key exists
346        let pair = self
347            .pair
348            .as_ref()
349            .ok_or_else(|| "No private key set to create signatures".to_string())?;
350
351        // Generate a signature depending on the type of cryptographic key
352        let signature = match self.crypto_type {
353            1 => {
354                // SR25519
355                pair.sign(&data)
356            }
357            _ => {
358                return Err("Crypto type not supported.".to_string());
359            }
360        };
361
362        Ok(signature.to_vec())
363    }
364
365    /// Verifies a signature against data using the keypair's public key.
366    ///
367    ///     Arguments:
368    ///         data (Vec<u8>): The data that was signed as bytes.
369    ///         signature (Vec<u8>): The signature to verify as bytes.
370    ///     Returns:
371    ///         verified (bool): ``True`` if the signature is valid, ``False`` otherwise.
372    pub fn verify(&self, data: Vec<u8>, signature: Vec<u8>) -> Result<bool, String> {
373        // Check if public key exists
374        let public_key = if let Some(public_key_str) = &self.public_key {
375            hex::decode(public_key_str.trim_start_matches("0x"))
376                .map_err(|e| format!("Invalid `public_key` string: {:?}", e))?
377        } else if let Some(pair) = &self.pair {
378            pair.public().to_vec()
379        } else {
380            return Err("No public key or pair available.".to_string());
381        };
382
383        let public = sr25519::Public::from_raw(
384            <[u8; 32]>::try_from(public_key)
385                .map_err(|e| format!("Invalid public key length: {:?}", e))?,
386        );
387
388        // Convert signature bytes to the type expected by the verify function
389        let signature = sr25519::Signature::from_slice(&signature)
390            .map_err(|_| "Invalid signature".to_string())?;
391
392        // Verify signature depending on the type of crypto key
393        let verified = match self.crypto_type {
394            1 => {
395                // SR25519
396                sr25519::Pair::verify(&signature, &data, &public)
397            }
398            _ => {
399                return Err("Crypto type not supported".to_string());
400            }
401        };
402
403        // If not verified, try to verify with data wrapper
404        if !verified {
405            let wrapped_data = [b"<Bytes>", data.as_slice(), b"</Bytes>"].concat();
406            let verified_wrapped = match self.crypto_type {
407                1 => {
408                    // SR25519
409                    sr25519::Pair::verify(&signature, wrapped_data, &public)
410                }
411                _ => {
412                    return Err("Crypto type not supported".to_string());
413                }
414            };
415
416            Ok(verified_wrapped)
417        } else {
418            Ok(verified)
419        }
420    }
421
422    /// Returns the SS58 address of the keypair.
423    pub fn ss58_address(&self) -> Option<String> {
424        match &self.pair {
425            Some(pair) => {
426                let ss58_address = pair.public().to_ss58check();
427                Some(ss58_address)
428            }
429            None => self.ss58_address.clone(),
430        }
431    }
432
433    /// Returns the public key of the keypair as bytes.
434    pub fn public_key(&self) -> Result<Option<Vec<u8>>, String> {
435        if let Some(pair) = &self.pair {
436            let public_key_vec = pair.public().to_vec();
437            Ok(Some(public_key_vec))
438        } else if let Some(public_key) = &self.public_key {
439            let public_key_vec = hex::decode(public_key.trim_start_matches("0x"))
440                .map_err(|e| format!("Invalid `public_key` string: {}", e))?;
441            Ok(Some(public_key_vec))
442        } else {
443            Ok(None)
444        }
445    }
446
447    /// Returns the SS58 format number.
448    pub fn ss58_format(&self) -> u8 {
449        self.ss58_format
450    }
451
452    pub fn seed_hex(&self) -> Option<Vec<u8>> {
453        self.seed_hex.clone()
454    }
455
456    /// Returns the cryptographic algorithm type.
457    pub fn crypto_type(&self) -> u8 {
458        self.crypto_type
459    }
460
461    /// Sets the cryptographic algorithm type.
462    ///
463    ///     Arguments:
464    ///         crypto_type (u8): The cryptographic algorithm type (1 for SR25519).
465    pub fn set_crypto_type(&mut self, crypto_type: u8) {
466        self.crypto_type = crypto_type;
467    }
468
469    /// Returns the mnemonic phrase of the keypair.
470    pub fn mnemonic(&self) -> Option<String> {
471        self.mnemonic.clone()
472    }
473
474    /// Returns the private key of the keypair as bytes.
475    pub fn private_key(&self) -> Result<Option<Vec<u8>>, String> {
476        match &self.pair {
477            Some(pair) => {
478                let seed = pair.to_raw_vec();
479                Ok(Some(seed))
480            }
481            None => {
482                if let Some(private_key) = &self.private_key {
483                    Ok(Some(private_key.as_bytes().to_vec()))
484                } else {
485                    Ok(None)
486                }
487            }
488        }
489    }
490}
491
492// Default values for Keypair
493impl Default for Keypair {
494    fn default() -> Self {
495        Keypair {
496            ss58_address: None,
497            public_key: None,
498            private_key: None,
499            ss58_format: 42,
500            seed_hex: None,
501            crypto_type: 1,
502            mnemonic: None,
503            pair: None,
504        }
505    }
506}
507
508impl fmt::Display for Keypair {
509    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510        let address = self.ss58_address();
511        match address {
512            Some(addr) => write!(f, "<Keypair (address={})>", addr),
513            None => write!(f, "<Keypair (address=None)>"),
514        }
515    }
516}
517
518impl fmt::Debug for Keypair {
519    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
520        let address = self.ss58_address();
521        match address {
522            Some(addr) => write!(f, "<Keypair (address={})>", addr),
523            None => write!(f, "<Keypair (address=None)>"),
524        }
525    }
526}