Skip to content

Commit

Permalink
osrandom_engine
Browse files Browse the repository at this point in the history
* Fix naming bug caused by search 'n replace mistake
* Make it easier to override osrandom auto-detection
* Add engine ctrl and backend API to get implementation from ENGINE

Signed-off-by: Christian Heimes <[email protected]>
  • Loading branch information
tiran committed Nov 13, 2016
1 parent 6ed7f91 commit da3f779
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 62 deletions.
78 changes: 70 additions & 8 deletions src/_cffi_src/openssl/src/osrandom_engine.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ static int osrandom_rand_status(void) {
return 1;
}
}

static const char* osurandom_get_implementation(void) {
return "CryptGenRandom";
}

#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE_CRYPTGENRANDOM */

/****************************************************************************
Expand Down Expand Up @@ -89,12 +94,16 @@ static int osrandom_finish(ENGINE *e) {
static int osrandom_rand_status(void) {
return 1;
}
#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM */

static const char* osurandom_get_implementation(void) {
return "CCRandomGenerateBytes";
}
#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE_CC_RANDOM */

/****************************************************************************
* BSD getentropy
*/
#if CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM
#if CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_GETENTROPY
static const char *Cryptography_osrandom_engine_name = "osrandom_engine getentropy()";

static int osrandom_init(ENGINE *e) {
Expand All @@ -109,7 +118,7 @@ static int osrandom_rand_bytes(unsigned char *buffer, int size) {
len = size > 256 : 256: size;
res = getentropy(buffer, len);
if (res < 0) {
CRYPTOGRAPHY_OSRANDOM_put_error(
CRYPTOGRAPHY_OSRANDOM_put_error(as
"osrandom_engine.py:getentropy()");
return 0;
}
Expand All @@ -126,7 +135,11 @@ static int osrandom_finish(ENGINE *e) {
static int osrandom_rand_status(void) {
return 1;
}
#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM */

static const char* osurandom_get_implementation(void) {
return "getentropy";
}
#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE_GETENTROPY */

/****************************************************************************
* /dev/urandom helpers for all non-BSD Unix platforms
Expand Down Expand Up @@ -233,13 +246,13 @@ static void dev_urandom_close(void) {
}
}
}
#endif
#endif /* CRYPTOGRAPHY_OSRANDOM_NEEDS_DEV_URANDOM */

/****************************************************************************
* Linux getrandom engine with fallback to dev_urandom
*/

#if CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_GETENTROPY
#if CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM
static const char *Cryptography_osrandom_engine_name = "osrandom_engine getrandom()";

static int getrandom_works = -1;
Expand Down Expand Up @@ -305,7 +318,14 @@ static int osrandom_rand_status(void) {
}
return 1;
}
#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE_GETENTROPY */

static const char* osurandom_get_implementation(void) {
if (getrandom_works == 1) {
return "getrandom";
}
return "/dev/urandom";
}
#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM */

/****************************************************************************
* dev_urandom engine for all remaining platforms
Expand Down Expand Up @@ -338,8 +358,15 @@ static int osrandom_rand_status(void) {
return 1;
}

static const char* osurandom_get_implementation(void) {
return "/dev/urandom";
}
#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE_DEV_URANDOM */

/****************************************************************************
* ENGINE boiler plate
*/

/* This replicates the behavior of the OpenSSL FIPS RNG, which returns a
-1 in the event that there is an error when calling RAND_pseudo_bytes. */
static int osrandom_pseudo_rand_bytes(unsigned char *buffer, int size) {
Expand All @@ -360,6 +387,39 @@ static RAND_METHOD osrandom_rand = {
osrandom_rand_status,
};

static const ENGINE_CMD_DEFN osrandom_cmd_defns[] = {
{CRYPTOGRAPHY_OSRANDOM_GET_IMPLEMENTATION,
"get_implementation",
"Get CPRNG implementation.",
ENGINE_CMD_FLAG_NO_INPUT},
{0, NULL, NULL, 0}
};

static int osrandom_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void)) {
const char* name;
size_t len;

switch (cmd) {
case CRYPTOGRAPHY_OSRANDOM_GET_IMPLEMENTATION:
name = osurandom_get_implementation();
len = strlen(name);
if ((p == NULL) && (i == 0)) {
/* return required buffer len */
return len;
}
if ((p == NULL) || ((size_t)i <= len)) {
/* no buffer or buffer too small */
ENGINEerr(ENGINE_F_ENGINE_CTRL, ENGINE_R_INVALID_ARGUMENT);
return 0;
}
strncpy((char *)p, name, len);
return len;
default:
ENGINEerr(ENGINE_F_ENGINE_CTRL, ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED);
return 0;
}
}

/* Returns 1 if successfully added, 2 if engine has previously been added,
and 0 for error. */
int Cryptography_add_osrandom_engine(void) {
Expand All @@ -380,7 +440,9 @@ int Cryptography_add_osrandom_engine(void) {
!ENGINE_set_name(e, Cryptography_osrandom_engine_name) ||
!ENGINE_set_RAND(e, &osrandom_rand) ||
!ENGINE_set_init_function(e, osrandom_init) ||
!ENGINE_set_finish_function(e, osrandom_finish)) {
!ENGINE_set_finish_function(e, osrandom_finish) ||
!ENGINE_set_cmd_defns(e, osrandom_cmd_defns) ||
!ENGINE_set_ctrl_function(e, osrandom_ctrl)) {
ENGINE_free(e);
return 0;
}
Expand Down
51 changes: 25 additions & 26 deletions src/_cffi_src/openssl/src/osrandom_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,34 +38,33 @@
#define CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM 4
#define CRYPTOGRAPHY_OSRANDOM_ENGINE_DEV_URANDOM 5

/* getentropy is not available in FreeBSD-10.1-RELEASE-p5 and older
* TODO: check NetBSD and Darwin */
#if defined(_WIN32)
/* Windows */
#define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_CRYPTGENRANDOM
#elif defined(__APPLE__) && defined(CRYPTOGRAPHY_HAVE_COMMON_RANDOM_H)
/* OSX 10.10+ */
#define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_CC_RANDOM
#elif defined(BSD) && defined(SYS_getentropy)
/* OpenBSD 5.6+ */
#define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM
#elif defined(__linux__) && defined(SYS_getrandom)
/* Linux 3.4.17+ */
#define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_GETENTROPY
#define CRYPTOGRAPHY_OSRANDOM_NEEDS_DEV_URANDOM 1
#endif

/* Fall back to /dev/urandom */
#ifndef CRYPTOGRAPHY_OSRANDOM_ENGINE
#define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_DEV_URANDOM
#if defined(_WIN32)
/* Windows */
#define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_CRYPTGENRANDOM
#elif defined(__APPLE__) && defined(CRYPTOGRAPHY_HAVE_COMMON_RANDOM_H)
/* OSX 10.10+ */
#define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_CC_RANDOM
#elif defined(BSD) && defined(SYS_getentropy)
/* OpenBSD 5.6+ */
#define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_GETENTROPY
#elif defined(__linux__) && defined(SYS_getrandom)
/* Linux 3.4.17+ */
#define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM
#else
/* Keep this as last entry, fall back to /dev/urandom */
#define CRYPTOGRAPHY_OSRANDOM_ENGINE CRYPTOGRAPHY_OSRANDOM_ENGINE_DEV_URANDOM
#endif
#endif /* CRYPTOGRAPHY_OSRANDOM_ENGINE */

/* Fallbacks need /dev/urandom helper functions. */
#if CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_GETRANDOM || \
CRYPTOGRAPHY_OSRANDOM_ENGINE == CRYPTOGRAPHY_OSRANDOM_ENGINE_DEV_URANDOM
#define CRYPTOGRAPHY_OSRANDOM_NEEDS_DEV_URANDOM 1
#endif

/* Open SSL 1.1.0+ has no ERR_R_RAND_LIB */
#ifdef ERR_R_RAND_LIB
#define CRYPTOGRAPHY_OSRANDOM_put_error(funcname) \
ERR_put_error(ERR_LIB_RAND, 0, ERR_R_RAND_LIB, funcname, 0)
#else
#define CRYPTOGRAPHY_OSRANDOM_put_error(funcname) \
#define CRYPTOGRAPHY_OSRANDOM_put_error(funcname) \
ERR_put_error(ERR_LIB_RAND, 0, 0, funcname, 0)
#endif

/* engine ctrl */
#define CRYPTOGRAPHY_OSRANDOM_GET_IMPLEMENTATION ENGINE_CMD_BASE
42 changes: 30 additions & 12 deletions src/cryptography/hazmat/backends/openssl/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import base64
import calendar
import collections
import contextlib
import itertools
import sys
from contextlib import contextmanager
Expand Down Expand Up @@ -153,28 +154,45 @@ def activate_builtin_random(self):
res = self._lib.ENGINE_finish(e)
self.openssl_assert(res == 1)

def activate_osrandom_engine(self):
# Unregister and free the current engine.
self.activate_builtin_random()
@contextlib.contextmanager
def _get_osurandom_engine(self):
# Fetches an engine by id and returns it. This creates a structural
# reference.
e = self._lib.ENGINE_by_id(self._binding._osrandom_engine_id)
self.openssl_assert(e != self._ffi.NULL)
# Initialize the engine for use. This adds a functional reference.
res = self._lib.ENGINE_init(e)
self.openssl_assert(res == 1)
# Set the engine as the default RAND provider.
res = self._lib.ENGINE_set_default_RAND(e)
self.openssl_assert(res == 1)
# Decrement the structural ref incremented by ENGINE_by_id.
res = self._lib.ENGINE_free(e)
self.openssl_assert(res == 1)
# Decrement the functional ref incremented by ENGINE_init.
res = self._lib.ENGINE_finish(e)
self.openssl_assert(res == 1)

try:
yield e
finally:
# Decrement the structural ref incremented by ENGINE_by_id.
res = self._lib.ENGINE_free(e)
self.openssl_assert(res == 1)
# Decrement the functional ref incremented by ENGINE_init.
res = self._lib.ENGINE_finish(e)
self.openssl_assert(res == 1)

def activate_osrandom_engine(self):
# Unregister and free the current engine.
self.activate_builtin_random()
with self._get_osurandom_engine() as e:
# Set the engine as the default RAND provider.
res = self._lib.ENGINE_set_default_RAND(e)
self.openssl_assert(res == 1)
# Reset the RNG to use the new engine.
self._lib.RAND_cleanup()

def osrandom_engine_implementation(self):
buf = self._ffi.new("char[]", 64)
with self._get_osurandom_engine() as e:
res = self._lib.ENGINE_ctrl_cmd(e, b"get_implementation",
len(buf), buf,
self._ffi.NULL, 0)
self.openssl_assert(res > 0)
return self._ffi.string(buf).decode('ascii')

def openssl_version_text(self):
"""
Friendly string name of the loaded OpenSSL library. This is not
Expand Down
16 changes: 0 additions & 16 deletions src/cryptography/hazmat/bindings/openssl/binding.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from __future__ import absolute_import, division, print_function

import collections
import os
import threading
import types
import warnings
Expand Down Expand Up @@ -82,21 +81,6 @@ def wrapper(func):
return wrapper


@ffi_callback("int (*)(unsigned char *, int)",
name="Cryptography_rand_bytes",
error=-1)
def _osrandom_rand_bytes(buf, size):
signed = ffi.cast("char *", buf)
result = os.urandom(size)
signed[0:size] = result
return 1


@ffi_callback("int (*)(void)", name="Cryptography_rand_status")
def _osrandom_rand_status():
return 1


def build_conditional_library(lib, conditional_names):
conditional_lib = types.ModuleType("lib")
excluded_names = set()
Expand Down
8 changes: 8 additions & 0 deletions tests/hazmat/backends/test_openssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,14 @@ def test_activate_builtin_random_already_active(self):
e = backend._lib.ENGINE_get_default_RAND()
assert e == backend._ffi.NULL

def test_osrandom_engine_implementation(self):
name = backend.osrandom_engine_implementation()
assert name
if sys.platform.startswith('linux'):
assert name in {'getrandom', '/dev/urandom'}
elif sys.platform == 'win32':
assert name == 'CryptGenRandom'

def test_activate_osrandom_already_default(self):
e = backend._lib.ENGINE_get_default_RAND()
name = backend._lib.ENGINE_get_name(e)
Expand Down

0 comments on commit da3f779

Please sign in to comment.