bip-schnorr 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|