Skip to content

Commit

Permalink
full session FROST tested OK
Browse files Browse the repository at this point in the history
  • Loading branch information
rdubois-crypto committed Dec 11, 2024
1 parent 9db2728 commit 9c75104
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 15 deletions.
16 changes: 14 additions & 2 deletions src/libMPC/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,14 @@ Note: the protocol requires to broadcast onchain 4 values (2 locked tokens, then
The element $t$ shall be as protected as a secret key, to prevent $B$ from stealing $A$ token. In the description, Alice has more duty regarding to the protection of this secret.


# Performing a Multisignature with libMPC FROST (WIP)
# Performing a Multisignature with libMPC FROST



## Design notes

The generation and distribution of FROST's shares are out of scope of its specification. However
[FROST-RFC] https://datatracker.ietf.org/doc/draft-irtf-cfrg-frost/15/ specifies a trusted key dealer generation which is the most obvious. The DKG is implemented in the class SCL_FROST_TDealer.
[FROST-RFC](https://datatracker.ietf.org/doc/draft-irtf-cfrg-frost/15/) specifies a trusted key dealer generation which is the most obvious. The DKG is implemented in the class SCL_trustedkeygen.
In the future the more decentralized chill-DKG shall be implemented.


Expand Down Expand Up @@ -224,6 +228,14 @@ Tests can be ran using the following command :
The `file test_atomic_bitcoin.js` aims to provide a full onchain demonstration of a bridging.


## FROST

Tests can be ran using the following command :
```
node test_frost.mjs
```


# Product Roadmap

- Musig2 can be used to provide protection against trappoored hardware [as explained here](https://docs.google.com/presentation/d/10JmRVq9qeoIouLyzIOMcMLnfQQbZlZoSP5eTOyXYUdI/edit#slide=id.g2bf9c14683d_1_265). Using Musig2 with account abstraction, it is possible to have a companion app handling a first key, the hardware wallet a second.
Expand Down
11 changes: 7 additions & 4 deletions src/libMPC/SCL_frost.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -307,10 +307,10 @@ Nonce_hash(rand, pk, aggpk, i, msgPrefixed, extraIn) {
}


Nonce_gen(sk,pk,aggpk, m,extra_in){
Nonce_gen(sk,pk,group_pk, m,extra_in){
const rand =randomBytes(32);//note that if sk is well protected, leakage of the nonce doesn't break the scheme
return this.Nonce_gen_internal(rand, sk,pk,aggpk, m,extra_in);

return this.Nonce_gen_internal(rand, sk,pk,group_pk, m,extra_in);

}

Expand All @@ -327,7 +327,6 @@ Nonce_hash(rand, pk, aggpk, i, msgPrefixed, extraIn) {
let Rj = this.curve.GetZero();//infinity neutral point

for(let i=0;i<u;i++){

let rij= pubnonces[i].slice((j - 1) * (2*this.RawBytesSize), j * (2*this.RawBytesSize));
//hex to bytes, to cpoint
let Rij=this.curve.PointDecompress(Buffer.from(rij,'hex'));
Expand Down Expand Up @@ -449,6 +448,8 @@ Mulmod(a,b){
//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;
Expand Down Expand Up @@ -540,6 +541,8 @@ Partial_sig_agg(psigs, ids, session_ctx){

//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];
Expand Down
89 changes: 80 additions & 9 deletions src/libMPC/test_frost.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -204,30 +204,100 @@ function test_partialSig(){

}


function randomSubset(n, subsetSize) {
if (subsetSize > n) {
throw new Error("Subset size cannot be larger than n");
}

const subset = new Set();

// Randomly select `subsetSize` unique numbers
while (subset.size < subsetSize) {
const randomNumber = Math.floor(Math.random() * n);
subset.add(randomNumber); // Use a Set to avoid duplicates
}

// Convert to array and sort in increasing order
return Array.from(subset).sort((a, b) => a - b);
}

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

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

let n=12;
let k=4;

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

let dealer=new SCL_trustedKeyGen( Curvename,sk, n,k);
let frost=new SCL_FROST(Curvename);

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:");
let elements=randomSubset(n, k+1);
console.log("set:", elements);
let Ids = elements.map(index => dealer.ids[index]);
console.log("ids:", Ids);
let pubshares=elements.map(index => dealer.pubshares[index]);//pubshares as points
let b8_pubshares=elements.map(index => curve.PointCompress(dealer.pubshares[index]))
let secshares=elements.map(index => dealer.secshares[index]);//this is the view from the dealer and shall never happen to be computed in practice

let group_pk=Interpolate_group_pubkey(pubshares, Ids, curve);
let x_group_pk=curve.ForceXonly(group_pk);//x-only version for noncegen, allways 32

console.log(" Interpolating public key from pubkey set", Buffer.from(group_pk).equals(dealer.pubkey));

console.log("----------Signer Set:");
let rec_secret=dealer.Interpolate_group_seckey(secshares);
console.log(" Interpolating secret key from seckey set:", rec_secret==int_from_bytes(dealer.sk));



console.log("----------Generate message and nonces");

const msg=Buffer.from(randomBytes(32));
const extra_in= Buffer.from(randomBytes(32));

let secnonces=[];
let pubnonces=[];

for(let i=0;i<k+1;i++){
let nonce=frost.Nonce_gen( int_to_bytes(secshares[i][1],32), b8_pubshares[i], group_pk, msg, extra_in );
secnonces.push(nonce[0]);
pubnonces.push(nonce[1].toString('hex'));
}
//console.log("secnonces", secnonces,"pubnonces", pubnonces);


console.log("----------Aggnonce");
let aggnonce=frost.Nonce_agg(pubnonces)
console.log("aggnonce", aggnonce);

//input session context: 'aggnonce', 'ids', 'pubkeys', 'tweaks', 'is_xonly','msg'
let session_context=[aggnonce, Ids, b8_pubshares, [], [], msg];


console.log("----------Psigs");
let psigs=[];
for(let i=0;i<k+1;i++){
let psig=frost.Psign(secnonces[i], int_to_bytes(secshares[i][1],32), Ids[i], session_context);
console.log("psig:", psig);
psigs.push(psig);
}


console.log("----------Aggregation");
let final_sig=frost.Partial_sig_agg(psigs, Ids, session_context);
console.log("sig", final_sig);

console.log("final verif:", frost.Schnorr_verify(msg, x_group_pk,final_sig ));
}


Expand All @@ -242,5 +312,6 @@ function test_random_fullsession(Curvename){

test_random_fullsession('secp256k1');

test_random_fullsession('ed25519');

})();

0 comments on commit 9c75104

Please sign in to comment.