solana_keypair/
lib.rs

1//! Concrete implementation of a Solana `Signer` from raw bytes
2#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3#[cfg(target_arch = "wasm32")]
4use wasm_bindgen::prelude::*;
5use {
6    ed25519_dalek::Signer as DalekSigner,
7    rand0_7::{rngs::OsRng, CryptoRng, RngCore},
8    solana_pubkey::Pubkey,
9    solana_seed_phrase::generate_seed_from_seed_phrase_and_passphrase,
10    solana_signature::{error::Error as SignatureError, Signature},
11    solana_signer::{EncodableKey, EncodableKeypair, Signer, SignerError},
12    std::{
13        error,
14        io::{Read, Write},
15        path::Path,
16    },
17};
18
19#[cfg(feature = "seed-derivable")]
20pub mod seed_derivable;
21pub mod signable;
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
28pub const KEYPAIR_LENGTH: usize = 64;
29
30impl Keypair {
31    /// Can be used for generating a Keypair without a dependency on `rand` types
32    pub const SECRET_KEY_LENGTH: usize = 32;
33
34    /// Constructs a new, random `Keypair` using a caller-provided RNG
35    #[deprecated(
36        since = "2.2.2",
37        note = "Use `Keypair::new()` instead or generate 32 random bytes and use `Keypair::new_from_array`"
38    )]
39    pub fn generate<R>(csprng: &mut R) -> Self
40    where
41        R: CryptoRng + RngCore,
42    {
43        Self(ed25519_dalek::Keypair::generate(csprng))
44    }
45
46    /// Constructs a new, random `Keypair` using `OsRng`
47    pub fn new() -> Self {
48        let mut rng = OsRng;
49        Self(ed25519_dalek::Keypair::generate(&mut rng))
50    }
51
52    /// Constructs a new `Keypair` using secret key bytes
53    pub fn new_from_array(secret_key: [u8; 32]) -> Self {
54        // unwrap is safe because the only error condition is an incorrect length
55        let secret = ed25519_dalek::SecretKey::from_bytes(&secret_key).unwrap();
56        let public = ed25519_dalek::PublicKey::from(&secret);
57        Self(ed25519_dalek::Keypair { secret, public })
58    }
59
60    /// Recovers a `Keypair` from a byte array
61    #[deprecated(since = "2.2.2", note = "Use Keypair::try_from(&[u8]) instead")]
62    pub fn from_bytes(bytes: &[u8]) -> Result<Self, ed25519_dalek::SignatureError> {
63        Self::try_from(bytes).map_err(ed25519_dalek::SignatureError::from_source)
64    }
65
66    /// Returns this `Keypair` as a byte array
67    pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
68        self.0.to_bytes()
69    }
70
71    /// Recovers a `Keypair` from a base58-encoded string
72    pub fn from_base58_string(s: &str) -> Self {
73        let mut buf = [0u8; ed25519_dalek::KEYPAIR_LENGTH];
74        five8::decode_64(s, &mut buf).unwrap();
75        Self::try_from(&buf[..]).unwrap()
76    }
77
78    /// Returns this `Keypair` as a base58-encoded string
79    pub fn to_base58_string(&self) -> String {
80        let mut out = [0u8; five8::BASE58_ENCODED_64_MAX_LEN];
81        let len = five8::encode_64(&self.0.to_bytes(), &mut out);
82        unsafe { String::from_utf8_unchecked(out[..len as usize].to_vec()) }
83    }
84
85    /// Gets this `Keypair`'s SecretKey
86    #[deprecated(since = "2.2.2", note = "Use secret_bytes()")]
87    pub fn secret(&self) -> &ed25519_dalek::SecretKey {
88        &self.0.secret
89    }
90
91    /// Gets this `Keypair`'s secret key bytes
92    pub fn secret_bytes(&self) -> &[u8; Self::SECRET_KEY_LENGTH] {
93        self.0.secret.as_bytes()
94    }
95
96    /// Allows Keypair cloning
97    ///
98    /// Note that the `Clone` trait is intentionally unimplemented because making a
99    /// second copy of sensitive secret keys in memory is usually a bad idea.
100    ///
101    /// Only use this in tests or when strictly required. Consider using [`std::sync::Arc<Keypair>`]
102    /// instead.
103    pub fn insecure_clone(&self) -> Self {
104        Self(ed25519_dalek::Keypair {
105            // This will never error since self is a valid keypair
106            secret: ed25519_dalek::SecretKey::from_bytes(self.0.secret.as_bytes()).unwrap(),
107            public: self.0.public,
108        })
109    }
110}
111
112impl TryFrom<&[u8]> for Keypair {
113    type Error = SignatureError;
114
115    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
116        if bytes.len() < ed25519_dalek::KEYPAIR_LENGTH {
117            return Err(SignatureError::from_source(String::from(
118                "candidate keypair byte array is too short",
119            )));
120        }
121        let secret =
122            ed25519_dalek::SecretKey::from_bytes(&bytes[..ed25519_dalek::SECRET_KEY_LENGTH])
123                .map_err(SignatureError::from_source)?;
124        let public =
125            ed25519_dalek::PublicKey::from_bytes(&bytes[ed25519_dalek::SECRET_KEY_LENGTH..])
126                .map_err(SignatureError::from_source)?;
127        let expected_public = ed25519_dalek::PublicKey::from(&secret);
128        (public == expected_public)
129            .then_some(Self(ed25519_dalek::Keypair { secret, public }))
130            .ok_or(SignatureError::from_source(String::from(
131                "keypair bytes do not specify same pubkey as derived from their secret key",
132            )))
133    }
134}
135
136#[cfg(target_arch = "wasm32")]
137#[allow(non_snake_case)]
138#[wasm_bindgen]
139impl Keypair {
140    /// Create a new `Keypair `
141    #[wasm_bindgen(constructor)]
142    pub fn constructor() -> Keypair {
143        Keypair::new()
144    }
145
146    /// Convert a `Keypair` to a `Uint8Array`
147    pub fn toBytes(&self) -> Box<[u8]> {
148        self.to_bytes().into()
149    }
150
151    /// Recover a `Keypair` from a `Uint8Array`
152    pub fn fromBytes(bytes: &[u8]) -> Result<Keypair, JsValue> {
153        Keypair::try_from(bytes).map_err(|e| e.to_string().into())
154    }
155
156    /// Return the `Pubkey` for this `Keypair`
157    #[wasm_bindgen(js_name = pubkey)]
158    pub fn js_pubkey(&self) -> Pubkey {
159        // `wasm_bindgen` does not support traits (`Signer) yet
160        self.pubkey()
161    }
162}
163
164// This should also be marked deprecated, but it's not possible to put a
165// `#[deprecated]` attribute on a trait implementation.
166// Remove during the next major version bump.
167impl From<ed25519_dalek::Keypair> for Keypair {
168    fn from(value: ed25519_dalek::Keypair) -> Self {
169        Self(value)
170    }
171}
172
173#[cfg(test)]
174static_assertions::const_assert_eq!(Keypair::SECRET_KEY_LENGTH, ed25519_dalek::SECRET_KEY_LENGTH);
175
176impl Signer for Keypair {
177    #[inline]
178    fn pubkey(&self) -> Pubkey {
179        Pubkey::from(self.0.public.to_bytes())
180    }
181
182    fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
183        Ok(self.pubkey())
184    }
185
186    fn sign_message(&self, message: &[u8]) -> Signature {
187        Signature::from(self.0.sign(message).to_bytes())
188    }
189
190    fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
191        Ok(self.sign_message(message))
192    }
193
194    fn is_interactive(&self) -> bool {
195        false
196    }
197}
198
199impl<T> PartialEq<T> for Keypair
200where
201    T: Signer,
202{
203    fn eq(&self, other: &T) -> bool {
204        self.pubkey() == other.pubkey()
205    }
206}
207
208impl EncodableKey for Keypair {
209    fn read<R: Read>(reader: &mut R) -> Result<Self, Box<dyn error::Error>> {
210        read_keypair(reader)
211    }
212
213    fn write<W: Write>(&self, writer: &mut W) -> Result<String, Box<dyn error::Error>> {
214        write_keypair(self, writer)
215    }
216}
217
218impl EncodableKeypair for Keypair {
219    type Pubkey = Pubkey;
220
221    /// Returns the associated pubkey. Use this function specifically for settings that involve
222    /// reading or writing pubkeys. For other settings, use `Signer::pubkey()` instead.
223    fn encodable_pubkey(&self) -> Self::Pubkey {
224        self.pubkey()
225    }
226}
227
228/// Reads a JSON-encoded `Keypair` from a `Reader` implementor
229pub fn read_keypair<R: Read>(reader: &mut R) -> Result<Keypair, Box<dyn error::Error>> {
230    let mut buffer = String::new();
231    reader.read_to_string(&mut buffer)?;
232    let trimmed = buffer.trim();
233    if !trimmed.starts_with('[') || !trimmed.ends_with(']') {
234        return Err(std::io::Error::new(
235            std::io::ErrorKind::InvalidData,
236            "Input must be a JSON array",
237        )
238        .into());
239    }
240    // we already checked that the string has at least two chars,
241    // so 1..trimmed.len() - 1 won't be out of bounds
242    #[allow(clippy::arithmetic_side_effects)]
243    let contents = &trimmed[1..trimmed.len() - 1];
244    let elements_vec: Vec<&str> = contents.split(',').map(|s| s.trim()).collect();
245    let len = elements_vec.len();
246    let elements: [&str; ed25519_dalek::KEYPAIR_LENGTH] =
247        elements_vec.try_into().map_err(|_| {
248            std::io::Error::new(
249                std::io::ErrorKind::InvalidData,
250                format!(
251                    "Expected {} elements, found {}",
252                    ed25519_dalek::KEYPAIR_LENGTH,
253                    len
254                ),
255            )
256        })?;
257    let mut out = [0u8; ed25519_dalek::KEYPAIR_LENGTH];
258    for (idx, element) in elements.into_iter().enumerate() {
259        let parsed: u8 = element.parse()?;
260        out[idx] = parsed;
261    }
262    Keypair::try_from(&out[..]).map_err(|e| std::io::Error::other(e.to_string()).into())
263}
264
265/// Reads a `Keypair` from a file
266pub fn read_keypair_file<F: AsRef<Path>>(path: F) -> Result<Keypair, Box<dyn error::Error>> {
267    Keypair::read_from_file(path)
268}
269
270/// Writes a `Keypair` to a `Write` implementor with JSON-encoding
271pub fn write_keypair<W: Write>(
272    keypair: &Keypair,
273    writer: &mut W,
274) -> Result<String, Box<dyn error::Error>> {
275    let keypair_bytes = keypair.0.to_bytes();
276    let mut result = Vec::with_capacity(64 * 4 + 2); // Estimate capacity: 64 numbers * (up to 3 digits + 1 comma) + 2 brackets
277
278    result.push(b'['); // Opening bracket
279
280    for (i, &num) in keypair_bytes.iter().enumerate() {
281        if i > 0 {
282            result.push(b','); // Comma separator for all elements except the first
283        }
284
285        // Convert number to string and then to bytes
286        let num_str = num.to_string();
287        result.extend_from_slice(num_str.as_bytes());
288    }
289
290    result.push(b']'); // Closing bracket
291    writer.write_all(&result)?;
292    let as_string = String::from_utf8(result)?;
293    Ok(as_string)
294}
295
296/// Writes a `Keypair` to a file with JSON-encoding
297pub fn write_keypair_file<F: AsRef<Path>>(
298    keypair: &Keypair,
299    outfile: F,
300) -> Result<String, Box<dyn error::Error>> {
301    keypair.write_to_file(outfile)
302}
303
304/// Constructs a `Keypair` from caller-provided seed entropy
305pub fn keypair_from_seed(seed: &[u8]) -> Result<Keypair, Box<dyn error::Error>> {
306    if seed.len() < ed25519_dalek::SECRET_KEY_LENGTH {
307        return Err("Seed is too short".into());
308    }
309    let secret = ed25519_dalek::SecretKey::from_bytes(&seed[..ed25519_dalek::SECRET_KEY_LENGTH])
310        .map_err(|e| e.to_string())?;
311    let public = ed25519_dalek::PublicKey::from(&secret);
312    let dalek_keypair = ed25519_dalek::Keypair { secret, public };
313    Ok(Keypair(dalek_keypair))
314}
315
316pub fn keypair_from_seed_phrase_and_passphrase(
317    seed_phrase: &str,
318    passphrase: &str,
319) -> Result<Keypair, Box<dyn std::error::Error>> {
320    keypair_from_seed(&generate_seed_from_seed_phrase_and_passphrase(
321        seed_phrase,
322        passphrase,
323    ))
324}
325
326#[cfg(test)]
327mod tests {
328    use {
329        super::*,
330        bip39::{Language, Mnemonic, MnemonicType, Seed},
331        solana_signer::unique_signers,
332        std::{
333            fs::{self, File},
334            mem,
335        },
336    };
337
338    fn tmp_file_path(name: &str) -> String {
339        use std::env;
340        let out_dir = env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
341        let keypair = Keypair::new();
342
343        format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey())
344    }
345
346    #[test]
347    fn test_write_keypair_file() {
348        let outfile = tmp_file_path("test_write_keypair_file.json");
349        let serialized_keypair = write_keypair_file(&Keypair::new(), &outfile).unwrap();
350        let keypair_vec: Vec<u8> = serde_json::from_str(&serialized_keypair).unwrap();
351        assert!(Path::new(&outfile).exists());
352        assert_eq!(
353            keypair_vec,
354            read_keypair_file(&outfile).unwrap().0.to_bytes().to_vec()
355        );
356
357        #[cfg(unix)]
358        {
359            use std::os::unix::fs::PermissionsExt;
360            assert_eq!(
361                File::open(&outfile)
362                    .expect("open")
363                    .metadata()
364                    .expect("metadata")
365                    .permissions()
366                    .mode()
367                    & 0o777,
368                0o600
369            );
370        }
371
372        assert_eq!(
373            read_keypair_file(&outfile).unwrap().pubkey().as_ref().len(),
374            mem::size_of::<Pubkey>()
375        );
376        fs::remove_file(&outfile).unwrap();
377        assert!(!Path::new(&outfile).exists());
378    }
379
380    #[test]
381    fn test_write_keypair_file_overwrite_ok() {
382        let outfile = tmp_file_path("test_write_keypair_file_overwrite_ok.json");
383
384        write_keypair_file(&Keypair::new(), &outfile).unwrap();
385        write_keypair_file(&Keypair::new(), &outfile).unwrap();
386    }
387
388    #[test]
389    fn test_write_keypair_file_truncate() {
390        let outfile = tmp_file_path("test_write_keypair_file_truncate.json");
391
392        write_keypair_file(&Keypair::new(), &outfile).unwrap();
393        read_keypair_file(&outfile).unwrap();
394
395        // Ensure outfile is truncated
396        {
397            let mut f = File::create(&outfile).unwrap();
398            f.write_all(String::from_utf8([b'a'; 2048].to_vec()).unwrap().as_bytes())
399                .unwrap();
400        }
401        write_keypair_file(&Keypair::new(), &outfile).unwrap();
402        read_keypair_file(&outfile).unwrap();
403    }
404
405    #[test]
406    fn test_keypair_from_seed() {
407        let good_seed = vec![0; 32];
408        assert!(keypair_from_seed(&good_seed).is_ok());
409
410        let too_short_seed = vec![0; 31];
411        assert!(keypair_from_seed(&too_short_seed).is_err());
412    }
413
414    #[test]
415    fn test_keypair() {
416        let keypair = keypair_from_seed(&[0u8; 32]).unwrap();
417        let pubkey = keypair.pubkey();
418        let data = [1u8];
419        let sig = keypair.sign_message(&data);
420
421        // Signer
422        assert_eq!(keypair.try_pubkey().unwrap(), pubkey);
423        assert_eq!(keypair.pubkey(), pubkey);
424        assert_eq!(keypair.try_sign_message(&data).unwrap(), sig);
425        assert_eq!(keypair.sign_message(&data), sig);
426
427        // PartialEq
428        let keypair2 = keypair_from_seed(&[0u8; 32]).unwrap();
429        assert_eq!(keypair, keypair2);
430    }
431
432    fn pubkeys(signers: &[&dyn Signer]) -> Vec<Pubkey> {
433        signers.iter().map(|x| x.pubkey()).collect()
434    }
435
436    #[test]
437    fn test_unique_signers() {
438        let alice = Keypair::new();
439        let bob = Keypair::new();
440        assert_eq!(
441            pubkeys(&unique_signers(vec![&alice, &bob, &alice])),
442            pubkeys(&[&alice, &bob])
443        );
444    }
445
446    #[test]
447    fn test_containers() {
448        use std::{rc::Rc, sync::Arc};
449
450        struct Foo<S: Signer> {
451            #[allow(unused)]
452            signer: S,
453        }
454
455        fn foo(_s: impl Signer) {}
456
457        let _arc_signer = Foo {
458            signer: Arc::new(Keypair::new()),
459        };
460        foo(Arc::new(Keypair::new()));
461
462        let _rc_signer = Foo {
463            signer: Rc::new(Keypair::new()),
464        };
465        foo(Rc::new(Keypair::new()));
466
467        let _ref_signer = Foo {
468            signer: &Keypair::new(),
469        };
470        foo(Keypair::new());
471
472        let _box_signer = Foo {
473            signer: Box::new(Keypair::new()),
474        };
475        foo(Box::new(Keypair::new()));
476
477        let _signer = Foo {
478            signer: Keypair::new(),
479        };
480        foo(Keypair::new());
481    }
482
483    #[test]
484    fn test_keypair_from_seed_phrase_and_passphrase() {
485        let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
486        let passphrase = "42";
487        let seed = Seed::new(&mnemonic, passphrase);
488        let expected_keypair = keypair_from_seed(seed.as_bytes()).unwrap();
489        let keypair =
490            keypair_from_seed_phrase_and_passphrase(mnemonic.phrase(), passphrase).unwrap();
491        assert_eq!(keypair.pubkey(), expected_keypair.pubkey());
492    }
493}