Repository navigation
Conversation
Since 1.8 replaced PyNaCl, segment encryption and decryption hold the GIL, so threads run one at a time. Release it around the libsodium calls. The Py_buffer views keep the buffers alive and unresizable meanwhile. Also reject keys that are not 32 bytes, and return NULL when the key exchange fails or kx_client gets bad arguments. Before, these paths returned a value with an exception set, which Python reported as SystemError.
The testsuite only ran on pushes, so pull requests from forks got no CI. Run it on pull requests and on demand too, with a read-only token and without persisting credentials in the checkout. A newer push cancels the older runs of the same branch or pull request.
The testsuite encrypts and decrypts with the same code. A bug that both directions share, like a wrong hash in the key exchange, passes every test but breaks all existing files. Decrypt two files that the released 1.8.6 wrote, one sent with a crypt4gh key and one with an ssh key. Checking the sender also covers the conversion of ssh public keys.
|
Hi @silverdaz, thanks for reading through all of them.
Agreed, this PR is not the place for it. I'd still strongly recommend adding such a test for crypt4gh later. Done right, such tests could also be reused for other implementations.
I'm sorry for having created this feeling. I split the work into three parts that build on each other, each adding fewer than 300 lines:
Most deletions in part 3 remove sodium.c and the libsodium build. These links show only the new commits of each PR:
I'm happy to split or reorder them differently if that helps.
I didn't know about the version 2 work. Is there a branch I could look at? I'm happy to rebase onto it. |
tl;dr
crypt4gh.sodiumholds the GIL while ChaCha20-Poly1305 runs, so threads that encrypt or decrypt run one at a time. Up to 1.7, PyNaCl released it. This PR releases it around the libsodium calls.chacha20poly1305_encrypt/decryptnow reject keys that aren't 32 bytes.kx_server/kx_clientnow raise their own error instead of aSystemError.GIL
PyNaCl is built with cffi, and cffi releases the GIL around every C call. The 1.8 bindings don't, so parallel callers serialize.
The libsodium calls now run between
Py_BEGIN_ALLOW_THREADSandPy_END_ALLOW_THREADS. That's safe because thePy_bufferviews keep the buffers alive and prevent resizing until they are released. The key exchange and key conversion functions run once per header, so they keep the GIL.With 4 threads, ChaCha20-Poly1305 throughput now scales by 2.5x to 3x. Before, it dropped below the single-thread rate.
Key sizes
key_lenwas commented out, but libsodium always readscrypto_aead_chacha20poly1305_ietf_KEYBYTES(32) bytes of key. Both functions now raiseValueError("Invalid key size")for any other length, as PyNaCl did.kx error paths
When
crypto_kx_*_session_keysfailed, e.g. for an all-zero peer key, the functions set aValueErrorbut still returned a key. Python turns that intoSystemError: <built-in function kx_client> returned a result with an exception set. They now returnNULL, so theValueErrorcomes through.kx_clienthad the same pattern (Py_RETURN_NONEwith an exception set) when argument parsing failed.CI on pull requests
testsuite.ymlonly ran on pushes, so PRs from forks got no CI. It now also runs onpull_requestandworkflow_dispatch, with a read-only token (permissions: contents: read) andpersist-credentials: false. Aconcurrencygroup cancels runs that a newer push replaced. For a branch of this repo with an open PR, the testsuite now runs twice, once per event.Test with files from 1.8.6
bats encrypts and decrypts with the same code. A bug shared by both directions passes every test and still breaks all existing files.
tests/compatibility.batsdecrypts two files that the released 1.8.6 wrote for Alice, one sent with Bob's crypt4gh key and one with Bob's ssh key. For the second,--sender_pkalso checks the conversion of Bob's ssh public key. Only the public half of that ssh test key is committed.Stack
This PR is part 1 of 3. Two PRs build on it: