From 62954f154994f0642463f9d4aae05f3efcd50647 Mon Sep 17 00:00:00 2001 From: AntiochP <30642645+antiochp@users.noreply.github.com> Date: Mon, 9 Oct 2017 13:19:26 -0400 Subject: [PATCH] Generate wallet key identifier from public key (#162) --- keychain/src/extkey.rs | 53 ++++++++++++++++++++++++++-------------- keychain/src/keychain.rs | 7 +++--- wallet/src/info.rs | 2 +- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/keychain/src/extkey.rs b/keychain/src/extkey.rs index 5f1a0f63..c91be750 100644 --- a/keychain/src/extkey.rs +++ b/keychain/src/extkey.rs @@ -19,8 +19,9 @@ use serde::{de, ser}; use byteorder::{ByteOrder, BigEndian}; use blake2::blake2b::blake2b; +use secp; use secp::Secp256k1; -use secp::key::SecretKey; +use secp::key::{PublicKey, SecretKey}; use util; /// An ExtKey error @@ -30,6 +31,13 @@ pub enum Error { InvalidSeedSize, InvalidSliceSize, InvalidExtendedKey, + Secp(secp::Error), +} + +impl From for Error { + fn from(e: secp::Error) -> Error { + Error::Secp(e) + } } // Passthrough Debug to Display, since errors should be user-visible @@ -46,10 +54,11 @@ impl error::Error for Error { fn description(&self) -> &str { match *self { - Error::InvalidSeedSize => "wallet: seed isn't of size 128, 256 or 512", + Error::InvalidSeedSize => "keychain: 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", + Error::InvalidSliceSize => "keychain: serialized extended key must be of size 73", + Error::InvalidExtendedKey => "keychain: the given serialized extended key is invalid", + Error::Secp(_) => "keychain: secp error", } } } @@ -225,16 +234,20 @@ impl ExtendedKey { key: secret_key, }; - ext_key.fingerprint = ext_key.identifier().fingerprint(); + let identifier = ext_key.identifier(secp)?; + ext_key.fingerprint = identifier.fingerprint(); Ok(ext_key) } /// Return the identifier of the key - /// which is the blake2b hash (20 bit digest) - pub fn identifier(&self) -> Identifier { - let identifier = blake2b(20, &[], &self.key[..]); - Identifier::from_bytes(&identifier.as_bytes()) + /// which is the blake2b hash (20 byte digest) of the PublicKey + // corresponding to the underlying SecretKey + pub fn identifier(&self, secp: &Secp256k1) -> Result { + let pubkey = PublicKey::from_secret_key(secp, &self.key)?; + let bytes = pubkey.serialize_vec(secp, true); + let identifier = blake2b(20, &[], &bytes[..]); + Ok(Identifier::from_bytes(&identifier.as_bytes())) } /// Derive an extended key from an extended key @@ -256,9 +269,11 @@ impl ExtendedKey { let mut chain_code: [u8; 32] = [0; 32]; (&mut chain_code).clone_from_slice(&derived.as_bytes()[32..]); + let identifier = self.identifier(&secp)?; + Ok(ExtendedKey { depth: self.depth + 1, - fingerprint: self.identifier().fingerprint(), + fingerprint: identifier.fingerprint(), n_child: n, chaincode: chain_code, key: secret_key, @@ -315,13 +330,13 @@ mod test { let chaincode = from_hex( "e7298e68452b0c6d54837670896e1aee76b118075150d90d4ee416ece106ae72", ); - let identifier = from_hex("942b6c0bd43bdcb24f3edfe7fadbc77054ecc4f2"); - let fingerprint = from_hex("942b6c0b"); + let identifier = from_hex("d291fc2dca90fc8b005a01638d616fda770ec552"); + let fingerprint = from_hex("d291fc2d"); let depth = 0; let n_child = 0; assert_eq!(extk.key, secret_key); assert_eq!( - extk.identifier(), + extk.identifier(&s).unwrap(), Identifier::from_bytes(identifier.as_slice()) ); assert_eq!( @@ -329,7 +344,7 @@ mod test { Fingerprint::from_bytes(fingerprint.as_slice()) ); assert_eq!( - extk.identifier().fingerprint(), + extk.identifier(&s).unwrap().fingerprint(), Fingerprint::from_bytes(fingerprint.as_slice()) ); assert_eq!(extk.chaincode, chaincode.as_slice()); @@ -351,14 +366,14 @@ mod test { let chaincode = from_hex( "243cb881e1549e714db31d23af45540b13ad07941f64a786bbf3313b4de1df52", ); - let fingerprint = from_hex("942b6c0b"); - let identifier = from_hex("8b011f14345f3f0071e85f6eec116de1e575ea10"); - let identifier_fingerprint = from_hex("8b011f14"); + let fingerprint = from_hex("d291fc2d"); + let identifier = from_hex("027a8e290736af382fc943bdabb774bc2d14fd95"); + let identifier_fingerprint = from_hex("027a8e29"); let depth = 1; let n_child = 0; assert_eq!(derived.key, secret_key); assert_eq!( - derived.identifier(), + derived.identifier(&s).unwrap(), Identifier::from_bytes(identifier.as_slice()) ); assert_eq!( @@ -366,7 +381,7 @@ mod test { Fingerprint::from_bytes(fingerprint.as_slice()) ); assert_eq!( - derived.identifier().fingerprint(), + derived.identifier(&s).unwrap().fingerprint(), Fingerprint::from_bytes(identifier_fingerprint.as_slice()) ); assert_eq!(derived.chaincode, chaincode.as_slice()); diff --git a/keychain/src/keychain.rs b/keychain/src/keychain.rs index e9ae347c..04280a52 100644 --- a/keychain/src/keychain.rs +++ b/keychain/src/keychain.rs @@ -74,7 +74,8 @@ impl Keychain { pub fn derive_pubkey(&self, derivation: u32) -> Result { let extkey = self.extkey.derive(&self.secp, derivation)?; - Ok(extkey.identifier()) + let pubkey = extkey.identifier(&self.secp)?; + Ok(pubkey) } // TODO - this is a work in progress @@ -83,7 +84,7 @@ impl Keychain { fn derived_key(&self, pubkey: &Identifier) -> Result { for i in 1..10000 { let extkey = self.extkey.derive(&self.secp, i)?; - if extkey.identifier() == *pubkey { + if extkey.identifier(&self.secp)? == *pubkey { return Ok(extkey.key); } } @@ -95,7 +96,7 @@ impl Keychain { pub fn derivation_from_pubkey(&self, pubkey: &Identifier) -> Result { for i in 1..10000 { let extkey = self.extkey.derive(&self.secp, i)?; - if extkey.identifier() == *pubkey { + if extkey.identifier(&self.secp)? == *pubkey { return Ok(extkey.n_child); } } diff --git a/wallet/src/info.rs b/wallet/src/info.rs index 4f75caa5..875bb1cb 100644 --- a/wallet/src/info.rs +++ b/wallet/src/info.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Grin Developers +// Copyright 2017 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.