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