diff --git a/extmod/random.h b/extmod/random.h new file mode 100644 index 00000000..3def2f70 --- /dev/null +++ b/extmod/random.h @@ -0,0 +1,74 @@ +/* + * Source: secp256k1/examples/examples_util.h + */ + +/************************************************************************* + * Copyright (c) 2020-2021 Elichai Turkel * + * Distributed under the CC0 software license, see the accompanying file * + * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * + *************************************************************************/ + +/* + * This file is an attempt at collecting best practice methods for obtaining randomness with different operating systems. + * It may be out-of-date. Consult the documentation of the operating system before considering to use the methods below. + * + * Platform randomness sources: + * Linux -> `getrandom(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. http://man7.org/linux/man-pages/man2/getrandom.2.html, https://linux.die.net/man/4/urandom + * macOS -> `getentropy(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. https://www.unix.com/man-page/mojave/2/getentropy, https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man4/random.4.auto.html + * FreeBSD -> `getrandom(2)`(`sys/random.h`), if not available `kern.arandom` should be used. https://www.freebsd.org/cgi/man.cgi?query=getrandom, https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4 + * OpenBSD -> `getentropy(2)`(`unistd.h`), if not available `/dev/urandom` should be used. https://man.openbsd.org/getentropy, https://man.openbsd.org/urandom + * Windows -> `BCryptGenRandom`(`bcrypt.h`). https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom + */ + +#if defined(_WIN32) +/* + * The defined WIN32_NO_STATUS macro disables return code definitions in + * windows.h, which avoids "macro redefinition" MSVC warnings in ntstatus.h. + */ +#define WIN32_NO_STATUS +#include +#undef WIN32_NO_STATUS +#include +#include +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) +#include +#elif defined(__OpenBSD__) +#include +#else +#error "Couldn't identify the OS" +#endif + +#include +#include +#include + + +/* Returns 1 on success, and 0 on failure. */ +static int fill_random(unsigned char* data, size_t size) { +#if defined(_WIN32) + NTSTATUS res = BCryptGenRandom(NULL, data, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG); + if (res != STATUS_SUCCESS || size > ULONG_MAX) { + return 0; + } else { + return 1; + } +#elif defined(__linux__) || defined(__FreeBSD__) + /* If `getrandom(2)` is not available you should fallback to /dev/urandom */ + ssize_t res = getrandom(data, size, 0); + if (res < 0 || (size_t)res != size ) { + return 0; + } else { + return 1; + } +#elif defined(__APPLE__) || defined(__OpenBSD__) + /* If `getentropy(2)` is not available you should fallback to either + * `SecRandomCopyBytes` or /dev/urandom */ + int res = getentropy(data, size); + if (res == 0) { + return 1; + } else { + return 0; + } +#endif + return 0; +} diff --git a/extmod/secp256k1mod.c b/extmod/secp256k1mod.c index d289bbe9..d5c350a9 100755 --- a/extmod/secp256k1mod.c +++ b/extmod/secp256k1mod.c @@ -25,6 +25,28 @@ #define PY_SSIZE_T_CLEAN #include #include +#include "random.h" + +static secp256k1_context * create_context( + const unsigned char randomize + ) { + secp256k1_context *ctx = secp256k1_context_create( + SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY + /* SECP256K1_CONTEXT_NONE */ /* see NOTE above */ + ); + if (randomize) { + unsigned char buf[32]; + if (!fill_random(buf, sizeof(buf))) { + printf("Failed to generate entropy\n"); + return NULL; + } + if (!secp256k1_context_randomize(ctx, buf)) { + printf("Failed to randomize context\n"); + return NULL; + } + } + return ctx; +} static int privkey_check( const secp256k1_context * ctx, @@ -96,8 +118,7 @@ static PyObject * pubkey_gen(PyObject *self, PyObject *args) { size_t pubkey_bytes_len = compressed == 1 ? 33 : 65; unsigned char pubkey_bytes[pubkey_bytes_len]; secp256k1_pubkey pubkey; - /* see NOTE */ - secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_context *ctx = create_context(1); if (ctx == NULL) { PyErr_SetString(PyExc_RuntimeError, "Context initialization failed"); return NULL; @@ -132,8 +153,7 @@ static PyObject * pubkey_tweak_add(PyObject *self, PyObject *args) { PyErr_SetString(PyExc_ValueError, "Unable to parse extension mod arguments"); return NULL; } - /* see NOTE */ - secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_context *ctx = create_context(1); secp256k1_pubkey pubkey; if (!pubkey_parse_with_check(ctx, &pubkey, pubkey_bytes, pubkey_bytes_len)) { return NULL; @@ -168,8 +188,7 @@ static PyObject * pubkey_check(PyObject *self, PyObject *args) { PyErr_SetString(PyExc_ValueError, "Unable to parse extension mod arguments"); return NULL; } - /* see NOTE */ - secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_context *ctx = create_context(1); secp256k1_pubkey pubkey; if (!pubkey_parse_with_check(ctx, &pubkey, pubkey_bytes, pubkey_bytes_len)) { return NULL;