Skip to content

Commit

Permalink
testing nonce hashing and generation
Browse files Browse the repository at this point in the history
  • Loading branch information
rdubois-crypto committed Dec 9, 2024
1 parent 21d4909 commit 009521a
Show file tree
Hide file tree
Showing 2 changed files with 256 additions and 16 deletions.
215 changes: 201 additions & 14 deletions src/libMPC/SCL_frost.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -163,20 +163,45 @@ export class SCL_trustedKeyGen

export class SCL_FROST{

constructor(curve, n, k, id, sk, pubkey) {
constructor(curve) {

this.curve=new SCL_ecc(curve);

this.id=id;//i
this.sk=sk;//P(i), P unknown secret polynomial
this.pubkey=pubkey;//P(i).G
if (this.curve.curve === 'secp256k1') {
this.order=secp256k1.CURVE.n;
this.RawBytesSize=33;//size of a compressed point with parity, 32bytes+1byte parity
} else if (this.curve.curve === 'ed25519') {
this.order=ed25519.CURVE.n;//size of a compressed point with parity, 32 bytes, including parity in msb bit.
this.RawBytesSize=32;
} else {
throw new Error('Unsupported curve');
}
}

//return bytes
TagHash(tag, message){
if (this.curve.curve === 'secp256k1') {
return tagged_hashBTC(tag, message);
} else if (this.curve.curve === 'ed25519') {
return taghash_rfc8032(tag, message);
} else {
throw new Error('Unsupported curve');
}
}

TagHashChallenge(tag,r,KpubC, Msg){
if (this.curve.curve === 'secp256k1') {
const encoded = Buffer.concat([r, KpubC, Msg]);
return tagged_hashBTC(tag, encoded);
} else if (this.curve.curve === 'ed25519') {
const encoded = Buffer.concat([reverse(r), reverse(KpubC), Msg]);

this.n=n;
this.degree=k;
this.min_participants=k+1;

return taghash_rfc8032('', encoded);
} else {
throw new Error('Unsupported curve');
}

}
/********************************************************************************************/
/* NONCE GENERATION FUNCTIONS*/
/********************************************************************************************/
Expand Down Expand Up @@ -370,23 +395,185 @@ Get_session_values(SessionContext){
/********************************************************************************************/
/* SIGNATURE FUNCTIONS*/
/********************************************************************************************/


//partial signature
//secnonce: 2 nonces + kpub
//sk: 32 bytes
//input session context: 'aggnonce', 'ids', 'pubkeys', 'tweaks', 'is_xonly','msg'
Psign(secnonce, secshare, id, session_ctx){
if(id>this.curve.order)
{
return false;
{
return false;
}

let k1= int_from_bytes(secnonce.slice(0, 32));
let k2= int_from_bytes(secnonce.slice(32, 64));

let session_values= this.Get_session_values(session_ctx);// (Q, gacc, _, b, R, e)


let Q=session_values[0];
let gacc=session_values[1];
let b=session_values[3];
let R=session_values[4];
let e=session_values[5];


//todo : test range of k1 and k2
if (this.curve.Has_even_y(R)==false)
{
k1=this.order-k1;
k2=this.order-k2;
}
let d_ = int_from_bytes(secshare)
//todo : test range d

let G= this.curve.GetBase();
let P = (G.multiply(d_));//should be equal to pk
let secnonce_pk=secnonce.slice(64, 64+this.RawBytesSize);//pk is part of secnonce, 32 or 33 bytes
let Q3=this.curve.PointDecompress(secnonce_pk);


//todo test x equality
if(this.curve.EqualsX(P,Q3)==false){
return false;//wrong public key
}

let a=this.Interpolate(ids, id);


}
let g=BigInt('0x1') ;
if(this.curve.Has_even_y(Q)==false){//this line ensures the compatibility with requirement that aggregated key is even in verification
g=this.order-g;//n-1

}
let d = this.Mulmod(g , gacc );//d = (g * gacc * d_) % n
d= this.Mulmod(d, d_);//g*gacc*d
let s = (k1 + this.Mulmod(b , k2) ) % this.order;//
s= (s+ this.Mulmod(this.Mulmod(e , a) , d))% this.order;

//todo: optional partial verif
let psig=int_to_bytes(s,32);

return psig;
}


Psig_Agg(psigs, ids, session_ctx){
if(psigs.length!= ids.length){
//operations are not constant time, not required as aggregation is a public function
Partial_sig_agg(psigs, ids, session_ctx){
let sessionV=this.Get_session_values(session_ctx);//(Q, gacc, tacc, b, R, e)

if(psigs.length!=ids.length){
return false;
}

let Q=sessionV[0];//aggnonce
let tacc=sessionV[2];

let e=sessionV[5];

let s = BigInt(0);
let u = psigs.length;
for(let i=0;i<u;i++){
let s_i = int_from_bytes(psigs[i])
if(s_i> this.order){
throw new Error('Invalid contribution for id:', ids[i]);
}
s = (s + s_i) % this.order;
}
let g=BigInt(1);
if(this.curve.Has_even_y(Q)==false)
g= this.order - g;//n-1


s = (s + e * g * tacc) % this.order;
s=int_to_bytes(s,32);

let R=this.curve.GetX(sessionV[4]);
return Buffer.concat([R,s]);

}

/********************************************************************************************/
/* VERIFICATIONS*/
/********************************************************************************************/

}
//verify one of the partial signature provided by a participant
Psig_verify(psig, id, pubnonce, pk, session_ctx){
let sessionV=this.Get_session_values(session_ctx);//(Q, gacc, _, b, R, e)
let s = int_from_bytes(psig);
let Q=sessionV[0];
let gacc=sessionV[1];
let b=sessionV[3];
let R=sessionV[4];
let e=sessionV[5];


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=Re_s_;

if(this.curve.Has_even_y(R)==false)
{
Re_s=Re_s.negate();//forced to even point
}
let P=this.curve.PointDecompress(pk);//partial input public key

let a=this.Interpolate(session_ctx[1], id);//session_ctx[1]=ids


let g=BigInt(1);
if(this.curve.Has_even_y(Q)==false){
g=this.order - g;//n-1
}

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

let G= this.curve.GetBase();
let P1 = (G.multiply(s));

let tmp=this.Mulmod(e,a);
tmp=this.Mulmod(tmp,g);//e*a*g % n
let P2=(Re_s.add(P.multiply(tmp)));

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){

if(sig.length!=64) {
console.log("bad sig length");
return false;}

if(pubkey.length!=32) {
console.log("bad pubkey length");
return false;
}

let r = int_from_bytes(sig.slice(0,32));
let s = int_from_bytes(sig.slice(32,64));

let P=this.curve.PointDecompressEven(pubkey);//extract even public key of coordinates x=pubkey

let e=int_from_bytes(this.TagHashChallenge('BIP0340/challenge', sig.slice(0,32), pubkey, msg)) % this.order
let sG=(this.curve.GetBase()).multiply(s);
let meP=P.multiply( this.order - e);//-eP
let PointR=sG.add(meP);//sG-eP

let R=this.curve.PointCompressXonly(PointR);

if(int_from_bytes(R)!=r)
return false;

return true;
}


}
57 changes: 55 additions & 2 deletions src/libMPC/test_frost.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ import{SCL_ecc} from './SCL_ecc.mjs';
import { randomBytes } from 'crypto'; // Use Node.js's crypto module
import { Field } from "@noble/curves/abstract/modular";

import { SCL_trustedKeyGen } from './SCL_frost.mjs';
import { SCL_FROST, SCL_trustedKeyGen } from './SCL_frost.mjs';


//random vector generation
function test_randomInterpolate_secret(){

let curve=new SCL_ecc('secp256k1');
Expand All @@ -40,12 +42,63 @@ function test_randomInterpolate_secret(){
console.log("interpolating public keys", Buffer.from(rec_public).equals(dealer.pubkey));
}

//same as Musig2 test, tested OK
function test_aggnonce(){

let frost = new SCL_FROST('secp256k1');

console.log("/*************************** ");
console.log("Test nonce_agg:");

let pnonces= [
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E66603BA47FBC1834437B3212E89A84D8425E7BF12E0245D98262268EBDCB385D50641",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833",
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E6660279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60379BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"
];
let expected_agg1="035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B024725377345BDE0E9C33AF3C43C0A29A9249F2F2956FA8CFEB55C8573D0262DC8";
let expected_agg2="035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B000000000000000000000000000000000000000000000000000000000000000000";


let res=frost.Nonce_agg([pnonces[0], pnonces[1]]);
console.log(res.equals(Buffer.from(expected_agg1,'hex')));


}

function test_noncegen()
{
let frost = new SCL_FROST('secp256k1');

console.log("/*************************** ");
console.log("Test nonce_gen:");

let rand_=Buffer.from("0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F",'hex');
let secshare=Buffer.from("0202020202020202020202020202020202020202020202020202020202020202",'hex');;

let pubshare=Buffer.from("024D4B6CD1361032CA9BD2AEB9D900AA4D45D9EAD80AC9423374C451A7254D0766",'hex');;

let group_pk=Buffer.from("0707070707070707070707070707070707070707070707070707070707070707",'hex');;
let msg=Buffer.from("0101010101010101010101010101010101010101010101010101010101010101",'hex');;

let extra_in=Buffer.from("0808080808080808080808080808080808080808080808080808080808080808",'hex');;
let expected_secnonce=Buffer.from("6135CE36209DB5E3E7B7A11ADE54D3028D3CFF089DA3C2EC7766921CC4FB3D1BBCD8A7035194A76F43D278C3CD541AEE014663A2251DDE34E8D900EDF1CAA3D9",'hex');
let expected_pubnonce=Buffer.from("02A5671568FD7AEA35369FE4A32530FD0D0A23D125627BEA374D9FA5676F645A6103EC4E899B1DBEFC08C51F48E3AFA8503759E9ECD3DE674D3C93FD0D92E15E631A",'hex');

let res=frost.Nonce_gen_internal(rand_, secshare, pubshare, group_pk, msg, extra_in);

console.log("res:",res, res[0].length);

console.log(expected_secnonce.equals(Buffer.from(res[0].slice(0,64))));
console.log(expected_pubnonce.equals(Buffer.from(res[1])));

}


(async () => {
test_randomInterpolate_secret();

test_aggnonce();
test_noncegen();


})();

0 comments on commit 009521a

Please sign in to comment.