ctypes使用指针和CFUNCTYPE

时间:2018-04-19 06:16:25

标签: python pointers ctypes

我写这个代码用于计算ecdh密钥。使用OpenSSL-1_0_2o。

C代码在那里(所有include和lib只是OpenSSL)

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;
}

然后,我在Python ctypes中重写,只需在Linux中使用带有libeay32.dll的python,在Linux中使用 OR libcrypto.so。 当我调用C函数ECDH_compute_key引发OS错误时。

此功能: result = ECDH_compute_key(ShareKey_p, outlen, server_ECpoint, pri_ec, mykdf)

错误: # ------- 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)

请告诉我该怎么办。 非常感谢。

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

我在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

所以,这个C函数返回一个void指针,然后在python中是一个int,如18446744072866849424 但是下一个函数需要一个指针,所以我使用pri_ec = c_void_p(pri_ec)来传递它(这很脏,但我不知道怎么做)。 OSError:异常:访问冲突读取0x000000000FA805C0 ,此十六进制均值18446744072866849472 pri_ec的地址,是Python中的18446744072866849424。所以,我认为这就是那个bug。 怎么解决?非常感谢。

0 个答案:

没有答案