diff --git a/c_src/enacl_nif.c b/c_src/enacl_nif.c index 607c262..f5f8ad9 100644 --- a/c_src/enacl_nif.c +++ b/c_src/enacl_nif.c @@ -44,6 +44,16 @@ ERL_NIF_TERM enif_crypto_box_BOXZEROBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM return enif_make_int64(env, crypto_box_BOXZEROBYTES); } +static +ERL_NIF_TERM enif_crypto_box_PUBLICKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_box_PUBLICKEYBYTES); +} + +static +ERL_NIF_TERM enif_crypto_box_SECRETKEYBYTES(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { + return enif_make_int64(env, crypto_box_SECRETKEYBYTES); +} + static ERL_NIF_TERM enif_crypto_box_keypair(ErlNifEnv *env, int argc, ERL_NIF_TERM const argv[]) { ErlNifBinary pk, sk; @@ -237,6 +247,8 @@ static ErlNifFunc nif_funcs[] = { {"crypto_box_NONCEBYTES", 0, enif_crypto_box_NONCEBYTES}, {"crypto_box_ZEROBYTES", 0, enif_crypto_box_ZEROBYTES}, {"crypto_box_BOXZEROBYTES", 0, enif_crypto_box_BOXZEROBYTES}, + {"crypto_box_PUBLICKEYBYTES", 0, enif_crypto_box_PUBLICKEYBYTES}, + {"crypto_box_SECRETKEYBYTES", 0, enif_crypto_box_SECRETKEYBYTES}, {"crypto_box_keypair", 0, enif_crypto_box_keypair}, {"crypto_box", 4, enif_crypto_box, ERL_NIF_DIRTY_JOB_CPU_BOUND}, {"crypto_box_open", 4, enif_crypto_box_open, ERL_NIF_DIRTY_JOB_CPU_BOUND}, diff --git a/eqc_test/enacl_eqc.erl b/eqc_test/enacl_eqc.erl index 3844912..48ecb82 100644 --- a/eqc_test/enacl_eqc.erl +++ b/eqc_test/enacl_eqc.erl @@ -2,54 +2,172 @@ -include_lib("eqc/include/eqc.hrl"). -compile(export_all). -nonce() -> +nonce_good() -> Sz = enacl:box_nonce_size(), binary(Sz). + +nonce_bad() -> + Sz = enacl:box_nonce_size(), + oneof([return(a), nat(), ?SUCHTHAT(B, binary(), byte_size(B) /= Sz)]). + +nonce_valid(N) when is_binary(N) -> + Sz = enacl:box_nonce_size(), + byte_size(N) == Sz; +nonce_valid(_) -> false. + +nonce() -> + fault(nonce_bad(), nonce_good()). + +keypair_good() -> + {ok, PK, SK} = enacl:box_keypair(), + {PK, SK}. + +keypair_bad() -> + ?LET(X, elements([pk, sk]), + begin + {ok, PK, SK} = enacl:box_keypair(), + case X of + pk -> + PKBytes = enacl:box_public_key_bytes(), + {oneof([return(a), nat(), ?SUCHTHAT(B, binary(), byte_size(B) /= PKBytes)]), SK}; + sk -> + SKBytes = enacl:box_secret_key_bytes(), + {PK, oneof([return(a), nat(), ?SUCHTHAT(B, binary(), byte_size(B) /= SKBytes)])} + end + end). + +keypair() -> + fault(keypair_bad(), keypair_good()). %% CRYPTO BOX %% --------------------------- +keypair_valid(PK, SK) when is_binary(PK), is_binary(SK) -> + PKBytes = enacl:box_public_key_bytes(), + SKBytes = enacl:box_secret_key_bytes(), + byte_size(PK) == PKBytes andalso byte_size(SK) == SKBytes; +keypair_valid(_PK, _SK) -> false. + prop_box_keypair() -> ?FORALL(_X, return(dummy), - ok_box(enacl:box_keypair())). + ok_box_keypair(enacl:box_keypair())). -ok_box({ok, _PK, _SK}) -> true; -ok_box(_) -> false. +ok_box_keypair({ok, _PK, _SK}) -> true; +ok_box_keypair(_) -> false. + +box(Msg, Nonce , PK, SK) -> + try + enacl:box(Msg, Nonce, PK, SK) + catch + error:badarg -> badarg + end. + +box_open(CphText, Nonce, PK, SK) -> + try + enacl:box_open(CphText, Nonce, PK, SK) + catch + error:badarg -> badarg + end. + +failure(badarg) -> true; +failure(_) -> false. prop_box_correct() -> - ?FORALL({Msg, Nonce}, {binary(), nonce()}, + ?FORALL({Msg, Nonce, {PK1, SK1}, {PK2, SK2}}, + {binary(), + fault_rate(1, 40, nonce()), + fault_rate(1, 40, keypair()), + fault_rate(1, 40, keypair())}, begin - {ok, PK1, SK1} = enacl:box_keypair(), - {ok, PK2, SK2} = enacl:box_keypair(), - CipherText = enacl:box(Msg, Nonce, PK2, SK1), - {ok, DecodedMsg} = enacl:box_open(CipherText, Nonce, PK1, SK2), - equals(Msg, DecodedMsg) + case nonce_valid(Nonce) andalso keypair_valid(PK1, SK1) andalso keypair_valid(PK2, SK2) of + true -> + CipherText = enacl:box(Msg, Nonce, PK2, SK1), + {ok, DecodedMsg} = enacl:box_open(CipherText, Nonce, PK1, SK2), + equals(Msg, DecodedMsg); + false -> + case box(Msg, Nonce, PK2, SK1) of + badarg -> true; + Res -> failure(box_open(Res, Nonce, PK1, SK2)) + end + end end). prop_box_failure_integrity() -> - ?FORALL({Msg, Nonce}, {binary(), nonce()}, + ?FORALL({Msg, Nonce, {PK1, SK1}, {PK2, SK2}}, + {binary(), + fault_rate(1, 40, nonce()), + fault_rate(1, 40, keypair()), + fault_rate(1, 40, keypair())}, begin - {ok, PK1, SK1} = enacl:box_keypair(), - {ok, PK2, SK2} = enacl:box_keypair(), - CipherText = enacl:box(Msg, Nonce, PK2, SK1), - Err = enacl:box_open([<<"x">>, CipherText], Nonce, PK1, SK2), - equals(Err, {error, failed_verification}) + case nonce_valid(Nonce) + andalso keypair_valid(PK1, SK1) + andalso keypair_valid(PK2, SK2) of + true -> + CipherText = enacl:box(Msg, Nonce, PK2, SK1), + Err = enacl:box_open([<<"x">>, CipherText], Nonce, PK1, SK2), + equals(Err, {error, failed_verification}); + false -> + case box(Msg, Nonce, PK2, SK1) of + badarg -> true; + Res -> + failure(box_open(Res, Nonce, PK1, SK2)) + end + end end). %% CRYPTO SECRET BOX %% ------------------------------- +secret_key_good() -> + Sz = enacl:secretbox_key_size(), + binary(Sz). + +secret_key_bad() -> + oneof([return(a), + nat(), + ?SUCHTHAT(B, binary(), byte_size(B) /= enacl:secretbox_key_size())]). + secret_key() -> - Sz = enacl:secretbox_key_size(), - binary(Sz). + fault(secret_key_bad(), secret_key_good()). + +secret_key_valid(SK) when is_binary(SK) -> + Sz = enacl:secretbox_key_size(), + byte_size(SK) == Sz; +secret_key_valid(_SK) -> false. + +secretbox(Msg, Nonce, Key) -> + try + enacl:secretbox(Msg, Nonce, Key) + catch + error:badarg -> badarg + end. + +secretbox_open(Msg, Nonce, Key) -> + try + enacl:secretbox_open(Msg, Nonce, Key) + catch + error:badarg -> badarg + end. prop_secretbox_correct() -> - ?FORALL({Msg, Nonce, Key}, {binary(), nonce(), secret_key()}, + ?FORALL({Msg, Nonce, Key}, + {binary(), + fault_rate(1, 40, nonce()), + fault_rate(1, 40, secret_key())}, begin - CipherText = enacl:secretbox(Msg, Nonce, Key), - {ok, DecodedMsg} = enacl:secretbox_open(CipherText, Nonce, Key), - equals(Msg, DecodedMsg) + case nonce_valid(Nonce) andalso secret_key_valid(Key) of + true -> + CipherText = enacl:secretbox(Msg, Nonce, Key), + {ok, DecodedMsg} = enacl:secretbox_open(CipherText, Nonce, Key), + equals(Msg, DecodedMsg); + false -> + case secretbox(Msg, Nonce, Key) of + badarg -> true; + Res -> + failure(secretbox_open(Res, Nonce, Key)) + end + end end). prop_secretbox_failure_integrity() -> @@ -66,10 +184,30 @@ diff_pair(Sz) -> ?SUCHTHAT({X, Y}, {binary(Sz), binary(Sz)}, X /= Y). +data_bad() -> + oneof([return(a), nat()]). + +data_good(Sz) -> binary(Sz). + +data(Sz) -> + fault(data_bad(), data_good(Sz)). + +data_valid(B) when is_binary(B) -> true; +data_valid(_B) -> false. + prop_crypto_hash_eq() -> ?FORALL(Sz, oneof([1, 128, 1024, 1024*4]), - ?FORALL(X, binary(Sz), - equals(enacl:hash(X), enacl:hash(X)) + ?FORALL(X, data(Sz), + case data_valid(X) of + true -> equals(enacl:hash(X), enacl:hash(X)); + false -> + try + enacl:hash(X), + false + catch + error:badarg -> true + end + end )). prop_crypto_hash_neq() -> diff --git a/src/enacl.erl b/src/enacl.erl index 459f517..e1aebd8 100644 --- a/src/enacl.erl +++ b/src/enacl.erl @@ -5,7 +5,9 @@ box_keypair/0, box/4, box_open/4, - box_nonce_size/0 + box_nonce_size/0, + box_public_key_bytes/0, + box_secret_key_bytes/0 ]). %% Secret key crypto @@ -38,6 +40,12 @@ box_open(CipherText, Nonce, PK, SK) -> box_nonce_size() -> enacl_nif:crypto_box_NONCEBYTES(). +box_public_key_bytes() -> + enacl_nif:crypto_box_PUBLICKEYBYTES(). + +box_secret_key_bytes() -> + enacl_nif:crypto_box_SECRETKEYBYTES(). + secretbox(Msg, Nonce, Key) -> enacl_nif:crypto_secretbox([s_zerobytes(), Msg], Nonce, Key). diff --git a/src/enacl_nif.erl b/src/enacl_nif.erl index 7b22a44..dede21a 100644 --- a/src/enacl_nif.erl +++ b/src/enacl_nif.erl @@ -9,7 +9,9 @@ crypto_box_open/4, crypto_box_NONCEBYTES/0, crypto_box_ZEROBYTES/0, - crypto_box_BOXZEROBYTES/0 + crypto_box_BOXZEROBYTES/0, + crypto_box_PUBLICKEYBYTES/0, + crypto_box_SECRETKEYBYTES/0 ]). %% Secret key crypto @@ -45,6 +47,9 @@ not_loaded() -> crypto_box_NONCEBYTES() -> not_loaded(). crypto_box_ZEROBYTES() -> not_loaded(). crypto_box_BOXZEROBYTES() -> not_loaded(). +crypto_box_PUBLICKEYBYTES() -> not_loaded(). +crypto_box_SECRETKEYBYTES() -> not_loaded(). + crypto_box_keypair() -> not_loaded(). crypto_box(_PaddedMsg, _Nonce, _PK, _SK) -> not_loaded(). crypto_box_open(_CipherText, _Nonce, _PK, _SK) -> not_loaded().