diff options
Diffstat (limited to 'nrfdemo/builddk/tfm/api_ns/platform/include/fih.h')
| -rw-r--r-- | nrfdemo/builddk/tfm/api_ns/platform/include/fih.h | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/nrfdemo/builddk/tfm/api_ns/platform/include/fih.h b/nrfdemo/builddk/tfm/api_ns/platform/include/fih.h new file mode 100644 index 0000000..fcb604b --- /dev/null +++ b/nrfdemo/builddk/tfm/api_ns/platform/include/fih.h @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2020-2022, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef __TFM_FIH_H__ +#define __TFM_FIH_H__ + +#include <stddef.h> +#include <stdint.h> + +/* + * Fault injection mitigation library. + * + * Has support for different measures, which can either be enabled/disabled + * separately or by defining one of the TFM_FIH_PROFILEs. + * + * NOTE: It is not guaranteed that these constructs against fault injection + * attacks can be preserved in all compilers. + * + * FIH_ENABLE_DOUBLE_VARS makes critical variables into a tuple (x, x ^ msk). + * Then the correctness of x can be checked by XORing the two tuple values + * together. This also means that comparisons between fih_ints can be verified + * by doing x == y && x_msk == y_msk. + * + * FIH_ENABLE_GLOBAL_FAIL makes all while(1) failure loops redirect to a global + * failure loop. This loop has mitigations against loop escapes / unlooping. + * This also means that any unlooping won't immediately continue executing the + * function that was executing before the failure. + * + * FIH_ENABLE_CFI (Control Flow Integrity) creates a global counter that is + * incremented before every FIH_CALL of vulnerable functions. On the function + * return the counter is decremented, and after the return it is verified that + * the counter has the same value as before this process. This can be used to + * verify that the function has actually been called. This protection is + * intended to discover that important functions are called in an expected + * sequence and none of them is missed due to an instruction skip which could + * be a result of glitching attack. It does not provide protection against ROP + * or JOP attacks. + * + * FIH_ENABLE_DELAY causes random delays. This makes it hard to cause faults + * precisely. It requires an RNG. A simple example using SysTick as entropy + * source is provided in tfm_fih_rng.h, but any RNG that has an entropy + * source can be used by implementing the fih_delay_random function. + * + * The basic call pattern is: + * + * fih_int fih_rc = FIH_FAILURE; + * FIH_CALL(vulnerable_function, fih_rc, arg1, arg2); + * if (fih_not_eq(fih_rc, FIH_SUCCESS)) { + * error_handling(); + * } + * + * If a fault injection is detected, call FIH_PANIC to trap the execution. + * + * Note that any function called by FIH_CALL must only return using FIH_RET, + * as otherwise the CFI counter will not be decremented and the CFI check will + * fail causing a panic. + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#undef FIH_ENABLE_GLOBAL_FAIL +#undef FIH_ENABLE_CFI +#undef FIH_ENABLE_DOUBLE_VARS +#undef FIH_ENABLE_DELAY + +#ifdef TFM_FIH_PROFILE_ON +#if defined(TFM_FIH_PROFILE_LOW) +#define FIH_ENABLE_GLOBAL_FAIL +#define FIH_ENABLE_CFI + +#elif defined(TFM_FIH_PROFILE_MEDIUM) +#define FIH_ENABLE_DOUBLE_VARS +#define FIH_ENABLE_GLOBAL_FAIL +#define FIH_ENABLE_CFI + +#elif defined(TFM_FIH_PROFILE_HIGH) +#define FIH_ENABLE_DELAY /* Requires an hardware entropy source */ +#define FIH_ENABLE_DOUBLE_VARS +#define FIH_ENABLE_GLOBAL_FAIL +#define FIH_ENABLE_CFI + +#else +#error "Invalid FIH Profile configuration" +#endif /* TFM_FIH_PROFILE */ + +#define FIH_TRUE 0xC35A +#define FIH_FALSE 0x0 + +#ifdef FIH_ENABLE_DOUBLE_VARS +#define FIH_POSITIVE_VALUE 0x5555AAAA +#define FIH_NEGATIVE_VALUE 0xAAAA5555 + +/* + * A volatile mask is used to prevent compiler optimization - the mask is xored + * with the variable to create the backup and the integrity can be checked with + * another xor. The mask value doesn't _really_ matter that much, as long as + * it has reasonably high hamming weight. + */ +#define _FIH_MASK_VALUE 0xA5C35A3C + +/* + * All ints are replaced with two int - the normal one and a backup which is + * XORed with the mask. + */ +typedef volatile struct { + volatile int32_t val; + volatile int32_t msk; +} fih_int; + +#define FIH_INT_INIT(x) {(x), (x) ^ _FIH_MASK_VALUE} +#else /* FIH_ENABLE_DOUBLE_VARS */ +#define FIH_POSITIVE_VALUE 0x0 +#define FIH_NEGATIVE_VALUE 0xAAAA5555 + +typedef volatile int32_t fih_int; + +#define FIH_INT_INIT(x) (x) +#endif /* FIH_ENABLE_DOUBLE_VARS */ + +extern fih_int FIH_SUCCESS; +extern fih_int FIH_FAILURE; + +#ifdef FIH_ENABLE_GLOBAL_FAIL +/* + * Global failure handler - more resistant to unlooping. noinline and used are + * used to prevent optimization. + * + * NOTE + * This failure handler shall be used as FIH specific error handling to capture + * FI attacks. Error handling in SPM and SP should be enhanced respectively. + */ +__attribute__((noinline)) __attribute__((used)) void fih_panic_loop(void); +#define FIH_PANIC fih_panic_loop() +#else /* FIH_ENABLE_GLOBAL_FAIL */ +#define FIH_PANIC \ + do { \ + FIH_LABEL("FAILURE_LOOP"); \ + while (1) {} \ + } while (0) +#endif /* FIH_ENABLE_GLOBAL_FAIL */ + +/* + * NOTE + * For functions to be inlined outside their compilation unit they have to + * have the body in the header file. This is required as function calls are easy + * to skip. + */ +#ifdef FIH_ENABLE_DELAY +/** + * @brief Set up the RNG for use with random delays. Called once at startup. + */ +void fih_delay_init(void); + +/** + * Get a random uint8_t value from an RNG seeded with an entropy source. + * + * NOTE + * Do not directly call this function. + */ +uint8_t fih_delay_random(void); + +/* Delaying logic, with randomness from a CSPRNG */ +__attribute__((always_inline)) inline +void fih_delay(void) +{ + uint32_t i = 0; + volatile uint32_t delay = FIH_NEGATIVE_VALUE; + volatile uint32_t counter = 0; + + delay = fih_delay_random(); + + if (delay == FIH_NEGATIVE_VALUE) { + FIH_PANIC; + } + + delay &= 0xFF; + + for (i = 0; i < delay; i++) { + counter++; + } + + if (counter != delay) { + FIH_PANIC; + } +} +#else /* FIH_ENABLE_DELAY */ +#define fih_delay_init() + +#define fih_delay() +#endif /* FIH_ENABLE_DELAY */ + +#ifdef FIH_ENABLE_DOUBLE_VARS +__attribute__((always_inline)) inline +void fih_int_validate(fih_int x) +{ + if (x.val != (x.msk ^ _FIH_MASK_VALUE)) { + FIH_PANIC; + } +} + +/* Convert a fih_int to an int. Validate for tampering. */ +__attribute__((always_inline)) inline +int32_t fih_int_decode(fih_int x) +{ + fih_int_validate(x); + return x.val; +} + +/* Convert an int to a fih_int, can be used to encode specific error codes. */ +__attribute__((always_inline)) inline +fih_int fih_int_encode(int32_t x) +{ + fih_int ret = {x, x ^ _FIH_MASK_VALUE}; + return ret; +} + +/* Standard equality. If A == B then 1, else 0 */ +__attribute__((always_inline)) inline +int32_t fih_eq(fih_int x, fih_int y) +{ + volatile int32_t rc1 = FIH_FALSE; + volatile int32_t rc2 = FIH_FALSE; + + fih_int_validate(x); + fih_int_validate(y); + + if (x.val == y.val) { + rc1 = FIH_TRUE; + } + + fih_delay(); + + if (x.msk == y.msk) { + rc2 = FIH_TRUE; + } + + fih_delay(); + + if (rc1 != rc2) { + FIH_PANIC; + } + + return rc1; +} + +__attribute__((always_inline)) inline +int32_t fih_not_eq(fih_int x, fih_int y) +{ + volatile int32_t rc1 = FIH_FALSE; + volatile int32_t rc2 = FIH_FALSE; + + fih_int_validate(x); + fih_int_validate(y); + + if (x.val != y.val) { + rc1 = FIH_TRUE; + } + + fih_delay(); + + if (x.msk != y.msk) { + rc2 = FIH_TRUE; + } + + fih_delay(); + + if (rc1 != rc2) { + FIH_PANIC; + } + + return rc1; +} +#else /* FIH_ENABLE_DOUBLE_VARS */ +/* NOOP */ +#define fih_int_validate(x) + +/* NOOP */ +#define fih_int_decode(x) (x) + +/* NOOP */ +#define fih_int_encode(x) (x) + +__attribute__((always_inline)) inline +int32_t fih_eq(fih_int x, fih_int y) +{ + volatile int32_t rc = FIH_FALSE; + + if (x == y) { + rc = FIH_TRUE; + } + + fih_delay(); + + if (x != y) { + rc = FIH_FALSE; + } + + return rc; +} + +__attribute__((always_inline)) inline +int32_t fih_not_eq(fih_int x, fih_int y) +{ + volatile int32_t rc = FIH_FALSE; + + if (x != y) { + rc = FIH_TRUE; + } + + fih_delay(); + + if (x == y) { + rc = FIH_FALSE; + } + + return rc; +} +#endif /* FIH_ENABLE_DOUBLE_VARS */ + +/* + * C has a common return pattern where 0 is a correct value and all others are + * errors. This function converts 0 to FIH_SUCCESS and any other number to a + * value that is not FIH_SUCCESS + */ +__attribute__((always_inline)) inline +fih_int fih_int_encode_zero_equality(int32_t x) +{ + if (x) { + return FIH_FAILURE; + } else { + return FIH_SUCCESS; + } +} + +#ifdef FIH_ENABLE_CFI +/* Global Control Flow Integrity counter */ +extern fih_int _fih_cfi_ctr; + +/* + * Increment the CFI counter by input counter and return the value before the + * increment. + * + * NOTE + * This function shall not be called directly. + */ +fih_int fih_cfi_get_and_increment(uint8_t cnt); + +/* + * Validate that the saved precall value is the same as the value of the global + * counter. For this to be the case, a fih_ret must have been called between + * these functions being executed. If the values aren't the same then panic. + * + * NOTE + * This function shall not be called directly. + */ +void fih_cfi_validate(fih_int saved); + +/* + * Decrement the global CFI counter by one, so that it has the same value as + * before the cfi_precall. + * + * NOTE + * This function shall not be called directly. + */ +void fih_cfi_decrement(void); + +/* + * Macro wrappers for functions - Even when the functions have zero body this + * saves a few bytes on noop functions as it doesn't generate the call/ret + * + * CFI precall function saves the CFI counter and then increments it - the + * postcall then checks if the counter is equal to the saved value. In order for + * this to be the case a FIH_RET must have been performed inside the called + * function in order to decrement the counter, so the function must have been + * called. + */ +#define FIH_CFI_PRECALL_BLOCK \ + fih_int _fih_cfi_precall_saved_value = fih_cfi_get_and_increment(1) + +#define FIH_CFI_POSTCALL_BLOCK \ + fih_cfi_validate(_fih_cfi_precall_saved_value) + +#define FIH_CFI_PRERET \ + fih_cfi_decrement() + +/* + * Marcos to support protect the control flow integrity inside a function. + * + * The FIH_CFI_PRECALL_BLOCK/FIH_CFI_POSTCALL_BLOCK pair mainly protect function + * calls from fault injection. Fault injection may attack a function to skip its + * critical steps which are not function calls. It is difficult for the caller + * to dectect the injection as long as the function successfully returns. + * + * The following macros can be called in a function to track the critical steps, + * especially those which are not function calls. + */ +/* + * FIH_CFI_STEP_INIT() saves the CFI counter and increase the CFI counter by the + * number of the critical steps. It should be called before execution starts. + */ +#define FIH_CFI_STEP_INIT(x) \ + fih_int _fih_cfi_step_saved_value = fih_cfi_get_and_increment(x) + +/* + * FIH_CFI_STEP_DECREMENT() decrease the CFI counter by one. It can be called + * after each critical step execution completes. + */ +#define FIH_CFI_STEP_DECREMENT() \ + fih_cfi_decrement() + +/* + * FIH_CFI_STEP_ERR_RESET() resets the CFI counter to the previous value saved + * by FIH_CFI_STEP_INIT(). It shall be called only when a functionality error + * occurs and forces the function to exit. It can enable the caller to capture + * the functionality error other than being trapped in fault injection error + * handling. + */ +#define FIH_CFI_STEP_ERR_RESET() \ + do { \ + _fih_cfi_ctr = _fih_cfi_step_saved_value; \ + fih_int_validate(_fih_cfi_ctr); \ + } while(0) + +#else /* FIH_ENABLE_CFI */ +#define FIH_CFI_PRECALL_BLOCK +#define FIH_CFI_POSTCALL_BLOCK +#define FIH_CFI_PRERET + +#define FIH_CFI_STEP_INIT(x) +#define FIH_CFI_STEP_DECREMENT() +#define FIH_CFI_STEP_ERR_RESET() +#endif /* FIH_ENABLE_CFI */ + +/* + * Label for interacting with FIH testing tool. Can be parsed from the elf file + * after compilation. Does not require debug symbols. + */ +#define FIH_LABEL(str) __asm volatile ("FIH_LABEL_" str "_0_%=:" ::) +#define FIH_LABEL_CRITICAL_POINT() FIH_LABEL("FIH_CRITICAL_POINT") + +/* + * Main FIH calling macro. return variable is second argument. Does some setup + * before and validation afterwards. Inserts labels for use with testing script. + * + * First perform the precall step - this gets the current value of the CFI + * counter and saves it to a local variable, and then increments the counter. + * + * Then set the return variable to FIH_FAILURE as a base case. + * + * Then perform the function call. As part of the function FIH_RET must be + * called which will decrement the counter. + * + * The postcall step gets the value of the counter and compares it to the + * previously saved value. If this is equal then the function call and all child + * function calls were performed. + */ +#define FIH_CALL(f, ret, ...) \ + do { \ + FIH_LABEL("FIH_CALL_START_" # f); \ + FIH_CFI_PRECALL_BLOCK; \ + ret = FIH_FAILURE; \ + fih_delay(); \ + ret = f(__VA_ARGS__); \ + FIH_CFI_POSTCALL_BLOCK; \ + fih_int_validate(ret); \ + FIH_LABEL("FIH_CALL_END"); \ + } while (0) + +/* + * FIH return changes the state of the internal state machine. If you do a + * FIH_CALL then you need to do a FIH_RET else the state machine will detect + * tampering and panic. + */ +#define FIH_RET(ret) \ + do { \ + FIH_CFI_PRERET; \ + return ret; \ + } while (0) + +/* + * FIH return type macro changes the function return types to fih_int. + * All functions that need to be protected by FIH and called via FIH_CALL must + * return a fih_int type. + */ +#define FIH_RET_TYPE(type) fih_int + +#else /* TFM_FIH_PROFILE_ON */ +typedef int32_t fih_int; + +#define FIH_INT_INIT(x) (x) + +#define FIH_SUCCESS 0 +#define FIH_FAILURE -1 + +#define fih_int_validate(x) + +#define fih_int_decode(x) (x) + +#define fih_int_encode(x) (x) + +#define fih_int_encode_zero_equality(x) ((x) == 0 ? 0 : 1) + +#define fih_eq(x, y) ((x) == (y)) + +#define fih_not_eq(x, y) ((x) != (y)) + +#define fih_delay_init() (0) +#define fih_delay() + +#define FIH_CALL(f, ret, ...) \ + do { \ + ret = f(__VA_ARGS__); \ + } while (0) + +#define FIH_RET(ret) \ + do { \ + return ret; \ + } while (0) + +#define FIH_RET_TYPE(type) type + +#define FIH_PANIC do { \ + while(1) {}; \ + } while (0) + +#define FIH_CFI_STEP_INIT(x) +#define FIH_CFI_STEP_DECREMENT() +#define FIH_CFI_STEP_ERR_RESET() + +#define FIH_LABEL_CRITICAL_POINT() + +#endif /* TFM_FIH_PROFILE_ON */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TFM_FIH_H__ */ |
