bip-schnorr 0.1.0 → 0.2.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 +6 -4
- data/bip-schnorrrb.gemspec +1 -1
- data/lib/schnorr.rb +28 -18
- data/lib/schnorr/ec_point_ext.rb +20 -1
- data/lib/schnorr/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e68ad5d5ccf6213171279052ddef947229a8eab7597cc93ca2b8cc312d0e7254
|
4
|
+
data.tar.gz: b77fed1e8cc5dba386dcf6ce81bfe3d36c20985bd246b5c5d5e6f9ce45db6177
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d2a1ffd174074375af26fe2e4dfae18d6a72e9e44e76861d09e3724d3d13c4b90e182b2dc7533cefb93fbc44317cd92f4941716ae3e0fe549dc4e950e09b710
|
7
|
+
data.tar.gz: ebeba83783283f328b321ab19a5c3cebf4f74aa0d56c9f491283a502367a4baf29ea78f905b335f547f1cd8b6215395260c72981776d9e2743d3b4d03d408d7f
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
This is a Ruby implementation of the Schnorr signature scheme over the elliptic curve.
|
4
4
|
This implementation relies on the [ecdsa gem](https://github.com/DavidEGrayson/ruby_ecdsa) for operate elliptic curves.
|
5
5
|
|
6
|
-
The code is based upon the
|
6
|
+
The code is based upon the [BIP340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
|
7
7
|
|
8
8
|
## Installation
|
9
9
|
|
@@ -28,7 +28,7 @@ Or install it yourself as:
|
|
28
28
|
```ruby
|
29
29
|
require 'schnorr'
|
30
30
|
|
31
|
-
private_key =
|
31
|
+
private_key = ['B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF'].pack("H*")
|
32
32
|
|
33
33
|
message = ['5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C'].pack('H*')
|
34
34
|
|
@@ -55,9 +55,9 @@ require 'schnorr'
|
|
55
55
|
# public key does not start with 02 or 03.
|
56
56
|
public_key = ['DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659'].pack('H*')
|
57
57
|
|
58
|
-
signature = ['
|
58
|
+
signature = ['0E12B8C520948A776753A96F21ABD7FDC2D7D0C0DDC90851BE17B04E75EF86A47EF0DA46C4DC4D0D1BCB8668C2CE16C54C7C23A6716EDE303AF86774917CF928'].pack('H*')
|
59
59
|
|
60
|
-
message = ['
|
60
|
+
message = ['243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89'].pack('H*')
|
61
61
|
|
62
62
|
# verify signature.(result is true or false)
|
63
63
|
result = Schnorr.valid_sig?(message, public_key, signature)
|
@@ -73,4 +73,6 @@ This library changes the following functions of `ecdsa` gem in `lib/schnorr/ec_p
|
|
73
73
|
* `ECDSA::Point` class has following two instance methods.
|
74
74
|
* `#has_square_y?` check this point does not infinity and square?(y coordinate)
|
75
75
|
* `#square?(x)` check whether `x` is a quadratic residue modulo p.
|
76
|
+
* `#has_even_y?` check the y-coordinate of this point is an even.
|
77
|
+
* `#encode(only_x = false)` encode this point into a binary string.
|
76
78
|
* `ECDSA::Format::PointOctetString#decode` supports decoding only from x coordinate.
|
data/bip-schnorrrb.gemspec
CHANGED
@@ -23,6 +23,6 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_runtime_dependency "ecdsa", "~> 1.2.0"
|
24
24
|
|
25
25
|
spec.add_development_dependency "bundler"
|
26
|
-
spec.add_development_dependency "rake", "
|
26
|
+
spec.add_development_dependency "rake", ">= 12.3.3"
|
27
27
|
spec.add_development_dependency "rspec", "~> 3.0"
|
28
28
|
end
|
data/lib/schnorr.rb
CHANGED
@@ -11,25 +11,32 @@ module Schnorr
|
|
11
11
|
|
12
12
|
# Generate schnorr signature.
|
13
13
|
# @param message (String) A message to be signed with binary format.
|
14
|
-
# @param private_key (
|
14
|
+
# @param private_key (String) The private key with binary format.
|
15
|
+
# @param aux_rand (String) The auxiliary random data with binary format. If not specified, SecureRandom is used to generate a random value.
|
15
16
|
# (The number of times to add the generator point to itself to get the public key.)
|
16
17
|
# @return (Schnorr::Signature)
|
17
|
-
def sign(message, private_key)
|
18
|
+
def sign(message, private_key, aux_rand = SecureRandom.bytes(32))
|
18
19
|
raise 'The message must be a 32-byte array.' unless message.bytesize == 32
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
d0 = private_key.unpack('H*').first.to_i(16)
|
21
|
+
raise 'private_key must be an integer in the range 1..n-1.' unless 0 < d0 && d0 <= (GROUP.order - 1)
|
22
|
+
raise 'aux_rand must be 32 bytes.' unless aux_rand.bytesize == 32
|
22
23
|
|
23
|
-
|
24
|
+
p = GROUP.new_point(d0)
|
25
|
+
d = p.has_even_y? ? d0 : GROUP.order - d0
|
26
|
+
|
27
|
+
t = d ^ tagged_hash('BIP340/aux', aux_rand).unpack('H*').first.to_i(16)
|
28
|
+
t = ECDSA::Format::IntegerOctetString.encode(t, GROUP.byte_length)
|
29
|
+
|
30
|
+
k0 = ECDSA::Format::IntegerOctetString.decode(tagged_hash('BIP340/nonce', t + p.encode(true) + message)) % GROUP.order
|
24
31
|
raise 'Creation of signature failed. k is zero' if k0.zero?
|
25
32
|
|
26
33
|
r = GROUP.new_point(k0)
|
27
|
-
|
28
34
|
k = r.has_square_y? ? k0 : GROUP.order - k0
|
35
|
+
e = create_challenge(r.x, p, message)
|
29
36
|
|
30
|
-
|
31
|
-
|
32
|
-
|
37
|
+
sig = Schnorr::Signature.new(r.x, (k + e * d) % GROUP.order)
|
38
|
+
raise 'The created signature does not pass verification.' unless valid_sig?(message, p.encode(true), sig.encode)
|
39
|
+
sig
|
33
40
|
end
|
34
41
|
|
35
42
|
# Verifies the given {Signature} and returns true if it is valid.
|
@@ -49,18 +56,20 @@ module Schnorr
|
|
49
56
|
# @param signature (String) The signature with binary format.
|
50
57
|
# @return (Boolean)
|
51
58
|
def check_sig!(message, public_key, signature)
|
59
|
+
raise InvalidSignatureError, 'The message must be a 32-byte array.' unless message.bytesize == 32
|
60
|
+
raise InvalidSignatureError, 'The public key must be a 32-byte array.' unless public_key.bytesize == 32
|
61
|
+
|
62
|
+
|
52
63
|
sig = Schnorr::Signature.decode(signature)
|
53
64
|
pubkey = ECDSA::Format::PointOctetString.decode(public_key, GROUP)
|
54
65
|
field = GROUP.field
|
55
66
|
|
56
|
-
raise Schnorr::InvalidSignatureError, 'Invalid signature: r is not in the field.' unless field.include?(sig.r)
|
57
|
-
raise Schnorr::InvalidSignatureError, 'Invalid signature: s is not in the field.' unless field.include?(sig.s)
|
58
67
|
raise Schnorr::InvalidSignatureError, 'Invalid signature: r is zero.' if sig.r.zero?
|
59
68
|
raise Schnorr::InvalidSignatureError, 'Invalid signature: s is zero.' if sig.s.zero?
|
60
69
|
raise Schnorr::InvalidSignatureError, 'Invalid signature: r is larger than field size.' if sig.r >= field.prime
|
61
70
|
raise Schnorr::InvalidSignatureError, 'Invalid signature: s is larger than group order.' if sig.s >= GROUP.order
|
62
71
|
|
63
|
-
e = create_challenge(sig.r, pubkey, message
|
72
|
+
e = create_challenge(sig.r, pubkey, message)
|
64
73
|
|
65
74
|
r = GROUP.new_point(sig.s) + pubkey.multiply_by_scalar(GROUP.order - e)
|
66
75
|
|
@@ -74,15 +83,16 @@ module Schnorr
|
|
74
83
|
# create signature digest.
|
75
84
|
# @param (Integer) x a x coordinate for R.
|
76
85
|
# @param (ECDSA::Point) p a public key.
|
77
|
-
# @param (ECDSA::Group) group the group of elliptic curve.
|
78
86
|
# @return (Integer) digest e.
|
79
|
-
def create_challenge(x, p, message
|
80
|
-
r_x = ECDSA::Format::IntegerOctetString.encode(x,
|
81
|
-
|
82
|
-
(ECDSA.normalize_digest(tagged_hash('BIPSchnorr', r_x + p_x + message), group.bit_length)) % group.order
|
87
|
+
def create_challenge(x, p, message)
|
88
|
+
r_x = ECDSA::Format::IntegerOctetString.encode(x, GROUP.byte_length)
|
89
|
+
(ECDSA.normalize_digest(tagged_hash('BIP340/challenge', r_x + p.encode(true) + message), GROUP.bit_length)) % GROUP.order
|
83
90
|
end
|
84
91
|
|
85
92
|
# Generate tagged hash value.
|
93
|
+
# @param (String) tag tag value.
|
94
|
+
# @param (String) msg the message to be hashed.
|
95
|
+
# @return (String) the hash value with binary format.
|
86
96
|
def tagged_hash(tag, msg)
|
87
97
|
tag_hash = Digest::SHA256.digest(tag)
|
88
98
|
Digest::SHA256.digest(tag_hash + tag_hash + msg)
|
data/lib/schnorr/ec_point_ext.rb
CHANGED
@@ -8,6 +8,12 @@ module ECDSA
|
|
8
8
|
!infinity? && square?(y)
|
9
9
|
end
|
10
10
|
|
11
|
+
# Check the y-coordinate of this point is an even.
|
12
|
+
# @return (Boolean) if even, return true.
|
13
|
+
def has_even_y?
|
14
|
+
y.even?
|
15
|
+
end
|
16
|
+
|
11
17
|
# Check whether +x+ is a quadratic residue modulo p.
|
12
18
|
# @param x (Integer)
|
13
19
|
# @return (Boolean)
|
@@ -15,6 +21,16 @@ module ECDSA
|
|
15
21
|
x.pow((group.field.prime - 1) / 2, group.field.prime) == 1
|
16
22
|
end
|
17
23
|
|
24
|
+
# Encode this point into a binary string.
|
25
|
+
# @param (Boolean) only_x whether or not to encode only X-coordinate. default is false.
|
26
|
+
def encode(only_x = false)
|
27
|
+
if only_x
|
28
|
+
ECDSA::Format::FieldElementOctetString.encode(x, group.field)
|
29
|
+
else
|
30
|
+
ECDSA::Format::PointOctetString.encode(self, {compression: true})
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
18
34
|
end
|
19
35
|
|
20
36
|
module Format
|
@@ -43,12 +59,15 @@ module ECDSA
|
|
43
59
|
end
|
44
60
|
|
45
61
|
# decode from x coordinate.
|
62
|
+
# @param (String) x_string X-coordinate binary string
|
63
|
+
# @param (ECDSA::Group) group A group of elliptic curves to use.
|
64
|
+
# @return (ECDSA::Point) decoded point.
|
46
65
|
def self.decode_from_x(x_string, group)
|
47
66
|
x = ECDSA::Format::FieldElementOctetString.decode(x_string, group.field)
|
48
67
|
y_sq = group.field.mod(x.pow(3, group.field.prime) + 7)
|
49
68
|
y = y_sq.pow((group.field.prime + 1)/4, group.field.prime)
|
50
69
|
raise DecodeError, 'Public key not on the curve.' unless y.pow(2, group.field.prime) == y_sq
|
51
|
-
finish_decode(x, y, group)
|
70
|
+
finish_decode(x, y.even? ? y : group.field.prime - y, group)
|
52
71
|
end
|
53
72
|
|
54
73
|
end
|
data/lib/schnorr/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bip-schnorr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- azuchi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-05-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ecdsa
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 12.3.3
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 12.3.3
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
requirements: []
|
112
|
-
rubygems_version: 3.
|
112
|
+
rubygems_version: 3.1.2
|
113
113
|
signing_key:
|
114
114
|
specification_version: 4
|
115
115
|
summary: The ruby implementation of bip-schnorr.
|