diff --git a/src/cryptography/hazmat/_oid.py b/src/cryptography/hazmat/_oid.py index fd5e37d9e..8bd240d09 100644 --- a/src/cryptography/hazmat/_oid.py +++ b/src/cryptography/hazmat/_oid.py @@ -39,6 +39,7 @@ class ExtensionOID: PRECERT_POISON = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3") SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.5") MS_CERTIFICATE_TEMPLATE = ObjectIdentifier("1.3.6.1.4.1.311.21.7") + ADMISSIONS = ObjectIdentifier("1.3.36.8.3.3") class OCSPExtensionOID: @@ -284,6 +285,7 @@ _OID_NAMES = { ), ExtensionOID.PRECERT_POISON: "ctPoison", ExtensionOID.MS_CERTIFICATE_TEMPLATE: "msCertificateTemplate", + ExtensionOID.ADMISSIONS: "Admissions", CRLEntryExtensionOID.CRL_REASON: "cRLReason", CRLEntryExtensionOID.INVALIDITY_DATE: "invalidityDate", CRLEntryExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer", diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index 82531a428..8a89d67f1 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -31,6 +31,7 @@ from cryptography.x509.base import ( from cryptography.x509.extensions import ( AccessDescription, Admission, + Admissions, AuthorityInformationAccess, AuthorityKeyIdentifier, BasicConstraints, @@ -178,6 +179,7 @@ __all__ = [ "OID_OCSP", "AccessDescription", "Admission", + "Admissions", "Attribute", "AttributeNotFound", "Attributes", diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py index f862a1363..202101208 100644 --- a/src/cryptography/x509/extensions.py +++ b/src/cryptography/x509/extensions.py @@ -2389,6 +2389,54 @@ class Admission: ) +class Admissions(ExtensionType): + oid = ExtensionOID.ADMISSIONS + + def __init__( + self, + authority: GeneralName | None, + admissions: typing.Iterable[Admission], + ) -> None: + if authority is not None and not isinstance(authority, GeneralName): + raise TypeError("authority must be a GeneralName") + + admissions = list(admissions) + if not all( + isinstance(admission, Admission) for admission in admissions + ): + raise TypeError( + "Every item in the contents_of_admissions list must be an " + "Admission" + ) + + self._authority = authority + self._admissions = admissions + + __len__, __iter__, __getitem__ = _make_sequence_methods("_admissions") + + @property + def authority(self) -> GeneralName | None: + return self._authority + + def __repr__(self) -> str: + return ( + f"" + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Admissions): + return NotImplemented + + return ( + self.authority == other.authority + and self._admissions == other._admissions + ) + + def __hash__(self) -> int: + return hash((self.authority, *tuple(self._admissions))) + + class UnrecognizedExtension(ExtensionType): def __init__(self, oid: ObjectIdentifier, value: bytes) -> None: if not isinstance(oid, ObjectIdentifier): diff --git a/src/rust/cryptography-x509/src/extensions.rs b/src/rust/cryptography-x509/src/extensions.rs index d1ebf95ae..5b224db50 100644 --- a/src/rust/cryptography-x509/src/extensions.rs +++ b/src/rust/cryptography-x509/src/extensions.rs @@ -326,6 +326,17 @@ pub struct Admission<'a> { */ } +// #[derive(asn1::Asn1Read, asn1::Asn1Write)] +pub struct Admissions<'a> { + pub admission_authority: Option>, + /* + pub contents_of_admissions: common::Asn1ReadableOrWritable< + asn1::SequenceOf<'a, Admission<'a>>, + asn1::SequenceOfWriter<'a, Admission<'a>, Vec>>, + >, + */ +} + #[cfg(test)] mod tests { use super::{BasicConstraints, Extension, Extensions, KeyUsage}; diff --git a/tests/x509/test_x509_ext.py b/tests/x509/test_x509_ext.py index fc73bdfa1..fa47c277a 100644 --- a/tests/x509/test_x509_ext.py +++ b/tests/x509/test_x509_ext.py @@ -6995,6 +6995,106 @@ class TestAdmission: assert hash(admission1) != hash(admission6) +class TestAdmissions: + def test_invalid_init(self): + with pytest.raises(TypeError): + x509.Admissions( + 42, # type:ignore[arg-type] + [], + ) + with pytest.raises(TypeError): + x509.Admissions( + None, + 42, # type:ignore[arg-type] + ) + with pytest.raises(TypeError): + x509.Admissions( + None, + [42], # type:ignore[list-item] + ) + with pytest.raises(TypeError): + x509.Admissions( + None, + [None], # type:ignore[list-item] + ) + + def test_eq(self): + admissions1 = x509.Admissions(None, []) + admissions2 = x509.Admissions(None, []) + assert admissions1 == admissions2 + + admissions1 = x509.Admissions( + x509.UniformResourceIdentifier(value="https://www.example.de"), + [x509.Admission(None, None, [])], + ) + admissions2 = x509.Admissions( + x509.UniformResourceIdentifier(value="https://www.example.de"), + [x509.Admission(None, None, [])], + ) + assert admissions1 == admissions2 + + def test_ne(self): + admissions1 = x509.Admissions( + x509.UniformResourceIdentifier(value="https://www.example.de"), + [x509.Admission(None, None, [])], + ) + admissions2 = x509.Admissions( + x509.UniformResourceIdentifier(value="https://www.example.de"), [] + ) + admissions3 = x509.Admissions( + None, + [x509.Admission(None, None, [])], + ) + admissions4 = x509.Admissions(None, []) + + assert admissions1 != admissions2 + assert admissions1 != admissions3 + assert admissions1 != admissions4 + assert admissions1 != object() + + def test_repr(self): + admissions = x509.Admissions(None, []) + assert repr(admissions) == ( + "" + ) + + admissions = x509.Admissions( + x509.UniformResourceIdentifier(value="https://www.example.de"), + [x509.Admission(None, None, [])], + ) + assert repr(admissions) == ( + ", " + "admissions=[])>" + ) + + def test_hash(self): + admissions1 = x509.Admissions( + x509.UniformResourceIdentifier(value="https://www.example.de"), + [x509.Admission(None, None, [])], + ) + admissions2 = x509.Admissions( + x509.UniformResourceIdentifier(value="https://www.example.de"), + [x509.Admission(None, None, [])], + ) + admissions3 = x509.Admissions( + x509.UniformResourceIdentifier(value="https://www.example.de"), [] + ) + admissions4 = x509.Admissions( + None, + [x509.Admission(None, None, [])], + ) + admissions5 = x509.Admissions(None, []) + assert hash(admissions1) == hash(admissions2) + assert hash(admissions1) != hash(admissions3) + assert hash(admissions1) != hash(admissions4) + assert hash(admissions1) != hash(admissions5) + + def test_all_extension_oid_members_have_names_defined(): for oid in dir(ExtensionOID): if oid.startswith("__"):