Skip to content

Commit

Permalink
Update epsilon paths, use ChainId (#196)
Browse files Browse the repository at this point in the history
* update epsilon paths

* use chainIds in derivation path

* updated test variables (breaking!)
  • Loading branch information
volovyks authored Feb 12, 2025
1 parent 1f20f56 commit 44b2be7
Show file tree
Hide file tree
Showing 9 changed files with 44 additions and 34 deletions.
4 changes: 2 additions & 2 deletions chain-signatures/contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use errors::{
use k256::elliptic_curve::sec1::ToEncodedPoint;
use k256::Scalar;
use mpc_crypto::{
derive_epsilon, derive_key, kdf::check_ec_signature, near_public_key_to_affine_point,
derive_epsilon_near, derive_key, kdf::check_ec_signature, near_public_key_to_affine_point,
types::SignatureResponse, ScalarExt as _,
};
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
Expand Down Expand Up @@ -207,7 +207,7 @@ impl VersionedMpcContract {
predecessor: Option<AccountId>,
) -> Result<PublicKey, Error> {
let predecessor = predecessor.unwrap_or_else(env::predecessor_account_id);
let epsilon = derive_epsilon(&predecessor, &path);
let epsilon = derive_epsilon_near(&predecessor, &path);
let derived_public_key =
derive_key(near_public_key_to_affine_point(self.public_key()?), epsilon);
let encoded_point = derived_public_key.to_encoded_point(false);
Expand Down
4 changes: 2 additions & 2 deletions chain-signatures/contract/src/primitives.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use k256::Scalar;
use mpc_crypto::{derive_epsilon, SerializableScalar};
use mpc_crypto::{derive_epsilon_near, SerializableScalar};
use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize};
use near_sdk::serde::{Deserialize, Serialize};
use near_sdk::{AccountId, BorshStorageKey, CryptoHash, NearToken, PublicKey};
Expand Down Expand Up @@ -42,7 +42,7 @@ pub struct ContractSignatureRequest {

impl SignatureRequest {
pub fn new(payload_hash: Scalar, predecessor_id: &AccountId, path: &str) -> Self {
let epsilon = derive_epsilon(predecessor_id, path);
let epsilon = derive_epsilon_near(predecessor_id, path);
let epsilon = SerializableScalar { scalar: epsilon };
let payload_hash = SerializableScalar {
scalar: payload_hash,
Expand Down
4 changes: 2 additions & 2 deletions chain-signatures/contract/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use mpc_contract::primitives::{
use mpc_contract::update::UpdateId;
use mpc_crypto::kdf::{check_ec_signature, derive_secret_key};
use mpc_crypto::{
derive_epsilon, derive_key, ScalarExt as _, SerializableAffinePoint, SerializableScalar,
derive_epsilon_near, derive_key, ScalarExt as _, SerializableAffinePoint, SerializableScalar,
SignatureResponse,
};
use near_workspaces::network::Sandbox;
Expand Down Expand Up @@ -161,7 +161,7 @@ pub async fn create_response(
let (digest, scalar_hash, payload_hash) = process_message(msg).await;
let pk = sk.public_key();

let epsilon = derive_epsilon(predecessor_id, path);
let epsilon = derive_epsilon_near(predecessor_id, path);
let derived_sk = derive_secret_key(sk, epsilon);
let derived_pk = derive_key(pk.into(), epsilon);
let signing_key = k256::ecdsa::SigningKey::from(&derived_sk);
Expand Down
19 changes: 13 additions & 6 deletions chain-signatures/crypto/src/kdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ use near_account_id::AccountId;
use sha3::{Digest, Keccak256, Sha3_256};

// Constant prefix that ensures epsilon derivation values are used specifically for
// near-mpc-recovery with key derivation protocol vX.Y.Z.
const EPSILON_DERIVATION_PREFIX: &str = "near-mpc-recovery v0.1.0 epsilon derivation:";
// Sig.Network with key derivation protocol vX.Y.Z.
const EPSILON_DERIVATION_PREFIX: &str = "sig.network v1.0.0 epsilon derivation";

pub fn derive_epsilon(predecessor_id: &AccountId, path: &str) -> Scalar {
const CHAIN_ID_NEAR: &str = "0x18d";
pub fn derive_epsilon_near(predecessor_id: &AccountId, path: &str) -> Scalar {
// TODO: Use a key derivation library instead of doing this manually.
// https://crates.io/crates/hkdf might be a good option?
//
Expand All @@ -21,16 +22,22 @@ pub fn derive_epsilon(predecessor_id: &AccountId, path: &str) -> Scalar {
// indicate the end of the account id in derivation path.
// Do not reuse this hash function on anything that isn't an account
// ID or it'll be vunerable to Hash Melleability/extention attacks.
let derivation_path = format!("{EPSILON_DERIVATION_PREFIX}{},{}", predecessor_id, path);
let derivation_path = format!(
"{EPSILON_DERIVATION_PREFIX},{CHAIN_ID_NEAR},{},{}",
predecessor_id, path
);
let mut hasher = Sha3_256::new();
hasher.update(derivation_path);
let hash: [u8; 32] = hasher.finalize().into();
Scalar::from_non_biased(hash)
}

const EPSILON_DERIVATION_PREFIX_ETH: &str = "near-mpc-recovery v0.2.0 epsilon derivation:";
const CHAIN_ID_ETHEREUM: &str = "0x1";
pub fn derive_epsilon_eth(requester: String, path: &str) -> Scalar {
let derivation_path = format!("{EPSILON_DERIVATION_PREFIX_ETH}{},{}", requester, path);
let derivation_path = format!(
"{EPSILON_DERIVATION_PREFIX},{CHAIN_ID_ETHEREUM},{},{}",
requester, path
);
let mut hasher = Keccak256::new();
hasher.update(derivation_path);
let hash: [u8; 32] = hasher.finalize().into();
Expand Down
2 changes: 1 addition & 1 deletion chain-signatures/crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub mod types;

use k256::elliptic_curve::sec1::FromEncodedPoint;
use k256::EncodedPoint;
pub use kdf::{derive_epsilon, derive_key, x_coordinate};
pub use kdf::{derive_epsilon_near, derive_key, x_coordinate};
pub use types::{
PublicKey, ScalarExt, SerializableAffinePoint, SerializableScalar, SignatureResponse,
};
Expand Down
5 changes: 3 additions & 2 deletions chain-signatures/node/src/indexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::protocol::Chain::NEAR;
use crate::protocol::{Chain, SignRequest};
use crate::storage::app_data_storage::AppDataStorage;
use k256::Scalar;
use mpc_crypto::{derive_epsilon, ScalarExt};
use mpc_crypto::{derive_epsilon_near, ScalarExt};
use near_account_id::AccountId;
use near_lake_framework::{Lake, LakeBuilder, LakeContext};
use near_lake_primitives::actions::ActionMetaDataExt;
Expand Down Expand Up @@ -233,7 +233,8 @@ async fn handle_block(
);
continue;
};
let epsilon = derive_epsilon(&action.predecessor_id(), &arguments.request.path);
let epsilon =
derive_epsilon_near(&action.predecessor_id(), &arguments.request.path);
tracing::info!(
receipt_id = %receipt_id,
caller_id = receipt.predecessor_id().to_string(),
Expand Down
26 changes: 13 additions & 13 deletions integration-tests/src/actions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use k256::{AffinePoint, EncodedPoint, Scalar, Secp256k1};
use mpc_contract::errors::SignError;
use mpc_contract::primitives::SignRequest;
use mpc_crypto::ScalarExt;
use mpc_crypto::{derive_epsilon, derive_key};
use mpc_crypto::{derive_epsilon_near, derive_key};
use near_crypto::InMemorySigner;
use near_fetch::ops::AsyncTransactionStatus;
use near_fetch::ops::Function;
Expand Down Expand Up @@ -110,7 +110,7 @@ pub async fn validate_signature(
) -> anyhow::Result<()> {
let mpc_point = EncodedPoint::from_bytes(mpc_pk_bytes).unwrap();
let mpc_pk = AffinePoint::from_encoded_point(&mpc_point).unwrap();
let epsilon = derive_epsilon(account_id, "test");
let epsilon = derive_epsilon_near(account_id, "test");
let user_pk = derive_key(mpc_pk, epsilon);
signature
.verify(
Expand Down Expand Up @@ -290,7 +290,7 @@ mod tests {
use k256::elliptic_curve::point::AffineCoordinates;
use k256::elliptic_curve::ProjectivePoint;
use k256::{AffinePoint, EncodedPoint, Scalar};
use mpc_crypto::{derive_epsilon, derive_key, ScalarExt as _};
use mpc_crypto::{derive_epsilon_near, derive_key, ScalarExt as _};

use super::{public_key_to_address, recover, x_coordinate};

Expand All @@ -300,12 +300,12 @@ mod tests {
fn signatures_havent_changed() {
const CHAIN_ID_ETH: u64 = 31337;

let big_r = "03f13a99141ce0a4043a7c02afdec6d52f25c6b3de01967acc5cf4a3fa43801589";
let s = "39e5631fcc06ffccf8469a3cdcdce0651ebafd998a4280ebbf5dc24a749c98fb";
let mpc_key = "04b5695a882aeaf36bf3933e21911b5cbcceae7fd7cb424f3ea221c7e8d390aad4ad2c1a427faec960f22a5442739c0a04fd64ab7ce4c93980417bd3d1d8bc04ea";
let account_id = "dev-20240719125040-80075249096169.test.near";
let big_r = "029b1b94bf4511b1a25986ba858cfa0fbdd5e4077c02e1d1102a194389b1f72df7";
let s = "25f3494bb7e7b3349a4b4d939d3e5ae1787a0863e4f698fb8ed2d3e11c195035";
let mpc_key = "045b4fa179e005361fd858f8a6f896d7afc23a53d3f95d6566a88cde954e7b2f1cb77c554705c35d4ffced67aeafbcda46d9d89d6f200c3a3d109f92872863b3dc";
let account_id = "dev-20250212213501-93636560094065.test.near";
let payload_hash: [u8; 32] =
hex::decode("49f32740939bfdcbd8d1786075df7aca384381ec203975c3a6c1fd80acddcd4c")
hex::decode("835b9f469b36126284df2e06ecab9482cf495413ab9275faaafb2d40d79cf7bb")
.unwrap()
.try_into()
.unwrap();
Expand All @@ -318,7 +318,7 @@ mod tests {
let mpc_pk = AffinePoint::from_encoded_point(&mpc_pk).unwrap();

let account_id = account_id.parse().unwrap();
let derivation_epsilon: k256::Scalar = derive_epsilon(&account_id, "test");
let derivation_epsilon: k256::Scalar = derive_epsilon_near(&account_id, "test");
let user_pk: AffinePoint = derive_key(mpc_pk, derivation_epsilon);
let user_pk_y_parity = match user_pk.y_is_odd().unwrap_u8() {
0 => secp256k1::Parity::Even,
Expand Down Expand Up @@ -455,7 +455,7 @@ mod tests {
) -> Result<(), &'static str> {
// let z: Scalar = Scalar::reduce_bytes(z);
let z =
<Scalar as Reduce<<k256::Secp256k1 as k256::elliptic_curve::Curve>::Uint>>::reduce_bytes(z);
<Scalar as Reduce<<k256::Secp256k1 as k256::elliptic_curve::Curve>::Uint>>::reduce_bytes(z);
let (r, s) = sig.split_scalars();
let s_inv = *s.invert_vartime();
let u1 = z * s_inv;
Expand All @@ -476,9 +476,9 @@ mod tests {
// println!("------------- verify_prehashed[end] -------------");

let reduced =
<Scalar as Reduce<<k256::Secp256k1 as k256::elliptic_curve::Curve>::Uint>>::reduce_bytes(
&x,
);
<Scalar as Reduce<<k256::Secp256k1 as k256::elliptic_curve::Curve>::Uint>>::reduce_bytes(
&x,
);

//println!("reduced {reduced:#?}");

Expand Down
10 changes: 6 additions & 4 deletions integration-tests/src/actions/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use k256::{Scalar, Secp256k1};
use mpc_contract::errors;
use mpc_contract::primitives::{SignRequest, SignatureRequest};
use mpc_crypto::{
derive_epsilon, ScalarExt as _, SerializableAffinePoint, SerializableScalar, SignatureResponse,
derive_epsilon_near, ScalarExt as _, SerializableAffinePoint, SerializableScalar,
SignatureResponse,
};
use near_crypto::InMemorySigner;
use near_fetch::ops::AsyncTransactionStatus;
Expand Down Expand Up @@ -162,11 +163,12 @@ impl SignAction<'_> {
mpc_pk_bytes.extend_from_slice(&state.public_key.as_bytes()[1..]);

// Useful for populating the "signatures_havent_changed" test's hardcoded values
// dbg!(
// tracing::warn!(
// "ref_string: big_r={}, s={}, mpc_pk_bytes={}, payload_hash={}, account_id={}",
// 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),
// hex::encode(payload_hash),
// account.id(),
// );
actions::validate_signature(account.id(), &mpc_pk_bytes, payload_hash, &signature).await?;
Expand Down Expand Up @@ -238,7 +240,7 @@ impl SignAction<'_> {
public_key: rogue.secret_key().public_key().clone().into(),
secret_key: rogue.secret_key().to_string().parse()?,
};
let epsilon = derive_epsilon(predecessor, &self.path);
let epsilon = derive_epsilon_near(predecessor, &self.path);

let request = SignatureRequest {
payload_hash: Scalar::from_bytes(payload_hash).unwrap().into(),
Expand Down
4 changes: 2 additions & 2 deletions integration-tests/tests/cases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use k256::elliptic_curve::point::AffineCoordinates;
use k256::Secp256k1;
use mpc_contract::config::Config;
use mpc_contract::update::ProposeUpdateArgs;
use mpc_crypto::{self, derive_epsilon, derive_key, x_coordinate, ScalarExt};
use mpc_crypto::{self, derive_epsilon_near, derive_key, x_coordinate, ScalarExt};
use mpc_node::kdf::into_eth_sig;
use mpc_node::protocol::presignature::{Presignature, PresignatureId, PresignatureManager};
use mpc_node::protocol::triple::{Triple, TripleManager};
Expand Down Expand Up @@ -102,7 +102,7 @@ async fn test_key_derivation() -> anyhow::Result<()> {
nodes.wait().signable().await?;
let outcome = nodes.sign().path(hd_path).await?;

let derivation_epsilon = derive_epsilon(outcome.account.id(), hd_path);
let derivation_epsilon = derive_epsilon_near(outcome.account.id(), hd_path);
let user_pk = derive_key(mpc_pk, derivation_epsilon);
let multichain_sig = into_eth_sig(
&user_pk,
Expand Down

0 comments on commit 44b2be7

Please sign in to comment.