1#![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#[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 pub const SECRET_KEY_LENGTH: usize = 32;
33
34 #[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 pub fn new() -> Self {
48 let mut rng = OsRng;
49 Self(ed25519_dalek::Keypair::generate(&mut rng))
50 }
51
52 pub fn new_from_array(secret_key: [u8; 32]) -> Self {
54 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 #[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 pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
68 self.0.to_bytes()
69 }
70
71 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 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 #[deprecated(since = "2.2.2", note = "Use secret_bytes()")]
87 pub fn secret(&self) -> &ed25519_dalek::SecretKey {
88 &self.0.secret
89 }
90
91 pub fn secret_bytes(&self) -> &[u8; Self::SECRET_KEY_LENGTH] {
93 self.0.secret.as_bytes()
94 }
95
96 pub fn insecure_clone(&self) -> Self {
104 Self(ed25519_dalek::Keypair {
105 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 #[wasm_bindgen(constructor)]
142 pub fn constructor() -> Keypair {
143 Keypair::new()
144 }
145
146 pub fn toBytes(&self) -> Box<[u8]> {
148 self.to_bytes().into()
149 }
150
151 pub fn fromBytes(bytes: &[u8]) -> Result<Keypair, JsValue> {
153 Keypair::try_from(bytes).map_err(|e| e.to_string().into())
154 }
155
156 #[wasm_bindgen(js_name = pubkey)]
158 pub fn js_pubkey(&self) -> Pubkey {
159 self.pubkey()
161 }
162}
163
164impl 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 fn encodable_pubkey(&self) -> Self::Pubkey {
224 self.pubkey()
225 }
226}
227
228pub 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 #[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
265pub fn read_keypair_file<F: AsRef<Path>>(path: F) -> Result<Keypair, Box<dyn error::Error>> {
267 Keypair::read_from_file(path)
268}
269
270pub 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); result.push(b'['); for (i, &num) in keypair_bytes.iter().enumerate() {
281 if i > 0 {
282 result.push(b','); }
284
285 let num_str = num.to_string();
287 result.extend_from_slice(num_str.as_bytes());
288 }
289
290 result.push(b']'); writer.write_all(&result)?;
292 let as_string = String::from_utf8(result)?;
293 Ok(as_string)
294}
295
296pub 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
304pub 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 {
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 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 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}