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

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


Hello,
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.

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].

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).

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.

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?

I tested against current OpenSSH 7.7p1 in Fedora and with all of the
openssl, libgcrypt and mbedtls and all the tests are passing.

[1] https://tools.ietf.org/html/rfc8332#section-2

-- 
Jakub Jelen
Software Engineer
Security Technologies
Red Hat, Inc.
From 7211a2f542e87e5f8849e454bd3a4d3839eeab6c Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Thu, 21 Jun 2018 14:22:59 +0200
Subject: [PATCH 1/9] 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 5a554647..3a92a1c2 100644
--- a/src/client.c
+++ b/src/client.c
@@ -710,6 +710,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);
@@ -725,6 +727,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 66bb369320799e03d00b5df3f7a7822b3a172afc Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Thu, 21 Jun 2018 14:26:37 +0200
Subject: [PATCH 2/9] 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 b66e3d22..b5f3106c 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -185,7 +185,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,
                         blocksize);
 #endif
-- 
2.17.1


From 80ffa0a07f66c06b38dde035497dfea215ba28c5 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 25 Jun 2018 11:20:31 +0200
Subject: [PATCH 3/9] Avoid warnings about unused functions

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

diff --git a/tests/unittests/torture_pki_ecdsa.c b/tests/unittests/torture_pki_ecdsa.c
index 6587a60d..e9939d56 100644
--- a/tests/unittests/torture_pki_ecdsa.c
+++ b/tests/unittests/torture_pki_ecdsa.c
@@ -338,6 +338,7 @@ static void torture_pki_generate_key_ecdsa(void **state)
     ssh_free(session);
 }
 
+#ifdef HAVE_LIBCRYPTO
 static void torture_pki_ecdsa_write_privkey(void **state)
 {
     ssh_key origkey;
@@ -412,6 +413,7 @@ static void torture_pki_ecdsa_write_privkey(void **state)
     ssh_key_free(origkey);
     ssh_key_free(privkey);
 }
+#endif /* HAVE_LIBCRYPTO */
 
 static void torture_pki_ecdsa_name(void **state, const char *expected_name)
 {
-- 
2.17.1


From 036644ff029b173b692e0aeb006261d5f2fb90cc Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 26 Jun 2018 10:58:03 +0200
Subject: [PATCH 4/9] 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            | 30 ++++++++++++-
 src/pki_mbedcrypto.c        | 26 ++++++++++-
 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 03241493..2ab37562 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 3a84eb70..e9743f4a 100644
--- a/include/libssh/packet.h
+++ b/include/libssh/packet.h
@@ -62,6 +62,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 1a069017..50cb8284 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 b658ed44..13c9756c 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
@@ -98,6 +98,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,
@@ -619,7 +622,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);
 
@@ -636,7 +640,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;
 }
@@ -647,8 +663,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 b5f3106c..aaababd5 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 fc676257..04a00427 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -268,3 +268,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 d2e6f61c..e51bf184 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -203,6 +203,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_RSA1:
       return "ssh-rsa1";
@@ -222,6 +224,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.
  *
@@ -242,7 +296,9 @@ enum ssh_keytypes_e ssh_key_type_from_name(const char *name) {
         return SSH_KEYTYPE_DSS;
     } else if (strcmp(name, "ssh-rsa1") == 0) {
         return SSH_KEYTYPE_RSA1;
-    } 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;
@@ -359,6 +415,8 @@ void ssh_signature_free(ssh_signature sig)
 #endif
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         case SSH_KEYTYPE_RSA1:
 #ifdef HAVE_LIBGCRYPT
             gcry_sexp_release(sig->rsa_sig);
@@ -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 5723d823..e8ae29ae 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_RSA1:
     case SSH_KEYTYPE_ECDSA:
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index 91e534e1..49df972d 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) {
@@ -1318,6 +1320,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;
@@ -1465,7 +1469,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);
 
@@ -1527,6 +1531,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;
@@ -1639,6 +1645,7 @@ int pki_signature_verify(ssh_session session,
                          size_t hlen)
 {
     int rc;
+    int nid;
 
     switch(key->type) {
         case SSH_KEYTYPE_DSS:
@@ -1656,13 +1663,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",
@@ -1696,6 +1723,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 713c035f..473a494e 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:
         case SSH_KEYTYPE_RSA1:
             if (_bignum_cmp(k1->rsa, k2->rsa, "e") != 0) {
                 return 1;
@@ -1721,6 +1723,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:
         case SSH_KEYTYPE_RSA1:
             sexp = gcry_sexp_find_token(sig->rsa_sig, "s", 0);
             if (sexp == NULL) {
@@ -1856,6 +1860,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:
             rsalen = (gcry_pk_get_nbits(pubkey->rsa) + 7) / 8;
 
@@ -1987,6 +1993,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;
 
@@ -2022,10 +2029,29 @@ int pki_signature_verify(ssh_session session,
             break;
         case SSH_KEYTYPE_RSA:
         case SSH_KEYTYPE_RSA1:
+            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 975dae67..1f30de16 100644
--- a/src/pki_mbedcrypto.c
+++ b/src/pki_mbedcrypto.c
@@ -744,6 +744,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:
         case SSH_KEYTYPE_RSA1:
             sig_blob = ssh_string_copy(sig->rsa_sig);
             break;
@@ -879,6 +881,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:
         case SSH_KEYTYPE_RSA1:
             sig = pki_signature_from_rsa_blob(pubkey, sig_blob, sig);
             break;
@@ -969,11 +973,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:
         case SSH_KEYTYPE_RSA1:
-            rc = mbedtls_pk_verify(key->rsa, MBEDTLS_MD_SHA1, hash, hlen,
+            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 a52fcdac..ba519ab9 100644
--- a/tests/torture_key.c
+++ b/tests/torture_key.c
@@ -319,6 +319,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 d57367c4fc36cc76d2618e70593ed04727b93bf6 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Wed, 27 Jun 2018 13:48:04 +0200
Subject: [PATCH 5/9] 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 13c9756c..2ff31b46 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -640,7 +640,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 accd678a710f3b741020f6cce3e39fff1f701182 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 26 Jun 2018 12:24:14 +0200
Subject: [PATCH 6/9] 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 6cbd12a9b3d58718e5ebb19fc7531f95499c9346 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 26 Jun 2018 11:17:10 +0200
Subject: [PATCH 7/9] 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 2ab37562..17a465ea 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 9a8857dc..171e0c29 100644
--- a/include/libssh/pki_priv.h
+++ b/include/libssh/pki_priv.h
@@ -100,6 +100,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 69f69141..88e218e3 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -443,6 +443,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) {
@@ -484,6 +486,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",
@@ -492,7 +496,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) {
@@ -553,7 +557,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;
@@ -591,7 +595,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);
@@ -655,6 +660,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) {
@@ -682,6 +689,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",
@@ -690,7 +699,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 e51bf184..e3956813 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -191,6 +191,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 49df972d..e29ac371 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)) {
@@ -1224,23 +1226,43 @@ int pki_export_pubkey_rsa1(const ssh_key key,
  *
  * @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;
@@ -1259,6 +1281,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 };
@@ -1733,17 +1775,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:
@@ -1765,7 +1822,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 473a494e..57e33772 100644
--- a/src/pki_gcrypt.c
+++ b/src/pki_gcrypt.c
@@ -2119,18 +2119,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 */
@@ -2155,10 +2170,28 @@ ssh_signature pki_do_sign(const ssh_key privkey,
             }
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         case SSH_KEYTYPE_RSA1:
+            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) {
@@ -2249,6 +2282,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:
         case SSH_KEYTYPE_RSA1:
             err = gcry_sexp_build(&sexp,
                                   NULL,
diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c
index 1f30de16..177154d1 100644
--- a/src/pki_mbedcrypto.c
+++ b/src/pki_mbedcrypto.c
@@ -1034,20 +1034,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) {
@@ -1071,22 +1088,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_c = privkey->type_c;
+    sig->type = algorithm;
+    sig->type_c = ssh_key_alg_to_char(algorithm);
 
     switch(privkey->type) {
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         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;
@@ -1145,7 +1178,7 @@ ssh_signature pki_do_sign_sessionid(const ssh_key key, const unsigned char
     switch (key->type) {
         case SSH_KEYTYPE_RSA:
         case SSH_KEYTYPE_RSA1:
-            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 41e315d0ff5b13c6526804aad914a73f20f54d79 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Wed, 27 Jun 2018 15:19:02 +0200
Subject: [PATCH 8/9] 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 | 43 +++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/tests/unittests/torture_pki_rsa.c b/tests/unittests/torture_pki_rsa.c
index 54e459f9..151fb1d6 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)
 {
@@ -376,6 +379,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-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;
@@ -581,6 +623,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 d5f41a8ffc6c59c179e5828809558a85410012f7 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 26 Jun 2018 12:22:31 +0200
Subject: [PATCH 9/9] 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 c7392732..bab94b8a 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 0b145ff3..c60fd1eb 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -555,6 +555,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


Follow-Ups:
Re: (Client side) RSA signatures with SHA2 (RFC 8332 and RFC 8308)Andreas Schneider <asn@xxxxxxxxxxxxxx>
Archive administrator: postmaster@lists.cynapses.org