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

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


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 bd02af7619281f5d641e894abed9819a2b56d4b7 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Thu, 21 Jun 2018 14:22:59 +0200
Subject: [PATCH 1/8] Properly reset session structures on disconnect

torture_connect_double  test case used to test the connect only up
to key exchange phase, but not after the new keys are stated to be
used for communication. The keys from previous connectoin were not
cleaned up from the previous invocation as well as the seqence
number was not reset and therefore any further packet was failing
with length-check errors or MAC errors.

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

diff --git a/src/client.c b/src/client.c
index 66aad23e..59dc1ee5 100644
--- a/src/client.c
+++ b/src/client.c
@@ -666,6 +666,8 @@ void ssh_disconnect(ssh_session session) {
     ssh_socket_close(session->socket);
   }
 error:
+  session->recv_seq = 0;
+  session->send_seq = 0;
   session->alive = 0;
   if (session->socket != NULL){
     ssh_socket_reset(session->socket);
@@ -681,6 +683,13 @@ error:
     crypto_free(session->current_crypto);
     session->current_crypto=NULL;
   }
+  if (session->next_crypto) {
+    crypto_free(session->next_crypto);
+    session->next_crypto = crypto_new();
+    if (session->next_crypto == NULL) {
+      ssh_set_error_oom(session);
+    }
+  }
   if (session->in_buffer) {
     ssh_buffer_reinit(session->in_buffer);
   }
-- 
2.17.1


From 025fe05793acdb4a1d0aff185506a5f5dbdd60c9 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Thu, 21 Jun 2018 14:26:37 +0200
Subject: [PATCH 2/8] Avoid warnings by using correct template

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

diff --git a/src/packet.c b/src/packet.c
index be7c5fb7..62250e4d 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -191,7 +191,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
                  */
 #ifdef DEBUG_PACKET
                 SSH_LOG(SSH_LOG_PACKET,
-                        "Waiting for more data (%zu < %zu)",
+                        "Waiting for more data (%zu < %u)",
                         receivedlen,
                         lenfield_blocksize);
 #endif
-- 
2.17.1


From 74ad74fcc8c25cc2f223bb26ef0e95fe5f0997ca Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 26 Jun 2018 10:58:03 +0200
Subject: [PATCH 3/8] 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                   | 32 ++++++++++++--
 src/packet.c                |  5 ++-
 src/packet_cb.c             | 56 +++++++++++++++++++++++
 src/pki.c                   | 88 ++++++++++++++++++++++++++++++++++---
 src/pki_container_openssh.c |  2 +
 src/pki_crypto.c            | 32 +++++++++++++-
 src/pki_gcrypt.c            | 32 +++++++++++++-
 src/pki_mbedcrypto.c        | 28 +++++++++++-
 tests/torture_key.c         |  2 +
 13 files changed, 275 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 3aedb218..84c662e1 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -85,12 +85,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
@@ -100,6 +100,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,
@@ -620,7 +623,8 @@ 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;
-    int i;
+    char *kex;
+    int i, kex_len;
 
     ssh_get_random(client->cookie, 16, 0);
 
@@ -637,7 +641,19 @@ int ssh_set_client_kex(ssh_session session){
         if (wanted == NULL)
             wanted = default_methods[i];
         client->methods[i] = strdup(wanted);
+        /* TODO check allocation result */
+    }
+
+    /* 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;
 }
@@ -648,8 +664,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 62250e4d..17960f37 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 2009d752..dd1d92fd 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -269,3 +269,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 6500648e..32cea7b1 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
@@ -1523,7 +1581,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);
@@ -1582,22 +1640,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 22fccbc6..50ae08f7 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..9310dfda 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,33 @@ 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:
+            case SSH_KEYTYPE_RSA1:
+                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 +1692,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 0a9bea57..ed324920 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,31 @@ 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:
+            case SSH_KEYTYPE_RSA1:
+                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 a850905d..ae97c31e 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,32 @@ 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:
+            case SSH_KEYTYPE_RSA1:
+                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 b0cfe14ec4af3ae9e591bef5e632e8f104a0fdb7 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Wed, 27 Jun 2018 13:48:04 +0200
Subject: [PATCH 4/8] kex: Check allocation result

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

diff --git a/src/kex.c b/src/kex.c
index 84c662e1..0dc19b46 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -641,7 +641,10 @@ int ssh_set_client_kex(ssh_session session){
         if (wanted == NULL)
             wanted = default_methods[i];
         client->methods[i] = strdup(wanted);
-        /* TODO check allocation result */
+        if (client->methods[i] == NULL) {
+            ssh_set_error_oom(session);
+            return SSH_ERROR;
+        }
     }
 
     /* Here we append  ext-info-c  to the list of kex algorithms */
-- 
2.17.1


From 4193e427b8088467bde21484032127d548771165 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 26 Jun 2018 12:24:14 +0200
Subject: [PATCH 5/8] tests: Verify various host keys can be successfully
 negotiated and verified

This verifies that all the supported host keys can be used and
verified by the client, including the SHA2 extension in RFC 8332.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 tests/client/CMakeLists.txt    |   1 +
 tests/client/torture_hostkey.c | 216 +++++++++++++++++++++++++++++++++
 2 files changed, 217 insertions(+)
 create mode 100644 tests/client/torture_hostkey.c

diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt
index ca41eeec..d07c9700 100644
--- a/tests/client/CMakeLists.txt
+++ b/tests/client/CMakeLists.txt
@@ -5,6 +5,7 @@ find_package(socket_wrapper)
 set(LIBSSH_CLIENT_TESTS
     torture_algorithms
     torture_connect
+    torture_hostkey
     torture_auth
     torture_forward
     torture_knownhosts
diff --git a/tests/client/torture_hostkey.c b/tests/client/torture_hostkey.c
new file mode 100644
index 00000000..1926b9c4
--- /dev/null
+++ b/tests/client/torture_hostkey.c
@@ -0,0 +1,216 @@
+/*
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2018 by Red Hat, Inc.
+ *
+ * Author: Jakub Jelen <jjelen@xxxxxxxxxx>
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#define LIBSSH_STATIC
+
+#include "torture.h"
+#include <libssh/libssh.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif /* HAVE_SYS_TIME_H */
+#include <errno.h>
+#include <pwd.h>
+
+static int sshd_setup(void **state)
+{
+    torture_setup_sshd_server(state);
+
+    return 0;
+}
+
+static int sshd_teardown(void **state) {
+    torture_teardown_sshd_server(state);
+
+    return 0;
+}
+
+static int session_setup(void **state)
+{
+    struct torture_state *s = *state;
+    int verbosity = torture_libssh_verbosity();
+    struct passwd *pwd;
+    int rc;
+
+    pwd = getpwnam("bob");
+    assert_non_null(pwd);
+
+    rc = setuid(pwd->pw_uid);
+    assert_return_code(rc, errno);
+
+    s->ssh.session = ssh_new();
+    assert_non_null(s->ssh.session);
+
+    ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
+    ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
+
+    return 0;
+}
+
+static int session_teardown(void **state)
+{
+    struct torture_state *s = *state;
+
+    ssh_disconnect(s->ssh.session);
+    ssh_free(s->ssh.session);
+
+    return 0;
+}
+
+static void torture_hostkey_rsa(void **state) {
+    struct torture_state *s = *state;
+    ssh_session session = s->ssh.session;
+    char rsa[] = "ssh-rsa";
+
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, &rsa);
+    assert_true(rc == SSH_OK);
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+    ssh_disconnect(session);
+
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+}
+
+static void torture_hostkey_ed25519(void **state) {
+    struct torture_state *s = *state;
+    ssh_session session = s->ssh.session;
+    char ed[] = "ssh-ed25519";
+
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, &ed);
+    assert_true(rc == SSH_OK);
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+    ssh_disconnect(session);
+
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+}
+
+#ifdef HAVE_DSA
+static void torture_hostkey_dss(void **state) {
+    struct torture_state *s = *state;
+    ssh_session session = s->ssh.session;
+    char rsa[] = "ssh-dss";
+
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, &rsa);
+    assert_true(rc == SSH_OK);
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+    ssh_disconnect(session);
+
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+}
+#endif /* HAVE_DSA */
+
+#ifdef HAVE_ECC
+static void torture_hostkey_ecdsa(void **state) {
+    struct torture_state *s = *state;
+    ssh_session session = s->ssh.session;
+    char ecdsa[] = "ecdsa-sha2-nistp521";
+
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, &ecdsa);
+    assert_true(rc == SSH_OK);
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+    ssh_disconnect(session);
+
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+}
+#endif
+
+static void torture_hostkey_rsa_sha256(void **state) {
+    struct torture_state *s = *state;
+    ssh_session session = s->ssh.session;
+    char rsa[] = "rsa-sha2-256,ssh-rsa";
+
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, &rsa);
+    assert_true(rc == SSH_OK);
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+    ssh_disconnect(session);
+
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+}
+
+static void torture_hostkey_rsa_sha512(void **state) {
+    struct torture_state *s = *state;
+    ssh_session session = s->ssh.session;
+    char rsa[] = "rsa-sha2-512,ssh-rsa";
+
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, &rsa);
+    assert_true(rc == SSH_OK);
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+    ssh_disconnect(session);
+
+    rc = ssh_connect(session);
+    assert_true(rc == SSH_OK);
+}
+
+int torture_run_tests(void) {
+    int rc;
+    struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(torture_hostkey_rsa, session_setup,
+                                        session_teardown),
+        cmocka_unit_test_setup_teardown(torture_hostkey_ed25519, session_setup,
+                                        session_teardown),
+#ifdef HAVE_ECC
+        cmocka_unit_test_setup_teardown(torture_hostkey_ecdsa, session_setup,
+                                        session_teardown),
+#endif
+#ifdef HAVE_DSA
+        cmocka_unit_test_setup_teardown(torture_hostkey_dss, session_setup,
+                                        session_teardown),
+#endif
+        /* the client is able to handle SHA2 extension (if negotiated) */
+        cmocka_unit_test_setup_teardown(torture_hostkey_rsa_sha256,
+                                        session_setup, session_teardown),
+        cmocka_unit_test_setup_teardown(torture_hostkey_rsa_sha512,
+                                        session_setup, session_teardown),
+    };
+
+    ssh_init();
+
+    torture_filter_tests(tests);
+    rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
+
+    ssh_finalize();
+    return rc;
+}
-- 
2.17.1


From 3028c5613a3f7f1911f840a3086b3cbfef4dbbea Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 26 Jun 2018 11:17:10 +0200
Subject: [PATCH 6/8] 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.

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          | 73 ++++++++++++++++++++++++++++++++++-----
 src/pki_gcrypt.c          | 43 ++++++++++++++++++++---
 src/pki_mbedcrypto.c      | 47 +++++++++++++++++++++----
 7 files changed, 223 insertions(+), 32 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 32cea7b1..c3af3161 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.
  *
@@ -1752,24 +1773,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 9310dfda..7c0fb136 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,43 @@ 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:
+    case SSH_KEYTYPE_RSA1:
+        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 +1250,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 };
@@ -1702,17 +1744,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)
+{
     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_c = privkey->type_c;
+    sig->type = algorithm;
+    sig->type_c = ssh_key_alg_to_char(algorithm);
 
     switch(privkey->type) {
         case SSH_KEYTYPE_DSS:
@@ -1734,7 +1791,7 @@ 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->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 ed324920..b0ce04a9 100644
--- a/src/pki_gcrypt.c
+++ b/src/pki_gcrypt.c
@@ -2091,18 +2091,33 @@ 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_c = privkey->type_c;
+    sig->type = algorithm;
+    sig->type_c = ssh_key_alg_to_char(algorithm);
     switch (privkey->type) {
         case SSH_KEYTYPE_DSS:
             /* That is to mark the number as positive */
@@ -2127,9 +2142,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:
+            switch (algorithm) {
+            case SSH_KEYTYPE_RSA:
+            case SSH_KEYTYPE_RSA1:
+                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) {
@@ -2221,6 +2254,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 ae97c31e..4c02b5f4 100644
--- a/src/pki_mbedcrypto.c
+++ b/src/pki_mbedcrypto.c
@@ -997,20 +997,37 @@ 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:
+    case SSH_KEYTYPE_RSA1:
+        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) {
@@ -1034,21 +1051,37 @@ 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_c = privkey->type_c;
+    sig->type = algorithm;
+    sig->type_c = ssh_key_alg_to_char(algorithm);
 
     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->rsa_sig = rsa_do_sign_alg(hash, hlen, privkey->rsa, algorithm);
             if (sig->rsa_sig == NULL) {
                 ssh_signature_free(sig);
                 return NULL;
@@ -1106,7 +1139,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 d014a191acd9c6ef3d044f3f67e89cd6c5cf5f9b Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Wed, 27 Jun 2018 15:19:02 +0200
Subject: [PATCH 7/8] tests: SHA2 extension signatures

This intoruces a new test case for RSA unit test, 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 | 87 +++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/tests/unittests/torture_pki_rsa.c b/tests/unittests/torture_pki_rsa.c
index c265f88c..eef6dbcb 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,89 @@ 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-256 algorithm */
+    sign = pki_do_sign_alg(key, SHA512_HASH, 64, SSH_KEYTYPE_RSA_SHA256);
+    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);
+}
+
+static void torture_pki_rsa_generate_key1(void **state)
+{
+    int rc;
+    ssh_key key;
+    ssh_signature sign;
+    ssh_session session=ssh_new();
+    (void) state;
+
+    rc = ssh_pki_generate(SSH_KEYTYPE_RSA1, 1024, &key);
+    assert_true(rc == SSH_OK);
+    assert_true(key != NULL);
+    sign = pki_do_sign(key, RSA_HASH, 20);
+    assert_true(sign != NULL);
+    rc = pki_signature_verify(session,sign,key,RSA_HASH,20);
+    assert_true(rc == SSH_OK);
+    ssh_signature_free(sign);
+    ssh_key_free(key);
+    key=NULL;
+
+    rc = ssh_pki_generate(SSH_KEYTYPE_RSA1, 2048, &key);
+    assert_true(rc == SSH_OK);
+    assert_true(key != NULL);
+    sign = pki_do_sign(key, RSA_HASH, 20);
+    assert_true(sign != NULL);
+    rc = pki_signature_verify(session,sign,key,RSA_HASH,20);
+    assert_true(rc == SSH_OK);
+    ssh_signature_free(sign);
+    ssh_key_free(key);
+    key=NULL;
+
+    rc = ssh_pki_generate(SSH_KEYTYPE_RSA1, 4096, &key);
+    assert_true(rc == SSH_OK);
+    assert_true(key != NULL);
+    sign = pki_do_sign(key, RSA_HASH, 20);
+    assert_true(sign != NULL);
+    rc = pki_signature_verify(session,sign,key,RSA_HASH,20);
+    assert_true(rc == SSH_OK);
+    ssh_signature_free(sign);
+    ssh_key_free(key);
+    key=NULL;
+
+    ssh_free(session);
+}
+
 #ifdef HAVE_LIBCRYPTO
 static void torture_pki_rsa_write_privkey(void **state)
 {
@@ -555,6 +641,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 35fb5b377c413e27f8ac8bb3c274661aa8fae991 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 26 Jun 2018 12:22:31 +0200
Subject: [PATCH 8/8] 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

diff --git a/tests/client/torture_connect.c b/tests/client/torture_connect.c
index 0d445974..9216c1e5 100644
--- a/tests/client/torture_connect.c
+++ b/tests/client/torture_connect.c
@@ -139,10 +139,21 @@ static void torture_connect_double(void **state) {
     assert_true(rc == SSH_OK);
     rc = ssh_connect(session);
     assert_true(rc == SSH_OK);
+
+    /* send some data in the encrypted channel to use the keys */
+    ssh_send_ignore(session, "");
+
     ssh_disconnect(session);
 
     rc = ssh_connect(session);
     assert_true(rc == SSH_OK);
+
+    /* send some data in the encrypted channel to use the new keys */
+    ssh_send_ignore(session, "");
+    /* If this fails, the cryptographic structures of the session were
+     * not properly cleaned up after disconnect
+     */
+    assert_true(ssh_is_connected(session));
 }
 
 static void torture_connect_failure(void **state) {

Archive administrator: postmaster@lists.cynapses.org