Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
address_book_crypto.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
29#include <string.h>
30#include <stdint.h>
31#include "address_book_crypto.h"
32#include "ledger_account.h"
33#include "identity.h"
34#include "lcx_sha256.h"
35#include "lcx_rng.h"
36#include "os_utils.h"
37#include "os_address_book.h"
38#include "io.h"
39
40#ifdef HAVE_ADDRESS_BOOK
41
49#define HMAC_MSG_MAX_SIZE \
50 (GID_SIZE + 1U + (SCOPE_LENGTH - 1U) + 1U + IDENTIFIER_MAX_LENGTH + 1U + 8U)
51
53#define HMAC_PROOF_MSG_SIZE (GID_SIZE + 1U + (CONTACT_NAME_LENGTH - 1U))
54
56#define HMAC_LEDGER_MSG_SIZE (1U + (ACCOUNT_NAME_LENGTH - 1U) + 1U + 8U)
57
58/* Private functions ---------------------------------------------------------*/
59
81static size_t serialize_hmac_msg(uint8_t *msg,
82 size_t msg_size,
83 const uint8_t *gid,
84 const char *str1,
85 const uint8_t *raw,
86 uint8_t raw_len,
87 bool include_family,
89 uint64_t chain_id)
90{
91 size_t offset = 0;
92
93 // Optional 32-byte GID prefix
94 if (gid != NULL) {
95 if (offset + GID_SIZE > msg_size) {
96 return SIZE_MAX;
97 }
98 memmove(&msg[offset], gid, GID_SIZE);
99 offset += GID_SIZE;
100 }
101
102 // Optional length-prefixed string
103 if (str1 != NULL) {
104 uint8_t len = (uint8_t) strlen(str1);
105 if (offset + 1U + (size_t) len > msg_size) {
106 return SIZE_MAX;
107 }
108 msg[offset++] = len;
109 if (len > 0) {
110 memmove(&msg[offset], str1, len);
111 offset += len;
112 }
113 }
114
115 // Optional length-prefixed raw bytes
116 if (raw != NULL) {
117 if (offset + 1U + (size_t) raw_len > msg_size) {
118 return SIZE_MAX;
119 }
120 msg[offset++] = raw_len;
121 memmove(&msg[offset], raw, raw_len);
122 offset += raw_len;
123 }
124
125 // Optional family byte + chain_id
126 if (include_family) {
127 if (offset + 1U > msg_size) {
128 return SIZE_MAX;
129 }
130 msg[offset++] = (uint8_t) family;
131 if (family == FAMILY_ETHEREUM) {
132 if (offset + 8U > msg_size) {
133 return SIZE_MAX;
134 }
135 U8BE_ENCODE(msg, offset, chain_id);
136 offset += 8U;
137 }
138 }
139
140 return offset;
141}
142
143/* Exported functions --------------------------------------------------------*/
144
154bool address_book_send_hmac_proof(uint8_t type, const uint8_t hmac_proof[CX_SHA256_SIZE])
155{
156 size_t tx = 0;
157
158 // Response format: type(1) | hmac(32)
159 G_io_tx_buffer[tx++] = type;
160 memmove(&G_io_tx_buffer[tx], hmac_proof, CX_SHA256_SIZE);
161 tx += CX_SHA256_SIZE;
162
163 io_send_response_pointer(G_io_tx_buffer, tx, SWO_SUCCESS);
164 return true;
165}
166
178bool address_book_send_register_identity_response(const uint8_t group_handle[GROUP_HANDLE_SIZE],
179 const uint8_t hmac_proof[CX_SHA256_SIZE],
180 const uint8_t hmac_rest[CX_SHA256_SIZE])
181{
182 size_t tx = 0;
183
184 G_io_tx_buffer[tx++] = TYPE_REGISTER_IDENTITY;
185 memmove(&G_io_tx_buffer[tx], group_handle, GROUP_HANDLE_SIZE);
186 tx += GROUP_HANDLE_SIZE;
187 memmove(&G_io_tx_buffer[tx], hmac_proof, CX_SHA256_SIZE);
188 tx += CX_SHA256_SIZE;
189 memmove(&G_io_tx_buffer[tx], hmac_rest, CX_SHA256_SIZE);
190 tx += CX_SHA256_SIZE;
191
192 io_send_response_pointer(G_io_tx_buffer, tx, SWO_SUCCESS);
193 return true;
194}
195
206bool address_book_generate_group_handle(const path_bip32_t *bip32_path,
207 uint8_t group_handle[GROUP_HANDLE_SIZE])
208{
209 bool success = false;
210 uint8_t *gid = group_handle;
211 uint8_t *mac = group_handle + GID_SIZE;
212
213 cx_rng_no_throw(gid, GID_SIZE);
214 if (!sys_address_book_hmac(
215 bip32_path->path, bip32_path->length, ADDRESS_BOOK_SALT_GROUP, gid, GID_SIZE, mac)) {
216 goto end;
217 }
218 success = true;
219
220end:
221 return success;
222}
223
235bool address_book_verify_group_handle(const path_bip32_t *bip32_path,
236 const uint8_t group_handle[GROUP_HANDLE_SIZE],
237 uint8_t gid_out[GID_SIZE])
238{
239 bool success = false;
240 const uint8_t *gid = group_handle;
241 const uint8_t *mac = group_handle + GID_SIZE;
242
243 if (!sys_address_book_hmac_verify(
244 bip32_path->path, bip32_path->length, ADDRESS_BOOK_SALT_GROUP, gid, GID_SIZE, mac)) {
245 PRINTF("[Address Book] Group handle MAC verification failed\n");
246 goto end;
247 }
248 memmove(gid_out, gid, GID_SIZE);
249 success = true;
250
251end:
252 return success;
253}
254
267bool address_book_compute_hmac_proof(const path_bip32_t *bip32_path,
268 const uint8_t gid[GID_SIZE],
269 const char *name,
270 uint8_t hmac_out[CX_SHA256_SIZE])
271{
272 uint8_t msg[HMAC_PROOF_MSG_SIZE] = {0};
273 bool success = false;
274 size_t msg_len = serialize_hmac_msg(msg, sizeof(msg), gid, name, NULL, 0, false, 0, 0);
275
276 if (msg_len == SIZE_MAX) {
277 goto end;
278 }
279 if (!sys_address_book_hmac(bip32_path->path,
280 bip32_path->length,
281 ADDRESS_BOOK_SALT_IDENTITY,
282 msg,
283 msg_len,
284 hmac_out)) {
285 goto end;
286 }
287 success = true;
288
289end:
290 explicit_bzero(msg, sizeof(msg));
291 return success;
292}
293
303bool address_book_verify_hmac_proof(const path_bip32_t *bip32_path,
304 const uint8_t gid[GID_SIZE],
305 const char *name,
306 const uint8_t hmac_expected[CX_SHA256_SIZE])
307{
308 uint8_t msg[HMAC_PROOF_MSG_SIZE] = {0};
309 bool success = false;
310 size_t msg_len = serialize_hmac_msg(msg, sizeof(msg), gid, name, NULL, 0, false, 0, 0);
311
312 if (msg_len == SIZE_MAX) {
313 goto end;
314 }
315 if (!sys_address_book_hmac_verify(bip32_path->path,
316 bip32_path->length,
317 ADDRESS_BOOK_SALT_IDENTITY,
318 msg,
319 msg_len,
320 hmac_expected)) {
321 PRINTF("HMAC_PROOF mismatch\n");
322 goto end;
323 }
324 success = true;
325
326end:
327 explicit_bzero(msg, sizeof(msg));
328 return success;
329}
330
348bool address_book_compute_hmac_rest(const path_bip32_t *bip32_path,
349 const uint8_t gid[GID_SIZE],
350 const char *scope,
351 const uint8_t *identifier,
352 uint8_t identifier_len,
353 blockchain_family_e family,
354 uint64_t chain_id,
355 uint8_t hmac_out[CX_SHA256_SIZE])
356{
357 uint8_t msg[HMAC_MSG_MAX_SIZE] = {0};
358 bool success = false;
359 size_t msg_len = serialize_hmac_msg(
360 msg, sizeof(msg), gid, scope, identifier, identifier_len, true, family, chain_id);
361
362 if (msg_len == SIZE_MAX) {
363 goto end;
364 }
365 if (!sys_address_book_hmac(bip32_path->path,
366 bip32_path->length,
367 ADDRESS_BOOK_SALT_IDENTITY,
368 msg,
369 msg_len,
370 hmac_out)) {
371 goto end;
372 }
373 success = true;
374
375end:
376 explicit_bzero(msg, sizeof(msg));
377 return success;
378}
379
393bool address_book_verify_hmac_rest(const path_bip32_t *bip32_path,
394 const uint8_t gid[GID_SIZE],
395 const char *scope,
396 const uint8_t *identifier,
397 uint8_t identifier_len,
398 blockchain_family_e family,
399 uint64_t chain_id,
400 const uint8_t hmac_expected[CX_SHA256_SIZE])
401{
402 uint8_t msg[HMAC_MSG_MAX_SIZE] = {0};
403 bool success = false;
404 size_t msg_len = serialize_hmac_msg(
405 msg, sizeof(msg), gid, scope, identifier, identifier_len, true, family, chain_id);
406
407 if (msg_len == SIZE_MAX) {
408 goto end;
409 }
410 if (!sys_address_book_hmac_verify(bip32_path->path,
411 bip32_path->length,
412 ADDRESS_BOOK_SALT_IDENTITY,
413 msg,
414 msg_len,
415 hmac_expected)) {
416 PRINTF("HMAC_REST mismatch\n");
417 goto end;
418 }
419 success = true;
420
421end:
422 explicit_bzero(msg, sizeof(msg));
423 return success;
424}
425
426#ifdef HAVE_ADDRESS_BOOK_LEDGER_ACCOUNT
427
441bool address_book_compute_hmac_proof_ledger_account(const path_bip32_t *bip32_path,
442 const char *name,
443 blockchain_family_e family,
444 uint64_t chain_id,
445 uint8_t hmac_out[CX_SHA256_SIZE])
446{
447 uint8_t msg[HMAC_LEDGER_MSG_SIZE] = {0};
448 bool success = false;
449 size_t msg_len
450 = serialize_hmac_msg(msg, sizeof(msg), NULL, name, NULL, 0, true, family, chain_id);
451
452 if (msg_len == SIZE_MAX) {
453 goto end;
454 }
455 if (!sys_address_book_hmac(bip32_path->path,
456 bip32_path->length,
457 ADDRESS_BOOK_SALT_LEDGER_ACCOUNT,
458 msg,
459 msg_len,
460 hmac_out)) {
461 goto end;
462 }
463 success = true;
464
465end:
466 explicit_bzero(msg, sizeof(msg));
467 return success;
468}
469
480bool address_book_verify_hmac_proof_ledger_account(const path_bip32_t *bip32_path,
481 const char *name,
482 blockchain_family_e family,
483 uint64_t chain_id,
484 const uint8_t hmac_expected[CX_SHA256_SIZE])
485{
486 uint8_t msg[HMAC_LEDGER_MSG_SIZE] = {0};
487 bool success = false;
488 size_t msg_len
489 = serialize_hmac_msg(msg, sizeof(msg), NULL, name, NULL, 0, true, family, chain_id);
490
491 if (msg_len == SIZE_MAX) {
492 goto end;
493 }
494 if (!sys_address_book_hmac_verify(bip32_path->path,
495 bip32_path->length,
496 ADDRESS_BOOK_SALT_LEDGER_ACCOUNT,
497 msg,
498 msg_len,
499 hmac_expected)) {
500 PRINTF("HMAC proof mismatch\n");
501 goto end;
502 }
503 success = true;
504
505end:
506 explicit_bzero(msg, sizeof(msg));
507 return success;
508}
509
510#endif // HAVE_ADDRESS_BOOK_LEDGER_ACCOUNT
511#endif // HAVE_ADDRESS_BOOK
blockchain_family_e
@ FAMILY_ETHEREUM
Register / Edit Contact Name / Edit Scope / Edit Identifier.
static int io_send_response_pointer(const uint8_t *ptr, size_t size, uint16_t sw)
Definition io.h:79
Random Number Generation.
SHA-2 (Secure Hash Algorithm 2)
uint32_t path[MAX_BIP32_PATH]
Definition bip32.h:19
uint8_t length
Definition bip32.h:18