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
34/* Includes ------------------------------------------------------------------*/
35#include <string.h>
36#include "os_apdu.h"
37#include "status_words.h"
38#include "tlv_library.h"
39#include "buffer.h"
40#include "bip32.h"
41#include "address_book.h"
43#include "address_book_crypto.h"
44#include "address_book_common.h"
45#include "ledger_account.h"
46#include "io.h"
47#include "nbgl_use_case.h"
48
49#if defined(HAVE_ADDRESS_BOOK) && defined(HAVE_ADDRESS_BOOK_LEDGER_ACCOUNT)
50
51/* Private defines------------------------------------------------------------*/
52#define STRUCT_VERSION 0x01
53
54/* Private enumerations ------------------------------------------------------*/
55
56/* Private types, structures, unions -----------------------------------------*/
57
58typedef struct {
60 TLV_reception_t received_tags;
61 uint8_t hmac_proof[CX_SHA256_SIZE];
62} s_edit_ledger_account_ctx;
63
64/* Private macros-------------------------------------------------------------*/
65#define EDIT_LEDGER_ACCOUNT_TAGS(X) \
66 X(0x01, TAG_STRUCTURE_TYPE, handle_struct_type, ENFORCE_UNIQUE_TAG) \
67 X(0x02, TAG_STRUCTURE_VERSION, handle_struct_version, ENFORCE_UNIQUE_TAG) \
68 X(0xf0, TAG_CONTACT_NAME, handle_contact_name, ENFORCE_UNIQUE_TAG) \
69 X(0xf3, TAG_PREVIOUS_NAME, handle_previous_name, ENFORCE_UNIQUE_TAG) \
70 X(0x69, TAG_DERIVATION_PATH, handle_derivation_path, ENFORCE_UNIQUE_TAG) \
71 X(0x23, TAG_CHAIN_ID, handle_chain_id, ENFORCE_UNIQUE_TAG) \
72 X(0x29, TAG_HMAC_PROOF, handle_hmac_proof, ENFORCE_UNIQUE_TAG) \
73 X(0x51, TAG_BLOCKCHAIN_FAMILY, handle_blockchain_family, ENFORCE_UNIQUE_TAG)
74
75/* Private variables ---------------------------------------------------------*/
76
77/* Private functions ---------------------------------------------------------*/
78
86static bool handle_struct_type(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
87{
88 UNUSED(context);
90 PRINTF("[Edit Ledger Account] Invalid STRUCTURE_TYPE value\n");
91 return false;
92 }
93 return true;
94}
95
103static bool handle_struct_version(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
104{
105 UNUSED(context);
106 if (!tlv_enforce_u8_value(data, STRUCT_VERSION)) {
107 PRINTF("[Edit Ledger Account] Invalid STRUCTURE_VERSION value\n");
108 return false;
109 }
110 return true;
111}
112
120static bool handle_contact_name(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
121{
122 if (!address_book_handle_printable_string(data,
123 context->edit->ledger_account.account_name,
124 sizeof(context->edit->ledger_account.account_name))) {
125 PRINTF("CONTACT_NAME: failed to parse\n");
126 return false;
127 }
128 return true;
129}
130
138static bool handle_previous_name(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
139{
140 if (!address_book_handle_printable_string(
141 data, context->edit->old_account_name, sizeof(context->edit->old_account_name))) {
142 PRINTF("PREVIOUS_NAME: failed to parse\n");
143 return false;
144 }
145 return true;
146}
147
155static bool handle_derivation_path(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
156{
157 return address_book_handle_derivation_path(data, &context->edit->ledger_account.bip32_path);
158}
159
167static bool handle_chain_id(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
168{
169 return address_book_handle_chain_id(data, &context->edit->ledger_account.chain_id);
170}
171
179static bool handle_blockchain_family(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
180{
181 return address_book_handle_blockchain_family(data,
182 &context->edit->ledger_account.blockchain_family);
183}
184
192static bool handle_hmac_proof(const tlv_data_t *data, s_edit_ledger_account_ctx *context)
193{
194 buffer_t buf = {0};
195 if (!get_buffer_from_tlv_data(data, &buf, CX_SHA256_SIZE, CX_SHA256_SIZE)) {
196 PRINTF("[Edit Ledger Account] HMAC_PROOF: invalid length (expected %d bytes)\n",
197 CX_SHA256_SIZE);
198 return false;
199 }
200 memmove(context->hmac_proof, buf.ptr, CX_SHA256_SIZE);
201 return true;
202}
203
204DEFINE_TLV_PARSER(EDIT_LEDGER_ACCOUNT_TAGS, NULL, edit_ledger_account_tlv_parser)
205
206
212static bool verify_fields(const s_edit_ledger_account_ctx *context)
213{
214 bool result = TLV_CHECK_RECEIVED_TAGS(context->received_tags,
215 TAG_STRUCTURE_TYPE,
216 TAG_STRUCTURE_VERSION,
217 TAG_CONTACT_NAME,
218 TAG_PREVIOUS_NAME,
219 TAG_DERIVATION_PATH,
220 TAG_BLOCKCHAIN_FAMILY,
221 TAG_HMAC_PROOF);
222 if (!result) {
223 PRINTF("Missing mandatory fields in Edit Ledger Account descriptor!\n");
224 return false;
225 }
226 if (context->edit->ledger_account.blockchain_family == FAMILY_ETHEREUM) {
227 result = TLV_CHECK_RECEIVED_TAGS(context->received_tags, TAG_CHAIN_ID);
228 if (!result) {
229 PRINTF("Missing CHAIN_ID for Ethereum family in Edit Ledger Account descriptor!\n");
230 return false;
231 }
232 }
233 return true;
234}
235
242static void print_payload(const s_edit_ledger_account_ctx *context)
243{
244 UNUSED(context);
245 char out[50] = {0};
246
247 PRINTF("****************************************************************************\n");
248 PRINTF("[Edit Ledger Account] - Retrieved Descriptor:\n");
249 PRINTF("[Edit Ledger Account] - Old name: %s\n", context->edit->old_account_name);
250 PRINTF("[Edit Ledger Account] - New name: %s\n",
251 context->edit->ledger_account.account_name);
252 if (bip32_path_format_simple(&context->edit->ledger_account.bip32_path, out, sizeof(out))) {
253 PRINTF("[Edit Ledger Account] - Derivation path[%d]: %s\n",
254 context->edit->ledger_account.bip32_path.length,
255 out);
256 }
257 else {
258 PRINTF("[Edit Ledger Account] - Derivation path length[%d] (failed to format)\n",
259 context->edit->ledger_account.bip32_path.length);
260 }
261 PRINTF("[Edit Ledger Account] - Blockchain family: %s\n",
262 FAMILY_AS_STR(context->edit->ledger_account.blockchain_family));
263 if (context->edit->ledger_account.blockchain_family == FAMILY_ETHEREUM) {
264 PRINTF("[Edit Ledger Account] - Chain ID: %llu\n",
265 context->edit->ledger_account.chain_id);
266 }
267}
268
277static bool build_and_send_response(void)
278{
279 uint8_t hmac_proof[CX_SHA256_SIZE] = {0};
280
281 if (!address_book_compute_hmac_proof_ledger_account(
282 &g_ab_payload.edit_ledger_account.ledger_account.bip32_path,
283 (const char *) g_ab_payload.edit_ledger_account.ledger_account.account_name,
284 g_ab_payload.edit_ledger_account.ledger_account.blockchain_family,
285 g_ab_payload.edit_ledger_account.ledger_account.chain_id,
286 hmac_proof)) {
287 PRINTF("[Edit Ledger Account] Error: Failed to compute new HMAC proof\n");
288 return false;
289 }
290
291 bool ok = address_book_send_hmac_proof(TYPE_EDIT_LEDGER_ACCOUNT, hmac_proof);
292 explicit_bzero(hmac_proof, sizeof(hmac_proof));
293 return ok;
294}
295
301static void review_choice(bool confirm)
302{
303 if (confirm) {
304 bool ok = build_and_send_response();
305 if (ok) {
306 on_edit_ledger_account_applied(&g_ab_payload.edit_ledger_account);
307 }
308 else {
309 PRINTF("[Edit Ledger Account] Error: Failed to build and send HMAC proof\n");
310 }
311 address_book_finalize_review(
312 ok, "Account name changed", "Error during update", finalize_ui_ledger_account);
313 }
314 else {
315 address_book_handle_review_rejected(finalize_ui_ledger_account);
316 }
317}
318
319/* Exported functions --------------------------------------------------------*/
320
328bolos_err_t edit_ledger_account(uint8_t *buffer_in, size_t buffer_in_length)
329{
330 const buffer_t payload = {.ptr = buffer_in, .size = buffer_in_length};
331 s_edit_ledger_account_ctx ctx = {0};
332
333 // Init the structure
334 ctx.edit = &g_ab_payload.edit_ledger_account;
335 memset(&g_ab_payload.edit_ledger_account, 0, sizeof(g_ab_payload.edit_ledger_account));
336
337 // Parse using SDK TLV parser
338 if (!edit_ledger_account_tlv_parser(&payload, &ctx, &ctx.received_tags)) {
339 PRINTF("[Edit Ledger Account] TLV parsing failed\n");
340 return SWO_INCORRECT_DATA;
341 }
342 if (!verify_fields(&ctx)) {
343 return SWO_INCORRECT_DATA;
344 }
345 print_payload(&ctx);
346
347 // Verify that the host holds a valid proof from the previous registration
348 if (!address_book_verify_hmac_proof_ledger_account(
349 &g_ab_payload.edit_ledger_account.ledger_account.bip32_path,
350 g_ab_payload.edit_ledger_account.old_account_name,
351 g_ab_payload.edit_ledger_account.ledger_account.blockchain_family,
352 g_ab_payload.edit_ledger_account.ledger_account.chain_id,
353 ctx.hmac_proof)) {
354 PRINTF("[Edit Ledger Account] HMAC proof verification failed\n");
355 return SWO_SECURITY_CONDITION_NOT_SATISFIED;
356 }
357
358 // Coin-app validation: derive and cache the address
359 if (!handle_check_edit_ledger_account(&g_ab_payload.edit_ledger_account)) {
360 PRINTF("[Edit Ledger Account] Coin-app rejected edit\n");
361 return SWO_INCORRECT_DATA;
362 }
363
364 // Display confirmation UI — reuse the Register Ledger Account review
365 display_register_ledger_account_review(review_choice);
366 return SWO_NO_RESPONSE;
367}
368
369#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.
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)