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// m/44'/60'/0'/0/0 — hardcoded for all Identity HMAC derivations
144// TODO: to be removed when the OS doesn't need it any more for the HMAC derivation
145static const path_bip32_t s_identity_path = {
146 .length = 5,
147 .path = {0x8000002C, 0x8000003C, 0x80000000, 0x00000000, 0x00000000},
148};
149
150/* Exported functions --------------------------------------------------------*/
151
161bool address_book_send_hmac_proof(uint8_t type, const uint8_t hmac_proof[CX_SHA256_SIZE])
162{
163 size_t tx = 0;
164
165 // Response format: type(1) | hmac(32)
166 G_io_tx_buffer[tx++] = type;
167 memmove(&G_io_tx_buffer[tx], hmac_proof, CX_SHA256_SIZE);
168 tx += CX_SHA256_SIZE;
169
170 io_send_response_pointer(G_io_tx_buffer, tx, SWO_SUCCESS);
171 return true;
172}
173
185bool address_book_send_register_identity_response(const uint8_t group_handle[GROUP_HANDLE_SIZE],
186 const uint8_t hmac_proof[CX_SHA256_SIZE],
187 const uint8_t hmac_rest[CX_SHA256_SIZE])
188{
189 size_t tx = 0;
190
191 G_io_tx_buffer[tx++] = TYPE_REGISTER_IDENTITY;
192 memmove(&G_io_tx_buffer[tx], group_handle, GROUP_HANDLE_SIZE);
193 tx += GROUP_HANDLE_SIZE;
194 memmove(&G_io_tx_buffer[tx], hmac_proof, CX_SHA256_SIZE);
195 tx += CX_SHA256_SIZE;
196 memmove(&G_io_tx_buffer[tx], hmac_rest, CX_SHA256_SIZE);
197 tx += CX_SHA256_SIZE;
198
199 io_send_response_pointer(G_io_tx_buffer, tx, SWO_SUCCESS);
200 return true;
201}
202
212bool address_book_generate_group_handle(uint8_t group_handle[GROUP_HANDLE_SIZE])
213{
214 bool success = false;
215 uint8_t *gid = group_handle;
216 uint8_t *mac = group_handle + GID_SIZE;
217
218 cx_rng_no_throw(gid, GID_SIZE);
219 if (!sys_address_book_hmac(s_identity_path.path,
220 s_identity_path.length,
221 ADDRESS_BOOK_SALT_GROUP,
222 gid,
223 GID_SIZE,
224 mac)) {
225 goto end;
226 }
227 success = true;
228
229end:
230 return success;
231}
232
243bool address_book_verify_group_handle(const uint8_t group_handle[GROUP_HANDLE_SIZE],
244 uint8_t gid_out[GID_SIZE])
245{
246 bool success = false;
247 const uint8_t *gid = group_handle;
248 const uint8_t *mac = group_handle + GID_SIZE;
249
250 if (!sys_address_book_hmac_verify(s_identity_path.path,
251 s_identity_path.length,
252 ADDRESS_BOOK_SALT_GROUP,
253 gid,
254 GID_SIZE,
255 mac)) {
256 PRINTF("[Address Book] Group handle MAC verification failed\n");
257 goto end;
258 }
259 memmove(gid_out, gid, GID_SIZE);
260 success = true;
261
262end:
263 return success;
264}
265
277bool address_book_compute_hmac_proof(const uint8_t gid[GID_SIZE],
278 const char *name,
279 uint8_t hmac_out[CX_SHA256_SIZE])
280{
281 uint8_t msg[HMAC_PROOF_MSG_SIZE] = {0};
282 bool success = false;
283 size_t msg_len = serialize_hmac_msg(msg, sizeof(msg), gid, name, NULL, 0, false, 0, 0);
284
285 if (msg_len == SIZE_MAX) {
286 goto end;
287 }
288 if (!sys_address_book_hmac(s_identity_path.path,
289 s_identity_path.length,
290 ADDRESS_BOOK_SALT_IDENTITY,
291 msg,
292 msg_len,
293 hmac_out)) {
294 goto end;
295 }
296 success = true;
297
298end:
299 explicit_bzero(msg, sizeof(msg));
300 return success;
301}
302
311bool address_book_verify_hmac_proof(const uint8_t gid[GID_SIZE],
312 const char *name,
313 const uint8_t hmac_expected[CX_SHA256_SIZE])
314{
315 uint8_t msg[HMAC_PROOF_MSG_SIZE] = {0};
316 bool success = false;
317 size_t msg_len = serialize_hmac_msg(msg, sizeof(msg), gid, name, NULL, 0, false, 0, 0);
318
319 if (msg_len == SIZE_MAX) {
320 goto end;
321 }
322 if (!sys_address_book_hmac_verify(s_identity_path.path,
323 s_identity_path.length,
324 ADDRESS_BOOK_SALT_IDENTITY,
325 msg,
326 msg_len,
327 hmac_expected)) {
328 PRINTF("HMAC_PROOF mismatch\n");
329 goto end;
330 }
331 success = true;
332
333end:
334 explicit_bzero(msg, sizeof(msg));
335 return success;
336}
337
354bool address_book_compute_hmac_rest(const uint8_t gid[GID_SIZE],
355 const char *scope,
356 const uint8_t *identifier,
357 uint8_t identifier_len,
358 blockchain_family_e family,
359 uint64_t chain_id,
360 uint8_t hmac_out[CX_SHA256_SIZE])
361{
362 uint8_t msg[HMAC_MSG_MAX_SIZE] = {0};
363 bool success = false;
364 size_t msg_len = serialize_hmac_msg(
365 msg, sizeof(msg), gid, scope, identifier, identifier_len, true, family, chain_id);
366
367 if (msg_len == SIZE_MAX) {
368 goto end;
369 }
370 if (!sys_address_book_hmac(s_identity_path.path,
371 s_identity_path.length,
372 ADDRESS_BOOK_SALT_IDENTITY,
373 msg,
374 msg_len,
375 hmac_out)) {
376 goto end;
377 }
378 success = true;
379
380end:
381 explicit_bzero(msg, sizeof(msg));
382 return success;
383}
384
397bool address_book_verify_hmac_rest(const uint8_t gid[GID_SIZE],
398 const char *scope,
399 const uint8_t *identifier,
400 uint8_t identifier_len,
401 blockchain_family_e family,
402 uint64_t chain_id,
403 const uint8_t hmac_expected[CX_SHA256_SIZE])
404{
405 uint8_t msg[HMAC_MSG_MAX_SIZE] = {0};
406 bool success = false;
407 size_t msg_len = serialize_hmac_msg(
408 msg, sizeof(msg), gid, scope, identifier, identifier_len, true, family, chain_id);
409
410 if (msg_len == SIZE_MAX) {
411 goto end;
412 }
413 if (!sys_address_book_hmac_verify(s_identity_path.path,
414 s_identity_path.length,
415 ADDRESS_BOOK_SALT_IDENTITY,
416 msg,
417 msg_len,
418 hmac_expected)) {
419 PRINTF("HMAC_REST mismatch\n");
420 goto end;
421 }
422 success = true;
423
424end:
425 explicit_bzero(msg, sizeof(msg));
426 return success;
427}
428
429#ifdef HAVE_ADDRESS_BOOK_LEDGER_ACCOUNT
430
444bool address_book_compute_hmac_proof_ledger_account(const path_bip32_t *bip32_path,
445 const char *name,
446 blockchain_family_e family,
447 uint64_t chain_id,
448 uint8_t hmac_out[CX_SHA256_SIZE])
449{
450 uint8_t msg[HMAC_LEDGER_MSG_SIZE] = {0};
451 bool success = false;
452 size_t msg_len
453 = serialize_hmac_msg(msg, sizeof(msg), NULL, name, NULL, 0, true, family, chain_id);
454
455 if (msg_len == SIZE_MAX) {
456 goto end;
457 }
458 if (!sys_address_book_hmac(bip32_path->path,
459 bip32_path->length,
460 ADDRESS_BOOK_SALT_LEDGER_ACCOUNT,
461 msg,
462 msg_len,
463 hmac_out)) {
464 goto end;
465 }
466 success = true;
467
468end:
469 explicit_bzero(msg, sizeof(msg));
470 return success;
471}
472
483bool address_book_verify_hmac_proof_ledger_account(const path_bip32_t *bip32_path,
484 const char *name,
485 blockchain_family_e family,
486 uint64_t chain_id,
487 const uint8_t hmac_expected[CX_SHA256_SIZE])
488{
489 uint8_t msg[HMAC_LEDGER_MSG_SIZE] = {0};
490 bool success = false;
491 size_t msg_len
492 = serialize_hmac_msg(msg, sizeof(msg), NULL, name, NULL, 0, true, family, chain_id);
493
494 if (msg_len == SIZE_MAX) {
495 goto end;
496 }
497 if (!sys_address_book_hmac_verify(bip32_path->path,
498 bip32_path->length,
499 ADDRESS_BOOK_SALT_LEDGER_ACCOUNT,
500 msg,
501 msg_len,
502 hmac_expected)) {
503 PRINTF("HMAC proof mismatch\n");
504 goto end;
505 }
506 success = true;
507
508end:
509 explicit_bzero(msg, sizeof(msg));
510 return success;
511}
512
513#endif // HAVE_ADDRESS_BOOK_LEDGER_ACCOUNT
514#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