-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
267 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
module FROST | ||
# Implements the Repairable Threshold Scheme (RTS) from <https://eprint.iacr.org/2017/1155> | ||
module Repairable | ||
module_function | ||
|
||
# Step 1 for RTS. | ||
# Each helper computes delta_i,j for other helpers. | ||
# @param [Array] helpers Array of helper's identifier. | ||
# @param [Integer] participant Identifier of the participant whose shares you want to restore. | ||
# @param [FROST::SecretShare] share Share of participant running this process. | ||
# @return [Hash] Hash with helper ID as key and value as delta value. | ||
def step1(helpers, participant, share) | ||
raise ArgumentError, "helpers must be greater than 1." if helpers.length < 2 | ||
raise ArgumentError, "participant must be greater than 1." if participant < 1 | ||
raise ArgumentError, "helpers has duplicate identifier." unless helpers.uniq.length == helpers.length | ||
raise ArgumentError, "helpers contains same identifier with participant." if helpers.include?(participant) | ||
|
||
field = ECDSA::PrimeField.new(share.group.order) | ||
random_values = (helpers.length - 1).times.map { SecureRandom.random_number(share.group.order - 1) } | ||
|
||
# compute last random value | ||
## Calculate Lagrange Coefficient for helper_i | ||
zeta_i = Polynomial.derive_interpolating_value(helpers, share.identifier, share.group, x: participant) | ||
lhs = field.mod(zeta_i * share.share) | ||
# last random value | ||
last = field.mod(lhs - random_values.sum) | ||
random_values << last | ||
|
||
helpers.zip(random_values).to_h | ||
end | ||
|
||
# Step 2 for RTS. | ||
# Each helper sum received delta values from other helpers. | ||
# @param [Array] step1_values Array of delta values. | ||
# @param [ECDSA::Group] group | ||
# @return [Integer] Sum of delta values. | ||
def step2(step1_values, group) | ||
raise ArgumentError, "group must be ECDSA::Group" unless group.is_a?(ECDSA::Group) | ||
|
||
field = ECDSA::PrimeField.new(group.order) | ||
field.mod(step1_values.sum) | ||
end | ||
|
||
# Participant compute own share with received sum of delta value. | ||
# @param [Integer] identifier Identifier of the participant whose shares you want to restore. | ||
# @param [Array] step2_results Array of Step 2 results received from other helpers. | ||
# @param [ECDSA::Group] group | ||
# @return | ||
def step3(identifier, step2_results, group) | ||
raise ArgumentError, "group must be ECDSA::Group" unless group.is_a?(ECDSA::Group) | ||
|
||
field = ECDSA::PrimeField.new(group.order) | ||
FROST::SecretShare.new(identifier, field.mod(step2_results.sum), group) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"scalar_generation": { | ||
"random_scalar_1": "72deefbbf904c44fe6bb41ad8fcd23e6de2bd950aa2bdd03a8d84fcf29a86e21", | ||
"random_scalar_2": "63d3c1867568bcf26c2c5525cbe2af1f689adfb0f7c8c2e769c1f8bca37e0e8f", | ||
"random_scalar_3": "90a1e9902329edc21bd8c15ceafa901ac028a543b4d06011667a29dfc377e5dd", | ||
"random_scalar_sum": "67549ad391976f036ec0583046aa63214a086397afad6177855aa7a8943b3d3c" | ||
}, | ||
"sigma_generation": { | ||
"sigma_1": "6234563e6c285de9e50569948c4648ef8205294a18f994a8f61e8c9f92c1b194", | ||
"sigma_2": "0c9f57342d4f20f05b3c61ac049230cad852ab541ef46394778aff6e7ff55700", | ||
"sigma_3": "1b50eb6a1b27bfbed21076913ca2892bc35a8be1c9f61e2cfa2e9c708869c049", | ||
"sigma_4": "44260f9f457d96bd0dcdcd9b83c45231bca28ecc5ab52dee9cf59f6b361c520c", | ||
"sigma_sum": "ce4aa87bfa1cd55620200f6d513f5517da54ef4c5c99445904cdc7e9d13d1ae9" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"scalar_generation": { | ||
"random_scalar_1": "1847f6c4a85096e5dbc9e200c9691c5164f8e276d32d4a54ebaf4275474a1403", | ||
"random_scalar_2": "eac5595269d108812eaa865bf62c703a2c128a61fa3bd4dc837b9314bc515204", | ||
"random_scalar_3": "5b3b6084e41c273a39a8d9bbbd87fbcd626c07030142bf78c6c91247bf175700", | ||
"random_scalar_sum": "5e48b09bf63dc6a1441d42187d1d885a38c896f51f633e6e76218944f27c7bc6" | ||
}, | ||
"sigma_generation": { | ||
"sigma_1": "ec3aa83140065181d75b746bfd6bbbbaf212bdfbb3a91670f924d1ca899cbc0c", | ||
"sigma_2": "5dd288d659e0a2dd3ef7523a9cc4f80f4a7f919e9980005c7fbec0961d3fb500", | ||
"sigma_3": "3e62e7461db9ca1ed2f1549a8114bbc87fa9242ce0012ed3f9ac9dcf23f4c30a", | ||
"sigma_4": "684c44e7aba416a1982a8db8ec2a3095f5cc6a3f958a4716b69ae76524dd7200", | ||
"sigma_sum": "f0bc5d356344d51f816ea8fa076fa029f7590120136bec7c6958b9081f7864d5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
require 'spec_helper' | ||
|
||
RSpec.describe FROST::Repairable do | ||
|
||
let(:group) { ECDSA::Group::Secp256k1 } | ||
let(:max_signers) { 5 } | ||
let(:min_signers) { 3 } | ||
let(:dealer) { FROST::SigningKey.generate(group) } | ||
|
||
shared_examples "Reparable Test" do | ||
it do | ||
# Dealer generate shares. | ||
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, group) | ||
end | ||
|
||
repair_share = FROST::Repairable.step3(2, participant_received_values, group) | ||
expect(repair_share.share).to eq(participant_share.share) | ||
end | ||
end | ||
|
||
describe "repair" do | ||
context "secp256k1" do | ||
let(:vector) { load_fixture("secp256k1/repair-share.json") } | ||
end | ||
it_behaves_like "Reparable Test", "secp256k1" | ||
end | ||
|
||
describe "step1" do | ||
it do | ||
# Dealer generate shares. | ||
polynomial = dealer.gen_poly(min_signers - 1) | ||
shares = 1.upto(max_signers).map {|identifier| polynomial.gen_share(identifier) } | ||
helper1 = shares[0] | ||
helper4 = shares[3] | ||
helper5 = shares[4] | ||
helper_shares = [helper1, helper4, helper5] | ||
helpers = helper_shares.map(&:identifier) | ||
participant = shares[1] | ||
|
||
# Generate deltas for helper 4 | ||
deltas = described_class.step1(helpers, participant.identifier, helper4) | ||
|
||
lagrange_coefficient = FROST::Polynomial.derive_interpolating_value(helpers, helper4.identifier, group, x: participant.identifier) | ||
|
||
field = ECDSA::PrimeField.new(group.order) | ||
rhs = field.mod(deltas.values.sum) | ||
lhs = field.mod(helper4.share * lagrange_coefficient) | ||
expect(rhs).to eq(lhs) | ||
end | ||
end | ||
|
||
shared_examples "repair share step2" do | ||
it do | ||
values = vectors['scalar_generation'] | ||
value1 = values['random_scalar_1'] | ||
value2 = values['random_scalar_2'] | ||
value3 = values['random_scalar_3'] | ||
expected = described_class.step2([value1, value2, value3].map(&:hex), group) | ||
expect(expected).to eq(values['random_scalar_sum'].hex) | ||
end | ||
end | ||
|
||
describe "#step2" do | ||
context "secp256k1" do | ||
let(:vectors) { load_fixture("secp256k1/repair-share.json") } | ||
it_behaves_like "repair share step2", "secp256k1" | ||
end | ||
context "P256" do | ||
let(:group) { ECDSA::Group::Secp256r1 } | ||
let(:vectors) { load_fixture("p256/repair-share.json") } | ||
it_behaves_like "repair share step2", "P256" | ||
end | ||
end | ||
|
||
shared_examples "repair share step3" do | ||
it do | ||
sigmas = vectors['sigma_generation'] | ||
sigma1 = sigmas['sigma_1'] | ||
sigma2 = sigmas['sigma_2'] | ||
sigma3 = sigmas['sigma_3'] | ||
sigma4 = sigmas['sigma_4'] | ||
|
||
expected = described_class.step3(2, [sigma1, sigma2, sigma3, sigma4].map(&:hex), group) | ||
expect(expected.share).to eq(sigmas['sigma_sum'].hex) | ||
expect(expected.identifier).to eq(2) | ||
end | ||
end | ||
|
||
describe "#step2" do | ||
context "secp256k1" do | ||
let(:vectors) { load_fixture("secp256k1/repair-share.json") } | ||
it_behaves_like "repair share step3", "secp256k1" | ||
end | ||
context "P256" do | ||
let(:group) { ECDSA::Group::Secp256r1 } | ||
let(:vectors) { load_fixture("p256/repair-share.json") } | ||
it_behaves_like "repair share step3", "P256" | ||
end | ||
end | ||
end |