Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
ledger_account_register.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
31/* Includes ------------------------------------------------------------------*/
32#include <string.h>
33#include "os_utils.h"
34#include "os_apdu.h"
35#include "os_pic.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 {
58 ledger_account_t *ledger_account;
59 TLV_reception_t received_tags;
60} s_ledger_account_ctx;
61
62/* Private macros-------------------------------------------------------------*/
63#define LEDGER_ACCOUNT_TAGS(X) \
64 X(0x01, TAG_STRUCTURE_TYPE, handle_struct_type, ENFORCE_UNIQUE_TAG) \
65 X(0x02, TAG_STRUCTURE_VERSION, handle_struct_version, ENFORCE_UNIQUE_TAG) \
66 X(0xf0, TAG_CONTACT_NAME, handle_contact_name, ENFORCE_UNIQUE_TAG) \
67 X(0x21, TAG_DERIVATION_PATH, handle_derivation_path, ENFORCE_UNIQUE_TAG) \
68 X(0x23, TAG_CHAIN_ID, handle_chain_id, ENFORCE_UNIQUE_TAG) \
69 X(0x51, TAG_BLOCKCHAIN_FAMILY, handle_blockchain_family, ENFORCE_UNIQUE_TAG)
70
71/* Private variables ---------------------------------------------------------*/
72
73/* Private functions ---------------------------------------------------------*/
74
82static bool handle_struct_type(const tlv_data_t *data, s_ledger_account_ctx *context)
83{
84 UNUSED(context);
85 if (!tlv_enforce_u8_value(data, TYPE_REGISTER_LEDGER_ACCOUNT)) {
86 PRINTF("[Register Ledger Account] Invalid STRUCTURE_TYPE value\n");
87 return false;
88 }
89 return true;
90}
91
99static bool handle_struct_version(const tlv_data_t *data, s_ledger_account_ctx *context)
100{
101 UNUSED(context);
102 if (!tlv_enforce_u8_value(data, STRUCT_VERSION)) {
103 PRINTF("[Register Ledger Account] Invalid STRUCTURE_VERSION value\n");
104 return false;
105 }
106 return true;
107}
108
116static bool handle_contact_name(const tlv_data_t *data, s_ledger_account_ctx *context)
117{
118 if (!address_book_handle_printable_string(data,
119 context->ledger_account->account_name,
120 sizeof(context->ledger_account->account_name))) {
121 PRINTF("CONTACT_NAME: failed to parse\n");
122 return false;
123 }
124 return true;
125}
126
134static bool handle_derivation_path(const tlv_data_t *data, s_ledger_account_ctx *context)
135{
136 return address_book_handle_derivation_path(data, &context->ledger_account->bip32_path);
137}
138
146static bool handle_chain_id(const tlv_data_t *data, s_ledger_account_ctx *context)
147{
148 return address_book_handle_chain_id(data, &context->ledger_account->chain_id);
149}
150
158static bool handle_blockchain_family(const tlv_data_t *data, s_ledger_account_ctx *context)
159{
160 return address_book_handle_blockchain_family(data, &context->ledger_account->blockchain_family);
161}
162
163DEFINE_TLV_PARSER(LEDGER_ACCOUNT_TAGS, NULL, ledger_account_tlv_parser)
164
165
171static bool verify_fields(const s_ledger_account_ctx *context)
172{
173 bool result = TLV_CHECK_RECEIVED_TAGS(context->received_tags,
174 TAG_STRUCTURE_TYPE,
175 TAG_STRUCTURE_VERSION,
176 TAG_CONTACT_NAME,
177 TAG_DERIVATION_PATH,
178 TAG_BLOCKCHAIN_FAMILY);
179 if (!result) {
180 PRINTF("Missing mandatory fields in Ledger Account descriptor!\n");
181 return false;
182 }
183 if (context->ledger_account->blockchain_family == FAMILY_ETHEREUM) {
184 result = TLV_CHECK_RECEIVED_TAGS(context->received_tags, TAG_CHAIN_ID);
185 if (!result) {
186 PRINTF("Missing CHAIN_ID for Ethereum family in Ledger Account descriptor!\n");
187 return false;
188 }
189 }
190 return true;
191}
192
199static void print_payload(const s_ledger_account_ctx *context)
200{
201 UNUSED(context);
202 char out[50] = {0};
203
204 PRINTF("****************************************************************************\n");
205 PRINTF("[Ledger Account] - Retrieved Descriptor:\n");
206 PRINTF("[Ledger Account] - Account name: %s\n",
207 context->ledger_account->account_name);
208 if (bip32_path_format_simple(&context->ledger_account->bip32_path, out, sizeof(out))) {
209 PRINTF("[Ledger Account] - Derivation path[%d]: %s\n",
210 context->ledger_account->bip32_path.length,
211 out);
212 }
213 else {
214 PRINTF("[Ledger Account] - Derivation path length[%d] (failed to format)\n",
215 context->ledger_account->bip32_path.length);
216 }
217 PRINTF("[Ledger Account] - Blockchain family: %s\n",
218 FAMILY_AS_STR(context->ledger_account->blockchain_family));
219 if (context->ledger_account->blockchain_family == FAMILY_ETHEREUM) {
220 PRINTF("[Ledger Account] - Chain ID: %llu\n",
221 context->ledger_account->chain_id);
222 }
223}
224
233static bool build_and_send_response(void)
234{
235 uint8_t hmac_proof[CX_SHA256_SIZE] = {0};
236
237 if (!address_book_compute_hmac_proof_ledger_account(
238 &g_ab_payload.ledger_account.bip32_path,
239 (const char *) g_ab_payload.ledger_account.account_name,
240 g_ab_payload.ledger_account.blockchain_family,
241 g_ab_payload.ledger_account.chain_id,
242 hmac_proof)) {
243 PRINTF("[Ledger Account] Error: Failed to compute HMAC proof\n");
244 return false;
245 }
246
247 bool ok = address_book_send_hmac_proof(TYPE_REGISTER_LEDGER_ACCOUNT, hmac_proof);
248 explicit_bzero(hmac_proof, sizeof(hmac_proof));
249 return ok;
250}
251
257static void review_choice(bool confirm)
258{
259 if (confirm) {
260 bool ok = build_and_send_response();
261 if (!ok) {
262 PRINTF("[Ledger Account] Error: Failed to build and send response\n");
263 }
264 address_book_finalize_review(
265 ok, "Name confirmed", "Error saving account", finalize_ui_register_ledger_account);
266 }
267 else {
268 address_book_handle_review_rejected(finalize_ui_register_ledger_account);
269 }
270}
271
272/* Exported functions --------------------------------------------------------*/
273
281bolos_err_t register_ledger_account(uint8_t *buffer_in, size_t buffer_in_length)
282{
283 const buffer_t payload = {.ptr = buffer_in, .size = buffer_in_length};
284 s_ledger_account_ctx ctx = {0};
285
286 // Init the structure
287 ctx.ledger_account = &g_ab_payload.ledger_account;
288 memset(&g_ab_payload.ledger_account, 0, sizeof(g_ab_payload.ledger_account));
289
290 // Parse using SDK TLV parser
291 if (!ledger_account_tlv_parser(&payload, &ctx, &ctx.received_tags)) {
292 PRINTF("[Ledger Account] Failed to parse TLV payload\n");
293 return SWO_INCORRECT_DATA;
294 }
295 if (!verify_fields(&ctx)) {
296 return SWO_INCORRECT_DATA;
297 }
298 print_payload(&ctx);
299
300 // Check the account validity according to the Coin application logic
301 if (!handle_check_register_ledger_account(ctx.ledger_account)) {
302 PRINTF("[Ledger Account] Error: Account rejected by coin application\n");
303 return SWO_WRONG_PARAMETER_VALUE;
304 }
305
306 // Display confirmation UI (delegated to coin app)
307 display_register_ledger_account_review(review_choice);
308 return SWO_NO_RESPONSE;
309}
310
311#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_REGISTER_LEDGER_ACCOUNT
bolos_err_t register_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 Register Ledger Account TLV payload.