Merge pull request #66 from mgp25/proxgrind

NTAG 215 Support + ChamTool update
This commit is contained in:
Iceman
2021-08-18 20:42:31 +02:00
committed by GitHub
9 changed files with 505 additions and 1 deletions
+3
View File
@@ -0,0 +1,3 @@
.DS_Store
*.pyc
@@ -21,6 +21,7 @@
#include "TITagitstandard.h"
#include "Sniff14443A.h"
#include "EM4233.h"
#include "NTAG215.h"
/* Function wrappers */
INLINE void ApplicationInit(void) {
@@ -0,0 +1,438 @@
/*
* NTAG215.c
*
* Created on: 20.02.2019
* Author: Giovanni Cammisa (gcammisa)
* Still missing support for:
* - The management of both static and dynamic lock bytes
* - Bruteforce protection (AUTHLIM COUNTER)
* Thanks to skuser for the MifareUltralight code used as a starting point
*/
#include "ISO14443-3A.h"
#include "../Codec/ISO14443-2A.h"
#include "../Memory.h"
#include "NTAG215.h"
// DEFINE ATQA and SAK
#define ATQA_VALUE 0x0044
#define SAK_VALUE 0x00
#define SAK_CL1_VALUE ISO14443A_SAK_INCOMPLETE
#define SAK_CL2_VALUE ISO14443A_SAK_COMPLETE_NOT_COMPLIANT
// ACK and NACK
#define ACK_VALUE 0x0A
#define ACK_FRAME_SIZE 4 /* Bits */
#define NAK_INVALID_ARG 0x00
#define NAK_CRC_ERROR 0x01
#define NAK_NOT_AUTHED 0x04
#define NAK_EEPROM_ERROR 0x05
#define NAK_FRAME_SIZE 4
// DEFINING COMMANDS
/* ISO commands */
#define CMD_HALT 0x50
// NTAG COMMANDS
#define CMD_GET_VERSION 0x60
#define CMD_READ 0x30
#define CMD_FAST_READ 0x3A
#define CMD_WRITE 0xA2
#define CMD_COMPAT_WRITE 0xA0
#define CMD_READ_CNT 0x39
#define CMD_PWD_AUTH 0x1B
#define CMD_READ_SIG 0x3C
// MEMORY LAYOUT STUFF, addresses and sizes in bytes
// UID stuff
#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
// LockBytes stuff
#define STATIC_LOCKBYTE_0_ADDRESS 0x0A
#define STATIC_LOCKBYTE_1_ADDRESS 0x0B
// CONFIG stuff
#define CONFIG_AREA_START_ADDRESS NTAG215_PAGE_SIZE * 0x83
#define CONFIG_AREA_SIZE 8
// CONFIG offsets, relative to config start address
#define CONF_AUTH0_OFFSET 0x03
#define CONF_ACCESS_OFFSET 0x04
#define CONF_PASSWORD_OFFSET 0x08
#define CONF_PACK_OFFSET 0x0C
// WRITE STUFF
#define BYTES_PER_WRITE 4
#define PAGE_WRITE_MIN 0x02
// CONFIG masks to check individual needed bits
#define CONF_ACCESS_PROT 0x80
#define VERSION_INFO_LENGTH 8 //8 bytes info lenght + crc
#define BYTES_PER_READ NTAG215_PAGE_SIZE * 4
// SIGNATURE Lenght
#define SIGNATURE_LENGTH 32
static enum {
STATE_HALT,
STATE_IDLE,
STATE_READY1,
STATE_READY2,
STATE_ACTIVE
} 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 Access;
void NTAG215AppInit(void)
{
State = STATE_IDLE;
FromHalt = false;
ArmedForCompatWrite = false;
Authenticated = false;
PageCount = NTAG215_PAGES;
/* Fetch some of the configuration into RAM */
MemoryReadBlock(&FirstAuthenticatedPage, CONFIG_AREA_START_ADDRESS + CONF_AUTH0_OFFSET, 1);
MemoryReadBlock(&Access, CONFIG_AREA_START_ADDRESS + CONF_ACCESS_OFFSET, 1);
ReadAccessProtected = !!(Access & CONF_ACCESS_PROT);
}
void NTAG215AppReset(void)
{
State = STATE_IDLE;
}
void NTAG215AppTask(void)
{
}
// Verify authentication
static bool VerifyAuthentication(uint8_t PageAddress)
{
/* If authenticated, no verification needed */
if (Authenticated) {
return true;
}
/* Otherwise, verify the accessed page is below the limit */
return PageAddress < FirstAuthenticatedPage;
}
// Writes a page
static uint8_t AppWritePage(uint8_t PageAddress, uint8_t* const Buffer)
{
if (!ActiveConfiguration.ReadOnly) {
MemoryWriteBlock(Buffer, PageAddress * NTAG215_PAGE_SIZE, NTAG215_PAGE_SIZE);
} else {
/* If the chameleon is in read only mode, it silently
* ignores any attempt to write data. */
}
return 0;
}
// Basic sketch of the command handling stuff
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;
AppWritePage(CompatWritePageAddress, &Buffer[2]);
Buffer[0] = ACK_VALUE;
return ACK_FRAME_SIZE;
}
switch (Cmd) {
case CMD_GET_VERSION: {
/* Provide hardcoded version response */ //VERSION RESPONSE FOR NTAG 215
Buffer[0] = 0x00;
Buffer[1] = 0x04;
Buffer[2] = 0x04;
Buffer[3] = 0x02;
Buffer[4] = 0x01;
Buffer[5] = 0x00;
Buffer[6] = 0x11;
Buffer[7] = 0x03;
ISO14443AAppendCRCA(Buffer, VERSION_INFO_LENGTH);
return (VERSION_INFO_LENGTH + ISO14443A_CRCA_SIZE) * 8;
}
case CMD_READ: {
uint8_t PageAddress = Buffer[1];
uint8_t PageLimit;
uint8_t Offset;
PageLimit = PageCount;
/* if protected and not autenticated, ensure the wraparound is at the first protected page */
if (ReadAccessProtected && !Authenticated) {
PageLimit = FirstAuthenticatedPage;
}
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 * NTAG215_PAGE_SIZE, NTAG215_PAGE_SIZE);
PageAddress++;
if (PageAddress == PageLimit) { // if arrived ad the last page, start reading from page 0
PageAddress = 0;
}
}
ISO14443AAppendCRCA(Buffer, BYTES_PER_READ);
return (BYTES_PER_READ + 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 (instead of only write protection) */
if (ReadAccessProtected) {
if (!VerifyAuthentication(StartPageAddress) || !VerifyAuthentication(EndPageAddress)) {
Buffer[0] = NAK_NOT_AUTHED;
return NAK_FRAME_SIZE;
}
}
ByteCount = (EndPageAddress - StartPageAddress + 1) * NTAG215_PAGE_SIZE;
MemoryReadBlock(Buffer, StartPageAddress * NTAG215_PAGE_SIZE, ByteCount);
ISO14443AAppendCRCA(Buffer, ByteCount);
return (ByteCount + ISO14443A_CRCA_SIZE) * 8;
}
case CMD_PWD_AUTH: {
uint8_t Password[4];
/* For now I don't care about bruteforce protection, so: */
/* TODO: IMPLEMENT COUNTER AUTHLIM */
/* Read and compare the password */
MemoryReadBlock(Password, CONFIG_AREA_START_ADDRESS + CONF_PASSWORD_OFFSET, 4);
if (Password[0] != Buffer[1] || Password[1] != Buffer[2] || Password[2] != Buffer[3] || Password[3] != Buffer[4]) {
Buffer[0] = NAK_NOT_AUTHED;
return NAK_FRAME_SIZE;
}
/* Authenticate the user */
//RESET AUTHLIM COUNTER, CURRENTLY NOT IMPLEMENTED
Authenticated = 1;
/* Send the PACK value back */
MemoryReadBlock(Buffer, CONFIG_AREA_START_ADDRESS + CONF_PACK_OFFSET, 2);
ISO14443AAppendCRCA(Buffer, 2);
return (2 + 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];
/* Validation */
if ((PageAddress < PAGE_WRITE_MIN) || (PageAddress >= PageCount)) {
Buffer[0] = NAK_INVALID_ARG;
return NAK_FRAME_SIZE;
}
if (!VerifyAuthentication(PageAddress)) {
Buffer[0] = NAK_NOT_AUTHED;
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_NOT_AUTHED;
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; //TODO:IMPLEMENT ARMED COMPAT WRITE
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;
}
//PART OF ISO STANDARD, NOT OF NTAG DATASHEET
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;
}
}
/* Command not handled. Switch to idle. */
State = STATE_IDLE;
return ISO14443A_APP_NO_RESPONSE;
}
// FINITE STATE MACHINE STUFF, SHOULD BE THE VERY SIMILAR TO Mifare Ultralight
uint16_t NTAG215AppProcess(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;
// Only ACTIVE state, no AUTHENTICATED state, PWD_AUTH is handled in commands.
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);
default:
/* Unknown state? Should never happen. */
break;
}
/* No response has been sent, when we reach here */
return ISO14443A_APP_NO_RESPONSE;
}
// HELPER FUNCTIONS
void NTAG215GetUid(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 NTAG215SetUid(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);
}
@@ -0,0 +1,27 @@
/*
* NTAG215.h
*
* Created on: 20.02.2019
* Author: gcammisa
*/
#ifndef NTAG215_H_
#define NTAG215_H_
#include "Application.h"
#include "ISO14443-3A.h"
#define NTAG215_UID_SIZE ISO14443A_UID_SIZE_DOUBLE // 7 bytes UID
#define NTAG215_PAGE_SIZE 4 // bytes per page
#define NTAG215_PAGES 135 // 135 pages total, from 0 to 134
#define NTAG215_MEM_SIZE ( NTAG215_PAGE_SIZE * NTAG215_PAGES )
void NTAG215AppInit(void);
void NTAG215AppReset(void);
void NTAG215AppTask(void);
uint16_t NTAG215AppProcess(uint8_t* Buffer, uint16_t BitCount);
void NTAG215GetUid(ConfigurationUidType Uid);
void NTAG215SetUid(ConfigurationUidType Uid);
#endif
+21
View File
@@ -51,6 +51,9 @@ static const MapEntryType PROGMEM ConfigurationMap[] = {
#ifdef CONFIG_ISO14443A_READER_SUPPORT
{ .Id = CONFIG_ISO14443A_READER, .Text = "ISO14443A_READER" },
#endif
#ifdef CONFIG_NTAG215_SUPPORT
{ .Id = CONFIG_NTAG215, .Text = "NTAG215" },
#endif
#ifdef CONFIG_VICINITY_SUPPORT
{ .Id = CONFIG_VICINITY, .Text = "VICINITY" },
#endif
@@ -478,6 +481,24 @@ static const PROGMEM ConfigurationType ConfigurationTable[] = {
.TagFamily = TAG_FAMILY_ISO15693
},
#endif
#ifdef CONFIG_NTAG215_SUPPORT
[CONFIG_NTAG215] = {
.CodecInitFunc = ISO14443ACodecInit,
.CodecDeInitFunc = ISO14443ACodecDeInit,
.CodecTaskFunc = ISO14443ACodecTask,
.ApplicationInitFunc = NTAG215AppInit,
.ApplicationResetFunc = NTAG215AppReset,
.ApplicationTaskFunc = NTAG215AppTask,
.ApplicationTickFunc = ApplicationTickDummy,
.ApplicationProcessFunc = NTAG215AppProcess,
.ApplicationGetUidFunc = NTAG215GetUid,
.ApplicationSetUidFunc = NTAG215SetUid,
.UidSize = NTAG215_UID_SIZE,
.MemorySize = NTAG215_MEM_SIZE,
.ReadOnly = false,
.TagFamily = TAG_FAMILY_ISO14443A
},
#endif
};
ConfigurationType ActiveConfiguration;
+3
View File
@@ -57,6 +57,9 @@ typedef enum {
#ifdef CONFIG_ISO14443A_READER_SUPPORT
CONFIG_ISO14443A_READER,
#endif
#ifdef CONFIG_NTAG215_SUPPORT
CONFIG_NTAG215,
#endif
#ifdef CONFIG_VICINITY_SUPPORT
CONFIG_VICINITY,
#endif
+2 -1
View File
@@ -17,6 +17,7 @@ SETTINGS += -DCONFIG_MF_CLASSIC_4K_7B_SUPPORT
SETTINGS += -DCONFIG_MF_ULTRALIGHT_SUPPORT
SETTINGS += -DCONFIG_ISO14443A_SNIFF_SUPPORT
SETTINGS += -DCONFIG_ISO14443A_READER_SUPPORT
SETTINGS += -DCONFIG_NTAG215_SUPPORT
SETTINGS += -DCONFIG_VICINITY_SUPPORT
SETTINGS += -DCONFIG_SL2S2002_SUPPORT
SETTINGS += -DCONFIG_TITAGITSTANDARD_SUPPORT
@@ -113,7 +114,7 @@ SRC += Terminal/Terminal.c Terminal/Commands.c Terminal/XModem.c Termina
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 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 += Application/Vicinity.c Application/Sl2s2002.c Application/TITagitstandard.c Application/ISO15693-A.c Application/EM4233.c Application/NTAG215.c
SRC += $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)
LUFA_PATH = ../LUFA
CC_FLAGS = -flto -DUSE_LUFA_CONFIG_HEADER -DFLASH_DATA_ADDR=$(FLASH_DATA_ADDR) -DFLASH_DATA_SIZE=$(FLASH_DATA_SIZE) -DSPM_HELPER_ADDR=$(SPM_HELPER_ADDR) -DBUILD_DATE=$(BUILD_DATE) -DCOMMIT_ID=\"$(COMMIT_ID)\" $(SETTINGS)
+4
View File
@@ -28,6 +28,7 @@ class Device:
COMMAND_RED_LED = "LEDRED"
COMMAND_THRESHOLD = "THRESHOLD"
COMMAND_UPGRADE = "upgrade"
COMMAND_CLEAR = "CLEAR"
STATUS_CODE_OK = 100
STATUS_CODE_OK_WITH_TEXT = 101
@@ -207,6 +208,9 @@ class Device:
def cmdClearLog(self):
return self.execCmd(self.COMMAND_LOG_CLEAR)
def cmdClear(self):
return self.execCmd(self.COMMAND_CLEAR)
def cmdLogMode(self, newLogMode):
return self.getSetCmd(self.COMMAND_LOGMODE, newLogMode)
+6
View File
@@ -182,6 +182,10 @@ def cmdUpgrade(chameleon, arg):
print ("Device changed into Upgrade Mode")
exit(0)
def cmdClear(chameleon, arg):
chameleon.cmdClear()
return "Slot has been cleared"
# Custom class for argparse
class CmdListAction(argparse.Action):
def __init__(self, option_strings, dest, default=False, required=False,
@@ -224,6 +228,7 @@ def main():
cmdArgGroup.add_argument("-rl", "--rled", dest="rled", action=CmdListAction, metavar="FUNCTION", nargs='?', help="retrieve or set the current red led function")
cmdArgGroup.add_argument("-th", "--threshold", dest="threshold", action=CmdListAction, nargs='?', help="retrieve or set the threshold")
cmdArgGroup.add_argument("-ug", "--upgrade", dest="upgrade", action=CmdListAction, nargs=0, help="set the micro Controller to upgrade mode")
cmdArgGroup.add_argument("-cl", "--clear", dest="clear", action=CmdListAction, nargs=0, help="clear the slot")
args = argParser.parse_args()
@@ -258,6 +263,7 @@ def main():
"rled" : cmdRedLED,
"threshold" : cmdThreshold,
"upgrade" : cmdUpgrade,
"clear" : cmdClear,
}
if hasattr(args, "cmdList"):