solana_sdk/signer/
keypair.rs

1#![cfg(feature = "full")]
2
3#[cfg(target_arch = "wasm32")]
4use wasm_bindgen::prelude::*;
5use {
6    crate::{
7        pubkey::Pubkey,
8        signature::Signature,
9        signer::{EncodableKey, EncodableKeypair, SeedDerivable, Signer, SignerError},
10    },
11    ed25519_dalek::Signer as DalekSigner,
12    ed25519_dalek_bip32::Error as Bip32Error,
13    hmac::Hmac,
14    rand0_7::{rngs::OsRng, CryptoRng, RngCore},
15    solana_derivation_path::DerivationPath,
16    std::{
17        error,
18        io::{Read, Write},
19        path::Path,
20    },
21};
22
23/// A vanilla Ed25519 key pair
24#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
25#[derive(Debug)]
26pub struct Keypair(ed25519_dalek::Keypair);
27
28impl Keypair {
29    /// Can be used for generating a Keypair without a dependency on `rand` types
30    pub const SECRET_KEY_LENGTH: usize = 32;
31
32    /// Constructs a new, random `Keypair` using a caller-provided RNG
33    pub fn generate<R>(csprng: &mut R) -> Self
34    where
35        R: CryptoRng + RngCore,
36    {
37        Self(ed25519_dalek::Keypair::generate(csprng))
38    }
39
40    /// Constructs a new, random `Keypair` using `OsRng`
41    pub fn new() -> Self {
42        let mut rng = OsRng;
43        Self::generate(&mut rng)
44    }
45
46    /// Recovers a `Keypair` from a byte array
47    pub fn from_bytes(bytes: &[u8]) -> Result<Self, ed25519_dalek::SignatureError> {
48        if bytes.len() < ed25519_dalek::KEYPAIR_LENGTH {
49            return Err(ed25519_dalek::SignatureError::from_source(String::from(
50                "candidate keypair byte array is too short",
51            )));
52        }
53        let secret =
54            ed25519_dalek::SecretKey::from_bytes(&bytes[..ed25519_dalek::SECRET_KEY_LENGTH])?;
55        let public =
56            ed25519_dalek::PublicKey::from_bytes(&bytes[ed25519_dalek::SECRET_KEY_LENGTH..])?;
57        let expected_public = ed25519_dalek::PublicKey::from(&secret);
58        (public == expected_public)
59            .then_some(Self(ed25519_dalek::Keypair { secret, public }))
60            .ok_or(ed25519_dalek::SignatureError::from_source(String::from(
61                "keypair bytes do not specify same pubkey as derived from their secret key",
62            )))
63    }
64
65    /// Returns this `Keypair` as a byte array
66    pub fn to_bytes(&self) -> [u8; 64] {
67        self.0.to_bytes()
68    }
69
70    /// Recovers a `Keypair` from a base58-encoded string
71    pub fn from_base58_string(s: &str) -> Self {
72        Self::from_bytes(&bs58::decode(s).into_vec().unwrap()).unwrap()
73    }
74
75    /// Returns this `Keypair` as a base58-encoded string
76    pub fn to_base58_string(&self) -> String {
77        bs58::encode(&self.0.to_bytes()).into_string()
78    }
79
80    /// Gets this `Keypair`'s SecretKey
81    pub fn secret(&self) -> &ed25519_dalek::SecretKey {
82        &self.0.secret
83    }
84
85    /// Allows Keypair cloning
86    ///
87    /// Note that the `Clone` trait is intentionally unimplemented because making a
88    /// second copy of sensitive secret keys in memory is usually a bad idea.
89    ///
90    /// Only use this in tests or when strictly required. Consider using [`std::sync::Arc<Keypair>`]
91    /// instead.
92    pub fn insecure_clone(&self) -> Self {
93        Self(ed25519_dalek::Keypair {
94            // This will never error since self is a valid keypair
95            secret: ed25519_dalek::SecretKey::from_bytes(self.0.secret.as_bytes()).unwrap(),
96            public: self.0.public,
97        })
98    }
99}
100
101#[cfg(test)]
102static_assertions::const_assert_eq!(Keypair::SECRET_KEY_LENGTH, ed25519_dalek::SECRET_KEY_LENGTH);
103
104impl Signer for Keypair {
105    #[inline]
106    fn pubkey(&self) -> Pubkey {
107        Pubkey::from(self.0.public.to_bytes())
108    }
109
110    fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
111        Ok(self.pubkey())
112    }
113
114    fn sign_message(&self, message: &[u8]) -> Signature {
115        Signature::from(self.0.sign(message).to_bytes())
116    }
117
118    fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
119        Ok(self.sign_message(message))
120    }
121
122    fn is_interactive(&self) -> bool {
123        false
124    }
125}
126
127impl<T> PartialEq<T> for Keypair
128where
129    T: Signer,
130{
131    fn eq(&self, other: &T) -> bool {
132        self.pubkey() == other.pubkey()
133    }
134}
135
136impl EncodableKey for Keypair {
137    fn read<R: Read>(reader: &mut R) -> Result<Self, Box<dyn error::Error>> {
138        read_keypair(reader)
139    }
140
141    fn write<W: Write>(&self, writer: &mut W) -> Result<String, Box<dyn error::Error>> {
142        write_keypair(self, writer)
143    }
144}
145
146impl SeedDerivable for Keypair {
147    fn from_seed(seed: &[u8]) -> Result<Self, Box<dyn error::Error>> {
148        keypair_from_seed(seed)
149    }
150
151    fn from_seed_and_derivation_path(
152        seed: &[u8],
153        derivation_path: Option<DerivationPath>,
154    ) -> Result<Self, Box<dyn error::Error>> {
155        keypair_from_seed_and_derivation_path(seed, derivation_path)
156    }
157
158    fn from_seed_phrase_and_passphrase(
159        seed_phrase: &str,
160        passphrase: &str,
161    ) -> Result<Self, Box<dyn error::Error>> {
162        keypair_from_seed_phrase_and_passphrase(seed_phrase, passphrase)
163    }
164}
165
166impl EncodableKeypair for Keypair {
167    type Pubkey = Pubkey;
168
169    /// Returns the associated pubkey. Use this function specifically for settings that involve
170    /// reading or writing pubkeys. For other settings, use `Signer::pubkey()` instead.
171    fn encodable_pubkey(&self) -> Self::Pubkey {
172        self.pubkey()
173    }
174}
175
176/// Reads a JSON-encoded `Keypair` from a `Reader` implementor
177pub fn read_keypair<R: Read>(reader: &mut R) -> Result<Keypair, Box<dyn error::Error>> {
178    let bytes: Vec<u8> = serde_json::from_reader(reader)?;
179    Keypair::from_bytes(&bytes)
180        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()).into())
181}
182
183/// Reads a `Keypair` from a file
184pub fn read_keypair_file<F: AsRef<Path>>(path: F) -> Result<Keypair, Box<dyn error::Error>> {
185    Keypair::read_from_file(path)
186}
187
188/// Writes a `Keypair` to a `Write` implementor with JSON-encoding
189pub fn write_keypair<W: Write>(
190    keypair: &Keypair,
191    writer: &mut W,
192) -> Result<String, Box<dyn error::Error>> {
193    let keypair_bytes = keypair.0.to_bytes();
194    let serialized = serde_json::to_string(&keypair_bytes.to_vec())?;
195    writer.write_all(serialized.as_bytes())?;
196    Ok(serialized)
197}
198
199/// Writes a `Keypair` to a file with JSON-encoding
200pub fn write_keypair_file<F: AsRef<Path>>(
201    keypair: &Keypair,
202    outfile: F,
203) -> Result<String, Box<dyn error::Error>> {
204    keypair.write_to_file(outfile)
205}
206
207/// Constructs a `Keypair` from caller-provided seed entropy
208pub fn keypair_from_seed(seed: &[u8]) -> Result<Keypair, Box<dyn error::Error>> {
209    if seed.len() < ed25519_dalek::SECRET_KEY_LENGTH {
210        return Err("Seed is too short".into());
211    }
212    let secret = ed25519_dalek::SecretKey::from_bytes(&seed[..ed25519_dalek::SECRET_KEY_LENGTH])
213        .map_err(|e| e.to_string())?;
214    let public = ed25519_dalek::PublicKey::from(&secret);
215    let dalek_keypair = ed25519_dalek::Keypair { secret, public };
216    Ok(Keypair(dalek_keypair))
217}
218
219/// Generates a Keypair using Bip32 Hierarchical Derivation if derivation-path is provided;
220/// otherwise generates the base Bip44 Solana keypair from the seed
221pub fn keypair_from_seed_and_derivation_path(
222    seed: &[u8],
223    derivation_path: Option<DerivationPath>,
224) -> Result<Keypair, Box<dyn error::Error>> {
225    let derivation_path = derivation_path.unwrap_or_default();
226    bip32_derived_keypair(seed, derivation_path).map_err(|err| err.to_string().into())
227}
228
229/// Generates a Keypair using Bip32 Hierarchical Derivation
230fn bip32_derived_keypair(
231    seed: &[u8],
232    derivation_path: DerivationPath,
233) -> Result<Keypair, Bip32Error> {
234    let extended = ed25519_dalek_bip32::ExtendedSecretKey::from_seed(seed)
235        .and_then(|extended| extended.derive(&derivation_path))?;
236    let extended_public_key = extended.public_key();
237    Ok(Keypair(ed25519_dalek::Keypair {
238        secret: extended.secret_key,
239        public: extended_public_key,
240    }))
241}
242
243pub fn generate_seed_from_seed_phrase_and_passphrase(
244    seed_phrase: &str,
245    passphrase: &str,
246) -> Vec<u8> {
247    const PBKDF2_ROUNDS: u32 = 2048;
248    const PBKDF2_BYTES: usize = 64;
249
250    let salt = format!("mnemonic{passphrase}");
251
252    let mut seed = vec![0u8; PBKDF2_BYTES];
253    pbkdf2::pbkdf2::<Hmac<sha2::Sha512>>(
254        seed_phrase.as_bytes(),
255        salt.as_bytes(),
256        PBKDF2_ROUNDS,
257        &mut seed,
258    );
259    seed
260}
261
262pub fn keypair_from_seed_phrase_and_passphrase(
263    seed_phrase: &str,
264    passphrase: &str,
265) -> Result<Keypair, Box<dyn error::Error>> {
266    keypair_from_seed(&generate_seed_from_seed_phrase_and_passphrase(
267        seed_phrase,
268        passphrase,
269    ))
270}
271
272#[cfg(test)]
273mod tests {
274    use {
275        super::*,
276        bip39::{Language, Mnemonic, MnemonicType, Seed},
277        std::{
278            fs::{self, File},
279            mem,
280        },
281    };
282
283    fn tmp_file_path(name: &str) -> String {
284        use std::env;
285        let out_dir = env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
286        let keypair = Keypair::new();
287
288        format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey())
289    }
290
291    #[test]
292    fn test_write_keypair_file() {
293        let outfile = tmp_file_path("test_write_keypair_file.json");
294        let serialized_keypair = write_keypair_file(&Keypair::new(), &outfile).unwrap();
295        let keypair_vec: Vec<u8> = serde_json::from_str(&serialized_keypair).unwrap();
296        assert!(Path::new(&outfile).exists());
297        assert_eq!(
298            keypair_vec,
299            read_keypair_file(&outfile).unwrap().0.to_bytes().to_vec()
300        );
301
302        #[cfg(unix)]
303        {
304            use std::os::unix::fs::PermissionsExt;
305            assert_eq!(
306                File::open(&outfile)
307                    .expect("open")
308                    .metadata()
309                    .expect("metadata")
310                    .permissions()
311                    .mode()
312                    & 0o777,
313                0o600
314            );
315        }
316
317        assert_eq!(
318            read_keypair_file(&outfile).unwrap().pubkey().as_ref().len(),
319            mem::size_of::<Pubkey>()
320        );
321        fs::remove_file(&outfile).unwrap();
322        assert!(!Path::new(&outfile).exists());
323    }
324
325    #[test]
326    fn test_write_keypair_file_overwrite_ok() {
327        let outfile = tmp_file_path("test_write_keypair_file_overwrite_ok.json");
328
329        write_keypair_file(&Keypair::new(), &outfile).unwrap();
330        write_keypair_file(&Keypair::new(), &outfile).unwrap();
331    }
332
333    #[test]
334    fn test_write_keypair_file_truncate() {
335        let outfile = tmp_file_path("test_write_keypair_file_truncate.json");
336
337        write_keypair_file(&Keypair::new(), &outfile).unwrap();
338        read_keypair_file(&outfile).unwrap();
339
340        // Ensure outfile is truncated
341        {
342            let mut f = File::create(&outfile).unwrap();
343            f.write_all(String::from_utf8([b'a'; 2048].to_vec()).unwrap().as_bytes())
344                .unwrap();
345        }
346        write_keypair_file(&Keypair::new(), &outfile).unwrap();
347        read_keypair_file(&outfile).unwrap();
348    }
349
350    #[test]
351    fn test_keypair_from_seed() {
352        let good_seed = vec![0; 32];
353        assert!(keypair_from_seed(&good_seed).is_ok());
354
355        let too_short_seed = vec![0; 31];
356        assert!(keypair_from_seed(&too_short_seed).is_err());
357    }
358
359    #[test]
360    fn test_keypair_from_seed_phrase_and_passphrase() {
361        let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
362        let passphrase = "42";
363        let seed = Seed::new(&mnemonic, passphrase);
364        let expected_keypair = keypair_from_seed(seed.as_bytes()).unwrap();
365        let keypair =
366            keypair_from_seed_phrase_and_passphrase(mnemonic.phrase(), passphrase).unwrap();
367        assert_eq!(keypair.pubkey(), expected_keypair.pubkey());
368    }
369
370    #[test]
371    fn test_keypair() {
372        let keypair = keypair_from_seed(&[0u8; 32]).unwrap();
373        let pubkey = keypair.pubkey();
374        let data = [1u8];
375        let sig = keypair.sign_message(&data);
376
377        // Signer
378        assert_eq!(keypair.try_pubkey().unwrap(), pubkey);
379        assert_eq!(keypair.pubkey(), pubkey);
380        assert_eq!(keypair.try_sign_message(&data).unwrap(), sig);
381        assert_eq!(keypair.sign_message(&data), sig);
382
383        // PartialEq
384        let keypair2 = keypair_from_seed(&[0u8; 32]).unwrap();
385        assert_eq!(keypair, keypair2);
386    }
387}