add secure messaging for UL_AES. Calculating CMAC on device side

This commit is contained in:
iceman1001
2025-10-17 13:31:19 +02:00
parent 88650e3311
commit 3595f42f6d
9 changed files with 567 additions and 206 deletions
+2 -1
View File
@@ -152,7 +152,8 @@ THUMBSRC = start.c \
ticks.c \
clocks.c \
hfsnoop.c \
generator.c
generator.c \
cmac_calc.c
# These are to be compiled in ARM mode
+1 -1
View File
@@ -188,7 +188,7 @@ void RunMod(void) {
for (int i = 0; i < block_count; i++) {
uint8_t dataout[16] = {0x00};
if (mifare_ultra_readblock(i, dataout)) {
if (mifare_ultra_readblock(i, dataout) != PM3_SUCCESS) {
// If there's an error reading, go back to search state
read_successful = false;
break;
+6 -11
View File
@@ -67,6 +67,7 @@
#include "sam_picopass.h"
#include "sam_seos.h"
#include "sam_mfc.h"
#include "cmac_calc.h"
#ifdef WITH_LCD
#include "LCD_disabled.h"
@@ -1832,7 +1833,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_MIFAREU_READBL: {
MifareUReadBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
MifareUReadBlock((mful_readblock_t *)packet->data.asBytes);
break;
}
case CMD_HF_MIFAREUC_AUTH: {
@@ -1840,17 +1841,11 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_MIFAREULAES_AUTH: {
struct p {
bool turn_off_field;
uint8_t keyno;
uint8_t key[16];
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
MifareUL_AES_Auth(payload->turn_off_field, payload->keyno, payload->key);
MifareUL_AES_Auth((mfulaes_keys_t *)packet->data.asBytes);
break;
}
case CMD_HF_MIFAREU_READCARD: {
MifareUReadCard(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes);
MifareUReadCard((mful_readblock_t *)packet->data.asBytes);
break;
}
case CMD_HF_MIFAREU_SETKEY: {
@@ -1888,11 +1883,11 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_MIFAREU_WRITEBL: {
MifareUWriteBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
MifareUWriteBlock((mful_writeblock_t *)packet->data.asBytes);
break;
}
case CMD_HF_MIFAREU_WRITEBL_COMPAT: {
MifareUWriteBlockCompat(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
MifareUWriteBlockCompat((mful_writeblock_t *)packet->data.asBytes);
break;
}
case CMD_HF_MIFARE_ACQ_ENCRYPTED_NONCES: {
+173
View File
@@ -0,0 +1,173 @@
//-----------------------------------------------------------------------------
// Copyright (C) Christian Herrmman, Iceman - October 2025
// 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.
//-----------------------------------------------------------------------------
// Calculate CMAC AES
//-----------------------------------------------------------------------------
#include "cmac_calc.h"
#include <string.h>
#include "commonutil.h"
#include "BigBuf.h"
#include "dbprint.h"
#include "mbedtls/aes.h"
static ulaes_key_t g_secure_session = {
.counter = 0,
.use_schann = false,
};
// init clears all
void init_secure_session(void) {
g_secure_session.counter = 0;
g_secure_session.use_schann = false;
memset(g_secure_session.cmac_sk1, 0, sizeof(g_secure_session.cmac_sk1));
memset(g_secure_session.cmac_sk2, 0, sizeof(g_secure_session.cmac_sk2));
memset(g_secure_session.sessionkey, 0, sizeof(g_secure_session.sessionkey));
}
void increase_session_counter(void) {
g_secure_session.counter++;
}
void set_session_channel(bool use_schann) {
g_secure_session.use_schann = use_schann;
}
ulaes_key_t *get_secure_session_obj(void) {
return &g_secure_session;
}
// XOR two 128-bit blocks
static void xor_128(const uint8_t *a, const uint8_t *b, uint8_t *out) {
for (int i = 0; i < 16; i++) {
out[i] = a[i] ^ b[i];
}
}
// Left shift one bit in a 128-bit block
static void left_shift_128(const uint8_t *input, uint8_t *output) {
uint8_t overflow = 0;
for (int i = 15; i >= 0; i--) {
output[i] = (input[i] << 1) | overflow;
overflow = (input[i] & 0x80) ? 1 : 0;
}
}
// Generate Subkeys K1 and K2
static void generate_subkeys(mbedtls_aes_context *ctx, uint8_t *K1, uint8_t *K2) {
uint8_t L[16] = {0};
// Step 1: L = AES-ENC(0^128)
mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, L, L);
// Step 2: K1 = L << 1 (with conditional XOR with 0x87)
left_shift_128(L, K1);
if (L[0] & 0x80) {
// If MSB is 1
K1[15] ^= 0x87;
}
// Step 3: K2 = K1 << 1 (with conditional XOR with 0x87)
left_shift_128(K1, K2);
if (K1[0] & 0x80) {
K2[15] ^= 0x87;
}
}
// Pad the last block (adds 0x80 followed by zeros)
static void padding(const uint8_t *lastb, uint8_t *pad, size_t len) {
memcpy(pad, lastb, len);
pad[len] = 0x80;
for (size_t i = len + 1; i < 16; i++) {
pad[i] = 0x00;
}
}
// CMAC implementation
void ulaes_cmac(const uint8_t *key, size_t key_len, const uint8_t *input, size_t ilen, uint8_t output[16]) {
uint8_t last[16] = {0};
uint8_t X[16] = {0};
uint8_t Y[16] = {0};
uint8_t buffer[16] = {0};
int last_block_complete = (ilen % 16 == 0 && ilen != 0);
memset(g_secure_session.cmac_sk1, 0, sizeof(g_secure_session.cmac_sk1));
memset(g_secure_session.cmac_sk2, 0, sizeof(g_secure_session.cmac_sk2));
mbedtls_aes_context ctx;
mbedtls_aes_init(&ctx);
mbedtls_aes_setkey_enc(&ctx, key, 128);
generate_subkeys(&ctx, g_secure_session.cmac_sk1, g_secure_session.cmac_sk2);
size_t n_blocks = (ilen + 15) / 16;
// prepare last block
if (n_blocks == 0) {
// if message is empty, CMAC is just MAC of padded block XOR K2
// since buffer is all zeros here.
n_blocks = 1;
buffer[0] = 0x80;
xor_128(buffer, g_secure_session.cmac_sk2, last);
} else {
const uint8_t *last_block = input + 16 * (n_blocks - 1);
if (last_block_complete) {
xor_128(last_block, g_secure_session.cmac_sk1, last);
} else {
padding(last_block, buffer, ilen % 16);
xor_128(buffer, g_secure_session.cmac_sk2, last);
}
}
// main loop
for (size_t i = 0; i < n_blocks - 1; i++) {
xor_128(X, input + 16 * i, Y);
mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, Y, X);
}
// last block
xor_128(X, last, Y);
mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, Y, output);
mbedtls_aes_free(&ctx);
}
// convert from 16 bytes to 8 bytes
void ulaes_cmac8(uint8_t *cmac, uint8_t *mac) {
uint8_t j = 0;
for (int i = 1; i < 16; i += 2) {
mac[j++] = cmac[i];
}
}
void append_cmac(uint8_t *d, size_t n) {
uint8_t mac[16] = {0};
uint8_t cmd_mac[2 + n];
cmd_mac[0] = g_secure_session.counter & 0xFF;
cmd_mac[1] = (g_secure_session.counter >> 8) & 0xFF;
memcpy(cmd_mac + 2, d, n);
print_result("cmd mac", cmd_mac, (2 + n));
ulaes_cmac(g_secure_session.sessionkey, sizeof(g_secure_session.sessionkey), cmd_mac, (2 + n), mac);
// append CMAC to end of the command we are trying to send
ulaes_cmac8(mac, d + n);
increase_session_counter();
}
+44
View File
@@ -0,0 +1,44 @@
//-----------------------------------------------------------------------------
// Copyright (C) Christian Herrmman, Iceman - October 2025
// 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.
//-----------------------------------------------------------------------------
// Calculate CMAC AES
//-----------------------------------------------------------------------------
#ifndef __CMAC_CALC_H
#define __CMAC_CALC_H
#include "common.h"
typedef struct {
bool use_schann;
uint8_t cmac_sk1[16];
uint8_t cmac_sk2[16];
uint8_t sessionkey[16];
uint16_t counter;
} ulaes_key_t;
ulaes_key_t *get_secure_session_obj(void);
void init_secure_session(void);
void increase_session_counter(void);
void set_session_channel(bool use_schann);
void ulaes_cmac(const uint8_t *key, size_t key_len, const uint8_t *input, size_t ilen, uint8_t output[16]);
void ulaes_cmac8(uint8_t *cmac, uint8_t *mac);
void append_cmac(uint8_t *d, size_t n);
#endif
+101 -122
View File
@@ -37,6 +37,7 @@
#include "usb_cdc.h" // usb_poll_validate_length
#include "spiffs.h" // spiffs
#include "appmain.h" // print_stack_usage
#include "cmac_calc.h"
#ifndef HARDNESTED_AUTHENTICATION_TIMEOUT
# define HARDNESTED_AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation)
@@ -287,7 +288,7 @@ void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes) {
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
}
void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes) {
void MifareUL_AES_Auth(mfulaes_keys_t *packet) {
LED_A_ON();
LED_B_OFF();
@@ -304,13 +305,13 @@ void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes) {
return;
};
if (mifare_ultra_aes_auth(keyno, keybytes) == 0) {
if (mifare_ultra_aes_auth(packet->keyno, packet->key, packet->use_schann) == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed");
OnErrorNG(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT);
return;
}
if (turn_off_field) {
if (packet->turn_off_field) {
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
LEDsoff();
}
@@ -320,12 +321,13 @@ void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes) {
// Arg0 = BlockNo,
// Arg1 = UsePwd bool
// datain = PWD bytes,
void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
uint8_t blockNo = arg0;
uint8_t dataout[16] = {0x00};
bool useCKey = (arg1 == 1); // UL_C
bool usePwd = (arg1 == 2); // UL_EV1/NTAG
bool useAESKey = (arg1 == 3); // UL_AES
void MifareUReadBlock(mful_readblock_t *packet) {
uint8_t blockNo = packet->block_no;
bool useCKey = (packet->keytype == 1); // UL_C
bool usePwd = (packet->keytype == 2); // UL_EV1/NTAG
bool useAESKey = (packet->keytype == 3); // UL_AES
init_secure_session();
LEDsoff();
LED_A_ON();
@@ -336,57 +338,50 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
OnError(1);
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
return;
}
// UL-C authentication
if (useCKey) {
uint8_t key[16] = {0x00};
memcpy(key, datain, sizeof(key));
if (mifare_ultra_auth(key) == 0) {
OnError(1);
if (mifare_ultra_auth(packet->key) == 0) {
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
return;
}
}
// UL-AES authentication
// UL-AES authentication, hardcode to use keyno 0
if (useAESKey) {
uint8_t key[16] = {0x00};
memcpy(key, datain, sizeof(key));
if (mifare_ultra_aes_auth(0, key) == 0) {
OnError(1);
if (mifare_ultra_aes_auth(0, packet->key, packet->use_schann) == 0) {
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
return;
}
}
// UL-EV1 / NTAG authentication
if (usePwd) {
uint8_t pwd[4] = {0x00};
memcpy(pwd, datain, 4);
uint8_t pack[4] = {0, 0, 0, 0};
if (!mifare_ul_ev1_auth(pwd, pack)) {
OnError(1);
if (mifare_ul_ev1_auth(packet->key, pack) == 0) {
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
return;
}
}
if (mifare_ultra_readblock(blockNo, dataout)) {
uint8_t dataout[16] = {0x00};
if (mifare_ultra_readblock(blockNo, dataout) != PM3_SUCCESS) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Read block error");
OnError(2);
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ECARDEXCHANGE);
return;
}
if (mifare_ultra_halt()) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error");
OnError(3);
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
return;
}
reply_mix(CMD_ACK, 1, 0, 0, dataout, 16);
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
reply_ng(CMD_HF_MIFAREU_READBL, PM3_SUCCESS, dataout, 16);
LEDsoff();
}
@@ -394,7 +389,10 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
// arg1 = Pages (number of blocks)
// arg2 = useKey
// datain = KEY bytes
void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) {
void MifareUReadCard(mful_readblock_t *packet) {
init_secure_session();
LEDsoff();
LED_A_ON();
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
@@ -405,56 +403,49 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain)
set_tracing(true);
// params
uint8_t blockNo = arg0;
uint16_t blocks = arg1;
bool useCKey = (arg2 == 1); // UL_C
bool usePwd = (arg2 == 2); // UL_EV1/NTAG
bool useAESKey = (arg2 == 3); // UL_AES
uint8_t blockNo = packet->block_no;
uint16_t blocks = packet->num_of_blocks;
bool useCKey = (packet->keytype == 1); // UL_C
bool usePwd = (packet->keytype == 2); // UL_EV1/NTAG
bool useAESKey = (packet->keytype == 3); // UL_AES
bool schann = packet->use_schann;
uint32_t countblocks = 0;
uint8_t *dataout = BigBuf_calloc(CARD_MEMORY_SIZE);
if (dataout == NULL) {
Dbprintf("Failed to allocate memory");
OnError(1);
OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_EMALLOC);
return;
}
int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true);
if (len == 0) {
if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
OnError(1);
OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ESOFT);
return;
}
// UL-C authentication
if (useCKey) {
uint8_t key[16] = {0x00};
memcpy(key, datain, sizeof(key));
if (mifare_ultra_auth(key) == 0) {
OnError(1);
if (mifare_ultra_auth(packet->key) == 0) {
OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ESOFT);
return;
}
}
// UL-AES authentication
if (useAESKey) {
uint8_t key[16] = {0x00};
memcpy(key, datain, sizeof(key));
if (mifare_ultra_aes_auth(0, key) == 0) {
OnError(1);
if (mifare_ultra_aes_auth(0, packet->key, schann) == 0) {
OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ESOFT);
return;
}
}
// UL-EV1 / NTAG authentication
if (usePwd) {
uint8_t pwd[4] = {0x00};
memcpy(pwd, datain, sizeof(pwd));
uint8_t pack[4] = {0, 0, 0, 0};
if (mifare_ul_ev1_auth(pwd, pack) == 0) {
OnError(1);
if (mifare_ul_ev1_auth(packet->key, pack) == 0) {
OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ESOFT);
return;
}
}
@@ -465,13 +456,12 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain)
break;
}
len = mifare_ultra_readblock(blockNo + i, dataout + (4 * i));
if (mifare_ultra_readblock(blockNo + i, dataout + (4 * i)) != PM3_SUCCESS) {
if (len) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Read block %d error", i);
// if no blocks read - error out
if (i == 0) {
OnError(2);
OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ECARDEXCHANGE);
return;
} else {
//stop at last successful read block and return what we got
@@ -482,10 +472,9 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain)
}
}
len = mifare_ultra_halt();
if (len) {
if (mifare_ultra_halt()) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error");
OnError(3);
OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ESOFT);
return;
}
@@ -493,8 +482,15 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain)
countblocks *= 4;
reply_mix(CMD_ACK, 1, countblocks, dataout - BigBuf_get_addr(), 0, 0);
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
mful_readblock_resp_t payload = {
.bytelen = countblocks,
.startidx = dataout - BigBuf_get_addr()
};
reply_ng(CMD_HF_MIFAREU_READCARD, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload));
LEDsoff();
BigBuf_free();
set_tracing(false);
@@ -593,101 +589,100 @@ void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) {
// 2 = use 0x1B authentication.
// datain : 4 first bytes is data to be written.
// : 4/16 next bytes is authentication key.
static void MifareUWriteBlockEx(uint8_t arg0, uint8_t arg1, uint8_t *datain, bool reply) {
uint8_t blockNo = arg0;
bool useCKey = (arg1 == 1); // UL_C
bool usePwd = (arg1 == 2); // UL_EV1/NTAG
bool useAESKey = (arg1 == 3); // UL_AES
static void MifareUWriteBlockEx(mful_writeblock_t *packet, bool reply) {
uint8_t blockNo = packet->block_no;
bool useCKey = (packet->keytype == 1); // UL_C
bool usePwd = (packet->keytype == 2); // UL_EV1/NTAG
bool useAESKey = (packet->keytype == 3); // UL_AES
uint8_t blockdata[4] = {0x00};
memcpy(blockdata, datain, 4);
memcpy(blockdata, packet->data, sizeof(blockdata));
init_secure_session();
LEDsoff();
LED_A_ON();
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
clear_trace();
set_tracing(true);
if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
OnError(0);
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
return;
};
// UL-C authentication
if (useCKey) {
uint8_t key[16] = {0x00};
memcpy(key, datain + 4, sizeof(key));
if (mifare_ultra_auth(key) == 0) {
OnError(1);
if (mifare_ultra_auth(packet->key) == 0) {
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
return;
}
}
// UL-AES authentication
if (useAESKey) {
uint8_t key[16] = {0x00};
memcpy(key, datain + 4, sizeof(key));
if (mifare_ultra_aes_auth(0, key) == 0) {
OnError(1);
if (mifare_ultra_aes_auth(0, packet->key, packet->use_schann) == 0) {
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
return;
}
}
// UL-EV1 / NTAG authentication
if (usePwd) {
uint8_t pwd[4] = {0x00};
memcpy(pwd, datain + 4, 4);
uint8_t pack[4] = {0, 0, 0, 0};
if (mifare_ul_ev1_auth(pwd, pack) == 0) {
OnError(1);
if (mifare_ul_ev1_auth(packet->key, pack) == 0) {
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
return;
}
}
if (mifare_ultra_writeblock(blockNo, blockdata) != PM3_SUCCESS) {
if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error");
OnError(0);
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ECARDEXCHANGE);
return;
};
if (mifare_ultra_halt()) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error");
OnError(0);
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
return;
};
if (g_dbglevel >= DBG_INFO) DbpString("WRITE BLOCK FINISHED");
if (reply) {
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
reply_ng(CMD_HF_MIFAREU_WRITEBL, PM3_SUCCESS, NULL, 0);
}
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
LEDsoff();
set_tracing(false);
}
void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
MifareUWriteBlockEx(arg0, arg1, datain, true);
void MifareUWriteBlock(mful_writeblock_t *packet) {
MifareUWriteBlockEx(packet, true);
}
// Arg0 : Block to write to.
// Arg1 : 0 = use no authentication.
// keytype: 0 = use no authentication.
// 1 = use 0x1A authentication.
// 2 = use 0x1B authentication.
// datain : 16 first bytes is data to be written.
// data : 16 first bytes is data to be written.
// : 4/16 next bytes is authentication key.
void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
uint8_t blockNo = arg0;
bool useCKey = (arg1 == 1); // UL_C
bool usePwd = (arg1 == 2); // UL_EV1/NTAG
bool useAESKey = (arg1 == 3); // UL_AES
uint8_t blockdata[16] = {0x00};
void MifareUWriteBlockCompat(mful_writeblock_t *packet) {
uint8_t blockNo = packet->block_no;
bool useCKey = (packet->keytype == 1); // UL_C
bool usePwd = (packet->keytype == 2); // UL_EV1/NTAG
bool useAESKey = (packet->keytype == 3); // UL_AES
memcpy(blockdata, datain, 16);
uint8_t blockdata[16] = {0x00};
memcpy(blockdata, packet->data, 16);
// UL-AES doesn't not support copmpability writes
if (useAESKey) {
reply_ng(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_EINVARG, NULL, 0);
return ;
}
LEDsoff();
LED_A_ON();
@@ -698,58 +693,42 @@ void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
OnError(0);
OnErrorNG(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_ESOFT);
return;
};
// UL-C authentication
if (useCKey) {
uint8_t key[16] = {0x00};
memcpy(key, datain + 16, sizeof(key));
if (mifare_ultra_auth(key) == 0) {
OnError(1);
return;
}
}
// UL-AES authentication
if (useAESKey) {
uint8_t key[16] = {0x00};
memcpy(key, datain + 16, sizeof(key));
if (mifare_ultra_aes_auth(0, key) == 0) {
OnError(1);
if (mifare_ultra_auth(packet->key) == 0) {
OnErrorNG(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_ESOFT);
return;
}
}
// UL-EV1 / NTAG authentication
if (usePwd) {
uint8_t pwd[4] = {0x00};
memcpy(pwd, datain + 16, 4);
uint8_t pack[4] = {0, 0, 0, 0};
if (!mifare_ul_ev1_auth(pwd, pack)) {
OnError(1);
if (mifare_ul_ev1_auth(packet->key, pack) == 0) {
OnErrorNG(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_ESOFT);
return;
}
}
if (mifare_ultra_writeblock_compat(blockNo, blockdata) != PM3_SUCCESS) {
if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error");
OnError(0);
OnErrorNG(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_ECARDEXCHANGE);
return;
};
if (mifare_ultra_halt()) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error");
OnError(0);
OnErrorNG(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_ESOFT);
return;
};
if (g_dbglevel >= DBG_INFO) DbpString("WRITE BLOCK FINISHED");
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
reply_ng(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_SUCCESS, NULL, 0);
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
LEDsoff();
set_tracing(false);
+6 -5
View File
@@ -25,13 +25,14 @@ int16_t mifare_cmd_writeblocks(MifareWakeupType wakeup, uint8_t key_auth_cmd, ui
void MifareReadSector(uint8_t sector_no, uint8_t key_type, uint8_t *key);
void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain);
void MifareUReadBlock(mful_readblock_t *packet);
void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes);
void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes);
void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain);
void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain);
void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain);
void MifareUL_AES_Auth(mfulaes_keys_t *packet);
void MifareUReadCard(mful_readblock_t *packet);
void MifareUWriteBlockCompat(mful_writeblock_t *packet);
void MifareUWriteBlock(mful_writeblock_t *packet);
void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, bool calibrate, uint8_t *key);
void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key);
+230 -65
View File
@@ -18,6 +18,8 @@
//-----------------------------------------------------------------------------
#include "mifareutil.h"
#include <stdint.h>
#include <string.h>
#include "appmain.h" // tearoff hook
#include "string.h"
#include "BigBuf.h"
@@ -29,6 +31,11 @@
#include "crc16.h"
#include "protocols.h"
#include "desfire_crypto.h"
#include "mbedtls/aes.h"
#include "cmac_calc.h"
#define AES_BLOCK_SIZE 16
// crypto1 helpers
void mf_crypto1_decryptEx(struct Crypto1State *pcs, const uint8_t *data_in, int len, uint8_t *data_out) {
@@ -92,7 +99,30 @@ uint16_t mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t *
}
uint16_t len = ReaderReceive(answer, answer_len, answer_parity);
if (len == 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("%02X Cmd failed. Card timeout.", cmd);
if (g_dbglevel >= DBG_ERROR)
Dbprintf("%02X Cmd failed. Card timeout.", cmd);
len = ReaderReceive(answer, answer_len, answer_parity);
}
return len;
}
// send X byte basic commands secure channel (UL AES)
uint16_t mifare_sendcmd_schann(uint8_t *data, uint8_t data_size, uint8_t *answer, uint16_t answer_len, uint8_t *answer_parity, uint32_t *timing) {
uint8_t dcmd[data_size + 2];
if (data_size > 0) {
memcpy(dcmd, data, data_size);
}
AddCrc14A(dcmd, data_size);
ReaderTransmit(dcmd, sizeof(dcmd), timing);
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
return 0;
}
uint16_t len = ReaderReceive(answer, answer_len, answer_parity);
if (len == 0) {
len = ReaderReceive(answer, answer_len, answer_parity);
}
return len;
@@ -435,20 +465,20 @@ int mifare_ultra_auth(uint8_t *keybytes) {
return 1;
}
int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) {
int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes, bool schann) {
/// aes-128
uint8_t random_a[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
uint8_t random_b[16] = { 0 };
uint8_t rnd_ab[32] = { 0 };
uint8_t enc_rnd_ab[32] = { 0 };
uint8_t IV[16] = { 0 };
uint8_t key[16] = { 0 };
uint8_t random_b[16] = { 0x00 };
uint8_t rnd_ab[32] = { 0x00 };
uint8_t enc_rnd_ab[32] = { 0x00 };
uint8_t IV[16] = { 0x00 };
uint8_t key[16] = { 0x00 };
memcpy(key, keybytes, sizeof(key));
// 1 cmd + 16 bytes + 2 crc
uint8_t resp[19] = {0x00};
uint8_t respPar[5] = {0};
uint8_t resp[19] = { 0x00 };
uint8_t respPar[5] = { 0x00 };
// setup AES
mbedtls_aes_context actx;
@@ -458,7 +488,9 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) {
// Send REQUEST AUTHENTICATION / receive tag nonce
uint16_t len = mifare_sendcmd_short(NULL, CRYPT_NONE, MIFARE_ULAES_AUTH_1, keyno, resp, sizeof(resp), respPar, NULL);
if (len != 19) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len);
if (g_dbglevel >= DBG_ERROR) {
Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len);
}
return 0;
}
@@ -488,71 +520,148 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) {
// send & receive
len = mifare_sendcmd(MIFARE_ULAES_AUTH_2, enc_rnd_ab, sizeof(enc_rnd_ab), resp, sizeof(resp), respPar, NULL);
if (len != 19) {
if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len);
if (g_dbglevel >= DBG_INFO) {
Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len);
}
return 0;
}
memset(IV, 0, 16);
mbedtls_aes_setkey_dec(&actx, key, 128);
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(random_b), IV, resp + 1, random_b);
uint8_t rec_rnd_a[16] = {0};
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(rec_rnd_a), IV, resp + 1, rec_rnd_a);
mbedtls_aes_free(&actx);
if (memcmp(random_b, random_a, 16) != 0) {
if (g_dbglevel >= DBG_INFO) Dbprintf("failed authentication");
if (memcmp(rec_rnd_a, random_a, 16) != 0) {
if (g_dbglevel >= DBG_INFO) {
Dbprintf("failed authentication");
}
return 0;
}
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("e_AB:");
Dbhexdump(32, enc_rnd_ab, false);
Dbprintf("A:");
Dbhexdump(16, random_a, false);
Dbprintf("B:");
Dbhexdump(16, random_b, false);
// If no secure messaging, return early.
if (schann == false) {
return 1;
}
// Session key calculation
if (g_dbglevel >= DBG_DEBUG) {
DbpString("");
}
// clear global session variable
init_secure_session();
set_session_channel(schann);
ulaes_key_t *sobj = get_secure_session_obj();
// SV = 5Ah||A5h||00h||01h||00h||80h||RndA[15..14]||RndA[13..8] XOR RndB[15..10])||RndB[9..0]||RndA[7..0]
uint8_t *ra = random_a;
uint8_t *rb = random_b;
// do we need to unroll it?
ror(rb, 16);
uint8_t session_vec[] = {
0x5a, 0xa5, // to be used for MAC
0x00, 0x01, // counter
0x00, 0x80, // 128 bit length 128
ra[0], ra[1],
(ra[2] ^ rb[0]),
(ra[3] ^ rb[1]),
(ra[4] ^ rb[2]),
(ra[5] ^ rb[3]),
(ra[6] ^ rb[4]),
(ra[7] ^ rb[5]),
rb[6], rb[7], rb[8], rb[9], rb[10], rb[11], rb[12], rb[13], rb[14], rb[15],
ra[8], ra[9], ra[10], ra[11], ra[12], ra[13], ra[14], ra[15]
};
// session key is the CMAC of session_vec using the KEY
ulaes_cmac(key, sizeof(key), session_vec, sizeof(session_vec), sobj->sessionkey);
return 1;
}
static int mifare_ultra_readblockEx(uint8_t blockNo, uint8_t *blockData) {
uint16_t len = 0;
uint8_t bt[2] = {0x00, 0x00};
uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00};
uint8_t receivedAnswerPar[MAX_PARITY_SIZE] = {0x00};
uint16_t len = 0;
uint8_t offset = 0;
ulaes_key_t *sobj = get_secure_session_obj();
if (sobj->use_schann) {
offset = 8;
uint8_t mac[16] = {0};
// counter 2b, cmd, arg, 8byte cmac, 2b crc
uint8_t cmd_mac[2 + 2 + 8] = {
sobj->counter & 0xFF,
(sobj->counter >> 8) & 0xFF,
ISO14443A_CMD_READBLOCK, blockNo
};
ulaes_cmac(sobj->sessionkey, sizeof(sobj->sessionkey), cmd_mac, 4, mac);
ulaes_cmac8(mac, cmd_mac + 4);
if (g_dbglevel >= DBG_DEBUG) {
print_result("cntr || cmd...", cmd_mac, 4);
print_result("session key...", sobj->sessionkey, 16);
print_result("session mac...", mac, 16);
print_result("calc cmac.....", cmd_mac, sizeof(cmd_mac));
print_result("session cmd data", cmd_mac + 4, 8);
}
increase_session_counter();
len = mifare_sendcmd_schann(cmd_mac + 2, sizeof(cmd_mac) - 2, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL);
} else {
len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL);
}
len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL);
if (len == 1) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
return 1;
}
if (len != 18) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: card timeout. len: %x", len);
return 2;
if (g_dbglevel >= DBG_ERROR) {
Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
}
return PM3_ECARDEXCHANGE;
}
memcpy(bt, receivedAnswer + 16, 2);
AddCrc14A(receivedAnswer, 16);
if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd CRC response error.");
return 3;
// Ev1 / Ul-C == 18 bytes response
// UL-Aes in secure messaging == 26
if (len != 18 + offset) {
if (g_dbglevel >= DBG_ERROR) {
Dbprintf("Cmd Error: card timeout. len: %x", len);
}
return PM3_ETIMEOUT;
}
uint8_t bt[2] = {0x00, 0x00};
memcpy(bt, receivedAnswer + 16 + offset, 2);
AddCrc14A(receivedAnswer, 16 + offset);
if (bt[0] != receivedAnswer[16 + offset] || bt[1] != receivedAnswer[17 + offset]) {
if (g_dbglevel >= DBG_ERROR) {
Dbprintf("Cmd CRC response error.");
}
return PM3_ECRC;
}
// we are skipping verifying the cmac, since we don't care.
// increase counter for the read response
increase_session_counter();
memcpy(blockData, receivedAnswer, 16);
return 0;
return PM3_SUCCESS;
}
int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData) {
#define MFU_MAX_RETRIES 5
uint8_t res;
int res;
for (uint8_t retries = 0; retries < MFU_MAX_RETRIES; ++retries) {
res = mifare_ultra_readblockEx(blockNo, blockData);
// break if OK, or NACK.
// break if OK, or NACK.
switch (res) {
case 0:
case 1:
case PM3_SUCCESS:
case PM3_ECARDEXCHANGE:
return res;
default:
continue;
@@ -574,8 +683,10 @@ int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint8_t blockNo, uint
// MIFARE_MAGIC_GDM_WRITEBLOCK or MIFARE_MAGIC_GDM_WRITE_CFG for certain magic tags
uint16_t len = mifare_sendcmd_short(pcs, 1, cmd, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL);
if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK
if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
if ((len != 1) || (receivedAnswer[0] != 0x0A)) {
// 0x0a - ACK
if (g_dbglevel >= DBG_INFO)
Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
return PM3_EFAILED;
}
@@ -642,8 +753,10 @@ int mifare_classic_value(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blo
// Send increment or decrement command
len = mifare_sendcmd_short(pcs, 1, command, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL);
if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK
if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
if ((len != 1) || (receivedAnswer[0] != 0x0A)) {
// 0x0a - ACK
if (g_dbglevel >= DBG_INFO)
Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
return PM3_EFAILED;
}
@@ -661,7 +774,8 @@ int mifare_classic_value(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blo
// Receive the response NO Response means OK ... i.e. NOT NACK
len = ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar);
if (len != 0) { // Something not right, len == 0 (no response is ok as its waiting for transfer
if (len != 0) {
// Something not right, len == 0 (no response is ok as its waiting for transfer
uint8_t res = 0;
res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0;
res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1;
@@ -679,21 +793,19 @@ int mifare_classic_value(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blo
int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) {
// variables
uint16_t len = 0;
uint8_t d_block[18];
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00};
uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00};
uint16_t len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL);
len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL);
if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK
if (receivedAnswer[0] != CARD_ACK) {
// 0x0a - ACK
if (g_dbglevel >= DBG_INFO) {
Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0], len);
}
return PM3_EFAILED;
}
uint8_t d_block[18];
memcpy(d_block, blockData, 16);
AddCrc14A(d_block, 16);
@@ -702,32 +814,86 @@ int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) {
// Receive the response
len = ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar);
if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK
if (receivedAnswer[0] != CARD_ACK) {
// 0x0a - ACK
if (g_dbglevel >= DBG_INFO) {
Dbprintf("Cmd Send Data Error: %02x %d", receivedAnswer[0], len);
}
return PM3_EFAILED;
}
return PM3_SUCCESS;
}
int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData) {
uint16_t len = 0;
uint8_t block[5] = {blockNo, 0x00, 0x00, 0x00, 0x00 };
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00};
uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00};
uint16_t len = 0;
// command MIFARE_CLASSIC_WRITEBLOCK
memcpy(block + 1, blockData, 4);
ulaes_key_t *sobj = get_secure_session_obj();
len = mifare_sendcmd(MIFARE_ULC_WRITE, block, sizeof(block), receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL);
if (sobj->use_schann) {
if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK
if (g_dbglevel >= DBG_INFO) {
Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0], len);
uint8_t mac[16] = {0};
/*
2b counter
1b cmd
1b blockno
4b data
8b cmac
2b crc --> not included
*/
uint8_t cmd_mac[2 + 1 + 1 + 4 + 8] = {
sobj->counter & 0xFF,
(sobj->counter >> 8) & 0xFF,
MIFARE_ULC_WRITE,
blockNo
};
memcpy(cmd_mac + 4, blockData, 4);
ulaes_cmac(sobj->sessionkey, sizeof(sobj->sessionkey), cmd_mac, 8, mac);
ulaes_cmac8(mac, cmd_mac + 8);
if (g_dbglevel >= DBG_DEBUG) {
print_result("cntr || cmd...", cmd_mac, 8);
print_result("session key...", sobj->sessionkey, 16);
print_result("session mac...", mac, 16);
print_result("calc cmac.....", cmd_mac, sizeof(cmd_mac));
print_result("session cmd data", cmd_mac + 8, 8);
}
increase_session_counter();
len = mifare_sendcmd_schann(cmd_mac + 2, sizeof(cmd_mac) - 2, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL);
// we are skipping verifying the cmac, since we don't care.
// increase counter for the write response
increase_session_counter();
if (len != 10) {
if (g_dbglevel >= DBG_INFO) {
Dbprintf("Cmd Send Error: " _RED_("%d"), len);
}
return PM3_EFAILED;
}
} else {
// command MIFARE_CLASSIC_WRITEBLOCK
uint8_t block[5] = {blockNo, 0x00, 0x00, 0x00, 0x00 };
memcpy(block + 1, blockData, 4);
len = mifare_sendcmd(MIFARE_ULC_WRITE, block, sizeof(block), receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL);
if (receivedAnswer[0] != CARD_ACK) {
if (g_dbglevel >= DBG_INFO) {
Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0], len);
}
return PM3_EFAILED;
}
return PM3_EFAILED;
}
return PM3_SUCCESS;
}
@@ -745,7 +911,6 @@ int mifare_ultra_halt(void) {
return mifare_classic_halt(NULL);
}
// Mifare Memory Structure: up to 32 Sectors with 4 blocks each (1k and 2k cards),
// plus evtl. 8 sectors with 16 blocks each (4k cards)
uint8_t NumBlocksPerSector(uint8_t sectorNo) {
+4 -1
View File
@@ -23,6 +23,7 @@
#include "common.h"
#include "crapto1/crapto1.h"
// mifare authentication
#define CRYPT_NONE 0
#define CRYPT_ALL 1
@@ -82,6 +83,8 @@
#endif
//functions
uint16_t mifare_sendcmd_schann(uint8_t *data, uint8_t data_size, uint8_t *answer, uint16_t answer_len, uint8_t *answer_parity, uint32_t *timing);
uint16_t mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t *answer, uint16_t answer_len, uint8_t *answer_parity, uint32_t *timing);
uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data,
uint8_t *answer, uint16_t answer_len, uint8_t *answer_parity, uint32_t *timing);
@@ -102,7 +105,7 @@ int mifare_classic_value(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blo
// Ultralight/NTAG...
int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack);
int mifare_ultra_auth(uint8_t *keybytes);
int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes);
int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes, bool schann);
int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData);
int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData);
int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData);