Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 37 additions & 22 deletions src/commander.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,10 @@ static int commander_process_backup_check(const char *key, const char *filename,
if (wallet_generate_node(key, seed, &node) == DBB_ERROR) {
ret = DBB_ERROR;
} else {
uint8_t main_ok = MEMEQ(node.private_key, memory_master_hww(NULL), MEM_PAGE_LEN) && MEMEQ(node.chain_code, memory_master_hww_chaincode(NULL), MEM_PAGE_LEN);
uint8_t hidden_ok = MEMEQ(node.private_key, memory_hidden_hww(NULL), MEM_PAGE_LEN) && MEMEQ(node.chain_code, memory_hidden_hww_chaincode(NULL), MEM_PAGE_LEN);
uint8_t main_ok = MEMEQ(node.private_key, memory_master_hww(NULL), MEM_PAGE_LEN) &&
MEMEQ(node.chain_code, memory_master_hww_chaincode(NULL), MEM_PAGE_LEN);
uint8_t hidden_ok = MEMEQ(node.private_key, memory_hidden_hww(NULL), MEM_PAGE_LEN) &&
MEMEQ(node.chain_code, memory_hidden_hww_chaincode(NULL), MEM_PAGE_LEN);
ret = (main_ok | hidden_ok) ? DBB_OK : DBB_ERROR; // bitwise for constant time
}
utils_zero(seed, sizeof(seed));
Expand Down Expand Up @@ -512,6 +514,13 @@ static void commander_process_seed(yajl_val json_node)
return;
}

if (wallet_seeded() == DBB_OK &&
!STREQ(source, attr_str(ATTR_U2F_load)) &&
!STREQ(source, attr_str(ATTR_U2F_create))) {
commander_fill_report(cmd_str(CMD_seed), NULL, DBB_ERR_SEED_SEEDED);
return;
}

if (STREQ(source, attr_str(ATTR_create))) {
// Generate a new wallet, optionally with entropy entered via USB
uint8_t i, add_entropy = 1, entropy_b[MEM_PAGE_LEN];
Expand Down Expand Up @@ -757,21 +766,24 @@ static void commander_process_xpub(yajl_val json_node)
return;
}

wallet_report_xpub(value, xpub);
int ret = wallet_report_xpub(value, xpub);

if (xpub[0]) {
commander_fill_report(cmd_str(CMD_xpub), xpub, DBB_OK);

int encrypt_len;
char *encoded_report = cipher_aes_b64_hmac_encrypt((unsigned char *) xpub, strlens(xpub),
&encrypt_len, memory_report_aeskey(TFA_SHARED_SECRET));

if (encoded_report) {
commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK);
free(encoded_report);
} else {
commander_clear_report();
commander_fill_report(cmd_str(CMD_xpub), NULL, DBB_ERR_MEM_ENCRYPT);
if (ret == DBB_WARN_KEYPATH) {
commander_fill_report(cmd_str(CMD_warning), flag_msg(DBB_WARN_KEYPATH), DBB_OK);
} else if (ret == DBB_OK) {
int encrypt_len;
char *encoded_report = cipher_aes_b64_hmac_encrypt((unsigned char *) xpub, strlens(xpub),
&encrypt_len, memory_report_aeskey(TFA_SHARED_SECRET));
if (encoded_report) {
commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK);
free(encoded_report);
} else {
commander_clear_report();
commander_fill_report(cmd_str(CMD_xpub), NULL, DBB_ERR_MEM_ENCRYPT);
}
}
} else {
commander_fill_report(cmd_str(CMD_xpub), NULL, DBB_ERR_KEY_CHILD);
Expand Down Expand Up @@ -864,9 +876,11 @@ static void commander_process_device(yajl_val json_node)

if (ext_flags & MEM_EXT_MASK_NEW_HIDDEN_WALLET) {
// Bit is set == enabled
snprintf(new_hidden_wallet_enabled, sizeof(new_hidden_wallet_enabled), "%s", attr_str(ATTR_true));
snprintf(new_hidden_wallet_enabled, sizeof(new_hidden_wallet_enabled), "%s",
attr_str(ATTR_true));
} else {
snprintf(new_hidden_wallet_enabled, sizeof(new_hidden_wallet_enabled), "%s", attr_str(ATTR_false));
snprintf(new_hidden_wallet_enabled, sizeof(new_hidden_wallet_enabled), "%s",
attr_str(ATTR_false));
}

if (wallet_is_paired()) {
Expand Down Expand Up @@ -1258,16 +1272,16 @@ static int commander_tfa_check_pin(yajl_val json_node)
return DBB_OK;
}

static uint8_t check_disable_pairing(const char* keypath)
static uint8_t check_disable_pairing(const char *keypath)
{
if (keypath == NULL) {
return 0;
}
const char* eth_keypath = "m/44'/60'/";
const char* etc_keypath = "m/44'/61'/";
const char *eth_keypath = "m/44'/60'/";
const char *etc_keypath = "m/44'/61'/";
// check if keypath starts with eth_keypath or etc_keypath.
return !strncmp(keypath, eth_keypath, strlen(eth_keypath)) ||
!strncmp(keypath, etc_keypath, strlen(etc_keypath));
!strncmp(keypath, etc_keypath, strlen(etc_keypath));
}

static int commander_echo_command(yajl_val json_node)
Expand Down Expand Up @@ -1353,7 +1367,8 @@ static int commander_echo_command(yajl_val json_node)

uint8_t disable_pairing = 1;
for (size_t i = 0; i < data->u.array.len; i++) {
const char *keypath = YAJL_GET_STRING(yajl_tree_get(data->u.array.values[i], keypath_path, yajl_t_string));
const char *keypath = YAJL_GET_STRING(yajl_tree_get(data->u.array.values[i], keypath_path,
yajl_t_string));
if (!check_disable_pairing(keypath)) {
disable_pairing = 0;
break;
Expand All @@ -1366,7 +1381,7 @@ static int commander_echo_command(yajl_val json_node)
}
int length;
char *encoded_report = cipher_aes_b64_hmac_encrypt((unsigned char *) json_report,
strlens(json_report), &length, memory_report_aeskey(TFA_SHARED_SECRET));
strlens(json_report), &length, memory_report_aeskey(TFA_SHARED_SECRET));
commander_clear_report();
if (encoded_report) {
commander_fill_report(cmd_str(CMD_echo), encoded_report, DBB_OK);
Expand Down Expand Up @@ -1651,7 +1666,7 @@ static int commander_check_init(const char *encrypted_command)
}
char device_info[100];
snprintf(device_info, sizeof(device_info),
"{\"%s\":\"%s\"}", attr_str(ATTR_version), DIGITAL_BITBOX_VERSION);
"{\"%s\":\"%s\"}", attr_str(ATTR_version), DIGITAL_BITBOX_VERSION);
commander_fill_report(cmd_str(CMD_device), device_info, DBB_JSON_OBJECT);
yajl_tree_free(json_node);
return DBB_ERROR;
Expand Down
2 changes: 2 additions & 0 deletions src/flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ X(ERR_SEED_SD, 200, "Seed creation requires an SD card for automatic enc
X(ERR_SEED_SD_NUM, 201, "Too many backup files. Please remove one from the SD card.")\
X(ERR_SEED_MEM, 202, "Could not allocate memory for seed.")\
X(ERR_SEED_INVALID, 204, "Invalid seed.")\
X(ERR_SEED_SEEDED, 205, "Wallet is seeded. Reset to continue.")\
X(ERR_KEY_MASTER, 250, "Master key not present.")\
X(ERR_KEY_CHILD, 251, "Could not generate key.")\
X(ERR_KEY_ECDH, 252, "Could not generate ECDH secret.")\
Expand Down Expand Up @@ -240,6 +241,7 @@ X(WARN_RESET, 900, "attempts remain before the device is reset.")\
X(WARN_NO_MCU, 901, "Ignored for non-embedded testing.")\
X(WARN_SD_NUM_FILES, 902, "Too many backup files to read. The list is truncated.")\
X(WARN_RESET_TOUCH, 903, "attempts remain before the device is reset. The next login requires holding the touch button.")\
X(WARN_KEYPATH, 904, "Non-standard keypath.")\
X(FLAG_NUM, 0, 0)/* keep last */


Expand Down
2 changes: 1 addition & 1 deletion src/flash.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ uint32_t flash_wrapper_erase_page(uint32_t ul_address, uint8_t uc_page_num)


uint32_t flash_wrapper_write(uint32_t ul_address, void *p_buffer,
uint32_t ul_size, uint32_t ul_erase_flag)
uint32_t ul_size, uint32_t ul_erase_flag)
{
#ifdef TESTING
uint32_t i;
Expand Down
2 changes: 1 addition & 1 deletion src/sd.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ static char ROOTDIR[] = "tests/digitalbitbox";// If change, update tests/CMakeLi
#else
static char ROOTDIR[256]; // 255 char path

void set_root_dir(const char* path)
void set_root_dir(const char *path)
{
memset(ROOTDIR, 0, sizeof(ROOTDIR));
snprintf(ROOTDIR, sizeof(ROOTDIR), "%s", path);
Expand Down
2 changes: 1 addition & 1 deletion src/sd.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ char *sd_load(const char *fn, int cmd);
uint8_t sd_write(const char *fn, const char *wallet_backup, const char *wallet_name,
const char *u2f_backup, uint8_t replace, int cmd);
#ifdef SIMULATOR
void set_root_dir(const char* path);
void set_root_dir(const char *path);
#endif


Expand Down
12 changes: 7 additions & 5 deletions src/simulator.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#define PORT 35345
#define BUF_SIZE COMMANDER_REPORT_SIZE

int main(int argc, char* argv[])
int main(int argc, char *argv[])
{
// Get and set the root directory
if (argc < 2) {
Expand Down Expand Up @@ -47,23 +47,24 @@ int main(int argc, char* argv[])
sock.sin_port = htons(PORT);

// Bind socket
if (bind(s, (struct sockaddr*)&sock, sizeof(sock)) == -1) {
if (bind(s, (struct sockaddr *)&sock, sizeof(sock)) == -1) {
fprintf(stderr, "Could not bind socket\n");
exit(1);
}
fprintf(stderr, "Digital Bitbox simulator started\n");

// Wait for connections and handle
char buf[BUF_SIZE];
char* result;
char *result;
struct sockaddr_in in_sock;
int recv_len;
socklen_t in_sock_len = sizeof(in_sock);
while (1) {
int rc;
// Receive
memset(buf, 0, BUF_SIZE);
if ((recv_len = recvfrom(s, buf, BUF_SIZE, 0, (struct sockaddr*)&in_sock, &in_sock_len)) < 0) {
if ((recv_len = recvfrom(s, buf, BUF_SIZE, 0, (struct sockaddr *)&in_sock,
&in_sock_len)) < 0) {
fprintf(stderr, "Failed to receive udp data, error %d\n", recv_len);
exit(1);
}
Expand All @@ -72,7 +73,8 @@ int main(int argc, char* argv[])
result = commander(buf);

// Send result to socket
if ((rc = sendto(s, result, strlen(result), 0, (struct sockaddr*)&in_sock, in_sock_len)) < 0) {
if ((rc = sendto(s, result, strlen(result), 0, (struct sockaddr *)&in_sock,
in_sock_len)) < 0) {
fprintf(stderr, "Failed to send udp data, error %d\n", rc);
exit(1);
}
Expand Down
90 changes: 78 additions & 12 deletions src/wallet.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>

#include "commander.h"
Expand All @@ -41,6 +42,15 @@
#include "ecc.h"


typedef enum BIP44_LEVELS {
BIP44_LEVEL_PURPOSE,
BIP44_LEVEL_COIN_TYPE,
BIP44_LEVEL_ACCOUNT,
BIP44_LEVEL_CHANGE,
BIP44_LEVEL_ADDRESS,
} BIP44_LEVELS;


extern const uint8_t MEM_PAGE_ERASE[MEM_PAGE_LEN];
extern const uint8_t MEM_PAGE_ERASE_FE[MEM_PAGE_LEN];
extern const uint16_t MEM_PAGE_ERASE_2X[MEM_PAGE_LEN];
Expand Down Expand Up @@ -139,6 +149,11 @@ int wallet_create(const char *passphrase, const char *entropy_in)
}


/*
Returns DBB_OK if successful and keypath is whitelisted
Returns DBB_WARN_KEYPATH if successful but keypath is not whitelisted
Returns DBB_ERROR if could not generate a key
*/
int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privkeymaster,
const uint8_t *chaincode)
{
Expand Down Expand Up @@ -171,45 +186,94 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke
if (pch == NULL) {
goto err;
}
int has_prm = 0;
uint8_t path_level = 0;
bool has_prime = false, whitelisted_keypath = true;
while (pch != NULL) {
size_t i = 0;
int prm = 0;
bool is_prime = false;
size_t pch_len = strlens(pch);
for ( ; i < pch_len; i++) {
if (strchr(prime, pch[i])) {
if (i != pch_len - 1) {
goto err;
}
prm = 1;
has_prm = 1;
is_prime = true;
has_prime = true;
} else if (!strchr(digits, pch[i])) {
goto err;
}
}
if (prm && pch_len == 1) {
if (is_prime && pch_len == 1) {
goto err;
}
idx = strtoull(pch, NULL, 10);
if (idx > UINT32_MAX) {
goto err;
}

if (prm) {
if (is_prime) {
if (hdnode_private_ckd_prime(node, idx) != DBB_OK) {
goto err;
}
} else {
// Note: if `idx` >= 0x80000000, prime derivation still occurs
// even if the `keypath` does not have a `prime` marker, following
// the BIP32 standard.
if (hdnode_private_ckd(node, idx) != DBB_OK) {
goto err;
}
}

// Check if the keypath is whitelisted
if (whitelisted_keypath) {
switch (path_level) {
case (BIP44_LEVEL_PURPOSE):
if (is_prime != BIP44_PURPOSE_HARDENED || (
idx != BIP44_PURPOSE_P2PKH &&
idx != BIP44_PURPOSE_P2WPKH_P2SH &&
idx != BIP44_PURPOSE_P2WPKH
)) {
whitelisted_keypath = false;
}
break;
case (BIP44_LEVEL_COIN_TYPE):
if (is_prime != BIP44_COIN_TYPE_HARDENED || (
idx != BIP44_COIN_TYPE_BTC &&
idx != BIP44_COIN_TYPE_LTC &&
idx != BIP44_COIN_TYPE_TESTNET
)) {
whitelisted_keypath = false;
}
break;
case (BIP44_LEVEL_ACCOUNT):
if (is_prime != BIP44_ACCOUNT_HARDENED || idx > BIP44_ACCOUNT_MAX) {
whitelisted_keypath = false;
}
break;
case (BIP44_LEVEL_CHANGE):
if (is_prime != BIP44_CHANGE_HARDENED || idx > BIP44_CHANGE_MAX) {
whitelisted_keypath = false;
}
break;
case (BIP44_LEVEL_ADDRESS):
if (is_prime != BIP44_ADDRESS_HARDENED || idx > BIP44_ADDRESS_MAX) {
whitelisted_keypath = false;
}
break;
default:
whitelisted_keypath = false;
}
}
pch = strtok(NULL, delim);
path_level++;
}
if (!has_prm) {
if (!has_prime) {
goto err;
}
free(kp);
if (!whitelisted_keypath) {
return DBB_WARN_KEYPATH;
}
return DBB_OK;

err:
Expand Down Expand Up @@ -244,16 +308,18 @@ int wallet_generate_node(const char *passphrase, const char *entropy, HDNode *no
}


void wallet_report_xpub(const char *keypath, char *xpub)
int wallet_report_xpub(const char *keypath, char *xpub)
{
HDNode node;
int ret = DBB_ERROR;
if (wallet_seeded() == DBB_OK) {
if (wallet_generate_key(&node, keypath, wallet_get_master(),
wallet_get_chaincode()) == DBB_OK) {
ret = wallet_generate_key(&node, keypath, wallet_get_master(), wallet_get_chaincode());
if (ret != DBB_ERROR) {
hdnode_serialize_public(&node, xpub, 112);
}
}
utils_zero(&node, sizeof(HDNode));
return ret;
}


Expand Down Expand Up @@ -288,7 +354,7 @@ int wallet_check_pubkey(const char *pubkey, const char *keypath)
}

if (wallet_generate_key(&node, keypath, wallet_get_master(),
wallet_get_chaincode()) != DBB_OK) {
wallet_get_chaincode()) == DBB_ERROR) {
commander_clear_report();
commander_fill_report(cmd_str(CMD_checkpub), NULL, DBB_ERR_KEY_CHILD);
goto err;
Expand Down Expand Up @@ -329,7 +395,7 @@ int wallet_sign(const char *message, const char *keypath)
}

if (wallet_generate_key(&node, keypath, wallet_get_master(),
wallet_get_chaincode()) != DBB_OK) {
wallet_get_chaincode()) == DBB_ERROR) {
commander_clear_report();
commander_fill_report(cmd_str(CMD_sign), NULL, DBB_ERR_KEY_CHILD);
goto err;
Expand Down
Loading