Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(admissions): implement encoding of admissions extension #11892

Merged
merged 11 commits into from
Nov 7, 2024
Merged
3 changes: 3 additions & 0 deletions src/cryptography/x509/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2439,6 +2439,9 @@ def __eq__(self, other: object) -> bool:
def __hash__(self) -> int:
return hash((self.authority, tuple(self._admissions)))

def public_bytes(self) -> bytes:
return rust_x509.encode_extension_value(self)


class UnrecognizedExtension(ExtensionType):
def __init__(self, oid: ObjectIdentifier, value: bytes) -> None:
Expand Down
18 changes: 7 additions & 11 deletions src/rust/cryptography-x509/src/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ impl KeyUsage<'_> {
}
}

// #[derive(asn1::Asn1Read, asn1::Asn1Write)]
#[derive(asn1::Asn1Read, asn1::Asn1Write)]
pub struct NamingAuthority<'a> {
pub id: Option<asn1::ObjectIdentifier>,
pub url: Option<asn1::IA5String<'a>>,
Expand All @@ -302,39 +302,35 @@ type SequenceOfObjectIdentifiers<'a> = common::Asn1ReadableOrWritable<
asn1::SequenceOfWriter<'a, asn1::ObjectIdentifier, Vec<asn1::ObjectIdentifier>>,
>;

// #[derive(asn1::Asn1Read, asn1::Asn1Write)]
#[derive(asn1::Asn1Read, asn1::Asn1Write)]
pub struct ProfessionInfo<'a> {
// #[explicit(0)]
#[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]>,
}

// #[derive(asn1::Asn1Read, asn1::Asn1Write)]
#[derive(asn1::Asn1Read, asn1::Asn1Write)]
pub struct Admission<'a> {
// #[explicit(0)]
#[explicit(0)]
pub admission_authority: Option<name::GeneralName<'a>>,
// #[explicit(1)]
#[explicit(1)]
pub naming_authority: Option<NamingAuthority<'a>>,
/*
pub profession_infos: common::Asn1ReadableOrWritable<
asn1::SequenceOf<'a, ProfessionInfo<'a>>,
asn1::SequenceOfWriter<'a, ProfessionInfo<'a>, Vec<ProfessionInfo<'a>>>,
>,
*/
}

// #[derive(asn1::Asn1Read, asn1::Asn1Write)]
#[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)]
Expand Down
1 change: 1 addition & 0 deletions src/rust/cryptography-x509/src/oid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub const FRESHEST_CRL_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 46);
pub const INHIBIT_ANY_POLICY_OID: asn1::ObjectIdentifier = asn1::oid!(2, 5, 29, 54);
pub const ACCEPTABLE_RESPONSES_OID: asn1::ObjectIdentifier =
asn1::oid!(1, 3, 6, 1, 5, 5, 7, 48, 1, 4);
pub const ADMISSIONS_OID: asn1::ObjectIdentifier = asn1::oid!(1, 3, 36, 8, 3, 3);

// Public key identifiers
pub const EC_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 10045, 2, 1);
Expand Down
172 changes: 172 additions & 0 deletions src/rust/src/x509/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,149 @@ fn encode_scts(ext: &pyo3::Bound<'_, pyo3::PyAny>) -> CryptographyResult<Vec<u8>
Ok(asn1::write_single(&result.as_slice())?)
}

fn encode_naming_authority<'a>(
py: pyo3::Python<'_>,
ka_str: &'a cryptography_keepalive::KeepAlive<pyo3::pybacked::PyBackedStr>,
py_naming_authority: &pyo3::Bound<'a, pyo3::PyAny>,
) -> CryptographyResult<extensions::NamingAuthority<'a>> {
let py_oid = py_naming_authority.getattr(pyo3::intern!(py, "id"))?;
let id = if !py_oid.is_none() {
Some(py_oid_to_oid(py_oid)?)
} else {
None
};
let py_url = py_naming_authority.getattr(pyo3::intern!(py, "url"))?;
let url = if !py_url.is_none() {
let py_url_str = ka_str.add(py_url.extract::<PyBackedStr>()?);
match asn1::IA5String::new(py_url_str) {
Some(s) => Some(s),
None => {
return Err(CryptographyError::from(
pyo3::exceptions::PyValueError::new_err("url value must be a valid IA5String"),
))
}
}
} else {
None
};
let py_text = py_naming_authority.getattr(pyo3::intern!(py, "text"))?;
let text = if !py_text.is_none() {
let py_text_str = ka_str.add(py_text.extract::<PyBackedStr>()?);
Some(extensions::DisplayText::Utf8String(asn1::Utf8String::new(
py_text_str,
)))
} else {
None
};
Ok(extensions::NamingAuthority { id, url, text })
}

fn encode_profession_info<'a>(
py: pyo3::Python<'_>,
ka_bytes: &'a cryptography_keepalive::KeepAlive<pyo3::pybacked::PyBackedBytes>,
ka_str: &'a cryptography_keepalive::KeepAlive<pyo3::pybacked::PyBackedStr>,
py_info: &pyo3::Bound<'a, pyo3::PyAny>,
) -> CryptographyResult<extensions::ProfessionInfo<'a>> {
let py_naming_authority = py_info.getattr(pyo3::intern!(py, "naming_authority"))?;
let naming_authority = if !py_naming_authority.is_none() {
Some(encode_naming_authority(py, ka_str, &py_naming_authority)?)
} else {
None
};
let mut profession_items = vec![];
let py_items = py_info.getattr(pyo3::intern!(py, "profession_items"))?;
for py_item in py_items.iter()? {
let py_item = py_item?;
let py_item_str = ka_str.add(py_item.extract::<PyBackedStr>()?);
let item = extensions::DisplayText::Utf8String(asn1::Utf8String::new(py_item_str));
profession_items.push(item);
}
let profession_items =
common::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new(profession_items));
let py_oids = py_info.getattr(pyo3::intern!(py, "profession_oids"))?;
let profession_oids = if !py_oids.is_none() {
let mut profession_oids = vec![];
for py_oid in py_oids.iter()? {
let py_oid = py_oid?;
let oid = py_oid_to_oid(py_oid)?;
profession_oids.push(oid);
}
Some(common::Asn1ReadableOrWritable::new_write(
asn1::SequenceOfWriter::new(profession_oids),
))
} else {
None
};
let py_registration_number = py_info.getattr(pyo3::intern!(py, "registration_number"))?;
let registration_number = if !py_registration_number.is_none() {
let py_registration_number_str =
ka_str.add(py_registration_number.extract::<PyBackedStr>()?);
match asn1::PrintableString::new(py_registration_number_str) {
Some(s) => Some(s),
None => {
return Err(CryptographyError::from(
pyo3::exceptions::PyValueError::new_err(
"registration_number value must be a valid PrintableString",
),
))
}
}
} else {
None
};
let py_add_profession_info = py_info.getattr(pyo3::intern!(py, "add_profession_info"))?;
let add_profession_info = if !py_add_profession_info.is_none() {
Some(ka_bytes.add(py_add_profession_info.extract::<pyo3::pybacked::PyBackedBytes>()?))
} else {
None
};
Ok(extensions::ProfessionInfo {
naming_authority,
profession_items,
profession_oids,
registration_number,
add_profession_info,
})
}

fn encode_admission<'a>(
py: pyo3::Python<'_>,
ka_bytes: &'a cryptography_keepalive::KeepAlive<pyo3::pybacked::PyBackedBytes>,
ka_str: &'a cryptography_keepalive::KeepAlive<pyo3::pybacked::PyBackedStr>,
py_admission: &pyo3::Bound<'a, pyo3::PyAny>,
) -> CryptographyResult<extensions::Admission<'a>> {
let py_admission_authority = py_admission.getattr(pyo3::intern!(py, "admission_authority"))?;
let admission_authority = if !py_admission_authority.is_none() {
Some(x509::common::encode_general_name(
py,
ka_bytes,
ka_str,
&py_admission_authority,
)?)
} else {
None
};
let py_naming_authority = py_admission.getattr(pyo3::intern!(py, "naming_authority"))?;
let naming_authority = if !py_naming_authority.is_none() {
Some(encode_naming_authority(py, ka_str, &py_naming_authority)?)
} else {
None
};

let py_profession_infos = py_admission.getattr(pyo3::intern!(py, "profession_infos"))?;
let mut profession_infos = vec![];
for py_info in py_profession_infos.iter()? {
profession_infos.push(encode_profession_info(py, ka_bytes, ka_str, &py_info?)?);
}
let profession_infos =
common::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new(profession_infos));
Ok(extensions::Admission {
admission_authority,
naming_authority,
profession_infos,
})
}

pub(crate) fn encode_extension(
py: pyo3::Python<'_>,
oid: &asn1::ObjectIdentifier,
Expand Down Expand Up @@ -563,6 +706,35 @@ pub(crate) fn encode_extension(
};
Ok(Some(asn1::write_single(&mstpl)?))
}
&oid::ADMISSIONS_OID => {
let ka_bytes = cryptography_keepalive::KeepAlive::new();
let ka_str = cryptography_keepalive::KeepAlive::new();
let py_admission_authority = ext.getattr(pyo3::intern!(py, "authority"))?;
let admission_authority = if !py_admission_authority.is_none() {
Some(x509::common::encode_general_name(
py,
&ka_bytes,
&ka_str,
&py_admission_authority,
)?)
} else {
None
};
let mut admissions = vec![];
for py_admission in ext.iter()? {
let admission = encode_admission(py, &ka_bytes, &ka_str, &py_admission?)?;
admissions.push(admission);
}

let contents_of_admissions =
common::Asn1ReadableOrWritable::new_write(asn1::SequenceOfWriter::new(admissions));

let admission = extensions::Admissions {
admission_authority,
contents_of_admissions,
};
Ok(Some(asn1::write_single(&admission)?))
}
_ => Ok(None),
}
}
Loading