schnorr_sig 1.0.0.1 → 1.0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +8 -1
- data/VERSION +1 -1
- data/lib/schnorr_sig/fast.rb +7 -14
- data/lib/schnorr_sig/pure.rb +7 -12
- data/lib/schnorr_sig/utils.rb +15 -7
- data/schnorr_sig.gemspec +1 -0
- data/sig/fast.rbs +15 -0
- data/sig/pure.rbs +24 -0
- data/sig/utils.rbs +18 -0
- data/test/utils.rb +3 -3
- data/test/vectors.rb +1 -2
- data/test/vectors_extra.rb +12 -8
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 583aef17bbda178fd790a7cfddb29bdd1d38ee1d44092d3cb6afc2517a564a09
|
4
|
+
data.tar.gz: 299a66f0e042c200b81f902e23ab2adb712a6260532fb28a6e23555cfc414db6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb3a7ade41fcdd713ae0b9da0d8c0cf8c390f7c3da232e19e4be146f66bf45311aa19ab02025ff3b55e5dc11a000094817751aaf4dd2c4a9d7e9f93b455ff866
|
7
|
+
data.tar.gz: 0dd87d6af1595d3a51b4d1788bb3b24f3c1a11a0997ceb3fc0543e422b4dde75fa04e2b3f1ca4ec5cdafe3fbd9f23091c23cd9608e9ea338e7a21f5b515a6dcf
|
data/Rakefile
CHANGED
@@ -4,7 +4,6 @@ Rake::TestTask.new :test do |t|
|
|
4
4
|
t.test_files = [
|
5
5
|
'test/utils.rb',
|
6
6
|
'test/pure.rb',
|
7
|
-
'test/vectors.rb',
|
8
7
|
]
|
9
8
|
t.warning = true
|
10
9
|
end
|
@@ -17,6 +16,14 @@ Rake::TestTask.new :vectors do |t|
|
|
17
16
|
t.warning = true
|
18
17
|
end
|
19
18
|
|
19
|
+
Rake::TestTask.new :fast do |t|
|
20
|
+
t.test_files = [
|
21
|
+
'test/utils.rb',
|
22
|
+
'test/fast.rb',
|
23
|
+
]
|
24
|
+
t.warning = true
|
25
|
+
end
|
26
|
+
|
20
27
|
task default: :test
|
21
28
|
|
22
29
|
begin
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.1.1
|
data/lib/schnorr_sig/fast.rb
CHANGED
@@ -34,11 +34,10 @@ module SchnorrSig
|
|
34
34
|
# Output
|
35
35
|
# Secp256k1::KeyPair
|
36
36
|
def keypair_obj(sk = nil)
|
37
|
-
if sk
|
38
|
-
binary!(sk, KEY)
|
39
|
-
CONTEXT.key_pair_from_private_key(sk)
|
40
|
-
else
|
37
|
+
if sk.nil?
|
41
38
|
CONTEXT.generate_key_pair
|
39
|
+
else
|
40
|
+
CONTEXT.key_pair_from_private_key(binary!(sk, KEY))
|
42
41
|
end
|
43
42
|
end
|
44
43
|
|
@@ -69,8 +68,7 @@ module SchnorrSig
|
|
69
68
|
# Output
|
70
69
|
# Secp256k1::SchnorrSignature
|
71
70
|
def signature(str)
|
72
|
-
binary!(str, SIG)
|
73
|
-
Secp256k1::SchnorrSignature.from_data(str)
|
71
|
+
Secp256k1::SchnorrSignature.from_data(binary!(str, SIG))
|
74
72
|
end
|
75
73
|
|
76
74
|
# Input
|
@@ -78,10 +76,8 @@ module SchnorrSig
|
|
78
76
|
# The message, m: 32 byte hash value
|
79
77
|
# Output
|
80
78
|
# 64 bytes binary
|
81
|
-
def sign(sk, m)
|
82
|
-
|
83
|
-
CONTEXT.sign_schnorr(keypair_obj(sk), m).serialized
|
84
|
-
end
|
79
|
+
def sign(sk, m) = CONTEXT.sign_schnorr(keypair_obj(sk),
|
80
|
+
binary!(m, 32)).serialized
|
85
81
|
|
86
82
|
# Input
|
87
83
|
# The public key, pk: 32 bytes binary
|
@@ -112,10 +108,7 @@ module SchnorrSig
|
|
112
108
|
# msg: UTF-8 / binary / agnostic
|
113
109
|
# Output
|
114
110
|
# 32 bytes binary
|
115
|
-
def tagged_hash(tag, msg)
|
116
|
-
check!(tag, String) and check!(msg, String)
|
117
|
-
CONTEXT.tagged_sha256(tag, msg)
|
118
|
-
end
|
111
|
+
def tagged_hash(tag, msg) = CONTEXT.tagged_sha256(str!(tag), str!(msg))
|
119
112
|
end
|
120
113
|
|
121
114
|
Fast.include Utils
|
data/lib/schnorr_sig/pure.rb
CHANGED
@@ -5,8 +5,6 @@ autoload :SecureRandom, 'securerandom' # stdlib
|
|
5
5
|
# This implementation is based on the BIP340 spec: https://bips.xyz/340
|
6
6
|
module SchnorrSig
|
7
7
|
class SanityCheck < Error; end
|
8
|
-
class VerifyFail < Error; end
|
9
|
-
class InfinityPoint < Error; end
|
10
8
|
|
11
9
|
GROUP = ECDSA::Group::Secp256k1
|
12
10
|
P = GROUP.field.prime # smaller than 256**32
|
@@ -48,7 +46,7 @@ module SchnorrSig
|
|
48
46
|
when ECDSA::Point
|
49
47
|
# BIP340: The function bytes(P), where P is a point,
|
50
48
|
# returns bytes(x(P)).
|
51
|
-
val.infinity? ? raise(
|
49
|
+
val.infinity? ? raise(SanityCheck, val.inspect) : big2bin(val.x)
|
52
50
|
else
|
53
51
|
raise(SanityCheck, val.inspect)
|
54
52
|
end
|
@@ -89,15 +87,12 @@ module SchnorrSig
|
|
89
87
|
# Output
|
90
88
|
# 32 bytes binary
|
91
89
|
def tagged_hash(tag, msg)
|
92
|
-
check!(tag, String) and check!(msg, String)
|
93
|
-
warn("tag expected to be UTF-8") unless tag.encoding == Encoding::UTF_8
|
94
|
-
|
95
90
|
# BIP340: The function hash[name](x) where x is a byte array
|
96
91
|
# returns the 32-byte hash
|
97
92
|
# SHA256(SHA256(tag) || SHA256(tag) || x)
|
98
93
|
# where tag is the UTF-8 encoding of name.
|
99
|
-
tag_hash = Digest::SHA256.digest
|
100
|
-
Digest::SHA256.digest(tag_hash + tag_hash + msg)
|
94
|
+
tag_hash = Digest::SHA256.digest tag
|
95
|
+
Digest::SHA256.digest(tag_hash + tag_hash + str!(msg).b)
|
101
96
|
end
|
102
97
|
|
103
98
|
#
|
@@ -137,7 +132,7 @@ module SchnorrSig
|
|
137
132
|
# The signature, sig: 64 bytes binary
|
138
133
|
def sign(sk, m, auxrand: nil)
|
139
134
|
a = auxrand.nil? ? random_bytes(B) : auxrand
|
140
|
-
binary!(sk, KEY) and
|
135
|
+
binary!(sk, KEY) and str!(m) and binary!(a, B)
|
141
136
|
|
142
137
|
# BIP340: Let d' = int(sk)
|
143
138
|
# BIP340: Fail if d' = 0 or d' >= n
|
@@ -177,7 +172,7 @@ module SchnorrSig
|
|
177
172
|
# BIP340: Fail unless Verify(bytes(P), m, sig)
|
178
173
|
# BIP340: Return the signature sig
|
179
174
|
sig = bytes_r + bytes((k + e * d) % N)
|
180
|
-
raise(
|
175
|
+
raise(SanityCheck, "sig did not verify") unless verify?(bytes_p, m, sig)
|
181
176
|
sig
|
182
177
|
end
|
183
178
|
|
@@ -188,7 +183,7 @@ module SchnorrSig
|
|
188
183
|
# Output
|
189
184
|
# Boolean
|
190
185
|
def verify?(pk, m, sig)
|
191
|
-
binary!(pk, KEY) and
|
186
|
+
binary!(pk, KEY) and str!(m) and binary!(sig, SIG)
|
192
187
|
|
193
188
|
# BIP340: Let P = lift_x(int(pk))
|
194
189
|
p = lift_x(int(pk))
|
@@ -219,7 +214,7 @@ module SchnorrSig
|
|
219
214
|
def soft_verify?(pk, m, sig)
|
220
215
|
begin
|
221
216
|
verify?(pk, m, sig)
|
222
|
-
rescue SanityCheck
|
217
|
+
rescue SanityCheck
|
223
218
|
false
|
224
219
|
end
|
225
220
|
end
|
data/lib/schnorr_sig/utils.rb
CHANGED
@@ -1,21 +1,29 @@
|
|
1
1
|
module SchnorrSig
|
2
2
|
class Error < RuntimeError; end
|
3
|
-
class
|
3
|
+
class SpecError < Error; end
|
4
4
|
|
5
5
|
KEY = 32 # bytes
|
6
6
|
SIG = 64 # bytes
|
7
7
|
|
8
8
|
module Utils
|
9
|
-
# raise
|
9
|
+
# raise SpecError or return val
|
10
10
|
def check!(val, cls)
|
11
|
-
val.is_a?(cls) ? val : raise(
|
11
|
+
val.is_a?(cls) ? val : raise(SpecError, "#{cls}: #{val.inspect}")
|
12
12
|
end
|
13
13
|
|
14
|
-
# raise
|
14
|
+
# raise SpecError or return str
|
15
|
+
def str!(str, length = nil)
|
16
|
+
if check!(str, String) and !length.nil? and length != str.length
|
17
|
+
raise(SpecError, "Length #{str.length} should be #{length}")
|
18
|
+
end
|
19
|
+
str
|
20
|
+
end
|
21
|
+
|
22
|
+
# raise SpecError or return str
|
15
23
|
def binary!(str, length)
|
16
|
-
|
17
|
-
|
18
|
-
|
24
|
+
if str!(str, length).encoding != Encoding::BINARY
|
25
|
+
raise(SpecError, "Encoding: #{str.encoding}")
|
26
|
+
end
|
19
27
|
str
|
20
28
|
end
|
21
29
|
|
data/schnorr_sig.gemspec
CHANGED
data/sig/fast.rbs
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module SchnorrSig
|
2
|
+
CONTEXT: Secp256k1::Context
|
3
|
+
|
4
|
+
module Fast
|
5
|
+
def keypair_obj: (?String sk) -> Secp256k1::KeyPair
|
6
|
+
def extract_keys: (Secp256k1::KeyPair keypair_obj) -> [String, String]
|
7
|
+
def pubkey: (String sk) -> String
|
8
|
+
def keypair: -> [String, String]
|
9
|
+
def signature: (String str) -> Secp256k1::SchnorrSignature
|
10
|
+
def sign: (String sk, String m) -> String
|
11
|
+
def verify?: (String pk, String m, String sig) -> bool
|
12
|
+
def soft_verify?: (String pk, String m, String sig) -> bool
|
13
|
+
def tagged_hash: (String tag, String msg) -> String
|
14
|
+
end
|
15
|
+
end
|
data/sig/pure.rbs
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module SchnorrSig
|
2
|
+
class SanityCheck < Error
|
3
|
+
end
|
4
|
+
|
5
|
+
GROUP: ECDSA::Group::Secp256k1
|
6
|
+
P: Integer
|
7
|
+
N: Integer
|
8
|
+
B: Integer
|
9
|
+
|
10
|
+
module Pure
|
11
|
+
def random_bytes: (Integer count) -> String
|
12
|
+
def point: (Integer int) -> ECDSA::Point
|
13
|
+
def select_even_y: (ECDSA::Point point, Integer even_val) -> Integer
|
14
|
+
def int: (String x) -> Integer
|
15
|
+
def bytes: (Integer | ECDSA::Point val) -> String
|
16
|
+
def lift_x: (Integer x) -> ECDSA::Point
|
17
|
+
def tagged_hash: (String tag, String msg) -> String
|
18
|
+
def pubkey: (String sk) -> String
|
19
|
+
def keypair: -> [String, String]
|
20
|
+
def sign: (String sk, String m, ?auxrand: String?) -> String
|
21
|
+
def verify?: (String pk, String m, String sig) -> bool
|
22
|
+
def soft_verify?: (String pk, String m, String sig) -> bool
|
23
|
+
end
|
24
|
+
end
|
data/sig/utils.rbs
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module SchnorrSig
|
2
|
+
class Error < RuntimeError
|
3
|
+
end
|
4
|
+
class SpecError < Error
|
5
|
+
end
|
6
|
+
|
7
|
+
KEY: 32
|
8
|
+
SIG: 64
|
9
|
+
|
10
|
+
module Utils
|
11
|
+
def check!: (untyped val, Class cls) -> untyped
|
12
|
+
def binary!: (String str, Integer length) -> String
|
13
|
+
def bin2big: (String str) -> Integer
|
14
|
+
def big2bin: (Integer bignum) -> String
|
15
|
+
def bin2hex: (String str) -> String
|
16
|
+
def hex2bin: (String hex) -> String
|
17
|
+
end
|
18
|
+
end
|
data/test/utils.rb
CHANGED
@@ -10,17 +10,17 @@ describe Utils do
|
|
10
10
|
it "enforces the class of any object" do
|
11
11
|
expect(Utils.check!('123', String)).must_equal '123'
|
12
12
|
expect(Utils.check!(123, Integer)).must_equal 123
|
13
|
-
expect { Utils.check!([], String) }.must_raise
|
13
|
+
expect { Utils.check!([], String) }.must_raise SpecError
|
14
14
|
end
|
15
15
|
|
16
16
|
it "enforces binary strings: type, encoding, length" do
|
17
17
|
expect(Utils.binary!("\x00\x01".b, 2)).must_equal "\x00\x01".b
|
18
18
|
expect {
|
19
19
|
Utils.binary!("\x00\x01".b, 3)
|
20
|
-
}.must_raise
|
20
|
+
}.must_raise SpecError
|
21
21
|
expect {
|
22
22
|
Utils.binary!("\x00\x01", 2)
|
23
|
-
}.must_raise
|
23
|
+
}.must_raise SpecError
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
data/test/vectors.rb
CHANGED
@@ -18,7 +18,7 @@ table.each { |row|
|
|
18
18
|
|
19
19
|
result = begin
|
20
20
|
SchnorrSig.soft_verify?(pk, m, sig)
|
21
|
-
rescue SchnorrSig::
|
21
|
+
rescue SchnorrSig::SpecError
|
22
22
|
skip << row
|
23
23
|
next
|
24
24
|
end
|
@@ -37,4 +37,3 @@ puts "Failure: #{failure.count}"
|
|
37
37
|
puts "Skipped: #{skip.count}"
|
38
38
|
|
39
39
|
failure.each { |row| p row }
|
40
|
-
exit failure.count
|
data/test/vectors_extra.rb
CHANGED
@@ -31,18 +31,22 @@ table.each { |row|
|
|
31
31
|
# calculate a signature
|
32
32
|
begin
|
33
33
|
calc_sig = SchnorrSig.sign(sk, m)
|
34
|
-
|
35
|
-
|
34
|
+
sig_msg = (calc_sig == sig) ? "sig match" : "sig mismatch"
|
35
|
+
rescue SchnorrSig::SpecError
|
36
|
+
sig_msg = "sig error"
|
36
37
|
end
|
37
|
-
sig_msg = (calc_sig == sig) ? "sig match" : "sig mismatch"
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
if sig_msg != "sig error"
|
41
|
+
begin
|
42
|
+
result = SchnorrSig.soft_verify?(pk, m, sig)
|
43
|
+
verify_msg = (result == expected) ? "verify match" : "verify mismatch"
|
44
|
+
rescue SchnorrSig::SpecError
|
45
|
+
verify_msg = "verify error"
|
46
|
+
end
|
47
|
+
else
|
48
|
+
verify_msg = "sig error"
|
44
49
|
end
|
45
|
-
verify_msg = (result == expected) ? "verify match" : "verify mismatch"
|
46
50
|
puts [index, pk_msg, sig_msg, verify_msg, comment].join("\t")
|
47
51
|
}
|
48
52
|
puts
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: schnorr_sig
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rick Hull
|
@@ -38,6 +38,9 @@ files:
|
|
38
38
|
- lib/schnorr_sig/pure.rb
|
39
39
|
- lib/schnorr_sig/utils.rb
|
40
40
|
- schnorr_sig.gemspec
|
41
|
+
- sig/fast.rbs
|
42
|
+
- sig/pure.rbs
|
43
|
+
- sig/utils.rbs
|
41
44
|
- test/fast.rb
|
42
45
|
- test/pure.rb
|
43
46
|
- test/utils.rb
|