Last active
September 25, 2022 11:22
-
-
Save jmastr/9ca67014944220b494bc5dc6a7fd9e38 to your computer and use it in GitHub Desktop.
Rust Implementation for Two Party MPC ECDSA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate pad; | |
extern crate paillier; | |
extern crate secp256k1; | |
use paillier::*; | |
use pad::{PadStr, Alignment}; | |
const Z: &str = "c493cb789beba59cd1db18ff5e5ccf30a39e63d49b03d86f2d5cb5ae2d5e2e44"; | |
fn twopmpc(d1: secp256k1::SecretKey, d2: secp256k1::SecretKey, k1: secp256k1::scalar::Scalar, k2: secp256k1::scalar::Scalar) -> (String, String) { | |
let curve = secp256k1::Secp256k1::new(); | |
let n = BigInt::from_str_radix(hex::encode(secp256k1::constants::CURVE_ORDER).as_str(), 16).unwrap(); | |
let nmt = BigInt::from_str_radix(hex::encode(secp256k1::constants::CURVE_ORDER).as_str(), 16).unwrap() - 2; | |
let generator = secp256k1::PublicKey::from_x_only_public_key(secp256k1::XOnlyPublicKey::from_slice(&secp256k1::constants::GENERATOR_X).unwrap(), secp256k1::Parity::Even); | |
let p = (generator.mul_tweak(&curve, &k1)).unwrap().mul_tweak(&curve, &k2).unwrap(); | |
println!("p {:?}", p); | |
let r = &p.serialize()[1..33]; | |
println!("r {:?}", hex::encode(r)); | |
let d1p = RawPlaintext::from(BigInt::from_str_radix(hex::encode(d1.secret_bytes()).as_str(), 16).unwrap()); | |
println!("d1p {:?}", d1p); | |
let (pllr_pub_1, pllr_priv_1) = Paillier::keypair().keys(); | |
let ckey_1 = Paillier::encrypt(&pllr_pub_1, d1p); | |
println!("ckey_1 {:?}", ckey_1); | |
let k2 = BigInt::from_str_radix(hex::encode(k2.to_be_bytes()).as_str(), 16).unwrap(); | |
println!("k2 {:?}", k2); | |
let z = BigInt::from_str_radix(Z, 16).unwrap(); | |
let ckey_2 = Paillier::encrypt(&pllr_pub_1, RawPlaintext::from(BigInt::powm(&k2, &nmt, &n) * z.clone())); | |
println!("ckey_2 {:?}", ckey_2); | |
let ckey_3 = BigInt::powm(&k2, &nmt, &n) * BigInt::from_str_radix(hex::encode(r).as_str(), 16).unwrap() * BigInt::from_str_radix(hex::encode(d2.secret_bytes()).as_str(), 16).unwrap(); | |
println!("ckey_3 {:?}", ckey_3); | |
let ckey_4 = Paillier::mul(&pllr_pub_1, ckey_1, RawPlaintext::from(ckey_3)); | |
println!("ckey_4 {:?}", ckey_4); | |
let ciphertext = Paillier::add(&pllr_pub_1, ckey_2, ckey_4); | |
println!("ciphertext {:?}", ciphertext); | |
let plaintext = Paillier::decrypt(&pllr_priv_1, ciphertext); | |
println!("plaintext {:?}", plaintext); | |
let k1 = BigInt::from_str_radix(hex::encode(k1.to_be_bytes()).as_str(), 16).unwrap(); | |
let s = (BigInt::powm(&k1, &nmt, &n) * BigInt::from(plaintext)) % n.clone(); | |
let r = hex::encode(r); | |
let s = s.to_str_radix(16); | |
assert_eq!(validation(d1, d2, r.clone(), s.clone()), r); | |
return (r, s); | |
} | |
fn validation(d1: secp256k1::SecretKey, d2: secp256k1::SecretKey, r: String, s: String) -> String{ | |
let curve = secp256k1::Secp256k1::new(); | |
let n = BigInt::from_str_radix(hex::encode(secp256k1::constants::CURVE_ORDER).as_str(), 16).unwrap(); | |
let nmt = BigInt::from_str_radix(hex::encode(secp256k1::constants::CURVE_ORDER).as_str(), 16).unwrap() - 2; | |
let generator = secp256k1::PublicKey::from_x_only_public_key(secp256k1::XOnlyPublicKey::from_slice(&secp256k1::constants::GENERATOR_X).unwrap(), secp256k1::Parity::Even); | |
let h = BigInt::from_str_radix(Z, 16).unwrap(); | |
let d = (BigInt::from_str_radix(hex::encode(d1.secret_bytes()).as_str(), 16).unwrap() * BigInt::from_str_radix(hex::encode(d2.secret_bytes()).as_str(), 16).unwrap()) % n.clone(); | |
let d = BigInt::to_str_radix(&d, 16).pad(64, '0', Alignment::Right, true); | |
// h = z | |
// d = d1.d * d2.d | |
// pubkey = d * G | |
// s1 = pow(s, n-2, n) | |
// Rh = ((h * s1) * G) + (r * s1) * pubkey | |
// r = Rh.x | |
let mut d_bytes = [0u8; 32]; | |
assert_eq!(hex::decode_to_slice(d, &mut d_bytes as &mut [u8]), Ok(())); | |
let d = secp256k1::scalar::Scalar::from_be_bytes(d_bytes).unwrap(); | |
let sn = BigInt::from_str_radix(s.as_str(), 16).unwrap(); | |
let s1 = BigInt::powm(&sn, &nmt, &n); | |
let hs1 = (h * s1.clone()) % n.clone(); | |
let mut hs1_bytes = [0u8; 32]; | |
assert_eq!(hex::decode_to_slice(BigInt::to_str_radix(&hs1, 16), &mut hs1_bytes as &mut [u8]), Ok(())); | |
let hs1 = secp256k1::scalar::Scalar::from_be_bytes(hs1_bytes).unwrap(); | |
let hs1g = generator.mul_tweak(&curve, &hs1).unwrap(); | |
let pubkey = generator.mul_tweak(&curve, &d).unwrap(); | |
let rn = BigInt::from_str_radix(r.as_str(), 16).unwrap(); | |
let rs1 = (rn * s1.clone()) % n.clone(); | |
let mut rs1_bytes = [0u8; 32]; | |
assert_eq!(hex::decode_to_slice(BigInt::to_str_radix(&rs1, 16), &mut rs1_bytes as &mut [u8]), Ok(())); | |
let rs1 = secp256k1::scalar::Scalar::from_be_bytes(rs1_bytes).unwrap(); | |
let rs1pubkey = pubkey.mul_tweak(&curve, &rs1).unwrap(); | |
let rh = hs1g.combine(&rs1pubkey).unwrap(); | |
let rh = hex::encode(&rh.serialize()[1..33]); | |
return rh; | |
} | |
fn main() { | |
let d1 = secp256k1::SecretKey::new(&mut secp256k1::rand::thread_rng()); | |
let d2 = secp256k1::SecretKey::new(&mut secp256k1::rand::thread_rng()); | |
let k1 = secp256k1::scalar::Scalar::random(); | |
let k2 = secp256k1::scalar::Scalar::random(); | |
let (r, s) = twopmpc(d1, d2, k1, k2); | |
println!("r {:?}", r); | |
println!("s {:?}", s); | |
} | |
#[cfg(test)] | |
mod tests { | |
#[test] | |
fn twopmpc() { | |
let d1 = secp256k1::SecretKey::from_slice(&[ | |
0xa0, 0xfb, 0xfa, 0x4e, 0xf9, 0x89, 0xa1, 0xb9, | |
0xfe, 0xae, 0x70, 0x6e, 0xe3, 0xa4, 0xc9, 0x64, | |
0x8d, 0x16, 0x9d, 0x68, 0x85, 0xef, 0xbd, 0x5c, | |
0x5f, 0xd9, 0x25, 0x5b, 0xd9, 0xda, 0x75, 0x7b | |
]).expect("32 bytes, within curve order"); | |
let d2 = secp256k1::SecretKey::from_slice(&[ | |
0xb9, 0x76, 0x9b, 0x12, 0x82, 0x30, 0xe5, 0xf5, | |
0x1b, 0x20, 0x05, 0x39, 0x41, 0x02, 0xa9, 0x1a, | |
0x71, 0xfe, 0xb6, 0xc9, 0x94, 0x14, 0xeb, 0xcf, | |
0xc1, 0xc5, 0x06, 0x2f, 0x3d, 0x6d, 0xe0, 0x8c | |
]).expect("32 bytes, within curve order"); | |
let k1 = secp256k1::scalar::Scalar::from_be_bytes([ | |
0x61, 0xba, 0xf5, 0xb6, 0x39, 0x2b, 0x0c, 0x19, | |
0x88, 0x04, 0x0d, 0x5f, 0xac, 0x53, 0xd3, 0xba, | |
0x6f, 0xd0, 0x8f, 0xe7, 0xaf, 0xdc, 0x25, 0x88, | |
0x3a, 0x05, 0x5f, 0x6f, 0x88, 0xe1, 0x82, 0x22 | |
]).expect("32 bytes, within curve order"); | |
let k2 = secp256k1::scalar::Scalar::from_be_bytes([ | |
0xc2, 0xc3, 0xaa, 0x9e, 0x81, 0x0f, 0xe5, 0x0e, | |
0x63, 0x46, 0x58, 0xd9, 0xc0, 0xe8, 0xe4, 0xff, | |
0x4a, 0xd6, 0x68, 0xe6, 0xf5, 0xd0, 0xe0, 0x62, | |
0x99, 0xf7, 0x8e, 0x43, 0xd2, 0xec, 0xbd, 0x2e | |
]).expect("32 bytes, within curve order"); | |
let (r, s) = super::twopmpc(d1, d2, k1, k2); | |
assert_eq!(r, "f4fa1a03e184ace562ea6eeffa654726be98287d5c4325a52f42903d9d27870c"); | |
assert_eq!(s, "30433f18d8ebcb3ff67e836be510fe7fce07e8ad299fef9f7db7cd7ca90ec147"); | |
let rh = super::validation(d1, d2, r.clone(), s.clone()); | |
assert_eq!(rh, r); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "rs-2pmpc" | |
version = "0.0.1" | |
edition = "2018" | |
[[bin]] | |
name = "2p_mpc_ecdsa" | |
path = "2pMPCpy/src/2pMPC_ecdsa.rs" | |
[dependencies] | |
hex = {version="0.4.3"} | |
pad = {version="0.1.6"} | |
paillier = {version="0.2.0"} | |
secp256k1 = {version="0.24.0", features = ["rand", "rand-std", "bitcoin_hashes"]} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example output:
And the unit test: