From 159e8f675056d44e6dcab300a22a84e0a88ea4ab Mon Sep 17 00:00:00 2001 From: Jesper Louis Andersen Date: Wed, 17 Dec 2014 17:12:29 +0100 Subject: [PATCH] Introduce precomputed keys API. This patch implements beforenm/afternm calls from NaCl for `box` style crypto. It's main advantage is way faster computations, since it avoids recomputing in the elliptic curve for every message. While here, bump the version to v0.11.0 as new functionality was added. --- README.md | 7 +++-- src/enacl.app.src | 2 +- src/enacl.erl | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2716dd4..a2591b9 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ or ### Features: -* Complete library for every NaCl call, save `beforenm/afternm` invocations +* Complete NaCl library, implementing all default functionality. * Implements a small set of additional functionality from libsodium. Most notably access to a proper CSPRNG random source * Tests created by aggressive use of Erlang QuickCheck. @@ -31,9 +31,12 @@ In addition, I would like to thank Steve Vinoski, Rickard Green, and Sverker Eri # Versions -## Upcoming release: +## v0.11.x + +### v0.11.0 * Introduce NIF layer beforenm/afternm calls. +* Introduce the API for precomputed keys (beforenm/afternm calls). * Use test cases which tries to inject `iodata()` rather than binaries in all places where `iodata()` tend to be accepted. * Fix type for `enacl:box_open/4`. The specification was wrong which results in errors in other applications using enacl. diff --git a/src/enacl.app.src b/src/enacl.app.src index 3394261..8a560e0 100644 --- a/src/enacl.app.src +++ b/src/enacl.app.src @@ -1,7 +1,7 @@ {application, enacl, [ {description, "Erlang NaCl bindings"}, - {vsn, "0.10.2"}, + {vsn, "0.11.0"}, {registered, []}, {applications, [kernel, stdlib]}, {env, []} diff --git a/src/enacl.erl b/src/enacl.erl index b23c943..35de4b3 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -21,9 +21,14 @@ box_keypair/0, box/4, box_open/4, + box_beforenm/2, + box_afternm/3, + box_open_afternm/3, + box_nonce_size/0, box_public_key_bytes/0, box_secret_key_bytes/0, + box_beforenm_bytes/0, sign_keypair_public_size/0, sign_keypair_secret_size/0, @@ -210,6 +215,64 @@ box_open(CipherText, Nonce, PK, SK) -> end end. +%% @doc box_beforenm/2 precomputes a box shared key for a PK/SK keypair +%% @end +-spec box_beforenm(PK, SK) -> binary() + when + PK :: binary(), + SK :: binary(). +box_beforenm(PK, SK) -> + R = enacl_nif:crypto_box_beforenm(PK, SK), + erlang:bump_reductions(?BOX_BEFORENM_REDUCTIONS), + R. + +%% @doc box_afternm/3 works like `box/4' but uses a precomputed key +%% Calling `box_afternm(M, Nonce, K)' for a precomputed key `K = box_beforenm(PK, SK)' works exactly as +%% if you had called `box(M, Nonce, PK, SK)'. Except that it avoids computations in the elliptic curve Curve25519, +%% and thus is a much faster operation. +%% @end +-spec box_afternm(Msg, Nonce, K) -> CipherText + when + Msg :: iodata(), + Nonce :: binary(), + K :: binary(), + CipherText :: binary(). +box_afternm(Msg, Nonce, Key) -> + case iolist_size(Msg) of + K when K =< ?BOX_AFTERNM_SIZE -> + bump(enacl_nif:crypto_box_afternm_b([p_zerobytes(), Msg], Nonce, Key), + ?BOX_AFTERNM_REDUCTIONS, ?BOX_AFTERNM_SIZE, K); + _ -> + enacl_nif:crypto_box_afternm([p_zerobytes(), Msg], Nonce, Key) + end. + +%% @doc box_open_afternm/3 works like `box_open/4` but uses a precomputed key +%% Calling `box_open_afternm(M, Nonce, K)' for a precomputed key `K = box_beforenm(PK, SK)' works exactly as +%% if you had called `box_open(M, Nonce, PK, SK)'. Except the operation is much faster as it avoids costly +%% computations in the elliptic curve Curve25519. +%% @end +-spec box_open_afternm(CT, Nonce, K) -> {ok, Msg} | {error, failed_verification} + when + CT :: binary(), + Nonce :: binary(), + K :: binary(), + Msg :: binary(). +box_open_afternm(CipherText, Nonce, Key) -> + case iolist_size(CipherText) of + K when K =< ?BOX_AFTERNM_SIZE -> + R = + case enacl_nif:crypto_box_open_afternm_b([p_box_zerobytes(), CipherText], Nonce, Key) of + {error, Err} -> {error, Err}; + Bin when is_binary(Bin) -> {ok, Bin} + end, + bump(R, ?BOX_AFTERNM_REDUCTIONS, ?BOX_AFTERNM_SIZE, K); + _ -> + case enacl_nif:crypto_box_open_afternm([p_box_zerobytes(), CipherText], Nonce, Key) of + {error, Err} -> {error, Err}; + Bin when is_binary(Bin) -> {ok, Bin} + end + end. + %% @doc box_nonce_size/0 return the byte-size of the nonce %% Used to obtain the size of the nonce. %% @end. @@ -222,6 +285,10 @@ box_nonce_size() -> box_public_key_bytes() -> enacl_nif:crypto_box_PUBLICKEYBYTES(). +%% @private +box_beforenm_bytes() -> + enacl_nif:crypto_box_BEFORENMBYTES(). + %% Signatures %% @private