Browse Source

extmod/secp256k1mod.c: randomize context

- provide enhanced protection against side-channel leakage
The MMGen Project 5 months ago
parent
commit
fbeda2f071
2 changed files with 99 additions and 6 deletions
  1. 74 0
      extmod/random.h
  2. 25 6
      extmod/secp256k1mod.c

+ 74 - 0
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 <windows.h>
+#undef WIN32_NO_STATUS
+#include <ntstatus.h>
+#include <bcrypt.h>
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
+#include <sys/random.h>
+#elif defined(__OpenBSD__)
+#include <unistd.h>
+#else
+#error "Couldn't identify the OS"
+#endif
+
+#include <stddef.h>
+#include <limits.h>
+#include <stdio.h>
+
+
+/* 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;
+}

+ 25 - 6
extmod/secp256k1mod.c

@@ -25,6 +25,28 @@
 #define PY_SSIZE_T_CLEAN
 #define PY_SSIZE_T_CLEAN
 #include <Python.h>
 #include <Python.h>
 #include <secp256k1.h>
 #include <secp256k1.h>
+#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(
 static int privkey_check(
 		const secp256k1_context * ctx,
 		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;
 	size_t pubkey_bytes_len = compressed == 1 ? 33 : 65;
 	unsigned char pubkey_bytes[pubkey_bytes_len];
 	unsigned char pubkey_bytes[pubkey_bytes_len];
 	secp256k1_pubkey pubkey;
 	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) {
 	if (ctx == NULL) {
 		PyErr_SetString(PyExc_RuntimeError, "Context initialization failed");
 		PyErr_SetString(PyExc_RuntimeError, "Context initialization failed");
 		return NULL;
 		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");
 		PyErr_SetString(PyExc_ValueError, "Unable to parse extension mod arguments");
 		return NULL;
 		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;
 	secp256k1_pubkey pubkey;
 	if (!pubkey_parse_with_check(ctx, &pubkey, pubkey_bytes, pubkey_bytes_len)) {
 	if (!pubkey_parse_with_check(ctx, &pubkey, pubkey_bytes, pubkey_bytes_len)) {
 		return NULL;
 		return NULL;
@@ -168,8 +188,7 @@ static PyObject * pubkey_check(PyObject *self, PyObject *args) {
 		PyErr_SetString(PyExc_ValueError, "Unable to parse extension mod arguments");
 		PyErr_SetString(PyExc_ValueError, "Unable to parse extension mod arguments");
 		return NULL;
 		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;
 	secp256k1_pubkey pubkey;
 	if (!pubkey_parse_with_check(ctx, &pubkey, pubkey_bytes, pubkey_bytes_len)) {
 	if (!pubkey_parse_with_check(ctx, &pubkey, pubkey_bytes, pubkey_bytes_len)) {
 		return NULL;
 		return NULL;