Merge pull request #2129 from alex/hash-csr

Fixed #2127 -- added __hash__ to CSR
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 82a3e5b..475a2a3 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -27,6 +27,11 @@
 * Add support for creating certificate signing requests with
   :class:`~cryptography.x509.CertificateSigningRequestBuilder`.
 
+0.9.3 - 2015-07-09
+~~~~~~~~~~~~~~~~~~
+
+* Updated Windows wheels to be compiled against OpenSSL 1.0.2d.
+
 0.9.2 - 2015-07-04
 ~~~~~~~~~~~~~~~~~~
 
diff --git a/docs/x509.rst b/docs/x509.rst
index a3cf7e2..bcb6ee6 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -699,6 +699,20 @@
 
         :type: :class:`ObjectIdentifier`
 
+.. class:: OtherName
+
+    .. versionadded:: 1.0
+
+    This corresponds to an ``otherName.``  An ``otherName`` has a type identifier and a value represented in binary DER format.
+
+    .. attribute:: type_id
+
+        :type: :class:`ObjectIdentifier`
+
+    .. attribute:: value
+
+        :type: `bytes`
+
 X.509 Extensions
 ~~~~~~~~~~~~~~~~
 
@@ -971,6 +985,7 @@
             :ref:`general name classes <general_name_classes>`.
 
         :returns: A list of values extracted from the matched general names.
+            The type of the returned values depends on the :class:`GeneralName`.
 
         .. doctest::
 
diff --git a/src/_cffi_src/openssl/asn1.py b/src/_cffi_src/openssl/asn1.py
index 5210c7c..01d6f4c 100644
--- a/src/_cffi_src/openssl/asn1.py
+++ b/src/_cffi_src/openssl/asn1.py
@@ -155,6 +155,8 @@
 
 /* Not a macro, const on openssl 1.0 */
 int ASN1_STRING_set_default_mask_asc(char *);
+
+int i2d_ASN1_TYPE(ASN1_TYPE *, unsigned char **);
 """
 
 CUSTOMIZATIONS = """
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index a401338..d78c60f 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -55,6 +55,17 @@
     return backend._ffi.buffer(buf[0], res)[:].decode('utf8')
 
 
+def _asn1_to_der(backend, asn1_type):
+    buf = backend._ffi.new("unsigned char **")
+    res = backend._lib.i2d_ASN1_TYPE(asn1_type, buf)
+    assert res >= 0
+    assert buf[0] != backend._ffi.NULL
+    buf = backend._ffi.gc(
+        buf, lambda buffer: backend._lib.OPENSSL_free(buffer[0])
+    )
+    return backend._ffi.buffer(buf[0], res)[:]
+
+
 def _decode_x509_name_entry(backend, x509_name_entry):
     obj = backend._lib.X509_NAME_ENTRY_get_object(x509_name_entry)
     assert obj != backend._ffi.NULL
@@ -158,8 +169,12 @@
             return x509.RFC822Name(
                 parts[0] + u"@" + idna.decode(parts[1])
             )
+    elif gn.type == backend._lib.GEN_OTHERNAME:
+        type_id = _obj2txt(backend, gn.d.otherName.type_id)
+        value = _asn1_to_der(backend, gn.d.otherName.value)
+        return x509.OtherName(x509.ObjectIdentifier(type_id), value)
     else:
-        # otherName, x400Address or ediPartyName
+        # x400Address or ediPartyName
         raise x509.UnsupportedGeneralNameType(
             "{0} is not a supported type".format(
                 x509._GENERAL_NAMES.get(gn.type, gn.type)
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 0ad1d4f..33c6416 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -1045,6 +1045,34 @@
         return not self == other
 
 
+@utils.register_interface(GeneralName)
+class OtherName(object):
+    def __init__(self, type_id, value):
+        if not isinstance(type_id, ObjectIdentifier):
+            raise TypeError("type_id must be an ObjectIdentifier")
+        if not isinstance(value, bytes):
+            raise TypeError("value must be a binary string")
+
+        self._type_id = type_id
+        self._value = value
+
+    type_id = utils.read_only_property("_type_id")
+    value = utils.read_only_property("_value")
+
+    def __repr__(self):
+        return "<OtherName(type_id={0}, value={1!r})>".format(
+            self.type_id, self.value)
+
+    def __eq__(self, other):
+        if not isinstance(other, OtherName):
+            return NotImplemented
+
+        return self.type_id == other.type_id and self.value == other.value
+
+    def __ne__(self, other):
+        return not self == other
+
+
 class GeneralNames(object):
     def __init__(self, general_names):
         if not all(isinstance(x, GeneralName) for x in general_names):
@@ -1062,7 +1090,13 @@
         return len(self._general_names)
 
     def get_values_for_type(self, type):
-        return [i.value for i in self if isinstance(i, type)]
+        # Return the value of each GeneralName, except for OtherName instances
+        # which we return directly because it has two important properties not
+        # just one value.
+        objs = (i for i in self if isinstance(i, type))
+        if type != OtherName:
+            objs = (i.value for i in objs)
+        return list(objs)
 
     def __repr__(self):
         return "<GeneralNames({0})>".format(self._general_names)
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index d15d666..993802b 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -1147,6 +1147,55 @@
         assert gn != object()
 
 
+class TestOtherName(object):
+    def test_invalid_args(self):
+        with pytest.raises(TypeError):
+            x509.OtherName(b"notanobjectidentifier", b"derdata")
+
+        with pytest.raises(TypeError):
+            x509.OtherName(x509.ObjectIdentifier("1.2.3.4"), u"notderdata")
+
+    def test_repr(self):
+        gn = x509.OtherName(x509.ObjectIdentifier("1.2.3.4"), b"derdata")
+        if six.PY3:
+            assert repr(gn) == (
+                "<OtherName(type_id=<ObjectIdentifier(oid=1.2.3.4, "
+                "name=Unknown OID)>, value=b'derdata')>"
+            )
+        else:
+            assert repr(gn) == (
+                "<OtherName(type_id=<ObjectIdentifier(oid=1.2.3.4, "
+                "name=Unknown OID)>, value='derdata')>"
+            )
+
+        gn = x509.OtherName(x509.ObjectIdentifier("2.5.4.65"), b"derdata")
+        if six.PY3:
+            assert repr(gn) == (
+                "<OtherName(type_id=<ObjectIdentifier(oid=2.5.4.65, "
+                "name=pseudonym)>, value=b'derdata')>"
+            )
+        else:
+            assert repr(gn) == (
+                "<OtherName(type_id=<ObjectIdentifier(oid=2.5.4.65, "
+                "name=pseudonym)>, value='derdata')>"
+            )
+
+    def test_eq(self):
+        gn = x509.OtherName(x509.ObjectIdentifier("1.2.3.4"), b"derdata")
+        gn2 = x509.OtherName(x509.ObjectIdentifier("1.2.3.4"), b"derdata")
+        assert gn == gn2
+
+    def test_ne(self):
+        gn = x509.OtherName(x509.ObjectIdentifier("1.2.3.4"), b"derdata")
+        assert gn != object()
+
+        gn2 = x509.OtherName(x509.ObjectIdentifier("1.2.3.4"), b"derdata2")
+        assert gn != gn2
+
+        gn2 = x509.OtherName(x509.ObjectIdentifier("1.2.3.5"), b"derdata")
+        assert gn != gn2
+
+
 class TestGeneralNames(object):
     def test_get_values_for_type(self):
         gns = x509.GeneralNames(
@@ -1578,6 +1627,29 @@
 
         assert 'Invalid rfc822name value' in str(exc.value)
 
+    def test_other_name(self, backend):
+        cert = _load_cert(
+            os.path.join(
+                "x509", "custom", "san_other_name.pem"
+            ),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+
+        ext = cert.extensions.get_extension_for_oid(
+            x509.OID_SUBJECT_ALTERNATIVE_NAME
+        )
+        assert ext is not None
+        assert ext.critical is False
+
+        expected = x509.OtherName(x509.ObjectIdentifier("1.2.3.4"),
+                                  b'\x16\x0bHello World')
+        assert len(ext.value) == 1
+        assert list(ext.value)[0] == expected
+
+        othernames = ext.value.get_values_for_type(x509.OtherName)
+        assert othernames == [expected]
+
 
 @pytest.mark.requires_backend_interface(interface=RSABackend)
 @pytest.mark.requires_backend_interface(interface=X509Backend)