Add initial suport for MIFARE Key Diversification

This adds two new options to `hf mfdes auth`

```
    -d, --kdf <kdf>                Key Derivation Function (KDF) (0=None, 1=AN10922)
    -i, --kdfi <kdfi>              KDF input (HEX 1-31 bytes)
```

By specifying `-d 1` and some kdf data `-i 00112233`, the key will be
diversified using AN10922.
This commit is contained in:
NZSmartie 2020-10-31 20:54:28 +13:00
commit b788054aca
No known key found for this signature in database
GPG key ID: B7E1258B1896B531
4 changed files with 97 additions and 10 deletions

View file

@ -64,6 +64,9 @@ typedef struct {
uint8_t keyno;
uint8_t keylen;
uint8_t key[24];
uint8_t kdfAlgo;
uint8_t kdfInputLen;
uint8_t kdfInput[31];
} PACKED mfdes_authinput_t;
static mfdes_authinput_t currentauth[0xF] = {{.keyno = -1}, {.keyno = -1}, {.keyno = -1}, {.keyno = -1}, {.keyno = -1}, {.keyno = -1}, {.keyno = -1}, {.keyno = -1}, {.keyno = -1}, {.keyno = -1}, {.keyno = -1}, {.keyno = -1}, {.keyno = -1}, {.keyno = -1}, {.keyno = -1}};
@ -707,6 +710,13 @@ static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rp
Desfire_3k3des_key_new_with_version(keybytes, key);
}
if (payload->kdfAlgo == MFDES_KDF_ALGO_AN10922) {
mifare_kdf_an10922(key, payload->kdfInput, payload->kdfInputLen);
if (g_debugMode) {
PrintAndLogEx(INFO, " Derrived key: " _GREEN_("%s"), sprint_hex(key->data, key_block_size(key)));
}
}
uint8_t subcommand = MFDES_AUTHENTICATE;
tag->authentication_scheme = AS_LEGACY;
@ -919,7 +929,7 @@ static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rp
memset(tag->ivect, 0, MAX_CRYPTO_BLOCK_SIZE);
tag->authenticated_key_no = payload->keyno;
if (tag->authentication_scheme == AS_NEW) {
cmac_generate_subkeys(tag->session_key);
cmac_generate_subkeys(tag->session_key, MCD_RECEIVE);
}
return PM3_SUCCESS;
}
@ -1988,7 +1998,7 @@ static void swap16(uint8_t *data) {
data[1] = tmp;
};
static int desfire_authenticate(int cmdAuthMode, int cmdAuthAlgo, uint8_t *aid, uint8_t *key, int cmdKeyNo, mfdes_auth_res_t *rpayload) {
static int desfire_authenticate(int cmdAuthMode, int cmdAuthAlgo, uint8_t *aid, uint8_t *key, int cmdKeyNo, uint8_t cmdKdfAlgo, uint8_t kdfInputLen, uint8_t *kdfInput, mfdes_auth_res_t *rpayload) {
switch (cmdAuthMode) {
case MFDES_AUTH_DES:
if (cmdAuthAlgo != MFDES_ALGO_DES && cmdAuthAlgo != MFDES_ALGO_3DES) {
@ -2037,6 +2047,25 @@ static int desfire_authenticate(int cmdAuthMode, int cmdAuthAlgo, uint8_t *aid,
break;
}
switch (cmdKdfAlgo) {
case MFDES_KDF_ALGO_AN10922:
// TODO: 2TDEA and 3TDEA keys use an input length of 1-15 bytes
if (cmdAuthAlgo != MFDES_ALGO_AES) {
PrintAndLogEx(FAILED, "Crypto algo not valid for the KDF AN10922 algo.");
return PM3_EINVARG;
}
if (kdfInputLen < 1 || kdfInputLen > 31) {
PrintAndLogEx(FAILED, "KDF AN10922 algo requires an input of length 1-31 bytes.");
return PM3_EINVARG;
}
case MFDES_KDF_ALGO_NONE:
break;
default:
PrintAndLogEx(WARNING, "KDF algo %d is not supported.", cmdKdfAlgo);
return PM3_EINVARG;
break;
}
// KEY
int res = handler_desfire_select_application(aid);
if (res != PM3_SUCCESS) return res;
@ -2054,6 +2083,9 @@ static int desfire_authenticate(int cmdAuthMode, int cmdAuthAlgo, uint8_t *aid,
payload.mode = cmdAuthMode;
payload.algo = cmdAuthAlgo;
payload.keyno = cmdKeyNo;
payload.kdfAlgo = cmdKdfAlgo;
payload.kdfInputLen = kdfInputLen;
memcpy(payload.kdfInput, kdfInput, kdfInputLen);
int error = handler_desfire_auth(&payload, rpayload);
if (error == PM3_SUCCESS) {
@ -3879,6 +3911,8 @@ static int CmdHF14ADesAuth(const char *Cmd) {
arg_strx0("a", "aid", "<aid>", "AID used for authentification (HEX 3 bytes)"),
arg_int0("n", "keyno", "<keyno>", "Key number used for authentification"),
arg_str0("k", "key", "<Key>", "Key for checking (HEX 8-24 bytes)"),
arg_int0("d", "kdf", "<kdf>", "Key Derivation Function (KDF) (0=None, 1=AN10922)"),
arg_str0("i", "kdfi", "<kdfi>", "KDF input (HEX 1-31 bytes)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -3895,6 +3929,13 @@ static int CmdHF14ADesAuth(const char *Cmd) {
uint8_t key[24] = {0};
int keylen = 0;
CLIGetHexWithReturn(ctx, 5, key, &keylen);
// Get KDF input
uint8_t kdfInput[31] = {0};
int kdfInputLen = 0;
uint8_t cmdKDFAlgo = arg_get_int_def(ctx, 6, 0);
CLIGetHexWithReturn(ctx, 7, kdfInput, &kdfInputLen);
CLIParserFree(ctx);
if (cmdAuthAlgo == MFDES_ALGO_AES) {
@ -3940,7 +3981,7 @@ static int CmdHF14ADesAuth(const char *Cmd) {
}
mfdes_auth_res_t rpayload;
int error = desfire_authenticate(cmdAuthMode, cmdAuthAlgo, aid, key, cmdKeyNo, &rpayload);
int error = desfire_authenticate(cmdAuthMode, cmdAuthAlgo, aid, key, cmdKeyNo, cmdKDFAlgo, kdfInputLen, kdfInput, &rpayload);
if (error == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, " Key : " _GREEN_("%s"), sprint_hex(key, keylength));
PrintAndLogEx(SUCCESS, " SESSION : " _GREEN_("%s"), sprint_hex(rpayload.sessionkey, keylength));
@ -4087,7 +4128,7 @@ static int AuthCheckDesfire(uint8_t *aid,
if (usedkeys[keyno] == 1 && foundKeys[0][keyno][0] == 0) {
for (uint32_t curkey = 0; curkey < deskeyListLen; curkey++) {
mfdes_auth_res_t rpayload;
error = desfire_authenticate(MFDES_AUTH_DES, MFDES_ALGO_DES, aid, deskeyList[curkey], keyno, &rpayload);
error = desfire_authenticate(MFDES_AUTH_DES, MFDES_ALGO_DES, aid, deskeyList[curkey], keyno, 0, 0, NULL, &rpayload);
if (error == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "AID 0x%06X, Found DES Key %u : " _GREEN_("%s"), curaid, keyno, sprint_hex(deskeyList[curkey], 8));
foundKeys[0][keyno][0] = 0x01;
@ -4119,7 +4160,7 @@ static int AuthCheckDesfire(uint8_t *aid,
if (usedkeys[keyno] == 1 && foundKeys[1][keyno][0] == 0) {
for (uint32_t curkey = 0; curkey < aeskeyListLen; curkey++) {
mfdes_auth_res_t rpayload;
error = desfire_authenticate(MFDES_AUTH_DES, MFDES_ALGO_3DES, aid, aeskeyList[curkey], keyno, &rpayload);
error = desfire_authenticate(MFDES_AUTH_DES, MFDES_ALGO_3DES, aid, aeskeyList[curkey], keyno, 0, 0, NULL, &rpayload);
if (error == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 3DES Key %u : " _GREEN_("%s"), curaid, keyno, sprint_hex(aeskeyList[curkey], 16));
foundKeys[1][keyno][0] = 0x01;
@ -4151,7 +4192,7 @@ static int AuthCheckDesfire(uint8_t *aid,
if (usedkeys[keyno] == 1 && foundKeys[2][keyno][0] == 0) {
for (uint32_t curkey = 0; curkey < aeskeyListLen; curkey++) {
mfdes_auth_res_t rpayload;
error = desfire_authenticate(MFDES_AUTH_AES, MFDES_ALGO_AES, aid, aeskeyList[curkey], keyno, &rpayload);
error = desfire_authenticate(MFDES_AUTH_AES, MFDES_ALGO_AES, aid, aeskeyList[curkey], keyno, 0, 0, NULL, &rpayload);
if (error == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "AID 0x%06X, Found AES Key %u : " _GREEN_("%s"), curaid, keyno, sprint_hex(aeskeyList[curkey], 16));
foundKeys[2][keyno][0] = 0x01;
@ -4183,7 +4224,7 @@ static int AuthCheckDesfire(uint8_t *aid,
if (usedkeys[keyno] == 1 && foundKeys[3][keyno][0] == 0) {
for (uint32_t curkey = 0; curkey < k3kkeyListLen; curkey++) {
mfdes_auth_res_t rpayload;
error = desfire_authenticate(MFDES_AUTH_ISO, MFDES_ALGO_3K3DES, aid, k3kkeyList[curkey], keyno, &rpayload);
error = desfire_authenticate(MFDES_AUTH_ISO, MFDES_ALGO_3K3DES, aid, k3kkeyList[curkey], keyno, 0, 0, NULL, &rpayload);
if (error == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 3K3 Key %u : " _GREEN_("%s"), curaid, keyno, sprint_hex(k3kkeyList[curkey], 24));
foundKeys[3][keyno][0] = 0x01;

View file

@ -251,7 +251,7 @@ static void xor(const uint8_t *ivect, uint8_t *data, const size_t len) {
}
}
void cmac_generate_subkeys(desfirekey_t key) {
void cmac_generate_subkeys(desfirekey_t key, MifareCryptoDirection direction) {
int kbs = key_block_size(key);
const uint8_t R = (kbs == 8) ? 0x1B : 0x87;
@ -261,7 +261,7 @@ void cmac_generate_subkeys(desfirekey_t key) {
uint8_t ivect[kbs];
memset(ivect, 0, kbs);
mifare_cypher_blocks_chained(NULL, key, ivect, l, kbs, MCD_RECEIVE, MCO_ENCYPHER);
mifare_cypher_blocks_chained(NULL, key, ivect, l, kbs, direction, MCO_ENCYPHER);
bool xor = false;
@ -306,6 +306,45 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t le
free(buffer);
}
// This function is almot like cmac(...). but with some key differences.
void mifare_kdf_an10922(const desfirekey_t key, const uint8_t *data, size_t len) {
int kbs = key_block_size(key);
int kbs2 = kbs * 2;
if (key == NULL || kbs == 0 || data == NULL || len < 1 || len > 31) {
return;
}
cmac_generate_subkeys(key, MCD_SEND);
uint8_t *buffer = malloc(kbs2);
uint8_t *ivect = malloc(kbs);
memset(ivect, 0, kbs);
buffer[0] = 0x01;
memcpy(&buffer[1], data, len++);
if (len != (kbs2)) {
buffer[len++] = 0x80;
while (len % kbs2) {
buffer[len++] = 0x00;
}
xor(key->cmac_sk2, buffer + kbs, kbs);
} else {
xor(key->cmac_sk1, buffer + kbs, kbs);
}
mbedtls_aes_context actx;
mbedtls_aes_init(&actx);
mbedtls_aes_setkey_enc(&actx, key->data, kbs * 8);
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, kbs2, ivect, buffer, buffer);
mbedtls_aes_free(&actx);
memcpy(key->data, buffer + kbs, kbs);
free(ivect);
free(buffer);
}
size_t key_block_size(const desfirekey_t key) {
if (key == NULL)
return 0;

View file

@ -126,9 +126,11 @@ size_t key_block_size(const desfirekey_t key);
size_t padded_data_length(const size_t nbytes, const size_t block_size);
size_t maced_data_length(const desfirekey_t key, const size_t nbytes);
size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int communication_settings);
void cmac_generate_subkeys(desfirekey_t key);
void cmac_generate_subkeys(desfirekey_t key, MifareCryptoDirection direction);
void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac);
void mifare_kdf_an10922(const desfirekey_t key, const uint8_t *data, size_t dataLen);
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);
void iso14443a_crc_append(uint8_t *data, size_t len);

View file

@ -94,6 +94,11 @@ typedef enum {
MFDES_ALGO_AES = 4
} mifare_des_authalgo_t;
typedef enum {
MFDES_KDF_ALGO_NONE = 0,
MFDES_KDF_ALGO_AN10922 = 1,
} mifare_des_kdf_algo_t;
//-----------------------------------------------------------------------------
// "hf 14a sim x", "hf mf sim x" attacks
//-----------------------------------------------------------------------------