diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index eab9ae2..947205e 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -554,6 +554,71 @@ ERL_NIF_TERM enif_crypto_sign_verify_detached(ErlNifEnv* env, int argc, ERL_NIF_ } } +/* Sealed box functions */ + +static +ERL_NIF_TERM enif_crypto_box_SEALBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_box_SEALBYTES); +} + +static +ERL_NIF_TERM enif_crypto_box_seal(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary key, msg, ciphertext; + + if ( + (argc != 2) || + (!enif_inspect_iolist_as_binary(env, argv[0], &msg)) || + (!enif_inspect_binary(env, argv[1], &key))) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(msg.size + crypto_box_SEALBYTES, &ciphertext)) { + return nacl_error_tuple(env, "alloc_failed"); + } + + crypto_box_seal( + ciphertext.data, + msg.data, + msg.size, + key.data); + + return enif_make_binary(env, &ciphertext); +} + + +static +ERL_NIF_TERM enif_crypto_box_seal_open(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + ErlNifBinary pk, sk, ciphertext, msg; + + if ( + (argc != 3) || + (!enif_inspect_iolist_as_binary(env, argv[0], &ciphertext)) || + (!enif_inspect_binary(env, argv[1], &pk)) || + (!enif_inspect_binary(env, argv[2], &sk))) { + return enif_make_badarg(env); + } + + if (ciphertext.size <= crypto_box_SEALBYTES) { + return enif_make_badarg(env); + } + + if (!enif_alloc_binary(ciphertext.size - crypto_box_SEALBYTES, &msg)) { + return nacl_error_tuple(env, "alloc_failed"); + } + + if (crypto_box_seal_open( + msg.data, + ciphertext.data, + ciphertext.size, + pk.data, + sk.data) != 0) { + enif_release_binary(&msg); + return nacl_error_tuple(env, "failed_verification"); + } + + return enif_make_binary(env, &msg); +} + /* Secret key cryptography */ static @@ -958,6 +1023,10 @@ static ErlNifFunc nif_funcs[] = { {"crypto_sign_detached", 2, enif_crypto_sign_detached, ERL_NIF_DIRTY_JOB_CPU_BOUND}, {"crypto_sign_verify_detached", 3, enif_crypto_sign_verify_detached, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"crypto_box_SEALBYTES", 0, enif_crypto_box_SEALBYTES}, + {"crypto_box_seal", 2, enif_crypto_box_seal, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"crypto_box_seal_open", 3, enif_crypto_box_seal_open, ERL_NIF_DIRTY_JOB_CPU_BOUND}, + {"crypto_secretbox_NONCEBYTES", 0, enif_crypto_secretbox_NONCEBYTES}, {"crypto_secretbox_ZEROBYTES", 0, enif_crypto_secretbox_ZEROBYTES}, {"crypto_secretbox_BOXZEROBYTES", 0, enif_crypto_secretbox_BOXZEROBYTES}, diff --git a/src/enacl.erl b/src/enacl.erl index f2c6d1a..54e1c10 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -35,8 +35,11 @@ sign_keypair/0, sign/2, sign_open/2, - sign_detached/2, - sign_verify_detached/3 + sign_detached/2, + sign_verify_detached/3, + + seal_box/2, + seal_box_open/3 ]). %% Secret key crypto @@ -196,6 +199,7 @@ box_keypair() -> {PK, SK} = enacl_nif:crypto_box_keypair(), #{ public => PK, secret => SK}. + %% @doc box/4 encrypts+authenticates a message to another party. %% %% Encrypt a `Msg' to the party identified by public key `PK' using your own secret key `SK' to @@ -414,6 +418,33 @@ sign_verify_detached(SIG, M, PK) -> box_secret_key_bytes() -> enacl_nif:crypto_box_SECRETKEYBYTES(). +%% @doc seal_box/2 encrypts an anonymous message to another party. +%% +%% Encrypt a `Msg' to a party using his public key, `PK'. This generates an ephemeral +%% keypair and then uses `box'. Ephemeral public key will sent to other party. Returns the +%% enciphered message `SealedCipherText' which includes ephemeral public key at head. +%% @end +-spec seal_box(Msg, PK) -> SealedCipherText + when Msg :: iodata(), + PK :: binary(), + SealedCipherText :: binary(). +seal_box(Msg, PK) -> + enacl_nif:crypto_box_seal(Msg, PK). + +%% @doc seal_box_open/3 decrypts+check message integrity from an unknown sender. +%% +%% Decrypt a `SealedCipherText' which contains an ephemeral public key from another party +%% into a `Msg' using that key and your public and secret keys, `PK' and `SK'. Returns the +%% plaintext message. +%% @end +-spec seal_box_open(SealedCipherText, PK, SK) -> {ok, Msg} | {error, failed_verification} + when SealedCipherText :: iodata(), + PK :: binary(), + SK :: binary(), + Msg :: binary(). +seal_box_open(SealedCipherText, PK, SK) -> + enacl_nif:crypto_box_seal_open(SealedCipherText, PK, SK). + %% @doc secretbox/3 encrypts a message with a key %% %% Given a `Msg', a `Nonce' and a `Key' encrypt the message with the Key while taking the diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index df6796e..b86892c 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -33,8 +33,12 @@ crypto_sign_open/2, crypto_sign_open_b/2, - crypto_sign_detached/2, - crypto_sign_verify_detached/3 + crypto_sign_detached/2, + crypto_sign_verify_detached/3, + + crypto_box_seal/2, + crypto_box_seal_open/3, + crypto_box_SEALBYTES/0 ]). @@ -151,6 +155,10 @@ crypto_sign_open_b(_SignedMessage, _PK) -> erlang:nif_error(nif_not_loaded). crypto_sign_detached(_M, _SK) -> erlang:nif_error(nif_not_loaded). crypto_sign_verify_detached(_SIG, _M, _PK) -> erlang:nif_error(nif_not_loaded). +crypto_box_seal(_Msg, _PK) -> erlang:nif_error(nif_not_loaded). +crypto_box_seal_open(_CipherText, _PK, _SK) -> erlang:nif_error(nif_not_loaded). +crypto_box_SEALBYTES() -> erlang:nif_error(nif_not_loaded). + crypto_secretbox_NONCEBYTES() -> erlang:nif_error(nif_not_loaded). crypto_secretbox_ZEROBYTES() -> erlang:nif_error(nif_not_loaded). crypto_secretbox_KEYBYTES() -> erlang:nif_error(nif_not_loaded).