|
25 | 25 | #include "wolfhsm/wh_common.h" |
26 | 26 | #include "wolfhsm/wh_error.h" |
27 | 27 | #include "wolfhsm/wh_client.h" |
28 | | -#include "wolfhsm/wh_nvm_flash.h" |
| 28 | +#include "wolfhsm/wh_client_crypto.h" |
29 | 29 |
|
30 | 30 | #include "wolfssl/wolfcrypt/settings.h" |
31 | 31 | #include "wolfssl/wolfcrypt/aes.h" |
32 | 32 | #include "wolfssl/wolfcrypt/random.h" |
33 | 33 |
|
34 | | - |
35 | 34 | #include "wh_demo_client_keywrap.h" |
36 | | -#include "test/wh_test_keywrap.h" |
37 | | - |
38 | | -#ifdef WOLFHSM_CFG_TEST_POSIX |
39 | | -#include "port/posix/posix_flash_file.h" |
40 | 35 |
|
41 | 36 | #ifdef WOLFHSM_CFG_KEYWRAP |
42 | 37 |
|
43 | | -int wh_DemoClient_KeyWrap(whClientContext* client) |
| 38 | +#define WH_TEST_KEKID 1 |
| 39 | +static int _InitServerKek(whClientContext* ctx) |
44 | 40 | { |
45 | | - int ret; |
| 41 | + /* IMPORTANT NOTE: Server KEK is typically intrinsic or set during |
| 42 | + * provisioning. Uploading the KEK via the client is for testing purposes |
| 43 | + * only and not intended as a recommendation */ |
| 44 | + whKeyId serverKeyId = WH_TEST_KEKID; |
| 45 | + whNvmFlags flags = WH_NVM_FLAGS_NONEXPORTABLE; |
| 46 | + uint8_t label[WH_NVM_LABEL_LEN] = "Server KEK key"; |
| 47 | + uint8_t kek[] = {0x03, 0x03, 0x0d, 0xd9, 0xeb, 0x18, 0x17, 0x2e, |
| 48 | + 0x06, 0x6e, 0x19, 0xce, 0x98, 0x44, 0x54, 0x0d, |
| 49 | + 0x78, 0xa0, 0xbe, 0xe7, 0x35, 0x43, 0x40, 0xa4, |
| 50 | + 0x22, 0x8a, 0xd1, 0x0e, 0xa3, 0x63, 0x1c, 0x0b}; |
| 51 | + |
| 52 | + return wh_Client_KeyCache(ctx, flags, label, sizeof(label), kek, |
| 53 | + sizeof(kek), &serverKeyId); |
| 54 | +} |
| 55 | + |
| 56 | +static int _CleanupServerKek(whClientContext* ctx) |
| 57 | +{ |
| 58 | + return wh_Client_KeyErase(ctx, WH_TEST_KEKID); |
| 59 | +} |
| 60 | + |
| 61 | +#ifndef NO_AES |
| 62 | +#ifdef HAVE_AESGCM |
| 63 | + |
| 64 | +#define WH_TEST_AES_KEYSIZE 16 |
| 65 | +#define WH_TEST_AES_TEXTSIZE 16 |
| 66 | +#define WH_TEST_AES_IVSIZE 12 |
| 67 | +#define WH_TEST_AES_TAGSIZE 16 |
| 68 | +#define WH_TEST_AES_WRAPPED_KEYSIZE \ |
| 69 | + (WH_TEST_AES_IVSIZE + WH_TEST_AES_TAGSIZE + WH_TEST_AES_KEYSIZE + \ |
| 70 | + sizeof(whNvmMetadata)) |
| 71 | +#define WH_TEST_AESGCM_WRAPKEY_ID 8 |
| 72 | + |
| 73 | +int wh_DemoClient_AesGcmKeyWrap(whClientContext* client) |
| 74 | +{ |
| 75 | + int ret = 0; |
| 76 | + Aes aes[1]; |
| 77 | + WC_RNG rng[1]; |
| 78 | + uint8_t key[WH_TEST_AES_KEYSIZE]; |
| 79 | + uint8_t exportedKey[WH_TEST_AES_KEYSIZE]; |
| 80 | + whNvmMetadata metadata = { |
| 81 | + .id = WH_MAKE_KEYID(WH_KEYTYPE_CRYPTO, 0, WH_TEST_AESGCM_WRAPKEY_ID), |
| 82 | + .label = "AES Key Label", |
| 83 | + .access = WH_NVM_ACCESS_ANY, |
| 84 | + .len = WH_TEST_AES_KEYSIZE}; |
| 85 | + whNvmMetadata exportedMetadata; |
| 86 | + uint8_t wrappedKey[WH_TEST_AES_WRAPPED_KEYSIZE]; |
| 87 | + whKeyId wrappedKeyId; |
| 88 | + |
| 89 | + const uint8_t plaintext[] = "hello, wolfSSL AES-GCM!"; |
| 90 | + uint8_t ciphertext[sizeof(plaintext)]; |
| 91 | + uint8_t decrypted[sizeof(plaintext)]; |
| 92 | + |
| 93 | + uint8_t tag[WH_TEST_AES_TAGSIZE]; |
| 94 | + uint8_t iv[WH_TEST_AES_IVSIZE]; |
| 95 | + const uint8_t aad[] = {0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, |
| 96 | + 0xef, 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, |
| 97 | + 0xbe, 0xef, 0xab, 0xad, 0xda, 0xd2}; |
46 | 98 |
|
47 | | - /* file-based flash state and configuration */ |
48 | | - posixFlashFileContext flashFileCtx; |
49 | | - posixFlashFileConfig flashFileCfg = {.partition_size = 1024 * 1024, |
50 | | - .erased_byte = 0xff}; |
51 | | - whFlashCb flashFileCb[1] = {POSIX_FLASH_FILE_CB}; |
| 99 | + /* Initialize the server KEK */ |
52 | 100 |
|
53 | | - ret = flashFileCb->Init(&flashFileCtx, &flashFileCfg); |
| 101 | + /* The key wrap feature requires the server to have a Key Encryption Key |
| 102 | + * (I.E. KEK) available for the client to use. In the case of this demo we |
| 103 | + * have the client initializing the KEK which is not recommended. Typically |
| 104 | + * the KEK ID would be a hard coded value that the client and server share |
| 105 | + * and the KEK would be provisioned on the server prior to runtime */ |
| 106 | + ret = _InitServerKek(client); |
54 | 107 | if (ret != WH_ERROR_OK) { |
55 | | - printf("Failed to flashCb->Init %d\n", ret); |
| 108 | + printf("Failed to _InitServerKek %d\n", ret); |
56 | 109 | return ret; |
57 | 110 | } |
58 | 111 |
|
59 | | - ret = |
60 | | - flashFileCb->WriteUnlock(&flashFileCtx, 0, flashFileCfg.partition_size); |
61 | | - if (ret != WH_ERROR_OK) { |
62 | | - printf("Failed to flashCb->WriteUnlock %d\n", ret); |
63 | | - return ret; |
| 112 | + /* Generating and wrapping a key */ |
| 113 | + |
| 114 | + /* Initialize the RNG so we can generate an AES GCM key to wrap */ |
| 115 | + ret = wc_InitRng_ex(rng, NULL, WH_DEV_ID); |
| 116 | + if (ret != 0) { |
| 117 | + printf("Failed to wc_InitRng_ex %d\n", ret); |
| 118 | + goto cleanup_kek; |
64 | 119 | } |
65 | | - ret = whTest_Client_KeyWrap(client); |
66 | | - if (ret != WH_ERROR_OK) { |
67 | | - printf("Failed to whTest_Client_KeyWrap %d\n", ret); |
68 | | - return ret; |
| 120 | + |
| 121 | + /* Now we generate the AES GCM key using the RNG */ |
| 122 | + ret = wc_RNG_GenerateBlock(rng, key, sizeof(key)); |
| 123 | + if (ret != 0) { |
| 124 | + printf("Failed to wc_RNG_GenerateBlock for key data %d\n", ret); |
| 125 | + goto cleanup_rng; |
69 | 126 | } |
70 | 127 |
|
71 | | - ret = |
72 | | - whTest_Client_WriteWrappedKeysToNvm(client, &flashFileCtx, flashFileCb); |
73 | | - if (ret != WH_ERROR_OK) { |
74 | | - printf("Failed to whTest_Client_WriteWrappedKeysToNvm %d\n", ret); |
75 | | - return ret; |
| 128 | + /* Now we request the server to wrap the key using the KEK we |
| 129 | + * establish above in the first step. */ |
| 130 | + ret = wh_Client_KeyWrap(client, WC_CIPHER_AES_GCM, WH_TEST_KEKID, key, |
| 131 | + sizeof(key), &metadata, wrappedKey, |
| 132 | + sizeof(wrappedKey)); |
| 133 | + if (ret != 0) { |
| 134 | + printf("Failed to wh_Client_KeyWrap %d\n", ret); |
| 135 | + goto cleanup_rng; |
| 136 | + } |
| 137 | + |
| 138 | + /* Now that the key is wrapped you store this in Non-Volatile Memory however |
| 139 | + * you wish */ |
| 140 | + |
| 141 | + |
| 142 | + /* Using a wrapped key to do crypto operations*/ |
| 143 | + |
| 144 | + /* Request the server to unwrap and cache the wrapped key we just created. |
| 145 | + * This will provide us back a key ID that the client can use to do crypto |
| 146 | + * operations */ |
| 147 | + ret = wh_Client_KeyUnwrapAndCache(client, WC_CIPHER_AES_GCM, WH_TEST_KEKID, |
| 148 | + wrappedKey, sizeof(wrappedKey), |
| 149 | + &wrappedKeyId); |
| 150 | + if (ret != 0) { |
| 151 | + printf("Failed to wh_Client_KeyUnwrapAndCache %d\n", ret); |
| 152 | + goto cleanup_rng; |
| 153 | + } |
| 154 | + |
| 155 | + /* Initialize AES context */ |
| 156 | + ret = wc_AesInit(aes, NULL, WH_DEV_ID); |
| 157 | + if (ret != 0) { |
| 158 | + printf("Failed to wc_AesInit %d\n", ret); |
| 159 | + goto cleanup_cached_key; |
76 | 160 | } |
77 | 161 |
|
78 | | - ret = |
79 | | - whTest_Client_UseWrappedKeysFromNvm(client, &flashFileCtx, flashFileCb); |
| 162 | + /* Set the key id for this AES context to the wrapped key ID that the server |
| 163 | + * provided us */ |
| 164 | + ret = wh_Client_AesSetKeyId(aes, wrappedKeyId); |
| 165 | + if (ret != 0) { |
| 166 | + printf("Failed to wh_Client_AesSetKeyId %d\n", ret); |
| 167 | + goto cleanup_aes; |
| 168 | + } |
| 169 | + |
| 170 | + /* Generate a random IV for the AES GCM encryption operation we are about to |
| 171 | + * do */ |
| 172 | + ret = wc_RNG_GenerateBlock(rng, iv, sizeof(iv)); |
| 173 | + if (ret != 0) { |
| 174 | + printf("Failed to wc_RNG_GenerateBlock for AES-GCM key %d\n", ret); |
| 175 | + goto cleanup_aes; |
| 176 | + } |
| 177 | + |
| 178 | + /* Request the server to encrypt some data using the |
| 179 | + * unwrapped and cached key via the key ID */ |
| 180 | + ret = wc_AesGcmEncrypt(aes, ciphertext, plaintext, sizeof(plaintext), iv, |
| 181 | + sizeof(iv), tag, sizeof(tag), aad, sizeof(aad)); |
| 182 | + if (ret != 0) { |
| 183 | + printf("Failed to wc_AesGcmEncrypt %d\n", ret); |
| 184 | + goto cleanup_aes; |
| 185 | + } |
| 186 | + |
| 187 | + /* Request the server to decrypt the encrypted data using the |
| 188 | + * unwrapped and cached key via the key ID */ |
| 189 | + ret = wc_AesGcmDecrypt(aes, decrypted, /* out */ |
| 190 | + ciphertext, sizeof(ciphertext), /* in, inLen */ |
| 191 | + iv, sizeof(iv), /* iv, ivLen */ |
| 192 | + tag, sizeof(tag), /* authTag, authTagSz */ |
| 193 | + aad, sizeof(aad)); /* authIn (AAD), authInSz */ |
| 194 | + if (ret != 0) { |
| 195 | + ret = WH_ERROR_ABORTED; |
| 196 | + printf("Failed to wc_AesGcmDecrypt %d\n", ret); |
| 197 | + goto cleanup_aes; |
| 198 | + } |
| 199 | + |
| 200 | + /* Check if the decrypted data matches an expected value */ |
| 201 | + if (memcmp(decrypted, plaintext, sizeof(decrypted)) != 0) { |
| 202 | + ret = WH_ERROR_ABORTED; |
| 203 | + printf("Decrypted value does not match expected value\n"); |
| 204 | + goto cleanup_aes; |
| 205 | + } |
| 206 | + |
| 207 | + /* Exporting a wrapped key */ |
| 208 | + |
| 209 | + /* Request the server to unwrap and export the wrapped key we created */ |
| 210 | + ret = wh_Client_KeyUnwrapAndExport(client, WC_CIPHER_AES_GCM, WH_TEST_KEKID, |
| 211 | + wrappedKey, sizeof(wrappedKey), |
| 212 | + &exportedMetadata, exportedKey, |
| 213 | + sizeof(exportedKey)); |
| 214 | + if (ret != 0) { |
| 215 | + printf("Failed to wh_Client_KeyUnwrapAndCache %d\n", ret); |
| 216 | + goto cleanup_aes; |
| 217 | + } |
| 218 | + |
| 219 | + /* Compare the exported key to the client key we requested to wrap */ |
| 220 | + if (memcmp(key, exportedKey, sizeof(key)) != 0) { |
| 221 | + ret = WH_ERROR_ABORTED; |
| 222 | + printf("AES GCM wrap/unwrap key failed to match\n"); |
| 223 | + goto cleanup_aes; |
| 224 | + } |
| 225 | + |
| 226 | + /* Compare the exported metadata to the metadata we requested to wrap */ |
| 227 | + if (memcmp(&metadata, &exportedMetadata, sizeof(metadata)) != 0) { |
| 228 | + ret = WH_ERROR_ABORTED; |
| 229 | + printf("AES GCM wrap/unwrap metadata failed to match\n"); |
| 230 | + goto cleanup_aes; |
| 231 | + } |
| 232 | + |
| 233 | +cleanup_aes: |
| 234 | + wc_AesFree(aes); |
| 235 | +cleanup_cached_key: |
| 236 | + wh_Client_KeyErase(client, wrappedKeyId); |
| 237 | +cleanup_rng: |
| 238 | + wc_FreeRng(rng); |
| 239 | +cleanup_kek: |
| 240 | + _CleanupServerKek(client); |
| 241 | + |
| 242 | + return ret; |
| 243 | +} |
| 244 | + |
| 245 | +#endif /* HAVE_AESGCM */ |
| 246 | + |
| 247 | + |
| 248 | +#endif /* !NO_AES */ |
| 249 | + |
| 250 | +int wh_DemoClient_KeyWrap(whClientContext* client) |
| 251 | +{ |
| 252 | + |
| 253 | + int ret; |
| 254 | + |
| 255 | +#ifndef NO_AES |
| 256 | +#ifdef HAVE_AESGCM |
| 257 | + |
| 258 | + ret = wh_DemoClient_AesGcmKeyWrap(client); |
80 | 259 | if (ret != WH_ERROR_OK) { |
81 | | - printf("Failed to whTest_Client_UseWrappedKeysFromNvm %d\n", ret); |
| 260 | + printf("Failed to wh_DemoClient_AesGcmKeyWrap %d\n", ret); |
82 | 261 | return ret; |
83 | 262 | } |
84 | 263 |
|
85 | | - flashFileCb->Cleanup(&flashFileCtx); |
| 264 | +#endif /* !NO_AES */ |
| 265 | +#endif /* HAVE_AESGCM */ |
86 | 266 |
|
87 | 267 | return ret; |
88 | 268 | } |
89 | 269 | #endif /* WOLFHSM_CFG_KEYWRAP */ |
90 | | -#endif /* WOLFHSM_CFG_TEST_POSIX */ |
|
0 commit comments