diff options
Diffstat (limited to 'lib/crypto')
-rw-r--r-- | lib/crypto/c_src/aead.c | 21 | ||||
-rw-r--r-- | lib/crypto/c_src/cipher.c | 12 | ||||
-rw-r--r-- | lib/crypto/doc/src/notes.xml | 58 | ||||
-rw-r--r-- | lib/crypto/test/Makefile | 2 | ||||
-rw-r--r-- | lib/crypto/test/crypto_SUITE.erl | 317 | ||||
-rw-r--r-- | lib/crypto/test/crypto_property_test_SUITE.erl | 50 | ||||
-rw-r--r-- | lib/crypto/test/property_test/crypto_ng_api.erl | 134 | ||||
-rw-r--r-- | lib/crypto/test/property_test/crypto_prop_generators.erl | 93 | ||||
-rw-r--r-- | lib/crypto/test/property_test/crypto_prop_generators.hrl | 36 | ||||
-rw-r--r-- | lib/crypto/vsn.mk | 2 |
10 files changed, 671 insertions, 54 deletions
diff --git a/lib/crypto/c_src/aead.c b/lib/crypto/c_src/aead.c index ab0e609130..6bb449a986 100644 --- a/lib/crypto/c_src/aead.c +++ b/lib/crypto/c_src/aead.c @@ -118,17 +118,19 @@ ERL_NIF_TERM aead_cipher(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {ret = EXCP_BADARG(env, "Can't set text size"); goto done;} } else #endif - { + { /* GCM_MODE or CHACHA20_POLY1305 */ + /* Set key and iv */ if (EVP_CipherInit_ex(ctx, NULL, NULL, key.data, iv.data, -1) != 1) {ret = EXCP_BADARG(env, "Can't set key or iv"); goto done;} } + /* Set the AAD */ if (EVP_CipherUpdate(ctx, NULL, &len, aad.data, (int)aad.size) != 1) {ret = EXCP_BADARG(env, "Can't set AAD"); goto done;} + /* Set the plain text and get the crypto text (or vice versa :) ) */ if ((outp = enif_make_new_binary(env, in.size, &out)) == NULL) {ret = EXCP_ERROR(env, "Can't make 'Out' binary"); goto done;} - if (EVP_CipherUpdate(ctx, outp, &len, in.data, (int)in.size) != 1) { if (encflg) @@ -141,29 +143,34 @@ ERL_NIF_TERM aead_cipher(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) if (encflg) { - if (EVP_CipherFinal_ex(ctx, outp/*+len*/, &len) != 1) + /* Finalize the encrypted text */ + if (EVP_CipherFinal_ex(ctx, outp, &len) != 1) {ret = EXCP_ERROR(env, "Encrypt error"); goto done;} + /* Get the tag */ if ((tagp = enif_make_new_binary(env, tag_len, &out_tag)) == NULL) {ret = EXCP_ERROR(env, "Can't make 'Out' binary"); goto done;} - if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_get_tag, (int)tag_len, tagp) != 1) {ret = EXCP_ERROR(env, "Can't get Tag"); goto done;} + /* Make the return value (the tuple with binary crypto text and the tag) */ ret = enif_make_tuple2(env, out, out_tag); } - else + else /* Decrypting. The plain text is already pointed to by 'out' */ { -#if defined(HAVE_GCM) - if (cipherp->flags & GCM_MODE) { +#if defined(HAVE_GCM) || defined(HAVE_CHACHA20_POLY1305) + /* Check the Tag before returning. CCM_MODE does this previously. */ + if (!(cipherp->flags & CCM_MODE)) { /* That is, CHACHA20_POLY1305 or GCM_MODE */ if (EVP_CIPHER_CTX_ctrl(ctx, cipherp->extra.aead.ctx_ctrl_set_tag, (int)tag_len, tag.data) != 1) /* Decrypt error */ {ret = atom_error; goto done;} + /* CCM dislikes EVP_DecryptFinal_ex on decrypting for pre 1.1.1, so we do it only here */ if (EVP_DecryptFinal_ex(ctx, outp+len, &len) != 1) /* Decrypt error */ {ret = atom_error; goto done;} } #endif + /* Make the return value, that is, the plain text */ ret = out; } diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c index e144a891a6..5b8835f0a9 100644 --- a/lib/crypto/c_src/cipher.c +++ b/lib/crypto/c_src/cipher.c @@ -66,13 +66,13 @@ static struct cipher_type_t cipher_types[] = {{"aes_192_cbc"}, {&EVP_aes_192_cbc}, 24, 0}, {{"aes_256_cbc"}, {&EVP_aes_256_cbc}, 32, 0}, - {{"aes_128_cfb8"}, {&EVP_aes_128_cfb8}, 16, NO_FIPS_CIPHER | AES_CFBx}, - {{"aes_192_cfb8"}, {&EVP_aes_192_cfb8}, 24, NO_FIPS_CIPHER | AES_CFBx}, - {{"aes_256_cfb8"}, {&EVP_aes_256_cfb8}, 32, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_128_cfb8"}, {&EVP_aes_128_cfb8}, 16, AES_CFBx}, + {{"aes_192_cfb8"}, {&EVP_aes_192_cfb8}, 24, AES_CFBx}, + {{"aes_256_cfb8"}, {&EVP_aes_256_cfb8}, 32, AES_CFBx}, - {{"aes_128_cfb128"}, {&EVP_aes_128_cfb128}, 16, NO_FIPS_CIPHER | AES_CFBx}, - {{"aes_192_cfb128"}, {&EVP_aes_192_cfb128}, 24, NO_FIPS_CIPHER | AES_CFBx}, - {{"aes_256_cfb128"}, {&EVP_aes_256_cfb128}, 32, NO_FIPS_CIPHER | AES_CFBx}, + {{"aes_128_cfb128"}, {&EVP_aes_128_cfb128}, 16, AES_CFBx}, + {{"aes_192_cfb128"}, {&EVP_aes_192_cfb128}, 24, AES_CFBx}, + {{"aes_256_cfb128"}, {&EVP_aes_256_cfb128}, 32, AES_CFBx}, {{"aes_128_ecb"}, {&EVP_aes_128_ecb}, 16, ECB_BUG_0_9_8L}, {{"aes_192_ecb"}, {&EVP_aes_192_ecb}, 24, ECB_BUG_0_9_8L}, diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml index e0bf845d52..e45a948a0c 100644 --- a/lib/crypto/doc/src/notes.xml +++ b/lib/crypto/doc/src/notes.xml @@ -31,6 +31,22 @@ </header> <p>This document describes the changes made to the Crypto application.</p> +<section><title>Crypto 4.6.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The AEAD tag was not previously checked on decrypt with + chacha20_poly1305</p> + <p> + Own Id: OTP-16242 Aux Id: ERL-1078 </p> + </item> + </list> + </section> + +</section> + <section><title>Crypto 4.6.1</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -256,6 +272,30 @@ </section> +<section><title>Crypto 4.4.2.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The chipers aes_cfb8 and aes_cfb128 are now using the EVP + interface. The supported key lengths are 128, 192 and 256 + bits.</p> + <p> + Own Id: OTP-16133 Aux Id: PR-2407 </p> + </item> + <item> + <p> + The chipers aes_cfb8 and aes_cfb128 are now available in + FIPS enabled mode.</p> + <p> + Own Id: OTP-16134 Aux Id: PR-2407 </p> + </item> + </list> + </section> + +</section> + <section><title>Crypto 4.4.2</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -546,6 +586,23 @@ </section> +<section><title>Crypto 4.2.2.3</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The chipers aes_cfb8 and aes_cfb128 are now using the EVP + interface. The supported key lengths are 128, 192 and 256 + bits.</p> + <p> + Own Id: OTP-16133 Aux Id: PR-2407 </p> + </item> + </list> + </section> + +</section> + <section><title>Crypto 4.2.2.1</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -2216,4 +2273,3 @@ Aux Id: seq7864</p> </p> </section> </chapter> - diff --git a/lib/crypto/test/Makefile b/lib/crypto/test/Makefile index 988d95a8bc..bc3d25585a 100644 --- a/lib/crypto/test/Makefile +++ b/lib/crypto/test/Makefile @@ -8,6 +8,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES = \ crypto_bench_SUITE \ crypto_SUITE \ + crypto_property_test_SUITE \ engine_SUITE ERL_FILES= $(MODULES:%=%.erl) @@ -80,6 +81,7 @@ release_tests_spec: $(TEST_TARGET) $(INSTALL_DATA) crypto.spec crypto_bench.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)" @tar cfh - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data property_test | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 0da70d5592..ce515a9ba0 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -64,7 +64,13 @@ all() -> {group, aes_192_gcm}, {group, aes_256_gcm}, {group, des_ede3_cbc}, - {group, des_ede3_cfb} + {group, des_ede3_cfb}, + {group, aes_128_cfb128}, + {group, aes_192_cfb128}, + {group, aes_256_cfb128}, + {group, aes_128_cfb8}, + {group, aes_192_cfb8}, + {group, aes_256_cfb8} ). -define(RETIRED_TYPE_ALIASES, @@ -78,7 +84,9 @@ all() -> {group, des3_cfb}, {group, des3_cbc}, {group, des3_cbf}, - {group, des_ede3} + {group, des_ede3}, + {group, aes_cfb128}, + {group, aes_cfb8} ). groups() -> @@ -123,15 +131,7 @@ groups() -> {group, rc4}, ?NEW_CIPHER_TYPE_SCHEMA, - {group, aes_128_cfb128}, - {group, aes_192_cfb128}, - {group, aes_256_cfb128}, - {group, aes_128_cfb8}, - {group, aes_192_cfb8}, - {group, aes_256_cfb8}, - ?RETIRED_TYPE_ALIASES, - {group, aes_cfb128}, - {group, aes_cfb8} + ?RETIRED_TYPE_ALIASES ]}, {fips, [], [ {group, no_blake2b}, @@ -163,8 +163,6 @@ groups() -> {group, no_blowfish_ecb}, {group, no_blowfish_ofb64}, - {group, no_aes_cfb128}, - {group, no_aes_cfb8}, {group, no_aes_ige256}, {group, no_des_cbc}, {group, no_des_cfb}, @@ -234,7 +232,7 @@ groups() -> {blowfish_ofb64, [], [block, api_ng, api_ng_one_shot, api_ng_tls]}, {rc4, [], [stream, api_ng, api_ng_one_shot, api_ng_tls]}, {aes_ctr, [], [stream]}, - {chacha20_poly1305, [], [aead]}, + {chacha20_poly1305, [], [aead, aead_ng, aead_bad_tag]}, {chacha20, [], [stream, api_ng, api_ng_one_shot, api_ng_tls]}, {poly1305, [], [poly1305]}, {no_poly1305, [], [no_poly1305]}, @@ -256,11 +254,19 @@ groups() -> {no_blowfish_cfb64, [], [no_support, no_block]}, {no_blowfish_ofb64, [], [no_support, no_block]}, {no_aes_ige256, [], [no_support, no_block]}, - {no_chacha20_poly1305, [], [no_support, no_aead]}, + {no_chacha20_poly1305, [], [no_support, no_aead, no_aead_ng]}, {no_chacha20, [], [no_support, no_stream_ivec]}, {no_rc2_cbc, [], [no_support, no_block]}, {no_rc4, [], [no_support, no_stream]}, - {api_errors, [], [api_errors_ecdh]}, + {api_errors, [], [api_errors_ecdh, + bad_cipher_name, + bad_generate_key_name, + bad_hash_name, + bad_hmac_name, + bad_mac_name, + bad_sign_name, + bad_verify_name + ]}, %% New cipher nameing schema {des_ede3_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls]}, @@ -271,15 +277,15 @@ groups() -> {aes_128_ctr, [], [api_ng, api_ng_one_shot, api_ng_tls]}, {aes_192_ctr, [], [api_ng, api_ng_one_shot, api_ng_tls]}, {aes_256_ctr, [], [api_ng, api_ng_one_shot, api_ng_tls]}, - {aes_128_ccm, [], [aead]}, - {aes_192_ccm, [], [aead]}, - {aes_256_ccm, [], [aead]}, + {aes_128_ccm, [], [aead, aead_ng, aead_bad_tag]}, + {aes_192_ccm, [], [aead, aead_ng, aead_bad_tag]}, + {aes_256_ccm, [], [aead, aead_ng, aead_bad_tag]}, {aes_128_ecb, [], [api_ng, api_ng_one_shot]}, {aes_192_ecb, [], [api_ng, api_ng_one_shot]}, {aes_256_ecb, [], [api_ng, api_ng_one_shot]}, - {aes_128_gcm, [], [aead]}, - {aes_192_gcm, [], [aead]}, - {aes_256_gcm, [], [aead]}, + {aes_128_gcm, [], [aead, aead_ng, aead_bad_tag]}, + {aes_192_gcm, [], [aead, aead_ng, aead_bad_tag]}, + {aes_256_gcm, [], [aead, aead_ng, aead_bad_tag]}, %% Retired aliases {aes_cbc, [], [block]}, @@ -334,21 +340,7 @@ end_per_suite(_Config) -> %%------------------------------------------------------------------- init_per_group(fips, Config) -> - FIPSConfig = [{fips, true} | Config], - case crypto:info_fips() of - enabled -> - FIPSConfig; - not_enabled -> - case crypto:enable_fips_mode(true) of - true -> - enabled = crypto:info_fips(), - FIPSConfig; - false -> - {fail, "Failed to enable FIPS mode"} - end; - not_supported -> - {skip, "FIPS mode not supported"} - end; + try_enable_fips_mode(Config); init_per_group(non_fips, Config) -> NonFIPSConfig = [{fips, false} | Config], case crypto:info_fips() of @@ -543,7 +535,7 @@ api_ng() -> api_ng(Config) when is_list(Config) -> [_|_] = Ciphers = lazy_eval(proplists:get_value(cipher, Config, [])), - lists:foreach(fun api_ng_cipher_increment/1, Ciphers). + lists:foreach(fun api_ng_cipher_increment/1, Ciphers ++ spec_0_bytes(Config)). api_ng_cipher_increment({Type, Key, PlainTexts}=_X) -> ct:log("~p",[_X]), @@ -593,12 +585,33 @@ api_ng_cipher_increment_loop(Ref, InTexts) -> end, InTexts). %%-------------------------------------------------------------------- +%% Check that crypto do not core dump on early 0.9.8 cryptolibs +spec_0_bytes(Config) -> + Type = proplists:get_value(type, Config), + #{iv_length := IVS, key_length := KS} = Spec = crypto:cipher_info(Type), + Key = <<0:KS/unit:8>>, + IV = <<0:IVS/unit:8>>, + spec_0_bytes(Type, Key, IV, Spec). + + +spec_0_bytes(chacha20_poly1305, _, _, _) -> + []; +spec_0_bytes(Type, Key, IV, #{mode := M}) when M == ccm_mode ; + M == gcm_mode -> + AAD = <<>>, + Plain = <<>>, + {_, Tag} = crypto:crypto_one_time_aead(Type, Key, IV, Plain, AAD, true), + [{Type, Key, Plain, IV, AAD, <<>>, Tag, []}]; +spec_0_bytes(Type, Key, IV, _Spec) -> + [{Type, Key, IV, <<>>, <<>>}]. + +%%-------------------------------------------------------------------- api_ng_one_shot() -> [{doc, "Test new api"}]. api_ng_one_shot(Config) when is_list(Config) -> [_|_] = Ciphers = lazy_eval(proplists:get_value(cipher, Config, [])), - lists:foreach(fun do_api_ng_one_shot/1, Ciphers). + lists:foreach(fun do_api_ng_one_shot/1, Ciphers ++ spec_0_bytes(Config)). do_api_ng_one_shot({Type, Key, PlainTexts}=_X) -> ct:log("~p",[_X]), @@ -700,6 +713,23 @@ no_aead(Config) when is_list(Config) -> notsup(fun crypto:block_decrypt/4, DecryptArgs). %%-------------------------------------------------------------------- +no_aead_ng() -> + [{doc, "Test disabled aead ciphers"}]. +no_aead_ng(Config) when is_list(Config) -> + {EncFun, EncryptArgs} = + case lazy_eval(proplists:get_value(cipher, Config)) of + [{Type, Key, PlainText, IV, AAD, CipherText, CipherTag, TagLen, _Info} | _] -> + {fun crypto:crypto_one_time_aead/7, [Type, Key, IV, PlainText, AAD, TagLen, true]}; + + [{Type, Key, PlainText, IV, AAD, CipherText, CipherTag, _Info} | _] -> + {fun crypto:crypto_one_time_aead/6, [Type, Key, IV, PlainText, AAD, true]} + end, + notsup(EncFun, EncryptArgs), + + DecryptArgs = [Type, Key, IV, CipherText, AAD, CipherTag, false], + notsup(fun crypto:crypto_one_time_aead/7, DecryptArgs). + +%%-------------------------------------------------------------------- stream() -> [{doc, "Test stream ciphers"}]. stream(Config) when is_list(Config) -> @@ -741,6 +771,40 @@ aead(Config) when is_list(Config) -> end, lists:foreach(fun aead_cipher/1, FilteredAEADs). +%%-------------------------------------------------------------------- +aead_ng(Config) when is_list(Config) -> + [_|_] = AEADs = lazy_eval(proplists:get_value(cipher, Config)), + FilteredAEADs = + case proplists:get_bool(fips, Config) of + false -> + AEADs; + true -> + %% In FIPS mode, the IV length must be at least 12 bytes. + lists:filter( + fun(Tuple) -> + IVLen = byte_size(element(4, Tuple)), + IVLen >= 12 + end, AEADs) + end, + lists:foreach(fun aead_cipher_ng/1, FilteredAEADs ++ spec_0_bytes(Config)). + +%%-------------------------------------------------------------------- +aead_bad_tag(Config) -> + [_|_] = AEADs = lazy_eval(proplists:get_value(cipher, Config)), + FilteredAEADs = + case proplists:get_bool(fips, Config) of + false -> + AEADs; + true -> + %% In FIPS mode, the IV length must be at least 12 bytes. + lists:filter( + fun(Tuple) -> + IVLen = byte_size(element(4, Tuple)), + IVLen >= 12 + end, AEADs) + end, + lists:foreach(fun aead_cipher_bad_tag/1, FilteredAEADs). + %%-------------------------------------------------------------------- sign_verify() -> [{doc, "Sign/verify digital signatures"}]. @@ -1281,6 +1345,97 @@ aead_cipher({Type, Key, PlainText, IV, AAD, CipherText, CipherTag, TagLen, Info} {got, Other1}}) end. +aead_cipher_ng({Type, Key, PlainText, IV, AAD, CipherText, CipherTag, Info}) -> + Plain = iolist_to_binary(PlainText), + case crypto:crypto_one_time_aead(Type, Key, IV, PlainText, AAD, true) of + {CipherText, CipherTag} -> + ok; + Other0 -> + ct:fail({{crypto, + block_encrypt, + [{info,Info}, {key,Key}, {pt,PlainText}, {iv,IV}, {aad,AAD}, {ct,CipherText}, {tag,CipherTag}]}, + {expected, {CipherText, CipherTag}}, + {got, Other0}}) + end, + case crypto:crypto_one_time_aead(Type, Key, IV, CipherText, AAD, CipherTag, false) of + Plain -> + ok; + Other1 -> + ct:fail({{crypto, + block_decrypt, + [{info,Info}, {key,Key}, {pt,PlainText}, {iv,IV}, {aad,AAD}, {ct,CipherText}, {tag,CipherTag}]}, + {expected, Plain}, + {got, Other1}}) + end; +aead_cipher_ng({Type, Key, PlainText, IV, AAD, CipherText, CipherTag, TagLen, Info}) -> + <<TruncatedCipherTag:TagLen/binary, _/binary>> = CipherTag, + Plain = iolist_to_binary(PlainText), + try crypto:crypto_one_time_aead(Type, Key, IV, PlainText, AAD, TagLen, true) of + {CipherText, TruncatedCipherTag} -> + ok; + Other0 -> + ct:fail({{crypto, + block_encrypt, + [{info,Info}, {key,Key}, {pt,PlainText}, {iv,IV}, {aad,AAD}, {ct,CipherText}, {tag,CipherTag}, {taglen,TagLen}]}, + {expected, {CipherText, TruncatedCipherTag}}, + {got, Other0}}) + catch + error:E -> + ct:log("~p",[{Type, Key, PlainText, IV, AAD, CipherText, CipherTag, TagLen, Info}]), + try crypto:crypto_one_time_aead(Type, Key, IV, PlainText, AAD, TagLen, true) + of + RR -> + ct:log("Works: ~p",[RR]) + catch + CC:EE -> + ct:log("~p:~p", [CC,EE]) + end, + ct:fail("~p",[E]) + end, + case crypto:crypto_one_time_aead(Type, Key, IV, CipherText, AAD, TruncatedCipherTag, false) of + Plain -> + ok; + Other1 -> + ct:fail({{crypto, + block_decrypt, + [{info,Info}, {key,Key}, {pt,PlainText}, {iv,IV}, {aad,AAD}, {ct,CipherText}, {tag,CipherTag}, + {truncated,TruncatedCipherTag}]}, + {expected, Plain}, + {got, Other1}}) + end. + +mk_bad_tag(CipherTag) -> + case <<0:(size(CipherTag))/unit:8>> of + CipherTag -> % The correct tag may happen to be a suite of zeroes + <<1:(size(CipherTag))/unit:8>>; + X -> + X + end. + +aead_cipher_bad_tag({Type, Key, PlainText, IV, AAD, CipherText, CipherTag, Info}) -> + Plain = iolist_to_binary(PlainText), + BadTag = mk_bad_tag(CipherTag), + case crypto:crypto_one_time_aead(Type, Key, IV, CipherText, AAD, BadTag, false) of + error -> + ok; + Plain -> + ct:log("~p:~p~n info: ~p~n key: ~p~n pt: ~p~n iv: ~p~n aad: ~p~n ct: ~p~n tag: ~p~n bad tag: ~p~n", + [?MODULE,?LINE,Info, Key, PlainText, IV, AAD, CipherText, CipherTag, BadTag]), + ct:fail("Didn't fail on bad tag") + end; +aead_cipher_bad_tag({Type, Key, PlainText, IV, AAD, CipherText, CipherTag, TagLen, Info}) -> + Plain = iolist_to_binary(PlainText), + <<TruncatedCipherTag:TagLen/binary, _/binary>> = CipherTag, + BadTruncatedTag = mk_bad_tag(TruncatedCipherTag), + case crypto:crypto_one_time_aead(Type, Key, IV, CipherText, AAD, BadTruncatedTag, false) of + error -> + ok; + Plain -> + ct:log("~p:~p~n info: ~p~n key: ~p~n pt: ~p~n iv: ~p~n aad: ~p~n ct: ~p~n tag: ~p~n bad tag: ~p~n", + [Info, Key, PlainText, IV, AAD, CipherText, TruncatedCipherTag, BadTruncatedTag]), + ct:fail("Didn't fail on bad tag") + end. + do_sign_verify({Type, undefined=Hash, Private, Public, Msg, Signature}) -> case crypto:sign(eddsa, Hash, Msg, [Private,Type]) of Signature -> @@ -1454,6 +1609,8 @@ notsup(Fun, Args) -> catch error:notsup -> ok; + error: {notsup, _, _} -> + ok; Class:Error -> {error, {Class, Error}} end, @@ -4157,3 +4314,85 @@ api_errors_ecdh(Config) when is_list(Config) -> Curves = [gaffel, 0, sect571r1], [_= (catch Test(O, C)) || O <- Others, C <- Curves], ok. + + +%%%----- Tests for bad algorithm name as argument +-define(chk_api_name(Call, Expect), + %% Check that we don't segfault on bad names + (fun() -> % avoid binding vars + try + Call + catch + Expect -> ok; + + Class:Reason:Stack -> + ct:log("~p:~p~n~p", [Class,Reason,Stack]), + ct:fail("Bad respons for bad name") + end + end)() + ). + +bad_cipher_name(_Config) -> + ?chk_api_name(crypto:crypto_init(foobar, <<1:128>>, true), + error:{badarg,{"api_ng.c",_Line},"Unknown cipher"}). + +bad_generate_key_name(_Config) -> + ?chk_api_name(crypto:generate_key(foobar, [1024]), + error:function_clause). + +bad_hash_name(_Config) -> + ?chk_api_name(crypto:hash_init(foobar), + error:badarg). + +bad_hmac_name(_Config) -> + ?chk_api_name(crypto:hmac(foobar, <<1:1024>>, "nothing"), + error:badarg). + +bad_mac_name(_Config) -> + ?chk_api_name(crypto:mac(foobar, <<1:1024>>, "nothing"), + error:function_clause). + +bad_sign_name(_Config) -> + ?chk_api_name(crypto:sign(rsa, foobar, "nothing", <<1:1024>>), + error:badarg), + ?chk_api_name(crypto:sign(foobar, sha, "nothing", <<1:1024>>), + error:badarg). + +bad_verify_name(_Config) -> + ?chk_api_name(crypto:verify(rsa, foobar, "nothing","nothing", <<1:1024>>), + error:badarg), + ?chk_api_name(crypto:verify(foobar, sha, "nothing", "nothing", <<1:1024>>), + error:badarg). + + +%%%---------------------------------------------------------------- +try_enable_fips_mode(Config) -> + FIPSConfig = [{fips, true} | Config], + case crypto:info_fips() of + enabled -> + FIPSConfig; + not_enabled -> + %% Erlang/crypto configured with --enable-fips + case crypto:enable_fips_mode(true) of + true -> + %% and also the cryptolib is fips enabled + enabled = crypto:info_fips(), + FIPSConfig; + false -> + try + [{_,_,Inf}] = crypto:info_lib(), + re:run(Inf, "(F|f)(I|i)(P|p)(S|s)") + of + nomatch -> + {skip, "FIPS mode not supported in cryptolib"}; + {match,_} -> + {fail, "Failed to enable FIPS mode"} + catch + _:_ -> + {fail, "Failed to check cryptolib info"} + end, + {skip, "FIPS mode not supported in cryptolib"} + end; + not_supported -> + {skip, "FIPS mode not supported"} + end. diff --git a/lib/crypto/test/crypto_property_test_SUITE.erl b/lib/crypto/test/crypto_property_test_SUITE.erl new file mode 100644 index 0000000000..75a3d4872f --- /dev/null +++ b/lib/crypto/test/crypto_property_test_SUITE.erl @@ -0,0 +1,50 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2017. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(crypto_property_test_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +all() -> [encrypt_decrypt__crypto_one_time, + prop__crypto_init_update + ]. + +%%% First prepare Config and compile the property tests for the found tool: +init_per_suite(Config) -> + ct_property_test:init_per_suite(Config). + +end_per_suite(Config) -> + Config. + +%%%================================================================ +%%% Test suites +%%% +encrypt_decrypt__crypto_one_time(Config) -> + ct_property_test:quickcheck( + crypto_ng_api:prop__crypto_one_time(), + Config + ). +prop__crypto_init_update(Config) -> + ct_property_test:quickcheck( + crypto_ng_api:prop__crypto_init_update(), + Config + ). diff --git a/lib/crypto/test/property_test/crypto_ng_api.erl b/lib/crypto/test/property_test/crypto_ng_api.erl new file mode 100644 index 0000000000..c3a21b0804 --- /dev/null +++ b/lib/crypto/test/property_test/crypto_ng_api.erl @@ -0,0 +1,134 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2017. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(crypto_ng_api). + +-compile(export_all). + +-proptest(eqc). +-proptest([triq,proper]). + +-ifndef(EQC). +-ifndef(PROPER). +-ifndef(TRIQ). +%%-define(EQC,true). +-define(PROPER,true). +%%-define(TRIQ,true). +-endif. +-endif. +-endif. + +-ifdef(EQC). +-include_lib("eqc/include/eqc.hrl"). +-define(MOD_eqc,eqc). + +-else. +-ifdef(PROPER). +-include_lib("proper/include/proper.hrl"). +-define(MOD_eqc,proper). +-else. +-ifdef(TRIQ). +-define(MOD_eqc,triq). +-include_lib("triq/include/triq.hrl"). + +-endif. +-endif. +-endif. + +-include("crypto_prop_generators.hrl"). + +%%%================================================================ +%%% Properties: + +prop__crypto_one_time() -> + numtests(10000, + ?FORALL({TextPlain, Cipher, Key, IV}, ?LET(Ciph,cipher(), + {text_plain(), Ciph, key(Ciph), iv(Ciph)}), + equal(TextPlain, + full_blocks(TextPlain, Cipher), + decrypt_encrypt_one_time(Cipher, Key, IV, TextPlain)) + ) + ). + +prop__crypto_init_update() -> + numtests(10000, + ?FORALL({TextPlain, Cipher, Key, IV}, ?LET(Ciph,cipher(), + {text_plain(), Ciph, key(Ciph), iv(Ciph)}), + equal(TextPlain, + full_blocks(TextPlain, Cipher), + decrypt_encrypt_init_update(Cipher, Key, IV, TextPlain)) + ) + ). + +%%%================================================================ +%%% Lib + +equal(_, T, T) -> true; +equal(F, Tp, Td) -> + ct:pal("Full: ~p~n" + "Block: ~p~n" + "Decr: ~p~n", + [F, Tp, Td]), + false. + + +decrypt_encrypt_one_time(Cipher, Key, IV, TextPlain) -> + io:format("~p:~p Cipher: ~p, BlockSize: ~p, Key: ~p, IV: ~p, TextPlain: ~p (~p chunks)", + [?MODULE,?LINE, Cipher, block_size(Cipher), size(Key), size(IV), size(iolist_to_binary(TextPlain)), + num_chunks(TextPlain)]), + TextCrypto = crypto:crypto_one_time(Cipher, Key, IV, TextPlain, true), + io:format("~p:~p TextCrypto: ~p", [?MODULE,?LINE, size(TextCrypto)]), + TextDecrypt = crypto:crypto_one_time(Cipher, Key, IV, TextCrypto, false), + io:format("~p:~p TextDecrypt: ~p", [?MODULE,?LINE, size(TextDecrypt)]), + TextDecrypt. + + +decrypt_encrypt_init_update(Cipher, Key, IV, TextPlain) when is_binary(TextPlain) -> + decrypt_encrypt_init_update(Cipher, Key, IV, [TextPlain]); + +decrypt_encrypt_init_update(Cipher, Key, IV, TextPlain) -> + io:format("~p:~p Cipher: ~p, BlockSize: ~p, Key: ~p, IV: ~p, TextPlain: ~p (~p chunks)", + [?MODULE,?LINE, Cipher, block_size(Cipher), size(Key), size(IV), size(iolist_to_binary(TextPlain)), + num_chunks(TextPlain)]), + Cenc = crypto:crypto_init(Cipher, Key, IV, true), + TextOut = lists:foldl(fun(TextIn, TextOutAcc) -> + [crypto:crypto_update(Cenc,TextIn) | TextOutAcc] + end, [], TextPlain), + TextCrypto = lists:reverse(TextOut), + io:format("~p:~p TextCrypto: ~p", + [?MODULE,?LINE, size(iolist_to_binary(TextCrypto))]), + + Cdec = crypto:crypto_init(Cipher, Key, IV, false), + TextDec = lists:foldl(fun(TextC, TextDecAcc) -> + [crypto:crypto_update(Cdec,TextC) | TextDecAcc] + end, [], TextCrypto), + iolist_to_binary(lists:reverse(TextDec)). + +full_blocks(TextPlain, Cipher) -> + TextPlainBin = iolist_to_binary(TextPlain), + {Head,_Tail} = split_binary(TextPlainBin, (size(TextPlainBin) - num_rest_bytes(TextPlainBin,Cipher))), + Head. + +num_chunks(B) when is_binary(B) -> 1; +num_chunks(L) when is_list(L) -> length(L). + +num_rest_bytes(Bin, Cipher) -> size(Bin) rem block_size(Cipher). + diff --git a/lib/crypto/test/property_test/crypto_prop_generators.erl b/lib/crypto/test/property_test/crypto_prop_generators.erl new file mode 100644 index 0000000000..5a53a000f0 --- /dev/null +++ b/lib/crypto/test/property_test/crypto_prop_generators.erl @@ -0,0 +1,93 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2017. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(crypto_prop_generators). + +-compile(export_all). + +-proptest(eqc). +-proptest([triq,proper]). + +-ifndef(EQC). +-ifndef(PROPER). +-ifndef(TRIQ). +%%-define(EQC,true). +-define(PROPER,true). +%%-define(TRIQ,true). +-endif. +-endif. +-endif. + +-ifdef(EQC). +-include_lib("eqc/include/eqc.hrl"). +-define(MOD_eqc,eqc). + +-else. +-ifdef(PROPER). +-include_lib("proper/include/proper.hrl"). +-define(MOD_eqc,proper). +-else. +-ifdef(TRIQ). +-define(MOD_eqc,triq). +-include_lib("triq/include/triq.hrl"). + +-endif. +-endif. +-endif. + +%%%================================================================ +%%% Generators + +text_plain() -> iolist(). + +cipher() -> oneof( non_aead_ciphers() -- [aes_ige256] ). + +key(Cipher) -> + %% Can't be shrinked + crypto:strong_rand_bytes( key_length(Cipher) ). + +iv(Cipher) -> + %% Can't be shrinked + crypto:strong_rand_bytes( iv_length(Cipher) ). + +iolist() -> frequency([{5, list( oneof([list(byte()), + binary(), + list(binary())]))}, + {1, mybinary(50000)} + ]). + +mybinary(MaxSize) -> ?LET(Sz, integer(0,MaxSize), binary(Sz)). + + +%%%================================================================ +non_aead_ciphers() -> + [C || C <- crypto:supports(ciphers), + C =/= chacha20_poly1305, + begin + #{mode := Mode} = crypto:cipher_info(C), + not lists:member(Mode, [ccm_mode, gcm_mode]) + end]. + +block_size(Cipher) -> maps:get(block_size, crypto:cipher_info(Cipher)). + +key_length(Cipher) -> maps:get(key_length, crypto:cipher_info(Cipher)). + +iv_length(Cipher) -> maps:get(iv_length, crypto:cipher_info(Cipher)). diff --git a/lib/crypto/test/property_test/crypto_prop_generators.hrl b/lib/crypto/test/property_test/crypto_prop_generators.hrl new file mode 100644 index 0000000000..56a762e651 --- /dev/null +++ b/lib/crypto/test/property_test/crypto_prop_generators.hrl @@ -0,0 +1,36 @@ +%%% +%%% %CopyrightBegin% +%%% +%%% Copyright Ericsson AB 2004-2017. All Rights Reserved. +%%% +%%% Licensed under the Apache License, Version 2.0 (the "License"); +%%% you may not use this file except in compliance with the License. +%%% You may obtain a copy of the License at +%%% +%%% http://www.apache.org/licenses/LICENSE-2.0 +%%% +%%% Unless required by applicable law or agreed to in writing, software +%%% distributed under the License is distributed on an "AS IS" BASIS, +%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%%% See the License for the specific language governing permissions and +%%% limitations under the License. +%%% +%%% %CopyrightEnd% +%%% +%%% + + +-import(crypto_prop_generators, + [ + text_plain/0, + cipher/0, + key/1, + iv/1, + iolist/0, + mybinary/1, + + non_aead_ciphers/0, + block_size/1, + key_length/1, + iv_length/1 + ]). diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk index c0ff0c6512..1ecffa37c2 100644 --- a/lib/crypto/vsn.mk +++ b/lib/crypto/vsn.mk @@ -1 +1 @@ -CRYPTO_VSN = 4.6.1 +CRYPTO_VSN = 4.6.2 |