From f444d1e4ac3dbdb7d950e1058adb31370fdff756 Mon Sep 17 00:00:00 2001 From: Jesper Louis Andersen Date: Fri, 14 Aug 2015 22:03:33 +0200 Subject: [PATCH] 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 --- c_src/enacl_nif.c | 5 +++- eqc_test/enacl_eqc.erl | 52 +++++++++++++++++++++++++++++++++++++++++- src/enacl.erl | 36 ++++++++++++++++++++--------- src/enacl_nif.erl | 4 ++++ 4 files changed, 84 insertions(+), 13 deletions(-) diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index 6c101b4..db6b0de 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -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}, diff --git a/eqc_test/enacl_eqc.erl b/eqc_test/enacl_eqc.erl index f36c2a1..b031c8f 100644 --- a/eqc_test/enacl_eqc.erl +++ b/eqc_test/enacl_eqc.erl @@ -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() -> diff --git a/src/enacl.erl b/src/enacl.erl index 1eaa094..d7f441c 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -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 %% diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index a66c9f0..a89c765 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -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).