From f444d1e4ac3dbdb7d950e1058adb31370fdff756 Mon Sep 17 00:00:00 2001
From: Jesper Louis Andersen <jesper.louis.andersen@gmail.com>
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).
 
-- 
GitLab