1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3use {
4 ed25519_dalek::Signer as DalekSigner,
5 rand::rngs::OsRng,
6 solana_seed_phrase::generate_seed_from_seed_phrase_and_passphrase,
7 solana_signer::SignerError,
8 std::{
9 error,
10 io::{Read, Write},
11 path::Path,
12 },
13};
14pub use {
15 solana_pubkey::Pubkey,
16 solana_signature::{error::Error as SignatureError, Signature},
17 solana_signer::{EncodableKey, EncodableKeypair, Signer},
18};
19
20#[cfg(feature = "seed-derivable")]
21pub mod seed_derivable;
22pub mod signable;
23
24#[derive(Debug)]
26pub struct Keypair(ed25519_dalek::SigningKey);
27
28pub const KEYPAIR_LENGTH: usize = 64;
29
30impl Keypair {
31 pub const SECRET_KEY_LENGTH: usize = 32;
33
34 #[allow(clippy::new_without_default)]
36 pub fn new() -> Self {
37 let mut rng = OsRng;
38 Self(ed25519_dalek::SigningKey::generate(&mut rng))
39 }
40
41 pub fn new_from_array(secret_key: [u8; 32]) -> Self {
43 Self(ed25519_dalek::SigningKey::from(secret_key))
44 }
45
46 pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
48 self.0.to_keypair_bytes()
49 }
50
51 pub fn from_base58_string(s: &str) -> Self {
53 let mut buf = [0u8; ed25519_dalek::KEYPAIR_LENGTH];
54 five8::decode_64(s, &mut buf).unwrap();
55 Self::try_from(&buf[..]).unwrap()
56 }
57
58 pub fn to_base58_string(&self) -> String {
60 let mut out = [0u8; five8::BASE58_ENCODED_64_MAX_LEN];
61 let len = five8::encode_64(&self.to_bytes(), &mut out);
62 unsafe { String::from_utf8_unchecked(out[..len as usize].to_vec()) }
63 }
64
65 pub fn secret_bytes(&self) -> &[u8; Self::SECRET_KEY_LENGTH] {
67 self.0.as_bytes()
68 }
69
70 pub fn insecure_clone(&self) -> Self {
78 Self(self.0.clone())
79 }
80}
81
82impl TryFrom<&[u8]> for Keypair {
83 type Error = SignatureError;
84
85 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
86 let keypair_bytes: &[u8; ed25519_dalek::KEYPAIR_LENGTH] =
87 bytes.try_into().map_err(|_| {
88 SignatureError::from_source(String::from(
89 "candidate keypair byte array is the wrong length",
90 ))
91 })?;
92 ed25519_dalek::SigningKey::from_keypair_bytes(keypair_bytes)
93 .map_err(|_| {
94 SignatureError::from_source(String::from(
95 "keypair bytes do not specify same pubkey as derived from their secret key",
96 ))
97 })
98 .map(Self)
99 }
100}
101
102#[cfg(test)]
103static_assertions::const_assert_eq!(Keypair::SECRET_KEY_LENGTH, ed25519_dalek::SECRET_KEY_LENGTH);
104
105impl Signer for Keypair {
106 #[inline]
107 fn pubkey(&self) -> Pubkey {
108 Pubkey::from(self.0.verifying_key().to_bytes())
109 }
110
111 fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
112 Ok(self.pubkey())
113 }
114
115 fn sign_message(&self, message: &[u8]) -> Signature {
116 Signature::from(self.0.sign(message).to_bytes())
117 }
118
119 fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
120 Ok(self.sign_message(message))
121 }
122
123 fn is_interactive(&self) -> bool {
124 false
125 }
126}
127
128impl<T> PartialEq<T> for Keypair
129where
130 T: Signer,
131{
132 fn eq(&self, other: &T) -> bool {
133 self.pubkey() == other.pubkey()
134 }
135}
136
137impl EncodableKey for Keypair {
138 fn read<R: Read>(reader: &mut R) -> Result<Self, Box<dyn error::Error>> {
139 read_keypair(reader)
140 }
141
142 fn write<W: Write>(&self, writer: &mut W) -> Result<String, Box<dyn error::Error>> {
143 write_keypair(self, writer)
144 }
145}
146
147impl EncodableKeypair for Keypair {
148 type Pubkey = Pubkey;
149
150 fn encodable_pubkey(&self) -> Self::Pubkey {
153 self.pubkey()
154 }
155}
156
157pub fn read_keypair<R: Read>(reader: &mut R) -> Result<Keypair, Box<dyn error::Error>> {
159 let mut buffer = String::new();
160 reader.read_to_string(&mut buffer)?;
161 let trimmed = buffer.trim();
162 if !trimmed.starts_with('[') || !trimmed.ends_with(']') {
163 return Err(std::io::Error::new(
164 std::io::ErrorKind::InvalidData,
165 "Input must be a JSON array",
166 )
167 .into());
168 }
169 #[allow(clippy::arithmetic_side_effects)]
172 let contents = &trimmed[1..trimmed.len() - 1];
173 let elements_vec: Vec<&str> = contents.split(',').map(|s| s.trim()).collect();
174 let len = elements_vec.len();
175 let elements: [&str; ed25519_dalek::KEYPAIR_LENGTH] =
176 elements_vec.try_into().map_err(|_| {
177 std::io::Error::new(
178 std::io::ErrorKind::InvalidData,
179 format!(
180 "Expected {} elements, found {}",
181 ed25519_dalek::KEYPAIR_LENGTH,
182 len
183 ),
184 )
185 })?;
186 let mut out = [0u8; ed25519_dalek::KEYPAIR_LENGTH];
187 for (idx, element) in elements.into_iter().enumerate() {
188 let parsed: u8 = element.parse()?;
189 out[idx] = parsed;
190 }
191 Keypair::try_from(&out[..]).map_err(|e| std::io::Error::other(e.to_string()).into())
192}
193
194pub fn read_keypair_file<F: AsRef<Path>>(path: F) -> Result<Keypair, Box<dyn error::Error>> {
196 Keypair::read_from_file(path)
197}
198
199pub fn write_keypair<W: Write>(
201 keypair: &Keypair,
202 writer: &mut W,
203) -> Result<String, Box<dyn error::Error>> {
204 let keypair_bytes = keypair.to_bytes();
205 let mut result = Vec::with_capacity(64 * 4 + 2); result.push(b'['); for (i, &num) in keypair_bytes.iter().enumerate() {
210 if i > 0 {
211 result.push(b','); }
213
214 let num_str = num.to_string();
216 result.extend_from_slice(num_str.as_bytes());
217 }
218
219 result.push(b']'); writer.write_all(&result)?;
221 let as_string = String::from_utf8(result)?;
222 Ok(as_string)
223}
224
225pub fn write_keypair_file<F: AsRef<Path>>(
227 keypair: &Keypair,
228 outfile: F,
229) -> Result<String, Box<dyn error::Error>> {
230 keypair.write_to_file(outfile)
231}
232
233pub fn keypair_from_seed(seed: &[u8]) -> Result<Keypair, Box<dyn error::Error>> {
235 if seed.len() < ed25519_dalek::SECRET_KEY_LENGTH {
236 return Err("Seed is too short".into());
237 }
238 let secret_key = ed25519_dalek::SecretKey::try_from(&seed[..ed25519_dalek::SECRET_KEY_LENGTH])?;
240 Ok(Keypair(ed25519_dalek::SigningKey::from(secret_key)))
241}
242
243pub fn keypair_from_seed_phrase_and_passphrase(
244 seed_phrase: &str,
245 passphrase: &str,
246) -> Result<Keypair, Box<dyn core::error::Error>> {
247 keypair_from_seed(&generate_seed_from_seed_phrase_and_passphrase(
248 seed_phrase,
249 passphrase,
250 ))
251}
252
253#[cfg(test)]
254mod tests {
255 use {
256 super::*,
257 bip39::{Language, Mnemonic, MnemonicType, Seed},
258 solana_signer::unique_signers,
259 std::{
260 fs::{self, File},
261 mem,
262 },
263 };
264
265 fn tmp_file_path(name: &str) -> String {
266 use std::env;
267 let out_dir = env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
268 let keypair = Keypair::new();
269
270 format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey())
271 }
272
273 #[test]
274 fn test_write_keypair_file() {
275 let outfile = tmp_file_path("test_write_keypair_file.json");
276 let serialized_keypair = write_keypair_file(&Keypair::new(), &outfile).unwrap();
277 let keypair_vec: Vec<u8> = serde_json::from_str(&serialized_keypair).unwrap();
278 assert!(Path::new(&outfile).exists());
279 assert_eq!(
280 keypair_vec,
281 read_keypair_file(&outfile).unwrap().to_bytes().to_vec()
282 );
283
284 #[cfg(unix)]
285 {
286 use std::os::unix::fs::PermissionsExt;
287 assert_eq!(
288 File::open(&outfile)
289 .expect("open")
290 .metadata()
291 .expect("metadata")
292 .permissions()
293 .mode()
294 & 0o777,
295 0o600
296 );
297 }
298
299 assert_eq!(
300 read_keypair_file(&outfile).unwrap().pubkey().as_ref().len(),
301 mem::size_of::<Pubkey>()
302 );
303 fs::remove_file(&outfile).unwrap();
304 assert!(!Path::new(&outfile).exists());
305 }
306
307 #[test]
308 fn test_write_keypair_file_overwrite_ok() {
309 let outfile = tmp_file_path("test_write_keypair_file_overwrite_ok.json");
310
311 write_keypair_file(&Keypair::new(), &outfile).unwrap();
312 write_keypair_file(&Keypair::new(), &outfile).unwrap();
313 }
314
315 #[test]
316 fn test_write_keypair_file_truncate() {
317 let outfile = tmp_file_path("test_write_keypair_file_truncate.json");
318
319 write_keypair_file(&Keypair::new(), &outfile).unwrap();
320 read_keypair_file(&outfile).unwrap();
321
322 {
324 let mut f = File::create(&outfile).unwrap();
325 f.write_all(String::from_utf8([b'a'; 2048].to_vec()).unwrap().as_bytes())
326 .unwrap();
327 }
328 write_keypair_file(&Keypair::new(), &outfile).unwrap();
329 read_keypair_file(&outfile).unwrap();
330 }
331
332 #[test]
333 fn test_keypair_from_seed() {
334 let good_seed = vec![0; 32];
335 assert!(keypair_from_seed(&good_seed).is_ok());
336
337 let too_short_seed = vec![0; 31];
338 assert!(keypair_from_seed(&too_short_seed).is_err());
339 }
340
341 #[test]
342 fn test_keypair() {
343 let keypair = keypair_from_seed(&[0u8; 32]).unwrap();
344 let pubkey = keypair.pubkey();
345 let data = [1u8];
346 let sig = keypair.sign_message(&data);
347
348 assert_eq!(keypair.try_pubkey().unwrap(), pubkey);
350 assert_eq!(keypair.pubkey(), pubkey);
351 assert_eq!(keypair.try_sign_message(&data).unwrap(), sig);
352 assert_eq!(keypair.sign_message(&data), sig);
353
354 let keypair2 = keypair_from_seed(&[0u8; 32]).unwrap();
356 assert_eq!(keypair, keypair2);
357 }
358
359 fn pubkeys(signers: &[&dyn Signer]) -> Vec<Pubkey> {
360 signers.iter().map(|x| x.pubkey()).collect()
361 }
362
363 #[test]
364 fn test_unique_signers() {
365 let alice = Keypair::new();
366 let bob = Keypair::new();
367 assert_eq!(
368 pubkeys(&unique_signers(vec![&alice, &bob, &alice])),
369 pubkeys(&[&alice, &bob])
370 );
371 }
372
373 #[test]
374 fn test_containers() {
375 use std::{rc::Rc, sync::Arc};
376
377 struct Foo<S: Signer> {
378 #[allow(unused)]
379 signer: S,
380 }
381
382 fn foo(_s: impl Signer) {}
383
384 let _arc_signer = Foo {
385 signer: Arc::new(Keypair::new()),
386 };
387 foo(Arc::new(Keypair::new()));
388
389 let _rc_signer = Foo {
390 signer: Rc::new(Keypair::new()),
391 };
392 foo(Rc::new(Keypair::new()));
393
394 let _ref_signer = Foo {
395 signer: &Keypair::new(),
396 };
397 foo(Keypair::new());
398
399 let _box_signer = Foo {
400 signer: Box::new(Keypair::new()),
401 };
402 foo(Box::new(Keypair::new()));
403
404 let _signer = Foo {
405 signer: Keypair::new(),
406 };
407 foo(Keypair::new());
408 }
409
410 #[test]
411 fn test_keypair_from_seed_phrase_and_passphrase() {
412 let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
413 let passphrase = "42";
414 let seed = Seed::new(&mnemonic, passphrase);
415 let expected_keypair = keypair_from_seed(seed.as_bytes()).unwrap();
416 let keypair =
417 keypair_from_seed_phrase_and_passphrase(mnemonic.phrase(), passphrase).unwrap();
418 assert_eq!(keypair.pubkey(), expected_keypair.pubkey());
419 }
420
421 #[test]
422 fn test_base58() {
423 let keypair = keypair_from_seed(&[0u8; 32]).unwrap();
424 let as_base58 = keypair.to_base58_string();
425 let parsed = Keypair::from_base58_string(&as_base58);
426 assert_eq!(keypair, parsed);
427 }
428}