1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3#[cfg(target_arch = "wasm32")]
4use wasm_bindgen::prelude::*;
5use {
6 clone_solana_pubkey::Pubkey,
7 clone_solana_seed_phrase::generate_seed_from_seed_phrase_and_passphrase,
8 clone_solana_signature::Signature,
9 clone_solana_signer::{EncodableKey, EncodableKeypair, Signer, SignerError},
10 ed25519_dalek::Signer as DalekSigner,
11 rand0_7::{rngs::OsRng, CryptoRng, RngCore},
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
28impl Keypair {
29 pub const SECRET_KEY_LENGTH: usize = 32;
31
32 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 pub fn new() -> Self {
42 let mut rng = OsRng;
43 Self::generate(&mut rng)
44 }
45
46 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 pub fn to_bytes(&self) -> [u8; 64] {
67 self.0.to_bytes()
68 }
69
70 pub fn from_base58_string(s: &str) -> Self {
72 let mut buf = [0u8; ed25519_dalek::KEYPAIR_LENGTH];
73 bs58::decode(s).onto(&mut buf).unwrap();
74 Self::from_bytes(&buf).unwrap()
75 }
76
77 pub fn to_base58_string(&self) -> String {
79 bs58::encode(&self.0.to_bytes()).into_string()
80 }
81
82 pub fn secret(&self) -> &ed25519_dalek::SecretKey {
84 &self.0.secret
85 }
86
87 pub fn insecure_clone(&self) -> Self {
95 Self(ed25519_dalek::Keypair {
96 secret: ed25519_dalek::SecretKey::from_bytes(self.0.secret.as_bytes()).unwrap(),
98 public: self.0.public,
99 })
100 }
101}
102
103#[cfg(target_arch = "wasm32")]
104#[allow(non_snake_case)]
105#[wasm_bindgen]
106impl Keypair {
107 #[wasm_bindgen(constructor)]
109 pub fn constructor() -> Keypair {
110 Keypair::new()
111 }
112
113 pub fn toBytes(&self) -> Box<[u8]> {
115 self.to_bytes().into()
116 }
117
118 pub fn fromBytes(bytes: &[u8]) -> Result<Keypair, JsValue> {
120 Keypair::from_bytes(bytes).map_err(|e| e.to_string().into())
121 }
122
123 #[wasm_bindgen(js_name = pubkey)]
125 pub fn js_pubkey(&self) -> Pubkey {
126 self.pubkey()
128 }
129}
130
131impl From<ed25519_dalek::Keypair> for Keypair {
132 fn from(value: ed25519_dalek::Keypair) -> Self {
133 Self(value)
134 }
135}
136
137#[cfg(test)]
138static_assertions::const_assert_eq!(Keypair::SECRET_KEY_LENGTH, ed25519_dalek::SECRET_KEY_LENGTH);
139
140impl Signer for Keypair {
141 #[inline]
142 fn pubkey(&self) -> Pubkey {
143 Pubkey::from(self.0.public.to_bytes())
144 }
145
146 fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
147 Ok(self.pubkey())
148 }
149
150 fn sign_message(&self, message: &[u8]) -> Signature {
151 Signature::from(self.0.sign(message).to_bytes())
152 }
153
154 fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
155 Ok(self.sign_message(message))
156 }
157
158 fn is_interactive(&self) -> bool {
159 false
160 }
161}
162
163impl<T> PartialEq<T> for Keypair
164where
165 T: Signer,
166{
167 fn eq(&self, other: &T) -> bool {
168 self.pubkey() == other.pubkey()
169 }
170}
171
172impl EncodableKey for Keypair {
173 fn read<R: Read>(reader: &mut R) -> Result<Self, Box<dyn error::Error>> {
174 read_keypair(reader)
175 }
176
177 fn write<W: Write>(&self, writer: &mut W) -> Result<String, Box<dyn error::Error>> {
178 write_keypair(self, writer)
179 }
180}
181
182impl EncodableKeypair for Keypair {
183 type Pubkey = Pubkey;
184
185 fn encodable_pubkey(&self) -> Self::Pubkey {
188 self.pubkey()
189 }
190}
191
192pub fn read_keypair<R: Read>(reader: &mut R) -> Result<Keypair, Box<dyn error::Error>> {
194 let mut buffer = String::new();
195 reader.read_to_string(&mut buffer)?;
196 let trimmed = buffer.trim();
197 if !trimmed.starts_with('[') || !trimmed.ends_with(']') {
198 return Err(std::io::Error::new(
199 std::io::ErrorKind::InvalidData,
200 "Input must be a JSON array",
201 )
202 .into());
203 }
204 #[allow(clippy::arithmetic_side_effects)]
207 let contents = &trimmed[1..trimmed.len() - 1];
208 let elements_vec: Vec<&str> = contents.split(',').map(|s| s.trim()).collect();
209 let len = elements_vec.len();
210 let elements: [&str; ed25519_dalek::KEYPAIR_LENGTH] =
211 elements_vec.try_into().map_err(|_| {
212 std::io::Error::new(
213 std::io::ErrorKind::InvalidData,
214 format!(
215 "Expected {} elements, found {}",
216 ed25519_dalek::KEYPAIR_LENGTH,
217 len
218 ),
219 )
220 })?;
221 let mut out = [0u8; ed25519_dalek::KEYPAIR_LENGTH];
222 for (idx, element) in elements.into_iter().enumerate() {
223 let parsed: u8 = element.parse()?;
224 out[idx] = parsed;
225 }
226 Keypair::from_bytes(&out)
227 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()).into())
228}
229
230pub fn read_keypair_file<F: AsRef<Path>>(path: F) -> Result<Keypair, Box<dyn error::Error>> {
232 Keypair::read_from_file(path)
233}
234
235pub fn write_keypair<W: Write>(
237 keypair: &Keypair,
238 writer: &mut W,
239) -> Result<String, Box<dyn error::Error>> {
240 let keypair_bytes = keypair.0.to_bytes();
241 let mut result = Vec::with_capacity(64 * 4 + 2); result.push(b'['); for (i, &num) in keypair_bytes.iter().enumerate() {
246 if i > 0 {
247 result.push(b','); }
249
250 let num_str = num.to_string();
252 result.extend_from_slice(num_str.as_bytes());
253 }
254
255 result.push(b']'); writer.write_all(&result)?;
257 let as_string = String::from_utf8(result)?;
258 Ok(as_string)
259}
260
261pub fn write_keypair_file<F: AsRef<Path>>(
263 keypair: &Keypair,
264 outfile: F,
265) -> Result<String, Box<dyn error::Error>> {
266 keypair.write_to_file(outfile)
267}
268
269pub fn keypair_from_seed(seed: &[u8]) -> Result<Keypair, Box<dyn error::Error>> {
271 if seed.len() < ed25519_dalek::SECRET_KEY_LENGTH {
272 return Err("Seed is too short".into());
273 }
274 let secret = ed25519_dalek::SecretKey::from_bytes(&seed[..ed25519_dalek::SECRET_KEY_LENGTH])
275 .map_err(|e| e.to_string())?;
276 let public = ed25519_dalek::PublicKey::from(&secret);
277 let dalek_keypair = ed25519_dalek::Keypair { secret, public };
278 Ok(Keypair(dalek_keypair))
279}
280
281pub fn keypair_from_seed_phrase_and_passphrase(
282 seed_phrase: &str,
283 passphrase: &str,
284) -> Result<Keypair, Box<dyn std::error::Error>> {
285 keypair_from_seed(&generate_seed_from_seed_phrase_and_passphrase(
286 seed_phrase,
287 passphrase,
288 ))
289}
290
291#[cfg(test)]
292mod tests {
293 use {
294 super::*,
295 bip39::{Language, Mnemonic, MnemonicType, Seed},
296 clone_solana_signer::unique_signers,
297 std::{
298 fs::{self, File},
299 mem,
300 },
301 };
302
303 fn tmp_file_path(name: &str) -> String {
304 use std::env;
305 let out_dir = env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
306 let keypair = Keypair::new();
307
308 format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey())
309 }
310
311 #[test]
312 fn test_write_keypair_file() {
313 let outfile = tmp_file_path("test_write_keypair_file.json");
314 let serialized_keypair = write_keypair_file(&Keypair::new(), &outfile).unwrap();
315 let keypair_vec: Vec<u8> = serde_json::from_str(&serialized_keypair).unwrap();
316 assert!(Path::new(&outfile).exists());
317 assert_eq!(
318 keypair_vec,
319 read_keypair_file(&outfile).unwrap().0.to_bytes().to_vec()
320 );
321
322 #[cfg(unix)]
323 {
324 use std::os::unix::fs::PermissionsExt;
325 assert_eq!(
326 File::open(&outfile)
327 .expect("open")
328 .metadata()
329 .expect("metadata")
330 .permissions()
331 .mode()
332 & 0o777,
333 0o600
334 );
335 }
336
337 assert_eq!(
338 read_keypair_file(&outfile).unwrap().pubkey().as_ref().len(),
339 mem::size_of::<Pubkey>()
340 );
341 fs::remove_file(&outfile).unwrap();
342 assert!(!Path::new(&outfile).exists());
343 }
344
345 #[test]
346 fn test_write_keypair_file_overwrite_ok() {
347 let outfile = tmp_file_path("test_write_keypair_file_overwrite_ok.json");
348
349 write_keypair_file(&Keypair::new(), &outfile).unwrap();
350 write_keypair_file(&Keypair::new(), &outfile).unwrap();
351 }
352
353 #[test]
354 fn test_write_keypair_file_truncate() {
355 let outfile = tmp_file_path("test_write_keypair_file_truncate.json");
356
357 write_keypair_file(&Keypair::new(), &outfile).unwrap();
358 read_keypair_file(&outfile).unwrap();
359
360 {
362 let mut f = File::create(&outfile).unwrap();
363 f.write_all(String::from_utf8([b'a'; 2048].to_vec()).unwrap().as_bytes())
364 .unwrap();
365 }
366 write_keypair_file(&Keypair::new(), &outfile).unwrap();
367 read_keypair_file(&outfile).unwrap();
368 }
369
370 #[test]
371 fn test_keypair_from_seed() {
372 let good_seed = vec![0; 32];
373 assert!(keypair_from_seed(&good_seed).is_ok());
374
375 let too_short_seed = vec![0; 31];
376 assert!(keypair_from_seed(&too_short_seed).is_err());
377 }
378
379 #[test]
380 fn test_keypair() {
381 let keypair = keypair_from_seed(&[0u8; 32]).unwrap();
382 let pubkey = keypair.pubkey();
383 let data = [1u8];
384 let sig = keypair.sign_message(&data);
385
386 assert_eq!(keypair.try_pubkey().unwrap(), pubkey);
388 assert_eq!(keypair.pubkey(), pubkey);
389 assert_eq!(keypair.try_sign_message(&data).unwrap(), sig);
390 assert_eq!(keypair.sign_message(&data), sig);
391
392 let keypair2 = keypair_from_seed(&[0u8; 32]).unwrap();
394 assert_eq!(keypair, keypair2);
395 }
396
397 fn pubkeys(signers: &[&dyn Signer]) -> Vec<Pubkey> {
398 signers.iter().map(|x| x.pubkey()).collect()
399 }
400
401 #[test]
402 fn test_unique_signers() {
403 let alice = Keypair::new();
404 let bob = Keypair::new();
405 assert_eq!(
406 pubkeys(&unique_signers(vec![&alice, &bob, &alice])),
407 pubkeys(&[&alice, &bob])
408 );
409 }
410
411 #[test]
412 fn test_containers() {
413 use std::{rc::Rc, sync::Arc};
414
415 struct Foo<S: Signer> {
416 #[allow(unused)]
417 signer: S,
418 }
419
420 fn foo(_s: impl Signer) {}
421
422 let _arc_signer = Foo {
423 signer: Arc::new(Keypair::new()),
424 };
425 foo(Arc::new(Keypair::new()));
426
427 let _rc_signer = Foo {
428 signer: Rc::new(Keypair::new()),
429 };
430 foo(Rc::new(Keypair::new()));
431
432 let _ref_signer = Foo {
433 signer: &Keypair::new(),
434 };
435 foo(Keypair::new());
436
437 let _box_signer = Foo {
438 signer: Box::new(Keypair::new()),
439 };
440 foo(Box::new(Keypair::new()));
441
442 let _signer = Foo {
443 signer: Keypair::new(),
444 };
445 foo(Keypair::new());
446 }
447
448 #[test]
449 fn test_keypair_from_seed_phrase_and_passphrase() {
450 let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
451 let passphrase = "42";
452 let seed = Seed::new(&mnemonic, passphrase);
453 let expected_keypair = keypair_from_seed(seed.as_bytes()).unwrap();
454 let keypair =
455 keypair_from_seed_phrase_and_passphrase(mnemonic.phrase(), passphrase).unwrap();
456 assert_eq!(keypair.pubkey(), expected_keypair.pubkey());
457 }
458}