├── priv └── .empty ├── .gitignore ├── c_src ├── build_deps.sh └── libsecp256k1_nif.c ├── README.md ├── Makefile ├── .circleci └── config.yml ├── LICENSE ├── mix.lock ├── mix.exs ├── etest └── libsecp256k1_tests.erl └── src └── libsecp256k1.erl /priv/.empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | c_src/*.o 2 | c_src/secp256k1 3 | ebin 4 | priv/*.so 5 | .eunit 6 | c_src/*.d 7 | _build/ 8 | deps/ 9 | .rebar 10 | doc/ 11 | *.tar 12 | -------------------------------------------------------------------------------- /c_src/build_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | test `basename $PWD` != "c_src" && cd c_src 6 | 7 | case "$1" in 8 | clean) 9 | rm -rf secp256k1 10 | ;; 11 | 12 | *) 13 | test -f secp256k1/.libs/libsecp256k1.so && exit 0 14 | 15 | (test -d secp256k1 || git clone https://github.com/bitcoin/secp256k1) 16 | 17 | (cd secp256k1 && git reset --hard d33352151699bd7598b868369dace092f7855740 && ./autogen.sh && ./configure --enable-module-recovery && make) 18 | #(cd secp256k1 && ./autogen.sh && ./configure --enable-module-recovery && make) 19 | ;; 20 | esac 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Erlang NIF C libsecp256k1 [![CircleCI](https://circleci.com/gh/exthereum/libsecp256k1.svg?style=svg)](https://circleci.com/gh/exthereum/libsecp256k1) 2 | 3 | ============ 4 | 5 | Bindings for most of the library functionality 6 | Tested with Erlang/OTP 17+ 7 | 8 | Installation 9 | ------------ 10 | If [available in Hex](https://hex.pm/docs/publish), the package can be installed 11 | by adding `libsecp256k1` to your list of dependencies in `mix.exs`: 12 | 13 | ```elixir 14 | def deps do 15 | [{:libsecp256k1, "~> 0.1.9"}] 16 | end 17 | ``` 18 | 19 | Build and usage steps 20 | --------------------- 21 | $ mix compile 22 | $ erl -pa _build/dev/lib/libsecp256k1/ebin/ 23 | Privkey = crypto:strong_rand_bytes(32). 24 | {ok, Pubkey} = libsecp256k1:ec_pubkey_create(Privkey, compressed). 25 | 26 | Testing 27 | ------- 28 | $ mix eunit 29 | 30 | Debugging 31 | --------- 32 | 33 | Library should be statically compiled. 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MIX = mix 2 | 3 | ERLANG_PATH = $(shell erl -eval 'io:format("~s", [lists:concat([code:root_dir(), "/erts-", erlang:system_info(version), "/include"])])' -s init stop -noshell) 4 | CFLAGS += -I$(ERLANG_PATH) 5 | CFLAGS += -I c_src/secp256k1 -I c_src/secp256k1/src -I c_src/secp256k1/include 6 | 7 | ifeq ($(wildcard deps/libsecp256k1),) 8 | LIB_PATH = ../libsecp256k1 9 | else 10 | LIB_PATH = deps/libsecp256k1 11 | endif 12 | 13 | CFLAGS += -I$(LIB_PATH)/src 14 | 15 | ifneq ($(OS),Windows_NT) 16 | CFLAGS += -fPIC 17 | 18 | ifeq ($(shell uname),Darwin) 19 | LDFLAGS += -dynamiclib -undefined dynamic_lookup 20 | endif 21 | endif 22 | 23 | LDFLAGS += c_src/secp256k1/.libs/libsecp256k1.a -lgmp 24 | 25 | .PHONY: clean 26 | 27 | all: priv/libsecp256k1_nif.so 28 | 29 | priv/libsecp256k1_nif.so: c_src/libsecp256k1_nif.c 30 | c_src/build_deps.sh 31 | $(CC) $(CFLAGS) -shared -o $@ c_src/libsecp256k1_nif.c $(LDFLAGS) 32 | 33 | clean: 34 | $(MIX) clean 35 | c_src/build_deps.sh clean 36 | $(MAKE) -C $(LIB_PATH) clean 37 | $(RM) priv/libsecp256k1_nif.so 38 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.0 2 | jobs: 3 | test: 4 | docker: 5 | - image: elixir:latest 6 | working_directory: ~/libsecp256k1 7 | steps: 8 | - run: apt-get update; apt-get -y install autoconf build-essential libgmp3-dev libtool 9 | - checkout: 10 | path: ~/libsecp256k1 11 | - restore_cache: 12 | keys: 13 | - v1-dependency-cache-{{ arch }}-{{ checksum "mix.lock" }} 14 | - v1-dependency-cache-{{ arch }} 15 | - v1-dependency-cache 16 | - run: mix local.hex --force 17 | - run: mix deps.get 18 | - run: make 19 | - run: 20 | name: Run tests 21 | command: | 22 | output=$(mix eunit -v -p) 23 | echo "$output" 24 | echo "$output" | grep "All [[:digit:]]* tests passed\." 25 | - save_cache: 26 | key: v1-dependency-cache-{{ arch }}-{{ checksum "mix.lock" }} 27 | paths: 28 | - _build 29 | - deps 30 | - ~/.mix 31 | 32 | workflows: 33 | version: 2 34 | test: 35 | jobs: 36 | - test 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Matthew Branton, Geoffrey Hayes 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "earmark": {:hex, :earmark, "1.2.5", "4d21980d5d2862a2e13ec3c49ad9ad783ffc7ca5769cf6ff891a4553fbaae761", [:mix], [], "hexpm"}, 3 | "ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, 4 | "makeup": {:hex, :makeup, "0.5.1", "966c5c2296da272d42f1de178c1d135e432662eca795d6dc12e5e8787514edf7", [:mix], [{:nimble_parsec, "~> 0.2.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, 5 | "makeup_elixir": {:hex, :makeup_elixir, "0.8.0", "1204a2f5b4f181775a0e456154830524cf2207cf4f9112215c05e0b76e4eca8b", [:mix], [{:makeup, "~> 0.5.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 0.2.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, 6 | "mix_erlang_tasks": {:hex, :mix_erlang_tasks, "0.1.0", "36819fec60b80689eb1380938675af215565a89320a9e29c72c70d97512e4649", [:mix], [], "hexpm"}, 7 | "nimble_parsec": {:hex, :nimble_parsec, "0.2.2", "d526b23bdceb04c7ad15b33c57c4526bf5f50aaa70c7c141b4b4624555c68259", [:mix], [], "hexpm"}, 8 | } 9 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Mix.Tasks.Compile.MakeBindings do 2 | def run(_) do 3 | {_, exit_code} = System.cmd("make", [], into: IO.stream(:stdio, :line)) 4 | 5 | case exit_code do 6 | 0 -> :ok 7 | _ -> :error 8 | end 9 | end 10 | end 11 | 12 | defmodule Libsecp256k1.Mixfile do 13 | use Mix.Project 14 | 15 | def project do 16 | [ 17 | app: :libsecp256k1, 18 | version: "0.1.10", 19 | language: :erlang, 20 | description: "Erlang NIF bindings for the the libsecp256k1 library", 21 | package: [ 22 | files: [ 23 | "LICENSE", 24 | "Makefile", 25 | "README.md", 26 | "c_src/build_deps.sh", 27 | "c_src/libsecp256k1_nif.c", 28 | "etest/libsecp256k1_tests.erl", 29 | "mix.exs", 30 | "priv/.empty", 31 | "src/libsecp256k1.erl" 32 | ], 33 | maintainers: ["Matthew Branton", "Geoffrey Hayes"], 34 | licenses: ["MIT"], 35 | links: %{"GitHub" => "https://github.com/exthereum/libsecp256k1"} 36 | ], 37 | compilers: [:make_bindings, :erlang, :app], 38 | deps: deps() 39 | ] 40 | end 41 | 42 | defp deps() do 43 | [ 44 | {:mix_erlang_tasks, "0.1.0", runtime: false}, 45 | {:ex_doc, "~> 0.17", only: :dev, runtime: false} 46 | ] 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /etest/libsecp256k1_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright 2015 Matthew Branton. All Rights Reserved. 2 | %% Use of this source code is governed by the MIT 3 | %% license that can be found in the LICENSE file. 4 | %% 5 | 6 | 7 | -module(libsecp256k1_tests). 8 | -author('mbranton@emberfinancial.com'). 9 | 10 | -include_lib("eunit/include/eunit.hrl"). 11 | 12 | start() -> 13 | ok. 14 | 15 | stop(_) -> 16 | ok. 17 | 18 | create_keys() -> 19 | A = crypto:strong_rand_bytes(32), %% Should use strong_rand in production 20 | {ok, B} = libsecp256k1:ec_pubkey_create(A, compressed), 21 | {ok, B2} = libsecp256k1:ec_pubkey_create(A, uncompressed), 22 | {ok, C} = libsecp256k1:ec_pubkey_decompress(B), 23 | ?assertEqual(B2, C), 24 | ?assertEqual(ok, libsecp256k1:ec_pubkey_verify(B)), 25 | ?assertEqual(ok, libsecp256k1:ec_pubkey_verify(C)). 26 | 27 | invalid_keys() -> 28 | A = crypto:strong_rand_bytes(16), 29 | ?assertMatch({error, _Msg}, libsecp256k1:ec_pubkey_create(A, compressed)), 30 | ?assertMatch({error, _Msg}, libsecp256k1:ec_pubkey_create(A, invalidflag)). 31 | 32 | import_export() -> 33 | A = crypto:strong_rand_bytes(32), 34 | {ok, B} = libsecp256k1:ec_privkey_export(A, compressed), 35 | {ok, C} = libsecp256k1:ec_privkey_import(B), 36 | ?assertEqual(A, C). 37 | 38 | tweaks() -> 39 | <> = crypto:strong_rand_bytes(64), 40 | {ok, Pubkey} = libsecp256k1:ec_pubkey_create(A, compressed), 41 | {ok, A2} = libsecp256k1:ec_privkey_tweak_add(A, Tweak), 42 | {ok, A3} = libsecp256k1:ec_privkey_tweak_mul(A, Tweak), 43 | {ok, Pubkey2} = libsecp256k1:ec_pubkey_tweak_add(Pubkey, Tweak), 44 | {ok, Pubkey3} = libsecp256k1:ec_pubkey_tweak_mul(Pubkey, Tweak), 45 | {ok, PubkeyA2} = libsecp256k1:ec_pubkey_create(A2, compressed), 46 | {ok, PubkeyA3} = libsecp256k1:ec_pubkey_create(A3, compressed), 47 | ?assertEqual(Pubkey2, PubkeyA2), 48 | ?assertEqual(Pubkey3, PubkeyA3). 49 | 50 | signing() -> 51 | Msg = <<"This is a secret message...">>, 52 | A = crypto:strong_rand_bytes(32), 53 | {ok, Pubkey} = libsecp256k1:ec_pubkey_create(A, compressed), 54 | {ok, Signature} = libsecp256k1:ecdsa_sign(Msg, A, default, <<>>), 55 | ?assertEqual(ok, libsecp256k1:ecdsa_verify(Msg, Signature, Pubkey)). 56 | 57 | blank_msg() -> 58 | Msg = <<>>, 59 | A = crypto:strong_rand_bytes(32), 60 | {ok, Pubkey} = libsecp256k1:ec_pubkey_create(A, compressed), 61 | {ok, Signature} = libsecp256k1:ecdsa_sign(Msg, A, default, <<>>), 62 | ?assertEqual(ok, libsecp256k1:ecdsa_verify(Msg, Signature, Pubkey)). 63 | 64 | compact_signing() -> 65 | Msg = <<"This is a very secret compact message...">>, 66 | A = crypto:strong_rand_bytes(32), 67 | {ok, Pubkey} = libsecp256k1:ec_pubkey_create(A, uncompressed), 68 | {ok, Signature, RecoveryID} = libsecp256k1:ecdsa_sign_compact(Msg, A, default, <<>>), 69 | {ok, RecoveredKey} = libsecp256k1:ecdsa_recover_compact(Msg, Signature, uncompressed, RecoveryID), 70 | ?assertEqual(Pubkey, RecoveredKey), 71 | ?assertEqual(ok, libsecp256k1:ecdsa_verify_compact(Msg, Signature, Pubkey)). 72 | 73 | sha256() -> 74 | A = crypto:strong_rand_bytes(64), 75 | DoubleHashed = crypto:hash(sha256, crypto:hash(sha256, A)), 76 | ?assertEqual(DoubleHashed, libsecp256k1:sha256(libsecp256k1:sha256(A))), 77 | ?assertEqual(DoubleHashed, libsecp256k1:dsha256(A)). 78 | 79 | secp256k1_test_() -> 80 | {setup, 81 | fun start/0, 82 | fun stop/1, 83 | [ 84 | {"Create keys", fun create_keys/0}, 85 | {"Invalid keys", fun invalid_keys/0}, 86 | {"Import export", fun import_export/0}, 87 | {"Curve tweaks", fun tweaks/0}, 88 | {"Signing", fun signing/0}, 89 | {"Blank sign", fun blank_msg/0}, 90 | {"Compact", fun compact_signing/0}, 91 | {"Sha256", fun sha256/0} 92 | ] 93 | }. 94 | -------------------------------------------------------------------------------- /src/libsecp256k1.erl: -------------------------------------------------------------------------------- 1 | %% Copyright 2015 Matthew Branton. All Rights Reserved. 2 | %% Use of this source code is governed by the MIT 3 | %% license that can be found in the LICENSE file. 4 | %% 5 | %% @doc Erlang NIF bindings 6 | %% libsec256k1 elliptic curve library 7 | 8 | -module(libsecp256k1). 9 | -author("mbranton@emberfinancial.com"). 10 | 11 | -export([dsha256/1, 12 | sha256/1, 13 | hmac_sha256/2, 14 | rand32/0, 15 | rand256/0, 16 | ec_seckey_verify/1, 17 | ec_pubkey_create/2, 18 | ec_pubkey_decompress/1, 19 | ec_pubkey_verify/1, 20 | ec_privkey_export/2, 21 | ec_privkey_import/1, 22 | ec_privkey_tweak_add/2, 23 | ec_pubkey_tweak_add/2, 24 | ec_privkey_tweak_mul/2, 25 | ec_pubkey_tweak_mul/2, 26 | ecdsa_sign/4, 27 | ecdsa_verify/3, 28 | ecdsa_sign_compact/4, 29 | ecdsa_verify_compact/3, 30 | ecdsa_recover_compact/4]). 31 | 32 | -on_load(init/0). 33 | 34 | -define(APPNAME, libsecp256k1). 35 | -define(LIBNAME, libsecp256k1_nif). 36 | 37 | -type hash() :: binary(). 38 | -type public_key() :: binary(). 39 | -type private_key() :: binary(). 40 | -type compression() :: compressed | uncompressed. 41 | -type signature() :: binary(). 42 | -type recovery_id() :: integer(). 43 | 44 | %% API 45 | -spec dsha256(binary()) -> hash(). 46 | dsha256(_) -> 47 | erlang:nif_error({error, not_loaded}). 48 | 49 | -spec sha256(binary()) -> hash(). 50 | sha256(_) -> 51 | erlang:nif_error({error, not_loaded}). 52 | 53 | -spec hmac_sha256(binary(), binary()) -> hash(). 54 | hmac_sha256(_, _) -> 55 | erlang:nif_error({error, not_loaded}). 56 | 57 | %% testing PRNG 58 | -spec rand32() -> binary(). 59 | rand32() -> 60 | erlang:nif_error({error, not_loaded}). 61 | 62 | -spec rand256() -> binary(). 63 | rand256() -> 64 | erlang:nif_error({error, not_loaded}). 65 | 66 | %% Ecdsa functions 67 | -spec ec_seckey_verify(private_key()) -> ok | error. 68 | ec_seckey_verify(_) -> 69 | erlang:nif_error({error, not_loaded}). 70 | 71 | -spec ec_pubkey_create(private_key(), compression()) -> {ok, public_key()} | {error, string()}. 72 | ec_pubkey_create(_, _) -> 73 | erlang:nif_error({error, not_loaded}). 74 | 75 | -spec ec_pubkey_decompress(public_key()) -> {ok, public_key()} | {error, string()}. 76 | ec_pubkey_decompress(_) -> 77 | erlang:nif_error({error, not_loaded}). 78 | 79 | -spec ec_pubkey_verify(public_key()) -> ok | error. 80 | ec_pubkey_verify(_) -> 81 | erlang:nif_error({error, not_loaded}). 82 | 83 | -spec ec_privkey_export(private_key(), compression()) -> {ok, binary()} | {error, string()}. 84 | ec_privkey_export(_, _) -> 85 | erlang:nif_error({error, not_loaded}). 86 | 87 | -spec ec_privkey_import(binary()) -> {ok, private_key()} | {error, string()}. 88 | ec_privkey_import(_) -> 89 | erlang:nif_error({error, not_loaded}). 90 | 91 | -spec ec_privkey_tweak_add(private_key(), binary()) -> {ok, private_key()} | {error, string()}. 92 | ec_privkey_tweak_add(_, _) -> 93 | erlang:nif_error({error, not_loaded}). 94 | 95 | -spec ec_pubkey_tweak_add(public_key(), binary()) -> {ok, public_key()} | {error, string()}. 96 | ec_pubkey_tweak_add(_, _) -> 97 | erlang:nif_error({error, not_loaded}). 98 | 99 | -spec ec_privkey_tweak_mul(private_key(), binary()) -> {ok, private_key()} | {error, string()}. 100 | ec_privkey_tweak_mul(_, _) -> 101 | erlang:nif_error({error, not_loaded}). 102 | 103 | -spec ec_pubkey_tweak_mul(public_key(), binary()) -> {ok, public_key()} | {error, string()}. 104 | ec_pubkey_tweak_mul(_, _) -> 105 | erlang:nif_error({error, not_loaded}). 106 | 107 | -spec ecdsa_sign(binary(), private_key(), atom(), binary()) -> {ok, signature()} | {error, string()}. 108 | ecdsa_sign(_, _, _, _) -> 109 | erlang:nif_error({error, not_loaded}). 110 | 111 | -spec ecdsa_verify(binary(), signature(), public_key()) -> ok | error. 112 | ecdsa_verify(_, _, _) -> 113 | erlang:nif_error({error, not_loaded}). 114 | 115 | -spec ecdsa_sign_compact(binary(), private_key(), atom(), binary()) -> {ok, signature(), recovery_id()} | {error, string()}. 116 | ecdsa_sign_compact(_, _, _, _) -> 117 | erlang:nif_error({error, not_loaded}). 118 | 119 | -spec ecdsa_verify_compact(binary(), signature(), public_key()) -> ok | error. 120 | ecdsa_verify_compact(_, _, _) -> 121 | erlang:nif_error({error, not_loaded}). 122 | 123 | -spec ecdsa_recover_compact(binary(), signature(), compression(), recovery_id()) -> {ok, public_key()} | {error, string()}. 124 | ecdsa_recover_compact(_, _, _, _) -> 125 | erlang:nif_error({error, not_loaded}). 126 | 127 | %% Iternal functions 128 | 129 | init() -> 130 | SoName = case code:priv_dir(?APPNAME) of 131 | {error, bad_name} -> 132 | case filelib:is_dir(filename:join(["..", priv])) of 133 | true -> 134 | filename:join(["..", priv, ?LIBNAME]); 135 | _ -> 136 | filename:join([priv, ?LIBNAME]) 137 | end; 138 | Dir -> 139 | filename:join(Dir, ?LIBNAME) 140 | end, 141 | erlang:load_nif(SoName, 0). 142 | 143 | % This is just a simple place holder. It mostly shouldn't ever be called 144 | % unless there was an unexpected error loading the NIF shared library. 145 | 146 | not_loaded(Line) -> 147 | exit({not_loaded, [{module, ?MODULE}, {line, Line}]}). 148 | -------------------------------------------------------------------------------- /c_src/libsecp256k1_nif.c: -------------------------------------------------------------------------------- 1 | /* Copyright, 2015 Matthew Branton 2 | * Distributed under the MIT license located in the LICENSE file. 3 | * 4 | * */ 5 | 6 | #include "erl_nif.h" 7 | 8 | #include "libsecp256k1-config.h" 9 | #include "secp256k1.c" 10 | #include "include/secp256k1.h" 11 | #include "testrand_impl.h" 12 | #include "include/secp256k1_recovery.h" 13 | 14 | // Key export 15 | #include "contrib/lax_der_parsing.c" 16 | #include "contrib/lax_der_privatekey_parsing.c" 17 | 18 | // Prototypes 19 | 20 | 21 | static ERL_NIF_TERM atom_from_result(ErlNifEnv* env, int res); 22 | static ERL_NIF_TERM error_result(ErlNifEnv* env, char* error_msg); 23 | static ERL_NIF_TERM ok_result(ErlNifEnv* env, ERL_NIF_TERM *r); 24 | int get_compressed_flag(ErlNifEnv* env, ERL_NIF_TERM arg, int* compressed, size_t* pubkeylen); 25 | int check_compressed(size_t Size); 26 | int get_nonce_function(ErlNifEnv* env, ERL_NIF_TERM nonce_term, ERL_NIF_TERM nonce_data_term, secp256k1_nonce_function* noncefp, ErlNifBinary* noncedata); 27 | int get_recid(ErlNifEnv* env, ERL_NIF_TERM argv, int* recid); 28 | // Global context 29 | static secp256k1_context *ctx = NULL; 30 | 31 | static int 32 | load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) 33 | { 34 | ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); 35 | return 0; 36 | } 37 | 38 | static int 39 | upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM load_info) 40 | { 41 | return 0; 42 | } 43 | 44 | static void 45 | unload(ErlNifEnv* env, void* priv) 46 | { 47 | secp256k1_context_destroy(ctx); 48 | return; 49 | } 50 | 51 | // Double Sha256 Hash 52 | static ERL_NIF_TERM 53 | dsha256(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 54 | { 55 | unsigned char* output; 56 | ErlNifBinary p; 57 | ERL_NIF_TERM r; 58 | 59 | if (!enif_inspect_binary(env, argv[0], &p)) { 60 | return enif_make_badarg(env); 61 | } 62 | 63 | // Create a NIF binary object with a 32 byte return 64 | output = enif_make_new_binary(env, 32, &r); 65 | 66 | secp256k1_sha256 hasher; 67 | secp256k1_sha256_initialize(&hasher); 68 | secp256k1_sha256_write(&hasher, (const unsigned char*)(p.data), p.size); 69 | secp256k1_sha256_finalize(&hasher, output); 70 | 71 | secp256k1_sha256 hasher2; 72 | secp256k1_sha256_initialize(&hasher2); 73 | secp256k1_sha256_write(&hasher2, (const unsigned char*)(output), 32); 74 | secp256k1_sha256_finalize(&hasher2, output); 75 | 76 | return r; 77 | } 78 | 79 | 80 | static ERL_NIF_TERM 81 | sha256(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 82 | { 83 | unsigned char* output; 84 | ErlNifBinary p; 85 | ERL_NIF_TERM r; 86 | 87 | if (!enif_inspect_binary(env, argv[0], &p)) { 88 | return enif_make_badarg(env); 89 | } 90 | 91 | // Create a NIF binary object with a 32 byte return 92 | output = enif_make_new_binary(env, 32, &r); 93 | secp256k1_sha256 hasher; 94 | secp256k1_sha256_initialize(&hasher); 95 | secp256k1_sha256_write(&hasher, (const unsigned char*)(p.data), p.size); 96 | secp256k1_sha256_finalize(&hasher, output); 97 | 98 | return r; 99 | } 100 | 101 | static ERL_NIF_TERM 102 | hmac_sha256(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 103 | { 104 | unsigned char* output; 105 | ErlNifBinary key, input; 106 | ERL_NIF_TERM r; 107 | 108 | if (!enif_inspect_binary(env, argv[0], &key)) { 109 | return enif_make_badarg(env); 110 | } 111 | 112 | if (!enif_inspect_binary(env, argv[1], &input)) { 113 | return enif_make_badarg(env); 114 | } 115 | 116 | // Create a NIF binary object with a 32 byte return 117 | output = enif_make_new_binary(env, 32, &r); 118 | secp256k1_hmac_sha256 hasher; 119 | secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(key.data), key.size); 120 | secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(input.data), input.size); 121 | secp256k1_hmac_sha256_finalize(&hasher, output); 122 | 123 | return r; 124 | } 125 | 126 | // Random Test Functions 127 | // 128 | 129 | static ERL_NIF_TERM 130 | rand32(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 131 | { 132 | ERL_NIF_TERM r; 133 | unsigned char* output = enif_make_new_binary(env, 4, &r); 134 | uint32_t v = secp256k1_rand32(); 135 | memcpy(&v, output, 4); 136 | return r; 137 | } 138 | 139 | static ERL_NIF_TERM 140 | rand256(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 141 | { 142 | ERL_NIF_TERM r; 143 | unsigned char* output = enif_make_new_binary(env, 32, &r); 144 | secp256k1_rand256(output); 145 | return r; 146 | } 147 | 148 | // Number operations 149 | 150 | // Scalar operations 151 | 152 | // ECDSA key operations 153 | 154 | static ERL_NIF_TERM 155 | ec_seckey_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 156 | { 157 | ErlNifBinary privkey; 158 | secp256k1_scalar key; 159 | unsigned char b32[32]; 160 | int overflow = 0; 161 | int result; 162 | 163 | if (!enif_inspect_binary(env, argv[0], &privkey)) { 164 | return enif_make_badarg(env); 165 | } 166 | 167 | if (privkey.size != 32) { 168 | return enif_make_badarg(env); 169 | } 170 | 171 | secp256k1_scalar_set_b32(&key, b32, &overflow); 172 | if (overflow || secp256k1_scalar_is_zero(&key)) { 173 | return enif_make_int(env, 0); 174 | } 175 | 176 | secp256k1_scalar_get_b32(privkey.data, &key); 177 | 178 | result = secp256k1_ec_seckey_verify(ctx, privkey.data); 179 | return atom_from_result(env, result); 180 | } 181 | 182 | static ERL_NIF_TERM 183 | ec_pubkey_create(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 184 | { 185 | int result; 186 | ErlNifBinary privkey; 187 | ERL_NIF_TERM r; 188 | unsigned char* pubkey_buf; 189 | size_t pubkeylen; 190 | int compressed; 191 | secp256k1_pubkey pubkey; 192 | 193 | if (!enif_inspect_binary(env, argv[0], &privkey)) { 194 | return enif_make_badarg(env); 195 | } 196 | 197 | if (!get_compressed_flag(env, argv[1], &compressed, &pubkeylen)) { 198 | return error_result(env, "Compression flag invalid"); 199 | } 200 | 201 | if (privkey.size != 32) { 202 | return error_result(env, "Private key size not 32 bytes"); 203 | } 204 | 205 | pubkey_buf = enif_make_new_binary(env, pubkeylen, &r); 206 | result = secp256k1_ec_pubkey_create(ctx, &pubkey, privkey.data); 207 | if (result == 1 && 208 | (secp256k1_ec_pubkey_serialize(ctx, pubkey_buf, &pubkeylen, &pubkey, compressed) == 1)) { 209 | return ok_result(env, &r); 210 | } else { 211 | return error_result(env, "Public key generation error"); 212 | } 213 | 214 | } 215 | 216 | 217 | static ERL_NIF_TERM 218 | ec_pubkey_decompress(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 219 | { 220 | 221 | ErlNifBinary pubkey; 222 | ERL_NIF_TERM r; 223 | unsigned char* decompressedkey; 224 | size_t decompressedkeylen = 65; 225 | secp256k1_pubkey pubkeyt; 226 | 227 | if (!enif_inspect_binary(env, argv[0], &pubkey)) { 228 | return enif_make_badarg(env); 229 | } 230 | 231 | if ((pubkey.size != 33) && (pubkey.size != 65)) { 232 | return error_result(env, "Public key size != 33 or 65 bytes"); 233 | } 234 | 235 | decompressedkey = enif_make_new_binary(env, decompressedkeylen, &r); 236 | 237 | if (secp256k1_ec_pubkey_parse(ctx, &pubkeyt, pubkey.data, pubkey.size) != 1) { 238 | return error_result(env, "Public key parse error"); 239 | } 240 | 241 | if (secp256k1_ec_pubkey_serialize(ctx, decompressedkey, &decompressedkeylen, 242 | &pubkeyt, SECP256K1_EC_UNCOMPRESSED) != 1) { 243 | return error_result(env, "Public key decompression error"); 244 | } 245 | 246 | return ok_result(env, &r); 247 | 248 | } 249 | 250 | static ERL_NIF_TERM 251 | ec_pubkey_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 252 | { 253 | int result; 254 | ErlNifBinary pubkey; 255 | secp256k1_pubkey pubkeyt; 256 | 257 | if (!enif_inspect_binary(env, argv[0], &pubkey)) { 258 | return enif_make_badarg(env); 259 | } 260 | 261 | if ((pubkey.size != 33) && (pubkey.size != 65)) { 262 | return error_result(env, "Public key size != 33 or 65 bytes"); 263 | } 264 | 265 | result = secp256k1_ec_pubkey_parse(ctx, &pubkeyt, pubkey.data, pubkey.size); 266 | return atom_from_result(env, result); 267 | } 268 | 269 | static ERL_NIF_TERM 270 | ec_privkey_export(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 271 | { 272 | ERL_NIF_TERM r; 273 | ErlNifBinary privkey; 274 | unsigned char exported_seckey[300]; 275 | unsigned char* seckey_buf; 276 | size_t seckey_len = 300; 277 | int compressed, result; 278 | size_t pubkeylen; 279 | 280 | if (!enif_inspect_binary(env, argv[0], &privkey)) { 281 | return enif_make_badarg(env); 282 | } 283 | 284 | if (privkey.size != 32) { 285 | return enif_make_badarg(env); 286 | } 287 | 288 | if (!get_compressed_flag(env, argv[1], &compressed, &pubkeylen)) { 289 | return error_result(env, "Compression flag invalid"); 290 | } 291 | 292 | result = ec_privkey_export_der(ctx, 293 | exported_seckey, &seckey_len, 294 | privkey.data, compressed); 295 | if (result == 0) { 296 | return error_result(env, "privkey export returned 0"); 297 | } 298 | 299 | seckey_buf = enif_make_new_binary(env, seckey_len, &r); 300 | memcpy(seckey_buf, exported_seckey, seckey_len); 301 | return ok_result(env, &r); 302 | } 303 | 304 | static ERL_NIF_TERM 305 | ec_privkey_import(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 306 | { 307 | ERL_NIF_TERM r; 308 | ErlNifBinary exportedkey; 309 | unsigned char* privkey_buf; 310 | int result; 311 | 312 | if (!enif_inspect_binary(env, argv[0], &exportedkey)) { 313 | return enif_make_badarg(env); 314 | } 315 | 316 | privkey_buf = enif_make_new_binary(env, 32, &r); 317 | result = ec_privkey_import_der(ctx, privkey_buf, exportedkey.data, exportedkey.size); 318 | if (result == 0) { 319 | return error_result(env, "privkey import returned 0"); 320 | } 321 | 322 | return ok_result(env, &r); 323 | 324 | } 325 | 326 | static ERL_NIF_TERM 327 | ec_privkey_tweak_add(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 328 | { 329 | ERL_NIF_TERM r; 330 | ErlNifBinary privkey, tweak; 331 | unsigned char* privkey_buf; 332 | int result; 333 | 334 | if (!enif_inspect_binary(env, argv[0], &privkey)) { 335 | return enif_make_badarg(env); 336 | } 337 | 338 | if (!enif_inspect_binary(env, argv[1], &tweak)) { 339 | return enif_make_badarg(env); 340 | } 341 | 342 | privkey_buf = enif_make_new_binary(env, 32, &r); 343 | memcpy(privkey_buf, privkey.data, privkey.size); 344 | 345 | result = secp256k1_ec_privkey_tweak_add(ctx, privkey_buf, tweak.data); 346 | 347 | if (result == 0) { 348 | return error_result(env, "ec_privkey_tweak_add returned 0"); 349 | } 350 | 351 | return ok_result(env, &r); 352 | } 353 | 354 | static ERL_NIF_TERM 355 | ec_pubkey_tweak_add(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 356 | { 357 | ERL_NIF_TERM r; 358 | ErlNifBinary pubkey, tweak; 359 | unsigned char* pubkey_buf; 360 | secp256k1_pubkey pubkeyt; 361 | 362 | if (!enif_inspect_binary(env, argv[0], &pubkey)) { 363 | return enif_make_badarg(env); 364 | } 365 | 366 | if (!enif_inspect_binary(env, argv[1], &tweak)) { 367 | return enif_make_badarg(env); 368 | } 369 | 370 | pubkey_buf = enif_make_new_binary(env, pubkey.size, &r); 371 | 372 | if (secp256k1_ec_pubkey_parse(ctx, &pubkeyt, pubkey.data, pubkey.size) != 1) { 373 | return enif_make_badarg(env); 374 | }; 375 | 376 | if (secp256k1_ec_pubkey_tweak_add(ctx, &pubkeyt, tweak.data) != 1) { 377 | return error_result(env, "ec_pubkey_tweak_add returned 0"); 378 | } 379 | 380 | if (secp256k1_ec_pubkey_serialize(ctx, pubkey_buf, (size_t *)&pubkey.size, &pubkeyt, 381 | check_compressed(pubkey.size)) != 1) { 382 | return error_result(env, "Public key serialize error"); 383 | } 384 | 385 | return ok_result(env, &r); 386 | } 387 | 388 | int check_compressed(size_t Size) { 389 | if (Size == 33) { 390 | return SECP256K1_EC_COMPRESSED; 391 | } 392 | return SECP256K1_EC_UNCOMPRESSED; 393 | } 394 | 395 | static ERL_NIF_TERM 396 | ec_privkey_tweak_mul(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 397 | { 398 | ERL_NIF_TERM r; 399 | ErlNifBinary privkey, tweak; 400 | unsigned char* privkey_buf; 401 | int result; 402 | 403 | if (!enif_inspect_binary(env, argv[0], &privkey)) { 404 | return enif_make_badarg(env); 405 | } 406 | 407 | if (!enif_inspect_binary(env, argv[1], &tweak)) { 408 | return enif_make_badarg(env); 409 | } 410 | 411 | privkey_buf = enif_make_new_binary(env, 32, &r); 412 | memcpy(privkey_buf, privkey.data, privkey.size); 413 | 414 | result = secp256k1_ec_privkey_tweak_mul(ctx, privkey_buf, tweak.data); 415 | 416 | if (result == 0) { 417 | return error_result(env, "ec_privkey_tweak_mul returned 0"); 418 | } 419 | 420 | return ok_result(env, &r); 421 | 422 | } 423 | 424 | static ERL_NIF_TERM 425 | ec_pubkey_tweak_mul(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 426 | { 427 | ERL_NIF_TERM r; 428 | ErlNifBinary pubkey, tweak; 429 | unsigned char* pubkey_buf; 430 | secp256k1_pubkey pubkeyt; 431 | 432 | if (!enif_inspect_binary(env, argv[0], &pubkey)) { 433 | return enif_make_badarg(env); 434 | } 435 | 436 | if (!enif_inspect_binary(env, argv[1], &tweak)) { 437 | return enif_make_badarg(env); 438 | } 439 | 440 | pubkey_buf = enif_make_new_binary(env, pubkey.size, &r); 441 | 442 | if (secp256k1_ec_pubkey_parse(ctx, &pubkeyt, pubkey.data, pubkey.size) != 1) { 443 | return enif_make_badarg(env); 444 | }; 445 | 446 | if (secp256k1_ec_pubkey_tweak_mul(ctx, &pubkeyt, tweak.data) != 1) { 447 | return error_result(env, "ec_pubkey_tweak_mul returned 0"); 448 | } 449 | 450 | if (secp256k1_ec_pubkey_serialize(ctx, pubkey_buf, (size_t *)&pubkey.size, &pubkeyt, 451 | check_compressed(pubkey.size)) != 1) { 452 | return error_result(env, "Public key serialize error"); 453 | } 454 | 455 | return ok_result(env, &r); 456 | 457 | } 458 | 459 | //// Ecdsa Signing 460 | // 461 | static ERL_NIF_TERM 462 | ecdsa_sign(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 463 | { 464 | ERL_NIF_TERM r; 465 | ErlNifBinary message, privkey, noncedata; 466 | int result; 467 | secp256k1_ecdsa_signature signature; 468 | unsigned char* finishedsig; 469 | unsigned char intermediatesig[74]; 470 | size_t siglen = 74; 471 | 472 | secp256k1_nonce_function noncefp; 473 | 474 | if (!enif_inspect_binary(env, argv[0], &message)) { 475 | return enif_make_badarg(env); 476 | } 477 | 478 | if (!enif_inspect_binary(env, argv[1], &privkey)) { 479 | return enif_make_badarg(env); 480 | } 481 | 482 | if (!get_nonce_function(env, argv[2], argv[3], &noncefp, &noncedata)) { 483 | return error_result(env, "Invalid nonce function name"); 484 | } 485 | 486 | result = secp256k1_ecdsa_sign(ctx, &signature, message.data, 487 | privkey.data, noncefp, noncedata.data); 488 | if (!result) { 489 | return error_result(env, "ecdsa_sign returned 0"); 490 | } 491 | 492 | // DER serialization may return a signature under buffer size 493 | // need to delay nif binary allocation 494 | if (secp256k1_ecdsa_signature_serialize_der(ctx, &intermediatesig, &siglen, &signature) != 1) { 495 | return error_result(env, "ecdsa_signature_serialize returned 0"); 496 | } 497 | CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature, &intermediatesig, siglen) == 1); 498 | finishedsig = enif_make_new_binary(env, siglen, &r); 499 | memcpy(finishedsig, intermediatesig, siglen); 500 | return ok_result(env, &r); 501 | 502 | } 503 | 504 | static ERL_NIF_TERM 505 | ecdsa_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 506 | { 507 | ErlNifBinary message, rawsignature, rawpubkey; 508 | secp256k1_ecdsa_signature signature; 509 | secp256k1_pubkey pubkey; 510 | int result; 511 | 512 | if (!enif_inspect_binary(env, argv[0], &message)) { 513 | return enif_make_badarg(env); 514 | } 515 | 516 | if (!enif_inspect_binary(env, argv[1], &rawsignature)) { 517 | return enif_make_badarg(env); 518 | } 519 | 520 | if (!enif_inspect_binary(env, argv[2], &rawpubkey)) { 521 | return enif_make_badarg(env); 522 | } 523 | 524 | // Parse serialized signature 525 | if (secp256k1_ecdsa_signature_parse_der(ctx, &signature, rawsignature.data, rawsignature.size) != 1) { 526 | return error_result(env, "ecdsa signature der parse error"); 527 | } 528 | // Parse serialized public key 529 | 530 | if (secp256k1_ec_pubkey_parse(ctx, &pubkey, rawpubkey.data, rawpubkey.size) != 1) { 531 | return error_result(env, "Public key invalid"); 532 | }; 533 | 534 | result = secp256k1_ecdsa_verify(ctx, &signature, message.data, &pubkey); 535 | 536 | return atom_from_result(env, result); 537 | } 538 | 539 | static ERL_NIF_TERM 540 | ecdsa_sign_compact(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 541 | { 542 | ERL_NIF_TERM r; 543 | ErlNifBinary message, privkey, noncedata; 544 | int result; 545 | secp256k1_ecdsa_recoverable_signature signature; 546 | unsigned char* finishedsig; 547 | int siglen = 64; 548 | int recid = 0; 549 | 550 | secp256k1_nonce_function noncefp; 551 | 552 | if (!enif_inspect_binary(env, argv[0], &message)) { 553 | return enif_make_badarg(env); 554 | } 555 | 556 | if (!enif_inspect_binary(env, argv[1], &privkey)) { 557 | return enif_make_badarg(env); 558 | } 559 | 560 | if (!get_nonce_function(env, argv[2], argv[3], &noncefp, &noncedata)) { 561 | return error_result(env, "Invalid nonce function name"); 562 | } 563 | 564 | result = secp256k1_ecdsa_sign_recoverable(ctx, &signature, message.data, 565 | privkey.data, noncefp, noncedata.data); 566 | if (!result) { 567 | return error_result(env, "ecdsa_sign returned 0"); 568 | } 569 | 570 | finishedsig = enif_make_new_binary(env, siglen, &r); 571 | if (secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, 572 | finishedsig, &recid, &signature) != 1) { 573 | return error_result(env, "ecdsa_signature_serialize returned 0"); 574 | } 575 | 576 | if (recid == -1) { 577 | return error_result(env, "invalid recovery id"); 578 | } 579 | 580 | return enif_make_tuple3(env, enif_make_atom(env, "ok"), r, enif_make_int(env, recid)); 581 | 582 | } 583 | 584 | static ERL_NIF_TERM 585 | ecdsa_verify_compact(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 586 | { 587 | ErlNifBinary message, rawsignature, rawpubkey; 588 | secp256k1_ecdsa_signature signature; 589 | secp256k1_pubkey pubkey; 590 | int result; 591 | 592 | if (!enif_inspect_binary(env, argv[0], &message)) { 593 | return enif_make_badarg(env); 594 | } 595 | 596 | if (!enif_inspect_binary(env, argv[1], &rawsignature)) { 597 | return enif_make_badarg(env); 598 | } 599 | 600 | if (!enif_inspect_binary(env, argv[2], &rawpubkey)) { 601 | return enif_make_badarg(env); 602 | } 603 | 604 | // Parse serialized signature 605 | if (secp256k1_ecdsa_signature_parse_compact(ctx, &signature, rawsignature.data) != 1) { 606 | return error_result(env, "ecdsa signature der parse error"); 607 | } 608 | // Parse serialized public key 609 | 610 | if (secp256k1_ec_pubkey_parse(ctx, &pubkey, rawpubkey.data, rawpubkey.size) != 1) { 611 | return error_result(env, "Public key invalid"); 612 | }; 613 | 614 | result = secp256k1_ecdsa_verify(ctx, &signature, message.data, &pubkey); 615 | 616 | return atom_from_result(env, result); 617 | } 618 | 619 | static ERL_NIF_TERM 620 | ecdsa_recover_compact(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 621 | { 622 | ERL_NIF_TERM r; 623 | ErlNifBinary message, csignature; 624 | int result, compressed; 625 | size_t pubkeylen; 626 | int recid = 0; 627 | unsigned char* finished_recpubkey_buf; 628 | secp256k1_ecdsa_recoverable_signature signature; 629 | secp256k1_pubkey recpubkey; 630 | 631 | if (!enif_inspect_binary(env, argv[0], &message)) { 632 | return enif_make_badarg(env); 633 | } 634 | 635 | if (!enif_inspect_binary(env, argv[1], &csignature)) { 636 | return enif_make_badarg(env); 637 | } 638 | 639 | if (!get_compressed_flag(env, argv[2], &compressed, &pubkeylen)) { 640 | return error_result(env, "Compression flag invalid"); 641 | } 642 | 643 | if (compressed == SECP256K1_EC_COMPRESSED) { 644 | pubkeylen = 33; 645 | } else { 646 | pubkeylen = 65; 647 | } 648 | 649 | if (!get_recid(env, argv[3], &recid)) { 650 | return error_result(env, "Recovery id invalid 0-3"); 651 | } 652 | result = secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &signature, csignature.data, recid); 653 | 654 | if (!result) { 655 | return error_result(env, "ecdsa_signature_parse_compact returned 0"); 656 | } 657 | 658 | // Now do ECDSA recovery 659 | result = secp256k1_ecdsa_recover(ctx, &recpubkey, &signature, message.data); 660 | 661 | if (!result) { 662 | return error_result(env, "ecdsa recovery problem"); 663 | } 664 | 665 | // Now serialize recpubkey based on the compression flag 666 | finished_recpubkey_buf = enif_make_new_binary(env, pubkeylen, &r); 667 | 668 | result = secp256k1_ec_pubkey_serialize(ctx, finished_recpubkey_buf, 669 | &pubkeylen, &recpubkey, compressed); 670 | 671 | if (!result) { 672 | return error_result(env, "ecdsa pubkey serialize error"); 673 | } 674 | 675 | return ok_result(env, &r); 676 | 677 | } 678 | 679 | 680 | // Utility functions 681 | 682 | 683 | // Grab the compressed atom 684 | int get_compressed_flag(ErlNifEnv* env, ERL_NIF_TERM arg, int* compressed, size_t* pubkeylen) { 685 | 686 | char compressed_atom[16]; 687 | 688 | if (!enif_get_atom(env, arg, compressed_atom, 16, ERL_NIF_LATIN1)) { 689 | return 0; 690 | } 691 | 692 | if (strcmp(compressed_atom, "compressed") == 0) { 693 | *pubkeylen = 33; 694 | *compressed = SECP256K1_EC_COMPRESSED; 695 | return 1; 696 | } else if (strcmp(compressed_atom, "uncompressed") == 0) { 697 | *pubkeylen = 65; 698 | *compressed = SECP256K1_EC_UNCOMPRESSED; 699 | return 1; 700 | } 701 | 702 | return 0; 703 | 704 | } 705 | 706 | 707 | // Grab recovery id 708 | int get_recid(ErlNifEnv* env, ERL_NIF_TERM argv, int* recid) { 709 | 710 | if (!enif_get_int(env, argv, recid)) { 711 | return 0; 712 | } 713 | 714 | if (*recid >= 0 && *recid <= 3) { 715 | return 1; 716 | } 717 | 718 | return 0; 719 | 720 | } 721 | 722 | // Get and validate nonce function and associated nonce 723 | int get_nonce_function(ErlNifEnv* env, ERL_NIF_TERM nonce_term, ERL_NIF_TERM nonce_data_term, secp256k1_nonce_function* noncefp, ErlNifBinary* noncedata) { 724 | char nonce_atom[32]; 725 | 726 | if (!enif_get_atom(env, nonce_term, nonce_atom, 32, ERL_NIF_LATIN1)) { 727 | return 0; 728 | } 729 | 730 | if (strcmp(nonce_atom, "default") == 0) { 731 | *noncefp = NULL; 732 | noncedata->data = NULL; 733 | noncedata->size = 0; 734 | return 1; 735 | } else if (strcmp(nonce_atom, "nonce_function_rfc6979") == 0) { 736 | *noncefp = nonce_function_rfc6979; 737 | 738 | if (!enif_inspect_binary(env, nonce_data_term, noncedata)) { 739 | return 0; 740 | } 741 | return 1; 742 | } 743 | 744 | return 0; 745 | } 746 | 747 | 748 | static ERL_NIF_TERM error_result(ErlNifEnv* env, char* error_msg) 749 | { 750 | return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_string(env, error_msg, ERL_NIF_LATIN1)); 751 | } 752 | 753 | static ERL_NIF_TERM ok_result(ErlNifEnv* env, ERL_NIF_TERM *r) 754 | { 755 | return enif_make_tuple2(env, enif_make_atom(env, "ok"), *r); 756 | } 757 | 758 | static ERL_NIF_TERM 759 | atom_from_result(ErlNifEnv* env, int res) 760 | { 761 | char* ret_string; 762 | if (res == 1) { 763 | ret_string = "ok"; 764 | } else { 765 | ret_string = "error"; 766 | } 767 | 768 | return enif_make_atom(env, ret_string); 769 | } 770 | 771 | static ErlNifFunc nif_funcs[] = { 772 | {"dsha256", 1, dsha256}, 773 | {"sha256", 1, sha256}, 774 | {"hmac_sha256", 2, hmac_sha256}, 775 | {"rand32", 0, rand32}, 776 | {"rand256", 0, rand256}, 777 | {"ec_seckey_verify", 1, ec_seckey_verify}, 778 | {"ec_pubkey_create", 2, ec_pubkey_create}, 779 | {"ec_pubkey_decompress", 1, ec_pubkey_decompress}, 780 | {"ec_pubkey_verify", 1, ec_pubkey_verify}, 781 | {"ec_privkey_export", 2, ec_privkey_export}, 782 | {"ec_privkey_import", 1, ec_privkey_import}, 783 | {"ec_privkey_tweak_add", 2, ec_privkey_tweak_add}, 784 | {"ec_pubkey_tweak_add", 2, ec_pubkey_tweak_add}, 785 | {"ec_privkey_tweak_mul", 2, ec_privkey_tweak_mul}, 786 | {"ec_pubkey_tweak_mul", 2, ec_pubkey_tweak_mul}, 787 | {"ecdsa_sign", 4, ecdsa_sign}, 788 | {"ecdsa_verify", 3, ecdsa_verify}, 789 | {"ecdsa_sign_compact", 4, ecdsa_sign_compact}, 790 | {"ecdsa_verify_compact", 3, ecdsa_verify_compact}, 791 | {"ecdsa_recover_compact", 4, ecdsa_recover_compact} 792 | }; 793 | 794 | ERL_NIF_INIT(libsecp256k1, nif_funcs, &load, NULL, &upgrade, &unload); 795 | --------------------------------------------------------------------------------