diff options
29 files changed, 1055 insertions, 184 deletions
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/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/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_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) -> |