Skip to content
/ frostrb Public

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

License

Notifications You must be signed in to change notification settings

azuchi/frostrb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

47 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FROST for Ruby Build Status

This library is ruby implementations 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:

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'

group = ECDSA::Group::Secp256k1

# Dealer generate secret.
secret = FROST::SigningKey.generate(group)
group_pubkey = secret.to_point

# Generate polynomial(f(x) = ax + b)
polynomial = secret.gen_poly(1)

# Calculate secret shares.
share1 = polynomial.gen_share(1)
share2 = polynomial.gen_share(2)
share3 = polynomial.gen_share(3)

# Round 1: Generate nonce and commitment
## each party generate hiding and binding nonce.
hiding_nonce1 = FROST::Nonce.gen_from_secret(share1)
binding_nonce1 = FROST::Nonce.gen_from_secret(share1)
hiding_nonce3 = FROST::Nonce.gen_from_secret(share3)
binding_nonce3 = FROST::Nonce.gen_from_secret(share3)

comm1 = 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(share1, group_pubkey, [hiding_nonce1, binding_nonce1], msg, commitment_list)
sig_share3 = FROST.sign(share3, group_pubkey, [hiding_nonce3, binding_nonce3], msg, commitment_list)

# verify signature share
FROST.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)

# Aggregation
sig = FROST.aggregate(commitment_list, msg, group_pubkey, [sig_share1, sig_share3])

# verify final signature
FROST.verify(sig, group_pubkey, msg)

Using DKG

DKG can be run as below.

max_signer = 5
min_signer = 3

secret_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(i, min_signer, max_signer, group)
  secret_packages[i] = secret_package
  round1_outputs[i] = secret_package.public_package
end

# 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 }.values
end

# Each participant verify knowledge of proof in received package.
received_package.each do |id, packages|
  secret_package = secret_packages[id]
  packages.each do |package|
    expect(FROST::DKG.verify_proof_of_knowledge(secret_package, package)).to be true
  end
end

# Round 2:
# Each participant generate share for other participants and send it.
received_shares = {}
1.upto(max_signer) do |i|
  secret_package = secret_packages[i] # own secret
  1.upto(max_signer) do |o|
    next if i == o
    received_shares[o] ||= []
    received_shares[o] << [i, secret_package.gen_share(o)]
  end
end

# Each participant verify received shares.
1.upto(max_signer) do |i|
  received_shares[i].each do |send_by, share|
    target_package = received_package[i].find { |package| package.identifier == send_by }
    expect(target_package.verify_share(share)).to be true
  end
end

# Each participant compute 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], 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.

# Dealer generate shares.
FROST::SigningKey.generate(ECDSA::Group::Secp256k1)
polynomial = dealer.gen_poly(min_signers - 1)
shares = 1.upto(max_signers).map {|identifier| polynomial.gen_share(identifier) }

# Signer 2 will lose their share
# Signers (helpers) 1, 4 and 5 will help signer 2 (participant) to recover their share
helper1 = 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.each do |helper_share|
  delta_values = FROST::Repairable.step1(helpers, participant_share.identifier, helper_share)
  delta_values.each do |target_id, value|
    received_values[target_id] ||= []
    received_values[target_id] << value
  end
end

# Each helper send sum value to participant.
participant_received_values = []
received_values.each do |_, values|
  participant_received_values << FROST::Repairable.step2(values, ECDSA::Group::Secp256k1)
end

# Participant can obtain his share.
repair_share = FROST::Repairable.step3(2, participant_received_values, ECDSA::Group::Secp256k1)

About

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

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Sponsor this project

 

Packages