Post
Topic
Board Development & Technical Discussion
Re: Transaction verification in Python (with M2Crypto)
by
Martin P. Hellwig
on 28/06/2011, 21:24:49 UTC
Hi,

Thanks for the snippet it is greatly appreciated!

My biggest problem was with M2Crypto: it doesn't seem to implement o2i_ECPublicKey from openssl, which is used by bitcoin to set the public key. I also looked at using pyOpenssl and pycrypto, but it looks like neither of these libraries can handle elliptic curve cryptography...

To make M2Crypto work for this I had to use some magic, which I'm really not happy about:
Code:
pkey = pubkey[::-1] + "0042030a0004812b050601023dce48862a070610305630".decode("hex")
pkey = M2Crypto.EC.pub_key_from_der(pkey[::-1])
As you can see, I need to add a fixed string in order to load the public key. Without this it fails to load it and I get an exception (ValueError: Received a NULL pointer.)
This string I got by making new public/private key pairs with M2Crypto and printing them out: I noticed that this part is fixed and never changes. I assume this string defines the parameters used (NID_secp256k)

Yes, that is correct, as you already figured out bitcoin only sends the xy position of the curve, however for openssl also needs to know which curve is used and of course what type of public key it is, since this is all DER encoded I opted for recreating the DER package. As you can see I used the same key to check if it works. Although in my code I only have a dependency on openssl directly (via subprocess) and thus do away with m2crypto or any other crypto wrapper, I do have a new dependency on pyasn1 http://sourceforge.net/projects/pyasn1/ though.

The relevant part of my code:
Code:
from pyasn1.codec.der import encoder
from pyasn1.type.univ import Sequence, ObjectIdentifier, BitString

# http://www.oid-info.com/get/1.2.840.10045.2.1
OID_EC_PUBLIC_KEY = "1.2.840.10045.2.1"
# http://www.oid-info.com/get/1.3.132.0.10
OID_SECP256K1 = "1.3.132.0.10"

def ec_public_key_in_der(xy_curve):
    "Create the DER public key part using the XY curve values"
    oid = Sequence()
    oid.setComponentByPosition(0, ObjectIdentifier(OID_EC_PUBLIC_KEY))
    oid.setComponentByPosition(1, ObjectIdentifier(OID_SECP256K1))
    
    xyc = BitString("'%s'H" % xy_curve.encode('hex'))
    
    tmp = Sequence()
    tmp.setComponentByPosition(0, oid)
    tmp.setComponentByPosition(1, xyc)
    return(encoder.encode(tmp))

if __name__ == '__main__':
    XY_CURVE = "0447d490561f396c8a9efc14486bc198884ba18379bcac2e0be2d8525134" +\
    "ab742f301a9aca36606e5d29aa238a9e2993003150423df6924563642d4afe9bf4fe28"
    XY_CURVE = XY_CURVE.decode('hex')
    
    PUBLIC_KEY = "3056301006072a8648ce3d020106052b8104000a0342000447d490561f" +\
    "396c8a9efc14486bc198884ba18379bcac2e0be2d8525134ab742f301a9aca36606e5d2" +\
    "9aa238a9e2993003150423df6924563642d4afe9bf4fe28"
    PUBLIC_KEY = PUBLIC_KEY.decode('hex')

    if ec_public_key_in_der(XY_CURVE) == PUBLIC_KEY:
        print(True)
    else:
        print(False)