Skip to content

Commit

Permalink
add submodule, build libsecp256k1, add baseline nif
Browse files Browse the repository at this point in the history
  • Loading branch information
samantehrani committed Jan 3, 2025
1 parent 81f404e commit f2cfad7
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 2 deletions.
23 changes: 23 additions & 0 deletions apps/arweave/c_src/secp256k1/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
ERTS_INCLUDE_DIR ?= $(shell erl -noshell -eval 'io:format("~ts/erts-~ts/include/", [code:root_dir(), erlang:system_info(version)]).' -s init stop)
ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -eval 'io:format("~ts", [code:lib_dir(erl_interface, include)]).' -s init stop)
ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -eval 'io:format("~ts", [code:lib_dir(erl_interface, lib)]).' -s init stop)

CC = cc
ifeq ($(MODE), debug)
CFLAGS ?= -O0 -g
else
CFLAGS ?= -O3
endif
CFLAGS += -fPIC -shared -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) -I /usr/local/include -I ../../lib/secp256k1/src -I ../../lib/secp256k1/include
LDFLAGS = -L../../lib/secp256k1/build/lib
LDLIBS = -L $(ERL_INTERFACE_LIB_DIR) -L /usr/local/lib -lsecp256k1
NIF_SRC = secp256k1_nif.c
NIF_SO = $(shell pwd)/../../priv/secp256k1_nif.so

all: $(NIF_SO)

$(NIF_SO): $(NIF_SRC)
$(CC) $(CFLAGS) $(NIF_SRC) -o $(NIF_SO) $(LDFLAGS) $(LDLIBS)

clean:
rm -f $(NIF_SO)
108 changes: 108 additions & 0 deletions apps/arweave/c_src/secp256k1/fill_random.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*************************************************************************
* 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>
#include <string.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;
}

static void print_hex(unsigned char* data, size_t size) {
size_t i;
printf("0x");
for (i = 0; i < size; i++) {
printf("%02x", data[i]);
}
printf("\n");
}

#if defined(_MSC_VER)
// For SecureZeroMemory
#include <Windows.h>
#endif
/* Cleanses memory to prevent leaking sensitive info. Won't be optimized out. */
static void secure_erase(void *ptr, size_t len) {
#if defined(_MSC_VER)
/* SecureZeroMemory is guaranteed not to be optimized out by MSVC. */
SecureZeroMemory(ptr, len);
#elif defined(__GNUC__)
/* We use a memory barrier that scares the compiler away from optimizing out the memset.
*
* Quoting Adam Langley <[email protected]> in commit ad1907fe73334d6c696c8539646c21b11178f20f
* in BoringSSL (ISC License):
* As best as we can tell, this is sufficient to break any optimisations that
* might try to eliminate "superfluous" memsets.
* This method used in memzero_explicit() the Linux kernel, too. Its advantage is that it is
* pretty efficient, because the compiler can still implement the memset() efficiently,
* just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by
* Yang et al. (USENIX Security 2017) for more background.
*/
memset(ptr, 0, len);
__asm__ __volatile__("" : : "r"(ptr) : "memory");
#else
void *(*volatile const volatile_memset)(void *, int, size_t) = memset;
volatile_memset(ptr, 0, len);
#endif
}
47 changes: 47 additions & 0 deletions apps/arweave/c_src/secp256k1/secp256k1_nif.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <secp256k1.h>

#include "../ar_nif.h"
#include "./fill_random.h"

#define SECP256K1_PUBKEY_UNCOMPRESSED_SIZE 65
#define SECP256K1_PUBKEY_COMPRESSED_SIZE 33
#define SECP256K1_PRIVKEY_SIZE 32
#define SECP256K1_CONTEXT_SEED_SIZE 32

static int secp256k1_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) {
return 0;
}

static ERL_NIF_TERM generate_key(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
unsigned char seed[SECP256K1_CONTEXT_SEED_SIZE];
if (!fill_random(seed, sizeof(seed))) {
return error_tuple(env, "Failed to generate random seed for context.");
}
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
if (!secp256k1_context_randomize(ctx, seed)) {
return error_tuple(env, "Failed to randomize context.");
}

unsigned char privbytes[SECP256K1_PRIVKEY_SIZE];
if (!fill_random(privbytes, sizeof(privbytes))) {
return error_tuple(env, "Failed to generate random key.");
}
if (!secp256k1_ec_seckey_verify(ctx, privbytes)) {
return error_tuple(env, "Generated secret secp256k1 key is invalid.");
}

secp256k1_pubkey pubkey;
if(!secp256k1_ec_pubkey_create(ctx, &pubkey, privbytes)) {
return error_tuple(env, "Failed to populate public key.");
}

unsigned char pubbytes[SECP256K1_PUBKEY_UNCOMPRESSED_SIZE];
size_t l = sizeof(pubbytes);
if (!secp256k1_ec_pubkey_serialize(ctx, pubbytes, &l, &pubkey, SECP256K1_EC_UNCOMPRESSED)) {
return error_tuple(env, "Failed to serialize public key.");
}

ERL_NIF_TERM privkey_bin = make_output_binary(env, privbytes, SECP256K1_PRIVKEY_SIZE);
ERL_NIF_TERM pubkey_bin = make_output_binary(env, pubbytes, SECP256K1_PUBKEY_UNCOMPRESSED_SIZE);
return ok_tuple2(env, privkey_bin, pubkey_bin);
}
2 changes: 1 addition & 1 deletion apps/arweave/lib/secp256k1
6 changes: 5 additions & 1 deletion rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@
{"(linux)", compile, "make -C apps/arweave/lib/RandomX/buildsquared"},
{"(freebsd|netbsd|openbsd)", compile, "gmake -C apps/arweave/lib/RandomX/buildsquared"},
{"(darwin|linux|freebsd|netbsd|openbsd)", compile, "bash -c \"cd apps/arweave/lib/RandomX/buildsquared && mv librandomx.a librandomxsquared.a\""},
% Build secp256k1
{"(darwin|linux|freebsd|netbsd|openbsd)", compile, "./scripts/build_secp256k1.sh > /dev/null"},
% Compile NIFs
{"(linux)", compile, "env AR=gcc-ar make all -C apps/arweave/c_src"},
{"(darwin)", compile, "make all -C apps/arweave/c_src"},
Expand All @@ -412,7 +414,9 @@
% Clean randomxsquared
{"(linux|darwin)", clean, "bash -c \"if [ -d apps/arweave/lib/RandomX/buildsquared ]; then make -C apps/arweave/lib/RandomX/buildsquared clean; fi\""},
{"(freebsd|netbsd|openbsd)", clean, "bash -c \"if [ -d apps/arweave/lib/RandomX/buildsquared ]; then gmake -C apps/arweave/lib/RandomX/buildsquared clean; fi\""},
% Clan NIFs
% Clean secp256k1
{"(linux|darwin|freebsd|netbsd|openbsd)", clean, "bash -c \"if [ -d apps/arweave/lib/secp256k1/build ]; then rm -rf apps/arweave/lib/secp256k1/build; fi\""},
% Clean NIFs
{"(linux|darwin)", clean, "make -C apps/arweave/c_src clean"},
{"(freebsd|netbsd|openbsd)", clean, "gmake -C apps/arweave/c_src clean"}
]}.
Expand Down
16 changes: 16 additions & 0 deletions scripts/build_secp256k1.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

BUILD_DIR="apps/arweave/lib/secp256k1/build"
CMAKE_OPTIONS="-DBUILD_SHARED_LIBS=OFF \
-DSECP256K1_BUILD_BENCHMARK=OFF \
-DSECP256K1_BUILD_EXHAUSTIVE_TESTS=OFF \
-DSECP256K1_BUILD_TESTS=OFF \
-DSECP256K1_DISABLE_SHARED=ON \
-DSECP256K1_ENABLE_MODULE_MUSIG=OFF \
-DSECP256K1_ENABLE_MODULE_EXTRAKEYS=OFF \
-DSECP256K1_ENABLE_MODULE_ELLSWIFT=OFF \
-DSECP256K1_ENABLE_MODULE_SCHNORRSIG=OFF"
mkdir -p $BUILD_DIR
cd $BUILD_DIR
cmake $CMAKE_OPTIONS ..
cmake --build .

0 comments on commit f2cfad7

Please sign in to comment.