From 63880f71c70e1d8c27d39edb8ef1a3ac2214ab49 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Tue, 4 Sep 2018 10:58:26 +0100 Subject: [PATCH] BIP32 Lib Integration (#1454) * Test integrating BIP-32 implementation (not complete) * Test integrating BIP-32 implementation (not complete) * factor out bip32 crypto functions into trait * rustfmt * compliation * rustfmt * fixes for test vectors.. all work now with hashes specified in BIP32 * rustfmt * move reference hasher out of test --- Cargo.lock | 14 + keychain/Cargo.toml | 2 + keychain/src/base58.rs | 428 ++++++++++++++++++ keychain/src/extkey.rs | 4 +- keychain/src/extkey_bip32.rs | 818 +++++++++++++++++++++++++++++++++++ keychain/src/keychain.rs | 32 +- keychain/src/lib.rs | 4 + util/src/lib.rs | 6 +- util/src/macros.rs | 262 +++++++++++ 9 files changed, 1553 insertions(+), 17 deletions(-) create mode 100644 keychain/src/base58.rs create mode 100644 keychain/src/extkey_bip32.rs create mode 100644 util/src/macros.rs diff --git a/Cargo.lock b/Cargo.lock index 558af6d6a..290ad280f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -713,6 +713,7 @@ dependencies = [ "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "grin_util 0.3.0", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.70 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1717,6 +1718,18 @@ dependencies = [ "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustc-demangle" version = "0.1.9" @@ -2629,6 +2642,7 @@ dependencies = [ "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum reqwest 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5866613d84e2a39c0479a960bf2d0eff1fbfc934f02cd42b5c08c1e1efc5b1fd" +"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" diff --git a/keychain/Cargo.toml b/keychain/Cargo.toml index 5a37c9cd5..8d742ce50 100644 --- a/keychain/Cargo.toml +++ b/keychain/Cargo.toml @@ -14,5 +14,7 @@ serde = "1" serde_derive = "1" serde_json = "1" uuid = { version = "0.6", features = ["serde", "v4"] } +#only used for hashing +rust-crypto = "0.2" grin_util = { path = "../util" } diff --git a/keychain/src/base58.rs b/keychain/src/base58.rs new file mode 100644 index 000000000..2703722e8 --- /dev/null +++ b/keychain/src/base58.rs @@ -0,0 +1,428 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! Base58 encoder and decoder + +use std::{error, fmt, mem, str}; + +use byteorder::{ByteOrder, LittleEndian}; +use crypto::digest::Digest; +use crypto::sha2::Sha256; + +/// Sha256dHash +fn sha256d_hash(data: &[u8]) -> [u8; 32] { + let mut ret = [0; 32]; + let mut sha2 = Sha256::new(); + sha2.input(data); + sha2.result(&mut ret); + sha2.reset(); + sha2.input(&ret); + sha2.result(&mut ret); + ret +} + +#[inline] +pub fn into_le_low_u32(data: [u8; 32]) -> u32 { + let mut ret: [u64; 4] = unsafe { mem::transmute(data) }; + for x in (&mut ret).iter_mut() { + *x = x.to_le(); + } + ret[0] as u32 +} + +/// An error that might occur during base58 decoding +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Error { + /// Invalid character encountered + BadByte(u8), + /// Checksum was not correct (expected, actual) + BadChecksum(u32, u32), + /// The length (in bytes) of the object was not correct + /// Note that if the length is excessively long the provided length may be + /// an estimate (and the checksum step may be skipped). + InvalidLength(usize), + /// Version byte(s) were not recognized + InvalidVersion(Vec), + /// Checked data was less than 4 bytes + TooShort(usize), + /// Any other error + Other(String), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::BadByte(b) => write!(f, "invalid base58 character 0x{:x}", b), + Error::BadChecksum(exp, actual) => write!( + f, + "base58ck checksum 0x{:x} does not match expected 0x{:x}", + actual, exp + ), + Error::InvalidLength(ell) => write!(f, "length {} invalid for this base58 type", ell), + Error::InvalidVersion(ref v) => { + write!(f, "version {:?} invalid for this base58 type", v) + } + Error::TooShort(_) => write!(f, "base58ck data not even long enough for a checksum"), + Error::Other(ref s) => f.write_str(s), + } + } +} + +impl error::Error for Error { + fn cause(&self) -> Option<&error::Error> { + None + } + fn description(&self) -> &'static str { + match *self { + Error::BadByte(_) => "invalid b58 character", + Error::BadChecksum(_, _) => "invalid b58ck checksum", + Error::InvalidLength(_) => "invalid length for b58 type", + Error::InvalidVersion(_) => "invalid version for b58 type", + Error::TooShort(_) => "b58ck data less than 4 bytes", + Error::Other(_) => "unknown b58 error", + } + } +} + +static BASE58_CHARS: &'static [u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +static BASE58_DIGITS: [Option; 128] = [ + None, + None, + None, + None, + None, + None, + None, + None, // 0-7 + None, + None, + None, + None, + None, + None, + None, + None, // 8-15 + None, + None, + None, + None, + None, + None, + None, + None, // 16-23 + None, + None, + None, + None, + None, + None, + None, + None, // 24-31 + None, + None, + None, + None, + None, + None, + None, + None, // 32-39 + None, + None, + None, + None, + None, + None, + None, + None, // 40-47 + None, + Some(0), + Some(1), + Some(2), + Some(3), + Some(4), + Some(5), + Some(6), // 48-55 + Some(7), + Some(8), + None, + None, + None, + None, + None, + None, // 56-63 + None, + Some(9), + Some(10), + Some(11), + Some(12), + Some(13), + Some(14), + Some(15), // 64-71 + Some(16), + None, + Some(17), + Some(18), + Some(19), + Some(20), + Some(21), + None, // 72-79 + Some(22), + Some(23), + Some(24), + Some(25), + Some(26), + Some(27), + Some(28), + Some(29), // 80-87 + Some(30), + Some(31), + Some(32), + None, + None, + None, + None, + None, // 88-95 + None, + Some(33), + Some(34), + Some(35), + Some(36), + Some(37), + Some(38), + Some(39), // 96-103 + Some(40), + Some(41), + Some(42), + Some(43), + None, + Some(44), + Some(45), + Some(46), // 104-111 + Some(47), + Some(48), + Some(49), + Some(50), + Some(51), + Some(52), + Some(53), + Some(54), // 112-119 + Some(55), + Some(56), + Some(57), + None, + None, + None, + None, + None, // 120-127 +]; + +/// Decode base58-encoded string into a byte vector +pub fn from(data: &str) -> Result, Error> { + // 11/15 is just over log_256(58) + let mut scratch = vec![0u8; 1 + data.len() * 11 / 15]; + // Build in base 256 + for d58 in data.bytes() { + // Compute "X = X * 58 + next_digit" in base 256 + if d58 as usize > BASE58_DIGITS.len() { + return Err(Error::BadByte(d58)); + } + let mut carry = match BASE58_DIGITS[d58 as usize] { + Some(d58) => d58 as u32, + None => { + return Err(Error::BadByte(d58)); + } + }; + for d256 in scratch.iter_mut().rev() { + carry += *d256 as u32 * 58; + *d256 = carry as u8; + carry /= 256; + } + assert_eq!(carry, 0); + } + + // Copy leading zeroes directly + let mut ret: Vec = data + .bytes() + .take_while(|&x| x == BASE58_CHARS[0]) + .map(|_| 0) + .collect(); + // Copy rest of string + ret.extend(scratch.into_iter().skip_while(|&x| x == 0)); + Ok(ret) +} + +/// Decode a base58check-encoded string +pub fn from_check(data: &str) -> Result, Error> { + let mut ret: Vec = from(data)?; + if ret.len() < 4 { + return Err(Error::TooShort(ret.len())); + } + let ck_start = ret.len() - 4; + let expected = sha256d_hash(&ret[..ck_start]); + let expected = into_le_low_u32(expected); + let actual = LittleEndian::read_u32(&ret[ck_start..(ck_start + 4)]); + if expected != actual { + return Err(Error::BadChecksum(expected, actual)); + } + + ret.truncate(ck_start); + Ok(ret) +} + +fn encode_iter_utf8(data: I) -> Vec +where + I: Iterator + Clone, +{ + let (len, _) = data.size_hint(); + + // 7/5 is just over log_58(256) + let mut ret = Vec::with_capacity(1 + len * 7 / 5); + + let mut leading_zero_count = 0; + let mut leading_zeroes = true; + // Build string in little endian with 0-58 in place of characters... + for d256 in data { + let mut carry = d256 as usize; + if leading_zeroes && carry == 0 { + leading_zero_count += 1; + } else { + leading_zeroes = false; + } + + for ch in ret.iter_mut() { + let new_ch = *ch as usize * 256 + carry; + *ch = (new_ch % 58) as u8; + carry = new_ch / 58; + } + while carry > 0 { + ret.push((carry % 58) as u8); + carry /= 58; + } + } + + // ... then reverse it and convert to chars + for _ in 0..leading_zero_count { + ret.push(0); + } + ret.reverse(); + for ch in ret.iter_mut() { + *ch = BASE58_CHARS[*ch as usize]; + } + ret +} + +fn encode_iter(data: I) -> String +where + I: Iterator + Clone, +{ + let ret = encode_iter_utf8(data); + String::from_utf8(ret).unwrap() +} + +/// Directly encode a slice as base58 into a `Formatter`. +fn _encode_iter_to_fmt(fmt: &mut fmt::Formatter, data: I) -> fmt::Result +where + I: Iterator + Clone, +{ + let ret = encode_iter_utf8(data); + fmt.write_str(str::from_utf8(&ret).unwrap()) +} + +/// Directly encode a slice as base58 +pub fn _encode_slice(data: &[u8]) -> String { + encode_iter(data.iter().cloned()) +} + +/// Obtain a string with the base58check encoding of a slice +/// (Tack the first 4 256-digits of the object's Bitcoin hash onto the end.) +pub fn check_encode_slice(data: &[u8]) -> String { + let checksum = sha256d_hash(&data); + encode_iter(data.iter().cloned().chain(checksum[0..4].iter().cloned())) +} + +/// Obtain a string with the base58check encoding of a slice +/// (Tack the first 4 256-digits of the object's Bitcoin hash onto the end.) +pub fn _check_encode_slice_to_fmt(fmt: &mut fmt::Formatter, data: &[u8]) -> fmt::Result { + let checksum = sha256d_hash(&data); + let iter = data.iter().cloned().chain(checksum[0..4].iter().cloned()); + _encode_iter_to_fmt(fmt, iter) +} + +#[cfg(test)] +mod tests { + use super::*; + use util::from_hex; + + #[test] + fn test_base58_encode() { + // Basics + assert_eq!(&_encode_slice(&[0][..]), "1"); + assert_eq!(&_encode_slice(&[1][..]), "2"); + assert_eq!(&_encode_slice(&[58][..]), "21"); + assert_eq!(&_encode_slice(&[13, 36][..]), "211"); + + // Leading zeroes + assert_eq!(&_encode_slice(&[0, 13, 36][..]), "1211"); + assert_eq!(&_encode_slice(&[0, 0, 0, 0, 13, 36][..]), "1111211"); + + // Addresses + let addr = from_hex("00f8917303bfa8ef24f292e8fa1419b20460ba064d".to_owned()).unwrap(); + assert_eq!( + &check_encode_slice(&addr[..]), + "1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH" + ); + } + + #[test] + fn test_base58_decode() { + // Basics + assert_eq!(from("1").ok(), Some(vec![0u8])); + assert_eq!(from("2").ok(), Some(vec![1u8])); + assert_eq!(from("21").ok(), Some(vec![58u8])); + assert_eq!(from("211").ok(), Some(vec![13u8, 36])); + + // Leading zeroes + assert_eq!(from("1211").ok(), Some(vec![0u8, 13, 36])); + assert_eq!(from("111211").ok(), Some(vec![0u8, 0, 0, 13, 36])); + + // Addresses + assert_eq!( + from_check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH").ok(), + Some(from_hex("00f8917303bfa8ef24f292e8fa1419b20460ba064d".to_owned()).unwrap()) + ) + } + + #[test] + fn test_base58_roundtrip() { + let s = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"; + let v: Vec = from_check(s).unwrap(); + assert_eq!(check_encode_slice(&v[..]), s); + assert_eq!(from_check(&check_encode_slice(&v[..])).ok(), Some(v)); + } +} diff --git a/keychain/src/extkey.rs b/keychain/src/extkey.rs index f01afab9b..d6d3f852b 100644 --- a/keychain/src/extkey.rs +++ b/keychain/src/extkey.rs @@ -15,8 +15,8 @@ use blake2::blake2b::blake2b; use byteorder::{BigEndian, ByteOrder}; use types::{Error, Identifier}; -use util::secp::Secp256k1; use util::secp::key::SecretKey; +use util::secp::Secp256k1; #[derive(Debug, Clone)] pub struct ChildKey { @@ -119,8 +119,8 @@ mod test { use super::{ExtendedKey, Identifier}; use util; - use util::secp::Secp256k1; use util::secp::key::SecretKey; + use util::secp::Secp256k1; fn from_hex(hex_str: &str) -> Vec { util::from_hex(hex_str.to_string()).unwrap() diff --git a/keychain/src/extkey_bip32.rs b/keychain/src/extkey_bip32.rs new file mode 100644 index 000000000..5c43457da --- /dev/null +++ b/keychain/src/extkey_bip32.rs @@ -0,0 +1,818 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! Implementation of BIP32 hierarchical deterministic wallets, as defined +//! at https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki +//! Modified from above to integrate into grin and allow for different +//! hashing algorithms if desired + +#[cfg(feature = "serde")] +use serde; +use std::default::Default; +use std::io::Cursor; +use std::str::FromStr; +use std::{error, fmt}; + +use byteorder::{BigEndian, ByteOrder, ReadBytesExt}; +use util::secp::key::{PublicKey, SecretKey}; +use util::secp::{self, ContextFlag, Secp256k1}; + +use crypto::digest::Digest; +use crypto::hmac::Hmac; +use crypto::mac::Mac; +use crypto::ripemd160::Ripemd160; +use crypto::sha2::{Sha256, Sha512}; + +use base58; + +/// A chain code +pub struct ChainCode([u8; 32]); +impl_array_newtype!(ChainCode, u8, 32); +impl_array_newtype_show!(ChainCode); +impl_array_newtype_encodable!(ChainCode, u8, 32); + +/// A fingerprint +pub struct Fingerprint([u8; 4]); +impl_array_newtype!(Fingerprint, u8, 4); +impl_array_newtype_show!(Fingerprint); +impl_array_newtype_encodable!(Fingerprint, u8, 4); + +impl Default for Fingerprint { + fn default() -> Fingerprint { + Fingerprint([0, 0, 0, 0]) + } +} + +/// Allow different implementations of hash functions used in BIP32 Derivations +/// Grin uses blake2 everywhere but the spec calls for SHA512/Ripemd160, so allow +/// this in future and allow us to unit test against published BIP32 test vectors +/// The function names refer to the place of the hash in the reference BIP32 spec, +/// not what the actual implementation is + +pub trait BIP32Hasher { + fn network_priv() -> [u8; 4]; + fn network_pub() -> [u8; 4]; + fn master_seed() -> [u8; 12]; + fn init_sha512(&mut self, seed: &[u8]); + fn append_sha512(&mut self, value: &[u8]); + fn result_sha512(&mut self) -> [u8; 64]; + fn sha_256(&self, input: &[u8]) -> [u8; 32]; + fn ripemd_160(&self, input: &[u8]) -> [u8; 20]; +} + +/// Implementation of the above that uses the standard BIP32 Hash algorithms +pub struct BIP32ReferenceHasher { + hmac_sha512: Hmac, +} + +impl BIP32ReferenceHasher { + /// New empty hasher + pub fn new() -> BIP32ReferenceHasher { + BIP32ReferenceHasher { + hmac_sha512: Hmac::new(Sha512::new(), &[0u8]), + } + } +} + +impl BIP32Hasher for BIP32ReferenceHasher { + fn network_priv() -> [u8; 4] { + // bitcoin network (xprv) (for test vectors) + [0x04, 0x88, 0xAD, 0xE4] + } + fn network_pub() -> [u8; 4] { + // bitcoin network (xpub) (for test vectors) + [0x04, 0x88, 0xB2, 0x1E] + } + fn master_seed() -> [u8; 12] { + b"Bitcoin seed".to_owned() + } + fn init_sha512(&mut self, seed: &[u8]) { + self.hmac_sha512 = Hmac::new(Sha512::new(), seed); + } + fn append_sha512(&mut self, value: &[u8]) { + self.hmac_sha512.input(value); + } + fn result_sha512(&mut self) -> [u8; 64] { + let mut result = [0; 64]; + self.hmac_sha512.raw_result(&mut result); + result + } + fn sha_256(&self, input: &[u8]) -> [u8; 32] { + let mut sha2_res = [0; 32]; + let mut sha2 = Sha256::new(); + sha2.input(input); + sha2.result(&mut sha2_res); + sha2_res + } + fn ripemd_160(&self, input: &[u8]) -> [u8; 20] { + let mut ripemd_res = [0; 20]; + let mut ripemd = Ripemd160::new(); + ripemd.input(input); + ripemd.result(&mut ripemd_res); + ripemd_res + } +} + +/// Extended private key +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct ExtendedPrivKey { + /// The network this key is to be used on + pub network: [u8; 4], + /// How many derivations this key is from the master (which is 0) + pub depth: u8, + /// Fingerprint of the parent key (0 for master) + pub parent_fingerprint: Fingerprint, + /// Child number of the key used to derive from parent (0 for master) + pub child_number: ChildNumber, + /// Secret key + pub secret_key: SecretKey, + /// Chain code + pub chain_code: ChainCode, +} + +/// Extended public key +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct ExtendedPubKey { + /// The network this key is to be used on + pub network: [u8; 4], + /// How many derivations this key is from the master (which is 0) + pub depth: u8, + /// Fingerprint of the parent key + pub parent_fingerprint: Fingerprint, + /// Child number of the key used to derive from parent (0 for master) + pub child_number: ChildNumber, + /// Public key + pub public_key: PublicKey, + /// Chain code + pub chain_code: ChainCode, +} + +/// A child number for a derived key +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum ChildNumber { + /// Non-hardened key + Normal { + /// Key index, within [0, 2^31 - 1] + index: u32, + }, + /// Hardened key + Hardened { + /// Key index, within [0, 2^31 - 1] + index: u32, + }, +} + +impl ChildNumber { + /// Create a [`Normal`] from an index, panics if the index is not within + /// [0, 2^31 - 1]. + /// + /// [`Normal`]: #variant.Normal + pub fn from_normal_idx(index: u32) -> Self { + assert_eq!( + index & (1 << 31), + 0, + "ChildNumber indices have to be within [0, 2^31 - 1], is: {}", + index + ); + ChildNumber::Normal { index: index } + } + + /// Create a [`Hardened`] from an index, panics if the index is not within + /// [0, 2^31 - 1]. + /// + /// [`Hardened`]: #variant.Hardened + pub fn from_hardened_idx(index: u32) -> Self { + assert_eq!( + index & (1 << 31), + 0, + "ChildNumber indices have to be within [0, 2^31 - 1], is: {}", + index + ); + ChildNumber::Hardened { index: index } + } + + /// Returns `true` if the child number is a [`Normal`] value. + /// + /// [`Normal`]: #variant.Normal + pub fn is_normal(&self) -> bool { + !self.is_hardened() + } + + /// Returns `true` if the child number is a [`Hardened`] value. + /// + /// [`Hardened`]: #variant.Hardened + pub fn is_hardened(&self) -> bool { + match *self { + ChildNumber::Hardened { .. } => true, + ChildNumber::Normal { .. } => false, + } + } +} + +impl From for ChildNumber { + fn from(number: u32) -> Self { + if number & (1 << 31) != 0 { + ChildNumber::Hardened { + index: number ^ (1 << 31), + } + } else { + ChildNumber::Normal { index: number } + } + } +} + +impl From for u32 { + fn from(cnum: ChildNumber) -> Self { + match cnum { + ChildNumber::Normal { index } => index, + ChildNumber::Hardened { index } => index | (1 << 31), + } + } +} + +impl fmt::Display for ChildNumber { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ChildNumber::Hardened { index } => write!(f, "{}'", index), + ChildNumber::Normal { index } => write!(f, "{}", index), + } + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for ChildNumber { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + u32::deserialize(deserializer).map(ChildNumber::from) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for ChildNumber { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + u32::from(*self).serialize(serializer) + } +} + +/// A BIP32 error +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Error { + /// A pk->pk derivation was attempted on a hardened key + CannotDeriveFromHardenedKey, + /// A secp256k1 error occured + Ecdsa(secp::Error), + /// A child number was provided that was out of range + InvalidChildNumber(ChildNumber), + /// Error creating a master seed --- for application use + RngError(String), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::CannotDeriveFromHardenedKey => { + f.write_str("cannot derive hardened key from public key") + } + Error::Ecdsa(ref e) => fmt::Display::fmt(e, f), + Error::InvalidChildNumber(ref n) => write!(f, "child number {} is invalid", n), + Error::RngError(ref s) => write!(f, "rng error {}", s), + } + } +} + +impl error::Error for Error { + fn cause(&self) -> Option<&error::Error> { + if let Error::Ecdsa(ref e) = *self { + Some(e) + } else { + None + } + } + + fn description(&self) -> &str { + match *self { + Error::CannotDeriveFromHardenedKey => "cannot derive hardened key from public key", + Error::Ecdsa(ref e) => error::Error::description(e), + Error::InvalidChildNumber(_) => "child number is invalid", + Error::RngError(_) => "rng error", + } + } +} + +impl From for Error { + fn from(e: secp::Error) -> Error { + Error::Ecdsa(e) + } +} + +impl ExtendedPrivKey { + /// Construct a new master key from a seed value + pub fn new_master( + secp: &Secp256k1, + hasher: &mut H, + seed: &[u8], + ) -> Result + where + H: BIP32Hasher, + { + hasher.init_sha512(&H::master_seed()); + hasher.append_sha512(seed); + let result = hasher.result_sha512(); + + Ok(ExtendedPrivKey { + network: H::network_priv(), + depth: 0, + parent_fingerprint: Default::default(), + child_number: ChildNumber::from_normal_idx(0), + secret_key: SecretKey::from_slice(secp, &result[..32]).map_err(Error::Ecdsa)?, + chain_code: ChainCode::from(&result[32..]), + }) + } + + /// Attempts to derive an extended private key from a path. + pub fn derive_priv( + &self, + secp: &Secp256k1, + hasher: &mut H, + cnums: &[ChildNumber], + ) -> Result + where + H: BIP32Hasher, + { + let mut sk: ExtendedPrivKey = *self; + for cnum in cnums { + sk = sk.ckd_priv(secp, hasher, *cnum)?; + } + Ok(sk) + } + + /// Private->Private child key derivation + pub fn ckd_priv( + &self, + secp: &Secp256k1, + hasher: &mut H, + i: ChildNumber, + ) -> Result + where + H: BIP32Hasher, + { + hasher.init_sha512(&self.chain_code[..]); + let mut be_n = [0; 4]; + match i { + ChildNumber::Normal { .. } => { + // Non-hardened key: compute public data and use that + hasher.append_sha512( + &PublicKey::from_secret_key(secp, &self.secret_key)?.serialize_vec(secp, true) + [..], + ); + } + ChildNumber::Hardened { .. } => { + // Hardened key: use only secret data to prevent public derivation + hasher.append_sha512(&[0u8]); + hasher.append_sha512(&self.secret_key[..]); + } + } + BigEndian::write_u32(&mut be_n, u32::from(i)); + + hasher.append_sha512(&be_n); + let result = hasher.result_sha512(); + let mut sk = SecretKey::from_slice(secp, &result[..32]).map_err(Error::Ecdsa)?; + sk.add_assign(secp, &self.secret_key).map_err(Error::Ecdsa)?; + + Ok(ExtendedPrivKey { + network: self.network, + depth: self.depth + 1, + parent_fingerprint: self.fingerprint(hasher), + child_number: i, + secret_key: sk, + chain_code: ChainCode::from(&result[32..]), + }) + } + + /// Returns the HASH160 of the chaincode + pub fn identifier(&self, hasher: &mut H) -> [u8; 20] + where + H: BIP32Hasher, + { + let secp = Secp256k1::with_caps(ContextFlag::SignOnly); + // Compute extended public key + let pk: ExtendedPubKey = ExtendedPubKey::from_private::(&secp, self); + // Do SHA256 of just the ECDSA pubkey + let sha2_res = hasher.sha_256(&pk.public_key.serialize_vec(&secp, true)[..]); + // do RIPEMD160 + let ripemd_res = hasher.ripemd_160(&sha2_res); + // Return + ripemd_res + } + + /// Returns the first four bytes of the identifier + pub fn fingerprint(&self, hasher: &mut H) -> Fingerprint + where + H: BIP32Hasher, + { + Fingerprint::from(&self.identifier(hasher)[0..4]) + } +} + +impl ExtendedPubKey { + /// Derives a public key from a private key + pub fn from_private(secp: &Secp256k1, sk: &ExtendedPrivKey) -> ExtendedPubKey + where + H: BIP32Hasher, + { + ExtendedPubKey { + network: H::network_pub(), + depth: sk.depth, + parent_fingerprint: sk.parent_fingerprint, + child_number: sk.child_number, + public_key: PublicKey::from_secret_key(secp, &sk.secret_key).unwrap(), + chain_code: sk.chain_code, + } + } + + /// Attempts to derive an extended public key from a path. + pub fn derive_pub( + &self, + secp: &Secp256k1, + hasher: &mut H, + cnums: &[ChildNumber], + ) -> Result + where + H: BIP32Hasher, + { + let mut pk: ExtendedPubKey = *self; + for cnum in cnums { + pk = pk.ckd_pub(secp, hasher, *cnum)? + } + Ok(pk) + } + + /// Compute the scalar tweak added to this key to get a child key + pub fn ckd_pub_tweak( + &self, + secp: &Secp256k1, + hasher: &mut H, + i: ChildNumber, + ) -> Result<(SecretKey, ChainCode), Error> + where + H: BIP32Hasher, + { + match i { + ChildNumber::Hardened { .. } => Err(Error::CannotDeriveFromHardenedKey), + ChildNumber::Normal { index: n } => { + hasher.init_sha512(&self.chain_code[..]); + hasher.append_sha512(&self.public_key.serialize_vec(secp, true)[..]); + let mut be_n = [0; 4]; + BigEndian::write_u32(&mut be_n, n); + hasher.append_sha512(&be_n); + + let mut result = hasher.result_sha512(); + + let secret_key = SecretKey::from_slice(secp, &result[..32])?; + let chain_code = ChainCode::from(&result[32..]); + Ok((secret_key, chain_code)) + } + } + } + + /// Public->Public child key derivation + pub fn ckd_pub( + &self, + secp: &Secp256k1, + hasher: &mut H, + i: ChildNumber, + ) -> Result + where + H: BIP32Hasher, + { + let (sk, chain_code) = self.ckd_pub_tweak(secp, hasher, i)?; + let mut pk = self.public_key.clone(); + pk.add_exp_assign(secp, &sk).map_err(Error::Ecdsa)?; + + Ok(ExtendedPubKey { + network: self.network, + depth: self.depth + 1, + parent_fingerprint: self.fingerprint(secp, hasher), + child_number: i, + public_key: pk, + chain_code: chain_code, + }) + } + + /// Returns the HASH160 of the chaincode + pub fn identifier(&self, secp: &Secp256k1, hasher: &mut H) -> [u8; 20] + where + H: BIP32Hasher, + { + // Do SHA256 of just the ECDSA pubkey + let sha2_res = hasher.sha_256(&self.public_key.serialize_vec(secp, true)[..]); + // do RIPEMD160 + let ripemd_res = hasher.ripemd_160(&sha2_res); + // Return + ripemd_res + } + + /// Returns the first four bytes of the identifier + pub fn fingerprint(&self, secp: &Secp256k1, hasher: &mut H) -> Fingerprint + where + H: BIP32Hasher, + { + Fingerprint::from(&self.identifier(secp, hasher)[0..4]) + } +} + +impl fmt::Display for ExtendedPrivKey { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let mut ret = [0; 78]; + ret[0..4].copy_from_slice(&self.network[0..4]); + ret[4] = self.depth as u8; + ret[5..9].copy_from_slice(&self.parent_fingerprint[..]); + + BigEndian::write_u32(&mut ret[9..13], u32::from(self.child_number)); + + ret[13..45].copy_from_slice(&self.chain_code[..]); + ret[45] = 0; + ret[46..78].copy_from_slice(&self.secret_key[..]); + fmt.write_str(&base58::check_encode_slice(&ret[..])) + } +} + +impl FromStr for ExtendedPrivKey { + type Err = base58::Error; + + fn from_str(inp: &str) -> Result { + let s = Secp256k1::without_caps(); + let data = base58::from_check(inp)?; + + if data.len() != 78 { + return Err(base58::Error::InvalidLength(data.len())); + } + + let cn_int: u32 = Cursor::new(&data[9..13]).read_u32::().unwrap(); + let child_number: ChildNumber = ChildNumber::from(cn_int); + + let mut network = [0; 4]; + network.copy_from_slice(&data[0..4]); + + Ok(ExtendedPrivKey { + network: network, + depth: data[4], + parent_fingerprint: Fingerprint::from(&data[5..9]), + child_number: child_number, + chain_code: ChainCode::from(&data[13..45]), + secret_key: SecretKey::from_slice(&s, &data[46..78]) + .map_err(|e| base58::Error::Other(e.to_string()))?, + }) + } +} + +impl fmt::Display for ExtendedPubKey { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let secp = Secp256k1::without_caps(); + let mut ret = [0; 78]; + ret[0..4].copy_from_slice(&self.network[0..4]); + ret[4] = self.depth as u8; + ret[5..9].copy_from_slice(&self.parent_fingerprint[..]); + + BigEndian::write_u32(&mut ret[9..13], u32::from(self.child_number)); + + ret[13..45].copy_from_slice(&self.chain_code[..]); + ret[45..78].copy_from_slice(&self.public_key.serialize_vec(&secp, true)[..]); + fmt.write_str(&base58::check_encode_slice(&ret[..])) + } +} + +impl FromStr for ExtendedPubKey { + type Err = base58::Error; + + fn from_str(inp: &str) -> Result { + let s = Secp256k1::without_caps(); + let data = base58::from_check(inp)?; + + if data.len() != 78 { + return Err(base58::Error::InvalidLength(data.len())); + } + + let cn_int: u32 = Cursor::new(&data[9..13]).read_u32::().unwrap(); + let child_number: ChildNumber = ChildNumber::from(cn_int); + + let mut network = [0; 4]; + network.copy_from_slice(&data[0..4]); + + Ok(ExtendedPubKey { + network: network, + depth: data[4], + parent_fingerprint: Fingerprint::from(&data[5..9]), + child_number: child_number, + chain_code: ChainCode::from(&data[13..45]), + public_key: PublicKey::from_slice(&s, &data[45..78]) + .map_err(|e| base58::Error::Other(e.to_string()))?, + }) + } +} + +#[cfg(test)] +mod tests { + extern crate crypto; + + use std::str::FromStr; + use std::string::ToString; + + use util::from_hex; + use util::secp::Secp256k1; + + use super::ChildNumber::{Hardened, Normal}; + use super::Error; + use super::{ChildNumber, ExtendedPrivKey, ExtendedPubKey}; + + use super::BIP32ReferenceHasher; + + fn test_path( + secp: &Secp256k1, + seed: &[u8], + path: &[ChildNumber], + expected_sk: &str, + expected_pk: &str, + ) { + let mut h = BIP32ReferenceHasher::new(); + let mut sk = ExtendedPrivKey::new_master(secp, &mut h, seed).unwrap(); + let mut pk = ExtendedPubKey::from_private::(secp, &sk); + + // Check derivation convenience method for ExtendedPrivKey + assert_eq!( + &sk.derive_priv(secp, &mut h, path).unwrap().to_string()[..], + expected_sk + ); + + // Check derivation convenience method for ExtendedPubKey, should error + // appropriately if any ChildNumber is hardened + if path.iter().any(|cnum| cnum.is_hardened()) { + assert_eq!( + pk.derive_pub(secp, &mut h, path), + Err(Error::CannotDeriveFromHardenedKey) + ); + } else { + assert_eq!( + &pk.derive_pub(secp, &mut h, path).unwrap().to_string()[..], + expected_pk + ); + } + + // Derive keys, checking hardened and non-hardened derivation one-by-one + for &num in path.iter() { + sk = sk.ckd_priv(secp, &mut h, num).unwrap(); + match num { + Normal { .. } => { + let pk2 = pk.ckd_pub(secp, &mut h, num).unwrap(); + pk = ExtendedPubKey::from_private::(secp, &sk); + assert_eq!(pk, pk2); + } + Hardened { .. } => { + assert_eq!( + pk.ckd_pub(secp, &mut h, num), + Err(Error::CannotDeriveFromHardenedKey) + ); + pk = ExtendedPubKey::from_private::(secp, &sk); + } + } + } + + // Check result against expected base58 + assert_eq!(&sk.to_string()[..], expected_sk); + assert_eq!(&pk.to_string()[..], expected_pk); + // Check decoded base58 against result + let decoded_sk = ExtendedPrivKey::from_str(expected_sk); + let decoded_pk = ExtendedPubKey::from_str(expected_pk); + assert_eq!(Ok(sk), decoded_sk); + assert_eq!(Ok(pk), decoded_pk); + } + + #[test] + fn test_vector_1() { + let secp = Secp256k1::new(); + let seed = from_hex("000102030405060708090a0b0c0d0e0f".to_owned()).unwrap(); + + // m + test_path(&secp, &seed, &[], + "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", + "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"); + + // m/0h + test_path(&secp, &seed, &[ChildNumber::from_hardened_idx(0)], + "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", + "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"); + + // m/0h/1 + test_path(&secp, &seed, &[ChildNumber::from_hardened_idx(0), ChildNumber::from_normal_idx(1)], + "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", + "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"); + + // m/0h/1/2h + test_path(&secp, &seed, &[ChildNumber::from_hardened_idx(0), ChildNumber::from_normal_idx(1), ChildNumber::from_hardened_idx(2)], + "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", + "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"); + + // m/0h/1/2h/2 + test_path(&secp, &seed, &[ChildNumber::from_hardened_idx(0), ChildNumber::from_normal_idx(1), ChildNumber::from_hardened_idx(2), ChildNumber::from_normal_idx(2)], + "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", + "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"); + + // m/0h/1/2h/2/1000000000 + test_path(&secp, &seed, &[ChildNumber::from_hardened_idx(0), ChildNumber::from_normal_idx(1), ChildNumber::from_hardened_idx(2), ChildNumber::from_normal_idx(2), ChildNumber::from_normal_idx(1000000000)], + "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", + "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"); + } + + #[test] + fn test_vector_2() { + let secp = Secp256k1::new(); + let seed = from_hex("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542".to_owned()).unwrap(); + + // m + test_path(&secp, &seed, &[], + "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", + "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"); + + // m/0 + test_path(&secp, &seed, &[ChildNumber::from_normal_idx(0)], + "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", + "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"); + + // m/0/2147483647h + test_path(&secp, &seed, &[ChildNumber::from_normal_idx(0), ChildNumber::from_hardened_idx(2147483647)], + "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", + "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"); + + // m/0/2147483647h/1 + test_path(&secp, &seed, &[ChildNumber::from_normal_idx(0), ChildNumber::from_hardened_idx(2147483647), ChildNumber::from_normal_idx(1)], + "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", + "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"); + + // m/0/2147483647h/1/2147483646h + test_path(&secp, &seed, &[ChildNumber::from_normal_idx(0), ChildNumber::from_hardened_idx(2147483647), ChildNumber::from_normal_idx(1), ChildNumber::from_hardened_idx(2147483646)], + "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", + "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"); + + // m/0/2147483647h/1/2147483646h/2 + test_path(&secp, &seed, &[ChildNumber::from_normal_idx(0), ChildNumber::from_hardened_idx(2147483647), ChildNumber::from_normal_idx(1), ChildNumber::from_hardened_idx(2147483646), ChildNumber::from_normal_idx(2)], + "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", + "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"); + } + + #[test] + fn test_vector_3() { + let secp = Secp256k1::new(); + let seed = from_hex("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be".to_owned()).unwrap(); + + // m + test_path(&secp, &seed, &[], + "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6", + "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13"); + + // m/0h + test_path(&secp, &seed, &[ChildNumber::from_hardened_idx(0)], + "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L", + "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y"); + } + + #[test] + #[cfg(all(feature = "serde", feature = "strason"))] + pub fn encode_decode_childnumber() { + serde_round_trip!(ChildNumber::from_normal_idx(0)); + serde_round_trip!(ChildNumber::from_normal_idx(1)); + serde_round_trip!(ChildNumber::from_normal_idx((1 << 31) - 1)); + serde_round_trip!(ChildNumber::from_hardened_idx(0)); + serde_round_trip!(ChildNumber::from_hardened_idx(1)); + serde_round_trip!(ChildNumber::from_hardened_idx((1 << 31) - 1)); + } + +} diff --git a/keychain/src/keychain.rs b/keychain/src/keychain.rs index 3e03d3231..248a2aeab 100644 --- a/keychain/src/keychain.rs +++ b/keychain/src/keychain.rs @@ -104,17 +104,21 @@ impl Keychain for ExtKeychain { .filter_map(|k| self.derived_key(&k).ok()) .collect(); - pos_keys.extend(&blind_sum - .positive_blinding_factors - .iter() - .filter_map(|b| b.secret_key(&self.secp).ok()) - .collect::>()); + pos_keys.extend( + &blind_sum + .positive_blinding_factors + .iter() + .filter_map(|b| b.secret_key(&self.secp).ok()) + .collect::>(), + ); - neg_keys.extend(&blind_sum - .negative_blinding_factors - .iter() - .filter_map(|b| b.secret_key(&self.secp).ok()) - .collect::>()); + neg_keys.extend( + &blind_sum + .negative_blinding_factors + .iter() + .filter_map(|b| b.secret_key(&self.secp).ok()) + .collect::>(), + ); let sum = self.secp.blind_sum(pos_keys, neg_keys)?; Ok(BlindingFactor::from_secret_key(sum)) @@ -287,9 +291,11 @@ mod test { // in the same way (convenience function) assert_eq!( keychain - .blind_sum(&BlindSum::new() - .add_blinding_factor(BlindingFactor::from_secret_key(skey1)) - .add_blinding_factor(BlindingFactor::from_secret_key(skey2))) + .blind_sum( + &BlindSum::new() + .add_blinding_factor(BlindingFactor::from_secret_key(skey1)) + .add_blinding_factor(BlindingFactor::from_secret_key(skey2)) + ) .unwrap(), BlindingFactor::from_secret_key(skey3), ); diff --git a/keychain/src/lib.rs b/keychain/src/lib.rs index 92a986442..5f4b87250 100644 --- a/keychain/src/lib.rs +++ b/keychain/src/lib.rs @@ -16,6 +16,7 @@ extern crate blake2_rfc as blake2; extern crate byteorder; +#[macro_use] extern crate grin_util as util; extern crate rand; extern crate serde; @@ -24,9 +25,12 @@ extern crate serde_derive; extern crate serde_json; #[macro_use] extern crate slog; +extern crate crypto; extern crate uuid; +mod base58; pub mod extkey; +pub mod extkey_bip32; mod types; pub mod keychain; diff --git a/util/src/lib.rs b/util/src/lib.rs index 835e0feda..13a0caa21 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -53,6 +53,8 @@ pub use secp_static::static_secp_instance; pub mod types; pub use types::{LogLevel, LoggingConfig}; +pub mod macros; + // other utils use byteorder::{BigEndian, ByteOrder}; use std::cell::{Ref, RefCell}; @@ -62,10 +64,10 @@ use std::ops::Deref; mod hex; pub use hex::*; -/// Compress and decompress zip bz2 archives -pub mod zip; /// File util pub mod file; +/// Compress and decompress zip bz2 archives +pub mod zip; /// Encapsulation of a RefCell> for one-time initialization after /// construction. This implementation will purposefully fail hard if not used diff --git a/util/src/macros.rs b/util/src/macros.rs new file mode 100644 index 000000000..95fc5ca7f --- /dev/null +++ b/util/src/macros.rs @@ -0,0 +1,262 @@ +// Copyright 2018 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! Macros to support Rust BIP-32 code (though could conceivably be used for other things) + +#[macro_export] +macro_rules! impl_array_newtype { + ($thing:ident, $ty:ty, $len:expr) => { + impl $thing { + #[inline] + /// Converts the object to a raw pointer + pub fn as_ptr(&self) -> *const $ty { + let &$thing(ref dat) = self; + dat.as_ptr() + } + + #[inline] + /// Converts the object to a mutable raw pointer + pub fn as_mut_ptr(&mut self) -> *mut $ty { + let &mut $thing(ref mut dat) = self; + dat.as_mut_ptr() + } + + #[inline] + /// Returns the length of the object as an array + pub fn len(&self) -> usize { $len } + + #[inline] + /// Returns whether the object, as an array, is empty. Always false. + pub fn is_empty(&self) -> bool { false } + + #[inline] + /// Returns the underlying bytes. + pub fn as_bytes(&self) -> &[$ty; $len] { &self.0 } + + #[inline] + /// Returns the underlying bytes. + pub fn to_bytes(&self) -> [$ty; $len] { self.0.clone() } + + #[inline] + /// Returns the underlying bytes. + pub fn into_bytes(self) -> [$ty; $len] { self.0 } + } + + impl<'a> From<&'a [$ty]> for $thing { + fn from(data: &'a [$ty]) -> $thing { + assert_eq!(data.len(), $len); + let mut ret = [0; $len]; + ret.copy_from_slice(&data[..]); + $thing(ret) + } + } + + impl ::std::ops::Index for $thing { + type Output = $ty; + + #[inline] + fn index(&self, index: usize) -> &$ty { + let &$thing(ref dat) = self; + &dat[index] + } + } + + impl_index_newtype!($thing, $ty); + + impl PartialEq for $thing { + #[inline] + fn eq(&self, other: &$thing) -> bool { + &self[..] == &other[..] + } + } + + impl Eq for $thing {} + + impl PartialOrd for $thing { + #[inline] + fn partial_cmp(&self, other: &$thing) -> Option<::std::cmp::Ordering> { + Some(self.cmp(&other)) + } + } + + impl Ord for $thing { + #[inline] + fn cmp(&self, other: &$thing) -> ::std::cmp::Ordering { + // manually implement comparison to get little-endian ordering + // (we need this for our numeric types; non-numeric ones shouldn't + // be ordered anyway except to put them in BTrees or whatever, and + // they don't care how we order as long as we're consisistent). + for i in 0..$len { + if self[$len - 1 - i] < other[$len - 1 - i] { return ::std::cmp::Ordering::Less; } + if self[$len - 1 - i] > other[$len - 1 - i] { return ::std::cmp::Ordering::Greater; } + } + ::std::cmp::Ordering::Equal + } + } + + #[cfg_attr(feature = "clippy", allow(expl_impl_clone_on_copy))] // we don't define the `struct`, we have to explicitly impl + impl Clone for $thing { + #[inline] + fn clone(&self) -> $thing { + $thing::from(&self[..]) + } + } + + impl Copy for $thing {} + + impl ::std::hash::Hash for $thing { + #[inline] + fn hash(&self, state: &mut H) + where H: ::std::hash::Hasher + { + (&self[..]).hash(state); + } + + fn hash_slice(data: &[$thing], state: &mut H) + where H: ::std::hash::Hasher + { + for d in data.iter() { + (&d[..]).hash(state); + } + } + } + + impl ::rand::Rand for $thing { + #[inline] + fn rand(r: &mut R) -> $thing { + $thing(::rand::Rand::rand(r)) + } + } + } +} + +#[macro_export] +macro_rules! impl_array_newtype_encodable { + ($thing:ident, $ty:ty, $len:expr) => { + #[cfg(feature = "serde")] + impl<'de> $crate::serde::Deserialize<'de> for $thing { + fn deserialize(deserializer: D) -> Result + where + D: $crate::serde::Deserializer<'de>, + { + use $crate::std::fmt::{self, Formatter}; + + struct Visitor; + impl<'de> $crate::serde::de::Visitor<'de> for Visitor { + type Value = $thing; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("a fixed size array") + } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: $crate::serde::de::SeqAccess<'de>, + { + let mut ret: [$ty; $len] = [0; $len]; + for item in ret.iter_mut() { + *item = match seq.next_element()? { + Some(c) => c, + None => { + return Err($crate::serde::de::Error::custom("end of stream")) + } + }; + } + Ok($thing(ret)) + } + } + + deserializer.deserialize_seq(Visitor) + } + } + + #[cfg(feature = "serde")] + impl $crate::serde::Serialize for $thing { + fn serialize(&self, serializer: S) -> Result + where + S: $crate::serde::Serializer, + { + let &$thing(ref dat) = self; + (&dat[..]).serialize(serializer) + } + } + }; +} + +#[macro_export] +macro_rules! impl_array_newtype_show { + ($thing:ident) => { + impl ::std::fmt::Debug for $thing { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, concat!(stringify!($thing), "({:?})"), &self[..]) + } + } + }; +} + +#[macro_export] +macro_rules! impl_index_newtype { + ($thing:ident, $ty:ty) => { + impl ::std::ops::Index<::std::ops::Range> for $thing { + type Output = [$ty]; + + #[inline] + fn index(&self, index: ::std::ops::Range) -> &[$ty] { + &self.0[index] + } + } + + impl ::std::ops::Index<::std::ops::RangeTo> for $thing { + type Output = [$ty]; + + #[inline] + fn index(&self, index: ::std::ops::RangeTo) -> &[$ty] { + &self.0[index] + } + } + + impl ::std::ops::Index<::std::ops::RangeFrom> for $thing { + type Output = [$ty]; + + #[inline] + fn index(&self, index: ::std::ops::RangeFrom) -> &[$ty] { + &self.0[index] + } + } + + impl ::std::ops::Index<::std::ops::RangeFull> for $thing { + type Output = [$ty]; + + #[inline] + fn index(&self, _: ::std::ops::RangeFull) -> &[$ty] { + &self.0[..] + } + } + }; +}