Skip to content

Commit 4aded96

Browse files
committed
Merge pull request #266
7d9a645 travis.yml: check commit sigs since january (djb) 1168acd signing code: validate change address (djb)
2 parents 7f43252 + 7d9a645 commit 4aded96

File tree

6 files changed

+435
-91
lines changed

6 files changed

+435
-91
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ before_script:
5454
- if astyle --style=kr --indent-switches --indent-labels --pad-oper --pad-header --align-pointer=name --add-braces --convert-tabs --max-code-length=90 --break-after-logical --suffix=none *.c *.h --recursive --exclude=contrib --exclude=src/yajl --exclude=src/secp256k1 --exclude=tests/windows/hidapi --exclude=src/drivers --dry-run -Q | grep "Formatted" ; then exit 1 ; fi;
5555
- gpg --import contrib/contributors_gpg_keys/*
5656
- if ! git show c6ce843547c5212e33665dd4ca7c951a43067044; then exit 1 ; fi;
57-
- if git log --pretty="format:%H %aN %s %G?" c6ce843547c5212e33665dd4ca7c951a43067044..HEAD^1 | grep -v "${t} G$" | grep -v "${t} U$"; then exit 1 ; fi;
57+
- if git log --pretty="format:%H %aN %s %G?" f68b3d23d4bb150f52e7fa0fa853a8887a8f7aff..HEAD^1 | grep -v "${t} G$" | grep -v "${t} U$"; then exit 1 ; fi;
5858

5959
script:
6060
- mkdir build && cd build

src/commander.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,18 +656,99 @@ static void commander_process_seed(yajl_val json_node)
656656
}
657657

658658

659+
static int commander_check_change_keypath(yajl_val data, yajl_val checkpub)
660+
{
661+
// Check that all UTXOs use the same BIP44 prefix and depth
662+
size_t i;
663+
uint32_t depth = 0;
664+
uint32_t keypath_utxo_0[BIP44_KEYPATH_ADDRESS_DEPTH] = {0};
665+
uint32_t keypath_utxo_i[BIP44_KEYPATH_ADDRESS_DEPTH] = {0};
666+
for (i = 0; i < data->u.array.len; i++) {
667+
const char *keypath_path[] = { cmd_str(CMD_keypath), NULL };
668+
yajl_val obj = data->u.array.values[i];
669+
const char *keypath = YAJL_GET_STRING(yajl_tree_get(obj, keypath_path, yajl_t_string));
670+
671+
if (!keypath) {
672+
commander_fill_report(cmd_str(CMD_sign), NULL, DBB_ERR_IO_INVALID_CMD);
673+
return DBB_ERROR;
674+
}
675+
676+
if (i == 0) {
677+
if (wallet_parse_bip44_keypath(NULL, keypath_utxo_0, &depth, keypath, NULL,
678+
NULL) != DBB_OK) {
679+
commander_fill_report(cmd_str(CMD_sign), NULL, DBB_ERR_SIGN_KEYPATH);
680+
return DBB_ERROR;
681+
}
682+
} else {
683+
if (wallet_parse_bip44_keypath(NULL, keypath_utxo_i, &depth, keypath, NULL,
684+
NULL) != DBB_OK) {
685+
commander_fill_report(cmd_str(CMD_sign), NULL, DBB_ERR_SIGN_KEYPATH);
686+
return DBB_ERROR;
687+
}
688+
if (wallet_check_bip44_keypath_prefix(keypath_utxo_0, keypath_utxo_i) != DBB_OK) {
689+
commander_fill_report(cmd_str(CMD_sign), NULL, DBB_ERR_SIGN_KEYPATH);
690+
return DBB_ERROR;
691+
}
692+
}
693+
694+
if (depth != BIP44_KEYPATH_ADDRESS_DEPTH) {
695+
commander_fill_report(cmd_str(CMD_sign), NULL, DBB_ERR_SIGN_KEYPATH);
696+
return DBB_ERROR;
697+
}
698+
}
699+
700+
// Check that change addresses correspond to the UTXO prefix and BIP44 depth
701+
uint32_t keypath_change[BIP44_KEYPATH_ADDRESS_DEPTH] = {0};
702+
for (i = 0; i < checkpub->u.array.len; i++) {
703+
const char *keypath_path[] = { cmd_str(CMD_keypath), NULL };
704+
yajl_val obj = checkpub->u.array.values[i];
705+
const char *keypath = YAJL_GET_STRING(yajl_tree_get(obj, keypath_path, yajl_t_string));
706+
707+
if (!keypath) {
708+
commander_fill_report(cmd_str(CMD_sign), NULL, DBB_ERR_IO_INVALID_CMD);
709+
return DBB_ERROR;
710+
}
711+
712+
if (wallet_parse_bip44_keypath(NULL, keypath_change, &depth, keypath, NULL,
713+
NULL) != DBB_OK) {
714+
commander_fill_report(cmd_str(CMD_sign), NULL, DBB_ERR_SIGN_KEYPATH);
715+
return DBB_ERROR;
716+
}
717+
718+
if (depth != BIP44_KEYPATH_ADDRESS_DEPTH) {
719+
commander_fill_report(cmd_str(CMD_sign), NULL, DBB_ERR_SIGN_CHANGE);
720+
return DBB_ERROR;
721+
}
722+
723+
if (wallet_check_bip44_change_keypath(keypath_utxo_0, keypath_change) != DBB_OK) {
724+
commander_fill_report(cmd_str(CMD_sign), NULL, DBB_ERR_SIGN_CHANGE);
725+
return DBB_ERROR;
726+
}
727+
}
728+
return DBB_OK;
729+
}
730+
731+
659732
static int commander_process_sign(yajl_val json_node)
660733
{
661734
size_t i;
662735
int ret;
663736
const char *data_path[] = { cmd_str(CMD_sign), cmd_str(CMD_data), NULL };
737+
const char *checkpub_path[] = { cmd_str(CMD_sign), cmd_str(CMD_checkpub), NULL };
664738
yajl_val data = yajl_tree_get(json_node, data_path, yajl_t_array);
739+
yajl_val checkpub = yajl_tree_get(json_node, checkpub_path, yajl_t_array);
665740

666741
if (!YAJL_IS_ARRAY(data) || data->u.array.len == 0) {
667742
commander_fill_report(cmd_str(CMD_sign), NULL, DBB_ERR_IO_INVALID_CMD);
668743
return DBB_ERROR;
669744
}
670745

746+
if (YAJL_IS_ARRAY(checkpub)) {
747+
if (commander_check_change_keypath(data, checkpub) != DBB_OK) {
748+
return DBB_ERROR;
749+
}
750+
}
751+
671752
memset(json_array, 0, COMMANDER_ARRAY_MAX);
672753
for (i = 0; i < data->u.array.len; i++) {
673754
const char *keypath_path[] = { cmd_str(CMD_keypath), NULL };

src/flags.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ X(ERR_SIGN_HASH_LEN, 301, "Incorrect hash length. A 32-byte hexadecimal value
218218
X(ERR_SIGN_DESERIAL, 302, "Could not deserialize outputs or wrong change keypath.")\
219219
X(ERR_SIGN_ECCLIB, 303, "Could not sign.")\
220220
X(ERR_SIGN_TFA_PIN, 304, "Incorrect TFA pin.")\
221+
X(ERR_SIGN_KEYPATH, 305, "Invalid keypath")\
222+
X(ERR_SIGN_CHANGE, 306, "Invalid change keypath")\
221223
X(ERR_SD_CARD, 400, "Please insert SD card.")\
222224
X(ERR_SD_MOUNT, 401, "Could not mount the SD card.")\
223225
X(ERR_SD_OPEN_FILE, 402, "Could not open a file to write - it may already exist.")\

src/wallet.c

Lines changed: 113 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,6 @@
4242
#include "ecc.h"
4343

4444

45-
typedef enum BIP44_LEVELS {
46-
BIP44_LEVEL_PURPOSE,
47-
BIP44_LEVEL_COIN_TYPE,
48-
BIP44_LEVEL_ACCOUNT,
49-
BIP44_LEVEL_CHANGE,
50-
BIP44_LEVEL_ADDRESS,
51-
} BIP44_LEVELS;
52-
53-
5445
extern const uint8_t MEM_PAGE_ERASE[MEM_PAGE_LEN];
5546
extern const uint8_t MEM_PAGE_ERASE_FE[MEM_PAGE_LEN];
5647
extern const uint16_t MEM_PAGE_ERASE_2X[MEM_PAGE_LEN];
@@ -149,18 +140,46 @@ int wallet_create(const char *passphrase, const char *entropy_in)
149140
}
150141

151142

152-
/*
153-
Returns DBB_OK if successful and keypath is whitelisted
154-
Returns DBB_WARN_KEYPATH if successful but keypath is not whitelisted
155-
Returns DBB_ERROR if could not generate a key
156-
*/
157-
int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privkeymaster,
158-
const uint8_t *chaincode)
143+
int wallet_check_bip44_keypath_prefix(const uint32_t
144+
keypath0[BIP44_KEYPATH_ADDRESS_DEPTH],
145+
const uint32_t keypath1[BIP44_KEYPATH_ADDRESS_DEPTH])
146+
{
147+
// Check that purpose, coin type, and account indices are the same
148+
if (keypath0[BIP44_LEVEL_PURPOSE] != keypath1[BIP44_LEVEL_PURPOSE] ||
149+
keypath0[BIP44_LEVEL_ACCOUNT] != keypath1[BIP44_LEVEL_ACCOUNT] ||
150+
keypath0[BIP44_LEVEL_COIN_TYPE] != keypath1[BIP44_LEVEL_COIN_TYPE]) {
151+
return DBB_ERROR;
152+
}
153+
return DBB_OK;
154+
}
155+
156+
157+
int wallet_check_bip44_change_keypath(const uint32_t utxo[BIP44_KEYPATH_ADDRESS_DEPTH],
158+
const uint32_t change[BIP44_KEYPATH_ADDRESS_DEPTH])
159+
{
160+
// Check the change keypath's change level
161+
if (change[BIP44_LEVEL_CHANGE] != 1) {
162+
return DBB_ERROR;
163+
}
164+
// Check that the change keypath address level is within range
165+
if (change[BIP44_LEVEL_ADDRESS] > BIP44_ADDRESS_MAX) {
166+
return DBB_ERROR;
167+
}
168+
// Check that purpose, coin type, and account indices are the same
169+
return wallet_check_bip44_keypath_prefix(utxo, change);
170+
}
171+
172+
173+
int wallet_parse_bip44_keypath(HDNode *node,
174+
uint32_t keypath_array[BIP44_KEYPATH_ADDRESS_DEPTH],
175+
uint32_t *depth, const char *keypath, const uint8_t *privkeymaster,
176+
const uint8_t *chaincodemaster)
159177
{
160178
static char delim[] = "/";
161179
static char prime[] = "phH\'";
162180
static char digits[] = "0123456789";
163181
uint64_t idx = 0;
182+
*depth = 0;
164183

165184
char *kp = strdup(keypath);
166185
if (!kp) {
@@ -175,19 +194,21 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke
175194
goto err;
176195
}
177196

178-
node->depth = 0;
179-
node->child_num = 0;
180-
node->fingerprint = 0;
181-
memcpy(node->chain_code, chaincode, 32);
182-
memcpy(node->private_key, privkeymaster, 32);
183-
hdnode_fill_public_key(node);
197+
if (node && privkeymaster && chaincodemaster) {
198+
node->depth = 0;
199+
node->child_num = 0;
200+
node->fingerprint = 0;
201+
memcpy(node->chain_code, chaincodemaster, 32);
202+
memcpy(node->private_key, privkeymaster, 32);
203+
hdnode_fill_public_key(node);
204+
}
184205

185206
char *pch = strtok(kp + 2, delim);
186207
if (pch == NULL) {
187208
goto err;
188209
}
189210
uint8_t path_level = 0;
190-
bool has_prime = false, whitelisted_keypath = true;
211+
bool has_prime = false;
191212
while (pch != NULL) {
192213
size_t i = 0;
193214
bool is_prime = false;
@@ -207,73 +228,34 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke
207228
goto err;
208229
}
209230
idx = strtoull(pch, NULL, 10);
210-
if (idx > UINT32_MAX) {
231+
if (idx >= BIP44_PRIME) {
211232
goto err;
212233
}
213234

214-
if (is_prime) {
215-
if (hdnode_private_ckd_prime(node, idx) != DBB_OK) {
216-
goto err;
217-
}
218-
} else {
219-
// Note: if `idx` >= 0x80000000, prime derivation still occurs
220-
// even if the `keypath` does not have a `prime` marker, following
221-
// the BIP32 standard.
222-
if (hdnode_private_ckd(node, idx) != DBB_OK) {
223-
goto err;
235+
if (node && privkeymaster && chaincodemaster) {
236+
if (is_prime) {
237+
if (hdnode_private_ckd_prime(node, idx) != DBB_OK) {
238+
goto err;
239+
}
240+
} else {
241+
if (hdnode_private_ckd(node, idx) != DBB_OK) {
242+
goto err;
243+
}
224244
}
225245
}
226246

227-
// Check if the keypath is whitelisted
228-
if (whitelisted_keypath) {
229-
switch (path_level) {
230-
case (BIP44_LEVEL_PURPOSE):
231-
if (is_prime != BIP44_PURPOSE_HARDENED || (
232-
idx != BIP44_PURPOSE_P2PKH &&
233-
idx != BIP44_PURPOSE_P2WPKH_P2SH &&
234-
idx != BIP44_PURPOSE_P2WPKH
235-
)) {
236-
whitelisted_keypath = false;
237-
}
238-
break;
239-
case (BIP44_LEVEL_COIN_TYPE):
240-
if (is_prime != BIP44_COIN_TYPE_HARDENED || (
241-
idx != BIP44_COIN_TYPE_BTC &&
242-
idx != BIP44_COIN_TYPE_LTC &&
243-
idx != BIP44_COIN_TYPE_TESTNET
244-
)) {
245-
whitelisted_keypath = false;
246-
}
247-
break;
248-
case (BIP44_LEVEL_ACCOUNT):
249-
if (is_prime != BIP44_ACCOUNT_HARDENED || idx > BIP44_ACCOUNT_MAX) {
250-
whitelisted_keypath = false;
251-
}
252-
break;
253-
case (BIP44_LEVEL_CHANGE):
254-
if (is_prime != BIP44_CHANGE_HARDENED || idx > BIP44_CHANGE_MAX) {
255-
whitelisted_keypath = false;
256-
}
257-
break;
258-
case (BIP44_LEVEL_ADDRESS):
259-
if (is_prime != BIP44_ADDRESS_HARDENED || idx > BIP44_ADDRESS_MAX) {
260-
whitelisted_keypath = false;
261-
}
262-
break;
263-
default:
264-
whitelisted_keypath = false;
265-
}
247+
if (path_level < BIP44_KEYPATH_ADDRESS_DEPTH) {
248+
keypath_array[path_level] = idx + (is_prime ? BIP44_PRIME : 0);
266249
}
250+
267251
pch = strtok(NULL, delim);
268252
path_level++;
253+
*depth = path_level;
269254
}
270255
if (!has_prime) {
271256
goto err;
272257
}
273258
free(kp);
274-
if (!whitelisted_keypath) {
275-
return DBB_WARN_KEYPATH;
276-
}
277259
return DBB_OK;
278260

279261
err:
@@ -282,6 +264,61 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke
282264
}
283265

284266

267+
/*
268+
Returns DBB_OK if successful and keypath is whitelisted
269+
Returns DBB_WARN_KEYPATH if successful but keypath is not whitelisted
270+
Returns DBB_ERROR if could not generate a key
271+
*/
272+
int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privkeymaster,
273+
const uint8_t *chaincodemaster)
274+
{
275+
uint32_t keypath_array[BIP44_KEYPATH_ADDRESS_DEPTH] = {0};
276+
uint32_t depth = 0;
277+
if (wallet_parse_bip44_keypath(node, keypath_array, &depth, keypath,
278+
privkeymaster, chaincodemaster) != DBB_OK) {
279+
return DBB_ERROR;
280+
}
281+
282+
// Check if the keypath is whitelisted
283+
uint32_t idx;
284+
idx = keypath_array[BIP44_LEVEL_PURPOSE];
285+
if (idx != (BIP44_PURPOSE_P2PKH + (BIP44_PURPOSE_HARDENED ? BIP44_PRIME : 0)) &&
286+
idx != (BIP44_PURPOSE_P2WPKH + (BIP44_PURPOSE_HARDENED ? BIP44_PRIME : 0)) &&
287+
idx != (BIP44_PURPOSE_P2WPKH_P2SH + (BIP44_PURPOSE_HARDENED ? BIP44_PRIME : 0))) {
288+
return DBB_WARN_KEYPATH;
289+
}
290+
291+
idx = keypath_array[BIP44_LEVEL_COIN_TYPE];
292+
if (idx != (BIP44_COIN_TYPE_BTC + (BIP44_COIN_TYPE_HARDENED ? BIP44_PRIME : 0)) &&
293+
idx != (BIP44_COIN_TYPE_LTC + (BIP44_COIN_TYPE_HARDENED ? BIP44_PRIME : 0)) &&
294+
idx != (BIP44_COIN_TYPE_TESTNET + (BIP44_COIN_TYPE_HARDENED ? BIP44_PRIME : 0))) {
295+
return DBB_WARN_KEYPATH;
296+
}
297+
298+
idx = keypath_array[BIP44_LEVEL_ACCOUNT];
299+
if (idx > (BIP44_ACCOUNT_MAX + (BIP44_ACCOUNT_HARDENED ? BIP44_PRIME : 0)) ||
300+
idx < (BIP44_ACCOUNT_HARDENED ? BIP44_PRIME : 0)) {
301+
return DBB_WARN_KEYPATH;
302+
}
303+
304+
idx = keypath_array[BIP44_LEVEL_CHANGE];
305+
if (idx > BIP44_CHANGE_MAX) {
306+
return DBB_WARN_KEYPATH;
307+
}
308+
309+
idx = keypath_array[BIP44_LEVEL_ADDRESS];
310+
if (idx > BIP44_ADDRESS_MAX) {
311+
return DBB_WARN_KEYPATH;
312+
}
313+
314+
if (node->depth != BIP44_KEYPATH_ADDRESS_DEPTH) {
315+
return DBB_WARN_KEYPATH;
316+
}
317+
318+
return DBB_OK;
319+
}
320+
321+
285322
int wallet_generate_node(const char *passphrase, const char *entropy, HDNode *node)
286323
{
287324
int ret;

0 commit comments

Comments
 (0)