diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index 7f59087..d558aa5 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -120,6 +120,80 @@ ERL_NIF_TERM enif_crypto_curve25519_scalarmult(ErlNifEnv *env, int argc, ERL_NIF return result; } +/* Ed 25519 */ +static +ERL_NIF_TERM enif_crypto_sign_ed25519_keypair(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary pk, sk; + + if (argc != 0) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(crypto_sign_ed25519_PUBLICKEYBYTES, &pk)) { + return nacl_error_tuple(env, "alloc_failed"); + } + + if (!enif_alloc_binary(crypto_sign_ed25519_SECRETKEYBYTES, &sk)) { + return nacl_error_tuple(env, "alloc_failed"); + } + + crypto_sign_ed25519_keypair(pk.data, sk.data); + + return enif_make_tuple2(env, enif_make_binary(env, &pk), enif_make_binary(env, &sk)); +} + +static +ERL_NIF_TERM enif_crypto_sign_ed25519_public_to_curve25519(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary curve25519_pk, ed25519_pk; + + if ((argc != 1) + || (!enif_inspect_binary(env, argv[0], &ed25519_pk)) + || (ed25519_pk.size != crypto_sign_ed25519_PUBLICKEYBYTES)) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &curve25519_pk)) { + return nacl_error_tuple(env, "alloc_failed"); + } + + if (crypto_sign_ed25519_pk_to_curve25519(curve25519_pk.data, ed25519_pk.data) != 0) { + return nacl_error_tuple(env, "ed25519_public_to_curve25519_failed"); + } + + return enif_make_binary(env, &curve25519_pk); +} + +static +ERL_NIF_TERM enif_crypto_sign_ed25519_secret_to_curve25519(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary curve25519_sk, ed25519_sk; + + if ((argc != 1) + || (!enif_inspect_binary(env, argv[0], &ed25519_sk)) + || (ed25519_sk.size != crypto_sign_ed25519_SECRETKEYBYTES)) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(crypto_scalarmult_curve25519_BYTES, &curve25519_sk)) { + return nacl_error_tuple(env, "alloc_failed"); + } + + if (crypto_sign_ed25519_sk_to_curve25519(curve25519_sk.data, ed25519_sk.data) != 0) { + return nacl_error_tuple(env, "ed25519_secret_to_curve25519_failed"); + } + + return enif_make_binary(env, &curve25519_sk); +} + +static +ERL_NIF_TERM enif_crypto_sign_ed25519_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_sign_ed25519_PUBLICKEYBYTES); +} + +static +ERL_NIF_TERM enif_crypto_sign_ed25519_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_sign_ed25519_SECRETKEYBYTES); +} + /* Public-key cryptography */ static ERL_NIF_TERM enif_crypto_box_NONCEBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { @@ -849,6 +923,12 @@ static ErlNifFunc nif_funcs[] = { {"crypto_curve25519_scalarmult", 2, enif_crypto_curve25519_scalarmult}, + {"crypto_sign_ed25519_keypair", 0, enif_crypto_sign_ed25519_keypair}, + {"crypto_sign_ed25519_public_to_curve25519", 1, enif_crypto_sign_ed25519_public_to_curve25519}, + {"crypto_sign_ed25519_secret_to_curve25519", 1, enif_crypto_sign_ed25519_secret_to_curve25519}, + {"crypto_sign_ed25519_PUBLICKEYBYTES", 0, enif_crypto_sign_ed25519_PUBLICKEYBYTES}, + {"crypto_sign_ed25519_SECRETKEYBYTES", 0, enif_crypto_sign_ed25519_SECRETKEYBYTES}, + {"randombytes_b", 1, enif_randombytes}, {"randombytes", 1, enif_randombytes, ERL_NIF_DIRTY_JOB_CPU_BOUND}, diff --git a/src/enacl.erl b/src/enacl.erl index f73eace..459d3c5 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -65,6 +65,15 @@ curve25519_scalarmult/2 ]). +%% Ed 25519. +-export([ + crypto_sign_ed25519_keypair/0, + crypto_sign_ed25519_public_to_curve25519/1, + crypto_sign_ed25519_secret_to_curve25519/1, + crypto_sign_ed25519_public_size/0, + crypto_sign_ed25519_secret_size/0 +]). + %% Low-level functions -export([ hash/1, @@ -604,6 +613,40 @@ onetime_auth_key_size() -> enacl_nif:crypto_onetimeauth_KEYBYTES(). curve25519_scalarmult(Secret, BasePoint) -> enacl_nif:crypto_curve25519_scalarmult(Secret, BasePoint). +%% Ed 25519 Crypto +%% --------------- +%% @doc crypto_sign_ed25519_keypair/0 creates a new Ed 25519 Public/Secret keypair. +%% +%% Generates and returns a new key pair for the Ed 25519 signature scheme. The return value is a +%% map in order to avoid using the public key as a secret key and vice versa. +%% @end +-spec crypto_sign_ed25519_keypair() -> #{ atom() => binary() }. +crypto_sign_ed25519_keypair() -> + {PK, SK} = enacl_nif:crypto_sign_ed25519_keypair(), + #{ public => PK, secret => SK }. + +%% @doc crypto_sign_ed25519_public_to_curve25519/1 converts a given Ed 25519 public +%% key to a Curve 25519 public key. +%% @end +-spec crypto_sign_ed25519_public_to_curve25519(PublicKey :: binary()) -> binary(). +crypto_sign_ed25519_public_to_curve25519(PublicKey) -> + enacl_nif:crypto_sign_ed25519_public_to_curve25519(PublicKey). + +%% @doc crypto_sign_ed25519_secret_to_curve25519/1 converts a given Ed 25519 secret +%% key to a Curve 25519 secret key. +%% @end +-spec crypto_sign_ed25519_secret_to_curve25519(SecretKey :: binary()) -> binary(). +crypto_sign_ed25519_secret_to_curve25519(SecretKey) -> + enacl_nif:crypto_sign_ed25519_secret_to_curve25519(SecretKey). + +-spec crypto_sign_ed25519_public_size() -> pos_integer(). +crypto_sign_ed25519_public_size() -> + enacl_nif:crypto_sign_ed25519_PUBLICKEYBYTES(). + +-spec crypto_sign_ed25519_secret_size() -> pos_integer(). +crypto_sign_ed25519_secret_size() -> + enacl_nif:crypto_sign_ed25519_SECRETKEYBYTES(). + %% Obtaining random bytes %% @doc randombytes/1 produces a stream of random bytes of the given size diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index e8c4e2e..7748856 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -76,6 +76,15 @@ crypto_curve25519_scalarmult/2 ]). +%% Ed 25519 +-export([ + crypto_sign_ed25519_keypair/0, + crypto_sign_ed25519_public_to_curve25519/1, + crypto_sign_ed25519_secret_to_curve25519/1, + crypto_sign_ed25519_PUBLICKEYBYTES/0, + crypto_sign_ed25519_SECRETKEYBYTES/0 +]). + %% Miscellaneous helper functions -export([ crypto_hash/1, @@ -168,6 +177,12 @@ crypto_onetimeauth_verify_b(_Authenticator, _Msg, _Key) -> erlang:nif_error(nif_ crypto_curve25519_scalarmult(_Secret, _BasePoint) -> erlang:nif_error(nif_not_loaded). +crypto_sign_ed25519_keypair() -> erlang:nif_error(nif_not_loaded). +crypto_sign_ed25519_public_to_curve25519(_PublicKey) -> erlang:nif_error(nif_not_loaded). +crypto_sign_ed25519_secret_to_curve25519(_SecretKey) -> erlang:nif_error(nif_not_loaded). +crypto_sign_ed25519_PUBLICKEYBYTES() -> erlang:nif_error(nif_not_loaded). +crypto_sign_ed25519_SECRETKEYBYTES() -> erlang:nif_error(nif_not_loaded). + crypto_hash(Input) when is_binary(Input) -> erlang:nif_error(nif_not_loaded). crypto_hash_b(Input) when is_binary(Input) -> erlang:nif_error(nif_not_loaded). crypto_verify_16(_X, _Y) -> erlang:nif_error(nif_not_loaded).