Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
tlv_library.h
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#pragma once
18
19#include <stddef.h>
20#include <stdint.h>
21#include <stdbool.h>
22
23#include "tlv_internals.h"
24#include "buffer.h"
25
26// ─────────────────────────────────────────────────────────────────────────────
27// TLV example
28// ─────────────────────────────────────────────────────────────────────────────
29
30/* The SDK exposes the TLV library to allow applications to implement their own TLV parser for
31 * their own use cases.
32 *
33 * The SDK also exposes several TLV parsers already coded for cross-application use-cases.
34 * Please refer instead to the documentation of each use case for more details about them.
35 *
36 * As usage of this library can seem complicated at first, here is as an introduction a minimalist
37 * example implementation
38 *
39```c
40
41typedef struct my_tlv_output_s {
42 TLV_reception_t received_tags;
43 uint8_t my_value_1;
44 uint32_t my_value_2;
45} my_tlv_output_t;
46
47static bool my_handler_1(const tlv_data_t *data, my_tlv_output_t *out) {
48 return get_uint8_t_from_tlv_data(data, &out->my_value_1);
49}
50
51static bool my_handler_2(const tlv_data_t *data, my_tlv_output_t *out) {
52 return get_uint32_t_from_tlv_data(data, &out->my_value_2);
53}
54
55// We are not interested in TAG_C
56#define MY_TAGS(X) \
57 X(0x0A, TAG_A, my_handler_1, ENFORCE_UNIQUE_TAG) \
58 X(0x1F, TAG_B, my_handler_2, ENFORCE_UNIQUE_TAG) \
59 X(0x77, TAG_C, NULL, ALLOW_MULTIPLE_TAG)
60
61// Create my_tlv_parser function
62DEFINE_TLV_PARSER(MY_TAGS, NULL, my_tlv_parser)
63
64bool parse_and_return_a_value(buffer_t payload, uint8_t *my_value_to_receive) {
65 my_tlv_output_t out = {0};
66 if (!my_tlv_parser(&payload, &out, &out.received_tags)) {
67 // my_tlv_parser failed
68 return false;
69 }
70 // Ensure we have received both values
71 if (!CHECK_RECEIVED_TAGS(out.received_tags, TAG_A, TAG_B)) {
72 return false;
73 }
74 *my_value_to_receive = out.my_value_1;
75 return true;
76}
77```
78 */
79
80// ─────────────────────────────────────────────────────────────────────────────
81// TLV library API
82// ─────────────────────────────────────────────────────────────────────────────
83
84// The received TLV data that will be fed as input to each handler
85typedef struct tlv_data_s {
86 // The tag is reminded in case the handler is generic and covers multiple tags
88 // The length of the value buffer
89 // uint16_t length;
90 // The value buffer
92
93 // In case the handler needs to do some processing on the raw TLV-encoded data
94 // Computing a signature for example
95 // const uint8_t *raw;
96 // uint16_t raw_size;
99
100// The handlers prototype to give to give to DEFINE_TLV_PARSER() through the TAG_LIST parameter
101// The type of tlv_extracted must be the same for every handler of a given TLV use case
102typedef bool(tlv_handler_cb_t)(const tlv_data_t *data, void *tlv_extracted);
103
104// The generated TLV parser will set in this structure the flags of each received tag
105// Actual structure content is private, use check_received_tags or CHECK_RECEIVED_TAGS
107
108typedef enum tag_unicity_e {
109 // The parser will fail if the associated tag is received more than once
111 // The parser will call the handler every time this tag is received
114
143// clang-format off
144#define DEFINE_TLV_PARSER(TAG_LIST, COMMON_HANDLER, PARSE_FUNCTION_NAME) \
145 /* Create an enum associating tags with their values */ \
146 enum { \
147 TAG_LIST(__X_DEFINE_TLV__TAG_ASSIGN) \
148 }; \
149 \
150 /* Create an enum associating tags with their indexes (for internal use). */ \
151 enum { \
152 TAG_LIST(__X_DEFINE_TLV__TAG_INDEX) \
153 PARSE_FUNCTION_NAME##_TAG_COUNT, \
154 }; \
155 \
156 _Static_assert(PARSE_FUNCTION_NAME##_TAG_COUNT <= sizeof(TLV_flag_t) * 8, \
157 "Too many tags: exceeds the number of bits in TLV_flag_t"); \
158 \
159 /* Create an enum associating tags with their flags (for internal use). */ \
160 enum { \
161 TAG_LIST(__X_DEFINE_TLV__TAG_FLAG) \
162 }; \
163 \
164 /* A dynamically generated function that maps tags to flags (for internal use). */ \
165 /* */ \
166 /* @param[in] tag The tag to get the flag of*/ \
167 /* @return The associated flag or 0 if none exist */ \
168 static inline TLV_flag_t PARSE_FUNCTION_NAME##_tag_to_flag(TLV_tag_t tag) { \
169 switch (tag) { \
170 TAG_LIST(__X_DEFINE_TLV__TAG_TO_FLAG_CASE) \
171 default: \
172 return 0; \
173 } \
174 } \
175 \
176 /* A dynamically generated TLV parser function for this TLV use case. */ \
177 /* */ \
178 /* Parses a TLV payload using the tag and handlers of TAG_LIST */ \
179 /* */ \
180 /* @param[in] payload the TLV payload to parse */ \
181 /* @param[out] tlv_out The output data of this parser, written in by the handlers */ \
182 /* @param[out] received_tags_flags The tag reception structure */ \
183 /* @return whether it was successful */ \
184 static inline bool PARSE_FUNCTION_NAME(const buffer_t *payload, \
185 void *tlv_out, \
186 TLV_reception_t *received_tags_flags) { \
187 /* Create a local array of handlers to give to the generic parser */ \
188 _internal_tlv_handler_t handlers[PARSE_FUNCTION_NAME##_TAG_COUNT] = { \
189 TAG_LIST(__X_DEFINE_TLV__TAG_CALLBACKS) \
190 }; \
191 return _parse_tlv_internal(handlers, \
192 PARSE_FUNCTION_NAME##_TAG_COUNT, \
193 (tlv_handler_cb_t*) COMMON_HANDLER, \
194 PARSE_FUNCTION_NAME##_tag_to_flag, \
195 payload, \
196 tlv_out, \
197 received_tags_flags); \
198 }
199// clang-format on
200
201bool tlv_enforce_u8_value(const tlv_data_t *data, uint8_t expected_value);
202
203bool get_uint64_t_from_tlv_data(const tlv_data_t *data, uint64_t *value);
204bool get_uint32_t_from_tlv_data(const tlv_data_t *data, uint32_t *value);
205bool get_uint16_t_from_tlv_data(const tlv_data_t *data, uint16_t *value);
206bool get_uint8_t_from_tlv_data(const tlv_data_t *data, uint8_t *value);
207
208bool get_bool_from_tlv_data(const tlv_data_t *data, bool *value);
209
210bool get_buffer_from_tlv_data(const tlv_data_t *data,
211 buffer_t *out,
212 uint16_t min_size,
213 uint16_t max_size);
214
215bool get_string_from_tlv_data(const tlv_data_t *data,
216 char *out,
217 uint16_t min_length,
218 uint16_t out_size);
219
220bool tlv_check_received_tags(TLV_reception_t received, const TLV_tag_t *tags, size_t tag_count);
221
225#define TLV_CHECK_RECEIVED_TAGS(received, ...) \
226 tlv_check_received_tags(received, \
227 (TLV_tag_t[]){__VA_ARGS__}, \
228 sizeof((TLV_tag_t[]){__VA_ARGS__}) / sizeof(TLV_tag_t))
229
230// ─────────────────────────────────────────────────────────────────────────────
231// TLV internal data
232// ─────────────────────────────────────────────────────────────────────────────
233
234// Maps a tag to a handler function and some associated metadata
235typedef struct {
237 // Handler to call when encountering this tag. Can be NULL.
239 // If true the parser will check if this tag was already received before calling the handler
242
244 uint8_t handlers_count,
245 tlv_handler_cb_t *common_handler,
246 tag_to_flag_function_t *tag_to_flag_function,
247 const buffer_t *payload,
248 void *tlv_out,
249 TLV_reception_t *received_tags_flags);
tlv_handler_cb_t * func
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
TLV_flag_t() tag_to_flag_function_t(TLV_tag_t tag)
enum tag_unicity_e tag_unicity_t
bool() tlv_handler_cb_t(const tlv_data_t *data, void *tlv_extracted)
bool get_uint16_t_from_tlv_data(const tlv_data_t *data, uint16_t *value)
Extract a uint16_t value from TLV data.
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.
bool get_uint8_t_from_tlv_data(const tlv_data_t *data, uint8_t *value)
Extract a uint8_t value from TLV data.
TLV_reception_internal_t TLV_reception_t
tag_unicity_e
@ ENFORCE_UNIQUE_TAG
@ ALLOW_MULTIPLE_TAG
struct tlv_data_s tlv_data_t
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.
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