2018-05-03 18:15:03 +08:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Copyright (C) 2018 iceman
|
|
|
|
//
|
|
|
|
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
|
|
|
// at your option, any later version. See the LICENSE.txt file for the text of
|
|
|
|
// the license.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Proxmark3 RDV40 Flash memory commands
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "cmdflashmem.h"
|
2019-08-08 22:57:33 +08:00
|
|
|
#include <ctype.h>
|
2020-10-03 23:12:58 +08:00
|
|
|
#include "cmdparser.h" // command_t
|
|
|
|
#include "cliparser.h"
|
2021-02-17 18:52:23 +08:00
|
|
|
#include "pmflash.h" // rdv40validation_t
|
2020-10-03 23:12:58 +08:00
|
|
|
#include "fileutils.h" // saveFile
|
|
|
|
#include "comms.h" // getfromdevice
|
2019-08-08 22:57:33 +08:00
|
|
|
#include "cmdflashmemspiffs.h" // spiffs commands
|
2020-05-25 05:23:55 +08:00
|
|
|
#include "rsa.h"
|
|
|
|
#include "sha1.h"
|
2021-04-04 23:01:43 +08:00
|
|
|
#include "pk.h" // PEM key load functions
|
2018-05-03 18:15:03 +08:00
|
|
|
|
2018-09-06 11:24:50 +08:00
|
|
|
#define MCK 48000000
|
|
|
|
#define FLASH_MINFAST 24000000 //33000000
|
|
|
|
#define FLASH_BAUD MCK/2
|
|
|
|
#define FLASH_FASTBAUD MCK
|
|
|
|
#define FLASH_MINBAUD FLASH_FASTBAUD
|
|
|
|
|
2018-05-03 18:15:03 +08:00
|
|
|
static int CmdHelp(const char *Cmd);
|
2018-09-06 11:24:50 +08:00
|
|
|
|
2021-02-17 18:52:23 +08:00
|
|
|
//-------------------------------------------------------------------------------------
|
|
|
|
// RRG Public RSA Key
|
|
|
|
#define RRG_RSA_KEY_LEN 128
|
|
|
|
|
|
|
|
// public key Exponent E
|
|
|
|
#define RRG_RSA_E "010001"
|
|
|
|
|
|
|
|
// public key modulus N
|
|
|
|
#define RRG_RSA_N "E28D809BF323171D11D1ACA4C32A5B7E0A8974FD171E75AD120D60E9B76968FF" \
|
|
|
|
"4B0A6364AE50583F9555B8EE1A725F279E949246DF0EFCE4C02B9F3ACDCC623F" \
|
|
|
|
"9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33" \
|
|
|
|
"DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5"
|
|
|
|
|
2021-03-07 15:56:36 +08:00
|
|
|
//-------------------------------------------------------------------------------------
|
2021-02-17 18:52:23 +08:00
|
|
|
|
|
|
|
int rdv4_get_signature(rdv40_validation_t *out) {
|
|
|
|
if (out == NULL) {
|
|
|
|
return PM3_EINVARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
clearCommandBuffer();
|
|
|
|
SendCommandNG(CMD_FLASHMEM_INFO, NULL, 0);
|
|
|
|
PacketResponseNG resp;
|
|
|
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
|
|
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
|
|
|
return PM3_ETIMEOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t isok = resp.oldarg[0] & 0xFF;
|
|
|
|
if (isok == false) {
|
|
|
|
PrintAndLogEx(FAILED, "fail reading from flashmemory");
|
|
|
|
return PM3_EFLASH;
|
|
|
|
}
|
|
|
|
|
|
|
|
//rdv40_validation_t mem;
|
|
|
|
memcpy(out, (rdv40_validation_t *)resp.data.asBytes, sizeof(rdv40_validation_t));
|
|
|
|
return PM3_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// validate signature
|
|
|
|
int rdv4_validate(rdv40_validation_t *mem) {
|
|
|
|
// Flash ID hash (sha1)
|
|
|
|
uint8_t sha_hash[20] = {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);
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
if (is_verified == 0) {
|
|
|
|
return PM3_SUCCESS;
|
|
|
|
}
|
|
|
|
return PM3_EFAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int rdv4_sign_write(uint8_t *signature, uint8_t slen){
|
|
|
|
// save to mem
|
|
|
|
clearCommandBuffer();
|
|
|
|
PacketResponseNG resp;
|
|
|
|
SendCommandOLD(CMD_FLASHMEM_WRITE, FLASH_MEM_SIGNATURE_OFFSET, FLASH_MEM_SIGNATURE_LEN, 0, signature, slen);
|
|
|
|
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
|
|
|
|
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
|
|
|
} else {
|
|
|
|
if (!resp.oldarg[0]) {
|
|
|
|
PrintAndLogEx(FAILED, "Writing signature ( "_RED_("fail") ")");
|
|
|
|
} else {
|
2021-03-07 17:19:52 +08:00
|
|
|
PrintAndLogEx(SUCCESS, "Writing signature at offset %u ( "_GREEN_("ok") " )", FLASH_MEM_SIGNATURE_OFFSET);
|
2021-02-17 18:52:23 +08:00
|
|
|
return PM3_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PM3_EFAILED;
|
|
|
|
}
|
|
|
|
|
2021-03-09 23:09:36 +08:00
|
|
|
static int CmdFlashmemSpiBaud(const char *Cmd) {
|
2020-10-07 00:00:00 +08:00
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
CLIParserContext *ctx;
|
2020-10-03 23:14:57 +08:00
|
|
|
CLIParserInit(&ctx, "mem baudrate",
|
2020-10-03 23:12:58 +08:00
|
|
|
"Set the baudrate for the SPI flash memory communications.\n"
|
|
|
|
"Reading Flash ID will virtually always fail under 48MHz setting.\n"
|
|
|
|
"Unless you know what you are doing, please stay at 24MHz.\n"
|
|
|
|
"If >= 24MHz, FASTREADS instead of READS instruction will be used.",
|
|
|
|
"mem baudrate --mhz 48"
|
2020-10-07 00:00:00 +08:00
|
|
|
);
|
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
void *argtable[] = {
|
|
|
|
arg_param_begin,
|
|
|
|
arg_int1(NULL, "mhz", "<24|48>", "SPI baudrate in MHz"),
|
|
|
|
arg_param_end
|
|
|
|
};
|
|
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
|
|
int br = arg_get_int_def(ctx, 1, -1);
|
|
|
|
CLIParserFree(ctx);
|
|
|
|
|
|
|
|
if (br == -1) {
|
|
|
|
PrintAndLogEx(ERR, "failed to get baudrate");
|
|
|
|
return PM3_EINVARG;
|
2019-04-19 05:26:12 +08:00
|
|
|
}
|
2019-05-08 07:35:51 +08:00
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
uint32_t baudrate = br * 1000000;
|
2019-04-19 05:26:12 +08:00
|
|
|
if (baudrate != FLASH_BAUD && baudrate != FLASH_MINBAUD) {
|
2020-10-03 23:12:58 +08:00
|
|
|
PrintAndLogEx(ERR, "wrong baudrate. Only 24 or 48 is allowed");
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_EINVARG;
|
|
|
|
}
|
2019-10-13 06:48:26 +08:00
|
|
|
SendCommandNG(CMD_FLASHMEM_SET_SPIBAUDRATE, (uint8_t *)&baudrate, sizeof(uint32_t));
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_SUCCESS;
|
2018-09-06 11:24:50 +08:00
|
|
|
}
|
|
|
|
|
2019-04-12 06:38:54 +08:00
|
|
|
static int CmdFlashMemLoad(const char *Cmd) {
|
2018-05-03 18:15:03 +08:00
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
CLIParserContext *ctx;
|
|
|
|
CLIParserInit(&ctx, "mem load",
|
|
|
|
"Loads binary file into flash memory on device\n"
|
|
|
|
"Warning: mem area to be written must have been wiped first\n"
|
|
|
|
"( this is already taken care when loading dictionaries )",
|
|
|
|
"mem load -f myfile -> upload file myfile values at default offset 0\n"
|
|
|
|
"mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n"
|
|
|
|
"mem load -f mfc_default_keys -m -> upload MFC keys\n"
|
|
|
|
"mem load -f t55xx_default_pwds -t -> upload T55XX passwords\n"
|
|
|
|
"mem load -f iclass_default_keys -i -> upload iCLASS keys\n"
|
2020-10-07 00:00:00 +08:00
|
|
|
);
|
2020-10-03 23:12:58 +08:00
|
|
|
|
|
|
|
void *argtable[] = {
|
|
|
|
arg_param_begin,
|
|
|
|
arg_int0("o", "offset", "<dec>", "offset in memory"),
|
|
|
|
arg_lit0("m", "mifare,mfc", "upload 6 bytes keys (mifare key dictionary)"),
|
|
|
|
arg_lit0("i", "iclass", "upload 8 bytes keys (iClass key dictionary)"),
|
|
|
|
arg_lit0("t", "t55xx", "upload 4 bytes keys (password dictionary)"),
|
|
|
|
arg_strx0("f", "file", "<filename>", "file name"),
|
|
|
|
arg_param_end
|
|
|
|
};
|
|
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
|
|
|
|
|
|
int offset = arg_get_int_def(ctx, 1, 0);
|
|
|
|
bool is_mfc = arg_get_lit(ctx, 2);
|
|
|
|
bool is_iclass = arg_get_lit(ctx, 3);
|
2020-10-07 00:00:00 +08:00
|
|
|
bool is_t55xx = arg_get_lit(ctx, 4);
|
2020-10-03 23:12:58 +08:00
|
|
|
int fnlen = 0;
|
2019-03-10 06:35:06 +08:00
|
|
|
char filename[FILE_PATH_SIZE] = {0};
|
2020-10-07 00:00:00 +08:00
|
|
|
CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
2020-10-03 23:12:58 +08:00
|
|
|
CLIParserFree(ctx);
|
2019-03-10 06:35:06 +08:00
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
Dictionary_t d = DICTIONARY_NONE;
|
|
|
|
if (is_mfc) {
|
|
|
|
d = DICTIONARY_MIFARE;
|
|
|
|
PrintAndLogEx(INFO, "treating file as MIFARE Classic keys");
|
|
|
|
} else if (is_iclass) {
|
|
|
|
d = DICTIONARY_ICLASS;
|
|
|
|
PrintAndLogEx(INFO, "treating file as iCLASS keys");
|
|
|
|
} else if (is_t55xx) {
|
|
|
|
d = DICTIONARY_T55XX;
|
|
|
|
PrintAndLogEx(INFO, "treating file as T55xx passwords");
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t datalen = 0;
|
2020-05-20 14:58:28 +08:00
|
|
|
uint32_t keycount = 0;
|
2019-03-10 06:35:06 +08:00
|
|
|
int res = 0;
|
|
|
|
uint8_t *data = calloc(FLASH_MEM_MAX_SIZE, sizeof(uint8_t));
|
|
|
|
|
|
|
|
switch (d) {
|
|
|
|
case DICTIONARY_MIFARE:
|
2020-10-03 23:12:58 +08:00
|
|
|
offset = DEFAULT_MF_KEYS_OFFSET;
|
2019-04-29 01:21:04 +08:00
|
|
|
res = loadFileDICTIONARY(filename, data + 2, &datalen, 6, &keycount);
|
2019-03-10 07:00:59 +08:00
|
|
|
if (res || !keycount) {
|
2019-03-10 06:35:06 +08:00
|
|
|
free(data);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_EFILE;
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
2020-05-20 14:58:28 +08:00
|
|
|
// limited space on flash mem
|
|
|
|
if (keycount > 0xFFFF)
|
|
|
|
keycount &= 0xFFFF;
|
|
|
|
|
2019-03-10 06:35:06 +08:00
|
|
|
data[0] = (keycount >> 0) & 0xFF;
|
|
|
|
data[1] = (keycount >> 8) & 0xFF;
|
|
|
|
datalen += 2;
|
|
|
|
break;
|
|
|
|
case DICTIONARY_T55XX:
|
2020-10-03 23:12:58 +08:00
|
|
|
offset = DEFAULT_T55XX_KEYS_OFFSET;
|
2019-04-29 01:21:04 +08:00
|
|
|
res = loadFileDICTIONARY(filename, data + 2, &datalen, 4, &keycount);
|
2019-03-10 07:00:59 +08:00
|
|
|
if (res || !keycount) {
|
2019-03-10 06:35:06 +08:00
|
|
|
free(data);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_EFILE;
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
2020-05-20 14:58:28 +08:00
|
|
|
// limited space on flash mem
|
|
|
|
if (keycount > 0xFFFF)
|
|
|
|
keycount &= 0xFFFF;
|
|
|
|
|
2019-03-10 06:35:06 +08:00
|
|
|
data[0] = (keycount >> 0) & 0xFF;
|
|
|
|
data[1] = (keycount >> 8) & 0xFF;
|
|
|
|
datalen += 2;
|
|
|
|
break;
|
|
|
|
case DICTIONARY_ICLASS:
|
2020-10-03 23:12:58 +08:00
|
|
|
offset = DEFAULT_ICLASS_KEYS_OFFSET;
|
2019-04-29 01:21:04 +08:00
|
|
|
res = loadFileDICTIONARY(filename, data + 2, &datalen, 8, &keycount);
|
2019-03-10 07:00:59 +08:00
|
|
|
if (res || !keycount) {
|
2019-03-10 06:35:06 +08:00
|
|
|
free(data);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_EFILE;
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
2020-05-20 14:58:28 +08:00
|
|
|
// limited space on flash mem
|
|
|
|
if (keycount > 0xFFFF)
|
|
|
|
keycount &= 0xFFFF;
|
|
|
|
|
2019-03-10 06:35:06 +08:00
|
|
|
data[0] = (keycount >> 0) & 0xFF;
|
|
|
|
data[1] = (keycount >> 8) & 0xFF;
|
|
|
|
datalen += 2;
|
|
|
|
break;
|
2019-04-14 04:46:08 +08:00
|
|
|
case DICTIONARY_NONE:
|
2019-09-05 06:48:48 +08:00
|
|
|
res = loadFile_safe(filename, ".bin", (void **)&data, &datalen);
|
2019-09-01 03:49:59 +08:00
|
|
|
if (res != PM3_SUCCESS) {
|
2019-03-10 06:35:06 +08:00
|
|
|
free(data);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_EFILE;
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (datalen > FLASH_MEM_MAX_SIZE) {
|
2019-08-08 22:57:33 +08:00
|
|
|
PrintAndLogEx(ERR, "error, filesize is larger than available memory");
|
2019-03-10 06:35:06 +08:00
|
|
|
free(data);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_EOVFLOW;
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2019-09-01 03:49:59 +08:00
|
|
|
// not needed when we transite to loadxxxx_safe methods.(iceman)
|
2019-03-25 05:09:25 +08:00
|
|
|
uint8_t *newdata = realloc(data, datalen);
|
|
|
|
if (newdata == NULL) {
|
|
|
|
free(data);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_EMALLOC;
|
2019-03-25 05:09:25 +08:00
|
|
|
} else {
|
|
|
|
data = newdata;
|
2019-03-19 03:35:03 +08:00
|
|
|
}
|
2019-03-10 06:35:06 +08:00
|
|
|
|
|
|
|
//Send to device
|
|
|
|
uint32_t bytes_sent = 0;
|
|
|
|
uint32_t bytes_remaining = datalen;
|
2019-05-08 00:50:45 +08:00
|
|
|
|
|
|
|
// fast push mode
|
|
|
|
conn.block_after_ACK = true;
|
2019-05-08 07:35:51 +08:00
|
|
|
|
2019-03-10 07:00:59 +08:00
|
|
|
while (bytes_remaining > 0) {
|
2019-03-10 06:35:06 +08:00
|
|
|
uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining);
|
|
|
|
|
|
|
|
clearCommandBuffer();
|
2019-05-08 00:50:45 +08:00
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
SendCommandOLD(CMD_FLASHMEM_WRITE, offset + bytes_sent, bytes_in_packet, 0, data + bytes_sent, bytes_in_packet);
|
2019-03-10 06:35:06 +08:00
|
|
|
|
|
|
|
bytes_remaining -= bytes_in_packet;
|
|
|
|
bytes_sent += bytes_in_packet;
|
|
|
|
|
2019-04-18 18:43:35 +08:00
|
|
|
PacketResponseNG resp;
|
2020-10-03 23:12:58 +08:00
|
|
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) {
|
2019-03-10 06:35:06 +08:00
|
|
|
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
2019-04-26 19:53:11 +08:00
|
|
|
conn.block_after_ACK = false;
|
2019-03-10 06:35:06 +08:00
|
|
|
free(data);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_ETIMEOUT;
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
|
|
|
|
2019-04-18 05:44:48 +08:00
|
|
|
uint8_t isok = resp.oldarg[0] & 0xFF;
|
2019-04-19 05:26:12 +08:00
|
|
|
if (!isok) {
|
2019-05-08 07:35:51 +08:00
|
|
|
conn.block_after_ACK = false;
|
2019-03-10 06:35:06 +08:00
|
|
|
PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_EFLASH;
|
|
|
|
}
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
|
|
|
|
2019-05-08 00:50:45 +08:00
|
|
|
conn.block_after_ACK = false;
|
|
|
|
free(data);
|
2020-10-03 23:12:58 +08:00
|
|
|
PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, offset);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_SUCCESS;
|
2018-05-03 18:15:03 +08:00
|
|
|
}
|
2020-10-03 23:12:58 +08:00
|
|
|
|
2019-07-15 18:48:43 +08:00
|
|
|
static int CmdFlashMemDump(const char *Cmd) {
|
2018-05-03 18:15:03 +08:00
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
CLIParserContext *ctx;
|
|
|
|
CLIParserInit(&ctx, "mem dump",
|
|
|
|
"Dumps flash memory on device into a file or view in console",
|
|
|
|
"mem dump -f myfile -> download all flashmem to file\n"
|
|
|
|
"mem dump --view -o 262015 --len 128 -> display 128 bytes from offset 262015 (RSA sig)\n"
|
|
|
|
"mem dump --view -f myfile -o 241664 --len 58 -> display 58 bytes from offset 241664 and save to file"
|
2020-10-07 00:00:00 +08:00
|
|
|
);
|
2020-10-03 23:12:58 +08:00
|
|
|
|
|
|
|
void *argtable[] = {
|
|
|
|
arg_param_begin,
|
|
|
|
arg_int0("o", "offset", "<dec>", "offset in memory"),
|
|
|
|
arg_int0("l", "len", "<dec>", "length"),
|
|
|
|
arg_lit0("v", "view", "view dump"),
|
|
|
|
arg_strx0("f", "file", "<filename>", "file name"),
|
2021-03-10 04:16:51 +08:00
|
|
|
arg_int0("c", "cols", "<dec>", "column breaks (def 32)"),
|
2020-10-03 23:12:58 +08:00
|
|
|
arg_param_end
|
|
|
|
};
|
|
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
|
|
|
|
|
|
int offset = arg_get_int_def(ctx, 1, 0);
|
|
|
|
int len = arg_get_int_def(ctx, 2, FLASH_MEM_MAX_SIZE);
|
|
|
|
bool view = arg_get_lit(ctx, 3);
|
|
|
|
int fnlen = 0;
|
2019-03-10 06:35:06 +08:00
|
|
|
char filename[FILE_PATH_SIZE] = {0};
|
2020-10-07 00:00:00 +08:00
|
|
|
CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
2021-03-10 04:16:51 +08:00
|
|
|
int breaks = arg_get_int_def(ctx, 5, 32);
|
2020-10-03 23:12:58 +08:00
|
|
|
CLIParserFree(ctx);
|
2019-03-10 06:35:06 +08:00
|
|
|
|
2019-03-10 07:00:59 +08:00
|
|
|
uint8_t *dump = calloc(len, sizeof(uint8_t));
|
2019-03-10 06:35:06 +08:00
|
|
|
if (!dump) {
|
2019-08-08 22:57:33 +08:00
|
|
|
PrintAndLogEx(ERR, "error, cannot allocate memory ");
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_EMALLOC;
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
PrintAndLogEx(INFO, "downloading "_YELLOW_("%u")" bytes from flash memory", len);
|
|
|
|
if (!GetFromDevice(FLASH_MEM, dump, len, offset, NULL, 0, NULL, -1, true)) {
|
|
|
|
PrintAndLogEx(FAILED, "ERROR; downloading from flash memory");
|
2019-03-10 06:35:06 +08:00
|
|
|
free(dump);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_EFLASH;
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
if (view) {
|
|
|
|
PrintAndLogEx(INFO, "---- " _CYAN_("data") " ---------------");
|
2021-03-10 04:16:51 +08:00
|
|
|
print_hex_break(dump, len, breaks);
|
2019-07-15 18:48:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (filename[0] != '\0') {
|
|
|
|
saveFile(filename, ".bin", dump, len);
|
|
|
|
saveFileEML(filename, dump, len, 16);
|
|
|
|
}
|
|
|
|
|
2019-03-10 06:35:06 +08:00
|
|
|
free(dump);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_SUCCESS;
|
2018-05-03 18:15:03 +08:00
|
|
|
}
|
2020-10-03 23:12:58 +08:00
|
|
|
|
2019-04-12 06:38:54 +08:00
|
|
|
static int CmdFlashMemWipe(const char *Cmd) {
|
2018-05-03 18:15:03 +08:00
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
CLIParserContext *ctx;
|
|
|
|
CLIParserInit(&ctx, "mem wipe",
|
|
|
|
"Wipe flash memory on device, which fills it with 0xFF\n"
|
2021-03-31 21:52:57 +08:00
|
|
|
_WHITE_("[ ") _RED_("!!! OBS") _WHITE_(" ] use with caution"),
|
|
|
|
"mem wipe -p 0 -> wipes first page"
|
|
|
|
// "mem wipe -i -> inital total wipe"
|
2020-10-07 00:00:00 +08:00
|
|
|
);
|
2020-10-03 23:12:58 +08:00
|
|
|
|
|
|
|
void *argtable[] = {
|
|
|
|
arg_param_begin,
|
|
|
|
arg_int0("p", NULL, "<dec>", "0,1,2 page memory"),
|
|
|
|
// arg_lit0("i", NULL, "inital total wipe"),
|
|
|
|
arg_param_end
|
|
|
|
};
|
|
|
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
|
|
|
|
2019-03-10 06:35:06 +08:00
|
|
|
bool initalwipe = false;
|
2020-10-03 23:12:58 +08:00
|
|
|
int page = arg_get_int_def(ctx, 1, -1);
|
|
|
|
// initalwipe = arg_get_lit(ctx, 2);
|
|
|
|
CLIParserFree(ctx);
|
2019-03-10 06:35:06 +08:00
|
|
|
|
2020-10-07 00:00:00 +08:00
|
|
|
if (page < 0 || page > 2) {
|
2020-10-03 23:12:58 +08:00
|
|
|
PrintAndLogEx(WARNING, "page must be 0, 1 or 2");
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_EINVARG;
|
|
|
|
}
|
2019-03-10 06:35:06 +08:00
|
|
|
|
|
|
|
clearCommandBuffer();
|
2019-05-08 00:50:45 +08:00
|
|
|
SendCommandMIX(CMD_FLASHMEM_WIPE, page, initalwipe, 0, NULL, 0);
|
2019-04-18 18:43:35 +08:00
|
|
|
PacketResponseNG resp;
|
2019-03-10 07:00:59 +08:00
|
|
|
if (!WaitForResponseTimeout(CMD_ACK, &resp, 8000)) {
|
2019-03-10 06:35:06 +08:00
|
|
|
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_ETIMEOUT;
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
2020-10-07 00:00:00 +08:00
|
|
|
|
|
|
|
const char *msg = "Flash WIPE ";
|
2019-04-18 05:44:48 +08:00
|
|
|
uint8_t isok = resp.oldarg[0] & 0xFF;
|
2019-03-10 06:35:06 +08:00
|
|
|
if (isok)
|
2020-10-03 23:12:58 +08:00
|
|
|
PrintAndLogEx(SUCCESS, "%s ( " _GREEN_("ok")" )", msg);
|
2019-04-19 05:26:12 +08:00
|
|
|
else {
|
2020-10-03 23:12:58 +08:00
|
|
|
PrintAndLogEx(FAILED, "%s ( " _RED_("failed") " )", msg);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_EFLASH;
|
|
|
|
}
|
2019-03-10 06:35:06 +08:00
|
|
|
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_SUCCESS;
|
2018-05-03 18:15:03 +08:00
|
|
|
}
|
2018-05-23 15:30:33 +08:00
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
static int CmdFlashMemInfo(const char *Cmd) {
|
2019-03-10 06:35:06 +08:00
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
CLIParserContext *ctx;
|
|
|
|
CLIParserInit(&ctx, "mem info",
|
|
|
|
"Collect signature and verify it from flash memory",
|
|
|
|
"mem info"
|
2020-10-07 00:00:00 +08:00
|
|
|
);
|
2020-10-03 23:12:58 +08:00
|
|
|
|
|
|
|
void *argtable[] = {
|
|
|
|
arg_param_begin,
|
2021-03-07 15:56:36 +08:00
|
|
|
arg_lit0("s", "sign", "create a signature"),
|
|
|
|
arg_str0("d", NULL, "<hex>", "flash memory id, 8 hex bytes"),
|
2021-03-31 21:52:57 +08:00
|
|
|
arg_str0("p", "pem", "<fn>", "key in PEM format"),
|
|
|
|
arg_lit0("v", "verbose", "verbose output"),
|
2021-03-07 15:56:36 +08:00
|
|
|
// arg_lit0("w", "write", "write signature to flash memory"),
|
2020-10-03 23:12:58 +08:00
|
|
|
arg_param_end
|
|
|
|
};
|
|
|
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
bool shall_sign = arg_get_lit(ctx, 1);
|
2021-03-07 15:56:36 +08:00
|
|
|
|
|
|
|
int dlen = 0;
|
|
|
|
uint8_t id[8] = {0};
|
|
|
|
int res = CLIParamHexToBuf(arg_get_str(ctx, 2), id, sizeof(id), &dlen);
|
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
int pemlen = 0;
|
|
|
|
char pem_fn[FILE_PATH_SIZE] = {0};
|
|
|
|
CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)pem_fn, FILE_PATH_SIZE, &pemlen);
|
|
|
|
|
|
|
|
bool verbose = arg_get_lit(ctx, 4);
|
|
|
|
bool shall_write = false;
|
|
|
|
// shall_write = arg_get_lit(ctx, 5);
|
2020-10-03 23:12:58 +08:00
|
|
|
CLIParserFree(ctx);
|
2019-03-10 06:35:06 +08:00
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
if (res || (dlen > 0 && dlen < sizeof(id)) ) {
|
2021-03-07 15:56:36 +08:00
|
|
|
PrintAndLogEx(FAILED, "Error parsing flash memory id, expect 8, got %d", dlen);
|
|
|
|
return PM3_EINVARG;
|
|
|
|
}
|
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
// 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) {
|
|
|
|
if (searchFile(&path, RESOURCES_SUBDIR, pem_fn, "", false) != PM3_SUCCESS) {
|
|
|
|
return PM3_EFILE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PrintAndLogEx(INFO, "loading file `" _YELLOW_("%s") "`" NOLF, path);
|
|
|
|
|
|
|
|
// load private
|
|
|
|
res = mbedtls_pk_parse_keyfile(&pkctx, path, NULL);
|
|
|
|
//res = mbedtls_pk_parse_public_keyfile(&pkctx, path);
|
|
|
|
if (res == 0) {
|
|
|
|
PrintAndLogEx(NORMAL, " ( " _GREEN_("ok") " )");
|
|
|
|
} else {
|
|
|
|
PrintAndLogEx(NORMAL, " ( " _RED_("fail") " )");
|
|
|
|
mbedtls_pk_free(&pkctx);
|
|
|
|
free(path);
|
|
|
|
return PM3_EFILE;
|
|
|
|
}
|
|
|
|
|
|
|
|
mbedtls_rsa_context *rsa = (mbedtls_rsa_context*)pkctx.pk_ctx;
|
|
|
|
if (rsa == NULL) {
|
|
|
|
PrintAndLogEx(FAILED, "failed to allocate rsa context memory");
|
|
|
|
return PM3_EMALLOC;
|
|
|
|
}
|
|
|
|
free(path);
|
|
|
|
got_private = true;
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// it not loaded, we need to setup the context manually
|
|
|
|
if (mbedtls_pk_setup( &pkctx, mbedtls_pk_info_from_type( (mbedtls_pk_type_t) MBEDTLS_PK_RSA ) ) != 0 ) {
|
|
|
|
PrintAndLogEx(FAILED, "failed, mbedtls_pk_setup returned ");
|
|
|
|
return PM3_ESOFT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-07 15:56:36 +08:00
|
|
|
// validate devicesignature data
|
2021-02-17 18:52:23 +08:00
|
|
|
rdv40_validation_t mem;
|
2021-03-07 15:56:36 +08:00
|
|
|
res = rdv4_get_signature(&mem);
|
2021-02-17 18:52:23 +08:00
|
|
|
if (res != PM3_SUCCESS) {
|
|
|
|
return res;
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
2021-04-04 23:01:43 +08:00
|
|
|
|
2021-02-17 18:52:23 +08:00
|
|
|
res = rdv4_validate(&mem);
|
2019-03-10 06:35:06 +08:00
|
|
|
|
|
|
|
// Flash ID hash (sha1)
|
2020-10-03 23:12:58 +08:00
|
|
|
uint8_t sha_hash[20] = {0};
|
2019-03-10 07:00:59 +08:00
|
|
|
mbedtls_sha1(mem.flashid, sizeof(mem.flashid), sha_hash);
|
2019-03-10 06:35:06 +08:00
|
|
|
|
|
|
|
// print header
|
2020-10-03 23:12:58 +08:00
|
|
|
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)));
|
2021-03-31 21:52:57 +08:00
|
|
|
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));
|
|
|
|
}
|
2021-03-07 15:56:36 +08:00
|
|
|
PrintAndLogEx(
|
|
|
|
(res == PM3_SUCCESS) ? SUCCESS : FAILED,
|
|
|
|
"Signature............ ( %s )",
|
|
|
|
(res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")
|
|
|
|
);
|
2020-10-03 23:12:58 +08:00
|
|
|
PrintAndLogEx(NORMAL, "");
|
2018-05-23 15:30:33 +08:00
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
mbedtls_rsa_context *rsa = NULL;
|
2019-03-10 06:35:06 +08:00
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
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 {
|
2019-03-10 06:35:06 +08:00
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
rsa = (mbedtls_rsa_context *)calloc(1, sizeof(mbedtls_rsa_context));
|
|
|
|
mbedtls_rsa_init(rsa, MBEDTLS_RSA_PKCS_V15, 0);
|
|
|
|
rsa->len = RRG_RSA_KEY_LEN;
|
2021-02-17 18:52:23 +08:00
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
// add public key
|
|
|
|
mbedtls_mpi_read_string(&rsa->N, 16, RRG_RSA_N);
|
|
|
|
mbedtls_mpi_read_string(&rsa->E, 16, RRG_RSA_E);
|
|
|
|
}
|
2019-03-10 06:35:06 +08:00
|
|
|
|
2020-10-03 23:12:58 +08:00
|
|
|
PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA Public key") " --------------");
|
2021-03-31 21:52:57 +08:00
|
|
|
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, "");
|
|
|
|
}
|
2020-10-03 23:12:58 +08:00
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
bool is_keyok = (mbedtls_rsa_check_pubkey(rsa) == 0);
|
2021-03-07 15:56:36 +08:00
|
|
|
PrintAndLogEx(
|
|
|
|
(is_keyok) ? SUCCESS : FAILED,
|
2021-03-31 21:52:57 +08:00
|
|
|
"RRG/Iceman RSA public key check.... ( %s )",
|
2021-03-07 15:56:36 +08:00
|
|
|
(is_keyok) ? _GREEN_("ok") : _RED_("fail")
|
|
|
|
);
|
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
is_keyok = (mbedtls_rsa_check_privkey(rsa) == 0);
|
|
|
|
if (verbose) {
|
|
|
|
PrintAndLogEx(
|
|
|
|
(is_keyok) ? SUCCESS : FAILED,
|
|
|
|
"RRG/Iceman RSA private key check... ( %s )",
|
|
|
|
(is_keyok) ? _GREEN_("ok") : _YELLOW_("N/A")
|
|
|
|
);
|
|
|
|
}
|
2021-03-07 15:56:36 +08:00
|
|
|
|
2019-03-10 06:35:06 +08:00
|
|
|
// to be verified
|
2021-02-17 18:52:23 +08:00
|
|
|
uint8_t from_device[RRG_RSA_KEY_LEN];
|
|
|
|
memcpy(from_device, mem.signature, RRG_RSA_KEY_LEN);
|
2019-03-10 06:35:06 +08:00
|
|
|
|
2021-02-17 18:52:23 +08:00
|
|
|
// to be signed
|
|
|
|
uint8_t sign[RRG_RSA_KEY_LEN];
|
|
|
|
memset(sign, 0, RRG_RSA_KEY_LEN);
|
2019-03-10 06:35:06 +08:00
|
|
|
|
|
|
|
// Signing (private key)
|
|
|
|
if (shall_sign) {
|
2021-03-07 15:56:36 +08:00
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
if (is_keyok) {
|
2019-03-10 06:35:06 +08:00
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
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);
|
|
|
|
PrintAndLogEx(
|
|
|
|
(is_signed == 0) ? SUCCESS : FAILED,
|
|
|
|
"RSA signing... ( %s )",
|
|
|
|
(is_signed == 0) ? _GREEN_("ok") : _RED_("fail")
|
|
|
|
);
|
|
|
|
|
|
|
|
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");
|
2020-10-03 23:12:58 +08:00
|
|
|
}
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify (public key)
|
2021-03-31 21:52:57 +08:00
|
|
|
bool is_verified = (mbedtls_rsa_pkcs1_verify(rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, from_device) == 0);
|
|
|
|
|
|
|
|
mbedtls_pk_free(&pkctx);
|
2021-02-17 18:52:23 +08:00
|
|
|
|
2021-03-31 21:52:57 +08:00
|
|
|
PrintAndLogEx(NORMAL, "");
|
2021-02-17 18:52:23 +08:00
|
|
|
PrintAndLogEx(
|
2021-03-07 15:56:36 +08:00
|
|
|
(is_verified) ? SUCCESS : FAILED,
|
2021-03-31 21:52:57 +08:00
|
|
|
"Genuine Proxmark3 RDV4 signature detected... %s",
|
|
|
|
(is_verified) ? ":heavy_check_mark:" : ":x:"
|
2021-02-17 18:52:23 +08:00
|
|
|
);
|
2020-10-03 23:12:58 +08:00
|
|
|
PrintAndLogEx(NORMAL, "");
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_SUCCESS;
|
2018-05-23 15:30:33 +08:00
|
|
|
}
|
2018-05-03 18:15:03 +08:00
|
|
|
|
|
|
|
static command_t CommandTable[] = {
|
2021-03-09 23:09:36 +08:00
|
|
|
{"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "{ SPI File system }"},
|
|
|
|
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
|
|
|
{"baudrate", CmdFlashmemSpiBaud, IfPm3Flash, "Set Flash memory Spi baudrate"},
|
|
|
|
{"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory"},
|
|
|
|
{"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information"},
|
|
|
|
{"load", CmdFlashMemLoad, IfPm3Flash, "Load data to flash memory"},
|
|
|
|
{"wipe", CmdFlashMemWipe, IfPm3Flash, "Wipe data from flash memory"},
|
2019-05-02 02:48:15 +08:00
|
|
|
{NULL, NULL, NULL, NULL}
|
2018-05-03 18:15:03 +08:00
|
|
|
};
|
|
|
|
|
2019-04-12 06:38:54 +08:00
|
|
|
static int CmdHelp(const char *Cmd) {
|
|
|
|
(void)Cmd; // Cmd is not used so far
|
|
|
|
CmdsHelp(CommandTable);
|
2019-04-19 05:26:12 +08:00
|
|
|
return PM3_SUCCESS;
|
2018-05-03 18:15:03 +08:00
|
|
|
}
|
|
|
|
|
2019-04-12 06:38:54 +08:00
|
|
|
int CmdFlashMem(const char *Cmd) {
|
|
|
|
clearCommandBuffer();
|
2019-04-19 06:42:25 +08:00
|
|
|
return CmdsParse(CommandTable, Cmd);
|
2018-05-03 18:15:03 +08:00
|
|
|
}
|