ssh
Revision 0:c4152c628df5, committed 2019-11-25
- Comitter:
- sPymbed
- Date:
- Mon Nov 25 14:24:05 2019 +0000
- Commit message:
- first commit
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/internal.c Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,6697 @@ +/* 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/io.c Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,374 @@ +/* io.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 io module provides the default send and receive callbacks used + * by the library to handle network I/O. By default they handle UNIX + * style I/O. + */ + + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <wolfssh/ssh.h> +#include <wolfssh/internal.h> +#include <wolfssh/log.h> + +#ifndef NULL + #include <stddef.h> +#endif + + +/* allow I/O callback handlers whether user I/O or not */ + +/* install I/O recv callback */ +void wolfSSH_SetIORecv(WOLFSSH_CTX* ctx, WS_CallbackIORecv cb) +{ + if (ctx) + ctx->ioRecvCb = cb; +} + + +/* install I/O send callback */ +void wolfSSH_SetIOSend(WOLFSSH_CTX* ctx, WS_CallbackIOSend cb) +{ + if (ctx) + ctx->ioSendCb = cb; +} + + +/* install I/O read context */ +void wolfSSH_SetIOReadCtx(WOLFSSH* ssh, void *ctx) +{ + if (ssh) + ssh->ioReadCtx = ctx; +} + + +/* install I/O read context */ +void wolfSSH_SetIOWriteCtx(WOLFSSH* ssh, void *ctx) +{ + if (ssh) + ssh->ioWriteCtx = ctx; +} + + +/* get I/O read context */ +void* wolfSSH_GetIOReadCtx(WOLFSSH* ssh) +{ + if (ssh) + return ssh->ioReadCtx; + + return NULL; +} + + +/* get I/O write context */ +void* wolfSSH_GetIOWriteCtx(WOLFSSH* ssh) +{ + if (ssh) + return ssh->ioWriteCtx; + + return NULL; +} + + +#ifndef WOLFSSH_USER_IO + +/* default I/O callbacks, use BSD style sockets */ + + +#ifndef USE_WINDOWS_API + #ifdef WOLFSSH_LWIP + /* lwIP needs to be configured to use sockets API in this mode */ + /* LWIP_SOCKET 1 in lwip/opt.h or in build */ + #include "lwip/sockets.h" + #include <errno.h> + #ifndef LWIP_PROVIDE_ERRNO + #define LWIP_PROVIDE_ERRNO 1 + #endif + #elif defined(FREESCALE_MQX) + #include <posix.h> + #include <rtcs.h> + #elif defined(WOLFSSH_MDK_ARM) + #if defined(WOLFSSH_MDK5) + #include "cmsis_os.h" + #include "rl_fs.h" + #include "rl_net.h" + #else + #include <rtl.h> + #endif + #undef RNG + #include "WOLFSSH_MDK_ARM.h" + #undef RNG + #define RNG CyaSSL_RNG + /* for avoiding name conflict in "stm32f2xx.h" */ + static int errno; + #else + //#include <sys/types.h> + #include <errno.h> + #ifndef EBSNET + //#include <unistd.h> + #endif + //#include <fcntl.h> + #if !(defined(DEVKITPRO) || defined(HAVE_RTP_SYS) || defined(EBSNET)) + #include <sys/socket.h> + #include <arpa/inet.h> + //#include <netinet/in.h> + #include <netdb.h> + #ifdef __PPU + #include <netex/errno.h> + #else + //#include <sys/ioctl.h> + #endif + #endif + #ifdef HAVE_RTP_SYS + #include <socket.h> + #endif + #ifdef EBSNET + #include "rtipapi.h" /* errno */ + #include "socket.h" + #endif + #endif +#endif /* USE_WINDOWS_API */ + +#ifdef __sun + #include <sys/filio.h> +#endif + +#ifdef USE_WINDOWS_API + /* no epipe yet */ + #ifndef WSAEPIPE + #define WSAEPIPE -12345 + #endif + #define SOCKET_EWOULDBLOCK WSAEWOULDBLOCK + #define SOCKET_EAGAIN WSAETIMEDOUT + #define SOCKET_ECONNRESET WSAECONNRESET + #define SOCKET_EINTR WSAEINTR + #define SOCKET_EPIPE WSAEPIPE + #define SOCKET_ECONNREFUSED WSAENOTCONN + #define SOCKET_ECONNABORTED WSAECONNABORTED + #define close(s) closesocket(s) +#elif defined(__PPU) + #define SOCKET_EWOULDBLOCK SYS_NET_EWOULDBLOCK + #define SOCKET_EAGAIN SYS_NET_EAGAIN + #define SOCKET_ECONNRESET SYS_NET_ECONNRESET + #define SOCKET_EINTR SYS_NET_EINTR + #define SOCKET_EPIPE SYS_NET_EPIPE + #define SOCKET_ECONNREFUSED SYS_NET_ECONNREFUSED + #define SOCKET_ECONNABORTED SYS_NET_ECONNABORTED +#elif defined(FREESCALE_MQX) + /* RTCS doesn't have an EWOULDBLOCK error */ + #define SOCKET_EWOULDBLOCK EAGAIN + #define SOCKET_EAGAIN EAGAIN + #define SOCKET_ECONNRESET RTCSERR_TCP_CONN_RESET + #define SOCKET_EINTR EINTR + #define SOCKET_EPIPE EPIPE + #define SOCKET_ECONNREFUSED RTCSERR_TCP_CONN_REFUSED + #define SOCKET_ECONNABORTED RTCSERR_TCP_CONN_ABORTED +#elif defined(WOLFSSH_MDK_ARM) + #if defined(WOLFSSH_MDK5) + #define SOCKET_EWOULDBLOCK BSD_ERROR_WOULDBLOCK + #define SOCKET_EAGAIN BSD_ERROR_LOCKED + #define SOCKET_ECONNRESET BSD_ERROR_CLOSED + #define SOCKET_EINTR BSD_ERROR + #define SOCKET_EPIPE BSD_ERROR + #define SOCKET_ECONNREFUSED BSD_ERROR + #define SOCKET_ECONNABORTED BSD_ERROR + #else + #define SOCKET_EWOULDBLOCK SCK_EWOULDBLOCK + #define SOCKET_EAGAIN SCK_ELOCKED + #define SOCKET_ECONNRESET SCK_ECLOSED + #define SOCKET_EINTR SCK_ERROR + #define SOCKET_EPIPE SCK_ERROR + #define SOCKET_ECONNREFUSED SCK_ERROR + #define SOCKET_ECONNABORTED SCK_ERROR + #endif +#else + #define SOCKET_EWOULDBLOCK EWOULDBLOCK + #define SOCKET_EAGAIN EAGAIN + #define SOCKET_ECONNRESET ECONNRESET + #define SOCKET_EINTR EINTR + #define SOCKET_EPIPE EPIPE + #define SOCKET_ECONNREFUSED ECONNREFUSED + #define SOCKET_ECONNABORTED ECONNABORTED +#endif /* USE_WINDOWS_API */ + +int recv(int a, void* b, int c, unsigned int d){ + return 0; + } + +int send(int a, const void* b, int c, unsigned int d){ + return 0; + } + +#ifdef DEVKITPRO + /* from network.h */ + int net_send(int, const void*, int, unsigned int); + int net_recv(int, void*, int, unsigned int); + #define SEND_FUNCTION net_send + #define RECV_FUNCTION net_recv +#elif defined(WOLFSSH_LWIP) + #define SEND_FUNCTION lwip_send + #define RECV_FUNCTION lwip_recv +#else + #define SEND_FUNCTION send + #define RECV_FUNCTION recv +#endif + + +/* Translates return codes returned from + * send() and recv() if need be. + */ +static INLINE int TranslateReturnCode(int old, int sd) +{ + (void)sd; + +#ifdef FREESCALE_MQX + if (old == 0) { + errno = SOCKET_EWOULDBLOCK; + return -1; /* convert to BSD style wouldblock as error */ + } + + if (old < 0) { + errno = RTCS_geterror(sd); + if (errno == RTCSERR_TCP_CONN_CLOSING) + return 0; /* convert to BSD style closing */ + } +#endif + + return old; +} + +static INLINE int LastError(void) +{ +#ifdef USE_WINDOWS_API + return WSAGetLastError(); +#elif defined(EBSNET) + return xn_getlasterror(); +#else + return errno; +#endif +} + + +/* The receive embedded callback + * return : nb bytes read, or error + */ +int wsEmbedRecv(WOLFSSH* ssh, void* data, word32 sz, void* ctx) +{ + int recvd; + int err; + int sd = *(int*)ctx; + char* buf = (char*)data; + + recvd = (int)RECV_FUNCTION(sd, buf, sz, ssh->rflags); + + recvd = TranslateReturnCode(recvd, sd); + + if (recvd < 0) { + err = LastError(); + WLOG(WS_LOG_DEBUG,"Embed Receive error"); + + /*if (err == SOCKET_EWOULDBLOCK || err == SOCKET_EAGAIN) { + WLOG(WS_LOG_DEBUG," Would block"); + return WS_CBIO_ERR_WANT_READ; + } + else if (err == SOCKET_ECONNRESET) { + WLOG(WS_LOG_DEBUG," Connection reset"); + return WS_CBIO_ERR_CONN_RST; + } + else if (err == SOCKET_EINTR) { + WLOG(WS_LOG_DEBUG," Socket interrupted"); + return WS_CBIO_ERR_ISR; + } + else if (err == SOCKET_ECONNREFUSED) { + WLOG(WS_LOG_DEBUG," Connection refused"); + return WS_CBIO_ERR_WANT_READ; + } + else if (err == SOCKET_ECONNABORTED) { + WLOG(WS_LOG_DEBUG," Connection aborted"); + return WS_CBIO_ERR_CONN_CLOSE; + } + else*/ { + WLOG(WS_LOG_DEBUG," General error"); + return WS_CBIO_ERR_GENERAL; + } + } + else if (recvd == 0) { + WLOG(WS_LOG_DEBUG,"Embed receive connection closed"); + return WS_CBIO_ERR_CONN_CLOSE; + } + + return recvd; +} + + +/* The send embedded callback + * return : nb bytes sent, or error + */ +int wsEmbedSend(WOLFSSH* ssh, void* data, word32 sz, void* ctx) +{ + int sd = *(int*)ctx; + int sent; + int err; + char* buf = (char*)data; + + WLOG(WS_LOG_DEBUG,"Embed Send trying to send %d", sz); + + sent = (int)SEND_FUNCTION(sd, buf, sz, ssh->wflags); + + WLOG(WS_LOG_DEBUG,"Embed Send sent %d", sent); + + if (sent < 0) { + err = LastError(); + WLOG(WS_LOG_DEBUG,"Embed Send error"); + + /*if (err == SOCKET_EWOULDBLOCK || err == SOCKET_EAGAIN) { + WLOG(WS_LOG_DEBUG," Would Block"); + return WS_CBIO_ERR_WANT_WRITE; + } + else if (err == SOCKET_ECONNRESET) { + WLOG(WS_LOG_DEBUG," Connection reset"); + return WS_CBIO_ERR_CONN_RST; + } + else if (err == SOCKET_EINTR) { + WLOG(WS_LOG_DEBUG," Socket interrupted"); + return WS_CBIO_ERR_ISR; + } + else if (err == SOCKET_EPIPE) { + WLOG(WS_LOG_DEBUG," Socket EPIPE"); + return WS_CBIO_ERR_CONN_CLOSE; + } + else */{ + WLOG(WS_LOG_DEBUG," General error"); + return WS_CBIO_ERR_GENERAL; + } + } + + return sent; +} + + +#endif /* WOLFSSH_USER_IO */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/keygen.c Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,102 @@ +/* keygen.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 keygen module contains utility functions wrapping the wolfCrypt + * key generation functions to product SSH friendly keys. + */ + + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <wolfssl/options.h> +#include <wolfcrypt/random.h> +#include <wolfcrypt/rsa.h> +#include <wolfssh/error.h> +#include <wolfssh/keygen.h> +#include <wolfssh/log.h> + +#ifdef WOLFSSH_KEYGEN + +#ifdef NO_INLINE + #include <wolfssh/misc.h> +#else + #define WOLFSSH_MISC_INCLUDED + #include "src/misc.c" +#endif + + +int wolfSSH_MakeRsaKey(byte* out, word32 outSz, + word32 size, word32 e) +{ + int ret = WS_SUCCESS; + WC_RNG rng; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_MakeRsaKey()"); + + if (wc_InitRng(&rng) != 0) { + WLOG(WS_LOG_DEBUG, "Couldn't create RNG"); + ret = WS_CRYPTO_FAILED; + } + + if (ret == WS_SUCCESS) { + RsaKey key; + + if (wc_InitRsaKey(&key, NULL) != 0) + ret = WS_CRYPTO_FAILED; + + if (ret == WS_SUCCESS) { + if (wc_MakeRsaKey(&key, size, e, &rng) != 0) { + WLOG(WS_LOG_DEBUG, "RSA key generation failed"); + ret = WS_CRYPTO_FAILED; + } + } + + if (ret == WS_SUCCESS) { + int keySz; + + keySz = wc_RsaKeyToDer(&key, out, outSz); + if (keySz < 0) { + WLOG(WS_LOG_DEBUG, "RSA key to DER failed"); + ret = WS_CRYPTO_FAILED; + } + else + ret = keySz; + } + + if (wc_FreeRsaKey(&key) != 0) { + WLOG(WS_LOG_DEBUG, "RSA key free failed"); + ret = WS_CRYPTO_FAILED; + } + + if (wc_FreeRng(&rng) != 0) { + WLOG(WS_LOG_DEBUG, "Couldn't free RNG"); + ret = WS_CRYPTO_FAILED; + } + } + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_MakeRsaKey(), ret = %d", ret); + return ret; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/log.c Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,162 @@ +/* log.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 log module contains the interface to the logging function. When + * debugging is enabled and turned on, the logger will output to STDOUT. + * A custom logging callback may be installed. + */ + + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <wolfssh/ssh.h> +#include <wolfssh/log.h> +#include <wolfssh/error.h> + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#ifndef NO_TIMESTAMP + //#include <time.h> +#endif + +//#include "esp_log.h" + + +#ifndef WOLFSSH_DEFAULT_LOG_WIDTH + #define WOLFSSH_DEFAULT_LOG_WIDTH 120 +#endif + + +static const char *TAG = "WOLFSSH"; + + +static void DefaultLoggingCb(enum wolfSSH_LogLevel, const char *const); + +static wolfSSH_LoggingCb logFunction = DefaultLoggingCb; +static enum wolfSSH_LogLevel logLevel = WS_LOG_USER; // esp32: max +#ifdef DEBUG_WOLFSSH + static int logEnable = 0; +#endif + + +/* turn debugging on if supported */ +void wolfSSH_Debugging_ON(void) +{ +#ifdef DEBUG_WOLFSSH + logEnable = 1; +#endif +} + + +/* turn debugging off */ +void wolfSSH_Debugging_OFF(void) +{ +#ifdef DEBUG_WOLFSSH + logEnable = 0; +#endif +} + + +/* set logging callback function */ +void wolfSSH_SetLoggingCb(wolfSSH_LoggingCb logF) +{ + if (logF) + logFunction = logF; +} + + +int wolfSSH_LogEnabled(void) +{ +#ifdef DEBUG_WOLFSSH + return logEnable; +#else + return 0; +#endif +} + + +/* log level string */ +static const char* GetLogStr(enum wolfSSH_LogLevel level) +{ + switch (level) { + case WS_LOG_INFO: + return "INFO"; + + case WS_LOG_WARN: + return "WARNING"; + + case WS_LOG_ERROR: + return "ERROR"; + + case WS_LOG_DEBUG: + return "DEBUG"; + + case WS_LOG_USER: + return "USER"; + + default: + return "UNKNOWN"; + } +} + + +void DefaultLoggingCb(enum wolfSSH_LogLevel level, const char *const msgStr) +{ + char timeStr[80]; + timeStr[0] = '\0'; +#ifndef NO_TIMESTAMP + { + time_t current; + struct tm local; + + current = time(NULL); + if (WLOCALTIME(¤t, &local)) { + /* make pretty */ + strftime(timeStr, sizeof(timeStr), "%b %d %T %Y: ", &local); + } + } +#endif /* NO_TIMESTAMP */ + //fprintf(stdout, "%s[%s] %s\n", timeStr, GetLogStr(level), msgStr); + //ESP_LOGI(TAG, "%s[%s] %s\n", timeStr, GetLogStr(level), msgStr); +} + + +/* our default logger */ +void wolfSSH_Log(enum wolfSSH_LogLevel level, const char *const fmt, ...) +{ + va_list vlist; + char msgStr[WOLFSSH_DEFAULT_LOG_WIDTH]; + + if (level < logLevel) + return; /* don't need to output */ + + /* format msg */ + va_start(vlist, fmt); + WVSNPRINTF(msgStr, sizeof(msgStr), fmt, vlist); + va_end(vlist); + + if (logFunction) + logFunction(level, msgStr); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/memory.c Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,109 @@ +/* memory.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 memory module contains the interface to the custom memory handling hooks. + */ + + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <wolfssh/settings.h> + + +#ifdef USE_WOLFSSH_MEMORY + +#include <wolfssh/memory.h> +#include <wolfssh/error.h> + +/* Set these to default values initially. */ +static wolfSSH_Malloc_cb malloc_function = NULL; +static wolfSSH_Free_cb free_function = NULL; +static wolfSSH_Realloc_cb realloc_function = NULL; + + +/* set wolfSSH allocator callbacks, 0 on success */ +int wolfSSH_SetAllocators(wolfSSH_Malloc_cb mf, + wolfSSH_Free_cb ff, + wolfSSH_Realloc_cb rf) +{ + int res = 0; + + if (mf) + malloc_function = mf; + else + res = WS_BAD_ARGUMENT; + + if (ff) + free_function = ff; + else + res = WS_BAD_ARGUMENT; + + if (rf) + realloc_function = rf; + else + res = WS_BAD_ARGUMENT; + + return res; +} + + +/* our malloc handler */ +void* wolfSSH_Malloc(size_t size) +{ + void* res = 0; + + if (malloc_function) + res = malloc_function(size); + else + res = malloc(size); + + return res; +} + + +/* our free handler */ +void wolfSSH_Free(void *ptr) +{ + if (free_function) + free_function(ptr); + else + free(ptr); +} + + +/* our realloc handler */ +void* wolfSSH_Realloc(void *ptr, size_t size) +{ + void* res = 0; + + if (realloc_function) + res = realloc_function(ptr, size); + else + res = realloc(ptr, size); + + return res; +} + +#endif /* USE_WOLFSSH_MEMORY */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/misc.c Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,118 @@ +/* misc.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 misc module contains inline functions. This file is either included + * into source files or built separately depending on the inline configure + * option. + */ + + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + + +#include <wolfssh/settings.h> + + +#ifndef WOLFSSH_MISC_C +#define WOLFSSH_MISC_C + + +#include <wolfssh/misc.h> + + +#ifdef NO_INLINE + #define STATIC +#else + #define STATIC static +#endif + + +/* Check for if compiling misc.c when not needed. */ +#if !defined(WOLFSSH_MISC_INCLUDED) && !defined(NO_INLINE) + #define MISC_WARNING "misc.c does not need to be compiled when using inline (NO_INLINE not defined))" + + #ifndef _MSC_VER + #warning MISC_WARNING + #else + #pragma message("warning: " MISC_WARNING) + #endif + +#else /* !WOLFSSL_MISC_INCLUDED && !NO_INLINE */ + + +#ifndef min +STATIC INLINE word32 min(word32 a, word32 b) +{ + return a > b ? b : a; +} +#endif /* min */ + + +/* convert opaque to 32 bit integer */ +STATIC INLINE void ato32(const byte* c, word32* u32) +{ + *u32 = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; +} + + +/* convert 32 bit integer to opaque */ +STATIC INLINE void c32toa(word32 u32, byte* c) +{ + c[0] = (u32 >> 24) & 0xff; + c[1] = (u32 >> 16) & 0xff; + c[2] = (u32 >> 8) & 0xff; + c[3] = u32 & 0xff; +} + + +/* Make sure compiler doesn't skip */ +STATIC INLINE void ForceZero(const void* mem, word32 length) +{ + volatile byte* z = (volatile byte*)mem; + + while (length--) *z++ = 0; +} + + +/* check all length bytes for equality, return 0 on success */ +STATIC INLINE int ConstantCompare(const byte* a, const byte* b, + word32 length) +{ + word32 i; + word32 compareSum = 0; + + for (i = 0; i < length; i++) { + compareSum |= a[i] ^ b[i]; + } + + return compareSum; +} + + +#undef STATIC + + +#endif /* !WOLFSSL_MISC_INCLUDED && !NO_INLINE */ + + +#endif /* WOLFSSH_MISC_C */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/port.c Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,72 @@ +/* port.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 port module wraps standard C library functions with macros to + * cover portablility issues when building in environments that rename + * those functions. This module also provides local versions of some + * standard C library functions that are missing on some platforms. + */ + + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <stdio.h> +#include <wolfssh/port.h> + + +int wfopen(WFILE** f, const char* filename, const char* mode) +{ +#ifdef USE_WINDOWS_API + return fopen_s(f, filename, mode) != 0; +#else + if (f != NULL) { + *f = fopen(filename, mode); + return *f == NULL; + } + return 1; +#endif +} + + +#ifndef WSTRING_USER + +char* wstrnstr(const char* s1, const char* s2, unsigned int n) +{ + unsigned int s2_len = (unsigned int)WSTRLEN(s2); + + if (s2_len == 0) + return (char*)s1; + + while (n >= s2_len && s1[0]) { + if (s1[0] == s2[0]) + if (WMEMCMP(s1, s2, s2_len) == 0) + return (char*)s1; + s1++; + n--; + } + + return NULL; +} + +#endif /* WSTRING_USER */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ssh.c Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,859 @@ +/* ssh.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 ssh module contains the public API for wolfSSH. + */ + + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <wolfssh/ssh.h> +#include <wolfssh/internal.h> +#include <wolfssh/log.h> +#include <wolfcrypt/wc_port.h> +//#include "esp_log.h" + +#ifdef NO_INLINE + #include <wolfssh/misc.h> +#else + #define WOLFSSH_MISC_INCLUDED + #include "src/misc.c" +#endif + + +int wolfSSH_Init(void) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_Init()"); + if (wolfCrypt_Init() != 0) + ret = WS_CRYPTO_FAILED; + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_Init(), returning %d", ret); + return ret; +} + + +int wolfSSH_Cleanup(void) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_Cleanup()"); + + if (wolfCrypt_Cleanup() != 0) + ret = WS_CRYPTO_FAILED; + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_Cleanup(), returning %d", ret); + return ret; +} + + +WOLFSSH_CTX* wolfSSH_CTX_new(byte side, void* heap) +{ + WOLFSSH_CTX* ctx; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_new()"); + + if (side != WOLFSSH_ENDPOINT_SERVER && side != WOLFSSH_ENDPOINT_CLIENT) { + WLOG(WS_LOG_DEBUG, "Invalid endpoint type"); + return NULL; + } + + ctx = (WOLFSSH_CTX*)WMALLOC(sizeof(WOLFSSH_CTX), heap, DYNTYPE_CTX); + ctx = CtxInit(ctx, side, heap); + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_CTX_new(), ctx = %p", ctx); + + return ctx; +} + + +void wolfSSH_CTX_free(WOLFSSH_CTX* ctx) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_free()"); + + if (ctx) { + CtxResourceFree(ctx); + WFREE(ctx, ctx->heap, DYNTYPE_CTX); + } +} + + +WOLFSSH* wolfSSH_new(WOLFSSH_CTX* ctx) +{ + WOLFSSH* ssh; + void* heap = NULL; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_new()"); + //ESP_LOGI("WOLFSSH", "Entering wolfSSH_new()"); + + if (ctx) + heap = ctx->heap; + else { + WLOG(WS_LOG_ERROR, "Trying to init a wolfSSH w/o wolfSSH_CTX"); + return NULL; + } + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_new()"); + //ESP_LOGI("WOLFSSH", "ctx check ok, now malloc WOLFSSH"); + + ssh = (WOLFSSH*)WMALLOC(sizeof(WOLFSSH), heap, DYNTYPE_SSH); + //ESP_LOGI("WOLFSSH", "malloc done, SSH Initialization..."); + //if(ssh == NULL) ESP_LOGE("WOLFSSH", "ssh is NULL, passing to SshInit() anyway..."); + ssh = SshInit(ssh, ctx); + //ESP_LOGI("WOLFSSH", "SshInit OK"); + //if(ssh == NULL) ESP_LOGE("WOLFSSH", "ssh is NULL after SshInit()"); + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_new(), ssh = %p", ssh); + //ESP_LOGI("WOLFSSH", "Leaving wolfSSH_new()"); + + return ssh; +} + + +void wolfSSH_free(WOLFSSH* ssh) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_free()"); + + if (ssh) { + void* heap = ssh->ctx ? ssh->ctx->heap : NULL; + SshResourceFree(ssh, heap); + WFREE(ssh, heap, DYNTYPE_SSH); + } +} + + +int wolfSSH_set_fd(WOLFSSH* ssh, int fd) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_set_fd()"); + + if (ssh) { + ssh->rfd = fd; + ssh->wfd = fd; + + ssh->ioReadCtx = &ssh->rfd; + ssh->ioWriteCtx = &ssh->wfd; + + return WS_SUCCESS; + } + return WS_BAD_ARGUMENT; +} + + +int wolfSSH_get_fd(const WOLFSSH* ssh) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_get_fd()"); + + if (ssh) + return ssh->rfd; + + return WS_BAD_ARGUMENT; +} + + +int wolfSSH_SetHighwater(WOLFSSH* ssh, word32 highwater) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetHighwater()"); + + if (ssh) { + ssh->highwaterMark = highwater; + + return WS_SUCCESS; + } + + return WS_BAD_ARGUMENT; +} + + +word32 wolfSSH_GetHighwater(WOLFSSH* ssh) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetHighwater()"); + + if (ssh) + return ssh->highwaterMark; + + return 0; +} + + +void wolfSSH_SetHighwaterCb(WOLFSSH_CTX* ctx, word32 highwater, + WS_CallbackHighwater cb) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetHighwaterCb()"); + + if (ctx) { + ctx->highwaterMark = highwater; + ctx->highwaterCb = cb; + } +} + + +void wolfSSH_SetHighwaterCtx(WOLFSSH* ssh, void* ctx) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetHighwaterCtx()"); + + if (ssh) + ssh->highwaterCtx = ctx; +} + + +void* wolfSSH_GetHighwaterCtx(WOLFSSH* ssh) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetHighwaterCtx()"); + + if (ssh) + return ssh->highwaterCtx; + + return NULL; +} + + +int wolfSSH_get_error(const WOLFSSH* ssh) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_get_error()"); + + if (ssh) + return ssh->error; + + return WS_BAD_ARGUMENT; +} + + +const char* wolfSSH_get_error_name(const WOLFSSH* ssh) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_get_error_name()"); + + if (ssh) + return GetErrorString(ssh->error); + + return NULL; +} + + +const char acceptError[] = "accept error: %s, %d"; +const char acceptState[] = "accept state: %s"; + + +int wolfSSH_accept(WOLFSSH* ssh) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_accept()"); + //ESP_LOGI("WOLFSSH", "Entering wolfSSH_accept()"); + + if (ssh == NULL) + return WS_BAD_ARGUMENT; + + //ESP_LOGI("WOLFSSH", "ssh is not NULL"); + + switch (ssh->acceptState) { + + case ACCEPT_BEGIN: + //ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_BEGIN"); + if ( (ssh->error = SendProtoId(ssh)) < WS_SUCCESS) { + while(ssh->error == WS_WANT_READ) { + // non-blocking tcp + //ESP_LOGW("WOLFSSH", "WANT_READ: looping"); + ssh->error = DoReceive(ssh); + } + if(ssh->error == WS_SUCCESS) break; + WLOG(WS_LOG_DEBUG, acceptError, "BEGIN", ssh->error); + return WS_FATAL_ERROR; + } + ssh->acceptState = ACCEPT_SERVER_VERSION_SENT; + WLOG(WS_LOG_DEBUG, acceptState, "SERVER_VERSION_SENT"); + FALL_THROUGH; + + case ACCEPT_SERVER_VERSION_SENT: + //ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_SERVER_VERSION_SENT"); + while (ssh->clientState < CLIENT_VERSION_DONE) { + if ( (ssh->error = DoProtoId(ssh)) < WS_SUCCESS) { + while(ssh->error == WS_WANT_READ) { + // non-blocking tcp + //ESP_LOGW("WOLFSSH", "WANT_READ: looping"); + ssh->error = DoReceive(ssh); + } + if(ssh->error == WS_SUCCESS) break; + WLOG(WS_LOG_DEBUG, acceptError, + "SERVER_VERSION_SENT", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->acceptState = ACCEPT_CLIENT_VERSION_DONE; + WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_VERSION_DONE"); + FALL_THROUGH; + + case ACCEPT_CLIENT_VERSION_DONE: + //ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_CLIENT_VERSION_DONE"); + if ( (ssh->error = SendKexInit(ssh)) < WS_SUCCESS) { + while(ssh->error == WS_WANT_READ) { + // non-blocking tcp + //ESP_LOGW("WOLFSSH", "WANT_READ: looping"); + ssh->error = DoReceive(ssh); + } + if(ssh->error == WS_SUCCESS) break; + WLOG(WS_LOG_DEBUG, acceptError, + "CLIENT_VERSION_DONE", ssh->error); + return WS_FATAL_ERROR; + } + ssh->acceptState = ACCEPT_SERVER_KEXINIT_SENT; + WLOG(WS_LOG_DEBUG, acceptState, "SERVER_KEXINIT_SENT"); + FALL_THROUGH; + + case ACCEPT_SERVER_KEXINIT_SENT: + //ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_SERVER_KEXINIT_SENT"); + while (ssh->isKeying) { + if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) { + while(ssh->error == WS_WANT_READ) { + // non-blocking tcp + //ESP_LOGW("WOLFSSH", "WANT_READ: looping"); + ssh->error = DoReceive(ssh); + } + if(ssh->error == WS_SUCCESS) break; + WLOG(WS_LOG_DEBUG, acceptError, + "SERVER_KEXINIT_SENT", ssh->error); + //ESP_LOGE("WOLFSSH", "Accept error: SERVER_KEXINIT_SENT, %d", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->acceptState = ACCEPT_KEYED; + WLOG(WS_LOG_DEBUG, acceptState, "KEYED"); + FALL_THROUGH; + + case ACCEPT_KEYED: + //ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_KEYED"); + while (ssh->clientState < CLIENT_USERAUTH_REQUEST_DONE) { + if ( (ssh->error = DoReceive(ssh)) < 0) { + while(ssh->error == WS_WANT_READ) { + // non-blocking tcp + //ESP_LOGW("WOLFSSH", "WANT_READ: looping"); + ssh->error = DoReceive(ssh); + } + if(ssh->error == WS_SUCCESS) break; + WLOG(WS_LOG_DEBUG, acceptError, + "KEYED", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->acceptState = ACCEPT_CLIENT_USERAUTH_REQUEST_DONE; + WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_USERAUTH_REQUEST_DONE"); + FALL_THROUGH; + + case ACCEPT_CLIENT_USERAUTH_REQUEST_DONE: + //ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_CLIENT_USERAUTH_REQUEST_DONE"); + if ( (ssh->error = SendServiceAccept(ssh, ID_SERVICE_USERAUTH)) < + WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, acceptError, + "CLIENT_USERAUTH_REQUEST_DONE", ssh->error); + return WS_FATAL_ERROR; + } + ssh->acceptState = ACCEPT_SERVER_USERAUTH_ACCEPT_SENT; + WLOG(WS_LOG_DEBUG, acceptState, + "ACCEPT_SERVER_USERAUTH_ACCEPT_SENT"); + FALL_THROUGH; + + case ACCEPT_SERVER_USERAUTH_ACCEPT_SENT: + //ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_SERVER_USERAUTH_ACCEPT_SENT"); + while (ssh->clientState < CLIENT_USERAUTH_DONE) { + if ( (ssh->error = DoReceive(ssh)) < 0) { + while(ssh->error == WS_WANT_READ) { + // non-blocking tcp + //ESP_LOGW("WOLFSSH", "WANT_READ: looping"); + ssh->error = DoReceive(ssh); + } + if(ssh->error == WS_SUCCESS) break; + WLOG(WS_LOG_DEBUG, acceptError, + "SERVER_USERAUTH_ACCEPT_SENT", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->acceptState = ACCEPT_CLIENT_USERAUTH_DONE; + WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_USERAUTH_DONE"); + FALL_THROUGH; + + case ACCEPT_CLIENT_USERAUTH_DONE: + //ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_CLIENT_USERAUTH_DONE"); + if ( (ssh->error = SendUserAuthSuccess(ssh)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, acceptError, + "CLIENT_USERAUTH_DONE", ssh->error); + return WS_FATAL_ERROR; + } + ssh->acceptState = ACCEPT_SERVER_USERAUTH_SENT; + WLOG(WS_LOG_DEBUG, acceptState, "SERVER_USERAUTH_SENT"); + FALL_THROUGH; + + case ACCEPT_SERVER_USERAUTH_SENT: + //ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_SERVER_USERAUTH_SENT"); + while (ssh->clientState < CLIENT_DONE) { + if ( (ssh->error = DoReceive(ssh)) < 0) { + while(ssh->error == WS_WANT_READ) { + // non-blocking tcp + //ESP_LOGW("WOLFSSH", "WANT_READ: looping"); + ssh->error = DoReceive(ssh); + } + if(ssh->error == WS_SUCCESS) break; + WLOG(WS_LOG_DEBUG, acceptError, + "SERVER_USERAUTH_SENT", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->acceptState = ACCEPT_CLIENT_CHANNEL_REQUEST_DONE; + WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_CHANNEL_REQUEST_DONE"); + FALL_THROUGH; + + case ACCEPT_CLIENT_CHANNEL_REQUEST_DONE: + //ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_CLIENT_CHANNEL_REQUEST_DONE"); + if ( (ssh->error = SendChannelOpenConf(ssh)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, acceptError, + "CLIENT_CHANNEL_REQUEST_DONE", ssh->error); + //ESP_LOGE("WOLFSSH", "SendChannelOpenConf failed: %d", ssh->error); + return WS_FATAL_ERROR; + } + ssh->acceptState = ACCEPT_SERVER_CHANNEL_ACCEPT_SENT; + WLOG(WS_LOG_DEBUG, acceptState, "SERVER_CHANNEL_ACCEPT_SENT"); + } + + return WS_SUCCESS; +} + + +const char connectError[] = "connect error: %s, %d"; +const char connectState[] = "connect state: %s"; + + +int wolfSSH_connect(WOLFSSH* ssh) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_connect()"); + + if (ssh == NULL) + return WS_BAD_ARGUMENT; + + switch (ssh->connectState) { + + case CONNECT_BEGIN: + if ( (ssh->error = SendProtoId(ssh)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, "BEGIN", ssh->error); + return WS_FATAL_ERROR; + } + ssh->connectState = CONNECT_CLIENT_VERSION_SENT; + WLOG(WS_LOG_DEBUG, connectState, "CLIENT_VERSION_SENT"); + FALL_THROUGH; + + case CONNECT_CLIENT_VERSION_SENT: + while (ssh->serverState < SERVER_VERSION_DONE) { + if ( (ssh->error = DoProtoId(ssh)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, + "CLIENT_VERSION_SENT", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->connectState = CONNECT_SERVER_VERSION_DONE; + WLOG(WS_LOG_DEBUG, connectState, "SERVER_VERSION_DONE"); + FALL_THROUGH; + + case CONNECT_SERVER_VERSION_DONE: + if ( (ssh->error = SendKexInit(ssh)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, acceptError, + "SERVER_VERSION_DONE", ssh->error); + return WS_FATAL_ERROR; + } + ssh->connectState = CONNECT_CLIENT_KEXINIT_SENT; + WLOG(WS_LOG_DEBUG, connectState, "CLIENT_KEXINIT_SENT"); + FALL_THROUGH; + + case CONNECT_CLIENT_KEXINIT_SENT: + while (ssh->serverState < SERVER_KEXINIT_DONE) { + if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, + "CLIENT_KEXINIT_SENT", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->connectState = CONNECT_SERVER_KEXINIT_DONE; + WLOG(WS_LOG_DEBUG, connectState, "SERVER_KEXINIT_DONE"); + FALL_THROUGH; + + case CONNECT_SERVER_KEXINIT_DONE: + if (ssh->handshake->kexId == ID_DH_GEX_SHA256) + ssh->error = SendKexDhGexRequest(ssh); + else + ssh->error = SendKexDhInit(ssh); + if (ssh->error < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, + "SERVER_KEXINIT_DONE", ssh->error); + return WS_FATAL_ERROR; + } + ssh->connectState = CONNECT_CLIENT_KEXDH_INIT_SENT; + WLOG(WS_LOG_DEBUG, connectState, "CLIENT_KEXDH_INIT_SENT"); + FALL_THROUGH; + + case CONNECT_CLIENT_KEXDH_INIT_SENT: + while (ssh->isKeying) { + if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, + "CLIENT_KEXDH_INIT_SENT", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->connectState = CONNECT_KEYED; + WLOG(WS_LOG_DEBUG, connectState, "KEYED"); + FALL_THROUGH; + + case CONNECT_KEYED: + if ( (ssh->error = SendServiceRequest(ssh, ID_SERVICE_USERAUTH)) < + WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, "KEYED", ssh->error); + return WS_FATAL_ERROR; + } + ssh->connectState = CONNECT_CLIENT_USERAUTH_REQUEST_SENT; + WLOG(WS_LOG_DEBUG, connectState, "CLIENT_USERAUTH_REQUEST_SENT"); + FALL_THROUGH; + + case CONNECT_CLIENT_USERAUTH_REQUEST_SENT: + while (ssh->serverState < SERVER_USERAUTH_REQUEST_DONE) { + if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, + "CLIENT_USERAUTH_REQUEST_SENT", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->connectState = CONNECT_SERVER_USERAUTH_REQUEST_DONE; + WLOG(WS_LOG_DEBUG, connectState, "SERVER_USERAUTH_REQUEST_DONE"); + FALL_THROUGH; + + case CONNECT_SERVER_USERAUTH_REQUEST_DONE: + if ( (ssh->error = SendUserAuthRequest(ssh, ID_NONE)) < + WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, + "SERVER_USERAUTH_REQUEST_DONE", ssh->error); + return WS_FATAL_ERROR; + } + ssh->connectState = CONNECT_CLIENT_USERAUTH_SENT; + WLOG(WS_LOG_DEBUG, connectState, "CLIENT_USERAUTH_SENT"); + FALL_THROUGH; + + case CONNECT_CLIENT_USERAUTH_SENT: + while (ssh->serverState < SERVER_USERAUTH_ACCEPT_DONE) { + if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, + "CLIENT_USERAUTH_SENT", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->connectState = CONNECT_SERVER_USERAUTH_ACCEPT_DONE; + WLOG(WS_LOG_DEBUG, connectState, "SERVER_USERAUTH_ACCEPT_DONE"); + FALL_THROUGH; + + case CONNECT_SERVER_USERAUTH_ACCEPT_DONE: + if ( (ssh->error = SendChannelOpenSession(ssh, DEFAULT_WINDOW_SZ, + DEFAULT_MAX_PACKET_SZ)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, + "SERVER_USERAUTH_ACCEPT_DONE", ssh->error); + return WS_FATAL_ERROR; + } + ssh->connectState = CONNECT_CLIENT_CHANNEL_OPEN_SESSION_SENT; + WLOG(WS_LOG_DEBUG, connectState, + "CLIENT_CHANNEL_OPEN_SESSION_SENT"); + FALL_THROUGH; + + case CONNECT_CLIENT_CHANNEL_OPEN_SESSION_SENT: + while (ssh->serverState < SERVER_CHANNEL_OPEN_DONE) { + if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, + "CLIENT_CHANNEL_OPEN_SESSION_SENT", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->connectState = CONNECT_SERVER_CHANNEL_OPEN_SESSION_DONE; + WLOG(WS_LOG_DEBUG, connectState, + "SERVER_CHANNEL_OPEN_SESSION_DONE"); + FALL_THROUGH; + + case CONNECT_SERVER_CHANNEL_OPEN_SESSION_DONE: + if ( (ssh->error = SendChannelRequestShell(ssh)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, + "SERVER_CHANNEL_OPEN_SESSION_DONE", ssh->error); + return WS_FATAL_ERROR; + } + ssh->connectState = CONNECT_CLIENT_CHANNEL_REQUEST_SHELL_SENT; + WLOG(WS_LOG_DEBUG, connectState, + "CLIENT_CHANNEL_REQUEST_SHELL_SENT"); + FALL_THROUGH; + + case CONNECT_CLIENT_CHANNEL_REQUEST_SHELL_SENT: + while (ssh->serverState < SERVER_DONE) { + if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, connectError, + "CLIENT_CHANNEL_REQUEST_SHELL_SENT", ssh->error); + return WS_FATAL_ERROR; + } + } + ssh->connectState = CONNECT_SERVER_CHANNEL_REQUEST_SHELL_DONE; + WLOG(WS_LOG_DEBUG, connectState, + "SERVER_CHANNEL_REQUEST_SHELL_DONE"); + } + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_connect()"); + return WS_SUCCESS; +} + + +int wolfSSH_shutdown(WOLFSSH* ssh) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_shutdown()"); + + if (ssh == NULL) + ret = WS_BAD_ARGUMENT; + + if (ret == WS_SUCCESS) + ret = SendChannelEof(ssh, 0); + + if (ret == WS_SUCCESS) + ret = SendChannelClose(ssh, 0); + + if (ret == WS_SUCCESS) + ret = SendDisconnect(ssh, WOLFSSH_DISCONNECT_BY_APPLICATION); + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_shutdown(), ret = %d", ret); + return ret; +} + + +int wolfSSH_TriggerKeyExchange(WOLFSSH* ssh) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_TriggerKeyExchange()"); + if (ssh == NULL) + ret = WS_BAD_ARGUMENT; + + if (ret == WS_SUCCESS) + ret = SendKexInit(ssh); + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_TriggerKeyExchange(), ret = %d", ret); + return ret; +} + + +int wolfSSH_stream_read(WOLFSSH* ssh, byte* buf, word32 bufSz) +{ + Buffer* inputBuffer; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_stream_read()"); + //ESP_LOGI("WOLFSSH", "wolfSSH_stream_read()"); + + if (ssh == NULL || buf == NULL || bufSz == 0 || ssh->channelList == NULL) + return WS_BAD_ARGUMENT; + + inputBuffer = &ssh->channelList->inputBuffer; + + while (inputBuffer->length - inputBuffer->idx == 0) { + int ret = DoReceive(ssh); + if (ret < 0) { + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_read(), ret = %d", ret); + return ret; + } + } + //ESP_LOGI("WOLFSSH", "wolfSSH_stream_read: while loop done"); + + bufSz = min(bufSz, inputBuffer->length - inputBuffer->idx); + WMEMCPY(buf, inputBuffer->buffer + inputBuffer->idx, bufSz); + inputBuffer->idx += bufSz; + + if (!ssh->isKeying && (inputBuffer->length > inputBuffer->bufferSz / 2)) { + + word32 usedSz = inputBuffer->length - inputBuffer->idx; + word32 bytesToAdd = inputBuffer->idx; + int sendResult; + + WLOG(WS_LOG_DEBUG, "Making more room: %u", usedSz); + if (usedSz) { + WLOG(WS_LOG_DEBUG, " ...moving data down"); + WMEMMOVE(inputBuffer->buffer, + inputBuffer->buffer + bytesToAdd, usedSz); + } + + sendResult = SendChannelWindowAdjust(ssh, + ssh->channelList->peerChannel, + bytesToAdd); + if (sendResult != WS_SUCCESS) + bufSz = sendResult; + + WLOG(WS_LOG_INFO, " bytesToAdd = %u", bytesToAdd); + WLOG(WS_LOG_INFO, " windowSz = %u", ssh->channelList->windowSz); + ssh->channelList->windowSz += bytesToAdd; + WLOG(WS_LOG_INFO, " update windowSz = %u", ssh->channelList->windowSz); + + inputBuffer->length = usedSz; + inputBuffer->idx = 0; + } + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_read(), rxd = %d", bufSz); + return bufSz; +} + + +int wolfSSH_stream_send(WOLFSSH* ssh, byte* buf, word32 bufSz) +{ + int bytesTxd = 0; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_stream_send()"); + + if (ssh == NULL || buf == NULL || ssh->channelList == NULL) + return WS_BAD_ARGUMENT; + + bytesTxd = SendChannelData(ssh, ssh->channelList->peerChannel, buf, bufSz); + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_send(), txd = %d", bytesTxd); + return bytesTxd; +} + + +void wolfSSH_SetUserAuth(WOLFSSH_CTX* ctx, WS_CallbackUserAuth cb) +{ + if (ctx != NULL) { + ctx->userAuthCb = cb; + } +} + + +void wolfSSH_SetUserAuthCtx(WOLFSSH* ssh, void* userAuthCtx) +{ + if (ssh != NULL) { + ssh->userAuthCtx = userAuthCtx; + } +} + + +void* wolfSSH_GetUserAuthCtx(WOLFSSH* ssh) +{ + if (ssh != NULL) { + return ssh->userAuthCtx; + } + return NULL; +} + + +int wolfSSH_SetUsername(WOLFSSH* ssh, const char* username) +{ + char* value = NULL; + word32 valueSz; + int ret = WS_SUCCESS; + + if (ssh == NULL || ssh->handshake == NULL || + ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER || + username == NULL) { + + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + valueSz = (word32)WSTRLEN(username); + if (valueSz > 0) + value = (char*)WMALLOC(valueSz + 1, ssh->ctx->heap, DYNTYPE_STRING); + if (value == NULL) + ret = WS_MEMORY_E; + } + + if (ret == WS_SUCCESS) { + WSTRNCPY(value, username, valueSz + 1); + if (ssh->userName != NULL) { + WFREE(ssh->userName, heap, DYNTYPE_STRING); + ssh->userName = NULL; + } + ssh->userName = value; + ssh->userNameSz = valueSz; + } + + return ret; +} + + +int wolfSSH_CTX_SetBanner(WOLFSSH_CTX* ctx, + const char* newBanner) +{ + word32 newBannerSz = 0; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_SetBanner()"); + + if (ctx == NULL) + return WS_BAD_ARGUMENT; + + if (newBanner != NULL) { + WLOG(WS_LOG_INFO, " setting banner to: \"%s\"", newBanner); + newBannerSz = (word32)WSTRLEN(newBanner); + } + + ctx->banner = newBanner; + ctx->bannerSz = newBannerSz; + + return WS_SUCCESS; +} + + +int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX* ctx, + const byte* in, word32 inSz, int format) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_UsePrivateKey_buffer()"); + return wolfSSH_ProcessBuffer(ctx, in, inSz, format, BUFTYPE_PRIVKEY); +} + + +void wolfSSH_GetStats(WOLFSSH* ssh, word32* txCount, word32* rxCount, + word32* seq, word32* peerSeq) +{ + word32 rTxCount = 0; + word32 rRxCount = 0; + word32 rSeq = 0; + word32 rPeerSeq = 0; + + if (ssh != NULL) { + rTxCount = ssh->txCount; + rRxCount = ssh->rxCount; + rSeq = ssh->seq; + rPeerSeq = ssh->peerSeq; + } + + if (txCount != NULL) + *txCount = rTxCount; + if (rxCount != NULL) + *rxCount = rRxCount; + if (seq != NULL) + *seq = rSeq; + if (peerSeq != NULL) + *peerSeq = rPeerSeq; +} + + +int wolfSSH_KDF(byte hashId, byte keyId, + byte* key, word32 keySz, + const byte* k, word32 kSz, + const byte* h, word32 hSz, + const byte* sessionId, word32 sessionIdSz) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_KDF()"); + return GenerateKey(hashId, keyId, key, keySz, k, kSz, h, hSz, + sessionId, sessionIdSz); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/error.h Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,94 @@ +/* error.h + * + * 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 error header file provides the error codes used in the library. + */ + + +#pragma once + +#include <wolfssh/settings.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* add new error strings to error.c */ + + +/* main public return values */ +enum WS_ErrorCodes { + WS_SUCCESS = 0, /* function success */ + WS_FATAL_ERROR = -1, /* general function failure */ + WS_BAD_ARGUMENT = -2, /* bad function argument */ + WS_MEMORY_E = -3, /* memory allocation failure */ + WS_BUFFER_E = -4, /* input/output buffer size error */ + WS_PARSE_E = -5, /* general parsing error */ + WS_NOT_COMPILED = -6, /* feature not compiled in */ + WS_OVERFLOW_E = -7, /* would overflow if continued */ + WS_BAD_USAGE = -8, /* bad example usage */ + WS_SOCKET_ERROR_E = -9, + WS_WANT_READ = -10, + WS_WANT_WRITE = -11, + WS_RECV_OVERFLOW_E = -12, + WS_VERSION_E = -13, /* Peer using wrong version of SSH */ + WS_SEND_OOB_READ_E = -14, + WS_INPUT_CASE_E = -15, + WS_BAD_FILETYPE_E = -16, + WS_UNIMPLEMENTED_E = -17, + WS_RSA_E = -18, + WS_BAD_FILE_E = -19, + WS_INVALID_ALGO_ID = -20, + WS_DECRYPT_E = -21, + WS_ENCRYPT_E = -22, + WS_VERIFY_MAC_E = -23, + WS_CREATE_MAC_E = -24, + WS_RESOURCE_E = -25, /* insufficient resources for new channel */ + WS_INVALID_CHANTYPE = -26, /* invalid channel type */ + WS_INVALID_CHANID = -27, + WS_INVALID_USERNAME = -28, + WS_CRYPTO_FAILED = -29, /* crypto action failed */ + WS_INVALID_STATE_E = -30, + WS_INVALID_PRIME_CURVE = -32, + WS_ECC_E = -33, + WS_CHANOPEN_FAILED = -34, + WS_REKEYING = -90, /* Status: rekey in progress */ + WS_CHANNEL_CLOSED = -91 /* Status: channel closed */ +}; + + +/* I/O Callback default errors */ +enum WS_IOerrors { + WS_CBIO_ERR_GENERAL = -1, /* general unexpected err */ + WS_CBIO_ERR_WANT_READ = -2, /* need to call read again */ + WS_CBIO_ERR_WANT_WRITE = -2, /* need to call write again */ + WS_CBIO_ERR_CONN_RST = -3, /* connection reset */ + WS_CBIO_ERR_ISR = -4, /* interrupt */ + WS_CBIO_ERR_CONN_CLOSE = -5, /* connection closed or epipe */ + WS_CBIO_ERR_TIMEOUT = -6 /* socket timeout */ +}; + + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/internal.h Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,525 @@ +/* internal.h + * + * 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. + */ + + +#pragma once + +#include <wolfssh/ssh.h> +#include <wolfcrypt/hash.h> +#include <wolfcrypt/random.h> +#include <wolfcrypt/aes.h> +#include <wolfcrypt/dh.h> +#include <wolfcrypt/ecc.h> + + +#if !defined (ALIGN16) + #if defined (__GNUC__) + #define ALIGN16 __attribute__ ( (aligned (16))) + #elif defined(_MSC_VER) + /* disable align warning, we want alignment ! */ + #pragma warning(disable: 4324) + #define ALIGN16 __declspec (align (16)) + #else + #define ALIGN16 + #endif +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +WOLFSSH_LOCAL const char* GetErrorString(int); + + +enum { + /* Any of the items can be none. */ + ID_NONE, + + /* Encryption IDs */ + ID_AES128_CBC, + ID_AES128_GCM, + + /* Integrity IDs */ + ID_HMAC_SHA1, + ID_HMAC_SHA1_96, + ID_HMAC_SHA2_256, + + /* Key Exchange IDs */ + ID_DH_GROUP1_SHA1, + ID_DH_GROUP14_SHA1, + ID_DH_GEX_SHA256, + ID_ECDH_SHA2_NISTP256, + ID_ECDH_SHA2_NISTP384, + ID_ECDH_SHA2_NISTP521, + + /* Public Key IDs */ + ID_SSH_RSA, + ID_ECDSA_SHA2_NISTP256, + ID_ECDSA_SHA2_NISTP384, + ID_ECDSA_SHA2_NISTP521, + + /* Service IDs */ + ID_SERVICE_USERAUTH, + ID_SERVICE_CONNECTION, + + /* UserAuth IDs */ + ID_USERAUTH_PASSWORD, + ID_USERAUTH_PUBLICKEY, + + /* Channel Type IDs */ + ID_CHANTYPE_SESSION, + + ID_UNKNOWN +}; + + +#define MAX_ENCRYPTION 3 +#define MAX_INTEGRITY 2 +#define MAX_KEY_EXCHANGE 2 +#define MAX_PUBLIC_KEY 1 +#define MAX_HMAC_SZ SHA256_DIGEST_SIZE +#define MIN_BLOCK_SZ 8 +#define COOKIE_SZ 16 +#define LENGTH_SZ 4 +#define PAD_LENGTH_SZ 1 +#define MIN_PAD_LENGTH 4 +#define BOOLEAN_SZ 1 +#define MSG_ID_SZ 1 +#define SHA1_96_SZ 12 +#define UINT32_SZ 4 +#define SSH_PROTO_SZ 7 /* "SSH-2.0" */ +#define SSH_PROTO_EOL_SZ 2 /* Just the CRLF */ +#define AEAD_IMP_IV_SZ 4 +#define AEAD_EXP_IV_SZ 8 +#define AEAD_NONCE_SZ (AEAD_IMP_IV_SZ+AEAD_EXP_IV_SZ) +#ifndef DEFAULT_HIGHWATER_MARK + #define DEFAULT_HIGHWATER_MARK ((1024 * 1024 * 1024) - (32 * 1024)) +#endif +#ifndef DEFAULT_WINDOW_SZ + //#define DEFAULT_WINDOW_SZ (1024 * 1024) + #define DEFAULT_WINDOW_SZ (8 * 1024) // ESP32 Doesn't have that much RAM +#endif +#ifndef DEFAULT_MAX_PACKET_SZ + //#define DEFAULT_MAX_PACKET_SZ (16 * 1024) + #define DEFAULT_MAX_PACKET_SZ (2 * 1024) // ESP32 Doesn't have that much RAM +#endif +#ifndef DEFAULT_NEXT_CHANNEL + #define DEFAULT_NEXT_CHANNEL 0 +#endif + + +WOLFSSH_LOCAL byte NameToId(const char*, word32); +WOLFSSH_LOCAL const char* IdToName(byte); + + +#define STATIC_BUFFER_LEN AES_BLOCK_SIZE +/* This is one AES block size. We always grab one + * block size first to decrypt to find the size of + * the rest of the data. */ + + +typedef struct Buffer { + void* heap; /* Heap for allocations */ + word32 length; /* total buffer length used */ + word32 idx; /* idx to part of length already consumed */ + byte* buffer; /* place holder for actual buffer */ + word32 bufferSz; /* current buffer size */ + ALIGN16 byte staticBuffer[STATIC_BUFFER_LEN]; + byte dynamicFlag; /* dynamic memory currently in use */ +} Buffer; + + +WOLFSSH_LOCAL int BufferInit(Buffer*, word32, void*); +WOLFSSH_LOCAL int GrowBuffer(Buffer*, word32, word32); +WOLFSSH_LOCAL void ShrinkBuffer(Buffer* buf, int); + + +/* our wolfSSH Context */ +struct WOLFSSH_CTX { + void* heap; /* heap hint */ + WS_CallbackIORecv ioRecvCb; /* I/O Receive Callback */ + WS_CallbackIOSend ioSendCb; /* I/O Send Callback */ + WS_CallbackUserAuth userAuthCb; /* User Authentication Callback */ + WS_CallbackHighwater highwaterCb; /* Data Highwater Mark Callback */ + + byte* privateKey; /* Owned by CTX */ + word32 privateKeySz; + byte useEcc; /* Depends on the private key */ + word32 highwaterMark; + const char* banner; + word32 bannerSz; + byte side; /* client or server */ + byte showBanner; +}; + + +typedef struct Ciphers { + Aes aes; +} Ciphers; + + +typedef struct Keys { + byte iv[AES_BLOCK_SIZE]; + byte ivSz; + byte encKey[AES_BLOCK_SIZE]; + byte encKeySz; + byte macKey[MAX_HMAC_SZ]; + byte macKeySz; +} Keys; + + +typedef struct HandshakeInfo { + byte kexId; + byte pubKeyId; + byte encryptId; + byte macId; + byte hashId; + byte kexPacketFollows; + byte aeadMode; + + byte blockSz; + byte macSz; + + Keys keys; + Keys peerKeys; + wc_HashAlg hash; + byte e[257]; /* May have a leading zero for unsigned or is a Q_S value. */ + word32 eSz; + byte x[257]; /* May have a leading zero, for unsigned. */ + word32 xSz; + byte* kexInit; + word32 kexInitSz; + + word32 dhGexMinSz; + word32 dhGexPreferredSz; + word32 dhGexMaxSz; + byte* primeGroup; + word32 primeGroupSz; + byte* generator; + word32 generatorSz; + + byte useEcc; + union { + DhKey dh; + ecc_key ecc; + } privKey; +} HandshakeInfo; + + +/* our wolfSSH session */ +struct WOLFSSH { + WOLFSSH_CTX* ctx; /* owner context */ + int error; + int rfd; + int wfd; + void* ioReadCtx; /* I/O Read Context handle */ + void* ioWriteCtx; /* I/O Write Context handle */ + int rflags; /* optional read flags */ + int wflags; /* optional write flags */ + word32 txCount; + word32 rxCount; + word32 highwaterMark; + byte highwaterFlag; /* Set when highwater CB called */ + void* highwaterCtx; + word32 curSz; + word32 seq; + word32 peerSeq; + word32 packetStartIdx; /* Current send packet start index */ + byte paddingSz; /* Current send packet padding size */ + byte acceptState; + byte connectState; + byte clientState; + byte serverState; + byte processReplyState; + byte isKeying; + + byte connReset; + byte isClosed; + + byte blockSz; + byte encryptId; + byte macId; + byte macSz; + byte aeadMode; + byte peerBlockSz; + byte peerEncryptId; + byte peerMacId; + byte peerMacSz; + byte peerAeadMode; + + Ciphers encryptCipher; + Ciphers decryptCipher; + + word32 nextChannel; + WOLFSSH_CHANNEL* channelList; + word32 channelListSz; + word32 defaultPeerChannelId; + + Buffer inputBuffer; + Buffer outputBuffer; + WC_RNG* rng; + + byte h[WC_MAX_DIGEST_SIZE]; + word32 hSz; + byte k[257]; /* May have a leading zero, for unsigned. */ + word32 kSz; + byte sessionId[WC_MAX_DIGEST_SIZE]; + word32 sessionIdSz; + + Keys keys; + Keys peerKeys; + HandshakeInfo* handshake; + + void* userAuthCtx; + char* userName; + word32 userNameSz; + char* password; + word32 passwordSz; + byte* pkBlob; + word32 pkBlobSz; + byte* peerProtoId; /* Save for rekey */ + word32 peerProtoIdSz; +}; + + +struct WOLFSSH_CHANNEL { + byte channelType; + word32 channel; + word32 windowSz; + word32 maxPacketSz; + word32 peerChannel; + word32 peerWindowSz; + word32 peerMaxPacketSz; + Buffer inputBuffer; + struct WOLFSSH* ssh; + struct WOLFSSH_CHANNEL* next; +}; + + +WOLFSSH_LOCAL WOLFSSH_CTX* CtxInit(WOLFSSH_CTX*, byte, void*); +WOLFSSH_LOCAL void CtxResourceFree(WOLFSSH_CTX*); +WOLFSSH_LOCAL WOLFSSH* SshInit(WOLFSSH*, WOLFSSH_CTX*); +WOLFSSH_LOCAL void SshResourceFree(WOLFSSH*, void*); + +WOLFSSH_LOCAL WOLFSSH_CHANNEL* ChannelNew(WOLFSSH*, byte, word32, word32); +WOLFSSH_LOCAL int ChannelUpdate(WOLFSSH_CHANNEL*, word32, word32, word32); +WOLFSSH_LOCAL void ChannelDelete(WOLFSSH_CHANNEL*, void*); +WOLFSSH_LOCAL WOLFSSH_CHANNEL* ChannelFind(WOLFSSH*, word32, byte); +WOLFSSH_LOCAL int ChannelRemove(WOLFSSH*, word32, byte); +WOLFSSH_LOCAL int ChannelPutData(WOLFSSH_CHANNEL*, byte*, word32); +WOLFSSH_LOCAL int wolfSSH_ProcessBuffer(WOLFSSH_CTX*, + const byte*, word32, + int, int); + + +#ifndef WOLFSSH_USER_IO + +/* default I/O handlers */ +WOLFSSH_LOCAL int wsEmbedRecv(WOLFSSH*, void*, word32, void*); +WOLFSSH_LOCAL int wsEmbedSend(WOLFSSH*, void*, word32, void*); + +#endif /* WOLFSSH_USER_IO */ + + +WOLFSSH_LOCAL int DoReceive(WOLFSSH*); +WOLFSSH_LOCAL int DoProtoId(WOLFSSH*); +WOLFSSH_LOCAL int SendProtoId(WOLFSSH*); +WOLFSSH_LOCAL int SendKexInit(WOLFSSH*); +WOLFSSH_LOCAL int SendKexDhInit(WOLFSSH*); +WOLFSSH_LOCAL int SendKexDhReply(WOLFSSH*); +WOLFSSH_LOCAL int SendKexDhGexRequest(WOLFSSH*); +WOLFSSH_LOCAL int SendKexDhGexGroup(WOLFSSH*); +WOLFSSH_LOCAL int SendNewKeys(WOLFSSH*); +WOLFSSH_LOCAL int SendUnimplemented(WOLFSSH*); +WOLFSSH_LOCAL int SendDisconnect(WOLFSSH*, word32); +WOLFSSH_LOCAL int SendIgnore(WOLFSSH*, const unsigned char*, word32); +WOLFSSH_LOCAL int SendDebug(WOLFSSH*, byte, const char*); +WOLFSSH_LOCAL int SendServiceRequest(WOLFSSH*, byte); +WOLFSSH_LOCAL int SendServiceAccept(WOLFSSH*, byte); +WOLFSSH_LOCAL int SendUserAuthRequest(WOLFSSH*, byte); +WOLFSSH_LOCAL int SendUserAuthSuccess(WOLFSSH*); +WOLFSSH_LOCAL int SendUserAuthFailure(WOLFSSH*, byte); +WOLFSSH_LOCAL int SendUserAuthBanner(WOLFSSH*); +WOLFSSH_LOCAL int SendUserAuthPkOk(WOLFSSH*, const byte*, word32, + const byte*, word32); +WOLFSSH_LOCAL int SendRequestSuccess(WOLFSSH*, int); +WOLFSSH_LOCAL int SendChannelOpenSession(WOLFSSH*, word32, word32); +WOLFSSH_LOCAL int SendChannelOpenConf(WOLFSSH*); +WOLFSSH_LOCAL int SendChannelEof(WOLFSSH*, word32); +WOLFSSH_LOCAL int SendChannelClose(WOLFSSH*, word32); +WOLFSSH_LOCAL int SendChannelData(WOLFSSH*, word32, byte*, word32); +WOLFSSH_LOCAL int SendChannelWindowAdjust(WOLFSSH*, word32, word32); +WOLFSSH_LOCAL int SendChannelRequestShell(WOLFSSH*); +WOLFSSH_LOCAL int SendChannelSuccess(WOLFSSH*, word32, int); +WOLFSSH_LOCAL int GenerateKey(byte, byte, byte*, word32, const byte*, word32, + const byte*, word32, const byte*, word32); + + +enum AcceptStates { + ACCEPT_BEGIN = 0, + ACCEPT_SERVER_VERSION_SENT, + ACCEPT_CLIENT_VERSION_DONE, + ACCEPT_SERVER_KEXINIT_SENT, + ACCEPT_KEYED, + ACCEPT_CLIENT_USERAUTH_REQUEST_DONE, + ACCEPT_SERVER_USERAUTH_ACCEPT_SENT, + ACCEPT_CLIENT_USERAUTH_DONE, + ACCEPT_SERVER_USERAUTH_SENT, + ACCEPT_CLIENT_CHANNEL_REQUEST_DONE, + ACCEPT_SERVER_CHANNEL_ACCEPT_SENT +}; + + +enum ConnectStates { + CONNECT_BEGIN = 0, + CONNECT_CLIENT_VERSION_SENT, + CONNECT_SERVER_VERSION_DONE, + CONNECT_CLIENT_KEXINIT_SENT, + CONNECT_SERVER_KEXINIT_DONE, + CONNECT_CLIENT_KEXDH_INIT_SENT, + CONNECT_KEYED, + CONNECT_CLIENT_USERAUTH_REQUEST_SENT, + CONNECT_SERVER_USERAUTH_REQUEST_DONE, + CONNECT_CLIENT_USERAUTH_SENT, + CONNECT_SERVER_USERAUTH_ACCEPT_DONE, + CONNECT_CLIENT_CHANNEL_OPEN_SESSION_SENT, + CONNECT_SERVER_CHANNEL_OPEN_SESSION_DONE, + CONNECT_CLIENT_CHANNEL_REQUEST_SHELL_SENT, + CONNECT_SERVER_CHANNEL_REQUEST_SHELL_DONE +}; + + +enum ClientStates { + CLIENT_BEGIN = 0, + CLIENT_VERSION_DONE, + CLIENT_KEXINIT_DONE, + CLIENT_KEXDH_INIT_DONE, + CLIENT_USERAUTH_REQUEST_DONE, + CLIENT_USERAUTH_DONE, + CLIENT_DONE +}; + + +enum ServerStates { + SERVER_BEGIN = 0, + SERVER_VERSION_DONE, + SERVER_KEXINIT_DONE, + SERVER_USERAUTH_REQUEST_DONE, + SERVER_USERAUTH_ACCEPT_DONE, + SERVER_CHANNEL_OPEN_DONE, + SERVER_DONE +}; + + +enum ProcessReplyStates { + PROCESS_INIT, + PROCESS_PACKET_LENGTH, + PROCESS_PACKET_FINISH, + PROCESS_PACKET +}; + + +enum WS_MessageIds { + MSGID_DISCONNECT = 1, + MSGID_IGNORE = 2, + MSGID_UNIMPLEMENTED = 3, + MSGID_DEBUG = 4, + MSGID_SERVICE_REQUEST = 5, + MSGID_SERVICE_ACCEPT = 6, + + MSGID_KEXINIT = 20, + MSGID_NEWKEYS = 21, + + MSGID_KEXDH_INIT = 30, + MSGID_KEXECDH_INIT = 30, + + MSGID_KEXDH_REPLY = 31, + MSGID_KEXECDH_REPLY = 31, + MSGID_KEXDH_GEX_GROUP = 31, + MSGID_KEXDH_GEX_INIT = 32, + MSGID_KEXDH_GEX_REPLY = 33, + MSGID_KEXDH_GEX_REQUEST = 34, + + MSGID_USERAUTH_REQUEST = 50, + MSGID_USERAUTH_FAILURE = 51, + MSGID_USERAUTH_SUCCESS = 52, + MSGID_USERAUTH_BANNER = 53, + MSGID_USERAUTH_PK_OK = 60, /* Public Key OK */ + MSGID_USERAUTH_PW_CHRQ = 60, /* Password Change Request */ + + MSGID_GLOBAL_REQUEST = 80, + MSGID_REQUEST_SUCCESS = 81, + MSGID_REQUEST_FAILURE = 82, + + MSGID_CHANNEL_OPEN = 90, + MSGID_CHANNEL_OPEN_CONF = 91, + MSGID_CHANNEL_OPEN_FAIL = 92, + MSGID_CHANNEL_WINDOW_ADJUST = 93, + MSGID_CHANNEL_DATA = 94, + MSGID_CHANNEL_EOF = 96, + MSGID_CHANNEL_CLOSE = 97, + MSGID_CHANNEL_REQUEST = 98, + MSGID_CHANNEL_SUCCESS = 99, + MSGID_CHANNEL_FAILURE = 100 +}; + + +/* dynamic memory types */ +enum WS_DynamicTypes { + DYNTYPE_CTX, + DYNTYPE_SSH, + DYNTYPE_CHANNEL, + DYNTYPE_BUFFER, + DYNTYPE_ID, + DYNTYPE_HS, + DYNTYPE_CA, + DYNTYPE_CERT, + DYNTYPE_PRIVKEY, + DYNTYPE_PUBKEY, + DYNTYPE_DH, + DYNTYPE_RNG, + DYNTYPE_STRING, + DYNTYPE_MPINT +}; + + +enum WS_BufferTypes { + BUFTYPE_CA, + BUFTYPE_CERT, + BUFTYPE_PRIVKEY, + BUFTYPE_PUBKEY +}; + + +WOLFSSH_LOCAL void DumpOctetString(const byte*, word32); + + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/keygen.h Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,52 @@ +/* keygen.h + * + * 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 keygen module contains utility functions wrapping the wolfCrypt + * key generation functions to product SSH friendly keys. + */ + + +#pragma once + +#ifndef WOLFSSH_KEYGEN_H +#define WOLFSSH_KEYGEN_H + +#include <wolfssh/settings.h> +#include <wolfssh/port.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +#define WOLFSSH_RSAKEY_DEFAULT_SZ 2048 +#define WOLFSSH_RSAKEY_DEFAULT_E 65537 + + +WOLFSSH_API int wolfSSH_MakeRsaKey(byte*, word32, word32, word32); + + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/log.h Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,73 @@ +/* log.h + * + * 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 log module contains the interface to the logging function. When + * debugging is enabled and turned on, the logger will output to STDOUT. + * A custom logging callback may be installed. + */ + + +#pragma once + +#include <wolfssh/settings.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +enum wolfSSH_LogLevel { + WS_LOG_USER = 5, + WS_LOG_ERROR = 4, + WS_LOG_WARN = 3, + WS_LOG_INFO = 2, + WS_LOG_DEBUG = 1, + WS_LOG_DEFAULT = WS_LOG_DEBUG +}; + + +typedef void (*wolfSSH_LoggingCb)(enum wolfSSH_LogLevel, + const char *const logMsg); +WOLFSSH_API void wolfSSH_SetLoggingCb(wolfSSH_LoggingCb logF); +WOLFSSH_API int wolfSSH_LogEnabled(void); + + +#ifdef __GNUC__ + #define FMTCHECK __attribute__((format(printf,2,3))) +#else + #define FMTCHECK +#endif /* __GNUC__ */ + + +WOLFSSH_API void wolfSSH_Log(enum wolfSSH_LogLevel, + const char *const, ...) FMTCHECK; + +#define WLOG(...) do { \ + if (wolfSSH_LogEnabled()) \ + wolfSSH_Log(__VA_ARGS__); \ + } while (0) + + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/memory.h Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,56 @@ +/* memory.h + * + * 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 memory module contains the interface to the custom memory handling hooks. + */ + + +#pragma once + +#include <wolfssh/settings.h> +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef void *(*wolfSSH_Malloc_cb)(size_t size); +typedef void (*wolfSSH_Free_cb)(void *ptr); +typedef void *(*wolfSSH_Realloc_cb)(void *ptr, size_t size); + + +/* Public set function */ +WOLFSSH_API int wolfSSH_SetAllocators(wolfSSH_Malloc_cb malloc_function, + wolfSSH_Free_cb free_function, + wolfSSH_Realloc_cb realloc_function); + +/* Public in case user app wants to use WMALLOC/WFREE */ +WOLFSSH_API void* wolfSSH_Malloc(size_t size); +WOLFSSH_API void wolfSSH_Free(void *ptr); +WOLFSSH_API void* wolfSSH_Realloc(void *ptr, size_t size); + + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/misc.h Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,57 @@ +/* misc.h + * + * 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/>. + */ + +#pragma once + +#ifndef WOLFSSH_MISC_H +#define WOLFSSH_MISC_H + + +#ifdef __cplusplus + extern "C" { +#endif + + +#include <wolfssh/port.h> + + +#ifdef NO_INLINE + + +#ifndef min +WOLFSSH_LOCAL word32 min(word32, word32); +#endif /* min */ + +WOLFSSH_LOCAL void ato32(const byte*, word32*); +WOLFSSH_LOCAL void c32toa(word32, byte*); +WOLFSSH_LOCAL void ForceZero(const void*, word32); +WOLFSSH_LOCAL int ConstantCompare(const byte*, const byte*, word32); + + +#endif /* NO_INLINE */ + + +#ifdef __cplusplus + } /* extern "C" */ +#endif + + +#endif /* WOLFSSH_MISC_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/port.h Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,125 @@ +/* port.h + * + * 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 port module wraps standard C library functions with macros to + * cover portablility issues when building in environments that rename + * those functions. This module also provides local versions of some + * standard C library functions that are missing on some platforms. + */ + + +#pragma once + +#include <wolfssh/settings.h> +#include <stdio.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +/* setup memory handling */ +#ifndef WMALLOC_USER + #include <wolfssh/memory.h> + + #define WMALLOC(s, h, t) ((void)h, (void)t, wolfSSH_Malloc((s))) + #define WFREE(p, h, t) {void* xp = (p); if ((xp)) wolfSSH_Free((xp));} + #define WREALLOC(p, n, h, t) wolfSSH_Realloc((p), (n)) +#endif /* WMALLOC_USER */ + + +/* setup string handling */ +#ifndef WSTRING_USER + #include <string.h> + + #define WFILE FILE + + WOLFSSH_API char* wstrnstr(const char*, const char*, unsigned int); + WOLFSSH_API int wfopen(WFILE**, const char*, const char*); + + #define WMEMCPY(d,s,l) memcpy((d),(s),(l)) + #define WMEMSET(b,c,l) memset((b),(c),(l)) + #define WMEMCMP(s1,s2,n) memcmp((s1),(s2),(n)) + #define WMEMMOVE(d,s,l) memmove((d),(s),(l)) + + #define WSTRLEN(s1) strlen((s1)) + #define WSTRSTR(s1,s2) strstr((s1),(s2)) + #define WSTRNSTR(s1,s2,n) wstrnstr((s1),(s2),(n)) + #define WSTRNCMP(s1,s2,n) strncmp((s1),(s2),(n)) + #define WSTRNCAT(s1,s2,n) strncat((s1),(s2),(n)) + #define WSTRSPN(s1,s2) strspn((s1),(s2)) + #define WSTRCSPN(s1,s2) strcspn((s1),(s2)) + #define WFOPEN(f,fn,m) wfopen((f),(fn),(m)) + #define WFCLOSE(f) fclose(f) + + #ifndef USE_WINDOWS_API + #include <stdio.h> + #define WSTRNCPY(s1,s2,n) strncpy((s1),(s2),(n)) + #define WSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n)) + #define WSNPRINTF(s,n,f,...) snprintf((s),(n),(f),##__VA_ARGS__) + #define WVSNPRINTF(s,n,f,...) vsnprintf((s),(n),(f),##__VA_ARGS__) + #define WLOCALTIME(c,r) (localtime_r((c),(r))!=NULL) + #else + #define WSTRNCPY(s1,s2,n) strncpy_s((s1),(n),(s2),(n)) + #define WSTRNCASECMP(s1,s2,n) _strnicmp((s1),(s2),(n)) + #define WSNPRINTF(s,n,f,...) _snprintf_s((s),(n),(n),(f),##__VA_ARGS__) + #define WVSNPRINTF(s,n,f,...) vsnprintf_s((s),(n),(n),(f),##__VA_ARGS__) + #define WLOCALTIME(c,r) (localtime_s((r),(c))==0) + #endif +#endif /* WSTRING_USER */ + + +/* setup compiler inlining */ +#ifndef INLINE +#ifndef NO_INLINE + #ifdef _MSC_VER + #define INLINE __inline + #elif defined(__GNUC__) + #define INLINE inline + #elif defined(__IAR_SYSTEMS_ICC__) + #define INLINE inline + #elif defined(THREADX) + #define INLINE _Inline + #else + #define INLINE + #endif +#else + #define INLINE +#endif +#endif /* INLINE */ + + +/* GCC 7 has new switch() fall-through detection */ +#if defined(__GNUC__) + #if ((__GNUC__ > 7) || ((__GNUC__ == 7) && (__GNUC_MINOR__ >= 1))) + #define FALL_THROUGH __attribute__ ((fallthrough)); + #endif +#endif +#ifndef FALL_THROUGH + #define FALL_THROUGH +#endif + + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/settings.h Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,52 @@ +/* settings.h + * + * 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 settings header provides a method for developers to provide the + * internal preprocessor defines that the configure script provides to + * environments that do not use the configure script. + */ + + +#pragma once + +#include <wolfssh/visibility.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +/* user settings here */ +#ifndef WMALLOC_USER + #define USE_WOLFSSH_MEMORY /* default memory handlers */ +#endif /* WMALLOC_USER */ + + +#if defined (_WIN32) + #define USE_WINDOWS_API +#endif + + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/ssh.h Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,205 @@ +/* ssh.h + * + * 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 ssh module contains the public API for wolfSSH. + */ + + +#pragma once + +#include <wolfssl/options.h> +#include <wolfcrypt/types.h> +#include <wolfssh/settings.h> +#include <wolfssh/version.h> +#include <wolfssh/port.h> +#include <wolfssh/error.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct WOLFSSH_CTX WOLFSSH_CTX; +typedef struct WOLFSSH WOLFSSH; +typedef struct WOLFSSH_CHANNEL WOLFSSH_CHANNEL; + + +WOLFSSH_API int wolfSSH_Init(void); +WOLFSSH_API int wolfSSH_Cleanup(void); + +/* debugging output functions */ +WOLFSSH_API void wolfSSH_Debugging_ON(void); +WOLFSSH_API void wolfSSH_Debugging_OFF(void); + +/* context functions */ +WOLFSSH_API WOLFSSH_CTX* wolfSSH_CTX_new(byte, void*); +WOLFSSH_API void wolfSSH_CTX_free(WOLFSSH_CTX*); + +/* ssh session functions */ +WOLFSSH_API WOLFSSH* wolfSSH_new(WOLFSSH_CTX*); +WOLFSSH_API void wolfSSH_free(WOLFSSH*); + +WOLFSSH_API int wolfSSH_set_fd(WOLFSSH*, int); +WOLFSSH_API int wolfSSH_get_fd(const WOLFSSH*); + +/* data high water mark functions */ +WOLFSSH_API int wolfSSH_SetHighwater(WOLFSSH*, word32); +WOLFSSH_API word32 wolfSSH_GetHighwater(WOLFSSH*); + +typedef int (*WS_CallbackHighwater)(byte, void*); +WOLFSSH_API void wolfSSH_SetHighwaterCb(WOLFSSH_CTX*, word32, + WS_CallbackHighwater); +WOLFSSH_API void wolfSSH_SetHighwaterCtx(WOLFSSH*, void*); +WOLFSSH_API void* wolfSSH_GetHighwaterCtx(WOLFSSH*); + + +WOLFSSH_API int wolfSSH_get_error(const WOLFSSH*); +WOLFSSH_API const char* wolfSSH_get_error_name(const WOLFSSH*); + +/* I/O callbacks */ +typedef int (*WS_CallbackIORecv)(WOLFSSH*, void*, word32, void*); +typedef int (*WS_CallbackIOSend)(WOLFSSH*, void*, word32, void*); +WOLFSSH_API void wolfSSH_SetIORecv(WOLFSSH_CTX*, WS_CallbackIORecv); +WOLFSSH_API void wolfSSH_SetIOSend(WOLFSSH_CTX*, WS_CallbackIOSend); +WOLFSSH_API void wolfSSH_SetIOReadCtx(WOLFSSH*, void*); +WOLFSSH_API void wolfSSH_SetIOWriteCtx(WOLFSSH*, void*); +WOLFSSH_API void* wolfSSH_GetIOReadCtx(WOLFSSH*); +WOLFSSH_API void* wolfSSH_GetIOWriteCtx(WOLFSSH*); + +/* User Authentication callback */ +typedef struct WS_UserAuthData_Password { + byte* password; + word32 passwordSz; + /* The following are present for future use. */ + byte hasNewPassword; + byte* newPassword; + word32 newPasswordSz; +} WS_UserAuthData_Password; + +typedef struct WS_UserAuthData_PublicKey { + byte* dataToSign; + byte* publicKeyType; + word32 publicKeyTypeSz; + byte* publicKey; + word32 publicKeySz; + byte hasSignature; + byte* signature; + word32 signatureSz; +} WS_UserAuthData_PublicKey; + +typedef struct WS_UserAuthData { + byte type; + byte* username; + word32 usernameSz; + byte* serviceName; + word32 serviceNameSz; + byte* authName; + word32 authNameSz; + union { + WS_UserAuthData_Password password; + WS_UserAuthData_PublicKey publicKey; + } sf; +} WS_UserAuthData; + +typedef int (*WS_CallbackUserAuth)(byte, WS_UserAuthData*, void*); +WOLFSSH_API void wolfSSH_SetUserAuth(WOLFSSH_CTX*, WS_CallbackUserAuth); +WOLFSSH_API void wolfSSH_SetUserAuthCtx(WOLFSSH*, void*); +WOLFSSH_API void* wolfSSH_GetUserAuthCtx(WOLFSSH*); + +WOLFSSH_API int wolfSSH_SetUsername(WOLFSSH*, const char*); + +WOLFSSH_API int wolfSSH_CTX_SetBanner(WOLFSSH_CTX*, const char*); +WOLFSSH_API int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX*, + const byte*, word32, int); + +WOLFSSH_API int wolfSSH_accept(WOLFSSH*); +WOLFSSH_API int wolfSSH_connect(WOLFSSH*); +WOLFSSH_API int wolfSSH_shutdown(WOLFSSH*); +WOLFSSH_API int wolfSSH_stream_read(WOLFSSH*, byte*, word32); +WOLFSSH_API int wolfSSH_stream_send(WOLFSSH*, byte*, word32); +WOLFSSH_API int wolfSSH_TriggerKeyExchange(WOLFSSH*); + +WOLFSSH_API void wolfSSH_GetStats(WOLFSSH*, + word32*, word32*, word32*, word32*); + +WOLFSSH_API int wolfSSH_KDF(byte, byte, byte*, word32, const byte*, word32, + const byte*, word32, const byte*, word32); + + +enum WS_HighwaterSide { + WOLFSSH_HWSIDE_TRANSMIT, + WOLFSSH_HWSIDE_RECEIVE +}; + + +enum WS_EndpointTypes { + WOLFSSH_ENDPOINT_SERVER, + WOLFSSH_ENDPOINT_CLIENT +}; + + +enum WS_FormatTypes { + WOLFSSH_FORMAT_ASN1, + WOLFSSH_FORMAT_PEM, + WOLFSSH_FORMAT_RAW +}; + + +enum WS_UserAuthTypes { + WOLFSSH_USERAUTH_PASSWORD, + WOLFSSH_USERAUTH_PUBLICKEY +}; + + +enum WS_UserAuthResults { + WOLFSSH_USERAUTH_SUCCESS, + WOLFSSH_USERAUTH_FAILURE, + WOLFSSH_USERAUTH_INVALID_AUTHTYPE, + WOLFSSH_USERAUTH_INVALID_USER, + WOLFSSH_USERAUTH_INVALID_PASSWORD, + WOLFSSH_USERAUTH_INVALID_PUBLICKEY +}; + + +enum WS_DisconnectReasonCodes { + WOLFSSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1, + WOLFSSH_DISCONNECT_PROTOCOL_ERROR = 2, + WOLFSSH_DISCONNECT_KEY_EXCHANGE_FAILED = 3, + WOLFSSH_DISCONNECT_RESERVED = 4, + WOLFSSH_DISCONNECT_MAC_ERROR = 5, + WOLFSSH_DISCONNECT_COMPRESSION_ERROR = 6, + WOLFSSH_DISCONNECT_SERVICE_NOT_AVAILABLE = 7, + WOLFSSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8, + WOLFSSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9, + WOLFSSH_DISCONNECT_CONNECTION_LOST = 10, + WOLFSSH_DISCONNECT_BY_APPLICATION = 11, + WOLFSSH_DISCONNECT_TOO_MANY_CONNECTIONS = 12, + WOLFSSH_DISCONNECT_AUTH_CANCELLED_BY_USER = 13, + WOLFSSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14, + WOLFSSH_DISCONNECT_ILLEGAL_USER_NAME = 15 +}; + + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/test.h Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,471 @@ +/* test.h */ + +#pragma once + +#ifndef _WOLFSSH_TEST_H_ +#define _WOLFSSH_TEST_H_ + + +#include <stdio.h> +/*#include <stdlib.h>*/ +#include <ctype.h> +/*#include <wolfssh/error.h>*/ + +#ifdef USE_WINDOWS_API + #include <winsock2.h> + #include <process.h> + #include <assert.h> + #ifdef TEST_IPV6 /* don't require newer SDK for IPV4 */ + #include <ws2tcpip.h> + #include <wspiapi.h> + #endif + #define SOCKET_T SOCKET +#else /* USE_WINDOWS_API */ + #include <unistd.h> + #include <netdb.h> + #include <netinet/in.h> + //#include <netinet/tcp.h> + #include <arpa/inet.h> + #include <sys/ioctl.h> + #include <sys/socket.h> + #include <pthread.h> + #include <fcntl.h> + #ifndef SO_NOSIGPIPE // FIX: ESP32 + #include <signal.h> /* ignore SIGPIPE */ + #endif + #define SOCKET_T int +#endif /* USE_WINDOWS_API */ + + +/* Socket Handling */ +#ifndef WOLFSSH_SOCKET_INVALID +#ifdef USE_WINDOWS_API + #define WOLFSSH_SOCKET_INVALID ((SOCKET_T)INVALID_SOCKET) +#elif defined(WOLFSSH_TIRTOS) + #define WOLFSSH_SOCKET_INVALID ((SOCKET_T)-1) +#else + #define WOLFSSH_SOCKET_INVALID (SOCKET_T)(0) +#endif +#endif /* WOLFSSH_SOCKET_INVALID */ + +#ifndef WOLFSSL_SOCKET_IS_INVALID +#if defined(USE_WINDOWS_API) || defined(WOLFSSL_TIRTOS) + #define WOLFSSL_SOCKET_IS_INVALID(s) ((SOCKET_T)(s) == WOLFSSL_SOCKET_INVALID) +#else + #define WOLFSSL_SOCKET_IS_INVALID(s) ((SOCKET_T)(s) < WOLFSSL_SOCKET_INVALID) +#endif +#endif /* WOLFSSL_SOCKET_IS_INVALID */ + + +#if defined(__MACH__) || defined(USE_WINDOWS_API) + #ifndef _SOCKLEN_T + typedef int socklen_t; + #endif +#endif + + +#ifdef USE_WINDOWS_API + #define WCLOSESOCKET(s) closesocket(s) + #define WSTARTTCP() do { WSADATA wsd; WSAStartup(0x0002, &wsd); } while(0) +#else + #define WCLOSESOCKET(s) close(s) + #define WSTARTTCP() +#endif + + +#ifdef SINGLE_THREADED + typedef unsigned int THREAD_RETURN; + typedef void* THREAD_TYPE; + #define WOLFSSH_THREAD +#else + #if defined(_POSIX_THREADS) && !defined(__MINGW32__) + typedef void* THREAD_RETURN; + typedef pthread_t THREAD_TYPE; + #define WOLFSSH_THREAD + #define INFINITE -1 + #define WAIT_OBJECT_0 0L + #else + typedef unsigned int THREAD_RETURN; + typedef intptr_t THREAD_TYPE; + #define WOLFSSH_THREAD __stdcall + #endif +#endif + +#ifdef TEST_IPV6 + typedef struct sockaddr_in6 SOCKADDR_IN_T; + #define AF_INET_V AF_INET6 +#else + typedef struct sockaddr_in SOCKADDR_IN_T; + #define AF_INET_V AF_INET +#endif + + +#define serverKeyRsaPemFile "./keys/server-key-rsa.pem" + + +typedef struct tcp_ready { + word16 ready; /* predicate */ + word16 port; + char* srfName; /* server ready file name */ +#if defined(_POSIX_THREADS) && !defined(__MINGW32__) + pthread_mutex_t mutex; + pthread_cond_t cond; +#endif +} tcp_ready; + + +static INLINE void InitTcpReady(tcp_ready* ready) +{ + ready->ready = 0; + ready->port = 0; + ready->srfName = NULL; +#ifdef SINGLE_THREADED +#elif defined(_POSIX_THREADS) && !defined(__MINGW32__) + pthread_mutex_init(&ready->mutex, 0); + pthread_cond_init(&ready->cond, 0); +#endif +} + + +static INLINE void FreeTcpReady(tcp_ready* ready) +{ +#ifdef SINGLE_THREADED + (void)ready; +#elif defined(_POSIX_THREADS) && !defined(__MINGW32__) + pthread_mutex_destroy(&ready->mutex); + pthread_cond_destroy(&ready->cond); +#else + (void)ready; +#endif +} + + +typedef struct func_args { + int argc; + char** argv; + int return_code; + tcp_ready* signal; + WS_CallbackUserAuth user_auth; +} func_args; + + +typedef THREAD_RETURN WOLFSSH_THREAD THREAD_FUNC(void*); +void ThreadStart(THREAD_FUNC, void*, THREAD_TYPE*); +void ThreadJoin(THREAD_TYPE); +void ThreadDetach(THREAD_TYPE); +void WaitTcpReady(func_args*); + + +#ifndef TEST_IPV6 + static const char* const wolfSshIp = "127.0.0.1"; +#else /* TEST_IPV6 */ + static const char* const wolfSshIp = "::1"; +#endif /* TEST_IPV6 */ +static const word16 wolfSshPort = 22222; + + +#ifdef __GNUC__ + #define WS_NORETURN __attribute__((noreturn)) +#else + #define WS_NORETURN +#endif + +static INLINE WS_NORETURN void err_sys(const char* msg) +{ + printf("wolfSSH error: %s\n", msg); + +#ifndef __GNUC__ + /* scan-build (which pretends to be gnuc) can get confused and think the + * msg pointer can be null even when hardcoded and then it won't exit, + * making null pointer checks above the err_sys() call useless. + * We could just always exit() but some compilers will complain about no + * possible return, with gcc we know the attribute to handle that with + * WS_NORETURN. */ + if (msg) +#endif + { + exit(EXIT_FAILURE); + } +} + + +#define MY_EX_USAGE 2 + +extern int myoptind; +extern char* myoptarg; + +static INLINE int mygetopt(int argc, char** argv, const char* optstring) +{ + static char* next = NULL; + + char c; + char* cp; + + if (myoptind == 0) + next = NULL; /* we're starting new/over */ + + if (next == NULL || *next == '\0') { + if (myoptind == 0) + myoptind++; + + if (myoptind >= argc || argv[myoptind][0] != '-' || + argv[myoptind][1] == '\0') { + myoptarg = NULL; + if (myoptind < argc) + myoptarg = argv[myoptind]; + + return -1; + } + + if (strcmp(argv[myoptind], "--") == 0) { + myoptind++; + myoptarg = NULL; + + if (myoptind < argc) + myoptarg = argv[myoptind]; + + return -1; + } + + next = argv[myoptind]; + next++; /* skip - */ + myoptind++; + } + + c = *next++; + /* The C++ strchr can return a different value */ + cp = (char*)strchr(optstring, c); + + if (cp == NULL || c == ':') + return '?'; + + cp++; + + if (*cp == ':') { + if (*next != '\0') { + myoptarg = next; + next = NULL; + } + else if (myoptind < argc) { + myoptarg = argv[myoptind]; + myoptind++; + } + else + return '?'; + } + + return c; +} + + +#ifdef USE_WINDOWS_API + #pragma warning(push) + #pragma warning(disable:4996) + /* For Windows builds, disable compiler warnings for: + * - 4996: deprecated function */ +#endif + +static INLINE void build_addr(SOCKADDR_IN_T* addr, const char* peer, + word16 port) +{ + int useLookup = 0; + (void)useLookup; + + memset(addr, 0, sizeof(SOCKADDR_IN_T)); + +#ifndef TEST_IPV6 + /* peer could be in human readable form */ + if ( ((size_t)peer != INADDR_ANY) && isalpha((int)peer[0])) { + #ifdef CYASSL_MDK_ARM + int err; + struct hostent* entry = gethostbyname(peer, &err); + #else + struct hostent* entry = gethostbyname(peer); + #endif + + if (entry) { + memcpy(&addr->sin_addr.s_addr, entry->h_addr_list[0], + entry->h_length); + useLookup = 1; + } + else + err_sys("no entry for host"); + } +#endif + +#ifndef TEST_IPV6 + #if defined(CYASSL_MDK_ARM) + addr->sin_family = PF_INET; + #else + addr->sin_family = AF_INET_V; + #endif + addr->sin_port = htons(port); + if ((size_t)peer == INADDR_ANY) + addr->sin_addr.s_addr = INADDR_ANY; + else { + if (!useLookup) + addr->sin_addr.s_addr = inet_addr(peer); + } +#else + addr->sin6_family = AF_INET_V; + addr->sin6_port = htons(port); + if ((size_t)peer == INADDR_ANY) + addr->sin6_addr = in6addr_any; + else { + #ifdef HAVE_GETADDRINFO + struct addrinfo hints; + struct addrinfo* answer = NULL; + int ret; + char strPort[80]; + + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = AF_INET_V; + hints.ai_socktype = udp ? SOCK_DGRAM : SOCK_STREAM; + hints.ai_protocol = udp ? IPPROTO_UDP : IPPROTO_TCP; + + WSNPRINTF(strPort, sizeof(strPort), "%d", port); + strPort[79] = '\0'; + + ret = getaddrinfo(peer, strPort, &hints, &answer); + if (ret < 0 || answer == NULL) + err_sys("getaddrinfo failed"); + + memcpy(addr, answer->ai_addr, answer->ai_addrlen); + freeaddrinfo(answer); + #else + printf("no ipv6 getaddrinfo, loopback only tests/examples\n"); + addr->sin6_addr = in6addr_loopback; + #endif + } +#endif +} + +#ifdef USE_WINDOWS_API + #pragma warning(pop) +#endif + + +static INLINE void tcp_socket(SOCKET_T* sockFd) +{ + *sockFd = socket(AF_INET_V, SOCK_STREAM, 0); + +#ifdef USE_WINDOWS_API + if (*sockFd == INVALID_SOCKET) + err_sys("socket failed\n"); +#else + if (*sockFd < 0) + err_sys("socket failed\n"); +#endif + +#ifndef USE_WINDOWS_API +#ifdef SO_NOSIGPIPE + { + int on = 1; + socklen_t len = sizeof(on); + int res = setsockopt(*sockFd, SOL_SOCKET, SO_NOSIGPIPE, &on, len); + if (res < 0) + err_sys("setsockopt SO_NOSIGPIPE failed\n"); + } +#elif defined(CYASSL_MDK_ARM) + /* nothing to define */ +#else /* no S_NOSIGPIPE */ + // signal(SIGPIPE, SIG_IGN); // FIX: ESP32 +#endif /* S_NOSIGPIPE */ + +#if defined(TCP_NODELAY) + { + int on = 1; + socklen_t len = sizeof(on); + int res = setsockopt(*sockFd, IPPROTO_TCP, TCP_NODELAY, &on, len); + if (res < 0) + err_sys("setsockopt TCP_NODELAY failed\n"); + } +#endif +#endif /* USE_WINDOWS_API */ +} + + +#ifndef XNTOHS + #define XNTOHS(a) ntohs((a)) +#endif + + +static INLINE void tcp_listen(SOCKET_T* sockfd, word16* port, int useAnyAddr) +{ + SOCKADDR_IN_T addr; + + /* don't use INADDR_ANY by default, firewall may block, make user switch + on */ + build_addr(&addr, (useAnyAddr ? INADDR_ANY : wolfSshIp), *port); + tcp_socket(sockfd); + +#if !defined(USE_WINDOWS_API) && !defined(WOLFSSL_MDK_ARM)\ + && !defined(WOLFSSL_KEIL_TCP_NET) + { + int res, on = 1; + socklen_t len = sizeof(on); + res = setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, &on, len); + if (res < 0) + err_sys("setsockopt SO_REUSEADDR failed\n"); + } +#endif + + + if (bind(*sockfd, (const struct sockaddr*)&addr, sizeof(addr)) != 0) + err_sys("tcp bind failed"); + if (listen(*sockfd, 5) != 0) + err_sys("tcp listen failed"); + #if !defined(USE_WINDOWS_API) && !defined(WOLFSSL_TIRTOS) + if (*port == 0) { + socklen_t len = sizeof(addr); + if (getsockname(*sockfd, (struct sockaddr*)&addr, &len) == 0) { + #ifndef TEST_IPV6 + *port = XNTOHS(addr.sin_port); + #else + *port = XNTOHS(addr.sin6_port); + #endif + } + } + #endif +} + + +/* Wolf Root Directory Helper */ +/* KEIL-RL File System does not support relative directory */ +#if !defined(WOLFSSL_MDK_ARM) && !defined(WOLFSSL_KEIL_FS) && !defined(WOLFSSL_TIRTOS) + /* Maximum depth to search for WolfSSL root */ + #define MAX_WOLF_ROOT_DEPTH 5 + + static INLINE int ChangeToWolfSshRoot(void) + { + #if !defined(NO_FILESYSTEM) + int depth, res; + WFILE* file; + for(depth = 0; depth <= MAX_WOLF_ROOT_DEPTH; depth++) { + if (WFOPEN(&file, serverKeyRsaPemFile, "rb") == 0) { + WFCLOSE(file); + return depth; + } + #ifdef USE_WINDOWS_API + res = SetCurrentDirectoryA("..\\"); + #else + res = chdir("../"); + #endif + if (res < 0) { + printf("chdir to ../ failed!\n"); + break; + } + } + + err_sys("wolfSSH root not found"); + return -1; + #else + return 0; + #endif + } +#endif /* !defined(WOLFSSL_MDK_ARM) && !defined(WOLFSSL_KEIL_FS) && !defined(WOL +FSSL_TIRTOS) */ + + +#endif /* _WOLFSSH_TEST_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/version.h Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,42 @@ +/* version.h.in + * + * 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 version header provides the current version number to the code + * based and is updated automatically by the configure script. A copy + * of the last generated copy of version.h is included with the + * distribution for environments that do not use configure. + */ + + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define LIBWOLFSSH_VERSION_STRING "1.2.0" +#define LIBWOLFSSH_VERSION_HEX 0x01002000 + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/version.h.in Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,42 @@ +/* version.h.in + * + * 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 version header provides the current version number to the code + * based and is updated automatically by the configure script. A copy + * of the last generated copy of version.h is included with the + * distribution for environments that do not use configure. + */ + + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define LIBWOLFSSH_VERSION_STRING "@VERSION@" +#define LIBWOLFSSH_VERSION_HEX @HEX_VERSION@ + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfssh/visibility.h Mon Nov 25 14:24:05 2019 +0000 @@ -0,0 +1,79 @@ +/* visibility.h + * + * 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 visibility header handles the visibility of function prototypes + * between the local (used between modules in the library) and public + * (exported for the library user) APIs. + */ + + +#pragma once + + +#ifdef __cplusplus +extern "C" { +#endif + +/* WOLFSSH_API is used for the public API symbols. + It either imports or exports (or does nothing for static builds) + + WOLFSSH_LOCAL is used for non-API symbols (private). +*/ + +#if defined(BUILDING_WOLFSSH) + #if defined(HAVE_VISIBILITY) && HAVE_VISIBILITY + #define WOLFSSH_API __attribute__ ((visibility("default"))) + #define WOLFSSH_LOCAL __attribute__ ((visibility("hidden"))) + #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) + #define WOLFSSH_API __global + #define WOLFSSH_LOCAL __hidden + #elif defined(_MSC_VER) + #ifdef WOLFSSH_DLL + #define WOLFSSH_API extern __declspec(dllexport) + #else + #define WOLFSSH_API + #endif + #define WOLFSSH_LOCAL + #else + #define WOLFSSH_API + #define WOLFSSH_LOCAL + #endif /* HAVE_VISIBILITY */ +#else /* BUILDING_WOLFSSH */ + #if defined(_MSC_VER) + #ifdef WOLFSSH_DLL + #define WOLFSSH_API extern __declspec(dllimport) + #else + #define WOLFSSH_API + #endif + #define WOLFSSH_LOCAL + #else + #define WOLFSSH_API + #define WOLFSSH_LOCAL + #endif +#endif /* BUILDING_WOLFSSH */ + + + +#ifdef __cplusplus +} +#endif +