ProtoCore
v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
crypto_opt.h
Go to the documentation of this file.
1
// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2
// SPDX-License-Identifier: AGPL-3.0-or-later
3
4
/**
5
* @file crypto_opt.h
6
* @brief Per-translation-unit optimization override for hot, pure-integer crypto.
7
*
8
* The library ships at the arduino framework's `-Os` (the framework appends it AFTER any PlatformIO env
9
* `build_flags`, so a plain `-O2` there is overridden). `-Os` roughly halves the throughput of the software
10
* ciphers/MACs. Put @ref PC_CRYPTO_HOT at the top of such a `.cpp` (after its includes) to force a higher
11
* level for that one translation unit, regardless of the consumer's size-optimized build:
12
*
13
* @code
14
* #include "crypto/cipher/chacha20.h"
15
* #include "crypto/crypto_opt.h"
16
* PC_CRYPTO_HOT // this TU builds at -O2 (or the configured level)
17
* @endcode
18
*
19
* The level is `PC_CRYPTO_OPT_LEVEL`: `2` or `3`, or `0` to inherit the framework `-Os`. It is NOT a
20
* ServerConfig / user knob - it is internal crypto tuning with a measured per-die default (see below), which
21
* a hot TU can override for its own build with `#define PC_CRYPTO_OPT_LEVEL 3` before this include. Only GCC
22
* honors `#pragma GCC optimize`; clang/other compilers get a no-op and inherit their normal level.
23
*
24
* When a TU's `-O3` win bisects (on-device) to a SINGLE named transform, prefer the deliberate
25
* `PC_CRYPTO_HOT_PEEL` / `PC_CRYPTO_HOT_UNSWITCH` pin (the `-O2` floor + just that transform) over full
26
* `-O3`, scoped inside the TU's own die guard - same speed, none of `-O3`'s extra code-size / miscompile risk:
27
* @code
28
* #include "crypto/crypto_opt.h"
29
* #if defined(CONFIG_IDF_TARGET_ESP32S3) && CONFIG_IDF_TARGET_ESP32S3
30
* PC_CRYPTO_HOT_UNSWITCH // S3: the -O3 win here is entirely -funswitch-loops (bisected)
31
* #else
32
* PC_CRYPTO_HOT // every other die: the per-die default
33
* #endif
34
* @endcode
35
*
36
* ═══════════════════════════════════════════════════════════════════════════
37
* CAVEATS - read before applying this to a new file
38
* ═══════════════════════════════════════════════════════════════════════════
39
*
40
* 1. CONSTANT TIME. Apply this ONLY to code that is constant-time by STRUCTURE - no secret-dependent
41
* branches and no secret-dependent memory indexing: stream ciphers (ChaCha20), MACs (Poly1305), hashes.
42
* Do NOT put it on scalar-multiplication / bignum / point-arithmetic code that relies on branchless
43
* mask-selects to stay constant-time: an aggressive optimizer can (rarely, but it is documented) turn a
44
* mask-select into a data-dependent branch and reintroduce a timing side channel. Those paths are also
45
* HW-accelerator-dominated (RSA/MPI MODMULT, HW AES/SHA), so the `-O` level buys them almost nothing -
46
* all risk, no reward. When in doubt, leave it off.
47
*
48
* 2. SIZE. `-O2`/`-O3` inline and unroll aggressively -> larger flash and IRAM; `-O3` more so. On the
49
* classic ESP32 (tight internal DRAM/IRAM, where TLS example builds already run close to the limit)
50
* prefer level `2` or `0`.
51
*
52
* 3. `-O3` IS NOT UNIVERSALLY FASTER than `-O2` here - it is per-die and per-algo. The bigger unrolled code
53
* can thrash the instruction / flash cache and regress (e.g. the S3's Ed25519 sign is ~1.2% SLOWER at
54
* `-O3`), and `-O3` widens the miscompile / latent-UB surface. So full `-O3` is taken only where it was
55
* MEASURED to help wholesale (the P4, whose win is `-O3`'s parameter budget - see below); on the S3 the
56
* win of a given TU is one transform, taken via a `PC_CRYPTO_HOT_*` pin, and `-O2` is the floor elsewhere.
57
*/
58
59
#ifndef PROTOCORE_CRYPTO_OPT_H
60
#define PROTOCORE_CRYPTO_OPT_H
61
62
#if defined(__GNUC__) && !defined(__clang__)
63
#ifdef ARDUINO
64
#include "sdkconfig.h"
// CONFIG_IDF_TARGET_* - the per-die default below + the single-transform die guards in the TUs
65
#endif
66
#ifndef PC_CRYPTO_OPT_LEVEL
67
// Per-variant default, measured on the crypto bench (main_cryptobench). The ESP32-P4 (RISC-V) is faster or flat
68
// at -O3 across the whole software crypto suite (chacha -22.9%, poly -15.6%, x25519 -6.8%, ed25519 -4.5%; the HW
69
// ops flat) - and its win is -O3's larger inline / unroll PARAMETER budget, not any one -f transform (verified:
70
// -O3 with all 14 O2->O3 delta flags disabled still hits the full -O3 numbers), so the P4 takes -O3 wholesale.
71
// Every other die defaults to -O2 (the big -Os -> -O2 win); the S3's few -O3 wins are each a SINGLE transform,
72
// pinned per-TU with the PC_CRYPTO_HOT_* macros below (see ecdsa/chacha20/hmac_sha256).
73
#if defined(CONFIG_IDF_TARGET_ESP32P4) && CONFIG_IDF_TARGET_ESP32P4
74
#define PC_CRYPTO_OPT_LEVEL 3
75
#else
76
#define PC_CRYPTO_OPT_LEVEL 2
77
#endif
78
#endif
79
#if PC_CRYPTO_OPT_LEVEL == 3
80
#define PC_CRYPTO_HOT _Pragma("GCC optimize(\"O3\")")
81
#elif PC_CRYPTO_OPT_LEVEL == 2
82
#define PC_CRYPTO_HOT _Pragma("GCC optimize(\"O2\")")
83
#else
84
#define PC_CRYPTO_HOT
// 0 / other: inherit the framework -Os
85
#endif
86
// Deliberate single-transform pins: the -O2 floor + exactly ONE -O3 transform that (alone, not full -O3) was
87
// MEASURED to carry a TU's win - capturing it without -O3's code-size / miscompile / cache-thrash baggage. A TU
88
// selects one inside its own die guard (e.g. #if CONFIG_IDF_TARGET_ESP32S3) and uses PC_CRYPTO_HOT otherwise.
89
// This is a closed set (add one only after bisecting the win to a named flag on-device); it is NOT a user knob.
90
#define PC_CRYPTO_HOT_PEEL _Pragma("GCC optimize(\"O2\",\"peel-loops\")")
91
#define PC_CRYPTO_HOT_UNSWITCH _Pragma("GCC optimize(\"O2\",\"unswitch-loops\")")
92
#else
93
#define PC_CRYPTO_HOT
// non-GCC: no per-TU pragma; inherit the toolchain default
94
#define PC_CRYPTO_HOT_PEEL
// "
95
#define PC_CRYPTO_HOT_UNSWITCH
// "
96
#endif
97
98
#endif
// PROTOCORE_CRYPTO_OPT_H
src
crypto
crypto_opt.h
Generated by
1.9.8