101 lines
2.4 KiB
C
101 lines
2.4 KiB
C
// Very specific NIF just for key recovery.
|
|
// Partly from https://github.com/mbrix/libsecp256k1
|
|
|
|
#include "erl_nif.h"
|
|
#include "secp256k1_recovery.h"
|
|
|
|
|
|
static secp256k1_context *ctx = NULL;
|
|
|
|
static ERL_NIF_TERM error_result(ErlNifEnv* env, char* error_msg)
|
|
{
|
|
return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_string(env, error_msg, ERL_NIF_LATIN1));
|
|
}
|
|
|
|
static ERL_NIF_TERM ok_result(ErlNifEnv* env, ERL_NIF_TERM *r)
|
|
{
|
|
return enif_make_tuple2(env, enif_make_atom(env, "ok"), *r);
|
|
}
|
|
|
|
static ERL_NIF_TERM
|
|
recover(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
|
{
|
|
ERL_NIF_TERM r;
|
|
ErlNifBinary message, csignature;
|
|
int result;
|
|
int compressed = SECP256K1_EC_UNCOMPRESSED;
|
|
size_t pubkeylen = 65;
|
|
int recid;
|
|
unsigned char* finished_recpubkey_buf;
|
|
secp256k1_ecdsa_recoverable_signature signature;
|
|
secp256k1_pubkey recpubkey;
|
|
|
|
if (!enif_inspect_binary(env, argv[0], &message)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_inspect_binary(env, argv[1], &csignature)) {
|
|
return enif_make_badarg(env);
|
|
}
|
|
|
|
if (!enif_get_int(env, argv[2], &recid)) {
|
|
return error_result(env, "Recovery id invalid not integer 0-3");
|
|
}
|
|
|
|
if (recid < 0 || recid > 3) {
|
|
error_result(env, "Recovery id invalid 0-3");
|
|
}
|
|
|
|
result = secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &signature, csignature.data, recid);
|
|
if (!result) {
|
|
return error_result(env, "ecdsa_signature_parse_compact returned 0");
|
|
}
|
|
|
|
// Now do ECDSA recovery
|
|
result = secp256k1_ecdsa_recover(ctx, &recpubkey, &signature, message.data);
|
|
|
|
if (!result) {
|
|
return error_result(env, "ecdsa recovery problem");
|
|
}
|
|
|
|
// Now serialize recpubkey
|
|
finished_recpubkey_buf = enif_make_new_binary(env, pubkeylen, &r);
|
|
|
|
result = secp256k1_ec_pubkey_serialize(ctx, finished_recpubkey_buf,
|
|
&pubkeylen, &recpubkey, compressed);
|
|
|
|
if (!result) {
|
|
return error_result(env, "ecdsa pubkey serialize error");
|
|
}
|
|
|
|
return ok_result(env, &r);
|
|
|
|
}
|
|
|
|
|
|
static int
|
|
load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info)
|
|
{
|
|
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM load_info)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
unload(ErlNifEnv* env, void* priv)
|
|
{
|
|
secp256k1_context_destroy(ctx);
|
|
return;
|
|
}
|
|
|
|
static ErlNifFunc nif_funcs[] =
|
|
{
|
|
{"recover", 3, recover}
|
|
};
|
|
|
|
ERL_NIF_INIT(ecrecover, nif_funcs, &load, NULL, NULL, &unload); |