-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Inspired by Python/random.c and the old implementation. Signed-off-by: Christian Heimes <christian@python.org>
- Loading branch information
Showing
6 changed files
with
420 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.