monacoin-ruby 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,266 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # bindings for secp256k1 inside bitcoin (https://github.com/bitcoin/bitcoin/tree/v0.13.1/src/secp256k1)
4
+ # tag: v0.13.1
5
+ # commit: 03422e564b552c1d3c16ae854f8471f7cb39e25d
6
+ # bitcoin@master% git checkout v0.13.1
7
+ # bitcoin@tags/v0.13.1^0% cd src/secp256k1
8
+ # bitcoin@tags/v0.13.1^0 src/secp256k1% ./autogen.sh
9
+ # bitcoin@tags/v0.13.1^0 src/secp256k1% ./configure --enable-module-recovery
10
+ # bitcoin@tags/v0.13.1^0 src/secp256k1% make libsecp256k1.la
11
+ # bitcoin@tags/v0.13.1^0 src/secp256k1% nm -D .libs/libsecp256k1.so.0.0.0 | grep secp
12
+ # export SECP256K1_LIB_PATH=/path/to/bitcoin/src/secp256k1/.libs/libsecp256k1.so.0.0.0
13
+
14
+ require 'ffi'
15
+
16
+ module Bitcoin
17
+ module Secp256k1
18
+ extend FFI::Library
19
+
20
+ SECP256K1_FLAGS_TYPE_MASK = ((1 << 8) - 1)
21
+ SECP256K1_FLAGS_TYPE_CONTEXT = (1 << 0)
22
+ SECP256K1_FLAGS_TYPE_COMPRESSION = (1 << 1)
23
+
24
+ # The higher bits contain the actual data. Do not use directly.
25
+ SECP256K1_FLAGS_BIT_CONTEXT_VERIFY = (1 << 8)
26
+ SECP256K1_FLAGS_BIT_CONTEXT_SIGN = (1 << 9)
27
+ SECP256K1_FLAGS_BIT_COMPRESSION = (1 << 8)
28
+
29
+ # Flags to pass to secp256k1_context_create.
30
+ SECP256K1_CONTEXT_VERIFY = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
31
+ SECP256K1_CONTEXT_SIGN = (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
32
+
33
+ # Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export.
34
+ SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
35
+ SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION)
36
+
37
+ def self.ffi_load_functions(file)
38
+ class_eval <<-RUBY
39
+ ffi_lib [ %[#{file}] ]
40
+
41
+ ##
42
+ # source: https://github.com/bitcoin/bitcoin/blob/v0.13.1/src/secp256k1/include/secp256k1.h
43
+ ##
44
+
45
+ # secp256k1_context* secp256k1_context_create(unsigned int flags)
46
+ attach_function :secp256k1_context_create, [:uint], :pointer
47
+
48
+ # void secp256k1_context_destroy(secp256k1_context* ctx)
49
+ attach_function :secp256k1_context_destroy, [:pointer], :void
50
+
51
+ # int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32)
52
+ attach_function :secp256k1_context_randomize, [:pointer, :pointer], :int
53
+
54
+ # int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char *seckey)
55
+ attach_function :secp256k1_ec_seckey_verify, [:pointer, :pointer], :int
56
+
57
+ # int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey)
58
+ attach_function :secp256k1_ec_pubkey_create, [:pointer, :pointer, :pointer], :int
59
+
60
+ # int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey* pubkey, unsigned int flags)
61
+ attach_function :secp256k1_ec_pubkey_serialize, [:pointer, :pointer, :pointer, :pointer, :uint], :int
62
+
63
+ # int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *sig, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void *ndata)
64
+ attach_function :secp256k1_ecdsa_sign_recoverable, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
65
+
66
+ # int secp256k1_ecdsa_recoverable_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, int *recid, const secp256k1_ecdsa_recoverable_signature* sig)
67
+ attach_function :secp256k1_ecdsa_recoverable_signature_serialize_compact, [:pointer, :pointer, :pointer, :pointer], :int
68
+
69
+ # int secp256k1_ecdsa_recoverable_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature* sig, const unsigned char *input64, int recid)
70
+ attach_function :secp256k1_ecdsa_recoverable_signature_parse_compact, [:pointer, :pointer, :pointer, :int], :int
71
+
72
+ # int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *sig, const unsigned char *msg32)
73
+ attach_function :secp256k1_ecdsa_recover, [:pointer, :pointer, :pointer, :pointer], :int
74
+
75
+ # int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void *ndata)
76
+ attach_function :secp256k1_ecdsa_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
77
+
78
+ # int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig)
79
+ attach_function :secp256k1_ecdsa_signature_serialize_der, [:pointer, :pointer, :pointer, :pointer], :int
80
+
81
+ # int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen)
82
+ attach_function :secp256k1_ec_pubkey_parse, [:pointer, :pointer, :pointer, :size_t], :int
83
+
84
+ # int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sigout, const secp256k1_ecdsa_signature *sigin)
85
+ attach_function :secp256k1_ecdsa_signature_normalize, [:pointer, :pointer, :pointer], :int
86
+
87
+ # int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey)
88
+ attach_function :secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :pointer], :int
89
+
90
+ # int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen)
91
+ attach_function :secp256k1_ecdsa_signature_parse_der, [:pointer, :pointer, :pointer, :size_t], :int
92
+
93
+ # TODO: add or port
94
+ # # int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen)
95
+ # attach_function :ecdsa_signature_parse_der_lax, [:pointer, :pointer, :pointer, :size_t], :int
96
+ RUBY
97
+ end
98
+
99
+ def self.init
100
+ return if @loaded
101
+ lib_path = [ ENV['SECP256K1_LIB_PATH'], 'vendor/bitcoin/src/secp256k1/.libs/libsecp256k1.so' ].find{|f| File.exists?(f.to_s) }
102
+ ffi_load_functions(lib_path)
103
+ @loaded = true
104
+ end
105
+
106
+ def self.with_context(flags=nil, seed=nil)
107
+ init
108
+ flags = flags || (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN)
109
+ context = secp256k1_context_create(flags)
110
+
111
+ ret, tries, max = 0, 0, 20
112
+ while ret != 1
113
+ raise "secp256k1_context_randomize failed." if tries >= max
114
+ tries += 1
115
+ ret = secp256k1_context_randomize(context, FFI::MemoryPointer.from_string(seed || SecureRandom.random_bytes(32)))
116
+ end
117
+
118
+ yield(context) if block_given?
119
+ ensure
120
+ secp256k1_context_destroy(context)
121
+ end
122
+
123
+ def self.generate_key_pair(compressed=true)
124
+ with_context do |context|
125
+
126
+ ret, tries, max = 0, 0, 20
127
+ while ret != 1
128
+ raise "secp256k1_ec_seckey_verify in generate_key_pair failed." if tries >= max
129
+ tries += 1
130
+
131
+ seckey = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, SecureRandom.random_bytes(32))
132
+ ret = secp256k1_ec_seckey_verify(context, seckey)
133
+ end
134
+
135
+ internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
136
+ result = secp256k1_ec_pubkey_create(context, internal_pubkey, seckey)
137
+ raise "error creating pubkey" unless result
138
+
139
+ pubkey, pubkey_len = FFI::MemoryPointer.new(:uchar, 65), FFI::MemoryPointer.new(:uint64)
140
+ result = if compressed
141
+ pubkey_len.put_uint64(0, 33)
142
+ secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_COMPRESSED)
143
+ else
144
+ pubkey_len.put_uint64(0, 65)
145
+ secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_UNCOMPRESSED)
146
+ end
147
+ raise "error serialize pubkey" unless result || pubkey_len.read_uint64 > 0
148
+
149
+ [ seckey.read_string(32), pubkey.read_string(pubkey_len.read_uint64) ]
150
+ end
151
+ end
152
+
153
+ def self.generate_key(compressed=true)
154
+ priv, pub = generate_key_pair(compressed)
155
+ Bitcoin::Key.new(priv.unpack("H*")[0], pub.unpack("H*")[0])
156
+ end
157
+
158
+ def self.sign(data, priv_key)
159
+ with_context do |context|
160
+ seckey = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key)
161
+ raise "priv_key invalid" unless secp256k1_ec_seckey_verify(context, seckey)
162
+
163
+ internal_signature = FFI::MemoryPointer.new(:uchar, 64)
164
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
165
+
166
+ ret, tries, max = 0, 0, 20
167
+ while ret != 1
168
+ raise "secp256k1_ecdsa_sign failed." if tries >= max
169
+ tries += 1
170
+
171
+ ret = secp256k1_ecdsa_sign(context, internal_signature, msg32, seckey, nil, nil)
172
+ end
173
+
174
+ signature, signature_len = FFI::MemoryPointer.new(:uchar, 72), FFI::MemoryPointer.new(:uint64).put_uint64(0, 72)
175
+ result = secp256k1_ecdsa_signature_serialize_der(context, signature, signature_len, internal_signature)
176
+ raise "secp256k1_ecdsa_signature_serialize_der failed" unless result
177
+
178
+ signature.read_string(signature_len.read_uint64)
179
+ end
180
+ end
181
+
182
+ def self.verify(data, sig, pub_key)
183
+ with_context do |context|
184
+ return false if data.bytesize == 0
185
+
186
+ pubkey = FFI::MemoryPointer.new(:uchar, pub_key.bytesize).put_bytes(0, pub_key)
187
+ internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
188
+ result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pubkey.size)
189
+ return false unless result
190
+
191
+ signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig)
192
+ internal_signature = FFI::MemoryPointer.new(:uchar, 64)
193
+ result = secp256k1_ecdsa_signature_parse_der(context, internal_signature, signature, signature.size)
194
+ #result = ecdsa_signature_parse_der_lax(context, internal_signature, signature, signature.size)
195
+ return false unless result
196
+
197
+ # libsecp256k1's ECDSA verification requires lower-S signatures, which have not historically been enforced in Bitcoin, so normalize them first.
198
+ secp256k1_ecdsa_signature_normalize(context, internal_signature, internal_signature)
199
+
200
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
201
+ result = secp256k1_ecdsa_verify(context, internal_signature, msg32, internal_pubkey)
202
+
203
+ return result ? true : false
204
+ end
205
+ end
206
+
207
+ def self.sign_compact(message, priv_key, compressed=true)
208
+ with_context do |context|
209
+ seckey = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key)
210
+ raise "priv_key invalid" unless secp256k1_ec_seckey_verify(context, seckey)
211
+
212
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, message)
213
+ internal_recoverable_signature = FFI::MemoryPointer.new(:uchar, 65)
214
+ rec_id = FFI::MemoryPointer.new(:int).put_int(0, -1)
215
+
216
+ ret, tries, max = 0, 0, 20
217
+ while ret != 1
218
+ raise "secp256k1_ecdsa_sign_recoverable failed." if tries >= max
219
+ tries += 1
220
+
221
+ ret = secp256k1_ecdsa_sign_recoverable(context, internal_recoverable_signature, msg32, seckey, nil, nil)
222
+ end
223
+
224
+ recoverable_signature = FFI::MemoryPointer.new(:uchar, 64)
225
+ result = secp256k1_ecdsa_recoverable_signature_serialize_compact(context, recoverable_signature, rec_id, internal_recoverable_signature)
226
+ raise "secp256k1_ecdsa_recoverable_signature_serialize_compact failed" unless result
227
+ raise "secp256k1_ecdsa_recoverable_signature_serialize_compact failed" unless rec_id.read_int != -1
228
+
229
+ header = [27 + rec_id.read_int + (compressed ? 4 : 0)].pack("C")
230
+ [ header, recoverable_signature.read_string(64) ].join
231
+ end
232
+ end
233
+
234
+ def self.recover_compact(message, signature)
235
+ with_context do |context|
236
+ return nil if signature.bytesize != 65
237
+
238
+ version = signature.unpack('C')[0]
239
+ return nil if version < 27 || version > 34
240
+
241
+ compressed = version >= 31 ? true : false
242
+ flag = compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED
243
+ version -= 4 if compressed
244
+
245
+ recid = version - 27
246
+ msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, message)
247
+ recoverable_signature = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, signature[1..-1])
248
+
249
+ internal_recoverable_signature = FFI::MemoryPointer.new(:uchar, 65)
250
+ result = secp256k1_ecdsa_recoverable_signature_parse_compact(context, internal_recoverable_signature, recoverable_signature, recid)
251
+ return nil unless result
252
+
253
+ internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
254
+ result = secp256k1_ecdsa_recover(context, internal_pubkey, internal_recoverable_signature, msg32)
255
+ return nil unless result
256
+
257
+ pubkey, pubkey_len = FFI::MemoryPointer.new(:uchar, 65), FFI::MemoryPointer.new(:uint64).put_uint64(0, 65)
258
+ result = secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, flag)
259
+ raise "error serialize pubkey" unless result || pubkey_len.read_uint64 > 0
260
+
261
+ pubkey.read_string(pubkey_len.read_uint64)
262
+ end
263
+ end
264
+
265
+ end
266
+ end
@@ -0,0 +1,280 @@
1
+ # encoding: ascii-8bit
2
+
3
+ module Bitcoin
4
+
5
+ # Elliptic Curve key as used in bitcoin.
6
+ class Key
7
+
8
+ attr_reader :key
9
+
10
+ MIN_PRIV_KEY_MOD_ORDER = 0x01
11
+ # Order of secp256k1's generator minus 1.
12
+ MAX_PRIV_KEY_MOD_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140
13
+
14
+ # Generate a new keypair.
15
+ # Bitcoin::Key.generate
16
+ def self.generate(opts={compressed: true})
17
+ k = new(nil, nil, opts); k.generate; k
18
+ end
19
+
20
+ # Import private key from base58 fromat as described in
21
+ # https://en.bitcoin.it/wiki/Private_key#Base_58_Wallet_Import_format and
22
+ # https://en.bitcoin.it/wiki/Base58Check_encoding#Encoding_a_private_key.
23
+ # See also #to_base58
24
+ def self.from_base58(str)
25
+ hex = Bitcoin.decode_base58(str)
26
+ compressed = hex.size == 76
27
+ version, key, flag, checksum = hex.unpack("a2a64a#{compressed ? 2 : 0}a8")
28
+ raise "Invalid version" unless version == Bitcoin.network[:privkey_version]
29
+ raise "Invalid checksum" unless Bitcoin.checksum(version + key + flag) == checksum
30
+ key = new(key, nil, compressed)
31
+ end
32
+
33
+ def ==(other)
34
+ self.priv == other.priv
35
+ end
36
+
37
+ # Create a new key with given +privkey+ and +pubkey+.
38
+ # Bitcoin::Key.new
39
+ # Bitcoin::Key.new(privkey)
40
+ # Bitcoin::Key.new(nil, pubkey)
41
+ def initialize(privkey = nil, pubkey = nil, opts={compressed: true})
42
+ compressed = opts.is_a?(Hash) ? opts.fetch(:compressed, true) : opts
43
+ @key = Bitcoin.bitcoin_elliptic_curve
44
+ @pubkey_compressed = pubkey ? self.class.is_compressed_pubkey?(pubkey) : compressed
45
+ set_priv(privkey) if privkey
46
+ set_pub(pubkey, @pubkey_compressed) if pubkey
47
+ end
48
+
49
+ # Generate new priv/pub key.
50
+ def generate
51
+ @key.generate_key
52
+ end
53
+
54
+ # Get the private key (in hex).
55
+ def priv
56
+ return nil unless @key.private_key
57
+ @key.private_key.to_hex.rjust(64, '0')
58
+ end
59
+
60
+ # Set the private key to +priv+ (in hex).
61
+ def priv= priv
62
+ set_priv(priv)
63
+ regenerate_pubkey
64
+ end
65
+
66
+ # Get the public key (in hex).
67
+ # In case the key was initialized with only
68
+ # a private key, the public key is regenerated.
69
+ def pub
70
+ regenerate_pubkey unless @key.public_key
71
+ return nil unless @key.public_key
72
+ @pubkey_compressed ? pub_compressed : pub_uncompressed
73
+ end
74
+
75
+ def pub_compressed
76
+ public_key = @key.public_key
77
+ public_key.group.point_conversion_form = :compressed
78
+ public_key.to_hex.rjust(66, '0')
79
+ end
80
+
81
+ def pub_uncompressed
82
+ public_key = @key.public_key
83
+ public_key.group.point_conversion_form = :uncompressed
84
+ public_key.to_hex.rjust(130, '0')
85
+ end
86
+
87
+ def compressed
88
+ @pubkey_compressed
89
+ end
90
+
91
+ # Set the public key (in hex).
92
+ def pub= pub
93
+ set_pub(pub)
94
+ end
95
+
96
+ # Get the hash160 of the public key.
97
+ def hash160
98
+ Bitcoin.hash160(pub)
99
+ end
100
+
101
+ # Get the address corresponding to the public key.
102
+ def addr
103
+ Bitcoin.hash160_to_address(hash160)
104
+ end
105
+
106
+ # Sign +data+ with the key.
107
+ # key1 = Bitcoin::Key.generate
108
+ # sig = key1.sign("some data")
109
+ def sign(data)
110
+ Bitcoin.sign_data(key, data)
111
+ end
112
+
113
+ # Verify signature +sig+ for +data+.
114
+ # key2 = Bitcoin::Key.new(nil, key1.pub)
115
+ # key2.verify("some data", sig)
116
+ def verify(data, sig)
117
+ regenerate_pubkey unless @key.public_key
118
+ sig = Bitcoin::OpenSSL_EC.repack_der_signature(sig)
119
+ if sig
120
+ @key.dsa_verify_asn1(data, sig)
121
+ else
122
+ false
123
+ end
124
+ end
125
+
126
+
127
+ def sign_message(message)
128
+ Bitcoin.sign_message(priv, pub, message)['signature']
129
+ end
130
+
131
+ def verify_message(signature, message)
132
+ Bitcoin.verify_message(addr, signature, message)
133
+ end
134
+
135
+ def self.verify_message(address, signature, message)
136
+ Bitcoin.verify_message(address, signature, message)
137
+ end
138
+
139
+ # Thanks to whoever wrote http://pastebin.com/bQtdDzHx
140
+ # for help with compact signatures
141
+ #
142
+ # Given +data+ and a compact signature (65 bytes, base64-encoded to
143
+ # a larger string), recover the public components of the key whose
144
+ # private counterpart validly signed +data+.
145
+ #
146
+ # If the signature validly signed +data+, create a new Key
147
+ # having the signing public key and address. Otherwise return nil.
148
+ #
149
+ # Be sure to check that the returned Key matches the one you were
150
+ # expecting! Otherwise you are merely checking that *someone* validly
151
+ # signed the data.
152
+ def self.recover_compact_signature_to_key(data, signature_base64)
153
+ signature = signature_base64.unpack("m0")[0]
154
+ return nil if signature.size != 65
155
+
156
+ version = signature.unpack('C')[0]
157
+ return nil if version < 27 or version > 34
158
+
159
+ compressed = (version >= 31) ? (version -= 4; true) : false
160
+
161
+ hash = Bitcoin.bitcoin_signed_message_hash(data)
162
+ pub_hex = Bitcoin::OpenSSL_EC.recover_public_key_from_signature(hash, signature, version-27, compressed)
163
+ return nil unless pub_hex
164
+
165
+ Key.new(nil, pub_hex)
166
+ end
167
+
168
+ # Export private key to base58 format.
169
+ # See also Key.from_base58
170
+ def to_base58
171
+ data = Bitcoin.network[:privkey_version] + priv
172
+ data += "01" if @pubkey_compressed
173
+ hex = data + Bitcoin.checksum(data)
174
+ Bitcoin.int_to_base58( hex.to_i(16) )
175
+ end
176
+
177
+
178
+ # Export private key to bip38 (non-ec-multiply) format as described in
179
+ # https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki
180
+ # See also Key.from_bip38
181
+ def to_bip38(passphrase)
182
+ flagbyte = compressed ? "\xe0" : "\xc0"
183
+ addresshash = Digest::SHA256.digest( Digest::SHA256.digest( self.addr ) )[0...4]
184
+
185
+ require 'scrypt' unless defined?(::SCrypt::Engine)
186
+ buf = SCrypt::Engine.__sc_crypt(passphrase, addresshash, 16384, 8, 8, 64)
187
+ derivedhalf1, derivedhalf2 = buf[0...32], buf[32..-1]
188
+
189
+ aes = proc{|k,a,b|
190
+ cipher = OpenSSL::Cipher::AES.new(256, :ECB); cipher.encrypt; cipher.padding = 0; cipher.key = k
191
+ cipher.update [ (a.to_i(16) ^ b.unpack("H*")[0].to_i(16)).to_s(16).rjust(32, '0') ].pack("H*")
192
+ }
193
+
194
+ encryptedhalf1 = aes.call(derivedhalf2, self.priv[0...32], derivedhalf1[0...16])
195
+ encryptedhalf2 = aes.call(derivedhalf2, self.priv[32..-1], derivedhalf1[16..-1])
196
+
197
+ encrypted_privkey = "\x01\x42" + flagbyte + addresshash + encryptedhalf1 + encryptedhalf2
198
+ encrypted_privkey += Digest::SHA256.digest( Digest::SHA256.digest( encrypted_privkey ) )[0...4]
199
+
200
+ encrypted_privkey = Bitcoin.encode_base58( encrypted_privkey.unpack("H*")[0] )
201
+ end
202
+
203
+ # Import private key from bip38 (non-ec-multiply) fromat as described in
204
+ # https://github.com/bitcoin/bips/blob/master/bip-0038.mediawiki
205
+ # See also #to_bip38
206
+ def self.from_bip38(encrypted_privkey, passphrase)
207
+ version, flagbyte, addresshash, encryptedhalf1, encryptedhalf2, checksum =
208
+ [ Bitcoin.decode_base58(encrypted_privkey) ].pack("H*").unpack("a2aa4a16a16a4")
209
+ compressed = (flagbyte == "\xe0") ? true : false
210
+
211
+ raise "Invalid version" unless version == "\x01\x42"
212
+ raise "Invalid checksum" unless Digest::SHA256.digest(Digest::SHA256.digest(version + flagbyte + addresshash + encryptedhalf1 + encryptedhalf2))[0...4] == checksum
213
+
214
+ require 'scrypt' unless defined?(::SCrypt::Engine)
215
+ buf = SCrypt::Engine.__sc_crypt(passphrase, addresshash, 16384, 8, 8, 64)
216
+ derivedhalf1, derivedhalf2 = buf[0...32], buf[32..-1]
217
+
218
+ aes = proc{|k,a|
219
+ cipher = OpenSSL::Cipher::AES.new(256, :ECB); cipher.decrypt; cipher.padding = 0; cipher.key = k
220
+ cipher.update(a)
221
+ }
222
+
223
+ decryptedhalf2 = aes.call(derivedhalf2, encryptedhalf2)
224
+ decryptedhalf1 = aes.call(derivedhalf2, encryptedhalf1)
225
+
226
+ priv = decryptedhalf1 + decryptedhalf2
227
+ priv = (priv.unpack("H*")[0].to_i(16) ^ derivedhalf1.unpack("H*")[0].to_i(16)).to_s(16).rjust(64, '0')
228
+ key = Bitcoin::Key.new(priv, nil, compressed)
229
+
230
+ if Digest::SHA256.digest( Digest::SHA256.digest( key.addr ) )[0...4] != addresshash
231
+ raise "Invalid addresshash! Password is likely incorrect."
232
+ end
233
+
234
+ key
235
+ end
236
+
237
+ # Import private key from warp fromat as described in
238
+ # https://github.com/keybase/warpwallet
239
+ # https://keybase.io/warp/
240
+ def self.from_warp(passphrase, salt="", compressed=false)
241
+ require 'scrypt' unless defined?(::SCrypt::Engine)
242
+ s1 = SCrypt::Engine.scrypt(passphrase+"\x01", salt+"\x01", 2**18, 8, 1, 32)
243
+ s2 = OpenSSL::PKCS5.pbkdf2_hmac(passphrase+"\x02", salt+"\x02", 2**16, 32, OpenSSL::Digest::SHA256.new)
244
+ s3 = s1.bytes.zip(s2.bytes).map{|a,b| a ^ b }.pack("C*")
245
+
246
+ key = Bitcoin::Key.new(s3.unpack("H*")[0], nil, compressed)
247
+ # [key.addr, key.to_base58, [s1,s2,s3].map{|i| i.unpack("H*")[0] }, compressed]
248
+ key
249
+ end
250
+
251
+
252
+ protected
253
+
254
+ # Regenerate public key from the private key.
255
+ def regenerate_pubkey
256
+ return nil unless @key.private_key
257
+ set_pub(Bitcoin::OpenSSL_EC.regenerate_key(priv)[1], @pubkey_compressed)
258
+ end
259
+
260
+ # Set +priv+ as the new private key (converting from hex).
261
+ def set_priv(priv)
262
+ value = priv.to_i(16)
263
+ raise 'private key is not on curve' unless MIN_PRIV_KEY_MOD_ORDER <= value && value <= MAX_PRIV_KEY_MOD_ORDER
264
+ @key.private_key = OpenSSL::BN.from_hex(priv)
265
+ end
266
+
267
+ # Set +pub+ as the new public key (converting from hex).
268
+ def set_pub(pub, compressed = nil)
269
+ @pubkey_compressed = compressed == nil ? self.class.is_compressed_pubkey?(pub) : compressed
270
+ @key.public_key = OpenSSL::PKey::EC::Point.from_hex(@key.group, pub)
271
+ end
272
+
273
+ def self.is_compressed_pubkey?(pub)
274
+ ["02","03"].include?(pub[0..1])
275
+ end
276
+
277
+ end
278
+
279
+ end
280
+