Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
ledger_account_edit.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
33/* Includes ------------------------------------------------------------------*/
34#include <string.h>
35#include "os_apdu.h"
36#include "status_words.h"
37#include "tlv_library.h"
38#include "buffer.h"
39#include "bip32.h"
40#include "address_book.h"
42#include "address_book_crypto.h"
43#include "address_book_common.h"
44#include "ledger_account.h"
45#include "io.h"
46#include "nbgl_use_case.h"
47
48#if defined(HAVE_ADDRESS_BOOK) && defined(HAVE_ADDRESS_BOOK_LEDGER_ACCOUNT)
49
50/* Private defines------------------------------------------------------------*/
51#define STRUCT_VERSION 0x01
52
53/* Private enumerations ------------------------------------------------------*/
54
55/* Private types, structures, unions -----------------------------------------*/
56
57typedef struct {
59 TLV_reception_t received_tags;
60 uint8_t hmac_proof[CX_SHA256_SIZE];
61} s_edit_ledger_account_ctx;
62
63/* Private macros-------------------------------------------------------------*/
64#define EDIT_LEDGER_ACCOUNT_TAGS(X) \
65 X(0x01, TAG_STRUCTURE_TYPE, handle_struct_type, ENFORCE_UNIQUE_TAG) \
66 X(0x02, TAG_STRUCTURE_VERSION, handle_struct_version, ENFORCE_UNIQUE_TAG) \
67 X(0xf0, TAG_CONTACT_NAME, handle_contact_name, ENFORCE_UNIQUE_TAG) \
68 X(0xf3, TAG_PREVIOUS_NAME, handle_previous_name, ENFORCE_UNIQUE_TAG) \
69 X(0x21, TAG_DERIVATION_PATH, handle_derivation_path, ENFORCE_UNIQUE_TAG) \
70 X(0x23, TAG_CHAIN_ID, handle_chain_id, ENFORCE_UNIQUE_TAG) \
71 X(0x29, TAG_HMAC_PROOF, handle_hmac_proof, ENFORCE_UNIQUE_TAG) \
72 X(0x51, TAG_BLOCKCHAIN_FAMILY, handle_blockchain_family, ENFORCE_UNIQUE_TAG)
73
74/* Private variables ---------------------------------------------------------*/
75
76/* Private functions ---------------------------------------------------------*/
77
85static bool handle_struct_type(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
86{
87 UNUSED(context);
88 if (!tlv_enforce_u8_value(data, TYPE_EDIT_LEDGER_ACCOUNT)) {
89 PRINTF("[Edit Ledger Account] Invalid STRUCTURE_TYPE value\n");
90 return false;
91 }
92 return true;
93}
94
102static bool handle_struct_version(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
103{
104 UNUSED(context);
105 if (!tlv_enforce_u8_value(data, STRUCT_VERSION)) {
106 PRINTF("[Edit Ledger Account] Invalid STRUCTURE_VERSION value\n");
107 return false;
108 }
109 return true;
110}
111
119static bool handle_contact_name(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
120{
121 if (!address_book_handle_printable_string(data,
122 context->edit->ledger_account.account_name,
123 sizeof(context->edit->ledger_account.account_name))) {
124 PRINTF("CONTACT_NAME: failed to parse\n");
125 return false;
126 }
127 return true;
128}
129
137static bool handle_previous_name(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
138{
139 if (!address_book_handle_printable_string(
140 data, context->edit->old_account_name, sizeof(context->edit->old_account_name))) {
141 PRINTF("PREVIOUS_NAME: failed to parse\n");
142 return false;
143 }
144 return true;
145}
146
154static bool handle_derivation_path(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
155{
156 return address_book_handle_derivation_path(data, &context->edit->ledger_account.bip32_path);
157}
158
166static bool handle_chain_id(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
167{
168 return address_book_handle_chain_id(data, &context->edit->ledger_account.chain_id);
169}
170
178static bool handle_blockchain_family(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
179{
180 return address_book_handle_blockchain_family(data,
181 &context->edit->ledger_account.blockchain_family);
182}
183
191static bool handle_hmac_proof(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
192{
193 buffer_t buf = {0};
194 if (!get_buffer_from_tlv_data(data, &buf, CX_SHA256_SIZE, CX_SHA256_SIZE)) {
195 PRINTF("[Edit Ledger Account] HMAC_PROOF: invalid length (expected %d bytes)\n",
196 CX_SHA256_SIZE);
197 return false;
198 }
199 memmove(context->hmac_proof, buf.ptr, CX_SHA256_SIZE);
200 return true;
201}
202
203DEFINE_TLV_PARSER(EDIT_LEDGER_ACCOUNT_TAGS, NULL, edit_ledger_account_tlv_parser)
204
205
211static bool verify_fields(const s_edit_ledger_account_ctx *context)
212{
213 bool result = TLV_CHECK_RECEIVED_TAGS(context->received_tags,
214 TAG_STRUCTURE_TYPE,
215 TAG_STRUCTURE_VERSION,
216 TAG_CONTACT_NAME,
217 TAG_PREVIOUS_NAME,
218 TAG_DERIVATION_PATH,
219 TAG_BLOCKCHAIN_FAMILY,
220 TAG_HMAC_PROOF);
221 if (!result) {
222 PRINTF("Missing mandatory fields in Edit Ledger Account descriptor!\n");
223 return false;
224 }
225 if (context->edit->ledger_account.blockchain_family == FAMILY_ETHEREUM) {
226 result = TLV_CHECK_RECEIVED_TAGS(context->received_tags, TAG_CHAIN_ID);
227 if (!result) {
228 PRINTF("Missing CHAIN_ID for Ethereum family in Edit Ledger Account descriptor!\n");
229 return false;
230 }
231 }
232 return true;
233}
234
241static void print_payload(const s_edit_ledger_account_ctx *context)
242{
243 UNUSED(context);
244 char out[50] = {0};
245
246 PRINTF("****************************************************************************\n");
247 PRINTF("[Edit Ledger Account] - Retrieved Descriptor:\n");
248 PRINTF("[Edit Ledger Account] - Old name: %s\n", context->edit->old_account_name);
249 PRINTF("[Edit Ledger Account] - New name: %s\n",
250 context->edit->ledger_account.account_name);
251 if (bip32_path_format_simple(&context->edit->ledger_account.bip32_path, out, sizeof(out))) {
252 PRINTF("[Edit Ledger Account] - Derivation path[%d]: %s\n",
253 context->edit->ledger_account.bip32_path.length,
254 out);
255 }
256 else {
257 PRINTF("[Edit Ledger Account] - Derivation path length[%d] (failed to format)\n",
258 context->edit->ledger_account.bip32_path.length);
259 }
260 PRINTF("[Edit Ledger Account] - Blockchain family: %s\n",
261 FAMILY_AS_STR(context->edit->ledger_account.blockchain_family));
262 if (context->edit->ledger_account.blockchain_family == FAMILY_ETHEREUM) {
263 PRINTF("[Edit Ledger Account] - Chain ID: %llu\n",
264 context->edit->ledger_account.chain_id);
265 }
266}
267
276static bool build_and_send_response(void)
277{
278 uint8_t hmac_proof[CX_SHA256_SIZE] = {0};
279
280 if (!address_book_compute_hmac_proof_ledger_account(
281 &g_ab_payload.edit_ledger_account.ledger_account.bip32_path,
282 (const char *) g_ab_payload.edit_ledger_account.ledger_account.account_name,
283 g_ab_payload.edit_ledger_account.ledger_account.blockchain_family,
284 g_ab_payload.edit_ledger_account.ledger_account.chain_id,
285 hmac_proof)) {
286 PRINTF("[Edit Ledger Account] Error: Failed to compute new HMAC proof\n");
287 return false;
288 }
289
290 bool ok = address_book_send_hmac_proof(TYPE_EDIT_LEDGER_ACCOUNT, hmac_proof);
291 explicit_bzero(hmac_proof, sizeof(hmac_proof));
292 return ok;
293}
294
300static void review_choice(bool confirm)
301{
302 if (confirm) {
303 bool ok = build_and_send_response();
304 if (ok) {
305 on_edit_ledger_account_applied(&g_ab_payload.edit_ledger_account);
306 }
307 else {
308 PRINTF("[Edit Ledger Account] Error: Failed to build and send HMAC proof\n");
309 }
310 address_book_finalize_review(
311 ok, "Account name changed", "Error during update", finalize_ui_edit_ledger_account);
312 }
313 else {
314 address_book_handle_review_rejected(finalize_ui_edit_ledger_account);
315 }
316}
317
321static void ui_display(void)
322{
323 uint8_t nbPairs = 0;
324 memset(&g_ab_ui.list, 0, sizeof(g_ab_ui.list));
325 memset(g_ab_ui.pairs, 0, sizeof(g_ab_ui.pairs));
326 g_ab_ui.pairs[nbPairs].item = "Old name";
327 g_ab_ui.pairs[nbPairs].value = g_ab_payload.edit_ledger_account.old_account_name;
328 nbPairs++;
329 g_ab_ui.pairs[nbPairs].item = "New name";
330 g_ab_ui.pairs[nbPairs].value = g_ab_payload.edit_ledger_account.ledger_account.account_name;
331 nbPairs++;
332 g_ab_ui.list.pairs = g_ab_ui.pairs;
333 g_ab_ui.list.nbPairs = nbPairs;
334 address_book_display_review(NULL,
335 "Edit account name",
336#ifdef SCREEN_SIZE_WALLET
337 "Confirm edit?",
338#else
339 "Confirm edit",
340#endif
341 review_choice);
342}
343
344/* Exported functions --------------------------------------------------------*/
345
353bolos_err_t edit_ledger_account(uint8_t *buffer_in, size_t buffer_in_length)
354{
355 const buffer_t payload = {.ptr = buffer_in, .size = buffer_in_length};
356 s_edit_ledger_account_ctx ctx = {0};
357
358 // Init the structure
359 ctx.edit = &g_ab_payload.edit_ledger_account;
360 memset(&g_ab_payload.edit_ledger_account, 0, sizeof(g_ab_payload.edit_ledger_account));
361
362 // Parse using SDK TLV parser
363 if (!edit_ledger_account_tlv_parser(&payload, &ctx, &ctx.received_tags)) {
364 PRINTF("[Edit Ledger Account] TLV parsing failed\n");
365 return SWO_INCORRECT_DATA;
366 }
367 if (!verify_fields(&ctx)) {
368 return SWO_INCORRECT_DATA;
369 }
370 print_payload(&ctx);
371
372 // Verify that the host holds a valid proof from the previous registration
373 if (!address_book_verify_hmac_proof_ledger_account(
374 &g_ab_payload.edit_ledger_account.ledger_account.bip32_path,
375 g_ab_payload.edit_ledger_account.old_account_name,
376 g_ab_payload.edit_ledger_account.ledger_account.blockchain_family,
377 g_ab_payload.edit_ledger_account.ledger_account.chain_id,
378 ctx.hmac_proof)) {
379 PRINTF("[Edit Ledger Account] HMAC proof verification failed\n");
380 return SWO_SECURITY_CONDITION_NOT_SATISFIED;
381 }
382
383 // Coin-app validation: derive and cache the address
384 if (!handle_check_edit_ledger_account(&g_ab_payload.edit_ledger_account)) {
385 PRINTF("[Edit Ledger Account] Coin-app rejected edit\n");
386 return SWO_INCORRECT_DATA;
387 }
388
389 // Display confirmation UI
390 ui_display();
391 return SWO_NO_RESPONSE;
392}
393
394#endif // HAVE_ADDRESS_BOOK && HAVE_ADDRESS_BOOK_LEDGER_ACCOUNT
@ FAMILY_ETHEREUM
#define FAMILY_AS_STR(x)
bool bip32_path_format_simple(path_bip32_t *bip32, char *out, size_t out_len)
Format a BIP32 path as a string.
Definition bip32.c:92
#define TYPE_EDIT_LEDGER_ACCOUNT
bolos_err_t edit_ledger_account(uint8_t *buffer_in, size_t buffer_in_length)
API of the Advanced BOLOS Graphical Library, for typical application use-cases.
uint8_t * ptr
Definition buffer.h:22
Data extracted from a Rename Ledger Account TLV payload.