diff options
| author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2023-05-07 15:26:45 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-07 20:26:45 +0000 |
| commit | b436fafa7cf43c96f66d50162ac495c99ade1f39 (patch) | |
| tree | 20a69e87bbc1cc2c60c1059941b9c9e88a86adaf | |
| parent | 8834b590ede72c79532ccd34857a6904c48d3634 (diff) | |
| download | cryptography-b436fafa7cf43c96f66d50162ac495c99ade1f39.tar.gz | |
add signature_algorithm_parameters to certificate (#8795)
this allows easier verification of cert signatures, but more
specifically allows PSS signature verification
| -rw-r--r-- | CHANGELOG.rst | 3 | ||||
| -rw-r--r-- | docs/x509/reference.rst | 52 | ||||
| -rw-r--r-- | src/cryptography/x509/base.py | 10 | ||||
| -rw-r--r-- | src/rust/cryptography-x509/src/common.rs | 57 | ||||
| -rw-r--r-- | src/rust/cryptography-x509/src/oid.rs | 13 | ||||
| -rw-r--r-- | src/rust/src/x509/certificate.rs | 130 | ||||
| -rw-r--r-- | tests/x509/test_x509.py | 92 |
7 files changed, 338 insertions, 19 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fcc6f28cb..d4fd57624 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -20,6 +20,9 @@ Changelog * Implemented support for equality checks on all asymmetric public key types. * Added support for ``aes256-gcm@openssh.com`` encrypted keys in :func:`~cryptography.hazmat.primitives.serialization.load_ssh_private_key`. +* Added support for obtaining X.509 certificate signature algorithm parameters + (including PSS) via + :meth:`~cryptography.x509.Certificate.signature_algorithm_parameters`. .. _v40-0-2: diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index 71a6eb179..647666c5c 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -146,6 +146,30 @@ X.509 Reference -----END CERTIFICATE----- """.strip() + rsa_pss_pem_cert = b""" + -----BEGIN CERTIFICATE----- + MIIDfTCCAjCgAwIBAgIUP4D/5rcT93vdYGPhsKf+hbes/JgwQgYJKoZIhvcNAQEK + MDWgDzANBglghkgBZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEF + AKIEAgIA3jAaMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wHhcNMjIwNDMwMjAz + MTE4WhcNMzMwNDEyMjAzMTE4WjAaMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8w + ggEgMAsGCSqGSIb3DQEBCgOCAQ8AMIIBCgKCAQEAt1jpboUoNppBVamc+nA+zEjl + jn/gPbRFCvyveRd8Yr0p8y1mlmjKXcQlXcHPVM4TopgFXqDykIHXxJxLV56ysb4K + UGe0nxpmhEso5ZGUgkDIIoH0NAQAsS8rS2ZzNJcLrLGrMY6DRgFsa+G6h2DvMwgl + nsX++a8FIm7Vu+OZnfWpDEuhJU4TRtHVviJSYkFMckyYBB48k1MU+0b4pezHconZ + mMEisBFFbwarNvowf2i/tRESe3myKXfiJsZZ2UzdE3FqycSgw1tx8qV/Z8myozUW + uihIdw8TGbbsJhEeVFxQEP/DVzC6HHDI3EVpr2jPYeIE60hhZwM7jUmQscLerQID + AQABo1MwUTAdBgNVHQ4EFgQUb1QD8QEIQn5DALIAujTDATssNcQwHwYDVR0jBBgw + FoAUb1QD8QEIQn5DALIAujTDATssNcQwDwYDVR0TAQH/BAUwAwEB/zBCBgkqhkiG + 9w0BAQowNaAPMA0GCWCGSAFlAwQCAQUAoRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFl + AwQCAQUAogQCAgDeA4IBAQAvKBXlx07tdmtfhNTPn16dupBIS5344ZE4tfGSE5Ir + iA1X0bukKQ6V+6xJXGreaIw0wvwtIeI/R0JwcR114HBDqjt40vklyNSpGCJzgkfD + Q/d8JXN/MLyQrk+5F9JMy+HuZAgefAQAjugC6389Klpqx2Z1CgwmALhjIs48GnMp + Iz9vU2O6RDkMBlBRdmfkJVjhhPvJYpDDW1ic5O3pxtMoiC1tAHHMm4gzM1WCFeOh + cDNxABlvVNPTnqkOhKBmmwRaBwdvvksgeu2RyBNR0KEy44gWzYB9/Ter2t4Z8ASq + qCv8TuYr2QGaCnI2FVS5S9n6l4JNkFHqPMtuhrkr3gEz + -----END CERTIFICATE----- + """.strip() + Loading Certificates ~~~~~~~~~~~~~~~~~~~~ @@ -413,6 +437,34 @@ X.509 Certificate Object >>> cert.signature_algorithm_oid <ObjectIdentifier(oid=1.2.840.113549.1.1.11, name=sha256WithRSAEncryption)> + .. attribute:: signature_algorithm_parameters + + .. versionadded:: 41.0.0 + + Returns the parameters of the signature algorithm used to sign the + certificate. 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 signatures on the certificate. + + :returns: None, + :class:`~cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15`, + :class:`~cryptography.hazmat.primitives.asymmetric.padding.PSS`, or + :class:`~cryptography.hazmat.primitives.asymmetric.ec.ECDSA` + + .. doctest:: + + >>> from cryptography.hazmat.primitives.asymmetric import padding + >>> pss_cert = x509.load_pem_x509_certificate(rsa_pss_pem_cert) + >>> isinstance(pss_cert.signature_algorithm_parameters, padding.PSS) + True + .. attribute:: extensions :type: :class:`Extensions` diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py index 63eaa6bd4..64453eb70 100644 --- a/src/cryptography/x509/base.py +++ b/src/cryptography/x509/base.py @@ -17,6 +17,7 @@ from cryptography.hazmat.primitives.asymmetric import ( ec, ed448, ed25519, + padding, rsa, x448, x25519, @@ -234,6 +235,15 @@ class Certificate(metaclass=abc.ABCMeta): @property @abc.abstractmethod + def signature_algorithm_parameters( + self, + ) -> typing.Union[None, padding.PSS, padding.PKCS1v15, ec.ECDSA]: + """ + Returns the signature algorithm parameters. + """ + + @property + @abc.abstractmethod def extensions(self) -> Extensions: """ Returns an Extensions object. diff --git a/src/rust/cryptography-x509/src/common.rs b/src/rust/cryptography-x509/src/common.rs index 9668ae237..d09971659 100644 --- a/src/rust/cryptography-x509/src/common.rs +++ b/src/rust/cryptography-x509/src/common.rs @@ -6,7 +6,7 @@ use crate::oid; use asn1::Asn1DefinedByWritable; use std::marker::PhantomData; -#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash, Clone)] +#[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Hash, Clone, Eq)] pub struct AlgorithmIdentifier<'a> { pub oid: asn1::DefinedByMarker<asn1::ObjectIdentifier>, #[defined_by(oid)] @@ -55,6 +55,11 @@ pub enum AlgorithmParameters<'a> { #[defined_by(oid::ECDSA_WITH_SHA3_512_OID)] EcDsaWithSha3_512, + #[defined_by(oid::RSA_WITH_SHA1_OID)] + RsaWithSha1(Option<asn1::Null>), + #[defined_by(oid::RSA_WITH_SHA1_ALT_OID)] + RsaWithSha1Alt(Option<asn1::Null>), + #[defined_by(oid::RSA_WITH_SHA224_OID)] RsaWithSha224(Option<asn1::Null>), #[defined_by(oid::RSA_WITH_SHA256_OID)] @@ -73,6 +78,12 @@ pub enum AlgorithmParameters<'a> { #[defined_by(oid::RSA_WITH_SHA3_512_OID)] RsaWithSha3_512(Option<asn1::Null>), + // RsaPssParameters must be present in Certificate::tbs_cert::signature_alg::params + // and Certificate::signature_alg::params, but Certificate::tbs_cert::spki::algorithm::oid + // also uses RSASSA_PSS_OID and the params field is omitted since it has no meaning there. + #[defined_by(oid::RSASSA_PSS_OID)] + RsaPss(Option<Box<RsaPssParameters<'a>>>), + #[defined_by(oid::DSA_WITH_SHA224_OID)] DsaWithSha224, #[defined_by(oid::DSA_WITH_SHA256_OID)] @@ -205,6 +216,50 @@ pub struct DHParams<'a> { pub g: asn1::BigUint<'a>, pub q: Option<asn1::BigUint<'a>>, } +// RSA-PSS ASN.1 default hash algorithm +pub const PSS_SHA1_HASH_ALG: AlgorithmIdentifier<'_> = AlgorithmIdentifier { + oid: asn1::DefinedByMarker::marker(), + params: AlgorithmParameters::Sha1(()), +}; + +// This is defined as an AlgorithmIdentifier in RFC 4055, +// but the mask generation algorithm **must** contain an AlgorithmIdentifier +// in its params, so we define it this way. +#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, Clone, PartialEq, Eq)] +pub struct MaskGenAlgorithm<'a> { + pub oid: asn1::ObjectIdentifier, + pub params: AlgorithmIdentifier<'a>, +} + +// RSA-PSS ASN.1 default mask gen algorithm +pub const PSS_SHA1_MASK_GEN_ALG: MaskGenAlgorithm<'_> = MaskGenAlgorithm { + oid: oid::MGF1_OID, + params: PSS_SHA1_HASH_ALG, +}; + +// From RFC 4055 section 3.1: +// RSASSA-PSS-params ::= SEQUENCE { +// hashAlgorithm [0] HashAlgorithm DEFAULT +// sha1Identifier, +// maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT +// mgf1SHA1Identifier, +// saltLength [2] INTEGER DEFAULT 20, +// trailerField [3] INTEGER DEFAULT 1 } +#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, Clone, PartialEq, Eq)] +pub struct RsaPssParameters<'a> { + #[explicit(0)] + #[default(PSS_SHA1_HASH_ALG)] + pub hash_algorithm: AlgorithmIdentifier<'a>, + #[explicit(1)] + #[default(PSS_SHA1_MASK_GEN_ALG)] + pub mask_gen_algorithm: MaskGenAlgorithm<'a>, + #[explicit(2)] + #[default(20u16)] + pub salt_length: u16, + #[explicit(3)] + #[default(1u8)] + _trailer_field: u8, +} /// A VisibleString ASN.1 element whose contents is not validated as meeting the /// requirements (visible characters of IA5), and instead is only known to be diff --git a/src/rust/cryptography-x509/src/oid.rs b/src/rust/cryptography-x509/src/oid.rs index b2d22ebdd..ac80b9a31 100644 --- a/src/rust/cryptography-x509/src/oid.rs +++ b/src/rust/cryptography-x509/src/oid.rs @@ -57,6 +57,8 @@ pub const ECDSA_WITH_SHA3_384_OID: asn1::ObjectIdentifier = pub const ECDSA_WITH_SHA3_512_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 3, 12); +pub const RSA_WITH_SHA1_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 1, 1, 5); +pub const RSA_WITH_SHA1_ALT_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 14, 3, 2, 29); pub const RSA_WITH_SHA224_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 1, 1, 14); pub const RSA_WITH_SHA256_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 1, 1, 11); pub const RSA_WITH_SHA384_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 1, 1, 12); @@ -84,3 +86,14 @@ pub const SHA224_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, pub const SHA256_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 2, 1); pub const SHA384_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 2, 2); pub const SHA512_OID: asn1::ObjectIdentifier = asn1::oid!(2, 16, 840, 1, 101, 3, 4, 2, 3); +pub const SHA3_224_OID: asn1::ObjectIdentifier = + asn1::oid!(1, 3, 6, 1, 4, 1, 37476, 3, 2, 1, 99, 7, 224); +pub const SHA3_256_OID: asn1::ObjectIdentifier = + asn1::oid!(1, 3, 6, 1, 4, 1, 37476, 3, 2, 1, 99, 7, 256); +pub const SHA3_384_OID: asn1::ObjectIdentifier = + asn1::oid!(1, 3, 6, 1, 4, 1, 37476, 3, 2, 1, 99, 7, 384); +pub const SHA3_512_OID: asn1::ObjectIdentifier = + asn1::oid!(1, 3, 6, 1, 4, 1, 37476, 3, 2, 1, 99, 7, 512); + +pub const MGF1_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 1, 1, 8); +pub const RSASSA_PSS_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 1, 1, 10); diff --git a/src/rust/src/x509/certificate.rs b/src/rust/src/x509/certificate.rs index 98f1a073f..03d8ae883 100644 --- a/src/rust/src/x509/certificate.rs +++ b/src/rust/src/x509/certificate.rs @@ -17,10 +17,28 @@ use cryptography_x509::extensions::{ }; use cryptography_x509::extensions::{Extension, Extensions}; use cryptography_x509::{common, name, oid}; +use once_cell::sync::Lazy; use pyo3::{IntoPy, ToPyObject}; use std::collections::hash_map::DefaultHasher; +use std::collections::HashMap; use std::hash::{Hash, Hasher}; +// This is similar to a hashmap in ocsp.rs but contains more hash algorithms +// that aren't allowable in OCSP +static HASH_OIDS_TO_HASH: Lazy<HashMap<&asn1::ObjectIdentifier, &str>> = Lazy::new(|| { + let mut h = HashMap::new(); + h.insert(&oid::SHA1_OID, "SHA1"); + h.insert(&oid::SHA224_OID, "SHA224"); + h.insert(&oid::SHA256_OID, "SHA256"); + h.insert(&oid::SHA384_OID, "SHA384"); + h.insert(&oid::SHA512_OID, "SHA512"); + h.insert(&oid::SHA3_224_OID, "SHA3_224"); + h.insert(&oid::SHA3_256_OID, "SHA3_256"); + h.insert(&oid::SHA3_384_OID, "SHA3_384"); + h.insert(&oid::SHA3_512_OID, "SHA3_512"); + h +}); + #[ouroboros::self_referencing] pub(crate) struct OwnedCertificate { data: pyo3::Py<pyo3::types::PyBytes>, @@ -241,15 +259,25 @@ impl Certificate { let sig_oids_to_hash = py .import(pyo3::intern!(py, "cryptography.hazmat._oid"))? .getattr(pyo3::intern!(py, "_SIG_OIDS_TO_HASH"))?; - let hash_alg = sig_oids_to_hash.get_item(self.signature_algorithm_oid(py)?); - match hash_alg { - Ok(data) => Ok(data), - Err(_) => Err(CryptographyError::from( - exceptions::UnsupportedAlgorithm::new_err(format!( - "Signature algorithm OID: {} not recognized", - self.raw.borrow_value().signature_alg.oid(), - )), - )), + match &self.raw.borrow_value().signature_alg.params { + common::AlgorithmParameters::RsaPss(opt_pss) => { + let pss = opt_pss.as_ref().ok_or_else(|| { + pyo3::exceptions::PyValueError::new_err("Invalid RSA PSS parameters") + })?; + hash_oid_py_hash(py, pss.hash_algorithm.oid().clone()) + } + _ => { + let hash_alg = sig_oids_to_hash.get_item(self.signature_algorithm_oid(py)?); + match hash_alg { + Ok(data) => Ok(data), + Err(_) => Err(CryptographyError::from( + exceptions::UnsupportedAlgorithm::new_err(format!( + "Signature algorithm OID: {} not recognized", + self.raw.borrow_value().signature_alg.oid() + )), + )), + } + } } } @@ -259,6 +287,74 @@ impl Certificate { } #[getter] + fn signature_algorithm_parameters<'p>( + &'p self, + py: pyo3::Python<'p>, + ) -> CryptographyResult<&'p pyo3::PyAny> { + match &self.raw.borrow_value().signature_alg.params { + common::AlgorithmParameters::RsaPss(opt_pss) => { + let pss = opt_pss.as_ref().ok_or_else(|| { + pyo3::exceptions::PyValueError::new_err("Invalid RSA PSS parameters") + })?; + if pss.mask_gen_algorithm.oid != oid::MGF1_OID { + return Err(CryptographyError::from( + pyo3::exceptions::PyValueError::new_err(format!( + "Unsupported mask generation OID: {}", + pss.mask_gen_algorithm.oid + )), + )); + } + let py_mask_gen_hash_alg = + hash_oid_py_hash(py, pss.mask_gen_algorithm.params.oid().clone())?; + let padding = py.import(pyo3::intern!( + py, + "cryptography.hazmat.primitives.asymmetric.padding" + ))?; + let py_mgf = padding + .getattr(pyo3::intern!(py, "MGF1"))? + .call1((py_mask_gen_hash_alg,))?; + Ok(padding + .getattr(pyo3::intern!(py, "PSS"))? + .call1((py_mgf, pss.salt_length))?) + } + common::AlgorithmParameters::RsaWithSha1(_) + | common::AlgorithmParameters::RsaWithSha1Alt(_) + | common::AlgorithmParameters::RsaWithSha224(_) + | common::AlgorithmParameters::RsaWithSha256(_) + | common::AlgorithmParameters::RsaWithSha384(_) + | common::AlgorithmParameters::RsaWithSha512(_) + | common::AlgorithmParameters::RsaWithSha3_224(_) + | common::AlgorithmParameters::RsaWithSha3_256(_) + | common::AlgorithmParameters::RsaWithSha3_384(_) + | common::AlgorithmParameters::RsaWithSha3_512(_) => { + let pkcs = py + .import(pyo3::intern!( + py, + "cryptography.hazmat.primitives.asymmetric.padding" + ))? + .getattr(pyo3::intern!(py, "PKCS1v15"))? + .call0()?; + Ok(pkcs) + } + common::AlgorithmParameters::EcDsaWithSha224 + | common::AlgorithmParameters::EcDsaWithSha256 + | common::AlgorithmParameters::EcDsaWithSha384 + | common::AlgorithmParameters::EcDsaWithSha512 + | common::AlgorithmParameters::EcDsaWithSha3_224 + | common::AlgorithmParameters::EcDsaWithSha3_256 + | common::AlgorithmParameters::EcDsaWithSha3_384 + | common::AlgorithmParameters::EcDsaWithSha3_512 => Ok(py + .import(pyo3::intern!( + py, + "cryptography.hazmat.primitives.asymmetric.ec" + ))? + .getattr(pyo3::intern!(py, "ECDSA"))? + .call1((self.signature_hash_algorithm(py)?,))?), + _ => Ok(py.None().into_ref(py)), + } + } + + #[getter] fn extensions(&mut self, py: pyo3::Python<'_>) -> pyo3::PyResult<pyo3::PyObject> { let x509_module = py.import(pyo3::intern!(py, "cryptography.x509"))?; x509::parse_and_cache_extensions( @@ -853,6 +949,22 @@ pub fn parse_cert_ext<'p>( } } +fn hash_oid_py_hash( + py: pyo3::Python<'_>, + oid: asn1::ObjectIdentifier, +) -> CryptographyResult<&pyo3::PyAny> { + let hashes = py.import(pyo3::intern!(py, "cryptography.hazmat.primitives.hashes"))?; + match HASH_OIDS_TO_HASH.get(&oid) { + Some(alg_name) => Ok(hashes.getattr(*alg_name)?.call0()?), + None => Err(CryptographyError::from( + exceptions::UnsupportedAlgorithm::new_err(format!( + "Signature algorithm OID: {} not recognized", + &oid + )), + )), + } +} + pub(crate) fn time_from_py( py: pyo3::Python<'_>, val: &pyo3::PyAny, diff --git a/tests/x509/test_x509.py b/tests/x509/test_x509.py index 1de45192b..a32dfca93 100644 --- a/tests/x509/test_x509.py +++ b/tests/x509/test_x509.py @@ -729,15 +729,15 @@ class TestRevokedCertificate: assert crl[2].serial_number == 3 +@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", +) class TestRSAPSSCertificate: - @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", - ) def test_load_cert_pub_key(self, backend): cert = _load_cert( os.path.join("x509", "custom", "rsa_pss_cert.pem"), @@ -751,7 +751,47 @@ class TestRSAPSSCertificate: assert isinstance(expected_pub_key, rsa.RSAPublicKey) pub_key = cert.public_key() assert isinstance(pub_key, rsa.RSAPublicKey) - assert pub_key.public_numbers() == expected_pub_key.public_numbers() + assert pub_key == expected_pub_key + pss = cert.signature_algorithm_parameters + assert isinstance(pss, padding.PSS) + assert isinstance(pss._mgf, padding.MGF1) + assert isinstance(pss._mgf._algorithm, hashes.SHA256) + assert pss._salt_length == 222 + assert isinstance(cert.signature_hash_algorithm, hashes.SHA256) + pub_key.verify( + cert.signature, + cert.tbs_certificate_bytes, + pss, + cert.signature_hash_algorithm, + ) + + def test_invalid_mgf(self, backend): + cert = _load_cert( + os.path.join("x509", "custom", "rsa_pss_cert_invalid_mgf.der"), + x509.load_der_x509_certificate, + ) + with pytest.raises(ValueError): + cert.signature_algorithm_parameters + + def test_unsupported_mgf_hash(self, backend): + cert = _load_cert( + os.path.join( + "x509", "custom", "rsa_pss_cert_unsupported_mgf_hash.der" + ), + x509.load_der_x509_certificate, + ) + with pytest.raises(UnsupportedAlgorithm): + cert.signature_algorithm_parameters + + def test_no_sig_params(self, backend): + cert = _load_cert( + os.path.join("x509", "custom", "rsa_pss_cert_no_sig_params.der"), + x509.load_der_x509_certificate, + ) + with pytest.raises(ValueError): + cert.signature_algorithm_parameters + with pytest.raises(ValueError): + cert.signature_hash_algorithm class TestRSACertificate: @@ -768,6 +808,28 @@ class TestRSACertificate: assert ( cert.signature_algorithm_oid == SignatureAlgorithmOID.RSA_WITH_SHA1 ) + assert isinstance( + cert.signature_algorithm_parameters, padding.PKCS1v15 + ) + + def test_check_pkcs1_signature_algorithm_parameters(self, backend): + cert = _load_cert( + os.path.join("x509", "custom", "ca", "rsa_ca.pem"), + x509.load_pem_x509_certificate, + ) + assert isinstance(cert, x509.Certificate) + assert isinstance( + cert.signature_algorithm_parameters, padding.PKCS1v15 + ) + pk = cert.public_key() + assert isinstance(pk, rsa.RSAPublicKey) + assert cert.signature_hash_algorithm is not None + pk.verify( + cert.signature, + cert.tbs_certificate_bytes, + cert.signature_algorithm_parameters, + cert.signature_hash_algorithm, + ) def test_load_legacy_pem_header(self, backend): cert = _load_cert( @@ -4599,6 +4661,7 @@ class TestDSACertificate: assert isinstance(cert.signature_hash_algorithm, hashes.SHA1) public_key = cert.public_key() assert isinstance(public_key, dsa.DSAPublicKey) + assert cert.signature_algorithm_parameters is None num = public_key.public_numbers() assert num.y == int( "4c08bfe5f2d76649c80acf7d431f6ae2124b217abc8c9f6aca776ddfa94" @@ -4847,6 +4910,15 @@ class TestECDSACertificate: 16, ) assert isinstance(num.curve, ec.SECP384R1) + assert isinstance(cert.signature_algorithm_parameters, ec.ECDSA) + assert isinstance( + cert.signature_algorithm_parameters.algorithm, hashes.SHA384 + ) + public_key.verify( + cert.signature, + cert.tbs_certificate_bytes, + cert.signature_algorithm_parameters, + ) def test_load_bitstring_dn(self, backend): cert = _load_cert( @@ -5590,6 +5662,7 @@ class TestEd25519Certificate: assert cert.serial_number == 9579446940964433301 assert cert.signature_hash_algorithm is None assert cert.signature_algorithm_oid == SignatureAlgorithmOID.ED25519 + assert cert.signature_algorithm_parameters is None def test_deepcopy(self, backend): cert = _load_cert( @@ -5635,6 +5708,7 @@ class TestEd448Certificate: assert cert.serial_number == 448 assert cert.signature_hash_algorithm is None assert cert.signature_algorithm_oid == SignatureAlgorithmOID.ED448 + assert cert.signature_algorithm_parameters is None def test_verify_directly_issued_by_ed448(self, backend): issuer_private_key = ed448.Ed448PrivateKey.generate() |
