Skip to content

Commit

Permalink
New osrandom_engine in C (WIP)
Browse files Browse the repository at this point in the history
Inspired by Python/random.c and the old implementation.

Signed-off-by: Christian Heimes <[email protected]>
  • Loading branch information
tiran committed Nov 10, 2016
1 parent fb0e719 commit 26836ff
Show file tree
Hide file tree
Showing 6 changed files with 420 additions and 43 deletions.
1 change: 1 addition & 0 deletions src/_cffi_src/build_openssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def _osx_libraries(build_static):
"objects",
"ocsp",
"opensslv",
"osrandom_engine",
"pem",
"pkcs12",
"rand",
Expand Down
29 changes: 29 additions & 0 deletions src/_cffi_src/openssl/osrandom_engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import absolute_import, division, print_function

import os

HERE = os.path.dirname(os.path.abspath(__file__))

with open(os.path.join(HERE, "src/osrandom_engine.h")) as f:
INCLUDES = f.read()

TYPES = """
static const char *const Cryptography_osrandom_engine_name;
static const char *const Cryptography_osrandom_engine_id;
"""

FUNCTIONS = """
int Cryptography_add_osrandom_engine(void);
"""

MACROS = """
"""

with open(os.path.join(HERE, "src/osrandom_engine.c")) as f:
CUSTOMIZATIONS = f.read()

CONDITIONAL_NAMES = {}
350 changes: 350 additions & 0 deletions src/_cffi_src/openssl/src/osrandom_engine.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,350 @@
/* Largely inspired by Python/random.c and the old implementation of osrandom_engine.c */

static const char *Cryptography_osrandom_engine_id = "osrandom";
static const char *Cryptography_osrandom_engine_name = CRYPTOGRAPHY_OSRANDOM_ENGINE_NAME;

/****************************************************************************
* Windows
*/
#if defined(_WIN32)
#define RANDOM_ENGINE 1

static HCRYPTPROV hCryptProv = 0;

static int osrandom_init(ENGINE *e) {
if (hCryptProv != 0) {
return 1;
}
if (CryptAcquireContext(&hCryptProv, NULL, NULL,
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
return 1;
} else {
return 0;
}
}

static int osrandom_rand_bytes(unsigned char *buffer, int size) {
if (hCryptProv == 0) {
return 0;
}

if (!CryptGenRandom(hCryptProv, (DWORD)size, buffer)) {
ERR_put_error(
ERR_LIB_RAND, 0, ERR_R_RAND_LIB,
"osrandom_engine.py:CryptGenRandom()", 0
);
return 0;
}
return 1;
}

static int osrandom_finish(ENGINE *e) {
if (CryptReleaseContext(hCryptProv, 0)) {
hCryptProv = 0;
return 1;
} else {
return 0;
}
}

static int osrandom_rand_status(void) {
if (hCryptProv == 0) {
return 0;
} else {
return 1;
}
}
#elif defined(CRYPTOGRAPHY_HAVE_GETENTROPY)

/****************************************************************************
* BSD getentropy
*/
static int osrandom_init(ENGINE *e) {
return 1;
}

static int osrandom_rand_bytes(unsigned char *buffer, int size) {
Py_ssize_t len;
int res;
while (size > 0) {
len = size > 256 : 256: size;
res = getentropy(buffer, len);
if (res < 0) {
ERR_put_error(
ERR_LIB_RAND, 0, ERR_R_RAND_LIB,
"osrandom_engine.py:getentropy()", 0
);
return 0;
}
buffer += len;
size -= len;
}
return 1;
}

static int osrandom_finish(ENGINE *e) {
return 1;
}

static int osrandom_rand_status(void) {
return 1;
}

#else /* not _WIN32 and not BSD CRYPTOGRAPHY_HAVE_GETENTROPY */

/****************************************************************************
* /dev/urandom helpers for all non-BSD Unix platforms
*/

static struct {
int fd;
dev_t st_dev;
ino_t st_ino;
} urandom_cache = { -1 };

/* return -1 on error */
static int dev_urandom_fd(void) {
int fd, n, flags;
struct stat st;

/* Check that fd still points to the correct device */
if (urandom_cache.fd >= 0) {
if (fstat(urandom_cache.fd, &st)
|| st.st_dev != urandom_cache.st_dev
|| st.st_ino != urandom_cache.st_ino) {
urandom_cache.fd = -1;
}
}
if (urandom_cache.fd < 0) {
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
goto error;
}
if (fstat(fd, &st)) {
goto error;
}
/* Another thread initialized the fd */
if (urandom_cache.fd >= 0) {
do {
n = close(fd);
} while (n < 0 && errno == EINTR);
return urandom_cache.fd;
}
flags = fcntl(fd, F_GETFD);
if (flags == -1) {
goto error;
} else if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
goto error;
}
urandom_cache.st_dev = st.st_dev;
urandom_cache.st_ino = st.st_ino;
urandom_cache.fd = fd;
}
return urandom_cache.fd;

error:
if (fd != -1) {
do {
n = close(fd);
} while (n < 0 && errno == EINTR);
}
ERR_put_error(
ERR_LIB_RAND, 0, ERR_R_RAND_LIB,
"osrandom_engine.py:dev_urandom_fd()", 0);
return -1;
}

static int dev_urandom_read(unsigned char *buffer, int size) {
int fd, n;

fd = dev_urandom_fd();
if (fd < 0) {
return 0;
}

while (size > 0) {
do {
n = read(fd, buffer, (size_t)size);
} while (n < 0 && errno == EINTR);

if (n <= 0) {
ERR_put_error(
ERR_LIB_RAND, 0, ERR_R_RAND_LIB,
"osrandom_engine.py:dev_urandom_read()", 0);
return 0;
}
buffer += n;
size -= n;
}
return 1;
}

static void dev_urandom_close(void) {
if (urandom_cache.fd >= 0) {
int fd, n;
struct stat st;

if (fstat(urandom_cache.fd, &st)
&& st.st_dev == urandom_cache.st_dev
&& st.st_ino == urandom_cache.st_ino) {
fd = urandom_cache.fd;
urandom_cache.fd = -1;
do {
n = close(fd);
} while (n < 0 && errno == EINTR);
}
}
}

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

#ifdef CRYPTOGRAPHY_HAVE_SYS_GETRANDOM
static int getrandom_works = -1;

static int osrandom_init(ENGINE *e) {
if (getrandom_works == -1) {
long n;
char dest[1];
n = syscall(SYS_getrandom, dest, sizeof(dest), GRND_NONBLOCK);
/* TODO: EAGAIN when Kernel RNG is not initialized. */
if (n < 0 && (errno == ENOSYS || errno == EPERM)) {
getrandom_works = 0;
} else {
getrandom_works = 1;
}
}
/* fallback to dev urandom */
if (getrandom_works == 0) {
int fd = dev_urandom_fd();
if (fd < 0) {
return 0;
}
}
return 1;
}

static int osrandom_rand_bytes(unsigned char *buffer, int size) {
if (getrandom_works == 1) {
long n;
while (size > 0) {
do {
n = syscall(SYS_getrandom, buffer, size, GRND_NONBLOCK);
} while (n < 0 && errno == EINTR);

if (n <= 0) {
ERR_put_error(
ERR_LIB_RAND, 0, ERR_R_RAND_LIB,
"osrandom_engine.py:SYS_getrandom", 0);
return 0;
}
buffer += n;
size -= n;
}
return 1;
} else {
return dev_urandom_read(buffer, size);
}
}

static int osrandom_finish(ENGINE *e) {
dev_urandom_close();
return 1;
}

static int osrandom_rand_status(void) {
if ((getrandom_works != 1) && (urandom_cache.fd < 0)) {
return 0;
}
return 1;
}
#endif /* CRYPTOGRAPHY_HAVE_SYS_GETRANDOM */

/****************************************************************************
* dev_urandom engine for all remaining platforms
*/

#ifdef CRYPTOGRAPHY_USE_DEV_URANDOM

static int osrandom_init(ENGINE *e) {
int fd = dev_urandom_fd();
if (fd < 0) {
return 0;
}
return 1;
}

static int osrandom_rand_bytes(unsigned char *buffer, int size) {
return dev_urandom_read(buffer, size);
}

static int osrandom_finish(ENGINE *e) {
dev_urandom_close();
return 1;
}

static int osrandom_rand_status(void) {
if (urandom_cache.fd < 0) {
return 0;
}
return 1;
}

#endif /* CRYPTOGRAPHY_USE_DEV_URANDOM */
#endif /* _WIN32 */

/* 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) {
int res = osrandom_rand_bytes(buffer, size);
if (res == 0) {
return -1;
} else {
return res;
}
}

static RAND_METHOD osrandom_rand = {
NULL,
osrandom_rand_bytes,
NULL,
NULL,
osrandom_pseudo_rand_bytes,
osrandom_rand_status,
};

/* Returns 1 if successfully added, 2 if engine has previously been added,
and 0 for error. */
int Cryptography_add_osrandom_engine(void) {
ENGINE *e;
e = ENGINE_by_id(Cryptography_osrandom_engine_id);
if (e != NULL) {
ENGINE_free(e);
return 2;
} else {
ERR_clear_error();
}

e = ENGINE_new();
if (e == NULL) {
return 0;
}
if(!ENGINE_set_id(e, Cryptography_osrandom_engine_id) ||
!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_free(e);
return 0;
}
if (!ENGINE_add(e)) {
ENGINE_free(e);
return 0;
}
if (!ENGINE_free(e)) {
return 0;
}

return 1;
}
Loading

0 comments on commit 26836ff

Please sign in to comment.