From fc2dd75f2d49c444eb3741b39719ebf5ff129575 Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Mon, 24 Aug 2026 17:04:25 +0900 Subject: [PATCH] ossh: check the key-type parse before the ID lookup - GetOpenSshPublicKey() calls NameToId() and enters the key-type switch only when GetStringRef() returns WS_SUCCESS, and returns that result otherwise. - publicKeyType starts NULL and keyId starts ID_UNKNOWN. - tests/api.c adds test_GetOpenSshPublicKey_type(), covering a truncated type string, a truncated length prefix, an empty type, an unsupported type and a well-formed ssh-rsa blob. Issue: F-11650 --- src/ossh.c | 83 ++++++++++++++++++++++++++++------------------------- tests/api.c | 62 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 39 deletions(-) diff --git a/src/ossh.c b/src/ossh.c index e6f1c2a3e..b09204d6e 100644 --- a/src/ossh.c +++ b/src/ossh.c @@ -471,50 +471,55 @@ static int GetOpenSshPublicKeyRsa(RsaKey* key, const byte* buf, word32 len, int GetOpenSshPublicKey(WS_KeySignature *key, const byte* buf, word32 len, word32* idx) { - int ret = WS_SUCCESS; - const byte* publicKeyType; + int ret; + const byte* publicKeyType = NULL; word32 publicKeyTypeSz = 0; - byte keyId; + byte keyId = ID_UNKNOWN; ret = GetStringRef(&publicKeyTypeSz, &publicKeyType, buf, len, idx); - keyId = NameToId((const char*)publicKeyType, publicKeyTypeSz); - switch (keyId) { - #ifndef WOLFSSH_NO_RSA - case ID_SSH_RSA: - ret = GetOpenSshPublicKeyRsa(&key->ks.rsa.key, buf, len, idx); - break; - #endif - #ifndef WOLFSSH_NO_ECDSA - case ID_ECDSA_SHA2_NISTP256: - case ID_ECDSA_SHA2_NISTP384: - case ID_ECDSA_SHA2_NISTP521: - ret = GetOpenSshPublicKeyEcc(&key->ks.ecc.key, buf, len, idx); - break; - #endif - #ifndef WOLFSSH_NO_ED25519 - case ID_ED25519: - ret = GetOpenSshKeyPublicEd25519(&key->ks.ed25519.key, buf, len, idx); - break; - #endif - #ifndef WOLFSSH_NO_MLDSA - case ID_MLDSA44: - ret = GetOpenSshKeyPublicMlDsa(&key->ks.mldsa.key, buf, len, - idx, WC_ML_DSA_44); - break; - case ID_MLDSA65: - ret = GetOpenSshKeyPublicMlDsa(&key->ks.mldsa.key, buf, len, - idx, WC_ML_DSA_65); - break; - case ID_MLDSA87: - ret = GetOpenSshKeyPublicMlDsa(&key->ks.mldsa.key, buf, len, - idx, WC_ML_DSA_87); - break; - #endif - default: - ret = WS_UNIMPLEMENTED_E; - break; + if (ret == WS_SUCCESS) { + keyId = NameToId((const char*)publicKeyType, publicKeyTypeSz); + + switch (keyId) { + #ifndef WOLFSSH_NO_RSA + case ID_SSH_RSA: + ret = GetOpenSshPublicKeyRsa(&key->ks.rsa.key, buf, len, idx); + break; + #endif + #ifndef WOLFSSH_NO_ECDSA + case ID_ECDSA_SHA2_NISTP256: + case ID_ECDSA_SHA2_NISTP384: + case ID_ECDSA_SHA2_NISTP521: + ret = GetOpenSshPublicKeyEcc(&key->ks.ecc.key, buf, len, idx); + break; + #endif + #ifndef WOLFSSH_NO_ED25519 + case ID_ED25519: + ret = GetOpenSshKeyPublicEd25519(&key->ks.ed25519.key, buf, len, + idx); + break; + #endif + #ifndef WOLFSSH_NO_MLDSA + case ID_MLDSA44: + ret = GetOpenSshKeyPublicMlDsa(&key->ks.mldsa.key, buf, len, + idx, WC_ML_DSA_44); + break; + case ID_MLDSA65: + ret = GetOpenSshKeyPublicMlDsa(&key->ks.mldsa.key, buf, len, + idx, WC_ML_DSA_65); + break; + case ID_MLDSA87: + ret = GetOpenSshKeyPublicMlDsa(&key->ks.mldsa.key, buf, len, + idx, WC_ML_DSA_87); + break; + #endif + default: + ret = WS_UNIMPLEMENTED_E; + break; + } } + return ret; } diff --git a/tests/api.c b/tests/api.c index e432fdc2a..3849012d7 100644 --- a/tests/api.c +++ b/tests/api.c @@ -2270,6 +2270,65 @@ static void test_LoadTpmSshKey_NoTrailingNewline(void) #endif /* WOLFSSH_TPM && FILESYSTEM && !USER_FILESYSTEM */ +#if defined(WOLFSSH_TPM) && defined(WOLFSSH_TEST_INTERNAL) + +/* The key type is read with GetStringRef(), which sets the length from the + * wire but leaves the pointer alone when the name runs past the buffer. */ +static void test_GetOpenSshPublicKey_type(void) +{ + /* "ssh" carrying a length of 7. */ + static const byte truncType[] = { + 0x00, 0x00, 0x00, 0x07, 's', 's', 'h' + }; + /* Too short to hold the length prefix. */ + static const byte truncLen[] = { 0x00, 0x00 }; + /* Parses, but names no key. */ + static const byte emptyType[] = { 0x00, 0x00, 0x00, 0x00 }; + static const byte unknownType[] = { + 0x00, 0x00, 0x00, 0x07, 's', 's', 'h', '-', 'd', 's', 's' + }; +#ifndef WOLFSSH_NO_RSA + /* string "ssh-rsa", mpint e, mpint n. */ + static const byte rsaKey[] = { + 0x00, 0x00, 0x00, 0x07, 's', 's', 'h', '-', 'r', 's', 'a', + 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x09, + 0x00, 0xC5, 0x1A, 0x37, 0x8B, 0x42, 0x9D, 0xE0, 0x6F + }; +#endif + WS_KeySignature keySig; + word32 idx; + + WMEMSET(&keySig, 0, sizeof(keySig)); + + idx = 0; + AssertIntEQ(GetOpenSshPublicKey(&keySig, truncType, + (word32)sizeof(truncType), &idx), WS_BUFFER_E); + + idx = 0; + AssertIntEQ(GetOpenSshPublicKey(&keySig, truncLen, + (word32)sizeof(truncLen), &idx), WS_BUFFER_E); + + idx = 0; + AssertIntEQ(GetOpenSshPublicKey(&keySig, emptyType, + (word32)sizeof(emptyType), &idx), WS_UNIMPLEMENTED_E); + + idx = 0; + AssertIntEQ(GetOpenSshPublicKey(&keySig, unknownType, + (word32)sizeof(unknownType), &idx), WS_UNIMPLEMENTED_E); + +#ifndef WOLFSSH_NO_RSA + idx = 0; + AssertIntEQ(wc_InitRsaKey(&keySig.ks.rsa.key, NULL), 0); + AssertIntEQ(GetOpenSshPublicKey(&keySig, rsaKey, + (word32)sizeof(rsaKey), &idx), WS_SUCCESS); + AssertIntEQ(idx, (word32)sizeof(rsaKey)); + AssertIntEQ(wc_FreeRsaKey(&keySig.ks.rsa.key), 0); +#endif +} + +#endif /* WOLFSSH_TPM && WOLFSSH_TEST_INTERNAL */ + static void test_wolfSSH_ReadKey_badPad(void) { @@ -7484,6 +7543,9 @@ int wolfSSH_ApiTest(int argc, char** argv) #if defined(WOLFSSH_TPM) && !defined(NO_FILESYSTEM) && \ !defined(NO_WRITE_TEMP_FILES) && !defined(WOLFSSH_USER_FILESYSTEM) test_LoadTpmSshKey_NoTrailingNewline(); +#endif +#if defined(WOLFSSH_TPM) && defined(WOLFSSH_TEST_INTERNAL) + test_GetOpenSshPublicKey_type(); #endif test_wolfSSH_ReadKey_shortBuffer(); test_wolfSSH_ReadKey_noTrailingNewline();