1 /*
2 * Copyright (c) 2020-2022, Arm Limited. All rights reserved.
3 * Copyright (c) 2024 Cypress Semiconductor Corporation (an Infineon
4 * company) or an affiliate of Cypress Semiconductor Corporation. All rights
5 * reserved.
6 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 *
9 */
10
11 #ifndef __TFM_FIH_H__
12 #define __TFM_FIH_H__
13
14 #include <stddef.h>
15 #include <stdint.h>
16
17 /*
18 * Fault injection mitigation library.
19 *
20 * Has support for different measures, which can either be enabled/disabled
21 * separately or by defining one of the TFM_FIH_PROFILEs.
22 *
23 * NOTE: It is not guaranteed that these constructs against fault injection
24 * attacks can be preserved in all compilers.
25 *
26 * FIH_ENABLE_DOUBLE_VARS makes critical variables into a tuple (x, x ^ msk).
27 * Then the correctness of x can be checked by XORing the two tuple values
28 * together. This also means that comparisons between fih_ints can be verified
29 * by doing x == y && x_msk == y_msk.
30 *
31 * FIH_ENABLE_GLOBAL_FAIL makes all while(1) failure loops redirect to a global
32 * failure loop. This loop has mitigations against loop escapes / unlooping.
33 * This also means that any unlooping won't immediately continue executing the
34 * function that was executing before the failure.
35 *
36 * FIH_ENABLE_CFI (Control Flow Integrity) creates a global counter that is
37 * incremented before every FIH_CALL of vulnerable functions. On the function
38 * return the counter is decremented, and after the return it is verified that
39 * the counter has the same value as before this process. This can be used to
40 * verify that the function has actually been called. This protection is
41 * intended to discover that important functions are called in an expected
42 * sequence and none of them is missed due to an instruction skip which could
43 * be a result of glitching attack. It does not provide protection against ROP
44 * or JOP attacks.
45 *
46 * FIH_ENABLE_DELAY causes random delays. This makes it hard to cause faults
47 * precisely. It requires an RNG. A simple example using SysTick as entropy
48 * source is provided in tfm_fih_rng.h, but any RNG that has an entropy
49 * source can be used by implementing the fih_delay_random function.
50 *
51 * The basic call pattern is:
52 *
53 * fih_int fih_rc = FIH_FAILURE;
54 * FIH_CALL(vulnerable_function, fih_rc, arg1, arg2);
55 * if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
56 * error_handling();
57 * }
58 *
59 * If a fault injection is detected, call FIH_PANIC to trap the execution.
60 *
61 * Note that any function called by FIH_CALL must only return using FIH_RET,
62 * as otherwise the CFI counter will not be decremented and the CFI check will
63 * fail causing a panic.
64 */
65
66 #ifdef __cplusplus
67 extern "C" {
68 #endif /* __cplusplus */
69
70 #undef FIH_ENABLE_GLOBAL_FAIL
71 #undef FIH_ENABLE_CFI
72 #undef FIH_ENABLE_DOUBLE_VARS
73 #undef FIH_ENABLE_DELAY
74
75 #ifdef TFM_FIH_PROFILE_ON
76 #if defined(TFM_FIH_PROFILE_LOW)
77 #define FIH_ENABLE_GLOBAL_FAIL
78 #define FIH_ENABLE_CFI
79
80 #elif defined(TFM_FIH_PROFILE_MEDIUM)
81 #define FIH_ENABLE_DOUBLE_VARS
82 #define FIH_ENABLE_GLOBAL_FAIL
83 #define FIH_ENABLE_CFI
84
85 #elif defined(TFM_FIH_PROFILE_HIGH)
86 #define FIH_ENABLE_DELAY /* Requires an hardware entropy source */
87 #define FIH_ENABLE_DOUBLE_VARS
88 #define FIH_ENABLE_GLOBAL_FAIL
89 #define FIH_ENABLE_CFI
90
91 #else
92 #error "Invalid FIH Profile configuration"
93 #endif /* TFM_FIH_PROFILE */
94
95 #define FIH_TRUE 0xC35A
96 #define FIH_FALSE 0x0
97
98 #ifdef FIH_ENABLE_DOUBLE_VARS
99 #define FIH_POSITIVE_VALUE 0x5555AAAA
100 #define FIH_NEGATIVE_VALUE 0xAAAA5555
101
102 /*
103 * A volatile mask is used to prevent compiler optimization - the mask is xored
104 * with the variable to create the backup and the integrity can be checked with
105 * another xor. The mask value doesn't _really_ matter that much, as long as
106 * it has reasonably high hamming weight.
107 */
108 #define _FIH_MASK_VALUE 0xA5C35A3C
109
110 /*
111 * All ints are replaced with two int - the normal one and a backup which is
112 * XORed with the mask.
113 */
114 typedef struct {
115 volatile int32_t val;
116 volatile int32_t msk;
117 } fih_int;
118
119 #define FIH_INT_INIT(x) {(x), (x) ^ _FIH_MASK_VALUE}
120 #else /* FIH_ENABLE_DOUBLE_VARS */
121 #define FIH_POSITIVE_VALUE 0x0
122 #define FIH_NEGATIVE_VALUE 0xAAAA5555
123
124 typedef volatile int32_t fih_int;
125
126 #define FIH_INT_INIT(x) (x)
127 #endif /* FIH_ENABLE_DOUBLE_VARS */
128
129 extern fih_int FIH_SUCCESS;
130 extern fih_int FIH_FAILURE;
131
132 #ifdef FIH_ENABLE_GLOBAL_FAIL
133 /*
134 * Global failure handler - more resistant to unlooping. noinline and used are
135 * used to prevent optimization.
136 *
137 * NOTE
138 * This failure handler shall be used as FIH specific error handling to capture
139 * FI attacks. Error handling in SPM and SP should be enhanced respectively.
140 */
141 __attribute__((noinline)) __attribute__((used)) void fih_panic_loop(void);
142 #define FIH_PANIC fih_panic_loop()
143 #else /* FIH_ENABLE_GLOBAL_FAIL */
144 #define FIH_PANIC \
145 do { \
146 FIH_LABEL("FAILURE_LOOP"); \
147 while (1) {} \
148 } while (0)
149 #endif /* FIH_ENABLE_GLOBAL_FAIL */
150
151 /*
152 * NOTE
153 * For functions to be inlined outside their compilation unit they have to
154 * have the body in the header file. This is required as function calls are easy
155 * to skip.
156 */
157 #ifdef FIH_ENABLE_DELAY
158 /**
159 * @brief Set up the RNG for use with random delays. Called once at startup.
160 */
161 void fih_delay_init(void);
162
163 /**
164 * Get a random uint8_t value from an RNG seeded with an entropy source.
165 *
166 * NOTE
167 * Do not directly call this function.
168 */
169 uint8_t fih_delay_random(void);
170
171 /* Delaying logic, with randomness from a CSPRNG */
172 __attribute__((always_inline)) inline
fih_delay(void)173 int fih_delay(void)
174 {
175 uint32_t i = 0;
176 volatile uint32_t delay = FIH_NEGATIVE_VALUE;
177 volatile uint32_t counter = 0;
178
179 delay = fih_delay_random();
180
181 if (delay == FIH_NEGATIVE_VALUE) {
182 FIH_PANIC;
183 }
184
185 delay &= 0xFF;
186
187 for (i = 0; i < delay; i++) {
188 counter++;
189 }
190
191 if (counter != delay) {
192 FIH_PANIC;
193 }
194
195 return 1;
196 }
197 #else /* FIH_ENABLE_DELAY */
198 #define fih_delay_init()
199
200 #define fih_delay() 1
201 #endif /* FIH_ENABLE_DELAY */
202
203 #ifdef FIH_ENABLE_DOUBLE_VARS
204 __attribute__((always_inline)) inline
fih_int_validate(fih_int x)205 int fih_int_validate(fih_int x)
206 {
207 if (x.val != (x.msk ^ _FIH_MASK_VALUE)) {
208 FIH_PANIC;
209 }
210
211 return 1;
212 }
213
214 /* Convert a fih_int to an int. Validate for tampering. */
215 __attribute__((always_inline)) inline
fih_int_decode(fih_int x)216 int32_t fih_int_decode(fih_int x)
217 {
218 (void)fih_int_validate(x);
219 return x.val;
220 }
221
222 /* Convert an int to a fih_int, can be used to encode specific error codes. */
223 __attribute__((always_inline)) inline
fih_int_encode(int32_t x)224 fih_int fih_int_encode(int32_t x)
225 {
226 fih_int ret = {x, x ^ _FIH_MASK_VALUE};
227 return ret;
228 }
229
230 /* Standard equality. If A == B then 1, else 0 */
231 /* Note that this is designed to be difficult to glitch so as to make it
232 * return 1 when it should return 0. It may not be as resistant the other
233 * way.
234 */
235 #define fih_eq(x, y) \
236 ( fih_int_validate(x) && \
237 fih_int_validate(y) && \
238 ((x).val == (y).val) && \
239 fih_delay() && \
240 ((x).msk == (y).msk) && \
241 fih_delay() && \
242 ((x).val == (_FIH_MASK_VALUE ^ (y).msk)) )
243
244 /* Note that this is designed to be difficult to glitch so as to make it
245 * return 0 when it should return 1. It may not be as resistant the other
246 * way.
247 */
248 #define fih_not_eq(x, y) \
249 ( !fih_int_validate(x) || \
250 !fih_int_validate(y) || \
251 ((x).val != (y).val) || \
252 !fih_delay() || \
253 ((x).msk != (y).msk) || \
254 !fih_delay() || \
255 ((x).val != (_FIH_MASK_VALUE ^ (y).msk)) )
256 #else /* FIH_ENABLE_DOUBLE_VARS */
257 /* NOOP */
258 #define fih_int_validate(x) 1
259
260 /* NOOP */
261 #define fih_int_decode(x) (x)
262
263 /* NOOP */
264 #define fih_int_encode(x) (x)
265
266 #define fih_eq(x, y) \
267 ( (x == y) && \
268 fih_delay() && \
269 !(x != y) )
270
271 #define fih_not_eq(x, y) \
272 ( (x != y) || \
273 !fih_delay() || \
274 !(x == y) )
275 #endif /* FIH_ENABLE_DOUBLE_VARS */
276
277 /*
278 * C has a common return pattern where 0 is a correct value and all others are
279 * errors. This function converts 0 to FIH_SUCCESS and any other number to a
280 * value that is not FIH_SUCCESS
281 */
282 __attribute__((always_inline)) inline
fih_int_encode_zero_equality(int32_t x)283 fih_int fih_int_encode_zero_equality(int32_t x)
284 {
285 if (x) {
286 return FIH_FAILURE;
287 } else {
288 return FIH_SUCCESS;
289 }
290 }
291
292 #ifdef FIH_ENABLE_CFI
293 /* Global Control Flow Integrity counter */
294 extern fih_int _fih_cfi_ctr;
295
296 /*
297 * Increment the CFI counter by input counter and return the value before the
298 * increment.
299 *
300 * NOTE
301 * This function shall not be called directly.
302 */
303 fih_int fih_cfi_get_and_increment(uint8_t cnt);
304
305 /*
306 * Validate that the saved precall value is the same as the value of the global
307 * counter. For this to be the case, a fih_ret must have been called between
308 * these functions being executed. If the values aren't the same then panic.
309 *
310 * NOTE
311 * This function shall not be called directly.
312 */
313 void fih_cfi_validate(fih_int saved);
314
315 /*
316 * Decrement the global CFI counter by one, so that it has the same value as
317 * before the cfi_precall.
318 *
319 * NOTE
320 * This function shall not be called directly.
321 */
322 void fih_cfi_decrement(void);
323
324 /*
325 * Macro wrappers for functions - Even when the functions have zero body this
326 * saves a few bytes on noop functions as it doesn't generate the call/ret
327 *
328 * CFI precall function saves the CFI counter and then increments it - the
329 * postcall then checks if the counter is equal to the saved value. In order for
330 * this to be the case a FIH_RET must have been performed inside the called
331 * function in order to decrement the counter, so the function must have been
332 * called.
333 */
334 #define FIH_CFI_PRECALL_BLOCK \
335 fih_int _fih_cfi_precall_saved_value = fih_cfi_get_and_increment(1)
336
337 #define FIH_CFI_POSTCALL_BLOCK \
338 fih_cfi_validate(_fih_cfi_precall_saved_value)
339
340 #define FIH_CFI_PRERET \
341 fih_cfi_decrement()
342
343 /*
344 * Marcos to support protect the control flow integrity inside a function.
345 *
346 * The FIH_CFI_PRECALL_BLOCK/FIH_CFI_POSTCALL_BLOCK pair mainly protect function
347 * calls from fault injection. Fault injection may attack a function to skip its
348 * critical steps which are not function calls. It is difficult for the caller
349 * to dectect the injection as long as the function successfully returns.
350 *
351 * The following macros can be called in a function to track the critical steps,
352 * especially those which are not function calls.
353 */
354 /*
355 * FIH_CFI_STEP_INIT() saves the CFI counter and increase the CFI counter by the
356 * number of the critical steps. It should be called before execution starts.
357 */
358 #define FIH_CFI_STEP_INIT(x) \
359 fih_int _fih_cfi_step_saved_value = fih_cfi_get_and_increment(x)
360
361 /*
362 * FIH_CFI_STEP_DECREMENT() decrease the CFI counter by one. It can be called
363 * after each critical step execution completes.
364 */
365 #define FIH_CFI_STEP_DECREMENT() \
366 fih_cfi_decrement()
367
368 /*
369 * FIH_CFI_STEP_ERR_RESET() resets the CFI counter to the previous value saved
370 * by FIH_CFI_STEP_INIT(). It shall be called only when a functionality error
371 * occurs and forces the function to exit. It can enable the caller to capture
372 * the functionality error other than being trapped in fault injection error
373 * handling.
374 */
375 #define FIH_CFI_STEP_ERR_RESET() \
376 do { \
377 _fih_cfi_ctr = _fih_cfi_step_saved_value; \
378 (void)fih_int_validate(_fih_cfi_ctr); \
379 } while(0)
380
381 #else /* FIH_ENABLE_CFI */
382 #define FIH_CFI_PRECALL_BLOCK
383 #define FIH_CFI_POSTCALL_BLOCK
384 #define FIH_CFI_PRERET
385
386 #define FIH_CFI_STEP_INIT(x)
387 #define FIH_CFI_STEP_DECREMENT()
388 #define FIH_CFI_STEP_ERR_RESET()
389 #endif /* FIH_ENABLE_CFI */
390
391 /*
392 * Label for interacting with FIH testing tool. Can be parsed from the elf file
393 * after compilation. Does not require debug symbols.
394 */
395 #define FIH_LABEL(str) __asm volatile ("FIH_LABEL_" str "_0_%=:" ::)
396 #define FIH_LABEL_CRITICAL_POINT() FIH_LABEL("FIH_CRITICAL_POINT")
397
398 /*
399 * Main FIH calling macro. return variable is second argument. Does some setup
400 * before and validation afterwards. Inserts labels for use with testing script.
401 *
402 * First perform the precall step - this gets the current value of the CFI
403 * counter and saves it to a local variable, and then increments the counter.
404 *
405 * Then set the return variable to FIH_FAILURE as a base case.
406 *
407 * Then perform the function call. As part of the function FIH_RET must be
408 * called which will decrement the counter.
409 *
410 * The postcall step gets the value of the counter and compares it to the
411 * previously saved value. If this is equal then the function call and all child
412 * function calls were performed.
413 */
414 #define FIH_CALL(f, ret, ...) \
415 do { \
416 FIH_LABEL("FIH_CALL_START_" # f); \
417 FIH_CFI_PRECALL_BLOCK; \
418 ret = FIH_FAILURE; \
419 (void)fih_delay(); \
420 ret = f(__VA_ARGS__); \
421 FIH_CFI_POSTCALL_BLOCK; \
422 (void)fih_int_validate(ret); \
423 FIH_LABEL("FIH_CALL_END"); \
424 } while (0)
425
426 /*
427 * FIH return changes the state of the internal state machine. If you do a
428 * FIH_CALL then you need to do a FIH_RET else the state machine will detect
429 * tampering and panic.
430 */
431 #define FIH_RET(ret) \
432 do { \
433 FIH_CFI_PRERET; \
434 return ret; \
435 } while (0)
436
437 /*
438 * FIH return type macro changes the function return types to fih_int.
439 * All functions that need to be protected by FIH and called via FIH_CALL must
440 * return a fih_int type.
441 */
442 #define FIH_RET_TYPE(type) fih_int
443
444 #else /* TFM_FIH_PROFILE_ON */
445 typedef int32_t fih_int;
446
447 #define FIH_INT_INIT(x) (x)
448
449 #define FIH_SUCCESS 0
450 #define FIH_FAILURE -1
451
452 #define fih_int_validate(x) 1
453
454 #define fih_int_decode(x) (x)
455
456 #define fih_int_encode(x) (x)
457
458 #define fih_int_encode_zero_equality(x) ((x) == 0 ? 0 : 1)
459
460 #define fih_eq(x, y) ((x) == (y))
461
462 #define fih_not_eq(x, y) ((x) != (y))
463
464 #define fih_delay_init() (0)
465 #define fih_delay() 1
466
467 #define FIH_CALL(f, ret, ...) \
468 do { \
469 ret = f(__VA_ARGS__); \
470 } while (0)
471
472 #define FIH_RET(ret) \
473 do { \
474 return ret; \
475 } while (0)
476
477 #define FIH_RET_TYPE(type) type
478
479 #define FIH_PANIC do { \
480 while(1) {}; \
481 } while (0)
482
483 #define FIH_CFI_STEP_INIT(x)
484 #define FIH_CFI_STEP_DECREMENT()
485 #define FIH_CFI_STEP_ERR_RESET()
486
487 #define FIH_LABEL_CRITICAL_POINT()
488
489 #endif /* TFM_FIH_PROFILE_ON */
490
491 #ifdef __cplusplus
492 }
493 #endif /* __cplusplus */
494
495 #endif /* __TFM_FIH_H__ */
496