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.
@@ -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
@@ -0,0 +1,5 @@
1
+ require "rake/extensiontask"
2
+
3
+ Rake::ExtensionTask.new "rbsecp256k1" do |ext|
4
+ ext.lib_dir = "lib/rbsecp256k1"
5
+ end
@@ -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
+ }
@@ -0,0 +1,7 @@
1
+ # Wraps libsecp256k1 in a ruby module and provides object interfaces.
2
+ module Secp256k1
3
+ end
4
+
5
+ require 'rbsecp256k1/util'
6
+ require 'rbsecp256k1/version'
7
+ require 'rbsecp256k1/rbsecp256k1'
@@ -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
@@ -0,0 +1,3 @@
1
+ module Secp256k1
2
+ VERSION = '1.0.0'.freeze
3
+ 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: []