Skip to content

Commit

Permalink
Merge pull request LedgerHQ#440 from LedgerHQ/persistent_seed
Browse files Browse the repository at this point in the history
Fetch seed / RNG / attestation / user keys / appname / appversion from env at startup then stick with it
  • Loading branch information
lpascal-ledger authored Jan 11, 2024
2 parents cb26a5a + fccb51c commit 3559f56
Show file tree
Hide file tree
Showing 116 changed files with 757 additions and 297 deletions.
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

0 comments on commit 3559f56

Please sign in to comment.