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