-module(netconfc_test_lib). -export([get_id_keys/1, remove_id_keys/1, make_dsa_files/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("public_key/include/public_key.hrl"). %%%----------------------------------------------------------------- %%% BEGIN SSH key management %% copy private keys to given dir from ~/.ssh get_id_keys(Config) -> DstDir = ?config(priv_dir, Config), SrcDir = filename:join(os:getenv("HOME"), ".ssh"), RsaOk = copyfile(SrcDir, DstDir, "id_rsa"), DsaOk = copyfile(SrcDir, DstDir, "id_dsa"), case {RsaOk, DsaOk} of {{ok, _}, {ok, _}} -> {ok, both}; {{ok, _}, _} -> {ok, rsa}; {_, {ok, _}} -> {ok, dsa}; {Error, _} -> Error end. %% Remove later on. Use make_dsa_files instead. remove_id_keys(Config) -> Dir = ?config(priv_dir, Config), file:delete(filename:join(Dir, "id_rsa")), file:delete(filename:join(Dir, "id_dsa")). make_dsa_files(Config) -> make_dsa_files(Config, rfc4716_public_key). make_dsa_files(Config, Type) -> {DSA, EncodedKey} = gen_dsa(128, 20), PKey = DSA#'DSAPrivateKey'.y, P = DSA#'DSAPrivateKey'.p, Q = DSA#'DSAPrivateKey'.q, G = DSA#'DSAPrivateKey'.g, Dss = #'Dss-Parms'{p=P, q=Q, g=G}, {ok, Hostname} = inet:gethostname(), {ok, {A, B, C, D}} = inet:getaddr(Hostname, inet), IP = lists:concat([A, ".", B, ".", C, ".", D]), Attributes = [], % Could be [{comment,"user@" ++ Hostname}], HostNames = [{hostnames,[IP, IP]}], PublicKey = [{{PKey, Dss}, Attributes}], KnownHosts = [{{PKey, Dss}, HostNames}], KnownHostsEnc = public_key:ssh_encode(KnownHosts, known_hosts), KnownHosts = public_key:ssh_decode(KnownHostsEnc, known_hosts), PublicKeyEnc = public_key:ssh_encode(PublicKey, Type), SystemTmpDir = ?config(data_dir, Config), filelib:ensure_dir(SystemTmpDir), file:make_dir(SystemTmpDir), DSAFile = filename:join(SystemTmpDir, "ssh_host_dsa_key.pub"), file:delete(DSAFile), DSAPrivateFile = filename:join(SystemTmpDir, "ssh_host_dsa_key"), file:delete(DSAPrivateFile), KHFile = filename:join(SystemTmpDir, "known_hosts"), file:delete(KHFile), PemBin = public_key:pem_encode([EncodedKey]), file:write_file(DSAFile, PublicKeyEnc), file:write_file(KHFile, KnownHostsEnc), file:write_file(DSAPrivateFile, PemBin), ok. %%-------------------------------------------------------------------- %% @doc Creates a dsa key (OBS: for testing only) %% the sizes are in bytes %% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()} %% @end %%-------------------------------------------------------------------- gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) -> Key = gen_dsa2(LSize, NSize), {Key, encode_key(Key)}. encode_key(Key = #'DSAPrivateKey'{}) -> Der = public_key:der_encode('DSAPrivateKey', Key), {'DSAPrivateKey', Der, not_encrypted}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% DSA key generation (OBS: for testing only) %% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm %% and the fips_186-3.pdf %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% gen_dsa2(LSize, NSize) -> Q = prime(NSize), %% Choose N-bit prime Q X0 = prime(LSize), P0 = prime((LSize div 2) +1), %% Choose L-bit prime modulus P such that p-1 is a multiple of q. case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of error -> gen_dsa2(LSize, NSize); P -> G = crypto:mod_pow(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used. X = prime(20), %% Choose x by some random method, where 0 < x < q. Y = crypto:mod_pow(G, X, P), %% Calculate y = g^x mod p. #'DSAPrivateKey'{version=0, p = P, q = Q, g = crypto:bytes_to_integer(G), y = crypto:bytes_to_integer(Y), x = X} end. %% See fips_186-3.pdf dsa_search(T, P0, Q, Iter) when Iter > 0 -> P = 2*T*Q*P0 + 1, case is_prime(P, 50) of true -> P; false -> dsa_search(T+1, P0, Q, Iter-1) end; dsa_search(_,_,_,_) -> error. %%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% prime(ByteSize) -> Rand = odd_rand(ByteSize), prime_odd(Rand, 0). prime_odd(Rand, N) -> case is_prime(Rand, 50) of true -> Rand; false -> prime_odd(Rand+2, N+1) end. %% see http://en.wikipedia.org/wiki/Fermat_primality_test is_prime(_, 0) -> true; is_prime(Candidate, Test) -> CoPrime = odd_rand(10000, Candidate), Result = crypto:mod_pow(CoPrime, Candidate, Candidate) , is_prime(CoPrime, crypto:bytes_to_integer(Result), Candidate, Test). is_prime(CoPrime, CoPrime, Candidate, Test) -> is_prime(Candidate, Test-1); is_prime(_,_,_,_) -> false. odd_rand(Size) -> Min = 1 bsl (Size*8-1), Max = (1 bsl (Size*8))-1, odd_rand(Min, Max). odd_rand(Min,Max) -> Rand = crypto:rand_uniform(Min,Max), case Rand rem 2 of 0 -> Rand + 1; _ -> Rand end. copyfile(SrcDir, DstDir, Fn) -> file:copy(filename:join(SrcDir, Fn), filename:join(DstDir, Fn)). %%% END SSH key management %%%-----------------------------------------------------------------