Skip to content

X509 Verification Custom Extension Policies #12360

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

Closed
wants to merge 15 commits into from
Closed
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
1 change: 1 addition & 0 deletions docs/spelling_wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ committers
conda
CPython
Cryptanalysis
criticalities
crypto
cryptographic
cryptographically
Expand Down
223 changes: 198 additions & 25 deletions docs/x509/verification.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ the root of trust:

.. versionadded:: 43.0.0

.. versionchanged:: 44.0.0
.. versionchanged:: 45.0.0
Made ``subjects`` optional with the addition of custom extension policies.

.. attribute:: subjects
Expand All @@ -133,6 +133,10 @@ the root of trust:

.. versionadded:: 43.0.0

.. versionchanged:: 45.0.0
``verification_time`` and ``max_chain_depth`` were replaced by the
``policy`` property that provides access to these values.

A ClientVerifier verifies client certificates.

It contains and describes various pieces of configurable path
Expand All @@ -142,17 +146,11 @@ the root of trust:
ClientVerifier instances cannot be constructed directly;
:class:`PolicyBuilder` must be used.

.. attribute:: validation_time

:type: :class:`datetime.datetime`

The verifier's validation time.

.. attribute:: max_chain_depth
.. attribute:: policy

:type: :class:`int`
:type: :class:`Policy`

The verifier's maximum intermediate CA chain depth.
The policy used by the verifier. Can be used to access verification time, chain depth, etc.

.. attribute:: store

Expand Down Expand Up @@ -181,6 +179,11 @@ the root of trust:

.. versionadded:: 42.0.0

.. versionchanged:: 45.0.0
``subject``, ``verification_time`` and ``max_chain_depth`` were replaced by the
``policy`` property that provides access to these values.


A ServerVerifier verifies server certificates.

It contains and describes various pieces of configurable path
Expand All @@ -191,23 +194,11 @@ the root of trust:
ServerVerifier instances cannot be constructed directly;
:class:`PolicyBuilder` must be used.

.. attribute:: subject

:type: :class:`Subject`

The verifier's subject.

.. attribute:: validation_time

:type: :class:`datetime.datetime`

The verifier's validation time.
.. attribute:: policy

.. attribute:: max_chain_depth

:type: :class:`int`
:type: :class:`Policy`

The verifier's maximum intermediate CA chain depth.
The policy used by the verifier. Can be used to access verification time, chain depth, etc.

.. attribute:: store

Expand Down Expand Up @@ -276,6 +267,20 @@ the root of trust:

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

.. method:: extension_policies(new_ee_policy, new_ca_policy)

.. versionadded:: 45.0.0

Sets the EE and CA extension policies for the verifier.
The default policies used are those returned by :meth:`ExtensionPolicy.webpki_defaults_ee`
and :meth:`ExtensionPolicy.webpki_defaults_ca`.

:param ExtensionPolicy new_ca_policy: The CA extension policy to use.
:param ExtensionPolicy new_ee_policy: The EE extension policy to use.

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


.. method:: build_server_verifier(subject)

Builds a verifier for verifying server certificates.
Expand All @@ -297,3 +302,171 @@ the root of trust:
for server verification.

:returns: An instance of :class:`ClientVerifier`

.. class:: ExtensionPolicy

.. versionadded:: 45.0.0

ExtensionPolicy provides a set of static methods to construct predefined
extension policies, and a builder-style interface for modifying them.

.. note:: Calling any of the builder methods (:meth:`require_not_present`, :meth:`may_be_present`, or :meth:`require_present`)
multiple times with the same extension OID will raise an exception.

.. staticmethod:: permit_all()

Creates an ExtensionPolicy initialized with a policy that does
not put any constraints on a certificate's extensions.
This can serve as a base for a fully custom extension policy.

:returns: An instance of :class:`ExtensionPolicy`

.. staticmethod:: webpki_defaults_ca()

Creates an ExtensionPolicy initialized with a
CA extension policy based on CA/B Forum guidelines.

This is the CA extension policy used by :class:`PolicyBuilder`.

:returns: An instance of :class:`ExtensionPolicy`

.. staticmethod:: webpki_defaults_ee()

Creates an ExtensionPolicy initialized with an
EE extension policy based on CA/B Forum guidelines.

This is the EE extension policy used by :class:`PolicyBuilder`.

:returns: An instance of :class:`ExtensionPolicy`

.. method:: require_not_present(oid)

Specifies that the extension identified by the given OID must not be present (must be absent).

:param oid: The OID of the extension that must not be present.

:returns: An instance of :class:`ExtensionPolicy`

.. method:: may_be_present(oid, criticality, validator_cb)

Specifies that the extension identified by the given OID is optional.
If it is present, it must conform to the given criticality constraint.
An optional validator callback may be provided.

If a validator callback is provided, the callback will be invoked
when :meth:`ClientVerifier.verify` or :meth:`ServerVerifier.verify` is called on a verifier
that uses the extension policy. For details on the callback signature, see :type:`MaybeExtensionValidatorCallback`.

:param ObjectIdentifier oid: The OID of the extension that may be present
:param Criticality criticality: The criticality of the extension
:param validator_cb: An optional Python callback to validate the extension value.
:type validator_cb: :type:`MaybeExtensionValidatorCallback` or None

:returns: An instance of :class:`ExtensionPolicy`

.. method:: require_present(oid, criticality, validator_cb)

Specifies that the extension identified by the given OID must be present and conform to the given criticality constraint.
An optional validator callback may be provided.

If a validator callback is provided, the callback will be invoked
when :meth:`ClientVerifier.verify` or :meth:`ServerVerifier.verify` is called on a verifier
that uses the extension policy. For details on the callback signature, see :type:`PresentExtensionValidatorCallback`.

:param ObjectIdentifier oid: The OID of the extension that must be present
:param Criticality criticality: The criticality of the extension
:param validator_cb: An optional Python callback to validate the extension
:type validator_cb: :type:`PresentExtensionValidatorCallback` or None

:returns: An instance of :class:`ExtensionPolicy`

.. class:: Criticality

.. versionadded:: 45.0.0

An enumeration of criticality constraints for certificate extensions.

.. attribute:: CRITICAL

The extension must be marked as critical.

.. attribute:: AGNOSTIC

The extension may be marked either as critical or non-critical.

.. attribute:: NON_CRITICAL

The extension must not be marked as critical.

.. class:: Policy

.. versionadded:: 45.0.0

Represents a policy for certificate verification. Passed to extension validator callbacks and
accessible via :class:`ClientVerifier` and :class:`ServerVerifier`.

.. attribute:: max_chain_depth

The maximum chain depth (as described in :meth:`PolicyBuilder.max_chain_depth`).

:type: int

.. attribute:: subject

The subject used during verification.
Will be None if the verifier is a :class:`ClientVerifier`.

:type: x509.verification.Subject or None

.. attribute:: validation_time

The validation time.

:type: datetime.datetime

.. attribute:: extended_key_usage

The Extended Key Usage required by the policy.

:type: x509.ObjectIdentifier

.. attribute:: minimum_rsa_modulus

The minimum RSA modulus size required by the policy.

:type: int

.. type:: MaybeExtensionValidatorCallback
:canonical: Callable[[Policy, Certificate, Optional[ExtensionType]], None]

.. versionadded:: 45.0.0


A Python callback that validates an extension that may or may not be present.
If the extension is not present, the callback will be invoked with `ext` set to `None`.

To fail the validation, the callback must raise an exception.

:param Policy policy: The verification policy.
:param Certificate certificate: The certificate being verified.
:param ExtensionType or None extension: The extension value or `None` if the extension is not present.

:returns: An extension validator callback must return `None`.
If the validation fails, the validator must raise an exception.

.. type:: PresentExtensionValidatorCallback
:canonical: Callable[[Policy, Certificate, ExtensionType], None]

.. versionadded:: 45.0.0


A Python callback that validates an extension that must be present.

To fail the validation, the callback must raise an exception.

:param Policy policy: The verification policy.
:param Certificate certificate: The certificate being verified.
:param ExtensionType extension: The extension value.

:returns: An extension validator callback must return `None`.
If the validation fails, the validator must raise an exception.
69 changes: 61 additions & 8 deletions src/cryptography/hazmat/bindings/_rust/x509.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,70 @@ class PolicyBuilder:
def time(self, new_time: datetime.datetime) -> PolicyBuilder: ...
def store(self, new_store: Store) -> PolicyBuilder: ...
def max_chain_depth(self, new_max_chain_depth: int) -> PolicyBuilder: ...
def extension_policies(
self,
new_ca_extension_policy: ExtensionPolicy,
new_ee_extension_policy: ExtensionPolicy,
) -> PolicyBuilder: ...
def build_client_verifier(self) -> ClientVerifier: ...
def build_server_verifier(
self, subject: x509.verification.Subject
) -> ServerVerifier: ...

class Criticality:
CRITICAL: Criticality
AGNOSTIC: Criticality
NON_CRITICAL: Criticality

class Policy:
@property
def max_chain_depth(self) -> int: ...
@property
def subject(self) -> x509.verification.Subject: ...
@property
def validation_time(self) -> datetime.datetime: ...
@property
def extended_key_usage(self) -> x509.ObjectIdentifier: ...
@property
def minimum_rsa_modulus(self) -> int: ...

MaybeExtensionValidatorCallback = typing.Callable[
[
Policy,
x509.Certificate,
x509.ExtensionType | None,
],
None,
]

PresentExtensionValidatorCallback = typing.Callable[
[Policy, x509.Certificate, x509.ExtensionType],
None,
]

class ExtensionPolicy:
@staticmethod
def permit_all() -> ExtensionPolicy: ...
@staticmethod
def webpki_defaults_ca() -> ExtensionPolicy: ...
@staticmethod
def webpki_defaults_ee() -> ExtensionPolicy: ...
def require_not_present(
self, oid: x509.ObjectIdentifier
) -> ExtensionPolicy: ...
def may_be_present(
self,
oid: x509.ObjectIdentifier,
criticality: Criticality,
validator: MaybeExtensionValidatorCallback | None,
) -> ExtensionPolicy: ...
def require_present(
self,
oid: x509.ObjectIdentifier,
criticality: Criticality,
validator: PresentExtensionValidatorCallback | None,
) -> ExtensionPolicy: ...

class VerifiedClient:
@property
def subjects(self) -> list[x509.GeneralName] | None: ...
Expand All @@ -214,11 +273,9 @@ class VerifiedClient:

class ClientVerifier:
@property
def validation_time(self) -> datetime.datetime: ...
def policy(self) -> Policy: ...
@property
def store(self) -> Store: ...
@property
def max_chain_depth(self) -> int: ...
def verify(
self,
leaf: x509.Certificate,
Expand All @@ -227,13 +284,9 @@ class ClientVerifier:

class ServerVerifier:
@property
def subject(self) -> x509.verification.Subject: ...
@property
def validation_time(self) -> datetime.datetime: ...
def policy(self) -> Policy: ...
@property
def store(self) -> Store: ...
@property
def max_chain_depth(self) -> int: ...
def verify(
self,
leaf: x509.Certificate,
Expand Down
3 changes: 3 additions & 0 deletions src/cryptography/x509/verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,8 @@
VerifiedClient = rust_x509.VerifiedClient
ClientVerifier = rust_x509.ClientVerifier
ServerVerifier = rust_x509.ServerVerifier
Policy = rust_x509.Policy
ExtensionPolicy = rust_x509.ExtensionPolicy
Criticality = rust_x509.Criticality
PolicyBuilder = rust_x509.PolicyBuilder
VerificationError = rust_x509.VerificationError
Loading
Loading