Skip to content

Commit

Permalink
Added stub EM mutual auth commands and fixed a couple of bugs
Browse files Browse the repository at this point in the history
E0 and E1 commands will be expanded as reverse engineering continues. At the moment E0 will be used to work on reader response to A1 random number.
Fixed 2 different bugs in Login. Fixed Lock DSFID command
Removed frame integrity checks from PrepareFrame, since it's already needed to do them before PrepareFrame should be called
  • Loading branch information
ceres-c authored and bosb committed Mar 5, 2019
1 parent 562764e commit 9ae3aec
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 26 deletions.
110 changes: 91 additions & 19 deletions Firmware/Chameleon-Mini/Application/EM4233.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
*
* Created on: 12-05-2018
* Author: ceres-c & MrMoDDoM
* Notes:
* - In EM4233.h you can find the define EM4233_LOGIN_YES_CARD that has to be uncommnted
* to allow any login request without checking the password.
*
* TODO:
* - Check with real tag every command's actual response in addressed/selected State
* (Only EM4233_Read_Single and EM4233_Read_Multiple have been checked up to now)
*/

#include "../Random.h"
#include "ISO15693-A.h"
#include "EM4233.h"

Expand Down Expand Up @@ -274,7 +279,7 @@ uint16_t EM4233_Lock_AFI(uint8_t* FrameBuf, uint16_t FrameBytes)

LockStatus |= EM4233_MASK_AFI_STATUS;

MemoryWriteBlock(&LockStatus, EM4233_MEM_INF_ADDRESS, 1); /* Actually write new AFI */
MemoryWriteBlock(&LockStatus, EM4233_MEM_INF_ADDRESS, 1); /* Write in info bits AFI lockdown */

// FrameBuf[ISO15693_ADDR_FLAGS] = ISO15693_RES_FLAG_NO_ERROR; /* flags */
// ResponseByteCount += 1;
Expand Down Expand Up @@ -329,7 +334,7 @@ uint16_t EM4233_Lock_DSFID(uint8_t* FrameBuf, uint16_t FrameBytes)

LockStatus |= EM4233_MASK_DSFID_STATUS;

MemoryWriteBlock(*FrameInfo.Parameters, EM4233_MEM_INF_ADDRESS, 4); /* Actually write the new DSFID */
MemoryWriteBlock(&LockStatus, EM4233_MEM_INF_ADDRESS, 1); /* Write in info bits DSFID lockdown */

// FrameBuf[ISO15693_ADDR_FLAGS] = ISO15693_RES_FLAG_NO_ERROR; /* flags */
// ResponseByteCount += 1;
Expand Down Expand Up @@ -450,8 +455,8 @@ uint16_t EM4233_Select(uint8_t* FrameBuf, uint16_t FrameBytes, uint8_t* Uid)
bool UidEquals = ISO15693CompareUid(&FrameBuf[ISO15693_REQ_ADDR_PARAM], Uid);

if (!(FrameBuf[ISO15693_ADDR_FLAGS] & ISO15693_REQ_FLAG_ADDRESS) ||
(FrameBuf[ISO15693_ADDR_FLAGS] & ISO15693_REQ_FLAG_SELECT)
) {
(FrameBuf[ISO15693_ADDR_FLAGS] & ISO15693_REQ_FLAG_SELECT)
) {
/* tag should remain silent if Select is performed without address flag or with select flag */
return ISO15693_APP_NO_RESPONSE;
} else if (!UidEquals) {
Expand All @@ -466,6 +471,10 @@ uint16_t EM4233_Select(uint8_t* FrameBuf, uint16_t FrameBytes, uint8_t* Uid)
ResponseByteCount += 1;
return ResponseByteCount;
}

/* This should never happen (TM), I've added it to shut the compiler up */
State = STATE_READY;
return ISO15693_APP_NO_RESPONSE;
}

uint16_t EM4233_Reset_To_Ready(uint8_t* FrameBuf, uint16_t FrameBytes)
Expand Down Expand Up @@ -493,19 +502,26 @@ uint16_t EM4233_Reset_To_Ready(uint8_t* FrameBuf, uint16_t FrameBytes)
return ResponseByteCount;
}

uint16_t EM4233_Login(uint8_t* FrameBuf, uint16_t FrameBytes, uint8_t* Uid)
uint16_t EM4233_Login(uint8_t* FrameBuf, uint16_t FrameBytes)
{
uint16_t ResponseByteCount = ISO15693_APP_NO_RESPONSE;
uint8_t Password[4] = { 0 };

if (FrameInfo.ParamLen != 4)
return ISO15693_APP_NO_RESPONSE; /* malformed: not enough or too much data */
if (FrameInfo.ParamLen != 4 || !FrameInfo.Addressed)
/* Malformed: not enough or too much data. Also this command only works in addressed mode */
return ISO15693_APP_NO_RESPONSE;

MemoryReadBlock(&Password, EM4233_MEM_PSW_ADDRESS, 4);

if( false ){ // YES-MAN!
// if (!memcmp(Password, FrameInfo.Parameters, 4)) { /* Incorrect password */
#ifdef EM4233_LOGIN_YES_CARD
/* Accept any password from reader as correct one */
loggedIn = true;

MemoryWriteBlock(FrameInfo.Parameters, EM4233_MEM_PSW_ADDRESS, 4); /* Store password in memory for retrival */

#else
/* Check if the password is actually the right one */
if (memcmp(Password, FrameInfo.Parameters, 4) != 0) { /* Incorrect password */
loggedIn = false;

// FrameBuf[ISO15693_ADDR_FLAGS] = ISO15693_RES_FLAG_ERROR;
Expand All @@ -514,21 +530,71 @@ uint16_t EM4233_Login(uint8_t* FrameBuf, uint16_t FrameBytes, uint8_t* Uid)
return ResponseByteCount;
}

loggedIn = true;
#endif

MemoryWriteBlock(Password, EM4233_MEM_PSW_ADDRESS, 4); /* Store password in memory for retrival */
loggedIn = true;

FrameBuf[ISO15693_ADDR_FLAGS] = ISO15693_RES_FLAG_NO_ERROR; /* flags */
ResponseByteCount += 1;
return ResponseByteCount;
}

uint16_t EM4233_Auth1(uint8_t* FrameBuf, uint16_t FrameBytes)
{
uint16_t ResponseByteCount = ISO15693_APP_NO_RESPONSE;
// uint8_t KeyNo = *FrameInfo.Parameters; /* Right now this parameter is unused, but it will be useful */

if (FrameInfo.ParamLen != 1 || !FrameInfo.Addressed)
/* Malformed: not enough or too much data. Also this command only works in addressed mode */
return ISO15693_APP_NO_RESPONSE;

FrameBuf[ISO15693_ADDR_FLAGS] = ISO15693_RES_FLAG_NO_ERROR;
#ifdef EM4233_LOGIN_YES_CARD
/* Will be useful for testing purposes, probably removed in final version */
FrameBuf[ISO15693_ADDR_FLAGS + 1] = 0x00;
FrameBuf[ISO15693_ADDR_FLAGS + 2] = 0x00;
FrameBuf[ISO15693_ADDR_FLAGS + 3] = 0x00;
FrameBuf[ISO15693_ADDR_FLAGS + 4] = 0x00;
FrameBuf[ISO15693_ADDR_FLAGS + 5] = 0x00;
FrameBuf[ISO15693_ADDR_FLAGS + 6] = 0x00;
FrameBuf[ISO15693_ADDR_FLAGS + 7] = 0x00;
#else
/* Respond like a real tag */
RandomGetBuffer(FrameBuf + 0x01, 7); /* Random number A1 in EM Marin definitions */
#endif
ResponseByteCount += 8;
return ResponseByteCount;
}

uint16_t EM4233_Auth2(uint8_t* FrameBuf, uint16_t FrameBytes)
{
uint16_t ResponseByteCount = ISO15693_APP_NO_RESPONSE;
// uint8_t A2 = FrameInfo.Parameters;
// uint8_t f = FrameInfo.Parameters + 0x08;
// uint8_t g[3] = { 0 }; /* Names according to EM Marin definitions */

if (FrameInfo.ParamLen != 11) /* Malformed: not enough or too much data */
return ISO15693_APP_NO_RESPONSE;
// else if (!fFunc()) /* Actual f implementation to check if the f bytes we received are correct */
// return ISO15693_APP_NO_RESPONSE;


FrameBuf[ISO15693_ADDR_FLAGS] = ISO15693_RES_FLAG_NO_ERROR;
RandomGetBuffer(FrameBuf + 0x01, 3); /* This should be replaced with actual gFunc() implementation */
ResponseByteCount += 4;
return ResponseByteCount;
}

uint16_t EM4233AppProcess(uint8_t* FrameBuf, uint16_t FrameBytes)
{
uint16_t ResponseByteCount = ISO15693_APP_NO_RESPONSE;
uint8_t Uid[ActiveConfiguration.UidSize];
EM4233GetUid(Uid);

if ((FrameBytes < ISO15693_MIN_FRAME_SIZE) || !ISO15693CheckCRC(FrameBuf, FrameBytes - ISO15693_CRC16_SIZE))
/* malformed frame */
return ResponseByteCount;

if (FrameBuf[ISO15693_REQ_ADDR_CMD] == ISO15693_CMD_SELECT) {
/* Select has its own path before PrepareFrame because we have to change the variable State
* from Select to Ready if "Select" cmd is addressed to another tag.
Expand All @@ -540,6 +606,7 @@ uint16_t EM4233AppProcess(uint8_t* FrameBuf, uint16_t FrameBytes)
return ISO15693_APP_NO_RESPONSE;

if (State == STATE_READY || State == STATE_SELECTED) {

if (*FrameInfo.Command == ISO15693_CMD_INVENTORY) {
if (FrameInfo.ParamLen == 0)
return ISO15693_APP_NO_RESPONSE; /* malformed: not enough or too much data */
Expand Down Expand Up @@ -588,25 +655,30 @@ uint16_t EM4233AppProcess(uint8_t* FrameBuf, uint16_t FrameBytes)
ResponseByteCount = EM4233_Reset_To_Ready (FrameBuf, FrameBytes);

} else if (*FrameInfo.Command == EM4233_CMD_LOGIN) {
ResponseByteCount = EM4233_Login(FrameBuf, FrameBytes, Uid);
ResponseByteCount = EM4233_Login(FrameBuf, FrameBytes);

} else if (*FrameInfo.Command == ISO15693_CMD_INVENTORY) {
/* This is just a placeholder to avoid falling in the following else */
} else if (*FrameInfo.Command == EM4233_CMD_AUTH1) {
ResponseByteCount = EM4233_Auth1(FrameBuf, FrameBytes);

} else if (*FrameInfo.Command == EM4233_CMD_AUTH2) {
ResponseByteCount = EM4233_Auth2(FrameBuf, FrameBytes);

} else {
/* EM4233 does not respond to non existing commands */
// FrameBuf[ISO15693_ADDR_FLAGS] = ISO15693_RES_FLAG_ERROR;
// FrameBuf[ISO15693_RES_ADDR_PARAM] = ISO15693_RES_ERR_NOT_SUPP;
// ResponseByteCount = 2;
if (FrameInfo.Addressed) {
FrameBuf[ISO15693_ADDR_FLAGS] = ISO15693_RES_FLAG_ERROR;
FrameBuf[ISO15693_RES_ADDR_PARAM] = ISO15693_RES_ERR_NOT_SUPP;
ResponseByteCount = 2;
} /* EM4233 respond with error flag only to addressed commands */
}

} else if (State == STATE_QUIET) {
if (*FrameInfo.Command == ISO15693_CMD_RESET_TO_READY) {
ResponseByteCount = EM4233_Reset_To_Ready (FrameBuf, FrameBytes);
}
}

if (ResponseByteCount > 0) {
/* There is data to be sent. Append CRC */
/* There is data to send. Append CRC */
ISO15693AppendCRC(FrameBuf, ResponseByteCount);
ResponseByteCount += ISO15693_CRC16_SIZE;
}
Expand Down
9 changes: 9 additions & 0 deletions Firmware/Chameleon-Mini/Application/EM4233.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,17 @@
#define EM4233_CMD_FST_READ_BLKS 0xC3

/* Proprietary command code */
#define EM4233_CMD_AUTH1 0xE0
#define EM4233_CMD_AUTH2 0xE1
#define EM4233_CMD_LOGIN 0xE4

/* Compile time switch */
/* EM4233_LOGIN_YES_CARD has to be uncommented if you want your emulated card
* to accept any given password from the reader when a Login request (E4) is issued.
* It is expecially useful when analyzing an unknown system and you want to fool a reader
* into thiking you are using the original tag without actually knowing the password.
*/
#define EM4233_LOGIN_YES_CARD

void EM4233AppInit(void);
void EM4233AppReset(void);
Expand Down
9 changes: 2 additions & 7 deletions Firmware/Chameleon-Mini/Application/ISO15693-A.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,8 @@ bool ISO15693CheckCRC(void* FrameBuf, uint16_t FrameBufSize)
/*
* ISO15693PrepareFrame
*
* This function validates frame lenght and sets pointers in 'frame' struct
* to relevant byte(s) of 'FrameBuf'. Also sets frame.addressed as true if
* the command is addressed
* This function sets pointers in 'frame' struct to relevant byte(s) of 'FrameBuf'.
* Also sets frame.Addressed as true if the command is addressed, same goes for frame.Selected
*
* Returns:
* - true: Frame is valid and a response is needed
Expand All @@ -67,10 +66,6 @@ bool ISO15693CheckCRC(void* FrameBuf, uint16_t FrameBufSize)
*/
bool ISO15693PrepareFrame(uint8_t* FrameBuf, uint16_t FrameBytes, CurrentFrame* FrameStruct, uint8_t IsSelected, uint8_t* MyUid, uint8_t MyAFI)
{
if ((FrameBytes < ISO15693_MIN_FRAME_SIZE) || !ISO15693CheckCRC(FrameBuf, FrameBytes - ISO15693_CRC16_SIZE))
/* malformed frame */
return false;

/* following declarations are not dependent on addressed/unaddressed state */
FrameStruct -> Flags = &FrameBuf[ISO15693_ADDR_FLAGS];
FrameStruct -> Command = &FrameBuf[ISO15693_REQ_ADDR_CMD];
Expand Down

0 comments on commit 9ae3aec

Please sign in to comment.