Skip to content

Commit 98c3c26

Browse files
Merge pull request #373 from LedgerHQ/mk_syscall
Switching to new Master Key Fingerprint Syscall + Derivation Path Hardening
2 parents a8db383 + d9756e6 commit 98c3c26

File tree

62 files changed

+163
-104
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+163
-104
lines changed

Makefile

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,9 @@ ifeq ($(BOLOS_SDK),)
1919
$(error Environment variable BOLOS_SDK is not set)
2020
endif
2121

22-
# TODO: Compile with the right path restrictions
23-
#
24-
# The right path restriction would be something like
25-
# --path "*'/0'"
26-
# for mainnet, and
27-
# --path "*'/1'"
28-
# for testnet.
29-
#
30-
# That is, restrict the BIP-44 coin_type, but not the purpose.
31-
# However, such wildcards are not currently supported by the OS.
32-
#
33-
# Note that the app still requires explicit user approval before exporting
34-
# any xpub outside of a small set of allowed standard paths.
35-
3622
# Application allowed derivation curves.
3723
CURVE_APP_LOAD_PARAMS = secp256k1
3824

39-
# Application allowed derivation paths.
40-
PATH_APP_LOAD_PARAMS = ""
41-
4225
# Allowed SLIP21 paths
4326
PATH_SLIP21_APP_LOAD_PARAMS = "LEDGER-Wallet policy"
4427

@@ -73,12 +56,14 @@ endif
7356
########################################
7457
# Application custom permissions #
7558
########################################
76-
HAVE_APPLICATION_FLAG_DERIVE_MASTER = 1
7759
HAVE_APPLICATION_FLAG_GLOBAL_PIN = 1
7860
HAVE_APPLICATION_FLAG_BOLOS_SETTINGS = 1
7961
HAVE_APPLICATION_FLAG_LIBRARY = 1
8062

8163
ifeq ($(COIN),bitcoin_testnet)
64+
# Application allowed derivation paths (testnet).
65+
PATH_APP_LOAD_PARAMS = "*/1'"
66+
8267
# Bitcoin testnet, no legacy support
8368
DEFINES += BIP32_PUBKEY_VERSION=0x043587CF
8469
DEFINES += BIP44_COIN_TYPE=1
@@ -89,6 +74,9 @@ ifeq ($(COIN),bitcoin_testnet)
8974

9075
APPNAME = "Bitcoin Test"
9176
else ifeq ($(COIN),bitcoin)
77+
# Application allowed derivation paths (mainnet).
78+
PATH_APP_LOAD_PARAMS = "*/0'"
79+
9280
# the version for performance tests automatically approves all requests
9381
# there is no reason to ever compile the mainnet app with this flag
9482
ifneq ($(AUTOAPPROVE_FOR_PERF_TESTS),0)

src/crypto.c

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "common/read.h"
3636
#include "common/write.h"
3737

38+
#include "../boilerplate/sw.h"
3839
#include "../debug-helpers/debug.h"
3940

4041
#include "crypto.h"
@@ -241,26 +242,28 @@ void crypto_get_checksum(const uint8_t *in, uint16_t in_len, uint8_t out[static
241242
memmove(out, buffer, 4);
242243
}
243244

244-
bool crypto_get_compressed_pubkey_at_path(const uint32_t bip32_path[],
245-
uint8_t bip32_path_len,
246-
uint8_t pubkey[static 33],
247-
uint8_t chain_code[]) {
245+
cx_err_t crypto_get_compressed_pubkey_at_path(const uint32_t bip32_path[],
246+
uint8_t bip32_path_len,
247+
uint8_t pubkey[static 33],
248+
uint8_t chain_code[]) {
248249
uint8_t raw_public_key[65];
250+
cx_err_t error = CX_OK;
249251

250-
if (bip32_derive_get_pubkey_256(CX_CURVE_256K1,
251-
bip32_path,
252-
bip32_path_len,
253-
raw_public_key,
254-
chain_code,
255-
CX_SHA512) != CX_OK) {
256-
return false;
252+
error = bip32_derive_get_pubkey_256(CX_CURVE_256K1,
253+
bip32_path,
254+
bip32_path_len,
255+
raw_public_key,
256+
chain_code,
257+
CX_SHA512);
258+
if (error != CX_OK) {
259+
return error;
257260
}
258261

259262
if (crypto_get_compressed_pubkey(raw_public_key, pubkey) < 0) {
260-
return false;
263+
return CX_INTERNAL_ERROR;
261264
}
262265

263-
return true;
266+
return error;
264267
}
265268

266269
uint32_t crypto_get_key_fingerprint(const uint8_t pub_key[static 33]) {
@@ -271,10 +274,14 @@ uint32_t crypto_get_key_fingerprint(const uint8_t pub_key[static 33]) {
271274
}
272275

273276
uint32_t crypto_get_master_key_fingerprint() {
274-
uint8_t master_pub_key[33];
275-
uint32_t bip32_path[] = {};
276-
crypto_get_compressed_pubkey_at_path(bip32_path, 0, master_pub_key, NULL);
277-
return crypto_get_key_fingerprint(master_pub_key);
277+
uint8_t master_key_identifier[CX_RIPEMD160_SIZE] = {0};
278+
279+
int res = os_perso_get_master_key_identifier(master_key_identifier, CX_RIPEMD160_SIZE);
280+
LEDGER_ASSERT(
281+
res == CX_OK,
282+
"Unexpected error in os_perso_get_master_key_identifier computation. Returned: %d",
283+
res);
284+
return read_u32_be(master_key_identifier, 0);
278285
}
279286

280287
bool crypto_derive_symmetric_key(const char *label, size_t label_len, uint8_t key[static 32]) {
@@ -311,23 +318,26 @@ bool crypto_derive_symmetric_key(const char *label, size_t label_len, uint8_t ke
311318
return ret == CX_OK;
312319
}
313320

314-
int get_extended_pubkey_at_path(const uint32_t bip32_path[],
315-
uint8_t bip32_path_len,
316-
uint32_t bip32_pubkey_version,
317-
serialized_extended_pubkey_t *out_pubkey) {
321+
cx_err_t get_extended_pubkey_at_path(const uint32_t bip32_path[],
322+
uint8_t bip32_path_len,
323+
uint32_t bip32_pubkey_version,
324+
serialized_extended_pubkey_t *out_pubkey) {
318325
// find parent key's fingerprint and child number
319326
uint32_t parent_fingerprint = 0;
320327
uint32_t child_number = 0;
328+
cx_err_t error = CX_OK;
329+
321330
if (bip32_path_len > 0) {
322331
// here we reuse the storage for the parent keys that we will later use
323332
// for the response, in order to save memory
324333

325334
uint8_t parent_pubkey[33];
326-
if (!crypto_get_compressed_pubkey_at_path(bip32_path,
327-
bip32_path_len - 1,
328-
parent_pubkey,
329-
NULL)) {
330-
return -1;
335+
error = crypto_get_compressed_pubkey_at_path(bip32_path,
336+
bip32_path_len - 1,
337+
parent_pubkey,
338+
NULL);
339+
if (error != CX_OK) {
340+
return error;
331341
}
332342

333343
parent_fingerprint = crypto_get_key_fingerprint(parent_pubkey);
@@ -339,14 +349,31 @@ int get_extended_pubkey_at_path(const uint32_t bip32_path[],
339349
write_u32_be(out_pubkey->parent_fingerprint, 0, parent_fingerprint);
340350
write_u32_be(out_pubkey->child_number, 0, child_number);
341351

342-
if (!crypto_get_compressed_pubkey_at_path(bip32_path,
343-
bip32_path_len,
344-
out_pubkey->compressed_pubkey,
345-
out_pubkey->chain_code)) {
346-
return -1;
352+
return crypto_get_compressed_pubkey_at_path(bip32_path,
353+
bip32_path_len,
354+
out_pubkey->compressed_pubkey,
355+
out_pubkey->chain_code);
356+
}
357+
358+
uint16_t cx_err_to_sw(cx_err_t error) {
359+
if (error == CX_OK) {
360+
return SW_OK;
347361
}
348362

349-
return 0;
363+
/* The error codes are not currently defined in the SDK */
364+
if (error == 0x4212) {
365+
PRINTF(
366+
"Attempt to derive a key at root level without "
367+
"HAVE_APPLICATION_FLAG_DERIVE_MASTER permission.\n");
368+
return SW_NOT_SUPPORTED;
369+
}
370+
if (error == 0x4215) {
371+
PRINTF("Attempt to derive a key at unauthorized path.\n");
372+
return SW_NOT_SUPPORTED;
373+
}
374+
375+
PRINTF("Failed getting bip32 pubkey, error = 0x%08X\n", error);
376+
return SW_BAD_STATE;
350377
}
351378

352379
int base58_encode_address(const uint8_t in[20], uint32_t version, char *out, size_t out_len) {

src/crypto.h

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,10 @@ void crypto_get_checksum(const uint8_t *in, uint16_t in_len, uint8_t out[static
252252
*
253253
* @return true on success, false in case of error.
254254
*/
255-
bool crypto_get_compressed_pubkey_at_path(const uint32_t bip32_path[],
256-
uint8_t bip32_path_len,
257-
uint8_t pubkey[static 33],
258-
uint8_t chain_code[]);
255+
cx_err_t crypto_get_compressed_pubkey_at_path(const uint32_t bip32_path[],
256+
uint8_t bip32_path_len,
257+
uint8_t pubkey[static 33],
258+
uint8_t chain_code[]);
259259

260260
/**
261261
* Computes the fingerprint of a compressed key as per BIP32; that is, the first 4 bytes of the
@@ -289,10 +289,10 @@ uint32_t crypto_get_master_key_fingerprint();
289289
*
290290
* @return 0 on success, or -1 on error.
291291
*/
292-
int get_extended_pubkey_at_path(const uint32_t bip32_path[],
293-
uint8_t bip32_path_len,
294-
uint32_t bip32_pubkey_version,
295-
serialized_extended_pubkey_t *out_pubkey);
292+
cx_err_t get_extended_pubkey_at_path(const uint32_t bip32_path[],
293+
uint8_t bip32_path_len,
294+
uint32_t bip32_pubkey_version,
295+
serialized_extended_pubkey_t *out_pubkey);
296296

297297
/**
298298
* Derives the level-1 symmetric key at the given label using SLIP-0021.
@@ -474,3 +474,13 @@ int crypto_tr_tweak_seckey(const uint8_t seckey[static 32],
474474
const uint8_t *h,
475475
size_t h_len,
476476
uint8_t out[static 32]);
477+
478+
/**
479+
* Converts cx_err_t to 2-bytes SW and prints out debug information.
480+
*
481+
* @param[in] error
482+
* Cryptographic error code
483+
*
484+
* @return SW (SW_OK on success, other value on error).
485+
*/
486+
uint16_t cx_err_to_sw(cx_err_t error);

src/handler/get_extended_pubkey.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,13 @@ void handler_get_extended_pubkey(dispatcher_context_t *dc, uint8_t protocol_vers
137137
}
138138

139139
serialized_extended_pubkey_check_t pubkey_check;
140-
if (0 > get_extended_pubkey_at_path(bip32_path,
141-
bip32_path_len,
142-
BIP32_PUBKEY_VERSION,
143-
&pubkey_check.serialized_extended_pubkey)) {
144-
PRINTF("Failed getting bip32 pubkey\n");
145-
SEND_SW(dc, SW_BAD_STATE);
140+
uint16_t sw =
141+
cx_err_to_sw(get_extended_pubkey_at_path(bip32_path,
142+
bip32_path_len,
143+
BIP32_PUBKEY_VERSION,
144+
&pubkey_check.serialized_extended_pubkey));
145+
if (SW_OK != sw) {
146+
SEND_SW(dc, sw);
146147
return;
147148
}
148149

src/handler/get_master_fingerprint.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#include <stdint.h>
1919

20+
#include "os_seed.h"
21+
2022
#include "boilerplate/dispatcher.h"
2123
#include "boilerplate/sw.h"
2224
#include "../commands.h"
@@ -27,14 +29,12 @@
2729
void handler_get_master_fingerprint(dispatcher_context_t *dc, uint8_t protocol_version) {
2830
(void) protocol_version;
2931

30-
uint8_t master_pubkey[33];
31-
if (!crypto_get_compressed_pubkey_at_path((uint32_t[]){}, 0, master_pubkey, NULL)) {
32+
uint8_t master_key_identifier[CX_RIPEMD160_SIZE] = {0};
33+
34+
if (os_perso_get_master_key_identifier(master_key_identifier, CX_RIPEMD160_SIZE) != CX_OK) {
3235
SEND_SW(dc, SW_BAD_STATE); // should never happen
3336
return;
3437
}
3538

36-
uint8_t master_fingerprint_be[4];
37-
write_u32_be(master_fingerprint_be, 0, crypto_get_key_fingerprint(master_pubkey));
38-
39-
SEND_RESPONSE(dc, master_fingerprint_be, sizeof(master_fingerprint_be), SW_OK);
39+
SEND_RESPONSE(dc, master_key_identifier, 4, SW_OK);
4040
}

src/handler/lib/policy.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,10 +1478,10 @@ bool is_wallet_policy_standard(dispatcher_context_t *dispatcher_context,
14781478

14791479
// generate pubkey and check if it matches
14801480
serialized_extended_pubkey_t derived_pubkey;
1481-
if (0 > get_extended_pubkey_at_path(key_info.master_key_derivation,
1482-
key_info.master_key_derivation_len,
1483-
BIP32_PUBKEY_VERSION,
1484-
&derived_pubkey)) {
1481+
if (CX_OK != get_extended_pubkey_at_path(key_info.master_key_derivation,
1482+
key_info.master_key_derivation_len,
1483+
BIP32_PUBKEY_VERSION,
1484+
&derived_pubkey)) {
14851485
PRINTF("Failed to derive pubkey\n");
14861486
return false;
14871487
}

src/handler/register_wallet.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,13 +193,13 @@ void handler_register_wallet(dispatcher_context_t *dc, uint8_t protocol_version)
193193
read_u32_be(key_info.master_key_fingerprint, 0) == master_key_fingerprint) {
194194
// we verify that we can actually generate the same pubkey
195195
serialized_extended_pubkey_t pubkey_derived;
196-
int serialized_pubkey_len =
197-
get_extended_pubkey_at_path(key_info.master_key_derivation,
198-
key_info.master_key_derivation_len,
199-
BIP32_PUBKEY_VERSION,
200-
&pubkey_derived);
201-
if (serialized_pubkey_len == -1) {
202-
SEND_SW(dc, SW_BAD_STATE);
196+
uint16_t sw =
197+
cx_err_to_sw(get_extended_pubkey_at_path(key_info.master_key_derivation,
198+
key_info.master_key_derivation_len,
199+
BIP32_PUBKEY_VERSION,
200+
&pubkey_derived));
201+
if (SW_OK != sw) {
202+
SEND_SW(dc, sw);
203203
return;
204204
}
205205

src/handler/sign_psbt.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,10 +399,10 @@ static bool __attribute__((noinline)) get_and_verify_key_info(dispatcher_context
399399
// it could be a collision on the fingerprint; we verify that we can actually generate
400400
// the same pubkey
401401
serialized_extended_pubkey_t derived_pubkey;
402-
if (0 > get_extended_pubkey_at_path(key_info.master_key_derivation,
403-
key_info.master_key_derivation_len,
404-
BIP32_PUBKEY_VERSION,
405-
&derived_pubkey)) {
402+
if (CX_OK != get_extended_pubkey_at_path(key_info.master_key_derivation,
403+
key_info.master_key_derivation_len,
404+
BIP32_PUBKEY_VERSION,
405+
&derived_pubkey)) {
406406
return false;
407407
}
408408

src/swap/handle_check_address.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,8 @@ int handle_check_address(check_address_parameters_t* params) {
110110
return false;
111111
}
112112

113-
if (!crypto_get_compressed_pubkey_at_path(path.path,
114-
path.length,
115-
compressed_public_key,
116-
NULL)) {
113+
if (CX_OK !=
114+
crypto_get_compressed_pubkey_at_path(path.path, path.length, compressed_public_key, NULL)) {
117115
return 0;
118116
}
119117
char address[MAX_ADDRESS_LENGTH_STR + 1];
@@ -133,4 +131,4 @@ int handle_check_address(check_address_parameters_t* params) {
133131
}
134132
PRINTF("Addresses match\n");
135133
return 1;
136-
}
134+
}
339 Bytes

0 commit comments

Comments
 (0)