Python Ctypes与树莓派

4 投票
1 回答
1915 浏览
提问于 2025-04-18 17:06

我正在为PySodium写一个ctypes的封装,用来调用libsodium中的一个函数crypto_aead_chacha20poly1305_encrypt,这个函数的定义是:

def crypto_aead_chacha20poly1305_encrypt(message,
                                         ad,
                                         nonce,
                                         key):

    mlen  = ctypes.c_ulonglong(len(message))
    adlen = ctypes.c_ulonglong(len(ad))

    c    =  ctypes.create_string_buffer(mlen.value+16L)
    clen  = ctypes.c_ulonglong(0)

    sodium.crypto_aead_chacha20poly1305_encrypt(c,
                                                clen,
                                                message,
                                                mlen,
                                                ad,
                                                adlen,
                                                None,
                                                nonce,
                                                key)
    return c.raw

我的测试驱动程序是:

from pysodium import  crypto_aead_chacha20poly1305_encrypt
from bitstring import BitStream

key = BitStream(hex="4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd1100a1007")
nonce = BitStream(hex="cd7cf67be39c794a")
ad = BitStream(hex="87e229d4500845a079c0")
msg = BitStream(hex="86d09974840bded2a5ca")

print(key)
print(nonce)
print(ad)
print(msg)

m = crypto_aead_chacha20poly1305_encrypt(message=msg.bytes,
                                         ad=ad.bytes,
                                         nonce=nonce.bytes,
                                         key=key.bytes)

edata = BitStream(bytes=m)
print(edata)

让我感到惊讶的是(因为这是我第一次使用libsodium、PySodium和ctypes),在AMD的x86_64系统上它运行得非常顺利。不过,当我把所有东西移到树莓派(ARMv6)上时,情况就不一样了。一运行gdb python-gdb,我就能看到堆栈跟踪信息:

#0  chacha_keysetup (x=0xbeffefc4, k=0x0) at crypto_stream/chacha20/ref/stream_chacha20_ref.c:69
#1  0x4059dcbc in crypto_stream_chacha20_ref (c=0xbefff0e4 "\264\335\024", clen=, n=0x0, k=0x0)
    at crypto_stream/chacha20/ref/stream_chacha20_ref.c:241
#2  0x40556f60 in crypto_aead_chacha20poly1305_encrypt (c=0x405385f4 "\206Йt\204\v\336", , clen=0x437f44, 
    m=0x0, mlen=4634464344201201140, ad=0xa , adlen=1079196860, nsec=0xa , npub=
    0x0, k=0x0) at crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c:49
#3  0x40501368 in ffi_call_VFP () from /usr/lib/python2.7/lib-dynload/_ctypes_d.so
#4  0x40500930 in ffi_call () from /usr/lib/python2.7/lib-dynload/_ctypes_d.so
#5  0x404ecf10 in _call_function_pointer (flags=4353, pProc=0x40556ea8 , avalues=0xbefff2d8, 
    atypes=0xbefff2a8, restype=0x402a6ed8, resmem=0xbefff308, argcount=9)
    at /build/python2.7-xJctIx/python2.7-2.7.3/Modules/_ctypes/callproc.c:827
#6  0x404edb84 in _ctypes_callproc (pProc=0x40556ea8 , argtuple=
    (, , '\x86\xd0\x99t\x84\x0b\xde\xd2\xa5\xca', , '\x87\xe2)\xd4P\x08E\xa0y\xc0', , None, '\xcd|\xf6{\xe3\x9cyJ', "B\x90\xbc\xb1T\x1751\xf3\x14\xafW\xf3\xbe;P\x06\xda7\x1e\xce'*\xfa\x1b]\xbd\xd1\x10\n\x10\x07"), flags=4353, argtypes=0x0, restype=
    , checker=0x0)
    at /build/python2.7-xJctIx/python2.7-2.7.3/Modules/_ctypes/callproc.c:1174
#7  0x404e5154 in PyCFuncPtr_call (self=0x40518e90, inargs=
    (, , '\x86\xd0\x99t\x84\x0b\xde\xd2\xa5\xca', , '\x87\xe2)\xd4P\x08E\xa0y\xc0', , None, '\xcd|\xf6{\xe3\x9cyJ', "B\x90\xbc\xb1T\x1751\xf3\x14\xafW\xf3\xbe;P\x06\xda7\x1e\xce'*\xfa\x1b]\xbd\xd1\x10\n\x10\x07"), kwds=0x0)
    at /build/python2.7-xJctIx/python2.7-2.7.3/Modules/_ctypes/_ctypes.c:3913
#8  0x0002fa08 in PyObject_Call (func=, arg=
    (, , '\x86\xd0\x99t\x84\x0b\xde\xd2\xa5\xca', , '\x87\xe2)\xd4P\x08E\xa0y\xc0', , None, '\xcd|\xf6{\xe3\x9cyJ', "B\x90\xbc\xb1T\x1751\xf3\x14\xafW\xf3\xbe;P\x06\xda7\x1e\xce'*\xfa\x1b]\xbd\xd1\x10\n\x10\x07"), kw=0x0) at ../Objects/abstract.c:2529

我查了一下FFI到底是干什么的,我开始怀疑在ctypes把数据传递给FFI的过程中,数据可能被破坏了。我搜索了一下,想看看在树莓派/ARM平台上是否有ctypes/FFI的已知问题,但没有找到。如果是我的绑定代码出了问题,我想知道为什么在x86_64上能正常工作,而在ARM上却不行。

最后,我也欢迎大家提供一些替代ctypes的建议(虽然我对SWIG不太感兴趣,但如果必须用的话也可以考虑)。

1 个回答

3

下面的 clen 需要通过 ctypes.byref() 方法以引用的方式传递。根据 Ctypes 的 文档有时候,C 语言的 API 函数需要一个数据类型的指针作为参数,这样可以在对应的位置写入数据,或者当数据太大时无法直接传值。这种方式也叫做通过引用传递参数。ctypes 提供了 byref() 函数,用于通过引用传递参数。 不管出于什么原因,在 x86 平台上不使用 ctypes.byref() 并不会出现问题,但在 ARM 处理器上就会出现问题。更新代码使用这个方法后,解决了 ARM 上的段错误,并且在 x86 上也能正常工作。

def crypto_aead_chacha20poly1305_encrypt(message,
                                         ad,
                                         nonce,
                                         key):

    mlen  = ctypes.c_ulonglong(len(message))
    adlen = ctypes.c_ulonglong(len(ad))

    c    =  ctypes.create_string_buffer(mlen.value+16L)
    clen  = ctypes.c_ulonglong(0)

    sodium.crypto_aead_chacha20poly1305_encrypt(c,
                                                ctypes.byref(clen),
                                                message,
                                                mlen,
                                                ad,
                                                adlen,
                                                None,
                                                nonce,
                                                key)
    return c.raw

关于 crypto_aead_chacha20poly1305_encryptcrypto_aead_chacha20poly1305_decryptcrypto_stream_chacha20_xor 的绑定可以在 pysodium 的一个分支上找到,地址是 https://github.com/iachievedit/pysodium,直到提交并接受一个拉取请求。

撰写回答