Skip to content

Repository files navigation

FROST for Ruby Build Status

This library is a ruby implementation of 'Two-Round Threshold Schnorr Signatures with FROST'.

Note: This library has not been security audited and tested widely, so should not be used in production.

The cipher suites currently supported by this library are:

Note: The implementation for Taproot is based on frost-secp256k1-tr, but since it is not an official BIP, it may change in the future.

Installation

Add this line to your application's Gemfile:

gem'frostrb',require: 'frost'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install frostrb

Usage

require'frost'# Setup context.ctx=FROST::Context.new(ECDSA::Group::Secp256k1,FROST::Type::RFC9591)max_signers=3min_signers=2# Setup dealer.dealer=FROST::Dealer.new(ctx,max_signers,min_signers)group_pubkey=dealer.group_public_key# Calculate secret shares.share1,_,share3=dealer.gen_shares# Round 1: Generate nonce and commitment## each party generates hiding and binding nonce.hiding_nonce1,binding_nonce1=share1.generate_nonceshiding_nonce3,binding_nonce3=share3.generate_noncescomm1=FROST::Commitments.new(1,hiding_nonce1.to_point,binding_nonce1.to_point)comm3=FROST::Commitments.new(3,hiding_nonce3.to_point,binding_nonce3.to_point)commitment_list=[comm1,comm3]msg=["74657374"].pack("H*")# Round 2: each participant generates their signature share(1 and 3)sig_share1=FROST.sign(ctx,share1,group_pubkey,[hiding_nonce1,binding_nonce1],msg,commitment_list)sig_share3=FROST.sign(ctx,share3,group_pubkey,[hiding_nonce3,binding_nonce3],msg,commitment_list)# verify signature shareFROST.verify_share(1,share1.to_point,sig_share1,commitment_list,group_pubkey,msg)FROST.verify_share(3,share3.to_point,sig_share3,commitment_list,group_pubkey,msg)# Aggregationsig=FROST.aggregate(ctx,commitment_list,msg,group_pubkey,[sig_share1,sig_share3])# verify final signatureFROST.verify(sig,group_pubkey,msg)

Bitcoin support

When using Bitcoin(taproot), the context type must be FROST::Type::TAPROOT instead of FROST::Type::RFC9591.

ctx=FROST::Context.new(ECDSA::Group::Secp256k1,FROST::Type::TAPROOT)

Using DKG

DKG can be run as below.

# Setup context.ctx=FROST::Context.new(ECDSA::Group::Secp256k1,FROST::Type::RFC9591)max_signer=5min_signer=3secret_packages={}round1_outputs={}# Round 1:# For each participant, perform the first part of the DKG protocol.1.upto(max_signer)do |i|
secret_package=FROST::DKG.generate_secret(ctx,i,min_signer,max_signer)secret_packages[i]=secret_packageround1_outputs[i]=secret_package.public_packageend# Each participant sends their commitments and proof to other participants.received_package={}1.upto(max_signer)do |i|
received_package[i]=round1_outputs.select{|k,_| k != i}.valuesend# Each participant verifies knowledge of proof in a received package.received_package.eachdo |id,packages|
secret_package=secret_packages[id]packages.eachdo |package|
expect(FROST::DKG.verify_proof_of_knowledge(secret_package,package)).tobetrueendend# Round 2:# Each participant generates a share for other participants and send it.received_shares={}1.upto(max_signer)do |i|
secret_package=secret_packages[i]# own secret1.upto(max_signer)do |o|
nextifi == oreceived_shares[o] ||= []received_shares[o] << [i,secret_package.gen_share(o)]endend# Each participant verify received shares.1.upto(max_signer)do |i|
received_shares[i].eachdo |send_by,share|
target_package=received_package[i].find{ |package| package.identifier == send_by}expect(target_package.verify_share(share)).tobetrueendend# Each participant computes a signing share.signing_shares={}1.upto(max_signer)do |i|
shares=received_shares[i].map{|_,share| share}signing_shares[i]=FROST::DKG.compute_signing_share(secret_packages[i],received_package[i],shares)end# Participant 1 compute group public key.group_pubkey=FROST::DKG.compute_group_pubkey(secret_packages[1],received_package[1])# The subsequent signing phase is the same as above with signing_shares as the secret.

Share repair

Using FROST::Repairable module, you can repair existing (or new) participant's share with the cooperation of T participants.

ctx=FROST::Context.new(ECDSA::Group::Secp256k1,FROST::Type::RFC9591)max_signers=5min_signers=3# Setup dalerdealer=FROST::SigningKey.generate(ctx,max_signers,min_signers)# Dealer generates shares.shares=dealer.gen_shares# Signer 2 will lose their share# Signers (helpers) 1, 4 and 5 will help signer 2 (participant) to recover their sharehelper1=shares[0]helper4=shares[3]helper5=shares[4]helper_shares=[helper1,helper4,helper5]helpers=helper_shares.map(&:identifier)participant_share=shares[1]# Each helper computes delta values.received_values={}helper_shares.eachdo |helper_share|
delta_values=FROST::Repairable.step1(helpers,participant_share.identifier,helper_share)delta_values.eachdo |target_id,value|
received_values[target_id] ||= []received_values[target_id] << valueendend# Each helper send sum value to participant.participant_received_values=[]received_values.eachdo |_,values|
participant_received_values << FROST::Repairable.step2(ctx,values)end# Participant can get his share.repair_share=FROST::Repairable.step3(ctx,2,participant_received_values)

About

Ruby implementations of Two-Round Threshold Schnorr Signatures with FROST.

Topics

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages