From a24fbe85c0d088a04989a1b15cd3cec23fd7e3bb Mon Sep 17 00:00:00 2001 From: Maxie Dion Schmidt Date: Sat, 12 Feb 2022 21:47:54 -0500 Subject: [PATCH] Preliminary (partial) support for more CommModes -- This is going to need substantial testing -- III --- .../Chameleon-Mini/Application/CryptoAES128.c | 30 ++-- .../Chameleon-Mini/Application/CryptoCMAC.c | 114 +++++++++---- .../DESFire/DESFireChameleonTerminal.c | 6 +- .../Application/DESFire/DESFirePICCControl.c | 16 +- .../Application/DESFire/DESFireUtils.c | 153 +++++++++--------- .../Application/DESFire/DESFireUtils.h | 24 +-- .../Application/MifareDESFire.c | 50 +++--- Firmware/Chameleon-Mini/Makefile | 2 +- 8 files changed, 222 insertions(+), 173 deletions(-) diff --git a/Firmware/Chameleon-Mini/Application/CryptoAES128.c b/Firmware/Chameleon-Mini/Application/CryptoAES128.c index c931d838..321b0273 100644 --- a/Firmware/Chameleon-Mini/Application/CryptoAES128.c +++ b/Firmware/Chameleon-Mini/Application/CryptoAES128.c @@ -296,8 +296,8 @@ uint8_t CryptoAESDecryptBuffer(uint16_t Count, uint8_t *Plaintext, uint8_t *Ciph // This routine performs the CBC "send" mode chaining: C = E(P ^ IV); IV = C static void CryptoAES_CBCSend(uint16_t Count, void *Plaintext, void *Ciphertext, uint8_t *IV, uint8_t *Key, CryptoAES_CBCSpec_t CryptoSpec); static void CryptoAES_CBCSend(uint16_t Count, void *Plaintext, void *Ciphertext, - uint8_t *IV, uint8_t *Key, - CryptoAES_CBCSpec_t CryptoSpec) { + uint8_t *IV, uint8_t *Key, + CryptoAES_CBCSpec_t CryptoSpec) { uint16_t numBlocks = CRYPTO_BYTES_TO_BLOCKS(Count, CryptoSpec.blockSize); uint16_t blockIndex = 0; uint8_t *ptBuf = (uint8_t *) Plaintext, *ctBuf = (uint8_t *) Ciphertext; @@ -323,8 +323,8 @@ static void CryptoAES_CBCSend(uint16_t Count, void *Plaintext, void *Ciphertext, // This routine performs the CBC "receive" mode chaining: C = E(P) ^ IV; IV = P static void CryptoAES_CBCRecv(uint16_t Count, void *Plaintext, void *Ciphertext, uint8_t *IV, uint8_t *Key, CryptoAES_CBCSpec_t CryptoSpec); static void CryptoAES_CBCRecv(uint16_t Count, void *Plaintext, void *Ciphertext, - uint8_t *IV, uint8_t *Key, - CryptoAES_CBCSpec_t CryptoSpec) { + uint8_t *IV, uint8_t *Key, + CryptoAES_CBCSpec_t CryptoSpec) { uint16_t numBlocks = CRYPTO_BYTES_TO_BLOCKS(Count, CryptoSpec.blockSize); uint16_t blockIndex = 0; uint8_t *ptBuf = (uint8_t *) Plaintext, *ctBuf = (uint8_t *) Ciphertext; @@ -389,18 +389,18 @@ 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++) { + for (int j = 0; j < 8; j++) { if (workingCRC & 1) { - workingCRC = (workingCRC >> 1) ^ LE_CRC32C_POLYNOMIAL; - } else { - workingCRC = workingCRC >> 1; - } - } + workingCRC = (workingCRC >> 1) ^ LE_CRC32C_POLYNOMIAL; + } else { + workingCRC = workingCRC >> 1; + } + } } - // 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); + // 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); return bufferSize + 4; } diff --git a/Firmware/Chameleon-Mini/Application/CryptoCMAC.c b/Firmware/Chameleon-Mini/Application/CryptoCMAC.c index c78526df..dbdaff9f 100644 --- a/Firmware/Chameleon-Mini/Application/CryptoCMAC.c +++ b/Firmware/Chameleon-Mini/Application/CryptoCMAC.c @@ -45,8 +45,8 @@ static void getCMACSubK1(const uint8_t *bufferL, uint8_t blockSize, uint8_t poly RotateArrayLeft(bufferL, bufferOut, blockSize); if ((bufferL[0] & 0x80) != 0) { for (int kidx = 0; kidx < blockSize; kidx++) { - bufferOut[kidx] = (uint8_t) (bufferOut[kidx] ^ _cmac_RB[kidx]); - } + bufferOut[kidx] = (uint8_t)(bufferOut[kidx] ^ _cmac_RB[kidx]); + } } } @@ -56,8 +56,8 @@ static void getCMACSubK2(const uint8_t *bufferK1, uint8_t blockSize, uint8_t pol RotateArrayLeft(bufferK1, bufferOut, blockSize); if ((bufferK1[0] & 0x80) != 0) { for (int kidx = 0; kidx < blockSize; kidx++) { - bufferOut[kidx] = (uint8_t) (bufferOut[kidx] ^ _cmac_RB[kidx]); - } + bufferOut[kidx] = (uint8_t)(bufferOut[kidx] ^ _cmac_RB[kidx]); + } } } @@ -66,32 +66,32 @@ static bool appendBufferCMACSubroutine(uint8_t cryptoType, const uint8_t *keyDat uint16_t newBlockSize = bufferSize; if (bufferSize == 0) { memset(bufferOut, 0x00, blockSize); - bufferOut[0] = (uint8_t) 0x80; - newBlockSize = blockSize; + bufferOut[0] = (uint8_t) 0x80; + newBlockSize = blockSize; } else if ((bufferSize % blockSize) != 0) { - newBlockSize = bufferSize - (bufferSize % blockSize) + blockSize; + newBlockSize = bufferSize - (bufferSize % blockSize) + blockSize; bufferOut[bufferSize] = (uint8_t) 0x80; } if (bufferSize != 0 && (bufferSize % blockSize) == 0) { // Complete block (use K1): - for (int i = bufferSize - blockSize; i < bufferSize; i++) { + for (int i = bufferSize - blockSize; i < bufferSize; i++) { bufferOut[i] ^= bufferK1[i - bufferSize + blockSize]; - } + } } else { // Incomplete block (use K2): - for (int i = bufferSize - blockSize; i < bufferSize; i++) { + for (int i = bufferSize - blockSize; i < bufferSize; i++) { bufferOut[i] ^= bufferK2[i - bufferSize + blockSize]; - } + } } switch (cryptoType) { case CRYPTO_TYPE_3K3DES: - Encrypt3DESBuffer(newBlockSize, bufferOut, bufferOut + bufferSize, keyData, bufferIV); - break; - case CRYPTO_TYPE_AES128: - CryptoAESEncryptBuffer(newBlockSize, bufferOut, bufferOut + bufferSize, keyData, bufferIV); - break; + Encrypt3DESBuffer(newBlockSize, bufferOut, &bufferOut[bufferSize], keyData, bufferIV); + break; + case CRYPTO_TYPE_AES128: + CryptoAESEncryptBuffer(newBlockSize, bufferOut, &bufferOut[bufferSize], keyData, bufferIV); + break; default: - return false; + return false; } memmove(&bufferOut[0], &bufferOut[newBlockSize], newBlockSize); memcpy(_cmac_final, &bufferOut[newBlockSize - blockSize], blockSize); @@ -103,40 +103,90 @@ bool appendBufferCMAC(uint8_t cryptoType, const uint8_t *keyData, uint8_t *buffe uint8_t blockSize, rb; uint8_t *nistL = _cmac_K2; switch (cryptoType) { - case CRYPTO_TYPE_3K3DES: + case CRYPTO_TYPE_3K3DES: blockSize = CRYPTO_3KTDEA_BLOCK_SIZE; - rb = CRYPTO_CMAC_RB64; - memset(_cmac_zeros, 0x00, blockSize); - Encrypt3DESBuffer(blockSize, _cmac_zeros, nistL, _cmac_zeros, keyData); - break; - case CRYPTO_TYPE_AES128: + rb = CRYPTO_CMAC_RB64; + memset(_cmac_zeros, 0x00, blockSize); + Encrypt3DESBuffer(blockSize, _cmac_zeros, nistL, _cmac_zeros, keyData); + break; + case CRYPTO_TYPE_AES128: blockSize = CRYPTO_AES_BLOCK_SIZE; - rb = CRYPTO_CMAC_RB128; - memset(_cmac_zeros, 0x00, blockSize); - CryptoAESEncryptBuffer(blockSize, _cmac_zeros, nistL, _cmac_zeros, keyData); - break; + rb = CRYPTO_CMAC_RB128; + memset(_cmac_zeros, 0x00, blockSize); + CryptoAESEncryptBuffer(blockSize, _cmac_zeros, nistL, _cmac_zeros, keyData); + break; default: - return false; + return false; } getCMACSubK1(nistL, blockSize, rb, _cmac_K1); getCMACSubK2(_cmac_K1, blockSize, rb, _cmac_K2); if (IV == NULL) { IV = _cmac_zeros; - memset(IV, 0x00, blockSize); - return appendBufferCMACSubroutine(cryptoType, keyData, _cmac_K1, _cmac_K2, IV, blockSize, bufferData, bufferSize); + memset(IV, 0x00, blockSize); + return appendBufferCMACSubroutine(cryptoType, keyData, _cmac_K1, _cmac_K2, IV, blockSize, bufferData, bufferSize); } else { return appendBufferCMACSubroutine(cryptoType, keyData, _cmac_K1, _cmac_K2, IV, blockSize, bufferData, bufferSize); } } +bool checkBufferCMAC(uint8_t *bufferData, uint16_t bufferSize, uint16_t checksumSize) { + if (checksumSize > bufferSize) { + return false; + } + if (checksumSize == CRYPTO_3KTDEA_BLOCK_SIZE) { + appendBufferCMAC(CRYPTO_TYPE_3K3DES, SessionKey, &bufferData[bufferSize], bufferSize - checksumSize, SessionIV); + memcpy(_cmac_zeros, &bufferData[bufferSize], checksumSize); + if (memcmp(_cmac_zeros, &bufferData[bufferSize - checksumSize], checksumSize)) { + return false; + } + return true; + } else if (checksumSize == CRYPTO_AES_BLOCK_SIZE) { + appendBufferCMAC(CRYPTO_TYPE_AES128, SessionKey, &bufferData[bufferSize], bufferSize - checksumSize, SessionIV); + memcpy(_cmac_zeros, &bufferData[bufferSize], checksumSize); + if (memcmp(_cmac_zeros, &bufferData[bufferSize - checksumSize], checksumSize)) { + return false; + } + return true; + } + return false; +} + uint16_t appendBufferMAC(const uint8_t *keyData, uint8_t *bufferData, uint16_t bufferSize) { memcpy(&_mac_key24[2 * CRYPTO_DES_BLOCK_SIZE], keyData, CRYPTO_DES_BLOCK_SIZE); memcpy(&_mac_key24[CRYPTO_DES_BLOCK_SIZE], keyData, CRYPTO_DES_BLOCK_SIZE); memcpy(&_mac_key24[0], keyData, CRYPTO_DES_BLOCK_SIZE); memset(&_cmac_zeros[0], 0x00, CRYPTO_DES_BLOCK_SIZE); - Encrypt3DESBuffer(bufferSize, bufferData, &bufferData[3 * CRYPTO_DES_BLOCK_SIZE], _cmac_zeros, keyData); + uint16_t paddedBufferSize = bufferSize; + if ((bufferSize % CRYPTO_DES_BLOCK_SIZE) != 0) { + paddedBufferSize = bufferSize + CRYPTO_DES_BLOCK_SIZE - (bufferSize % CRYPTO_DES_BLOCK_SIZE); + memset(&bufferData[bufferSize], 0x00, paddedBufferSize - bufferSize); + } + Encrypt3DESBuffer(paddedBufferSize, bufferData, &bufferData[paddedBufferSize], _cmac_zeros, keyData); // Copy the 4-byte MAC from the ciphertext (end of the bufferData array): - memcpy(&_cmac_zeros[0], &bufferData[3 * CRYPTO_DES_BLOCK_SIZE + bufferSize - CRYPTO_DES_BLOCK_SIZE], 4); + memcpy(&_cmac_zeros[0], &bufferData[paddedBufferSize - CRYPTO_DES_BLOCK_SIZE], 4); memcpy(&bufferData[bufferSize], &_cmac_zeros[0], 4); return bufferSize + 4; } + +bool checkBufferMAC(uint8_t *bufferData, uint16_t bufferSize, uint16_t checksumSize) { + if (checksumSize > bufferSize) { + return false; + } + const uint8_t *keyData = SessionKey; + memcpy(&_mac_key24[2 * CRYPTO_DES_BLOCK_SIZE], keyData, CRYPTO_DES_BLOCK_SIZE); + memcpy(&_mac_key24[CRYPTO_DES_BLOCK_SIZE], keyData, CRYPTO_DES_BLOCK_SIZE); + memcpy(&_mac_key24[0], keyData, CRYPTO_DES_BLOCK_SIZE); + memset(&_cmac_zeros[0], 0x00, CRYPTO_DES_BLOCK_SIZE); + uint16_t paddedBufferSize = bufferSize; + if ((bufferSize % CRYPTO_DES_BLOCK_SIZE) != 0) { + paddedBufferSize = bufferSize + CRYPTO_DES_BLOCK_SIZE - (bufferSize % CRYPTO_DES_BLOCK_SIZE); + memset(&bufferData[bufferSize], 0x00, paddedBufferSize - bufferSize); + } + Encrypt3DESBuffer(paddedBufferSize, bufferData, &bufferData[paddedBufferSize], _cmac_zeros, keyData); + // Copy the 4-byte MAC from the ciphertext (end of the bufferData array): + memcpy(&_cmac_zeros[0], &bufferData[paddedBufferSize - CRYPTO_DES_BLOCK_SIZE], 4); + if (memcmp(&bufferData[bufferSize - checksumSize], &_cmac_zeros[0], 4)) { + return false; + } + return true; +} diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c index f8997814..24f824aa 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c @@ -226,7 +226,7 @@ CommandStatusIdType CommandDESFireSetCommMode(char *OutParam, const char *InPara if (!strcasecmp_P(valueStr, PSTR("Plaintext"))) { DesfireCommMode = DESFIRE_COMMS_PLAINTEXT; DesfireCommandState.ActiveCommMode = DesfireCommMode; - return COMMAND_INFO_OK; + return COMMAND_INFO_OK; } else if (!strcasecmp_P(valueStr, PSTR("Plaintext:MAC"))) { DesfireCommMode = DESFIRE_COMMS_PLAINTEXT_MAC; DesfireCommandState.ActiveCommMode = DesfireCommMode; @@ -234,11 +234,11 @@ CommandStatusIdType CommandDESFireSetCommMode(char *OutParam, const char *InPara } else if (!strcasecmp_P(valueStr, PSTR("Enciphered:3K3DES"))) { DesfireCommMode = DESFIRE_COMMS_CIPHERTEXT_DES; DesfireCommandState.ActiveCommMode = DesfireCommMode; - return COMMAND_INFO_OK; + return COMMAND_INFO_OK; } else if (!strcasecmp_P(valueStr, PSTR("Enciphered:AES128"))) { DesfireCommMode = DESFIRE_COMMS_CIPHERTEXT_AES128; DesfireCommandState.ActiveCommMode = DesfireCommMode; - return COMMAND_INFO_OK; + return COMMAND_INFO_OK; } snprintf_P(OutParam, TERMINAL_BUFFER_SIZE, PSTR("Options are: Plaintext|Plaintext:MAC|Enciphered:3K3DES|Enciphered:AES128")); return COMMAND_ERR_INVALID_USAGE_ID; diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c index 8021d40f..b108d47f 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFirePICCControl.c @@ -151,7 +151,7 @@ uint8_t ReadDataFilterSetup(uint8_t CommSettings) { TransferState.WriteData.Encryption.Func = &CryptoAESEncrypt_CBCSend; memset(SessionIV, 0, sizeof(SessionIVByteSize)); SessionIVByteSize = CRYPTO_AES_KEY_SIZE; - default: + default: return STATUS_PARAMETER_ERROR; } return STATUS_OPERATION_OK; @@ -160,12 +160,12 @@ uint8_t ReadDataFilterSetup(uint8_t CommSettings) { uint8_t WriteDataFilterSetup(uint8_t CommSettings) { switch (CommSettings) { case DESFIRE_COMMS_PLAINTEXT: - TransferState.Checksums.UpdateFunc = NULL; - TransferState.Checksums.FinalFunc = NULL; - TransferState.Checksums.MACData.CryptoChecksumFunc.TDEAFunc = NULL; + TransferState.Checksums.UpdateFunc = NULL; + TransferState.Checksums.FinalFunc = NULL; + TransferState.Checksums.MACData.CryptoChecksumFunc.TDEAFunc = NULL; memset(SessionIV, 0, sizeof(SessionIVByteSize)); - SessionIVByteSize = 0; - break; + SessionIVByteSize = 0; + break; case DESFIRE_COMMS_PLAINTEXT_MAC: TransferState.Checksums.UpdateFunc = &TransferChecksumUpdateMACTDEA; TransferState.Checksums.FinalFunc = &TransferChecksumFinalMACTDEA; @@ -181,8 +181,8 @@ uint8_t WriteDataFilterSetup(uint8_t CommSettings) { memset(SessionIV, 0, sizeof(SessionIVByteSize)); SessionIVByteSize = CRYPTO_AES_KEY_SIZE; break; - case DESFIRE_COMMS_CIPHERTEXT_AES128: - // A.k.a., CommMode=FULL from NXP application note AN12343: + case DESFIRE_COMMS_CIPHERTEXT_AES128: + // A.k.a., CommMode=FULL from NXP application note AN12343: TransferState.Checksums.UpdateFunc = &TransferChecksumUpdateCMAC; TransferState.Checksums.FinalFunc = &TransferChecksumFinalCMAC; TransferState.Checksums.MACData.CRCA = ISO14443A_CRCA_INIT; // TODO ??? diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.c index 5283956b..8abec899 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.c @@ -149,49 +149,48 @@ bool DesfireCheckParityBits(uint8_t *Buffer, uint16_t BitCount) { uint16_t DesfirePreprocessAPDU(uint8_t CommMode, uint8_t *Buffer, uint16_t BufferSize) { switch (CommMode) { case DESFIRE_COMMS_PLAINTEXT: - // Remove the CRCA bytes at the end of the buffer: - return MAX(0, BufferSize - 2); - case DESFIRE_COMMS_PLAINTEXT_MAC: { - uint16_t ChecksumBytes = 0; + // Remove the CRCA bytes at the end of the buffer: + return MAX(0, BufferSize - 2); + case DESFIRE_COMMS_PLAINTEXT_MAC: { + uint16_t ChecksumBytes = 0; if (DesfireCommandState.CryptoMethodType == CRYPTO_TYPE_DES || DesfireCommandState.CryptoMethodType == CRYPTO_TYPE_2KTDEA) { ChecksumBytes = 4; - if (!checkBufferMAC(Buffer, BufferSize, ChecksumBytes)) { - return 0; - } - } - else if (DesfireCommandState.CryptoMethodType == CRYPTO_TYPE_3K3DES) { - ChecksumBytes = CRYPTO_3KTDEA_BLOCK_SIZE; - if (!checkBufferCMAC(Buffer, BufferSize, ChecksumBytes)) { - return 0; - } - } else { - ChecksumBytes = CRYPTO_AES_BLOCK_SIZE; - if (!checkBufferCMAC(Buffer, BufferSize, ChecksumBytes)) { - return 0; - } - } - return MAX(0, BufferSize - ChecksumBytes); - } + if (!checkBufferMAC(Buffer, BufferSize, ChecksumBytes)) { + return 0; + } + } else if (DesfireCommandState.CryptoMethodType == CRYPTO_TYPE_3K3DES) { + ChecksumBytes = CRYPTO_3KTDEA_BLOCK_SIZE; + if (!checkBufferCMAC(Buffer, BufferSize, ChecksumBytes)) { + return 0; + } + } else { + ChecksumBytes = CRYPTO_AES_BLOCK_SIZE; + if (!checkBufferCMAC(Buffer, BufferSize, ChecksumBytes)) { + return 0; + } + } + return MAX(0, BufferSize - ChecksumBytes); + } case DESFIRE_COMMS_CIPHERTEXT_DES: { - Decrypt3DESBuffer(BufferSize, Buffer, &Buffer[BufferSize], SessionIV, SessionKey); + Decrypt3DESBuffer(BufferSize, Buffer, &Buffer[BufferSize], SessionIV, SessionKey); memmove(&Buffer[0], &Buffer[BufferSize], BufferSize); - uint16_t ChecksumBytes = CRYPTO_3KTDEA_BLOCK_SIZE; - if (!checkBufferCMAC(Buffer, BufferSize, ChecksumBytes)) { - return 0; - } - return MAX(0, BufferSize - ChecksumBytes); - } - case DESFIRE_COMMS_CIPHERTEXT_AES128: { - CryptoAESDecryptBuffer(BufferSize, Buffer, &Buffer[BufferSize], SessionIV, SessionKey); + uint16_t ChecksumBytes = CRYPTO_3KTDEA_BLOCK_SIZE; + if (!checkBufferCMAC(Buffer, BufferSize, ChecksumBytes)) { + return 0; + } + return MAX(0, BufferSize - ChecksumBytes); + } + case DESFIRE_COMMS_CIPHERTEXT_AES128: { + CryptoAESDecryptBuffer(BufferSize, Buffer, &Buffer[BufferSize], SessionIV, SessionKey); memmove(&Buffer[0], &Buffer[BufferSize], BufferSize); - uint16_t ChecksumBytes = CRYPTO_AES_BLOCK_SIZE; - if (!checkBufferCMAC(Buffer, BufferSize, ChecksumBytes)) { - return 0; - } - return MAX(0, BufferSize - ChecksumBytes); - } - default: - break; + uint16_t ChecksumBytes = CRYPTO_AES_BLOCK_SIZE; + if (!checkBufferCMAC(Buffer, BufferSize, ChecksumBytes)) { + return 0; + } + return MAX(0, BufferSize - ChecksumBytes); + } + default: + break; } return 0; } @@ -199,48 +198,48 @@ uint16_t DesfirePreprocessAPDU(uint8_t CommMode, uint8_t *Buffer, uint16_t Buffe uint16_t DesfirePostprocessAPDU(uint8_t CommMode, uint8_t *Buffer, uint16_t BufferSize) { switch (CommMode) { case DESFIRE_COMMS_PLAINTEXT: - ISO14443AAppendCRCA(Buffer, BufferSize); - return BufferSize + 2; - case DESFIRE_COMMS_PLAINTEXT_MAC: { - if (DesfireCommandState.CryptoMethodType == CRYPTO_TYPE_DES || DesfireCommandState.CryptoMethodType == CRYPTO_TYPE_2KTDEA) { - return appendBufferMAC(SessionKey, Buffer, BufferSize); - } else { - // AES-128 or 3DES: - uint16_t MacedBytes = appendBufferCMAC(DesfireCommandState.CryptoMethodType, SessionKey, Buffer, BufferSize, SessionIV); - memcpy(SessionIV, &Buffer[BufferSize], MacedBytes - BufferSize); + ISO14443AAppendCRCA(Buffer, BufferSize); + return BufferSize + 2; + case DESFIRE_COMMS_PLAINTEXT_MAC: { + if (DesfireCommandState.CryptoMethodType == CRYPTO_TYPE_DES || DesfireCommandState.CryptoMethodType == CRYPTO_TYPE_2KTDEA) { + return appendBufferMAC(SessionKey, Buffer, BufferSize); + } else { + // AES-128 or 3DES: + uint16_t MacedBytes = appendBufferCMAC(DesfireCommandState.CryptoMethodType, SessionKey, Buffer, BufferSize, SessionIV); + memcpy(SessionIV, &Buffer[BufferSize], MacedBytes - BufferSize); return MacedBytes; - } - break; - } - case DESFIRE_COMMS_CIPHERTEXT_DES: { - // TripleDES: - uint16_t CryptoBlockSize = CRYPTO_3KTDEA_BLOCK_SIZE; - uint16_t BlockPadding = 0; - if ((BufferSize % CryptoBlockSize) != 0) { - BlockPadding = CryptoBlockSize - (BufferSize % CryptoBlockSize); - } - uint16_t MacedBytes = appendBufferCMAC(CRYPTO_TYPE_3K3DES, SessionKey, Buffer, BufferSize, SessionIV); + } + break; + } + case DESFIRE_COMMS_CIPHERTEXT_DES: { + // TripleDES: + uint16_t CryptoBlockSize = CRYPTO_3KTDEA_BLOCK_SIZE; + uint16_t BlockPadding = 0; + if ((BufferSize % CryptoBlockSize) != 0) { + BlockPadding = CryptoBlockSize - (BufferSize % CryptoBlockSize); + } + uint16_t MacedBytes = appendBufferCMAC(CRYPTO_TYPE_3K3DES, SessionKey, Buffer, BufferSize, SessionIV); memset(&Buffer[MacedBytes], 0x00, BlockPadding); - uint16_t XferBytes = MacedBytes + BlockPadding; - Encrypt3DESBuffer(XferBytes, Buffer, &Buffer[XferBytes], SessionIV, SessionKey); - memmove(&Buffer[0], &Buffer[XferBytes], XferBytes); - return XferBytes; - } - case DESFIRE_COMMS_CIPHERTEXT_AES128: { - uint16_t CryptoBlockSize = CRYPTO_AES_BLOCK_SIZE; - uint16_t BlockPadding = 0; - if ((BufferSize % CryptoBlockSize) != 0) { - BlockPadding = CryptoBlockSize - (BufferSize % CryptoBlockSize); - } - uint16_t MacedBytes = appendBufferCMAC(CRYPTO_TYPE_AES128, SessionKey, Buffer, BufferSize, SessionIV); + uint16_t XferBytes = MacedBytes + BlockPadding; + Encrypt3DESBuffer(XferBytes, Buffer, &Buffer[XferBytes], SessionIV, SessionKey); + memmove(&Buffer[0], &Buffer[XferBytes], XferBytes); + return XferBytes; + } + case DESFIRE_COMMS_CIPHERTEXT_AES128: { + uint16_t CryptoBlockSize = CRYPTO_AES_BLOCK_SIZE; + uint16_t BlockPadding = 0; + if ((BufferSize % CryptoBlockSize) != 0) { + BlockPadding = CryptoBlockSize - (BufferSize % CryptoBlockSize); + } + uint16_t MacedBytes = appendBufferCMAC(CRYPTO_TYPE_AES128, SessionKey, Buffer, BufferSize, SessionIV); memset(&Buffer[MacedBytes], 0x00, BlockPadding); - uint16_t XferBytes = MacedBytes + BlockPadding; - CryptoAESEncryptBuffer(XferBytes, Buffer, &Buffer[XferBytes], SessionIV, SessionKey); - memmove(&Buffer[0], &Buffer[XferBytes], XferBytes); - return XferBytes; - } - default: - break; + uint16_t XferBytes = MacedBytes + BlockPadding; + CryptoAESEncryptBuffer(XferBytes, Buffer, &Buffer[XferBytes], SessionIV, SessionKey); + memmove(&Buffer[0], &Buffer[XferBytes], XferBytes); + return XferBytes; + } + default: + break; } return 0; } diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.h b/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.h index 0b81298e..0f48759f 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.h +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireUtils.h @@ -53,21 +53,21 @@ uint16_t DesfireAddParityBits(uint8_t *Buffer, uint16_t bits); uint16_t DesfireRemoveParityBits(uint8_t *Buffer, uint16_t BitCount); bool DesfireCheckParityBits(uint8_t *Buffer, uint16_t BitCount); -/* Add utility wrapper functions to perform pre and postprocessing on - * the raw input APDU commands sent by the PCD depending on which - * CommMode (PLAINTEXT|PLAINTEXT-MAC|ENCIPHERED-CMAC-3DES|ECIPHERED-CMAC-AES128) - * setting is active. +/* Add utility wrapper functions to perform pre and postprocessing on + * the raw input APDU commands sent by the PCD depending on which + * CommMode (PLAINTEXT|PLAINTEXT-MAC|ENCIPHERED-CMAC-3DES|ECIPHERED-CMAC-AES128) + * setting is active. * - * The implementation is adapted from the Java sources at - * @github/andrade/nfcjlib (in the DESFireEV1 source files). - * We will use the conventions in that library to update the SessionIV buffer - * when the next rounds of data are exchanged. Note that the SessionIV and - * SessionKey arrays are initialized in the Authenticate(Legacy|ISO|AES) commands + * The implementation is adapted from the Java sources at + * @github/andrade/nfcjlib (in the DESFireEV1 source files). + * We will use the conventions in that library to update the SessionIV buffer + * when the next rounds of data are exchanged. Note that the SessionIV and + * SessionKey arrays are initialized in the Authenticate(Legacy|ISO|AES) commands * used to initiate the working session from PCD <--> PICC. * - * Helper methods to format and encode quirky or pathological cases of the - * CommSettings and wrapped APDU format combinations are defined statically in the - * C source file to save space in the symbol table for the firmware (ELF) binary. + * Helper methods to format and encode quirky or pathological cases of the + * CommSettings and wrapped APDU format combinations are defined statically in the + * C source file to save space in the symbol table for the firmware (ELF) binary. */ uint16_t DesfirePreprocessAPDU(uint8_t CommMode, uint8_t *Buffer, uint16_t BufferSize); uint16_t DesfirePostprocessAPDU(uint8_t CommMode, uint8_t *Buffer, uint16_t BufferSize); diff --git a/Firmware/Chameleon-Mini/Application/MifareDESFire.c b/Firmware/Chameleon-Mini/Application/MifareDESFire.c index 01c61834..1a4ec777 100644 --- a/Firmware/Chameleon-Mini/Application/MifareDESFire.c +++ b/Firmware/Chameleon-Mini/Application/MifareDESFire.c @@ -175,9 +175,9 @@ uint16_t MifareDesfireProcess(uint8_t *Buffer, uint16_t BitCount) { size_t ByteCount = (BitCount + BITS_PER_BYTE - 1) / BITS_PER_BYTE; DesfireCmdCLA = Buffer[0]; if (BitCount == 0) { - return ISO14443A_APP_NO_RESPONSE; + return ISO14443A_APP_NO_RESPONSE; } else if ((ByteCount >= 8 && DesfireCLA(Buffer[0]) && Buffer[2] == 0x00 && - Buffer[3] == 0x00 && (Buffer[4] == ByteCount - 6 || Buffer[4] == ByteCount - 8)) || Iso7816CLA(DesfireCmdCLA)) { + Buffer[3] == 0x00 && (Buffer[4] == ByteCount - 6 || Buffer[4] == ByteCount - 8)) || Iso7816CLA(DesfireCmdCLA)) { // Wrapped native command structure: /* Unwrap the PDU from ISO 7816-4 */ // Check CRC bytes appended to the buffer: @@ -224,13 +224,13 @@ uint16_t MifareDesfireAppProcess(uint8_t *Buffer, uint16_t BitCount) { // return MifareDesfireProcess(Buffer, BitCount); //} if (ByteCount >= 6 && DesfireCLA(Buffer[0]) && Buffer[2] == 0x00 && - Buffer[3] == 0x00 && Buffer[4] == ByteCount - 6) { + Buffer[3] == 0x00 && Buffer[4] == ByteCount - 6) { uint16_t IncomingByteCount = (BitCount + BITS_PER_BYTE - 1) / BITS_PER_BYTE; uint16_t UnwrappedBitCount = DesfirePreprocessAPDU(DesfireCommMode, Buffer, IncomingByteCount) * BITS_PER_BYTE; - uint16_t ProcessedBitCount = MifareDesfireProcess(Buffer, UnwrappedBitCount); + uint16_t ProcessedBitCount = MifareDesfireProcess(Buffer, UnwrappedBitCount); uint16_t ProcessedByteCount = (ProcessedBitCount + BITS_PER_BYTE - 1) / BITS_PER_BYTE; - ProcessedBitCount = DesfirePostprocessAPDU(DesfireCommMode, Buffer, ProcessedByteCount) * BITS_PER_BYTE; - return ProcessedBitCount; + ProcessedBitCount = DesfirePostprocessAPDU(DesfireCommMode, Buffer, ProcessedByteCount) * BITS_PER_BYTE; + return ProcessedBitCount; } else if (ByteCount == 4 && Buffer[2] == 0x37 && Buffer[3] == 0xC8) { // NXP-based PCD sent a "keep alive" response of ACK, // so we respond with a corresponding NAK (with CRCA bytes appended): @@ -243,33 +243,33 @@ uint16_t MifareDesfireAppProcess(uint8_t *Buffer, uint16_t BitCount) { memcpy(&ISO7816PrologueBytes[0], Buffer, 2); memmove(&Buffer[0], &Buffer[2], ByteCount - 2); uint16_t IncomingByteCount = (BitCount + BITS_PER_BYTE - 1) / BITS_PER_BYTE; - uint16_t UnwrappedBitCount = DesfirePreprocessAPDU(DesfireCommMode, Buffer, IncomingByteCount) * BITS_PER_BYTE; - uint16_t ProcessedBitCount = MifareDesfireProcess(Buffer, UnwrappedBitCount); + uint16_t UnwrappedBitCount = DesfirePreprocessAPDU(DesfireCommMode, Buffer, IncomingByteCount) * BITS_PER_BYTE; + uint16_t ProcessedBitCount = MifareDesfireProcess(Buffer, UnwrappedBitCount); uint16_t ProcessedByteCount = (ProcessedBitCount + BITS_PER_BYTE - 1) / BITS_PER_BYTE; /* Append the same ISO7816 prologue bytes to the response: */ memmove(&Buffer[2], &Buffer[0], ProcessedByteCount); memcpy(&Buffer[0], &ISO7816PrologueBytes[0], 2); - ProcessedBitCount = DesfirePostprocessAPDU(DesfireCommMode, Buffer, ProcessedByteCount) * BITS_PER_BYTE; + ProcessedBitCount = DesfirePostprocessAPDU(DesfireCommMode, Buffer, ProcessedByteCount) * BITS_PER_BYTE; return ProcessedBitCount; } else if ((ReturnedBytes = CallInstructionHandler(Buffer, ByteCount)) != ISO14443A_APP_NO_RESPONSE) { /* This case should handle non-wrappped native commands. No pre/postprocessing afterwards: */ - return ReturnedBytes; + return ReturnedBytes; } else if (!AnticolNoResp) { - /* This case is to exchange anticollision loop and RATS data. No need to pre/postprocess it depending - * on the CommMode, which has not been set yet if we reach this point: - */ - uint16_t PiccProcessRespBytes = ISO144433APiccProcess(Buffer, BitCount); - if (PiccProcessRespBytes == ISO14443A_APP_NO_RESPONSE) { - // Stop pesky USB readers trying to autodetect all tag types by brute-force enumeration - // from interfering with making it into the command exchange (DESFIRE_IDLE) states. - // Once the anticollision and/or RATS has completed, set this flag to keep it from - // resending that initial handshaking until the AppReset() function is called on a timeout. - // N.b., the ACR-122 reader does this repeatedly when trying to run the LibNFC testing code - // even when the reader has not been put in scan mode -- - // and it really screws things up timing-wise! - AnticolNoResp = true; - } - return PiccProcessRespBytes; + /* This case is to exchange anticollision loop and RATS data. No need to pre/postprocess it depending + * on the CommMode, which has not been set yet if we reach this point: + */ + uint16_t PiccProcessRespBytes = ISO144433APiccProcess(Buffer, BitCount); + if (PiccProcessRespBytes == ISO14443A_APP_NO_RESPONSE) { + // Stop pesky USB readers trying to autodetect all tag types by brute-force enumeration + // from interfering with making it into the command exchange (DESFIRE_IDLE) states. + // Once the anticollision and/or RATS has completed, set this flag to keep it from + // resending that initial handshaking until the AppReset() function is called on a timeout. + // N.b., the ACR-122 reader does this repeatedly when trying to run the LibNFC testing code + // even when the reader has not been put in scan mode -- + // and it really screws things up timing-wise! + AnticolNoResp = true; + } + return PiccProcessRespBytes; } return ISO14443A_APP_NO_RESPONSE; } diff --git a/Firmware/Chameleon-Mini/Makefile b/Firmware/Chameleon-Mini/Makefile index b879f71c..93324f1a 100644 --- a/Firmware/Chameleon-Mini/Makefile +++ b/Firmware/Chameleon-Mini/Makefile @@ -251,7 +251,7 @@ program-latest: check_size avrdude $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_APP_LATEST) $(AVRDUDE_WRITE_EEPROM_LATEST) # Program the device using batchisp and the DFU bootloader -# Note that the device has to be in bootloader mode already-ffunction-sections -fdata-sections +# Note that the device has to be in bootloader mode already-ffunction-sections -fdata-sections dfu-flip: $(TARGET).hex $(TARGET).eep check_size cp $(TARGET).eep EEPROM.hex batchisp -hardware usb -device $(MCU) -operation erase f memory FLASH loadbuffer $(TARGET).hex program verify memory EEPROM loadbuffer EEPROM.hex program verify start reset 0