saltstack / libnacl Goto Github PK
View Code? Open in Web Editor NEWPython ctypes wrapper for libsodium
License: Apache License 2.0
Python ctypes wrapper for libsodium
License: Apache License 2.0
Hi, I'm one packager maintainer of libnacl on Linux Fedora, I got a report today that fails to build from the source with new libsodium 1.0.15 in rawhide [1]
It is an easy fix ?
[1]
https://koji.fedoraproject.org/koji/buildinfo?buildID=977895
https://kojipkgs.fedoraproject.org//work/tasks/4814/22204814/build.log
libnacl (unittest.loader._FailedTest) ... ERROR
======================================================================
ERROR: libnacl (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: libnacl
Traceback (most recent call last):
File "/usr/lib/python3.6/unittest/loader.py", line 462, in _find_test_path
package = self._get_module_from_name(name)
File "/usr/lib/python3.6/unittest/loader.py", line 369, in _get_module_from_name
__import__(name)
File "/builddir/build/BUILD/libnacl-1.5.2/libnacl/__init__.py", line 83, in <module>
nacl = _get_nacl()
File "/builddir/build/BUILD/libnacl-1.5.2/libnacl/__init__.py", line 81, in _get_nacl
raise OSError(msg)
OSError: Could not locate nacl lib, searched for libsodium.so, libsodium.so.18, libsodium.so.17, libsodium.so.13, libsodium.so.10, libsodium.so.5, libsodium.so.4,
According to RFC 7693 Appendix-A, the BLAKE2b hexadecimal digest of "abc" should be:
BLAKE2b-512("abc") = BA 80 A5 3F 98 1C 4D 0D 6A 27 97 B6 9F 12 F6 E9
4C 21 2F 14 68 5A C4 B7 4B 12 BB 6F DB FF A2 D1
7D 87 C5 39 2A AB 79 2D C2 52 D5 DE 45 33 CC 95
18 D3 8A A8 DB F1 92 5A B9 23 86 ED D4 00 99 23
This is confirmed by compiling and executing the reference code at https://github.com/blake2/blake2:
% printf 'abc' | b2sum -a blake2b | tr 'a-f' 'A-F' | sed -r 's/(..)/\1 /g' | fold -w 48
BA 80 A5 3F 98 1C 4D 0D 6A 27 97 B6 9F 12 F6 E9
4C 21 2F 14 68 5A C4 B7 4B 12 BB 6F DB FF A2 D1
7D 87 C5 39 2A AB 79 2D C2 52 D5 DE 45 33 CC 95
18 D3 8A A8 DB F1 92 5A B9 23 86 ED D4 00 99 23
crypto_generichash seems to be printing the length of a BLAKE2s digest (32-bytes instead of 64-bytes), and even then, the hexadecimal output is still wrong:
% python -c 'import libnacl;h=libnacl.crypto_generichash("abc");print h.encode("hex")'
bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319
According to the RFC, the BLAKE2s hexadecimal digest should be:
BLAKE2s-256("abc") = 50 8C 5E 8C 32 7C 14 E2 E1 A7 2B A3 4E EB 45 2F
37 45 8B 20 9E D6 3A 29 4D 99 9B 4C 86 67 59 82
Verified with the reference executable:
% printf 'abc' | b2sum -a blake2s | tr 'a-f' 'A-F' | sed -r 's/(..)/\1 /g' | fold -w 48
50 8C 5E 8C 32 7C 14 E2 E1 A7 2B A3 4E EB 45 2F
37 45 8B 20 9E D6 3A 29 4D 99 9B 4C 86 67 59 82
Whatever crypto_generichash is printing, it doesn't conform to the BLAKE2 specification.
$ python
Python 3.5.1 (default, Dec 7 2015, 12:58:09)
[GCC 5.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import libnacl
>>> libnacl.crypto_scalarmult_base(None)
Segmentation fault (core dumped)
This came up in keybase/saltpack-python#1.
Please include install direction in the readme.
salt module nacl references this git project for install directions.
[yum|apt-get] install libsodium
pip install?
or perhaps:
git cone https://github.com/saltstack/libnacl.git /tmp/libnacl
mv /tmp/libnacl/libnacl /usr/lib/python2.7/site-packages/libnacl
ibnacl cannot use libsodium.so.23, /libnacl/init.py should be modified to for libsodium.so.23.
Adding 23 to the __SONAMES array on line 12 should be the simplest fix.
In [5]: signer = libnacl.sign.Signer()
In [6]: signer.save('dev_nacl.key')
In [7]: libnacl.utils.load_key('dev_nacl.key')
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
... in <module>()
----> 1 libnacl.utils.load_key('dev_nacl.key')
C:\Python33\lib\site-packages\libnacl\utils.py in load_key(path, serial)
28 return libnacl.dual.DualSecret(
29 libnacl.encode.hex_decode(key_data['priv']),
---> 30 libnacl.encode.hex_decode(key_data['sign']))
31 elif 'priv' in key_data:
32 return libnacl.public.SecretKey(
C:\Python33\lib\site-packages\libnacl\dual.py in __init__(self, crypt, sign)
15 '''
16 def __init__(self, crypt=None, sign=None):
---> 17 self.crypt = libnacl.public.SecretKey(crypt)
18 self.signer = libnacl.sign.Signer(sign)
19 self.sk = self.crypt.sk
C:\Python33\lib\site-packages\libnacl\public.py in __init__(self, sk)
33 self.pk = libnacl.crypto_scalarmult_base(sk)
34 else:
---> 35 raise ValueError('Passed in invalid secret key')
36
37
ValueError: Passed in invalid secret key
The file "dev_nacl.key" has the fields sign - 32 bytes, verify - 32 bytes, and priv - 64 bytes. It looks like priv is passed in as the crypt param to a DualSecret object, which is in turn passed as sk to SecretKey, which in turn is unhappy about the priv's length being 64 and not 32. It seems that box and sign differ in the lengths of their secret keys.
The problem may be that the key is being read back in as a DualSecret rather than a signer, or that the JSON struct doesn't adequately capture the different key types.
As a newcomer to nacl I'm also kind of confused about all the different key types and their relations in addition to libnacl's renaming of them. Some clarification in the docs would be very helpful.
Thanks!!
FYI, libsodium 1.0.19 is released at Sep 13 2023, with soname changed to libsodium.so.26
. This change makes module to fail with:
======================================================================
ERROR: unit.test_aead (unittest.loader._FailedTest.unit.test_aead)
----------------------------------------------------------------------
ImportError: Failed to import test module: unit.test_aead
Traceback (most recent call last):
File "/usr/lib64/python3.11/unittest/loader.py", line 407, in _find_test_path
module = self._get_module_from_name(name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.11/unittest/loader.py", line 350, in _get_module_from_name
__import__(name)
File "/usr/src/RPM/BUILD/python3-module-libnacl-1.7.1/tests/unit/test_aead.py", line 2, in <module>
import libnacl.aead
File "/usr/src/RPM/BUILD/python3-module-libnacl-1.7.1/libnacl/__init__.py", line 85, in <module>
nacl = _get_nacl()
^^^^^^^^^^^
File "/usr/src/RPM/BUILD/python3-module-libnacl-1.7.1/libnacl/__init__.py", line 81, in _get_nacl
raise OSError(msg)
OSError: Could not locate nacl lib, searched for libsodium.so, libsodium.so.23, libsodium.so.18, libsodium.so.17, libsodium.so.13, libsodium.so.10, libsodium.so.5, libsodium.so.4,
Perhaps, __SONAMES
list should be updated.
we need a basic test suite, all functions need tests
The documentation for "Signing and Verifying Messages" states:
Please be advised that public key encrypted messages do not need to be signed, the nacl box construct verifies the validity of the sender.
However, the NaCl docs explicitly state:
The crypto_box function is not meant to provide non-repudiation. On the contrary: the crypto_box function guarantees repudiability. A receiver can freely modify a boxed message, and therefore cannot convince third parties that this particular message came from the sender. The sender and receiver are nevertheless protected against forgeries by other parties. In the terminology of https://groups.google.com/group/sci.crypt/msg/ec5c18b23b11d82c, crypto_box uses "public-key authenticators" rather than "public-key signatures."
Users who want public verifiability (or receiver-assisted public verifiability) should instead use signatures (or signcryption).
So the documentation is at least misleading; encrypted messages need to be signed if they are intended to provide non-repudiation.
We are using ValueError everywhere, this should be changed to a custom exception type
After upgrading from 1.5.0 to 1.5.1, I'm getting the following error:
File "/usr/local/lib/python3.5/site-packages/libnacl/__init__.py", line 105, in <module>
crypto_box_SEALBYTES = nacl.crypto_box_sealbytes()
File "/usr/local/lib/python3.5/ctypes/__init__.py", line 360, in __getattr__
func = self.__getitem__(name)
File "/usr/local/lib/python3.5/ctypes/__init__.py", line 365, in __getitem__
func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /usr/lib/x86_64-linux-gnu/libsodium.so: undefined symbol: crypto_box_sealbytes
I'm using libsodium-dev=1.0.0-1
from apt-get
test_save.py due to missing os.close() for the mkstemp returned file handle and due to -
base.py:57:
cumask = os.umask(191)
on win32 this sets the file as read only for the user and os.remove() will fail on it.
It may be helpful to use stat.* symbolic flags instead hardcoded numbers?
crypto_box_open
raises CryptError on failure, but crypto_box_open_afternm
raises ValueError. Presumably they should raise the same thing?
please push latest version up to pip
would like to add support to saltstack module.nacl for sealedbox.
TL;DR init.py looks for libsodium.dll, but the prebuilt dll is libsodium-10.dll. Easy fix is to rename it but I don't know what that might break. Alternate fix is to make dependency lookup version-independent.
Here is my setup story on both a Mac and Windows machine, for posterity.
I first trialed libnacl on a Mac dev box and when I tried to pip install libnacl, I got an error saying I was missing a nacl lib. In fact, this was my first time even hearing about libsodium (I was using tweetnacl) but thankfully, it was only a brew install libsodium
away and I was up and running before long. Great!
However...
For many unsavory reasons, I do most of my development in Windows, so I now had to figure out how to satisfy the nacl dependency without brew. I was using a weird mix of cygwin and msys so I wasn't entirely sure where to place a dll even if I got my hands on one.
I noticed libsodium had prebuilt releases for mingw, so I jettisoned my cygwin setup and started with mingw from scratch. After finding a dll named libsodium-10.dll in the libsodium release tarball, I figured I'd place it in C:\MinGW\bin and that would be that. Well, init.py looks specifically for libsodium.dll so the dll had to be renamed before it was happy.
It would be an easy fix to make the lookup in python a less version-dependent but I don't know how safe that is in both a security and dependency sense.
https://github.com/saltstack/libnacl
https://github.com/pyca/pynacl
pynacl seemed to be around longer.
why the rewrite?
We need to get a standard sphinx tree built
Convert the job configurations within .travis.yml to GitHub Actions workflows, and decommission integration with Travis CI.
This is still just an idea, but it would allow for a pure python deployment of encrypted code to systems that do not have libsodium installed.
Tests fail on 32-bit Intel:
==> Starting check()...
test_gcm_aead (unit.test_aead.TestAEAD) ... /startdir/PKGBUILD: line 30: 424 Aborted (core dumped) python -m unittest discover --start-directory tests -v
==> ERROR: A failure occurred in check().
Aborting...
In sodium/crypto_aead_aes256gcm.h
I see that mlen_p
is a pointer to a 64-bit long long:
int crypto_aead_aes256gcm_decrypt(unsigned char *m,
unsigned long long *mlen_p,
unsigned char *nsec,
const unsigned char *c,
unsigned long long clen,
const unsigned char *ad,
unsigned long long adlen,
const unsigned char *npub,
const unsigned char *k)
In __init.py__
in the 'ctypes' wrapper I see:
mlen = ctypes.c_ulonglong()
..
ret = nacl.crypto_aead_aes256gcm_decrypt(
m, mlen,
None,
ctxt, ctypes.c_ulonglong(len(ctxt)),
aad, ctypes.c_ulonglong(len(aad)),
nonce, key)
...
ret = nacl.crypto_aead_chacha20poly1305_ietf_decrypt(
m, mlen,
None,
ctxt, ctypes.c_ulonglong(len(ctxt)),
aad, ctypes.c_ulonglong(len(aad)),
nonce, key)
...
This works fine on 64-bit, but not on 32-bit. The tests crash with segfault.
When I change the calls to use a ctypes.byref
the tests work fine:
ret = nacl.crypto_aead_aes256gcm_decrypt(
m, ctypes.byref(mlen),
None,
ctxt, ctypes.c_ulonglong(len(ctxt)),
aad, ctypes.c_ulonglong(len(aad)),
nonce, key)
...
ret = nacl.crypto_aead_chacha20poly1305_ietf_decrypt(
m, ctypes.byref(mlen),
None,
ctxt, ctypes.c_ulonglong(len(ctxt)),
aad, ctypes.c_ulonglong(len(aad)),
nonce, key)
See also debugging session on https://bbs.archlinux32.org/viewtopic.php?pid=219#p219.
Creating a SecretKey
in public.py
should be allowed by seed.
However i'd like to create a PR and implement that i'm unsure how to achieve that.
SecretKey
model called something like generate_from_seed
generate_secret_key_from_seed
__init__
method of the SecretKey
and add seed as argument.sk
and pk
im mostly for 2.
sk
or seed
or both or none of them. Not a good api and not easy to understand.Signer
and SecretKey
creation.What do you think?
the d1b25c0 change which was included in the 1.5.1 release changed the interface when failing to decrypt a message from ValueError to CryptError.
While I'm all for consistent interfaces, please do document these kinds of API changes in the Changelog, as they did unexpectedly affect our code base and will likely bite anybody who just updates to the latest patch-version of the code 1.5 branch.
crypto_box_open_easy() and crypto_box_open() in libsodium seem to be different。
`def inttobytes(param_int_list):
param_hex = ""
for param_int in param_int_list:
if param_int<0:
param_int = param_int + 256
if len(hex(param_int)[2:])==1:
param_hex = param_hex + "0" + hex(param_int)[2:]
else:
param_hex = param_hex + hex(param_int)[2:]
return bytes.fromhex(param_hex)
def bytesToString(bs):
return bytes.decode(bs,encoding='utf8')
privateKey_int_list = [-42,23,-8,-49,68,59,-86,57,124,-34,-56,90,44,-103,-48,115,-94,57,-32,-53,40,-82,7,-66,-15,-83,72,25,-14,105,95,-61]
privateKey = base64.b64decode("1hf4z0Q7qjl83shaLJnQc6I54Msorge+8a1IGfJpX8M=")
publickey_int_list = [95,73,-2,-73,63,-78,-1,41,-103,73,125,-8,-78,122,-5,-124,36,-23,9,-125,38,-81,-63,3,-63,26,-12,-33,120,60,-23,36]
publickey = inttobytes(publickey_int_list)
bArr2_int_hex= [-41,-81,-73,-39,-40,106,-127,82,5,118,83,-128,-121,-81,-17,63,-121,6,108,-81,-99,75,93,105,-74,22,21,67,72,-101,110,108,55,-60,-64,-115,-4,-49,15,80,-95,51,98,31,-1,7,86,78,-27,-128,-78,-74,-86,-78,123,28,-126,-41,-76,97,51,-11,122,65,-80,-80,-114,-90,13,-55,-101,89,-112,-7,-31,64,92,61,-111,49,-35,-120,110,-6,-26,107,61,5,88,57,75,-102,18,-104,120,15,-124,58,19,-9,-12,-45,-22,14,42,-51,-90,39,-109,-70,89,53,-80,-19,-55,-93,-42,29,-46,-116,-96,-8,33,1,-9,-68,-119,-116,34,-78,-54,-29,-6,-100,-74,33,-89,-87,6,-8,43,-74,75,8,-57,-9,12,25,60,67,52,32,53,54,59,107,38,85,-83,-45,-1,32,93,-11,-17,-19,-41,116,-56,74,70,10,-70,20,-119,-113,55,80,-98,31,-99,-36,88,114,72,-40,75,116,44,-65,92,-61,106,88,121,-79,-18,-33,-73,-86,-13,-33,19,125,71,2,104,-16,-23,72,-91,-75,-54,-3,-63,111,69,87,24,10,19,-108,-111,-21,118,-51,20,98,-54,44,-59,-110,69,77,14,-70,40,80,-31,-50,43,-82,-11,-2,-96,-89,101,34,-8,54,-5,-86,-31,19,25,123,-106,-23,105,73,-26,-61,118,-111,37,108,114,77,-43,-50,83,76,-71,-55,65,9,90,-115,121,-81,-51,88,89,-29,-97,5,-46,-21,-59,33,-51,34,-27,18,95,65,-98,-68,86,-65,-96,91,-86,52,-33,-26,-121,-63,68,-41,-114,125,62,62,39,-51,85,95,-90,-8,77]
bArr2 = inttobytes(bArr2_int_hex)
bArr3_int_hex = [106,112,-58,-70,28,-71,45,-121,65,24,13,48,96,39,79,-15,37,-73,125,-6,120,-78,-54,-111]
bArr3 = inttobytes(bArr3_int_hex)
i = 321
bArr_int_hex = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
bArr = inttobytes(bArr_int_hex)
print(libnacl.crypto_box_open_easy(bArr2,bArr3,publickey,privateKey))`
When I change crypto_box_open in the picture to crypto_box_open_easy, no Crypterror will be reported
If I run:
import libnacl
# Empty message with the nonce and key having one byte each, instead of 24 and 32.
print(libnacl.crypto_secretbox(b'', b'\x00', b'\x00'))
I get output that's different every time I run it. Is this function reading uninitialized memory?
________________________________________________ TestRandomBytes.test_crypto_kdf_derive_from_key _________________________________________________
self = <unit.test_raw_random.TestRandomBytes testMethod=test_crypto_kdf_derive_from_key>
def test_crypto_kdf_derive_from_key(self):
master_key = libnacl.crypto_kdf_keygen()
subkey = libnacl.crypto_kdf_derive_from_key(16, 1, "Examples", master_key)
subkey2 = libnacl.crypto_kdf_derive_from_key(16, 1, "Examples", master_key)
subkey3 = libnacl.crypto_kdf_derive_from_key(16, 2, "Examples", master_key)
self.assertEqual(16, len(subkey))
self.assertEqual(16, len(subkey2))
self.assertEqual(16, len(subkey3))
> self.assertEqual(subkey, subkey2)
E AssertionError: b'r\xefe\xafr~\x9f\xf5\xe7\x17uK\xa7Ty-' != b'\xf5N\x9c\xe9\xc6\x1ag\xe6\xffJ\xd7\xf8\xd1>\xb8\x12'
tests/unit/test_raw_random.py:64: AssertionError
$ python --version
Python 3.8.8
$ pkg-config --modversion libsodium
1.0.18
Reproduced with git 33b22d2.
Judging by #123, this function has serious portability problems.
The documentation says that you can initialize a public key like so:
tom = libnacl.public.PublicKey(tom_public_key_hex)
However, that is actually incorrect as the initializer does not unhexlify the argument:
class PublicKey(libnacl.base.BaseKey):
'''
This class is used to manage public keys
'''
def __init__(self, pk):
self.pk = pk
The same is true for the SecretKey:
class SecretKey(libnacl.base.BaseKey):
'''
This class is used to manage keypairs
'''
def __init__(self, sk=None):
'''
If a secret key is not passed in then it will be generated
'''
if sk is None:
self.pk, self.sk = libnacl.crypto_box_keypair()
elif len(sk) == libnacl.crypto_box_SECRETKEYBYTES:
self.sk = sk
This is what happens obviously:
>>> keys = libnacl.utils.load_key("TestingKeys.keys")
>>> keys
<libnacl.public.SecretKey object at 0x0000000002225A20>
>>> keys.hex_pk()
b'7c8e073e1bbb8342235d9f21478d29b7969d436cf1fa613d363d00701cc57d40'
>>> pubkey = libnacl.public.PublicKey(b'7c8e073e1bbb8342235d9f21478d29b7969d436c
f1fa613d363d00701cc57d40')
>>> pubkey
<libnacl.public.PublicKey object at 0x0000000002232FD0>
>>> pubkey.hex_pk()
b'376338653037336531626262383334323233356439663231343738643239623739363964343336
63663166613631336433363364303037303163633537643430'
>>> keys.pk
b'|\x8e\x07>\x1b\xbb\x83B#]\x9f!G\x8d)\xb7\x96\x9dCl\xf1\xfaa=6=\x00p\x1c\xc5}@'
>>> pubkey.pk
b'7c8e073e1bbb8342235d9f21478d29b7969d436cf1fa613d363d00701cc57d40'
>>> import binascii
>>> binascii.unhexlify(pubkey.pk)
b'|\x8e\x07>\x1b\xbb\x83B#]\x9f!G\x8d)\xb7\x96\x9dCl\xf1\xfaa=6=\x00p\x1c\xc5}@'
>>>
I'm not sure which behavior would be better, maybe hex form is better because if you're manually feeding them in, that's probably what you'll be using. At least I was.
Either way, the documentation is incorrect.
On debian 8, when upgrading to 1.7 I see this error:
ImportError: Failed to import test module: unit.modules.test_nacl
Traceback (most recent call last):
File "/usr/lib/python2.7/unittest/loader.py", line 254, in _find_tests
module = self._get_module_from_name(name)
File "/usr/lib/python2.7/unittest/loader.py", line 232, in _get_module_from_name
__import__(name)
File "/tmp/kitchen/testing/tests/unit/modules/test_nacl.py", line 10, in <module>
import salt.modules.nacl as nacl
File "/tmp/kitchen/testing/salt/modules/nacl.py", line 157, in <module>
import salt.utils.nacl
File "/tmp/kitchen/testing/salt/utils/nacl.py", line 26, in <module>
import libnacl.secret
File "/tmp/kitchen/testing/.nox/runtests-parametrized-2-7-coverage-true-crypto-none-transport-zeromq/local/lib/python2.7/site-packages/libnacl/__init__.py", line 166, in <module>
randombytes_SEEDBYTES = nacl.randombytes_seedbytes()
File "/usr/lib/python2.7/ctypes/__init__.py", line 378, in __getattr__
func = self.__getitem__(name)
File "/usr/lib/python2.7/ctypes/__init__.py", line 383, in __getitem__
func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /usr/lib/x86_64-linux-gnu/libsodium.so: undefined symbol: randombytes_seedbytes
This is because randombytes_seedbytes
was added here jedisct1/libsodium@cafb0a6 which was added to libsodium version 1.0.12 and by default it runs libsodium18:amd64 1.0.8-5
I'm also seeing this error on amazon linux 1 for similar reasons:
ImportError: Failed to import test module: integration.runners.test_nacl
Traceback (most recent call last):
File "/usr/lib64/python2.7/unittest/loader.py", line 254, in _find_tests
module = self._get_module_from_name(name)
File "/usr/lib64/python2.7/unittest/loader.py", line 232, in _get_module_from_name
__import__(name)
File "/tmp/kitchen/testing/tests/integration/runners/test_nacl.py", line 15, in <module>
import libnacl.secret # pylint: disable=unused-import
File "/tmp/kitchen/testing/.nox/runtests-parametrized-2-7-coverage-true-crypto-none-transport-zeromq/local/lib/python2.7/site-packages/libnacl/__init__.py", line 126, in <module>
crypto_box_SEEDBYTES = nacl.crypto_box_seedbytes()
File "/usr/lib64/python2.7/ctypes/__init__.py", line 374, in __getattr__
func = self.__getitem__(name)
File "/usr/lib64/python2.7/ctypes/__init__.py", line 379, in __getitem__
func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /usr/lib64/libsodium.so.4: undefined symbol: crypto_box_seedbytes
ping @thatch45 should we just add this to a try/except or update docs for libsodium version requirements? im willing to put a PR together, just not sure the correct approach here.
It would make it easier to redistribute python-libnacl in Fedora[1] if each source file contains a license header. None of the source code files contains a license header. Can you update the source files with license header and confirm that all the files are licensed ASL 2.0?
Check https://bugzilla.redhat.com/show_bug.cgi?id=1399833#c3 and for details.
JSON unit tests fail on python 3.11.4;
======================================================================
ERROR: test_save_load (unit.test_save.TestSave.test_save_load)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/src/RPM/BUILD/python3-module-libnacl-1.7.1/tests/unit/test_save.py", line 32, in test_save_load
bob_load = libnacl.utils.load_key(bob_path)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/RPM/BUILD/python3-module-libnacl-1.7.1/libnacl/utils.py", line 34, in load_key
key_data = json.loads(stream.read(), encoding='UTF-8')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.11/json/__init__.py", line 359, in loads
return cls(**kw).decode(s)
^^^^^^^^^
TypeError: JSONDecoder.__init__() got an unexpected keyword argument 'encoding'
======================================================================
ERROR: test_save_load_secret (unit.test_save.TestSave.test_save_load_secret)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/src/RPM/BUILD/python3-module-libnacl-1.7.1/tests/unit/test_save.py", line 63, in test_save_load_secret
lbox = libnacl.utils.load_key(box_path)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/RPM/BUILD/python3-module-libnacl-1.7.1/libnacl/utils.py", line 34, in load_key
key_data = json.loads(stream.read(), encoding='UTF-8')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.11/json/__init__.py", line 359, in loads
return cls(**kw).decode(s)
^^^^^^^^^
TypeError: JSONDecoder.__init__() got an unexpected keyword argument 'encoding'
======================================================================
ERROR: test_save_load_sign (unit.test_save.TestSave.test_save_load_sign)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/src/RPM/BUILD/python3-module-libnacl-1.7.1/tests/unit/test_save.py", line 74, in test_save_load_sign
signer_load = libnacl.utils.load_key(sign_path)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/RPM/BUILD/python3-module-libnacl-1.7.1/libnacl/utils.py", line 34, in load_key
key_data = json.loads(stream.read(), encoding='UTF-8')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.11/json/__init__.py", line 359, in loads
return cls(**kw).decode(s)
^^^^^^^^^
TypeError: JSONDecoder.__init__() got an unexpected keyword argument 'encoding'
----------------------------------------------------------------------
Ran 65 tests in 0.040s
FAILED (errors=3)
error: Bad exit status from /usr/src/tmp/rpm-tmp.12559 (%check)
It seems that encoding
is removed since 3.9: https://docs.python.org/3/library/json.html#json.loads
Changed in version 3.9: The keyword argument encoding has been removed.
We want to have a compiled/not compiled option for deployment of libnacl, since compiled is slightly faster but ctypes is a little more flexible. We want to deliver the best of both worlds!
Hello.
Thank you for the very well documented and finely crafted software. We are currently evaluating and considering for developing a security infrastructure.
I have a query regarding dual keys. Ed25519 keys can be converted to Curve25519 keys (http://doc.libsodium.org/advanced/ed25519-curve25519.html). Is this the same relationship between the signing -> encryption keys in dual keys?
Also, more out of curiosity, can we generate sub-keys from either Ed25519/Curve25519 keys?
Thanks very much.
Sam
I want to add classes and methods that encapsulate the curvecp key handshake and messaging. The goal being to make it super easy to use libnacl for network communication very easy
libsodium is allowing a uint64_t subkey_id
for crypto_kdf_derive_from_key
, but ctypes seems to truncate the 64bit value.
A possible fix is to use ctypes.c_ulonglong(subkey_id)
in the snippet below.
Line 1185 in 5822ca3
How to reproduce:
subkey_id = 14303928198542443005
master_key = b"12345678901234567890123456789012"
buf = libnacl.crypto_kdf_derive_from_key(16, subkey_id, b"Context0", master_key)
print(buf.hex())
Result: 1e6d55e5b5abb245a32596f946926644
Result with the fix: 3fb440810c54617ab62096b56409a3e2
Reference C code:
uint8_t master_key[32] = "12345678901234567890123456789012";
uint64_t keyid = 14303928198542443005ULL;
uint8_t subkey[16];
crypto_kdf_derive_from_key(subkey, sizeof subkey, keyid, "Context0", master_key);
for (int i=0; i<sizeof subkey; i++)
printf("%02x", subkey[i]);
Result: 3fb440810c54617ab62096b56409a3e2
I think it'd be better if for each Object.x
where Object.hex_x()
exists, there would be Object.x_hex
instead.
Since this might introduce incompatibilities with previous versions, I create this ticket for the discussion on how to move forward (for example, this idea has not value at all :))
The 1.6.1 tarball on PyPI includes all the precompiled .pyc files. This causes a problem since they have file attributes that don't match for other folks. .pyc files shouldn't be included in the tarball.
We want to get all functions in libsodium wrapped
As the documentation doesn't clearly specify whether libnacl's Secret Key Encryption is actually authenticated encryption or not, I started reading the source code and eventually noticed this:
libnacl.secret.SecretBox calls libnacl.crypto_secretbox which calls the libsodium crypto_secretbox method...
The libsodium documentation contains the following statement:
The original NaCl
crypto_secretbox
API is also supported, albeit not recommended.
crypto_secretbox()
takes a pointer to 32 bytes before the message, and stores the ciphertext 16 bytes after the destination pointer, the first 16 bytes being overwritten with zeros.
crypto_secretbox_open()
takes a pointer to 16 bytes before the ciphertext and stores the message 32 bytes after the destination pointer, overwriting the first 32 bytes with zeros.The _easy and _detached APIs are faster and improve usability by not requiring padding, copying or tricky pointer arithmetic.
So, it appears libnacl can be made simpler/faster/more secure(?) by using the newer, simpler API. Unless compatibility with some other (older) implementations is required?
I would like to make things asynchronous, but if it doesn't release the GIL I would be only creating overhead when doing operations with libnacl on a separate thread.
I found nothing of the stuff mentioned in https://docs.python.org/3/c-api/init.html in the source code, so I assume it doesn't release it?
It seems that 1.8.0 was added to pypi without pushing the relevant commits/tag here.
When using libnacl-1.4.4 on centos-6.7 box, with libsodium-0.4.5-3.el6.x86_64 installed, which is the latest available version of package in EPEL repo for centos 6, I get this error:
Traceback (most recent call last): File "./decrypt_file.py", line 5, in <module> import libnacl.public File "/usr/lib/python2.6/site-packages/libnacl/__init__.py", line 127, in <module> crypto_verify_64_BYTES = nacl.crypto_verify_64_bytes() File "/usr/lib64/python2.6/ctypes/__init__.py", line 366, in __getattr__ func = self.__getitem__(name) File "/usr/lib64/python2.6/ctypes/__init__.py", line 371, in __getitem__ func = self._FuncPtr((name_or_ordinal, self)) AttributeError: /usr/lib64/libsodium.so.4: undefined symbol: crypto_verify_64_bytes
I found that crypto_verify_64_bytes support has been implemented in libsodium starting version libsodium-0.5.0.
I'd recommend to modify rpm spec file you provide, to reflect dependency on specific version of libsodium:
-Requires: libsodium
+Requires: libsodium >= 0.5.0
Note: libnacl-1.4.3 does not have this dependency and works fine with libsodium-0.4.5-3.el6.x86_64 on centos 6.7.
https://download.libsodium.org/doc/
sealed_boxes.html
I would like to add support in the salt nacl module for public key one way encryption.
This way people adding encrypted data to pillars wont need access to the private key that can decrypt.
salt-call nacl.pub_enc data="PASSWOTD" key="PUBKEY"
only the holder of the private key can render the pillar.
password: {{ salt['nacl.dec']('l8kuMoSUG5bStHO7Er08Wz/T10w8HDWfzgAIzbQ==') }}
In some applications it might be nice to not have to generate and manage both a signing key pair and an encryption key pair. Since Ed25519 and Curve25519 keys are birationally equivalent , one can be converted into the other. Libsodium has a function, to perform this conversion. The request to to expose this function in libnacl.
test_crypto_kdf_derive_from_key
segfaults on the ARM architecture due to incorrect use of ctypes
.
The information about argument/return types of C function is present in .h
files that aren't available at run time. So, when using ctypes, you need to declare argument types (technically only if different from defaults – but it's better to do it always). See ctypes documentation.
For kdf_derive_from_key
specifically, this should probably be:
nacl.crypto_kdf_derive_from_key.argtypes = [
ctypes.c_char_p, ctypes.c_size_t, ctypes.c_ulonglong, ctypes.c_char_p, ctypes.c_char_p,
]
Without that, subkey_id
is treated as C int
instead of uint64_t
. This is most likely causing #122 (Cannot use 64bit subkey ID) and the segfault on ARM architecture.
Take the above fix with a grain of NaCl though. I'm not really a ctypes
expert.
You probably know about tools like Cython or CFFI, which can use info in .h
files to avoid problems like this.
Use case: retrieve a key from server, then decrypt it. It could look like:
import requests
from Crypto.Cipher import AES
from filelike.wrappers import Decrypt
from libnacl.utils import load_key
enc = requests.get("http://example.com/keybackup.aes").raw # http://urllib3.readthedocs.org/
cipher = AES.new('abcdefgh')
dec = Decrypt(enc, cipher) # http://www.rfk.id.au/software/filelike/
key = load_key(dec)
And the same thing with saving.
Then, loading key from file would look like just load_key(open("abc.key", 'r'))
. For a smooth transition we could check if the first parameter is a string and open a file accordingly:
def load_key(fp, serial='json'):
if not is_filelike(fp, 'r'):
fp = open(fp, 'rb')
# ...
i'd like to use e.g. crypto_box_curve25519xsalsa20poly1305 rather than just crypto_box so i know what i get.
It seems that the RTD builds are failing due to the lack of libsodium.so
All salt projects do this, we will not be using setuptools by default.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.