Skip to content

Commit

Permalink
Add CryptoStore::ecdsa_sign_prehashed() (paritytech#8838)
Browse files Browse the repository at this point in the history
* Pair::sign_prehashed()

* add CryptoStore::ecdsa_sign_prehashed()

* add test for testing keystore

* address review comments
  • Loading branch information
adoerr authored and jordy25519 committed Sep 19, 2021
1 parent 88d8d9f commit 4c8f8da
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 2 deletions.
21 changes: 21 additions & 0 deletions client/keystore/src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@ impl CryptoStore for LocalKeystore {
) -> std::result::Result<Option<VRFSignature>, TraitError> {
SyncCryptoStore::sr25519_vrf_sign(self, key_type, public, transcript_data)
}

async fn ecdsa_sign_prehashed(
&self,
id: KeyTypeId,
public: &ecdsa::Public,
msg: &[u8; 32],
) -> std::result::Result<Option<ecdsa::Signature>, TraitError> {
SyncCryptoStore::ecdsa_sign_prehashed(self, id, public, msg)
}
}

impl SyncCryptoStore for LocalKeystore {
Expand Down Expand Up @@ -301,6 +310,18 @@ impl SyncCryptoStore for LocalKeystore {
Ok(None)
}
}

fn ecdsa_sign_prehashed(
&self,
id: KeyTypeId,
public: &ecdsa::Public,
msg: &[u8; 32],
) -> std::result::Result<Option<ecdsa::Signature>, TraitError> {
let pair = self.0.read()
.key_pair_by_type::<ecdsa::Pair>(public, id)?;

pair.map(|k| k.sign_prehashed(msg)).map(Ok).transpose()
}
}

impl Into<SyncCryptoStorePtr> for LocalKeystore {
Expand Down
32 changes: 31 additions & 1 deletion primitives/core/src/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,12 @@ impl Pair {
Self::from_seed(&padded_seed)
})
}

/// Sign a pre-hashed message
pub fn sign_prehashed(&self, message: &[u8; 32]) -> Signature {
let message = secp256k1::Message::parse(message);
secp256k1::sign(&message, &self.secret).into()
}
}

impl CryptoType for Public {
Expand All @@ -552,7 +558,7 @@ impl CryptoType for Pair {
mod test {
use super::*;
use hex_literal::hex;
use crate::crypto::{DEV_PHRASE, set_default_ss58_version};
use crate::{crypto::{DEV_PHRASE, set_default_ss58_version}, keccak_256};
use serde_json;
use crate::crypto::PublicError;

Expand Down Expand Up @@ -761,4 +767,28 @@ mod test {
// Poorly-sized
assert!(deserialize_signature("\"abc123\"").is_err());
}

#[test]
fn sign_prehashed_works() {
let (pair, _, _) = Pair::generate_with_phrase(Some("password"));

// `msg` shouldn't be mangled
let msg = [0u8; 32];
let sig1 = pair.sign_prehashed(&msg);
let sig2: Signature = secp256k1::sign(&secp256k1::Message::parse(&msg), &pair.secret).into();

assert_eq!(sig1, sig2);

// signature is actually different
let sig2 = pair.sign(&msg);

assert_ne!(sig1, sig2);

// using pre-hashed `msg` works
let msg = keccak_256(b"this should be hashed");
let sig1 = pair.sign_prehashed(&msg);
let sig2: Signature = secp256k1::sign(&secp256k1::Message::parse(&msg), &pair.secret).into();

assert_eq!(sig1, sig2);
}
}
28 changes: 28 additions & 0 deletions primitives/keystore/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,20 @@ pub trait CryptoStore: Send + Sync {
public: &sr25519::Public,
transcript_data: VRFTranscriptData,
) -> Result<Option<VRFSignature>, Error>;

/// Sign pre-hashed
///
/// Signs a pre-hashed message with the private key that matches
/// the ECDSA public key passed.
///
/// Returns the SCALE encoded signature if key is found and supported,
/// `None` if the key doesn't exist or an error when something failed.
async fn ecdsa_sign_prehashed(
&self,
id: KeyTypeId,
public: &ecdsa::Public,
msg: &[u8; 32],
) -> Result<Option<ecdsa::Signature>, Error>;
}

/// Sync version of the CryptoStore
Expand Down Expand Up @@ -353,6 +367,20 @@ pub trait SyncCryptoStore: CryptoStore + Send + Sync {
public: &sr25519::Public,
transcript_data: VRFTranscriptData,
) -> Result<Option<VRFSignature>, Error>;

/// Sign pre-hashed
///
/// Signs a pre-hashed message with the private key that matches
/// the ECDSA public key passed.
///
/// Returns the SCALE encoded signature if key is found and supported,
/// `None` if the key doesn't exist or an error when something failed.
fn ecdsa_sign_prehashed(
&self,
id: KeyTypeId,
public: &ecdsa::Public,
msg: &[u8; 32],
) -> Result<Option<ecdsa::Signature>, Error>;
}

/// A pointer to a keystore.
Expand Down
43 changes: 42 additions & 1 deletion primitives/keystore/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use sp_core::{
crypto::{Pair, Public, CryptoTypePublicPair},
ed25519, sr25519, ecdsa,
};

use crate::{
{CryptoStore, SyncCryptoStorePtr, Error, SyncCryptoStore},
vrf::{VRFTranscriptData, VRFSignature, make_transcript},
Expand Down Expand Up @@ -144,6 +145,15 @@ impl CryptoStore for KeyStore {
) -> Result<Option<VRFSignature>, Error> {
SyncCryptoStore::sr25519_vrf_sign(self, key_type, public, transcript_data)
}

async fn ecdsa_sign_prehashed(
&self,
id: KeyTypeId,
public: &ecdsa::Public,
msg: &[u8; 32],
) -> Result<Option<ecdsa::Signature>, Error> {
SyncCryptoStore::ecdsa_sign_prehashed(self, id, public, msg)
}
}

impl SyncCryptoStore for KeyStore {
Expand Down Expand Up @@ -325,6 +335,16 @@ impl SyncCryptoStore for KeyStore {
proof,
}))
}

fn ecdsa_sign_prehashed(
&self,
id: KeyTypeId,
public: &ecdsa::Public,
msg: &[u8; 32],
) -> Result<Option<ecdsa::Signature>, Error> {
let pair = self.ecdsa_key_pair(id, public);
pair.map(|k| k.sign_prehashed(msg)).map(Ok).transpose()
}
}

impl Into<SyncCryptoStorePtr> for KeyStore {
Expand All @@ -342,7 +362,7 @@ impl Into<Arc<dyn CryptoStore>> for KeyStore {
#[cfg(test)]
mod tests {
use super::*;
use sp_core::{sr25519, testing::{ED25519, SR25519}};
use sp_core::{sr25519, testing::{ED25519, SR25519, ECDSA}};
use crate::{SyncCryptoStore, vrf::VRFTranscriptValue};

#[test]
Expand Down Expand Up @@ -416,4 +436,25 @@ mod tests {

assert!(result.unwrap().is_some());
}

#[test]
fn ecdsa_sign_prehashed_works() {
let store = KeyStore::new();

let suri = "//Alice";
let pair = ecdsa::Pair::from_string(suri, None).unwrap();

let msg = sp_core::keccak_256(b"this should be a hashed message");

// no key in key store
let res = SyncCryptoStore::ecdsa_sign_prehashed(&store, ECDSA, &pair.public(), &msg).unwrap();
assert!(res.is_none());

// insert key, sign again
let res = SyncCryptoStore::insert_unknown(&store, ECDSA, suri, pair.public().as_ref()).unwrap();
assert_eq!((), res);

let res = SyncCryptoStore::ecdsa_sign_prehashed(&store, ECDSA, &pair.public(), &msg).unwrap();
assert!(res.is_some());
}
}

0 comments on commit 4c8f8da

Please sign in to comment.