Commit f444d1e4 authored by Jesper Louis Andersen's avatar Jesper Louis Andersen

Fix box_seal/2 and box_seal_open/3.

* Call the functions `box_seal` and `box_seal_open` to match the libsodium names in module `enacl`.
* Fix a bug in the C NIF: We should fail if the input is `<` SEALBYTES but not on `<=` SEALBYTES. The latter made it impossible to encode empty messages.
* Add variants which run directly on the interpreter scheduler for small messages.

Also:

* Provide full EQC functions for the testing purposes. This generated around 13000 random test cases in a 5 minute run, all passing.# Please enter the commit message for your changes. Lines starting
parent 4676328e
......@@ -598,7 +598,7 @@ ERL_NIF_TERM enif_crypto_box_seal_open(ErlNifEnv *env, int argc, ERL_NIF_TERM co
return enif_make_badarg(env);
}
if (ciphertext.size <= crypto_box_SEALBYTES) {
if (ciphertext.size < crypto_box_SEALBYTES) {
return enif_make_badarg(env);
}
......@@ -1027,7 +1027,10 @@ static ErlNifFunc nif_funcs[] = {
{"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_b", 2, enif_crypto_box_seal},
{"crypto_box_seal", 2, enif_crypto_box_seal, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"crypto_box_seal_open_b", 3, enif_crypto_box_seal_open},
{"crypto_box_seal_open", 3, enif_crypto_box_seal_open, ERL_NIF_DIRTY_JOB_CPU_BOUND},
{"crypto_secretbox_NONCEBYTES", 0, enif_crypto_secretbox_NONCEBYTES},
......
......@@ -129,6 +129,20 @@ box(Msg, Nonce , PK, SK) ->
error:badarg -> badarg
end.
box_seal(Msg, PK) ->
try
enacl:box_seal(Msg, PK)
catch
error:badarg -> badarg
end.
box_seal_open(Cph, PK, SK) ->
try
enacl:box_seal_open(Cph, PK, SK)
catch
error:badarg -> badarg
end.
box_open(CphText, Nonce, PK, SK) ->
try
enacl:box_open(CphText, Nonce, PK, SK)
......@@ -137,7 +151,8 @@ box_open(CphText, Nonce, PK, SK) ->
end.
failure(badarg) -> true;
failure(_) -> false.
failure({error, failed_verification}) -> true;
failure(X) -> {failure, X}.
prop_box_correct() ->
?FORALL({Msg, Nonce, {PK1, SK1}, {PK2, SK2}},
......@@ -188,6 +203,41 @@ prop_box_failure_integrity() ->
end
end
end).
prop_seal_box_failure_integrity() ->
?FORALL({Msg, {PK1, SK1}}, {fault_rate(1,40,g_iodata()), fault_rate(1,40,keypair())},
begin
case v_iodata(Msg) andalso keypair_valid(PK1, SK1) of
true ->
CT = enacl:box_seal(Msg, PK1),
Err = enacl:box_seal_open([<<"x">>, CT], PK1, SK1),
equals(Err, {error, failed_verification});
false ->
case box_seal(Msg, PK1) of
badarg -> true;
Res ->
failure(box_seal_open(Res, PK1, SK1))
end
end
end).
prop_seal_box_correct() ->
?FORALL({Msg, {PK1, SK1}},
{fault_rate(1, 40, g_iodata()),
fault_rate(1, 40, keypair())},
begin
case v_iodata(Msg) andalso keypair_valid(PK1, SK1) of
true ->
SealedCipherText = enacl:box_seal(Msg, PK1),
{ok, DecodedMsg} = enacl:box_seal_open(SealedCipherText, PK1, SK1),
equals(iolist_to_binary(Msg), DecodedMsg);
false ->
case box_seal(Msg, PK1) of
badarg -> true;
Res -> failure(box_seal_open(Res, PK1, SK1))
end
end
end).
%% PRECOMPUTATIONS
beforenm_key() ->
......
......@@ -38,8 +38,8 @@
sign_detached/2,
sign_verify_detached/3,
seal_box/2,
seal_box_open/3
box_seal/2,
box_seal_open/3
]).
%% Secret key crypto
......@@ -436,12 +436,17 @@ box_secret_key_bytes() ->
%% 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
-spec box_seal(Msg, PK) -> SealedCipherText
when Msg :: iodata(),
PK :: binary(),
SealedCipherText :: binary().
seal_box(Msg, PK) ->
enacl_nif:crypto_box_seal(Msg, PK).
box_seal(Msg, PK) ->
case iolist_size(Msg) of
K when K =< ?BOX_SIZE ->
bump(enacl_nif:crypto_box_seal_b(Msg, PK), ?BOX_REDUCTIONS, ?BOX_SIZE, K);
_ ->
enacl_nif:crypto_box_seal(Msg, PK)
end.
%% @doc seal_box_open/3 decrypts+check message integrity from an unknown sender.
%%
......@@ -449,16 +454,25 @@ seal_box(Msg, PK) ->
%% 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}
-spec box_seal_open(SealedCipherText, PK, SK) -> {ok, Msg} | {error, failed_verification}
when SealedCipherText :: iodata(),
PK :: binary(),
SK :: binary(),
Msg :: binary().
seal_box_open(SealedCipherText, PK, SK) ->
case enacl_nif:crypto_box_seal_open(SealedCipherText, PK, SK) of
{error, Err} -> {error, Err};
Bin when is_binary(Bin) -> Bin
end.
box_seal_open(SealedCipherText, PK, SK) ->
case iolist_size(SealedCipherText) of
K when K =< ?BOX_SIZE ->
R = case enacl_nif:crypto_box_seal_open_b(SealedCipherText, 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_seal_open(SealedCipherText, PK, SK) of
{error, Err} -> {error, Err};
Bin when is_binary(Bin) -> {ok, Bin}
end
end.
%% @doc secretbox/3 encrypts a message with a key
%%
......
......@@ -39,6 +39,8 @@
crypto_sign_verify_detached_b/3,
crypto_box_seal/2,
crypto_box_seal_b/2,
crypto_box_seal_open_b/3,
crypto_box_seal_open/3,
crypto_box_SEALBYTES/0
......@@ -160,7 +162,9 @@ crypto_sign_detached_b(_M, _SK) -> erlang:nif_error(nif_not_loaded).
crypto_sign_verify_detached(_Sig, _M, _PK) -> erlang:nif_error(nif_not_loaded).
crypto_sign_verify_detached_b(_Sig, _M, _PK) -> erlang:nif_error(nif_not_loaded).
crypto_box_seal_b(_Msg, _PK) -> erlang:nif_error(nif_not_loaded).
crypto_box_seal(_Msg, _PK) -> erlang:nif_error(nif_not_loaded).
crypto_box_seal_open_b(_CipherText, _PK, _SK) -> 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).
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment