aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Nilsson <[email protected]>2015-09-28 12:10:06 +0200
committerHans Nilsson <[email protected]>2015-10-08 16:02:07 +0200
commit437448c16ac18208838a638717309ee0294b004e (patch)
tree196bc7445d659206cb6df5c2c6271e8bf4c4c234
parent794a6d1f2c904be72d4b4327a7c6faa759a25690 (diff)
downloadotp-437448c16ac18208838a638717309ee0294b004e.tar.gz
otp-437448c16ac18208838a638717309ee0294b004e.tar.bz2
otp-437448c16ac18208838a638717309ee0294b004e.zip
public_key: Add ssh2 ECDSA pub key handling + test case
Added encode/decode for ecdsa public keys in openssh and rfc4716 format. This is for the ssh public key algorithm ecdsa-sha2-*.
-rw-r--r--lib/public_key/src/pubkey_ssh.erl49
-rw-r--r--lib/public_key/test/public_key_SUITE.erl32
-rw-r--r--lib/public_key/test/public_key_SUITE_data/openssh_ecdsa_pub1
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_ecdsa_pub6
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 ----