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