diff --git a/CHANGELOG.md b/CHANGELOG.md index c356b280..a37c7578 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.0] - 2023-??-?? + +### Added +- Seed is fetched from the environment during the Speculos launch and stored internally for further + use. This avoids several Speculos instances from messing up with each other's seeds. + ## [0.4.0] - 2023-12-04 ### Fixed diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f82179e..605a56fb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -56,6 +56,7 @@ add_library(emu emulate_blue_2.2.5.c emulate_lnsp_1.0.c emulate_unified_sdk.c + seed.c svc.c) add_dependencies(emu openssl) diff --git a/src/bolos/os_bip32.c b/src/bolos/os_bip32.c index 4b29f8b4..55d5abb0 100644 --- a/src/bolos/os_bip32.c +++ b/src/bolos/os_bip32.c @@ -9,24 +9,15 @@ #include "cx.h" #include "cx_utils.h" #include "emulate.h" +#include "seed.h" #define BIP32_HARDEN_MASK 0x80000000 #define BIP32_SECP_SEED_LENGTH 12 -#define MAX_SEED_SIZE 64 #define cx_ecfp_generate_pair sys_cx_ecfp_generate_pair #define cx_ecfp_init_private_key sys_cx_ecfp_init_private_key #define cx_ecdsa_init_private_key cx_ecfp_init_private_key -/* glory promote mansion idle axis finger extra february uncover one trip - * resource lawn turtle enact monster seven myth punch hobby comfort wild raise - * skin */ -static uint8_t default_seed[MAX_SEED_SIZE] = - "\xb1\x19\x97\xfa\xff\x42\x0a\x33\x1b\xb4\xa4\xff\xdc\x8b\xdc\x8b\xa7\xc0" - "\x17\x32\xa9\x9a\x30\xd8\x3d\xbb\xeb\xd4\x69\x66\x6c\x84\xb4\x7d\x09\xd3" - "\xf5\xf4\x72\xb3\xb9\x38\x4a\xc6\x34\xbe\xba\x2a\x44\x0b\xa3\x6e\xc7\x66" - "\x11\x44\x13\x2f\x35\xe2\x06\x87\x35\x64"; - static uint8_t const BIP32_SECP_SEED[] = { 'B', 'i', 't', 'c', 'o', 'i', 'n', ' ', 's', 'e', 'e', 'd' }; @@ -166,65 +157,6 @@ static void expand_seed(cx_curve_t curve, const uint8_t *sk, size_t sk_length, } } -int unhex(uint8_t *dst, size_t dst_size, const char *src, size_t src_size) -{ - unsigned int i; - uint8_t acc; - int8_t c; - - acc = 0; - for (i = 0; i < src_size && (i >> 1) < dst_size; i++) { - c = src[i]; - switch (c) { - case '0' ... '9': - acc = (acc << 4) + c - '0'; - break; - case 'a' ... 'f': - acc = (acc << 4) + c - 'a' + 10; - break; - case 'A' ... 'F': - acc = (acc << 4) + c - 'A' + 10; - break; - default: - return -1; - } - - if (i % 2 != 0) { - dst[i >> 1] = acc; - acc = 0; - } - } - - if (i != src_size) { - return -1; - } - - return src_size / 2; -} - -size_t get_seed_from_env(const char *name, uint8_t *seed, size_t max_size) -{ - ssize_t seed_size; - char *p; - - p = getenv(name); - if (p != NULL) { - seed_size = unhex(seed, max_size, p, strlen(p)); - if (seed_size < 0) { - warnx("invalid seed passed through %s environment variable", name); - p = NULL; - } - } - - if (p == NULL) { - warnx("using default seed"); - memcpy(seed, default_seed, sizeof(default_seed)); - seed_size = sizeof(default_seed); - } - - return seed_size; -} - static int hdw_bip32_ed25519(extended_private_key *key, const uint32_t *path, size_t length, uint8_t *private_key, uint8_t *chain) @@ -512,7 +444,7 @@ unsigned long sys_os_perso_derive_node_with_seed_key( sk_length = seed_key_length; } - seed_size = get_seed_from_env("SPECULOS_SEED", seed, sizeof(seed)); + seed_size = get_seed(seed, sizeof(seed)); if (mode == HDW_SLIP21) { ret = hdw_slip21(sk, sk_length, seed, seed_size, (const uint8_t *)path, diff --git a/src/bolos/os_bip32.h b/src/bolos/os_bip32.h index 57e0629d..743cddea 100644 --- a/src/bolos/os_bip32.h +++ b/src/bolos/os_bip32.h @@ -16,5 +16,3 @@ typedef struct { void expand_seed_bip32(const cx_curve_domain_t *domain, uint8_t *seed, unsigned int seed_length, extended_private_key *key); -int unhex(uint8_t *dst, size_t dst_size, const char *src, size_t src_size); -size_t get_seed_from_env(const char *name, uint8_t *seed, size_t max_size); diff --git a/src/bolos/os_eip2333.c b/src/bolos/os_eip2333.c index a753a35c..017a4f10 100644 --- a/src/bolos/os_eip2333.c +++ b/src/bolos/os_eip2333.c @@ -7,7 +7,7 @@ #include "cx_utils.h" #include "emulate.h" #include "exception.h" -#include "os_bip32.h" +#include "seed.h" #define MAX_SEED_SIZE 64 #define CX_SHA256_SIZE 32 @@ -142,7 +142,7 @@ unsigned long sys_os_perso_derive_eip2333(cx_curve_t curve, THROW(EXCEPTION); } - seed_size = get_seed_from_env("SPECULOS_SEED", seed, sizeof(seed)); + seed_size = get_seed(seed, sizeof(seed)); cx_derive_master_sk(seed, seed_size, sk); if (privateKey != NULL) { diff --git a/src/launcher.c b/src/launcher.c index 43c962a9..3344857f 100644 --- a/src/launcher.c +++ b/src/launcher.c @@ -14,6 +14,7 @@ #include "emulate.h" #include "fonts.h" +#include "seed.h" #include "svc.h" #define LOAD_ADDR ((void *)0x40000000) @@ -722,6 +723,7 @@ int main(int argc, char *argv[]) extra_rampage_size = 0; fprintf(stderr, "[*] speculos launcher revision: " GIT_REVISION "\n"); + init_seed(); while ((opt = getopt(argc, argv, "c:tr:s:m:k:a:f:")) != -1) { switch (opt) { diff --git a/src/seed.c b/src/seed.c new file mode 100644 index 00000000..37c71c99 --- /dev/null +++ b/src/seed.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include + +#include "seed.h" + + +/* glory promote mansion idle axis finger extra february uncover one trip + * resource lawn turtle enact monster seven myth punch hobby comfort wild raise + * skin */ +static uint8_t default_seed[MAX_SEED_SIZE] = + "\xb1\x19\x97\xfa\xff\x42\x0a\x33\x1b\xb4\xa4\xff\xdc\x8b\xdc\x8b\xa7\xc0" + "\x17\x32\xa9\x9a\x30\xd8\x3d\xbb\xeb\xd4\x69\x66\x6c\x84\xb4\x7d\x09\xd3" + "\xf5\xf4\x72\xb3\xb9\x38\x4a\xc6\x34\xbe\xba\x2a\x44\x0b\xa3\x6e\xc7\x66" + "\x11\x44\x13\x2f\x35\xe2\x06\x87\x35\x64"; + +const char *SEED_ENV_NAME = "SPECULOS_SEED"; +typedef struct { + size_t size; + uint8_t seed[MAX_SEED_SIZE]; +} seed_t; +static seed_t actual_seed = {0}; + + +static int unhex(uint8_t *dst, size_t dst_size, const char *src, + size_t src_size) +{ + unsigned int i; + uint8_t acc; + int8_t c; + + acc = 0; + for (i = 0; i < src_size && (i >> 1) < dst_size; i++) { + c = src[i]; + switch (c) { + case '0' ... '9': + acc = (acc << 4) + c - '0'; + break; + case 'a' ... 'f': + acc = (acc << 4) + c - 'a' + 10; + break; + case 'A' ... 'F': + acc = (acc << 4) + c - 'A' + 10; + break; + default: + return -1; + } + + if (i % 2 != 0) { + dst[i >> 1] = acc; + acc = 0; + } + } + + if (i != src_size) { + return -1; + } + + return src_size / 2; +} + +void init_seed() +{ + ssize_t size; + char *p; + + p = getenv(SEED_ENV_NAME); + if (p != NULL) { + size = unhex(actual_seed.seed, sizeof(actual_seed.seed), p, strlen(p)); + if (size < 0) { + warnx("invalid seed passed through %s environment variable", + SEED_ENV_NAME); + p = NULL; + } else { + fprintf(stderr, "[*] Seed initialized from environment: '%s'\n", p); + } + } + + if (p == NULL) { + warnx("using default seed"); + memcpy(actual_seed.seed, default_seed, sizeof(default_seed)); + size = sizeof(default_seed); + fprintf(stderr, "[*] Seed initialized from default value: '%s'\n", + default_seed); + } + actual_seed.size = size; +} + + +size_t get_seed(uint8_t *seed, size_t max_size) +{ + memcpy(seed, actual_seed.seed, max_size); + return (actual_seed.size < max_size) ? actual_seed.size : max_size; +} diff --git a/src/seed.h b/src/seed.h new file mode 100644 index 00000000..cd6151d8 --- /dev/null +++ b/src/seed.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +#define MAX_SEED_SIZE 64 + +size_t get_seed(uint8_t *seed, size_t max_size); +void init_seed(); diff --git a/tests/syscalls/test_bip32.c b/tests/syscalls/test_bip32.c index 95791712..290821a0 100644 --- a/tests/syscalls/test_bip32.c +++ b/tests/syscalls/test_bip32.c @@ -12,6 +12,7 @@ #include "bolos/os_bip32.h" #include "emulate.h" #include "utils.h" +#include "seed.h" #define MAX_CHAIN_LEN 5 @@ -578,6 +579,7 @@ static void test_bip32_vector(const bip32_test_vector *v) memset(&extkey, 0, sizeof(extkey)); assert_int_equal(setenv("SPECULOS_SEED", v->seed, 1), 0); + init_seed(); for (i = 0; i < v->chain_len; i++) { path[i] = v->chain[i].index; @@ -599,7 +601,7 @@ static void test_bip32_vector(const bip32_test_vector *v) static void test_bip32(void **state __attribute__((unused))) { size_t i; - + init_seed(); for (i = 0; i < ARRAY_SIZE(test_vectors); i++) { test_bip32_vector(&test_vectors[i]); } @@ -614,6 +616,7 @@ static void test_bolos_vector(const struct bolos_vector *v) size_t sk_length; ssize_t path_len; uint8_t *p; + init_seed(); switch (v->mode) { case 0: diff --git a/tests/syscalls/test_eip2333.c b/tests/syscalls/test_eip2333.c index 263aca2f..4dbf145d 100644 --- a/tests/syscalls/test_eip2333.c +++ b/tests/syscalls/test_eip2333.c @@ -12,6 +12,7 @@ #include "bolos/cx_utils.h" #include "emulate.h" #include "utils.h" +#include "seed.h" #define MAX_PATH_LEN 10 @@ -228,6 +229,7 @@ static void test_eip_vector(const eip2333_test_vector *v) int path_len; assert_int_equal(setenv("SPECULOS_SEED", v->seed, 1), 0); + init_seed(); path_len = get_path(v->path, path, MAX_PATH_LEN); assert_int_equal(path_len, 1); @@ -245,6 +247,7 @@ static void test_eip2333_derive(void **state __attribute__((unused))) unsigned int i; assert_int_equal(setenv("SPECULOS_SEED", default_seed, 1), 0); + init_seed(); for (i = 0; i < ARRAY_SIZE(test_vectors); i++) { test_eip_vector(&test_vectors[i]); @@ -257,6 +260,7 @@ static void test_bolos_vector(const bolos_test_vector *v) unsigned int path[10]; int path_len; + init_seed(); path_len = get_path(v->path, path, MAX_PATH_LEN); assert_int_equal(path_len, v->path_len); diff --git a/tests/syscalls/test_slip21.c b/tests/syscalls/test_slip21.c index 27e2a8b7..3d92a211 100644 --- a/tests/syscalls/test_slip21.c +++ b/tests/syscalls/test_slip21.c @@ -9,6 +9,7 @@ #include "bolos/cx.h" #include "bolos/os_bip32.h" #include "emulate.h" +#include "seed.h" void test_slip21(void **state __attribute__((unused))) { @@ -30,6 +31,8 @@ void test_slip21(void **state __attribute__((unused))) 1), 0); + init_seed(); + sys_os_perso_derive_node_with_seed_key(HDW_SLIP21, CX_CURVE_SECP256K1, (uint32_t *)SLIP77_LABEL, 10, key, NULL, NULL, 0);