Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fetch seed / RNG / attestation / user keys / appname / appversion from env at startup then stick with it #440

Merged
merged 12 commits into from
Jan 11, 2024
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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ 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] - 2024-??-??

### Added
- Attestation key or user private keys can now be configured with the new `--attestation-key`
and `--user-private-key` arguments (or `ATTESTATION_PRIVATE_KEY` and `USER_PRIVATE_KEY` through
environment variables). User certificates are correctly calculated signed from the user private
keys and the attestation key.

### Changed
- Seed, RNG, application name and version are now fetched from the environment when Speculos starts
then stored internally for further use, rather than fetched when needed during runtime. This
avoids several Speculos instances from messing up with each other's environment variables.

## [0.4.1] - 2023-12-19

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ link_libraries(ssl crypto dl blst)
add_subdirectory(src)

if (BUILD_TESTING)
add_subdirectory(tests/syscalls)
add_subdirectory(tests/c/)
endif()

add_custom_target(
Expand Down
11 changes: 10 additions & 1 deletion speculos/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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. '
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ add_library(emu
emulate_blue_2.2.5.c
emulate_lnsp_1.0.c
emulate_unified_sdk.c
environment.c
svc.c)

add_dependencies(emu openssl)
Expand Down
16 changes: 2 additions & 14 deletions src/bolos/cx.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,20 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#include "emulate.h"
#include "environment.h"

static bool initialized = false;

static unsigned int get_rng_seed_from_env(const char *name)
{
char *p;

p = getenv(name);
if (p != NULL) {
return atoi(p);
} else {
return time(NULL);
}
}

/* not secure, but this is clearly not the goal of this emulator */
unsigned long sys_cx_rng(uint8_t *buffer, unsigned int length)
{
unsigned int i;

if (!initialized) {
srand(get_rng_seed_from_env("RNG_SEED"));
srand(env_get_rng());
initialized = true;
}

Expand Down
1 change: 0 additions & 1 deletion src/bolos/cx_ec.c
Original file line number Diff line number Diff line change
Expand Up @@ -1652,7 +1652,6 @@ int sys_cx_ecdsa_sign(const cx_ecfp_private_key_t *key, int mode,
BIGNUM *normalized_s = BN_new();
ECDSA_SIG_get0(ecdsa_sig, &r, &s);
if ((mode & CX_NO_CANONICAL) == 0 && BN_cmp(s, halfn) > 0) {
fprintf(stderr, "cx_ecdsa_sign: normalizing s > n/2\n");
BN_sub(normalized_s, n, s);
if (info != NULL) {
*info ^= CX_ECCINFO_PARITY_ODD; // Inverse the bit
Expand Down
65 changes: 11 additions & 54 deletions src/bolos/endorsement.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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(
Expand All @@ -140,7 +96,8 @@ 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);
Expand Down
55 changes: 2 additions & 53 deletions src/bolos/os.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
#include <unistd.h>

#include "emulate.h"
#include "environment.h"
#include "svc.h"

#define OS_SETTING_PLANEMODE_OLD 5
#define OS_SETTING_PLANEMODE_NEW 6
#define OS_SETTING_SOUND 9

#define BOLOS_TAG_APPNAME 0x01
#define BOLOS_TAG_APPVERSION 0x02

#undef PATH_MAX
#define PATH_MAX 1024

Expand Down Expand Up @@ -69,56 +67,7 @@ unsigned long sys_os_registry_get_current_app_tag(unsigned int tag,
uint8_t *buffer,
size_t length)
{
const char *name;
const char *version;
const char *str;
char *str_dup = NULL;

if (length < 1) {
return 0;
}

name = "app";
version = "1.33.7";

str = getenv("SPECULOS_APPNAME");
if (str == NULL) {
str = getenv("SPECULOS_DETECTED_APPNAME");
}

if (str != NULL) {
str_dup = strdup(str);
if (str_dup != NULL) {
char *p = strstr(str_dup, ":");
if (p != NULL) {
*p = '\x00';
name = str_dup;
version = p + 1;
}
}
}

switch (tag) {
case BOLOS_TAG_APPNAME:
strncpy((char *)buffer, name, length);
length = MIN(length, strlen(name));
break;
case BOLOS_TAG_APPVERSION:
strncpy((char *)buffer, version, length);
length = MIN(length, strlen(version));
break;
default:
length = 0;
break;
}

buffer[length] = '\x00';

if (str_dup != NULL) {
free(str_dup);
}

return length;
return env_get_app_tag((char *)buffer, length, tag);
}

unsigned long sys_os_lib_call(unsigned long *call_parameters)
Expand Down
72 changes: 2 additions & 70 deletions src/bolos/os_bip32.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,15 @@
#include "cx.h"
#include "cx_utils.h"
#include "emulate.h"
#include "environment.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' };

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 = env_get_seed(seed, sizeof(seed));

if (mode == HDW_SLIP21) {
ret = hdw_slip21(sk, sk_length, seed, seed_size, (const uint8_t *)path,
Expand Down
Loading
Loading