lib/tapyrus/secp256k1/native.rb in tapyrus-0.2.2 vs lib/tapyrus/secp256k1/native.rb in tapyrus-0.2.3

- old
+ new

@@ -48,10 +48,12 @@ 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_schnorr_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int) + attach_function(:secp256k1_schnorr_verify, [:pointer, :pointer, :pointer, :pointer], :int) end def with_context(flags: (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN)) init begin @@ -98,12 +100,73 @@ # sign data. # @param [String] data a data to be signed with binary format # @param [String] privkey a private key using sign # @param [String] extra_entropy a extra entropy for rfc6979 # @return [String] signature data with binary format - def sign_data(data, privkey, extra_entropy) + 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 + + # verify signature. + # @param[String] data a data. + # @param [String] sig signature data with binary format + # @param [String] pub_key a public key using verify. + 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 + + # # validate whether this is a valid public key (more expensive than IsValid()) + # @param [String] pub_key public key with hex format. + # @param [Boolean] allow_hybrid whether support hybrid public key. + # @return [Boolean] If valid public key return true, otherwise false. + 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 + + private + + 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 + + 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) @@ -124,12 +187,24 @@ signature.read_string(signature_len.read_uint64) end end - def verify_sig(data, sig, pub_key) + 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) + raise 'Failed to generate schnorr signature.' unless secp256k1_schnorr_sign(context, signature, msg32, secret, nil, nil) == 1 + signature.read_string + end + end + + 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) @@ -148,27 +223,25 @@ result == 1 end end - private + 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 - 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 + signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig) - 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 + msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data) + result = secp256k1_schnorr_verify(context, signature, msg32, internal_pubkey) + + result == 1 + end end + end end end