Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
tlv_use_case_trusted_name.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 <stdint.h>
18#include <string.h>
19#include <ctype.h>
20
21#include "os_utils.h"
22#include "ox_ec.h"
23#include "cx.h"
24#include "os_pki.h"
25#include "ledger_pki.h"
27
28// Progressive hash of the received TLVs (except the signature type)
29// We will know the correct format to use at reception of the signer_algo tag. We don't try to
30// be clever and just calculate all hash until then.
31// Performance hit is unnoticeable. Memory footprint is negligeable.
32typedef struct multi_hash_ctx_s {
33 cx_sha256_t sha256;
34 cx_sha3_t sha3_256;
35 cx_sha3_t keccak_256;
36 cx_ripemd160_t ripemd160;
37 cx_sha512_t sha512;
39
40// Output of the multi hash after hash finalize
41typedef struct multi_hash_finalized_u {
42 // Use a union trick to always declare at max size
43 union {
44 uint8_t _sha256[CX_SHA256_SIZE];
45 uint8_t _sha3_256[CX_SHA3_256_SIZE];
46 uint8_t _keccak_256[CX_KECCAK_256_SIZE];
47 uint8_t _ripemd160[CX_RIPEMD160_SIZE];
48 uint8_t _sha512[CX_SHA512_SIZE];
49 // Generic access at offset 0
50 uint8_t _offset_0;
51 };
54
56{
57 // Use CX_ASSERT, none of those functions can reasonably fail
58 CX_ASSERT(cx_sha256_init_no_throw(&hash_ctx->sha256));
59 CX_ASSERT(cx_sha3_init_no_throw(&hash_ctx->sha3_256, CX_SHA3_256_SIZE * 8));
60 CX_ASSERT(cx_keccak_init_no_throw(&hash_ctx->keccak_256, CX_KECCAK_256_SIZE * 8));
61 CX_ASSERT(cx_ripemd160_init_no_throw(&hash_ctx->ripemd160));
62 CX_ASSERT(cx_sha512_init_no_throw(&hash_ctx->sha512));
63}
64
65// Feed data into all hash without concern
67{
68 // Use CX_ASSERT, none of those functions can reasonably fail
69 CX_ASSERT(cx_hash_update((cx_hash_t *) &hash_ctx->sha256, data.ptr, data.size));
70 CX_ASSERT(cx_hash_update((cx_hash_t *) &hash_ctx->sha3_256, data.ptr, data.size));
71 CX_ASSERT(cx_hash_update((cx_hash_t *) &hash_ctx->keccak_256, data.ptr, data.size));
72 CX_ASSERT(cx_hash_update((cx_hash_t *) &hash_ctx->ripemd160, data.ptr, data.size));
73 CX_ASSERT(cx_hash_update((cx_hash_t *) &hash_ctx->sha512, data.ptr, data.size));
74}
75
76// Select the correct hash to use depending on the requested algorithm and finalize it
77static int finalize_hash_for_algo(const multi_hash_ctx_t *hash_ctx,
79 multi_hash_finalized_t *multi_hash_finalized)
80{
81 cx_hash_t *hash;
82 switch (signer_algo) {
84 hash = (cx_hash_t *) &hash_ctx->sha256;
85 multi_hash_finalized->hash.size = sizeof(multi_hash_finalized->_sha256);
86 break;
89 hash = (cx_hash_t *) &hash_ctx->sha3_256;
90 multi_hash_finalized->hash.size = sizeof(multi_hash_finalized->_sha3_256);
91 break;
94 hash = (cx_hash_t *) &hash_ctx->keccak_256;
95 multi_hash_finalized->hash.size = sizeof(multi_hash_finalized->_keccak_256);
96 break;
98 hash = (cx_hash_t *) &hash_ctx->ripemd160;
99 multi_hash_finalized->hash.size = sizeof(multi_hash_finalized->_ripemd160);
100 break;
102 hash = (cx_hash_t *) &hash_ctx->sha512;
103 multi_hash_finalized->hash.size = sizeof(multi_hash_finalized->_sha512);
104 break;
105 default:
106 PRINTF("Unknown signer_algo %d\n", signer_algo);
107 return -1;
108 }
109
110 multi_hash_finalized->hash.ptr = &multi_hash_finalized->_offset_0;
111 if (cx_hash_final(hash, &multi_hash_finalized->_offset_0) != CX_OK) {
112 PRINTF("cx_hash_final failed for algo %d\n", signer_algo);
113 return -1;
114 }
115
116 return 0;
117}
118
119// Parsed TLV data
120typedef struct tlv_extracted_s {
121 // Received tags set by the parser
123
124 // Trusted name output data
126
127 // Tags handled by the use case API and not the caller
128 // Note: signer_key_id is parsed but not verified, the signer authorization is enforced by the
129 // key usage of the PKI certificate, see verify_signature()
130 uint8_t structure_type;
132 uint8_t signer_algo;
134
135 // Hash using all protocols at once
138
139static bool handle_structure_type(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
140{
141 return get_uint8_t_from_tlv_data(data, &tlv_extracted->structure_type);
142}
143
144static bool handle_version(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
145{
146 return get_uint8_t_from_tlv_data(data, &tlv_extracted->output->version);
147}
148
149static bool handle_trusted_name_type(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
150{
151 return get_uint8_t_from_tlv_data(data, &tlv_extracted->output->trusted_name_type);
152}
153
154static bool handle_trusted_name_source(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
155{
156 return get_uint8_t_from_tlv_data(data, &tlv_extracted->output->trusted_name_source);
157}
158
159static bool handle_blockchain_family(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
160{
161 return get_uint8_t_from_tlv_data(data, &tlv_extracted->output->blockchain_family);
162}
163
164static bool handle_trusted_name(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
165{
167 data, &tlv_extracted->output->trusted_name, 1, TRUSTED_NAME_STRINGS_MAX_SIZE);
168}
169
170static bool handle_chain_id(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
171{
172 return get_uint64_t_from_tlv_data(data, &tlv_extracted->output->chain_id);
173}
174
175static bool handle_address(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
176{
177 return get_buffer_from_tlv_data(data, &tlv_extracted->output->address, 1, 0);
178}
179
180static bool handle_nft_id(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
181{
182 return get_buffer_from_tlv_data(data, &tlv_extracted->output->nft_id, NFT_ID_SIZE, NFT_ID_SIZE);
183}
184
185static bool handle_source_contract(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
186{
187 return get_buffer_from_tlv_data(data, &tlv_extracted->output->source_contract, 1, 0);
188}
189
190static bool handle_challenge(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
191{
192 return get_uint32_t_from_tlv_data(data, &tlv_extracted->output->challenge);
193}
194
195static bool handle_not_valid_after(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
196{
197 switch (data->value.size) {
198 case 3:
199 // uint8 major, uint8 minor, uint8 patch
200 tlv_extracted->output->not_valid_after.patch = data->value.ptr[2];
201 break;
202 case 4:
203 // uint8 major, uint8 minor, uint16 BE patch
204 tlv_extracted->output->not_valid_after.patch = U2BE(data->value.ptr, 2);
205 break;
206 default:
207 PRINTF("Invalid handle_not_valid_after format length %d\n", data->value.size);
208 return false;
209 }
210 tlv_extracted->output->not_valid_after.major = data->value.ptr[0];
211 tlv_extracted->output->not_valid_after.minor = data->value.ptr[1];
212 PRINTF("major = %d minor = %d patch = %d\n",
213 tlv_extracted->output->not_valid_after.major,
214 tlv_extracted->output->not_valid_after.minor,
215 tlv_extracted->output->not_valid_after.patch);
216 return true;
217}
218
219static bool handle_signer_key_id(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
220{
221 return get_uint16_t_from_tlv_data(data, &tlv_extracted->signer_key_id);
222}
223
224static bool handle_signer_algorithm(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
225{
226 return get_uint8_t_from_tlv_data(data, &tlv_extracted->signer_algo);
227}
228
229static bool handle_der_signature(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
230{
233}
234
235static bool handle_common(const tlv_data_t *data, tlv_extracted_t *tlv_extracted);
236
237// clang-format off
238// List of TLV tags recognized by the Solana application
239#define TLV_TAGS(X) \
240 X(0x01, TAG_STRUCTURE_TYPE, handle_structure_type, ENFORCE_UNIQUE_TAG) \
241 X(0x02, TAG_VERSION, handle_version, ENFORCE_UNIQUE_TAG) \
242 X(0x70, TAG_TRUSTED_NAME_TYPE, handle_trusted_name_type, ENFORCE_UNIQUE_TAG) \
243 X(0x71, TAG_TRUSTED_NAME_SOURCE, handle_trusted_name_source, ENFORCE_UNIQUE_TAG) \
244 X(0x51, TAG_BLOCKCHAIN_FAMILY, handle_blockchain_family, ENFORCE_UNIQUE_TAG) \
245 X(0x20, TAG_TRUSTED_NAME, handle_trusted_name, ENFORCE_UNIQUE_TAG) \
246 X(0x23, TAG_CHAIN_ID, handle_chain_id, ENFORCE_UNIQUE_TAG) \
247 X(0x22, TAG_ADDRESS, handle_address, ENFORCE_UNIQUE_TAG) \
248 X(0x72, TAG_NFT_ID, handle_nft_id, ENFORCE_UNIQUE_TAG) \
249 X(0x73, TAG_SOURCE_CONTRACT, handle_source_contract, ENFORCE_UNIQUE_TAG) \
250 X(0x12, TAG_CHALLENGE, handle_challenge, ENFORCE_UNIQUE_TAG) \
251 X(0x10, TAG_NOT_VALID_AFTER, handle_not_valid_after, ENFORCE_UNIQUE_TAG) \
252 X(0x13, TAG_SIGNER_KEY_ID, handle_signer_key_id, ENFORCE_UNIQUE_TAG) \
253 X(0x14, TAG_SIGNER_ALGORITHM, handle_signer_algorithm, ENFORCE_UNIQUE_TAG) \
254 X(0x15, TAG_DER_SIGNATURE, handle_der_signature, ENFORCE_UNIQUE_TAG)
255// clang-format on
256
257DEFINE_TLV_PARSER(TLV_TAGS, &handle_common, parse_tlv_trusted_name)
258
259static bool handle_common(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
260{
261 if (data->tag != TAG_DER_SIGNATURE) {
262 update_multi_hash_ctx(&tlv_extracted->hash_ctx, data->raw);
263 }
264 return true;
265}
266
268{
269 if (!TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags, TAG_STRUCTURE_TYPE)) {
270 PRINTF("Error: no struct type specified!\n");
272 }
273
274 if (tlv_extracted->structure_type != TLV_STRUCTURE_TYPE_TRUSTED_NAME) {
275 PRINTF("Error: unexpected struct type %d\n", tlv_extracted->structure_type);
277 }
278
279 // Check required fields
280 if (!TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags,
281 TAG_VERSION,
282 TAG_TRUSTED_NAME_TYPE,
283 TAG_TRUSTED_NAME_SOURCE,
284 TAG_TRUSTED_NAME,
285 TAG_CHAIN_ID,
286 TAG_ADDRESS,
287 TAG_SIGNER_KEY_ID,
288 TAG_SIGNER_ALGORITHM,
289 TAG_DER_SIGNATURE)) {
290 PRINTF("Error: missing required fields in struct version %d\n",
293 }
294
295 // Forward optional fields to caller application
296 tlv_extracted->output->nft_id_received
297 = TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags, TAG_NFT_ID);
298 tlv_extracted->output->source_contract_received
299 = TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags, TAG_SOURCE_CONTRACT);
300 tlv_extracted->output->challenge_received
301 = TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags, TAG_CHALLENGE);
302 tlv_extracted->output->not_valid_after_received
303 = TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags, TAG_NOT_VALID_AFTER);
304 tlv_extracted->output->blockchain_family_received
305 = TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags, TAG_BLOCKCHAIN_FAMILY);
306
307 if (tlv_extracted->output->version == 0
308 || tlv_extracted->output->version > CURRENT_TRUSTED_NAME_SPEC_VERSION) {
309 PRINTF("Error: unsupported struct version %d\n", tlv_extracted->output->version);
311 }
312
313 if (tlv_extracted->output->source_contract_received
315 PRINTF("Error: source_contract_received is only supported in v >= %d\n",
318 }
319
321}
322
324{
325 // Finalize hash object filled by the parser
326 multi_hash_finalized_t multi_hash_finalized;
328 &tlv_extracted->hash_ctx, tlv_extracted->signer_algo, &multi_hash_finalized)
329 != 0) {
330 PRINTF("finalize_hash_for_algo failed\n");
332 }
333
334 cx_curve_t curve;
337 curve = CX_CURVE_Ed25519;
338 }
339 else {
340 // ECDSA
341 curve = CX_CURVE_SECP256K1;
342 }
343
344 // Verify that the signature field of the TLV is the signature of the TLV hash by the key loaded
345 // by the PKI
347 uint8_t expected_key_usage = CERTIFICATE_PUBLIC_KEY_USAGE_TRUSTED_NAME;
349 multi_hash_finalized.hash, &expected_key_usage, &curve, tlv_extracted->input_sig);
351 PRINTF("Failed to verify signature of trusted token info\n");
353 }
354
356}
357
360{
361 // Main structure that will received the parsed TLV data
362 tlv_extracted_t tlv_extracted = {0};
363 tlv_extracted.output = out;
365
366 // The parser will fill it with the hash of the whole TLV payload (except SIGN tag)
367 init_multi_hash_ctx(&tlv_extracted.hash_ctx);
368
369 // Call the function created by the macro from the TLV lib
370 if (!parse_tlv_trusted_name(payload, &tlv_extracted, &tlv_extracted.received_tags)) {
371 PRINTF("Failed to parse tlv payload\n");
373 }
374
375 // Verify that the fields received are correct in our context
376 err = verify_struct(&tlv_extracted);
377 if (err != TLV_TRUSTED_NAME_SUCCESS) {
378 PRINTF("Failed to verify tlv payload\n");
379 return err;
380 }
381
382 // Verify that the fields received are correct in our context
383 err = verify_signature(&tlv_extracted);
384 if (err != TLV_TRUSTED_NAME_SUCCESS) {
385 PRINTF("Failed to verify trusted name signature\n");
386 return err;
387 }
388
390}
check_signature_with_pki_status_t check_signature_with_pki(const buffer_t hash, const uint8_t *expected_key_usage, const cx_curve_t *expected_curve, const buffer_t signature)
Checks a signature using the PKI certificate.
Definition ledger_pki.c:5
@ CHECK_SIGNATURE_WITH_PKI_SUCCESS
Definition ledger_pki.h:7
enum check_signature_with_pki_status_e check_signature_with_pki_status_t
uint8_t * ptr
Definition buffer.h:22
size_t size
Pointer to byte buffer.
Definition buffer.h:23
uint8_t _keccak_256[CX_KECCAK_256_SIZE]
uint8_t _ripemd160[CX_RIPEMD160_SIZE]
uint8_t _sha3_256[CX_SHA3_256_SIZE]
uint8_t _sha256[CX_SHA256_SIZE]
uint8_t _sha512[CX_SHA512_SIZE]
buffer_t value
Definition tlv_library.h:91
tlv_trusted_name_out_t * output
tlv_dynamic_descriptor_out_t * output
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_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_uint8_t_from_tlv_data(const tlv_data_t *data, uint8_t *value)
Extract a uint8_t value from TLV data.
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
#define DEFINE_TLV_PARSER(TAG_LIST, COMMON_HANDLER, PARSE_FUNCTION_NAME)
Creates a parser function for a given TLV use case.
#define TLV_CHECK_RECEIVED_TAGS(received,...)
#define DER_SIGNATURE_MIN_SIZE
#define DER_SIGNATURE_MAX_SIZE
static tlv_trusted_name_status_t verify_struct(const tlv_extracted_t *tlv_extracted)
tlv_trusted_name_status_t tlv_use_case_trusted_name(const buffer_t *payload, tlv_trusted_name_out_t *out)
Processes a TLV dynamic descriptor use case.
static bool handle_trusted_name_source(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static bool handle_trusted_name_type(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static bool handle_chain_id(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static void update_multi_hash_ctx(multi_hash_ctx_t *hash_ctx, buffer_t data)
struct tlv_extracted_s tlv_extracted_t
static bool handle_address(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
#define TLV_TAGS(X)
static bool handle_signer_key_id(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static tlv_trusted_name_status_t verify_signature(const tlv_extracted_t *tlv_extracted)
static bool handle_nft_id(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static bool handle_signer_algorithm(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static bool handle_version(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static bool handle_source_contract(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static bool handle_not_valid_after(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static bool handle_blockchain_family(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static bool handle_trusted_name(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
struct multi_hash_finalized_u multi_hash_finalized_t
static bool handle_structure_type(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static void init_multi_hash_ctx(multi_hash_ctx_t *hash_ctx)
struct multi_hash_ctx_s multi_hash_ctx_t
static bool handle_common(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static int finalize_hash_for_algo(const multi_hash_ctx_t *hash_ctx, tlv_trusted_name_signer_algorithm_t signer_algo, multi_hash_finalized_t *multi_hash_finalized)
static bool handle_der_signature(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
static bool handle_challenge(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
#define TRUSTED_NAME_STRINGS_MAX_SIZE
enum tlv_trusted_name_status_e tlv_trusted_name_status_t
#define CURRENT_TRUSTED_NAME_SPEC_VERSION
@ TLV_STRUCTURE_TYPE_TRUSTED_NAME
@ TLV_TRUSTED_NAME_SIGNATURE_ERROR
@ TLV_TRUSTED_NAME_PARSING_ERROR
@ TLV_TRUSTED_NAME_UNKNOWN_VERSION
@ TLV_TRUSTED_NAME_SUCCESS
@ TLV_TRUSTED_NAME_MISSING_TAG
@ TLV_TRUSTED_NAME_UNSUPPORTED_TAG
@ TLV_TRUSTED_NAME_MISSING_STRUCTURE_TAG
@ TLV_TRUSTED_NAME_WRONG_TYPE
@ TLV_TRUSTED_NAME_HASH_FAILED
enum tlv_trusted_name_signer_algorithm_e tlv_trusted_name_signer_algorithm_t
@ TLV_TRUSTED_NAME_SIGNER_ALGORITHM_ECDSA_SHA3_256
@ TLV_TRUSTED_NAME_SIGNER_ALGORITHM_EDDSA_KECCAK_256
@ TLV_TRUSTED_NAME_SIGNER_ALGORITHM_ECDSA_SHA256
@ TLV_TRUSTED_NAME_SIGNER_ALGORITHM_ECDSA_KECCAK_256
@ TLV_TRUSTED_NAME_SIGNER_ALGORITHM_EDDSA_SHA3_256
@ TLV_TRUSTED_NAME_SIGNER_ALGORITHM_ECDSA_RIPEMD160
@ TLV_TRUSTED_NAME_SIGNER_ALGORITHM_ECDSA_SHA512
#define NFT_ID_SIZE
#define SOURCE_CONTRACT_RECEIVED_MIN_VERSION