mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
Create the beginning of the wallet library. (#36)
The wallet library will be used to manage the keys of the user. Currenty it only contains a minimal implementation of the extended keys. The format of the extended keys is subject to change
This commit is contained in:
parent
a3a06951ff
commit
24b107bdf7
4 changed files with 273 additions and 0 deletions
12
wallet/Cargo.toml
Normal file
12
wallet/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "grin_wallet"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Laurent Meunier <laurent.meunier95@gmail.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
secp256k1zkp = { path = "../secp256k1zkp" }
|
||||||
|
rust-crypto = "^0.2"
|
||||||
|
rand = "^0.3"
|
||||||
|
byteorder = "1"
|
||||||
|
rustc-serialize = "0.3.23"
|
13
wallet/src/constants.rs
Normal file
13
wallet/src/constants.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2016 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.
|
207
wallet/src/extendedkey.rs
Normal file
207
wallet/src/extendedkey.rs
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
use Error;
|
||||||
|
use secp::Secp256k1;
|
||||||
|
use secp::key::SecretKey;
|
||||||
|
use crypto::mac::Mac;
|
||||||
|
use crypto::hmac::Hmac;
|
||||||
|
use crypto::sha2::Sha256;
|
||||||
|
use crypto::sha2::Sha512;
|
||||||
|
use crypto::ripemd160::Ripemd160;
|
||||||
|
use crypto::digest::Digest;
|
||||||
|
use Error::{InvalidSeedSize, InvalidSliceSize, InvalidExtendedKey};
|
||||||
|
use byteorder::{ByteOrder, BigEndian};
|
||||||
|
|
||||||
|
/// An extended private key.
|
||||||
|
/// An ExtendedKey is a secret key which can be used to derive new
|
||||||
|
/// secret keys to blind the commitment of a transaction output.
|
||||||
|
/// To be usable, a secret key should have an amount assigned to it,
|
||||||
|
/// but when the key is derived, the amount is not known and must be
|
||||||
|
/// given.
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ExtendedKey {
|
||||||
|
/// Depth of the extended key
|
||||||
|
pub depth: u8,
|
||||||
|
/// Child number of the key
|
||||||
|
pub n_child: u32,
|
||||||
|
/// Parent key's fingerprint
|
||||||
|
pub fingerprint: [u8; 4],
|
||||||
|
/// Code of the derivation chain
|
||||||
|
pub chaincode: [u8; 32],
|
||||||
|
/// Actual private key
|
||||||
|
pub key: SecretKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtendedKey {
|
||||||
|
|
||||||
|
/// Creates a new extended key from a serialized one
|
||||||
|
pub fn from_slice(secp: &Secp256k1, slice: &[u8]) -> Result<ExtendedKey, Error> {
|
||||||
|
//TODO change when ser. ext. size is fixed
|
||||||
|
if slice.len() != 73 {
|
||||||
|
return Err(InvalidSliceSize);
|
||||||
|
}
|
||||||
|
let depth: u8 = slice[0];
|
||||||
|
let mut fingerprint: [u8; 4] = [0; 4];
|
||||||
|
(&mut fingerprint).copy_from_slice(&slice[1..5]);
|
||||||
|
let n_child = BigEndian::read_u32(&slice[5..9]);
|
||||||
|
let mut chaincode: [u8; 32] = [0; 32];
|
||||||
|
(&mut chaincode).copy_from_slice(&slice[9..41]);
|
||||||
|
let secret_key = match SecretKey::from_slice(secp, &slice[41..73]) {
|
||||||
|
Ok(key) => key,
|
||||||
|
Err(_) => return Err(InvalidExtendedKey),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ExtendedKey {
|
||||||
|
depth: depth,
|
||||||
|
fingerprint: fingerprint,
|
||||||
|
n_child: n_child,
|
||||||
|
chaincode: chaincode,
|
||||||
|
key: secret_key,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new extended master key from a seed
|
||||||
|
pub fn from_seed(secp: &Secp256k1, seed: &[u8]) -> Result<ExtendedKey, Error> {
|
||||||
|
let mut hmac = Hmac::new(Sha512::new(), b"Mimble seed");
|
||||||
|
match seed.len() {
|
||||||
|
16 | 32 | 64 => hmac.input(&seed),
|
||||||
|
_ => {
|
||||||
|
return Err(InvalidSeedSize)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut derived: [u8; 64] = [0; 64];
|
||||||
|
hmac.raw_result(&mut derived);
|
||||||
|
|
||||||
|
let mut chaincode: [u8; 32] = [0; 32];
|
||||||
|
(&mut chaincode).copy_from_slice(&derived[32..]);
|
||||||
|
// TODO Error handling
|
||||||
|
let secret_key = SecretKey::from_slice(&secp, &derived[0..32])
|
||||||
|
.expect("Error generating from seed");
|
||||||
|
|
||||||
|
Ok(ExtendedKey {
|
||||||
|
depth: 0,
|
||||||
|
fingerprint: [0; 4],
|
||||||
|
n_child: 0,
|
||||||
|
chaincode: chaincode,
|
||||||
|
key: secret_key,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the identifier of the key, which is the
|
||||||
|
/// Hash160 of the private key
|
||||||
|
pub fn identifier(&self) -> [u8; 20] {
|
||||||
|
let mut sha = Sha256::new();
|
||||||
|
sha.input(&self.key[..]);
|
||||||
|
|
||||||
|
let mut shres = [0; 32];
|
||||||
|
sha.result(&mut shres);
|
||||||
|
|
||||||
|
let mut ripe = Ripemd160::new();
|
||||||
|
ripe.input(&shres[..]);
|
||||||
|
|
||||||
|
let mut identifier = [0; 20];
|
||||||
|
ripe.result(&mut identifier);
|
||||||
|
return identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Derive an extended key from an extended key
|
||||||
|
pub fn derive(&self, secp: &Secp256k1, n: u32) -> Result<ExtendedKey, Error> {
|
||||||
|
let mut hmac = Hmac::new(Sha512::new(), &self.chaincode[..]);
|
||||||
|
let mut n_bytes: [u8; 4] = [0; 4];
|
||||||
|
BigEndian::write_u32(&mut n_bytes, n);
|
||||||
|
|
||||||
|
hmac.input(&self.key[..]);
|
||||||
|
hmac.input(&n_bytes[..]);
|
||||||
|
|
||||||
|
let mut derived = [0; 64];
|
||||||
|
hmac.raw_result(&mut derived);
|
||||||
|
|
||||||
|
let mut secret_key = SecretKey::from_slice(&secp, &derived[0..32])
|
||||||
|
.expect("Error deriving key");
|
||||||
|
secret_key.add_assign(secp, &self.key)
|
||||||
|
.expect("Error deriving key");
|
||||||
|
// TODO check if key != 0 ?
|
||||||
|
|
||||||
|
let mut chain_code: [u8; 32] = [0; 32];
|
||||||
|
(&mut chain_code).clone_from_slice(&derived[32..]);
|
||||||
|
|
||||||
|
let mut fingerprint: [u8; 4] = [0; 4];
|
||||||
|
let parent_identifier = self.identifier();
|
||||||
|
(&mut fingerprint).clone_from_slice(&parent_identifier[0..4]);
|
||||||
|
|
||||||
|
Ok(ExtendedKey {
|
||||||
|
depth: self.depth + 1,
|
||||||
|
fingerprint: fingerprint,
|
||||||
|
n_child: n,
|
||||||
|
chaincode: chain_code,
|
||||||
|
key: secret_key
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
extern crate rustc_serialize as serialize;
|
||||||
|
|
||||||
|
use secp::Secp256k1;
|
||||||
|
use secp::key::SecretKey;
|
||||||
|
use super::ExtendedKey;
|
||||||
|
use self::serialize::hex::{FromHex};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extkey_from_seed() {
|
||||||
|
//TODO More test vectors
|
||||||
|
let s = Secp256k1::new();
|
||||||
|
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||||
|
let extk = ExtendedKey::from_seed(&s, &seed.as_slice())
|
||||||
|
.unwrap();
|
||||||
|
let sec = "04a7d66a82221501e67f2665332180bd1192c5e58a2cd26613827deb8ba14e75".from_hex().unwrap();
|
||||||
|
let secret_key = SecretKey::from_slice(&s, sec.as_slice())
|
||||||
|
.unwrap();
|
||||||
|
let chaincode = "b7c6740dea1920ec629b3593678f6d8dc40fe6ec1ed824fcde37f476cd6c048c".from_hex().unwrap();
|
||||||
|
let fingerprint = "00000000".from_hex().unwrap();
|
||||||
|
let depth = 0;
|
||||||
|
let n_child = 0;
|
||||||
|
assert_eq!(extk.key, secret_key);
|
||||||
|
assert_eq!(extk.fingerprint, fingerprint.as_slice());
|
||||||
|
assert_eq!(extk.chaincode, chaincode.as_slice());
|
||||||
|
assert_eq!(extk.depth, depth);
|
||||||
|
assert_eq!(extk.n_child, n_child);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extkey_derivation() {
|
||||||
|
//TODO More test verctors
|
||||||
|
let s = Secp256k1::new();
|
||||||
|
let seed = "000102030405060708090a0b0c0d0e0f".from_hex().unwrap();
|
||||||
|
let extk = ExtendedKey::from_seed(&s, &seed.as_slice())
|
||||||
|
.unwrap();
|
||||||
|
let derived = extk.derive(&s, 0).unwrap();
|
||||||
|
let sec = "908bf3264b8f5f5a5be57d3b0afa36eb5dbcc464ff4da2cf71183e8ec755184b".from_hex().unwrap();
|
||||||
|
let secret_key = SecretKey::from_slice(&s, sec.as_slice())
|
||||||
|
.unwrap();
|
||||||
|
let chaincode = "e90c4559501fb956fa8ddcd6d08499691678cfd6d69e41efb9ee8e87f327e30a".from_hex().unwrap();
|
||||||
|
let fingerprint = "8963be69".from_hex().unwrap();
|
||||||
|
let depth = 1;
|
||||||
|
let n_child = 0;
|
||||||
|
assert_eq!(derived.key, secret_key);
|
||||||
|
assert_eq!(derived.fingerprint, fingerprint.as_slice());
|
||||||
|
assert_eq!(derived.chaincode, chaincode.as_slice());
|
||||||
|
assert_eq!(derived.depth, depth);
|
||||||
|
assert_eq!(derived.n_child, n_child);
|
||||||
|
}
|
||||||
|
}
|
41
wallet/src/lib.rs
Normal file
41
wallet/src/lib.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
|
||||||
|
extern crate secp256k1zkp as secp;
|
||||||
|
extern crate crypto;
|
||||||
|
extern crate rand;
|
||||||
|
extern crate byteorder;
|
||||||
|
|
||||||
|
use std::{error, fmt};
|
||||||
|
|
||||||
|
pub mod extendedkey;
|
||||||
|
pub mod constants;
|
||||||
|
|
||||||
|
/// An ExtKey error
|
||||||
|
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// The size of the seed is invalid
|
||||||
|
InvalidSeedSize,
|
||||||
|
InvalidSliceSize,
|
||||||
|
InvalidExtendedKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Passthrough Debug to Display, since errors should be user-visible
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
f.write_str(error::Error::description(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for Error {
|
||||||
|
fn cause(&self) -> Option<&error::Error> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
Error::InvalidSeedSize => "wallet: seed isn't of size 128, 256 or 512",
|
||||||
|
//TODO change when ser. ext. size is fixed
|
||||||
|
Error::InvalidSliceSize => "wallet: serialized extended key must be of size 73",
|
||||||
|
Error::InvalidExtendedKey => "wallet: the given serialized extended key is invalid",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue