Skip to content

Commit

Permalink
WIP Frost
Browse files Browse the repository at this point in the history
  • Loading branch information
rdubois-crypto committed Dec 6, 2024
1 parent b974f49 commit 1338f90
Show file tree
Hide file tree
Showing 4 changed files with 335 additions and 30 deletions.
17 changes: 16 additions & 1 deletion src/libMPC/SCL_ecc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
/********************************************************************************************/


import { etc, utils, getPublicKey } from '@noble/secp256k1';

import { ed25519 } from '@noble/curves/ed25519';
import { secp256k1 } from '@noble/curves/secp256k1';
import { reverse, int_from_bytes, int_to_bytes } from './common.mjs';
Expand All @@ -32,7 +34,20 @@ export class SCL_ecc
throw new Error('Unsupported curve');
}
}


IndividualPubKey_array(scalar_array){

if (this.curve === 'secp256k1') {
const publicKey = getPublicKey(scalar_array);
return publicKey;
}
if (this.curve === 'ed25519') {
const publicKey = this.curve.GetBase().multiply(int_from_bytes(scalar_array)); // 'true' for compressed format
return this.curve.PointCompress(publicKey);//the getPublicKey is replaced by a scalar multiplication to be compatible with key aggregation
}

throw new Error('Unsupported curve');
}

GetBase(){
if (this.curve === 'secp256k1') {
Expand Down
184 changes: 155 additions & 29 deletions src/libMPC/SCL_frost.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,68 +18,194 @@ import { secp256k1 } from '@noble/curves/secp256k1';
import { etc, utils, getPublicKey } from '@noble/secp256k1';
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_Musig2} from './SCL_Musig2.mjs';

export class SCL_polynomials{

constructor(modulus, coeffs){
this.coeffs=coeffs;
this.modulus=this.modulus;
//beware that because horner method is used degree 0 coefficient is last of the list
function Evaluate(coeffs,x, modulus){
let res=coeffs[0];//a0
for(let i=1;i<coeffs.length;i++){
res=((res*x)+(coeffs[i]))%modulus;
}
return res;
}

//Lagrangian interpolation in 0= prod(x_i)/prod(xj-xi)
function Interpolate(L, x_i, modulus){
let num=BigInt(1);
let deno=BigInt(1);

for(let j=0;j<L.length;j++)
{
let x_j=L[j]
if( x_j != x_i){
num=(num*x_j)%modulus;
deno=deno*(modulus+x_j-x_i)%modulus;

Evaluate(x){
let res=coeffs[0];//a0
for(i=1;i<this.coeffs.length;i++){
res=(this.MulMod(res,x)+(this.coeffs[i]))%this.modulus;
}
return res;
}
let F=Field(BigInt(modulus));
return (num * F.pow(deno, modulus - BigInt(2))) % modulus;
}



}

export class SCL_trustedKeyGen
{
constructor(curve, sk, n, k) {

this.signer=new SCL_Musig2(curve);
this.curve=new SCL_ecc(curve);
this.sk=sk;

this.pubkey=this.signer.IndividualPubKey_array(sk);
this.pubkey=this.curve.IndividualPubKey_array(sk);

this.n=0;
this.k=0;

this.n=n;
this.degree=k;
this.min_participants=k+1;
this.pubshares=[];
this.secshares=[];

this.coeffs=[];

this.KeyGen(n, k+1);
this.ids=this.secshares.map(points =>points[0]);

}

//in the future, improve it with a PRNG using secret and random generator
GetRandomElement(){
return this.signer.curve.Get_Random_privateKey()
return this.curve.Get_Random_privateKey();
}

KeyGen(n, k){
KeyGen(n, min_participants){

if(min_participants<1) return false;

this.n=n;//maximum number of participants
this.k=k;//minimum number of participants = degree of polynomial-1
this.degree=min_participants-1;//minimum number of participants = degree of polynomial-1


//generate secret shares:
for(i=0;i<this.k;i++){
//generate secret polynomial:
for(let i=this.degree;i>0;i--){
let ai=this.GetRandomElement();
this.coeffs.push(int_from_bytes(ai));
}
this.coeffs.push(int_from_bytes(this.sk));//a0=P(0)=secret


//Shares are evaluation of P starting from 1, P(0) being the secret
for(let xi=1;xi<this.n+1;xi++){

let yi=Evaluate(this.coeffs, BigInt(xi), this.curve.order);
this.secshares.push([BigInt(xi),yi]);
this.pubshares.push(this.curve.GetBase().multiply(BigInt(yi)));

}

}

//interpolate the points (xi,P(xi)) in 0 (group secret key)
Interpolate_group_seckey( points ){

let P0=BigInt(0);
for(let i=0;i<points.length;i++){
let x=points[i][0];
let delta=points[i][1] * Interpolate(points.map(points =>points[0]),x, this.curve.order);//yi * interpolate(L,xi)
P0=(P0 + delta)%this.curve.order;
}
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);
}

let Rs1 = this.curve.PointCompress(P.multiply(bk_1));
let Rs2 = this.curve.PointCompress(P.multiply(bk_2));

let pubnonce = Buffer.concat([Rs1, Rs2]);
let secnonce = Buffer.concat([k_1, k_2, pk]);

Check_Shares()
{
if (this.n<this.min_participants)
return false;
//check secshares.G=pubshares
console.log(this.secshares);

for(let i=0;i<this.n;i++){
let recPub=this.curve.GetBase().multiply(this.secshares[i][1]);
if(recPub.equals(this.pubshares[i])==false)
return false;
}

return true;
}



}


export class SCL_FROST{

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

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

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

}

/********************************************************************************************/
/* NONCE GENERATION FUNCTIONS*/
/********************************************************************************************/

//identical to Musig2, except hash domain separation
Nonce_hash(rand, pk, aggpk, i, msgPrefixed, extraIn) {
// Buffer to concatenate all inputs
let buf =rand;

// Append all parts to the buffer

buf = Buffer.concat([buf, new Uint8Array([pk.length])]); // Length of pk (1 byte)
buf = Buffer.concat([buf, pk]);
buf = Buffer.concat([buf, new Uint8Array([aggpk.length])]); // Length of aggpk (1 byte)
buf = Buffer.concat([buf, aggpk]);
buf = Buffer.concat([buf, msgPrefixed]);
let extraInLengthBuffer = Buffer.alloc(4);
extraInLengthBuffer.writeUInt32BE(extraIn.length);
buf = Buffer.concat([buf, extraInLengthBuffer]); // Length of extraIn (4 bytes)
buf = Buffer.concat([buf, extraIn]);
buf = Buffer.concat([buf, new Uint8Array([i])]); // Index i (1 byte)

// Compute the tagged hash with 'MuSig/nonce' as the tag
const hash = this.TagHash('FROST/nonce', buf);
// Return the result as a BigInt
return hash;
}




}
113 changes: 113 additions & 0 deletions src/libMPC/test_atomic_bitcoin.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/********************************************************************************************/
/*
/* ___ _ _ ___ _ _ _ _
/* / __|_ __ ___ ___| |_| |_ / __|_ _ _ _ _ __| |_ ___ | | (_) |__
/* \__ \ ' \/ _ \/ _ \ _| ' \ | (__| '_| || | '_ \ _/ _ \ | |__| | '_ \
/* |___/_|_|_\___/\___/\__|_||_| \___|_| \_, | .__/\__\___/ |____|_|_.__/
/* |__/|_|
/*
/* Copyright (C) 2024 - Renaud Dubois - This file is part of SCL (Smooth CryptoLib) project
/* License: This software is licensed under MIT License
/********************************************************************************************/

import { secp256k1 } from '@noble/curves/secp256k1';
import{atomic_example} from './SCL_atomic_swaps.mjs';
import * as secp from 'tiny-secp256k1';
import fetch from 'node-fetch';//npm install node-fetch

// Initialize the ECC library with TinySecp256k1
import { ECPairFactory } from 'ecpair';

const ECPair = ECPairFactory(secp);

import * as bitcoin from 'bitcoinjs-lib';//npm install bitcoinjs
bitcoin.initEccLib(secp);

//const ECPair = ECPairFactory(ecc);

//import { Signer, SignerAsync, ECPairInterface, ECPairFactory, ECPairAPI, TinySecp256k1Interface } from 'ecpair';

// Set up ECPair with tiny-secp256k1 as required by ECPairFactory
//const ECPair = ECPairFactory(ecc);

const network = bitcoin.networks.testnet;
//atomic_example();
const privateKey = secp256k1.utils.randomPrivateKey();
const publicK= secp256k1.getPublicKey(privateKey, true);
console.log('key',privateKey);


console.log('key pub',publicK);
console.log(Object.keys(ECPairFactory));

const keyPair = ECPair.fromPrivateKey(Buffer.from(privateKey), { network });
console.log('key',keyPair);
console.log('key pub',publicK);

console.log('Private Key (WIF):', keyPair.toWIF());
const { address } = bitcoin.payments.p2tr({ pubkey: publicK.slice(1,33), network: bitcoin.networks.testnet });

console.log('Testnet Address:', address);

const tx = new bitcoin.Psbt({ network: bitcoin.networks.testnet }); // Use Psbt for transaction creation
// Define UTXO to spend
const txId = '62d9b44b6a6268bd4a74ab2141133343849f012b694e0ff7c32b0b3f5e3c87fa'; // Replace with the transaction ID of the UTXO to spend
const vout = 0; // The output index of the UTXO
const amountToSend = 1 * 1e1; // 1.10-7 BTC in satoshis
tx.addInput({
hash: txId, // Transaction ID of the UTXO
index: 0, // Output index within the transaction
witnessUtxo: {
script: Buffer.from('0014d85a4e1a2b1d8f27c405b5b5d715b2f23f3ddcc3', 'hex'), // P2WPKH script or appropriate script for your UTXO type
value: amountToSend, // Value of the UTXO in satoshis
},
});

tx.addOutput("1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK", amountToSend);
const sighash = tx.hashForSignature(0, p2tr.output, Transaction.SIGHASH_ALL);
const signature = secp.signSchnorr(sighash, privateKey);
console.log("Schnorr Signature:", signature.toString('hex'));

// Build the transaction without signing it
const txInc = tx.buildIncomplete();
/*
key Uint8Array(32) [
240, 165, 76, 109, 221, 29, 213, 231,
139, 138, 178, 140, 161, 87, 32, 101,
172, 31, 194, 164, 114, 186, 31, 96,
65, 226, 8, 13, 225, 64, 163, 69
]
key pub Uint8Array(33) [
2, 248, 59, 32, 158, 235, 60, 129,
164, 106, 165, 105, 84, 237, 54, 69,
94, 92, 106, 152, 120, 142, 249, 156,
21, 141, 229, 187, 241, 24, 201, 176,
35
]
[]
key ECPair {
__D: <Buffer f0 a5 4c 6d dd 1d d5 e7 8b 8a b2 8c a1 57 20 65 ac 1f c2 a4 72 ba 1f 60 41 e2 08 0d e1 40 a3 45>,
__Q: undefined,
compressed: true,
network: {
messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'tb',
bip32: { public: 70617039, private: 70615956 },
pubKeyHash: 111,
scriptHash: 196,
wif: 239
},
lowR: false
}
key pub Uint8Array(33) [
2, 248, 59, 32, 158, 235, 60, 129,
164, 106, 165, 105, 84, 237, 54, 69,
94, 92, 106, 152, 120, 142, 249, 156,
21, 141, 229, 187, 241, 24, 201, 176,
35
]
Private Key (WIF): cVeV7EtdSB2waytpSAikiuXakmWvmaaAnyw1X9cxrokKEdWgWuxz
Testnet Address: tb1plqajp8ht8jq6g649d92w6dj9tewx4xrc3muec9vdukalzxxfkq3s2vk3nu
//funded with TxID 62d9b44b6a6268bd4a74ab2141133343849f012b694e0ff7c32b0b3f5e3c87fa
*/
Loading

0 comments on commit 1338f90

Please sign in to comment.