From cb01e21518a36a14e1a86fa6540f95ddb87c5f1e Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Mon, 21 May 2018 12:53:56 +0200 Subject: public_key: Add ed25519 and ed448 sign/verify --- lib/public_key/src/pubkey_pem.erl | 11 ++++- lib/public_key/src/pubkey_ssh.erl | 99 +++++++++++++++++++++++++++++++++++++-- lib/public_key/src/public_key.erl | 19 ++++++-- 3 files changed, 119 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl index b92790554f..d7e5bc3ad8 100644 --- a/lib/public_key/src/pubkey_pem.erl +++ b/lib/public_key/src/pubkey_pem.erl @@ -222,7 +222,9 @@ pem_start('CertificateList') -> pem_start('EcpkParameters') -> <<"-----BEGIN EC PARAMETERS-----">>; pem_start('ECPrivateKey') -> - <<"-----BEGIN EC PRIVATE KEY-----">>. + <<"-----BEGIN EC PRIVATE KEY-----">>; +pem_start({no_asn1, new_openssh}) -> %% Temporarily in the prototype of this format + <<"-----BEGIN OPENSSH PRIVATE KEY-----">>. pem_end(<<"-----BEGIN CERTIFICATE-----">>) -> <<"-----END CERTIFICATE-----">>; @@ -250,6 +252,8 @@ pem_end(<<"-----BEGIN EC PARAMETERS-----">>) -> <<"-----END EC PARAMETERS-----">>; pem_end(<<"-----BEGIN EC PRIVATE KEY-----">>) -> <<"-----END EC PRIVATE KEY-----">>; +pem_end(<<"-----BEGIN OPENSSH PRIVATE KEY-----">>) -> + <<"-----END OPENSSH PRIVATE KEY-----">>; pem_end(_) -> undefined. @@ -278,7 +282,10 @@ asn1_type(<<"-----BEGIN X509 CRL-----">>) -> asn1_type(<<"-----BEGIN EC PARAMETERS-----">>) -> 'EcpkParameters'; asn1_type(<<"-----BEGIN EC PRIVATE KEY-----">>) -> - 'ECPrivateKey'. + 'ECPrivateKey'; +asn1_type(<<"-----BEGIN OPENSSH PRIVATE KEY-----">>) -> + {no_asn1, new_openssh}. %% Temporarily in the prototype of this format + pem_decrypt() -> <<"Proc-Type: 4,ENCRYPTED">>. diff --git a/lib/public_key/src/pubkey_ssh.erl b/lib/public_key/src/pubkey_ssh.erl index 02c061efc9..d0ef4abfb1 100644 --- a/lib/public_key/src/pubkey_ssh.erl +++ b/lib/public_key/src/pubkey_ssh.erl @@ -25,7 +25,8 @@ -export([decode/2, encode/2, dh_gex_group/4, - dh_gex_group_sizes/0 + dh_gex_group_sizes/0, +pad/2, new_openssh_encode/1, new_openssh_decode/1 % For test and experiments ]). -define(UINT32(X), X:32/unsigned-big-integer). @@ -67,6 +68,8 @@ decode(Bin, rfc4716_public_key) -> rfc4716_decode(Bin); decode(Bin, ssh2_pubkey) -> ssh2_pubkey_decode(Bin); +decode(Bin, new_openssh) -> + new_openssh_decode(Bin); decode(Bin, Type) -> openssh_decode(Bin, Type). @@ -177,6 +180,70 @@ join_entry([Line | Lines], Entry) -> rfc4716_pubkey_decode(BinKey) -> ssh2_pubkey_decode(BinKey). +%% From https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key +new_openssh_decode(<<"openssh-key-v1",0, + ?DEC_BIN(CipherName, _L1), + ?DEC_BIN(KdfName, _L2), + ?DEC_BIN(KdfOptions, _L3), + ?UINT32(N), % number of keys + ?DEC_BIN(PublicKey, _L4), + ?DEC_BIN(Encrypted, _L5), + _Rest/binary + >>) -> + %%io:format("CipherName = ~p~nKdfName = ~p~nKdfOptions = ~p~nPublicKey = ~p~nN = ~p~nEncrypted = ~p~nRest = ~p~n", [CipherName, KdfName, KdfOptions, PublicKey, N, Encrypted, _Rest]), + new_openssh_decode(CipherName, KdfName, KdfOptions, PublicKey, N, Encrypted). + +new_openssh_decode(<<"none">>, <<"none">>, <<"">>, _PublicKey, 1, + <>) -> + case {Type,PrivPubKey} of + {<<"ssh-ed25519">>, + <>} -> + {ed_pri, ed25519, PubKey, PrivKey}; + + {<<"ssh-ed448">>, + <>} -> % "Intelligent" guess from + % https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-ed448 + {ed_pri, ed448, PubKey, PrivKey} + end. + + +new_openssh_encode({ed_pri,_,PubKey,PrivKey}=Key) -> + Type = key_type(Key), + CheckInt = 17*256+17, %crypto:strong_rand_bytes(4), + Comment = <<>>, + PublicKey = <>, + CipherName = <<"none">>, + KdfName = <<"none">>, + KdfOptions = <<>>, + BlockSize = 8, % Crypto dependent + NumKeys = 1, + Encrypted0 = <>), + ?STRING(Comment) + >>, + Pad = pad(size(Encrypted0), BlockSize), + Encrypted = <>, + <<"openssh-key-v1",0, + ?STRING(CipherName), + ?STRING(KdfName), + ?STRING(KdfOptions), + ?UINT32(NumKeys), + ?STRING(PublicKey), + ?STRING(Encrypted)>>. + +pad(N, BlockSize) when N>BlockSize -> pad(N rem BlockSize, BlockSize); +pad(N, BlockSize) -> list_to_binary(lists:seq(1,BlockSize-N)). + + openssh_decode(Bin, FileType) -> Lines = binary:split(Bin, <<"\n">>, [global]), do_openssh_decode(FileType, Lines, []). @@ -235,6 +302,8 @@ do_openssh_decode(openssh_public_key = FileType, [Line | Lines], Acc) -> <<"ssh-rsa">> -> true; <<"ssh-dss">> -> true; <<"ecdsa-sha2-",Curve/binary>> -> is_ssh_curvename(Curve); + <<"ssh-ed25519">> -> true; + <<"ssh-ed448">> -> true; _ -> false end, @@ -247,7 +316,9 @@ do_openssh_decode(openssh_public_key = FileType, [Line | Lines], Acc) -> Comment = string:strip(string_decode(iolist_to_binary(Comment0)), right, $\n), do_openssh_decode(FileType, Lines, [{openssh_pubkey_decode(KeyType, Base64Enc), - [{comment, Comment}]} | Acc]) + [{comment, Comment}]} | Acc]); + _ when KnownKeyType==false -> + do_openssh_decode(FileType, Lines, Acc) end. @@ -386,6 +457,10 @@ line_end(Comment) -> key_type(#'RSAPublicKey'{}) -> <<"ssh-rsa">>; key_type({_, #'Dss-Parms'{}}) -> <<"ssh-dss">>; +key_type({ed_pub,ed25519,_}) -> <<"ssh-ed25519">>; +key_type({ed_pub,ed448,_}) -> <<"ssh-ed448">>; +key_type({ed_pri,ed25519,_,_}) -> <<"ssh-ed25519">>; +key_type({ed_pri,ed448,_,_}) -> <<"ssh-ed448">>; key_type({#'ECPoint'{}, {namedCurve,Curve}}) -> <<"ecdsa-sha2-", (public_key:oid2ssh_curvename(Curve))/binary>>. comma_list_encode([Option], []) -> @@ -404,7 +479,12 @@ ssh2_pubkey_encode({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) -> <>), ?Empint(P), ?Empint(Q), ?Empint(G), ?Empint(Y)>>; ssh2_pubkey_encode(Key={#'ECPoint'{point = Q}, {namedCurve,OID}}) -> Curve = public_key:oid2ssh_curvename(OID), - <>. + <>; +ssh2_pubkey_encode({ed_pub, ed25519, Key}) -> + <>), ?Estring(Key)>>; +ssh2_pubkey_encode({ed_pub, ed448, Key}) -> + <>), ?Estring(Key)>>. + ssh2_pubkey_decode(<>) -> @@ -430,12 +510,23 @@ ssh2_pubkey_decode(<<"ssh-dss">>, ssh2_pubkey_decode(<<"ecdsa-sha2-",Id/binary>>, <>) -> - {#'ECPoint'{point = Q}, {namedCurve,public_key:ssh_curvename2oid(Id)}}. + {#'ECPoint'{point = Q}, {namedCurve,public_key:ssh_curvename2oid(Id)}}; + +ssh2_pubkey_decode(<<"ssh-ed25519">>, + <>) -> + {ed_pub, ed25519, Key}; + +ssh2_pubkey_decode(<<"ssh-ed448">>, + <>) -> + {ed_pub, ed448, Key}. + is_key_field(<<"ssh-dss">>) -> true; is_key_field(<<"ssh-rsa">>) -> true; +is_key_field(<<"ssh-ed25519">>) -> true; +is_key_field(<<"ssh-ed448">>) -> true; is_key_field(<<"ecdsa-sha2-",Id/binary>>) -> is_ssh_curvename(Id); is_key_field(_) -> false. diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index dca1e0766e..75d40d2e8a 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -68,8 +68,8 @@ pki_asn1_type/0, asn1_type/0, ssh_file/0, der_encoded/0, key_params/0, digest_type/0]). --type public_key() :: rsa_public_key() | dsa_public_key() | ec_public_key(). --type private_key() :: rsa_private_key() | dsa_private_key() | ec_private_key(). +-type public_key() :: rsa_public_key() | dsa_public_key() | ec_public_key() | ed_public_key() . +-type private_key() :: rsa_private_key() | dsa_private_key() | ec_private_key() | ed_private_key() . -type rsa_public_key() :: #'RSAPublicKey'{}. -type rsa_private_key() :: #'RSAPrivateKey'{}. @@ -79,6 +79,9 @@ -type ecpk_parameters_api() :: ecpk_parameters() | #'ECParameters'{} | {namedCurve, Name::crypto:ec_named_curve()}. -type ec_public_key() :: {#'ECPoint'{}, ecpk_parameters_api()}. -type ec_private_key() :: #'ECPrivateKey'{}. +-type ed_public_key() :: {ed_pub, ed25519|ed448, Key::binary()}. +-type ed_private_key() :: {ed_pri, ed25519|ed448, Pub::binary(), Priv::binary()}. + -type key_params() :: #'DHParameter'{} | {namedCurve, oid()} | #'ECParameters'{} | {rsa, Size::integer(), PubExp::integer()}. -type der_encoded() :: binary(). @@ -166,6 +169,8 @@ pem_entry_decode({'SubjectPublicKeyInfo', Der, _}) -> ECCParams = der_decode('EcpkParameters', Params), {#'ECPoint'{point = Key0}, ECCParams} end; +pem_entry_decode({{no_asn1,new_openssh}, Special, not_encrypted}) -> + ssh_decode(Special, new_openssh); pem_entry_decode({Asn1Type, Der, not_encrypted}) when is_atom(Asn1Type), is_binary(Der) -> der_decode(Asn1Type, Der). @@ -1070,8 +1075,9 @@ pkix_verify_hostname_match_fun(https) -> -spec ssh_decode(SshBin, Type) -> Decoded when SshBin :: binary(), - Type :: ssh2_pubkey | OtherType, + Type :: ssh2_pubkey | OtherType | InternalType, OtherType :: public_key | ssh_file(), + InternalType :: new_openssh, Decoded :: Decoded_ssh2_pubkey | Decoded_OtherType, Decoded_ssh2_pubkey :: public_key(), @@ -1090,7 +1096,8 @@ ssh_decode(SshBin, Type) when is_binary(SshBin), Type == openssh_public_key; Type == auth_keys; Type == known_hosts; - Type == ssh2_pubkey -> + Type == ssh2_pubkey; + Type == new_openssh -> pubkey_ssh:decode(SshBin, Type). %%-------------------------------------------------------------------- @@ -1233,6 +1240,8 @@ format_sign_key(#'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) -> {dss, [P, Q, G, X]}; format_sign_key(#'ECPrivateKey'{privateKey = PrivKey, parameters = Param}) -> {ecdsa, [PrivKey, ec_curve_spec(Param)]}; +format_sign_key({ed_pri, Curve, _Pub, Priv}) -> + {eddsa, [Priv,Curve]}; format_sign_key(_) -> badarg. @@ -1242,6 +1251,8 @@ format_verify_key({#'ECPoint'{point = Point}, Param}) -> {ecdsa, [Point, ec_curve_spec(Param)]}; format_verify_key({Key, #'Dss-Parms'{p = P, q = Q, g = G}}) -> {dss, [P, Q, G, Key]}; +format_verify_key({ed_pub, Curve, Key}) -> + {eddsa, [Key,Curve]}; %% Convert private keys to public keys format_verify_key(#'RSAPrivateKey'{modulus = Mod, publicExponent = Exp}) -> format_verify_key(#'RSAPublicKey'{modulus = Mod, publicExponent = Exp}); -- cgit v1.2.3