【问题标题】:Python Ctypes with RaspberryPi带有 RaspberryPi 的 Python Ctypes
【发布时间】:2014-08-13 04:42:43
【问题描述】:

我正在为 libsodium 例程 crypto_aead_chacha20poly1305_encrypt 在 PySodium 中编写一个 ctypes 包装器,该例程定义为:

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 系统上运行顺利。不幸的是,当我将所有内容都移植到 RaspberryPi (ARMv6) 上时,事情就被取消了。我对它运行了gdb python-gdb,可以看到堆栈跟踪:

#0 chacha_keysetup (x=0xbeffefc4, k=0x0) 在 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) 在 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) 在 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) 在/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', , 无, '\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"),标志=4353,argtypes=0x0,restype= , 检查器=0x0) 在/build/python2.7-xJctIx/python2.7-2.7.3/Modules/_ctypes/callproc.c:1174 #7 0x404e5154 在 PyCFuncPtr_call (self=0x40518e90, inargs= (, , '\x86\xd0\x99t\x84\x0b\xde\xd2\xa5\xca', , '\x87\xe2)\xd4P\x08E\xa0y\xc0', , 无, '\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) 在/build/python2.7-xJctIx/python2.7-2.7.3/Modules/_ctypes/_ctypes.c:3913 #8 0x0002fa08 在 PyObject_Call (func=, arg= (, , '\x86\xd0\x99t\x84\x0b\xde\xd2\xa5\xca', , '\x87\xe2)\xd4P\x08E\xa0y\xc0', , 无, '\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) 在../Objects/abstract.c:2529

阅读 FFI 实际上做了什么我不得不认为,由于某种原因,当 ctypes 将数据传递给 FFI 时,它会在此过程中被损坏。我已经搜索过,试图找出在 Pi/ARM 平台上 ctypes/FFI 等是否存在一些已知问题,但一直空白。如果这是我的实际绑定代码,我很想知道为什么它在 x86_64 与 ARM 上的行为正确。

最后,我对使用 ctypes 替代品的建议持开放态度(尽管对 SWIG 并不疯狂,如果需要的话)。

【问题讨论】:

  • 您可能应该尝试设置显式argtypes(和restype,但这并不重要,尤其是对于返回int 的函数)。当您不确定您创建的值是错误的还是错误的签名时,调试这些问题已经够难的了……
  • 供参考,这里是the docs for the function。乍一看,您似乎掌握了正确的类型……
  • 最后一件事:根据Bindings for other languages,libsodium 有两组不同的 Python 绑定。你两个都试过了吗?它们是否缺少您需要的功能,或者它们没有在 RPi 上运行,或者......?
  • 感谢argtypes 的提示,我去看看。我看了一眼其他 Python 绑定,它们完全是在旧版本的 libsodium 上,但我已经提出了一个问题请求,希望我可以与他们合作更新它。
  • 如果他们都使用ctypes,也许对方的来源有一些可以提供帮助的想法,即使你不能直接使用它?

标签: python raspberry-pi ctypes ffi


【解决方案1】:

下面的clen 需要使用ctypes.byref() 方法通过引用传递。来自 Ctypes documentation有时 C api 函数需要一个指向数据类型的指针作为参数,可能要写入相应的位置,或者如果数据太大而无法按值传递。这也称为通过引用传递参数。 ctypes 导出用于通过引用传递参数的 byref() 函数。 无论出于何种原因 使用 ctypes.byref() 在 x86 平台上没有问题,但在 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 的绑定可在 https://github.com/iachievedit/pysodium 的 pysodium 分支上获得,直到提交并接受拉取请求。

【讨论】:

  • 看看你包装的函数,这很有意义。 clen 是一个输出参数——你传递一个指向 long long 的指针,它会填充长度。通过传递0,您将传递一个NULL 指针。显然这没关系。但是您还向它传递了一个 64 位 NULL 指针,它几乎可以在任何 64 位平台上工作,或者在任何将 args 传递到寄存器中的平台上——但在堆栈上,这意味着您已经搞砸了每个后续参数。
  • 顺便说一下,这正是你想使用argtypes的原因。如果您将函数声明为采用POINTER(c_ulonglong) 参数,您将从ctypes 获得易于诊断的TypeError,而不是依赖于平台的段错误……
  • 啊,太棒了,在阅读了示例 printf.argtypes = [c_char_p, c_char_p, c_int, c_double] 之后,这很有意义。一种用于捕获错误的静态声明。
  • 没错。参数类型可用于捕获错误,甚至可以进行一些自动转换。即使 C 也可以,例如,将 int 转换为 long,如果你给它一个函数原型,但 ctypes 可以为你做各种漂亮的转换,你可以按照你想要的任何方式扩展它。
猜你喜欢
  • 2017-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-09
  • 1970-01-01
  • 2015-01-31
相关资源
最近更新 更多