summaryrefslogtreecommitdiff
path: root/nrfdemo/builddk/tfm/api_ns/platform/include/fih.h
blob: fcb604b2bfd4addb613c68fa71af12d999abd8e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
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__ */