Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ Changelog
empty ``GeneralNames`` sequence, matching the ``SIZE (1..MAX)`` constraint
RFC 5280 4.2.1.6/4.2.1.7 places on the field and the strictness already
applied to ``extendedKeyUsage``.
* Added the :meth:`PolicyBuilder.extended_key_usage
<cryptography.x509.verification.PolicyBuilder.extended_key_usage>` method to
configure the Extended Key Usage checked by X.509 verification policies.

.. _v50-0-2:

Expand Down
17 changes: 17 additions & 0 deletions docs/x509/verification.rst
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,23 @@ the root of trust:

:returns: A new instance of :class:`PolicyBuilder`

.. method:: extended_key_usage(extended_key_usage)

Sets the Extended Key Usage OID used for checking certificates.
Under the default extension policies, an end-entity certificate may
omit the EKU extension; if present, it must include this OID.
If this method is not called, client and server verifiers require
``clientAuth`` and ``serverAuth``, respectively.

The configured OID is also checked when a CA certificate includes an
EKU extension.

:param extended_key_usage: An
:class:`~cryptography.x509.ObjectIdentifier` for the required
Extended Key Usage

:returns: A new instance of :class:`PolicyBuilder`

.. method:: extension_policies(*, ee_policy, ca_policy)

.. versionadded:: 45.0.0
Expand Down
3 changes: 3 additions & 0 deletions src/cryptography/hazmat/bindings/_rust/x509.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ class PolicyBuilder:
def time(self, time: datetime.datetime) -> PolicyBuilder: ...
def store(self, store: Store) -> PolicyBuilder: ...
def max_chain_depth(self, max_chain_depth: int) -> PolicyBuilder: ...
def extended_key_usage(
self, extended_key_usage: x509.ObjectIdentifier
) -> PolicyBuilder: ...
def extension_policies(
self, *, ca_policy: ExtensionPolicy, ee_policy: ExtensionPolicy
) -> PolicyBuilder: ...
Expand Down
6 changes: 6 additions & 0 deletions src/rust/cryptography-x509-verification/src/policy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,12 @@ impl<'a, B: CryptoOps + 'a> PolicyDefinition<'a, B> {
Ok(retval)
}

/// Set the Extended Key Usage required by this policy.
pub fn with_extended_key_usage(mut self, extended_key_usage: ObjectIdentifier) -> Self {
self.extended_key_usage = extended_key_usage;
self
}

/// Create a new policy with suitable defaults for client certification
/// validation.
///
Expand Down
31 changes: 31 additions & 0 deletions src/rust/src/x509/verify/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub(crate) use extension_policy::{PyCriticality, PyExtensionPolicy};
pub(crate) use policy::PyPolicy;

use super::parse_general_names;
use crate::asn1::py_oid_to_oid;
use crate::backend::keys;
use crate::error::{CryptographyError, CryptographyResult};
use crate::types;
Expand Down Expand Up @@ -85,6 +86,7 @@ pub(crate) struct PolicyBuilder {
time: Option<asn1::DateTime>,
store: Option<pyo3::Py<PyStore>>,
max_chain_depth: Option<u8>,
extended_key_usage: Option<asn1::ObjectIdentifier>,
ca_ext_policy: Option<pyo3::Py<PyExtensionPolicy>>,
ee_ext_policy: Option<pyo3::Py<PyExtensionPolicy>>,
}
Expand All @@ -95,6 +97,7 @@ impl PolicyBuilder {
time: self.time.clone(),
store: self.store.as_ref().map(|s| s.clone_ref(py)),
max_chain_depth: self.max_chain_depth,
extended_key_usage: self.extended_key_usage.clone(),
ca_ext_policy: self.ca_ext_policy.as_ref().map(|p| p.clone_ref(py)),
ee_ext_policy: self.ee_ext_policy.as_ref().map(|p| p.clone_ref(py)),
}
Expand All @@ -109,6 +112,7 @@ impl PolicyBuilder {
time: None,
store: None,
max_chain_depth: None,
extended_key_usage: None,
ca_ext_policy: None,
ee_ext_policy: None,
}
Expand Down Expand Up @@ -153,6 +157,19 @@ impl PolicyBuilder {
})
}

fn extended_key_usage(
&self,
py: pyo3::Python<'_>,
extended_key_usage: pyo3::Bound<'_, pyo3::PyAny>,
) -> CryptographyResult<PolicyBuilder> {
policy_builder_set_once_check!(self, extended_key_usage, "extended key usage");

Ok(PolicyBuilder {
extended_key_usage: Some(py_oid_to_oid(extended_key_usage)?),
..self.py_clone(py)
})
}

#[pyo3(signature = (*, ca_policy, ee_policy))]
fn extension_policies(
&self,
Expand Down Expand Up @@ -199,6 +216,13 @@ impl PolicyBuilder {
.as_ref()
.map(|p| p.get().clone_inner_policy()),
)
.map(|policy_definition| {
if let Some(extended_key_usage) = &self.extended_key_usage {
policy_definition.with_extended_key_usage(extended_key_usage.clone())
} else {
policy_definition
}
})
.map_err(pyo3::exceptions::PyValueError::new_err)
})?;

Expand Down Expand Up @@ -256,6 +280,13 @@ impl PolicyBuilder {
.as_ref()
.map(|p| p.get().clone_inner_policy()),
)
.map(|policy_definition| {
if let Some(extended_key_usage) = &self.extended_key_usage {
policy_definition.with_extended_key_usage(extended_key_usage.clone())
} else {
policy_definition
}
})
.map_err(pyo3::exceptions::PyValueError::new_err)
})?;

Expand Down
29 changes: 29 additions & 0 deletions tests/x509/verification/test_verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,35 @@ def test_max_chain_depth_already_set(self):
with pytest.raises(ValueError):
PolicyBuilder().max_chain_depth(8).max_chain_depth(9)

def test_extended_key_usage_already_set(self):
builder = PolicyBuilder().extended_key_usage(
ExtendedKeyUsageOID.CODE_SIGNING
)
with pytest.raises(ValueError):
builder.extended_key_usage(ExtendedKeyUsageOID.CODE_SIGNING)

def test_extended_key_usage_bad_type(self):
with pytest.raises(TypeError):
PolicyBuilder().extended_key_usage(
typing.cast(typing.Any, "not an object identifier")
)

def test_extended_key_usage(self):
extended_key_usage = ExtendedKeyUsageOID.CODE_SIGNING
builder = (
PolicyBuilder()
.store(dummy_store())
.extended_key_usage(extended_key_usage)
)

server_verifier = builder.build_server_verifier(
DNSName("cryptography.io")
)
client_verifier = builder.build_client_verifier()

assert server_verifier.policy.extended_key_usage == extended_key_usage
assert client_verifier.policy.extended_key_usage == extended_key_usage

def test_ipaddress_subject(self):
verifier = (
PolicyBuilder()
Expand Down
Loading