/* unit.c * * Copyright (C) 2014-2026 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 . */ #ifdef HAVE_CONFIG_H #include #endif #ifdef WOLFSSL_USER_SETTINGS #include #else #include #endif #include #include #include #include #include #include #include #include #define WOLFSSH_TEST_HEX2BIN #include #include "unit.h" /* Key Derivation Function (KDF) Unit Test */ typedef struct { byte hashId; byte keyId; const char* k; const char* h; const char* sessionId; const char* expectedKey; } KdfTestVector; #ifndef NO_SHA /** Test Vector Set #1: SHA-1 **/ const char kdfTvSet1k[] = "35618FD3AABF980A5F766408961600D4933C60DD7B22D69EEB4D7A987C938F6F" "7BB2E60E0F638BB4289297B588E6109057325F010D021DF60EBF8BE67AD9C3E2" "6376A326A16210C7AF07B3FE562B8DD1DCBECB17AA7BFAF38708B0136120B2FC" "723E93EF4237AC3737BAE3A16EC03F605C7EEABFD526B38C826B506BBAECD2F7" "9932F1371AEABFBEB4F8222313506677330C714A2A6FDC70CB859B581AA18625" "ECCB6BA9DDEEAECF0E41D9E5076B899B477112E59DDADC4B4D9C13E9F07E1107" "B560FEFDC146B8ED3E73441D05345031C35F9E6911B00319481D80015855BE4D" "1C7D7ACC8579B1CC2E5F714109C0882C3B57529ABDA1F2255D2B27C4A83AE11E"; const char kdfTvSet1h[] = "40555741F6DE70CDC4E740104A97E75473F49064"; const char kdfTvSet1sid[] = "40555741F6DE70CDC4E740104A97E75473F49064"; const char kdfTvSet1a[] = "B2EC4CF6943632C39972EE2801DC7393"; const char kdfTvSet1b[] = "BC92238B6FA69ECC10B2B013C2FC9785"; const char kdfTvSet1c[] = "9EF0E2053F66C56F3E4503DA1C2FBD6B"; const char kdfTvSet1d[] = "47C8395B08277020A0645DA3959FA1A9"; const char kdfTvSet1e[] = "EE436BFDABF9B0313224EC800E7390445E2F575E"; const char kdfTvSet1f[] = "FB9FDEEC78B0FB258F1A4F47F6BCE166680994BB"; /** Test Vector Set #2: SHA-1 **/ const char kdfTvSet2k[] = "19FA2B7C7F4FE7DE61CDE17468C792CCEAB0E3F2CE37CDE2DAA0974BCDFFEDD4" "A29415CDB330FA6A97ECA742359DC1223B581D8AC61B43CFFDF66D20952840B0" "2593B48354E352E2A396BDF7F1C9D414FD31C2BF47E6EED306069C4F4F5F66C3" "003A90E85412A1FBE89CDFB457CDA0D832E8DA701627366ADEC95B70E8A8B7BF" "3F85775CCF36E40631B83B32CF643088F01A82C97C5C3A820EB4149F551CAF8C" "C98EE6B3065E6152FF877823F7C618C1CD93CE26DB9FAAFED222F1C93E8F4068" "BFDA4480432E14F98FFC821F05647693040B07D71DC273121D53866294434D46" "0E95CFA4AB4414705BF1F8224655F907A418A6A893F2A71019225869CB7FE988"; const char kdfTvSet2h[] = "DFB748905CC8647684C3E0B7F26A3E8E7414AC51"; const char kdfTvSet2sid[] = "DFB748905CC8647684C3E0B7F26A3E8E7414AC51"; const char kdfTvSet2a[] = "52EDBFD5E414A3CC6C7F7A0F4EA60503"; const char kdfTvSet2b[] = "926C6987696C5FFCC6511BFE34557878"; const char kdfTvSet2c[] = "CB6D56EC5B9AFECD326D544DA2D22DED"; const char kdfTvSet2d[] = "F712F6451F1BD6CE9BAA597AC87C5A24"; const char kdfTvSet2e[] = "E42FC62C76B76B37818F78292D3C2226D0264760"; const char kdfTvSet2f[] = "D14BE4DD0093A3E759580233C80BB8399CE4C4E7"; #endif /** Test Vector Set #3: SHA-256 **/ const char kdfTvSet3k[] = "6AC382EAACA093E125E25C24BEBC84640C11987507344B5C739CEB84A9E0B222" "B9A8B51C839E5EBE49CFADBFB39599764ED522099DC912751950DC7DC97FBDC0" "6328B68F22781FD315AF568009A5509E5B87A11BF527C056DAFFD82AB6CBC25C" "CA37143459E7BC63BCDE52757ADEB7DF01CF12173F1FEF8102EC5AB142C213DD" "9D30696278A8D8BC32DDE9592D28C078C6D92B947D825ACAAB6494846A49DE24" "B9623F4889E8ADC38E8C669EFFEF176040AD945E90A7D3EEC15EFEEE78AE7104" "3C96511103A16BA7CAF0ACD0642EFDBE809934FAA1A5F1BD11043649B25CCD1F" "EE2E38815D4D5F5FC6B4102969F21C22AE1B0E7D3603A556A13262FF628DE222"; const char kdfTvSet3h[] = "7B7001185E256D4493445F39A55FB905E6321F4B5DD8BBF3100D51BA0BDA3D2D"; const char kdfTvSet3sid[] = "7B7001185E256D4493445F39A55FB905E6321F4B5DD8BBF3100D51BA0BDA3D2D"; const char kdfTvSet3a[] = "81F0330EF6F05361B3823BFDED6E1DE9"; const char kdfTvSet3b[] = "3F6FD2065EEB2B0B1D93195A1FED48A5"; const char kdfTvSet3c[] = "C35471034E6FD6547613178E23435F21"; const char kdfTvSet3d[] = "7E9D79032090D99F98B015634DD9F462"; const char kdfTvSet3e[] = "24EE559AD7CE712B685D0B2271E443C17AB1D1DCEB5A360569D25D5DC243002F"; const char kdfTvSet3f[] = "C3419C2B966235869D714BA5AC48DDB7D9E35C8C19AAC73422337A373453607E"; /** Test Vector Set #4: SHA-256 **/ const char kdfTvSet4k[] = "44708C76616F700BD31B0C155EF74E36390EEB39BC5C32CDC90E21922B0ED930" "B5B519C8AFEBEF0F4E4FB5B41B81D649D2127506620B594E9899F7F0D442ECDD" "D68308307B82F00065E9D75220A5A6F5641795772132215A236064EA965C6493" "C21F89879730EBBC3C20A22D8F5BFD07B525B194323B22D8A49944D1AA58502E" "756101EF1E8A91C9310E71F6DB65A3AD0A542CFA751F83721A99E89F1DBE5497" "1A3620ECFFC967AA55EED1A42D6E7A138B853557AC84689889F6D0C8553575FB" "89B4E13EAB5537DA72EF16F0D72F5E8505D97F110745193D550FA315FE88F672" "DB90D73843E97BA1F3D087BA8EB39025BBFFAD37589A6199227303D9D8E7F1E3"; const char kdfTvSet4h[] = "FE3727FD99A5AC7987C2CFBE062129E3027BF5E10310C6BCCDE9C916C8329DC2"; const char kdfTvSet4sid[] = "FFFA598BC0AD2AE84DC8DC05B1F72C5B0134025AE7EDF8A2E8DB11472E18E1FC"; const char kdfTvSet4a[] = "36730BAE8DE5CB98898D6B4A00B37058"; const char kdfTvSet4b[] = "5DFE446A83F40E8358D28CB97DF8F340"; const char kdfTvSet4c[] = "495B7AFED0872B761437728E9E94E2B8"; const char kdfTvSet4d[] = "C1474B3925BEC36F0B7F6CC698E949C8"; const char kdfTvSet4e[] = "B730F8DF6A0697645BE261169486C32A11612229276CBAC5D8B3669AFB2E4262"; const char kdfTvSet4f[] = "14A5EA98245FB058978B82A3CB092B1CCA7CE0109A4F98C16E1529579D58B819"; #define HASH_SHA WC_HASH_TYPE_SHA #define HASH_SHA256 WC_HASH_TYPE_SHA256 static const KdfTestVector kdfTestVectors[] = { #ifndef NO_SHA {HASH_SHA, 'A', kdfTvSet1k, kdfTvSet1h, kdfTvSet1sid, kdfTvSet1a}, {HASH_SHA, 'B', kdfTvSet1k, kdfTvSet1h, kdfTvSet1sid, kdfTvSet1b}, {HASH_SHA, 'C', kdfTvSet1k, kdfTvSet1h, kdfTvSet1sid, kdfTvSet1c}, {HASH_SHA, 'D', kdfTvSet1k, kdfTvSet1h, kdfTvSet1sid, kdfTvSet1d}, {HASH_SHA, 'E', kdfTvSet1k, kdfTvSet1h, kdfTvSet1sid, kdfTvSet1e}, {HASH_SHA, 'F', kdfTvSet1k, kdfTvSet1h, kdfTvSet1sid, kdfTvSet1f}, {HASH_SHA, 'A', kdfTvSet2k, kdfTvSet2h, kdfTvSet2sid, kdfTvSet2a}, {HASH_SHA, 'B', kdfTvSet2k, kdfTvSet2h, kdfTvSet2sid, kdfTvSet2b}, {HASH_SHA, 'C', kdfTvSet2k, kdfTvSet2h, kdfTvSet2sid, kdfTvSet2c}, {HASH_SHA, 'D', kdfTvSet2k, kdfTvSet2h, kdfTvSet2sid, kdfTvSet2d}, {HASH_SHA, 'E', kdfTvSet2k, kdfTvSet2h, kdfTvSet2sid, kdfTvSet2e}, {HASH_SHA, 'F', kdfTvSet2k, kdfTvSet2h, kdfTvSet2sid, kdfTvSet2f}, #endif {HASH_SHA256, 'A', kdfTvSet3k, kdfTvSet3h, kdfTvSet3sid, kdfTvSet3a}, {HASH_SHA256, 'B', kdfTvSet3k, kdfTvSet3h, kdfTvSet3sid, kdfTvSet3b}, {HASH_SHA256, 'C', kdfTvSet3k, kdfTvSet3h, kdfTvSet3sid, kdfTvSet3c}, {HASH_SHA256, 'D', kdfTvSet3k, kdfTvSet3h, kdfTvSet3sid, kdfTvSet3d}, {HASH_SHA256, 'E', kdfTvSet3k, kdfTvSet3h, kdfTvSet3sid, kdfTvSet3e}, {HASH_SHA256, 'F', kdfTvSet3k, kdfTvSet3h, kdfTvSet3sid, kdfTvSet3f}, {HASH_SHA256, 'A', kdfTvSet4k, kdfTvSet4h, kdfTvSet4sid, kdfTvSet4a}, {HASH_SHA256, 'B', kdfTvSet4k, kdfTvSet4h, kdfTvSet4sid, kdfTvSet4b}, {HASH_SHA256, 'C', kdfTvSet4k, kdfTvSet4h, kdfTvSet4sid, kdfTvSet4c}, {HASH_SHA256, 'D', kdfTvSet4k, kdfTvSet4h, kdfTvSet4sid, kdfTvSet4d}, {HASH_SHA256, 'E', kdfTvSet4k, kdfTvSet4h, kdfTvSet4sid, kdfTvSet4e}, {HASH_SHA256, 'F', kdfTvSet4k, kdfTvSet4h, kdfTvSet4sid, kdfTvSet4f} }; static int test_KDF(void) { int result = 0; word32 i; word32 tc = sizeof(kdfTestVectors)/sizeof(KdfTestVector); const KdfTestVector* tv = NULL; byte* k = NULL; byte* h = NULL; byte* sId = NULL; byte* eKey = NULL; word32 kSz = 0, hSz = 0, sIdSz = 0, eKeySz = 0; byte cKey[32]; /* Greater of SHA256_DIGEST_SIZE and AES_BLOCK_SIZE */ /* sId - Session ID, eKey - Expected Key, cKey - Calculated Key */ for (i = 0, tv = kdfTestVectors; i < tc; i++, tv++) { result = ConvertHexToBin(tv->k, &k, &kSz, tv->h, &h, &hSz, tv->sessionId, &sId, &sIdSz, tv->expectedKey, &eKey, &eKeySz); if (result != 0 || eKey == NULL) { printf("KDF: Could not convert test vector %u.\n", i); result = -100; } if (result == 0) { result = wolfSSH_KDF(tv->hashId, tv->keyId, cKey, eKeySz, k, kSz, h, hSz, sId, sIdSz); if (result != 0) { printf("KDF: Could not derive key.\n"); result = -101; } } if (result == 0) { if (memcmp(cKey, eKey, eKeySz) != 0) { printf("KDF: Calculated Key does not match Expected Key.\n"); result = -102; } } FreeBins(k, h, sId, eKey); k = NULL; h = NULL; sId = NULL; eKey = NULL; if (result != 0) break; } return result; } /* Key Generation Unit Test */ #ifdef WOLFSSH_KEYGEN #ifndef WOLFSSH_NO_RSA static int test_RsaKeyGen(void) { int result = 0; byte der[1200]; int derSz; derSz = wolfSSH_MakeRsaKey(der, sizeof(der), WOLFSSH_RSAKEY_DEFAULT_SZ, WOLFSSH_RSAKEY_DEFAULT_E); if (derSz < 0) { printf("RsaKeyGen: MakeRsaKey failed\n"); result = -103; } return result; } #endif #ifndef WOLFSSH_NO_ECDSA static int test_EcdsaKeyGen(void) { int result = 0; byte der[1200]; int derSz; derSz = wolfSSH_MakeEcdsaKey(der, sizeof(der), WOLFSSH_ECDSAKEY_PRIME256); if (derSz < 0) { printf("EcdsaKeyGen: MakeEcdsaKey failed\n"); result = -104; } return result; } #endif #if !defined(WOLFSSH_NO_ED25519) && defined(HAVE_ED25519) && \ defined(HAVE_ED25519_MAKE_KEY) && defined(HAVE_ED25519_KEY_EXPORT) static int test_Ed25519KeyGen(void) { int result = 0; byte der[1200]; int derSz; derSz = wolfSSH_MakeEd25519Key(der, sizeof(der), WOLFSSH_ED25519KEY); if (derSz < 0) { printf("Ed25519KeyGen: MakeEd25519Key failed\n"); result = -105; } return result; } #endif #endif #if defined(WOLFSSH_TEST_INTERNAL) && \ (!defined(WOLFSSH_NO_HMAC_SHA1) || \ !defined(WOLFSSH_NO_HMAC_SHA1_96) || \ !defined(WOLFSSH_NO_HMAC_SHA2_256) || \ !defined(WOLFSSH_NO_HMAC_SHA2_512)) /* Minimal SSH binary packet: uint32 length, padding_length, msgId, padding. * Same layout as tests/regress.c BuildPacket (8-byte aligned body). */ static word32 BuildMacTestPacketPrefix(byte msgId, byte* out, word32 outSz) { byte padLen = 6; word32 packetLen = (word32)(1 + 1 + padLen); word32 need = UINT32_SZ + packetLen; if (outSz < need) return 0; out[0] = (byte)(packetLen >> 24); out[1] = (byte)(packetLen >> 16); out[2] = (byte)(packetLen >> 8); out[3] = (byte)(packetLen); out[4] = padLen; out[5] = msgId; WMEMSET(out + 6, 0, padLen); return need; } static int test_DoReceive_VerifyMacFailure(void) { WOLFSSH_CTX* ctx = NULL; WOLFSSH* ssh = NULL; int ret = WS_SUCCESS; int result = 0; byte flatSeq[LENGTH_SZ]; byte macKey[MAX_HMAC_SZ]; Hmac hmac; word32 prefixLen; word32 totalLen; byte pkt[UINT32_SZ + 8 + MAX_HMAC_SZ]; int i; struct { byte macId; int hmacType; byte macSz; byte keySz; } cases[] = { #ifndef WOLFSSH_NO_HMAC_SHA1 { ID_HMAC_SHA1, WC_SHA, WC_SHA_DIGEST_SIZE, WC_SHA_DIGEST_SIZE }, #endif #ifndef WOLFSSH_NO_HMAC_SHA1_96 { ID_HMAC_SHA1_96, WC_SHA, SHA1_96_SZ, WC_SHA_DIGEST_SIZE }, #endif #ifndef WOLFSSH_NO_HMAC_SHA2_256 { ID_HMAC_SHA2_256, WC_SHA256, WC_SHA256_DIGEST_SIZE, WC_SHA256_DIGEST_SIZE }, #endif #ifndef WOLFSSH_NO_HMAC_SHA2_512 { ID_HMAC_SHA2_512, WC_SHA512, WC_SHA512_DIGEST_SIZE, WC_SHA512_DIGEST_SIZE }, #endif }; ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL); if (ctx == NULL) return -200; ssh = wolfSSH_new(ctx); if (ssh == NULL) { wolfSSH_CTX_free(ctx); return -201; } WMEMSET(macKey, 0xA5, sizeof(macKey)); for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])); i++) { prefixLen = BuildMacTestPacketPrefix(MSGID_IGNORE, pkt, sizeof(pkt)); if (prefixLen == 0) { result = -202; goto done; } totalLen = prefixLen + cases[i].macSz; ssh->peerEncryptId = ID_NONE; ssh->peerAeadMode = 0; ssh->peerBlockSz = MIN_BLOCK_SZ; ssh->peerMacId = cases[i].macId; ssh->peerMacSz = cases[i].macSz; WMEMCPY(ssh->peerKeys.macKey, macKey, cases[i].keySz); ssh->peerKeys.macKeySz = cases[i].keySz; ssh->peerSeq = 0; ssh->curSz = 0; ssh->processReplyState = PROCESS_INIT; ssh->error = 0; flatSeq[0] = (byte)(ssh->peerSeq >> 24); flatSeq[1] = (byte)(ssh->peerSeq >> 16); flatSeq[2] = (byte)(ssh->peerSeq >> 8); flatSeq[3] = (byte)(ssh->peerSeq); ret = wc_HmacInit(&hmac, ssh->ctx->heap, INVALID_DEVID); if (ret != WS_SUCCESS) { result = -203; goto done; } { byte digest[WC_MAX_DIGEST_SIZE]; ret = wc_HmacSetKey(&hmac, cases[i].hmacType, 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, pkt, prefixLen); if (ret == WS_SUCCESS) ret = wc_HmacFinal(&hmac, digest); wc_HmacFree(&hmac); if (ret == WS_SUCCESS) WMEMCPY(pkt + prefixLen, digest, cases[i].macSz); } if (ret != WS_SUCCESS) { result = -204; goto done; } pkt[prefixLen] ^= 0x01; ShrinkBuffer(&ssh->inputBuffer, 1); ret = GrowBuffer(&ssh->inputBuffer, totalLen); if (ret != WS_SUCCESS) { result = -205; goto done; } WMEMCPY(ssh->inputBuffer.buffer, pkt, totalLen); ssh->inputBuffer.length = totalLen; ssh->inputBuffer.idx = 0; ret = wolfSSH_TestDoReceive(ssh); if (ret != WS_FATAL_ERROR) { result = -206; goto done; } if (ssh->error != WS_VERIFY_MAC_E) { result = -207; goto done; } } done: wolfSSH_free(ssh); wolfSSH_CTX_free(ctx); return result; } #endif /* WOLFSSH_TEST_INTERNAL && any HMAC SHA variant enabled */ #if defined(WOLFSSH_TEST_INTERNAL) && !defined(WOLFSSH_NO_DH_GEX_SHA256) typedef struct { const char* candidate; const char* generator; word32 minBits; word32 maxBits; int expectedResult; } PrimeTestVector; static const PrimeTestVector primeTestVectors[] = { { /* * For testing the ValidateKexDhGexGroup() function, we need to * verify that the function detects unsafe primes. The following * unsafe prime is the prime used with GOST-ECC. (RFC 7836) It is * prime and fine for its application. It isn't safe for DH, as * q = (p-1)/2 is not prime. */ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc7", "02", 512, 8192, WS_CRYPTO_FAILED }, { /* * We need to verify that the function detects safe primes. The * following safePrime is the MODP 2048-bit group from RFC 3526. */ "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74" "020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f1437" "4fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7ed" "ee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf05" "98da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb" "9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3b" "e39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf695581718" "3995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff", "02", 2048, 8192, WS_SUCCESS }, { /* * This checks for g = p - 1. */ "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74" "020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f1437" "4fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7ed" "ee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf05" "98da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb" "9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3b" "e39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf695581718" "3995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff", "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74" "020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f1437" "4fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7ed" "ee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf05" "98da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb" "9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3b" "e39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf695581718" "3995497cea956ae515d2261898fa051015728e5a8aacaa68fffffffffffffffe", 2048, 8192, WS_CRYPTO_FAILED }, { /* * This checks for g = 1. */ "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74" "020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f1437" "4fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7ed" "ee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf05" "98da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb" "9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3b" "e39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf695581718" "3995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff", "01", 2048, 8192, WS_CRYPTO_FAILED }, { /* * This checks prime size less than minBits. */ "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74" "020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f1437" "4fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7ed" "ee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf05" "98da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb" "9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3b" "e39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf695581718" "3995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff", "02", 3072, 8192, WS_DH_SIZE_E }, { /* * This checks prime size greater than maxBits. */ "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74" "020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f1437" "4fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7ed" "ee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf05" "98da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb" "9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3b" "e39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf695581718" "3995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff", "02", 512, 1024, WS_DH_SIZE_E }, { /* * This checks for even p. */ "ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74" "020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f1437" "4fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7ed" "ee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf05" "98da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb" "9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3b" "e39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf695581718" "3995497cea956ae515d2261898fa051015728e5a8aacaa68fffffffffffffffe", "02", 2048, 8192, WS_CRYPTO_FAILED }, { /* * A well known composite number that breaks some MR implementations. * This is calculated by wolfCrypt for one of its prime tests. */ "000000000088cbf655be37a612fa535b4a9b81d394854ecbedfe1a4afbecdc7b" "a6a263549dd3c17882b054329384962576e7c5aa281e04ab5a0e7245584ad324" "9c7ac4de7caf5663bae95f6bb9e8bec4124e04d82eac54a246bda49a5c5c2a1b" "366ef8c085fc7c5f87478a55832d1b2184154c24260df67561d17c4359724403", "02", 512, 8192, WS_CRYPTO_FAILED }, }; static int test_DhGexGroupValidate(void) { WC_RNG rng; const PrimeTestVector* tv; byte* candidate; byte* generator; word32 candidateSz; word32 generatorSz; int tc = (int)(sizeof(primeTestVectors)/sizeof(primeTestVectors[0])); int result = 0, ret, i; if (wc_InitRng(&rng) != 0) { printf("DhGexGroupValidate: wc_InitRng failed\n"); return -110; } for (i = 0, tv = primeTestVectors; i < tc && !result; i++, tv++) { candidate = NULL; candidateSz = 0; generator = NULL; generatorSz = 0; ret = ConvertHexToBin(tv->candidate, &candidate, &candidateSz, tv->generator, &generator, &generatorSz, NULL, NULL, NULL, NULL, NULL, NULL); if (ret != 0) { result = -113; break; } ret = wolfSSH_TestValidateKexDhGexGroup(candidate, candidateSz, generator, generatorSz, tv->minBits, tv->maxBits, &rng); if (ret != tv->expectedResult) { printf("DhGexGroupValidate: validator returned %d, expected %d\n", ret, tv->expectedResult); result = -121; } FreeBins(candidate, generator, NULL, NULL); } wc_FreeRng(&rng); return result; } #endif /* WOLFSSH_TEST_INTERNAL && !WOLFSSH_NO_DH_GEX_SHA256 */ #ifdef WOLFSSH_TEST_INTERNAL /* Verify DoUserAuthBanner fully consumes the payload, including a non-empty * language tag. Before the fix, the tag's data bytes were left unconsumed, * which would misalign packet decoding for subsequent messages. */ static int test_DoUserAuthBanner(void) { WOLFSSH_CTX* ctx = NULL; WOLFSSH* ssh = NULL; int result = 0; /* Payload layout: [4-byte banner len][banner][4-byte lang len][lang] */ struct { const char* banner; word32 bannerSz; const char* lang; word32 langSz; int expectRet; const char* label; } cases[] = { { "Welcome", 7, "", 0, WS_SUCCESS, "empty lang tag" }, { "Welcome", 7, "en-US", 5, WS_SUCCESS, "non-empty lang tag" }, { NULL, 0, NULL, 0, WS_BAD_ARGUMENT, "null ssh" }, }; int i; ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL); if (ctx == NULL) return -300; ssh = wolfSSH_new(ctx); if (ssh == NULL) { wolfSSH_CTX_free(ctx); return -301; } ctx->showBanner = 0; for (i = 0; i < (int)(sizeof(cases)/sizeof(cases[0])); i++) { byte buf[128]; word32 idx = 0; word32 len = 0; int ret; if (cases[i].banner == NULL) { /* null-ssh case: pass NULL ssh, dummy non-null buf */ buf[0] = 0; len = 1; ret = wolfSSH_TestDoUserAuthBanner(NULL, buf, len, &idx); } else { /* encode banner string */ buf[len++] = (byte)(cases[i].bannerSz >> 24); buf[len++] = (byte)(cases[i].bannerSz >> 16); buf[len++] = (byte)(cases[i].bannerSz >> 8); buf[len++] = (byte)(cases[i].bannerSz); WMEMCPY(buf + len, cases[i].banner, cases[i].bannerSz); len += cases[i].bannerSz; /* encode language tag string */ buf[len++] = (byte)(cases[i].langSz >> 24); buf[len++] = (byte)(cases[i].langSz >> 16); buf[len++] = (byte)(cases[i].langSz >> 8); buf[len++] = (byte)(cases[i].langSz); WMEMCPY(buf + len, cases[i].lang, cases[i].langSz); len += cases[i].langSz; ret = wolfSSH_TestDoUserAuthBanner(ssh, buf, len, &idx); } if (ret != cases[i].expectRet) { printf("DoUserAuthBanner[%s]: ret=%d, expected=%d\n", cases[i].label, ret, cases[i].expectRet); result = -302 - i; break; } /* On success the entire payload must be consumed. */ if (ret == WS_SUCCESS && idx != len) { printf("DoUserAuthBanner[%s]: idx=%u, len=%u (unconsumed bytes)\n", cases[i].label, idx, len); result = -310 - i; break; } } wolfSSH_free(ssh); wolfSSH_CTX_free(ctx); return result; } /* Verify DoChannelRequest sends CHANNEL_SUCCESS for known types and * CHANNEL_FAILURE for unrecognized ones (RFC 4254 Section 5.4). * * A custom IoSend callback captures the outgoing packet in plaintext * (no cipher negotiated on a fresh session). The SSH packet layout is: * [4-byte packet_length][1-byte padding_length][1-byte msg_id]... * so the message ID lives at byte offset 5. */ static byte s_chanReqCapture[256]; static word32 s_chanReqCaptureSz = 0; static int CaptureIoSendChanReq(WOLFSSH* ssh, void* buf, word32 sz, void* ctx) { (void)ssh; (void)ctx; s_chanReqCaptureSz = (sz < (word32)sizeof(s_chanReqCapture)) ? sz : (word32)sizeof(s_chanReqCapture); WMEMCPY(s_chanReqCapture, buf, s_chanReqCaptureSz); return (int)sz; } static int test_DoChannelRequest(void) { WOLFSSH_CTX* ctx = NULL; WOLFSSH* ssh = NULL; WOLFSSH_CHANNEL* ch = NULL; int result = 0; int i; /* Payloads: [uint32 channelId=0][string type][byte wantReply=1][extra] */ static const byte payShell[] = { 0x00,0x00,0x00,0x00, /* channelId = 0 */ 0x00,0x00,0x00,0x05, /* typeSz = 5 */ 0x73,0x68,0x65,0x6C,0x6C, /* "shell" */ 0x01 /* wantReply = 1 */ }; static const byte payExec[] = { 0x00,0x00,0x00,0x00, /* channelId = 0 */ 0x00,0x00,0x00,0x04, /* typeSz = 4 */ 0x65,0x78,0x65,0x63, /* "exec" */ 0x01, /* wantReply = 1 */ 0x00,0x00,0x00,0x02, /* cmdSz = 2 */ 0x6C,0x73 /* "ls" */ }; static const byte payUnknown[] = { 0x00,0x00,0x00,0x00, /* channelId = 0 */ 0x00,0x00,0x00,0x0C, /* typeSz = 12 */ 0x75,0x6E,0x6B,0x6E,0x6F,0x77, 0x6E,0x2D,0x74,0x79,0x70,0x65, /* "unknown-type" */ 0x01 /* wantReply = 1 */ }; struct { const char* label; const byte* payload; word32 payloadSz; int expectRet; byte expectMsgId; } cases[] = { { "shell", payShell, (word32)sizeof(payShell), WS_SUCCESS, MSGID_CHANNEL_SUCCESS }, { "exec", payExec, (word32)sizeof(payExec), WS_SUCCESS, MSGID_CHANNEL_SUCCESS }, { "unknown-type", payUnknown, (word32)sizeof(payUnknown), WS_SUCCESS, MSGID_CHANNEL_FAILURE }, }; ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL); if (ctx == NULL) return -400; wolfSSH_SetIOSend(ctx, CaptureIoSendChanReq); ssh = wolfSSH_new(ctx); if (ssh == NULL) { result = -401; goto done; } ch = ChannelNew(ssh, ID_CHANTYPE_SESSION, DEFAULT_WINDOW_SZ, DEFAULT_MAX_PACKET_SZ); if (ch == NULL) { result = -402; goto done; } if (ChannelAppend(ssh, ch) != WS_SUCCESS) { ChannelDelete(ch, ssh->ctx->heap); result = -403; goto done; } for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])); i++) { word32 idx = 0; int ret; s_chanReqCaptureSz = 0; WMEMSET(s_chanReqCapture, 0, sizeof(s_chanReqCapture)); ret = wolfSSH_TestDoChannelRequest(ssh, (byte*)cases[i].payload, cases[i].payloadSz, &idx); if (ret != cases[i].expectRet) { printf("DoChannelRequest[%s]: ret=%d, expected=%d\n", cases[i].label, ret, cases[i].expectRet); result = -404 - i; goto done; } if (s_chanReqCaptureSz <= 5) { printf("DoChannelRequest[%s]: captured packet too short (%u)\n", cases[i].label, s_chanReqCaptureSz); result = -410 - i; goto done; } if (s_chanReqCapture[5] != cases[i].expectMsgId) { printf("DoChannelRequest[%s]: msg_id=0x%02x, expected=0x%02x\n", cases[i].label, s_chanReqCapture[5], cases[i].expectMsgId); result = -420 - i; goto done; } } done: wolfSSH_free(ssh); wolfSSH_CTX_free(ctx); return result; } #endif /* WOLFSSH_TEST_INTERNAL */ /* Error Code And Message Test */ static int test_Errors(void) { const char* errStr; const char* unknownStr = wolfSSH_ErrorToName(1); int result = 0; #ifdef NO_WOLFSSH_STRINGS /* Ensure a valid error code's string matches an invalid code's. * The string is that error strings are not available. */ errStr = wolfSSH_ErrorToName(WS_BAD_ARGUMENT); if (errStr != unknownStr) result = -104; #else int i, j = 0; /* Values that are not or no longer error codes. */ int missing[] = { -1059 }; int missingSz = (int)sizeof(missing)/sizeof(missing[0]); /* Check that all errors have a string and it's the same through the two * APIs. Check that the values that are not errors map to the unknown * string. */ for (i = WS_ERROR; i >= WS_LAST_E; i--) { errStr = wolfSSH_ErrorToName(i); if (j < missingSz && i == missing[j]) { j++; if (errStr != unknownStr) { result = -105; break; } } else { if (errStr == unknownStr) { result = -106; break; } } } /* Check if the next possible value has been given a string. */ if (result == 0) { errStr = wolfSSH_ErrorToName(i); if (errStr != unknownStr) return -107; } #endif return result; } int wolfSSH_UnitTest(int argc, char** argv) { int testResult = 0, unitResult = 0; (void)argc; (void)argv; wolfSSH_Init(); unitResult = test_Errors(); printf("Errors: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED")); testResult = testResult || unitResult; unitResult = test_KDF(); printf("KDF: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED")); testResult = testResult || unitResult; #if defined(WOLFSSH_TEST_INTERNAL) && \ (!defined(WOLFSSH_NO_HMAC_SHA1) || \ !defined(WOLFSSH_NO_HMAC_SHA1_96) || \ !defined(WOLFSSH_NO_HMAC_SHA2_256) || \ !defined(WOLFSSH_NO_HMAC_SHA2_512)) unitResult = test_DoReceive_VerifyMacFailure(); printf("DoReceiveVerifyMac: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED")); testResult = testResult || unitResult; #endif #if defined(WOLFSSH_TEST_INTERNAL) && !defined(WOLFSSH_NO_DH_GEX_SHA256) unitResult = test_DhGexGroupValidate(); printf("DhGexGroupValidate: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED")); testResult = testResult || unitResult; #endif #ifdef WOLFSSH_TEST_INTERNAL unitResult = test_DoUserAuthBanner(); printf("DoUserAuthBanner: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED")); testResult = testResult || unitResult; unitResult = test_DoChannelRequest(); printf("DoChannelRequest: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED")); testResult = testResult || unitResult; #endif #ifdef WOLFSSH_KEYGEN #ifndef WOLFSSH_NO_RSA unitResult = test_RsaKeyGen(); printf("RsaKeyGen: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED")); testResult = testResult || unitResult; #endif #ifndef WOLFSSH_NO_ECDSA unitResult = test_EcdsaKeyGen(); printf("EcdsaKeyGen: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED")); testResult = testResult || unitResult; #endif #if !defined(WOLFSSH_NO_ED25519) && defined(HAVE_ED25519) && \ defined(HAVE_ED25519_MAKE_KEY) && defined(HAVE_ED25519_KEY_EXPORT) unitResult = test_Ed25519KeyGen(); printf("Ed25519KeyGen: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED")); testResult = testResult || unitResult; #endif #endif wolfSSH_Cleanup(); return (testResult ? 1 : 0); } #ifndef NO_UNITTEST_MAIN_DRIVER int main(int argc, char** argv) { return wolfSSH_UnitTest(argc, argv); } #endif