1
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
57
58
59
60
61
62
63
64
65
66
67
68
76
77
81
82
86
87
91
92
96
97
101
102
106
107
112
113
120
124
125
126
127
128
129
130
131
132
133
137
138
139
144
145
146
147
152
153
154
155
156
160
161
162
163
167
168
169
170
171
172
173
174
175
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
203
204
205
206
207
208
209
210
216
217
218
219
220
222
223
224
227
228
229
233
234
235
236
237
238
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
282
283
284
285
286
291
292
296
297
298
299
303
304
305
306
307
308
309
310
311
314
315
316
317
318
319
320
323
332
333
334
335
336
337
340
341
345
346
355
356
357
358
359
366
367
371
372
/* ... */
#include <string.h>
#include <stdbool.h>
#include <sys/param.h>
#include "esp_check.h"
#include "esp_crt_bundle.h"
#include "esp_log.h"
#include "mbedtls/pk.h"
#include "mbedtls/oid.h"
#include "mbedtls/asn1.h"9 includes
/* ... */
#define CRT_NAME_LEN_OFFSET 0
#define CRT_KEY_LEN_OFFSET (CRT_NAME_LEN_OFFSET + sizeof(uint16_t))
#define CRT_NAME_OFFSET (CRT_KEY_LEN_OFFSET + sizeof(uint16_t))
#define CRT_HEADER_SIZE CRT_NAME_OFFSET
static const char *TAG = "esp-x509-crt-bundle";
/* ... */
static const mbedtls_x509_crt s_dummy_crt;
extern const uint8_t x509_crt_imported_bundle_bin_start[] asm("_binary_x509_crt_bundle_start");
extern const uint8_t x509_crt_imported_bundle_bin_end[] asm("_binary_x509_crt_bundle_end");
typedef const uint8_t* bundle_t;
typedef const uint8_t* cert_t;
static bundle_t s_crt_bundle;
static uint16_t get16_le(const uint8_t* ptr)
{
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
return *((const uint16_t*)ptr);
#else
return (((uint16_t)ptr[1]) << 8) | ptr[0];
#endif
}{ ... }
static uint16_t esp_crt_get_name_len(const cert_t cert)
{
return get16_le(cert + CRT_NAME_LEN_OFFSET);
}{ ... }
static const uint8_t* esp_crt_get_name(const cert_t cert)
{
return cert + CRT_NAME_OFFSET;
}{ ... }
static uint16_t esp_crt_get_key_len(const cert_t cert)
{
return get16_le(cert + CRT_KEY_LEN_OFFSET);
}{ ... }
static const uint8_t* esp_crt_get_key(const cert_t cert)
{
return esp_crt_get_name(cert) + esp_crt_get_name_len(cert);
}{ ... }
static uint16_t esp_crt_get_len(const cert_t cert)
{
return CRT_HEADER_SIZE + esp_crt_get_name_len(cert) + esp_crt_get_key_len(cert);
}{ ... }
static uint32_t esp_crt_get_cert_offset(const bundle_t bundle, const uint32_t index)
{
return ((const uint32_t*)bundle)[index];
}{ ... }
static uint32_t esp_crt_get_certcount(const bundle_t bundle)
{
return esp_crt_get_cert_offset(bundle, 0) / sizeof(uint32_t);
}{ ... }
/* ... */
static cert_t esp_crt_get_cert(const bundle_t bundle, const uint32_t index)
{
return bundle + esp_crt_get_cert_offset(bundle, index);
}{ ... }
static int esp_crt_check_signature(const mbedtls_x509_crt* child, const uint8_t* pub_key_buf, const size_t pub_key_len)
{
int ret = 0;
mbedtls_pk_context pubkey;
const mbedtls_md_info_t *md_info;
mbedtls_pk_init(&pubkey);
if (unlikely((ret = mbedtls_pk_parse_public_key(&pubkey, pub_key_buf, pub_key_len)) != 0)) {
ESP_LOGE(TAG, "PK parse failed with error 0x%x", -ret);
goto cleanup;
}{...}
if (unlikely(!mbedtls_pk_can_do(&pubkey, child->MBEDTLS_PRIVATE(sig_pk)))) {
ESP_LOGE(TAG, "Unsuitable public key");
ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
goto cleanup;
}{...}
md_info = mbedtls_md_info_from_type(child->MBEDTLS_PRIVATE(sig_md));
if (unlikely(md_info == NULL)) {
ESP_LOGE(TAG, "Unknown message digest");
ret = MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE;
goto cleanup;
}{...}
unsigned char hash[MBEDTLS_MD_MAX_SIZE];
const unsigned char md_size = mbedtls_md_get_size(md_info);
if ((ret = mbedtls_md(md_info, child->tbs.p, child->tbs.len, hash)) != 0) {
ESP_LOGE(TAG, "MD failed with error 0x%x", -ret);
goto cleanup;
}{...}
if (unlikely((ret = mbedtls_pk_verify_ext(child->MBEDTLS_PRIVATE(sig_pk), child->MBEDTLS_PRIVATE(sig_opts), &pubkey,
child->MBEDTLS_PRIVATE(sig_md), hash, md_size,
child->MBEDTLS_PRIVATE(sig).p, child->MBEDTLS_PRIVATE(sig).len)) != 0)) {
ESP_LOGE(TAG, "PK verify failed with error 0x%x", -ret);
goto cleanup;
}{...}
cleanup:
mbedtls_pk_free(&pubkey);
return ret;
}{ ... }
static cert_t esp_crt_find_cert(const unsigned char* const issuer, const size_t issuer_len)
{
if (unlikely(issuer == NULL || issuer_len == 0)) {
return NULL;
}{...}
int start = 0;
int end = esp_crt_get_certcount(s_crt_bundle) - 1;
int middle = (start + end) / 2;
cert_t cert = NULL;
size_t cert_name_len = 0;
while (start <= end) {
cert = esp_crt_get_cert(s_crt_bundle, middle);
cert_name_len = esp_crt_get_name_len(cert);
int cmp_res = memcmp(issuer, esp_crt_get_name(cert), MIN(issuer_len, cert_name_len));
if (unlikely(cmp_res == 0)) {
return cert;
}{...} else if (cmp_res < 0) {
end = middle - 1;
}{...} else {
start = middle + 1;
}{...}
middle = (start + end) / 2;
}{...}
return NULL;
}{ ... }
/* ... */
int esp_crt_verify_callback(void *buf, mbedtls_x509_crt* const crt, const int depth, uint32_t* const flags)
{
const mbedtls_x509_crt* const child = crt;
/* ... */
uint32_t flags_filtered = *flags & ~(MBEDTLS_X509_BADCERT_BAD_MD);
if (flags_filtered != MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
return 0;
}{...}
if (unlikely(s_crt_bundle == NULL)) {
ESP_LOGE(TAG, "No certificates in bundle");
return MBEDTLS_ERR_X509_FATAL_ERROR;
}{...}
ESP_LOGD(TAG, "%" PRIu16 " certificates in bundle", (uint16_t)esp_crt_get_certcount(s_crt_bundle));
cert_t cert = esp_crt_find_cert(child->issuer_raw.p, child->issuer_raw.len);
if (likely(cert != NULL)) {
const int ret = esp_crt_check_signature(child, esp_crt_get_key(cert), esp_crt_get_key_len(cert));
if (likely(ret == 0)) {
ESP_LOGI(TAG, "Certificate validated");
*flags = 0;
return 0;
}{...} else {
ESP_LOGE(TAG, "Certificate matched but signature verification failed");
#if (CONFIG_LOG_DEFAULT_LEVEL_DEBUG || CONFIG_LOG_DEFAULT_LEVEL_VERBOSE)
char *cert_name = malloc((esp_crt_get_name_len(cert) + 1) * sizeof(char));
if (cert_name) {
memcpy(cert_name, esp_crt_get_name(cert), esp_crt_get_name_len(cert));
cert_name[esp_crt_get_name_len(cert)] = '\0';
ESP_LOGE(TAG, "Certificate matched with %s but signature verification failed", cert_name);
free(cert_name);
}{...}
#endif/* ... */
}{...}
}{...} else {
ESP_LOGI(TAG, "No matching trusted root certificate found");
}{...}
ESP_LOGE(TAG, "Failed to verify certificate");
return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED;
}{ ... }
/* ... */
static bool esp_crt_check_bundle(const uint8_t* const x509_bundle, const size_t bundle_size)
{
if (unlikely(x509_bundle == NULL || bundle_size <= (sizeof(uint32_t) + CRT_HEADER_SIZE))) {
return false;
}{...}
const uint32_t* offsets = (const uint32_t*)x509_bundle;
if (unlikely(offsets[0] == 0 || (offsets[0] % sizeof(uint32_t)) != 0)) {
return false;
}{...}
if (unlikely(offsets[0] >= bundle_size)) {
return false;
}{...}
const uint32_t num_certs = esp_crt_get_certcount(x509_bundle);
if (unlikely(num_certs > CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS)) {
ESP_LOGE(TAG, "Cert bundle certificates exceed max allowed certificates");
return false;
}{...}
for (uint32_t i = 0; i < num_certs - 1; ++i) {
const uint32_t off = offsets[i];
cert_t cert = x509_bundle + off;
const uint32_t expected_next_offset = off + esp_crt_get_len(cert);
if (unlikely(offsets[i + 1] != expected_next_offset || expected_next_offset >= bundle_size)) {
return false;
}{...}
}{...}
return true;
}{ ... }
/* ... */
static esp_err_t esp_crt_bundle_init(const uint8_t* const x509_bundle, const size_t bundle_size)
{
if (likely(esp_crt_check_bundle(x509_bundle, bundle_size))) {
s_crt_bundle = x509_bundle;
return ESP_OK;
}{...} else {
return ESP_ERR_INVALID_ARG;
}{...}
}{ ... }
esp_err_t esp_crt_bundle_attach(void *conf)
{
esp_err_t ret = ESP_OK;
if (s_crt_bundle == NULL) {
ret = esp_crt_bundle_init(x509_crt_imported_bundle_bin_start, x509_crt_imported_bundle_bin_end - x509_crt_imported_bundle_bin_start);
}{...}
if (unlikely(ret != ESP_OK)) {
ESP_LOGE(TAG, "Failed to attach bundle");
return ret;
}{...}
if (conf) {
/* ... */
mbedtls_ssl_config *ssl_conf = (mbedtls_ssl_config *)conf;
mbedtls_ssl_conf_ca_chain(ssl_conf, (mbedtls_x509_crt*)&s_dummy_crt, NULL);
mbedtls_ssl_conf_verify(ssl_conf, esp_crt_verify_callback, NULL);
}{...}
return ret;
}{ ... }
void esp_crt_bundle_detach(mbedtls_ssl_config *conf)
{
s_crt_bundle = NULL;
if (conf) {
mbedtls_ssl_conf_verify(conf, NULL, NULL);
}{...}
}{ ... }
esp_err_t esp_crt_bundle_set(const uint8_t *x509_bundle, size_t bundle_size)
{
return esp_crt_bundle_init(x509_bundle, bundle_size);
}{ ... }
bool esp_crt_bundle_in_use(const mbedtls_x509_crt* ca_chain)
{
return ((ca_chain == &s_dummy_crt) ? true : false);
}{ ... }