tapyrus 0.2.1 → 0.2.6

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.
@@ -30,7 +30,7 @@ module Tapyrus
30
30
  # @param [Integer] key_type a key type which determine address type.
31
31
  # @param [Boolean] compressed [Deprecated] whether public key is compressed.
32
32
  # @return [Tapyrus::Key] a key object.
33
- def initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true)
33
+ def initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true, allow_hybrid: false)
34
34
  puts "[Warning] Use key_type parameter instead of compressed. compressed parameter removed in the future." if key_type.nil? && !compressed.nil? && pubkey.nil?
35
35
  if key_type
36
36
  @key_type = key_type
@@ -41,13 +41,14 @@ module Tapyrus
41
41
  @secp256k1_module = Tapyrus.secp_impl
42
42
  @priv_key = priv_key
43
43
  if @priv_key
44
- raise ArgumentError, 'private key is not on curve' unless validate_private_key_range(@priv_key)
44
+ raise ArgumentError, Errors::Messages::INVALID_PRIV_KEY unless validate_private_key_range(@priv_key)
45
45
  end
46
46
  if pubkey
47
47
  @pubkey = pubkey
48
48
  else
49
49
  @pubkey = generate_pubkey(priv_key, compressed: compressed) if priv_key
50
50
  end
51
+ raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless fully_valid_pubkey?(allow_hybrid)
51
52
  end
52
53
 
53
54
  # generate key pair
@@ -65,7 +66,7 @@ module Tapyrus
65
66
  data = hex[2...-8].htb
66
67
  checksum = hex[-8..-1]
67
68
  raise ArgumentError, 'invalid version' unless version == Tapyrus.chain_params.privkey_version
68
- raise ArgumentError, 'invalid checksum' unless Tapyrus.calc_checksum(version + data.bth) == checksum
69
+ raise ArgumentError, Errors::Messages::INVALID_CHECKSUM unless Tapyrus.calc_checksum(version + data.bth) == checksum
69
70
  key_len = data.bytesize
70
71
  if key_len == COMPRESSED_PUBLIC_KEY_SIZE && data[-1].unpack('C').first == 1
71
72
  key_type = TYPES[:compressed]
@@ -95,10 +96,13 @@ module Tapyrus
95
96
  # @return [String] signature data with binary format
96
97
  def sign(data, low_r = true, extra_entropy = nil, algo: :ecdsa)
97
98
  raise ArgumentError, "Unsupported algorithm has been specified." unless SIG_ALGO.include?(algo)
98
- if algo == :ecdsa
99
+ case algo
100
+ when :ecdsa
99
101
  sign_ecdsa(data, low_r, extra_entropy)
102
+ when :schnorr
103
+ secp256k1_module.sign_data(data, priv_key, extra_entropy, algo: algo)
100
104
  else
101
- sign_schnorr(data)
105
+ false
102
106
  end
103
107
  end
104
108
 
@@ -111,11 +115,8 @@ module Tapyrus
111
115
  return false unless valid_pubkey?
112
116
  begin
113
117
  raise ArgumentError, "Unsupported algorithm has been specified." unless SIG_ALGO.include?(algo)
114
- if algo == :ecdsa
115
- verify_ecdsa_sig(sig, origin)
116
- else
117
- verify_schnorr_sig(sig, origin)
118
- end
118
+ sig = ecdsa_signature_parse_der_lax(sig) if algo == :ecdsa
119
+ secp256k1_module.verify_sig(origin, sig, pubkey, algo: algo)
119
120
  rescue Exception
120
121
  false
121
122
  end
@@ -223,14 +224,8 @@ module Tapyrus
223
224
  end
224
225
 
225
226
  # fully validate whether this is a valid public key (more expensive than IsValid())
226
- def fully_valid_pubkey?
227
- return false unless valid_pubkey?
228
- begin
229
- point = ECDSA::Format::PointOctetString.decode(pubkey.htb, ECDSA::Group::Secp256k1)
230
- ECDSA::Group::Secp256k1.valid_public_key?(point)
231
- rescue ECDSA::Format::DecodeError
232
- false
233
- end
227
+ def fully_valid_pubkey?(allow_hybrid = false)
228
+ valid_pubkey? && secp256k1_module.parse_ec_pubkey?(pubkey, allow_hybrid)
234
229
  end
235
230
 
236
231
  private
@@ -291,34 +286,18 @@ module Tapyrus
291
286
 
292
287
  # generate ecdsa signature
293
288
  def sign_ecdsa(data, low_r, extra_entropy)
294
- sig = secp256k1_module.sign_data(data, priv_key, extra_entropy)
289
+ sig = secp256k1_module.sign_data(data, priv_key, extra_entropy, algo: :ecdsa)
295
290
  if low_r && !sig_has_low_r?(sig)
296
291
  counter = 1
297
292
  until sig_has_low_r?(sig)
298
293
  extra_entropy = [counter].pack('I*').bth.ljust(64, '0').htb
299
- sig = secp256k1_module.sign_data(data, priv_key, extra_entropy)
294
+ sig = secp256k1_module.sign_data(data, priv_key, extra_entropy, algo: :ecdsa)
300
295
  counter += 1
301
296
  end
302
297
  end
303
298
  sig
304
299
  end
305
300
 
306
- # generate schnorr signature
307
- def sign_schnorr(msg)
308
- Schnorr.sign(msg, priv_key.to_i(16)).encode
309
- end
310
-
311
- # verify ecdsa signature
312
- def verify_ecdsa_sig(sig, message)
313
- sig = ecdsa_signature_parse_der_lax(sig)
314
- secp256k1_module.verify_sig(message, sig, pubkey)
315
- end
316
-
317
- # verify schnorr signature
318
- def verify_schnorr_sig(sig, message)
319
- Schnorr.valid_sig?(message, sig, pubkey.htb)
320
- end
321
-
322
301
  end
323
302
 
324
303
  end
@@ -19,7 +19,7 @@ module Tapyrus
19
19
 
20
20
  def self.parse_from_payload(payload)
21
21
  buf = StringIO.new(payload)
22
- header = Tapyrus::BlockHeader.parse_from_payload(buf.read(80))
22
+ header = Tapyrus::BlockHeader.parse_from_payload(buf)
23
23
  transactions = []
24
24
  unless buf.eof?
25
25
  tx_count = Tapyrus.unpack_var_int_from_io(buf)
@@ -21,7 +21,7 @@ module Tapyrus
21
21
 
22
22
  def self.parse_from_payload(payload)
23
23
  buf = StringIO.new(payload)
24
- header = Tapyrus::BlockHeader.parse_from_payload(buf.read(80))
24
+ header = Tapyrus::BlockHeader.parse_from_payload(buf)
25
25
  nonce = buf.read(8).unpack('q*').first
26
26
  short_ids_len = Tapyrus.unpack_var_int_from_io(buf)
27
27
  short_ids = short_ids_len.times.map do
@@ -19,7 +19,7 @@ module Tapyrus
19
19
  header_count = Tapyrus.unpack_var_int_from_io(buf)
20
20
  h = new
21
21
  header_count.times do
22
- h.headers << Tapyrus::BlockHeader.parse_from_payload(buf.read(80))
22
+ h.headers << Tapyrus::BlockHeader.parse_from_payload(buf)
23
23
  buf.read(1) # read tx count 0x00 (headers message doesn't include any tx.)
24
24
  end
25
25
  h
@@ -19,7 +19,7 @@ module Tapyrus
19
19
  def self.parse_from_payload(payload)
20
20
  m = new
21
21
  buf = StringIO.new(payload)
22
- m.header = Tapyrus::BlockHeader.parse_from_payload(buf.read(80))
22
+ m.header = Tapyrus::BlockHeader.parse_from_payload(buf)
23
23
  m.tx_count = buf.read(4).unpack('V').first
24
24
  hash_count = Tapyrus.unpack_var_int_from_io(buf)
25
25
  hash_count.times do
@@ -11,7 +11,6 @@ module Tapyrus
11
11
  best_block = node.chain.latest_block
12
12
  h[:headers] = best_block.height
13
13
  h[:bestblockhash] = best_block.header.block_id
14
- h[:chainwork] = best_block.header.work
15
14
  h[:mediantime] = node.chain.mtp(best_block.block_hash)
16
15
  h
17
16
  end
@@ -32,12 +31,14 @@ module Tapyrus
32
31
  hash: block_id,
33
32
  height: entry.height,
34
33
  features: entry.header.features,
35
- featuresHex: entry.header.features.to_even_length_hex,
34
+ featuresHex: entry.header.features.to_even_length_hex.ljust(8, '0'),
36
35
  merkleroot: entry.header.merkle_root.rhex,
36
+ immutablemerkleroot: entry.header.im_merkle_root.rhex,
37
37
  time: entry.header.time,
38
38
  mediantime: node.chain.mtp(block_hash),
39
- nonce: entry.header.nonce,
40
- bits: entry.header.bits.to_even_length_hex,
39
+ xfield_type: entry.header.x_field_type,
40
+ xfield: entry.header.x_field,
41
+ proof: entry.header.proof,
41
42
  previousblockhash: entry.prev_hash.rhex,
42
43
  nextblockhash: node.chain.next_hash(block_hash).rhex
43
44
  }
@@ -64,7 +64,7 @@ module Tapyrus
64
64
  response = http.request(request)
65
65
  body = response.body
66
66
  response = Tapyrus::Ext::JsonParser.new(body.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8') }).parse
67
- raise response['error'].to_s if response['error']
67
+ raise response['error'].to_json if response['error']
68
68
  response['result']
69
69
  end
70
70
 
@@ -43,6 +43,14 @@ module Tapyrus
43
43
  true
44
44
  end
45
45
 
46
+ def hash
47
+ to_payload.hash
48
+ end
49
+
50
+ def eql?(other)
51
+ to_payload.eql?(other.to_payload)
52
+ end
53
+
46
54
  private
47
55
 
48
56
  def initialize(type, payload)
@@ -53,11 +61,11 @@ module Tapyrus
53
61
 
54
62
  module ColoredOutput
55
63
  def colored?
56
- script_pubkey.cp2pkh? || script_pubkey.cp2sh?
64
+ script_pubkey.colored?
57
65
  end
58
66
 
59
67
  def color_id
60
- @color_id ||= ColorIdentifier.parse_from_payload(script_pubkey.chunks[0].pushed_data)
68
+ @color_id ||= script_pubkey.color_id
61
69
  end
62
70
 
63
71
  def reissuable?
@@ -75,6 +75,18 @@ module Tapyrus
75
75
  end
76
76
  end
77
77
 
78
+ # Remove color identifier from cp2pkh or cp2sh
79
+ # @param [ColorIdentifier] color identifier
80
+ # @return [Script] P2PKH or P2SH script
81
+ # @raise [RuntimeError] if script is neither cp2pkh nor cp2sh
82
+ def remove_color
83
+ raise RuntimeError, 'Only cp2pkh and cp2sh can remove color' unless cp2pkh? or cp2sh?
84
+
85
+ Tapyrus::Script.new.tap do |s|
86
+ s.chunks = self.chunks[2..-1]
87
+ end
88
+ end
89
+
78
90
  def get_multisig_pubkeys
79
91
  num = Tapyrus::Opcodes.opcode_to_small_int(chunks[-2].bth.to_i(16))
80
92
  (1..num).map{ |i| chunks[i].pushed_data }
@@ -214,6 +226,8 @@ module Tapyrus
214
226
  (chunks.size == 1 || chunks[1].opcode <= OP_16)
215
227
  end
216
228
 
229
+ # Return whether this script is a CP2PKH format script or not.
230
+ # @return [Boolean] true if this script is cp2pkh, otherwise false.
217
231
  def cp2pkh?
218
232
  return false unless chunks.size == 7
219
233
  return false unless chunks[0].bytesize == 34
@@ -223,6 +237,8 @@ module Tapyrus
223
237
  (chunks[2..3]+ chunks[5..6]).map(&:ord) && chunks[4].bytesize == 21
224
238
  end
225
239
 
240
+ # Return whether this script is a CP2SH format script or not.
241
+ # @return [Boolean] true if this script is cp2pkh, otherwise false.
226
242
  def cp2sh?
227
243
  return false unless chunks.size == 5
228
244
  return false unless chunks[0].bytesize == 34
@@ -230,6 +246,20 @@ module Tapyrus
230
246
  return false unless chunks[1].ord == OP_COLOR
231
247
  OP_HASH160 == chunks[2].ord && OP_EQUAL == chunks[4].ord && chunks[3].bytesize == 21
232
248
  end
249
+
250
+ # Return whether this script represents colored coin.
251
+ # @return [Boolean] true if this script is colored, otherwise false.
252
+ def colored?
253
+ cp2pkh? || cp2sh?
254
+ end
255
+
256
+ # Return color identifier for this script.
257
+ # @return [ColorIdentifer] color identifier for this script if this script is colored. return nil if this script is not colored.
258
+ def color_id
259
+ return nil unless colored?
260
+
261
+ Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)
262
+ end
233
263
 
234
264
  def op_return_data
235
265
  return nil unless op_return?
@@ -32,12 +32,16 @@ module Tapyrus
32
32
  # @param [String] pubkey a public key with hex format.
33
33
  # @param [String] digest a message digest with binary format to be verified.
34
34
  # @return [Boolean] if check is passed return true, otherwise false.
35
- def verify_sig(sig, pubkey, digest)
35
+ def verify_sig(sig, pubkey, digest, allow_hybrid: false)
36
36
  key_type = pubkey.start_with?('02') || pubkey.start_with?('03') ? Key::TYPES[:compressed] : Key::TYPES[:uncompressed]
37
37
  sig = sig.htb
38
38
  algo = sig.bytesize == 64 ? :schnorr : :ecdsa
39
- key = Key.new(pubkey: pubkey, key_type: key_type)
40
- key.verify(sig, digest, algo: algo)
39
+ begin
40
+ key = Key.new(pubkey: pubkey, key_type: key_type, allow_hybrid: allow_hybrid)
41
+ key.verify(sig, digest, algo: algo)
42
+ rescue Exception
43
+ false
44
+ end
41
45
  end
42
46
 
43
47
  def check_locktime(locktime)
@@ -4,8 +4,8 @@
4
4
  module Tapyrus
5
5
  module Secp256k1
6
6
 
7
- # binding for secp256k1 (https://github.com/bitcoin/bitcoin/tree/v0.14.2/src/secp256k1)
8
- # tag: v0.14.2
7
+ # binding for secp256k1 (https://github.com/chaintope/tapyrus-core/tree/v0.4.0/src/secp256k1)
8
+ # tag: v0.4.0
9
9
  # this is not included by default, to enable set shared object path to ENV['SECP256K1_LIB_PATH']
10
10
  # for linux, ENV['SECP256K1_LIB_PATH'] = '/usr/local/lib/libsecp256k1.so'
11
11
  # for mac,
@@ -50,6 +50,8 @@ module Tapyrus
50
50
  attach_function(:secp256k1_ecdsa_signature_parse_der, [:pointer, :pointer, :pointer, :size_t], :int)
51
51
  attach_function(:secp256k1_ecdsa_signature_normalize, [:pointer, :pointer, :pointer], :int)
52
52
  attach_function(:secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :pointer], :int)
53
+ attach_function(:secp256k1_schnorr_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
54
+ attach_function(:secp256k1_schnorr_verify, [:pointer, :pointer, :pointer, :pointer], :int)
53
55
  end
54
56
 
55
57
  def with_context(flags: (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN))
@@ -100,7 +102,68 @@ module Tapyrus
100
102
  # @param [String] privkey a private key using sign
101
103
  # @param [String] extra_entropy a extra entropy for rfc6979
102
104
  # @return [String] signature data with binary format
103
- def sign_data(data, privkey, extra_entropy)
105
+ def sign_data(data, privkey, extra_entropy, algo: :ecdsa)
106
+ case algo
107
+ when :ecdsa
108
+ sign_ecdsa(data, privkey, extra_entropy)
109
+ when :schnorr
110
+ sign_schnorr(data, privkey)
111
+ else
112
+ nil
113
+ end
114
+ end
115
+
116
+ # verify signature.
117
+ # @param[String] data a data.
118
+ # @param [String] sig signature data with binary format
119
+ # @param [String] pub_key a public key using verify.
120
+ def verify_sig(data, sig, pub_key, algo: :ecdsa)
121
+ case algo
122
+ when :ecdsa
123
+ verify_ecdsa(data, sig, pub_key)
124
+ when :schnorr
125
+ verify_schnorr(data, sig, pub_key)
126
+ else
127
+ false
128
+ end
129
+ end
130
+
131
+ # # validate whether this is a valid public key (more expensive than IsValid())
132
+ # @param [String] pub_key public key with hex format.
133
+ # @param [Boolean] allow_hybrid whether support hybrid public key.
134
+ # @return [Boolean] If valid public key return true, otherwise false.
135
+ def parse_ec_pubkey?(pub_key, allow_hybrid = false)
136
+ pub_key = pub_key.htb
137
+ return false if !allow_hybrid && ![0x02, 0x03, 0x04].include?(pub_key[0].ord)
138
+ with_context do |context|
139
+ pubkey = FFI::MemoryPointer.new(:uchar, pub_key.bytesize).put_bytes(0, pub_key)
140
+ internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
141
+ result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pub_key.bytesize)
142
+ result == 1
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ def generate_pubkey_in_context(context, privkey, compressed: true)
149
+ internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
150
+ result = secp256k1_ec_pubkey_create(context, internal_pubkey, privkey.htb)
151
+ raise 'error creating pubkey' unless result
152
+
153
+ pubkey = FFI::MemoryPointer.new(:uchar, 65)
154
+ pubkey_len = FFI::MemoryPointer.new(:uint64)
155
+ result = if compressed
156
+ pubkey_len.put_uint64(0, 33)
157
+ secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_COMPRESSED)
158
+ else
159
+ pubkey_len.put_uint64(0, 65)
160
+ secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_UNCOMPRESSED)
161
+ end
162
+ raise 'error serialize pubkey' unless result || pubkey_len.read_uint64 > 0
163
+ pubkey.read_string(pubkey_len.read_uint64).bth
164
+ end
165
+
166
+ def sign_ecdsa(data, privkey, extra_entropy)
104
167
  with_context do |context|
105
168
  secret = FFI::MemoryPointer.new(:uchar, privkey.htb.bytesize).put_bytes(0, privkey.htb)
106
169
  raise 'priv_key invalid' unless secp256k1_ec_seckey_verify(context, secret)
@@ -126,7 +189,19 @@ module Tapyrus
126
189
  end
127
190
  end
128
191
 
129
- def verify_sig(data, sig, pub_key)
192
+ def sign_schnorr(data, privkey)
193
+ with_context do |context|
194
+ secret = FFI::MemoryPointer.new(:uchar, privkey.htb.bytesize).put_bytes(0, privkey.htb)
195
+ raise 'priv_key invalid' unless secp256k1_ec_seckey_verify(context, secret)
196
+
197
+ signature = FFI::MemoryPointer.new(:uchar, 64)
198
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
199
+ raise 'Failed to generate schnorr signature.' unless secp256k1_schnorr_sign(context, signature, msg32, secret, nil, nil) == 1
200
+ signature.read_string(64)
201
+ end
202
+ end
203
+
204
+ def verify_ecdsa(data, sig, pub_key)
130
205
  with_context do |context|
131
206
  return false if data.bytesize == 0
132
207
 
@@ -150,25 +225,23 @@ module Tapyrus
150
225
  end
151
226
  end
152
227
 
153
- private
228
+ def verify_schnorr(data, sig, pub_key)
229
+ with_context do |context|
230
+ return false if data.bytesize == 0
231
+ pubkey = FFI::MemoryPointer.new(:uchar, pub_key.htb.bytesize).put_bytes(0, pub_key.htb)
232
+ internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
233
+ result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pubkey.size)
234
+ return false unless result
154
235
 
155
- def generate_pubkey_in_context(context, privkey, compressed: true)
156
- internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
157
- result = secp256k1_ec_pubkey_create(context, internal_pubkey, privkey.htb)
158
- raise 'error creating pubkey' unless result
236
+ signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig)
159
237
 
160
- pubkey = FFI::MemoryPointer.new(:uchar, 65)
161
- pubkey_len = FFI::MemoryPointer.new(:uint64)
162
- result = if compressed
163
- pubkey_len.put_uint64(0, 33)
164
- secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_COMPRESSED)
165
- else
166
- pubkey_len.put_uint64(0, 65)
167
- secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_UNCOMPRESSED)
168
- end
169
- raise 'error serialize pubkey' unless result || pubkey_len.read_uint64 > 0
170
- pubkey.read_string(pubkey_len.read_uint64).bth
238
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
239
+ result = secp256k1_schnorr_verify(context, signature, msg32, internal_pubkey)
240
+
241
+ result == 1
242
+ end
171
243
  end
244
+
172
245
  end
173
246
  end
174
247
  end
@@ -12,8 +12,8 @@ module Tapyrus
12
12
  private_key = 1 + SecureRandom.random_number(GROUP.order - 1)
13
13
  public_key = GROUP.generator.multiply_by_scalar(private_key)
14
14
  privkey = ECDSA::Format::IntegerOctetString.encode(private_key, 32)
15
- pubkey = ECDSA::Format::PointOctetString.encode(public_key, compression: compressed)
16
- [privkey.bth, pubkey.bth]
15
+ pubkey = public_key.to_hex(compressed)
16
+ [privkey.bth, pubkey]
17
17
  end
18
18
 
19
19
  # generate tapyrus key object
@@ -24,14 +24,71 @@ module Tapyrus
24
24
 
25
25
  def generate_pubkey(privkey, compressed: true)
26
26
  public_key = ECDSA::Group::Secp256k1.generator.multiply_by_scalar(privkey.to_i(16))
27
- ECDSA::Format::PointOctetString.encode(public_key, compression: compressed).bth
27
+ public_key.to_hex(compressed)
28
28
  end
29
29
 
30
30
  # sign data.
31
31
  # @param [String] data a data to be signed with binary format
32
32
  # @param [String] privkey a private key using sign
33
33
  # @return [String] signature data with binary format
34
- def sign_data(data, privkey, extra_entropy)
34
+ def sign_data(data, privkey, extra_entropy, algo: :ecdsa)
35
+ case algo
36
+ when :ecdsa
37
+ sign_ecdsa(data, privkey, extra_entropy)
38
+ when :schnorr
39
+ Schnorr.sign(data, privkey.to_i(16)).encode
40
+ else
41
+ nil
42
+ end
43
+ end
44
+
45
+ # verify signature using public key
46
+ # @param [String] digest a SHA-256 message digest with binary format
47
+ # @param [String] sig a signature for +data+ with binary format
48
+ # @param [String] pubkey a public key corresponding to the private key used for sign
49
+ # @return [Boolean] verify result
50
+ def verify_sig(digest, sig, pubkey, algo: :ecdsa)
51
+ case algo
52
+ when :ecdsa
53
+ verify_ecdsa(digest, sig, pubkey)
54
+ when :schnorr
55
+ Schnorr.valid_sig?(digest, sig, pubkey.htb)
56
+ else
57
+ false
58
+ end
59
+ end
60
+
61
+ alias :valid_sig? :verify_sig
62
+
63
+ module_function :valid_sig?
64
+
65
+ # validate whether this is a valid public key (more expensive than IsValid())
66
+ # @param [String] pubkey public key with hex format.
67
+ # @param [Boolean] allow_hybrid whether support hybrid public key.
68
+ # @return [Boolean] If valid public key return true, otherwise false.
69
+ def parse_ec_pubkey?(pubkey, allow_hybrid = false)
70
+ begin
71
+ point = ECDSA::Format::PointOctetString.decode(pubkey.htb, ECDSA::Group::Secp256k1, allow_hybrid: allow_hybrid)
72
+ ECDSA::Group::Secp256k1.valid_public_key?(point)
73
+ rescue ECDSA::Format::DecodeError
74
+ false
75
+ end
76
+ end
77
+
78
+ # if +pubkey+ is hybrid public key format, it convert uncompressed format.
79
+ # https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2012-June/001578.html
80
+ def repack_pubkey(pubkey)
81
+ p = pubkey.htb
82
+ case p[0]
83
+ when "\x06", "\x07"
84
+ p[0] = "\x04"
85
+ p
86
+ else
87
+ pubkey.htb
88
+ end
89
+ end
90
+
91
+ def sign_ecdsa(data, privkey, extra_entropy)
35
92
  privkey = privkey.htb
36
93
  private_key = ECDSA::Format::IntegerOctetString.decode(privkey)
37
94
  extra_entropy ||= ''
@@ -59,12 +116,7 @@ module Tapyrus
59
116
  signature
60
117
  end
61
118
 
62
- # verify signature using public key
63
- # @param [String] digest a SHA-256 message digest with binary format
64
- # @param [String] sig a signature for +data+ with binary format
65
- # @param [String] pubkey a public key corresponding to the private key used for sign
66
- # @return [Boolean] verify result
67
- def verify_sig(digest, sig, pubkey)
119
+ def verify_ecdsa(digest, sig, pubkey)
68
120
  begin
69
121
  k = ECDSA::Format::PointOctetString.decode(repack_pubkey(pubkey), GROUP)
70
122
  signature = ECDSA::Format::SignatureDerString.decode(sig)
@@ -74,23 +126,6 @@ module Tapyrus
74
126
  end
75
127
  end
76
128
 
77
- alias :valid_sig? :verify_sig
78
-
79
- module_function :valid_sig?
80
-
81
- # if +pubkey+ is hybrid public key format, it convert uncompressed format.
82
- # https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2012-June/001578.html
83
- def repack_pubkey(pubkey)
84
- p = pubkey.htb
85
- case p[0]
86
- when "\x06", "\x07"
87
- p[0] = "\x04"
88
- p
89
- else
90
- pubkey.htb
91
- end
92
- end
93
-
94
129
  end
95
130
 
96
131
  end