rbsecp256k1 1.0.0 → 2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad49bf4a099e7afb5cf5ef09af6d61f5019c4378fdc0bda9bc1b7b66d2cbca8b
4
- data.tar.gz: 8de35376f226a7955feb12c8214a6f023cbed7dde25fc1d98aa49464e6777cb5
3
+ metadata.gz: 5549a7ec71d1bddbefe1ea360d9316ea1bbe74f015e6676cfc7495a7b852831a
4
+ data.tar.gz: dada9673b8fdf01e29ca2a2479ac3ce92ff43ba736f577feb6e58515ae3ab240
5
5
  SHA512:
6
- metadata.gz: 68a48a57862f066da1477de75283e18c20daa80bb8508e5cf39da06885eb12bc38d40f06e35958d13667fac942d31315b6a13a175970bc5e929eff8bcf1a1514
7
- data.tar.gz: b802bda821e12c398c379b003a4a60956d6d69b8f30fe26e2d799ef23290f228ffca603a30e3c376dfe0b546b2d8b7b9dedc86c6862d963228d67c3f3fbb0efe
6
+ metadata.gz: 9ac13a464fd1e0c5ee4378cea9f6b9c557d65b08e00a00f4dc94b64fc43822cec9c5e69463a450daa6725d12679a5e9440bb0e60a20f282346551099b64e2802
7
+ data.tar.gz: 58f2932b693056f4051094d43d68636c2a3d99c71d475c5c1e7ca9c062a4e5d956e844209956e7e4041351677e4c4b376bd4b4a0e9dbdc13dab7e52c099302d0
@@ -1,13 +1,19 @@
1
1
  require 'mkmf'
2
2
 
3
3
  # OpenSSL flags
4
- print("Looking for OpenSSL")
4
+ print("checking for OpenSSL\n")
5
5
  results = pkg_config('openssl')
6
6
  abort "missing openssl pkg-config information" unless results[1]
7
7
 
8
8
  # Require that libsecp256k1 be installed using `make install` or similar.
9
- print("Looking for libsecp256k1")
9
+ print("checking for libsecp256k1\n")
10
10
  results = pkg_config('libsecp256k1')
11
11
  abort "missing libsecp256k1" unless results[1]
12
12
 
13
+ # Check if we have the libsecp256k1 recoverable signature header.
14
+ have_header('secp256k1_recovery.h')
15
+
16
+ # Check if we have EC Diffie-Hellman functionality
17
+ have_header('secp256k1_ecdh.h')
18
+
13
19
  create_makefile('rbsecp256k1')
@@ -9,12 +9,21 @@
9
9
  // * libsecp256k1
10
10
  // * openssl
11
11
  #include <ruby.h>
12
- #include <stdio.h>
13
12
 
14
13
  #include <openssl/rand.h>
15
- #include <openssl/sha.h>
14
+
16
15
  #include <secp256k1.h>
17
16
 
17
+ // Include recoverable signatures functionality if available
18
+ #ifdef HAVE_SECP256K1_RECOVERY_H
19
+ #include <secp256k1_recovery.h>
20
+ #endif // HAVE_SECP256K1_RECOVERY_H
21
+
22
+ // Include EC Diffie-Hellman functionality
23
+ #ifdef HAVE_SECP256K1_ECDH_H
24
+ #include <secp256k1_ecdh.h>
25
+ #endif // HAVE_SECP256K1_ECDH_H
26
+
18
27
  // High-level design:
19
28
  //
20
29
  // The Ruby wrapper is divided into the following hierarchical organization:
@@ -24,20 +33,30 @@
24
33
  // |-- KeyPair
25
34
  // |-- PublicKey
26
35
  // |-- PrivateKey
36
+ // |-- RecoverableSignature
37
+ // |-- SharedSecret
27
38
  // |-- Signature
28
39
  //
29
40
  // 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.
41
+ // The KayPair, PublicKey, PrivateKey, RecoverableSignature, SharedSecret, and
42
+ // Signature objects act as data objects and are passed to various
43
+ // methods. Contexts are thread safe and can be used across
44
+ // applications. Context initialization is expensive so it is recommended that
45
+ // a single context be initialized and used throughout an application when
46
+ // possible.
35
47
 
36
48
  //
37
49
  // The section below contains purely internal methods used exclusively by the
38
50
  // C internals of the library.
39
51
  //
40
52
 
53
+ // Size of an uncompressed public key
54
+ const size_t UNCOMPRESSED_PUBKEY_SIZE_BYTES = 65;
55
+ // Size of a compressed public key
56
+ const size_t COMPRESSED_PUBKEY_SIZE_BYTES = 33;
57
+ // Size of a compact signature in bytes
58
+ const size_t COMPACT_SIG_SIZE_BYTES = 64;
59
+
41
60
  // Globally define our module and its associated classes so we can instantiate
42
61
  // objects from anywhere. The use of global variables seems to be inline with
43
62
  // how the Ruby project builds its own extension gems.
@@ -48,6 +67,14 @@ static VALUE Secp256k1_PublicKey_class;
48
67
  static VALUE Secp256k1_PrivateKey_class;
49
68
  static VALUE Secp256k1_Signature_class;
50
69
 
70
+ #ifdef HAVE_SECP256K1_RECOVERY_H
71
+ static VALUE Secp256k1_RecoverableSignature_class;
72
+ #endif // HAVE_SECP256K1_RECOVERY_H
73
+
74
+ #ifdef HAVE_SECP256K1_ECDH_H
75
+ static VALUE Secp256k1_SharedSecret_class;
76
+ #endif // HAVE_SECP256K1_ECDH_H
77
+
51
78
  // Forward definitions for all structures
52
79
  typedef struct Context_dummy {
53
80
  secp256k1_context *ctx; // Context used by libsecp256k1 library
@@ -59,19 +86,168 @@ typedef struct KeyPair_dummy {
59
86
  } KeyPair;
60
87
 
61
88
  typedef struct PublicKey_dummy {
62
- secp256k1_pubkey pubkey;
63
- Context *context;
89
+ secp256k1_pubkey pubkey; // Opaque object containing public key data
90
+ secp256k1_context *ctx;
64
91
  } PublicKey;
65
92
 
66
93
  typedef struct PrivateKey_dummy {
67
94
  unsigned char data[32]; // Bytes comprising the private key data
95
+ secp256k1_context *ctx;
68
96
  } PrivateKey;
69
97
 
70
98
  typedef struct Signature_dummy {
71
- secp256k1_ecdsa_signature sig; // Signature object, contains 64-byte signature.
72
- Context *context;
99
+ secp256k1_ecdsa_signature sig; // Signature object, contains 64-byte signature
100
+ secp256k1_context *ctx;
73
101
  } Signature;
74
102
 
103
+ #ifdef HAVE_SECP256K1_RECOVERY_H
104
+ typedef struct RecoverableSignature_dummy {
105
+ secp256k1_ecdsa_recoverable_signature sig; // Recoverable signature object
106
+ secp256k1_context *ctx;
107
+ } RecoverableSignature;
108
+ #endif // HAVE_SECP256K1_RECOVERY_H
109
+
110
+ #ifdef HAVE_SECP256K1_ECDH_H
111
+ typedef struct SharedSecret_dummy {
112
+ unsigned char data[32]; // Shared secret data
113
+ } SharedSecret;
114
+ #endif // HAVE_SECP256K1_ECDH_H
115
+
116
+ //
117
+ // Typed data definitions
118
+ //
119
+
120
+ // Context
121
+ static void
122
+ Context_free(void* in_context)
123
+ {
124
+ Context *context;
125
+ context = (Context*)in_context;
126
+ secp256k1_context_destroy(context->ctx);
127
+ xfree(context);
128
+ }
129
+
130
+ static const rb_data_type_t Context_DataType = {
131
+ "Context",
132
+ { 0, Context_free, 0 },
133
+ 0, 0,
134
+ RUBY_TYPED_FREE_IMMEDIATELY
135
+ };
136
+
137
+ // PublicKey
138
+ static void
139
+ PublicKey_free(void *in_public_key)
140
+ {
141
+ PublicKey *public_key;
142
+ public_key = (PublicKey*)in_public_key;
143
+ secp256k1_context_destroy(public_key->ctx);
144
+ xfree(public_key);
145
+ }
146
+
147
+ static const rb_data_type_t PublicKey_DataType = {
148
+ "PublicKey",
149
+ { 0, PublicKey_free, 0 },
150
+ 0, 0,
151
+ RUBY_TYPED_FREE_IMMEDIATELY
152
+ };
153
+
154
+ // PrivateKey
155
+ static void
156
+ PrivateKey_free(void *in_private_key)
157
+ {
158
+ PrivateKey *private_key;
159
+ private_key = (PrivateKey*)in_private_key;
160
+ secp256k1_context_destroy(private_key->ctx);
161
+ xfree(private_key);
162
+ }
163
+
164
+ static const rb_data_type_t PrivateKey_DataType = {
165
+ "PrivateKey",
166
+ { 0, PrivateKey_free, 0 },
167
+ 0, 0,
168
+ RUBY_TYPED_FREE_IMMEDIATELY
169
+ };
170
+
171
+ // KeyPair
172
+ static void
173
+ KeyPair_mark(void *in_key_pair)
174
+ {
175
+ KeyPair *key_pair = (KeyPair*)in_key_pair;
176
+
177
+ // Mark both contained objects to ensure they are properly garbage collected
178
+ rb_gc_mark(key_pair->public_key);
179
+ rb_gc_mark(key_pair->private_key);
180
+ }
181
+
182
+ static void
183
+ KeyPair_free(void *self)
184
+ {
185
+ xfree(self);
186
+ }
187
+
188
+ static const rb_data_type_t KeyPair_DataType = {
189
+ "KeyPair",
190
+ { KeyPair_mark, KeyPair_free, 0 },
191
+ 0, 0,
192
+ RUBY_TYPED_FREE_IMMEDIATELY
193
+ };
194
+
195
+ // Signature
196
+ static void
197
+ Signature_free(void *in_signature)
198
+ {
199
+ Signature *signature = (Signature*)in_signature;
200
+ secp256k1_context_destroy(signature->ctx);
201
+ xfree(signature);
202
+ }
203
+
204
+ static const rb_data_type_t Signature_DataType = {
205
+ "Signature",
206
+ { 0, Signature_free, 0 },
207
+ 0, 0,
208
+ RUBY_TYPED_FREE_IMMEDIATELY
209
+ };
210
+
211
+ // RecoverableSignature
212
+ #ifdef HAVE_SECP256K1_RECOVERY_H
213
+ static void
214
+ RecoverableSignature_free(void *in_recoverable_signature)
215
+ {
216
+ RecoverableSignature *recoverable_signature = (
217
+ (RecoverableSignature*)in_recoverable_signature
218
+ );
219
+
220
+ secp256k1_context_destroy(recoverable_signature->ctx);
221
+ xfree(recoverable_signature);
222
+ }
223
+
224
+ static const rb_data_type_t RecoverableSignature_DataType = {
225
+ "RecoverableSignature",
226
+ { 0, RecoverableSignature_free, 0 },
227
+ 0, 0,
228
+ RUBY_TYPED_FREE_IMMEDIATELY
229
+ };
230
+ #endif // HAVE_SECP256K1_RECOVERY_H
231
+
232
+ // SharedSecret
233
+ #ifdef HAVE_SECP256K1_ECDH_H
234
+ static void
235
+ SharedSecret_free(void *in_shared_secret)
236
+ {
237
+ SharedSecret *shared_secret;
238
+
239
+ shared_secret = (SharedSecret*)in_shared_secret;
240
+ xfree(shared_secret);
241
+ }
242
+
243
+ static const rb_data_type_t SharedSecret_DataType = {
244
+ "SharedSecret",
245
+ { 0, SharedSecret_free, 0 },
246
+ 0, 0,
247
+ RUBY_TYPED_FREE_IMMEDIATELY
248
+ };
249
+ #endif // HAVE_SECP256K1_ECDH_H
250
+
75
251
  /**
76
252
  * Macro: SUCCESS
77
253
  *
@@ -120,225 +296,718 @@ GenerateRandomBytes(unsigned char *out_bytes, size_t in_size)
120
296
  }
121
297
 
122
298
  /**
123
- * Computes the ECDSA signature of the given data.
299
+ * Computes the ECDSA signature of the given 32-byte SHA-256 hash.
124
300
  *
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.
301
+ * \param in_context libsecp256k1 context
302
+ * \param in_hash32 32-byte SHA-256 hash
303
+ * \param in_private_key Private key to be used for signing
304
+ * \param out_signature Signature produced during the signing proccess
305
+ * \return RESULT_SUCCESS if the hash and signature were computed successfully,
306
+ * RESULT_FAILURE if signing failed or DER encoding failed.
307
+ */
308
+ static ResultT
309
+ SignData(secp256k1_context *in_context,
310
+ unsigned char *in_hash32,
311
+ unsigned char *in_private_key,
312
+ secp256k1_ecdsa_signature *out_signature)
313
+ {
314
+ // Sign the hash of the data
315
+ if (secp256k1_ecdsa_sign(in_context,
316
+ out_signature,
317
+ in_hash32,
318
+ in_private_key,
319
+ NULL,
320
+ NULL) == 1)
321
+ {
322
+ return RESULT_SUCCESS;
323
+ }
324
+
325
+ return RESULT_FAILURE;
326
+ }
327
+
328
+ #ifdef HAVE_SECP256K1_RECOVERY_H
329
+
330
+ /**
331
+ * Computes the recoverable ECDSA signature of the given data.
127
332
  *
128
333
  * ECDSA signing involves the following steps:
129
334
  * 1. Compute the 32-byte SHA-256 hash of the given data.
130
335
  * 2. Sign the 32-byte hash using the private key provided.
131
336
  *
132
337
  * \param in_context libsecp256k1 context
133
- * \param in_data Data to be signed
134
- * \param in_data_len Length of data to be signed
338
+ * \param in_hash32 32-byte SHA-256 hash to sign
135
339
  * \param in_private_key Private key to be used for signing
136
- * \param out_signature Signature produced during the signing proccess
340
+ * \param out_signature Recoverable signature computed
137
341
  * \return RESULT_SUCCESS if the hash and signature were computed successfully,
138
342
  * RESULT_FAILURE if signing failed or DER encoding failed.
139
343
  */
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)
344
+ static ResultT
345
+ RecoverableSignData(secp256k1_context *in_context,
346
+ unsigned char *in_hash32,
347
+ unsigned char *in_private_key,
348
+ secp256k1_ecdsa_recoverable_signature *out_signature)
349
+ {
350
+ if (secp256k1_ecdsa_sign_recoverable(in_context,
351
+ out_signature,
352
+ in_hash32,
353
+ in_private_key,
354
+ NULL,
355
+ NULL) == 1)
356
+ {
357
+ return RESULT_SUCCESS;
358
+ }
359
+
360
+ return RESULT_FAILURE;
361
+ }
362
+
363
+ #endif // HAVE_SECP256K1_RECOVERY_H
364
+
365
+ //
366
+ // Secp256k1::KeyPair class interface
367
+ //
368
+
369
+ static VALUE
370
+ KeyPair_alloc(VALUE klass)
371
+ {
372
+ KeyPair *key_pair;
373
+
374
+ key_pair = ALLOC(KeyPair);
375
+
376
+ return TypedData_Wrap_Struct(klass, &KeyPair_DataType, key_pair);
377
+ }
378
+
379
+ /**
380
+ * Default constructor.
381
+ *
382
+ * @param in_public_key [Secp256k1::PublicKey] public key
383
+ * @param in_private_key [Secp256k1::PrivateKey] private key
384
+ * @return [Secp256k1::KeyPair] newly initialized key pair.
385
+ */
386
+ static VALUE
387
+ KeyPair_initialize(VALUE self, VALUE in_public_key, VALUE in_private_key)
388
+ {
389
+ KeyPair *key_pair;
390
+
391
+ TypedData_Get_Struct(self, KeyPair, &KeyPair_DataType, key_pair);
392
+ Check_TypedStruct(in_public_key, &PublicKey_DataType);
393
+ Check_TypedStruct(in_private_key, &PrivateKey_DataType);
394
+
395
+ key_pair->public_key = in_public_key;
396
+ key_pair->private_key = in_private_key;
397
+
398
+ rb_iv_set(self, "@public_key", in_public_key);
399
+ rb_iv_set(self, "@private_key", in_private_key);
400
+
401
+ return self;
402
+ }
403
+
404
+ /**
405
+ * Compare two key pairs.
406
+ *
407
+ * Two key pairs are equal if they have the same public and private key. The
408
+ * keys are compared using their own comparison operators.
409
+ *
410
+ * @param other [Secp256k1::KeyPair] key pair to compare to.
411
+ * @return [Boolean] true if the keys match, false otherwise.
412
+ */
413
+ static VALUE
414
+ KeyPair_equals(VALUE self, VALUE other)
415
+ {
416
+ KeyPair *lhs;
417
+ KeyPair *rhs;
418
+ VALUE public_keys_equal;
419
+ VALUE private_keys_equal;
420
+
421
+ TypedData_Get_Struct(self, KeyPair, &KeyPair_DataType, lhs);
422
+ TypedData_Get_Struct(other, KeyPair, &KeyPair_DataType, rhs);
423
+
424
+ public_keys_equal = rb_funcall(
425
+ lhs->public_key, rb_intern("=="), 1, rhs->public_key
426
+ );
427
+ private_keys_equal = rb_funcall(
428
+ lhs->private_key, rb_intern("=="), 1, rhs->private_key
429
+ );
430
+
431
+ if (public_keys_equal == Qtrue && private_keys_equal == Qtrue)
432
+ {
433
+ return Qtrue;
434
+ }
435
+
436
+ return Qfalse;
437
+ }
438
+
439
+ //
440
+ // Secp256k1::PublicKey class interface
441
+ //
442
+
443
+ static VALUE
444
+ PublicKey_alloc(VALUE klass)
445
+ {
446
+ VALUE result;
447
+ PublicKey *public_key;
448
+
449
+ public_key = ALLOC(PublicKey);
450
+ MEMZERO(public_key, PublicKey, 1);
451
+ result = TypedData_Wrap_Struct(klass, &PublicKey_DataType, public_key);
452
+
453
+ return result;
454
+ }
455
+
456
+ static VALUE
457
+ PublicKey_create_from_private_key(Context *in_context,
458
+ unsigned char *private_key_data)
459
+ {
460
+ PublicKey *public_key;
461
+ VALUE result;
462
+
463
+ result = PublicKey_alloc(Secp256k1_PublicKey_class);
464
+ TypedData_Get_Struct(result, PublicKey, &PublicKey_DataType, public_key);
465
+
466
+ if (secp256k1_ec_pubkey_create(
467
+ in_context->ctx,
468
+ (&public_key->pubkey),
469
+ private_key_data) != 1)
470
+ {
471
+ rb_raise(rb_eTypeError, "invalid private key data");
472
+ }
473
+
474
+ public_key->ctx = secp256k1_context_clone(in_context->ctx);
475
+ return result;
476
+ }
477
+
478
+ static VALUE
479
+ PublicKey_create_from_data(Context *in_context,
480
+ unsigned char *in_public_key_data,
481
+ unsigned int in_public_key_data_len)
482
+ {
483
+ PublicKey *public_key;
484
+ VALUE result;
485
+
486
+ result = PublicKey_alloc(Secp256k1_PublicKey_class);
487
+ TypedData_Get_Struct(result, PublicKey, &PublicKey_DataType, public_key);
488
+
489
+ if (secp256k1_ec_pubkey_parse(in_context->ctx,
490
+ &(public_key->pubkey),
491
+ in_public_key_data,
492
+ in_public_key_data_len) != 1)
493
+ {
494
+ rb_raise(rb_eRuntimeError, "invalid public key data");
495
+ }
496
+
497
+ public_key->ctx = secp256k1_context_clone(in_context->ctx);
498
+ return result;
499
+ }
500
+
501
+ /**
502
+ * @return [String] binary string containing the uncompressed representation
503
+ * of this public key.
504
+ */
505
+ static VALUE
506
+ PublicKey_uncompressed(VALUE self)
507
+ {
508
+ // TODO: Cache value after first computation
509
+ PublicKey *public_key;
510
+ size_t serialized_pubkey_len = UNCOMPRESSED_PUBKEY_SIZE_BYTES;
511
+ unsigned char serialized_pubkey[UNCOMPRESSED_PUBKEY_SIZE_BYTES];
512
+
513
+ TypedData_Get_Struct(self, PublicKey, &PublicKey_DataType, public_key);
514
+
515
+ secp256k1_ec_pubkey_serialize(public_key->ctx,
516
+ serialized_pubkey,
517
+ &serialized_pubkey_len,
518
+ &(public_key->pubkey),
519
+ SECP256K1_EC_UNCOMPRESSED);
520
+
521
+ return rb_str_new((char*)serialized_pubkey, serialized_pubkey_len);
522
+ }
523
+
524
+ /**
525
+ * @return [String] binary string containing the compressed representation of
526
+ * this public key.
527
+ */
528
+ static VALUE
529
+ PublicKey_compressed(VALUE self)
530
+ {
531
+ // TODO: Cache value after first computation
532
+ PublicKey *public_key;
533
+ size_t serialized_pubkey_len = COMPRESSED_PUBKEY_SIZE_BYTES;
534
+ unsigned char serialized_pubkey[COMPRESSED_PUBKEY_SIZE_BYTES];
535
+
536
+ TypedData_Get_Struct(self, PublicKey, &PublicKey_DataType, public_key);
537
+
538
+ secp256k1_ec_pubkey_serialize(public_key->ctx,
539
+ serialized_pubkey,
540
+ &serialized_pubkey_len,
541
+ &(public_key->pubkey),
542
+ SECP256K1_EC_COMPRESSED);
543
+
544
+ return rb_str_new((char*)serialized_pubkey, serialized_pubkey_len);
545
+ }
546
+
547
+ /**
548
+ * Compares two public keys.
549
+ *
550
+ * Public keys are considered equal if their compressed representations match.
551
+ *
552
+ * @param other [Secp256k1::PublicKey] public key to compare.
553
+ * @return [Boolean] true if the public keys are identical, false otherwise.
554
+ */
555
+ static VALUE
556
+ PublicKey_equals(VALUE self, VALUE other)
557
+ {
558
+ PublicKey *lhs;
559
+ PublicKey *rhs;
560
+ unsigned char lhs_compressed[33];
561
+ unsigned char rhs_compressed[33];
562
+ size_t lhs_len;
563
+ size_t rhs_len;
564
+
565
+ lhs_len = 33;
566
+ rhs_len = 33;
567
+
568
+ TypedData_Get_Struct(self, PublicKey, &PublicKey_DataType, lhs);
569
+ TypedData_Get_Struct(other, PublicKey, &PublicKey_DataType, rhs);
570
+
571
+ secp256k1_ec_pubkey_serialize(
572
+ lhs->ctx,
573
+ lhs_compressed,
574
+ &lhs_len,
575
+ &(lhs->pubkey),
576
+ SECP256K1_EC_COMPRESSED
577
+ );
578
+ secp256k1_ec_pubkey_serialize(
579
+ rhs->ctx,
580
+ rhs_compressed,
581
+ &rhs_len,
582
+ &(rhs->pubkey),
583
+ SECP256K1_EC_COMPRESSED
584
+ );
585
+
586
+ if (lhs_len == rhs_len &&
587
+ memcmp(lhs_compressed, rhs_compressed, lhs_len) == 0)
588
+ {
589
+ return Qtrue;
590
+ }
591
+
592
+ return Qfalse;
593
+ }
594
+
595
+ //
596
+ // Secp256k1::PrivateKey class interface
597
+ //
598
+
599
+ static VALUE
600
+ PrivateKey_alloc(VALUE klass)
601
+ {
602
+ VALUE new_instance;
603
+ PrivateKey *private_key;
604
+
605
+ private_key = ALLOC(PrivateKey);
606
+ MEMZERO(private_key, PrivateKey, 1);
607
+ new_instance = TypedData_Wrap_Struct(klass, &PrivateKey_DataType, private_key);
608
+
609
+ return new_instance;
610
+ }
611
+
612
+ static VALUE
613
+ PrivateKey_create(Context *in_context, unsigned char *in_private_key_data)
614
+ {
615
+ PrivateKey *private_key;
616
+ VALUE result;
617
+
618
+ if (secp256k1_ec_seckey_verify(in_context->ctx, in_private_key_data) != 1)
619
+ {
620
+ rb_raise(rb_eArgError, "invalid private key data");
621
+ }
622
+
623
+ result = PrivateKey_alloc(Secp256k1_PrivateKey_class);
624
+ TypedData_Get_Struct(result, PrivateKey, &PrivateKey_DataType, private_key);
625
+ MEMCPY(private_key->data, in_private_key_data, char, 32);
626
+ private_key->ctx = secp256k1_context_clone(in_context->ctx);
627
+
628
+ rb_iv_set(result, "@data", rb_str_new((char*)in_private_key_data, 32));
629
+
630
+ return result;
631
+ }
632
+
633
+ /**
634
+ * Compare two private keys.
635
+ *
636
+ * Private keys are considered equal if their data fields are identical.
637
+ *
638
+ * @param other [Secp256k1::PrivateKey] private key to compare.
639
+ * @return [Boolean] true if they are equal, false otherwise.
640
+ */
641
+ static VALUE
642
+ PrivateKey_equals(VALUE self, VALUE other)
643
+ {
644
+ PrivateKey *lhs;
645
+ PrivateKey *rhs;
646
+
647
+ TypedData_Get_Struct(self, PrivateKey, &PrivateKey_DataType, lhs);
648
+ TypedData_Get_Struct(other, PrivateKey, &PrivateKey_DataType, rhs);
649
+
650
+ if (memcmp(lhs->data, rhs->data, 32) == 0)
651
+ {
652
+ return Qtrue;
653
+ }
654
+
655
+ return Qfalse;
656
+ }
657
+
658
+ //
659
+ // Secp256k1::Signature class interface
660
+ //
661
+
662
+ static VALUE
663
+ Signature_alloc(VALUE klass)
664
+ {
665
+ VALUE new_instance;
666
+ Signature *signature;
667
+
668
+ signature = ALLOC(Signature);
669
+ MEMZERO(signature, Signature, 1);
670
+ new_instance = TypedData_Wrap_Struct(klass, &Signature_DataType, signature);
671
+
672
+ return new_instance;
673
+ }
674
+
675
+ /**
676
+ * Return Distinguished Encoding Rules (DER) encoded signature data.
677
+ *
678
+ * @return [String] binary string containing DER-encoded signature data.
679
+ */
680
+ static VALUE
681
+ Signature_der_encoded(VALUE self)
682
+ {
683
+ // TODO: Cache value after first computation
684
+ Signature *signature;
685
+ unsigned long der_signature_len;
686
+ unsigned char der_signature[72];
687
+
688
+ TypedData_Get_Struct(self, Signature, &Signature_DataType, signature);
689
+
690
+ der_signature_len = 72;
691
+ if (secp256k1_ecdsa_signature_serialize_der(signature->ctx,
692
+ der_signature,
693
+ &der_signature_len,
694
+ &(signature->sig)) != 1)
695
+ {
696
+ rb_raise(rb_eRuntimeError, "could not compute DER encoded signature");
697
+ }
698
+
699
+ return rb_str_new((char*)der_signature, der_signature_len);
700
+ }
701
+
702
+ /**
703
+ * Returns the 64 byte compact representation of this signature.
704
+ *
705
+ * @return [String] 64 byte binary string containing signature data.
706
+ */
707
+ static VALUE
708
+ Signature_compact(VALUE self)
709
+ {
710
+ // TODO: Cache value after first computation
711
+ Signature *signature;
712
+ unsigned char compact_signature[COMPACT_SIG_SIZE_BYTES];
713
+
714
+ TypedData_Get_Struct(self, Signature, &Signature_DataType, signature);
715
+
716
+ if (secp256k1_ecdsa_signature_serialize_compact(signature->ctx,
717
+ compact_signature,
718
+ &(signature->sig)) != 1)
719
+ {
720
+ rb_raise(rb_eRuntimeError, "unable to compute compact signature");
721
+ }
722
+
723
+ return rb_str_new((char*)compact_signature, COMPACT_SIG_SIZE_BYTES);
724
+ }
725
+
726
+ /**
727
+ * Returns the normalized lower-S form of this signature.
728
+ *
729
+ * This can be useful when importing signatures generated by other applications
730
+ * that may not be normalized. Non-normalized signatures are potentially
731
+ * forgeable.
732
+ *
733
+ * @return [Array] first element is a boolean that is `true` if the signature
734
+ * was normalized, false otherwise. The second element is a `Signature`
735
+ * object corresponding to the normalized signature.
736
+ */
737
+ static VALUE
738
+ Signature_normalized(VALUE self)
146
739
  {
147
- unsigned char hash[SHA256_DIGEST_LENGTH];
740
+ VALUE result_sig;
741
+ VALUE was_normalized;
742
+ VALUE result;
743
+ Signature *signature;
744
+ Signature *normalized_signature;
148
745
 
149
- // Compute the SHA-256 hash of data
150
- SHA256(in_data, in_data_len, hash);
746
+ TypedData_Get_Struct(self, Signature, &Signature_DataType, signature);
747
+ result_sig = Signature_alloc(Secp256k1_Signature_class);
748
+ TypedData_Get_Struct(
749
+ result_sig, Signature, &Signature_DataType, normalized_signature
750
+ );
151
751
 
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)
752
+ was_normalized = Qfalse;
753
+ if (secp256k1_ecdsa_signature_normalize(
754
+ signature->ctx,
755
+ &(normalized_signature->sig),
756
+ &(signature->sig)) == 1)
159
757
  {
160
- return RESULT_SUCCESS;
758
+ was_normalized = Qtrue;
161
759
  }
162
760
 
163
- return RESULT_FAILURE;
761
+ normalized_signature->ctx = secp256k1_context_clone(signature->ctx);
762
+
763
+ result = rb_ary_new2(2);
764
+ rb_ary_push(result, was_normalized);
765
+ rb_ary_push(result, result_sig);
766
+
767
+ return result;
164
768
  }
165
769
 
166
770
  /**
167
- * Secp256k1.generate_private_key_bytes
771
+ * Compares two signatures.
168
772
  *
169
- * Generate cryptographically secure 32 byte private key data.
773
+ * Two signatures are equal if their compact encodings are identical.
170
774
  *
171
- * Raises:
172
- * RuntimeError - If random number generation fails for any reason.
775
+ * @param other [Secp256k1::Signature] signature to compare
776
+ * @return [Boolean] true if signatures match, false otherwise.
173
777
  */
174
778
  static VALUE
175
- Secp256k1_generate_private_key_bytes(VALUE self)
779
+ Signature_equals(VALUE self, VALUE other)
176
780
  {
177
- unsigned char private_key_bytes[32];
781
+ Signature *lhs;
782
+ Signature *rhs;
783
+ unsigned char lhs_compact[64];
784
+ unsigned char rhs_compact[64];
178
785
 
179
- if (FAILURE(GenerateRandomBytes(private_key_bytes, 32)))
786
+ TypedData_Get_Struct(self, Signature, &Signature_DataType, lhs);
787
+ TypedData_Get_Struct(other, Signature, &Signature_DataType, rhs);
788
+
789
+ secp256k1_ecdsa_signature_serialize_compact(
790
+ lhs->ctx, lhs_compact, &(lhs->sig)
791
+ );
792
+ secp256k1_ecdsa_signature_serialize_compact(
793
+ rhs->ctx, rhs_compact, &(rhs->sig)
794
+ );
795
+
796
+ if (memcmp(lhs_compact, rhs_compact, 64) == 0)
180
797
  {
181
- rb_raise(rb_eRuntimeError, "Random number generation failed.");
798
+ return Qtrue;
182
799
  }
183
800
 
184
- return rb_str_new((char*)private_key_bytes, 32);
801
+ return Qfalse;
185
802
  }
186
803
 
187
804
  //
188
- // Secp256k1::PrivateKey class interface
805
+ // Secp256k1::RecoverableSignature class interface
189
806
  //
190
807
 
191
- /* Allocate space for new private key internal data */
808
+ #ifdef HAVE_SECP256K1_RECOVERY_H
809
+
192
810
  static VALUE
193
- PrivateKey_alloc(VALUE klass)
811
+ RecoverableSignature_alloc(VALUE klass)
194
812
  {
195
813
  VALUE new_instance;
196
- PrivateKey *private_key;
814
+ RecoverableSignature *recoverable_signature;
197
815
 
198
- new_instance = Data_Make_Struct(
199
- klass, PrivateKey, NULL, free, private_key
816
+ recoverable_signature = ALLOC(RecoverableSignature);
817
+ MEMZERO(recoverable_signature, RecoverableSignature, 1);
818
+ new_instance = TypedData_Wrap_Struct(
819
+ klass, &RecoverableSignature_DataType, recoverable_signature
200
820
  );
201
- memset(private_key->data, 0, 32);
202
821
 
203
822
  return new_instance;
204
823
  }
205
824
 
206
825
  /**
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.
826
+ * Returns the compact encoding of recoverable signature.
227
827
  *
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.
828
+ * @return [Array] first element is the 64 byte compact encoding of signature,
829
+ * the second element is the integer recovery ID.
830
+ * @raise [RuntimeError] if signature serialization fails.
231
831
  */
232
832
  static VALUE
233
- PrivateKey_initialize(VALUE self, VALUE in_bytes)
833
+ RecoverableSignature_compact(VALUE self)
234
834
  {
235
- PrivateKey *private_key;
835
+ RecoverableSignature *recoverable_signature;
836
+ unsigned char compact_sig[64];
837
+ int recovery_id;
838
+ VALUE result;
236
839
 
237
- Check_Type(in_bytes, T_STRING);
840
+ TypedData_Get_Struct(
841
+ self,
842
+ RecoverableSignature,
843
+ &RecoverableSignature_DataType,
844
+ recoverable_signature
845
+ );
238
846
 
239
- if (RSTRING_LEN(in_bytes) != 32)
847
+ if (secp256k1_ecdsa_recoverable_signature_serialize_compact(
848
+ recoverable_signature->ctx,
849
+ compact_sig,
850
+ &recovery_id,
851
+ &(recoverable_signature->sig)) != 1)
240
852
  {
241
- rb_raise(rb_eArgError, "private key data must be 32 bytes in length");
242
- return self;
853
+ rb_raise(rb_eRuntimeError, "unable to serialize recoverable signature");
243
854
  }
244
855
 
245
- Data_Get_Struct(self, PrivateKey, private_key);
246
- memcpy(private_key->data, RSTRING_PTR(in_bytes), 32);
856
+ // Create a new array with room for 2 elements and push data onto it
857
+ result = rb_ary_new2(2);
858
+ rb_ary_push(result, rb_str_new((char*)compact_sig, 64));
859
+ rb_ary_push(result, rb_int_new(recovery_id));
247
860
 
248
- // Set the PrivateKey.data attribute for later reading
249
- rb_iv_set(self, "@data", in_bytes);
250
-
251
- return self;
861
+ return result;
252
862
  }
253
863
 
254
- //
255
- // Secp256k1::Signature class interface
256
- //
257
-
258
- /* Allocate memory for Signature object */
864
+ /**
865
+ * Convert a recoverable signature to a non-recoverable signature.
866
+ *
867
+ * @return [Secp256k1::Signature] non-recoverable signature derived from this
868
+ * recoverable signature.
869
+ */
259
870
  static VALUE
260
- Signature_alloc(VALUE klass)
871
+ RecoverableSignature_to_signature(VALUE self)
261
872
  {
262
- VALUE new_instance;
873
+ RecoverableSignature *recoverable_signature;
263
874
  Signature *signature;
875
+ VALUE result;
264
876
 
265
- new_instance = Data_Make_Struct(klass,
266
- Signature,
267
- NULL,
268
- free,
269
- signature);
270
- memset(signature, 0, sizeof(Signature));
877
+ TypedData_Get_Struct(
878
+ self,
879
+ RecoverableSignature,
880
+ &RecoverableSignature_DataType,
881
+ recoverable_signature
882
+ );
271
883
 
272
- return new_instance;
884
+ result = Signature_alloc(Secp256k1_Signature_class);
885
+ TypedData_Get_Struct(
886
+ result,
887
+ Signature,
888
+ &Signature_DataType,
889
+ signature
890
+ );
891
+
892
+ // NOTE: This method cannot fail
893
+ secp256k1_ecdsa_recoverable_signature_convert(
894
+ recoverable_signature->ctx,
895
+ &(signature->sig),
896
+ &(recoverable_signature->sig));
897
+
898
+ signature->ctx = secp256k1_context_clone(recoverable_signature->ctx);
899
+ return result;
273
900
  }
274
901
 
275
902
  /**
276
- * Signature#der_encoded
903
+ * Attempts to recover the public key associated with this signature.
277
904
  *
278
- * \param self
279
- * \return DER encoded version of this signature
905
+ * @param in_hash32 [String] 32-byte SHA-256 hash of data.
906
+ * @return [Secp256k1::PublicKey] recovered public key.
907
+ * @raise [RuntimeError] if the public key could not be recovered.
280
908
  */
281
909
  static VALUE
282
- Signature_der_encoded(VALUE self)
910
+ RecoverableSignature_recover_public_key(VALUE self, VALUE in_hash32)
283
911
  {
284
- Signature *signature;
285
- unsigned long der_signature_len;
286
- unsigned char der_signature[512];
912
+ RecoverableSignature *recoverable_signature;
913
+ PublicKey *public_key;
914
+ VALUE result;
915
+ unsigned char *hash32;
287
916
 
288
- Data_Get_Struct(self, Signature, signature);
917
+ Check_Type(in_hash32, T_STRING);
918
+ if (RSTRING_LEN(in_hash32) != 32)
919
+ {
920
+ rb_raise(rb_eArgError, "in_hash32 is not 32 bytes in length");
921
+ }
289
922
 
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)
923
+ TypedData_Get_Struct(
924
+ self,
925
+ RecoverableSignature,
926
+ &RecoverableSignature_DataType,
927
+ recoverable_signature
928
+ );
929
+ hash32 = (unsigned char*)StringValuePtr(in_hash32);
930
+
931
+ result = PublicKey_alloc(Secp256k1_PublicKey_class);
932
+ TypedData_Get_Struct(result, PublicKey, &PublicKey_DataType, public_key);
933
+
934
+ if (secp256k1_ecdsa_recover(recoverable_signature->ctx,
935
+ &(public_key->pubkey),
936
+ &(recoverable_signature->sig),
937
+ hash32) == 1)
295
938
  {
296
- return rb_str_new((char*)der_signature, der_signature_len);
939
+ public_key->ctx = secp256k1_context_clone(recoverable_signature->ctx);
940
+ return result;
297
941
  }
298
942
 
299
- rb_raise(rb_eRuntimeError, "Could not compute DER encoded signature");
943
+ rb_raise(rb_eRuntimeError, "unable to recover public key");
300
944
  }
301
945
 
302
946
  /**
303
- * Signature#compact
947
+ * Compares two recoverable signatures.
304
948
  *
305
- * Returns the compact (64-byte) representation of this signature.
949
+ * Two recoverable signatures their secp256k1_ecdsa_recoverable_signature data
950
+ * is identical.
306
951
  *
307
- * \param self
308
- * \return Compact encoding of this signature
952
+ * @param other [Secp256k1::RecoverableSignature] recoverable signature to
953
+ * compare.
954
+ * @return [Boolean] true if the recoverable signatures are identical, false
955
+ * otherwise.
309
956
  */
310
957
  static VALUE
311
- Signature_compact(VALUE self)
958
+ RecoverableSignature_equals(VALUE self, VALUE other)
312
959
  {
313
- Signature *signature;
314
- unsigned char compact_signature[65];
960
+ RecoverableSignature *lhs;
961
+ RecoverableSignature *rhs;
315
962
 
316
- Data_Get_Struct(self, Signature, signature);
963
+ TypedData_Get_Struct(
964
+ self, RecoverableSignature, &RecoverableSignature_DataType, lhs
965
+ );
966
+ TypedData_Get_Struct(
967
+ other, RecoverableSignature, &RecoverableSignature_DataType, rhs
968
+ );
317
969
 
318
- if (secp256k1_ecdsa_signature_serialize_compact(signature->context->ctx,
319
- compact_signature,
320
- &(signature->sig)) == 1)
970
+ // NOTE: It is safe to directly compare these data structures rather than
971
+ // first serializing and then comparing.
972
+ if (memcmp(&(lhs->sig),
973
+ &(rhs->sig),
974
+ sizeof(secp256k1_ecdsa_recoverable_signature)) == 0)
321
975
  {
322
- return rb_str_new((char*)compact_signature, 65);
976
+ return Qtrue;
323
977
  }
324
978
 
325
- rb_raise(rb_eRuntimeError, "Unable to compute compact signature");
979
+ return Qfalse;
326
980
  }
327
981
 
982
+ #endif // HAVE_SECP256K1_RECOVERY_H
983
+
328
984
  //
329
- // Secp256k1::Context class interface
985
+ // Secp256k1::SharedSecret class interface
330
986
  //
331
987
 
332
- /* Deallocate a context when it is garbage collected */
333
- static void
334
- Context_free(void* in_context)
988
+ #ifdef HAVE_SECP256K1_ECDH_H
989
+
990
+ static VALUE
991
+ SharedSecret_alloc(VALUE klass)
335
992
  {
336
- Context *context = (Context*)in_context;
993
+ VALUE new_instance;
994
+ SharedSecret *shared_secret;
337
995
 
338
- secp256k1_context_destroy(context->ctx);
339
- free(context);
996
+ shared_secret = ALLOC(SharedSecret);
997
+ MEMZERO(shared_secret, SharedSecret, 1);
998
+ new_instance = TypedData_Wrap_Struct(
999
+ klass, &SharedSecret_DataType, shared_secret
1000
+ );
1001
+
1002
+ return new_instance;
340
1003
  }
341
1004
 
1005
+ #endif // HAVE_SECP256K1_ECDH_H
1006
+
1007
+ //
1008
+ // Secp256k1::Context class interface
1009
+ //
1010
+
342
1011
  /* Allocate a new context object */
343
1012
  static VALUE
344
1013
  Context_alloc(VALUE klass)
@@ -346,20 +1015,21 @@ Context_alloc(VALUE klass)
346
1015
  VALUE new_instance;
347
1016
  Context *context;
348
1017
 
349
- new_instance = Data_Make_Struct(
350
- klass, Context, NULL, Context_free, context
351
- );
352
- context->ctx = NULL;
1018
+ context = ALLOC(Context);
1019
+ MEMZERO(context, Context, 1);
1020
+
1021
+ new_instance = TypedData_Wrap_Struct(klass, &Context_DataType, context);
353
1022
 
354
1023
  return new_instance;
355
1024
  }
356
1025
 
357
1026
  /**
358
- * Context#initialize
1027
+ * Initialize a new context.
359
1028
  *
360
- * Initialize a new libsecp256k1 context.
1029
+ * Context initialization should be infrequent as it is an expensive operation.
361
1030
  *
362
- * \raises RuntimeError if context randomizatino fails.
1031
+ * @return [Secp256k1::Context]
1032
+ * @raise [RuntimeError] if context randomization fails.
363
1033
  */
364
1034
  static VALUE
365
1035
  Context_initialize(VALUE self)
@@ -367,7 +1037,7 @@ Context_initialize(VALUE self)
367
1037
  Context *context;
368
1038
  unsigned char seed[32];
369
1039
 
370
- Data_Get_Struct(self, Context, context);
1040
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
371
1041
 
372
1042
  context->ctx = secp256k1_context_create(
373
1043
  SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY
@@ -378,86 +1048,104 @@ Context_initialize(VALUE self)
378
1048
  GenerateRandomBytes(seed, 32);
379
1049
  if (secp256k1_context_randomize(context->ctx, seed) != 1)
380
1050
  {
381
- rb_raise(rb_eRuntimeError, "Randomization of context failed.");
1051
+ rb_raise(rb_eRuntimeError, "context randomization failed");
382
1052
  }
383
1053
 
384
1054
  return self;
385
1055
  }
386
1056
 
387
1057
  /**
388
- * Context#generate_key_pair
1058
+ * Generate a new public-private key pair.
389
1059
  *
390
- * Generate a new (public, private) key pair.
1060
+ * @return [Secp256k1::KeyPair] newly generated key pair.
1061
+ * @raise [RuntimeError] if private key generation fails.
391
1062
  */
392
1063
  static VALUE
393
1064
  Context_generate_key_pair(VALUE self)
394
1065
  {
1066
+ Context *context;
395
1067
  VALUE private_key;
396
1068
  VALUE public_key;
397
- VALUE key_pair;
1069
+ VALUE result;
1070
+ unsigned char private_key_bytes[32];
1071
+
1072
+ if (FAILURE(GenerateRandomBytes(private_key_bytes, 32)))
1073
+ {
1074
+ rb_raise(rb_eRuntimeError, "unable to generate private key bytes.");
1075
+ }
1076
+
1077
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
398
1078
 
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);
1079
+ private_key = PrivateKey_create(context, private_key_bytes);
1080
+ public_key = PublicKey_create_from_private_key(context, private_key_bytes);
1081
+ result = rb_funcall(
1082
+ Secp256k1_KeyPair_class,
1083
+ rb_intern("new"),
1084
+ 2,
1085
+ public_key,
1086
+ private_key
1087
+ );
410
1088
 
411
- return key_pair;
1089
+ return result;
412
1090
  }
413
1091
 
414
1092
  /**
415
- * Context#public_key_from_data
416
- *
417
1093
  * Loads a public key from compressed or uncompressed binary data.
418
1094
  *
419
- * \param self
420
- * \param in_public_key_data Compressed or uncompressed binary public key data.
1095
+ * @param in_public_key_data [String] binary string with compressed or
1096
+ * uncompressed public key data.
1097
+ * @return [Secp256k1::PublicKey] public key derived from data.
1098
+ * @raise [RuntimeError] if public key data is invalid.
421
1099
  */
422
1100
  static VALUE
423
1101
  Context_public_key_from_data(VALUE self, VALUE in_public_key_data)
424
1102
  {
425
1103
  Context *context;
426
- PublicKey *public_key;
427
1104
  unsigned char *public_key_data;
428
- VALUE result;
429
1105
 
430
1106
  Check_Type(in_public_key_data, T_STRING);
431
1107
 
432
- Data_Get_Struct(self, Context, context);
1108
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
433
1109
  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)
1110
+ return PublicKey_create_from_data(
1111
+ context,
1112
+ public_key_data,
1113
+ RSTRING_LEN(in_public_key_data)
1114
+ );
1115
+ }
1116
+
1117
+ /**
1118
+ * Load a private key from binary data.
1119
+ *
1120
+ * @param in_private_key_data [String] 32 byte binary string of private key
1121
+ * data.
1122
+ * @return [Secp256k1::PrivateKey] private key loaded from the given data.
1123
+ * @raise [ArgumentError] if private key data is not 32 bytes or is invalid.
1124
+ */
1125
+ static VALUE
1126
+ Context_private_key_from_data(VALUE self, VALUE in_private_key_data)
1127
+ {
1128
+ Context *context;
1129
+ unsigned char *private_key_data;
1130
+
1131
+ Check_Type(in_private_key_data, T_STRING);
1132
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
1133
+ private_key_data = (unsigned char*)StringValuePtr(in_private_key_data);
1134
+
1135
+ if (RSTRING_LEN(in_private_key_data) != 32)
445
1136
  {
446
- rb_raise(rb_eRuntimeError, "Invalid public key data");
1137
+ rb_raise(rb_eArgError, "private key data must be 32 bytes in length");
447
1138
  }
448
1139
 
449
- return result;
1140
+ return PrivateKey_create(context, private_key_data);
450
1141
  }
451
1142
 
452
1143
  /**
453
- * Context#key_pair_from_private_key
454
- *
455
- * Converts a binary private key into a key pair
1144
+ * Converts binary private key data into a new key pair.
456
1145
  *
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
1146
+ * @param in_private_key_data [String] binary private key data
1147
+ * @return [Secp256k1::KeyPair] key pair initialized from the private key data.
1148
+ * @raise [ArgumentError] if the private key data is invalid or key derivation
461
1149
  * fails.
462
1150
  */
463
1151
  static VALUE
@@ -466,44 +1154,36 @@ Context_key_pair_from_private_key(VALUE self, VALUE in_private_key_data)
466
1154
  Context *context;
467
1155
  VALUE public_key;
468
1156
  VALUE private_key;
469
- VALUE key_pair;
470
1157
  unsigned char *private_key_data;
471
1158
 
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)
1159
+ if (RSTRING_LEN(in_private_key_data) != 32)
478
1160
  {
479
- rb_raise(rb_eRuntimeError, "Invalid private key data.");
1161
+ rb_raise(rb_eArgError, "private key data must be 32 bytes in length");
480
1162
  }
481
1163
 
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);
1164
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
1165
+ private_key_data = (unsigned char*)StringValuePtr(in_private_key_data);
1166
+
1167
+ private_key = PrivateKey_create(context, private_key_data);
1168
+ public_key = PublicKey_create_from_private_key(context, private_key_data);
496
1169
 
497
- return key_pair;
1170
+ return rb_funcall(
1171
+ Secp256k1_KeyPair_class,
1172
+ rb_intern("new"),
1173
+ 2,
1174
+ public_key,
1175
+ private_key
1176
+ );
498
1177
  }
499
1178
 
500
1179
  /**
501
- * Context#signature_from_der_encoded
1180
+ * Converts a DER encoded binary signature into a signature object.
502
1181
  *
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
1182
+ * @param in_der_encoded_signature [String] DER encoded signature as binary
1183
+ * string.
1184
+ * @return [Secp256k1::Signature] signature object initialized using signature
1185
+ * data.
1186
+ * @raise [ArgumentError] if signature data is invalid.
507
1187
  */
508
1188
  static VALUE
509
1189
  Context_signature_from_der_encoded(VALUE self, VALUE in_der_encoded_signature)
@@ -515,36 +1195,31 @@ Context_signature_from_der_encoded(VALUE self, VALUE in_der_encoded_signature)
515
1195
 
516
1196
  Check_Type(in_der_encoded_signature, T_STRING);
517
1197
 
518
- Data_Get_Struct(self, Context, context);
1198
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
519
1199
  signature_data = (unsigned char*)StringValuePtr(in_der_encoded_signature);
520
1200
 
521
- signature_result = Data_Make_Struct(Secp256k1_Signature_class,
522
- Signature,
523
- NULL,
524
- free,
525
- signature);
1201
+ signature_result = Signature_alloc(Secp256k1_Signature_class);
1202
+ TypedData_Get_Struct(signature_result, Signature, &Signature_DataType, signature);
526
1203
 
527
1204
  if (secp256k1_ecdsa_signature_parse_der(context->ctx,
528
1205
  &(signature->sig),
529
1206
  signature_data,
530
1207
  RSTRING_LEN(in_der_encoded_signature)) != 1)
531
1208
  {
532
- rb_raise(rb_eRuntimeError, "Invalid DER encoded signature.");
1209
+ rb_raise(rb_eArgError, "invalid DER encoded signature");
533
1210
  }
534
1211
 
535
- signature->context = context;
1212
+ signature->ctx = secp256k1_context_clone(context->ctx);
536
1213
  return signature_result;
537
1214
  }
538
1215
 
539
1216
  /**
540
- * Context#signature_from_compact
541
- *
542
1217
  * Deserializes a Signature from 64-byte compact signature data.
543
1218
  *
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
1219
+ * @param in_compact_signature [String] compact signature as 64-byte binary
1220
+ * string.
1221
+ * @return [Secp256k1::Signature] object deserialized from compact signature.
1222
+ * @raise [ArgumentError] if signature data is invalid.
548
1223
  */
549
1224
  static VALUE
550
1225
  Context_signature_from_compact(VALUE self, VALUE in_compact_signature)
@@ -554,105 +1229,104 @@ Context_signature_from_compact(VALUE self, VALUE in_compact_signature)
554
1229
  VALUE signature_result;
555
1230
  unsigned char *signature_data;
556
1231
 
557
- Check_Type(in_compact_signature, T_STRING);
558
-
559
- Data_Get_Struct(self, Context, context);
1232
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
560
1233
  signature_data = (unsigned char*)StringValuePtr(in_compact_signature);
561
1234
 
562
- signature_result = Data_Make_Struct(Secp256k1_Signature_class,
563
- Signature,
564
- NULL,
565
- free,
566
- signature);
1235
+ signature_result = Signature_alloc(Secp256k1_Signature_class);
1236
+ TypedData_Get_Struct(signature_result, Signature, &Signature_DataType, signature);
567
1237
 
568
1238
  if (secp256k1_ecdsa_signature_parse_compact(context->ctx,
569
1239
  &(signature->sig),
570
1240
  signature_data) != 1)
571
1241
  {
572
- rb_raise(rb_eRuntimeError, "Invalid compact signature.");
1242
+ rb_raise(rb_eArgError, "invalid compact signature");
573
1243
  }
574
1244
 
575
- signature->context = context;
1245
+ signature->ctx = secp256k1_context_clone(context->ctx);
576
1246
  return signature_result;
577
1247
  }
578
1248
 
579
1249
  /**
580
- * Context#sign
581
- *
582
- * Computes the ECDSA signature of the data using the secp256k1 EC.
1250
+ * Computes the ECDSA signature of the data using the secp256k1 elliptic curve.
583
1251
  *
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
1252
+ * @param in_private_key [Secp256k1::PrivateKey] private key to use for
1253
+ * signing.
1254
+ * @param in_hash32 [String] 32-byte binary string with SHA-256 hash of data.
1255
+ * @return [Secp256k1::Signature] signature resulting from signing data.
1256
+ * @raise [RuntimeError] if signature computation fails.
1257
+ * @raise [ArgumentError] if hash is not 32-bytes in length.
588
1258
  */
589
1259
  static VALUE
590
- Context_sign(VALUE self, VALUE in_private_key, VALUE in_data)
1260
+ Context_sign(VALUE self, VALUE in_private_key, VALUE in_hash32)
591
1261
  {
592
- unsigned char *data_ptr;
1262
+ unsigned char *hash32;
593
1263
  PrivateKey *private_key;
594
1264
  Context *context;
595
1265
  Signature *signature;
596
1266
  VALUE signature_result;
597
1267
 
598
- Check_Type(in_data, T_STRING);
1268
+ Check_Type(in_hash32, T_STRING);
599
1269
 
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);
1270
+ if (RSTRING_LEN(in_hash32) != 32)
1271
+ {
1272
+ rb_raise(rb_eArgError, "in_hash32 is not 32 bytes in length");
1273
+ }
603
1274
 
604
- signature_result = Data_Make_Struct(Secp256k1_Signature_class,
605
- Signature,
606
- NULL,
607
- free,
608
- signature);
1275
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
1276
+ TypedData_Get_Struct(in_private_key, PrivateKey, &PrivateKey_DataType, private_key);
1277
+ hash32 = (unsigned char*)StringValuePtr(in_hash32);
609
1278
 
1279
+ signature_result = Signature_alloc(Secp256k1_Signature_class);
1280
+ TypedData_Get_Struct(signature_result, Signature, &Signature_DataType, signature);
1281
+
610
1282
  // Attempt to sign the hash of the given data
611
1283
  if (SUCCESS(SignData(context->ctx,
612
- data_ptr,
613
- RSTRING_LEN(in_data),
1284
+ hash32,
614
1285
  private_key->data,
615
1286
  &(signature->sig))))
616
1287
  {
617
- signature->context = context;
1288
+ signature->ctx = secp256k1_context_clone(context->ctx);
618
1289
  return signature_result;
619
1290
  }
620
1291
 
621
- rb_raise(rb_eRuntimeError, "Unable to compute signature");
1292
+ rb_raise(rb_eRuntimeError, "unable to compute signature");
622
1293
  }
623
1294
 
624
1295
  /**
625
- * Context#verify
1296
+ * Verifies that signature matches public key and data.
626
1297
  *
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.
1298
+ * @param in_signature [Secp256k1::Signature] signature to be verified.
1299
+ * @param in_pubkey [Secp256k1::PublicKey] public key to verify signature
1300
+ * against.
1301
+ * @param in_hash32 [String] 32-byte binary string containing SHA-256 hash of
1302
+ * data.
1303
+ * @return [Boolean] True if the signature is valid, false otherwise.
1304
+ * @raise [ArgumentError] if hash is not 32-bytes in length.
634
1305
  */
635
1306
  static VALUE
636
- Context_verify(VALUE self, VALUE in_signature, VALUE in_pubkey, VALUE in_message)
1307
+ Context_verify(VALUE self, VALUE in_signature, VALUE in_pubkey, VALUE in_hash32)
637
1308
  {
638
1309
  Context *context;
639
1310
  PublicKey *public_key;
640
1311
  Signature *signature;
641
- unsigned char *message_ptr;
642
- unsigned char hash[SHA256_DIGEST_LENGTH];
1312
+ unsigned char *hash32;
1313
+
1314
+ Check_Type(in_hash32, T_STRING);
643
1315
 
644
- Check_Type(in_message, T_STRING);
1316
+ if (RSTRING_LEN(in_hash32) != 32)
1317
+ {
1318
+ rb_raise(rb_eArgError, "in_hash32 is not 32-bytes in length");
1319
+ }
645
1320
 
646
- Data_Get_Struct(self, Context, context);
647
- Data_Get_Struct(in_pubkey, PublicKey, public_key);
648
- Data_Get_Struct(in_signature, Signature, signature);
1321
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
1322
+ TypedData_Get_Struct(in_pubkey, PublicKey, &PublicKey_DataType, public_key);
1323
+ TypedData_Get_Struct(in_signature, Signature, &Signature_DataType, signature);
649
1324
 
650
- message_ptr = (unsigned char*)StringValuePtr(in_message);
651
- SHA256(message_ptr, RSTRING_LEN(in_message), hash);
1325
+ hash32 = (unsigned char*)StringValuePtr(in_hash32);
652
1326
 
653
1327
  if (secp256k1_ecdsa_verify(context->ctx,
654
1328
  &(signature->sig),
655
- hash,
1329
+ hash32,
656
1330
  &(public_key->pubkey)) == 1)
657
1331
  {
658
1332
  return Qtrue;
@@ -661,127 +1335,196 @@ Context_verify(VALUE self, VALUE in_signature, VALUE in_pubkey, VALUE in_message
661
1335
  return Qfalse;
662
1336
  }
663
1337
 
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
- }
1338
+ // Context recoverable signature methods
1339
+ #ifdef HAVE_SECP256K1_RECOVERY_H
678
1340
 
679
1341
  /**
680
- * PublicKey#initialize
681
- *
682
- * Initialize a new public key from the given context and private key.
1342
+ * Computes the recoverable ECDSA signature of data signed with private key.
683
1343
  *
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
1344
+ * @param in_private_key [Secp256k1::PrivateKey] private key to sign with.
1345
+ * @param in_hash32 [String] 32-byte binary string with SHA-256 hash of data.
1346
+ * @return [Secp256k1::RecoverableSignature] recoverable signature produced by
1347
+ * signing the SHA-256 hash `in_hash32` with `in_private_key`.
1348
+ * @raise [ArgumentError] if the hash is not 32 bytes
688
1349
  */
689
1350
  static VALUE
690
- PublicKey_initialize(VALUE self, VALUE in_context, VALUE in_private_key)
1351
+ Context_sign_recoverable(VALUE self, VALUE in_private_key, VALUE in_hash32)
691
1352
  {
692
1353
  Context *context;
693
- PublicKey *public_key;
694
1354
  PrivateKey *private_key;
1355
+ RecoverableSignature *recoverable_signature;
1356
+ unsigned char *hash32;
1357
+ VALUE result;
695
1358
 
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)
1359
+ Check_Type(in_hash32, T_STRING);
1360
+ if (RSTRING_LEN(in_hash32) != 32)
703
1361
  {
704
- rb_raise(rb_eTypeError, "Invalid private key data");
705
- return self;
1362
+ rb_raise(rb_eArgError, "in_hash32 is not 32 bytes in length");
706
1363
  }
707
1364
 
708
- public_key->context = context;
1365
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
1366
+ TypedData_Get_Struct(
1367
+ in_private_key, PrivateKey, &PrivateKey_DataType, private_key
1368
+ );
1369
+ hash32 = (unsigned char*)StringValuePtr(in_hash32);
1370
+
1371
+ result = RecoverableSignature_alloc(Secp256k1_RecoverableSignature_class);
1372
+ TypedData_Get_Struct(
1373
+ result,
1374
+ RecoverableSignature,
1375
+ &RecoverableSignature_DataType,
1376
+ recoverable_signature
1377
+ );
1378
+
1379
+ if (SUCCESS(RecoverableSignData(context->ctx,
1380
+ hash32,
1381
+ private_key->data,
1382
+ &(recoverable_signature->sig))))
1383
+ {
1384
+ recoverable_signature->ctx = secp256k1_context_clone(context->ctx);
1385
+ return result;
1386
+ }
709
1387
 
710
- return self;
1388
+ rb_raise(rb_eRuntimeError, "unable to compute recoverable signature");
711
1389
  }
712
1390
 
713
- /* PublicKey#as_uncompressed */
1391
+ /**
1392
+ * Loads recoverable signature from compact representation and recovery ID.
1393
+ *
1394
+ * @param in_compact_sig [String] binary string containing compact signature
1395
+ * data.
1396
+ * @param in_recovery_id [Integer] recovery ID.
1397
+ * @return [Secp256k1::RecoverableSignature] signature parsed from data.
1398
+ * @raise [RuntimeError] if signature data or recovery ID is invalid.
1399
+ * @raise [ArgumentError] if compact signature is not 64 bytes.
1400
+ */
714
1401
  static VALUE
715
- PublicKey_as_uncompressed(VALUE self)
1402
+ Context_recoverable_signature_from_compact(
1403
+ VALUE self, VALUE in_compact_sig, VALUE in_recovery_id)
716
1404
  {
717
- PublicKey *public_key;
718
- size_t serialized_pubkey_len = 65;
719
- unsigned char serialized_pubkey[65];
1405
+ Context *context;
1406
+ RecoverableSignature *recoverable_signature;
1407
+ unsigned char *compact_sig;
1408
+ int recovery_id;
1409
+ VALUE result;
1410
+
1411
+ Check_Type(in_compact_sig, T_STRING);
1412
+ Check_Type(in_recovery_id, T_FIXNUM);
1413
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
720
1414
 
721
- Data_Get_Struct(self, PublicKey, public_key);
1415
+ compact_sig = (unsigned char*)StringValuePtr(in_compact_sig);
1416
+ recovery_id = FIX2INT(in_recovery_id);
722
1417
 
723
- if (public_key->context == NULL || public_key->context->ctx == NULL)
1418
+ if (RSTRING_LEN(in_compact_sig) != 64)
724
1419
  {
725
- rb_raise(rb_eRuntimeError, "Public key context is NULL");
1420
+ rb_raise(rb_eArgError, "compact signature is not 64 bytes");
726
1421
  }
727
1422
 
728
- secp256k1_ec_pubkey_serialize(public_key->context->ctx,
729
- serialized_pubkey,
730
- &serialized_pubkey_len,
731
- &(public_key->pubkey),
732
- SECP256K1_EC_UNCOMPRESSED);
1423
+ result = RecoverableSignature_alloc(Secp256k1_RecoverableSignature_class);
1424
+ TypedData_Get_Struct(
1425
+ result,
1426
+ RecoverableSignature,
1427
+ &RecoverableSignature_DataType,
1428
+ recoverable_signature
1429
+ );
733
1430
 
734
- return rb_str_new((char*)serialized_pubkey, serialized_pubkey_len);
1431
+ if (secp256k1_ecdsa_recoverable_signature_parse_compact(
1432
+ context->ctx,
1433
+ &(recoverable_signature->sig),
1434
+ compact_sig,
1435
+ recovery_id) == 1)
1436
+ {
1437
+ recoverable_signature->ctx = secp256k1_context_clone(context->ctx);
1438
+ return result;
1439
+ }
1440
+
1441
+ rb_raise(rb_eRuntimeError, "unable to parse recoverable signature");
735
1442
  }
736
1443
 
737
- /* PublicKey#as_compressed */
1444
+ #endif // HAVE_SECP256K1_RECOVERY_H
1445
+
1446
+ // Context EC Diffie-Hellman methods
1447
+ #ifdef HAVE_SECP256K1_ECDH_H
1448
+
1449
+ /**
1450
+ * Compute EC Diffie-Hellman secret in constant time.
1451
+ *
1452
+ * Creates a new shared secret from public_key and private_key.
1453
+ *
1454
+ * @param point [Secp256k1::PublicKey] public-key representing ECDH point.
1455
+ * @param scalar [Secp256k1::PrivateKey] private-key representing ECDH scalar.
1456
+ * @return [Secp256k1::SharedSecret] shared secret
1457
+ * @raise [RuntimeError] If scalar was invalid (zero or caused overflow).
1458
+ */
738
1459
  static VALUE
739
- PublicKey_as_compressed(VALUE self)
1460
+ Context_ecdh(VALUE self, VALUE point, VALUE scalar)
740
1461
  {
1462
+ Context *context;
741
1463
  PublicKey *public_key;
742
- size_t serialized_pubkey_len = 65;
743
- unsigned char serialized_pubkey[65];
1464
+ PrivateKey *private_key;
1465
+ SharedSecret *shared_secret;
1466
+ VALUE result;
744
1467
 
745
- Data_Get_Struct(self, PublicKey, public_key);
1468
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
1469
+ TypedData_Get_Struct(point, PublicKey, &PublicKey_DataType, public_key);
1470
+ TypedData_Get_Struct(scalar, PrivateKey, &PrivateKey_DataType, private_key);
746
1471
 
747
- secp256k1_ec_pubkey_serialize(public_key->context->ctx,
748
- serialized_pubkey,
749
- &serialized_pubkey_len,
750
- &(public_key->pubkey),
751
- SECP256K1_EC_COMPRESSED);
1472
+ result = SharedSecret_alloc(Secp256k1_SharedSecret_class);
1473
+ TypedData_Get_Struct(
1474
+ result, SharedSecret, &SharedSecret_DataType, shared_secret
1475
+ );
752
1476
 
753
- return rb_str_new((char*)serialized_pubkey, serialized_pubkey_len);
1477
+ if (secp256k1_ecdh(context->ctx,
1478
+ shared_secret->data,
1479
+ &(public_key->pubkey),
1480
+ (unsigned char*)private_key->data,
1481
+ NULL,
1482
+ NULL) != 1)
1483
+ {
1484
+ rb_raise(rb_eRuntimeError, "invalid scalar provided to ecdh");
1485
+ }
1486
+
1487
+ rb_iv_set(result, "@data", rb_str_new((char*)shared_secret->data, 32));
1488
+
1489
+ return result;
754
1490
  }
755
1491
 
1492
+ #endif // HAVE_SECP256K1_ECDH_H
756
1493
 
757
1494
  //
758
- // Secp256k1::KeyPair class interface
1495
+ // Secp256k1 module methods
759
1496
  //
760
1497
 
1498
+ /**
1499
+ * Indicates whether or not the libsecp256k1 recovery module is installed.
1500
+ *
1501
+ * @return [Boolean] true if libsecp256k1 was built with the recovery module,
1502
+ * false otherwise.
1503
+ */
761
1504
  static VALUE
762
- KeyPair_alloc(VALUE klass)
1505
+ Secp256k1_have_recovery(VALUE module)
763
1506
  {
764
- VALUE result;
765
- KeyPair *key_pair;
766
- result = Data_Make_Struct(klass, KeyPair, NULL, free, key_pair);
767
-
768
- return result;
1507
+ #ifdef HAVE_SECP256K1_RECOVERY_H
1508
+ return Qtrue;
1509
+ #else // HAVE_SECP256K1_RECOVERY_H
1510
+ return Qfalse;
1511
+ #endif // HAVE_SECP256K1_RECOVERY_H
769
1512
  }
770
1513
 
1514
+ /**
1515
+ * Indicates whether or not libsecp256k1 EC Diffie-Hellman module is installed.
1516
+ *
1517
+ * @return [Boolean] true if libsecp256k1 was build with the ECDH module, false
1518
+ * otherwise.
1519
+ */
771
1520
  static VALUE
772
- KeyPair_initialize(VALUE self, VALUE public_key, VALUE private_key)
1521
+ Secp256k1_have_ecdh(VALUE module)
773
1522
  {
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;
1523
+ #ifdef HAVE_SECP256K1_ECDH_H
1524
+ return Qtrue;
1525
+ #else // HAVE_SECP256K1_ECDH_H
1526
+ return Qfalse;
1527
+ #endif // HAVE_SECP256K1_ECDH_H
785
1528
  }
786
1529
 
787
1530
  //
@@ -790,16 +1533,31 @@ KeyPair_initialize(VALUE self, VALUE public_key, VALUE private_key)
790
1533
 
791
1534
  void Init_rbsecp256k1()
792
1535
  {
1536
+ // NOTE: All classes derive from Data (rb_cData) rather than Object
1537
+ // (rb_cObject). This makes it so we don't have to call rb_undef_alloc_func
1538
+ // for each class and can instead simply define the allocation methods for
1539
+ // each class.
1540
+ //
1541
+ // See: https://github.com/ruby/ruby/blob/trunk/doc/extension.rdoc#encapsulate-c-data-into-a-ruby-object
1542
+
793
1543
  // Secp256k1
794
1544
  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);
1545
+ rb_define_singleton_method(
1546
+ Secp256k1_module,
1547
+ "have_recovery?",
1548
+ Secp256k1_have_recovery,
1549
+ 0
1550
+ );
1551
+ rb_define_singleton_method(
1552
+ Secp256k1_module,
1553
+ "have_ecdh?",
1554
+ Secp256k1_have_ecdh,
1555
+ 0
1556
+ );
799
1557
 
800
1558
  // Secp256k1::Context
801
1559
  Secp256k1_Context_class = rb_define_class_under(
802
- Secp256k1_module, "Context", rb_cObject
1560
+ Secp256k1_module, "Context", rb_cData
803
1561
  );
804
1562
  rb_define_alloc_func(Secp256k1_Context_class, Context_alloc);
805
1563
  rb_define_method(Secp256k1_Context_class,
@@ -818,6 +1576,10 @@ void Init_rbsecp256k1()
818
1576
  "public_key_from_data",
819
1577
  Context_public_key_from_data,
820
1578
  1);
1579
+ rb_define_method(Secp256k1_Context_class,
1580
+ "private_key_from_data",
1581
+ Context_private_key_from_data,
1582
+ 1);
821
1583
  rb_define_method(Secp256k1_Context_class,
822
1584
  "sign",
823
1585
  Context_sign,
@@ -838,7 +1600,7 @@ void Init_rbsecp256k1()
838
1600
  // Secp256k1::KeyPair
839
1601
  Secp256k1_KeyPair_class = rb_define_class_under(Secp256k1_module,
840
1602
  "KeyPair",
841
- rb_cObject);
1603
+ rb_cData);
842
1604
  rb_define_alloc_func(Secp256k1_KeyPair_class, KeyPair_alloc);
843
1605
  rb_define_attr(Secp256k1_KeyPair_class, "public_key", 1, 0);
844
1606
  rb_define_attr(Secp256k1_KeyPair_class, "private_key", 1, 0);
@@ -846,44 +1608,35 @@ void Init_rbsecp256k1()
846
1608
  "initialize",
847
1609
  KeyPair_initialize,
848
1610
  2);
1611
+ rb_define_method(Secp256k1_KeyPair_class, "==", KeyPair_equals, 1);
849
1612
 
850
1613
  // Secp256k1::PublicKey
851
1614
  Secp256k1_PublicKey_class = rb_define_class_under(Secp256k1_module,
852
1615
  "PublicKey",
853
- rb_cObject);
1616
+ rb_cData);
854
1617
  rb_define_alloc_func(Secp256k1_PublicKey_class, PublicKey_alloc);
855
1618
  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,
1619
+ "compressed",
1620
+ PublicKey_compressed,
862
1621
  0);
863
1622
  rb_define_method(Secp256k1_PublicKey_class,
864
- "as_uncompressed",
865
- PublicKey_as_uncompressed,
1623
+ "uncompressed",
1624
+ PublicKey_uncompressed,
866
1625
  0);
1626
+ rb_define_method(Secp256k1_PublicKey_class, "==", PublicKey_equals, 1);
867
1627
 
868
1628
  // Secp256k1::PrivateKey
869
1629
  Secp256k1_PrivateKey_class = rb_define_class_under(
870
- Secp256k1_module, "PrivateKey", rb_cObject
1630
+ Secp256k1_module, "PrivateKey", rb_cData
871
1631
  );
872
1632
  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
1633
  rb_define_attr(Secp256k1_PrivateKey_class, "data", 1, 0);
878
- rb_define_method(Secp256k1_PrivateKey_class,
879
- "initialize",
880
- PrivateKey_initialize,
881
- 1);
1634
+ rb_define_method(Secp256k1_PrivateKey_class, "==", PrivateKey_equals, 1);
882
1635
 
883
1636
  // Secp256k1::Signature
884
1637
  Secp256k1_Signature_class = rb_define_class_under(Secp256k1_module,
885
1638
  "Signature",
886
- rb_cObject);
1639
+ rb_cData);
887
1640
  rb_define_alloc_func(Secp256k1_Signature_class, Signature_alloc);
888
1641
  rb_define_method(Secp256k1_Signature_class,
889
1642
  "der_encoded",
@@ -893,4 +1646,81 @@ void Init_rbsecp256k1()
893
1646
  "compact",
894
1647
  Signature_compact,
895
1648
  0);
1649
+ rb_define_method(Secp256k1_Signature_class,
1650
+ "normalized",
1651
+ Signature_normalized,
1652
+ 0);
1653
+ rb_define_method(Secp256k1_Signature_class,
1654
+ "==",
1655
+ Signature_equals,
1656
+ 1);
1657
+
1658
+ #ifdef HAVE_SECP256K1_RECOVERY_H
1659
+ // Secp256k1::RecoverableSignature
1660
+ Secp256k1_RecoverableSignature_class = rb_define_class_under(
1661
+ Secp256k1_module,
1662
+ "RecoverableSignature",
1663
+ rb_cData
1664
+ );
1665
+ rb_define_alloc_func(
1666
+ Secp256k1_RecoverableSignature_class,
1667
+ RecoverableSignature_alloc
1668
+ );
1669
+ rb_define_method(
1670
+ Secp256k1_RecoverableSignature_class,
1671
+ "compact",
1672
+ RecoverableSignature_compact,
1673
+ 0
1674
+ );
1675
+ rb_define_method(
1676
+ Secp256k1_RecoverableSignature_class,
1677
+ "to_signature",
1678
+ RecoverableSignature_to_signature,
1679
+ 0
1680
+ );
1681
+ rb_define_method(
1682
+ Secp256k1_RecoverableSignature_class,
1683
+ "recover_public_key",
1684
+ RecoverableSignature_recover_public_key,
1685
+ 1
1686
+ );
1687
+ rb_define_method(
1688
+ Secp256k1_RecoverableSignature_class,
1689
+ "==",
1690
+ RecoverableSignature_equals,
1691
+ 1
1692
+ );
1693
+
1694
+ // Context recoverable signature methods
1695
+ rb_define_method(
1696
+ Secp256k1_Context_class,
1697
+ "sign_recoverable",
1698
+ Context_sign_recoverable,
1699
+ 2
1700
+ );
1701
+ rb_define_method(
1702
+ Secp256k1_Context_class,
1703
+ "recoverable_signature_from_compact",
1704
+ Context_recoverable_signature_from_compact,
1705
+ 2
1706
+ );
1707
+ #endif // HAVE_SECP256K1_RECOVERY_H
1708
+
1709
+ #ifdef HAVE_SECP256K1_ECDH_H
1710
+ Secp256k1_SharedSecret_class = rb_define_class_under(
1711
+ Secp256k1_module,
1712
+ "SharedSecret",
1713
+ rb_cData
1714
+ );
1715
+ rb_define_alloc_func(Secp256k1_SharedSecret_class, SharedSecret_alloc);
1716
+ rb_define_attr(Secp256k1_SharedSecret_class, "data", 1, 0);
1717
+
1718
+ // Context EC Diffie-Hellman methods
1719
+ rb_define_method(
1720
+ Secp256k1_Context_class,
1721
+ "ecdh",
1722
+ Context_ecdh,
1723
+ 2
1724
+ );
1725
+ #endif // HAVE_SECP256K1_ECDH_H
896
1726
  }