diff --git a/Firmware/ChameleonMini/Application/ISO14443-3A.c b/Firmware/ChameleonMini/Application/ISO14443-3A.c index 37f9a3e..4eaff70 100644 --- a/Firmware/ChameleonMini/Application/ISO14443-3A.c +++ b/Firmware/ChameleonMini/Application/ISO14443-3A.c @@ -89,9 +89,92 @@ bool ISO14443ACheckCRCA(const void* Buffer, uint16_t ByteCount) } */ -/* Coded in H to allow exportable inlining -INLINE bool ISO14443ASelect(void* Buffer, uint16_t* BitCount, uint8_t* UidCL, uint8_t SAKValue); -INLINE bool ISO14443AWakeUp(void* Buffer, uint16_t* BitCount, uint16_t ATQAValue, bool FromHalt); -INLINE bool ISO14443AIsWakeUp(uint8_t* Buffer, bool FromHalt); -INLINE void ISO14443ASetWakeUpResponse(uint8_t* Buffer, uint16_t ATQAValue); -*/ +bool ISO14443AIsWakeUp(uint8_t* Buffer, bool FromHalt) { + return ( ((!FromHalt) && (Buffer[0] == ISO14443A_CMD_REQA)) + || (Buffer[0] == ISO14443A_CMD_WUPA) ); +} + +void ISO14443ASetWakeUpResponse(uint8_t* Buffer, uint16_t ATQAValue) { + Buffer[0] = (ATQAValue >> 0) & 0x00FF; + Buffer[1] = (ATQAValue >> 8) & 0x00FF; +} + +bool ISO14443AWakeUp(void* Buffer, uint16_t* BitCount, uint16_t ATQAValue, bool FromHalt) +{ + bool ret = false; + uint8_t* DataPtr = (uint8_t*) Buffer; + + if ( ISO14443AIsWakeUp(DataPtr, FromHalt) ) { + ISO14443ASetWakeUpResponse(DataPtr, ATQAValue); + + *BitCount = ISO14443A_ATQA_FRAME_SIZE; + + ret = true; + } else { + *BitCount = 0; + } + return ret; +} + +bool ISO14443ASelect(void* Buffer, uint16_t* BitCount, uint8_t* UidCL, uint8_t SAKValue) +{ + bool ret = false; + uint8_t* DataPtr = (uint8_t*) Buffer; + uint8_t NVB = DataPtr[1]; + //uint8_t CollisionByteCount = (NVB >> 4) & 0x0F; + //uint8_t CollisionBitCount = (NVB >> 0) & 0x0F; + + switch (NVB) { + case ISO14443A_NVB_AC_START: + /* Start of anticollision procedure. + * Send whole UID CLn + BCC */ + DataPtr[0] = UidCL[0]; + DataPtr[1] = UidCL[1]; + DataPtr[2] = UidCL[2]; + DataPtr[3] = UidCL[3]; + DataPtr[4] = ISO14443A_CALC_BCC(DataPtr); + + *BitCount = ISO14443A_CL_FRAME_SIZE; + + break; + + case ISO14443A_NVB_AC_END: + /* End of anticollision procedure. + * Send SAK CLn if we are selected. */ + if ( (DataPtr[2] == UidCL[0]) && + (DataPtr[3] == UidCL[1]) && + (DataPtr[4] == UidCL[2]) && + (DataPtr[5] == UidCL[3]) ) { + + DataPtr[0] = SAKValue; + ISO14443AAppendCRCA(Buffer, 1); + + *BitCount = ISO14443A_SAK_FRAME_SIZE; + ret = true; + } else { + /* We have not been selected. Don't send anything. */ + *BitCount = 0; + } + break; + default: + { + uint8_t CollisionBitCount = NVB & 0x0f; + if (CollisionBitCount == 0) { + /* Full-byte anticollision frame supports */ + uint8_t CollisionByteCount = ((NVB >> 4) & 0x0f) - 2; + /* Check for our UID is selecting */ + if (memcmp(UidCL, &DataPtr[2], CollisionByteCount) != 0) { + *BitCount = 0; + } + memcpy(DataPtr, &UidCL[CollisionByteCount], 4 - CollisionByteCount); + /* Calc original BCC */ + DataPtr[4 - CollisionByteCount] = ISO14443A_CALC_BCC(UidCL); + *BitCount = (5 - CollisionByteCount) * BITS_PER_BYTE; + } else { + /* Partial-byte anticollision frame not supported */ + *BitCount = 0; + } + } + } + return ret; +} diff --git a/Firmware/ChameleonMini/Application/ISO14443-3A.h b/Firmware/ChameleonMini/Application/ISO14443-3A.h index 97825ef..5f5891d 100644 --- a/Firmware/ChameleonMini/Application/ISO14443-3A.h +++ b/Firmware/ChameleonMini/Application/ISO14443-3A.h @@ -47,105 +47,9 @@ void ISO14443AAppendCRCA(void* Buffer, uint16_t ByteCount); bool ISO14443ACheckCRCA(const void* Buffer, uint16_t ByteCount); - -/* Coded here to allow exportable inlining */ -INLINE bool ISO14443ASelect(void* Buffer, uint16_t* BitCount, uint8_t* UidCL, uint8_t SAKValue); -INLINE bool ISO14443AWakeUp(void* Buffer, uint16_t* BitCount, uint16_t ATQAValue, bool FromHalt); -INLINE bool ISO14443AIsWakeUp(uint8_t* Buffer, bool FromHalt); -INLINE void ISO14443ASetWakeUpResponse(uint8_t* Buffer, uint16_t ATQAValue); - -INLINE -bool ISO14443ASelect(void* Buffer, uint16_t* BitCount, uint8_t* UidCL, uint8_t SAKValue) -{ - bool ret = false; - uint8_t* DataPtr = (uint8_t*) Buffer; - uint8_t NVB = DataPtr[1]; - //uint8_t CollisionByteCount = (NVB >> 4) & 0x0F; - //uint8_t CollisionBitCount = (NVB >> 0) & 0x0F; - - switch (NVB) { - case ISO14443A_NVB_AC_START: - /* Start of anticollision procedure. - * Send whole UID CLn + BCC */ - DataPtr[0] = UidCL[0]; - DataPtr[1] = UidCL[1]; - DataPtr[2] = UidCL[2]; - DataPtr[3] = UidCL[3]; - DataPtr[4] = ISO14443A_CALC_BCC(DataPtr); - - *BitCount = ISO14443A_CL_FRAME_SIZE; - - break; - - case ISO14443A_NVB_AC_END: - /* End of anticollision procedure. - * Send SAK CLn if we are selected. */ - if ( (DataPtr[2] == UidCL[0]) && - (DataPtr[3] == UidCL[1]) && - (DataPtr[4] == UidCL[2]) && - (DataPtr[5] == UidCL[3]) ) { - - DataPtr[0] = SAKValue; - ISO14443AAppendCRCA(Buffer, 1); - - *BitCount = ISO14443A_SAK_FRAME_SIZE; - ret = true; - } else { - /* We have not been selected. Don't send anything. */ - *BitCount = 0; - } - break; - default: - { - uint8_t CollisionBitCount = NVB & 0x0f; - if (CollisionBitCount == 0) { - /* Full-byte anticollision frame supports */ - uint8_t CollisionByteCount = ((NVB >> 4) & 0x0f) - 2; - /* Check for our UID is selecting */ - if (memcmp(UidCL, &DataPtr[2], CollisionByteCount) != 0) { - *BitCount = 0; - } - memcpy(DataPtr, &UidCL[CollisionByteCount], 4 - CollisionByteCount); - /* Calc original BCC */ - DataPtr[4 - CollisionByteCount] = ISO14443A_CALC_BCC(UidCL); - *BitCount = (5 - CollisionByteCount) * BITS_PER_BYTE; - } else { - /* Partial-byte anticollision frame not supported */ - *BitCount = 0; - } - } - } - return ret; -} - -INLINE -bool ISO14443AIsWakeUp(uint8_t* Buffer, bool FromHalt) { - return ( ((!FromHalt) && (Buffer[0] == ISO14443A_CMD_REQA)) - || (Buffer[0] == ISO14443A_CMD_WUPA) ); -} - -INLINE -void ISO14443ASetWakeUpResponse(uint8_t* Buffer, uint16_t ATQAValue) { - Buffer[0] = (ATQAValue >> 0) & 0x00FF; - Buffer[1] = (ATQAValue >> 8) & 0x00FF; -} - -INLINE -bool ISO14443AWakeUp(void* Buffer, uint16_t* BitCount, uint16_t ATQAValue, bool FromHalt) -{ - bool ret = false; - uint8_t* DataPtr = (uint8_t*) Buffer; - - if ( ISO14443AIsWakeUp(DataPtr, FromHalt) ) { - ISO14443ASetWakeUpResponse(DataPtr, ATQAValue); - - *BitCount = ISO14443A_ATQA_FRAME_SIZE; - - ret = true; - } else { - *BitCount = 0; - } - return ret; -} +bool ISO14443AIsWakeUp(uint8_t* Buffer, bool FromHalt); +void ISO14443ASetWakeUpResponse(uint8_t* Buffer, uint16_t ATQAValue); +bool ISO14443AWakeUp(void* Buffer, uint16_t* BitCount, uint16_t ATQAValue, bool FromHalt); +bool ISO14443ASelect(void* Buffer, uint16_t* BitCount, uint8_t* UidCL, uint8_t SAKValue); #endif diff --git a/Firmware/ChameleonMini/Application/MifareClassic.c b/Firmware/ChameleonMini/Application/MifareClassic.c index 62f399b..6b0884c 100644 --- a/Firmware/ChameleonMini/Application/MifareClassic.c +++ b/Firmware/ChameleonMini/Application/MifareClassic.c @@ -144,8 +144,7 @@ enum estate { STATE_IDLE, STATE_CHINESE_IDLE, STATE_CHINESE_WRITE, - STATE_READY1, - STATE_READY2, + STATE_READY, STATE_ACTIVE, STATE_AUTHING, STATE_AUTHED_IDLE, @@ -168,11 +167,15 @@ static uint8_t AccessAddress; static uint16_t CardATQAValue; static uint8_t CardSAKValue; static bool is7BytesUID = false; -/* To differentiate between IDLE and HALT, and to check for valid WUPA or REQA */ -static bool isFromHaltState = false; +/* To differentiate between IDLE and HALT starts */ +static bool isFromHaltChain = false; +/* To check if previous step of any cascading sequence has passed */ +static bool isCascadeStepOnePassed = false; /* To enable MF_DETECTION behavior */ static bool isDetectionEnabled = false; #ifdef CONFIG_MF_DETECTION_SUPPORT +static bool isDetectionCanaryWritten = false; +static uint8_t DetectionCanary[DETECTION_BLOCK0_CANARY_SIZE] = { DETECTION_BLOCK0_CANARY }; static uint8_t DetectionDataSave[DETECTION_BYTES_PER_SAVE] = {0}; static uint8_t DetectionAttemptsKeyA = 0; static uint8_t DetectionAttemptsKeyB = 0; @@ -272,111 +275,199 @@ INLINE void ValueToBlock(uint8_t* Block, uint32_t Value) { Block[11] = Block[3]; } - -void MifareClassicAppInit(uint16_t ATQA_4B, uint8_t SAK, bool is7B, bool isDetection) { +void MifareClassicAppInit(uint16_t ATQA_4B, uint8_t SAK, bool is7B) { State = STATE_IDLE; is7BytesUID = is7B; CardATQAValue = (is7BytesUID) ? (ATQA_4B | MFCLASSIC_7B_ATQA_MASK) : (ATQA_4B); CardSAKValue = SAK; - isFromHaltState = false; - isDetectionEnabled = isDetection; - if(isDetectionEnabled) { - uint8_t canary[DETECTION_BLOCK0_CANARY_SIZE] = { DETECTION_BLOCK0_CANARY }; - AppMemoryWrite(canary, DETECTION_BLOCK0_CANARY_ADDR, DETECTION_BLOCK0_CANARY_SIZE); - } + isFromHaltChain = false; + isCascadeStepOnePassed = false; } void MifareClassicAppInit1K(void) { MifareClassicAppInit( MFCLASSIC_1K_ATQA_VALUE, MFCLASSIC_1K_SAK_VALUE, - (ActiveConfiguration.UidSize == MFCLASSIC_UID_7B_SIZE), false ); + (ActiveConfiguration.UidSize == MFCLASSIC_UID_7B_SIZE) ); } void MifareClassicAppInit4K(void) { MifareClassicAppInit( MFCLASSIC_4K_ATQA_VALUE, MFCLASSIC_4K_SAK_VALUE, - (ActiveConfiguration.UidSize == MFCLASSIC_UID_7B_SIZE), false ); + (ActiveConfiguration.UidSize == MFCLASSIC_UID_7B_SIZE) ); } #ifdef CONFIG_MF_CLASSIC_MINI_SUPPORT -void MifareClassicAppInitMini(void) -{ - MifareClassicAppInit( MFCLASSIC_MINI_ATQA_VALUE, MFCLASSIC_MINI_SAK_VALUE, - false, false ); +void MifareClassicAppInitMini(void) { + MifareClassicAppInit(MFCLASSIC_MINI_ATQA_VALUE, MFCLASSIC_MINI_SAK_VALUE, false); } #endif #ifdef CONFIG_MF_DETECTION_SUPPORT void MifareClassicAppDetectionInit(void) { - MifareClassicAppInit( MFCLASSIC_1K_ATQA_VALUE, MFCLASSIC_1K_SAK_VALUE, - false, true ); + isDetectionEnabled = true; + DetectionAttemptsKeyA = 0; + DetectionAttemptsKeyB = 0; + MifareClassicAppInit(MFCLASSIC_1K_ATQA_VALUE, MFCLASSIC_1K_SAK_VALUE, false); } #endif -void MifareClassicAppReset(void) -{ +void MifareClassicAppReset(void) { State = STATE_IDLE; } -void MifareClassicAppTask(void) -{ +void MifareClassicAppTask(void) { } /* Handle a MFCLASSIC_CMD_HALT during main process, as can be raised in many states. -* Sets State, isFromHaltState, response buffer and returns response size */ -uint16_t mfcHandleHaltCommand(uint8_t* Buffer) { - uint16_t ret = MFCLASSIC_ACK_NAK_FRAME_SIZE; - /* Halts the tag. According to the ISO14443, the second byte is supposed to be 0 */ - if (Buffer[1] == 0) { +* Sets State, response buffer and response size. Returns if valid HALT. */ +bool mfcHandleHaltCommand(uint8_t * Buffer, uint16_t * RetValue) { + bool ret = false; + /* Halts the tag. According to the ISO 14443-3, the second byte is supposed to be 0 */ + if ( (Buffer[0] == MFCLASSIC_CMD_HALT) && (Buffer[1] == 0) ) { + /* If we get a buggy HALT, we fallback to IDLE or HALT depending on origin */ + State = isFromHaltChain ? STATE_HALT : STATE_IDLE; + ret = true; + /* If valid HALT with CRC passed */ if (ISO14443ACheckCRCA(Buffer, MFCLASSIC_CMD_HALT_FRAME_SIZE)) { - /* According to ISO14443, we must not send anything to ACK */ + /* According to ISO 14443-3, we must not send anything to ACK */ State = STATE_HALT; - isFromHaltState = true; - ret = ISO14443A_APP_NO_RESPONSE; + *RetValue = ISO14443A_APP_NO_RESPONSE; } else { Buffer[0] = MFCLASSIC_NAK_TBOK_CRCKO; + *RetValue = MFCLASSIC_ACK_NAK_FRAME_SIZE; } - } else { - Buffer[0] = MFCLASSIC_NAK_TBOK_OPKO; } return ret; } /* Handle a WUPA or REQA during main process, as can be raised in all states. * Sets State, response buffer and response size. Returns if WUPA/REQA received. */ -uint16_t mfcHandleWUPCommand(uint8_t* Buffer, uint16_t BitCount, uint16_t ATQAValue, uint16_t * RetValue) { +bool mfcHandleWUPCommand(bool isFromHaltState, uint8_t * Buffer, uint16_t BitCount, uint16_t ATQAValue, uint16_t * RetValue) { bool ret = false; /* If we have been awoken */ if ( (BitCount == MFCLASSIC_CMD_WUPA_BITCOUNT) && ISO14443AIsWakeUp(Buffer, isFromHaltState) ) { /* Set response buffer */ ISO14443ASetWakeUpResponse(Buffer, ATQAValue); - /* We should go to HALT or IDLE depending on previous state if we are - * in a ACTIVE, READY1 or READY2 */ - if ( (State == STATE_READY1) || (State == STATE_READY2) || (State == STATE_ACTIVE) ) { - State = isFromHaltState ? STATE_HALT : STATE_IDLE; - *RetValue = ISO14443A_APP_NO_RESPONSE; - } else { + ret = true; + /* If valid WUPA or REQA, go to READY state */ + if ( (State == STATE_IDLE) || (State == STATE_HALT) ) { /* Not implemented yet. AccessAddress = MFCLASSIC_MEM_INVALID_ADDRESS; */ - State = STATE_READY1; + State = STATE_READY; *RetValue = ISO14443A_ATQA_FRAME_SIZE; + /* Else we go back to IDLE or HALT, depending on where + * we come from, as per ISO 14443-3 */ + } else { + State = isFromHaltChain ? STATE_HALT : STATE_IDLE; + *RetValue = ISO14443A_APP_NO_RESPONSE; } - ret = true; } return ret; } +/* Handle an authentication request. +* Sets State, response buffer and response size. */ +void mfcHandleAuthenticationRequest(bool isNested, uint8_t * Buffer, uint16_t * RetValue) { + uint8_t SectorAddress; + uint8_t Key[MFCLASSIC_MEM_KEY_SIZE]; + uint8_t Uid[MFCLASSIC_UID_SIZE]; + uint8_t KeyOffset = (Buffer[0] == MFCLASSIC_CMD_AUTH_A) ? MFCLASSIC_MEM_KEY_A_OFFSET : MFCLASSIC_MEM_KEY_B_OFFSET; + uint16_t KeyAddress; + /* Save Nonce in detection mode */ + if(isDetectionEnabled && !isNested) { +#ifdef CONFIG_MF_DETECTION_SUPPORT + memset(DetectionDataSave, 0x00, DETECTION_BYTES_PER_SAVE); + // Save reader's auth phase 1: KEY type (A or B), and sector number + memcpy(DetectionDataSave, Buffer, DETECTION_READER_AUTH_P1_SIZE); + // Set selected key to be the DETECTION canary + KeyAddress = DETECTION_BLOCK0_CANARY_ADDR; +#endif + /* Set key address and loads it */ + } else { + /* Fix for MFClassic 4k cards */ + if(Buffer[1] >= 128) { + SectorAddress = Buffer[1] & MFCLASSIC_MEM_BIGSECTOR_ADDR_MASK; + KeyOffset += MFCLASSIC_MEM_KEY_BIGSECTOR_OFFSET; + } else { + SectorAddress = Buffer[1] & MFCLASSIC_MEM_SECTOR_ADDR_MASK; + } + KeyAddress = (uint16_t) SectorAddress * MFCLASSIC_MEM_BYTES_PER_BLOCK + KeyOffset; + } + AppMemoryRead(Key, KeyAddress, MFCLASSIC_MEM_KEY_SIZE); + /* Load UID */ + if (is7BytesUID) { + AppMemoryRead(Uid, MFCLASSIC_MEM_UID_CL2_ADDRESS, MFCLASSIC_MEM_UID_CL2_SIZE); + } else { + AppMemoryRead(Uid, MFCLASSIC_MEM_UID_CL1_ADDRESS, MFCLASSIC_MEM_UID_CL1_SIZE); + } + /* Proceed with nested or regular authent */ + if(isNested) { + uint8_t CardNonce[MFCLASSIC_MEM_NONCE_SIZE] = {0x01}; + uint8_t CardNonceParity[MFCLASSIC_MEM_NONCE_SIZE]; + /* Precalculate the reader response from card-nonce */ + memcpy(ReaderResponse, CardNonce, MFCLASSIC_MEM_NONCE_SIZE); + Crypto1PRNG(ReaderResponse, 64); + /* Precalculate our response from the reader response */ + memcpy(CardResponse, ReaderResponse, MFCLASSIC_MEM_NONCE_SIZE); + Crypto1PRNG(CardResponse, 32); + /* Setup crypto1 cipher for nested authentication. */ + Crypto1Setup(Key, Uid, CardNonce, CardNonceParity); + for (uint8_t i=0; i= 128) { - SectorAddress = Buffer[1] & MFCLASSIC_MEM_BIGSECTOR_ADDR_MASK; - KeyOffset += MFCLASSIC_MEM_KEY_BIGSECTOR_OFFSET; - } else { - SectorAddress = Buffer[1] & MFCLASSIC_MEM_SECTOR_ADDR_MASK; - } - uint16_t KeyAddress = (uint16_t) SectorAddress * MFCLASSIC_MEM_BYTES_PER_BLOCK + KeyOffset; - uint8_t Key[MFCLASSIC_MEM_KEY_SIZE]; - uint8_t Uid[MFCLASSIC_UID_SIZE]; - uint8_t CardNonceSuccessor1[MFCLASSIC_MEM_NONCE_SIZE] = {0x63, 0xe5, 0xbc, 0xa7}; - uint8_t CardNonceSuccessor2[MFCLASSIC_MEM_NONCE_SIZE] = {0x99, 0x37, 0x30, 0xbd}; - /* Generate a random nonce and read UID and key from memory */ - //RandomGetBuffer(CardNonce, sizeof(CardNonce)); - if (is7BytesUID) { - AppMemoryRead(Uid, MFCLASSIC_MEM_UID_CL2_ADDRESS, MFCLASSIC_MEM_UID_CL2_SIZE); - } else { - AppMemoryRead(Uid, MFCLASSIC_MEM_UID_CL1_ADDRESS, MFCLASSIC_MEM_UID_CL1_SIZE); - } - AppMemoryRead(Key, KeyAddress, MFCLASSIC_MEM_KEY_SIZE); - /* Precalculate the reader response from card-nonce */ - memcpy(ReaderResponse, CardNonceSuccessor1, MFCLASSIC_MEM_NONCE_SIZE); - /* Precalculate our response from the reader response */ - memcpy(CardResponse, CardNonceSuccessor2, MFCLASSIC_MEM_NONCE_SIZE); - /* Setup crypto1 cipher. Discard in-place encrypted CardNonce. */ - Crypto1Setup(Key, Uid, CardNonce, NULL); - } - /* Respond with the random card nonce and expect further authentication - * form the reader in the next frame. */ - memcpy(Buffer, CardNonce, MFCLASSIC_MEM_NONCE_SIZE); - State = STATE_AUTHING; - retSize = MFCLASSIC_CMD_AUTH_RB_FRAME_SIZE * BITS_PER_BYTE; + mfcHandleAuthenticationRequest(false, Buffer, &retSize); } else { Buffer[0] = MFCLASSIC_NAK_TBOK_CRCKO; retSize = MFCLASSIC_ACK_NAK_FRAME_SIZE; } - } else if ( (Buffer[0] == MFCLASSIC_CMD_READ) || - (Buffer[0] == MFCLASSIC_CMD_WRITE) || - (Buffer[0] == MFCLASSIC_CMD_DECREMENT) || - (Buffer[0] == MFCLASSIC_CMD_INCREMENT) || - (Buffer[0] == MFCLASSIC_CMD_RESTORE) || - (Buffer[0] == MFCLASSIC_CMD_TRANSFER) ) { - State = STATE_IDLE; + } else { Buffer[0] = MFCLASSIC_NAK_TBKO_OPKO; retSize = MFCLASSIC_ACK_NAK_FRAME_SIZE; - } else { - /* Unknown command. Enter HALT state. */ - State = STATE_HALT; } break; /* End of state ACTIVE */ case STATE_AUTHING: if(isDetectionEnabled) { +#ifdef CONFIG_MF_DETECTION_SUPPORT // Save reader's auth phase 2 answer to our nonce from STATE_ACTIVE memcpy(DetectionDataSave+DETECTION_SAVE_P2_OFFSET, Buffer, DETECTION_READER_AUTH_P2_SIZE); // Align data storage in each KEYX dedicated memory space, and iterate counters - uint16_t memSaveAddr = (uint16_t)DETECTION_MEM_BLOCK0_SIZE; + uint8_t memSaveAddr; if (DetectionDataSave[DETECTION_KEYX_SAVE_IDX] == MFCLASSIC_CMD_AUTH_A) { - memSaveAddr += ((uint16_t)DetectionAttemptsKeyA * DETECTION_BYTES_PER_SAVE); + memSaveAddr = (DETECTION_MEM_DATA_START_ADDR + (DetectionAttemptsKeyA * DETECTION_BYTES_PER_SAVE)); DetectionAttemptsKeyA++; DetectionAttemptsKeyA = DetectionAttemptsKeyA % DETECTION_MEM_MAX_KEYX_SAVES; } else { - memSaveAddr += (DETECTION_MEM_KEYX_SEPARATOR_OFFSET + ((uint16_t)DetectionAttemptsKeyB * DETECTION_BYTES_PER_SAVE)); + memSaveAddr = (DETECTION_MEM_KEYX_SEPARATOR_OFFSET + (DetectionAttemptsKeyB * DETECTION_BYTES_PER_SAVE)); DetectionAttemptsKeyB++; DetectionAttemptsKeyB = DetectionAttemptsKeyB % DETECTION_MEM_MAX_KEYX_SAVES; } // Write to app memory + if(!isDetectionCanaryWritten) { + AppMemoryWrite(DetectionCanary, DETECTION_BLOCK0_CANARY_ADDR, DETECTION_BLOCK0_CANARY_SIZE); + isDetectionCanaryWritten = true; + } AppMemoryWrite(DetectionDataSave, memSaveAddr, DETECTION_BYTES_PER_SAVE); - // Rage quit - State = STATE_HALT; + State = STATE_ACTIVE; +#endif } else { /* Reader delivers an encrypted nonce. We use it * to setup the crypto1 LFSR in nonlinear feedback mode. * Furthermore it delivers an encrypted answer. Decrypt and check it */ Crypto1Auth(&Buffer[0]); - for (uint8_t i=0; i<4; i++) { - Buffer[i+4] ^= Crypto1Byte(); - } - if ((Buffer[4] == ReaderResponse[0]) && - (Buffer[5] == ReaderResponse[1]) && - (Buffer[6] == ReaderResponse[2]) && - (Buffer[7] == ReaderResponse[3])) { + mfcDecryptBuffer(&Buffer[MFCLASSIC_MEM_NONCE_SIZE], MFCLASSIC_MEM_NONCE_SIZE); + if ( !memcmp(&Buffer[MFCLASSIC_MEM_NONCE_SIZE], ReaderResponse, MFCLASSIC_MEM_NONCE_SIZE) ) { /* Reader is authenticated. Encrypt the precalculated card response * and generate the parity bits. */ - for (uint8_t i=0; i= 128) { - SectorAddress = Buffer[1] & MFCLASSIC_MEM_BIGSECTOR_ADDR_MASK; - KeyOffset += MFCLASSIC_MEM_KEY_BIGSECTOR_OFFSET; - } else { - SectorAddress = Buffer[1] & MFCLASSIC_MEM_SECTOR_ADDR_MASK; - } - uint16_t KeyAddress = (uint16_t) SectorAddress * MFCLASSIC_MEM_BYTES_PER_BLOCK + KeyOffset; - uint8_t Key[MFCLASSIC_MEM_KEY_SIZE]; - uint8_t Uid[MFCLASSIC_UID_SIZE]; - uint8_t CardNonce[MFCLASSIC_MEM_NONCE_SIZE] = {0x01}; - uint8_t CardNonceParity[MFCLASSIC_MEM_NONCE_SIZE]; - /* Generate a random nonce and read UID and key from memory */ - //RandomGetBuffer(CardNonce, sizeof(CardNonce)); - if (is7BytesUID) { - AppMemoryRead(Uid, MFCLASSIC_MEM_UID_CL2_ADDRESS, MFCLASSIC_MEM_UID_CL2_SIZE); - } else { - AppMemoryRead(Uid, MFCLASSIC_MEM_UID_CL1_ADDRESS, MFCLASSIC_MEM_UID_CL1_SIZE); - } - AppMemoryRead(Key, KeyAddress, MFCLASSIC_MEM_KEY_SIZE); - /* Precalculate the reader response from card-nonce */ - memcpy(ReaderResponse, CardNonce, MFCLASSIC_MEM_NONCE_SIZE); - Crypto1PRNG(ReaderResponse, 64); - /* Precalculate our response from the reader response */ - memcpy(CardResponse, ReaderResponse, MFCLASSIC_MEM_NONCE_SIZE); - Crypto1PRNG(CardResponse, 32); - /* Setup crypto1 cipher for nested authentication. */ - Crypto1Setup(Key, Uid, CardNonce, CardNonceParity); - for (uint8_t i=0; i