rbsecp256k1 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }