Skip to content

Commit 8d5d7be

Browse files
committed
Remove optional dependency on libnacl/libsodium
Now that the minimum supported version of OpenSSL in the Cryptograhy package is new enough to include support for chacha20-poly1305 and Edwards curves, AsyncSSH no longer needs to fall back to using libnacl and libsodium.
1 parent 00600f0 commit 8d5d7be

File tree

5 files changed

+121
-327
lines changed

5 files changed

+121
-327
lines changed

.github/workflows/run_tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ jobs:
6666
if: ${{ runner.os == 'Linux' }}
6767
run: |
6868
sudo apt update
69-
sudo apt install -y --no-install-recommends libnettle8 libsodium-dev libssl-dev libkrb5-dev ssh cmake ninja-build
69+
sudo apt install -y --no-install-recommends libnettle8 libssl-dev libkrb5-dev ssh cmake ninja-build
7070
7171
- name: Install macOS dependencies
7272
if: ${{ runner.os == 'macOS' }}
73-
run: brew install nettle liboqs libsodium
73+
run: brew install nettle liboqs
7474

7575
- name: Install nettle (Windows)
7676
if: ${{ runner.os == 'Windows' }}

README.rst

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,6 @@ functionality:
160160
if you want support for the OpenSSH post-quantum key exchange
161161
algorithms based on ML-KEM and SNTRUP.
162162

163-
* Install libsodium from https://github.com/jedisct1/libsodium
164-
and libnacl from https://pypi.python.org/pypi/libnacl if you have
165-
a version of OpenSSL older than 1.1.1b installed and you want
166-
support for Curve25519 key exchange, Ed25519 keys and certificates,
167-
or the Chacha20-Poly1305 cipher.
168-
169163
* Install libnettle from http://www.lysator.liu.se/~nisse/nettle/
170164
if you want support for UMAC cryptographic hashes.
171165

@@ -182,28 +176,26 @@ easy to install any or all of these dependencies:
182176
| bcrypt
183177
| fido2
184178
| gssapi
185-
| libnacl
186179
| pkcs11
187180
| pyOpenSSL
188181
| pywin32
189182
190-
For example, to install bcrypt, fido2, gssapi, libnacl, pkcs11, and
191-
pyOpenSSL on UNIX, you can run:
183+
For example, to install bcrypt, fido2, gssapi, pkcs11, and pyOpenSSL
184+
on UNIX, you can run:
192185

193186
::
194187

195-
pip install 'asyncssh[bcrypt,fido2,gssapi,libnacl,pkcs11,pyOpenSSL]'
188+
pip install 'asyncssh[bcrypt,fido2,gssapi,pkcs11,pyOpenSSL]'
196189

197-
To install bcrypt, fido2, libnacl, pkcs11, pyOpenSSL, and pywin32 on
198-
Windows, you can run:
190+
To install bcrypt, fido2, pkcs11, pyOpenSSL, and pywin32 on Windows,
191+
you can run:
199192

200193
::
201194

202-
pip install 'asyncssh[bcrypt,fido2,libnacl,pkcs11,pyOpenSSL,pywin32]'
195+
pip install 'asyncssh[bcrypt,fido2,pkcs11,pyOpenSSL,pywin32]'
203196

204-
Note that you will still need to manually install the libsodium library
205-
listed above for libnacl to work correctly and/or libnettle for UMAC
206-
support. Unfortunately, since liboqs, libsodium, and libnettle are not
197+
Note that you will still need to manually install the libnettle library
198+
for UMAC support. Unfortunately, since liboqs and libnettle are not
207199
Python packages, they cannot be directly installed using pip.
208200

209201
Installing the development branch

asyncssh/crypto/__init__.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@
6262
'BasicCipher', 'ChachaCipher', 'CryptoKey', 'Curve25519DH', 'Curve448DH',
6363
'DH', 'DSAPrivateKey', 'DSAPublicKey', 'ECDH', 'ECDSAPrivateKey',
6464
'ECDSAPublicKey', 'EdDSAPrivateKey', 'EdDSAPublicKey', 'GCMCipher', 'PQDH',
65-
'PyCAKey', 'RSAPrivateKey', 'RSAPublicKey', 'chacha_available',
66-
'curve25519_available', 'curve448_available', 'X509Certificate',
67-
'X509Name', 'X509NamePattern', 'ed25519_available', 'ed448_available',
68-
'generate_x509_certificate', 'get_cipher_params', 'import_x509_certificate',
69-
'lookup_ec_curve_by_params', 'mlkem_available', 'pbkdf2_hmac',
70-
'register_cipher', 'sntrup_available', 'umac32', 'umac64', 'umac96',
71-
'umac128'
65+
'PyCAKey', 'RSAPrivateKey', 'RSAPublicKey', 'X509Certificate',
66+
'X509Name', 'X509NamePattern', 'chacha_available', 'curve25519_available',
67+
'curve448_available', 'ed25519_available', 'ed448_available',
68+
'generate_x509_certificate', 'get_cipher_params',
69+
'import_x509_certificate', 'lookup_ec_curve_by_params', 'mlkem_available',
70+
'pbkdf2_hmac', 'register_cipher', 'sntrup_available', 'umac32', 'umac64',
71+
'umac96', 'umac128'
7272
]

asyncssh/crypto/chacha.py

Lines changed: 22 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
"""Chacha20-Poly1305 symmetric encryption handler"""
2222

23-
from ctypes import c_ulonglong, create_string_buffer
2423
from typing import Optional, Tuple
2524

2625
from cryptography.exceptions import InvalidSignature
@@ -32,97 +31,39 @@
3231
from .cipher import register_cipher
3332

3433

35-
if backend.poly1305_supported():
36-
_CTR_0 = (0).to_bytes(8, 'little')
37-
_CTR_1 = (1).to_bytes(8, 'little')
34+
chacha_available = backend.poly1305_supported()
3835

39-
_POLY1305_KEYBYTES = 32
4036

41-
def chacha20(key: bytes, data: bytes, nonce: bytes, ctr: int) -> bytes:
42-
"""Encrypt/decrypt a block of data with the ChaCha20 cipher"""
37+
_CTR_0 = (0).to_bytes(8, 'little')
38+
_CTR_1 = (1).to_bytes(8, 'little')
4339

44-
return Cipher(ChaCha20(key, (_CTR_1 if ctr else _CTR_0) + nonce),
45-
mode=None).encryptor().update(data)
40+
_POLY1305_KEYBYTES = 32
4641

47-
def poly1305_key(key: bytes, nonce: bytes) -> bytes:
48-
"""Derive a Poly1305 key"""
42+
def chacha20(key: bytes, data: bytes, nonce: bytes, ctr: int) -> bytes:
43+
"""Encrypt/decrypt a block of data with the ChaCha20 cipher"""
4944

50-
return chacha20(key, _POLY1305_KEYBYTES * b'\0', nonce, 0)
45+
return Cipher(ChaCha20(key, (_CTR_1 if ctr else _CTR_0) + nonce),
46+
mode=None).encryptor().update(data)
5147

52-
def poly1305(key: bytes, data: bytes, nonce: bytes) -> bytes:
53-
"""Compute a Poly1305 tag for a block of data"""
48+
def poly1305_key(key: bytes, nonce: bytes) -> bytes:
49+
"""Derive a Poly1305 key"""
5450

55-
return Poly1305.generate_tag(poly1305_key(key, nonce), data)
51+
return chacha20(key, _POLY1305_KEYBYTES * b'\0', nonce, 0)
5652

57-
def poly1305_verify(key: bytes, data: bytes,
58-
nonce: bytes, tag: bytes) -> bool:
59-
"""Verify a Poly1305 tag for a block of data"""
53+
def poly1305(key: bytes, data: bytes, nonce: bytes) -> bytes:
54+
"""Compute a Poly1305 tag for a block of data"""
6055

61-
try:
62-
Poly1305.verify_tag(poly1305_key(key, nonce), data, tag)
63-
return True
64-
except InvalidSignature:
65-
return False
56+
return Poly1305.generate_tag(poly1305_key(key, nonce), data)
6657

67-
chacha_available = True
68-
else: # pragma: no cover
69-
try:
70-
from libnacl import nacl
71-
72-
_chacha20 = nacl.crypto_stream_chacha20
73-
_chacha20_xor_ic = nacl.crypto_stream_chacha20_xor_ic
74-
75-
_POLY1305_BYTES = nacl.crypto_onetimeauth_poly1305_bytes()
76-
_POLY1305_KEYBYTES = nacl.crypto_onetimeauth_poly1305_keybytes()
77-
78-
_poly1305 = nacl.crypto_onetimeauth_poly1305
79-
_poly1305_verify = nacl.crypto_onetimeauth_poly1305_verify
80-
81-
def chacha20(key: bytes, data: bytes, nonce: bytes, ctr: int) -> bytes:
82-
"""Encrypt/decrypt a block of data with the ChaCha20 cipher"""
83-
84-
datalen = len(data)
85-
result = create_string_buffer(datalen)
86-
ull_datalen = c_ulonglong(datalen)
87-
ull_ctr = c_ulonglong(ctr)
88-
89-
_chacha20_xor_ic(result, data, ull_datalen, nonce, ull_ctr, key)
90-
91-
return result.raw
92-
93-
def poly1305_key(key: bytes, nonce: bytes) -> bytes:
94-
"""Derive a Poly1305 key"""
95-
96-
polykey = create_string_buffer(_POLY1305_KEYBYTES)
97-
ull_polykeylen = c_ulonglong(_POLY1305_KEYBYTES)
58+
def poly1305_verify(key: bytes, data: bytes,
59+
nonce: bytes, tag: bytes) -> bool:
60+
"""Verify a Poly1305 tag for a block of data"""
9861

99-
_chacha20(polykey, ull_polykeylen, nonce, key)
100-
101-
return polykey.raw
102-
103-
def poly1305(key: bytes, data: bytes, nonce: bytes) -> bytes:
104-
"""Compute a Poly1305 tag for a block of data"""
105-
106-
tag = create_string_buffer(_POLY1305_BYTES)
107-
ull_datalen = c_ulonglong(len(data))
108-
polykey = poly1305_key(key, nonce)
109-
110-
_poly1305(tag, data, ull_datalen, polykey)
111-
112-
return tag.raw
113-
114-
def poly1305_verify(key: bytes, data: bytes,
115-
nonce: bytes, tag: bytes) -> bool:
116-
"""Verify a Poly1305 tag for a block of data"""
117-
118-
ull_datalen = c_ulonglong(len(data))
119-
polykey = poly1305_key(key, nonce)
120-
121-
return _poly1305_verify(tag, data, ull_datalen, polykey) == 0
122-
123-
chacha_available = True
124-
except (ImportError, OSError, AttributeError):
125-
chacha_available = False
62+
try:
63+
Poly1305.verify_tag(poly1305_key(key, nonce), data, tag)
64+
return True
65+
except InvalidSignature:
66+
return False
12667

12768

12869
class ChachaCipher:

0 commit comments

Comments
 (0)