diff options
author | Sándor Oroszi <oroszisam@gmail.com> | 2020-10-12 15:42:23 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-10-12 08:42:23 -0500 |
commit | 83ef2306a1481e0cf7f53899c390497256711e29 (patch) | |
tree | f27916271dde163143de9d7963805695d3f038b9 /tests | |
parent | 43c97767afe7aa1dccefec35946dbc8b9cde73a4 (diff) | |
download | pyopenssl-83ef2306a1481e0cf7f53899c390497256711e29.tar.gz |
Allow using additional untrusted certificates for chain building in X509StoreContext (#948)
The additional certificates provided in the new `chain` parameter will be
untrusted but may be used to build the chain.
This makes it easier to validate a certificate against a store which
contains only root ca certificates, and the intermediates come from e.g.
the same untrusted source as the certificate to be verified.
Co-authored-by: Sandor Oroszi <sandor.oroszi@balabit.com>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/test_crypto.py | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/tests/test_crypto.py b/tests/test_crypto.py index 820a4b2..815bd8b 100644 --- a/tests/test_crypto.py +++ b/tests/test_crypto.py @@ -3824,6 +3824,145 @@ class TestX509StoreContext(object): assert store_ctx.verify_certificate() is None assert store_ctx.verify_certificate() is None + @pytest.mark.parametrize( + "root_cert, chain, verified_cert", + [ + pytest.param( + root_cert, + [intermediate_cert], + intermediate_server_cert, + id="intermediate in chain", + ), + pytest.param( + root_cert, + [], + intermediate_cert, + id="empty chain", + ), + pytest.param( + root_cert, + [root_cert, intermediate_server_cert, intermediate_cert], + intermediate_server_cert, + id="extra certs in chain", + ), + ], + ) + def test_verify_success_with_chain(self, root_cert, chain, verified_cert): + store = X509Store() + store.add_cert(root_cert) + store_ctx = X509StoreContext(store, verified_cert, chain=chain) + assert store_ctx.verify_certificate() is None + + def test_valid_untrusted_chain_reuse(self): + """ + `verify_certificate` using an untrusted chain can be called multiple + times with the same ``X509StoreContext`` instance to produce the same + result. + """ + store = X509Store() + store.add_cert(self.root_cert) + chain = [self.intermediate_cert] + + store_ctx = X509StoreContext( + store, self.intermediate_server_cert, chain=chain + ) + assert store_ctx.verify_certificate() is None + assert store_ctx.verify_certificate() is None + + def test_chain_reference(self): + """ + ``X509StoreContext`` properly keeps references to the untrusted chain + certificates. + """ + store = X509Store() + store.add_cert(self.root_cert) + chain = [load_certificate(FILETYPE_PEM, intermediate_cert_pem)] + + store_ctx = X509StoreContext( + store, self.intermediate_server_cert, chain=chain + ) + + del chain + assert store_ctx.verify_certificate() is None + + @pytest.mark.parametrize( + "root_cert, chain, verified_cert", + [ + pytest.param( + root_cert, + [], + intermediate_server_cert, + id="intermediate missing", + ), + pytest.param( + None, + [intermediate_cert], + intermediate_server_cert, + id="no trusted root", + ), + pytest.param( + None, + [root_cert, intermediate_cert], + intermediate_server_cert, + id="untrusted root, full chain is available", + ), + pytest.param( + intermediate_cert, + [root_cert, intermediate_cert], + intermediate_server_cert, + id="untrusted root, intermediate is trusted and in chain", + ), + ], + ) + def test_verify_fail_with_chain(self, root_cert, chain, verified_cert): + store = X509Store() + if root_cert: + store.add_cert(root_cert) + + store_ctx = X509StoreContext(store, verified_cert, chain=chain) + + with pytest.raises(X509StoreContextError): + store_ctx.verify_certificate() + + @pytest.mark.parametrize( + "chain, expected_error", + [ + pytest.param( + [intermediate_cert, "This is not a certificate"], + TypeError, + id="non-certificate in chain", + ), + pytest.param( + 42, + TypeError, + id="non-list chain", + ), + ], + ) + def test_untrusted_chain_wrong_args(self, chain, expected_error): + """ + Creating ``X509StoreContext`` with wrong chain raises an exception. + """ + store = X509Store() + store.add_cert(self.root_cert) + + with pytest.raises(expected_error): + X509StoreContext(store, self.intermediate_server_cert, chain=chain) + + def test_failure_building_untrusted_chain_raises(self, monkeypatch): + """ + Creating ``X509StoreContext`` raises ``OpenSSL.crypto.Error`` when + the underlying lib fails to add the certificate to the stack. + """ + monkeypatch.setattr(_lib, "sk_X509_push", lambda _stack, _x509: -1) + + store = X509Store() + store.add_cert(self.root_cert) + chain = [self.intermediate_cert] + + with pytest.raises(Error): + X509StoreContext(store, self.intermediate_server_cert, chain=chain) + def test_trusted_self_signed(self): """ `verify_certificate` returns ``None`` when called with a self-signed |