Skip to content

Commit 3f457a0

Browse files
nasahlpavogelpi
authored andcommitted
[pentest] AES and KMAC FI tests
This commit adds the following fault injection tests for AES and KMAC: -crypto_fi_aes_key -crypto_fi_aes_plaintext -crypto_fi_aes_encrypt -crypto_fi_aes_ciphertext -crypto_fi_kmac_key -crypto_fi_kmac_absorb -crypto_fi_kmac_squeeze The host code is located in lowRISC/ot-sca#358 Signed-off-by: Pascal Nasahl <[email protected]>
1 parent 9cdc3bf commit 3f457a0

File tree

8 files changed

+440
-0
lines changed

8 files changed

+440
-0
lines changed

sw/device/tests/penetrationtests/firmware/BUILD

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,25 @@ cc_library(
5151
],
5252
)
5353

54+
cc_library(
55+
name = "crypto_fi",
56+
srcs = ["crypto_fi.c"],
57+
hdrs = ["crypto_fi.h"],
58+
deps = [
59+
"//sw/device/lib/base:memory",
60+
"//sw/device/lib/base:status",
61+
"//sw/device/lib/dif:aes",
62+
"//sw/device/lib/dif:kmac",
63+
"//sw/device/lib/runtime:log",
64+
"//sw/device/lib/testing:aes_testutils",
65+
"//sw/device/lib/testing/test_framework:ujson_ottf",
66+
"//sw/device/lib/ujson",
67+
"//sw/device/sca/lib:sca",
68+
"//sw/device/tests/penetrationtests/firmware:sca_lib",
69+
"//sw/device/tests/penetrationtests/json:crypto_fi_commands",
70+
],
71+
)
72+
5473
cc_library(
5574
name = "ibex_fi",
5675
srcs = [
@@ -225,6 +244,7 @@ cc_library(
225244

226245
FIRMWARE_DEPS = [
227246
":aes_sca",
247+
":crypto_fi",
228248
":extclk_sca_fi",
229249
":hmac_sca",
230250
":ibex_fi",
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
// Copyright lowRISC contributors (OpenTitan project).
2+
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include "sw/device/tests/penetrationtests/firmware/crypto_fi.h"
6+
7+
#include "hw/ip/aes/model/aes_modes.h"
8+
#include "sw/device/lib/base/memory.h"
9+
#include "sw/device/lib/base/status.h"
10+
#include "sw/device/lib/dif/dif_aes.h"
11+
#include "sw/device/lib/dif/dif_kmac.h"
12+
#include "sw/device/lib/runtime/log.h"
13+
#include "sw/device/lib/testing/aes_testutils.h"
14+
#include "sw/device/lib/testing/test_framework/ottf_test_config.h"
15+
#include "sw/device/lib/testing/test_framework/ujson_ottf.h"
16+
#include "sw/device/lib/ujson/ujson.h"
17+
#include "sw/device/sca/lib/sca.h"
18+
#include "sw/device/tests/penetrationtests/firmware/sca_lib.h"
19+
#include "sw/device/tests/penetrationtests/json/crypto_fi_commands.h"
20+
21+
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
22+
23+
enum {
24+
/**
25+
* Timeout for waiting that an AES operation has completed.
26+
*/
27+
kAesWaitTimeout = 1000000,
28+
};
29+
30+
static dif_aes_t aes;
31+
static dif_kmac_t kmac;
32+
33+
static dif_aes_key_share_t aes_key_shares;
34+
static dif_aes_data_t aes_plaintext;
35+
36+
/**
37+
* KMAC test description.
38+
*/
39+
typedef struct kmac_test {
40+
dif_kmac_mode_kmac_t mode;
41+
dif_kmac_key_t key;
42+
43+
const char *message;
44+
size_t message_len;
45+
46+
const char *customization_string;
47+
size_t customization_string_len;
48+
49+
const uint32_t digest[16];
50+
size_t digest_len;
51+
bool digest_len_is_fixed;
52+
} kmac_test_t;
53+
54+
/**
55+
* A single KMAC example:
56+
* https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/KMAC_samples.pdf
57+
*/
58+
const kmac_test_t kKmacTestVector = {
59+
.mode = kDifKmacModeKmacLen256,
60+
.key =
61+
(dif_kmac_key_t){
62+
.share0 = {0x43424140, 0x47464544, 0x4b4a4948, 0x4f4e4f4c,
63+
0x53525150, 0x57565554, 0x5b5a5958, 0x5f5e5d5c},
64+
.share1 = {0},
65+
.length = kDifKmacKeyLen256,
66+
},
67+
.message =
68+
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
69+
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
70+
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
71+
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
72+
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
73+
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
74+
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
75+
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
76+
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
77+
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
78+
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
79+
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
80+
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7",
81+
.message_len = 200,
82+
.customization_string = "My Tagged Application",
83+
.customization_string_len = 21,
84+
.digest = {0x1c73bed5, 0x73d74e95, 0x59bb4628, 0xe3a8e3db, 0x7ae7830f,
85+
0x5944ff4b, 0xb4c2f1f2, 0xceb8ebec, 0xc601ba67, 0x57b88a2e,
86+
0x9b492d8d, 0x6727bbd1, 0x90117868, 0x6a300a02, 0x1d28de97,
87+
0x5d3030cc},
88+
.digest_len = 16,
89+
.digest_len_is_fixed = false,
90+
};
91+
92+
static const uint8_t kKeyShare1[] = {
93+
0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f, 0xaf,
94+
0xbf, 0xcf, 0xdf, 0xef, 0xff, 0x0a, 0x1a, 0x2a, 0x3a, 0x4a, 0x5a,
95+
0x6a, 0x7a, 0x8a, 0x9a, 0xaa, 0xba, 0xca, 0xda, 0xea, 0xfa,
96+
};
97+
98+
static dif_aes_transaction_t transaction = {
99+
.operation = kDifAesOperationEncrypt,
100+
.mode = kDifAesModeEcb,
101+
.key_len = kDifAesKey128,
102+
.manual_operation = kDifAesManualOperationManual,
103+
.key_provider = kDifAesKeySoftwareProvided,
104+
.mask_reseeding = kDifAesReseedPer8kBlock,
105+
.reseed_on_key_change = false,
106+
.force_masks = false,
107+
.ctrl_aux_lock = false,
108+
};
109+
110+
status_t handle_crypto_fi_aes(ujson_t *uj) {
111+
// Get the test mode.
112+
crypto_fi_aes_mode_t uj_data;
113+
TRY(ujson_deserialize_crypto_fi_aes_mode_t(uj, &uj_data));
114+
// Clear registered alerts in alert handler.
115+
sca_registered_alerts_t reg_alerts = sca_get_triggered_alerts();
116+
117+
// Write the key into the AES block. Set and unset the trigger when
118+
// key_trigger is true.
119+
if (uj_data.key_trigger) {
120+
sca_set_trigger_high();
121+
}
122+
TRY(dif_aes_start(&aes, &transaction, &aes_key_shares, NULL));
123+
AES_TESTUTILS_WAIT_FOR_STATUS(&aes, kDifAesStatusInputReady, true,
124+
kAesWaitTimeout);
125+
if (uj_data.key_trigger) {
126+
sca_set_trigger_low();
127+
}
128+
129+
// Write the plaintext into the AES block. Set and unset the trigger when
130+
// plaintext_trigger is true.
131+
if (uj_data.plaintext_trigger) {
132+
sca_set_trigger_high();
133+
}
134+
TRY(dif_aes_load_data(&aes, aes_plaintext));
135+
if (uj_data.plaintext_trigger) {
136+
sca_set_trigger_low();
137+
}
138+
139+
// Start the encryption. Set and unset the trigger when encrypt_trigger is
140+
// true.
141+
if (uj_data.encrypt_trigger) {
142+
sca_set_trigger_high();
143+
}
144+
TRY(dif_aes_trigger(&aes, kDifAesTriggerStart));
145+
AES_TESTUTILS_WAIT_FOR_STATUS(&aes, kDifAesStatusOutputValid, true,
146+
kAesWaitTimeout);
147+
if (uj_data.encrypt_trigger) {
148+
sca_set_trigger_low();
149+
}
150+
151+
// Read the ciphertext. Set and unset the trigger when ciphertext_trigger is
152+
// true.
153+
dif_aes_data_t ciphertext;
154+
if (uj_data.ciphertext_trigger) {
155+
sca_set_trigger_high();
156+
}
157+
TRY(dif_aes_read_output(&aes, &ciphertext));
158+
if (uj_data.ciphertext_trigger) {
159+
sca_set_trigger_low();
160+
}
161+
162+
// Get registered alerts from alert handler.
163+
reg_alerts = sca_get_triggered_alerts();
164+
165+
// Send the ciphertext and the alerts back to the host.
166+
crypto_fi_aes_ciphertext_t uj_output;
167+
memcpy(uj_output.ciphertext, ciphertext.data, 16);
168+
uj_output.alerts_1 = reg_alerts.alerts_1;
169+
uj_output.alerts_2 = reg_alerts.alerts_2;
170+
uj_output.alerts_3 = reg_alerts.alerts_3;
171+
RESP_OK(ujson_serialize_crypto_fi_aes_ciphertext_t, uj, &uj_output);
172+
return OK_STATUS();
173+
}
174+
175+
status_t handle_crypto_fi_kmac(ujson_t *uj) {
176+
// Get the test mode.
177+
crypto_fi_kmac_mode_t uj_data;
178+
TRY(ujson_deserialize_crypto_fi_kmac_mode_t(uj, &uj_data));
179+
// Clear registered alerts in alert handler.
180+
sca_registered_alerts_t reg_alerts = sca_get_triggered_alerts();
181+
182+
// Configure and write key to the KMAC block. Set and unset the trigger when
183+
// key_trigger is true.
184+
dif_kmac_operation_state_t kmac_operation_state;
185+
if (uj_data.key_trigger) {
186+
sca_set_trigger_high();
187+
}
188+
189+
TRY(dif_kmac_mode_kmac_start(&kmac, &kmac_operation_state,
190+
kKmacTestVector.mode, 0, &kKmacTestVector.key,
191+
NULL));
192+
if (uj_data.key_trigger) {
193+
sca_set_trigger_low();
194+
}
195+
196+
// Absorb. Set and unset the trigger when absorb_trigger is true.
197+
if (uj_data.absorb_trigger) {
198+
sca_set_trigger_high();
199+
}
200+
TRY(dif_kmac_absorb(&kmac, &kmac_operation_state, kKmacTestVector.message,
201+
kKmacTestVector.message_len, NULL));
202+
if (uj_data.absorb_trigger) {
203+
sca_set_trigger_low();
204+
}
205+
206+
// Squeeze. Set and unset the trigger when squeeze_trigger is true.
207+
uint32_t digest[kKmacTestVector.digest_len];
208+
if (uj_data.squeeze_trigger) {
209+
sca_set_trigger_high();
210+
}
211+
TRY(dif_kmac_squeeze(&kmac, &kmac_operation_state, digest,
212+
kKmacTestVector.digest_len, /*processed=*/NULL));
213+
if (uj_data.squeeze_trigger) {
214+
sca_set_trigger_low();
215+
}
216+
217+
// Get registered alerts from alert handler.
218+
reg_alerts = sca_get_triggered_alerts();
219+
220+
TRY(dif_kmac_end(&kmac, &kmac_operation_state));
221+
222+
// Send the first 8 bytes of the digest and the alerts back to the host.
223+
crypto_fi_kmac_digest_t uj_output;
224+
memcpy(uj_output.digest, (uint8_t *)digest, 8);
225+
uj_output.alerts_1 = reg_alerts.alerts_1;
226+
uj_output.alerts_2 = reg_alerts.alerts_2;
227+
uj_output.alerts_3 = reg_alerts.alerts_3;
228+
RESP_OK(ujson_serialize_crypto_fi_kmac_digest_t, uj, &uj_output);
229+
return OK_STATUS();
230+
}
231+
232+
status_t handle_crypto_fi_init(ujson_t *uj) {
233+
sca_select_trigger_type(kScaTriggerTypeSw);
234+
sca_init(kScaTriggerSourceAes,
235+
kScaPeripheralIoDiv4 | kScaPeripheralAes | kScaPeripheralKmac |
236+
kScaPeripheralEdn | kScaPeripheralCsrng | kScaPeripheralEntropy);
237+
// Configure the alert handler. Alerts triggered by IP blocks are captured
238+
// and reported to the test.
239+
sca_configure_alert_handler();
240+
241+
// Disable the instruction cache and dummy instructions for FI attacks.
242+
sca_configure_cpu();
243+
244+
// Init the AES block.
245+
TRY(dif_aes_init(mmio_region_from_addr(TOP_EARLGREY_AES_BASE_ADDR), &aes));
246+
TRY(dif_aes_reset(&aes));
247+
248+
// Mask the AES key.
249+
uint8_t key_share0[sizeof(kAesModesKey256)];
250+
for (int i = 0; i < sizeof(kAesModesKey256); ++i) {
251+
key_share0[i] = kAesModesKey256[i] ^ kKeyShare1[i];
252+
}
253+
// "Convert" AES key share byte arrays to `dif_aes_key_share_t`.
254+
memcpy(aes_key_shares.share0, key_share0, sizeof(aes_key_shares.share0));
255+
memcpy(aes_key_shares.share1, kKeyShare1, sizeof(aes_key_shares.share1));
256+
// Copy the plaintext into `dif_aes_data_t`.
257+
memcpy(aes_plaintext.data, kAesModesPlainText, sizeof(aes_plaintext.data));
258+
259+
// Init the KMAC block.
260+
TRY(dif_kmac_init(mmio_region_from_addr(TOP_EARLGREY_KMAC_BASE_ADDR), &kmac));
261+
262+
dif_kmac_config_t config = (dif_kmac_config_t){
263+
.entropy_mode = kDifKmacEntropyModeSoftware,
264+
.entropy_fast_process = kDifToggleDisabled,
265+
.entropy_seed = {0xaa25b4bf, 0x48ce8fff, 0x5a78282a, 0x48465647,
266+
0x70410fef},
267+
.message_big_endian = kDifToggleDisabled,
268+
.output_big_endian = kDifToggleDisabled,
269+
.sideload = kDifToggleDisabled,
270+
.msg_mask = kDifToggleEnabled,
271+
};
272+
273+
TRY(dif_kmac_configure(&kmac, config));
274+
275+
return OK_STATUS();
276+
}
277+
278+
status_t handle_crypto_fi(ujson_t *uj) {
279+
crypto_fi_subcommand_t cmd;
280+
TRY(ujson_deserialize_crypto_fi_subcommand_t(uj, &cmd));
281+
switch (cmd) {
282+
case kCryptoFiSubcommandInit:
283+
return handle_crypto_fi_init(uj);
284+
case kCryptoFiSubcommandAes:
285+
return handle_crypto_fi_aes(uj);
286+
case kCryptoFiSubcommandKmac:
287+
return handle_crypto_fi_kmac(uj);
288+
default:
289+
LOG_ERROR("Unrecognized Crypto FI subcommand: %d", cmd);
290+
return INVALID_ARGUMENT();
291+
}
292+
return OK_STATUS();
293+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright lowRISC contributors (OpenTitan project).
2+
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#ifndef OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_CRYPTO_FI_H_
6+
#define OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_CRYPTO_FI_H_
7+
8+
#include "sw/device/lib/base/status.h"
9+
#include "sw/device/lib/ujson/ujson.h"
10+
11+
/**
12+
* KMAC FI test.
13+
*
14+
* This test absorbs a static message with a static key and returns the
15+
* digest over UART back to the host. The host can define, whether the
16+
* trigger gets set and unset during(i) loading the key, (ii) absorbing, or
17+
* (iii) squeezing.
18+
*
19+
* @param uj An initialized uJSON context.
20+
* @return OK or error.
21+
*/
22+
status_t handle_crypto_fi_kmac(ujson_t *uj);
23+
24+
/**
25+
* AES FI test.
26+
*
27+
* This test encrypts a static plaintext with a static key and returns the
28+
* ciphertext over UART back to the host. The host can define, whether the
29+
* trigger gets set and unset during(i) loading the key, (ii) loading the
30+
* plaintext, (iii) encryption, or (iv) when reading back the ciphertext.
31+
*
32+
* @param uj An initialized uJSON context.
33+
* @return OK or error.
34+
*/
35+
status_t handle_crypto_fi_aes(ujson_t *uj);
36+
37+
/**
38+
* Initializes the trigger and configures the device for the Crypto FI test.
39+
*
40+
* @param uj An initialized uJSON context.
41+
* @return OK or error.
42+
*/
43+
status_t handle_crypto_fi_init(ujson_t *uj);
44+
45+
/**
46+
* Crypto FI command handler.
47+
*
48+
* Command handler for the Crypto FI command.
49+
*
50+
* @param uj An initialized uJSON context.
51+
* @return OK or error.
52+
*/
53+
status_t handle_crypto_fi(ujson_t *uj);
54+
55+
#endif // OPENTITAN_SW_DEVICE_TESTS_PENETRATIONTESTS_FIRMWARE_CRYPTO_FI_H_

0 commit comments

Comments
 (0)