Skip to content

Instantly share code, notes, and snippets.

@jmastr
Last active September 25, 2022 11:22
Show Gist options
  • Save jmastr/9ca67014944220b494bc5dc6a7fd9e38 to your computer and use it in GitHub Desktop.
Save jmastr/9ca67014944220b494bc5dc6a7fd9e38 to your computer and use it in GitHub Desktop.
Rust Implementation for Two Party MPC ECDSA
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);
}
}
[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"]}
@jmastr
Copy link
Author

jmastr commented Aug 23, 2022

Example output:

> rustup run nightly cargo build
   Compiling autocfg v1.1.0
...
    Finished dev [unoptimized + debuginfo] target(s) in 11.11s

> ./2p_mpc_ecdsa 
p PublicKey(407af85c12b5e7d6d9fe6b553a6a38752df19b6b3d82249c4daa8080da7531a0b8b64a6328e00af3d14c61b52f2dadbba5c584f3e5553c2f52ab9b2097ce94bc)
r "a03175da8080aa4d9c24823d6b9bf12d75386a3a556bfed9d6e7b5125cf87a40"
d1p RawPlaintext(18221851957347484108178172120881248348572946847930148885831520220767026078576)
ckey_1 RawCiphertext(399060200827978707329606941579279434037168331541223317457161945113629846492181927602936671109156423291659510192315310009359706251079662159099270025869013418522403813356566360582367904978831178319543614966354914948221123382192218345040466983570930990643216737356117955393507622810965110885866269283074066268583785093840887819243974716272842092125061364892379919670710732592280093736471591759046686162026375598753581611698888786550995108990000410998602904322546591312635268478155977331026107878888257665003171353286906242235471986364025944552063281705422127794447641716780265779582414109477734137337345084667209142482971676235291370447010316305469513006992411050145385355590716997788157800671221661245880606188716390549788875265223279194474777565202055557670359408997065579314951194813327151845957935523008253190477534945475912075850295128771350553469690351655015377383054762726528830139287818712409608103677852511556175017898067825306860229662692108890893510591479546909661607310951210000253402065930451383368149096911951375731565591820207902561414926390900778569411794210436990589031348805742801723279504608482411184474322366732276891312835137420754824061788245125145320487507136065760972831183400457285815324081113504679460759696722)
k2 102064124096856529074684186260070063013337384845577124757035364210734951860016
ckey_2 RawCiphertext(240095648808244573902616093366875496205895119185905878027895931628468575172129083607329396143588452601545049418614640486393752637589717400844389073955095667510117960882552128019289207421332123114843809893837257724751066638814534780317392749646940645405669949497830704281901950614054099255976601876033288212160044662206785627953165501909678532945358466832317054946657721620146918120272781743490002337302188265467907074265508296102134826222529202995493519939984840097218396328663954637592985841341108473471949639886242629354888492721590268264994731700195220186354186035742495263850388982066257077179691227583085362773909699634221755301358452778203599365480618473632182809510078630298000108627256404600293476003149563612207687177212251042113105547782782915014806245205982488678237092266989294276478970445833659916515292282318018213644382717947125890769709133736290517448066325443665709929480899107371452992544419104026737030754929193056345681939228008745322462260573617388419214380114861924991130514160750514680036683208443733321303674826223310795817746201871059769356598923360472771090726239119255031558235434418419431848775478862607343555107216620802579173566739569805675798458976839238774221837585288624783859217762925876892281903142)
ckey_3 82936399251316637083333561082947934974606982695980380698691375154000364628281489787823860950972612173752667784822453859185837810721800773337292517888598836622533142213775179016187877910231207596774960993974407637574428695685861120
ckey_4 RawCiphertext(370212167202691033294308856256889061860761428975083656119771804590252305250903396084126385821798755507809963728156358427415546277436731297883715475268183557579071681982519876525687554616623299907145045834953269402466457064618689605112100460752405709416197140288640243535258063945580835700450256367803037429880158082755678132538001336314560355033234382362037075417644777179795531254923040855459198708656499323529292333349660387516953026671298413845680702111886836130056306838094764518588933607537679400027884864437136773803305678931746850494980905899443311004820298070773121525645999009853010633615177071271994241503771609067521413591199922103969808411453582608929621904511071519534681739278182426022164534854785390397387739693555935382156901402166671569496652983481249187882902848852971331611896296125781635362668090702492758015373273421379134719037612500664336542977301976796351026688801958397060938171379902993474959920252625744987520580540942998680069799653857385952013321170429070106925539169202466049833323149347361660389242091592838966387417678428436360394040727574873745110726396563179160394482024175455639102150746999088492977058251427906280313146494008910125928485415616439766754562892378458695730409786265149542635436678128)
ciphertext RawCiphertext(73088544244942150558688374613638862007800999723719445604040706846878177666520166396646347368077263828885849289671016099154544129314982774679545030657753463544982622747258592132992345127788137362515350862250777414735536104264133150009326075906602080644394970061819068384506106320315603678015860740475953332399394857810350398560005129502158024637957568674420615962438040079248008757661719948931622824284673066788845849960777812990507663113910190803915789421059306416742623640226164482808038145765557649222079098185283981216829783531680516472284862165027273921998494512506524313305904584784098654196399506290113768077172232016571176027535543678905144086797519416082613862818029259651119298884880531101517176889559658478492046772036888952820166714253285774620313425358979258077609885515582011754600027731576450305265087456249153601519851780997478066720267333493582648250925706912769354065195267149154722329785416159013829415445520913154968513348870840739793681271709389997940915321384980558264483324083603308330338138076094036655759522081189465725629544757105087641509684069327334010437213608934775838663012143717104792012676508968496094049764311463821988761918859539819147645433575243702104492930211832785893117500077340246631467119901)
r "a03175da8080aa4d9c24823d6b9bf12d75386a3a556bfed9d6e7b5125cf87a40"
s "f89570c3c5d241f26a39fcdcb086925920f6fe3deb7f44a5321a6fce4df031ab"

And the unit test:

> rustup run nightly cargo test 
...

running 1 test
test tests::twopmpc ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.08s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment