Skip to content

Commit e13e4c6

Browse files
committed
add pkcs7_x509_extension_policies from PR pyca#12465 of @nitneuqr
1 parent 3032224 commit e13e4c6

File tree

1 file changed

+120
-0
lines changed
  • src/cryptography/hazmat/primitives/serialization

1 file changed

+120
-0
lines changed

src/cryptography/hazmat/primitives/serialization/pkcs7.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121
algorithms,
2222
)
2323
from cryptography.utils import _check_byteslike
24+
from cryptography.x509.oid import ExtendedKeyUsageOID
25+
from cryptography.x509.verification import (
26+
Criticality,
27+
ExtensionPolicy,
28+
Policy,
29+
)
2430

2531
load_pem_pkcs7_certificates = rust_pkcs7.load_pem_pkcs7_certificates
2632

@@ -53,6 +59,120 @@ class PKCS7Options(utils.Enum):
5359
NoCerts = "Don't embed signer certificate"
5460

5561

62+
def pkcs7_x509_extension_policies() -> tuple[ExtensionPolicy, ExtensionPolicy]:
63+
"""
64+
Gets the default X.509 extension policy for S/MIME, based on RFC 8550.
65+
Visit https://www.rfc-editor.org/rfc/rfc8550#section-4.4 for more info.
66+
"""
67+
# CA policy
68+
ca_policy = ExtensionPolicy.webpki_defaults_ca()
69+
70+
# EE policy
71+
def _validate_basic_constraints(
72+
policy: Policy, cert: Certificate, bc: x509.BasicConstraints | None
73+
) -> None:
74+
"""
75+
We check that Certificates used as EE (i.e., the cert used to sign
76+
a PKCS#7/SMIME message) must not have ca=true in their basic
77+
constraints extension. RFC 5280 doesn't impose this requirement, but we
78+
firmly agree about it being best practice.
79+
"""
80+
if bc is not None and bc.ca:
81+
raise ValueError("Basic Constraints CA must be False.")
82+
83+
def _validate_key_usage(
84+
policy: Policy, cert: Certificate, ku: x509.KeyUsage | None
85+
) -> None:
86+
"""
87+
Checks that the Key Usage extension, if present, has at least one of
88+
the digital signature or content commitment (formerly non-repudiation)
89+
bits set.
90+
"""
91+
if (
92+
ku is not None
93+
and not ku.digital_signature
94+
and not ku.content_commitment
95+
):
96+
raise ValueError(
97+
"Key Usage, if specified, must have at least one of the "
98+
"digital signature or content commitment (formerly non "
99+
"repudiation) bits set."
100+
)
101+
102+
def _validate_subject_alternative_name(
103+
policy: Policy,
104+
cert: Certificate,
105+
san: x509.SubjectAlternativeName,
106+
) -> None:
107+
"""
108+
For each general name in the SAN, for those which are email addresses:
109+
- If it is an RFC822Name, general part must be ascii.
110+
- If it is an OtherName, general part must be non-ascii.
111+
"""
112+
for general_name in san:
113+
if (
114+
isinstance(general_name, x509.RFC822Name)
115+
and "@" in general_name.value
116+
and not general_name.value.split("@")[0].isascii()
117+
):
118+
raise ValueError(
119+
f"RFC822Name {general_name.value} contains non-ASCII "
120+
"characters."
121+
)
122+
if (
123+
isinstance(general_name, x509.OtherName)
124+
and "@" in general_name.value.decode()
125+
and general_name.value.decode().split("@")[0].isascii()
126+
):
127+
raise ValueError(
128+
f"OtherName {general_name.value.decode()} is ASCII, "
129+
"so must be stored in RFC822Name."
130+
)
131+
132+
def _validate_extended_key_usage(
133+
policy: Policy, cert: Certificate, eku: x509.ExtendedKeyUsage | None
134+
) -> None:
135+
"""
136+
Checks that the Extended Key Usage extension, if present,
137+
includes either emailProtection or anyExtendedKeyUsage bits.
138+
"""
139+
if (
140+
eku is not None
141+
and ExtendedKeyUsageOID.EMAIL_PROTECTION not in eku
142+
and ExtendedKeyUsageOID.ANY_EXTENDED_KEY_USAGE not in eku
143+
):
144+
raise ValueError(
145+
"Extended Key Usage, if specified, must include "
146+
"emailProtection or anyExtendedKeyUsage."
147+
)
148+
149+
ee_policy = (
150+
ExtensionPolicy.webpki_defaults_ee()
151+
.may_be_present(
152+
x509.BasicConstraints,
153+
Criticality.AGNOSTIC,
154+
_validate_basic_constraints,
155+
)
156+
.may_be_present(
157+
x509.KeyUsage,
158+
Criticality.CRITICAL,
159+
_validate_key_usage,
160+
)
161+
.require_present(
162+
x509.SubjectAlternativeName,
163+
Criticality.AGNOSTIC,
164+
_validate_subject_alternative_name,
165+
)
166+
.may_be_present(
167+
x509.ExtendedKeyUsage,
168+
Criticality.AGNOSTIC,
169+
_validate_extended_key_usage,
170+
)
171+
)
172+
173+
return ca_policy, ee_policy
174+
175+
56176
class PKCS7SignatureBuilder:
57177
def __init__(
58178
self,

0 commit comments

Comments
 (0)