diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index 2333c18..afd9def 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -11,6 +11,7 @@ #include "kx.h" #include "public.h" #include "pwhash.h" +#include "kdf.h" #include "randombytes.h" #include "secret.h" #include "sign.h" @@ -293,6 +294,11 @@ static ErlNifFunc nif_funcs[] = { erl_nif_dirty_job_cpu_bound_macro("crypto_pwhash_str_verify", 2, enacl_crypto_pwhash_str_verify), + {"crypto_kdf_KEYBYTES", 0, enacl_crypto_kdf_KEYBYTES}, + {"crypto_kdf_CONTEXTBYTES", 0, enacl_crypto_kdf_CONTEXTBYTES}, + erl_nif_dirty_job_cpu_bound_macro("crypto_kdf_derive_from_key", 3, + enacl_crypto_kdf_derive_from_key), + erl_nif_dirty_job_cpu_bound_macro("crypto_curve25519_scalarmult", 2, enacl_crypto_curve25519_scalarmult), erl_nif_dirty_job_cpu_bound_macro("crypto_curve25519_scalarmult_base", 1, diff --git a/c_src/kdf.c b/c_src/kdf.c new file mode 100644 index 0000000..5290513 --- /dev/null +++ b/c_src/kdf.c @@ -0,0 +1,59 @@ +#include + +#include + +#include "enacl.h" +#include "kdf.h" + + +ERL_NIF_TERM enacl_crypto_kdf_KEYBYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_kdf_KEYBYTES); +} + +ERL_NIF_TERM enacl_crypto_kdf_CONTEXTBYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_kdf_CONTEXTBYTES); +} + + +ERL_NIF_TERM enacl_crypto_kdf_derive_from_key(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]) { + ErlNifBinary m, c, r; + uint64_t id; + + // Validate the arguments + if ((argc != 3) || + (!enif_inspect_iolist_as_binary(env, argv[0], &m)) || + (!enif_inspect_binary(env, argv[1], &c)) || + (!enif_get_uint64(env, argv[2], &id))) { + return enif_make_badarg(env); + } + + // Check Master Key length + if (m.size != crypto_kdf_KEYBYTES) { + return enif_make_badarg(env); + } + + // Check Context Key length + if (c.size != crypto_kdf_CONTEXTBYTES) { + return enif_make_badarg(env); + } + + // Allocate memory for return binary + if (!enif_alloc_binary(crypto_kdf_KEYBYTES, &r)) { + return enacl_internal_error(env); + } + + if (crypto_kdf_derive_from_key(r.data, r.size, + id, + (const char *)c.data, + m.data) != 0) { + /* out of memory */ + enif_release_binary(&r); + return enacl_internal_error(env); + } + + return enif_make_binary(env, &r); +} + diff --git a/c_src/kdf.h b/c_src/kdf.h new file mode 100644 index 0000000..56b654b --- /dev/null +++ b/c_src/kdf.h @@ -0,0 +1,15 @@ +#ifndef ENACL_KDF_H +#define ENACL_KDF_H + +#include + +ERL_NIF_TERM enacl_crypto_kdf_KEYBYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); + +ERL_NIF_TERM enacl_crypto_kdf_CONTEXTBYTES(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); + +ERL_NIF_TERM enacl_crypto_kdf_derive_from_key(ErlNifEnv *env, int argc, + ERL_NIF_TERM const argv[]); + +#endif diff --git a/src/enacl.erl b/src/enacl.erl index d2a4fff..40749e9 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -125,6 +125,13 @@ ]). +%% Key derivation +-export([ + kdf_KEYBYTES/0, + kdf_CONTEXTBYTES/0, + kdf_derive_from_key/3 +]). + %% Low-level subtle functions which are hard to get correct -export([ %% EQC @@ -456,6 +463,29 @@ null_terminate(ASCII) -> pwhash_str_verify(HashPassword, Password) -> enacl_nif:crypto_pwhash_str_verify(null_terminate(HashPassword), Password). +%% Key Derivation +%% @doc kdf_KEYBYTES/0 returns the number of bytes required for master key. +%% @end +-spec kdf_KEYBYTES() -> pos_integer(). +kdf_KEYBYTES() -> + enacl_nif:crypto_kdf_KEYBYTES(). + +%% @doc kdf_CONTEXTBYTES/0 returns the number of bytes required for context. +%% @end +-spec kdf_CONTEXTBYTES() -> pos_integer(). +kdf_CONTEXTBYTES() -> + enacl_nif:crypto_kdf_CONTEXTBYTES(). + +%% @doc kdf_derive_from_key/3 derive a key from a single high entropy key +%% @end. +-spec kdf_derive_from_key(MasterKey, Context, Id) -> binary() + when + MasterKey :: iodata(), + Context :: binary(), + Id :: pos_integer(). +kdf_derive_from_key(MasterKey, Context, Id) -> + enacl_nif:crypto_kdf_derive_from_key(MasterKey, Context, Id). + %% Public Key Crypto %% --------------------- %% @doc box_keypair/0 creates a new Public/Secret keypair. diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index f2255f8..1e1d671 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -152,6 +152,13 @@ crypto_pwhash_str_verify/2 ]). +%% Key Derivation +-export([ + crypto_kdf_KEYBYTES/0, + crypto_kdf_CONTEXTBYTES/0, + crypto_kdf_derive_from_key/3 + ]). + %% Generic hash -export([ crypto_generichash_BYTES/0, @@ -242,6 +249,10 @@ crypto_pwhash(_Password, _Salt, _Ops, _Mem) -> erlang:nif_error(nif_not_loaded). crypto_pwhash_str(_Password, _Ops, _Mem) -> erlang:nif_error(nif_not_loaded). crypto_pwhash_str_verify(_HashedPassword, _Password) -> erlang:nif_error(nif_not_loaded). +crypto_kdf_KEYBYTES() -> erlang:nif_error(nif_not_loaded). +crypto_kdf_CONTEXTBYTES() -> erlang:nif_error(nif_not_loaded). +crypto_kdf_derive_from_key(_MasterKey, _Context, _Id) -> erlang:nif_error(nif_not_loaded). + crypto_box_NONCEBYTES() -> erlang:nif_error(nif_not_loaded). crypto_box_ZEROBYTES() -> erlang:nif_error(nif_not_loaded). crypto_box_BOXZEROBYTES() -> erlang:nif_error(nif_not_loaded).