From 90509026ce4e2e285143d1b59f8cac32e6c27199 Mon Sep 17 00:00:00 2001 From: Jesper Louis Andersen Date: Sat, 29 Nov 2014 16:45:58 +0100 Subject: [PATCH] reduction handling, iodata() conversion. --- c_src/enacl_nif.c | 2 +- src/enacl.app.src | 2 +- src/enacl.erl | 184 +++++++++++++++++++++++++++++++--------------- 3 files changed, 126 insertions(+), 62 deletions(-) diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index e23f094..4a8a285 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -39,7 +39,7 @@ static ERL_NIF_TERM enif_crypto_verify_16(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { ErlNifBinary x,y; - if ((argc != 2) || (!enif_inspect_iolist_as_binary(env, argv[0], &x)) + if ((argc != 2) || (!enif_inspect_binary(env, argv[0], &x)) || (!enif_inspect_iolist_as_binary(env, argv[1], &y))) { return enif_make_badarg(env); } diff --git a/src/enacl.app.src b/src/enacl.app.src index e107520..b1232a1 100644 --- a/src/enacl.app.src +++ b/src/enacl.app.src @@ -1,7 +1,7 @@ {application, enacl, [ {description, "Erlang NaCl bindings"}, - {vsn, "0.0.1"}, + {vsn, "0.9.0"}, {registered, []}, {applications, [kernel, stdlib]}, {env, []} diff --git a/src/enacl.erl b/src/enacl.erl index db2ad8e..fe9da24 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -68,12 +68,23 @@ ]). %% Definitions of system budgets +%% To get a grip for these, call `enacl_timing:all/0' on your system. The numbers here are +%% described in the README.md file. -define(HASH_SIZE, 32 * 1024). --define(HASH_REDUCTIONS, 2 * 200). +-define(HASH_REDUCTIONS, 104 * 2 * 2). -define(BOX_SIZE, 32 * 1024). --define(BOX_REDUCTIONS, 2 * 250). +-define(BOX_REDUCTIONS, 115 *2 *2). -define(SIGN_SIZE, 16 * 1024). --define(SIGN_REDUCTIONS, 2 * 350). +-define(SIGN_REDUCTIONS, 160 * 2 *2). +-define(SECRETBOX_SIZE, 64 * 1024). +-define(SECRETBOX_REDUCTIONS, 107 * 2 * 2). +-define(SECRETBOX_OPEN_REDUCTIONS, 51 * 2 * 2). +-define(STREAM_SIZE, 128 * 1024). +-define(STREAM_REDUCTIONS, 120 * 2 * 2). +-define(AUTH_SIZE, 32 * 1024). +-define(AUTH_REDUCTIONS, 102 * 2 * 2). +-define(ONETIME_AUTH_SIZE, 128 * 1024). +-define(ONETIME_AUTH_REDUCTIONS, 105 * 2 * 2). %% Count reductions and number of scheduler yields for Fun. Fun is assumed %% to be one of the above exor variants. @@ -96,39 +107,41 @@ reds(Fun) -> %% ----------------- %% @doc hash/1 hashes data into a cryptographically secure checksum. -%%

Given a binary, `Data' of any size, run a cryptographically secure hash algorithm to +%%

Given an iodata(), `Data' of any size, run a cryptographically secure hash algorithm to %% produce a checksum of the data. This can be used to verify the integrity of a data block %% since the checksum have the properties of cryptographic hashes in general.

%%

The currently selected primitive (Nov. 2014) is SHA-512

%% @end -spec hash(Data) -> Checksum - when Data :: binary(), + when Data :: iodata(), Checksum :: binary(). -hash(Bin) when byte_size(Bin) =< ?HASH_SIZE -> - R = enacl_nif:crypto_hash_b(Bin), - bump(?HASH_REDUCTIONS, ?HASH_SIZE, byte_size(Bin)), - R; hash(Bin) -> - enacl_nif:crypto_hash(Bin). + case iolist_size(Bin) of + K when K =< ?HASH_SIZE -> bump(enacl_nif:crypto_hash_b(Bin), ?HASH_REDUCTIONS, ?HASH_SIZE, K); + _ -> enacl_nif:crypto_hash(Bin) + end. -%% @doc verify_16/2 implements constant time 16-byte string verification +%% @doc verify_16/2 implements constant time 16-byte binary() verification %%

A subtle problem in cryptographic software are timing attacks where an attacker exploits %% early exist in string verification if the strings happen to mismatch. This allows the %% attacker to time how long verification took and thus learn the structure of the desired %% string to use. The verify_16/2 call will check two 16 byte strings for equality while %% guaranteeing the equality operation is constant time.

%%

If the strings are not exactly 16 bytes, the comparison function will fail with badarg.

+%%

The functions take binary() values and not iolist() values since the latter would convert in non-constant time

%%

Verification returns a boolean. `true' if the strings match, `false' otherwise.

%% @end -spec verify_16(binary(), binary()) -> boolean(). -verify_16(X, Y) -> enacl_nif:crypto_verify_16(X, Y). +verify_16(X, Y) when is_binary(X), is_binary(Y) -> enacl_nif:crypto_verify_16(X, Y); +verify_16(_, _) -> error(badarg). -%% @doc verify_32/2 implements constant time 32-byte string verification -%% This function works as {@link verify_16/2} but does so on 32 byte strings. +%% @doc verify_32/2 implements constant time 32-byte iolist() verification +%% This function works as {@link verify_16/2} but does so on 32 byte strings. Same caveats apply. %% @end -spec verify_32(binary(), binary()) -> boolean(). -verify_32(X, Y) -> enacl_nif:crypto_verify_32(X, Y). +verify_32(X, Y) when is_binary(X), is_binary(Y) -> enacl_nif:crypto_verify_32(X, Y); +verify_32(_, _) -> error(badarg). %% Public Key Crypto %% --------------------- @@ -146,39 +159,43 @@ box_keypair() -> %% authenticate yourself. Requires a `Nonce` in addition. Returns the ciphered message. %% @end -spec box(Msg, Nonce, PK, SK) -> CipherText - when Msg :: binary(), + when Msg :: iodata(), Nonce :: binary(), PK :: binary(), SK :: binary(), CipherText :: binary(). -box(Msg, Nonce, PK, SK) when byte_size(Msg) =< ?BOX_SIZE -> - R = enacl_nif:crypto_box_b([p_zerobytes(), Msg], Nonce, PK, SK), - bump(?BOX_REDUCTIONS, ?BOX_SIZE, byte_size(Msg)), - R; box(Msg, Nonce, PK, SK) -> - enacl_nif:crypto_box([p_zerobytes(), Msg], Nonce, PK, SK). + case iolist_size(Msg) of + K when K =< ?BOX_SIZE -> + bump(enacl_nif:crypto_box_b([p_zerobytes(), Msg], Nonce, PK, SK), ?BOX_REDUCTIONS, ?BOX_SIZE, K); + _ -> + enacl_nif:crypto_box([p_zerobytes(), Msg], Nonce, PK, SK) + end. %% @doc box_open/4 decrypts+verifies a message from another party. %% Decrypt a `CipherText` into a `Msg` given the other partys public key `PK` and your secret %% key `SK`. Also requires the same nonce as was used by the other party. Returns the plaintext %% message. -spec box_open(CipherText, Nonce, PK, SK) -> Msg - when CipherText :: binary(), + when CipherText :: iodata(), Nonce :: binary(), PK :: binary(), SK :: binary(), Msg :: binary(). -box_open(CipherText, Nonce, PK, SK) when byte_size(CipherText) =< ?BOX_SIZE -> - R = case enacl_nif:crypto_box_open_b([p_box_zerobytes(), CipherText], Nonce, PK, SK) of - {error, Err} -> {error, Err}; - Bin when is_binary(Bin) -> {ok, Bin} - end, - bump(?BOX_REDUCTIONS, ?BOX_SIZE, byte_size(CipherText)), - R; box_open(CipherText, Nonce, PK, SK) -> - case enacl_nif:crypto_box_open([p_box_zerobytes(), CipherText], Nonce, PK, SK) of - {error, Err} -> {error, Err}; - Bin when is_binary(Bin) -> {ok, Bin} + case iolist_size(CipherText) of + K when K =< ?BOX_SIZE -> + R = + case enacl_nif:crypto_box_open_b([p_box_zerobytes(), CipherText], Nonce, PK, SK) of + {error, Err} -> {error, Err}; + Bin when is_binary(Bin) -> {ok, Bin} + end, + bump(R, ?BOX_REDUCTIONS, ?BOX_SIZE, K); + _ -> + case enacl_nif:crypto_box_open([p_box_zerobytes(), CipherText], Nonce, PK, SK) 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 @@ -217,36 +234,40 @@ sign_keypair() -> %% @end -spec sign(M, SK) -> SM when - M :: binary(), + M :: iodata(), SK :: binary(), SM :: binary(). -sign(M, SK) when byte_size(M) =< ?SIGN_SIZE -> - R = enacl_nif:crypto_sign_b(M, SK), - bump(?SIGN_REDUCTIONS, ?SIGN_SIZE, byte_size(M)), - R; sign(M, SK) -> - enacl_nif:crypto_sign(M, SK). + case iolist_size(M) of + K when K =< ?SIGN_SIZE -> + bump(enacl_nif:crypto_sign_b(M, SK), ?SIGN_REDUCTIONS, ?SIGN_SIZE, K); + _ -> + enacl_nif:crypto_sign(M, SK) + end. %% @doc sign_open/2 opens a digital signature -%% Given a signed message `SM' and a public key `PK', verify that the message has the right signature. Returns either -%% `{ok, M}' or `{error, failed_verification}' depending on the correctness of the signature. +%% Given a signed message `SM' and a public key `PK', verify that the message has the +%% right signature. Returns either `{ok, M}' or `{error, failed_verification}' depending +%% on the correctness of the signature. %% @end -spec sign_open(SM, PK) -> {ok, M} | {error, failed_verification} when - SM :: binary(), + SM :: iodata(), PK :: binary(), M :: binary(). -sign_open(SM, PK) when byte_size(SM) =< ?SIGN_SIZE -> - R = case enacl_nif:crypto_sign_open(SM, PK) of - M when is_binary(M) -> {ok, M}; - {error, Err} -> {error, Err} - end, - bump(?SIGN_REDUCTIONS, ?SIGN_SIZE, byte_size(SM)), - R; sign_open(SM, PK) -> - case enacl_nif:crypto_sign_open(SM, PK) of - M when is_binary(M) -> {ok, M}; - {error, Err} -> {error, Err} + case iolist_size(SM) of + K when K =< ?SIGN_SIZE -> + R = case enacl_nif:crypto_sign_open(SM, PK) of + M when is_binary(M) -> {ok, M}; + {error, Err} -> {error, Err} + end, + bump(R, ?SIGN_REDUCTIONS, ?SIGN_SIZE, byte_size(SM)); + _ -> + case enacl_nif:crypto_sign_open(SM, PK) of + M when is_binary(M) -> {ok, M}; + {error, Err} -> {error, Err} + end end. %% @private @@ -254,15 +275,53 @@ sign_open(SM, PK) -> box_secret_key_bytes() -> enacl_nif:crypto_box_SECRETKEYBYTES(). +%% @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 +%% nonce into consideration. The function returns the Box obtained from the encryption. +%% @end +-spec secretbox(Msg, Nonce, Key) -> Box + when + Msg :: iodata(), + Nonce :: binary(), + Key :: binary(), + Box :: binary(). + secretbox(Msg, Nonce, Key) -> - enacl_nif:crypto_secretbox([s_zerobytes(), Msg], Nonce, Key). - -secretbox_open(CipherText, Nonce, Key) -> - case enacl_nif:crypto_secretbox_open([s_box_zerobytes(), CipherText], Nonce, Key) of - {error, Err} -> {error, Err}; - Bin when is_binary(Bin) -> {ok, Bin} + case iolist_size(Msg) of + K when K =< ?SECRETBOX_SIZE -> + bump(enacl_nif:crypto_secretbox_b([s_zerobytes(), Msg], Nonce, Key), + ?SECRETBOX_REDUCTIONS, + ?SECRETBOX_SIZE, + K); + _ -> + enacl_nif:crypto_secretbox([s_zerobytes(), Msg], Nonce, Key) end. - +%% @doc secretbox_open/3 opens a sealed box. +%% Given a boxed `CipherText' and given we know the used `Nonce' and `Key' we can open the box +%% to obtain the `Msg` within. Returns either `{ok, Msg}' or `{error, failed_verification}'. +%% @end +-spec secretbox_open(CipherText, Nonce, Key) -> {ok, Msg} | {error, failed_verification} + when + CipherText :: iodata(), + Nonce :: binary(), + Key :: binary(), + Msg :: binary(). +secretbox_open(CipherText, Nonce, Key) -> + case iolist_size(CipherText) of + K when K =< ?SECRETBOX_SIZE -> + R = case enacl_nif:crypto_secretbox_open_b([s_box_zerobytes(), CipherText], + Nonce, Key) of + {error, Err} -> {error, Err}; + Bin when is_binary(Bin) -> {ok, Bin} + end, + bump(R, ?SECRETBOX_OPEN_REDUCTIONS, ?SECRETBOX_SIZE, K); + _ -> + case enacl_nif:crypto_secretbox_open([s_box_zerobytes(), CipherText], Nonce, Key) of + {error, Err} -> {error, Err}; + Bin when is_binary(Bin) -> {ok, Bin} + end + end. + secretbox_nonce_size() -> enacl_nif:crypto_secretbox_NONCEBYTES(). @@ -292,6 +351,11 @@ stream_key_size() -> enacl_nif:crypto_stream_KEYBYTES(). Nonce :: binary(), Key :: binary(), CryptoStream :: binary(). +stream(Len, Nonce, Key) when is_integer(Len), Len >= 0, Len =< ?STREAM_SIZE -> + bump(enacl_nif:crypto_stream_b(Len, Nonce, Key), + ?STREAM_REDUCTIONS, + ?STREAM_SIZE, + Len); stream(Len, Nonce, Key) when is_integer(Len), Len >= 0 -> enacl_nif:crypto_stream(Len, Nonce, Key); stream(_, _, _) -> error(badarg). @@ -387,7 +451,7 @@ s_zerobytes() -> s_box_zerobytes() -> binary:copy(<<0>>, enacl_nif:crypto_secretbox_BOXZEROBYTES()). -bump(Budget, Max, Sz) -> +bump(Res, Budget, Max, Sz) -> Reds = (Budget * Sz) div Max, erlang:bump_reductions(max(1, Reds)), - ok. + Res.