Repository: pyca/cryptography

Issue:
TypeError: initializer for ctype 'HMAC_CTX *
I sometimes get the below exception when using Fernet within an Apache mod_wsgi runtime.  It does not occur when using a non-Apache environment (Eventlet).

Is this cryptography module thread safe?  Or maybe is there an issue in how I used the module?

TypeError: initializer for ctype 'HMAC_CTX *' must be a pointer to same type, not cdata 'HMAC_CTX *'
Cryptography version: 0.7.2

Sample code:

```
def __init__(self):
    key = self._load_key()
    self.fernet = Fernet(key)

def encode(self, input, errors='strict'):
    return (self.fernet.encrypt(input), len(input))

def decode(self, input, errors='strict'):
    return (self.fernet.decrypt(input), len(input))

def _load_key(self):
    # Load the key from a file
    username = pwd.getpwuid(os.getuid()).pw_name
    filename = '/etc/fernet/%s.key' % username
    try:
        with open(filename) as f:
            key = f.read()
            return key
    except IOError:
        raise UnicodeEncodeError()
```

```
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi   File "/usr/lib/python2.7/encodings/fernet.py", line 19, in decode
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi     return (self.fernet.decrypt(input), len(input))
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi   File "/usr/local/lib/python2.7/dist-packages/cryptography/fernet.py", line 96, in decrypt
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi     h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend)
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi   File "/usr/local/lib/python2.7/dist-packages/cryptography/hazmat/primitives/hmac.py", line 32, in __init__
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi     self._ctx = self._backend.create_hmac_ctx(key, self.algorithm)
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi   File "/usr/local/lib/python2.7/dist-packages/cryptography/hazmat/backends/multibackend.py", line 99, in create_hmac_ctx
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi     return b.create_hmac_ctx(key, algorithm)
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi   File "/usr/local/lib/python2.7/dist-packages/cryptography/hazmat/backends/openssl/backend.py", line 140, in create_hmac_ctx
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi     return _HMACContext(self, key, algorithm)
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi   File "/usr/local/lib/python2.7/dist-packages/cryptography/hazmat/backends/openssl/hmac.py", line 24, in __init__
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi     self._backend._lib.HMAC_CTX_init(ctx)
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi TypeError: initializer for ctype 'HMAC_CTX *' must be a pointer to same type, not cdata 'HMAC_CTX *'
2015-03-18 22:55:08.512 6509 TRACE keystone.common.wsgi 
```



Discussion:
`TypeError: initializer for ctype 'HMAC_CTX *' must be a pointer to same type, not cdata 'HMAC_CTX *'`

This definitely looks like the type of error you'd see if the underlying c module were duplicately loaded. I thought we were thread safe in this respect though.

Or maybe not, I see a threading bug.


Resolving patch:
diff --git a/src/cryptography/hazmat/bindings/commoncrypto/binding.py b/src/cryptography/hazmat/bindings/commoncrypto/binding.py
--- a/src/cryptography/hazmat/bindings/commoncrypto/binding.py
+++ b/src/cryptography/hazmat/bindings/commoncrypto/binding.py
@@ -4,6 +4,8 @@
 
 from __future__ import absolute_import, division, print_function
 
+import threading
+
 from cryptography.hazmat.bindings.utils import (
     build_ffi_for_binding, load_library_for_binding,
 )
@@ -35,6 +37,7 @@ class Binding(object):
         ],
     )
     lib = None
+    _init_lock = threading.Lock()
 
     def __init__(self):
         self._ensure_ffi_initialized()
@@ -44,8 +47,10 @@ def _ensure_ffi_initialized(cls):
         if cls.lib is not None:
             return
 
-        cls.lib = load_library_for_binding(
-            cls.ffi,
-            module_prefix=cls._module_prefix,
-            modules=cls._modules,
-        )
+        with cls._init_lock:
+            if cls.lib is None:
+                cls.lib = load_library_for_binding(
+                    cls.ffi,
+                    module_prefix=cls._module_prefix,
+                    modules=cls._modules,
+                )
diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py
--- a/src/cryptography/hazmat/bindings/openssl/binding.py
+++ b/src/cryptography/hazmat/bindings/openssl/binding.py
@@ -98,6 +98,7 @@ class Binding(object):
 
     _locks = None
     _lock_cb_handle = None
+    _init_lock = threading.Lock()
     _lock_init_lock = threading.Lock()
 
     ffi = build_ffi_for_binding(
@@ -117,14 +118,16 @@ def _ensure_ffi_initialized(cls):
         if cls.lib is not None:
             return
 
-        cls.lib = load_library_for_binding(
-            cls.ffi,
-            cls._module_prefix,
-            cls._modules,
-        )
+        with cls._init_lock:
+            if cls.lib is None:
+                cls.lib = load_library_for_binding(
+                    cls.ffi,
+                    cls._module_prefix,
+                    cls._modules,
+                )
 
-        res = cls.lib.Cryptography_add_osrandom_engine()
-        assert res != 0
+                res = cls.lib.Cryptography_add_osrandom_engine()
+                assert res != 0
 
     @classmethod
     def init_static_locks(cls):


Test patch: