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 uint8_t structure_type;
130 uint8_t signer_algo;
132
133 // Hash using all protocols at once
136
137static bool handle_structure_type(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
138{
139 return get_uint8_t_from_tlv_data(data, &tlv_extracted->structure_type);
140}
141
142static bool handle_version(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
143{
144 return get_uint8_t_from_tlv_data(data, &tlv_extracted->output->version);
145}
146
147static bool handle_trusted_name_type(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
148{
149 return get_uint8_t_from_tlv_data(data, &tlv_extracted->output->trusted_name_type);
150}
151
152static bool handle_trusted_name_source(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
153{
154 return get_uint8_t_from_tlv_data(data, &tlv_extracted->output->trusted_name_source);
155}
156
157static bool handle_trusted_name(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
158{
160 data, &tlv_extracted->output->trusted_name, 1, TRUSTED_NAME_STRINGS_MAX_SIZE);
161}
162
163static bool handle_chain_id(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
164{
165 return get_uint64_t_from_tlv_data(data, &tlv_extracted->output->chain_id);
166}
167
168static bool handle_address(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
169{
170 return get_buffer_from_tlv_data(data, &tlv_extracted->output->address, 1, 0);
171}
172
173static bool handle_nft_id(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
174{
175 return get_buffer_from_tlv_data(data, &tlv_extracted->output->nft_id, NFT_ID_SIZE, NFT_ID_SIZE);
176}
177
178static bool handle_source_contract(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
179{
180 return get_buffer_from_tlv_data(data, &tlv_extracted->output->source_contract, 1, 0);
181}
182
183static bool handle_challenge(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
184{
185 return get_uint32_t_from_tlv_data(data, &tlv_extracted->output->challenge);
186}
187
188static bool handle_not_valid_after(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
189{
190 if (data->value.size != sizeof(tlv_extracted->output->not_valid_after)) {
191 PRINTF("Invalid handle_not_valid_after format length %d\n", data->value.size);
192 return false;
193 }
194 tlv_extracted->output->not_valid_after.major = data->value.ptr[0];
195 tlv_extracted->output->not_valid_after.minor = data->value.ptr[1];
196 tlv_extracted->output->not_valid_after.patch = U2BE(data->value.ptr, 2);
197 PRINTF("major = %d minor = %d patch = %d\n",
198 tlv_extracted->output->not_valid_after.major,
199 tlv_extracted->output->not_valid_after.minor,
200 tlv_extracted->output->not_valid_after.patch);
201 return true;
202}
203
204static bool handle_signer_key_id(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
205{
206 return get_uint16_t_from_tlv_data(data, &tlv_extracted->signer_key_id);
207}
208
209static bool handle_signer_algorithm(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
210{
211 return get_uint8_t_from_tlv_data(data, &tlv_extracted->signer_algo);
212}
213
214static bool handle_der_signature(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
215{
218}
219
220static bool handle_common(const tlv_data_t *data, tlv_extracted_t *tlv_extracted);
221
222// clang-format off
223// List of TLV tags recognized by the Solana application
224#define TLV_TAGS(X) \
225 X(0x01, TAG_STRUCTURE_TYPE, handle_structure_type, ENFORCE_UNIQUE_TAG) \
226 X(0x02, TAG_VERSION, handle_version, ENFORCE_UNIQUE_TAG) \
227 X(0x70, TAG_TRUSTED_NAME_TYPE, handle_trusted_name_type, ENFORCE_UNIQUE_TAG) \
228 X(0x71, TAG_TRUSTED_NAME_SOURCE, handle_trusted_name_source, ENFORCE_UNIQUE_TAG) \
229 X(0x20, TAG_TRUSTED_NAME, handle_trusted_name, ENFORCE_UNIQUE_TAG) \
230 X(0x23, TAG_CHAIN_ID, handle_chain_id, ENFORCE_UNIQUE_TAG) \
231 X(0x22, TAG_ADDRESS, handle_address, ENFORCE_UNIQUE_TAG) \
232 X(0x72, TAG_NFT_ID, handle_nft_id, ENFORCE_UNIQUE_TAG) \
233 X(0x73, TAG_SOURCE_CONTRACT, handle_source_contract, ENFORCE_UNIQUE_TAG) \
234 X(0x12, TAG_CHALLENGE, handle_challenge, ENFORCE_UNIQUE_TAG) \
235 X(0x10, TAG_NOT_VALID_AFTER, handle_not_valid_after, ENFORCE_UNIQUE_TAG) \
236 X(0x13, TAG_SIGNER_KEY_ID, handle_signer_key_id, ENFORCE_UNIQUE_TAG) \
237 X(0x14, TAG_SIGNER_ALGORITHM, handle_signer_algorithm, ENFORCE_UNIQUE_TAG) \
238 X(0x15, TAG_DER_SIGNATURE, handle_der_signature, ENFORCE_UNIQUE_TAG)
239// clang-format on
240
241DEFINE_TLV_PARSER(TLV_TAGS, &handle_common, parse_tlv_trusted_name)
242
243static bool handle_common(const tlv_data_t *data, tlv_extracted_t *tlv_extracted)
244{
245 if (data->tag != TAG_DER_SIGNATURE) {
246 update_multi_hash_ctx(&tlv_extracted->hash_ctx, data->raw);
247 }
248 return true;
249}
250
252{
253#ifdef TRUSTED_NAME_TEST_KEY
254 uint16_t valid_key_id = TLV_TRUSTED_NAME_SIGNER_KEY_ID_TEST;
255#else
256 uint16_t valid_key_id = TLV_TRUSTED_NAME_SIGNER_KEY_ID_PROD;
257#endif
258
259 if (!TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags, TAG_STRUCTURE_TYPE)) {
260 PRINTF("Error: no struct type specified!\n");
262 }
263
264 if (tlv_extracted->structure_type != TLV_STRUCTURE_TYPE_TRUSTED_NAME) {
265 PRINTF("Error: unexpected struct type %d\n", tlv_extracted->structure_type);
267 }
268
269 // Check required fields
270 if (!TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags,
271 TAG_VERSION,
272 TAG_TRUSTED_NAME_TYPE,
273 TAG_TRUSTED_NAME_SOURCE,
274 TAG_TRUSTED_NAME,
275 TAG_CHAIN_ID,
276 TAG_ADDRESS,
277 TAG_SIGNER_KEY_ID,
278 TAG_SIGNER_ALGORITHM,
279 TAG_DER_SIGNATURE)) {
280 PRINTF("Error: missing required fields in struct version %d\n",
283 }
284
285 // Forward optional fields to caller application
286 tlv_extracted->output->nft_id_received
287 = TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags, TAG_NFT_ID);
288 tlv_extracted->output->source_contract_received
289 = TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags, TAG_SOURCE_CONTRACT);
290 tlv_extracted->output->challenge_received
291 = TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags, TAG_CHALLENGE);
292 tlv_extracted->output->not_valid_after_received
293 = TLV_CHECK_RECEIVED_TAGS(tlv_extracted->received_tags, TAG_NOT_VALID_AFTER);
294
295 if (tlv_extracted->output->version == 0
296 || tlv_extracted->output->version > CURRENT_TRUSTED_NAME_SPEC_VERSION) {
297 PRINTF("Error: unsupported struct version %d\n", tlv_extracted->output->version);
299 }
300
301 if (tlv_extracted->output->source_contract_received
303 PRINTF("Error: source_contract_received is only supported in v >= %d\n",
306 }
307
308 if (tlv_extracted->signer_key_id != valid_key_id) {
309 PRINTF("Error: wrong metadata key ID %u\n", tlv_extracted->signer_key_id);
311 }
312
314}
315
317{
318 // Finalize hash object filled by the parser
319 multi_hash_finalized_t multi_hash_finalized;
321 &tlv_extracted->hash_ctx, tlv_extracted->signer_algo, &multi_hash_finalized)
322 != 0) {
323 PRINTF("finalize_hash_for_algo failed\n");
325 }
326
327 cx_curve_t curve;
330 curve = CX_CURVE_Ed25519;
331 }
332 else {
333 // ECDSA
334 curve = CX_CURVE_SECP256K1;
335 }
336
337 // Verify that the signature field of the TLV is the signature of the TLV hash by the key loaded
338 // by the PKI
340 uint8_t expected_key_usage = CERTIFICATE_PUBLIC_KEY_USAGE_TRUSTED_NAME;
342 multi_hash_finalized.hash, &expected_key_usage, &curve, tlv_extracted->input_sig);
344 PRINTF("Failed to verify signature of trusted token info\n");
346 }
347
349}
350
353{
354 // Main structure that will received the parsed TLV data
355 tlv_extracted_t tlv_extracted = {0};
356 tlv_extracted.output = out;
358
359 // The parser will fill it with the hash of the whole TLV payload (except SIGN tag)
360 init_multi_hash_ctx(&tlv_extracted.hash_ctx);
361
362 // Call the function created by the macro from the TLV lib
363 if (!parse_tlv_trusted_name(payload, &tlv_extracted, &tlv_extracted.received_tags)) {
364 PRINTF("Failed to parse tlv payload\n");
366 }
367
368 // Verify that the fields received are correct in our context
369 err = verify_struct(&tlv_extracted);
370 if (err != TLV_TRUSTED_NAME_SUCCESS) {
371 PRINTF("Failed to verify tlv payload\n");
372 return err;
373 }
374
375 // Verify that the fields received are correct in our context
376 err = verify_signature(&tlv_extracted);
377 if (err != TLV_TRUSTED_NAME_SUCCESS) {
378 PRINTF("Failed to verify trusted name signature\n");
379 return err;
380 }
381
383}
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_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)
@ TLV_TRUSTED_NAME_SIGNER_KEY_ID_PROD
@ TLV_TRUSTED_NAME_SIGNER_KEY_ID_TEST
#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_WRONG_KEY_ID
@ 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