diff --git a/speculos/main.py b/speculos/main.py index 0b80b42c..da531853 100644 --- a/speculos/main.py +++ b/speculos/main.py @@ -227,6 +227,11 @@ def run_qemu(s1: socket.socket, s2: socket.socket, args: argparse.Namespace) -> if args.deterministic_rng: os.environ['RNG_SEED'] = args.deterministic_rng + if args.user_private_key: + os.environ['USER_PRIVATE_KEY'] = args.user_private_key + if args.attestation_key: + os.environ['ATTESTATION_PRIVATE_KEY'] = args.attestation_key + logger.debug(f"executing qemu: {argv}") try: os.execvp(argv[0], argv) @@ -260,8 +265,12 @@ def main(prog=None) -> int: 'to specify a path') parser.add_argument('--color', default='MATTE_BLACK', choices=list(display.COLORS.keys()), help='Nano color') parser.add_argument('-d', '--debug', action='store_true', help='Wait gdb connection to port 1234') - parser.add_argument('--deterministic-rng', default="", help='Seed the rng with a given value to produce ' + parser.add_argument('--deterministic-rng', default='', help='Seed the rng with a given value to produce ' 'deterministic randomness') + parser.add_argument('--user-private-key', default='', + help='32B in hex format, will be used as the user private keys') + parser.add_argument('--attestation-key', default='', help='32B in hex format, will be used as the private ' + 'attestation key') parser.add_argument('-k', '--sdk', type=str, help='SDK version') parser.add_argument('-a', '--apiLevel', type=str, help='Api level') parser.add_argument('-l', '--library', default=[], action='append', help='Additional library (eg. ' diff --git a/src/bolos/cx.c b/src/bolos/cx.c index a3179cc4..0a7fc990 100644 --- a/src/bolos/cx.c +++ b/src/bolos/cx.c @@ -17,7 +17,7 @@ unsigned long sys_cx_rng(uint8_t *buffer, unsigned int length) unsigned int i; if (!initialized) { - srand(get_env_rng()); + srand(env_get_rng()); initialized = true; } diff --git a/src/bolos/endorsement.c b/src/bolos/endorsement.c index 27141601..a3362332 100644 --- a/src/bolos/endorsement.c +++ b/src/bolos/endorsement.c @@ -3,51 +3,10 @@ #include "bolos/exception.h" #include "cx.h" #include "emulate.h" +#include "environment.h" #define cx_ecdsa_init_public_key sys_cx_ecfp_init_public_key -// TODO: all keys are currently hardcoded - -static cx_ecfp_private_key_t user_private_key_1 = { - CX_CURVE_256K1, - 32, - { 0xe1, 0x5e, 0x01, 0xd4, 0x70, 0x82, 0xf0, 0xea, 0x47, 0x71, 0xc9, - 0x9f, 0xe3, 0x12, 0xf9, 0xd7, 0x00, 0x93, 0xc8, 0x9a, 0xf4, 0x77, - 0x87, 0xfd, 0xf8, 0x2e, 0x03, 0x1f, 0x67, 0x28, 0xb7, 0x10 }, -}; - -static cx_ecfp_private_key_t user_private_key_2 = { - CX_CURVE_256K1, - 32, - { 0xe1, 0x5e, 0x01, 0xd4, 0x70, 0x82, 0xf0, 0xea, 0x47, 0x71, 0xc9, - 0x9f, 0xe3, 0x12, 0xf9, 0xd7, 0x00, 0x93, 0xc8, 0x9a, 0xf4, 0x77, - 0x87, 0xfd, 0xf8, 0x2e, 0x03, 0x1f, 0x67, 0x28, 0xb7, 0x10 }, -}; - -// user_private_key_1 signed by test owner private key -// "138fb9b91da745f12977a2b46f0bce2f0418b50fcb76631baf0f08ceefdb5d57" -static uint8_t user_certificate_1[] = { - 0x30, 0x45, 0x02, 0x21, 0x00, 0xbf, 0x23, 0x7e, 0x5b, 0x40, 0x06, 0x14, - 0x17, 0xf6, 0x62, 0xa6, 0xd0, 0x8a, 0x4b, 0xde, 0x1f, 0xe3, 0x34, 0x3b, - 0xd8, 0x70, 0x8c, 0xed, 0x04, 0x6c, 0x84, 0x17, 0x49, 0x5a, 0xd3, 0x6c, - 0xcf, 0x02, 0x20, 0x3d, 0x39, 0xa5, 0x32, 0xee, 0xca, 0xdf, 0xf6, 0xdf, - 0x20, 0x53, 0xe4, 0xab, 0x98, 0x96, 0xaa, 0x00, 0xf3, 0xbe, 0xf1, 0x5c, - 0x4b, 0xd1, 0x1c, 0x53, 0x66, 0x1e, 0x54, 0xfe, 0x5e, 0x2f, 0xf4 -}; -static const uint8_t user_certificate_1_length = sizeof(user_certificate_1); - -// user_private_key_2 signed by test owner private key -// "138fb9b91da745f12977a2b46f0bce2f0418b50fcb76631baf0f08ceefdb5d57" -static uint8_t user_certificate_2[] = { - 0x30, 0x45, 0x02, 0x21, 0x00, 0xbf, 0x23, 0x7e, 0x5b, 0x40, 0x06, 0x14, - 0x17, 0xf6, 0x62, 0xa6, 0xd0, 0x8a, 0x4b, 0xde, 0x1f, 0xe3, 0x34, 0x3b, - 0xd8, 0x70, 0x8c, 0xed, 0x04, 0x6c, 0x84, 0x17, 0x49, 0x5a, 0xd3, 0x6c, - 0xcf, 0x02, 0x20, 0x3d, 0x39, 0xa5, 0x32, 0xee, 0xca, 0xdf, 0xf6, 0xdf, - 0x20, 0x53, 0xe4, 0xab, 0x98, 0x96, 0xaa, 0x00, 0xf3, 0xbe, 0xf1, 0x5c, - 0x4b, 0xd1, 0x1c, 0x53, 0x66, 0x1e, 0x54, 0xfe, 0x5e, 0x2f, 0xf4 -}; -static uint8_t user_certificate_2_length; - unsigned int sys_os_endorsement_get_code_hash(uint8_t *buffer) { memcpy(buffer, "12345678abcdef0000fedcba87654321", 32); @@ -61,10 +20,10 @@ unsigned long sys_os_endorsement_get_public_key(uint8_t index, uint8_t *buffer) switch (index) { case 1: - privateKey = &user_private_key_1; + privateKey = env_get_user_private_key(1); break; case 2: - privateKey = &user_private_key_2; + privateKey = env_get_user_private_key(2); break; default: THROW(EXCEPTION); @@ -93,30 +52,27 @@ unsigned int sys_os_endorsement_get_public_key_certificate(unsigned char index, unsigned char *buffer) { - unsigned char *certificate; - unsigned char length; + env_user_certificate_t* certificate; switch (index) { case 1: - length = user_certificate_1_length; - certificate = user_certificate_1; + certificate = env_get_user_certificate(1); break; case 2: - length = user_certificate_2_length; - certificate = user_certificate_2; + certificate = env_get_user_certificate(2); break; default: THROW(EXCEPTION); break; } - if (length == 0) { + if (certificate->length == 0) { THROW(EXCEPTION); } - memcpy(buffer, certificate, length); + memcpy(buffer, certificate->buffer, certificate->length); - return length; + return certificate->length; } unsigned int sys_os_endorsement_get_public_key_certificate_new( @@ -140,7 +96,7 @@ unsigned long sys_os_endorsement_key1_sign_data(uint8_t *data, sys_cx_hash((cx_hash_t *)&sha256, CX_LAST, hash, sizeof(hash), hash, 32); /* XXX: CX_RND_TRNG is set but actually ignored by speculos' * sys_cx_ecdsa_sign implementation */ - sys_cx_ecdsa_sign(&user_private_key_1, CX_LAST | CX_RND_TRNG, CX_SHA256, hash, + sys_cx_ecdsa_sign(env_get_user_private_key(1), CX_LAST | CX_RND_TRNG, CX_SHA256, hash, sizeof(hash), // size of SHA256 hash signature, 6 + 33 * 2, /*3TL+2V*/ NULL); diff --git a/src/bolos/os_bip32.c b/src/bolos/os_bip32.c index e8ebe09c..161d331f 100644 --- a/src/bolos/os_bip32.c +++ b/src/bolos/os_bip32.c @@ -444,7 +444,7 @@ unsigned long sys_os_perso_derive_node_with_seed_key( sk_length = seed_key_length; } - seed_size = get_env_seed(seed, sizeof(seed)); + seed_size = env_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_eip2333.c b/src/bolos/os_eip2333.c index 042b47d4..2e8e050c 100644 --- a/src/bolos/os_eip2333.c +++ b/src/bolos/os_eip2333.c @@ -142,7 +142,7 @@ unsigned long sys_os_perso_derive_eip2333(cx_curve_t curve, THROW(EXCEPTION); } - seed_size = get_env_seed(seed, sizeof(seed)); + seed_size = env_get_seed(seed, sizeof(seed)); cx_derive_master_sk(seed, seed_size, sk); if (privateKey != NULL) { diff --git a/src/environment.c b/src/environment.c index 70e366ba..e1e3650e 100644 --- a/src/environment.c +++ b/src/environment.c @@ -1,12 +1,19 @@ #include +#include #include #include #include #include #include +#include "bolos/cx.h" +#include "bolos/endorsement.h" +#include "bolos/exception.h" +#include "emulate.h" #include "environment.h" +/* SEED VARIABLES */ + /* glory promote mansion idle axis finger extra february uncover one trip * resource lawn turtle enact monster seven myth punch hobby comfort wild raise * skin */ @@ -16,15 +23,74 @@ static uint8_t default_seed[MAX_SEED_SIZE] = "\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"; -const char *RNG_ENV_NAME = "RNG_SEED"; +static const char *SEED_ENV_NAME = "SPECULOS_SEED"; static struct { size_t size; uint8_t seed[MAX_SEED_SIZE]; } actual_seed = { 0 }; + +/* RNG VARIABLES */ + +static const char *RNG_ENV_NAME = "RNG_SEED"; static unsigned int actual_rng = 0; +/* ENDORSEMENT VARIABLES */ + +static const char *USER_KEY_ENV_NAME = "USER_PRIVATE_KEY"; +static const char *ATTESTATION_ENV_NAME = "ATTESTATION_PRIVATE_KEY"; + +static cx_ecfp_private_key_t attestation_key = { + CX_CURVE_256K1, + 32, + { 0x13, 0x8f, 0xb9, 0xb9, 0x1d, 0xa7, 0x45, 0xf1, 0x29, 0x77, 0xa2, + 0xb4, 0x6f, 0x0b, 0xce, 0x2f, 0x04, 0x18, 0xb5, 0x0f, 0xcb, 0x76, + 0x63, 0x1b, 0xaf, 0x0f, 0x08, 0xce, 0xef, 0xdb, 0x5d, 0x57}, +}; + +static cx_ecfp_private_key_t user_private_key_1 = { + CX_CURVE_256K1, + 32, + { 0xe1, 0x5e, 0x01, 0xd4, 0x70, 0x82, 0xf0, 0xea, 0x47, 0x71, 0xc9, + 0x9f, 0xe3, 0x12, 0xf9, 0xd7, 0x00, 0x93, 0xc8, 0x9a, 0xf4, 0x77, + 0x87, 0xfd, 0xf8, 0x2e, 0x03, 0x1f, 0x67, 0x28, 0xb7, 0x10 }, +}; + +static cx_ecfp_private_key_t user_private_key_2 = { + CX_CURVE_256K1, + 32, + { 0xe1, 0x5e, 0x01, 0xd4, 0x70, 0x82, 0xf0, 0xea, 0x47, 0x71, 0xc9, + 0x9f, 0xe3, 0x12, 0xf9, 0xd7, 0x00, 0x93, 0xc8, 0x9a, 0xf4, 0x77, + 0x87, 0xfd, 0xf8, 0x2e, 0x03, 0x1f, 0x67, 0x28, 0xb7, 0x10 }, +}; + +static env_user_certificate_t user_certificate_1 = { + 71, + // user_private_key_1 signed by test owner private key + // "138fb9b91da745f12977a2b46f0bce2f0418b50fcb76631baf0f08ceefdb5d57" + { 0x30, 0x45, 0x02, 0x21, 0x00, 0xbf, 0x23, 0x7e, 0x5b, 0x40, 0x06, 0x14, + 0x17, 0xf6, 0x62, 0xa6, 0xd0, 0x8a, 0x4b, 0xde, 0x1f, 0xe3, 0x34, 0x3b, + 0xd8, 0x70, 0x8c, 0xed, 0x04, 0x6c, 0x84, 0x17, 0x49, 0x5a, 0xd3, 0x6c, + 0xcf, 0x02, 0x20, 0x3d, 0x39, 0xa5, 0x32, 0xee, 0xca, 0xdf, 0xf6, 0xdf, + 0x20, 0x53, 0xe4, 0xab, 0x98, 0x96, 0xaa, 0x00, 0xf3, 0xbe, 0xf1, 0x5c, + 0x4b, 0xd1, 0x1c, 0x53, 0x66, 0x1e, 0x54, 0xfe, 0x5e, 0x2f, 0xf4 }, +}; + +static env_user_certificate_t user_certificate_2 = { + 71, + // user_private_key_2 signed by test owner private key + // "138fb9b91da745f12977a2b46f0bce2f0418b50fcb76631baf0f08ceefdb5d57" + { 0x30, 0x45, 0x02, 0x21, 0x00, 0xbf, 0x23, 0x7e, 0x5b, 0x40, 0x06, 0x14, + 0x17, 0xf6, 0x62, 0xa6, 0xd0, 0x8a, 0x4b, 0xde, 0x1f, 0xe3, 0x34, 0x3b, + 0xd8, 0x70, 0x8c, 0xed, 0x04, 0x6c, 0x84, 0x17, 0x49, 0x5a, 0xd3, 0x6c, + 0xcf, 0x02, 0x20, 0x3d, 0x39, 0xa5, 0x32, 0xee, 0xca, 0xdf, 0xf6, 0xdf, + 0x20, 0x53, 0xe4, 0xab, 0x98, 0x96, 0xaa, 0x00, 0xf3, 0xbe, 0xf1, 0x5c, + 0x4b, 0xd1, 0x1c, 0x53, 0x66, 0x1e, 0x54, 0xfe, 0x5e, 0x2f, 0xf4 + }, +}; + +/* UTILS */ + static int unhex(uint8_t *dst, size_t dst_size, const char *src, size_t src_size) { @@ -62,7 +128,7 @@ static int unhex(uint8_t *dst, size_t dst_size, const char *src, return src_size / 2; } -void init_env_seed() +static void env_init_seed() { ssize_t size; char *p; @@ -89,25 +155,148 @@ void init_env_seed() actual_seed.size = size; } -size_t get_env_seed(uint8_t *seed, size_t max_size) +size_t env_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; } -void init_env_rng() +static void env_init_rng() { char *p; p = getenv(RNG_ENV_NAME); if (p != NULL) { actual_rng = atoi(p); - fprintf(stderr, "[*] Seed initialized from environment: '%ud'\n", actual_rng); + fprintf(stderr, "[*] RNG initialized from environment: '%u'\n", actual_rng); } else { actual_rng = time(NULL); - fprintf(stderr, "[*] Seed initialized by default: '%ud'\n", actual_rng); + fprintf(stderr, "[*] RNG initialized by default (time(NULL)): '%u'\n", + actual_rng); } } -unsigned int get_env_rng() { +unsigned int env_get_rng() +{ return actual_rng; } + +static bool env_init_user_hex_private_key(const char *ENV_NAME, cx_ecfp_private_key_t *dst) +{ + ssize_t size; + char *p; + unsigned char tmp[dst->d_len]; + + p = getenv(ENV_NAME); + if (p != NULL) { + size = unhex(tmp, dst->d_len, p, strlen(p)); + if (size < 0) { + warnx( + "invalid user key passed through %s environment variable: not an hex string", + USER_KEY_ENV_NAME); + p = NULL; + } else if ((unsigned int)size != dst->d_len) { + warnx( + "invalid size for user key passed through %s environment variable: expecting '%u', got '%i'", + USER_KEY_ENV_NAME, dst->d_len, size); + p = NULL; + } + } + + if (p == NULL) { + fprintf(stderr, "[*] Private key ('%s') initialized with default value: '%p'\n", + ENV_NAME, dst->d); + return false; + } else { + memcpy(dst->d, tmp, dst->d_len); + fprintf(stderr, + "[*] Private key ('%s') initialized from environment: '%s'\n", + ENV_NAME, p); + return true; + } +} + +static void env_init_user_certificate(unsigned int index) +{ + env_user_certificate_t *certificate; + uint8_t pkey[65] = { 0 }; + sys_os_endorsement_get_public_key(index, &pkey[0]); + uint8_t hash[32] = { 0 }; + cx_sha256_t sha256; + + switch (index) { + case 1: + certificate = &user_certificate_1; + break; + case 2: + certificate = &user_certificate_2; + break; + default: + THROW(EXCEPTION); + break; + } + + sys_cx_sha256_init(&sha256); + sys_cx_hash((cx_hash_t *)&sha256, 0, (unsigned char *)"\xfe", 1, NULL, 0); + sys_cx_hash((cx_hash_t *)&sha256, CX_LAST, pkey, sizeof(pkey), + hash, sizeof(hash)); + sys_cx_ecdsa_sign(&attestation_key, CX_RND_TRNG, CX_SHA256, + hash, sizeof(hash), + certificate->buffer, 6 + 33 * 2, + NULL); + certificate->length = certificate->buffer[1] + 2; + + fprintf(stderr, "[*] User certificate %u initialized (size: %u)\n", + index, certificate->length); +} + +static void env_init_endorsement() +{ + bool attestation_changed = + env_init_user_hex_private_key(ATTESTATION_ENV_NAME, &attestation_key); + if (env_init_user_hex_private_key(USER_KEY_ENV_NAME, &user_private_key_1) || + attestation_changed) { + env_init_user_certificate(1); + } + if (env_init_user_hex_private_key(USER_KEY_ENV_NAME, &user_private_key_2) || + attestation_changed) { + env_init_user_certificate(2); + } +} + +cx_ecfp_private_key_t* env_get_user_private_key(unsigned int index) +{ + switch (index) { + case 1: + return &user_private_key_1; + break; + case 2: + return &user_private_key_2; + break; + default: + THROW(EXCEPTION); + break; + } +} + + +env_user_certificate_t* env_get_user_certificate(unsigned int index) +{ + switch (index) { + case 1: + return &user_certificate_1; + break; + case 2: + return &user_certificate_2; + break; + default: + THROW(EXCEPTION); + break; + } +} + +void init_environment() +{ + env_init_seed(); + env_init_rng(); + env_init_endorsement(); +} diff --git a/src/environment.h b/src/environment.h index b23b0c63..e9eeed5c 100644 --- a/src/environment.h +++ b/src/environment.h @@ -4,8 +4,14 @@ #define MAX_SEED_SIZE 64 -void init_env_seed(); -size_t get_env_seed(uint8_t *seed, size_t max_size); +typedef struct { + uint8_t length; + uint8_t buffer[]; +} env_user_certificate_t; -void init_env_rng(); -unsigned int get_env_rng(); +size_t env_get_seed(uint8_t *seed, size_t max_size); +unsigned int env_get_rng(); +cx_ecfp_private_key_t* env_get_user_private_key(unsigned int index); +env_user_certificate_t* env_get_user_certificate(unsigned int index); + +void init_environment(); diff --git a/src/launcher.c b/src/launcher.c index c14cd15a..8ded5805 100644 --- a/src/launcher.c +++ b/src/launcher.c @@ -723,8 +723,8 @@ int main(int argc, char *argv[]) extra_rampage_size = 0; fprintf(stderr, "[*] speculos launcher revision: " GIT_REVISION "\n"); - init_env_seed(); - init_env_rng(); + + init_environment(); while ((opt = getopt(argc, argv, "c:tr:s:m:k:a:f:")) != -1) { switch (opt) { diff --git a/tests/syscalls/test_bip32.c b/tests/syscalls/test_bip32.c index f486efbf..e2dd82eb 100644 --- a/tests/syscalls/test_bip32.c +++ b/tests/syscalls/test_bip32.c @@ -579,7 +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_env_seed(); + init_environment(); for (i = 0; i < v->chain_len; i++) { path[i] = v->chain[i].index; @@ -601,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_env_seed(); + init_environment(); for (i = 0; i < ARRAY_SIZE(test_vectors); i++) { test_bip32_vector(&test_vectors[i]); } @@ -616,7 +616,7 @@ static void test_bolos_vector(const struct bolos_vector *v) size_t sk_length; ssize_t path_len; uint8_t *p; - init_env_seed(); + init_environment(); switch (v->mode) { case 0: diff --git a/tests/syscalls/test_eip2333.c b/tests/syscalls/test_eip2333.c index 0f96ef03..1df789be 100644 --- a/tests/syscalls/test_eip2333.c +++ b/tests/syscalls/test_eip2333.c @@ -229,7 +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_env_seed(); + init_environment(); path_len = get_path(v->path, path, MAX_PATH_LEN); assert_int_equal(path_len, 1); @@ -247,7 +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_env_seed(); + init_environment(); for (i = 0; i < ARRAY_SIZE(test_vectors); i++) { test_eip_vector(&test_vectors[i]); @@ -260,7 +260,7 @@ static void test_bolos_vector(const bolos_test_vector *v) unsigned int path[10]; int path_len; - init_env_seed(); + init_environment(); 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 671108ed..a994be05 100644 --- a/tests/syscalls/test_slip21.c +++ b/tests/syscalls/test_slip21.c @@ -31,7 +31,7 @@ void test_slip21(void **state __attribute__((unused))) 1), 0); - init_env_seed(); + init_environment(); sys_os_perso_derive_node_with_seed_key(HDW_SLIP21, CX_CURVE_SECP256K1, (uint32_t *)SLIP77_LABEL, 10, key,