feat(admissions): add admissions extension type (#11886)

* feat(admissions): add admissions extension type

Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com>

* fix: use tuple for admissions unpacking in hash code calculation

Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com>

---------

Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com>
This commit is contained in:
Oleg Höfling 2024-11-04 12:42:08 +01:00 committed by GitHub
parent 78b3750a3b
commit cf93084b0e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 163 additions and 0 deletions

View file

@ -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",

View file

@ -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",

View file

@ -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"<Admissions(authority={self._authority}, "
f"admissions={self._admissions})>"
)
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):

View file

@ -326,6 +326,17 @@ pub struct Admission<'a> {
*/
}
// #[derive(asn1::Asn1Read, asn1::Asn1Write)]
pub struct Admissions<'a> {
pub admission_authority: Option<name::GeneralName<'a>>,
/*
pub contents_of_admissions: common::Asn1ReadableOrWritable<
asn1::SequenceOf<'a, Admission<'a>>,
asn1::SequenceOfWriter<'a, Admission<'a>, Vec<Admission<'a>>>,
>,
*/
}
#[cfg(test)]
mod tests {
use super::{BasicConstraints, Extension, Extensions, KeyUsage};

View file

@ -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(authority=None, admissions=[])>"
)
admissions = x509.Admissions(
x509.UniformResourceIdentifier(value="https://www.example.de"),
[x509.Admission(None, None, [])],
)
assert repr(admissions) == (
"<Admissions("
"authority=<UniformResourceIdentifier("
"value='https://www.example.de')>, "
"admissions=[<Admission("
"admission_authority=None, "
"naming_authority=None, "
"profession_infos=[])>])>"
)
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("__"):