Module: Bitcoin::Secp256k1::Native
- Extended by:
- Native
- Includes:
- FFI::Library
- Included in:
- Native
- Defined in:
- lib/bitcoin/secp256k1/native.rb
Overview
binding for secp256k1 (github.com/bitcoin-core/secp256k1/) tag: v0.4.0 this is not included by default, to enable set shared object path to ENV for linux, ENV = ‘/usr/local/lib/libsecp256k1.so’ for mac,
Constant Summary collapse
- SECP256K1_FLAGS_TYPE_MASK =
((1 << 8) - 1)
- SECP256K1_FLAGS_TYPE_CONTEXT =
(1 << 0)
- SECP256K1_FLAGS_TYPE_COMPRESSION =
(1 << 1)
- SECP256K1_FLAGS_BIT_CONTEXT_VERIFY =
(1 << 8)
- SECP256K1_FLAGS_BIT_CONTEXT_SIGN =
(1 << 9)
- SECP256K1_FLAGS_BIT_COMPRESSION =
(1 << 8)
- SECP256K1_CONTEXT_VERIFY =
Flags to pass to secp256k1_context_create.
(SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
- SECP256K1_CONTEXT_SIGN =
(SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
- SECP256K1_EC_COMPRESSED =
Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export.
(SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
- SECP256K1_EC_UNCOMPRESSED =
(SECP256K1_FLAGS_TYPE_COMPRESSION)
Class Method Summary collapse
-
.create_keypair(priv_key) ⇒ String
Create key pair data from private key.
-
.ellswift_create(priv_key) ⇒ String
Compute an ElligatorSwift public key for a secret key.
-
.ellswift_decode(ell_key) ⇒ String
Decode ellswift public key.
-
.ellswift_ecdh_xonly(their_ell_pubkey, our_ell_pubkey, priv_key, initiating) ⇒ String
Compute X coordinate of shared ECDH point between elswift pubkey and privkey.
-
.generate_key(compressed: true) ⇒ Object
generate bitcoin key object.
-
.generate_key_pair(compressed: true) ⇒ Object
generate ec private key and public key.
- .generate_pubkey(priv_key, compressed: true) ⇒ Object
- .init ⇒ Object
- .load_functions ⇒ Object
-
.parse_ec_pubkey?(pub_key, allow_hybrid = false) ⇒ Boolean
# validate whether this is a valid public key (more expensive than IsValid()).
-
.recover_compact(data, signature, rec, compressed) ⇒ Bitcoin::Key
Recover public key from compact signature.
-
.sign_compact(data, privkey) ⇒ Array[signature, recovery id]
Sign data with compact format.
-
.sign_data(data, privkey, extra_entropy = nil, algo: :ecdsa) ⇒ String
sign data.
-
.valid_xonly_pubkey?(pub_key) ⇒ Boolean
Check whether valid x-only public key or not.
-
.verify_sig(data, sig, pubkey, algo: :ecdsa) ⇒ Boolean
verify signature # @param [Symbol] algo signature algorithm.
- .with_context(flags: (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN)) ⇒ Object
Class Method Details
.create_keypair(priv_key) ⇒ String
Create key pair data from private key.
210 211 212 213 214 215 216 217 218 219 |
# File 'lib/bitcoin/secp256k1/native.rb', line 210 def create_keypair(priv_key) with_context do |context| priv_key = priv_key.htb secret = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key) raise 'priv_key is invalid.' unless secp256k1_ec_seckey_verify(context, secret) keypair = FFI::MemoryPointer.new(:uchar, 96) raise 'priv_key is invalid.' unless secp256k1_keypair_create(context, keypair, secret) == 1 keypair.read_string(96).bth end end |
.ellswift_create(priv_key) ⇒ String
Compute an ElligatorSwift public key for a secret key.
249 250 251 252 253 254 255 256 257 |
# File 'lib/bitcoin/secp256k1/native.rb', line 249 def ellswift_create(priv_key) with_context(flags: SECP256K1_CONTEXT_SIGN) do |context| ell64 = FFI::MemoryPointer.new(:uchar, 64) seckey32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, priv_key.htb) result = secp256k1_ellswift_create(context, ell64, seckey32, nil) raise ArgumentError, 'Failed to create ElligatorSwift public key.' unless result == 1 ell64.read_string(64).bth end end |
.ellswift_decode(ell_key) ⇒ String
Decode ellswift public key.
236 237 238 239 240 241 242 243 244 |
# File 'lib/bitcoin/secp256k1/native.rb', line 236 def ellswift_decode(ell_key) with_context do |context| ell64 = FFI::MemoryPointer.new(:uchar, ell_key.bytesize).put_bytes(0, ell_key) internal = FFI::MemoryPointer.new(:uchar, 64) result = secp256k1_ellswift_decode(context, internal, ell64) raise ArgumentError, 'Decode failed.' unless result == 1 serialize_pubkey_internal(context, internal, true) end end |
.ellswift_ecdh_xonly(their_ell_pubkey, our_ell_pubkey, priv_key, initiating) ⇒ String
Compute X coordinate of shared ECDH point between elswift pubkey and privkey.
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/bitcoin/secp256k1/native.rb', line 265 def ellswift_ecdh_xonly(their_ell_pubkey, our_ell_pubkey, priv_key, initiating) with_context(flags: SECP256K1_CONTEXT_SIGN) do |context| output = FFI::MemoryPointer.new(:uchar, 32) our_ell_ptr = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, our_ell_pubkey.key) their_ell_ptr = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, their_ell_pubkey.key) seckey32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, priv_key.htb) hashfp = secp256k1_ellswift_xdh_hash_function_bip324 result = secp256k1_ellswift_xdh(context, output, initiating ? our_ell_ptr : their_ell_ptr, initiating ? their_ell_ptr : our_ell_ptr, seckey32, initiating ? 0 : 1, hashfp, nil) raise ArgumentError, "secret was invalid or hashfp returned 0" unless result == 1 output.read_string(32).bth end end |
.generate_key(compressed: true) ⇒ Object
generate bitcoin key object
101 102 103 104 |
# File 'lib/bitcoin/secp256k1/native.rb', line 101 def generate_key(compressed: true) privkey, pubkey = generate_key_pair(compressed: compressed) Bitcoin::Key.new(priv_key: privkey, pubkey: pubkey, compressed: compressed) end |
.generate_key_pair(compressed: true) ⇒ Object
generate ec private key and public key
86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/bitcoin/secp256k1/native.rb', line 86 def generate_key_pair(compressed: true) with_context do |context| ret, tries, max = 0, 0, 20 while ret != 1 raise 'secp256k1_ec_seckey_verify in generate_key_pair failed.' if tries >= max tries += 1 priv_key = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, SecureRandom.random_bytes(32)) ret = secp256k1_ec_seckey_verify(context, priv_key) end private_key = priv_key.read_string(32).bth [private_key , generate_pubkey_in_context(context, private_key, compressed: compressed) ] end end |
.generate_pubkey(priv_key, compressed: true) ⇒ Object
106 107 108 109 110 |
# File 'lib/bitcoin/secp256k1/native.rb', line 106 def generate_pubkey(priv_key, compressed: true) with_context do |context| generate_pubkey_in_context(context, priv_key, compressed: compressed) end end |
.init ⇒ Object
34 35 36 37 38 |
# File 'lib/bitcoin/secp256k1/native.rb', line 34 def init raise 'secp256k1 library dose not found.' unless File.exist?(ENV['SECP256K1_LIB_PATH']) ffi_lib(ENV['SECP256K1_LIB_PATH']) load_functions end |
.load_functions ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/bitcoin/secp256k1/native.rb', line 40 def load_functions attach_function(:secp256k1_context_create, [:uint], :pointer) attach_function(:secp256k1_context_destroy, [:pointer], :void) attach_function(:secp256k1_context_randomize, [:pointer, :pointer], :int) attach_function(:secp256k1_ec_pubkey_create, [:pointer, :pointer, :pointer], :int) attach_function(:secp256k1_ec_seckey_verify, [:pointer, :pointer], :int) attach_function(:secp256k1_ecdsa_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int) attach_function(:secp256k1_ec_pubkey_serialize, [:pointer, :pointer, :pointer, :pointer, :uint], :int) attach_function(:secp256k1_ecdsa_signature_serialize_der, [:pointer, :pointer, :pointer, :pointer], :int) attach_function(:secp256k1_ec_pubkey_parse, [:pointer, :pointer, :pointer, :size_t], :int) attach_function(:secp256k1_ecdsa_signature_parse_der, [:pointer, :pointer, :pointer, :size_t], :int) attach_function(:secp256k1_ecdsa_signature_normalize, [:pointer, :pointer, :pointer], :int) attach_function(:secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :pointer], :int) attach_function(:secp256k1_schnorrsig_sign32, [:pointer, :pointer, :pointer, :pointer, :pointer], :int) attach_function(:secp256k1_schnorrsig_verify, [:pointer, :pointer, :pointer, :size_t, :pointer], :int) attach_function(:secp256k1_keypair_create, [:pointer, :pointer, :pointer], :int) attach_function(:secp256k1_xonly_pubkey_parse, [:pointer, :pointer, :pointer], :int) attach_function(:secp256k1_ecdsa_sign_recoverable, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int) attach_function(:secp256k1_ecdsa_recoverable_signature_serialize_compact, [:pointer, :pointer, :pointer, :pointer], :int) attach_function(:secp256k1_ecdsa_recover, [:pointer, :pointer, :pointer, :pointer], :int) attach_function(:secp256k1_ecdsa_recoverable_signature_parse_compact, [:pointer, :pointer, :pointer, :int], :int) attach_function(:secp256k1_ellswift_decode, [:pointer, :pointer, :pointer], :int) attach_function(:secp256k1_ellswift_create, [:pointer, :pointer, :pointer, :pointer], :int) # Define function pointer callback(:secp256k1_ellswift_xdh_hash_function, [:pointer, :pointer, :pointer, :pointer, :pointer], :int) attach_variable(:secp256k1_ellswift_xdh_hash_function_bip324, :secp256k1_ellswift_xdh_hash_function) attach_function(:secp256k1_ellswift_xdh, [:pointer, :pointer, :pointer, :pointer, :pointer, :int, :pointer, :pointer], :int) end |
.parse_ec_pubkey?(pub_key, allow_hybrid = false) ⇒ Boolean
# validate whether this is a valid public key (more expensive than IsValid())
196 197 198 199 200 201 202 203 204 205 |
# File 'lib/bitcoin/secp256k1/native.rb', line 196 def parse_ec_pubkey?(pub_key, allow_hybrid = false) pub_key = pub_key.htb return false if !allow_hybrid && ![0x02, 0x03, 0x04].include?(pub_key[0].ord) with_context do |context| pubkey = FFI::MemoryPointer.new(:uchar, pub_key.bytesize).put_bytes(0, pub_key) internal_pubkey = FFI::MemoryPointer.new(:uchar, 64) result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pub_key.bytesize) result == 1 end end |
.recover_compact(data, signature, rec, compressed) ⇒ Bitcoin::Key
Recover public key from compact signature.
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/bitcoin/secp256k1/native.rb', line 158 def recover_compact(data, signature, rec, compressed) with_context do |context| sig = FFI::MemoryPointer.new(:uchar, 65) input = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, signature[1..-1]) result = secp256k1_ecdsa_recoverable_signature_parse_compact(context, sig, input, rec) raise 'secp256k1_ecdsa_recoverable_signature_parse_compact failed.' unless result == 1 pubkey = FFI::MemoryPointer.new(:uchar, 64) msg = FFI::MemoryPointer.new(:uchar, data.bytesize).put_bytes(0, data) result = secp256k1_ecdsa_recover(context, pubkey, sig, msg) raise 'secp256k1_ecdsa_recover failed.' unless result == 1 pubkey = serialize_pubkey_internal(context, pubkey.read_string(64), compressed) Bitcoin::Key.new(pubkey: pubkey, compressed: compressed) end end |
.sign_compact(data, privkey) ⇒ Array[signature, recovery id]
Sign data with compact format.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/bitcoin/secp256k1/native.rb', line 133 def sign_compact(data, privkey) with_context do |context| sig = FFI::MemoryPointer.new(:uchar, 65) hash =FFI::MemoryPointer.new(:uchar, data.bytesize).put_bytes(0, data) priv_key = privkey.htb sec_key = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key) result = secp256k1_ecdsa_sign_recoverable(context, sig, hash, sec_key, nil, nil) raise 'secp256k1_ecdsa_sign_recoverable failed.' if result == 0 output = FFI::MemoryPointer.new(:uchar, 64) rec = FFI::MemoryPointer.new(:uint64) result = secp256k1_ecdsa_recoverable_signature_serialize_compact(context, output, rec, sig) raise 'secp256k1_ecdsa_recoverable_signature_serialize_compact failed.' unless result == 1 raw_sig = output.read_string(64) [ECDSA::Signature.new(raw_sig[0...32].bti, raw_sig[32..-1].bti), rec.read(:int)] end end |
.sign_data(data, privkey, extra_entropy = nil, algo: :ecdsa) ⇒ String
sign data.
118 119 120 121 122 123 124 125 126 127 |
# File 'lib/bitcoin/secp256k1/native.rb', line 118 def sign_data(data, privkey, extra_entropy = nil, algo: :ecdsa) case algo when :ecdsa sign_ecdsa(data, privkey, extra_entropy) when :schnorr sign_schnorr(data, privkey, extra_entropy) else nil end end |
.valid_xonly_pubkey?(pub_key) ⇒ Boolean
Check whether valid x-only public key or not.
224 225 226 227 228 229 230 231 |
# File 'lib/bitcoin/secp256k1/native.rb', line 224 def valid_xonly_pubkey?(pub_key) begin full_pubkey_from_xonly_pubkey(pub_key) rescue Exception return false end true end |
.verify_sig(data, sig, pubkey, algo: :ecdsa) ⇒ Boolean
verify signature # @param [Symbol] algo signature algorithm. ecdsa(default) or schnorr.
181 182 183 184 185 186 187 188 189 190 |
# File 'lib/bitcoin/secp256k1/native.rb', line 181 def verify_sig(data, sig, pubkey, algo: :ecdsa) case algo when :ecdsa verify_ecdsa(data, sig, pubkey) when :schnorr verify_schnorr(data, sig, pubkey) else false end end |
.with_context(flags: (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN)) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/bitcoin/secp256k1/native.rb', line 69 def with_context(flags: (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN)) init begin context = secp256k1_context_create(flags) ret, tries, max = 0, 0, 20 while ret != 1 raise 'secp256k1_context_randomize failed.' if tries >= max tries += 1 ret = secp256k1_context_randomize(context, FFI::MemoryPointer.from_string(SecureRandom.random_bytes(32))) end yield(context) if block_given? ensure secp256k1_context_destroy(context) end end |