Skip to content

Commit

Permalink
Merge de6f553 into adbea0d
Browse files Browse the repository at this point in the history
  • Loading branch information
public committed Jan 3, 2014
2 parents adbea0d + de6f553 commit 68b993b
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 0 deletions.
10 changes: 10 additions & 0 deletions cryptography/hazmat/backends/openssl/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ def __init__(self):
self._ffi = self._binding.ffi
self._lib = self._binding.lib

try:
__import__("_ssl")
raise ImportError
except ImportError:
pass

if self._lib.CRYPTO_get_locking_callback() == self._ffi.NULL:
res = self._lib.Cryptography_setup_locking()
assert res == 0

self._lib.OpenSSL_add_all_algorithms()
self._lib.SSL_load_error_strings()

Expand Down
1 change: 1 addition & 0 deletions cryptography/hazmat/bindings/openssl/binding.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class Binding(object):
"rand",
"rsa",
"ssl",
"threads",
"x509",
"x509name",
"x509v3",
Expand Down
231 changes: 231 additions & 0 deletions cryptography/hazmat/bindings/openssl/threads.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

INCLUDES = """
#include <openssl/crypto.h>
"""

TYPES = """
"""

FUNCTIONS = """
static int Cryptography_setup_locking();
static void (*Cryptography_locking_function_ptr)(int, int, const char *, int);
"""

MACROS = """
"""

CUSTOMIZATIONS = """
typedef enum CryptographyLockStatus {
CRYPTOGRAPHY_LOCK_FAILURE = 0,
CRYPTOGRAPHY_LOCK_ACQUIRED = 1,
CRYPTOGRAPHY_LOCK_INTR = 2
} CryptographyLockStatus;
#if defined(_WIN32)
#include <windows.h>
typedef struct CryptographyOpaque_ThreadLock NRMUTEX, *PNRMUTEX;
BOOL InitializeNonRecursiveMutex(PNRMUTEX mutex)
{
mutex->sem = CreateSemaphore(NULL, 1, 1, NULL);
return !!mutex->sem;
}
VOID DeleteNonRecursiveMutex(PNRMUTEX mutex)
{
/* No in-use check */
CloseHandle(mutex->sem);
mutex->sem = NULL ; /* Just in case */
}
DWORD EnterNonRecursiveMutex(PNRMUTEX mutex, DWORD milliseconds)
{
return WaitForSingleObject(mutex->sem, milliseconds);
}
BOOL LeaveNonRecursiveMutex(PNRMUTEX mutex)
{
return ReleaseSemaphore(mutex->sem, 1, NULL);
}
int CryptographyThreadLockInit (struct CryptographyOpaque_ThreadLock *lock)
{
return InitializeNonRecursiveMutex(lock);
}
void CryptographyOpaqueDealloc_ThreadLock
(struct CryptographyOpaque_ThreadLock *lock)
{
if (lock->sem != NULL)
DeleteNonRecursiveMutex(lock);
}
/*
* Return 1 on success if the lock was acquired
*
* and 0 if the lock was not acquired. This means a 0 is returned
* if the lock has already been acquired by this thread!
*/
CryptographyLockStatus CryptographyThreadAcquireLock
(struct CryptographyOpaque_ThreadLock *lock, int intr_flag)
{
/* Fow now, intr_flag does nothing on Windows, and lock acquires are
* uninterruptible. */
CryptographyLockStatus success;
if ((lock &&
EnterNonRecursiveMutex(lock, (DWORD)INFINITE) == WAIT_OBJECT_0)
) {
success = CRYPTOGRAPHY_LOCK_ACQUIRED;
}
else {
success = CRYPTOGRAPHY_LOCK_FAILURE;
}
return success;
}
void CryptographyThreadReleaseLock(struct CryptographyOpaque_ThreadLock *lock)
{
if (!LeaveNonRecursiveMutex(lock))
/* XXX complain? */;
}
#else
#include <unistd.h>
#include <pthread.h>
#define CHECK_STATUS(name) if (status != 0) { perror(name); error = 1; }
#if !defined(pthread_mutexattr_default)
# define pthread_mutexattr_default ((pthread_mutexattr_t *)NULL)
#endif
struct CryptographyOpaque_ThreadLock {
char locked; /* 0=unlocked, 1=locked */
char initialized;
pthread_mutex_t mut;
};
typedef struct CryptographyOpaque_ThreadLock CryptographyOpaque_ThreadLock;
int CryptographyThreadLockInit
(struct CryptographyOpaque_ThreadLock *lock)
{
int status, error = 0;
lock->initialized = 0;
lock->locked = 0;
status = pthread_mutex_init(&lock->mut,
pthread_mutexattr_default);
CHECK_STATUS("pthread_mutex_init");
if (error)
return 0;
lock->initialized = 1;
return 1;
}
void CryptographyOpaqueDealloc_ThreadLock
(struct CryptographyOpaque_ThreadLock *lock)
{
int status, error = 0;
if (lock->initialized) {
status = pthread_mutex_destroy(&lock->mut);
CHECK_STATUS("pthread_mutex_destroy");
/* 'error' is ignored;
CHECK_STATUS already printed an error message */
}
}
CryptographyLockStatus CryptographyThreadAcquireLock
(struct CryptographyOpaque_ThreadLock *lock, int intr_flag)
{
CryptographyLockStatus success;
int status, error = 0;
status = pthread_mutex_lock(&lock->mut);
CHECK_STATUS("pthread_mutex_lock[1]");
if (error) success = CRYPTOGRAPHY_LOCK_FAILURE;
else success = CRYPTOGRAPHY_LOCK_ACQUIRED;
return success;
}
void CryptographyThreadReleaseLock
(struct CryptographyOpaque_ThreadLock *lock)
{
int status, error = 0;
status = pthread_mutex_unlock( &lock->mut );
CHECK_STATUS("pthread_mutex_unlock[3]");
}
#endif
static int Cryptography_lock_count = -1;
static CryptographyOpaque_ThreadLock *Cryptography_locks = NULL;
static void Cryptography_locking_function
(int mode, int n, const char *file, int line)
{
if ((Cryptography_locks == NULL ||
n < 0 ||
n >= Cryptography_lock_count)
) {
return;
}
if (mode & CRYPTO_LOCK) {
CryptographyThreadAcquireLock(&Cryptography_locks[n], 1);
} else {
CryptographyThreadReleaseLock(&Cryptography_locks[n]);
}
}
static void (*Cryptography_locking_function_ptr)
(int, int, const char *, int) = Cryptography_locking_function;
static int Cryptography_setup_locking() {
unsigned int i;
Cryptography_lock_count = CRYPTO_num_locks();
Cryptography_locks = calloc(Cryptography_lock_count,
sizeof(CryptographyOpaque_ThreadLock));
if (Cryptography_locks == NULL) {
return -1;
}
for (i = 0; i < Cryptography_lock_count; ++i) {
if (CryptographyThreadLockInit(&Cryptography_locks[i]) != 1) {
return -1;
}
}
CRYPTO_set_locking_callback(Cryptography_locking_function);
return 0;
}
"""

CONDITIONAL_NAMES = {}
49 changes: 49 additions & 0 deletions tests/hazmat/backends/test_openssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import threading

import pytest

from cryptography import utils
Expand Down Expand Up @@ -92,3 +94,50 @@ def test_handle_unknown_error(self):
backend._lib.EVP_F_EVP_DECRYPTFINAL_EX,
0
)

def test_locking_callback_set(self):
b = Backend()

locking_cb = b._lib.CRYPTO_get_locking_callback()
assert locking_cb != b._ffi.NULL

# emulate import _ssl not setting this for some reason
# because _ssl will have already been inited by the first Backend
# instance this will cause the next one to end up setting ours
b._lib.CRYPTO_set_locking_callback(b._ffi.NULL)

# force cffi to reinit
b._binding.ffi = None
b._binding.lib = None

# now it should get set to our one
b = Backend()
locking_cb = b._lib.CRYPTO_get_locking_callback()

assert locking_cb != b._ffi.NULL
assert locking_cb == b._lib.Cryptography_locking_function_ptr

def test_threads(self):
b = Backend()

def randloop():
s = b._ffi.new("char[]", 16)
sb = b._ffi.buffer(s)
sb[:] = b"\0" * 16

for i in range(300000):
b._lib.RAND_seed(s, 16)

threads = []
for x in range(3):
t = threading.Thread(target=randloop)
t.daemon = True
t.start()

threads.append(t)

while threads:
for t in threads:
t.join(0.1)
if not t.isAlive():
threads.remove(t)

0 comments on commit 68b993b

Please sign in to comment.