1#![cfg(feature = "full")]
2
3use {
4 crate::{
5 derivation_path::DerivationPath,
6 pubkey::Pubkey,
7 signature::Signature,
8 signer::{Signer, SignerError},
9 },
10 ed25519_dalek::Signer as DalekSigner,
11 ed25519_dalek_bip32::Error as Bip32Error,
12 hmac::Hmac,
13 rand::{rngs::OsRng, CryptoRng, RngCore},
14 std::{
15 error,
16 fs::{self, File, OpenOptions},
17 io::{Read, Write},
18 path::Path,
19 },
20 wasm_bindgen::prelude::*,
21};
22
23#[wasm_bindgen]
25#[derive(Debug)]
26pub struct Keypair(ed25519_dalek::Keypair);
27
28impl Keypair {
29 pub fn generate<R>(csprng: &mut R) -> Self
31 where
32 R: CryptoRng + RngCore,
33 {
34 Self(ed25519_dalek::Keypair::generate(csprng))
35 }
36
37 pub fn new() -> Self {
39 let mut rng = OsRng::default();
40 Self::generate(&mut rng)
41 }
42
43 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ed25519_dalek::SignatureError> {
45 ed25519_dalek::Keypair::from_bytes(bytes).map(Self)
46 }
47
48 pub fn to_bytes(&self) -> [u8; 64] {
50 self.0.to_bytes()
51 }
52
53 pub fn from_base58_string(s: &str) -> Self {
55 Self::from_bytes(&bs58::decode(s).into_vec().unwrap()).unwrap()
56 }
57
58 pub fn to_base58_string(&self) -> String {
60 bs58::encode(&self.0.to_bytes()).into_string()
61 }
62
63 pub fn secret(&self) -> &ed25519_dalek::SecretKey {
65 &self.0.secret
66 }
67}
68
69impl Signer for Keypair {
70 #[inline]
71 fn pubkey(&self) -> Pubkey {
72 Pubkey::from(self.0.public.to_bytes())
73 }
74
75 fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
76 Ok(self.pubkey())
77 }
78
79 fn sign_message(&self, message: &[u8]) -> Signature {
80 Signature::new(&self.0.sign(message).to_bytes())
81 }
82
83 fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
84 Ok(self.sign_message(message))
85 }
86
87 fn is_interactive(&self) -> bool {
88 false
89 }
90}
91
92impl<T> PartialEq<T> for Keypair
93where
94 T: Signer,
95{
96 fn eq(&self, other: &T) -> bool {
97 self.pubkey() == other.pubkey()
98 }
99}
100
101pub fn read_keypair<R: Read>(reader: &mut R) -> Result<Keypair, Box<dyn error::Error>> {
103 let bytes: Vec<u8> = serde_json::from_reader(reader)?;
104 let dalek_keypair = ed25519_dalek::Keypair::from_bytes(&bytes)
105 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
106 Ok(Keypair(dalek_keypair))
107}
108
109pub fn read_keypair_file<F: AsRef<Path>>(path: F) -> Result<Keypair, Box<dyn error::Error>> {
111 let mut file = File::open(path.as_ref())?;
112 read_keypair(&mut file)
113}
114
115pub fn write_keypair<W: Write>(
117 keypair: &Keypair,
118 writer: &mut W,
119) -> Result<String, Box<dyn error::Error>> {
120 let keypair_bytes = keypair.0.to_bytes();
121 let serialized = serde_json::to_string(&keypair_bytes.to_vec())?;
122 writer.write_all(&serialized.clone().into_bytes())?;
123 Ok(serialized)
124}
125
126pub fn write_keypair_file<F: AsRef<Path>>(
128 keypair: &Keypair,
129 outfile: F,
130) -> Result<String, Box<dyn error::Error>> {
131 let outfile = outfile.as_ref();
132
133 if let Some(outdir) = outfile.parent() {
134 fs::create_dir_all(outdir)?;
135 }
136
137 let mut f = {
138 #[cfg(not(unix))]
139 {
140 OpenOptions::new()
141 }
142 #[cfg(unix)]
143 {
144 use std::os::unix::fs::OpenOptionsExt;
145 OpenOptions::new().mode(0o600)
146 }
147 }
148 .write(true)
149 .truncate(true)
150 .create(true)
151 .open(outfile)?;
152
153 write_keypair(keypair, &mut f)
154}
155
156pub fn keypair_from_seed(seed: &[u8]) -> Result<Keypair, Box<dyn error::Error>> {
158 if seed.len() < ed25519_dalek::SECRET_KEY_LENGTH {
159 return Err("Seed is too short".into());
160 }
161 let secret = ed25519_dalek::SecretKey::from_bytes(&seed[..ed25519_dalek::SECRET_KEY_LENGTH])
162 .map_err(|e| e.to_string())?;
163 let public = ed25519_dalek::PublicKey::from(&secret);
164 let dalek_keypair = ed25519_dalek::Keypair { secret, public };
165 Ok(Keypair(dalek_keypair))
166}
167
168pub fn keypair_from_seed_and_derivation_path(
171 seed: &[u8],
172 derivation_path: Option<DerivationPath>,
173) -> Result<Keypair, Box<dyn error::Error>> {
174 let derivation_path = derivation_path.unwrap_or_default();
175 bip32_derived_keypair(seed, derivation_path).map_err(|err| err.to_string().into())
176}
177
178fn bip32_derived_keypair(
180 seed: &[u8],
181 derivation_path: DerivationPath,
182) -> Result<Keypair, Bip32Error> {
183 let extended = ed25519_dalek_bip32::ExtendedSecretKey::from_seed(seed)
184 .and_then(|extended| extended.derive(&derivation_path))?;
185 let extended_public_key = extended.public_key();
186 Ok(Keypair(ed25519_dalek::Keypair {
187 secret: extended.secret_key,
188 public: extended_public_key,
189 }))
190}
191
192pub fn generate_seed_from_seed_phrase_and_passphrase(
193 seed_phrase: &str,
194 passphrase: &str,
195) -> Vec<u8> {
196 const PBKDF2_ROUNDS: u32 = 2048;
197 const PBKDF2_BYTES: usize = 64;
198
199 let salt = format!("mnemonic{}", passphrase);
200
201 let mut seed = vec![0u8; PBKDF2_BYTES];
202 pbkdf2::pbkdf2::<Hmac<sha2::Sha512>>(
203 seed_phrase.as_bytes(),
204 salt.as_bytes(),
205 PBKDF2_ROUNDS,
206 &mut seed,
207 );
208 seed
209}
210
211pub fn keypair_from_seed_phrase_and_passphrase(
212 seed_phrase: &str,
213 passphrase: &str,
214) -> Result<Keypair, Box<dyn error::Error>> {
215 keypair_from_seed(&generate_seed_from_seed_phrase_and_passphrase(
216 seed_phrase,
217 passphrase,
218 ))
219}
220
221#[cfg(test)]
222mod tests {
223 use {
224 super::*,
225 bip39::{Language, Mnemonic, MnemonicType, Seed},
226 std::mem,
227 };
228
229 fn tmp_file_path(name: &str) -> String {
230 use std::env;
231 let out_dir = env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
232 let keypair = Keypair::new();
233
234 format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey())
235 }
236
237 #[test]
238 fn test_write_keypair_file() {
239 let outfile = tmp_file_path("test_write_keypair_file.json");
240 let serialized_keypair = write_keypair_file(&Keypair::new(), &outfile).unwrap();
241 let keypair_vec: Vec<u8> = serde_json::from_str(&serialized_keypair).unwrap();
242 assert!(Path::new(&outfile).exists());
243 assert_eq!(
244 keypair_vec,
245 read_keypair_file(&outfile).unwrap().0.to_bytes().to_vec()
246 );
247
248 #[cfg(unix)]
249 {
250 use std::os::unix::fs::PermissionsExt;
251 assert_eq!(
252 File::open(&outfile)
253 .expect("open")
254 .metadata()
255 .expect("metadata")
256 .permissions()
257 .mode()
258 & 0o777,
259 0o600
260 );
261 }
262
263 assert_eq!(
264 read_keypair_file(&outfile).unwrap().pubkey().as_ref().len(),
265 mem::size_of::<Pubkey>()
266 );
267 fs::remove_file(&outfile).unwrap();
268 assert!(!Path::new(&outfile).exists());
269 }
270
271 #[test]
272 fn test_write_keypair_file_overwrite_ok() {
273 let outfile = tmp_file_path("test_write_keypair_file_overwrite_ok.json");
274
275 write_keypair_file(&Keypair::new(), &outfile).unwrap();
276 write_keypair_file(&Keypair::new(), &outfile).unwrap();
277 }
278
279 #[test]
280 fn test_write_keypair_file_truncate() {
281 let outfile = tmp_file_path("test_write_keypair_file_truncate.json");
282
283 write_keypair_file(&Keypair::new(), &outfile).unwrap();
284 read_keypair_file(&outfile).unwrap();
285
286 {
288 let mut f = File::create(&outfile).unwrap();
289 f.write_all(String::from_utf8([b'a'; 2048].to_vec()).unwrap().as_bytes())
290 .unwrap();
291 }
292 write_keypair_file(&Keypair::new(), &outfile).unwrap();
293 read_keypair_file(&outfile).unwrap();
294 }
295
296 #[test]
297 fn test_keypair_from_seed() {
298 let good_seed = vec![0; 32];
299 assert!(keypair_from_seed(&good_seed).is_ok());
300
301 let too_short_seed = vec![0; 31];
302 assert!(keypair_from_seed(&too_short_seed).is_err());
303 }
304
305 #[test]
306 fn test_keypair_from_seed_phrase_and_passphrase() {
307 let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
308 let passphrase = "42";
309 let seed = Seed::new(&mnemonic, passphrase);
310 let expected_keypair = keypair_from_seed(seed.as_bytes()).unwrap();
311 let keypair =
312 keypair_from_seed_phrase_and_passphrase(mnemonic.phrase(), passphrase).unwrap();
313 assert_eq!(keypair.pubkey(), expected_keypair.pubkey());
314 }
315
316 #[test]
317 fn test_keypair() {
318 let keypair = keypair_from_seed(&[0u8; 32]).unwrap();
319 let pubkey = keypair.pubkey();
320 let data = [1u8];
321 let sig = keypair.sign_message(&data);
322
323 assert_eq!(keypair.try_pubkey().unwrap(), pubkey);
325 assert_eq!(keypair.pubkey(), pubkey);
326 assert_eq!(keypair.try_sign_message(&data).unwrap(), sig);
327 assert_eq!(keypair.sign_message(&data), sig);
328
329 let keypair2 = keypair_from_seed(&[0u8; 32]).unwrap();
331 assert_eq!(keypair, keypair2);
332 }
333}