Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
tlv_library.c
Go to the documentation of this file.
1/*****************************************************************************
2 * (c) 2025 Ledger SAS.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
16
17#include <stddef.h>
18#include <stdint.h>
19#include <stdbool.h>
20#include <string.h>
21
22#include "os_utils.h"
23#include "os_pic.h"
24#include "cx.h"
25
26#include "tlv_library.h"
27
28#define DER_LONG_FORM_FLAG 0x80 // 8th bit set
29#define DER_FIRST_BYTE_VALUE_MASK 0x7f
30
38bool tlv_enforce_u8_value(const tlv_data_t *data, uint8_t expected_value)
39{
40 uint8_t value = 0;
41 if (!get_uint8_t_from_tlv_data(data, &value)) {
42 PRINTF("Failed to extract uint8 value from TLV data (tag 0x%x)\n", data->tag);
43 return false;
44 }
45 if (value != expected_value) {
46 PRINTF(
47 "Value mismatch for tag 0x%x: expected %u, got %u\n", data->tag, expected_value, value);
48 return false;
49 }
50 return true;
51}
52
64bool get_uint64_t_from_tlv_data(const tlv_data_t *data, uint64_t *value)
65{
66 if (data == NULL || value == NULL) {
67 PRINTF("Received NULL parameter\n");
68 return false;
69 }
70 if (data->value.size == 0 || data->value.size > sizeof(uint64_t)) {
71 PRINTF("Invalid length (%u bytes) for tag 0x%x (expected 1-%zu bytes)\n",
72 data->value.size,
73 data->tag,
74 sizeof(uint64_t));
75 return false;
76 }
77
78 // Pad with zeros before calling U8BE()
79 uint8_t buffer[sizeof(uint64_t)] = {0};
80 memcpy(buffer + (sizeof(buffer) - data->value.size), data->value.ptr, data->value.size);
81
82 *value = U8BE(buffer, 0);
83 return true;
84}
85
96bool get_uint32_t_from_tlv_data(const tlv_data_t *data, uint32_t *value)
97{
98 if (data == NULL || value == NULL) {
99 PRINTF("Received NULL parameter\n");
100 return false;
101 }
102 uint64_t tmp_value;
103 if (!get_uint64_t_from_tlv_data(data, &tmp_value) || tmp_value > UINT32_MAX) {
104 return false;
105 }
106 *value = (uint32_t) tmp_value;
107 return true;
108}
109
120bool get_uint16_t_from_tlv_data(const tlv_data_t *data, uint16_t *value)
121{
122 if (data == NULL || value == NULL) {
123 PRINTF("Received NULL parameter\n");
124 return false;
125 }
126 uint64_t tmp_value;
127 if (!get_uint64_t_from_tlv_data(data, &tmp_value) || (tmp_value > UINT16_MAX)) {
128 return false;
129 }
130 *value = (uint16_t) tmp_value;
131 return true;
132}
133
144bool get_uint8_t_from_tlv_data(const tlv_data_t *data, uint8_t *value)
145{
146 if (data == NULL || value == NULL) {
147 PRINTF("Received NULL parameter\n");
148 return false;
149 }
150 uint64_t tmp_value;
151 if (!get_uint64_t_from_tlv_data(data, &tmp_value) || (tmp_value > UINT8_MAX)) {
152 return false;
153 }
154 *value = (uint8_t) tmp_value;
155 return true;
156}
157
169bool get_bool_from_tlv_data(const tlv_data_t *data, bool *value)
170{
171 if (data == NULL || value == NULL) {
172 PRINTF("Received NULL parameter\n");
173 return false;
174 }
175 uint8_t tmp_value;
176 if (!get_uint8_t_from_tlv_data(data, &tmp_value) || (tmp_value > 1)) {
177 return false;
178 }
179 *value = (tmp_value == 1);
180 return true;
181}
182
196 buffer_t *out,
197 uint16_t min_size,
198 uint16_t max_size)
199{
200 if (data == NULL || out == NULL) {
201 PRINTF("Received NULL parameter\n");
202 return false;
203 }
204
205 if (min_size != 0 && data->value.size < min_size) {
206 PRINTF("Expected at least %d bytes, found %d\n", min_size, data->value.size);
207 return false;
208 }
209 if (max_size != 0 && data->value.size > max_size) {
210 PRINTF("Expected at most %d bytes, found %d\n", max_size, data->value.size);
211 return false;
212 }
213 out->size = data->value.size;
214 out->ptr = data->value.ptr;
215 return true;
216}
217
234 char *out,
235 uint16_t min_length,
236 uint16_t out_size)
237{
238 if (data == NULL || out == NULL) {
239 PRINTF("Received NULL parameter\n");
240 return false;
241 }
242
243 // Reject TLV strings with embedded null bytes
244 size_t actual_length = 0;
245 if (data->value.size > 0) {
246 actual_length = strnlen((const char *) data->value.ptr, data->value.size);
247 }
248 if (actual_length != data->value.size) {
249 PRINTF("Embedded null byte at offset %u\n", (unsigned) actual_length);
250 return false;
251 }
252
253 if (min_length != 0 && data->value.size < min_length) {
254 PRINTF("Expected at least %u bytes, found %u\n", min_length, data->value.size);
255 return false;
256 }
257 // The input is not '\0' terminated
258 if (out_size != 0 && data->value.size + 1 > out_size) {
259 PRINTF("Expected at most %u bytes, found %u (+1)\n", out_size, data->value.size);
260 return false;
261 }
262
263 if (data->value.size > 0) {
264 memcpy(out, data->value.ptr, data->value.size);
265 }
266 out[data->value.size] = '\0';
267
268 return true;
269}
270
281static bool get_der_value_as_uint32(const buffer_t *payload, size_t *offset, uint32_t *value)
282{
283 uint8_t byte_length;
284 uint8_t buf[sizeof(*value)];
285
286 if (payload == NULL || offset == NULL || value == NULL) {
287 PRINTF("Received NULL parameter\n");
288 return false;
289 }
290
291 if (payload->ptr[*offset] & DER_LONG_FORM_FLAG) { // long form
292 byte_length = payload->ptr[*offset] & DER_FIRST_BYTE_VALUE_MASK;
293 *offset += 1;
294 if ((*offset + byte_length) > payload->size) {
295 PRINTF("TLV payload too small for DER encoded value\n");
296 return false;
297 }
298 if (byte_length > sizeof(buf) || byte_length == 0) {
299 PRINTF("Unexpectedly long DER-encoded value (%u bytes)\n", byte_length);
300 return false;
301 }
302 memset(buf, 0, (sizeof(buf) - byte_length));
303 memcpy(buf + (sizeof(buf) - byte_length), &payload->ptr[*offset], byte_length);
304 *value = U4BE(buf, 0);
305 *offset += byte_length;
306 return true;
307 }
308 else { // short form
309 *value = payload->ptr[*offset];
310 *offset += 1;
311 return true;
312 }
313}
314
325static bool get_der_value_as_uint16(const buffer_t *payload, size_t *offset, uint16_t *value)
326{
327 uint32_t tmp_value;
328 if (!get_der_value_as_uint32(payload, offset, &tmp_value) || (tmp_value > UINT16_MAX)) {
329 return false;
330 }
331
332 *value = (uint16_t) tmp_value;
333 return true;
334}
335
349bool tlv_check_received_tags(TLV_reception_t received, const TLV_tag_t *tags, size_t tag_count)
350{
351 for (size_t i = 0; i < tag_count; i++) {
352 TLV_flag_t flag = received.tag_to_flag_function(tags[i]);
353 if (flag == 0) {
354 PRINTF("No flag found for tag 0x%x\n", tags[i]);
355 return false;
356 }
357 if ((received.flags & flag) != flag) {
358 PRINTF("Tag 0x%x no received\n", tags[i]);
359 return false;
360 }
361 }
362 return true;
363}
364
374 uint8_t handlers_count,
375 TLV_tag_t tag)
376{
377 // check if a handler exists for this tag
378 for (uint8_t idx = 0; idx < handlers_count; ++idx) {
379 if (handlers[idx].tag == tag) {
380 return &handlers[idx];
381 }
382 }
383 return NULL;
384}
385
391
409 uint8_t handlers_count,
410 tlv_handler_cb_t *common_handler,
411 tag_to_flag_function_t *tag_to_flag_function,
412 const buffer_t *payload,
413 void *tlv_out,
414 TLV_reception_t *received_tags_flags)
415{
416 tlv_step_t step = TLV_TAG;
417 tlv_data_t data;
418 size_t offset = 0;
419 size_t tag_start_offset;
420 const _internal_tlv_handler_t *handler;
421 tlv_handler_cb_t *fptr;
422 uint16_t size;
423 TLV_flag_t flag;
424
425 explicit_bzero(received_tags_flags, sizeof(*received_tags_flags));
426 received_tags_flags->tag_to_flag_function = PIC(tag_to_flag_function);
427
428 PRINTF("Parsing TLV payload %.*H\n", payload->size, payload->ptr);
429
430 // handle TLV payload
431 while (offset < payload->size || (step == TLV_VALUE && data.value.size == 0)) {
432 switch (step) {
433 case TLV_TAG:
434 tag_start_offset = offset;
435 if (!get_der_value_as_uint32(payload, &offset, &data.tag)) {
436 return false;
437 }
438 handler = find_handler(handlers, handlers_count, data.tag);
439 if (handler == NULL) {
440 PRINTF("No handler found for tag 0x%x\n", data.tag);
441 return false;
442 }
443 step = TLV_LENGTH;
444 break;
445
446 case TLV_LENGTH:
447 if (!get_der_value_as_uint16(payload, &offset, &size)) {
448 return false;
449 }
450 data.value.size = size;
451 step = TLV_VALUE;
452 break;
453
454 case TLV_VALUE:
455 if ((offset + data.value.size) > payload->size) {
456 PRINTF("Error: can't read %d + %d, only %d\n",
457 offset,
458 data.value.size,
459 payload->size);
460 return false;
461 }
462 if (data.value.size > 0) {
463 data.value.ptr = &payload->ptr[offset];
464 PRINTF("Handling tag 0x%02x length %d value '%.*H'\n",
465 data.tag,
466 data.value.size,
467 data.value.size,
468 data.value.ptr);
469 }
470 else {
471 data.value.ptr = NULL;
472 PRINTF("Handling tag 0x%02x length %d\n", data.tag, data.value.size);
473 }
474 offset += data.value.size;
475
476 // Calculate raw TLV start/end to give to the handler
477 data.raw.ptr = &payload->ptr[tag_start_offset];
478 data.raw.size = offset - tag_start_offset;
479
480 // Check for duplicate tag
481 flag = received_tags_flags->tag_to_flag_function(data.tag);
482 if (handler->is_unique && ((received_tags_flags->flags & flag) == flag)) {
483 PRINTF("Tag = %d was already received and is flagged unique\n", data.tag);
484 return false;
485 }
486
487 // Call the common handler if there is one
488 fptr = PIC(common_handler);
489 if (fptr != NULL && !(*fptr)(&data, tlv_out)) {
490 PRINTF("Common handler error while handling tag 0x%x\n", handler->tag);
491 return false;
492 }
493
494 // Call this tag's handler if there is one
495 fptr = PIC(handler->func);
496 if (fptr != NULL && !(*fptr)(&data, tlv_out)) {
497 PRINTF("Handler error while handling tag 0x%x\n", handler->tag);
498 return false;
499 }
500
501 // Flag reception after handler callback in case the handler wants to check it
502 received_tags_flags->flags |= flag;
503 step = TLV_TAG;
504 break;
505
506 default:
507 return false;
508 }
509 }
510 if (step != TLV_TAG) {
511 PRINTF("Error: unexpected end step %d\n", step);
512 return false;
513 }
514 if (offset != payload->size) {
515 PRINTF("Error: unexpected data at the end of the TLV payload!\n");
516 return false;
517 }
518
519 return true;
520}
tag_to_flag_function_t * tag_to_flag_function
tlv_handler_cb_t * func
uint8_t * ptr
Definition buffer.h:22
size_t size
Pointer to byte buffer.
Definition buffer.h:23
buffer_t value
Definition tlv_library.h:91
buffer_t raw
Definition tlv_library.h:97
TLV_tag_t tag
Definition tlv_library.h:87
uint32_t TLV_tag_t
uint64_t TLV_flag_t
TLV_flag_t() tag_to_flag_function_t(TLV_tag_t tag)
enum tlv_step_e tlv_step_t
bool get_uint16_t_from_tlv_data(const tlv_data_t *data, uint16_t *value)
Extract a uint16_t value from TLV data.
#define DER_LONG_FORM_FLAG
Definition tlv_library.c:28
bool get_string_from_tlv_data(const tlv_data_t *data, char *out, uint16_t min_length, uint16_t out_size)
Copy a NUL-terminated string from TLV data.
bool get_uint64_t_from_tlv_data(const tlv_data_t *data, uint64_t *value)
Extract a uint64_t value from TLV data.
Definition tlv_library.c:64
bool get_bool_from_tlv_data(const tlv_data_t *data, bool *value)
Extract a boolean value from TLV data.
#define DER_FIRST_BYTE_VALUE_MASK
Definition tlv_library.c:29
bool get_uint8_t_from_tlv_data(const tlv_data_t *data, uint8_t *value)
Extract a uint8_t value from TLV data.
static bool get_der_value_as_uint32(const buffer_t *payload, size_t *offset, uint32_t *value)
bool tlv_check_received_tags(TLV_reception_t received, const TLV_tag_t *tags, size_t tag_count)
Check that every tag in tags was received during parsing.
static const _internal_tlv_handler_t * find_handler(const _internal_tlv_handler_t *handlers, uint8_t handlers_count, TLV_tag_t tag)
Find the handler registered for a given tag.
static bool get_der_value_as_uint16(const buffer_t *payload, size_t *offset, uint16_t *value)
Parse a DER-encoded value and fit it into a uint16_t, or fail.
tlv_step_e
@ TLV_TAG
@ TLV_VALUE
@ TLV_LENGTH
bool tlv_enforce_u8_value(const tlv_data_t *data, uint8_t expected_value)
Enforce that a TLV data contains the expected uint8 value.
Definition tlv_library.c:38
bool _parse_tlv_internal(const _internal_tlv_handler_t *handlers, uint8_t handlers_count, tlv_handler_cb_t *common_handler, tag_to_flag_function_t *tag_to_flag_function, const buffer_t *payload, void *tlv_out, TLV_reception_t *received_tags_flags)
Parse a raw TLV payload using a pre-built handler table.
bool get_buffer_from_tlv_data(const tlv_data_t *data, buffer_t *out, uint16_t min_size, uint16_t max_size)
Extract a sized buffer from TLV data (zero-copy).
bool get_uint32_t_from_tlv_data(const tlv_data_t *data, uint32_t *value)
Extract a uint32_t value from TLV data.
Definition tlv_library.c:96
bool() tlv_handler_cb_t(const tlv_data_t *data, void *tlv_extracted)