libssh2/backport-RSA-SHA2-256-512-key-upgrade-support-RFC-8332.patch
2022-03-22 16:23:05 +08:00

1026 lines
33 KiB
Diff

From 64a555d6f5aafed504a10e5b756e85c91b1d56ce Mon Sep 17 00:00:00 2001
From: Will Cosgrove <will@panic.com>
Date: Thu, 6 Jan 2022 09:50:58 -0800
Subject: [PATCH] RSA SHA2 256/512 key upgrade support RFC 8332 #536 (#626)
Notes:
* Host Key RSA 256/512 support #536
* Client side key hash upgrading for RFC 8332
* Support for server-sig-algs, ext-info-c server messages
* Customizing preferred server-sig-algs via the preference LIBSSH2_METHOD_SIGN_ALGO
Credit: Anders Borum, Will Cosgrove
---
docs/HACKING-CRYPTO | 37 ++++++++
docs/libssh2_session_methods.3 | 7 +-
include/libssh2.h | 1 +
src/crypto.h | 32 +++++++
src/hostkey.c | 198 ++++++++++++++++++++++++++++++++++++++++-
src/kex.c | 22 +++++
src/libgcrypt.c | 18 ++++
src/libgcrypt.h | 1 +
src/libssh2_priv.h | 8 ++
src/mbedtls.c | 19 ++++
src/mbedtls.h | 1 +
src/openssl.c | 94 +++++++++++++++++--
src/openssl.h | 2 +
src/packet.c | 69 ++++++++++++++
src/userauth.c | 159 +++++++++++++++++++++++++++++++--
src/wincng.c | 18 ++++
src/wincng.h | 1 +
19 files changed, 685 insertions(+), 21 deletions(-)
diff --git a/docs/HACKING-CRYPTO b/docs/HACKING-CRYPTO
index ca94772..85d813a 100644
--- a/docs/HACKING-CRYPTO
+++ b/docs/HACKING-CRYPTO
@@ -637,6 +637,32 @@ Note: this procedure is not used if macro _libssh2_rsa_sha1_signv() is defined.
void _libssh2_rsa_free(libssh2_rsa_ctx *rsactx);
Releases the RSA computation context at rsactx.
+LIBSSH2_RSA_SHA2
+#define as 1 if the crypto library supports RSA SHA2 256/512, else 0.
+If defined as 0, the rest of this section can be omitted.
+
+int _libssh2_rsa_sha2_sign(LIBSSH2_SESSION * session,
+ libssh2_rsa_ctx * rsactx,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len);
+RSA signs the (hash, hashlen) SHA-2 hash bytes based on hash length and stores
+the allocated signature at (signature, signature_len).
+Signature buffer must be allocated from the given session.
+Returns 0 if OK, else -1.
+This procedure is already prototyped in crypto.h.
+Note: this procedure is not used if macro _libssh2_rsa_sha1_signv() is defined.
+
+int _libssh2_rsa_sha2_verify(libssh2_rsa_ctx * rsa,
+ size_t hash_len,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m, unsigned long m_len);
+Verify (sig, sig_len) signature of (m, m_len) using an SHA-2 hash based on
+hash length and the RSA context.
+Return 0 if OK, else -1.
+This procedure is already prototyped in crypto.h.
7.2) DSA
LIBSSH2_DSA
@@ -900,3 +926,14 @@ If this is not needed, it should be defined as an empty macro.
int _libssh2_random(unsigned char *buf, int len);
Store len random bytes at buf.
Returns 0 if OK, else -1.
+
+const char * _libssh2_supported_key_sign_algorithms(LIBSSH2_SESSION *session,
+ unsigned char *key_method,
+ size_t key_method_len);
+
+This function is for implementing key hash upgrading as defined in RFC 8332.
+
+Based on the incoming key_method value, this function will return a
+list of supported algorithms that can upgrade the original key method algorithm
+as a comma seperated list, if there is no upgrade option this function should
+return NULL.
diff --git a/docs/libssh2_session_methods.3 b/docs/libssh2_session_methods.3
index cc4f6d4..0e7f79f 100644
--- a/docs/libssh2_session_methods.3
+++ b/docs/libssh2_session_methods.3
@@ -1,4 +1,4 @@
-.TH libssh2_session_methods 3 "1 Jun 2007" "libssh2 0.15" "libssh2 manual"
+.TH libssh2_session_methods 3 "8 Nov 2021" "libssh2 1.11" "libssh2 manual"
.SH NAME
libssh2_session_methods - return the currently active algorithms
.SH SYNOPSIS
@@ -8,13 +8,14 @@ const char *
libssh2_session_methods(LIBSSH2_SESSION *session, int method_type);
.SH DESCRIPTION
-\fIsession\fP - Session instance as returned by
+\fIsession\fP - Session instance as returned by
.BR libssh2_session_init_ex(3)
\fImethod_type\fP - one of the method type constants: LIBSSH2_METHOD_KEX,
LIBSSH2_METHOD_HOSTKEY, LIBSSH2_METHOD_CRYPT_CS, LIBSSH2_METHOD_CRYPT_SC,
LIBSSH2_METHOD_MAC_CS, LIBSSH2_METHOD_MAC_SC, LIBSSH2_METHOD_COMP_CS,
-LIBSSH2_METHOD_COMP_SC, LIBSSH2_METHOD_LANG_CS, LIBSSH2_METHOD_LANG_SC.
+LIBSSH2_METHOD_COMP_SC, LIBSSH2_METHOD_LANG_CS, LIBSSH2_METHOD_LANG_SC,
+LIBSSH2_METHOD_SIGN_ALGO.
Returns the actual method negotiated for a particular transport parameter.
.SH RETURN VALUE
diff --git a/include/libssh2.h b/include/libssh2.h
index d064b31..b9ae809 100644
--- a/include/libssh2.h
+++ b/include/libssh2.h
@@ -356,6 +356,7 @@ typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE
#define LIBSSH2_METHOD_COMP_SC 7
#define LIBSSH2_METHOD_LANG_CS 8
#define LIBSSH2_METHOD_LANG_SC 9
+#define LIBSSH2_METHOD_SIGN_ALGO 10
/* flags */
#define LIBSSH2_FLAG_SIGPIPE 1
diff --git a/src/crypto.h b/src/crypto.h
index f512d60..809aef7 100644
--- a/src/crypto.h
+++ b/src/crypto.h
@@ -93,6 +93,19 @@ int _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session,
size_t hash_len,
unsigned char **signature,
size_t *signature_len);
+#if LIBSSH2_RSA_SHA2
+int _libssh2_rsa_sha2_sign(LIBSSH2_SESSION * session,
+ libssh2_rsa_ctx * rsactx,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len);
+int _libssh2_rsa_sha2_verify(libssh2_rsa_ctx * rsa,
+ size_t hash_len,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m, unsigned long m_len);
+#endif
int _libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx ** rsa,
LIBSSH2_SESSION * session,
const char *filedata,
@@ -245,4 +258,23 @@ int _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session,
size_t privatekeydata_len,
const char *passphrase);
+
+/**
+ * @function _libssh2_supported_key_sign_algorithms
+ * @abstract Returns supported algorithms used for upgrading public
+ * key signing RFC 8332
+ * @discussion Based on the incoming key_method value, this function
+ * will return supported algorithms that can upgrade the key method
+ * @related _libssh2_key_sign_algorithm()
+ * @param key_method current key method, usually the default key sig method
+ * @param key_method_len length of the key method buffer
+ * @result comma seperated list of supported upgrade options per RFC 8332, if
+ * there is no upgrade option return NULL
+ */
+
+const char *
+_libssh2_supported_key_sign_algorithms(LIBSSH2_SESSION *session,
+ unsigned char *key_method,
+ size_t key_method_len);
+
#endif /* __LIBSSH2_CRYPTO_H */
diff --git a/src/hostkey.c b/src/hostkey.c
index c0e2c63..f005d90 100644
--- a/src/hostkey.c
+++ b/src/hostkey.c
@@ -64,8 +64,8 @@ hostkey_method_ssh_rsa_init(LIBSSH2_SESSION * session,
void **abstract)
{
libssh2_rsa_ctx *rsactx;
- unsigned char *e, *n;
- size_t e_len, n_len;
+ unsigned char *e, *n, *type;
+ size_t e_len, n_len, type_len;
struct string_buf buf;
if(*abstract) {
@@ -83,8 +83,27 @@ hostkey_method_ssh_rsa_init(LIBSSH2_SESSION * session,
buf.dataptr = buf.data;
buf.len = hostkey_data_len;
- if(_libssh2_match_string(&buf, "ssh-rsa"))
+ if(_libssh2_get_string(&buf, &type, &type_len)) {
return -1;
+ }
+
+ /* we accept one of 3 header types */
+ if(type_len == 7 && strncmp("ssh-rsa", (char *)type, 7) == 0) {
+ /* ssh-rsa */
+ }
+#if LIBSSH2_RSA_SHA2
+ else if(type_len == 12 && strncmp("rsa-sha2-256", (char *)type, 12) == 0) {
+ /* rsa-sha2-256 */
+ }
+ else if(type_len == 12 && strncmp("rsa-sha2-512", (char *)type, 12) == 0) {
+ /* rsa-sha2-512 */
+ }
+#endif
+ else {
+ _libssh2_debug(session, LIBSSH2_TRACE_ERROR,
+ "unexpected rsa type: %.*s", type_len, type);
+ return -1;
+ }
if(_libssh2_get_string(&buf, &e, &e_len))
return -1;
@@ -228,6 +247,146 @@ hostkey_method_ssh_rsa_signv(LIBSSH2_SESSION * session,
}
/*
+ * hostkey_method_ssh_rsa_sha2_256_sig_verify
+ *
+ * Verify signature created by remote
+ */
+#if LIBSSH2_RSA_SHA2
+
+static int
+hostkey_method_ssh_rsa_sha2_256_sig_verify(LIBSSH2_SESSION * session,
+ const unsigned char *sig,
+ size_t sig_len,
+ const unsigned char *m,
+ size_t m_len, void **abstract)
+{
+ libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
+ (void) session;
+
+ /* Skip past keyname_len(4) + keyname(12){"rsa-sha2-256"} +
+ signature_len(4) */
+ if(sig_len < 20)
+ return -1;
+
+ sig += 20;
+ sig_len -= 20;
+ return _libssh2_rsa_sha2_verify(rsactx, SHA256_DIGEST_LENGTH, sig, sig_len,
+ m, m_len);
+}
+
+/*
+ * hostkey_method_ssh_rsa_sha2_256_signv
+ *
+ * Construct a signature from an array of vectors
+ */
+
+static int
+hostkey_method_ssh_rsa_sha2_256_signv(LIBSSH2_SESSION * session,
+ unsigned char **signature,
+ size_t *signature_len,
+ int veccount,
+ const struct iovec datavec[],
+ void **abstract)
+{
+ libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
+
+#ifdef _libssh2_rsa_sha2_256_signv
+ return _libssh2_rsa_sha2_256_signv(session, signature, signature_len,
+ veccount, datavec, rsactx);
+#else
+ int ret;
+ int i;
+ unsigned char hash[SHA256_DIGEST_LENGTH];
+ libssh2_sha256_ctx ctx;
+
+ libssh2_sha256_init(&ctx);
+ for(i = 0; i < veccount; i++) {
+ libssh2_sha256_update(ctx, datavec[i].iov_base, datavec[i].iov_len);
+ }
+ libssh2_sha256_final(ctx, hash);
+
+ ret = _libssh2_rsa_sha2_sign(session, rsactx, hash, SHA256_DIGEST_LENGTH,
+ signature, signature_len);
+ if(ret) {
+ return -1;
+ }
+
+ return 0;
+#endif
+}
+
+/*
+ * hostkey_method_ssh_rsa_sha2_512_sig_verify
+ *
+ * Verify signature created by remote
+ */
+
+static int
+hostkey_method_ssh_rsa_sha2_512_sig_verify(LIBSSH2_SESSION * session,
+ const unsigned char *sig,
+ size_t sig_len,
+ const unsigned char *m,
+ size_t m_len, void **abstract)
+{
+ libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
+ (void) session;
+
+ /* Skip past keyname_len(4) + keyname(12){"rsa-sha2-512"} +
+ signature_len(4) */
+ if(sig_len < 20)
+ return -1;
+
+ sig += 20;
+ sig_len -= 20;
+ return _libssh2_rsa_sha2_verify(rsactx, SHA512_DIGEST_LENGTH, sig,
+ sig_len, m, m_len);
+}
+
+
+/*
+ * hostkey_method_ssh_rsa_sha2_512_signv
+ *
+ * Construct a signature from an array of vectors
+ */
+static int
+hostkey_method_ssh_rsa_sha2_512_signv(LIBSSH2_SESSION * session,
+ unsigned char **signature,
+ size_t *signature_len,
+ int veccount,
+ const struct iovec datavec[],
+ void **abstract)
+{
+ libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
+
+#ifdef _libssh2_rsa_sha2_512_signv
+ return _libssh2_rsa_sha2_512_signv(session, signature, signature_len,
+ veccount, datavec, rsactx);
+#else
+ int ret;
+ int i;
+ unsigned char hash[SHA512_DIGEST_LENGTH];
+ libssh2_sha512_ctx ctx;
+
+ libssh2_sha512_init(&ctx);
+ for(i = 0; i < veccount; i++) {
+ libssh2_sha512_update(ctx, datavec[i].iov_base, datavec[i].iov_len);
+ }
+ libssh2_sha512_final(ctx, hash);
+
+ ret = _libssh2_rsa_sha2_sign(session, rsactx, hash, SHA512_DIGEST_LENGTH,
+ signature, signature_len);
+ if(ret) {
+ return -1;
+ }
+
+ return 0;
+#endif
+}
+
+#endif /* LIBSSH2_RSA_SHA2 */
+
+
+/*
* hostkey_method_ssh_rsa_dtor
*
* Shutdown the hostkey
@@ -260,6 +419,35 @@ static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa = {
NULL, /* encrypt */
hostkey_method_ssh_rsa_dtor,
};
+
+#if LIBSSH2_RSA_SHA2
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa_sha2_256 = {
+ "rsa-sha2-256",
+ SHA256_DIGEST_LENGTH,
+ hostkey_method_ssh_rsa_init,
+ hostkey_method_ssh_rsa_initPEM,
+ hostkey_method_ssh_rsa_initPEMFromMemory,
+ hostkey_method_ssh_rsa_sha2_256_sig_verify,
+ hostkey_method_ssh_rsa_sha2_256_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_rsa_dtor,
+};
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa_sha2_512 = {
+ "rsa-sha2-512",
+ SHA512_DIGEST_LENGTH,
+ hostkey_method_ssh_rsa_init,
+ hostkey_method_ssh_rsa_initPEM,
+ hostkey_method_ssh_rsa_initPEMFromMemory,
+ hostkey_method_ssh_rsa_sha2_512_sig_verify,
+ hostkey_method_ssh_rsa_sha2_512_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_rsa_dtor,
+};
+
+#endif /* LIBSSH2_RSA_SHA2 */
+
#endif /* LIBSSH2_RSA */
#if LIBSSH2_DSA
@@ -1041,6 +1229,10 @@ static const LIBSSH2_HOSTKEY_METHOD *hostkey_methods[] = {
&hostkey_method_ssh_ed25519,
#endif
#if LIBSSH2_RSA
+#if LIBSSH2_RSA_SHA2
+ &hostkey_method_ssh_rsa_sha2_512,
+ &hostkey_method_ssh_rsa_sha2_256,
+#endif /* LIBSSH2_RSA_SHA2 */
&hostkey_method_ssh_rsa,
#endif /* LIBSSH2_RSA */
#if LIBSSH2_DSA
diff --git a/src/kex.c b/src/kex.c
index c300ecb..8f02808 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -3026,6 +3026,17 @@ kex_method_ssh_curve25519_sha256 = {
};
#endif
+/* this kex method signals that client can receive extensions
+ * as described in https://datatracker.ietf.org/doc/html/rfc8308
+*/
+
+static const LIBSSH2_KEX_METHOD
+kex_method_extension_negotiation = {
+ "ext-info-c",
+ NULL,
+ 0,
+};
+
static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = {
#if LIBSSH2_ED25519
&kex_method_ssh_curve25519_sha256,
@@ -3043,6 +3054,7 @@ static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = {
&kex_method_diffie_helman_group14_sha1,
&kex_method_diffie_helman_group1_sha1,
&kex_method_diffie_helman_group_exchange_sha1,
+ &kex_method_extension_negotiation,
NULL
};
@@ -3978,6 +3990,11 @@ libssh2_session_method_pref(LIBSSH2_SESSION * session, int method_type,
mlist = NULL;
break;
+ case LIBSSH2_METHOD_SIGN_ALGO:
+ prefvar = &session->sign_algo_prefs;
+ mlist = NULL;
+ break;
+
default:
return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
"Invalid parameter specified for method_type");
@@ -4073,6 +4090,11 @@ LIBSSH2_API int libssh2_session_supported_algs(LIBSSH2_SESSION* session,
_libssh2_comp_methods(session);
break;
+ case LIBSSH2_METHOD_SIGN_ALGO:
+ /* no built-in supported list due to backend support */
+ mlist = NULL;
+ break;
+
default:
return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
"Unknown method type");
diff --git a/src/libgcrypt.c b/src/libgcrypt.c
index 0aff176..f6e9b64 100644
--- a/src/libgcrypt.c
+++ b/src/libgcrypt.c
@@ -664,4 +664,22 @@ _libssh2_dh_dtor(_libssh2_dh_ctx *dhctx)
*dhctx = NULL;
}
+/* _libssh2_supported_key_sign_algorithms
+ *
+ * Return supported key hash algo upgrades, see crypto.h
+ *
+ */
+
+const char *
+_libssh2_supported_key_sign_algorithms(LIBSSH2_SESSION *session,
+ unsigned char *key_method,
+ size_t key_method_len)
+{
+ (void)session;
+ (void)key_method;
+ (void)key_method_len;
+
+ return NULL;
+}
+
#endif /* LIBSSH2_LIBGCRYPT */
diff --git a/src/libgcrypt.h b/src/libgcrypt.h
index 298c65e..95876b9 100644
--- a/src/libgcrypt.h
+++ b/src/libgcrypt.h
@@ -55,6 +55,7 @@
#define LIBSSH2_3DES 1
#define LIBSSH2_RSA 1
+#define LIBSSH2_RSA_SHA2 0
#define LIBSSH2_DSA 1
#define LIBSSH2_ECDSA 0
#define LIBSSH2_ED25519 0
diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h
index da488b7..aff791e 100644
--- a/src/libssh2_priv.h
+++ b/src/libssh2_priv.h
@@ -640,6 +640,13 @@ struct _LIBSSH2_SESSION
unsigned char server_hostkey_sha256[SHA256_DIGEST_LENGTH];
int server_hostkey_sha256_valid;
+ /* public key algorithms accepted as comma separated list */
+ char *server_sign_algorithms;
+ size_t server_sign_algorithms_len;
+
+ /* key signing algorithm preferences -- NULL yields server order */
+ char *sign_algo_prefs;
+
/* (remote as source of data -- packet_read ) */
libssh2_endpoint_data remote;
@@ -1006,6 +1013,7 @@ _libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, ...)
#define SSH_MSG_DEBUG 4
#define SSH_MSG_SERVICE_REQUEST 5
#define SSH_MSG_SERVICE_ACCEPT 6
+#define SSH_MSG_EXT_INFO 7
#define SSH_MSG_KEXINIT 20
#define SSH_MSG_NEWKEYS 21
diff --git a/src/mbedtls.c b/src/mbedtls.c
index 4629ce4..dc76ef5 100644
--- a/src/mbedtls.c
+++ b/src/mbedtls.c
@@ -1247,5 +1247,24 @@ _libssh2_mbedtls_ecdsa_free(libssh2_ecdsa_ctx *ctx)
mbedtls_free(ctx);
}
+
+/* _libssh2_supported_key_sign_algorithms
+ *
+ * Return supported key hash algo upgrades, see crypto.h
+ *
+ */
+
+const char *
+_libssh2_supported_key_sign_algorithms(LIBSSH2_SESSION *session,
+ unsigned char *key_method,
+ size_t key_method_len)
+{
+ (void)session;
+ (void)key_method;
+ (void)key_method_len;
+
+ return NULL;
+}
+
#endif /* LIBSSH2_ECDSA */
#endif /* LIBSSH2_MBEDTLS */
diff --git a/src/mbedtls.h b/src/mbedtls.h
index 671932c..0450113 100644
--- a/src/mbedtls.h
+++ b/src/mbedtls.h
@@ -71,6 +71,7 @@
#define LIBSSH2_3DES 1
#define LIBSSH2_RSA 1
+#define LIBSSH2_RSA_SHA2 0
#define LIBSSH2_DSA 0
#ifdef MBEDTLS_ECDSA_C
# define LIBSSH2_ECDSA 1
diff --git a/src/openssl.c b/src/openssl.c
index 7a6810f..72a85b3 100644
--- a/src/openssl.c
+++ b/src/openssl.c
@@ -154,21 +154,57 @@ _libssh2_rsa_new(libssh2_rsa_ctx ** rsa,
}
int
-_libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsactx,
+_libssh2_rsa_sha2_verify(libssh2_rsa_ctx * rsactx,
+ size_t hash_len,
const unsigned char *sig,
unsigned long sig_len,
const unsigned char *m, unsigned long m_len)
{
- unsigned char hash[SHA_DIGEST_LENGTH];
int ret;
+ int nid_type;
+ unsigned char *hash = malloc(hash_len);
+ if(hash == NULL)
+ return -1;
+
+ if(hash_len == SHA_DIGEST_LENGTH) {
+ nid_type = NID_sha1;
+ ret = _libssh2_sha1(m, m_len, hash);
+ }
+ else if(hash_len == SHA256_DIGEST_LENGTH) {
+ nid_type = NID_sha256;
+ ret = _libssh2_sha256(m, m_len, hash);
+
+ }
+ else if(hash_len == SHA512_DIGEST_LENGTH) {
+ nid_type = NID_sha512;
+ ret = _libssh2_sha512(m, m_len, hash);
+ }
+ else
+ ret = -1; /* unsupported digest */
- if(_libssh2_sha1(m, m_len, hash))
+ if(ret != 0) {
+ free(hash);
return -1; /* failure */
- ret = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH,
+ }
+
+ ret = RSA_verify(nid_type, hash, hash_len,
(unsigned char *) sig, sig_len, rsactx);
+
+ free(hash);
+
return (ret == 1) ? 0 : -1;
}
+int
+_libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsactx,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m, unsigned long m_len)
+{
+ return _libssh2_rsa_sha2_verify(rsactx, SHA_DIGEST_LENGTH, sig, sig_len, m,
+ m_len);
+}
+
#if LIBSSH2_DSA
int
_libssh2_dsa_new(libssh2_dsa_ctx ** dsactx,
@@ -1876,7 +1912,7 @@ _libssh2_ed25519_new_public(libssh2_ed25519_ctx ** ed_ctx,
int
-_libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session,
+_libssh2_rsa_sha2_sign(LIBSSH2_SESSION * session,
libssh2_rsa_ctx * rsactx,
const unsigned char *hash,
size_t hash_len,
@@ -1893,7 +1929,17 @@ _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session,
return -1;
}
- ret = RSA_sign(NID_sha1, hash, hash_len, sig, &sig_len, rsactx);
+ if(hash_len == SHA_DIGEST_LENGTH)
+ ret = RSA_sign(NID_sha1, hash, hash_len, sig, &sig_len, rsactx);
+ else if(hash_len == SHA256_DIGEST_LENGTH)
+ ret = RSA_sign(NID_sha256, hash, hash_len, sig, &sig_len, rsactx);
+ else if(hash_len == SHA512_DIGEST_LENGTH)
+ ret = RSA_sign(NID_sha512, hash, hash_len, sig, &sig_len, rsactx);
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unsupported hash digest length");
+ ret = -1;
+ }
if(!ret) {
LIBSSH2_FREE(session, sig);
@@ -1906,6 +1952,19 @@ _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session,
return 0;
}
+
+int
+_libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session,
+ libssh2_rsa_ctx * rsactx,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature, size_t *signature_len)
+{
+ return _libssh2_rsa_sha2_sign(session, rsactx, hash, hash_len,
+ signature, signature_len);
+}
+
+
#if LIBSSH2_DSA
int
_libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx,
@@ -3283,4 +3342,27 @@ _libssh2_dh_dtor(_libssh2_dh_ctx *dhctx)
*dhctx = NULL;
}
+/* _libssh2_supported_key_sign_algorithms
+ *
+ * Return supported key hash algo upgrades, see crypto.h
+ *
+ */
+
+const char *
+_libssh2_supported_key_sign_algorithms(LIBSSH2_SESSION *session,
+ unsigned char *key_method,
+ size_t key_method_len)
+{
+ (void)session;
+
+#if LIBSSH2_RSA_SHA2
+ if(key_method_len == 7 &&
+ memcmp(key_method, "ssh-rsa", key_method_len) == 0) {
+ return "rsa-sha2-512,rsa-sha2-256,ssh-rsa";
+ }
+#endif
+
+ return NULL;
+}
+
#endif /* LIBSSH2_OPENSSL */
diff --git a/src/openssl.h b/src/openssl.h
index 658b040..2a002b4 100644
--- a/src/openssl.h
+++ b/src/openssl.h
@@ -64,8 +64,10 @@
#ifdef OPENSSL_NO_RSA
# define LIBSSH2_RSA 0
+# define LIBSSH2_RSA_SHA2 0
#else
# define LIBSSH2_RSA 1
+# define LIBSSH2_RSA_SHA2 1
#endif
#ifdef OPENSSL_NO_DSA
diff --git a/src/packet.c b/src/packet.c
index 04937d6..686be5c 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -616,6 +616,75 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
return 0;
/*
+ byte SSH_MSG_EXT_INFO
+ uint32 nr-extensions
+ [repeat "nr-extensions" times]
+ string extension-name [RFC8308]
+ string extension-value (binary)
+ */
+
+ case SSH_MSG_EXT_INFO:
+ if(datalen >= 5) {
+ uint32_t nr_extensions = 0;
+ struct string_buf buf;
+ buf.data = (unsigned char *)data;
+ buf.dataptr = buf.data;
+ buf.len = datalen;
+ buf.dataptr += 1; /* advance past type */
+
+ if(_libssh2_get_u32(&buf, &nr_extensions) != 0) {
+ rc = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Invalid extension info received");
+ }
+
+ while(rc == 0 && nr_extensions > 0) {
+
+ size_t name_len = 0;
+ size_t value_len = 0;
+ unsigned char *name = NULL;
+ unsigned char *value = NULL;
+
+ nr_extensions -= 1;
+
+ _libssh2_get_string(&buf, &name, &name_len);
+ _libssh2_get_string(&buf, &value, &value_len);
+
+ if(name != NULL && value != NULL) {
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_KEX,
+ "Server to Client extension %.*s: %.*s",
+ name_len, name, value_len, value);
+ }
+
+ if(name_len == 15 &&
+ memcmp(name, "server-sig-algs", 15) == 0) {
+ if(session->server_sign_algorithms) {
+ LIBSSH2_FREE(session,
+ session->server_sign_algorithms);
+ }
+
+ session->server_sign_algorithms =
+ LIBSSH2_ALLOC(session,
+ value_len);
+
+ if(session->server_sign_algorithms) {
+ session->server_sign_algorithms_len = value_len;
+ memcpy(session->server_sign_algorithms,
+ value, value_len);
+ }
+ else {
+ rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "memory for server sign algo");
+ }
+ }
+ }
+ }
+
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return rc;
+
+ /*
byte SSH_MSG_GLOBAL_REQUEST
string request name in US-ASCII only
boolean want reply
diff --git a/src/userauth.c b/src/userauth.c
index 4442eee..988dc17 100644
--- a/src/userauth.c
+++ b/src/userauth.c
@@ -1086,6 +1086,148 @@ static int plain_method_len(const char *method, size_t method_len)
return method_len;
}
+/**
+ * @function _libssh2_key_sign_algorithm
+ * @abstract Upgrades the algorithm used for public key signing RFC 8332
+ * @discussion Based on the incoming key_method value, this function
+ * will upgrade the key method input based on user preferences,
+ * server support algos and crypto backend support
+ * @related _libssh2_supported_key_sign_algorithms()
+ * @param key_method current key method, usually the default key sig method
+ * @param key_method_len length of the key method buffer
+ * @result error code or zero on success
+ */
+
+static int
+_libssh2_key_sign_algorithm(LIBSSH2_SESSION *session,
+ unsigned char **key_method,
+ size_t *key_method_len)
+{
+ const char *s = NULL;
+ const char *a = NULL;
+ const char *match = NULL;
+ const char *p = NULL;
+ const char *f = NULL;
+ char *i = NULL;
+ int p_len = 0;
+ int f_len = 0;
+ int rc = 0;
+ int match_len = 0;
+ char *filtered_algs = NULL;
+
+ const char *supported_algs =
+ _libssh2_supported_key_sign_algorithms(session,
+ *key_method,
+ *key_method_len);
+
+ if(supported_algs == NULL || session->server_sign_algorithms == NULL) {
+ /* no upgrading key algorithm supported, do nothing */
+ return LIBSSH2_ERROR_NONE;
+ }
+
+ filtered_algs = LIBSSH2_ALLOC(session, strlen(supported_algs) + 1);
+ if(!filtered_algs) {
+ rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate filtered algs");
+ return rc;
+ }
+
+ s = session->server_sign_algorithms;
+ i = filtered_algs;
+
+ /* this walks the server algo list and the supported algo list and creates
+ a filtered list that includes matches */
+
+ while(s && *s) {
+ p = strchr(s, ',');
+ p_len = p ? (p - s) : (int) strlen(s);
+ a = supported_algs;
+
+ while(a && *a) {
+ f = strchr(a, ',');
+ f_len = f ? (f - a) : (int) strlen(a);
+
+ if(f_len == p_len && memcmp(a, s, p_len)) {
+
+ if(i != filtered_algs) {
+ memcpy(i, ",", 1);
+ i += 1;
+ }
+
+ memcpy(i, s, p_len);
+ i += p_len;
+ }
+
+ a = f ? (f + 1) : NULL;
+ }
+
+ s = p ? (p + 1) : NULL;
+ }
+
+ filtered_algs[i - filtered_algs] = '\0';
+
+ if(session->sign_algo_prefs) {
+ s = session->sign_algo_prefs;
+ }
+ else {
+ s = supported_algs;
+ }
+
+ /* now that we have the possible supported algos, match based on the prefs
+ or what is supported by the crypto backend, look for a match */
+
+ while(s && *s && !match) {
+ p = strchr(s, ',');
+ p_len = p ? (p - s) : (int) strlen(s);
+ a = filtered_algs;
+
+ while(a && *a && !match) {
+ f = strchr(a, ',');
+ f_len = f ? (f - a) : (int) strlen(a);
+
+ if(f_len == p_len && memcmp(a, s, p_len)) {
+ /* found a match, upgrade key method */
+ match = s;
+ match_len = p_len;
+ }
+ else {
+ a = f ? (f + 1) : NULL;
+ }
+ }
+
+ s = p ? (p + 1) : NULL;
+ }
+
+ if(match != NULL) {
+ if(*key_method)
+ LIBSSH2_FREE(session, *key_method);
+
+ *key_method = LIBSSH2_ALLOC(session, match_len);
+ if(key_method) {
+ memcpy(*key_method, match, match_len);
+ *key_method_len = match_len;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Signing using %.*s", match_len, match);
+ }
+ else {
+ *key_method_len = 0;
+ rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate key method upgrade");
+ }
+ }
+ else {
+ /* no match was found */
+ rc = _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE,
+ "No signing signature matched");
+ }
+
+ if(filtered_algs)
+ LIBSSH2_FREE(session, filtered_algs);
+
+ return rc;
+}
+
int
_libssh2_userauth_publickey(LIBSSH2_SESSION *session,
const char *username,
@@ -1144,15 +1286,14 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session,
memcpy(session->userauth_pblc_method, pubkeydata + 4,
session->userauth_pblc_method_len);
}
- /*
- * The length of the method name read from plaintext prefix in the
- * file must match length embedded in the key.
- * TODO: The data should match too but we don't check that. Should we?
- */
- else if(session->userauth_pblc_method_len !=
- _libssh2_ntohu32(pubkeydata))
- return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
- "Invalid public key");
+
+ /* upgrade key key signing algo needed */
+ rc = _libssh2_key_sign_algorithm(session,
+ &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len);
+
+ if(rc)
+ return rc;
/*
* 45 = packet_type(1) + username_len(4) + servicename_len(4) +
diff --git a/src/wincng.c b/src/wincng.c
index 9ae8dde..58e2251 100644
--- a/src/wincng.c
+++ b/src/wincng.c
@@ -2591,4 +2591,22 @@ fb:
return _libssh2_wincng_bignum_mod_exp(secret, f, dhctx->bn, p);
}
+/* _libssh2_supported_key_sign_algorithms
+ *
+ * Return supported key hash algo upgrades, see crypto.h
+ *
+ */
+
+const char *
+_libssh2_supported_key_sign_algorithms(LIBSSH2_SESSION *session,
+ unsigned char *key_method,
+ size_t key_method_len)
+{
+ (void)session;
+ (void)key_method;
+ (void)key_method_len;
+
+ return NULL;
+}
+
#endif /* LIBSSH2_WINCNG */
diff --git a/src/wincng.h b/src/wincng.h
index eaf6f90..538cc43 100644
--- a/src/wincng.h
+++ b/src/wincng.h
@@ -63,6 +63,7 @@
#define LIBSSH2_3DES 1
#define LIBSSH2_RSA 1
+#define LIBSSH2_RSA_SHA2 0
#define LIBSSH2_DSA 1
#define LIBSSH2_ECDSA 0
#define LIBSSH2_ED25519 0
--
1.8.3.1