From 564b7c9bd538187c4d5d24b7ab13a5709da5bf4a Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 5 Jun 2024 15:04:03 -0700 Subject: [PATCH] Merge pull request #709 from ejohnstown/rsa-verify-refactor RSA Verify Refactor --- src/internal.c | 250 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) diff --git a/src/internal.c b/src/internal.c index 0fbd105f..38aaa5c4 100644 --- a/src/internal.c +++ b/src/internal.c @@ -6346,6 +6346,20 @@ static int DoUnimplemented(WOLFSSH* ssh, WOLFSSH_UNUSED(ssh); WOLFSSH_UNUSED(len); + const byte* publicKeyType; + const byte* sig; + word32 publicKeyTypeSz = 0; + word32 sigSz; + word32 encDigestSz; + word32 i = 0; + int ret = WS_SUCCESS; +#ifdef WOLFSSH_SMALL_STACK + byte* encDigest = NULL; + RsaKey* key = NULL; +#else + byte encDigest[MAX_ENCODED_SIG_SZ]; + RsaKey key[1]; +#endif ato32(buf + begin, &seq); begin += UINT32_SZ; @@ -6357,6 +6371,26 @@ static int DoUnimplemented(WOLFSSH* ssh, return WS_SUCCESS; } +#ifdef WOLFSSH_SMALL_STACK + if (ret == WS_SUCCESS) { + encDigest = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, + ssh->ctx->heap, DYNTYPE_BUFFER); + if (encDigest == NULL) + ret = WS_MEMORY_E; + } + if (ret == WS_SUCCESS) { + key = (RsaKey*)WMALLOC(sizeof(RsaKey), ssh->ctx->heap, DYNTYPE_PUBKEY); + if (key == NULL) + ret = WS_MEMORY_E; + } +#endif + + if (ret == WS_SUCCESS) { + ret = wc_InitRsaKey(key, ssh->ctx->heap); + if (ret == 0) { + ret = WS_SUCCESS; + } + } static int DoDisconnect(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { @@ -6415,6 +6449,29 @@ static int DoDisconnect(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) return WS_SUCCESS; } + /* Load up the key. */ + if (ret == WS_SUCCESS) { + const byte* n = NULL; + word32 nSz = 0; + const byte* e = NULL; + word32 eSz = 0; + + if (ret == WS_SUCCESS) { + ret = GetMpint(&eSz, &e, pk->publicKey, pk->publicKeySz, &i); + } + + if (ret == WS_SUCCESS) { + ret = GetMpint(&nSz, &n, pk->publicKey, pk->publicKeySz, &i); + } + + if (ret == WS_SUCCESS) { + ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, key); + if (ret != 0) { + WLOG(WS_LOG_DEBUG, "Could not decode public key"); + ret = WS_CRYPTO_FAILED; + } + } + } static int DoServiceRequest(WOLFSSH* ssh, @@ -6443,6 +6500,25 @@ static int DoServiceRequest(WOLFSSH* ssh, ssh->clientState = CLIENT_USERAUTH_REQUEST_DONE; return WS_SUCCESS; + if (ret == WS_SUCCESS) { + encDigestSz = wc_EncodeSignature(encDigest, + digest, digestSz, wc_HashGetOID(hashId)); + ret = wolfSSH_RsaVerify(sig, sigSz, encDigest, encDigestSz, + key, ssh->ctx->heap, "DoUserAuthRequestRsa"); + } + + wc_FreeRsaKey(key); +#ifdef WOLFSSH_SMALL_STACK + if (key) { + WFREE(key, ssh->ctx->heap, DYNTYPE_PUBKEY); + } + if (encDigest) { + WFREE(encDigest, ssh->ctx->heap, DYNTYPE_BUFFER); + } +#endif + + WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestRsa(), ret = %d", ret); + return ret; } @@ -6455,6 +6531,20 @@ static int DoServiceAccept(WOLFSSH* ssh, ato32(buf + begin, &nameSz); begin += LENGTH_SZ; + const byte* publicKeyType; + const byte* sig; + word32 publicKeyTypeSz = 0; + word32 sigSz; + word32 encDigestSz; + word32 i = 0; + int ret = WS_SUCCESS; +#ifdef WOLFSSH_SMALL_STACK + byte* encDigest = NULL; + RsaKey* key = NULL; +#else + byte encDigest[MAX_ENCODED_SIG_SZ]; + RsaKey key[1]; +#endif if (begin + nameSz > len || nameSz >= WOLFSSH_MAX_NAMESZ) { return WS_BUFFER_E; @@ -6472,6 +6562,32 @@ static int DoServiceAccept(WOLFSSH* ssh, return WS_SUCCESS; } +#ifdef WOLFSSH_SMALL_STACK + if (ret == WS_SUCCESS) { + encDigest = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, + ssh->ctx->heap, DYNTYPE_BUFFER); + if (encDigest == NULL) + ret = WS_MEMORY_E; + } + if (ret == WS_SUCCESS) { + key = (RsaKey*)WMALLOC(sizeof(RsaKey), ssh->ctx->heap, DYNTYPE_PUBKEY); + if (key == NULL) + ret = WS_MEMORY_E; + } +#endif + + if (ret == WS_SUCCESS) { + ret = wc_InitRsaKey(key, ssh->ctx->heap); + if (ret == 0) { + ret = WS_SUCCESS; + } + } + + /* Load up the key. */ + if (ret == WS_SUCCESS) { + byte* pub = NULL; + word32 pubSz; + DecodedCert cert; static int DoExtInfoServerSigAlgs(WOLFSSH* ssh, const byte* names, word32 namesSz) @@ -6486,6 +6602,13 @@ static int DoExtInfoServerSigAlgs(WOLFSSH* ssh, peerSigId = (byte*)WMALLOC(peerSigIdSz, ssh->ctx->heap, DYNTYPE_ID); if (peerSigId == NULL) { ret = WS_MEMORY_E; + if (ret == 0) { + i = 0; + ret = wc_RsaPublicKeyDecode(pub, &i, key, pubSz); + if (ret != 0) { + WLOG(WS_LOG_DEBUG, "Could not decode public key"); + ret = WS_CRYPTO_FAILED; + } } } @@ -6500,6 +6623,11 @@ static int DoExtInfoServerSigAlgs(WOLFSSH* ssh, if (algoId == ID_UNKNOWN) { ret = WS_MATCH_UA_KEY_ID_E; + if (ret == WS_SUCCESS) { + int keySz = wc_RsaEncryptSize(key) * 8; + if (keySz < 2048) { + WLOG(WS_LOG_DEBUG, "Key size too small (%d)", keySz); + ret = WS_CERT_KEY_SIZE_E; } } @@ -6602,6 +6730,44 @@ static int DoUserAuthRequestNone(WOLFSSH* ssh, WS_UserAuthData* authData, } WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestNone(), ret = %d", ret); + i = 0; + /* Check that the signature's pubkey type matches the expected one. */ + ret = GetStringRef(&publicKeyTypeSz, &publicKeyType, + pk->signature, pk->signatureSz, &i); + } + + if (ret == WS_SUCCESS) { + if (publicKeyTypeSz != pk->publicKeyTypeSz && + WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) { + + WLOG(WS_LOG_DEBUG, + "Signature's type does not match public key type"); + ret = WS_INVALID_ALGO_ID; + } + } + + if (ret == WS_SUCCESS) { + ret = GetMpint(&sigSz, &sig, pk->signature, pk->signatureSz, &i); + } + + if (ret == WS_SUCCESS) { + encDigestSz = wc_EncodeSignature(encDigest, + digest, digestSz, wc_HashGetOID(hashId)); + ret = wolfSSH_RsaVerify(sig, sigSz, encDigest, encDigestSz, + key, ssh->ctx->heap, "DoUserAuthRequestRsaCert"); + } + + wc_FreeRsaKey(key); +#ifdef WOLFSSH_SMALL_STACK + if (key) { + WFREE(key, ssh->ctx->heap, DYNTYPE_PUBKEY); + } + if (encDigest) { + WFREE(encDigest, ssh->ctx->heap, DYNTYPE_BUFFER); + } +#endif + + WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestRsaCert(), ret = %d", ret); return ret; } #endif @@ -11742,6 +11908,90 @@ static int KeyAgreeEcdhKyber1_server(WOLFSSH* ssh, byte hashId, } WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeEcdhKyber1_server(), ret = %d", ret); +#ifndef WOLFSSH_NO_RSA +/* + * wolfSSH_RsaVerify + * sig - signature to verify + * sigSz - signature size + * encDigest - encoded digest for verification + * encDigestSz - encoded digest size + * key - key used to sign and verify signature + * heap - allocation heap + * loc - calling function for logging + * + * Takes the provided digest of type digestId and converts it to an + * encoded digest. Then verifies the signature, comparing the output + * digest and compares it. + */ +int wolfSSH_RsaVerify(const byte *sig, word32 sigSz, + const byte* encDigest, word32 encDigestSz, + RsaKey* key, void* heap, const char* loc) +{ + byte* checkSig = NULL; + int checkDigestSz; + word32 keySz; + int ret = WS_SUCCESS; +#ifdef WOLFSSH_SMALL_STACK + byte* checkDigest = NULL; +#else + byte checkDigest[MAX_ENCODED_SIG_SZ]; +#endif + + keySz = (word32)wc_RsaEncryptSize(key); + + if (ret == WS_SUCCESS) { + checkSig = (byte*)WMALLOC(keySz, heap, DYNTYPE_TEMP); + if (checkSig == NULL) + ret = WS_MEMORY_E; + } +#ifdef WOLFSSH_SMALL_STACK + if (ret == WS_SUCCESS) { + checkDigest = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, heap, DYNTYPE_TEMP); + if (checkDigest == NULL) + ret = WS_MEMORY_E; + } +#endif + + /* Normalize the peer's signature. Some SSH implementations remove + * leading zeros on the signatures they encode. We need to pad the + * front of the signature to the key size. */ + if (ret == WS_SUCCESS) { + word32 offset; + + if (keySz > sigSz) { + offset = keySz - sigSz; + } + else { + sigSz = keySz; + offset = 0; + } + + WMEMSET(checkSig, 0, offset); + WMEMCPY(checkSig + offset, sig, sigSz); + } + + if (ret == WS_SUCCESS) { + volatile int sizeCompare; + volatile int compare; + + checkDigestSz = wc_RsaSSL_Verify(checkSig, keySz, + checkDigest, MAX_ENCODED_SIG_SZ, key); + + sizeCompare = checkDigestSz > 0 && encDigestSz != (word32)checkDigestSz; + compare = ConstantCompare(encDigest, checkDigest, encDigestSz); + + if (checkDigestSz < 0 || sizeCompare || compare) { + WLOG(WS_LOG_DEBUG, "%s: %s", loc, "Bad RSA Verify"); + ret = WS_RSA_E; + } + } + +#ifdef WOLFSSH_SMALL_STACK + if (checkDigest) + WFREE(checkDigest, heap, DYNTYPE_TEMP); +#endif + if (checkSig) + WFREE(checkSig, heap, DYNTYPE_TEMP); return ret; } #else /* WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */