RUSTSEC-2021-0076 bump libsecp256k1 (#9391)

* RUSTSEC-2021-0076 bump libsecp256k1

libsecp256k1 allows overflowing signatures
https://rustsec.org/advisories/RUSTSEC-2021-0076

Changes were made to conform to libsecp256k1 version differences.

Closes #9356

* parse_standard_slice() -> parse_overflowing_slice()

* Added v2 host function for ecdsa_verify

* Add feature tag over helpers

* Added ecdsa_verify v2 to test runner

* PR feedback

- Spaces -> tabs
- renamed two helper functions

* Fixed imports after rebasing

* Bump rest of libsecp256k1 (and libp2p)

libp2p also uses libsecp256k1 so it is required to be bumped too, along
with all the version difference changes.

* Add version2 for ecdsa pubkey recovery

* libp2p rebase master fixes

* Fix test panic when non Behaviour event is returned

* Update bin/node/browser-testing/Cargo.toml

* Update primitives/core/src/ecdsa.rs

* Update primitives/core/src/ecdsa.rs

* Update Cargo.lock

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
Trevor Arjeski
2021-08-16 17:17:10 +03:00
committed by GitHub
parent 65a8b00f3b
commit d9f02296ab
26 changed files with 583 additions and 367 deletions
+1 -3
View File
@@ -62,9 +62,7 @@ schnorrkel = { version = "0.9.1", features = [
sha2 = { version = "0.9.2", default-features = false, optional = true }
hex = { version = "0.4", default-features = false, optional = true }
twox-hash = { version = "1.5.0", default-features = false, optional = true }
libsecp256k1 = { version = "0.3.2", default-features = false, features = [
"hmac",
], optional = true }
libsecp256k1 = { version = "0.6", default-features = false, features = ["hmac", "static-context"], optional = true }
merlin = { version = "2.0", default-features = false, optional = true }
sp-runtime-interface = { version = "4.0.0-dev", default-features = false, path = "../runtime-interface" }
+61 -39
View File
@@ -19,10 +19,8 @@
//! Simple ECDSA API.
// end::description[]
#[cfg(feature = "full_crypto")]
use sp_std::vec::Vec;
use codec::{Decode, Encode, MaxEncodedLen};
use sp_runtime_interface::pass_by::PassByInner;
use sp_std::cmp::Ordering;
#[cfg(feature = "std")]
@@ -40,12 +38,11 @@ use bip39::{Language, Mnemonic, MnemonicType};
#[cfg(feature = "full_crypto")]
use core::convert::{TryFrom, TryInto};
#[cfg(feature = "full_crypto")]
use secp256k1::{PublicKey, SecretKey};
use libsecp256k1::{PublicKey, SecretKey};
#[cfg(feature = "std")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use sp_runtime_interface::pass_by::PassByInner;
#[cfg(feature = "std")]
use substrate_bip39::seed_from_entropy;
#[cfg(feature = "full_crypto")]
use sp_std::vec::Vec;
/// An identifier used to match public keys against ecdsa keys
pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ecds");
@@ -108,7 +105,7 @@ impl Public {
/// This will convert the full public key into the compressed format.
#[cfg(feature = "std")]
pub fn from_full(full: &[u8]) -> Result<Self, ()> {
secp256k1::PublicKey::parse_slice(full, None)
libsecp256k1::PublicKey::parse_slice(full, None)
.map(|k| k.serialize_compressed())
.map(Self)
.map_err(|_| ())
@@ -364,9 +361,9 @@ impl Signature {
/// 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 = secp256k1::Message::parse(&blake2_256(message.as_ref()));
let message = libsecp256k1::Message::parse(&blake2_256(message.as_ref()));
let sig: (_, _) = self.try_into().ok()?;
secp256k1::recover(&message, &sig.0, &sig.1)
libsecp256k1::recover(&message, &sig.0, &sig.1)
.ok()
.map(|recovered| Public(recovered.serialize_compressed()))
}
@@ -374,19 +371,19 @@ impl Signature {
/// 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 = secp256k1::Message::parse(message);
let message = libsecp256k1::Message::parse(message);
let sig: (_, _) = self.try_into().ok()?;
secp256k1::recover(&message, &sig.0, &sig.1)
libsecp256k1::recover(&message, &sig.0, &sig.1)
.ok()
.map(|key| Public(key.serialize_compressed()))
}
}
#[cfg(feature = "full_crypto")]
impl From<(secp256k1::Signature, secp256k1::RecoveryId)> for Signature {
fn from(x: (secp256k1::Signature, secp256k1::RecoveryId)) -> Signature {
impl From<(libsecp256k1::Signature, libsecp256k1::RecoveryId)> for Signature {
fn from(x: (libsecp256k1::Signature, libsecp256k1::RecoveryId)) -> Signature {
let mut r = Self::default();
r.0[0..64].copy_from_slice(&x.0.serialize()[..]);
r.0[64] = x.1.serialize();
@@ -395,15 +392,12 @@ impl From<(secp256k1::Signature, secp256k1::RecoveryId)> for Signature {
}
#[cfg(feature = "full_crypto")]
impl<'a> TryFrom<&'a Signature> for (secp256k1::Signature, secp256k1::RecoveryId) {
impl<'a> TryFrom<&'a Signature> for (libsecp256k1::Signature, libsecp256k1::RecoveryId) {
type Error = ();
fn try_from(
x: &'a Signature,
) -> Result<(secp256k1::Signature, secp256k1::RecoveryId), Self::Error> {
Ok((
secp256k1::Signature::parse_slice(&x.0[0..64]).expect("hardcoded to 64 bytes; qed"),
secp256k1::RecoveryId::parse(x.0[64]).map_err(|_| ())?,
))
) -> Result<(libsecp256k1::Signature, libsecp256k1::RecoveryId), Self::Error> {
parse_signature_standard(&x.0).map_err(|_| ())
}
}
@@ -457,7 +451,7 @@ impl TraitPair for Pair {
phrase: &str,
password: Option<&str>,
) -> Result<(Pair, Seed), SecretStringError> {
let big_seed = seed_from_entropy(
let big_seed = substrate_bip39::seed_from_entropy(
Mnemonic::from_phrase(phrase, Language::English)
.map_err(|_| SecretStringError::InvalidPhrase)?
.entropy(),
@@ -510,18 +504,18 @@ impl TraitPair for Pair {
/// Sign a message.
fn sign(&self, message: &[u8]) -> Signature {
let message = secp256k1::Message::parse(&blake2_256(message));
secp256k1::sign(&message, &self.secret).into()
let message = libsecp256k1::Message::parse(&blake2_256(message));
libsecp256k1::sign(&message, &self.secret).into()
}
/// 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 = secp256k1::Message::parse(&blake2_256(message.as_ref()));
let message = libsecp256k1::Message::parse(&blake2_256(message.as_ref()));
let sig: (_, _) = match sig.try_into() {
Ok(x) => x,
_ => return false,
};
match secp256k1::recover(&message, &sig.0, &sig.1) {
match libsecp256k1::recover(&message, &sig.0, &sig.1) {
Ok(actual) => pubkey.0[..] == actual.serialize_compressed()[..],
_ => false,
}
@@ -532,19 +526,15 @@ 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 = secp256k1::Message::parse(&blake2_256(message.as_ref()));
let message = libsecp256k1::Message::parse(&blake2_256(message.as_ref()));
if sig.len() != 65 {
return false
}
let ri = match secp256k1::RecoveryId::parse(sig[64]) {
Ok(x) => x,
let (sig, ri) = match parse_signature_standard(&sig) {
Ok(sigri) => sigri,
_ => return false,
};
let sig = match secp256k1::Signature::parse_slice(&sig[0..64]) {
Ok(x) => x,
_ => return false,
};
match secp256k1::recover(&message, &sig, &ri) {
match libsecp256k1::recover(&message, &sig, &ri) {
Ok(actual) => pubkey.as_ref() == &actual.serialize()[1..],
_ => false,
}
@@ -577,25 +567,57 @@ impl Pair {
/// 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()
let message = libsecp256k1::Message::parse(message);
libsecp256k1::sign(&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 = secp256k1::Message::parse(message);
let message = libsecp256k1::Message::parse(message);
let sig: (_, _) = match sig.try_into() {
Ok(x) => x,
_ => return false,
};
match secp256k1::recover(&message, &sig.0, &sig.1) {
match libsecp256k1::recover(&message, &sig.0, &sig.1) {
Ok(actual) => public.0[..] == actual.serialize_compressed()[..],
_ => false,
}
}
/// Verify a signature on a message. Returns true if the signature is good.
/// Parses Signature using parse_overflowing_slice
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,
_ => return false,
};
match libsecp256k1::recover(&message, &sig, &ri) {
Ok(actual) => pubkey.0[..] == actual.serialize_compressed()[..],
_ => false,
}
}
}
#[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 CryptoType for Public {
@@ -840,7 +862,7 @@ mod test {
let msg = [0u8; 32];
let sig1 = pair.sign_prehashed(&msg);
let sig2: Signature =
secp256k1::sign(&secp256k1::Message::parse(&msg), &pair.secret).into();
libsecp256k1::sign(&libsecp256k1::Message::parse(&msg), &pair.secret).into();
assert_eq!(sig1, sig2);
@@ -853,7 +875,7 @@ mod test {
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();
libsecp256k1::sign(&libsecp256k1::Message::parse(&msg), &pair.secret).into();
assert_eq!(sig1, sig2);
}
+1 -1
View File
@@ -20,7 +20,7 @@ hash-db = { version = "0.15.2", default-features = false }
sp-core = { version = "4.0.0-dev", default-features = false, path = "../core" }
sp-keystore = { version = "0.10.0-dev", default-features = false, optional = true, path = "../keystore" }
sp-std = { version = "4.0.0-dev", default-features = false, path = "../std" }
libsecp256k1 = { version = "0.3.4", optional = true }
libsecp256k1 = { version = "0.6", optional = true }
sp-state-machine = { version = "0.10.0-dev", optional = true, path = "../state-machine" }
sp-wasm-interface = { version = "4.0.0-dev", path = "../wasm-interface", default-features = false }
sp-runtime-interface = { version = "4.0.0-dev", default-features = false, path = "../runtime-interface" }
+69 -12
View File
@@ -718,6 +718,14 @@ pub trait Crypto {
/// Verify `ecdsa` signature.
///
/// Returns `true` when the verification was successful.
fn ecdsa_verify(sig: &ecdsa::Signature, msg: &[u8], pub_key: &ecdsa::Public) -> bool {
ecdsa::Pair::verify_deprecated(sig, msg, pub_key)
}
/// Verify `ecdsa` signature.
///
/// Returns `true` when the verification was successful.
#[version(2)]
fn ecdsa_verify(sig: &ecdsa::Signature, msg: &[u8], pub_key: &ecdsa::Public) -> bool {
ecdsa::Pair::verify(sig, msg, pub_key)
}
@@ -752,12 +760,38 @@ pub trait Crypto {
sig: &[u8; 65],
msg: &[u8; 32],
) -> Result<[u8; 64], EcdsaVerifyError> {
let rs =
secp256k1::Signature::parse_slice(&sig[0..64]).map_err(|_| EcdsaVerifyError::BadRS)?;
let v =
secp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8)
.map_err(|_| EcdsaVerifyError::BadV)?;
let pubkey = secp256k1::recover(&secp256k1::Message::parse(msg), &rs, &v)
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
)
.map_err(|_| EcdsaVerifyError::BadV)?;
let pubkey = libsecp256k1::recover(&libsecp256k1::Message::parse(msg), &rs, &v)
.map_err(|_| EcdsaVerifyError::BadSignature)?;
let mut res = [0u8; 64];
res.copy_from_slice(&pubkey.serialize()[1..65]);
Ok(res)
}
/// Verify and recover a SECP256k1 ECDSA signature.
///
/// - `sig` is passed in RSV format. V should be either `0/1` or `27/28`.
/// - `msg` is the blake2-256 hash of the message.
///
/// Returns `Err` if the signature is bad, otherwise the 64-byte pubkey
/// (doesn't include the 0x04 prefix).
#[version(2)]
fn secp256k1_ecdsa_recover(
sig: &[u8; 65],
msg: &[u8; 32],
) -> Result<[u8; 64], EcdsaVerifyError> {
let rs = libsecp256k1::Signature::parse_standard_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
)
.map_err(|_| EcdsaVerifyError::BadV)?;
let pubkey = libsecp256k1::recover(&libsecp256k1::Message::parse(msg), &rs, &v)
.map_err(|_| EcdsaVerifyError::BadSignature)?;
let mut res = [0u8; 64];
res.copy_from_slice(&pubkey.serialize()[1..65]);
@@ -774,12 +808,35 @@ pub trait Crypto {
sig: &[u8; 65],
msg: &[u8; 32],
) -> Result<[u8; 33], EcdsaVerifyError> {
let rs =
secp256k1::Signature::parse_slice(&sig[0..64]).map_err(|_| EcdsaVerifyError::BadRS)?;
let v =
secp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8)
.map_err(|_| EcdsaVerifyError::BadV)?;
let pubkey = secp256k1::recover(&secp256k1::Message::parse(msg), &rs, &v)
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
)
.map_err(|_| EcdsaVerifyError::BadV)?;
let pubkey = libsecp256k1::recover(&libsecp256k1::Message::parse(msg), &rs, &v)
.map_err(|_| EcdsaVerifyError::BadSignature)?;
Ok(pubkey.serialize_compressed())
}
/// Verify and recover a SECP256k1 ECDSA signature.
///
/// - `sig` is passed in RSV format. V should be either `0/1` or `27/28`.
/// - `msg` is the blake2-256 hash of the message.
///
/// Returns `Err` if the signature is bad, otherwise the 33-byte compressed pubkey.
#[version(2)]
fn secp256k1_ecdsa_recover_compressed(
sig: &[u8; 65],
msg: &[u8; 32],
) -> Result<[u8; 33], EcdsaVerifyError> {
let rs = libsecp256k1::Signature::parse_standard_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
)
.map_err(|_| EcdsaVerifyError::BadV)?;
let pubkey = libsecp256k1::recover(&libsecp256k1::Message::parse(msg), &rs, &v)
.map_err(|_| EcdsaVerifyError::BadSignature)?;
Ok(pubkey.serialize_compressed())
}