Skip to content

Commit 2efcd5b

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 2efcd5b

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

Diff for: src/OpenSSL/crypto.py

+38
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.
@@ -1832,6 +1855,21 @@ def load_locations(
18321855
cafile: StrOrBytesPath | None,
18331856
capath: StrOrBytesPath | None = None,
18341857
) -> None:
1858+
1859+
def set_purpose(self, purpose):
1860+
"""
1861+
Set purpose of this store.
1862+
1863+
.. versionadded:: 26.0.0
1864+
1865+
:param int flags: The verification flags to set on this store.
1866+
See :class:`X509StorePurposes` for available constants.
1867+
:return: ``None`` if the verification flags were successfully set.
1868+
"""
1869+
_openssl_assert(_lib.X509_STORE_set_purpose(self._store, purpose) != 0)
1870+
1871+
1872+
def load_locations(self, cafile, capath=None):
18351873
"""
18361874
Let X509Store know where we can find trusted certificates for the
18371875
certificate chain. Note that the certificates have to be in PEM

Diff for: tests/test_crypto.py

+29-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
X509StoreContext,
4444
X509StoreContextError,
4545
X509StoreFlags,
46+
X509StorePurposes,
4647
_EllipticCurve,
4748
_Key,
4849
_PrivateKey,
@@ -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(X509StorePurposes.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(X509StorePurposes.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)