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

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


On Thu, 2018-08-02 at 17:25 +0200, Andreas Schneider wrote:
> On Wednesday, 1 August 2018 18:26:34 CEST Andreas Schneider wrote:
> > On Thursday, 26 July 2018 17:45:12 CEST Jakub Jelen wrote:
> > > Hello,
> > 
> > Hi Jakub,
> > 
> > > the current patch-set provides also the server side
> > > implementation of
> > > the SHA2 extension, which is tested with current tests against
> > > OpenSSH.
> > > 
> > > There are few partially related changes, such as follow up on
> > > SSH1
> > > removal and support for PubkeyAcceptedTypes as discussed before.
> > > 
> > > While testing server, I noticed some issues with incomplete
> > > support for
> > > the ed25519 keys so this is also in.
> > 
> > I've already pushed the fixes, however the rsa sha2 stuff needs
> > some more
> > love. The individual commits should compile at least.

I am sorry. The commits got somehow mixed up while I was rebasing them.

> > See the TODO commit which should be squashed, once squashed you
> > need to add
> > the missing functions. You can find the code here:
> > 
> > https://git.libssh.org/users/asn/libssh.git/log/?h=master-fix
> 
> I've worked a bit on this and I'm not happy with the pki stuff. The
> first 
> patch:
> 
> extension negotiation with SHA2 extension for RSA keys
> 
> is too big. I don't like how the pki stuff is implemented. When we
> add new 
> functions there we need tests for it.
> 
> I think the rsa keys should be handled like ecdsa ->
> ssh_pki_key_ecdsa_name() 
> for example.

From my reading of the RFCs, there are physically no new key types, but
only signature algorithm types, which makes it different from the ECDSA
keys.

> Let me know as soon as you're back and we can have a phone call over
> this.

I mostly broke the patches to smaller chunks, making sure each of them
compiles and passes all the tests. I fixing few minor issues I hit on
the way.

Ping me if you would like to discuss some other aspects.

The last thing that might need some care is the code duplication in
pki_do_sign_sessionid(_alg) and pki_do_sign(_alg). These functions are
same in all backends, except for the ed25519 code. If there is no good
reason to keep both of them, I would be for merging them before the 0.8
release, but I did not want to complicate the SHA2 stuff with it now.

Thanks,
-- 
Jakub Jelen
Software Engineer
Security Technologies
Red Hat, Inc.
From 7e24eceb71b4bb3e1876da3f75040f0540461072 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 6 Aug 2018 11:47:32 +0200
Subject: [PATCH 01/20] kex: Signalize support for the extension negotiation in
 client (RFC 8308)

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

diff --git a/src/kex.c b/src/kex.c
index 6ac59ec8..aa6fe4fc 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -101,6 +101,9 @@
 #define KEY_EXCHANGE CURVE25519 ECDH "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
 #define KEX_METHODS_SIZE 10
 
+/* RFC 8308 */
+#define KEX_EXTENSION_CLIENT "ext-info-c"
+
 /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */
 static const char *default_methods[] = {
   KEY_EXCHANGE,
@@ -642,11 +645,14 @@ static char *ssh_client_select_hostkeys(ssh_session session)
  * @brief sets the key exchange parameters to be sent to the server,
  *        in function of the options and available methods.
  */
-int ssh_set_client_kex(ssh_session session){
+int ssh_set_client_kex(ssh_session session)
+{
     struct ssh_kex_struct *client= &session->next_crypto->client_kex;
     const char *wanted;
+    char *kex = NULL;
+    char *kex_tmp = NULL;
     int ok;
-    int i;
+    int i, kex_len;
 
     ok = ssh_get_random(client->cookie, 16, 0);
     if (!ok) {
@@ -673,6 +679,18 @@ int ssh_set_client_kex(ssh_session session){
         }
     }
 
+    /* Here we append  ext-info-c  to the list of kex algorithms */
+    kex = client->methods[SSH_KEX];
+    kex_len = strlen(kex) + strlen(KEX_EXTENSION_CLIENT) + 2; /* comma, NULL */
+    kex_tmp = realloc(kex, kex_len);
+    if (kex_tmp == NULL) {
+        free(kex);
+        ssh_set_error_oom(session);
+        return SSH_ERROR;
+    }
+    strcat(kex_tmp, ","KEX_EXTENSION_CLIENT);
+    client->methods[SSH_KEX] = kex_tmp;
+
     return SSH_OK;
 }
 
@@ -682,8 +700,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){
-- 
2.17.1


From 223cd8479512084594fcad085098cb2703a8fc75 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 6 Aug 2018 11:51:26 +0200
Subject: [PATCH 02/20] client: Handle the MSG_EXT_INFO packet signalling
 supported extensions

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

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

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/packet.h  |  1 +
 include/libssh/session.h |  8 ++++++
 include/libssh/ssh2.h    |  1 +
 src/packet.c             |  5 ++--
 src/packet_cb.c          | 62 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 75 insertions(+), 2 deletions(-)

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..875f0754 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 */
+    uint32_t 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/packet.c b/src/packet.c
index 16f96149..5b9252f5 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -59,8 +59,9 @@ static ssh_packet_callback default_packet_handlers[]= {
   NULL,
 #endif
   ssh_packet_service_accept,               // SSH2_MSG_SERVICE_ACCEPT             6
-  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-  NULL, NULL, NULL,	NULL, NULL, NULL,      //                                     7-19
+  ssh_packet_ext_info,                     // SSH2_MSG_EXT_INFO                   7
+  NULL, NULL, NULL, NULL, NULL, NULL,
+  NULL, NULL, NULL, NULL, NULL, NULL,      //                                     8-19
   ssh_packet_kexinit,                      // SSH2_MSG_KEXINIT	                  20
   ssh_packet_newkeys,                      // SSH2_MSG_NEWKEYS                    21
   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
diff --git a/src/packet_cb.c b/src/packet_cb.c
index 2c8d9935..4153fff2 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -270,3 +270,65 @@ 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 nr_extensions = 0;
+    uint32_t i;
+    (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 = NULL;
+        ssh_string ext_value = NULL;
+        const char *name = NULL;
+        const char *value = NULL;
+        int cmp;
+
+        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);
+        cmp = strcmp(name, "server-sig-algs");
+        if (cmp == 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;
+}
-- 
2.17.1


From f7f584c1e733737412145dbac9eec56df6ea4e37 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 7 Aug 2018 12:17:29 +0200
Subject: [PATCH 03/20] pki: Support RSA verification using different hash
 algorithms

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

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/libssh.h     |  6 ++-
 src/pki.c                   | 79 ++++++++++++++++++++++++++++++++++---
 src/pki_container_openssh.c |  2 +
 src/pki_crypto.c            | 31 ++++++++++++++-
 src/pki_gcrypt.c            | 31 ++++++++++++++-
 src/pki_mbedcrypto.c        | 25 +++++++++++-
 tests/torture_key.c         |  2 +
 7 files changed, 164 insertions(+), 12 deletions(-)

diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index e78b8a27..7134f26a 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_algorithm_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/src/pki.c b/src/pki.c
index b8731730..2d11fac9 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_algorithm_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.
  *
@@ -215,6 +236,8 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
     case SSH_KEYTYPE_RSA_CERT01:
       return "ssh-rsa-cert-v01@xxxxxxxxxxx";
     case SSH_KEYTYPE_RSA1:
+    case SSH_KEYTYPE_RSA_SHA256:
+    case SSH_KEYTYPE_RSA_SHA512:
     case SSH_KEYTYPE_UNKNOWN:
       return NULL;
   }
@@ -223,6 +246,28 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
   return NULL;
 }
 
+/**
+ * @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 signature algorithm matches the key type */
+    return ssh_key_type_from_name(name);
+}
+
 /**
  * @brief Convert a ssh key name to a ssh key type.
  *
@@ -239,7 +284,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 +403,8 @@ void ssh_signature_free(ssh_signature sig)
 #endif
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
 #ifdef HAVE_LIBGCRYPT
             gcry_sexp_release(sig->rsa_sig);
 #elif defined HAVE_LIBCRYPTO
@@ -1534,7 +1583,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 +1642,40 @@ int ssh_pki_signature_verify_blob(ssh_session session,
     } else if (key->type == SSH_KEYTYPE_ED25519) {
         rc = pki_signature_verify(session, sig, key, digest, dlen);
     } else {
-        unsigned char hash[SHA_DIGEST_LEN] = {0};
+        unsigned char hash[SHA512_DIGEST_LEN] = {0};
+        uint32_t hlen = 0;
 
-        sha1(digest, dlen, hash);
+        switch (sig->type) {
+        case SSH_KEYTYPE_RSA_SHA256:
+            sha256(digest, dlen, hash);
+            hlen = SHA256_DIGEST_LEN;
+            break;
+        case SSH_KEYTYPE_RSA_SHA512:
+            sha512(digest, dlen, hash);
+            hlen = SHA512_DIGEST_LEN;
+            break;
+        case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_DSS:
+            sha1(digest, dlen, hash);
+            hlen = SHA_DIGEST_LEN;
+            break;
+        default:
+            SSH_LOG(SSH_LOG_TRACE, "Unknown sig->type: %d", sig->type);
+            return SSH_ERROR;
+        }
 #ifdef DEBUG_CRYPTO
         ssh_print_hexa(key->type == SSH_KEYTYPE_DSS
                        ? "Hash to be verified with DSA"
                        : "Hash to be verified with RSA",
                        hash,
-                       SHA_DIGEST_LEN);
+                       hlen);
 #endif
 
         rc = pki_signature_verify(session,
                                   sig,
                                   key,
                                   hash,
-                                  SHA_DIGEST_LEN);
+                                  hlen);
     }
 
     ssh_signature_free(sig);
diff --git a/src/pki_container_openssh.c b/src/pki_container_openssh.c
index 53e1e7fe..16d235e9 100644
--- a/src/pki_container_openssh.c
+++ b/src/pki_container_openssh.c
@@ -124,6 +124,8 @@ static int pki_openssh_import_privkey_blob(ssh_buffer key_blob_buffer,
         SSH_LOG(SSH_LOG_WARN, "Unsupported private key method %s", key->type_c);
         goto fail;
     case SSH_KEYTYPE_RSA1:
+    case SSH_KEYTYPE_RSA_SHA256:
+    case SSH_KEYTYPE_RSA_SHA512:
     case SSH_KEYTYPE_UNKNOWN:
         SSH_LOG(SSH_LOG_WARN, "Unknown private key protocol %s", key->type_c);
         goto fail;
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index 7494b162..29f12217 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -851,6 +851,8 @@ ssh_key pki_private_key_from_base64(const char *b64_key,
             /* Cannot open ed25519 keys with libcrypto */
         case SSH_KEYTYPE_DSS_CERT01:
         case SSH_KEYTYPE_RSA_CERT01:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         case SSH_KEYTYPE_UNKNOWN:
             BIO_free(mem);
             SSH_LOG(SSH_LOG_WARN, "Unkown or invalid private key type %d", type);
@@ -1277,6 +1279,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;
@@ -1424,7 +1428,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_algorithm_to_char(type);
 
     len = ssh_string_len(sig_blob);
 
@@ -1486,6 +1490,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;
@@ -1598,6 +1604,7 @@ int pki_signature_verify(ssh_session session,
                          size_t hlen)
 {
     int rc;
+    int nid;
 
     switch(key->type) {
         case SSH_KEYTYPE_DSS:
@@ -1615,13 +1622,32 @@ int pki_signature_verify(ssh_session session,
             break;
         case SSH_KEYTYPE_RSA:
         case SSH_KEYTYPE_RSA1:
-            rc = RSA_verify(NID_sha1,
+            switch (sig->type) {
+            case SSH_KEYTYPE_RSA:
+                nid = NID_sha1;
+                break;
+            case SSH_KEYTYPE_RSA_SHA256:
+                nid = NID_sha256;
+                break;
+            case SSH_KEYTYPE_RSA_SHA512:
+                nid = NID_sha512;
+                break;
+            default:
+                SSH_LOG(SSH_LOG_TRACE, "Unknown sig type %d", sig->type);
+                ssh_set_error(session,
+                              SSH_FATAL,
+                              "Unexpected signature type %s during RSA verify",
+                              sig->type_c);
+                return SSH_ERROR;
+            }
+            rc = RSA_verify(nid,
                             hash,
                             hlen,
                             ssh_string_data(sig->rsa_sig),
                             ssh_string_len(sig->rsa_sig),
                             key->rsa);
             if (rc <= 0) {
+                SSH_LOG(SSH_LOG_TRACE, "RSA verify failed");
                 ssh_set_error(session,
                               SSH_FATAL,
                               "RSA error: %s",
@@ -1655,6 +1681,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 9f53321f..d31d4569 100644
--- a/src/pki_gcrypt.c
+++ b/src/pki_gcrypt.c
@@ -1408,6 +1408,8 @@ int pki_key_compare(const ssh_key k1,
         case SSH_KEYTYPE_DSS_CERT01:
         case SSH_KEYTYPE_RSA_CERT01:
         case SSH_KEYTYPE_RSA1:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         case SSH_KEYTYPE_UNKNOWN:
             return 1;
     }
@@ -1686,6 +1688,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;
@@ -1821,6 +1825,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) {
@@ -1952,6 +1958,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;
 
@@ -1986,10 +1993,28 @@ int pki_signature_verify(ssh_session session,
             }
             break;
         case SSH_KEYTYPE_RSA:
+            switch (sig->type) {
+            case SSH_KEYTYPE_RSA_SHA256:
+                hash_type = "sha256";
+                break;
+            case SSH_KEYTYPE_RSA_SHA512:
+                hash_type = "sha512";
+                break;
+            case SSH_KEYTYPE_RSA:
+                hash_type = "sha1";
+                break;
+            default:
+                SSH_LOG(SSH_LOG_TRACE, "Unknown sig type %d", sig->type);
+                ssh_set_error(session,
+                              SSH_FATAL,
+                              "Unexpected signature type %s during RSA verify",
+                              sig->type_c);
+                return SSH_ERROR;
+            }
             err = gcry_sexp_build(&sexp,
                                   NULL,
-                                  "(data(flags pkcs1)(hash sha1 %b))",
-                                  hlen, hash);
+                                  "(data(flags pkcs1)(hash %s %b))",
+                                  hash_type, hlen, hash);
             if (err) {
                 ssh_set_error(session,
                               SSH_FATAL,
@@ -2047,6 +2072,8 @@ int pki_signature_verify(ssh_session session,
             break;
 #endif
         case SSH_KEYTYPE_RSA1:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         case SSH_KEYTYPE_UNKNOWN:
         default:
             ssh_set_error(session, SSH_FATAL, "Unknown public key type");
diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c
index 3263db47..bd6c6612 100644
--- a/src/pki_mbedcrypto.c
+++ b/src/pki_mbedcrypto.c
@@ -704,6 +704,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: {
@@ -838,6 +840,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: {
@@ -927,10 +931,29 @@ 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,
+            switch (sig->type) {
+            case SSH_KEYTYPE_RSA:
+                md = MBEDTLS_MD_SHA1;
+                break;
+            case SSH_KEYTYPE_RSA_SHA256:
+                md = MBEDTLS_MD_SHA256;
+                break;
+            case SSH_KEYTYPE_RSA_SHA512:
+                md = MBEDTLS_MD_SHA512;
+                break;
+            default:
+                SSH_LOG(SSH_LOG_TRACE, "Unknown sig type %d", sig->type);
+                ssh_set_error(session,
+                              SSH_FATAL,
+                              "Unexpected signature type %s during RSA verify",
+                              sig->type_c);
+                return SSH_ERROR;
+            }
+            rc = mbedtls_pk_verify(key->rsa, md, hash, hlen,
                     ssh_string_data(sig->rsa_sig),
                     ssh_string_len(sig->rsa_sig));
             if (rc != 0) {
diff --git a/tests/torture_key.c b/tests/torture_key.c
index c0ec845a..e5737a1c 100644
--- a/tests/torture_key.c
+++ b/tests/torture_key.c
@@ -360,6 +360,8 @@ static const char *torture_get_testkey_internal(enum ssh_keytypes_e type,
         case SSH_KEYTYPE_RSA_CERT01:
             return torture_rsa_testkey_cert;
         case SSH_KEYTYPE_RSA1:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         case SSH_KEYTYPE_UNKNOWN:
             return NULL;
     }
-- 
2.17.1


From cb57fdeab9b7b6b1f2e53aadc4b7f22351c90dc6 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 7 Aug 2018 15:04:13 +0200
Subject: [PATCH 04/20] kex: Offer SHA2 extension signature algorithms by
 default

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

diff --git a/src/kex.c b/src/kex.c
index aa6fe4fc..85d971d3 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -86,12 +86,12 @@
 
 #ifdef HAVE_ECDH
 #define ECDH "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,"
-#define HOSTKEYS "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss"
+#define HOSTKEYS "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss"
 #else
 #ifdef HAVE_DSA
-#define HOSTKEYS "ssh-ed25519,ssh-rsa,ssh-dss"
+#define HOSTKEYS "ssh-ed25519,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss"
 #else
-#define HOSTKEYS "ssh-ed25519,ssh-rsa"
+#define HOSTKEYS "ssh-ed25519,ssh-rsa,rsa-sha2-512,rsa-sha2-256"
 #endif
 #define ECDH ""
 #endif
-- 
2.17.1


From 09ef655cadf367c3bdee04832d8e6dca698de395 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 7 Aug 2018 15:04:45 +0200
Subject: [PATCH 05/20] pki: RSA signatures with SHA2 hash algorithms (RFC
 8332)

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

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

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/pki_priv.h |  9 +++--
 src/pki.c                 | 82 ++++++++++++++++++++++++++++++++++-----
 src/pki_crypto.c          | 68 +++++++++++++++++++++++++++-----
 src/pki_gcrypt.c          | 39 ++++++++++++++++---
 src/pki_mbedcrypto.c      | 43 ++++++++++++++++----
 5 files changed, 207 insertions(+), 34 deletions(-)

diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h
index af041504..4e4d2d5b 100644
--- a/include/libssh/pki_priv.h
+++ b/include/libssh/pki_priv.h
@@ -93,9 +93,12 @@ int pki_signature_verify(ssh_session session,
                          size_t hlen);
 
 /* SSH Signing Functions */
-ssh_signature pki_do_sign(const ssh_key privkey,
-                          const unsigned char *hash,
-                          size_t hlen);
+#define pki_do_sign(key, hash, hlen) \
+    pki_do_sign_alg(key, hash, hlen, key->type)
+ssh_signature pki_do_sign_alg(const ssh_key privkey,
+                              const unsigned char *hash,
+                              size_t hlen,
+                              enum ssh_keytypes_e algorithm);
 ssh_signature pki_do_sign_sessionid(const ssh_key key,
                                     const unsigned char *hash,
                                     size_t hlen);
diff --git a/src/pki.c b/src/pki.c
index 2d11fac9..8c8cd4a0 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -246,6 +246,40 @@ 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.
+ */
+static enum ssh_keytypes_e ssh_key_type_to_algorithm(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.
  *
@@ -1754,24 +1788,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_algorithm(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 29f12217..b002b16a 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -619,6 +619,8 @@ int pki_key_compare(const ssh_key k1,
 #endif
         case SSH_KEYTYPE_ED25519:
             /* ed25519 keys handled globaly */
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
         case SSH_KEYTYPE_UNKNOWN:
         default:
             return 1;
@@ -1183,23 +1185,42 @@ fail:
  *
  * @param[in]  privkey   The private rsa key to use for signing.
  *
+ * @param[in]  algorithm The public key algorithm to use.
+ *
  * @return               A newly allocated rsa sig blob or NULL on error.
  */
-static ssh_string _RSA_do_sign(const unsigned char *digest,
-                               int dlen,
-                               RSA *privkey)
+static ssh_string _RSA_do_sign_alg(const unsigned char *digest,
+                                   int dlen,
+                                   RSA *privkey,
+                                   enum ssh_keytypes_e algorithm)
 {
     ssh_string sig_blob;
     unsigned char *sig;
     unsigned int slen;
     int ok;
+    int nid = 0;
+
+    switch (algorithm) {
+    case SSH_KEYTYPE_RSA:
+        nid = NID_sha1;
+        break;
+    case SSH_KEYTYPE_RSA_SHA256:
+        nid = NID_sha256;
+        break;
+    case SSH_KEYTYPE_RSA_SHA512:
+        nid = NID_sha512;
+        break;
+    default:
+        SSH_LOG(SSH_LOG_WARN, "Incomplatible key algorithm");
+        return NULL;
+    }
 
     sig = malloc(RSA_size(privkey));
     if (sig == NULL) {
         return NULL;
     }
 
-    ok = RSA_sign(NID_sha1, digest, dlen, sig, &slen, privkey);
+    ok = RSA_sign(nid, digest, dlen, sig, &slen, privkey);
     if (!ok) {
         SAFE_FREE(sig);
         return NULL;
@@ -1218,6 +1239,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 };
@@ -1689,18 +1730,26 @@ int pki_signature_verify(ssh_session session,
     return SSH_OK;
 }
 
-ssh_signature pki_do_sign(const ssh_key privkey,
-                          const unsigned char *hash,
-                          size_t hlen) {
+ssh_signature pki_do_sign_alg(const ssh_key privkey,
+                              const unsigned char *hash,
+                              size_t hlen,
+                              enum ssh_keytypes_e algorithm)
+{
     ssh_signature sig;
     int rc;
 
+    /* Only RSA supports different signature algorithm types now */
+    if (privkey->type != algorithm && privkey->type != SSH_KEYTYPE_RSA) {
+        SSH_LOG(SSH_LOG_WARN, "Incompatible signature algorithm passed");
+        return NULL;
+    }
+
     sig = ssh_signature_new();
     if (sig == NULL) {
         return NULL;
     }
 
-    sig->type = privkey->type;
+    sig->type = algorithm;
     sig->type_c = privkey->type_c;
 
     switch(privkey->type) {
@@ -1723,7 +1772,8 @@ ssh_signature pki_do_sign(const ssh_key privkey,
             break;
         case SSH_KEYTYPE_RSA:
         case SSH_KEYTYPE_RSA1:
-            sig->rsa_sig = _RSA_do_sign(hash, hlen, privkey->rsa);
+            sig->type_c = ssh_key_algorithm_to_char(algorithm);
+            sig->rsa_sig = _RSA_do_sign_alg(hash, hlen, privkey->rsa, algorithm);
             if (sig->rsa_sig == NULL) {
                 ssh_signature_free(sig);
                 return NULL;
diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c
index d31d4569..1ce3446a 100644
--- a/src/pki_gcrypt.c
+++ b/src/pki_gcrypt.c
@@ -2083,19 +2083,28 @@ int pki_signature_verify(ssh_session session,
     return SSH_OK;
 }
 
-ssh_signature pki_do_sign(const ssh_key privkey,
-                          const unsigned char *hash,
-                          size_t hlen) {
+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) {
+        SSH_LOG(SSH_LOG_WARN, "Incompatible signature algorithm passed");
+        return NULL;
+    }
+
     sig = ssh_signature_new();
     if (sig == NULL) {
         return NULL;
     }
-    sig->type = privkey->type;
+    sig->type = algorithm;
     sig->type_c = privkey->type_c;
     switch (privkey->type) {
         case SSH_KEYTYPE_DSS:
@@ -2121,9 +2130,27 @@ ssh_signature pki_do_sign(const ssh_key privkey,
             }
             break;
         case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
+            sig->type_c = ssh_key_algorithm_to_char(algorithm);
+            switch (algorithm) {
+            case SSH_KEYTYPE_RSA:
+                hash_type = "sha1";
+                break;
+            case SSH_KEYTYPE_RSA_SHA256:
+                hash_type = "sha256";
+                break;
+            case SSH_KEYTYPE_RSA_SHA512:
+                hash_type = "sha512";
+                break;
+            default:
+                SSH_LOG(SSH_LOG_WARN, "Incomplatible key algorithm");
+                return NULL;
+            }
             err = gcry_sexp_build(&sexp,
                                   NULL,
-                                  "(data(flags pkcs1)(hash sha1 %b))",
+                                  "(data(flags pkcs1)(hash %s %b))",
+                                  hash_type,
                                   hlen,
                                   hash);
             if (err) {
@@ -2215,6 +2242,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 bd6c6612..2bd93c3f 100644
--- a/src/pki_mbedcrypto.c
+++ b/src/pki_mbedcrypto.c
@@ -990,20 +990,36 @@ int pki_signature_verify(ssh_session session, const ssh_signature sig, const
     return SSH_OK;
 }
 
-static ssh_string rsa_do_sign(const unsigned char *digest, int dlen,
-        mbedtls_pk_context *privkey)
+static ssh_string rsa_do_sign_alg(const unsigned char *digest, int dlen,
+        mbedtls_pk_context *privkey, enum ssh_keytypes_e algorithm)
 {
     ssh_string sig_blob = NULL;
+    mbedtls_md_type_t md = 0;
     unsigned char *sig = NULL;
     size_t slen;
     int ok;
 
+    switch (algorithm) {
+    case SSH_KEYTYPE_RSA:
+        md = MBEDTLS_MD_SHA1;
+        break;
+    case SSH_KEYTYPE_RSA_SHA256:
+        md = MBEDTLS_MD_SHA256;
+        break;
+    case SSH_KEYTYPE_RSA_SHA512:
+        md = MBEDTLS_MD_SHA512;
+        break;
+    default:
+        SSH_LOG(SSH_LOG_WARN, "Incomplatible key algorithm");
+        return NULL;
+    }
+
     sig = malloc(mbedtls_pk_get_bitlen(privkey) / 8);
     if (sig == NULL) {
         return NULL;
     }
 
-    ok = mbedtls_pk_sign(privkey, MBEDTLS_MD_SHA1, digest, dlen, sig, &slen,
+    ok = mbedtls_pk_sign(privkey, md, digest, dlen, sig, &slen,
             mbedtls_ctr_drbg_random, &ssh_mbedtls_ctr_drbg);
 
     if (ok != 0) {
@@ -1025,23 +1041,34 @@ 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)
+ssh_signature pki_do_sign_alg(const ssh_key privkey,
+                              const unsigned char *hash,
+                              size_t hlen,
+                              enum ssh_keytypes_e algorithm)
 {
     ssh_signature sig = NULL;
     int rc;
 
+    /* Only RSA supports different signature algorithm types now */
+    if (privkey->type != algorithm && privkey->type != SSH_KEYTYPE_RSA) {
+        SSH_LOG(SSH_LOG_WARN, "Incompatible signature algorithm passed");
+        return NULL;
+    }
+
     sig = ssh_signature_new();
     if (sig == NULL) {
         return NULL;
     }
 
-    sig->type = privkey->type;
+    sig->type = algorithm;
     sig->type_c = privkey->type_c;
 
     switch(privkey->type) {
         case SSH_KEYTYPE_RSA:
-            sig->rsa_sig = rsa_do_sign(hash, hlen, privkey->rsa);
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
+            sig->type_c = ssh_key_algorithm_to_char(algorithm);
+            sig->rsa_sig = rsa_do_sign_alg(hash, hlen, privkey->rsa, algorithm);
             if (sig->rsa_sig == NULL) {
                 ssh_signature_free(sig);
                 return NULL;
@@ -1099,7 +1126,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 465f870b4672c699d2461c612cb7db7b3a23e3b1 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 6 Aug 2018 13:40:32 +0200
Subject: [PATCH 06/20] auth: Support SHA2 extension for pubkey authentication
 (RFC 8332)

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/libssh.h |  2 ++
 src/auth.c              | 17 +++++++++++++----
 src/pki.c               |  2 +-
 3 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 7134f26a..53d146b6 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_algorithm_to_char(enum ssh_keytypes_e type);
+LIBSSH_API enum ssh_keytypes_e ssh_key_type_to_algorithm(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/src/auth.c b/src/auth.c
index bd2fecc3..b8bde175 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -432,6 +432,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) {
@@ -468,6 +470,8 @@ int ssh_userauth_try_publickey(ssh_session session,
     if (rc < 0) {
         goto fail;
     }
+    sig_type = ssh_key_type_to_algorithm(session, pubkey->type);
+    sig_type_c = ssh_key_algorithm_to_char(sig_type);
 
     /* request */
     rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
@@ -476,7 +480,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) {
@@ -537,7 +541,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;
@@ -569,7 +573,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_algorithm(session, key_type);
+    type_c = ssh_key_algorithm_to_char(sig_type);
 
     /* get public key or cert */
     rc = ssh_pki_export_pubkey_blob(privkey, &str);
@@ -633,6 +638,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) {
@@ -660,6 +667,8 @@ static int ssh_userauth_agent_publickey(ssh_session session,
     if (rc < 0) {
         goto fail;
     }
+    sig_type = ssh_key_type_to_algorithm(session, pubkey->type);
+    sig_type_c = ssh_key_algorithm_to_char(sig_type);
 
     /* request */
     rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
@@ -668,7 +677,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 8c8cd4a0..91c24f2a 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -257,7 +257,7 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
  *
  * @return              A public key algorithm to be used.
  */
-static enum ssh_keytypes_e ssh_key_type_to_algorithm(ssh_session session,
+enum ssh_keytypes_e ssh_key_type_to_algorithm(ssh_session session,
                                               enum ssh_keytypes_e type)
 {
     /* TODO this should also reflect supported key types specified in
-- 
2.17.1


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

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

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

diff --git a/tests/unittests/torture_pki_rsa.c b/tests/unittests/torture_pki_rsa.c
index 8ee74dea..1b3cbb3d 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)
 {
@@ -393,6 +396,45 @@ static void torture_pki_rsa_generate_key(void **state)
     ssh_free(session);
 }
 
+static void torture_pki_rsa_sha2(void **state)
+{
+    int rc;
+    ssh_key key;
+    ssh_signature sign;
+    ssh_session session=ssh_new();
+    (void) state;
+
+    /* Setup */
+    rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &key);
+    assert_true(rc == SSH_OK);
+    assert_true(key != NULL);
+
+    /* Sign using old ssh-rsa algorithm */
+    sign = pki_do_sign_alg(key, RSA_HASH, 20, SSH_KEYTYPE_RSA);
+    assert_true(sign != NULL);
+    rc = pki_signature_verify(session,sign,key,RSA_HASH,20);
+    assert_true(rc == SSH_OK);
+    ssh_signature_free(sign);
+
+    /* Sign using rsa-sha2-256 algorithm */
+    sign = pki_do_sign_alg(key, SHA256_HASH, 32, SSH_KEYTYPE_RSA_SHA256);
+    assert_true(sign != NULL);
+    rc = pki_signature_verify(session,sign,key,SHA256_HASH,32);
+    assert_true(rc == SSH_OK);
+    ssh_signature_free(sign);
+
+    /* Sign using rsa-sha2-512 algorithm */
+    sign = pki_do_sign_alg(key, SHA512_HASH, 64, SSH_KEYTYPE_RSA_SHA512);
+    assert_true(sign != NULL);
+    rc = pki_signature_verify(session,sign,key,SHA512_HASH,64);
+    assert_true(rc == SSH_OK);
+    ssh_signature_free(sign);
+
+    /* Cleanup */
+    ssh_key_free(key);
+    ssh_free(session);
+}
+
 #ifdef HAVE_LIBCRYPTO
 static void torture_pki_rsa_write_privkey(void **state)
 {
@@ -557,6 +599,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 5d5c0b1952ed55404fd3b47ca00dee675f0265b8 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 26 Jun 2018 12:22:31 +0200
Subject: [PATCH 08/20] SHA2 extension in the ssh-agent interface

The new constants for flags are defined in draft-miller-ssh-agent-02
are 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            | 8 ++++++++
 2 files changed, 11 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 114dadf1..6cda7744 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -548,6 +548,14 @@ ssh_string ssh_agent_sign_data(ssh_session session,
         return NULL;
     }
 
+    /* Add Flags: SHA2 extension (RFC 8332) if negotiated */
+    if (pubkey->type == SSH_KEYTYPE_RSA) {
+        if (session->extensions & SSH_EXT_SIG_RSA_SHA512) {
+            flags |= SSH_AGENT_RSA_SHA2_512;
+        } else if (session->extensions & SSH_EXT_SIG_RSA_SHA256) {
+            flags |= SSH_AGENT_RSA_SHA2_256;
+        }
+    }
     if (ssh_buffer_add_u32(request, htonl(flags)) < 0) {
         ssh_buffer_free(request);
         return NULL;
-- 
2.17.1


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

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

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


From 2a6ee37f1f04eaa5058fd4782e7b2d0afcfadfa9 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 7 Aug 2018 11:25:27 +0200
Subject: [PATCH 10/20] options: The new option
 SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES

This option allows to specify acceptable public key algorithms
and reflects the PubkeyAcceptedTypes configuration option from
OpenSSH.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/libssh.h  |  1 +
 include/libssh/session.h |  1 +
 src/options.c            | 31 +++++++++++++++++++++++++++++++
 src/session.c            |  1 +
 4 files changed, 34 insertions(+)

diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
index 53d146b6..b31aa76a 100644
--- a/include/libssh/libssh.h
+++ b/include/libssh/libssh.h
@@ -379,6 +379,7 @@ enum ssh_options_e {
   SSH_OPTIONS_GSSAPI_AUTH,
   SSH_OPTIONS_GLOBAL_KNOWNHOSTS,
   SSH_OPTIONS_NODELAY,
+  SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
 };
 
 enum {
diff --git a/include/libssh/session.h b/include/libssh/session.h
index 875f0754..84b21245 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -199,6 +199,7 @@ struct ssh_session_struct {
         char *knownhosts;
         char *global_knownhosts;
         char *wanted_methods[10];
+        char *pubkey_accepted_types;
         char *ProxyCommand;
         char *custombanner;
         unsigned long timeout; /* seconds */
diff --git a/src/options.c b/src/options.c
index 87c5bb42..2e80f2b2 100644
--- a/src/options.c
+++ b/src/options.c
@@ -147,6 +147,14 @@ int ssh_options_copy(ssh_session src, ssh_session *dest) {
             return -1;
         }
     }
+
+    if (src->opts.pubkey_accepted_types) {
+        new->opts.pubkey_accepted_types = strdup(src->opts.pubkey_accepted_types);
+        if (new->opts.pubkey_accepted_types == NULL) {
+            ssh_free(new);
+            return -1;
+        }
+    }
     new->opts.fd                   = src->opts.fd;
     new->opts.port                 = src->opts.port;
     new->opts.timeout              = src->opts.timeout;
@@ -343,6 +351,11 @@ int ssh_options_set_algo(ssh_session session,
  *                comma-separated list). ex:
  *                "ssh-rsa,ssh-dss,ecdh-sha2-nistp256"
  *
+ *              - SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
+ *                Set the preferred public key algorithms to be used for
+ *                authentication (const char *, comma-separated list). ex:
+ *                "ssh-rsa,rsa-sha2-256,ssh-dss,ecdh-sha2-nistp256"
+ *
  *              - SSH_OPTIONS_COMPRESSION_C_S:
  *                Set the compression to use for client to server
  *                communication (const char *, "yes", "no" or a specific
@@ -743,6 +756,24 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
                     return -1;
             }
             break;
+        case SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
+            v = value;
+            if (v == NULL || v[0] == '\0') {
+                ssh_set_error_invalid(session);
+                return -1;
+            } else {
+                p = ssh_keep_known_algos(SSH_HOSTKEYS, v);
+                if (p == NULL) {
+                    ssh_set_error(session, SSH_REQUEST_DENIED,
+                        "Setting method: no known public key algorithm (%s)",
+                         v);
+                    return -1;
+                }
+
+                SAFE_FREE(session->opts.pubkey_accepted_types);
+                session->opts.pubkey_accepted_types = p;
+            }
+            break;
         case SSH_OPTIONS_HMAC_C_S:
             v = value;
             if (v == NULL || v[0] == '\0') {
diff --git a/src/session.c b/src/session.c
index 35841183..37e83c2b 100644
--- a/src/session.c
+++ b/src/session.c
@@ -282,6 +282,7 @@ void ssh_free(ssh_session session) {
   SAFE_FREE(session->opts.ProxyCommand);
   SAFE_FREE(session->opts.gss_server_identity);
   SAFE_FREE(session->opts.gss_client_identity);
+  SAFE_FREE(session->opts.pubkey_accepted_types);
 
   for (i = 0; i < 10; i++) {
       if (session->opts.wanted_methods[i]) {
-- 
2.17.1


From 8f62b8bc5b2606d95f6da7eda3a991d8d1bc2027 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 7 Aug 2018 11:29:19 +0200
Subject: [PATCH 11/20] config: Accept the PubkeyAcceptedTypes configuration
 option

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

diff --git a/src/config.c b/src/config.c
index 12792afd..d3c5a0db 100644
--- a/src/config.c
+++ b/src/config.c
@@ -72,6 +72,7 @@ enum ssh_config_opcode_e {
   SOC_KBDINTERACTIVEAUTHENTICATION,
   SOC_PASSWORDAUTHENTICATION,
   SOC_PUBKEYAUTHENTICATION,
+  SOC_PUBKEYACCEPTEDTYPES,
 
   SOC_END /* Keep this one last in the list */
 };
@@ -144,7 +145,7 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
   { "preferredauthentications", SOC_UNSUPPORTED},
   { "proxyjump", SOC_UNSUPPORTED},
   { "proxyusefdpass", SOC_UNSUPPORTED},
-  { "pubkeyacceptedtypes", SOC_UNSUPPORTED},
+  { "pubkeyacceptedtypes", SOC_PUBKEYACCEPTEDTYPES},
   { "rekeylimit", SOC_UNSUPPORTED},
   { "remotecommand", SOC_UNSUPPORTED},
   { "revokedhostkeys", SOC_UNSUPPORTED},
@@ -591,6 +592,12 @@ static int ssh_config_parse_line(ssh_session session, const char *line,
             ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, p);
         }
         break;
+    case SOC_PUBKEYACCEPTEDTYPES:
+        p = ssh_config_get_str_tok(&s, NULL);
+        if (p && *parsing) {
+            ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, p);
+        }
+        break;
     case SOC_KEXALGORITHMS:
         p = ssh_config_get_str_tok(&s, NULL);
         if (p && *parsing) {
-- 
2.17.1


From 7da708e1a1b477fbc148701bf628abf8900093c5 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 7 Aug 2018 11:31:01 +0200
Subject: [PATCH 12/20] tests: Cover PubkeyAcceptedTypes configuration option

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

diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c
index f6cf5377..675a14e3 100644
--- a/tests/unittests/torture_config.c
+++ b/tests/unittests/torture_config.c
@@ -23,6 +23,7 @@ extern LIBSSH_THREAD int ssh_log_level;
 #define ID_FILE "/etc/xxx"
 #define KEXALGORITHMS "ecdh-sha2-nistp521,diffie-hellman-group14-sha1"
 #define HOSTKEYALGORITHMS "ssh-ed25519,ecdsa-sha2-nistp521,ssh-rsa"
+#define PUBKEYACCEPTEDTYPES "rsa-sha2-512,ssh-rsa,ecdsa-sha2-nistp521"
 #define MACS "hmac-sha1,hmac-sha2-256"
 #define USER_KNOWN_HOSTS "%d/my_known_hosts"
 #define GLOBAL_KNOWN_HOSTS "/etc/ssh/my_ssh_known_hosts"
@@ -50,6 +51,7 @@ static int setup_config_files(void **state)
                        "\n\nIdentityFile "ID_FILE"\n"
                        "\n\nKexAlgorithms "KEXALGORITHMS"\n"
                        "\n\nHostKeyAlgorithms "HOSTKEYALGORITHMS"\n"
+                       "\n\nPubkeyAcceptedTypes "PUBKEYACCEPTEDTYPES"\n"
                        "\n\nMACs "MACS"\n");
 
     /* Multiple Port settings -> parsing returns early. */
@@ -148,6 +150,8 @@ static void torture_config_from_file(void **state) {
 
     assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], HOSTKEYALGORITHMS);
 
+    assert_string_equal(session->opts.pubkey_accepted_types, PUBKEYACCEPTEDTYPES);
+
     assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], MACS);
     assert_string_equal(session->opts.wanted_methods[SSH_MAC_S_C], MACS);
 }
-- 
2.17.1


From 29b8ca89981c55429088c71684c019c7bcf8b186 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 7 Aug 2018 11:32:36 +0200
Subject: [PATCH 13/20] pki: Allow filtering accepted public key types based on
 the configuration

This effectively allows to disable using the SHA2 extension, disable
other old public key mechanisms out of the box (hello DSA) or force
the new SHA2-based key algorithm types if needed.

This exposes the  default_methods  array from  kex.c.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/pki.h |  1 +
 src/kex.c            |  2 +-
 src/pki.c            | 31 +++++++++++++++++++++++++++----
 3 files changed, 29 insertions(+), 5 deletions(-)

diff --git a/include/libssh/pki.h b/include/libssh/pki.h
index 4a4ce612..7856da9b 100644
--- a/include/libssh/pki.h
+++ b/include/libssh/pki.h
@@ -133,4 +133,5 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
 ssh_public_key ssh_pki_convert_key_to_publickey(const ssh_key key);
 ssh_private_key ssh_pki_convert_key_to_privatekey(const ssh_key key);
 
+int ssh_key_algorithm_allowed(ssh_session session, const char *type);
 #endif /* PKI_H_ */
diff --git a/src/kex.c b/src/kex.c
index 9b6b9d2e..952e32eb 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -105,7 +105,7 @@
 #define KEX_EXTENSION_CLIENT "ext-info-c"
 
 /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */
-static const char *default_methods[] = {
+const char *default_methods[] = {
   KEY_EXCHANGE,
   PUBLIC_KEY_ALGORITHMS,
   AES BLOWFISH DES,
diff --git a/src/pki.c b/src/pki.c
index 91c24f2a..57c9712c 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -80,6 +80,8 @@ enum ssh_keytypes_e pki_privatekey_type_from_string(const char *privkey) {
     return SSH_KEYTYPE_UNKNOWN;
 }
 
+extern const char *default_methods[];
+
 /**
  * @brief returns the ECDSA key name ("ecdsa-sha2-nistp256" for example)
  *
@@ -246,6 +248,26 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
   return NULL;
 }
 
+/**
+ * @brief Checks the given key against the configured allowed
+ * public key algorithm types
+ *
+ * @param[in] session The SSH session
+ * @parma[in] type    The key algorithm to check
+ * @returns           1 if the key algorithm is allowed 0 otherwise
+ */
+int ssh_key_algorithm_allowed(ssh_session session, const char *type)
+{
+    const char *allowed_list;
+
+    allowed_list = session->opts.pubkey_accepted_types;
+    if (allowed_list == NULL)
+        allowed_list = default_methods[SSH_HOSTKEYS];
+
+    SSH_LOG(SSH_LOG_DEBUG, "Checking %s with list <%s>", type, allowed_list);
+    return ssh_match_group(allowed_list, type);
+}
+
 /**
  * @brief Convert a key type to a pubkey algorithm type. This is usually
  * the same as public key type, unless the SHA2 extension (RFC 8332) is
@@ -260,19 +282,20 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
 enum ssh_keytypes_e ssh_key_type_to_algorithm(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) {
+        if (ssh_key_algorithm_allowed(session, "rsa-sha2-512") &&
+            (session->extensions & SSH_EXT_SIG_RSA_SHA512)) {
             return SSH_KEYTYPE_RSA_SHA512;
         }
 
-        if (session->extensions & SSH_EXT_SIG_RSA_SHA256) {
+        if (ssh_key_algorithm_allowed(session, "rsa-sha2-256") &&
+            (session->extensions & SSH_EXT_SIG_RSA_SHA256)) {
             return SSH_KEYTYPE_RSA_SHA256;
         }
         FALL_THROUGH;
     default:
+        /* Other key types match the signature algorithm */
         return type;
     }
 
-- 
2.17.1


From 22aedc468c2bf99f70233a9eea26daa2401b7596 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Tue, 7 Aug 2018 11:34:30 +0200
Subject: [PATCH 14/20] tests: PUBLICKEY_ACCEPTED_TYPES are effective

Verify the PUBLICKEY_ACCEPTED_TYPES option is handled correctly
and affects the signature algorithm selection based on the
extensions and can be used to limit list of offered mechanisms
to the server.

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

diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c
index 412df102..60c378b9 100644
--- a/tests/unittests/torture_options.c
+++ b/tests/unittests/torture_options.c
@@ -115,6 +115,54 @@ static void torture_options_set_hostkey(void **state) {
     assert_false(rc == 0);
 }
 
+static void torture_options_set_pubkey_accepted_types(void **state) {
+    ssh_session session = *state;
+    int rc;
+    enum ssh_keytypes_e type;
+
+    /* Test known public key algorithms */
+    rc = ssh_options_set(session,
+                         SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+                         "ssh-ed25519,ecdsa-sha2-nistp384,ssh-rsa");
+    assert_true(rc == 0);
+    assert_string_equal(session->opts.pubkey_accepted_types,
+                        "ssh-ed25519,ecdsa-sha2-nistp384,ssh-rsa");
+
+    /* Test one unknown public key algorithms */
+    rc = ssh_options_set(session,
+                         SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+                         "ssh-ed25519,unknown-crap@xxxxxxxxxxx,ssh-rsa");
+    assert_true(rc == 0);
+    assert_string_equal(session->opts.pubkey_accepted_types,
+                        "ssh-ed25519,ssh-rsa");
+
+    /* Test all unknown public key algorithms */
+    rc = ssh_options_set(session,
+                         SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+                         "unknown-crap@xxxxxxxxxxx,more-crap@xxxxxxxxxxx");
+    assert_false(rc == 0);
+
+    /* Test that the option affects the algorithm selection for RSA keys */
+    /* simulate the SHA2 extension was negotiated */
+    session->extensions = SSH_EXT_SIG_RSA_SHA256;
+
+    /* previous configuration did not list the SHA2 extension, so
+     * it should not be used */
+    type = ssh_key_type_to_algorithm(session, SSH_KEYTYPE_RSA);
+    assert_int_equal(type, SSH_KEYTYPE_RSA);
+
+    /* now, lets allow the signature from SHA2 extension and expect
+     * it to be used */
+    rc = ssh_options_set(session,
+                         SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
+                         "rsa-sha2-256,ssh-rsa");
+    assert_true(rc == 0);
+    assert_string_equal(session->opts.pubkey_accepted_types,
+                        "rsa-sha2-256,ssh-rsa");
+    type = ssh_key_type_to_algorithm(session, SSH_KEYTYPE_RSA);
+    assert_int_equal(type, SSH_KEYTYPE_RSA_SHA256);
+}
+
 static void torture_options_set_macs(void **state) {
     ssh_session session = *state;
     int rc;
@@ -401,6 +449,7 @@ int torture_run_tests(void) {
         cmocka_unit_test_setup_teardown(torture_options_set_ciphers, setup, teardown),
         cmocka_unit_test_setup_teardown(torture_options_set_key_exchange, setup, teardown),
         cmocka_unit_test_setup_teardown(torture_options_set_hostkey, setup, teardown),
+        cmocka_unit_test_setup_teardown(torture_options_set_pubkey_accepted_types, setup, teardown),
         cmocka_unit_test_setup_teardown(torture_options_set_macs, setup, teardown),
         cmocka_unit_test_setup_teardown(torture_options_config_host, setup, teardown)
     };
-- 
2.17.1


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

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

diff --git a/src/auth.c b/src/auth.c
index b8bde175..dddb6fde 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -458,6 +458,18 @@ int ssh_userauth_try_publickey(ssh_session session,
             return SSH_ERROR;
     }
 
+    sig_type = ssh_key_type_to_algorithm(session, pubkey->type);
+    sig_type_c = ssh_key_algorithm_to_char(sig_type);
+
+    /* Check if the given public key algorithm is allowed */
+    if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+        ssh_set_error(session, SSH_REQUEST_DENIED,
+                      "The key algorithm %s is not allowed to be used by"
+                      " PubkeyAcceptedKeyTypes configuration option",
+                      sig_type_c);
+        return SSH_AUTH_DENIED;
+    }
+
     rc = ssh_userauth_request_service(session);
     if (rc == SSH_AGAIN) {
         return SSH_AUTH_AGAIN;
@@ -470,8 +482,6 @@ int ssh_userauth_try_publickey(ssh_session session,
     if (rc < 0) {
         goto fail;
     }
-    sig_type = ssh_key_type_to_algorithm(session, pubkey->type);
-    sig_type_c = ssh_key_algorithm_to_char(sig_type);
 
     /* request */
     rc = ssh_buffer_pack(session->out_buffer, "bsssbsS",
@@ -540,7 +550,7 @@ int ssh_userauth_publickey(ssh_session session,
 {
     ssh_string str = NULL;
     int rc;
-    const char *type_c;
+    const char *sig_type_c;
     enum ssh_keytypes_e key_type, sig_type;
 
     if (session == NULL) {
@@ -564,6 +574,20 @@ int ssh_userauth_publickey(ssh_session session,
             return SSH_AUTH_ERROR;
     }
 
+    /* Cert auth requires presenting the cert type name (*-cert@xxxxxxxxxxx) */
+    key_type = privkey->cert != NULL ? privkey->cert_type : privkey->type;
+    sig_type = ssh_key_type_to_algorithm(session, key_type);
+    sig_type_c = ssh_key_algorithm_to_char(sig_type);
+
+    /* Check if the given public key algorithm is allowed */
+    if (!ssh_key_algorithm_allowed(session, sig_type_c)) {
+        ssh_set_error(session, SSH_REQUEST_DENIED,
+                      "The key algorithm %s is not allowed to be used by"
+                      " PubkeyAcceptedKeyTypes configuration option",
+                      sig_type_c);
+        return SSH_AUTH_DENIED;
+    }
+
     rc = ssh_userauth_request_service(session);
     if (rc == SSH_AGAIN) {
         return SSH_AUTH_AGAIN;
@@ -571,11 +595,6 @@ int ssh_userauth_publickey(ssh_session session,
         return SSH_AUTH_ERROR;
     }
 
-    /* Cert auth requires presenting the cert type name (*-cert@xxxxxxxxxxx) */
-    key_type = privkey->cert != NULL ? privkey->cert_type : privkey->type;
-    sig_type = ssh_key_type_to_algorithm(session, key_type);
-    type_c = ssh_key_algorithm_to_char(sig_type);
-
     /* get public key or cert */
     rc = ssh_pki_export_pubkey_blob(privkey, &str);
     if (rc < 0) {
@@ -589,7 +608,7 @@ int ssh_userauth_publickey(ssh_session session,
             "ssh-connection",
             "publickey",
             1, /* private key */
-            type_c, /* algo */
+            sig_type_c, /* algo */
             str /* public key or cert */
             );
     if (rc < 0) {
-- 
2.17.1


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

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

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

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

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


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

This does not affect old signatures, where the public key algorithm
matches the public key type.

This is a problem when using SHA2 extension for the RSA keys, where
the new signature algorithsm are introduced in addition to the
exitsing ssh-rsa which was ignored throughout the code.

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

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


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

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

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

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


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

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

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

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


From 5ee484b5d4c6b0d64fb1b89f6af4aa018f5b144c Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@xxxxxxxxxx>
Date: Mon, 6 Aug 2018 14:32:28 +0200
Subject: [PATCH 20/20] pki: Support RSA SHA2 signatures of sessionid for
 server

This involves mostly creation of host keys proofs but needs
to follow the same procedure as the client authentication
signatures.

At the same time, the SHA2 extension is enabled in the pkd
so we are able to atomicaly provide correct signatures and
pass tests.

Signed-off-by: Jakub Jelen <jjelen@xxxxxxxxxx>
---
 include/libssh/pki_priv.h |  9 ++++++---
 src/pki.c                 | 31 +++++++++++++++++++++++--------
 src/pki_crypto.c          | 39 ++++++++++++++-------------------------
 src/pki_gcrypt.c          | 34 +++++++++++++++++++++++++++++-----
 src/pki_mbedcrypto.c      | 20 ++++++++++++++++----
 tests/pkd/pkd_client.h    |  4 ++--
 6 files changed, 90 insertions(+), 47 deletions(-)

diff --git a/include/libssh/pki_priv.h b/include/libssh/pki_priv.h
index 4e4d2d5b..69d3665b 100644
--- a/include/libssh/pki_priv.h
+++ b/include/libssh/pki_priv.h
@@ -99,9 +99,12 @@ ssh_signature pki_do_sign_alg(const ssh_key privkey,
                               const unsigned char *hash,
                               size_t hlen,
                               enum ssh_keytypes_e algorithm);
-ssh_signature pki_do_sign_sessionid(const ssh_key key,
-                                    const unsigned char *hash,
-                                    size_t hlen);
+#define pki_do_sign_sessionid(key, hash, hlen) \
+    pki_do_sign_sessionid_alg(key, hash, hlen, key->type)
+ssh_signature pki_do_sign_sessionid_alg(const ssh_key key,
+                                        const unsigned char *hash,
+                                        size_t hlen,
+                                        enum ssh_keytypes_e algorithm);
 int pki_ed25519_sign(const ssh_key privkey, ssh_signature sig,
         const unsigned char *hash, size_t hlen);
 int pki_ed25519_verify(const ssh_key pubkey, ssh_signature sig,
diff --git a/src/pki.c b/src/pki.c
index 57c9712c..83c78a22 100644
--- a/src/pki.c
+++ b/src/pki.c
@@ -1981,21 +1981,36 @@ ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session,
             sig = NULL;
         }
     } else {
-        unsigned char hash[SHA_DIGEST_LEN] = {0};
-        SHACTX ctx;
+        unsigned char hash[SHA512_DIGEST_LEN] = {0};
+        uint32_t hlen = 0;
+        enum ssh_keytypes_e sig_type;
 
-        ctx = sha1_init();
-        if (ctx == NULL) {
+        sig_type = ssh_key_type_to_algorithm(session, privkey->type);
+        switch (sig_type) {
+        case SSH_KEYTYPE_RSA_SHA256:
+            sha256(crypto->secret_hash, crypto->digest_len, hash);
+            hlen = SHA256_DIGEST_LEN;
+            break;
+        case SSH_KEYTYPE_RSA_SHA512:
+            sha512(crypto->secret_hash, crypto->digest_len, hash);
+            hlen = SHA512_DIGEST_LEN;
+            break;
+        case SSH_KEYTYPE_RSA:
+        case SSH_KEYTYPE_DSS:
+            sha1(crypto->secret_hash, crypto->digest_len, hash);
+            hlen = SHA_DIGEST_LEN;
+            break;
+        default:
+            SSH_LOG(SSH_LOG_TRACE, "Unknown sig->type: %d", sig->type);
             return NULL;
         }
-        sha1_update(ctx, crypto->secret_hash, crypto->digest_len);
-        sha1_final(hash, ctx);
+
 
 #ifdef DEBUG_CRYPTO
-        ssh_print_hexa("Hash being signed", hash, SHA_DIGEST_LEN);
+        ssh_print_hexa("Hash being signed", hash, hlen);
 #endif
 
-        sig = pki_do_sign_sessionid(privkey, hash, SHA_DIGEST_LEN);
+        sig = pki_do_sign_sessionid_alg(privkey, hash, hlen, sig_type);
         if (sig == NULL) {
             return NULL;
         }
diff --git a/src/pki_crypto.c b/src/pki_crypto.c
index b002b16a..a9473950 100644
--- a/src/pki_crypto.c
+++ b/src/pki_crypto.c
@@ -1239,26 +1239,6 @@ static ssh_string _RSA_do_sign_alg(const unsigned char *digest,
     return sig_blob;
 }
 
-/**
- * @internal
- *
- * @brief Compute a digital signature.
- *
- * @param[in]  digest    The message digest.
- *
- * @param[in]  dlen      The length of the digest.
- *
- * @param[in]  privkey   The private rsa key to use for signing.
- *
- * @return               A newly allocated rsa sig blob or NULL on error.
- */
-static ssh_string _RSA_do_sign(const unsigned char *digest,
-                                    int dlen,
-                                    RSA *privkey)
-{
-    return _RSA_do_sign_alg(digest, dlen, privkey, SSH_KEYTYPE_RSA);
-}
-
 static ssh_string pki_dsa_signature_to_blob(const ssh_signature sig)
 {
     char buffer[40] = { 0 };
@@ -1816,17 +1796,25 @@ ssh_signature pki_do_sign_alg(const ssh_key privkey,
 }
 
 #ifdef WITH_SERVER
-ssh_signature pki_do_sign_sessionid(const ssh_key key,
-                                    const unsigned char *hash,
-                                    size_t hlen)
+ssh_signature pki_do_sign_sessionid_alg(const ssh_key key,
+                                        const unsigned char *hash,
+                                        size_t hlen,
+                                        enum ssh_keytypes_e algorithm)
 {
     ssh_signature sig;
 
+    /* Only RSA supports different signature algorithm types now */
+    if (key->type != algorithm && key->type != SSH_KEYTYPE_RSA) {
+        SSH_LOG(SSH_LOG_WARN, "Incompatible signature algorithm passed");
+        return NULL;
+    }
+
     sig = ssh_signature_new();
     if (sig == NULL) {
         return NULL;
     }
-    sig->type = key->type;
+
+    sig->type = algorithm;
     sig->type_c = key->type_c;
 
     switch(key->type) {
@@ -1839,7 +1827,8 @@ ssh_signature pki_do_sign_sessionid(const ssh_key key,
             break;
         case SSH_KEYTYPE_RSA:
         case SSH_KEYTYPE_RSA1:
-            sig->rsa_sig = _RSA_do_sign(hash, hlen, key->rsa);
+            sig->type_c = ssh_key_algorithm_to_char(algorithm);
+            sig->rsa_sig = _RSA_do_sign_alg(hash, hlen, key->rsa, algorithm);
             if (sig->rsa_sig == NULL) {
                 ssh_signature_free(sig);
                 return NULL;
diff --git a/src/pki_gcrypt.c b/src/pki_gcrypt.c
index 1ce3446a..bd91d2fa 100644
--- a/src/pki_gcrypt.c
+++ b/src/pki_gcrypt.c
@@ -2203,20 +2203,28 @@ ssh_signature pki_do_sign_alg(const ssh_key privkey,
 }
 
 #ifdef WITH_SERVER
-ssh_signature pki_do_sign_sessionid(const ssh_key key,
-                                    const unsigned char *hash,
-                                    size_t hlen)
+ssh_signature pki_do_sign_sessionid_alg(const ssh_key key,
+                                        const unsigned char *hash,
+                                        size_t hlen,
+                                        enum ssh_keytypes_e algorithm)
 {
     unsigned char ghash[hlen + 1];
+    const char *hash_type = NULL;
     ssh_signature sig;
     gcry_sexp_t sexp;
     gcry_error_t err;
 
+    /* Only RSA supports different signature algorithm types now */
+    if (key->type != algorithm && key->type != SSH_KEYTYPE_RSA) {
+        return NULL;
+    }
+
     sig = ssh_signature_new();
     if (sig == NULL) {
         return NULL;
     }
-    sig->type = key->type;
+
+    sig->type = algorithm;
     sig->type_c = key->type_c;
 
     switch(key->type) {
@@ -2244,9 +2252,25 @@ ssh_signature pki_do_sign_sessionid(const ssh_key key,
         case SSH_KEYTYPE_RSA:
         case SSH_KEYTYPE_RSA_SHA256:
         case SSH_KEYTYPE_RSA_SHA512:
+            sig->type_c = ssh_key_algorithm_to_char(algorithm);
+            switch (algorithm) {
+            case SSH_KEYTYPE_RSA:
+                hash_type = "sha1";
+                break;
+            case SSH_KEYTYPE_RSA_SHA256:
+                hash_type = "sha256";
+                break;
+            case SSH_KEYTYPE_RSA_SHA512:
+                hash_type = "sha512";
+                break;
+            default:
+                SSH_LOG(SSH_LOG_WARN, "Incomplatible key algorithm");
+                return NULL;
+            }
             err = gcry_sexp_build(&sexp,
                                   NULL,
-                                  "(data(flags pkcs1)(hash sha1 %b))",
+                                  "(data(flags pkcs1)(hash %s %b))",
+                                  hash_type,
                                   hlen,
                                   hash);
             if (err) {
diff --git a/src/pki_mbedcrypto.c b/src/pki_mbedcrypto.c
index 2bd93c3f..215714d6 100644
--- a/src/pki_mbedcrypto.c
+++ b/src/pki_mbedcrypto.c
@@ -1111,22 +1111,34 @@ ssh_signature pki_do_sign_alg(const ssh_key privkey,
 }
 
 #ifdef WITH_SERVER
-ssh_signature pki_do_sign_sessionid(const ssh_key key, const unsigned char
-        *hash, size_t hlen)
+ssh_signature pki_do_sign_sessionid_alg(const ssh_key key,
+                                        const unsigned char *hash,
+                                        size_t hlen,
+                                        enum ssh_keytypes_e algorithm)
 {
     ssh_signature sig = NULL;
     int rc;
 
+    /* Only RSA supports different signature algorithm types now */
+    if (key->type != algorithm && key->type != SSH_KEYTYPE_RSA) {
+        SSH_LOG(SSH_LOG_WARN, "Incompatible signature algorithm passed");
+        return NULL;
+    }
+
     sig = ssh_signature_new();
     if (sig == NULL) {
         return NULL;
     }
-    sig->type = key->type;
+
+    sig->type = algorithm;
     sig->type_c = key->type_c;
 
     switch (key->type) {
         case SSH_KEYTYPE_RSA:
-            sig->rsa_sig = rsa_do_sign_alg(hash, hlen, key->rsa, key->type);
+        case SSH_KEYTYPE_RSA_SHA256:
+        case SSH_KEYTYPE_RSA_SHA512:
+            sig->type_c = ssh_key_algorithm_to_char(algorithm);
+            sig->rsa_sig = rsa_do_sign_alg(hash, hlen, key->rsa, algorithm);
             if (sig->rsa_sig == NULL) {
                 ssh_signature_free(sig);
                 return NULL;
diff --git a/tests/pkd/pkd_client.h b/tests/pkd/pkd_client.h
index da40a7c5..4d01a607 100644
--- a/tests/pkd/pkd_client.h
+++ b/tests/pkd/pkd_client.h
@@ -15,8 +15,8 @@
 #define OPENSSH_BINARY "ssh"
 #define OPENSSH_KEYGEN "ssh-keygen"
 
-#define OPENSSH_HOSTKEY_ALGOS_DEFAULT "ssh-ed25519,ssh-rsa"
-#define OPENSSH_PKACCEPTED_DEFAULT    "ssh-ed25519,ssh-rsa"
+#define OPENSSH_HOSTKEY_ALGOS_DEFAULT "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa"
+#define OPENSSH_PKACCEPTED_DEFAULT    "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa"
 
 #if       HAVE_ECC
 #define OPENSSH_HOSTKEY_ALGOS_ECDSA   ",ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521"
-- 
2.17.1


Archive administrator: postmaster@lists.cynapses.org