diff --git a/docs/HACKING-CRYPTO b/docs/HACKING-CRYPTO index ca947728..d0173553 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 diff --git a/src/crypto.h b/src/crypto.h index f512d603..efaf1564 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, diff --git a/src/hostkey.c b/src/hostkey.c index d126b611..1a0011ce 100644 --- a/src/hostkey.c +++ b/src/hostkey.c @@ -227,6 +227,146 @@ hostkey_method_ssh_rsa_signv(LIBSSH2_SESSION * session, #endif } +/* + * 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 * @@ -260,6 +400,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 @@ -1043,6 +1212,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/libgcrypt.h b/src/libgcrypt.h index 298c65ed..95876b96 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/mbedtls.h b/src/mbedtls.h index 671932c5..0450113f 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 7a6810f1..537031e1 100644 --- a/src/openssl.c +++ b/src/openssl.c @@ -153,20 +153,56 @@ _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, return 0; } +int +_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) +{ + 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(ret != 0) { + free(hash); + return -1; /* failure */ + } + + 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) { - unsigned char hash[SHA_DIGEST_LENGTH]; - int ret; - - if(_libssh2_sha1(m, m_len, hash)) - return -1; /* failure */ - ret = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH, - (unsigned char *) sig, sig_len, rsactx); - return (ret == 1) ? 0 : -1; + return _libssh2_rsa_sha2_verify(rsactx, SHA_DIGEST_LENGTH, sig, sig_len, m, + m_len); } #if LIBSSH2_DSA @@ -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, diff --git a/src/openssl.h b/src/openssl.h index 658b040d..2a002b41 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/os400qc3.h b/src/os400qc3.h index e3602d9f..7bcef233 100644 --- a/src/os400qc3.h +++ b/src/os400qc3.h @@ -175,6 +175,7 @@ #define LIBSSH2_3DES 1 #define LIBSSH2_RSA 1 +#define LIBSSH2_RSA_SHA2 0 #define LIBSSH2_DSA 0 #define LIBSSH2_ECDSA 0 #define LIBSSH2_ED25519 0 diff --git a/src/userauth.c b/src/userauth.c index eff8a296..dda79b55 100644 --- a/src/userauth.c +++ b/src/userauth.c @@ -549,6 +549,24 @@ libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username, return rc; } +/* + * upgrade_publickey_method + * + * Change method from ssh-rsa to rsa-sha2-256 is RSA SHA2 is supported. + */ +static void upgrade_publickey_method(LIBSSH2_SESSION * session, + unsigned char **method, + size_t *method_len) { +#if LIBSSH2_RSA_SHA2 + if(*method_len == 7 && memcmp(*method, "ssh-rsa", 7) == 0) { + LIBSSH2_FREE(session, *method); + *method_len = 12; + *method = LIBSSH2_ALLOC(session, 12); + memcpy(*method, "rsa-sha2-256", 12); + } +#endif +} + static int memory_read_publickey(LIBSSH2_SESSION * session, unsigned char **method, size_t *method_len, @@ -615,6 +633,9 @@ memory_read_publickey(LIBSSH2_SESSION * session, unsigned char **method, *method = pubkey; *method_len = sp1 - pubkey - 1; + /* upgrade to sha2 for rsa keys when supported */ + upgrade_publickey_method(session, method, method_len); + *pubkeydata = tmp; *pubkeydata_len = tmp_len; @@ -717,6 +738,9 @@ file_read_publickey(LIBSSH2_SESSION * session, unsigned char **method, * it a wash */ *method = pubkey; *method_len = sp1 - pubkey - 1; + + /* upgrade to sha2 for rsa keys when supported */ + upgrade_publickey_method(session, method, method_len); *pubkeydata = tmp; *pubkeydata_len = tmp_len; @@ -1219,10 +1243,19 @@ _libssh2_userauth_publickey(LIBSSH2_SESSION *session, * 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"); + _libssh2_ntohu32(pubkeydata)) { + + // we accept mismatch if public key is "ssh-rsa" and + // method has prefix "rsa-sha2-" to satisfy RFC 8332 + if(session->userauth_pblc_method_len < 9 || + _libssh2_ntohu32(pubkeydata) != 7 || + memcmp(pubkeydata + 4, "ssh-rsa", 7) || + memcmp(session->userauth_pblc_method, "rsa-sha2-", 9)) { + return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, + "Invalid public key"); + } + } /* * 45 = packet_type(1) + username_len(4) + servicename_len(4) + * service_name(14)"ssh-connection" + authmethod_len(4) + diff --git a/src/wincng.h b/src/wincng.h index eaf6f905..538cc431 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