frostrb 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +20 -19
- data/lib/frost/dealer.rb +50 -0
- data/lib/frost/version.rb +1 -1
- data/lib/frost.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 971ebd8ce7d6cf7faae3a152a2ac1ab89af43ba8317276b1a9595610c543b5b5
|
4
|
+
data.tar.gz: 03b261ac549b8745c35ef35b61a9b5c63534d4043e2b287a58d680c79e510318
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96d05d23ca3dec9b7d187a4a6ffdda6c78e193d7bf9f56699e0abbb85926d3763a61ceb7b05952fcd16ad748f15a7ad1712c03719ba0ac75ae11613d80cda895
|
7
|
+
data.tar.gz: 8f7a5926c9c755704f8cc13260956c08d4a2ba1f46ed8e20e8ec8786cfc7fe70fad27250d03ce83e64168b99a100f4eaec42c96025e02d1ed1d665be88d998d2
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# FROST for Ruby [](https://github.com/azuchi/frostrb/actions/workflows/main.yml)
|
2
2
|
|
3
|
-
This library is ruby
|
3
|
+
This library is a ruby implementation of ['Two-Round Threshold Schnorr Signatures with FROST'](https://datatracker.ietf.org/doc/draft-irtf-cfrg-frost/).
|
4
4
|
|
5
5
|
Note: This library has not been security audited and tested widely, so should not be used in production.
|
6
6
|
|
@@ -37,20 +37,18 @@ require 'frost'
|
|
37
37
|
# Setup context.
|
38
38
|
ctx = FROST::Context.new(ECDSA::Group::Secp256k1, FROST::Type::RFC9591)
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
group_pubkey = secret.to_point
|
40
|
+
max_signers = 3
|
41
|
+
min_signers = 2
|
43
42
|
|
44
|
-
#
|
45
|
-
|
43
|
+
# Setup dealer.
|
44
|
+
dealer = FROST::Dealer.new(ctx, max_signers, min_signers)
|
45
|
+
group_pubkey = dealer.group_public_key
|
46
46
|
|
47
47
|
# Calculate secret shares.
|
48
|
-
share1 =
|
49
|
-
share2 = polynomial.gen_share(2)
|
50
|
-
share3 = polynomial.gen_share(3)
|
48
|
+
share1, _, share3 = dealer.gen_shares
|
51
49
|
|
52
50
|
# Round 1: Generate nonce and commitment
|
53
|
-
## each party
|
51
|
+
## each party generates hiding and binding nonce.
|
54
52
|
hiding_nonce1 = FROST::Nonce.gen_from_secret(share1)
|
55
53
|
binding_nonce1 = FROST::Nonce.gen_from_secret(share1)
|
56
54
|
hiding_nonce3 = FROST::Nonce.gen_from_secret(share3)
|
@@ -79,7 +77,7 @@ FROST.verify(sig, group_pubkey, msg)
|
|
79
77
|
|
80
78
|
### Bitcoin support
|
81
79
|
|
82
|
-
When using Bitcoin(taproot), the context type must be `FROST::Type::
|
80
|
+
When using Bitcoin(taproot), the context type must be `FROST::Type::TAPROOT` instead of `FROST::Type::RFC9591`.
|
83
81
|
|
84
82
|
```ruby
|
85
83
|
ctx = FROST::Context.new(ECDSA::Group::Secp256k1, FROST::Type::TAPROOT)
|
@@ -106,13 +104,13 @@ round1_outputs = {}
|
|
106
104
|
round1_outputs[i] = secret_package.public_package
|
107
105
|
end
|
108
106
|
|
109
|
-
# Each participant
|
107
|
+
# Each participant sends their commitments and proof to other participants.
|
110
108
|
received_package = {}
|
111
109
|
1.upto(max_signer) do |i|
|
112
110
|
received_package[i] = round1_outputs.select {|k, _| k != i}.values
|
113
111
|
end
|
114
112
|
|
115
|
-
# Each participant
|
113
|
+
# Each participant verifies knowledge of proof in a received package.
|
116
114
|
received_package.each do |id, packages|
|
117
115
|
secret_package = secret_packages[id]
|
118
116
|
packages.each do |package|
|
@@ -121,7 +119,7 @@ received_package.each do |id, packages|
|
|
121
119
|
end
|
122
120
|
|
123
121
|
# Round 2:
|
124
|
-
# Each participant
|
122
|
+
# Each participant generates a share for other participants and send it.
|
125
123
|
received_shares = {}
|
126
124
|
1.upto(max_signer) do |i|
|
127
125
|
secret_package = secret_packages[i] # own secret
|
@@ -140,7 +138,7 @@ end
|
|
140
138
|
end
|
141
139
|
end
|
142
140
|
|
143
|
-
# Each participant computes signing share.
|
141
|
+
# Each participant computes a signing share.
|
144
142
|
signing_shares = {}
|
145
143
|
1.upto(max_signer) do |i|
|
146
144
|
shares = received_shares[i].map{|_, share| share}
|
@@ -159,11 +157,14 @@ Using `FROST::Repairable` module, you can repair existing (or new) participant's
|
|
159
157
|
|
160
158
|
```ruby
|
161
159
|
ctx = FROST::Context.new(ECDSA::Group::Secp256k1, FROST::Type::RFC9591)
|
162
|
-
|
160
|
+
max_signers = 5
|
161
|
+
min_signers = 3
|
163
162
|
|
164
|
-
#
|
165
|
-
|
166
|
-
|
163
|
+
# Setup daler
|
164
|
+
dealer = FROST::SigningKey.generate(ctx, max_signers, min_signers)
|
165
|
+
|
166
|
+
# Dealer generates shares.
|
167
|
+
shares = dealer.gen_shares
|
167
168
|
|
168
169
|
# Signer 2 will lose their share
|
169
170
|
# Signers (helpers) 1, 4 and 5 will help signer 2 (participant) to recover their share
|
data/lib/frost/dealer.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module FROST
|
2
|
+
# Dealer
|
3
|
+
class Dealer
|
4
|
+
attr_reader :ctx
|
5
|
+
attr_reader :max_signers
|
6
|
+
attr_reader :min_signers
|
7
|
+
attr_reader :polynomial
|
8
|
+
|
9
|
+
# Create a new dealer.
|
10
|
+
# @param [FROST::Context] ctx FROST context.
|
11
|
+
# @param [Integer] max_signers Maximum number of signers.
|
12
|
+
# @return [FROST::Dealer]
|
13
|
+
# @raise [ArgumentError]
|
14
|
+
def initialize(ctx, max_signers, min_signers)
|
15
|
+
raise ArgumentError, "context must be FROST::Context." unless ctx.is_a?(FROST::Context)
|
16
|
+
raise ArgumentError, "min_signers must be Integer." unless min_signers.is_a?(Integer)
|
17
|
+
raise ArgumentError, "min_signers must be greater than 1." if min_signers < 2
|
18
|
+
raise ArgumentError, "max_signers must be Integer." unless max_signers.is_a?(Integer)
|
19
|
+
raise ArgumentError, "max_signers must be greater than or equal to min_signers." if max_signers < min_signers
|
20
|
+
@ctx = ctx
|
21
|
+
@min_signers = min_signers
|
22
|
+
@max_signers = max_signers
|
23
|
+
key = SigningKey.generate(ctx)
|
24
|
+
@polynomial = key.gen_poly(min_signers - 1)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Generate shares.
|
28
|
+
# @return [Array] Array of shares(FROST::SecretShare).
|
29
|
+
# @raise [ArgumentError]
|
30
|
+
def gen_shares(identifiers = nil)
|
31
|
+
raise ArgumentError, "identifiers must be Array." if identifiers && !identifiers.is_a?(Array)
|
32
|
+
identifiers = if identifiers
|
33
|
+
identifiers.each do |id|
|
34
|
+
raise ArgumentError, "identifier must be Integer." unless id.is_a?(Integer)
|
35
|
+
raise ArgumentError, "identifier must be greater than 0." if id < 1
|
36
|
+
end
|
37
|
+
identifiers
|
38
|
+
else
|
39
|
+
(1..max_signers).to_a
|
40
|
+
end
|
41
|
+
identifiers.map{ |i| polynomial.gen_share(i) }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get a group public key.
|
45
|
+
# @return [ECDSA::Point]
|
46
|
+
def group_public_key
|
47
|
+
polynomial.verification_point
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/frost/version.rb
CHANGED
data/lib/frost.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: frostrb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- azuchi
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-05-
|
10
|
+
date: 2025-05-17 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: ecdsa_ext
|
@@ -59,6 +59,7 @@ files:
|
|
59
59
|
- lib/frost.rb
|
60
60
|
- lib/frost/commitments.rb
|
61
61
|
- lib/frost/context.rb
|
62
|
+
- lib/frost/dealer.rb
|
62
63
|
- lib/frost/dkg.rb
|
63
64
|
- lib/frost/dkg/public_package.rb
|
64
65
|
- lib/frost/dkg/secret_package.rb
|