diff --git a/ethkey/README.md b/ethkey/README.md index 1a588cd48..726047481 100644 --- a/ethkey/README.md +++ b/ethkey/README.md @@ -21,7 +21,8 @@ Usage: ethkey generate prefix [options] ethkey generate brain [options] ethkey sign - ethkey verify + ethkey verify public + ethkey verify address
ethkey [-h | --help] Options: @@ -126,15 +127,32 @@ c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8 -- -#### `verify ` +#### `verify public ` *Verify the signature.* -- `` - ethereum public, 64 bytes long +- `` - ethereum public, 64 bytes long - `` - message signature, 65 bytes long - `` - message, 32 bytes long ``` -ethkey verify 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124 c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200 bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987 +ethkey verify public 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124 c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200 bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987 +``` + +``` +true +``` + +-- + +#### `verify address
` +*Verify the signature.* + +- `
` - ethereum address, 20 bytes long +- `` - message signature, 65 bytes long +- `` - message, 32 bytes long + +``` +ethkey verify address 689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124 c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200 bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987 ``` ``` diff --git a/ethkey/src/bin/ethkey.rs b/ethkey/src/bin/ethkey.rs index a7661c42f..9707ba4b2 100644 --- a/ethkey/src/bin/ethkey.rs +++ b/ethkey/src/bin/ethkey.rs @@ -7,7 +7,7 @@ use std::{env, fmt, process}; use std::num::ParseIntError; use docopt::Docopt; use rustc_serialize::hex::{FromHex, FromHexError}; -use ethkey::{KeyPair, Random, Brain, Prefix, Error as EthkeyError, Generator, Secret, Message, Public, Signature, sign, verify}; +use ethkey::{KeyPair, Random, Brain, Prefix, Error as EthkeyError, Generator, Secret, Message, Public, Signature, Address, sign, verify_public, verify_address}; pub const USAGE: &'static str = r#" Ethereum keys generator. @@ -19,7 +19,8 @@ Usage: ethkey generate prefix [options] ethkey generate brain [options] ethkey sign - ethkey verify + ethkey verify public + ethkey verify address
ethkey [-h | --help] Options: @@ -47,12 +48,15 @@ struct Args { cmd_brain: bool, cmd_sign: bool, cmd_verify: bool, + cmd_public: bool, + cmd_address: bool, arg_prefix: String, arg_iterations: String, arg_seed: String, arg_secret: String, arg_message: String, arg_public: String, + arg_address: String, arg_signature: String, flag_secret: bool, flag_public: bool, @@ -164,10 +168,17 @@ fn execute(command: I) -> Result where I: IntoIterator>(); + + let expected = "true".to_owned(); + assert_eq!(execute(command).unwrap(), expected); + } + + #[test] + fn verify_valid_address() { + let command = vec!["ethkey", "verify", "address", "26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec987"] .into_iter() .map(Into::into) .collect::>(); @@ -263,7 +285,7 @@ address: 26d1ec50b4e62c1d1a40d16e7cacc6a6580757d5".to_owned(); #[test] fn verify_invalid() { - let command = vec!["ethkey", "verify", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec986"] + let command = vec!["ethkey", "verify", "public", "689268c0ff57a20cd299fa60d3fb374862aff565b20b5f1767906a99e6e09f3ff04ca2b2a5cd22f62941db103c0356df1a8ed20ce322cab2483db67685afd124", "c1878cf60417151c766a712653d26ef350c8c75393458b7a9be715f053215af63dfd3b02c2ae65a8677917a8efa3172acb71cb90196e42106953ea0363c5aaf200", "bd50b7370c3f96733b31744c6c45079e7ae6c8d299613246d28ebcef507ec986"] .into_iter() .map(Into::into) .collect::>(); diff --git a/ethkey/src/keypair.rs b/ethkey/src/keypair.rs index dcfb67460..9d33dcdb3 100644 --- a/ethkey/src/keypair.rs +++ b/ethkey/src/keypair.rs @@ -4,6 +4,13 @@ use rustc_serialize::hex::ToHex; use keccak::Keccak256; use super::{Secret, Public, Address, SECP256K1, Error}; +pub fn public_to_address(public: &Public) -> Address { + let hash = public.keccak256(); + let mut result = Address::default(); + result.copy_from_slice(&hash[12..]); + result +} + /// secp256k1 key pair pub struct KeyPair { secret: Secret, @@ -60,10 +67,7 @@ impl KeyPair { } pub fn address(&self) -> Address { - let hash = self.public.keccak256(); - let mut result = Address::default(); - result.copy_from_slice(&hash[12..]); - result + public_to_address(&self.public) } } diff --git a/ethkey/src/lib.rs b/ethkey/src/lib.rs index 218abbbf3..b03ca544e 100644 --- a/ethkey/src/lib.rs +++ b/ethkey/src/lib.rs @@ -26,8 +26,8 @@ pub trait Generator { pub use self::brain::Brain; pub use self::error::Error; -pub use self::keypair::KeyPair; +pub use self::keypair::{KeyPair, public_to_address}; pub use self::primitive::{Secret, Public, Address, Message}; pub use self::prefix::Prefix; pub use self::random::Random; -pub use self::signature::{sign, verify, Signature}; +pub use self::signature::{sign, verify_public, verify_address, recover, Signature}; diff --git a/ethkey/src/signature.rs b/ethkey/src/signature.rs index f34baa04f..9c3ad2e27 100644 --- a/ethkey/src/signature.rs +++ b/ethkey/src/signature.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use secp256k1::{Message as SecpMessage, RecoverableSignature, RecoveryId, Error as SecpError}; use secp256k1::key::{SecretKey, PublicKey}; use rustc_serialize::hex::{ToHex, FromHex}; -use {Secret, Public, SECP256K1, Error, Message}; +use {Secret, Public, SECP256K1, Error, Message, public_to_address, Address}; #[repr(C)] #[derive(Eq)] @@ -113,7 +113,7 @@ pub fn sign(secret: &Secret, message: &Message) -> Result { Ok(Signature(data_arr)) } -pub fn verify(public: &Public, signature: &Signature, message: &Message) -> Result { +pub fn verify_public(public: &Public, signature: &Signature, message: &Message) -> Result { let context = &SECP256K1; let rsig = try!(RecoverableSignature::from_compact(context, &signature[0..64], try!(RecoveryId::from_i32(signature[64] as i32)))); let sig = rsig.to_standard(context); @@ -132,11 +132,28 @@ pub fn verify(public: &Public, signature: &Signature, message: &Message) -> Resu } } +pub fn verify_address(address: &Address, signature: &Signature, message: &Message) -> Result { + let public = try!(recover(signature, message)); + let recovered_address = public_to_address(&public); + Ok(address == &recovered_address) +} + +pub fn recover(signature: &Signature, message: &Message) -> Result { + let context = &SECP256K1; + let rsig = try!(RecoverableSignature::from_compact(context, &signature[0..64], try!(RecoveryId::from_i32(signature[64] as i32)))); + let pubkey = try!(context.recover(&try!(SecpMessage::from_slice(&message[..])), &rsig)); + let serialized = pubkey.serialize_vec(context, false); + + let mut public = Public::default(); + public.copy_from_slice(&serialized[1..65]); + Ok(public) +} + #[cfg(test)] mod tests { use std::str::FromStr; use {Generator, Random, Message}; - use super::{sign, verify, Signature}; + use super::{sign, verify_public, verify_address, recover, Signature}; #[test] fn signature_to_and_from_str() { @@ -149,10 +166,26 @@ mod tests { } #[test] - fn sign_and_verify() { + fn sign_and_recover_public() { let keypair = Random.generate().unwrap(); let message = Message::default(); let signature = sign(keypair.secret(), &message).unwrap(); - assert!(verify(keypair.public(), &signature, &message).unwrap()); + assert_eq!(keypair.public(), &recover(&signature, &message).unwrap()); + } + + #[test] + fn sign_and_verify_public() { + let keypair = Random.generate().unwrap(); + let message = Message::default(); + let signature = sign(keypair.secret(), &message).unwrap(); + assert!(verify_public(keypair.public(), &signature, &message).unwrap()); + } + + #[test] + fn sign_and_verify_address() { + let keypair = Random.generate().unwrap(); + let message = Message::default(); + let signature = sign(keypair.secret(), &message).unwrap(); + assert!(verify_address(&keypair.address(), &signature, &message).unwrap()); } } diff --git a/ethstore/src/account/safe_account.rs b/ethstore/src/account/safe_account.rs index 934bfd03d..4b52d0397 100644 --- a/ethstore/src/account/safe_account.rs +++ b/ethstore/src/account/safe_account.rs @@ -154,7 +154,7 @@ impl SafeAccount { #[cfg(test)] mod tests { - use ethkey::{Generator, Random, verify, Message}; + use ethkey::{Generator, Random, verify_public, Message}; use super::{Crypto, SafeAccount}; #[test] @@ -174,13 +174,13 @@ mod tests { } #[test] - fn sign_and_verify() { + fn sign_and_verify_public() { let keypair = Random.generate().unwrap(); let password = "hello world"; let message = Message::default(); let account = SafeAccount::create(&keypair, [0u8; 16], password, 10240); let signature = account.sign(password, &message).unwrap(); - assert!(verify(keypair.public(), &signature, &message).unwrap()); + assert!(verify_public(keypair.public(), &signature, &message).unwrap()); } #[test] diff --git a/ethstore/src/ethkey.rs b/ethstore/src/ethkey.rs index d77fa7a52..2d0609106 100644 --- a/ethstore/src/ethkey.rs +++ b/ethstore/src/ethkey.rs @@ -1,5 +1,5 @@ //! ethkey reexport to make documentation look pretty. -pub use _ethkey::{Address, Message, Signature, Public, Secret, Generator, sign, verify, Error, KeyPair, Random, Prefix}; +pub use _ethkey::*; use json; impl Into for Address {