1#![cfg(feature = "full")]
2
3#[cfg(target_arch = "wasm32")]
4use wasm_bindgen::prelude::*;
5use {
6 crate::{
7 pubkey::Pubkey,
8 signature::Signature,
9 signer::{EncodableKey, EncodableKeypair, SeedDerivable, Signer, SignerError},
10 },
11 ed25519_dalek::Signer as DalekSigner,
12 ed25519_dalek_bip32::Error as Bip32Error,
13 hmac::Hmac,
14 rand0_7::{rngs::OsRng, CryptoRng, RngCore},
15 solana_derivation_path::DerivationPath,
16 std::{
17 error,
18 io::{Read, Write},
19 path::Path,
20 },
21};
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 Self::from_bytes(&bs58::decode(s).into_vec().unwrap()).unwrap()
73 }
74
75 pub fn to_base58_string(&self) -> String {
77 bs58::encode(&self.0.to_bytes()).into_string()
78 }
79
80 pub fn secret(&self) -> &ed25519_dalek::SecretKey {
82 &self.0.secret
83 }
84
85 pub fn insecure_clone(&self) -> Self {
93 Self(ed25519_dalek::Keypair {
94 secret: ed25519_dalek::SecretKey::from_bytes(self.0.secret.as_bytes()).unwrap(),
96 public: self.0.public,
97 })
98 }
99}
100
101#[cfg(test)]
102static_assertions::const_assert_eq!(Keypair::SECRET_KEY_LENGTH, ed25519_dalek::SECRET_KEY_LENGTH);
103
104impl Signer for Keypair {
105 #[inline]
106 fn pubkey(&self) -> Pubkey {
107 Pubkey::from(self.0.public.to_bytes())
108 }
109
110 fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
111 Ok(self.pubkey())
112 }
113
114 fn sign_message(&self, message: &[u8]) -> Signature {
115 Signature::from(self.0.sign(message).to_bytes())
116 }
117
118 fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
119 Ok(self.sign_message(message))
120 }
121
122 fn is_interactive(&self) -> bool {
123 false
124 }
125}
126
127impl<T> PartialEq<T> for Keypair
128where
129 T: Signer,
130{
131 fn eq(&self, other: &T) -> bool {
132 self.pubkey() == other.pubkey()
133 }
134}
135
136impl EncodableKey for Keypair {
137 fn read<R: Read>(reader: &mut R) -> Result<Self, Box<dyn error::Error>> {
138 read_keypair(reader)
139 }
140
141 fn write<W: Write>(&self, writer: &mut W) -> Result<String, Box<dyn error::Error>> {
142 write_keypair(self, writer)
143 }
144}
145
146impl SeedDerivable for Keypair {
147 fn from_seed(seed: &[u8]) -> Result<Self, Box<dyn error::Error>> {
148 keypair_from_seed(seed)
149 }
150
151 fn from_seed_and_derivation_path(
152 seed: &[u8],
153 derivation_path: Option<DerivationPath>,
154 ) -> Result<Self, Box<dyn error::Error>> {
155 keypair_from_seed_and_derivation_path(seed, derivation_path)
156 }
157
158 fn from_seed_phrase_and_passphrase(
159 seed_phrase: &str,
160 passphrase: &str,
161 ) -> Result<Self, Box<dyn error::Error>> {
162 keypair_from_seed_phrase_and_passphrase(seed_phrase, passphrase)
163 }
164}
165
166impl EncodableKeypair for Keypair {
167 type Pubkey = Pubkey;
168
169 fn encodable_pubkey(&self) -> Self::Pubkey {
172 self.pubkey()
173 }
174}
175
176pub fn read_keypair<R: Read>(reader: &mut R) -> Result<Keypair, Box<dyn error::Error>> {
178 let bytes: Vec<u8> = serde_json::from_reader(reader)?;
179 Keypair::from_bytes(&bytes)
180 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()).into())
181}
182
183pub fn read_keypair_file<F: AsRef<Path>>(path: F) -> Result<Keypair, Box<dyn error::Error>> {
185 Keypair::read_from_file(path)
186}
187
188pub fn write_keypair<W: Write>(
190 keypair: &Keypair,
191 writer: &mut W,
192) -> Result<String, Box<dyn error::Error>> {
193 let keypair_bytes = keypair.0.to_bytes();
194 let serialized = serde_json::to_string(&keypair_bytes.to_vec())?;
195 writer.write_all(serialized.as_bytes())?;
196 Ok(serialized)
197}
198
199pub fn write_keypair_file<F: AsRef<Path>>(
201 keypair: &Keypair,
202 outfile: F,
203) -> Result<String, Box<dyn error::Error>> {
204 keypair.write_to_file(outfile)
205}
206
207pub fn keypair_from_seed(seed: &[u8]) -> Result<Keypair, Box<dyn error::Error>> {
209 if seed.len() < ed25519_dalek::SECRET_KEY_LENGTH {
210 return Err("Seed is too short".into());
211 }
212 let secret = ed25519_dalek::SecretKey::from_bytes(&seed[..ed25519_dalek::SECRET_KEY_LENGTH])
213 .map_err(|e| e.to_string())?;
214 let public = ed25519_dalek::PublicKey::from(&secret);
215 let dalek_keypair = ed25519_dalek::Keypair { secret, public };
216 Ok(Keypair(dalek_keypair))
217}
218
219pub fn keypair_from_seed_and_derivation_path(
222 seed: &[u8],
223 derivation_path: Option<DerivationPath>,
224) -> Result<Keypair, Box<dyn error::Error>> {
225 let derivation_path = derivation_path.unwrap_or_default();
226 bip32_derived_keypair(seed, derivation_path).map_err(|err| err.to_string().into())
227}
228
229fn bip32_derived_keypair(
231 seed: &[u8],
232 derivation_path: DerivationPath,
233) -> Result<Keypair, Bip32Error> {
234 let extended = ed25519_dalek_bip32::ExtendedSecretKey::from_seed(seed)
235 .and_then(|extended| extended.derive(&derivation_path))?;
236 let extended_public_key = extended.public_key();
237 Ok(Keypair(ed25519_dalek::Keypair {
238 secret: extended.secret_key,
239 public: extended_public_key,
240 }))
241}
242
243pub fn generate_seed_from_seed_phrase_and_passphrase(
244 seed_phrase: &str,
245 passphrase: &str,
246) -> Vec<u8> {
247 const PBKDF2_ROUNDS: u32 = 2048;
248 const PBKDF2_BYTES: usize = 64;
249
250 let salt = format!("mnemonic{passphrase}");
251
252 let mut seed = vec![0u8; PBKDF2_BYTES];
253 pbkdf2::pbkdf2::<Hmac<sha2::Sha512>>(
254 seed_phrase.as_bytes(),
255 salt.as_bytes(),
256 PBKDF2_ROUNDS,
257 &mut seed,
258 );
259 seed
260}
261
262pub fn keypair_from_seed_phrase_and_passphrase(
263 seed_phrase: &str,
264 passphrase: &str,
265) -> Result<Keypair, Box<dyn error::Error>> {
266 keypair_from_seed(&generate_seed_from_seed_phrase_and_passphrase(
267 seed_phrase,
268 passphrase,
269 ))
270}
271
272#[cfg(test)]
273mod tests {
274 use {
275 super::*,
276 bip39::{Language, Mnemonic, MnemonicType, Seed},
277 std::{
278 fs::{self, File},
279 mem,
280 },
281 };
282
283 fn tmp_file_path(name: &str) -> String {
284 use std::env;
285 let out_dir = env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
286 let keypair = Keypair::new();
287
288 format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey())
289 }
290
291 #[test]
292 fn test_write_keypair_file() {
293 let outfile = tmp_file_path("test_write_keypair_file.json");
294 let serialized_keypair = write_keypair_file(&Keypair::new(), &outfile).unwrap();
295 let keypair_vec: Vec<u8> = serde_json::from_str(&serialized_keypair).unwrap();
296 assert!(Path::new(&outfile).exists());
297 assert_eq!(
298 keypair_vec,
299 read_keypair_file(&outfile).unwrap().0.to_bytes().to_vec()
300 );
301
302 #[cfg(unix)]
303 {
304 use std::os::unix::fs::PermissionsExt;
305 assert_eq!(
306 File::open(&outfile)
307 .expect("open")
308 .metadata()
309 .expect("metadata")
310 .permissions()
311 .mode()
312 & 0o777,
313 0o600
314 );
315 }
316
317 assert_eq!(
318 read_keypair_file(&outfile).unwrap().pubkey().as_ref().len(),
319 mem::size_of::<Pubkey>()
320 );
321 fs::remove_file(&outfile).unwrap();
322 assert!(!Path::new(&outfile).exists());
323 }
324
325 #[test]
326 fn test_write_keypair_file_overwrite_ok() {
327 let outfile = tmp_file_path("test_write_keypair_file_overwrite_ok.json");
328
329 write_keypair_file(&Keypair::new(), &outfile).unwrap();
330 write_keypair_file(&Keypair::new(), &outfile).unwrap();
331 }
332
333 #[test]
334 fn test_write_keypair_file_truncate() {
335 let outfile = tmp_file_path("test_write_keypair_file_truncate.json");
336
337 write_keypair_file(&Keypair::new(), &outfile).unwrap();
338 read_keypair_file(&outfile).unwrap();
339
340 {
342 let mut f = File::create(&outfile).unwrap();
343 f.write_all(String::from_utf8([b'a'; 2048].to_vec()).unwrap().as_bytes())
344 .unwrap();
345 }
346 write_keypair_file(&Keypair::new(), &outfile).unwrap();
347 read_keypair_file(&outfile).unwrap();
348 }
349
350 #[test]
351 fn test_keypair_from_seed() {
352 let good_seed = vec![0; 32];
353 assert!(keypair_from_seed(&good_seed).is_ok());
354
355 let too_short_seed = vec![0; 31];
356 assert!(keypair_from_seed(&too_short_seed).is_err());
357 }
358
359 #[test]
360 fn test_keypair_from_seed_phrase_and_passphrase() {
361 let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
362 let passphrase = "42";
363 let seed = Seed::new(&mnemonic, passphrase);
364 let expected_keypair = keypair_from_seed(seed.as_bytes()).unwrap();
365 let keypair =
366 keypair_from_seed_phrase_and_passphrase(mnemonic.phrase(), passphrase).unwrap();
367 assert_eq!(keypair.pubkey(), expected_keypair.pubkey());
368 }
369
370 #[test]
371 fn test_keypair() {
372 let keypair = keypair_from_seed(&[0u8; 32]).unwrap();
373 let pubkey = keypair.pubkey();
374 let data = [1u8];
375 let sig = keypair.sign_message(&data);
376
377 assert_eq!(keypair.try_pubkey().unwrap(), pubkey);
379 assert_eq!(keypair.pubkey(), pubkey);
380 assert_eq!(keypair.try_sign_message(&data).unwrap(), sig);
381 assert_eq!(keypair.sign_message(&data), sig);
382
383 let keypair2 = keypair_from_seed(&[0u8; 32]).unwrap();
385 assert_eq!(keypair, keypair2);
386 }
387}