Skip to content

Commit

Permalink
RUSTSEC-2021-0076 bump libsecp256k1
Browse files Browse the repository at this point in the history
libsecp256k1 allows overflowing signatures
https://rustsec.org/advisories/RUSTSEC-2021-0076

Changes were made to conform to libsecp256k1 version differences.

Closes paritytech#9356
  • Loading branch information
trevor-crypto committed Aug 5, 2021
1 parent 4d93a6e commit dd35dcb
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 54 deletions.
69 changes: 64 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion primitives/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ schnorrkel = { version = "0.9.1", features = ["preaudit_deprecated", "u64_backen
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" }
Expand Down
76 changes: 28 additions & 48 deletions primitives/core/src/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,7 @@ use bip39::{Language, Mnemonic, MnemonicType};
#[cfg(feature = "full_crypto")]
use core::convert::{TryFrom, TryInto};
#[cfg(feature = "full_crypto")]
use secp256k1::{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;
use libsecp256k1::{PublicKey, SecretKey};

/// An identifier used to match public keys against ecdsa keys
pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ecds");
Expand Down Expand Up @@ -108,7 +103,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(|_| ())
Expand Down Expand Up @@ -364,29 +359,29 @@ 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()))
}

/// 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();
Expand All @@ -395,14 +390,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> {
fn try_from(x: &'a Signature) -> Result<(libsecp256k1::Signature, libsecp256k1::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(|_| ())?,
libsecp256k1::Signature::parse_standard_slice(&x.0[0..64]).expect("hardcoded to 64 bytes; qed"),
libsecp256k1::RecoveryId::parse(x.0[64]).map_err(|_| ())?,
))
}
}
Expand Down Expand Up @@ -510,18 +503,15 @@ 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 sig: (_, _) = match sig.try_into() {
Ok(x) => x,
_ => return false,
};
match secp256k1::recover(&message, &sig.0, &sig.1) {
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,
}
Expand All @@ -532,19 +522,11 @@ 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()));
if sig.len() != 65 {
return false
}
let ri = match secp256k1::RecoveryId::parse(sig[64]) {
Ok(x) => x,
_ => return false,
};
let sig = match secp256k1::Signature::parse_slice(&sig[0..64]) {
Ok(x) => x,
_ => return false,
};
match secp256k1::recover(&message, &sig, &ri) {
let message = libsecp256k1::Message::parse(&blake2_256(message.as_ref()));
if sig.len() != 65 { return false }
let ri = match libsecp256k1::RecoveryId::parse(sig[64]) { Ok(x) => x, _ => return false };
let sig = match libsecp256k1::Signature::parse_standard_slice(&sig[0..64]) { Ok(x) => x, _ => return false };
match libsecp256k1::recover(&message, &sig, &ri) {
Ok(actual) => pubkey.as_ref() == &actual.serialize()[1..],
_ => false,
}
Expand Down Expand Up @@ -577,21 +559,21 @@ 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,
}
Expand Down Expand Up @@ -839,8 +821,7 @@ mod test {
// `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();
let sig2: Signature = libsecp256k1::sign(&libsecp256k1::Message::parse(&msg), &pair.secret).into();

assert_eq!(sig1, sig2);

Expand All @@ -852,8 +833,7 @@ mod test {
// 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();
let sig2: Signature = libsecp256k1::sign(&libsecp256k1::Message::parse(&msg), &pair.secret).into();

assert_eq!(sig1, sig2);
}
Expand Down

0 comments on commit dd35dcb

Please sign in to comment.