From feef0131af67441ff10d6d53919613509a0433c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=93=E6=B4=AA=E6=98=8C?= Date: Fri, 17 Jan 2025 13:18:16 +0800 Subject: [PATCH] add gm sm2-sm3 --- 0001-feat-SM2-SM3.patch | 1142 +++++++++++++++++++++++++++++++++++++++ libssh2.spec | 9 +- 2 files changed, 1150 insertions(+), 1 deletion(-) create mode 100644 0001-feat-SM2-SM3.patch diff --git a/0001-feat-SM2-SM3.patch b/0001-feat-SM2-SM3.patch new file mode 100644 index 0000000..8d376f8 --- /dev/null +++ b/0001-feat-SM2-SM3.patch @@ -0,0 +1,1142 @@ +From 7ce70c61f13cbe991258e9ca1c73f90d4f1415bc Mon Sep 17 00:00:00 2001 +From: Super User +Date: Thu, 16 Jan 2025 18:15:37 +0800 +Subject: [PATCH] add sm2-sm3 + +--- + include/libssh2.h | 1 + + src/crypto.h | 20 ++ + src/hostkey.c | 159 +++++++++++++++ + src/kex.c | 492 +++++++++++++++++++++++++++++++++++++++++++++ + src/libssh2_priv.h | 15 ++ + src/openssl.c | 166 +++++++++++++++ + src/openssl.h | 16 +- + src/userauth.c | 104 ++++++++++ + 8 files changed, 972 insertions(+), 1 deletion(-) + +diff --git a/include/libssh2.h b/include/libssh2.h +index 97ac589..f655e28 100644 +--- a/include/libssh2.h ++++ b/include/libssh2.h +@@ -466,6 +466,7 @@ typedef struct _LIBSSH2_POLLFD { + #define LIBSSH2_HOSTKEY_HASH_MD5 1 + #define LIBSSH2_HOSTKEY_HASH_SHA1 2 + #define LIBSSH2_HOSTKEY_HASH_SHA256 3 ++#define LIBSSH2_HOSTKEY_HASH_SM3 4 + + /* Hostkey Types */ + #define LIBSSH2_HOSTKEY_TYPE_UNKNOWN 0 +diff --git a/src/crypto.h b/src/crypto.h +index 4d3d49c..a6a1b5d 100644 +--- a/src/crypto.h ++++ b/src/crypto.h +@@ -121,6 +121,13 @@ + #define LIBSSH2_ED25519_PRIVATE_KEY_LEN 64 + #define LIBSSH2_ED25519_SIG_LEN 64 + ++/*GM SM2*/ ++#define SM3_DIGEST_LENGTH 32 ++#define KLEN 32 ++#define RANDOMLEN 8 ++#define CATRANDOMLEN 16 ++#define SM4_KEY_LENGTH 16 ++ + #if LIBSSH2_RSA + int _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, + const unsigned char *edata, +@@ -334,6 +341,19 @@ _libssh2_ed25519_new_private_frommemory_sk(libssh2_ed25519_ctx **ed_ctx, + + #endif /* LIBSSH2_ED25519 */ + ++ /* GM SM2 SM4 */ ++int _libssh2_sm4_encrypt(unsigned char *key, ++ unsigned char *data, ++ int d_len, ++ unsigned char *cipher); ++int _libssh2_sm2_new_public(libssh2_sm2_ctx **sm2_ctx, ++ const unsigned char *key, ++ const size_t key_len); ++int _libssh2_sm2_verify(libssh2_sm2_ctx *pkey, ++ const unsigned char *sig, ++ size_t sig_len, ++ const unsigned char *m, ++ size_t m_len); + + int _libssh2_cipher_init(_libssh2_cipher_ctx * h, + _libssh2_cipher_type(algo), +diff --git a/src/hostkey.c b/src/hostkey.c +index f382367..9fc6d43 100644 +--- a/src/hostkey.c ++++ b/src/hostkey.c +@@ -1271,8 +1271,162 @@ static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_ed25519_cert = { + + #endif /* LIBSSH2_ED25519 */ + ++/* *********** ++ * GM SM2 * ++ *********** */ ++static int hostkey_method_ssh_sm2_dtor(LIBSSH2_SESSION * session, ++ void **abstract); ++ ++static int ++hostkey_method_ssh_sm2_init(LIBSSH2_SESSION * session, ++ const unsigned char *hostkey_data, ++ size_t hostkey_data_len, ++ void **abstract) ++{ ++ libssh2_sm2_ctx *ctx = NULL; ++ ++ if(*abstract) { ++ hostkey_method_ssh_sm2_dtor(session, abstract); ++ *abstract = NULL; ++ } ++ ++ if(_libssh2_sm2_new_public(&ctx, hostkey_data, hostkey_data_len) != 0) { ++ return -1; ++ } ++ ++ *abstract = ctx; ++ ++ return 0; ++} ++ ++ /* ++ * hostkey_method_ssh_sm2_initPEM ++ * Load a Private Key from a PEM file ++ */ ++static int ++hostkey_method_ssh_sm2_initPEM(LIBSSH2_SESSION * session, ++ const char *privkeyfile, ++ unsigned const char *passphrase, ++ void **abstract) ++{ ++ int ret = 0; ++ (void)privkeyfile; ++ (void)passphrase; ++ ++ if(*abstract) { ++ hostkey_method_ssh_sm2_dtor(session, abstract); ++ *abstract = NULL; ++ } ++ ++ return ret; ++} ++ ++ ++/* ++ * hostkey_method_ssh_sm2_initPEMFromMemory ++ * Load a Private Key from memory ++ */ ++static int ++hostkey_method_ssh_sm2_initPEMFromMemory(LIBSSH2_SESSION * session, ++ const char *privkeyfiledata, ++ size_t privkeyfiledata_len, ++ unsigned const char *passphrase, ++ void **abstract) ++{ ++ int ret = 0; ++ (void)privkeyfiledata; ++ (void)privkeyfiledata_len; ++ (void)passphrase; ++ ++ if(abstract && *abstract) { ++ hostkey_method_ssh_sm2_dtor(session, abstract); ++ *abstract = NULL; ++ } ++ ++ return ret; ++} ++ ++/* ++ * hostkey_method_ssh_sm2_sig_verify ++ * Verify signature created by remote ++ */ ++static int ++hostkey_method_ssh_sm2_sig_verify(LIBSSH2_SESSION * session, ++ const unsigned char *sig, ++ size_t sig_len, ++ const unsigned char *m, ++ size_t m_len, void **abstract) ++{ ++ libssh2_sm2_ctx *ctx = (libssh2_sm2_ctx *) (*abstract); ++ ++ /* Skip past = keyname_len(4) + keyname(7){"ssh-sm2"} + ++ signature_len(4) = 15*/ ++ if(sig_len != 79) { ++ return _libssh2_error(session, LIBSSH2_ERROR_PROTO, ++ "Invalid DSS signature length"); ++ } ++ ++ sig += 15; ++ sig_len -= 15; ++ ++ return _libssh2_sm2_verify(ctx, sig, sig_len, m, m_len); ++} ++ ++/* ++ * hostkey_method_ssh_sm2_signv ++ * Construct a signature from an array of vectors ++ */ ++static int ++hostkey_method_ssh_sm2_signv(LIBSSH2_SESSION * session, ++ unsigned char **signature, ++ size_t *signature_len, ++ int veccount, ++ const struct iovec datavec[], ++ void **abstract) ++{ ++ (void)session; ++ (void)signature; ++ (void)signature_len; ++ (void)datavec; ++ (void)abstract; ++ ++ if(veccount != 1) { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * hostkey_method_ssh_sm2_dtor ++ * Shutdown the hostkey by freeing key context ++ */ ++static int ++hostkey_method_ssh_sm2_dtor(LIBSSH2_SESSION * session, void **abstract) ++{ ++ libssh2_sm2_ctx *keyctx = (libssh2_sm2_ctx*) (*abstract); ++ (void)session; ++ ++ _libssh2_sm2_free(keyctx); ++ *abstract = NULL; ++ ++ return 0; ++} ++ ++static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_sm2 = { ++ "ssh-sm2", ++ SM3_DIGEST_LENGTH, ++ hostkey_method_ssh_sm2_init, ++ hostkey_method_ssh_sm2_initPEM, ++ hostkey_method_ssh_sm2_initPEMFromMemory, ++ hostkey_method_ssh_sm2_sig_verify, ++ hostkey_method_ssh_sm2_signv, ++ NULL, /* encrypt */ ++ hostkey_method_ssh_sm2_dtor, ++}; + + static const LIBSSH2_HOSTKEY_METHOD *hostkey_methods[] = { ++ &hostkey_method_ssh_sm2, + #if LIBSSH2_ECDSA + &hostkey_method_ecdsa_ssh_nistp256, + &hostkey_method_ecdsa_ssh_nistp384, +@@ -1336,6 +1490,11 @@ libssh2_hostkey_hash(LIBSSH2_SESSION * session, int hash_type) + ? (char *) session->server_hostkey_sha256 + : NULL; + break; ++ case LIBSSH2_HOSTKEY_HASH_SM3: ++ return (session->server_hostkey_sm3_valid) ++ ? (char *) session->server_hostkey_sm3 ++ : NULL; ++ break; + default: + return NULL; + } +diff --git a/src/kex.c b/src/kex.c +index d4034a0..320d823 100644 +--- a/src/kex.c ++++ b/src/kex.c +@@ -97,6 +97,22 @@ do { \ + } \ + } while(0) + ++#define LIBSSH2_KEX_METHOD_SM3_VALUE_HASH(value, version) \ ++ do { \ ++ libssh2_sm3_ctx ctx_sm3; \ ++ if (!(value)) { \ ++ value = LIBSSH2_ALLOC(session, SM3_DIGEST_LENGTH); \ ++ } \ ++ if (value) { \ ++ libssh2_sm3_init(&ctx_sm3); \ ++ libssh2_sm3_update(ctx_sm3, exchange_state->K, KLEN); \ ++ libssh2_sm3_update(ctx_sm3, hash, SM3_DIGEST_LENGTH); \ ++ libssh2_sm3_update(ctx_sm3, (version), 1); \ ++ libssh2_sm3_update(ctx_sm3, session->session_id, session->session_id_len); \ ++ libssh2_sm3_final(ctx_sm3, value); \ ++ } \ ++ } while (0) ++ + /*! + * @note The following are wrapper functions used by diffie_hellman_sha_algo(). + * TODO: Switch backend SHA macros to functions to allow function pointers +@@ -3026,6 +3042,481 @@ kex_method_ssh_curve25519_sha256 = { + }; + #endif + ++/* ++************** ++**GM SM2-SM3** ++************** ++*/ ++static void kex_method_sm2_cleanup(LIBSSH2_SESSION *session, ++ key_exchange_state_low_t *key_state) { ++ ++ if (key_state->data) { ++ LIBSSH2_FREE(session, key_state->data); ++ key_state->data = NULL; ++ } ++ ++ key_state->state = libssh2_NB_state_idle; ++ key_state->exchange_state.state = libssh2_NB_state_idle; ++} ++ ++static int sm2_sm3(LIBSSH2_SESSION *session, unsigned char *data, ++ size_t data_len, kmdhgGPshakex_state_t *exchange_state) { ++ int ret = 0; ++ int rc; ++ unsigned char random[CATRANDOMLEN] = {0}; ++ unsigned char *server_public_key, *server_host_key; ++ size_t server_public_key_len, hostkey_len; ++ ++ if (data_len < 5) { ++ return _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, ++ "Data is too short"); ++ } ++ ++ if (exchange_state->state == libssh2_NB_state_idle) { ++ /* Setup initial values */ ++ memset(exchange_state->K, 0, KLEN); ++ exchange_state->state = libssh2_NB_state_created; ++ } ++ ++ if (exchange_state->state == libssh2_NB_state_created) { ++ /* parse INIT reply data */ ++ struct string_buf buf; ++ unsigned char *randoms = NULL; ++ ++ if (data_len < 5) { ++ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO, ++ "Unexpected sm2 key length 1"); ++ goto clean_exit; ++ } ++ ++ buf.data = data; ++ buf.len = data_len; ++ buf.dataptr = buf.data; ++ buf.dataptr++; /* advance past packet type */ ++ ++ if (_libssh2_get_string(&buf, &server_host_key, &hostkey_len)) { ++ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO, ++ "Unexpected sm2 key length 2"); ++ goto clean_exit; ++ } ++ ++ session->server_hostkey_len = (uint32_t)hostkey_len; ++ session->server_hostkey = ++ LIBSSH2_ALLOC(session, session->server_hostkey_len); ++ if (!session->server_hostkey) { ++ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC, ++ "Unable to allocate memory for a copy " ++ "of the host sm2 key"); ++ goto clean_exit; ++ } ++ ++ memcpy(session->server_hostkey, server_host_key, ++ session->server_hostkey_len); ++ ++ { ++ libssh2_sm3_ctx fingerprint_ctx; ++ ++ if(!libssh2_sm3_init(&fingerprint_ctx)) { ++ libssh2_sm3_update(fingerprint_ctx, session->server_hostkey, ++ session->server_hostkey_len); ++ libssh2_sm3_final(fingerprint_ctx, ++ session->server_hostkey_sm3); ++ session->server_hostkey_sm3_valid = TRUE; ++ } ++ else { ++ session->server_hostkey_sm3_valid = FALSE; ++ } ++ } ++ ++ if (session->hostkey->init(session, session->server_hostkey, ++ session->server_hostkey_len, ++ &session->server_hostkey_abstract) != 0) { ++ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, ++ "Unable to initialize hostkey importer sm2"); ++ goto clean_exit; ++ } ++ ++ /* server public key Q_S */ ++ if (_libssh2_get_string(&buf, &server_public_key, &server_public_key_len)) { ++ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO, ++ "Unexpected sm2 key length"); ++ goto clean_exit; ++ } ++ ++ memcpy(session->sm4_key, server_public_key, server_public_key_len); ++ if (server_public_key_len != SM4_KEY_LENGTH) { ++ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, ++ "Unexpected sm2 server " ++ "public key length"); ++ goto clean_exit; ++ } ++ ++ /* Compute the shared secret K */ ++ rc = _libssh2_random(&exchange_state->K, KLEN); ++ if (rc) { ++ ret = _libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE, ++ "Unable to create sm2 shared secret"); ++ goto clean_exit; ++ } ++ ++ if (_libssh2_get_string(&buf, &randoms, NULL)) { ++ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO, ++ "Unexpected randoms length"); ++ goto clean_exit; ++ } ++ memcpy(session->randoms, randoms, RANDOMLEN); ++ ++ /* server signature */ ++ if (_libssh2_get_string(&buf, &exchange_state->h_sig, ++ &(exchange_state->h_sig_len))) { ++ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, ++ "Unexpected sm2 server sig length"); ++ goto clean_exit; ++ } ++ ++ memcpy(random, session->randomc, RANDOMLEN); ++ memcpy(random + RANDOMLEN, session->randoms, RANDOMLEN); ++ ++ rc = session->hostkey->sig_verify( ++ session, exchange_state->h_sig, exchange_state->h_sig_len, random, ++ CATRANDOMLEN, &session->server_hostkey_abstract); ++ ++ if (rc) { ++ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN, ++ "Unable to verify hostkey signature " ++ "sm2"); ++ goto clean_exit; ++ } ++ exchange_state->state = libssh2_NB_state_sent; ++ } ++ ++ if (exchange_state->state == libssh2_NB_state_sent) { ++ unsigned char hash[SM3_DIGEST_LENGTH] = {0}; ++ libssh2_sm3_ctx sm3_ctx; ++ libssh2_sm3_init(&sm3_ctx); ++ libssh2_sm3_update(sm3_ctx, session->randomc, RANDOMLEN); ++ libssh2_sm3_update(sm3_ctx, session->randoms, RANDOMLEN); ++ libssh2_sm3_update(sm3_ctx, server_public_key, server_public_key_len); ++ libssh2_sm3_update(sm3_ctx, exchange_state->K, KLEN); ++ libssh2_sm3_final(sm3_ctx, hash); ++ ++ if (!session->session_id) { ++ size_t digest_length = SM3_DIGEST_LENGTH; ++ session->session_id = LIBSSH2_ALLOC(session, digest_length); ++ if (!session->session_id) { ++ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC, ++ "Unable to allocate buffer for " ++ "sm2 digest"); ++ goto clean_exit; ++ } ++ memcpy(session->session_id, hash, digest_length); ++ session->session_id_len = (uint32_t)digest_length; ++ _libssh2_debug((session, LIBSSH2_TRACE_KEX, "session_id calculated")); ++ } ++ ++ /* Cleanup any existing cipher */ ++ if (session->local.crypt->dtor) { ++ session->local.crypt->dtor(session, &session->local.crypt_abstract); ++ } ++ /* Calculate IV/Secret/Key for each direction */ ++ if (session->local.crypt->init) { ++ unsigned char *iv = NULL, *secret = NULL; ++ int free_iv = 0, free_secret = 0; ++ ++ LIBSSH2_KEX_METHOD_SM3_VALUE_HASH(iv, "A"); ++ if (!iv) { ++ ret = -1; ++ goto clean_exit; ++ } ++ ++ LIBSSH2_KEX_METHOD_SM3_VALUE_HASH(secret, "C"); ++ if (!secret) { ++ LIBSSH2_FREE(session, iv); ++ ret = LIBSSH2_ERROR_KEX_FAILURE; ++ goto clean_exit; ++ } ++ if (session->local.crypt->init(session, session->local.crypt, iv, ++ &free_iv, secret, &free_secret, 1, ++ &session->local.crypt_abstract)) { ++ LIBSSH2_FREE(session, iv); ++ LIBSSH2_FREE(session, secret); ++ ret = LIBSSH2_ERROR_KEX_FAILURE; ++ goto clean_exit; ++ } ++ ++ if (free_iv) { ++ _libssh2_explicit_zero(iv, session->local.crypt->iv_len); ++ LIBSSH2_FREE(session, iv); ++ } ++ ++ if (free_secret) { ++ _libssh2_explicit_zero(secret, session->local.crypt->secret_len); ++ LIBSSH2_FREE(session, secret); ++ } ++ } ++ ++ _libssh2_debug( ++ (session, LIBSSH2_TRACE_KEX, "Client to Server IV and Key calculated")); ++ ++ if (session->remote.crypt->dtor) { ++ /* Cleanup any existing cipher */ ++ session->remote.crypt->dtor(session, &session->remote.crypt_abstract); ++ } ++ ++ if (session->remote.crypt->init) { ++ unsigned char *iv = NULL, *secret = NULL; ++ int free_iv = 0, free_secret = 0; ++ ++ LIBSSH2_KEX_METHOD_SM3_VALUE_HASH(iv, "B"); ++ if (!iv) { ++ ret = LIBSSH2_ERROR_KEX_FAILURE; ++ goto clean_exit; ++ } ++ LIBSSH2_KEX_METHOD_SM3_VALUE_HASH(secret, "D"); ++ if (!secret) { ++ LIBSSH2_FREE(session, iv); ++ ret = LIBSSH2_ERROR_KEX_FAILURE; ++ goto clean_exit; ++ } ++ if (session->remote.crypt->init(session, session->remote.crypt, iv, ++ &free_iv, secret, &free_secret, 0, ++ &session->remote.crypt_abstract)) { ++ LIBSSH2_FREE(session, iv); ++ LIBSSH2_FREE(session, secret); ++ ret = LIBSSH2_ERROR_KEX_FAILURE; ++ goto clean_exit; ++ } ++ ++ if (free_iv) { ++ _libssh2_explicit_zero(iv, session->remote.crypt->iv_len); ++ LIBSSH2_FREE(session, iv); ++ } ++ if (free_secret) { ++ _libssh2_explicit_zero(secret, session->remote.crypt->secret_len); ++ LIBSSH2_FREE(session, secret); ++ } ++ } ++ _libssh2_debug( ++ (session, LIBSSH2_TRACE_KEX, "Server to Client IV and Key calculated")); ++ ++ if (session->local.mac->dtor) { ++ session->local.mac->dtor(session, &session->local.mac_abstract); ++ } ++ if (session->local.mac->init) { ++ unsigned char *key = NULL; ++ int free_key = 0; ++ ++ LIBSSH2_KEX_METHOD_SM3_VALUE_HASH(key, "E"); ++ if (!key) { ++ ret = LIBSSH2_ERROR_KEX_FAILURE; ++ goto clean_exit; ++ } ++ session->local.mac->init(session, key, &free_key, ++ &session->local.mac_abstract); ++ if (free_key) { ++ _libssh2_explicit_zero(key, session->local.mac->key_len); ++ LIBSSH2_FREE(session, key); ++ } ++ } ++ _libssh2_debug( ++ (session, LIBSSH2_TRACE_KEX, "Client to Server HMAC Key calculated")); ++ if (session->remote.mac->dtor) { ++ session->remote.mac->dtor(session, &session->remote.mac_abstract); ++ } ++ ++ if (session->remote.mac->init) { ++ unsigned char *key = NULL; ++ int free_key = 0; ++ LIBSSH2_KEX_METHOD_SM3_VALUE_HASH(key, "F"); ++ if (!key) { ++ ret = LIBSSH2_ERROR_KEX_FAILURE; ++ goto clean_exit; ++ } ++ session->remote.mac->init(session, key, &free_key, ++ &session->remote.mac_abstract); ++ ++ if (free_key) { ++ _libssh2_explicit_zero(key, session->remote.mac->key_len); ++ LIBSSH2_FREE(session, key); ++ } ++ } ++ _libssh2_debug( ++ (session, LIBSSH2_TRACE_KEX, "Server to Client HMAC Key calculated")); ++ ++ /* Cleanup any existing compression */ ++ if (session->local.comp && session->local.comp->dtor) { ++ session->local.comp->dtor(session, 1, &session->local.comp_abstract); ++ } ++ ++ if (session->local.comp && session->local.comp->init) { ++ if (session->local.comp->init(session, 1, ++ &session->local.comp_abstract)) { ++ ret = LIBSSH2_ERROR_KEX_FAILURE; ++ goto clean_exit; ++ } ++ } ++ _libssh2_debug((session, LIBSSH2_TRACE_KEX, ++ "Client to Server compression initialized")); ++ ++ if (session->remote.comp && session->remote.comp->dtor) { ++ session->remote.comp->dtor(session, 0, &session->remote.comp_abstract); ++ } ++ ++ if (session->remote.comp && session->remote.comp->init) { ++ if (session->remote.comp->init(session, 0, ++ &session->remote.comp_abstract)) { ++ ret = LIBSSH2_ERROR_KEX_FAILURE; ++ goto clean_exit; ++ } ++ } ++ _libssh2_debug((session, LIBSSH2_TRACE_KEX, ++ "Server to Client compression initialized")); ++ exchange_state->state = libssh2_NB_state_sent2; ++ } ++ ++ if (exchange_state->state == libssh2_NB_state_sent2) { ++ } ++ ++clean_exit: ++ return ret; ++} ++ ++static int kex_method_sm2_key_exchange(LIBSSH2_SESSION *session, ++ key_exchange_state_low_t *key_state) { ++ int ret = 0; ++ int rc = 0; ++ ++ if (key_state->state == libssh2_NB_state_idle) { ++ key_state->public_key_oct = NULL; ++ key_state->state = libssh2_NB_state_created; ++ } ++ ++ if (key_state->state == libssh2_NB_state_created) { ++ unsigned char *s = NULL; ++ rc = strcmp(session->kex->name, "SM2-SM3"); ++ if (rc) { ++ ret = _libssh2_error(session, -1, "Unknown KEX sm2 type"); ++ goto clean_exit; ++ } ++ ++ _libssh2_random(session->randomc, RANDOMLEN); ++ ++ key_state->request[0] = SSH_MSG_KEX_REQUEST; ++ s = key_state->request + 1; ++ _libssh2_store_str(&s, (const char *)session->randomc, RANDOMLEN); ++ key_state->request_len = RANDOMLEN + 5; ++ ++ _libssh2_debug((session, LIBSSH2_TRACE_KEX, "Initiating SM2 SM3")); ++ ++ key_state->state = libssh2_NB_state_sent; ++ } ++ ++ if (key_state->state == libssh2_NB_state_sent) { ++ rc = _libssh2_transport_send(session, key_state->request, ++ key_state->request_len, NULL, 0); ++ if (rc == LIBSSH2_ERROR_EAGAIN) { ++ return rc; ++ } else if (rc) { ++ ret = _libssh2_error(session, rc, "Unable to send SSH_MSG_KEX_REQUEST"); ++ goto clean_exit; ++ } ++ ++ key_state->state = libssh2_NB_state_sent1; ++ } ++ ++ if (key_state->state == libssh2_NB_state_sent1) { ++ rc = _libssh2_packet_require(session, SSH_MSG_KEX_REPLY, &key_state->data, ++ &key_state->data_len, 0, NULL, 0, ++ &key_state->req_state); ++ if (rc == LIBSSH2_ERROR_EAGAIN) { ++ return rc; ++ } else if (rc) { ++ ret = _libssh2_error(session, rc, ++ "Timeout waiting for SSH_MSG_KEX_REPLY reply"); ++ goto clean_exit; ++ } ++ ++ key_state->state = libssh2_NB_state_sent2; ++ } ++ ++ if (key_state->state == libssh2_NB_state_sent2) { ++ unsigned char cipher[KLEN] = {0}; ++ unsigned char *c = NULL; ++ ++ ret = sm2_sm3(session, key_state->data, key_state->data_len, ++ &key_state->exchange_state); ++ if (ret == LIBSSH2_ERROR_EAGAIN) { ++ key_state->exchange_state.state = libssh2_NB_state_idle; ++ return ret; ++ } ++ ++ rc = _libssh2_sm4_encrypt(session->sm4_key, ++ key_state->exchange_state.K, ++ KLEN, cipher); ++ if(rc == 1){ ++ ret = _libssh2_error(session, rc, "Unable to sm4_encrypt data"); ++ goto clean_exit; ++ } ++ ++ key_state->request[0] = SSH_MSG_KEX; ++ c = key_state->request + 1; ++ _libssh2_store_str(&c, (const char*)cipher, KLEN); ++ key_state->request_len = KLEN + 5; ++ ++ _libssh2_debug((session, LIBSSH2_TRACE_KEX, "Initiating SM2 SM3")); ++ ++ rc = _libssh2_transport_send(session, key_state->request, ++ key_state->request_len, NULL, 0); ++ if (rc == LIBSSH2_ERROR_EAGAIN) { ++ return rc; ++ } else if (rc) { ++ ret = _libssh2_error(session, rc, "Unable to send SSH_MSG_KEX_REQUEST"); ++ goto clean_exit; ++ } ++ ++ key_state->exchange_state.c = SSH_MSG_NEWKEYS; ++ rc = _libssh2_transport_send(session, &key_state->exchange_state.c, 1, NULL, ++ 0); ++ if (rc == LIBSSH2_ERROR_EAGAIN) { ++ return rc; ++ } else if (rc) { ++ ret = _libssh2_error(session, rc, "Unable to send NEWKEYS message sm2"); ++ goto clean_exit; ++ } ++ sleep(1);/*server need work with data*/ ++ rc = _libssh2_packet_require(session, SSH_MSG_NEWKEYS, ++ &key_state->exchange_state.tmp, ++ &key_state->exchange_state.tmp_len, 0, NULL, 0, ++ &key_state->exchange_state.req_state); ++ if (rc == LIBSSH2_ERROR_EAGAIN) { ++ return rc; ++ } else if (rc) { ++ ret = _libssh2_error(session, rc, "Timed out waiting for NEWKEYS sm2"); ++ goto clean_exit; ++ } ++ ++ /* The first key exchange has been performed, ++ switch to active crypt/comp/mac mode */ ++ session->state |= LIBSSH2_STATE_NEWKEYS; ++ _libssh2_debug( ++ (session, LIBSSH2_TRACE_KEX, "Received NEWKEYS message sm2")); ++ ++ /* This will actually end up being just packet_type(1) ++ for this packet type anyway */ ++ LIBSSH2_FREE(session, key_state->exchange_state.tmp); ++ } ++ ++clean_exit: ++ kex_method_sm2_cleanup(session, key_state); ++ return ret; ++} ++ ++static const LIBSSH2_KEX_METHOD kex_method_ssh_sm2_sm3 = { ++ "SM2-SM3", ++ kex_method_sm2_key_exchange, ++ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY, ++}; ++ + /* this kex method signals that client can receive extensions + * as described in https://datatracker.ietf.org/doc/html/rfc8308 + */ +@@ -3038,6 +3529,7 @@ kex_method_extension_negotiation = { + }; + + static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = { ++ &kex_method_ssh_sm2_sm3, + #if LIBSSH2_ED25519 + &kex_method_ssh_curve25519_sha256, + &kex_method_ssh_curve25519_sha256_libssh, +diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h +index 82c3afe..36adfa2 100644 +--- a/src/libssh2_priv.h ++++ b/src/libssh2_priv.h +@@ -298,6 +298,7 @@ typedef struct kmdhgGPshakex_state_t + void *exchange_hash; + packet_require_state_t req_state; + libssh2_nonblocking_states burn_state; ++ unsigned char K[32]; /*GM SM2 K data*/ + } kmdhgGPshakex_state_t; + + typedef struct key_exchange_state_low_t +@@ -693,6 +694,10 @@ struct _LIBSSH2_SESSION + unsigned char server_hostkey_sha256[SHA256_DIGEST_LENGTH]; + int server_hostkey_sha256_valid; + ++ /*GM SM3*/ ++ unsigned char server_hostkey_sm3[SM3_DIGEST_LENGTH]; ++ int server_hostkey_sm3_valid; ++ + /* public key algorithms accepted as comma separated list */ + char *server_sign_algorithms; + +@@ -907,6 +912,11 @@ struct _LIBSSH2_SESSION + + /* Configurable timeout for packets. Replaces LIBSSH2_READ_TIMEOUT */ + long packet_read_timeout; ++ ++ unsigned char randomc[8]; ++ unsigned char randoms[8]; ++ unsigned char sm4_key[16]; ++ + }; + + /* session.state bits */ +@@ -1077,6 +1087,9 @@ _libssh2_debug_low(LIBSSH2_SESSION * session, int context, const char *format, + + #define SSH_MSG_KEXINIT 20 + #define SSH_MSG_NEWKEYS 21 ++#define SSH_MSG_KEX_REQUEST 22 ++#define SSH_MSG_KEX_REPLY 23 ++#define SSH_MSG_KEX 24 + + /* diffie-hellman-group1-sha1 */ + #define SSH_MSG_KEXDH_INIT 30 +@@ -1099,6 +1112,8 @@ _libssh2_debug_low(LIBSSH2_SESSION * session, int context, const char *format, + #define SSH_MSG_USERAUTH_FAILURE 51 + #define SSH_MSG_USERAUTH_SUCCESS 52 + #define SSH_MSG_USERAUTH_BANNER 53 ++#define SSH_MSG_USERAUTH_CHALLENGE 54 ++#define SSH_MSG_USERAUTH_RESPOND 55 + + /* "public key" method */ + #define SSH_MSG_USERAUTH_PK_OK 60 +diff --git a/src/openssl.c b/src/openssl.c +index 919a8d9..833460c 100644 +--- a/src/openssl.c ++++ b/src/openssl.c +@@ -4087,3 +4087,169 @@ _libssh2_supported_key_sign_algorithms(LIBSSH2_SESSION *session, + } + + #endif /* LIBSSH2_CRYPTO_C */ ++ ++/*GM SM4*/ ++int _libssh2_sm4_encrypt(unsigned char *key, ++ unsigned char *data, ++ int d_len, ++ unsigned char *cipher) ++{ ++ int len = 0; ++ _libssh2_cipher_ctx ctx = EVP_CIPHER_CTX_new(); ++ if(!ctx) ++ return 1; ++ ++ if(EVP_EncryptInit_ex(ctx, EVP_sm4_ecb(), NULL, key, NULL) != 1){ ++ EVP_CIPHER_CTX_free(ctx); ++ return 1; ++ } ++ ++ if(EVP_EncryptUpdate(ctx, cipher, &len, data, d_len) != 1){ ++ EVP_CIPHER_CTX_free(ctx); ++ return 1; ++ } ++ ++ if(EVP_EncryptFinal_ex(ctx, cipher+len, &len) != 1){ ++ EVP_CIPHER_CTX_free(ctx); ++ return 1; ++ } ++ ++ EVP_CIPHER_CTX_free(ctx); ++ return 0; ++} ++ ++/*GM SM3*/ ++int _libssh2_sm3_init(libssh2_sm3_ctx *ctx) ++{ ++ *ctx = EVP_MD_CTX_new(); ++ ++ if(!*ctx) ++ return 0; ++ ++ if(EVP_DigestInit_ex(*ctx, EVP_sm3(), NULL) != 1){ ++ EVP_MD_CTX_free(*ctx); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/*GM SM2*/ ++int _libssh2_sm2_new_public(libssh2_sm2_ctx **sm2_ctx, ++ const unsigned char *pubkey, ++ const size_t key_len) ++{ ++ EC_KEY *sm2_key = NULL; ++ const EC_GROUP *group = NULL; ++ EC_POINT *point = NULL; ++ BN_CTX *ctx = NULL; ++ BIGNUM *x = NULL; ++ BIGNUM *y = NULL; ++ ++ sm2_key = EC_KEY_new_by_curve_name(NID_sm2); ++ if(!sm2_key) ++ return 1; ++ ++ group = EC_KEY_get0_group(sm2_key); ++ if(!group) ++ return 1; ++ ++ ctx = BN_CTX_new(); ++ if(!ctx) ++ return 1; ++ ++ point = EC_POINT_new(group); ++ if(!point) ++ return 1; ++ ++ x = BN_bin2bn(pubkey, 32, NULL); ++ y = BN_bin2bn(pubkey+32, 32, NULL); ++ if(!x || !y) ++ return 1; ++ ++ if(EC_POINT_set_affine_coordinates_GFp(group,point, x, y, ctx) == 0){ ++ return 1; ++ } ++ ++ EC_KEY_set_public_key(sm2_key, point); ++ ++ *sm2_ctx = EVP_PKEY_new(); ++ if(EVP_PKEY_assign_EC_KEY(*sm2_ctx, sm2_key) == 0){ ++ return 1; ++ } ++ ++ return 0; ++} ++ ++int _libssh2_sm2_verify(libssh2_sm2_ctx *pkey, ++ const unsigned char *sig, ++ size_t sig_len, ++ const unsigned char *data, ++ size_t d_len) ++{ ++ int der_len, len; ++ int ret = 0; ++ unsigned char *der_sig = NULL; ++ unsigned char * p = NULL; ++ ECDSA_SIG *signature = NULL; ++ BIGNUM *r = NULL; ++ BIGNUM *s = NULL; ++ libssh2_sm3_ctx sm3_ctx = NULL; ++ ++ sm3_ctx = EVP_MD_CTX_new(); ++ if(!sm3_ctx) ++ return 1; /* error */ ++ ++ if(EVP_DigestVerifyInit(sm3_ctx, NULL, EVP_sm3(), NULL, pkey) <= 0){ ++ ret = -1; ++ goto clean_exit; ++ } ++ ++ if(1 != EVP_DigestVerifyUpdate(sm3_ctx, data, d_len)){ ++ ret = -1; ++ goto clean_exit; ++ } ++ ++ /*sig value is r + s ; needs to be converted to ANS.1*/ ++ signature = ECDSA_SIG_new(); ++ if(!signature){ ++ ret = -1; ++ goto clean_exit; ++ } ++ r = BN_bin2bn(sig,32, NULL); ++ s = BN_bin2bn(sig+32, 32, NULL); ++ ++ ECDSA_SIG_set0(signature,r,s); ++ der_len = i2d_ECDSA_SIG(signature, NULL); ++ if (der_len <= 0){ ++ ret = -1; ++ goto clean_exit; ++ } ++ der_sig = (unsigned char *)malloc(der_len); ++ if(!der_sig){ ++ ret = -1; ++ goto clean_exit; ++ } ++ p = der_sig; ++ len = i2d_ECDSA_SIG(signature, &p); ++ if(len != der_len){ ++ ret = -1; ++ goto clean_exit; ++ } ++ ++ if(1 != EVP_DigestVerifyFinal(sm3_ctx, der_sig, len)){ ++ ret = -1; ++ goto clean_exit; ++ } ++ ++clean_exit: ++ if(der_sig) ++ EVP_MD_CTX_free(sm3_ctx); ++ if(signature){ ++ ECDSA_SIG_free(signature); ++ } ++ if(der_sig) ++ free(der_sig); ++ ++ return ret; ++} +\ No newline at end of file +diff --git a/src/openssl.h b/src/openssl.h +index b7652c0..6e13643 100644 +--- a/src/openssl.h ++++ b/src/openssl.h +@@ -468,4 +468,18 @@ const EVP_CIPHER *_libssh2_EVP_aes_128_ctr(void); + const EVP_CIPHER *_libssh2_EVP_aes_192_ctr(void); + const EVP_CIPHER *_libssh2_EVP_aes_256_ctr(void); + +-#endif /* __LIBSSH2_OPENSSL_H */ ++/*GM SM3*/ ++#define libssh2_sm3_ctx EVP_MD_CTX * ++/* returns 0 in case of failure */ ++int _libssh2_sm3_init(libssh2_sm3_ctx *ctx); ++#define libssh2_sm3_init(x) _libssh2_sm3_init(x) ++#define libssh2_sm3_update(ctx, data, len) EVP_DigestUpdate(ctx, data, len) ++#define libssh2_sm3_final(ctx, out) do { \ ++ EVP_DigestFinal_ex(ctx, out, NULL); \ ++ EVP_MD_CTX_free(ctx); \ ++ } while(0) ++ ++/*GM SM2*/ ++#define libssh2_sm2_ctx EVP_PKEY ++#define _libssh2_sm2_free(ctx) EVP_PKEY_free(ctx) ++#endif /* __LIBSSH2_OPENSSL_H */ +\ No newline at end of file +diff --git a/src/userauth.c b/src/userauth.c +index 7745199..9e00a75 100644 +--- a/src/userauth.c ++++ b/src/userauth.c +@@ -276,7 +276,85 @@ libssh2_userauth_authenticated(LIBSSH2_SESSION * session) + return (session->state & LIBSSH2_STATE_AUTHENTICATED) ? 1 : 0; + } + ++/* ++* input_userauth_challenge_pw ++* ++* ++*/ ++static int input_userauth_challenge_pw(LIBSSH2_SESSION *session, ++ const char *username, ++ unsigned int username_len, ++ const unsigned char *password) { ++ size_t dlen; ++ size_t hashlen = SM3_DIGEST_LENGTH; ++ size_t packetlen; ++ int r; ++ unsigned char *challenge = NULL; ++ unsigned char password_hash[SM3_DIGEST_LENGTH] = {0}; ++ unsigned char hash[SM3_DIGEST_LENGTH] = {0}; ++ struct string_buf buf; ++ libssh2_sm3_ctx ctx_sm3; ++ unsigned char *s; ++ ++ buf.data = session->userauth_pswd_data; ++ buf.len = session->userauth_pswd_data_len; ++ buf.dataptr = buf.data; ++ buf.dataptr++; /* advance past packet type */ ++ ++ if (_libssh2_get_string(&buf, &challenge, &dlen)) { ++ return _libssh2_error(session, LIBSSH2_ERROR_PROTO, ++ "Unable to allocate memory for " ++ "userauth-password challenge"); ++ } ++ ++ /*GM_SM3 hash*/ ++ libssh2_sm3_init(&ctx_sm3); ++ libssh2_sm3_update(ctx_sm3, password, strlen(password)); ++ libssh2_sm3_final(ctx_sm3, password_hash); ++ ++ libssh2_sm3_init(&ctx_sm3); ++ libssh2_sm3_update(ctx_sm3, challenge, dlen); ++ libssh2_sm3_update(ctx_sm3, password_hash, SM3_DIGEST_LENGTH); ++ libssh2_sm3_final(ctx_sm3, hash); ++ ++ LIBSSH2_FREE(session, session->userauth_pswd_data); ++ session->userauth_list_data = NULL; ++ ++ /*46 = packet_type(1) + username_len(4) + service_len(4) + ++ service(14)"ssh-connection" + passwordlen(4) + passwd(8)"password" + ++ hashlen(4) + method_len(4) + method(3)"sm3"*/ ++ ++ packetlen = 46 + username_len + hashlen; ++ s = session->userauth_pswd_data = LIBSSH2_ALLOC(session, packetlen); ++ if (!session->userauth_pswd_data) { ++ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, ++ "Unable to allocate memory for " ++ "userauth-password challenge"); ++ } ++ *(s++) = SSH_MSG_USERAUTH_RESPOND; ++ _libssh2_store_str(&s, username, username_len); ++ _libssh2_store_str(&s, "ssh-connection", strlen("ssh-connection")); ++ _libssh2_store_str(&s, "password", strlen("password")); ++ _libssh2_store_str(&s, hash, hashlen); ++ _libssh2_store_str(&s, "sm3", strlen("sm3")); ++ ++ r = _libssh2_transport_send(session, session->userauth_pswd_data, packetlen, ++ NULL, 0); ++ if (r == LIBSSH2_ERROR_EAGAIN) { ++ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block waiting"); ++ } ++ /* now free the sent packet */ ++ LIBSSH2_FREE(session, session->userauth_pswd_data); ++ session->userauth_pswd_data = NULL; ++ ++ if (r) { ++ session->userauth_pswd_state = libssh2_NB_state_idle; ++ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, ++ "Unable to send userauth-password challenge"); ++ } + ++ return r; ++} + + /* userauth_password + * Plain ol' login +@@ -360,6 +438,32 @@ password_response: + || (session->userauth_pswd_state == libssh2_NB_state_sent1) + || (session->userauth_pswd_state == libssh2_NB_state_sent2)) { + if(session->userauth_pswd_state == libssh2_NB_state_sent) { ++ if (strcmp(session->kex->name, "SM2-SM3") == 0) { ++ packet_require_state_t challenge = {0}; ++ ++ rc = _libssh2_packet_require(session, SSH_MSG_USERAUTH_CHALLENGE, ++ &session->userauth_pswd_data, ++ &session->userauth_pswd_data_len, ++ 0, NULL, 0, &challenge); ++ if (rc) { ++ if (rc != LIBSSH2_ERROR_EAGAIN) ++ session->userauth_pswd_state = libssh2_NB_state_idle; ++ ++ return _libssh2_error(session, rc, "Waiting for password response"); ++ } ++ ++ rc = input_userauth_challenge_pw(session, username, username_len, ++ password); ++ if (rc) { ++ if (rc != LIBSSH2_ERROR_EAGAIN) ++ session->userauth_pswd_state = libssh2_NB_state_idle; ++ ++ return _libssh2_error(session, rc, ++ "Unable to send password responsed"); ++ } ++ } ++ sleep(1); /*server need work with data*/ ++ + rc = _libssh2_packet_requirev(session, reply_codes, + &session->userauth_pswd_data, + &session->userauth_pswd_data_len, +-- +2.43.0 + diff --git a/libssh2.spec b/libssh2.spec index fd933df..f3ec16f 100644 --- a/libssh2.spec +++ b/libssh2.spec @@ -1,6 +1,6 @@ Name: libssh2 Version: 1.11.0 -Release: 4 +Release: 5 Summary: A library implementing the SSH2 protocol License: BSD URL: https://www.libssh2.org/ @@ -18,6 +18,7 @@ Patch8: backport-buildconf-drop.patch Patch9: backport-Prevent-possible-double-free-of-hostkey.patch Patch10: backport-Fix-unstable-connections-over-nonblocking-sockets.patch Patch11: backport-session-support-server-banners-up-to-8192-bytes-was-256.patch +Patch12: 0001-feat-SM2-SM3.patch BuildRequires: coreutils findutils /usr/bin/man zlib-devel BuildRequires: gcc make sed openssl-devel > 1:1.0.2 openssh-server @@ -97,6 +98,12 @@ echo "exit 0" > tests/mansyntax.sh %{_mandir}/man3/libssh2_*.3* %changelog +* Fri Jan 17 2025 hcku - 1.11.0-5 +- Type:feature +- ID:NA +- SUG:NA +- DESC:Add Kex Algorithm SM2-SM3 + * Tue Oct 29 2024 bitianyuan - 1.11.0-4 - Type:bugfix - ID:NA -- Gitee