diff --git a/Firmware/Chameleon-Mini/Application/CryptoAES128.c b/Firmware/Chameleon-Mini/Application/CryptoAES128.c index a6c8a626..ef5abcc9 100644 --- a/Firmware/Chameleon-Mini/Application/CryptoAES128.c +++ b/Firmware/Chameleon-Mini/Application/CryptoAES128.c @@ -36,6 +36,9 @@ #include "CryptoAES128.h" +#include "MifareDESFire.h" +#include "DESFire/DESFireLogging.h" + #define NOP() __asm__ __volatile__("nop") /* AES interrupt callback function pointer. */ @@ -454,22 +457,39 @@ void CryptoAESEncrypt_CBCReceive(uint16_t Count, uint8_t *PlainText, uint8_t *Ci CryptoAES_CBCRecv(Count, PlainText, CipherText, IV, Key, CryptoSpec); } -uint16_t appendBufferCRC32C(uint8_t *bufferData, uint16_t bufferSize) { - uint32_t workingCRC = INIT_CRC32C_VALUE; - for (int i = 0; i < bufferSize; i++) { - workingCRC = workingCRC ^ *(bufferData++); - for (int j = 0; j < 8; j++) { - if (workingCRC & 1) { - workingCRC = (workingCRC >> 1) ^ LE_CRC32C_POLYNOMIAL; - } else { - workingCRC = workingCRC >> 1; - } - } +//Taken from the Proxmark DESFire lib +static void desfire_crc32_byte(uint32_t *crc, const uint8_t value) { + /* x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1 */ + const uint32_t poly = 0xEDB88320; + + *crc ^= value; + for (int current_bit = 7; current_bit >= 0; current_bit--) { + int bit_out = (*crc) & 0x00000001; + *crc >>= 1; + if (bit_out) + *crc ^= poly; + } +} + +//Taken from the Proxmark DESFire lib +void desfire_crc32(const uint8_t *data, const uint16_t len, uint8_t *crc) { + uint32_t desfire_crc = 0xFFFFFFFF; + for (uint16_t i = 0; i < len; i++) { + desfire_crc32_byte(&desfire_crc, data[i]); } + + *((uint32_t *)(crc)) = (desfire_crc); +} + +uint16_t appendBufferCRC32C(uint8_t *bufferData, uint16_t bufferSize) { + uint8_t crc[4]; + desfire_crc32(bufferData, bufferSize, crc); + // Append the CRC32C bytes in little endian byte order to the end of the buffer: - bufferData[bufferSize] = (uint8_t)(workingCRC & 0x000000FF); - bufferData[bufferSize + 1] = (uint8_t)((workingCRC & 0x0000FF00) >> 8); - bufferData[bufferSize + 2] = (uint8_t)((workingCRC & 0x00FF0000) >> 16); - bufferData[bufferSize + 4] = (uint8_t)((workingCRC & 0xFF000000) >> 24); + bufferData[bufferSize] = crc[0]; + bufferData[bufferSize + 1] = crc[1]; + bufferData[bufferSize + 2] = crc[2]; + bufferData[bufferSize + 3] = crc[3]; + return bufferSize + 4; } diff --git a/Firmware/Chameleon-Mini/Application/CryptoCMAC.c b/Firmware/Chameleon-Mini/Application/CryptoCMAC.c index dbdaff9f..bb35b7ab 100644 --- a/Firmware/Chameleon-Mini/Application/CryptoCMAC.c +++ b/Firmware/Chameleon-Mini/Application/CryptoCMAC.c @@ -88,7 +88,8 @@ static bool appendBufferCMACSubroutine(uint8_t cryptoType, const uint8_t *keyDat Encrypt3DESBuffer(newBlockSize, bufferOut, &bufferOut[bufferSize], keyData, bufferIV); break; case CRYPTO_TYPE_AES128: - CryptoAESEncryptBuffer(newBlockSize, bufferOut, &bufferOut[bufferSize], keyData, bufferIV); + //CryptoAESEncryptBuffer(newBlockSize, bufferOut, &bufferOut[bufferSize], keyData, bufferIV); + CryptoAESEncryptBuffer(newBlockSize, bufferOut, &bufferOut[bufferSize], bufferIV, keyData); break; default: return false; @@ -99,6 +100,7 @@ static bool appendBufferCMACSubroutine(uint8_t cryptoType, const uint8_t *keyDat return true; } +//TODO: This did not pass my tests -tomaspre bool appendBufferCMAC(uint8_t cryptoType, const uint8_t *keyData, uint8_t *bufferData, uint16_t bufferSize, uint8_t *IV) { uint8_t blockSize, rb; uint8_t *nistL = _cmac_K2; @@ -129,6 +131,132 @@ bool appendBufferCMAC(uint8_t cryptoType, const uint8_t *keyData, uint8_t *buffe } } +void bin_xor(uint8_t *d1, const uint8_t *d2, size_t len) { + for (size_t i = 0; i < len; i++) + d1[i] = d1[i] ^ d2[i]; +} + +void lsl(uint8_t *data, size_t len) { + for (size_t n = 0; n < len - 1; n++) { + data[n] = (data[n] << 1) | (data[n + 1] >> 7); + } + data[len - 1] <<= 1; +} + +//Taken from https://github.com/RfidResearchGroup/proxmark3/blob/master/client/src/mifare/desfirecrypto.c +bool DesfireCMACGenerateSubkeys(uint8_t cryptoType, const uint8_t *keyData, uint8_t *sk1, uint8_t *sk2) { + int kbs; + uint8_t l[2*CRYPTO_MAX_BLOCK_SIZE]; + uint8_t ivect[CRYPTO_MAX_BLOCK_SIZE]; + + switch (cryptoType) { + case CRYPTO_TYPE_3K3DES: + kbs = CRYPTO_3KTDEA_BLOCK_SIZE; + break; + case CRYPTO_TYPE_AES128: + kbs = CRYPTO_AES_BLOCK_SIZE; + break; + default: + return false; + } + + const uint8_t R = (kbs == 8) ? 0x1B : 0x87; + + memset(l, 0, kbs); + memset(ivect, 0, kbs); + + switch (cryptoType) { + case CRYPTO_TYPE_3K3DES: + Encrypt3DESBuffer(kbs, l, l+kbs, ivect, keyData); + break; + case CRYPTO_TYPE_AES128: + CryptoAESEncryptBuffer(kbs, l, l+kbs, ivect, keyData); + break; + default: + return false; + } + + memcpy(l, l+kbs, kbs); + + bool txor = false; + + // Used to compute CMAC on complete blocks + memcpy(sk1, l, kbs); + txor = l[0] & 0x80; + lsl(sk1, kbs); + if (txor) { + sk1[kbs - 1] ^= R; + } + + // Used to compute CMAC on the last block if non-complete + memcpy(sk2, sk1, kbs); + txor = sk1[0] & 0x80; + lsl(sk2, kbs); + if (txor) { + sk2[kbs - 1] ^= R; + } + + return true; +} + +//Taken from https://github.com/RfidResearchGroup/proxmark3/blob/master/client/src/mifare/desfirecrypto.c +bool DesfireCryptoCMAC(uint8_t cryptoType, const uint8_t *keyData, uint8_t *bufferDataIn, uint16_t bufferSize, uint8_t *IV, uint8_t *cmac) { + uint8_t kbs; + uint8_t len = bufferSize; + uint8_t * bufferData = bufferDataIn + bufferSize; + memcpy(bufferData, bufferDataIn, bufferSize); + + switch (cryptoType) { + case CRYPTO_TYPE_3K3DES: + kbs = CRYPTO_3KTDEA_BLOCK_SIZE; + break; + case CRYPTO_TYPE_AES128: + kbs = CRYPTO_AES_BLOCK_SIZE; + break; + default: + return false; + } + + + uint8_t sk1[24] = {0}; + uint8_t sk2[24] = {0}; + + DesfireCMACGenerateSubkeys(cryptoType, keyData, sk1, sk2); + + if ((!len) || (len % kbs)) { + bufferData[len++] = 0x80; + while (len % kbs) { + bufferData[len++] = 0x00; + } + bin_xor(bufferData + len - kbs, sk2, kbs); + } else { + bin_xor(bufferData + len - kbs, sk1, kbs); + } + + bool retval = false; + + switch (cryptoType) { + case CRYPTO_TYPE_3K3DES: + retval = Encrypt3DESBuffer(len, bufferData, bufferData+len, IV, keyData); + break; + case CRYPTO_TYPE_AES128: + retval = CryptoAESEncryptBuffer(len, bufferData, bufferData+len, IV, keyData); + break; + default: + return false; + } + + if (cmac != NULL) { + memcpy(cmac, IV, kbs); + return retval; + } + + return false; + +} + + + bool checkBufferCMAC(uint8_t *bufferData, uint16_t bufferSize, uint16_t checksumSize) { if (checksumSize > bufferSize) { return false; diff --git a/Firmware/Chameleon-Mini/Application/CryptoCMAC.h b/Firmware/Chameleon-Mini/Application/CryptoCMAC.h index 06fae00a..8ee397cd 100644 --- a/Firmware/Chameleon-Mini/Application/CryptoCMAC.h +++ b/Firmware/Chameleon-Mini/Application/CryptoCMAC.h @@ -42,4 +42,6 @@ bool checkBufferMAC(uint8_t *bufferData, uint16_t bufferSize, uint16_t checksumS uint16_t appendBufferMAC(const uint8_t *keyData, uint8_t *bufferData, uint16_t bufferSize); bool checkBufferCMAC(uint8_t *bufferData, uint16_t bufferSize, uint16_t checksumSize); +bool DesfireCryptoCMAC(uint8_t cryptoType, const uint8_t *keyData, uint8_t *bufferDataIn, uint16_t bufferSize, uint8_t *IV, uint8_t *cmac); + #endif diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireApplicationDirectory.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireApplicationDirectory.c index df51cce0..1f884fe1 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireApplicationDirectory.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireApplicationDirectory.c @@ -208,8 +208,8 @@ void SetAppProperty(DesfireCardLayout propId, BYTE AppSlot, SIZET Value) { bool KeyIdValid(uint8_t AppSlot, uint8_t KeyId) { if (KeyId >= DESFIRE_MAX_KEYS || KeyId >= ReadMaxKeyCount(AppSlot)) { - const char *debugMsg = PSTR("INVKEY-KeyId(%02x)-RdMax(%02x)"); - DEBUG_PRINT_P(debugMsg, KeyId, ReadMaxKeyCount(AppSlot)); + const char *debugMsg = PSTR("INVKEY-KeyId(%02x)-RdMax(%02x),App(%02x)"); + DEBUG_PRINT_P(debugMsg, KeyId, ReadMaxKeyCount(AppSlot), AppSlot); return false; } return true; @@ -304,10 +304,12 @@ void ReadAppKey(uint8_t AppSlot, uint8_t KeyId, uint8_t *Key, SIZET KeySize) { } else if (KeySize > CRYPTO_MAX_KEY_SIZE) { return; } + SIZET keyStorageArrayBlockId = ReadKeyStorageAddress(AppSlot); - SIZET keyStorageArray[DESFIRE_MAX_KEYS]; - ReadBlockBytes(keyStorageArray, keyStorageArrayBlockId, 2 * DESFIRE_MAX_KEYS); - ReadBlockBytes(Key, keyStorageArray[KeyId], KeySize); + + SIZET keyBlockID; + ReadBlockBytes(&keyBlockID, keyStorageArrayBlockId + (sizeof(SIZET) * KeyId), sizeof(SIZET)); + ReadBlockBytes(Key, keyBlockID, KeySize); } void WriteAppKey(uint8_t AppSlot, uint8_t KeyId, const uint8_t *Key, SIZET KeySize) { @@ -316,10 +318,12 @@ void WriteAppKey(uint8_t AppSlot, uint8_t KeyId, const uint8_t *Key, SIZET KeySi } else if (KeySize > CRYPTO_MAX_KEY_SIZE) { return; } + SIZET keyStorageArrayBlockId = ReadKeyStorageAddress(AppSlot); - SIZET keyStorageArray[DESFIRE_MAX_KEYS]; - ReadBlockBytes(keyStorageArray, keyStorageArrayBlockId, 2 * DESFIRE_MAX_KEYS); - WriteBlockBytes(Key, keyStorageArray[KeyId], KeySize); + + SIZET keyBlockID; + ReadBlockBytes(&keyBlockID, keyStorageArrayBlockId + (sizeof(SIZET) * KeyId), sizeof(SIZET)); + WriteBlockBytes(Key, keyBlockID, KeySize); } /* @@ -662,11 +666,14 @@ uint16_t CreateApp(const DESFireAidType Aid, uint8_t KeyCount, uint8_t KeySettin } else { SIZET keyAddresses[DESFIRE_MAX_KEYS]; memset(keyAddresses, 0x00, sizeof(SIZET) * DESFIRE_MAX_KEYS); - // Allocate the application Master Key: - keyAddresses[0] = AllocateBlocks(APP_CACHE_MAX_KEY_BLOCK_SIZE); - if (keyAddresses[0] == 0) { - return STATUS_OUT_OF_EEPROM_ERROR; + // Allocate space for all keys: + for (uint8_t i = 0; i < KeyCount; ++i) { + keyAddresses[i] = AllocateBlocks(APP_CACHE_MAX_KEY_BLOCK_SIZE); + if (keyAddresses[i] == 0) { + return STATUS_OUT_OF_EEPROM_ERROR; + } } + BYTE cryptoBlankKeyData[CRYPTO_MAX_KEY_SIZE]; memset(cryptoBlankKeyData, 0x00, CRYPTO_MAX_KEY_SIZE); WriteBlockBytes(cryptoBlankKeyData, keyAddresses[0], CRYPTO_MAX_KEY_SIZE); diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireApplicationDirectory.h b/Firmware/Chameleon-Mini/Application/DESFire/DESFireApplicationDirectory.h index 3c501617..e9240597 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireApplicationDirectory.h +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireApplicationDirectory.h @@ -58,7 +58,7 @@ This notice must be retained at the top of all source files where indicated. #endif #ifdef MEMORY_LIMITED_TESTING -#define DESFIRE_MAX_KEYS (2) +#define DESFIRE_MAX_KEYS (3) #else #ifdef DESFIRE_CUSTOM_MAX_KEYS #define DESFIRE_MAX_KEYS (DESFIRE_CUSTOM_MAX_KEYS) diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireCrypto.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireCrypto.c index 5eed543f..ba9ce6f8 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireCrypto.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireCrypto.c @@ -37,6 +37,7 @@ This notice must be retained at the top of all source files where indicated. #include "DESFireISO14443Support.h" #include "DESFireStatusCodes.h" #include "DESFireLogging.h" +#include "System.h" CryptoKeyBufferType SessionKey = { 0 }; CryptoIVBufferType SessionIV = { 0 }; @@ -56,9 +57,11 @@ void InvalidateAuthState(BYTE keepPICCAuthData) { if (!keepPICCAuthData) { AuthenticatedWithPICCMasterKey = false; memset(&SessionKey[0], 0x00, CRYPTO_MAX_BLOCK_SIZE); - memset(&SessionIV[0], 0x00, CRYPTO_MAX_BLOCK_SIZE); - SessionIVByteSize = 0; } + + memset(&SessionIV[0], 0x00, CRYPTO_MAX_BLOCK_SIZE); + SessionIVByteSize = 0; + Authenticated = false; AuthenticatedWithKey = DESFIRE_NOT_AUTHENTICATED; Iso7816FileSelected = false; diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireFile.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireFile.c index 3a4d37e6..1cc8b595 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireFile.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireFile.c @@ -308,6 +308,8 @@ uint16_t ReadDataFileIterator(uint8_t *Buffer) { uint8_t WriteDataFileInternal(uint8_t *Buffer, uint16_t ByteCount) { uint8_t Status; + + //decipher? Status = WriteDataFileTransfer(Buffer, ByteCount); switch (Status) { case STATUS_OPERATION_OK: @@ -337,7 +339,7 @@ uint8_t CreateFileCommonValidation(uint8_t FileNum) { return STATUS_AUTHENTICATION_ERROR; } uint8_t selectedKeyPerms = ReadKeySettings(SelectedApp.Slot, AuthenticatedWithKey); - if ((selectedKeyPerms & DESFIRE_FREE_CREATE_DELETE) == 0x00) { + if ((selectedKeyPerms & DESFIRE_FREE_CREATE_DELETE) == 0x00 && !AuthenticatedWithPICCMasterKey) { return STATUS_PERMISSION_DENIED; } return STATUS_OPERATION_OK; @@ -350,6 +352,12 @@ uint8_t ValidateAuthentication(uint16_t AccessRights, uint16_t CheckMask) { GetReadWritePermissions(CheckMask & AccessRights), GetChangePermissions(CheckMask & AccessRights) }; + + if (DesfireDebuggingOn) { + const char *debugMsg = PSTR("Perm-R(%02x),W(%02x),RW(%02x),CHG(%02x)"); + DEBUG_PRINT_P(debugMsg, SplitPerms[0], SplitPerms[1], SplitPerms[2], SplitPerms[3]); + } + bool PermsRelevant[] = { CheckMask & VALIDATE_ACCESS_READ, CheckMask & VALIDATE_ACCESS_WRITE, diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.c index 1bbc701d..02e89568 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireISO14443Support.c @@ -354,6 +354,9 @@ uint16_t ISO144433APiccProcess(uint8_t *Buffer, uint16_t BitCount) { StateRetryCount = 0; } else if (ISO14443ACmdIsWUPA(Cmd)) { DesfireLogEntry(LOG_INFO_APP_CMD_WUPA, NULL, 0); + //Resetting the selected app seems to be required but not docummented + const DESFireAidType Aid = {0,0,0}; + SelectApp(Aid); ISO144433ASwitchState(ISO14443_3A_STATE_IDLE); StateRetryCount = 0; } else if (ISO144433AIsHalt(Buffer, BitCount)) { diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireInstructions.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireInstructions.c index cb964a32..880a64ac 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireInstructions.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireInstructions.c @@ -311,6 +311,29 @@ static const DESFireCommand DESFireCommandSet[] = { }, }; +//Sets the key number to a real key number after deriving the crypto type from it +uint8_t ProcessKeyNumber(uint8_t * KeyNumber) { + if (*KeyNumber & APPLICATION_CRYPTO_AES) { + *KeyNumber = (*KeyNumber) & APPLICATION_CRYPTO_AES; + return CRYPTO_TYPE_AES128; + } else if (*KeyNumber & APPLICATION_CRYPTO_3K3DES) { + *KeyNumber = (*KeyNumber) & APPLICATION_CRYPTO_3K3DES; + return CRYPTO_TYPE_3K3DES; + } + return 0xFF; +} + +//This should be only called when the command itself does not require encryption! +//But it should be called every time we're authenticated, using EV1 mode and are not ecrypting! TODO!! +//Currently used only in commands needed for Gallagher +//Conforms with EV1 datsheet, chap 7.3.4 (CMAC) +void UpdateIVIfNeeded(uint8_t *Buffer, uint16_t ByteCount) { + if (ActiveCommMode == DESFIRE_COMMS_PLAINTEXT && Authenticated && ReadKeyCryptoType(SelectedApp.Slot, AuthenticatedWithKey)== CRYPTO_TYPE_AES128) { + uint8_t cmac[32]; + DesfireCryptoCMAC(CRYPTO_TYPE_AES128, SessionKey, Buffer, ByteCount, SessionIV, cmac); + } +} + uint16_t CallInstructionHandler(uint8_t *Buffer, uint16_t ByteCount) { if (ByteCount == 0) { Buffer[0] = STATUS_PARAMETER_ERROR; @@ -321,7 +344,7 @@ uint16_t CallInstructionHandler(uint8_t *Buffer, uint16_t ByteCount) { uint16_t curInsIndex; DESFireCommand dfCmd; while (curInsUpper >= curInsLower) { - curInsIndex = curInsLower + (curInsUpper + 1 - curInsLower) / 2; + curInsIndex = (curInsLower + curInsUpper) / 2; dfCmd = DESFireCommandSet[curInsIndex]; if (dfCmd.insCode == insCode) { if (dfCmd.insFunc == NULL) { @@ -598,8 +621,23 @@ uint16_t EV0CmdChangeKey(uint8_t *Buffer, uint16_t ByteCount) { Buffer[0] = STATUS_LENGTH_ERROR; return DESFIRE_STATUS_RESPONSE_SIZE; } + + KeyId = Buffer[1]; + + /* Are we changing the card master key? + * Which crypto type are we using? + * TODO: Is this really ok for non master keys that are AES/3k3DES? */ + uint8_t keySize = ByteCount - 2; + uint8_t cryptoType = 0xFF; + + if (keySize == CRYPTO_AES_KEY_SIZE || keySize == CRYPTO_2KTDEA_KEY_SIZE || keySize == CRYPTO_3KTDEA_KEY_SIZE) { + cryptoType = ProcessKeyNumber(&KeyId); + } else { + Buffer[0] = STATUS_NO_SUCH_KEY; + return DESFIRE_STATUS_RESPONSE_SIZE; + } + /* Validate number of keys, and make sure the KeyId is valid given the AID selected */ - KeyId = Buffer[1]; if (!KeyIdValid(SelectedApp.Slot, KeyId)) { Buffer[0] = STATUS_PARAMETER_ERROR; return DESFIRE_STATUS_RESPONSE_SIZE; @@ -628,31 +666,33 @@ uint16_t EV0CmdChangeKey(uint8_t *Buffer, uint16_t ByteCount) { break; default: /* Authentication with a specific key is required */ - if (!IsAuthenticated() || (KeyId != ChangeKeyId)) { + if (!IsAuthenticated() || (AuthenticatedWithKey != ChangeKeyId)) { Buffer[0] = STATUS_PERMISSION_DENIED; return DESFIRE_STATUS_RESPONSE_SIZE; } break; } - /* Figure out the key size, and the crypto type from it: */ - uint8_t keySize = ByteCount - 2; - uint8_t cryptoType; - if ((keySize != CRYPTO_3KTDEA_KEY_SIZE) && (keySize != CRYPTO_AES_KEY_SIZE)) { - Buffer[0] = STATUS_NO_SUCH_KEY; - return DESFIRE_STATUS_RESPONSE_SIZE; - } else if (keySize == CRYPTO_3KTDEA_KEY_SIZE) { - cryptoType = CRYPTO_TYPE_3K3DES; - } else { - cryptoType = CRYPTO_TYPE_AES128; + /* Figure out the key size, and the crypto type from it:*/ + if (cryptoType == 0xFF) { + uint8_t keySize = ByteCount - 2; + if ((keySize != CRYPTO_3KTDEA_KEY_SIZE) && (keySize != CRYPTO_AES_KEY_SIZE)) { + Buffer[0] = STATUS_NO_SUCH_KEY; + return DESFIRE_STATUS_RESPONSE_SIZE; + } else if (keySize == CRYPTO_3KTDEA_KEY_SIZE) { + cryptoType = CRYPTO_TYPE_3K3DES; + } else { + cryptoType = CRYPTO_TYPE_AES128; + } } uint8_t nextKeyVersion = ReadKeyVersion(SelectedApp.Slot, KeyId) + 1; - /* TODO: The PCD generates data differently based on whether AuthKeyId == ChangeKeyId */ - /* TODO: [NEED DOCS] NewKey^OldKey | CRC(NewKey^OldKey) | CRC(NewKey) | Padding */ - /* TODO: [NEED DOCS] NewKey | CRC(NewKey) | Padding */ /* TODO: NOTE: Padding checks are skipped, because meh. */ + const char *debugMsg = PSTR("KeyWrt,id(%02x),v(%02x),CT(%02x),size(%02x),ASlot(%02x)"); + DEBUG_PRINT_P(debugMsg, KeyId, nextKeyVersion, cryptoType,keySize, SelectedApp.Slot); + DesfireLogEntry(LOG_APP_AUTH_KEY, (void *) &Buffer[2], keySize); + /* Write the key, next version, and scrub */ WriteAppKey(SelectedApp.Slot, KeyId, &Buffer[2], keySize); WriteKeyVersion(SelectedApp.Slot, KeyId, nextKeyVersion); @@ -756,13 +796,13 @@ uint16_t EV0CmdGetApplicationIds1(uint8_t *Buffer, uint16_t ByteCount) { return DESFIRE_STATUS_RESPONSE_SIZE; } /* Require the PICC app to be selected */ - if (!AuthenticatedWithPICCMasterKey) { + if (SelectedApp.Slot != DESFIRE_PICC_APP_SLOT) { Buffer[0] = STATUS_PERMISSION_DENIED; return DESFIRE_STATUS_RESPONSE_SIZE; } + /* Verify authentication settings */ - if ((ReadKeySettings(SelectedApp.Slot, AuthenticatedWithKey) & DESFIRE_FREE_DIRECTORY_LIST) && - (AuthenticatedWithKey != DESFIRE_MASTER_KEY_ID)) { + if (!AMKFreeDirectoryListing() && (AuthenticatedWithKey != DESFIRE_MASTER_KEY_ID)) { /* PICC master key authentication is required */ Buffer[0] = STATUS_AUTHENTICATION_ERROR; return DESFIRE_STATUS_RESPONSE_SIZE; @@ -777,29 +817,40 @@ uint16_t EV0CmdCreateApplication(uint8_t *Buffer, uint16_t ByteCount) { uint8_t Status; uint8_t KeyCount; uint8_t KeySettings; + uint8_t KeySettings2; + /* Validate command length */ + //TODO: Can contain optional fields if (ByteCount != 1 + 3 + 1 + 1) { Status = STATUS_LENGTH_ERROR; return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } + + UpdateIVIfNeeded(Buffer, ByteCount); + /* Require the PICC app to be selected */ if (!AuthenticatedWithPICCMasterKey) { Status = STATUS_PERMISSION_DENIED; return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } + const DESFireAidType Aid = { Buffer[1], Buffer[2], Buffer[3] }; KeySettings = Buffer[4]; - KeyCount = Buffer[5]; + KeyCount = Buffer[5] & 0x0f; //Only the lower four bits contain key count + KeySettings2 = Buffer[5] & 0xf0; + /* Validate number of keys: less than max (one for the Master Key) */ if (KeyCount > DESFIRE_MAX_KEYS || KeyCount == 0) { Status = STATUS_PARAMETER_ERROR; return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } + if (PMKRequiredForAppCreateDelete() != 0x00 && (Authenticated == 0x00 || AuthenticatedWithKey != 0x00)) { /* PICC master key authentication is required */ Status = STATUS_AUTHENTICATION_ERROR; return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } + /* Done */ Status = CreateApp(Aid, KeyCount, KeySettings); return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); @@ -891,6 +942,9 @@ uint16_t EV0CmdCreateStandardDataFile(uint8_t *Buffer, uint16_t ByteCount) { Status = STATUS_LENGTH_ERROR; return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } + + UpdateIVIfNeeded(Buffer, ByteCount); + /* Common args validation */ FileNum = Buffer[1]; CommSettings = Buffer[2]; @@ -899,7 +953,9 @@ uint16_t EV0CmdCreateStandardDataFile(uint8_t *Buffer, uint16_t ByteCount) { if (Status != STATUS_OPERATION_OK) { return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } - FileSize = GET_LE24(&Buffer[5]); + //Warning, stripping the MSB of the size! + FileSize = Buffer[5] + (Buffer[6] * 256); + DesfireLogEntry(LOG_INFO_DESFIRE_PROTECTED_DATA_SET, (void *) &FileSize, 2); Status = CreateStandardFile(FileNum, CommSettings, AccessRights, (uint16_t)FileSize); return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } @@ -1165,34 +1221,45 @@ uint16_t EV0CmdReadData(uint8_t *Buffer, uint16_t ByteCount) { Status = STATUS_LENGTH_ERROR; return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } + + UpdateIVIfNeeded(Buffer, ByteCount); + /* Validate file number */ FileNum = Buffer[1]; uint8_t fileIndex = LookupFileNumberIndex(SelectedApp.Slot, FileNum); if (fileIndex >= DESFIRE_MAX_FILES) { - Status = STATUS_PARAMETER_ERROR; + Status = STATUS_FILE_NOT_FOUND; + DEBUG_PRINT_P(PSTR("FileIndexError")); return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } AccessRights = ReadFileAccessRights(SelectedApp.Slot, fileIndex); CommSettings = ReadFileCommSettings(SelectedApp.Slot, fileIndex); - /* Verify authentication: read or read&write required */ - switch (ValidateAuthentication(AccessRights, VALIDATE_ACCESS_READWRITE | VALIDATE_ACCESS_READ)) { - case VALIDATED_ACCESS_DENIED: - Status = STATUS_AUTHENTICATION_ERROR; - return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); - case VALIDATED_ACCESS_GRANTED_PLAINTEXT: - CommSettings = DESFIRE_COMMS_PLAINTEXT; - /* Fall through */ - case VALIDATED_ACCESS_GRANTED: - /* Carry on */ - break; + + /* Verify authentication: read or read&write required + * Except for cases when AppDirectory can be read freely*/ + if (!AMKFreeDirectoryListing()) { + switch (ValidateAuthentication(AccessRights, VALIDATE_ACCESS_READWRITE | VALIDATE_ACCESS_READ)) { + case VALIDATED_ACCESS_DENIED: + Status = STATUS_AUTHENTICATION_ERROR; + return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); + case VALIDATED_ACCESS_GRANTED_PLAINTEXT: + CommSettings = DESFIRE_COMMS_PLAINTEXT; + /* Fall through */ + case VALIDATED_ACCESS_GRANTED: + /* Carry on */ + break; + } } + /* Validate the file type */ uint8_t fileType = ReadFileType(SelectedApp.Slot, fileIndex); if (fileType != DESFIRE_FILE_STANDARD_DATA && fileType != DESFIRE_FILE_BACKUP_DATA) { Status = STATUS_PARAMETER_ERROR; + DEBUG_PRINT_P(PSTR("FileTypeError")); return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } + /* Validate offset and length (preliminary) */ Offset = GET_LE24(&Buffer[2]); Length = GET_LE24(&Buffer[5]); @@ -1201,11 +1268,42 @@ uint16_t EV0CmdReadData(uint8_t *Buffer, uint16_t ByteCount) { Status = STATUS_BOUNDARY_ERROR; return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } + /* Setup and start the transfer */ Status = ReadDataFileSetup(fileIndex, CommSettings, (uint16_t) Offset, (uint16_t) Length); if (Status != STATUS_OPERATION_OK) { return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } + + //TODO: This WILL break for files that use full encryption and are more than 16 bytes long! + //TODO: See commands for file writing for instructions on how to fix this + //Non-encrypted files are fine + if (CommSettings == 0x03) { + Length = (Length > 32) ? 32 : Length; + uint16_t size = ReadDataFileIterator(Buffer); + + Buffer[size] = Buffer[0]; + appendBufferCRC32C(Buffer+1, size); + memmove(Buffer+size, Buffer+size+1, 4); + + size += 4; + + uint8_t rem = (size-1) % CRYPTO_AES_BLOCK_SIZE; + if (rem) { + for (uint8_t i = 0; i < CRYPTO_AES_BLOCK_SIZE - rem; ++i) { + Buffer[size] = 0; + size++; + } + } + + uint8_t ciphertext[64]; + CryptoAESEncryptBuffer(size-1, Buffer+1, ciphertext, SessionIV, SessionKey); + memmove(Buffer+1, ciphertext, size-1); + + return size; + } + + return ReadDataFileIterator(Buffer); } @@ -1230,6 +1328,7 @@ uint16_t EV0CmdWriteData(uint8_t *Buffer, uint16_t ByteCount) { } AccessRights = ReadFileAccessRights(SelectedApp.Slot, fileIndex); CommSettings = ReadFileCommSettings(SelectedApp.Slot, fileIndex); + /* Verify authentication: read or read&write required */ switch (ValidateAuthentication(AccessRights, VALIDATE_ACCESS_READWRITE | VALIDATE_ACCESS_WRITE)) { case VALIDATED_ACCESS_DENIED: @@ -1240,6 +1339,7 @@ uint16_t EV0CmdWriteData(uint8_t *Buffer, uint16_t ByteCount) { case VALIDATED_ACCESS_GRANTED: break; } + /* Validate the file type */ uint8_t fileType = ReadFileType(SelectedApp.Slot, fileIndex); if (fileType != DESFIRE_FILE_STANDARD_DATA && @@ -1267,6 +1367,7 @@ uint16_t EV0CmdWriteData(uint8_t *Buffer, uint16_t ByteCount) { * blocks to FRAM memory: */ uint16_t dataWriteSize = ByteCount - 8; + uint8_t *dataWriteBuffer = &Buffer[8]; if (Offset > 0) { uint8_t precursorFileData[Offset]; @@ -1277,11 +1378,42 @@ uint16_t EV0CmdWriteData(uint8_t *Buffer, uint16_t ByteCount) { dataWriteSize += Offset; dataWriteBuffer = &Buffer[1]; } + /* Setup and start the transfer */ Status = WriteDataFileSetup(fileIndex, fileType, CommSettings, (uint16_t) Offset, (uint16_t) Length); if (Status != STATUS_OPERATION_OK) { return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } + + //TODO: This will(!) break if we would need to split the **encrypted** write into multiple segments! + //TODO: But at least there is *some* support for ecrypted write + //TODO: It'll be best to decipher in the actual writing function as we can then use + //TODO: the existing code for encrypted comms + //TODO: This also breaks if there's any write offset + //Non-encrypted write is ok + //Decrypt the incoming buffer if needed: + if (CommSettings == 0x03) { + //TODO: Check if the cipher is actually AES + DesfireLogEntry(LOG_APP_SESSION_IV, (void *) SessionIV, 16); + CryptoAESDecryptBuffer(dataWriteSize, dataWriteBuffer + dataWriteSize, dataWriteBuffer, SessionIV, SessionKey); + memmove(dataWriteBuffer, dataWriteBuffer + dataWriteSize, dataWriteSize); + + dataWriteSize = (Length > 32) ? 32 : Length; //To make sure we only process data in one command + + //Check checksum + uint8_t recvCRC[4]; + recvCRC[0] = dataWriteBuffer[dataWriteSize]; + recvCRC[1] = dataWriteBuffer[dataWriteSize+1]; + recvCRC[2] = dataWriteBuffer[dataWriteSize+2]; + recvCRC[3] = dataWriteBuffer[dataWriteSize+3]; + appendBufferCRC32C(Buffer, dataWriteSize+8); + if (recvCRC[0] != dataWriteBuffer[dataWriteSize] || recvCRC[1] != dataWriteBuffer[dataWriteSize+1] || + recvCRC[2] != dataWriteBuffer[dataWriteSize+2] || recvCRC[3] != dataWriteBuffer[dataWriteSize+3]) { + Status = STATUS_PARAMETER_ERROR; + return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); + } + } + Status = WriteDataFileIterator(dataWriteBuffer, dataWriteSize); return ExitWithStatus(Buffer, Status, DESFIRE_STATUS_RESPONSE_SIZE); } @@ -1890,6 +2022,8 @@ uint16_t DesfireCmdAuthenticateAES1(uint8_t *Buffer, uint16_t ByteCount) { /* Check if we are authenticating with the PICC/Master key setup correctly */ KeyId = Buffer[1]; + uint8_t cryptoType = ProcessKeyNumber(&KeyId); + if (SelectedApp.Slot == DESFIRE_PICC_APP_SLOT && KeyId != DESFIRE_MASTER_KEY_ID) { Buffer[0] = STATUS_PERMISSION_DENIED; return DESFIRE_STATUS_RESPONSE_SIZE; @@ -1901,6 +2035,10 @@ uint16_t DesfireCmdAuthenticateAES1(uint8_t *Buffer, uint16_t ByteCount) { return DESFIRE_STATUS_RESPONSE_SIZE; } + const char *debugMsg = PSTR("KeyId(%02x)-RdMax(%02x),App(%02x)"); + DEBUG_PRINT_P(debugMsg, KeyId, ReadMaxKeyCount(SelectedApp.Slot), SelectedApp.Slot); + + /* Make sure that this key is AES, and figure out its byte size */ BYTE cryptoKeyType = ReadKeyCryptoType(SelectedApp.Slot, KeyId); if (!CryptoTypeAES(cryptoKeyType)) { @@ -2020,8 +2158,11 @@ uint16_t DesfireCmdAuthenticateAES2(uint8_t *Buffer, uint16_t ByteCount) { */ Authenticated = true; AuthenticatedWithKey = KeyId; - AuthenticatedWithPICCMasterKey = (SelectedApp.Slot == DESFIRE_PICC_APP_SLOT) && - (KeyId == DESFIRE_MASTER_KEY_ID); + if ((SelectedApp.Slot == DESFIRE_PICC_APP_SLOT) && (KeyId == DESFIRE_MASTER_KEY_ID)){ + AuthenticatedWithPICCMasterKey = true; + } + + memset(&SessionIV[0], 0x00, CRYPTO_MAX_BLOCK_SIZE); /* Return the status on success */ Buffer[0] = STATUS_OPERATION_OK; diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireMemoryOperations.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireMemoryOperations.c index 44af3e51..fd8b7789 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireMemoryOperations.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireMemoryOperations.c @@ -64,9 +64,11 @@ uint16_t AllocateBlocksMain(uint16_t BlockCount) { if (Block + BlockCount < Block || Block + BlockCount >= StorageSizeToBytes(Picc.StorageSize) || Block + BlockCount >= MEMORY_SIZE_PER_SETTING / BLOCKWISE_IO_MULTIPLIER) { return 0; } + Picc.FirstFreeBlock = Block + BlockCount; DESFIRE_FIRST_FREE_BLOCK_ID = Picc.FirstFreeBlock; SynchronizePICCInfo(); + return Block; } diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c index 1082c229..3d249f92 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c @@ -115,15 +115,15 @@ uint8_t ReadDataFilterSetup(uint8_t CommSettings) { case DESFIRE_COMMS_PLAINTEXT: break; case DESFIRE_COMMS_PLAINTEXT_MAC: - memset(SessionIV, PICC_EMPTY_BYTE, sizeof(SessionIV)); + //memset(SessionIV, PICC_EMPTY_BYTE, sizeof(SessionIV)); SessionIVByteSize = CRYPTO_2KTDEA_KEY_SIZE; break; case DESFIRE_COMMS_CIPHERTEXT_DES: - memset(SessionIV, PICC_EMPTY_BYTE, sizeof(SessionIV)); + //memset(SessionIV, PICC_EMPTY_BYTE, sizeof(SessionIV)); SessionIVByteSize = CRYPTO_3KTDEA_KEY_SIZE; break; case DESFIRE_COMMS_CIPHERTEXT_AES128: - memset(SessionIV, 0, sizeof(SessionIVByteSize)); + //memset(SessionIV, 0, sizeof(SessionIVByteSize)); SessionIVByteSize = CRYPTO_AES_KEY_SIZE; default: return STATUS_PARAMETER_ERROR; @@ -134,19 +134,19 @@ uint8_t ReadDataFilterSetup(uint8_t CommSettings) { uint8_t WriteDataFilterSetup(uint8_t CommSettings) { switch (CommSettings) { case DESFIRE_COMMS_PLAINTEXT: - memset(SessionIV, 0, sizeof(SessionIVByteSize)); + //memset(SessionIV, 0, sizeof(SessionIVByteSize)); SessionIVByteSize = 0; break; case DESFIRE_COMMS_PLAINTEXT_MAC: - memset(SessionIV, 0, sizeof(SessionIVByteSize)); + //memset(SessionIV, 0, sizeof(SessionIVByteSize)); SessionIVByteSize = CRYPTO_2KTDEA_KEY_SIZE; break; case DESFIRE_COMMS_CIPHERTEXT_DES: - memset(SessionIV, 0, sizeof(SessionIVByteSize)); + //memset(SessionIV, 0, sizeof(SessionIVByteSize)); SessionIVByteSize = CRYPTO_AES_KEY_SIZE; break; case DESFIRE_COMMS_CIPHERTEXT_AES128: - memset(SessionIV, 0, sizeof(SessionIVByteSize)); + //memset(SessionIV, 0, sizeof(SessionIVByteSize)); SessionIVByteSize = CRYPTO_AES_KEY_SIZE; break; default: @@ -338,6 +338,7 @@ void FactoryFormatPiccEV1(uint8_t StorageSize) { Picc.SwVersionMajor = DESFIRE_SW_MAJOR_EV1; Picc.SwVersionMinor = DESFIRE_SW_MINOR_EV1; /* Reset the free block pointer */ + InitBlockSizes(); Picc.FirstFreeBlock = DESFIRE_FIRST_FREE_BLOCK_ID; /* Continue with user data initialization */ SynchronizePICCInfo(); diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.c index 172870db..3d19f6b3 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.c @@ -148,6 +148,9 @@ bool DesfireCheckParityBits(uint8_t *Buffer, uint16_t BitCount) { uint16_t DesfirePreprocessAPDUWrapper(uint8_t CommMode, uint8_t *Buffer, uint16_t BufferSize, bool TruncateChecksumBytes) { uint16_t ChecksumBytes = 0; + const char *debugMsg = PSTR("Commode(%02x)"); + DEBUG_PRINT_P(debugMsg, CommMode); + switch (CommMode) { case DESFIRE_COMMS_PLAINTEXT_MAC: { if (DesfireCommandState.CryptoMethodType == CRYPTO_TYPE_DES || DesfireCommandState.CryptoMethodType == CRYPTO_TYPE_2KTDEA) { diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.h b/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.h index d4dd0e86..2717647d 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.h +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.h @@ -35,7 +35,7 @@ This notice must be retained at the top of all source files where indicated. #define ASBITS(bc) ((bc) * BITS_PER_BYTE) #define GET_LE16(p) (*((uint16_t*)&(p)[0])) -#define GET_LE24(p) (*((__uint24*)&(p)[0])) +#define GET_LE24(p) (*((__uint24*)&(p)[0])) // Careful! Don't use this if the first byte is LSB!! #define GET_LE32(p) (*((uint32_t*)&(p)[0])) #define UnsignedTypeToUINT(typeValue) \ diff --git a/Firmware/Chameleon-Mini/Application/MifareDESFire.c b/Firmware/Chameleon-Mini/Application/MifareDESFire.c index 576b186a..ac9f5ade 100644 --- a/Firmware/Chameleon-Mini/Application/MifareDESFire.c +++ b/Firmware/Chameleon-Mini/Application/MifareDESFire.c @@ -121,8 +121,8 @@ void MifareDesfireAppReset(void) { void MifareDesfireAppTick(void) { if (!CheckStateRetryCount(false)) { - ResetISOState(); - MifareDesfireReset(); + //ResetISOState(); + //MifareDesfireReset(); } } @@ -130,6 +130,85 @@ void MifareDesfireAppTask(void) { /* EMPTY -- Do nothing. */ } +//Checks if this is an EV1 frame which's content needs decryption. +//TODO: Other modes than AES +uint16_t CheckNeedForDecryptionAndDecrypt(uint8_t *Buffer, uint16_t ByteCount) { + if (ByteCount == 0) { + return 0; + } + + if (Buffer[0] == CMD_CHANGE_KEY_SETTINGS) { + //TODO + return ByteCount; + } + + if (Buffer[0] == CMD_CHANGE_KEY) { + if (((ByteCount - 2) % 16) != 0 ) { + return ByteCount; + } + + //TODO: 0x0A and 0x1A autrhenticate commands + + uint8_t tmpIV[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + CryptoAESDecryptBuffer(ByteCount - 2, Buffer + ByteCount, Buffer + 2, tmpIV, SessionKey); + memmove(Buffer + 2, Buffer + ByteCount, ByteCount - 2); + + //TODO: + //check decryption success + + //UnXOR + //TODO: Check that the application id matches too? EV1 datasheet, page 43, unclear + if ((AuthenticatedWithKey != Buffer[1]) && (ReadKeySettings(SelectedApp.Slot, Buffer[1]) == 0x0E)) { + //Strip padding + // cmd + keyNo + data + vers + crc + crc + ByteCount = 1 + 1 + 16 + 1 + 4 + 4; + + uint8_t tmpKeyBuff[16]; + ReadAppKey(SelectedApp.Slot, Buffer[1], tmpKeyBuff, 16); + for (int i = 2; i < 18; ++i) { + Buffer[i] = Buffer[i] ^ tmpKeyBuff[i]; + } + //TODO: + //Check first checksum + //Check second checksum + //Strip checksums + return 18; + } else { + //Strip padding + //cmd + keyNo + data + vers + crc + ByteCount = 1 + 1 + 16 + 1 + 4; + + //Check checksum + uint8_t recvCRC[4]; + recvCRC[0] = Buffer[19]; + recvCRC[1] = Buffer[20]; + recvCRC[2] = Buffer[21]; + recvCRC[3] = Buffer[22]; + appendBufferCRC32C(Buffer, 19); + if (recvCRC[0] != Buffer[19] || recvCRC[1] != Buffer[20] || recvCRC[2] != Buffer[21] || recvCRC[3] != Buffer[22]) { + DEBUG_PRINT_P(PSTR("CRC does not match")); + return 0; + } else { + return 18; //Caution! Stripping KeyVers! + } + } + } + + if (Buffer[0] == CMD_SET_CONFIGURATION) { + //TODO + return ByteCount; + } + + if (Buffer[0] == CMD_CHANGE_FILE_SETTINGS) { + //TODO + return ByteCount; + } + + return ByteCount; +} + + + uint16_t MifareDesfireProcessCommand(uint8_t *Buffer, uint16_t ByteCount) { if (ByteCount == 0) { @@ -137,6 +216,9 @@ uint16_t MifareDesfireProcessCommand(uint8_t *Buffer, uint16_t ByteCount) { } else if (Buffer[0] != STATUS_ADDITIONAL_FRAME) { DesfireState = DESFIRE_IDLE; } + + ByteCount = CheckNeedForDecryptionAndDecrypt(Buffer, ByteCount); + DesfireLogEntry(LOG_INFO_DESFIRE_INCOMING_DATA, (void *) Buffer, ByteCount); uint16_t ReturnBytes = 0; @@ -256,9 +338,16 @@ uint16_t MifareDesfireAppProcess(uint8_t *Buffer, uint16_t BitCount) { } else { ISO14443ALastIncomingDataFrameBits = ISO14443AStoreLastDataFrameAndReturn(Buffer, BitCount); } + if (ByteCount >= 8 && DesfireCLA(Buffer[1]) && Buffer[2] == STATUS_ADDITIONAL_FRAME && - Buffer[3] == 0x00 && Buffer[4] == 0x00 && Buffer[5] == ByteCount - 8 && + Buffer[3] == 0x00 && Buffer[4] == 0x00 && (Buffer[5] == ByteCount - 8 || Buffer[5] == ByteCount - 9) && DesfireStateExpectingAdditionalFrame(DesfireState)) { + + bool nine = false; + if (Buffer[5] == ByteCount - 9) { + nine = true; + } + /* [PM3-V1] : Handle the native-wrapped version of the additional frame data: */ uint16_t checkSumPostVerifyBytes = DesfirePreprocessAPDUAndTruncate(ActiveCommMode, Buffer, ByteCount); if (checkSumPostVerifyBytes == 0) { @@ -274,8 +363,16 @@ uint16_t MifareDesfireAppProcess(uint8_t *Buffer, uint16_t BitCount) { if (ProcessedByteCount == 0) { return ISO14443A_APP_NO_RESPONSE; } - /* Re-wrap into padded APDU form */ + + uint8_t StatusByte = Buffer[0]; Buffer[0] = PrologueCounterByte; + if (nine) { + Buffer[ProcessedByteCount] = 0x91; + Buffer[ProcessedByteCount + 1] = StatusByte; + ++ProcessedByteCount; + } + + /* Re-wrap into padded APDU form */ uint16_t ProcessedByteCountWithCRCA = DesfirePostprocessAPDU(ActiveCommMode, Buffer, ProcessedByteCount + 1); return ISO14443AStoreLastDataFrameAndReturn(Buffer, ASBITS(ProcessedByteCountWithCRCA)); } else if (ByteCount >= 3 && Buffer[2] == STATUS_ADDITIONAL_FRAME && DesfireStateExpectingAdditionalFrame(DesfireState)) { @@ -331,7 +428,7 @@ uint16_t MifareDesfireAppProcess(uint8_t *Buffer, uint16_t BitCount) { } ProcessedByteCount = DesfirePostprocessAPDU(ActiveCommMode, Buffer, ProcessedByteCount); return ISO14443AStoreLastDataFrameAndReturn(Buffer, ASBITS(ProcessedByteCount)); - } else if ((ByteCount >= 8 && DesfireCLA(Buffer[1]) && + } else if ((ByteCount >= 8 && Buffer[1] == DESFIRE_NATIVE_CLA && Buffer[3] == 0x00 && Buffer[4] == 0x00 && Buffer[5] == ByteCount - 8) || (ByteCount >= 9 && DesfireCLA(Buffer[1]) && Buffer[3] == 0x00 && Buffer[4] == 0x00 && Buffer[5] == ByteCount - 9)) {