lib/bitcoin/ffi/secp256k1.rb in bitcoin-ruby-0.0.7 vs lib/bitcoin/ffi/secp256k1.rb in bitcoin-ruby-0.0.8

- old
+ new

@@ -1,9 +1,10 @@ # encoding: ascii-8bit -# Wraps libsecp256k1 (https://github.com/bitcoin/secp256k1) -# commit: 50cc6ab0625efda6dddf1dc86c1e2671f069b0d8 +# bindings for secp256k1 inside bitcoin (https://github.com/bitcoin/bitcoin/tree/v0.11.0/src/secp256k1) +# tag: v0.11.0 +# commit: d26f951802c762de04fb68e1a112d611929920ba require 'ffi' module Bitcoin module Secp256k1 @@ -14,131 +15,192 @@ def self.ffi_load_functions(file) class_eval <<-RUBY ffi_lib [ %[#{file}] ] - attach_function :secp256k1_start, [:int], :void - attach_function :secp256k1_stop, [], :void - attach_function :secp256k1_ec_seckey_verify, [:pointer], :int - attach_function :secp256k1_ec_pubkey_verify, [:pointer, :int], :int - attach_function :secp256k1_ec_pubkey_create, [:pointer, :pointer, :pointer, :int], :int + ## + # source: https://github.com/bitcoin/bitcoin/blob/v0.11.0/src/secp256k1/include/secp256k1.h + ## - # int secp256k1_ecdsa_sign(const unsigned char *msg32, unsigned char *sig, int *siglen, const unsigned char *seckey, secp256k1_nonce_function_t noncefp, const void *ndata) - attach_function :secp256k1_ecdsa_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int + # secp256k1_context_t* secp256k1_context_create(int flags) + attach_function :secp256k1_context_create, [:int], :pointer - # int secp256k1_ecdsa_verify(const unsigned char *msg32, const unsigned char *sig, int siglen, const unsigned char *pubkey, int pubkeylen) - attach_function :secp256k1_ecdsa_verify, [:pointer, :pointer, :int, :pointer, :int], :int + # secp256k1_context_t* secp256k1_context_clone(const secp256k1_context_t* ctx) + attach_function :secp256k1_context_clone, [:pointer], :pointer - # int secp256k1_ecdsa_sign_compact(const unsigned char *msg32, unsigned char *sig64, const unsigned char *seckey, secp256k1_nonce_function_t noncefp, const void *ndata, int *recid) - attach_function :secp256k1_ecdsa_sign_compact, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int + # void secp256k1_context_destroy(secp256k1_context_t* ctx) + attach_function :secp256k1_context_destroy, [:pointer], :void - # int secp256k1_ecdsa_recover_compact(const unsigned char *msg32, const unsigned char *sig64, unsigned char *pubkey, int *pubkeylen, int compressed, int recid) - attach_function :secp256k1_ecdsa_recover_compact, [:pointer, :pointer, :pointer, :pointer, :int, :int], :int + # int secp256k1_ecdsa_verify(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig, int siglen, const unsigned char *pubkey, int pubkeylen) + attach_function :secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :int, :pointer, :int], :int + + # int secp256k1_ecdsa_sign(const secp256k1_context_t* ctx, const unsigned char *msg32, unsigned char *sig, int *siglen, const unsigned char *seckey, secp256k1_nonce_function_t noncefp, const void *ndata) + attach_function :secp256k1_ecdsa_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int + + # int secp256k1_ecdsa_sign_compact(const secp256k1_context_t* ctx, const unsigned char *msg32, unsigned char *sig64, const unsigned char *seckey, secp256k1_nonce_function_t noncefp, const void *ndata, int *recid) + attach_function :secp256k1_ecdsa_sign_compact, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int + + # int secp256k1_ecdsa_recover_compact(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig64, unsigned char *pubkey, int *pubkeylen, int compressed, int recid) + attach_function :secp256k1_ecdsa_recover_compact, [:pointer, :pointer, :pointer, :pointer, :pointer, :int, :int], :int + + # int secp256k1_ec_seckey_verify(const secp256k1_context_t* ctx, const unsigned char *seckey) + attach_function :secp256k1_ec_seckey_verify, [:pointer, :pointer], :int + + # int secp256k1_ec_pubkey_verify(const secp256k1_context_t* ctx, const unsigned char *pubkey, int pubkeylen) + attach_function :secp256k1_ec_pubkey_verify, [:pointer, :pointer, :int], :int + + # int secp256k1_ec_pubkey_create(const secp256k1_context_t* ctx, unsigned char *pubkey, int *pubkeylen, const unsigned char *seckey, int compressed) + attach_function :secp256k1_ec_pubkey_create, [:pointer, :pointer, :pointer, :pointer, :int], :int + + # int secp256k1_ec_pubkey_decompress(const secp256k1_context_t* ctx, unsigned char *pubkey, int *pubkeylen) + attach_function :secp256k1_ec_pubkey_decompress, [:pointer, :pointer, :pointer], :int + + # int secp256k1_ec_privkey_export(const secp256k1_context_t* ctx, const unsigned char *seckey, unsigned char *privkey, int *privkeylen, int compressed) + attach_function :secp256k1_ec_privkey_export, [:pointer, :pointer, :pointer, :pointer, :int], :int + + # int secp256k1_ec_privkey_import(const secp256k1_context_t* ctx, unsigned char *seckey, const unsigned char *privkey, int privkeylen) + attach_function :secp256k1_ec_privkey_import, [:pointer, :pointer, :pointer, :pointer], :int + + # int secp256k1_ec_privkey_tweak_add(const secp256k1_context_t* ctx, unsigned char *seckey, const unsigned char *tweak) + attach_function :secp256k1_ec_privkey_tweak_add, [:pointer, :pointer, :pointer], :int + + # int secp256k1_ec_pubkey_tweak_add(const secp256k1_context_t* ctx, unsigned char *pubkey, int pubkeylen, const unsigned char *tweak) + attach_function :secp256k1_ec_pubkey_tweak_add, [:pointer, :pointer, :int, :pointer], :int + + # int secp256k1_ec_privkey_tweak_mul(const secp256k1_context_t* ctx, unsigned char *seckey, const unsigned char *tweak) + attach_function :secp256k1_ec_privkey_tweak_mul, [:pointer, :pointer, :pointer], :int + + # int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context_t* ctx, unsigned char *pubkey, int pubkeylen, const unsigned char *tweak) + attach_function :secp256k1_ec_pubkey_tweak_mul, [:pointer, :pointer, :int, :pointer], :int + + # int secp256k1_context_randomize(secp256k1_context_t* ctx, const unsigned char *seed32) + attach_function :secp256k1_context_randomize, [:pointer, :pointer], :int RUBY end def self.init - return if @secp256k1_started - - lib_path = [ ENV['SECP256K1_LIB_PATH'], 'vendor/secp256k1/.libs/libsecp256k1.so' ].find{|f| File.exists?(f.to_s) } - lib_path = 'libsecp256k1.so' unless lib_path + return if @loaded + lib_path = [ ENV['SECP256K1_LIB_PATH'], 'vendor/bitcoin/src/secp256k1/.libs/libsecp256k1.so' ].find{|f| File.exists?(f.to_s) } ffi_load_functions(lib_path) - - secp256k1_start(SECP256K1_START_VERIFY | SECP256K1_START_SIGN) - @secp256k1_started = true + @loaded = true end - def self.generate_key_pair(compressed=true) + def self.with_context(flags=nil, seed=nil) init + flags = flags || (SECP256K1_START_VERIFY | SECP256K1_START_SIGN ) + context = secp256k1_context_create(flags) - while true do - priv_key = SecureRandom.random_bytes(32) - priv_key_buf = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, priv_key) - break if secp256k1_ec_seckey_verify(priv_key_buf) + 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(seed || SecureRandom.random_bytes(32))) end - pub_key_buf = FFI::MemoryPointer.new(:uchar, 65) - pub_key_size = FFI::MemoryPointer.new(:int) - result = secp256k1_ec_pubkey_create(pub_key_buf, pub_key_size, priv_key_buf, compressed ? 1 : 0) - raise "error creating pubkey" unless result + yield(context) if block_given? + ensure + secp256k1_context_destroy(context) + end - [ priv_key, pub_key_buf.read_string(pub_key_size.read_int) ] + def self.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 + + pub_key, pub_key_length = FFI::MemoryPointer.new(:uchar, 65), FFI::MemoryPointer.new(:int) + result = secp256k1_ec_pubkey_create(context, pub_key, pub_key_length, priv_key, compressed ? 1 : 0) + raise "error creating pubkey" unless result + + [ priv_key.read_string(32), pub_key.read_string(pub_key_length.read_int) ] + end end + def self.generate_key(compressed=true) + priv, pub = generate_key_pair(compressed) + Bitcoin::Key.new(priv.unpack("H*")[0], pub.unpack("H*")[0]) + end + def self.sign(data, priv_key) - init + with_context do |context| + msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data) + seckey = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key) + raise "priv_key invalid" unless secp256k1_ec_seckey_verify(context, seckey) - msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data) - seckey = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key) - raise "priv_key invalid" unless secp256k1_ec_seckey_verify(seckey) + sig, siglen = FFI::MemoryPointer.new(:uchar, 72), FFI::MemoryPointer.new(:int).write_int(72) - sig, siglen = FFI::MemoryPointer.new(:uchar, 72), FFI::MemoryPointer.new(:int).write_int(72) + while true do + break if secp256k1_ecdsa_sign(context, msg32, sig, siglen, seckey, nil, nil) + end - while true do - break if secp256k1_ecdsa_sign(msg32, sig, siglen, seckey, nil, nil) + sig.read_string(siglen.read_int) end - - sig.read_string(siglen.read_int) end def self.verify(data, signature, pub_key) - init + with_context do |context| + data_buf = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data) + sig_buf = FFI::MemoryPointer.new(:uchar, signature.bytesize).put_bytes(0, signature) + pub_buf = FFI::MemoryPointer.new(:uchar, pub_key.bytesize).put_bytes(0, pub_key) - data_buf = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data) - sig_buf = FFI::MemoryPointer.new(:uchar, signature.bytesize).put_bytes(0, signature) - pub_buf = FFI::MemoryPointer.new(:uchar, pub_key.bytesize).put_bytes(0, pub_key) + result = secp256k1_ecdsa_verify(context, data_buf, sig_buf, sig_buf.size, pub_buf, pub_buf.size) - result = secp256k1_ecdsa_verify(data_buf, sig_buf, sig_buf.size, pub_buf, pub_buf.size) - - case result - when 0; false - when 1; true - when -1; raise "error invalid pubkey" - when -2; raise "error invalid signature" - else ; raise "error invalid result" + case result + when 0; false + when 1; true + when -1; raise "error invalid pubkey" + when -2; raise "error invalid signature" + else ; raise "error invalid result" + end end end def self.sign_compact(message, priv_key, compressed=true) - init + with_context do |context| + msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, message) + sig64 = FFI::MemoryPointer.new(:uchar, 64) + rec_id = FFI::MemoryPointer.new(:int) - msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, message) - sig64 = FFI::MemoryPointer.new(:uchar, 64) - rec_id = FFI::MemoryPointer.new(:int) + seckey = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key) + raise "priv_key invalid" unless secp256k1_ec_seckey_verify(context, seckey) - seckey = FFI::MemoryPointer.new(:uchar, priv_key.bytesize).put_bytes(0, priv_key) - raise "priv_key invalid" unless secp256k1_ec_seckey_verify(seckey) + while true do + break if secp256k1_ecdsa_sign_compact(context, msg32, sig64, seckey, nil, nil, rec_id) + end - while true do - break if secp256k1_ecdsa_sign_compact(msg32, sig64, seckey, nil, nil, rec_id) + header = [27 + rec_id.read_int + (compressed ? 4 : 0)].pack("C") + [ header, sig64.read_string(64) ].join end - - header = [27 + rec_id.read_int + (compressed ? 4 : 0)].pack("C") - [ header, sig64.read_string(64) ].join end def self.recover_compact(message, signature) - init + with_context do |context| + return nil if signature.bytesize != 65 - return nil if signature.bytesize != 65 + version = signature.unpack('C')[0] + return nil if version < 27 || version > 34 - version = signature.unpack('C')[0] - return nil if version < 27 || version > 34 + compressed = version >= 31 ? true : false + version -= 4 if compressed - compressed = version >= 31 ? true : false - version -= 4 if compressed + recid = version - 27 + msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, message) + sig64 = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, signature[1..-1]) + pubkey = FFI::MemoryPointer.new(:uchar, pub_key_len = compressed ? 33 : 65) + pubkeylen = FFI::MemoryPointer.new(:int).write_int(pub_key_len) - recid = version - 27 - msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, message) - sig64 = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, signature[1..-1]) - pubkey = FFI::MemoryPointer.new(:uchar, pub_key_len = compressed ? 33 : 65) - pubkeylen = FFI::MemoryPointer.new(:int).write_int(pub_key_len) + result = secp256k1_ecdsa_recover_compact(context, msg32, sig64, pubkey, pubkeylen, (compressed ? 1 : 0), recid) - result = secp256k1_ecdsa_recover_compact(msg32, sig64, pubkey, pubkeylen, (compressed ? 1 : 0), recid) + return nil unless result - return nil unless result - - pubkey.read_bytes(pubkeylen.read_int) + pubkey.read_bytes(pubkeylen.read_int) + end end end end