Skip to content

Commit af1a5e8

Browse files
committed
Expose set_purpose on X509Store to allow setting purpose of a store
Signed-off-by: Arne Schwabe <[email protected]>
1 parent 787f176 commit af1a5e8

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

src/OpenSSL/crypto.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def deprecated(msg: str, **kwargs: object) -> Callable[[_T], _T]:
6666
"X509Extension",
6767
"X509Name",
6868
"X509Req",
69+
"X509Purpose",
6970
"X509Store",
7071
"X509StoreContext",
7172
"X509StoreContextError",
@@ -1709,6 +1710,28 @@ class X509StoreFlags:
17091710
PARTIAL_CHAIN: int = _lib.X509_V_FLAG_PARTIAL_CHAIN
17101711

17111712

1713+
class X509Purpose:
1714+
"""
1715+
Enumeration of X509 purposes, e.g. used to set the purpose of a
1716+
:class:`X509Store`.
1717+
1718+
See `OpenSSL check purpose`_ for details.
1719+
1720+
.. _OpenSSL check purpose:
1721+
https://www.openssl.org/docs/manmaster/man3/X509_check_purpose.html
1722+
"""
1723+
1724+
X509_PURPOSE_SSL_CLIENT = _lib.X509_PURPOSE_SSL_CLIENT
1725+
X509_PURPOSE_SSL_SERVER = _lib.X509_PURPOSE_SSL_SERVER
1726+
X509_PURPOSE_NS_SSL_SERVER = _lib.X509_PURPOSE_NS_SSL_SERVER
1727+
X509_PURPOSE_SMIME_SIGN = _lib.X509_PURPOSE_SMIME_SIGN
1728+
X509_PURPOSE_SMIME_ENCRYPT = _lib.X509_PURPOSE_SMIME_ENCRYPT
1729+
X509_PURPOSE_CRL_SIGN = _lib.X509_PURPOSE_CRL_SIGN
1730+
X509_PURPOSE_ANY = _lib.X509_PURPOSE_ANY
1731+
X509_PURPOSE_OCSP_HELPER = _lib.X509_PURPOSE_OCSP_HELPER
1732+
X509_PURPOSE_TIMESTAMP_SIGN = _lib.X509_PURPOSE_TIMESTAMP_SIGN
1733+
1734+
17121735
class X509Store:
17131736
"""
17141737
An X.509 store.
@@ -1827,6 +1850,18 @@ def set_time(self, vfy_time: datetime.datetime) -> None:
18271850
)
18281851
_openssl_assert(_lib.X509_STORE_set1_param(self._store, param) != 0)
18291852

1853+
def set_purpose(self, purpose):
1854+
"""
1855+
Set purpose of this store.
1856+
1857+
.. versionadded:: 26.0.0
1858+
1859+
:param int flags: The verification flags to set on this store.
1860+
See :class:`X509StorePurposes` for available constants.
1861+
:return: ``None`` if the verification flags were successfully set.
1862+
"""
1863+
_openssl_assert(_lib.X509_STORE_set_purpose(self._store, purpose) != 0)
1864+
18301865
def load_locations(
18311866
self,
18321867
cafile: StrOrBytesPath | None,

tests/test_crypto.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
PKey,
3939
X509Extension,
4040
X509Name,
41+
X509Purpose,
4142
X509Req,
4243
X509Store,
4344
X509StoreContext,
@@ -3007,6 +3008,7 @@ class TestCRL:
30073008
intermediate_server_key = load_privatekey(
30083009
FILETYPE_PEM, intermediate_server_key_pem
30093010
)
3011+
server_cert = load_certificate(FILETYPE_PEM, server_cert_pem)
30103012

30113013
@staticmethod
30123014
def _make_test_crl_cryptography(
@@ -3069,7 +3071,33 @@ def test_verify_with_revoked(self) -> None:
30693071
store_ctx.verify_certificate()
30703072
assert str(err.value) == "certificate revoked"
30713073

3072-
def test_verify_with_missing_crl(self) -> None:
3074+
def test_verify_with_correct_purpose(self):
3075+
store = X509Store()
3076+
store.add_cert(self.root_cert)
3077+
store.add_cert(self.intermediate_cert)
3078+
store.set_purpose(X509Purpose.X509_PURPOSE_SSL_SERVER)
3079+
3080+
store_ctx = X509StoreContext(store, self.server_cert)
3081+
store_ctx.verify_certificate()
3082+
3083+
# The intermediate server certificate has no EKU and so it is fit
3084+
# for any purpose
3085+
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
3086+
store_ctx.verify_certificate()
3087+
3088+
def test_verify_with_incorrect_purpose(self):
3089+
store = X509Store()
3090+
store.add_cert(self.root_cert)
3091+
store.add_cert(self.intermediate_cert)
3092+
store.set_purpose(X509Purpose.X509_PURPOSE_SSL_CLIENT)
3093+
3094+
store_ctx = X509StoreContext(store, self.server_cert)
3095+
with pytest.raises(X509StoreContextError) as err:
3096+
store_ctx.verify_certificate()
3097+
3098+
assert err.value.args[0][2] == "unsupported certificate purpose"
3099+
3100+
def test_verify_with_missing_crl(self):
30733101
"""
30743102
`verify_certificate` raises error when an intermediate certificate's
30753103
CRL is missing.

0 commit comments

Comments
 (0)