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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c22da99e3fed7eea60436b7007532b752066996c9f2dd87f1f71028d7eaec357
4
- data.tar.gz: 87bf98d7796bb6a31aad59e12914dd2f67584b852d45a7561e505061b3876284
3
+ metadata.gz: e68ad5d5ccf6213171279052ddef947229a8eab7597cc93ca2b8cc312d0e7254
4
+ data.tar.gz: b77fed1e8cc5dba386dcf6ce81bfe3d36c20985bd246b5c5d5e6f9ce45db6177
5
5
  SHA512:
6
- metadata.gz: aa3263695c27d8453971bb881c19f5b05c5d69bea52770823de956fc5cfe8d65fdd5393790fbd282eb8a78a13f56fbde3046f0222d9bbfefc668fedb099d150d
7
- data.tar.gz: f828993f5d2c149aebc61dbd51c3569bf3ba97fe71aaab5e5aafbf1c7742d880e55b2db6dfa698e7dae521c50eea5d43c7ace1eb1e750b389ab11e8c2a0d9991
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 initial proposal of Pieter Wuille's [bip-schnorr](https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki).
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 = 0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF
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 = ['e7758e20e67a0607433cf8a1f0383a02c7b56792aab71763173ab7085cab8768082aae167787bc3572d15176dc26c073d9b03726aed0b59c728aa6538dc03d57'].pack('H*')
58
+ signature = ['0E12B8C520948A776753A96F21ABD7FDC2D7D0C0DDC90851BE17B04E75EF86A47EF0DA46C4DC4D0D1BCB8668C2CE16C54C7C23A6716EDE303AF86774917CF928'].pack('H*')
59
59
 
60
- message = ['5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C'].pack('H*')
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.
@@ -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", "~> 10.0"
26
+ spec.add_development_dependency "rake", ">= 12.3.3"
27
27
  spec.add_development_dependency "rspec", "~> 3.0"
28
28
  end
@@ -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 (Integer) The 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
- p = GROUP.new_point(private_key)
20
- seckey = p.has_square_y? ? private_key : GROUP.order - private_key
21
- secret = ECDSA::Format::IntegerOctetString.encode(seckey, GROUP.byte_length)
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
- k0 = ECDSA::Format::IntegerOctetString.decode(tagged_hash('BIPSchnorrDerive', secret + message)) % GROUP.order
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
- e = create_challenge(r.x, p, message, GROUP)
31
-
32
- Schnorr::Signature.new(r.x, (k + e * seckey) % GROUP.order)
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, GROUP)
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, group)
80
- r_x = ECDSA::Format::IntegerOctetString.encode(x, group.byte_length)
81
- p_x = ECDSA::Format::IntegerOctetString.encode(p.x, group.byte_length)
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)
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Schnorr
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
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.1.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-01-22 00:00:00.000000000 Z
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: '10.0'
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: '10.0'
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.0.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.