Add code style settings, new excludes, run 'test_x509_ext (Py3)' (#3041)

Fix DNSName wildcard encoding for NameConstraints

Previously '.example.com' would get normalised to 'example.com', making
it impossible to add wildcard NameConstraints.
diff --git a/src/cryptography/hazmat/backends/openssl/encode_asn1.py b/src/cryptography/hazmat/backends/openssl/encode_asn1.py
index b0e2e73..944dedc 100644
--- a/src/cryptography/hazmat/backends/openssl/encode_asn1.py
+++ b/src/cryptography/hazmat/backends/openssl/encode_asn1.py
@@ -358,6 +358,15 @@
     return _encode_asn1_str_gc(backend, ski.digest, len(ski.digest))
 
 
+def _idna_encode(value):
+    # Retain prefixes '*.' for common/alt names and '.' for name constraints
+    for prefix in ['*.', '.']:
+        if value.startswith(prefix):
+            value = value[len(prefix):]
+            return prefix.encode('ascii') + idna.encode(value)
+    return idna.encode(value)
+
+
 def _encode_general_name(backend, name):
     if isinstance(name, x509.DNSName):
         gn = backend._lib.GENERAL_NAME_new()
@@ -366,11 +375,7 @@
 
         ia5 = backend._lib.ASN1_IA5STRING_new()
         backend.openssl_assert(ia5 != backend._ffi.NULL)
-
-        if name.value.startswith(u"*."):
-            value = b"*." + idna.encode(name.value[2:])
-        else:
-            value = idna.encode(name.value)
+        value = _idna_encode(name.value)
 
         res = backend._lib.ASN1_STRING_set(ia5, value, len(value))
         backend.openssl_assert(res == 1)
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 28ddab8..f3677d1 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -17,16 +17,33 @@
 from cryptography.hazmat.backends.interfaces import (
     DSABackend, EllipticCurveBackend, RSABackend, X509Backend
 )
+from cryptography.hazmat.primitives import hashes
 from cryptography.hazmat.primitives.asymmetric import ec
+from cryptography.x509 import DNSName, NameConstraints, SubjectAlternativeName
 from cryptography.x509.oid import (
     AuthorityInformationAccessOID, ExtendedKeyUsageOID, ExtensionOID,
     NameOID, ObjectIdentifier
 )
 
+from .hazmat.primitives.fixtures_rsa import RSA_KEY_2048
 from .hazmat.primitives.test_ec import _skip_curve_unsupported
 from .test_x509 import _load_cert
 
 
+def _make_certbuilder(private_key):
+    name = x509.Name(
+        [x509.NameAttribute(NameOID.COMMON_NAME, u'example.org')])
+    return (
+        x509.CertificateBuilder()
+            .subject_name(name)
+            .issuer_name(name)
+            .public_key(private_key.public_key())
+            .serial_number(777)
+            .not_valid_before(datetime.datetime(1999, 1, 1))
+            .not_valid_after(datetime.datetime(2020, 1, 1))
+    )
+
+
 class TestExtension(object):
     def test_not_an_oid(self):
         bc = x509.BasicConstraints(ca=False, path_length=None)
@@ -2141,6 +2158,19 @@
         othernames = ext.value.get_values_for_type(x509.OtherName)
         assert othernames == [expected]
 
+    def test_certbuilder(self, backend):
+        sans = [u'*.example.org', u'*.\xf5\xe4\xf6\xfc.example.com',
+                u'foobar.example.net']
+        private_key = RSA_KEY_2048.private_key(backend)
+        builder = _make_certbuilder(private_key)
+        builder = builder.add_extension(
+            SubjectAlternativeName(list(map(DNSName, sans))), True)
+
+        cert = builder.sign(private_key, hashes.SHA1(), backend)
+        result = [x.value for x in cert.extensions.get_extension_for_class(
+            SubjectAlternativeName).value]
+        assert result == sans
+
 
 @pytest.mark.requires_backend_interface(interface=RSABackend)
 @pytest.mark.requires_backend_interface(interface=X509Backend)
@@ -2889,6 +2919,20 @@
                 ExtensionOID.NAME_CONSTRAINTS
             )
 
+    def test_certbuilder(self, backend):
+        permitted = [u'.example.org', u'.\xf5\xe4\xf6\xfc.example.com',
+                     u'foobar.example.net']
+        private_key = RSA_KEY_2048.private_key(backend)
+        builder = _make_certbuilder(private_key)
+        builder = builder.add_extension(
+            NameConstraints(permitted_subtrees=list(map(DNSName, permitted)),
+                            excluded_subtrees=[]), True)
+
+        cert = builder.sign(private_key, hashes.SHA1(), backend)
+        result = [x.value for x in cert.extensions.get_extension_for_class(
+            NameConstraints).value.permitted_subtrees]
+        assert result == permitted
+
 
 class TestDistributionPoint(object):
     def test_distribution_point_full_name_not_general_names(self):