Merge pull request #1375 from merlokk/desf_changekey

Desfire changekey
This commit is contained in:
Oleg Moiseenko 2021-07-19 20:04:28 +03:00 committed by GitHub
commit a395873094
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 389 additions and 31 deletions

View file

@ -369,7 +369,7 @@ typedef struct aidhdr {
static int CmdHelp(const char *Cmd);
static const char *getEncryptionAlgoStr(uint8_t algo) {
/*static const char *getEncryptionAlgoStr(uint8_t algo) {
switch (algo) {
case MFDES_ALGO_AES :
return "AES";
@ -382,7 +382,7 @@ static const char *getEncryptionAlgoStr(uint8_t algo) {
default :
return "";
}
}
}*/
/*
The 7 MSBits (= n) code the storage size itself based on 2^n,
the LSBit is set to '0' if the size is exactly 2^n
@ -1051,7 +1051,7 @@ static int handler_desfire_freemem(uint32_t *free_mem) {
return res;
}
static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t new_algo, uint8_t *old_key, uint8_t old_algo, uint8_t aes_version) {
/*static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t new_algo, uint8_t *old_key, uint8_t old_algo, uint8_t aes_version) {
if (new_key == NULL || old_key == NULL) {
return PM3_EINVARG;
@ -1060,10 +1060,10 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n
// AID == 000000 6bits LSB needs to be 0
key_no &= 0x0F;
/*
Desfire treats Des keys as TDes but with the first half = 2nd half
As such, we should be able to convert the Des to TDes then run the code as TDes
*/
if (new_algo == MFDES_ALGO_DES) {
memcpy(&new_key[8], new_key, 8);
new_algo = MFDES_ALGO_3DES;
@ -1074,10 +1074,10 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n
old_algo = MFDES_ALGO_3DES;
}
/*
*
* Because new crypto methods can be setup only at application creation,
* changing the card master key to one of them require a key_no tweak.
*/
*
if (0x000000 == tag->selected_application) {
// PICC master key, 6bits LSB needs to be 0
@ -1096,13 +1096,13 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n
break;
}
}
/*
*
keyno 1b
key 8b
cpy 8b
crc 2b
padding
*/
*
// Variable length ciphered key data 24-42 bytes plus padding..
uint8_t data[64] = {key_no};
@ -1113,13 +1113,13 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n
uint8_t new_key_length = 16;
switch (new_algo) {
/*
*
// We have converted the DES to 3DES above,so this will never hit
case MFDES_ALGO_DES:
memcpy(data + cmdcnt + 1, new_key, new_key_length);
memcpy(data + cmdcnt + 1 + new_key_length, new_key, new_key_length);
break;
*/
*
case MFDES_ALGO_3DES:
case MFDES_ALGO_AES:
new_key_length = 16;
@ -1251,12 +1251,12 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n
DropFieldDesfire();
if (!p) {
/*
*
Note in my testing on an EV1, the AES password did change, with the number of returned bytes was 8, expected 9 <status><8 byte cmac>
As such !p is true and the code reports "Error on changing key"; so comment back to user until its fixed.
Note: as at 19 May 2021, with the sn = 1 patch above, this should no longer be reachable!
*/
*
if (new_algo == MFDES_ALGO_AES) {
PrintAndLogEx(WARNING, "AES Key may have been changed, please check new password with the auth command.");
}
@ -1264,17 +1264,17 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n
return PM3_ESOFT;
}
/*
*
* If we changed the current authenticated key, we are not authenticated
* anymore.
*/
*
if (key_no == tag->authenticated_key_no) {
free(tag->session_key);
tag->session_key = NULL;
}
return PM3_SUCCESS;
}
}*/
// --- GET SIGNATURE
static int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, size_t signature_len, nxp_cardtype_t card_type) {
@ -3805,7 +3805,7 @@ static int CmdHF14ADesBruteApps(const char *Cmd) {
DropFieldDesfire();
return PM3_SUCCESS;
}
/*
static int CmdHF14ADesChangeKey(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mfdes changekey",
@ -3900,7 +3900,7 @@ static int CmdHF14ADesChangeKey(const char *Cmd) {
PrintAndLogEx(FAILED, "Change key ( " _RED_("fail") " )");
}
return res;
}
}*/
// MIAFRE DESFire Authentication
@ -4885,6 +4885,129 @@ static int CmdHF14ADesDefault(const char *Cmd) {
return PM3_SUCCESS;
}
static int CmdHF14ADesChangeKey(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mfdes changekey",
"Change PICC/Application key. Needs to provide keynum/key for a valid authentication (may get from default parameters).",
"hf mfdes changekey --aid 123456 -> execute with default factory setup");
void *argtable[] = {
arg_param_begin,
arg_lit0("a", "apdu", "show APDU requests and responses"),
arg_lit0("v", "verbose", "show technical data"),
arg_int0("n", "keyno", "<keyno>", "Key number"),
arg_str0("t", "algo", "<DES/2TDEA/3TDEA/AES>", "Crypt algo: DES, 2TDEA, 3TDEA, AES"),
arg_str0("k", "key", "<Key>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0("f", "kdf", "<none/AN10922/gallagher>", "Key Derivation Function (KDF): None, AN10922, Gallagher"),
arg_str0("i", "kdfi", "<kdfi>", "KDF input (HEX 1-31 bytes)"),
arg_str0("m", "cmode", "<plain/mac/encrypt>", "Communicaton mode: plain/mac/encrypt"),
arg_str0("c", "ccset", "<native/niso/iso>", "Communicaton command set: native/niso/iso"),
arg_str0("s", "schann", "<d40/ev1/ev2>", "Secure channel: d40/ev1/ev2"),
arg_str0(NULL, "aid", "<app id hex>", "Application ID of application (3 hex bytes, big endian)"),
arg_str0(NULL, "oldalgo", "<DES/2TDEA/3TDEA/AES>", "Old key crypto algorithm: DES, 2TDEA, 3TDEA, AES"),
arg_str0(NULL, "oldkey", "<old key>", "Old key (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_int0(NULL, "newkeyno", "<keyno>", "Key number for change"),
arg_str0(NULL, "newalgo", "<DES/2TDEA/3TDEA/AES>", "New key crypto algorithm: DES, 2TDEA, 3TDEA, AES"),
arg_str0(NULL, "newkey", "<new key>", "New key (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
arg_str0(NULL, "newver", "<version hex>", "New key's version (1 hex byte)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
bool APDULogging = arg_get_lit(ctx, 1);
bool verbose = arg_get_lit(ctx, 2);
DesfireContext dctx;
int securechann = defaultSecureChannel;
uint32_t appid = 0x000000;
int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMEncrypted, &appid);
if (res) {
CLIParserFree(ctx);
return res;
}
int oldkeytype = dctx.keyType;
if (CLIGetOptionList(arg_get_str(ctx, 12), DesfireAlgoOpts, &oldkeytype))
return PM3_ESOFT;
uint8_t oldkey[DESFIRE_MAX_KEY_SIZE] = {0};
uint8_t keydata[200] = {0};
int oldkeylen = sizeof(keydata);
CLIGetHexWithReturn(ctx, 13, keydata, &oldkeylen);
if (oldkeylen && oldkeylen != desfire_get_key_length(oldkeytype)) {
PrintAndLogEx(ERR, "%s old key must have %d bytes length instead of %d.", CLIGetOptionListStr(DesfireAlgoOpts, oldkeytype), desfire_get_key_length(oldkeytype), oldkeylen);
return PM3_EINVARG;
}
if (oldkeylen)
memcpy(oldkey, keydata, oldkeylen);
uint8_t newkeynum = arg_get_int_def(ctx, 14, 0);
int newkeytype = oldkeytype;
if (CLIGetOptionList(arg_get_str(ctx, 15), DesfireAlgoOpts, &newkeytype))
return PM3_ESOFT;
uint8_t newkey[DESFIRE_MAX_KEY_SIZE] = {0};
memset(keydata, 0x00, sizeof(keydata));
int keylen = sizeof(keydata);
CLIGetHexWithReturn(ctx, 16, keydata, &keylen);
if (keylen && keylen != desfire_get_key_length(newkeytype)) {
PrintAndLogEx(ERR, "%s new key must have %d bytes length instead of %d.", CLIGetOptionListStr(DesfireAlgoOpts, newkeytype), desfire_get_key_length(newkeytype), keylen);
return PM3_EINVARG;
}
if (keylen)
memcpy(newkey, keydata, keylen);
uint32_t newkeyver = 0x100;
res = arg_get_u32_hexstr_def_nlen(ctx, 17, 0x100, &newkeyver, 1, true);
if (res == 2) {
PrintAndLogEx(ERR, "Key version must have 1 bytes length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
SetAPDULogging(APDULogging);
CLIParserFree(ctx);
// if we change the same key
if (oldkeylen == 0 && newkeynum == dctx.keyNum) {
oldkeytype = dctx.keyType;
memcpy(oldkey, dctx.key, desfire_get_key_length(dctx.keyType));
}
if (appid == 0x000000) {
PrintAndLogEx(WARNING, "Changing the root aid (0x000000)");
}
if (appid)
PrintAndLogEx(INFO, _CYAN_("Changing key in the application: ") _YELLOW_("%06x"), appid);
else
PrintAndLogEx(INFO, _CYAN_("Changing PICC key"));
PrintAndLogEx(INFO, "auth key %d: %s [%d] %s", dctx.keyNum, CLIGetOptionListStr(DesfireAlgoOpts, dctx.keyType), desfire_get_key_length(dctx.keyType), sprint_hex(dctx.key, desfire_get_key_length(dctx.keyType)));
PrintAndLogEx(INFO, "changing key number " _YELLOW_("0x%02x") " (%d)", newkeynum, newkeynum);
PrintAndLogEx(INFO, "old key: %s [%d] %s", CLIGetOptionListStr(DesfireAlgoOpts, oldkeytype), desfire_get_key_length(oldkeytype), sprint_hex(oldkey, desfire_get_key_length(oldkeytype)));
PrintAndLogEx(INFO, "new key: %s [%d] %s", CLIGetOptionListStr(DesfireAlgoOpts, newkeytype), desfire_get_key_length(newkeytype), sprint_hex(newkey, desfire_get_key_length(newkeytype)));
if (newkeyver < 0x100 || newkeytype == T_AES)
PrintAndLogEx(INFO, "new key version: 0x%02x", newkeyver & 0x00);
res = DesfireSelectAndAuthenticate(&dctx, securechann, appid, verbose);
if (res != PM3_SUCCESS) {
DropField();
return res;
}
DesfireSetCommMode(&dctx, DCMEncryptedPlain);
res = DesfireChangeKey(&dctx, (appid == 0x000000) && (newkeynum == 0) && (dctx.keyNum == 0), newkeynum, newkeytype, newkeyver, newkey, oldkeytype, oldkey, true);
if (res == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "Change key " _GREEN_("ok") " ");
} else {
PrintAndLogEx(FAILED, "Change key " _RED_("failed") " ");
}
DropField();
return res;
}
static int CmdHF14ADesCreateApp(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mfdes createapp",
@ -4938,7 +5061,7 @@ static int CmdHF14ADesCreateApp(const char *Cmd) {
arg_str0(NULL, "aid", "<app id hex>", "Application ID for create. Mandatory. (3 hex bytes, big endian)"),
arg_str0(NULL, "fid", "<file id hex>", "ISO file ID. Forbidden values: 0000 3F00, 3FFF, FFFF. (2 hex bytes, big endian). If specified - enable iso file id over all the files in the app."),
arg_str0(NULL, "dfname", "<df name str>", "ISO DF Name 1..16 chars string"),
arg_str0(NULL, "ks1", "<key settings HEX>", "Key settings 1 (HEX 1 byte). Application Master Key Settings. default 0x2f"),
arg_str0(NULL, "ks1", "<key settings HEX>", "Key settings 1 (HEX 1 byte). Application Master Key Settings. default 0x0f"),
arg_str0(NULL, "ks2", "<key settings HEX>", "Key settings 2 (HEX 1 byte). default 0x0e"),
arg_str0(NULL, "dstalgo", "<DES/2TDEA/3TDEA/AES>", "Application key crypt algo: DES, 2TDEA, 3TDEA, AES. default DES"),
arg_int0(NULL, "numkeys", "<number of keys>", "Keys count. 0x00..0x0e. default 0x0e"),
@ -4974,8 +5097,8 @@ static int CmdHF14ADesCreateApp(const char *Cmd) {
int dfnamelen = 16;
CLIGetStrWithReturn(ctx, 14, dfname, &dfnamelen);
uint32_t ks1 = 0x2f;
res = arg_get_u32_hexstr_def_nlen(ctx, 15, 0x2f, &ks1, 1, true);
uint32_t ks1 = 0x0f;
res = arg_get_u32_hexstr_def_nlen(ctx, 15, 0x0f, &ks1, 1, true);
if (res == 2) {
PrintAndLogEx(ERR, "Key settings 1 must have 1 byte length");
return PM3_EINVARG;
@ -5767,7 +5890,7 @@ static command_t CommandTable[] = {
// {"ndefread", CmdHF14aDesNDEFRead, IfPm3Iso14443a, "Prints NDEF records from card"},
// {"mad", CmdHF14aDesMAD, IfPm3Iso14443a, "Prints MAD records from card"},
{"-----------", CmdHelp, IfPm3Iso14443a, "------------------------ " _CYAN_("Keys") " -----------------------"},
{"changekey", CmdHF14ADesChangeKey, IfPm3Iso14443a, "Change Key"},
{"changekey", CmdHF14ADesChangeKey, IfPm3Iso14443a, "[new]Change Key"},
{"chkeysettings", CmdHF14ADesChKeySettings, IfPm3Iso14443a, "[new]Change Key Settings"},
{"getkeysettings", CmdHF14ADesGetKeySettings, IfPm3Iso14443a, "[new]Get Key Settings"},
{"getkeyversions", CmdHF14ADesGetKeyVersions, IfPm3Iso14443a, "[new]Get Key Versions"},

View file

@ -65,6 +65,54 @@ void des_decrypt_cbc(void *out, const void *in, const int length, const void *ke
mbedtls_des_crypt_cbc(&ctx, MBEDTLS_DES_DECRYPT, length, iv, in, out);
}
void des3_encrypt(void *out, const void *in, const void *key, uint8_t keycount) {
switch (keycount) {
case 1:
des_encrypt(out, in, key);
break;
case 2: {
mbedtls_des3_context ctx3;
mbedtls_des3_set2key_enc(&ctx3, key);
mbedtls_des3_crypt_ecb(&ctx3, in, out);
mbedtls_des3_free(&ctx3);
break;
}
case 3: {
mbedtls_des3_context ctx3;
mbedtls_des3_set3key_enc(&ctx3, key);
mbedtls_des3_crypt_ecb(&ctx3, in, out);
mbedtls_des3_free(&ctx3);
break;
}
default:
break;
}
}
void des3_decrypt(void *out, const void *in, const void *key, uint8_t keycount) {
switch (keycount) {
case 1:
des_encrypt(out, in, key);
break;
case 2: {
mbedtls_des3_context ctx3;
mbedtls_des3_set2key_dec(&ctx3, key);
mbedtls_des3_crypt_ecb(&ctx3, in, out);
mbedtls_des3_free(&ctx3);
break;
}
case 3: {
mbedtls_des3_context ctx3;
mbedtls_des3_set3key_dec(&ctx3, key);
mbedtls_des3_crypt_ecb(&ctx3, in, out);
mbedtls_des3_free(&ctx3);
break;
}
default:
break;
}
}
// NIST Special Publication 800-38A — Recommendation for block cipher modes of operation: methods and techniques, 2001.
int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length) {
uint8_t iiv[16] = {0};

View file

@ -22,6 +22,8 @@ void des_encrypt_ecb(void *out, const void *in, const int length, const void *ke
void des_decrypt_ecb(void *out, const void *in, const int length, const void *key);
void des_encrypt_cbc(void *out, const void *in, const int length, const void *key, uint8_t *iv);
void des_decrypt_cbc(void *out, const void *in, const int length, const void *key, uint8_t *iv);
void des3_encrypt(void *out, const void *in, const void *key, uint8_t keycount);
void des3_decrypt(void *out, const void *in, const void *key, uint8_t keycount);
int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length);
int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length);

View file

@ -764,7 +764,15 @@ int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel
des_decrypt(encRndB, rotRndB, key->data);
memcpy(both + rndlen, encRndB, rndlen);
} else if (dctx->keyType == T_3DES) {
//TODO
des3_decrypt(encRndA, RndA, key->data, 2);
memcpy(both, encRndA, rndlen);
for (uint32_t x = 0; x < rndlen; x++) {
rotRndB[x] = rotRndB[x] ^ encRndA[x];
}
des3_decrypt(encRndB, rotRndB, key->data, 2);
memcpy(both + rndlen, encRndB, rndlen);
}
} else if (secureChannel == DACEV1 && dctx->keyType != T_AES) {
if (dctx->keyType == T_DES) {
@ -857,7 +865,10 @@ int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel
if (secureChannel == DACEV1)
des_decrypt_cbc(encRndA, encRndA, rndlen, key->data, IV);
} else if (dctx->keyType == T_3DES)
tdes_nxp_receive(encRndA, encRndA, rndlen, key->data, IV, 2);
if (secureChannel == DACd40)
des3_decrypt(encRndA, encRndA, key->data, 2);
else
tdes_nxp_receive(encRndA, encRndA, rndlen, key->data, IV, 2);
else if (dctx->keyType == T_3K3DES)
tdes_nxp_receive(encRndA, encRndA, rndlen, key->data, IV, 3);
else if (dctx->keyType == T_AES) {
@ -988,6 +999,10 @@ int DesfireChangeKeySettings(DesfireContext *dctx, uint8_t *data, size_t len) {
return DesfireCommandTxData(dctx, MFDES_CHANGE_KEY_SETTINGS, data, len);
}
int DesfireChangeKeyCmd(DesfireContext *dctx, uint8_t *data, size_t len, uint8_t *resp, size_t *resplen) {
return DesfireCommand(dctx, MFDES_CHANGE_KEY, data, len, resp, resplen, -1);
}
uint8_t DesfireKeyAlgoToType(DesfireCryptoAlgorythm keyType) {
switch (keyType) {
case T_DES:
@ -1046,9 +1061,10 @@ static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print
break;
default:
PrintAndLogEx(SUCCESS,
"-- Authentication with the specified key is necessary to change any key.\n"
"-- Authentication with the specified key " _YELLOW_("(0x%02x)") " is necessary to change any key.\n"
"A change key and a PICC master key (CMK) can only be changed after authentication with the master key.\n"
"For keys other then the master or change key, an authentication with the same key is needed."
"For keys other then the master or change key, an authentication with the same key is needed.",
rights & 0x0f
);
break;
}
@ -1074,3 +1090,92 @@ void PrintKeySettings(uint8_t keysettings, uint8_t numkeys, bool applevel, bool
else
PrintKeySettingsPICC(keysettings, numkeys, print2ndbyte);
}
int DesfireChangeKey(DesfireContext *dctx, bool change_master_key, uint8_t newkeynum, DesfireCryptoAlgorythm newkeytype, uint32_t newkeyver, uint8_t *newkey, DesfireCryptoAlgorythm oldkeytype, uint8_t *oldkey, bool verbose) {
uint8_t okeybuf[DESFIRE_MAX_KEY_SIZE] = {0};
uint8_t nkeybuf[DESFIRE_MAX_KEY_SIZE] = {0};
uint8_t pckcdata[DESFIRE_MAX_KEY_SIZE + 10] = {0};
uint8_t *cdata = &pckcdata[2];
uint8_t keynodata = newkeynum & 0x3f;
/*
* Because new crypto methods can be setup only at application creation,
* changing the card master key to one of them require a key_no tweak.
*/
if (change_master_key) {
keynodata |= (DesfireKeyAlgoToType(newkeytype) & 0x03) << 6;
}
pckcdata[0] = MFDES_CHANGE_KEY; // TODO
pckcdata[1] = keynodata;
// DES -> 2TDEA
memcpy(okeybuf, oldkey, desfire_get_key_length(oldkeytype));
if (oldkeytype == T_DES) {
memcpy(&okeybuf[8], oldkey, 8);
}
memcpy(nkeybuf, newkey, desfire_get_key_length(newkeytype));
size_t nkeylen = desfire_get_key_length(newkeytype);
if (newkeytype == T_DES) {
memcpy(&nkeybuf[8], newkey, 8);
nkeylen = desfire_get_key_length(T_3DES);
}
// set key version for DES. if newkeyver > 0xff - setting key version is disabled
if (newkeytype != T_AES && newkeyver < 0x100) {
DesfireDESKeySetVersion(nkeybuf, newkeytype, newkeyver);
if (verbose)
PrintAndLogEx(INFO, "changed new key: %s [%d] %s", CLIGetOptionListStr(DesfireAlgoOpts, newkeytype), desfire_get_key_length(newkeytype), sprint_hex(nkeybuf, desfire_get_key_length(newkeytype)));
}
// xor if we change current auth key
if (newkeynum == dctx->keyNum) {
memcpy(cdata, nkeybuf, nkeylen);
} else {
memcpy(cdata, nkeybuf, nkeylen);
bin_xor(cdata, okeybuf, nkeylen);
}
// add key version for AES
size_t cdatalen = nkeylen;
if (newkeytype == T_AES) {
cdata[cdatalen] = newkeyver;
cdatalen++;
}
// add crc||crc_new_key
if (dctx->secureChannel == DACd40) {
iso14443a_crc_append(cdata, cdatalen);
cdatalen += 2;
if (newkeynum != dctx->keyNum) {
iso14443a_crc(nkeybuf, nkeylen, &cdata[cdatalen]);
cdatalen += 2;
}
} else {
// EV1 Checksum must cover : <KeyNo> <PrevKey XOR Newkey> [<AES NewKeyVer>]
desfire_crc32_append(pckcdata, cdatalen + 2);
cdatalen += 4;
if (newkeynum != dctx->keyNum) {
desfire_crc32(nkeybuf, nkeylen, &cdata[cdatalen]);
cdatalen += 4;
}
}
// send command
uint8_t resp[257] = {0};
size_t resplen = 0;
int res = DesfireChangeKeyCmd(dctx, &pckcdata[1], cdatalen, resp, &resplen);
// check response
if (res == 0 && resplen > 0)
res = -20;
// clear auth
if (newkeynum == dctx->keyNum)
DesfireClearSession(dctx);
return res;
}

View file

@ -55,4 +55,7 @@ int DesfireChangeKeySettings(DesfireContext *dctx, uint8_t *data, size_t len);
void PrintKeySettings(uint8_t keysettings, uint8_t numkeys, bool applevel, bool print2ndbyte);
uint8_t DesfireKeyAlgoToType(DesfireCryptoAlgorythm keyType);
int DesfireChangeKeyCmd(DesfireContext *dctx, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen);
int DesfireChangeKey(DesfireContext *dctx, bool change_master_key, uint8_t newkeynum, DesfireCryptoAlgorythm newkeytype, uint32_t newkeyver, uint8_t *newkey, DesfireCryptoAlgorythm oldkeytype, uint8_t *oldkey, bool verbose);
#endif // __DESFIRECORE_H

View file

@ -311,6 +311,38 @@ void DesfireCryptoCMAC(DesfireContext *ctx, uint8_t *data, size_t len, uint8_t *
memcpy(cmac, ctx->IV, kbs);
}
void DesfireDESKeySetVersion(uint8_t *key, DesfireCryptoAlgorythm keytype, uint8_t version) {
if (keytype == T_AES)
return;
// clear version
for (int n = 0; n < desfire_get_key_length(keytype); n++)
key[n] &= 0xFE;
// set version
for (int n = 0; n < 8; n++) {
uint8_t version_bit = ((version & (1 << (7 - n))) >> (7 - n));
key[n] &= 0xFE;
key[n] |= version_bit;
if (keytype == T_DES) {
key[n + 8] = key[n];
} else {
// Write ~version to avoid turning a 3DES key into a DES key
key[n + 8] &= 0xFE;
key[n + 8] |= (~version_bit) & 0x01;
}
}
}
uint8_t DesfireDESKeyGetVersion(uint8_t *key) {
uint8_t version = 0;
for (int n = 0; n < 8; n++)
version |= ((key[n] & 1) << (7 - n));
return version;
}
void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc) {
crc32_ex(data, len, crc);

View file

@ -56,7 +56,8 @@ typedef enum {
DCMNone,
DCMPlain,
DCMMACed,
DCMEncrypted
DCMEncrypted,
DCMEncryptedPlain
} DesfireCommunicationMode;
@ -101,6 +102,9 @@ void DesfireCryptoEncDec(DesfireContext *ctx, bool use_session_key, uint8_t *src
void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode, uint8_t *iv);
void DesfireCryptoCMAC(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *cmac);
void DesfireDESKeySetVersion(uint8_t *key, DesfireCryptoAlgorythm keytype, uint8_t version);
uint8_t DesfireDESKeyGetVersion(uint8_t *key);
void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc);
void desfire_crc32_append(uint8_t *data, const size_t len);
bool desfire_crc32_check(uint8_t *data, const size_t len, uint8_t *crc);

View file

@ -46,12 +46,16 @@ AllowedChannelModesS AllowedChannelModes[] = {
{MFDES_FINALIZE_KEY_SETTINGS, DACd40, DCCNative, DCMMACed},
{MFDES_ROLL_KEY_SETTINGS, DACd40, DCCNative, DCMMACed},
{MFDES_COMMIT_READER_ID, DACd40, DCCNative, DCMMACed},
{MFDES_FORMAT_PICC, DACd40, DCCNative, DCMMACed},
{MFDES_GET_UID, DACd40, DCCNative, DCMEncrypted},
{MFDES_CHANGE_KEY_SETTINGS, DACd40, DCCNative, DCMEncrypted},
{MFDES_READ_DATA, DACd40, DCCNative, DCMEncrypted},
{MFDES_WRITE_DATA, DACd40, DCCNative, DCMEncrypted},
{MFDES_CHANGE_KEY, DACd40, DCCNative, DCMEncryptedPlain},
{MFDES_CHANGE_KEY_EV2, DACd40, DCCNative, DCMEncryptedPlain},
{MFDES_GET_KEY_VERSION, DACEV1, DCCNative, DCMPlain},
{MFDES_GET_FREE_MEMORY, DACEV1, DCCNative, DCMPlain},
@ -60,17 +64,32 @@ AllowedChannelModesS AllowedChannelModes[] = {
{MFDES_GET_APPLICATION_IDS, DACEV1, DCCNative, DCMMACed},
{MFDES_GET_DF_NAMES, DACEV1, DCCNative, DCMMACed},
{MFDES_GET_KEY_SETTINGS, DACEV1, DCCNative, DCMMACed},
{MFDES_FORMAT_PICC, DACEV1, DCCNative, DCMMACed},
{MFDES_GET_UID, DACEV1, DCCNative, DCMEncrypted},
{MFDES_CHANGE_KEY_SETTINGS, DACEV1, DCCNative, DCMEncrypted},
{MFDES_CHANGE_KEY, DACEV1, DCCNative, DCMEncryptedPlain},
{MFDES_CHANGE_KEY_EV2, DACEV1, DCCNative, DCMEncryptedPlain},
};
static uint8_t DesfireGetCmdHeaderLen(uint8_t cmd) {
if (cmd == MFDES_CHANGE_KEY || cmd == MFDES_CHANGE_CONFIGURATION)
return 1;
if (cmd == MFDES_CHANGE_KEY_EV2)
return 2;
return 0;
}
static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
uint8_t data[1024] = {0};
size_t rlen = 0;
uint8_t hdrlen = DesfireGetCmdHeaderLen(cmd);
switch (ctx->commMode) {
case DCMPlain:
@ -89,7 +108,7 @@ static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint
*dstdatalen = rlen;
break;
case DCMEncrypted:
if (srcdatalen == 0)
if (srcdatalen == 0 || srcdatalen <= hdrlen)
break;
rlen = padded_data_length(srcdatalen + 2, desfire_get_key_block_length(ctx->keyType)); // 2 - crc16
@ -98,6 +117,16 @@ static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint
DesfireCryptoEncDec(ctx, true, data, rlen, dstdata, true);
*dstdatalen = rlen;
break;
case DCMEncryptedPlain:
if (srcdatalen == 0 || srcdatalen <= hdrlen)
break;
rlen = padded_data_length(srcdatalen - hdrlen, desfire_get_key_block_length(ctx->keyType)) + hdrlen;
memcpy(data, srcdata, srcdatalen);
memcpy(dstdata, srcdata, hdrlen);
DesfireCryptoEncDec(ctx, true, &data[hdrlen], rlen - hdrlen, &dstdata[hdrlen], true);
*dstdatalen = rlen;
break;
case DCMNone:
;
}
@ -107,6 +136,10 @@ static void DesfireSecureChannelEncodeEV1(DesfireContext *ctx, uint8_t cmd, uint
uint8_t data[1024] = {0};
size_t rlen = 0;
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
uint8_t hdrlen = DesfireGetCmdHeaderLen(cmd);
// we calc MAC anyway
// if encypted channel and no data - we only calc MAC
if (ctx->commMode == DCMPlain || ctx->commMode == DCMMACed || (ctx->commMode == DCMEncrypted && srcdatalen == 0)) {
@ -130,9 +163,15 @@ static void DesfireSecureChannelEncodeEV1(DesfireContext *ctx, uint8_t cmd, uint
DesfireCryptoEncDec(ctx, true, &data[1], rlen, dstdata, true);
*dstdatalen = rlen;
} else {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
} else if (ctx->commMode == DCMEncryptedPlain) {
if (srcdatalen == 0 || srcdatalen <= hdrlen)
return;
memcpy(&dstdata[0], srcdata, hdrlen);
memcpy(data, &srcdata[hdrlen], srcdatalen);
rlen = padded_data_length(srcdatalen - hdrlen, desfire_get_key_block_length(ctx->keyType));
DesfireCryptoEncDec(ctx, true, data, rlen, &dstdata[hdrlen], true);
*dstdatalen = hdrlen + rlen;
}
}
@ -184,6 +223,7 @@ static void DesfireSecureChannelDecodeD40(DesfireContext *ctx, uint8_t *srcdata,
break;
case DCMPlain:
case DACNone:
case DCMEncryptedPlain:
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
break;

View file

@ -424,6 +424,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
#define MFDES_INIT_KEY_SETTINGS 0x56
#define MFDES_FINALIZE_KEY_SETTINGS 0x57
#define MFDES_SELECT_APPLICATION 0x5A
#define MFDES_CHANGE_CONFIGURATION 0x5C
#define MFDES_CHANGE_FILE_SETTINGS 0x5F
#define MFDES_GET_VERSION 0x60
#define MFDES_GET_ISOFILE_IDS 0x61