|
28 | 28 | * _whTest_NonExportableKeystore - confirm WH_NVM_FLAGS_NONEXPORTABLE keys |
29 | 29 | * cannot be exported while ordinary keys can |
30 | 30 | * (std and DMA export paths) |
| 31 | + * _whTest_NonModifiableCommit - re-commit over a stored |
| 32 | + * WH_NVM_FLAGS_NONMODIFIABLE object is |
| 33 | + * denied, cached or not, and the stored key |
| 34 | + * and label survive the denial |
| 35 | + * _whTest_ModifiableRecommit - a key without the flag still re-commits |
| 36 | + * |
| 37 | + * _whTest_NonModifiableCommit is gated by |
| 38 | + * WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS: the object it commits |
| 39 | + * cannot be erased, so it holds an NVM slot for the rest of the run. |
31 | 40 | */ |
32 | 41 |
|
33 | 42 | #include "wolfhsm/wh_settings.h" |
@@ -839,13 +848,155 @@ static int _whTest_NonExportableKeystore(whClientContext* ctx) |
839 | 848 | return 0; |
840 | 849 | } |
841 | 850 |
|
| 851 | +#if defined(WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS) |
| 852 | +/* Committing a NONMODIFIABLE key leaves an object that |
| 853 | + * wh_Nvm_DestroyObjectsChecked refuses to erase, so it occupies one NVM |
| 854 | + * slot for the rest of the run. Gated like the keypolicy revocation test. */ |
| 855 | +static int _whTest_NonModifiableCommit(whClientContext* ctx) |
| 856 | +{ |
| 857 | + int ret = 0; |
| 858 | + whKeyId keyId = WH_KEYID_ERASED; |
| 859 | + uint8_t key[WH_TEST_KEYSTORE_TEST_SZ] = { |
| 860 | + 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, |
| 861 | + 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, |
| 862 | + 0x32, 0x10, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}; |
| 863 | + uint8_t exportedKey[WH_TEST_KEYSTORE_TEST_SZ] = {0}; |
| 864 | + uint8_t label[WH_NVM_LABEL_LEN] = "NonModifiableCommitKey"; |
| 865 | + uint8_t exportedLabel[WH_NVM_LABEL_LEN] = {0}; |
| 866 | + uint16_t exportedKeySize; |
| 867 | + |
| 868 | + WH_TEST_PRINT("Testing non-modifiable commit enforcement...\n"); |
| 869 | + |
| 870 | + /* Test 1: first commit of a NONMODIFIABLE key stores it, and the commit |
| 871 | + * leaves the slot cached, so a repeat commit is an overwrite attempt. */ |
| 872 | + ret = wh_Client_KeyCache(ctx, WH_NVM_FLAGS_NONMODIFIABLE, label, |
| 873 | + sizeof(label), key, sizeof(key), &keyId); |
| 874 | + if (ret != 0) { |
| 875 | + WH_ERROR_PRINT("Failed to cache non-modifiable key: %d\n", ret); |
| 876 | + return ret; |
| 877 | + } |
| 878 | + |
| 879 | + ret = wh_Client_KeyCommit(ctx, keyId); |
| 880 | + if (ret != 0) { |
| 881 | + WH_ERROR_PRINT("Failed first commit of non-modifiable key: %d\n", ret); |
| 882 | + return ret; |
| 883 | + } |
| 884 | + |
| 885 | + /* Test 2: re-committing over the stored non-modifiable object is denied */ |
| 886 | + ret = wh_Client_KeyCommit(ctx, keyId); |
| 887 | + if (ret != WH_ERROR_ACCESS) { |
| 888 | + WH_ERROR_PRINT("Non-modifiable key was re-committed unexpectedly: %d\n", |
| 889 | + ret); |
| 890 | + return -1; |
| 891 | + } |
| 892 | + |
| 893 | + WH_TEST_DEBUG_PRINT("Non-modifiable key re-commit correctly denied\n"); |
| 894 | + |
| 895 | + /* Test 3: the denial left the stored object intact. Evicting is allowed |
| 896 | + * because the key is committed, so the export below must freshen it back |
| 897 | + * out of NVM rather than read the surviving cache slot. */ |
| 898 | + ret = wh_Client_KeyEvict(ctx, keyId); |
| 899 | + if (ret != 0) { |
| 900 | + WH_ERROR_PRINT("Failed to evict committed non-modifiable key: %d\n", |
| 901 | + ret); |
| 902 | + return ret; |
| 903 | + } |
| 904 | + |
| 905 | + exportedKeySize = sizeof(exportedKey); |
| 906 | + ret = wh_Client_KeyExport(ctx, keyId, exportedLabel, sizeof(exportedLabel), |
| 907 | + exportedKey, &exportedKeySize); |
| 908 | + if (ret != 0) { |
| 909 | + WH_ERROR_PRINT("Failed to export stored non-modifiable key: %d\n", ret); |
| 910 | + return ret; |
| 911 | + } |
| 912 | + |
| 913 | + if (exportedKeySize != sizeof(key) || |
| 914 | + memcmp(key, exportedKey, exportedKeySize) != 0 || |
| 915 | + memcmp(label, exportedLabel, sizeof(label)) != 0) { |
| 916 | + WH_ERROR_PRINT("Denied commit altered the stored key\n"); |
| 917 | + return -1; |
| 918 | + } |
| 919 | + |
| 920 | + WH_TEST_DEBUG_PRINT("Stored non-modifiable key unchanged after denial\n"); |
| 921 | + |
| 922 | + /* Evicting reclaims only the cache slot; wh_Nvm_DestroyObjectsChecked |
| 923 | + * refuses the NONMODIFIABLE object. Checked so Test 4 cannot degrade into |
| 924 | + * a repeat of Test 2 with the slot still resident. */ |
| 925 | + ret = wh_Client_KeyEvict(ctx, keyId); |
| 926 | + if (ret != 0) { |
| 927 | + WH_ERROR_PRINT("Failed to evict before uncached commit check: %d\n", |
| 928 | + ret); |
| 929 | + return ret; |
| 930 | + } |
| 931 | + |
| 932 | + /* Test 4: the denial does not depend on cache residency. With no slot |
| 933 | + * left, the stored flags still decide, so commit reports ACCESS rather |
| 934 | + * than the NOTFOUND raised by the missing slot. */ |
| 935 | + ret = wh_Client_KeyCommit(ctx, keyId); |
| 936 | + if (ret != WH_ERROR_ACCESS) { |
| 937 | + WH_ERROR_PRINT("Uncached non-modifiable commit not denied: %d\n", ret); |
| 938 | + return -1; |
| 939 | + } |
| 940 | + |
| 941 | + WH_TEST_DEBUG_PRINT("Uncached non-modifiable commit correctly denied\n"); |
| 942 | + |
| 943 | + WH_TEST_PRINT("NON-MODIFIABLE COMMIT TEST SUCCESS\n"); |
| 944 | + return 0; |
| 945 | +} |
| 946 | +#endif /* WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS */ |
| 947 | + |
| 948 | +static int _whTest_ModifiableRecommit(whClientContext* ctx) |
| 949 | +{ |
| 950 | + int ret = 0; |
| 951 | + whKeyId keyId = WH_KEYID_ERASED; |
| 952 | + uint8_t key[WH_TEST_KEYSTORE_TEST_SZ] = { |
| 953 | + 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, |
| 954 | + 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, |
| 955 | + 0x32, 0x10, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10}; |
| 956 | + uint8_t label[WH_NVM_LABEL_LEN] = "ModifiableCommitKey"; |
| 957 | + |
| 958 | + WH_TEST_PRINT("Testing modifiable commit is unaffected...\n"); |
| 959 | + |
| 960 | + /* A key without the flag still commits repeatedly */ |
| 961 | + ret = wh_Client_KeyCache(ctx, WH_NVM_FLAGS_NONE, label, sizeof(label), key, |
| 962 | + sizeof(key), &keyId); |
| 963 | + if (ret != 0) { |
| 964 | + WH_ERROR_PRINT("Failed to cache modifiable key: %d\n", ret); |
| 965 | + return ret; |
| 966 | + } |
| 967 | + |
| 968 | + ret = wh_Client_KeyCommit(ctx, keyId); |
| 969 | + if (ret != 0) { |
| 970 | + WH_ERROR_PRINT("Failed first commit of modifiable key: %d\n", ret); |
| 971 | + return ret; |
| 972 | + } |
| 973 | + |
| 974 | + ret = wh_Client_KeyCommit(ctx, keyId); |
| 975 | + if (ret != 0) { |
| 976 | + WH_ERROR_PRINT("Failed repeat commit of modifiable key: %d\n", ret); |
| 977 | + return ret; |
| 978 | + } |
| 979 | + |
| 980 | + WH_TEST_DEBUG_PRINT("Modifiable key repeat commit allowed\n"); |
| 981 | + |
| 982 | + /* Clean up */ |
| 983 | + (void)wh_Client_KeyErase(ctx, keyId); |
| 984 | + |
| 985 | + WH_TEST_PRINT("MODIFIABLE COMMIT TEST SUCCESS\n"); |
| 986 | + return 0; |
| 987 | +} |
| 988 | + |
842 | 989 | int whTest_Crypto_Keystore(whClientContext* ctx) |
843 | 990 | { |
844 | 991 | /* A preceding suite may leave the DMA-preferred dispatch mode set; reset |
845 | 992 | * to the std path so this suite runs the same way in every config. */ |
846 | 993 | (void)wh_Client_SetDmaMode(ctx, 0); |
847 | 994 | WH_TEST_RETURN_ON_FAIL(_whTest_KeyCache(ctx)); |
848 | 995 | WH_TEST_RETURN_ON_FAIL(_whTest_NonExportableKeystore(ctx)); |
| 996 | +#if defined(WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS) |
| 997 | + WH_TEST_RETURN_ON_FAIL(_whTest_NonModifiableCommit(ctx)); |
| 998 | +#endif |
| 999 | + WH_TEST_RETURN_ON_FAIL(_whTest_ModifiableRecommit(ctx)); |
849 | 1000 | return 0; |
850 | 1001 | } |
851 | 1002 |
|
|
0 commit comments