Skip to content

Commit

Permalink
partial sig verify and automatas
Browse files Browse the repository at this point in the history
  • Loading branch information
rdubois-crypto committed Nov 29, 2024
1 parent c728c2c commit 389658b
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 68 deletions.
33 changes: 32 additions & 1 deletion src/libMPC/SCL_Musig2.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,38 @@ Psign(secnonce, sk, session_ctx){
}

/********************************************************************************************/
/* VERIFICATION*/
/* VERIFICATIONS*/
/********************************************************************************************/

//verify one of the partial signature provided by a participant
Psig_verify(psig, pubnonce, pk, session_ctx){
let sessionV=this.Get_session_values(session_ctx);//(Q, gacc, tacc, b, R, e)
let s = int_from_bytes(psig);
let R_s1 = this.curve.PointDecompress(pubnonce.slice(0,this.RawBytesSize));
let R_s2 = this.curve.PointDecompress(pubnonce.slice(this.RawBytesSize,2*this.RawBytesSize));

let Re_s_ =R_s1.add(R_s2.multiply(b));

let Re_s=this.curve.PointCompressXonly(Re_s_);//forced to even point


a=key_agg_coeff(session_ctx[1], pk);
let g=BigInt(1);
if(has_even_y(Q)==false)
g=secp256k1.CURVE.n - g;//n-1
let P=ProjectivePoint.fromHex(pk);//partial input public key

g=(g*gacc) % this.order;

let G= secp256k1.ProjectivePoint.BASE;
let P1 = (G.multiply(s));
let P2=Re_s.add(P.multiply((e*a*g)%this.order));


return (P1.equals(P2));
}


//beware that this function take as input a msb representation of pubkey and signature
//key is assumed to be even
Schnorr_verify(msg, pubkey, sig){
Expand Down Expand Up @@ -464,6 +494,7 @@ Psign(secnonce, sk, session_ctx){
}



}
/********************************************************************************************/
/* END OF CLASS MUSIG2 */
Expand Down
256 changes: 190 additions & 66 deletions src/libMPC/SCL_atomic_swaps.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import { randomBytes } from 'crypto'; // Use Node.js's crypto module



/********************************************************************************************/
/* ATOMIC SWAP PRIMITIVES */
/********************************************************************************************/

//the Alice adaptator signature
//sk_A is Alice secret Key
//rA is the secnonce
Expand Down Expand Up @@ -126,114 +130,234 @@ export function atomic_example(){

}

export class SCL_Atomic_Swap
{
constructor(curve) {
this.signer=new SCL_Musig2(curve);

this.curve=signer.curve;
}

Psign_adapt(psig, t){
function Psign_adapt(psig, t){


let sprime=(int_from_bytes(psig)+t ) % this.curve.order;
let G= this.curve.GetBase(t);

return [sprime, G.multiply(t)];

return sprime;
}

Untweak(t, psigA_adapt, psigB){
function Untweak(t, psigA_adapt, psigB){
const sABp=partial_sig_agg([psigA_adapt, psigB]);

const sAB= (sABp - t)% secp256k1.CURVE.n;

return sAB;
}

}


function test_full_atomic_session(curve){
const swapper= new SCL_Atomic_Swap(curve);
const signer = swapper.signer;
/********************************************************************************************/
/* INITIATOR AUTOMATA*/
/********************************************************************************************/
export class SCL_Atomic_Initiator{

constructor(curve, pubkey, sk) {

this.signer=new SCL_Musig2(curve);
this.pubkey=pubkey;
this.sk=sk;
this.state="idle";

console.log("/*************************** ");
console.log("Test full Atomic session on curve", Curve);
this.nonceA1=0;
this.nonceA2=0;

this.nonceB1=0;
this.nonceB2=0;

this.t=0;

this.tx1=0;
this.tx2=0;
this.tG=0;
}

console.log(" -Generate random keys");
ResetSession(){
this.state="idle";

const sk1=signer.curve.Get_Random_privateKey();//this provides a 32 bytes array
const sk2=signer.curve.Get_Random_privateKey();
this.nonceA1=0;
this.nonceA2=0;

console.log("sk1=",sk1 );
console.log("sk2=",sk2 );
let seckeys=[sk1, sk2];
this.nonceB1=0;
this.nonceB2=0;

}

const pubK1=signer.IndividualPubKey_array(sk1);
const pubK2=signer.IndividualPubKey_array(sk2);
InitSession(tx1, tx2)
{

console.log("pubK1=",pubK1 );
console.log("pubK2=",pubK2 );
this. nonceA1= signer.Nonce_gen(this.sk, this.pubkey, x_aggpk, tx1, extra_in1);
this. nonceA2= signer.Nonce_gen(this.sk, this.pubkey, x_aggpk, tx2, extra_in2);
this.tx1=tx1;
this.tx2=tx2;

const pubkeys=[pubK1, pubK2];
let Message_I1=[tx1, tx2, nonceA1[1], nonceA2[1]];

let aggpk = signer.Key_agg(pubkeys)[0];//here aggpk is a 32 or 33 bytes compressed public key
let x_aggpk=signer.curve.ForceXonly(aggpk);//x-only version for noncegen, allways 32
this.state="waitR1"
return Message_I1;//this message is broadcast offchain
}

console.log("Aggregated Pubkey:", aggpk);
PartialSign_Tweaked(Message_R1){
let Message_I2=[];

const msg1=Buffer.from("Unlock 1strkBTC on Starknet to Alice",'utf-8');
console.log("msg1=",msg1);
const msg2=Buffer.from("Unlock 1WBTC on Ethereum to Bob",'utf-8');
console.log("msg2=",msg2);
this.nonceB1=Message_R1[2];
this.nonceB2=Message_R1[3];

let aggnonce1 = signer.Nonce_agg([nonceA1[1].toString('hex'), nonceB1.toString('hex')]);
let aggnonce2 = signer.Nonce_agg([nonceA2[1].toString('hex'), nonceB2.toString('hex')]);

const session_ctx1=[aggnonce1, [this.pubkey, Message_R1[4]], [], [], this.tx1];
const session_ctx2=[aggnonce2, [this.pubkey, Message_R1[4]], [], [], this.tx2];

//nonce generation
console.log(" -Generate random Nonces with commitment", aggpk);
//diversification chain
const extra_in1= Buffer.from(randomBytes(32));
const extra_in2= Buffer.from(randomBytes(32));


let nonceA1= signer.Nonce_gen(seckeys[0], pubkeys[0], x_aggpk, msg1, extra_in1);
let nonceB1= signer.Nonce_gen(seckeys[1], pubkeys[1], x_aggpk, msg2, extra_in1);
let psigI1=signer.Psign(nonceA1[0], this.sk, session_ctx1);
let psigI2=signer.Psign(nonceA2[0], this.sk, session_ctx2);


this.t=int_from_bytes(signer.Get_Random_privateKey());
let G= this.curve.GetBase(t);
this.tG=G.multiply(t);


let psigI1p=Psign_adapt(psigI1,t)
let psigI2p=Psign_adapt(psigI2,t)

Message_I2=[psigI1p, psigI2p, this.tG];

this.state="waitR2"
return Message_I2;//this message is broadcast offchain
}

//here it is assumed that Initiator checked that deposit has been made and locked by signature of tx1 on Chain1
FinalUnlock(Message_R2){
let Message_I3=[];


let nonceA2= signer.Nonce_gen(seckeys[0], pubkeys[0], x_aggpk, msg1, extra_in2);
let nonceB2= signer.Nonce_gen(seckeys[1], pubkeys[1], x_aggpk, msg2, extra_in2);
this.state="idle";
return Message_I3;//this message is broadcast onchain to unlock initiator exit liquidity

}


}

/********************************************************************************************/
/* RESPONDER AUTOMATA*/
/********************************************************************************************/
export class SCL_Atomic_Responder{

//aggregation of public nonces
let aggnonce1 = signer.Nonce_agg([nonceA1[1].toString('hex'), nonceB1[1].toString('hex')]);
let aggnonce2 = signer.Nonce_agg([nonceA2[1].toString('hex'), nonceB2[1].toString('hex')]);
constructor(curve, pubkey, sk) {

this.signer=new SCL_Musig2(curve);
this.pubkey=pubkey;
this.sk=sk;
this.state="idle";

this.nonceA1=0;
this.nonceA2=0;

this.nonceB1=0;
this.nonceB2=0;

this.tx1=0;
this.tx2=0;
}


ResetSession(){
this.state="idle";

this.nonceA1=0;
this.nonceA2=0;

this.nonceB1=0;
this.nonceB2=0;

}

const session_ctx1=[aggnonce1, pubkeys, [], [], msg];
const session_ctx2=[aggnonce2, pubkeys, [], [], msg];
RespondInit(Message_I1 )
{
let tx1=Message_I1[0];
let tx2=Message_I1[1];

let pA1=signer.Psign(nonceA1[0], seckeys[0], session_ctx1);
let pA2=signer.Psign(nonceA2[0], seckeys[0], session_ctx2);
let nonceA1= Message_I1[2];
let nonceA2= Message_I1[3];

let nonceB1= signer.Nonce_gen(this.sk, this.pubkey, x_aggpk, tx1, extra_in1);
let nonceB2= signer.Nonce_gen(this.sk, this.pubkey, x_aggpk, tx2, extra_in2);

let pB1=signer.Psign(nonceB1[0], seckeys[0], session_ctx1);
let pB2=signer.Psign(nonceB2[0], seckeys[0], session_ctx2);
let aggnonce1 = signer.Nonce_agg([nonceA1.toString('hex'), nonceB1[1].toString('hex')]);
let aggnonce2 = signer.Nonce_agg([nonceA2.toString('hex'), nonceB2[1].toString('hex')]);

let Message_R1=[aggnonce1, aggnonce2, nonceB1[1], nonceB2[1], this.pubkey];

//Alice tweaks signatures
let t=int_from_bytes(signer.curve.Get_Random_privateKey());
let atomic_ctx1=swapper.Psign_adapt(pA1, t);
let atomic_ctx2=swapper.Psign_adapt(pA2, t);
this.state="waitI2";

//todo:Bob checks compliance of tG, pA2p, pA1p, msg1, msg2
return Message_R1;//this message is broadcast offchain
}

PartialSign(Message_I2){
let Message_R2=[];

//Bob compute sAB', then substract to obtain sAB
let sAB1=swapper.Untweak(t,atomic_ctx1 );
//Prior to release PsigB, check compliance of transmitted elements

//
//Compute partial sig
let psigI1=signer.Psign(nonceB1[0], this.sk, session_ctx1);
let psigI2=signer.Psign(nonceB2[0], this.sk, session_ctx2);



this.state="waitI3";
return Message_R2;//this message is broadcast onchain to unlock responder exit liquidity

}

console.log("p1=",p1);
FinalUnlock(Message_I3){
let Message_R3=[];

this.state="idle";
return Message_R3;//this message is broadcast onchain to unlock responder exit liquidity

}

}


//example of full session with automata
//note that worst case is assumed (Bob read tweak from Alice's signature)
function test_full_atomic_session_automatas(curve){
let signer=new SCL_Musig2(curve);

//generating keypairs
let Initiator=new SCL_Atomic_Initiator(curve, signer.curve.Get_Random_privateKey());
let Responder=new SCL_Atomic_Responder(curve, signer.curve.Get_Random_privateKey());

//the transaction unlocking tokens for Alice and Bob, must be multisigned with Musig2
//Alice want to compute msg1 signed by AB
//Bob wants to compute msg2 signed by AB
const msg1=Buffer.from("Unlock 1strkBTC on Starknet to Alice",'utf-8');
const msg2=Buffer.from("Unlock 1WBTC on Ethereum to Bob",'utf-8');


console.log("Initiator Start session");
let Message_I1=Initiator.InitSession(msg1, msg2); //Initiator sends I1 to responder offchain

console.log("Responder Start session");
let Message_R1=Responder.RespondInit(Message_I1);//Respondeur sends R1 to Initiator offchain

console.log("Initiator Partial Sign and tweak");
let Message_I2=Initiator.PartialSign_Tweaked(Message_R1);//Initiator sends I2 to responder offchain
//At this Point Alice and Bob locks the funds to multisig address on chain 1 and chain 2

console.log("Responder Check and Partial Sign");
let Message_R2=Responder.PartialSign(Message_I2);//Respondeur sends R2 to Initiator offchain

console.log("Initiator Signature Aggregation and Unlock");
let UnlockSigAlice=Initiator.FinalUnlock(Message_R2);//final signature to Unlock chain1 token by Initiator

console.log("Responder Signature Aggregation and Unlock");
let UnlockSigBob=Initiator.FinalUnlock(UnlockSigAlice);//final signature to Unlock chain2 token by Responder

//todo: result is ok if UnlockSigBob is equal to classic multisig

}
2 changes: 1 addition & 1 deletion src/libMPC/SCL_ecc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export class SCL_ecc
}
}

//takes as input a compressed key and return an even Xonly key
//takes as input a msb compressed key and return an even Xonly key
ForceXonly(bytePoint){
if (this.curve === 'secp256k1') {
return bytePoint.slice(1,33);//x-only version for noncegen
Expand Down

0 comments on commit 389658b

Please sign in to comment.