diff options
-rw-r--r-- | lib/public_key/src/pubkey_ssh.erl | 49 | ||||
-rw-r--r-- | lib/public_key/test/public_key_SUITE.erl | 32 | ||||
-rw-r--r-- | lib/public_key/test/public_key_SUITE_data/openssh_ecdsa_pub | 1 | ||||
-rw-r--r-- | lib/public_key/test/public_key_SUITE_data/ssh2_ecdsa_pub | 6 |
4 files changed, 81 insertions, 7 deletions
diff --git a/lib/public_key/src/pubkey_ssh.erl b/lib/public_key/src/pubkey_ssh.erl index 7680d0ce59..26fbeb68ce 100644 --- a/lib/public_key/src/pubkey_ssh.erl +++ b/lib/public_key/src/pubkey_ssh.erl @@ -24,6 +24,8 @@ -export([decode/2, encode/2]). -define(UINT32(X), X:32/unsigned-big-integer). +-define(STRING(X), ?UINT32((size(X))), (X)/binary). + %% 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 @@ -130,7 +132,13 @@ rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary, {erlint(SizeY, Y), #'Dss-Parms'{p = erlint(SizeP, P), q = erlint(SizeQ, Q), - g = erlint(SizeG, G)}}. + g = erlint(SizeG, G)}}; +rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary, + ?UINT32(SizeId), Id:SizeId/binary, + ?UINT32(SizeQ), Q:SizeQ/binary>>) when Type == <<"ecdsa-sha2-nistp256">>; + Type == <<"ecdsa-sha2-nistp384">>; + Type == <<"ecdsa-sha2-nistp521">> -> + {#'ECPoint'{point = Q}, Id}. openssh_decode(Bin, FileType) -> Lines = binary:split(Bin, <<"\n">>, [global]), @@ -186,12 +194,18 @@ do_openssh_decode(known_hosts = FileType, [Line | Lines], Acc) -> do_openssh_decode(openssh_public_key = FileType, [Line | Lines], Acc) -> case split_n(2, Line, []) of [KeyType, Base64Enc] when KeyType == <<"ssh-rsa">>; - KeyType == <<"ssh-dss">> -> + KeyType == <<"ssh-dss">>; + KeyType == <<"ecdsa-sha2-nistp256">>; + KeyType == <<"ecdsa-sha2-nistp384">>; + KeyType == <<"ecdsa-sha2-nistp521">> -> do_openssh_decode(FileType, Lines, [{openssh_pubkey_decode(KeyType, Base64Enc), []} | Acc]); [KeyType, Base64Enc | Comment0] when KeyType == <<"ssh-rsa">>; - KeyType == <<"ssh-dss">> -> + KeyType == <<"ssh-dss">>; + KeyType == <<"ecdsa-sha2-nistp256">>; + KeyType == <<"ecdsa-sha2-nistp384">>; + KeyType == <<"ecdsa-sha2-nistp521">> -> Comment = string:strip(string_decode(iolist_to_binary(Comment0)), right, $\n), do_openssh_decode(FileType, Lines, [{openssh_pubkey_decode(KeyType, Base64Enc), @@ -203,6 +217,7 @@ decode_comment([]) -> decode_comment(Comment) -> [{comment, string_decode(iolist_to_binary(Comment))}]. + openssh_pubkey_decode(<<"ssh-rsa">>, Base64Enc) -> <<?UINT32(StrLen), _:StrLen/binary, ?UINT32(SizeE), E:SizeE/binary, @@ -222,9 +237,19 @@ openssh_pubkey_decode(<<"ssh-dss">>, Base64Enc) -> #'Dss-Parms'{p = erlint(SizeP, P), q = erlint(SizeQ, Q), g = erlint(SizeG, G)}}; + +openssh_pubkey_decode(<<"ecdsa-sha2-", Id/binary>>, Base64Enc) -> + %% rfc5656#section-3.1 + <<?UINT32(StrLen), _:StrLen/binary, + ?UINT32(SizeId), Id:SizeId/binary, + ?UINT32(SizeQ), Q:SizeQ/binary>> + = base64:mime_decode(Base64Enc), + {#'ECPoint'{point = Q}, Id}; + openssh_pubkey_decode(KeyType, Base64Enc) -> {KeyType, base64:mime_decode(Base64Enc)}. + erlint(MPIntSize, MPIntValue) -> Bits= MPIntSize * 8, <<Integer:Bits/integer>> = MPIntValue, @@ -350,7 +375,9 @@ line_end(Comment) -> key_type(#'RSAPublicKey'{}) -> <<"ssh-rsa">>; key_type({_, #'Dss-Parms'{}}) -> - <<"ssh-dss">>. + <<"ssh-dss">>; +key_type({#'ECPoint'{}, Id}) -> + <<"ecdsa-sha2-",Id/binary>>. comma_list_encode([Option], []) -> Option; @@ -380,7 +407,13 @@ ssh2_pubkey_encode({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) -> PBin/binary, QBin/binary, GBin/binary, - YBin/binary>>. + YBin/binary>>; +ssh2_pubkey_encode({#'ECPoint'{point = Q}, Id}) -> + TypeStr = <<"ecdsa-sha2-", Id/binary>>, + StrLen = size(TypeStr), + <<?UINT32(StrLen), TypeStr:StrLen/binary, + (string(Id))/binary, + (string(Q))/binary>>. is_key_field(<<"ssh-dss">>) -> true; @@ -507,3 +540,9 @@ int_to_bin_neg(-1, Ds=[MSB|_]) when MSB >= 16#80 -> list_to_binary(Ds); int_to_bin_neg(X,Ds) -> int_to_bin_neg(X bsr 8, [(X band 255)|Ds]). + + +string(X) when is_binary(X) -> + << ?STRING(X) >>; +string(X) -> + << ?STRING(list_to_binary(X)) >>. diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index 6f142c951c..5e677f31d6 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -49,8 +49,10 @@ groups() -> [{pem_decode_encode, [], [dsa_pem, rsa_pem, encrypted_pem, dh_pem, cert_pem, pkcs7_pem, pkcs10_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_rsa_public_key, ssh_dsa_public_key, ssh_ecdsa_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]} @@ -291,6 +293,32 @@ ssh_dsa_public_key(Config) when is_list(Config) -> public_key:ssh_decode(EncodedOpenSsh, public_key). %%-------------------------------------------------------------------- + +ssh_ecdsa_public_key() -> + [{doc, "ssh ecdsa public key decode/encode"}]. +ssh_ecdsa_public_key(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + + {ok, ECDSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_ecdsa_pub")), + [{PubKey, Attributes1}] = public_key:ssh_decode(ECDSARawSsh2, public_key), + [{PubKey, Attributes1}] = public_key:ssh_decode(ECDSARawSsh2, rfc4716_public_key), + + {ok, ECDSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_ecdsa_pub")), + [{PubKey, Attributes2}] = public_key:ssh_decode(ECDSARawOpenSsh, public_key), + [{PubKey, Attributes2}] = public_key:ssh_decode(ECDSARawOpenSsh, openssh_public_key), + + %% Can not check EncodedSSh == ECDSARawSsh2 and EncodedOpenSsh + %% = ECDSARawOpenSsh 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(Config) when is_list(Config) -> diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_ecdsa_pub b/lib/public_key/test/public_key_SUITE_data/openssh_ecdsa_pub new file mode 100644 index 0000000000..a49b4264b8 --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/openssh_ecdsa_pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIJrVlKYIT+MlxxRx5BFXisHHkcGMAAKv2dguUeOsutsYyzs9JAczvl6c+Sypra5+qOi2LHPXw6GGluuXcOssOM= uabhnil@elxadlj3q32 diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_ecdsa_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_ecdsa_pub new file mode 100644 index 0000000000..702e5c4fde --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/ssh2_ecdsa_pub @@ -0,0 +1,6 @@ +---- BEGIN SSH2 PUBLIC KEY ---- +Comment: "256-bit ECDSA, converted by uabhnil@elxadlj3q32 from OpenSSH" +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIJrVlKYIT+MlxxRx5 +BFXisHHkcGMAAKv2dguUeOsutsYyzs9JAczvl6c+Sypra5+qOi2LHPXw6GGluuXcOssOM= + +---- END SSH2 PUBLIC KEY ---- |