Skip to content

Commit

Permalink
Merge pull request emsec#327 from tomaspre/Desfire-Gallagher-dev
Browse files Browse the repository at this point in the history
Basic Gallagher support on DESFire
  • Loading branch information
fptrs authored Oct 28, 2022
2 parents 9fbb7fb + 108d45c commit d946e0b
Show file tree
Hide file tree
Showing 14 changed files with 495 additions and 80 deletions.
50 changes: 35 additions & 15 deletions Firmware/Chameleon-Mini/Application/CryptoAES128.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@

#include "CryptoAES128.h"

#include "MifareDESFire.h"
#include "DESFire/DESFireLogging.h"

#define NOP() __asm__ __volatile__("nop")

/* AES interrupt callback function pointer. */
Expand Down Expand Up @@ -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;
}
130 changes: 129 additions & 1 deletion Firmware/Chameleon-Mini/Application/CryptoCMAC.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions Firmware/Chameleon-Mini/Application/CryptoCMAC.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
}

/*
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 5 additions & 2 deletions Firmware/Chameleon-Mini/Application/DESFire/DESFireCrypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand All @@ -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;
Expand Down
10 changes: 9 additions & 1 deletion Firmware/Chameleon-Mini/Application/DESFire/DESFireFile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
Loading

0 comments on commit d946e0b

Please sign in to comment.