[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RSA signatures with SHA2 (RFC 8332 and RFC 8308) (Was: (Client side) RSA signatures with SHA2 (RFC 8332 and RFC 8308))


Hello,
the current patch-set provides also the server side implementation of
the SHA2 extension, which is tested with current tests against OpenSSH.

There are few partially related changes, such as follow up on SSH1
removal and support for PubkeyAcceptedTypes as discussed before.

While testing server, I noticed some issues with incomplete support for
the ed25519 keys so this is also in.

Regards,
Jakub

On Fri, 2018-06-29 at 18:21 +0200, Jakub Jelen wrote:
> On Fri, 2018-06-29 at 16:30 +0200, Andreas Schneider wrote:
> > On Wednesday, 27 June 2018 15:23:08 CEST Jakub Jelen wrote:
> > > Hello,
> > 
> > Hi Jakub,
> > 
> > > The attached are patches to implement extension negotiation for
> > > SSH
> > > (RFC 8308) and a new RSA signatures with SHA2 (RFC 8332), which
> > > are
> > > negotiated using this mechanism and already used for few years in
> > > OpenSSH.
> > 
> > thank you very much for your contribution. Could you please rebase
> > them on 
> > current master and resend the patchset?
> 
> I attached the rebased version. I will work on other points.
> 
> > > While debugging the initial implementation, I noticed failures in
> > > torture_connect_double test, which showed up the disconnect was
> > > not
> > > properly resetting all the session structures and values to be
> > > ready
> > > for the new connection. This worked before, because just the
> > > connect
> > > did not use the negotiated crypto before [first patch].
> > 
> > Nice catch. Someone complained about that in the IRC channel
> > already.
> 
> This patch is not related, but it can be already merged if you will
> not
> spot some significant issue in there that I missed.
> 
> There is an attached reproducer, that makes the server fail with
> current master, but I was not able to capture the server failure in
> the
> preauthentication phase from the client side (while server obviously
> ends):
> 
> Bad packet length 3528793056. [preauth]
> debug3: send packet: type 1 [preauth]
> debug3: mm_request_send entering: type 122 [preauth]
> debug3: mm_request_receive entering
> debug3: monitor_read: checking request 122
> debug3: mm_request_send entering: type 123
> debug3: mm_request_receive_expect entering: type 123 [preauth]
> debug3: mm_request_receive entering [preauth]
> ssh_dispatch_run_fatal: Connection from 127.0.0.21 port 10559:
> message
> authentication code incorrect     [preauth]
> 
> > > Then there are few more minor issues that I hit during the work
> > > throughout the code.
> > > 
> > > From the RFC 8332 (SHA2 extension), Section 2 [1], it is not
> > > completely
> > > clear whether the the new algorithms are supposed to reference
> > > only
> > > the
> > > signatures in the code, or they can reference also the
> > > public/private
> > > keys themselves. The distinction is somehow fuzzy also in the
> > > OpenSSH
> > > code if I remember well, so this is one thing to consideration.
> > > 
> > > Another thing to consider is possibility to
> > > configure/enable/disable
> > > these algorithms if needed. This is done in OpenSSH with
> > > PubkeyAcceptedKeyTypes option, which is not implemented in libssh
> > > at
> > > this moment, but should not be too hard to handle (I can do that,
> > > if
> > > you would consider it make sense).
> > 
> > I think it would make sense to support that.
> 
> I will create a separate patch.
>  
> > > The host keys are tested in a new test torture_hostkey, which
> > > tries
> > > all
> > > the supported mechanisms and verifies the server key signature is
> > > properly validated.
> > > 
> > > The second part is about using this extension for public key
> > > authentication, which adds the signature counterparts for the
> > > verify
> > > added in the previous part. This requires addition to the PKI api
> > > to
> > > specify the algorithm to use in addition to the key itself. The
> > > attached patch provides the verification that signatures provided
> > > by
> > > this interface are verifiable.
> > 
> > Does this touch public API? I haven't looked into them yet.
> 
> Yes, it adds the new key/signature types. If we leave them as a key
> types also or create some new enum for signature types, that is other
> question, but key types enum is in public API now.
> 
> > > The last part is about ability to request the same signatures
> > > from
> > > compatible ssh-agent using the flags described in the ssh-agent
> > > protocol.
> > > 
> > > What is missing is a server side implementation: The server
> > > recognizing
> > > the extension, sending its supported algorithms, sending the host
> > > keys
> > > signatures using the new algorithms and verifying the new
> > > signatures
> > > during authentication (should work, but needs to be verified). I
> > > did
> > > not go into that, because of the lack of the test suite for the
> > > server.
> > > Are there any plans for introducing tests for server?
> > 
> > This has been added now, see tests/pkd
> > 
> > configure with: -DWITH_SERVER=ON -DSERVER_TESTING=ON
> 
> Thanks. I will try with the server. This will make it significantly
> easier to test.
> 
> Regards,
-- 
Jakub Jelen
Software Engineer
Security Technologies
Red Hat, Inc.
From 2f539171cd7bdd70fea4edb64fc3a943bcba7162 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 2 Jul 2018 14:34:16 +0200
Subject: [PATCH 01/19] test: Fix test labels (copy & paste error)

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 src/options.c                     | 2 +-
 tests/unittests/torture_options.c | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/options.c b/src/options.c
index 97c37e63..87c5bb42 100644
--- a/src/options.c
+++ b/src/options.c
@@ -268,7 +268,7 @@ int ssh_options_set_algo(ssh_session session,
  *                Deprecated
  *
  *              - SSH_OPTIONS_SSH2:
-                  Unused
+ *                Unused
  *
  *              - SSH_OPTIONS_LOG_VERBOSITY:
  *                Set the session logging verbosity (int).\n
diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c
index d0d11d4d..a3000aec 100644
--- a/tests/unittests/torture_options.c
+++ b/tests/unittests/torture_options.c
@@ -100,7 +100,7 @@ static void torture_options_set_hostkey(void **state) {
     assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
                         "ssh-ed25519,ecdsa-sha2-nistp384,ssh-rsa");
 
-    /* Test one unknown kex */
+    /* Test one unknown host key */
     rc = ssh_options_set(session,
                          SSH_OPTIONS_HOSTKEYS,
                          "ssh-ed25519,unknown-crap@xxxxxxxxxxx,ssh-rsa");
@@ -108,7 +108,7 @@ static void torture_options_set_hostkey(void **state) {
     assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS],
                         "ssh-ed25519,ssh-rsa");
 
-    /* Test all unknown kexes */
+    /* Test all unknown host keys */
     rc = ssh_options_set(session,
                          SSH_OPTIONS_HOSTKEYS,
                          "unknown-crap@xxxxxxxxxxx,more-crap@xxxxxxxxxxx");
-- 
2.17.1


From c801188aabde78e10dc07320c3591e4f31654414 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 2 Jul 2018 16:38:16 +0200
Subject: [PATCH 02/19] session: Do not search for RSA1 keys in ~/.ssh/identity

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 src/session.c | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/src/session.c b/src/session.c
index 272b3428..35841183 100644
--- a/src/session.c
+++ b/src/session.c
@@ -151,15 +151,6 @@ ssh_session ssh_new(void) {
     }
 #endif
 
-    id = strdup("%d/identity");
-    if (id == NULL) {
-      goto err;
-    }
-    rc = ssh_list_append(session->opts.identity, id);
-    if (rc == SSH_ERROR) {
-      goto err;
-    }
-
     return session;
 
 err:
-- 
2.17.1


From 339564cb2efed10e5695550d54cb378d4ae3a7f7 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Fri, 13 Jul 2018 16:21:29 +0200
Subject: [PATCH 03/19] bind: Complete loading ed25519 in server

Previously, the support was only partial and if the ed25519 key was
the only one, the internal checks were failing the tests.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 src/bind.c | 36 ++++++++++++++++++++++++++++++++----
 1 file changed, 32 insertions(+), 4 deletions(-)

diff --git a/src/bind.c b/src/bind.c
index 47837259..7b350d9a 100644
--- a/src/bind.c
+++ b/src/bind.c
@@ -149,9 +149,10 @@ static int ssh_bind_import_keys(ssh_bind sshbind) {
 
   if (sshbind->ecdsakey == NULL &&
       sshbind->dsakey == NULL &&
-      sshbind->rsakey == NULL) {
+      sshbind->rsakey == NULL &&
+      sshbind->ed25519key == NULL) {
       ssh_set_error(sshbind, SSH_FATAL,
-                    "ECDSA, DSA, or RSA host key file must be set");
+                    "ECDSA, ED25519, DSA, or RSA host key file must be set");
       return SSH_ERROR;
   }
 
@@ -223,6 +224,27 @@ static int ssh_bind_import_keys(ssh_bind sshbind) {
       }
   }
 
+  if (sshbind->ed25519 == NULL && sshbind->ed25519key != NULL) {
+      rc = ssh_pki_import_privkey_file(sshbind->ed25519key,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       &sshbind->ed25519);
+      if (rc == SSH_ERROR || rc == SSH_EOF) {
+          ssh_set_error(sshbind, SSH_FATAL,
+                  "Failed to import private ED25519 host key");
+          return SSH_ERROR;
+      }
+
+      if (ssh_key_type(sshbind->ed25519) != SSH_KEYTYPE_ED25519) {
+          ssh_set_error(sshbind, SSH_FATAL,
+                  "The ED25519 host key has the wrong type");
+          ssh_key_free(sshbind->ed25519);
+          sshbind->ed25519 = NULL;
+          return SSH_ERROR;
+      }
+  }
+
   return SSH_OK;
 }
 
@@ -236,7 +258,10 @@ int ssh_bind_listen(ssh_bind sshbind) {
     return -1;
   }
 
-  if (sshbind->rsa == NULL && sshbind->dsa == NULL && sshbind->ecdsa == NULL) {
+  if (sshbind->rsa == NULL &&
+      sshbind->dsa == NULL &&
+      sshbind->ecdsa == NULL &&
+      sshbind->ed25519 == NULL) {
       rc = ssh_bind_import_keys(sshbind);
       if (rc != SSH_OK) {
           return SSH_ERROR;
@@ -255,6 +280,7 @@ int ssh_bind_listen(ssh_bind sshbind) {
           sshbind->dsa = NULL;
           ssh_key_free(sshbind->rsa);
           sshbind->rsa = NULL;
+          /* XXX should this clear also other structures that were allocated */
           return -1;
       }
 
@@ -267,6 +293,7 @@ int ssh_bind_listen(ssh_bind sshbind) {
           sshbind->dsa = NULL;
           ssh_key_free(sshbind->rsa);
           sshbind->rsa = NULL;
+          /* XXX should this clear also other structures that were allocated */
           return -1;
       }
 
@@ -434,7 +461,8 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
      */
     if (sshbind->rsa == NULL &&
         sshbind->dsa == NULL &&
-        sshbind->ecdsa == NULL) {
+        sshbind->ecdsa == NULL &&
+        sshbind->ed25519 == NULL) {
         rc = ssh_bind_import_keys(sshbind);
         if (rc != SSH_OK) {
             return SSH_ERROR;
-- 
2.17.1


From 4a7e58fc7166d6f35d309fd8fe6e2a4595396a72 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Fri, 13 Jul 2018 16:25:57 +0200
Subject: [PATCH 04/19] pkd: Support ed25519 host keys in server

This adds support for the ed25519 keys in the pkd framework and adds
openssh-only tests utilizing these host keys (dropbear does not support
them yet).

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 tests/pkd/pkd_daemon.c  |  2 ++
 tests/pkd/pkd_daemon.h  |  1 +
 tests/pkd/pkd_hello.c   | 77 +++++++++++++++++++++++++++++++++++++++++
 tests/pkd/pkd_keyutil.c | 13 +++++++
 tests/pkd/pkd_keyutil.h |  3 ++
 5 files changed, 96 insertions(+)

diff --git a/tests/pkd/pkd_daemon.c b/tests/pkd/pkd_daemon.c
index fc14d33c..6c9c3592 100644
--- a/tests/pkd/pkd_daemon.c
+++ b/tests/pkd/pkd_daemon.c
@@ -253,6 +253,8 @@ static int pkd_exec_hello(int fd, struct pkd_daemon_args *args) {
 
     if (type == PKD_RSA) {
         opts = SSH_BIND_OPTIONS_RSAKEY;
+    } else if (type == PKD_ED25519) {
+        opts = SSH_BIND_OPTIONS_HOSTKEY;
 #ifdef HAVE_DSA
     } else if (type == PKD_DSA) {
         opts = SSH_BIND_OPTIONS_DSAKEY;
diff --git a/tests/pkd/pkd_daemon.h b/tests/pkd/pkd_daemon.h
index abb07acd..f3ef4e67 100644
--- a/tests/pkd/pkd_daemon.h
+++ b/tests/pkd/pkd_daemon.h
@@ -15,6 +15,7 @@ enum pkd_hostkey_type_e {
 #ifdef HAVE_DSA
     PKD_DSA,
 #endif
+    PKD_ED25519,
     PKD_ECDSA
 };
 
diff --git a/tests/pkd/pkd_hello.c b/tests/pkd/pkd_hello.c
index 0c55a294..22976c95 100644
--- a/tests/pkd/pkd_hello.c
+++ b/tests/pkd/pkd_hello.c
@@ -152,6 +152,13 @@ static int torture_pkd_setup_rsa(void **state) {
     return 0;
 }
 
+static int torture_pkd_setup_ed25519(void **state) {
+    setup_ed25519_key();
+    *state = (void *) torture_pkd_setup(PKD_ED25519, LIBSSH_ED25519_TESTKEY);
+
+    return 0;
+}
+
 #ifdef HAVE_DSA
 static int torture_pkd_setup_dsa(void **state) {
     setup_dsa_key();
@@ -203,6 +210,10 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
     f(client, ecdsa_521_default,  cmd,  setup_ecdsa_521,  teardown)
 #endif
 
+#define PKDTESTS_DEFAULT_OPENSSHONLY(f, client, cmd) \
+    /* Default passes by server key type. */ \
+    f(client, ed25519_default,    cmd,  setup_ed25519,    teardown)
+
 #ifdef HAVE_DSA
 #define PKDTESTS_KEX(f, client, kexcmd) \
     /* Kex algorithms. */ \
@@ -266,6 +277,29 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
     f(client, ecdsa_521_diffie_hellman_group1_sha1,   kexcmd("diffie-hellman-group1-sha1"),    setup_ecdsa_521,  teardown)
 #endif
 
+
+#ifdef HAVE_DSA
+#define PKDTESTS_KEX_OPENSSHONLY(f, client, kexcmd) \
+    /* Kex algorithms. */ \
+    f(client, ed25519_curve25519_sha256,              kexcmd("curve25519-sha256"),             setup_ed25519,    teardown) \
+    f(client, ed25519_curve25519_sha256_libssh_org,   kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_ed25519,    teardown) \
+    f(client, ed25519_ecdh_sha2_nistp256,             kexcmd("ecdh-sha2-nistp256"),            setup_ed25519,    teardown) \
+    f(client, ed25519_ecdh_sha2_nistp384,             kexcmd("ecdh-sha2-nistp384"),            setup_ed25519,    teardown) \
+    f(client, ed25519_ecdh_sha2_nistp521,             kexcmd("ecdh-sha2-nistp521"),            setup_ed25519,    teardown) \
+    f(client, ed25519_diffie_hellman_group14_sha1,    kexcmd("diffie-hellman-group14-sha1"),   setup_ed25519,    teardown) \
+    f(client, ed25519_diffie_hellman_group1_sha1,     kexcmd("diffie-hellman-group1-sha1"),    setup_ed25519,    teardown)
+#else
+#define PKDTESTS_KEX_OPENSSHONLY(f, client, kexcmd) \
+    /* Kex algorithms. */ \
+    f(client, ed25519_curve25519_sha256,              kexcmd("curve25519-sha256"),             setup_ed25519,    teardown) \
+    f(client, ed25519_curve25519_sha256_libssh_org,   kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_ed25519,    teardown) \
+    f(client, ed25519_ecdh_sha2_nistp256,             kexcmd("ecdh-sha2-nistp256"),            setup_ed25519,    teardown) \
+    f(client, ed25519_ecdh_sha2_nistp384,             kexcmd("ecdh-sha2-nistp384"),            setup_ed25519,    teardown) \
+    f(client, ed25519_ecdh_sha2_nistp521,             kexcmd("ecdh-sha2-nistp521"),            setup_ed25519,    teardown) \
+    f(client, ed25519_diffie_hellman_group14_sha1,    kexcmd("diffie-hellman-group14-sha1"),   setup_ed25519,    teardown) \
+    f(client, ed25519_diffie_hellman_group1_sha1,     kexcmd("diffie-hellman-group1-sha1"),    setup_ed25519,    teardown)
+#endif
+
 #ifdef HAVE_DSA
 #define PKDTESTS_CIPHER(f, client, ciphercmd) \
     /* Ciphers. */ \
@@ -330,6 +364,14 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
     f(client, dsa_aes192_cbc,          ciphercmd("aes192-cbc"),    setup_dsa,        teardown) \
     f(client, dsa_aes192_ctr,          ciphercmd("aes192-ctr"),    setup_dsa,        teardown) \
     f(client, dsa_chacha20,            ciphercmd(CHACHA20),        setup_dsa,        teardown) \
+    f(client, ed25519_3des_cbc,        ciphercmd("3des-cbc"),      setup_ed25519,    teardown) \
+    f(client, ed25519_aes128_cbc,      ciphercmd("aes128-cbc"),    setup_ed25519,    teardown) \
+    f(client, ed25519_aes128_ctr,      ciphercmd("aes128-ctr"),    setup_ed25519,    teardown) \
+    f(client, ed25519_aes256_cbc,      ciphercmd("aes256-cbc"),    setup_ed25519,    teardown) \
+    f(client, ed25519_aes256_ctr,      ciphercmd("aes256-ctr"),    setup_ed25519,    teardown) \
+    f(client, ed25519_aes192_cbc,      ciphercmd("aes192-cbc"),    setup_ed25519,    teardown) \
+    f(client, ed25519_aes192_ctr,      ciphercmd("aes192-ctr"),    setup_ed25519,    teardown) \
+    f(client, ed25519_chacha20,        ciphercmd(CHACHA20),        setup_ed25519,    teardown) \
     f(client, ecdsa_256_aes192_cbc,    ciphercmd("aes192-cbc"),    setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_aes192_ctr,    ciphercmd("aes192-ctr"),    setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_chacha20,      ciphercmd(CHACHA20),        setup_ecdsa_256,  teardown) \
@@ -345,6 +387,14 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
     f(client, rsa_aes192_cbc,          ciphercmd("aes192-cbc"),    setup_rsa,        teardown) \
     f(client, rsa_aes192_ctr,          ciphercmd("aes192-ctr"),    setup_rsa,        teardown) \
     f(client, rsa_chacha20,            ciphercmd(CHACHA20),        setup_rsa,        teardown) \
+    f(client, ed25519_3des_cbc,        ciphercmd("3des-cbc"),      setup_ed25519,    teardown) \
+    f(client, ed25519_aes128_cbc,      ciphercmd("aes128-cbc"),    setup_ed25519,    teardown) \
+    f(client, ed25519_aes128_ctr,      ciphercmd("aes128-ctr"),    setup_ed25519,    teardown) \
+    f(client, ed25519_aes256_cbc,      ciphercmd("aes256-cbc"),    setup_ed25519,    teardown) \
+    f(client, ed25519_aes256_ctr,      ciphercmd("aes256-ctr"),    setup_ed25519,    teardown) \
+    f(client, ed25519_aes192_cbc,      ciphercmd("aes192-cbc"),    setup_ed25519,    teardown) \
+    f(client, ed25519_aes192_ctr,      ciphercmd("aes192-ctr"),    setup_ed25519,    teardown) \
+    f(client, ed25519_chacha20,        ciphercmd(CHACHA20),        setup_ed25519,    teardown) \
     f(client, ecdsa_256_aes192_cbc,    ciphercmd("aes192-cbc"),    setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_aes192_ctr,    ciphercmd("aes192-ctr"),    setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_chacha20,      ciphercmd(CHACHA20),        setup_ecdsa_256,  teardown) \
@@ -370,8 +420,11 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
     f(client, ecdsa_384_hmac_sha2_256,  maccmd("hmac-sha2-256"),  setup_ecdsa_384,  teardown) \
     f(client, ecdsa_521_hmac_sha2_256,  maccmd("hmac-sha2-256"),  setup_ecdsa_521,  teardown)
 #define PKDTESTS_MAC_OPENSSHONLY(f, client, maccmd) \
+    f(client, ed25519_hmac_sha1,        maccmd("hmac-sha1"),      setup_ed25519,    teardown) \
+    f(client, ed25519_hmac_sha2_256,    maccmd("hmac-sha2-256"),  setup_ed25519,    teardown) \
     f(client, rsa_hmac_sha2_512,        maccmd("hmac-sha2-512"),  setup_rsa,        teardown) \
     f(client, dsa_hmac_sha2_512,        maccmd("hmac-sha2-512"),  setup_dsa,        teardown) \
+    f(client, ed25519_hmac_sha2_512,    maccmd("hmac-sha2-512"),  setup_ed25519,    teardown) \
     f(client, ecdsa_256_hmac_sha2_512,  maccmd("hmac-sha2-512"),  setup_ecdsa_256,  teardown) \
     f(client, ecdsa_384_hmac_sha2_512,  maccmd("hmac-sha2-512"),  setup_ecdsa_384,  teardown) \
     f(client, ecdsa_521_hmac_sha2_512,  maccmd("hmac-sha2-512"),  setup_ecdsa_521,  teardown)
@@ -387,7 +440,10 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
     f(client, ecdsa_384_hmac_sha2_256,  maccmd("hmac-sha2-256"),  setup_ecdsa_384,  teardown) \
     f(client, ecdsa_521_hmac_sha2_256,  maccmd("hmac-sha2-256"),  setup_ecdsa_521,  teardown)
 #define PKDTESTS_MAC_OPENSSHONLY(f, client, maccmd) \
+    f(client, ed25519_hmac_sha1,        maccmd("hmac-sha1"),      setup_ed25519,    teardown) \
+    f(client, ed25519_hmac_sha2_256,    maccmd("hmac-sha2-256"),  setup_ed25519,    teardown) \
     f(client, rsa_hmac_sha2_512,        maccmd("hmac-sha2-512"),  setup_rsa,        teardown) \
+    f(client, ed25519_hmac_sha2_512,    maccmd("hmac-sha2-512"),  setup_ed25519,    teardown) \
     f(client, ecdsa_256_hmac_sha2_512,  maccmd("hmac-sha2-512"),  setup_ecdsa_256,  teardown) \
     f(client, ecdsa_384_hmac_sha2_512,  maccmd("hmac-sha2-512"),  setup_ecdsa_384,  teardown) \
     f(client, ecdsa_521_hmac_sha2_512,  maccmd("hmac-sha2-512"),  setup_ecdsa_521,  teardown)
@@ -441,7 +497,9 @@ static void torture_pkd_runtest(const char *testname,
 #ifdef HAVE_DSA
 #define CLIENT_ID_FILE OPENSSH_DSA_TESTKEY
 PKDTESTS_DEFAULT(emit_keytest, openssh_dsa, OPENSSH_CMD)
+PKDTESTS_DEFAULT_OPENSSHONLY(emit_keytest, openssh_dsa, OPENSSH_CMD)
 PKDTESTS_KEX(emit_keytest, openssh_dsa, OPENSSH_KEX_CMD)
+PKDTESTS_KEX_OPENSSHONLY(emit_keytest, openssh_dsa, OPENSSH_KEX_CMD)
 PKDTESTS_CIPHER(emit_keytest, openssh_dsa, OPENSSH_CIPHER_CMD)
 PKDTESTS_CIPHER_OPENSSHONLY(emit_keytest, openssh_dsa, OPENSSH_CIPHER_CMD)
 PKDTESTS_MAC(emit_keytest, openssh_dsa, OPENSSH_MAC_CMD)
@@ -451,7 +509,9 @@ PKDTESTS_MAC_OPENSSHONLY(emit_keytest, openssh_dsa, OPENSSH_MAC_CMD)
 
 #define CLIENT_ID_FILE OPENSSH_RSA_TESTKEY
 PKDTESTS_DEFAULT(emit_keytest, openssh_rsa, OPENSSH_CMD)
+PKDTESTS_DEFAULT_OPENSSHONLY(emit_keytest, openssh_rsa, OPENSSH_CMD)
 PKDTESTS_KEX(emit_keytest, openssh_rsa, OPENSSH_KEX_CMD)
+PKDTESTS_KEX_OPENSSHONLY(emit_keytest, openssh_rsa, OPENSSH_KEX_CMD)
 PKDTESTS_CIPHER(emit_keytest, openssh_rsa, OPENSSH_CIPHER_CMD)
 PKDTESTS_CIPHER_OPENSSHONLY(emit_keytest, openssh_rsa, OPENSSH_CIPHER_CMD)
 PKDTESTS_MAC(emit_keytest, openssh_rsa, OPENSSH_MAC_CMD)
@@ -460,7 +520,9 @@ PKDTESTS_MAC_OPENSSHONLY(emit_keytest, openssh_rsa, OPENSSH_MAC_CMD)
 
 #define CLIENT_ID_FILE OPENSSH_ECDSA256_TESTKEY
 PKDTESTS_DEFAULT(emit_keytest, openssh_e256, OPENSSH_CMD)
+PKDTESTS_DEFAULT_OPENSSHONLY(emit_keytest, openssh_e256, OPENSSH_CMD)
 PKDTESTS_KEX(emit_keytest, openssh_e256, OPENSSH_KEX_CMD)
+PKDTESTS_KEX_OPENSSHONLY(emit_keytest, openssh_e256, OPENSSH_KEX_CMD)
 PKDTESTS_CIPHER(emit_keytest, openssh_e256, OPENSSH_CIPHER_CMD)
 PKDTESTS_CIPHER_OPENSSHONLY(emit_keytest, openssh_e256, OPENSSH_CIPHER_CMD)
 PKDTESTS_MAC(emit_keytest, openssh_e256, OPENSSH_MAC_CMD)
@@ -473,7 +535,9 @@ PKDTESTS_MAC_OPENSSHONLY(emit_keytest, openssh_e256, OPENSSH_MAC_CMD)
 
 #define CLIENT_ID_FILE OPENSSH_ED25519_TESTKEY
 PKDTESTS_DEFAULT(emit_keytest, openssh_ed, OPENSSH_CMD)
+PKDTESTS_DEFAULT_OPENSSHONLY(emit_keytest, openssh_ed, OPENSSH_CMD)
 PKDTESTS_KEX(emit_keytest, openssh_ed, OPENSSH_KEX_CMD)
+PKDTESTS_KEX_OPENSSHONLY(emit_keytest, openssh_ed, OPENSSH_KEX_CMD)
 PKDTESTS_CIPHER(emit_keytest, openssh_ed, OPENSSH_CIPHER_CMD)
 PKDTESTS_CIPHER_OPENSSHONLY(emit_keytest, openssh_ed, OPENSSH_CIPHER_CMD)
 PKDTESTS_MAC(emit_keytest, openssh_ed, OPENSSH_MAC_CMD)
@@ -511,7 +575,9 @@ struct {
     /* OpenSSH */
 #ifdef HAVE_DSA
     PKDTESTS_DEFAULT(emit_testmap, openssh_dsa, OPENSSH_CMD)
+    PKDTESTS_DEFAULT_OPENSSHONLY(emit_testmap, openssh_dsa, OPENSSH_CMD)
     PKDTESTS_KEX(emit_testmap, openssh_dsa, OPENSSH_KEX_CMD)
+    PKDTESTS_KEX_OPENSSHONLY(emit_testmap, openssh_dsa, OPENSSH_KEX_CMD)
     PKDTESTS_CIPHER(emit_testmap, openssh_dsa, OPENSSH_CIPHER_CMD)
     PKDTESTS_CIPHER_OPENSSHONLY(emit_testmap, openssh_dsa, OPENSSH_CIPHER_CMD)
     PKDTESTS_MAC(emit_testmap, openssh_dsa, OPENSSH_MAC_CMD)
@@ -519,21 +585,27 @@ struct {
 #endif
 
     PKDTESTS_DEFAULT(emit_testmap, openssh_rsa, OPENSSH_CMD)
+    PKDTESTS_DEFAULT_OPENSSHONLY(emit_testmap, openssh_rsa, OPENSSH_CMD)
     PKDTESTS_KEX(emit_testmap, openssh_rsa, OPENSSH_KEX_CMD)
+    PKDTESTS_KEX_OPENSSHONLY(emit_testmap, openssh_rsa, OPENSSH_KEX_CMD)
     PKDTESTS_CIPHER(emit_testmap, openssh_rsa, OPENSSH_CIPHER_CMD)
     PKDTESTS_CIPHER_OPENSSHONLY(emit_testmap, openssh_rsa, OPENSSH_CIPHER_CMD)
     PKDTESTS_MAC(emit_testmap, openssh_rsa, OPENSSH_MAC_CMD)
     PKDTESTS_MAC_OPENSSHONLY(emit_testmap, openssh_rsa, OPENSSH_MAC_CMD)
 
     PKDTESTS_DEFAULT(emit_testmap, openssh_e256, OPENSSH_CMD)
+    PKDTESTS_DEFAULT_OPENSSHONLY(emit_testmap, openssh_e256, OPENSSH_CMD)
     PKDTESTS_KEX(emit_testmap, openssh_e256, OPENSSH_KEX_CMD)
+    PKDTESTS_KEX_OPENSSHONLY(emit_testmap, openssh_e256, OPENSSH_KEX_CMD)
     PKDTESTS_CIPHER(emit_testmap, openssh_e256, OPENSSH_CIPHER_CMD)
     PKDTESTS_CIPHER_OPENSSHONLY(emit_testmap, openssh_e256, OPENSSH_CIPHER_CMD)
     PKDTESTS_MAC(emit_testmap, openssh_e256, OPENSSH_MAC_CMD)
     PKDTESTS_MAC_OPENSSHONLY(emit_testmap, openssh_e256, OPENSSH_MAC_CMD)
 
     PKDTESTS_DEFAULT(emit_testmap, openssh_ed, OPENSSH_CMD)
+    PKDTESTS_DEFAULT_OPENSSHONLY(emit_testmap, openssh_ed, OPENSSH_CMD)
     PKDTESTS_KEX(emit_testmap, openssh_ed, OPENSSH_KEX_CMD)
+    PKDTESTS_KEX_OPENSSHONLY(emit_testmap, openssh_ed, OPENSSH_KEX_CMD)
     PKDTESTS_CIPHER(emit_testmap, openssh_ed, OPENSSH_CIPHER_CMD)
     PKDTESTS_CIPHER_OPENSSHONLY(emit_testmap, openssh_ed, OPENSSH_CIPHER_CMD)
     PKDTESTS_MAC(emit_testmap, openssh_ed, OPENSSH_MAC_CMD)
@@ -562,6 +634,7 @@ static int pkd_run_tests(void) {
     const struct CMUnitTest openssh_tests[] = {
 #ifdef HAVE_DSA
         PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_dsa, OPENSSH_CMD)
+        PKDTESTS_DEFAULT_OPENSSHONLY(emit_unit_test_comma, openssh_dsa, OPENSSH_CMD)
         PKDTESTS_KEX(emit_unit_test_comma, openssh_dsa, OPENSSH_KEX_CMD)
         PKDTESTS_CIPHER(emit_unit_test_comma, openssh_dsa, OPENSSH_CIPHER_CMD)
         PKDTESTS_CIPHER_OPENSSHONLY(emit_unit_test_comma, openssh_dsa, OPENSSH_CIPHER_CMD)
@@ -570,6 +643,7 @@ static int pkd_run_tests(void) {
 #endif
 
         PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_rsa, OPENSSH_CMD)
+        PKDTESTS_DEFAULT_OPENSSHONLY(emit_unit_test_comma, openssh_rsa, OPENSSH_CMD)
         PKDTESTS_KEX(emit_unit_test_comma, openssh_rsa, OPENSSH_KEX_CMD)
         PKDTESTS_CIPHER(emit_unit_test_comma, openssh_rsa, OPENSSH_CIPHER_CMD)
         PKDTESTS_CIPHER_OPENSSHONLY(emit_unit_test_comma, openssh_rsa, OPENSSH_CIPHER_CMD)
@@ -577,6 +651,7 @@ static int pkd_run_tests(void) {
         PKDTESTS_MAC_OPENSSHONLY(emit_unit_test_comma, openssh_rsa, OPENSSH_MAC_CMD)
 
         PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_e256, OPENSSH_CMD)
+        PKDTESTS_DEFAULT_OPENSSHONLY(emit_unit_test_comma, openssh_e256, OPENSSH_CMD)
         PKDTESTS_KEX(emit_unit_test_comma, openssh_e256, OPENSSH_KEX_CMD)
         PKDTESTS_CIPHER(emit_unit_test_comma, openssh_e256, OPENSSH_CIPHER_CMD)
         PKDTESTS_CIPHER_OPENSSHONLY(emit_unit_test_comma, openssh_e256, OPENSSH_CIPHER_CMD)
@@ -584,6 +659,7 @@ static int pkd_run_tests(void) {
         PKDTESTS_MAC_OPENSSHONLY(emit_unit_test_comma, openssh_e256, OPENSSH_MAC_CMD)
 
         PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_ed, OPENSSH_CMD)
+        PKDTESTS_DEFAULT_OPENSSHONLY(emit_unit_test_comma, openssh_ed, OPENSSH_CMD)
         PKDTESTS_KEX(emit_unit_test_comma, openssh_ed, OPENSSH_KEX_CMD)
         PKDTESTS_CIPHER(emit_unit_test_comma, openssh_ed, OPENSSH_CIPHER_CMD)
         PKDTESTS_CIPHER_OPENSSHONLY(emit_unit_test_comma, openssh_ed, OPENSSH_CIPHER_CMD)
@@ -674,6 +750,7 @@ static int pkd_run_tests(void) {
 
     /* Clean up any server keys that were generated. */
     cleanup_rsa_key();
+    cleanup_ed25519_key();
 #ifdef HAVE_DSA
     cleanup_dsa_key();
 #endif
diff --git a/tests/pkd/pkd_keyutil.c b/tests/pkd/pkd_keyutil.c
index 7cb2ed48..14856d3c 100644
--- a/tests/pkd/pkd_keyutil.c
+++ b/tests/pkd/pkd_keyutil.c
@@ -27,6 +27,15 @@ void setup_rsa_key() {
     assert_int_equal(rc, 0);
 }
 
+void setup_ed25519_key() {
+    int rc = 0;
+    if (access(LIBSSH_ED25519_TESTKEY, F_OK) != 0) {
+        rc = system_checked(OPENSSH_KEYGEN " -t ed25519 -q -N \"\" -f "
+                            LIBSSH_ED25519_TESTKEY);
+    }
+    assert_int_equal(rc, 0);
+}
+
 #ifdef HAVE_DSA
 void setup_dsa_key() {
     int rc = 0;
@@ -67,6 +76,10 @@ void cleanup_rsa_key() {
     cleanup_key(LIBSSH_RSA_TESTKEY, LIBSSH_RSA_TESTKEY ".pub");
 }
 
+void cleanup_ed25519_key() {
+    cleanup_key(LIBSSH_ED25519_TESTKEY, LIBSSH_ED25519_TESTKEY ".pub");
+}
+
 #ifdef HAVE_DSA
 void cleanup_dsa_key() {
     cleanup_key(LIBSSH_DSA_TESTKEY, LIBSSH_DSA_TESTKEY ".pub");
diff --git a/tests/pkd/pkd_keyutil.h b/tests/pkd/pkd_keyutil.h
index 3d0ae5a7..b0750066 100644
--- a/tests/pkd/pkd_keyutil.h
+++ b/tests/pkd/pkd_keyutil.h
@@ -14,6 +14,7 @@
 #define LIBSSH_DSA_TESTKEY        "libssh_testkey.id_dsa"
 #endif
 #define LIBSSH_RSA_TESTKEY        "libssh_testkey.id_rsa"
+#define LIBSSH_ED25519_TESTKEY    "libssh_testkey.id_ed25519"
 #define LIBSSH_ECDSA_256_TESTKEY  "libssh_testkey.id_ecdsa256"
 #define LIBSSH_ECDSA_384_TESTKEY  "libssh_testkey.id_ecdsa384"
 #define LIBSSH_ECDSA_521_TESTKEY  "libssh_testkey.id_ecdsa521"
@@ -22,11 +23,13 @@
 void setup_dsa_key(void);
 #endif
 void setup_rsa_key(void);
+void setup_ed25519_key(void);
 void setup_ecdsa_keys(void);
 #ifdef HAVE_DSA
 void cleanup_dsa_key(void);
 #endif
 void cleanup_rsa_key(void);
+void cleanup_ed25519_key(void);
 void cleanup_ecdsa_keys(void);
 
 /* Client keys. */
-- 
2.17.1


From c6597ff51d7fb8944f3bec183acb6fb678f91905 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 16 Jul 2018 09:13:57 +0200
Subject: [PATCH 05/19] pkd: Add missing ECDH mechanisms + whitespace cleanup

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 tests/pkd/pkd_hello.c | 40 ++++++++++++++++++++++++----------------
 1 file changed, 24 insertions(+), 16 deletions(-)

diff --git a/tests/pkd/pkd_hello.c b/tests/pkd/pkd_hello.c
index 22976c95..c7713d49 100644
--- a/tests/pkd/pkd_hello.c
+++ b/tests/pkd/pkd_hello.c
@@ -219,9 +219,9 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
     /* Kex algorithms. */ \
     f(client, rsa_curve25519_sha256,                  kexcmd("curve25519-sha256"),             setup_rsa,        teardown) \
     f(client, rsa_curve25519_sha256_libssh_org,       kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_rsa,        teardown) \
-    f(client, rsa_ecdh_sha2_nistp256,                 kexcmd("ecdh-sha2-nistp256 "),           setup_rsa,        teardown) \
-    f(client, rsa_ecdh_sha2_nistp384,                 kexcmd("ecdh-sha2-nistp384 "),           setup_rsa,        teardown) \
-    f(client, rsa_ecdh_sha2_nistp521,                 kexcmd("ecdh-sha2-nistp521 "),           setup_rsa,        teardown) \
+    f(client, rsa_ecdh_sha2_nistp256,                 kexcmd("ecdh-sha2-nistp256"),            setup_rsa,        teardown) \
+    f(client, rsa_ecdh_sha2_nistp384,                 kexcmd("ecdh-sha2-nistp384"),            setup_rsa,        teardown) \
+    f(client, rsa_ecdh_sha2_nistp521,                 kexcmd("ecdh-sha2-nistp521"),            setup_rsa,        teardown) \
     f(client, rsa_diffie_hellman_group14_sha1,        kexcmd("diffie-hellman-group14-sha1"),   setup_rsa,        teardown) \
     f(client, rsa_diffie_hellman_group1_sha1,         kexcmd("diffie-hellman-group1-sha1"),    setup_rsa,        teardown) \
     f(client, dsa_curve25519_sha256,                  kexcmd("curve25519-sha256"),             setup_dsa,        teardown) \
@@ -233,23 +233,23 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
     f(client, dsa_diffie_hellman_group1_sha1,         kexcmd("diffie-hellman-group1-sha1"),    setup_dsa,        teardown) \
     f(client, ecdsa_256_curve25519_sha256,            kexcmd("curve25519-sha256"),             setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_ecdsa_256,  teardown) \
-    f(client, ecdsa_256_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256 "),           setup_ecdsa_256,  teardown) \
-    f(client, ecdsa_256_ecdh_sha2_nistp384,           kexcmd("ecdh-sha2-nistp384 "),           setup_ecdsa_256,  teardown) \
-    f(client, ecdsa_256_ecdh_sha2_nistp521,           kexcmd("ecdh-sha2-nistp521 "),           setup_ecdsa_256,  teardown) \
+    f(client, ecdsa_256_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256"),            setup_ecdsa_256,  teardown) \
+    f(client, ecdsa_256_ecdh_sha2_nistp384,           kexcmd("ecdh-sha2-nistp384"),            setup_ecdsa_256,  teardown) \
+    f(client, ecdsa_256_ecdh_sha2_nistp521,           kexcmd("ecdh-sha2-nistp521"),            setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_diffie_hellman_group14_sha1,  kexcmd("diffie-hellman-group14-sha1"),   setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_diffie_hellman_group1_sha1,   kexcmd("diffie-hellman-group1-sha1"),    setup_ecdsa_256,  teardown) \
     f(client, ecdsa_384_curve25519_sha256,            kexcmd("curve25519-sha256"),             setup_ecdsa_384,  teardown) \
     f(client, ecdsa_384_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_ecdsa_384,  teardown) \
-    f(client, ecdsa_384_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256 "),           setup_ecdsa_384,  teardown) \
-    f(client, ecdsa_384_ecdh_sha2_nistp384,           kexcmd("ecdh-sha2-nistp384 "),           setup_ecdsa_384,  teardown) \
-    f(client, ecdsa_384_ecdh_sha2_nistp521,           kexcmd("ecdh-sha2-nistp521 "),           setup_ecdsa_384,  teardown) \
+    f(client, ecdsa_384_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256"),            setup_ecdsa_384,  teardown) \
+    f(client, ecdsa_384_ecdh_sha2_nistp384,           kexcmd("ecdh-sha2-nistp384"),            setup_ecdsa_384,  teardown) \
+    f(client, ecdsa_384_ecdh_sha2_nistp521,           kexcmd("ecdh-sha2-nistp521"),            setup_ecdsa_384,  teardown) \
     f(client, ecdsa_384_diffie_hellman_group14_sha1,  kexcmd("diffie-hellman-group14-sha1"),   setup_ecdsa_384,  teardown) \
     f(client, ecdsa_384_diffie_hellman_group1_sha1,   kexcmd("diffie-hellman-group1-sha1"),    setup_ecdsa_384,  teardown) \
     f(client, ecdsa_521_curve25519_sha256,            kexcmd("curve25519-sha256"),             setup_ecdsa_521,  teardown) \
     f(client, ecdsa_521_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_ecdsa_521,  teardown) \
-    f(client, ecdsa_521_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256 "),           setup_ecdsa_521,  teardown) \
-    f(client, ecdsa_521_ecdh_sha2_nistp384,           kexcmd("ecdh-sha2-nistp384 "),           setup_ecdsa_521,  teardown) \
-    f(client, ecdsa_521_ecdh_sha2_nistp521,           kexcmd("ecdh-sha2-nistp521 "),           setup_ecdsa_521,  teardown) \
+    f(client, ecdsa_521_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256"),            setup_ecdsa_521,  teardown) \
+    f(client, ecdsa_521_ecdh_sha2_nistp384,           kexcmd("ecdh-sha2-nistp384"),            setup_ecdsa_521,  teardown) \
+    f(client, ecdsa_521_ecdh_sha2_nistp521,           kexcmd("ecdh-sha2-nistp521"),            setup_ecdsa_521,  teardown) \
     f(client, ecdsa_521_diffie_hellman_group14_sha1,  kexcmd("diffie-hellman-group14-sha1"),   setup_ecdsa_521,  teardown) \
     f(client, ecdsa_521_diffie_hellman_group1_sha1,   kexcmd("diffie-hellman-group1-sha1"),    setup_ecdsa_521,  teardown)
 #else
@@ -257,22 +257,30 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
     /* Kex algorithms. */ \
     f(client, rsa_curve25519_sha256,                  kexcmd("curve25519-sha256"),             setup_rsa,        teardown) \
     f(client, rsa_curve25519_sha256_libssh_org,       kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_rsa,        teardown) \
-    f(client, rsa_ecdh_sha2_nistp256,                 kexcmd("ecdh-sha2-nistp256 "),           setup_rsa,        teardown) \
+    f(client, rsa_ecdh_sha2_nistp256,                 kexcmd("ecdh-sha2-nistp256"),            setup_rsa,        teardown) \
+    f(client, rsa_ecdh_sha2_nistp384,                 kexcmd("ecdh-sha2-nistp384"),            setup_rsa,        teardown) \
+    f(client, rsa_ecdh_sha2_nistp521,                 kexcmd("ecdh-sha2-nistp521"),            setup_rsa,        teardown) \
     f(client, rsa_diffie_hellman_group14_sha1,        kexcmd("diffie-hellman-group14-sha1"),   setup_rsa,        teardown) \
     f(client, rsa_diffie_hellman_group1_sha1,         kexcmd("diffie-hellman-group1-sha1"),    setup_rsa,        teardown) \
     f(client, ecdsa_256_curve25519_sha256,            kexcmd("curve25519-sha256"),             setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_ecdsa_256,  teardown) \
-    f(client, ecdsa_256_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256 "),           setup_ecdsa_256,  teardown) \
+    f(client, ecdsa_256_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256"),            setup_ecdsa_256,  teardown) \
+    f(client, ecdsa_256_ecdh_sha2_nistp384,           kexcmd("ecdh-sha2-nistp384"),            setup_ecdsa_256,  teardown) \
+    f(client, ecdsa_256_ecdh_sha2_nistp521,           kexcmd("ecdh-sha2-nistp521"),            setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_diffie_hellman_group14_sha1,  kexcmd("diffie-hellman-group14-sha1"),   setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_diffie_hellman_group1_sha1,   kexcmd("diffie-hellman-group1-sha1"),    setup_ecdsa_256,  teardown) \
     f(client, ecdsa_384_curve25519_sha256,            kexcmd("curve25519-sha256"),             setup_ecdsa_384,  teardown) \
     f(client, ecdsa_384_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_ecdsa_384,  teardown) \
-    f(client, ecdsa_384_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256 "),           setup_ecdsa_384,  teardown) \
+    f(client, ecdsa_384_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256"),            setup_ecdsa_384,  teardown) \
+    f(client, ecdsa_384_ecdh_sha2_nistp384,           kexcmd("ecdh-sha2-nistp384"),            setup_ecdsa_384,  teardown) \
+    f(client, ecdsa_384_ecdh_sha2_nistp521,           kexcmd("ecdh-sha2-nistp521"),            setup_ecdsa_384,  teardown) \
     f(client, ecdsa_384_diffie_hellman_group14_sha1,  kexcmd("diffie-hellman-group14-sha1"),   setup_ecdsa_384,  teardown) \
     f(client, ecdsa_384_diffie_hellman_group1_sha1,   kexcmd("diffie-hellman-group1-sha1"),    setup_ecdsa_384,  teardown) \
     f(client, ecdsa_521_curve25519_sha256,            kexcmd("curve25519-sha256"),             setup_ecdsa_521,  teardown) \
     f(client, ecdsa_521_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_ecdsa_521,  teardown) \
-    f(client, ecdsa_521_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256 "),           setup_ecdsa_521,  teardown) \
+    f(client, ecdsa_521_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256"),            setup_ecdsa_521,  teardown) \
+    f(client, ecdsa_521_ecdh_sha2_nistp384,           kexcmd("ecdh-sha2-nistp384"),            setup_ecdsa_521,  teardown) \
+    f(client, ecdsa_521_ecdh_sha2_nistp521,           kexcmd("ecdh-sha2-nistp521"),            setup_ecdsa_521,  teardown) \
     f(client, ecdsa_521_diffie_hellman_group14_sha1,  kexcmd("diffie-hellman-group14-sha1"),   setup_ecdsa_521,  teardown) \
     f(client, ecdsa_521_diffie_hellman_group1_sha1,   kexcmd("diffie-hellman-group1-sha1"),    setup_ecdsa_521,  teardown)
 #endif
-- 
2.17.1


From 07f6fdd2e37894185f65c1d67115623245b6aa0d Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 3 Jul 2018 15:57:15 +0200
Subject: [PATCH 06/19] pki: Log really the signature algorithm type

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 src/pki.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pki.c b/src/pki.c
index ecb9c381..b8731730 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -1569,7 +1569,7 @@ int ssh_pki_signature_verify_blob(ssh_session session,
 
     SSH_LOG(SSH_LOG_FUNCTIONS,
             "Going to verify a %s type signature",
-            key->type_c);
+            sig->type_c);
 
 
     if (key->type == SSH_KEYTYPE_ECDSA) {
-- 
2.17.1


From 68ec1d89f1e4d2c56f8993e47dce426ecfe1d8ac Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 26 Jun 2018 10:58:03 +0200
Subject: [PATCH 07/19] extension negotiation with SHA2 extension for RSA keys

RFC 8308: The extension negotiation in Secure Shell (SSH) Protocol

 * Implementation of client side message requesting the extension
    negotiation and parsing the server answer.

RFC 8332: Use of RSA Keys with SHA-256 and SHA-512
          in the Secure Shell (SSH) Protocol

 * Implementation of client-side parsing of exntesion message used
   to negotiate this extension and allowing the verification of
   host keys signed with this extension.

This intorudces a new key types, that are internally used only for
signature types.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/libssh.h     |  6 ++-
 include/libssh/packet.h     |  1 +
 include/libssh/session.h    |  8 ++++
 include/libssh/ssh2.h       |  1 +
 src/kex.c                   | 31 +++++++++++--
 src/packet.c                |  5 ++-
 src/packet_cb.c             | 56 +++++++++++++++++++++++
 src/pki.c                   | 88 ++++++++++++++++++++++++++++++++++---
 src/pki_container_openssh.c |  2 +
 src/pki_crypto.c            | 31 ++++++++++++-
 src/pki_gcrypt.c            | 31 ++++++++++++-
 src/pki_mbedcrypto.c        | 27 +++++++++++-
 tests/torture_key.c         |  2 +
 13 files changed, 271 insertions(+), 18 deletions(-)

diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index e78b8a27..00432028 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -268,7 +268,10 @@ enum ssh_keytypes_e{
   SSH_KEYTYPE_ECDSA,
   SSH_KEYTYPE_ED25519,
   SSH_KEYTYPE_DSS_CERT01,
-  SSH_KEYTYPE_RSA_CERT01
+  SSH_KEYTYPE_RSA_CERT01,
+  /* signature types, but should be usable also as key types */
+  SSH_KEYTYPE_RSA_SHA256,
+  SSH_KEYTYPE_RSA_SHA512
 };
 
 enum ssh_keycmp_e {
@@ -603,6 +606,7 @@ LIBSSH_API ssh_key ssh_key_new(void);
 LIBSSH_API void ssh_key_free (ssh_key key);
 LIBSSH_API enum ssh_keytypes_e ssh_key_type(const ssh_key key);
 LIBSSH_API const char *ssh_key_type_to_char(enum ssh_keytypes_e type);
+LIBSSH_API const char *ssh_key_alg_to_char(enum ssh_keytypes_e type);
 LIBSSH_API enum ssh_keytypes_e ssh_key_type_from_name(const char *name);
 LIBSSH_API int ssh_key_is_public(const ssh_key k);
 LIBSSH_API int ssh_key_is_private(const ssh_key k);
diff --git a/include/libssh/packet.h b/include/libssh/packet.h
index 1a9283d8..a3bcb9a8 100644
--- a/include/libssh/packet.h
+++ b/include/libssh/packet.h
@@ -51,6 +51,7 @@ SSH_PACKET_CALLBACK(ssh_packet_ignore_callback);
 SSH_PACKET_CALLBACK(ssh_packet_dh_reply);
 SSH_PACKET_CALLBACK(ssh_packet_newkeys);
 SSH_PACKET_CALLBACK(ssh_packet_service_accept);
+SSH_PACKET_CALLBACK(ssh_packet_ext_info);
 
 #ifdef WITH_SERVER
 SSH_PACKET_CALLBACK(ssh_packet_kexdh_init);
diff --git a/include/libssh/session.h b/include/libssh/session.h
index 5421056f..a01afba0 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -86,6 +86,11 @@ enum ssh_pending_call_e {
 #define SSH_OPT_FLAG_KBDINT_AUTH 0x4
 #define SSH_OPT_FLAG_GSSAPI_AUTH 0x8
 
+/* extensions flags */
+/* server-sig-algs extension */
+#define SSH_EXT_SIG_RSA_SHA256  0x01
+#define SSH_EXT_SIG_RSA_SHA512  0x02
+
 /* members that are common to ssh_session and ssh_bind */
 struct ssh_common_struct {
     struct error_struct error;
@@ -114,6 +119,9 @@ struct ssh_session_struct {
     /* session flags (SSH_SESSION_FLAG_*) */
     int flags;
 
+    /* Extensions negotiated using RFC 8308 */
+    int extensions;
+
     ssh_string banner; /* that's the issue banner from
                        the server */
     char *discon_msg; /* disconnect message from
diff --git a/include/libssh/ssh2.h b/include/libssh/ssh2.h
index 8b39b9a6..35214330 100644
--- a/include/libssh/ssh2.h
+++ b/include/libssh/ssh2.h
@@ -7,6 +7,7 @@
 #define SSH2_MSG_DEBUG	4
 #define SSH2_MSG_SERVICE_REQUEST	5
 #define SSH2_MSG_SERVICE_ACCEPT 6
+#define SSH2_MSG_EXT_INFO 7
 
 #define SSH2_MSG_KEXINIT	 20
 #define SSH2_MSG_NEWKEYS 21
diff --git a/src/kex.c b/src/kex.c
index 6ac59ec8..889eb2bf 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -86,12 +86,12 @@
 
 #ifdef HAVE_ECDH
 #define ECDH "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,"
-#define HOSTKEYS "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss"
+#define HOSTKEYS "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss"
 #else
 #ifdef HAVE_DSA
-#define HOSTKEYS "ssh-ed25519,ssh-rsa,ssh-dss"
+#define HOSTKEYS "ssh-ed25519,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss"
 #else
-#define HOSTKEYS "ssh-ed25519,ssh-rsa"
+#define HOSTKEYS "ssh-ed25519,ssh-rsa,rsa-sha2-512,rsa-sha2-256"
 #endif
 #define ECDH ""
 #endif
@@ -101,6 +101,9 @@
 #define KEY_EXCHANGE CURVE25519 ECDH "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
 #define KEX_METHODS_SIZE 10
 
+/* RFC 8308 */
+#define KEX_EXTENSION_CLIENT "ext-info-c"
+
 /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */
 static const char *default_methods[] = {
   KEY_EXCHANGE,
@@ -645,8 +648,9 @@ static char *ssh_client_select_hostkeys(ssh_session session)
 int ssh_set_client_kex(ssh_session session){
     struct ssh_kex_struct *client= &session->next_crypto->client_kex;
     const char *wanted;
+    char *kex;
     int ok;
-    int i;
+    int i, kex_len;
 
     ok = ssh_get_random(client->cookie, 16, 0);
     if (!ok) {
@@ -673,6 +677,17 @@ int ssh_set_client_kex(ssh_session session){
         }
     }
 
+    /* Here we append  ext-info-c  to the list of kex algorithms */
+    kex = client->methods[SSH_KEX];
+    kex_len = strlen(kex) + strlen(KEX_EXTENSION_CLIENT) + 2; /* comma, NULL */
+    kex = realloc(kex, kex_len);
+    if (kex == NULL) {
+        ssh_set_error_oom(session);
+        return SSH_ERROR;
+    }
+    strcat(kex, ","KEX_EXTENSION_CLIENT);
+    client->methods[SSH_KEX] = kex;
+
     return SSH_OK;
 }
 
@@ -682,8 +697,16 @@ int ssh_set_client_kex(ssh_session session){
 int ssh_kex_select_methods (ssh_session session){
     struct ssh_kex_struct *server = &session->next_crypto->server_kex;
     struct ssh_kex_struct *client = &session->next_crypto->client_kex;
+    char *ext_start = NULL;
     int i;
 
+    /* Here we should drop the  ext-info-c  from the list so we avoid matching.
+     * it. We added it to the end, so we can just truncate the string here */
+    ext_start = strstr(client->methods[SSH_KEX], ","KEX_EXTENSION_CLIENT);
+    if (ext_start != NULL) {
+        ext_start = '\0';
+    }
+
     for (i = 0; i < KEX_METHODS_SIZE; i++) {
         session->next_crypto->kex_methods[i]=ssh_find_matching(server->methods[i],client->methods[i]);
         if(session->next_crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S){
diff --git a/src/packet.c b/src/packet.c
index 16f96149..5b9252f5 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -59,8 +59,9 @@ static ssh_packet_callback default_packet_handlers[]= {
   NULL,
 #endif
   ssh_packet_service_accept,               // SSH2_MSG_SERVICE_ACCEPT             6
-  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-  NULL, NULL, NULL,	NULL, NULL, NULL,      //                                     7-19
+  ssh_packet_ext_info,                     // SSH2_MSG_EXT_INFO                   7
+  NULL, NULL, NULL, NULL, NULL, NULL,
+  NULL, NULL, NULL, NULL, NULL, NULL,      //                                     8-19
   ssh_packet_kexinit,                      // SSH2_MSG_KEXINIT	                  20
   ssh_packet_newkeys,                      // SSH2_MSG_NEWKEYS                    21
   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
diff --git a/src/packet_cb.c b/src/packet_cb.c
index 2c8d9935..21151c5f 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -270,3 +270,59 @@ SSH_PACKET_CALLBACK(ssh_packet_service_accept){
 
 	return SSH_PACKET_USED;
 }
+
+/**
+ * @internal
+ * @brief handles a SSH2_MSG_EXT_INFO packet defined in RFC 8308
+ *
+ */
+SSH_PACKET_CALLBACK(ssh_packet_ext_info){
+    int rc;
+    uint32_t i, nr_extensions = 0;
+    (void)session;
+    (void)type;
+    (void)user;
+
+    SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_EXT_INFO");
+
+    rc = ssh_buffer_get_u32(packet, &nr_extensions);
+    if (rc == 0) {
+        SSH_LOG(SSH_LOG_PACKET, "Failed to read number of extensions");
+        return SSH_PACKET_USED;
+    }
+    nr_extensions = ntohl(nr_extensions);
+    SSH_LOG(SSH_LOG_PACKET, "Follows %u extensions", nr_extensions);
+
+    for (i = 0; i < nr_extensions; i++) {
+        ssh_string ext_name, ext_value;
+        const char *name, *value;
+        ext_name = ssh_buffer_get_ssh_string(packet);
+        if (ext_name == NULL) {
+            SSH_LOG(SSH_LOG_PACKET, "Error reading extension name");
+            return SSH_PACKET_USED;
+        }
+        ext_value = ssh_buffer_get_ssh_string(packet);
+        if (ext_value == NULL) {
+            SSH_LOG(SSH_LOG_PACKET, "Error reading extension value");
+            ssh_string_free(ext_name);
+            return SSH_PACKET_USED;
+        }
+
+        name = ssh_string_get_char(ext_name);
+        value = ssh_string_get_char(ext_value);
+        if (strcmp(name, "server-sig-algs") == 0) {
+            /* TODO check for NULL bytes */
+            SSH_LOG(SSH_LOG_PACKET, "Extension: %s=<%s>", name, value);
+            if (ssh_match_group(value, "rsa-sha2-512")) {
+                session->extensions |= SSH_EXT_SIG_RSA_SHA512;
+            }
+            if (ssh_match_group(value, "rsa-sha2-256")) {
+                session->extensions |= SSH_EXT_SIG_RSA_SHA256;
+            }
+        }
+        ssh_string_free(ext_name);
+        ssh_string_free(ext_value);
+    }
+
+    return SSH_PACKET_USED;
+}
diff --git a/src/pki.c b/src/pki.c
index b8731730..c68f23dd 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -205,6 +205,8 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
     case SSH_KEYTYPE_DSS:
       return "ssh-dss";
     case SSH_KEYTYPE_RSA:
+    case SSH_KEYTYPE_RSA_SHA256:
+    case SSH_KEYTYPE_RSA_SHA512:
       return "ssh-rsa";
     case SSH_KEYTYPE_ECDSA:
       return "ssh-ecdsa";
@@ -223,6 +225,58 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
   return NULL;
 }
 
+/**
+ * @brief Convert a key type to a pubkey algorithm type. This is usually
+ * the same as public key type, unless the SHA2 extension (RFC 8332) is
+ * negotiated during key exchange
+ *
+ * @param[in]  session  SSH Session.
+ * @param[in]  type     The type to convert.
+ *
+ * @return              A public key algorithm to be used.
+ */
+enum ssh_keytypes_e ssh_key_type_to_alg(ssh_session session,
+                                        enum ssh_keytypes_e type)
+{
+  /* TODO this should also reflect supported key types specified in
+   * configuration (ssh_config PubkeyAcceptedKeyTypes) */
+  switch (type) {
+    case SSH_KEYTYPE_RSA:
+      if (session->extensions & SSH_EXT_SIG_RSA_SHA512)
+        return SSH_KEYTYPE_RSA_SHA512;
+      if (session->extensions & SSH_EXT_SIG_RSA_SHA256)
+        return SSH_KEYTYPE_RSA_SHA256;
+      FALL_THROUGH;
+    default:
+      return type;
+  }
+
+  /* We should never reach this */
+  return SSH_KEYTYPE_UNKNOWN;
+}
+
+/**
+ * @brief Convert a ssh key algorithm name to a ssh key algorithm type.
+ *
+ * @param[in] name      The name to convert.
+ *
+ * @return              The enum ssh key algorithm type.
+ */
+static enum ssh_keytypes_e ssh_key_algorithm_type_from_name(const char *name) {
+    if (name == NULL) {
+        return SSH_KEYTYPE_UNKNOWN;
+    }
+
+    if (strcmp(name, "rsa-sha2-256") == 0) {
+        return SSH_KEYTYPE_RSA_SHA256;
+    } else if (strcmp(name, "rsa-sha2-512") == 0) {
+        return SSH_KEYTYPE_RSA_SHA512;
+    }
+
+    /* Otherwise the sig algorithem matches the key type */
+    return ssh_key_type_from_name(name);
+}
+
 /**
  * @brief Convert a ssh key name to a ssh key type.
  *
@@ -239,7 +293,9 @@ enum ssh_keytypes_e ssh_key_type_from_name(const char *name) {
         return SSH_KEYTYPE_RSA;
     } else if (strcmp(name, "dsa") == 0) {
         return SSH_KEYTYPE_DSS;
-    } else if (strcmp(name, "ssh-rsa") == 0) {
+    } else if (strcmp(name, "ssh-rsa") == 0
+            || strcmp(name, "rsa-sha2-256") == 0
+            || strcmp(name, "rsa-sha2-512") == 0) {
         return SSH_KEYTYPE_RSA;
     } else if (strcmp(name, "ssh-dss") == 0) {
         return SSH_KEYTYPE_DSS;
@@ -356,6 +412,8 @@ void ssh_signature_free(ssh_signature sig)
 #endif
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
 #ifdef HAVE_LIBGCRYPT
             gcry_sexp_release(sig->rsa_sig);
 #elif defined HAVE_LIBCRYPTO
@@ -1534,7 +1592,7 @@ int ssh_pki_import_signature_blob(const ssh_string sig_blob,
         return SSH_ERROR;
     }
 
-    type = ssh_key_type_from_name(ssh_string_get_char(str));
+    type = ssh_key_algorithm_type_from_name(ssh_string_get_char(str));
     ssh_string_free(str);
 
     str = ssh_buffer_get_ssh_string(buf);
@@ -1593,22 +1651,40 @@ int ssh_pki_signature_verify_blob(ssh_session session,
     } else if (key->type == SSH_KEYTYPE_ED25519) {
         rc = pki_signature_verify(session, sig, key, digest, dlen);
     } else {
-        unsigned char hash[SHA_DIGEST_LEN] = {0};
+        unsigned char hash[SHA512_DIGEST_LEN] = {0};
+        uint32_t hlen = 0;
 
-        sha1(digest, dlen, hash);
+        switch (sig->type) {
+        case SSH_KEYTYPE_RSA_SHA256:
+            sha256(digest, dlen, hash);
+            hlen = SHA256_DIGEST_LEN;
+            break;
+        case SSH_KEYTYPE_RSA_SHA512:
+            sha512(digest, dlen, hash);
+            hlen = SHA512_DIGEST_LEN;
+            break;
+        case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_DSS:
+            sha1(digest, dlen, hash);
+            hlen = SHA_DIGEST_LEN;
+            break;
+        default:
+            SSH_LOG(SSH_LOG_TRACE, "Unknown sig->type: %d", sig->type);
+            return SSH_ERROR;
+        }
 #ifdef DEBUG_CRYPTO
         ssh_print_hexa(key->type == SSH_KEYTYPE_DSS
                        ? "Hash to be verified with DSA"
                        : "Hash to be verified with RSA",
                        hash,
-                       SHA_DIGEST_LEN);
+                       hlen);
 #endif
 
         rc = pki_signature_verify(session,
                                   sig,
                                   key,
                                   hash,
-                                  SHA_DIGEST_LEN);
+                                  hlen);
     }
 
     ssh_signature_free(sig);
diff --git a/src/pki_container_openssh.c b/src/pki_container_openssh.c
index 53e1e7fe..c9aed897 100644
--- a/src/pki_container_openssh.c
+++ b/src/pki_container_openssh.c
@@ -118,6 +118,8 @@ static int pki_openssh_import_privkey_blob(ssh_buffer key_blob_buffer,
         /* p,q,g,pub_key,priv_key */
     case SSH_KEYTYPE_RSA_CERT01:
     case SSH_KEYTYPE_RSA:
+    case SSH_KEYTYPE_RSA_SHA256:
+    case SSH_KEYTYPE_RSA_SHA512:
         /* n,e,d,iqmp,p,q */
     case SSH_KEYTYPE_ECDSA:
         /* curve_name, group, privkey */
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index 4b6251ee..538eeaf8 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -807,6 +807,8 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
 
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         case SSH_KEYTYPE_RSA1:
             if (passphrase == NULL) {
                 if (auth_fn) {
@@ -1287,6 +1289,8 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
             sig_blob = pki_dsa_signature_to_blob(sig);
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         case SSH_KEYTYPE_RSA1:
             sig_blob = ssh_string_copy(sig->rsa_sig);
             break;
@@ -1434,7 +1438,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
     }
 
     sig->type = type;
-    sig->type_c = ssh_key_type_to_char(type);
+    sig->type_c = ssh_key_alg_to_char(type);
 
     len = ssh_string_len(sig_blob);
 
@@ -1496,6 +1500,8 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
 
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         case SSH_KEYTYPE_RSA1:
             sig = pki_signature_from_rsa_blob(pubkey, sig_blob, sig);
             break;
@@ -1608,6 +1614,7 @@ int pki_signature_verify(ssh_session session,
                          size_t hlen)
 {
     int rc;
+    int nid;
 
     switch(key->type) {
         case SSH_KEYTYPE_DSS:
@@ -1625,13 +1632,32 @@ int pki_signature_verify(ssh_session session,
             break;
         case SSH_KEYTYPE_RSA:
         case SSH_KEYTYPE_RSA1:
-            rc = RSA_verify(NID_sha1,
+            switch (sig->type) {
+            case SSH_KEYTYPE_RSA:
+                nid = NID_sha1;
+                break;
+            case SSH_KEYTYPE_RSA_SHA256:
+                nid = NID_sha256;
+                break;
+            case SSH_KEYTYPE_RSA_SHA512:
+                nid = NID_sha512;
+                break;
+            default:
+                SSH_LOG(SSH_LOG_TRACE, "Unknown sig type %d", sig->type);
+                ssh_set_error(session,
+                              SSH_FATAL,
+                              "Unexpected signature type %s during RSA verify",
+                              sig->type_c);
+                return SSH_ERROR;
+            }
+            rc = RSA_verify(nid,
                             hash,
                             hlen,
                             ssh_string_data(sig->rsa_sig),
                             ssh_string_len(sig->rsa_sig),
                             key->rsa);
             if (rc <= 0) {
+                SSH_LOG(SSH_LOG_TRACE, "RSA verify failed");
                 ssh_set_error(session,
                               SSH_FATAL,
                               "RSA error: %s",
@@ -1665,6 +1691,7 @@ int pki_signature_verify(ssh_session session,
 #endif
         case SSH_KEYTYPE_UNKNOWN:
         default:
+            SSH_LOG(SSH_LOG_TRACE, "Unknown key type");
             ssh_set_error(session, SSH_FATAL, "Unknown public key type");
             return SSH_ERROR;
     }
diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c
index 4d6c2586..ccad326e 100644
--- a/src/pki_gcrypt.c
+++ b/src/pki_gcrypt.c
@@ -1366,6 +1366,8 @@ int pki_key_compare(const ssh_key k1,
             }
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
             if (_bignum_cmp(k1->rsa, k2->rsa, "e") != 0) {
                 return 1;
             }
@@ -1691,6 +1693,8 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
             ssh_string_fill(sig_blob, buffer, 40);
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
             sexp = gcry_sexp_find_token(sig->rsa_sig, "s", 0);
             if (sexp == NULL) {
                 return NULL;
@@ -1826,6 +1830,8 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
             }
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
             rsalen = (gcry_pk_get_nbits(pubkey->rsa) + 7) / 8;
 
             if (len > rsalen) {
@@ -1957,6 +1963,7 @@ int pki_signature_verify(ssh_session session,
                          size_t hlen)
 {
     unsigned char ghash[hlen + 1];
+    const char *hash_type = NULL;
     gcry_sexp_t sexp;
     gcry_error_t err;
 
@@ -1991,10 +1998,30 @@ int pki_signature_verify(ssh_session session,
             }
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
+            switch (sig->type) {
+            case SSH_KEYTYPE_RSA_SHA256:
+                hash_type = "sha256";
+                break;
+            case SSH_KEYTYPE_RSA_SHA512:
+                hash_type = "sha512";
+                break;
+            case SSH_KEYTYPE_RSA:
+                hash_type = "sha1";
+                break;
+            default:
+                SSH_LOG(SSH_LOG_TRACE, "Unknown sig type %d", sig->type);
+                ssh_set_error(session,
+                              SSH_FATAL,
+                              "Unexpected signature type %s during RSA verify",
+                              sig->type_c);
+                return SSH_ERROR;
+            }
             err = gcry_sexp_build(&sexp,
                                   NULL,
-                                  "(data(flags pkcs1)(hash sha1 %b))",
-                                  hlen, hash);
+                                  "(data(flags pkcs1)(hash %s %b))",
+                                  hash_type, hlen, hash);
             if (err) {
                 ssh_set_error(session,
                               SSH_FATAL,
diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c
index acef7ee1..e4ba5de2 100644
--- a/src/pki_mbedcrypto.c
+++ b/src/pki_mbedcrypto.c
@@ -708,6 +708,8 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
 
     switch(sig->type) {
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
             sig_blob = ssh_string_copy(sig->rsa_sig);
             break;
         case SSH_KEYTYPE_ECDSA: {
@@ -842,6 +844,8 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey, const ssh_string
 
     switch(type) {
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
             sig = pki_signature_from_rsa_blob(pubkey, sig_blob, sig);
             break;
         case SSH_KEYTYPE_ECDSA: {
@@ -931,10 +935,31 @@ int pki_signature_verify(ssh_session session, const ssh_signature sig, const
         ssh_key key, const unsigned char *hash, size_t hlen)
 {
     int rc;
+    mbedtls_md_type_t md = 0;
 
     switch (key->type) {
         case SSH_KEYTYPE_RSA:
-            rc = mbedtls_pk_verify(key->rsa, MBEDTLS_MD_SHA1, hash, hlen,
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
+            switch (sig->type) {
+            case SSH_KEYTYPE_RSA:
+                md = MBEDTLS_MD_SHA1;
+                break;
+            case SSH_KEYTYPE_RSA_SHA256:
+                md = MBEDTLS_MD_SHA256;
+                break;
+            case SSH_KEYTYPE_RSA_SHA512:
+                md = MBEDTLS_MD_SHA512;
+                break;
+            default:
+                SSH_LOG(SSH_LOG_TRACE, "Unknown sig type %d", sig->type);
+                ssh_set_error(session,
+                              SSH_FATAL,
+                              "Unexpected signature type %s during RSA verify",
+                              sig->type_c);
+                return SSH_ERROR;
+            }
+            rc = mbedtls_pk_verify(key->rsa, md, hash, hlen,
                     ssh_string_data(sig->rsa_sig),
                     ssh_string_len(sig->rsa_sig));
             if (rc != 0) {
diff --git a/tests/torture_key.c b/tests/torture_key.c
index c0ec845a..47384391 100644
--- a/tests/torture_key.c
+++ b/tests/torture_key.c
@@ -318,6 +318,8 @@ static const char *torture_get_testkey_internal(enum ssh_keytypes_e type,
                 return torture_dsa_private_testkey_passphrase;
             }
             return torture_dsa_private_testkey;
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         case SSH_KEYTYPE_RSA:
             if (pubkey) {
                 return torture_rsa_public_testkey;
-- 
2.17.1


From f97ea1a19ab2e0a859295dc49f9f115fc7517cf0 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 26 Jun 2018 11:17:10 +0200
Subject: [PATCH 08/19] Allow authentication using SHA2 extension to RSA keys

RFC 8332:  Use of RSA Keys with SHA-256 and SHA-512
           in the Secure Shell (SSH) Protocol

 * This change intropduces new API to request signature using a key
   and a different pki mechanism. This is used only for RSA keys,
   that used to have SHA1 hardcoded, but the new algorithsms allow
   to use the SHA2 hashes, if the extension is negotiated.

 * We can not use the ssh_key_alg_to_char() for other keys (ECDSA),
   because they have different  type_c  names while having common
   type id.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/libssh.h   |  2 ++
 include/libssh/pki_priv.h |  4 +++
 src/auth.c                | 17 +++++++---
 src/pki.c                 | 69 ++++++++++++++++++++++++++++++++-----
 src/pki_crypto.c          | 71 +++++++++++++++++++++++++++++++++++----
 src/pki_gcrypt.c          | 41 ++++++++++++++++++++--
 src/pki_mbedcrypto.c      | 45 +++++++++++++++++++++----
 7 files changed, 220 insertions(+), 29 deletions(-)

diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 00432028..35059e02 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -607,6 +607,8 @@ LIBSSH_API void ssh_key_free (ssh_key key);
 LIBSSH_API enum ssh_keytypes_e ssh_key_type(const ssh_key key);
 LIBSSH_API const char *ssh_key_type_to_char(enum ssh_keytypes_e type);
 LIBSSH_API const char *ssh_key_alg_to_char(enum ssh_keytypes_e type);
+LIBSSH_API enum ssh_keytypes_e ssh_key_type_to_alg(ssh_session session,
+                                                   enum ssh_keytypes_e type);
 LIBSSH_API enum ssh_keytypes_e ssh_key_type_from_name(const char *name);
 LIBSSH_API int ssh_key_is_public(const ssh_key k);
 LIBSSH_API int ssh_key_is_private(const ssh_key k);
diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h
index af041504..868794f6 100644
--- a/include/libssh/pki_priv.h
+++ b/include/libssh/pki_priv.h
@@ -96,6 +96,10 @@ int pki_signature_verify(ssh_session session,
 ssh_signature pki_do_sign(const ssh_key privkey,
                           const unsigned char *hash,
                           size_t hlen);
+ssh_signature pki_do_sign_alg(const ssh_key privkey,
+                              const unsigned char *hash,
+                              size_t hlen,
+                              enum ssh_keytypes_e algorithm);
 ssh_signature pki_do_sign_sessionid(const ssh_key key,
                                     const unsigned char *hash,
                                     size_t hlen);
diff --git a/src/auth.c b/src/auth.c
index da746822..534e9185 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -431,6 +431,8 @@ int ssh_userauth_try_publickey(ssh_session session,
                                const ssh_key pubkey)
 {
     ssh_string pubkey_s = NULL;
+    const char *sig_type_c = NULL;
+    enum ssh_keytypes_e sig_type;
     int rc;
 
     if (session == NULL) {
@@ -466,6 +468,8 @@ int ssh_userauth_try_publickey(ssh_session session,
     if (rc < 0) {
         goto fail;
     }
+    sig_type = ssh_key_type_to_alg(session, pubkey->type);
+    sig_type_c = ssh_key_alg_to_char(sig_type);
 
     /* request */
     rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
@@ -474,7 +478,7 @@ int ssh_userauth_try_publickey(ssh_session session,
             "ssh-connection",
             "publickey",
             0, /* private key ? */
-            pubkey->type_c, /* algo */
+            sig_type_c, /* algo */
             pubkey_s /* public key */
             );
     if (rc < 0) {
@@ -535,7 +539,7 @@ int ssh_userauth_publickey(ssh_session session,
     ssh_string str = NULL;
     int rc;
     const char *type_c;
-    enum ssh_keytypes_e key_type;
+    enum ssh_keytypes_e key_type, sig_type;
 
     if (session == NULL) {
         return SSH_AUTH_ERROR;
@@ -567,7 +571,8 @@ int ssh_userauth_publickey(ssh_session session,
 
     /* Cert auth requires presenting the cert type name (*-cert@xxxxxxxxxxx) */
     key_type = privkey->cert != NULL ? privkey->cert_type : privkey->type;
-    type_c = ssh_key_type_to_char(key_type);
+    sig_type = ssh_key_type_to_alg(session, key_type);
+    type_c = ssh_key_alg_to_char(sig_type);
 
     /* get public key or cert */
     rc = ssh_pki_export_pubkey_blob(privkey, &str);
@@ -631,6 +636,8 @@ static int ssh_userauth_agent_publickey(ssh_session session,
                                         ssh_key pubkey)
 {
     ssh_string str = NULL;
+    const char *sig_type_c = NULL;
+    enum ssh_keytypes_e sig_type;
     int rc;
 
     switch(session->pending_call_state) {
@@ -658,6 +665,8 @@ static int ssh_userauth_agent_publickey(ssh_session session,
     if (rc < 0) {
         goto fail;
     }
+    sig_type = ssh_key_type_to_alg(session, pubkey->type);
+    sig_type_c = ssh_key_alg_to_char(sig_type);
 
     /* request */
     rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
@@ -666,7 +675,7 @@ static int ssh_userauth_agent_publickey(ssh_session session,
             "ssh-connection",
             "publickey",
             1, /* private key */
-            pubkey->type_c, /* algo */
+            sig_type_c, /* algo */
             str /* public key */
             );
     if (rc < 0) {
diff --git a/src/pki.c b/src/pki.c
index c68f23dd..eef081a4 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -193,6 +193,27 @@ enum ssh_keytypes_e ssh_key_type(const ssh_key key){
     return key->type;
 }
 
+/**
+ * @brief Convert a key algorithm type to a string.
+ *
+ * @param[in]  type     The algorithm type to convert.
+ *
+ * @return              A string for the keytype or NULL if unknown.
+ */
+const char *ssh_key_alg_to_char(enum ssh_keytypes_e type) {
+  switch (type) {
+    case SSH_KEYTYPE_RSA_SHA256:
+      return "rsa-sha2-256";
+    case SSH_KEYTYPE_RSA_SHA512:
+      return "rsa-sha2-512";
+    default:
+      return ssh_key_type_to_char(type);
+  }
+
+  /* We should never reach this */
+  return NULL;
+}
+
 /**
  * @brief Convert a key type to a string.
  *
@@ -1763,24 +1784,54 @@ ssh_string ssh_pki_do_sign(ssh_session session,
                           ssh_buffer_get_len(buf));
         ssh_buffer_free(buf);
     } else {
-        unsigned char hash[SHA_DIGEST_LEN] = {0};
-        SHACTX ctx;
+        unsigned char hash[SHA512_DIGEST_LEN] = {0};
+        uint32_t hlen = 0;
+        enum ssh_keytypes_e sig_type;
+        ssh_buffer buf;
 
-        ctx = sha1_init();
-        if (ctx == NULL) {
+        buf = ssh_buffer_new();
+        if (buf == NULL) {
             ssh_string_free(session_id);
             return NULL;
         }
 
-        sha1_update(ctx, session_id, ssh_string_len(session_id) + 4);
-        sha1_update(ctx, ssh_buffer_get(sigbuf), ssh_buffer_get_len(sigbuf));
-        sha1_final(hash, ctx);
+        ssh_buffer_set_secure(buf);
+        rc = ssh_buffer_pack(buf,
+                             "SP",
+                             session_id,
+                             ssh_buffer_get_len(sigbuf), ssh_buffer_get(sigbuf));
+        if (rc != SSH_OK) {
+            ssh_string_free(session_id);
+            ssh_buffer_free(buf);
+            return NULL;
+        }
+
+        sig_type = ssh_key_type_to_alg(session, privkey->type);
+        switch (sig_type) {
+        case SSH_KEYTYPE_RSA_SHA256:
+            sha256(ssh_buffer_get(buf), ssh_buffer_get_len(buf), hash);
+            hlen = SHA256_DIGEST_LEN;
+            break;
+        case SSH_KEYTYPE_RSA_SHA512:
+            sha512(ssh_buffer_get(buf), ssh_buffer_get_len(buf), hash);
+            hlen = SHA512_DIGEST_LEN;
+            break;
+        case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_DSS:
+            sha1(ssh_buffer_get(buf), ssh_buffer_get_len(buf), hash);
+            hlen = SHA_DIGEST_LEN;
+            break;
+        default:
+            SSH_LOG(SSH_LOG_TRACE, "Unknown sig->type: %d", sig->type);
+            ssh_string_free(session_id);
+            return NULL;
+        }
 
 #ifdef DEBUG_CRYPTO
-        ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN);
+        ssh_print_hexa("Hash being signed", hash, hlen);
 #endif
 
-        sig = pki_do_sign(privkey, hash, SHA_DIGEST_LEN);
+        sig = pki_do_sign_alg(privkey, hash, hlen, sig_type);
     }
     ssh_string_free(session_id);
     if (sig == NULL) {
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index 538eeaf8..46f790ac 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -560,6 +560,8 @@ int pki_key_compare(const ssh_key k1,
             break;
         }
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         case SSH_KEYTYPE_RSA1: {
             const BIGNUM *e1, *e2, *n1, *n2, *p1, *p2, *q1, *q2;
             if (RSA_size(k1->rsa) != RSA_size(k2->rsa)) {
@@ -1193,23 +1195,42 @@ fail:
  *
  * @param[in]  privkey   The private rsa key to use for signing.
  *
+ * @param[in]  algorithm The public key algorithm to use.
+ *
  * @return               A newly allocated rsa sig blob or NULL on error.
  */
-static ssh_string _RSA_do_sign(const unsigned char *digest,
-                               int dlen,
-                               RSA *privkey)
+static ssh_string _RSA_do_sign_alg(const unsigned char *digest,
+                                   int dlen,
+                                   RSA *privkey,
+                                   enum ssh_keytypes_e algorithm)
 {
     ssh_string sig_blob;
     unsigned char *sig;
     unsigned int slen;
     int ok;
+    int nid = 0;
+
+    switch (algorithm) {
+    case SSH_KEYTYPE_RSA:
+        nid = NID_sha1;
+        break;
+    case SSH_KEYTYPE_RSA_SHA256:
+        nid = NID_sha256;
+        break;
+    case SSH_KEYTYPE_RSA_SHA512:
+        nid = NID_sha512;
+        break;
+    default:
+        SSH_LOG(SSH_LOG_WARN, "Incomplatible key algorithm");
+        return NULL;
+    }
 
     sig = malloc(RSA_size(privkey));
     if (sig == NULL) {
         return NULL;
     }
 
-    ok = RSA_sign(NID_sha1, digest, dlen, sig, &slen, privkey);
+    ok = RSA_sign(nid, digest, dlen, sig, &slen, privkey);
     if (!ok) {
         SAFE_FREE(sig);
         return NULL;
@@ -1228,6 +1249,26 @@ static ssh_string _RSA_do_sign(const unsigned char *digest,
     return sig_blob;
 }
 
+/**
+ * @internal
+ *
+ * @brief Compute a digital signature.
+ *
+ * @param[in]  digest    The message digest.
+ *
+ * @param[in]  dlen      The length of the digest.
+ *
+ * @param[in]  privkey   The private rsa key to use for signing.
+ *
+ * @return               A newly allocated rsa sig blob or NULL on error.
+ */
+static ssh_string _RSA_do_sign(const unsigned char *digest,
+                                    int dlen,
+                                    RSA *privkey)
+{
+    return _RSA_do_sign_alg(digest, dlen, privkey, SSH_KEYTYPE_RSA);
+}
+
 static ssh_string pki_dsa_signature_to_blob(const ssh_signature sig)
 {
     char buffer[40] = { 0 };
@@ -1701,16 +1742,31 @@ int pki_signature_verify(ssh_session session,
 
 ssh_signature pki_do_sign(const ssh_key privkey,
                           const unsigned char *hash,
-                          size_t hlen) {
+                          size_t hlen)
+{
+    return pki_do_sign_alg(privkey, hash, hlen, privkey->type);
+}
+
+ssh_signature pki_do_sign_alg(const ssh_key privkey,
+                              const unsigned char *hash,
+                              size_t hlen,
+                              enum ssh_keytypes_e algorithm)
+{
     ssh_signature sig;
     int rc;
 
+    /* Only RSA supports different signature algorithm types now */
+    if (privkey->type != algorithm && privkey->type != SSH_KEYTYPE_RSA) {
+        SSH_LOG(SSH_LOG_WARN, "Incompatible signature algorithm passed");
+        return NULL;
+    }
+
     sig = ssh_signature_new();
     if (sig == NULL) {
         return NULL;
     }
 
-    sig->type = privkey->type;
+    sig->type = algorithm;
     sig->type_c = privkey->type_c;
 
     switch(privkey->type) {
@@ -1733,7 +1789,8 @@ ssh_signature pki_do_sign(const ssh_key privkey,
             break;
         case SSH_KEYTYPE_RSA:
         case SSH_KEYTYPE_RSA1:
-            sig->rsa_sig = _RSA_do_sign(hash, hlen, privkey->rsa);
+            sig->type_c = ssh_key_alg_to_char(algorithm);
+            sig->rsa_sig = _RSA_do_sign_alg(hash, hlen, privkey->rsa, algorithm);
             if (sig->rsa_sig == NULL) {
                 ssh_signature_free(sig);
                 return NULL;
diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c
index ccad326e..7678e5a4 100644
--- a/src/pki_gcrypt.c
+++ b/src/pki_gcrypt.c
@@ -2090,17 +2090,32 @@ int pki_signature_verify(ssh_session session,
 
 ssh_signature pki_do_sign(const ssh_key privkey,
                           const unsigned char *hash,
-                          size_t hlen) {
+                          size_t hlen)
+{
+    return pki_do_sign_alg(privkey, hash, hlen, privkey->type);
+}
+
+ssh_signature pki_do_sign_alg(const ssh_key privkey,
+                              const unsigned char *hash,
+                              size_t hlen,
+                              enum ssh_keytypes_e algorithm)
+{
     unsigned char ghash[hlen + 1];
+    const char *hash_type = NULL;
     ssh_signature sig;
     gcry_sexp_t sexp;
     gcry_error_t err;
 
+    /* Only RSA supports different signature algorithm types now */
+    if (privkey->type != algorithm && privkey->type != SSH_KEYTYPE_RSA) {
+        return NULL;
+    }
+
     sig = ssh_signature_new();
     if (sig == NULL) {
         return NULL;
     }
-    sig->type = privkey->type;
+    sig->type = algorithm;
     sig->type_c = privkey->type_c;
     switch (privkey->type) {
         case SSH_KEYTYPE_DSS:
@@ -2126,9 +2141,27 @@ ssh_signature pki_do_sign(const ssh_key privkey,
             }
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
+            sig->type_c = ssh_key_alg_to_char(algorithm);
+            switch (algorithm) {
+            case SSH_KEYTYPE_RSA:
+                hash_type = "sha1";
+                break;
+            case SSH_KEYTYPE_RSA_SHA256:
+                hash_type = "sha256";
+                break;
+            case SSH_KEYTYPE_RSA_SHA512:
+                hash_type = "sha512";
+                break;
+            default:
+                SSH_LOG(SSH_LOG_WARN, "Incomplatible key algorithm");
+                return NULL;
+            }
             err = gcry_sexp_build(&sexp,
                                   NULL,
-                                  "(data(flags pkcs1)(hash sha1 %b))",
+                                  "(data(flags pkcs1)(hash %s %b))",
+                                  hash_type,
                                   hlen,
                                   hash);
             if (err) {
@@ -2220,6 +2253,8 @@ ssh_signature pki_do_sign_sessionid(const ssh_key key,
             }
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
             err = gcry_sexp_build(&sexp,
                                   NULL,
                                   "(data(flags pkcs1)(hash sha1 %b))",
diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c
index e4ba5de2..b73c1d60 100644
--- a/src/pki_mbedcrypto.c
+++ b/src/pki_mbedcrypto.c
@@ -996,20 +996,36 @@ int pki_signature_verify(ssh_session session, const ssh_signature sig, const
     return SSH_OK;
 }
 
-static ssh_string rsa_do_sign(const unsigned char *digest, int dlen,
-        mbedtls_pk_context *privkey)
+static ssh_string rsa_do_sign_alg(const unsigned char *digest, int dlen,
+        mbedtls_pk_context *privkey, enum ssh_keytypes_e algorithm)
 {
     ssh_string sig_blob = NULL;
+    mbedtls_md_type_t md = 0;
     unsigned char *sig = NULL;
     size_t slen;
     int ok;
 
+    switch (algorithm) {
+    case SSH_KEYTYPE_RSA:
+        md = MBEDTLS_MD_SHA1;
+        break;
+    case SSH_KEYTYPE_RSA_SHA256:
+        md = MBEDTLS_MD_SHA256;
+        break;
+    case SSH_KEYTYPE_RSA_SHA512:
+        md = MBEDTLS_MD_SHA512;
+        break;
+    default:
+        SSH_LOG(SSH_LOG_WARN, "Incomplatible key algorithm");
+        return NULL;
+    }
+
     sig = malloc(mbedtls_pk_get_bitlen(privkey) / 8);
     if (sig == NULL) {
         return NULL;
     }
 
-    ok = mbedtls_pk_sign(privkey, MBEDTLS_MD_SHA1, digest, dlen, sig, &slen,
+    ok = mbedtls_pk_sign(privkey, md, digest, dlen, sig, &slen,
             mbedtls_ctr_drbg_random, &ssh_mbedtls_ctr_drbg);
 
     if (ok != 0) {
@@ -1033,21 +1049,38 @@ static ssh_string rsa_do_sign(const unsigned char *digest, int dlen,
 
 ssh_signature pki_do_sign(const ssh_key privkey, const unsigned char *hash,
         size_t hlen)
+{
+    return pki_do_sign_alg(privkey, hash, hlen, privkey->type);
+}
+
+ssh_signature pki_do_sign_alg(const ssh_key privkey,
+                              const unsigned char *hash,
+                              size_t hlen,
+                              enum ssh_keytypes_e algorithm)
 {
     ssh_signature sig = NULL;
     int rc;
 
+    /* Only RSA supports different signature algorithm types now */
+    if (privkey->type != algorithm && privkey->type != SSH_KEYTYPE_RSA) {
+        SSH_LOG(SSH_LOG_WARN, "Incompatible signature algorithm passed");
+        return NULL;
+    }
+
     sig = ssh_signature_new();
     if (sig == NULL) {
         return NULL;
     }
 
-    sig->type = privkey->type;
+    sig->type = algorithm;
     sig->type_c = privkey->type_c;
 
     switch(privkey->type) {
         case SSH_KEYTYPE_RSA:
-            sig->rsa_sig = rsa_do_sign(hash, hlen, privkey->rsa);
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
+            sig->type_c = ssh_key_alg_to_char(algorithm);
+            sig->rsa_sig = rsa_do_sign_alg(hash, hlen, privkey->rsa, algorithm);
             if (sig->rsa_sig == NULL) {
                 ssh_signature_free(sig);
                 return NULL;
@@ -1105,7 +1138,7 @@ ssh_signature pki_do_sign_sessionid(const ssh_key key, const unsigned char
 
     switch (key->type) {
         case SSH_KEYTYPE_RSA:
-            sig->rsa_sig = rsa_do_sign(hash, hlen, key->rsa);
+            sig->rsa_sig = rsa_do_sign_alg(hash, hlen, key->rsa, key->type);
             if (sig->rsa_sig == NULL) {
                 ssh_signature_free(sig);
                 return NULL;
-- 
2.17.1


From 344228385f0a6f8a36780c605b86ba2e915e83e7 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Wed, 27 Jun 2018 15:19:02 +0200
Subject: [PATCH 09/19] tests: SHA2 extension signatures

This introduces a new test case for RSA unit tests, verifying that
libraries are able to provide and verify the RSA signatures with
SHA2.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 tests/unittests/torture_pki_rsa.c | 43 +++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/tests/unittests/torture_pki_rsa.c b/tests/unittests/torture_pki_rsa.c
index c265f88c..aafc0260 100644
--- a/tests/unittests/torture_pki_rsa.c
+++ b/tests/unittests/torture_pki_rsa.c
@@ -15,6 +15,9 @@
 #define LIBSSH_RSA_TESTKEY_PASSPHRASE "libssh_testkey_passphrase.id_rsa"
 
 const unsigned char RSA_HASH[] = "12345678901234567890";
+const unsigned char SHA256_HASH[] = "12345678901234567890123456789012";
+const unsigned char SHA512_HASH[] = "1234567890123456789012345678901234567890"
+                                    "123456789012345678901234";
 
 static int setup_rsa_key(void **state)
 {
@@ -392,6 +395,45 @@ static void torture_pki_rsa_generate_key(void **state)
     ssh_free(session);
 }
 
+static void torture_pki_rsa_sha2(void **state)
+{
+    int rc;
+    ssh_key key;
+    ssh_signature sign;
+    ssh_session session=ssh_new();
+    (void) state;
+
+    /* Setup */
+    rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &key);
+    assert_true(rc == SSH_OK);
+    assert_true(key != NULL);
+
+    /* Sign using old ssh-rsa algorithm */
+    sign = pki_do_sign_alg(key, RSA_HASH, 20, SSH_KEYTYPE_RSA);
+    assert_true(sign != NULL);
+    rc = pki_signature_verify(session,sign,key,RSA_HASH,20);
+    assert_true(rc == SSH_OK);
+    ssh_signature_free(sign);
+
+    /* Sign using rsa-sha2-256 algorithm */
+    sign = pki_do_sign_alg(key, SHA256_HASH, 32, SSH_KEYTYPE_RSA_SHA256);
+    assert_true(sign != NULL);
+    rc = pki_signature_verify(session,sign,key,SHA256_HASH,32);
+    assert_true(rc == SSH_OK);
+    ssh_signature_free(sign);
+
+    /* Sign using rsa-sha2-512 algorithm */
+    sign = pki_do_sign_alg(key, SHA512_HASH, 64, SSH_KEYTYPE_RSA_SHA512);
+    assert_true(sign != NULL);
+    rc = pki_signature_verify(session,sign,key,SHA512_HASH,64);
+    assert_true(rc == SSH_OK);
+    ssh_signature_free(sign);
+
+    /* Cleanup */
+    ssh_key_free(key);
+    ssh_free(session);
+}
+
 #ifdef HAVE_LIBCRYPTO
 static void torture_pki_rsa_write_privkey(void **state)
 {
@@ -555,6 +597,7 @@ int torture_run_tests(void) {
                                         setup_rsa_key,
                                         teardown),
 #endif /* HAVE_LIBCRYPTO */
+        cmocka_unit_test(torture_pki_rsa_sha2),
     };
 
     ssh_init();
-- 
2.17.1


From ac43fb058c41c0b53e027b049dcf118afe707ca9 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 26 Jun 2018 12:22:31 +0200
Subject: [PATCH 10/19] SHA2 extension in the ssh-agent interface

The new constants for flags are defined in draft-miller-ssh-agent-02
and active if the SHA2 extension is negotiated with the server.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/agent.h | 3 +++
 src/agent.c            | 7 +++++++
 2 files changed, 10 insertions(+)

diff --git a/include/libssh/agent.h b/include/libssh/agent.h
index 8f9ef941..0142f575 100644
--- a/include/libssh/agent.h
+++ b/include/libssh/agent.h
@@ -66,6 +66,9 @@
 #define SSH_COM_AGENT2_FAILURE                   102
 
 #define SSH_AGENT_OLD_SIGNATURE                  0x01
+/* Signature flags from draft-miller-ssh-agent-02 */
+#define SSH_AGENT_RSA_SHA2_256                   0x02
+#define SSH_AGENT_RSA_SHA2_512                   0x04
 
 struct ssh_agent_struct {
   struct ssh_socket_struct *sock;
diff --git a/src/agent.c b/src/agent.c
index 2b063074..786a2937 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -533,6 +533,13 @@ ssh_string ssh_agent_sign_data(ssh_session session,
         return NULL;
     }
 
+    /* Add Flags: SHA2 extension (RFC 8332) if negotiated */
+    if (pubkey->type == SSH_KEYTYPE_RSA) {
+        if (session->extensions & SSH_EXT_SIG_RSA_SHA512)
+            flags |= SSH_AGENT_RSA_SHA2_512;
+        else if (session->extensions & SSH_EXT_SIG_RSA_SHA256)
+            flags |= SSH_AGENT_RSA_SHA2_256;
+    }
     if (ssh_buffer_add_u32(request, htonl(flags)) < 0) {
         ssh_buffer_free(request);
         return NULL;
-- 
2.17.1


From fe4670de8d9fbdab2bd148ce252f20aea251d85c Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 2 Jul 2018 10:50:28 +0200
Subject: [PATCH 11/19] kex: The public key algorithms are no longer only host
 keys only

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 src/kex.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/kex.c b/src/kex.c
index 889eb2bf..e00696ec 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -86,12 +86,12 @@
 
 #ifdef HAVE_ECDH
 #define ECDH "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,"
-#define HOSTKEYS "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss"
+#define PUBLIC_KEY_ALGORITHMS "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss"
 #else
 #ifdef HAVE_DSA
-#define HOSTKEYS "ssh-ed25519,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss"
+#define PUBLIC_KEY_ALGORITHMS "ssh-ed25519,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss"
 #else
-#define HOSTKEYS "ssh-ed25519,ssh-rsa,rsa-sha2-512,rsa-sha2-256"
+#define PUBLIC_KEY_ALGORITHMS "ssh-ed25519,ssh-rsa,rsa-sha2-512,rsa-sha2-256"
 #endif
 #define ECDH ""
 #endif
@@ -107,7 +107,7 @@
 /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */
 static const char *default_methods[] = {
   KEY_EXCHANGE,
-  HOSTKEYS,
+  PUBLIC_KEY_ALGORITHMS,
   AES BLOWFISH DES,
   AES BLOWFISH DES,
   "hmac-sha2-256,hmac-sha2-512,hmac-sha1",
@@ -122,7 +122,7 @@ static const char *default_methods[] = {
 /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */
 static const char *supported_methods[] = {
   KEY_EXCHANGE,
-  HOSTKEYS,
+  PUBLIC_KEY_ALGORITHMS,
   CHACHA20 AES BLOWFISH DES_SUPPORTED,
   CHACHA20 AES BLOWFISH DES_SUPPORTED,
   "hmac-sha2-256,hmac-sha2-512,hmac-sha1",
-- 
2.17.1


From aba4fe6d0108db1b11ceda3b9367c57a17a94ea0 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 2 Jul 2018 14:58:31 +0200
Subject: [PATCH 12/19] Configuration PubkeyAcceptedTypes and
 SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES

This adds support for PubkeyAcceptedTypes option in configuration files
and a new option SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES having the same
meaning.
This effectively allows to disable using the SHA2 extension, can disable
other old public key mechanisms out of the box (hello DSA) or force
the new SHA2-based key algorithm types if needed.

This exposes the  default_methods  array from  kex.c.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/libssh.h           |  1 +
 include/libssh/pki.h              |  1 +
 include/libssh/session.h          |  1 +
 src/config.c                      |  9 +++++-
 src/kex.c                         |  2 +-
 src/options.c                     | 31 +++++++++++++++++++
 src/pki.c                         | 46 +++++++++++++++++++++--------
 src/session.c                     |  1 +
 tests/unittests/torture_config.c  |  4 +++
 tests/unittests/torture_options.c | 49 +++++++++++++++++++++++++++++++
 10 files changed, 131 insertions(+), 14 deletions(-)

diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 35059e02..3b907834 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -379,6 +379,7 @@ enum ssh_options_e {
   SSH_OPTIONS_GSSAPI_AUTH,
   SSH_OPTIONS_GLOBAL_KNOWNHOSTS,
   SSH_OPTIONS_NODELAY,
+  SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
 };
 
 enum {
diff --git a/include/libssh/pki.h b/include/libssh/pki.h
index 4a4ce612..7856da9b 100644
--- a/include/libssh/pki.h
+++ b/include/libssh/pki.h
@@ -133,4 +133,5 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
 ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key);
 ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key);
 
+int ssh_key_algorithm_allowed(ssh_session session, const char *type);
 #endif /* PKI_H_ */
diff --git a/include/libssh/session.h b/include/libssh/session.h
index a01afba0..17d8f00a 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -199,6 +199,7 @@ struct ssh_session_struct {
         char *knownhosts;
         char *global_knownhosts;
         char *wanted_methods[10];
+        char *pubkey_accepted_types;
         char *ProxyCommand;
         char *custombanner;
         unsigned long timeout; /* seconds */
diff --git a/src/config.c b/src/config.c
index 12792afd..d3c5a0db 100644
--- a/src/config.c
+++ b/src/config.c
@@ -72,6 +72,7 @@ enum ssh_config_opcode_e {
   SOC_KBDINTERACTIVEAUTHENTICATION,
   SOC_PASSWORDAUTHENTICATION,
   SOC_PUBKEYAUTHENTICATION,
+  SOC_PUBKEYACCEPTEDTYPES,
 
   SOC_END /* Keep this one last in the list */
 };
@@ -144,7 +145,7 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
   { "preferredauthentications", SOC_UNSUPPORTED},
   { "proxyjump", SOC_UNSUPPORTED},
   { "proxyusefdpass", SOC_UNSUPPORTED},
-  { "pubkeyacceptedtypes", SOC_UNSUPPORTED},
+  { "pubkeyacceptedtypes", SOC_PUBKEYACCEPTEDTYPES},
   { "rekeylimit", SOC_UNSUPPORTED},
   { "remotecommand", SOC_UNSUPPORTED},
   { "revokedhostkeys", SOC_UNSUPPORTED},
@@ -591,6 +592,12 @@ static int ssh_config_parse_line(ssh_session session, const char *line,
             ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, p);
         }
         break;
+    case SOC_PUBKEYACCEPTEDTYPES:
+        p = ssh_config_get_str_tok(&s, NULL);
+        if (p && *parsing) {
+            ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, p);
+        }
+        break;
     case SOC_KEXALGORITHMS:
         p = ssh_config_get_str_tok(&s, NULL);
         if (p && *parsing) {
diff --git a/src/kex.c b/src/kex.c
index e00696ec..668e68ea 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -105,7 +105,7 @@
 #define KEX_EXTENSION_CLIENT "ext-info-c"
 
 /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */
-static const char *default_methods[] = {
+const char *default_methods[] = {
   KEY_EXCHANGE,
   PUBLIC_KEY_ALGORITHMS,
   AES BLOWFISH DES,
diff --git a/src/options.c b/src/options.c
index 87c5bb42..2e80f2b2 100644
--- a/src/options.c
+++ b/src/options.c
@@ -147,6 +147,14 @@ int ssh_options_copy(ssh_session src, ssh_session *dest) {
             return -1;
         }
     }
+
+    if (src->opts.pubkey_accepted_types) {
+        new->opts.pubkey_accepted_types = strdup(src->opts.pubkey_accepted_types);
+        if (new->opts.pubkey_accepted_types == NULL) {
+            ssh_free(new);
+            return -1;
+        }
+    }
     new->opts.fd                   = src->opts.fd;
     new->opts.port                 = src->opts.port;
     new->opts.timeout              = src->opts.timeout;
@@ -343,6 +351,11 @@ int ssh_options_set_algo(ssh_session session,
  *                comma-separated list). ex:
  *                "ssh-rsa,ssh-dss,ecdh-sha2-nistp256"
  *
+ *              - SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
+ *                Set the preferred public key algorithms to be used for
+ *                authentication (const char *, comma-separated list). ex:
+ *                "ssh-rsa,rsa-sha2-256,ssh-dss,ecdh-sha2-nistp256"
+ *
  *              - SSH_OPTIONS_COMPRESSION_C_S:
  *                Set the compression to use for client to server
  *                communication (const char *, "yes", "no" or a specific
@@ -743,6 +756,24 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
                     return -1;
             }
             break;
+        case SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
+            v = value;
+            if (v == NULL || v[0] == '\0') {
+                ssh_set_error_invalid(session);
+                return -1;
+            } else {
+                p = ssh_keep_known_algos(SSH_HOSTKEYS, v);
+                if (p == NULL) {
+                    ssh_set_error(session, SSH_REQUEST_DENIED,
+                        "Setting method: no known public key algorithm (%s)",
+                         v);
+                    return -1;
+                }
+
+                SAFE_FREE(session->opts.pubkey_accepted_types);
+                session->opts.pubkey_accepted_types = p;
+            }
+            break;
         case SSH_OPTIONS_HMAC_C_S:
             v = value;
             if (v == NULL || v[0] == '\0') {
diff --git a/src/pki.c b/src/pki.c
index eef081a4..7c51999d 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -80,6 +80,8 @@ enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey) {
     return SSH_KEYTYPE_UNKNOWN;
 }
 
+extern const char *default_methods[];
+
 /**
  * @brief returns the ECDSA key name ("ecdsa-sha2-nistp256" for example)
  *
@@ -246,6 +248,26 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
   return NULL;
 }
 
+/**
+ * @brief Checks the given key against the configured allowed
+ * public key algorithm types
+ *
+ * @param[in] session The SSH session
+ * @parma[in] type    The key algorithm to check
+ * @returns           1 if the key algorithm is allowed 0 otherwise
+ */
+int ssh_key_algorithm_allowed(ssh_session session, const char *type)
+{
+    const char *allowed_list;
+
+    allowed_list = session->opts.pubkey_accepted_types;
+    if (allowed_list == NULL)
+        allowed_list = default_methods[SSH_HOSTKEYS];
+
+    SSH_LOG(SSH_LOG_DEBUG, "Checking %s with list <%s>", type, allowed_list);
+    return ssh_match_group(allowed_list, type);
+}
+
 /**
  * @brief Convert a key type to a pubkey algorithm type. This is usually
  * the same as public key type, unless the SHA2 extension (RFC 8332) is
@@ -259,21 +281,21 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
 enum ssh_keytypes_e ssh_key_type_to_alg(ssh_session session,
                                         enum ssh_keytypes_e type)
 {
-  /* TODO this should also reflect supported key types specified in
-   * configuration (ssh_config PubkeyAcceptedKeyTypes) */
-  switch (type) {
+    switch (type) {
     case SSH_KEYTYPE_RSA:
-      if (session->extensions & SSH_EXT_SIG_RSA_SHA512)
-        return SSH_KEYTYPE_RSA_SHA512;
-      if (session->extensions & SSH_EXT_SIG_RSA_SHA256)
-        return SSH_KEYTYPE_RSA_SHA256;
-      FALL_THROUGH;
+        if (ssh_key_algorithm_allowed(session, "rsa-sha2-512")
+          && (session->extensions & SSH_EXT_SIG_RSA_SHA512))
+            return SSH_KEYTYPE_RSA_SHA512;
+        if (ssh_key_algorithm_allowed(session, "rsa-sha2-256")
+          && (session->extensions & SSH_EXT_SIG_RSA_SHA256))
+            return SSH_KEYTYPE_RSA_SHA256;
+        FALL_THROUGH;
     default:
-      return type;
-  }
+        /* Other key types match the signature algorithm */
+        return type;
+    }
 
-  /* We should never reach this */
-  return SSH_KEYTYPE_UNKNOWN;
+    return SSH_KEYTYPE_UNKNOWN;
 }
 
 /**
diff --git a/src/session.c b/src/session.c
index 35841183..37e83c2b 100644
--- a/src/session.c
+++ b/src/session.c
@@ -282,6 +282,7 @@ void ssh_free(ssh_session session) {
   SAFE_FREE(session->opts.ProxyCommand);
   SAFE_FREE(session->opts.gss_server_identity);
   SAFE_FREE(session->opts.gss_client_identity);
+  SAFE_FREE(session->opts.pubkey_accepted_types);
 
   for (i = 0; i < 10; i++) {
       if (session->opts.wanted_methods[i]) {
diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c
index a7e00c73..fcfd0bde 100644
--- a/tests/unittests/torture_config.c
+++ b/tests/unittests/torture_config.c
@@ -23,6 +23,7 @@ extern LIBSSH_THREAD int ssh_log_level;
 #define ID_FILE "/etc/xxx"
 #define KEXALGORITHMS "ecdh-sha2-nistp521,diffie-hellman-group14-sha1"
 #define HOSTKEYALGORITHMS "ssh-ed25519,ecdsa-sha2-nistp521,ssh-rsa"
+#define PUBKEYACCEPTEDTYPES "rsa-sha2-512,ssh-rsa,ecdsa-sha2-nistp521"
 #define MACS "hmac-sha1,hmac-sha2-256"
 #define USER_KNOWN_HOSTS "%d/my_known_hosts"
 #define GLOBAL_KNOWN_HOSTS "/etc/ssh/my_ssh_known_hosts"
@@ -50,6 +51,7 @@ static int setup_config_files(void **state)
                        "\n\nIdentityFile "ID_FILE"\n"
                        "\n\nKexAlgorithms "KEXALGORITHMS"\n"
                        "\n\nHostKeyAlgorithms "HOSTKEYALGORITHMS"\n"
+                       "\n\nPubkeyAcceptedTypes "PUBKEYACCEPTEDTYPES"\n"
                        "\n\nMACs "MACS"\n");
 
     /* Multiple Port settings -> parsing returns early. */
@@ -148,6 +150,8 @@ static void torture_config_from_file(void **state) {
 
     assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], HOSTKEYALGORITHMS);
 
+    assert_string_equal(session->opts.pubkey_accepted_types, PUBKEYACCEPTEDTYPES);
+
     assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], MACS);
     assert_string_equal(session->opts.wanted_methods[SSH_MAC_S_C], MACS);
 }
diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c
index a3000aec..5a3b103e 100644
--- a/tests/unittests/torture_options.c
+++ b/tests/unittests/torture_options.c
@@ -115,6 +115,54 @@ static void torture_options_set_hostkey(void **state) {
     assert_false(rc == 0);
 }
 
+static void torture_options_set_pubkey_accepted_types(void **state) {
+    ssh_session session = *state;
+    int rc;
+    enum ssh_keytypes_e type;
+
+    /* Test known public key algorithms */
+    rc = ssh_options_set(session,
+                         SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+                         "ssh-ed25519,ecdsa-sha2-nistp384,ssh-rsa");
+    assert_true(rc == 0);
+    assert_string_equal(session->opts.pubkey_accepted_types,
+                        "ssh-ed25519,ecdsa-sha2-nistp384,ssh-rsa");
+
+    /* Test one unknown public key algorithms */
+    rc = ssh_options_set(session,
+                         SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+                         "ssh-ed25519,unknown-crap@xxxxxxxxxxx,ssh-rsa");
+    assert_true(rc == 0);
+    assert_string_equal(session->opts.pubkey_accepted_types,
+                        "ssh-ed25519,ssh-rsa");
+
+    /* Test all unknown public key algorithms */
+    rc = ssh_options_set(session,
+                         SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+                         "unknown-crap@xxxxxxxxxxx,more-crap@xxxxxxxxxxx");
+    assert_false(rc == 0);
+
+    /* Test that the option affects the algorithm selection for RSA keys */
+    /* simulate the SHA2 extension was negotiated */
+    session->extensions = SSH_EXT_SIG_RSA_SHA256;
+
+    /* previous configuration did not list the SHA2 extension, so
+     * it should not be used */
+    type = ssh_key_type_to_alg(session, SSH_KEYTYPE_RSA);
+    assert_int_equal(type, SSH_KEYTYPE_RSA);
+
+    /* now, lets allow the signature from SHA2 extension and expect
+     * it to be used */
+    rc = ssh_options_set(session,
+                         SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+                         "rsa-sha2-256,ssh-rsa");
+    assert_true(rc == 0);
+    assert_string_equal(session->opts.pubkey_accepted_types,
+                        "rsa-sha2-256,ssh-rsa");
+    type = ssh_key_type_to_alg(session, SSH_KEYTYPE_RSA);
+    assert_int_equal(type, SSH_KEYTYPE_RSA_SHA256);
+}
+
 static void torture_options_set_macs(void **state) {
     ssh_session session = *state;
     int rc;
@@ -399,6 +447,7 @@ int torture_run_tests(void) {
         cmocka_unit_test_setup_teardown(torture_options_set_ciphers, setup, teardown),
         cmocka_unit_test_setup_teardown(torture_options_set_key_exchange, setup, teardown),
         cmocka_unit_test_setup_teardown(torture_options_set_hostkey, setup, teardown),
+        cmocka_unit_test_setup_teardown(torture_options_set_pubkey_accepted_types, setup, teardown),
         cmocka_unit_test_setup_teardown(torture_options_set_macs, setup, teardown),
         cmocka_unit_test_setup_teardown(torture_options_config_host, setup, teardown)
     };
-- 
2.17.1


From 38754ed83448943dc683bf8a9bd5fc9499b4f7dd Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 2 Jul 2018 16:44:47 +0200
Subject: [PATCH 13/19] auth: Prevent authentication with non-allowed key
 algorithms

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 src/auth.c | 38 ++++++++++++++++++++++++++++----------
 1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/src/auth.c b/src/auth.c
index 534e9185..aa26bff5 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -456,6 +456,18 @@ int ssh_userauth_try_publickey(ssh_session session,
             return SSH_ERROR;
     }
 
+    sig_type = ssh_key_type_to_alg(session, pubkey->type);
+    sig_type_c = ssh_key_alg_to_char(sig_type);
+
+    /* Check if the given public key algorithm is allowed */
+    if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+        ssh_set_error(session, SSH_REQUEST_DENIED,
+                      "The key algorithm %s is not allowed to be used by"
+                      " PubkeyAcceptedKeyTypes configuration option",
+                      sig_type_c);
+        return SSH_AUTH_DENIED;
+    }
+
     rc = ssh_userauth_request_service(session);
     if (rc == SSH_AGAIN) {
         return SSH_AUTH_AGAIN;
@@ -468,9 +480,6 @@ int ssh_userauth_try_publickey(ssh_session session,
     if (rc < 0) {
         goto fail;
     }
-    sig_type = ssh_key_type_to_alg(session, pubkey->type);
-    sig_type_c = ssh_key_alg_to_char(sig_type);
-
     /* request */
     rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
             SSH2_MSG_USERAUTH_REQUEST,
@@ -538,7 +547,7 @@ int ssh_userauth_publickey(ssh_session session,
 {
     ssh_string str = NULL;
     int rc;
-    const char *type_c;
+    const char *sig_type_c;
     enum ssh_keytypes_e key_type, sig_type;
 
     if (session == NULL) {
@@ -562,6 +571,20 @@ int ssh_userauth_publickey(ssh_session session,
             return SSH_AUTH_ERROR;
     }
 
+    /* Cert auth requires presenting the cert type name (*-cert@xxxxxxxxxxx) */
+    key_type = privkey->cert != NULL ? privkey->cert_type : privkey->type;
+    sig_type = ssh_key_type_to_alg(session, key_type);
+    sig_type_c = ssh_key_alg_to_char(sig_type);
+
+    /* Check if the given public key algorithm is allowed */
+    if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+        ssh_set_error(session, SSH_REQUEST_DENIED,
+                      "The key algorithm %s is not allowed to be used by"
+                      " PubkeyAcceptedKeyTypes configuration option",
+                      sig_type_c);
+        return SSH_AUTH_DENIED;
+    }
+
     rc = ssh_userauth_request_service(session);
     if (rc == SSH_AGAIN) {
         return SSH_AUTH_AGAIN;
@@ -569,11 +592,6 @@ int ssh_userauth_publickey(ssh_session session,
         return SSH_AUTH_ERROR;
     }
 
-    /* Cert auth requires presenting the cert type name (*-cert@xxxxxxxxxxx) */
-    key_type = privkey->cert != NULL ? privkey->cert_type : privkey->type;
-    sig_type = ssh_key_type_to_alg(session, key_type);
-    type_c = ssh_key_alg_to_char(sig_type);
-
     /* get public key or cert */
     rc = ssh_pki_export_pubkey_blob(privkey, &str);
     if (rc < 0) {
@@ -587,7 +605,7 @@ int ssh_userauth_publickey(ssh_session session,
             "ssh-connection",
             "publickey",
             1, /* private key */
-            type_c, /* algo */
+            sig_type_c, /* algo */
             str /* public key or cert */
             );
     if (rc < 0) {
-- 
2.17.1


From c79f41737b5f7ca15ca0fb7c639ef166f46dd1bd Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 2 Jul 2018 16:49:04 +0200
Subject: [PATCH 14/19] test: Verify the public key algorithms can be limited
 by configuration option

SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES configuration option can limit
what keys can or can not be used for public key authentication.

This is useful for disabling obsolete algorithms while not completely
removing the support for them or allows to configure what public key
algorithms will be used with the SHA2 RSA extension.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 tests/client/torture_auth.c | 87 +++++++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/tests/client/torture_auth.c b/tests/client/torture_auth.c
index 7c436711..e1b096d6 100644
--- a/tests/client/torture_auth.c
+++ b/tests/client/torture_auth.c
@@ -547,6 +547,87 @@ static void torture_auth_agent_cert_nonblocking(void **state) {
   torture_auth_agent_nonblocking(state);
 }
 
+static void torture_auth_pubkey_types(void **state) {
+    struct torture_state *s = *state;
+    ssh_session session = s->ssh.session;
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+    assert_int_equal(rc, SSH_OK);
+
+    rc = ssh_connect(session);
+    assert_int_equal(rc, SSH_OK);
+
+    rc = ssh_userauth_none(session,NULL);
+    /* This request should return a SSH_REQUEST_DENIED error */
+    if (rc == SSH_ERROR) {
+        assert_true(ssh_get_error_code(session) == SSH_REQUEST_DENIED);
+    }
+    rc = ssh_userauth_list(session, NULL);
+    assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+    /* Disable RSA key types for authentication */
+    rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+                         "ssh-dss");
+    assert_int_equal(rc, SSH_OK);
+
+    rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+    assert_int_equal(rc, SSH_AUTH_DENIED);
+
+    /* Now enable it and retry */
+    rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+                         "rsa-sha2-512,ssh-rsa");
+    assert_int_equal(rc, SSH_OK);
+
+    rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+    assert_int_equal(rc, SSH_AUTH_SUCCESS);
+}
+
+static void torture_auth_pubkey_types_nonblocking(void **state) {
+    struct torture_state *s = *state;
+    ssh_session session = s->ssh.session;
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
+    assert_int_equal(rc, SSH_OK);
+
+    rc = ssh_connect(session);
+    assert_int_equal(rc, SSH_OK);
+
+    ssh_set_blocking(session,0);
+    do {
+      rc = ssh_userauth_none(session, NULL);
+    } while (rc == SSH_AUTH_AGAIN);
+
+    /* This request should return a SSH_REQUEST_DENIED error */
+    if (rc == SSH_ERROR) {
+        assert_int_equal(ssh_get_error_code(session), SSH_REQUEST_DENIED);
+    }
+
+    rc = ssh_userauth_list(session, NULL);
+    assert_true(rc & SSH_AUTH_METHOD_PUBLICKEY);
+
+    /* Disable RSA key types for authentication */
+    rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+                         "ssh-dss");
+    assert_int_equal(rc, SSH_OK);
+
+    do {
+        rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+    } while (rc == SSH_AUTH_AGAIN);
+    assert_int_equal(rc, SSH_AUTH_DENIED);
+
+    /* Now enable it and retry */
+    rc = ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+                         "rsa-sha2-512,ssh-rsa");
+    assert_int_equal(rc, SSH_OK);
+
+    do {
+        rc = ssh_userauth_publickey_auto(session, NULL, NULL);
+    } while (rc == SSH_AUTH_AGAIN);
+    assert_int_equal(rc, SSH_AUTH_SUCCESS);
+}
+
 
 int torture_run_tests(void) {
     int rc;
@@ -590,6 +671,12 @@ int torture_run_tests(void) {
         cmocka_unit_test_setup_teardown(torture_auth_agent_cert_nonblocking,
                                         agent_cert_setup,
                                         agent_teardown),
+        cmocka_unit_test_setup_teardown(torture_auth_pubkey_types,
+                                        pubkey_setup,
+                                        session_teardown),
+        cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_nonblocking,
+                                        pubkey_setup,
+                                        session_teardown),
     };
 
     ssh_init();
-- 
2.17.1


From 06dcf2acc906b9df1b7059513fd6e191695c781e Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 3 Jul 2018 15:53:44 +0200
Subject: [PATCH 15/19] messages: Generate correct digest for pki signatures

This is no change for old signatures, where the public key algorithm
matches the public key type

This is aproblem when using SHA2 extension for the RSA keys, where
the new signature algorithsm are introduced in addition to the
exitsing ssh-rsa and the signature verification was failing.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 src/messages.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/src/messages.c b/src/messages.c
index 6fe87f78..8a469d41 100644
--- a/src/messages.c
+++ b/src/messages.c
@@ -645,7 +645,8 @@ error:
  */
 static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session,
                                                 ssh_message msg,
-                                                const char *service)
+                                                const char *service,
+                                                ssh_string algo)
 {
     struct ssh_crypto_struct *crypto =
         session->current_crypto ? session->current_crypto :
@@ -673,7 +674,7 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session,
                          service,
                          "publickey", /* method */
                          1, /* has to be signed (true) */
-                         msg->auth_request.pubkey->type_c, /* pubkey algorithm */
+                         ssh_string_get_char(algo), /* pubkey algorithm */
                          str); /* public key as a blob */
 
     ssh_string_free(str);
@@ -785,13 +786,13 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){
     if (rc != SSH_OK) {
       goto error;
     }
-    ssh_string_free(algo);
-    algo = NULL;
 
     rc = ssh_pki_import_pubkey_blob(pubkey_blob, &msg->auth_request.pubkey);
     ssh_string_free(pubkey_blob);
     pubkey_blob = NULL;
     if (rc < 0) {
+        ssh_string_free(algo);
+        algo = NULL;
         goto error;
     }
     msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_NONE;
@@ -804,10 +805,14 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request){
         if(sig_blob == NULL) {
             SSH_LOG(SSH_LOG_PACKET, "Invalid signature packet from peer");
             msg->auth_request.signature_state = SSH_PUBLICKEY_STATE_ERROR;
+            ssh_string_free(algo);
+            algo = NULL;
             goto error;
         }
 
-        digest = ssh_msg_userauth_build_digest(session, msg, service);
+        digest = ssh_msg_userauth_build_digest(session, msg, service, algo);
+        ssh_string_free(algo);
+        algo = NULL;
         if (digest == NULL) {
             ssh_string_free(sig_blob);
             SSH_LOG(SSH_LOG_PACKET, "Failed to get digest");
-- 
2.17.1


From 92731562a2750e3447374b8f1577a152e3e40fb2 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 3 Jul 2018 16:16:25 +0200
Subject: [PATCH 16/19] server: Support for extension negotiation

This includes intercepting the  ext-info-c  string from
the client kex proposal, configuring the server to allow using
this extension and sending the SSH_MSG_EXT_INFO packet back
to the client after the new keys are in use.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/session.h |  1 +
 src/kex.c                | 16 ++++++++++++++++
 src/server.c             | 38 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 55 insertions(+)

diff --git a/include/libssh/session.h b/include/libssh/session.h
index 17d8f00a..4d3e0f77 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -90,6 +90,7 @@ enum ssh_pending_call_e {
 /* server-sig-algs extension */
 #define SSH_EXT_SIG_RSA_SHA256  0x01
 #define SSH_EXT_SIG_RSA_SHA512  0x02
+#define SSH_EXT_ALL             SSH_EXT_SIG_RSA_SHA256 | SSH_EXT_SIG_RSA_SHA512
 
 /* members that are common to ssh_session and ssh_bind */
 struct ssh_common_struct {
diff --git a/src/kex.c b/src/kex.c
index 668e68ea..12ee2480 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -510,6 +510,22 @@ SSH_PACKET_CALLBACK(ssh_packet_kexinit){
             goto error;
         }
 
+        /*
+         * If client sent a ext-info-c message in the kex list, it supports
+         * RFC 8308 extension negotiation.
+         */
+        rc = ssh_match_group(session->next_crypto->client_kex.methods[SSH_KEX],
+                             KEX_EXTENSION_CLIENT);
+        if (rc == 1) {
+            /*
+             * Enable all the supported extensions and when the time comes
+             * (after NEWKEYS) send them to the client.
+             */
+            SSH_LOG(SSH_LOG_DEBUG, "The client supports extension "
+                    "negotiation: enabling all extensions");
+            session->extensions = SSH_EXT_ALL;
+        }
+
         /*
          * Remember whether 'first_kex_packet_follows' was set and the client
          * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message
diff --git a/src/server.c b/src/server.c
index 1e1ef8e7..3a59d775 100644
--- a/src/server.c
+++ b/src/server.c
@@ -67,6 +67,7 @@
 
 static int dh_handshake_server(ssh_session session);
 
+extern char *default_methods[];
 
 /**
  * @addtogroup libssh_server
@@ -194,6 +195,34 @@ static int ssh_server_kexdh_init(ssh_session session, ssh_buffer packet){
     return SSH_OK;
 }
 
+static int ssh_server_send_extensions(ssh_session session) {
+    int rc;
+
+    SSH_LOG(SSH_LOG_PACKET, "Sending SSH_MSG_EXT_INFO");
+    /*
+     * We can list here all the default hostkey methods, since
+     * they already contain the SHA2 extension algorithms
+     */
+    rc = ssh_buffer_pack(session->out_buffer,
+                         "bdss",
+                         SSH2_MSG_EXT_INFO,
+                         1, /* nr. of extensions */
+                         "server-sig-algs",
+                         default_methods[SSH_HOSTKEYS]);
+    if (rc != SSH_OK)
+        goto error;
+
+    if (ssh_packet_send(session) == SSH_ERROR) {
+        goto error;
+    }
+
+    return 0;
+error:
+    ssh_buffer_reinit(session->out_buffer);
+
+    return -1;
+}
+
 SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){
   int rc = SSH_ERROR;
   (void)type;
@@ -486,6 +515,15 @@ static void ssh_server_connection_callback(ssh_session session){
                 session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
                 if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
                     session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
+
+               /*
+                * If the client supports extension negotiation, we will send
+                * our supported extensions now. This is the first message after
+                * sending NEWKEYS message and after turning on crypto.
+                */
+               if (session->extensions) {
+                   ssh_server_send_extensions(session);
+               }
             }
             break;
         case SSH_SESSION_STATE_AUTHENTICATING:
-- 
2.17.1


From 386a2ae1a608325090b33cc558bac201ecb13053 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 3 Jul 2018 16:54:35 +0200
Subject: [PATCH 17/19] server: We should list SHA2 variants in offered
 hostkeys

The SHA2 variants should be preferred. Also to buffer needs to be
extended to fit all possible public key algorithms.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 src/server.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/server.c b/src/server.c
index 3a59d775..11d0a276 100644
--- a/src/server.c
+++ b/src/server.c
@@ -88,7 +88,7 @@ static int server_set_kex(ssh_session session) {
   struct ssh_kex_struct *server = &session->next_crypto->server_kex;
   int i, j, rc;
   const char *wanted;
-  char hostkeys[64] = {0};
+  char hostkeys[128] = {0};
   enum ssh_keytypes_e keytype;
   size_t len;
   int ok;
@@ -124,6 +124,11 @@ static int server_set_kex(ssh_session session) {
   }
 #endif
   if (session->srv.rsa_key != NULL) {
+      /* We support also the SHA2 variants */
+      len = strlen(hostkeys);
+      snprintf(hostkeys + len, sizeof(hostkeys) - len,
+               ",rsa-sha2-512,rsa-sha2-256");
+
       len = strlen(hostkeys);
       keytype = ssh_key_type(session->srv.rsa_key);
 
-- 
2.17.1


From 11b2d3c3697bbff4777911b0b9369c701d9db26c Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 16 Jul 2018 09:22:42 +0200
Subject: [PATCH 18/19] pki: Support RSA SHA2 extension in server

This involves mostly creation of host keys proofs

TODO: The signing functions could be shared with the client ones
used for public key authentication to avoid code duplication.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/pki_priv.h | 14 +++++++-----
 src/pki.c                 | 31 +++++++++++++++++++-------
 src/pki_crypto.c          | 46 ++++++++++++---------------------------
 src/pki_gcrypt.c          | 41 ++++++++++++++++++++++++----------
 src/pki_mbedcrypto.c      | 26 +++++++++++++---------
 5 files changed, 90 insertions(+), 68 deletions(-)

diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h
index 868794f6..69d3665b 100644
--- a/include/libssh/pki_priv.h
+++ b/include/libssh/pki_priv.h
@@ -93,16 +93,18 @@ int pki_signature_verify(ssh_session session,
                          size_t hlen);
 
 /* SSH Signing Functions */
-ssh_signature pki_do_sign(const ssh_key privkey,
-                          const unsigned char *hash,
-                          size_t hlen);
+#define pki_do_sign(key, hash, hlen) \
+    pki_do_sign_alg(key, hash, hlen, key->type)
 ssh_signature pki_do_sign_alg(const ssh_key privkey,
                               const unsigned char *hash,
                               size_t hlen,
                               enum ssh_keytypes_e algorithm);
-ssh_signature pki_do_sign_sessionid(const ssh_key key,
-                                    const unsigned char *hash,
-                                    size_t hlen);
+#define pki_do_sign_sessionid(key, hash, hlen) \
+    pki_do_sign_sessionid_alg(key, hash, hlen, key->type)
+ssh_signature pki_do_sign_sessionid_alg(const ssh_key key,
+                                        const unsigned char *hash,
+                                        size_t hlen,
+                                        enum ssh_keytypes_e algorithm);
 int pki_ed25519_sign(const ssh_key privkey, ssh_signature sig,
         const unsigned char *hash, size_t hlen);
 int pki_ed25519_verify(const ssh_key pubkey, ssh_signature sig,
diff --git a/src/pki.c b/src/pki.c
index 7c51999d..3009705b 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -1976,21 +1976,36 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
             sig = NULL;
         }
     } else {
-        unsigned char hash[SHA_DIGEST_LEN] = {0};
-        SHACTX ctx;
+        unsigned char hash[SHA512_DIGEST_LEN] = {0};
+        uint32_t hlen = 0;
+        enum ssh_keytypes_e sig_type;
 
-        ctx = sha1_init();
-        if (ctx == NULL) {
+        sig_type = ssh_key_type_to_alg(session, privkey->type);
+        switch (sig_type) {
+        case SSH_KEYTYPE_RSA_SHA256:
+            sha256(crypto->secret_hash, crypto->digest_len, hash);
+            hlen = SHA256_DIGEST_LEN;
+            break;
+        case SSH_KEYTYPE_RSA_SHA512:
+            sha512(crypto->secret_hash, crypto->digest_len, hash);
+            hlen = SHA512_DIGEST_LEN;
+            break;
+        case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_DSS:
+            sha1(crypto->secret_hash, crypto->digest_len, hash);
+            hlen = SHA_DIGEST_LEN;
+            break;
+        default:
+            SSH_LOG(SSH_LOG_TRACE, "Unknown sig->type: %d", sig->type);
             return NULL;
         }
-        sha1_update(ctx, crypto->secret_hash, crypto->digest_len);
-        sha1_final(hash, ctx);
+
 
 #ifdef DEBUG_CRYPTO
-        ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN);
+        ssh_print_hexa("Hash being signed", hash, hlen);
 #endif
 
-        sig = pki_do_sign_sessionid(privkey, hash, SHA_DIGEST_LEN);
+        sig = pki_do_sign_sessionid_alg(privkey, hash, hlen, sig_type);
         if (sig == NULL) {
             return NULL;
         }
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index 46f790ac..e947a2b5 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -1249,26 +1249,6 @@ static ssh_string _RSA_do_sign_alg(const unsigned char *digest,
     return sig_blob;
 }
 
-/**
- * @internal
- *
- * @brief Compute a digital signature.
- *
- * @param[in]  digest    The message digest.
- *
- * @param[in]  dlen      The length of the digest.
- *
- * @param[in]  privkey   The private rsa key to use for signing.
- *
- * @return               A newly allocated rsa sig blob or NULL on error.
- */
-static ssh_string _RSA_do_sign(const unsigned char *digest,
-                                    int dlen,
-                                    RSA *privkey)
-{
-    return _RSA_do_sign_alg(digest, dlen, privkey, SSH_KEYTYPE_RSA);
-}
-
 static ssh_string pki_dsa_signature_to_blob(const ssh_signature sig)
 {
     char buffer[40] = { 0 };
@@ -1740,13 +1720,6 @@ int pki_signature_verify(ssh_session session,
     return SSH_OK;
 }
 
-ssh_signature pki_do_sign(const ssh_key privkey,
-                          const unsigned char *hash,
-                          size_t hlen)
-{
-    return pki_do_sign_alg(privkey, hash, hlen, privkey->type);
-}
-
 ssh_signature pki_do_sign_alg(const ssh_key privkey,
                               const unsigned char *hash,
                               size_t hlen,
@@ -1833,17 +1806,25 @@ ssh_signature pki_do_sign_alg(const ssh_key privkey,
 }
 
 #ifdef WITH_SERVER
-ssh_signature pki_do_sign_sessionid(const ssh_key key,
-                                    const unsigned char *hash,
-                                    size_t hlen)
+ssh_signature pki_do_sign_sessionid_alg(const ssh_key key,
+                                        const unsigned char *hash,
+                                        size_t hlen,
+                                        enum ssh_keytypes_e algorithm)
 {
     ssh_signature sig;
 
+    /* Only RSA supports different signature algorithm types now */
+    if (key->type != algorithm && key->type != SSH_KEYTYPE_RSA) {
+        SSH_LOG(SSH_LOG_WARN, "Incompatible signature algorithm passed");
+        return NULL;
+    }
+
     sig = ssh_signature_new();
     if (sig == NULL) {
         return NULL;
     }
-    sig->type = key->type;
+
+    sig->type = algorithm;
     sig->type_c = key->type_c;
 
     switch(key->type) {
@@ -1856,7 +1837,8 @@ ssh_signature pki_do_sign_sessionid(const ssh_key key,
             break;
         case SSH_KEYTYPE_RSA:
         case SSH_KEYTYPE_RSA1:
-            sig->rsa_sig = _RSA_do_sign(hash, hlen, key->rsa);
+            sig->type_c = ssh_key_alg_to_char(algorithm);
+            sig->rsa_sig = _RSA_do_sign_alg(hash, hlen, key->rsa, algorithm);
             if (sig->rsa_sig == NULL) {
                 ssh_signature_free(sig);
                 return NULL;
diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c
index 7678e5a4..e86e432d 100644
--- a/src/pki_gcrypt.c
+++ b/src/pki_gcrypt.c
@@ -2088,13 +2088,6 @@ int pki_signature_verify(ssh_session session,
     return SSH_OK;
 }
 
-ssh_signature pki_do_sign(const ssh_key privkey,
-                          const unsigned char *hash,
-                          size_t hlen)
-{
-    return pki_do_sign_alg(privkey, hash, hlen, privkey->type);
-}
-
 ssh_signature pki_do_sign_alg(const ssh_key privkey,
                               const unsigned char *hash,
                               size_t hlen,
@@ -2214,20 +2207,28 @@ ssh_signature pki_do_sign_alg(const ssh_key privkey,
 }
 
 #ifdef WITH_SERVER
-ssh_signature pki_do_sign_sessionid(const ssh_key key,
-                                    const unsigned char *hash,
-                                    size_t hlen)
+ssh_signature pki_do_sign_sessionid_alg(const ssh_key key,
+                                        const unsigned char *hash,
+                                        size_t hlen,
+                                        enum ssh_keytypes_e algorithm)
 {
     unsigned char ghash[hlen + 1];
+    const char *hash_type = NULL;
     ssh_signature sig;
     gcry_sexp_t sexp;
     gcry_error_t err;
 
+    /* Only RSA supports different signature algorithm types now */
+    if (key->type != algorithm && key->type != SSH_KEYTYPE_RSA) {
+        return NULL;
+    }
+
     sig = ssh_signature_new();
     if (sig == NULL) {
         return NULL;
     }
-    sig->type = key->type;
+
+    sig->type = algorithm;
     sig->type_c = key->type_c;
 
     switch(key->type) {
@@ -2255,9 +2256,25 @@ ssh_signature pki_do_sign_sessionid(const ssh_key key,
         case SSH_KEYTYPE_RSA:
         case SSH_KEYTYPE_RSA_SHA256:
         case SSH_KEYTYPE_RSA_SHA512:
+            sig->type_c = ssh_key_alg_to_char(algorithm);
+            switch (algorithm) {
+            case SSH_KEYTYPE_RSA:
+                hash_type = "sha1";
+                break;
+            case SSH_KEYTYPE_RSA_SHA256:
+                hash_type = "sha256";
+                break;
+            case SSH_KEYTYPE_RSA_SHA512:
+                hash_type = "sha512";
+                break;
+            default:
+                SSH_LOG(SSH_LOG_WARN, "Incomplatible key algorithm");
+                return NULL;
+            }
             err = gcry_sexp_build(&sexp,
                                   NULL,
-                                  "(data(flags pkcs1)(hash sha1 %b))",
+                                  "(data(flags pkcs1)(hash %s %b))",
+                                  hash_type,
                                   hlen,
                                   hash);
             if (err) {
diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c
index b73c1d60..832d740b 100644
--- a/src/pki_mbedcrypto.c
+++ b/src/pki_mbedcrypto.c
@@ -1047,12 +1047,6 @@ static ssh_string rsa_do_sign_alg(const unsigned char *digest, int dlen,
 }
 
 
-ssh_signature pki_do_sign(const ssh_key privkey, const unsigned char *hash,
-        size_t hlen)
-{
-    return pki_do_sign_alg(privkey, hash, hlen, privkey->type);
-}
-
 ssh_signature pki_do_sign_alg(const ssh_key privkey,
                               const unsigned char *hash,
                               size_t hlen,
@@ -1123,22 +1117,34 @@ ssh_signature pki_do_sign_alg(const ssh_key privkey,
 }
 
 #ifdef WITH_SERVER
-ssh_signature pki_do_sign_sessionid(const ssh_key key, const unsigned char
-        *hash, size_t hlen)
+ssh_signature pki_do_sign_sessionid_alg(const ssh_key key,
+                                        const unsigned char *hash,
+                                        size_t hlen,
+                                        enum ssh_keytypes_e algorithm)
 {
     ssh_signature sig = NULL;
     int rc;
 
+    /* Only RSA supports different signature algorithm types now */
+    if (key->type != algorithm && key->type != SSH_KEYTYPE_RSA) {
+        SSH_LOG(SSH_LOG_WARN, "Incompatible signature algorithm passed");
+        return NULL;
+    }
+
     sig = ssh_signature_new();
     if (sig == NULL) {
         return NULL;
     }
-    sig->type = key->type;
+
+    sig->type = algorithm;
     sig->type_c = key->type_c;
 
     switch (key->type) {
         case SSH_KEYTYPE_RSA:
-            sig->rsa_sig = rsa_do_sign_alg(hash, hlen, key->rsa, key->type);
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
+            sig->type_c = ssh_key_alg_to_char(algorithm);
+            sig->rsa_sig = rsa_do_sign_alg(hash, hlen, key->rsa, algorithm);
             if (sig->rsa_sig == NULL) {
                 ssh_signature_free(sig);
                 return NULL;
-- 
2.17.1


From 6a3b665f79e3e7ae0890f847b1fd78a16eff0c5e Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 16 Jul 2018 09:29:34 +0200
Subject: [PATCH 19/19] pkd: Enable the SHA2 extension in server tests

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 tests/pkd/pkd_client.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/pkd/pkd_client.h b/tests/pkd/pkd_client.h
index da40a7c5..4d01a607 100644
--- a/tests/pkd/pkd_client.h
+++ b/tests/pkd/pkd_client.h
@@ -15,8 +15,8 @@
 #define OPENSSH_BINARY "ssh"
 #define OPENSSH_KEYGEN "ssh-keygen"
 
-#define OPENSSH_HOSTKEY_ALGOS_DEFAULT "ssh-ed25519,ssh-rsa"
-#define OPENSSH_PKACCEPTED_DEFAULT    "ssh-ed25519,ssh-rsa"
+#define OPENSSH_HOSTKEY_ALGOS_DEFAULT "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa"
+#define OPENSSH_PKACCEPTED_DEFAULT    "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa"
 
 #if       HAVE_ECC
 #define OPENSSH_HOSTKEY_ALGOS_ECDSA   ",ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521"
-- 
2.17.1


Archive administrator: postmaster@lists.cynapses.org