From b097e10aa32a3bae984dd92356e8cf6561fc48a9 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 25 Sep 2025 19:44:36 +0200 Subject: [PATCH] this commit refactors the signature checks. First we introduce a common pm3 generic private / public key pair in order to allow for users to self sign their own modded device. The verification checks now looks at both hard coded public keys. If a vendor wants to add their own public key thus allowing for a simple way for us to identify their devices they can now do so. The downside is that the firmware mismatch detection becomes a bit dodgy. `mem info; mem info -v; mem info -s -p ; mem info -s -p -w` contains the changes. OBS! when `-w` be careful to not overwrite your genuine RDV4 signature. As always, with great power comes great responsibility --- CHANGELOG.md | 10 +- armsrc/appmain.c | 11 +- client/resources/pm3_generic_private_key.pem | 16 + .../pm3_generic_private_key.sha512.txt | 1 + client/src/cmdflashmem.c | 395 +++++++++++------- client/src/cmdflashmem.h | 12 +- client/src/cmdhw.c | 40 +- common_arm/flashmem.c | 11 +- common_arm/flashmem.h | 8 +- include/pm3_cmd.h | 8 +- include/pmflash.h | 6 + 11 files changed, 340 insertions(+), 178 deletions(-) create mode 100644 client/resources/pm3_generic_private_key.pem create mode 100644 client/resources/pm3_generic_private_key.sha512.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f7877d94..062f19944 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,15 +3,17 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Changed `mem info` and how the signature handling is done (@iceman1001) +- Added `client/resources/pm3_generic_private_key.pem` in order to self-sign a modded device (@iceman1001) - Fix `hf mfdes value --op clear` commands for clearing more than 0x80000000 values and getfilesettings mac mode (@merlokk) - Added ATR fingerprinting to `hf 14a/14b info` (@doegox) ## [Phrack.4.20728][2025-09-11] -- Change `lf t55xx restore` - now skips writing block0 if its all zeros (@iceman1001) +- Changed `lf t55xx restore` - now skips writing block0 if its all zeros (@iceman1001) - Added `HID Simplex Grinnell 36-bit` - Improved Simplex decoder (@datafx, @henrygab) -- Change `lf search` - also test for chipset even if there was just signal noice (@iceman1001) +- Changed `lf search` - also test for chipset even if there was just signal noice (@iceman1001) - Added detection of PCF 7961 , thanks progman (@iceman1001) -- Change `reveng -g` - now correctly take 1024 hexstring as input (@iceman1001) +- Changed `reveng -g` - now correctly take 1024 hexstring as input (@iceman1001) - Added `--override` parameter to NDEF read for overriding MAD CRC check (@iceman1001) - Added `hf saflok` commands (@stiebeljoshua) - Added `ntag_clean.lua` script for easier NTAG memory wipe (@trigat) @@ -19,7 +21,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `lf t55xx view` - now viewing of T55XX dump files is possible (@iceman1001) - Fixed `lf indala cone` - now writing the right bits when using `--fc` and `--cn` - Changed readline hack logic for async dbg msg to be ready for readline 8.3 (@doegox) -- Improved To avoid conflicts with ModemManager on Linux, is recommended to masking the service (@grugnoymeme) +- Changed documentation to avoid conflicts with ModemManager on Linux, is recommended to masking the service (@grugnoymeme) - Changed `data crypto` - now also handles AES-256 (@iceman1001) - Changed `hf mfdes info` - add recognition of Swissbit iShield Key Mifare (@ah01) - Changed `hf mf info` - add detection for unknown backdoor keys and for some backdoor variants (@doegox) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 8f3469b4a..d8c4153f3 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -2968,7 +2968,7 @@ static void PacketReceived(PacketCommandNG *packet) { LED_B_OFF(); break; } - case CMD_FLASHMEM_INFO: { + case CMD_FLASHMEM_GET_SIGNATURE: { LED_B_ON(); @@ -2983,12 +2983,19 @@ static void PacketReceived(PacketCommandNG *packet) { FlashStop(); } - reply_ng(CMD_FLASHMEM_INFO, (isok) ? PM3_SUCCESS : PM3_EFLASH, (uint8_t *)info, sizeof(rdv40_validation_t)); + reply_ng(CMD_FLASHMEM_GET_SIGNATURE, (isok) ? PM3_SUCCESS : PM3_EFLASH, (uint8_t *)info, sizeof(rdv40_validation_t)); BigBuf_free(); LED_B_OFF(); break; } + case CMD_FLASHMEM_GET_INFO: { + LED_B_ON(); + spi_flash_t *spi = flash_get_info(); + reply_ng(CMD_FLASHMEM_GET_INFO, PM3_SUCCESS, (uint8_t *)spi, sizeof(spi_flash_t)); + LED_B_OFF(); + break; + } case CMD_FLASHMEM_PAGES64K: { LED_B_ON(); diff --git a/client/resources/pm3_generic_private_key.pem b/client/resources/pm3_generic_private_key.pem new file mode 100644 index 000000000..e513c1628 --- /dev/null +++ b/client/resources/pm3_generic_private_key.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPrs5grcEJNNgoTl +KgYSHfAYeGqUVyy7DzGNzpQryLBN3eVIj2+2oQB/BfW4wGpfg35s/R0ohCZOjJ81 +oLK1gFx+mvFMmzUP9MzNDxMs/3TumiSQqEQSPQYi8BQWLXbd73pfJP6p40+mCK61 +i3wQtL/H85xL/G5GNQOg3bubdz4BAgMBAAECgYB8QTVFKWZX2ykHsAdYTD5g920h +seVdNCA49LTYMLpHe7em44CL3E7r1PrZjCxUQIn33/aTKs4NvhXqfP7fn/s4V0CZ +mjZSvWQ3ts5CYOGa8LWgaU7qYREPJKVDRgmMFiRntbP9woNLOhnQkZEs75KCKgnT +kDndVTtxlxbn2evKVQJBAP8M7RNI1JuiE/gTq9dL9q4ZKo7NpATanFKwgd7gCooN +anlfpMxhN7FgQRcfjHpndmpMQXhe5UE6C+XcziZj2TcCQQD73AqHNjU6hI0/rgiQ +MXiiC+Oe5G7QPxRwvbjTuLghC/lWCDd96LkKSZLZR0pmSj5nldB0/dDXiZUNF/Q+ +Ht6HAkEAxGMQnXvD8b6gP7Ht7I6aeD36jhD8DZzDJ8yvg4zhsKFZ7fFedN0axCLI +m+R4B7xo6rqi4uu2/T2T8b/OoLLbiwJAOzSYzPwzOxRZ1/RjUvIuN3mwAnQUkKOj +fHU885hiwHH+JPk9gj2o8gAloyodFTqyvMr8++OGqoRPI4rUAZ5kFQJBANQCGq2g +Qh2cFU6pmgoSDExnzOLWWeeCs1buw1XyVULHGv+n6zFowNmkmjJCTF1lvGehcnyW +MNgnxt2QK7Q+RnI= +-----END PRIVATE KEY----- diff --git a/client/resources/pm3_generic_private_key.sha512.txt b/client/resources/pm3_generic_private_key.sha512.txt new file mode 100644 index 000000000..7089364b3 --- /dev/null +++ b/client/resources/pm3_generic_private_key.sha512.txt @@ -0,0 +1 @@ +d99cb37ec1ea04790746a97b439fd3a3dbe5b0eb3de439cdfbf6e7e82ff871d278f11923049fd19e945d90135de112250103850305ed1eca0c82a51a6c5f1682 pm3_generic_private_key.pem diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index fcd21214b..97453c245 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -27,6 +27,7 @@ #include "rsa.h" #include "sha1.h" #include "pk.h" // PEM key load functions +#include "commonutil.h" #define MCK 48000000 #define FLASH_MINFAST 24000000 //33000000 @@ -37,21 +38,36 @@ static int CmdHelp(const char *Cmd); //------------------------------------------------------------------------------------- +#define PM3_RSA_KEY_LEN ( 128 ) +#define PM3_RSA_SHA1_LEN ( 20 ) + +typedef struct { + const char *desc; + const char *E; // public key Exponent E + const char *N; // public key modulus N +} rsa_keypairs_t; + +static const rsa_keypairs_t rsa_keypairs[] = { // RRG Public RSA Key -#define RRG_RSA_KEY_LEN 128 + { + "RDV4", "010001", "E28D809BF323171D11D1ACA4C32A5B7E0A8974FD171E75AD120D60E9B76968FF" \ + "4B0A6364AE50583F9555B8EE1A725F279E949246DF0EFCE4C02B9F3ACDCC623F" \ + "9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33" \ + "DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5" + }, -// public key Exponent E -#define RRG_RSA_E "010001" - -// public key modulus N -#define RRG_RSA_N "E28D809BF323171D11D1ACA4C32A5B7E0A8974FD171E75AD120D60E9B76968FF" \ - "4B0A6364AE50583F9555B8EE1A725F279E949246DF0EFCE4C02B9F3ACDCC623F" \ - "9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33" \ - "DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5" + // GENERIC Public RSA Key for modded devices. They can now be self signed + { + "GENERIC", "010001", "FAECE60ADC10934D8284E52A06121DF018786A94572CBB0F318DCE942BC8B04D" \ + "DDE5488F6FB6A1007F05F5B8C06A5F837E6CFD1D2884264E8C9F35A0B2B5805C" \ + "7E9AF14C9B350FF4CCCD0F132CFF74EE9A2490A844123D0622F014162D76DDEF" \ + "7A5F24FEA9E34FA608AEB58B7C10B4BFC7F39C4BFC6E463503A0DDBB9B773E01" + }, +}; //------------------------------------------------------------------------------------- -int rdv4_get_flash_pages64k(uint8_t *pages64k) { +int pm3_get_flash_pages64k(uint8_t *pages64k) { if (pages64k == NULL) { return PM3_EINVARG; } @@ -60,7 +76,7 @@ int rdv4_get_flash_pages64k(uint8_t *pages64k) { SendCommandNG(CMD_FLASHMEM_PAGES64K, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_FLASHMEM_PAGES64K, &resp, 2500) == false) { - PrintAndLogEx(WARNING, "rdv4_get_flash_pages64k() timeout while waiting for reply"); + PrintAndLogEx(WARNING, "pm3_get_flash_pages64k() timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -73,15 +89,15 @@ int rdv4_get_flash_pages64k(uint8_t *pages64k) { return PM3_SUCCESS; } -int rdv4_get_signature(rdv40_validation_t *out) { +int pm3_get_signature(rdv40_validation_t *out) { if (out == NULL) { return PM3_EINVARG; } clearCommandBuffer(); - SendCommandNG(CMD_FLASHMEM_INFO, NULL, 0); + SendCommandNG(CMD_FLASHMEM_GET_SIGNATURE, NULL, 0); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_FLASHMEM_INFO, &resp, 2500) == false) { + if (WaitForResponseTimeout(CMD_FLASHMEM_GET_SIGNATURE, &resp, 2500) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -96,32 +112,63 @@ int rdv4_get_signature(rdv40_validation_t *out) { } // validate signature -int rdv4_validate(rdv40_validation_t *mem) { +int pm3_validate(rdv40_validation_t *mem, signature_e *type) { + // Flash ID hash (sha1) - uint8_t sha_hash[20] = {0}; + uint8_t sha_hash[PM3_RSA_SHA1_LEN] = {0}; mbedtls_sha1(mem->flashid, sizeof(mem->flashid), sha_hash); - // set up RSA - mbedtls_rsa_context rsa; - mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0); - rsa.len = RRG_RSA_KEY_LEN; - mbedtls_mpi_read_string(&rsa.N, 16, RRG_RSA_N); - mbedtls_mpi_read_string(&rsa.E, 16, RRG_RSA_E); + *type = SIGN_UNK; + int is_valid = 0; - // Verify (public key) - int is_verified = mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, mem->signature); - mbedtls_rsa_free(&rsa); + for (uint8_t i = 0; i < ARRAYLEN(rsa_keypairs); i++) { - if (is_verified == 0) { - return PM3_SUCCESS; + // set up RSA + mbedtls_rsa_context rsa; + mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0); + rsa.len = PM3_RSA_KEY_LEN; + mbedtls_mpi_read_string(&rsa.N, 16, rsa_keypairs[i].N); + mbedtls_mpi_read_string(&rsa.E, 16, rsa_keypairs[i].E); + + // Verify (public key) + is_valid = mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, PM3_RSA_SHA1_LEN, sha_hash, mem->signature); + mbedtls_rsa_free(&rsa); + + if (is_valid == 0) { + *type = i; + return PM3_SUCCESS; + } } + return PM3_EFAILED; } -static int rdv4_sign_write(uint8_t *signature, uint8_t slen) { +static int pm3_get_flash_info(spi_flash_t *info) { + if (info == NULL) { + return PM3_EINVARG; + } + + clearCommandBuffer(); + SendCommandNG(CMD_FLASHMEM_GET_INFO, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_FLASHMEM_GET_INFO, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "pm3_get_flash_info() timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "fail reading flash info"); + return PM3_EFLASH; + } + + memcpy(info, resp.data.asBytes, sizeof(spi_flash_t)); + return PM3_SUCCESS; +} + +static int pm3_sign_write(uint8_t *signature, uint8_t slen) { uint8_t spi_flash_pages = 0; - int res = rdv4_get_flash_pages64k(&spi_flash_pages); + int res = pm3_get_flash_pages64k(&spi_flash_pages); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "failed to get flash pages (%x)", res); return res; @@ -146,10 +193,84 @@ static int rdv4_sign_write(uint8_t *signature, uint8_t slen) { return PM3_EFAILED; } } - PrintAndLogEx(SUCCESS, "Writing signature at offset %u ( "_GREEN_("ok") " )", FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages)); + PrintAndLogEx(SUCCESS, "Wrote signature at offset " _YELLOW_("%u") " ( "_GREEN_("ok") " )", FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages)); return PM3_SUCCESS; } +static void pm3_print_flash_memory_info(spi_flash_t *spi, rdv40_validation_t *mem, uint8_t *sha_hash, signature_e *type) { + + // print header + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Flash memory Information")); + PrintAndLogEx(INFO, "ID...................... %s", sprint_hex_inrow(mem->flashid, sizeof(mem->flashid))); + PrintAndLogEx(INFO, "SHA1.................... %s", sprint_hex_inrow(sha_hash, PM3_RSA_SHA1_LEN)); + + if (spi->device_id) { + PrintAndLogEx(INFO, "Mfr ID / Dev ID......... " _YELLOW_("%02X") " / " _YELLOW_("%02X"), spi->manufacturer_id, spi->device_id); + } + + if (spi->jedec_id) { + PrintAndLogEx(INFO, "JEDEC Mfr ID / Dev ID... " _YELLOW_("%02X") " / "_YELLOW_("%04X"), spi->manufacturer_id, spi->jedec_id); + } + + PrintAndLogEx(INFO, "Memory size............. " _YELLOW_("%d Kb") " ( %d * 64 Kb )", spi->pages64k * 64, spi->pages64k); + PrintAndLogEx(NORMAL, ""); + + // Print Signature Header + switch (*type) { + case SIGN_RDV4: + case SIGN_GENERIC: + PrintAndLogEx(INFO, "--- " _CYAN_("PM3 %s RSA signature"), rsa_keypairs[*type].desc); + break; + case SIGN_UNK: + default: + PrintAndLogEx(INFO, "--- " _CYAN_("Unknown RSA signature")); + break; + } + + // Print the Signature Data + for (int i = 0; i < (sizeof(mem->signature) / 32); i++) { + PrintAndLogEx(INFO, " %s", sprint_hex_inrow(mem->signature + (i * 32), 32)); + } +} + +static void pm3_print_public_keys(void) { + + mbedtls_rsa_context rsa; + mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0); + rsa.len = PM3_RSA_KEY_LEN; + + for (uint8_t i = 0; i < ARRAYLEN(rsa_keypairs); i++) { + + mbedtls_mpi_read_string(&rsa.N, 16, rsa_keypairs[i].N); + mbedtls_mpi_read_string(&rsa.E, 16, rsa_keypairs[i].E); + + PrintAndLogEx(INFO, "--- " _CYAN_("%s RSA Public key"), rsa_keypairs[i].desc); + char str_exp[10]; + char str_pk[261]; + size_t exlen = 0, pklen = 0; + mbedtls_mpi_write_string(&rsa.E, 16, str_exp, sizeof(str_exp), &exlen); + mbedtls_mpi_write_string(&rsa.N, 16, str_pk, sizeof(str_pk), &pklen); + + PrintAndLogEx(INFO, "Len........ %"PRIu64, rsa.len); + PrintAndLogEx(INFO, "Exponent... %s", str_exp); + PrintAndLogEx(INFO, "Public key modulus N"); + PrintAndLogEx(INFO, " %.64s", str_pk); + PrintAndLogEx(INFO, " %.64s", str_pk + 64); + PrintAndLogEx(INFO, " %.64s", str_pk + 128); + PrintAndLogEx(INFO, " %.64s", str_pk + 192); + + bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0); + PrintAndLogEx( + (is_keyok) ? SUCCESS : FAILED, + "RSA public key check.... ( %s )", + (is_keyok) ? _GREEN_("ok") : _RED_("fail") + ); + PrintAndLogEx(NORMAL, ""); + } +} + + static int CmdFlashmemSpiBaud(const char *Cmd) { CLIParserContext *ctx; @@ -242,7 +363,7 @@ static int CmdFlashMemLoad(const char *Cmd) { } uint8_t spi_flash_pages = 0; - int res = rdv4_get_flash_pages64k(&spi_flash_pages); + int res = pm3_get_flash_pages64k(&spi_flash_pages); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Failed to get flash pages count (%x)", res); return res; @@ -442,7 +563,7 @@ static int CmdFlashMemDump(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); uint8_t spi_flash_pages = 0; - int res = rdv4_get_flash_pages64k(&spi_flash_pages); + int res = pm3_get_flash_pages64k(&spi_flash_pages); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "failed to get flash pages count (%x)", res); return res; @@ -507,7 +628,7 @@ static int CmdFlashMemWipe(const char *Cmd) { CLIParserFree(ctx); uint8_t spi_flash_pages = 0; - int res = rdv4_get_flash_pages64k(&spi_flash_pages); + int res = pm3_get_flash_pages64k(&spi_flash_pages); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "failed to get flash pages count (%x)", res); return res; @@ -541,7 +662,8 @@ static int CmdFlashMemInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "mem info", "Collect signature and verify it from flash memory", - "mem info" + "mem info\n" + "mem info -s -d 0102030405060708 -p pm3_generic_private_key.pem -w --> generate and write a RSA 1024 signature " ); void *argtable[] = { @@ -574,16 +696,19 @@ static int CmdFlashMemInfo(const char *Cmd) { } if (dlen > 0 && dlen < sizeof(id)) { - PrintAndLogEx(FAILED, "Error parsing flash memory id, expect 8, got %d", dlen); + PrintAndLogEx(FAILED, "Error parsing flash memory id, expect 8, got " _RED_("%d"), dlen); return PM3_EINVARG; } // set up PK key context now. mbedtls_pk_context pkctx; mbedtls_pk_init(&pkctx); + bool got_private = false; + // PEM if (pemlen) { + // PEM file char *path = NULL; if (searchFile(&path, RESOURCES_SUBDIR, pem_fn, ".pem", true) != PM3_SUCCESS) { @@ -597,6 +722,7 @@ static int CmdFlashMemInfo(const char *Cmd) { // load private res = mbedtls_pk_parse_keyfile(&pkctx, path, NULL); free(path); + //res = mbedtls_pk_parse_public_keyfile(&pkctx, path); if (res == 0) { PrintAndLogEx(NORMAL, " ( " _GREEN_("ok") " )"); @@ -611,7 +737,9 @@ static int CmdFlashMemInfo(const char *Cmd) { PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } + got_private = true; + } else { // it not loaded, we need to setup the context manually @@ -621,148 +749,117 @@ static int CmdFlashMemInfo(const char *Cmd) { } } - // validate devicesignature data - rdv40_validation_t mem; - res = rdv4_get_signature(&mem); + // download signature data from device + rdv40_validation_t mem = {0}; + res = pm3_get_signature(&mem); if (res != PM3_SUCCESS) { return res; } - res = rdv4_validate(&mem); + // download SPI chip information + spi_flash_t spi = {0}; + res = pm3_get_flash_info(&spi); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Failed to get flash information. Are you sure your device have a external flash?"); + return res; + } - // Flash ID hash (sha1) - uint8_t sha_hash[20] = {0}; + // try to verify the found device signature + signature_e type; + res = pm3_validate(&mem, &type); + + // Calculate Flash ID hash ( SHA1 ) + uint8_t sha_hash[PM3_RSA_SHA1_LEN] = {0}; mbedtls_sha1(mem.flashid, sizeof(mem.flashid), sha_hash); - // print header - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Flash memory Information") " ---------"); - PrintAndLogEx(INFO, "ID................... %s", sprint_hex_inrow(mem.flashid, sizeof(mem.flashid))); - PrintAndLogEx(INFO, "SHA1................. %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash))); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA signature") " ---------------"); - for (int i = 0; i < (sizeof(mem.signature) / 32); i++) { - PrintAndLogEx(INFO, " %s", sprint_hex_inrow(mem.signature + (i * 32), 32)); - } + pm3_print_flash_memory_info(&spi, &mem, sha_hash, &type); + PrintAndLogEx( (res == PM3_SUCCESS) ? SUCCESS : FAILED, - "Signature............ ( %s )", + "Signature............... ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail") ); PrintAndLogEx(NORMAL, ""); + // Print the hardcoded RSA public keys + if (verbose) { + pm3_print_public_keys(); + } + + if (type != SIGN_UNK) { + PrintAndLogEx(SUCCESS, "Genuine Proxmark3 " _CYAN_("%s") " signature detected :white_check_mark:", rsa_keypairs[type].desc); + } else { + PrintAndLogEx(FAILED, "No genuine Proxmark3 signature detected :x:"); + } + + // end here if we not going to create a signature + if (shall_sign == false) { + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } + + // enter the Signing process mbedtls_rsa_context *rsa = NULL; + // called with private key .pem file if (got_private) { + rsa = mbedtls_pk_rsa(pkctx); rsa->padding = MBEDTLS_RSA_PKCS_V15; rsa->hash_id = 0; - rsa->len = RRG_RSA_KEY_LEN; - } else { + rsa->len = PM3_RSA_KEY_LEN; - rsa = (mbedtls_rsa_context *)calloc(1, sizeof(mbedtls_rsa_context)); - if (rsa == NULL) { - PrintAndLogEx(WARNING, "Failed to allocate memory"); - return PM3_EMALLOC; - } - mbedtls_rsa_init(rsa, MBEDTLS_RSA_PKCS_V15, 0); - rsa->len = RRG_RSA_KEY_LEN; - - // add public key - mbedtls_mpi_read_string(&rsa->N, 16, RRG_RSA_N); - mbedtls_mpi_read_string(&rsa->E, 16, RRG_RSA_E); - } - - PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA Public key") " --------------"); - if (verbose) { - char str_exp[10]; - char str_pk[261]; - size_t exlen = 0, pklen = 0; - mbedtls_mpi_write_string(&rsa->E, 16, str_exp, sizeof(str_exp), &exlen); - mbedtls_mpi_write_string(&rsa->N, 16, str_pk, sizeof(str_pk), &pklen); - - PrintAndLogEx(INFO, "Len.................. %"PRIu64, rsa->len); - PrintAndLogEx(INFO, "Exponent............. %s", str_exp); - PrintAndLogEx(INFO, "Public key modulus N"); - PrintAndLogEx(INFO, " %.64s", str_pk); - PrintAndLogEx(INFO, " %.64s", str_pk + 64); - PrintAndLogEx(INFO, " %.64s", str_pk + 128); - PrintAndLogEx(INFO, " %.64s", str_pk + 192); - PrintAndLogEx(NORMAL, ""); - } - - bool is_keyok = (mbedtls_rsa_check_pubkey(rsa) == 0); - PrintAndLogEx( - (is_keyok) ? SUCCESS : FAILED, - "RDV4 RSA public key check.... ( %s )", - (is_keyok) ? _GREEN_("ok") : _RED_("fail") - ); - - is_keyok = (mbedtls_rsa_check_privkey(rsa) == 0); - if (verbose) { - PrintAndLogEx( - (is_keyok) ? SUCCESS : FAILED, - "RDV4 RSA private key check... ( %s )", - (is_keyok) ? _GREEN_("ok") : _YELLOW_("n/aA") - ); - } - - // to be verified - uint8_t from_device[RRG_RSA_KEY_LEN]; - memcpy(from_device, mem.signature, RRG_RSA_KEY_LEN); - - // to be signed - uint8_t sign[RRG_RSA_KEY_LEN]; - memset(sign, 0, RRG_RSA_KEY_LEN); - - // Signing (private key) - if (shall_sign) { - - if (is_keyok) { - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Enter signing") " --------------------"); - - if (dlen == 8) { - mbedtls_sha1(id, sizeof(id), sha_hash); - } - PrintAndLogEx(INFO, "Signing....... %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash))); - - int is_signed = mbedtls_rsa_pkcs1_sign(rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA1, 20, sha_hash, sign); + bool is_keyok = (mbedtls_rsa_check_privkey(rsa) == 0); + if (verbose) { PrintAndLogEx( - (is_signed == 0) ? SUCCESS : FAILED, - "RSA signing... ( %s )", - (is_signed == 0) ? _GREEN_("ok") : _RED_("fail") + (is_keyok) ? SUCCESS : FAILED, + "RSA private key check... ( %s )", + (is_keyok) ? _GREEN_("ok") : _YELLOW_("n/a") ); - - if (shall_write) { - rdv4_sign_write(sign, RRG_RSA_KEY_LEN); - } - PrintAndLogEx(INFO, "New signature"); - for (int i = 0; i < (sizeof(sign) / 32); i++) { - PrintAndLogEx(INFO, " %s", sprint_hex_inrow(sign + (i * 32), 32)); - } - } else { - PrintAndLogEx(FAILED, "no private key available to sign"); } - } - // Verify (public key) - bool is_verified = (mbedtls_rsa_pkcs1_verify(rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, from_device) == 0); + if (is_keyok == false) { + PrintAndLogEx(FAILED, "No private key available to sign"); + return PM3_ECRYPTO; + } + + // to be signed + uint8_t sign[PM3_RSA_KEY_LEN] = {0}; + + // Signing (private key) + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter signing")); + + // use ID from CLI, otherwise we use FLash ID from device + if (dlen == 8) { + mbedtls_sha1(id, sizeof(id), sha_hash); + PrintAndLogEx(INFO, "Using ID......... %s", sprint_hex_inrow(id, dlen)); + } else { + PrintAndLogEx(INFO, "Using ID......... %s", sprint_hex_inrow(mem.flashid, sizeof(mem.flashid))); + } + + PrintAndLogEx(INFO, "Signing.......... %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash))); + + int is_signed = mbedtls_rsa_pkcs1_sign(rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA1, PM3_RSA_SHA1_LEN, sha_hash, sign); + PrintAndLogEx( + (is_signed == 0) ? SUCCESS : FAILED, + "RSA signing...... ( %s )", + (is_signed == 0) ? _GREEN_("ok") : _RED_("fail") + ); + + PrintAndLogEx(INFO, "--- " _CYAN_("New signature")); + for (int i = 0; i < (sizeof(sign) / 32); i++) { + PrintAndLogEx(INFO, " %s", sprint_hex_inrow(sign + (i * 32), 32)); + } + + if (shall_write) { + pm3_sign_write(sign, PM3_RSA_KEY_LEN); + } - if (got_private == false) { mbedtls_rsa_free(rsa); - free(rsa); + mbedtls_pk_free(&pkctx); } - mbedtls_pk_free(&pkctx); - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx( - (is_verified) ? SUCCESS : FAILED, - "Genuine Proxmark3 RDV4 signature detected... %s", - (is_verified) ? ":heavy_check_mark:" : ":x:" - ); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } diff --git a/client/src/cmdflashmem.h b/client/src/cmdflashmem.h index f47547166..55a46f4b1 100644 --- a/client/src/cmdflashmem.h +++ b/client/src/cmdflashmem.h @@ -31,8 +31,14 @@ typedef enum { DICTIONARY_MIFARE_ULAES, } Dictionary_t; +typedef enum { + SIGN_RDV4 = 0, + SIGN_GENERIC, + SIGN_UNK = 99 +} signature_e; + int CmdFlashMem(const char *Cmd); -int rdv4_get_signature(rdv40_validation_t *out); -int rdv4_validate(rdv40_validation_t *mem); -int rdv4_get_flash_pages64k(uint8_t *pages64k); +int pm3_get_signature(rdv40_validation_t *out); +int pm3_validate(rdv40_validation_t *mem, signature_e *type); +int pm3_get_flash_pages64k(uint8_t *pages64k); #endif diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index 047cf387f..ddb162fcf 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -1575,16 +1575,25 @@ void pm3_version_short(void) { if (IfPm3Rdv4Fw()) { - bool is_genuine_rdv4 = false; + // validate signature data rdv40_validation_t mem; - if (rdv4_get_signature(&mem) == PM3_SUCCESS) { - if (rdv4_validate(&mem) == PM3_SUCCESS) { - is_genuine_rdv4 = true; + signature_e type; + + if (pm3_get_signature(&mem) == PM3_SUCCESS) { + if (pm3_validate(&mem, &type) == PM3_SUCCESS) { + + if (type == SIGN_RDV4) { + PrintAndLogEx(NORMAL, " Target.... %s", _YELLOW_("RDV4")); + } else if (type == SIGN_GENERIC) { + PrintAndLogEx(NORMAL, " Target.... %s", _YELLOW_("GENERIC")); + } else { + PrintAndLogEx(NORMAL, " Target.... %s", _RED_("device / fw mismatch")); + } } } - PrintAndLogEx(NORMAL, " Target.... %s", (is_genuine_rdv4) ? _YELLOW_("RDV4") : _RED_("device / fw mismatch")); + } else { PrintAndLogEx(NORMAL, " Target.... %s", _YELLOW_("PM3 GENERIC")); } @@ -1702,17 +1711,26 @@ void pm3_version(bool verbose, bool oneliner) { if (WaitForResponseTimeout(CMD_VERSION, &resp, 1000)) { if (IfPm3Rdv4Fw()) { - bool is_genuine_rdv4 = false; // validate signature data rdv40_validation_t mem; - if (rdv4_get_signature(&mem) == PM3_SUCCESS) { - if (rdv4_validate(&mem) == PM3_SUCCESS) { - is_genuine_rdv4 = true; + signature_e type; + + if (pm3_get_signature(&mem) == PM3_SUCCESS) { + if (pm3_validate(&mem, &type) == PM3_SUCCESS) { + + if (type == SIGN_RDV4) { + PrintAndLogEx(NORMAL, " Device.................... " _GREEN_("RDV4")); + PrintAndLogEx(NORMAL, " Firmware.................. " _GREEN_("RDV4")); + } else if (type == SIGN_GENERIC) { + PrintAndLogEx(NORMAL, " Device.................... ", _GREEN_("GENERIC")); + PrintAndLogEx(NORMAL, " Firmware.................. ", _GREEN_("GENERIC")); + } else { + PrintAndLogEx(NORMAL, " Device.................... " _RED_("Bad signature detected!")); + PrintAndLogEx(NORMAL, " Firmware.................. " _YELLOW_("N/A")); + } } } - PrintAndLogEx(NORMAL, " Device.................... %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _RED_("device / fw mismatch")); - PrintAndLogEx(NORMAL, " Firmware.................. %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _YELLOW_("RDV4")); PrintAndLogEx(NORMAL, " External flash............ %s", IfPm3Flash() ? _GREEN_("present") : _YELLOW_("absent")); PrintAndLogEx(NORMAL, " Smartcard reader.......... %s", IfPm3Smartcard() ? _GREEN_("present") : _YELLOW_("absent")); PrintAndLogEx(NORMAL, " FPC USART for BT add-on... %s", IfPm3FpcUsartHost() ? _GREEN_("present") : _YELLOW_("absent")); diff --git a/common_arm/flashmem.c b/common_arm/flashmem.c index c2c9781be..37ba1481d 100644 --- a/common_arm/flashmem.c +++ b/common_arm/flashmem.c @@ -43,9 +43,13 @@ static uint32_t FLASHMEM_SPIBAUDRATE = FLASH_BAUD; #ifndef AS_BOOTROM - -spi_flash_t spi_flash_data = {0}; uint8_t spi_flash_pages64k = 4; +static spi_flash_t spi_flash_data = {0}; + +spi_flash_t *flash_get_info(void) { + return &spi_flash_data; +} + void FlashmemSetSpiBaudrate(uint32_t baudrate) { FLASHMEM_SPIBAUDRATE = baudrate; @@ -387,7 +391,7 @@ void Flashmem_print_status(void) { ); } - Dbprintf(" Memory size............. " _YELLOW_("%d kB (%d pages * 64k)"), spi_flash_pages64k * 64, spi_flash_pages64k); + Dbprintf(" Memory size............. " _YELLOW_("%d Kb") " ( %d pages * 64k )", spi_flash_pages64k * 64, spi_flash_pages64k); uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; Flash_UniqueID(uid); @@ -439,6 +443,7 @@ bool FlashDetect(void) { } } + spi_flash_data.pages64k = spi_flash_pages64k; return ret; } diff --git a/common_arm/flashmem.h b/common_arm/flashmem.h index 45b8e67c2..97a46fd38 100644 --- a/common_arm/flashmem.h +++ b/common_arm/flashmem.h @@ -21,6 +21,8 @@ #define __FLASHMEM_H #include "common.h" +#include "pmflash.h" + // Used Command #define ID 0x90 @@ -135,11 +137,7 @@ uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len); uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len); void Flashmem_print_status(void); -typedef struct { - uint8_t manufacturer_id; - uint8_t device_id; - uint16_t jedec_id; -} spi_flash_t; +spi_flash_t *flash_get_info(void); extern uint8_t spi_flash_pages64k; diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 79a7b64eb..a95a431f7 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -305,7 +305,7 @@ typedef struct { typedef struct { // 64KB SRAM -> 524288 bits(max sample num) < 2^30 - uint32_t samples : +uint32_t samples : LF_SAMPLES_BITS; bool realtime : 1; bool verbose : 1; @@ -473,6 +473,9 @@ typedef struct { #define CMD_SPIFFS_UNMOUNT 0x0131 #define CMD_SPIFFS_WRITE 0x0132 +#define CMD_FLASHMEM_GET_SIGNATURE 0x0147 +#define CMD_FLASHMEM_GET_INFO 0x0148 + // We take +0x1000 when having a variant of similar function (todo : make it an argument!) #define CMD_SPIFFS_APPEND 0x1132 @@ -844,11 +847,13 @@ typedef struct { #define MIFARE_2K_MAX_BYTES 2048 #define MIFARE_1K_MAX_BYTES 1024 #define MIFARE_MINI_MAX_BYTES 320 + #define FLAG_MASK_MF_SIZE 0x00C0 #define FLAG_MF_MINI 0x0000 #define FLAG_MF_1K 0x0040 #define FLAG_MF_2K 0x0080 #define FLAG_MF_4K 0x00C0 + #define FLAG_SET_MF_SIZE(flags, size) {\ flags = (flags & (~FLAG_MASK_MF_SIZE))|\ (size == MIFARE_MINI_MAX_BYTES ? FLAG_MF_MINI : \ @@ -857,6 +862,7 @@ typedef struct { (size == MIFARE_4K_MAX_BYTES ? FLAG_MF_4K : \ 0))));\ } + // else we tell to take UID from block 0: #define IS_FLAG_MF_SIZE(flags, size) (\ (flags & FLAG_MASK_MF_SIZE) == \ diff --git a/include/pmflash.h b/include/pmflash.h index 7e601c2bc..7969ac4d9 100644 --- a/include/pmflash.h +++ b/include/pmflash.h @@ -93,5 +93,11 @@ typedef struct { uint8_t signature[FLASH_MEM_SIGNATURE_LEN]; } PACKED rdv40_validation_t; +typedef struct { + uint8_t manufacturer_id; + uint8_t device_id; + uint16_t jedec_id; + uint8_t pages64k; +} PACKED spi_flash_t; #endif // __PMFLASH_H