Skip to content

Commit

Permalink
partial signature and aggregation tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rdubois-crypto committed Dec 10, 2024
1 parent 14c404e commit 9db2728
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 45 deletions.
123 changes: 81 additions & 42 deletions src/libMPC/SCL_frost.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,60 @@ function Interpolate(L, x_i, modulus){
return (num * F.pow(deno, modulus - BigInt(2))) % modulus;
}

//interpolate the points (xi,P(xi).G) in 0 (group public key)
//pubkeys is a list of point
//ids is a list of bigInt
export function Interpolate_group_pubkey(pubkeys, ids, curve){
let Q = curve.GetZero();
if(pubkeys.length!=ids.length){
return false;
}
for(let i=0;i<pubkeys.length;i++){
//console.log("Pi", pubkeys[i])
//console.log("id", ids[i])

let Xi=pubkeys[i];
// console.log("Xi", Xi);
let lam_i = Interpolate(ids, ids[i], curve.order);
// console.log("lam_i", lam_i);
// console.log("lam_i.Xi", Xi.multiply(lam_i));

Q=Q.add(Xi.multiply(lam_i));

// console.log("trace Q:", Q)
}
//todo: test infty
return curve.PointCompress(Q);
}


//interpolate the points (xi,P(xi).G) in 0 (group public key)
//pubkeys is a list of bytes
//ids is a list of bigInt
export function Interpolate_group_BytesPubkey(pubkeys, ids, curve){
let Q = curve.GetZero();
if(pubkeys.length!=ids.length){
return false;
}
for(let i=0;i<pubkeys.length;i++){
//console.log("Pi", pubkeys[i])
//console.log("id", ids[i])

let Xi=curve.PointDecompress(pubkeys[i]);
// console.log("Xi", Xi);
let lam_i = Interpolate(ids, ids[i], curve.order);
// console.log("lam_i", lam_i);
// console.log("lam_i.Xi", Xi.multiply(lam_i));

Q=Q.add(Xi.multiply(lam_i));

// console.log("trace Q:", Q)
}
//todo: test infty
return curve.PointCompress(Q);
}


export class SCL_trustedKeyGen
{
constructor(curve, sk, n, k) {
Expand Down Expand Up @@ -115,29 +168,6 @@ export class SCL_trustedKeyGen
return P0;
}

//interpolate the points (xi,P(xi).G) in 0 (group public key)
Interpolate_group_pubkey(pubkeys, ids){
let Q = this.curve.GetZero();
if(pubkeys.length!=ids.length){
return false;
}
for(let i=0;i<pubkeys.length;i++){
//console.log("Pi", pubkeys[i])
//console.log("id", ids[i])

let Xi=pubkeys[i];
// console.log("Xi", Xi);
let lam_i = Interpolate(ids, ids[i], this.curve.order);
// console.log("lam_i", lam_i);
// console.log("lam_i.Xi", Xi.multiply(lam_i));

Q=Q.add(Xi.multiply(lam_i));

// console.log("trace Q:", Q)
}
//todo: test infty
return this.curve.PointCompress(Q);
}


Check_Shares()
Expand Down Expand Up @@ -340,37 +370,51 @@ Nonce_hash(rand, pk, aggpk, i, msgPrefixed, extraIn) {
return[this.curve.PointCompress(Q), gacc_, tacc_];
}


//pubkeys as list of points here
Group_pubkey_and_tweak(pubkeys, ids, tweaks, is_xonly){
if(pubkeys.length!=ids.length){
return false;
}
let Q=Interpolate_group_pubkey(pubkeys, ids);
let gacc=1
let tacc=0
let Q=Interpolate_group_BytesPubkey(pubkeys, ids, this.curve);
let gacc=BigInt(1);
let tacc=BigInt(0);

let tweak_ctx=[Q, gacc, tacc];

for(i=0;i<pubkeys.length;i++){
for(let i=0;i<tweaks.length;i++){
tweak_ctx=this.Apply_tweak(tweak_ctx, tweaks[i], is_xonly[i])

}

return tweak_ctx;
}

//ids are assumed to be sorted
#ConcatIds(ids){
let concat=int_to_bytes(ids[0],32);
for(let i=1;i<ids.length;i++){
if(ids[i]<ids[i-1]){
throw new Error('ids are not sorted ');
}
concat=Buffer.concat([concat, int_to_bytes(ids[i], 32)])//well, we shall not see more than 4B distincts signers
}

return concat;
}

//input session context: 'aggnonce','ids', 'pubkeys', 'tweaks', 'is_xonly','msg
//return (Q, gacc, tacc, b, R, e)=[Point, int, int, int, Point, int]
//ids are supposed to be sorted here
Get_session_values(SessionContext){

let aggnonce=SessionContext[0];
if(aggnonce.length!=2*this.RawBytesSize) return false;
let tweak_ctx=this.Group_pubkey_and_tweak(SessionContext[2], SessionContext[1], SessionContext[3], SessionContext[4] );//Q, gacc, tacc

let concat_ids=Buffer.concat(SessionContext[1]);
let concat_ids=this.#ConcatIds(SessionContext[1])//concatenation of identifiers

let preconcat=Buffer.concat([this.curve.GetX(tweak_ctx[0]), SessionContext[4]]);
let tweak_ctx=this.Group_pubkey_and_tweak(SessionContext[2], SessionContext[1], SessionContext[3], SessionContext[4] );//Q, gacc, tacc

let preconcat=Buffer.concat([this.curve.GetX(tweak_ctx[0]), SessionContext[5]]);//Qx,msg
let concat=Buffer.concat([concat_ids, aggnonce, preconcat]);//ids, aggnonce,Qx,msg

let b = int_from_bytes(this.TagHash('FROST/noncecoef',concat)) % this.order;
Expand All @@ -384,7 +428,7 @@ Get_session_values(SessionContext){
let RCompressed=this.curve.PointCompress(R);


let e=this.TagHashChallenge('BIP0340/challenge', this.curve.GetX(RCompressed), this.curve.GetX(tweak_ctx[0]), SessionContext[4])
let e=this.TagHashChallenge('BIP0340/challenge', this.curve.GetX(RCompressed), this.curve.GetX(tweak_ctx[0]), SessionContext[5])
e=int_from_bytes(e) % this.order;


Expand All @@ -396,6 +440,9 @@ Get_session_values(SessionContext){
/* SIGNATURE FUNCTIONS*/
/********************************************************************************************/

Mulmod(a,b){
return (a*b)%this.order;
}

//partial signature
//secnonce: 2 nonces + kpub
Expand Down Expand Up @@ -431,18 +478,9 @@ Psign(secnonce, secshare, id, session_ctx){

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 a=Interpolate(session_ctx[1], id, this.curve.order);


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
Expand All @@ -461,6 +499,7 @@ Psign(secnonce, secshare, id, session_ctx){


//operations are not constant time, not required as aggregation is a public function
//input session context: 'aggnonce', 'ids', 'pubkeys', 'tweaks', 'is_xonly','msg'
Partial_sig_agg(psigs, ids, session_ctx){
let sessionV=this.Get_session_values(session_ctx);//(Q, gacc, tacc, b, R, e)

Expand Down
144 changes: 141 additions & 3 deletions src/libMPC/test_frost.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ 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_FROST, SCL_trustedKeyGen } from './SCL_frost.mjs';
import { SCL_FROST, SCL_trustedKeyGen, Interpolate_group_pubkey } from './SCL_frost.mjs';


//random vector generation
Expand All @@ -35,13 +35,15 @@ console.log("Test lagrange interpolation on curve:", Curvename);

console.log("Consistency secret/public shares:",dealer.Check_Shares());
//erasing to prove Reed Solomon like recovery of missing shares

dealer.secshares.pop();
dealer.secshares.pop();

let rec_secret=dealer.Interpolate_group_seckey(dealer.secshares);
console.log("interpolating secret:", rec_secret==int_from_bytes(sk));

let rec_public=dealer.Interpolate_group_pubkey(dealer.pubshares, dealer.ids);

let rec_public=Interpolate_group_pubkey(dealer.pubshares, dealer.ids, curve);

console.log("interpolating public keys", Buffer.from(rec_public).equals(dealer.pubkey));
}

Expand Down Expand Up @@ -94,6 +96,138 @@ function test_noncegen()
console.log(expected_secnonce.equals(Buffer.from(res[0].slice(0,64))));
console.log(expected_pubnonce.equals(Buffer.from(res[1])));

}

//test signature agglomeration
function test_SigAgg(){

let frost = new SCL_FROST('secp256k1');

console.log("/*************************** ");
console.log("Test Signature aggregation:");

let n= 5;
let min_participants=3;
let group_public_key="037940B3ED1FDC360252A6F48058C7B94276DFB6AA2B7D51706FB48326B19E7AE1";
let ids_all=[BigInt(1), BigInt(2), BigInt(3), BigInt(4), BigInt(5)];
let pubshares_all=[
Buffer.from("02BB66437FCAA01292BFB4BB6F19D67818FE693215C36C4663857F1DC8AB8BF4FA",'hex'),
Buffer.from("02C3250013C86AA9C3011CD40B2658CBC5B950DD21FFAA4EDE1BB66E18A063CED5",'hex'),
Buffer.from("03259D7068335012C08C5D80E181969ED7FFA08F7973E3ED9C8C0BFF3EC03C223E",'hex'),
Buffer.from("02A22971750242F6DA35B8DB0DFE74F38A3227118B296ADD2C65E324E2B7EB20AD",'hex'),
Buffer.from("03541293535BB662F8294C4BEB7EA25F55FEAE86C6BAE0CEBD741EAAA28639A6E6",'hex')];

let pubnonces=[
"021AD89905F193EC1CBED15CDD5F4F0E04FF187648390639C88AC291F2F88D407E02FD49462A942948DF6718EEE86FDD858B124375E6A034A4985D19EE95431E9E03",
"03A0640E5746CC90EC3EF04F133AF1B79DE67011927A9BA1510B9254E9C8698062037209BB6915B573D2E6394032E508B8285DD498FE8A85971AAB01ACF0C785A56B",
"02861EFD258C9099BEF14FA9B3B4E6229595D8200FC72D27F789D4CCC4352BB32B038496DA1C20DFE16D24D20F0374812347EE9CFF06928802C04A2D1B2D369F4628",
"0398DD496FFE3C14D2DDFB5D9FD1189DB829301A83C45F2A1DDF07238529F75D1D0233E8FF18899A66276D27AE5CE28A5170EEAAC4F05DEACC8E7DB1C55F8985495F",
"03C7B31E363526D04B5D31148EE6B042AF8CC7DFA922A42A69EB78B816D458D0B20257495EC72B1E59FB90A48B036FBD3D9AE4415C49B6171E108185124B99DE56AA"];


let tweaks=[
"B511DA492182A91B0FFB9A98020D55F260AE86D7ECBD0399C7383D59A5F2AF7C",
"A815FE049EE3C5AAB66310477FBC8BCCCAC2F3395F59F921C364ACD78A2F48DC",
"75448A87274B056468B977BE06EB1E9F657577B7320B0A3376EA51FD420D18A8"
];

let msg=Buffer.from("599C67EA410D005B9DA90817CF03ED3B1C868E4DA4EDF00A5880B0082C237869",'hex');
let psigs_all=[
Buffer.from("447D69D4E02693E3F6C04E198F34E89E17D65DC29C92E635E8BFB8D2908DCA6A",'hex'),
Buffer.from("E7E02FDE0FA66D116C0DCF932F7976D611A4D0CF225087C2B8282153E461FA8B",'hex'),
Buffer.from("E84B98E0B132F4049B061A949EF69E3DFBEB3E2712AEE2DEE0C5B6D517860339",'hex'),
Buffer.from("714B7FCF4D3EA2F4BB2B22F786AEBF0C65E1A6E6FBEF04C39B60EAA1CA257CD5",'hex'),
Buffer.from("DA815BBE9D06203D5ADD3AD5D3FE5F0D5405939EFD7EA3FED6DACA9E5449AD80",'hex'),
Buffer.from("8E367AE4000EEEFCEF7F83DA1AC96181DC51BA0D83E0F834F67A0CFD487DBEF7",'hex'),
Buffer.from("9CAB74D0FBCF14D89330D81C85482B8C720DC69899187F3A5432A5856609E92D",'hex'),
Buffer.from("351F38F8B3126944362D9B3F0D83791CF3D623E746B84A58012DF4C9383909EC",'hex'),
Buffer.from("B9ABA5EE2181EDE7A0D3D29DB147741F66B5A8EF3BB6C9CFD1FAD0D98E5A8A93",'hex'),
Buffer.from("A2DF2C5ECB1141E0B55F47711BBDAE491F2F22D967FA1D9569200B7FB0754AD6",'hex'),
Buffer.from("441DFF8E4E0E8368D21BD3DD70F151C7C581EC2B1931B8F041CC8C052FEBF046",'hex'),
Buffer.from("DDC813A7AA07415634F2F6CC10984EF68216C75EA4F7A8E883DBA163C41CE2BA",'hex'),
Buffer.from("2D64FC0371D08A7069997C1009814AF9C964DB64AEDB919AC229DA774AB09888",'hex'),
Buffer.from("5F6481FC18E4CB223CB5BAB966165A1033349267702E7D75B5A0E5CACEA0E6A0",'hex'),
Buffer.from("312170A9C271F67D09C8BE06A468106505CF6B7CD4DB1A40E02AF13213069EB0",'hex'),
Buffer.from("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",'hex')
];


let aggnonce=frost.Nonce_agg([pubnonces[0], pubnonces[1], pubnonces[2]]);
//console.log("aggnonce:", aggnonce);

//input session context: 'aggnonce', 'ids', 'pubkeys', 'tweaks', 'is_xonly','msg'

let ids=[ids_all[0], ids_all[1], ids_all[2]];
let psigs=[psigs_all[0], psigs_all[1], psigs_all[2]];
let pubshares=[pubshares_all[0], pubshares_all[1], pubshares_all[2]]

let session_ctx=[aggnonce, ids, pubshares, [], [], msg];
let res=frost.Partial_sig_agg(psigs, ids, session_ctx);
let expected=Buffer.from("8471BE6E49D0E43097DD32DA374039149F5D00165A8AD369AE86E362D13730DA14A93293A0FFF4F9FDD438415DA4FDB4B008B2EB730110600208D3E1EC0945AC",'hex');
console.log(expected.equals(res));
}

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

console.log("/*************************** ");
console.log("Test Partial Signature:");

let group_public_key="037940B3ED1FDC360252A6F48058C7B94276DFB6AA2B7D51706FB48326B19E7AE1";
let secshare_p1=Buffer.from("81D0D40CDF044588167A987C14552954DB187AC5AD3B1CA40D7B03DCA32AFDFB",'hex');
let ids_all=[BigInt(1), BigInt(2), BigInt(3), BigInt(4), BigInt(5)];

let pubshares=[
Buffer.from("02BB66437FCAA01292BFB4BB6F19D67818FE693215C36C4663857F1DC8AB8BF4FA",'hex'),
Buffer.from("02C3250013C86AA9C3011CD40B2658CBC5B950DD21FFAA4EDE1BB66E18A063CED5",'hex'),
Buffer.from("03259D7068335012C08C5D80E181969ED7FFA08F7973E3ED9C8C0BFF3EC03C223E",'hex')
];

let secnonces_p1=Buffer.from("96DF27F46CB6E0399C7A02811F6A4D695BBD7174115477679E956658FF2E83D618E4F670DF3DEB215934E4F68D4EEC71055B87288947D75F6E1EA9037FF62173",'hex');

let pubnonces=[
Buffer.from("02FCDBEE416E4426FB4004BAB2B416164845DEC27337AD2B96184236D715965AB2039F71F389F6808DC6176F062F80531E13EA5BC2612B690FC284AE66C2CD859CE9",'hex'),
Buffer.from("02D26EF7E09A4BC0A2CF295720C64BAD56A28EF50B6BECBD59AF6F3ADE6C2480C503D11B9993AE4C2D38EA2591287F7B744976F0F0B79104B96D6399507FC533E893",'hex'),
Buffer.from("03C7E3D6456228347B658911BF612967F36C7791C24F9607ADB34E09F8CC1126D803D2C9C6E3D1A11463F8C2D57B145A814F5D44FD1A42F7A024140AC30D48EE0BEE",'hex')
];

let aggnonce=Buffer.from("02047C99228CEA528AE200A82CBE4CD188BC67D58F537D1904A16B07FCDE07C3A6038708199DFA5BC5C41A0DD0FBD7D0620ADB4AC9991F7DB55A155CE9396AA80D1A",'hex');
let ids=[BigInt(1), BigInt(2), BigInt(3)];
let msg=Buffer.from("F95466D086770E689964664219266FE5ED215C92AE20BAB5C9D79ADDDDF3C0CF",'hex');
//input session context: 'aggnonce', 'ids', 'pubkeys', 'tweaks', 'is_xonly','msg'
let session_ctx=[aggnonce, ids, pubshares, [], [], msg];
let res=frost.Psign(secnonces_p1, secshare_p1, BigInt(1), session_ctx);

let expected=Buffer.from("DEDAA44E6DB7FF1B40D8CBAA44DF3F8C80BD7CEC6A21AE22F34ED7ABC59E2AEC",'hex');

console.log("expected=", expected.equals(res));

}

//randomly generated full session
function test_random_fullsession(Curvename){

console.log("/*************************** ");
console.log("Test Full session, random input on curve:", Curvename);

console.log("----------Generate Keys:");
let curve=new SCL_ecc(Curvename);
let sk=curve.Get_Random_privateKey();
let dealer=new SCL_trustedKeyGen( Curvename,sk, 12,4);

console.log(" Check key correctness:", dealer.Check_Shares());

let rec_secret=dealer.Interpolate_group_seckey(dealer.secshares);
console.log(" Interpolating secret:", rec_secret==int_from_bytes(sk));

let rec_public=Interpolate_group_pubkey(dealer.pubshares, dealer.ids, curve);
console.log(" Interpolating public keys", Buffer.from(rec_public).equals(dealer.pubkey));


console.log("----------Signer Set:");



}


Expand All @@ -103,6 +237,10 @@ function test_noncegen()

test_aggnonce();
test_noncegen();
test_partialSig();
test_SigAgg();

test_random_fullsession('secp256k1');


})();

0 comments on commit 9db2728

Please sign in to comment.