Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
identity_edit_identifier.c
Go to the documentation of this file.
1/*****************************************************************************
2 * (c) 2026 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
40/* Includes ------------------------------------------------------------------*/
41#include <string.h>
42#include "os_apdu.h"
43#include "status_words.h"
44#include "tlv_library.h"
45#include "buffer.h"
46#include "bip32.h"
47#include "address_book.h"
49#include "address_book_crypto.h"
50#include "address_book_common.h"
51#include "identity.h"
52#include "io.h"
53#include "nbgl_use_case.h"
54
55#if defined(HAVE_ADDRESS_BOOK)
56
57/* Private defines------------------------------------------------------------*/
58#define STRUCT_VERSION 0x01
59
60/* Private types, structures, unions -----------------------------------------*/
61typedef struct {
62 edit_identifier_t *edit;
63 TLV_reception_t received_tags;
64 uint8_t hmac_proof[CX_SHA256_SIZE];
65 uint8_t hmac_rest[CX_SHA256_SIZE];
66 uint8_t group_handle[GROUP_HANDLE_SIZE];
67} s_edit_ctx;
68
69/* Private macros-------------------------------------------------------------*/
70#define EDIT_IDENTIFIER_TAGS(X) \
71 X(0x01, TAG_STRUCTURE_TYPE, handle_struct_type, ENFORCE_UNIQUE_TAG) \
72 X(0x02, TAG_STRUCTURE_VERSION, handle_struct_version, ENFORCE_UNIQUE_TAG) \
73 X(0xf0, TAG_CONTACT_NAME, handle_contact_name, ENFORCE_UNIQUE_TAG) \
74 X(0xf1, TAG_SCOPE, handle_scope, ENFORCE_UNIQUE_TAG) \
75 X(0xf2, TAG_ACCOUNT_IDENTIFIER, handle_identifier, ENFORCE_UNIQUE_TAG) \
76 X(0xf4, TAG_PREVIOUS_IDENTIFIER, handle_previous_identifier, ENFORCE_UNIQUE_TAG) \
77 X(0xf6, TAG_GROUP_HANDLE, handle_group_handle, ENFORCE_UNIQUE_TAG) \
78 X(0x23, TAG_CHAIN_ID, handle_chain_id, ENFORCE_UNIQUE_TAG) \
79 X(0x29, TAG_HMAC_PROOF, handle_hmac_proof, ENFORCE_UNIQUE_TAG) \
80 X(0xf7, TAG_HMAC_REST, handle_hmac_rest, ENFORCE_UNIQUE_TAG) \
81 X(0x51, TAG_BLOCKCHAIN_FAMILY, handle_blockchain_family, ENFORCE_UNIQUE_TAG)
82
83/* Private variables ---------------------------------------------------------*/
84
85/* Private functions ---------------------------------------------------------*/
86
94static bool handle_struct_type(const tlv_data_t *data, s_edit_ctx *context)
95{
96 UNUSED(context);
97 if (!tlv_enforce_u8_value(data, TYPE_EDIT_IDENTIFIER)) {
98 PRINTF("[Edit Identifier] Invalid STRUCTURE_TYPE value\n");
99 return false;
100 }
101 return true;
102}
103
111static bool handle_struct_version(const tlv_data_t *data, s_edit_ctx *context)
112{
113 UNUSED(context);
114 if (!tlv_enforce_u8_value(data, STRUCT_VERSION)) {
115 PRINTF("[Edit Identifier] Invalid STRUCTURE_VERSION value\n");
116 return false;
117 }
118 return true;
119}
120
128static bool handle_contact_name(const tlv_data_t *data, s_edit_ctx *context)
129{
130 if (!address_book_handle_printable_string(data,
131 context->edit->identity.contact_name,
132 sizeof(context->edit->identity.contact_name))) {
133 PRINTF("[Edit Identifier] CONTACT_NAME: failed to parse\n");
134 return false;
135 }
136 return true;
137}
138
146static bool handle_scope(const tlv_data_t *data, s_edit_ctx *context)
147{
148 if (!address_book_handle_printable_string(
149 data, context->edit->identity.scope, sizeof(context->edit->identity.scope))) {
150 PRINTF("[Edit Identifier] SCOPE: failed to parse\n");
151 return false;
152 }
153 return true;
154}
155
163static bool handle_identifier(const tlv_data_t *data, s_edit_ctx *context)
164{
165 buffer_t buf = {0};
166 if (!get_buffer_from_tlv_data(data, &buf, 1, IDENTIFIER_MAX_LENGTH)) {
167 PRINTF("[Edit Identifier] IDENTIFIER: failed to extract\n");
168 return false;
169 }
170 memmove(context->edit->identity.identifier, buf.ptr, buf.size);
171 context->edit->identity.identifier_len = (uint8_t) buf.size;
172 return true;
173}
174
184static bool handle_previous_identifier(const tlv_data_t *data, s_edit_ctx *context)
185{
186 buffer_t buf = {0};
187 if (!get_buffer_from_tlv_data(data, &buf, 1, IDENTIFIER_MAX_LENGTH)) {
188 PRINTF("[Edit Identifier] PREVIOUS_IDENTIFIER: failed to extract\n");
189 return false;
190 }
191 memmove(context->edit->old_identifier, buf.ptr, buf.size);
192 context->edit->old_identifier_len = (uint8_t) buf.size;
193 return true;
194}
195
203static bool handle_group_handle(const tlv_data_t *data, s_edit_ctx *context)
204{
205 buffer_t buf = {0};
206 if (!get_buffer_from_tlv_data(data, &buf, GROUP_HANDLE_SIZE, GROUP_HANDLE_SIZE)) {
207 PRINTF("[Edit Identifier] GROUP_HANDLE: invalid length (expected %d bytes)\n",
208 GROUP_HANDLE_SIZE);
209 return false;
210 }
211 memmove(context->group_handle, buf.ptr, GROUP_HANDLE_SIZE);
212 return true;
213}
214
222static bool handle_chain_id(const tlv_data_t *data, s_edit_ctx *context)
223{
224 return address_book_handle_chain_id(data, &context->edit->identity.chain_id);
225}
226
234static bool handle_blockchain_family(const tlv_data_t *data, s_edit_ctx *context)
235{
236 return address_book_handle_blockchain_family(data, &context->edit->identity.blockchain_family);
237}
238
246static bool handle_hmac_proof(const tlv_data_t *data, s_edit_ctx *context)
247{
248 buffer_t buf = {0};
249 if (!get_buffer_from_tlv_data(data, &buf, CX_SHA256_SIZE, CX_SHA256_SIZE)) {
250 PRINTF("[Edit Identifier] HMAC_PROOF: invalid length (expected %d bytes)\n",
251 CX_SHA256_SIZE);
252 return false;
253 }
254 memmove(context->hmac_proof, buf.ptr, CX_SHA256_SIZE);
255 return true;
256}
257
265static bool handle_hmac_rest(const tlv_data_t *data, s_edit_ctx *context)
266{
267 buffer_t buf = {0};
268 if (!get_buffer_from_tlv_data(data, &buf, CX_SHA256_SIZE, CX_SHA256_SIZE)) {
269 PRINTF("[Edit Identifier] HMAC_REST: invalid length (expected %d bytes)\n", CX_SHA256_SIZE);
270 return false;
271 }
272 memmove(context->hmac_rest, buf.ptr, CX_SHA256_SIZE);
273 return true;
274}
275
276DEFINE_TLV_PARSER(EDIT_IDENTIFIER_TAGS, NULL, edit_identifier_tlv_parser)
277
278
284static bool verify_fields(const s_edit_ctx *context)
285{
286 bool result = TLV_CHECK_RECEIVED_TAGS(context->received_tags,
287 TAG_STRUCTURE_TYPE,
288 TAG_STRUCTURE_VERSION,
289 TAG_CONTACT_NAME,
290 TAG_ACCOUNT_IDENTIFIER,
291 TAG_PREVIOUS_IDENTIFIER,
292 TAG_GROUP_HANDLE,
293 TAG_BLOCKCHAIN_FAMILY,
294 TAG_HMAC_PROOF,
295 TAG_HMAC_REST);
296 if (!result) {
297 PRINTF("[Edit Identifier] Missing mandatory fields!\n");
298 return false;
299 }
300 if (context->edit->identity.blockchain_family == FAMILY_ETHEREUM) {
301 result = TLV_CHECK_RECEIVED_TAGS(context->received_tags, TAG_CHAIN_ID);
302 if (!result) {
303 PRINTF("[Edit Identifier] Missing CHAIN_ID for Ethereum family!\n");
304 return false;
305 }
306 }
307 return true;
308}
309
316static void print_payload(const s_edit_ctx *context)
317{
318 UNUSED(context);
319 PRINTF("****************************************************************************\n");
320 PRINTF("[Edit Identifier] - Retrieved Descriptor:\n");
321 PRINTF("[Edit Identifier] - Contact name: %s\n", context->edit->identity.contact_name);
322 if (context->edit->identity.scope[0] != '\0') {
323 PRINTF("[Edit Identifier] - Scope: %s\n", context->edit->identity.scope);
324 }
325 PRINTF("[Edit Identifier] - New identifier len: %d\n",
326 context->edit->identity.identifier_len);
327 PRINTF("[Edit Identifier] - Old identifier len: %d\n", context->edit->old_identifier_len);
328 PRINTF("[Edit Identifier] - Blockchain family: %s\n",
329 FAMILY_AS_STR(context->edit->identity.blockchain_family));
330}
331
340static bool build_and_send_response(void)
341{
342 uint8_t hmac_rest[CX_SHA256_SIZE] = {0};
343
344 if (!address_book_compute_hmac_rest(g_ab_payload.edit_identifier.identity.gid,
345 g_ab_payload.edit_identifier.identity.scope,
346 g_ab_payload.edit_identifier.identity.identifier,
347 g_ab_payload.edit_identifier.identity.identifier_len,
348 g_ab_payload.edit_identifier.identity.blockchain_family,
349 g_ab_payload.edit_identifier.identity.chain_id,
350 hmac_rest)) {
351 PRINTF("[Edit Identifier] Error: Failed to compute new HMAC_REST\n");
352 explicit_bzero(hmac_rest, sizeof(hmac_rest));
353 return false;
354 }
355 bool ok = address_book_send_hmac_proof(TYPE_EDIT_IDENTIFIER, hmac_rest);
356 explicit_bzero(hmac_rest, sizeof(hmac_rest));
357 return ok;
358}
359
365static void review_choice(bool confirm)
366{
367 if (confirm) {
368 bool ok = build_and_send_response();
369 const char *successMsg
370 = (g_ab_payload.edit_identifier.identity.blockchain_family == FAMILY_BITCOIN)
371 ? "Identifier changed"
372 : "Address changed";
373 if (ok) {
374 on_edit_identifier_applied(&g_ab_payload.edit_identifier);
375 }
376 else {
377 PRINTF("[Edit Identifier] Error: Failed to build and send HMAC proof\n");
378 }
379 address_book_finalize_review(
380 ok, successMsg, "Error during update", finalize_ui_edit_identifier);
381 }
382 else {
383 address_book_handle_review_rejected(finalize_ui_edit_identifier);
384 }
385}
386
390static void ui_display(void)
391{
392 memset(&g_ab_ui.list, 0, sizeof(g_ab_ui.list));
393 g_ab_ui.list.nbPairs = 4; // name + scope + old identifier + new identifier
394 g_ab_ui.list.callback = get_edit_identifier_tagValue;
395 address_book_display_review(&LARGE_ADDRESS_BOOK_ICON,
396 "Review change to contact details",
397#ifdef SCREEN_SIZE_WALLET
398 "Confirm change?",
399#else
400 "Confirm change",
401#endif
402 review_choice);
403}
404
405/* Exported functions --------------------------------------------------------*/
406
414bolos_err_t edit_identifier(uint8_t *buffer_in, size_t buffer_in_length)
415{
416 const buffer_t payload = {.ptr = buffer_in, .size = buffer_in_length};
417 s_edit_ctx ctx = {0};
418
419 // Init the structure
420 ctx.edit = &g_ab_payload.edit_identifier;
421 memset(&g_ab_payload.edit_identifier, 0, sizeof(g_ab_payload.edit_identifier));
422
423 // Parse using SDK TLV parser
424 if (!edit_identifier_tlv_parser(&payload, &ctx, &ctx.received_tags)) {
425 PRINTF("[Edit Identifier] TLV parsing failed\n");
426 return SWO_INCORRECT_DATA;
427 }
428 if (!verify_fields(&ctx)) {
429 return SWO_INCORRECT_DATA;
430 }
431 print_payload(&ctx);
432
433 // Verify the group handle and extract the gid
434 if (!address_book_verify_group_handle(ctx.group_handle,
435 g_ab_payload.edit_identifier.identity.gid)) {
436 PRINTF("[Edit Identifier] Group handle verification failed\n");
437 return SWO_SECURITY_CONDITION_NOT_SATISFIED;
438 }
439
440 // Verify that the wallet holds a valid HMAC_PROOF for the contact name
441 if (!address_book_verify_hmac_proof(g_ab_payload.edit_identifier.identity.gid,
442 g_ab_payload.edit_identifier.identity.contact_name,
443 ctx.hmac_proof)) {
444 PRINTF("[Edit Identifier] HMAC_PROOF verification failed\n");
445 return SWO_SECURITY_CONDITION_NOT_SATISFIED;
446 }
447
448 // Verify that the wallet holds a valid HMAC_REST for the previous identifier
449 if (!address_book_verify_hmac_rest(g_ab_payload.edit_identifier.identity.gid,
450 g_ab_payload.edit_identifier.identity.scope,
451 g_ab_payload.edit_identifier.old_identifier,
452 g_ab_payload.edit_identifier.old_identifier_len,
453 g_ab_payload.edit_identifier.identity.blockchain_family,
454 g_ab_payload.edit_identifier.identity.chain_id,
455 ctx.hmac_rest)) {
456 PRINTF("[Edit Identifier] HMAC_REST verification failed\n");
457 return SWO_SECURITY_CONDITION_NOT_SATISFIED;
458 }
459
460 // Notify the coin app so it can validate and store display data
461 if (!handle_check_edit_identifier(&g_ab_payload.edit_identifier)) {
462 PRINTF("[Edit Identifier] Rejected by coin application\n");
463 return SWO_WRONG_PARAMETER_VALUE;
464 }
465
466 // Display confirmation UI
467 ui_display();
468 return SWO_NO_RESPONSE;
469}
470
471#endif // HAVE_ADDRESS_BOOK
@ FAMILY_BITCOIN
@ FAMILY_ETHEREUM
#define FAMILY_AS_STR(x)
Register / Edit Contact Name / Edit Scope / Edit Identifier.
API of the Advanced BOLOS Graphical Library, for typical application use-cases.
uint8_t * ptr
Definition buffer.h:22
size_t size
Pointer to byte buffer.
Definition buffer.h:23
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 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).
#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,...)
static bool handle_chain_id(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)