secp256k1-ruby 0.4.0

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.
@@ -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