Files
2019-11-15 10:21:40 +01:00

676 lines
24 KiB
C

/*
* MifareUltralight.c
*
* Created on: 20.03.2013
* Author: skuser
*/
#include "MifareUltralight.h"
#include "ISO14443-3A.h"
#include "../Codec/ISO14443-2A.h"
#include "../Memory.h"
#include "../Random.h"
#include "CryptoTDEA.h"
#define ATQA_VALUE 0x0044
#define SAK_CL1_VALUE ISO14443A_SAK_INCOMPLETE
#define SAK_CL2_VALUE ISO14443A_SAK_COMPLETE_NOT_COMPLIANT
#define ACK_VALUE 0x0A
#define ACK_FRAME_SIZE 4 /* Bits */
#define NAK_INVALID_ARG 0x00
#define NAK_CRC_ERROR 0x01
#define NAK_CTR_ERROR 0x04
#define NAK_EEPROM_ERROR 0x05
#define NAK_OTHER_ERROR 0x06
/* NOTE: the spec is not crystal clear which error is returned */
#define NAK_AUTH_REQUIRED NAK_OTHER_ERROR
#define NAK_AUTH_FAILED NAK_OTHER_ERROR
#define NAK_FRAME_SIZE 4
/* 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 */
#define CMD_WRITE 0xA2
#define CMD_WRITE_FRAME_SIZE 6 /* without CRC bytes */
#define CMD_COMPAT_WRITE 0xA0
#define CMD_COMPAT_WRITE_FRAME_SIZE 2
/* EV1 commands */
#define CMD_GET_VERSION 0x60
#define CMD_FAST_READ 0x3A
#define CMD_READ_CNT 0x39
#define CMD_INCREMENT_CNT 0xA5
#define CMD_PWD_AUTH 0x1B
#define CMD_READ_SIG 0x3C
#define CMD_CHECK_TEARING_EVENT 0x3E
#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
#define UID_CL2_ADDRESS 0x04
#define UID_CL2_SIZE 4
#define UID_BCC2_ADDRESS 0x08
#define LOCK_BYTES_1_ADDRESS 0x0A
#define LOCK_BYTES_2_ADDRESS 0x90
#define CONFIG_AREA_SIZE 0x10
#define CONF_AUTH0_OFFSET 0x03
#define CONF_ACCESS_OFFSET 0x04
#define CONF_VCTID_OFFSET 0x05
#define CONF_PASSWORD_OFFSET 0x08
#define CONF_PACK_OFFSET 0x0C
#define CONF_ACCESS_PROT 0x80
#define CONF_ACCESS_CNFLCK 0x40
#define CNT_MAX 2
#define CNT_SIZE 4
#define CNT_MAX_VALUE 0x00FFFFFF
#define BYTES_PER_READ 16
#define PAGE_READ_MIN 0x00
#define BYTES_PER_WRITE 4
#define PAGE_WRITE_MIN 0x02
#define BYTES_PER_COMPAT_WRITE 16
#define VERSION_INFO_LENGTH 8
#define SIGNATURE_LENGTH 32
static enum {
UL_EV0,
UL_C,
UL_EV1,
} Flavor;
static enum {
STATE_HALT,
STATE_IDLE,
STATE_READY1,
STATE_READY2,
STATE_ACTIVE,
STATE_COMPAT_WRITE,
STATE_AUTH
} State;
static bool FromHalt = false;
static uint8_t PageCount;
static bool ArmedForCompatWrite;
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) {
State = STATE_IDLE;
FromHalt = false;
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) {
/* Set up the emulation flavor */
Flavor = UL_EV0;
/* EV0 cards have fixed size */
PageCount = MIFARE_ULTRALIGHT_PAGES;
/* Default values */
FirstAuthenticatedPage = 0xFF;
ReadAccessProtected = false;
AppInitCommon();
}
static void AppInitEV1Common(void) {
uint8_t ConfigAreaAddress = PageCount * MIFARE_ULTRALIGHT_PAGE_SIZE - CONFIG_AREA_SIZE;
uint8_t Access;
/* Set up the emulation flavor */
Flavor = UL_EV1;
/* Fetch some of the configuration into RAM */
MemoryReadBlock(&FirstAuthenticatedPage, ConfigAreaAddress + CONF_AUTH0_OFFSET, 1);
MemoryReadBlock(&Access, ConfigAreaAddress + CONF_ACCESS_OFFSET, 1);
ReadAccessProtected = !!(Access & CONF_ACCESS_PROT);
AppInitCommon();
}
void MifareUltralightEV11AppInit(void) {
PageCount = MIFARE_ULTRALIGHT_EV11_PAGES;
AppInitEV1Common();
}
void MifareUltralightEV12AppInit(void) {
PageCount = MIFARE_ULTRALIGHT_EV12_PAGES;
AppInitEV1Common();
}
void MifareUltralightAppReset(void) {
State = STATE_IDLE;
}
void MifareUltralightCAppReset(void) {
Authenticated = false;
State = STATE_IDLE;
}
void MifareUltralightAppTask(void) {
}
static bool VerifyAuthentication(uint8_t PageAddress) {
/* No authentication for EV0 cards; always pass */
if (Flavor < UL_C) {
return true;
}
/* If authenticated, no verification needed */
if (Authenticated) {
return true;
}
/* Otherwise, verify the accessed page is below the limit */
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 */
return true;
}
static void AuthCounterReset(void) {
/* Currently not implemented */
}
/* Perform access verification and commit data if passed */
static uint8_t AppWritePage(uint8_t PageAddress, uint8_t *const Buffer) {
if (!ActiveConfiguration.ReadOnly) {
MemoryWriteBlock(Buffer, PageAddress * MIFARE_ULTRALIGHT_PAGE_SIZE, MIFARE_ULTRALIGHT_PAGE_SIZE);
} else {
/* If the chameleon is in read only mode, it silently
* ignores any attempt to write data. */
}
return 0;
}
/* Handles processing of MF commands */
static uint16_t AppProcess(uint8_t *const Buffer, uint16_t ByteCount) {
uint8_t Cmd = Buffer[0];
/* 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;
}
/* Handle EV0 commands */
switch (Cmd) {
case CMD_READ: {
uint8_t PageAddress = Buffer[1];
uint8_t PageLimit;
uint8_t Offset;
/* For EV1+ cards, ensure the wraparound is at the first protected page */
if (Flavor >= UL_C && ReadAccessProtected && !Authenticated) {
PageLimit = FirstAuthenticatedPage;
} else {
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) {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
/* Read out, emulating the wraparound */
for (Offset = 0; Offset < BYTES_PER_READ; Offset += 4) {
MemoryReadBlock(&Buffer[Offset], PageAddress * MIFARE_ULTRALIGHT_PAGE_SIZE, MIFARE_ULTRALIGHT_PAGE_SIZE);
PageAddress++;
if (PageAddress == PageLimit) {
PageAddress = 0;
}
}
ISO14443AAppendCRCA(Buffer, BYTES_PER_READ);
return (BYTES_PER_READ + ISO14443A_CRCA_SIZE) * 8;
}
case CMD_WRITE: {
/* 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;
return NAK_FRAME_SIZE;
}
if (!VerifyAuthentication(PageAddress)) {
Buffer[0] = NAK_AUTH_REQUIRED;
return NAK_FRAME_SIZE;
}
AppWritePage(PageAddress, &Buffer[2]);
Buffer[0] = ACK_VALUE;
return ACK_FRAME_SIZE;
}
case CMD_COMPAT_WRITE: {
uint8_t PageAddress = Buffer[1];
/* Validation */
if ((PageAddress < PAGE_WRITE_MIN) || (PageAddress >= PageCount)) {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
if (!VerifyAuthentication(PageAddress)) {
Buffer[0] = NAK_AUTH_REQUIRED;
return NAK_FRAME_SIZE;
}
/* CRC check passed and page-address is within bounds.
* Store address and proceed to receiving the data. */
CompatWritePageAddress = PageAddress;
ArmedForCompatWrite = true;
Buffer[0] = ACK_VALUE;
return ACK_FRAME_SIZE;
}
case CMD_HALT: {
/* Halts the tag. According to the ISO14443, the second
* byte is supposed to be 0. */
if (Buffer[1] == 0) {
/* According to ISO14443, we must not send anything
* in order to acknowledge the HALT command. */
State = STATE_HALT;
return ISO14443A_APP_NO_RESPONSE;
} else {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
}
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) {
case CMD_GET_VERSION: {
/* Provide hardcoded version response */
Buffer[0] = 0x00;
Buffer[1] = 0x04;
Buffer[2] = 0x03;
Buffer[3] = 0x01; /**/
Buffer[4] = 0x01;
Buffer[5] = 0x00;
Buffer[6] = PageCount == MIFARE_ULTRALIGHT_EV11_PAGES ? 0x0B : 0x0E;
Buffer[7] = 0x03;
ISO14443AAppendCRCA(Buffer, VERSION_INFO_LENGTH);
return (VERSION_INFO_LENGTH + ISO14443A_CRCA_SIZE) * 8;
}
case CMD_FAST_READ: {
uint8_t StartPageAddress = Buffer[1];
uint8_t EndPageAddress = Buffer[2];
/* Validation */
if ((StartPageAddress > EndPageAddress) || (StartPageAddress >= PageCount) || (EndPageAddress >= PageCount)) {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
/* Check authentication only if protection is read&write */
if (ReadAccessProtected) {
if (!VerifyAuthentication(StartPageAddress) || !VerifyAuthentication(EndPageAddress)) {
Buffer[0] = NAK_AUTH_REQUIRED;
return NAK_FRAME_SIZE;
}
}
/* NOTE: With the current implementation, reading the password out is possible. */
ByteCount = (EndPageAddress - StartPageAddress + 1) * MIFARE_ULTRALIGHT_PAGE_SIZE;
MemoryReadBlock(Buffer, StartPageAddress * MIFARE_ULTRALIGHT_PAGE_SIZE, ByteCount);
ISO14443AAppendCRCA(Buffer, ByteCount);
return (ByteCount + ISO14443A_CRCA_SIZE) * 8;
}
case CMD_PWD_AUTH: {
uint8_t ConfigAreaAddress = PageCount * MIFARE_ULTRALIGHT_PAGE_SIZE - CONFIG_AREA_SIZE;
uint8_t Password[4];
/* Verify value and increment authentication attempt counter */
if (!AuthCounterIncrement()) {
/* Too many failed attempts */
Buffer[0] = NAK_AUTH_FAILED;
return NAK_FRAME_SIZE;
}
/* Read and compare the password */
MemoryReadBlock(Password, ConfigAreaAddress + CONF_PASSWORD_OFFSET, 4);
if (Password[0] != Buffer[1] || Password[1] != Buffer[2] || Password[2] != Buffer[3] || Password[3] != Buffer[4]) {
Buffer[0] = NAK_AUTH_FAILED;
return NAK_FRAME_SIZE;
}
/* Authenticate the user */
AuthCounterReset();
Authenticated = 1;
/* Send the PACK value back */
MemoryReadBlock(Buffer, ConfigAreaAddress + CONF_PACK_OFFSET, 2);
ISO14443AAppendCRCA(Buffer, 2);
return (2 + ISO14443A_CRCA_SIZE) * 8;
}
case CMD_READ_CNT: {
uint8_t CounterId = Buffer[1];
/* Validation */
if (CounterId > CNT_MAX) {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
/* Returned counter length is 3 bytes */
MemoryReadBlock(Buffer, (PageCount + CounterId) * MIFARE_ULTRALIGHT_PAGE_SIZE, 3);
ISO14443AAppendCRCA(Buffer, 3);
return (3 + ISO14443A_CRCA_SIZE) * 8;
}
case CMD_INCREMENT_CNT: {
uint8_t CounterId = Buffer[1];
uint32_t Addend = (Buffer[0]) | (Buffer[1] << 8) | ((uint32_t)Buffer[2] << 16);
uint32_t Counter;
/* Validation */
if (CounterId > CNT_MAX) {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
/* Read the value out */
MemoryReadBlock(&Counter, (PageCount + CounterId) * MIFARE_ULTRALIGHT_PAGE_SIZE, MIFARE_ULTRALIGHT_PAGE_SIZE);
/* Add and check for overflow */
Counter += Addend;
if (Counter > CNT_MAX_VALUE) {
Buffer[0] = NAK_CTR_ERROR;
return NAK_FRAME_SIZE;
}
/* Update memory */
MemoryWriteBlock(&Counter, (PageCount + CounterId) * MIFARE_ULTRALIGHT_PAGE_SIZE, MIFARE_ULTRALIGHT_PAGE_SIZE);
Buffer[0] = ACK_VALUE;
return ACK_FRAME_SIZE;
}
case CMD_READ_SIG:
/* Hardcoded response */
memset(Buffer, 0xCA, SIGNATURE_LENGTH);
ISO14443AAppendCRCA(Buffer, SIGNATURE_LENGTH);
return (SIGNATURE_LENGTH + ISO14443A_CRCA_SIZE) * 8;
case CMD_CHECK_TEARING_EVENT:
/* Hardcoded response */
Buffer[0] = 0xBD;
ISO14443AAppendCRCA(Buffer, 1);
return (1 + ISO14443A_CRCA_SIZE) * 8;
case CMD_VCSL: {
uint8_t ConfigAreaAddress = PageCount * MIFARE_ULTRALIGHT_PAGE_SIZE - CONFIG_AREA_SIZE;
/* Input is ignored completely */
/* Read out the value */
MemoryReadBlock(Buffer, ConfigAreaAddress + CONF_VCTID_OFFSET, 1);
ISO14443AAppendCRCA(Buffer, 1);
return (1 + ISO14443A_CRCA_SIZE) * 8;
}
default:
break;
}
}
/* Command not handled. Switch to idle. */
State = STATE_IDLE;
return ISO14443A_APP_NO_RESPONSE;
}
uint16_t MifareUltralightAppProcess(uint8_t *Buffer, uint16_t BitCount) {
uint8_t Cmd = Buffer[0];
uint16_t ByteCount;
switch (State) {
case STATE_IDLE:
case STATE_HALT:
FromHalt = State == STATE_HALT;
if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE, FromHalt)) {
/* We received a REQA or WUPA command, so wake up. */
State = STATE_READY1;
return BitCount;
}
break;
case STATE_READY1:
if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE, FromHalt)) {
State = FromHalt ? STATE_HALT : STATE_IDLE;
return ISO14443A_APP_NO_RESPONSE;
} else if (Cmd == ISO14443A_CMD_SELECT_CL1) {
/* Load UID CL1 and perform anticollision. Since
* MF Ultralight use a double-sized UID, the first byte
* of CL1 has to be the cascade-tag byte. */
uint8_t UidCL1[ISO14443A_CL_UID_SIZE] = { [0] = ISO14443A_UID0_CT };
MemoryReadBlock(&UidCL1[1], UID_CL1_ADDRESS, UID_CL1_SIZE);
if (ISO14443ASelect(Buffer, &BitCount, UidCL1, SAK_CL1_VALUE)) {
/* CL1 stage has ended successfully */
State = STATE_READY2;
}
return BitCount;
} else {
/* Unknown command. Enter halt state */
State = STATE_IDLE;
}
break;
case STATE_READY2:
if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE, FromHalt)) {
State = FromHalt ? STATE_HALT : STATE_IDLE;
return ISO14443A_APP_NO_RESPONSE;
} else if (Cmd == ISO14443A_CMD_SELECT_CL2) {
/* Load UID CL2 and perform anticollision */
uint8_t UidCL2[ISO14443A_CL_UID_SIZE];
MemoryReadBlock(UidCL2, UID_CL2_ADDRESS, UID_CL2_SIZE);
if (ISO14443ASelect(Buffer, &BitCount, UidCL2, SAK_CL2_VALUE)) {
/* CL2 stage has ended successfully. This means
* our complete UID has been sent to the reader. */
State = STATE_ACTIVE;
}
return BitCount;
} else {
/* Unknown command. Enter halt state */
State = STATE_IDLE;
}
break;
case STATE_ACTIVE:
/* Preserve incoming data length */
ByteCount = (BitCount + 7) >> 3;
if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE, FromHalt)) {
State = FromHalt ? STATE_HALT : STATE_IDLE;
return ISO14443A_APP_NO_RESPONSE;
}
/* At the very least, there should be 3 bytes in the buffer. */
if (ByteCount < (1 + ISO14443A_CRCA_SIZE)) {
State = STATE_IDLE;
return ISO14443A_APP_NO_RESPONSE;
}
/* All commands here have CRCA appended; verify it right away */
ByteCount -= 2;
if (!ISO14443ACheckCRCA(Buffer, ByteCount)) {
Buffer[0] = NAK_CRC_ERROR;
return NAK_FRAME_SIZE;
}
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;
}
/* No response has been sent, when we reach here */
return ISO14443A_APP_NO_RESPONSE;
}
void MifareUltralightGetUid(ConfigurationUidType Uid) {
/* Read UID from memory */
MemoryReadBlock(&Uid[0], UID_CL1_ADDRESS, UID_CL1_SIZE);
MemoryReadBlock(&Uid[UID_CL1_SIZE], UID_CL2_ADDRESS, UID_CL2_SIZE);
}
void MifareUltralightSetUid(ConfigurationUidType Uid) {
/* Calculate check bytes and write everything into memory */
uint8_t BCC1 = ISO14443A_UID0_CT ^ Uid[0] ^ Uid[1] ^ Uid[2];
uint8_t BCC2 = Uid[3] ^ Uid[4] ^ Uid[5] ^ Uid[6];
MemoryWriteBlock(&Uid[0], UID_CL1_ADDRESS, UID_CL1_SIZE);
MemoryWriteBlock(&BCC1, UID_BCC1_ADDRESS, ISO14443A_CL_BCC_SIZE);
MemoryWriteBlock(&Uid[UID_CL1_SIZE], UID_CL2_ADDRESS, UID_CL2_SIZE);
MemoryWriteBlock(&BCC2, UID_BCC2_ADDRESS, ISO14443A_CL_BCC_SIZE);
}