mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-05-12 11:18:11 -07:00
add secure messaging for UL_AES. Calculating CMAC on device side
This commit is contained in:
+2
-1
@@ -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
|
||||
|
||||
@@ -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
@@ -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: {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user