From 7d64a4b224e6f477ec8ae59f155dbf9a2ce51e54 Mon Sep 17 00:00:00 2001 From: Faried Nawaz Date: Thu, 9 Sep 2021 19:28:57 +0500 Subject: [PATCH 1/2] add ec_pubkey_compress -- the reverse of ec_pubkey_decompress --- c_src/libsecp256k1_nif.c | 32 ++++++++++++++++++++++++++++++++ src/libsecp256k1.erl | 5 +++++ 2 files changed, 37 insertions(+) diff --git a/c_src/libsecp256k1_nif.c b/c_src/libsecp256k1_nif.c index 3469703..f00f51c 100644 --- a/c_src/libsecp256k1_nif.c +++ b/c_src/libsecp256k1_nif.c @@ -213,6 +213,37 @@ ec_pubkey_create(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) } +static ERL_NIF_TERM +ec_pubkey_compress(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + + ErlNifBinary pubkey; + ERL_NIF_TERM r; + unsigned char* compressedkey; + size_t compressedkeylen = 33; + secp256k1_pubkey pubkeyt; + + if (!enif_inspect_binary(env, argv[0], &pubkey)) { + return enif_make_badarg(env); + } + + if ((pubkey.size != 33) && (pubkey.size != 65)) { + return error_result(env, "Public key size != 33 or 65 bytes"); + } + + compressedkey = enif_make_new_binary(env, compressedkeylen, &r); + + if (secp256k1_ec_pubkey_parse(ctx, &pubkeyt, pubkey.data, pubkey.size) != 1) { + return error_result(env, "Public key parse error"); + } + + if (secp256k1_ec_pubkey_serialize(ctx, compressedkey, &compressedkeylen, + &pubkeyt, SECP256K1_EC_COMPRESSED) != 1) { + return error_result(env, "Public key compression error"); + } + + return ok_result(env, &r); +} static ERL_NIF_TERM ec_pubkey_decompress(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) @@ -776,6 +807,7 @@ static ErlNifFunc nif_funcs[] = { {"rand256", 0, rand256}, {"ec_seckey_verify", 1, ec_seckey_verify}, {"ec_pubkey_create", 2, ec_pubkey_create}, + {"ec_pubkey_compress", 1, ec_pubkey_compress}, {"ec_pubkey_decompress", 1, ec_pubkey_decompress}, {"ec_pubkey_verify", 1, ec_pubkey_verify}, {"ec_privkey_export", 2, ec_privkey_export}, diff --git a/src/libsecp256k1.erl b/src/libsecp256k1.erl index cef80a6..6224165 100644 --- a/src/libsecp256k1.erl +++ b/src/libsecp256k1.erl @@ -15,6 +15,7 @@ rand256/0, ec_seckey_verify/1, ec_pubkey_create/2, + ec_pubkey_compress/1, ec_pubkey_decompress/1, ec_pubkey_verify/1, ec_privkey_export/2, @@ -72,6 +73,10 @@ ec_seckey_verify(_) -> ec_pubkey_create(_, _) -> erlang:nif_error({error, not_loaded}). +-spec ec_pubkey_compress(public_key()) -> {ok, public_key()} | {error, string()}. +ec_pubkey_compress(_) -> + erlang:nif_error({error, not_loaded}). + -spec ec_pubkey_decompress(public_key()) -> {ok, public_key()} | {error, string()}. ec_pubkey_decompress(_) -> erlang:nif_error({error, not_loaded}). From 21d1b025ef070fff6b3b6154fd59c0672cab47e0 Mon Sep 17 00:00:00 2001 From: Faried Nawaz Date: Thu, 9 Sep 2021 19:29:36 +0500 Subject: [PATCH 2/2] add tests for ec_pubkey_compress and ec_pubkey_decompress --- etest/libsecp256k1_tests.erl | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/etest/libsecp256k1_tests.erl b/etest/libsecp256k1_tests.erl index c4e1469..e6b12d5 100644 --- a/etest/libsecp256k1_tests.erl +++ b/etest/libsecp256k1_tests.erl @@ -24,6 +24,26 @@ create_keys() -> ?assertEqual(ok, libsecp256k1:ec_pubkey_verify(B)), ?assertEqual(ok, libsecp256k1:ec_pubkey_verify(C)). +%% compressing a compressed pubkey should be a no-op +compress_pubkeys() -> + A = crypto:strong_rand_bytes(32), %% Should use strong_rand in production + {ok, B} = libsecp256k1:ec_pubkey_create(A, compressed), + {ok, B2} = libsecp256k1:ec_pubkey_create(A, uncompressed), + {ok, C} = libsecp256k1:ec_pubkey_compress(B), + {ok, D} = libsecp256k1:ec_pubkey_compress(B2), + ?assertEqual(B, C), + ?assertEqual(B, D). + +%% decompressing an uncompressed pubkey should be a no-op +decompress_pubkeys() -> + A = crypto:strong_rand_bytes(32), %% Should use strong_rand in production + {ok, B} = libsecp256k1:ec_pubkey_create(A, compressed), + {ok, B2} = libsecp256k1:ec_pubkey_create(A, uncompressed), + {ok, C} = libsecp256k1:ec_pubkey_decompress(B), + {ok, D} = libsecp256k1:ec_pubkey_decompress(B2), + ?assertEqual(B2, C), + ?assertEqual(B2, D). + invalid_keys() -> A = crypto:strong_rand_bytes(16), ?assertMatch({error, _Msg}, libsecp256k1:ec_pubkey_create(A, compressed)), @@ -76,12 +96,14 @@ sha256() -> ?assertEqual(DoubleHashed, libsecp256k1:sha256(libsecp256k1:sha256(A))), ?assertEqual(DoubleHashed, libsecp256k1:dsha256(A)). -secp256k1_test_() -> +secp256k1_test_() -> {setup, fun start/0, fun stop/1, [ {"Create keys", fun create_keys/0}, + {"Compress keys", fun compress_pubkeys/0}, + {"Decompress keys", fun decompress_pubkeys/0}, {"Invalid keys", fun invalid_keys/0}, {"Import export", fun import_export/0}, {"Curve tweaks", fun tweaks/0},