diff --git a/README.md b/README.md index b7b5b8fb..1d5f1b5a 100644 --- a/README.md +++ b/README.md @@ -50,12 +50,12 @@ From another terminal run: The server will send a canned banner to the client: - CANNED BANNER - This server is an example test server. It should have its own banner, but - it is currently using a canned one in the library. Be happy or not. + wolfSSH Example Echo Server Characters typed into the client will be echoed to the screen by the server. -If the characters are echoed twice, the client has local echo enabled. +If the characters are echoed twice, the client has local echo enabled. The +echo server isn't being a proper terminal so the CR/LF translation will not +work as expected. testing notes @@ -86,6 +86,16 @@ Where the user can be `gretel` or `hansel`. release notes ------------- +### wolfSSH v1.2.0 (07/XX/2017) + +- Added ECDH Group Exchange with SHA2 hashing and curves nistp256, + nistp384, and nistp521. +- Added ECDSA with SHA2 hashing and curves nistp256, nistp384, and nistp521. +- Changed the echoserver to allow only one connection, but multiple + connections are allowed with a command line option. +- Added option to echoserver to offer an ECC public key. +- Other small bug fixes and enhancements. + ### wolfSSH v1.1.0 (06/16/2017) - Added DH Group Exchange with SHA-256 hashing to the key exchange. diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index b418a379..5a64c0a1 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -737,6 +737,7 @@ static void ShowUsage(void) printf("echoserver %s\n", LIBWOLFSSH_VERSION_STRING); printf("-h Help, print this usage\n"); printf("-m Allow multiple connections\n"); + printf("-e Use ECC private key\n"); } @@ -748,13 +749,14 @@ int main(int argc, char** argv) uint32_t defaultHighwater = EXAMPLE_HIGHWATER_MARK; uint32_t threadCount = 0; int multipleConnections = 0; + int useEcc = 0; char ch; #ifdef DEBUG_WOLFSSH wolfSSH_Debugging_ON(); #endif - while ((ch = mygetopt(argc, argv, "hm")) != -1) { + while ((ch = mygetopt(argc, argv, "hme")) != -1) { switch (ch) { case 'h' : ShowUsage(); @@ -764,6 +766,10 @@ int main(int argc, char** argv) multipleConnections = 1; break; + case 'e' : + useEcc = 1; + break; + default: ShowUsage(); exit(MY_EX_USAGE); @@ -790,13 +796,16 @@ int main(int argc, char** argv) uint8_t buf[SCRATCH_BUFFER_SIZE]; uint32_t bufSz; - bufSz = load_file("./keys/server-key.der", buf, SCRATCH_BUFFER_SIZE); + bufSz = load_file(useEcc ? + "./keys/server-key-ecc.der" : + "./keys/server-key-rsa.der", + buf, SCRATCH_BUFFER_SIZE); if (bufSz == 0) { fprintf(stderr, "Couldn't load key file.\n"); exit(EXIT_FAILURE); } - if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, - buf, bufSz, WOLFSSH_FORMAT_ASN1) < 0) { + if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, buf, bufSz, + WOLFSSH_FORMAT_ASN1) < 0) { fprintf(stderr, "Couldn't use key buffer.\n"); exit(EXIT_FAILURE); } diff --git a/keys/include.am b/keys/include.am index f5fa27ae..8bd995f1 100644 --- a/keys/include.am +++ b/keys/include.am @@ -4,8 +4,14 @@ EXTRA_DIST+= \ - keys/server-key.der \ - keys/server-key.pem \ + keys/server-key-ecc.der \ + keys/server-key-ecc.pem \ + keys/server-key-ecc-384.der \ + keys/server-key-ecc-384.pem \ + keys/server-key-ecc-521.der \ + keys/server-key-ecc-521.pem \ + keys/server-key-rsa.der \ + keys/server-key-rsa.pem \ keys/key-hansel.pem \ keys/key-gretel.pem \ keys/publickeys.txt \ diff --git a/keys/server-key-ecc-384.der b/keys/server-key-ecc-384.der new file mode 100644 index 00000000..fd024a43 Binary files /dev/null and b/keys/server-key-ecc-384.der differ diff --git a/keys/server-key-ecc-384.pem b/keys/server-key-ecc-384.pem new file mode 100644 index 00000000..62ec4d2e --- /dev/null +++ b/keys/server-key-ecc-384.pem @@ -0,0 +1,11 @@ +ASN1 OID: secp384r1 +NIST CURVE: P-384 +-----BEGIN EC PARAMETERS----- +BgUrgQQAIg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDA+rdK7vwWnvjo/fCgVEoneW7NkTXARdh21byoDYvumT5jmT/mG3E+4 +79sta42lcUKgBwYFK4EEACKhZANiAAQ41ivkGP9XP9DgINSIdsThEh37LW6+5Ild +dyQxbUaiMQWHPymG1ccSgDpvRxq4aFDrBj4QiWE0nPi0xqTPXpe9flHpdePpIXJh +UG65zzxJPT64jUZ7XyfrqyFhwABm/r0= +-----END EC PRIVATE KEY----- diff --git a/keys/server-key-ecc-521.der b/keys/server-key-ecc-521.der new file mode 100644 index 00000000..008a28a9 Binary files /dev/null and b/keys/server-key-ecc-521.der differ diff --git a/keys/server-key-ecc-521.pem b/keys/server-key-ecc-521.pem new file mode 100644 index 00000000..97052b68 --- /dev/null +++ b/keys/server-key-ecc-521.pem @@ -0,0 +1,12 @@ +ASN1 OID: secp521r1 +NIST CURVE: P-521 +-----BEGIN EC PARAMETERS----- +BgUrgQQAIw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIATKTYZCjZQA57LfORLrmWwZWJUEOvkuht5wrk30byKikaa7J0iq6C +WA32w59Js+2C8XiezhtlfUVDjP8VZTQ1RXWgBwYFK4EEACOhgYkDgYYABAH40KfD +xY2EGVeWnyE6lPPaVQ7fdtjdFxUx81uwaci8MA1vazfRgEapcX8sb1lRnIJwlbKa +YxMwYhjCNXaUAND5bQAKGTujRmUr60CamkXFl6Ptky3Vqq6Wvy8xflp6x0WLPGzb +qpDDVTgs383Kc3fZLrIKXox0I3ylo0Wxnj8aIpCxVA== +-----END EC PRIVATE KEY----- diff --git a/keys/server-key-ecc.der b/keys/server-key-ecc.der new file mode 100644 index 00000000..ace1592b Binary files /dev/null and b/keys/server-key-ecc.der differ diff --git a/keys/server-key-ecc.pem b/keys/server-key-ecc.pem new file mode 100644 index 00000000..957460f1 --- /dev/null +++ b/keys/server-key-ecc.pem @@ -0,0 +1,10 @@ +ASN1 OID: prime256v1 +NIST CURVE: P-256 +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIGEJmQt50l8oWg9dFcyhVlT5KzmHIS2nfYV7uH84xm3VoAoGCCqGSM49 +AwEHoUQDQgAEgRP/pCu3nEV0eoNMYfM/rSbPIs2po7ylYbR85mLUwvdVQ5ox+4AR +ILUSSyT1eNf9Iu9GNfAFWGtfY8jaG8T1aQ== +-----END EC PRIVATE KEY----- diff --git a/keys/server-key.der b/keys/server-key-rsa.der similarity index 100% rename from keys/server-key.der rename to keys/server-key-rsa.der diff --git a/keys/server-key.pem b/keys/server-key-rsa.pem similarity index 100% rename from keys/server-key.pem rename to keys/server-key-rsa.pem diff --git a/src/internal.c b/src/internal.c index 9c30f42a..a5708e79 100644 --- a/src/internal.c +++ b/src/internal.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #ifdef NO_INLINE @@ -150,6 +151,12 @@ const char* GetErrorString(int err) 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"; + default: return "Unknown error code"; } @@ -371,6 +378,101 @@ void SshResourceFree(WOLFSSH* ssh, void* heap) } +int ProcessBuffer(WOLFSSH_CTX* ctx, const uint8_t* in, uint32_t inSz, + int format, int type) +{ + int dynamicType; + void* heap; + uint8_t* der; + uint32_t 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 = (uint8_t*)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; + uint32_t 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; +} + + typedef struct { uint8_t id; const char* name; @@ -393,9 +495,15 @@ static const NameIdPair NameIdMap[] = { { 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" }, /* UserAuth IDs */ { ID_USERAUTH_PASSWORD, "password" }, @@ -1072,13 +1180,21 @@ static int DoNameList(uint8_t* idList, uint32_t* idListSz, static const uint8_t cannedEncAlgo[] = {ID_AES128_GCM, ID_AES128_CBC}; static const uint8_t cannedMacAlgo[] = {ID_HMAC_SHA2_256, ID_HMAC_SHA1_96, ID_HMAC_SHA1}; -static const uint8_t cannedKeyAlgo[] = {ID_SSH_RSA}; -static const uint8_t cannedKexAlgo[] = {ID_DH_GEX_SHA256, ID_DH_GROUP14_SHA1, +static const uint8_t cannedKeyAlgoRsa[] = {ID_SSH_RSA}; +static const uint8_t cannedKeyAlgoEcc256[] = {ID_ECDSA_SHA2_NISTP256}; +static const uint8_t cannedKeyAlgoEcc384[] = {ID_ECDSA_SHA2_NISTP384}; +static const uint8_t cannedKeyAlgoEcc521[] = {ID_ECDSA_SHA2_NISTP521}; +static const uint8_t cannedKexAlgo[] = {ID_ECDH_SHA2_NISTP256, + ID_DH_GEX_SHA256, + ID_DH_GROUP14_SHA1, ID_DH_GROUP1_SHA1}; static const uint32_t cannedEncAlgoSz = sizeof(cannedEncAlgo); static const uint32_t cannedMacAlgoSz = sizeof(cannedMacAlgo); -static const uint32_t cannedKeyAlgoSz = sizeof(cannedKeyAlgo); +static const uint32_t cannedKeyAlgoRsaSz = sizeof(cannedKeyAlgoRsa); +static const uint32_t cannedKeyAlgoEcc256Sz = sizeof(cannedKeyAlgoEcc256); +static const uint32_t cannedKeyAlgoEcc384Sz = sizeof(cannedKeyAlgoEcc384); +static const uint32_t cannedKeyAlgoEcc521Sz = sizeof(cannedKeyAlgoEcc521); static const uint32_t cannedKexAlgoSz = sizeof(cannedKexAlgo); @@ -1153,15 +1269,58 @@ static INLINE uint8_t HashForId(uint8_t 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(uint8_t 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(uint8_t 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 uint8_t AeadModeForId(uint8_t id) { return (id == ID_AES128_GCM); @@ -1172,7 +1331,7 @@ static int DoKexInit(WOLFSSH* ssh, uint8_t* buf, uint32_t len, uint32_t* idx) { int ret = WS_SUCCESS; uint8_t algoId; - uint8_t list[3]; + uint8_t list[6]; uint32_t listSz; uint32_t skipSz; uint32_t begin; @@ -1236,7 +1395,28 @@ static int DoKexInit(WOLFSSH* ssh, uint8_t* buf, uint32_t len, uint32_t* idx) listSz = 1; ret = DoNameList(list, &listSz, buf, len, &begin); if (ret == WS_SUCCESS) { - algoId = MatchIdLists(list, listSz, cannedKeyAlgo, cannedKeyAlgoSz); + const uint8_t *cannedKeyAlgo; + uint32_t 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; @@ -1514,7 +1694,11 @@ static int DoKexDhInit(WOLFSSH* ssh, uint8_t* buf, uint32_t len, uint32_t* 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. */ + * 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 */ uint8_t* e; @@ -3449,15 +3633,26 @@ static INLINE void CopyNameList(uint8_t* buf, uint32_t* idx, 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 cannedKeyAlgoNames[] = "ssh-rsa"; -static const char cannedKexAlgoNames[] = "diffie-hellman-group-exchange-sha256," +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 uint32_t cannedEncAlgoNamesSz = sizeof(cannedEncAlgoNames) - 1; static const uint32_t cannedMacAlgoNamesSz = sizeof(cannedMacAlgoNames) - 1; -static const uint32_t cannedKeyAlgoNamesSz = sizeof(cannedKeyAlgoNames) - 1; +static const uint32_t cannedKeyAlgoRsaNamesSz = + sizeof(cannedKeyAlgoRsaNames) - 1; +static const uint32_t cannedKeyAlgoEcc256NamesSz = + sizeof(cannedKeyAlgoEcc256Names) - 1; +static const uint32_t cannedKeyAlgoEcc384NamesSz = + sizeof(cannedKeyAlgoEcc384Names) - 1; +static const uint32_t cannedKeyAlgoEcc521NamesSz = + sizeof(cannedKeyAlgoEcc521Names) - 1; static const uint32_t cannedKexAlgoNamesSz = sizeof(cannedKexAlgoNames) - 1; static const uint32_t cannedNoneNamesSz = sizeof(cannedNoneNames) - 1; @@ -3469,6 +3664,8 @@ int SendKexInit(WOLFSSH* ssh) uint32_t idx = 0; uint32_t payloadSz; int ret = WS_SUCCESS; + const char* cannedKeyAlgoNames; + uint32_t cannedKeyAlgoNamesSz; WLOG(WS_LOG_DEBUG, "Entering SendKexInit()"); @@ -3484,6 +3681,23 @@ int SendKexInit(WOLFSSH* ssh) } 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) + @@ -3561,30 +3775,55 @@ int SendKexInit(WOLFSSH* ssh) } -/* This function is clunky, but outdated. */ +/* This function is clunky, but outdated. + * 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) { - const uint8_t* primeGroup; - uint32_t primeGroupSz; - const uint8_t* generator; - uint32_t generatorSz; + const uint8_t* primeGroup = dhPrimeGroup14; + uint32_t primeGroupSz = dhPrimeGroup14Sz; + const uint8_t* generator = dhGenerator; + uint32_t generatorSz = dhGeneratorSz; - DhKey dhKey; - uint8_t f[256]; + uint8_t useEcc = 0; + uint8_t f[257]; uint32_t fSz = sizeof(f); uint8_t fPad = 0; - uint8_t y[256]; - uint32_t ySz = sizeof(y); uint8_t kPad = 0; - RsaKey rsaKey; - uint8_t rsaE[257]; - uint32_t rsaESz = sizeof(rsaE); - uint8_t rsaEPad = 0; - uint8_t rsaN[257]; - uint32_t rsaNSz = sizeof(rsaN); - uint8_t rsaNPad = 0; - uint32_t rsaKeyBlockSz; + struct { + uint8_t useRsa; + uint32_t sz; + const char *name; + uint32_t nameSz; + union { + struct { + RsaKey key; + uint8_t e[257]; + uint32_t eSz; + uint8_t ePad; + uint8_t n[257]; + uint32_t nSz; + uint8_t nPad; + } rsa; + struct { + ecc_key key; + uint32_t keyBlobSz; + const char *keyBlobName; + uint32_t keyBlobNameSz; + uint8_t q[257]; + uint32_t qSz; + uint8_t qPad; + const char *primeName; + uint32_t primeNameSz; + } ecc; + } sk; + } sigKeyBlock; uint8_t sig[512]; uint32_t sigSz = sizeof(sig); @@ -3595,141 +3834,245 @@ int SendKexDhReply(WOLFSSH* ssh) uint32_t scratch = 0; uint8_t* output; uint32_t idx; - int ret; - uint8_t msgId; + int ret = WS_SUCCESS; + uint8_t msgId = MSGID_KEXDH_REPLY; WLOG(WS_LOG_DEBUG, "Entering SendKexDhReply()"); - ret = wc_InitDhKey(&dhKey); - if (ret == WS_SUCCESS) { - switch (ssh->handshake->kexId) { - case ID_DH_GROUP1_SHA1: - primeGroup = dhPrimeGroup1; - primeGroupSz = dhPrimeGroup1Sz; - generator = dhGenerator; - generatorSz = dhGeneratorSz; - msgId = MSGID_KEXDH_REPLY; - break; + sigKeyBlock.useRsa = ssh->handshake->pubKeyId == ID_SSH_RSA; + sigKeyBlock.name = IdToName(ssh->handshake->pubKeyId); + sigKeyBlock.nameSz = (uint32_t)strlen(sigKeyBlock.name); - case ID_DH_GROUP14_SHA1: - primeGroup = dhPrimeGroup14; - primeGroupSz = dhPrimeGroup14Sz; - generator = dhGenerator; - generatorSz = dhGeneratorSz; - msgId = MSGID_KEXDH_REPLY; - break; + switch (ssh->handshake->kexId) { + case ID_DH_GROUP1_SHA1: + primeGroup = dhPrimeGroup1; + primeGroupSz = dhPrimeGroup1Sz; + msgId = MSGID_KEXDH_REPLY; + break; - case ID_DH_GEX_SHA256: - primeGroup = dhPrimeGroup14; - primeGroupSz = dhPrimeGroup14Sz; - generator = dhGenerator; - generatorSz = dhGeneratorSz; - msgId = MSGID_KEXDH_GEX_REPLY; - break; + case ID_DH_GROUP14_SHA1: + /* This is the default case. */ + break; - default: - ret = WS_INVALID_ALGO_ID; - } + case 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: + useEcc = 1; + msgId = MSGID_KEXDH_REPLY; + break; + + default: + 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 (wc_DhSetKey(&dhKey, primeGroup, primeGroupSz, - generator, generatorSz) < 0) - ret = WS_CRYPTO_FAILED; - } + if (sigKeyBlock.useRsa) { + /* 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); + ret = wc_InitRsaKey(&sigKeyBlock.sk.rsa.key, ssh->ctx->heap); + if (ret == 0) + ret = wc_RsaPrivateKeyDecode(ssh->ctx->privateKey, &scratch, + &sigKeyBlock.sk.rsa.key, + (int)ssh->ctx->privateKeySz); + /* 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); + 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); + } + /* 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, + (uint8_t*)sigKeyBlock.name, + sigKeyBlock.nameSz); + /* 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); + } + /* 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); + } + } + /* 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); + /* 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); + } + /* 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); + } + } + /* 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); + } + else { + sigKeyBlock.sk.ecc.primeName = + PrimeNameForId(ssh->handshake->pubKeyId); + sigKeyBlock.sk.ecc.primeNameSz = + (uint32_t)strlen(sigKeyBlock.sk.ecc.primeName); - /* Hash in the server's RSA key. */ - if (ret == WS_SUCCESS) { - ret = wc_InitRsaKey(&rsaKey, ssh->ctx->heap); - if (ret == 0) - ret = wc_RsaPrivateKeyDecode(ssh->ctx->privateKey, &scratch, - &rsaKey, (int)ssh->ctx->privateKeySz); - if (ret == 0) - ret = wc_RsaFlattenPublicKey(&rsaKey, rsaE, &rsaESz, rsaN, &rsaNSz); - if (ret == 0) { - if (rsaE[0] & 0x80) rsaEPad = 1; - if (rsaN[0] & 0x80) rsaNPad = 1; - rsaKeyBlockSz = (LENGTH_SZ * 3) + 7 + rsaESz + rsaEPad + - rsaNSz + rsaNPad; - /* The 7 is for the name "ssh-rsa". */ - c32toa(rsaKeyBlockSz, scratchLen); - ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, - scratchLen, LENGTH_SZ); - } - if (ret == 0) { - c32toa(7, scratchLen); - ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, - scratchLen, LENGTH_SZ); - } - if (ret == 0) - ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, - (const uint8_t*)"ssh-rsa", 7); - if (ret == 0) { - c32toa(rsaESz + rsaEPad, scratchLen); - ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, - scratchLen, LENGTH_SZ); - } - if (ret == 0) { - if (rsaEPad) { - scratchLen[0] = 0; + /* 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, 1); + ssh->handshake->hashId, + scratchLen, LENGTH_SZ); } - } - if (ret == 0) - ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, - rsaE, rsaESz); - if (ret == 0) { - c32toa(rsaNSz + rsaNPad, scratchLen); - ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, - scratchLen, LENGTH_SZ); - } - if (ret == 0) { - if (rsaNPad) { - scratchLen[0] = 0; + /* 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, 1); + ssh->handshake->hashId, + scratchLen, LENGTH_SZ); } + /* Hash in the key type string. */ + if (ret == 0) + ret = wc_HashUpdate(&ssh->handshake->hash, + ssh->handshake->hashId, + (uint8_t*)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 uint8_t*)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 (ret == 0) - ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, - rsaN, rsaNSz); /* If using DH-GEX include the GEX specific values. */ if (ssh->handshake->kexId == ID_DH_GEX_SHA256) { uint8_t 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 (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); } - + /* Hash in the pad byte for the GEX prime group. */ if (ret == 0) { if (primeGroupPad) { scratchLen[0] = 0; @@ -3738,22 +4081,23 @@ int SendKexDhReply(WOLFSSH* ssh) scratchLen, 1); } } - + /* Hash in the GEX prime group. */ if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, primeGroup, primeGroupSz); - + /* 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); } - + /* Hash in the pad byte for the GEX generator. */ if (ret == 0) { if (generatorPad) { scratchLen[0] = 0; @@ -3762,35 +4106,84 @@ int SendKexDhReply(WOLFSSH* ssh) scratchLen, 1); } } - + /* Hash in the GEX generator. */ if (ret == 0) - ret = wc_HashUpdate(&ssh->handshake->hash, - ssh->handshake->hashId, - generator, generatorSz); + ret = wc_HashUpdate(&ssh->handshake->hash, + ssh->handshake->hashId, + generator, generatorSz); } - /* Hash in the client's DH e-value. */ + /* 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); - /* Make the server's DH f-value, and the shared secret k. */ - if (ret == 0) - ret = wc_DhGenerateKeyPair(&dhKey, ssh->rng, y, &ySz, f, &fSz); + /* 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 (f[0] & 0x80) fPad = 1; - ret = wc_DhAgree(&dhKey, ssh->k, &ssh->kSz, y, ySz, - ssh->handshake->e, ssh->handshake->eSz); + if (!useEcc) { + DhKey privKey; + uint8_t y[256]; + uint32_t ySz = sizeof(y); + + ret = wc_InitDhKey(&privKey); + if (ret == 0) + ret = wc_DhSetKey(&privKey, primeGroup, primeGroupSz, + generator, generatorSz); + if (ret == 0) + ret = wc_DhGenerateKeyPair(&privKey, ssh->rng, + y, &ySz, f, &fSz); + if (ret == 0) + ret = wc_DhAgree(&privKey, ssh->k, &ssh->kSz, y, ySz, + ssh->handshake->e, ssh->handshake->eSz); + 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); + } } + + /* Add a pad byte if the mpint has the MSB set. */ if (ret == 0) { - if (ssh->k[0] & 0x80) kPad = 1; + fPad = (f[0] & 0x80) ? 1 : 0; + kPad = (ssh->k[0] & 0x80) ? 1 : 0; } - wc_FreeDhKey(&dhKey); /* Hash in the server's DH f-value. */ if (ret == 0) { @@ -3808,8 +4201,7 @@ int SendKexDhReply(WOLFSSH* ssh) if (ret == 0) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, f, fSz); - - /* Hash in the shared secret k. */ + /* Hash in the shared secret K. */ if (ret == 0) { c32toa(ssh->kSz + kPad, scratchLen); ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, @@ -3826,7 +4218,7 @@ int SendKexDhReply(WOLFSSH* ssh) ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId, ssh->k, ssh->kSz); - /* Save the handshake hash value h, and session ID. */ + /* Save the exchange hash value H, and session ID. */ if (ret == 0) ret = wc_HashFinal(&ssh->handshake->hash, ssh->handshake->hashId, ssh->h); @@ -3842,43 +4234,92 @@ int SendKexDhReply(WOLFSSH* ssh) ret = WS_CRYPTO_FAILED; } - /* Sign h with the server's RSA private key. */ + /* Sign h with the server's private key. */ if (ret == WS_SUCCESS) { wc_HashAlg digestHash; uint8_t digest[WC_MAX_DIGEST_SIZE]; - uint8_t encSig[MAX_ENCODED_SIG_SZ]; - uint32_t encSigSz; + uint8_t sigHashId; - ret = wc_HashInit(&digestHash, WC_HASH_TYPE_SHA); + sigHashId = HashForId(ssh->handshake->pubKeyId); + + ret = wc_HashInit(&digestHash, sigHashId); if (ret == 0) - ret = wc_HashUpdate(&digestHash, WC_HASH_TYPE_SHA, - ssh->h, ssh->hSz); + ret = wc_HashUpdate(&digestHash, sigHashId, ssh->h, ssh->hSz); if (ret == 0) - ret = wc_HashFinal(&digestHash, WC_HASH_TYPE_SHA, digest); + ret = wc_HashFinal(&digestHash, sigHashId, digest); if (ret != 0) ret = WS_CRYPTO_FAILED; if (ret == WS_SUCCESS) { - encSigSz = wc_EncodeSignature(encSig, digest, - wc_HashGetDigestSize(WC_HASH_TYPE_SHA), - wc_HashGetOID(WC_HASH_TYPE_SHA)); - if (encSigSz <= 0) { - WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad Encode Sig"); - ret = WS_CRYPTO_FAILED; + if (sigKeyBlock.useRsa) { + uint8_t encSig[MAX_ENCODED_SIG_SZ]; + uint32_t 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 { - /* At this point, sigSz should already be sizeof(sig) */ - sigSz = wc_RsaSSL_Sign(encSig, encSigSz, - sig, sigSz, &rsaKey, ssh->rng); - if (sigSz <= 0) { - WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad RSA Sign"); - ret = WS_RSA_E; + 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 { + uint8_t r[257]; + uint32_t rSz = sizeof(r); + uint8_t rPad; + uint8_t s[257]; + uint32_t sSz = sizeof(s); + uint8_t 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); + } } } } } - wc_FreeRsaKey(&rsaKey); - sigBlockSz = (LENGTH_SZ * 2) + 7 + sigSz; + + 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); @@ -3887,7 +4328,7 @@ int SendKexDhReply(WOLFSSH* ssh) * add it to the hash and then add K. */ if (ret == WS_SUCCESS) { payloadSz = MSG_ID_SZ + (LENGTH_SZ * 3) + - rsaKeyBlockSz + fSz + fPad + sigBlockSz; + sigKeyBlock.sz + fSz + fPad + sigBlockSz; ret = PreparePacket(ssh, payloadSz); } @@ -3898,35 +4339,53 @@ int SendKexDhReply(WOLFSSH* ssh) output[idx++] = msgId; /* Copy the rsaKeyBlock into the buffer. */ - c32toa(rsaKeyBlockSz, output + idx); + c32toa(sigKeyBlock.sz, output + idx); idx += LENGTH_SZ; - c32toa(7, output + idx); + c32toa(sigKeyBlock.nameSz, output + idx); idx += LENGTH_SZ; - WMEMCPY(output + idx, "ssh-rsa", 7); - idx += 7; - c32toa(rsaESz + rsaEPad, output + idx); - idx += LENGTH_SZ; - if (rsaEPad) output[idx++] = 0; - WMEMCPY(output + idx, rsaE, rsaESz); - idx += rsaESz; - c32toa(rsaNSz + rsaNPad, output + idx); - idx += LENGTH_SZ; - if (rsaNPad) output[idx++] = 0; - WMEMCPY(output + idx, rsaN, rsaNSz); - idx += rsaNSz; + 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(7, output + idx); + c32toa(sigKeyBlock.nameSz, output + idx); idx += LENGTH_SZ; - WMEMCPY(output + idx, "ssh-rsa", 7); - idx += 7; + WMEMCPY(output + idx, sigKeyBlock.name, sigKeyBlock.nameSz); + idx += sigKeyBlock.nameSz; c32toa(sigSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, sig, sigSz); diff --git a/src/ssh.c b/src/ssh.c index 3379ef27..bb8552bd 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -31,8 +31,6 @@ #include #include #include -#include -#include #include #ifdef NO_INLINE @@ -459,74 +457,6 @@ void* wolfSSH_GetUserAuthCtx(WOLFSSH* ssh) } -static int ProcessBuffer(WOLFSSH_CTX* ctx, const uint8_t* in, uint32_t inSz, - int format, int type) -{ - int dynamicType; - void* heap; - uint8_t* der; - uint32_t 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 = (uint8_t*)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 */ - RsaKey key; - uint32_t scratch = 0; - - if (wc_InitRsaKey(&key, NULL) < 0) - return WS_RSA_E; - - if (wc_RsaPrivateKeyDecode(der, &scratch, &key, derSz) < 0) - return WS_BAD_FILE_E; - - wc_FreeRsaKey(&key); - } - - return WS_SUCCESS; -} - - int wolfSSH_CTX_SetBanner(WOLFSSH_CTX* ctx, const char* newBanner) { diff --git a/wolfssh/error.h b/wolfssh/error.h index 7cd7c2c0..b90a74fa 100644 --- a/wolfssh/error.h +++ b/wolfssh/error.h @@ -68,7 +68,9 @@ enum WS_ErrorCodes { WS_INVALID_USERNAME = -28, WS_CRYPTO_FAILED = -29, /* crypto action failed */ WS_INVALID_STATE_E = -30, - WS_REKEYING = -31 + WS_REKEYING = -31, + WS_INVALID_PRIME_CURVE = -32, + WS_ECC_E = -33 }; diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 77c0c6b2..6d8230b3 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -31,7 +31,6 @@ #include #include #include -#include #include @@ -73,9 +72,15 @@ enum { 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, /* UserAuth IDs */ ID_USERAUTH_PASSWORD, @@ -155,6 +160,7 @@ struct WOLFSSH_CTX { uint8_t* privateKey; /* Owned by CTX */ uint32_t privateKeySz; + uint8_t useEcc; /* Depends on the private key */ uint32_t highwaterMark; const char* banner; uint32_t bannerSz; @@ -191,7 +197,8 @@ typedef struct HandshakeInfo { Keys clientKeys; Keys serverKeys; wc_HashAlg hash; - uint8_t e[257]; /* May have a leading zero, for unsigned. */ + uint8_t e[257]; /* May have a leading zero, for unsigned, or + * it is a nistp521 Q_S value. */ uint32_t eSz; uint8_t* serverKexInit; uint32_t serverKexInitSz; @@ -251,7 +258,7 @@ struct WOLFSSH { Buffer inputBuffer; Buffer outputBuffer; - RNG* rng; + WC_RNG* rng; uint8_t h[WC_MAX_DIGEST_SIZE]; uint32_t hSz; @@ -299,6 +306,8 @@ WOLFSSH_LOCAL void ChannelDelete(WOLFSSH_CHANNEL*, void*); WOLFSSH_LOCAL WOLFSSH_CHANNEL* ChannelFind(WOLFSSH*, uint32_t, uint8_t); WOLFSSH_LOCAL int ChannelRemove(WOLFSSH*, uint32_t, uint8_t); WOLFSSH_LOCAL int ChannelPutData(WOLFSSH_CHANNEL*, uint8_t*, uint32_t); +WOLFSSH_LOCAL int ProcessBuffer(WOLFSSH_CTX*, const uint8_t*, uint32_t, + int, int); #ifndef WOLFSSH_USER_IO