Skip to content

Commit

Permalink
make error handling more verbose
Browse files Browse the repository at this point in the history
  • Loading branch information
Vladislav Melnik committed Apr 1, 2019
1 parent 70e75e9 commit ccaa79f
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 50 deletions.
36 changes: 25 additions & 11 deletions wallet/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ use crypto::symmetriccipher;

/// An error class to offer a unified error interface upstream
pub enum WalletError {
/// generic error message
Generic(&'static str),
/// Network IO error
IO(io::Error),
/// key derivation error
Expand All @@ -37,37 +35,53 @@ pub enum WalletError {
SymmetricCipherError(symmetriccipher::SymmetricCipherError),
/// has no key in db
HasNoWalletInDatabase,
/// Mnemonic contains an unknown word
UnknownMnemonicWord,
/// Mnemonic must have a word count divisible by 3
InvalidMnemonicLength,
/// Data for mnemonic should have a length divisible by 4
InvalidMnemonicData,
/// Mnemonic checking bits not match
MnemonicChecksumNotMatch,
/// Cannot obtain random source
CannotObtainRandomSource,
}

impl Error for WalletError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
&WalletError::Generic(_) => None,
&WalletError::IO(ref err) => Some(err),
&WalletError::KeyDerivation(ref err) => Some(err),
&WalletError::SymmetricCipherError(_) => None,
&WalletError::HasNoWalletInDatabase => None,
_ => None,
}
}
}

impl fmt::Display for WalletError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
match self {
// Both underlying errors already impl `Display`, so we defer to
// their implementations.
WalletError::Generic(ref s) => write!(f, "Generic: {}", s),
WalletError::IO(ref err) => write!(f, "IO error: {}", err),
WalletError::KeyDerivation(ref err) => write!(f, "BIP32 error: {}", err),
WalletError::SymmetricCipherError(ref err) => write!(
&WalletError::IO(ref err) => write!(f, "IO error: {}", err),
&WalletError::KeyDerivation(ref err) => write!(f, "BIP32 error: {}", err),
&WalletError::SymmetricCipherError(ref err) => write!(
f,
"Cipher error: {}",
match err {
&symmetriccipher::SymmetricCipherError::InvalidLength => "invalid length",
&symmetriccipher::SymmetricCipherError::InvalidPadding => "invalid padding",
}
),
WalletError::HasNoWalletInDatabase => write!(f, "has no wallet in database"),
&WalletError::HasNoWalletInDatabase => write!(f, "has no wallet in database"),
&WalletError::UnknownMnemonicWord => write!(f, "mnemonic contains an unknown word"),
&WalletError::InvalidMnemonicLength => {
write!(f, "mnemonic must have a word count divisible by 3")
},
&WalletError::InvalidMnemonicData => {
write!(f, "data for mnemonic should have a length divisible by 4")
},
&WalletError::MnemonicChecksumNotMatch => write!(f, "mnemonic checking bits not match"),
&WalletError::CannotObtainRandomSource => write!(f, "cannot obtain random source"),
}
}
}
Expand Down
41 changes: 20 additions & 21 deletions wallet/src/keyfactory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@
//!
//! TREZOR compatible key derivation
//!
use bitcoin::network::constants::Network;
use bitcoin::util::bip32::{ExtendedPubKey, ExtendedPrivKey,ChildNumber};
use bitcoin::util::bip32::{ExtendedPubKey, ExtendedPrivKey, ChildNumber};
use secp256k1::Secp256k1;
use super::error::WalletError;
use crypto::pbkdf2::pbkdf2;
use crypto::hmac::Hmac;
use crypto::sha2::Sha512;
use rand::{rngs::OsRng, RngCore};

use super::error::WalletError;
use super::mnemonic::Mnemonic;

/// a fabric of keys
Expand All @@ -49,7 +48,7 @@ impl KeyFactory {
let key = KeyFactory::master_private_key(network, &seed)?;
return Ok((key, mnemonic, encrypted));
}
Err(WalletError::Generic("can not obtain random source"))
Err(WalletError::CannotObtainRandomSource)
}

/// decrypt stored master key
Expand All @@ -71,19 +70,16 @@ impl KeyFactory {
salt: &str,
) -> Result<ExtendedPrivKey, WalletError> {
let seed = Seed::new(&mnemonic, salt);
let key = KeyFactory::master_private_key(network, &seed)?;
Ok(key)
KeyFactory::master_private_key(network, &seed)
}

/// create a master private key from seed
pub fn master_private_key(
network: Network,
seed: &Seed,
) -> Result<ExtendedPrivKey, WalletError> {
Ok(ExtendedPrivKey::new_master(
network,
&seed.0,
)?)
ExtendedPrivKey::new_master(network, &seed.0)
.map_err(WalletError::KeyDerivation)
}

/// get extended public key for a known private key
Expand All @@ -95,15 +91,17 @@ impl KeyFactory {
extended_private_key: &ExtendedPrivKey,
child: ChildNumber,
) -> Result<ExtendedPrivKey, WalletError> {
Ok(extended_private_key.ckd_priv(&Secp256k1::new(), child)?)
extended_private_key.ckd_priv(&Secp256k1::new(), child)
.map_err(WalletError::KeyDerivation)
}

pub fn public_child(
&self,
extended_public_key: &ExtendedPubKey,
child: ChildNumber,
) -> Result<ExtendedPubKey, WalletError> {
Ok(extended_public_key.ckd_pub(&Secp256k1::new(), child)?)
extended_public_key.ckd_pub(&Secp256k1::new(), child)
.map_err(WalletError::KeyDerivation)
}
}

Expand All @@ -116,6 +114,7 @@ pub enum MasterKeyEntropy {

pub struct Seed(Vec<u8>);

#[cfg(test)]
impl Seed {
// return a copy of the seed data
pub fn data(&self) -> Vec<u8> {
Expand All @@ -126,10 +125,14 @@ impl Seed {
impl Seed {
/// create a seed from mnemonic (optionally with salt)
pub fn new(mnemonic: &Mnemonic, salt: &str) -> Seed {
use crypto::pbkdf2;
use crypto::hmac::Hmac;
use crypto::sha2::Sha512;

let mut mac = Hmac::new(Sha512::new(), mnemonic.to_string().as_bytes());
let mut output = [0u8; 64];
let msalt = "mnemonic".to_owned() + salt;
pbkdf2(&mut mac, msalt.as_bytes(), 2048, &mut output);
pbkdf2::pbkdf2(&mut mac, msalt.as_bytes(), 2048, &mut output);
Seed(output.to_vec())
}
}
Expand All @@ -142,11 +145,7 @@ mod test {
use bitcoin::network::constants::Network;
use bitcoin::util::bip32::ChildNumber;
use crate::keyfactory::Seed;

extern crate rustc_serialize;
extern crate hex;
use self::rustc_serialize::json::Json;
use self::hex::decode;
use rustc_serialize::json::Json;

#[test]
fn bip32_tests() {
Expand All @@ -158,7 +157,7 @@ mod test {
let json = Json::from_str(&data).unwrap();
let tests = json.as_array().unwrap();
for test in tests {
let seed = Seed(decode(test["seed"].as_string().unwrap()).unwrap());
let seed = Seed(hex::decode(test["seed"].as_string().unwrap()).unwrap());
let master_private =
super::KeyFactory::master_private_key(Network::Bitcoin, &seed).unwrap();
assert_eq!(
Expand Down
26 changes: 8 additions & 18 deletions wallet/src/mnemonic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,14 @@ impl Mnemonic {
pub fn from(s: &str) -> Result<Self, WalletError> {
let words: Vec<_> = s.split(' ').collect();
if words.len() < 3 || words.len() % 3 != 0 {
return Err(WalletError::Generic(
"Mnemonic must have a word count divisible with 3",
));
return Err(WalletError::InvalidMnemonicLength);
}
let mut mnemonic = Vec::new();
for word in &words {
if let Ok(idx) = WORDS.binary_search(word) {
mnemonic.push(WORDS[idx]);
} else {
return Err(WalletError::Generic("Mnemonic contains an unknown word"));
return Err(WalletError::UnknownMnemonicWord);
}
}
Ok(Mnemonic(mnemonic))
Expand Down Expand Up @@ -91,9 +89,7 @@ impl Mnemonic {
// create a mnemonic for some data
fn mnemonic(data: &[u8]) -> Result<Self, WalletError> {
if data.len() % 4 != 0 {
return Err(WalletError::Generic(
"Data for mnemonic should have a length divisible by 4",
));
return Err(WalletError::InvalidMnemonicData);
}
let mut check = [0u8; 32];

Expand Down Expand Up @@ -137,7 +133,7 @@ impl Mnemonic {
for word in self.0.iter() {
let index = WORDS
.binary_search(word)
.map_err(|_| WalletError::Generic("Mnemonic contains an unknown word"))?;
.map_err(|_| WalletError::UnknownMnemonicWord)?;
for i in 0..11 {
bits.push(index & (1 << (10 - i)) > 0);
}
Expand Down Expand Up @@ -178,9 +174,7 @@ impl Mnemonic {
if check_bytes_result && check_bits_result {
Ok(data.to_vec())
} else {
Err(WalletError::Generic(
"Invalid mnemonic checking bits not match",
))
Err(WalletError::MnemonicChecksumNotMatch)
}
}
}
Expand All @@ -193,11 +187,7 @@ mod test {
use std::io::Read;
use bitcoin::network::constants::Network;
use crate::keyfactory::{Seed, KeyFactory};

extern crate rustc_serialize;
extern crate hex;
use self::rustc_serialize::json::Json;
use self::hex::decode;
use rustc_serialize::json::Json;

#[test]
fn test_mnemonic() {
Expand All @@ -214,15 +204,15 @@ mod test {

for t in 0..tests.len() {
let values = tests[t].as_array().unwrap();
let data = decode(values[0].as_string().unwrap()).unwrap();
let data = hex::decode(values[0].as_string().unwrap()).unwrap();
let mnemonic = Mnemonic::from(values[1].as_string().unwrap()).unwrap();
let seed = Seed::new(&mnemonic, "TREZOR");
assert_eq!(
mnemonic.to_string(),
Mnemonic::mnemonic(data.as_slice()).unwrap().to_string()
);
assert_eq!(mnemonic.data().unwrap(), data);
assert_eq!(seed.data(), decode(values[2].as_string().unwrap()).unwrap());
assert_eq!(seed.data(), hex::decode(values[2].as_string().unwrap()).unwrap());

if values.len() == 4 {
let pk = values[3].as_string().unwrap();
Expand Down

0 comments on commit ccaa79f

Please sign in to comment.