mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
Replace libsecp256k1 with secp256k1 (#10798)
* Replace libsecp256k1 with secp256k1 * Wipe ecdsa secret key from memory on drop * Some comments for a known issue * Safer core crypto primitives `from_slice` constructor Previous version panics if slice lenght is not the expected one. * Unit test fix * Enable use of global secp256k1 context * Better comments for ecdsa `Pair` drop * Replace `libsecp256k1` with `seco256k1` in `beefy-mmr` Used to convert ecdsa public key to ETH address * Replace `libsecp256k1` with `secp256k1` in FRAME `contracts`benchmarks * Temporary rollback of `beefy-mmr` to libsecp256k1 Check for detected build issues * Cargo fmt * Rollback of FRAME `contracts` benchmarks to `libsecp256k1` * Rollback for unrelated changes * Typo fix * Add comments for deprecated `ecdsa_verify` and `secp256k1_ecdsa_recover`
This commit is contained in:
Generated
+21
-1
@@ -8382,7 +8382,6 @@ version = "0.10.0-dev"
|
||||
dependencies = [
|
||||
"hex-literal",
|
||||
"lazy_static",
|
||||
"libsecp256k1",
|
||||
"lru 0.6.6",
|
||||
"parity-scale-codec",
|
||||
"parking_lot 0.12.0",
|
||||
@@ -9170,6 +9169,25 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secp256k1"
|
||||
version = "0.21.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab7883017d5b21f011ef8040ea9c6c7ac90834c0df26a69e4c0b06276151f125"
|
||||
dependencies = [
|
||||
"rand 0.6.5",
|
||||
"secp256k1-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secp256k1-sys"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secrecy"
|
||||
version = "0.8.0"
|
||||
@@ -9817,6 +9835,7 @@ dependencies = [
|
||||
"regex",
|
||||
"scale-info",
|
||||
"schnorrkel",
|
||||
"secp256k1",
|
||||
"secrecy",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -9927,6 +9946,7 @@ dependencies = [
|
||||
"log 0.4.14",
|
||||
"parity-scale-codec",
|
||||
"parking_lot 0.12.0",
|
||||
"secp256k1",
|
||||
"sp-core",
|
||||
"sp-externalities",
|
||||
"sp-keystore",
|
||||
|
||||
@@ -265,6 +265,7 @@ percent-encoding = { opt-level = 3 }
|
||||
primitive-types = { opt-level = 3 }
|
||||
ring = { opt-level = 3 }
|
||||
rustls = { opt-level = 3 }
|
||||
secp256k1 = { opt-level = 3 }
|
||||
sha2 = { opt-level = 3 }
|
||||
sha3 = { opt-level = 3 }
|
||||
smallvec = { opt-level = 3 }
|
||||
|
||||
@@ -105,7 +105,7 @@ fn cryptos_are_compatible() {
|
||||
let sp_core_signature = sp_core_secret.sign(message); // no error expected...
|
||||
|
||||
assert!(sp_core::ed25519::Pair::verify(
|
||||
&sp_core::ed25519::Signature::from_slice(&libp2p_signature),
|
||||
&sp_core::ed25519::Signature::from_slice(&libp2p_signature).unwrap(),
|
||||
message,
|
||||
&sp_core_public
|
||||
));
|
||||
|
||||
@@ -31,7 +31,6 @@ sc-executor-common = { version = "0.10.0-dev", path = "common" }
|
||||
sc-executor-wasmi = { version = "0.10.0-dev", path = "wasmi" }
|
||||
sc-executor-wasmtime = { version = "0.10.0-dev", path = "wasmtime", optional = true }
|
||||
parking_lot = "0.12.0"
|
||||
libsecp256k1 = "0.7"
|
||||
sp-core-hashing-proc-macro = { version = "4.0.0-dev", path = "../../primitives/core/hashing/proc-macro" }
|
||||
lru = "0.6.6"
|
||||
tracing = "0.1.29"
|
||||
|
||||
@@ -54,8 +54,9 @@ schnorrkel = { version = "0.9.1", features = [
|
||||
"u64_backend",
|
||||
], default-features = false, optional = true }
|
||||
hex = { version = "0.4", default-features = false, optional = true }
|
||||
libsecp256k1 = { version = "0.7", default-features = false, features = ["hmac", "static-context"], optional = true }
|
||||
libsecp256k1 = { version = "0.7", default-features = false, features = ["static-context"], optional = true }
|
||||
merlin = { version = "2.0", default-features = false, optional = true }
|
||||
secp256k1 = { version = "0.21.2", default-features = false, features = ["recovery", "global-context"], optional = true }
|
||||
ss58-registry = { version = "1.11.0", default-features = false }
|
||||
sp-core-hashing = { version = "4.0.0", path = "./hashing", default-features = false, optional = true }
|
||||
sp-runtime-interface = { version = "5.0.0", default-features = false, path = "../runtime-interface" }
|
||||
@@ -106,6 +107,7 @@ std = [
|
||||
"schnorrkel/std",
|
||||
"regex",
|
||||
"num-traits/std",
|
||||
"secp256k1/std",
|
||||
"sp-core-hashing/std",
|
||||
"sp-debug-derive/std",
|
||||
"sp-externalities",
|
||||
@@ -129,6 +131,7 @@ full_crypto = [
|
||||
"schnorrkel",
|
||||
"hex",
|
||||
"libsecp256k1",
|
||||
"secp256k1",
|
||||
"sp-core-hashing",
|
||||
"sp-runtime-interface/disable_target_static_assertions",
|
||||
"merlin",
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// tag::description[]
|
||||
//! Simple ECDSA API.
|
||||
// end::description[]
|
||||
//! Simple ECDSA secp256k1 API.
|
||||
|
||||
use codec::{Decode, Encode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
@@ -37,9 +35,12 @@ use crate::{
|
||||
#[cfg(feature = "std")]
|
||||
use bip39::{Language, Mnemonic, MnemonicType};
|
||||
#[cfg(feature = "full_crypto")]
|
||||
use core::convert::{TryFrom, TryInto};
|
||||
use core::convert::TryFrom;
|
||||
#[cfg(feature = "full_crypto")]
|
||||
use libsecp256k1::{PublicKey, SecretKey};
|
||||
use secp256k1::{
|
||||
ecdsa::{RecoverableSignature, RecoveryId},
|
||||
Message, PublicKey, SecretKey, SECP256K1,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
#[cfg(feature = "full_crypto")]
|
||||
@@ -57,7 +58,17 @@ type Seed = [u8; 32];
|
||||
/// The ECDSA compressed public key.
|
||||
#[cfg_attr(feature = "full_crypto", derive(Hash))]
|
||||
#[derive(
|
||||
Clone, Encode, Decode, PassByInner, MaxEncodedLen, TypeInfo, Eq, PartialEq, PartialOrd, Ord,
|
||||
Clone,
|
||||
Copy,
|
||||
Encode,
|
||||
Decode,
|
||||
PassByInner,
|
||||
MaxEncodedLen,
|
||||
TypeInfo,
|
||||
Eq,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
)]
|
||||
pub struct Public(pub [u8; 33]);
|
||||
|
||||
@@ -75,10 +86,16 @@ impl Public {
|
||||
/// This will convert the full public key into the compressed format.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn from_full(full: &[u8]) -> Result<Self, ()> {
|
||||
libsecp256k1::PublicKey::parse_slice(full, None)
|
||||
.map(|k| k.serialize_compressed())
|
||||
.map(Self)
|
||||
.map_err(|_| ())
|
||||
let pubkey = if full.len() == 64 {
|
||||
// Tag it as uncompressed public key.
|
||||
let mut tagged_full = [0u8; 65];
|
||||
tagged_full[0] = 0x04;
|
||||
tagged_full[1..].copy_from_slice(full);
|
||||
secp256k1::PublicKey::from_slice(&tagged_full)
|
||||
} else {
|
||||
secp256k1::PublicKey::from_slice(full)
|
||||
};
|
||||
pubkey.map(|k| Self(k.serialize())).map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,55 +313,46 @@ impl Signature {
|
||||
///
|
||||
/// NOTE: No checking goes on to ensure this is a real signature. Only use it if
|
||||
/// you are certain that the array actually is a signature. GIGO!
|
||||
pub fn from_slice(data: &[u8]) -> Self {
|
||||
pub fn from_slice(data: &[u8]) -> Option<Self> {
|
||||
if data.len() != 65 {
|
||||
return None
|
||||
}
|
||||
let mut r = [0u8; 65];
|
||||
r.copy_from_slice(data);
|
||||
Signature(r)
|
||||
Some(Signature(r))
|
||||
}
|
||||
|
||||
/// Recover the public key from this signature and a message.
|
||||
#[cfg(feature = "full_crypto")]
|
||||
pub fn recover<M: AsRef<[u8]>>(&self, message: M) -> Option<Public> {
|
||||
let message = libsecp256k1::Message::parse(&blake2_256(message.as_ref()));
|
||||
let sig: (_, _) = self.try_into().ok()?;
|
||||
libsecp256k1::recover(&message, &sig.0, &sig.1)
|
||||
.ok()
|
||||
.map(|recovered| Public(recovered.serialize_compressed()))
|
||||
self.recover_prehashed(&blake2_256(message.as_ref()))
|
||||
}
|
||||
|
||||
/// Recover the public key from this signature and a pre-hashed message.
|
||||
#[cfg(feature = "full_crypto")]
|
||||
pub fn recover_prehashed(&self, message: &[u8; 32]) -> Option<Public> {
|
||||
let message = libsecp256k1::Message::parse(message);
|
||||
|
||||
let sig: (_, _) = self.try_into().ok()?;
|
||||
|
||||
libsecp256k1::recover(&message, &sig.0, &sig.1)
|
||||
let rid = RecoveryId::from_i32(self.0[64] as i32).ok()?;
|
||||
let sig = RecoverableSignature::from_compact(&self.0[..64], rid).ok()?;
|
||||
let message = Message::from_slice(message).expect("Message is 32 bytes; qed");
|
||||
SECP256K1
|
||||
.recover_ecdsa(&message, &sig)
|
||||
.ok()
|
||||
.map(|key| Public(key.serialize_compressed()))
|
||||
.map(|pubkey| Public(pubkey.serialize()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
impl From<(libsecp256k1::Signature, libsecp256k1::RecoveryId)> for Signature {
|
||||
fn from(x: (libsecp256k1::Signature, libsecp256k1::RecoveryId)) -> Signature {
|
||||
impl From<RecoverableSignature> for Signature {
|
||||
fn from(recsig: RecoverableSignature) -> Signature {
|
||||
let mut r = Self::default();
|
||||
r.0[0..64].copy_from_slice(&x.0.serialize()[..]);
|
||||
r.0[64] = x.1.serialize();
|
||||
let (recid, sig) = recsig.serialize_compact();
|
||||
r.0[..64].copy_from_slice(&sig);
|
||||
// This is safe due to the limited range of possible valid ids.
|
||||
r.0[64] = recid.to_i32() as u8;
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
impl<'a> TryFrom<&'a Signature> for (libsecp256k1::Signature, libsecp256k1::RecoveryId) {
|
||||
type Error = ();
|
||||
fn try_from(
|
||||
x: &'a Signature,
|
||||
) -> Result<(libsecp256k1::Signature, libsecp256k1::RecoveryId), Self::Error> {
|
||||
parse_signature_standard(&x.0).map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
/// Derive a single hard junction.
|
||||
#[cfg(feature = "full_crypto")]
|
||||
fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed {
|
||||
@@ -362,7 +370,7 @@ pub enum DeriveError {
|
||||
#[cfg(feature = "full_crypto")]
|
||||
#[derive(Clone)]
|
||||
pub struct Pair {
|
||||
public: PublicKey,
|
||||
public: Public,
|
||||
secret: SecretKey,
|
||||
}
|
||||
|
||||
@@ -416,8 +424,9 @@ impl TraitPair for Pair {
|
||||
/// You should never need to use this; generate(), generate_with_phrase
|
||||
fn from_seed_slice(seed_slice: &[u8]) -> Result<Pair, SecretStringError> {
|
||||
let secret =
|
||||
SecretKey::parse_slice(seed_slice).map_err(|_| SecretStringError::InvalidSeedLength)?;
|
||||
let public = PublicKey::from_secret_key(&secret);
|
||||
SecretKey::from_slice(seed_slice).map_err(|_| SecretStringError::InvalidSeedLength)?;
|
||||
let public = PublicKey::from_secret_key(SECP256K1, &secret);
|
||||
let public = Public(public.serialize());
|
||||
Ok(Pair { public, secret })
|
||||
}
|
||||
|
||||
@@ -427,7 +436,7 @@ impl TraitPair for Pair {
|
||||
path: Iter,
|
||||
_seed: Option<Seed>,
|
||||
) -> Result<(Pair, Option<Seed>), DeriveError> {
|
||||
let mut acc = self.secret.serialize();
|
||||
let mut acc = self.seed();
|
||||
for j in path {
|
||||
match j {
|
||||
DeriveJunction::Soft(_cc) => return Err(DeriveError::SoftKeyInPath),
|
||||
@@ -439,25 +448,19 @@ impl TraitPair for Pair {
|
||||
|
||||
/// Get the public key.
|
||||
fn public(&self) -> Public {
|
||||
Public(self.public.serialize_compressed())
|
||||
self.public
|
||||
}
|
||||
|
||||
/// Sign a message.
|
||||
fn sign(&self, message: &[u8]) -> Signature {
|
||||
let message = libsecp256k1::Message::parse(&blake2_256(message));
|
||||
libsecp256k1::sign(&message, &self.secret).into()
|
||||
self.sign_prehashed(&blake2_256(message))
|
||||
}
|
||||
|
||||
/// Verify a signature on a message. Returns true if the signature is good.
|
||||
fn verify<M: AsRef<[u8]>>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool {
|
||||
let message = libsecp256k1::Message::parse(&blake2_256(message.as_ref()));
|
||||
let sig: (_, _) = match sig.try_into() {
|
||||
Ok(x) => x,
|
||||
_ => return false,
|
||||
};
|
||||
match libsecp256k1::recover(&message, &sig.0, &sig.1) {
|
||||
Ok(actual) => pubkey.0[..] == actual.serialize_compressed()[..],
|
||||
_ => false,
|
||||
match sig.recover(message) {
|
||||
Some(actual) => actual == *pubkey,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,17 +469,9 @@ impl TraitPair for Pair {
|
||||
/// This doesn't use the type system to ensure that `sig` and `pubkey` are the correct
|
||||
/// size. Use it only if you're coming from byte buffers and need the speed.
|
||||
fn verify_weak<P: AsRef<[u8]>, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool {
|
||||
let message = libsecp256k1::Message::parse(&blake2_256(message.as_ref()));
|
||||
if sig.len() != 65 {
|
||||
return false
|
||||
}
|
||||
let (sig, ri) = match parse_signature_standard(&sig) {
|
||||
Ok(sigri) => sigri,
|
||||
_ => return false,
|
||||
};
|
||||
match libsecp256k1::recover(&message, &sig, &ri) {
|
||||
Ok(actual) => pubkey.as_ref() == &actual.serialize()[1..],
|
||||
_ => false,
|
||||
match Signature::from_slice(sig).and_then(|sig| sig.recover(message)) {
|
||||
Some(actual) => actual.as_ref() == pubkey.as_ref(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,7 +485,7 @@ impl TraitPair for Pair {
|
||||
impl Pair {
|
||||
/// Get the seed for this key.
|
||||
pub fn seed(&self) -> Seed {
|
||||
self.secret.serialize()
|
||||
self.secret.serialize_secret()
|
||||
}
|
||||
|
||||
/// Exactly as `from_string` except that if no matches are found then, the the first 32
|
||||
@@ -507,57 +502,57 @@ impl Pair {
|
||||
|
||||
/// Sign a pre-hashed message
|
||||
pub fn sign_prehashed(&self, message: &[u8; 32]) -> Signature {
|
||||
let message = libsecp256k1::Message::parse(message);
|
||||
libsecp256k1::sign(&message, &self.secret).into()
|
||||
let message = Message::from_slice(message).expect("Message is 32 bytes; qed");
|
||||
SECP256K1.sign_ecdsa_recoverable(&message, &self.secret).into()
|
||||
}
|
||||
|
||||
/// Verify a signature on a pre-hashed message. Return `true` if the signature is valid
|
||||
/// and thus matches the given `public` key.
|
||||
pub fn verify_prehashed(sig: &Signature, message: &[u8; 32], public: &Public) -> bool {
|
||||
let message = libsecp256k1::Message::parse(message);
|
||||
|
||||
let sig: (_, _) = match sig.try_into() {
|
||||
Ok(x) => x,
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
match libsecp256k1::recover(&message, &sig.0, &sig.1) {
|
||||
Ok(actual) => public.0[..] == actual.serialize_compressed()[..],
|
||||
_ => false,
|
||||
match sig.recover_prehashed(message) {
|
||||
Some(actual) => actual == *public,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify a signature on a message. Returns true if the signature is good.
|
||||
/// Parses Signature using parse_overflowing_slice
|
||||
/// Parses Signature using parse_overflowing_slice.
|
||||
#[deprecated(note = "please use `verify` instead")]
|
||||
pub fn verify_deprecated<M: AsRef<[u8]>>(sig: &Signature, message: M, pubkey: &Public) -> bool {
|
||||
let message = libsecp256k1::Message::parse(&blake2_256(message.as_ref()));
|
||||
let (sig, ri) = match parse_signature_overflowing(&sig.0) {
|
||||
Ok(sigri) => sigri,
|
||||
|
||||
let parse_signature_overflowing = |x: [u8; 65]| {
|
||||
let sig = libsecp256k1::Signature::parse_overflowing_slice(&x[..64]).ok()?;
|
||||
let rid = libsecp256k1::RecoveryId::parse(x[64]).ok()?;
|
||||
Some((sig, rid))
|
||||
};
|
||||
|
||||
let (sig, rid) = match parse_signature_overflowing(sig.0) {
|
||||
Some(sigri) => sigri,
|
||||
_ => return false,
|
||||
};
|
||||
match libsecp256k1::recover(&message, &sig, &ri) {
|
||||
Ok(actual) => pubkey.0[..] == actual.serialize_compressed()[..],
|
||||
match libsecp256k1::recover(&message, &sig, &rid) {
|
||||
Ok(actual) => pubkey.0 == actual.serialize_compressed(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The `secp256k1` backend doesn't implement cleanup for their private keys.
|
||||
// Currently we should take care of wiping the secret from memory.
|
||||
// NOTE: this solution is not effective when `Pair` is moved around memory.
|
||||
// The very same problem affects other cryptographic backends that are just using
|
||||
// `zeroize`for their secrets.
|
||||
#[cfg(feature = "full_crypto")]
|
||||
fn parse_signature_standard(
|
||||
x: &[u8],
|
||||
) -> Result<(libsecp256k1::Signature, libsecp256k1::RecoveryId), libsecp256k1::Error> {
|
||||
let sig = libsecp256k1::Signature::parse_standard_slice(&x[..64])?;
|
||||
let ri = libsecp256k1::RecoveryId::parse(x[64])?;
|
||||
Ok((sig, ri))
|
||||
}
|
||||
|
||||
#[cfg(feature = "full_crypto")]
|
||||
fn parse_signature_overflowing(
|
||||
x: &[u8],
|
||||
) -> Result<(libsecp256k1::Signature, libsecp256k1::RecoveryId), libsecp256k1::Error> {
|
||||
let sig = libsecp256k1::Signature::parse_overflowing_slice(&x[..64])?;
|
||||
let ri = libsecp256k1::RecoveryId::parse(x[64])?;
|
||||
Ok((sig, ri))
|
||||
impl Drop for Pair {
|
||||
fn drop(&mut self) {
|
||||
let ptr = self.secret.as_mut_ptr();
|
||||
for off in 0..self.secret.len() {
|
||||
unsafe {
|
||||
core::ptr::write_volatile(ptr.add(off), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptoType for Public {
|
||||
@@ -578,12 +573,9 @@ impl CryptoType for Pair {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::{
|
||||
crypto::{
|
||||
set_default_ss58_version, PublicError, Ss58AddressFormat, Ss58AddressFormatRegistry,
|
||||
DEV_PHRASE,
|
||||
},
|
||||
keccak_256,
|
||||
use crate::crypto::{
|
||||
set_default_ss58_version, PublicError, Ss58AddressFormat, Ss58AddressFormatRegistry,
|
||||
DEV_PHRASE,
|
||||
};
|
||||
use hex_literal::hex;
|
||||
use serde_json;
|
||||
@@ -802,22 +794,20 @@ mod test {
|
||||
// `msg` shouldn't be mangled
|
||||
let msg = [0u8; 32];
|
||||
let sig1 = pair.sign_prehashed(&msg);
|
||||
let sig2: Signature =
|
||||
libsecp256k1::sign(&libsecp256k1::Message::parse(&msg), &pair.secret).into();
|
||||
|
||||
let sig2: Signature = {
|
||||
let message = Message::from_slice(&msg).unwrap();
|
||||
SECP256K1.sign_ecdsa_recoverable(&message, &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 =
|
||||
libsecp256k1::sign(&libsecp256k1::Message::parse(&msg), &pair.secret).into();
|
||||
|
||||
let msg = b"this should be hashed";
|
||||
let sig1 = pair.sign_prehashed(&blake2_256(msg));
|
||||
let sig2 = pair.sign(msg);
|
||||
assert_eq!(sig1, sig2);
|
||||
}
|
||||
|
||||
@@ -826,12 +816,12 @@ mod test {
|
||||
let (pair, _, _) = Pair::generate_with_phrase(Some("password"));
|
||||
|
||||
// `msg` and `sig` match
|
||||
let msg = keccak_256(b"this should be hashed");
|
||||
let msg = blake2_256(b"this should be hashed");
|
||||
let sig = pair.sign_prehashed(&msg);
|
||||
assert!(Pair::verify_prehashed(&sig, &msg, &pair.public()));
|
||||
|
||||
// `msg` and `sig` don't match
|
||||
let msg = keccak_256(b"this is a different message");
|
||||
let msg = blake2_256(b"this is a different message");
|
||||
assert!(!Pair::verify_prehashed(&sig, &msg, &pair.public()));
|
||||
}
|
||||
|
||||
@@ -840,7 +830,7 @@ mod test {
|
||||
let (pair, _, _) = Pair::generate_with_phrase(Some("password"));
|
||||
|
||||
// recovered key matches signing key
|
||||
let msg = keccak_256(b"this should be hashed");
|
||||
let msg = blake2_256(b"this should be hashed");
|
||||
let sig = pair.sign_prehashed(&msg);
|
||||
let key = sig.recover_prehashed(&msg).unwrap();
|
||||
assert_eq!(pair.public(), key);
|
||||
@@ -849,7 +839,7 @@ mod test {
|
||||
assert!(Pair::verify_prehashed(&sig, &msg, &key));
|
||||
|
||||
// recovered key and signing key don't match
|
||||
let msg = keccak_256(b"this is a different message");
|
||||
let msg = blake2_256(b"this is a different message");
|
||||
let key = sig.recover_prehashed(&msg).unwrap();
|
||||
assert_ne!(pair.public(), key);
|
||||
}
|
||||
|
||||
@@ -321,10 +321,13 @@ impl Signature {
|
||||
///
|
||||
/// NOTE: No checking goes on to ensure this is a real signature. Only use it if
|
||||
/// you are certain that the array actually is a signature. GIGO!
|
||||
pub fn from_slice(data: &[u8]) -> Self {
|
||||
pub fn from_slice(data: &[u8]) -> Option<Self> {
|
||||
if data.len() != 64 {
|
||||
return None
|
||||
}
|
||||
let mut r = [0u8; 64];
|
||||
r.copy_from_slice(data);
|
||||
Signature(r)
|
||||
Some(Signature(r))
|
||||
}
|
||||
|
||||
/// A new instance from an H512.
|
||||
|
||||
@@ -341,10 +341,13 @@ impl Signature {
|
||||
///
|
||||
/// NOTE: No checking goes on to ensure this is a real signature. Only use it if
|
||||
/// you are certain that the array actually is a signature. GIGO!
|
||||
pub fn from_slice(data: &[u8]) -> Self {
|
||||
pub fn from_slice(data: &[u8]) -> Option<Self> {
|
||||
if data.len() != 64 {
|
||||
return None
|
||||
}
|
||||
let mut r = [0u8; 64];
|
||||
r.copy_from_slice(data);
|
||||
Signature(r)
|
||||
Some(Signature(r))
|
||||
}
|
||||
|
||||
/// A new instance from an H512.
|
||||
|
||||
@@ -30,6 +30,7 @@ sp-tracing = { version = "4.0.0", default-features = false, path = "../tracing"
|
||||
log = { version = "0.4.8", optional = true }
|
||||
futures = { version = "0.3.1", features = ["thread-pool"], optional = true }
|
||||
parking_lot = { version = "0.12.0", optional = true }
|
||||
secp256k1 = { version = "0.21.2", features = ["recovery", "global-context"], optional = true }
|
||||
tracing = { version = "0.1.29", default-features = false }
|
||||
tracing-core = { version = "0.1.17", default-features = false}
|
||||
|
||||
@@ -44,6 +45,7 @@ std = [
|
||||
"sp-trie",
|
||||
"sp-state-machine",
|
||||
"libsecp256k1",
|
||||
"secp256k1",
|
||||
"sp-runtime-interface/std",
|
||||
"sp-externalities",
|
||||
"sp-wasm-interface/std",
|
||||
|
||||
@@ -66,6 +66,12 @@ use sp_runtime_interface::{
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use secp256k1::{
|
||||
ecdsa::{RecoverableSignature, RecoveryId},
|
||||
Message, SECP256K1,
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use sp_externalities::{Externalities, ExternalitiesExt};
|
||||
|
||||
@@ -647,7 +653,7 @@ pub trait Crypto {
|
||||
SyncCryptoStore::sign_with(keystore, id, &pub_key.into(), msg)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|sig| ed25519::Signature::from_slice(sig.as_slice()))
|
||||
.and_then(|sig| ed25519::Signature::from_slice(&sig))
|
||||
}
|
||||
|
||||
/// Verify `ed25519` signature.
|
||||
@@ -771,7 +777,7 @@ pub trait Crypto {
|
||||
SyncCryptoStore::sign_with(keystore, id, &pub_key.into(), msg)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|sig| sr25519::Signature::from_slice(sig.as_slice()))
|
||||
.and_then(|sig| sr25519::Signature::from_slice(&sig))
|
||||
}
|
||||
|
||||
/// Verify an `sr25519` signature.
|
||||
@@ -820,7 +826,7 @@ pub trait Crypto {
|
||||
SyncCryptoStore::sign_with(keystore, id, &pub_key.into(), msg)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|sig| ecdsa::Signature::from_slice(sig.as_slice()))
|
||||
.and_then(|sig| ecdsa::Signature::from_slice(&sig))
|
||||
}
|
||||
|
||||
/// Sign the given a pre-hashed `msg` with the `ecdsa` key that corresponds to the given public
|
||||
@@ -842,7 +848,9 @@ pub trait Crypto {
|
||||
/// Verify `ecdsa` signature.
|
||||
///
|
||||
/// Returns `true` when the verification was successful.
|
||||
/// This version is able to handle, non-standard, overflowing signatures.
|
||||
fn ecdsa_verify(sig: &ecdsa::Signature, msg: &[u8], pub_key: &ecdsa::Public) -> bool {
|
||||
#[allow(deprecated)]
|
||||
ecdsa::Pair::verify_deprecated(sig, msg, pub_key)
|
||||
}
|
||||
|
||||
@@ -891,18 +899,20 @@ pub trait Crypto {
|
||||
///
|
||||
/// Returns `Err` if the signature is bad, otherwise the 64-byte pubkey
|
||||
/// (doesn't include the 0x04 prefix).
|
||||
/// This version is able to handle, non-standard, overflowing signatures.
|
||||
fn secp256k1_ecdsa_recover(
|
||||
sig: &[u8; 65],
|
||||
msg: &[u8; 32],
|
||||
) -> Result<[u8; 64], EcdsaVerifyError> {
|
||||
let rs = libsecp256k1::Signature::parse_overflowing_slice(&sig[0..64])
|
||||
.map_err(|_| EcdsaVerifyError::BadRS)?;
|
||||
let v = libsecp256k1::RecoveryId::parse(
|
||||
if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8
|
||||
let rid = libsecp256k1::RecoveryId::parse(
|
||||
if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8,
|
||||
)
|
||||
.map_err(|_| EcdsaVerifyError::BadV)?;
|
||||
let pubkey = libsecp256k1::recover(&libsecp256k1::Message::parse(msg), &rs, &v)
|
||||
.map_err(|_| EcdsaVerifyError::BadSignature)?;
|
||||
let sig = libsecp256k1::Signature::parse_overflowing_slice(&sig[..64])
|
||||
.map_err(|_| EcdsaVerifyError::BadRS)?;
|
||||
let msg = libsecp256k1::Message::parse(msg);
|
||||
let pubkey =
|
||||
libsecp256k1::recover(&msg, &sig, &rid).map_err(|_| EcdsaVerifyError::BadSignature)?;
|
||||
let mut res = [0u8; 64];
|
||||
res.copy_from_slice(&pubkey.serialize()[1..65]);
|
||||
Ok(res)
|
||||
@@ -920,16 +930,16 @@ pub trait Crypto {
|
||||
sig: &[u8; 65],
|
||||
msg: &[u8; 32],
|
||||
) -> Result<[u8; 64], EcdsaVerifyError> {
|
||||
let rs = libsecp256k1::Signature::parse_standard_slice(&sig[0..64])
|
||||
let rid = RecoveryId::from_i32(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as i32)
|
||||
.map_err(|_| EcdsaVerifyError::BadV)?;
|
||||
let sig = RecoverableSignature::from_compact(&sig[..64], rid)
|
||||
.map_err(|_| EcdsaVerifyError::BadRS)?;
|
||||
let v = libsecp256k1::RecoveryId::parse(
|
||||
if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8
|
||||
)
|
||||
.map_err(|_| EcdsaVerifyError::BadV)?;
|
||||
let pubkey = libsecp256k1::recover(&libsecp256k1::Message::parse(msg), &rs, &v)
|
||||
let msg = Message::from_slice(msg).expect("Message is 32 bytes; qed");
|
||||
let pubkey = SECP256K1
|
||||
.recover_ecdsa(&msg, &sig)
|
||||
.map_err(|_| EcdsaVerifyError::BadSignature)?;
|
||||
let mut res = [0u8; 64];
|
||||
res.copy_from_slice(&pubkey.serialize()[1..65]);
|
||||
res.copy_from_slice(&pubkey.serialize_uncompressed()[1..]);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -943,14 +953,15 @@ pub trait Crypto {
|
||||
sig: &[u8; 65],
|
||||
msg: &[u8; 32],
|
||||
) -> Result<[u8; 33], EcdsaVerifyError> {
|
||||
let rs = libsecp256k1::Signature::parse_overflowing_slice(&sig[0..64])
|
||||
.map_err(|_| EcdsaVerifyError::BadRS)?;
|
||||
let v = libsecp256k1::RecoveryId::parse(
|
||||
if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8
|
||||
let rid = libsecp256k1::RecoveryId::parse(
|
||||
if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8,
|
||||
)
|
||||
.map_err(|_| EcdsaVerifyError::BadV)?;
|
||||
let pubkey = libsecp256k1::recover(&libsecp256k1::Message::parse(msg), &rs, &v)
|
||||
.map_err(|_| EcdsaVerifyError::BadSignature)?;
|
||||
let sig = libsecp256k1::Signature::parse_overflowing_slice(&sig[0..64])
|
||||
.map_err(|_| EcdsaVerifyError::BadRS)?;
|
||||
let msg = libsecp256k1::Message::parse(msg);
|
||||
let pubkey =
|
||||
libsecp256k1::recover(&msg, &sig, &rid).map_err(|_| EcdsaVerifyError::BadSignature)?;
|
||||
Ok(pubkey.serialize_compressed())
|
||||
}
|
||||
|
||||
@@ -965,15 +976,15 @@ pub trait Crypto {
|
||||
sig: &[u8; 65],
|
||||
msg: &[u8; 32],
|
||||
) -> Result<[u8; 33], EcdsaVerifyError> {
|
||||
let rs = libsecp256k1::Signature::parse_standard_slice(&sig[0..64])
|
||||
let rid = RecoveryId::from_i32(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as i32)
|
||||
.map_err(|_| EcdsaVerifyError::BadV)?;
|
||||
let sig = RecoverableSignature::from_compact(&sig[..64], rid)
|
||||
.map_err(|_| EcdsaVerifyError::BadRS)?;
|
||||
let v = libsecp256k1::RecoveryId::parse(
|
||||
if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8
|
||||
)
|
||||
.map_err(|_| EcdsaVerifyError::BadV)?;
|
||||
let pubkey = libsecp256k1::recover(&libsecp256k1::Message::parse(msg), &rs, &v)
|
||||
let msg = Message::from_slice(msg).expect("Message is 32 bytes; qed");
|
||||
let pubkey = SECP256K1
|
||||
.recover_ecdsa(&msg, &sig)
|
||||
.map_err(|_| EcdsaVerifyError::BadSignature)?;
|
||||
Ok(pubkey.serialize_compressed())
|
||||
Ok(pubkey.serialize())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user