diff --git a/port/posix/posix_transport_shm.c b/port/posix/posix_transport_shm.c index a61863dcd..f03af7cc6 100644 --- a/port/posix/posix_transport_shm.c +++ b/port/posix/posix_transport_shm.c @@ -603,6 +603,9 @@ int posixTransportShm_ClientStaticMemDmaCallback( else if (oper == WH_DMA_OPER_CLIENT_READ_POST) { if (isInDma == 0) { uint8_t* ptr = (uint8_t*)dmaPtr + (uintptr_t)*xformedCliAddr; + /* Scrub key material before freeing. len is bounded by the temp + * buffer's XMALLOC, well within uint32_t for this transport. */ + wh_Utils_ForceZero(ptr, (uint32_t)len); XFREE(ptr, heap, DYNAMIC_TYPE_TMP_BUFFER); } } @@ -611,6 +614,9 @@ int posixTransportShm_ClientStaticMemDmaCallback( uint8_t* ptr = (uint8_t*)dmaPtr + (uintptr_t)*xformedCliAddr; memcpy((void*)clientAddr, ptr, len); /* copy results of what server wrote */ + /* Scrub key material before freeing. len is bounded by the temp + * buffer's XMALLOC, well within uint32_t for this transport. */ + wh_Utils_ForceZero(ptr, (uint32_t)len); XFREE(ptr, heap, DYNAMIC_TYPE_TMP_BUFFER); } } diff --git a/src/wh_auth_base.c b/src/wh_auth_base.c index 9b49f7001..b04518f81 100644 --- a/src/wh_auth_base.c +++ b/src/wh_auth_base.c @@ -109,6 +109,29 @@ static int wh_Auth_BaseHashPin(const void* pin, uint16_t pin_len, #endif /* WOLFHSM_CFG_NO_CRYPTO */ } +/* Constant-time credential compare: the byte compare and length check combine + * without an early-out on mismatch, so timing can't reveal the stored length. + * (The oversized-length guard below returns early, but only on the supplied + * length, never the stored one.) Returns 0 on match. */ +static int wh_Auth_BaseCredsCompare(const unsigned char* stored, + uint16_t stored_len, const void* supplied, + uint16_t supplied_len) +{ + int diff; + + /* Oversized supplied length can't match and must not read past the + * buffer. */ + if (supplied_len > WH_AUTH_BASE_MAX_CREDENTIALS_LEN) { + return 1; + } + + diff = wh_Utils_ConstantCompare(stored, (const uint8_t*)supplied, + supplied_len); + /* Fold in the length-equality check without branching. */ + diff |= (int)((unsigned int)stored_len ^ (unsigned int)supplied_len); + return diff; +} + static whAuthBase_User* wh_Auth_BaseCheckPin(const char* username, const void* auth_data, uint16_t auth_data_len) @@ -133,8 +156,8 @@ static whAuthBase_User* wh_Auth_BaseCheckPin(const char* username, found_user = wh_Auth_BaseFindUser(username); if (found_user != NULL && found_user->method == WH_AUTH_METHOD_PIN && - found_user->credentials_len == authCheck_len && - wh_Utils_ConstantCompare(found_user->credentials, authCheck, + wh_Auth_BaseCredsCompare(found_user->credentials, + found_user->credentials_len, authCheck, authCheck_len) == 0) { ret = found_user; } @@ -518,19 +541,19 @@ int wh_Auth_BaseUserSetCredentials(void* context, uint16_t current_user_id, } wh_Utils_ForceZero(hash, sizeof(hash)); #else - /* When crypto is disabled, compare PINs directly */ - if (user->credentials_len != current_credentials_len || - wh_Utils_ConstantCompare(user->credentials, current_credentials, - current_credentials_len) != 0) { + /* When crypto is disabled, compare PINs directly (constant time) */ + if (wh_Auth_BaseCredsCompare( + user->credentials, user->credentials_len, + current_credentials, current_credentials_len) != 0) { return WH_ERROR_ACCESS; } #endif /* WOLFHSM_CFG_NO_CRYPTO */ } else { - /* For non-PIN methods, compare as-is */ - if (user->credentials_len != current_credentials_len || - wh_Utils_ConstantCompare(user->credentials, current_credentials, - current_credentials_len) != 0) { + /* For non-PIN methods, compare as-is (constant time) */ + if (wh_Auth_BaseCredsCompare( + user->credentials, user->credentials_len, + current_credentials, current_credentials_len) != 0) { return WH_ERROR_ACCESS; } } diff --git a/src/wh_client_she.c b/src/wh_client_she.c index 716934f83..c18b5d2d6 100644 --- a/src/wh_client_she.c +++ b/src/wh_client_she.c @@ -56,9 +56,10 @@ int wh_Client_ShePreProgramKey(whClientContext* c, whNvmId keyId, /* Create a key with 0 counter */ wh_She_Meta2Label(0, flags, label); - ret = wh_Client_NvmAddObject(c, - WH_MAKE_KEYID(WH_KEYTYPE_SHE, c->comm->client_id, keyId), - 0, 0, sizeof(label), label, keySz, key, (int32_t*)&outRc); + ret = wh_Client_NvmAddObject( + c, WH_MAKE_KEYID(WH_KEYTYPE_SHE, c->comm->client_id, keyId), + WH_NVM_ACCESS_ANY, 0, sizeof(label), label, keySz, key, + (int32_t*)&outRc); if (ret == 0) ret = outRc; return ret; diff --git a/src/wh_server_counter.c b/src/wh_server_counter.c index a681c894a..22a5b1225 100644 --- a/src/wh_server_counter.c +++ b/src/wh_server_counter.c @@ -225,7 +225,8 @@ int wh_Server_HandleCounter(whServerContext* server, uint16_t magic, } break; default: - ret = WH_ERROR_BADARGS; + *out_resp_size = 0; + ret = WH_ERROR_BADARGS; break; } diff --git a/src/wh_server_she.c b/src/wh_server_she.c index 3615dce16..508887959 100644 --- a/src/wh_server_she.c +++ b/src/wh_server_she.c @@ -606,6 +606,7 @@ static int _LoadKey(whServerContext* server, uint16_t magic, uint16_t req_size, if (ret == 0) { meta->id = WH_MAKE_KEYID(WH_KEYTYPE_SHE, server->comm->client_id, _PopId(req.messageOne)); + meta->access = WH_NVM_ACCESS_ANY; she_meta_flags = _PopFlags(req.messageTwo); she_meta_count = wh_Utils_ntohl(msg_counter_val) >> 4; /* Update the meta label with new values */ @@ -725,9 +726,10 @@ static int _LoadPlainKey(whServerContext* server, uint16_t magic, &req); } - meta->id = WH_MAKE_KEYID(WH_KEYTYPE_SHE, server->comm->client_id, - WH_SHE_RAM_KEY_ID); - meta->len = WH_SHE_KEY_SZ; + meta->id = WH_MAKE_KEYID(WH_KEYTYPE_SHE, server->comm->client_id, + WH_SHE_RAM_KEY_ID); + meta->access = WH_NVM_ACCESS_ANY; + meta->len = WH_SHE_KEY_SZ; /* cache if ram key, overwrite otherwise */ if (ret == 0) { @@ -984,10 +986,11 @@ static int _InitRnd(whServerContext* server, uint16_t magic, uint16_t req_size, wc_AesFree(server->she->sheAes); /* save PRNG_SEED, i */ if (ret == 0) { - meta->id = WH_MAKE_KEYID(WH_KEYTYPE_SHE, server->comm->client_id, - WH_SHE_PRNG_SEED_ID); - meta->len = WH_SHE_KEY_SZ; - ret = wh_Nvm_AddObject(server->nvm, meta, meta->len, cmacOutput); + meta->id = WH_MAKE_KEYID(WH_KEYTYPE_SHE, server->comm->client_id, + WH_SHE_PRNG_SEED_ID); + meta->access = WH_NVM_ACCESS_ANY; + meta->len = WH_SHE_KEY_SZ; + ret = wh_Nvm_AddObject(server->nvm, meta, meta->len, cmacOutput); if (ret != 0) { ret = WH_SHE_ERC_KEY_UPDATE_ERROR; } @@ -1121,10 +1124,11 @@ static int _ExtendSeed(whServerContext* server, uint16_t magic, } /* save PRNG_SEED */ if (ret == 0) { - meta->id = WH_MAKE_KEYID(WH_KEYTYPE_SHE, server->comm->client_id, - WH_SHE_PRNG_SEED_ID); - meta->len = WH_SHE_KEY_SZ; - ret = wh_Nvm_AddObject(server->nvm, meta, meta->len, kdfInput); + meta->id = WH_MAKE_KEYID(WH_KEYTYPE_SHE, server->comm->client_id, + WH_SHE_PRNG_SEED_ID); + meta->access = WH_NVM_ACCESS_ANY; + meta->len = WH_SHE_KEY_SZ; + ret = wh_Nvm_AddObject(server->nvm, meta, meta->len, kdfInput); if (ret != 0) { ret = WH_SHE_ERC_KEY_UPDATE_ERROR; } diff --git a/test/wh_test_auth.c b/test/wh_test_auth.c index 254ecf99f..cf2602bcf 100644 --- a/test/wh_test_auth.c +++ b/test/wh_test_auth.c @@ -637,6 +637,24 @@ int whTest_AuthLogin(whClientContext* client) server_rc != WH_ERROR_OK); WH_TEST_ASSERT_RETURN(user_id == WH_USER_ID_INVALID); + /* Wrong-length prefixes of the admin PIN "1234" must be rejected + * (report 5458): "123" is a shorter prefix, "12345" a longer one. */ + WH_TEST_PRINT(" Test: Login rejects wrong-length credential\n"); + server_rc = 0; + user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + TEST_ADMIN_USERNAME, "123", 3, + &server_rc, &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_AUTH_LOGIN_FAILED); + WH_TEST_ASSERT_RETURN(user_id == WH_USER_ID_INVALID); + server_rc = 0; + user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + TEST_ADMIN_USERNAME, "12345", 5, + &server_rc, &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_AUTH_LOGIN_FAILED); + WH_TEST_ASSERT_RETURN(user_id == WH_USER_ID_INVALID); + /* Test 2: Login with valid credentials - use blocking version */ WH_TEST_PRINT(" Test: Login with valid credentials\n"); server_rc = 0; @@ -1412,6 +1430,35 @@ int whTest_AuthSetCredentials(whClientContext* client) _whTest_Auth_DeleteUserByName(client, "victimcreduser"); } + /* Wrong-length current credential must be rejected like a wrong-value one + * (report 5458). testuser4's PIN is "newpass" (7); we are admin. */ + WH_TEST_PRINT(" Test: Set-credentials rejects wrong current credential\n"); + /* Wrong length (shorter) */ + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserSetCredsOp(client, user_id, WH_AUTH_METHOD_PIN, + "newpas", 6, "changed", 7, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + /* Wrong length (longer, correct value as a prefix) */ + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserSetCredsOp(client, user_id, WH_AUTH_METHOD_PIN, + "newpassX", 8, "changed", 7, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + /* Correct length, wrong value */ + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserSetCredsOp(client, user_id, WH_AUTH_METHOD_PIN, + "wrongpw", 7, "changed", 7, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_ACCESS); + /* Correct current credential still succeeds (behavior preserved); keep the + * PIN as "newpass" so the verification below continues to hold. */ + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserSetCredsOp(client, user_id, WH_AUTH_METHOD_PIN, + "newpass", 7, "newpass", 7, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + /* Verify new credentials work */ _whTest_Auth_LogoutOp(client, admin_id, &server_rc); memset(&admin_perms, 0, sizeof(admin_perms)); diff --git a/test/wh_test_clientserver.c b/test/wh_test_clientserver.c index 56caebc70..207ae724e 100644 --- a/test/wh_test_clientserver.c +++ b/test/wh_test_clientserver.c @@ -678,6 +678,26 @@ static int _testClientCounter(whClientContext* client) wh_Client_CounterRead(client, (whNvmId)i, &counter)); } + /* Report 2001: an invalid counter action hits the handler's default case, + * which must report a 0-length response instead of echoing the stale + * request-sized buffer back to the client. */ + { + uint8_t reqbuf[8] = {0}; + uint8_t respbuf[64] = {0}; + uint16_t respGroup = 0; + uint16_t respAction = 0; + uint16_t respSz = 0xFFFF; + + WH_TEST_RETURN_ON_FAIL(wh_Client_SendRequest( + client, WH_MESSAGE_GROUP_COUNTER, 0x7F, sizeof(reqbuf), reqbuf)); + do { + rc = wh_Client_RecvResponse(client, &respGroup, &respAction, + &respSz, respbuf); + } while (rc == WH_ERROR_NOTREADY); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(respSz == 0); + } + /* Ensure NVM is empty */ WH_TEST_RETURN_ON_FAIL(rc = wh_Client_NvmGetAvailable( client, &server_rc, &avail_size, &avail_objects, diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index a5850cde5..b4e4fb96f 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -14856,6 +14856,224 @@ int whTest_CryptoKeyUsagePolicies(whClientContext* client, WC_RNG* rng) #endif /* HAVE_ECC_DHE */ #endif /* HAVE_ECC */ +#ifdef HAVE_CURVE25519 + /* X25519 (Curve25519) shared secret without DERIVE flag */ + WH_TEST_PRINT(" Testing X25519 shared secret without DERIVE flag...\n"); + { + curve25519_key privKey[1]; + curve25519_key pubKey[1]; + uint8_t x25519Secret[CURVE25519_KEYSIZE] = {0}; + word32 x25519SecretLen = sizeof(x25519Secret); + whKeyId privId = WH_KEYID_ERASED; + whKeyId pubId = WH_KEYID_ERASED; + + /* Generate a private key on the server WITHOUT the derive flag */ + ret = wh_Client_Curve25519MakeCacheKey( + client, CURVE25519_KEYSIZE, &privId, WH_NVM_FLAGS_USAGE_SIGN, + (uint8_t*)"x25519-no-derive", strlen("x25519-no-derive")); + if (ret == 0) { + /* Valid peer public key (DERIVE allowed) */ + ret = wh_Client_Curve25519MakeCacheKey( + client, CURVE25519_KEYSIZE, &pubId, WH_NVM_FLAGS_USAGE_DERIVE, + (uint8_t*)"x25519-peer", strlen("x25519-peer")); + } + if (ret == 0) { + ret = wc_curve25519_init_ex(privKey, NULL, WH_CLIENT_DEVID(client)); + if (ret == 0) { + ret = wc_curve25519_init_ex(pubKey, NULL, + WH_CLIENT_DEVID(client)); + if (ret == 0) { + /* Associate the cached keyIds with the local key objects */ + ret = wh_Client_Curve25519SetKeyId(privKey, privId); + if (ret == 0) { + ret = wh_Client_Curve25519SetKeyId(pubKey, pubId); + } + + /* Must fail: private key lacks DERIVE */ + if (ret == 0) { + ret = wc_curve25519_shared_secret( + privKey, pubKey, x25519Secret, &x25519SecretLen); + if (ret == WH_ERROR_USAGE) { + WH_TEST_PRINT( + " PASS: Correctly denied key derivation\n"); + ret = 0; /* Test passed */ + } + else { + WH_ERROR_PRINT( + " FAIL: Expected WH_ERROR_USAGE, got %d\n", + ret); + ret = WH_ERROR_ABORTED; + } + } + wc_curve25519_free(pubKey); + } + wc_curve25519_free(privKey); + } + } + /* Clean up cached keys */ + if (!WH_KEYID_ISERASED(privId)) { + wh_Client_KeyEvict(client, privId); + } + if (!WH_KEYID_ISERASED(pubId)) { + wh_Client_KeyEvict(client, pubId); + } + } + if (ret != 0) { + return ret; + } +#endif /* HAVE_CURVE25519 */ + +#ifndef NO_RSA + /* RSA usage policy plus the sign/verify fallback (PUBLIC_DECRYPT falls back + * to VERIFY, PRIVATE_ENCRYPT to SIGN). */ + WH_TEST_PRINT(" Testing RSA usage policy and sign/verify fallback...\n"); + { + RsaKey rsaKey[1]; + whKeyId rsaId = WH_KEYID_ERASED; + byte rsaIn[32]; + byte rsaSig[RSA_KEY_BYTES]; + byte rsaRec[RSA_KEY_BYTES]; + int opRc; + int sigLen; + + memset(rsaIn, 0xA5, sizeof(rsaIn)); + + /* 1) SIGN-only key: PublicEncrypt needs ENCRYPT (no fallback) -> deny + */ + ret = + wh_Client_RsaMakeCacheKey(client, RSA_KEY_BITS, RSA_EXPONENT, + &rsaId, WH_NVM_FLAGS_USAGE_SIGN, 0, NULL); + if (ret == 0) { + ret = wc_InitRsaKey_ex(rsaKey, NULL, WH_CLIENT_DEVID(client)); + if (ret == 0) { + ret = wh_Client_RsaSetKeyId(rsaKey, rsaId); + if (ret == 0) { + opRc = wc_RsaPublicEncrypt(rsaIn, sizeof(rsaIn), rsaSig, + sizeof(rsaSig), rsaKey, rng); + if (opRc == WH_ERROR_USAGE) { + WH_TEST_PRINT(" PASS: encrypt denied without " + "ENCRYPT\n"); + } + else { + WH_ERROR_PRINT( + " FAIL: expected WH_ERROR_USAGE, got %d\n", + opRc); + ret = WH_ERROR_ABORTED; + } + } + wc_FreeRsaKey(rsaKey); + } + wh_Client_KeyEvict(client, rsaId); + } + if (ret != 0) { + return ret; + } + + /* 2) ENCRYPT-only key: Verify needs DECRYPT or VERIFY -> deny */ + rsaId = WH_KEYID_ERASED; + ret = wh_Client_RsaMakeCacheKey(client, RSA_KEY_BITS, RSA_EXPONENT, + &rsaId, WH_NVM_FLAGS_USAGE_ENCRYPT, 0, + NULL); + if (ret == 0) { + ret = wc_InitRsaKey_ex(rsaKey, NULL, WH_CLIENT_DEVID(client)); + if (ret == 0) { + ret = wh_Client_RsaSetKeyId(rsaKey, rsaId); + if (ret == 0) { + memset(rsaSig, 0, sizeof(rsaSig)); + opRc = wc_RsaSSL_Verify(rsaSig, sizeof(rsaSig), rsaRec, + sizeof(rsaRec), rsaKey); + if (opRc == WH_ERROR_USAGE) { + WH_TEST_PRINT(" PASS: verify denied without " + "VERIFY\n"); + } + else { + WH_ERROR_PRINT( + " FAIL: expected WH_ERROR_USAGE, got %d\n", + opRc); + ret = WH_ERROR_ABORTED; + } + } + wc_FreeRsaKey(rsaKey); + } + wh_Client_KeyEvict(client, rsaId); + } + if (ret != 0) { + return ret; + } + + /* 3) VERIFY-only key: Sign needs ENCRYPT or SIGN -> deny */ + rsaId = WH_KEYID_ERASED; + ret = wh_Client_RsaMakeCacheKey(client, RSA_KEY_BITS, RSA_EXPONENT, + &rsaId, WH_NVM_FLAGS_USAGE_VERIFY, 0, + NULL); + if (ret == 0) { + ret = wc_InitRsaKey_ex(rsaKey, NULL, WH_CLIENT_DEVID(client)); + if (ret == 0) { + ret = wh_Client_RsaSetKeyId(rsaKey, rsaId); + if (ret == 0) { + opRc = wc_RsaSSL_Sign(rsaIn, sizeof(rsaIn), rsaSig, + sizeof(rsaSig), rsaKey, rng); + if (opRc == WH_ERROR_USAGE) { + WH_TEST_PRINT(" PASS: sign denied without SIGN\n"); + } + else { + WH_ERROR_PRINT( + " FAIL: expected WH_ERROR_USAGE, got %d\n", + opRc); + ret = WH_ERROR_ABORTED; + } + } + wc_FreeRsaKey(rsaKey); + } + wh_Client_KeyEvict(client, rsaId); + } + if (ret != 0) { + return ret; + } + + /* 4) SIGN|VERIFY key: sign then verify must succeed via the fallback + * paths, proving the fallback is live rather than dead code. */ + rsaId = WH_KEYID_ERASED; + ret = wh_Client_RsaMakeCacheKey( + client, RSA_KEY_BITS, RSA_EXPONENT, &rsaId, + WH_NVM_FLAGS_USAGE_SIGN | WH_NVM_FLAGS_USAGE_VERIFY, 0, NULL); + if (ret == 0) { + ret = wc_InitRsaKey_ex(rsaKey, NULL, WH_CLIENT_DEVID(client)); + if (ret == 0) { + ret = wh_Client_RsaSetKeyId(rsaKey, rsaId); + if (ret == 0) { + sigLen = wc_RsaSSL_Sign(rsaIn, sizeof(rsaIn), rsaSig, + sizeof(rsaSig), rsaKey, rng); + if (sigLen < 0) { + WH_ERROR_PRINT(" FAIL: sign via fallback %d\n", + sigLen); + ret = WH_ERROR_ABORTED; + } + } + if (ret == 0) { + opRc = wc_RsaSSL_Verify(rsaSig, sigLen, rsaRec, + sizeof(rsaRec), rsaKey); + if (opRc != (int)sizeof(rsaIn) || + memcmp(rsaRec, rsaIn, sizeof(rsaIn)) != 0) { + WH_ERROR_PRINT(" FAIL: verify via fallback %d\n", + opRc); + ret = WH_ERROR_ABORTED; + } + else { + WH_TEST_PRINT( + " PASS: sign/verify fallback round trip\n"); + } + } + wc_FreeRsaKey(rsaKey); + } + wh_Client_KeyEvict(client, rsaId); + } + if (ret != 0) { + return ret; + } + } +#endif /* NO_RSA */ + #ifdef HAVE_HKDF /* HKDF without DERIVE flag */ WH_TEST_PRINT(" Testing HKDF without DERIVE flag...\n"); diff --git a/test/wh_test_she.c b/test/wh_test_she.c index b15deddd4..0e9b036c7 100644 --- a/test/wh_test_she.c +++ b/test/wh_test_she.c @@ -405,6 +405,44 @@ int whTest_SheClientConfig(whClientConfig* config) WH_TEST_PRINT("SHE LOAD KEY UID checks SUCCESS\n"); } + /* _LoadKey M3 CMAC auth (report 3110): a corrupted M3 with valid M1/M2 must + * be rejected; reloading with the restored M3 at the same counter then + * succeeds, proving the rejected load left the slot untouched. */ + { + uint8_t savedM3; + + if ((ret = wh_She_GenerateLoadableKey( + SHE_TEST_VECTOR_KEY_ID, WH_SHE_MASTER_ECU_KEY_ID, 2, 0, sheUid, + vectorRawKey, vectorMasterEcuKey, messageOne, messageTwo, + messageThree, messageFour, messageFive)) != 0) { + WH_ERROR_PRINT("Failed to generate M3-test M1/M2/M3 %d\n", ret); + goto exit; + } + + savedM3 = messageThree[0]; + messageThree[0] ^= 0xFF; + ret = wh_Client_SheLoadKey(client, messageOne, messageTwo, messageThree, + outMessageFour, outMessageFive); + if (ret != WH_SHE_ERC_KEY_UPDATE_ERROR) { + WH_ERROR_PRINT("SHE LOAD KEY corrupt M3: expected " + "KEY_UPDATE_ERROR, got %d\n", + ret); + ret = WH_ERROR_ABORTED; + goto exit; + } + + messageThree[0] = savedM3; + if ((ret = wh_Client_SheLoadKey(client, messageOne, messageTwo, + messageThree, outMessageFour, + outMessageFive)) != 0) { + WH_ERROR_PRINT("SHE LOAD KEY restored M3: expected success, " + "got %d\n", + ret); + goto exit; + } + WH_TEST_PRINT("SHE LOAD KEY M3 CMAC auth SUCCESS\n"); + } + if ((ret = wh_Client_SheInitRnd(client)) != 0) { WH_ERROR_PRINT("Failed to wh_Client_SheInitRnd %d\n", ret); goto exit;