aboutsummaryrefslogblamecommitdiffstats
path: root/lib/crypto/test/engine_SUITE.erl
blob: aac894689302359452acdea5367b4f8c3c642de3 (plain) (tree)












































                                                                           

                               

      













                                        











                                                                      














                                                                   


                                  






                                                       























































































































































































































































                                                                                            









































































































































                                                                                  
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2017-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(engine_SUITE).

-include_lib("common_test/include/ct.hrl").

%% Note: This directive should only be used in test suites.
-compile(export_all).

%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
suite() ->
    [{ct_hooks,[ts_install_cth]},
     {timetrap,{seconds, 10}}
    ].

all() ->
    [
     get_all_possible_methods,
     engine_load_all_methods,
     engine_load_some_methods,
     bad_arguments,
     unknown_engine,
     pre_command_fail_bad_value,
     pre_command_fail_bad_key,
     failed_engine_init,
     {group, engine_stored_key}
    ].

groups() ->
    [{engine_stored_key, [],
      [sign_verify_rsa,
       sign_verify_dsa,
       sign_verify_ecdsa,
       sign_verify_rsa_pwd,
       priv_encrypt_pub_decrypt_rsa,
       priv_encrypt_pub_decrypt_rsa_pwd,
       pub_encrypt_priv_decrypt_rsa,
       pub_encrypt_priv_decrypt_rsa_pwd
      ]}].


init_per_suite(Config) ->
    try crypto:start() of
	ok ->
            Config;
        {error,{already_started,crypto}} ->
            Config
    catch _:_ ->
	    {skip, "Crypto did not start"}
    end.
end_per_suite(_Config) ->
    ok.

%%--------------------------------------------------------------------
init_per_group(engine_stored_key, Config) ->
    case load_storage_engine(Config) of
        {ok, E} ->
            KeyDir = key_dir(Config),
            [{storage_engine,E}, {storage_dir,KeyDir} | Config];
        {error, notexist} ->
            {skip, "OTP Test engine not found"};
        {error, notsup} ->
            {skip, "Engine not supported on this OpenSSL version"};
        {error, bad_engine_id} ->
            {skip, "Dynamic Engine not supported"};
        Other ->
            ct:log("Engine load failed: ~p",[Other]),
            {fail, "Engine load failed"}
    end;
init_per_group(_Group, Config0) ->
    Config0.

end_per_group(engine_stored_key, Config) ->
    case proplists:get_value(storage_engine, Config) of
        undefined ->
            ok;
        E ->
            ok = crypto:engine_unload(E)
    end;
end_per_group(_, _) ->
    ok.

%%--------------------------------------------------------------------
init_per_testcase(_Case, Config) ->
    Config.
end_per_testcase(_Case, _Config) ->
    ok.

%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
get_all_possible_methods() ->
    [{doc, "Just fetch all possible engine methods supported."}].

get_all_possible_methods(Config) when is_list(Config) ->
    try
        List = crypto:engine_get_all_methods(),
        ct:log("crypto:engine_get_all_methods() -> ~p\n", [List]),
        ok
    catch
        error:notsup ->
            {skip, "Engine not supported on this OpenSSL version"}
    end.

engine_load_all_methods()->
    [{doc, "Use a dummy md5 engine that does not implement md5"
      "but rather returns a static binary to test that crypto:engine_load "
      "functions works."}].

engine_load_all_methods(Config) when is_list(Config) ->
    case crypto:get_test_engine() of
        {error, notexist} ->
            {skip, "OTP Test engine not found"};
        {ok, Engine} ->
            try 
                Md5Hash1 =  <<106,30,3,246,166,222,229,158,244,217,241,179,50,232,107,109>>,
                Md5Hash1 = crypto:hash(md5, "Don't panic"),
                Md5Hash2 =  <<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
                case crypto:engine_load(<<"dynamic">>,
                                        [{<<"SO_PATH">>, Engine},
                                         {<<"ID">>, <<"MD5">>},
                                         <<"LOAD">>],
                                        []) of
                    {ok, E} ->
                        case crypto:hash(md5, "Don't panic") of
                            Md5Hash1 ->
                                ct:fail(fail_to_load_still_original_engine);
                            Md5Hash2 ->
                                ok;
                            _ ->
                                ct:fail(fail_to_load_engine)
                        end,
                        ok = crypto:engine_unload(E),
                        case crypto:hash(md5, "Don't panic") of
                            Md5Hash2 ->
                                ct:fail(fail_to_unload_still_test_engine);
                            Md5Hash1 ->
                                ok;
                            _ ->
                                ct:fail(fail_to_unload_engine)
                        end;
                    {error, bad_engine_id} ->
                        {skip, "Dynamic Engine not supported"}
                end
           catch
               error:notsup ->
                  {skip, "Engine not supported on this OpenSSL version"}
           end
    end.

engine_load_some_methods()->
    [{doc, "Use a dummy md5 engine that does not implement md5"
      "but rather returns a static binary to test that crypto:engine_load "
      "functions works."}].

engine_load_some_methods(Config) when is_list(Config) ->
    case crypto:get_test_engine() of
        {error, notexist} ->
            {skip, "OTP Test engine not found"};
        {ok, Engine} ->
            try 
                Md5Hash1 =  <<106,30,3,246,166,222,229,158,244,217,241,179,50,232,107,109>>,
                Md5Hash1 = crypto:hash(md5, "Don't panic"),
                Md5Hash2 =  <<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
                EngineMethods = crypto:engine_get_all_methods() --
                    [engine_method_dh,engine_method_rand,
                     engine_method_ciphers, engine_method_store,
                     engine_method_pkey_meths, engine_method_pkey_asn1_meths],
                case crypto:engine_load(<<"dynamic">>,
                                        [{<<"SO_PATH">>, Engine},
                                         {<<"ID">>, <<"MD5">>},
                                         <<"LOAD">>],
                                        [],
                                        EngineMethods) of
                    {ok, E} ->        
                        case crypto:hash(md5, "Don't panic") of
                            Md5Hash1 ->
                                ct:fail(fail_to_load_engine_still_original);
                            Md5Hash2 ->
                                ok;
                            _ ->
                                ct:fail(fail_to_load_engine)
                        end,
                        ok = crypto:engine_unload(E),
                        case crypto:hash(md5, "Don't panic") of
                            Md5Hash2 ->
                                ct:fail(fail_to_unload_still_test_engine);
                            Md5Hash1 ->
                                ok;
                            _ ->
                                ct:fail(fail_to_unload_engine)
                        end;
                    {error, bad_engine_id} ->
                    {skip, "Dynamic Engine not supported"}
                end
           catch
               error:notsup ->
                  {skip, "Engine not supported on this OpenSSL version"}
           end
    end.

%%-------------------------------------------------------------------------
%% Error cases
bad_arguments()->
    [{doc, "Test different arguments in bad format."}].

bad_arguments(Config) when is_list(Config) ->
    case crypto:get_test_engine() of
        {error, notexist} ->
            {skip, "OTP Test engine not found"};
        {ok, Engine} ->
            try 
                try
                    crypto:engine_load(fail_engine, [], [])
                catch
                    error:badarg ->
                       ok
                end,
                try
                    crypto:engine_load(<<"dynamic">>,
                                       [{<<"SO_PATH">>, Engine},
                                        1,
                                        {<<"ID">>, <<"MD5">>},
                                        <<"LOAD">>],
                                       [])
                catch
                    error:badarg ->
                       ok
                end,
                try
                    crypto:engine_load(<<"dynamic">>,
                                       [{<<"SO_PATH">>, Engine},
                                        {'ID', <<"MD5">>},
                                        <<"LOAD">>],
                                       [])
                catch
                    error:badarg ->
                       ok
                end
          catch
              error:notsup ->
                 {skip, "Engine not supported on this OpenSSL version"}
          end
    end.

unknown_engine() ->
    [{doc, "Try to load a non existent engine."}].

unknown_engine(Config) when is_list(Config) ->
    try
        {error, bad_engine_id} = crypto:engine_load(<<"fail_engine">>, [], []),
        ok
    catch
        error:notsup ->
           {skip, "Engine not supported on this OpenSSL version"}
    end.

pre_command_fail_bad_value() ->
    [{doc, "Test pre command due to bad value"}].

pre_command_fail_bad_value(Config) when is_list(Config) ->
    DataDir = unicode:characters_to_binary(code:priv_dir(crypto)),
    try
        case crypto:engine_load(<<"dynamic">>,
                                [{<<"SO_PATH">>,
                                  <<DataDir/binary, <<"/libfail_engine.so">>/binary >>},
                                 {<<"ID">>, <<"MD5">>},
                                 <<"LOAD">>],
                                []) of
            {error, ctrl_cmd_failed} ->
                ok;
            {error, bad_engine_id} ->
                {skip, "Dynamic Engine not supported"}
        end
    catch
        error:notsup ->
           {skip, "Engine not supported on this OpenSSL version"}
    end.

pre_command_fail_bad_key() ->
    [{doc, "Test pre command due to bad key"}].

pre_command_fail_bad_key(Config) when is_list(Config) ->
    try
        case crypto:get_test_engine() of
            {error, notexist} ->
                {skip, "OTP Test engine not found"};
            {ok, Engine} ->
                case crypto:engine_load(<<"dynamic">>,
                                        [{<<"SO_WRONG_PATH">>, Engine},
                                         {<<"ID">>, <<"MD5">>},
                                         <<"LOAD">>],
                                        []) of
                    {error, ctrl_cmd_failed} ->
                        ok;
                    {error, bad_engine_id} ->
                        {skip, "Dynamic Engine not supported"}
                end
        end
   catch 
       error:notsup ->
          {skip, "Engine not supported on this OpenSSL version"}
   end.

failed_engine_init()->
    [{doc, "Test failing engine init due to missed pre command"}].

failed_engine_init(Config) when is_list(Config) ->
    try
        case crypto:get_test_engine() of
            {error, notexist} ->
                {skip, "OTP Test engine not found"};
            {ok, Engine} ->
                case crypto:engine_load(<<"dynamic">>,
                                        [{<<"SO_PATH">>, Engine},
                                         {<<"ID">>, <<"MD5">>}],
                                        []) of
                    {error, add_engine_failed} ->
                        ok;
                    {error, bad_engine_id} ->
                        {skip, "Dynamic Engine not supported"}
                end
        end
   catch 
       error:notsup ->
          {skip, "Engine not supported on this OpenSSL version"}
   end.

%%%----------------------------------------------------------------
%%% Pub/priv key storage tests.  Thoose are for testing the crypto.erl
%%% support for using priv/pub keys stored in an engine.

sign_verify_rsa(Config) ->
    Priv = #{engine => engine_ref(Config),
             key_id => key_id(Config, "rsa_private_key.pem")},
    Pub  = #{engine => engine_ref(Config),
             key_id => key_id(Config, "rsa_public_key.pem")},
    sign_verify(rsa, sha, Priv, Pub).

sign_verify_dsa(Config) ->
    Priv = #{engine => engine_ref(Config),
             key_id => key_id(Config, "dsa_private_key.pem")},
    Pub  = #{engine => engine_ref(Config),
             key_id => key_id(Config, "dsa_public_key.pem")},
    sign_verify(dss, sha, Priv, Pub).

sign_verify_ecdsa(Config) ->
    Priv = #{engine => engine_ref(Config),
             key_id => key_id(Config, "ecdsa_private_key.pem")},
    Pub  = #{engine => engine_ref(Config),
             key_id => key_id(Config, "ecdsa_public_key.pem")},
    sign_verify(ecdsa, sha, Priv, Pub).

sign_verify_rsa_pwd(Config) ->
    Priv = #{engine => engine_ref(Config),
             key_id => key_id(Config, "rsa_private_key_pwd.pem"),
             password => "password"},
    Pub  = #{engine => engine_ref(Config),
             key_id => key_id(Config, "rsa_public_key_pwd.pem")},
    sign_verify(rsa, sha, Priv, Pub).

priv_encrypt_pub_decrypt_rsa(Config) ->
    Priv = #{engine => engine_ref(Config),
             key_id => key_id(Config, "rsa_private_key.pem")},
    Pub  = #{engine => engine_ref(Config),
             key_id => key_id(Config, "rsa_public_key.pem")},
    priv_enc_pub_dec(rsa, Priv, Pub,  rsa_pkcs1_padding).

priv_encrypt_pub_decrypt_rsa_pwd(Config) ->
    Priv = #{engine => engine_ref(Config),
             key_id => key_id(Config, "rsa_private_key_pwd.pem"),
             password => "password"},
    Pub  = #{engine => engine_ref(Config),
             key_id => key_id(Config, "rsa_public_key_pwd.pem")},
    priv_enc_pub_dec(rsa, Priv, Pub,  rsa_pkcs1_padding).

pub_encrypt_priv_decrypt_rsa(Config) ->
    Priv = #{engine => engine_ref(Config),
             key_id => key_id(Config, "rsa_private_key.pem")},
    Pub  = #{engine => engine_ref(Config),
             key_id => key_id(Config, "rsa_public_key.pem")},
    pub_enc_priv_dec(rsa, Pub, Priv,  rsa_pkcs1_padding).

pub_encrypt_priv_decrypt_rsa_pwd(Config) ->
    Priv = #{engine => engine_ref(Config),
             key_id => key_id(Config, "rsa_private_key.pem"),
             password => "password"},
    Pub  = #{engine => engine_ref(Config),
             key_id => key_id(Config, "rsa_public_key.pem")},
    pub_enc_priv_dec(rsa, Pub, Priv,  rsa_pkcs1_padding).

%%%================================================================
%%% Help for engine_stored_pub_priv_keys* test cases
%%%
load_storage_engine(_Config) ->
    case crypto:get_test_engine() of
        {ok, Engine} ->
            try crypto:engine_load(<<"dynamic">>,
                                   [{<<"SO_PATH">>, Engine},
                                    <<"LOAD">>],
                                   [])
            catch
                error:notsup ->
                    {error, notsup}
            end;

        {error, Error} ->
            {error, Error}
    end.


key_dir(Config) ->
    DataDir = unicode:characters_to_binary(proplists:get_value(data_dir, Config)),
    filename:join(DataDir, "pkcs8").


engine_ref(Config) ->
    proplists:get_value(storage_engine, Config).

key_id(Config, File) ->
    filename:join(proplists:get_value(storage_dir,Config), File).

pubkey_alg_supported(Alg) ->
    lists:member(Alg,
                 proplists:get_value(public_keys, crypto:supports())).


pub_enc_priv_dec(Alg, KeyEnc, KeyDec, Padding) ->
    case pubkey_alg_supported(Alg) of
        true ->
            PlainText = <<"Hej på dig">>,
            CryptoText = crypto:public_encrypt(Alg, PlainText, KeyEnc, Padding),
            case crypto:private_decrypt(Alg, CryptoText, KeyDec, Padding) of
                PlainText -> ok;
                _ -> {fail, "Encrypt-decrypt error"}
            end;
        false ->
            {skip, lists:concat([Alg," is not supported by cryptolib"])}
    end.

priv_enc_pub_dec(Alg, KeyEnc, KeyDec, Padding) ->
    case pubkey_alg_supported(Alg) of
        true ->
            PlainText = <<"Hej på dig">>,
            CryptoText = crypto:private_encrypt(Alg, PlainText, KeyEnc, Padding),
            case crypto:public_decrypt(Alg, CryptoText, KeyDec, Padding) of
                PlainText -> ok;
                _ -> {fail, "Encrypt-decrypt error"}
            end;
        false ->
            {skip, lists:concat([Alg," is not supported by cryptolib"])}
    end.

sign_verify(Alg, Sha, KeySign, KeyVerify) ->
    case pubkey_alg_supported(Alg) of
        true ->
            PlainText = <<"Hej på dig">>,
            Signature = crypto:sign(Alg, Sha, PlainText, KeySign),
            case crypto:verify(Alg, Sha, PlainText, Signature, KeyVerify) of
                true -> ok;
                _ -> {fail, "Sign-verify error"}
            end;
        false ->
            {skip, lists:concat([Alg," is not supported by cryptolib"])}
    end.