diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b0b90e0c..e9be21f3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,4 +4,5 @@ - Payload hash scalars are now big endian where previously they were little endian. In general this means that clients of the contract should no longer reverse their hashed payload before sending it to the MPC contract. - The sha256 scalar used to derive the epsilon value is now big endian where previously it was little endian. In general this means clients should no longer reverse the bytes generated when hashing the epsilon derivation path. These new derivation paths will result in all testnet keys being lost. +- The KDF function now uses Sha3 instead of Sha2 when hashing "{EPSILON_DERIVATION_PREFIX}{predecessor_id},{path}" diff --git a/chain-signatures/Cargo.lock b/chain-signatures/Cargo.lock index 0c39a8c12..2717cadc3 100644 --- a/chain-signatures/Cargo.lock +++ b/chain-signatures/Cargo.lock @@ -1753,6 +1753,7 @@ dependencies = [ "near-sdk", "serde", "serde_json", + "sha3", ] [[package]] diff --git a/chain-signatures/crypto-shared/Cargo.toml b/chain-signatures/crypto-shared/Cargo.toml index 5a2748595..4891be579 100644 --- a/chain-signatures/crypto-shared/Cargo.toml +++ b/chain-signatures/crypto-shared/Cargo.toml @@ -17,6 +17,7 @@ borsh = "1.3.0" near-account-id = "1" serde_json = "1" near-sdk = { version = "5.2.1", features = ["unstable"] } +sha3 = "0.10.8" [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { version = "0.2.12", features = ["custom"] } diff --git a/chain-signatures/crypto-shared/src/kdf.rs b/chain-signatures/crypto-shared/src/kdf.rs index 75ade7bd1..27590b3bb 100644 --- a/chain-signatures/crypto-shared/src/kdf.rs +++ b/chain-signatures/crypto-shared/src/kdf.rs @@ -3,10 +3,10 @@ use anyhow::Context; use k256::{ ecdsa::{RecoveryId, Signature, VerifyingKey}, elliptic_curve::{point::AffineCoordinates, sec1::ToEncodedPoint, CurveArithmetic}, - sha2::{Digest, Sha256}, Scalar, Secp256k1, SecretKey, }; use near_account_id::AccountId; +use sha3::{Digest, Sha3_256}; // Constant prefix that ensures epsilon derivation values are used specifically for // near-mpc-recovery with key derivation protocol vX.Y.Z. @@ -20,7 +20,7 @@ pub fn derive_epsilon(predecessor_id: &AccountId, path: &str) -> Scalar { // of the accound id in the trie key. We reuse the same constant to // indicate the end of the account id in derivation path. let derivation_path = format!("{EPSILON_DERIVATION_PREFIX}{},{}", predecessor_id, path); - let mut hasher = Sha256::new(); + let mut hasher = Sha3_256::new(); hasher.update(derivation_path); Scalar::from_bytes(&hasher.finalize()) } diff --git a/flake.nix b/flake.nix index 885f39aec..87a1cf47c 100644 --- a/flake.nix +++ b/flake.nix @@ -54,6 +54,7 @@ cargo-watch cargo-audit sccache + toxiproxy # TODO Add podman + docker image dependencies # TODO Add AWS-CLI and dummy credentials diff --git a/integration-tests/chain-signatures/Cargo.lock b/integration-tests/chain-signatures/Cargo.lock index ae5d03c3c..3dbbbdb4d 100644 --- a/integration-tests/chain-signatures/Cargo.lock +++ b/integration-tests/chain-signatures/Cargo.lock @@ -1837,6 +1837,7 @@ dependencies = [ "near-sdk", "serde", "serde_json", + "sha3", ] [[package]] diff --git a/integration-tests/chain-signatures/tests/actions/mod.rs b/integration-tests/chain-signatures/tests/actions/mod.rs index 3cb287267..9a0ee4545 100644 --- a/integration-tests/chain-signatures/tests/actions/mod.rs +++ b/integration-tests/chain-signatures/tests/actions/mod.rs @@ -120,6 +120,15 @@ pub async fn single_signature_rogue_responder( let mut mpc_pk_bytes = vec![0x04]; mpc_pk_bytes.extend_from_slice(&state.public_key.as_bytes()[1..]); + + // Useful for populating the "signatures_havent_changed" test's hardcoded values + // dbg!( + // hex::encode(signature.big_r.to_encoded_point(true).to_bytes()), + // hex::encode(signature.s.to_bytes()), + // hex::encode(&mpc_pk_bytes), + // hex::encode(&payload_hash), + // account.id(), + // ); assert_signature(account.id(), &mpc_pk_bytes, &payload_hash, &signature).await; Ok(()) @@ -347,23 +356,22 @@ pub async fn clear_toxics() -> anyhow::Result<()> { Ok(()) } +// This test hardcodes the output of the signing process and checks that everything verifies as expected +// If you find yourself changing the constants in this test you are likely breaking backwards compatibility #[tokio::test] -async fn test_signatures_verify() { - let big_r = "03d6d674dae94517646708cfde6e2e46a2e666e06b92eba19290eb0ca11d5e45dc"; - let s = "2a5f2bff1b8e7da4257d480c5610d0d2c35426ee12abb87ff9c3141fe448ab27"; - let mpc_key = "04cc5ed2a876b6fc54176bcde0805e469ac7eca43a97bfff90acd5babbef3a33b10d14fed35065a06a67b9a243169f33ab20bf9dab49cf6c1466a15349c011ca2b"; - let account_id = "dev-20240717130550-33209224232133.test.near"; +async fn signatures_havent_changed() { + let big_r = "03f13a99141ce0a4043a7c02afdec6d52f25c6b3de01967acc5cf4a3fa43801589"; + let s = "39e5631fcc06ffccf8469a3cdcdce0651ebafd998a4280ebbf5dc24a749c98fb"; + let mpc_key = "04b5695a882aeaf36bf3933e21911b5cbcceae7fd7cb424f3ea221c7e8d390aad4ad2c1a427faec960f22a5442739c0a04fd64ab7ce4c93980417bd3d1d8bc04ea"; + let account_id = "dev-20240719125040-80075249096169.test.near"; let payload_hash: [u8; 32] = - hex::decode("7be9d96ac6895be4c59e59bb67c015f28cb94669657ddb00e8aa063f62e18031") + hex::decode("49f32740939bfdcbd8d1786075df7aca384381ec203975c3a6c1fd80acddcd4c") .unwrap() .try_into() .unwrap(); let payload_hash_scalar = Scalar::from_bytes(&payload_hash); - println!("payload_hash: {payload_hash:?}"); - println!("payload_hash_scallar: {payload_hash_scalar:#?}"); - // Derive and convert user pk let mpc_pk = hex::decode(mpc_key).unwrap(); let mpc_pk = EncodedPoint::from_bytes(mpc_pk).unwrap(); @@ -396,12 +404,6 @@ async fn test_signatures_verify() { let signature = cait_sith::FullSignature:: { big_r, s }; - println!("R: {big_r:#?}"); - println!("r: {r:#?}"); - println!("y parity: {}", big_r_y_parity); - println!("s: {s:#?}"); - println!("epsilon: {derivation_epsilon:#?}"); - let multichain_sig = into_eth_sig( &user_pk, &signature.big_r, @@ -409,7 +411,6 @@ async fn test_signatures_verify() { payload_hash_scalar, ) .unwrap(); - println!("{multichain_sig:#?}"); // Check signature using cait-sith tooling let is_signature_valid_for_user_pk = signature.verify(&user_pk, &payload_hash_scalar);