Commit e8eeb3ce by Vincent Pelletier

test: Skip testHttpNetlocIPv6 if OpenSSL lacks IP constraints support

1 parent 40a89260
......@@ -20,7 +20,7 @@ Caucase - Certificate Authority for Users, Certificate Authority for SErvices
Test suite
"""
# pylint: disable=too-many-lines
# pylint: disable=too-many-lines, too-many-public-methods
from __future__ import absolute_import
from Cookie import SimpleCookie
import datetime
......@@ -47,8 +47,11 @@ from urllib import quote, urlencode
import urlparse
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from caucase import cli
from caucase.ca import Extension
from caucase.client import CaucaseError, CaucaseClient
from caucase.exceptions import CertificateVerificationError
# Do not import caucase.http into this namespace: 2to3 will import standard
# http module, which will then be masqued by caucase's http submodule.
import caucase.http
......@@ -325,6 +328,160 @@ class CaucaseTest(unittest.TestCase):
self._stopServer()
shutil.rmtree(self._data_dir)
@staticmethod
def _getCAKeyPair(extension_list=(), not_before=None, not_after=None):
"""
Build a reasonably-realistic CA, return key & self-signed cert.
"""
if not_before is None:
not_before = datetime.datetime.utcnow()
if not_after is None:
not_after = not_before + datetime.timedelta(10, 0)
private_key = utils.generatePrivateKey(2048)
subject = x509.Name([
x509.NameAttribute(
oid=x509.oid.NameOID.COMMON_NAME,
value=u'John Doe CA',
),
])
public_key = private_key.public_key()
subject_key_identifier = x509.SubjectKeyIdentifier.from_public_key(
public_key,
)
return private_key, x509.CertificateBuilder(
subject_name=subject,
issuer_name=subject,
not_valid_before=not_before,
not_valid_after=not_after,
serial_number=x509.random_serial_number(),
public_key=public_key,
extensions=[
Extension(
subject_key_identifier,
critical=False, # "MUST mark this extension as non-critical"
),
Extension(
x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
# Dummy extension, from_issuer_subject_key_identifier accesses
# .data directly
Extension(
subject_key_identifier,
critical=False,
),
),
critical=False, # "MUST mark this extension as non-critical"
),
Extension(
x509.BasicConstraints(
ca=True,
path_length=0,
),
critical=True, # "MUST mark the extension as critical"
),
Extension(
x509.KeyUsage(
# pylint: disable=bad-whitespace
digital_signature =False,
content_commitment=False,
key_encipherment =False,
data_encipherment =False,
key_agreement =False,
key_cert_sign =True,
crl_sign =True,
encipher_only =False,
decipher_only =False,
# pylint: enable=bad-whitespace
),
critical=True, # "SHOULD mark this extension critical"
),
] + list(extension_list),
).sign(
private_key=private_key,
algorithm=hashes.SHA256(),
backend=_cryptography_backend,
)
@staticmethod
def _getKeyPair(
ca_key,
ca_crt,
extension_list=(),
not_before=None,
not_after=None,
):
"""
Build a reasonably-realistic signed cert, return key & self-signed cert.
"""
if not_before is None:
not_before = datetime.datetime.utcnow()
if not_after is None:
not_after = not_before + datetime.timedelta(10, 0)
crt_key = utils.generatePrivateKey(2048)
return crt_key, x509.CertificateBuilder(
subject_name=x509.Name([
x509.NameAttribute(
oid=x509.oid.NameOID.ORGANIZATIONAL_UNIT_NAME,
value=u'Jane Doe',
),
]),
issuer_name=ca_crt.subject,
not_valid_before=not_before,
not_valid_after=not_after,
serial_number=x509.random_serial_number(),
public_key=crt_key.public_key(),
extensions=[
Extension(
x509.KeyUsage(
# pylint: disable=bad-whitespace
digital_signature =True,
content_commitment=False,
key_encipherment =True,
data_encipherment =False,
key_agreement =False,
key_cert_sign =False,
crl_sign =False,
encipher_only =False,
decipher_only =False,
# pylint: enable=bad-whitespace
),
critical=True,
),
] + list(extension_list),
).sign(
private_key=ca_key,
algorithm=hashes.SHA256(),
backend=_cryptography_backend,
)
def _skipIfOpenSSLDoesNotSupportIPContraints(self):
ca_key, ca_crt = self._getCAKeyPair(
extension_list=[
Extension(
x509.NameConstraints(
permitted_subtrees=[
x509.IPAddress(ipaddress.ip_network(u'127.0.0.1')),
],
excluded_subtrees=None,
),
critical=True,
),
],
)
_, crt = self._getKeyPair(
ca_key=ca_key,
ca_crt=ca_crt,
)
try:
# pylint: disable=protected-access
utils._verifyCertificateChain(
cert=crt,
trusted_cert_list=[ca_crt],
crl=None,
)
# pylint: enable=protected-access
except CertificateVerificationError:
raise unittest.SkipTest('OpenSSL versoin does not support IP constraints')
def _restoreServer(
self,
backup_path,
......@@ -2567,16 +2724,15 @@ class CaucaseTest(unittest.TestCase):
after = ssl.get_server_certificate(address)
self.assertNotEqual(before, after)
def testHttpNetlocIPv6(self):
def _testHttpCustomNetLoc(self, netloc):
"""
Test that it is possible to use a literal IPv6 as netloc.
This used to fail because cryptography module would reject bare IPv6
address in CRL distribution point extension.
Breaks on OpenSSL < 1.1.0 as it lacks support for validating
certificates with IP constraints.
"""
self._skipIfOpenSSLDoesNotSupportIPContraints()
self._stopServer()
os.unlink(self._server_key)
os.unlink(self._server_db)
netloc = '[::1]'
port = urlparse.urlparse(self._caucase_url).port
if port:
netloc += ':%s' % port
......@@ -2601,6 +2757,20 @@ class CaucaseTest(unittest.TestCase):
uri, = distribution_point.full_name
self.assertEqual(uri.value, self._caucase_url + u'/cas/crl')
def testHttpNetlocIPv4(self):
"""
Test that it is possible to use a literal IPv4 as netloc.
"""
self._testHttpCustomNetLoc(netloc='127.0.0.1')
def testHttpNetlocIPv6(self):
"""
Test that it is possible to use a literal IPv6 as netloc.
This used to fail because cryptography module would reject bare IPv6
address in CRL distribution point extension (unlike IPv4).
"""
self._testHttpCustomNetLoc(netloc='[::1]')
def testServerFilePermissions(self):
"""
Check that both the sqlite database and server keys are group- and
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!