-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for ChaCha20 with LibreSSL #9209
Conversation
58e6efe
to
2cc4a53
Compare
@@ -226,6 +229,12 @@ def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool: | |||
return self.hash_supported(algorithm) | |||
|
|||
def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool: | |||
# ChaCha20 is supported in LibreSSL by a different API than OpenSSL, | |||
# so checking for the corresponding EVP_CIPHER is not useful | |||
if (self._lib.CRYPTOGRAPHY_IS_LIBRESSL) and isinstance( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (self._lib.CRYPTOGRAPHY_IS_LIBRESSL) and isinstance( | |
if self._lib.CRYPTOGRAPHY_IS_LIBRESSL and isinstance( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
@@ -14,7 +15,114 @@ | |||
from cryptography.hazmat.backends.openssl.backend import Backend | |||
|
|||
|
|||
class _CipherContext: | |||
class _CipherContext(metaclass=abc.ABCMeta): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is a new interface required? This seems to match our existing public cipher context ABC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's an interface for the backend cipher context, which needs a couple of methods not present in the existing public CipherContext
ABC:
@abc.abstractmethod
def authenticate_additional_data(self, data: bytes) -> None:
...
@abc.abstractmethod
def finalize_with_tag(self, tag: bytes) -> bytes:
...
I think it also makes sense to have separate interfaces for public vs backend ctx, since the public context wraps the backend one:
def _wrap_ctx(
self, ctx: _BackendCipherContext, encrypt: bool
) -> typing.Union[
AEADEncryptionContext, AEADDecryptionContext, CipherContext
]:
if isinstance(self.mode, modes.ModeWithAuthenticationTag):
if encrypt:
return _AEADEncryptionContext(ctx)
else:
return _AEADDecryptionContext(ctx)
else:
return _CipherContext(ctx)
total_data_len, | ||
self._backend._ffi.from_buffer(self._cipher.key), | ||
self._backend._ffi.from_buffer(self._iv_nonce), | ||
self._counter, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, shouldn't counter be advancing across multiple calls?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I missed that completely. I have rewritten the update_into()
function as per our discussion
1e90828
to
6cffe5a
Compare
The current CI failure is due to not having coverage of these lines (the counter rollover during 64-bit overflow). Once the test vectors from this PR are merged, the coverage should go to 100% |
Ok. @reaperhulk has the TODO to review that one, once it merges I'll look here. |
6cffe5a
to
b446e66
Compare
b446e66
to
3ad3d35
Compare
@alex The test vectors PR was merged and now the coverage for this code is 100% |
I now realize I'm doing this out of order, but did we ever talk to the LibreSSL maintainers about if they'd be interested in an EVP API for this? |
We haven't, I believe, because the main goal for these PRs was to improve BoringSSL feature parity, and this LibreSSL implementation was just a bonus due to the LibreSSL API being the same as BoringSSL's. My idea was to follow up this PR with the BoringSSL implementation (which is trivial once this LibreSSL PR is merged). I don't know if we're currently interested in implementing the EVP API for ChaCha20 in LibreSSL (@woodruffw thoughts?) |
I believe that doing that implementation in LibreSSL would be out of scope for us. That being said, we can certainly raise it upstream and see if they'd be interested in exposing ChaCha20 via Would you prefer we do that before moving forwards here @alex? |
I must be missing something. EVP_chacha20 has existed in LibreSSL since forever (it was committed on May 1, 2014, not even a month into the fork's existence). Regrettably, we adjusted it to be compatible with OpenSSL for OpenSSH to be able to use the OpenSSL 1.1 API in early 2020, even if that is wrong per spec. |
Thanks for the clarification, I now see why I thought the EVP API was not available in LibreSSL: Turns out that However, the error was because the name for ChaCha20 in OpenSSL is different from LibreSSL. OpenSSL uses #define SN_chacha20 "ChaCha20"
#define LN_chacha20 "chacha20"
#define NID_chacha20 1019 Whereas LibresSSL uses
I'll close this PR and open a new one that conditionally changes the name according to the backend used. cc @woodruffw |
I see.
@botovq Do you know why the names don't match?
There will be no deeper reason than that it was added one way to
LibreSSL and the other way to OpenSSL and no one noticed in the
following 9 years...
I guess there is no harm in adding an alias on our side to make
OpenSSL's naming choice work, but that won't be available before 3.9.0
|
Closing in favor of #9758 |
OpenSSL has the 20 in the long and short names, so add aliases to the existing names to make things work. In particular, EVP_get_cipherbyname() will now return EVP_chacha20() for both 'ChaCha20' and 'chacha20'. Found by Facundo Tuesca when trying to add LibreSSL support for ChaCha20 in pyca/cryptography#9209 ok jsing
OpenSSL has the 20 in the long and short names, so add aliases to the existing names to make things work. In particular, EVP_get_cipherbyname() will now return EVP_chacha20() for both 'ChaCha20' and 'chacha20'. Found by Facundo Tuesca when trying to add LibreSSL support for ChaCha20 in pyca/cryptography#9209 ok jsing
This PR adds support for ChaCha20 with LibreSSL. Since cryptography's ChaCha20 uses OpenSSL's implementation (which uses a 64 bit counter + 64 bit nonce), and LibreSSL does the same, it is straightforward to use LibreSSL's API.
The only complication is that the current
_CipherContext
implementation assumes that the underlying cipher can be accessed through theEVP_CIPHER
API. This is not the case for LibreSSL's ChaCha20, which has a separate CRYPTO_chacha_20() API for it.In order to solve that, this PR makes
_CipherContext
an abstract class with two implementations,_CipherContextEVP
(which is the previous_CipherContext
) and_CipherContextChaCha
, which is a simple context that is used only for the LibreSSL+ChaCha20 combination.This means all of the code that uses
_CipherContext
remains the same, and we only have a single branching point isolated in thecreate_cipher_context()
function, which selects the correct context depending on the cipher+backend.It also leaves the option open in the future if we want to add BoringSSL's ChaCha20, since we can reuse the same
_CipherContextChaCha
for it (the API is almost the same, and we can deal with the differences in a C wrapper)