btcruby 0.0.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +18 -0
- data/.travis.yml +7 -0
- data/FAQ.md +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +18 -0
- data/HOWTO.md +17 -0
- data/LICENSE +19 -0
- data/README.md +59 -0
- data/Rakefile +6 -0
- data/TODO.txt +40 -0
- data/bin/console +19 -0
- data/btcruby.gemspec +20 -0
- data/documentation/address.md +73 -0
- data/documentation/base58.md +52 -0
- data/documentation/block.md +127 -0
- data/documentation/block_header.md +120 -0
- data/documentation/constants.md +88 -0
- data/documentation/data.md +54 -0
- data/documentation/diagnostics.md +90 -0
- data/documentation/extensions.md +76 -0
- data/documentation/hash_functions.md +58 -0
- data/documentation/hash_id.md +22 -0
- data/documentation/index.md +230 -0
- data/documentation/key.md +177 -0
- data/documentation/keychain.md +180 -0
- data/documentation/network.md +75 -0
- data/documentation/opcode.md +220 -0
- data/documentation/openssl.md +7 -0
- data/documentation/p2pkh.md +71 -0
- data/documentation/p2sh.md +64 -0
- data/documentation/proof_of_work.md +84 -0
- data/documentation/script.md +280 -0
- data/documentation/signature.md +71 -0
- data/documentation/transaction.md +213 -0
- data/documentation/transaction_builder.md +188 -0
- data/documentation/transaction_input.md +133 -0
- data/documentation/transaction_output.md +130 -0
- data/documentation/wif.md +72 -0
- data/documentation/wire_format.md +70 -0
- data/lib/btcruby/address.rb +296 -0
- data/lib/btcruby/base58.rb +108 -0
- data/lib/btcruby/big_number.rb +47 -0
- data/lib/btcruby/block.rb +170 -0
- data/lib/btcruby/block_header.rb +231 -0
- data/lib/btcruby/constants.rb +59 -0
- data/lib/btcruby/currency_formatter.rb +64 -0
- data/lib/btcruby/data.rb +98 -0
- data/lib/btcruby/diagnostics.rb +92 -0
- data/lib/btcruby/errors.rb +8 -0
- data/lib/btcruby/extensions.rb +65 -0
- data/lib/btcruby/hash_functions.rb +54 -0
- data/lib/btcruby/hash_id.rb +18 -0
- data/lib/btcruby/key.rb +517 -0
- data/lib/btcruby/keychain.rb +464 -0
- data/lib/btcruby/network.rb +73 -0
- data/lib/btcruby/opcode.rb +197 -0
- data/lib/btcruby/open_assets/asset.rb +35 -0
- data/lib/btcruby/open_assets/asset_address.rb +49 -0
- data/lib/btcruby/open_assets/asset_definition.rb +75 -0
- data/lib/btcruby/open_assets/asset_id.rb +24 -0
- data/lib/btcruby/open_assets/asset_marker.rb +94 -0
- data/lib/btcruby/open_assets/asset_processor.rb +377 -0
- data/lib/btcruby/open_assets/asset_transaction.rb +184 -0
- data/lib/btcruby/open_assets/asset_transaction_builder/errors.rb +15 -0
- data/lib/btcruby/open_assets/asset_transaction_builder/provider.rb +32 -0
- data/lib/btcruby/open_assets/asset_transaction_builder/result.rb +47 -0
- data/lib/btcruby/open_assets/asset_transaction_builder.rb +418 -0
- data/lib/btcruby/open_assets/asset_transaction_input.rb +64 -0
- data/lib/btcruby/open_assets/asset_transaction_output.rb +140 -0
- data/lib/btcruby/open_assets.rb +26 -0
- data/lib/btcruby/openssl.rb +536 -0
- data/lib/btcruby/proof_of_work.rb +110 -0
- data/lib/btcruby/safety.rb +26 -0
- data/lib/btcruby/script.rb +733 -0
- data/lib/btcruby/signature_hashtype.rb +37 -0
- data/lib/btcruby/transaction.rb +511 -0
- data/lib/btcruby/transaction_builder/errors.rb +15 -0
- data/lib/btcruby/transaction_builder/provider.rb +54 -0
- data/lib/btcruby/transaction_builder/result.rb +73 -0
- data/lib/btcruby/transaction_builder/signer.rb +28 -0
- data/lib/btcruby/transaction_builder.rb +520 -0
- data/lib/btcruby/transaction_input.rb +298 -0
- data/lib/btcruby/transaction_outpoint.rb +30 -0
- data/lib/btcruby/transaction_output.rb +315 -0
- data/lib/btcruby/version.rb +3 -0
- data/lib/btcruby/wif.rb +118 -0
- data/lib/btcruby/wire_format.rb +362 -0
- data/lib/btcruby.rb +44 -2
- data/sample_code/creating_a_p2sh_multisig_address.rb +21 -0
- data/sample_code/creating_a_transaction_manually.rb +44 -0
- data/sample_code/generating_an_address.rb +20 -0
- data/sample_code/using_transaction_builder.rb +49 -0
- data/spec/address_spec.rb +206 -0
- data/spec/all.rb +6 -0
- data/spec/base58_spec.rb +83 -0
- data/spec/block_header_spec.rb +18 -0
- data/spec/block_spec.rb +18 -0
- data/spec/currency_formatter_spec.rb +46 -0
- data/spec/data_spec.rb +50 -0
- data/spec/diagnostics_spec.rb +41 -0
- data/spec/key_spec.rb +205 -0
- data/spec/keychain_spec.rb +261 -0
- data/spec/network_spec.rb +48 -0
- data/spec/open_assets/asset_address_spec.rb +33 -0
- data/spec/open_assets/asset_id_spec.rb +15 -0
- data/spec/open_assets/asset_marker_spec.rb +47 -0
- data/spec/open_assets/asset_processor_spec.rb +567 -0
- data/spec/open_assets/asset_transaction_builder_spec.rb +273 -0
- data/spec/open_assets/asset_transaction_spec.rb +70 -0
- data/spec/proof_of_work_spec.rb +53 -0
- data/spec/script_spec.rb +66 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/transaction_builder_spec.rb +338 -0
- data/spec/transaction_spec.rb +162 -0
- data/spec/wire_format_spec.rb +283 -0
- metadata +141 -7
@@ -0,0 +1,536 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
# This is a collection of binding to OpenSSL that are missing in standard library in Ruby 2.0.
|
4
|
+
# You need an 'ffi' gem to make it work.
|
5
|
+
module BTC
|
6
|
+
module OpenSSL
|
7
|
+
include ::FFI::Library
|
8
|
+
extend self
|
9
|
+
|
10
|
+
if FFI::Platform.windows?
|
11
|
+
ffi_lib 'libeay32', 'ssleay32'
|
12
|
+
else
|
13
|
+
ffi_lib 'ssl'
|
14
|
+
end
|
15
|
+
|
16
|
+
NID_secp256k1 = 714
|
17
|
+
|
18
|
+
POINT_CONVERSION_COMPRESSED = 0x02
|
19
|
+
POINT_CONVERSION_UNCOMPRESSED = 0x04
|
20
|
+
|
21
|
+
attach_function :SSL_library_init, [], :int
|
22
|
+
attach_function :ERR_load_crypto_strings, [], :void
|
23
|
+
attach_function :SSL_load_error_strings, [], :void
|
24
|
+
attach_function :RAND_poll, [], :int
|
25
|
+
|
26
|
+
attach_function :BN_CTX_free, [:pointer], :int
|
27
|
+
attach_function :BN_CTX_new, [], :pointer
|
28
|
+
attach_function :BN_new, [], :pointer
|
29
|
+
attach_function :BN_free, [:pointer], :int
|
30
|
+
attach_function :BN_copy, [:pointer, :pointer], :pointer
|
31
|
+
attach_function :BN_dup, [:pointer], :pointer
|
32
|
+
attach_function :BN_bin2bn, [:pointer, :int, :pointer], :pointer
|
33
|
+
attach_function :BN_bn2bin, [:pointer, :pointer], :void
|
34
|
+
attach_function :BN_num_bits, [:pointer], :int
|
35
|
+
attach_function :BN_cmp, [:pointer, :pointer], :int
|
36
|
+
attach_function :BN_set_word, [:pointer, :int], :int
|
37
|
+
attach_function :BN_add, [:pointer, :pointer, :pointer], :int
|
38
|
+
attach_function :BN_sub, [:pointer, :pointer, :pointer], :int
|
39
|
+
attach_function :BN_div, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
|
40
|
+
attach_function :BN_mod_inverse, [:pointer, :pointer, :pointer, :pointer], :pointer
|
41
|
+
attach_function :BN_mod_mul, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
|
42
|
+
attach_function :BN_mod_add, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
|
43
|
+
attach_function :BN_mod_add_quick, [:pointer, :pointer, :pointer, :pointer], :int
|
44
|
+
attach_function :BN_mod_sub, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
|
45
|
+
attach_function :BN_mul_word, [:pointer, :int], :int
|
46
|
+
attach_function :BN_rshift, [:pointer, :pointer, :int], :int
|
47
|
+
attach_function :BN_rshift1, [:pointer, :pointer], :int
|
48
|
+
|
49
|
+
attach_function :EC_GROUP_new_by_curve_name, [:int], :pointer
|
50
|
+
attach_function :EC_GROUP_free, [:pointer], :void
|
51
|
+
attach_function :EC_GROUP_get_curve_GFp, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
|
52
|
+
attach_function :EC_GROUP_get_degree, [:pointer], :int
|
53
|
+
attach_function :EC_GROUP_get_order, [:pointer, :pointer, :pointer], :int
|
54
|
+
attach_function :EC_GROUP_get0_generator, [:pointer], :pointer
|
55
|
+
|
56
|
+
attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
|
57
|
+
attach_function :EC_KEY_free, [:pointer], :void
|
58
|
+
attach_function :EC_KEY_get0_group, [:pointer], :pointer
|
59
|
+
attach_function :EC_KEY_get0_private_key, [:pointer], :pointer
|
60
|
+
attach_function :EC_KEY_set_conv_form, [:pointer, :int], :void
|
61
|
+
attach_function :EC_KEY_set_private_key, [:pointer, :pointer], :int
|
62
|
+
attach_function :EC_KEY_set_public_key, [:pointer, :pointer], :int
|
63
|
+
|
64
|
+
attach_function :d2i_ECPrivateKey, [:pointer, :pointer, :long], :pointer
|
65
|
+
attach_function :i2d_ECPrivateKey, [:pointer, :pointer], :int
|
66
|
+
attach_function :o2i_ECPublicKey, [:pointer, :pointer, :long], :pointer
|
67
|
+
attach_function :i2o_ECPublicKey, [:pointer, :pointer], :uint
|
68
|
+
|
69
|
+
attach_function :EC_POINT_new, [:pointer], :pointer
|
70
|
+
attach_function :EC_POINT_free, [:pointer], :int
|
71
|
+
attach_function :EC_POINT_is_at_infinity, [:pointer, :pointer], :int
|
72
|
+
attach_function :EC_POINT_mul, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
|
73
|
+
attach_function :EC_POINT_set_compressed_coordinates_GFp, [:pointer, :pointer, :pointer, :int, :pointer], :int
|
74
|
+
attach_function :EC_POINT_copy, [:pointer, :pointer], :int
|
75
|
+
attach_function :EC_POINT_get_affine_coordinates_GFp, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
|
76
|
+
attach_function :EC_POINT_point2bn, [:pointer, :pointer, :int, :pointer, :pointer], :pointer
|
77
|
+
attach_function :EC_POINT_bn2point, [:pointer, :pointer, :pointer, :pointer], :pointer
|
78
|
+
|
79
|
+
attach_function :ECDSA_SIG_new, [], :pointer
|
80
|
+
attach_function :ECDSA_SIG_free, [:pointer], :void
|
81
|
+
attach_function :ECDSA_do_sign, [:pointer, :uint, :pointer], :pointer
|
82
|
+
attach_function :ECDSA_verify, [:int, :pointer, :int, :pointer, :int, :pointer], :int
|
83
|
+
|
84
|
+
attach_function :i2d_ECDSA_SIG, [:pointer, :pointer], :int
|
85
|
+
attach_function :d2i_ECDSA_SIG, [:pointer, :pointer, :long], :pointer
|
86
|
+
|
87
|
+
def BN_num_bytes(a) # in openssl this is defined by a macro
|
88
|
+
(BN_num_bits(a)+7)/8
|
89
|
+
end
|
90
|
+
|
91
|
+
def prepare_if_needed
|
92
|
+
if !@prepared_openssl
|
93
|
+
SSL_library_init()
|
94
|
+
ERR_load_crypto_strings()
|
95
|
+
SSL_load_error_strings()
|
96
|
+
RAND_poll()
|
97
|
+
@prepared_openssl = true
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def group
|
102
|
+
@group ||= EC_GROUP_new_by_curve_name(NID_secp256k1)
|
103
|
+
end
|
104
|
+
|
105
|
+
def group_order
|
106
|
+
@group_order ||= begin
|
107
|
+
n = BN_new()
|
108
|
+
bn_ctx = BN_CTX_new()
|
109
|
+
EC_GROUP_get_order(self.group, n, bn_ctx)
|
110
|
+
BN_CTX_free(bn_ctx)
|
111
|
+
n
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def group_half_order
|
116
|
+
@group_half_order ||= begin
|
117
|
+
halforder = BN_new()
|
118
|
+
BN_rshift1(halforder, self.group_order)
|
119
|
+
halforder
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Creates autorelease pool from which various objects can be created.
|
124
|
+
# When block returns, pool deallocates all created objects.
|
125
|
+
# Available methods on pool instance:
|
126
|
+
# - ec_key - last EC_KEY (created lazily if needed)
|
127
|
+
# - group - group of the ec_key
|
128
|
+
# - bn_ctx - lazily created single instance of BN_CTX
|
129
|
+
# - new_ec_key - creates new instance of EC_KEY
|
130
|
+
# - new_bn - creates new instance of BIGNUM
|
131
|
+
# - new_ec_point - creates new instance of EC_POINT
|
132
|
+
def autorelease(&block) # {|pool| }
|
133
|
+
prepare_if_needed
|
134
|
+
result = nil
|
135
|
+
begin
|
136
|
+
pool = AutoreleasePool.new
|
137
|
+
result = yield(pool)
|
138
|
+
ensure
|
139
|
+
pool.drain
|
140
|
+
end
|
141
|
+
result
|
142
|
+
end
|
143
|
+
|
144
|
+
def public_key_with_compression(pubkey, compressed)
|
145
|
+
raise ArgumentError, "Public key is missing" if !pubkey
|
146
|
+
|
147
|
+
autorelease do |pool|
|
148
|
+
|
149
|
+
eckey = pool.new_ec_key
|
150
|
+
|
151
|
+
# 1. Load EC_KEY with pubkey binary data.
|
152
|
+
buf = FFI::MemoryPointer.from_string(pubkey)
|
153
|
+
eckey = o2i_ECPublicKey(pointer_to_pointer(eckey), pointer_to_pointer(buf), buf.size-1)
|
154
|
+
if eckey.null?
|
155
|
+
raise BTCError, "OpenSSL failed to create EC_KEY with public key: #{BTC::Data.hex_from_data(pubkey).inspect}"
|
156
|
+
end
|
157
|
+
|
158
|
+
# 2. Extract re-compressed pubkey from EC_KEY
|
159
|
+
EC_KEY_set_conv_form(eckey, compressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED);
|
160
|
+
|
161
|
+
length = i2o_ECPublicKey(eckey, nil)
|
162
|
+
buf = FFI::MemoryPointer.new(:uint8, length)
|
163
|
+
if i2o_ECPublicKey(eckey, pointer_to_pointer(buf)) == length
|
164
|
+
public_key = buf.read_string(length)
|
165
|
+
else
|
166
|
+
raise BTCError, "OpenSSL failed to regenerate a public key."
|
167
|
+
end
|
168
|
+
|
169
|
+
public_key
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Returns a pair of private key, public key
|
174
|
+
def regenerate_keypair(private_key, public_key_compressed: false)
|
175
|
+
|
176
|
+
autorelease do |pool|
|
177
|
+
|
178
|
+
eckey = pool.new_ec_key
|
179
|
+
|
180
|
+
priv_bn = pool.new_bn(private_key)
|
181
|
+
|
182
|
+
pub_key = pool.new_ec_point
|
183
|
+
EC_POINT_mul(self.group, pub_key, priv_bn, nil, nil, pool.bn_ctx)
|
184
|
+
EC_KEY_set_private_key(eckey, priv_bn)
|
185
|
+
EC_KEY_set_public_key(eckey, pub_key)
|
186
|
+
|
187
|
+
length = i2d_ECPrivateKey(eckey, nil)
|
188
|
+
buf = FFI::MemoryPointer.new(:uint8, length)
|
189
|
+
if i2d_ECPrivateKey(eckey, pointer_to_pointer(buf)) == length
|
190
|
+
# We have a full DER representation of private key, it contains a length
|
191
|
+
# of a private key at offset 8 and private key at offset 9.
|
192
|
+
size = buf.get_array_of_uint8(8, 1)[0]
|
193
|
+
private_key2 = buf.get_array_of_uint8(9, size).pack("C*").rjust(32, "\x00")
|
194
|
+
else
|
195
|
+
raise BTCError, "OpenSSL failed to convert private key to DER format"
|
196
|
+
end
|
197
|
+
|
198
|
+
if private_key2 != private_key
|
199
|
+
raise BTCError, "OpenSSL somehow regenerated a wrong private key."
|
200
|
+
end
|
201
|
+
|
202
|
+
EC_KEY_set_conv_form(eckey, public_key_compressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED);
|
203
|
+
|
204
|
+
length = i2o_ECPublicKey(eckey, nil)
|
205
|
+
buf = FFI::MemoryPointer.new(:uint8, length)
|
206
|
+
if i2o_ECPublicKey(eckey, pointer_to_pointer(buf)) == length
|
207
|
+
public_key = buf.read_string(length)
|
208
|
+
else
|
209
|
+
raise BTCError, "OpenSSL failed to regenerate a public key."
|
210
|
+
end
|
211
|
+
|
212
|
+
[ private_key2, public_key ]
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Returns k value computed deterministically from message hash and privkey.
|
217
|
+
# See https://tools.ietf.org/html/rfc6979
|
218
|
+
def rfc6979_ecdsa_nonce(hash, privkey)
|
219
|
+
raise ArgumentError, "Hash must be 32 bytes long" if hash.bytesize != 32
|
220
|
+
raise ArgumentError, "Private key must be 32 bytes long" if privkey.bytesize != 32
|
221
|
+
|
222
|
+
autorelease do |pool|
|
223
|
+
order = self.group_order
|
224
|
+
|
225
|
+
# Step 3.2.a. hash = H(message). Already performed by the caller.
|
226
|
+
|
227
|
+
# Step 3.2.b. V = 0x01 0x01 0x01 ... 0x01 (32 bytes equal 0x01)
|
228
|
+
v = "\x01".b*32
|
229
|
+
|
230
|
+
# Step 3.2.c. K = 0x00 0x00 0x00 ... 0x00 (32 bytes equal 0x00)
|
231
|
+
k = "\x00".b*32
|
232
|
+
|
233
|
+
# Step 3.2.d. K = HMAC-SHA256(key: K, data: V || 0x00 || int2octets(privkey) || bits2octets(hash))
|
234
|
+
h1 = pool.new_bn(hash)
|
235
|
+
BN_div(nil, h1, h1, order, pool.bn_ctx) # h1 = h1 % order
|
236
|
+
h1data = data_from_bn(h1, min_length: 32)
|
237
|
+
k = BTC.hmac_sha256(key: k, data: v + "\x00".b + privkey + h1data)
|
238
|
+
|
239
|
+
# Step 3.2.e. V = HMAC-SHA256(key: K, data: V)
|
240
|
+
v = BTC.hmac_sha256(key: k, data: v)
|
241
|
+
|
242
|
+
# Step 3.2.f. K = HMAC-SHA256(key: K, data: V || 0x01 || int2octets(privkey) || bits2octets(hash))
|
243
|
+
k = BTC.hmac_sha256(key: k, data: v + "\x01".b + privkey + h1data)
|
244
|
+
|
245
|
+
# Step 3.2.g. V = HMAC-SHA256(key: K, data: V)
|
246
|
+
v = BTC.hmac_sha256(key: k, data: v)
|
247
|
+
|
248
|
+
# Step 3.2.h.
|
249
|
+
zero32 = "\x00".b*32
|
250
|
+
10000.times do
|
251
|
+
t = BTC.hmac_sha256(key: k, data: v)
|
252
|
+
tn = pool.new_bn(t)
|
253
|
+
if BN_cmp(tn, order) < 0
|
254
|
+
nonce = data_from_bn(tn, min_length: 32)
|
255
|
+
if nonce != zero32
|
256
|
+
return nonce
|
257
|
+
end
|
258
|
+
end
|
259
|
+
# Note: the probability of not succeeding at the first try is about 2^-127.
|
260
|
+
k = BTC.hmac_sha256(key: k, data: v + zero32)
|
261
|
+
v = BTC.hmac_sha256(key: k, data: v)
|
262
|
+
end
|
263
|
+
# we generated 10000 numbers, none of them is good -> fail.
|
264
|
+
raise "Cannot find any good ECDSA nonce after 10000 iterations of RFC6979."
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Computes a deterministic ECDSA signature with canonical (lowest) S value.
|
269
|
+
# Nonce k is equal to HMAC-SHA256(data: hash, key: privkey)
|
270
|
+
def ecdsa_signature(hash, privkey, normalized: true)
|
271
|
+
raise ArgumentError, "Hash is missing" if !hash
|
272
|
+
raise ArgumentError, "Cannot make a ECDSA signature without the private key" if !privkey
|
273
|
+
|
274
|
+
# ECDSA signature is a pair of numbers: (Kx, s)
|
275
|
+
# Where Kx = x coordinate of k*G mod n (n is the order of secp256k1).
|
276
|
+
# And s = (k^-1)*(h + Kx*privkey).
|
277
|
+
# By default, k is chosen randomly on interval [0, n - 1].
|
278
|
+
# But this makes signatures harder to test and allows faulty or
|
279
|
+
# backdoored RNGs to leak private keys from ECDSA signatures.
|
280
|
+
# To avoid these issues, we'll generate k = Hash256(hash || privatekey)
|
281
|
+
# and make all computations by hand.
|
282
|
+
|
283
|
+
autorelease do |pool|
|
284
|
+
|
285
|
+
# Order of our curve
|
286
|
+
n = self.group_order
|
287
|
+
halfn = self.group_half_order
|
288
|
+
|
289
|
+
# Generate k deterministically from private key and message using HMAC-SHA256
|
290
|
+
# This is an important point #1.
|
291
|
+
kdata = rfc6979_ecdsa_nonce(hash, privkey)
|
292
|
+
k = pool.new_bn(kdata)
|
293
|
+
|
294
|
+
# Enforce k within group order: k = k % n
|
295
|
+
BN_div(nil, k, k, n, pool.bn_ctx)
|
296
|
+
|
297
|
+
# Compute K = k*G
|
298
|
+
#(can't use K variable name because Ruby does not allow
|
299
|
+
# constant assignment in methods)
|
300
|
+
kG = pool.new_ec_point
|
301
|
+
EC_POINT_mul(self.group, kG, k, nil, nil, pool.bn_ctx)
|
302
|
+
|
303
|
+
# Compute r = K.x. This is first half of the signature.
|
304
|
+
r = pool.new_bn
|
305
|
+
EC_POINT_get_affine_coordinates_GFp(self.group, kG, r, nil, pool.bn_ctx)
|
306
|
+
|
307
|
+
# Compute s = (k^-1)*(h + r*privkey).
|
308
|
+
h = pool.new_bn(hash)
|
309
|
+
p = pool.new_bn(privkey)
|
310
|
+
tmp = pool.new_bn
|
311
|
+
s = pool.new_bn
|
312
|
+
BN_mod_mul(tmp, r, p, n, pool.bn_ctx) # tmp = r*privkey
|
313
|
+
BN_mod_add_quick(s, tmp, h, n) # s = h + tmp = h + r*privkey
|
314
|
+
BN_mod_inverse(k, k, n, pool.bn_ctx) # k' = k^-1
|
315
|
+
BN_mod_mul(s, s, k, n, pool.bn_ctx) # s = k'*(h + r*privkey)
|
316
|
+
|
317
|
+
# Enforce low S values, by negating the value (modulo the order) if above order/2.
|
318
|
+
# This is an important point #2. Not doing that would yield (sometimes)
|
319
|
+
# non-canonical signatures that will be rejected by many relaying nodes.
|
320
|
+
if normalized
|
321
|
+
if BN_cmp(s, halfn) > 0
|
322
|
+
BN_sub(s, n, s)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# Fill in ECDSA_SIG structure so we can convert it into a proper DER format.
|
327
|
+
sig = ECDSA_SIG.new
|
328
|
+
sig[:r] = r
|
329
|
+
sig[:s] = s
|
330
|
+
|
331
|
+
# Encode signature in DER format.
|
332
|
+
|
333
|
+
sig_size = 72 # typical size of a signature (when both numbers are 33 bytes).
|
334
|
+
|
335
|
+
# allocate a bit more memory just in case (cargo cult)
|
336
|
+
buffer = FFI::MemoryPointer.new(:uint8, sig_size + 16)
|
337
|
+
sig_size = i2d_ECDSA_SIG(sig.pointer, pointer_to_pointer(buffer))
|
338
|
+
|
339
|
+
# read actual number of bytes composed by OpenSSL
|
340
|
+
signature = buffer.read_string(sig_size)
|
341
|
+
signature
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
# Normalizes S value of the signature and returns normalized signature.
|
346
|
+
# Returns nil if signature is completely invalid.
|
347
|
+
def ecdsa_normalized_signature(signature)
|
348
|
+
raise ArgumentError, "Signature is missing" if !signature
|
349
|
+
|
350
|
+
autorelease do |pool|
|
351
|
+
|
352
|
+
# Order of our curve
|
353
|
+
n = self.group_order
|
354
|
+
halfn = self.group_half_order
|
355
|
+
|
356
|
+
# ECDSA_SIG *psig = NULL;
|
357
|
+
# d2i_ECDSA_SIG(&psig, &input, vchSig.size());
|
358
|
+
buf = FFI::MemoryPointer.from_string(signature)
|
359
|
+
psig = d2i_ECDSA_SIG(nil, pointer_to_pointer(buf), buf.size-1)
|
360
|
+
if psig.null?
|
361
|
+
raise BTCError, "OpenSSL failed to read ECDSA signature with DER: #{BTC::Data.hex_from_data(signature).inspect}"
|
362
|
+
end
|
363
|
+
|
364
|
+
sig = ECDSA_SIG.new(psig) # read sig from its pointer
|
365
|
+
s = sig[:s]
|
366
|
+
|
367
|
+
# Enforce low S values, by negating the value (modulo the order) if above order/2.
|
368
|
+
if BN_cmp(s, halfn) > 0
|
369
|
+
BN_sub(s, n, s)
|
370
|
+
end
|
371
|
+
|
372
|
+
# Note: we'll place new s value back to s bignum,
|
373
|
+
# so we don't need another sig structure.
|
374
|
+
|
375
|
+
# Encode signature in DER format.
|
376
|
+
sig_size = 72 # typical size of a signature (when both numbers are 33 bytes).
|
377
|
+
|
378
|
+
# allocate a bit more memory just in case (cargo cult)
|
379
|
+
buffer = FFI::MemoryPointer.new(:uint8, sig_size + 16)
|
380
|
+
sig_size = i2d_ECDSA_SIG(sig.pointer, pointer_to_pointer(buffer))
|
381
|
+
|
382
|
+
# read actual number of bytes composed by OpenSSL
|
383
|
+
signature = buffer.read_string(sig_size)
|
384
|
+
|
385
|
+
# Free the signature created by d2i_ECDSA_SIG above.
|
386
|
+
ECDSA_SIG_free(psig)
|
387
|
+
|
388
|
+
signature
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
def ecdsa_verify(signature, hash, public_key)
|
393
|
+
raise ArgumentError, "Signature is missing" if !signature
|
394
|
+
raise ArgumentError, "Hash is missing" if !hash
|
395
|
+
raise ArgumentError, "Public key is missing" if !public_key
|
396
|
+
|
397
|
+
autorelease do |pool|
|
398
|
+
eckey = pool.new_ec_key
|
399
|
+
|
400
|
+
buf = FFI::MemoryPointer.from_string(public_key)
|
401
|
+
eckey = o2i_ECPublicKey(pointer_to_pointer(eckey), pointer_to_pointer(buf), buf.size - 1)
|
402
|
+
if eckey.null?
|
403
|
+
raise BTCError, "OpenSSL failed to create EC_KEY with public key: #{BTC::Data.hex_from_data(public_key).inspect}"
|
404
|
+
end
|
405
|
+
|
406
|
+
# -1 = error, 0 = bad sig, 1 = good
|
407
|
+
hash_buf = FFI::MemoryPointer.from_string(hash)
|
408
|
+
sig_buf = FFI::MemoryPointer.from_string(signature)
|
409
|
+
result = ECDSA_verify(0, hash_buf, hash_buf.size-1, sig_buf, sig_buf.size-1, eckey)
|
410
|
+
|
411
|
+
if result == 1
|
412
|
+
return true
|
413
|
+
end
|
414
|
+
|
415
|
+
if result == 0
|
416
|
+
Diagnostics.current.add_message("OpenSSL detected invalid ECDSA signature. Signature: #{BTC::Data.hex_from_data(signature).inspect}; Hash: #{BTC::Data.hex_from_data(hash).inspect}; Pubkey: #{BTC::Data.hex_from_data(public_key).inspect}")
|
417
|
+
else
|
418
|
+
raise BTCError, "OpenSSL failed with error while verifying ECDSA signature. Signature: #{BTC::Data.hex_from_data(signature).inspect}; Hash: #{BTC::Data.hex_from_data(hash).inspect}; Pubkey: #{BTC::Data.hex_from_data(public_key).inspect}"
|
419
|
+
end
|
420
|
+
return false
|
421
|
+
end
|
422
|
+
false
|
423
|
+
end
|
424
|
+
|
425
|
+
# extract private key from uncompressed DER format
|
426
|
+
def private_key_from_der_format(der_key)
|
427
|
+
raise ArgumentError, "Missing DER private key" if !der_key
|
428
|
+
|
429
|
+
prepare_if_needed
|
430
|
+
|
431
|
+
buf = FFI::MemoryPointer.from_string(der_key)
|
432
|
+
ec_key = d2i_ECPrivateKey(nil, pointer_to_pointer(buf), buf.size-1)
|
433
|
+
if ec_key.null?
|
434
|
+
raise BTCError, "OpenSSL failed to create EC_KEY with DER private key"
|
435
|
+
end
|
436
|
+
bn = EC_KEY_get0_private_key(ec_key)
|
437
|
+
BN_bn2bin(bn, buf)
|
438
|
+
buf.read_string(32)
|
439
|
+
end
|
440
|
+
|
441
|
+
# Returns data from bignum
|
442
|
+
def data_from_bn(bn, min_length: nil, required_length: nil)
|
443
|
+
raise ArgumentError, "Missing big number" if !bn
|
444
|
+
|
445
|
+
length = BN_num_bytes(bn)
|
446
|
+
buf = FFI::MemoryPointer.from_string("\x00"*length)
|
447
|
+
BN_bn2bin(bn, buf)
|
448
|
+
s = buf.read_string(length)
|
449
|
+
s = s.rjust(min_length, "\x00") if min_length
|
450
|
+
if required_length && s.bytesize != required_length
|
451
|
+
raise BTCError, "Non-matching length of the number: #{s.bytesize} bytes vs required #{required_length}"
|
452
|
+
end
|
453
|
+
s
|
454
|
+
end
|
455
|
+
|
456
|
+
protected
|
457
|
+
|
458
|
+
# Returns instance of **SomeType for input of type *SomeType.
|
459
|
+
def pointer_to_pointer(pointer)
|
460
|
+
FFI::MemoryPointer.new(:pointer).put_pointer(0, pointer)
|
461
|
+
end
|
462
|
+
|
463
|
+
|
464
|
+
|
465
|
+
# typedef struct ECDSA_SIG_st {
|
466
|
+
# BIGNUM *r;
|
467
|
+
# BIGNUM *s;
|
468
|
+
# } ECDSA_SIG;
|
469
|
+
class ECDSA_SIG < ::FFI::Struct
|
470
|
+
layout :r, :pointer,
|
471
|
+
:s, :pointer
|
472
|
+
end
|
473
|
+
|
474
|
+
class AutoreleasePool
|
475
|
+
|
476
|
+
LIB = BTC::OpenSSL
|
477
|
+
|
478
|
+
# Returns last created EC_KEY or creates one on the fly.
|
479
|
+
attr_reader :ec_key
|
480
|
+
|
481
|
+
# Returns current BN_CTX object or creates one on the fly.
|
482
|
+
attr_reader :bn_ctx
|
483
|
+
|
484
|
+
def initialize
|
485
|
+
@ec_keys = []
|
486
|
+
@bns = []
|
487
|
+
@ec_points = []
|
488
|
+
end
|
489
|
+
|
490
|
+
def ec_key
|
491
|
+
@ec_keys.last || new_ec_key
|
492
|
+
end
|
493
|
+
|
494
|
+
def bn_ctx
|
495
|
+
@bn_ctx ||= LIB.BN_CTX_new()
|
496
|
+
end
|
497
|
+
|
498
|
+
def new_ec_key
|
499
|
+
eckey = LIB.EC_KEY_new_by_curve_name(NID_secp256k1)
|
500
|
+
@ec_keys << eckey
|
501
|
+
return eckey
|
502
|
+
end
|
503
|
+
|
504
|
+
# Creates new bignum object optionally initialized with binary data (bin2bn)
|
505
|
+
def new_bn(data = nil)
|
506
|
+
bn = LIB.BN_new()
|
507
|
+
if data && data.size > 0
|
508
|
+
data_ptr = FFI::MemoryPointer.from_string(data)
|
509
|
+
# size-1 to skip \0 terminator
|
510
|
+
bn = LIB.BN_bin2bn(data_ptr, data_ptr.size - 1, bn)
|
511
|
+
end
|
512
|
+
@bns << bn
|
513
|
+
return bn
|
514
|
+
end
|
515
|
+
|
516
|
+
def new_ec_point
|
517
|
+
p = LIB.EC_POINT_new(LIB.group)
|
518
|
+
@ec_points << p
|
519
|
+
return p
|
520
|
+
end
|
521
|
+
|
522
|
+
def drain
|
523
|
+
@ec_keys.each {|eckey| LIB.EC_KEY_free(eckey) }; @ec_keys = nil
|
524
|
+
@bns.each {|bn| LIB.BN_free(bn) }; @bns = nil
|
525
|
+
@ec_points.each {|p| LIB.EC_POINT_free(p) }; @ec_points = nil
|
526
|
+
if @bn_ctx
|
527
|
+
LIB.BN_CTX_free(@bn_ctx)
|
528
|
+
@bn_ctx = nil
|
529
|
+
end
|
530
|
+
return nil
|
531
|
+
end
|
532
|
+
|
533
|
+
end
|
534
|
+
|
535
|
+
end
|
536
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module BTC
|
2
|
+
# Proof of work is specified using several terms.
|
3
|
+
# 1. `target` is big unsigned integer derived from 256-bit hash (interpreted as little-endian integer).
|
4
|
+
# Hash of a valid block should be below target.
|
5
|
+
# 2. `bits` is a 'satoshi compact' representation of a target as uint32.
|
6
|
+
# 3. `difficulty` is a floating point multiple of the minimum difficulty.
|
7
|
+
# Difficulty = 2 means the block is 2x more difficult than the minimal difficulty.
|
8
|
+
module ProofOfWork
|
9
|
+
extend self
|
10
|
+
|
11
|
+
MAX_TARGET_MAINNET = 0x00000000ffff0000000000000000000000000000000000000000000000000000
|
12
|
+
MAX_TARGET_TESTNET = 0x00000007fff80000000000000000000000000000000000000000000000000000
|
13
|
+
|
14
|
+
# Note on Satoshi Compact format (used for 'bits' value).
|
15
|
+
#
|
16
|
+
# The "compact" format is a representation of a whole
|
17
|
+
# number N using an unsigned 32bit number similar to a
|
18
|
+
# floating point format.
|
19
|
+
# The most significant 8 bits are the unsigned exponent of base 256.
|
20
|
+
# This exponent can be thought of as "number of bytes of N".
|
21
|
+
# The lower 23 bits are the mantissa.
|
22
|
+
# Bit number 24 (0x800000) represents the sign of N.
|
23
|
+
# N = (-1^sign) * mantissa * 256^(exponent-3)
|
24
|
+
#
|
25
|
+
# Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn().
|
26
|
+
# MPI uses the most significant bit of the first byte as sign.
|
27
|
+
# Thus 0x1234560000 is compact (0x05123456)
|
28
|
+
# and 0xc0de000000 is compact (0x0600c0de)
|
29
|
+
# (0x05c0de00) would be -0x40de000000
|
30
|
+
|
31
|
+
# Converts 256-bit integer to 32-bit compact representation.
|
32
|
+
def bits_from_target(target)
|
33
|
+
exponent = 3
|
34
|
+
signed = (target < 0)
|
35
|
+
target = -target if signed
|
36
|
+
while target > 0x7fffff
|
37
|
+
target >>= 8
|
38
|
+
exponent += 1
|
39
|
+
end
|
40
|
+
# The 0x00800000 bit denotes the sign.
|
41
|
+
# Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
|
42
|
+
if (target & 0x00800000) > 0
|
43
|
+
target >>= 8
|
44
|
+
exponent += 1
|
45
|
+
end
|
46
|
+
result = (exponent << 24) + target
|
47
|
+
result = result | 0x00800000 if signed
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
# Converts 32-bit compact representation to a 256-bit integer.
|
52
|
+
def target_from_bits(bits)
|
53
|
+
exponent = ((bits >> 24) & 0xff)
|
54
|
+
mantissa = bits & 0x7fffff
|
55
|
+
mantissa *= -1 if (bits & 0x800000) > 0
|
56
|
+
(mantissa * (256**(exponent-3))).to_i
|
57
|
+
end
|
58
|
+
|
59
|
+
# Computes bits from difficulty.
|
60
|
+
# Could be inaccurate since difficulty is a limited-precision floating-point number.
|
61
|
+
# Default max_target is for Bitcoin mainnet.
|
62
|
+
def bits_from_difficulty(difficulty, max_target: MAX_TARGET_MAINNET)
|
63
|
+
bits_from_target(target_from_difficulty(difficulty, max_target: max_target))
|
64
|
+
end
|
65
|
+
|
66
|
+
# Computes difficulty from bits.
|
67
|
+
# Default max_target is for Bitcoin mainnet.
|
68
|
+
def difficulty_from_bits(bits, max_target: MAX_TARGET_MAINNET)
|
69
|
+
difficulty_from_target(target_from_bits(bits), max_target: max_target)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Computes target from difficulty.
|
73
|
+
# Could be inaccurate since difficulty is a limited-precision floating-point number.
|
74
|
+
# Default max_target is for Bitcoin mainnet.
|
75
|
+
def target_from_difficulty(difficulty, max_target: MAX_TARGET_MAINNET)
|
76
|
+
(max_target / difficulty).round.to_i
|
77
|
+
end
|
78
|
+
|
79
|
+
# Compute relative difficulty from a given target.
|
80
|
+
# E.g. returns 2.5 if target is 2.5 times harder to reach than the max_target.
|
81
|
+
# Default max_target is for Bitcoin mainnet.
|
82
|
+
def difficulty_from_target(target, max_target: MAX_TARGET_MAINNET)
|
83
|
+
(max_target / target.to_f)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Converts target integer to a binary 32-byte hash.
|
87
|
+
def hash_from_target(target)
|
88
|
+
bytes = []
|
89
|
+
while target > 0
|
90
|
+
bytes << (target % 256)
|
91
|
+
target /= 256
|
92
|
+
end
|
93
|
+
BTC::Data.data_from_bytes(bytes).ljust(32, "\x00".b)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Converts 32-byte hash to target integer (hash is treated as little-endian integer)
|
97
|
+
def target_from_hash(hash)
|
98
|
+
target = 0
|
99
|
+
i = 0
|
100
|
+
hash.each_byte do |byte|
|
101
|
+
target += byte * (256**i)
|
102
|
+
i += 1
|
103
|
+
end
|
104
|
+
target
|
105
|
+
end
|
106
|
+
|
107
|
+
# TODO: add retargeting calculation routines
|
108
|
+
|
109
|
+
end # ProofOfWork
|
110
|
+
end # BTC
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module BTC
|
2
|
+
|
3
|
+
# Several functions intended to detect bad data in runtime and throw exceptions.
|
4
|
+
# These are for programmer's errors, not for bad user input.
|
5
|
+
# Bad user input should never raise exceptions.
|
6
|
+
module Safety
|
7
|
+
def AssertType(value, type)
|
8
|
+
if !value.is_a?(type)
|
9
|
+
raise ArgumentError, "Value #{value.inspect} must be of type #{type}!"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
def AssertTypeOrNil(value, type)
|
13
|
+
return if value == nil
|
14
|
+
AssertType(value, type)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Checks invariant and raises an exception.
|
18
|
+
def Invariant(condition, message)
|
19
|
+
if !condition
|
20
|
+
raise RuntimeError, "BTC Invariant Failure: #{message}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
include Safety
|
26
|
+
end
|