proxmark3/client/src/mifare/mifarehost.c

1990 lines
70 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//-----------------------------------------------------------------------------
// Borrowed initially from https://nethemba.com/tag/darkside-attack/
// Copyright (C) mifare@nethemba.com, 2010
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// mifare commands
//-----------------------------------------------------------------------------
#include "mifarehost.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "comms.h"
#include "commonutil.h"
#include "mifare4.h"
#include "ui.h" // PrintAndLog...
#include "crapto1/crapto1.h"
#include "crc16.h"
#include "protocols.h"
#include "mfkey.h"
#include "util_posix.h" // msclock
#include "cmdparser.h" // detection of flash capabilities
#include "cmdflashmemspiffs.h" // upload to flash mem
#include "mifaredefault.h" // default keys
#include "protocol_vigik.h" // VIGIK struct
#include "crypto/libpcrypto.h"
#include "util.h" // xor
#include "mbedtls/sha1.h" // SHA1
#include "cmdhf14a.h"
#include "gen4.h"
#include "parity.h"
int mf_dark_side(uint8_t blockno, uint8_t key_type, uint64_t *key) {
uint32_t uid = 0;
uint32_t nt = 0, nr = 0, ar = 0;
uint64_t par_list = 0, ks_list = 0;
uint64_t *keylist = NULL, *last_keylist = NULL;
bool first_run = true;
// message
PrintAndLogEx(INFO, "Expected execution time is about " _YELLOW_("25") " seconds on average");
PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort");
while (true) {
clearCommandBuffer();
//TODO: Not really stopping the command in time.
//flush queue
while (kbd_enter_pressed()) {
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
PrintAndLogEx(WARNING, "Aborted via keyboard");
return PM3_EOPABORTED;
}
struct {
uint8_t first_run;
uint8_t blockno;
uint8_t key_type;
} PACKED payload;
payload.first_run = first_run;
payload.blockno = blockno;
payload.key_type = key_type;
SendCommandNG(CMD_HF_MIFARE_READER, (uint8_t *)&payload, sizeof(payload));
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Running darkside " NOLF);
// wait cycle
while (true) {
PrintAndLogEx(NORMAL, "." NOLF);
if (IsCommunicationThreadDead()) return PM3_EIO;
//TODO: Not really stopping the command in time.
if (kbd_enter_pressed()) {
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
PrintAndLogEx(WARNING, "\nAborted via keyboard");
return PM3_EOPABORTED;
}
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_READER, &resp, 2000)) {
struct p {
int32_t isOK;
uint8_t cuid[4];
uint8_t nt[4];
uint8_t par_list[8];
uint8_t ks_list[8];
uint8_t nr[4];
uint8_t ar[4];
} PACKED;
struct p *package = (struct p *)resp.data.asBytes;
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(NORMAL, "");
switch (package->isOK) {
case 2:
PrintAndLogEx(FAILED, "Card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests).");
break;
case 3:
PrintAndLogEx(FAILED, "Card is not vulnerable to Darkside attack (its random number generator is not predictable).");
break;
case 4:
PrintAndLogEx(FAILED, "Card is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown");
PrintAndLogEx(FAILED, "generating polynomial with 16 effective bits only, but shows unexpected behaviour.");
break;
case 5:
PrintAndLogEx(WARNING, "Button pressed. aborted");
break;
case 6:
*key = 0101;
return PM3_SUCCESS;
default:
PrintAndLogEx(FAILED, "Unknown error. Darkside attack failed.");
break;
}
return resp.status;
}
uid = (uint32_t)bytes_to_num(package->cuid, sizeof(package->cuid));
nt = (uint32_t)bytes_to_num(package->nt, sizeof(package->nr));
par_list = bytes_to_num(package->par_list, sizeof(package->par_list));
ks_list = bytes_to_num(package->ks_list, sizeof(package->ks_list));
nr = (uint32_t)bytes_to_num(package->nr, 4);
ar = (uint32_t)bytes_to_num(package->ar, 4);
break;
}
}
PrintAndLogEx(NORMAL, "");
if (par_list == 0 && first_run == true) {
PrintAndLogEx(SUCCESS, "Parity is all zero. Most likely this card sends NACK on every authentication.");
}
first_run = false;
uint32_t keycount = nonce2key(uid, nt, nr, ar, par_list, ks_list, &keylist);
if (keycount == 0) {
PrintAndLogEx(FAILED, "Key not found (lfsr_common_prefix list is null). Nt = %08x", nt);
PrintAndLogEx(FAILED, "This is expected to happen in 25%% of all cases.");
PrintAndLogEx(FAILED, "Trying again with a different reader nonce...");
continue;
}
// only parity zero attack
if (par_list == 0) {
qsort(keylist, keycount, sizeof(*keylist), compare_uint64);
keycount = intersection(last_keylist, keylist);
if (keycount == 0) {
free(last_keylist);
last_keylist = keylist;
PrintAndLogEx(FAILED, "No candidates found, trying again");
continue;
}
}
PrintAndLogEx(SUCCESS, "found " _YELLOW_("%u") " candidate key%s", keycount, (keycount > 1) ? "s" : "");
*key = UINT64_C(-1);
uint8_t keyBlock[PM3_CMD_DATA_SIZE];
uint32_t max_keys = KEYS_IN_BLOCK;
for (uint32_t i = 0; i < keycount; i += max_keys) {
uint8_t size = keycount - i > max_keys ? max_keys : keycount - i;
register uint8_t j;
for (j = 0; j < size; j++) {
if (par_list == 0) {
num_to_bytes(last_keylist[i * max_keys + j], MIFARE_KEY_SIZE, keyBlock + (j * MIFARE_KEY_SIZE));
} else {
num_to_bytes(keylist[i * max_keys + j], MIFARE_KEY_SIZE, keyBlock + (j * MIFARE_KEY_SIZE));
}
}
if (mf_check_keys(blockno, key_type - 0x60, false, size, keyBlock, key) == PM3_SUCCESS) {
break;
}
}
if (*key != UINT64_C(-1)) {
break;
} else {
PrintAndLogEx(FAILED, "All key candidates failed. Restarting darkside");
free(last_keylist);
last_keylist = keylist;
first_run = true;
}
}
free(last_keylist);
free(keylist);
return PM3_SUCCESS;
}
int mf_check_keys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key) {
if (key) {
*key = -1;
}
clearCommandBuffer();
uint8_t data[PM3_CMD_DATA_SIZE] = {0};
data[0] = keyType;
data[1] = blockNo;
data[2] = clear_trace;
data[3] = 0;
data[4] = keycnt;
memcpy(data + 5, keyBlock, MIFARE_KEY_SIZE * keycnt);
SendCommandNG(CMD_HF_MIFARE_CHKKEYS, data, (5 + MIFARE_KEY_SIZE * keycnt));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_CHKKEYS, &resp, 2500) == false) {
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
return resp.status;
}
struct kr {
uint8_t key[MIFARE_KEY_SIZE];
bool found;
} PACKED;
struct kr *keyresult = (struct kr *)&resp.data.asBytes;
if (keyresult->found == false) {
return PM3_ESOFT;
}
if (key) {
*key = bytes_to_num(keyresult->key, sizeof(keyresult->key));
}
return PM3_SUCCESS;
}
// Sends chunks of keys to device.
// 0 == ok all keys found
// 1 ==
// 2 == Time-out, aborting
int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy,
uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory,
bool verbose, bool quiet, uint16_t singleSectorParams) {
uint64_t t2 = msclock();
// send keychunk
clearCommandBuffer();
SendCommandOLD(CMD_HF_MIFARE_CHKKEYS_FAST, (sectorsCnt | (firstChunk << 8) | (lastChunk << 12) | (singleSectorParams << 16)), ((use_flashmemory << 8) | strategy), size, keyBlock, 6 * size);
PacketResponseNG resp;
uint32_t timeout = 0;
while (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) {
if (quiet == false) {
PrintAndLogEx((timeout) ? NORMAL : INFO, "." NOLF);
fflush(stdout);
}
timeout++;
// max timeout for one chunk of 85keys, 60*3sec = 180seconds
// s70 with 40*2 keys to check, 80*85 = 6800 auth.
// takes about 97s, still some margin before abort
if (timeout > 180) {
PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting...");
return PM3_ETIMEOUT;
}
}
t2 = msclock() - t2;
if (timeout && (quiet == false)) {
PrintAndLogEx(NORMAL, "");
}
// time to convert the returned data.
uint8_t curr_keys = resp.oldarg[0];
if ((singleSectorParams >> 15) & 1) {
if (curr_keys) {
uint64_t foo = bytes_to_num(resp.data.asBytes, 6);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, _GREEN_("Key %s for block %2i found: %012" PRIx64), (singleSectorParams >> 8) & 1 ? "B" : "A", singleSectorParams & 0xFF, foo);
return PM3_SUCCESS;
}
}
if (verbose) {
PrintAndLogEx(INFO, "Chunk %.1fs | found %u/%u keys (%u)", (float)(t2 / 1000.0), curr_keys, (sectorsCnt << 1), size);
}
// all keys?
if (curr_keys == sectorsCnt * 2 || lastChunk) {
// success array. each byte is status of key
uint8_t arr[80];
uint64_t foo = 0;
uint16_t bar = 0;
foo = bytes_to_num(resp.data.asBytes + 480, 8);
bar = (resp.data.asBytes[489] << 8 | resp.data.asBytes[488]);
for (uint8_t i = 0; i < 64; i++) {
arr[i] = (foo >> i) & 0x1;
}
for (uint8_t i = 0; i < 16; i++) {
arr[i + 64] = (bar >> i) & 0x1;
}
// initialize storage for found keys
icesector_t *tmp = calloc(sectorsCnt, sizeof(icesector_t));
if (tmp == NULL) {
return PM3_EMALLOC;
}
memcpy(tmp, resp.data.asBytes, sectorsCnt * sizeof(icesector_t));
for (int i = 0; i < sectorsCnt; i++) {
// key A
if (!e_sector[i].foundKey[0]) {
e_sector[i].Key[0] = bytes_to_num(tmp[i].keyA, MIFARE_KEY_SIZE);
e_sector[i].foundKey[0] = arr[(i * 2) ];
}
// key B
if (!e_sector[i].foundKey[1]) {
e_sector[i].Key[1] = bytes_to_num(tmp[i].keyB, MIFARE_KEY_SIZE);
e_sector[i].foundKey[1] = arr[(i * 2) + 1 ];
}
}
free(tmp);
// if all keys where found
if (curr_keys == sectorsCnt * 2) {
return PM3_SUCCESS;
}
// if some keys was found
if (curr_keys > 0) {
return PM3_EPARTIAL;
}
if (lastChunk) {
return PM3_ESOFT;
}
}
return PM3_ESOFT;
}
int mf_check_keys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy,
uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, bool verbose) {
return mf_check_keys_fast_ex(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock, e_sector, use_flashmemory, verbose, false, 0);
}
// Trigger device to use a binary file on flash mem as keylist for mfCheckKeys.
// As of now, 255 keys possible in the file
// 6 * 255 = 1500 bytes
int mf_check_keys_file(uint8_t *destfn, uint64_t *key) {
*key = -1;
clearCommandBuffer();
struct {
uint8_t filename[32];
} PACKED payload_file;
memcpy(payload_file.filename, destfn, sizeof(payload_file.filename) - 1);
PacketResponseNG resp;
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_CHKKEYS_FILE, (uint8_t *)&payload_file, sizeof(payload_file));
uint8_t retry = 10;
while (!WaitForResponseTimeout(CMD_HF_MIFARE_CHKKEYS, &resp, 2000)) {
//flush queue
while (kbd_enter_pressed()) {
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
return PM3_EOPABORTED;
}
retry--;
if (retry == 0) {
PrintAndLogEx(WARNING, "Chk keys file, command execution time out");
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
return PM3_ETIMEOUT;
}
}
if (resp.status != PM3_SUCCESS) return resp.status;
struct kr {
uint8_t key[MIFARE_KEY_SIZE];
bool found;
} PACKED;
struct kr *keyresult = (struct kr *)&resp.data.asBytes;
if (!keyresult->found) return PM3_ESOFT;
*key = bytes_to_num(keyresult->key, sizeof(keyresult->key));
return PM3_SUCCESS;
}
// PM3 imp of J-Run mf_key_brute (part 2)
// ref: https://github.com/J-Run/mf_key_brute
int mf_key_brute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *resultkey) {
uint64_t key64;
uint8_t found = false;
uint8_t candidates[CANDIDATE_SIZE] = {0x00};
uint8_t keyBlock[KEYBLOCK_SIZE] = {0x00};
memset(candidates, 0, sizeof(candidates));
memset(keyBlock, 0, sizeof(keyBlock));
// Generate all possible keys for the first two unknown bytes.
for (uint16_t i = 0; i < 0xFFFF; ++i) {
uint32_t j = i * MIFARE_KEY_SIZE;
candidates[0 + j] = i >> 8;
candidates[1 + j] = i;
candidates[2 + j] = key[2];
candidates[3 + j] = key[3];
candidates[4 + j] = key[4];
candidates[5 + j] = key[5];
}
uint32_t counter, i;
for (i = 0, counter = 1; i < CANDIDATE_SIZE; i += KEYBLOCK_SIZE, ++counter) {
key64 = 0;
// copy candidatekeys to test key block
memcpy(keyBlock, candidates + i, KEYBLOCK_SIZE);
// check a block of generated key candidates.
if (mf_check_keys(blockNo, keyType, true, KEYS_IN_BLOCK, keyBlock, &key64) == PM3_SUCCESS) {
*resultkey = key64;
found = true;
break;
}
// progress
if (counter % 20 == 0)
PrintAndLogEx(SUCCESS, "tried %s.. \t %u keys", sprint_hex(candidates + i, 6), counter * KEYS_IN_BLOCK);
}
return found;
}
// Compare 16 Bits out of cryptostate
inline static int Compare16Bits(const void *a, const void *b) {
if ((*(uint64_t *)b & 0x00ff000000ff0000) == (*(uint64_t *)a & 0x00ff000000ff0000)) return 0;
if ((*(uint64_t *)b & 0x00ff000000ff0000) > (*(uint64_t *)a & 0x00ff000000ff0000)) return 1;
return -1;
}
// wrapper function for multi-threaded lfsr_recovery32
static void
#ifdef __has_attribute
#if __has_attribute(force_align_arg_pointer)
__attribute__((force_align_arg_pointer))
#endif
#endif
*nested_worker_thread(void *arg) {
struct Crypto1State *p1;
StateList_t *statelist = arg;
statelist->head.slhead = lfsr_recovery32(statelist->ks1, statelist->nt_enc ^ statelist->uid);
for (p1 = statelist->head.slhead; p1->odd | p1->even; p1++) {};
statelist->len = p1 - statelist->head.slhead;
statelist->tail.sltail = --p1;
qsort(statelist->head.slhead, statelist->len, sizeof(uint64_t), Compare16Bits);
return statelist->head.slhead;
}
int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey, bool calibrate) {
uint32_t uid;
StateList_t statelists[2];
struct Crypto1State *p1, *p2, *p3, *p4;
struct {
uint8_t block;
uint8_t keytype;
uint8_t target_block;
uint8_t target_keytype;
bool calibrate;
uint8_t key[6];
} PACKED payload;
payload.block = blockNo;
payload.keytype = keyType;
payload.target_block = trgBlockNo;
payload.target_keytype = trgKeyType;
payload.calibrate = calibrate;
memcpy(payload.key, key, sizeof(payload.key));
PacketResponseNG resp;
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_NESTED, (uint8_t *)&payload, sizeof(payload));
if (WaitForResponseTimeout(CMD_HF_MIFARE_NESTED, &resp, 2000) == false) {
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
return PM3_ETIMEOUT;
}
struct p {
int16_t isOK;
uint8_t block;
uint8_t keytype;
uint8_t cuid[4];
uint8_t nt_a[4];
uint8_t ks_a[4];
uint8_t nt_b[4];
uint8_t ks_b[4];
} PACKED;
struct p *package = (struct p *)resp.data.asBytes;
// error during nested on device side
if (package->isOK != PM3_SUCCESS)
return package->isOK;
memcpy(&uid, package->cuid, sizeof(package->cuid));
for (uint8_t i = 0; i < 2; i++) {
statelists[i].blockNo = package->block;
statelists[i].keyType = package->keytype;
statelists[i].uid = uid;
}
memcpy(&statelists[0].nt_enc, package->nt_a, sizeof(package->nt_a));
memcpy(&statelists[0].ks1, package->ks_a, sizeof(package->ks_a));
memcpy(&statelists[1].nt_enc, package->nt_b, sizeof(package->nt_b));
memcpy(&statelists[1].ks1, package->ks_b, sizeof(package->ks_b));
// calc keys
pthread_t thread_id[2];
// create and run worker threads
for (uint8_t i = 0; i < 2; i++)
pthread_create(thread_id + i, NULL, nested_worker_thread, &statelists[i]);
// wait for threads to terminate:
for (uint8_t i = 0; i < 2; i++)
pthread_join(thread_id[i], (void *)&statelists[i].head.slhead);
// the first 16 Bits of the cryptostate already contain part of our key.
// Create the intersection of the two lists based on these 16 Bits and
// roll back the cryptostate
p1 = p3 = statelists[0].head.slhead;
p2 = p4 = statelists[1].head.slhead;
while (p1 <= statelists[0].tail.sltail && p2 <= statelists[1].tail.sltail) {
if (Compare16Bits(p1, p2) == 0) {
struct Crypto1State savestate;
savestate = *p1;
while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) {
*p3 = *p1;
lfsr_rollback_word(p3, statelists[0].nt_enc ^ statelists[0].uid, 0);
p3++;
p1++;
}
savestate = *p2;
while (Compare16Bits(p2, &savestate) == 0 && p2 <= statelists[1].tail.sltail) {
*p4 = *p2;
lfsr_rollback_word(p4, statelists[1].nt_enc ^ statelists[1].uid, 0);
p4++;
p2++;
}
} else {
while (Compare16Bits(p1, p2) == -1) p1++;
while (Compare16Bits(p1, p2) == 1) p2++;
}
}
p3->odd = -1;
p3->even = -1;
p4->odd = -1;
p4->even = -1;
statelists[0].len = p3 - statelists[0].head.slhead;
statelists[1].len = p4 - statelists[1].head.slhead;
statelists[0].tail.sltail = --p3;
statelists[1].tail.sltail = --p4;
// the statelists now contain possible keys. The key we are searching for must be in the
// intersection of both lists
qsort(statelists[0].head.keyhead, statelists[0].len, sizeof(uint64_t), compare_uint64);
qsort(statelists[1].head.keyhead, statelists[1].len, sizeof(uint64_t), compare_uint64);
// Create the intersection
statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead);
//statelists[0].tail.keytail = --p7;
uint32_t keycnt = statelists[0].len;
if (keycnt == 0) goto out;
PrintAndLogEx(SUCCESS, "Found " _YELLOW_("%u") " key candidates", keycnt);
memset(resultKey, 0, MIFARE_KEY_SIZE);
uint64_t key64 = -1;
// The list may still contain several key candidates. Test each of them with mfCheckKeys
uint32_t max_keys = keycnt > KEYS_IN_BLOCK ? KEYS_IN_BLOCK : keycnt;
uint8_t keyBlock[PM3_CMD_DATA_SIZE] = {0x00};
for (uint32_t i = 0; i < keycnt; i += max_keys) {
uint64_t start_time = msclock();
uint8_t size = keycnt - i > max_keys ? max_keys : keycnt - i;
register uint8_t j;
for (j = 0; j < size; j++) {
crypto1_get_lfsr(statelists[0].head.slhead + i, &key64);
num_to_bytes(key64, 6, keyBlock + j * MIFARE_KEY_SIZE);
}
if (mf_check_keys(statelists[0].blockNo, statelists[0].keyType, false, size, keyBlock, &key64) == PM3_SUCCESS) {
free(statelists[0].head.slhead);
free(statelists[1].head.slhead);
num_to_bytes(key64, 6, resultKey);
if (package->keytype < 2) {
PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]",
package->block,
package->keytype ? 'B' : 'A',
sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE)
);
} else {
PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %02x -- found valid key [ " _GREEN_("%s") " ]",
package->block,
MIFARE_AUTH_KEYA + package->keytype,
sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE)
);
}
return PM3_SUCCESS;
}
float bruteforce_per_second = (float)(i + max_keys) / ((msclock() - start_time) / 1000.0);
PrintAndLogEx(INPLACE, "%6d/%u keys | %5.1f keys/sec | worst case %6.1f seconds remaining", i, keycnt, bruteforce_per_second, (keycnt - i) / bruteforce_per_second);
}
out:
if (package->keytype < 2) {
PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c",
package->block,
package->keytype ? 'B' : 'A'
);
} else {
PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %02x",
package->block,
MIFARE_AUTH_KEYA + package->keytype
);
}
free(statelists[0].head.slhead);
free(statelists[1].head.slhead);
return PM3_ESOFT;
}
int mf_static_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey) {
uint32_t uid;
StateList_t statelists[2];
struct Crypto1State *p1, *p2, *p3, *p4;
struct {
uint8_t block;
uint8_t keytype;
uint8_t target_block;
uint8_t target_keytype;
uint8_t key[6];
} PACKED payload;
payload.block = blockNo;
payload.keytype = keyType;
payload.target_block = trgBlockNo;
payload.target_keytype = trgKeyType;
memcpy(payload.key, key, sizeof(payload.key));
PacketResponseNG resp;
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_STATIC_NESTED, (uint8_t *)&payload, sizeof(payload));
if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_NESTED, &resp, 2000) == false)
return PM3_ETIMEOUT;
if (resp.status != PM3_SUCCESS)
return resp.status;
struct p {
uint8_t block;
uint8_t keytype;
uint8_t cuid[4];
uint8_t nt_a[4];
uint8_t ks_a[4];
uint8_t nt_b[4];
uint8_t ks_b[4];
} PACKED;
struct p *package = (struct p *)resp.data.asBytes;
memcpy(&uid, package->cuid, sizeof(package->cuid));
for (uint8_t i = 0; i < 2; i++) {
statelists[i].blockNo = package->block;
statelists[i].keyType = package->keytype;
statelists[i].uid = uid;
}
memcpy(&statelists[0].nt_enc, package->nt_a, sizeof(package->nt_a));
memcpy(&statelists[0].ks1, package->ks_a, sizeof(package->ks_a));
memcpy(&statelists[1].nt_enc, package->nt_b, sizeof(package->nt_b));
memcpy(&statelists[1].ks1, package->ks_b, sizeof(package->ks_b));
// calc keys
pthread_t thread_id[2];
// create and run worker threads
for (uint8_t i = 0; i < 2; i++)
pthread_create(thread_id + i, NULL, nested_worker_thread, &statelists[i]);
// wait for threads to terminate:
for (uint8_t i = 0; i < 2; i++)
pthread_join(thread_id[i], (void *)&statelists[i].head.slhead);
// the first 16 Bits of the cryptostate already contain part of our key.
// Create the intersection of the two lists based on these 16 Bits and
// roll back the cryptostate
p1 = p3 = statelists[0].head.slhead;
p2 = p4 = statelists[1].head.slhead;
while (p1 <= statelists[0].tail.sltail && p2 <= statelists[1].tail.sltail) {
if (Compare16Bits(p1, p2) == 0) {
struct Crypto1State savestate;
savestate = *p1;
while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) {
*p3 = *p1;
lfsr_rollback_word(p3, statelists[0].nt_enc ^ statelists[0].uid, 0);
p3++;
p1++;
}
savestate = *p2;
while (Compare16Bits(p2, &savestate) == 0 && p2 <= statelists[1].tail.sltail) {
*p4 = *p2;
lfsr_rollback_word(p4, statelists[1].nt_enc ^ statelists[1].uid, 0);
p4++;
p2++;
}
} else {
while (Compare16Bits(p1, p2) == -1) p1++;
while (Compare16Bits(p1, p2) == 1) p2++;
}
}
p3->odd = -1;
p3->even = -1;
p4->odd = -1;
p4->even = -1;
statelists[0].len = p3 - statelists[0].head.slhead;
statelists[1].len = p4 - statelists[1].head.slhead;
statelists[0].tail.sltail = --p3;
statelists[1].tail.sltail = --p4;
// the statelists now contain possible keys. The key we are searching for must be in the
// intersection of both lists
qsort(statelists[0].head.keyhead, statelists[0].len, sizeof(uint64_t), compare_uint64);
qsort(statelists[1].head.keyhead, statelists[1].len, sizeof(uint64_t), compare_uint64);
// Create the intersection
statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead);
/*
memcpy(&uid, package->cuid, sizeof(package->cuid));
statelists[0].blockNo = package->block;
statelists[0].keyType = package->keytype;
statelists[0].uid = uid;
memcpy(&statelists[0].nt_enc, package->nt, sizeof(package->nt));
memcpy(&statelists[0].ks1, package->ks, sizeof(package->ks));
// calc keys
pthread_t t;
// create and run worker thread
pthread_create(&t, NULL, nested_worker_thread, &statelists[0]);
// wait for thread to terminate:
pthread_join(t, (void *)&statelists[0].head.slhead);
// the first 16 Bits of the cryptostate already contain part of our key.
p1 = p3 = statelists[0].head.slhead;
// create key candidates.
while (p1 <= statelists[0].tail.sltail) {
struct Crypto1State savestate;
savestate = *p1;
while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) {
*p3 = *p1;
lfsr_rollback_word(p3, statelists[0].nt_enc ^ statelists[0].uid, 0);
p3++;
p1++;
}
}
p3->odd = -1;
p3->even = -1;
statelists[0].len = p3 - statelists[0].head.slhead;
statelists[0].tail.sltail = --p3;
*/
uint32_t keycnt = statelists[0].len;
if (keycnt == 0) {
goto out;
}
PrintAndLogEx(SUCCESS, "Found " _YELLOW_("%u") " key candidates", keycnt);
memset(resultKey, 0, MIFARE_KEY_SIZE);
// The list may still contain several key candidates. Test each of them with mfCheckKeys
uint32_t maxkeysinblock = IfPm3Flash() ? 1000 : KEYS_IN_BLOCK;
uint32_t max_keys_chunk = keycnt > maxkeysinblock ? maxkeysinblock : keycnt;
uint8_t *mem = NULL;
uint8_t *p_keyblock = NULL;
// if RDV4 and more than 10 candidate keys
if (IfPm3Flash() && keycnt > 70) {
// used for mfCheckKeys_file, which needs a header
mem = calloc((maxkeysinblock * MIFARE_KEY_SIZE) + 5, sizeof(uint8_t));
if (mem == NULL) {
free(statelists[0].head.slhead);
return PM3_EMALLOC;
}
mem[0] = statelists[0].keyType;
mem[1] = statelists[0].blockNo;
mem[2] = 1;
mem[3] = ((max_keys_chunk >> 8) & 0xFF);
mem[4] = (max_keys_chunk & 0xFF);
p_keyblock = mem + 5;
} else {
// used for mfCheckKeys, which adds its own header.
mem = calloc((maxkeysinblock * MIFARE_KEY_SIZE), sizeof(uint8_t));
if (mem == NULL) {
free(statelists[0].head.slhead);
return PM3_EMALLOC;
}
p_keyblock = mem;
}
uint8_t fn[32] = "static_nested_000.bin";
uint64_t start_time = msclock();
for (uint32_t i = 0; i < keycnt; i += max_keys_chunk) {
//flush queue
while (kbd_enter_pressed()) {
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
PrintAndLogEx(NORMAL, "");
free(mem);
return PM3_EOPABORTED;
}
int res = 0;
uint64_t key64 = 0;
uint32_t chunk = keycnt - i > max_keys_chunk ? max_keys_chunk : keycnt - i;
// copy x keys to device.
for (uint32_t j = 0; j < chunk; j++) {
crypto1_get_lfsr(statelists[0].head.slhead + i + j, &key64);
num_to_bytes(key64, MIFARE_KEY_SIZE, p_keyblock + j * MIFARE_KEY_SIZE);
}
// check a block of generated key candidates.
if (IfPm3Flash() && keycnt > 70) {
mem[3] = ((chunk >> 8) & 0xFF);
mem[4] = (chunk & 0xFF);
// upload to flash.
res = flashmem_spiffs_load((char *)fn, mem, 5 + (chunk * MIFARE_KEY_SIZE));
if (res != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "\nSPIFFS upload failed");
free(mem);
return res;
}
res = mf_check_keys_file(fn, &key64);
} else {
res = mf_check_keys(statelists[0].blockNo, statelists[0].keyType, true, chunk, mem, &key64);
}
if (res == PM3_SUCCESS) {
p_keyblock = NULL;
free(statelists[0].head.slhead);
free(mem);
num_to_bytes(key64, MIFARE_KEY_SIZE, resultKey);
if (IfPm3Flash() && keycnt > 70)
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "target block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]",
package->block,
package->keytype ? 'B' : 'A',
sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE)
);
return PM3_SUCCESS;
} else if (res == PM3_ETIMEOUT || res == PM3_EOPABORTED) {
PrintAndLogEx(NORMAL, "");
free(mem);
return res;
}
float bruteforce_per_second = (float)(i + max_keys_chunk) / ((msclock() - start_time) / 1000.0);
PrintAndLogEx(INPLACE, "%6u/%u keys | %5.1f keys/sec | worst case %6.1f seconds", i, keycnt, bruteforce_per_second, (keycnt - i) / bruteforce_per_second);
}
p_keyblock = NULL;
free(mem);
out:
PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c",
package->block,
package->keytype ? 'B' : 'A'
);
free(statelists[0].head.slhead);
free(statelists[1].head.slhead);
return PM3_ESOFT;
}
// MIFARE
int mf_read_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data) {
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, (uint8_t *)key, MIFARE_KEY_SIZE);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
uint8_t isOK = resp.oldarg[0] & 0xFF;
if (isOK) {
memcpy(data, resp.data.asBytes, mfNumBlocksPerSector(sectorNo) * MFBLOCK_SIZE);
return PM3_SUCCESS;
} else {
return PM3_EUNDEF;
}
} else {
PrintAndLogEx(DEBUG, "command execution time out");
return PM3_ETIMEOUT;
}
return PM3_SUCCESS;
}
int mf_read_block(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *data) {
mf_readblock_t payload = {
.blockno = blockNo,
.keytype = keyType
};
memcpy(payload.key, key, sizeof(payload.key));
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) {
memcpy(data, resp.data.asBytes, MFBLOCK_SIZE);
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(DEBUG, "failed reading block");
return PM3_ESOFT;
}
} else {
PrintAndLogEx(DEBUG, "command execution time out");
return PM3_ETIMEOUT;
}
return PM3_SUCCESS;
}
int mf_write_block(uint8_t blockno, uint8_t keyType, const uint8_t *key, uint8_t *block) {
uint8_t data[26];
memcpy(data, key, MIFARE_KEY_SIZE);
memcpy(data + 10, block, MFBLOCK_SIZE);
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, keyType, 0, data, sizeof(data));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(FAILED, "mfWriteBlock execution time out");
return PM3_ETIMEOUT;
}
int res = PM3_SUCCESS;
if ((resp.oldarg[0] & 0xff) != 1) {
res = PM3_EFAILED;
}
return res;
}
int mf_write_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *sector){
int res;
for (int i=0;i<4; i++){
res = mf_write_block((sectorNo*4)+i, keyType, key, sector+(i*MFBLOCK_SIZE));
if (res != PM3_SUCCESS){
return (i==0)?PM3_EFAILED:PM3_EPARTIAL;
}
}
return PM3_SUCCESS;
}
// EMULATOR
int mf_eml_get_mem(uint8_t *data, int blockNum, int blocksCount) {
size_t size = blocksCount * MFBLOCK_SIZE;
if (size > PM3_CMD_DATA_SIZE) {
return PM3_ESOFT;
}
struct {
uint8_t blockno;
uint8_t blockcnt;
} PACKED payload;
payload.blockno = blockNum;
payload.blockcnt = blocksCount;
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_EML_MEMGET, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_EML_MEMGET, &resp, 1500) == 0) {
PrintAndLogEx(WARNING, "command execution time out");
return PM3_ETIMEOUT;
}
if (resp.status == PM3_SUCCESS)
memcpy(data, resp.data.asBytes, size);
return resp.status;
}
int mf_elm_set_mem(uint8_t *data, int blockNum, int blocksCount) {
return mf_eml_set_mem_xt(data, blockNum, blocksCount, MFBLOCK_SIZE);
}
int mf_eml_set_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) {
struct p {
uint8_t blockno;
uint8_t blockcnt;
uint8_t blockwidth;
uint8_t data[];
} PACKED;
size_t size = ((size_t) blocksCount) * blockBtWidth;
if (size > (PM3_CMD_DATA_SIZE - sizeof(struct p))) {
return PM3_EINVARG;
}
size_t paylen = sizeof(struct p) + size;
struct p *payload = calloc(1, paylen);
payload->blockno = blockNum;
payload->blockcnt = blocksCount;
payload->blockwidth = blockBtWidth;
memcpy(payload->data, data, size);
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_EML_MEMSET, (uint8_t *)payload, paylen);
free(payload);
return PM3_SUCCESS;
}
// "MAGIC" CARD
int mf_chinese_set_uid(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t *sak, uint8_t *old_uid, uint8_t *verifed_uid, uint8_t wipecard, uint8_t gdm) {
uint8_t params = MAGIC_SINGLE | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC);
uint8_t block0[MFBLOCK_SIZE];
memset(block0, 0x00, sizeof(block0));
int res = mf_chinese_get_block(0, block0, params);
if (res == 0) {
PrintAndLogEx(SUCCESS, "old block 0... %s", sprint_hex_inrow(block0, sizeof(block0)));
if (old_uid) {
memcpy(old_uid, block0, uidlen);
}
} else {
PrintAndLogEx(INFO, "couldn't get old data. Will write over the last bytes of block 0");
}
// fill in the new values
// UID
memcpy(block0, uid, uidlen);
// Mifare UID BCC
if (uidlen == 4) {
block0[4] = block0[0] ^ block0[1] ^ block0[2] ^ block0[3];
// mifare classic SAK(byte 5) and ATQA(byte 6 and 7, reversed)
if (sak)
block0[5] = sak[0];
if (atqa) {
block0[6] = atqa[1];
block0[7] = atqa[0];
}
} else if (uidlen == 7) {
block0[7] = block0[0] ^ block0[1] ^ block0[2] ^ block0[3] ^ block0[4] ^ block0[5] ^ block0[6];
// mifare classic SAK(byte 8) and ATQA(byte 9 and 10, reversed)
if (sak)
block0[8] = sak[0];
if (atqa) {
block0[9] = atqa[1];
block0[10] = atqa[0];
}
}
PrintAndLogEx(SUCCESS, "new block 0... %s", sprint_hex_inrow(block0, sizeof(block0)));
if (wipecard) {
params |= MAGIC_WIPE;
}
res = mf_chinese_set_block(0, block0, NULL, params);
if (res == PM3_SUCCESS) {
params = MAGIC_SINGLE | MAGIC_WUPC;
memset(block0, 0, sizeof(block0));
res = mf_chinese_get_block(0, block0, params);
if (res == 0) {
if (verifed_uid) {
memcpy(verifed_uid, block0, uidlen);
}
}
}
return res;
}
int mf_chinese_wipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak, uint8_t gdm) {
uint8_t block0[MFBLOCK_SIZE] = {0x00, 0x56, 0x78, 0xBB, 0x95, 0x08, 0x04, 0x00, 0x02, 0xB2, 0x1E, 0x24, 0x23, 0x27, 0x1E, 0x1D};
// uint8_t block0[MFBLOCK_SIZE] = {0x04, 0x03, 0x02, 0x01, 0x04, 0x08, 0x04, 0x00, 0x64, 0xB9, 0x95, 0x11, 0x4D, 0x20, 0x42, 0x09};
uint8_t blockD[MFBLOCK_SIZE] = {0x00};
// default transport ACL
uint8_t blockK[MFBLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
uint8_t params = MAGIC_SINGLE | (gdm ? MAGIC_GDM_ALT_WUPC : MAGIC_WUPC);
if (uid != NULL) {
memcpy(block0, uid, 4);
block0[4] = block0[0] ^ block0[1] ^ block0[2] ^ block0[3];
}
if (sak != NULL)
block0[5] = sak[0];
if (atqa != NULL) {
block0[6] = atqa[1];
block0[7] = atqa[0];
}
int res;
for (int blockNo = 0; blockNo < 4 * 16; blockNo++) {
for (int retry = 0; retry < 3; retry++) {
PrintAndLogEx(INPLACE, "wipe block %d", blockNo);
if (blockNo == 0) {
res = mf_chinese_set_block(blockNo, block0, NULL, params);
} else {
if (mfIsSectorTrailer(blockNo))
res = mf_chinese_set_block(blockNo, blockK, NULL, params);
else
res = mf_chinese_set_block(blockNo, blockD, NULL, params);
}
if (res == PM3_SUCCESS)
break;
PrintAndLogEx(WARNING, "retry block %d ...", blockNo);
}
if (res) {
PrintAndLogEx(ERR, "error setting block %d (%d)", blockNo, res);
return res;
}
}
DropField();
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
int mf_chinese_set_block(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params) {
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_CSETBL, params, blockNo, 0, data, MFBLOCK_SIZE);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 3500)) {
uint8_t isOK = resp.oldarg[0] & 0xff;
if (uid != NULL) {
memcpy(uid, resp.data.asBytes, 4);
}
if (!isOK) {
return PM3_EUNDEF;
}
} else {
PrintAndLogEx(WARNING, "command execution time out");
return PM3_ETIMEOUT;
}
return PM3_SUCCESS;
}
int mf_chinese_get_block(uint8_t blockNo, uint8_t *data, uint8_t params) {
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_CGETBL, params, blockNo, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
uint8_t isOK = resp.oldarg[0] & 0xff;
if (!isOK) {
return PM3_EUNDEF;
}
memcpy(data, resp.data.asBytes, MFBLOCK_SIZE);
} else {
PrintAndLogEx(WARNING, "command execution time out");
return PM3_ETIMEOUT;
}
return PM3_SUCCESS;
}
int mf_chinese_gen_3_uid(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid) {
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_GEN3UID, uidlen, 0, 0, uid, uidlen);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_GEN3UID, &resp, 3500)) {
if (resp.status == PM3_SUCCESS && oldUid) {
memcpy(oldUid, resp.data.asBytes, uidlen);
}
return resp.status;
} else {
PrintAndLogEx(WARNING, "command execution time out");
return PM3_ETIMEOUT;
}
}
int mf_chinese_gen_3_block(uint8_t *block, int blockLen, uint8_t *newBlock) {
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_GEN3BLK, blockLen, 0, 0, block, MFBLOCK_SIZE);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_GEN3BLK, &resp, 3500)) {
if (resp.status == PM3_SUCCESS && newBlock) {
memcpy(newBlock, resp.data.asBytes, MFBLOCK_SIZE);
}
return resp.status;
} else {
PrintAndLogEx(WARNING, "command execution time out");
return PM3_ETIMEOUT;
}
}
int mf_chinese_gen_3_freeze(void) {
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_GEN3FREEZ, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_GEN3FREEZ, &resp, 3500)) {
return resp.status;
} else {
PrintAndLogEx(WARNING, "command execution time out");
return PM3_ETIMEOUT;
}
}
// variables
uint32_t cuid = 0; // uid part used for crypto1.
void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted) {
if (len != 1) {
for (int i = 0; i < len; i++) {
data[i] = crypto1_byte(pcs, 0x00, isEncrypted) ^ data[i];
}
} else {
uint8_t bt = 0;
bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 0)) << 0;
bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 1)) << 1;
bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 2)) << 2;
bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 3)) << 3;
data[0] = bt;
}
}
int try_decrypt_word(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len) {
PrintAndLogEx(SUCCESS, "encrypted data... %s", sprint_hex(data, len));
uint32_t ks2 = ar_enc ^ prng_successor(nt, 64);
uint32_t ks3 = at_enc ^ prng_successor(nt, 96);
struct Crypto1State *s = lfsr_recovery64(ks2, ks3);
mf_crypto1_decrypt(s, data, len, false);
PrintAndLogEx(SUCCESS, "decrypted data... " _YELLOW_("%s"), sprint_hex(data, len));
PrintAndLogEx(NORMAL, "");
crypto1_destroy(s);
return PM3_SUCCESS;
}
/* Detect Tag Prng,
* function performs a partial AUTH, where it tries to authenticate against block0, key A, but only collects tag nonce.
* the tag nonce is check to see if it has a predictable PRNG.
* @returns
* TRUE if tag uses WEAK prng (ie Now the NACK bug also needs to be present for Darkside attack)
* FALSE is tag uses HARDEND prng (ie hardnested attack possible, with known key)
*/
int detect_classic_prng(void) {
PacketResponseNG resp, respA;
uint8_t cmd[] = {MIFARE_AUTH_KEYA, 0x00};
uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS;
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(cmd), 0, cmd, sizeof(cmd));
if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "PRNG UID: Reply timeout.");
return PM3_ETIMEOUT;
}
// if select tag failed.
if (resp.oldarg[0] == 0) {
PrintAndLogEx(ERR, "error: selecting tag failed, can't detect prng\n");
return PM3_ERFTRANS;
}
if (WaitForResponseTimeout(CMD_ACK, &respA, 2500) == false) {
PrintAndLogEx(WARNING, "PRNG data: Reply timeout.");
return PM3_ETIMEOUT;
}
// check respA
if (respA.oldarg[0] != 4) {
PrintAndLogEx(ERR, "PRNG data error: Wrong length: %"PRIu64, respA.oldarg[0]);
return PM3_ESOFT;
}
uint32_t nonce = bytes_to_num(respA.data.asBytes, respA.oldarg[0]);
return validate_prng_nonce(nonce);
}
/* Detect Mifare Classic NACK bug
returns:
0 = error during test / aborted
1 = has nack bug
2 = has not nack bug
3 = always leak nacks (clones)
*/
int detect_classic_nackbug(bool verbose) {
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_NACK_DETECT, NULL, 0);
PacketResponseNG resp;
PrintAndLogEx(INFO, "Checking for NACK bug");
if (verbose) {
PrintAndLogEx(SUCCESS, "press " _GREEN_("pm3 button") " to abort both Proxmark3 and client\n");
}
PrintAndLogEx(INFO, "." NOLF);
while (true) {
PrintAndLogEx(NORMAL, "." NOLF);
if (kbd_enter_pressed()) {
return PM3_EOPABORTED;
}
if (WaitForResponseTimeout(CMD_HF_MIFARE_NACK_DETECT, &resp, 500)) {
PrintAndLogEx(NORMAL, "");
if (resp.status == PM3_EOPABORTED) {
PrintAndLogEx(WARNING, "button pressed. Aborted.");
return PM3_EOPABORTED;
}
uint8_t ok = resp.data.asBytes[0];
uint8_t nacks = resp.data.asBytes[1];
uint16_t auths = bytes_to_num(resp.data.asBytes + 2, 2);
if (verbose) {
PrintAndLogEx(SUCCESS, "num of auth requests : %u", auths);
PrintAndLogEx(SUCCESS, "num of received NACK : %u", nacks);
}
switch (ok) {
case 96 :
case 98 : {
if (verbose)
PrintAndLogEx(FAILED, "card random number generator is not predictable.");
PrintAndLogEx(WARNING, "detection failed");
return PM3_SUCCESS;
}
case 97 : {
if (verbose) {
PrintAndLogEx(FAILED, "card random number generator seems to be based on the well-known generating polynomial");
PrintAndLogEx(FAILED, "with 16 effective bits only, but shows unexpected behavior, try again.");
}
return PM3_SUCCESS;
}
case 2 :
PrintAndLogEx(SUCCESS, "NACK test: " _GREEN_("always leak NACK"));
return PM3_SUCCESS;
case 1 :
PrintAndLogEx(SUCCESS, "NACK test: " _GREEN_("detected"));
return PM3_SUCCESS;
case 0 :
PrintAndLogEx(SUCCESS, "NACK test: " _GREEN_("no bug"));
return PM3_SUCCESS;
default :
PrintAndLogEx(ERR, "errorcode from device " _RED_("[%i]"), ok);
return PM3_EUNDEF;
}
break;
}
}
return PM3_SUCCESS;
}
/* Detect Mifare Classic Static / Fixed nonce
detects special magic cards that has a static / fixed nonce
returns:
0 = has normal nonce
1 = has static/fixed nonce
2 = cmd failed
*/
int detect_classic_static_nonce(void) {
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_STATIC_NONCE, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_NONCE, &resp, 1000)) {
if (resp.status == PM3_ESOFT)
return NONCE_FAIL;
return resp.data.asBytes[0];
}
return NONCE_FAIL;
}
/* Detect Mifare Classic static encrypted nonce
detects special magic cards that has a static / fixed nonce
returns:
0 = nonce ok
1 = has static/fixed nonce
2 = cmd failed
3 = has encrypted nonce
*/
int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, uint8_t *key, uint8_t block_no_nested, uint8_t key_type_nested, uint8_t *key_nested, uint8_t nr_nested, bool reset, bool hardreset, bool addread, bool addauth, bool incblk2, bool corruptnrar, bool corruptnrarparity, bool verbose) {
clearCommandBuffer();
uint8_t cdata[1 + 1 + MIFARE_KEY_SIZE + 1 + 1 + MIFARE_KEY_SIZE + 1 + 1 + 1 + 1 + 1 + 1 + 1] = { 0 };
cdata[0] = block_no;
cdata[1] = key_type;
memcpy(&cdata[2], key, MIFARE_KEY_SIZE);
cdata[8] = block_no_nested;
cdata[9] = key_type_nested;
memcpy(&cdata[10], key_nested, MIFARE_KEY_SIZE);
cdata[16] = nr_nested;
cdata[17] = hardreset << 1 | reset;
cdata[18] = addread;
cdata[19] = addauth;
cdata[20] = incblk2;
cdata[21] = corruptnrar;
cdata[22] = corruptnrarparity;
SendCommandNG(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, cdata, sizeof(cdata));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) {
if (resp.status == PM3_ESOFT) {
return NONCE_FAIL;
}
if (verbose && (resp.data.asBytes[0] == NONCE_STATIC_ENC)) {
uint32_t uid = resp.data.asBytes[1] << 24 |
resp.data.asBytes[2] << 16 |
resp.data.asBytes[3] << 8 |
resp.data.asBytes[4];
uint32_t nt = resp.data.asBytes[5] << 24 |
resp.data.asBytes[6] << 16 |
resp.data.asBytes[7] << 8 |
resp.data.asBytes[8];
uint32_t ntenc = resp.data.asBytes[9] << 24 |
resp.data.asBytes[10] << 16 |
resp.data.asBytes[11] << 8 |
resp.data.asBytes[12];
uint8_t ntencparenc = resp.data.asBytes[13];
// recompute nt on client, just because
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs;
pcs = &mpcs;
uint64_t ui64key = bytes_to_num(key_nested, 6);
crypto1_init(pcs, ui64key); // key_nested
uint32_t ks = crypto1_word(pcs, ntenc ^ uid, 1);
uint32_t mynt = ks ^ ntenc;
if (mynt != nt) {
PrintAndLogEx(ERR, "Client computed nT " _YELLOW_("%08x") " does not match ARM computed nT " _YELLOW_("%08x"), mynt, nt);
}
ntencparenc >>= 4;
// [...] Additionally, the bit of keystream used to encrypt the parity bits is reused to encrypt the next bit of plaintext.
// we can decrypt first 3 parity bits, not last one as it's using future keystream
uint8_t ksp = (((ks >> 16) & 1) << 3) | (((ks >> 8) & 1) << 2) | (((ks >> 0) & 1) << 1);
uint8_t ntencpar = ntencparenc ^ ksp;
if (validate_prng_nonce(nt)) {
PrintAndLogEx(INFO, "nTenc " _GREEN_("%08x") " par {" _YELLOW_("%i%i%i%i") "}=" _YELLOW_("%i%i%ix") " | ks " _GREEN_("%08x") " | nT " _GREEN_("%08x") " par " _YELLOW_("%i%i%i%i")" | lfsr16 index " _GREEN_("%i"),
ntenc,
(ntencparenc >> 3) & 1, (ntencparenc >> 2) & 1, (ntencparenc >> 1) & 1, ntencparenc & 1,
(ntencpar >> 3) & 1, (ntencpar >> 2) & 1, (ntencpar >> 1) & 1,
ks, nt,
oddparity8((nt >> 24) & 0xFF), oddparity8((nt >> 16) & 0xFF), oddparity8((nt >> 8) & 0xFF), oddparity8(nt & 0xFF),
nonce_distance(0, nt)
);
} else {
PrintAndLogEx(INFO, "nTenc " _GREEN_("%08x") " par {" _YELLOW_("%i%i%i%i") "}=" _YELLOW_("%i%i%ix") " | ks " _YELLOW_("%08x") " | nT " _YELLOW_("%08x") " par " _YELLOW_("%i%i%i%i") " | " _RED_("not lfsr16") " (wrong key)",
ntenc,
(ntencparenc >> 3) & 1, (ntencparenc >> 2) & 1, (ntencparenc >> 1) & 1, ntencparenc & 1,
(ntencpar >> 3) & 1, (ntencpar >> 2) & 1, (ntencpar >> 1) & 1,
ks, nt,
oddparity8((nt >> 24) & 0xFF), oddparity8((nt >> 16) & 0xFF), oddparity8((nt >> 8) & 0xFF), oddparity8(nt & 0xFF)
);
}
}
return resp.data.asBytes[0];
}
return NONCE_FAIL;
}
int detect_classic_static_encrypted_nonce(uint8_t block_no, uint8_t key_type, uint8_t *key) {
return detect_classic_static_encrypted_nonce_ex(block_no, key_type, key, block_no, key_type, key, 3, false, false, false, false, false, false, false, false);
}
// try to see if card responses to "Chinese magic backdoor" commands.
// returns flag
uint16_t detect_mf_magic(bool is_mfc, uint8_t key_type, uint64_t key) {
PacketResponseNG resp;
clearCommandBuffer();
uint8_t payload[1 + 1 + MIFARE_KEY_SIZE] = { is_mfc, key_type };
num_to_bytes(key, MIFARE_KEY_SIZE, payload + 2);
SendCommandNG(CMD_HF_MIFARE_CIDENT, payload, sizeof(payload));
if (WaitForResponseTimeout(CMD_HF_MIFARE_CIDENT, &resp, 1500)) {
if (resp.status != PM3_SUCCESS) {
return MAGIC_FLAG_NONE;
}
}
uint16_t isMagic = MAGIC_FLAG_NONE;
if ((resp.status == PM3_SUCCESS) && resp.length == sizeof(uint16_t)) {
isMagic = MemLeToUint2byte(resp.data.asBytes);
}
if (isMagic) {
PrintAndLogEx(NORMAL, "");
}
if ((isMagic & MAGIC_FLAG_GEN_1A) == MAGIC_FLAG_GEN_1A) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 1a"));
}
if ((isMagic & MAGIC_FLAG_GEN_1B) == MAGIC_FLAG_GEN_1B) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 1b"));
}
if ((isMagic & MAGIC_FLAG_GEN_2) == MAGIC_FLAG_GEN_2) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 2 / CUID"));
}
if ((isMagic & MAGIC_FLAG_GEN_3) == MAGIC_FLAG_GEN_3) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 3 / APDU") " ( possibly )");
}
if ((isMagic & MAGIC_FLAG_GEN_4GTU) == MAGIC_FLAG_GEN_4GTU) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 4 GTU"));
}
if ((isMagic & MAGIC_FLAG_GDM_AUTH) == MAGIC_FLAG_GDM_AUTH) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 4 GDM / USCUID") " ( Magic Auth )");
}
if ((isMagic & MAGIC_FLAG_GDM_WUP_20) == MAGIC_FLAG_GDM_WUP_20) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 4 GDM / USCUID") " ( Gen4 Magic Wakeup )");
}
if ((isMagic & MAGIC_FLAG_GDM_WUP_40) == MAGIC_FLAG_GDM_WUP_40) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 4 GDM / USCUID") " ( Gen1 Magic Wakeup )");
}
if ((isMagic & MAGIC_FLAG_GEN_UNFUSED) == MAGIC_FLAG_GEN_UNFUSED) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Write Once / FUID"));
}
if ((isMagic & MAGIC_FLAG_SUPER_GEN1) == MAGIC_FLAG_SUPER_GEN1) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Super card ( ") _CYAN_("Gen 1") _GREEN_(" )"));
}
if ((isMagic & MAGIC_FLAG_SUPER_GEN2) == MAGIC_FLAG_SUPER_GEN2) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Super card ( ") _CYAN_("Gen 2") _GREEN_(" )"));
}
if ((isMagic & MAGIC_FLAG_NTAG21X) == MAGIC_FLAG_NTAG21X) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("NTAG21x"));
}
if ((isMagic & MAGIC_FLAG_QL88) == MAGIC_FLAG_QL88) {
PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("QL88"));
}
return isMagic;
}
bool detect_mfc_ev1_signature(void) {
uint64_t key = 0;
int res = mf_check_keys(69, MF_KEY_B, false, 1, (uint8_t *)g_mifare_signature_key_b, &key);
return (res == PM3_SUCCESS);
}
int read_mfc_ev1_signature(uint8_t *signature) {
if (signature == NULL) {
return PM3_EINVARG;
}
uint8_t sign[32] = {0};
int res = mf_read_block(69, MF_KEY_B, g_mifare_signature_key_b, sign);
if (res == PM3_SUCCESS) {
res = mf_read_block(70, MF_KEY_B, g_mifare_signature_key_b, sign + 16);
if (res == PM3_SUCCESS) {
memcpy(signature, sign, sizeof(sign));
}
} else {
// try QL88
res = mf_read_block(69, MF_KEY_B, g_mifare_ql88_signature_key_b, sign);
if (res == PM3_SUCCESS) {
res = mf_read_block(70, MF_KEY_B, g_mifare_ql88_signature_key_b, sign + 16);
if (res == PM3_SUCCESS) {
memcpy(signature, sign, sizeof(sign));
}
}
}
return res;
}
int convert_mfc_2_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen) {
if (in == NULL || out == NULL)
return PM3_EINVARG;
uint8_t blockno = 0;
while (ilen) {
if (mfIsSectorTrailer(blockno) == false) {
memcpy(out, in, MFBLOCK_SIZE);
out += MFBLOCK_SIZE;
*olen += MFBLOCK_SIZE;
}
blockno++;
in += MFBLOCK_SIZE;
ilen -= MFBLOCK_SIZE;
}
return PM3_SUCCESS;
}
static const vigik_pk_t vigik_rsa_pk[] = {
{"La Poste Service Universel", 0x07AA, "AB9953CBFCCD9375B6C028ADBAB7584BED15B9CA037FADED9765996F9EA1AB983F3041C90DA3A198804FF90D5D872A96A4988F91F2243B821E01C5021E3ED4E1BA83B7CFECAB0E766D8563164DE0B2412AE4E6EA63804DF5C19C7AA78DC14F608294D732D7C8C67A88C6F84C0F2E3FAFAE34084349E11AB5953AC68729D07715"},
{"La Poste Service Universel", 0x07AA, "1577D02987C63A95B51AE149430834AEAF3F2E0F4CF8C6887AC6C8D732D79482604FC18DA77A9CC1F54D8063EAE6E42A41B2E04D1663856D760EABECCFB783BAE1D43E1E02C5011E823B24F2918F98A4962A875D0DF94F8098A1A30DC941303F98ABA19E6F996597EDAD7F03CAB915ED4B58B7BAAD28C0B67593CDFCCB5399AB"},
{"La Poste Autres Services", 0x07AB, "A6D99B8D902893B04F3F8DE56CB6BF24338FEE897C1BCE6DFD4EBD05B7B1A07FD2EB564BB4F7D35DBFE0A42966C2C137AD156E3DAB62904592BCA20C0BC7B8B1E261EF82D53F52D203843566305A49A22062DECC38C2FE3864CAD08E79219487651E2F79F1C9392B48CAFE1BFFAFF4802AE451E7A283E55A4026AD1E82DF1A15"},
{"La Poste Autres Services", 0x07AB, "151adf821ead26405ae583a2e751e42a80f4afff1bfeca482b39c9f1792f1e65879421798ed0ca6438fec238ccde6220a2495a3066358403d2523fd582ef61e2b1b8c70b0ca2bc92459062ab3d6e15ad37c1c26629a4e0bf5dd3f7b44b56ebd27fa0b1b705bd4efd6dce1b7c89ee8f3324bfb66ce58d3f4fb09328908d9bd9a6"},
{"France Telecom", 0x07AC, "C44DBCD92F9DCF42F4902A87335DBB35D2FF530CDB09814CFA1F4B95A1BD018D099BC6AB69F667B4922AE1ED826E72951AA3E0EAAA7D49A695F04F8CDAAE2D18D10D25BD529CBB05ABF070DC7C041EC35C2BA7F58CC4C349983CC6E11A5CBE828FB8ECBC26F08E1094A6B44C8953C8E1BAFD214DF3E69F430A98CCC75C03669D"},
{"France Telecom", 0x07AC, "9d66035cc7cc980a439fe6f34d21fdbae1c853894cb4a694108ef026bcecb88f82be5c1ae1c63c9849c3c48cf5a72b5cc31e047cdc70f0ab05bb9c52bd250dd1182daeda8c4ff095a6497daaeae0a31a95726e82ede12a92b467f669abc69b098d01bda1954b1ffa4c8109db0c53ffd235bb5d33872a90f442cf9d2fd9bc4dc4"},
{"EDF-GDF", 0x07AD, "B35193DBD2F88A21CDCFFF4BF84F7FC036A991A363DCB3E802407A5E5879DC2127EECFC520779E79E911394882482C87D09A88B0711CBC2973B77FFDAE40EA0001F595072708C558B484AB89D02BCBCB971FF1B80371C0BE30CB13661078078BB68EBCCA524B9DD55EBF7D47D9355AFC95511350CC1103A5DEE847868848B235"},
{"EDF-GDF", 0x07AD, "35b248888647e8dea50311cc50135195fc5a35d9477dbf5ed59d4b52cabc8eb68b0778106613cb30bec07103b8f11f97cbcb2bd089ab84b458c508270795f50100ea40aefd7fb77329bc1c71b0889ad0872c4882483911e9799e7720c5cfee2721dc79585e7a4002e8b3dc63a391a936c07f4ff84bffcfcd218af8d2db9351b3"},
{NULL, 0, NULL}
};
const char *vigik_get_service(uint16_t service_code) {
for (int i = 0; i < ARRAYLEN(vigik_rsa_pk); ++i)
if (service_code == vigik_rsa_pk[i].code)
return vigik_rsa_pk[i].desc;
//No match, return default
return vigik_rsa_pk[ARRAYLEN(vigik_rsa_pk) - 1].desc;
}
int vigik_verify(mfc_vigik_t *d) {
#define PUBLIC_VIGIK_KEYLEN 128
// iso9796
// Exponent V = 2
// n = The public modulus n is the product of the secret prime factors p and q. Its length is 1024 bits.
if (g_debugMode == DEBUG) {
PrintAndLogEx(INFO, "Raw");
print_hex_noascii_break((uint8_t *)d, sizeof(*d) - sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
PrintAndLogEx(INFO, "Raw signature");
print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
}
/*
int dl = 0;
param_gethex_to_eol("1C07D46DA3849326D24B3468BD76673F4F3C41827DC413E81E4F3C7804FAC727213059B21D047510D6432448643A92EBFC67FBEDDAB468D13D948B172F5EBC79A0E3FEFDFAF4E81FC7108E070F1E3CD0", 0, signature, PUBLIC_VIGIK_KEYLEN, &dl);
param_gethex_to_eol("1AB86FE0C17FFFFE4379D5E15A4B2FAFFEFCFA0F1F3F7FA03E7DDDF1E3C78FFFB1F0E23F7FFF51584771C5C18307FEA36CA74E60AA6B0409ACA66A9EC155F4E9112345708A2B8457E722608EE1157408", 0, signature, PUBLIC_VIGIK_KEYLEN, &dl);
signature_len = dl;
*/
uint8_t rev_sig[128];
reverse_array_copy(d->rsa_signature, sizeof(d->rsa_signature), rev_sig);
PrintAndLogEx(INFO, "Raw signature reverse");
print_hex_noascii_break(rev_sig, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
// t = 0xBC = Implicitly known
// t = 0xCC = look at byte before to determine hash function
// uint8_t T[] = {0x33, 0xCC};
// Success decrypt would mean 0x4b BB ... BB BA padding
// padding, message, hash, 8 bits or 16 bits
// signature = h( C || M1 || h(M2) )
// 1024 - 786 - 160 - 16 -1
// salt C
// message M = 96 bytes, 768 bits
// sha1 hash H = 20 bytes, 160 bits
// padding = 20 bytes, 96 bits
uint8_t i;
bool is_valid = false;
for (i = 0; i < ARRAYLEN(vigik_rsa_pk); i++) {
if (vigik_rsa_pk[i].desc == NULL) {
break;
}
mbedtls_mpi RN, E;
mbedtls_mpi_init(&RN);
// exponent 2 = even
mbedtls_mpi_init(&E);
mbedtls_mpi_add_int(&E, &E, 2);
int dl = 0;
uint8_t n[PUBLIC_VIGIK_KEYLEN];
memset(n, 0, sizeof(n));
param_gethex_to_eol(vigik_rsa_pk[i].n, 0, n, PUBLIC_VIGIK_KEYLEN, &dl);
// convert
mbedtls_mpi N, s, sqr, res;
mbedtls_mpi_init(&N);
mbedtls_mpi_init(&s);
mbedtls_mpi_init(&sqr);
mbedtls_mpi_init(&res);
mbedtls_mpi_read_binary(&N, (const unsigned char *)n, PUBLIC_VIGIK_KEYLEN);
//mbedtls_mpi_read_binary(&s, (const unsigned char*)signature, signature_len);
mbedtls_mpi_read_binary(&s, (const unsigned char *)rev_sig, sizeof(d->rsa_signature));
// check is sign < (N/2)
mbedtls_mpi n_2;
mbedtls_mpi_init(&n_2);
mbedtls_mpi_copy(&n_2, &N);
mbedtls_mpi_shift_r(&n_2, 1);
bool is_less = (mbedtls_mpi_cmp_mpi(&s, &n_2) > 0) ? false : true;
PrintAndLogEx(DEBUG, "z < (N/2) ..... %s", (is_less) ? _GREEN_("YES") : _RED_("NO"));
mbedtls_mpi_free(&n_2);
if (is_less) {
mbedtls_mpi_exp_mod(&sqr, &s, &E, &N, &RN);
} else {
continue;
}
/*
if v is even and
⎯ if J* mod 8 = 1, then f* = nJ*.
⎯ if J* mod 8 = 4, then f* = J*,
⎯ if J* mod 8 = 6, then f* = 2J*,
⎯ if J* mod 8 = 7, then f* = 2(nJ*),
*/
uint8_t b2 = mbedtls_mpi_get_bit(&sqr, 2);
uint8_t b1 = mbedtls_mpi_get_bit(&sqr, 1);
uint8_t b0 = mbedtls_mpi_get_bit(&sqr, 0);
uint8_t lsb = (b2 << 2) | (b1 << 1) | b0;
/*
//1
mbedtls_mpi_sub_mpi(&res, &N, &sqr);
mbedtls_mpi_write_file( "[=] 1... ", &res, 16, NULL );
// 4
mbedtls_mpi_copy(&res, &sqr);
mbedtls_mpi_write_file( "[=] 4... ", &res, 16, NULL );
// 6
mbedtls_mpi_mul_int(&res, &sqr, 2);
mbedtls_mpi_write_file( "[=] 6... ", &res, 16, NULL );
// 7
mbedtls_mpi foo;
mbedtls_mpi_init(&foo);
mbedtls_mpi_sub_mpi(&foo, &N, &sqr);
mbedtls_mpi_mul_int(&res, &foo, 2);
mbedtls_mpi_free(&foo);
mbedtls_mpi_write_file( "[=] 7... ", &res, 16, NULL );
*/
switch (lsb) {
case 1: {
mbedtls_mpi_sub_mpi(&res, &N, &sqr);
break;
}
case 4: {
mbedtls_mpi_copy(&res, &sqr);
break;
}
case 6: {
mbedtls_mpi_mul_int(&res, &sqr, 2);
break;
}
case 7: {
mbedtls_mpi foo2;
mbedtls_mpi_init(&foo2);
mbedtls_mpi_sub_mpi(&foo2, &N, &sqr);
mbedtls_mpi_mul_int(&res, &foo2, 2);
mbedtls_mpi_free(&foo2);
break;
}
default: {
continue;
}
}
PrintAndLogEx(DEBUG, "LSB............ " _GREEN_("%u"), lsb);
if (g_debugMode == DEBUG) {
mbedtls_mpi_write_file("[=] N.............. ", &N, 16, NULL);
mbedtls_mpi_write_file("[=] signature...... ", &s, 16, NULL);
mbedtls_mpi_write_file("[=] square mod n... ", &sqr, 16, NULL);
mbedtls_mpi_write_file("[=] n-fs........... ", &res, 16, NULL);
}
uint8_t nfs[128] = {0};
mbedtls_mpi_write_binary(&res, nfs, sizeof(nfs));
// xor 0xDC01
int count_zero = 0;
for (int x = 0; x < sizeof(nfs); x += 2) {
nfs[x] ^= 0xDC;
nfs[x + 1] ^= 0x01;
if (nfs[x] == 0x00)
count_zero++;
if (nfs[x + 1] == 0x00)
count_zero++;
}
if (count_zero > 10) {
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "Message XORED");
print_hex_noascii_break(nfs, sizeof(nfs), 32);
PrintAndLogEx(INFO, "\n");
is_valid = true;
break;
}
/*
if (bar == 0) {
typedef struct vigik_rsa_s {
uint8_t rsa[127];
uint8_t hash;
} vigik_rsa_t;
vigik_rsa_t ts;
memcpy(&ts, nfs, sizeof(ts));
if ( ts.hash == 0xCC ) {
PrintAndLogEx(INFO, "Hash byte... 0x%02X", ts.hash);
switch(ts.rsa[126]) {
case 0x11:
PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - SHA1");
break;
case 0x22:
PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - RIPEMD");
break;
case 0x33:
PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - SHA1");
break;
default:
PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - " _RED_("err"));
break;
}
} else if ( ts.hash == 0xBC) {
PrintAndLogEx(INFO, "Hash byte... 0x%02X - " _GREEN_("implict"), ts.hash);
} else {
PrintAndLogEx(INFO, "Hash byte... 0x%02x - " _RED_("err"), ts.hash);
}
PrintAndLogEx(INFO, "Message w padding");
print_hex_noascii_break(ts.rsa, sizeof(ts.rsa) - 20, 32);
}
*/
mbedtls_mpi_free(&N);
mbedtls_mpi_free(&s);
mbedtls_mpi_free(&res);
mbedtls_mpi_free(&RN);
mbedtls_mpi_free(&E);
}
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
PrintAndLogEx(INFO, "RSA: 1024bit");
if (is_valid == false || i == ARRAYLEN(vigik_rsa_pk)) {
PrintAndLogEx(INFO, "Signature:");
print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
PrintAndLogEx(SUCCESS, "Signature verification: " _RED_("failed"));
return PM3_ESOFT;
}
PrintAndLogEx(INFO, "Signature public key name: " _YELLOW_("%s"), vigik_rsa_pk[i].desc);
PrintAndLogEx(INFO, "Signature public key value:");
PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n);
PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n + 64);
PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n + 128);
PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n + 192);
PrintAndLogEx(INFO, "Signature:");
print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
PrintAndLogEx(SUCCESS, "Signature verification: " _GREEN_("successful"));
return PM3_SUCCESS;
}
int vigik_annotate(mfc_vigik_t *d) {
if (d == NULL)
return PM3_EINVARG;
PrintAndLogEx(INFO, "Manufacture......... %s", sprint_hex(d->b0, sizeof(d->b0)));
PrintAndLogEx(INFO, "MAD................. %s", sprint_hex(d->mad, sizeof(d->mad)));
PrintAndLogEx(INFO, "Counters............ %u", d->counters);
PrintAndLogEx(INFO, "rtf................. %s", sprint_hex(d->rtf, sizeof(d->rtf)));
PrintAndLogEx(INFO, "Service code........ 0x%08x / %u - " _YELLOW_("%s"), d->service_code, d->service_code, vigik_get_service(d->service_code));
PrintAndLogEx(INFO, "Info flag........... %u -", d->info_flag); // , sprint_bin(d->info_flag, 1));
PrintAndLogEx(INFO, "Key version......... %u", d->key_version);
PrintAndLogEx(INFO, "PTR Counter......... %u", d->ptr_counter);
PrintAndLogEx(INFO, "Counter num......... %u", d->counter_num);
PrintAndLogEx(INFO, "Slot access date.... %s", sprint_hex(d->slot_access_date, sizeof(d->slot_access_date)));
PrintAndLogEx(INFO, "Slot dst duration... %u", d->slot_dst_duration);
PrintAndLogEx(INFO, "Other Slots......... %s", sprint_hex(d->other_slots, sizeof(d->other_slots)));
PrintAndLogEx(INFO, "Services counter.... %u", d->services_counter);
PrintAndLogEx(INFO, "Loading date........ %s", sprint_hex(d->loading_date, sizeof(d->loading_date)));
PrintAndLogEx(INFO, "Reserved null....... %u", d->reserved_null);
PrintAndLogEx(INFO, "----------------------------------------------------------------");
PrintAndLogEx(INFO, "");
vigik_verify(d);
PrintAndLogEx(INFO, "----------------------------------------------------------------");
PrintAndLogEx(INFO, "");
return PM3_SUCCESS;
}