support RSA PSS for CRLs (#10013)

adds rsa_padding kwarg to sign and also adds
signature_algorithm_parameters as a method to CRLs
This commit is contained in:
Paul Kehrer 2023-12-18 16:54:38 -06:00 committed by GitHub
parent 9ca6fd1e15
commit 2525eb048a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 157 additions and 21 deletions

View file

@ -18,12 +18,16 @@ Changelog
values, as documented in the 41.0.2 release notes.
* Updated the minimum supported Rust version (MSRV) to 1.63.0, from 1.56.0.
* Support :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS` for
X.509 certificate signing requests with the keyword-only argument
``rsa_padding`` on
:meth:`~cryptography.x509.CertificateSigningRequestBuilder.sign`.
X.509 certificate signing requests and certificate revocation lists with the
keyword-only argument ``rsa_padding`` on the ``sign`` methods for
:class:`~cryptography.x509.CertificateSigningRequestBuilder` and
:class:`~cryptography.x509.CertificateRevocationListBuilder`.
* Added support for obtaining X.509 certificate signing request signature
algorithm parameters (including PSS) via
:meth:`~cryptography.x509.CertificateSigningRequest.signature_algorithm_parameters`.
* Added support for obtaining X.509 certificate revocation list signature
algorithm parameters (including PSS) via
:meth:`~cryptography.x509.CertificateRevocationList.signature_algorithm_parameters`.
* Added `mgf` property to
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`.
* Added `algorithm` and `mgf` properties to

View file

@ -716,6 +716,27 @@ X.509 CRL (Certificate Revocation List) Object
>>> crl.signature_algorithm_oid
<ObjectIdentifier(oid=1.2.840.113549.1.1.11, name=sha256WithRSAEncryption)>
.. attribute:: signature_algorithm_parameters
.. versionadded:: 42.0.0
Returns the parameters of the signature algorithm used to sign the
certificate revocation list. For RSA signatures it will return either a
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15` or
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS` object.
For ECDSA signatures it will
return an :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDSA`.
For EdDSA and DSA signatures it will return ``None``.
These objects can be used to verify the CRL signature.
:returns: None,
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`,
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`, or
:class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDSA`
.. attribute:: issuer
:type: :class:`Name`
@ -1212,7 +1233,7 @@ X.509 Certificate Revocation List Builder
obtained from an existing CRL or created with
:class:`~cryptography.x509.RevokedCertificateBuilder`.
.. method:: sign(private_key, algorithm)
.. method:: sign(private_key, algorithm, *, rsa_padding=None)
Sign this CRL using the CA's private key.
@ -1231,6 +1252,22 @@ X.509 Certificate Revocation List Builder
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
otherwise.
:param rsa_padding:
.. versionadded:: 42.0.0
This is a keyword-only argument. If ``private_key`` is an
``RSAPrivateKey`` then this can be set to either
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15` or
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS` to sign
with those respective paddings. If this is ``None`` then RSA
keys will default to ``PKCS1v15`` padding. All other key types **must**
not pass a value other than ``None``.
:type rsa_padding: ``None``,
:class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`,
or :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`
:returns: :class:`~cryptography.x509.CertificateRevocationList`
X.509 Revoked Certificate Object

View file

@ -24,18 +24,19 @@ def create_x509_certificate(
builder: x509.CertificateBuilder,
private_key: PrivateKeyTypes,
hash_algorithm: hashes.HashAlgorithm | None,
padding: PKCS1v15 | PSS | None,
rsa_padding: PKCS1v15 | PSS | None,
) -> x509.Certificate: ...
def create_x509_csr(
builder: x509.CertificateSigningRequestBuilder,
private_key: PrivateKeyTypes,
hash_algorithm: hashes.HashAlgorithm | None,
padding: PKCS1v15 | PSS | None,
rsa_padding: PKCS1v15 | PSS | None,
) -> x509.CertificateSigningRequest: ...
def create_x509_crl(
builder: x509.CertificateRevocationListBuilder,
private_key: PrivateKeyTypes,
hash_algorithm: hashes.HashAlgorithm | None,
rsa_padding: PKCS1v15 | PSS | None,
) -> x509.CertificateRevocationList: ...
def create_server_verifier(
name: x509.verification.Subject,

View file

@ -423,6 +423,15 @@ class CertificateRevocationList(metaclass=abc.ABCMeta):
Returns the ObjectIdentifier of the signature algorithm.
"""
@property
@abc.abstractmethod
def signature_algorithm_parameters(
self,
) -> None | padding.PSS | padding.PKCS1v15 | ec.ECDSA:
"""
Returns the signature algorithm parameters.
"""
@property
@abc.abstractmethod
def issuer(self) -> Name:
@ -1146,6 +1155,8 @@ class CertificateRevocationListBuilder:
private_key: CertificateIssuerPrivateKeyTypes,
algorithm: _AllowedHashTypes | None,
backend: typing.Any = None,
*,
rsa_padding: padding.PSS | padding.PKCS1v15 | None = None,
) -> CertificateRevocationList:
if self._issuer_name is None:
raise ValueError("A CRL must have an issuer name")
@ -1156,7 +1167,15 @@ class CertificateRevocationListBuilder:
if self._next_update is None:
raise ValueError("A CRL must have a next update time")
return rust_x509.create_x509_crl(self, private_key, algorithm)
if rsa_padding is not None:
if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
raise TypeError("Padding must be PSS or PKCS1v15")
if not isinstance(private_key, rsa.RSAPrivateKey):
raise TypeError("Padding is only supported for RSA keys")
return rust_x509.create_x509_crl(
self, private_key, algorithm, rsa_padding
)
class RevokedCertificateBuilder:

View file

@ -194,6 +194,17 @@ impl CertificateRevocationList {
}
}
#[getter]
fn signature_algorithm_parameters<'p>(
&'p self,
py: pyo3::Python<'p>,
) -> CryptographyResult<&'p pyo3::PyAny> {
sign::identify_signature_algorithm_parameters(
py,
&self.owned.borrow_dependent().signature_algorithm,
)
}
#[getter]
fn signature(&self) -> &[u8] {
self.owned.borrow_dependent().signature_value.as_bytes()
@ -594,13 +605,10 @@ fn create_x509_crl(
builder: &pyo3::PyAny,
private_key: &pyo3::PyAny,
hash_algorithm: &pyo3::PyAny,
rsa_padding: &pyo3::PyAny,
) -> CryptographyResult<CertificateRevocationList> {
let sigalg = x509::sign::compute_signature_algorithm(
py,
private_key,
hash_algorithm,
py.None().into_ref(py),
)?;
let sigalg =
x509::sign::compute_signature_algorithm(py, private_key, hash_algorithm, rsa_padding)?;
let mut revoked_certs = vec![];
for py_revoked_cert in builder
.getattr(pyo3::intern!(py, "_revoked_certificates"))?
@ -648,13 +656,8 @@ fn create_x509_crl(
};
let tbs_bytes = asn1::write_single(&tbs_cert_list)?;
let signature = x509::sign::sign_data(
py,
private_key,
hash_algorithm,
py.None().into_ref(py),
&tbs_bytes,
)?;
let signature =
x509::sign::sign_data(py, private_key, hash_algorithm, rsa_padding, &tbs_bytes)?;
let data = asn1::write_single(&crl::CertificateRevocationList {
tbs_cert_list,
signature_algorithm: sigalg,

View file

@ -10,7 +10,13 @@ import pytest
from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec, ed448, ed25519, rsa
from cryptography.hazmat.primitives.asymmetric import (
ec,
ed448,
ed25519,
padding,
rsa,
)
from cryptography.x509.oid import (
AuthorityInformationAccessOID,
NameOID,
@ -204,6 +210,38 @@ class TestCertificateRevocationListBuilder:
with pytest.raises(ValueError):
builder.sign(private_key, hashes.SHA256(), backend)
def test_sign_invalid_padding(
self, rsa_key_2048: rsa.RSAPrivateKey, backend
):
last_update = datetime.datetime(2002, 1, 1, 12, 1)
next_update = datetime.datetime(2030, 1, 1, 12, 1)
builder = (
x509.CertificateRevocationListBuilder()
.issuer_name(
x509.Name(
[
x509.NameAttribute(
NameOID.COMMON_NAME, "cryptography.io CA"
)
]
)
)
.last_update(last_update)
.next_update(next_update)
)
with pytest.raises(TypeError):
builder.sign(
rsa_key_2048,
hashes.SHA256(),
rsa_padding=b"notapadding", # type: ignore[arg-type]
)
eckey = ec.generate_private_key(ec.SECP256R1())
with pytest.raises(TypeError):
builder.sign(
eckey, hashes.SHA256(), rsa_padding=padding.PKCS1v15()
)
def test_sign_empty_list(self, rsa_key_2048: rsa.RSAPrivateKey, backend):
private_key = rsa_key_2048
last_update = datetime.datetime(2002, 1, 1, 12, 1)
@ -235,6 +273,40 @@ class TestCertificateRevocationListBuilder:
tzinfo=datetime.timezone.utc
)
def test_sign_pss(self, rsa_key_2048: rsa.RSAPrivateKey, backend):
private_key = rsa_key_2048
last_update = datetime.datetime(2002, 1, 1, 12, 1)
next_update = datetime.datetime(2030, 1, 1, 12, 1)
builder = (
x509.CertificateRevocationListBuilder()
.issuer_name(
x509.Name(
[
x509.NameAttribute(
NameOID.COMMON_NAME, "cryptography.io CA"
)
]
)
)
.last_update(last_update)
.next_update(next_update)
)
pss = padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.DIGEST_LENGTH,
)
crl = builder.sign(private_key, hashes.SHA256(), rsa_padding=pss)
assert len(crl) == 0
assert isinstance(crl.signature_algorithm_parameters, padding.PSS)
assert crl.signature_algorithm_parameters._salt_length == 32
private_key.public_key().verify(
crl.signature,
crl.tbs_certlist_bytes,
crl.signature_algorithm_parameters,
hashes.SHA256(),
)
@pytest.mark.parametrize(
"extension",
[