Skip to content

Commit

Permalink
Expose set_purpose on X509Store to allow verify_certificate with purpose
Browse files Browse the repository at this point in the history
Signed-off-by: Arne Schwabe <[email protected]>
  • Loading branch information
schwabe committed Feb 1, 2022
1 parent ca63d31 commit e2a6ad9
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 0 deletions.
39 changes: 39 additions & 0 deletions src/OpenSSL/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"X509Req",
"X509",
"X509StoreFlags",
"X509StorePurposes",
"X509Store",
"X509StoreContextError",
"X509StoreContext",
Expand Down Expand Up @@ -1583,6 +1584,28 @@ class X509StoreFlags:
CHECK_SS_SIGNATURE = _lib.X509_V_FLAG_CHECK_SS_SIGNATURE


class X509StorePurposes:
"""
Flags for X509 verification, used to change the behavior of
:class:`X509Store`.
See `OpenSSL check purpose`_ for details.
.. _OpenSSL check purpose:
https://www.openssl.org/docs/manmaster/man3/X509_check_purpose.html
"""

X509_PURPOSE_SSL_CLIENT = _lib.X509_PURPOSE_SSL_CLIENT
X509_PURPOSE_SSL_SERVER = _lib.X509_PURPOSE_SSL_SERVER
X509_PURPOSE_NS_SSL_SERVER = _lib.X509_PURPOSE_NS_SSL_SERVER
X509_PURPOSE_SMIME_SIGN = _lib.X509_PURPOSE_SMIME_SIGN
X509_PURPOSE_SMIME_ENCRYPT = _lib.X509_PURPOSE_SMIME_ENCRYPT
X509_PURPOSE_CRL_SIGN = _lib.X509_PURPOSE_CRL_SIGN
X509_PURPOSE_ANY = _lib.X509_PURPOSE_ANY
X509_PURPOSE_OCSP_HELPER = _lib.X509_PURPOSE_OCSP_HELPER
X509_PURPOSE_TIMESTAMP_SIGN = _lib.X509_PURPOSE_TIMESTAMP_SIGN


class X509Store:
"""
An X.509 store.
Expand Down Expand Up @@ -1687,6 +1710,22 @@ def set_time(self, vfy_time):
)
_openssl_assert(_lib.X509_STORE_set1_param(self._store, param) != 0)

def set_purpose(self, purpose):
"""
Set purpose of this store.
.. versionadded:: 22.1.0
:param int flags: The verification flags to set on this store.
See :class:`X509StorePurposes` for available constants.
:return: ``None`` if the verification flags were successfully set.
"""

param = _lib.X509_VERIFY_PARAM_new()
param = _ffi.gc(param, _lib.X509_VERIFY_PARAM_free)
_lib.X509_VERIFY_PARAM_set_purpose(param, purpose)
_openssl_assert(_lib.X509_STORE_set1_param(self._store, param) != 0)

def load_locations(self, cafile, capath=None):
"""
Let X509Store know where we can find trusted certificates for the
Expand Down
28 changes: 28 additions & 0 deletions tests/test_crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from OpenSSL.crypto import (
X509Store,
X509StoreFlags,
X509StorePurposes,
X509StoreContext,
X509StoreContextError,
)
Expand Down Expand Up @@ -3528,6 +3529,7 @@ class TestCRL:
intermediate_server_key = load_privatekey(
FILETYPE_PEM, intermediate_server_key_pem
)
server_cert = load_certificate(FILETYPE_PEM, server_cert_pem)

def test_construction(self):
"""
Expand Down Expand Up @@ -3835,6 +3837,32 @@ def test_verify_with_revoked(self):
store_ctx.verify_certificate()
assert err.value.args[0][2] == "certificate revoked"

def test_verify_with_correct_purpose(self):
store = X509Store()
store.add_cert(self.root_cert)
store.add_cert(self.intermediate_cert)
store.set_purpose(X509StorePurposes.X509_PURPOSE_SSL_SERVER)

store_ctx = X509StoreContext(store, self.server_cert)
store_ctx.verify_certificate()

# The intermediate server certificate has no EKU and so it is fit
# for any purpose
store_ctx = X509StoreContext(store, self.intermediate_server_cert)
store_ctx.verify_certificate()

def test_verify_with_incorrect_purpose(self):
store = X509Store()
store.add_cert(self.root_cert)
store.add_cert(self.intermediate_cert)
store.set_purpose(X509StorePurposes.X509_PURPOSE_SSL_CLIENT)

store_ctx = X509StoreContext(store, self.server_cert)
with pytest.raises(X509StoreContextError) as err:
store_ctx.verify_certificate()

assert err.value.args[0][2] == "unsupported certificate purpose"

def test_verify_with_missing_crl(self):
"""
`verify_certificate` raises error when an intermediate certificate's
Expand Down

0 comments on commit e2a6ad9

Please sign in to comment.