secp256k1-ruby 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9daded734687b356d4147443cafec3ebd9ce34389b359ada33187985126bda24
4
+ data.tar.gz: e47abb0c23075e7ab2e9cf49123fbf615ab8565761fba0cde772b8c199c475f4
5
+ SHA512:
6
+ metadata.gz: 748ae0fe478678a849aee8884f19e8e8ba493fee30e3d8cd60c65479120718d30f4ff0a22134dec67fbf8487f199a9ad8edbe63da3604e55337e9fe1c3530f33
7
+ data.tar.gz: 26eacdf649584978df4433eb103eb6a35f512e4ccd31e52403f7baf741275f0218d2a32dfff00fcb7037b95ed9c736b65bffba9c5088e0e649c50b199d0adbb9
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,3 @@
1
+ [submodule "secp256k1"]
2
+ path = secp256k1
3
+ url = https://github.com/bitcoin-core/secp256k1.git
@@ -0,0 +1 @@
1
+ 2.5.0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Jan Xie
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,28 @@
1
+ # secp256k1-ruby
2
+
3
+ ## Prerequiste
4
+
5
+ In order to use this gem, [libsecp256k1](https://github.com/bitcoin/secp256k1) with recovery module enabled must be in place.
6
+
7
+ If you have cloned the project and in project root:
8
+
9
+ ```
10
+ git submodule update --init --recursive
11
+ ./install_lib.sh
12
+ ```
13
+
14
+ ## Install
15
+
16
+ ```
17
+ gem i secp256k1-ruby
18
+ ```
19
+
20
+ Then `require 'secp256k1'` in your source code.
21
+
22
+ ## Usage
23
+
24
+ Check [test](test) for examples.
25
+
26
+ ## LICENSE
27
+
28
+ [MIT License](LICENSE)
@@ -0,0 +1,11 @@
1
+ require 'rake/testtask'
2
+
3
+ require 'bundler/gem_tasks'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs += %w(lib test)
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ task default: [:test]
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ pushd secp256k1
4
+ ./autogen.sh
5
+ ./configure --enable-module-recovery --enable-experimental --enable-module-ecdh
6
+ make && sudo make install
7
+ popd
@@ -0,0 +1,20 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+ require 'secp256k1/version'
3
+ require 'secp256k1/c'
4
+ require 'secp256k1/utils'
5
+ require 'secp256k1/ecdsa'
6
+ require 'secp256k1/key'
7
+
8
+ module Secp256k1
9
+
10
+ EC_COMPRESSED = C::Constants['SECP256K1_EC_COMPRESSED'].to_i
11
+ EC_UNCOMPRESSED = C::Constants['SECP256K1_EC_UNCOMPRESSED'].to_i
12
+
13
+ FLAG_SIGN = C::Constants['SECP256K1_CONTEXT_SIGN'].to_i
14
+ FLAG_VERIFY = C::Constants['SECP256K1_CONTEXT_VERIFY'].to_i
15
+ NO_FLAGS = C::Constants['SECP256K1_CONTEXT_NONE'].to_i
16
+ ALL_FLAGS = FLAG_SIGN | FLAG_VERIFY
17
+
18
+ class AssertError < StandardError; end
19
+
20
+ end
@@ -0,0 +1,91 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+ require 'ffi'
3
+ require 'ffi/tools/const_generator'
4
+
5
+ module Secp256k1
6
+ module C
7
+ extend FFI::Library
8
+
9
+ ffi_lib (ENV['LIBSECP256K1'] || 'libsecp256k1')
10
+
11
+ Constants = FFI::ConstGenerator.new('Secp256k1', required: true) do |gen|
12
+ gen.include 'secp256k1.h'
13
+
14
+ gen.const(:SECP256K1_EC_COMPRESSED)
15
+ gen.const(:SECP256K1_EC_UNCOMPRESSED)
16
+
17
+ gen.const(:SECP256K1_CONTEXT_SIGN)
18
+ gen.const(:SECP256K1_CONTEXT_VERIFY)
19
+ gen.const(:SECP256K1_CONTEXT_NONE)
20
+ end
21
+
22
+ class Pubkey < FFI::Struct
23
+ layout :data, [:uchar, 64]
24
+ end
25
+
26
+ class ECDSASignature < FFI::Struct
27
+ layout :data, [:uchar, 64]
28
+ end
29
+
30
+ class ECDSARecoverableSignature < FFI::Struct
31
+ layout :data, [:uchar, 65]
32
+ end
33
+
34
+ # secp256k1_context* secp256k1_context_create(unsigned int flags)
35
+ attach_function :secp256k1_context_create, [:uint], :pointer
36
+
37
+ # void secp256k1_context_destroy(secp256k1_context* ctx)
38
+ attach_function :secp256k1_context_destroy, [:pointer], :void
39
+
40
+ # int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen)
41
+ attach_function :secp256k1_ec_pubkey_parse, [:pointer, :pointer, :pointer, :size_t], :int
42
+
43
+ # int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey)
44
+ attach_function :secp256k1_ec_pubkey_create, [:pointer, :pointer, :pointer], :int
45
+
46
+ # int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey *pubkey, unsigned int flags)
47
+ attach_function :secp256k1_ec_pubkey_serialize, [:pointer, :pointer, :pointer, :pointer, :uint], :int
48
+
49
+ # int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char *seckey)
50
+ attach_function :secp256k1_ec_seckey_verify, [:pointer, :pointer], :int
51
+
52
+ # int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig)
53
+ attach_function :secp256k1_ecdsa_signature_serialize_der, [:pointer, :pointer, :pointer, :pointer], :int
54
+
55
+ # int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, const secp256k1_ecdsa_signature* sig)
56
+ attach_function :secp256k1_ecdsa_signature_serialize_compact, [:pointer, :pointer, :pointer], :int
57
+
58
+ # int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen)
59
+ attach_function :secp256k1_ecdsa_signature_parse_der, [:pointer, :pointer, :pointer, :size_t], :int
60
+
61
+ # int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input64)
62
+ attach_function :secp256k1_ecdsa_signature_parse_compact, [:pointer, :pointer, :pointer], :int
63
+
64
+ # 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)
65
+ attach_function :secp256k1_ecdsa_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
66
+
67
+ # 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)
68
+ attach_function :secp256k1_ecdsa_sign_recoverable, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int
69
+
70
+ # int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, secp256k1_ecdsa_recoverable_signature *sig, const unsigned char *msg32)
71
+ attach_function :secp256k1_ecdsa_recover, [:pointer, :pointer, :pointer, :pointer], :int
72
+
73
+ # int secp256k1_ecdsa_verify(const secp256k1_context *ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey)
74
+ attach_function :secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :pointer], :int
75
+
76
+ # int secp256k1_ecdsa_signature_normalize(const secp256k1_context *ctx, const secp256k1_ecdsa_signature *sigout, const secp256k1_ecdsa_signature *sigin)
77
+ attach_function :secp256k1_ecdsa_signature_normalize, [:pointer, :pointer, :pointer], :int
78
+
79
+ # int secp256k1_ecdsa_recoverable_signature_serialize_compact(const secp256k1_context *ctx, unsigned char *output64, int *recid, const secp256k1_ecdsa_recoverable_signature *sig)
80
+ attach_function :secp256k1_ecdsa_recoverable_signature_serialize_compact, [:pointer, :pointer, :pointer, :pointer], :int
81
+
82
+ # int secp256k1_ecdsa_recoverable_signature_parse_compact(const secp256k1_context *ctx, secp256k1_ecdsa_recoverable_signature *sig, const unsigned char *input64, int recid)
83
+ attach_function :secp256k1_ecdsa_recoverable_signature_parse_compact, [:pointer, :pointer, :pointer, :int], :int
84
+
85
+ # int secp256k1_ecdsa_recoverable_signature_convert(const secp256k1_context *ctx, secp256k1_ecdsa_signature *sig, const secp256k1_ecdsa_recoverable_signature *sigin)
86
+ attach_function :secp256k1_ecdsa_recoverable_signature_convert, [:pointer, :pointer, :pointer], :int
87
+
88
+ # int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const secp256k1_pubkey *point, const unsigned char *scalar)
89
+ attach_function :secp256k1_ecdh, [:pointer, :pointer, :pointer, :pointer], :int
90
+ end
91
+ end
@@ -0,0 +1,102 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+ module Secp256k1
3
+ module ECDSA
4
+
5
+ SIZE_SERIALIZED = 74
6
+ SIZE_COMPACT = 64
7
+
8
+ def ecdsa_serialize(raw_sig)
9
+ output = FFI::MemoryPointer.new(:uchar, SIZE_SERIALIZED)
10
+ outputlen = FFI::MemoryPointer.new(:size_t).put_uint(0, SIZE_SERIALIZED)
11
+
12
+ res = C.secp256k1_ecdsa_signature_serialize_der(@ctx, output, outputlen, raw_sig)
13
+ raise AssertError, "failed to seriazlie signature" unless res == 1
14
+
15
+ output.read_bytes(outputlen.read_uint)
16
+ end
17
+
18
+ def ecdsa_deserialize(ser_sig)
19
+ raw_sig = C::ECDSASignature.new.pointer
20
+
21
+ res = C.secp256k1_ecdsa_signature_parse_der(@ctx, raw_sig, ser_sig, ser_sig.size)
22
+ raise AssertError, "raw signature parse failed" unless res == 1
23
+
24
+ raw_sig
25
+ end
26
+
27
+ def ecdsa_serialize_compact(raw_sig)
28
+ output = FFI::MemoryPointer.new(:uchar, SIZE_COMPACT)
29
+
30
+ res = C.secp256k1_ecdsa_signature_serialize_compact(@ctx, output, raw_sig)
31
+ raise AssertError, "failed to seriazlie compact signature" unless res == 1
32
+
33
+ output.read_bytes(SIZE_COMPACT)
34
+ end
35
+
36
+ def ecdsa_deserialize_compact(ser_sig)
37
+ raise ArgumentError, 'invalid signature length' unless ser_sig.size == 64
38
+
39
+ raw_sig = C::ECDSASignature.new.pointer
40
+
41
+ res = C.secp256k1_ecdsa_signature_parse_compact(@ctx, raw_sig, ser_sig)
42
+ raise AssertError, "failed to deserialize compact signature" unless res == 1
43
+
44
+ raw_sig
45
+ end
46
+
47
+ ##
48
+ # Check and optionally convert a signature to a normalized lower-S form. If
49
+ # check_only is `true` then the normalized signature is not returned.
50
+ #
51
+ # This function always return a tuple containing a boolean (`true` if not
52
+ # previously normalized or `false` if signature was already normalized),
53
+ # and the normalized signature. When check_only is `true`, the normalized
54
+ # signature returned is always `nil`.
55
+ #
56
+ def ecdsa_signature_normalize(raw_sig, check_only: false)
57
+ sigout = check_only ? nil : C::ECDSASignature.new.pointer
58
+ res = C.secp256k1_ecdsa_signature_normalize(@ctx, sigout, raw_sig)
59
+ [res == 1, sigout]
60
+ end
61
+
62
+ def ecdsa_recover(msg, recover_sig, raw: false, digest: Digest::SHA256)
63
+ raise AssertError, 'instance not configured for ecdsa recover' if (@flags & ALL_FLAGS) != ALL_FLAGS
64
+
65
+ msg32 = hash32 msg, raw, digest
66
+ pubkey = C::Pubkey.new.pointer
67
+
68
+ res = C.secp256k1_ecdsa_recover(@ctx, pubkey, recover_sig, msg32)
69
+ raise AssertError, 'failed to recover ECDSA public key' unless res == 1
70
+
71
+ pubkey
72
+ end
73
+
74
+ def ecdsa_recoverable_serialize(recover_sig)
75
+ output = FFI::MemoryPointer.new :uchar, SIZE_COMPACT
76
+ recid = FFI::MemoryPointer.new :int
77
+
78
+ C.secp256k1_ecdsa_recoverable_signature_serialize_compact(@ctx, output, recid, recover_sig)
79
+
80
+ [output.read_bytes(SIZE_COMPACT), recid.read_int]
81
+ end
82
+
83
+ def ecdsa_recoverable_deserialize(ser_sig, rec_id)
84
+ raise ArgumentError, 'invalid rec_id' if rec_id < 0 || rec_id > 3
85
+ raise ArgumentError, 'invalid signature length' if ser_sig.size != 64
86
+
87
+ recover_sig = C::ECDSARecoverableSignature.new.pointer
88
+
89
+ res = C.secp256k1_ecdsa_recoverable_signature_parse_compact(@ctx, recover_sig, ser_sig, rec_id)
90
+ raise AssertError, 'failed to parse ECDSA compact sig' unless res == 1
91
+
92
+ recover_sig
93
+ end
94
+
95
+ def ecdsa_recoverable_convert(recover_sig)
96
+ normal_sig = C::ECDSASignature.new.pointer
97
+ C.secp256k1_ecdsa_recoverable_signature_convert(@ctx, normal_sig, recover_sig)
98
+ normal_sig
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,250 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+ require 'digest'
3
+ require 'securerandom'
4
+
5
+ module Secp256k1
6
+
7
+ class BaseKey
8
+ def initialize(ctx, flags)
9
+ @destroy = false
10
+
11
+ unless ctx
12
+ raise ArgumentError, "invalid flags" unless [NO_FLAGS, FLAG_SIGN, FLAG_VERIFY, ALL_FLAGS].include?(flags)
13
+ ctx = FFI::AutoPointer.new C.secp256k1_context_create(flags), C.method(:secp256k1_context_destroy)
14
+ @destroy = true
15
+ end
16
+
17
+ @flags = flags
18
+ @ctx = ctx
19
+ end
20
+ end
21
+
22
+ class PublicKey < BaseKey
23
+ include ECDSA, Utils
24
+
25
+ attr_accessor :public_key
26
+
27
+ def initialize(pubkey: nil, raw: false, flags: FLAG_VERIFY, ctx: nil)
28
+ super(ctx, flags)
29
+
30
+ if pubkey
31
+ if raw
32
+ raise ArgumentError, 'raw pubkey must be bytes' unless pubkey.instance_of?(String)
33
+ @public_key = deserialize pubkey
34
+ else
35
+ #raise ArgumentError, 'pubkey must be an internal object' unless pubkey.instance_of?(String)
36
+ @public_key = pubkey
37
+ end
38
+ else
39
+ @public_key = nil
40
+ end
41
+ end
42
+
43
+ def serialize(compressed: true)
44
+ raise AssertError, 'No public key defined' unless @public_key
45
+
46
+ len_compressed = compressed ? 33 : 65
47
+ res_compressed = FFI::MemoryPointer.new :char, len_compressed
48
+ outlen = FFI::MemoryPointer.new(:size_t).write_uint(len_compressed)
49
+ compflag = compressed ? EC_COMPRESSED : EC_UNCOMPRESSED
50
+
51
+ res = C.secp256k1_ec_pubkey_serialize(@ctx, res_compressed, outlen, @public_key, compflag)
52
+ raise AssertError, 'pubkey serialization failed' unless res == 1
53
+
54
+ res_compressed.read_bytes(len_compressed)
55
+ end
56
+
57
+ def deserialize(pubkey_ser)
58
+ raise ArgumentError, 'unknown public key size (expected 33 or 65)' unless [33,65].include?(pubkey_ser.size)
59
+
60
+ pubkey = C::Pubkey.new.pointer
61
+
62
+ res = C.secp256k1_ec_pubkey_parse(@ctx, pubkey, pubkey_ser, pubkey_ser.size)
63
+ raise AssertError, 'invalid public key' unless res == 1
64
+
65
+ @public_key = pubkey
66
+ pubkey
67
+ end
68
+
69
+ ##
70
+ # Add a number of public keys together.
71
+ #
72
+ def combine(pubkeys)
73
+ raise ArgumentError, 'must give at least 1 pubkey' if pubkeys.empty?
74
+
75
+ outpub = FFI::Pubkey.new.pointer
76
+ #pubkeys.each {|item| }
77
+
78
+ res = C.secp256k1_ec_pubkey_combine(@ctx, outpub, pubkeys, pubkeys.size)
79
+ raise AssertError, 'failed to combine public keys' unless res == 1
80
+
81
+ @public_key = outpub
82
+ outpub
83
+ end
84
+
85
+ ##
86
+ # Tweak the current public key by adding a 32 byte scalar times the
87
+ # generator to it and return a new PublicKey instance.
88
+ #
89
+ def tweak_add(scalar)
90
+ tweak_public :secp256k1_ec_pubkey_tweak_add, scalar
91
+ end
92
+
93
+ ##
94
+ # Tweak the current public key by multiplying it by a 32 byte scalar and
95
+ # return a new PublicKey instance.
96
+ #
97
+ def tweak_mul(scalar)
98
+ tweak_public :secp256k1_ec_pubkey_tweak_mul, scalar
99
+ end
100
+
101
+ def ecdsa_verify(msg, raw_sig, raw: false, digest: Digest::SHA256)
102
+ raise AssertError, 'No public key defined' unless @public_key
103
+ raise AssertError, 'instance not configured for sig verification' if (@flags & FLAG_VERIFY) != FLAG_VERIFY
104
+
105
+ msg32 = hash32 msg, raw, digest
106
+
107
+ !!C.secp256k1_ecdsa_verify(@ctx, raw_sig, msg32, @public_key)
108
+ end
109
+
110
+ def ecdh(scalar)
111
+ raise AssertError, 'No public key defined' unless @public_key
112
+ raise ArgumentError, 'scalar must be composed of 32 bytes' unless scalar.instance_of?(String) && scalar.size == 32
113
+
114
+ result = FFI::MemoryPointer.new :char, 32
115
+
116
+ res = C.secp256k1_ecdh @ctx, result, @public_key, scalar
117
+ raise AssertError, "invalid scalar (#{scalar})" unless res == 1
118
+
119
+ result.read_bytes(32)
120
+ end
121
+
122
+ private
123
+
124
+ def tweak_public(meth, scalar)
125
+ raise ArgumentError, 'scalar must be composed of 32 bytes' unless scalar.instance_of?(String) && scalar.size == 32
126
+ raise AssertError, 'No public key defined.' unless @public_key
127
+
128
+ newpub = self.class.new serialize, raw: true
129
+
130
+ res = C.send meth, newpub.public_key, scalar
131
+ raise AssertError, 'Tweak is out of range' unless res == 1
132
+
133
+ newpub
134
+ end
135
+
136
+ end
137
+
138
+ class PrivateKey < BaseKey
139
+ include ECDSA, Utils
140
+
141
+ attr :pubkey
142
+
143
+ def initialize(privkey: nil, raw: true, flags: ALL_FLAGS, ctx: nil)
144
+ raise AssertError, "invalid flags" unless [ALL_FLAGS, FLAG_SIGN].include?(flags)
145
+
146
+ super(ctx, flags)
147
+
148
+ @pubkey = nil
149
+ @private_key = nil
150
+
151
+ if privkey
152
+ if raw
153
+ raise ArgumentError, "privkey must be composed of 32 bytes" unless privkey.instance_of?(String) && privkey.size == 32
154
+ set_raw_privkey privkey
155
+ else
156
+ deserialize privkey
157
+ end
158
+ else
159
+ set_raw_privkey generate_private_key
160
+ end
161
+ end
162
+
163
+ def ecdsa_sign(msg, raw: false, digest: Digest::SHA256)
164
+ msg32 = hash32 msg, raw, digest
165
+ raw_sig = C::ECDSASignature.new.pointer
166
+
167
+ res = C.secp256k1_ecdsa_sign @ctx, raw_sig, msg32, @private_key, nil, nil
168
+ raise AssertError, "failed to sign" unless res == 1
169
+
170
+ raw_sig
171
+ end
172
+
173
+ def ecdsa_sign_recoverable(msg, raw: false, digest: Digest::SHA256)
174
+ msg32 = hash32 msg, raw, digest
175
+ raw_sig = C::ECDSARecoverableSignature.new.pointer
176
+
177
+ res = C.secp256k1_ecdsa_sign_recoverable @ctx, raw_sig, msg32, @private_key, nil, nil
178
+ raise AssertError, "failed to sign" unless res == 1
179
+
180
+ raw_sig
181
+ end
182
+
183
+ def set_raw_privkey(privkey)
184
+ raise ArgumentError, "invalid private key" unless C.secp256k1_ec_seckey_verify(@ctx, privkey)
185
+ @private_key = privkey
186
+ update_public_key
187
+ end
188
+
189
+ ##
190
+ # Tweak the current private key by adding a 32 bytes scalar to it and
191
+ # return a new raw private key composed of 32 bytes.
192
+ #
193
+ def tweak_add(scalar)
194
+ tweak_private :secp256k1_ec_privkey_tweak_add, scalar
195
+ end
196
+
197
+ ##
198
+ # Tweak the current private key by multiplying it by a 32 byte scalar and
199
+ # return a new raw private key composed of 32 bytes.
200
+ #
201
+ def tweak_mul(scalar)
202
+ tweak_private :secp256k1_ec_pubkey_tweak_mul, scalar
203
+ end
204
+
205
+ private
206
+
207
+ def tweak_private(meth, scalar)
208
+ raise ArgumentError, "scalar must be composed of 32 bytes" unless scalar.instance_of?(String) && scalar.size == 32
209
+
210
+ key = FFI::MemoryPointer.new(:uchar, 32).put_string(@private_key)
211
+
212
+ C.send meth, @ctx, key, scalar
213
+ raise AssertError, "Tweak is out of range" unless res == 1
214
+
215
+ key.read_string(32)
216
+ end
217
+
218
+ def update_public_key
219
+ public_key = generate_public_key @private_key
220
+ @pubkey = PublicKey.new pubkey: public_key, raw: false, ctx: @ctx, flags: @flags
221
+ end
222
+
223
+ def generate_public_key(privkey)
224
+ pubkey_ptr = C::Pubkey.new.pointer
225
+
226
+ res = C.secp256k1_ec_pubkey_create @ctx, pubkey_ptr, privkey
227
+ raise AssertError, "failed to generate public key" unless res == 1
228
+
229
+ pubkey_ptr
230
+ end
231
+
232
+ def generate_private_key
233
+ SecureRandom.random_bytes(32)
234
+ end
235
+
236
+ def serialize
237
+ encode_hex @private_key
238
+ end
239
+
240
+ def deserialize(privkey_serialized)
241
+ raise ArgumentError, "invalid private key" unless privkey_serialized.size == 64
242
+
243
+ rawkey = decode_hex privkey_serialized
244
+ set_raw_privkey rawkey
245
+
246
+ @private_key
247
+ end
248
+
249
+ end
250
+ end