rbsecp256k1 1.0.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.
- checksums.yaml +7 -0
- data/Rakefile +5 -0
- data/ext/rbsecp256k1/extconf.rb +13 -0
- data/ext/rbsecp256k1/rbsecp256k1.c +896 -0
- data/lib/rbsecp256k1.rb +7 -0
- data/lib/rbsecp256k1/util.rb +20 -0
- data/lib/rbsecp256k1/version.rb +3 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ad49bf4a099e7afb5cf5ef09af6d61f5019c4378fdc0bda9bc1b7b66d2cbca8b
|
4
|
+
data.tar.gz: 8de35376f226a7955feb12c8214a6f023cbed7dde25fc1d98aa49464e6777cb5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 68a48a57862f066da1477de75283e18c20daa80bb8508e5cf39da06885eb12bc38d40f06e35958d13667fac942d31315b6a13a175970bc5e929eff8bcf1a1514
|
7
|
+
data.tar.gz: b802bda821e12c398c379b003a4a60956d6d69b8f30fe26e2d799ef23290f228ffca603a30e3c376dfe0b546b2d8b7b9dedc86c6862d963228d67c3f3fbb0efe
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
# OpenSSL flags
|
4
|
+
print("Looking for OpenSSL")
|
5
|
+
results = pkg_config('openssl')
|
6
|
+
abort "missing openssl pkg-config information" unless results[1]
|
7
|
+
|
8
|
+
# Require that libsecp256k1 be installed using `make install` or similar.
|
9
|
+
print("Looking for libsecp256k1")
|
10
|
+
results = pkg_config('libsecp256k1')
|
11
|
+
abort "missing libsecp256k1" unless results[1]
|
12
|
+
|
13
|
+
create_makefile('rbsecp256k1')
|
@@ -0,0 +1,896 @@
|
|
1
|
+
// rbsecp256k1.c - Ruby VM interfaces for library.
|
2
|
+
//
|
3
|
+
// Description:
|
4
|
+
// This library provides a low-level and high-performance Ruby wrapper around
|
5
|
+
// libsecp256k1. It includes functions for generating key pairs, signing data,
|
6
|
+
// and verifying signatures using the library.
|
7
|
+
//
|
8
|
+
// Dependencies:
|
9
|
+
// * libsecp256k1
|
10
|
+
// * openssl
|
11
|
+
#include <ruby.h>
|
12
|
+
#include <stdio.h>
|
13
|
+
|
14
|
+
#include <openssl/rand.h>
|
15
|
+
#include <openssl/sha.h>
|
16
|
+
#include <secp256k1.h>
|
17
|
+
|
18
|
+
// High-level design:
|
19
|
+
//
|
20
|
+
// The Ruby wrapper is divided into the following hierarchical organization:
|
21
|
+
//
|
22
|
+
// +- Secp256k1 (Top-level module)
|
23
|
+
// |-- Context
|
24
|
+
// |-- KeyPair
|
25
|
+
// |-- PublicKey
|
26
|
+
// |-- PrivateKey
|
27
|
+
// |-- Signature
|
28
|
+
//
|
29
|
+
// The Context class contains most of the methods that invoke libsecp256k1.
|
30
|
+
// The KayPair, PublicKey, PrivateKey, and Signature objects act as data
|
31
|
+
// objects and are passed to various methods. Contexts are thread safe and can
|
32
|
+
// be used across applications. Context initialization is expensive so it is
|
33
|
+
// recommended that a single context be initialized and used throughout an
|
34
|
+
// application when possible.
|
35
|
+
|
36
|
+
//
|
37
|
+
// The section below contains purely internal methods used exclusively by the
|
38
|
+
// C internals of the library.
|
39
|
+
//
|
40
|
+
|
41
|
+
// Globally define our module and its associated classes so we can instantiate
|
42
|
+
// objects from anywhere. The use of global variables seems to be inline with
|
43
|
+
// how the Ruby project builds its own extension gems.
|
44
|
+
static VALUE Secp256k1_module;
|
45
|
+
static VALUE Secp256k1_Context_class;
|
46
|
+
static VALUE Secp256k1_KeyPair_class;
|
47
|
+
static VALUE Secp256k1_PublicKey_class;
|
48
|
+
static VALUE Secp256k1_PrivateKey_class;
|
49
|
+
static VALUE Secp256k1_Signature_class;
|
50
|
+
|
51
|
+
// Forward definitions for all structures
|
52
|
+
typedef struct Context_dummy {
|
53
|
+
secp256k1_context *ctx; // Context used by libsecp256k1 library
|
54
|
+
} Context;
|
55
|
+
|
56
|
+
typedef struct KeyPair_dummy {
|
57
|
+
VALUE public_key;
|
58
|
+
VALUE private_key;
|
59
|
+
} KeyPair;
|
60
|
+
|
61
|
+
typedef struct PublicKey_dummy {
|
62
|
+
secp256k1_pubkey pubkey;
|
63
|
+
Context *context;
|
64
|
+
} PublicKey;
|
65
|
+
|
66
|
+
typedef struct PrivateKey_dummy {
|
67
|
+
unsigned char data[32]; // Bytes comprising the private key data
|
68
|
+
} PrivateKey;
|
69
|
+
|
70
|
+
typedef struct Signature_dummy {
|
71
|
+
secp256k1_ecdsa_signature sig; // Signature object, contains 64-byte signature.
|
72
|
+
Context *context;
|
73
|
+
} Signature;
|
74
|
+
|
75
|
+
/**
|
76
|
+
* Macro: SUCCESS
|
77
|
+
*
|
78
|
+
* Determines whether or not the given function result was a success.
|
79
|
+
*/
|
80
|
+
#define SUCCESS(x) ((x) == RESULT_SUCCESS)
|
81
|
+
|
82
|
+
/**
|
83
|
+
* Macro: FAILURE
|
84
|
+
*
|
85
|
+
* Indicates whether or not the given function result is a failure.
|
86
|
+
*/
|
87
|
+
#define FAILURE(x) !SUCCESS(x)
|
88
|
+
|
89
|
+
/* Result type for internally defined functions */
|
90
|
+
typedef enum ResultT_dummy {
|
91
|
+
RESULT_SUCCESS,
|
92
|
+
RESULT_FAILURE
|
93
|
+
} ResultT;
|
94
|
+
|
95
|
+
/**
|
96
|
+
* Generate a series of cryptographically secure random bytes using OpenSSL.
|
97
|
+
*
|
98
|
+
* \param out_bytes Desired number of bytes will be written here.
|
99
|
+
* \param in_size Number of bytes of random data to be generated.
|
100
|
+
* \return RESULT_SUCCESS if the bytes were generated successfully,
|
101
|
+
* RESULT_FAILURE otherwise.
|
102
|
+
*/
|
103
|
+
static ResultT
|
104
|
+
GenerateRandomBytes(unsigned char *out_bytes, size_t in_size)
|
105
|
+
{
|
106
|
+
// OpenSSL RNG has not been seeded with enough data and is therefore
|
107
|
+
// not usable.
|
108
|
+
if (RAND_status() == 0)
|
109
|
+
{
|
110
|
+
return RESULT_FAILURE;
|
111
|
+
}
|
112
|
+
|
113
|
+
// Attempt to generate random bytes using the OpenSSL RNG
|
114
|
+
if (RAND_bytes(out_bytes, in_size) != 1)
|
115
|
+
{
|
116
|
+
return RESULT_FAILURE;
|
117
|
+
}
|
118
|
+
|
119
|
+
return RESULT_SUCCESS;
|
120
|
+
}
|
121
|
+
|
122
|
+
/**
|
123
|
+
* Computes the ECDSA signature of the given data.
|
124
|
+
*
|
125
|
+
* This method first computes the ECDSA signature of the given data (can be
|
126
|
+
* text or binary data) and outputs both the raw libsecp256k1 signature.
|
127
|
+
*
|
128
|
+
* ECDSA signing involves the following steps:
|
129
|
+
* 1. Compute the 32-byte SHA-256 hash of the given data.
|
130
|
+
* 2. Sign the 32-byte hash using the private key provided.
|
131
|
+
*
|
132
|
+
* \param in_context libsecp256k1 context
|
133
|
+
* \param in_data Data to be signed
|
134
|
+
* \param in_data_len Length of data to be signed
|
135
|
+
* \param in_private_key Private key to be used for signing
|
136
|
+
* \param out_signature Signature produced during the signing proccess
|
137
|
+
* \return RESULT_SUCCESS if the hash and signature were computed successfully,
|
138
|
+
* RESULT_FAILURE if signing failed or DER encoding failed.
|
139
|
+
*/
|
140
|
+
static ResultT
|
141
|
+
SignData(secp256k1_context *in_context,
|
142
|
+
unsigned char *in_data,
|
143
|
+
unsigned long in_data_len,
|
144
|
+
unsigned char *in_private_key,
|
145
|
+
secp256k1_ecdsa_signature *out_signature)
|
146
|
+
{
|
147
|
+
unsigned char hash[SHA256_DIGEST_LENGTH];
|
148
|
+
|
149
|
+
// Compute the SHA-256 hash of data
|
150
|
+
SHA256(in_data, in_data_len, hash);
|
151
|
+
|
152
|
+
// Sign the hash of the data
|
153
|
+
if (secp256k1_ecdsa_sign(in_context,
|
154
|
+
out_signature,
|
155
|
+
hash,
|
156
|
+
in_private_key,
|
157
|
+
NULL,
|
158
|
+
NULL) == 1)
|
159
|
+
{
|
160
|
+
return RESULT_SUCCESS;
|
161
|
+
}
|
162
|
+
|
163
|
+
return RESULT_FAILURE;
|
164
|
+
}
|
165
|
+
|
166
|
+
/**
|
167
|
+
* Secp256k1.generate_private_key_bytes
|
168
|
+
*
|
169
|
+
* Generate cryptographically secure 32 byte private key data.
|
170
|
+
*
|
171
|
+
* Raises:
|
172
|
+
* RuntimeError - If random number generation fails for any reason.
|
173
|
+
*/
|
174
|
+
static VALUE
|
175
|
+
Secp256k1_generate_private_key_bytes(VALUE self)
|
176
|
+
{
|
177
|
+
unsigned char private_key_bytes[32];
|
178
|
+
|
179
|
+
if (FAILURE(GenerateRandomBytes(private_key_bytes, 32)))
|
180
|
+
{
|
181
|
+
rb_raise(rb_eRuntimeError, "Random number generation failed.");
|
182
|
+
}
|
183
|
+
|
184
|
+
return rb_str_new((char*)private_key_bytes, 32);
|
185
|
+
}
|
186
|
+
|
187
|
+
//
|
188
|
+
// Secp256k1::PrivateKey class interface
|
189
|
+
//
|
190
|
+
|
191
|
+
/* Allocate space for new private key internal data */
|
192
|
+
static VALUE
|
193
|
+
PrivateKey_alloc(VALUE klass)
|
194
|
+
{
|
195
|
+
VALUE new_instance;
|
196
|
+
PrivateKey *private_key;
|
197
|
+
|
198
|
+
new_instance = Data_Make_Struct(
|
199
|
+
klass, PrivateKey, NULL, free, private_key
|
200
|
+
);
|
201
|
+
memset(private_key->data, 0, 32);
|
202
|
+
|
203
|
+
return new_instance;
|
204
|
+
}
|
205
|
+
|
206
|
+
/**
|
207
|
+
* PrivateKey.generate
|
208
|
+
*
|
209
|
+
* Generates a new random private key.
|
210
|
+
*
|
211
|
+
* \return PrivateKey instance populated with randomly generated key.
|
212
|
+
*/
|
213
|
+
static VALUE
|
214
|
+
PrivateKey_generate(VALUE klass)
|
215
|
+
{
|
216
|
+
VALUE result = rb_funcall(klass,
|
217
|
+
rb_intern("new"),
|
218
|
+
1,
|
219
|
+
Secp256k1_generate_private_key_bytes(Secp256k1_module));
|
220
|
+
return result;
|
221
|
+
}
|
222
|
+
|
223
|
+
/**
|
224
|
+
* PrivateKey#initialize
|
225
|
+
*
|
226
|
+
* Initialize a new private key with the given private key data.
|
227
|
+
*
|
228
|
+
* \param self allocated class instance
|
229
|
+
* \param in_bytes private key data as 32 byte string
|
230
|
+
* \raises ArgumentError If private key data is not 32 bytes long.
|
231
|
+
*/
|
232
|
+
static VALUE
|
233
|
+
PrivateKey_initialize(VALUE self, VALUE in_bytes)
|
234
|
+
{
|
235
|
+
PrivateKey *private_key;
|
236
|
+
|
237
|
+
Check_Type(in_bytes, T_STRING);
|
238
|
+
|
239
|
+
if (RSTRING_LEN(in_bytes) != 32)
|
240
|
+
{
|
241
|
+
rb_raise(rb_eArgError, "private key data must be 32 bytes in length");
|
242
|
+
return self;
|
243
|
+
}
|
244
|
+
|
245
|
+
Data_Get_Struct(self, PrivateKey, private_key);
|
246
|
+
memcpy(private_key->data, RSTRING_PTR(in_bytes), 32);
|
247
|
+
|
248
|
+
// Set the PrivateKey.data attribute for later reading
|
249
|
+
rb_iv_set(self, "@data", in_bytes);
|
250
|
+
|
251
|
+
return self;
|
252
|
+
}
|
253
|
+
|
254
|
+
//
|
255
|
+
// Secp256k1::Signature class interface
|
256
|
+
//
|
257
|
+
|
258
|
+
/* Allocate memory for Signature object */
|
259
|
+
static VALUE
|
260
|
+
Signature_alloc(VALUE klass)
|
261
|
+
{
|
262
|
+
VALUE new_instance;
|
263
|
+
Signature *signature;
|
264
|
+
|
265
|
+
new_instance = Data_Make_Struct(klass,
|
266
|
+
Signature,
|
267
|
+
NULL,
|
268
|
+
free,
|
269
|
+
signature);
|
270
|
+
memset(signature, 0, sizeof(Signature));
|
271
|
+
|
272
|
+
return new_instance;
|
273
|
+
}
|
274
|
+
|
275
|
+
/**
|
276
|
+
* Signature#der_encoded
|
277
|
+
*
|
278
|
+
* \param self
|
279
|
+
* \return DER encoded version of this signature
|
280
|
+
*/
|
281
|
+
static VALUE
|
282
|
+
Signature_der_encoded(VALUE self)
|
283
|
+
{
|
284
|
+
Signature *signature;
|
285
|
+
unsigned long der_signature_len;
|
286
|
+
unsigned char der_signature[512];
|
287
|
+
|
288
|
+
Data_Get_Struct(self, Signature, signature);
|
289
|
+
|
290
|
+
der_signature_len = 512;
|
291
|
+
if (secp256k1_ecdsa_signature_serialize_der(signature->context->ctx,
|
292
|
+
der_signature,
|
293
|
+
&der_signature_len,
|
294
|
+
&(signature->sig)) == 1)
|
295
|
+
{
|
296
|
+
return rb_str_new((char*)der_signature, der_signature_len);
|
297
|
+
}
|
298
|
+
|
299
|
+
rb_raise(rb_eRuntimeError, "Could not compute DER encoded signature");
|
300
|
+
}
|
301
|
+
|
302
|
+
/**
|
303
|
+
* Signature#compact
|
304
|
+
*
|
305
|
+
* Returns the compact (64-byte) representation of this signature.
|
306
|
+
*
|
307
|
+
* \param self
|
308
|
+
* \return Compact encoding of this signature
|
309
|
+
*/
|
310
|
+
static VALUE
|
311
|
+
Signature_compact(VALUE self)
|
312
|
+
{
|
313
|
+
Signature *signature;
|
314
|
+
unsigned char compact_signature[65];
|
315
|
+
|
316
|
+
Data_Get_Struct(self, Signature, signature);
|
317
|
+
|
318
|
+
if (secp256k1_ecdsa_signature_serialize_compact(signature->context->ctx,
|
319
|
+
compact_signature,
|
320
|
+
&(signature->sig)) == 1)
|
321
|
+
{
|
322
|
+
return rb_str_new((char*)compact_signature, 65);
|
323
|
+
}
|
324
|
+
|
325
|
+
rb_raise(rb_eRuntimeError, "Unable to compute compact signature");
|
326
|
+
}
|
327
|
+
|
328
|
+
//
|
329
|
+
// Secp256k1::Context class interface
|
330
|
+
//
|
331
|
+
|
332
|
+
/* Deallocate a context when it is garbage collected */
|
333
|
+
static void
|
334
|
+
Context_free(void* in_context)
|
335
|
+
{
|
336
|
+
Context *context = (Context*)in_context;
|
337
|
+
|
338
|
+
secp256k1_context_destroy(context->ctx);
|
339
|
+
free(context);
|
340
|
+
}
|
341
|
+
|
342
|
+
/* Allocate a new context object */
|
343
|
+
static VALUE
|
344
|
+
Context_alloc(VALUE klass)
|
345
|
+
{
|
346
|
+
VALUE new_instance;
|
347
|
+
Context *context;
|
348
|
+
|
349
|
+
new_instance = Data_Make_Struct(
|
350
|
+
klass, Context, NULL, Context_free, context
|
351
|
+
);
|
352
|
+
context->ctx = NULL;
|
353
|
+
|
354
|
+
return new_instance;
|
355
|
+
}
|
356
|
+
|
357
|
+
/**
|
358
|
+
* Context#initialize
|
359
|
+
*
|
360
|
+
* Initialize a new libsecp256k1 context.
|
361
|
+
*
|
362
|
+
* \raises RuntimeError if context randomizatino fails.
|
363
|
+
*/
|
364
|
+
static VALUE
|
365
|
+
Context_initialize(VALUE self)
|
366
|
+
{
|
367
|
+
Context *context;
|
368
|
+
unsigned char seed[32];
|
369
|
+
|
370
|
+
Data_Get_Struct(self, Context, context);
|
371
|
+
|
372
|
+
context->ctx = secp256k1_context_create(
|
373
|
+
SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY
|
374
|
+
);
|
375
|
+
|
376
|
+
// Randomize the context at initialization time rather than before calls so
|
377
|
+
// the same context can be used across threads safely.
|
378
|
+
GenerateRandomBytes(seed, 32);
|
379
|
+
if (secp256k1_context_randomize(context->ctx, seed) != 1)
|
380
|
+
{
|
381
|
+
rb_raise(rb_eRuntimeError, "Randomization of context failed.");
|
382
|
+
}
|
383
|
+
|
384
|
+
return self;
|
385
|
+
}
|
386
|
+
|
387
|
+
/**
|
388
|
+
* Context#generate_key_pair
|
389
|
+
*
|
390
|
+
* Generate a new (public, private) key pair.
|
391
|
+
*/
|
392
|
+
static VALUE
|
393
|
+
Context_generate_key_pair(VALUE self)
|
394
|
+
{
|
395
|
+
VALUE private_key;
|
396
|
+
VALUE public_key;
|
397
|
+
VALUE key_pair;
|
398
|
+
|
399
|
+
private_key = PrivateKey_generate(Secp256k1_PrivateKey_class);
|
400
|
+
public_key = rb_funcall(Secp256k1_PublicKey_class,
|
401
|
+
rb_intern("new"),
|
402
|
+
2,
|
403
|
+
self,
|
404
|
+
private_key);
|
405
|
+
key_pair = rb_funcall(Secp256k1_KeyPair_class,
|
406
|
+
rb_intern("new"),
|
407
|
+
2,
|
408
|
+
public_key,
|
409
|
+
private_key);
|
410
|
+
|
411
|
+
return key_pair;
|
412
|
+
}
|
413
|
+
|
414
|
+
/**
|
415
|
+
* Context#public_key_from_data
|
416
|
+
*
|
417
|
+
* Loads a public key from compressed or uncompressed binary data.
|
418
|
+
*
|
419
|
+
* \param self
|
420
|
+
* \param in_public_key_data Compressed or uncompressed binary public key data.
|
421
|
+
*/
|
422
|
+
static VALUE
|
423
|
+
Context_public_key_from_data(VALUE self, VALUE in_public_key_data)
|
424
|
+
{
|
425
|
+
Context *context;
|
426
|
+
PublicKey *public_key;
|
427
|
+
unsigned char *public_key_data;
|
428
|
+
VALUE result;
|
429
|
+
|
430
|
+
Check_Type(in_public_key_data, T_STRING);
|
431
|
+
|
432
|
+
Data_Get_Struct(self, Context, context);
|
433
|
+
public_key_data = (unsigned char*)StringValuePtr(in_public_key_data);
|
434
|
+
result = Data_Make_Struct(Secp256k1_PublicKey_class,
|
435
|
+
PublicKey,
|
436
|
+
NULL,
|
437
|
+
free,
|
438
|
+
public_key);
|
439
|
+
public_key->context = context;
|
440
|
+
|
441
|
+
if (secp256k1_ec_pubkey_parse(context->ctx,
|
442
|
+
&(public_key->pubkey),
|
443
|
+
public_key_data,
|
444
|
+
RSTRING_LEN(in_public_key_data)) != 1)
|
445
|
+
{
|
446
|
+
rb_raise(rb_eRuntimeError, "Invalid public key data");
|
447
|
+
}
|
448
|
+
|
449
|
+
return result;
|
450
|
+
}
|
451
|
+
|
452
|
+
/**
|
453
|
+
* Context#key_pair_from_private_key
|
454
|
+
*
|
455
|
+
* Converts a binary private key into a key pair
|
456
|
+
*
|
457
|
+
* \param self
|
458
|
+
* \param in_private_key_data Binary private key data to be used
|
459
|
+
* \return A KeyPair initialized from the given private key data
|
460
|
+
* \raises ArgumentError if the private key data is invalid or key derivation
|
461
|
+
* fails.
|
462
|
+
*/
|
463
|
+
static VALUE
|
464
|
+
Context_key_pair_from_private_key(VALUE self, VALUE in_private_key_data)
|
465
|
+
{
|
466
|
+
Context *context;
|
467
|
+
VALUE public_key;
|
468
|
+
VALUE private_key;
|
469
|
+
VALUE key_pair;
|
470
|
+
unsigned char *private_key_data;
|
471
|
+
|
472
|
+
// TODO: Move verification into PrivateKey_initialize?
|
473
|
+
// Verify secret key data before attempting to recover key pair
|
474
|
+
Data_Get_Struct(self, Context, context);
|
475
|
+
private_key_data = (unsigned char*)StringValuePtr(in_private_key_data);
|
476
|
+
|
477
|
+
if (secp256k1_ec_seckey_verify(context->ctx, private_key_data) != 1)
|
478
|
+
{
|
479
|
+
rb_raise(rb_eRuntimeError, "Invalid private key data.");
|
480
|
+
}
|
481
|
+
|
482
|
+
private_key = rb_funcall(Secp256k1_PrivateKey_class,
|
483
|
+
rb_intern("new"),
|
484
|
+
1,
|
485
|
+
in_private_key_data);
|
486
|
+
public_key = rb_funcall(Secp256k1_PublicKey_class,
|
487
|
+
rb_intern("new"),
|
488
|
+
2,
|
489
|
+
self,
|
490
|
+
private_key);
|
491
|
+
key_pair = rb_funcall(Secp256k1_KeyPair_class,
|
492
|
+
rb_intern("new"),
|
493
|
+
2,
|
494
|
+
public_key,
|
495
|
+
private_key);
|
496
|
+
|
497
|
+
return key_pair;
|
498
|
+
}
|
499
|
+
|
500
|
+
/**
|
501
|
+
* Context#signature_from_der_encoded
|
502
|
+
*
|
503
|
+
* Converts a DER encoded signature into a Secp256k1::Signature object.
|
504
|
+
*
|
505
|
+
* \param self
|
506
|
+
* \param in_der_encoded_signature DER encoded signature as a binary string
|
507
|
+
*/
|
508
|
+
static VALUE
|
509
|
+
Context_signature_from_der_encoded(VALUE self, VALUE in_der_encoded_signature)
|
510
|
+
{
|
511
|
+
Context *context;
|
512
|
+
Signature *signature;
|
513
|
+
VALUE signature_result;
|
514
|
+
unsigned char *signature_data;
|
515
|
+
|
516
|
+
Check_Type(in_der_encoded_signature, T_STRING);
|
517
|
+
|
518
|
+
Data_Get_Struct(self, Context, context);
|
519
|
+
signature_data = (unsigned char*)StringValuePtr(in_der_encoded_signature);
|
520
|
+
|
521
|
+
signature_result = Data_Make_Struct(Secp256k1_Signature_class,
|
522
|
+
Signature,
|
523
|
+
NULL,
|
524
|
+
free,
|
525
|
+
signature);
|
526
|
+
|
527
|
+
if (secp256k1_ecdsa_signature_parse_der(context->ctx,
|
528
|
+
&(signature->sig),
|
529
|
+
signature_data,
|
530
|
+
RSTRING_LEN(in_der_encoded_signature)) != 1)
|
531
|
+
{
|
532
|
+
rb_raise(rb_eRuntimeError, "Invalid DER encoded signature.");
|
533
|
+
}
|
534
|
+
|
535
|
+
signature->context = context;
|
536
|
+
return signature_result;
|
537
|
+
}
|
538
|
+
|
539
|
+
/**
|
540
|
+
* Context#signature_from_compact
|
541
|
+
*
|
542
|
+
* Deserializes a Signature from 64-byte compact signature data.
|
543
|
+
*
|
544
|
+
* \param self
|
545
|
+
* \param in_compact_signature Compact signature to deserialize
|
546
|
+
* \return Signature object deserialized from compact signature.
|
547
|
+
* \raises RuntimeError if signature deserialization fails
|
548
|
+
*/
|
549
|
+
static VALUE
|
550
|
+
Context_signature_from_compact(VALUE self, VALUE in_compact_signature)
|
551
|
+
{
|
552
|
+
Context *context;
|
553
|
+
Signature *signature;
|
554
|
+
VALUE signature_result;
|
555
|
+
unsigned char *signature_data;
|
556
|
+
|
557
|
+
Check_Type(in_compact_signature, T_STRING);
|
558
|
+
|
559
|
+
Data_Get_Struct(self, Context, context);
|
560
|
+
signature_data = (unsigned char*)StringValuePtr(in_compact_signature);
|
561
|
+
|
562
|
+
signature_result = Data_Make_Struct(Secp256k1_Signature_class,
|
563
|
+
Signature,
|
564
|
+
NULL,
|
565
|
+
free,
|
566
|
+
signature);
|
567
|
+
|
568
|
+
if (secp256k1_ecdsa_signature_parse_compact(context->ctx,
|
569
|
+
&(signature->sig),
|
570
|
+
signature_data) != 1)
|
571
|
+
{
|
572
|
+
rb_raise(rb_eRuntimeError, "Invalid compact signature.");
|
573
|
+
}
|
574
|
+
|
575
|
+
signature->context = context;
|
576
|
+
return signature_result;
|
577
|
+
}
|
578
|
+
|
579
|
+
/**
|
580
|
+
* Context#sign
|
581
|
+
*
|
582
|
+
* Computes the ECDSA signature of the data using the secp256k1 EC.
|
583
|
+
*
|
584
|
+
* \param self
|
585
|
+
* \param in_private_key Private key to use for signing
|
586
|
+
* \param in_data Data to be signed
|
587
|
+
* \raises RuntimeError if signing fails
|
588
|
+
*/
|
589
|
+
static VALUE
|
590
|
+
Context_sign(VALUE self, VALUE in_private_key, VALUE in_data)
|
591
|
+
{
|
592
|
+
unsigned char *data_ptr;
|
593
|
+
PrivateKey *private_key;
|
594
|
+
Context *context;
|
595
|
+
Signature *signature;
|
596
|
+
VALUE signature_result;
|
597
|
+
|
598
|
+
Check_Type(in_data, T_STRING);
|
599
|
+
|
600
|
+
Data_Get_Struct(self, Context, context);
|
601
|
+
Data_Get_Struct(in_private_key, PrivateKey, private_key);
|
602
|
+
data_ptr = (unsigned char*)StringValuePtr(in_data);
|
603
|
+
|
604
|
+
signature_result = Data_Make_Struct(Secp256k1_Signature_class,
|
605
|
+
Signature,
|
606
|
+
NULL,
|
607
|
+
free,
|
608
|
+
signature);
|
609
|
+
|
610
|
+
// Attempt to sign the hash of the given data
|
611
|
+
if (SUCCESS(SignData(context->ctx,
|
612
|
+
data_ptr,
|
613
|
+
RSTRING_LEN(in_data),
|
614
|
+
private_key->data,
|
615
|
+
&(signature->sig))))
|
616
|
+
{
|
617
|
+
signature->context = context;
|
618
|
+
return signature_result;
|
619
|
+
}
|
620
|
+
|
621
|
+
rb_raise(rb_eRuntimeError, "Unable to compute signature");
|
622
|
+
}
|
623
|
+
|
624
|
+
/**
|
625
|
+
* Context#verify
|
626
|
+
*
|
627
|
+
* Verifies that the signature by the holder of public key on message.
|
628
|
+
*
|
629
|
+
* \param self
|
630
|
+
* \param in_signature Signature to be verified
|
631
|
+
* \param in_pubkey Public key to verify signature against
|
632
|
+
* \param in_message Message to verify signature of
|
633
|
+
* \return Qtrue if the signature is valid, Qfalse otherwise.
|
634
|
+
*/
|
635
|
+
static VALUE
|
636
|
+
Context_verify(VALUE self, VALUE in_signature, VALUE in_pubkey, VALUE in_message)
|
637
|
+
{
|
638
|
+
Context *context;
|
639
|
+
PublicKey *public_key;
|
640
|
+
Signature *signature;
|
641
|
+
unsigned char *message_ptr;
|
642
|
+
unsigned char hash[SHA256_DIGEST_LENGTH];
|
643
|
+
|
644
|
+
Check_Type(in_message, T_STRING);
|
645
|
+
|
646
|
+
Data_Get_Struct(self, Context, context);
|
647
|
+
Data_Get_Struct(in_pubkey, PublicKey, public_key);
|
648
|
+
Data_Get_Struct(in_signature, Signature, signature);
|
649
|
+
|
650
|
+
message_ptr = (unsigned char*)StringValuePtr(in_message);
|
651
|
+
SHA256(message_ptr, RSTRING_LEN(in_message), hash);
|
652
|
+
|
653
|
+
if (secp256k1_ecdsa_verify(context->ctx,
|
654
|
+
&(signature->sig),
|
655
|
+
hash,
|
656
|
+
&(public_key->pubkey)) == 1)
|
657
|
+
{
|
658
|
+
return Qtrue;
|
659
|
+
}
|
660
|
+
|
661
|
+
return Qfalse;
|
662
|
+
}
|
663
|
+
|
664
|
+
//
|
665
|
+
// Secp256k1::PublicKey class interface
|
666
|
+
//
|
667
|
+
|
668
|
+
static VALUE
|
669
|
+
PublicKey_alloc(VALUE klass)
|
670
|
+
{
|
671
|
+
VALUE result;
|
672
|
+
PublicKey *public_key;
|
673
|
+
|
674
|
+
result = Data_Make_Struct(klass, PublicKey, NULL, free, public_key);
|
675
|
+
|
676
|
+
return result;
|
677
|
+
}
|
678
|
+
|
679
|
+
/**
|
680
|
+
* PublicKey#initialize
|
681
|
+
*
|
682
|
+
* Initialize a new public key from the given context and private key.
|
683
|
+
*
|
684
|
+
* \param in_context Context instance to be used in derivation
|
685
|
+
* \param in_private_key PrivateKey to derive public key from
|
686
|
+
* \return PublicKey instance initialized with data
|
687
|
+
* \raises TypeError if private key data is invalid
|
688
|
+
*/
|
689
|
+
static VALUE
|
690
|
+
PublicKey_initialize(VALUE self, VALUE in_context, VALUE in_private_key)
|
691
|
+
{
|
692
|
+
Context *context;
|
693
|
+
PublicKey *public_key;
|
694
|
+
PrivateKey *private_key;
|
695
|
+
|
696
|
+
Data_Get_Struct(self, PublicKey, public_key);
|
697
|
+
Data_Get_Struct(in_context, Context, context);
|
698
|
+
Data_Get_Struct(in_private_key, PrivateKey, private_key);
|
699
|
+
|
700
|
+
if (secp256k1_ec_pubkey_create(context->ctx,
|
701
|
+
&(public_key->pubkey),
|
702
|
+
private_key->data) == 0)
|
703
|
+
{
|
704
|
+
rb_raise(rb_eTypeError, "Invalid private key data");
|
705
|
+
return self;
|
706
|
+
}
|
707
|
+
|
708
|
+
public_key->context = context;
|
709
|
+
|
710
|
+
return self;
|
711
|
+
}
|
712
|
+
|
713
|
+
/* PublicKey#as_uncompressed */
|
714
|
+
static VALUE
|
715
|
+
PublicKey_as_uncompressed(VALUE self)
|
716
|
+
{
|
717
|
+
PublicKey *public_key;
|
718
|
+
size_t serialized_pubkey_len = 65;
|
719
|
+
unsigned char serialized_pubkey[65];
|
720
|
+
|
721
|
+
Data_Get_Struct(self, PublicKey, public_key);
|
722
|
+
|
723
|
+
if (public_key->context == NULL || public_key->context->ctx == NULL)
|
724
|
+
{
|
725
|
+
rb_raise(rb_eRuntimeError, "Public key context is NULL");
|
726
|
+
}
|
727
|
+
|
728
|
+
secp256k1_ec_pubkey_serialize(public_key->context->ctx,
|
729
|
+
serialized_pubkey,
|
730
|
+
&serialized_pubkey_len,
|
731
|
+
&(public_key->pubkey),
|
732
|
+
SECP256K1_EC_UNCOMPRESSED);
|
733
|
+
|
734
|
+
return rb_str_new((char*)serialized_pubkey, serialized_pubkey_len);
|
735
|
+
}
|
736
|
+
|
737
|
+
/* PublicKey#as_compressed */
|
738
|
+
static VALUE
|
739
|
+
PublicKey_as_compressed(VALUE self)
|
740
|
+
{
|
741
|
+
PublicKey *public_key;
|
742
|
+
size_t serialized_pubkey_len = 65;
|
743
|
+
unsigned char serialized_pubkey[65];
|
744
|
+
|
745
|
+
Data_Get_Struct(self, PublicKey, public_key);
|
746
|
+
|
747
|
+
secp256k1_ec_pubkey_serialize(public_key->context->ctx,
|
748
|
+
serialized_pubkey,
|
749
|
+
&serialized_pubkey_len,
|
750
|
+
&(public_key->pubkey),
|
751
|
+
SECP256K1_EC_COMPRESSED);
|
752
|
+
|
753
|
+
return rb_str_new((char*)serialized_pubkey, serialized_pubkey_len);
|
754
|
+
}
|
755
|
+
|
756
|
+
|
757
|
+
//
|
758
|
+
// Secp256k1::KeyPair class interface
|
759
|
+
//
|
760
|
+
|
761
|
+
static VALUE
|
762
|
+
KeyPair_alloc(VALUE klass)
|
763
|
+
{
|
764
|
+
VALUE result;
|
765
|
+
KeyPair *key_pair;
|
766
|
+
result = Data_Make_Struct(klass, KeyPair, NULL, free, key_pair);
|
767
|
+
|
768
|
+
return result;
|
769
|
+
}
|
770
|
+
|
771
|
+
static VALUE
|
772
|
+
KeyPair_initialize(VALUE self, VALUE public_key, VALUE private_key)
|
773
|
+
{
|
774
|
+
KeyPair *key_pair;
|
775
|
+
|
776
|
+
Data_Get_Struct(self, KeyPair, key_pair);
|
777
|
+
|
778
|
+
key_pair->public_key = public_key;
|
779
|
+
key_pair->private_key = private_key;
|
780
|
+
|
781
|
+
rb_iv_set(self, "@public_key", public_key);
|
782
|
+
rb_iv_set(self, "@private_key", private_key);
|
783
|
+
|
784
|
+
return self;
|
785
|
+
}
|
786
|
+
|
787
|
+
//
|
788
|
+
// Library initialization
|
789
|
+
//
|
790
|
+
|
791
|
+
void Init_rbsecp256k1()
|
792
|
+
{
|
793
|
+
// Secp256k1
|
794
|
+
Secp256k1_module = rb_define_module("Secp256k1");
|
795
|
+
rb_define_singleton_method(Secp256k1_module,
|
796
|
+
"generate_private_key_bytes",
|
797
|
+
Secp256k1_generate_private_key_bytes,
|
798
|
+
0);
|
799
|
+
|
800
|
+
// Secp256k1::Context
|
801
|
+
Secp256k1_Context_class = rb_define_class_under(
|
802
|
+
Secp256k1_module, "Context", rb_cObject
|
803
|
+
);
|
804
|
+
rb_define_alloc_func(Secp256k1_Context_class, Context_alloc);
|
805
|
+
rb_define_method(Secp256k1_Context_class,
|
806
|
+
"initialize",
|
807
|
+
Context_initialize,
|
808
|
+
0);
|
809
|
+
rb_define_method(Secp256k1_Context_class,
|
810
|
+
"generate_key_pair",
|
811
|
+
Context_generate_key_pair,
|
812
|
+
0);
|
813
|
+
rb_define_method(Secp256k1_Context_class,
|
814
|
+
"key_pair_from_private_key",
|
815
|
+
Context_key_pair_from_private_key,
|
816
|
+
1);
|
817
|
+
rb_define_method(Secp256k1_Context_class,
|
818
|
+
"public_key_from_data",
|
819
|
+
Context_public_key_from_data,
|
820
|
+
1);
|
821
|
+
rb_define_method(Secp256k1_Context_class,
|
822
|
+
"sign",
|
823
|
+
Context_sign,
|
824
|
+
2);
|
825
|
+
rb_define_method(Secp256k1_Context_class,
|
826
|
+
"verify",
|
827
|
+
Context_verify,
|
828
|
+
3);
|
829
|
+
rb_define_method(Secp256k1_Context_class,
|
830
|
+
"signature_from_der_encoded",
|
831
|
+
Context_signature_from_der_encoded,
|
832
|
+
1);
|
833
|
+
rb_define_method(Secp256k1_Context_class,
|
834
|
+
"signature_from_compact",
|
835
|
+
Context_signature_from_compact,
|
836
|
+
1);
|
837
|
+
|
838
|
+
// Secp256k1::KeyPair
|
839
|
+
Secp256k1_KeyPair_class = rb_define_class_under(Secp256k1_module,
|
840
|
+
"KeyPair",
|
841
|
+
rb_cObject);
|
842
|
+
rb_define_alloc_func(Secp256k1_KeyPair_class, KeyPair_alloc);
|
843
|
+
rb_define_attr(Secp256k1_KeyPair_class, "public_key", 1, 0);
|
844
|
+
rb_define_attr(Secp256k1_KeyPair_class, "private_key", 1, 0);
|
845
|
+
rb_define_method(Secp256k1_KeyPair_class,
|
846
|
+
"initialize",
|
847
|
+
KeyPair_initialize,
|
848
|
+
2);
|
849
|
+
|
850
|
+
// Secp256k1::PublicKey
|
851
|
+
Secp256k1_PublicKey_class = rb_define_class_under(Secp256k1_module,
|
852
|
+
"PublicKey",
|
853
|
+
rb_cObject);
|
854
|
+
rb_define_alloc_func(Secp256k1_PublicKey_class, PublicKey_alloc);
|
855
|
+
rb_define_method(Secp256k1_PublicKey_class,
|
856
|
+
"initialize",
|
857
|
+
PublicKey_initialize,
|
858
|
+
2);
|
859
|
+
rb_define_method(Secp256k1_PublicKey_class,
|
860
|
+
"as_compressed",
|
861
|
+
PublicKey_as_compressed,
|
862
|
+
0);
|
863
|
+
rb_define_method(Secp256k1_PublicKey_class,
|
864
|
+
"as_uncompressed",
|
865
|
+
PublicKey_as_uncompressed,
|
866
|
+
0);
|
867
|
+
|
868
|
+
// Secp256k1::PrivateKey
|
869
|
+
Secp256k1_PrivateKey_class = rb_define_class_under(
|
870
|
+
Secp256k1_module, "PrivateKey", rb_cObject
|
871
|
+
);
|
872
|
+
rb_define_alloc_func(Secp256k1_PrivateKey_class, PrivateKey_alloc);
|
873
|
+
rb_define_singleton_method(Secp256k1_PrivateKey_class,
|
874
|
+
"generate",
|
875
|
+
PrivateKey_generate,
|
876
|
+
0);
|
877
|
+
rb_define_attr(Secp256k1_PrivateKey_class, "data", 1, 0);
|
878
|
+
rb_define_method(Secp256k1_PrivateKey_class,
|
879
|
+
"initialize",
|
880
|
+
PrivateKey_initialize,
|
881
|
+
1);
|
882
|
+
|
883
|
+
// Secp256k1::Signature
|
884
|
+
Secp256k1_Signature_class = rb_define_class_under(Secp256k1_module,
|
885
|
+
"Signature",
|
886
|
+
rb_cObject);
|
887
|
+
rb_define_alloc_func(Secp256k1_Signature_class, Signature_alloc);
|
888
|
+
rb_define_method(Secp256k1_Signature_class,
|
889
|
+
"der_encoded",
|
890
|
+
Signature_der_encoded,
|
891
|
+
0);
|
892
|
+
rb_define_method(Secp256k1_Signature_class,
|
893
|
+
"compact",
|
894
|
+
Signature_compact,
|
895
|
+
0);
|
896
|
+
}
|
data/lib/rbsecp256k1.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Secp256k1
|
2
|
+
# Contains utility methods that complement the functionality of the library.
|
3
|
+
module Util
|
4
|
+
# Converts a binary string to a hex string.
|
5
|
+
#
|
6
|
+
# @param binary_string [String] binary string to be converted.
|
7
|
+
# @return [String] hex string equivalent of the given binary string.
|
8
|
+
def self.bin_to_hex(binary_string)
|
9
|
+
binary_string.unpack('H*').first
|
10
|
+
end
|
11
|
+
|
12
|
+
# Converts a hex string to a binary string.
|
13
|
+
#
|
14
|
+
# @param hex_string [String] string with hexadeimcal value.
|
15
|
+
# @return [String] binary string equivalent of the given hex string.
|
16
|
+
def self.hex_to_bin(hex_string)
|
17
|
+
[hex_string].pack('H*')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rbsecp256k1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eric Scrivner
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-12-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '12.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '12.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake-compiler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.8'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.61'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.61'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
executables: []
|
72
|
+
extensions:
|
73
|
+
- ext/rbsecp256k1/extconf.rb
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- Rakefile
|
77
|
+
- ext/rbsecp256k1/extconf.rb
|
78
|
+
- ext/rbsecp256k1/rbsecp256k1.c
|
79
|
+
- lib/rbsecp256k1.rb
|
80
|
+
- lib/rbsecp256k1/util.rb
|
81
|
+
- lib/rbsecp256k1/version.rb
|
82
|
+
homepage: https://github.com/etscrivner/rbsecp256k1
|
83
|
+
licenses:
|
84
|
+
- MIT
|
85
|
+
metadata: {}
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- ext
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 2.7.6
|
104
|
+
signing_key:
|
105
|
+
specification_version: 4
|
106
|
+
summary: Compiled, native ruby extension interfaces to libsecp256k1
|
107
|
+
test_files: []
|