#![cfg(feature = "full")]
use {
crate::{
derivation_path::DerivationPath,
pubkey::Pubkey,
signature::Signature,
signer::{EncodableKey, EncodableKeypair, SeedDerivable, Signer, SignerError},
},
ed25519_dalek::Signer as DalekSigner,
ed25519_dalek_bip32::Error as Bip32Error,
hmac::Hmac,
rand0_7::{rngs::OsRng, CryptoRng, RngCore},
std::{
error,
io::{Read, Write},
path::Path,
},
wasm_bindgen::prelude::*,
};
#[wasm_bindgen]
#[derive(Debug)]
pub struct Keypair(ed25519_dalek::Keypair);
impl Keypair {
pub const SECRET_KEY_LENGTH: usize = 32;
pub fn generate<R>(csprng: &mut R) -> Self
where
R: CryptoRng + RngCore,
{
Self(ed25519_dalek::Keypair::generate(csprng))
}
pub fn new() -> Self {
let mut rng = OsRng;
Self::generate(&mut rng)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ed25519_dalek::SignatureError> {
if bytes.len() < ed25519_dalek::KEYPAIR_LENGTH {
return Err(ed25519_dalek::SignatureError::from_source(String::from(
"candidate keypair byte array is too short",
)));
}
let secret =
ed25519_dalek::SecretKey::from_bytes(&bytes[..ed25519_dalek::SECRET_KEY_LENGTH])?;
let public =
ed25519_dalek::PublicKey::from_bytes(&bytes[ed25519_dalek::SECRET_KEY_LENGTH..])?;
let expected_public = ed25519_dalek::PublicKey::from(&secret);
(public == expected_public)
.then_some(Self(ed25519_dalek::Keypair { secret, public }))
.ok_or(ed25519_dalek::SignatureError::from_source(String::from(
"keypair bytes do not specify same pubkey as derived from their secret key",
)))
}
pub fn to_bytes(&self) -> [u8; 64] {
self.0.to_bytes()
}
pub fn from_base58_string(s: &str) -> Self {
Self::from_bytes(&bs58::decode(s).into_vec().unwrap()).unwrap()
}
pub fn to_base58_string(&self) -> String {
bs58::encode(&self.0.to_bytes()).into_string()
}
pub fn secret(&self) -> &ed25519_dalek::SecretKey {
&self.0.secret
}
pub fn insecure_clone(&self) -> Self {
Self(ed25519_dalek::Keypair {
secret: ed25519_dalek::SecretKey::from_bytes(self.0.secret.as_bytes()).unwrap(),
public: self.0.public,
})
}
}
#[cfg(test)]
static_assertions::const_assert_eq!(Keypair::SECRET_KEY_LENGTH, ed25519_dalek::SECRET_KEY_LENGTH);
impl Signer for Keypair {
#[inline]
fn pubkey(&self) -> Pubkey {
Pubkey::from(self.0.public.to_bytes())
}
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
Ok(self.pubkey())
}
fn sign_message(&self, message: &[u8]) -> Signature {
Signature::from(self.0.sign(message).to_bytes())
}
fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
Ok(self.sign_message(message))
}
fn is_interactive(&self) -> bool {
false
}
}
impl<T> PartialEq<T> for Keypair
where
T: Signer,
{
fn eq(&self, other: &T) -> bool {
self.pubkey() == other.pubkey()
}
}
impl EncodableKey for Keypair {
fn read<R: Read>(reader: &mut R) -> Result<Self, Box<dyn error::Error>> {
read_keypair(reader)
}
fn write<W: Write>(&self, writer: &mut W) -> Result<String, Box<dyn error::Error>> {
write_keypair(self, writer)
}
}
impl SeedDerivable for Keypair {
fn from_seed(seed: &[u8]) -> Result<Self, Box<dyn error::Error>> {
keypair_from_seed(seed)
}
fn from_seed_and_derivation_path(
seed: &[u8],
derivation_path: Option<DerivationPath>,
) -> Result<Self, Box<dyn error::Error>> {
keypair_from_seed_and_derivation_path(seed, derivation_path)
}
fn from_seed_phrase_and_passphrase(
seed_phrase: &str,
passphrase: &str,
) -> Result<Self, Box<dyn error::Error>> {
keypair_from_seed_phrase_and_passphrase(seed_phrase, passphrase)
}
}
impl EncodableKeypair for Keypair {
type Pubkey = Pubkey;
fn encodable_pubkey(&self) -> Self::Pubkey {
self.pubkey()
}
}
pub fn read_keypair<R: Read>(reader: &mut R) -> Result<Keypair, Box<dyn error::Error>> {
let bytes: Vec<u8> = serde_json::from_reader(reader)?;
Keypair::from_bytes(&bytes)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()).into())
}
pub fn read_keypair_file<F: AsRef<Path>>(path: F) -> Result<Keypair, Box<dyn error::Error>> {
Keypair::read_from_file(path)
}
pub fn write_keypair<W: Write>(
keypair: &Keypair,
writer: &mut W,
) -> Result<String, Box<dyn error::Error>> {
let keypair_bytes = keypair.0.to_bytes();
let serialized = serde_json::to_string(&keypair_bytes.to_vec())?;
writer.write_all(serialized.as_bytes())?;
Ok(serialized)
}
pub fn write_keypair_file<F: AsRef<Path>>(
keypair: &Keypair,
outfile: F,
) -> Result<String, Box<dyn error::Error>> {
keypair.write_to_file(outfile)
}
pub fn keypair_from_seed(seed: &[u8]) -> Result<Keypair, Box<dyn error::Error>> {
if seed.len() < ed25519_dalek::SECRET_KEY_LENGTH {
return Err("Seed is too short".into());
}
let secret = ed25519_dalek::SecretKey::from_bytes(&seed[..ed25519_dalek::SECRET_KEY_LENGTH])
.map_err(|e| e.to_string())?;
let public = ed25519_dalek::PublicKey::from(&secret);
let dalek_keypair = ed25519_dalek::Keypair { secret, public };
Ok(Keypair(dalek_keypair))
}
pub fn keypair_from_seed_and_derivation_path(
seed: &[u8],
derivation_path: Option<DerivationPath>,
) -> Result<Keypair, Box<dyn error::Error>> {
let derivation_path = derivation_path.unwrap_or_default();
bip32_derived_keypair(seed, derivation_path).map_err(|err| err.to_string().into())
}
fn bip32_derived_keypair(
seed: &[u8],
derivation_path: DerivationPath,
) -> Result<Keypair, Bip32Error> {
let extended = ed25519_dalek_bip32::ExtendedSecretKey::from_seed(seed)
.and_then(|extended| extended.derive(&derivation_path))?;
let extended_public_key = extended.public_key();
Ok(Keypair(ed25519_dalek::Keypair {
secret: extended.secret_key,
public: extended_public_key,
}))
}
pub fn generate_seed_from_seed_phrase_and_passphrase(
seed_phrase: &str,
passphrase: &str,
) -> Vec<u8> {
const PBKDF2_ROUNDS: u32 = 2048;
const PBKDF2_BYTES: usize = 64;
let salt = format!("mnemonic{passphrase}");
let mut seed = vec![0u8; PBKDF2_BYTES];
pbkdf2::pbkdf2::<Hmac<sha2::Sha512>>(
seed_phrase.as_bytes(),
salt.as_bytes(),
PBKDF2_ROUNDS,
&mut seed,
);
seed
}
pub fn keypair_from_seed_phrase_and_passphrase(
seed_phrase: &str,
passphrase: &str,
) -> Result<Keypair, Box<dyn error::Error>> {
keypair_from_seed(&generate_seed_from_seed_phrase_and_passphrase(
seed_phrase,
passphrase,
))
}
#[cfg(test)]
mod tests {
use {
super::*,
bip39::{Language, Mnemonic, MnemonicType, Seed},
std::{
fs::{self, File},
mem,
},
};
fn tmp_file_path(name: &str) -> String {
use std::env;
let out_dir = env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
let keypair = Keypair::new();
format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey())
}
#[test]
fn test_write_keypair_file() {
let outfile = tmp_file_path("test_write_keypair_file.json");
let serialized_keypair = write_keypair_file(&Keypair::new(), &outfile).unwrap();
let keypair_vec: Vec<u8> = serde_json::from_str(&serialized_keypair).unwrap();
assert!(Path::new(&outfile).exists());
assert_eq!(
keypair_vec,
read_keypair_file(&outfile).unwrap().0.to_bytes().to_vec()
);
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
assert_eq!(
File::open(&outfile)
.expect("open")
.metadata()
.expect("metadata")
.permissions()
.mode()
& 0o777,
0o600
);
}
assert_eq!(
read_keypair_file(&outfile).unwrap().pubkey().as_ref().len(),
mem::size_of::<Pubkey>()
);
fs::remove_file(&outfile).unwrap();
assert!(!Path::new(&outfile).exists());
}
#[test]
fn test_write_keypair_file_overwrite_ok() {
let outfile = tmp_file_path("test_write_keypair_file_overwrite_ok.json");
write_keypair_file(&Keypair::new(), &outfile).unwrap();
write_keypair_file(&Keypair::new(), &outfile).unwrap();
}
#[test]
fn test_write_keypair_file_truncate() {
let outfile = tmp_file_path("test_write_keypair_file_truncate.json");
write_keypair_file(&Keypair::new(), &outfile).unwrap();
read_keypair_file(&outfile).unwrap();
{
let mut f = File::create(&outfile).unwrap();
f.write_all(String::from_utf8([b'a'; 2048].to_vec()).unwrap().as_bytes())
.unwrap();
}
write_keypair_file(&Keypair::new(), &outfile).unwrap();
read_keypair_file(&outfile).unwrap();
}
#[test]
fn test_keypair_from_seed() {
let good_seed = vec![0; 32];
assert!(keypair_from_seed(&good_seed).is_ok());
let too_short_seed = vec![0; 31];
assert!(keypair_from_seed(&too_short_seed).is_err());
}
#[test]
fn test_keypair_from_seed_phrase_and_passphrase() {
let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
let passphrase = "42";
let seed = Seed::new(&mnemonic, passphrase);
let expected_keypair = keypair_from_seed(seed.as_bytes()).unwrap();
let keypair =
keypair_from_seed_phrase_and_passphrase(mnemonic.phrase(), passphrase).unwrap();
assert_eq!(keypair.pubkey(), expected_keypair.pubkey());
}
#[test]
fn test_keypair() {
let keypair = keypair_from_seed(&[0u8; 32]).unwrap();
let pubkey = keypair.pubkey();
let data = [1u8];
let sig = keypair.sign_message(&data);
assert_eq!(keypair.try_pubkey().unwrap(), pubkey);
assert_eq!(keypair.pubkey(), pubkey);
assert_eq!(keypair.try_sign_message(&data).unwrap(), sig);
assert_eq!(keypair.sign_message(&data), sig);
let keypair2 = keypair_from_seed(&[0u8; 32]).unwrap();
assert_eq!(keypair, keypair2);
}
}