ssh
src/internal.c
- Committer:
- sPymbed
- Date:
- 2019-11-25
- Revision:
- 0:c4152c628df5
File content as of revision 0:c4152c628df5:
/* internal.c * * Copyright (C) 2014-2016 wolfSSL Inc. * * This file is part of wolfSSH. * * wolfSSH is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * wolfSSH is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with wolfSSH. If not, see <http://www.gnu.org/licenses/>. */ /* * The internal module contains the private data and functions. The public * API calls into this module to do the work of processing the connections. */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <wolfssh/ssh.h> #include <wolfssh/internal.h> #include <wolfssh/log.h> #include <wolfcrypt/asn.h> #include <wolfcrypt/dh.h> #include <wolfcrypt/rsa.h> #include <wolfcrypt/ecc.h> #include <wolfcrypt/hmac.h> #include <wolfcrypt/integer.h> #include <wolfcrypt/signature.h> //#include "esp_log.h" //#include "esp_system.h" #ifdef NO_INLINE #include <wolfssh/misc.h> #else #define WOLFSSH_MISC_INCLUDED #include "src/misc.c" #endif static const char sshProtoIdStr[] = "SSH-2.0-wolfSSHv" LIBWOLFSSH_VERSION_STRING "\r\n"; #ifndef WOLFSSH_DEFAULT_GEXDH_MIN #define WOLFSSH_DEFAULT_GEXDH_MIN 1024 #endif #ifndef WOLFSSH_DEFAULT_GEXDH_PREFERRED #define WOLFSSH_DEFAULT_GEXDH_PREFERRED 3072 #endif #ifndef WOLFSSH_DEFAULT_GEXDH_MAX #define WOLFSSH_DEFAULT_GEXDH_MAX 8192 #endif const char* GetErrorString(int err) { (void)err; #ifdef NO_WOLFSSH_STRINGS return "No wolfSSH strings available"; #else switch (err) { case WS_SUCCESS: return "function success"; case WS_FATAL_ERROR: return "general function failure"; case WS_BAD_ARGUMENT: return "bad function argument"; case WS_MEMORY_E: return "memory allocation failure"; case WS_BUFFER_E: return "input/output buffer size error"; case WS_PARSE_E: return "general parsing error"; case WS_NOT_COMPILED: return "feature not compiled in"; case WS_OVERFLOW_E: return "would overflow if continued failure"; case WS_BAD_USAGE: return "bad example usage"; case WS_SOCKET_ERROR_E: return "socket error"; case WS_WANT_READ: return "I/O callback would read block error"; case WS_WANT_WRITE: return "I/O callback would write block error"; case WS_RECV_OVERFLOW_E: return "receive buffer overflow"; case WS_VERSION_E: return "peer version unsupported"; case WS_SEND_OOB_READ_E: return "attempted to read buffer out of bounds"; case WS_INPUT_CASE_E: return "bad process input state, programming error"; case WS_BAD_FILETYPE_E: return "bad filetype"; case WS_UNIMPLEMENTED_E: return "feature not implemented"; case WS_RSA_E: return "RSA buffer error"; case WS_BAD_FILE_E: return "bad file"; case WS_DECRYPT_E: return "decrypt error"; case WS_ENCRYPT_E: return "encrypt error"; case WS_VERIFY_MAC_E: return "verify mac error"; case WS_CREATE_MAC_E: return "verify mac error"; case WS_RESOURCE_E: return "insufficient resources for new channel"; case WS_INVALID_CHANTYPE: return "peer requested invalid channel type"; case WS_INVALID_CHANID: return "peer requested invalid channel id"; case WS_INVALID_USERNAME: return "invalid user name"; case WS_CRYPTO_FAILED: return "crypto action failed"; case WS_INVALID_STATE_E: return "invalid state"; case WS_REKEYING: return "rekeying with peer"; case WS_INVALID_PRIME_CURVE: return "invalid prime curve in ecc"; case WS_ECC_E: return "ECDSA buffer error"; case WS_CHANOPEN_FAILED: return "peer returned channel open failure"; case WS_CHANNEL_CLOSED: return "channel closed"; default: return "Unknown error code"; } #endif } static int wsHighwater(byte dir, void* ctx) { int ret = WS_SUCCESS; (void)dir; if (ctx) { WOLFSSH* ssh = (WOLFSSH*)ctx; WLOG(WS_LOG_DEBUG, "HIGHWATER MARK: (%u) %s", wolfSSH_GetHighwater(ssh), (dir == WOLFSSH_HWSIDE_RECEIVE) ? "receive" : "transmit"); ret = wolfSSH_TriggerKeyExchange(ssh); } return ret; } static INLINE void HighwaterCheck(WOLFSSH* ssh, byte side) { if (!ssh->highwaterFlag && ssh->highwaterMark && (ssh->txCount >= ssh->highwaterMark || ssh->rxCount >= ssh->highwaterMark)) { WLOG(WS_LOG_DEBUG, "%s over high water mark", (side == WOLFSSH_HWSIDE_TRANSMIT) ? "Transmit" : "Receive"); ssh->highwaterFlag = 1; if (ssh->ctx->highwaterCb) ssh->ctx->highwaterCb(side, ssh->highwaterCtx); } } static HandshakeInfo* HandshakeInfoNew(void* heap) { HandshakeInfo* newHs; WLOG(WS_LOG_DEBUG, "Entering HandshakeInfoNew()"); newHs = (HandshakeInfo*)WMALLOC(sizeof(HandshakeInfo), heap, DYNTYPE_HS); if (newHs != NULL) { WMEMSET(newHs, 0, sizeof(HandshakeInfo)); newHs->kexId = ID_NONE; newHs->pubKeyId = ID_NONE; newHs->encryptId = ID_NONE; newHs->macId = ID_NONE; newHs->blockSz = MIN_BLOCK_SZ; newHs->hashId = WC_HASH_TYPE_NONE; newHs->dhGexMinSz = WOLFSSH_DEFAULT_GEXDH_MIN; newHs->dhGexPreferredSz = WOLFSSH_DEFAULT_GEXDH_PREFERRED; newHs->dhGexMaxSz = WOLFSSH_DEFAULT_GEXDH_MAX; } return newHs; } void ForceZero(const void* mem, word32 length){ } static void HandshakeInfoFree(HandshakeInfo* hs, void* heap) { (void)heap; WLOG(WS_LOG_DEBUG, "Entering HandshakeInfoFree()"); if (hs) { WFREE(hs->kexInit, heap, DYNTYPE_STRING); WFREE(hs->primeGroup, heap, DYNTYPE_MPINT); WFREE(hs->generator, heap, DYNTYPE_MPINT); ForceZero(hs, sizeof(HandshakeInfo)); WFREE(hs, heap, DYNTYPE_HS); } } #ifdef DEBUG_WOLFSSH static const char cannedBanner[] = "CANNED BANNER\r\n" "This server is an example test server. " "It should have its own banner, but\r\n" "it is currently using a canned one in " "the library. Be happy or not.\r\n"; static const word32 cannedBannerSz = sizeof(cannedBanner) - 1; #endif /* DEBUG_WOLFSSH */ WOLFSSH_CTX* CtxInit(WOLFSSH_CTX* ctx, byte side, void* heap) { WLOG(WS_LOG_DEBUG, "Entering CtxInit()"); if (ctx == NULL) return ctx; WMEMSET(ctx, 0, sizeof(WOLFSSH_CTX)); if (heap) ctx->heap = heap; ctx->side = side; #ifndef WOLFSSH_USER_IO ctx->ioRecvCb = wsEmbedRecv; ctx->ioSendCb = wsEmbedSend; #endif /* WOLFSSH_USER_IO */ ctx->highwaterMark = DEFAULT_HIGHWATER_MARK; ctx->highwaterCb = wsHighwater; #ifdef DEBUG_WOLFSSH ctx->banner = cannedBanner; ctx->bannerSz = cannedBannerSz; #endif /* DEBUG_WOLFSSH */ return ctx; } void CtxResourceFree(WOLFSSH_CTX* ctx) { WLOG(WS_LOG_DEBUG, "Entering CtxResourceFree()"); if (ctx->privateKey) { ForceZero(ctx->privateKey, ctx->privateKeySz); WFREE(ctx->privateKey, ctx->heap, DYNTYPE_PRIVKEY); } } WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx) { HandshakeInfo* handshake; RNG* rng; void* heap; WLOG(WS_LOG_DEBUG, "Entering SshInit()"); //ESP_LOGI("WOLFSSH", "SshInit()"); if (ssh == NULL || ctx == NULL) return ssh; heap = ctx->heap; //ESP_LOGI("WOLFSSH", "heap=ctx->heap"); handshake = HandshakeInfoNew(heap); //ESP_LOGI("WOLFSSH", "HandshakeInfoNew(heap) OK"); rng = (RNG*)WMALLOC(sizeof(RNG), heap, DYNTYPE_RNG); //ESP_LOGI("WOLFSSH", "malloc RNG OK"); //if(handshake == NULL) ESP_LOGE("WOLFSSH", "handshake is NULL"); //if(rng == NULL) ESP_LOGE("WOLFSSH", "rng is NULL"); //ESP_LOGI("WOLFSSH", "wc_InitRng(rng), free heap:%d", esp_get_free_heap_size()); int wc_InitRng_result = wc_InitRng(rng); if (handshake == NULL || rng == NULL || wc_InitRng_result != 0) { //ESP_LOGE("WOLFSSH", "Cannot allocate memory, wc_InitRng_result ret=%d", wc_InitRng_result); WLOG(WS_LOG_DEBUG, "SshInit: Cannot allocate memory.\n"); WFREE(handshake, heap, DYNTYPE_HS); WFREE(rng, heap, DYNTYPE_RNG); wolfSSH_free(ssh); return NULL; } WMEMSET(ssh, 0, sizeof(WOLFSSH)); /* default init to zeros */ //ESP_LOGI("WOLFSSH", "memset WOLFSSH"); ssh->ctx = ctx; ssh->error = WS_SUCCESS; ssh->rfd = -1; /* set to invalid */ ssh->wfd = -1; /* set to invalid */ ssh->ioReadCtx = &ssh->rfd; /* prevent invalid access if not correctly */ ssh->ioWriteCtx = &ssh->wfd; /* set */ ssh->highwaterMark = ctx->highwaterMark; ssh->highwaterCtx = (void*)ssh; ssh->acceptState = ACCEPT_BEGIN; ssh->clientState = CLIENT_BEGIN; ssh->isKeying = 1; ssh->nextChannel = DEFAULT_NEXT_CHANNEL; ssh->blockSz = MIN_BLOCK_SZ; ssh->encryptId = ID_NONE; ssh->macId = ID_NONE; ssh->peerBlockSz = MIN_BLOCK_SZ; ssh->rng = rng; ssh->kSz = sizeof(ssh->k); ssh->handshake = handshake; //ESP_LOGI("WOLFSSH", "BufferInit..."); if (BufferInit(&ssh->inputBuffer, 0, ctx->heap) != WS_SUCCESS || BufferInit(&ssh->outputBuffer, 0, ctx->heap) != WS_SUCCESS) { wolfSSH_free(ssh); ssh = NULL; } //ESP_LOGI("WOLFSSH", "BufferInit OK"); return ssh; } void SshResourceFree(WOLFSSH* ssh, void* heap) { /* when ssh holds resources, free here */ (void)heap; WLOG(WS_LOG_DEBUG, "Entering sshResourceFree()"); ShrinkBuffer(&ssh->inputBuffer, 1); ShrinkBuffer(&ssh->outputBuffer, 1); ForceZero(ssh->k, ssh->kSz); HandshakeInfoFree(ssh->handshake, heap); ForceZero(&ssh->keys, sizeof(Keys)); ForceZero(&ssh->peerKeys, sizeof(Keys)); if (ssh->rng) { wc_FreeRng(ssh->rng); WFREE(ssh->rng, heap, DYNTYPE_RNG); } if (ssh->userName) { WFREE(ssh->userName, heap, DYNTYPE_STRING); } if (ssh->peerProtoId) { WFREE(ssh->peerProtoId, heap, DYNTYPE_STRING); } if (ssh->channelList) { WOLFSSH_CHANNEL* cur = ssh->channelList; WOLFSSH_CHANNEL* next; while (cur) { next = cur->next; ChannelDelete(cur, heap); cur = next; } } } int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx, const byte* in, word32 inSz, int format, int type) { int dynamicType; void* heap; byte* der; word32 derSz; if (ctx == NULL || in == NULL || inSz == 0) return WS_BAD_ARGUMENT; if (format != WOLFSSH_FORMAT_ASN1 && format != WOLFSSH_FORMAT_PEM && format != WOLFSSH_FORMAT_RAW) return WS_BAD_FILETYPE_E; if (type == BUFTYPE_CA) dynamicType = DYNTYPE_CA; else if (type == BUFTYPE_CERT) dynamicType = DYNTYPE_CERT; else if (type == BUFTYPE_PRIVKEY) dynamicType = DYNTYPE_PRIVKEY; else return WS_BAD_ARGUMENT; heap = ctx->heap; if (format == WOLFSSH_FORMAT_PEM) return WS_UNIMPLEMENTED_E; else { /* format is ASN1 or RAW */ der = (byte*)WMALLOC(inSz, heap, dynamicType); if (der == NULL) return WS_MEMORY_E; WMEMCPY(der, in, inSz); derSz = inSz; } /* Maybe decrypt */ if (type == BUFTYPE_PRIVKEY) { if (ctx->privateKey) WFREE(ctx->privateKey, heap, dynamicType); ctx->privateKey = der; ctx->privateKeySz = derSz; } else { WFREE(der, heap, dynamicType); return WS_UNIMPLEMENTED_E; } if (type == BUFTYPE_PRIVKEY && format != WOLFSSH_FORMAT_RAW) { /* Check RSA key */ union { RsaKey rsa; ecc_key ecc; } key; word32 scratch = 0; int ret; if (wc_InitRsaKey(&key.rsa, NULL) < 0) return WS_RSA_E; ret = wc_RsaPrivateKeyDecode(der, &scratch, &key.rsa, derSz); wc_FreeRsaKey(&key.rsa); if (ret < 0) { /* Couldn't decode as RSA key. Try decoding as ECC key. */ scratch = 0; if (wc_ecc_init_ex(&key.ecc, ctx->heap, INVALID_DEVID) != 0) return WS_ECC_E; ret = wc_EccPrivateKeyDecode(ctx->privateKey, &scratch, &key.ecc, ctx->privateKeySz); if (ret == 0) { int curveId = wc_ecc_get_curve_id(key.ecc.idx); if (curveId == ECC_SECP256R1 || curveId == ECC_SECP384R1 || curveId == ECC_SECP521R1) { ctx->useEcc = curveId; } else ret = WS_BAD_FILE_E; } wc_ecc_free(&key.ecc); if (ret != 0) return WS_BAD_FILE_E; } } return WS_SUCCESS; } void c32toa(word32 u32, byte* c){ } int GenerateKey(byte hashId, byte keyId, byte* key, word32 keySz, const byte* k, word32 kSz, const byte* h, word32 hSz, const byte* sessionId, word32 sessionIdSz) { word32 blocks, remainder; wc_HashAlg hash; byte kPad = 0; byte pad = 0; byte kSzFlat[LENGTH_SZ]; int digestSz; int ret; if (key == NULL || keySz == 0 || k == NULL || kSz == 0 || h == NULL || hSz == 0 || sessionId == NULL || sessionIdSz == 0) { WLOG(WS_LOG_DEBUG, "GK: bad argument"); return WS_BAD_ARGUMENT; } digestSz = wc_HashGetDigestSize(hashId); if (digestSz == 0) { WLOG(WS_LOG_DEBUG, "GK: bad hash ID"); return WS_BAD_ARGUMENT; } if (k[0] & 0x80) kPad = 1; c32toa(kSz + kPad, kSzFlat); blocks = keySz / digestSz; remainder = keySz % digestSz; ret = wc_HashInit(&hash, hashId); if (ret == WS_SUCCESS) ret = wc_HashUpdate(&hash, hashId, kSzFlat, LENGTH_SZ); if (ret == WS_SUCCESS && kPad) ret = wc_HashUpdate(&hash, hashId, &pad, 1); if (ret == WS_SUCCESS) ret = wc_HashUpdate(&hash, hashId, k, kSz); if (ret == WS_SUCCESS) ret = wc_HashUpdate(&hash, hashId, h, hSz); if (ret == WS_SUCCESS) ret = wc_HashUpdate(&hash, hashId, &keyId, sizeof(keyId)); if (ret == WS_SUCCESS) ret = wc_HashUpdate(&hash, hashId, sessionId, sessionIdSz); if (ret == WS_SUCCESS) { if (blocks == 0) { if (remainder > 0) { byte lastBlock[WC_MAX_DIGEST_SIZE]; ret = wc_HashFinal(&hash, hashId, lastBlock); if (ret == WS_SUCCESS) WMEMCPY(key, lastBlock, remainder); } } else { word32 runningKeySz, curBlock; runningKeySz = digestSz; ret = wc_HashFinal(&hash, hashId, key); for (curBlock = 1; curBlock < blocks; curBlock++) { ret = wc_HashInit(&hash, hashId); if (ret != WS_SUCCESS) break; ret = wc_HashUpdate(&hash, hashId, kSzFlat, LENGTH_SZ); if (ret != WS_SUCCESS) break; if (kPad) ret = wc_HashUpdate(&hash, hashId, &pad, 1); if (ret != WS_SUCCESS) break; ret = wc_HashUpdate(&hash, hashId, k, kSz); if (ret != WS_SUCCESS) break; ret = wc_HashUpdate(&hash, hashId, h, hSz); if (ret != WS_SUCCESS) break; ret = wc_HashUpdate(&hash, hashId, key, runningKeySz); if (ret != WS_SUCCESS) break; ret = wc_HashFinal(&hash, hashId, key + runningKeySz); if (ret != WS_SUCCESS) break; runningKeySz += digestSz; } if (remainder > 0) { byte lastBlock[WC_MAX_DIGEST_SIZE]; if (ret == WS_SUCCESS) ret = wc_HashInit(&hash, hashId); if (ret == WS_SUCCESS) ret = wc_HashUpdate(&hash, hashId, kSzFlat, LENGTH_SZ); if (ret == WS_SUCCESS && kPad) ret = wc_HashUpdate(&hash, hashId, &pad, 1); if (ret == WS_SUCCESS) ret = wc_HashUpdate(&hash, hashId, k, kSz); if (ret == WS_SUCCESS) ret = wc_HashUpdate(&hash, hashId, h, hSz); if (ret == WS_SUCCESS) ret = wc_HashUpdate(&hash, hashId, key, runningKeySz); if (ret == WS_SUCCESS) ret = wc_HashFinal(&hash, hashId, lastBlock); if (ret == WS_SUCCESS) WMEMCPY(key + runningKeySz, lastBlock, remainder); } } } if (ret != WS_SUCCESS) ret = WS_CRYPTO_FAILED; return ret; } static int GenerateKeys(WOLFSSH* ssh) { Keys* cK; Keys* sK; byte hashId; int ret = WS_SUCCESS; if (ssh == NULL) ret = WS_BAD_ARGUMENT; else { if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) { cK = &ssh->handshake->peerKeys; sK = &ssh->handshake->keys; } else { cK = &ssh->handshake->keys; sK = &ssh->handshake->peerKeys; } hashId = ssh->handshake->hashId; } if (ret == WS_SUCCESS) ret = GenerateKey(hashId, 'A', cK->iv, cK->ivSz, ssh->k, ssh->kSz, ssh->h, ssh->hSz, ssh->sessionId, ssh->sessionIdSz); if (ret == WS_SUCCESS) ret = GenerateKey(hashId, 'B', sK->iv, sK->ivSz, ssh->k, ssh->kSz, ssh->h, ssh->hSz, ssh->sessionId, ssh->sessionIdSz); if (ret == WS_SUCCESS) ret = GenerateKey(hashId, 'C', cK->encKey, cK->encKeySz, ssh->k, ssh->kSz, ssh->h, ssh->hSz, ssh->sessionId, ssh->sessionIdSz); if (ret == WS_SUCCESS) ret = GenerateKey(hashId, 'D', sK->encKey, sK->encKeySz, ssh->k, ssh->kSz, ssh->h, ssh->hSz, ssh->sessionId, ssh->sessionIdSz); if (!ssh->handshake->aeadMode) { if (ret == WS_SUCCESS) ret = GenerateKey(hashId, 'E', cK->macKey, cK->macKeySz, ssh->k, ssh->kSz, ssh->h, ssh->hSz, ssh->sessionId, ssh->sessionIdSz); if (ret == WS_SUCCESS) ret = GenerateKey(hashId, 'F', sK->macKey, sK->macKeySz, ssh->k, ssh->kSz, ssh->h, ssh->hSz, ssh->sessionId, ssh->sessionIdSz); } #ifdef SHOW_SECRETS if (ret == WS_SUCCESS) { printf("\n** Showing Secrets **\nK:\n"); DumpOctetString(ssh->k, ssh->kSz); printf("H:\n"); DumpOctetString(ssh->h, ssh->hSz); printf("Session ID:\n"); DumpOctetString(ssh->sessionId, ssh->sessionIdSz); printf("A:\n"); DumpOctetString(cK->iv, cK->ivSz); printf("B:\n"); DumpOctetString(sK->iv, sK->ivSz); printf("C:\n"); DumpOctetString(cK->encKey, cK->encKeySz); printf("D:\n"); DumpOctetString(sK->encKey, sK->encKeySz); printf("E:\n"); DumpOctetString(cK->macKey, cK->macKeySz); printf("F:\n"); DumpOctetString(sK->macKey, sK->macKeySz); printf("\n"); } #endif /* SHOW_SECRETS */ return ret; } typedef struct { byte id; const char* name; } NameIdPair; static const NameIdPair NameIdMap[] = { { ID_NONE, "none" }, /* Encryption IDs */ { ID_AES128_CBC, "aes128-cbc" }, { ID_AES128_GCM, "aes128-gcm@openssh.com" }, /* Integrity IDs */ { ID_HMAC_SHA1, "hmac-sha1" }, { ID_HMAC_SHA1_96, "hmac-sha1-96" }, { ID_HMAC_SHA2_256, "hmac-sha2-256" }, /* Key Exchange IDs */ { ID_DH_GROUP1_SHA1, "diffie-hellman-group1-sha1" }, { ID_DH_GROUP14_SHA1, "diffie-hellman-group14-sha1" }, { ID_DH_GEX_SHA256, "diffie-hellman-group-exchange-sha256" }, { ID_ECDH_SHA2_NISTP256, "ecdh-sha2-nistp256" }, { ID_ECDH_SHA2_NISTP384, "ecdh-sha2-nistp384" }, { ID_ECDH_SHA2_NISTP521, "ecdh-sha2-nistp521" }, /* Public Key IDs */ { ID_SSH_RSA, "ssh-rsa" }, { ID_ECDSA_SHA2_NISTP256, "ecdsa-sha2-nistp256" }, { ID_ECDSA_SHA2_NISTP384, "ecdsa-sha2-nistp384" }, { ID_ECDSA_SHA2_NISTP521, "ecdsa-sha2-nistp521" }, /* Service IDs */ { ID_SERVICE_USERAUTH, "ssh-userauth" }, { ID_SERVICE_CONNECTION, "ssh-connection" }, /* UserAuth IDs */ { ID_USERAUTH_PASSWORD, "password" }, { ID_USERAUTH_PUBLICKEY, "publickey" }, /* Channel Type IDs */ { ID_CHANTYPE_SESSION, "session" } }; byte NameToId(const char* name, word32 nameSz) { byte id = ID_UNKNOWN; word32 i; for (i = 0; i < (sizeof(NameIdMap)/sizeof(NameIdPair)); i++) { if (nameSz == WSTRLEN(NameIdMap[i].name) && WSTRNCMP(name, NameIdMap[i].name, nameSz) == 0) { id = NameIdMap[i].id; break; } } return id; } const char* IdToName(byte id) { const char* name = "unknown"; word32 i; for (i = 0; i < (sizeof(NameIdMap)/sizeof(NameIdPair)); i++) { if (NameIdMap[i].id == id) { name = NameIdMap[i].name; break; } } return name; } WOLFSSH_CHANNEL* ChannelNew(WOLFSSH* ssh, byte channelType, word32 initialWindowSz, word32 maxPacketSz) { WOLFSSH_CHANNEL* newChannel = NULL; WLOG(WS_LOG_DEBUG, "Entering ChannelNew()"); if (ssh == NULL || ssh->ctx == NULL) { WLOG(WS_LOG_DEBUG, "Trying to create new channel without ssh or ctx"); } else { void* heap = ssh->ctx->heap; newChannel = (WOLFSSH_CHANNEL*)WMALLOC(sizeof(WOLFSSH_CHANNEL), heap, DYNTYPE_CHANNEL); if (newChannel != NULL) { byte* buffer; buffer = (byte*)WMALLOC(initialWindowSz, heap, DYNTYPE_BUFFER); if (buffer != NULL) { WMEMSET(newChannel, 0, sizeof(WOLFSSH_CHANNEL)); newChannel->channelType = channelType; newChannel->channel = ssh->nextChannel++; newChannel->windowSz = initialWindowSz; newChannel->maxPacketSz = maxPacketSz; /* * In the context of the channel input buffer, the buffer is * a fixed size. The property length will be the insert point * for new received data. The property idx will be the pull * point for the data. */ newChannel->inputBuffer.heap = heap; newChannel->inputBuffer.buffer = buffer; newChannel->inputBuffer.bufferSz = initialWindowSz; newChannel->inputBuffer.dynamicFlag = 1; } else { WLOG(WS_LOG_DEBUG, "Unable to allocate new channel's buffer"); WFREE(newChannel, heap, DYNTYPE_CHANNEL); newChannel = NULL; } } else { WLOG(WS_LOG_DEBUG, "Unable to allocate new channel"); } } WLOG(WS_LOG_INFO, "Leaving ChannelNew(), ret = %p", newChannel); return newChannel; } void ChannelDelete(WOLFSSH_CHANNEL* channel, void* heap) { (void)heap; if (channel) { WFREE(channel->inputBuffer.buffer, channel->inputBuffer.heap, DYNTYPE_BUFFER); WFREE(channel, heap, DYNTYPE_CHANNEL); } } #define FIND_SELF 0 #define FIND_PEER 1 WOLFSSH_CHANNEL* ChannelFind(WOLFSSH* ssh, word32 channel, byte peer) { WOLFSSH_CHANNEL* findChannel = NULL; WLOG(WS_LOG_DEBUG, "Entering ChannelFind()"); if (ssh == NULL) { WLOG(WS_LOG_DEBUG, "Null ssh, not looking for channel"); } else { WOLFSSH_CHANNEL* list = ssh->channelList; word32 listSz = ssh->channelListSz; while (list && listSz) { if (channel == ((peer == FIND_PEER) ? list->peerChannel : list->channel)) { findChannel = list; break; } list = list->next; listSz--; } } WLOG(WS_LOG_DEBUG, "Leaving ChannelFind(): %p", findChannel); return findChannel; } int ChannelUpdate(WOLFSSH_CHANNEL* channel, word32 peerChannelId, word32 peerInitialWindowSz, word32 peerMaxPacketSz) { int ret = WS_SUCCESS; if (channel == NULL) ret = WS_BAD_ARGUMENT; else { channel->peerChannel = peerChannelId; channel->peerWindowSz = peerInitialWindowSz; channel->peerMaxPacketSz = peerMaxPacketSz; } return ret; } static int ChannelAppend(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering ChannelAppend()"); if (ssh == NULL || channel == NULL) ret = WS_BAD_ARGUMENT; if (ssh->channelList == NULL) { ssh->channelList = channel; ssh->channelListSz = 1; } else { WOLFSSH_CHANNEL* cur = ssh->channelList; while (cur->next != NULL) cur = cur->next; cur->next = channel; ssh->channelListSz++; } WLOG(WS_LOG_DEBUG, "Leaving ChannelAppend(), ret = %d", ret); return ret; } int ChannelRemove(WOLFSSH* ssh, word32 channel, byte peer) { int ret = WS_SUCCESS; WOLFSSH_CHANNEL* list; WLOG(WS_LOG_DEBUG, "Entering ChannelRemove()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; list = ssh->channelList; if (list == NULL) ret = WS_INVALID_CHANID; if (ret == WS_SUCCESS) { WOLFSSH_CHANNEL* prev = NULL; word32 listSz = ssh->channelListSz; while (list && listSz) { if (channel == ((peer == FIND_PEER) ? list->peerChannel : list->channel)) { if (prev == NULL) ssh->channelList = list->next; else prev->next = list->next; ChannelDelete(list, ssh->ctx->heap); ssh->channelListSz--; break; } prev = list; list = list->next; listSz--; } if (listSz == 0) ret = WS_INVALID_CHANID; } WLOG(WS_LOG_DEBUG, "Leaving ChannelRemove(), ret = %d", ret); return ret; } int ChannelPutData(WOLFSSH_CHANNEL* channel, byte* data, word32 dataSz) { Buffer* inBuf; WLOG(WS_LOG_DEBUG, "Entering ChannelPutData()"); if (channel == NULL || data == NULL) return WS_BAD_ARGUMENT; inBuf = &channel->inputBuffer; if (inBuf->length < inBuf->bufferSz && inBuf->length + dataSz <= inBuf->bufferSz) { WMEMCPY(inBuf->buffer + inBuf->length, data, dataSz); inBuf->length += dataSz; WLOG(WS_LOG_INFO, " dataSz = %u", dataSz); WLOG(WS_LOG_INFO, " windowSz = %u", channel->windowSz); channel->windowSz -= dataSz; WLOG(WS_LOG_INFO, " windowSz = %u", channel->windowSz); } else { return WS_RECV_OVERFLOW_E; } return WS_SUCCESS; } int BufferInit(Buffer* buffer, word32 size, void* heap) { if (buffer == NULL) return WS_BAD_ARGUMENT; if (size <= STATIC_BUFFER_LEN) size = STATIC_BUFFER_LEN; WMEMSET(buffer, 0, sizeof(Buffer)); buffer->heap = heap; buffer->bufferSz = size; if (size > STATIC_BUFFER_LEN) { buffer->buffer = (byte*)WMALLOC(size, heap, DYNTYPE_BUFFER); if (buffer->buffer == NULL) return WS_MEMORY_E; buffer->dynamicFlag = 1; } else buffer->buffer = buffer->staticBuffer; return WS_SUCCESS; } int GrowBuffer(Buffer* buf, word32 sz, word32 usedSz) { #if 0 WLOG(WS_LOG_DEBUG, "GB: buf = %p", buf); WLOG(WS_LOG_DEBUG, "GB: sz = %d", sz); WLOG(WS_LOG_DEBUG, "GB: usedSz = %d", usedSz); #endif /* New buffer will end up being sz+usedSz long * empty space at the head of the buffer will be compressed */ if (buf != NULL) { word32 newSz = sz + usedSz; /*WLOG(WS_LOG_DEBUG, "GB: newSz = %d", newSz);*/ if (newSz > buf->bufferSz) { byte* newBuffer = (byte*)WMALLOC(newSz, buf->heap, DYNTYPE_BUFFER); if (newBuffer == NULL) return WS_MEMORY_E; /*WLOG(WS_LOG_DEBUG, "GB: resizing buffer");*/ if (buf->length > 0) WMEMCPY(newBuffer, buf->buffer + buf->idx, usedSz); if (!buf->dynamicFlag) buf->dynamicFlag = 1; else WFREE(buf->buffer, buf->heap, DYNTYPE_BUFFER); buf->buffer = newBuffer; buf->bufferSz = newSz; buf->length = usedSz; buf->idx = 0; } } return WS_SUCCESS; } void ShrinkBuffer(Buffer* buf, int forcedFree) { WLOG(WS_LOG_DEBUG, "Entering ShrinkBuffer()"); if (buf != NULL) { word32 usedSz = buf->length - buf->idx; WLOG(WS_LOG_DEBUG, "SB: usedSz = %u, forcedFree = %u", usedSz, forcedFree); if (!forcedFree && usedSz > STATIC_BUFFER_LEN) return; if (!forcedFree && usedSz) { WLOG(WS_LOG_DEBUG, "SB: shifting down"); WMEMCPY(buf->staticBuffer, buf->buffer + buf->idx, usedSz); } if (buf->dynamicFlag) { WLOG(WS_LOG_DEBUG, "SB: releasing dynamic buffer"); WFREE(buf->buffer, buf->heap, DYNTYPE_BUFFER); } buf->dynamicFlag = 0; buf->buffer = buf->staticBuffer; buf->bufferSz = STATIC_BUFFER_LEN; buf->length = forcedFree ? 0 : usedSz; buf->idx = 0; } WLOG(WS_LOG_DEBUG, "Leaving ShrinkBuffer()"); } static int Receive(WOLFSSH* ssh, byte* buf, word32 sz) { int recvd; if (ssh->ctx->ioRecvCb == NULL) { WLOG(WS_LOG_DEBUG, "Your IO Recv callback is null, please set"); return -1; } retry: recvd = ssh->ctx->ioRecvCb(ssh, buf, sz, ssh->ioReadCtx); WLOG(WS_LOG_DEBUG, "Receive: recvd = %d", recvd); if (recvd < 0) switch (recvd) { case WS_CBIO_ERR_GENERAL: /* general/unknown error */ return -1; case WS_CBIO_ERR_WANT_READ: /* want read, would block */ return WS_WANT_READ; case WS_CBIO_ERR_CONN_RST: /* connection reset */ ssh->connReset = 1; return -1; case WS_CBIO_ERR_ISR: /* interrupt */ goto retry; case WS_CBIO_ERR_CONN_CLOSE: /* peer closed connection */ ssh->isClosed = 1; return -1; case WS_CBIO_ERR_TIMEOUT: return -1; default: return recvd; } return recvd; } static int GetInputText(WOLFSSH* ssh, byte** pEol) { int gotLine = 0; int inSz = 255; int in; char *eol; if (GrowBuffer(&ssh->inputBuffer, inSz, 0) < 0) return WS_MEMORY_E; do { in = Receive(ssh, ssh->inputBuffer.buffer + ssh->inputBuffer.length, inSz); if (in == -1) return WS_SOCKET_ERROR_E; if (in == WS_WANT_READ) return WS_WANT_READ; if (in > inSz) return WS_RECV_OVERFLOW_E; ssh->inputBuffer.length += in; inSz -= in; eol = WSTRNSTR((const char*)ssh->inputBuffer.buffer, "\r\n", ssh->inputBuffer.length); if (eol) gotLine = 1; } while (!gotLine && inSz); if (pEol) *pEol = (byte*)eol; return (gotLine ? WS_SUCCESS : WS_VERSION_E); } static int SendBuffered(WOLFSSH* ssh) { WLOG(WS_LOG_DEBUG, "Entering SendBuffered()"); if (ssh->ctx->ioSendCb == NULL) { WLOG(WS_LOG_DEBUG, "Your IO Send callback is null, please set"); return WS_SOCKET_ERROR_E; } while (ssh->outputBuffer.length > 0) { int sent = ssh->ctx->ioSendCb(ssh, ssh->outputBuffer.buffer + ssh->outputBuffer.idx, ssh->outputBuffer.length, ssh->ioReadCtx); if (sent < 0) { switch (sent) { case WS_CBIO_ERR_WANT_WRITE: /* want write, would block */ return WS_WANT_WRITE; case WS_CBIO_ERR_CONN_RST: /* connection reset */ ssh->connReset = 1; break; case WS_CBIO_ERR_CONN_CLOSE: /* peer closed connection */ ssh->isClosed = 1; break; } return WS_SOCKET_ERROR_E; } if ((word32)sent > ssh->outputBuffer.length) { WLOG(WS_LOG_DEBUG, "SendBuffered() out of bounds read"); return WS_SEND_OOB_READ_E; } ssh->outputBuffer.idx += sent; ssh->outputBuffer.length -= sent; } ssh->outputBuffer.idx = 0; WLOG(WS_LOG_DEBUG, "SB: Shrinking output buffer"); ShrinkBuffer(&ssh->outputBuffer, 0); HighwaterCheck(ssh, WOLFSSH_HWSIDE_TRANSMIT); return WS_SUCCESS; } static int GetInputData(WOLFSSH* ssh, word32 size) { int in; int inSz; int maxLength; int usedLength; /* check max input length */ usedLength = ssh->inputBuffer.length - ssh->inputBuffer.idx; maxLength = ssh->inputBuffer.bufferSz - usedLength; inSz = (int)(size - usedLength); /* from last partial read */ #if 0 WLOG(WS_LOG_DEBUG, "GID: size = %u", size); WLOG(WS_LOG_DEBUG, "GID: usedLength = %d", usedLength); WLOG(WS_LOG_DEBUG, "GID: maxLength = %d", maxLength); WLOG(WS_LOG_DEBUG, "GID: inSz = %d", inSz); #endif /* * usedLength - how much untouched data is in the buffer * maxLength - how much empty space is in the buffer * inSz - difference between requested data and empty space in the buffer * how much more we need to allocate */ if (inSz <= 0) return WS_SUCCESS; /* * If we need more space than there is left in the buffer grow buffer. * Growing the buffer also compresses empty space at the head of the * buffer and resets idx to 0. */ if (inSz > maxLength) { if (GrowBuffer(&ssh->inputBuffer, size, usedLength) < 0) return WS_MEMORY_E; } /* Put buffer data at start if not there */ /* Compress the buffer if needed, i.e. buffer idx is non-zero */ if (usedLength > 0 && ssh->inputBuffer.idx != 0) { WMEMMOVE(ssh->inputBuffer.buffer, ssh->inputBuffer.buffer + ssh->inputBuffer.idx, usedLength); } /* remove processed data */ ssh->inputBuffer.idx = 0; ssh->inputBuffer.length = usedLength; /* read data from network */ do { in = Receive(ssh, ssh->inputBuffer.buffer + ssh->inputBuffer.length, inSz); if (in == -1) { //ESP_LOGE("WOLFSSH", "GetInputData: WS_SOCKET_ERROR_E"); return WS_SOCKET_ERROR_E; } if (in == WS_WANT_READ) { //ESP_LOGE("WOLFSSH", "GetInputData: WS_WANT_READ"); return WS_WANT_READ; } if (in > inSz) { //ESP_LOGE("WOLFSSH", "GetInputData: WS_RECV_OVERFLOW_E"); return WS_RECV_OVERFLOW_E; } ssh->inputBuffer.length += in; inSz -= in; } while (ssh->inputBuffer.length < size); return 0; } static int GetBoolean(byte* v, byte* buf, word32 len, word32* idx) { int result = WS_BUFFER_E; if (*idx < len) { *v = buf[*idx]; *idx += BOOLEAN_SZ; result = WS_SUCCESS; } return result; } void ato32(const byte* c, word32 * u32){} static int GetUint32(word32* v, byte* buf, word32 len, word32* idx) { int result = WS_BUFFER_E; if (*idx < len && *idx + UINT32_SZ <= len) { ato32(buf + *idx, v); *idx += UINT32_SZ; result = WS_SUCCESS; } return result; } /* Gets the size of the mpint, and puts the pointer to the start of * buf's number into *mpint. This function does not copy. */ static int GetMpint(word32* mpintSz, byte** mpint, byte* buf, word32 len, word32* idx) { int result; result = GetUint32(mpintSz, buf, len, idx); if (result == WS_SUCCESS) { result = WS_BUFFER_E; if (*idx < len && *idx + *mpintSz <= len) { *mpint = buf + *idx; *idx += *mpintSz; result = WS_SUCCESS; } } return result; } /* Gets the size of a string, copies it as much of it as will fit in * the provided buffer, and terminates it with a NULL. */ static int GetString(char* s, word32* sSz, byte* buf, word32 len, word32 *idx) { int result; word32 strSz; result = GetUint32(&strSz, buf, len, idx); if (result == WS_SUCCESS) { result = WS_BUFFER_E; if (*idx < len && *idx + strSz <= len) { *sSz = (strSz >= *sSz) ? *sSz : strSz; WMEMCPY(s, buf + *idx, *sSz); *idx += strSz; s[*sSz] = 0; result = WS_SUCCESS; } } return result; } static int GetNameList(byte* idList, word32* idListSz, byte* buf, word32 len, word32* idx) { byte idListIdx; word32 nameListSz, nameListIdx; word32 begin; byte* name; word32 nameSz; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering GetNameList()"); if (idList == NULL || idListSz == NULL || buf == NULL || len == 0 || idx == NULL) { ret = WS_BAD_ARGUMENT; } /* * This iterates across a name list and finds names that end in either the * comma delimeter or with the end of the list. */ if (ret == WS_SUCCESS) { begin = *idx; if (begin >= len || begin + 4 >= len) ret = WS_BUFFER_E; } if (ret == WS_SUCCESS) ret = GetUint32(&nameListSz, buf, len, &begin); /* The strings we want are now in the bounds of the message, and the * length of the list. Find the commas, or end of list, and then decode * the values. */ if (ret == WS_SUCCESS) { name = buf + begin; nameSz = 0; nameListIdx = 0; idListIdx = 0; while (nameListIdx < nameListSz) { nameListIdx++; if (nameListIdx == nameListSz) nameSz++; if (nameListIdx == nameListSz || name[nameSz] == ',') { byte id; id = NameToId((char*)name, nameSz); { const char* displayName = IdToName(id); if (displayName) { WLOG(WS_LOG_DEBUG, "DNL: name ID = %s", displayName); } } if (id != ID_UNKNOWN) idList[idListIdx++] = id; name += 1 + nameSz; nameSz = 0; } else nameSz++; } begin += nameListSz; *idListSz = idListIdx; *idx = begin; } WLOG(WS_LOG_DEBUG, "Leaving GetNameList(), ret = %d", ret); return ret; } static const byte cannedEncAlgo[] = {ID_AES128_GCM, ID_AES128_CBC}; static const byte cannedMacAlgo[] = {ID_HMAC_SHA2_256, ID_HMAC_SHA1_96, ID_HMAC_SHA1}; static const byte cannedKeyAlgoRsa[] = {ID_SSH_RSA}; static const byte cannedKeyAlgoEcc256[] = {ID_ECDSA_SHA2_NISTP256}; static const byte cannedKeyAlgoEcc384[] = {ID_ECDSA_SHA2_NISTP384}; static const byte cannedKeyAlgoEcc521[] = {ID_ECDSA_SHA2_NISTP521}; static const byte cannedKexAlgo[] = {ID_ECDH_SHA2_NISTP256, ID_DH_GEX_SHA256, ID_DH_GROUP14_SHA1, ID_DH_GROUP1_SHA1}; static const word32 cannedEncAlgoSz = sizeof(cannedEncAlgo); static const word32 cannedMacAlgoSz = sizeof(cannedMacAlgo); static const word32 cannedKeyAlgoRsaSz = sizeof(cannedKeyAlgoRsa); static const word32 cannedKeyAlgoEcc256Sz = sizeof(cannedKeyAlgoEcc256); static const word32 cannedKeyAlgoEcc384Sz = sizeof(cannedKeyAlgoEcc384); static const word32 cannedKeyAlgoEcc521Sz = sizeof(cannedKeyAlgoEcc521); static const word32 cannedKexAlgoSz = sizeof(cannedKexAlgo); static byte MatchIdLists(const byte* left, word32 leftSz, const byte* right, word32 rightSz) { word32 i, j; if (left != NULL && leftSz > 0 && right != NULL && rightSz > 0) { for (i = 0; i < leftSz; i++) { for (j = 0; j < rightSz; j++) { if (left[i] == right[j]) { #if 0 WLOG(WS_LOG_DEBUG, "MID: matched %s", IdToName(left[i])); #endif return left[i]; } } } } return ID_UNKNOWN; } static INLINE byte BlockSzForId(byte id) { switch (id) { case ID_AES128_CBC: case ID_AES128_GCM: return AES_BLOCK_SIZE; default: return 0; } } static INLINE byte MacSzForId(byte id) { switch (id) { case ID_HMAC_SHA1: return SHA_DIGEST_SIZE; case ID_HMAC_SHA1_96: return SHA1_96_SZ; case ID_HMAC_SHA2_256: return SHA256_DIGEST_SIZE; default: return 0; } } static INLINE byte KeySzForId(byte id) { switch (id) { case ID_HMAC_SHA1: case ID_HMAC_SHA1_96: return SHA_DIGEST_SIZE; case ID_HMAC_SHA2_256: return SHA256_DIGEST_SIZE; case ID_AES128_CBC: case ID_AES128_GCM: return AES_BLOCK_SIZE; default: return 0; } } static INLINE byte HashForId(byte id) { switch (id) { case ID_DH_GROUP1_SHA1: case ID_DH_GROUP14_SHA1: case ID_SSH_RSA: return WC_HASH_TYPE_SHA; case ID_DH_GEX_SHA256: case ID_ECDH_SHA2_NISTP256: case ID_ECDSA_SHA2_NISTP256: return WC_HASH_TYPE_SHA256; case ID_ECDH_SHA2_NISTP384: case ID_ECDSA_SHA2_NISTP384: return WC_HASH_TYPE_SHA384; case ID_ECDH_SHA2_NISTP521: case ID_ECDSA_SHA2_NISTP521: return WC_HASH_TYPE_SHA512; default: return WC_HASH_TYPE_NONE; } } static INLINE int wcPrimeForId(byte id) { switch (id) { case ID_ECDH_SHA2_NISTP256: case ID_ECDSA_SHA2_NISTP256: return ECC_SECP256R1; case ID_ECDH_SHA2_NISTP384: case ID_ECDSA_SHA2_NISTP384: return ECC_SECP384R1; case ID_ECDH_SHA2_NISTP521: case ID_ECDSA_SHA2_NISTP521: return ECC_SECP521R1; default: return ECC_CURVE_INVALID; } } static INLINE const char *PrimeNameForId(byte id) { switch (id) { case ID_ECDH_SHA2_NISTP256: case ID_ECDSA_SHA2_NISTP256: return "nistp256"; case ID_ECDH_SHA2_NISTP384: case ID_ECDSA_SHA2_NISTP384: return "nistp384"; case ID_ECDH_SHA2_NISTP521: case ID_ECDSA_SHA2_NISTP521: return "nistp521"; default: return "unknown"; } } static INLINE byte AeadModeForId(byte id) { return (id == ID_AES128_GCM); } static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { int ret = WS_SUCCESS; byte algoId; byte list[6]; word32 listSz; word32 skipSz; word32 begin; WLOG(WS_LOG_DEBUG, "Entering DoKexInit()"); if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) ret = WS_BAD_ARGUMENT; /* * I don't need to save what the client sends here. I should decode * each list into a local array of IDs, and pick the one the peer is * using that's on my known list, or verify that the one the peer can * support the other direction is on my known list. All I need to do * is save the actual values. */ if (ssh->handshake == NULL) { ssh->handshake = HandshakeInfoNew(ssh->ctx->heap); if (ssh->handshake == NULL) { WLOG(WS_LOG_DEBUG, "Couldn't allocate handshake info"); ret = WS_MEMORY_E; } } if (ret == WS_SUCCESS) { begin = *idx; /* Check that the cookie exists inside the message */ if (begin + COOKIE_SZ > len) { /* error, out of bounds */ ret = WS_PARSE_E; } else { /* Move past the cookie. */ begin += COOKIE_SZ; } } /* KEX Algorithms */ if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DKI: KEX Algorithms"); listSz = 2; ret = GetNameList(list, &listSz, buf, len, &begin); if (ret == WS_SUCCESS) { algoId = MatchIdLists(list, listSz, cannedKexAlgo, cannedKexAlgoSz); if (algoId == ID_UNKNOWN) { WLOG(WS_LOG_DEBUG, "Unable to negotiate KEX Algo"); ret = WS_INVALID_ALGO_ID; } else { ssh->handshake->kexId = algoId; ssh->handshake->hashId = HashForId(algoId); } } } /* Server Host Key Algorithms */ if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DKI: Server Host Key Algorithms"); listSz = 1; ret = GetNameList(list, &listSz, buf, len, &begin); if (ret == WS_SUCCESS) { const byte *cannedKeyAlgo; word32 cannedKeyAlgoSz; switch (ssh->ctx->useEcc) { case ECC_SECP256R1: cannedKeyAlgo = cannedKeyAlgoEcc256; cannedKeyAlgoSz = cannedKeyAlgoEcc256Sz; break; case ECC_SECP384R1: cannedKeyAlgo = cannedKeyAlgoEcc384; cannedKeyAlgoSz = cannedKeyAlgoEcc384Sz; break; case ECC_SECP521R1: cannedKeyAlgo = cannedKeyAlgoEcc521; cannedKeyAlgoSz = cannedKeyAlgoEcc521Sz; break; default: cannedKeyAlgo = cannedKeyAlgoRsa; cannedKeyAlgoSz = cannedKeyAlgoRsaSz; } algoId = MatchIdLists(list, listSz, cannedKeyAlgo, cannedKeyAlgoSz); if (algoId == ID_UNKNOWN) { WLOG(WS_LOG_DEBUG, "Unable to negotiate Server Host Key Algo"); return WS_INVALID_ALGO_ID; } else ssh->handshake->pubKeyId = algoId; } } /* Enc Algorithms - Client to Server */ if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DKI: Enc Algorithms - Client to Server"); listSz = 3; ret = GetNameList(list, &listSz, buf, len, &begin); if (ret == WS_SUCCESS) { algoId = MatchIdLists(list, listSz, cannedEncAlgo, cannedEncAlgoSz); if (algoId == ID_UNKNOWN) { WLOG(WS_LOG_DEBUG, "Unable to negotiate Encryption Algo C2S"); ret = WS_INVALID_ALGO_ID; } } } /* Enc Algorithms - Server to Client */ if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DKI: Enc Algorithms - Server to Client"); listSz = 3; ret = GetNameList(list, &listSz, buf, len, &begin); if (MatchIdLists(list, listSz, &algoId, 1) == ID_UNKNOWN) { WLOG(WS_LOG_DEBUG, "Unable to negotiate Encryption Algo S2C"); ret = WS_INVALID_ALGO_ID; } else { ssh->handshake->encryptId = algoId; ssh->handshake->aeadMode = AeadModeForId(algoId); ssh->handshake->blockSz = BlockSzForId(algoId); ssh->handshake->keys.encKeySz = ssh->handshake->peerKeys.encKeySz = KeySzForId(algoId); if (!ssh->handshake->aeadMode) { ssh->handshake->keys.ivSz = ssh->handshake->peerKeys.ivSz = ssh->handshake->blockSz; } else { ssh->handshake->keys.ivSz = ssh->handshake->peerKeys.ivSz = AEAD_NONCE_SZ; ssh->handshake->macSz = ssh->handshake->blockSz; } } } /* MAC Algorithms - Client to Server */ if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DKI: MAC Algorithms - Client to Server"); listSz = 2; ret = GetNameList(list, &listSz, buf, len, &begin); if (ret == WS_SUCCESS && !ssh->aeadMode) { algoId = MatchIdLists(list, listSz, cannedMacAlgo, cannedMacAlgoSz); if (algoId == ID_UNKNOWN) { WLOG(WS_LOG_DEBUG, "Unable to negotiate MAC Algo C2S"); ret = WS_INVALID_ALGO_ID; } } } /* MAC Algorithms - Server to Client */ if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DKI: MAC Algorithms - Server to Client"); listSz = 2; ret = GetNameList(list, &listSz, buf, len, &begin); if (ret == WS_SUCCESS && !ssh->handshake->aeadMode) { if (MatchIdLists(list, listSz, &algoId, 1) == ID_UNKNOWN) { WLOG(WS_LOG_DEBUG, "Unable to negotiate MAC Algo S2C"); ret = WS_INVALID_ALGO_ID; } else { ssh->handshake->macId = algoId; ssh->handshake->macSz = MacSzForId(algoId); ssh->handshake->keys.macKeySz = ssh->handshake->peerKeys.macKeySz = KeySzForId(algoId); } } } /* Compression Algorithms - Client to Server */ if (ret == WS_SUCCESS) { /* The compression algorithm lists should have none as a value. */ algoId = ID_NONE; WLOG(WS_LOG_DEBUG, "DKI: Compression Algorithms - Client to Server"); listSz = 1; ret = GetNameList(list, &listSz, buf, len, &begin); if (ret == WS_SUCCESS) { if (MatchIdLists(list, listSz, &algoId, 1) == ID_UNKNOWN) { WLOG(WS_LOG_DEBUG, "Unable to negotiate Compression Algo C2S"); ret = WS_INVALID_ALGO_ID; } } } /* Compression Algorithms - Server to Client */ if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DKI: Compression Algorithms - Server to Client"); listSz = 1; ret = GetNameList(list, &listSz, buf, len, &begin); if (ret == WS_SUCCESS) { if (MatchIdLists(list, listSz, &algoId, 1) == ID_UNKNOWN) { WLOG(WS_LOG_DEBUG, "Unable to negotiate Compression Algo S2C"); ret = WS_INVALID_ALGO_ID; } } } /* Languages - Client to Server, skip */ if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DKI: Languages - Client to Server"); ret = GetUint32(&skipSz, buf, len, &begin); if (ret == WS_SUCCESS) begin += skipSz; } /* Languages - Server to Client, skip */ if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DKI: Languages - Server to Client"); ret = GetUint32(&skipSz, buf, len, &begin); if (ret == WS_SUCCESS) begin += skipSz; } /* First KEX Packet Follows */ if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DKI: KEX Packet Follows"); ret = GetBoolean(&ssh->handshake->kexPacketFollows, buf, len, &begin); } /* Skip the "for future use" length. */ if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DKI: For Future Use"); ret = GetUint32(&skipSz, buf, len, &begin); if (ret == WS_SUCCESS) begin += skipSz; } if (ret == WS_SUCCESS) { byte scratchLen[LENGTH_SZ]; word32 strSz; if (!ssh->isKeying) { WLOG(WS_LOG_DEBUG, "Keying initiated"); ret = SendKexInit(ssh); } if (ret == WS_SUCCESS) ret = wc_HashInit(&ssh->handshake->hash, ssh->handshake->hashId); if (ret == WS_SUCCESS) { if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, ssh->peerProtoId, ssh->peerProtoIdSz); } if (ret == WS_SUCCESS) { strSz = (word32)WSTRLEN(sshProtoIdStr) - SSH_PROTO_EOL_SZ; c32toa(strSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } if (ret == WS_SUCCESS) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, (const byte*)sshProtoIdStr, strSz); if (ret == WS_SUCCESS) { if (ssh->ctx->side == WOLFSSH_ENDPOINT_CLIENT) { ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, ssh->peerProtoId, ssh->peerProtoIdSz); if (ret == WS_SUCCESS) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, ssh->handshake->kexInit, ssh->handshake->kexInitSz); } } if (ret == WS_SUCCESS) { c32toa(len + 1, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } if (ret == WS_SUCCESS) { scratchLen[0] = MSGID_KEXINIT; ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, MSG_ID_SZ); } if (ret == WS_SUCCESS) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, buf, len); if (ret == WS_SUCCESS) { if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, ssh->handshake->kexInit, ssh->handshake->kexInitSz); } if (ret == WS_SUCCESS) { *idx = begin; if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) ssh->clientState = CLIENT_KEXINIT_DONE; else ssh->serverState = SERVER_KEXINIT_DONE; } } WLOG(WS_LOG_DEBUG, "Leaving DoKexInit(), ret = %d", ret); return ret; } static const byte dhGenerator[] = { 2 }; static const byte dhPrimeGroup1[] = { /* SSH DH Group 1 (Oakley Group 2, 1024-bit MODP Group, RFC 2409) */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const byte dhPrimeGroup14[] = { /* SSH DH Group 14 (Oakley Group 14, 2048-bit MODP Group, RFC 3526) */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static const word32 dhGeneratorSz = sizeof(dhGenerator); static const word32 dhPrimeGroup1Sz = sizeof(dhPrimeGroup1); static const word32 dhPrimeGroup14Sz = sizeof(dhPrimeGroup14); static int DoKexDhInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { /* First get the length of the MP_INT, and then add in the hash of the * mp_int value of e as it appears in the packet. After that, decode e * into an mp_int struct for the DH calculation by wolfCrypt. * * This function also works as MSGID_KEXECDH_INIT (30). That message * has the same format as MSGID_KEXDH_INIT, except it is the ECDH Q value * in the message isn't of the DH e value. Treat the Q as e. */ /* DYNTYPE_DH */ byte* e; word32 eSz; word32 begin; int ret = WS_SUCCESS; if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { begin = *idx; ret = GetUint32(&eSz, buf, len, &begin); } if (ret == WS_SUCCESS) { e = buf + begin; begin += eSz; if (eSz <= sizeof(ssh->handshake->e)) { WMEMCPY(ssh->handshake->e, e, eSz); ssh->handshake->eSz = eSz; } ssh->clientState = CLIENT_KEXDH_INIT_DONE; *idx = begin; //ESP_LOGI("WOLFSSH", "Sending KexDH Reply to client"); ret = SendKexDhReply(ssh); } return ret; } static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { byte* pubKey; word32 pubKeySz; byte* f; word32 fSz; byte* sig; word32 sigSz; word32 scratch; byte scratchLen[LENGTH_SZ]; word32 kPad = 0; struct { byte useRsa; word32 keySz; union { struct { RsaKey key; } rsa; struct { ecc_key key; } ecc; } sk; } sigKeyBlock; word32 begin; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering DoKexDhReply()"); if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { begin = *idx; pubKey = buf + begin; ret = GetUint32(&pubKeySz, buf, len, &begin); } if (ret == WS_SUCCESS) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, pubKey, pubKeySz + LENGTH_SZ); if (ret == WS_SUCCESS) { pubKey = buf + begin; begin += pubKeySz; } /* If using DH-GEX include the GEX specific values. */ if (ssh->handshake->kexId == ID_DH_GEX_SHA256) { byte primeGroupPad = 0, generatorPad = 0; /* Hash in the client's requested minimum key size. */ if (ret == 0) { c32toa(ssh->handshake->dhGexMinSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } /* Hash in the client's requested preferred key size. */ if (ret == 0) { c32toa(ssh->handshake->dhGexPreferredSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } /* Hash in the client's requested maximum key size. */ if (ret == 0) { c32toa(ssh->handshake->dhGexMaxSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } /* Add a pad byte if the mpint has the MSB set. */ if (ret == 0) { if (ssh->handshake->primeGroup[0] & 0x80) primeGroupPad = 1; /* Hash in the length of the GEX prime group. */ c32toa(ssh->handshake->primeGroupSz + primeGroupPad, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } /* Hash in the pad byte for the GEX prime group. */ if (ret == 0) { if (primeGroupPad) { scratchLen[0] = 0; ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, 1); } } /* Hash in the GEX prime group. */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, ssh->handshake->primeGroup, ssh->handshake->primeGroupSz); /* Add a pad byte if the mpint has the MSB set. */ if (ret == 0) { if (ssh->handshake->generator[0] & 0x80) generatorPad = 1; /* Hash in the length of the GEX generator. */ c32toa(ssh->handshake->generatorSz + generatorPad, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } /* Hash in the pad byte for the GEX generator. */ if (ret == 0) { if (generatorPad) { scratchLen[0] = 0; ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, 1); } } /* Hash in the GEX generator. */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, ssh->handshake->generator, ssh->handshake->generatorSz); } /* Hash in the size of the client's DH e-value (ECDH Q-value). */ if (ret == 0) { c32toa(ssh->handshake->eSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } /* Hash in the client's DH e-value (ECDH Q-value). */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, ssh->handshake->e, ssh->handshake->eSz); /* Get and hash in the server's DH f-value (ECDH Q-value) */ if (ret == WS_SUCCESS) { f = buf + begin; ret = GetUint32(&fSz, buf, len, &begin); } if (ret == WS_SUCCESS) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, f, fSz + LENGTH_SZ); if (ret == WS_SUCCESS) { f = buf + begin; begin += fSz; ret = GetUint32(&sigSz, buf, len, &begin); } if (ret == WS_SUCCESS) { sig = buf + begin; begin += sigSz; *idx = begin; /* Load in the server's public signing key */ sigKeyBlock.useRsa = ssh->handshake->pubKeyId == ID_SSH_RSA; if (sigKeyBlock.useRsa) { byte* e; word32 eSz; byte* n; word32 nSz; word32 pubKeyIdx = 0; ret = wc_InitRsaKey(&sigKeyBlock.sk.rsa.key, ssh->ctx->heap); if (ret != 0) ret = WS_RSA_E; if (ret == 0) ret = GetUint32(&scratch, pubKey, pubKeySz, &pubKeyIdx); /* This is the algo name. */ if (ret == WS_SUCCESS) { pubKeyIdx += scratch; ret = GetUint32(&eSz, pubKey, pubKeySz, &pubKeyIdx); } if (ret == WS_SUCCESS) { e = pubKey + pubKeyIdx; pubKeyIdx += eSz; ret = GetUint32(&nSz, pubKey, pubKeySz, &pubKeyIdx); } if (ret == WS_SUCCESS) { n = pubKey + pubKeyIdx; ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, &sigKeyBlock.sk.rsa.key); } if (ret == 0) sigKeyBlock.keySz = sizeof(sigKeyBlock.sk.rsa.key); else ret = WS_RSA_E; } else { ret = wc_ecc_init_ex(&sigKeyBlock.sk.ecc.key, ssh->ctx->heap, INVALID_DEVID); if (ret == 0) ret = wc_ecc_import_x963(pubKey, pubKeySz, &sigKeyBlock.sk.ecc.key); if (ret == 0) sigKeyBlock.keySz = sizeof(sigKeyBlock.sk.ecc.key); else ret = WS_ECC_E; } /* Generate and hash in the shared secret */ if (ret == 0) { if (!ssh->handshake->useEcc) { ret = wc_DhAgree(&ssh->handshake->privKey.dh, ssh->k, &ssh->kSz, ssh->handshake->x, ssh->handshake->xSz, f, fSz); ForceZero(ssh->handshake->x, ssh->handshake->xSz); wc_FreeDhKey(&ssh->handshake->privKey.dh); } else { ecc_key key; ret = wc_ecc_init(&key); if (ret == 0) ret = wc_ecc_import_x963(f, fSz, &key); if (ret == 0) ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc, &key, ssh->k, &ssh->kSz); wc_ecc_free(&key); wc_ecc_free(&ssh->handshake->privKey.ecc); } } /* Hash in the shared secret K. */ if (ret == 0) { kPad = (ssh->k[0] & 0x80) ? 1 : 0; c32toa(ssh->kSz + kPad, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } if (ret == 0) { if (kPad) { scratchLen[0] = 0; ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, 1); } } if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, ssh->k, ssh->kSz); /* Save the exchange hash value H, and session ID. */ if (ret == 0) ret = wc_HashFinal(&ssh->handshake->hash, ssh->handshake->hashId, ssh->h); if (ret == 0) { ssh->hSz = wc_HashGetDigestSize(ssh->handshake->hashId); if (ssh->sessionIdSz == 0) { WMEMCPY(ssh->sessionId, ssh->h, ssh->hSz); ssh->sessionIdSz = ssh->hSz; } } if (ret != WS_SUCCESS) ret = WS_CRYPTO_FAILED; } /* Verify h with the server's public key. */ if (ret == WS_SUCCESS) { /* Skip past the sig name. Check it, though. Other SSH implementations * do the verify based on the name, despite what was agreed upon. XXX*/ begin = 0; ret = GetUint32(&scratch, sig, sigSz, &begin); if (ret == WS_SUCCESS) { begin += scratch; ret = GetUint32(&scratch, sig, sigSz, &begin); } if (ret == WS_SUCCESS) { sig = sig + begin; sigSz = scratch; ret = wc_SignatureVerify(HashForId(ssh->handshake->pubKeyId), sigKeyBlock.useRsa ? WC_SIGNATURE_TYPE_RSA_W_ENC : WC_SIGNATURE_TYPE_ECC, ssh->h, ssh->hSz, sig, sigSz, &sigKeyBlock.sk, sigKeyBlock.keySz); if (ret != 0) { WLOG(WS_LOG_DEBUG, "DoKexDhReply: Signature Verify fail (%d)", ret); ret = sigKeyBlock.useRsa ? WS_RSA_E : WS_ECC_E; } } } if (sigKeyBlock.useRsa) wc_FreeRsaKey(&sigKeyBlock.sk.rsa.key); else wc_ecc_free(&sigKeyBlock.sk.ecc.key); if (ret == WS_SUCCESS) ret = GenerateKeys(ssh); if (ret == WS_SUCCESS) ret = SendNewKeys(ssh); WLOG(WS_LOG_DEBUG, "Leaving DoKexDhReply(), ret = %d", ret); return ret; } static int DoNewKeys(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { int ret = WS_SUCCESS; (void)buf; (void)len; (void)idx; if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { ssh->peerEncryptId = ssh->handshake->encryptId; ssh->peerMacId = ssh->handshake->macId; ssh->peerBlockSz = ssh->handshake->blockSz; ssh->peerMacSz = ssh->handshake->macSz; ssh->peerAeadMode = ssh->handshake->aeadMode; WMEMCPY(&ssh->peerKeys, &ssh->handshake->peerKeys, sizeof(Keys)); switch (ssh->peerEncryptId) { case ID_NONE: WLOG(WS_LOG_DEBUG, "DNK: peer using cipher none"); //ESP_LOGI("WOLFSSH", "DNK: peer using cipher none"); break; case ID_AES128_CBC: WLOG(WS_LOG_DEBUG, "DNK: peer using cipher aes128-cbc"); ret = wc_AesSetKey(&ssh->decryptCipher.aes, ssh->peerKeys.encKey, ssh->peerKeys.encKeySz, ssh->peerKeys.iv, AES_DECRYPTION); //ESP_LOGI("WOLFSSH", "DNK: peer using cipher aes128-cbc"); break; case ID_AES128_GCM: WLOG(WS_LOG_DEBUG, "DNK: peer using cipher aes128-gcm"); ret = wc_AesGcmSetKey(&ssh->decryptCipher.aes, ssh->peerKeys.encKey, ssh->peerKeys.encKeySz); //ESP_LOGI("WOLFSSH", "DNK: peer using cipher aes128-gcm"); break; default: WLOG(WS_LOG_DEBUG, "DNK: peer using cipher invalid"); //ESP_LOGI("WOLFSSH", "DNK: peer using cipher invalid"); break; } if (ret == 0) ret = WS_SUCCESS; else ret = WS_CRYPTO_FAILED; } if (ret == WS_SUCCESS) { ssh->rxCount = 0; ssh->highwaterFlag = 0; ssh->isKeying = 0; HandshakeInfoFree(ssh->handshake, ssh->ctx->heap); ssh->handshake = NULL; WLOG(WS_LOG_DEBUG, "Keying completed"); //ESP_LOGI("WOLFSSH", "Keying completed"); } return ret; } static int DoKexDhGexRequest(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { word32 begin; int ret = WS_SUCCESS; if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { begin = *idx; ret = GetUint32(&ssh->handshake->dhGexMinSz, buf, len, &begin); } if (ret == WS_SUCCESS) { ret = GetUint32(&ssh->handshake->dhGexPreferredSz, buf, len, &begin); } if (ret == WS_SUCCESS) { ret = GetUint32(&ssh->handshake->dhGexMaxSz, buf, len, &begin); } if (ret == WS_SUCCESS) { WLOG(WS_LOG_INFO, " min = %u, preferred = %u, max = %u", ssh->handshake->dhGexMinSz, ssh->handshake->dhGexPreferredSz, ssh->handshake->dhGexMaxSz); *idx = begin; ret = SendKexDhGexGroup(ssh); } return ret; } static int DoKexDhGexGroup(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { byte* primeGroup = NULL; word32 primeGroupSz; byte* generator = NULL; word32 generatorSz; word32 begin; int ret = WS_UNIMPLEMENTED_E; if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { begin = *idx; ret = GetMpint(&primeGroupSz, &primeGroup, buf, len, &begin); } if (ret == WS_SUCCESS) ret = GetMpint(&generatorSz, &generator, buf, len, &begin); if (ret == WS_SUCCESS) { ssh->handshake->primeGroup = (byte*)WMALLOC(primeGroupSz + UINT32_SZ, ssh->ctx->heap, DYNTYPE_MPINT); if (ssh->handshake->primeGroup == NULL) ret = WS_MEMORY_E; } if (ret == WS_SUCCESS) { ssh->handshake->generator = (byte*)WMALLOC(generatorSz + UINT32_SZ, ssh->ctx->heap, DYNTYPE_MPINT); if (ssh->handshake->generator == NULL) { ret = WS_MEMORY_E; WFREE(ssh->handshake->primeGroup, ssh->ctx->heap, DYNTYPE_MPINT); ssh->handshake->primeGroup = NULL; } } if (WS_SUCCESS) { c32toa(primeGroupSz, ssh->handshake->primeGroup); WMEMCPY(ssh->handshake->primeGroup + UINT32_SZ, primeGroup, primeGroupSz); ssh->handshake->primeGroupSz = primeGroupSz; c32toa(generatorSz, ssh->handshake->generator); WMEMCPY(ssh->handshake->generator + UINT32_SZ, generator, generatorSz); ssh->handshake->generatorSz = generatorSz; *idx = begin; ret = SendKexDhInit(ssh); } return ret; } static int DoIgnore(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { word32 dataSz; word32 begin = *idx; (void)ssh; (void)len; ato32(buf + begin, &dataSz); begin += LENGTH_SZ + dataSz; *idx = begin; return WS_SUCCESS; } static int DoDebug(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { byte alwaysDisplay; char* msg = NULL; char* lang = NULL; word32 strSz; word32 begin = *idx; (void)ssh; (void)len; alwaysDisplay = buf[begin++]; ato32(buf + begin, &strSz); begin += LENGTH_SZ; if (strSz > 0) { msg = (char*)WMALLOC(strSz + 1, ssh->ctx->heap, DYNTYPE_STRING); if (msg != NULL) { WMEMCPY(msg, buf + begin, strSz); msg[strSz] = 0; } else { return WS_MEMORY_E; } begin += strSz; } ato32(buf + begin, &strSz); begin += LENGTH_SZ; if (strSz > 0) { lang = (char*)WMALLOC(strSz + 1, ssh->ctx->heap, DYNTYPE_STRING); if (lang != NULL) { WMEMCPY(lang, buf + begin, strSz); lang[strSz] = 0; } else { WFREE(msg, ssh->ctx->heap, DYNTYPE_STRING); return WS_MEMORY_E; } begin += strSz; } if (alwaysDisplay) { WLOG(WS_LOG_DEBUG, "DEBUG MSG (%s): %s", (lang == NULL) ? "none" : lang, (msg == NULL) ? "no message" : msg); } *idx = begin; WFREE(msg, ssh->ctx->heap, DYNTYPE_STRING); WFREE(lang, ssh->ctx->heap, DYNTYPE_STRING); return WS_SUCCESS; } static int DoUnimplemented(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { word32 seq; word32 begin = *idx; (void)ssh; (void)len; ato32(buf + begin, &seq); begin += UINT32_SZ; WLOG(WS_LOG_DEBUG, "UNIMPLEMENTED: seq %u", seq); *idx = begin; return WS_SUCCESS; } static int DoDisconnect(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { word32 reason; const char* reasonStr; word32 begin = *idx; (void)ssh; (void)len; (void)reasonStr; ato32(buf + begin, &reason); begin += UINT32_SZ; #ifdef NO_WOLFSSH_STRINGS WLOG(WS_LOG_DEBUG, "DISCONNECT: (%u)", reason); #elif defined(DEBUG_WOLFSSH) switch (reason) { case WOLFSSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT: reasonStr = "host not allowed to connect"; break; case WOLFSSH_DISCONNECT_PROTOCOL_ERROR: reasonStr = "protocol error"; break; case WOLFSSH_DISCONNECT_KEY_EXCHANGE_FAILED: reasonStr = "key exchange failed"; break; case WOLFSSH_DISCONNECT_RESERVED: reasonStr = "reserved"; break; case WOLFSSH_DISCONNECT_MAC_ERROR: reasonStr = "mac error"; break; case WOLFSSH_DISCONNECT_COMPRESSION_ERROR: reasonStr = "compression error"; break; case WOLFSSH_DISCONNECT_SERVICE_NOT_AVAILABLE: reasonStr = "service not available"; break; case WOLFSSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED: reasonStr = "protocol version not supported"; break; case WOLFSSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE: reasonStr = "host key not verifiable"; break; case WOLFSSH_DISCONNECT_CONNECTION_LOST: reasonStr = "connection lost"; break; case WOLFSSH_DISCONNECT_BY_APPLICATION: reasonStr = "disconnect by application"; break; case WOLFSSH_DISCONNECT_TOO_MANY_CONNECTIONS: reasonStr = "too many connections"; break; case WOLFSSH_DISCONNECT_AUTH_CANCELLED_BY_USER: reasonStr = "auth cancelled by user"; break; case WOLFSSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE: reasonStr = "no more auth methods available"; break; case WOLFSSH_DISCONNECT_ILLEGAL_USER_NAME: reasonStr = "illegal user name"; break; default: reasonStr = "unknown reason"; } WLOG(WS_LOG_DEBUG, "DISCONNECT: (%u) %s", reason, reasonStr); #endif *idx = begin; return WS_SUCCESS; } static int DoServiceRequest(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { word32 begin = *idx; word32 nameSz; char serviceName[32]; (void)len; ato32(buf + begin, &nameSz); begin += LENGTH_SZ; WMEMCPY(serviceName, buf + begin, nameSz); begin += nameSz; serviceName[nameSz] = 0; *idx = begin; WLOG(WS_LOG_DEBUG, "Requesting service: %s", serviceName); ssh->clientState = CLIENT_USERAUTH_REQUEST_DONE; return WS_SUCCESS; } static int DoServiceAccept(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { word32 begin = *idx; word32 nameSz; char serviceName[32]; (void)len; ato32(buf + begin, &nameSz); begin += LENGTH_SZ; WMEMCPY(serviceName, buf + begin, nameSz); begin += nameSz; serviceName[nameSz] = 0; *idx = begin; WLOG(WS_LOG_DEBUG, "Accepted service: %s", serviceName); ssh->serverState = SERVER_USERAUTH_REQUEST_DONE; return WS_SUCCESS; } /* Utility for DoUserAuthRequest() */ static int DoUserAuthRequestPassword(WOLFSSH* ssh, WS_UserAuthData* authData, byte* buf, word32 len, word32* idx) { word32 begin; WS_UserAuthData_Password* pw; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestPassword()"); if (ssh == NULL || authData == NULL || buf == NULL || len == 0 || idx == NULL) { ret = WS_BAD_ARGUMENT; } if (ret == WS_SUCCESS) { begin = *idx; pw = &authData->sf.password; authData->type = WOLFSSH_USERAUTH_PASSWORD; ret = GetBoolean(&pw->hasNewPassword, buf, len, &begin); } if (ret == WS_SUCCESS) ret = GetUint32(&pw->passwordSz, buf, len, &begin); if (ret == WS_SUCCESS) { pw->password = buf + begin; begin += pw->passwordSz; if (pw->hasNewPassword) { /* Skip the password change. Maybe error out since we aren't * supporting password changes at this time. */ ret = GetUint32(&pw->newPasswordSz, buf, len, &begin); if (ret == WS_SUCCESS) { pw->newPassword = buf + begin; begin += pw->newPasswordSz; } } else { pw->newPassword = NULL; pw->newPasswordSz = 0; } if (ssh->ctx->userAuthCb != NULL) { WLOG(WS_LOG_DEBUG, "DUARPW: Calling the userauth callback"); ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PASSWORD, authData, ssh->userAuthCtx); if (ret == WOLFSSH_USERAUTH_SUCCESS) { WLOG(WS_LOG_DEBUG, "DUARPW: password check successful"); ssh->clientState = CLIENT_USERAUTH_DONE; ret = WS_SUCCESS; } else { WLOG(WS_LOG_DEBUG, "DUARPW: password check failed"); ret = SendUserAuthFailure(ssh, 0); } } else { WLOG(WS_LOG_DEBUG, "DUARPW: No user auth callback"); ret = SendUserAuthFailure(ssh, 0); } } if (ret == WS_SUCCESS) *idx = begin; WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestPassword(), ret = %d", ret); return ret; } int ConstantCompare(byte encDigest[], byte checkDigest[], word32 encDigestSz){ return 0; } /* Utility for DoUserAuthRequestPublicKey() */ /* returns negative for error, positive is size of digest. */ static int DoUserAuthRequestRsa(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, byte hashId, byte* digest, word32 digestSz) { RsaKey key; byte checkDigest[MAX_ENCODED_SIG_SZ]; int checkDigestSz; byte* publicKeyType; word32 publicKeyTypeSz = 0; byte* n; word32 nSz = 0; byte* e; word32 eSz = 0; word32 i = 0; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestRsa()"); if (ssh == NULL || pk == NULL || digest == NULL || digestSz == 0) ret = WS_BAD_ARGUMENT; /* First check that the public key's type matches the one we are * expecting. */ if (ret == WS_SUCCESS) ret = GetUint32(&publicKeyTypeSz, pk->publicKey, pk->publicKeySz, &i); if (ret == WS_SUCCESS) { publicKeyType = pk->publicKey + i; i += publicKeyTypeSz; if (publicKeyTypeSz != pk->publicKeyTypeSz && WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) { WLOG(WS_LOG_DEBUG, "Public Key's type does not match public key type"); ret = WS_INVALID_ALGO_ID; } } if (ret == WS_SUCCESS) ret = GetUint32(&eSz, pk->publicKey, pk->publicKeySz, &i); if (ret == WS_SUCCESS) { e = pk->publicKey + i; i += eSz; ret = GetUint32(&nSz, pk->publicKey, pk->publicKeySz, &i); } if (ret == WS_SUCCESS) { n = pk->publicKey + i; ret = wc_InitRsaKey(&key, ssh->ctx->heap); if (ret == 0) ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, &key); if (ret != 0) { WLOG(WS_LOG_DEBUG, "Could not decode public key"); ret = WS_CRYPTO_FAILED; } } if (ret == WS_SUCCESS) { i = 0; /* First check that the signature's public key type matches the one * we are expecting. */ ret = GetUint32(&publicKeyTypeSz, pk->publicKey, pk->publicKeySz, &i); } if (ret == WS_SUCCESS) { publicKeyType = pk->publicKey + i; i += publicKeyTypeSz; if (publicKeyTypeSz != pk->publicKeyTypeSz && WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) { WLOG(WS_LOG_DEBUG, "Signature's type does not match public key type"); ret = WS_INVALID_ALGO_ID; } } if (ret == WS_SUCCESS) ret = GetUint32(&nSz, pk->signature, pk->signatureSz, &i); if (ret == WS_SUCCESS) { n = pk->signature + i; checkDigestSz = wc_RsaSSL_Verify(n, nSz, checkDigest, sizeof(checkDigest), &key); if (checkDigestSz <= 0) { WLOG(WS_LOG_DEBUG, "Could not verify signature"); ret = WS_CRYPTO_FAILED; } } if (ret == WS_SUCCESS) { byte encDigest[MAX_ENCODED_SIG_SZ]; word32 encDigestSz; volatile int compare; volatile int sizeCompare; encDigestSz = wc_EncodeSignature(encDigest, digest, wc_HashGetDigestSize(hashId), wc_HashGetOID(hashId)); compare = ConstantCompare(encDigest, checkDigest, encDigestSz); sizeCompare = encDigestSz != (word32)checkDigestSz; if ((compare | sizeCompare) == 0) ret = WS_SUCCESS; else ret = WS_RSA_E; } wc_FreeRsaKey(&key); WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestRsa(), ret = %d", ret); return ret; } /* Utility for DoUserAuthRequestPublicKey() */ /* returns negative for error, positive is size of digest. */ static int DoUserAuthRequestEcc(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, byte hashId, byte* digest, word32 digestSz) { ecc_key key; byte* publicKeyType; word32 publicKeyTypeSz = 0; byte* curveName; word32 curveNameSz = 0; mp_int r, s; byte* q; word32 sz, qSz; word32 i = 0; int ret = WS_SUCCESS; (void)hashId; WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestRsa()"); if (ssh == NULL || pk == NULL || digest == NULL || digestSz == 0) ret = WS_BAD_ARGUMENT; /* First check that the public key's type matches the one we are * expecting. */ if (ret == WS_SUCCESS) ret = GetUint32(&publicKeyTypeSz, pk->publicKey, pk->publicKeySz, &i); if (ret == WS_SUCCESS) { publicKeyType = pk->publicKey + i; i += publicKeyTypeSz; if (publicKeyTypeSz != pk->publicKeyTypeSz && WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) { WLOG(WS_LOG_DEBUG, "Public Key's type does not match public key type"); ret = WS_INVALID_ALGO_ID; } } if (ret == WS_SUCCESS) ret = GetUint32(&curveNameSz, pk->publicKey, pk->publicKeySz, &i); if (ret == WS_SUCCESS) { curveName = pk->publicKey + i; (void)curveName; /* Not used at the moment, hush the compiler. */ i += curveNameSz; ret = GetUint32(&qSz, pk->publicKey, pk->publicKeySz, &i); } if (ret == WS_SUCCESS) { q = pk->publicKey + i; i += qSz; ret = wc_ecc_init_ex(&key, ssh->ctx->heap, INVALID_DEVID); } if (ret == 0) ret = wc_ecc_import_x963(q, qSz, &key); if (ret != 0) { WLOG(WS_LOG_DEBUG, "Could not decode public key"); ret = WS_CRYPTO_FAILED; } if (ret == WS_SUCCESS) { i = 0; /* First check that the signature's public key type matches the one * we are expecting. */ ret = GetUint32(&publicKeyTypeSz, pk->signature, pk->signatureSz, &i); } if (ret == WS_SUCCESS) { publicKeyType = pk->signature + i; i += publicKeyTypeSz; if (publicKeyTypeSz != pk->publicKeyTypeSz && WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) { WLOG(WS_LOG_DEBUG, "Signature's type does not match public key type"); ret = WS_INVALID_ALGO_ID; } } if (ret == WS_SUCCESS) { /* Get the size of the signature blob. */ ret = GetUint32(&sz, pk->signature, pk->signatureSz, &i); } if (ret == WS_SUCCESS) { /* Get R and S. */ ret = GetUint32(&sz, pk->signature, pk->signatureSz, &i); } if (ret == WS_SUCCESS) { ret = mp_read_unsigned_bin(&r, pk->signature + i, sz); if (ret != 0) ret = WS_PARSE_E; else ret = WS_SUCCESS; } if (ret == WS_SUCCESS) { i += sz; ret = GetUint32(&sz, pk->signature, pk->signatureSz, &i); } if (ret == WS_SUCCESS) { ret = mp_read_unsigned_bin(&s, pk->signature + i, sz); if (ret != 0) ret = WS_PARSE_E; else ret = WS_SUCCESS; } if (ret == WS_SUCCESS) { int status = 0; ret = wc_ecc_verify_hash_ex(&r, &s, digest, digestSz, &status, &key); if (ret != 0) { WLOG(WS_LOG_DEBUG, "Could not verify signature"); ret = WS_CRYPTO_FAILED; } else ret = status ? WS_SUCCESS : WS_ECC_E; } mp_clear(&r); mp_clear(&s); wc_ecc_free(&key); WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestEcc(), ret = %d", ret); return ret; } /* Utility for DoUserAuthRequest() */ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, byte* buf, word32 len, word32* idx) { word32 begin; WS_UserAuthData_PublicKey* pk; int ret = WS_SUCCESS; int authFailure = 0; WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestPublicKey()"); if (ssh == NULL || authData == NULL || buf == NULL || len == 0 || idx == NULL) { ret = WS_BAD_ARGUMENT; } if (ret == WS_SUCCESS) { begin = *idx; pk = &authData->sf.publicKey; authData->type = WOLFSSH_USERAUTH_PUBLICKEY; ret = GetBoolean(&pk->hasSignature, buf, len, &begin); } if (ret == WS_SUCCESS) ret = GetUint32(&pk->publicKeyTypeSz, buf, len, &begin); if (ret == WS_SUCCESS) { pk->publicKeyType = buf + begin; begin += pk->publicKeyTypeSz; ret = GetUint32(&pk->publicKeySz, buf, len, &begin); } if (ret == WS_SUCCESS) { pk->publicKey = buf + begin; begin += pk->publicKeySz; if (pk->hasSignature) { ret = GetUint32(&pk->signatureSz, buf, len, &begin); if (ret == WS_SUCCESS) { pk->signature = buf + begin; begin += pk->signatureSz; } } else { pk->signature = NULL; pk->signatureSz = 0; } if (ret == WS_SUCCESS) { *idx = begin; if (ssh->ctx->userAuthCb != NULL) { WLOG(WS_LOG_DEBUG, "DUARPK: Calling the userauth callback"); ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PUBLICKEY, authData, ssh->userAuthCtx); WLOG(WS_LOG_DEBUG, "DUARPK: callback result = %d", ret); if (ret == WOLFSSH_USERAUTH_SUCCESS) ret = WS_SUCCESS; else { ret = SendUserAuthFailure(ssh, 0); authFailure = 1; } } else { WLOG(WS_LOG_DEBUG, "DUARPK: no userauth callback set"); ret = SendUserAuthFailure(ssh, 0); authFailure = 1; } } } if (ret == WS_SUCCESS && !authFailure) { if (pk->signature == NULL) { WLOG(WS_LOG_DEBUG, "DUARPK: Send the PK OK"); ret = SendUserAuthPkOk(ssh, pk->publicKeyType, pk->publicKeyTypeSz, pk->publicKey, pk->publicKeySz); } else { wc_HashAlg hash; byte digest[WC_MAX_DIGEST_SIZE]; word32 digestSz; byte hashId = WC_HASH_TYPE_SHA; byte pkTypeId; pkTypeId = NameToId((char*)pk->publicKeyType, pk->publicKeyTypeSz); if (pkTypeId == ID_UNKNOWN) ret = WS_INVALID_ALGO_ID; if (ret == WS_SUCCESS) { hashId = HashForId(pkTypeId); WMEMSET(digest, 0, sizeof(digest)); digestSz = wc_HashGetDigestSize(hashId); ret = wc_HashInit(&hash, hashId); } if (ret == 0) { c32toa(ssh->sessionIdSz, digest); ret = wc_HashUpdate(&hash, hashId, digest, UINT32_SZ); } if (ret == 0) ret = wc_HashUpdate(&hash, hashId, ssh->sessionId, ssh->sessionIdSz); if (ret == 0) { digest[0] = MSGID_USERAUTH_REQUEST; ret = wc_HashUpdate(&hash, hashId, digest, MSG_ID_SZ); } /* The rest of the fields in the signature are already * in the buffer. Just need to account for the sizes. */ if (ret == 0) ret = wc_HashUpdate(&hash, hashId, pk->dataToSign, authData->usernameSz + authData->serviceNameSz + authData->authNameSz + BOOLEAN_SZ + pk->publicKeyTypeSz + pk->publicKeySz + (UINT32_SZ * 5)); if (ret == 0) { ret = wc_HashFinal(&hash, hashId, digest); if (ret != 0) ret = WS_CRYPTO_FAILED; else ret = WS_SUCCESS; } if (ret == WS_SUCCESS) { if (pkTypeId == ID_SSH_RSA) ret = DoUserAuthRequestRsa(ssh, pk, hashId, digest, digestSz); else if (pkTypeId == ID_ECDSA_SHA2_NISTP256 || pkTypeId == ID_ECDSA_SHA2_NISTP384 || pkTypeId == ID_ECDSA_SHA2_NISTP521) ret = DoUserAuthRequestEcc(ssh, pk, hashId, digest, digestSz); } if (ret != WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DUARPK: signature compare failure"); ret = SendUserAuthFailure(ssh, 0); } else { ssh->clientState = CLIENT_USERAUTH_DONE; } } } WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestPublicKey(), ret = %d", ret); return ret; } static int DoUserAuthRequest(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { word32 begin; int ret = WS_SUCCESS; byte authNameId; WS_UserAuthData authData; WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequest()"); if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { begin = *idx; WMEMSET(&authData, 0, sizeof(authData)); ret = GetUint32(&authData.usernameSz, buf, len, &begin); } if (ret == WS_SUCCESS) { authData.username = buf + begin; begin += authData.usernameSz; ret = GetUint32(&authData.serviceNameSz, buf, len, &begin); } if (ret == WS_SUCCESS) { authData.serviceName = buf + begin; begin += authData.serviceNameSz; ret = GetUint32(&authData.authNameSz, buf, len, &begin); } if (ret == WS_SUCCESS) { authData.authName = buf + begin; begin += authData.authNameSz; authNameId = NameToId((char*)authData.authName, authData.authNameSz); if (authNameId == ID_USERAUTH_PASSWORD) ret = DoUserAuthRequestPassword(ssh, &authData, buf, len, &begin); else if (authNameId == ID_USERAUTH_PUBLICKEY) { authData.sf.publicKey.dataToSign = buf + *idx; ret = DoUserAuthRequestPublicKey(ssh, &authData, buf, len, &begin); } #ifdef WOLFSSH_ALLOW_USERAUTH_NONE else if (authNameId == ID_NONE) { ssh->clientState = CLIENT_USERAUTH_DONE; } #endif else { WLOG(WS_LOG_DEBUG, "invalid userauth type: %s", IdToName(authNameId)); ret = SendUserAuthFailure(ssh, 0); } *idx = begin; } WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequest(), ret = %d", ret); return ret; } static int DoUserAuthFailure(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { byte authList[3]; /* Should only ever be password, publickey, hostname */ word32 authListSz; byte partialSuccess; byte authId = ID_USERAUTH_PASSWORD; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering DoUserAuthFailure()"); if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) ret = GetNameList(authList, &authListSz, buf, len, idx); if (ret == WS_SUCCESS) ret = GetBoolean(&partialSuccess, buf, len, idx); if (ret == WS_SUCCESS) ret = SendUserAuthRequest(ssh, authId); WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthFailure(), ret = %d", ret); return ret; } static int DoUserAuthSuccess(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering DoUserAuthSuccess()"); /* This message does not have any payload. len should be 0. */ if (ssh == NULL || buf == NULL || len != 0 || idx == NULL) ret = WS_BAD_ARGUMENT; ssh->serverState = SERVER_USERAUTH_ACCEPT_DONE; WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthSuccess(), ret = %d", ret); return ret; } static int DoUserAuthBanner(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { word32 begin; char banner[80]; word32 bannerSz = sizeof(banner); int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering DoUserAuthBanner()"); if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { begin = *idx; ret = GetString(banner, &bannerSz, buf, len, idx); } if (ret == WS_SUCCESS) ret = GetUint32(&bannerSz, buf, len, idx); if (ret == WS_SUCCESS) { begin += bannerSz; if (ssh->ctx->showBanner) { WLOG(WS_LOG_INFO, "%s", banner); } } WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthBanner(), ret = %d", ret); return ret; } static int DoGlobalRequest(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { word32 begin; int ret = WS_SUCCESS; char name[80]; word32 nameSz = sizeof(name); byte wantReply = 0; WLOG(WS_LOG_DEBUG, "Entering DoGlobalRequest()"); if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { begin = *idx; ret = GetString(name, &nameSz, buf, len, &begin); } if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "DGR: request name = %s", name); ret = GetBoolean(&wantReply, buf, len, &begin); } if (ret == WS_SUCCESS) { *idx += len; if (wantReply) ret = SendRequestSuccess(ssh, 0); } WLOG(WS_LOG_DEBUG, "Leaving DoGlobalRequest(), ret = %d", ret); return ret; } static int DoChannelOpen(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { word32 begin = *idx; word32 typeSz; char type[32]; byte typeId = ID_UNKNOWN; word32 peerChannelId; word32 peerInitialWindowSz; word32 peerMaxPacketSz; int ret; WOLFSSH_CHANNEL* newChannel; WLOG(WS_LOG_DEBUG, "Entering DoChannelOpen()"); //ESP_LOGI("WOLFSSH", "Entering DoChannelOpen()"); typeSz = sizeof(type); ret = GetString(type, &typeSz, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&peerChannelId, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&peerInitialWindowSz, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&peerMaxPacketSz, buf, len, &begin); if (ret == WS_SUCCESS) { *idx = begin; WLOG(WS_LOG_INFO, " type = %s", type); WLOG(WS_LOG_INFO, " peerChannelId = %u", peerChannelId); WLOG(WS_LOG_INFO, " peerInitialWindowSz = %u", peerInitialWindowSz); WLOG(WS_LOG_INFO, " peerMaxPacketSz = %u", peerMaxPacketSz); //ESP_LOGI("WOLFSSH", "DoChannelOpen type = %s", type); //ESP_LOGI("WOLFSSH", "DoChannelOpen peerChId = %u", peerChannelId); //ESP_LOGI("WOLFSSH", "DoChannelOpen peerInitWndSz = %u", peerInitialWindowSz); //ESP_LOGI("WOLFSSH", "DoChannelOpen peerMaxPktSz = %u", peerMaxPacketSz); typeId = NameToId(type, typeSz); if (typeId != ID_CHANTYPE_SESSION) ret = WS_INVALID_CHANTYPE; //ESP_LOGI("WOLFSSH", "DoChannelOpen ret = %d", ret); } if (ret == WS_SUCCESS) { newChannel = ChannelNew(ssh, typeId, DEFAULT_WINDOW_SZ, DEFAULT_MAX_PACKET_SZ); //ESP_LOGI("WOLFSSH", "DoChannelOpen newCh = %p", newChannel); if (newChannel == NULL) ret = WS_RESOURCE_E; else { ChannelUpdate(newChannel, peerChannelId, peerInitialWindowSz, peerMaxPacketSz); //ESP_LOGI("WOLFSSH", "DoChannelOpen ChUpdate"); if (ssh->channelListSz == 0) ssh->defaultPeerChannelId = peerChannelId; ChannelAppend(ssh, newChannel); ssh->clientState = CLIENT_DONE; } } WLOG(WS_LOG_DEBUG, "Leaving DoChannelOpen(), ret = %d", ret); //ESP_LOGI("WOLFSSH", "Leaving DoChannelOpen(), ret = %d", ret); return ret; } static int DoChannelOpenConf(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { WOLFSSH_CHANNEL* channel; word32 begin, channelId, peerChannelId, peerInitialWindowSz, peerMaxPacketSz; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering DoChannelOpenConf()"); if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { begin = *idx; ret = GetUint32(&channelId, buf, len, &begin); } if (ret == WS_SUCCESS) ret = GetUint32(&peerChannelId, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&peerInitialWindowSz, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&peerMaxPacketSz, buf, len, &begin); if (ret == WS_SUCCESS) { WLOG(WS_LOG_INFO, " channelId = %u", channelId); WLOG(WS_LOG_INFO, " peerChannelId = %u", peerChannelId); WLOG(WS_LOG_INFO, " peerInitialWindowSz = %u", peerInitialWindowSz); WLOG(WS_LOG_INFO, " peerMaxPacketSz = %u", peerMaxPacketSz); channel = ChannelFind(ssh, channelId, FIND_SELF); if (channel == NULL) ret = WS_INVALID_CHANID; } if (ret == WS_SUCCESS) ret = ChannelUpdate(channel, peerChannelId, peerInitialWindowSz, peerMaxPacketSz); if (ret == WS_SUCCESS) ssh->serverState = SERVER_CHANNEL_OPEN_DONE; WLOG(WS_LOG_DEBUG, "Leaving DoChannelOpenConf(), ret = %d", ret); return ret; } static int DoChannelOpenFail(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { char desc[80]; word32 begin, channelId, reasonId, descSz, langSz; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering DoChannelOpenFail()"); if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { begin = *idx; ret = GetUint32(&channelId, buf, len, &begin); } if (ret == WS_SUCCESS) ret = GetUint32(&reasonId, buf, len, &begin); if (ret == WS_SUCCESS) { descSz = sizeof(desc); ret = GetString(desc, &descSz, buf, len, &begin); } if (ret == WS_SUCCESS) ret = GetUint32(&langSz, buf, len, &begin); if (ret == WS_SUCCESS) { *idx = begin + langSz; WLOG(WS_LOG_INFO, "channel open failure reason code: %u", reasonId); if (descSz > 0) { WLOG(WS_LOG_INFO, "description: %s", desc); } ret = ChannelRemove(ssh, channelId, FIND_SELF); } if (ret == WS_SUCCESS) ret = WS_CHANOPEN_FAILED; WLOG(WS_LOG_DEBUG, "Leaving DoChannelOpenFail(), ret = %d", ret); return ret; } static int DoChannelClose(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { WOLFSSH_CHANNEL* channel = NULL; word32 begin = *idx; word32 channelId; int ret; WLOG(WS_LOG_DEBUG, "Entering DoChannelClose()"); ret = GetUint32(&channelId, buf, len, &begin); if (ret == WS_SUCCESS) { *idx = begin; channel = ChannelFind(ssh, channelId, FIND_SELF); if (channel == NULL) ret = WS_INVALID_CHANID; } if (ret == WS_SUCCESS) ret = SendChannelClose(ssh, channelId); if (ret == WS_SUCCESS) ret = ChannelRemove(ssh, channelId, FIND_SELF); if (ret == WS_SUCCESS) ret = WS_CHANNEL_CLOSED; WLOG(WS_LOG_DEBUG, "Leaving DoChannelClose(), ret = %d", ret); return ret; } static int DoChannelRequest(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { word32 begin = *idx; word32 channelId; word32 typeSz; char type[32]; byte wantReply; int ret; WLOG(WS_LOG_DEBUG, "Entering DoChannelRequest()"); ret = GetUint32(&channelId, buf, len, &begin); typeSz = sizeof(type); if (ret == WS_SUCCESS) ret = GetString(type, &typeSz, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetBoolean(&wantReply, buf, len, &begin); if (ret != WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "Leaving DoChannelRequest(), ret = %d", ret); return ret; } if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, " channelId = %u", channelId); WLOG(WS_LOG_DEBUG, " type = %s", type); WLOG(WS_LOG_DEBUG, " wantReply = %u", wantReply); if (WSTRNCMP(type, "pty-req", typeSz) == 0) { char term[32]; word32 termSz; word32 widthChar, heightRows, widthPixels, heightPixels; word32 modesSz; termSz = sizeof(term); ret = GetString(term, &termSz, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&widthChar, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&heightRows, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&widthPixels, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&heightPixels, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&modesSz, buf, len, &begin); if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, " term = %s", term); WLOG(WS_LOG_DEBUG, " widthChar = %u", widthChar); WLOG(WS_LOG_DEBUG, " heightRows = %u", heightRows); WLOG(WS_LOG_DEBUG, " widthPixels = %u", widthPixels); WLOG(WS_LOG_DEBUG, " heightPixels = %u", heightPixels); WLOG(WS_LOG_DEBUG, " modes = %u", (modesSz - 1) / 5); } } else if (WSTRNCMP(type, "env", typeSz) == 0) { char name[32]; word32 nameSz; char value[32]; word32 valueSz; nameSz = sizeof(name); valueSz = sizeof(value); ret = GetString(name, &nameSz, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetString(value, &valueSz, buf, len, &begin); WLOG(WS_LOG_DEBUG, " %s = %s", name, value); } } if (ret == WS_SUCCESS) *idx = len; if (wantReply) { int replyRet; replyRet = SendChannelSuccess(ssh, channelId, (ret == WS_SUCCESS)); if (replyRet != WS_SUCCESS) ret = replyRet; } WLOG(WS_LOG_DEBUG, "Leaving DoChannelRequest(), ret = %d", ret); return ret; } static int DoChannelSuccess(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering DoChannelSuccess()"); if (ssh == NULL || buf == NULL || len == 0 || idx == NULL) ret = WS_BAD_ARGUMENT; ssh->serverState = SERVER_DONE; WLOG(WS_LOG_DEBUG, "Leaving DoChannelSuccess(), ret = %d", ret); return ret; } static int DoChannelFailure(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering DoChannelFailure()"); if (ssh == NULL || buf == NULL || len != 0 || idx == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) ret = WS_CHANOPEN_FAILED; WLOG(WS_LOG_DEBUG, "Leaving DoChannelFailure(), ret = %d", ret); return ret; } static int DoChannelWindowAdjust(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { WOLFSSH_CHANNEL* channel = NULL; word32 begin = *idx; word32 channelId, bytesToAdd; int ret; WLOG(WS_LOG_DEBUG, "Entering DoChannelWindowAdjust()"); ret = GetUint32(&channelId, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&bytesToAdd, buf, len, &begin); if (ret == WS_SUCCESS) { *idx = begin; channel = ChannelFind(ssh, channelId, FIND_SELF); if (channel == NULL) ret = WS_INVALID_CHANID; else { WLOG(WS_LOG_INFO, " channelId = %u", channelId); WLOG(WS_LOG_INFO, " bytesToAdd = %u", bytesToAdd); WLOG(WS_LOG_INFO, " peerWindowSz = %u", channel->peerWindowSz); channel->peerWindowSz += bytesToAdd; WLOG(WS_LOG_INFO, " update peerWindowSz = %u", channel->peerWindowSz); } } WLOG(WS_LOG_DEBUG, "Leaving DoChannelWindowAdjust(), ret = %d", ret); return ret; } static int DoChannelData(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { WOLFSSH_CHANNEL* channel = NULL; word32 begin = *idx; word32 dataSz = 0; word32 channelId; int ret; WLOG(WS_LOG_DEBUG, "Entering DoChannelData()"); ret = GetUint32(&channelId, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&dataSz, buf, len, &begin); if (ret == WS_SUCCESS) { *idx = begin + dataSz; channel = ChannelFind(ssh, channelId, FIND_SELF); if (channel == NULL) ret = WS_INVALID_CHANID; else ret = ChannelPutData(channel, buf + begin, dataSz); } WLOG(WS_LOG_DEBUG, "Leaving DoChannelData(), ret = %d", ret); return ret; } static int DoPacket(WOLFSSH* ssh) { byte* buf = (byte*)ssh->inputBuffer.buffer; word32 idx = ssh->inputBuffer.idx; word32 len = ssh->inputBuffer.length; word32 payloadSz; byte padSz; byte msg; word32 payloadIdx = 0; int ret; //ESP_LOGI("WOLFSSH", "DoPacket sequence number: %d", ssh->peerSeq); WLOG(WS_LOG_DEBUG, "DoPacket sequence number: %d", ssh->peerSeq); idx += LENGTH_SZ; padSz = buf[idx++]; payloadSz = ssh->curSz - PAD_LENGTH_SZ - padSz - MSG_ID_SZ; msg = buf[idx++]; /* At this point, payload starts at "buf + idx". */ switch (msg) { case MSGID_DISCONNECT: //ESP_LOGI("WOLFSSH", "Decoding MSGID_DISCONNECT"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_DISCONNECT"); ret = DoDisconnect(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_IGNORE: //ESP_LOGI("WOLFSSH", "Decoding MSGID_IGNORE"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_IGNORE"); ret = DoIgnore(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_UNIMPLEMENTED: //ESP_LOGI("WOLFSSH", "Decoding MSGID_UNIMPLEMENTED"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_UNIMPLEMENTED"); ret = DoUnimplemented(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_DEBUG: //ESP_LOGI("WOLFSSH", "Decoding MSGID_DEBUG"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_DEBUG"); ret = DoDebug(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_KEXINIT: //ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXINIT"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXINIT"); ret = DoKexInit(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_NEWKEYS: //ESP_LOGI("WOLFSSH", "Decoding MSGID_NEWKEYS"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_NEWKEYS"); ret = DoNewKeys(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_KEXDH_INIT: //ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXDH_INIT"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_INIT"); ret = DoKexDhInit(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_KEXDH_REPLY: if (ssh->handshake->kexId == ID_DH_GEX_SHA256) { //ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXDH_GEX_GROUP"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_GEX_GROUP"); ret = DoKexDhGexGroup(ssh, buf + idx, payloadSz, &payloadIdx); } else { //ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXDH_REPLY"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_REPLY"); ret = DoKexDhReply(ssh, buf + idx, payloadSz, &payloadIdx); } break; case MSGID_KEXDH_GEX_REQUEST: //ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXDH_GEX_REQUEST"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_GEX_REQUEST"); ret = DoKexDhGexRequest(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_KEXDH_GEX_INIT: //ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXDH_GEX_INIT"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_GEX_INIT"); ret = DoKexDhInit(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_KEXDH_GEX_REPLY: //ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXDH_GEX_INIT"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_GEX_INIT"); ret = DoKexDhReply(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_SERVICE_REQUEST: //ESP_LOGI("WOLFSSH", "Decoding MSGID_SERVICE_REQUEST"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_SERVICE_REQUEST"); ret = DoServiceRequest(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_SERVICE_ACCEPT: //ESP_LOGI("WOLFSSH", "Decoding MSGID_SERVER_ACCEPT"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_SERVER_ACCEPT"); ret = DoServiceAccept(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_USERAUTH_REQUEST: //ESP_LOGI("WOLFSSH", "Decoding MSGID_USERAUTH_REQUEST"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_REQUEST"); ret = DoUserAuthRequest(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_USERAUTH_FAILURE: //ESP_LOGI("WOLFSSH", "Decoding MSGID_USERAUTH_FAILURE"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_FAILURE"); ret = DoUserAuthFailure(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_USERAUTH_SUCCESS: //ESP_LOGI("WOLFSSH", "Decoding MSGID_USERAUTH_SUCCESS"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_SUCCESS"); ret = DoUserAuthSuccess(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_USERAUTH_BANNER: //ESP_LOGI("WOLFSSH", "Decoding MSGID_USERAUTH_BANNER"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_BANNER"); ret = DoUserAuthBanner(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_GLOBAL_REQUEST: //ESP_LOGI("WOLFSSH", "Decoding MSGID_GLOBAL_REQUEST"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_GLOBAL_REQUEST"); ret = DoGlobalRequest(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_CHANNEL_OPEN: //ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_OPEN"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_OPEN"); ret = DoChannelOpen(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_CHANNEL_OPEN_CONF: //ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_OPEN_CONF"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_OPEN_CONF"); ret = DoChannelOpenConf(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_CHANNEL_OPEN_FAIL: //ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_OPEN_FAIL"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_OPEN_FAIL"); ret = DoChannelOpenFail(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_CHANNEL_WINDOW_ADJUST: //ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_WINDOW_ADJUST"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_WINDOW_ADJUST"); ret = DoChannelWindowAdjust(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_CHANNEL_DATA: //ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_DATA"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_DATA"); ret = DoChannelData(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_CHANNEL_EOF: //ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_EOF"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_EOF"); ret = WS_SUCCESS; break; case MSGID_CHANNEL_CLOSE: //ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_CLOSE"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_CLOSE"); ret = DoChannelClose(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_CHANNEL_REQUEST: //ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_REQUEST"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_REQUEST"); ret = DoChannelRequest(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_CHANNEL_SUCCESS: //ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_SUCCESS"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_SUCCESS"); ret = DoChannelSuccess(ssh, buf + idx, payloadSz, &payloadIdx); break; case MSGID_CHANNEL_FAILURE: //ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_FAILURE"); WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_FAILURE"); ret = DoChannelFailure(ssh, buf + idx, payloadSz, &payloadIdx); break; default: //ESP_LOGI("WOLFSSH", "Unimplemented message ID (%d)", msg); WLOG(WS_LOG_DEBUG, "Unimplemented message ID (%d)", msg); #ifdef SHOW_UNIMPLEMENTED DumpOctetString(buf + idx, payloadSz); #endif ret = SendUnimplemented(ssh); } if (ret == WS_SUCCESS) { idx += payloadIdx; if (idx + padSz > len) { WLOG(WS_LOG_DEBUG, "Not enough data in buffer for pad."); ret = WS_BUFFER_E; } } if (ret == WS_SUCCESS) { idx += padSz; ssh->inputBuffer.idx = idx; ssh->peerSeq++; } return ret; } static INLINE int Encrypt(WOLFSSH* ssh, byte* cipher, const byte* input, word16 sz) { int ret = WS_SUCCESS; if (ssh == NULL || cipher == NULL || input == NULL || sz == 0) return WS_BAD_ARGUMENT; WLOG(WS_LOG_DEBUG, "Encrypt %s", IdToName(ssh->encryptId)); switch (ssh->encryptId) { case ID_NONE: break; case ID_AES128_CBC: if (wc_AesCbcEncrypt(&ssh->encryptCipher.aes, cipher, input, sz) < 0) { ret = WS_ENCRYPT_E; } break; default: ret = WS_INVALID_ALGO_ID; } ssh->txCount += sz; return ret; } static INLINE int Decrypt(WOLFSSH* ssh, byte* plain, const byte* input, word16 sz) { int ret = WS_SUCCESS; if (ssh == NULL || plain == NULL || input == NULL || sz == 0) return WS_BAD_ARGUMENT; WLOG(WS_LOG_DEBUG, "Decrypt %s", IdToName(ssh->peerEncryptId)); switch (ssh->peerEncryptId) { case ID_NONE: break; case ID_AES128_CBC: if (wc_AesCbcDecrypt(&ssh->decryptCipher.aes, plain, input, sz) < 0) { ret = WS_DECRYPT_E; } break; default: ret = WS_INVALID_ALGO_ID; } ssh->rxCount += sz; HighwaterCheck(ssh, WOLFSSH_HWSIDE_RECEIVE); return ret; } static INLINE int CreateMac(WOLFSSH* ssh, const byte* in, word32 inSz, byte* mac) { byte flatSeq[LENGTH_SZ]; int ret; c32toa(ssh->seq, flatSeq); WLOG(WS_LOG_DEBUG, "CreateMac %s", IdToName(ssh->macId)); /* Need to MAC the sequence number and the unencrypted packet */ switch (ssh->macId) { case ID_NONE: ret = WS_SUCCESS; break; case ID_HMAC_SHA1_96: { Hmac hmac; byte digest[SHA_DIGEST_SIZE]; ret = wc_HmacSetKey(&hmac, SHA, ssh->keys.macKey, ssh->keys.macKeySz); if (ret == WS_SUCCESS) ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq)); if (ret == WS_SUCCESS) ret = wc_HmacUpdate(&hmac, in, inSz); if (ret == WS_SUCCESS) ret = wc_HmacFinal(&hmac, digest); if (ret == WS_SUCCESS) WMEMCPY(mac, digest, SHA1_96_SZ); } break; case ID_HMAC_SHA1: { Hmac hmac; ret = wc_HmacSetKey(&hmac, SHA, ssh->keys.macKey, ssh->keys.macKeySz); if (ret == WS_SUCCESS) ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq)); if (ret == WS_SUCCESS) ret = wc_HmacUpdate(&hmac, in, inSz); if (ret == WS_SUCCESS) ret = wc_HmacFinal(&hmac, mac); } break; case ID_HMAC_SHA2_256: { Hmac hmac; ret = wc_HmacSetKey(&hmac, SHA256, ssh->keys.macKey, ssh->keys.macKeySz); if (ret == WS_SUCCESS) ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq)); if (ret == WS_SUCCESS) ret = wc_HmacUpdate(&hmac, in, inSz); if (ret == WS_SUCCESS) ret = wc_HmacFinal(&hmac, mac); } break; default: WLOG(WS_LOG_DEBUG, "Invalid Mac ID"); ret = WS_FATAL_ERROR; } return ret; } static INLINE int VerifyMac(WOLFSSH* ssh, const byte* in, word32 inSz, const byte* mac) { int ret; byte flatSeq[LENGTH_SZ]; byte checkMac[MAX_HMAC_SZ]; Hmac hmac; c32toa(ssh->peerSeq, flatSeq); WLOG(WS_LOG_DEBUG, "VerifyMac %s", IdToName(ssh->peerMacId)); WLOG(WS_LOG_DEBUG, "VM: inSz = %u", inSz); WLOG(WS_LOG_DEBUG, "VM: seq = %u", ssh->peerSeq); WLOG(WS_LOG_DEBUG, "VM: keyLen = %u", ssh->peerKeys.macKeySz); WMEMSET(checkMac, 0, sizeof(checkMac)); switch (ssh->peerMacId) { case ID_NONE: ret = WS_SUCCESS; break; case ID_HMAC_SHA1: case ID_HMAC_SHA1_96: ret = wc_HmacSetKey(&hmac, SHA, ssh->peerKeys.macKey, ssh->peerKeys.macKeySz); if (ret == WS_SUCCESS) ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq)); if (ret == WS_SUCCESS) ret = wc_HmacUpdate(&hmac, in, inSz); if (ret == WS_SUCCESS) ret = wc_HmacFinal(&hmac, checkMac); if (ConstantCompare(checkMac, mac, ssh->peerMacSz) != 0) ret = WS_VERIFY_MAC_E; break; case ID_HMAC_SHA2_256: ret = wc_HmacSetKey(&hmac, SHA256, ssh->peerKeys.macKey, ssh->peerKeys.macKeySz); if (ret == WS_SUCCESS) ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq)); if (ret == WS_SUCCESS) ret = wc_HmacUpdate(&hmac, in, inSz); if (ret == WS_SUCCESS) ret = wc_HmacFinal(&hmac, checkMac); if (ConstantCompare(checkMac, mac, ssh->peerMacSz) != 0) ret = WS_VERIFY_MAC_E; break; default: ret = WS_INVALID_ALGO_ID; } return ret; } static INLINE void AeadIncrementExpIv(byte* iv) { int i; iv += AEAD_IMP_IV_SZ; for (i = AEAD_EXP_IV_SZ-1; i >= 0; i--) { if (++iv[i]) return; } } static INLINE int EncryptAead(WOLFSSH* ssh, byte* cipher, const byte* input, word16 sz, byte* authTag, const byte* auth, word16 authSz) { int ret = WS_SUCCESS; if (ssh == NULL || cipher == NULL || input == NULL || sz == 0 || authTag == NULL || auth == NULL || authSz == 0) return WS_BAD_ARGUMENT; WLOG(WS_LOG_DEBUG, "EncryptAead %s", IdToName(ssh->encryptId)); if (ssh->encryptId == ID_AES128_GCM) { ret = wc_AesGcmEncrypt(&ssh->encryptCipher.aes, cipher, input, sz, ssh->keys.iv, ssh->keys.ivSz, authTag, ssh->macSz, auth, authSz); } else ret = WS_INVALID_ALGO_ID; AeadIncrementExpIv(ssh->keys.iv); ssh->txCount += sz; return ret; } static INLINE int DecryptAead(WOLFSSH* ssh, byte* plain, const byte* input, word16 sz, const byte* authTag, const byte* auth, word16 authSz) { int ret = WS_SUCCESS; if (ssh == NULL || plain == NULL || input == NULL || sz == 0 || authTag == NULL || auth == NULL || authSz == 0) return WS_BAD_ARGUMENT; WLOG(WS_LOG_DEBUG, "DecryptAead %s", IdToName(ssh->peerEncryptId)); if (ssh->peerEncryptId == ID_AES128_GCM) { ret = wc_AesGcmDecrypt(&ssh->decryptCipher.aes, plain, input, sz, ssh->peerKeys.iv, ssh->peerKeys.ivSz, authTag, ssh->peerMacSz, auth, authSz); } else ret = WS_INVALID_ALGO_ID; AeadIncrementExpIv(ssh->peerKeys.iv); ssh->rxCount += sz; HighwaterCheck(ssh, WOLFSSH_HWSIDE_RECEIVE); return ret; } int DoReceive(WOLFSSH* ssh) { int ret = WS_FATAL_ERROR; int verifyResult; word32 readSz; byte peerBlockSz = ssh->peerBlockSz; byte peerMacSz = ssh->peerMacSz; byte aeadMode = ssh->peerAeadMode; for (;;) { switch (ssh->processReplyState) { case PROCESS_INIT: readSz = peerBlockSz; //ESP_LOGI("WOLFSSH", "DoReceive: PROCESS_INT"); WLOG(WS_LOG_DEBUG, "PR1: size = %u", readSz); //ESP_LOGI("WOLFSSH", "PR1: size = %u", readSz); if ((ret = GetInputData(ssh, readSz)) < 0) { return ret; } ssh->processReplyState = PROCESS_PACKET_LENGTH; if (!aeadMode) { /* Decrypt first block if encrypted */ ret = Decrypt(ssh, ssh->inputBuffer.buffer + ssh->inputBuffer.idx, ssh->inputBuffer.buffer + ssh->inputBuffer.idx, readSz); if (ret != WS_SUCCESS) { //ESP_LOGI("WOLFSSH", "PR: First decrypt fail"); WLOG(WS_LOG_DEBUG, "PR: First decrypt fail"); return ret; } } //ESP_LOGI("WOLFSSH", "DoReceive: PROCESS_INT DONE"); FALL_THROUGH; case PROCESS_PACKET_LENGTH: //ESP_LOGI("WOLFSSH", "DoReceive: PROCESS_PACKET_LENGTH"); /* Peek at the packet_length field. */ ato32(ssh->inputBuffer.buffer + ssh->inputBuffer.idx, &ssh->curSz); ssh->processReplyState = PROCESS_PACKET_FINISH; FALL_THROUGH; case PROCESS_PACKET_FINISH: //ESP_LOGI("WOLFSSH", "DoReceive: PROCESS_PACKET_FINISH"); readSz = ssh->curSz + LENGTH_SZ + peerMacSz; WLOG(WS_LOG_DEBUG, "PR2: size = %u", readSz); if (readSz > 0) { if ((ret = GetInputData(ssh, readSz)) < 0) { return ret; } if (!aeadMode) { if (ssh->curSz + LENGTH_SZ - peerBlockSz > 0) { ret = Decrypt(ssh, ssh->inputBuffer.buffer + ssh->inputBuffer.idx + peerBlockSz, ssh->inputBuffer.buffer + ssh->inputBuffer.idx + peerBlockSz, ssh->curSz + LENGTH_SZ - peerBlockSz); } else { WLOG(WS_LOG_INFO, "Not trying to decrypt short message."); } /* Verify the buffer is big enough for the data and mac. * Even if the decrypt step fails, verify the MAC anyway. * This keeps consistent timing. */ verifyResult = VerifyMac(ssh, ssh->inputBuffer.buffer + ssh->inputBuffer.idx, ssh->curSz + LENGTH_SZ, ssh->inputBuffer.buffer + ssh->inputBuffer.idx + LENGTH_SZ + ssh->curSz); if (ret != WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "PR: Decrypt fail"); return ret; } if (verifyResult != WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "PR: VerifyMac fail"); return ret; } } else { ret = DecryptAead(ssh, ssh->inputBuffer.buffer + ssh->inputBuffer.idx + LENGTH_SZ, ssh->inputBuffer.buffer + ssh->inputBuffer.idx + LENGTH_SZ, ssh->curSz, ssh->inputBuffer.buffer + ssh->inputBuffer.idx + ssh->curSz + LENGTH_SZ, ssh->inputBuffer.buffer + ssh->inputBuffer.idx, LENGTH_SZ); if (ret != WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "PR: DecryptAead fail"); return ret; } } } ssh->processReplyState = PROCESS_PACKET; FALL_THROUGH; case PROCESS_PACKET: //ESP_LOGI("WOLFSSH", "DoReceive: PROCESS_PACKET"); if ( (ret = DoPacket(ssh)) < 0) { return ret; } WLOG(WS_LOG_DEBUG, "PR3: peerMacSz = %u", peerMacSz); ssh->inputBuffer.idx += peerMacSz; break; default: //ESP_LOGI("WOLFSSH", "DoReceive: Bad input state!!"); WLOG(WS_LOG_DEBUG, "Bad process input state, program error"); return WS_INPUT_CASE_E; } WLOG(WS_LOG_DEBUG, "PR4: Shrinking input buffer"); //ESP_LOGI("WOLFSSH", "PR4: Shrinking input buffer"); ShrinkBuffer(&ssh->inputBuffer, 1); ssh->processReplyState = PROCESS_INIT; WLOG(WS_LOG_DEBUG, "PR5: txCount = %u, rxCount = %u", ssh->txCount, ssh->rxCount); //ESP_LOGI("WOLFSSH", "PR5: txCount = %u, rxCount = %u", ssh->txCount, ssh->rxCount); return WS_SUCCESS; } } int DoProtoId(WOLFSSH* ssh) { int ret; word32 idSz; byte* eol; if ( (ret = GetInputText(ssh, &eol)) < 0) { WLOG(WS_LOG_DEBUG, "get input text failed"); return ret; } if (eol == NULL) { WLOG(WS_LOG_DEBUG, "invalid EOL"); return WS_VERSION_E; } if (WSTRNCASECMP((char*)ssh->inputBuffer.buffer, sshProtoIdStr, SSH_PROTO_SZ) == 0) { if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) ssh->clientState = CLIENT_VERSION_DONE; else ssh->serverState = SERVER_VERSION_DONE; } else { WLOG(WS_LOG_DEBUG, "SSH version mismatch"); return WS_VERSION_E; } *eol = 0; idSz = (word32)WSTRLEN((char*)ssh->inputBuffer.buffer); /* Store the proto ID for later use. It is used in keying and rekeying. */ ssh->peerProtoId = (byte*)WMALLOC(idSz + LENGTH_SZ, ssh->ctx->heap, DYNTYPE_STRING); if (ssh->peerProtoId == NULL) ret = WS_MEMORY_E; else { c32toa(idSz, ssh->peerProtoId); WMEMCPY(ssh->peerProtoId + LENGTH_SZ, ssh->inputBuffer.buffer, idSz); ssh->peerProtoIdSz = idSz + LENGTH_SZ; } ssh->inputBuffer.idx += idSz + SSH_PROTO_EOL_SZ; ShrinkBuffer(&ssh->inputBuffer, 0); return ret; } int SendProtoId(WOLFSSH* ssh) { int ret = WS_SUCCESS; word32 sshProtoIdStrSz; if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "%s", sshProtoIdStr); sshProtoIdStrSz = (word32)WSTRLEN(sshProtoIdStr); ret = GrowBuffer(&ssh->outputBuffer, sshProtoIdStrSz, 0); } if (ret == WS_SUCCESS) { WMEMCPY(ssh->outputBuffer.buffer, sshProtoIdStr, sshProtoIdStrSz); ssh->outputBuffer.length = sshProtoIdStrSz; ret = SendBuffered(ssh); } return ret; } static int PreparePacket(WOLFSSH* ssh, word32 payloadSz) { int ret = WS_SUCCESS; byte* output; word32 outputSz; word32 packetSz; word32 usedSz; byte paddingSz; if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { /* Minimum value for paddingSz is 4. */ paddingSz = ssh->blockSz - ((ssh->aeadMode ? 0 : LENGTH_SZ) + PAD_LENGTH_SZ + payloadSz) % ssh->blockSz; if (paddingSz < MIN_PAD_LENGTH) paddingSz += ssh->blockSz; ssh->paddingSz = paddingSz; packetSz = PAD_LENGTH_SZ + payloadSz + paddingSz; outputSz = LENGTH_SZ + packetSz + ssh->macSz; usedSz = ssh->outputBuffer.length - ssh->outputBuffer.idx; ret = GrowBuffer(&ssh->outputBuffer, outputSz, usedSz); } if (ret == WS_SUCCESS) { ssh->packetStartIdx = ssh->outputBuffer.length; output = ssh->outputBuffer.buffer + ssh->outputBuffer.length; /* fill in the packetSz, paddingSz */ c32toa(packetSz, output); output[LENGTH_SZ] = paddingSz; ssh->outputBuffer.length += LENGTH_SZ + PAD_LENGTH_SZ; } return ret; } static int BundlePacket(WOLFSSH* ssh) { byte* output; word32 idx; byte paddingSz; int ret = WS_SUCCESS; if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; paddingSz = ssh->paddingSz; /* Add the padding */ WLOG(WS_LOG_DEBUG, "BP: paddingSz = %u", paddingSz); if (ssh->encryptId == ID_NONE) WMEMSET(output + idx, 0, paddingSz); else if (wc_RNG_GenerateBlock(ssh->rng, output + idx, paddingSz) < 0) ret = WS_CRYPTO_FAILED; } if (!ssh->aeadMode) { if (ret == WS_SUCCESS) { idx += paddingSz; ret = CreateMac(ssh, ssh->outputBuffer.buffer + ssh->packetStartIdx, ssh->outputBuffer.length - ssh->packetStartIdx + paddingSz, output + idx); } else { WLOG(WS_LOG_DEBUG, "BP: failed to add padding"); } if (ret == WS_SUCCESS) { idx += ssh->macSz; ret = Encrypt(ssh, ssh->outputBuffer.buffer + ssh->packetStartIdx, ssh->outputBuffer.buffer + ssh->packetStartIdx, ssh->outputBuffer.length - ssh->packetStartIdx + paddingSz); } else { WLOG(WS_LOG_DEBUG, "BP: failed to generate mac"); } } else { if (ret == WS_SUCCESS) { idx += paddingSz; ret = EncryptAead(ssh, ssh->outputBuffer.buffer + ssh->packetStartIdx + LENGTH_SZ, ssh->outputBuffer.buffer + ssh->packetStartIdx + LENGTH_SZ, ssh->outputBuffer.length - ssh->packetStartIdx + paddingSz - LENGTH_SZ, output + idx, ssh->outputBuffer.buffer + ssh->packetStartIdx, LENGTH_SZ); idx += ssh->macSz; } } if (ret == WS_SUCCESS) { ssh->seq++; ssh->outputBuffer.length = idx; } else { WLOG(WS_LOG_DEBUG, "BP: failed to encrypt buffer"); } return ret; } static INLINE void CopyNameList(byte* buf, word32* idx, const char* src, word32 srcSz) { word32 begin = *idx; c32toa(srcSz, buf + begin); begin += LENGTH_SZ; WMEMCPY(buf + begin, src, srcSz); begin += srcSz; *idx = begin; } static const char cannedEncAlgoNames[] = "aes128-gcm@openssh.com,aes128-cbc"; static const char cannedMacAlgoNames[] = "hmac-sha2-256,hmac-sha1-96," "hmac-sha1"; static const char cannedKeyAlgoRsaNames[] = "ssh-rsa"; static const char cannedKeyAlgoEcc256Names[] = "ecdsa-sha2-nistp256"; static const char cannedKeyAlgoEcc384Names[] = "ecdsa-sha2-nistp384"; static const char cannedKeyAlgoEcc521Names[] = "ecdsa-sha2-nistp521"; static const char cannedKexAlgoNames[] = "ecdh-sha2-nistp256," "diffie-hellman-group-exchange-sha256," "diffie-hellman-group14-sha1," "diffie-hellman-group1-sha1"; static const char cannedNoneNames[] = "none"; static const word32 cannedEncAlgoNamesSz = sizeof(cannedEncAlgoNames) - 1; static const word32 cannedMacAlgoNamesSz = sizeof(cannedMacAlgoNames) - 1; static const word32 cannedKeyAlgoRsaNamesSz = sizeof(cannedKeyAlgoRsaNames) - 1; static const word32 cannedKeyAlgoEcc256NamesSz = sizeof(cannedKeyAlgoEcc256Names) - 1; static const word32 cannedKeyAlgoEcc384NamesSz = sizeof(cannedKeyAlgoEcc384Names) - 1; static const word32 cannedKeyAlgoEcc521NamesSz = sizeof(cannedKeyAlgoEcc521Names) - 1; static const word32 cannedKexAlgoNamesSz = sizeof(cannedKexAlgoNames) - 1; static const word32 cannedNoneNamesSz = sizeof(cannedNoneNames) - 1; int SendKexInit(WOLFSSH* ssh) { byte* output; byte* payload; word32 idx = 0; word32 payloadSz; int ret = WS_SUCCESS; const char* cannedKeyAlgoNames; word32 cannedKeyAlgoNamesSz; WLOG(WS_LOG_DEBUG, "Entering SendKexInit()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; ssh->isKeying = 1; if (ssh->handshake == NULL) { ssh->handshake = HandshakeInfoNew(ssh->ctx->heap); if (ssh->handshake == NULL) { WLOG(WS_LOG_DEBUG, "Couldn't allocate handshake info"); ret = WS_MEMORY_E; } } if (ret == WS_SUCCESS) { switch (ssh->ctx->useEcc) { case ECC_SECP256R1: cannedKeyAlgoNames = cannedKeyAlgoEcc256Names; cannedKeyAlgoNamesSz = cannedKeyAlgoEcc256NamesSz; break; case ECC_SECP384R1: cannedKeyAlgoNames = cannedKeyAlgoEcc384Names; cannedKeyAlgoNamesSz = cannedKeyAlgoEcc384NamesSz; break; case ECC_SECP521R1: cannedKeyAlgoNames = cannedKeyAlgoEcc521Names; cannedKeyAlgoNamesSz = cannedKeyAlgoEcc521NamesSz; break; default: cannedKeyAlgoNames = cannedKeyAlgoRsaNames; cannedKeyAlgoNamesSz = cannedKeyAlgoRsaNamesSz; } payloadSz = MSG_ID_SZ + COOKIE_SZ + (LENGTH_SZ * 11) + BOOLEAN_SZ + cannedKexAlgoNamesSz + cannedKeyAlgoNamesSz + (cannedEncAlgoNamesSz * 2) + (cannedMacAlgoNamesSz * 2) + (cannedNoneNamesSz * 2); ret = PreparePacket(ssh, payloadSz); } if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; payload = output + idx; output[idx++] = MSGID_KEXINIT; ret = wc_RNG_GenerateBlock(ssh->rng, output + idx, COOKIE_SZ); } if (ret == WS_SUCCESS) { byte* buf; word32 bufSz = payloadSz + LENGTH_SZ; idx += COOKIE_SZ; CopyNameList(output, &idx, cannedKexAlgoNames, cannedKexAlgoNamesSz); CopyNameList(output, &idx, cannedKeyAlgoNames, cannedKeyAlgoNamesSz); CopyNameList(output, &idx, cannedEncAlgoNames, cannedEncAlgoNamesSz); CopyNameList(output, &idx, cannedEncAlgoNames, cannedEncAlgoNamesSz); CopyNameList(output, &idx, cannedMacAlgoNames, cannedMacAlgoNamesSz); CopyNameList(output, &idx, cannedMacAlgoNames, cannedMacAlgoNamesSz); CopyNameList(output, &idx, cannedNoneNames, cannedNoneNamesSz); CopyNameList(output, &idx, cannedNoneNames, cannedNoneNamesSz); c32toa(0, output + idx); /* Languages - Client To Server (0) */ idx += LENGTH_SZ; c32toa(0, output + idx); /* Languages - Server To Client (0) */ idx += LENGTH_SZ; output[idx++] = 0; /* First KEX packet follows (false) */ c32toa(0, output + idx); /* Reserved (0) */ idx += LENGTH_SZ; ssh->outputBuffer.length = idx; buf = (byte*)WMALLOC(bufSz, ssh->ctx->heap, DYNTYPE_STRING); if (buf == NULL) { WLOG(WS_LOG_DEBUG, "Cannot allocate storage for KEX Init msg"); ret = WS_MEMORY_E; } else { c32toa(payloadSz, buf); WMEMCPY(buf + LENGTH_SZ, payload, payloadSz); ssh->handshake->kexInit = buf; ssh->handshake->kexInitSz = bufSz; } } if (ret == WS_SUCCESS) ret = BundlePacket(ssh); if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendKexInit(), ret = %d", ret); return ret; } /* SendKexDhReply() * It is also the funciton used for MSGID_KEXECDH_REPLY. The parameters * are analogous between the two messages. Where MSGID_KEXDH_REPLY has * server's public host key (K_S), f, and the signature of H; * MSGID_KEXECDH_REPLY has K_S, the server'e ephemeral public key (Q_S), * and the signature of H. This also applies to the GEX version of this. * H is calculated the same for KEXDH and KEXECDH, and has some exceptions * for GEXDH. */ int SendKexDhReply(WOLFSSH* ssh) { /* This function and DoKexDhReply() are unwieldy and in need of refactoring. */ const byte* primeGroup = dhPrimeGroup14; word32 primeGroupSz = dhPrimeGroup14Sz; const byte* generator = dhGenerator; word32 generatorSz = dhGeneratorSz; byte useEcc = 0; byte f[257]; word32 fSz = sizeof(f); byte fPad = 0; byte kPad = 0; struct { byte useRsa; word32 sz; const char *name; word32 nameSz; union { struct { RsaKey key; byte e[257]; word32 eSz; byte ePad; byte n[257]; word32 nSz; byte nPad; } rsa; struct { ecc_key key; word32 keyBlobSz; const char *keyBlobName; word32 keyBlobNameSz; byte q[257]; word32 qSz; byte qPad; const char *primeName; word32 primeNameSz; } ecc; } sk; } sigKeyBlock; byte sig[512]; word32 sigSz = sizeof(sig); word32 sigBlockSz; word32 payloadSz; byte scratchLen[LENGTH_SZ]; word32 scratch = 0; byte* output; word32 idx; int ret = WS_SUCCESS; byte msgId = MSGID_KEXDH_REPLY; WLOG(WS_LOG_DEBUG, "Entering SendKexDhReply()"); //ESP_LOGI("WOLFSSH", "Entering SendKexDhReply()"); sigKeyBlock.useRsa = ssh->handshake->pubKeyId == ID_SSH_RSA; sigKeyBlock.name = IdToName(ssh->handshake->pubKeyId); sigKeyBlock.nameSz = (word32)strlen(sigKeyBlock.name); switch (ssh->handshake->kexId) { case ID_DH_GROUP1_SHA1: //ESP_LOGI("WOLFSSH", "KexID: ID_DH_GROUP1_SHA1"); primeGroup = dhPrimeGroup1; primeGroupSz = dhPrimeGroup1Sz; break; case ID_DH_GROUP14_SHA1: //ESP_LOGI("WOLFSSH", "KexID: ID_DH_GROUP14_SHA1 (default)"); /* This is the default case. */ break; case ID_DH_GEX_SHA256: //ESP_LOGI("WOLFSSH", "KexID: ID_DH_GEX_SHA256"); msgId = MSGID_KEXDH_GEX_REPLY; break; case ID_ECDH_SHA2_NISTP256: case ID_ECDH_SHA2_NISTP384: case ID_ECDH_SHA2_NISTP521: //ESP_LOGI("WOLFSSH", "KexID: ID_ECDH_SHA2_NISTP256/384/521"); useEcc = 1; msgId = MSGID_KEXDH_REPLY; break; default: //ESP_LOGI("WOLFSSH", "KexID: INVALID"); ret = WS_INVALID_ALGO_ID; } /* At this point, the exchange hash, H, includes items V_C, V_S, I_C, * and I_S. Next add K_S, the server's public host key. K_S will * either be RSA or ECDSA public key blob. */ if (ret == WS_SUCCESS) { if (sigKeyBlock.useRsa) { //ESP_LOGI("WOLFSSH", "KeyBlock: RSA"); /* Decode the user-configured RSA private key. */ sigKeyBlock.sk.rsa.eSz = sizeof(sigKeyBlock.sk.rsa.e); sigKeyBlock.sk.rsa.nSz = sizeof(sigKeyBlock.sk.rsa.n); //ESP_LOGI("WOLFSSH", "wc_InitRsaKey"); ret = wc_InitRsaKey(&sigKeyBlock.sk.rsa.key, ssh->ctx->heap); //ESP_LOGI("WOLFSSH", "wc_InitRsaKey ret=%d", ret); if (ret == 0) ret = wc_RsaPrivateKeyDecode(ssh->ctx->privateKey, &scratch, &sigKeyBlock.sk.rsa.key, (int)ssh->ctx->privateKeySz); //ESP_LOGI("WOLFSSH", "RSAPrivKey: %p (%d)", ssh->ctx->privateKey, (int)ssh->ctx->privateKeySz); //ESP_LOGI("WOLFSSH", "wc_RsaPrivateKeyDecode ret=%d", ret); /* Flatten the public key into mpint values for the hash. */ if (ret == 0) ret = wc_RsaFlattenPublicKey(&sigKeyBlock.sk.rsa.key, sigKeyBlock.sk.rsa.e, &sigKeyBlock.sk.rsa.eSz, sigKeyBlock.sk.rsa.n, &sigKeyBlock.sk.rsa.nSz); //ESP_LOGI("WOLFSSH", "wc_RsaFlattenPublicKey ret=%d", ret); if (ret == 0) { /* Add a pad byte if the mpint has the MSB set. */ sigKeyBlock.sk.rsa.ePad = (sigKeyBlock.sk.rsa.e[0] & 0x80) ? 1 : 0; sigKeyBlock.sk.rsa.nPad = (sigKeyBlock.sk.rsa.n[0] & 0x80) ? 1 : 0; sigKeyBlock.sz = (LENGTH_SZ * 3) + sigKeyBlock.nameSz + sigKeyBlock.sk.rsa.eSz + sigKeyBlock.sk.rsa.ePad + sigKeyBlock.sk.rsa.nSz + sigKeyBlock.sk.rsa.nPad; c32toa(sigKeyBlock.sz, scratchLen); /* Hash in the length of the public key block. */ ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); } /* Hash in the length of the key type string. */ if (ret == 0) { c32toa(sigKeyBlock.nameSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); } /* Hash in the key type string. */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, (byte*)sigKeyBlock.name, sigKeyBlock.nameSz); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); /* Hash in the length of the RSA public key E value. */ if (ret == 0) { c32toa(sigKeyBlock.sk.rsa.eSz + sigKeyBlock.sk.rsa.ePad, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); } /* Hash in the pad byte for the RSA public key E value. */ if (ret == 0) { if (sigKeyBlock.sk.rsa.ePad) { scratchLen[0] = 0; ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, 1); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); } } /* Hash in the RSA public key E value. */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, sigKeyBlock.sk.rsa.e, sigKeyBlock.sk.rsa.eSz); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); /* Hash in the length of the RSA public key N value. */ if (ret == 0) { c32toa(sigKeyBlock.sk.rsa.nSz + sigKeyBlock.sk.rsa.nPad, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); } /* Hash in the pad byte for the RSA public key N value. */ if (ret == 0) { if (sigKeyBlock.sk.rsa.nPad) { scratchLen[0] = 0; ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, 1); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); } } /* Hash in the RSA public key N value. */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, sigKeyBlock.sk.rsa.n, sigKeyBlock.sk.rsa.nSz); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); } else { //ESP_LOGI("WOLFSSH", "KeyBlock: Non-RSA"); sigKeyBlock.sk.ecc.primeName = PrimeNameForId(ssh->handshake->pubKeyId); sigKeyBlock.sk.ecc.primeNameSz = (word32)strlen(sigKeyBlock.sk.ecc.primeName); /* Decode the user-configured ECDSA private key. */ sigKeyBlock.sk.ecc.qSz = sizeof(sigKeyBlock.sk.ecc.q); ret = wc_ecc_init_ex(&sigKeyBlock.sk.ecc.key, ssh->ctx->heap, INVALID_DEVID); scratch = 0; if (ret == 0) ret = wc_EccPrivateKeyDecode(ssh->ctx->privateKey, &scratch, &sigKeyBlock.sk.ecc.key, ssh->ctx->privateKeySz); /* Flatten the public key into x963 value for the exchange hash. */ if (ret == 0) ret = wc_ecc_export_x963(&sigKeyBlock.sk.ecc.key, sigKeyBlock.sk.ecc.q, &sigKeyBlock.sk.ecc.qSz); /* Hash in the length of the public key block. */ if (ret == 0) { sigKeyBlock.sz = (LENGTH_SZ * 3) + sigKeyBlock.nameSz + sigKeyBlock.sk.ecc.primeNameSz + sigKeyBlock.sk.ecc.qSz; c32toa(sigKeyBlock.sz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } /* Hash in the length of the key type string. */ if (ret == 0) { c32toa(sigKeyBlock.nameSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } /* Hash in the key type string. */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, (byte*)sigKeyBlock.name, sigKeyBlock.nameSz); /* Hash in the length of the name of the prime. */ if (ret == 0) { c32toa(sigKeyBlock.sk.ecc.primeNameSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } /* Hash in the name of the prime. */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, (const byte*)sigKeyBlock.sk.ecc.primeName, sigKeyBlock.sk.ecc.primeNameSz); /* Hash in the length of the public key. */ if (ret == 0) { c32toa(sigKeyBlock.sk.ecc.qSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } /* Hash in the public key. */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, sigKeyBlock.sk.ecc.q, sigKeyBlock.sk.ecc.qSz); } /* If using DH-GEX include the GEX specific values. */ if (ssh->handshake->kexId == ID_DH_GEX_SHA256) { byte primeGroupPad = 0, generatorPad = 0; /* Hash in the client's requested minimum key size. */ if (ret == 0) { c32toa(ssh->handshake->dhGexMinSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } //ESP_LOGI("WOLFSSH", "wc_HashUpdate(dhGexMinSz) ret=%d", ret); /* Hash in the client's requested preferred key size. */ if (ret == 0) { c32toa(ssh->handshake->dhGexPreferredSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } //ESP_LOGI("WOLFSSH", "wc_HashUpdate(dhGexPreferredSz) ret=%d", ret); /* Hash in the client's requested maximum key size. */ if (ret == 0) { c32toa(ssh->handshake->dhGexMaxSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); } //ESP_LOGI("WOLFSSH", "wc_HashUpdate(dhGexMaxSz) ret=%d", ret); /* Add a pad byte if the mpint has the MSB set. */ if (ret == 0) { if (primeGroup[0] & 0x80) primeGroupPad = 1; /* Hash in the length of the GEX prime group. */ c32toa(primeGroupSz + primeGroupPad, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); //ESP_LOGI("WOLFSSH", "wc_HashUpdate(primeGroupSz + primeGroupPad) ret=%d", ret); } /* Hash in the pad byte for the GEX prime group. */ if (ret == 0) { if (primeGroupPad) { scratchLen[0] = 0; ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, 1); //ESP_LOGI("WOLFSSH", "wc_HashUpdate(primeGroupPad) ret=%d", ret); } } /* Hash in the GEX prime group. */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, primeGroup, primeGroupSz); //ESP_LOGI("WOLFSSH", "wc_HashUpdate(hasn in gex prime grp) ret=%d", ret); /* Add a pad byte if the mpint has the MSB set. */ if (ret == 0) { if (generator[0] & 0x80) generatorPad = 1; /* Hash in the length of the GEX generator. */ c32toa(generatorSz + generatorPad, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); //ESP_LOGI("WOLFSSH", "wc_HashUpdate(add pad byte) ret=%d", ret); } /* Hash in the pad byte for the GEX generator. */ if (ret == 0) { if (generatorPad) { scratchLen[0] = 0; ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, 1); //ESP_LOGI("WOLFSSH", "wc_HashUpdate(hash in the pad byte for gex gen) ret=%d", ret); } } /* Hash in the GEX generator. */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, generator, generatorSz); //ESP_LOGI("WOLFSSH", "wc_HashUpdate(hash in the gex gen) ret=%d", ret); } /* Hash in the size of the client's DH e-value (ECDH Q-value). */ if (ret == 0) { c32toa(ssh->handshake->eSz, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); //ESP_LOGI("WOLFSSH", "wc_HashUpdate(client DH e-val size) ret=%d", ret); } /* Hash in the client's DH e-value (ECDH Q-value). */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, ssh->handshake->e, ssh->handshake->eSz); //ESP_LOGI("WOLFSSH", "wc_HashUpdate(client DH e-val) ret=%d", ret); /* Make the server's DH f-value and the shared secret K. */ /* Or make the server's ECDH private value, and the shared secret K. */ if (ret == 0) { if (!useEcc) { DhKey privKey; byte y[256]; word32 ySz = sizeof(y); ret = wc_InitDhKey(&privKey); //ESP_LOGI("WOLFSSH", "wc_InitDhKey ret=%d", ret); if (ret == 0) ret = wc_DhSetKey(&privKey, primeGroup, primeGroupSz, generator, generatorSz); //ESP_LOGI("WOLFSSH", "wc_DhSetKey ret=%d", ret); if (ret == 0) ret = wc_DhGenerateKeyPair(&privKey, ssh->rng, y, &ySz, f, &fSz); //ESP_LOGI("WOLFSSH", "wc_DhGenerateKeyPair ret=%d", ret); if (ret == 0) ret = wc_DhAgree(&privKey, ssh->k, &ssh->kSz, y, ySz, ssh->handshake->e, ssh->handshake->eSz); //ESP_LOGI("WOLFSSH", "wc_DhAgree ret=%d", ret); ForceZero(y, ySz); wc_FreeDhKey(&privKey); } else { ecc_key pubKey; ecc_key privKey; int primeId = wcPrimeForId(ssh->handshake->kexId); if (primeId == ECC_CURVE_INVALID) ret = WS_INVALID_PRIME_CURVE; if (ret == 0) ret = wc_ecc_init_ex(&pubKey, ssh->ctx->heap, INVALID_DEVID); if (ret == 0) ret = wc_ecc_init_ex(&privKey, ssh->ctx->heap, INVALID_DEVID); if (ret == 0) ret = wc_ecc_import_x963_ex(ssh->handshake->e, ssh->handshake->eSz, &pubKey, primeId); if (ret == 0) ret = wc_ecc_make_key_ex(ssh->rng, wc_ecc_get_curve_size_from_id(primeId), &privKey, primeId); if (ret == 0) ret = wc_ecc_export_x963(&privKey, f, &fSz); if (ret == 0) ret = wc_ecc_shared_secret(&privKey, &pubKey, ssh->k, &ssh->kSz); wc_ecc_free(&privKey); wc_ecc_free(&pubKey); } } /* Hash in the server's DH f-value. */ if (ret == 0) { fPad = (f[0] & 0x80) ? 1 : 0; c32toa(fSz + fPad, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); } if (ret == 0) { if (fPad) { scratchLen[0] = 0; ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, 1); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); } } if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, f, fSz); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); /* Hash in the shared secret K. */ if (ret == 0) { kPad = (ssh->k[0] & 0x80) ? 1 : 0; c32toa(ssh->kSz + kPad, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, LENGTH_SZ); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); } if (ret == 0) { if (kPad) { scratchLen[0] = 0; ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, scratchLen, 1); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); } } if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, ssh->k, ssh->kSz); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); /* Save the exchange hash value H, and session ID. */ if (ret == 0) ret = wc_HashFinal(&ssh->handshake->hash, ssh->handshake->hashId, ssh->h); //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret); if (ret == 0) { ssh->hSz = wc_HashGetDigestSize(ssh->handshake->hashId); if (ssh->sessionIdSz == 0) { WMEMCPY(ssh->sessionId, ssh->h, ssh->hSz); ssh->sessionIdSz = ssh->hSz; } } if (ret != WS_SUCCESS) ret = WS_CRYPTO_FAILED; //ESP_LOGI("WOLFSSH", "overall ret ret=%d", ret); } /* Sign h with the server's private key. */ if (ret == WS_SUCCESS) { wc_HashAlg digestHash; byte digest[WC_MAX_DIGEST_SIZE]; byte sigHashId; sigHashId = HashForId(ssh->handshake->pubKeyId); ret = wc_HashInit(&digestHash, sigHashId); if (ret == 0) ret = wc_HashUpdate(&digestHash, sigHashId, ssh->h, ssh->hSz); if (ret == 0) ret = wc_HashFinal(&digestHash, sigHashId, digest); if (ret != 0) ret = WS_CRYPTO_FAILED; if (ret == WS_SUCCESS) { if (sigKeyBlock.useRsa) { byte encSig[MAX_ENCODED_SIG_SZ]; word32 encSigSz; encSigSz = wc_EncodeSignature(encSig, digest, wc_HashGetDigestSize(sigHashId), wc_HashGetOID(sigHashId)); if (encSigSz <= 0) { WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad Encode Sig"); ret = WS_CRYPTO_FAILED; } else { WLOG(WS_LOG_INFO, "Signing hash with RSA."); sigSz = wc_RsaSSL_Sign(encSig, encSigSz, sig, sizeof(sig), &sigKeyBlock.sk.rsa.key, ssh->rng); if (sigSz <= 0) { WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad RSA Sign"); ret = WS_RSA_E; } } } else { WLOG(WS_LOG_INFO, "Signing hash with ECDSA."); sigSz = sizeof(sig); ret = wc_ecc_sign_hash(digest, wc_HashGetDigestSize(sigHashId), sig, &sigSz, ssh->rng, &sigKeyBlock.sk.ecc.key); if (ret != MP_OKAY) { WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad ECDSA Sign"); ret = WS_ECC_E; } else { byte r[257]; word32 rSz = sizeof(r); byte rPad; byte s[257]; word32 sSz = sizeof(s); byte sPad; ret = wc_ecc_sig_to_rs(sig, sigSz, r, &rSz, s, &sSz); if (ret == 0) { idx = 0; rPad = (r[0] & 0x80) ? 1 : 0; sPad = (s[0] & 0x80) ? 1 : 0; sigSz = (LENGTH_SZ * 2) + rSz + rPad + sSz + sPad; c32toa(rSz + rPad, sig + idx); idx += LENGTH_SZ; if (rPad) sig[idx++] = 0; WMEMCPY(sig + idx, r, rSz); idx += rSz; c32toa(sSz + sPad, sig + idx); idx += LENGTH_SZ; if (sPad) sig[idx++] = 0; WMEMCPY(sig + idx, s, sSz); } } } } } //ESP_LOGI("WOLFSSH", "Here??? %d", ret); if (sigKeyBlock.useRsa) wc_FreeRsaKey(&sigKeyBlock.sk.rsa.key); else wc_ecc_free(&sigKeyBlock.sk.ecc.key); sigBlockSz = (LENGTH_SZ * 2) + sigKeyBlock.nameSz + sigSz; if (ret == WS_SUCCESS) ret = GenerateKeys(ssh); /* Get the buffer, copy the packet data, once f is laid into the buffer, * add it to the hash and then add K. */ if (ret == WS_SUCCESS) { payloadSz = MSG_ID_SZ + (LENGTH_SZ * 3) + sigKeyBlock.sz + fSz + fPad + sigBlockSz; ret = PreparePacket(ssh, payloadSz); } if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = msgId; /* Copy the rsaKeyBlock into the buffer. */ c32toa(sigKeyBlock.sz, output + idx); idx += LENGTH_SZ; c32toa(sigKeyBlock.nameSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, sigKeyBlock.name, sigKeyBlock.nameSz); idx += sigKeyBlock.nameSz; if (sigKeyBlock.useRsa) { c32toa(sigKeyBlock.sk.rsa.eSz + sigKeyBlock.sk.rsa.ePad, output + idx); idx += LENGTH_SZ; if (sigKeyBlock.sk.rsa.ePad) output[idx++] = 0; WMEMCPY(output + idx, sigKeyBlock.sk.rsa.e, sigKeyBlock.sk.rsa.eSz); idx += sigKeyBlock.sk.rsa.eSz; c32toa(sigKeyBlock.sk.rsa.nSz + sigKeyBlock.sk.rsa.nPad, output + idx); idx += LENGTH_SZ; if (sigKeyBlock.sk.rsa.nPad) output[idx++] = 0; WMEMCPY(output + idx, sigKeyBlock.sk.rsa.n, sigKeyBlock.sk.rsa.nSz); idx += sigKeyBlock.sk.rsa.nSz; } else { c32toa(sigKeyBlock.sk.ecc.primeNameSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, sigKeyBlock.sk.ecc.primeName, sigKeyBlock.sk.ecc.primeNameSz); idx += sigKeyBlock.sk.ecc.primeNameSz; c32toa(sigKeyBlock.sk.ecc.qSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, sigKeyBlock.sk.ecc.q, sigKeyBlock.sk.ecc.qSz); idx += sigKeyBlock.sk.ecc.qSz; } /* Copy the server's public key. F for DE, or Q_S for ECDH. */ c32toa(fSz + fPad, output + idx); idx += LENGTH_SZ; if (fPad) output[idx++] = 0; WMEMCPY(output + idx, f, fSz); idx += fSz; /* Copy the signature of the exchange hash. */ c32toa(sigBlockSz, output + idx); idx += LENGTH_SZ; c32toa(sigKeyBlock.nameSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, sigKeyBlock.name, sigKeyBlock.nameSz); idx += sigKeyBlock.nameSz; c32toa(sigSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, sig, sigSz); idx += sigSz; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendNewKeys(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendKexDhReply(), ret = %d", ret); return ret; } int SendNewKeys(WOLFSSH* ssh) { byte* output; word32 idx = 0; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering SendNewKeys()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_NEWKEYS; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); if (ret == WS_SUCCESS) { ssh->blockSz = ssh->handshake->blockSz; ssh->encryptId = ssh->handshake->encryptId; ssh->macSz = ssh->handshake->macSz; ssh->macId = ssh->handshake->macId; ssh->aeadMode = ssh->handshake->aeadMode; WMEMCPY(&ssh->keys, &ssh->handshake->keys, sizeof(Keys)); switch (ssh->encryptId) { case ID_NONE: WLOG(WS_LOG_DEBUG, "SNK: using cipher none"); break; case ID_AES128_CBC: WLOG(WS_LOG_DEBUG, "SNK: using cipher aes128-cbc"); ret = wc_AesSetKey(&ssh->encryptCipher.aes, ssh->keys.encKey, ssh->keys.encKeySz, ssh->keys.iv, AES_ENCRYPTION); break; case ID_AES128_GCM: WLOG(WS_LOG_DEBUG, "SNK: using cipher aes128-gcm"); ret = wc_AesGcmSetKey(&ssh->encryptCipher.aes, ssh->keys.encKey, ssh->keys.encKeySz); break; default: WLOG(WS_LOG_DEBUG, "SNK: using cipher invalid"); ret = WS_INVALID_ALGO_ID; } } if (ret == WS_SUCCESS) { ssh->txCount = 0; } WLOG(WS_LOG_DEBUG, "Leaving SendNewKeys(), ret = %d", ret); return ret; } int SendKexDhGexRequest(WOLFSSH* ssh) { byte* output; word32 idx = 0; word32 payloadSz; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering SendKexDhGexRequest()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { payloadSz = MSG_ID_SZ + (UINT32_SZ * 3); ret = PreparePacket(ssh, payloadSz); } if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_KEXDH_GEX_REQUEST; c32toa(ssh->handshake->dhGexMinSz, output + idx); idx += UINT32_SZ; c32toa(ssh->handshake->dhGexPreferredSz, output + idx); idx += UINT32_SZ; c32toa(ssh->handshake->dhGexMaxSz, output + idx); idx += UINT32_SZ; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendKexDhGexRequest(), ret = %d", ret); return ret; } int SendKexDhGexGroup(WOLFSSH* ssh) { byte* output; word32 idx = 0; word32 payloadSz; const byte* primeGroup = dhPrimeGroup14; word32 primeGroupSz = dhPrimeGroup14Sz; const byte* generator = dhGenerator; word32 generatorSz = dhGeneratorSz; byte primePad = 0; byte generatorPad = 0; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering SendKexDhGexGroup()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (primeGroup[0] & 0x80) primePad = 1; if (generator[0] & 0x80) generatorPad = 1; if (ret == WS_SUCCESS) { payloadSz = MSG_ID_SZ + (LENGTH_SZ * 2) + primeGroupSz + primePad + generatorSz + generatorPad; ret = PreparePacket(ssh, payloadSz); } if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_KEXDH_GEX_GROUP; c32toa(primeGroupSz + primePad, output + idx); idx += LENGTH_SZ; if (primePad) { output[idx] = 0; idx += 1; } WMEMCPY(output + idx, primeGroup, primeGroupSz); idx += primeGroupSz; c32toa(generatorSz + generatorPad, output + idx); idx += LENGTH_SZ; if (generatorPad) { output[idx] = 0; idx += 1; } WMEMCPY(output + idx, generator, generatorSz); idx += generatorSz; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendKexDhGexGroup(), ret = %d", ret); return ret; } int SendKexDhInit(WOLFSSH* ssh) { byte* output; word32 idx = 0; word32 payloadSz; const byte* primeGroup = dhPrimeGroup14; word32 primeGroupSz = dhPrimeGroup14Sz; const byte* generator = dhGenerator; word32 generatorSz = dhGeneratorSz; int ret = WS_SUCCESS; byte msgId = MSGID_KEXDH_INIT; byte e[256]; word32 eSz = sizeof(e); byte ePad = 0; WLOG(WS_LOG_DEBUG, "Entering SendKexDhInit()"); switch (ssh->handshake->kexId) { case ID_DH_GROUP1_SHA1: primeGroup = dhPrimeGroup1; primeGroupSz = dhPrimeGroup1Sz; break; case ID_DH_GROUP14_SHA1: /* This is the default case. */ break; case ID_DH_GEX_SHA256: primeGroup = ssh->handshake->primeGroup; primeGroupSz = ssh->handshake->primeGroupSz; generator = ssh->handshake->generator; generatorSz = ssh->handshake->generatorSz; msgId = MSGID_KEXDH_GEX_INIT; break; case ID_ECDH_SHA2_NISTP256: case ID_ECDH_SHA2_NISTP384: case ID_ECDH_SHA2_NISTP521: ssh->handshake->useEcc = 1; msgId = MSGID_KEXECDH_INIT; break; default: WLOG(WS_LOG_DEBUG, "Invalid algo: %u", ssh->handshake->kexId); ret = WS_INVALID_ALGO_ID; } if (ret == WS_SUCCESS) { if (!ssh->handshake->useEcc) { DhKey* privKey = &ssh->handshake->privKey.dh; ret = wc_InitDhKey(privKey); if (ret == 0) ret = wc_DhSetKey(privKey, primeGroup, primeGroupSz, generator, generatorSz); if (ret == 0) ret = wc_DhGenerateKeyPair(privKey, ssh->rng, ssh->handshake->x, &ssh->handshake->xSz, e, &eSz); } else { ecc_key* privKey = &ssh->handshake->privKey.ecc; int primeId = wcPrimeForId(ssh->handshake->kexId); if (primeId == ECC_CURVE_INVALID) ret = WS_INVALID_PRIME_CURVE; if (ret == 0) ret = wc_ecc_init_ex(privKey, ssh->ctx->heap, INVALID_DEVID); if (ret == 0) ret = wc_ecc_make_key_ex(ssh->rng, wc_ecc_get_curve_size_from_id(primeId), privKey, primeId); if (ret == 0) ret = wc_ecc_export_x963(privKey, e, &eSz); } if (ret == 0) ret = WS_SUCCESS; } if (ret == WS_SUCCESS) { if (e[0] & 0x80) { ePad = 1; ssh->handshake->e[0] = 0; } WMEMCPY(ssh->handshake->e + ePad, e, eSz); ssh->handshake->eSz = eSz + ePad; payloadSz = MSG_ID_SZ + LENGTH_SZ + eSz + ePad; ret = PreparePacket(ssh, payloadSz); } if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = msgId; c32toa(eSz + ePad, output + idx); idx += LENGTH_SZ; if (ePad) { output[idx] = 0; idx += 1; } WMEMCPY(output + idx, e, eSz); idx += eSz; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendKexDhInit(), ret = %d", ret); return ret; } int SendUnimplemented(WOLFSSH* ssh) { byte* output; word32 idx = 0; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering SendUnimplemented(), peerSeq = %u", ssh->peerSeq); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_UNIMPLEMENTED; c32toa(ssh->peerSeq, output + idx); idx += UINT32_SZ; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendUnimplemented(), ret = %d", ret); return ret; } int SendDisconnect(WOLFSSH* ssh, word32 reason) { byte* output; word32 idx = 0; int ret = WS_SUCCESS; if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + (LENGTH_SZ * 2)); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_DISCONNECT; c32toa(reason, output + idx); idx += UINT32_SZ; c32toa(0, output + idx); idx += LENGTH_SZ; c32toa(0, output + idx); idx += LENGTH_SZ; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); return ret; } int SendIgnore(WOLFSSH* ssh, const unsigned char* data, word32 dataSz) { byte* output; word32 idx = 0; int ret = WS_SUCCESS; if (ssh == NULL || (data == NULL && dataSz > 0)) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ + dataSz); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_IGNORE; c32toa(dataSz, output + idx); idx += LENGTH_SZ; if (dataSz > 0) { WMEMCPY(output + idx, data, dataSz); idx += dataSz; } ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); return ret; } static const char cannedLangTag[] = "en-us"; static const word32 cannedLangTagSz = sizeof(cannedLangTag) - 1; int SendDebug(WOLFSSH* ssh, byte alwaysDisplay, const char* msg) { word32 msgSz; byte* output; word32 idx = 0; int ret = WS_SUCCESS; if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { msgSz = (msg != NULL) ? (word32)WSTRLEN(msg) : 0; ret = PreparePacket(ssh, MSG_ID_SZ + BOOLEAN_SZ + (LENGTH_SZ * 2) + msgSz + cannedLangTagSz); } if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_DEBUG; output[idx++] = (alwaysDisplay != 0); c32toa(msgSz, output + idx); idx += LENGTH_SZ; if (msgSz > 0) { WMEMCPY(output + idx, msg, msgSz); idx += msgSz; } c32toa(cannedLangTagSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, cannedLangTag, cannedLangTagSz); idx += cannedLangTagSz; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); return ret; } int SendServiceRequest(WOLFSSH* ssh, byte serviceId) { const char* serviceName; word32 serviceNameSz; byte* output; word32 idx; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering SendServiceRequest()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { serviceName = IdToName(serviceId); serviceNameSz = (word32)WSTRLEN(serviceName); ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ + serviceNameSz); } if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_SERVICE_REQUEST; c32toa(serviceNameSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, serviceName, serviceNameSz); idx += serviceNameSz; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendServiceRequest(), ret = %d", ret); return ret; } int SendServiceAccept(WOLFSSH* ssh, byte serviceId) { const char* serviceName; word32 serviceNameSz; byte* output; word32 idx; int ret = WS_SUCCESS; if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { serviceName = IdToName(serviceId); serviceNameSz = (word32)WSTRLEN(serviceName); ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ + serviceNameSz); } if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_SERVICE_ACCEPT; c32toa(serviceNameSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, serviceName, serviceNameSz); idx += serviceNameSz; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendUserAuthBanner(ssh); return ret; } static const char cannedAuths[] = "publickey,password"; static const word32 cannedAuthsSz = sizeof(cannedAuths) - 1; int SendUserAuthRequest(WOLFSSH* ssh, byte authId) { byte* output; word32 idx; const char* authName; word32 authNameSz; const char* serviceName; word32 serviceNameSz; word32 payloadSz; int ret = WS_SUCCESS; WS_UserAuthData authData; WLOG(WS_LOG_DEBUG, "Entering SendUserAuthRequest()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { if (authId == ID_USERAUTH_PASSWORD && ssh->ctx->userAuthCb != NULL) { WLOG(WS_LOG_DEBUG, "SUARPW: Calling the userauth callback"); ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PASSWORD, &authData, ssh->userAuthCtx); if (ret != WOLFSSH_USERAUTH_SUCCESS) { WLOG(WS_LOG_DEBUG, "SUARPW: Couldn't get password"); ret = WS_FATAL_ERROR; } } } if (ret == WS_SUCCESS) { serviceName = IdToName(ID_SERVICE_CONNECTION); serviceNameSz = (word32)WSTRLEN(serviceName); authName = IdToName(authId); authNameSz = (word32)WSTRLEN(authName); payloadSz = MSG_ID_SZ + (LENGTH_SZ * 3) + ssh->userNameSz + serviceNameSz + authNameSz; if (authId == ID_USERAUTH_PASSWORD) { payloadSz += BOOLEAN_SZ + LENGTH_SZ + authData.sf.password.passwordSz; } else if (authId != ID_NONE) ret = WS_INVALID_ALGO_ID; } if (ret == WS_SUCCESS) ret = PreparePacket(ssh, payloadSz); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_USERAUTH_REQUEST; c32toa(ssh->userNameSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, ssh->userName, ssh->userNameSz); idx += ssh->userNameSz; c32toa(serviceNameSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, serviceName, serviceNameSz); idx += serviceNameSz; c32toa(authNameSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, authName, authNameSz); idx += authNameSz; if (authId == ID_USERAUTH_PASSWORD) { output[idx++] = 0; /* Boolean "FALSE" for password change */ c32toa(authData.sf.password.passwordSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, authData.sf.password.password, authData.sf.password.passwordSz); idx += authData.sf.password.passwordSz; } ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendUserAuthRequest(), ret = %d", ret); return ret; } int SendUserAuthFailure(WOLFSSH* ssh, byte partialSuccess) { byte* output; word32 idx; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering SendUserAuthFailure()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ + cannedAuthsSz + BOOLEAN_SZ); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_USERAUTH_FAILURE; c32toa(cannedAuthsSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, cannedAuths, cannedAuthsSz); idx += cannedAuthsSz; output[idx++] = (partialSuccess != 0); ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); return ret; } int SendUserAuthSuccess(WOLFSSH* ssh) { byte* output; word32 idx; int ret = WS_SUCCESS; if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_USERAUTH_SUCCESS; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); return ret; } int SendUserAuthPkOk(WOLFSSH* ssh, const byte* algoName, word32 algoNameSz, const byte* publicKey, word32 publicKeySz) { byte* output; word32 idx; int ret = WS_SUCCESS; if (ssh == NULL || algoName == NULL || algoNameSz == 0 || publicKey == NULL || publicKeySz == 0) { ret = WS_BAD_ARGUMENT; } if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ + (LENGTH_SZ * 2) + algoNameSz + publicKeySz); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_USERAUTH_PK_OK; c32toa(algoNameSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, algoName, algoNameSz); idx += algoNameSz; c32toa(publicKeySz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, publicKey, publicKeySz); idx += publicKeySz; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); return ret; } int SendUserAuthBanner(WOLFSSH* ssh) { byte* output; word32 idx; int ret = WS_SUCCESS; const char* banner; word32 bannerSz = 0; if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ssh->ctx->banner != NULL && ssh->ctx->bannerSz > 0) { banner = ssh->ctx->banner; bannerSz = ssh->ctx->bannerSz; } if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ + (LENGTH_SZ * 2) + bannerSz + cannedLangTagSz); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_USERAUTH_BANNER; c32toa(bannerSz, output + idx); idx += LENGTH_SZ; if (bannerSz > 0) WMEMCPY(output + idx, banner, bannerSz); idx += bannerSz; c32toa(cannedLangTagSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, cannedLangTag, cannedLangTagSz); idx += cannedLangTagSz; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); return ret; } int SendRequestSuccess(WOLFSSH* ssh, int success) { byte* output; word32 idx; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering SendRequestSuccess(), %s", success ? "Success" : "Failure"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = success ? MSGID_REQUEST_SUCCESS : MSGID_REQUEST_FAILURE; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendRequestSuccess(), ret = %d", ret); return ret; } int SendChannelOpenSession(WOLFSSH* ssh, word32 initialWindowSz, word32 maxPacketSz) { WOLFSSH_CHANNEL* newChannel; byte* output; const char* channelType; word32 channelTypeSz, channelId, idx; int ret = WS_SUCCESS; WLOG(WS_LOG_DEBUG, "Entering SendChannelOpenSession()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { channelId = ssh->nextChannel; newChannel = ChannelNew(ssh, ID_CHANTYPE_SESSION, initialWindowSz, maxPacketSz); if (newChannel == NULL) ret = WS_MEMORY_E; if (ret == WS_SUCCESS) ret = ChannelAppend(ssh, newChannel); } if (ret == WS_SUCCESS) { channelType = IdToName(ID_CHANTYPE_SESSION); channelTypeSz = (word32)WSTRLEN(channelType); ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ + channelTypeSz + (UINT32_SZ * 3)); } if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_CHANNEL_OPEN; c32toa(channelTypeSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, channelType, channelTypeSz); idx += channelTypeSz; c32toa(channelId, output + idx); idx += UINT32_SZ; c32toa(initialWindowSz, output + idx); idx += UINT32_SZ; c32toa(maxPacketSz, output + idx); idx += UINT32_SZ; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendChannelOpenSession(), ret = %d", ret); return ret; } int SendChannelOpenConf(WOLFSSH* ssh) { byte* output; word32 idx; int ret = WS_SUCCESS; WOLFSSH_CHANNEL* channel; WLOG(WS_LOG_DEBUG, "Entering SendChannelOpenConf()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { channel = ChannelFind(ssh, ssh->defaultPeerChannelId, FIND_PEER); if (channel == NULL) ret = WS_INVALID_CHANID; } if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ + (UINT32_SZ * 4)); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_CHANNEL_OPEN_CONF; c32toa(channel->peerChannel, output + idx); idx += UINT32_SZ; c32toa(channel->channel, output + idx); idx += UINT32_SZ; c32toa(channel->windowSz, output + idx); idx += UINT32_SZ; c32toa(channel->maxPacketSz, output + idx); idx += UINT32_SZ; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendChannelOpenConf(), ret = %d", ret); return ret; } int SendChannelEof(WOLFSSH* ssh, word32 peerChannelId) { byte* output; word32 idx; int ret = WS_SUCCESS; WOLFSSH_CHANNEL* channel; WLOG(WS_LOG_DEBUG, "Entering SendChannelEof()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { channel = ChannelFind(ssh, peerChannelId, FIND_PEER); if (channel == NULL) ret = WS_INVALID_CHANID; } if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_CHANNEL_EOF; c32toa(channel->peerChannel, output + idx); idx += UINT32_SZ; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendChannelEof(), ret = %d", ret); return ret; } int SendChannelClose(WOLFSSH* ssh, word32 peerChannelId) { byte* output; word32 idx; int ret = WS_SUCCESS; WOLFSSH_CHANNEL* channel; WLOG(WS_LOG_DEBUG, "Entering SendChannelClose()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { channel = ChannelFind(ssh, peerChannelId, FIND_PEER); if (channel == NULL) ret = WS_INVALID_CHANID; } if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_CHANNEL_CLOSE; c32toa(channel->peerChannel, output + idx); idx += UINT32_SZ; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendChannelClose(), ret = %d", ret); return ret; } word32 min(word32 a, word32 b){ return 0; } int SendChannelData(WOLFSSH* ssh, word32 peerChannel, byte* data, word32 dataSz) { byte* output; word32 idx; int ret = WS_SUCCESS; WOLFSSH_CHANNEL* channel; WLOG(WS_LOG_DEBUG, "Entering SendChannelData()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { if (ssh->isKeying) ret = WS_REKEYING; } if (ret == WS_SUCCESS) { channel = ChannelFind(ssh, peerChannel, FIND_PEER); if (channel == NULL) { WLOG(WS_LOG_DEBUG, "Invalid peer channel"); ret = WS_INVALID_CHANID; } } if (ret == WS_SUCCESS) { word32 bound = min(channel->peerWindowSz, channel->peerMaxPacketSz); if (dataSz > bound) { WLOG(WS_LOG_DEBUG, "Trying to send %u, client will only accept %u, limiting", dataSz, bound); dataSz = bound; } ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + LENGTH_SZ + dataSz); } if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_CHANNEL_DATA; c32toa(channel->peerChannel, output + idx); idx += UINT32_SZ; c32toa(dataSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, data, dataSz); idx += dataSz; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); if (ret == WS_SUCCESS) { WLOG(WS_LOG_INFO, " dataSz = %u", dataSz); WLOG(WS_LOG_INFO, " peerWindowSz = %u", channel->peerWindowSz); channel->peerWindowSz -= dataSz; WLOG(WS_LOG_INFO, " update peerWindowSz = %u", channel->peerWindowSz); ret = dataSz; } WLOG(WS_LOG_DEBUG, "Leaving SendChannelData(), ret = %d", ret); return ret; } int SendChannelWindowAdjust(WOLFSSH* ssh, word32 peerChannel, word32 bytesToAdd) { byte* output; word32 idx; int ret = WS_SUCCESS; WOLFSSH_CHANNEL* channel; WLOG(WS_LOG_DEBUG, "Entering SendChannelWindowAdjust()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; channel = ChannelFind(ssh, peerChannel, FIND_PEER); if (channel == NULL) { WLOG(WS_LOG_DEBUG, "Invalid peer channel"); ret = WS_INVALID_CHANID; } if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ + (UINT32_SZ * 2)); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_CHANNEL_WINDOW_ADJUST; c32toa(channel->peerChannel, output + idx); idx += UINT32_SZ; c32toa(bytesToAdd, output + idx); idx += UINT32_SZ; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendChannelWindowAdjust(), ret = %d", ret); return ret; } static const char cannedShellName[] = "shell"; static const word32 cannedShellNameSz = sizeof(cannedShellName) - 1; int SendChannelRequestShell(WOLFSSH* ssh) { byte* output; word32 idx; int ret = WS_SUCCESS; WOLFSSH_CHANNEL* channel; WLOG(WS_LOG_DEBUG, "Entering SendChannelRequestShell()"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { channel = ChannelFind(ssh, ssh->defaultPeerChannelId, FIND_PEER); if (channel == NULL) ret = WS_INVALID_CHANID; } if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + LENGTH_SZ + cannedShellNameSz + BOOLEAN_SZ); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = MSGID_CHANNEL_REQUEST; c32toa(channel->peerChannel, output + idx); idx += UINT32_SZ; c32toa(cannedShellNameSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, cannedShellName, cannedShellNameSz); idx += cannedShellNameSz; output[idx++] = 1; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendChannelRequestShell(), ret = %d", ret); return ret; } int SendChannelSuccess(WOLFSSH* ssh, word32 channelId, int success) { byte* output; word32 idx; int ret = WS_SUCCESS; WOLFSSH_CHANNEL* channel; WLOG(WS_LOG_DEBUG, "Entering SendChannelSuccess(), %s", success ? "Success" : "Failure"); if (ssh == NULL) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { channel = ChannelFind(ssh, channelId, FIND_SELF); if (channel == NULL) { WLOG(WS_LOG_DEBUG, "Invalid channel"); ret = WS_INVALID_CHANID; } } if (ret == WS_SUCCESS) ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ); if (ret == WS_SUCCESS) { output = ssh->outputBuffer.buffer; idx = ssh->outputBuffer.length; output[idx++] = success ? MSGID_CHANNEL_SUCCESS : MSGID_CHANNEL_FAILURE; c32toa(channel->peerChannel, output + idx); idx += UINT32_SZ; ssh->outputBuffer.length = idx; ret = BundlePacket(ssh); } if (ret == WS_SUCCESS) ret = SendBuffered(ssh); WLOG(WS_LOG_DEBUG, "Leaving SendChannelSuccess(), ret = %d", ret); return ret; } #ifdef DEBUG_WOLFSSH #define LINE_WIDTH 16 void DumpOctetString(const byte* input, word32 inputSz) { int rows = inputSz / LINE_WIDTH; int remainder = inputSz % LINE_WIDTH; int i,j; for (i = 0; i < rows; i++) { printf("%04X: ", i * LINE_WIDTH); for (j = 0; j < LINE_WIDTH; j++) { printf("%02X ", input[i * LINE_WIDTH + j]); } printf("\n"); } if (remainder) { printf("%04X: ", i * LINE_WIDTH); for (j = 0; j < remainder; j++) { printf("%02X ", input[i * LINE_WIDTH + j]); } printf("\n"); } } #endif