From 5daabe3f75ec150d8e4c1c6e6a347e118388a1a8 Mon Sep 17 00:00:00 2001 From: Fabio Alemagna <507164+falemagn@users.noreply.github.com> Date: Mon, 10 Jul 2023 12:17:02 +0200 Subject: [PATCH 1/3] Callback on keying completion, plus wolfSSH_GetText. --- src/internal.c | 15 +++- src/ssh.c | 182 +++++++++++++++++++++++++++++++++++++++++++++ wolfssh/internal.h | 10 +++ wolfssh/ssh.h | 33 ++++++++ 4 files changed, 236 insertions(+), 4 deletions(-) diff --git a/src/internal.c b/src/internal.c index 098f51825..d10093505 100644 --- a/src/internal.c +++ b/src/internal.c @@ -758,6 +758,8 @@ WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx) #endif #endif + ssh->keyingCompletionCtx = (void*)ssh; + if (BufferInit(&ssh->inputBuffer, 0, ctx->heap) != WS_SUCCESS || BufferInit(&ssh->outputBuffer, 0, ctx->heap) != WS_SUCCESS || BufferInit(&ssh->extDataBuffer, 0, ctx->heap) != WS_SUCCESS) { @@ -2836,8 +2838,7 @@ static INLINE byte KeySzForId(byte id) } } - -static INLINE enum wc_HashType HashForId(byte id) +INLINE enum wc_HashType HashForId(byte id) { switch (id) { @@ -2920,7 +2921,7 @@ static INLINE enum wc_HashType HashForId(byte id) #if !defined(WOLFSSH_NO_ECDSA) || !defined(WOLFSSH_NO_ECDH) -static INLINE int wcPrimeForId(byte id) +INLINE int wcPrimeForId(byte id) { switch (id) { #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 @@ -3058,7 +3059,7 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) ret = WS_MATCH_KEX_ALGO_E; } else { - ssh->handshake->kexId = algoId; + ssh->kexId = ssh->handshake->kexId = algoId; ssh->handshake->kexHashId = HashForId(algoId); } } @@ -4513,6 +4514,9 @@ static int DoNewKeys(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) HandshakeInfoFree(ssh->handshake, ssh->ctx->heap); ssh->handshake = NULL; WLOG(WS_LOG_DEBUG, "Keying completed"); + + if (ssh->ctx->keyingCompletionCb) + ssh->ctx->keyingCompletionCb(ssh->keyingCompletionCtx); } return ret; @@ -9206,6 +9210,9 @@ int SendKexDhReply(WOLFSSH* ssh) #endif if (ret == WS_SUCCESS) { + #ifndef WOLFSSH_NO_DH + ssh->primeGroupSz = primeGroupSz; + #endif ret = wc_InitDhKey(privKey); } if (ret == 0) diff --git a/src/ssh.c b/src/ssh.c index 93a5df572..e5a8fb70d 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -2321,6 +2321,188 @@ int wolfSSH_ChannelGetEof(WOLFSSH_CHANNEL* channel) return eof; } +static const char* HashNameForId(byte id) +{ + switch (HashForId(id)) { + case WC_HASH_TYPE_SHA: + return "SHA-1"; + + case WC_HASH_TYPE_SHA256: + return "SHA-256"; + + case WC_HASH_TYPE_SHA384: + return "SHA-384"; + + case WC_HASH_TYPE_SHA512: + return "SHA-512"; + } + + return ""; +} + +static const char* CurveNameForId(byte id) +{ +#if !defined(WOLFSSH_NO_ECDSA) || !defined(WOLFSSH_NO_ECDH) + switch (wcPrimeForId(id)) { + case ECC_SECP256R1: + return "nistp256"; + + case ECC_SECP384R1: + return "nistp384"; + + case ECC_SECP521R1: + return "nistp521"; + + case ECC_X25519: + return "Curve25519"; + } +#endif + return ""; +} + +static const char* CipherNameForId(byte id) +{ + switch (id) { + case ID_AES128_CBC: + return "AES-128 CBC"; + + case ID_AES192_CBC: + return "AES-192 CBC"; + + case ID_AES256_CBC: + return "AES-256 CBC"; + + case ID_AES128_CTR: + return "AES-128 SDCTR"; + + case ID_AES192_CTR: + return "AES-192 SDCTR"; + + case ID_AES256_CTR: + return "AES-256 SDCTR"; + + case ID_AES128_GCM: + return "AES-128 GCM"; + + case ID_AES192_GCM: + return "AES-192 GCM"; + + case ID_AES256_GCM: + return "AES-256 GCM"; + } + + return ""; +} + +static const char* MacNameForId(byte macid, byte cipherid) +{ + if (macid != ID_NONE) { + switch (macid) { + case ID_HMAC_SHA1: + return "HMAC-SHA-1"; + + case ID_HMAC_SHA1_96: + return "HMAC-SHA-1-96"; + + case ID_HMAC_SHA2_256: + return "HMAC-SHA-256"; + } + } + else { + switch (cipherid) { + case ID_AES128_GCM: + return "AES128 GCM (in ETM mode)"; + + case ID_AES192_GCM: + return "AES192 GCM (in ETM mode)"; + + case ID_AES256_GCM: + return "AES256 GCM (in ETM mode)"; + } + } + + return ""; +} + +size_t wolfSSH_GetText(WOLFSSH *ssh, WS_Text id, char *str, size_t strsz) +{ + int ret = 0; + + if (!ssh) + return 0; + + static const char standard_dh_format[] = "%d-bit Diffie-Hellman with standard group %d"; + + switch (id) { + case WOLFSSH_TEXT_KEX_HASH: + ret = WSNPRINTF(str, strsz, "%s", HashNameForId(ssh->kexId)); + break; + + case WOLFSSH_TEXT_KEX_CURVE: + ret = WSNPRINTF(str, strsz, "%s", CurveNameForId(ssh->kexId)); + break; + + case WOLFSSH_TEXT_CRYPTO_IN_CIPHER: + ret = WSNPRINTF(str, strsz, "%s", CipherNameForId(ssh->peerEncryptId)); + break; + + case WOLFSSH_TEXT_CRYPTO_OUT_CIPHER: + ret = WSNPRINTF(str, strsz, "%s", CipherNameForId(ssh->encryptId)); + break; + + case WOLFSSH_TEXT_CRYPTO_IN_MAC: + ret = WSNPRINTF(str, strsz, "%s", MacNameForId(ssh->peerMacId, ssh->peerEncryptId)); + break; + + case WOLFSSH_TEXT_CRYPTO_OUT_MAC: + ret = WSNPRINTF(str, strsz, "%s", MacNameForId(ssh->macId, ssh->encryptId)); + break; + + case WOLFSSH_TEXT_KEX_ALGO: + switch (ssh->kexId) { + case ID_ECDH_SHA2_NISTP256: + case ID_ECDH_SHA2_NISTP384: + case ID_ECDH_SHA2_NISTP521: + case ID_ECDH_SHA2_ED25519: + case ID_ECDH_SHA2_ED25519_LIBSSH: + ret = WSNPRINTF(str, strsz, "%s", "ECDH"); + break; + + case ID_DH_GROUP1_SHA1: + ret = WSNPRINTF(str, strsz, standard_dh_format, ssh->primeGroupSz*8, 1); + break; + + case ID_DH_GROUP14_SHA1: + case ID_DH_GROUP14_SHA256: + ret = WSNPRINTF(str, strsz, standard_dh_format, ssh->primeGroupSz*8, 14); + break; + + case ID_DH_GEX_SHA256: + ret = WSNPRINTF(str, strsz, "%d-bit Diffie-Hellman with server-supplied group", ssh->primeGroupSz*8); + break; + } + break; + } + + return ret < 0 ? 0 : (size_t)ret; +} + +void wolfSSH_SetKeyingCompletionCb(WOLFSSH_CTX* ctx, WS_CallbackKeyingCompletion cb) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetKeyingCompletionCb()"); + + if (ctx) + ctx->keyingCompletionCb = cb; +} + +void wolfSSH_SetKeyingCompletionCbCtx(WOLFSSH* ssh, void* ctx) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetKeyingCompletionCbCtx()"); + + if (ssh) + ssh->keyingCompletionCtx = ctx; +} + #if (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)) && \ !defined(NO_WOLFSSH_SERVER) diff --git a/wolfssh/internal.h b/wolfssh/internal.h index f9b5dcc5a..637d65226 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -502,6 +502,7 @@ struct WOLFSSH_CTX { #ifdef WOLFSSH_AGENT byte agentEnabled; #endif /* WOLFSSH_AGENT */ + WS_CallbackKeyingCompletion keyingCompletionCb; }; @@ -684,6 +685,7 @@ struct WOLFSSH { byte isClosed; byte clientOpenSSH; + byte kexId; byte blockSz; byte encryptId; byte macId; @@ -694,6 +696,9 @@ struct WOLFSSH { byte peerMacId; byte peerMacSz; byte peerAeadMode; +#ifndef WOLFSSH_NO_DH + word32 primeGroupSz; +#endif Ciphers encryptCipher; Ciphers decryptCipher; @@ -805,6 +810,7 @@ struct WOLFSSH { word32 curX; /* current terminal width */ word32 curY; /* current terminal height */ #endif + void* keyingCompletionCtx; }; @@ -936,6 +942,10 @@ WOLFSSH_LOCAL int SendChannelAgentRequest(WOLFSSH* ssh); WOLFSSH_LOCAL int SendChannelSuccess(WOLFSSH*, word32, int); WOLFSSH_LOCAL int GenerateKey(byte, byte, byte*, word32, const byte*, word32, const byte*, word32, const byte*, word32, byte doKeyPad); +#if !defined(WOLFSSH_NO_ECDSA) || !defined(WOLFSSH_NO_ECDH) +WOLFSSH_LOCAL int wcPrimeForId(byte); +#endif +WOLFSSH_LOCAL enum wc_HashType HashForId(byte); enum AcceptStates { diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index 492ab5dd4..3596b20c1 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -90,6 +90,39 @@ WOLFSSH_API int wolfSSH_ReadKey_file(const char* name, byte** out, word32* outSz, const byte** outType, word32* outTypeSz, byte* isPrivate, void* heap); +typedef enum WS_Text { + WOLFSSH_TEXT_KEX_ALGO, + WOLFSSH_TEXT_KEX_CURVE, + WOLFSSH_TEXT_KEX_HASH, + + WOLFSSH_TEXT_CRYPTO_IN_CIPHER, + WOLFSSH_TEXT_CRYPTO_IN_MAC, + WOLFSSH_TEXT_CRYPTO_OUT_CIPHER, + WOLFSSH_TEXT_CRYPTO_OUT_MAC, +} WS_Text; + +/* + * Outputs the c-string representation of the data entry identified by the id to + * the character string str, writing no more than strsz bytes, including the + * terminating null byte ('\0'). + * + * Returns the number of characters written (excluding the null byte used to end + * output to strings), unless the output was truncated, in which case the return + * value is the number of characters (excluding the terminating null byte) which + * would have been written to the final string if enough space had been + * available. + * + * Thus, a return value of strsz or more means that the output was truncated. + */ + +WOLFSSH_API size_t wolfSSH_GetText(WOLFSSH *ssh, WS_Text id, char *str, + size_t strsz); + +typedef void (*WS_CallbackKeyingCompletion)(void *); +WOLFSSH_API void wolfSSH_SetKeyingCompletionCb(WOLFSSH_CTX*, + WS_CallbackKeyingCompletion); +WOLFSSH_API void wolfSSH_SetKeyingCompletionCbCtx(WOLFSSH*, + void*); #define WS_CHANNEL_ID_SELF 0 #define WS_CHANNEL_ID_PEER 1 From 8abf3ca6929edc4a5b9a22516c6e5a7beabf40c3 Mon Sep 17 00:00:00 2001 From: Fabio <507164+falemagn@users.noreply.github.com> Date: Mon, 21 Aug 2023 14:00:47 +0200 Subject: [PATCH 2/3] #ifdef around ECC_X25519 with HAVE_CURVE25519, so that it compiles when wolfssl is configured without CURVE25519. --- src/ssh.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ssh.c b/src/ssh.c index e5a8fb70d..634fce5fd 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -2353,8 +2353,10 @@ static const char* CurveNameForId(byte id) case ECC_SECP521R1: return "nistp521"; +#ifdef HAVE_CURVE25519 case ECC_X25519: return "Curve25519"; +#endif } #endif return ""; From c3aa3714504d5bc687e9bcf0a58a5ce45c8f704a Mon Sep 17 00:00:00 2001 From: Fabio <507164+falemagn@users.noreply.github.com> Date: Fri, 20 Oct 2023 09:48:32 +0200 Subject: [PATCH 3/3] Don't use a switch in HashNameForId, to avoid warnings. --- src/ssh.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ssh.c b/src/ssh.c index 634fce5fd..b5bd2f9ca 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -2323,19 +2323,19 @@ int wolfSSH_ChannelGetEof(WOLFSSH_CHANNEL* channel) static const char* HashNameForId(byte id) { - switch (HashForId(id)) { - case WC_HASH_TYPE_SHA: - return "SHA-1"; + enum wc_HashType hash = HashForId(id); - case WC_HASH_TYPE_SHA256: - return "SHA-256"; + if (hash == WC_HASH_TYPE_SHA) + return "SHA-1"; - case WC_HASH_TYPE_SHA384: - return "SHA-384"; + if (hash == WC_HASH_TYPE_SHA256) + return "SHA-256"; - case WC_HASH_TYPE_SHA512: - return "SHA-512"; - } + if (hash == WC_HASH_TYPE_SHA384) + return "SHA-384"; + + if (hash == WC_HASH_TYPE_SHA512) + return "SHA-512"; return ""; }