From ac8b1cc944a8b8f1a9054db1b455d91448157fab Mon Sep 17 00:00:00 2001 From: dev_zzo Date: Fri, 17 Nov 2017 19:27:20 +0100 Subject: [PATCH] UL EV1: enable x21 size; minor improvements --- .../Application/MifareUltralight.c | 92 ++++++++++++++----- .../Application/MifareUltralight.h | 4 +- Firmware/Chameleon-Mini/Configuration.c | 5 +- 3 files changed, 76 insertions(+), 25 deletions(-) diff --git a/Firmware/Chameleon-Mini/Application/MifareUltralight.c b/Firmware/Chameleon-Mini/Application/MifareUltralight.c index 6a57e36..c1bb6af 100644 --- a/Firmware/Chameleon-Mini/Application/MifareUltralight.c +++ b/Firmware/Chameleon-Mini/Application/MifareUltralight.c @@ -24,7 +24,9 @@ #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 */ @@ -58,6 +60,7 @@ #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 @@ -79,6 +82,9 @@ #define BYTES_PER_COMPAT_WRITE 16 +#define VERSION_INFO_LENGTH 8 +#define SIGNATURE_LENGTH 32 + static enum { UL_EV0, UL_EV1, @@ -111,8 +117,11 @@ static void AppInitCommon(void) 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(); @@ -122,7 +131,10 @@ static void AppInitEV1Common(void) { uint8_t ConfigAreaAddress = PageCount * BYTES_PER_PAGE - 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); @@ -153,17 +165,29 @@ void MifareUltralightAppTask(void) static bool VerifyAuthentication(uint8_t PageAddress) { - /* No authentication for EV0 cards */ + /* No authentication for EV0 cards; always pass */ if (Flavor < UL_EV1) { return true; } - /* If authenticated; no verification needed */ + /* If authenticated, no verification needed */ if (Authenticated) { return true; } + /* Otherwise, verify the accessed page is below the limit */ return PageAddress < FirstAuthenticatedPage; } +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) { @@ -181,12 +205,7 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount) { uint8_t Cmd = Buffer[0]; - /* All commands here have CRCA appended; verify it right away */ - if (!ISO14443ACheckCRCA(Buffer, ByteCount - 2)) { - Buffer[0] = NAK_CRC_ERROR; - return NAK_FRAME_SIZE; - } - + /* Handle the compatibility write command */ if (ArmedForCompatWrite) { ArmedForCompatWrite = false; AppWritePage(CompatWritePageAddress, &Buffer[2]); @@ -291,8 +310,8 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount) Buffer[5] = 0x00; Buffer[6] = PageCount == MIFARE_ULTRALIGHT_EV11_PAGES ? 0x0B : 0x0E; Buffer[7] = 0x03; - ISO14443AAppendCRCA(Buffer, 8); - return (8 + ISO14443A_CRCA_SIZE) * 8; + ISO14443AAppendCRCA(Buffer, VERSION_INFO_LENGTH); + return (VERSION_INFO_LENGTH + ISO14443A_CRCA_SIZE) * 8; } case CMD_FAST_READ: { @@ -320,12 +339,23 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount) case CMD_PWD_AUTH: { uint8_t ConfigAreaAddress = PageCount * BYTES_PER_PAGE - CONFIG_AREA_SIZE; uint8_t Password[4]; - 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_REQUIRED; + + /* 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; @@ -338,9 +368,10 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount) Buffer[0] = NAK_INVALID_ARG; return NAK_FRAME_SIZE; } - MemoryReadBlock(Buffer, (PageCount + CounterId) * BYTES_PER_PAGE, BYTES_PER_PAGE); - ISO14443AAppendCRCA(Buffer, BYTES_PER_PAGE); - return (BYTES_PER_PAGE + ISO14443A_CRCA_SIZE) * 8; + /* Returned counter length is 3 bytes */ + MemoryReadBlock(Buffer, (PageCount + CounterId) * BYTES_PER_PAGE, 3); + ISO14443AAppendCRCA(Buffer, 3); + return (3 + ISO14443A_CRCA_SIZE) * 8; } case CMD_INCREMENT_CNT: { @@ -352,12 +383,15 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount) Buffer[0] = NAK_INVALID_ARG; return NAK_FRAME_SIZE; } + /* Read the value out */ MemoryReadBlock(&Counter, (PageCount + CounterId) * BYTES_PER_PAGE, BYTES_PER_PAGE); + /* 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) * BYTES_PER_PAGE, BYTES_PER_PAGE); Buffer[0] = ACK_VALUE; return ACK_FRAME_SIZE; @@ -365,9 +399,9 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount) case CMD_READ_SIG: /* Hardcoded response */ - memset(Buffer, 0xCA, 32); - ISO14443AAppendCRCA(Buffer, 32); - return (32 + ISO14443A_CRCA_SIZE) * 8; + memset(Buffer, 0xCA, SIGNATURE_LENGTH); + ISO14443AAppendCRCA(Buffer, SIGNATURE_LENGTH); + return (SIGNATURE_LENGTH + ISO14443A_CRCA_SIZE) * 8; case CMD_CHECK_TEARING_EVENT: /* Hardcoded response */ @@ -375,11 +409,14 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount) ISO14443AAppendCRCA(Buffer, 1); return (1 + ISO14443A_CRCA_SIZE) * 8; - case CMD_VCSL: - /* Hardcoded response */ - Buffer[0] = 0x05; + case CMD_VCSL: { + uint8_t ConfigAreaAddress = PageCount * BYTES_PER_PAGE - 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; @@ -460,6 +497,17 @@ uint16_t MifareUltralightAppProcess(uint8_t* Buffer, uint16_t BitCount) 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); default: diff --git a/Firmware/Chameleon-Mini/Application/MifareUltralight.h b/Firmware/Chameleon-Mini/Application/MifareUltralight.h index 61577de..bae7a38 100644 --- a/Firmware/Chameleon-Mini/Application/MifareUltralight.h +++ b/Firmware/Chameleon-Mini/Application/MifareUltralight.h @@ -16,7 +16,9 @@ #define MIFARE_ULTRALIGHT_PAGES 16 #define MIFARE_ULTRALIGHT_EV11_PAGES 20 #define MIFARE_ULTRALIGHT_EV12_PAGES 41 -#define MIFARE_ULTRALIGHT_MEM_SIZE (MIFARE_ULTRALIGHT_PAGES * MIFARE_ULTRALIGHT_PAGE_SIZE) +#define MIFARE_ULTRALIGHT_MEM_SIZE (MIFARE_ULTRALIGHT_PAGES * MIFARE_ULTRALIGHT_PAGE_SIZE) +#define MIFARE_ULTRALIGHT_EV11_MEM_SIZE (MIFARE_ULTRALIGHT_EV11_PAGES * MIFARE_ULTRALIGHT_PAGE_SIZE) +#define MIFARE_ULTRALIGHT_EV12_MEM_SIZE (MIFARE_ULTRALIGHT_EV12_PAGES * MIFARE_ULTRALIGHT_PAGE_SIZE) void MifareUltralightAppInit(void); void MifareUltralightEV11AppInit(void); diff --git a/Firmware/Chameleon-Mini/Configuration.c b/Firmware/Chameleon-Mini/Configuration.c index 2bf5039..8beba51 100644 --- a/Firmware/Chameleon-Mini/Configuration.c +++ b/Firmware/Chameleon-Mini/Configuration.c @@ -17,6 +17,7 @@ static const MapEntryType PROGMEM ConfigurationMap[] = { #ifdef CONFIG_MF_ULTRALIGHT_SUPPORT { .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" }, #endif #ifdef CONFIG_MF_CLASSIC_1K_SUPPORT { .Id = CONFIG_MF_CLASSIC_1K, .Text = "MF_CLASSIC_1K" }, @@ -98,7 +99,7 @@ static const PROGMEM ConfigurationType ConfigurationTable[] = { .ApplicationGetUidFunc = MifareUltralightGetUid, .ApplicationSetUidFunc = MifareUltralightSetUid, .UidSize = MIFARE_ULTRALIGHT_UID_SIZE, - .MemorySize = MIFARE_ULTRALIGHT_MEM_SIZE, + .MemorySize = MIFARE_ULTRALIGHT_EV11_MEM_SIZE, .ReadOnly = false }, [CONFIG_MF_ULTRALIGHT_EV1_164B] = { @@ -113,7 +114,7 @@ static const PROGMEM ConfigurationType ConfigurationTable[] = { .ApplicationGetUidFunc = MifareUltralightGetUid, .ApplicationSetUidFunc = MifareUltralightSetUid, .UidSize = MIFARE_ULTRALIGHT_UID_SIZE, - .MemorySize = MIFARE_ULTRALIGHT_MEM_SIZE, + .MemorySize = MIFARE_ULTRALIGHT_EV12_MEM_SIZE, .ReadOnly = false }, #endif