mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-05-12 11:18:11 -07:00
Merge pull request #3133 from kormax/aliro-vas-maintenance
Aliro & VAS maintenance
This commit is contained in:
Binary file not shown.
Binary file not shown.
+53
-122
@@ -65,18 +65,14 @@ static const char ALIRO_DEFAULT_STEP_UP_SCOPE[] = "matter1";
|
||||
#define ALIRO_MAX_TLV 512
|
||||
#define ALIRO_MAX_STEP_UP_SCOPES 16
|
||||
#define ALIRO_MAX_STEP_UP_SCOPE_LEN 128
|
||||
#define ALIRO_MAX_KEY_INPUT 8192
|
||||
#define ALIRO_ACCESS_DOCUMENT_TYPE "aliro-a"
|
||||
#define ALIRO_REVOCATION_DOCUMENT_TYPE "aliro-r"
|
||||
|
||||
#define ALIRO_SIGNALING_ACCESS_DOCUMENT_RETRIEVABLE (1U << 0)
|
||||
#define ALIRO_SIGNALING_REVOCATION_DOCUMENT_RETRIEVABLE (1U << 1)
|
||||
#define ALIRO_SIGNALING_STEP_UP_SELECT_REQUIRED_FOR_DOC_RETRIEVAL (1U << 2)
|
||||
|
||||
typedef struct {
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
bool seeded;
|
||||
} aliro_rng_t;
|
||||
static const uint8_t ALIRO_RNG_PERSONALIZATION[] = "pm3-aliro";
|
||||
|
||||
typedef struct {
|
||||
uint16_t type;
|
||||
@@ -207,63 +203,6 @@ static const char *get_aliro_application_type_name(uint16_t type) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void aliro_print_big_header(const char *title) {
|
||||
static const char dashes[] = "----------------------------------------------------------------------------------------------------";
|
||||
const size_t width = 82;
|
||||
const size_t title_len = strlen(title);
|
||||
|
||||
if (title_len + 2 >= width) {
|
||||
PrintAndLogEx(INFO, _CYAN_("%s"), title);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t dash_count = width - (title_len + 2);
|
||||
size_t left = dash_count / 2;
|
||||
size_t right = dash_count - left;
|
||||
if (left > (sizeof(dashes) - 1)) {
|
||||
left = sizeof(dashes) - 1;
|
||||
}
|
||||
if (right > (sizeof(dashes) - 1)) {
|
||||
right = sizeof(dashes) - 1;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "%.*s " _CYAN_("%s") " %.*s",
|
||||
(int)left, dashes, title, (int)right, dashes);
|
||||
}
|
||||
|
||||
static int aliro_rng_init(aliro_rng_t *rng) {
|
||||
if (rng == NULL) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
memset(rng, 0, sizeof(*rng));
|
||||
mbedtls_entropy_init(&rng->entropy);
|
||||
mbedtls_ctr_drbg_init(&rng->ctr_drbg);
|
||||
|
||||
static const uint8_t personalization[] = "pm3-aliro";
|
||||
int ret = mbedtls_ctr_drbg_seed(&rng->ctr_drbg, mbedtls_entropy_func, &rng->entropy,
|
||||
personalization, sizeof(personalization) - 1);
|
||||
if (ret != 0) {
|
||||
PrintAndLogEx(ERR, "Failed to initialize random generator (mbedtls: %d)", ret);
|
||||
mbedtls_ctr_drbg_free(&rng->ctr_drbg);
|
||||
mbedtls_entropy_free(&rng->entropy);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
rng->seeded = true;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static void aliro_rng_free(aliro_rng_t *rng) {
|
||||
if (rng == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
mbedtls_ctr_drbg_free(&rng->ctr_drbg);
|
||||
mbedtls_entropy_free(&rng->entropy);
|
||||
rng->seeded = false;
|
||||
}
|
||||
|
||||
static int aliro_ber_encode_length(size_t len, uint8_t *out, size_t out_max, size_t *out_len) {
|
||||
if (out == NULL || out_len == NULL) {
|
||||
return PM3_EINVARG;
|
||||
@@ -644,7 +583,7 @@ static bool aliro_choose_protocol_version(const aliro_select_info_t *select_info
|
||||
return true;
|
||||
}
|
||||
|
||||
static int aliro_load_private_key(const uint8_t private_key_bytes[32], mbedtls_ecp_keypair *keypair, aliro_rng_t *rng) {
|
||||
static int aliro_load_private_key(const uint8_t private_key_bytes[32], mbedtls_ecp_keypair *keypair, pcrypto_rng_t *rng) {
|
||||
if (private_key_bytes == NULL || keypair == NULL || rng == NULL || rng->seeded == false) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
@@ -677,7 +616,7 @@ static int aliro_load_private_key(const uint8_t private_key_bytes[32], mbedtls_e
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int aliro_generate_ephemeral_keypair(mbedtls_ecp_keypair *keypair, aliro_rng_t *rng) {
|
||||
static int aliro_generate_ephemeral_keypair(mbedtls_ecp_keypair *keypair, pcrypto_rng_t *rng) {
|
||||
if (keypair == NULL || rng == NULL || rng->seeded == false) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
@@ -740,7 +679,7 @@ static int aliro_compute_kdh(const mbedtls_ecp_keypair *reader_ephemeral_private
|
||||
const uint8_t endpoint_ephemeral_public_key[65],
|
||||
const uint8_t transaction_identifier[16],
|
||||
uint8_t kdh[32],
|
||||
aliro_rng_t *rng) {
|
||||
pcrypto_rng_t *rng) {
|
||||
if (reader_ephemeral_private_key == NULL || endpoint_ephemeral_public_key == NULL ||
|
||||
transaction_identifier == NULL || kdh == NULL || rng == NULL || rng->seeded == false) {
|
||||
return PM3_EINVARG;
|
||||
@@ -1117,18 +1056,6 @@ static bool aliro_is_zeroed(const uint8_t *buf, size_t len) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool aliro_is_ascii(const uint8_t *buf, size_t len) {
|
||||
if (buf == NULL) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (buf[i] < 0x20 || buf[i] > 0x7E) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void aliro_print_timestamp(const char *label, const uint8_t *timestamp, bool have) {
|
||||
if (have == false) {
|
||||
PrintAndLogEx(INFO, "%s not present", label);
|
||||
@@ -1138,27 +1065,13 @@ static void aliro_print_timestamp(const char *label, const uint8_t *timestamp, b
|
||||
PrintAndLogEx(INFO, "%s all-zero", label);
|
||||
return;
|
||||
}
|
||||
if (aliro_is_ascii(timestamp, 20)) {
|
||||
if (is_printable_ascii(timestamp, 20)) {
|
||||
PrintAndLogEx(INFO, "%s %s", label, sprint_ascii(timestamp, 20));
|
||||
} else {
|
||||
PrintAndLogEx(INFO, "%s %s", label, sprint_hex_inrow(timestamp, 20));
|
||||
}
|
||||
}
|
||||
|
||||
static int aliro_buf_append(uint8_t *buf, size_t buf_len, size_t *offset, const uint8_t *data, size_t data_len) {
|
||||
if (buf == NULL || offset == NULL || (data_len > 0 && data == NULL)) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (*offset > buf_len || data_len > (buf_len - *offset)) {
|
||||
return PM3_EOVFLOW;
|
||||
}
|
||||
if (data_len > 0) {
|
||||
memcpy(buf + *offset, data, data_len);
|
||||
*offset += data_len;
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int aliro_build_kdf_info(uint8_t *info, size_t info_len, size_t *written_len,
|
||||
const uint8_t endpoint_ephemeral_public_key_x[32],
|
||||
const uint8_t *auth0_suffix, size_t auth0_suffix_len) {
|
||||
@@ -1168,11 +1081,11 @@ static int aliro_build_kdf_info(uint8_t *info, size_t info_len, size_t *written_
|
||||
}
|
||||
|
||||
*written_len = 0;
|
||||
int res = aliro_buf_append(info, info_len, written_len, endpoint_ephemeral_public_key_x, 32);
|
||||
int res = buffer_append_bytes_with_offset(info, info_len, written_len, endpoint_ephemeral_public_key_x, 32);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
return aliro_buf_append(info, info_len, written_len, auth0_suffix, auth0_suffix_len);
|
||||
return buffer_append_bytes_with_offset(info, info_len, written_len, auth0_suffix, auth0_suffix_len);
|
||||
}
|
||||
|
||||
static int aliro_build_kdf_salt(uint8_t *salt, size_t salt_len, size_t *written_len,
|
||||
@@ -1197,44 +1110,44 @@ static int aliro_build_kdf_salt(uint8_t *salt, size_t salt_len, size_t *written_
|
||||
uint8_t flag[2] = {command_parameters, authentication_policy};
|
||||
|
||||
*written_len = 0;
|
||||
int res = aliro_buf_append(salt, salt_len, written_len, reader_public_key_x, 32);
|
||||
int res = buffer_append_bytes_with_offset(salt, salt_len, written_len, reader_public_key_x, 32);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
res = aliro_buf_append(salt, salt_len, written_len, mode, mode_len);
|
||||
res = buffer_append_bytes_with_offset(salt, salt_len, written_len, mode, mode_len);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
res = aliro_buf_append(salt, salt_len, written_len, reader_identifier, 32);
|
||||
res = buffer_append_bytes_with_offset(salt, salt_len, written_len, reader_identifier, 32);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
res = aliro_buf_append(salt, salt_len, written_len, protocol_marker, sizeof(protocol_marker));
|
||||
res = buffer_append_bytes_with_offset(salt, salt_len, written_len, protocol_marker, sizeof(protocol_marker));
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
res = aliro_buf_append(salt, salt_len, written_len, protocol_version, 2);
|
||||
res = buffer_append_bytes_with_offset(salt, salt_len, written_len, protocol_version, 2);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
res = aliro_buf_append(salt, salt_len, written_len, reader_ephemeral_public_key_x, 32);
|
||||
res = buffer_append_bytes_with_offset(salt, salt_len, written_len, reader_ephemeral_public_key_x, 32);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
res = aliro_buf_append(salt, salt_len, written_len, transaction_identifier, 16);
|
||||
res = buffer_append_bytes_with_offset(salt, salt_len, written_len, transaction_identifier, 16);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
res = aliro_buf_append(salt, salt_len, written_len, flag, sizeof(flag));
|
||||
res = buffer_append_bytes_with_offset(salt, salt_len, written_len, flag, sizeof(flag));
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
res = aliro_buf_append(salt, salt_len, written_len, fci_proprietary_tlv, fci_proprietary_tlv_len);
|
||||
res = buffer_append_bytes_with_offset(salt, salt_len, written_len, fci_proprietary_tlv, fci_proprietary_tlv_len);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
if (endpoint_public_key_x != NULL) {
|
||||
res = aliro_buf_append(salt, salt_len, written_len, endpoint_public_key_x, 32);
|
||||
res = buffer_append_bytes_with_offset(salt, salt_len, written_len, endpoint_public_key_x, 32);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
@@ -1494,7 +1407,7 @@ static int aliro_read_prepare_session(aliro_read_state_t *state,
|
||||
const uint8_t reader_group_sub_identifier[16],
|
||||
const uint8_t reader_private_key_raw[32],
|
||||
const uint8_t *transaction_identifier_in, size_t transaction_identifier_len,
|
||||
aliro_rng_t *rng) {
|
||||
pcrypto_rng_t *rng) {
|
||||
if (state == NULL || reader_private_key == NULL || reader_ephemeral_key == NULL ||
|
||||
reader_group_identifier == NULL || reader_group_sub_identifier == NULL ||
|
||||
reader_private_key_raw == NULL || rng == NULL) {
|
||||
@@ -1518,7 +1431,7 @@ static int aliro_read_prepare_session(aliro_read_state_t *state,
|
||||
return res;
|
||||
}
|
||||
|
||||
aliro_print_big_header("Applet information");
|
||||
PrintAndLogInfoHeader("Applet information");
|
||||
print_aliro_select_info(&state->select_info);
|
||||
|
||||
if (state->select_info.have_proprietary_tlv == false) {
|
||||
@@ -1586,7 +1499,7 @@ aliro_append_tlv(0x4D, state->reader_identifier, 32, auth0_data, sizeof(auth0_da
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
aliro_print_big_header("AUTH0");
|
||||
PrintAndLogInfoHeader("AUTH0");
|
||||
PrintAndLogEx(INFO, "Reader group id........... %s", sprint_hex_inrow(reader_group_identifier, 16));
|
||||
PrintAndLogEx(INFO, "Reader sub id............. %s", sprint_hex_inrow(reader_group_sub_identifier, 16));
|
||||
PrintAndLogEx(INFO, "Reader id................. %s", sprint_hex_inrow(state->reader_identifier, 32));
|
||||
@@ -1703,7 +1616,7 @@ aliro_append_tlv(0x4D, state->reader_identifier, 32, auth0_data, sizeof(auth0_da
|
||||
|
||||
static int aliro_read_prepare_auth1_keys(aliro_read_state_t *state,
|
||||
mbedtls_ecp_keypair *reader_ephemeral_key,
|
||||
aliro_rng_t *rng) {
|
||||
pcrypto_rng_t *rng) {
|
||||
if (state == NULL || reader_ephemeral_key == NULL || rng == NULL) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
@@ -1881,7 +1794,7 @@ static void aliro_read_print_auth1_report(const aliro_read_state_t *state) {
|
||||
return;
|
||||
}
|
||||
|
||||
aliro_print_big_header("AUTH1");
|
||||
PrintAndLogInfoHeader("AUTH1");
|
||||
PrintAndLogEx(INFO, "AUTH1 signature........... %s", sprint_hex_inrow(state->auth1_parsed.signature, 64));
|
||||
if (state->auth1_parsed.have_key_slot) {
|
||||
PrintAndLogEx(INFO, "AUTH1 key slot............ %s", sprint_hex_inrow(state->auth1_parsed.key_slot, 8));
|
||||
@@ -1963,7 +1876,7 @@ static void aliro_read_print_fast_suggestion_note(const char *fast_cmd) {
|
||||
if (fast_cmd == NULL || fast_cmd[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
aliro_print_big_header("Note");
|
||||
PrintAndLogInfoHeader("Note");
|
||||
PrintAndLogEx(INFO, _GREEN_("use the following command to perform FAST authentication flow for this endpoint:"));
|
||||
PrintAndLogEx(INFO, _YELLOW_("%s"), fast_cmd);
|
||||
}
|
||||
@@ -1977,7 +1890,7 @@ static void aliro_print_bytes_or_ascii(const char *label, const uint8_t *data, s
|
||||
return;
|
||||
}
|
||||
|
||||
if (aliro_is_ascii(data, data_len)) {
|
||||
if (is_printable_ascii(data, data_len)) {
|
||||
PrintAndLogEx(INFO, "%s %s", label, sprint_ascii(data, data_len));
|
||||
} else {
|
||||
PrintAndLogEx(INFO, "%s %s", label, sprint_hex_inrow(data, data_len));
|
||||
@@ -3336,7 +3249,7 @@ static int aliro_read_do_step_up(const aliro_read_state_t *state,
|
||||
document_types[document_type_count++] = ALIRO_REVOCATION_DOCUMENT_TYPE;
|
||||
}
|
||||
|
||||
aliro_print_big_header("Step-up");
|
||||
PrintAndLogInfoHeader("Step-up");
|
||||
if (document_type_count == 0) {
|
||||
PrintAndLogEx(WARNING, "Signaling bitmap does not indicate retrievable step-up documents");
|
||||
return PM3_SUCCESS;
|
||||
@@ -3483,8 +3396,8 @@ static int aliro_read_auth_flow(const uint8_t *kpersistent, size_t kpersistent_l
|
||||
aliro_flow_t flow,
|
||||
const aliro_step_up_scopes_t *step_up_scopes) {
|
||||
int status = PM3_ESOFT;
|
||||
aliro_rng_t rng;
|
||||
int res = aliro_rng_init(&rng);
|
||||
pcrypto_rng_t rng;
|
||||
int res = pcrypto_rng_init(&rng, ALIRO_RNG_PERSONALIZATION, sizeof(ALIRO_RNG_PERSONALIZATION) - 1);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
@@ -3558,7 +3471,7 @@ static int aliro_read_auth_flow(const uint8_t *kpersistent, size_t kpersistent_l
|
||||
DropField();
|
||||
mbedtls_ecp_keypair_free(&reader_ephemeral_key);
|
||||
mbedtls_ecp_keypair_free(&reader_private_key);
|
||||
aliro_rng_free(&rng);
|
||||
pcrypto_rng_free(&rng);
|
||||
|
||||
if (status == PM3_SUCCESS && have_fast_suggestion_cmd) {
|
||||
aliro_read_print_fast_suggestion_note(fast_suggestion_cmd);
|
||||
@@ -3597,14 +3510,14 @@ static int CmdHFAliroRead(const char *Cmd) {
|
||||
"Execute ALIRO expedited flow and optional step-up document retrieval.",
|
||||
"hf aliro read --reader-group-id 00112233445566778899AABBCCDDEEFF --reader-sub-group-id 00112233445566778899AABBCCDDEEFF --reader-private-key 00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF\n"
|
||||
"hf aliro read --reader-group-id 00112233445566778899AABBCCDDEEFF --reader-private-key 00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF --transaction-id 00112233445566778899AABBCCDDEEFF --k-persistent 00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF --endpoint-public-key 04AABBCCDDEEFF00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF --flow fast -a\n"
|
||||
"hf aliro read --reader-group-id 00112233445566778899AABBCCDDEEFF --reader-private-key 00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF --step-up-scopes matter1,non_access_extensions");
|
||||
"hf aliro read --reader-group-id 00112233445566778899AABBCCDDEEFF --reader-private-key ./reader-private-key.pem --step-up-scopes matter1,non_access_extensions");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("k", "k-persistent,key-persistent,kpersistent,keypersistent,kp", "<hex>", "Kpersistent (32 bytes, optional; used for fast cryptogram verification)"),
|
||||
arg_str1("g", "reader-group-id,readergroupid,rgi", "<hex>", "Reader group identifier (16 bytes)"),
|
||||
arg_str0("s", "reader-sub-group-id,readersubid,rsi", "<hex>", "Reader subgroup identifier (16 bytes, default: all zeroes)"),
|
||||
arg_str1("p", "reader-private-key,readerprivkey,rpk", "<hex>", "Reader private key (32 bytes, P-256)"),
|
||||
arg_str1("p", "reader-private-key,readerprivkey,rpk", "<pem|der-b64|der-hex|scalar-b64|scalar-hex|path>", "Reader private key (P-256): PEM, DER hex, scalar hex/base64, or file path"),
|
||||
arg_str0("t", "transaction-id,ti", "<hex>", "Transaction identifier (16 bytes, optional; random if omitted)"),
|
||||
arg_str0("e", "endpoint-public-key,endpointpublickey,epk", "<hex>", "Endpoint public key for AUTH0 fast verification (32-byte X or 65-byte uncompressed)"),
|
||||
arg_str0("f", "flow", "<step-up|standard|fast>", "Transaction flow (default: step-up)"),
|
||||
@@ -3627,8 +3540,20 @@ static int CmdHFAliroRead(const char *Cmd) {
|
||||
CLIGetHexWithReturn(ctx, 3, reader_group_sub_identifier, &reader_group_sub_identifier_len);
|
||||
|
||||
uint8_t reader_private_key[32] = {0};
|
||||
int reader_private_key_len = 0;
|
||||
CLIGetHexWithReturn(ctx, 4, reader_private_key, &reader_private_key_len);
|
||||
char reader_private_key_text[ALIRO_MAX_KEY_INPUT] = {0};
|
||||
int reader_private_key_text_len = 0;
|
||||
if (CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)reader_private_key_text, sizeof(reader_private_key_text), &reader_private_key_text_len) != 0) {
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (reader_private_key_text_len < 0) {
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if ((size_t)reader_private_key_text_len >= sizeof(reader_private_key_text)) {
|
||||
reader_private_key_text_len = (int)sizeof(reader_private_key_text) - 1;
|
||||
}
|
||||
reader_private_key_text[reader_private_key_text_len] = '\0';
|
||||
|
||||
uint8_t transaction_identifier[16] = {0};
|
||||
int transaction_identifier_len = 0;
|
||||
@@ -3668,8 +3593,14 @@ static int CmdHFAliroRead(const char *Cmd) {
|
||||
PrintAndLogEx(ERR, "readersubid must be 16 bytes (got %d)", reader_group_sub_identifier_len);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (reader_private_key_len != 32) {
|
||||
PrintAndLogEx(ERR, "readerprivkey must be 32 bytes (got %d)", reader_private_key_len);
|
||||
if (ensure_ec_private_key(reader_private_key_text, MBEDTLS_ECP_DP_SECP256R1,
|
||||
reader_private_key, sizeof(reader_private_key)) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(ERR, "readerprivkey has invalid format");
|
||||
PrintAndLogEx(INFO, "Accepted formats:");
|
||||
PrintAndLogEx(INFO, " 1) PEM string with headers (BEGIN PRIVATE KEY)");
|
||||
PrintAndLogEx(INFO, " 2) DER bytes as hex or base64");
|
||||
PrintAndLogEx(INFO, " 3) Scalar as hex or base64");
|
||||
PrintAndLogEx(INFO, " 4) File path to a key in any of the formats above");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (kpersistent_len != 0 && kpersistent_len != 32) {
|
||||
|
||||
@@ -2299,7 +2299,7 @@ static int CmdHFGSTRead(const char *Cmd) {
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str1("c", "collector-id,collectorid,cid", "<hex|dec>", "Collector identifier (32-bit value)"),
|
||||
arg_str1("p", "reader-private-key,readerprivkey,rpk", "<pem|pem-base64|der-hex|scalar-hex|path>", "Reader private key in PEM, PEM base64 body, DER hex, 32-byte scalar hex, or file path"),
|
||||
arg_str1("p", "reader-private-key,readerprivkey,rpk", "<pem|der-b64|der-hex|scalar-b64|scalar-hex|path>", "Reader private key: PEM, DER hex, scalar hex/base64, or file path"),
|
||||
arg_str0(NULL, "key-version,keyversion,kv", "<hex|dec>", "Long-term key version (default: 1)"),
|
||||
arg_str0(NULL, "session-id,sid", "<hex>", "Session id (8 bytes, random if omitted)"),
|
||||
arg_str0(NULL, "reader-nonce,nonce", "<hex>", "Reader nonce (32 bytes, random if omitted)"),
|
||||
@@ -2336,11 +2336,10 @@ static int CmdHFGSTRead(const char *Cmd) {
|
||||
ensure_ec_private_key(reader_key_text, MBEDTLS_ECP_DP_SECP256R1, cfg.reader_private_key, sizeof(cfg.reader_private_key)) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(ERR, "Invalid reader-private-key format");
|
||||
PrintAndLogEx(INFO, "Accepted formats:");
|
||||
PrintAndLogEx(INFO, " 1) PEM string (BEGIN/END EC PRIVATE KEY or PRIVATE KEY)");
|
||||
PrintAndLogEx(INFO, " 2) PEM base64 body only");
|
||||
PrintAndLogEx(INFO, " 3) DER key bytes as hex");
|
||||
PrintAndLogEx(INFO, " 4) Raw 32-byte private scalar as hex");
|
||||
PrintAndLogEx(INFO, " 5) File path to a key in one of the formats above");
|
||||
PrintAndLogEx(INFO, " 1) PEM string with headers (BEGIN PRIVATE KEY)");
|
||||
PrintAndLogEx(INFO, " 2) DER bytes as hex or base64");
|
||||
PrintAndLogEx(INFO, " 3) Scalar as hex or base64");
|
||||
PrintAndLogEx(INFO, " 4) File path to a key in any of the formats above");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
+84
-81
@@ -34,12 +34,15 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "crypto/libpcrypto.h"
|
||||
#include "fileutils.h"
|
||||
#include "mbedtls/ecp.h"
|
||||
#include "mbedtls/bignum.h"
|
||||
#include "mbedtls/ecdh.h"
|
||||
#include "mbedtls/ecc_point_compression.h"
|
||||
#include "mbedtls/gcm.h"
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include "mbedtls/entropy.h"
|
||||
|
||||
#define VAS_MAX_KEY_INPUT 8192
|
||||
|
||||
static const iso14a_polling_frame_t WUPA_FRAME = {
|
||||
.frame = { 0x52 },
|
||||
@@ -292,6 +295,9 @@ static int CreateGetVASDataCommand(const uint8_t *pidHash, const char *url, size
|
||||
|
||||
static int ParseGetVASDataResponse(const uint8_t *res, size_t resLen, uint8_t *cryptogram, size_t *cryptogramLen) {
|
||||
struct tlvdb *tlvRoot = tlvdb_parse_multi(res, resLen);
|
||||
if (tlvRoot == NULL) {
|
||||
return PM3_ECARDEXCHANGE;
|
||||
}
|
||||
|
||||
const struct tlvdb *cryptogramTlvdb = tlvdb_find_full(tlvRoot, 0x9F27);
|
||||
if (cryptogramTlvdb == NULL) {
|
||||
@@ -307,48 +313,52 @@ static int ParseGetVASDataResponse(const uint8_t *res, size_t resLen, uint8_t *c
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int LoadReaderPrivateKey(const uint8_t *buf, size_t bufLen, mbedtls_ecp_keypair *privKey) {
|
||||
struct tlvdb *derRoot = tlvdb_parse_multi(buf, bufLen);
|
||||
|
||||
const struct tlvdb *privkeyTlvdb = tlvdb_find_full(derRoot, 0x04);
|
||||
if (privkeyTlvdb == NULL) {
|
||||
tlvdb_free(derRoot);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
const struct tlv *privkeyTlv = tlvdb_get_tlv(privkeyTlvdb);
|
||||
|
||||
if (mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, privKey, privkeyTlv->value, privkeyTlv->len)) {
|
||||
tlvdb_free(derRoot);
|
||||
PrintAndLogEx(FAILED, "Unable to parse private key file. Should be DER encoded ASN1");
|
||||
return PM3_EINVARG;
|
||||
static int LoadReaderPrivateKey(const char *input_or_path, mbedtls_ecp_keypair *privKey) {
|
||||
uint8_t key_d[32] = {0};
|
||||
int res = ensure_ec_private_key(input_or_path, MBEDTLS_ECP_DP_SECP256R1, key_d, sizeof(key_d));
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(FAILED, "Invalid private key input");
|
||||
PrintAndLogEx(INFO, "Accepted formats:");
|
||||
PrintAndLogEx(INFO, " 1) PEM string with headers (BEGIN PRIVATE KEY)");
|
||||
PrintAndLogEx(INFO, " 2) DER bytes as hex or base64");
|
||||
PrintAndLogEx(INFO, " 3) Scalar as hex or base64");
|
||||
PrintAndLogEx(INFO, " 4) File path to a key in any of the formats above");
|
||||
return res;
|
||||
}
|
||||
|
||||
const struct tlvdb *pubkeyCoordsTlvdb = tlvdb_find_full(derRoot, 0x03);
|
||||
if (pubkeyCoordsTlvdb == NULL) {
|
||||
tlvdb_free(derRoot);
|
||||
PrintAndLogEx(FAILED, "Private key file should include public key component");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
const struct tlv *pubkeyCoordsTlv = tlvdb_get_tlv(pubkeyCoordsTlvdb);
|
||||
if (pubkeyCoordsTlv->len != 66 || pubkeyCoordsTlv->value[0] != 0x00 || pubkeyCoordsTlv->value[1] != 0x04) {
|
||||
tlvdb_free(derRoot);
|
||||
PrintAndLogEx(FAILED, "Invalid public key data");
|
||||
return PM3_EINVARG;
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
mbedtls_entropy_init(&entropy);
|
||||
mbedtls_ctr_drbg_init(&ctr_drbg);
|
||||
|
||||
static const uint8_t personalization[] = "pm3-vas";
|
||||
int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
|
||||
personalization, sizeof(personalization) - 1);
|
||||
if (ret != 0) {
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
PrintAndLogEx(FAILED, "Unable to initialize random generator for key derivation");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (mbedtls_ecp_point_read_binary(&privKey->grp, &privKey->Q, pubkeyCoordsTlv->value + 1, 65)) {
|
||||
PrintAndLogEx(FAILED, "Failed to read in public key coordinates");
|
||||
tlvdb_free(derRoot);
|
||||
ret = mbedtls_ecp_group_load(&privKey->grp, MBEDTLS_ECP_DP_SECP256R1);
|
||||
if (ret == 0) {
|
||||
ret = mbedtls_mpi_read_binary(&privKey->d, key_d, sizeof(key_d));
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = mbedtls_ecp_check_privkey(&privKey->grp, &privKey->d);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = mbedtls_ecp_mul(&privKey->grp, &privKey->Q, &privKey->d, &privKey->grp.G,
|
||||
mbedtls_ctr_drbg_random, &ctr_drbg);
|
||||
}
|
||||
mbedtls_ctr_drbg_free(&ctr_drbg);
|
||||
mbedtls_entropy_free(&entropy);
|
||||
|
||||
if (ret != 0 || mbedtls_ecp_check_pubkey(&privKey->grp, &privKey->Q) != 0) {
|
||||
PrintAndLogEx(FAILED, "VAS protocol requires a valid private key on curve P-256");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (mbedtls_ecp_check_pubkey(&privKey->grp, &privKey->Q)) {
|
||||
PrintAndLogEx(FAILED, "VAS protocol requires an elliptic key on the P-256 curve");
|
||||
tlvdb_free(derRoot);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
tlvdb_free(derRoot);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -427,7 +437,11 @@ static int DecryptVASCryptogram(uint8_t *pidHash, uint8_t *cryptogram, size_t cr
|
||||
|
||||
mbedtls_ecp_keypair mobilePubKey;
|
||||
mbedtls_ecp_keypair_init(&mobilePubKey);
|
||||
mobilePubKey.grp = privKey->grp;
|
||||
if (mbedtls_ecp_group_load(&mobilePubKey.grp, privKey->grp.id) != 0) {
|
||||
mbedtls_ecp_keypair_free(&mobilePubKey);
|
||||
PrintAndLogEx(FAILED, "Unable to initialize mobile ephemeral key");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (LoadMobileEphemeralKey(cryptogram + 4, &mobilePubKey) != PM3_SUCCESS) {
|
||||
mbedtls_ecp_keypair_free(&mobilePubKey);
|
||||
@@ -535,6 +549,7 @@ static int VASReader(uint8_t *pidHash, const char *url, size_t urlLen, uint8_t *
|
||||
|
||||
if (apduResLen == 0 || apduRes[0] != 0x70) {
|
||||
PrintAndLogEx(FAILED, "Invalid response from peer");
|
||||
return PM3_ECARDEXCHANGE;
|
||||
}
|
||||
|
||||
return ParseGetVASDataResponse(apduRes, apduResLen, cryptogram, cryptogramLen);
|
||||
@@ -545,13 +560,15 @@ static int CmdVASReader(const char *Cmd) {
|
||||
CLIParserInit(&ctx, "hf vas reader",
|
||||
"Read and decrypt Value Added Services (VAS) message",
|
||||
"hf vas reader --url https://example.com -> URL Only mode\n"
|
||||
"hf vas reader --pid pass.com.passkit.pksamples.nfcdemo -f vas_privkey.der -@\n"
|
||||
"hf vas reader --pid pass.com.passkit.pksamples.nfcdemo -k vas.passkit.der -@\n"
|
||||
"hf vas reader --pid pass.com.pronto.zebra-wallet-pass.demo -k vas.zebra.der -@\n"
|
||||
"hf vas reader --pid pass.com.springcard.springblue.generic -k vas.springcard.der -@\n"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0(NULL, "pid", "<str>", "PID, pass type id"),
|
||||
arg_str0("f", "file", "<fn>", "path to terminal private key file"),
|
||||
arg_str0("k", "key,file,reader-private-key,readerprivkey,rpk", "<pem|der-b64|der-hex|scalar-b64|scalar-hex|path>", "Terminal private key: PEM, DER hex, scalar hex/base64, or file path"),
|
||||
arg_str0(NULL, "url", "<str>", "a URL to provide to the mobile device"),
|
||||
arg_lit0("@", NULL, "continuous mode"),
|
||||
arg_lit0("v", "verbose", "Verbose output"),
|
||||
@@ -563,12 +580,12 @@ static int CmdVASReader(const char *Cmd) {
|
||||
char pid[512] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)pid, 512, &pidlen);
|
||||
|
||||
int keyfnlen = 0;
|
||||
char keyfn[FILE_PATH_SIZE] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)keyfn, FILE_PATH_SIZE, &keyfnlen);
|
||||
int key_input_len = 0;
|
||||
char key_input[VAS_MAX_KEY_INPUT] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)key_input, sizeof(key_input), &key_input_len);
|
||||
|
||||
if (keyfnlen == 0 && pidlen > 0) {
|
||||
PrintAndLogEx(FAILED, "Must provide path to terminal private key if a pass type id is provided");
|
||||
if (key_input_len == 0 && pidlen > 0) {
|
||||
PrintAndLogEx(FAILED, "Must provide terminal private key if a pass type id is provided");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
@@ -581,22 +598,14 @@ static int CmdVASReader(const char *Cmd) {
|
||||
bool verbose = arg_get_lit(ctx, 5);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
// santity checks
|
||||
uint8_t *key_data = NULL;
|
||||
size_t key_datalen = 0;
|
||||
if (loadFile_safe(keyfn, "", (void **)&key_data, &key_datalen) != PM3_SUCCESS) {
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
const bool has_pid = pidlen > 0;
|
||||
mbedtls_ecp_keypair privKey;
|
||||
mbedtls_ecp_keypair_init(&privKey);
|
||||
|
||||
if (LoadReaderPrivateKey(key_data, key_datalen, &privKey) != PM3_SUCCESS) {
|
||||
free(key_data);
|
||||
if (has_pid && LoadReaderPrivateKey(key_input, &privKey) != PM3_SUCCESS) {
|
||||
mbedtls_ecp_keypair_free(&privKey);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
free(key_data);
|
||||
|
||||
PrintAndLogEx(INFO, "Requesting pass type id... " _GREEN_("%s"), sprint_ascii((uint8_t *) pid, pidlen));
|
||||
|
||||
@@ -619,17 +628,20 @@ static int CmdVASReader(const char *Cmd) {
|
||||
break;
|
||||
}
|
||||
|
||||
res = VASReader((pidlen > 0) ? pidhash : NULL, url, urllen, cryptogram, &clen, verbose);
|
||||
res = VASReader(has_pid ? pidhash : NULL, url, urllen, cryptogram, &clen, verbose);
|
||||
if (res == PM3_SUCCESS) {
|
||||
|
||||
res = DecryptVASCryptogram(pidhash, cryptogram, clen, &privKey, msg, &mlen, ×tamp);
|
||||
if (res == PM3_SUCCESS) {
|
||||
PrintAndLogEx(SUCCESS, "Timestamp... " _YELLOW_("%d") " (secs since Jan 1, 2001)", timestamp);
|
||||
PrintAndLogEx(SUCCESS, "Message..... " _YELLOW_("%s"), sprint_ascii(msg, mlen));
|
||||
// extra sleep after successfull read
|
||||
if (continuous) {
|
||||
msleep(3000);
|
||||
if (has_pid) {
|
||||
res = DecryptVASCryptogram(pidhash, cryptogram, clen, &privKey, msg, &mlen, ×tamp);
|
||||
if (res == PM3_SUCCESS) {
|
||||
PrintAndLogEx(SUCCESS, "Timestamp... " _YELLOW_("%d") " (secs since Jan 1, 2001)", timestamp);
|
||||
PrintAndLogEx(SUCCESS, "Message..... " _YELLOW_("%s"), sprint_ascii(msg, mlen));
|
||||
// extra sleep after successfull read
|
||||
if (continuous) {
|
||||
msleep(3000);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PrintAndLogEx(SUCCESS, "URL-only request completed");
|
||||
}
|
||||
}
|
||||
msleep(300);
|
||||
@@ -667,13 +679,13 @@ static int CmdVASDecrypt(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf vas decrypt",
|
||||
"Decrypt a previously captured cryptogram",
|
||||
"hf vas decrypt --pid pass.com.passkit.pksamples.nfcdemo -f vas_privkey.der -d c0b77375eae416b79449347f9fe838c05cdb57dc7470b97b93b806cb348771d9bfbe29d58538c7c7d7c3d015fa205b68bfccd726058a62f7f44085ac98dbf877120fd9059f1507b956e0a6d56d0a\n"
|
||||
"hf vas decrypt --pid pass.com.passkit.pksamples.nfcdemo -k vas.passkit.der -d c0b77375eae416b79449347f9fe838c05cdb57dc7470b97b93b806cb348771d9bfbe29d58538c7c7d7c3d015fa205b68bfccd726058a62f7f44085ac98dbf877120fd9059f1507b956e0a6d56d0a\n"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0(NULL, "pid", "<str>", "PID, pass type id"),
|
||||
arg_str0("f", "file", "<fn>", "path to terminal private key file"),
|
||||
arg_str0("k", "key,file,reader-private-key,readerprivkey,rpk", "<pem|der-b64|der-hex|scalar-b64|scalar-hex|path>", "Terminal private key: PEM, DER hex, scalar hex/base64, or file path"),
|
||||
arg_str0("d", "data", "<hex>", "cryptogram to decrypt"),
|
||||
arg_param_end
|
||||
};
|
||||
@@ -683,12 +695,12 @@ static int CmdVASDecrypt(const char *Cmd) {
|
||||
char pid[512] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)pid, 512, &pidlen);
|
||||
|
||||
int keyfnlen = 0;
|
||||
char keyfn[FILE_PATH_SIZE] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)keyfn, FILE_PATH_SIZE, &keyfnlen);
|
||||
int key_input_len = 0;
|
||||
char key_input[VAS_MAX_KEY_INPUT] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)key_input, sizeof(key_input), &key_input_len);
|
||||
|
||||
if (keyfnlen == 0 && pidlen > 0) {
|
||||
PrintAndLogEx(FAILED, "Must provide path to terminal private key if a pass type id is provided");
|
||||
if (key_input_len == 0) {
|
||||
PrintAndLogEx(FAILED, "Must provide terminal private key input or file path");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
@@ -698,22 +710,13 @@ static int CmdVASDecrypt(const char *Cmd) {
|
||||
CLIGetHexWithReturn(ctx, 3, cryptogram, &clen);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
// santity checks
|
||||
uint8_t *key_data = NULL;
|
||||
size_t key_datalen = 0;
|
||||
if (loadFile_safe(keyfn, "", (void **)&key_data, &key_datalen) != PM3_SUCCESS) {
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
mbedtls_ecp_keypair privKey;
|
||||
mbedtls_ecp_keypair_init(&privKey);
|
||||
|
||||
if (LoadReaderPrivateKey(key_data, key_datalen, &privKey) != PM3_SUCCESS) {
|
||||
free(key_data);
|
||||
if (LoadReaderPrivateKey(key_input, &privKey) != PM3_SUCCESS) {
|
||||
mbedtls_ecp_keypair_free(&privKey);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
free(key_data);
|
||||
|
||||
uint8_t pidhash[32] = {0};
|
||||
sha256hash((uint8_t *) pid, pidlen, pidhash);
|
||||
|
||||
@@ -189,6 +189,8 @@ static int pcrypto_extract_priv_scalar_from_pk(const mbedtls_pk_context *pkctx,
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int pcrypto_validate_raw_scalar(const uint8_t *scalar, size_t scalar_len, mbedtls_ecp_group_id curveid);
|
||||
|
||||
static int pcrypto_parse_ec_private_blob(const uint8_t *blob, size_t blob_len,
|
||||
mbedtls_ecp_group_id curveid,
|
||||
uint8_t *out_priv, size_t out_priv_len) {
|
||||
@@ -221,6 +223,22 @@ static int pcrypto_parse_ec_private_blob(const uint8_t *blob, size_t blob_len,
|
||||
return res;
|
||||
}
|
||||
|
||||
static int pcrypto_parse_ec_private_scalar_or_blob(const uint8_t *blob, size_t blob_len,
|
||||
mbedtls_ecp_group_id curveid,
|
||||
uint8_t *out_priv, size_t out_priv_len) {
|
||||
if (blob == NULL || out_priv == NULL || blob_len == 0 || out_priv_len == 0) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (blob_len == out_priv_len &&
|
||||
pcrypto_validate_raw_scalar(blob, blob_len, curveid) == PM3_SUCCESS) {
|
||||
memcpy(out_priv, blob, out_priv_len);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
return pcrypto_parse_ec_private_blob(blob, blob_len, curveid, out_priv, out_priv_len);
|
||||
}
|
||||
|
||||
static int pcrypto_parse_ec_private_base64(const char *input,
|
||||
mbedtls_ecp_group_id curveid,
|
||||
uint8_t *out_priv, size_t out_priv_len) {
|
||||
@@ -249,7 +267,7 @@ static int pcrypto_parse_ec_private_base64(const char *input,
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
res = pcrypto_parse_ec_private_blob(decoded, decoded_len, curveid, out_priv, out_priv_len);
|
||||
res = pcrypto_parse_ec_private_scalar_or_blob(decoded, decoded_len, curveid, out_priv, out_priv_len);
|
||||
free(decoded);
|
||||
return res;
|
||||
}
|
||||
@@ -331,7 +349,7 @@ static int pcrypto_parse_ec_private_file(const char *path,
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
int res = pcrypto_parse_ec_private_blob(data, file_len, curveid, out_priv, out_priv_len);
|
||||
int res = pcrypto_parse_ec_private_scalar_or_blob(data, file_len, curveid, out_priv, out_priv_len);
|
||||
if (res == PM3_SUCCESS) {
|
||||
free(data);
|
||||
return PM3_SUCCESS;
|
||||
@@ -392,12 +410,7 @@ static int pcrypto_parse_ec_private_text(const char *input, bool allow_file_path
|
||||
decoded_len = hex_to_bytes(compact, decoded, sizeof(decoded));
|
||||
}
|
||||
if (decoded_len > 0) {
|
||||
if ((size_t)decoded_len == out_priv_len &&
|
||||
pcrypto_validate_raw_scalar(decoded, out_priv_len, curveid) == PM3_SUCCESS) {
|
||||
memcpy(out_priv, decoded, out_priv_len);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
int res = pcrypto_parse_ec_private_blob(decoded, (size_t)decoded_len, curveid, out_priv, out_priv_len);
|
||||
int res = pcrypto_parse_ec_private_scalar_or_blob(decoded, (size_t)decoded_len, curveid, out_priv, out_priv_len);
|
||||
if (res == PM3_SUCCESS) {
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -82,6 +82,9 @@ static size_t tlv_parse_len(const unsigned char **buf, size_t *len) {
|
||||
size_t ll = l & ~ TLV_LEN_LONG;
|
||||
if (ll > 5)
|
||||
return TLV_LEN_INVALID;
|
||||
// Long-form lengths must have all declared length bytes available.
|
||||
if (*len < ll)
|
||||
return TLV_LEN_INVALID;
|
||||
|
||||
l = 0;
|
||||
for (int i = 1; i <= ll; i++) {
|
||||
|
||||
+8
-6
@@ -8654,34 +8654,36 @@
|
||||
"command": "hf vas decrypt",
|
||||
"description": "Decrypt a previously captured cryptogram",
|
||||
"notes": [
|
||||
"hf vas decrypt --pid pass.com.passkit.pksamples.nfcdemo -f vas_privkey.der -d c0b77375eae416b79449347f9fe838c05cdb57dc7470b97b93b806cb348771d9bfbe29d58538c7c7d7c3d015fa205b68bfccd726058a62f7f44085ac98dbf877120fd9059f1507b956e0a6d56d0a"
|
||||
"hf vas decrypt --pid pass.com.passkit.pksamples.nfcdemo -k vas.passkit.der -d c0b77375eae416b79449347f9fe838c05cdb57dc7470b97b93b806cb348771d9bfbe29d58538c7c7d7c3d015fa205b68bfccd726058a62f7f44085ac98dbf877120fd9059f1507b956e0a6d56d0a"
|
||||
],
|
||||
"offline": true,
|
||||
"options": [
|
||||
"-h, --help This help",
|
||||
"--pid <str> PID, pass type id",
|
||||
"-f, --file <fn> path to terminal private key file",
|
||||
"-k, --key <fn> path to terminal private key file",
|
||||
"-d, --data <hex> cryptogram to decrypt"
|
||||
],
|
||||
"usage": "hf vas decrypt [-h] [--pid <str>] [-f <fn>] [-d <hex>]"
|
||||
"usage": "hf vas decrypt [-h] [--pid <str>] [-k <fn>] [-d <hex>]"
|
||||
},
|
||||
"hf vas help": {
|
||||
"command": "hf vas help",
|
||||
"description": "-------- ----------- Value Added Service ----------- help This help -------- ----------------- General ----------------- decrypt Decrypt a previously captured VAS cryptogram --------------------------------------------------------------------------------------- hf vas reader available offline: no Read and decrypt Value Added Services (VAS) message",
|
||||
"notes": [
|
||||
"hf vas reader --url https://example.com -> URL Only mode",
|
||||
"hf vas reader --pid pass.com.passkit.pksamples.nfcdemo -f vas_privkey.der -@"
|
||||
"hf vas reader --pid pass.com.passkit.pksamples.nfcdemo -k vas.passkit.der -@",
|
||||
"hf vas reader --pid pass.com.pronto.zebra-wallet-pass.demo -k vas.zebra.der -@",
|
||||
"hf vas reader --pid pass.com.springcard.springblue.generic -k vas.springcard.der -@"
|
||||
],
|
||||
"offline": true,
|
||||
"options": [
|
||||
"-h, --help This help",
|
||||
"--pid <str> PID, pass type id",
|
||||
"-f, --file <fn> path to terminal private key file",
|
||||
"-k, --key <fn> path to terminal private key file",
|
||||
"--url <str> a URL to provide to the mobile device",
|
||||
"-@ continuous mode",
|
||||
"-v, --verbose Verbose output"
|
||||
],
|
||||
"usage": "hf vas reader [-h@v] [--pid <str>] [-f <fn>] [--url <str>]"
|
||||
"usage": "hf vas reader [-h@v] [--pid <str>] [-k <fn>] [--url <str>]"
|
||||
},
|
||||
"hf waveshare help": {
|
||||
"command": "hf waveshare help",
|
||||
|
||||
Reference in New Issue
Block a user