aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/compiler/src/v3_core.erl69
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl9
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c2
-rw-r--r--lib/kernel/doc/src/inet.xml2
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl132
-rw-r--r--lib/kernel/test/init_SUITE.erl69
-rw-r--r--lib/public_key/doc/src/public_key.xml98
-rw-r--r--lib/public_key/include/public_key.hrl12
-rw-r--r--lib/public_key/src/Makefile1
-rw-r--r--lib/public_key/src/pubkey_cert.erl6
-rw-r--r--lib/public_key/src/pubkey_cert_records.erl2
-rw-r--r--lib/public_key/src/pubkey_pem.erl38
-rw-r--r--lib/public_key/src/pubkey_ssh.erl431
-rw-r--r--lib/public_key/src/public_key.app.src6
-rw-r--r--lib/public_key/src/public_key.erl86
-rw-r--r--lib/public_key/test/public_key_SUITE.erl416
-rw-r--r--lib/public_key/test/public_key_SUITE_data/auth_keys3
-rw-r--r--lib/public_key/test/public_key_SUITE_data/known_hosts3
-rw-r--r--lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub1
-rw-r--r--lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub3
-rw-r--r--lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub1
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys3
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts2
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub13
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub12
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub7
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub13
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub8
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub9
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub9
-rw-r--r--lib/reltool/src/reltool_server.erl2
-rw-r--r--lib/reltool/test/reltool_app_SUITE.erl17
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl34
-rw-r--r--lib/sasl/src/release_handler.erl42
-rw-r--r--lib/sasl/src/release_handler_1.erl44
-rw-r--r--lib/ssl/src/ssl.erl7
-rw-r--r--lib/ssl/src/ssl_handshake.hrl5
-rw-r--r--lib/stdlib/doc/src/binary.xml2
-rw-r--r--lib/stdlib/doc/src/zip.xml20
-rw-r--r--lib/stdlib/src/erl_parse.yrl10
-rw-r--r--lib/stdlib/src/erl_pp.erl10
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl11
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl24
-rw-r--r--lib/tv/src/tv_io_lib.erl9
44 files changed, 1421 insertions, 282 deletions
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index 2da24b2908..e1a593fffa 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -1820,7 +1820,21 @@ upattern_list([], _, St) -> {[],[],[],[],St}.
%% upat_bin([Pat], [KnownVar], State) ->
%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
upat_bin(Es0, Ks, St0) ->
- upat_bin(Es0, Ks, [], St0).
+ {Es1,Pg,Pv,Pu0,St1} = upat_bin(Es0, Ks, [], St0),
+
+ %% In a clause such as <<Sz:8,V:Sz>> in a function head, Sz will both
+ %% be new and used; a situation that is not handled properly by
+ %% uclause/4. (Basically, since Sz occurs in two sets that are
+ %% subtracted from each other, Sz will not be added to the list of
+ %% known variables and will seem to be new the next time it is
+ %% used in a match.)
+ %% Since the variable Sz really is new (it does not use a
+ %% value bound prior to the binary matching), Sz should only be
+ %% included in the set of new variables. Thus we should take it
+ %% out of the set of used variables.
+
+ Pu1 = subtract(Pu0, intersection(Pv, Pu0)),
+ {Es1,Pg,Pv,Pu1,St1}.
%% upat_bin([Pat], [KnownVar], [LocalVar], State) ->
%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
@@ -1832,35 +1846,36 @@ upat_bin([], _, _, St) -> {[],[],[],[],St}.
%% upat_element(Segment, [KnownVar], [LocalVar], State) ->
-%% {Segment,[GuardTest],[NewVar],[UsedVar],[LocalVar],State}
-upat_element(#c_bitstr{val=H0,size=Sz}=Seg, Ks, Bs, St0) ->
- {H1,Hg,Hv,[],St1} = upattern(H0, Ks, St0),
- Bs1 = case H0 of
- #c_var{name=Hname} ->
- case H1 of
+%% {Segment,[GuardTest],[NewVar],[UsedVar],[LocalVar],State}
+upat_element(#c_bitstr{val=H0,size=Sz0}=Seg, Ks, Bs0, St0) ->
+ {H1,Hg,Hv,[],St1} = upattern(H0, Ks, St0),
+ Bs1 = case H0 of
#c_var{name=Hname} ->
- Bs;
- #c_var{name=Other} ->
- [{Hname, Other}|Bs]
- end;
- _ ->
- Bs
- end,
- {Sz1, Us} = case Sz of
- #c_var{name=Vname} ->
- rename_bitstr_size(Vname, Bs);
- _Other -> {Sz, []}
- end,
- {Seg#c_bitstr{val=H1, size=Sz1},Hg,Hv,Us,Bs1,St1}.
-
-rename_bitstr_size(V, [{V, N}|_]) ->
- New = #c_var{name=N},
- {New, [N]};
+ case H1 of
+ #c_var{name=Hname} ->
+ Bs0;
+ #c_var{name=Other} ->
+ [{Hname,Other}|Bs0]
+ end;
+ _ ->
+ Bs0
+ end,
+ {Sz1,Us} = case Sz0 of
+ #c_var{name=Vname} ->
+ rename_bitstr_size(Vname, Bs0);
+ _Other ->
+ {Sz0,[]}
+ end,
+ {Seg#c_bitstr{val=H1,size=Sz1},Hg,Hv,Us,Bs1,St1}.
+
+rename_bitstr_size(V, [{V,N}|_]) ->
+ New = #c_var{name=N},
+ {New,[N]};
rename_bitstr_size(V, [_|Rest]) ->
- rename_bitstr_size(V, Rest);
+ rename_bitstr_size(V, Rest);
rename_bitstr_size(V, []) ->
- Old = #c_var{name=V},
- {Old, [V]}.
+ Old = #c_var{name=V},
+ {Old,[V]}.
used_in_any(Les) ->
foldl(fun (Le, Ns) -> union((get_anno(Le))#a.us, Ns) end,
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 1e3c670fb8..9184e14cb2 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -142,7 +142,14 @@ otp_5269(Config) when is_list(Config) ->
[X || <<X:X>> <- [<<1:32>>,<<2:32>>,<<3:8>>]] end,
%% "binsize variable" ^
[1,2]),
-
+ ?line check(fun() ->
+ (fun (<<A:1/binary, B:8/integer, _C:B/binary>>) ->
+ case A of
+ B -> wrong;
+ _ -> ok
+ end
+ end)(<<1,2,3,4>>) end,
+ ok),
ok.
null_fields(Config) when is_list(Config) ->
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index 158c1ec430..34362b4b9f 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -1197,7 +1197,7 @@ static char *hex(char digest[16], char buff[33])
char *p = buff;
int i;
- for (i = 0; i < sizeof(digest); ++i) {
+ for (i = 0; i < 16; ++i) {
*p++ = tab[(int)((*d) >> 4)];
*p++ = tab[(int)((*d++) & 0xF)];
}
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index a22c0a8346..f05a224f33 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -432,7 +432,7 @@ fe80::204:acff:fe17:bf38
</desc>
</func>
<func>
- <name>port(Socket) -> {ok, Port}</name>
+ <name>port(Socket) -> {ok, Port} | {error, any()}</name>
<fsummary>Return the local port number for a socket</fsummary>
<type>
<v>Socket = socket()</v>
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index 3b313a6c21..b1ef8826d5 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -39,7 +39,7 @@
accept_timeouts_in_order/1,accept_timeouts_in_order2/1,
accept_timeouts_in_order3/1,accept_timeouts_mixed/1,
killing_acceptor/1,killing_multi_acceptors/1,killing_multi_acceptors2/1,
- several_accepts_in_one_go/1,active_once_closed/1, send_timeout/1,
+ several_accepts_in_one_go/1,active_once_closed/1, send_timeout/1, send_timeout_active/1,
otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1]).
%% Internal exports.
@@ -71,7 +71,7 @@ all() ->
accept_timeouts_in_order3, accept_timeouts_mixed,
killing_acceptor, killing_multi_acceptors,
killing_multi_acceptors2, several_accepts_in_one_go,
- active_once_closed, send_timeout, otp_7731,
+ active_once_closed, send_timeout, send_timeout_active, otp_7731,
zombie_sockets, otp_7816, otp_8102].
groups() ->
@@ -1957,6 +1957,60 @@ send_timeout(Config) when is_list(Config) ->
ParaFun(false),
ParaFun(true),
ok.
+mad_sender(S) ->
+ {_, _, USec} = now(),
+ case gen_tcp:send(S, integer_to_list(USec)) of
+ ok ->
+ mad_sender(S);
+ Err ->
+ Err
+ end.
+
+
+flush() ->
+ receive
+ _X ->
+ %erlang:display(_X),
+ flush()
+ after 0 ->
+ ok
+ end.
+
+send_timeout_active(suite) ->
+ [];
+send_timeout_active(doc) ->
+ ["Test the send_timeout socket option for active sockets"];
+send_timeout_active(Config) when is_list(Config) ->
+ Dog = test_server:timetrap(test_server:seconds(20)),
+ %% Basic
+ BasicFun =
+ fun(AutoClose) ->
+ ?line {Loop,A,RNode,C} = setup_active_timeout_sink(1, AutoClose),
+ inet:setopts(A, [{active, once}]),
+ ?line Mad = spawn_link(RNode,fun() -> mad_sender(C) end),
+ ?line {error,timeout} =
+ Loop(fun() ->
+ receive
+ {tcp, Sock, _Data} ->
+ inet:setopts(A, [{active, once}]),
+ Res = gen_tcp:send(A,lists:duplicate(1000, $a)),
+ %erlang:display(Res),
+ Res;
+ Err ->
+ io:format("sock closed: ~p~n", [Err]),
+ Err
+ end
+ end),
+ unlink(Mad),
+ exit(Mad,kill),
+ ?line test_server:stop_node(RNode)
+ end,
+ BasicFun(false),
+ flush(),
+ BasicFun(true),
+ flush(),
+ test_server:timetrap_cancel(Dog),
+ ok.
after_send_timeout(AutoClose) ->
case AutoClose of
@@ -2039,35 +2093,35 @@ setup_closed_ao() ->
{Loop,A}.
setup_timeout_sink(Timeout, AutoClose) ->
- Dir = filename:dirname(code:which(?MODULE)),
- {ok,R} = test_server:start_node(test_default_options_slave,slave,
+ ?line Dir = filename:dirname(code:which(?MODULE)),
+ ?line {ok,R} = test_server:start_node(test_default_options_slave,slave,
[{args,"-pa " ++ Dir}]),
- Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
- {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2},
+ ?line Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
+ ?line {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2},
{send_timeout,Timeout},
{send_timeout_close,AutoClose}]),
- Fun = fun(F) ->
+ ?line Fun = fun(F) ->
receive
{From,X} when is_function(X) ->
From ! {self(),X()}, F(F);
die -> ok
end
end,
- Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
- {ok, Port} = inet:port(L),
- Remote = fun(Fu) ->
+ ?line Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
+ ?line {ok, Port} = inet:port(L),
+ ?line Remote = fun(Fu) ->
Pid ! {self(), Fu},
receive {Pid,X} -> X
end
end,
- {ok, C} = Remote(fun() ->
+ ?line {ok, C} = Remote(fun() ->
gen_tcp:connect(Host,Port,
[{active,false},{packet,2}])
end),
- {ok,A} = gen_tcp:accept(L),
- gen_tcp:send(A,"Hello"),
- {ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end),
- Loop2 = fun(_,_,0) ->
+ ?line {ok,A} = gen_tcp:accept(L),
+ ?line gen_tcp:send(A,"Hello"),
+ ?line {ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end),
+ ?line Loop2 = fun(_,_,0) ->
{failure, timeout};
(L2,F2,N) ->
Ret = F2(),
@@ -2078,9 +2132,53 @@ setup_timeout_sink(Timeout, AutoClose) ->
Other -> Other
end
end,
- Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
+ ?line Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
{Loop,A,R}.
-
+
+setup_active_timeout_sink(Timeout, AutoClose) ->
+ ?line Dir = filename:dirname(code:which(?MODULE)),
+ ?line {ok,R} = test_server:start_node(test_default_options_slave,slave,
+ [{args,"-pa " ++ Dir}]),
+ ?line Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
+ ?line {ok, L} = gen_tcp:listen(0, [binary,{active,false},{packet,0},{nodelay, true},{keepalive, true},
+ {send_timeout,Timeout},
+ {send_timeout_close,AutoClose}]),
+ ?line Fun = fun(F) ->
+ receive
+ {From,X} when is_function(X) ->
+ From ! {self(),X()}, F(F);
+ die -> ok
+ end
+ end,
+ ?line Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
+ ?line {ok, Port} = inet:port(L),
+ ?line Remote = fun(Fu) ->
+ Pid ! {self(), Fu},
+ receive {Pid,X} -> X
+ end
+ end,
+ ?line {ok, C} = Remote(fun() ->
+ gen_tcp:connect(Host,Port,
+ [{active,false}])
+ end),
+ ?line {ok,A} = gen_tcp:accept(L),
+ ?line gen_tcp:send(A,"Hello"),
+ ?line {ok, "H"++_} = Remote(fun() -> gen_tcp:recv(C,0) end),
+ ?line Loop2 = fun(_,_,0) ->
+ {failure, timeout};
+ (L2,F2,N) ->
+ Ret = F2(),
+ io:format("~p~n",[Ret]),
+ case Ret of
+ ok -> receive after 1 -> ok end,
+ L2(L2,F2,N-1);
+ Other -> Other
+ end
+ end,
+ ?line Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
+ {Loop,A,R,C}.
+
+
millistamp() ->
{Mega, Secs, Micros} = erlang:now(),
(Micros div 1000) + Secs * 1000 + Mega * 1000000000.
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index 06bfe97bc4..2db0f7dcb8 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -24,6 +24,7 @@
init_per_group/2,end_per_group/2]).
-export([get_arguments/1, get_argument/1, boot_var/1, restart/1,
+ many_restarts/1,
get_plain_arguments/1,
reboot/1, stop/1, get_status/1, script_id/1]).
-export([boot1/1, boot2/1]).
@@ -43,6 +44,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[get_arguments, get_argument, boot_var,
+ many_restarts,
get_plain_arguments, restart, get_status, script_id,
{group, boot}].
@@ -317,6 +319,73 @@ is_real_system(KernelVsn, StdlibVsn) ->
%% Therefore the slave process must be killed
%% before restart.
%% ------------------------------------------------
+many_restarts(doc) -> [];
+many_restarts(suite) ->
+ case ?t:os_type() of
+ {Fam, _} when Fam == unix; Fam == win32 ->
+ {req, [distribution, {local_slave_nodes, 1}, {time, 5}]};
+ _ ->
+ {skip, "Only run on unix and win32"}
+ end;
+
+many_restarts(Config) when is_list(Config) ->
+ ?line Dog = ?t:timetrap(?t:seconds(480)),
+ ?line {ok, Node} = loose_node:start(init_test, "", ?DEFAULT_TIMEOUT_SEC),
+ ?line loop_restart(30,Node,rpc:call(Node,erlang,whereis,[error_logger])),
+ ?line loose_node:stop(Node),
+ ?line ?t:timetrap_cancel(Dog),
+ ok.
+
+loop_restart(0,_,_) ->
+ ok;
+loop_restart(N,Node,EHPid) ->
+ ?line erlang:monitor_node(Node, true),
+ ?line ok = rpc:call(Node, init, restart, []),
+ ?line receive
+ {nodedown, Node} ->
+ ok
+ after 10000 ->
+ loose_node:stop(Node),
+ ?t:fail(not_stopping)
+ end,
+ ?line ok = wait_for(30, Node, EHPid),
+ ?line loop_restart(N-1,Node,rpc:call(Node,erlang,whereis,[error_logger])).
+
+wait_for(0,Node,_) ->
+ loose_node:stop(Node),
+ error;
+wait_for(N,Node,EHPid) ->
+ ?line case rpc:call(Node, erlang, whereis, [error_logger]) of
+ Pid when is_pid(Pid), Pid =/= EHPid ->
+ %% ?line erlang:display(ok),
+ ?line ok;
+ _X ->
+ %% ?line erlang:display(_X),
+ %% ?line Procs = rpc:call(Node, erlang, processes, []),
+ %% ?line erlang:display(Procs),
+ %% case is_list(Procs) of
+ %% true ->
+ %% ?line [(catch erlang:display(
+ %% rpc:call(Node,
+ %% erlang,
+ %% process_info,
+ %% [Y,registered_name])))
+ %% || Y <- Procs];
+ %% _ ->
+ %% ok
+ %% end,
+ receive
+ after 100 ->
+ ok
+ end,
+ ?line wait_for(N-1,Node,EHPid)
+ end.
+
+%% ------------------------------------------------
+%% Slave executes erlang:halt() on master nodedown.
+%% Therefore the slave process must be killed
+%% before restart.
+%% ------------------------------------------------
restart(doc) -> [];
restart(suite) ->
case ?t:os_type() of
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 81aedaea56..c5f57214b1 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -56,44 +56,43 @@
<p><em>Data Types </em></p>
- <p><c>boolean() = true | false</c></p>
+ <p><code>boolean() = true | false</code></p>
- <p><c>string = [bytes()]</c></p>
+ <p><code>string = [bytes()]</code></p>
- <p><c>der_encoded() = binary() </c></p>
-
- <p><c>decrypt_der() = binary() </c></p>
+ <p><code>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey'
+ 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'</code></p>
- <p><c>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey'
- 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'</c></p>
-
- <p><c>pem_entry () = {pki_asn1_type(), der_encoded() | decrypt_der(), not_encrypted |
- {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)}}.</c></p>
-
- <p><c>rsa_public_key() = #'RSAPublicKey'{}</c></p>
+ <p><code>pem_entry () = {pki_asn1_type(), binary() %% DER or encrypted DER
+ not_encrypted | {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)}}.</code></p>
- <p><c>rsa_private_key() = #'RSAPrivateKey'{} </c></p>
+ <p><code>rsa_public_key() = #'RSAPublicKey'{}</code></p>
+
+ <p><code>rsa_private_key() = #'RSAPrivateKey'{} </code></p>
- <p><c>dsa_public_key() = {integer(), #'Dss-Parms'{}} </c></p>
+ <p><code>dsa_public_key() = {integer(), #'Dss-Parms'{}} </code></p>
- <p><c>rsa_private_key() = #'RSAPrivateKey'{} </c></p>
+ <p><code>rsa_private_key() = #'RSAPrivateKey'{} </code></p>
- <p><c>dsa_private_key() = #'DSAPrivateKey'{}</c></p>
+ <p><code>dsa_private_key() = #'DSAPrivateKey'{}</code></p>
- <p><c> public_crypt_options() = [{rsa_pad, rsa_padding()}]. </c></p>
+ <p><code> public_crypt_options() = [{rsa_pad, rsa_padding()}]. </code></p>
- <p><c> rsa_padding() = 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding'
- | 'rsa_no_padding'</c></p>
+ <p><code> rsa_padding() = 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding'
+ | 'rsa_no_padding'</code></p>
- <p><c> rsa_digest_type() = 'md5' | 'sha' </c></p>
-
- <p><c> dss_digest_type() = 'none' | 'sha' </c></p>
+ <p><code> rsa_digest_type() = 'md5' | 'sha' </code></p>
+
+ <p><code> dss_digest_type() = 'none' | 'sha' </code></p>
+
+ <p><code> ssh_file() = openssh_public_key | rfc4716_public_key |
+ known_hosts | auth_keys </code></p>
-<!-- <p><c>policy_tree() = [Root, Children]</c></p> -->
+<!-- <p><code>policy_tree() = [Root, Children]</code></p> -->
-<!-- <p><c>Root = #policy_tree_node{}</c></p> -->
+<!-- <p><code>Root = #policy_tree_node{}</code></p> -->
-<!-- <p><c>Children = [] | policy_tree()</c></p> -->
+<!-- <p><code>Children = [] | policy_tree()</code></p> -->
<!-- <p> The policy_tree_node record has the following fields:</p> -->
@@ -403,6 +402,55 @@
</func>
<func>
+ <name>ssh_decode(SshBin, Type) -> [{public_key(), Attributes::list()}]</name>
+ <fsummary>Decodes a ssh file-binary. </fsummary>
+ <type>
+ <v>SshBin = binary()</v>
+ <d>Example {ok, SshBin} = file:read_file("known_hosts").</d>
+ <v> Type = public_key | ssh_file()</v>
+ <d>If <c>Type</c> is <c>public_key</c> the binary may be either
+ a rfc4716 public key or a openssh public key.</d>
+ </type>
+ <desc>
+ <p> Decodes a ssh file-binary. In the case of know_hosts or
+ auth_keys the binary may include one or more lines of the
+ file. Returns a list of public keys and their attributes, possible
+ attribute values depends on the file type represented by the
+ binary.
+ </p>
+
+ <taglist>
+ <tag>rfc4716 attributes - see RFC 4716</tag>
+ <item>{headers, [{string(), utf8_string()}]}</item>
+ <tag>auth_key attributes - see man sshd </tag>
+ <item>{comment, string()}</item>
+ <item>{options, [string()]}</item>
+ <item>{bits, integer()} - In ssh version 1 files</item>
+ <tag>known_host attributes - see man sshd</tag>
+ <item>{hostnames, [string()]}</item>
+ <item>{comment, string()}</item>
+ <item>{bits, integer()} - In ssh version 1 files</item>
+ </taglist>
+
+ </desc>
+ </func>
+
+ <func>
+ <name>ssh_encode([{Key, Attributes}], Type) -> binary()</name>
+ <fsummary> Encodes a list of ssh file entries to a binary.</fsummary>
+ <type>
+ <v>Key = public_key()</v>
+ <v>Attributes = list()</v>
+ <v>Type = ssh_file()</v>
+ </type>
+ <desc>
+ <p>Encodes a list of ssh file entries (public keys and attributes) to a binary. Possible
+ attributes depends on the file type, see <seealso
+ marker="ssh_decode"> ssh_decode/2 </seealso></p>
+ </desc>
+ </func>
+
+ <func>
<name>verify(Msg, DigestType, Signature, Key) -> boolean()</name>
<fsummary>Verifies a digital signature.</fsummary>
<type>
diff --git a/lib/public_key/include/public_key.hrl b/lib/public_key/include/public_key.hrl
index 3498a2a433..5f97d80f7e 100644
--- a/lib/public_key/include/public_key.hrl
+++ b/lib/public_key/include/public_key.hrl
@@ -70,14 +70,18 @@
interim_reasons_mask
}).
-
--type der_encoded() :: binary().
--type decrypt_der() :: binary().
+-type public_key() :: rsa_public_key() | dsa_public_key().
+-type rsa_public_key() :: #'RSAPublicKey'{}.
+-type rsa_private_key() :: #'RSAPrivateKey'{}.
+-type dsa_private_key() :: #'DSAPrivateKey'{}.
+-type dsa_public_key() :: {integer(), #'Dss-Parms'{}}.
-type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey'
| 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter'
| 'SubjectPublicKeyInfo'.
--type pem_entry() :: {pki_asn1_type(), der_encoded() | decrypt_der(),
+-type pem_entry() :: {pki_asn1_type(), binary(), %% DER or Encrypted DER
not_encrypted | {Cipher :: string(), Salt :: binary()}}.
-type asn1_type() :: atom(). %% see "OTP-PUB-KEY.hrl
+-type ssh_file() :: openssh_public_key | rfc4716_public_key | known_hosts |
+ auth_keys.
-endif. % -ifdef(public_key).
diff --git a/lib/public_key/src/Makefile b/lib/public_key/src/Makefile
index b042b0c30a..5a24b02d2a 100644
--- a/lib/public_key/src/Makefile
+++ b/lib/public_key/src/Makefile
@@ -41,6 +41,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/public_key-$(VSN)
MODULES = \
public_key \
pubkey_pem \
+ pubkey_ssh \
pubkey_cert \
pubkey_cert_records
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index fadb993ed9..5ab9642279 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -38,7 +38,7 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec verify_data(der_encoded()) -> {md5 | sha, binary(), binary()}.
+-spec verify_data(DER::binary()) -> {md5 | sha, binary(), binary()}.
%%
%% Description: Extracts data from DerCert needed to call public_key:verify/4.
%%--------------------------------------------------------------------
@@ -146,7 +146,7 @@ validate_issuer(OtpCert, Issuer, UserState, VerifyFun) ->
verify_fun(OtpCert, {bad_cert, invalid_issuer}, UserState, VerifyFun)
end.
%%--------------------------------------------------------------------
--spec validate_signature(#'OTPCertificate'{}, der_encoded(),
+-spec validate_signature(#'OTPCertificate'{}, DER::binary(),
term(),term(), term(), fun()) -> term().
%%
diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl
index 2441cfc0e0..b86d7a1f0c 100644
--- a/lib/public_key/src/pubkey_cert_records.erl
+++ b/lib/public_key/src/pubkey_cert_records.erl
@@ -30,7 +30,7 @@
%%====================================================================
%%--------------------------------------------------------------------
--spec decode_cert(der_encoded()) -> {ok, #'OTPCertificate'{}}.
+-spec decode_cert(DerCert::binary()) -> {ok, #'OTPCertificate'{}}.
%%
%% Description: Recursively decodes a Certificate.
%%--------------------------------------------------------------------
diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl
index c8c69bcdd0..c26815bc04 100644
--- a/lib/public_key/src/pubkey_pem.erl
+++ b/lib/public_key/src/pubkey_pem.erl
@@ -69,8 +69,9 @@ encode(PemEntries) ->
encode_pem_entries(PemEntries).
%%--------------------------------------------------------------------
--spec decipher({pki_asn1_type(), decrypt_der(),{Cipher :: string(), Salt :: binary()}}, string()) ->
- der_encoded().
+-spec decipher({pki_asn1_type(), DerEncrypted::binary(),{Cipher :: string(),
+ Salt :: binary()}},
+ string()) -> Der::binary().
%%
%% Description: Deciphers a decrypted pem entry.
%%--------------------------------------------------------------------
@@ -78,7 +79,8 @@ decipher({_, DecryptDer, {Cipher,Salt}}, Password) ->
decode_key(DecryptDer, Password, Cipher, Salt).
%%--------------------------------------------------------------------
--spec cipher(der_encoded(),{Cipher :: string(), Salt :: binary()} , string()) -> binary().
+-spec cipher(Der::binary(),{Cipher :: string(), Salt :: binary()} ,
+ string()) -> binary().
%%
%% Description: Ciphers a PEM entry
%%--------------------------------------------------------------------
@@ -91,11 +93,11 @@ cipher(Der, {Cipher,Salt}, Password)->
encode_pem_entries(Entries) ->
[encode_pem_entry(Entry) || Entry <- Entries].
-encode_pem_entry({Asn1Type, Der, not_encrypted}) ->
- StartStr = pem_start(Asn1Type),
+encode_pem_entry({Type, Der, not_encrypted}) ->
+ StartStr = pem_start(Type),
[StartStr, "\n", b64encode_and_split(Der), "\n", pem_end(StartStr) ,"\n\n"];
-encode_pem_entry({Asn1Type, Der, {Cipher, Salt}}) ->
- StartStr = pem_start(Asn1Type),
+encode_pem_entry({Type, Der, {Cipher, Salt}}) ->
+ StartStr = pem_start(Type),
[StartStr,"\n", pem_decrypt(),"\n", pem_decrypt_info(Cipher, Salt),"\n",
b64encode_and_split(Der), "\n", pem_end(StartStr) ,"\n\n"].
@@ -115,17 +117,17 @@ decode_pem_entries([Start| Lines], Entries) ->
end.
decode_pem_entry(Start, [<<"Proc-Type: 4,ENCRYPTED", _/binary>>, Line | Lines]) ->
- Asn1Type = asn1_type(Start),
+ Type = asn1_type(Start),
Cs = erlang:iolist_to_binary(Lines),
Decoded = base64:mime_decode(Cs),
[_, DekInfo0] = string:tokens(binary_to_list(Line), ": "),
[Cipher, Salt] = string:tokens(DekInfo0, ","),
- {Asn1Type, Decoded, {Cipher, unhex(Salt)}};
+ {Type, Decoded, {Cipher, unhex(Salt)}};
decode_pem_entry(Start, Lines) ->
- Asn1Type = asn1_type(Start),
+ Type = asn1_type(Start),
Cs = erlang:iolist_to_binary(Lines),
- Der = base64:mime_decode(Cs),
- {Asn1Type, Der, not_encrypted}.
+ Decoded = base64:mime_decode(Cs),
+ {Type, Decoded, not_encrypted}.
split_bin(Bin) ->
split_bin(0, Bin).
@@ -153,17 +155,7 @@ split_lines(Bin) ->
[Bin].
%% Ignore white space at end of line
-join_entry([<<"-----END CERTIFICATE-----", _/binary>>| Lines], Entry) ->
- {lists:reverse(Entry), Lines};
-join_entry([<<"-----END RSA PRIVATE KEY-----", _/binary>>| Lines], Entry) ->
- {lists:reverse(Entry), Lines};
-join_entry([<<"-----END PUBLIC KEY-----", _/binary>>| Lines], Entry) ->
- {lists:reverse(Entry), Lines};
-join_entry([<<"-----END RSA PUBLIC KEY-----", _/binary>>| Lines], Entry) ->
- {lists:reverse(Entry), Lines};
-join_entry([<<"-----END DSA PRIVATE KEY-----", _/binary>>| Lines], Entry) ->
- {lists:reverse(Entry), Lines};
-join_entry([<<"-----END DH PARAMETERS-----", _/binary>>| Lines], Entry) ->
+join_entry([<<"-----END ", _/binary>>| Lines], Entry) ->
{lists:reverse(Entry), Lines};
join_entry([Line | Lines], Entry) ->
join_entry(Lines, [Line | Entry]).
diff --git a/lib/public_key/src/pubkey_ssh.erl b/lib/public_key/src/pubkey_ssh.erl
new file mode 100644
index 0000000000..f342eab159
--- /dev/null
+++ b/lib/public_key/src/pubkey_ssh.erl
@@ -0,0 +1,431 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(pubkey_ssh).
+
+-include("public_key.hrl").
+
+-export([decode/2, encode/2]).
+
+-define(UINT32(X), X:32/unsigned-big-integer).
+%% Max encoded line length is 72, but conformance examples use 68
+%% Comment from rfc 4716: "The following are some examples of public
+%% key files that are compliant (note that the examples all wrap
+%% before 72 bytes to meet IETF document requirements; however, they
+%% are still compliant.)" So we choose to use 68 also.
+-define(ENCODED_LINE_LENGTH, 68).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+-spec decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}].
+%%
+%% Description: Decodes a ssh file-binary.
+%%--------------------------------------------------------------------
+decode(Bin, public_key)->
+ case binary:match(Bin, begin_marker()) of
+ nomatch ->
+ openssh_decode(Bin, openssh_public_key);
+ _ ->
+ rfc4716_decode(Bin)
+ end;
+decode(Bin, rfc4716_public_key) ->
+ rfc4716_decode(Bin);
+decode(Bin, Type) ->
+ openssh_decode(Bin, Type).
+
+%%--------------------------------------------------------------------
+-spec encode([{public_key(), Attributes::list()}], ssh_file()) ->
+ binary().
+%%
+%% Description: Encodes a list of ssh file entries.
+%%--------------------------------------------------------------------
+encode(Entries, Type) ->
+ erlang:iolist_to_binary(lists:map(fun({Key, Attributes}) ->
+ do_encode(Type, Key, Attributes)
+ end, Entries)).
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+begin_marker() ->
+ <<"---- BEGIN SSH2 PUBLIC KEY ----">>.
+end_marker() ->
+ <<"---- END SSH2 PUBLIC KEY ----">>.
+
+rfc4716_decode(Bin) ->
+ Lines = binary:split(Bin, <<"\n">>, [global]),
+ do_rfc4716_decode(Lines, []).
+
+do_rfc4716_decode([<<"---- BEGIN SSH2 PUBLIC KEY ----", _/binary>> | Lines], Acc) ->
+ do_rfc4716_decode(Lines, Acc);
+%% Ignore empty lines before or after begin/end - markers.
+do_rfc4716_decode([<<>> | Lines], Acc) ->
+ do_rfc4716_decode(Lines, Acc);
+do_rfc4716_decode([], Acc) ->
+ lists:reverse(Acc);
+do_rfc4716_decode(Lines, Acc) ->
+ {Headers, PubKey, Rest} = rfc4716_decode_lines(Lines, []),
+ case Headers of
+ [_|_] ->
+ do_rfc4716_decode(Rest, [{PubKey, [{headers, Headers}]} | Acc]);
+ _ ->
+ do_rfc4716_decode(Rest, [{PubKey, []} | Acc])
+ end.
+
+rfc4716_decode_lines([Line | Lines], Acc) ->
+ case binary:last(Line) of
+ $\\ ->
+ NewLine = binary:replace(Line,<<"\\">>, hd(Lines), []),
+ rfc4716_decode_lines([NewLine | tl(Lines)], Acc);
+ _ ->
+ rfc4716_decode_line(Line, Lines, Acc)
+ end.
+
+rfc4716_decode_line(Line, Lines, Acc) ->
+ case binary:split(Line, <<":">>) of
+ [Tag, Value] ->
+ rfc4716_decode_lines(Lines, [{string_decode(Tag), unicode_decode(Value)} | Acc]);
+ _ ->
+ {Body, Rest} = join_entry([Line | Lines], []),
+ {lists:reverse(Acc), rfc4716_pubkey_decode(base64:mime_decode(Body)), Rest}
+ end.
+
+join_entry([<<"---- END SSH2 PUBLIC KEY ----", _/binary>>| Lines], Entry) ->
+ {lists:reverse(Entry), Lines};
+join_entry([Line | Lines], Entry) ->
+ join_entry(Lines, [Line | Entry]).
+
+
+rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary,
+ ?UINT32(SizeE), E:SizeE/binary,
+ ?UINT32(SizeN), N:SizeN/binary>>) when Type == <<"ssh-rsa">> ->
+ #'RSAPublicKey'{modulus = erlint(SizeN, N),
+ publicExponent = erlint(SizeE, E)};
+
+rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary,
+ ?UINT32(SizeP), P:SizeP/binary,
+ ?UINT32(SizeQ), Q:SizeQ/binary,
+ ?UINT32(SizeG), G:SizeG/binary,
+ ?UINT32(SizeY), Y:SizeY/binary>>) when Type == <<"ssh-dss">> ->
+ {erlint(SizeY, Y),
+ #'Dss-Parms'{p = erlint(SizeP, P),
+ q = erlint(SizeQ, Q),
+ g = erlint(SizeG, G)}}.
+
+openssh_decode(Bin, FileType) ->
+ Lines = binary:split(Bin, <<"\n">>, [global]),
+ do_openssh_decode(FileType, Lines, []).
+
+do_openssh_decode(_, [], Acc) ->
+ lists:reverse(Acc);
+%% Ignore empty lines
+do_openssh_decode(FileType, [<<>> | Lines], Acc) ->
+ do_openssh_decode(FileType, Lines, Acc);
+%% Ignore lines that start with #
+do_openssh_decode(FileType,[<<"#", _/binary>> | Lines], Acc) ->
+ do_openssh_decode(FileType, Lines, Acc);
+do_openssh_decode(auth_keys = FileType, [Line | Lines], Acc) ->
+ Split = binary:split(Line, <<" ">>, [global]),
+ case mend_split(Split, []) of
+ %% ssh2
+ [Options, KeyType, Base64Enc, Comment] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ [{comment, string_decode(Comment)},
+ {options, comma_list_decode(Options)}]}
+ | Acc]);
+
+ [KeyType, Base64Enc, Comment] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ [{comment, string_decode(Comment)}]} | Acc]);
+ %% ssh1
+ [Options, Bits, Exponent, Modulus, Comment] ->
+ do_openssh_decode(FileType, Lines,
+ [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
+ [{comment, string_decode(Comment)},
+ {options, comma_list_decode(Options)},
+ {bits, integer_decode(Bits)}]} | Acc]);
+ [Bits, Exponent, Modulus, Comment] ->
+ do_openssh_decode(FileType, Lines,
+ [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
+ [{comment, string_decode(Comment)},
+ {bits, integer_decode(Bits)}]} | Acc])
+ end;
+
+do_openssh_decode(known_hosts = FileType, [Line | Lines], Acc) ->
+ case binary:split(Line, <<" ">>, [global]) of
+ %% ssh 2
+ [HostNames, KeyType, Base64Enc] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ [{hostnames, comma_list_decode(HostNames)}]}| Acc]);
+ [HostNames, KeyType, Base64Enc, Comment] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ [{comment, string_decode(Comment)},
+ {hostnames, comma_list_decode(HostNames)}]} | Acc]);
+ %% ssh 1
+ [HostNames, Bits, Exponent, Modulus, Comment] ->
+ do_openssh_decode(FileType, Lines,
+ [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
+ [{comment, string_decode(Comment)},
+ {hostnames, comma_list_decode(HostNames)},
+ {bits, integer_decode(Bits)}]} | Acc]);
+ [HostNames, Bits, Exponent, Modulus] ->
+ do_openssh_decode(FileType, Lines,
+ [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
+ [{comment, []},
+ {hostnames, comma_list_decode(HostNames)},
+ {bits, integer_decode(Bits)}]} | Acc])
+ end;
+
+do_openssh_decode(openssh_public_key = FileType, [Line | Lines], Acc) ->
+ case binary:split(Line, <<" ">>, [global]) of
+ [KeyType, Base64Enc, Comment0] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ Comment = string:strip(binary_to_list(Comment0), right, $\n),
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ [{comment, Comment}]} | Acc])
+ end.
+
+
+openssh_pubkey_decode(<<"ssh-rsa">>, Base64Enc) ->
+ <<?UINT32(StrLen), _:StrLen/binary,
+ ?UINT32(SizeE), E:SizeE/binary,
+ ?UINT32(SizeN), N:SizeN/binary>>
+ = base64:mime_decode(Base64Enc),
+ #'RSAPublicKey'{modulus = erlint(SizeN, N),
+ publicExponent = erlint(SizeE, E)};
+
+openssh_pubkey_decode(<<"ssh-dss">>, Base64Enc) ->
+ <<?UINT32(StrLen), _:StrLen/binary,
+ ?UINT32(SizeP), P:SizeP/binary,
+ ?UINT32(SizeQ), Q:SizeQ/binary,
+ ?UINT32(SizeG), G:SizeG/binary,
+ ?UINT32(SizeY), Y:SizeY/binary>>
+ = base64:mime_decode(Base64Enc),
+ {erlint(SizeY, Y),
+ #'Dss-Parms'{p = erlint(SizeP, P),
+ q = erlint(SizeQ, Q),
+ g = erlint(SizeG, G)}}.
+
+erlint(MPIntSize, MPIntValue) ->
+ Bits= MPIntSize * 8,
+ <<Integer:Bits/integer>> = MPIntValue,
+ Integer.
+
+ssh1_rsa_pubkey_decode(MBin, EBin) ->
+ #'RSAPublicKey'{modulus = integer_decode(MBin),
+ publicExponent = integer_decode(EBin)}.
+
+integer_decode(BinStr) ->
+ list_to_integer(binary_to_list(BinStr)).
+
+string_decode(BinStr) ->
+ binary_to_list(BinStr).
+
+unicode_decode(BinStr) ->
+ unicode:characters_to_list(BinStr).
+
+comma_list_decode(BinOpts) ->
+ CommaList = binary:split(BinOpts, <<",">>, [global]),
+ lists:map(fun(Item) ->
+ binary_to_list(Item)
+ end, CommaList).
+
+do_encode(rfc4716_public_key, Key, Attributes) ->
+ rfc4716_encode(Key, proplists:get_value(headers, Attributes, []), []);
+
+do_encode(Type, Key, Attributes) ->
+ openssh_encode(Type, Key, Attributes).
+
+rfc4716_encode(Key, [],[]) ->
+ erlang:iolist_to_binary([begin_marker(),"\n",
+ split_lines(base64:encode(ssh2_pubkey_encode(Key))),
+ "\n", end_marker(), "\n"]);
+rfc4716_encode(Key, [], [_|_] = Acc) ->
+ erlang:iolist_to_binary([begin_marker(), "\n",
+ lists:reverse(Acc),
+ split_lines(base64:encode(ssh2_pubkey_encode(Key))),
+ "\n", end_marker(), "\n"]);
+rfc4716_encode(Key, [ Header | Headers], Acc) ->
+ LinesStr = rfc4716_encode_header(Header),
+ rfc4716_encode(Key, Headers, [LinesStr | Acc]).
+
+rfc4716_encode_header({Tag, Value}) ->
+ TagLen = length(Tag),
+ ValueLen = length(Value),
+ case TagLen + 1 + ValueLen of
+ N when N > ?ENCODED_LINE_LENGTH ->
+ NumOfChars = ?ENCODED_LINE_LENGTH - (TagLen + 1),
+ {First, Rest} = lists:split(NumOfChars, Value),
+ [Tag,":" , First, [$\\], "\n", rfc4716_encode_value(Rest) , "\n"];
+ _ ->
+ [Tag, ":", Value, "\n"]
+ end.
+
+rfc4716_encode_value(Value) ->
+ case length(Value) of
+ N when N > ?ENCODED_LINE_LENGTH ->
+ {First, Rest} = lists:split(?ENCODED_LINE_LENGTH, Value),
+ [First, [$\\], "\n", rfc4716_encode_value(Rest)];
+ _ ->
+ Value
+ end.
+
+openssh_encode(openssh_public_key, Key, Attributes) ->
+ Comment = proplists:get_value(comment, Attributes),
+ Enc = base64:encode(ssh2_pubkey_encode(Key)),
+ erlang:iolist_to_binary([key_type(Key), " ", Enc, " ", Comment, "\n"]);
+
+openssh_encode(auth_keys, Key, Attributes) ->
+ Comment = proplists:get_value(comment, Attributes, ""),
+ Options = proplists:get_value(options, Attributes, undefined),
+ Bits = proplists:get_value(bits, Attributes, undefined),
+ case Bits of
+ undefined ->
+ openssh_ssh2_auth_keys_encode(Options, Key, Comment);
+ _ ->
+ openssh_ssh1_auth_keys_encode(Options, Bits, Key, Comment)
+ end;
+openssh_encode(known_hosts, Key, Attributes) ->
+ Comment = proplists:get_value(comment, Attributes, ""),
+ Hostnames = proplists:get_value(hostnames, Attributes),
+ Bits = proplists:get_value(bits, Attributes, undefined),
+ case Bits of
+ undefined ->
+ openssh_ssh2_know_hosts_encode(Hostnames, Key, Comment);
+ _ ->
+ openssh_ssh1_known_hosts_encode(Hostnames, Bits, Key, Comment)
+ end.
+
+openssh_ssh2_auth_keys_encode(undefined, Key, Comment) ->
+ erlang:iolist_to_binary([key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]);
+openssh_ssh2_auth_keys_encode(Options, Key, Comment) ->
+ erlang:iolist_to_binary([comma_list_encode(Options, []), " ",
+ key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]).
+
+openssh_ssh1_auth_keys_encode(undefined, Bits,
+ #'RSAPublicKey'{modulus = N, publicExponent = E},
+ Comment) ->
+ erlang:iolist_to_binary([integer_to_list(Bits), " ", integer_to_list(E), " ", integer_to_list(N),
+ line_end(Comment)]);
+openssh_ssh1_auth_keys_encode(Options, Bits,
+ #'RSAPublicKey'{modulus = N, publicExponent = E},
+ Comment) ->
+ erlang:iolist_to_binary([comma_list_encode(Options, []), " ", integer_to_list(Bits),
+ " ", integer_to_list(E), " ", integer_to_list(N), line_end(Comment)]).
+
+openssh_ssh2_know_hosts_encode(Hostnames, Key, Comment) ->
+ erlang:iolist_to_binary([comma_list_encode(Hostnames, []), " ",
+ key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]).
+
+openssh_ssh1_known_hosts_encode(Hostnames, Bits,
+ #'RSAPublicKey'{modulus = N, publicExponent = E},
+ Comment) ->
+ erlang:iolist_to_binary([comma_list_encode(Hostnames, [])," ", integer_to_list(Bits)," ",
+ integer_to_list(E)," ", integer_to_list(N), line_end(Comment)]).
+
+line_end("") ->
+ "\n";
+line_end(Comment) ->
+ [" ", Comment, "\n"].
+
+key_type(#'RSAPublicKey'{}) ->
+ <<"ssh-rsa">>;
+key_type({_, #'Dss-Parms'{}}) ->
+ <<"ssh-dss">>.
+
+comma_list_encode([Option], []) ->
+ Option;
+comma_list_encode([Option], Acc) ->
+ Acc ++ "," ++ Option;
+comma_list_encode([Option | Rest], []) ->
+ comma_list_encode(Rest, Option);
+comma_list_encode([Option | Rest], Acc) ->
+ comma_list_encode(Rest, Acc ++ "," ++ Option).
+
+ssh2_pubkey_encode(#'RSAPublicKey'{modulus = N, publicExponent = E}) ->
+ TypeStr = <<"ssh-rsa">>,
+ StrLen = size(TypeStr),
+ EBin = crypto:mpint(E),
+ NBin = crypto:mpint(N),
+ <<?UINT32(StrLen), TypeStr:StrLen/binary,
+ EBin/binary,
+ NBin/binary>>;
+ssh2_pubkey_encode({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) ->
+ TypeStr = <<"ssh-dss">>,
+ StrLen = size(TypeStr),
+ PBin = crypto:mpint(P),
+ QBin = crypto:mpint(Q),
+ GBin = crypto:mpint(G),
+ YBin = crypto:mpint(Y),
+ <<?UINT32(StrLen), TypeStr:StrLen/binary,
+ PBin/binary,
+ QBin/binary,
+ GBin/binary,
+ YBin/binary>>.
+
+mend_split([Part1, Part2 | Rest] = List, Acc) ->
+ case option_end(Part1, Part2) of
+ true ->
+ lists:reverse(Acc) ++ List;
+ false ->
+ case length(binary:matches(Part1, <<"\"">>)) of
+ N when N rem 2 == 0 ->
+ mend_split(Rest, [Part1 | Acc]);
+ _ ->
+ mend_split([<<Part1/binary, Part2/binary>> | Rest], Acc)
+ end
+ end.
+
+option_end(Part1, Part2) ->
+ (is_key_field(Part1) orelse is_bits_field(Part1))
+ orelse
+ (is_key_field(Part2) orelse is_bits_field(Part2)).
+
+is_key_field(<<"ssh-dss">>) ->
+ true;
+is_key_field(<<"ssh-rsa">>) ->
+ true;
+is_key_field(_) ->
+ false.
+
+is_bits_field(Part) ->
+ try list_to_integer(binary_to_list(Part)) of
+ _ ->
+ true
+ catch _:_ ->
+ false
+ end.
+
+split_lines(<<Text:?ENCODED_LINE_LENGTH/binary>>) ->
+ [Text];
+split_lines(<<Text:?ENCODED_LINE_LENGTH/binary, Rest/binary>>) ->
+ [Text, $\n | split_lines(Rest)];
+split_lines(Bin) ->
+ [Bin].
diff --git a/lib/public_key/src/public_key.app.src b/lib/public_key/src/public_key.app.src
index 60487946fa..1963bd05d4 100644
--- a/lib/public_key/src/public_key.app.src
+++ b/lib/public_key/src/public_key.app.src
@@ -1,9 +1,9 @@
{application, public_key,
[{description, "Public key infrastructure"},
{vsn, "%VSN%"},
- {modules, [
- public_key,
- pubkey_pem,
+ {modules, [ public_key,
+ pubkey_pem,
+ pubkey_ssh,
pubkey_cert,
pubkey_cert_records,
'OTP-PUB-KEY'
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 7e022da7e5..2901020e83 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -41,7 +41,8 @@
pkix_is_issuer/2,
pkix_issuer_id/2,
pkix_normalize_name/1,
- pkix_path_validation/3
+ pkix_path_validation/3,
+ ssh_decode/2, ssh_encode/2
]).
%% Deprecated
@@ -51,10 +52,6 @@
-deprecated({decode_private_key, 1, next_major_release}).
-deprecated({decode_private_key, 2, next_major_release}).
--type rsa_public_key() :: #'RSAPublicKey'{}.
--type rsa_private_key() :: #'RSAPrivateKey'{}.
--type dsa_private_key() :: #'DSAPrivateKey'{}.
--type dsa_public_key() :: {integer(), #'Dss-Parms'{}}.
-type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding'
| 'rsa_no_padding'.
-type public_crypt_options() :: [{rsa_pad, rsa_padding()}].
@@ -67,7 +64,6 @@
%%====================================================================
%% API
%%====================================================================
-
%%--------------------------------------------------------------------
-spec pem_decode(binary()) -> [pem_entry()].
%%
@@ -152,7 +148,7 @@ pem_entry_encode(Asn1Type, Entity,
{Asn1Type, DecryptDer, CipherInfo}.
%%--------------------------------------------------------------------
--spec der_decode(asn1_type(), der_encoded()) -> term().
+-spec der_decode(asn1_type(), Der::binary()) -> term().
%%
%% Description: Decodes a public key asn1 der encoded entity.
%%--------------------------------------------------------------------
@@ -166,7 +162,7 @@ der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) ->
end.
%%--------------------------------------------------------------------
--spec der_encode(asn1_type(), term()) -> der_encoded().
+-spec der_encode(asn1_type(), term()) -> Der::binary().
%%
%% Description: Encodes a public key entity with asn1 DER encoding.
%%--------------------------------------------------------------------
@@ -180,7 +176,7 @@ der_encode(Asn1Type, Entity) when is_atom(Asn1Type) ->
end.
%%--------------------------------------------------------------------
--spec pkix_decode_cert(der_encoded(), plain | otp) ->
+-spec pkix_decode_cert(Cert::binary(), plain | otp) ->
#'Certificate'{} | #'OTPCertificate'{}.
%%
%% Description: Decodes an asn1 der encoded pkix certificate. The otp
@@ -201,7 +197,7 @@ pkix_decode_cert(DerCert, otp) when is_binary(DerCert) ->
end.
%%--------------------------------------------------------------------
--spec pkix_encode(asn1_type(), term(), otp | plain) -> der_encoded().
+-spec pkix_encode(asn1_type(), term(), otp | plain) -> Der::binary().
%%
%% Description: Der encodes a certificate or part of a certificate.
%% This function must be used for encoding certificates or parts of certificates
@@ -361,7 +357,7 @@ verify(PlainText, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}})
crypto:mpint(G), crypto:mpint(Key)]).
%%--------------------------------------------------------------------
-spec pkix_sign(#'OTPTBSCertificate'{},
- rsa_private_key() | dsa_private_key()) -> der_encoded().
+ rsa_private_key() | dsa_private_key()) -> Der::binary().
%%
%% Description: Sign a pkix x.509 certificate. Returns the corresponding
%% der encoded 'Certificate'{}
@@ -370,7 +366,7 @@ pkix_sign(#'OTPTBSCertificate'{signature =
#'SignatureAlgorithm'{algorithm = Alg}
= SigAlg} = TBSCert, Key) ->
- Msg = pkix_encode('OTPTBSCertificate', TBSCert, otp),
+ Msg = pkix_encode('OTPTBSCertificate', TBSCert, otp),
DigestType = pubkey_cert:digest_type(Alg),
Signature = sign(Msg, DigestType, Key),
Cert = #'OTPCertificate'{tbsCertificate= TBSCert,
@@ -380,7 +376,7 @@ pkix_sign(#'OTPTBSCertificate'{signature =
pkix_encode('OTPCertificate', Cert, otp).
%%--------------------------------------------------------------------
--spec pkix_verify(der_encoded(), rsa_public_key()|
+-spec pkix_verify(Cert::binary(), rsa_public_key()|
dsa_public_key()) -> boolean().
%%
%% Description: Verify pkix x.509 certificate signature.
@@ -396,9 +392,9 @@ pkix_verify(DerCert, #'RSAPublicKey'{} = RSAKey)
verify(PlainText, DigestType, Signature, RSAKey).
%%--------------------------------------------------------------------
--spec pkix_is_issuer(Cert :: der_encoded()| #'OTPCertificate'{},
- IssuerCert :: der_encoded()|
- #'OTPCertificate'{}) -> boolean().
+-spec pkix_is_issuer(Cert::binary()| #'OTPCertificate'{},
+ IssuerCert::binary()|
+ #'OTPCertificate'{}) -> boolean().
%%
%% Description: Checks if <IssuerCert> issued <Cert>.
%%--------------------------------------------------------------------
@@ -414,7 +410,7 @@ pkix_is_issuer(#'OTPCertificate'{tbsCertificate = TBSCert},
Candidate#'OTPTBSCertificate'.subject).
%%--------------------------------------------------------------------
--spec pkix_is_self_signed(der_encoded()| #'OTPCertificate'{}) -> boolean().
+-spec pkix_is_self_signed(Cert::binary()| #'OTPCertificate'{}) -> boolean().
%%
%% Description: Checks if a Certificate is self signed.
%%--------------------------------------------------------------------
@@ -425,7 +421,7 @@ pkix_is_self_signed(Cert) when is_binary(Cert) ->
pkix_is_self_signed(OtpCert).
%%--------------------------------------------------------------------
--spec pkix_is_fixed_dh_cert(der_encoded()| #'OTPCertificate'{}) -> boolean().
+-spec pkix_is_fixed_dh_cert(Cert::binary()| #'OTPCertificate'{}) -> boolean().
%%
%% Description: Checks if a Certificate is a fixed Diffie-Hellman Cert.
%%--------------------------------------------------------------------
@@ -436,14 +432,14 @@ pkix_is_fixed_dh_cert(Cert) when is_binary(Cert) ->
pkix_is_fixed_dh_cert(OtpCert).
%%--------------------------------------------------------------------
--spec pkix_issuer_id(der_encoded()| #'OTPCertificate'{},
- IssuedBy :: self | other) ->
- {ok, {SerialNr :: integer(),
- Issuer :: {rdnSequence,
- [#'AttributeTypeAndValue'{}]}}}
+-spec pkix_issuer_id(Cert::binary()| #'OTPCertificate'{},
+ IssuedBy :: self | other) ->
+ {ok, {SerialNr :: integer(),
+ Issuer :: {rdnSequence,
+ [#'AttributeTypeAndValue'{}]}}}
| {error, Reason :: term()}.
%
-%% Description: Returns the issuer id.
+%% Description: Returns the issuer id.
%%--------------------------------------------------------------------
pkix_issuer_id(#'OTPCertificate'{} = OtpCert, self) ->
pubkey_cert:issuer_id(OtpCert, self);
@@ -456,8 +452,8 @@ pkix_issuer_id(Cert, Signed) when is_binary(Cert) ->
pkix_issuer_id(OtpCert, Signed).
%%--------------------------------------------------------------------
--spec pkix_normalize_name({rdnSequence,
- [#'AttributeTypeAndValue'{}]}) ->
+-spec pkix_normalize_name({rdnSequence,
+ [#'AttributeTypeAndValue'{}]}) ->
{rdnSequence,
[#'AttributeTypeAndValue'{}]}.
%%
@@ -468,8 +464,8 @@ pkix_normalize_name(Issuer) ->
pubkey_cert:normalize_general_name(Issuer).
%%--------------------------------------------------------------------
--spec pkix_path_validation(der_encoded()| #'OTPCertificate'{} | atom(),
- CertChain :: [der_encoded()] ,
+-spec pkix_path_validation(Cert::binary()| #'OTPCertificate'{} | atom(),
+ CertChain :: [binary()] ,
Options :: list()) ->
{ok, {PublicKeyInfo :: term(),
PolicyTree :: term()}} |
@@ -496,7 +492,7 @@ pkix_path_validation(TrustedCert, CertChain, Options) when
is_binary(TrustedCert) -> OtpCert = pkix_decode_cert(TrustedCert,
otp), pkix_path_validation(OtpCert, CertChain, Options);
-pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options)
+pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options)
when is_list(CertChain), is_list(Options) ->
MaxPathDefault = length(CertChain),
ValidationState = pubkey_cert:init_validation_state(TrustedCert,
@@ -505,6 +501,37 @@ pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options)
path_validation(CertChain, ValidationState).
%%--------------------------------------------------------------------
+-spec ssh_decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}].
+%%
+%% Description: Decodes a ssh file-binary. In the case of know_hosts
+%% or auth_keys the binary may include one or more lines of the
+%% file. Returns a list of public keys and their attributes, possible
+%% attribute values depends on the file type represented by the
+%% binary.
+%%--------------------------------------------------------------------
+ssh_decode(SshBin, Type) when is_binary(SshBin),
+ Type == public_key;
+ Type == rfc4716_public_key;
+ Type == openssh_public_key;
+ Type == auth_keys;
+ Type == known_hosts ->
+ pubkey_ssh:decode(SshBin, Type).
+
+%%--------------------------------------------------------------------
+-spec ssh_encode([{public_key(), Attributes::list()}], ssh_file()) ->
+ binary().
+%% Description: Encodes a list of ssh file entries (public keys and
+%% attributes) to a binary. Possible attributes depends on the file
+%% type.
+%%--------------------------------------------------------------------
+ssh_encode(Entries, Type) when is_list(Entries),
+ Type == rfc4716_public_key;
+ Type == openssh_public_key;
+ Type == auth_keys;
+ Type == known_hosts ->
+ pubkey_ssh:encode(Entries, Type).
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -518,7 +545,6 @@ decrypt_public(CipherText, N,E, Options) ->
crypto:rsa_public_decrypt(CipherText,[crypto:mpint(E), crypto:mpint(N)],
Padding).
-
path_validation([], #path_validation_state{working_public_key_algorithm
= Algorithm,
working_public_key =
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index 6c482f9c30..b11e4d092a 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -102,11 +102,23 @@ end_per_testcase(_TestCase, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app, pk_decode_encode, encrypt_decrypt, sign_verify,
+ [app,
+ {group, pem_decode_encode},
+ {group, ssh_public_key_decode_encode},
+ encrypt_decrypt,
+ {group, sign_verify},
pkix, pkix_path_validation, deprecated].
groups() ->
- [].
+ [{pem_decode_encode, [], [dsa_pem, rsa_pem, encrypted_pem,
+ dh_pem, cert_pem]},
+ {ssh_public_key_decode_encode, [],
+ [ssh_rsa_public_key, ssh_dsa_public_key, ssh_rfc4716_rsa_comment,
+ ssh_rfc4716_dsa_comment, ssh_rfc4716_rsa_subject, ssh_known_hosts,
+ ssh_auth_keys, ssh1_known_hosts, ssh1_auth_keys, ssh_openssh_public_key_with_comment,
+ ssh_openssh_public_key_long_header]},
+ {sign_verify, [], [rsa_sign_verify, dsa_sign_verify]}
+ ].
init_per_group(_GroupName, Config) ->
Config.
@@ -125,22 +137,20 @@ app(suite) ->
app(Config) when is_list(Config) ->
ok = test_server:app_test(public_key).
-pk_decode_encode(doc) ->
- ["Tests pem_decode/1, pem_encode/1, "
- "der_decode/2, der_encode/2, "
- "pem_entry_decode/1, pem_entry_decode/2,"
- "pem_entry_encode/2, pem_entry_encode/3."];
+%%--------------------------------------------------------------------
-pk_decode_encode(suite) ->
+dsa_pem(doc) ->
+ [""];
+dsa_pem(suite) ->
[];
-pk_decode_encode(Config) when is_list(Config) ->
+dsa_pem(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
- [{'DSAPrivateKey', DerDSAKey, not_encrypted} = Entry0 ] =
- erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")),
-
+ [{'DSAPrivateKey', DerDSAKey, not_encrypted} = Entry0 ] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")),
+
DSAKey = public_key:der_decode('DSAPrivateKey', DerDSAKey),
-
+
DSAKey = public_key:pem_entry_decode(Entry0),
{ok, DSAPubPem} = file:read_file(filename:join(Datadir, "dsa_pub.pem")),
@@ -150,74 +160,107 @@ pk_decode_encode(Config) when is_list(Config) ->
true = check_entry_type(DSAPubKey, 'DSAPublicKey'),
PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', DSAPubKey),
DSAPubPemNoEndNewLines = strip_ending_newlines(DSAPubPem),
- DSAPubPemEndNoNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])),
-
- [{'RSAPrivateKey', DerRSAKey, not_encrypted} = Entry1 ] =
+ DSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])).
+
+%%--------------------------------------------------------------------
+
+rsa_pem(doc) ->
+ [""];
+rsa_pem(suite) ->
+ [];
+rsa_pem(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+ [{'RSAPrivateKey', DerRSAKey, not_encrypted} = Entry0 ] =
erl_make_certs:pem_to_der(filename:join(Datadir, "client_key.pem")),
-
+
RSAKey0 = public_key:der_decode('RSAPrivateKey', DerRSAKey),
+
+ RSAKey0 = public_key:pem_entry_decode(Entry0),
- RSAKey0 = public_key:pem_entry_decode(Entry1),
-
- [{'RSAPrivateKey', _, {_,_}} = Entry2] =
+ [{'RSAPrivateKey', _, {_,_}} = Entry1] =
erl_make_certs:pem_to_der(filename:join(Datadir, "rsa.pem")),
-
- true = check_entry_type(public_key:pem_entry_decode(Entry2, "abcd1234"),
+
+ true = check_entry_type(public_key:pem_entry_decode(Entry1, "abcd1234"),
'RSAPrivateKey'),
{ok, RSAPubPem} = file:read_file(filename:join(Datadir, "rsa_pub.pem")),
- [{'SubjectPublicKeyInfo', _, _} = PubEntry1] =
+ [{'SubjectPublicKeyInfo', _, _} = PubEntry0] =
public_key:pem_decode(RSAPubPem),
- RSAPubKey = public_key:pem_entry_decode(PubEntry1),
+ RSAPubKey = public_key:pem_entry_decode(PubEntry0),
true = check_entry_type(RSAPubKey, 'RSAPublicKey'),
- PubEntry1 = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey),
+ PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey),
RSAPubPemNoEndNewLines = strip_ending_newlines(RSAPubPem),
- RSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry1])),
+ RSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])),
{ok, RSARawPem} = file:read_file(filename:join(Datadir, "rsa_pub_key.pem")),
- [{'RSAPublicKey', _, _} = PubEntry2] =
+ [{'RSAPublicKey', _, _} = PubEntry1] =
public_key:pem_decode(RSARawPem),
- RSAPubKey = public_key:pem_entry_decode(PubEntry2),
+ RSAPubKey = public_key:pem_entry_decode(PubEntry1),
RSARawPemNoEndNewLines = strip_ending_newlines(RSARawPem),
- RSARawPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry2])),
+ RSARawPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry1])).
+
+%%--------------------------------------------------------------------
+
+encrypted_pem(doc) ->
+ [""];
+encrypted_pem(suite) ->
+ [];
+encrypted_pem(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ [{'RSAPrivateKey', DerRSAKey, not_encrypted}] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "client_key.pem")),
+
+ RSAKey = public_key:der_decode('RSAPrivateKey', DerRSAKey),
Salt0 = crypto:rand_bytes(8),
- Entry3 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey0,
+ Entry0 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey,
{{"DES-EDE3-CBC", Salt0}, "1234abcd"}),
-
- RSAKey0 = public_key:pem_entry_decode(Entry3,"1234abcd"),
-
+ RSAKey = public_key:pem_entry_decode(Entry0,"1234abcd"),
Des3KeyFile = filename:join(Datadir, "des3_client_key.pem"),
+ erl_make_certs:der_to_pem(Des3KeyFile, [Entry0]),
+ [{'RSAPrivateKey', _, {"DES-EDE3-CBC", Salt0}}] =
+ erl_make_certs:pem_to_der(Des3KeyFile),
- erl_make_certs:der_to_pem(Des3KeyFile, [Entry3]),
-
- [{'RSAPrivateKey', _, {"DES-EDE3-CBC", Salt0}}] = erl_make_certs:pem_to_der(Des3KeyFile),
-
Salt1 = crypto:rand_bytes(8),
- Entry4 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey0,
+ Entry1 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey,
{{"DES-CBC", Salt1}, "4567efgh"}),
-
-
DesKeyFile = filename:join(Datadir, "des_client_key.pem"),
+ erl_make_certs:der_to_pem(DesKeyFile, [Entry1]),
+ [{'RSAPrivateKey', _, {"DES-CBC", Salt1}} =Entry2] =
+ erl_make_certs:pem_to_der(DesKeyFile),
+ true = check_entry_type(public_key:pem_entry_decode(Entry2, "4567efgh"),
+ 'RSAPrivateKey').
- erl_make_certs:der_to_pem(DesKeyFile, [Entry4]),
-
- [{'RSAPrivateKey', _, {"DES-CBC", Salt1}} =Entry5] = erl_make_certs:pem_to_der(DesKeyFile),
-
-
- true = check_entry_type(public_key:pem_entry_decode(Entry5, "4567efgh"),
- 'RSAPrivateKey'),
+%%--------------------------------------------------------------------
- [{'DHParameter', DerDH, not_encrypted} = Entry6] =
+dh_pem(doc) ->
+ [""];
+dh_pem(suite) ->
+ [];
+dh_pem(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+ [{'DHParameter', DerDH, not_encrypted} = Entry] =
erl_make_certs:pem_to_der(filename:join(Datadir, "dh.pem")),
-
- erl_make_certs:der_to_pem(filename:join(Datadir, "new_dh.pem"), [Entry6]),
+
+ erl_make_certs:der_to_pem(filename:join(Datadir, "new_dh.pem"), [Entry]),
DHParameter = public_key:der_decode('DHParameter', DerDH),
- DHParameter = public_key:pem_entry_decode(Entry6),
+ DHParameter = public_key:pem_entry_decode(Entry),
- Entry6 = public_key:pem_entry_encode('DHParameter', DHParameter),
+ Entry = public_key:pem_entry_encode('DHParameter', DHParameter).
+%%--------------------------------------------------------------------
+cert_pem(doc) ->
+ [""];
+cert_pem(suite) ->
+ [];
+cert_pem(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ [Entry0] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")),
+
[{'Certificate', DerCert, not_encrypted} = Entry7] =
erl_make_certs:pem_to_der(filename:join(Datadir, "client_cert.pem")),
@@ -227,15 +270,232 @@ pk_decode_encode(Config) when is_list(Config) ->
CertEntries = [{'Certificate', _, not_encrypted} = CertEntry0,
{'Certificate', _, not_encrypted} = CertEntry1] =
erl_make_certs:pem_to_der(filename:join(Datadir, "cacerts.pem")),
-
+
ok = erl_make_certs:der_to_pem(filename:join(Datadir, "wcacerts.pem"), CertEntries),
ok = erl_make_certs:der_to_pem(filename:join(Datadir, "wdsa.pem"), [Entry0]),
NewCertEntries = erl_make_certs:pem_to_der(filename:join(Datadir, "wcacerts.pem")),
true = lists:member(CertEntry0, NewCertEntries),
true = lists:member(CertEntry1, NewCertEntries),
- [Entry0] = erl_make_certs:pem_to_der(filename:join(Datadir, "wdsa.pem")),
- ok.
+ [Entry0] = erl_make_certs:pem_to_der(filename:join(Datadir, "wdsa.pem")).
+
+%%--------------------------------------------------------------------
+ssh_rsa_public_key(doc) ->
+ "";
+ssh_rsa_public_key(suite) ->
+ [];
+ssh_rsa_public_key(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_pub")),
+ [{PubKey, Attributes1}] = public_key:ssh_decode(RSARawSsh2, public_key),
+ [{PubKey, Attributes1}] = public_key:ssh_decode(RSARawSsh2, rfc4716_public_key),
+
+ {ok, RSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_rsa_pub")),
+ [{PubKey, Attributes2}] = public_key:ssh_decode(RSARawOpenSsh, public_key),
+ [{PubKey, Attributes2}] = public_key:ssh_decode(RSARawOpenSsh, openssh_public_key),
+
+ %% Can not check EncodedSSh == RSARawSsh2 and EncodedOpenSsh
+ %% = RSARawOpenSsh as line breakpoints may differ
+
+ EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key),
+ EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key),
+
+ [{PubKey, Attributes1}] =
+ public_key:ssh_decode(EncodedSSh, public_key),
+ [{PubKey, Attributes2}] =
+ public_key:ssh_decode(EncodedOpenSsh, public_key).
+
+%%--------------------------------------------------------------------
+
+ssh_dsa_public_key(doc) ->
+ "";
+ssh_dsa_public_key(suite) ->
+ [];
+ssh_dsa_public_key(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_pub")),
+ [{PubKey, Attributes1}] = public_key:ssh_decode(DSARawSsh2, public_key),
+ [{PubKey, Attributes1}] = public_key:ssh_decode(DSARawSsh2, rfc4716_public_key),
+
+ {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_pub")),
+ [{PubKey, Attributes2}] = public_key:ssh_decode(DSARawOpenSsh, public_key),
+ [{PubKey, Attributes2}] = public_key:ssh_decode(DSARawOpenSsh, openssh_public_key),
+
+ %% Can not check EncodedSSh == DSARawSsh2 and EncodedOpenSsh
+ %% = DSARawOpenSsh as line breakpoints may differ
+
+ EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key),
+ EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key),
+
+ [{PubKey, Attributes1}] =
+ public_key:ssh_decode(EncodedSSh, public_key),
+ [{PubKey, Attributes2}] =
+ public_key:ssh_decode(EncodedOpenSsh, public_key).
+
+%%--------------------------------------------------------------------
+ssh_rfc4716_rsa_comment(doc) ->
+ "Test comment header and rsa key";
+ssh_rfc4716_rsa_comment(suite) ->
+ [];
+ssh_rfc4716_rsa_comment(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_comment_pub")),
+ [{#'RSAPublicKey'{} = PubKey, Attributes}] =
+ public_key:ssh_decode(RSARawSsh2, public_key),
+
+ Headers = proplists:get_value(headers, Attributes),
+
+ Value = proplists:get_value("Comment", Headers, undefined),
+ true = Value =/= undefined,
+ RSARawSsh2 = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key).
+
+%%--------------------------------------------------------------------
+ssh_rfc4716_dsa_comment(doc) ->
+ "Test comment header and dsa key";
+ssh_rfc4716_dsa_comment(suite) ->
+ [];
+ssh_rfc4716_dsa_comment(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_comment_pub")),
+ [{{_, #'Dss-Parms'{}} = PubKey, Attributes}] =
+ public_key:ssh_decode(DSARawSsh2, public_key),
+
+ Headers = proplists:get_value(headers, Attributes),
+
+ Value = proplists:get_value("Comment", Headers, undefined),
+ true = Value =/= undefined,
+
+ %% Can not check Encoded == DSARawSsh2 as line continuation breakpoints may differ
+ Encoded = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key),
+ [{PubKey, Attributes}] =
+ public_key:ssh_decode(Encoded, public_key).
+
+%%--------------------------------------------------------------------
+ssh_rfc4716_rsa_subject(doc) ->
+ "Test another header value than comment";
+ssh_rfc4716_rsa_subject(suite) ->
+ [];
+ssh_rfc4716_rsa_subject(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_subject_pub")),
+ [{#'RSAPublicKey'{} = PubKey, Attributes}] =
+ public_key:ssh_decode(RSARawSsh2, public_key),
+
+ Headers = proplists:get_value(headers, Attributes),
+
+ Value = proplists:get_value("Subject", Headers, undefined),
+ true = Value =/= undefined,
+
+ %% Can not check Encoded == RSARawSsh2 as line continuation breakpoints may differ
+ Encoded = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key),
+ [{PubKey, Attributes}] =
+ public_key:ssh_decode(Encoded, public_key).
+
+%%--------------------------------------------------------------------
+ssh_known_hosts(doc) ->
+ "";
+ssh_known_hosts(suite) ->
+ [];
+ssh_known_hosts(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "known_hosts")),
+ [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}] = Decoded =
+ public_key:ssh_decode(SshKnownHosts, known_hosts),
+
+ Value1 = proplists:get_value(hostnames, Attributes1, undefined),
+ Value2 = proplists:get_value(hostnames, Attributes2, undefined),
+ true = (Value1 =/= undefined) and (Value2 =/= undefined),
+
+ Encoded = public_key:ssh_encode(Decoded, known_hosts),
+ Decoded = public_key:ssh_decode(Encoded, known_hosts).
+
+%%--------------------------------------------------------------------
+
+ssh1_known_hosts(doc) ->
+ "";
+ssh1_known_hosts(suite) ->
+ [];
+ssh1_known_hosts(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "ssh1_known_hosts")),
+ [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}] = Decoded =
+ public_key:ssh_decode(SshKnownHosts, known_hosts),
+
+ Value1 = proplists:get_value(hostnames, Attributes1, undefined),
+ Value2 = proplists:get_value(hostnames, Attributes2, undefined),
+ true = (Value1 =/= undefined) and (Value2 =/= undefined),
+
+ Encoded = public_key:ssh_encode(Decoded, known_hosts),
+ Decoded = public_key:ssh_decode(Encoded, known_hosts).
+
+%%--------------------------------------------------------------------
+ssh_auth_keys(doc) ->
+ "";
+ssh_auth_keys(suite) ->
+ [];
+ssh_auth_keys(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "auth_keys")),
+ [{#'RSAPublicKey'{}, Attributes1}, {{_, #'Dss-Parms'{}}, _Attributes2}] = Decoded =
+ public_key:ssh_decode(SshAuthKeys, auth_keys),
+
+ Value1 = proplists:get_value(options, Attributes1, undefined),
+ true = Value1 =/= undefined,
+
+ Encoded = public_key:ssh_encode(Decoded, auth_keys),
+ Decoded = public_key:ssh_decode(Encoded, auth_keys).
+
+%%--------------------------------------------------------------------
+ssh1_auth_keys(doc) ->
+ "";
+ssh1_auth_keys(suite) ->
+ [];
+ssh1_auth_keys(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "ssh1_auth_keys")),
+ [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}] = Decoded =
+ public_key:ssh_decode(SshAuthKeys, auth_keys),
+
+ Value1 = proplists:get_value(bits, Attributes1, undefined),
+ Value2 = proplists:get_value(bits, Attributes2, undefined),
+ true = (Value1 =/= undefined) and (Value2 =/= undefined),
+
+ Encoded = public_key:ssh_encode(Decoded, auth_keys),
+ Decoded = public_key:ssh_decode(Encoded, auth_keys).
+
+%%--------------------------------------------------------------------
+ssh_openssh_public_key_with_comment(doc) ->
+ "Test that emty lines and lines starting with # are ignored";
+ssh_openssh_public_key_with_comment(suite) ->
+ [];
+ssh_openssh_public_key_with_comment(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_with_comment_pub")),
+ [{{_, #'Dss-Parms'{}}, _}] = public_key:ssh_decode(DSARawOpenSsh, openssh_public_key).
+
+%%--------------------------------------------------------------------
+ssh_openssh_public_key_long_header(doc) ->
+ "Test that long headers are handled";
+ssh_openssh_public_key_long_header(suite) ->
+ [];
+ssh_openssh_public_key_long_header(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok,RSARawOpenSsh} = file:read_file(filename:join(Datadir, "ssh_rsa_long_header_pub")),
+ [{#'RSAPublicKey'{}, _}] = Decoded = public_key:ssh_decode(RSARawOpenSsh, public_key),
+
+ Encoded = public_key:ssh_encode(Decoded, rfc4716_public_key),
+ Decoded = public_key:ssh_decode(Encoded, rfc4716_public_key).
%%--------------------------------------------------------------------
encrypt_decrypt(doc) ->
@@ -258,44 +518,49 @@ encrypt_decrypt(Config) when is_list(Config) ->
ok.
%%--------------------------------------------------------------------
-sign_verify(doc) ->
- ["Checks that we can sign and verify signatures."];
-sign_verify(suite) ->
+rsa_sign_verify(doc) ->
+ ["Checks that we can sign and verify rsa signatures."];
+rsa_sign_verify(suite) ->
[];
-sign_verify(Config) when is_list(Config) ->
- %% Make cert signs and validates the signature using RSA and DSA
+rsa_sign_verify(Config) when is_list(Config) ->
Ca = {_, CaKey} = erl_make_certs:make_cert([]),
+ {Cert1, _} = erl_make_certs:make_cert([{key, dsa}, {issuer, Ca}]),
PrivateRSA = #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} =
public_key:pem_entry_decode(CaKey),
-
- CertInfo = {Cert1,CertKey1} = erl_make_certs:make_cert([{key, dsa}, {issuer, Ca}]),
-
PublicRSA = #'RSAPublicKey'{modulus=Mod, publicExponent=Exp},
true = public_key:pkix_verify(Cert1, PublicRSA),
- {Cert2,_CertKey} = erl_make_certs:make_cert([{issuer, CertInfo}]),
-
- #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y, x=_X} =
- public_key:pem_entry_decode(CertKey1),
- true = public_key:pkix_verify(Cert2, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}),
-
- %% RSA sign
Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")),
-
RSASign = public_key:sign(Msg, sha, PrivateRSA),
true = public_key:verify(Msg, sha, RSASign, PublicRSA),
false = public_key:verify(<<1:8, Msg/binary>>, sha, RSASign, PublicRSA),
false = public_key:verify(Msg, sha, <<1:8, RSASign/binary>>, PublicRSA),
RSASign1 = public_key:sign(Msg, md5, PrivateRSA),
- true = public_key:verify(Msg, md5, RSASign1, PublicRSA),
+ true = public_key:verify(Msg, md5, RSASign1, PublicRSA).
- %% DSA sign
+%%--------------------------------------------------------------------
+
+dsa_sign_verify(doc) ->
+ ["Checks that we can sign and verify dsa signatures."];
+dsa_sign_verify(suite) ->
+ [];
+dsa_sign_verify(Config) when is_list(Config) ->
+ Ca = erl_make_certs:make_cert([]),
+ CertInfo = {_,CertKey1} = erl_make_certs:make_cert([{key, dsa}, {issuer, Ca}]),
+ {Cert2,_CertKey} = erl_make_certs:make_cert([{issuer, CertInfo}]),
+
+ #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y, x=_X} =
+ public_key:pem_entry_decode(CertKey1),
+ true = public_key:pkix_verify(Cert2, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}),
+
Datadir = ?config(data_dir, Config),
[DsaKey = {'DSAPrivateKey', _, _}] =
erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")),
DSAPrivateKey = public_key:pem_entry_decode(DsaKey),
#'DSAPrivateKey'{p=P1, q=Q1, g=G1, y=Y1, x=_X1} = DSAPrivateKey,
+
+ Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")),
DSASign = public_key:sign(Msg, sha, DSAPrivateKey),
DSAPublicKey = Y1,
DSAParams = #'Dss-Parms'{p=P1, q=Q1, g=G1},
@@ -312,9 +577,8 @@ sign_verify(Config) when is_list(Config) ->
false = public_key:verify(<<1:8, RestDigest/binary>>, none, DigestSign,
{DSAPublicKey, DSAParams}),
false = public_key:verify(Digest, none, <<1:8, DigestSign/binary>>,
- {DSAPublicKey, DSAParams}),
-
- ok.
+ {DSAPublicKey, DSAParams}).
+
%%--------------------------------------------------------------------
pkix(doc) ->
"Misc pkix tests not covered elsewhere";
diff --git a/lib/public_key/test/public_key_SUITE_data/auth_keys b/lib/public_key/test/public_key_SUITE_data/auth_keys
new file mode 100644
index 0000000000..0c4b47edde
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/auth_keys
@@ -0,0 +1,3 @@
+command="dump /home",no-pty,no-port-forwarding ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmtisaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRuLDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368+dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNWjeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4fuKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV225JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRMIB+X+OTUUI8= dhopson@VMUbuntu-DSH
+
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH
diff --git a/lib/public_key/test/public_key_SUITE_data/known_hosts b/lib/public_key/test/public_key_SUITE_data/known_hosts
new file mode 100644
index 0000000000..30fc3b1fe8
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/known_hosts
@@ -0,0 +1,3 @@
+hostname.domain.com,192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM=
+
+|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM= [email protected]
diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub b/lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub
new file mode 100644
index 0000000000..a765ba8189
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH
diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub b/lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub
new file mode 100644
index 0000000000..d5a34a3f78
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub
@@ -0,0 +1,3 @@
+#This should be ignored!!
+
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH
diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub b/lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub
new file mode 100644
index 0000000000..0a0838db40
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmtisaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRuLDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368+dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNWjeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4fuKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV225JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRMIB+X+OTUUI8= dhopson@VMUbuntu-DSH
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys b/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys
new file mode 100644
index 0000000000..c91f4e4679
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys
@@ -0,0 +1,3 @@
+1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
+
+command="dump /home",no-pty,no-port-forwarding 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts b/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts
new file mode 100644
index 0000000000..ec668fe05b
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts
@@ -0,0 +1,2 @@
+hostname.domain.com,192.168.0.1 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
+hostname2.domain.com,192.168.0.2 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub
new file mode 100644
index 0000000000..ca5089dbd7
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub
@@ -0,0 +1,13 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: This is my public key for use on \
+servers which I don't like.
+AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbET
+W6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdH
+YI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5c
+vwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGf
+J0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAA
+vioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACB
+AN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HS
+n24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5
+sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub
new file mode 100644
index 0000000000..a5e38be81a
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub
@@ -0,0 +1,12 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: DSA Public Key for use with MyIsp
+AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbET
+W6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdH
+YI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5c
+vwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGf
+J0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAA
+vioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACB
+AN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HS
+n24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5
+sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub
new file mode 100644
index 0000000000..e4d446147c
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub
@@ -0,0 +1,7 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: "1024-bit RSA, converted from OpenSSH by [email protected]"
+x-command: /home/me/bin/lock-in-guest.sh
+AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb
+YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ
+5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE=
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub
new file mode 100644
index 0000000000..761088b517
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub
@@ -0,0 +1,13 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o
+39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS
+7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmt
+isaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2
+sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRu
+LDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368
++dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNW
+jeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4f
+uKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV22
+5JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRM
+IB+X+OTUUI8=
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub
new file mode 100644
index 0000000000..8b8ccda8ba
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub
@@ -0,0 +1,8 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Subject: me
+Comment: 1024-bit rsa, created by [email protected] Mon Jan 15 \
+08:31:24 2001
+AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
+596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
+soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub b/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub
new file mode 100644
index 0000000000..7b42ced93e
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub
@@ -0,0 +1,9 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: This is an example of a very very very very looooooooooooo\
+ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong\
+commment
+x-command: /home/me/bin/lock-in-guest.sh
+AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb
+YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ
+5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE=
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub b/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub
new file mode 100644
index 0000000000..7b42ced93e
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub
@@ -0,0 +1,9 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: This is an example of a very very very very looooooooooooo\
+ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong\
+commment
+x-command: /home/me/bin/lock-in-guest.sh
+AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb
+YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ
+5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE=
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index d7cad8b29e..9743289ca6 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -1318,7 +1318,7 @@ decode(#sys{} = Sys, [{Key, Val} | KeyVals], Status) ->
Val =:= none;
Val =:= undefined ->
{Sys#sys{embedded_app_type = Val}, Status};
- app_file when Val =:= keep; Val =:= strip, Val =:= all ->
+ app_file when Val =:= keep; Val =:= strip; Val =:= all ->
{Sys#sys{app_file = Val}, Status};
debug_info when Val =:= keep; Val =:= strip ->
{Sys#sys{debug_info = Val}, Status};
diff --git a/lib/reltool/test/reltool_app_SUITE.erl b/lib/reltool/test/reltool_app_SUITE.erl
index 97076589ba..a6e00cde08 100644
--- a/lib/reltool/test/reltool_app_SUITE.erl
+++ b/lib/reltool/test/reltool_app_SUITE.erl
@@ -45,15 +45,16 @@ init_per_suite(Config) ->
end_per_suite(Config) ->
reltool_test_lib:end_per_suite(Config).
+init_per_testcase(undef_funcs=Case, Config) ->
+ case test_server:is_debug() of
+ true ->
+ {skip,"Debug-compiled emulator -- far too slow"};
+ false ->
+ Config2 = [{tc_timeout, timer:minutes(10)} | Config],
+ reltool_test_lib:init_per_testcase(Case, Config2)
+ end;
init_per_testcase(Case, Config) ->
- Config2 =
- case Case of
- undef_funcs ->
- [{tc_timeout, timer:minutes(10)} | Config];
- _ ->
- Config
- end,
- reltool_test_lib:init_per_testcase(Case, Config2).
+ reltool_test_lib:init_per_testcase(Case, Config).
end_per_testcase(Func,Config) ->
reltool_test_lib:end_per_testcase(Func,Config).
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index ef3076f305..b77560db94 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -52,7 +52,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[start_server, set_config, create_release,
create_script, create_target, create_embedded,
- create_standalone, create_old_target].
+ create_standalone, create_old_target,
+ otp_9135].
groups() ->
[].
@@ -110,6 +111,37 @@ set_config(_Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% OTP-9135, test that app_file option can be set to all | keep | strip
+
+otp_9135(TestInfo) when is_atom(TestInfo) ->
+ reltool_test_lib:tc_info(TestInfo);
+otp_9135(_Config) ->
+ Libs = lists:sort(erl_libs()),
+ StrippedDefaultSys =
+ case Libs of
+ [] -> [];
+ _ -> {lib_dirs, Libs}
+ end,
+
+ Config1 = {sys,[{app_file, keep}]}, % this is the default
+ {ok, Pid1} = ?msym({ok, _}, reltool:start_server([{config, Config1}])),
+ ?m({ok, {sys,StrippedDefaultSys}}, reltool:get_config(Pid1)),
+ ?m(ok, reltool:stop(Pid1)),
+
+ Config2 = {sys,[{app_file, strip}]},
+ {ok, Pid2} = ?msym({ok, _}, reltool:start_server([{config, Config2}])),
+ ExpectedConfig2 = StrippedDefaultSys++[{app_file,strip}],
+ ?m({ok, {sys,ExpectedConfig2}}, reltool:get_config(Pid2)),
+ ?m(ok, reltool:stop(Pid2)),
+
+ Config3 = {sys,[{app_file, all}]},
+ {ok, Pid3} = ?msym({ok, _}, reltool:start_server([{config, Config3}])),
+ ExpectedConfig3 = StrippedDefaultSys++[{app_file,all}],
+ ?m({ok, {sys,ExpectedConfig3}}, reltool:get_config(Pid3)),
+ ?m(ok, reltool:stop(Pid3)),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate releases
create_release(TestInfo) when is_atom(TestInfo) ->
diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl
index 4c43277848..b60aa847df 100644
--- a/lib/sasl/src/release_handler.erl
+++ b/lib/sasl/src/release_handler.erl
@@ -791,7 +791,7 @@ check_rel(Root, RelFile, Masters) ->
check_rel(Root, RelFile, LibDirs, Masters) ->
case consult(RelFile, Masters) of
{ok, [RelData]} ->
- check_rel_data(RelData, Root, LibDirs);
+ check_rel_data(RelData, Root, LibDirs, Masters);
{ok, _} ->
throw({error, {bad_rel_file, RelFile}});
{error, Reason} when is_tuple(Reason) ->
@@ -800,7 +800,8 @@ check_rel(Root, RelFile, LibDirs, Masters) ->
throw({error, {FileError, RelFile}})
end.
-check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs) ->
+check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs,
+ Masters) ->
Libs2 =
lists:map(fun(LibSpec) ->
Lib = element(1, LibSpec),
@@ -810,7 +811,7 @@ check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs) ->
case lists:keysearch(Lib, 1, LibDirs) of
{value, {_Lib, _Vsn, Dir}} ->
Path = filename:join(Dir,LibName),
- check_path(Path),
+ check_path(Path, Masters),
Path;
_ ->
filename:join([Root, "lib", LibName])
@@ -820,19 +821,34 @@ check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs) ->
Libs),
#release{name = Name, vsn = Vsn, erts_vsn = EVsn,
libs = Libs2, status = unpacking};
-check_rel_data(RelData, _Root, _LibDirs) ->
+check_rel_data(RelData, _Root, _LibDirs, _Masters) ->
throw({error, {bad_rel_data, RelData}}).
check_path(Path) ->
- case file:read_file_info(Path) of
- {ok, Info} when Info#file_info.type==directory ->
- ok;
- {ok, _Info} ->
- throw({error, {not_a_directory, Path}});
- {error, _Reason} ->
- throw({error, {no_such_directory, Path}})
- end.
-
+ check_path_response(Path, file:read_file_info(Path)).
+check_path(Path, false) -> check_path(Path);
+check_path(Path, Masters) -> check_path_master(Masters, Path).
+
+%%-----------------------------------------------------------------
+%% check_path at any master node.
+%% If the path does not exist or is not a directory
+%% at one node it should not exist at any other node either.
+%%-----------------------------------------------------------------
+check_path_master([Master|Ms], Path) ->
+ case rpc:call(Master, file, read_file_info, [Path]) of
+ {badrpc, _} -> consult_master(Ms, Path);
+ Res -> check_path_response(Path, Res)
+ end;
+check_path_master([], _Path) ->
+ {error, no_master}.
+
+check_path_response(_Path, {ok, Info}) when Info#file_info.type==directory ->
+ ok;
+check_path_response(Path, {ok, _Info}) ->
+ throw({error, {not_a_directory, Path}});
+check_path_response(Path, {error, _Reason}) ->
+ throw({error, {no_such_directory, Path}}).
+
do_check_install_release(RelDir, Vsn, Releases, Masters) ->
case lists:keysearch(Vsn, #release.vsn, Releases) of
{value, #release{status = current}} ->
diff --git a/lib/sasl/src/release_handler_1.erl b/lib/sasl/src/release_handler_1.erl
index 9c0edf4e99..8d050fb7b0 100644
--- a/lib/sasl/src/release_handler_1.erl
+++ b/lib/sasl/src/release_handler_1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
%% External exports
-export([eval_script/3, eval_script/4, check_script/2]).
--export([get_vsn/1]). %% exported because used in a test case
+-export([get_current_vsn/1]). %% exported because used in a test case
-record(eval_state, {bins = [], stopped = [], suspended = [], apps = [],
libdirs, unpurged = [], vsns = [], newlibs = [],
@@ -223,7 +223,7 @@ eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) ->
FName = filename:join(Ebin, File),
case erl_prim_loader:get_file(FName) of
{ok, Bin, FName2} ->
- NVsns = add_new_vsn(Mod, FName2, Vsns),
+ NVsns = add_new_vsn(Mod, Bin, Vsns),
{[{Mod, Bin, FName2} | Bins],NVsns};
error ->
throw({error, {no_such_file,FName}})
@@ -609,17 +609,17 @@ sync_nodes(Id, Nodes) ->
add_old_vsn(Mod, Vsns) ->
case lists:keysearch(Mod, 1, Vsns) of
{value, {Mod, undefined, NewVsn}} ->
- OldVsn = get_vsn(code:which(Mod)),
+ OldVsn = get_current_vsn(Mod),
lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn});
{value, {Mod, _OldVsn, _NewVsn}} ->
Vsns;
false ->
- OldVsn = get_vsn(code:which(Mod)),
+ OldVsn = get_current_vsn(Mod),
[{Mod, OldVsn, undefined} | Vsns]
end.
-add_new_vsn(Mod, File, Vsns) ->
- NewVsn = get_vsn(File),
+add_new_vsn(Mod, Bin, Vsns) ->
+ NewVsn = get_vsn(Bin),
case lists:keysearch(Mod, 1, Vsns) of
{value, {Mod, OldVsn, undefined}} ->
lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn});
@@ -627,17 +627,35 @@ add_new_vsn(Mod, File, Vsns) ->
[{Mod, undefined, NewVsn} | Vsns]
end.
-
+%%-----------------------------------------------------------------
+%% Func: get_current_vsn/1
+%% Args: Mod = atom()
+%% Purpose: This function returns the equivalent of
+%% beam_lib:version(code:which(Mod)), but it will also handle the
+%% case when using erl_prim_loader loader different from 'efile'.
+%% The reason for not using the Binary from the 'bins' or the
+%% version directly from the 'vsns' state field is that these are
+%% updated already by load_object_code, and this function is called
+%% from load and remove.
+%% Returns: Vsn = term()
+%%-----------------------------------------------------------------
+get_current_vsn(Mod) ->
+ File = code:which(Mod),
+ case erl_prim_loader:get_file(File) of
+ {ok, Bin, _File2} ->
+ get_vsn(Bin);
+ error ->
+ throw({error, {no_such_file, File}})
+ end.
%%-----------------------------------------------------------------
%% Func: get_vsn/1
-%% Args: File = string()
+%% Args: Bin = binary()
%% Purpose: Finds the version attribute of a module.
-%% Returns: Vsn
-%% Vsn = term()
+%% Returns: Vsn = term()
%%-----------------------------------------------------------------
-get_vsn(File) ->
- {ok, {_Mod, Vsn}} = beam_lib:version(File),
+get_vsn(Bin) ->
+ {ok, {_Mod, Vsn}} = beam_lib:version(Bin),
case misc_supp:is_string(Vsn) of
true ->
Vsn;
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 3512e194bc..7b1fda4cf9 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -52,13 +52,12 @@
-type option() :: socketoption() | ssloption() | transportoption().
-type socketoption() :: [{property(), term()}]. %% See gen_tcp and inet
-type property() :: atom().
-
-type ssloption() :: {verify, verify_type()} |
{verify_fun, {fun(), InitialUserState::term()}} |
{fail_if_no_peer_cert, boolean()} | {depth, integer()} |
- {cert, der_encoded()} | {certfile, path()} | {key, der_encoded()} |
- {keyfile, path()} | {password, string()} | {cacerts, [der_encoded()]} |
- {cacertfile, path()} | {dh, der_encoded()} | {dhfile, path()} |
+ {cert, Der::binary()} | {certfile, path()} | {key, Der::binary()} |
+ {keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} |
+ {cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} |
{ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} |
{reuse_session, fun()} | {hibernate_after, integer()|undefined}.
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index 8ae4d2332e..fb0ebac7d1 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,9 +29,8 @@
-include_lib("public_key/include/public_key.hrl").
-type algo_oid() :: ?'rsaEncryption' | ?'id-dsa'.
--type public_key() :: #'RSAPublicKey'{} | integer().
-type public_key_params() :: #'Dss-Parms'{} | term().
--type public_key_info() :: {algo_oid(), public_key(), public_key_params()}.
+-type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}.
-record(session, {
session_id,
diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml
index c5eb81a86a..c81023862e 100644
--- a/lib/stdlib/doc/src/binary.xml
+++ b/lib/stdlib/doc/src/binary.xml
@@ -485,7 +485,7 @@
<code>
1> Bin = &lt;&lt;1,2,3,4,5,6,7,8,9,10&gt;&gt;.
-2> binary:part(Bin,{byte_size(Bin), -5)).
+2> binary:part(Bin,{byte_size(Bin), -5}).
&lt;&lt;6,7,8,9,10&gt;&gt;
</code>
diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml
index 4d98a20206..529a70a23d 100644
--- a/lib/stdlib/doc/src/zip.xml
+++ b/lib/stdlib/doc/src/zip.xml
@@ -34,11 +34,11 @@
<module>zip</module>
<modulesummary>Utility for reading and creating 'zip' archives.</modulesummary>
<description>
- <p>The <c>zip</c> module archives and extract files to and from a zip
+ <p>The <c>zip</c> module archives and extracts files to and from a zip
archive. The zip format is specified by the "ZIP Appnote.txt" file
available on PKWare's website www.pkware.com.</p>
<p>The zip module supports zip archive versions up to 6.1. However,
- password-protection and Zip64 is not supported.</p>
+ password-protection and Zip64 are not supported.</p>
<p>By convention, the name of a zip file should end in "<c>.zip</c>".
To abide to the convention, you'll need to add "<c>.zip</c>" yourself
to the name.</p>
@@ -52,7 +52,7 @@
<seealso marker="#unzip_2">unzip/2</seealso> function. (They are
also available as <c>extract</c>.)</p>
<p>To fold a function over all files in a zip archive, use the
- <seealso marker="#foldl_3">foldl_3</seealso>.</p>
+ <seealso marker="#foldl_3">foldl_3</seealso> function.</p>
<p>To return a list of the files in a zip archive, use the
<seealso marker="#list_dir_1">list_dir/1</seealso> or the
<seealso marker="#list_dir_2">list_dir/2</seealso> function. (They
@@ -155,13 +155,13 @@ zip_file() </code>
<p>Files will be compressed using the DEFLATE compression, as
described in the Appnote.txt file. However, files will be
stored without compression if they already are compressed.
- The <c>zip/2</c> and <c>zip/3</c> checks the file extension
+ The <c>zip/2</c> and <c>zip/3</c> functions check the file extension
to see whether the file should be stored without compression.
Files with the following extensions are not compressed:
<c>.Z</c>, <c>.zip</c>, <c>.zoo</c>, <c>.arc</c>, <c>.lzh</c>,
<c>.arj</c>.</p>
<p>It is possible to override the default behavior and
- explicitly control what types of files that should be
+ explicitly control what types of files should be
compressed by using the <c>{compress, What}</c> and
<c>{uncompress, What}</c> options. It is possible to have
several <c>compress</c> and <c>uncompress</c> options. In
@@ -208,7 +208,7 @@ zip_file() </code>
</item>
<tag><c>{compress, What}</c></tag>
<item>
- <p>Controls what types of files that will be
+ <p>Controls what types of files will be
compressed. It is by default set to <c>all</c>. The
following values of <c>What</c> are allowed:</p>
<taglist>
@@ -228,7 +228,7 @@ zip_file() </code>
</item>
<tag><c>{uncompress, What}</c></tag>
<item>
- <p>Controls what types of files that will be uncompressed. It is by
+ <p>Controls what types of files will be uncompressed. It is by
default set to <c>[".Z",".zip",".zoo",".arc",".lzh",".arj"]</c>.
The following values of <c>What</c> are allowed:</p>
<taglist>
@@ -292,7 +292,7 @@ zip_file() </code>
<p>By default, the <c>open/2</c> function will open the
zip file in <c>raw</c> mode, which is faster but does not allow
a remote (erlang) file server to be used. Adding <c>cooked</c>
- to the mode list will override the default and open zip file
+ to the mode list will override the default and open the zip file
without the <c>raw</c> option. The same goes for the files
extracted.</p>
</item>
@@ -301,7 +301,7 @@ zip_file() </code>
<p>By default, all existing files with the same name as file in
the zip archive will be overwritten. With the <c>keep_old_files</c>
option, the <c>unzip/2</c> function will not overwrite any existing
- files. Not that even with the <c>memory</c> option given, which
+ files. Note that even with the <c>memory</c> option given, which
means that no files will be overwritten, files existing will be
excluded from the result.</p>
</item>
@@ -418,7 +418,7 @@ zip_file() </code>
<p>By default, the <c>open/2</c> function will open the
zip file in <c>raw</c> mode, which is faster but does not allow
a remote (erlang) file server to be used. Adding <c>cooked</c>
- to the mode list will override the default and open zip file
+ to the mode list will override the default and open the zip file
without the <c>raw</c> option.</p>
</item>
</taglist>
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index bb4b18cf9b..15b45d72f4 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -757,7 +757,8 @@ record_fields([{typed,Expr,TypeInfo}|Fields]) ->
{atom, La, _} ->
case has_undefined(TypeInfo) of
false ->
- lift_unions(abstract(undefined, La), TypeInfo);
+ TypeInfo2 = maybe_add_paren(TypeInfo),
+ lift_unions(abstract(undefined, La), TypeInfo2);
true ->
TypeInfo
end
@@ -778,6 +779,11 @@ has_undefined({type,_,union,Ts}) ->
has_undefined(_) ->
false.
+maybe_add_paren({ann_type,L,T}) ->
+ {paren_type,L,[{ann_type,L,T}]};
+maybe_add_paren(T) ->
+ T.
+
term(Expr) ->
try normalise(Expr)
catch _:_R -> ret_err(?line(Expr), "bad attribute")
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index df4a20b833..66c80a45cb 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -558,17 +558,11 @@ record_field({typed_record_field,{record_field,_,F,Val},Type}, Hook) ->
Fl = lexpr(F, L, Hook),
Vl = typed(lexpr(Val, R, Hook), Type),
{list,[{cstep,[Fl,' ='],Vl}]};
-record_field({typed_record_field,Field,Type0}, Hook) ->
- Type = remove_undefined(Type0),
+record_field({typed_record_field,Field,Type}, Hook) ->
typed(record_field(Field, Hook), Type);
record_field({record_field,_,F}, Hook) ->
lexpr(F, 0, Hook).
-remove_undefined({type,L,union,[{atom,_,undefined}|T]}) ->
- {type,L,union,T};
-remove_undefined(T) -> % cannot happen
- T.
-
list({cons,_,H,T}, Es, Hook) ->
list(T, [H|Es], Hook);
list({nil,_}, Es, Hook) ->
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index 6b2eb78e2c..4b59cee99e 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -571,6 +571,17 @@ otp_5269(Config) when is_list(Config) ->
B:A>> <- [<<16:8,19:16>>],
<<X:8>> <- [<<B:8>>]].",
[19]),
+ ?line check(fun() ->
+ (fun (<<A:1/binary, B:8/integer, _C:B/binary>>) ->
+ case A of
+ B -> wrong;
+ _ -> ok
+ end
+ end)(<<1,2,3,4>>) end,
+ "(fun(<<A:1/binary, B:8/integer, _C:B/binary>>) ->"
+ " case A of B -> wrong; _ -> ok end"
+ " end)(<<1, 2, 3, 4>>).",
+ ok),
ok.
otp_6539(doc) ->
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 822886cb8a..bc811355ab 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -48,7 +48,7 @@
neg_indent/1,
otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1,
- otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1]).
+ otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1]).
%% Internal export.
-export([ehook/6]).
@@ -79,7 +79,7 @@ groups() ->
{attributes, [], [misc_attrs, import_export]},
{tickets, [],
[otp_6321, otp_6911, otp_6914, otp_8150, otp_8238,
- otp_8473, otp_8522, otp_8567, otp_8664]}].
+ otp_8473, otp_8522, otp_8567, otp_8664, otp_9147]}].
init_per_suite(Config) ->
Config.
@@ -1047,6 +1047,26 @@ otp_8664(Config) when is_list(Config) ->
ok.
+otp_9147(doc) ->
+ "OTP_9147. Create well-formed types when adding 'undefined'.";
+otp_9147(suite) -> [];
+otp_9147(Config) when is_list(Config) ->
+ FileName = filename('otp_9147.erl', Config),
+ C1 = <<"-module(otp_9147).\n"
+ "-export_type([undef/0]).\n"
+ "-record(undef, {f1 :: F1 :: a | b}).\n"
+ "-type undef() :: #undef{}.\n">>,
+ ?line ok = file:write_file(FileName, C1),
+ ?line {ok, _, []} =
+ compile:file(FileName, [return,'P',{outdir,?privdir}]),
+ PFileName = filename('otp_9147.P', Config),
+ ?line {ok, Bin} = file:read_file(PFileName),
+ %% The parentheses around "F1 :: a | b" are new (bugfix).
+ ?line true =
+ lists:member("-record(undef,{f1 :: undefined | (F1 :: a | b)}).",
+ string:tokens(binary_to_list(Bin), "\n")),
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
compile(Config, Tests) ->
diff --git a/lib/tv/src/tv_io_lib.erl b/lib/tv/src/tv_io_lib.erl
index f693ff796d..5457575b7d 100644
--- a/lib/tv/src/tv_io_lib.erl
+++ b/lib/tv/src/tv_io_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -52,10 +52,11 @@ write(_Term, 0) -> "...";
write(Term, _D) when is_integer(Term) -> integer_to_list(Term);
write(Term, _D) when is_float(Term) -> tv_io_lib_format:fwrite_g(Term);
write(Atom, _D) when is_atom(Atom) -> write_atom(Atom);
-write(Term, _D) when is_port(Term) -> "#Port";
+write(Term, _D) when is_port(Term) -> lists:flatten(io_lib:write(Term));
write(Term, _D) when is_pid(Term) -> pid_to_list(Term);
-write(Term, _D) when is_reference(Term) -> "#Ref";
-write(Term, _D) when is_binary(Term) -> "#Bin";
+write(Term, _D) when is_reference(Term) -> io_lib:write(Term);
+write(Term, _D) when is_binary(Term), byte_size(Term) > 100 -> "#Bin";
+write(Term, _D) when is_binary(Term) -> "<<\"" ++ binary_to_list(Term) ++ "\">>";
write(Term, _D) when is_bitstring(Term) -> "#Bitstr";
write([], _D) -> "[]";
write({}, _D) -> "{}";