Skip to content

Commit 8feaef9

Browse files
Add support for parameterised validation
The initial set of tests includes only some basic policy checks from PKITS; more to come.
1 parent 11df11c commit 8feaef9

File tree

5 files changed

+653
-46
lines changed

5 files changed

+653
-46
lines changed

pyhanko_certvalidator/context.py

+79
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from __future__ import unicode_literals, division, absolute_import, print_function
33

44
import socket
5+
from dataclasses import dataclass, field
56
from datetime import datetime, timedelta
67
import binascii
78

@@ -13,6 +14,8 @@
1314
from ._errors import pretty_message
1415
from ._types import type_name, byte_cls, str_cls
1516
from .errors import SoftFailError
17+
from .name_trees import default_permitted_subtrees, PKIXSubtrees, \
18+
default_excluded_subtrees
1619
from .path import ValidationPath
1720
from .registry import CertificateRegistry
1821

@@ -611,3 +614,79 @@ def check_crl_issuer(self, certificate_list):
611614
"""
612615

613616
return self._crl_issuer_map.get(certificate_list.signature)
617+
618+
619+
@dataclass(frozen=True)
620+
class PKIXValidationParams:
621+
user_initial_policy_set: frozenset = frozenset(['any_policy'])
622+
"""
623+
Set of policies that the user is willing to accept. By default, any policy
624+
is acceptable.
625+
626+
When setting this parameter to a non-default value, you probably want to
627+
set :attr:`initial_explicit_policy` as well.
628+
629+
.. note::
630+
These are specified in the policy domain of the trust root(s), and
631+
subject to policy mapping by intermediate certificate authorities.
632+
"""
633+
634+
initial_policy_mapping_inhibit: bool = False
635+
"""
636+
Flag indicating whether policy mapping is forbidden along the entire
637+
certification chains. By default, policy mapping is permitted.
638+
639+
.. note::
640+
Policy constraints on intermediate certificates may force policy mapping
641+
to be inhibited from some point onwards.
642+
"""
643+
644+
initial_explicit_policy: bool = False
645+
"""
646+
Flag indicating whether path validation must terminate with at least one
647+
permissible policy; see :attr:`user_initial_policy_set`.
648+
By default, no such requirement is imposed.
649+
650+
.. note::
651+
If :attr:`user_initial_policy_set` is set to its default value of
652+
``{'any_policy'}``, the effect is that the path validation must accept
653+
at least one policy, without specifying which.
654+
655+
.. warning::
656+
Due to widespread mis-specification of policy extensions in the wild,
657+
many real-world certification chains terminate with an empty set
658+
(or rather, tree) of valid policies. Therefore, this flag is set to
659+
``False`` by default.
660+
"""
661+
662+
initial_any_policy_inhibit: bool = False
663+
"""
664+
Flag indicating whether ``anyPolicy`` should be left unprocessed when it
665+
appears in a certificate. By default, ``anyPolicy`` is always processed
666+
when it appears.
667+
"""
668+
669+
initial_permitted_subtrees: PKIXSubtrees = \
670+
field(default_factory=default_permitted_subtrees)
671+
"""
672+
Set of permitted subtrees for each name type, indicating restrictions
673+
to impose on subject names (and alternative names) in the certification
674+
path.
675+
676+
By default, all names are permitted.
677+
This behaviour can be modified by name constraints on intermediate CA
678+
certificates.
679+
"""
680+
681+
initial_excluded_subtrees: PKIXSubtrees = field(
682+
default_factory=default_excluded_subtrees
683+
)
684+
"""
685+
Set of excluded subtrees for each name type, indicating restrictions
686+
to impose on subject names (and alternative names) in the certification
687+
path.
688+
689+
By default, no names are excluded.
690+
This behaviour can be modified by name constraints on intermediate CA
691+
certificates.
692+
"""

pyhanko_certvalidator/name_trees.py

+11
Original file line numberDiff line numberDiff line change
@@ -333,3 +333,14 @@ def accept_cert(self, cert: x509.Certificate) \
333333
)
334334
except StopIteration:
335335
return NameConstraintValidationResult()
336+
337+
338+
def default_permitted_subtrees() -> PKIXSubtrees:
339+
return {
340+
name_type: {NameSubtree.universal_tree(name_type)}
341+
for name_type in GeneralNameType
342+
}
343+
344+
345+
def default_excluded_subtrees() -> PKIXSubtrees:
346+
return {name_type: set() for name_type in GeneralNameType}

pyhanko_certvalidator/path.py

+18
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
# coding: utf-8
22
from __future__ import unicode_literals, division, absolute_import, print_function
33

4+
from collections import Set
5+
from dataclasses import dataclass
6+
47
from asn1crypto import pem, x509
58

69
from ._errors import pretty_message
710
from ._types import byte_cls, type_name
811
from .errors import DuplicateCertificateError
912

1013

14+
@dataclass(frozen=True)
15+
class QualifiedPolicy:
16+
user_domain_policy_id: str
17+
issuer_domain_policy_id: str
18+
qualifiers: frozenset
19+
20+
1121
class ValidationPath():
1222
"""
1323
Represents a path going towards an end-entity certificate
@@ -21,6 +31,8 @@ class ValidationPath():
2131
# certificates that are already in ._certs
2232
_cert_hashes = None
2333

34+
_qualified_policies = None
35+
2436
def __init__(self, end_entity_cert=None):
2537
"""
2638
:param end_entity_cert:
@@ -222,6 +234,12 @@ def prepend(self, cert):
222234

223235
return self
224236

237+
def _set_qualified_policies(self, policies):
238+
self._qualified_policies = frozenset(policies)
239+
240+
def qualified_policies(self) -> Set[QualifiedPolicy]:
241+
return self._qualified_policies
242+
225243
def __len__(self):
226244
return len(self._certs)
227245

0 commit comments

Comments
 (0)