reduction handling, iodata() conversion.

This commit is contained in:
Jesper Louis Andersen 2014-11-29 16:45:58 +01:00
parent d9dbabf736
commit 90509026ce
3 changed files with 126 additions and 62 deletions

View File

@ -39,7 +39,7 @@ static
ERL_NIF_TERM enif_crypto_verify_16(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { ERL_NIF_TERM enif_crypto_verify_16(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) {
ErlNifBinary x,y; 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))) { || (!enif_inspect_iolist_as_binary(env, argv[1], &y))) {
return enif_make_badarg(env); return enif_make_badarg(env);
} }

View File

@ -1,7 +1,7 @@
{application, enacl, {application, enacl,
[ [
{description, "Erlang NaCl bindings"}, {description, "Erlang NaCl bindings"},
{vsn, "0.0.1"}, {vsn, "0.9.0"},
{registered, []}, {registered, []},
{applications, [kernel, stdlib]}, {applications, [kernel, stdlib]},
{env, []} {env, []}

View File

@ -68,12 +68,23 @@
]). ]).
%% Definitions of system budgets %% 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_SIZE, 32 * 1024).
-define(HASH_REDUCTIONS, 2 * 200). -define(HASH_REDUCTIONS, 104 * 2 * 2).
-define(BOX_SIZE, 32 * 1024). -define(BOX_SIZE, 32 * 1024).
-define(BOX_REDUCTIONS, 2 * 250). -define(BOX_REDUCTIONS, 115 *2 *2).
-define(SIGN_SIZE, 16 * 1024). -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 %% Count reductions and number of scheduler yields for Fun. Fun is assumed
%% to be one of the above exor variants. %% to be one of the above exor variants.
@ -96,39 +107,41 @@ reds(Fun) ->
%% ----------------- %% -----------------
%% @doc hash/1 hashes data into a cryptographically secure checksum. %% @doc hash/1 hashes data into a cryptographically secure checksum.
%% <p>Given a binary, `Data' of any size, run a cryptographically secure hash algorithm to %% <p>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 %% 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.</p> %% since the checksum have the properties of cryptographic hashes in general.</p>
%% <p>The currently selected primitive (Nov. 2014) is SHA-512</p> %% <p>The currently selected primitive (Nov. 2014) is SHA-512</p>
%% @end %% @end
-spec hash(Data) -> Checksum -spec hash(Data) -> Checksum
when Data :: binary(), when Data :: iodata(),
Checksum :: binary(). 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) -> 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
%% <p>A subtle problem in cryptographic software are timing attacks where an attacker exploits %% <p>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 %% 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 %% 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 %% string to use. The verify_16/2 call will check two 16 byte strings for equality while
%% guaranteeing the equality operation is constant time.</p> %% guaranteeing the equality operation is constant time.</p>
%% <p>If the strings are not exactly 16 bytes, the comparison function will fail with badarg.</p> %% <p>If the strings are not exactly 16 bytes, the comparison function will fail with badarg.</p>
%% <p>The functions take binary() values and not iolist() values since the latter would convert in non-constant time</p>
%% <p>Verification returns a boolean. `true' if the strings match, `false' otherwise.</p> %% <p>Verification returns a boolean. `true' if the strings match, `false' otherwise.</p>
%% @end %% @end
-spec verify_16(binary(), binary()) -> boolean(). -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 %% @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. %% This function works as {@link verify_16/2} but does so on 32 byte strings. Same caveats apply.
%% @end %% @end
-spec verify_32(binary(), binary()) -> boolean(). -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 %% Public Key Crypto
%% --------------------- %% ---------------------
@ -146,39 +159,43 @@ box_keypair() ->
%% authenticate yourself. Requires a `Nonce` in addition. Returns the ciphered message. %% authenticate yourself. Requires a `Nonce` in addition. Returns the ciphered message.
%% @end %% @end
-spec box(Msg, Nonce, PK, SK) -> CipherText -spec box(Msg, Nonce, PK, SK) -> CipherText
when Msg :: binary(), when Msg :: iodata(),
Nonce :: binary(), Nonce :: binary(),
PK :: binary(), PK :: binary(),
SK :: binary(), SK :: binary(),
CipherText :: 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) -> 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. %% @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 %% 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 %% key `SK`. Also requires the same nonce as was used by the other party. Returns the plaintext
%% message. %% message.
-spec box_open(CipherText, Nonce, PK, SK) -> Msg -spec box_open(CipherText, Nonce, PK, SK) -> Msg
when CipherText :: binary(), when CipherText :: iodata(),
Nonce :: binary(), Nonce :: binary(),
PK :: binary(), PK :: binary(),
SK :: binary(), SK :: binary(),
Msg :: 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) -> box_open(CipherText, Nonce, PK, SK) ->
case enacl_nif:crypto_box_open([p_box_zerobytes(), CipherText], Nonce, PK, SK) of case iolist_size(CipherText) of
{error, Err} -> {error, Err}; K when K =< ?BOX_SIZE ->
Bin when is_binary(Bin) -> {ok, Bin} 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. end.
%% @doc box_nonce_size/0 return the byte-size of the nonce %% @doc box_nonce_size/0 return the byte-size of the nonce
@ -217,36 +234,40 @@ sign_keypair() ->
%% @end %% @end
-spec sign(M, SK) -> SM -spec sign(M, SK) -> SM
when when
M :: binary(), M :: iodata(),
SK :: binary(), SK :: binary(),
SM :: 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) -> 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 %% @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 %% Given a signed message `SM' and a public key `PK', verify that the message has the
%% `{ok, M}' or `{error, failed_verification}' depending on the correctness of the signature. %% right signature. Returns either `{ok, M}' or `{error, failed_verification}' depending
%% on the correctness of the signature.
%% @end %% @end
-spec sign_open(SM, PK) -> {ok, M} | {error, failed_verification} -spec sign_open(SM, PK) -> {ok, M} | {error, failed_verification}
when when
SM :: binary(), SM :: iodata(),
PK :: binary(), PK :: binary(),
M :: 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) -> sign_open(SM, PK) ->
case enacl_nif:crypto_sign_open(SM, PK) of case iolist_size(SM) of
M when is_binary(M) -> {ok, M}; K when K =< ?SIGN_SIZE ->
{error, Err} -> {error, Err} 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. end.
%% @private %% @private
@ -254,15 +275,53 @@ sign_open(SM, PK) ->
box_secret_key_bytes() -> box_secret_key_bytes() ->
enacl_nif:crypto_box_SECRETKEYBYTES(). 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) -> secretbox(Msg, Nonce, Key) ->
enacl_nif:crypto_secretbox([s_zerobytes(), Msg], Nonce, Key). case iolist_size(Msg) of
K when K =< ?SECRETBOX_SIZE ->
secretbox_open(CipherText, Nonce, Key) -> bump(enacl_nif:crypto_secretbox_b([s_zerobytes(), Msg], Nonce, Key),
case enacl_nif:crypto_secretbox_open([s_box_zerobytes(), CipherText], Nonce, Key) of ?SECRETBOX_REDUCTIONS,
{error, Err} -> {error, Err}; ?SECRETBOX_SIZE,
Bin when is_binary(Bin) -> {ok, Bin} K);
_ ->
enacl_nif:crypto_secretbox([s_zerobytes(), Msg], Nonce, Key)
end. 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() -> secretbox_nonce_size() ->
enacl_nif:crypto_secretbox_NONCEBYTES(). enacl_nif:crypto_secretbox_NONCEBYTES().
@ -292,6 +351,11 @@ stream_key_size() -> enacl_nif:crypto_stream_KEYBYTES().
Nonce :: binary(), Nonce :: binary(),
Key :: binary(), Key :: binary(),
CryptoStream :: 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 -> stream(Len, Nonce, Key) when is_integer(Len), Len >= 0 ->
enacl_nif:crypto_stream(Len, Nonce, Key); enacl_nif:crypto_stream(Len, Nonce, Key);
stream(_, _, _) -> error(badarg). stream(_, _, _) -> error(badarg).
@ -387,7 +451,7 @@ s_zerobytes() ->
s_box_zerobytes() -> s_box_zerobytes() ->
binary:copy(<<0>>, enacl_nif:crypto_secretbox_BOXZEROBYTES()). binary:copy(<<0>>, enacl_nif:crypto_secretbox_BOXZEROBYTES()).
bump(Budget, Max, Sz) -> bump(Res, Budget, Max, Sz) ->
Reds = (Budget * Sz) div Max, Reds = (Budget * Sz) div Max,
erlang:bump_reductions(max(1, Reds)), erlang:bump_reductions(max(1, Reds)),
ok. Res.