From c3086f9611088e07553ed5b98632a1fb6a6090dc Mon Sep 17 00:00:00 2001 From: Fabio Alemagna <507164+falemagn@users.noreply.github.com> Date: Fri, 14 Jul 2023 21:19:10 +0200 Subject: [PATCH 1/5] Add Ed25519 1. Added support for Ed25519 private keys. 2. Added more define guards for ED25519 3. The userAuthResultCb must be invoked only in the case the pubkey has a signature. 4. Define WOLFSSH_NO_ED25519 if the ssh-ed25519 pubkey support must not be compiled for lack of prerequisites. --- src/internal.c | 747 +++++++++++++++++++++++++++++++-------------- wolfssh/internal.h | 20 +- 2 files changed, 538 insertions(+), 229 deletions(-) diff --git a/src/internal.c b/src/internal.c index 4d95c3057..f0b106a80 100644 --- a/src/internal.c +++ b/src/internal.c @@ -37,6 +37,8 @@ #ifndef WOLFSSH_NO_DH #include #endif +#include +#include #ifdef WOLFSSH_CERTS #include #endif @@ -1177,6 +1179,11 @@ typedef struct WS_KeySignature { struct { ecc_key key; } ecc; +#endif +#ifndef WOLFSSH_NO_ED25519 + struct { + ed25519_key key; + } ed25519; #endif } ks; } WS_KeySignature; @@ -1290,6 +1297,29 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap) wc_ecc_free(&key->ks.ecc.key); } #endif /* WOLFSSH_NO_ECDSA */ +#if !defined(WOLFSSH_NO_ED25519) + if (key != NULL) { + if (key->keySigId == ID_UNKNOWN) { + idx = 0; + ret = wc_ed25519_init_ex(&key->ks.ed25519.key, heap, INVALID_DEVID); + + if(ret == 0) { + if (isPrivate) { + ret = wc_Ed25519PrivateKeyDecode(in, &idx, &key->ks.ed25519.key, inSz); + } + else { + ret = wc_Ed25519PublicKeyDecode(in, &idx, &key->ks.ed25519.key, inSz); + } + } + + /* If decode was successful, this is a Ed25519 key. */ + if(ret == 0) + key->keySigId = ID_ED25519; + + wc_ed25519_free(&key->ks.ed25519.key); + } + } +#endif /* WOLFSSH_NO_ED25519 */ if (key->keySigId == ID_UNKNOWN) { ret = WS_UNIMPLEMENTED_E; @@ -2273,10 +2303,12 @@ static const NameIdPair NameIdMap[] = { #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521 { ID_ECDH_SHA2_NISTP521, TYPE_KEX, "ecdh-sha2-nistp521" }, #endif +#if 0 #ifndef WOLFSSH_NO_ECDH_SHA2_ED25519 { ID_ECDH_SHA2_ED25519, TYPE_KEX, "curve25519-sha256" }, { ID_ECDH_SHA2_ED25519_LIBSSH, TYPE_KEX, "curve25519-sha256@libssh.org" }, #endif +#endif #ifndef WOLFSSH_NO_DH_GEX_SHA256 { ID_DH_GROUP14_SHA256, TYPE_KEX, "diffie-hellman-group14-sha256" }, #endif @@ -2311,6 +2343,9 @@ static const NameIdPair NameIdMap[] = { #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521 { ID_ECDSA_SHA2_NISTP521, TYPE_KEY, "ecdsa-sha2-nistp521" }, #endif +#ifndef WOLFSSH_NO_ED25519 + { ID_ED25519, TYPE_KEY, "ssh-ed25519" }, +#endif #ifdef WOLFSSH_CERTS #ifndef WOLFSSH_NO_SSH_RSA_SHA1 { ID_X509V3_SSH_RSA, TYPE_KEY, "x509v3-ssh-rsa" }, @@ -3341,6 +3376,9 @@ static const byte cannedKeyAlgoClient[] = { ID_SSH_RSA, #endif /* WOLFSSH_NO_SSH_RSA_SHA1 */ #endif /* WOLFSSH_NO_SHA1_SOFT_DISABLE */ +#ifndef WOLFSSH_NO_ED25519 + ID_ED25519, +#endif }; static const word32 cannedKeyAlgoClientSz = (word32)sizeof(cannedKeyAlgoClient); @@ -3522,7 +3560,18 @@ static INLINE enum wc_HashType HashForId(byte id) case ID_RSA_SHA2_256: return WC_HASH_TYPE_SHA256; #endif +#ifndef WOLFSSH_NO_ECDH_SHA2_ED25519 + case ID_ECDH_SHA2_ED25519: + return WC_HASH_TYPE_SHA256; + + case ID_ECDH_SHA2_ED25519_LIBSSH: + return WC_HASH_TYPE_SHA256; +#endif +#ifndef WOLFSSH_NO_ED25519 + case ID_ED25519: + return WC_HASH_TYPE_SHA512; +#endif /* SHA2-384 */ #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP384 case ID_ECDH_SHA2_NISTP384: @@ -3594,6 +3643,13 @@ static INLINE int wcPrimeForId(byte id) #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521 case ID_ECDSA_SHA2_NISTP521: return ECC_SECP521R1; +#endif +#ifndef WOLFSSH_NO_ECDH_SHA2_ED25519 + case ID_ECDH_SHA2_ED25519: + return ECC_X25519; + + case ID_ECDH_SHA2_ED25519_LIBSSH: + return ECC_X25519; #endif default: return ECC_CURVE_INVALID; @@ -3614,6 +3670,10 @@ static INLINE const char *PrimeNameForId(byte id) #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521 case ID_ECDSA_SHA2_NISTP521: return "nistp521"; +#endif +#ifndef WOLFSSH_NO_ED25519 + case ID_ED25519: + return "ed25519"; #endif default: return "unknown"; @@ -6652,6 +6712,149 @@ static int DoUserAuthRequestEccCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, #endif /* WOLFSSH_CERTS */ #endif /* ! WOLFSSH_NO_ECDSA */ +#ifndef WOLFSSH_NO_ED25519 +static int DoUserAuthRequestEd25519(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, + WS_UserAuthData* authData) +{ + const byte* publicKeyType; + byte temp[32]; + word32 publicKeyTypeSz = 0; + word32 sz, qSz; + word32 i = 0; + int ret = WS_SUCCESS; + ed25519_key *key_ptr = NULL; +#ifndef WOLFSSH_SMALL_STACK + ed25519_key s_key; +#endif + + WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestEd25519()"); + + if (ssh == NULL || ssh->ctx == NULL || pk == NULL || authData == NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { +#ifdef WOLFSSH_SMALL_STACK + key_ptr = (ecc_key*)WMALLOC(sizeof(ecc_key), ssh->ctx->heap, + DYNTYPE_PUBKEY); + if (key_ptr == NULL) + ret = WS_MEMORY_E; +#else + key_ptr = &s_key; +#endif + } + + if (ret == WS_SUCCESS) { + ret = wc_ed25519_init_ex(key_ptr, ssh->ctx->heap, INVALID_DEVID); + if (ret == 0) { + ret = WS_SUCCESS; + } + } + + /* First check that the public key's type matches the one we are + * expecting. */ + if (ret == WS_SUCCESS) + ret = GetSize(&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 = GetSize(&qSz, pk->publicKey, pk->publicKeySz, &i); + } + + if (ret == WS_SUCCESS) { + ret = wc_ed25519_import_public(pk->publicKey + i, qSz, key_ptr); + } + + 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 = GetSize(&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 = GetSize(&sz, pk->signature, pk->signatureSz, &i); + } + + if(ret == WS_SUCCESS) { + ret = wc_ed25519_verify_msg_init(pk->signature + i, sz, key_ptr, (byte)Ed25519, NULL, 0); + } + + if(ret == WS_SUCCESS) { + c32toa(ssh->sessionIdSz, temp); + ret = wc_ed25519_verify_msg_update(temp, UINT32_SZ, key_ptr); + } + + if(ret == WS_SUCCESS) { + ret = wc_ed25519_verify_msg_update(ssh->sessionId, ssh->sessionIdSz, key_ptr); + } + + if(ret == WS_SUCCESS) { + temp[0] = MSGID_USERAUTH_REQUEST; + ret = wc_ed25519_verify_msg_update(temp, MSG_ID_SZ, key_ptr); + } + + /* The rest of the fields in the signature are already + * in the buffer. Just need to account for the sizes. */ + if(ret == WS_SUCCESS) { + ret = wc_ed25519_verify_msg_update(pk->dataToSign, + authData->usernameSz + + authData->serviceNameSz + + authData->authNameSz + BOOLEAN_SZ + + pk->publicKeyTypeSz + pk->publicKeySz + + (UINT32_SZ * 5), key_ptr); + } + + if(ret == WS_SUCCESS) { + int status = 0; + ret = wc_ed25519_verify_msg_final(pk->signature + i, sz, &status, key_ptr); + if (ret != 0) { + WLOG(WS_LOG_DEBUG, "Could not verify signature"); + ret = WS_CRYPTO_FAILED; + } + else + ret = status ? WS_SUCCESS : WS_ECC_E; + } + + if (key_ptr) + wc_ed25519_free(key_ptr); +#ifdef WOLFSSH_SMALL_STACK + if (key_ptr) + WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PUBKEY); +#endif + WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestEd25519(), ret = %d", ret); + return ret; +} +#endif /* !WOLFSSH_NO_ED25519 */ #if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA) /* Utility for DoUserAuthRequest() */ @@ -6842,100 +7045,109 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, WLOG(WS_LOG_DEBUG, "DUARPK: Send the PK OK"); ret = SendUserAuthPkOk(ssh, pubKeyAlgo, pubKeyAlgoSz, pubKeyBlob, pubKeyBlobSz); - } - else { - wc_HashAlg hash; - byte digest[WC_MAX_DIGEST_SIZE]; - word32 digestSz = 0; - enum wc_HashType hashId = WC_HASH_TYPE_SHA; + } else { + if(pkTypeId == ID_ED25519) { +#ifndef WOLFSSH_NO_ED25519 + if(ret == WS_SUCCESS) + ret = DoUserAuthRequestEd25519(ssh, &authData->sf.publicKey, authData); +#else + ret = WS_CRYPTO_FAILED; +#endif + } else { + wc_HashAlg hash; + byte digest[WC_MAX_DIGEST_SIZE]; + word32 digestSz = 0; + enum wc_HashType hashId = WC_HASH_TYPE_SHA; - if (ret == WS_SUCCESS) { - hashId = HashForId(pkTypeId); - WMEMSET(digest, 0, sizeof(digest)); - ret = wc_HashGetDigestSize(hashId); - if (ret > 0) { - digestSz = ret; - ret = 0; + if (ret == WS_SUCCESS) { + hashId = HashForId(pkTypeId); + WMEMSET(digest, 0, sizeof(digest)); + ret = wc_HashGetDigestSize(hashId); + if (ret > 0) { + digestSz = ret; + ret = 0; + } } - } - if (ret == 0) - ret = wc_HashInit(&hash, hashId); + if (ret == 0) + ret = wc_HashInit(&hash, hashId); - if (ret == 0) { - c32toa(ssh->sessionIdSz, digest); - ret = HashUpdate(&hash, hashId, digest, UINT32_SZ); - } + if (ret == 0) { + c32toa(ssh->sessionIdSz, digest); + ret = HashUpdate(&hash, hashId, digest, UINT32_SZ); + } - if (ret == 0) - ret = HashUpdate(&hash, hashId, - ssh->sessionId, ssh->sessionIdSz); + if (ret == 0) + ret = HashUpdate(&hash, hashId, + ssh->sessionId, ssh->sessionIdSz); - if (ret == 0) { - digest[0] = MSGID_USERAUTH_REQUEST; - ret = HashUpdate(&hash, hashId, digest, MSG_ID_SZ); - } + if (ret == 0) { + digest[0] = MSGID_USERAUTH_REQUEST; + ret = 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, which total - * the length of the buffer minus the signature and size of - * signature. */ - if (ret == 0) { - ret = HashUpdate(&hash, hashId, - authData->sf.publicKey.dataToSign, - len - sigSz - LENGTH_SZ); - } - if (ret == 0) { - ret = wc_HashFinal(&hash, hashId, digest); + /* The rest of the fields in the signature are already + * in the buffer. Just need to account for the sizes, which total + * the length of the buffer minus the signature and size of + * signature. */ + if (ret == 0) { + ret = HashUpdate(&hash, hashId, + authData->sf.publicKey.dataToSign, + len - sigSz - LENGTH_SZ); + } + if (ret == 0) { + ret = wc_HashFinal(&hash, hashId, digest); - if (ret != 0) - ret = WS_CRYPTO_FAILED; - else - ret = WS_SUCCESS; - } - wc_HashFree(&hash, hashId); + if (ret != 0) + ret = WS_CRYPTO_FAILED; + else + ret = WS_SUCCESS; + } + wc_HashFree(&hash, hashId); - if (ret == WS_SUCCESS) { - WLOG(WS_LOG_DEBUG, "Verify user signature type: %s", - IdToName(pkTypeId)); - - switch (pkTypeId) { - #ifndef WOLFSSH_NO_RSA - case ID_SSH_RSA: - case ID_RSA_SHA2_256: - case ID_RSA_SHA2_512: - ret = DoUserAuthRequestRsa(ssh, - &authData->sf.publicKey, - hashId, digest, digestSz); - break; - #ifdef WOLFSSH_CERTS - case ID_X509V3_SSH_RSA: - ret = DoUserAuthRequestRsaCert(ssh, - &authData->sf.publicKey, - hashId, digest, digestSz); - break; - #endif - #endif - #ifndef WOLFSSH_NO_ECDSA - case ID_ECDSA_SHA2_NISTP256: - case ID_ECDSA_SHA2_NISTP384: - case ID_ECDSA_SHA2_NISTP521: - ret = DoUserAuthRequestEcc(ssh, - &authData->sf.publicKey, - hashId, digest, digestSz); - break; - #ifdef WOLFSSH_CERTS - case ID_X509V3_ECDSA_SHA2_NISTP256: - case ID_X509V3_ECDSA_SHA2_NISTP384: - case ID_X509V3_ECDSA_SHA2_NISTP521: - ret = DoUserAuthRequestEccCert(ssh, - &authData->sf.publicKey, - hashId, digest, digestSz); - break; - #endif - #endif - default: - ret = WS_INVALID_ALGO_ID; + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, "Verify user signature type: %s", + IdToName(pkTypeId)); + + switch (pkTypeId) { + #ifndef WOLFSSH_NO_RSA + case ID_SSH_RSA: + case ID_RSA_SHA2_256: + case ID_RSA_SHA2_512: + ret = DoUserAuthRequestRsa(ssh, + &authData->sf.publicKey, + hashId, digest, digestSz); + break; + #ifdef WOLFSSH_CERTS + case ID_X509V3_SSH_RSA: + ret = DoUserAuthRequestRsaCert(ssh, + &authData->sf.publicKey, + hashId, digest, digestSz); + break; + #endif + #endif + #ifndef WOLFSSH_NO_ECDSA + case ID_ECDSA_SHA2_NISTP256: + case ID_ECDSA_SHA2_NISTP384: + case ID_ECDSA_SHA2_NISTP521: + case ID_ED25519: + ret = DoUserAuthRequestEcc(ssh, + &authData->sf.publicKey, + hashId, digest, digestSz); + break; + #ifdef WOLFSSH_CERTS + case ID_X509V3_ECDSA_SHA2_NISTP256: + case ID_X509V3_ECDSA_SHA2_NISTP384: + case ID_X509V3_ECDSA_SHA2_NISTP521: + ret = DoUserAuthRequestEccCert(ssh, + &authData->sf.publicKey, + hashId, digest, digestSz); + break; + #endif + #endif + default: + ret = WS_INVALID_ALGO_ID; + } } } @@ -9560,6 +9772,18 @@ struct wolfSSH_sigKeyBlockFull { const char *primeName; word32 primeNameSz; } ecc; + +#ifndef WOLFSSH_NO_ED25519 + struct { + ed25519_key key; + word32 keyBlobSz; + const char *keyBlobName; + word32 keyBlobNameSz; + byte q[ED25519_PUB_KEY_SIZE+1]; + word32 qSz; + byte qPad; + } ed; +#endif #endif } sk; }; @@ -9918,6 +10142,58 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, sigKeyBlock_ptr->sk.ecc.qSz); } break; + + #ifndef WOLFSSH_NO_ED25519 + case ID_ED25519: + WLOG(WS_LOG_DEBUG, "Using Ed25519 Host key"); + + /* Decode the user-configured ED25519 private key. */ + sigKeyBlock_ptr->sk.ed.qSz = sizeof(sigKeyBlock_ptr->sk.ed.q); + + ret = wc_ed25519_init(&sigKeyBlock_ptr->sk.ed.key); + + scratch = 0; + if (ret == 0) + ret = wc_Ed25519PrivateKeyDecode(ssh->ctx->privateKey[keyIdx].key, &scratch, &sigKeyBlock_ptr->sk.ed.key, ssh->ctx->privateKey[keyIdx].keySz); + + if (ret == 0) + ret = wc_ed25519_export_public(&sigKeyBlock_ptr->sk.ed.key, + sigKeyBlock_ptr->sk.ed.q, + &sigKeyBlock_ptr->sk.ed.qSz ); + + /* Hash in the length of the public key block. */ + if (ret == 0) { + sigKeyBlock_ptr->sz = (LENGTH_SZ * 2) + + sigKeyBlock_ptr->pubKeyFmtNameSz + + sigKeyBlock_ptr->sk.ed.qSz; + c32toa(sigKeyBlock_ptr->sz, scratchLen); + ret = wc_HashUpdate(hash, hashId, + scratchLen, LENGTH_SZ); + } + /* Hash in the length of the key type string. */ + if (ret == 0) { + c32toa(sigKeyBlock_ptr->pubKeyFmtNameSz, scratchLen); + ret = wc_HashUpdate(hash, hashId, + scratchLen, LENGTH_SZ); + } + /* Hash in the key type string. */ + if (ret == 0) + ret = wc_HashUpdate(hash, hashId, + (byte*)sigKeyBlock_ptr->pubKeyFmtName, + sigKeyBlock_ptr->pubKeyFmtNameSz); + /* Hash in the length of the public key. */ + if (ret == 0) { + c32toa(sigKeyBlock_ptr->sk.ed.qSz, scratchLen); + ret = wc_HashUpdate(hash, hashId, + scratchLen, LENGTH_SZ); + } + /* Hash in the public key. */ + if (ret == 0) + ret = wc_HashUpdate(hash, hashId, + sigKeyBlock_ptr->sk.ed.q, + sigKeyBlock_ptr->sk.ed.qSz); + break; + #endif #endif default: @@ -10757,140 +11033,154 @@ int SendKexDhReply(WOLFSSH* ssh) /* Sign h with the server's private key. */ if (ret == WS_SUCCESS) { - wc_HashAlg digestHash; - byte digest[WC_MAX_DIGEST_SIZE]; - enum wc_HashType sigHashId; + if (sigKeyBlock_ptr->pubKeyId == ID_ED25519) { +#ifndef WOLFSSH_NO_ED25519 + WLOG(WS_LOG_INFO, "Signing hash with %s.", IdToName(ssh->handshake->pubKeyId)); - sigHashId = HashForId(ssh->handshake->pubKeyId); - ret = wc_HashInit(&digestHash, sigHashId); - if (ret == 0) - ret = HashUpdate(&digestHash, sigHashId, ssh->h, ssh->hSz); - if (ret == 0) - ret = wc_HashFinal(&digestHash, sigHashId, digest); - if (ret != 0) - ret = WS_CRYPTO_FAILED; - wc_HashFree(&digestHash, sigHashId); + sigSz = KEX_SIG_SIZE; + ret = wc_ed25519_sign_msg(ssh->h, ssh->hSz, + sig_ptr, &sigSz, &sigKeyBlock_ptr->sk.ed.key); + if (ret != 0) { + WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad ED25519 Sign (error: %d)", ret); + ret = WS_ECC_E; + } +#endif + } else { + wc_HashAlg digestHash; + byte digest[WC_MAX_DIGEST_SIZE]; + enum wc_HashType sigHashId; - if (ret == WS_SUCCESS) { - if (sigKeyBlock_ptr->pubKeyId == ID_SSH_RSA - #ifdef WOLFSSH_CERTS - || sigKeyBlock_ptr->pubKeyId == ID_X509V3_SSH_RSA - #endif - || sigKeyBlock_ptr->pubKeyId == ID_RSA_SHA2_256 - || sigKeyBlock_ptr->pubKeyId == ID_RSA_SHA2_512 - ) { -#ifndef WOLFSSH_NO_RSA - word32 encSigSz; - #ifdef WOLFSSH_SMALL_STACK - byte *encSig = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, heap, - DYNTYPE_TEMP); - if (encSig == NULL) { - ret = WS_MEMORY_E; - } + sigHashId = HashForId(ssh->handshake->pubKeyId); + ret = wc_HashInit(&digestHash, sigHashId); + if (ret == 0) + ret = HashUpdate(&digestHash, sigHashId, ssh->h, ssh->hSz); + if (ret == 0) + ret = wc_HashFinal(&digestHash, sigHashId, digest); + if (ret != 0) + ret = WS_CRYPTO_FAILED; + wc_HashFree(&digestHash, sigHashId); - if (ret == WS_SUCCESS) - #else - byte encSig[MAX_ENCODED_SIG_SZ]; + if (ret == WS_SUCCESS) { + if (sigKeyBlock_ptr->pubKeyId == ID_SSH_RSA + #ifdef WOLFSSH_CERTS + || sigKeyBlock_ptr->pubKeyId == ID_X509V3_SSH_RSA #endif - { - 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; + || sigKeyBlock_ptr->pubKeyId == ID_RSA_SHA2_256 + || sigKeyBlock_ptr->pubKeyId == ID_RSA_SHA2_512 + ) { + #ifndef WOLFSSH_NO_RSA + word32 encSigSz; + #ifdef WOLFSSH_SMALL_STACK + byte *encSig = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, heap, + DYNTYPE_TEMP); + if (encSig == NULL) { + ret = WS_MEMORY_E; } - else { - WLOG(WS_LOG_INFO, "Signing hash with %s.", - IdToName(ssh->handshake->pubKeyId)); - sigSz = wc_RsaSSL_Sign(encSig, encSigSz, sig_ptr, - KEX_SIG_SIZE, &sigKeyBlock_ptr->sk.rsa.key, - ssh->rng); - if (sigSz <= 0) { - WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad RSA Sign"); - ret = WS_RSA_E; + + if (ret == WS_SUCCESS) + #else + byte encSig[MAX_ENCODED_SIG_SZ]; + #endif + { + 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 { - ret = wolfSSH_RsaVerify(sig_ptr, sigSz, - encSig, encSigSz, - &sigKeyBlock_ptr->sk.rsa.key, - heap, "SendKexDhReply"); + WLOG(WS_LOG_INFO, "Signing hash with %s.", + IdToName(ssh->handshake->pubKeyId)); + sigSz = wc_RsaSSL_Sign(encSig, encSigSz, sig_ptr, + KEX_SIG_SIZE, &sigKeyBlock_ptr->sk.rsa.key, + ssh->rng); + if (sigSz <= 0) { + WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad RSA Sign"); + ret = WS_RSA_E; + } + else { + ret = wolfSSH_RsaVerify(sig_ptr, sigSz, + encSig, encSigSz, + &sigKeyBlock_ptr->sk.rsa.key, + heap, "SendKexDhReply"); + } } - } - #ifdef WOLFSSH_SMALL_STACK - WFREE(encSig, heap, DYNTYPE_TEMP); - #endif - } -#endif /* WOLFSSH_NO_RSA */ - } - else if (sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP256 - || sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP384 - || sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP521 -#ifdef WOLFSSH_CERTS - || sigKeyBlock_ptr->pubKeyId == ID_X509V3_ECDSA_SHA2_NISTP256 - || sigKeyBlock_ptr->pubKeyId == ID_X509V3_ECDSA_SHA2_NISTP384 - || sigKeyBlock_ptr->pubKeyId == ID_X509V3_ECDSA_SHA2_NISTP521 -#endif - ) { -#ifndef WOLFSSH_NO_ECDSA - WLOG(WS_LOG_INFO, "Signing hash with %s.", - IdToName(ssh->handshake->pubKeyId)); - sigSz = KEX_SIG_SIZE; - ret = wc_ecc_sign_hash(digest, wc_HashGetDigestSize(sigHashId), - sig_ptr, &sigSz, - ssh->rng, &sigKeyBlock_ptr->sk.ecc.key); - if (ret != MP_OKAY) { - WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad ECDSA Sign"); - ret = WS_ECC_E; - } - else { - byte *r_ptr = NULL, *s_ptr = NULL; - word32 rSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ; - word32 sSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ; - byte rPad; - byte sPad; - #ifdef WOLFSSH_SMALL_STACK - r_ptr = (byte*)WMALLOC(rSz, heap, DYNTYPE_BUFFER); - s_ptr = (byte*)WMALLOC(sSz, heap, DYNTYPE_BUFFER); - if (r_ptr == NULL || s_ptr == NULL) - ret = WS_MEMORY_E; - #else - byte r_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ]; - byte s_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ]; - r_ptr = r_s; - s_ptr = s_s; + WFREE(encSig, heap, DYNTYPE_TEMP); #endif - if (ret == WS_SUCCESS) { - ret = wc_ecc_sig_to_rs(sig_ptr, sigSz, - r_ptr, &rSz, s_ptr, &sSz); + } + #endif /* WOLFSSH_NO_RSA */ + } + else if (sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP256 + || sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP384 + || sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP521 + #ifdef WOLFSSH_CERTS + || sigKeyBlock_ptr->pubKeyId == ID_X509V3_ECDSA_SHA2_NISTP256 + || sigKeyBlock_ptr->pubKeyId == ID_X509V3_ECDSA_SHA2_NISTP384 + || sigKeyBlock_ptr->pubKeyId == ID_X509V3_ECDSA_SHA2_NISTP521 + #endif + ) { + #ifndef WOLFSSH_NO_ECDSA + WLOG(WS_LOG_INFO, "Signing hash with %s.", + IdToName(ssh->handshake->pubKeyId)); + sigSz = KEX_SIG_SIZE; + ret = wc_ecc_sign_hash(digest, wc_HashGetDigestSize(sigHashId), + sig_ptr, &sigSz, + ssh->rng, &sigKeyBlock_ptr->sk.ecc.key); + if (ret != MP_OKAY) { + WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad ECDSA Sign"); + ret = WS_ECC_E; } - if (ret == 0) { - idx = 0; - rPad = (r_ptr[0] & 0x80) ? 1 : 0; - sPad = (s_ptr[0] & 0x80) ? 1 : 0; - sigSz = (LENGTH_SZ * 2) + rSz + rPad + sSz + sPad; - - c32toa(rSz + rPad, sig_ptr + idx); - idx += LENGTH_SZ; - if (rPad) - sig_ptr[idx++] = 0; - WMEMCPY(sig_ptr + idx, r_ptr, rSz); - idx += rSz; - c32toa(sSz + sPad, sig_ptr + idx); - idx += LENGTH_SZ; - if (sPad) - sig_ptr[idx++] = 0; - WMEMCPY(sig_ptr + idx, s_ptr, sSz); + else { + byte *r_ptr = NULL, *s_ptr = NULL; + word32 rSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ; + word32 sSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ; + byte rPad; + byte sPad; + + #ifdef WOLFSSH_SMALL_STACK + r_ptr = (byte*)WMALLOC(rSz, heap, DYNTYPE_BUFFER); + s_ptr = (byte*)WMALLOC(sSz, heap, DYNTYPE_BUFFER); + if (r_ptr == NULL || s_ptr == NULL) + ret = WS_MEMORY_E; + #else + byte r_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ]; + byte s_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ]; + r_ptr = r_s; + s_ptr = s_s; + #endif + if (ret == WS_SUCCESS) { + ret = wc_ecc_sig_to_rs(sig_ptr, sigSz, + r_ptr, &rSz, s_ptr, &sSz); + } + if (ret == 0) { + idx = 0; + rPad = (r_ptr[0] & 0x80) ? 1 : 0; + sPad = (s_ptr[0] & 0x80) ? 1 : 0; + sigSz = (LENGTH_SZ * 2) + rSz + rPad + sSz + sPad; + + c32toa(rSz + rPad, sig_ptr + idx); + idx += LENGTH_SZ; + if (rPad) + sig_ptr[idx++] = 0; + WMEMCPY(sig_ptr + idx, r_ptr, rSz); + idx += rSz; + c32toa(sSz + sPad, sig_ptr + idx); + idx += LENGTH_SZ; + if (sPad) + sig_ptr[idx++] = 0; + WMEMCPY(sig_ptr + idx, s_ptr, sSz); + } + #ifdef WOLFSSH_SMALL_STACK + if (r_ptr) + WFREE(r_ptr, heap, DYNTYPE_BUFFER); + if (s_ptr) + WFREE(s_ptr, heap, DYNTYPE_BUFFER); + #endif } - #ifdef WOLFSSH_SMALL_STACK - if (r_ptr) - WFREE(r_ptr, heap, DYNTYPE_BUFFER); - if (s_ptr) - WFREE(s_ptr, heap, DYNTYPE_BUFFER); - #endif + #endif /* WOLFSSH_NO_ECDSA */ } -#endif /* WOLFSSH_NO_ECDSA */ } } } @@ -10906,6 +11196,10 @@ int SendKexDhReply(WOLFSSH* ssh) || sigKeyBlock_ptr->pubKeyFmtId == ID_ECDSA_SHA2_NISTP521) { #ifndef WOLFSSH_NO_ECDSA wc_ecc_free(&sigKeyBlock_ptr->sk.ecc.key); +#endif + } else if (sigKeyBlock_ptr->pubKeyId == ID_ED25519) { +#if !defined(WOLFSSH_NO_ED25519) + wc_ed25519_free(&sigKeyBlock_ptr->sk.ed.key); #endif } } @@ -10930,21 +11224,22 @@ int SendKexDhReply(WOLFSSH* ssh) output[idx++] = msgId; + /* Copy the key block size into the buffer */ + c32toa(sigKeyBlock_ptr->sz, output + idx); + idx += LENGTH_SZ; + + /* Copy the key name into the buffer */ + c32toa(sigKeyBlock_ptr->pubKeyFmtNameSz, output + idx); + idx += LENGTH_SZ; + WMEMCPY(output + idx, sigKeyBlock_ptr->pubKeyFmtName, sigKeyBlock_ptr->pubKeyFmtNameSz); + idx += sigKeyBlock_ptr->pubKeyFmtNameSz; + /* add host public key */ switch (sigKeyBlock_ptr->pubKeyFmtId) { case ID_SSH_RSA: { #ifndef WOLFSSH_NO_RSA /* Copy the rsaKeyBlock into the buffer. */ - c32toa(sigKeyBlock_ptr->sz, output + idx); - idx += LENGTH_SZ; - c32toa(sigKeyBlock_ptr->pubKeyFmtNameSz, output + idx); - idx += LENGTH_SZ; - WMEMCPY(output + idx, - sigKeyBlock_ptr->pubKeyFmtName, - sigKeyBlock_ptr->pubKeyFmtNameSz); - idx += sigKeyBlock_ptr->pubKeyFmtNameSz; - c32toa(sigKeyBlock_ptr->sk.rsa.eSz + sigKeyBlock_ptr->sk.rsa.ePad, output + idx); idx += LENGTH_SZ; @@ -10969,15 +11264,6 @@ int SendKexDhReply(WOLFSSH* ssh) { #ifndef WOLFSSH_NO_ECDSA /* Copy the ecdsaKeyBlock into the buffer. */ - c32toa(sigKeyBlock_ptr->sz, output + idx); - idx += LENGTH_SZ; - c32toa(sigKeyBlock_ptr->pubKeyFmtNameSz, output + idx); - idx += LENGTH_SZ; - WMEMCPY(output + idx, - sigKeyBlock_ptr->pubKeyFmtName, - sigKeyBlock_ptr->pubKeyFmtNameSz); - idx += sigKeyBlock_ptr->pubKeyFmtNameSz; - c32toa(sigKeyBlock_ptr->sk.ecc.primeNameSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, sigKeyBlock_ptr->sk.ecc.primeName, @@ -10992,6 +11278,19 @@ int SendKexDhReply(WOLFSSH* ssh) } break; + case ID_ED25519: + { +#if !defined(WOLFSSH_NO_ED25519) + /* Copy the edKeyBlock into the buffer. */ + c32toa(sigKeyBlock_ptr->sk.ed.qSz, output + idx); + idx += LENGTH_SZ; + WMEMCPY(output + idx, sigKeyBlock_ptr->sk.ed.q, + sigKeyBlock_ptr->sk.ed.qSz); + idx += sigKeyBlock_ptr->sk.ed.qSz; +#endif + } + break; + #ifdef WOLFSSH_CERTS case ID_X509V3_SSH_RSA: case ID_X509V3_ECDSA_SHA2_NISTP256: diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 8bfc95638..3742340ca 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -105,6 +105,13 @@ extern "C" { #define WOLFSSH_NO_SHA1 #endif +#if !defined(HAVE_ED25519) \ + || !defined(WOLFSSL_ED25519_STREAMING_VERIFY) \ + || !defined(HAVE_ED25519_KEY_IMPORT) \ + || !defined(HAVE_ED25519_KEY_EXPORT) + #undef WOLFSSH_NO_ED25519 + #define WOLFSSH_NO_ED25519 +#endif #if defined(NO_HMAC) || defined(WOLFSSH_NO_SHA1) #undef WOLFSSH_NO_HMAC_SHA1 @@ -152,8 +159,7 @@ extern "C" { #undef WOLFSSH_NO_ECDH_SHA2_NISTP521 #define WOLFSSH_NO_ECDH_SHA2_NISTP521 #endif -#if !defined(HAVE_ED25519) || defined(NO_SHA256) || 1 - /* ED25519 isn't supported yet. Force disabled. */ +#if !defined(HAVE_CURVE25519) || defined(NO_SHA256) #undef WOLFSSH_NO_ECDH_SHA2_ED25519 #define WOLFSSH_NO_ECDH_SHA2_ED25519 #endif @@ -187,7 +193,8 @@ extern "C" { #endif #if defined(WOLFSSH_NO_ECDH_SHA2_NISTP256) && \ defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \ - defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) + defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \ + defined(WOLFSSH_NO_ECDH_SHA2_ED25519) #undef WOLFSSH_NO_ECDH #define WOLFSSH_NO_ECDH #endif @@ -225,7 +232,8 @@ extern "C" { defined(WOLFSSH_NO_RSA_SHA2_512) && \ defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && \ defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && \ - defined(WOLFSSH_NO_ECDSA_SHA2_NISTP521) + defined(WOLFSSH_NO_ECDSA_SHA2_NISTP521) && \ + defined(WOLFSSH_NO_ED25519) #error "You need at least one signing algorithm." #endif @@ -237,7 +245,8 @@ extern "C" { #endif #if defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && \ defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && \ - defined(WOLFSSH_NO_ECDSA_SHA2_NISTP521) + defined(WOLFSSH_NO_ECDSA_SHA2_NISTP521) && \ + !defined(HAVE_ED25519) #undef WOLFSSH_NO_ECDSA #define WOLFSSH_NO_ECDSA #endif @@ -328,6 +337,7 @@ enum { ID_ECDSA_SHA2_NISTP256, ID_ECDSA_SHA2_NISTP384, ID_ECDSA_SHA2_NISTP521, + ID_ED25519, ID_X509V3_SSH_RSA, ID_X509V3_ECDSA_SHA2_NISTP256, ID_X509V3_ECDSA_SHA2_NISTP384, From 3602bc10cac62266be344a080ac00a2ec74904e0 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Wed, 8 May 2024 15:11:55 -0700 Subject: [PATCH 2/5] Add Ed25519 1. Whitespace fixes. 2. Refactor the signing KEX message signing to break the signing into functions depending on the key type. --- src/internal.c | 400 ++++++++++++++++++++++++++++++------------------- 1 file changed, 249 insertions(+), 151 deletions(-) diff --git a/src/internal.c b/src/internal.c index f0b106a80..f2125eda2 100644 --- a/src/internal.c +++ b/src/internal.c @@ -10793,6 +10793,252 @@ static int KeyAgreeEcdhKyber1_server(WOLFSSH* ssh, byte hashId, #endif /* WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */ +static int SignHRsa(WOLFSSH* ssh, byte* sig, word32* sigSz, + struct wolfSSH_sigKeyBlockFull *sigKey) +#ifndef WOLFSSH_NO_RSA +{ + void* heap; + byte* encSig = NULL; + byte digest[WC_MAX_DIGEST_SIZE]; + word32 digestSz = (word32)sizeof(digest); + word32 encSigSz; + int ret = WS_SUCCESS; + enum wc_HashType hashId; +#ifndef WOLFSSH_SMALL_STACK + byte encSig_s[MAX_ENCODED_SIG_SZ]; +#endif + + WLOG(WS_LOG_DEBUG, "Entering SignHRsa()"); + + heap = ssh->ctx->heap; +#ifdef WOLFSSH_SMALL_STACK + encSig = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, heap, DYNTYPE_TEMP); + if (encSig == NULL) { + ret = WS_MEMORY_E; + } +#else + encSig = encSig_s; +#endif + + if (ret == WS_SUCCESS) { + hashId = HashForId(ssh->handshake->pubKeyId); + digestSz = wc_HashGetDigestSize(hashId); + + ret = wc_Hash(hashId, ssh->h, ssh->hSz, digest, digestSz); + if (ret != 0) { + ret = WS_CRYPTO_FAILED; + } + } + + if (ret == WS_SUCCESS) { + encSigSz = wc_EncodeSignature(encSig, digest, digestSz, + wc_HashGetOID(hashId)); + if (encSigSz <= 0) { + WLOG(WS_LOG_DEBUG, "SignHRsa: Bad Encode Sig"); + ret = WS_CRYPTO_FAILED; + } + } + + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_INFO, "Signing hash with %s.", + IdToName(ssh->handshake->pubKeyId)); + *sigSz = wc_RsaSSL_Sign(encSig, encSigSz, sig, + KEX_SIG_SIZE, &sigKey->sk.rsa.key, + ssh->rng); + if (*sigSz <= 0) { + WLOG(WS_LOG_DEBUG, "SignHRsa: Bad RSA Sign"); + ret = WS_RSA_E; + } + } + + if (ret == WS_SUCCESS) { + ret = wolfSSH_RsaVerify(sig, *sigSz, encSig, encSigSz, + &sigKey->sk.rsa.key, heap, "SignHRsa"); + } + + #ifdef WOLFSSH_SMALL_STACK + if (encSig != NULL) + WFREE(encSig, heap, DYNTYPE_TEMP); + #endif + WLOG(WS_LOG_DEBUG, "Leaving SignHRsa(), ret = %d", ret); + return ret; +} +#else /* WOLFSSH_NO_RSA */ +{ + WOLFSSH_UNUSED(ssh); + WOLFSSH_UNUSED(sig); + WOLFSSH_UNUSED(sigSz); + WOLFSSH_UNUSED(sigKey); + return WS_INVALID_ALGO_ID; +} +#endif /* WOLFSSH_NO_RSA */ + + +static int SignHEcdsa(WOLFSSH* ssh, byte* sig, word32* sigSz, + struct wolfSSH_sigKeyBlockFull *sigKey) +#ifndef WOLFSSH_NO_ECDSA +{ +#ifdef WOLFSSH_SMALL_STACK + void* heap = NULL; +#endif + byte *r = NULL, *s = NULL; + byte digest[WC_MAX_DIGEST_SIZE]; + word32 digestSz = (word32)sizeof(digest); + int ret = WS_SUCCESS; + enum wc_HashType hashId; + word32 rSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ, + sSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ; + byte rPad, sPad; +#ifndef WOLFSSH_SMALL_STACK + byte r_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ]; + byte s_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ]; +#endif + + WLOG(WS_LOG_DEBUG, "Entering SignHEcdsa()"); + + hashId = HashForId(ssh->handshake->pubKeyId); + digestSz = wc_HashGetDigestSize(hashId); + + ret = wc_Hash(hashId, ssh->h, ssh->hSz, digest, digestSz); + if (ret != 0) { + ret = WS_CRYPTO_FAILED; + } + + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_INFO, "Signing hash with %s.", + IdToName(ssh->handshake->pubKeyId)); + ret = wc_ecc_sign_hash(digest, digestSz, sig, sigSz, ssh->rng, + &sigKey->sk.ecc.key); + if (ret != MP_OKAY) { + WLOG(WS_LOG_DEBUG, "SignHEcdsa: Bad ECDSA Sign"); + ret = WS_ECC_E; + } + else { + ret = WS_SUCCESS; + } + } + + if (ret == WS_SUCCESS) { +#ifdef WOLFSSH_SMALL_STACK + heap = ssh->ctx->heap; + r = (byte*)WMALLOC(rSz, heap, DYNTYPE_BUFFER); + s = (byte*)WMALLOC(sSz, heap, DYNTYPE_BUFFER); + if (r == NULL || s == NULL) { + ret = WS_MEMORY_E; + } +#else + r = r_s; + s = s_s; +#endif + } + + if (ret == WS_SUCCESS) { + ret = wc_ecc_sig_to_rs(sig, *sigSz, r, &rSz, s, &sSz); + if (ret != 0) { + ret = WS_ECC_E; + } + } + + if (ret == WS_SUCCESS) { + int 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); + } + + #ifdef WOLFSSH_SMALL_STACK + if (r) + WFREE(r, heap, DYNTYPE_BUFFER); + if (s) + WFREE(s, heap, DYNTYPE_BUFFER); + #endif + + WLOG(WS_LOG_DEBUG, "Leaving SignHEcdsa(), ret = %d", ret); + return ret; +} +#else /* WOLFSSH_NO_ECDSA */ +{ + WOLFSSH_UNUSED(ssh); + WOLFSSH_UNUSED(sig); + WOLFSSH_UNUSED(sigSz); + WOLFSSH_UNUSED(sigKey); + return WS_INVALID_ALGO_ID; +} +#endif /* WOLFSSH_NO_ECDSA */ + + +static int SignHEd25519(WOLFSSH* ssh, byte* sig, word32* sigSz, + struct wolfSSH_sigKeyBlockFull *sigKey) +#ifndef WOLFSSH_NO_ED25519 +{ + int ret; + + WLOG(WS_LOG_DEBUG, "Entering SignHEd25519()"); + + ret = wc_ed25519_sign_msg(ssh->h, ssh->hSz, sig, sigSz, &sigKey->sk.ed.key); + if (ret != 0) { + WLOG(WS_LOG_DEBUG, + "SignHEd5519: Bad ED25519 Sign (error: %d)", ret); + ret = WS_ECC_E; + } + + WLOG(WS_LOG_DEBUG, "Leaving SignHEd25519(), ret = %d", ret); + return ret; +} +#else /* WOLFSSH_NO_ED25519 */ +{ + WOLFSSH_UNUSED(ssh); + WOLFSSH_UNUSED(sig); + WOLFSSH_UNUSED(sigSz); + WOLFSSH_UNUSED(sigKey); + return WS_INVALID_ALGO_ID; +} +#endif /* WOLFSSH_NO_ED25519 */ + + +static int SignH(WOLFSSH* ssh, byte* sig, word32* sigSz, + struct wolfSSH_sigKeyBlockFull *sigKey) +{ + int ret; + + switch (sigKey->pubKeyId) { + case ID_SSH_RSA: + case ID_X509V3_SSH_RSA: + case ID_RSA_SHA2_256: + case ID_RSA_SHA2_512: + ret = SignHRsa(ssh, sig, sigSz, sigKey); + break; + case ID_ECDSA_SHA2_NISTP256: + case ID_ECDSA_SHA2_NISTP384: + case ID_ECDSA_SHA2_NISTP521: + case ID_X509V3_ECDSA_SHA2_NISTP256: + case ID_X509V3_ECDSA_SHA2_NISTP384: + case ID_X509V3_ECDSA_SHA2_NISTP521: + ret = SignHEcdsa(ssh, sig, sigSz, sigKey); + break; + case ID_ED25519: + ret = SignHEd25519(ssh, sig, sigSz, sigKey); + break; + default: + ret = WS_INVALID_ALGO_ID; + } + + 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 @@ -11033,156 +11279,7 @@ int SendKexDhReply(WOLFSSH* ssh) /* Sign h with the server's private key. */ if (ret == WS_SUCCESS) { - if (sigKeyBlock_ptr->pubKeyId == ID_ED25519) { -#ifndef WOLFSSH_NO_ED25519 - WLOG(WS_LOG_INFO, "Signing hash with %s.", IdToName(ssh->handshake->pubKeyId)); - - sigSz = KEX_SIG_SIZE; - ret = wc_ed25519_sign_msg(ssh->h, ssh->hSz, - sig_ptr, &sigSz, &sigKeyBlock_ptr->sk.ed.key); - if (ret != 0) { - WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad ED25519 Sign (error: %d)", ret); - ret = WS_ECC_E; - } -#endif - } else { - wc_HashAlg digestHash; - byte digest[WC_MAX_DIGEST_SIZE]; - enum wc_HashType sigHashId; - - sigHashId = HashForId(ssh->handshake->pubKeyId); - ret = wc_HashInit(&digestHash, sigHashId); - if (ret == 0) - ret = HashUpdate(&digestHash, sigHashId, ssh->h, ssh->hSz); - if (ret == 0) - ret = wc_HashFinal(&digestHash, sigHashId, digest); - if (ret != 0) - ret = WS_CRYPTO_FAILED; - wc_HashFree(&digestHash, sigHashId); - - if (ret == WS_SUCCESS) { - if (sigKeyBlock_ptr->pubKeyId == ID_SSH_RSA - #ifdef WOLFSSH_CERTS - || sigKeyBlock_ptr->pubKeyId == ID_X509V3_SSH_RSA - #endif - || sigKeyBlock_ptr->pubKeyId == ID_RSA_SHA2_256 - || sigKeyBlock_ptr->pubKeyId == ID_RSA_SHA2_512 - ) { - #ifndef WOLFSSH_NO_RSA - word32 encSigSz; - #ifdef WOLFSSH_SMALL_STACK - byte *encSig = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, heap, - DYNTYPE_TEMP); - if (encSig == NULL) { - ret = WS_MEMORY_E; - } - - if (ret == WS_SUCCESS) - #else - byte encSig[MAX_ENCODED_SIG_SZ]; - #endif - { - 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 %s.", - IdToName(ssh->handshake->pubKeyId)); - sigSz = wc_RsaSSL_Sign(encSig, encSigSz, sig_ptr, - KEX_SIG_SIZE, &sigKeyBlock_ptr->sk.rsa.key, - ssh->rng); - if (sigSz <= 0) { - WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad RSA Sign"); - ret = WS_RSA_E; - } - else { - ret = wolfSSH_RsaVerify(sig_ptr, sigSz, - encSig, encSigSz, - &sigKeyBlock_ptr->sk.rsa.key, - heap, "SendKexDhReply"); - } - } - #ifdef WOLFSSH_SMALL_STACK - WFREE(encSig, heap, DYNTYPE_TEMP); - #endif - } - #endif /* WOLFSSH_NO_RSA */ - } - else if (sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP256 - || sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP384 - || sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP521 - #ifdef WOLFSSH_CERTS - || sigKeyBlock_ptr->pubKeyId == ID_X509V3_ECDSA_SHA2_NISTP256 - || sigKeyBlock_ptr->pubKeyId == ID_X509V3_ECDSA_SHA2_NISTP384 - || sigKeyBlock_ptr->pubKeyId == ID_X509V3_ECDSA_SHA2_NISTP521 - #endif - ) { - #ifndef WOLFSSH_NO_ECDSA - WLOG(WS_LOG_INFO, "Signing hash with %s.", - IdToName(ssh->handshake->pubKeyId)); - sigSz = KEX_SIG_SIZE; - ret = wc_ecc_sign_hash(digest, wc_HashGetDigestSize(sigHashId), - sig_ptr, &sigSz, - ssh->rng, &sigKeyBlock_ptr->sk.ecc.key); - if (ret != MP_OKAY) { - WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad ECDSA Sign"); - ret = WS_ECC_E; - } - else { - byte *r_ptr = NULL, *s_ptr = NULL; - word32 rSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ; - word32 sSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ; - byte rPad; - byte sPad; - - #ifdef WOLFSSH_SMALL_STACK - r_ptr = (byte*)WMALLOC(rSz, heap, DYNTYPE_BUFFER); - s_ptr = (byte*)WMALLOC(sSz, heap, DYNTYPE_BUFFER); - if (r_ptr == NULL || s_ptr == NULL) - ret = WS_MEMORY_E; - #else - byte r_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ]; - byte s_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ]; - r_ptr = r_s; - s_ptr = s_s; - #endif - if (ret == WS_SUCCESS) { - ret = wc_ecc_sig_to_rs(sig_ptr, sigSz, - r_ptr, &rSz, s_ptr, &sSz); - } - if (ret == 0) { - idx = 0; - rPad = (r_ptr[0] & 0x80) ? 1 : 0; - sPad = (s_ptr[0] & 0x80) ? 1 : 0; - sigSz = (LENGTH_SZ * 2) + rSz + rPad + sSz + sPad; - - c32toa(rSz + rPad, sig_ptr + idx); - idx += LENGTH_SZ; - if (rPad) - sig_ptr[idx++] = 0; - WMEMCPY(sig_ptr + idx, r_ptr, rSz); - idx += rSz; - c32toa(sSz + sPad, sig_ptr + idx); - idx += LENGTH_SZ; - if (sPad) - sig_ptr[idx++] = 0; - WMEMCPY(sig_ptr + idx, s_ptr, sSz); - } - #ifdef WOLFSSH_SMALL_STACK - if (r_ptr) - WFREE(r_ptr, heap, DYNTYPE_BUFFER); - if (s_ptr) - WFREE(s_ptr, heap, DYNTYPE_BUFFER); - #endif - } - #endif /* WOLFSSH_NO_ECDSA */ - } - } - } + ret = SignH(ssh, sig_ptr, &sigSz, sigKeyBlock_ptr); } if (sigKeyBlock_ptr != NULL) { @@ -11197,7 +11294,8 @@ int SendKexDhReply(WOLFSSH* ssh) #ifndef WOLFSSH_NO_ECDSA wc_ecc_free(&sigKeyBlock_ptr->sk.ecc.key); #endif - } else if (sigKeyBlock_ptr->pubKeyId == ID_ED25519) { + } + else if (sigKeyBlock_ptr->pubKeyId == ID_ED25519) { #if !defined(WOLFSSH_NO_ED25519) wc_ed25519_free(&sigKeyBlock_ptr->sk.ed.key); #endif From 8f61e26819e0f869991de5c4e7f9409a921b64e3 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Fri, 10 May 2024 11:09:16 -0700 Subject: [PATCH 3/5] Add Ed25519 1. Remove some redundant (and incorrect) scaffolding for a couple algorithms. 2. Whitespace fixes and add some braces to if-else blocks. 3. Fix allocating an ed25519 key, instead of using an ecc_key for it. 4. Replace a crypto failure error with an invalid algo error. --- src/internal.c | 74 ++++++++++++++++++---------------------------- wolfssh/internal.h | 8 +---- 2 files changed, 29 insertions(+), 53 deletions(-) diff --git a/src/internal.c b/src/internal.c index f2125eda2..866a30444 100644 --- a/src/internal.c +++ b/src/internal.c @@ -104,9 +104,6 @@ WOLFSSH_NO_ECDH_SHA2_NISTP521 Set when ECC or SHA2-512 are disabled. Set to disable use of ECDHE key exchange with prime NISTP521. - WOLFSSH_NO_ECDH_SHA2_ED25519 - Set when ED25519 or SHA2-256 are disabled. Set to disable use of ECDHE key - exchange with prime ED25519. (It just decodes the ID for output.) WOLFSSH_NO_RSA Set when RSA is disabled. Set to disable use of RSA server and user authentication. @@ -2303,12 +2300,6 @@ static const NameIdPair NameIdMap[] = { #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521 { ID_ECDH_SHA2_NISTP521, TYPE_KEX, "ecdh-sha2-nistp521" }, #endif -#if 0 -#ifndef WOLFSSH_NO_ECDH_SHA2_ED25519 - { ID_ECDH_SHA2_ED25519, TYPE_KEX, "curve25519-sha256" }, - { ID_ECDH_SHA2_ED25519_LIBSSH, TYPE_KEX, "curve25519-sha256@libssh.org" }, -#endif -#endif #ifndef WOLFSSH_NO_DH_GEX_SHA256 { ID_DH_GROUP14_SHA256, TYPE_KEX, "diffie-hellman-group14-sha256" }, #endif @@ -3560,13 +3551,6 @@ static INLINE enum wc_HashType HashForId(byte id) case ID_RSA_SHA2_256: return WC_HASH_TYPE_SHA256; #endif -#ifndef WOLFSSH_NO_ECDH_SHA2_ED25519 - case ID_ECDH_SHA2_ED25519: - return WC_HASH_TYPE_SHA256; - - case ID_ECDH_SHA2_ED25519_LIBSSH: - return WC_HASH_TYPE_SHA256; -#endif #ifndef WOLFSSH_NO_ED25519 case ID_ED25519: @@ -3643,13 +3627,6 @@ static INLINE int wcPrimeForId(byte id) #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521 case ID_ECDSA_SHA2_NISTP521: return ECC_SECP521R1; -#endif -#ifndef WOLFSSH_NO_ECDH_SHA2_ED25519 - case ID_ECDH_SHA2_ED25519: - return ECC_X25519; - - case ID_ECDH_SHA2_ED25519_LIBSSH: - return ECC_X25519; #endif default: return ECC_CURVE_INVALID; @@ -6712,9 +6689,10 @@ static int DoUserAuthRequestEccCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, #endif /* WOLFSSH_CERTS */ #endif /* ! WOLFSSH_NO_ECDSA */ + #ifndef WOLFSSH_NO_ED25519 -static int DoUserAuthRequestEd25519(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, - WS_UserAuthData* authData) +static int DoUserAuthRequestEd25519(WOLFSSH* ssh, + WS_UserAuthData_PublicKey* pk, WS_UserAuthData* authData) { const byte* publicKeyType; byte temp[32]; @@ -6735,7 +6713,7 @@ static int DoUserAuthRequestEd25519(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, if (ret == WS_SUCCESS) { #ifdef WOLFSSH_SMALL_STACK - key_ptr = (ecc_key*)WMALLOC(sizeof(ecc_key), ssh->ctx->heap, + key_ptr = (ed25519_key*)WMALLOC(sizeof(ed25519_key), ssh->ctx->heap, DYNTYPE_PUBKEY); if (key_ptr == NULL) ret = WS_MEMORY_E; @@ -6759,9 +6737,9 @@ static int DoUserAuthRequestEd25519(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, if (ret == WS_SUCCESS) { publicKeyType = pk->publicKey + i; i += publicKeyTypeSz; - if (publicKeyTypeSz != pk->publicKeyTypeSz && - WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) { - + 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; @@ -6805,17 +6783,19 @@ static int DoUserAuthRequestEd25519(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, ret = GetSize(&sz, pk->signature, pk->signatureSz, &i); } - if(ret == WS_SUCCESS) { - ret = wc_ed25519_verify_msg_init(pk->signature + i, sz, key_ptr, (byte)Ed25519, NULL, 0); + if (ret == WS_SUCCESS) { + ret = wc_ed25519_verify_msg_init(pk->signature + i, sz, + key_ptr, (byte)Ed25519, NULL, 0); } - if(ret == WS_SUCCESS) { + if (ret == WS_SUCCESS) { c32toa(ssh->sessionIdSz, temp); ret = wc_ed25519_verify_msg_update(temp, UINT32_SZ, key_ptr); } - if(ret == WS_SUCCESS) { - ret = wc_ed25519_verify_msg_update(ssh->sessionId, ssh->sessionIdSz, key_ptr); + if (ret == WS_SUCCESS) { + ret = wc_ed25519_verify_msg_update(ssh->sessionId, ssh->sessionIdSz, + key_ptr); } if(ret == WS_SUCCESS) { @@ -6836,7 +6816,8 @@ static int DoUserAuthRequestEd25519(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, if(ret == WS_SUCCESS) { int status = 0; - ret = wc_ed25519_verify_msg_final(pk->signature + i, sz, &status, key_ptr); + ret = wc_ed25519_verify_msg_final(pk->signature + i, sz, + &status, key_ptr); if (ret != 0) { WLOG(WS_LOG_DEBUG, "Could not verify signature"); ret = WS_CRYPTO_FAILED; @@ -6845,12 +6826,13 @@ static int DoUserAuthRequestEd25519(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, ret = status ? WS_SUCCESS : WS_ECC_E; } - if (key_ptr) + if (key_ptr) { wc_ed25519_free(key_ptr); #ifdef WOLFSSH_SMALL_STACK - if (key_ptr) WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PUBKEY); #endif + } + WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestEd25519(), ret = %d", ret); return ret; } @@ -7045,13 +7027,14 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, WLOG(WS_LOG_DEBUG, "DUARPK: Send the PK OK"); ret = SendUserAuthPkOk(ssh, pubKeyAlgo, pubKeyAlgoSz, pubKeyBlob, pubKeyBlobSz); - } else { - if(pkTypeId == ID_ED25519) { + } + else { + if (pkTypeId == ID_ED25519) { #ifndef WOLFSSH_NO_ED25519 - if(ret == WS_SUCCESS) - ret = DoUserAuthRequestEd25519(ssh, &authData->sf.publicKey, authData); + ret = DoUserAuthRequestEd25519(ssh, + &authData->sf.publicKey, authData); #else - ret = WS_CRYPTO_FAILED; + ret = WS_INVALID_ALGO_ID; #endif } else { wc_HashAlg hash; @@ -7087,9 +7070,9 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, } /* The rest of the fields in the signature are already - * in the buffer. Just need to account for the sizes, which total - * the length of the buffer minus the signature and size of - * signature. */ + * in the buffer. Just need to account for the sizes, which + * total the length of the buffer minus the signature and + * size of signature. */ if (ret == 0) { ret = HashUpdate(&hash, hashId, authData->sf.publicKey.dataToSign, @@ -7130,7 +7113,6 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, case ID_ECDSA_SHA2_NISTP256: case ID_ECDSA_SHA2_NISTP384: case ID_ECDSA_SHA2_NISTP521: - case ID_ED25519: ret = DoUserAuthRequestEcc(ssh, &authData->sf.publicKey, hashId, digest, digestSz); diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 3742340ca..a7f854071 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -159,10 +159,6 @@ extern "C" { #undef WOLFSSH_NO_ECDH_SHA2_NISTP521 #define WOLFSSH_NO_ECDH_SHA2_NISTP521 #endif -#if !defined(HAVE_CURVE25519) || defined(NO_SHA256) - #undef WOLFSSH_NO_ECDH_SHA2_ED25519 - #define WOLFSSH_NO_ECDH_SHA2_ED25519 -#endif #if !defined(WOLFSSH_HAVE_LIBOQS) || defined(NO_SHA256) \ || defined(WOLFSSH_NO_ECDH_SHA2_NISTP256) #undef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 @@ -179,7 +175,6 @@ extern "C" { defined(WOLFSSH_NO_ECDH_SHA2_NISTP256) && \ defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \ defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \ - defined(WOLFSSH_NO_ECDH_SHA2_ED25519) && \ defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256) && \ defined(WOLFSSH_NO_CURVE25519_SHA256) #error "You need at least one key agreement algorithm." @@ -193,8 +188,7 @@ extern "C" { #endif #if defined(WOLFSSH_NO_ECDH_SHA2_NISTP256) && \ defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \ - defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \ - defined(WOLFSSH_NO_ECDH_SHA2_ED25519) + defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) #undef WOLFSSH_NO_ECDH #define WOLFSSH_NO_ECDH #endif From 9b97927cd851bd1f4a24120b9ab6434bc9f58451 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Fri, 10 May 2024 11:30:58 -0700 Subject: [PATCH 4/5] Add Ed25519 1. Add testing key for user barney. 2. Remove some instances of the incorrect macro guard WOLFSSH_NO_ECC. We deal in ECDSA or ECDHE separately only. 3. Add WIP function for decoding the OpenSSH format Ed25519 key. --- keys/id_barney | 7 +++++++ keys/id_barney.pub | 1 + src/internal.c | 41 ++++++++++++++++++++++++++++++++++++++--- wolfssh/internal.h | 3 +-- 4 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 keys/id_barney create mode 100644 keys/id_barney.pub diff --git a/keys/id_barney b/keys/id_barney new file mode 100644 index 000000000..07c504bb3 --- /dev/null +++ b/keys/id_barney @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBs8gsipHiL/VP0nvJOeDeR0EYF9AXtXnjGlGmqHru5NQAAAJghFgrDIRYK +wwAAAAtzc2gtZWQyNTUxOQAAACBs8gsipHiL/VP0nvJOeDeR0EYF9AXtXnjGlGmqHru5NQ +AAAEDuTSTiIfkHZlxI+gjjETACk3F3PPU7jgOHG6NH/THSXWzyCyKkeIv9U/Se8k54N5HQ +RgX0Be1eeMaUaaoeu7k1AAAAEGJhcm5leUBsb2NhbGhvc3QBAgMEBQ== +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/id_barney.pub b/keys/id_barney.pub new file mode 100644 index 000000000..64a15f34b --- /dev/null +++ b/keys/id_barney.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGzyCyKkeIv9U/Se8k54N5HQRgX0Be1eeMaUaaoeu7k1 barney@localhost diff --git a/src/internal.c b/src/internal.c index 866a30444..82f394200 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1409,8 +1409,7 @@ static int GetOpenSshKeyRsa(RsaKey* key, } #endif - -#if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECC) +#ifndef WOLFSSH_NO_ECDSA /* * Utility for GetOpenSshKey() to read in ECDSA keys. */ @@ -1440,6 +1439,35 @@ static int GetOpenSshKeyEcc(ecc_key* key, } #endif +#ifndef WOLFSSH_NO_ED25519 +/* + * Utility for GetOpenSshKey() to read in Ed25519 keys. + */ +static int GetOpenSshKeyEd25519(ed25519_key* key, + const byte* buf, word32 len, word32* idx) +{ + const byte *name = NULL, *priv = NULL, *pub = NULL; + word32 nameSz = 0, privSz = 0, pubSz = 0; + int ret; + + ret = wc_ed25519_init_ex(key, ssh->ctx->heap, INVALID_DEVID); + if (ret == WS_SUCCESS) + ret = GetStringRef(&nameSz, &name, buf, len, idx); /* curve name */ + if (ret == WS_SUCCESS) + ret = GetStringRef(&pubSz, &pub, buf, len, idx); /* ENC(A) */ + if (ret == WS_SUCCESS) + ret = GetMpint(&privSz, &priv, buf, len, idx); /* k || ENC(A) */ + + if (ret == WS_SUCCESS) + ret = wc_ecc_import_private_key_ex(priv, privSz, pub, pubSz, + key, ECC_CURVE_DEF); + + if (ret != WS_SUCCESS) + ret = WS_ECC_E; + + return ret; +} +#endif /* * Decodes an OpenSSH format key. */ @@ -1522,11 +1550,18 @@ static int GetOpenSshKey(WS_KeySignature *key, str, strSz, &subIdx); break; #endif - #if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECC) + #ifndef WOLFSSH_NO_ECDSA case ID_ECDSA_SHA2_NISTP256: + case ID_ECDSA_SHA2_NISTP384: + case ID_ECDSA_SHA2_NISTP521: ret = GetOpenSshKeyEcc(&key->ks.ecc.key, str, strSz, &subIdx); break; + #endif + #ifndef WOLFSSH_NO_ED25519 + ret = GetOpenSshKeyEd25519(&key->ks.ed25519.key, + str, strSz, &subIdx); + break; #endif default: ret = WS_UNIMPLEMENTED_E; diff --git a/wolfssh/internal.h b/wolfssh/internal.h index a7f854071..e617a4cf7 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -239,8 +239,7 @@ extern "C" { #endif #if defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && \ defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && \ - defined(WOLFSSH_NO_ECDSA_SHA2_NISTP521) && \ - !defined(HAVE_ED25519) + defined(WOLFSSH_NO_ECDSA_SHA2_NISTP521) #undef WOLFSSH_NO_ECDSA #define WOLFSSH_NO_ECDSA #endif From 0d887bd6449c0dc6c3dafd5b4c96c54b855f2148 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Mon, 13 May 2024 12:22:30 -0700 Subject: [PATCH 5/5] Add Ed25519 1. Add an error code for Ed25519 signing or verify issues. 2. Add Ed25519 key support to ReadKey. 3. Add client side support for Ed25519. 4. Update some key usage log strings to be more descriptive. --- src/internal.c | 306 ++++++++++++++++++++++++++++++++++++++++++++---- src/ssh.c | 15 +-- wolfssh/error.h | 3 +- 3 files changed, 296 insertions(+), 28 deletions(-) diff --git a/src/internal.c b/src/internal.c index 82f394200..507d95d2a 100644 --- a/src/internal.c +++ b/src/internal.c @@ -447,6 +447,9 @@ const char* GetErrorString(int err) case WS_MSGID_NOT_ALLOWED_E: return "message not allowed before user authentication"; + case WS_ED25519_E: + return "Ed25519 buffer error"; + default: return "Unknown error code"; } @@ -730,8 +733,12 @@ static const char cannedKexAlgoNames[] = #ifndef WOLFSSH_NO_RSA_SHA2_512 static const char cannedKeyAlgoRsaSha2_512Names[] = "rsa-sha2-512"; #endif +#ifndef WOLFSSH_NO_ED25519 + static const char cannedKeyAlgoEd25519Name[] = "ssh-ed25519"; +#endif static const char cannedKeyAlgoNames[] = + "ssh-ed25519," "rsa-sha2-256," "ecdsa-sha2-nistp256," #ifdef WOLFSSH_CERTS @@ -1165,6 +1172,7 @@ typedef struct WS_KeySignature { byte keySigId; word32 sigSz; const char *name; + void *heap; word32 nameSz; union { #ifndef WOLFSSH_NO_RSA @@ -1302,10 +1310,12 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap) if(ret == 0) { if (isPrivate) { - ret = wc_Ed25519PrivateKeyDecode(in, &idx, &key->ks.ed25519.key, inSz); + ret = wc_Ed25519PrivateKeyDecode(in, &idx, + &key->ks.ed25519.key, inSz); } else { - ret = wc_Ed25519PublicKeyDecode(in, &idx, &key->ks.ed25519.key, inSz); + ret = wc_Ed25519PublicKeyDecode(in, &idx, + &key->ks.ed25519.key, inSz); } } @@ -1446,21 +1456,22 @@ static int GetOpenSshKeyEcc(ecc_key* key, static int GetOpenSshKeyEd25519(ed25519_key* key, const byte* buf, word32 len, word32* idx) { - const byte *name = NULL, *priv = NULL, *pub = NULL; - word32 nameSz = 0, privSz = 0, pubSz = 0; + const byte *priv = NULL, *pub = NULL; + word32 privSz = 0, pubSz = 0; int ret; - ret = wc_ed25519_init_ex(key, ssh->ctx->heap, INVALID_DEVID); - if (ret == WS_SUCCESS) - ret = GetStringRef(&nameSz, &name, buf, len, idx); /* curve name */ + ret = wc_ed25519_init_ex(key, key->heap, INVALID_DEVID); + + /* OpenSSH key formatting stores the public key, ENC(A), and the + * private key (k) concatenated with the public key, k || ENC(A). */ if (ret == WS_SUCCESS) ret = GetStringRef(&pubSz, &pub, buf, len, idx); /* ENC(A) */ if (ret == WS_SUCCESS) - ret = GetMpint(&privSz, &priv, buf, len, idx); /* k || ENC(A) */ + ret = GetStringRef(&privSz, &priv, buf, len, idx); /* k || ENC(A) */ if (ret == WS_SUCCESS) - ret = wc_ecc_import_private_key_ex(priv, privSz, pub, pubSz, - key, ECC_CURVE_DEF); + ret = wc_ed25519_import_private_key(priv, privSz - pubSz, + pub, pubSz, key); if (ret != WS_SUCCESS) ret = WS_ECC_E; @@ -1559,6 +1570,7 @@ static int GetOpenSshKey(WS_KeySignature *key, break; #endif #ifndef WOLFSSH_NO_ED25519 + case ID_ED25519: ret = GetOpenSshKeyEd25519(&key->ks.ed25519.key, str, strSz, &subIdx); break; @@ -1626,6 +1638,7 @@ int IdentifyOpenSshKey(const byte* in, word32 inSz, void* heap) } else { WMEMSET(key, 0, sizeof(*key)); + key->heap = heap; key->keySigId = ID_NONE; ret = GetOpenSshKey(key, in, inSz, &idx); @@ -4275,6 +4288,7 @@ static int DoKexDhInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) struct wolfSSH_sigKeyBlock { byte useRsa:1; byte useEcc:1; + byte useEd25519:1; byte keyAllocated:1; word32 keySz; union { @@ -4287,6 +4301,11 @@ struct wolfSSH_sigKeyBlock { struct { ecc_key key; } ecc; +#endif +#ifndef WOLFSSH_NO_ED25519 + struct { + ed25519_key key; + } ed25519; #endif } sk; }; @@ -4410,6 +4429,49 @@ static int ParseECCPubKey(WOLFSSH *ssh, } +/* Parse out a RAW Ed25519 public key from buffer */ +static int ParseEd25519PubKey(WOLFSSH *ssh, + struct wolfSSH_sigKeyBlock *sigKeyBlock_ptr, + byte *pubKey, word32 pubKeySz) +#ifndef WOLFSSH_NO_ED25519 +{ + int ret; + const byte* encA; + word32 encASz, pubKeyIdx = 0; + + ret = wc_ed25519_init_ex(&sigKeyBlock_ptr->sk.ed25519.key, + ssh->ctx->heap, INVALID_DEVID); + if (ret != 0) + ret = WS_ED25519_E; + + /* Skip the algo name */ + if (ret == WS_SUCCESS) { + ret = GetSkip(pubKey, pubKeySz, &pubKeyIdx); + } + + if (ret == WS_SUCCESS) { + ret = GetStringRef(&encASz, &encA, pubKey, pubKeySz, &pubKeyIdx); + } + + if (ret == WS_SUCCESS) { + ret = wc_ed25519_import_public(encA, encASz, + &sigKeyBlock_ptr->sk.ed25519.key); + if (ret != 0) + ret = WS_ED25519_E; + } + return ret; +} +#else +{ + WOLFSSH_UNUSED(ssh); + WOLFSSH_UNUSED(sigKeyBlock_ptr); + WOLFSSH_UNUSED(pubKey); + WOLFSSH_UNUSED(pubKeySz); + return WS_INVALID_ALGO_ID; +} +#endif + + #ifdef WOLFSSH_CERTS /* finds the leaf certificate and optionally the bounds of the cert chain, * returns WS_SUCCESS on success */ @@ -4682,6 +4744,11 @@ static int ParsePubKey(WOLFSSH *ssh, break; #endif + case ID_ED25519: + sigKeyBlock_ptr->useEd25519 = 1; + ret = ParseEd25519PubKey(ssh, sigKeyBlock_ptr, pubKey, pubKeySz); + break; + default: ret = WS_INVALID_ALGO_ID; } @@ -5311,12 +5378,12 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) } } if (ret == WS_SUCCESS) { + sig = sig + begin; + /* In the fuzz, sigSz ends up 1 and it has issues. */ + sigSz = scratch; + if (sigKeyBlock_ptr->useRsa) { #ifndef WOLFSSH_NO_RSA - sig = sig + begin; - /* In the fuzz, sigSz ends up 1 and it has issues. */ - sigSz = scratch; - if (sigSz < MIN_RSA_SIG_SZ) { WLOG(WS_LOG_DEBUG, "Provided signature is too small."); ret = WS_RSA_E; @@ -5343,15 +5410,13 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) } #endif } - else { + else if (sigKeyBlock_ptr->useEcc) { #ifndef WOLFSSH_NO_ECDSA const byte* r; const byte* s; word32 rSz, sSz, asnSigSz; byte asnSig[256]; - sig = sig + begin; - sigSz = scratch; begin = 0; asnSigSz = (word32)sizeof(asnSig); XMEMSET(asnSig, 0, asnSigSz); @@ -5379,6 +5444,24 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) } #endif } + else if (sigKeyBlock_ptr->useEd25519) { +#ifndef WOLFSSH_NO_ED25519 + int res = 0; + + ret = wc_ed25519_verify_msg(sig, sigSz, + ssh->h, ssh->hSz, &res, + &sigKeyBlock_ptr->sk.ed25519.key); + if (ret != 0 || res != 1) { + WLOG(WS_LOG_DEBUG, + "DoKexDhReply: Signature Verify fail (%d)", + ret); + ret = WS_ED25519_E; + } +#endif /* WOLFSSH_NO_ED25519 */ + } + else { + ret = WS_INVALID_ALGO_ID; + } } } FreePubKey(sigKeyBlock_ptr); @@ -6858,7 +6941,7 @@ static int DoUserAuthRequestEd25519(WOLFSSH* ssh, ret = WS_CRYPTO_FAILED; } else - ret = status ? WS_SUCCESS : WS_ECC_E; + ret = status ? WS_SUCCESS : WS_ED25519_E; } if (key_ptr) { @@ -13207,7 +13290,168 @@ static int BuildUserAuthRequestEccCert(WOLFSSH* ssh, #endif /* WOLFSSH_NO_ECDSA */ -#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA) +#ifndef WOLFSSH_NO_ED25519 + +static int PrepareUserAuthRequestEd25519(WOLFSSH* ssh, word32* payloadSz, + const WS_UserAuthData* authData, WS_KeySignature* keySig) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering PrepareUserAuthRequestEd25519()"); + if (ssh == NULL || payloadSz == NULL || authData == NULL || keySig == NULL) + ret = WS_BAD_ARGUMENT; + + if (ret == WS_SUCCESS) + ret = wc_ed25519_init_ex(&keySig->ks.ed25519.key, + keySig->heap, INVALID_DEVID); + + if (ret == 0) { + word32 idx = 0; + #ifdef WOLFSSH_AGENT + if (ssh->agentEnabled) { + /* XXX: Pending */ + } + else + #endif + { + ret = GetOpenSshKey(keySig, + authData->sf.publicKey.privateKey, + authData->sf.publicKey.privateKeySz, &idx); + } + } + + if (ret == WS_SUCCESS) { + if (authData->sf.publicKey.hasSignature) { + int sigSz = wc_ed25519_sig_size(&keySig->ks.ed25519.key); + + if (sigSz >= 0) { + *payloadSz += (LENGTH_SZ * 3) + (word32)sigSz + + authData->sf.publicKey.publicKeyTypeSz; + keySig->sigSz = sigSz; + } + else + ret = sigSz; + } + } + + WLOG(WS_LOG_DEBUG, + "Leaving PrepareUserAuthRequestEd25519(), ret = %d", ret); + return ret; +} + + +static int BuildUserAuthRequestEd25519(WOLFSSH* ssh, + byte* output, word32* idx, + const WS_UserAuthData* authData, + const byte* sigStart, word32 sigStartIdx, + WS_KeySignature* keySig) +{ + word32 begin; + int ret = WS_SUCCESS; + byte* sig; + word32 sigSz = ED25519_SIG_SIZE; + byte* checkData = NULL; + word32 checkDataSz = 0; +#ifndef WOLFSSH_SMALL_STACK + byte sig_s[ED25519_SIG_SIZE]; +#endif + + WLOG(WS_LOG_DEBUG, "Entering BuildUserAuthRequestEd25519()"); + if (ssh == NULL || output == NULL || idx == NULL || authData == NULL || + sigStart == NULL || keySig == NULL) { + ret = WS_BAD_ARGUMENT; + return ret; + } + +#ifdef WOLFSSH_SMALL_STACK + sig = (byte*)WMALLOC(sigSz, keySig->heap, DYNTYPE_BUFFER); + if (sig == NULL) + ret = WS_MEMORY_E; +#else + sig = sig_s; +#endif + + begin = *idx; + + if (ret == WS_SUCCESS) { + checkDataSz = LENGTH_SZ + ssh->sessionIdSz + (begin - sigStartIdx); + checkData = (byte*)WMALLOC(checkDataSz, keySig->heap, DYNTYPE_TEMP); + if (checkData == NULL) + ret = WS_MEMORY_E; + } + + if (ret == WS_SUCCESS) { + word32 i = 0; + + c32toa(ssh->sessionIdSz, checkData + i); + i += LENGTH_SZ; + WMEMCPY(checkData + i, ssh->sessionId, ssh->sessionIdSz); + i += ssh->sessionIdSz; + WMEMCPY(checkData + i, sigStart, begin - sigStartIdx); + } + + #ifdef WOLFSSH_AGENT + if (ssh->agentEnabled) { + /* XXX: Pending */ + } + else + #endif + { + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_INFO, "Signing with Ed25519."); + ret = wc_ed25519_sign_msg(checkData, checkDataSz, + sig, &sigSz, &keySig->ks.ed25519.key); + + if (ret != WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, "SUAR: Bad ED25519 Sign"); + ret = WS_ED25519_E; + } + } + + if (ret == WS_SUCCESS) { + const char* name = cannedKeyAlgoEd25519Name; + word32 nameSz = (word32)WSTRLEN(name); + + c32toa(LENGTH_SZ * 2 + nameSz + sigSz, output + begin); + begin += LENGTH_SZ; + + c32toa(nameSz, output + begin); + begin += LENGTH_SZ; + + WMEMCPY(output + begin, name, nameSz); + begin += nameSz; + + c32toa(sigSz, output + begin); + begin += LENGTH_SZ; + + WMEMCPY(output + begin, sig, sigSz); + begin += sigSz; + } + } + + if (ret == WS_SUCCESS) + *idx = begin; + + if (checkData != NULL) { + ForceZero(checkData, checkDataSz); + WFREE(checkData, keySig->heap, DYNTYPE_TEMP); + } + +#ifdef WOLFSSH_SMALL_STACK + if (sig) + WFREE(sig, keySig->heap, DYNTYPE_BUFFER); +#endif + + WLOG(WS_LOG_DEBUG, + "Leaving BuildUserAuthRequestEd25519(), ret = %d", ret); + return ret; +} + +#endif /* WOLFSSH_NO_ED25519 */ + + +#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA) \ + || !defined(WOLFSSH_NO_ED25519) static int PrepareUserAuthRequestPublicKey(WOLFSSH* ssh, word32* payloadSz, const WS_UserAuthData* authData, WS_KeySignature* keySig) { @@ -13245,7 +13489,7 @@ static int PrepareUserAuthRequestPublicKey(WOLFSSH* ssh, word32* payloadSz, matchId = MatchIdLists(WOLFSSH_ENDPOINT_CLIENT, algoId, algoIdSz, ssh->peerSigId, ssh->peerSigIdSz); if (matchId == ID_UNKNOWN) { - ret = WS_MATCH_KEX_ALGO_E; + ret = WS_MATCH_KEY_ALGO_E; } keySig->keySigId = matchId; keySig->name = IdToName(matchId); @@ -13290,6 +13534,12 @@ static int PrepareUserAuthRequestPublicKey(WOLFSSH* ssh, word32* payloadSz, break; #endif #endif + #ifndef WOLFSSH_NO_ED25519 + case ID_ED25519: + ret = PrepareUserAuthRequestEd25519(ssh, + payloadSz, authData, keySig); + break; + #endif default: ret = WS_INVALID_ALGO_ID; } @@ -13399,6 +13649,21 @@ static int BuildUserAuthRequestPublicKey(WOLFSSH* ssh, break; #endif #endif + #ifndef WOLFSSH_NO_ED25519 + case ID_ED25519: + c32toa(pk->publicKeyTypeSz, output + begin); + begin += LENGTH_SZ; + WMEMCPY(output + begin, + pk->publicKeyType, pk->publicKeyTypeSz); + begin += pk->publicKeyTypeSz; + c32toa(pk->publicKeySz, output + begin); + begin += LENGTH_SZ; + WMEMCPY(output + begin, pk->publicKey, pk->publicKeySz); + begin += pk->publicKeySz; + ret = BuildUserAuthRequestEd25519(ssh, output, &begin, + authData, sigStart, sigStartIdx, keySig); + break; + #endif default: ret = WS_INVALID_ALGO_ID; } @@ -13451,6 +13716,7 @@ int SendUserAuthRequest(WOLFSSH* ssh, byte authType, int addSig) if (ret == WS_SUCCESS) { WMEMSET(keySig_ptr, 0, sizeof(WS_KeySignature)); keySig_ptr->keySigId = ID_NONE; + keySig_ptr->heap = ssh->ctx->heap; if (ssh->ctx->userAuthCb != NULL) { WLOG(WS_LOG_DEBUG, "SUAR: Calling the userauth callback"); diff --git a/src/ssh.c b/src/ssh.c index 9f519ce47..14c6112aa 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -1689,7 +1689,7 @@ static int DoAsn1Key(const byte* in, word32 inSz, byte** out, ret = WS_SUCCESS; } else { - WLOG(WS_LOG_DEBUG, "unable to identify key"); + WLOG(WS_LOG_DEBUG, "Unable to identify ASN.1 key"); if (*out == NULL) { WFREE(newKey, heap, DYNTYPE_PRIVKEY); } @@ -1746,7 +1746,7 @@ static int DoPemKey(const byte* in, word32 inSz, byte** out, ret = WS_SUCCESS; } else { - WLOG(WS_LOG_DEBUG, "unable to identify key"); + WLOG(WS_LOG_DEBUG, "Unable to identify PEM key"); if (*out == NULL) { WFREE(newKey, heap, DYNTYPE_PRIVKEY); } @@ -1802,7 +1802,7 @@ static int DoOpenSshKey(const byte* in, word32 inSz, byte** out, ret = WS_SUCCESS; } else { - WLOG(WS_LOG_DEBUG, "unable to identify key"); + WLOG(WS_LOG_DEBUG, "Unable to identify key"); if (*out == NULL) { WFREE(newKey, heap, DYNTYPE_PRIVKEY); } @@ -1897,10 +1897,11 @@ int wolfSSH_ReadKey_file(const char* name, ret = WS_BAD_FILE_E; } else { - if (WSTRNSTR((const char*)in, - "ssh-rsa", inSz) == (const char*)in || - WSTRNSTR((const char*)in, - "ecdsa-sha2-nistp", inSz) == (const char*)in) { + if (WSTRNSTR((const char*)in, "ssh-rsa", inSz) == (const char*)in + || WSTRNSTR((const char*)in, + "ecdsa-sha2-nistp", inSz) == (const char*)in + || WSTRNSTR((const char*)in, + "ssh-ed25519", inSz) == (const char*)in) { *isPrivate = 0; format = WOLFSSH_FORMAT_SSH; in[inSz] = 0; diff --git a/wolfssh/error.h b/wolfssh/error.h index 3749a55b5..c7b5bfcd5 100644 --- a/wolfssh/error.h +++ b/wolfssh/error.h @@ -133,8 +133,9 @@ enum WS_ErrorCodes { WS_KEY_FORMAT_E = -1092, /* OpenSSH key format fail */ WS_SFTP_NOT_FILE_E = -1093, /* Not a regular file */ WS_MSGID_NOT_ALLOWED_E = -1094, /* Message not allowed before userauth */ + WS_ED25519_E = -1095, /* Ed25519 failure */ - WS_LAST_E = -1094 /* Update this to indicate last error */ + WS_LAST_E = -1095 /* Update this to indicate last error */ };