mirror of
https://github.com/saymrwulf/cryptography.git
synced 2026-05-14 20:37:55 +00:00
feat(admissions): add profession info type for the admissions extension (#11881)
* feat(admissions): add profession info python type for the admissions extension Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * feat(admissions): add profession info rust type for the admissions extension Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * feat(admissions): add test for profession info hash implementation Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * fix(admissions): minor fixes Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * remove the asn1 traits from the profession info rust type Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * remove the explicit mark from the naming authority field Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * chore: add commented out annotation for the naming authority field Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> * fix: use correct type for add_profeccion_info field Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> * refactor: explicitly convert profession items and oids to tuples for hash calculation Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> * refactor: add asn1 trait derives to naming authority and profession info types, commented out Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> --------- Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com>
This commit is contained in:
parent
9e46c93034
commit
f65ab4d7f5
4 changed files with 352 additions and 0 deletions
|
|
@ -64,6 +64,7 @@ from cryptography.x509.extensions import (
|
|||
PolicyInformation,
|
||||
PrecertificateSignedCertificateTimestamps,
|
||||
PrecertPoison,
|
||||
ProfessionInfo,
|
||||
ReasonFlags,
|
||||
SignedCertificateTimestamps,
|
||||
SubjectAlternativeName,
|
||||
|
|
@ -228,6 +229,7 @@ __all__ = [
|
|||
"PolicyInformation",
|
||||
"PrecertPoison",
|
||||
"PrecertificateSignedCertificateTimestamps",
|
||||
"ProfessionInfo",
|
||||
"PublicKeyAlgorithmOID",
|
||||
"RFC822Name",
|
||||
"ReasonFlags",
|
||||
|
|
|
|||
|
|
@ -2222,6 +2222,104 @@ class NamingAuthority:
|
|||
)
|
||||
|
||||
|
||||
class ProfessionInfo:
|
||||
def __init__(
|
||||
self,
|
||||
naming_authority: NamingAuthority | None,
|
||||
profession_items: typing.Iterable[str],
|
||||
profession_oids: typing.Iterable[ObjectIdentifier],
|
||||
registration_number: str | None,
|
||||
add_profession_info: bytes | None,
|
||||
) -> None:
|
||||
if naming_authority is not None and not isinstance(
|
||||
naming_authority, NamingAuthority
|
||||
):
|
||||
raise TypeError("naming_authority must be a NamingAuthority")
|
||||
|
||||
profession_items = list(profession_items)
|
||||
if not all(isinstance(item, str) for item in profession_items):
|
||||
raise TypeError(
|
||||
"Every item in the profession_items list must be a str"
|
||||
)
|
||||
|
||||
profession_oids = list(profession_oids)
|
||||
if not all(
|
||||
isinstance(oid, ObjectIdentifier) for oid in profession_oids
|
||||
):
|
||||
raise TypeError(
|
||||
"Every item in the profession_oids list must be an "
|
||||
"ObjectIdentifier"
|
||||
)
|
||||
|
||||
if registration_number is not None and not isinstance(
|
||||
registration_number, str
|
||||
):
|
||||
raise TypeError("registration_number must be a str")
|
||||
|
||||
if add_profession_info is not None and not isinstance(
|
||||
add_profession_info, bytes
|
||||
):
|
||||
raise TypeError("add_profession_info must be bytes")
|
||||
|
||||
self._naming_authority = naming_authority
|
||||
self._profession_items = profession_items
|
||||
self._profession_oids = profession_oids
|
||||
self._registration_number = registration_number
|
||||
self._add_profession_info = add_profession_info
|
||||
|
||||
@property
|
||||
def naming_authority(self) -> NamingAuthority | None:
|
||||
return self._naming_authority
|
||||
|
||||
@property
|
||||
def profession_items(self) -> list[str]:
|
||||
return self._profession_items
|
||||
|
||||
@property
|
||||
def profession_oids(self) -> list[ObjectIdentifier]:
|
||||
return self._profession_oids
|
||||
|
||||
@property
|
||||
def registration_number(self) -> str | None:
|
||||
return self._registration_number
|
||||
|
||||
@property
|
||||
def add_profession_info(self) -> bytes | None:
|
||||
return self._add_profession_info
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<ProfessionInfo(naming_authority={self.naming_authority}, "
|
||||
f"profession_items={self.profession_items}, "
|
||||
f"profession_oids={self.profession_oids}, "
|
||||
f"registration_number={self.registration_number}, "
|
||||
f"add_profession_info={self.add_profession_info!r})>"
|
||||
)
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, ProfessionInfo):
|
||||
return NotImplemented
|
||||
|
||||
return (
|
||||
self.naming_authority == other.naming_authority
|
||||
and self.profession_items == other.profession_items
|
||||
and self.profession_oids == other.profession_oids
|
||||
and self.registration_number == other.registration_number
|
||||
and self.add_profession_info == other.add_profession_info
|
||||
)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(
|
||||
(
|
||||
self.naming_authority,
|
||||
*tuple(self.profession_items),
|
||||
*tuple(self.profession_oids),
|
||||
self.registration_number,
|
||||
self.add_profession_info,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class UnrecognizedExtension(ExtensionType):
|
||||
def __init__(self, oid: ObjectIdentifier, value: bytes) -> None:
|
||||
if not isinstance(oid, ObjectIdentifier):
|
||||
|
|
|
|||
|
|
@ -285,12 +285,33 @@ impl KeyUsage<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
// #[derive(asn1::Asn1Read, asn1::Asn1Write)]
|
||||
pub struct NamingAuthority<'a> {
|
||||
pub id: Option<asn1::ObjectIdentifier>,
|
||||
pub url: Option<asn1::IA5String<'a>>,
|
||||
pub text: Option<DisplayText<'a>>,
|
||||
}
|
||||
|
||||
type SequenceOfDisplayTexts<'a> = common::Asn1ReadableOrWritable<
|
||||
asn1::SequenceOf<'a, DisplayText<'a>>,
|
||||
asn1::SequenceOfWriter<'a, DisplayText<'a>, Vec<DisplayText<'a>>>,
|
||||
>;
|
||||
|
||||
type SequenceOfObjectIdentifiers<'a> = common::Asn1ReadableOrWritable<
|
||||
asn1::SequenceOf<'a, asn1::ObjectIdentifier>,
|
||||
asn1::SequenceOfWriter<'a, asn1::ObjectIdentifier, Vec<asn1::ObjectIdentifier>>,
|
||||
>;
|
||||
|
||||
// #[derive(asn1::Asn1Read, asn1::Asn1Write)]
|
||||
pub struct ProfessionInfo<'a> {
|
||||
// #[explicit(0)]
|
||||
pub naming_authority: Option<NamingAuthority<'a>>,
|
||||
pub profession_items: SequenceOfDisplayTexts<'a>,
|
||||
pub profession_oids: Option<SequenceOfObjectIdentifiers<'a>>,
|
||||
pub registration_number: Option<asn1::PrintableString<'a>>,
|
||||
pub add_profession_info: Option<&'a [u8]>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{BasicConstraints, Extension, Extensions, KeyUsage};
|
||||
|
|
|
|||
|
|
@ -6437,6 +6437,237 @@ class TestNamingAuthority:
|
|||
assert hash(authority1) != hash(authority9)
|
||||
|
||||
|
||||
class TestProfessionInfo:
|
||||
def test_invalid_init(self):
|
||||
with pytest.raises(TypeError):
|
||||
x509.ProfessionInfo(
|
||||
None,
|
||||
None, # type:ignore[arg-type]
|
||||
None, # type:ignore[arg-type]
|
||||
None,
|
||||
None,
|
||||
)
|
||||
with pytest.raises(TypeError):
|
||||
x509.ProfessionInfo(
|
||||
"spam", # type:ignore[arg-type]
|
||||
[],
|
||||
[],
|
||||
None,
|
||||
None,
|
||||
)
|
||||
with pytest.raises(TypeError):
|
||||
x509.ProfessionInfo(
|
||||
None,
|
||||
[42], # type:ignore[list-item]
|
||||
[],
|
||||
None,
|
||||
None,
|
||||
)
|
||||
with pytest.raises(TypeError):
|
||||
x509.ProfessionInfo(
|
||||
None,
|
||||
[],
|
||||
"spam", # type:ignore[arg-type]
|
||||
None,
|
||||
None,
|
||||
)
|
||||
with pytest.raises(TypeError):
|
||||
x509.ProfessionInfo(
|
||||
None,
|
||||
[],
|
||||
[],
|
||||
42, # type:ignore[arg-type]
|
||||
None,
|
||||
)
|
||||
with pytest.raises(TypeError):
|
||||
x509.ProfessionInfo(
|
||||
None,
|
||||
[],
|
||||
[],
|
||||
None,
|
||||
42, # type:ignore[arg-type]
|
||||
)
|
||||
|
||||
def test_eq(self):
|
||||
info1 = x509.ProfessionInfo(None, [], [], None, None)
|
||||
info2 = x509.ProfessionInfo(None, [], [], None, None)
|
||||
assert info1 == info2
|
||||
|
||||
info1 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(
|
||||
x509.ObjectIdentifier("1.2.3"), "https://example.com", "spam"
|
||||
),
|
||||
["spam"],
|
||||
[x509.ObjectIdentifier("1.2.3.4")],
|
||||
"eggs",
|
||||
b"\x01\x02\x03",
|
||||
)
|
||||
info2 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(
|
||||
x509.ObjectIdentifier("1.2.3"), "https://example.com", "spam"
|
||||
),
|
||||
["spam"],
|
||||
[x509.ObjectIdentifier("1.2.3.4")],
|
||||
"eggs",
|
||||
b"\x01\x02\x03",
|
||||
)
|
||||
assert info1 == info2
|
||||
|
||||
def test_ne(self):
|
||||
info1 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(
|
||||
x509.ObjectIdentifier("1.2.3"), "https://example.com", "spam"
|
||||
),
|
||||
["spam"],
|
||||
[x509.ObjectIdentifier("1.2.3.4")],
|
||||
"eggs",
|
||||
b"\x01\x02\x03",
|
||||
)
|
||||
info2 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(
|
||||
x509.ObjectIdentifier("1.2.3"), "https://example.com", "spam"
|
||||
),
|
||||
["spam"],
|
||||
[x509.ObjectIdentifier("1.2.3.4")],
|
||||
"eggs",
|
||||
None,
|
||||
)
|
||||
info3 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(
|
||||
x509.ObjectIdentifier("1.2.3"), "https://example.com", "spam"
|
||||
),
|
||||
["spam"],
|
||||
[x509.ObjectIdentifier("1.2.3.4")],
|
||||
None,
|
||||
None,
|
||||
)
|
||||
info4 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(
|
||||
x509.ObjectIdentifier("1.2.3"), "https://example.com", "spam"
|
||||
),
|
||||
["spam"],
|
||||
[],
|
||||
None,
|
||||
None,
|
||||
)
|
||||
info5 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(
|
||||
x509.ObjectIdentifier("1.2.3"), "https://example.com", "spam"
|
||||
),
|
||||
[],
|
||||
[],
|
||||
None,
|
||||
None,
|
||||
)
|
||||
info6 = x509.ProfessionInfo(None, ["spam"], [], None, None)
|
||||
info7 = x509.ProfessionInfo(
|
||||
None, [], [x509.ObjectIdentifier("1.2.3")], None, None
|
||||
)
|
||||
info8 = x509.ProfessionInfo(None, [], [], "spam", None)
|
||||
info9 = x509.ProfessionInfo(None, [], [], None, b"\x01\x02\x03")
|
||||
info10 = x509.ProfessionInfo(None, [], [], None, None)
|
||||
|
||||
assert info1 != info2
|
||||
assert info1 != info2
|
||||
assert info1 != info3
|
||||
assert info1 != info4
|
||||
assert info1 != info5
|
||||
assert info1 != info6
|
||||
assert info1 != info7
|
||||
assert info1 != info8
|
||||
assert info1 != info9
|
||||
assert info1 != info10
|
||||
assert info1 != object()
|
||||
|
||||
def test_repr(self):
|
||||
info = x509.ProfessionInfo(None, [], [], None, None)
|
||||
assert repr(info) == (
|
||||
"<ProfessionInfo("
|
||||
"naming_authority=None, "
|
||||
"profession_items=[], "
|
||||
"profession_oids=[], "
|
||||
"registration_number=None, "
|
||||
"add_profession_info=None)>"
|
||||
)
|
||||
|
||||
info = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(
|
||||
x509.ObjectIdentifier("1.2.3"), "https://example.com", "spam"
|
||||
),
|
||||
["spam"],
|
||||
[x509.ObjectIdentifier("1.2.3.4")],
|
||||
"eggs",
|
||||
b"\x01\x02\x03",
|
||||
)
|
||||
assert repr(info) == (
|
||||
"<ProfessionInfo("
|
||||
"naming_authority=<NamingAuthority("
|
||||
"id=<ObjectIdentifier(oid=1.2.3, name=Unknown OID)>, "
|
||||
"url=https://example.com, text=spam)>, "
|
||||
"profession_items=['spam'], "
|
||||
"profession_oids="
|
||||
"[<ObjectIdentifier(oid=1.2.3.4, name=Unknown OID)>], "
|
||||
"registration_number=eggs, "
|
||||
"add_profession_info=b'\\x01\\x02\\x03')>"
|
||||
)
|
||||
|
||||
def test_hash(self):
|
||||
info1 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(None, None, None),
|
||||
["spam"],
|
||||
[x509.ObjectIdentifier("1.2.3.4")],
|
||||
"eggs",
|
||||
b"\x01\x02\x03",
|
||||
)
|
||||
info2 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(None, None, None),
|
||||
["spam"],
|
||||
[x509.ObjectIdentifier("1.2.3.4")],
|
||||
"eggs",
|
||||
b"\x01\x02\x03",
|
||||
)
|
||||
info3 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(
|
||||
x509.ObjectIdentifier("1.2.3"), "https://example.com", "spam"
|
||||
),
|
||||
["spam"],
|
||||
[x509.ObjectIdentifier("1.2.3.4")],
|
||||
"eggs",
|
||||
b"\x01\x02\x03",
|
||||
)
|
||||
info4 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(None, None, None),
|
||||
[],
|
||||
[x509.ObjectIdentifier("1.2.3.4")],
|
||||
"eggs",
|
||||
b"\x01\x02\x03",
|
||||
)
|
||||
info5 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(None, None, None),
|
||||
[],
|
||||
[],
|
||||
"eggs",
|
||||
b"\x01\x02\x03",
|
||||
)
|
||||
info6 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(None, None, None),
|
||||
[],
|
||||
[],
|
||||
None,
|
||||
b"\x01\x02\x03",
|
||||
)
|
||||
info7 = x509.ProfessionInfo(
|
||||
x509.NamingAuthority(None, None, None), [], [], None, None
|
||||
)
|
||||
|
||||
assert hash(info1) == hash(info2)
|
||||
assert hash(info1) != hash(info3)
|
||||
assert hash(info1) != hash(info4)
|
||||
assert hash(info1) != hash(info5)
|
||||
assert hash(info1) != hash(info6)
|
||||
assert hash(info1) != hash(info7)
|
||||
|
||||
|
||||
def test_all_extension_oid_members_have_names_defined():
|
||||
for oid in dir(ExtensionOID):
|
||||
if oid.startswith("__"):
|
||||
|
|
|
|||
Loading…
Reference in a new issue