Add raw hash signing (#5423)

* add sign any

* Add RPC signMessage call to JS API

* Add signMessage to JSON RPC docs

* PostSignTransaction -> EthSignMessage

* fix doc typo

* revert incorect naming
This commit is contained in:
keorn 2017-04-12 11:15:13 +01:00 committed by Gav Wood
parent daf1495c4e
commit 52eae66c72
11 changed files with 81 additions and 18 deletions

View File

@ -522,6 +522,11 @@ export default class Parity {
.then(outNumber);
}
signMessage (address, password, messageHash) {
return this._transport
.execute('parity_signMessage', inAddress(address), password, inHex(messageHash));
}
testPassword (account, password) {
return this._transport
.execute('parity_testPassword', inAddress(account), password);

View File

@ -1881,5 +1881,31 @@ export default {
desc: 'Decrypted message.',
example: withComment('0x68656c6c6f20776f726c64', 'hello world')
}
},
signMessage: {
desc: 'Sign the hashed message bytes with the given account.',
params: [
{
type: Address,
desc: 'Account which signs the message.',
example: '0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e'
},
{
type: String,
desc: 'Passphrase to unlock the account.',
example: 'password1'
},
{
type: Data,
desc: 'Hashed message.',
example: '0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a'
}
],
returns: {
type: Data,
desc: 'Message signature.',
example: '0x1d9e33a8cf8bfc089a172bca01da462f9e359c6cb1b0f29398bc884e4d18df4f78588aee4fb5cc067ca62d2abab995e0bba29527be6ac98105b0320020a2efaf00'
}
}
};

View File

@ -474,7 +474,7 @@ pub fn execute<D: Dispatcher + 'static>(
.map(ConfirmationResponse::SignTransaction)
).boxed()
},
ConfirmationPayload::Signature(address, mut data) => {
ConfirmationPayload::EthSignMessage(address, mut data) => {
let mut message_data =
format!("\x19Ethereum Signed Message:\n{}", data.len())
.into_bytes();
@ -574,8 +574,8 @@ pub fn from_rpc<D>(payload: RpcConfirmationPayload, default_account: Address, di
RpcConfirmationPayload::Decrypt(RpcDecryptRequest { address, msg }) => {
future::ok(ConfirmationPayload::Decrypt(address.into(), msg.into())).boxed()
},
RpcConfirmationPayload::Signature(RpcSignRequest { address, data }) => {
future::ok(ConfirmationPayload::Signature(address.into(), data.into())).boxed()
RpcConfirmationPayload::EthSignMessage(RpcSignRequest { address, data }) => {
future::ok(ConfirmationPayload::EthSignMessage(address.into(), data.into())).boxed()
},
}
}

View File

@ -113,8 +113,8 @@ pub enum ConfirmationPayload {
SendTransaction(FilledTransactionRequest),
/// Sign Transaction
SignTransaction(FilledTransactionRequest),
/// Sign request
Signature(Address, Bytes),
/// Sign a message with an Ethereum specific security prefix.
EthSignMessage(Address, Bytes),
/// Decrypt request
Decrypt(Address, Bytes),
}
@ -124,7 +124,7 @@ impl ConfirmationPayload {
match *self {
ConfirmationPayload::SendTransaction(ref request) => request.from,
ConfirmationPayload::SignTransaction(ref request) => request.from,
ConfirmationPayload::Signature(ref address, _) => *address,
ConfirmationPayload::EthSignMessage(ref address, _) => *address,
ConfirmationPayload::Decrypt(ref address, _) => *address,
}
}

View File

@ -17,7 +17,7 @@
//! Account management (personal) rpc implementation
use std::sync::{Arc, Weak};
use std::collections::BTreeMap;
use util::{Address};
use util::Address;
use ethkey::{Brain, Generator, Secret};
use ethstore::KeyFile;
@ -27,7 +27,7 @@ use jsonrpc_core::Error;
use v1::helpers::errors;
use v1::helpers::accounts::unwrap_provider;
use v1::traits::ParityAccounts;
use v1::types::{H160 as RpcH160, H256 as RpcH256, DappId, Derive, DeriveHierarchical, DeriveHash};
use v1::types::{H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, DappId, Derive, DeriveHierarchical, DeriveHash};
/// Account management (personal) rpc implementation.
pub struct ParityAccountsClient {
@ -334,6 +334,17 @@ impl ParityAccounts for ParityAccountsClient {
.map(Into::into)
.map_err(|e| errors::account("Could not export account.", e))
}
fn sign_message(&self, addr: RpcH160, password: String, message: RpcH256) -> Result<RpcH520, Error> {
self.account_provider()?
.sign(
addr.into(),
Some(password),
message.into()
)
.map(Into::into)
.map_err(|e| errors::account("Could not sign message.", e))
}
}
fn into_vec<A, B>(a: Vec<A>) -> Vec<B> where

View File

@ -140,7 +140,7 @@ impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> {
fn post_sign(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> {
let pending = self.pending.clone();
self.dispatch(
RpcConfirmationPayload::Signature((address.clone(), data).into()),
RpcConfirmationPayload::EthSignMessage((address.clone(), data).into()),
DefaultAccount::Provided(address.into()),
meta.origin
).map(move |result| match result {
@ -216,7 +216,7 @@ impl<D: Dispatcher + 'static> EthSigning for SigningQueueClient<D> {
fn sign(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> {
let res = self.dispatch(
RpcConfirmationPayload::Signature((address.clone(), data).into()),
RpcConfirmationPayload::EthSignMessage((address.clone(), data).into()),
address.into(),
meta.origin,
);

View File

@ -78,7 +78,7 @@ impl<D: Dispatcher + 'static> EthSigning for SigningUnsafeClient<D>
type Metadata = Metadata;
fn sign(&self, _: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> {
self.handle(RpcConfirmationPayload::Signature((address.clone(), data).into()), address.into())
self.handle(RpcConfirmationPayload::EthSignMessage((address.clone(), data).into()), address.into())
.then(|res| match res {
Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature),
Err(e) => Err(e),

View File

@ -500,3 +500,20 @@ fn should_export_account() {
println!("Response: {:?}", response);
assert_eq!(result, Some(response.into()));
}
#[test]
fn should_sign_message() {
let tester = setup();
let hash = tester.accounts
.insert_account(
"0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a".parse().unwrap(),
"password1")
.expect("account should be inserted ok");
assert_eq!(hash, "c171033d5cbff7175f29dfd3a63dda3d6f8f385e".parse().unwrap());
let request = r#"{"jsonrpc": "2.0", "method": "parity_signMessage", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", "0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a"], "id": 3}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x1d9e33a8cf8bfc089a172bca01da462f9e359c6cb1b0f29398bc884e4d18df4f78588aee4fb5cc067ca62d2abab995e0bba29527be6ac98105b0320020a2efaf00","id":3}"#;
let res = tester.io.handle_request_sync(&request);
assert_eq!(res, Some(response.into()));
}

View File

@ -90,7 +90,7 @@ fn should_return_list_of_items_to_confirm() {
nonce: None,
condition: None,
}), Origin::Dapps("http://parity.io".into())).unwrap();
tester.signer.add_request(ConfirmationPayload::Signature(1.into(), vec![5].into()), Origin::Unknown).unwrap();
tester.signer.add_request(ConfirmationPayload::EthSignMessage(1.into(), vec![5].into()), Origin::Unknown).unwrap();
// when
let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#;
@ -163,7 +163,7 @@ fn should_not_remove_transaction_if_password_is_invalid() {
fn should_not_remove_sign_if_password_is_invalid() {
// given
let tester = signer_tester();
tester.signer.add_request(ConfirmationPayload::Signature(0.into(), vec![5].into()), Origin::Unknown).unwrap();
tester.signer.add_request(ConfirmationPayload::EthSignMessage(0.into(), vec![5].into()), Origin::Unknown).unwrap();
assert_eq!(tester.signer.requests().len(), 1);
// when

View File

@ -19,7 +19,7 @@ use std::collections::BTreeMap;
use jsonrpc_core::Error;
use ethstore::KeyFile;
use v1::types::{H160, H256, DappId, DeriveHash, DeriveHierarchical};
use v1::types::{H160, H256, H520, DappId, DeriveHash, DeriveHierarchical};
build_rpc_trait! {
/// Personal Parity rpc interface.
@ -180,5 +180,9 @@ build_rpc_trait! {
/// Exports an account with given address if provided password matches.
#[rpc(name = "parity_exportAccount")]
fn export_account(&self, H160, String) -> Result<KeyFile, Error>;
/// Sign raw hash with the key corresponding to address and password.
#[rpc(name = "parity_signMessage")]
fn sign_message(&self, H160, String, H256) -> Result<H520, Error>;
}
}

View File

@ -57,7 +57,7 @@ impl fmt::Display for ConfirmationPayload {
match *self {
ConfirmationPayload::SendTransaction(ref transaction) => write!(f, "{}", transaction),
ConfirmationPayload::SignTransaction(ref transaction) => write!(f, "(Sign only) {}", transaction),
ConfirmationPayload::Signature(ref sign) => write!(f, "{}", sign),
ConfirmationPayload::EthSignMessage(ref sign) => write!(f, "{}", sign),
ConfirmationPayload::Decrypt(ref decrypt) => write!(f, "{}", decrypt),
}
}
@ -169,7 +169,7 @@ pub enum ConfirmationPayload {
SignTransaction(TransactionRequest),
/// Signature
#[serde(rename="sign")]
Signature(SignRequest),
EthSignMessage(SignRequest),
/// Decryption
#[serde(rename="decrypt")]
Decrypt(DecryptRequest),
@ -180,7 +180,7 @@ impl From<helpers::ConfirmationPayload> for ConfirmationPayload {
match c {
helpers::ConfirmationPayload::SendTransaction(t) => ConfirmationPayload::SendTransaction(t.into()),
helpers::ConfirmationPayload::SignTransaction(t) => ConfirmationPayload::SignTransaction(t.into()),
helpers::ConfirmationPayload::Signature(address, data) => ConfirmationPayload::Signature(SignRequest {
helpers::ConfirmationPayload::EthSignMessage(address, data) => ConfirmationPayload::EthSignMessage(SignRequest {
address: address.into(),
data: data.into(),
}),
@ -255,7 +255,7 @@ mod tests {
// given
let request = helpers::ConfirmationRequest {
id: 15.into(),
payload: helpers::ConfirmationPayload::Signature(1.into(), vec![5].into()),
payload: helpers::ConfirmationPayload::EthSignMessage(1.into(), vec![5].into()),
origin: Origin::Rpc("test service".into()),
};