0

I write this code for computing ecdh key. Use OpenSSL-1_0_2o.

The C code is there(all of include and lib is just OpenSSL only)

void *KDF_MD5(const void *in, size_t inlen, void *out, size_t *outlen)
{
    MD5_CTX ctx;
    MD5_Init(&ctx);
    MD5_Update(&ctx, in, inlen);
    MD5_Final((unsigned char*)out, &ctx);

    *outlen = MD5_DIGEST_LENGTH;

    return out;
}

bool DoEcdh(int nid, unsigned char * szServerPubKey, int nLenServerPub, unsigned char * szLocalPriKey, int nLenLocalPri, unsigned char * szShareKey, int *pLenShareKey)
{
    const unsigned char *public_material = (const unsigned char *)szServerPubKey;
    const unsigned char *private_material = (const unsigned char *)szLocalPriKey;

    EC_KEY *pub_ec_key = EC_KEY_new_by_curve_name(nid);
    if (!pub_ec_key)    return FALSE;
    pub_ec_key = o2i_ECPublicKey(&pub_ec_key, &public_material, nLenServerPub);
    if (!pub_ec_key)    return FALSE;

    EC_KEY *pri_ec_key = EC_KEY_new_by_curve_name(nid);
    if (!pri_ec_key)    return FALSE;
    pri_ec_key = d2i_ECPrivateKey(&pri_ec_key, &private_material, nLenLocalPri);
    if (!pri_ec_key) return FALSE;

    if (MD5_DIGEST_LENGTH != ECDH_compute_key((void *)szShareKey, MD5_DIGEST_LENGTH, EC_KEY_get0_public_key(pub_ec_key), pri_ec_key, KDF_MD5))
    {
        EC_KEY_free(pub_ec_key);
        EC_KEY_free(pri_ec_key);

        return FALSE;
    }

    *pLenShareKey = MD5_DIGEST_LENGTH;

    if (pub_ec_key)
    {
        EC_KEY_free(pub_ec_key);
        pub_ec_key = NULL;
    }

    if (pri_ec_key)
    {
        EC_KEY_free(pri_ec_key);
        pri_ec_key = NULL;
    }

    return TRUE;
}

then, I rewrite in Python ctypes, just use python with libeay32.dll in Windows OR libcrypto.so in Linux. When I call C function ECDH_compute_key raise an OSError.

this function: result = ECDH_compute_key(ShareKey_p, outlen, server_ECpoint, pri_ec, mykdf)

Error: # ------- OSError: exception: access violation reading 0xFFFFFFFFCDC562C0

from ecdhlib.openssl_wrapper import OpenSSL
import ctypes
from ctypes import c_int, c_char_p, pointer
from ctypes import *
from hashlib import md5
class Ecdh(object):
    def __init__(self, curve='secp224r1'):
        self.pubkey = None
        self.prikey = None
        self.shake_hands = None
        self.curveID = OpenSSL.curves[curve]
    def do_ECDHshare(self, server_ECDH_pub, priKey=None):
        OUTLEN = 16
        server_ECDH = server_ECDH_pub
        # EC_KEY *EC_KEY_new_by_curve_name(int nid)
        server_ec = OpenSSL.EC_KEY_new_by_curve_name(self.curveID)
        if not server_ec:
            return None, None
        server_pubLen = len(server_ECDH_pub)
        server_pub = ctypes.cast(server_ECDH_pub, ctypes.POINTER(ctypes.c_char * server_pubLen))
        # EC_KEY *o2i_ECPublicKey(EC_KEY **a, const unsigned char **in, long len)
        server_ec = c_void_p(server_ec)
        server_ec = OpenSSL.o2i_ECPublicKey(ctypes.byref(server_ec),
                                            ctypes.byref(server_pub), ctypes.c_long(server_pubLen))
        if not server_ec:
            return None, None
        # const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key)
        server_ec = c_void_p(server_ec)
        server_ECpoint = c_void_p(OpenSSL.EC_KEY_get0_public_key(server_ec))

        pri_ec = OpenSSL.EC_KEY_new_by_curve_name(self.curveID)
        if not pri_ec:
            return None, None
        if not priKey:
            priKey = self.prikey
        priKey_len = len(priKey)
        priKey = ctypes.cast(priKey, ctypes.POINTER(ctypes.c_char * priKey_len)) 
        # EC_KEY *d2i_ECPrivateKey(EC_KEY **a, const unsigned char **in, long len)
        pri_ec = c_void_p(pri_ec)
        pri_ec = OpenSSL.d2i_ECPrivateKey(ctypes.byref(pri_ec),
                                        ctypes.byref(priKey), ctypes.c_long(priKey_len))
        if not pri_ec:
            return None, None
        ECDH_compute_key = OpenSSL.ECDH_compute_key
        # void *(*KDF) (const void *in, size_t inlen, void *out, size_t *outlen))
        KDF = CFUNCTYPE(c_void_p,  # return type
                        c_void_p, c_size_t, c_void_p, POINTER(c_size_t))
        @KDF
        def mykdf(_in, _inlen, out, outlen):
            nonlocal OUTLEN
            data = addressof(_in)[:_inlen]
            md5 = hashlib.md5()
            md5.update(data)
            outlen.contents = c_size_t(OUTLEN)
            outlen = POINTER(c_int(len(out)))
            return out
        # int ECDH_compute_key(void *out, size_t outlen, const EC_POINT pub_key, EC_KEY ecdh,
        #           void (KDF) (const void in, size_t inlen, void out, size_t *outlen));
        ECDH_compute_key.argtypes = (c_void_p, c_size_t,
                                    c_void_p, c_void_p, KDF)
        ECDH_compute_key.restype = c_int
        ShareKey_buf = ctypes.create_string_buffer(b'\000', 128)
        ShareKey_p = ctypes.cast(ShareKey_buf, ctypes.POINTER(ctypes.c_char * 128))
        outlen = c_size_t(OUTLEN)
        # int ECDH_compute_key(void *out, size_t outlen, const EC_POINT *pub_key,
        #              EC_KEY *eckey, void *(*KDF) )
        result = ECDH_compute_key(ShareKey_p, outlen, server_ECpoint, pri_ec, mykdf)
        # ------- OSError: exception: access violation reading 0x000000000FA805C0

        OpenSSL.EC_KEY_free(server_ec), OpenSSL.EC_KEY_free(pri_ec)
        if result == OUTLEN:
            EcdhShareKey = ShareKey_buf[:OUTLEN]
        else:
            return None
if __name__ == '__main__':
    EcdhPriKey = b'0\x82\x01D\x02\x01\x01\x04\x1c\xb9~,]@\x81\xe2\x04\x86\xdd\xc4\r\xa3\xaad\xc1\x8b\xa4\xb3\xef\x1ce\x9ck\xe6\x91\xc5\x1f\xa0\x81\xe20\x81\xdf\x02\x01\x010(\x06\x07*\x86H\xce=\x01\x01\x02\x1d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x010S\x04\x1c\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x04\x1c\xb4\x05\n\x85\x0c\x04\xb3\xab\xf5A2VPD\xb0\xb7\xd7\xbf\xd8\xba\'\x0b9C#U\xff\xb4\x03\x15\x00\xbdq4G\x99\xd5\xc7\xfc\xdcE\xb5\x9f\xa3\xb9\xab\x8fj\x94\x8b\xc5\x049\x04\xb7\x0e\x0c\xbdk\xb4\xbf\x7f2\x13\x90\xb9J\x03\xc1\xd3V\xc2\x11"42\x80\xd6\x11\\\x1d!\xbd7c\x88\xb5\xf7#\xfbL"\xdf\xe6\xcdCu\xa0Z\x07GdD\xd5\x81\x99\x85\x00~4\x02\x1d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x16\xa2\xe0\xb8\xf0>\x13\xdd)E\\\\*=\x02\x01\x01\xa1<\x03:\x00\x04\xabd#\x90\xd3H3\xf5o\xe8.\t\x0f\xa0\x90\x8f\xb7-t\x85\xf83\xd8\x0b\xa7\xe5{\xf6\xdd%\xa4\xd6\xaa!\xb7\x8f\xfa\xdd\xe4]\x81q\xb6\xb4|E\xdc\xe0h\x14o\x98\xb4\xa1\xf3>'
    ServerEcdhPubKey = b'\x04\xea/\xe0\xf3\xb0\xfb2]n-Y\x80\xa4\xea\xaa\xd2\xf6\x11\x95\xa7o\xf6Zj-\x8d\xdd\x1c\x84\xaa\x9a\n\xdf\xdf\x1f\x95h\xc0w<\x9du$\xe9\xfd\xf2\x0c%x\xab0\xf4@\xed\xa0g'
    EcdhShareKey = b'\x7f+\xcc\x0b\x05\x0fC=?3\x90\xb3Bt\x89\xc8'
    ec = Ecdh()
    ec.do_ECDHshare(ServerEcdhPubKey, EcdhPriKey)

Please tell me what should I do. Thank you very much.

pri_ec = OpenSSL.d2i_ECPrivateKey(ctypes.byref(pri_ec), ctypes.byref(priKey), ctypes.c_long(priKey_len))

What I write in openssl_wrapper.py:

self.d2i_ECPrivateKey = self._lib.d2i_ECPrivateKey
self.d2i_ECPrivateKey.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_long]
self.i2d_ECPrivateKey.restype = ctypes.c_void_p

so, this C function return a void pointer, then in python is an int, such as 18446744072866849424 But next function needs an pointer, so I use pri_ec = c_void_p(pri_ec) to transfer it (This is very dirty, but I don't know howto). OSError: exception: access violation reading 0x000000000FA805C0, this hex mean 18446744072866849472 pri_ec's address, is 18446744072866849424 in Python. So, I think this is that bug. How to fix it? Thanks, a lot.

yensan
  • 1
  • 2
  • Please specify what the error is (include full stacktrace). Didn't work with *ecdhlib* but It doesn't seem a good idea to mix it with *ctypes* (should have its own interfaces). And for *Lnx* it's _libcrypto. **so**_. – CristiFati Apr 19 '18 at 09:46
  • @CristiFati yes you are right. I just test in Windows, if I do it, then move to linux. You see, the C source I pasted in there is compiled in windows dll, but when I compiled in Linux, it can't work. So, I use Ctypes as an optional choice, once I make it, I move to linux – yensan Apr 19 '18 at 10:19
  • this C function call, raise an Error. **result = ECDH_compute_key(ShareKey_p, outlen, server_ECpoint, pri_ec, mykdf)** # ------- OSError: exception: access violation reading 0x000000000FA805C0 – yensan Apr 19 '18 at 10:23
  • Please add the error in the question rather than comments. `OSError` alone was totally misleading. Now things are clear, you're messing up with the memory (trying to access something that isn't yours). This confirms that mixing *ctypes* with *ecdhlib* is wrong. – CristiFati Apr 19 '18 at 10:29
  • there is no ecdhlib... just 3 files: **OpenSSL's libcrypto file**, a compiled C lib , **wrapper.py** use ctypes to connect lib file, **ecdh.py** use C function to generate key. – yensan Apr 19 '18 at 10:47
  • `from ecdhlib.openssl_wrapper import OpenSSL` that means that *ecdhlib* is a *Python* package. How did you get it? – CristiFati Apr 19 '18 at 10:57
  • @CristiFati yes you are so careful ! took this advice. There is no ecdhlib... This is a very simple stuff...just 3 files: OpenSSL's libcrypto file, a compiled C lib , wrapper.py use ctypes to connect lib file, ecdh.py use C function to generate key. no, this is my package, I paste all crypto libs (win32 win64 Linux MacOS ...) in there, so I named it ecdhlib. Misguide you – yensan Apr 19 '18 at 11:00
  • I paste it in https://github.com/Yensan/ecdh/blob/master/util/ecdh.py – yensan Apr 19 '18 at 12:51
  • You commented out `ECDH_compute_key`'s `argtypes` and `restype`, meaning that all default to `c_int`. Also most of the funcs require struct pointers, but you deal with `c_void_p`. Technically that's possible, but you'd need to know the struct format (at binary level), but the error you get is an indicator that you don't. Define the structs (which would require huge amount of work). Here's how to use *ctypes*: https://stackoverflow.com/questions/48788549/python-ctypes-oserror-exception-access-violation-writing-0xfffffffffa1c001/48811895#48811895. You could use *pyOpenSSL*, or *pyCrypto*. – CristiFati Apr 19 '18 at 15:14
  • @CristiFati I finnally fix it and generate the right result, although this is dirty. I don't know much about ctypes, I just read _Python cookbook_ chapter 15 then satrt coding. If you know a good tutorial, tell me. Surelly, I should try to find others package first. So did I. Ihave tried pyOpenSSL, or pyCrypto. I must tell you, I find all of PyPI and github, I can not find what I want... That is painfull – yensan Apr 20 '18 at 06:58

0 Answers0