Merge pull request #231 from slaenger/master

Mifare Ultralight C Implementation
This commit is contained in:
fptrs
2019-10-01 15:04:30 +02:00
committed by GitHub
8 changed files with 958 additions and 8 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,101 @@
/*
* CryptoDES.h
*
* Created on: 18.10.2016
* Author: dev_zzo
*/
#ifndef CRYPTODES_H_
#define CRYPTODES_H_
#include <stdint.h>
#include "../Common.h"
/* Notes on cryptography in DESFire cards
The EV0 was the first chip in the DESFire series. It makes use of the TDEA
encryption to secure the comms. It also employs CBC to make things more secure.
CBC is used in two "modes": "send mode" and "receive mode".
- Sending data uses the same structure as conventional encryption with CBC
(XOR with the IV then apply block cipher)
- Receiving data uses the same structure as conventional decryption with CBC
(apply block cipher then XOR with the IV)
Both operations employ TDEA encryption on the PICC's side and decryption on
PCD's side.
*/
/* Key sizes, in bytes */
#define CRYPTO_DES_KEY_SIZE 8 /* Bytes */
#define CRYPTO_2KTDEA_KEY_SIZE (CRYPTO_DES_KEY_SIZE * 2)
#define CRYPTO_3KTDEA_KEY_SIZE (CRYPTO_DES_KEY_SIZE * 3)
#define CRYPTO_DES_BLOCK_SIZE 8 /* Bytes */
/* Prototype the CBC function pointer in case anyone needs it */
typedef void (*CryptoTDEACBCFuncType)(uint16_t Count, const void* Plaintext, void* Ciphertext, void *IV, const uint8_t* Keys);
/** Performs the Triple DEA enciphering in ECB mode (single block)
*
* \param Plaintext Source buffer with plaintext
* \param Ciphertext Destination buffer to contain ciphertext
* \param Keys Key block pointer (CRYPTO_2KTDEA_KEY_SIZE)
*/
void CryptoEncrypt2KTDEA(const void* Plaintext, void* Ciphertext, const uint8_t* Keys);
void CryptoDecrypt2KTDEA(const void* Plaintext, void* Ciphertext, const uint8_t* Keys);
/** Performs the 2-key Triple DES en/deciphering in the CBC "send" mode (xor-then-crypt)
*
* \param Count Block count, expected to be >= 1
* \param Plaintext Source buffer with plaintext
* \param Ciphertext Destination buffer to contain ciphertext
* \param IV Initialization vector buffer, will be updated
* \param Keys Key block pointer (CRYPTO_2KTDEA_KEY_SIZE)
*/
void CryptoEncrypt2KTDEA_CBCSend(uint16_t Count, const void* Input, void* Output, void *IV, const uint8_t* Keys);
void CryptoDecrypt2KTDEA_CBCSend(uint16_t Count, const void* Input, void* Output, void *IV, const uint8_t* Keys);
/** Performs the 2-key Triple DES en/deciphering in the CBC "receive" mode (crypt-then-xor)
*
* \param Count Block count, expected to be >= 1
* \param Plaintext Source buffer with plaintext
* \param Ciphertext Destination buffer to contain ciphertext
* \param IV Initialization vector buffer, will be updated
* \param Keys Key block pointer (CRYPTO_2KTDEA_KEY_SIZE)
*/
void CryptoEncrypt2KTDEA_CBCReceive(uint16_t Count, const void* Input, void* Output, void *IV, const uint8_t* Keys);
void CryptoDecrypt2KTDEA_CBCReceive(uint16_t Count, const void* Input, void* Output, void *IV, const uint8_t* Keys);
/** Performs the 3-key Triple DES en/deciphering in the CBC "send" or "receive" mode
*
* \param Count Block count, expected to be >= 1
* \param Plaintext Source buffer with plaintext
* \param Ciphertext Destination buffer to contain ciphertext
* \param IV Initialization vector buffer, will be updated
* \param Keys Key block pointer (CRYPTO_3KTDEA_KEY_SIZE)
*/
void CryptoEncrypt3KTDEA_CBCSend(uint16_t Count, const void* Plaintext, void* Ciphertext, void *IV, const uint8_t* Keys);
void CryptoDecrypt3KTDEA_CBCReceive(uint16_t Count, const void* Plaintext, void* Ciphertext, void *IV, const uint8_t* Keys);
/** Applies padding to the data within the buffer
*
* \param Buffer Data buffer to pad
* \param BytesInBuffer How much data there is in the buffer already
* \param FirstPaddingBitSet Whether the very first bit (MSB) will be set or not
*/
INLINE void CryptoPaddingTDEA(uint8_t* Buffer, uint8_t BytesInBuffer, bool FirstPaddingBitSet)
{
uint8_t PaddingByte = FirstPaddingBitSet << 7;
uint8_t i;
for (i = BytesInBuffer; i < CRYPTO_DES_BLOCK_SIZE; ++i) {
Buffer[i] = PaddingByte;
PaddingByte = 0x00;
}
}
#endif /* CRYPTODES_H_ */
@@ -9,6 +9,8 @@
#include "ISO14443-3A.h"
#include "../Codec/ISO14443-2A.h"
#include "../Memory.h"
#include "../Random.h"
#include "CryptoTDEA.h"
#define ATQA_VALUE 0x0044
@@ -29,6 +31,10 @@
/* ISO commands */
#define CMD_HALT 0x50
/* ULC commands */
#define CMD_ULC_AUTH 0x1A
#define CMD_ULC_AUTH_2 0xAF
#define CMD_ULC_AUTH_FINISHED 0x00
/* EV0 commands */
#define CMD_READ 0x30
#define CMD_READ_FRAME_SIZE 2 /* without CRC bytes */
@@ -47,6 +53,9 @@
#define CMD_VCSL 0x4B
/* Tag memory layout; addresses and sizes in bytes */
#define MF_ULC_COUNTER_ADDRESS 0x29
#define MF_ULC_READ_MAX_PAGE 0x2C
#define UID_CL1_ADDRESS 0x00
#define UID_CL1_SIZE 3
#define UID_BCC1_ADDRESS 0x03
@@ -82,6 +91,7 @@
static enum {
UL_EV0,
UL_C,
UL_EV1,
} Flavor;
@@ -91,7 +101,8 @@ static enum {
STATE_READY1,
STATE_READY2,
STATE_ACTIVE,
STATE_COMPAT_WRITE
STATE_COMPAT_WRITE,
STATE_AUTH
} State;
static bool FromHalt = false;
@@ -101,6 +112,44 @@ static uint8_t CompatWritePageAddress;
static bool Authenticated;
static uint8_t FirstAuthenticatedPage;
static bool ReadAccessProtected;
static uint8_t RNDBBuff [8];
static uint8_t InitialVector[8] = {0};
static uint8_t TripleDesKey [16];
static void leftshift1byte ( uint8_t* Input)
{
uint8_t tmpstorage;
tmpstorage =Input[0];
memmove(Input,&Input[1],7);
Input[7] = tmpstorage;
}
/* Keys are stored in little Endian Order but we need them in Big */
static void rotateKey ( uint8_t* Key)
{
uint8_t tmpstorage [8] ;
tmpstorage [0]=Key[7];
tmpstorage [1]=Key[6];
tmpstorage [2]=Key[5];
tmpstorage [3]=Key[4];
tmpstorage [4]=Key[3];
tmpstorage [5]=Key[2];
tmpstorage [6]=Key[1];
tmpstorage [7]=Key[0];
memcpy (Key, tmpstorage, 8);
}
static bool MifareUltralightcCheckRNDB (const uint8_t* InMessage)
{
/*Checks if RNDB of Card is equal to decrypted RNDB' send by reader shifted left one Byte */
return (RNDBBuff [0]==InMessage [7]&&
RNDBBuff [1]==InMessage [0]&&
RNDBBuff [2]==InMessage [1]&&
RNDBBuff [3]==InMessage [2]&&
RNDBBuff [4]==InMessage [3]&&
RNDBBuff [5]==InMessage [4]&&
RNDBBuff [6]==InMessage [5]&&
RNDBBuff [7]==InMessage [6]);
}
static void AppInitCommon(void)
{
@@ -109,6 +158,30 @@ static void AppInitCommon(void)
Authenticated = false;
ArmedForCompatWrite = false;
}
void MifareUltralightCAppInit (void)
{
Flavor = UL_C;
uint8_t AuthentificationAddress = 0x2A * MIFARE_ULTRALIGHTC_PAGE_SIZE;
uint8_t ReadAccessAddress = 0x2b * MIFARE_ULTRALIGHTC_PAGE_SIZE;
uint8_t KeyAddress = 0x2c * MIFARE_ULTRALIGHTC_PAGE_SIZE;
uint8_t Access;
/*Get and rotate Keys*/
MemoryReadBlock (TripleDesKey, KeyAddress, CRYPTO_2KTDEA_KEY_SIZE );
rotateKey(TripleDesKey);
rotateKey(&TripleDesKey[8]);
PageCount = MIFARE_ULTRALIGHTC_PAGES;
MemoryReadBlock(&FirstAuthenticatedPage,AuthentificationAddress, 1 );
MemoryReadBlock(&Access, ReadAccessAddress, 1);
ReadAccessProtected = (Access == 0x00);
State = STATE_IDLE;
FromHalt = false;
Authenticated = false;
ArmedForCompatWrite = false;
}
void MifareUltralightAppInit(void)
{
@@ -152,7 +225,11 @@ void MifareUltralightAppReset(void)
{
State = STATE_IDLE;
}
void MifareUltralightCAppReset(void)
{
Authenticated = false;
State = STATE_IDLE;
}
void MifareUltralightAppTask(void)
{
@@ -161,7 +238,7 @@ void MifareUltralightAppTask(void)
static bool VerifyAuthentication(uint8_t PageAddress)
{
/* No authentication for EV0 cards; always pass */
if (Flavor < UL_EV1) {
if (Flavor < UL_C) {
return true;
}
/* If authenticated, no verification needed */
@@ -172,6 +249,26 @@ static bool VerifyAuthentication(uint8_t PageAddress)
return PageAddress < FirstAuthenticatedPage;
}
static bool IncrementCounter (uint8_t* IncrementValue)
{
uint16_t CounterValue;
MemoryReadBlock(&CounterValue,MF_ULC_COUNTER_ADDRESS, 2 );
if(CounterValue==0){
CounterValue =IncrementValue[0]+(IncrementValue[1]<<8);
MemoryWriteBlock(&CounterValue, MF_ULC_COUNTER_ADDRESS, 2);
return true;
}else{
IncrementValue[0] &= 0x0f;
if (IncrementValue[0] <= (0xffff - CounterValue)){
CounterValue += IncrementValue[0];
MemoryWriteBlock(&CounterValue, MF_ULC_COUNTER_ADDRESS, 2);
return true;
}
return false;
}
}
static bool AuthCounterIncrement(void)
{
/* Currently not implemented */
@@ -203,6 +300,18 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount)
/* Handle the compatibility write command */
if (ArmedForCompatWrite) {
ArmedForCompatWrite = false;
//Handle MF ULC counter
if (CompatWritePageAddress == MF_ULC_COUNTER_ADDRESS && Flavor == UL_C) {
if (IncrementCounter(&Buffer[2])) {
Buffer[0] = ACK_VALUE;
return ACK_FRAME_SIZE;
} else {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
}
AppWritePage(CompatWritePageAddress, &Buffer[2]);
Buffer[0] = ACK_VALUE;
return ACK_FRAME_SIZE;
@@ -216,10 +325,11 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount)
uint8_t PageLimit;
uint8_t Offset;
/* For EV1+ cards, ensure the wraparound is at the first protected page */
if (Flavor >= UL_EV1 && ReadAccessProtected && !Authenticated) {
if (Flavor >= UL_C && ReadAccessProtected && !Authenticated) {
PageLimit = FirstAuthenticatedPage;
} else {
PageLimit = PageCount;
if(Flavor == UL_C) PageLimit = MF_ULC_READ_MAX_PAGE; // For ULC make sure wraparound is at the first key page
else PageLimit = PageCount;
}
/* Validation */
if (PageAddress >= PageLimit) {
@@ -242,6 +352,18 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount)
/* This is a write command containing 4 bytes of data that
* should be written to the given page address. */
uint8_t PageAddress = Buffer[1];
//Handle MF ULC counter
if (PageAddress == MF_ULC_COUNTER_ADDRESS && Flavor == UL_C) {
if (IncrementCounter(&Buffer[2])) {
Buffer[0] = ACK_VALUE;
return ACK_FRAME_SIZE;
} else {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
}
/* Validation */
if ((PageAddress < PAGE_WRITE_MIN) || (PageAddress >= PageCount)) {
Buffer[0] = NAK_INVALID_ARG;
@@ -291,6 +413,19 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount)
default:
break;
}
if(Flavor == UL_C){
if (Cmd == CMD_ULC_AUTH) {
State = STATE_AUTH;
RandomGetBuffer(RNDBBuff,8); // Get Random Number
memset(InitialVector,0,8); // initialize InitialVector
CryptoEncrypt2KTDEA_CBCSend(1,RNDBBuff,&Buffer[1],InitialVector,TripleDesKey);// Crypt
Buffer [0] = CMD_ULC_AUTH_2 ;
ISO14443AAppendCRCA(Buffer, 9);
return (9 + ISO14443A_CRCA_SIZE) * 8;
}
}
/* Handle EV1 commands */
if (Flavor >= UL_EV1) {
switch (Cmd) {
@@ -505,6 +640,34 @@ uint16_t MifareUltralightAppProcess(uint8_t* Buffer, uint16_t BitCount)
}
return AppProcess(Buffer, ByteCount);
case STATE_AUTH: // ULC Authing
ByteCount = (BitCount + 7) >> 3;
/* We check if we received an auth message */
if (Buffer[0] == CMD_ULC_AUTH_2 && ISO14443ACheckCRCA(Buffer,ByteCount-2) )
{
uint8_t tmpBuff [8];
uint8_t RNDA [8] = {0};
CryptoDecrypt2KTDEA_CBCReceive(1,&Buffer[1],RNDA,InitialVector,TripleDesKey);
CryptoDecrypt2KTDEA_CBCReceive(1,&Buffer[9],tmpBuff,InitialVector,TripleDesKey);
if(MifareUltralightcCheckRNDB (tmpBuff))
{
leftshift1byte(RNDA);
CryptoEncrypt2KTDEA_CBCSend(1,RNDA,&Buffer[1],InitialVector,TripleDesKey);
Buffer[0] = CMD_ULC_AUTH_FINISHED;
ISO14443AAppendCRCA(Buffer,9);
Authenticated = true;
State = STATE_ACTIVE;
return (9 + ISO14443A_CRCA_SIZE) * 8;
}
}
State = STATE_IDLE;
Buffer[0] = NAK_AUTH_FAILED;
return NAK_FRAME_SIZE;
default:
/* Unknown state? Should never happen. */
break;
@@ -11,6 +11,11 @@
#include "Application.h"
#include "ISO14443-3A.h"
#define MIFARE_ULTRALIGHTC_UID_SIZE ISO14443A_UID_SIZE_DOUBLE
#define MIFARE_ULTRALIGHTC_PAGE_SIZE 4
#define MIFARE_ULTRALIGHTC_PAGES 48
#define MIFARE_ULTRALIGHTC_MEM_SIZE (MIFARE_ULTRALIGHTC_PAGES * MIFARE_ULTRALIGHTC_PAGE_SIZE)
#define MIFARE_ULTRALIGHT_UID_SIZE ISO14443A_UID_SIZE_DOUBLE
#define MIFARE_ULTRALIGHT_PAGE_SIZE 4
#define MIFARE_ULTRALIGHT_PAGES 16
@@ -26,6 +31,9 @@ void MifareUltralightEV12AppInit(void);
void MifareUltralightAppReset(void);
void MifareUltralightAppTask(void);
void MifareUltralightCAppInit(void);
void MifareUltralightCAppReset(void);
uint16_t MifareUltralightAppProcess(uint8_t* Buffer, uint16_t BitCount);
void MifareUltralightGetUid(ConfigurationUidType Uid);
+17
View File
@@ -18,6 +18,7 @@ static const MapEntryType PROGMEM ConfigurationMap[] = {
{ .Id = CONFIG_MF_ULTRALIGHT, .Text = "MF_ULTRALIGHT" },
{ .Id = CONFIG_MF_ULTRALIGHT_EV1_80B, .Text = "MF_ULTRALIGHT_EV1_80B" },
{ .Id = CONFIG_MF_ULTRALIGHT_EV1_164B, .Text = "MF_ULTRALIGHT_EV1_164B" },
{.Id = CONFIG_MF_ULTRALIGHT_C, .Text = "MF_ULTRALIGHT_C"},
#endif
#ifdef CONFIG_MF_CLASSIC_MINI_4B_SUPPORT
{ .Id = CONFIG_MF_CLASSIC_MINI_4B, .Text = "MF_CLASSIC_MINI_4B" },
@@ -107,6 +108,22 @@ static const PROGMEM ConfigurationType ConfigurationTable[] = {
.ReadOnly = false,
.TagFamily = TAG_FAMILY_ISO14443A
},
[CONFIG_MF_ULTRALIGHT_C] ={
.CodecInitFunc = ISO14443ACodecInit,
.CodecDeInitFunc = ISO14443ACodecDeInit,
.CodecTaskFunc = ISO14443ACodecTask,
.ApplicationInitFunc = MifareUltralightCAppInit,
.ApplicationResetFunc = MifareUltralightCAppReset,
.ApplicationTaskFunc = MifareUltralightAppTask,
.ApplicationTickFunc = ApplicationTickDummy,
.ApplicationProcessFunc = MifareUltralightAppProcess,
.ApplicationGetUidFunc = MifareUltralightGetUid,
.ApplicationSetUidFunc = MifareUltralightSetUid,
.UidSize = MIFARE_ULTRALIGHT_UID_SIZE,
.MemorySize = MIFARE_ULTRALIGHTC_MEM_SIZE,
.ReadOnly = false,
.TagFamily = TAG_FAMILY_ISO14443A
},
[CONFIG_MF_ULTRALIGHT_EV1_80B] = {
.CodecInitFunc = ISO14443ACodecInit,
.CodecDeInitFunc = ISO14443ACodecDeInit,
+1
View File
@@ -22,6 +22,7 @@ typedef enum {
#ifdef CONFIG_MF_ULTRALIGHT_SUPPORT
CONFIG_MF_ULTRALIGHT,
CONFIG_MF_ULTRALIGHT_C,
CONFIG_MF_ULTRALIGHT_EV1_80B,
CONFIG_MF_ULTRALIGHT_EV1_164B,
#endif
+1 -1
View File
@@ -100,7 +100,7 @@ OPTIMIZATION = s
SRC += $(TARGET).c LUFADescriptors.c System.c Configuration.c Random.c Common.c Memory.c MemoryAsm.S Button.c Log.c Settings.c LED.c Map.c AntennaLevel.c
SRC += Terminal/Terminal.c Terminal/Commands.c Terminal/XModem.c Terminal/CommandLine.c
SRC += Codec/Codec.c Codec/ISO14443-2A.c Codec/Reader14443-2A.c Codec/SniffISO14443-2A.c Codec/Reader14443-ISR.S
SRC += Application/MifareUltralight.c Application/MifareClassic.c Application/ISO14443-3A.c Application/Crypto1.c Application/Reader14443A.c Application/Sniff14443A.c
SRC += Application/MifareUltralight.c Application/MifareClassic.c Application/ISO14443-3A.c Application/Crypto1.c Application/Reader14443A.c Application/Sniff14443A.c Application/CryptoTDEA.S
SRC += Codec/ISO15693.c
SRC += Application/Vicinity.c Application/Sl2s2002.c Application/TITagitstandard.c Application/ISO15693-A.c Application/EM4233.c
SRC += $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)
+3 -2
View File
@@ -72,12 +72,13 @@ bool SettingsSetActiveById(uint8_t Setting) {
GlobalSettings.ActiveSettingPtr =
&GlobalSettings.Settings[SettingIdx];
/* Recall new memory contents ( Moved this to allow for Access to new Memory in Application init())*/
MemoryRecall();
/* Settings have changed. Progress changes through system */
ConfigurationSetById(GlobalSettings.ActiveSettingPtr->Configuration);
LogSetModeById(GlobalSettings.ActiveSettingPtr->LogMode);
/* Recall new memory contents */
MemoryRecall();
SETTING_UPDATE(GlobalSettings.ActiveSettingIdx);
SETTING_UPDATE(GlobalSettings.ActiveSettingPtr);