I am trying to write Ruby code to check the Elliptic Curve Digital Signature Algorithm (ECDSA) signature on a particular message that I found here.
The problem is that I don't know how to convert the octet string for the public key into an OpenSSL::PKey::EC::Point object. If I were writing this in C, I would just pass the octet string to OpenSSL's o2i_ECPublicKey
, which does something close to what I would want and in fact is used by the reference implementation. However, I searched the source code of Ruby (MRI) and it contains no calls to o2i_ECPublicKey
so I don't know how I would use that function from Ruby without writing a C extension.
Here is the octet string, in hex. It is just a 0x04 byte followed by two 32-byte integers that represent the x and y coordinates of the point on the elliptic curve:
04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284
So does anyone know how to convert that string into an in OpenSSL::PKey::EC::Point
in Ruby? Once I get the point object, I will use it in the following code which I believe will verify the signature:
key = OpenSSL::PKey::EC.new('secp256k1')
key.public_key = point
result = key.dsa_verify_asn1(digest, signature)
UPDATE:
Thanks to Jay-Ar Polidario I got it to work. Here is the full code I have that verifies the signature using OpenSSL. I also wrote a gem called ecdsa and I included code showing how to use my gem to do the same thing.
# coding: ASCII-8BIT
digest =
"\xbf\x91\xfb\x0b\x4f\x63\x33\x77\x4a\x02\x2b\xd3\x07\x8e\xd6\xcc" \
"\xd1\x76\xee\x31\xed\x4f\xb3\xf9\xaf\xce\xb7\x2a\x37\xe7\x87\x86"
signature_der_string =
"\x30\x45" \
"\x02\x21\x00" \
"\x83\x89\xdf\x45\xf0\x70\x3f\x39\xec\x8c\x1c\xc4\x2c\x13\x81\x0f" \
"\xfc\xae\x14\x99\x5b\xb6\x48\x34\x02\x19\xe3\x53\xb6\x3b\x53\xeb" \
"\x02\x20" \
"\x09\xec\x65\xe1\xc1\xaa\xee\xc1\xfd\x33\x4c\x6b\x68\x4b\xde\x2b" \
"\x3f\x57\x30\x60\xd5\xb7\x0c\x3a\x46\x72\x33\x26\xe4\xe8\xa4\xf1"
public_key_octet_string =
"\x04" \
"\xfc\x97\x02\x84\x78\x40\xaa\xf1\x95\xde\x84\x42\xeb\xec\xed\xf5" \
"\xb0\x95\xcd\xbb\x9b\xc7\x16\xbd\xa9\x11\x09\x71\xb2\x8a\x49\xe0" \
"\xea\xd8\x56\x4f\xf0\xdb\x22\x20\x9e\x03\x74\x78\x2c\x09\x3b\xb8" \
"\x99\x69\x2d\x52\x4e\x9d\x6a\x69\x56\xe7\xc5\xec\xbc\xd6\x82\x84"
# Verifying with openssl.
require 'openssl'
ec = OpenSSL::PKey::EC.new('secp256k1')
key_bn = OpenSSL::BN.new(public_key_octet_string, 2) # 2 means binary
ec.public_key = OpenSSL::PKey::EC::Point.new(ec.group, key_bn)
result = ec.dsa_verify_asn1(digest, signature_der_string)
puts result # => true
# Verifying with the new ECDSA gem I wrote, version 0.1.5
require 'ecdsa'
group = ECDSA::Group::Secp256k1
point = ECDSA::Format::PointOctetString.decode(public_key_octet_string, group)
signature = ECDSA::Format::SignatureDerString.decode(signature_der_string)
result = ECDSA.valid_signature?(point, digest, signature)
puts result # => true
I think it's weird that OpenSSL makes you represent the public key temporarily as a single BN (big number), because it is actually two big numbers. My gem can directly convert octet strings (as defined in the SEC2 standard) into ECDSA::Point
objects.
Try the following (Tested without errors):
key = '04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284'
key_bn = OpenSSL::BN.new(key, 16) #Input: 16=Hexa, Output: BigNumber
group = OpenSSL::PKey::EC::Group.new('secp256k1')
point = OpenSSL::PKey::EC::Point.new(group, key_bn)
#--> <OpenSSL::PKey::EC::Point:0x5288178>
OpenSSL allows you to create a point with a group and a bignum, so I tried this:
require 'openssl'
include OpenSSL
group = PKey::EC::Group.new('secp256k1')
bignum = BN.new('04fc9702847840...')
point = PKey::EC::Point.new(group, bignum)
But it raises OpenSSL::PKey::EC::Point::Error: invalid encoding
. I'm not quite sure how to troubleshoot it from here, but hopefully this helps you out a little bit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With