From f02a45b6ef67106efeb85b18cbaa4f233c9050a6 Mon Sep 17 00:00:00 2001 From: slaenger <55430878+slaenger@users.noreply.github.com> Date: Tue, 17 Sep 2019 10:24:23 +0200 Subject: [PATCH 1/5] --- .../Application/MifareUltralight.c | 173 +++++++++++++++++- .../Application/MifareUltralight.h | 8 + 2 files changed, 176 insertions(+), 5 deletions(-) diff --git a/Firmware/Chameleon-Mini/Application/MifareUltralight.c b/Firmware/Chameleon-Mini/Application/MifareUltralight.c index b381b93..ef4384d 100644 --- a/Firmware/Chameleon-Mini/Application/MifareUltralight.c +++ b/Firmware/Chameleon-Mini/Application/MifareUltralight.c @@ -7,8 +7,10 @@ #include "MifareUltralight.h" #include "ISO14443-3A.h" +#include "../CryptoAlgorithms/CryptoTDEA.h" #include "../Codec/ISO14443-2A.h" #include "../Memory.h" +#include "../Random.h" #define ATQA_VALUE 0x0044 @@ -29,6 +31,10 @@ /* 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 */ @@ -47,6 +53,9 @@ #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 @@ -82,6 +91,7 @@ static enum { UL_EV0, + UL_C, UL_EV1, } Flavor; @@ -91,7 +101,8 @@ static enum { STATE_READY1, STATE_READY2, STATE_ACTIVE, - STATE_COMPAT_WRITE + STATE_COMPAT_WRITE, + STATE_AUTH } State; static bool FromHalt = false; @@ -101,6 +112,44 @@ 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) { @@ -109,6 +158,30 @@ static void AppInitCommon(void) 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) { @@ -152,7 +225,11 @@ void MifareUltralightAppReset(void) { State = STATE_IDLE; } - +void MifareUltralightCAppReset(void) +{ + Authenticated = false; + State = STATE_IDLE; +} void MifareUltralightAppTask(void) { @@ -161,7 +238,7 @@ void MifareUltralightAppTask(void) static bool VerifyAuthentication(uint8_t PageAddress) { /* No authentication for EV0 cards; always pass */ - if (Flavor < UL_EV1) { + if (Flavor < UL_C) { return true; } /* If authenticated, no verification needed */ @@ -172,6 +249,26 @@ static bool VerifyAuthentication(uint8_t PageAddress) 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 */ @@ -203,6 +300,18 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount) /* 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; @@ -216,10 +325,11 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount) uint8_t PageLimit; uint8_t Offset; /* For EV1+ cards, ensure the wraparound is at the first protected page */ - if (Flavor >= UL_EV1 && ReadAccessProtected && !Authenticated) { + if (Flavor >= UL_C && ReadAccessProtected && !Authenticated) { PageLimit = FirstAuthenticatedPage; } else { - PageLimit = PageCount; + 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) { @@ -242,6 +352,18 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount) /* 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; @@ -291,6 +413,19 @@ static uint16_t AppProcess(uint8_t* const Buffer, uint16_t ByteCount) 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) { @@ -505,6 +640,34 @@ uint16_t MifareUltralightAppProcess(uint8_t* Buffer, uint16_t BitCount) } 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; diff --git a/Firmware/Chameleon-Mini/Application/MifareUltralight.h b/Firmware/Chameleon-Mini/Application/MifareUltralight.h index bae7a38..ee82f3d 100644 --- a/Firmware/Chameleon-Mini/Application/MifareUltralight.h +++ b/Firmware/Chameleon-Mini/Application/MifareUltralight.h @@ -11,6 +11,11 @@ #include "Application.h" #include "ISO14443-3A.h" +#define MIFARE_ULTRALIGHTC_UID_SIZE ISO14443A_UID_SIZE_DOUBLE +#define MIFARE_ULTRALIGHTC_PAGE_SIZE 4 +#define MIFARE_ULTRALIGHTC_PAGES 48 +#define MIFARE_ULTRALIGHTC_MEM_SIZE (MIFARE_ULTRALIGHTC_PAGES * MIFARE_ULTRALIGHTC_PAGE_SIZE) + #define MIFARE_ULTRALIGHT_UID_SIZE ISO14443A_UID_SIZE_DOUBLE #define MIFARE_ULTRALIGHT_PAGE_SIZE 4 #define MIFARE_ULTRALIGHT_PAGES 16 @@ -26,6 +31,9 @@ void MifareUltralightEV12AppInit(void); void MifareUltralightAppReset(void); void MifareUltralightAppTask(void); +void MifareUltralightCAppInit(void); +void MifareUltralightCAppReset(void); + uint16_t MifareUltralightAppProcess(uint8_t* Buffer, uint16_t BitCount); void MifareUltralightGetUid(ConfigurationUidType Uid); From 96ff627f48382f65fdc6a7f3036ce471cdd4f8e5 Mon Sep 17 00:00:00 2001 From: slaenger <55430878+slaenger@users.noreply.github.com> Date: Tue, 17 Sep 2019 10:25:14 +0200 Subject: [PATCH 2/5] Added Mifare Ultralight C support. Changed SettingsSetActiveById() to first MemoryRecall() before initializing Application. --- Firmware/Chameleon-Mini/Configuration.c | 17 +++++++++++++++++ Firmware/Chameleon-Mini/Configuration.h | 1 + Firmware/Chameleon-Mini/Settings.c | 5 +++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Firmware/Chameleon-Mini/Configuration.c b/Firmware/Chameleon-Mini/Configuration.c index cefa5f3..159e036 100644 --- a/Firmware/Chameleon-Mini/Configuration.c +++ b/Firmware/Chameleon-Mini/Configuration.c @@ -18,6 +18,7 @@ static const MapEntryType PROGMEM ConfigurationMap[] = { { .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" }, + {.Id = CONFIG_MF_ULTRALIGHT_C, .Text = "MF_ULTRALIGHT_C"}, #endif #ifdef CONFIG_MF_CLASSIC_MINI_4B_SUPPORT { .Id = CONFIG_MF_CLASSIC_MINI_4B, .Text = "MF_CLASSIC_MINI_4B" }, @@ -107,6 +108,22 @@ static const PROGMEM ConfigurationType ConfigurationTable[] = { .ReadOnly = false, .TagFamily = TAG_FAMILY_ISO14443A }, + [CONFIG_MF_ULTRALIGHT_C] ={ + .CodecInitFunc = ISO14443ACodecInit, + .CodecDeInitFunc = ISO14443ACodecDeInit, + .CodecTaskFunc = ISO14443ACodecTask, + .ApplicationInitFunc = MifareUltralightCAppInit, + .ApplicationResetFunc = MifareUltralightCAppReset, + .ApplicationTaskFunc = MifareUltralightAppTask, + .ApplicationTickFunc = ApplicationTickDummy, + .ApplicationProcessFunc = MifareUltralightAppProcess, + .ApplicationGetUidFunc = MifareUltralightGetUid, + .ApplicationSetUidFunc = MifareUltralightSetUid, + .UidSize = MIFARE_ULTRALIGHT_UID_SIZE, + .MemorySize = MIFARE_ULTRALIGHTC_MEM_SIZE, + .ReadOnly = false, + .TagFamily = TAG_FAMILY_ISO14443A + }, [CONFIG_MF_ULTRALIGHT_EV1_80B] = { .CodecInitFunc = ISO14443ACodecInit, .CodecDeInitFunc = ISO14443ACodecDeInit, diff --git a/Firmware/Chameleon-Mini/Configuration.h b/Firmware/Chameleon-Mini/Configuration.h index 230f8e7..7ab8540 100644 --- a/Firmware/Chameleon-Mini/Configuration.h +++ b/Firmware/Chameleon-Mini/Configuration.h @@ -22,6 +22,7 @@ typedef enum { #ifdef CONFIG_MF_ULTRALIGHT_SUPPORT CONFIG_MF_ULTRALIGHT, + CONFIG_MF_ULTRALIGHT_C, CONFIG_MF_ULTRALIGHT_EV1_80B, CONFIG_MF_ULTRALIGHT_EV1_164B, #endif diff --git a/Firmware/Chameleon-Mini/Settings.c b/Firmware/Chameleon-Mini/Settings.c index 5ea9174..767e06d 100644 --- a/Firmware/Chameleon-Mini/Settings.c +++ b/Firmware/Chameleon-Mini/Settings.c @@ -72,12 +72,13 @@ bool SettingsSetActiveById(uint8_t Setting) { GlobalSettings.ActiveSettingPtr = &GlobalSettings.Settings[SettingIdx]; + /* Recall new memory contents ( Moved this to allow for Access to new Memory in Application init())*/ + MemoryRecall(); + /* Settings have changed. Progress changes through system */ ConfigurationSetById(GlobalSettings.ActiveSettingPtr->Configuration); LogSetModeById(GlobalSettings.ActiveSettingPtr->LogMode); - /* Recall new memory contents */ - MemoryRecall(); SETTING_UPDATE(GlobalSettings.ActiveSettingIdx); SETTING_UPDATE(GlobalSettings.ActiveSettingPtr); From 4923dc9322fc604e1f7052fa8783f8a56f0fae18 Mon Sep 17 00:00:00 2001 From: slaenger <55430878+slaenger@users.noreply.github.com> Date: Tue, 17 Sep 2019 10:48:44 +0200 Subject: [PATCH 3/5] Added triple DES Implementation (from dev-zzo's DESFire branch). --- .../Chameleon-Mini/Application/CryptoTDEA.S | 659 ++++++++++++++++++ .../Chameleon-Mini/Application/CryptoTDEA.h | 113 +++ .../Application/MifareUltralight.c | 2 +- Firmware/Chameleon-Mini/Makefile | 2 +- 4 files changed, 774 insertions(+), 2 deletions(-) create mode 100644 Firmware/Chameleon-Mini/Application/CryptoTDEA.S create mode 100644 Firmware/Chameleon-Mini/Application/CryptoTDEA.h diff --git a/Firmware/Chameleon-Mini/Application/CryptoTDEA.S b/Firmware/Chameleon-Mini/Application/CryptoTDEA.S new file mode 100644 index 0000000..438844b --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/CryptoTDEA.S @@ -0,0 +1,659 @@ +; +; DEA related code is kept in this file. +; All data is handled big-endian style (MSByte first) in memory. +; + +.section .text + +; This routine loads the key and performs 16 rounds of DEA. +; +; Input: +; R31:R30 - A pointer to the 8-byte key (with parity bits), MSB first. +; R7:R0 - 8-byte input data block, LSB in R0. +; SREG:H - Set to decipher, clear to encipher. +; +; Returns: +; R7:R0 - Result of en/deciphering, LSB in R0 +_LoadKeyAndRunDEA: + ld r15, Z+ + ld r14, Z+ + ld r13, Z+ + ld r12, Z+ + ld r11, Z+ + ld r10, Z+ + ld r9, Z+ + ld r8, Z+ + des 0 + des 1 + des 2 + des 3 + des 4 + des 5 + des 6 + des 7 + des 8 + des 9 + des 10 + des 11 + des 12 + des 13 + des 14 + des 15 + ret + +; +; Triple DEA subroutines +; + +; This routine performs Triple DEA encryption (E-D-E) using keying option 1: K1, K2, K3. +; +; Input: +; R17:R16 - Key block pointer. +; R7:R0 - Input data, LSB in R0 +; +; Returns: +; R7:R0 - Result of enciphering, LSB in R0 +_Encrypt3KTDEA: + ; Reload Z with the key block pointer + movw r30, r16 + ; Encipher + clh + rcall _LoadKeyAndRunDEA + + ; Z now points to K2 + ; Decipher + seh + rcall _LoadKeyAndRunDEA + + ; Z now points to K3 + ; Encipher + clh + rjmp _LoadKeyAndRunDEA + +; This routine performs Triple DEA encryption (E-D-E) using keying option 2: K1, K2, K1. +; +; Input: +; R17:R16 - Key block pointer. +; R7:R0 - Input data, LSB in R0 +; +; Returns: +; R7:R0 - Result of enciphering, LSB in R0 +_Encrypt2KTDEA: + ; Reload Z with the key block pointer + movw r30, r16 + ; Encipher + clh + rcall _LoadKeyAndRunDEA + + ; Z now points to K2 + ; Decipher + seh + rcall _LoadKeyAndRunDEA + + ; Reload Z with the key block pointer + movw r30, r16 + ; Encipher + clh + rjmp _LoadKeyAndRunDEA + + + +; Input: +; R17:R16 - Key block pointer. +; R7:R0 - Input data, LSB in R0 +; +; Returns: +; R7:R0 - Result of enciphering, LSB in R0 +_EncryptDEA: + ; Reload Z with the key block pointer + movw r30, r16 + ; Encipher + clh + rjmp _LoadKeyAndRunDEA + + +; Input: +; R17:R16 - Key block pointer. +; R7:R0 - Input data, LSB in R0 +; +; Returns: +; R7:R0 - Result of enciphering, LSB in R0 +_DecryptDEA: + ; Reload Z with the key block pointer + movw r30, r16 + ; Encipher + seh + rjmp _LoadKeyAndRunDEA + + + +; This routine performs Triple DEA decryption (D-E-D) using keying option 1: K1, K2, K3. +; +; Input: +; R17:R16 - Key block pointer. +; R7:R0 - Input data, LSB in R0 +; +; Returns: +; R7:R0 - Result of enciphering, LSB in R0 +_Decrypt3KTDEA: + ; Reload Z with the key block pointer and adjust to point to K3 + movw r30, r16 + adiw r30, 16 + ; Decipher + seh + rcall _LoadKeyAndRunDEA + + ; Reload Z with the key block pointer and adjust to point to K2 + movw r30, r16 + adiw r30, 8 + ; Encipher + clh + rcall _LoadKeyAndRunDEA + + ; Reload Z with the key block pointer + movw r30, r16 + ; Decipher + seh + rjmp _LoadKeyAndRunDEA + +; This routine performs Triple DEA decryption (D-E-D) using keying option 2: K1, K2, K1. +; +; Input: +; R17:R16 - Key block pointer. +; R7:R0 - Input data, LSB in R0 +; +; Returns: +; R7:R0 - Result of enciphering, LSB in R0 +_Decrypt2KTDEA: + ; Reload Z with the key block pointer + movw r30, r16 + ; Decipher + seh + rcall _LoadKeyAndRunDEA + + ; Z now points to K2 + ; Encipher + clh + rcall _LoadKeyAndRunDEA + + ; Reload Z with the key block pointer + movw r30, r16 + ; Decipher + seh + rjmp _LoadKeyAndRunDEA + +; +; Common prologue and epilogue code +; + +_CommonEpilogue: + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop r7 + pop r6 + pop r5 + pop r4 + pop r3 + pop r2 + eor r1, r1 + ret + +; +; Triple DEA ECB Routines +; + +; This routine performs Triple DEA encryption using keying option 2: K1, K2, K1. +; +; Input: +; R25:R24 - Pointer to plaintext output buffer +; R23:R22 - Pointer to ciphertext input buffer +; R21:R20 - Key block pointer. +; +; Returns: +; Nothing. +.global CryptoDecrypt2KTDEA +CryptoDecrypt2KTDEA: + ; Preserve the clobbered regs + push r2 + push r3 + push r4 + push r5 + push r6 + push r7 + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + push r16 + push r17 + + ; Load the plaintext pointer to Z and fetch data + movw r30, r22 + ld r7, Z+ + ld r6, Z+ + ld r5, Z+ + ld r4, Z+ + ld r3, Z+ + ld r2, Z+ + ld r1, Z+ + ld r0, Z+ + ; Encrypt + movw r16, r20 + rcall _Decrypt2KTDEA + ; Store the ciphertext + movw r30, r24 + st Z+, r7 + st Z+, r6 + st Z+, r5 + st Z+, r4 + st Z+, r3 + st Z+, r2 + st Z+, r1 + st Z+, r0 + + ; Restore clobbered regs + pop r17 + pop r16 + ; Reuse epilogue code + rjmp _CommonEpilogue + + +; This routine performs Triple DEA encryption using keying option 2: K1, K2, K1. +; +; Input: +; R25:R24 - Pointer to plaintext input buffer +; R23:R22 - Pointer to ciphertext output buffer +; R21:R20 - Key block pointer. +; +; Returns: +; Nothing. +.global CryptoEncrypt2KTDEA +CryptoEncrypt2KTDEA: + ; Preserve the clobbered regs + push r2 + push r3 + push r4 + push r5 + push r6 + push r7 + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + push r16 + push r17 + + ; Load the plaintext pointer to Z and fetch data + movw r30, r24 + ld r7, Z+ + ld r6, Z+ + ld r5, Z+ + ld r4, Z+ + ld r3, Z+ + ld r2, Z+ + ld r1, Z+ + ld r0, Z+ + ; Encrypt + movw r16, r20 + rcall _Encrypt2KTDEA + ; Store the ciphertext + movw r30, r22 + st Z+, r7 + st Z+, r6 + st Z+, r5 + st Z+, r4 + st Z+, r3 + st Z+, r2 + st Z+, r1 + st Z+, r0 + + ; Restore clobbered regs + pop r17 + pop r16 + ; Reuse epilogue code + rjmp _CommonEpilogue + +; +; Triple DEA CBC Routines +; + +; This routine performs the CBC "send" mode chaining: C = E(P ^ IV); IV = C +; +; Input: +; R31:R30 - Cryptographic primitive pointer +; R25:R24 - Count of blocks. +; R23:R22 - Pointer to plaintext input buffer +; R21:R20 - Pointer to ciphertext output buffer +; R19:R18 - IV block pointer. +; R17:R16 - Key block pointer. +; +; Returns: +; Nothing. +; +_DEACBCSend: + ; Preserve the clobbered regs + push r2 + push r3 + push r4 + push r5 + push r6 + push r7 + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + push r28 + push r29 + + ; Load the plaintext pointer to Y + movw r28, r22 + ; Load the ciphertext pointer to X + movw r26, r20 + ; Store the crypto primitive pointer in r23:r22 + movw r22, r30 + + ; Load the IV pointer to Z + movw r30, r18 + ; Load the IV + ld r7, Z+ + ld r6, Z+ + ld r5, Z+ + ld r4, Z+ + ld r3, Z+ + ld r2, Z+ + ld r1, Z+ + ld r0, Z+ + +1: + ; Load the plaintext block + ld r15, Y+ + ld r14, Y+ + ld r13, Y+ + ld r12, Y+ + ld r11, Y+ + ld r10, Y+ + ld r9, Y+ + ld r8, Y+ + ; XOR the plaintext with the IV + eor r7, r15 + eor r6, r14 + eor r5, r13 + eor r4, r12 + eor r3, r11 + eor r2, r10 + eor r1, r9 + eor r0, r8 + + ; Call the primitive + movw r30, r22 + icall + + ; Store the ciphertext + ; It will be reused as the IV for the next block, if any + st X+, r7 + st X+, r6 + st X+, r5 + st X+, r4 + st X+, r3 + st X+, r2 + st X+, r1 + st X+, r0 + + ; Decrement the counter, repeat if more blocks. + sbiw r24, 1 + brne 1b + + ; Load the IV pointer to X + movw r26, r18 + ; Store the updated IV + st X+, r7 + st X+, r6 + st X+, r5 + st X+, r4 + st X+, r3 + st X+, r2 + st X+, r1 + st X+, r0 + + ; Restore clobbered regs + pop r29 + pop r28 + rjmp _CommonEpilogue + +; This routine performs the CBC "receive" mode chaining: C = E(P) ^ IV; IV = P +; +; Input: +; R31:R30 - Cryptographic primitive pointer +; R25:R24 - Count of blocks. +; R23:R22 - Pointer to plaintext input buffer +; R21:R20 - Pointer to ciphertext output buffer +; R19:R18 - IV block pointer. +; R17:R16 - Key block pointer. +; +; Returns: +; Nothing. +; +_DEACBCReceive: + ; Preserve the clobbered regs + push r2 + push r3 + push r4 + push r5 + push r6 + push r7 + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + push r28 + push r29 + + ; Load the plaintext pointer to Y + movw r28, r22 + ; Load the ciphertext pointer to X + movw r26, r20 + ; Store the crypto primitive pointer in r23:r22 + movw r22, r30 + +1: + ; Load the plaintext block + ldd r7, Y+0 + ldd r6, Y+1 + ldd r5, Y+2 + ldd r4, Y+3 + ldd r3, Y+4 + ldd r2, Y+5 + ldd r1, Y+6 + ldd r0, Y+7 + + ; Call the primitive + movw r30, r22 + icall + + ; Load the IV block + movw r30, r18 + ld r15, Z+ + ld r14, Z+ + ld r13, Z+ + ld r12, Z+ + ld r11, Z+ + ld r10, Z+ + ld r9, Z+ + ld r8, Z+ + ; XOR the ciphertext with the IV + eor r7, r15 + eor r6, r14 + eor r5, r13 + eor r4, r12 + eor r3, r11 + eor r2, r10 + eor r1, r9 + eor r0, r8 + ; Reload the plaintext block + ld r15, Y+ + ld r14, Y+ + ld r13, Y+ + ld r12, Y+ + ld r11, Y+ + ld r10, Y+ + ld r9, Y+ + ld r8, Y+ + ; Store the new IV before it gets potentially overwritten + movw r30, r18 + st Z+, r15 + st Z+, r14 + st Z+, r13 + st Z+, r12 + st Z+, r11 + st Z+, r10 + st Z+, r9 + st Z+, r8 + ; Store the ciphertext + st X+, r7 + st X+, r6 + st X+, r5 + st X+, r4 + st X+, r3 + st X+, r2 + st X+, r1 + st X+, r0 + + ; Decrement the counter, repeat if more blocks. + sbiw r24, 1 + brne 1b + + ; Restore clobbered regs + pop r29 + pop r28 + rjmp _CommonEpilogue + + +; This routine performs Triple DEA encryption in CBC mode using keying option 2: K1, K2, K1. +; The CBC is operated in the "send" mode: C = E(P ^ IV); IV = C +; +; Input: +; R25:R24 - Count of blocks. +; R23:R22 - Pointer to plaintext input buffer +; R21:R20 - Pointer to ciphertext output buffer +; R19:R18 - IV block pointer. +; R17:R16 - Key block pointer. +; +; Returns: +; Nothing. +.global CryptoEncrypt2KTDEA_CBCSend +CryptoEncrypt2KTDEA_CBCSend: + ldi r31, pm_hi8(_Encrypt2KTDEA) + ldi r30, pm_lo8(_Encrypt2KTDEA) + rjmp _DEACBCSend + +; This routine performs Triple DEA encryption in CBC mode using keying option 2: K1, K2, K1. +; The CBC is operated in the "receive" mode: C = E(P) ^ IV; IV = P +; +; Input: +; R25:R24 - Count of blocks. +; R23:R22 - Pointer to plaintext input buffer +; R21:R20 - Pointer to ciphertext output buffer +; R19:R18 - IV block pointer. +; R17:R16 - Key block pointer. +; +; Returns: +; Nothing. +.global CryptoEncrypt2KTDEA_CBCReceive +CryptoEncrypt2KTDEA_CBCReceive: + ldi r31, pm_hi8(_Encrypt2KTDEA) + ldi r30, pm_lo8(_Encrypt2KTDEA) + rjmp _DEACBCReceive + +; This routine performs Triple DEA decryption in CBC mode using keying option 2: K1, K2, K1. +; The CBC is operated in the "send" mode: C = E(P ^ IV); IV = C +; +; Input: +; R25:R24 - Count of blocks. +; R23:R22 - Pointer to plaintext input buffer +; R21:R20 - Pointer to ciphertext output buffer +; R19:R18 - IV block pointer. +; R17:R16 - Key block pointer. +; +; Returns: +; Nothing. +.global CryptoDecrypt2KTDEA_CBCSend +CryptoDecrypt2KTDEA_CBCSend: + ldi r31, pm_hi8(_Decrypt2KTDEA) + ldi r30, pm_lo8(_Decrypt2KTDEA) + rjmp _DEACBCSend + +; This routine performs Triple DEA decryption in CBC mode using keying option 2: K1, K2, K1. +; The CBC is operated in the "receive" mode: C = E(P) ^ IV; IV = P +; +; Input: +; R25:R24 - Count of blocks. +; R23:R22 - Pointer to plaintext input buffer +; R21:R20 - Pointer to ciphertext output buffer +; R19:R18 - IV block pointer. +; R17:R16 - Key block pointer. +; +; Returns: +; Nothing. +.global CryptoDecrypt2KTDEA_CBCReceive +CryptoDecrypt2KTDEA_CBCReceive: + ldi r31, pm_hi8(_Decrypt2KTDEA) + ldi r30, pm_lo8(_Decrypt2KTDEA) + rjmp _DEACBCReceive + +; This routine performs Triple DEA encryption in CBC mode using keying option 1: K1, K2, K3. +; The CBC is operated in the "send" mode: C = E(P ^ IV); IV = C +; +; Input: +; R25:R24 - Count of blocks. +; R23:R22 - Pointer to plaintext input buffer +; R21:R20 - Pointer to ciphertext output buffer +; R19:R18 - IV block pointer. +; R17:R16 - Key block pointer. +; +; Returns: +; Nothing. +.global CryptoEncrypt3KTDEA_CBCSend +CryptoEncrypt3KTDEA_CBCSend: + ldi r31, pm_hi8(_Encrypt3KTDEA) + ldi r30, pm_lo8(_Encrypt3KTDEA) + rjmp _DEACBCSend + +; This routine performs Triple DEA decryption in CBC mode using keying option 1: K1, K2, K3. +; The CBC is operated in the "receive" mode: C = E(P) ^ IV; IV = P +; +; Input: +; R25:R24 - Count of blocks. +; R23:R22 - Pointer to plaintext input buffer +; R21:R20 - Pointer to ciphertext output buffer +; R19:R18 - IV block pointer. +; R17:R16 - Key block pointer. +; +; Returns: +; Nothing. +.global CryptoEncrypt3KTDEA_CBCReceive +CryptoEncrypt3KTDEA_CBCReceive: + ldi r31, pm_hi8(_Decrypt3KTDEA) + ldi r30, pm_lo8(_Decrypt3KTDEA) + rjmp _DEACBCReceive diff --git a/Firmware/Chameleon-Mini/Application/CryptoTDEA.h b/Firmware/Chameleon-Mini/Application/CryptoTDEA.h new file mode 100644 index 0000000..7215bae --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/CryptoTDEA.h @@ -0,0 +1,113 @@ +/* + * CryptoDES.h + * + * Created on: 18.10.2016 + * Author: dev_zzo + */ + +#ifndef CRYPTODES_H_ +#define CRYPTODES_H_ + +#include +#include "../Common.h" + +/* Notes on cryptography in DESFire cards + +The EV0 was the first chip in the DESFire series. It makes use of the TDEA +encryption to secure the comms. It also employs CBC to make things more secure. + +CBC is used in two "modes": "send mode" and "receive mode". +- Sending data uses the same structure as conventional encryption with CBC + (XOR with the IV then apply block cipher) +- Receiving data uses the same structure as conventional decryption with CBC + (apply block cipher then XOR with the IV) + +Both operations employ TDEA encryption on the PICC's side and decryption on +PCD's side. + +*/ + +/* Key sizes, in bytes */ +#define CRYPTO_DES_KEY_SIZE 8 /* Bytes */ +#define CRYPTO_2KTDEA_KEY_SIZE (CRYPTO_DES_KEY_SIZE * 2) +#define CRYPTO_3KTDEA_KEY_SIZE (CRYPTO_DES_KEY_SIZE * 3) + +#define CRYPTO_DES_BLOCK_SIZE 8 /* Bytes */ + +/* Prototype the CBC function pointer in case anyone needs it */ +typedef void (*CryptoTDEACBCFuncType)(uint16_t Count, const void* Plaintext, void* Ciphertext, void *IV, const uint8_t* Keys); + +/** Performs the Triple DEA enciphering in ECB mode (single block) + * + * \param Plaintext Source buffer with plaintext + * \param Ciphertext Destination buffer to contain ciphertext + * \param Keys Key block pointer (CRYPTO_2KTDEA_KEY_SIZE) + */ +void CryptoEncrypt2KTDEA(const void* Plaintext, void* Ciphertext, const uint8_t* Keys); +void CryptoDecrypt2KTDEA(const void* Plaintext, void* Ciphertext, const uint8_t* Keys); + +/** Performs the 2-key Triple DES en/deciphering in the CBC "send" mode (xor-then-crypt) + * + * \param Count Block count, expected to be >= 1 + * \param Plaintext Source buffer with plaintext + * \param Ciphertext Destination buffer to contain ciphertext + * \param IV Initialization vector buffer, will be updated + * \param Keys Key block pointer (CRYPTO_2KTDEA_KEY_SIZE) + */ +void CryptoEncrypt2KTDEA_CBCSend(uint16_t Count, const void* Input, void* Output, void *IV, const uint8_t* Keys); +void CryptoDecrypt2KTDEA_CBCSend(uint16_t Count, const void* Input, void* Output, void *IV, const uint8_t* Keys); + +/** Performs the 2-key Triple DES en/deciphering in the CBC "receive" mode (crypt-then-xor) + * + * \param Count Block count, expected to be >= 1 + * \param Plaintext Source buffer with plaintext + * \param Ciphertext Destination buffer to contain ciphertext + * \param IV Initialization vector buffer, will be updated + * \param Keys Key block pointer (CRYPTO_2KTDEA_KEY_SIZE) + */ +void CryptoEncrypt2KTDEA_CBCReceive(uint16_t Count, const void* Input, void* Output, void *IV, const uint8_t* Keys); +void CryptoDecrypt2KTDEA_CBCReceive(uint16_t Count, const void* Input, void* Output, void *IV, const uint8_t* Keys); + + +/** Performs the DES en/deciphering in ECB mode + * + * \param Count Block count, expected to be >= 1 + * \param Plaintext Source buffer with plaintext + * \param Ciphertext Destination buffer to contain ciphertext + * \param IV Initialization vector buffer, will be updated + * \param Keys Key block pointer (CRYPTO_DES_KEY_SIZE) + */ +void CryptoEncryptDEA(const void* Plaintext, void* Ciphertext, const uint8_t* Key); +void CryptoDecryptDEA(const void* Plaintext, void* Ciphertext, const uint8_t* Key); + + +/** Performs the 3-key Triple DES en/deciphering in the CBC "send" or "receive" mode + * + * \param Count Block count, expected to be >= 1 + * \param Plaintext Source buffer with plaintext + * \param Ciphertext Destination buffer to contain ciphertext + * \param IV Initialization vector buffer, will be updated + * \param Keys Key block pointer (CRYPTO_3KTDEA_KEY_SIZE) + */ +void CryptoEncrypt3KTDEA_CBCSend(uint16_t Count, const void* Plaintext, void* Ciphertext, void *IV, const uint8_t* Keys); +void CryptoDecrypt3KTDEA_CBCReceive(uint16_t Count, const void* Plaintext, void* Ciphertext, void *IV, const uint8_t* Keys); + + +/** Applies padding to the data within the buffer + * + * \param Buffer Data buffer to pad + * \param BytesInBuffer How much data there is in the buffer already + * \param FirstPaddingBitSet Whether the very first bit (MSB) will be set or not + */ +INLINE void CryptoPaddingTDEA(uint8_t* Buffer, uint8_t BytesInBuffer, bool FirstPaddingBitSet) +{ + uint8_t PaddingByte = FirstPaddingBitSet << 7; + uint8_t i; + + for (i = BytesInBuffer; i < CRYPTO_DES_BLOCK_SIZE; ++i) { + Buffer[i] = PaddingByte; + PaddingByte = 0x00; + } +} + +#endif /* CRYPTODES_H_ */ diff --git a/Firmware/Chameleon-Mini/Application/MifareUltralight.c b/Firmware/Chameleon-Mini/Application/MifareUltralight.c index ef4384d..4fd982c 100644 --- a/Firmware/Chameleon-Mini/Application/MifareUltralight.c +++ b/Firmware/Chameleon-Mini/Application/MifareUltralight.c @@ -7,10 +7,10 @@ #include "MifareUltralight.h" #include "ISO14443-3A.h" -#include "../CryptoAlgorithms/CryptoTDEA.h" #include "../Codec/ISO14443-2A.h" #include "../Memory.h" #include "../Random.h" +#include "CryptoTDEA.h" #define ATQA_VALUE 0x0044 diff --git a/Firmware/Chameleon-Mini/Makefile b/Firmware/Chameleon-Mini/Makefile index 89d375b..47dbd29 100644 --- a/Firmware/Chameleon-Mini/Makefile +++ b/Firmware/Chameleon-Mini/Makefile @@ -100,7 +100,7 @@ OPTIMIZATION = s SRC += $(TARGET).c LUFADescriptors.c System.c Configuration.c Random.c Common.c Memory.c MemoryAsm.S Button.c Log.c Settings.c LED.c Map.c AntennaLevel.c SRC += Terminal/Terminal.c Terminal/Commands.c Terminal/XModem.c Terminal/CommandLine.c 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 +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 += $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS) From abb021fd5c76012890b2595d60685de9101c5380 Mon Sep 17 00:00:00 2001 From: slaenger <55430878+slaenger@users.noreply.github.com> Date: Tue, 17 Sep 2019 11:01:48 +0200 Subject: [PATCH 4/5] fixed indentation --- Firmware/Chameleon-Mini/Configuration.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/Chameleon-Mini/Configuration.h b/Firmware/Chameleon-Mini/Configuration.h index 7ab8540..19a3aa6 100644 --- a/Firmware/Chameleon-Mini/Configuration.h +++ b/Firmware/Chameleon-Mini/Configuration.h @@ -22,7 +22,7 @@ typedef enum { #ifdef CONFIG_MF_ULTRALIGHT_SUPPORT CONFIG_MF_ULTRALIGHT, - CONFIG_MF_ULTRALIGHT_C, + CONFIG_MF_ULTRALIGHT_C, CONFIG_MF_ULTRALIGHT_EV1_80B, CONFIG_MF_ULTRALIGHT_EV1_164B, #endif From b89ff710755c83409313957386fcb1bea1789d50 Mon Sep 17 00:00:00 2001 From: slaenger <55430878+slaenger@users.noreply.github.com> Date: Tue, 17 Sep 2019 11:27:37 +0200 Subject: [PATCH 5/5] removed CryptoEncrypt-/DecryptDEA(). --- Firmware/Chameleon-Mini/Application/CryptoTDEA.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Firmware/Chameleon-Mini/Application/CryptoTDEA.h b/Firmware/Chameleon-Mini/Application/CryptoTDEA.h index 7215bae..e1da5bc 100644 --- a/Firmware/Chameleon-Mini/Application/CryptoTDEA.h +++ b/Firmware/Chameleon-Mini/Application/CryptoTDEA.h @@ -69,18 +69,6 @@ void CryptoEncrypt2KTDEA_CBCReceive(uint16_t Count, const void* Input, void* Out void CryptoDecrypt2KTDEA_CBCReceive(uint16_t Count, const void* Input, void* Output, void *IV, const uint8_t* Keys); -/** Performs the DES en/deciphering in ECB mode - * - * \param Count Block count, expected to be >= 1 - * \param Plaintext Source buffer with plaintext - * \param Ciphertext Destination buffer to contain ciphertext - * \param IV Initialization vector buffer, will be updated - * \param Keys Key block pointer (CRYPTO_DES_KEY_SIZE) - */ -void CryptoEncryptDEA(const void* Plaintext, void* Ciphertext, const uint8_t* Key); -void CryptoDecryptDEA(const void* Plaintext, void* Ciphertext, const uint8_t* Key); - - /** Performs the 3-key Triple DES en/deciphering in the CBC "send" or "receive" mode * * \param Count Block count, expected to be >= 1