Skip to content
File

Blob: firmware/platform/crypto_profile.c

c197 lines
1/* Opt-in diagnostics. This file and its linker wraps are absent from normal builds. */
2#include <inttypes.h>
3#include <stdbool.h>
4#include <stdio.h>
5#include <string.h>
6#include "radio_bridge.h"
7#include "esp_timer.h"
8#include "freertos/FreeRTOS.h"
9#include "freertos/task.h"
10 
11typedef enum {
12 HMAC_SHA1,
13 HMAC_SHA256,
14 HMAC_SHA384,
15 AES_CTR,
16 GCM_ENCRYPT,
17 GCM_DECRYPT,
18 OPERATION_COUNT,
19 NO_OPERATION = OPERATION_COUNT,
20} operation;
21 
22typedef struct {
23 uint64_t calls;
24 uint64_t failures;
25 uint64_t elapsed_us;
26 uint64_t allocation_calls;
27 uint64_t allocation_failures;
28 uint64_t requested_bytes;
29} counters;
30 
31static portMUX_TYPE profile_lock = portMUX_INITIALIZER_UNLOCKED;
32static struct {
33 TaskHandle_t owner;
34 operation active;
35 bool enabled;
36 int64_t started_us;
37 counters totals[OPERATION_COUNT];
38} profile;
39 
40/* All instrumentation state is fixed-size and contains no keys or payloads.
41 * Only the owner task enters measured calls; the lock also excludes concurrent
42 * allocation wrappers from unrelated SDK tasks. Never allocate or log here. */
43static bool begin(operation op)
44{
45 if (xPortInIsrContext())
46 return false;
47 TaskHandle_t current = xTaskGetCurrentTaskHandle();
48 portENTER_CRITICAL(&profile_lock);
49 bool measured = profile.enabled && profile.owner == current && profile.active == NO_OPERATION &&
50 op < OPERATION_COUNT;
51 if (measured)
52 profile.active = op;
53 portEXIT_CRITICAL(&profile_lock);
54 return measured;
55}
56 
57static void finish(operation op, int32_t result, int64_t started_us)
58{
59 uint64_t elapsed_us = esp_timer_get_time() - started_us;
60 portENTER_CRITICAL(&profile_lock);
61 counters *total = &profile.totals[op];
62 total->calls++;
63 total->failures += result != 0;
64 total->elapsed_us += elapsed_us;
65 profile.active = NO_OPERATION;
66 portEXIT_CRITICAL(&profile_lock);
67}
68 
69int32_t radio_crypto_profile_start(void)
70{
71 if (xPortInIsrContext())
72 return -1;
73 int64_t now = esp_timer_get_time();
74 TaskHandle_t current = xTaskGetCurrentTaskHandle();
75 portENTER_CRITICAL(&profile_lock);
76 if (profile.enabled) {
77 portEXIT_CRITICAL(&profile_lock);
78 return -1;
79 }
80 memset(profile.totals, 0, sizeof(profile.totals));
81 profile.owner = current;
82 profile.active = NO_OPERATION;
83 profile.started_us = now;
84 profile.enabled = true;
85 portEXIT_CRITICAL(&profile_lock);
86 return 0;
87}
88 
89int32_t radio_crypto_profile_finish(void)
90{
91 if (xPortInIsrContext())
92 return -1;
93 counters totals[OPERATION_COUNT];
94 TaskHandle_t current = xTaskGetCurrentTaskHandle();
95 int64_t now = esp_timer_get_time();
96 portENTER_CRITICAL(&profile_lock);
97 if (!profile.enabled || profile.owner != current || profile.active != NO_OPERATION) {
98 portEXIT_CRITICAL(&profile_lock);
99 return -1;
100 }
101 uint64_t elapsed_us = now - profile.started_us;
102 memcpy(totals, profile.totals, sizeof(totals));
103 profile.enabled = false;
104 profile.owner = NULL;
105 portEXIT_CRITICAL(&profile_lock);
106 
107 /* Format after releasing the lock, with profiling disabled. */
108 char line[256];
109 snprintf(line, sizeof(line), "Crypto profile: elapsed_us=%" PRIu64, elapsed_us);
110 radio_log(line);
111 static const char *const names[OPERATION_COUNT] = {"hmac-sha1", "hmac-sha256", "hmac-sha384",
112 "aes-ctr", "gcm-encrypt", "gcm-decrypt"};
113 for (size_t i = 0; i < OPERATION_COUNT; i++) {
114 const counters *total = &totals[i];
115 snprintf(line, sizeof(line),
116 "Crypto profile: op=%s calls=%" PRIu64 " failures=%" PRIu64 " us=%" PRIu64
117 " calloc=%" PRIu64 " calloc_failures=%" PRIu64 " requested_bytes=%" PRIu64,
118 names[i], total->calls, total->failures, total->elapsed_us,
119 total->allocation_calls, total->allocation_failures, total->requested_bytes);
120 radio_log(line);
121 }
122 return 0;
123}
124 
125void *__real_esp_mbedtls_mem_calloc(size_t count, size_t size);
126void *__wrap_esp_mbedtls_mem_calloc(size_t count, size_t size)
127{
128 void *allocation = __real_esp_mbedtls_mem_calloc(count, size);
129 if (xPortInIsrContext())
130 return allocation;
131 TaskHandle_t current = xTaskGetCurrentTaskHandle();
132 portENTER_CRITICAL(&profile_lock);
133 if (profile.enabled && profile.owner == current && profile.active < OPERATION_COUNT) {
134 counters *total = &profile.totals[profile.active];
135 total->allocation_calls++;
136 total->allocation_failures += allocation == NULL;
137 total->requested_bytes += (uint64_t)count * size;
138 }
139 portEXIT_CRITICAL(&profile_lock);
140 return allocation;
141}
142 
143int32_t __real_radio_crypto_hmac(int32_t bits, const uint8_t *key, size_t key_length,
144 const radio_crypto_part *parts, size_t count, uint8_t *output,
145 size_t capacity);
146int32_t __wrap_radio_crypto_hmac(int32_t bits, const uint8_t *key, size_t key_length,
147 const radio_crypto_part *parts, size_t count, uint8_t *output,
148 size_t capacity)
149{
150 operation op = bits == 160 ? HMAC_SHA1
151 : bits == 256 ? HMAC_SHA256
152 : bits == 384 ? HMAC_SHA384
153 : NO_OPERATION;
154 bool measured = begin(op);
155 int64_t started_us = measured ? esp_timer_get_time() : 0;
156 int32_t result =
157 __real_radio_crypto_hmac(bits, key, key_length, parts, count, output, capacity);
158 if (measured)
159 finish(op, result, started_us);
160 return result;
161}
162 
163int32_t __real_radio_crypto_aes_ctr(const uint8_t *key, size_t key_length, const uint8_t iv[16],
164 const uint8_t *input, size_t length, uint8_t *output,
165 size_t capacity);
166int32_t __wrap_radio_crypto_aes_ctr(const uint8_t *key, size_t key_length, const uint8_t iv[16],
167 const uint8_t *input, size_t length, uint8_t *output,
168 size_t capacity)
169{
170 bool measured = begin(AES_CTR);
171 int64_t started_us = measured ? esp_timer_get_time() : 0;
172 int32_t result =
173 __real_radio_crypto_aes_ctr(key, key_length, iv, input, length, output, capacity);
174 if (measured)
175 finish(AES_CTR, result, started_us);
176 return result;
177}
178 
179int32_t __real_radio_crypto_aes_gcm(int32_t decrypt, const uint8_t *key, size_t key_length,
180 const uint8_t iv[12], const uint8_t *aad, size_t aad_length,
181 const uint8_t *input, size_t length, uint8_t *output,
182 size_t capacity);
183int32_t __wrap_radio_crypto_aes_gcm(int32_t decrypt, const uint8_t *key, size_t key_length,
184 const uint8_t iv[12], const uint8_t *aad, size_t aad_length,
185 const uint8_t *input, size_t length, uint8_t *output,
186 size_t capacity)
187{
188 operation op = decrypt ? GCM_DECRYPT : GCM_ENCRYPT;
189 bool measured = begin(op);
190 int64_t started_us = measured ? esp_timer_get_time() : 0;
191 int32_t result = __real_radio_crypto_aes_gcm(decrypt, key, key_length, iv, aad, aad_length,
192 input, length, output, capacity);
193 if (measured)
194 finish(op, result, started_us);
195 return result;
196}