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(0x21, TAG_DERIVATION_PATH, handle_derivation_path, ENFORCE_UNIQUE_TAG) \
79 X(0x23, TAG_CHAIN_ID, handle_chain_id, ENFORCE_UNIQUE_TAG) \
80 X(0x29, TAG_HMAC_PROOF, handle_hmac_proof, ENFORCE_UNIQUE_TAG) \
81 X(0xf7, TAG_HMAC_REST, handle_hmac_rest, ENFORCE_UNIQUE_TAG) \
82 X(0x51, TAG_BLOCKCHAIN_FAMILY, handle_blockchain_family, ENFORCE_UNIQUE_TAG)
83
84/* Private variables ---------------------------------------------------------*/
85
86/* Private functions ---------------------------------------------------------*/
87
95static bool handle_struct_type(const tlv_data_t *data, s_edit_ctx *context)
96{
97 UNUSED(context);
98 if (!tlv_enforce_u8_value(data, TYPE_EDIT_IDENTIFIER)) {
99 PRINTF("[Edit Identifier] Invalid STRUCTURE_TYPE value\n");
100 return false;
101 }
102 return true;
103}
104
112static bool handle_struct_version(const tlv_data_t *data, s_edit_ctx *context)
113{
114 UNUSED(context);
115 if (!tlv_enforce_u8_value(data, STRUCT_VERSION)) {
116 PRINTF("[Edit Identifier] Invalid STRUCTURE_VERSION value\n");
117 return false;
118 }
119 return true;
120}
121
129static bool handle_contact_name(const tlv_data_t *data, s_edit_ctx *context)
130{
131 if (!address_book_handle_printable_string(data,
132 context->edit->identity.contact_name,
133 sizeof(context->edit->identity.contact_name))) {
134 PRINTF("[Edit Identifier] CONTACT_NAME: failed to parse\n");
135 return false;
136 }
137 return true;
138}
139
147static bool handle_scope(const tlv_data_t *data, s_edit_ctx *context)
148{
149 if (!address_book_handle_printable_string(
150 data, context->edit->identity.scope, sizeof(context->edit->identity.scope))) {
151 PRINTF("[Edit Identifier] SCOPE: failed to parse\n");
152 return false;
153 }
154 return true;
155}
156
164static bool handle_identifier(const tlv_data_t *data, s_edit_ctx *context)
165{
166 buffer_t buf = {0};
167 if (!get_buffer_from_tlv_data(data, &buf, 1, IDENTIFIER_MAX_LENGTH)) {
168 PRINTF("[Edit Identifier] IDENTIFIER: failed to extract\n");
169 return false;
170 }
171 memmove(context->edit->identity.identifier, buf.ptr, buf.size);
172 context->edit->identity.identifier_len = (uint8_t) buf.size;
173 return true;
174}
175
185static bool handle_previous_identifier(const tlv_data_t *data, s_edit_ctx *context)
186{
187 buffer_t buf = {0};
188 if (!get_buffer_from_tlv_data(data, &buf, 1, IDENTIFIER_MAX_LENGTH)) {
189 PRINTF("[Edit Identifier] PREVIOUS_IDENTIFIER: failed to extract\n");
190 return false;
191 }
192 memmove(context->edit->old_identifier, buf.ptr, buf.size);
193 context->edit->old_identifier_len = (uint8_t) buf.size;
194 return true;
195}
196
204static bool handle_group_handle(const tlv_data_t *data, s_edit_ctx *context)
205{
206 buffer_t buf = {0};
207 if (!get_buffer_from_tlv_data(data, &buf, GROUP_HANDLE_SIZE, GROUP_HANDLE_SIZE)) {
208 PRINTF("[Edit Identifier] GROUP_HANDLE: invalid length (expected %d bytes)\n",
209 GROUP_HANDLE_SIZE);
210 return false;
211 }
212 memmove(context->group_handle, buf.ptr, GROUP_HANDLE_SIZE);
213 return true;
214}
215
223static bool handle_derivation_path(const tlv_data_t *data, s_edit_ctx *context)
224{
225 return address_book_handle_derivation_path(data, &context->edit->identity.bip32_path);
226}
227
235static bool handle_chain_id(const tlv_data_t *data, s_edit_ctx *context)
236{
237 return address_book_handle_chain_id(data, &context->edit->identity.chain_id);
238}
239
247static bool handle_blockchain_family(const tlv_data_t *data, s_edit_ctx *context)
248{
249 return address_book_handle_blockchain_family(data, &context->edit->identity.blockchain_family);
250}
251
259static bool handle_hmac_proof(const tlv_data_t *data, s_edit_ctx *context)
260{
261 buffer_t buf = {0};
262 if (!get_buffer_from_tlv_data(data, &buf, CX_SHA256_SIZE, CX_SHA256_SIZE)) {
263 PRINTF("[Edit Identifier] HMAC_PROOF: invalid length (expected %d bytes)\n",
264 CX_SHA256_SIZE);
265 return false;
266 }
267 memmove(context->hmac_proof, buf.ptr, CX_SHA256_SIZE);
268 return true;
269}
270
278static bool handle_hmac_rest(const tlv_data_t *data, s_edit_ctx *context)
279{
280 buffer_t buf = {0};
281 if (!get_buffer_from_tlv_data(data, &buf, CX_SHA256_SIZE, CX_SHA256_SIZE)) {
282 PRINTF("[Edit Identifier] HMAC_REST: invalid length (expected %d bytes)\n", CX_SHA256_SIZE);
283 return false;
284 }
285 memmove(context->hmac_rest, buf.ptr, CX_SHA256_SIZE);
286 return true;
287}
288
289DEFINE_TLV_PARSER(EDIT_IDENTIFIER_TAGS, NULL, edit_identifier_tlv_parser)
290
291
297static bool verify_fields(const s_edit_ctx *context)
298{
299 bool result = TLV_CHECK_RECEIVED_TAGS(context->received_tags,
300 TAG_STRUCTURE_TYPE,
301 TAG_STRUCTURE_VERSION,
302 TAG_CONTACT_NAME,
303 TAG_ACCOUNT_IDENTIFIER,
304 TAG_PREVIOUS_IDENTIFIER,
305 TAG_GROUP_HANDLE,
306 TAG_DERIVATION_PATH,
307 TAG_BLOCKCHAIN_FAMILY,
308 TAG_HMAC_PROOF,
309 TAG_HMAC_REST);
310 if (!result) {
311 PRINTF("[Edit Identifier] Missing mandatory fields!\n");
312 return false;
313 }
314 if (context->edit->identity.blockchain_family == FAMILY_ETHEREUM) {
315 result = TLV_CHECK_RECEIVED_TAGS(context->received_tags, TAG_CHAIN_ID);
316 if (!result) {
317 PRINTF("[Edit Identifier] Missing CHAIN_ID for Ethereum family!\n");
318 return false;
319 }
320 }
321 return true;
322}
323
330static void print_payload(const s_edit_ctx *context)
331{
332 UNUSED(context);
333 PRINTF("****************************************************************************\n");
334 PRINTF("[Edit Identifier] - Retrieved Descriptor:\n");
335 PRINTF("[Edit Identifier] - Contact name: %s\n", context->edit->identity.contact_name);
336 if (context->edit->identity.scope[0] != '\0') {
337 PRINTF("[Edit Identifier] - Scope: %s\n", context->edit->identity.scope);
338 }
339 PRINTF("[Edit Identifier] - New identifier len: %d\n",
340 context->edit->identity.identifier_len);
341 PRINTF("[Edit Identifier] - Old identifier len: %d\n", context->edit->old_identifier_len);
342 PRINTF("[Edit Identifier] - Blockchain family: %s\n",
343 FAMILY_AS_STR(context->edit->identity.blockchain_family));
344}
345
354static bool build_and_send_response(void)
355{
356 uint8_t hmac_rest[CX_SHA256_SIZE] = {0};
357
358 if (!address_book_compute_hmac_rest(&g_ab_payload.edit_identifier.identity.bip32_path,
359 g_ab_payload.edit_identifier.identity.gid,
360 g_ab_payload.edit_identifier.identity.scope,
361 g_ab_payload.edit_identifier.identity.identifier,
362 g_ab_payload.edit_identifier.identity.identifier_len,
363 g_ab_payload.edit_identifier.identity.blockchain_family,
364 g_ab_payload.edit_identifier.identity.chain_id,
365 hmac_rest)) {
366 PRINTF("[Edit Identifier] Error: Failed to compute new HMAC_REST\n");
367 return false;
368 }
369
370 bool ok = address_book_send_hmac_proof(TYPE_EDIT_IDENTIFIER, hmac_rest);
371 explicit_bzero(hmac_rest, sizeof(hmac_rest));
372 return ok;
373}
374
380static void review_choice(bool confirm)
381{
382 if (confirm) {
383 bool ok = build_and_send_response();
384 const char *successMsg
385 = (g_ab_payload.edit_identifier.identity.blockchain_family == FAMILY_BITCOIN)
386 ? "Identifier changed"
387 : "Address changed";
388 if (ok) {
389 on_edit_identifier_applied(&g_ab_payload.edit_identifier);
390 }
391 else {
392 PRINTF("[Edit Identifier] Error: Failed to build and send HMAC proof\n");
393 }
394 address_book_finalize_review(
395 ok, successMsg, "Error during update", finalize_ui_edit_identifier);
396 }
397 else {
398 address_book_handle_review_rejected(finalize_ui_edit_identifier);
399 }
400}
401
405static void ui_display(void)
406{
407 memset(&g_ab_ui.list, 0, sizeof(g_ab_ui.list));
408 g_ab_ui.list.nbPairs = 4; // name + scope + old identifier + new identifier
409 g_ab_ui.list.callback = get_edit_identifier_tagValue;
410 address_book_display_review(&LARGE_ADDRESS_BOOK_ICON,
411 "Review change to contact details",
412#ifdef SCREEN_SIZE_WALLET
413 "Confirm change?",
414#else
415 "Confirm change",
416#endif
417 review_choice);
418}
419
420/* Exported functions --------------------------------------------------------*/
421
429bolos_err_t edit_identifier(uint8_t *buffer_in, size_t buffer_in_length)
430{
431 const buffer_t payload = {.ptr = buffer_in, .size = buffer_in_length};
432 s_edit_ctx ctx = {0};
433
434 // Init the structure
435 ctx.edit = &g_ab_payload.edit_identifier;
436 memset(&g_ab_payload.edit_identifier, 0, sizeof(g_ab_payload.edit_identifier));
437
438 // Parse using SDK TLV parser
439 if (!edit_identifier_tlv_parser(&payload, &ctx, &ctx.received_tags)) {
440 PRINTF("[Edit Identifier] TLV parsing failed\n");
441 return SWO_INCORRECT_DATA;
442 }
443 if (!verify_fields(&ctx)) {
444 return SWO_INCORRECT_DATA;
445 }
446 print_payload(&ctx);
447
448 // Verify the group handle and extract the gid
449 if (!address_book_verify_group_handle(&g_ab_payload.edit_identifier.identity.bip32_path,
450 ctx.group_handle,
451 g_ab_payload.edit_identifier.identity.gid)) {
452 PRINTF("[Edit Identifier] Group handle verification failed\n");
453 return SWO_SECURITY_CONDITION_NOT_SATISFIED;
454 }
455
456 // Verify that the wallet holds a valid HMAC_PROOF for the contact name
457 if (!address_book_verify_hmac_proof(&g_ab_payload.edit_identifier.identity.bip32_path,
458 g_ab_payload.edit_identifier.identity.gid,
459 g_ab_payload.edit_identifier.identity.contact_name,
460 ctx.hmac_proof)) {
461 PRINTF("[Edit Identifier] HMAC_PROOF verification failed\n");
462 return SWO_SECURITY_CONDITION_NOT_SATISFIED;
463 }
464
465 // Verify that the wallet holds a valid HMAC_REST for the previous identifier
466 if (!address_book_verify_hmac_rest(&g_ab_payload.edit_identifier.identity.bip32_path,
467 g_ab_payload.edit_identifier.identity.gid,
468 g_ab_payload.edit_identifier.identity.scope,
469 g_ab_payload.edit_identifier.old_identifier,
470 g_ab_payload.edit_identifier.old_identifier_len,
471 g_ab_payload.edit_identifier.identity.blockchain_family,
472 g_ab_payload.edit_identifier.identity.chain_id,
473 ctx.hmac_rest)) {
474 PRINTF("[Edit Identifier] HMAC_REST verification failed\n");
475 return SWO_SECURITY_CONDITION_NOT_SATISFIED;
476 }
477
478 // Notify the coin app so it can validate and store display data
479 if (!handle_check_edit_identifier(&g_ab_payload.edit_identifier)) {
480 PRINTF("[Edit Identifier] Rejected by coin application\n");
481 return SWO_WRONG_PARAMETER_VALUE;
482 }
483
484 // Display confirmation UI
485 ui_display();
486 return SWO_NO_RESPONSE;
487}
488
489#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