rbsecp256k1 3.0.0 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ [Index](index.md)
2
+
3
+ Secp256k1::KeyPair
4
+ ==================
5
+
6
+ Secp256k1::KeyPair represents a public-private Secp256k1 key pair.
7
+
8
+ Initializers
9
+ ------------
10
+
11
+ #### new(public_key, private_key)
12
+
13
+ Initializes a new key pair with `public_key` (type: [PublicKey](public_key.md)) and `private_key` (type: [PrivateKey](private_key.md)).
14
+
15
+ Instance Methods
16
+ ----------------
17
+
18
+ #### public_key
19
+
20
+ Returns the [PublicKey](public_key.md) part of this key pair.
21
+
22
+ #### private_key
23
+
24
+ Returns the [PrivateKey](private_key.md) part of this key pair.
25
+
26
+ #### ==(other)
27
+
28
+ Returns `true` if the `other` has the same public and private key.
@@ -0,0 +1,25 @@
1
+ [Index](index.md)
2
+
3
+ Secp256k1::PrivateKey
4
+ =====================
5
+
6
+ Secp256k1::PrivateKey represents the private key part of a public-private key pair.
7
+
8
+ Class Methods
9
+ -------------
10
+
11
+ #### from_data(private_key_data)
12
+
13
+ Loads new private key from the given binary `private_key_data` string. Raises
14
+ `Secp256k1::Error` if the given data is invalid.
15
+
16
+ Instance Methods
17
+ ----------------
18
+
19
+ #### data
20
+
21
+ Returns the binary private key data as a `String`.
22
+
23
+ #### ==(other)
24
+
25
+ Returns `true` if this private key matches `other`.
@@ -0,0 +1,32 @@
1
+ [Index](index.md)
2
+
3
+ Secp256k1::PublicKey
4
+ ====================
5
+
6
+ Secp256k1::PublicKey represents the public key part of a public-private key pair.
7
+
8
+ See: [KeyPair](key_pair.md)
9
+
10
+ Class Methods
11
+ -------------
12
+
13
+ #### from_data(public_key_data)
14
+
15
+ Parses compressed or uncompressed from binary string `public_key_data` and
16
+ creates and returns a new public key from it. Raises a `Secp256k1::DeserializationError`
17
+ if the given public key data is invalid.
18
+
19
+ Instance Methods
20
+ ----------------
21
+
22
+ #### compressed
23
+
24
+ Returns the binary compressed representation of this public key.
25
+
26
+ #### uncompressed
27
+
28
+ Returns the binary uncompressed representation of this public key.
29
+
30
+ #### ==(other)
31
+
32
+ Return `true` if this public key matches `other`.
@@ -0,0 +1,30 @@
1
+ [Index](index.md)
2
+
3
+ Secp256k1::RecoverableSignature
4
+ ===============================
5
+
6
+ **Requires:** libsecp256k1 was build with recovery module.
7
+
8
+ Secp256k1::RecoverableSignature represents a recoverable ECDSA signature
9
+ signing the 32-byte SHA-256 hash of some data.
10
+
11
+ Instance Methods
12
+ ----------------
13
+
14
+ #### compact
15
+
16
+ Returns an array whose first element is the 64-byte compact signature as a
17
+ binary string and whose second element is the integer recovery ID.
18
+
19
+ #### recover_public_key
20
+
21
+ Recovers the public key corresponding to the recoverable signature. Returns a
22
+ [PublicKey](public_key.md).
23
+
24
+ #### to_signature
25
+
26
+ Converts a recoverable signature to a non-recoverable [Signature](signature.md) object.
27
+
28
+ #### ==(other)
29
+
30
+ Returns `true` if this recoverable signature matches `other`.
@@ -0,0 +1,19 @@
1
+ [Index](index.md)
2
+
3
+ Secp256k1
4
+ =========
5
+
6
+ Secp256k1 is the top-level module for this library.
7
+
8
+ Class Methods
9
+ -------------
10
+
11
+ #### have_recovery?
12
+
13
+ Returns `true` if the recovery module was built with libsecp256k1, `false`
14
+ otherwise.
15
+
16
+ #### have_ecdh?
17
+
18
+ Returns `true` if the EC Diffie-Hellman module was built with libsecp256k1,
19
+ `false` otherwise.
@@ -0,0 +1,16 @@
1
+ [Index](index.md)
2
+
3
+ Secp256k1::SharedSecret
4
+ =======================
5
+
6
+ **Requires:** libsecp256k1 was build with ECDH module.
7
+
8
+ Secp256k1::SharedSecret represents a 32-byte shared secret computed from a
9
+ public key (point) and private key (scalar).
10
+
11
+ Instance Methods
12
+ ----------------
13
+
14
+ #### data
15
+
16
+ Binary string containing the 32-byte shared secret.
@@ -0,0 +1,42 @@
1
+ [Index](index.md)
2
+
3
+ Secp256k1::Signature
4
+ ====================
5
+
6
+ Secp256k1::Signature represents an ECDSA signature signing the 32-byte SHA-256
7
+ hash of some data.
8
+
9
+ Class Methods
10
+ -------------
11
+
12
+ #### from_compact(compact_signature)
13
+
14
+ Parses a signature from binary string `compact_signature`. Raises a
15
+ `Secp256k1::DeserializationError` if the signature data is invalid.
16
+
17
+ #### from_der_encoded(der_encoded_signature)
18
+
19
+ Parses a signature from binary string `der_encoded_signature`. Raises a
20
+ `Secp256k1::DeserializationError` if the signature data is invalid.
21
+
22
+ Instance Methods
23
+ ----------------
24
+
25
+ #### der_encoded
26
+
27
+ Returns the DER encoded representation of this signature.
28
+
29
+ #### compact
30
+
31
+ Returns the compact 64-byte representation of this signature.
32
+
33
+ #### normalized
34
+
35
+ Returns an array containing two elements. The first is a Boolean indicating
36
+ whether or not the signature was normalized, false if it was already in lower-S
37
+ normal form. The second element is a `Signature` containing the normalized
38
+ signature object.
39
+
40
+ #### ==(other)
41
+
42
+ Returns `true` if this signature matches `other`.
@@ -0,0 +1,17 @@
1
+ [Index](index.md)
2
+
3
+ Secp256k1::Util
4
+ ===============
5
+
6
+ Secp256k1::Util in a module containing generally useful methods for using the library.
7
+
8
+ Class Methods
9
+ -------------
10
+
11
+ #### bin_to_hex(binary_data)
12
+
13
+ Returns the hexadecimal string representation of `binary_data`
14
+
15
+ #### hex_to_bin(hex_string)
16
+
17
+ Returns the binary string representation of `hex_string`
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'mini_portile2'
2
4
  require 'mkmf'
3
5
  require 'zip'
@@ -5,10 +7,10 @@ require 'zip'
5
7
  # Recipe for downloading and building libsecp256k1 as part of installation
6
8
  class Secp256k1Recipe < MiniPortile
7
9
  # Hard-coded URL for libsecp256k1 zipfile (HEAD of master as of 26-11-2018)
8
- LIBSECP256K1_ZIP_URL = 'https://github.com/bitcoin-core/secp256k1/archive/e34ceb333b1c0e6f4115ecbb80c632ac1042fa49.zip'.freeze
10
+ LIBSECP256K1_ZIP_URL = 'https://github.com/bitcoin-core/secp256k1/archive/e34ceb333b1c0e6f4115ecbb80c632ac1042fa49.zip'
9
11
 
10
12
  # Expected SHA-256 of the zipfile above (computed using sha256sum)
11
- LIBSECP256K1_SHA256 = 'd87d3ca7ebc42edbabb0f38e79205040b24b09b3e6d1c9ac89585de9bf302143'.freeze
13
+ LIBSECP256K1_SHA256 = 'd87d3ca7ebc42edbabb0f38e79205040b24b09b3e6d1c9ac89585de9bf302143'
12
14
 
13
15
  WITH_RECOVERY = ENV.fetch('WITH_RECOVERY', '1') == '1'
14
16
  WITH_ECDH = ENV.fetch('WITH_ECDH', '1') == '1'
@@ -36,6 +38,7 @@ class Secp256k1Recipe < MiniPortile
36
38
  # Windows doesn't recognize the shebang.
37
39
  execute('autogen', %w[sh ./autogen.sh])
38
40
  else
41
+ execute('chmod', %w[chmod +x ./autogen.sh])
39
42
  execute('autogen', %w[./autogen.sh])
40
43
  end
41
44
 
@@ -69,14 +72,9 @@ class Secp256k1Recipe < MiniPortile
69
72
  end
70
73
  end
71
74
 
72
- # OpenSSL flags
73
- print("checking for OpenSSL\n")
74
- results = pkg_config('openssl')
75
- abort "missing openssl pkg-config information" unless results && results[1]
76
-
77
75
  if with_config('system-library')
78
76
  # Require that libsecp256k1 be installed using `make install` or similar.
79
- print("checking for libsecp256k1\n")
77
+ message("checking for libsecp256k1\n")
80
78
  results = pkg_config('libsecp256k1')
81
79
  abort "missing libsecp256k1" unless results && results[1]
82
80
  else
@@ -94,6 +92,11 @@ else
94
92
  "-Wall"
95
93
  ]
96
94
  )
95
+ append_ldflags(
96
+ [
97
+ "-Wl,--no-as-needed"
98
+ ]
99
+ )
97
100
  # rubocop:disable Style/GlobalVars
98
101
  $LIBPATH = ["#{recipe.path}/lib"] | $LIBPATH
99
102
  # rubocop:enable Style/GlobalVars
@@ -6,12 +6,8 @@
6
6
  // and verifying signatures using the library.
7
7
  //
8
8
  // Dependencies:
9
- // * libsecp256k1
10
- // * openssl
9
+ // * libsecp256k1
11
10
  #include <ruby.h>
12
-
13
- #include <openssl/rand.h>
14
-
15
11
  #include <secp256k1.h>
16
12
 
17
13
  // Include recoverable signatures functionality if available
@@ -44,6 +40,14 @@
44
40
  // applications. Context initialization is expensive so it is recommended that
45
41
  // a single context be initialized and used throughout an application when
46
42
  // possible.
43
+ //
44
+ // Exception Hierarchy:
45
+ //
46
+ // The following hierarchy is used for exceptions raised from the library:
47
+ //
48
+ // +- Error (Descends from StandardError)
49
+ // |-- SerializationError
50
+ // |-- DeserializationError
47
51
 
48
52
  //
49
53
  // The section below contains purely internal methods used exclusively by the
@@ -61,6 +65,9 @@ const size_t COMPACT_SIG_SIZE_BYTES = 64;
61
65
  // objects from anywhere. The use of global variables seems to be inline with
62
66
  // how the Ruby project builds its own extension gems.
63
67
  static VALUE Secp256k1_module;
68
+ static VALUE Secp256k1_Error_class;
69
+ static VALUE Secp256k1_SerializationError_class;
70
+ static VALUE Secp256k1_DeserializationError_class;
64
71
  static VALUE Secp256k1_Context_class;
65
72
  static VALUE Secp256k1_KeyPair_class;
66
73
  static VALUE Secp256k1_PublicKey_class;
@@ -87,17 +94,14 @@ typedef struct KeyPair_dummy {
87
94
 
88
95
  typedef struct PublicKey_dummy {
89
96
  secp256k1_pubkey pubkey; // Opaque object containing public key data
90
- secp256k1_context *ctx;
91
97
  } PublicKey;
92
98
 
93
99
  typedef struct PrivateKey_dummy {
94
100
  unsigned char data[32]; // Bytes comprising the private key data
95
- secp256k1_context *ctx;
96
101
  } PrivateKey;
97
102
 
98
103
  typedef struct Signature_dummy {
99
104
  secp256k1_ecdsa_signature sig; // Signature object, contains 64-byte signature
100
- secp256k1_context *ctx;
101
105
  } Signature;
102
106
 
103
107
  #ifdef HAVE_SECP256K1_RECOVERY_H
@@ -140,7 +144,6 @@ PublicKey_free(void *in_public_key)
140
144
  {
141
145
  PublicKey *public_key;
142
146
  public_key = (PublicKey*)in_public_key;
143
- secp256k1_context_destroy(public_key->ctx);
144
147
  xfree(public_key);
145
148
  }
146
149
 
@@ -157,7 +160,6 @@ PrivateKey_free(void *in_private_key)
157
160
  {
158
161
  PrivateKey *private_key;
159
162
  private_key = (PrivateKey*)in_private_key;
160
- secp256k1_context_destroy(private_key->ctx);
161
163
  xfree(private_key);
162
164
  }
163
165
 
@@ -197,7 +199,6 @@ static void
197
199
  Signature_free(void *in_signature)
198
200
  {
199
201
  Signature *signature = (Signature*)in_signature;
200
- secp256k1_context_destroy(signature->ctx);
201
202
  xfree(signature);
202
203
  }
203
204
 
@@ -268,33 +269,6 @@ typedef enum ResultT_dummy {
268
269
  RESULT_FAILURE
269
270
  } ResultT;
270
271
 
271
- /**
272
- * Generate a series of cryptographically secure random bytes using OpenSSL.
273
- *
274
- * \param out_bytes Desired number of bytes will be written here.
275
- * \param in_size Number of bytes of random data to be generated.
276
- * \return RESULT_SUCCESS if the bytes were generated successfully,
277
- * RESULT_FAILURE otherwise.
278
- */
279
- static ResultT
280
- GenerateRandomBytes(unsigned char *out_bytes, int in_size)
281
- {
282
- // OpenSSL RNG has not been seeded with enough data and is therefore
283
- // not usable.
284
- if (RAND_status() == 0)
285
- {
286
- return RESULT_FAILURE;
287
- }
288
-
289
- // Attempt to generate random bytes using the OpenSSL RNG
290
- if (RAND_bytes(out_bytes, in_size) != 1)
291
- {
292
- return RESULT_FAILURE;
293
- }
294
-
295
- return RESULT_SUCCESS;
296
- }
297
-
298
272
  /**
299
273
  * Computes the ECDSA signature of the given 32-byte SHA-256 hash.
300
274
  *
@@ -468,16 +442,14 @@ PublicKey_create_from_private_key(Context *in_context,
468
442
  (&public_key->pubkey),
469
443
  private_key_data) != 1)
470
444
  {
471
- rb_raise(rb_eTypeError, "invalid private key data");
445
+ rb_raise(Secp256k1_DeserializationError_class, "invalid private key data");
472
446
  }
473
447
 
474
- public_key->ctx = secp256k1_context_clone(in_context->ctx);
475
448
  return result;
476
449
  }
477
450
 
478
451
  static VALUE
479
- PublicKey_create_from_data(Context *in_context,
480
- unsigned char *in_public_key_data,
452
+ PublicKey_create_from_data(unsigned char *in_public_key_data,
481
453
  unsigned int in_public_key_data_len)
482
454
  {
483
455
  PublicKey *public_key;
@@ -486,18 +458,39 @@ PublicKey_create_from_data(Context *in_context,
486
458
  result = PublicKey_alloc(Secp256k1_PublicKey_class);
487
459
  TypedData_Get_Struct(result, PublicKey, &PublicKey_DataType, public_key);
488
460
 
489
- if (secp256k1_ec_pubkey_parse(in_context->ctx,
461
+ if (secp256k1_ec_pubkey_parse(secp256k1_context_no_precomp,
490
462
  &(public_key->pubkey),
491
463
  in_public_key_data,
492
464
  in_public_key_data_len) != 1)
493
465
  {
494
- rb_raise(rb_eRuntimeError, "invalid public key data");
466
+ rb_raise(Secp256k1_DeserializationError_class, "invalid public key data");
495
467
  }
496
468
 
497
- public_key->ctx = secp256k1_context_clone(in_context->ctx);
498
469
  return result;
499
470
  }
500
471
 
472
+ /**
473
+ * Loads a public key from compressed or uncompressed binary data.
474
+ *
475
+ * @param in_public_key_data [String] binary string with compressed or
476
+ * uncompressed public key data.
477
+ * @return [Secp256k1::PublicKey] public key derived from data.
478
+ * @raise [Secp256k1::DeserializationError] if public key data is invalid.
479
+ */
480
+ static VALUE
481
+ PublicKey_from_data(VALUE klass, VALUE in_public_key_data)
482
+ {
483
+ unsigned char *public_key_data;
484
+
485
+ Check_Type(in_public_key_data, T_STRING);
486
+
487
+ public_key_data = (unsigned char*)StringValuePtr(in_public_key_data);
488
+ return PublicKey_create_from_data(
489
+ public_key_data,
490
+ (int)RSTRING_LEN(in_public_key_data)
491
+ );
492
+ }
493
+
501
494
  /**
502
495
  * @return [String] binary string containing the uncompressed representation
503
496
  * of this public key.
@@ -512,7 +505,7 @@ PublicKey_uncompressed(VALUE self)
512
505
 
513
506
  TypedData_Get_Struct(self, PublicKey, &PublicKey_DataType, public_key);
514
507
 
515
- secp256k1_ec_pubkey_serialize(public_key->ctx,
508
+ secp256k1_ec_pubkey_serialize(secp256k1_context_no_precomp,
516
509
  serialized_pubkey,
517
510
  &serialized_pubkey_len,
518
511
  &(public_key->pubkey),
@@ -535,7 +528,7 @@ PublicKey_compressed(VALUE self)
535
528
 
536
529
  TypedData_Get_Struct(self, PublicKey, &PublicKey_DataType, public_key);
537
530
 
538
- secp256k1_ec_pubkey_serialize(public_key->ctx,
531
+ secp256k1_ec_pubkey_serialize(secp256k1_context_no_precomp,
539
532
  serialized_pubkey,
540
533
  &serialized_pubkey_len,
541
534
  &(public_key->pubkey),
@@ -569,14 +562,14 @@ PublicKey_equals(VALUE self, VALUE other)
569
562
  TypedData_Get_Struct(other, PublicKey, &PublicKey_DataType, rhs);
570
563
 
571
564
  secp256k1_ec_pubkey_serialize(
572
- lhs->ctx,
565
+ secp256k1_context_no_precomp,
573
566
  lhs_compressed,
574
567
  &lhs_len,
575
568
  &(lhs->pubkey),
576
569
  SECP256K1_EC_COMPRESSED
577
570
  );
578
571
  secp256k1_ec_pubkey_serialize(
579
- rhs->ctx,
572
+ secp256k1_context_no_precomp,
580
573
  rhs_compressed,
581
574
  &rhs_len,
582
575
  &(rhs->pubkey),
@@ -610,26 +603,52 @@ PrivateKey_alloc(VALUE klass)
610
603
  }
611
604
 
612
605
  static VALUE
613
- PrivateKey_create(Context *in_context, unsigned char *in_private_key_data)
606
+ PrivateKey_create(unsigned char *in_private_key_data)
614
607
  {
615
608
  PrivateKey *private_key;
616
609
  VALUE result;
617
610
 
618
- if (secp256k1_ec_seckey_verify(in_context->ctx, in_private_key_data) != 1)
611
+ if (secp256k1_ec_seckey_verify(secp256k1_context_no_precomp,
612
+ in_private_key_data) != 1)
619
613
  {
620
- rb_raise(rb_eArgError, "invalid private key data");
614
+ rb_raise(Secp256k1_Error_class, "invalid private key data");
621
615
  }
622
616
 
623
617
  result = PrivateKey_alloc(Secp256k1_PrivateKey_class);
624
618
  TypedData_Get_Struct(result, PrivateKey, &PrivateKey_DataType, private_key);
625
619
  MEMCPY(private_key->data, in_private_key_data, char, 32);
626
- private_key->ctx = secp256k1_context_clone(in_context->ctx);
627
620
 
628
621
  rb_iv_set(result, "@data", rb_str_new((char*)in_private_key_data, 32));
629
622
 
630
623
  return result;
631
624
  }
632
625
 
626
+ /**
627
+ * Load a private key from binary data.
628
+ *
629
+ * @param in_private_key_data [String] 32 byte binary string of private key
630
+ * data.
631
+ * @return [Secp256k1::PrivateKey] private key loaded from the given data.
632
+ * @raise [Secp256k1::Error] if private key data is not 32 bytes or is invalid.
633
+ */
634
+ static VALUE
635
+ PrivateKey_from_data(VALUE klass, VALUE in_private_key_data)
636
+ {
637
+ unsigned char *private_key_data;
638
+
639
+ Check_Type(in_private_key_data, T_STRING);
640
+ if (RSTRING_LEN(in_private_key_data) != 32)
641
+ {
642
+ rb_raise(
643
+ Secp256k1_Error_class,
644
+ "private key data must be 32 bytes in length"
645
+ );
646
+ }
647
+
648
+ private_key_data = (unsigned char*)StringValuePtr(in_private_key_data);
649
+ return PrivateKey_create(private_key_data);
650
+ }
651
+
633
652
  /**
634
653
  * Compare two private keys.
635
654
  *
@@ -672,6 +691,77 @@ Signature_alloc(VALUE klass)
672
691
  return new_instance;
673
692
  }
674
693
 
694
+ /**
695
+ * Deserializes a Signature from 64-byte compact signature data.
696
+ *
697
+ * @param in_compact_signature [String] compact signature as 64-byte binary
698
+ * string.
699
+ * @return [Secp256k1::Signature] object deserialized from compact signature.
700
+ * @raise [Secp256k1::DeserializationError] if signature data is invalid.
701
+ */
702
+ static VALUE
703
+ Signature_from_compact(VALUE klass, VALUE in_compact_signature)
704
+ {
705
+ Signature *signature;
706
+ VALUE signature_result;
707
+ unsigned char *signature_data;
708
+
709
+ Check_Type(in_compact_signature, T_STRING);
710
+
711
+ if (RSTRING_LEN(in_compact_signature) != 64)
712
+ {
713
+ rb_raise(Secp256k1_Error_class, "compact signature must be 64 bytes");
714
+ }
715
+
716
+ signature_data = (unsigned char*)StringValuePtr(in_compact_signature);
717
+
718
+ signature_result = Signature_alloc(Secp256k1_Signature_class);
719
+ TypedData_Get_Struct(signature_result, Signature, &Signature_DataType, signature);
720
+
721
+ if (secp256k1_ecdsa_signature_parse_compact(secp256k1_context_no_precomp,
722
+ &(signature->sig),
723
+ signature_data) != 1)
724
+ {
725
+ rb_raise(Secp256k1_DeserializationError_class, "invalid compact signature");
726
+ }
727
+
728
+ return signature_result;
729
+ }
730
+
731
+ /**
732
+ * Converts a DER encoded binary signature into a signature object.
733
+ *
734
+ * @param in_der_encoded_signature [String] DER encoded signature as binary
735
+ * string.
736
+ * @return [Secp256k1::Signature] signature object initialized using signature
737
+ * data.
738
+ * @raise [Secp256k1::DeserializationError] if signature data is invalid.
739
+ */
740
+ static VALUE
741
+ Signature_from_der_encoded(VALUE klass, VALUE in_der_encoded_signature)
742
+ {
743
+ Signature *signature;
744
+ VALUE signature_result;
745
+ unsigned char *signature_data;
746
+
747
+ Check_Type(in_der_encoded_signature, T_STRING);
748
+
749
+ signature_data = (unsigned char*)StringValuePtr(in_der_encoded_signature);
750
+
751
+ signature_result = Signature_alloc(Secp256k1_Signature_class);
752
+ TypedData_Get_Struct(signature_result, Signature, &Signature_DataType, signature);
753
+
754
+ if (secp256k1_ecdsa_signature_parse_der(secp256k1_context_no_precomp,
755
+ &(signature->sig),
756
+ signature_data,
757
+ RSTRING_LEN(in_der_encoded_signature)) != 1)
758
+ {
759
+ rb_raise(Secp256k1_DeserializationError_class, "invalid DER encoded signature");
760
+ }
761
+
762
+ return signature_result;
763
+ }
764
+
675
765
  /**
676
766
  * Return Distinguished Encoding Rules (DER) encoded signature data.
677
767
  *
@@ -688,12 +778,15 @@ Signature_der_encoded(VALUE self)
688
778
  TypedData_Get_Struct(self, Signature, &Signature_DataType, signature);
689
779
 
690
780
  der_signature_len = 72;
691
- if (secp256k1_ecdsa_signature_serialize_der(signature->ctx,
781
+ if (secp256k1_ecdsa_signature_serialize_der(secp256k1_context_no_precomp,
692
782
  der_signature,
693
783
  &der_signature_len,
694
784
  &(signature->sig)) != 1)
695
785
  {
696
- rb_raise(rb_eRuntimeError, "could not compute DER encoded signature");
786
+ rb_raise(
787
+ Secp256k1_SerializationError_class,
788
+ "could not compute DER encoded signature"
789
+ );
697
790
  }
698
791
 
699
792
  return rb_str_new((char*)der_signature, der_signature_len);
@@ -713,11 +806,14 @@ Signature_compact(VALUE self)
713
806
 
714
807
  TypedData_Get_Struct(self, Signature, &Signature_DataType, signature);
715
808
 
716
- if (secp256k1_ecdsa_signature_serialize_compact(signature->ctx,
809
+ if (secp256k1_ecdsa_signature_serialize_compact(secp256k1_context_no_precomp,
717
810
  compact_signature,
718
811
  &(signature->sig)) != 1)
719
812
  {
720
- rb_raise(rb_eRuntimeError, "unable to compute compact signature");
813
+ rb_raise(
814
+ Secp256k1_SerializationError_class,
815
+ "unable to compute compact signature"
816
+ );
721
817
  }
722
818
 
723
819
  return rb_str_new((char*)compact_signature, COMPACT_SIG_SIZE_BYTES);
@@ -751,15 +847,13 @@ Signature_normalized(VALUE self)
751
847
 
752
848
  was_normalized = Qfalse;
753
849
  if (secp256k1_ecdsa_signature_normalize(
754
- signature->ctx,
850
+ secp256k1_context_no_precomp,
755
851
  &(normalized_signature->sig),
756
852
  &(signature->sig)) == 1)
757
853
  {
758
854
  was_normalized = Qtrue;
759
855
  }
760
856
 
761
- normalized_signature->ctx = secp256k1_context_clone(signature->ctx);
762
-
763
857
  result = rb_ary_new2(2);
764
858
  rb_ary_push(result, was_normalized);
765
859
  rb_ary_push(result, result_sig);
@@ -787,10 +881,10 @@ Signature_equals(VALUE self, VALUE other)
787
881
  TypedData_Get_Struct(other, Signature, &Signature_DataType, rhs);
788
882
 
789
883
  secp256k1_ecdsa_signature_serialize_compact(
790
- lhs->ctx, lhs_compact, &(lhs->sig)
884
+ secp256k1_context_no_precomp, lhs_compact, &(lhs->sig)
791
885
  );
792
886
  secp256k1_ecdsa_signature_serialize_compact(
793
- rhs->ctx, rhs_compact, &(rhs->sig)
887
+ secp256k1_context_no_precomp, rhs_compact, &(rhs->sig)
794
888
  );
795
889
 
796
890
  if (memcmp(lhs_compact, rhs_compact, 64) == 0)
@@ -827,7 +921,7 @@ RecoverableSignature_alloc(VALUE klass)
827
921
  *
828
922
  * @return [Array] first element is the 64 byte compact encoding of signature,
829
923
  * the second element is the integer recovery ID.
830
- * @raise [RuntimeError] if signature serialization fails.
924
+ * @raise [Secp256k1::SerializationError] if signature serialization fails.
831
925
  */
832
926
  static VALUE
833
927
  RecoverableSignature_compact(VALUE self)
@@ -845,12 +939,15 @@ RecoverableSignature_compact(VALUE self)
845
939
  );
846
940
 
847
941
  if (secp256k1_ecdsa_recoverable_signature_serialize_compact(
848
- recoverable_signature->ctx,
942
+ secp256k1_context_no_precomp,
849
943
  compact_sig,
850
944
  &recovery_id,
851
945
  &(recoverable_signature->sig)) != 1)
852
946
  {
853
- rb_raise(rb_eRuntimeError, "unable to serialize recoverable signature");
947
+ rb_raise(
948
+ Secp256k1_SerializationError_class,
949
+ "unable to serialize recoverable signature"
950
+ );
854
951
  }
855
952
 
856
953
  // Create a new array with room for 2 elements and push data onto it
@@ -891,11 +988,10 @@ RecoverableSignature_to_signature(VALUE self)
891
988
 
892
989
  // NOTE: This method cannot fail
893
990
  secp256k1_ecdsa_recoverable_signature_convert(
894
- recoverable_signature->ctx,
991
+ secp256k1_context_no_precomp,
895
992
  &(signature->sig),
896
993
  &(recoverable_signature->sig));
897
994
 
898
- signature->ctx = secp256k1_context_clone(recoverable_signature->ctx);
899
995
  return result;
900
996
  }
901
997
 
@@ -904,7 +1000,9 @@ RecoverableSignature_to_signature(VALUE self)
904
1000
  *
905
1001
  * @param in_hash32 [String] 32-byte SHA-256 hash of data.
906
1002
  * @return [Secp256k1::PublicKey] recovered public key.
907
- * @raise [RuntimeError] if the public key could not be recovered.
1003
+ * @raise [Secp256k1::Error] if hash given is not 32 bytes.
1004
+ * @raise [Secp256k1::DeserializationError] if public key could not be
1005
+ * recovered.
908
1006
  */
909
1007
  static VALUE
910
1008
  RecoverableSignature_recover_public_key(VALUE self, VALUE in_hash32)
@@ -917,7 +1015,7 @@ RecoverableSignature_recover_public_key(VALUE self, VALUE in_hash32)
917
1015
  Check_Type(in_hash32, T_STRING);
918
1016
  if (RSTRING_LEN(in_hash32) != 32)
919
1017
  {
920
- rb_raise(rb_eArgError, "in_hash32 is not 32 bytes in length");
1018
+ rb_raise(Secp256k1_Error_class, "in_hash32 is not 32 bytes in length");
921
1019
  }
922
1020
 
923
1021
  TypedData_Get_Struct(
@@ -936,11 +1034,10 @@ RecoverableSignature_recover_public_key(VALUE self, VALUE in_hash32)
936
1034
  &(recoverable_signature->sig),
937
1035
  hash32) == 1)
938
1036
  {
939
- public_key->ctx = secp256k1_context_clone(recoverable_signature->ctx);
940
1037
  return result;
941
1038
  }
942
1039
 
943
- rb_raise(rb_eRuntimeError, "unable to recover public key");
1040
+ rb_raise(Secp256k1_DeserializationError_class, "unable to recover public key");
944
1041
  }
945
1042
 
946
1043
  /**
@@ -1028,14 +1125,27 @@ Context_alloc(VALUE klass)
1028
1125
  *
1029
1126
  * Context initialization should be infrequent as it is an expensive operation.
1030
1127
  *
1128
+ * @param context_randomization_bytes [String,nil] (Optional) 32 bytes of
1129
+ * random data used to randomize the context. If omitted then the
1130
+ * context remains unrandomized. It is recommended that you provide this
1131
+ * argument.
1031
1132
  * @return [Secp256k1::Context]
1032
- * @raise [RuntimeError] if context randomization fails.
1133
+ * @raise [Secp256k1::Error] if context randomization fails.
1033
1134
  */
1034
1135
  static VALUE
1035
- Context_initialize(VALUE self)
1136
+ Context_initialize(int argc, const VALUE* argv, VALUE self)
1036
1137
  {
1037
1138
  Context *context;
1038
- unsigned char seed[32];
1139
+ unsigned char *seed32;
1140
+ VALUE context_randomization_bytes;
1141
+ VALUE opts;
1142
+ static ID kwarg_ids;
1143
+
1144
+ context_randomization_bytes = Qnil;
1145
+ if (!kwarg_ids)
1146
+ {
1147
+ CONST_ID(kwarg_ids, "context_randomization_bytes");
1148
+ }
1039
1149
 
1040
1150
  TypedData_Get_Struct(self, Context, &Context_DataType, context);
1041
1151
 
@@ -1043,101 +1153,46 @@ Context_initialize(VALUE self)
1043
1153
  SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY
1044
1154
  );
1045
1155
 
1046
- // Randomize the context at initialization time rather than before calls so
1047
- // the same context can be used across threads safely.
1048
- GenerateRandomBytes(seed, 32);
1049
- if (secp256k1_context_randomize(context->ctx, seed) != 1)
1050
- {
1051
- rb_raise(rb_eRuntimeError, "context randomization failed");
1052
- }
1053
-
1054
- return self;
1055
- }
1056
-
1057
- /**
1058
- * Generate a new public-private key pair.
1059
- *
1060
- * @return [Secp256k1::KeyPair] newly generated key pair.
1061
- * @raise [RuntimeError] if private key generation fails.
1062
- */
1063
- static VALUE
1064
- Context_generate_key_pair(VALUE self)
1065
- {
1066
- Context *context;
1067
- VALUE private_key;
1068
- VALUE public_key;
1069
- VALUE result;
1070
- unsigned char private_key_bytes[32];
1071
-
1072
- if (FAILURE(GenerateRandomBytes(private_key_bytes, 32)))
1156
+ // Handle optional second argument containing random bytes to use for
1157
+ // randomization. We pass ":" to rb_scan_args to say that we expect keyword
1158
+ // arguments. We then parse the opts result of the scan in order to grab
1159
+ // context_randomization_bytes from the hash.
1160
+ rb_scan_args(argc, argv, ":", &opts);
1161
+ rb_get_kwargs(opts, &kwarg_ids, 0, 1, &context_randomization_bytes);
1162
+
1163
+ // We need this check because rb_get_kwargs will set the result to Qundef if
1164
+ // the keyword argument is not provided. This lets us use the NIL_P
1165
+ // predicate.
1166
+ if (context_randomization_bytes == Qundef)
1073
1167
  {
1074
- rb_raise(rb_eRuntimeError, "unable to generate private key bytes.");
1168
+ context_randomization_bytes = Qnil;
1075
1169
  }
1076
1170
 
1077
- TypedData_Get_Struct(self, Context, &Context_DataType, context);
1078
-
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
- );
1088
-
1089
- return result;
1090
- }
1091
-
1092
- /**
1093
- * Loads a public key from compressed or uncompressed binary data.
1094
- *
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.
1099
- */
1100
- static VALUE
1101
- Context_public_key_from_data(VALUE self, VALUE in_public_key_data)
1102
- {
1103
- Context *context;
1104
- unsigned char *public_key_data;
1105
-
1106
- Check_Type(in_public_key_data, T_STRING);
1107
-
1108
- TypedData_Get_Struct(self, Context, &Context_DataType, context);
1109
- public_key_data = (unsigned char*)StringValuePtr(in_public_key_data);
1110
- return PublicKey_create_from_data(
1111
- context,
1112
- public_key_data,
1113
- (int)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)
1171
+ if (!NIL_P(context_randomization_bytes)) // Random bytes given
1136
1172
  {
1137
- rb_raise(rb_eArgError, "private key data must be 32 bytes in length");
1173
+ Check_Type(context_randomization_bytes, T_STRING);
1174
+ if (RSTRING_LEN(context_randomization_bytes) != 32)
1175
+ {
1176
+ rb_raise(
1177
+ Secp256k1_Error_class,
1178
+ "context_randomization_bytes must be 32 bytes in length"
1179
+ );
1180
+ }
1181
+
1182
+ seed32 = (unsigned char*)StringValuePtr(context_randomization_bytes);
1183
+
1184
+ // Randomize the context at initialization time rather than before calls so
1185
+ // the same context can be used across threads safely.
1186
+ if (secp256k1_context_randomize(context->ctx, seed32) != 1)
1187
+ {
1188
+ rb_raise(
1189
+ Secp256k1_Error_class,
1190
+ "context randomization failed"
1191
+ );
1192
+ }
1138
1193
  }
1139
1194
 
1140
- return PrivateKey_create(context, private_key_data);
1195
+ return self;
1141
1196
  }
1142
1197
 
1143
1198
  /**
@@ -1145,7 +1200,7 @@ Context_private_key_from_data(VALUE self, VALUE in_private_key_data)
1145
1200
  *
1146
1201
  * @param in_private_key_data [String] binary private key data
1147
1202
  * @return [Secp256k1::KeyPair] key pair initialized from the private key data.
1148
- * @raise [ArgumentError] if the private key data is invalid or key derivation
1203
+ * @raise [Secp256k1::Error] if the private key data is invalid or key derivation
1149
1204
  * fails.
1150
1205
  */
1151
1206
  static VALUE
@@ -1157,16 +1212,16 @@ Context_key_pair_from_private_key(VALUE self, VALUE in_private_key_data)
1157
1212
  unsigned char *private_key_data;
1158
1213
 
1159
1214
  Check_Type(in_private_key_data, T_STRING);
1215
+ TypedData_Get_Struct(self, Context, &Context_DataType, context);
1160
1216
 
1161
1217
  if (RSTRING_LEN(in_private_key_data) != 32)
1162
1218
  {
1163
- rb_raise(rb_eArgError, "private key data must be 32 bytes in length");
1219
+ rb_raise(Secp256k1_Error_class, "private key data must be 32 bytes in length");
1164
1220
  }
1165
1221
 
1166
- TypedData_Get_Struct(self, Context, &Context_DataType, context);
1167
1222
  private_key_data = (unsigned char*)StringValuePtr(in_private_key_data);
1168
1223
 
1169
- private_key = PrivateKey_create(context, private_key_data);
1224
+ private_key = PrivateKey_create(private_key_data);
1170
1225
  public_key = PublicKey_create_from_private_key(context, private_key_data);
1171
1226
 
1172
1227
  return rb_funcall(
@@ -1178,83 +1233,6 @@ Context_key_pair_from_private_key(VALUE self, VALUE in_private_key_data)
1178
1233
  );
1179
1234
  }
1180
1235
 
1181
- /**
1182
- * Converts a DER encoded binary signature into a signature object.
1183
- *
1184
- * @param in_der_encoded_signature [String] DER encoded signature as binary
1185
- * string.
1186
- * @return [Secp256k1::Signature] signature object initialized using signature
1187
- * data.
1188
- * @raise [ArgumentError] if signature data is invalid.
1189
- */
1190
- static VALUE
1191
- Context_signature_from_der_encoded(VALUE self, VALUE in_der_encoded_signature)
1192
- {
1193
- Context *context;
1194
- Signature *signature;
1195
- VALUE signature_result;
1196
- unsigned char *signature_data;
1197
-
1198
- Check_Type(in_der_encoded_signature, T_STRING);
1199
-
1200
- TypedData_Get_Struct(self, Context, &Context_DataType, context);
1201
- signature_data = (unsigned char*)StringValuePtr(in_der_encoded_signature);
1202
-
1203
- signature_result = Signature_alloc(Secp256k1_Signature_class);
1204
- TypedData_Get_Struct(signature_result, Signature, &Signature_DataType, signature);
1205
-
1206
- if (secp256k1_ecdsa_signature_parse_der(context->ctx,
1207
- &(signature->sig),
1208
- signature_data,
1209
- RSTRING_LEN(in_der_encoded_signature)) != 1)
1210
- {
1211
- rb_raise(rb_eArgError, "invalid DER encoded signature");
1212
- }
1213
-
1214
- signature->ctx = secp256k1_context_clone(context->ctx);
1215
- return signature_result;
1216
- }
1217
-
1218
- /**
1219
- * Deserializes a Signature from 64-byte compact signature data.
1220
- *
1221
- * @param in_compact_signature [String] compact signature as 64-byte binary
1222
- * string.
1223
- * @return [Secp256k1::Signature] object deserialized from compact signature.
1224
- * @raise [ArgumentError] if signature data is invalid.
1225
- */
1226
- static VALUE
1227
- Context_signature_from_compact(VALUE self, VALUE in_compact_signature)
1228
- {
1229
- Context *context;
1230
- Signature *signature;
1231
- VALUE signature_result;
1232
- unsigned char *signature_data;
1233
-
1234
- Check_Type(in_compact_signature, T_STRING);
1235
-
1236
- if (RSTRING_LEN(in_compact_signature) != 64)
1237
- {
1238
- rb_raise(rb_eArgError, "compact signature must be 64 bytes");
1239
- }
1240
-
1241
- TypedData_Get_Struct(self, Context, &Context_DataType, context);
1242
- signature_data = (unsigned char*)StringValuePtr(in_compact_signature);
1243
-
1244
- signature_result = Signature_alloc(Secp256k1_Signature_class);
1245
- TypedData_Get_Struct(signature_result, Signature, &Signature_DataType, signature);
1246
-
1247
- if (secp256k1_ecdsa_signature_parse_compact(context->ctx,
1248
- &(signature->sig),
1249
- signature_data) != 1)
1250
- {
1251
- rb_raise(rb_eArgError, "invalid compact signature");
1252
- }
1253
-
1254
- signature->ctx = secp256k1_context_clone(context->ctx);
1255
- return signature_result;
1256
- }
1257
-
1258
1236
  /**
1259
1237
  * Computes the ECDSA signature of the data using the secp256k1 elliptic curve.
1260
1238
  *
@@ -1262,8 +1240,8 @@ Context_signature_from_compact(VALUE self, VALUE in_compact_signature)
1262
1240
  * signing.
1263
1241
  * @param in_hash32 [String] 32-byte binary string with SHA-256 hash of data.
1264
1242
  * @return [Secp256k1::Signature] signature resulting from signing data.
1265
- * @raise [RuntimeError] if signature computation fails.
1266
- * @raise [ArgumentError] if hash is not 32-bytes in length.
1243
+ * @raise [Secp256k1::Error] if hash is not 32-bytes in length or signature
1244
+ * computation fails.
1267
1245
  */
1268
1246
  static VALUE
1269
1247
  Context_sign(VALUE self, VALUE in_private_key, VALUE in_hash32)
@@ -1278,7 +1256,7 @@ Context_sign(VALUE self, VALUE in_private_key, VALUE in_hash32)
1278
1256
 
1279
1257
  if (RSTRING_LEN(in_hash32) != 32)
1280
1258
  {
1281
- rb_raise(rb_eArgError, "in_hash32 is not 32 bytes in length");
1259
+ rb_raise(Secp256k1_Error_class, "in_hash32 is not 32 bytes in length");
1282
1260
  }
1283
1261
 
1284
1262
  TypedData_Get_Struct(self, Context, &Context_DataType, context);
@@ -1294,11 +1272,10 @@ Context_sign(VALUE self, VALUE in_private_key, VALUE in_hash32)
1294
1272
  private_key->data,
1295
1273
  &(signature->sig))))
1296
1274
  {
1297
- signature->ctx = secp256k1_context_clone(context->ctx);
1298
1275
  return signature_result;
1299
1276
  }
1300
1277
 
1301
- rb_raise(rb_eRuntimeError, "unable to compute signature");
1278
+ rb_raise(Secp256k1_Error_class, "unable to compute signature");
1302
1279
  }
1303
1280
 
1304
1281
  /**
@@ -1310,7 +1287,7 @@ Context_sign(VALUE self, VALUE in_private_key, VALUE in_hash32)
1310
1287
  * @param in_hash32 [String] 32-byte binary string containing SHA-256 hash of
1311
1288
  * data.
1312
1289
  * @return [Boolean] True if the signature is valid, false otherwise.
1313
- * @raise [ArgumentError] if hash is not 32-bytes in length.
1290
+ * @raise [Secp256k1::Error] if hash is not 32-bytes in length.
1314
1291
  */
1315
1292
  static VALUE
1316
1293
  Context_verify(VALUE self, VALUE in_signature, VALUE in_pubkey, VALUE in_hash32)
@@ -1324,7 +1301,7 @@ Context_verify(VALUE self, VALUE in_signature, VALUE in_pubkey, VALUE in_hash32)
1324
1301
 
1325
1302
  if (RSTRING_LEN(in_hash32) != 32)
1326
1303
  {
1327
- rb_raise(rb_eArgError, "in_hash32 is not 32-bytes in length");
1304
+ rb_raise(Secp256k1_Error_class, "in_hash32 is not 32-bytes in length");
1328
1305
  }
1329
1306
 
1330
1307
  TypedData_Get_Struct(self, Context, &Context_DataType, context);
@@ -1354,7 +1331,8 @@ Context_verify(VALUE self, VALUE in_signature, VALUE in_pubkey, VALUE in_hash32)
1354
1331
  * @param in_hash32 [String] 32-byte binary string with SHA-256 hash of data.
1355
1332
  * @return [Secp256k1::RecoverableSignature] recoverable signature produced by
1356
1333
  * signing the SHA-256 hash `in_hash32` with `in_private_key`.
1357
- * @raise [ArgumentError] if the hash is not 32 bytes
1334
+ * @raise [Secp256k1::Error] if the hash is not 32 bytes or signature could not
1335
+ * be computed.
1358
1336
  */
1359
1337
  static VALUE
1360
1338
  Context_sign_recoverable(VALUE self, VALUE in_private_key, VALUE in_hash32)
@@ -1368,7 +1346,7 @@ Context_sign_recoverable(VALUE self, VALUE in_private_key, VALUE in_hash32)
1368
1346
  Check_Type(in_hash32, T_STRING);
1369
1347
  if (RSTRING_LEN(in_hash32) != 32)
1370
1348
  {
1371
- rb_raise(rb_eArgError, "in_hash32 is not 32 bytes in length");
1349
+ rb_raise(Secp256k1_Error_class, "in_hash32 is not 32 bytes in length");
1372
1350
  }
1373
1351
 
1374
1352
  TypedData_Get_Struct(self, Context, &Context_DataType, context);
@@ -1394,7 +1372,7 @@ Context_sign_recoverable(VALUE self, VALUE in_private_key, VALUE in_hash32)
1394
1372
  return result;
1395
1373
  }
1396
1374
 
1397
- rb_raise(rb_eRuntimeError, "unable to compute recoverable signature");
1375
+ rb_raise(Secp256k1_Error_class, "unable to compute recoverable signature");
1398
1376
  }
1399
1377
 
1400
1378
  /**
@@ -1404,8 +1382,9 @@ Context_sign_recoverable(VALUE self, VALUE in_private_key, VALUE in_hash32)
1404
1382
  * data.
1405
1383
  * @param in_recovery_id [Integer] recovery ID (range [0, 3])
1406
1384
  * @return [Secp256k1::RecoverableSignature] signature parsed from data.
1407
- * @raise [RuntimeError] if signature data or recovery ID is invalid.
1408
- * @raise [ArgumentError] if compact signature is not 64 bytes or recovery ID
1385
+ * @raise [Secp256k1::DeserializationError] if signature data or recovery ID is
1386
+ * invalid.
1387
+ * @raise [Secp256k1::Error] if compact signature is not 64 bytes or recovery ID
1409
1388
  * is not in range [0, 3].
1410
1389
  */
1411
1390
  static VALUE
@@ -1427,12 +1406,12 @@ Context_recoverable_signature_from_compact(
1427
1406
 
1428
1407
  if (RSTRING_LEN(in_compact_sig) != 64)
1429
1408
  {
1430
- rb_raise(rb_eArgError, "compact signature is not 64 bytes");
1409
+ rb_raise(Secp256k1_Error_class, "compact signature is not 64 bytes");
1431
1410
  }
1432
1411
 
1433
1412
  if (recovery_id < 0 || recovery_id > 3)
1434
1413
  {
1435
- rb_raise(rb_eArgError, "invalid recovery ID, must be in range [0, 3]");
1414
+ rb_raise(Secp256k1_Error_class, "invalid recovery ID, must be in range [0, 3]");
1436
1415
  }
1437
1416
 
1438
1417
  result = RecoverableSignature_alloc(Secp256k1_RecoverableSignature_class);
@@ -1444,7 +1423,7 @@ Context_recoverable_signature_from_compact(
1444
1423
  );
1445
1424
 
1446
1425
  if (secp256k1_ecdsa_recoverable_signature_parse_compact(
1447
- context->ctx,
1426
+ secp256k1_context_no_precomp,
1448
1427
  &(recoverable_signature->sig),
1449
1428
  compact_sig,
1450
1429
  recovery_id) == 1)
@@ -1453,7 +1432,7 @@ Context_recoverable_signature_from_compact(
1453
1432
  return result;
1454
1433
  }
1455
1434
 
1456
- rb_raise(rb_eRuntimeError, "unable to parse recoverable signature");
1435
+ rb_raise(Secp256k1_DeserializationError_class, "unable to parse recoverable signature");
1457
1436
  }
1458
1437
 
1459
1438
  #endif // HAVE_SECP256K1_RECOVERY_H
@@ -1469,7 +1448,7 @@ Context_recoverable_signature_from_compact(
1469
1448
  * @param point [Secp256k1::PublicKey] public-key representing ECDH point.
1470
1449
  * @param scalar [Secp256k1::PrivateKey] private-key representing ECDH scalar.
1471
1450
  * @return [Secp256k1::SharedSecret] shared secret
1472
- * @raise [RuntimeError] If scalar was invalid (zero or caused overflow).
1451
+ * @raise [Secp256k1::Error] If scalar was invalid (zero or caused overflow).
1473
1452
  */
1474
1453
  static VALUE
1475
1454
  Context_ecdh(VALUE self, VALUE point, VALUE scalar)
@@ -1496,7 +1475,7 @@ Context_ecdh(VALUE self, VALUE point, VALUE scalar)
1496
1475
  NULL,
1497
1476
  NULL) != 1)
1498
1477
  {
1499
- rb_raise(rb_eRuntimeError, "invalid scalar provided to ecdh");
1478
+ rb_raise(Secp256k1_Error_class, "invalid scalar provided to ecdh");
1500
1479
  }
1501
1480
 
1502
1481
  rb_iv_set(result, "@data", rb_str_new((char*)shared_secret->data, 32));
@@ -1548,13 +1527,6 @@ Secp256k1_have_ecdh(VALUE module)
1548
1527
 
1549
1528
  void Init_rbsecp256k1()
1550
1529
  {
1551
- // NOTE: All classes derive from Data (rb_cData) rather than Object
1552
- // (rb_cObject). This makes it so we don't have to call rb_undef_alloc_func
1553
- // for each class and can instead simply define the allocation methods for
1554
- // each class.
1555
- //
1556
- // See: https://github.com/ruby/ruby/blob/trunk/doc/extension.rdoc#encapsulate-c-data-into-a-ruby-object
1557
-
1558
1530
  // Secp256k1
1559
1531
  Secp256k1_module = rb_define_module("Secp256k1");
1560
1532
  rb_define_singleton_method(
@@ -1570,31 +1542,31 @@ void Init_rbsecp256k1()
1570
1542
  0
1571
1543
  );
1572
1544
 
1545
+ // Secp256k1 exception hierarchy
1546
+ Secp256k1_Error_class = rb_define_class_under(
1547
+ Secp256k1_module, "Error", rb_eStandardError
1548
+ );
1549
+ Secp256k1_SerializationError_class = rb_define_class_under(
1550
+ Secp256k1_module, "SerializationError", Secp256k1_Error_class
1551
+ );
1552
+ Secp256k1_DeserializationError_class = rb_define_class_under(
1553
+ Secp256k1_module, "DeserializationError", Secp256k1_Error_class
1554
+ );
1555
+
1573
1556
  // Secp256k1::Context
1574
1557
  Secp256k1_Context_class = rb_define_class_under(
1575
- Secp256k1_module, "Context", rb_cData
1558
+ Secp256k1_module, "Context", rb_cObject
1576
1559
  );
1560
+ rb_undef_alloc_func(Secp256k1_Context_class);
1577
1561
  rb_define_alloc_func(Secp256k1_Context_class, Context_alloc);
1578
1562
  rb_define_method(Secp256k1_Context_class,
1579
1563
  "initialize",
1580
1564
  Context_initialize,
1581
- 0);
1582
- rb_define_method(Secp256k1_Context_class,
1583
- "generate_key_pair",
1584
- Context_generate_key_pair,
1585
- 0);
1565
+ -1);
1586
1566
  rb_define_method(Secp256k1_Context_class,
1587
1567
  "key_pair_from_private_key",
1588
1568
  Context_key_pair_from_private_key,
1589
1569
  1);
1590
- rb_define_method(Secp256k1_Context_class,
1591
- "public_key_from_data",
1592
- Context_public_key_from_data,
1593
- 1);
1594
- rb_define_method(Secp256k1_Context_class,
1595
- "private_key_from_data",
1596
- Context_private_key_from_data,
1597
- 1);
1598
1570
  rb_define_method(Secp256k1_Context_class,
1599
1571
  "sign",
1600
1572
  Context_sign,
@@ -1603,19 +1575,12 @@ void Init_rbsecp256k1()
1603
1575
  "verify",
1604
1576
  Context_verify,
1605
1577
  3);
1606
- rb_define_method(Secp256k1_Context_class,
1607
- "signature_from_der_encoded",
1608
- Context_signature_from_der_encoded,
1609
- 1);
1610
- rb_define_method(Secp256k1_Context_class,
1611
- "signature_from_compact",
1612
- Context_signature_from_compact,
1613
- 1);
1614
1578
 
1615
1579
  // Secp256k1::KeyPair
1616
1580
  Secp256k1_KeyPair_class = rb_define_class_under(Secp256k1_module,
1617
1581
  "KeyPair",
1618
- rb_cData);
1582
+ rb_cObject);
1583
+ rb_undef_alloc_func(Secp256k1_KeyPair_class);
1619
1584
  rb_define_alloc_func(Secp256k1_KeyPair_class, KeyPair_alloc);
1620
1585
  rb_define_attr(Secp256k1_KeyPair_class, "public_key", 1, 0);
1621
1586
  rb_define_attr(Secp256k1_KeyPair_class, "private_key", 1, 0);
@@ -1628,7 +1593,8 @@ void Init_rbsecp256k1()
1628
1593
  // Secp256k1::PublicKey
1629
1594
  Secp256k1_PublicKey_class = rb_define_class_under(Secp256k1_module,
1630
1595
  "PublicKey",
1631
- rb_cData);
1596
+ rb_cObject);
1597
+ rb_undef_alloc_func(Secp256k1_PublicKey_class);
1632
1598
  rb_define_alloc_func(Secp256k1_PublicKey_class, PublicKey_alloc);
1633
1599
  rb_define_method(Secp256k1_PublicKey_class,
1634
1600
  "compressed",
@@ -1638,20 +1604,34 @@ void Init_rbsecp256k1()
1638
1604
  "uncompressed",
1639
1605
  PublicKey_uncompressed,
1640
1606
  0);
1607
+ rb_define_singleton_method(
1608
+ Secp256k1_PublicKey_class,
1609
+ "from_data",
1610
+ PublicKey_from_data,
1611
+ 1
1612
+ );
1641
1613
  rb_define_method(Secp256k1_PublicKey_class, "==", PublicKey_equals, 1);
1642
1614
 
1643
1615
  // Secp256k1::PrivateKey
1644
1616
  Secp256k1_PrivateKey_class = rb_define_class_under(
1645
- Secp256k1_module, "PrivateKey", rb_cData
1617
+ Secp256k1_module, "PrivateKey", rb_cObject
1646
1618
  );
1619
+ rb_undef_alloc_func(Secp256k1_PrivateKey_class);
1647
1620
  rb_define_alloc_func(Secp256k1_PrivateKey_class, PrivateKey_alloc);
1648
1621
  rb_define_attr(Secp256k1_PrivateKey_class, "data", 1, 0);
1649
1622
  rb_define_method(Secp256k1_PrivateKey_class, "==", PrivateKey_equals, 1);
1623
+ rb_define_singleton_method(
1624
+ Secp256k1_PrivateKey_class,
1625
+ "from_data",
1626
+ PrivateKey_from_data,
1627
+ 1
1628
+ );
1650
1629
 
1651
1630
  // Secp256k1::Signature
1652
1631
  Secp256k1_Signature_class = rb_define_class_under(Secp256k1_module,
1653
1632
  "Signature",
1654
- rb_cData);
1633
+ rb_cObject);
1634
+ rb_undef_alloc_func(Secp256k1_Signature_class);
1655
1635
  rb_define_alloc_func(Secp256k1_Signature_class, Signature_alloc);
1656
1636
  rb_define_method(Secp256k1_Signature_class,
1657
1637
  "der_encoded",
@@ -1669,14 +1649,27 @@ void Init_rbsecp256k1()
1669
1649
  "==",
1670
1650
  Signature_equals,
1671
1651
  1);
1652
+ rb_define_singleton_method(
1653
+ Secp256k1_Signature_class,
1654
+ "from_compact",
1655
+ Signature_from_compact,
1656
+ 1
1657
+ );
1658
+ rb_define_singleton_method(
1659
+ Secp256k1_Signature_class,
1660
+ "from_der_encoded",
1661
+ Signature_from_der_encoded,
1662
+ 1
1663
+ );
1672
1664
 
1673
1665
  #ifdef HAVE_SECP256K1_RECOVERY_H
1674
1666
  // Secp256k1::RecoverableSignature
1675
1667
  Secp256k1_RecoverableSignature_class = rb_define_class_under(
1676
1668
  Secp256k1_module,
1677
1669
  "RecoverableSignature",
1678
- rb_cData
1670
+ rb_cObject
1679
1671
  );
1672
+ rb_undef_alloc_func(Secp256k1_RecoverableSignature_class);
1680
1673
  rb_define_alloc_func(
1681
1674
  Secp256k1_RecoverableSignature_class,
1682
1675
  RecoverableSignature_alloc
@@ -1725,8 +1718,9 @@ void Init_rbsecp256k1()
1725
1718
  Secp256k1_SharedSecret_class = rb_define_class_under(
1726
1719
  Secp256k1_module,
1727
1720
  "SharedSecret",
1728
- rb_cData
1721
+ rb_cObject
1729
1722
  );
1723
+ rb_undef_alloc_func(Secp256k1_SharedSecret_class);
1730
1724
  rb_define_alloc_func(Secp256k1_SharedSecret_class, SharedSecret_alloc);
1731
1725
  rb_define_attr(Secp256k1_SharedSecret_class, "data", 1, 0);
1732
1726