From 01cb43f7e87cc806963a74cbe0fcb4155f23a2a3 Mon Sep 17 00:00:00 2001 From: "Peter D. Gray" Date: Fri, 11 Mar 2022 13:42:49 -0500 Subject: [PATCH] Seed RNG with RNG from both SE's --- shared/callgate.py | 9 +++++++++ shared/mk4.py | 16 ++++++++++++++++ stm32/mk4-bootloader/ae.c | 29 ++++++++++++++++++++++++++--- stm32/mk4-bootloader/ae.h | 3 ++- stm32/mk4-bootloader/dispatch.c | 27 +++++++++++++++++++++++++++ stm32/mk4-bootloader/se2.c | 24 ++++++++++++++++-------- stm32/mk4-bootloader/se2.h | 3 +++ unix/variant/ckcc.py | 12 ++++++++++++ unix/variant/sim_mk4.py | 2 ++ 9 files changed, 113 insertions(+), 12 deletions(-) diff --git a/shared/callgate.py b/shared/callgate.py index 68725570..bacf305c 100644 --- a/shared/callgate.py +++ b/shared/callgate.py @@ -105,4 +105,13 @@ def mcu_key_usage(): ckcc.gate(25, arg, 0); return unpack('3I', arg) +def read_rng(source=2): + # return random bytes from a secure source + # - first byte is # of valid random bytes + arg = bytearray(33) + rv = ckcc.gate(26, arg, source); + assert not rv + return arg[1:1+arg[0]] + + # EOF diff --git a/shared/mk4.py b/shared/mk4.py index 1d67963e..80ee9ee4 100644 --- a/shared/mk4.py +++ b/shared/mk4.py @@ -39,6 +39,19 @@ COLDCARD Virtual Disk # generally, leave it unmounted os.umount('/psram') +def rng_seeding(): + # seed our RNG with entropy from secure elements + import callgate, ngu, ustruct + + a = callgate.read_rng(1) # SE1 + b = callgate.read_rng(2) # SE2 + + n = ngu.hash.sha256d(a+b) + n, = ustruct.unpack('I', n[0:4]) + + ngu.random.reseed(n) + + def init0(): # called very early try: @@ -57,6 +70,9 @@ def init0(): import sim_display except: pass + # seed RNGs with entropy from secure elements + rng_seeding() + async def dev_enable_repl(*a): # Mk4: Enable serial port connection. You'll have to break case open. from ux import ux_show_story diff --git a/stm32/mk4-bootloader/ae.c b/stm32/mk4-bootloader/ae.c index adf6f2cb..2b326b16 100644 --- a/stm32/mk4-bootloader/ae.c +++ b/stm32/mk4-bootloader/ae.c @@ -670,11 +670,11 @@ ae_send_n(aeopcode_t opcode, uint8_t p1, uint16_t p2, const uint8_t *data, uint8 } #if 0 -// RISKY - Easy for Mitm to control value. - // ae_random() // // Get a fresh random number. +// +// RISKY - Easy for Mitm to control value. // int ae_random(uint8_t randout[32]) @@ -690,6 +690,29 @@ ae_random(uint8_t randout[32]) } #endif + +// ae_secure_random() +// +// Generate a random number, using nonces generated by chip and by us. +// Verify the result was not modified by MitM. +// + int +ae_secure_random(uint8_t randout[32]) +{ + // Generate a digest of pairing secret slot, which will include + // a nonce from chip. + int rv = ae_gendig_slot(KEYNUM_pairing, rom_secrets->pairing_secret, randout); + + // Verify digest was made using inputs we think. + if(rv || !ae_is_correct_tempkey(randout)) { + fatal_mitm(); + } + + // since that value is "tempkey" inside the secure element, it feels + // wrong to share that directly, so hash it up. + sha256_single(randout, 32, randout); +} + // ae_get_info() // // Do Info(p1=2) command, and return result. @@ -825,7 +848,7 @@ ae_is_correct_tempkey(const uint8_t expected_tempkey[32]) // // Check the chip produces a hash over various things the same way we would // meaning that we both know the shared secret and the state of stuff in -// the 508a is what we expect. +// the chip is what we expect. // int ae_checkmac_hard(uint8_t keynum, const uint8_t secret[32]) diff --git a/stm32/mk4-bootloader/ae.h b/stm32/mk4-bootloader/ae.h index e9ab91be..10d30e22 100644 --- a/stm32/mk4-bootloader/ae.h +++ b/stm32/mk4-bootloader/ae.h @@ -88,7 +88,8 @@ int ae_delay_time(aeopcode_t opcode); void ae_keep_alive(void); // Pick a fresh random number. -int ae_random(uint8_t randout[32]); +//int ae_random(uint8_t randout[32]); +int ae_secure_random(uint8_t randout[32]); // Pick a EC keypair and return public part; private saved. int ae_gen_ecc_key(uint8_t keynum, uint8_t pubkey_out[64]); diff --git a/stm32/mk4-bootloader/dispatch.c b/stm32/mk4-bootloader/dispatch.c index 7f18ebdf..21cb183e 100644 --- a/stm32/mk4-bootloader/dispatch.c +++ b/stm32/mk4-bootloader/dispatch.c @@ -562,6 +562,33 @@ firewall_dispatch(int method_num, uint8_t *buf_io, int len_in, break; } + case 26: { + // Read some random bytes from various sources, like SE's. + REQUIRE_OUT(33); + + switch(arg2) { + case 1: // for SE1 + // LIMITATION: this has no MitM protection, subject to tampering + ae_setup(); + int rv = ae_secure_random(&buf_io[1]); + if(rv) fatal_mitm(); + buf_io[0] = 32; + break; + + case 2: // for SE2 + // secure, requires knowledge of pairing secret + se2_read_rng(&buf_io[1]); + buf_io[0] = 8; + break; + + default: + rv = ERANGE; + break; + } + + break; + } + #if 0 // p256r1 test code case 130: { // verify signature diff --git a/stm32/mk4-bootloader/se2.c b/stm32/mk4-bootloader/se2.c index e7f7938a..cbe6a6db 100644 --- a/stm32/mk4-bootloader/se2.c +++ b/stm32/mk4-bootloader/se2.c @@ -1273,8 +1273,6 @@ se2_pin_hash(uint8_t digest_io[32], uint32_t purpose) uint8_t tmp[32]; HMAC_CTX ctx; -//sdcard_light(true); - // HMAC(key=tpin_key, msg=given hash so far) hmac_sha256_init(&ctx); hmac_sha256_update(&ctx, digest_io, 32); @@ -1308,14 +1306,24 @@ se2_pin_hash(uint8_t digest_io[32], uint32_t purpose) hmac_sha256_update(&ctx, rx+2, 32); hmac_sha256_update(&ctx, digest_io, 32); hmac_sha256_final(&ctx, SE2_SECRETS->tpin_key, digest_io); -#if 0 -sdcard_light(false); +} - puts2("md: "); - hex_dump(digest_io, 32); +// se2_read_rng() +// +// Read some random bytes, which we know cannot be MitM'ed. +// + void +se2_read_rng(uint8_t value[8]) +{ + // funny business means MitM here + se2_setup(); + if(setjmp(error_env)) fatal_mitm(); - memset(digest_io, 0x1, 32); -#endif + // read a field with "RPS" bytes, and verify those were read true + uint8_t tmp[32]; + se2_read_page(PGN_ROM_OPTIONS, tmp, true); + + memcpy(value, &tmp[4], 8); } // EOF diff --git a/stm32/mk4-bootloader/se2.h b/stm32/mk4-bootloader/se2.h index 5a825625..8bc03bd6 100644 --- a/stm32/mk4-bootloader/se2.h +++ b/stm32/mk4-bootloader/se2.h @@ -86,4 +86,7 @@ void se2_decrypt_secret(uint8_t secret[], int secret_len, int offset, const uint8_t main_slot[], const uint8_t *check_value, const uint8_t pin_digest[32], bool *is_valid); +// Read some random bytes, which we know cannot be MitM'ed. +void se2_read_rng(uint8_t value[8]); + // EOF diff --git a/unix/variant/ckcc.py b/unix/variant/ckcc.py index 0650e49d..1a261f37 100644 --- a/unix/variant/ckcc.py +++ b/unix/variant/ckcc.py @@ -160,6 +160,18 @@ def gate(method, buf_io, arg2): ustruct.pack_into('3I', buf_io, 0, N-5, 1, N) return 0 + if method == 26: + # read RNG (not) from SE (not) + if arg2 == 1: + buf_io[0] = 32 + buf_io[1:1+32] = bytes(range(32)) + elif arg2 == 2: + buf_io[0] = 8 + buf_io[1:1+8] = bytes(range(8)) + else: + return ERANGE; + return 0 + return ENOENT def oneway(method, arg2): diff --git a/unix/variant/sim_mk4.py b/unix/variant/sim_mk4.py index a4f3a3de..a663e813 100644 --- a/unix/variant/sim_mk4.py +++ b/unix/variant/sim_mk4.py @@ -18,6 +18,8 @@ def _init0(): import sim_nfc sys.modules['nfc'] = sim_nfc + mk4.rng_seeding() + mk4.init0 = _init0 mk4.make_flash_fs = lambda: print("Would rebuild /flash")