Skip to content

Commit

Permalink
Load RSA PSS keys as regular RSA keys (#7112)
Browse files Browse the repository at this point in the history
* RSA PSS openssl constant

* load PSS keys (OpenSSL only) but strip the constraints

* empty commit for CI, sigh

* review feedback

* nit
  • Loading branch information
reaperhulk committed Apr 26, 2022
1 parent d7aac1c commit 34fd658
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -71,6 +71,12 @@ Changelog
to :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`. This
constant will set the salt length to the same length as the ``PSS`` hash
algorithm.
* Added support for loading RSA-PSS key types with
:func:`~cryptography.hazmat.primitives.serialization.load_pem_private_key`
and
:func:`~cryptography.hazmat.primitives.serialization.load_der_private_key`.
This functionality is limited to OpenSSL 1.1.1e+ and loads the key as a
normal RSA private key, discarding the PSS constraint information.

.. _v36-0-2:

Expand Down
3 changes: 3 additions & 0 deletions src/_cffi_src/openssl/cryptography.py
Expand Up @@ -70,6 +70,8 @@
(OPENSSL_VERSION_NUMBER < 0x10101020 || CRYPTOGRAPHY_IS_LIBRESSL)
#define CRYPTOGRAPHY_OPENSSL_LESS_THAN_111D \
(OPENSSL_VERSION_NUMBER < 0x10101040 || CRYPTOGRAPHY_IS_LIBRESSL)
#define CRYPTOGRAPHY_OPENSSL_LESS_THAN_111E \
(OPENSSL_VERSION_NUMBER < 0x10101050 || CRYPTOGRAPHY_IS_LIBRESSL)
#if (CRYPTOGRAPHY_OPENSSL_LESS_THAN_111D && !CRYPTOGRAPHY_IS_LIBRESSL && \
!defined(OPENSSL_NO_ENGINE)) || defined(USE_OSRANDOM_RNG_FOR_TESTING)
#define CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE 1
Expand All @@ -84,6 +86,7 @@

static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_111;
static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B;
static const int CRYPTOGRAPHY_OPENSSL_LESS_THAN_111E;
static const int CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE;

static const int CRYPTOGRAPHY_LIBRESSL_LESS_THAN_340;
Expand Down
7 changes: 7 additions & 0 deletions src/_cffi_src/openssl/evp.py
Expand Up @@ -16,6 +16,7 @@
typedef ... EVP_PKEY;
typedef ... EVP_PKEY_CTX;
static const int EVP_PKEY_RSA;
static const int EVP_PKEY_RSA_PSS;
static const int EVP_PKEY_DSA;
static const int EVP_PKEY_DH;
static const int EVP_PKEY_DHX;
Expand Down Expand Up @@ -293,4 +294,10 @@
#else
static const long Cryptography_HAS_EVP_PKEY_DH = 1;
#endif

// This can be removed when we drop OpenSSL 1.1.0 support
// OPENSSL_LESS_THAN_111
#if !defined(EVP_PKEY_RSA_PSS)
#define EVP_PKEY_RSA_PSS 912
#endif
"""
19 changes: 19 additions & 0 deletions src/cryptography/hazmat/backends/openssl/backend.py
Expand Up @@ -644,6 +644,25 @@ def _evp_pkey_to_private_key(self, evp_pkey) -> PRIVATE_KEY_TYPES:
return _RSAPrivateKey(
self, rsa_cdata, evp_pkey, self._rsa_skip_check_key
)
elif (
key_type == self._lib.EVP_PKEY_RSA_PSS
and not self._lib.CRYPTOGRAPHY_IS_LIBRESSL
and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL
and not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111E
):
# At the moment the way we handle RSA PSS keys is to strip the
# PSS constraints from them and treat them as normal RSA keys
# Unfortunately the RSA * itself tracks this data so we need to
# extract, serialize, and reload it without the constraints.
rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey)
self.openssl_assert(rsa_cdata != self._ffi.NULL)
rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
bio = self._create_mem_bio_gc()
res = self._lib.i2d_RSAPrivateKey_bio(bio, rsa_cdata)
self.openssl_assert(res == 1)
return self.load_der_private_key(
self._read_mem_bio(bio), password=None
)
elif key_type == self._lib.EVP_PKEY_DSA:
dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey)
self.openssl_assert(dsa_cdata != self._ffi.NULL)
Expand Down
62 changes: 62 additions & 0 deletions tests/hazmat/primitives/test_rsa.py
Expand Up @@ -11,6 +11,7 @@

from cryptography.exceptions import (
InvalidSignature,
UnsupportedAlgorithm,
_Reasons,
)
from cryptography.hazmat.primitives import hashes, serialization
Expand Down Expand Up @@ -256,6 +257,67 @@ def test_load_pss_vect_example_keys(self, pkcs1_example):
assert public_num.n == public_num2.n
assert public_num.e == public_num2.e

@pytest.mark.supported(
only_if=lambda backend: (
not backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
and not backend._lib.CRYPTOGRAPHY_IS_BORINGSSL
and not backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111E
),
skip_message="Does not support RSA PSS loading",
)
@pytest.mark.parametrize(
"path",
[
os.path.join("asymmetric", "PKCS8", "rsa_pss_2048.pem"),
os.path.join("asymmetric", "PKCS8", "rsa_pss_2048_hash.pem"),
os.path.join("asymmetric", "PKCS8", "rsa_pss_2048_hash_mask.pem"),
os.path.join(
"asymmetric", "PKCS8", "rsa_pss_2048_hash_mask_diff.pem"
),
os.path.join(
"asymmetric", "PKCS8", "rsa_pss_2048_hash_mask_salt.pem"
),
],
)
def test_load_pss_keys_strips_constraints(self, path, backend):
key = load_vectors_from_file(
filename=path,
loader=lambda p: serialization.load_pem_private_key(
p.read(), password=None
),
mode="rb",
)
# These keys have constraints that prohibit PKCS1v15 signing,
# but for now we load them without the constraint and test that
# it's truly removed by performing a disallowed signature.
assert isinstance(key, rsa.RSAPrivateKey)
signature = key.sign(b"whatever", padding.PKCS1v15(), hashes.SHA224())
key.public_key().verify(
signature, b"whatever", padding.PKCS1v15(), hashes.SHA224()
)

@pytest.mark.supported(
only_if=lambda backend: (
backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
or backend._lib.CRYPTOGRAPHY_IS_BORINGSSL
or backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111E
),
skip_message="Test requires a backend without RSA-PSS key support",
)
def test_load_pss_unsupported(self, backend):
# Key loading errors unfortunately have multiple paths so
# we need to allow ValueError and UnsupportedAlgorithm
with pytest.raises((UnsupportedAlgorithm, ValueError)):
load_vectors_from_file(
filename=os.path.join(
"asymmetric", "PKCS8", "rsa_pss_2048.pem"
),
loader=lambda p: serialization.load_pem_private_key(
p.read(), password=None
),
mode="rb",
)

@pytest.mark.parametrize(
"vector",
load_vectors_from_file(
Expand Down

0 comments on commit 34fd658

Please sign in to comment.