Module: Tapyrus::Secp256k1::Native
- Extended by:
- Native
- Includes:
- FFI::Library
- Included in:
- Native
- Defined in:
- lib/tapyrus/secp256k1/native.rb
Overview
binding for secp256k1 (https://github.com/chaintope/tapyrus-core/tree/v0.4.0/src/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
-
.generate_key(compressed: true) ⇒ Object
generate tapyrus key object.
-
.generate_key_pair(compressed: true) ⇒ Object
generate ec private key and public key.
- .generate_pubkey(priv_key, compressed: true) ⇒ Object
- .generate_pubkey_in_context(context, privkey, 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()).
-
.sign_data(data, privkey, extra_entropy, algo: :ecdsa) ⇒ String
sign data.
- .sign_ecdsa(data, privkey, extra_entropy) ⇒ Object
- .sign_schnorr(data, privkey) ⇒ Object
- .verify_ecdsa(data, sig, pub_key) ⇒ Object
- .verify_schnorr(data, sig, pub_key) ⇒ Object
-
.verify_sig(data, sig, pub_key, algo: :ecdsa) ⇒ Object
verify signature.
- .with_context(flags: (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN)) ⇒ Object
Class Method Details
.generate_key(compressed: true) ⇒ Object
generate tapyrus key object
88 89 90 91 |
# File 'lib/tapyrus/secp256k1/native.rb', line 88 def generate_key(compressed: true) privkey, pubkey = generate_key_pair(compressed: compressed) Tapyrus::Key.new(priv_key: privkey, pubkey: pubkey, compressed: compressed) end |
.generate_key_pair(compressed: true) ⇒ Object
generate ec private key and public key
73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/tapyrus/secp256k1/native.rb', line 73 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
93 94 95 |
# File 'lib/tapyrus/secp256k1/native.rb', line 93 def generate_pubkey(priv_key, compressed: true) with_context { |context| generate_pubkey_in_context(context, priv_key, compressed: compressed) } end |
.generate_pubkey_in_context(context, privkey, compressed: true) ⇒ Object
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/tapyrus/secp256k1/native.rb', line 145 def generate_pubkey_in_context(context, privkey, compressed: true) internal_pubkey = FFI::MemoryPointer.new(:uchar, 64) result = secp256k1_ec_pubkey_create(context, internal_pubkey, privkey.htb) raise "error creating pubkey" unless result pubkey = FFI::MemoryPointer.new(:uchar, 65) pubkey_len = FFI::MemoryPointer.new(:uint64) result = if compressed pubkey_len.put_uint64(0, 33) secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_COMPRESSED) else pubkey_len.put_uint64(0, 65) secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_UNCOMPRESSED) end raise "error serialize pubkey" unless result || pubkey_len.read_uint64 > 0 pubkey.read_string(pubkey_len.read_uint64).bth end |
.init ⇒ Object
33 34 35 36 37 |
# File 'lib/tapyrus/secp256k1/native.rb', line 33 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
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/tapyrus/secp256k1/native.rb', line 39 def load_functions attach_function(:secp256k1_context_create, [:uint], :pointer) attach_function(:secp256k1_context_destroy, [:pointer], :void) attach_function(:secp256k1_context_randomize, %i[pointer pointer], :int) attach_function(:secp256k1_ec_pubkey_create, %i[pointer pointer pointer], :int) attach_function(:secp256k1_ec_seckey_verify, %i[pointer pointer], :int) attach_function(:secp256k1_ecdsa_sign, %i[pointer pointer pointer pointer pointer pointer], :int) attach_function(:secp256k1_ec_pubkey_serialize, %i[pointer pointer pointer pointer uint], :int) attach_function(:secp256k1_ecdsa_signature_serialize_der, %i[pointer pointer pointer pointer], :int) attach_function(:secp256k1_ec_pubkey_parse, %i[pointer pointer pointer size_t], :int) attach_function(:secp256k1_ecdsa_signature_parse_der, %i[pointer pointer pointer size_t], :int) attach_function(:secp256k1_ecdsa_signature_normalize, %i[pointer pointer pointer], :int) attach_function(:secp256k1_ecdsa_verify, %i[pointer pointer pointer pointer], :int) attach_function(:secp256k1_schnorr_sign, %i[pointer pointer pointer pointer pointer pointer], :int) attach_function(:secp256k1_schnorr_verify, %i[pointer pointer 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())
132 133 134 135 136 137 138 139 140 141 |
# File 'lib/tapyrus/secp256k1/native.rb', line 132 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 |
.sign_data(data, privkey, extra_entropy, algo: :ecdsa) ⇒ String
sign data.
102 103 104 105 106 107 108 109 110 111 |
# File 'lib/tapyrus/secp256k1/native.rb', line 102 def sign_data(data, privkey, extra_entropy, algo: :ecdsa) case algo when :ecdsa sign_ecdsa(data, privkey, extra_entropy) when :schnorr sign_schnorr(data, privkey) else nil end end |
.sign_ecdsa(data, privkey, extra_entropy) ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/tapyrus/secp256k1/native.rb', line 164 def sign_ecdsa(data, privkey, extra_entropy) with_context do |context| secret = FFI::MemoryPointer.new(:uchar, privkey.htb.bytesize).put_bytes(0, privkey.htb) raise "priv_key invalid" unless secp256k1_ec_seckey_verify(context, secret) internal_signature = FFI::MemoryPointer.new(:uchar, 64) msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data) entropy = extra_entropy ? FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, extra_entropy) : nil ret, tries, max = 0, 0, 20 while ret != 1 raise "secp256k1_ecdsa_sign failed." if tries >= max tries += 1 ret = secp256k1_ecdsa_sign(context, internal_signature, msg32, secret, nil, entropy) end signature = FFI::MemoryPointer.new(:uchar, 72) signature_len = FFI::MemoryPointer.new(:uint64).put_uint64(0, 72) result = secp256k1_ecdsa_signature_serialize_der(context, signature, signature_len, internal_signature) raise "secp256k1_ecdsa_signature_serialize_der failed" unless result signature.read_string(signature_len.read_uint64) end end |
.sign_schnorr(data, privkey) ⇒ Object
190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/tapyrus/secp256k1/native.rb', line 190 def sign_schnorr(data, privkey) with_context do |context| secret = FFI::MemoryPointer.new(:uchar, privkey.htb.bytesize).put_bytes(0, privkey.htb) raise "priv_key invalid" unless secp256k1_ec_seckey_verify(context, secret) signature = FFI::MemoryPointer.new(:uchar, 64) msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data) unless secp256k1_schnorr_sign(context, signature, msg32, secret, nil, nil) == 1 raise "Failed to generate schnorr signature." end signature.read_string(64) end end |
.verify_ecdsa(data, sig, pub_key) ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/tapyrus/secp256k1/native.rb', line 204 def verify_ecdsa(data, sig, pub_key) with_context do |context| return false if data.bytesize == 0 pubkey = FFI::MemoryPointer.new(:uchar, pub_key.htb.bytesize).put_bytes(0, pub_key.htb) internal_pubkey = FFI::MemoryPointer.new(:uchar, 64) result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pubkey.size) return false unless result signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig) internal_signature = FFI::MemoryPointer.new(:uchar, 64) result = secp256k1_ecdsa_signature_parse_der(context, internal_signature, signature, signature.size) return false unless result # libsecp256k1's ECDSA verification requires lower-S signatures, which have not historically been enforced in Bitcoin, so normalize them first. secp256k1_ecdsa_signature_normalize(context, internal_signature, internal_signature) msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data) result = secp256k1_ecdsa_verify(context, internal_signature, msg32, internal_pubkey) result == 1 end end |
.verify_schnorr(data, sig, pub_key) ⇒ Object
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/tapyrus/secp256k1/native.rb', line 228 def verify_schnorr(data, sig, pub_key) with_context do |context| return false if data.bytesize == 0 pubkey = FFI::MemoryPointer.new(:uchar, pub_key.htb.bytesize).put_bytes(0, pub_key.htb) internal_pubkey = FFI::MemoryPointer.new(:uchar, 64) result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pubkey.size) return false unless result signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig) msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data) result = secp256k1_schnorr_verify(context, signature, msg32, internal_pubkey) result == 1 end end |
.verify_sig(data, sig, pub_key, algo: :ecdsa) ⇒ Object
verify signature. @param data a data.
117 118 119 120 121 122 123 124 125 126 |
# File 'lib/tapyrus/secp256k1/native.rb', line 117 def verify_sig(data, sig, pub_key, algo: :ecdsa) case algo when :ecdsa verify_ecdsa(data, sig, pub_key) when :schnorr verify_schnorr(data, sig, pub_key) else false end end |
.with_context(flags: (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN)) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/tapyrus/secp256k1/native.rb', line 56 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 |