diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 0951c13c3..48b20686b 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -289,6 +289,7 @@ static int callbackReqFailure(WOLFSSH *ssh, void *buf, word32 sz, void *ctx) return WS_SUCCESS; } + static void *global_req(void *ctx) { int ret; @@ -328,6 +329,50 @@ static void *global_req(void *ctx) #endif +static void printKeyCompleteText(WOLFSSH* ssh, WS_Text id, const char* tag) +{ + char str[200]; + size_t strSz = sizeof(str); + size_t ret; + + ret = wolfSSH_GetText(ssh, id, str, strSz); + if (ret == strSz) { + printf("\tString size was not large enough for %s\n", tag); + } + printf("\t%-30s : %s\n", tag, str); +} + + +static void callbackKeyingComplete(void* ctx) +{ + WOLFSSH* ssh = (WOLFSSH*)ctx; + + if (ssh != NULL) { + printf("Keying Complete:\n"); + printKeyCompleteText(ssh, WOLFSSH_TEXT_KEX_ALGO, + "WOLFSSH_TEXT_KEX_ALGO"); + + printKeyCompleteText(ssh, WOLFSSH_TEXT_KEX_CURVE, + "WOLFSSH_TEXT_KEX_CURVE"); + + printKeyCompleteText(ssh, WOLFSSH_TEXT_KEX_HASH, + "WOLFSSH_TEXT_KEX_HASH"); + + printKeyCompleteText(ssh, WOLFSSH_TEXT_CRYPTO_IN_CIPHER, + "WOLFSSH_TEXT_CRYPTO_IN_CIPHER"); + + printKeyCompleteText(ssh, WOLFSSH_TEXT_CRYPTO_IN_MAC, + "WOLFSSH_TEXT_CRYPTO_IN_MAC"); + + printKeyCompleteText(ssh, WOLFSSH_TEXT_CRYPTO_OUT_CIPHER, + "WOLFSSH_TEXT_CRYPTO_OUT_CIPHER"); + + printKeyCompleteText(ssh, WOLFSSH_TEXT_CRYPTO_OUT_MAC, + "WOLFSSH_TEXT_CRYPTO_OUT_MAC"); + } +} + + #ifdef WOLFSSH_AGENT static const char EnvNameAuthPort[] = "SSH_AUTH_SOCK"; @@ -2435,6 +2480,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) ES_ERROR("Couldn't allocate SSH CTX data.\n"); } + wolfSSH_SetKeyingCompletionCb(ctx, callbackKeyingComplete); if (keyList) { if (wolfSSH_CTX_SetAlgoListKey(ctx, keyList) != WS_SUCCESS) { ES_ERROR("Error setting key list.\n"); @@ -2678,6 +2724,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) wolfSSH_MemoryConnPrintStats(heap); #endif wolfSSH_SetUserAuthCtx(ssh, &pwMapList); + wolfSSH_SetKeyingCompletionCbCtx(ssh, (void*)ssh); /* Use the session object for its own highwater callback ctx */ if (defaultHighwater > 0) { wolfSSH_SetHighwaterCtx(ssh, (void*)ssh); diff --git a/src/internal.c b/src/internal.c index 5d58d7e2b..e94285029 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1072,6 +1072,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) { @@ -3557,8 +3559,7 @@ static INLINE byte KeySzForId(byte id) } } - -static INLINE enum wc_HashType HashForId(byte id) +INLINE enum wc_HashType HashForId(byte id) { switch (id) { @@ -3649,7 +3650,7 @@ static INLINE enum wc_HashType HashForId(byte id) #if !defined(WOLFSSH_NO_ECDSA) || !defined(WOLFSSH_NO_ECDH) -static INLINE int wcPrimeForId(byte id) +int wcPrimeForId(byte id) { switch (id) { #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 @@ -3819,7 +3820,7 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) } } if (ret == WS_SUCCESS) { - ssh->handshake->kexId = algoId; + ssh->kexId = ssh->handshake->kexId = algoId; ssh->handshake->kexHashId = HashForId(algoId); } /* Extension Info Flag */ @@ -5565,6 +5566,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; @@ -10530,6 +10534,7 @@ static int KeyAgreeDh_server(WOLFSSH* ssh, byte hashId, byte* f, word32* fSz) &primeGroupSz, &generator, &generatorSz); if (ret == WS_SUCCESS) { + ssh->primeGroupSz = primeGroupSz; ret = wc_InitDhKey(privKey); } if (ret == 0) diff --git a/src/ssh.c b/src/ssh.c index 14c6112aa..19192da16 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -2834,6 +2834,220 @@ int wolfSSH_ChannelGetEof(WOLFSSH_CHANNEL* channel) return eof; } +static const char* HashNameForId(byte id) +{ + enum wc_HashType hash = HashForId(id); + + if (hash == WC_HASH_TYPE_SHA) + return "SHA-1"; + + if (hash == WC_HASH_TYPE_SHA256) + return "SHA-256"; + + if (hash == WC_HASH_TYPE_SHA384) + return "SHA-384"; + + if (hash == 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"; + +#ifdef HAVE_CURVE25519 + case ECC_X25519: + return "Curve25519"; +#endif + } +#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; + +#ifndef WOLFSSH_NO_DH + static const char standard_dh_format[] = + "%d-bit Diffie-Hellman with standard group %d"; +#endif + + if (!ssh || str == NULL || strSz <= 0) + return 0; + + 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: + #ifndef WOLFSSH_NO_CURVE25519_SHA256 + case ID_CURVE25519_SHA256: + #endif + ret = WSNPRINTF(str, strSz, "%s", "ECDH"); + break; + + #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 + case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256: + ret = WSNPRINTF(str, strSz, "%s", "Kyber1"); + break; + #endif + + #ifndef WOLFSSH_NO_DH + 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; + #endif /* !WOLFSSH_NO_DH */ + + case ID_EXTINFO_S: + ret = WSNPRINTF(str, strSz, "Server extensions KEX"); + break; + + case ID_EXTINFO_C: + ret = WSNPRINTF(str, strSz, "Client extensions KEX"); + 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 e617a4cf7..f99bbe1a3 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -535,6 +535,7 @@ struct WOLFSSH_CTX { #ifdef WOLFSSH_AGENT byte agentEnabled; #endif /* WOLFSSH_AGENT */ + WS_CallbackKeyingCompletion keyingCompletionCb; }; @@ -725,6 +726,7 @@ struct WOLFSSH { byte isClosed; byte clientOpenSSH; + byte kexId; byte blockSz; byte encryptId; byte macId; @@ -735,6 +737,9 @@ struct WOLFSSH { byte peerMacId; byte peerMacSz; byte peerAeadMode; +#ifndef WOLFSSH_NO_DH + word32 primeGroupSz; +#endif Ciphers encryptCipher; Ciphers decryptCipher; @@ -853,6 +858,7 @@ struct WOLFSSH { #if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL) word32 exitStatus; #endif + void* keyingCompletionCtx; }; @@ -989,6 +995,10 @@ WOLFSSH_LOCAL int SendChannelExitStatus(WOLFSSH* ssh, word32 channelId, word32 exitStatus); 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 3b4f65e09..3be5d0fd0 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -124,6 +124,39 @@ WOLFSSH_API const char* wolfSSH_QueryKey(word32* index); WOLFSSH_API const char* wolfSSH_QueryCipher(word32* index); WOLFSSH_API const char* wolfSSH_QueryMac(word32* index); +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