From 76d051b16e53e026c5519470858cc7fc01e20e88 Mon Sep 17 00:00:00 2001 From: rajk93 Date: Tue, 24 Jun 2025 20:31:13 +0530 Subject: [PATCH] chore: added check in `ext_secp_recover` for signature normalization (#579) * chore: added check in ext_secp_recover for signature normalization * update wasm-bindgen * Add test for non-normalized signature --------- Co-authored-by: Valentin Fernandez --- packages/wasm-crypto/Cargo.lock | 205 ++++++++++++++--------- packages/wasm-crypto/Cargo.toml | 2 +- packages/wasm-crypto/src/rs/secp256k1.rs | 196 ++++++++++++---------- 3 files changed, 238 insertions(+), 165 deletions(-) diff --git a/packages/wasm-crypto/Cargo.lock b/packages/wasm-crypto/Cargo.lock index 1d826f28..b944cd2e 100644 --- a/packages/wasm-crypto/Cargo.lock +++ b/packages/wasm-crypto/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.2.1" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "blake2-rfc" @@ -125,15 +125,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - [[package]] name = "constant_time_eq" version = "0.1.5" @@ -219,7 +210,7 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.89", "synstructure", ] @@ -313,27 +304,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" -[[package]] -name = "lock_api" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.11" @@ -384,11 +360,11 @@ checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" [[package]] name = "once_cell" -version = "1.4.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" dependencies = [ - "parking_lot", + "parking_lot_core", ] [[package]] @@ -397,28 +373,17 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -[[package]] -name = "parking_lot" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" -dependencies = [ - "lock_api", - "parking_lot_core", -] - [[package]] name = "parking_lot_core" -version = "0.7.2" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ - "cfg-if 0.1.10", - "cloudabi", + "cfg-if 1.0.0", "libc", "redox_syscall", "smallvec", - "winapi", + "windows-targets", ] [[package]] @@ -445,18 +410,18 @@ checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.7" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -504,9 +469,12 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +dependencies = [ + "bitflags", +] [[package]] name = "rustc-demangle" @@ -520,6 +488,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + [[package]] name = "schnorrkel" version = "0.9.1" @@ -538,12 +512,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "scrypt" version = "0.2.0" @@ -601,9 +569,9 @@ checksum = "29f060a7d147e33490ec10da418795238fd7545bba241504d6b31a409f2e6210" [[package]] name = "smallvec" -version = "1.4.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "subtle" @@ -628,6 +596,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "synstructure" version = "0.12.4" @@ -636,7 +615,7 @@ checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.89", "unicode-xid", ] @@ -686,6 +665,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + [[package]] name = "unicode-normalization" version = "0.1.13" @@ -740,34 +725,35 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.79" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if 1.0.0", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.79" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", - "lazy_static", "log", "proc-macro2", "quote", - "syn", + "syn 2.0.104", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.79" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -775,22 +761,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.79" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.79" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wee_alloc" @@ -826,6 +815,70 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "zeroize" version = "1.1.0" @@ -843,6 +896,6 @@ checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.89", "synstructure", ] diff --git a/packages/wasm-crypto/Cargo.toml b/packages/wasm-crypto/Cargo.toml index ddb6ed81..2b06b860 100644 --- a/packages/wasm-crypto/Cargo.toml +++ b/packages/wasm-crypto/Cargo.toml @@ -32,7 +32,7 @@ sha2 = "0.8.1" tiny-bip39 = { version = "0.7", default-features = false } tiny-keccak = { version = "2.0.1", features = ["keccak"] } twox-hash = "1.5.0" -wasm-bindgen = "=0.2.79" +wasm-bindgen = "0.2.88" wee_alloc = "0.4.3" [dev-dependencies] diff --git a/packages/wasm-crypto/src/rs/secp256k1.rs b/packages/wasm-crypto/src/rs/secp256k1.rs index 792e5891..e97de3c4 100644 --- a/packages/wasm-crypto/src/rs/secp256k1.rs +++ b/packages/wasm-crypto/src/rs/secp256k1.rs @@ -1,130 +1,150 @@ // Copyright 2019-2025 @polkadot/wasm-crypto authors & contributors // SPDX-License-Identifier: Apache-2.0 -use secp256k1::{ecdsa::{RecoverableSignature, RecoveryId}, Message, PublicKey, SecretKey, SECP256K1}; +use secp256k1::{ + ecdsa::{RecoverableSignature, RecoveryId}, + Message, PublicKey, SecretKey, SECP256K1, +}; use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn ext_secp_pub_compress(pubkey: &[u8]) -> Vec { - match PublicKey::from_slice(&pubkey) { - Ok(p) => p - .serialize() - .to_vec(), - _ => panic!("Invalid pubkey provided.") - } + match PublicKey::from_slice(&pubkey) { + Ok(p) => p.serialize().to_vec(), + _ => panic!("Invalid pubkey provided."), + } } #[wasm_bindgen] pub fn ext_secp_pub_expand(pubkey: &[u8]) -> Vec { - match PublicKey::from_slice(&pubkey) { - Ok(p) => p - .serialize_uncompressed() - .to_vec(), - _ => panic!("Invalid pubkey provided.") - } + match PublicKey::from_slice(&pubkey) { + Ok(p) => p.serialize_uncompressed().to_vec(), + _ => panic!("Invalid pubkey provided."), + } } #[wasm_bindgen] pub fn ext_secp_from_seed(seed: &[u8]) -> Vec { - match SecretKey::from_slice(seed) { - Ok(s) => { - let mut res = vec![]; - let pubkey = PublicKey::from_secret_key(SECP256K1, &s); + match SecretKey::from_slice(seed) { + Ok(s) => { + let mut res = vec![]; + let pubkey = PublicKey::from_secret_key(SECP256K1, &s); - res.extend_from_slice(&s.serialize_secret()); - res.extend_from_slice(&pubkey.serialize()); + res.extend_from_slice(&s.serialize_secret()); + res.extend_from_slice(&pubkey.serialize()); - res - }, - _ => panic!("Invalid seed provided.") - } + res + } + _ => panic!("Invalid seed provided."), + } } #[wasm_bindgen] pub fn ext_secp_recover(hash: &[u8], sig: &[u8], rec: i32) -> Vec { - match RecoveryId::from_i32(rec) { - Ok(r) => match (Message::from_slice(hash), RecoverableSignature::from_compact(&sig, r)) { - (Ok(m), Ok(s)) => match s.recover(&m) { - Ok(k) => k - .serialize() - .to_vec(), - _ => panic!("Unable to recover.") - }, - _ => panic!("Invalid signature provided.") - }, - _ => panic!("Invalid recovery data provided.") - } + match RecoveryId::from_i32(rec) { + Ok(r) => match ( + Message::from_slice(hash), + RecoverableSignature::from_compact(&sig, r), + ) { + (Ok(m), Ok(s)) => { + let standard = s.to_standard(); + let mut normalized = standard; + normalized.normalize_s(); + + // check if the signature is normalized + if normalized.ne(&standard) { + panic!("Non-normalized signature provided."); + } + + match s.recover(&m) { + Ok(k) => k.serialize().to_vec(), + _ => panic!("Unable to recover."), + } + } + _ => panic!("Invalid signature provided."), + }, + _ => panic!("Invalid recovery data provided."), + } } #[wasm_bindgen] pub fn ext_secp_sign(hash: &[u8], seckey: &[u8]) -> Vec { - match (Message::from_slice(hash), SecretKey::from_slice(seckey)) { - (Ok(m), Ok(s)) => { - let mut res = vec![]; - let (rec, sig) = SECP256K1 - .sign_ecdsa_recoverable(&m, &s) - .serialize_compact(); + match (Message::from_slice(hash), SecretKey::from_slice(seckey)) { + (Ok(m), Ok(s)) => { + let mut res = vec![]; + let (rec, sig) = SECP256K1.sign_ecdsa_recoverable(&m, &s).serialize_compact(); - res.extend_from_slice(&sig); - res.push(rec.to_i32() as u8); + res.extend_from_slice(&sig); + res.push(rec.to_i32() as u8); - res - }, - _ => panic!("Invalid message or secret provided.") - } + res + } + _ => panic!("Invalid message or secret provided."), + } } #[cfg(test)] pub mod tests { - use hex_literal::hex; - use super::*; + use super::*; + use hex_literal::hex; - #[test] - fn can_create_pair() { - let seckey = hex!("4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae"); - let expected = hex!("4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae03fd8c74f795ced92064b86191cb2772b1e3a0947740aa0a5a6e379592471fd85b"); - let res = ext_secp_from_seed(&seckey); + #[test] + fn can_create_pair() { + let seckey = hex!("4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae"); + let expected = hex!("4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae03fd8c74f795ced92064b86191cb2772b1e3a0947740aa0a5a6e379592471fd85b"); + let res = ext_secp_from_seed(&seckey); - assert_eq!(res[..], expected[..]); - } + assert_eq!(res[..], expected[..]); + } - #[test] - fn can_pub_compress_full() { - let pubkey = hex!("04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af"); - let expected = hex!("03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077"); - let res = ext_secp_pub_compress(&pubkey); + #[test] + fn can_pub_compress_full() { + let pubkey = hex!("04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af"); + let expected = hex!("03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077"); + let res = ext_secp_pub_compress(&pubkey); - assert_eq!(res[..], expected[..]); - } + assert_eq!(res[..], expected[..]); + } - #[test] - fn can_pub_expand_comp() { - let pubkey = hex!("03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077"); - let expected = hex!("04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af"); - let res = ext_secp_pub_expand(&pubkey); + #[test] + fn can_pub_expand_comp() { + let pubkey = hex!("03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077"); + let expected = hex!("04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af"); + let res = ext_secp_pub_expand(&pubkey); - assert_eq!(res[..], expected[..]); - } + assert_eq!(res[..], expected[..]); + } - #[test] - fn can_recover() { - let expected = hex!("028d13da15a02f3a70677339d51b14177ee9b49657662b35e56a9d9dee17db1d30"); - let sig = hex!("7505f2880114da51b3f5d535f8687953c0ab9af4ab81e592eaebebf53b728d2b6dfd9b5bcd70fee412b1f31360e7c2774009305cb84fc50c1d0ff8034dfa5fff"); - let msg = hex!("a30b64ce1eedf409c8afb801d72c05234e64849ea538c15dd3c8cf4ffcf166c9"); - let res = ext_secp_recover(&msg, &sig, 0); + #[test] + fn can_recover() { + let expected = hex!("028d13da15a02f3a70677339d51b14177ee9b49657662b35e56a9d9dee17db1d30"); + let sig = hex!("7505f2880114da51b3f5d535f8687953c0ab9af4ab81e592eaebebf53b728d2b6dfd9b5bcd70fee412b1f31360e7c2774009305cb84fc50c1d0ff8034dfa5fff"); + let msg = hex!("a30b64ce1eedf409c8afb801d72c05234e64849ea538c15dd3c8cf4ffcf166c9"); + let res = ext_secp_recover(&msg, &sig, 0); - assert_eq!(res[..], expected[..]); - } + assert_eq!(res[..], expected[..]); + } - #[test] - fn can_sign() { - // JS expectation - doesn't match? - // let expected = hex!("df92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba8c4efa521361678d9b8582744d77aa4b8d886d7380b7808a683174afad9c4700300"); - let expected = hex!("df92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba83b105adec9e9872647a7d8bb28855b45e22805aea3d097953cbb1391f671d13e01"); - let seckey = hex!("4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae"); - let msg = hex!("68c731589a583d08b70861683b59ce3dd56284cb2f0da5b6cd83e6641dac3aab"); - let res = ext_secp_sign(&msg, &seckey); + #[test] + fn can_sign() { + // JS expectation - doesn't match? + // let expected = hex!("df92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba8c4efa521361678d9b8582744d77aa4b8d886d7380b7808a683174afad9c4700300"); + let expected = hex!("df92f73d9f060cefacf187b5414491cb992998ace017fa48839b5cda3e264ba83b105adec9e9872647a7d8bb28855b45e22805aea3d097953cbb1391f671d13e01"); + let seckey = hex!("4380de832af797688026ce24f85204d508243f201650c1a134929e5458b7fbae"); + let msg = hex!("68c731589a583d08b70861683b59ce3dd56284cb2f0da5b6cd83e6641dac3aab"); + let res = ext_secp_sign(&msg, &seckey); - assert_eq!(res[..], expected[..]); - } + assert_eq!(res[..], expected[..]); + } + + #[test] + #[should_panic(expected = "Non-normalized signature provided.")] + fn rejects_high_s_signature() { + // valid ECDSA signature with a high S value (non-normalized) + let sig = hex!( + "fa1b7a71500322d70bcd5b982c678c96c95f68893063e1621b8b85c867b80b1caae8c7c4f8b5c6d181e4c8898a3f4c329da0e2c21f6f80552a660e6c1d9fa201"); + let msg = hex!("9a3c9be3a14a94df8cda1c474c0e9f8b1b35b1d85d2122cb87e6eb3c518e3be3"); + + let _ = ext_secp_recover(&msg, &sig, 0); + } }