File 2533-public_key-Add-support-for-EDDSA-key-on-RFC-5958-for.patch of Package erlang

From 37a58368d7876e325710a4c269f0af27b40434c6 Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin <ingela@erlang.org>
Date: Fri, 9 Apr 2021 12:34:00 +0200
Subject: [PATCH 3/8] public_key: Add support for EDDSA key on RFC 5958 format

For now make minimal ASN-1 addition. We probably want to modernize
all ASN-1 specs.
---
 lib/public_key/asn1/ECPrivateKey.asn1         |  7 +++-
 lib/public_key/asn1/PKCS-8.asn1               | 16 ++++++++
 lib/public_key/src/pubkey_pem.erl             |  2 +
 lib/public_key/src/public_key.erl             | 38 +++++++++++++++++--
 lib/public_key/test/public_key_SUITE.erl      | 16 +++++++-
 .../eddsa_key_rfc5958.pem                     |  5 +++
 6 files changed, 78 insertions(+), 6 deletions(-)
 create mode 100644 lib/public_key/test/public_key_SUITE_data/eddsa_key_rfc5958.pem

diff --git a/lib/public_key/asn1/ECPrivateKey.asn1 b/lib/public_key/asn1/ECPrivateKey.asn1
index 5f7267506f..e0f4c3a011 100644
--- a/lib/public_key/asn1/ECPrivateKey.asn1
+++ b/lib/public_key/asn1/ECPrivateKey.asn1
@@ -18,9 +18,14 @@ ECPrivateKey ::= SEQUENCE {
   version        INTEGER,
   privateKey     CurvePrivateKey,
   parameters [0] EcpkParameters OPTIONAL,
-  publicKey  [1] BIT STRING OPTIONAL
+  publicKey  [1] CurvePublicKey OPTIONAL,
+  -- Should be PKCS-8 Attributes but problem at the moment with PKCS-8 beeing part
+  -- of PCKS-FRAME and PKIX1Algorithms88 is part of OTP-PUB-KEY. Procrastinate
+  -- the solution as it mostly not used anyway
+  attributes     ANY OPTIONAL
 }
 
 CurvePrivateKey ::= OCTET STRING
+CurvePublicKey ::= BIT STRING
 
 END
diff --git a/lib/public_key/asn1/PKCS-8.asn1 b/lib/public_key/asn1/PKCS-8.asn1
index 292a7b2029..87b3ecd99c 100644
--- a/lib/public_key/asn1/PKCS-8.asn1
+++ b/lib/public_key/asn1/PKCS-8.asn1
@@ -51,6 +51,22 @@ PrivateKeyInfo ::= SEQUENCE {
   privateKey PrivateKey,
   attributes [0] Attributes OPTIONAL }
 
+-- START FROM RFC 5958
+OneAsymmetricKey ::= SEQUENCE {
+     version                   Version,
+     privateKeyAlgorithm       AlgorithmIdentifier {{...}},
+     privateKey                PrivateKey,
+     attributes            [0] Attributes OPTIONAL,
+     ...,
+     [[2: publicKey        [1] PublicKey OPTIONAL ]],
+     ...
+   }
+
+PublicKey ::= BIT STRING
+                     -- Content varies based on type of key. The
+                     -- algorithm identifier dictates the format of
+                     -- the key.
+-- END FROM RFC 5958
 Version ::= INTEGER {v1(0)} (v1,...)
 
 PrivateKey ::= OCTET STRING
diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl
index a33db4ecf0..a4d54c9e63 100644
--- a/lib/public_key/src/pubkey_pem.erl
+++ b/lib/public_key/src/pubkey_pem.erl
@@ -212,6 +212,8 @@ pem_start('DHParameter') ->
     <<"-----BEGIN DH PARAMETERS-----">>;
 pem_start('PrivateKeyInfo') ->
     <<"-----BEGIN PRIVATE KEY-----">>;
+pem_start('OneAsymmetricKey') ->
+    <<"-----BEGIN PRIVATE KEY-----">>;
 pem_start('EncryptedPrivateKeyInfo') ->
     <<"-----BEGIN ENCRYPTED PRIVATE KEY-----">>;
 pem_start('CertificationRequest') ->
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 4f097a1e2d..b9eeac1b12 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -291,7 +291,7 @@ der_decode(Asn1Type, Der) when (((Asn1Type == 'PrivateKeyInfo')
 	der_priv_key_decode(Decoded)
     catch
 	error:{badmatch, {error, _}} = Error ->
-	    erlang:error(Error)
+            handle_pkcs_frame_error(Asn1Type, Der, Error)
     end;
 
 der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) ->
@@ -303,6 +303,17 @@ der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) ->
 	    erlang:error(Error)
     end.
 
+handle_pkcs_frame_error('PrivateKeyInfo', Der, _) ->
+    try
+	{ok, Decoded} = 'PKCS-FRAME':decode('OneAsymmetricKey', Der),
+	der_priv_key_decode(Decoded)
+    catch
+	error:{badmatch, {error, _}} = Error ->
+	    erlang:error(Error)
+    end;
+handle_pkcs_frame_error(_, _, Error) ->
+    erlang:error(Error).
+
 der_priv_key_decode({'PrivateKeyInfo', v1,
 	{'PrivateKeyInfo_privateKeyAlgorithm', ?'id-ecPublicKey', {asn1_OPENTYPE, Parameters}}, PrivKey, _}) ->
 	EcPrivKey = der_decode('ECPrivateKey', PrivKey),
@@ -312,6 +323,13 @@ der_priv_key_decode({'PrivateKeyInfo', v1,
       CurveOId == ?'id-Ed25519'orelse
       CurveOId == ?'id-Ed448' ->
     #'ECPrivateKey'{version = 1, parameters = {namedCurve, CurveOId}, privateKey = PrivKey};
+der_priv_key_decode({'OneAsymmetricKey', _,
+                     {'OneAsymmetricKey_privateKeyAlgorithm', CurveOId, _}, PrivKey, Attr, PubKey}) when
+      CurveOId == ?'id-Ed25519'orelse
+      CurveOId == ?'id-Ed448' ->
+    #'ECPrivateKey'{version = 2, parameters = {namedCurve, CurveOId}, privateKey = PrivKey,
+                    attributes = Attr,
+                    publicKey = PubKey};
 der_priv_key_decode({'PrivateKeyInfo', v1,
 	{'PrivateKeyInfo_privateKeyAlgorithm', ?'rsaEncryption', _}, PrivKey, _}) ->
 	der_decode('RSAPrivateKey', PrivKey);
@@ -351,10 +369,10 @@ der_encode('PrivateKeyInfo', #'RSAPrivateKey'{} = PrivKey) ->
 der_encode('PrivateKeyInfo', {#'RSAPrivateKey'{} = PrivKey, Parameters}) ->
     der_encode('PrivateKeyInfo',
                {'PrivateKeyInfo', v1,
-                {'PrivateKeyInfo_privateKeyAlgorithm', ?'id-RSASSA-PSS', 
+                {'PrivateKeyInfo_privateKeyAlgorithm', ?'id-RSASSA-PSS',
                  {asn1_OPENTYPE, der_encode('RSASSA-PSS-params', Parameters)}},
                 der_encode('RSAPrivateKey', PrivKey), asn1_NOVALUE});
-der_encode('PrivateKeyInfo', #'ECPrivateKey'{parameters = {namedCurve, CurveOId} = Parameters,
+der_encode('PrivateKeyInfo', #'ECPrivateKey'{parameters = {namedCurve, CurveOId},
                                              privateKey = PrivKey}) when
       CurveOId == ?'id-Ed25519' orelse
       CurveOId == ?'id-Ed448' ->
@@ -369,7 +387,19 @@ der_encode('PrivateKeyInfo', #'ECPrivateKey'{parameters = Parameters} = PrivKey)
                  {asn1_OPENTYPE, der_encode('EcpkParameters', Parameters)}},
                 der_encode('ECPrivateKey', PrivKey#'ECPrivateKey'{parameters = asn1_NOVALUE}), 
                 asn1_NOVALUE});
-der_encode(Asn1Type, Entity) when (Asn1Type == 'PrivateKeyInfo') or
+der_encode('OneAsymmetricKey', #'ECPrivateKey'{parameters = {namedCurve, CurveOId},
+                                               privateKey = PrivKey,
+                                               attributes = Attr,
+                                               publicKey = PubKey}) ->
+    der_encode('OneAsymmetricKey',
+               {'OneAsymmetricKey', 1,
+		{'OneAsymmetricKey_privateKeyAlgorithm', CurveOId, asn1_NOVALUE},
+                PrivKey,
+                Attr,
+                PubKey
+               });
+der_encode(Asn1Type, Entity) when (Asn1Type == 'PrivateKeyInfo') orelse
+                                  (Asn1Type == 'OneAsymmetricKey') orelse
 				  (Asn1Type == 'EncryptedPrivateKeyInfo') ->
      try
          {ok, Encoded} = 'PKCS-FRAME':encode(Asn1Type, Entity),
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index ae16eca83e..bbd458c036 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -57,6 +57,8 @@
          eddsa_priv_pkcs8/1,
          eddsa_pub/0,
          eddsa_pub/1,
+         eddsa_priv_rfc5958/0,
+         eddsa_priv_rfc5958/1,
          init_ec_pem_encode_generated/1,
          ec_pem_encode_generated/0,
          ec_pem_encode_generated/1,
@@ -159,7 +161,7 @@ groups() ->
     [{pem_decode_encode, [], [dsa_pem, rsa_pem, rsa_pss_pss_pem, ec_pem, encrypted_pem,
 			      dh_pem, cert_pem, pkcs7_pem, pkcs10_pem, ec_pem2,
 			      rsa_priv_pkcs8, dsa_priv_pkcs8, ec_priv_pkcs8,
-                              eddsa_priv_pkcs8,
+                              eddsa_priv_pkcs8, eddsa_priv_rfc5958,
                               ec_pem_encode_generated,
                               gen_ec_param_prime_field, gen_ec_param_char_2_field
                              ]},
@@ -415,6 +417,19 @@ eddsa_pub(Config) when is_list(Config) ->
     ECPemNoEndNewLines = strip_superfluous_newlines(EDDSAPubPem),
     ECPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([PemEntry])).
 
+eddsa_priv_rfc5958() ->
+    [{doc, "EDDSA PKCS8 private key decode/encode"}].
+eddsa_priv_rfc5958(Config) when is_list(Config) ->
+    Datadir = proplists:get_value(data_dir, Config),
+    {ok, ECPrivPem} = file:read_file(filename:join(Datadir, "eddsa_key_rfc5958.pem")),
+    [{'PrivateKeyInfo', _, not_encrypted} = PKCS8Key] = public_key:pem_decode(ECPrivPem),
+    ECPrivKey = public_key:pem_entry_decode(PKCS8Key),
+    true = check_entry_type(ECPrivKey, 'ECPrivateKey'),
+    true = ECPrivKey#'ECPrivateKey'.parameters == {namedCurve, ?'id-Ed25519'},
+    PrivEntry0 = public_key:pem_entry_encode('OneAsymmetricKey', ECPrivKey),
+    ECPemNoEndNewLines = strip_superfluous_newlines(ECPrivPem),
+    ECPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([PrivEntry0])).
+
 init_ec_pem_encode_generated(Config) ->
     case catch true = lists:member('secp384r1', crypto:ec_curves()) of
         {'EXIT', _} -> {skip, {'secp384r1', not_supported}};
diff --git a/lib/public_key/test/public_key_SUITE_data/eddsa_key_rfc5958.pem b/lib/public_key/test/public_key_SUITE_data/eddsa_key_rfc5958.pem
new file mode 100644
index 0000000000..7179f294ba
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/eddsa_key_rfc5958.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
+oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB
+Z9w7lshQhqowtrbLDFw4rXAxZuE=
+-----END PRIVATE KEY-----
\ No newline at end of file
-- 
2.26.2

openSUSE Build Service is sponsored by