Merge branch 'nestednonces' into ofw-3822-nestednonces

This commit is contained in:
Willy-JL
2024-09-04 01:30:16 +02:00
17 changed files with 1581 additions and 73 deletions

View File

@@ -6,14 +6,16 @@
extern "C" {
#endif
#define MF_CLASSIC_CMD_AUTH_KEY_A (0x60U)
#define MF_CLASSIC_CMD_AUTH_KEY_B (0x61U)
#define MF_CLASSIC_CMD_READ_BLOCK (0x30U)
#define MF_CLASSIC_CMD_WRITE_BLOCK (0xA0U)
#define MF_CLASSIC_CMD_VALUE_DEC (0xC0U)
#define MF_CLASSIC_CMD_VALUE_INC (0xC1U)
#define MF_CLASSIC_CMD_VALUE_RESTORE (0xC2U)
#define MF_CLASSIC_CMD_VALUE_TRANSFER (0xB0U)
#define MF_CLASSIC_CMD_AUTH_KEY_A (0x60U)
#define MF_CLASSIC_CMD_AUTH_KEY_B (0x61U)
#define MF_CLASSIC_CMD_BACKDOOR_AUTH_KEY_A (0x64U)
#define MF_CLASSIC_CMD_BACKDOOR_AUTH_KEY_B (0x65U)
#define MF_CLASSIC_CMD_READ_BLOCK (0x30U)
#define MF_CLASSIC_CMD_WRITE_BLOCK (0xA0U)
#define MF_CLASSIC_CMD_VALUE_DEC (0xC0U)
#define MF_CLASSIC_CMD_VALUE_INC (0xC1U)
#define MF_CLASSIC_CMD_VALUE_RESTORE (0xC2U)
#define MF_CLASSIC_CMD_VALUE_TRANSFER (0xB0U)
#define MF_CLASSIC_CMD_HALT_MSB (0x50)
#define MF_CLASSIC_CMD_HALT_LSB (0x00)

File diff suppressed because it is too large Load Diff

View File

@@ -77,6 +77,9 @@ typedef struct {
uint8_t sectors_read; /**< Number of sectors read. */
uint8_t keys_found; /**< Number of keys found. */
uint8_t current_sector; /**< Current sector number. */
uint8_t nested_phase; /**< Nested attack phase. */
uint8_t prng_type; /**< PRNG (weak or hard). */
uint8_t backdoor; /**< Backdoor type. */
} MfClassicPollerEventDataUpdate;
/**
@@ -170,13 +173,15 @@ typedef struct {
* @param[in] block_num block number for authentication.
* @param[in] key_type key type to be used for authentication.
* @param[out] nt pointer to the MfClassicNt structure to be filled with nonce data.
* @param[in] backdoor_auth flag indicating if backdoor authentication is used.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_get_nt(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt);
MfClassicNt* nt,
bool backdoor_auth);
/**
* @brief Collect tag nonce during nested authentication.
@@ -189,13 +194,15 @@ MfClassicError mf_classic_poller_get_nt(
* @param[in] block_num block number for authentication.
* @param[in] key_type key type to be used for authentication.
* @param[out] nt pointer to the MfClassicNt structure to be filled with nonce data.
* @param[in] backdoor_auth flag indicating if backdoor authentication is used.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_get_nt_nested(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt);
MfClassicNt* nt,
bool backdoor_auth);
/**
* @brief Perform authentication.
@@ -210,6 +217,7 @@ MfClassicError mf_classic_poller_get_nt_nested(
* @param[in] key key to be used for authentication.
* @param[in] key_type key type to be used for authentication.
* @param[out] data pointer to MfClassicAuthContext structure to be filled with authentication data.
* @param[in] backdoor_auth flag indicating if backdoor authentication is used.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_auth(
@@ -217,20 +225,23 @@ MfClassicError mf_classic_poller_auth(
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data);
MfClassicAuthContext* data,
bool backdoor_auth);
/**
* @brief Perform nested authentication.
*
* Must ONLY be used inside the callback function.
*
* Perform nested authentication as specified in Mf Classic protocol.
* Perform nested authentication as specified in Mf Classic protocol.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] block_num block number for authentication.
* @param[in] key key to be used for authentication.
* @param[in] key_type key type to be used for authentication.
* @param[out] data pointer to MfClassicAuthContext structure to be filled with authentication data.
* @param[in] backdoor_auth flag indicating if backdoor authentication is used.
* @param[in] early_ret return immediately after receiving encrypted nonce.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_auth_nested(
@@ -238,7 +249,9 @@ MfClassicError mf_classic_poller_auth_nested(
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data);
MfClassicAuthContext* data,
bool backdoor_auth,
bool early_ret);
/**
* @brief Halt the tag.

View File

@@ -38,13 +38,20 @@ static MfClassicError mf_classic_poller_get_nt_common(
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt,
bool is_nested) {
bool is_nested,
bool backdoor_auth) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
do {
uint8_t auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B :
MF_CLASSIC_CMD_AUTH_KEY_A;
uint8_t auth_type;
if(!backdoor_auth) {
auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B :
MF_CLASSIC_CMD_AUTH_KEY_A;
} else {
auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_BACKDOOR_AUTH_KEY_B :
MF_CLASSIC_CMD_BACKDOOR_AUTH_KEY_A;
}
uint8_t auth_cmd[2] = {auth_type, block_num};
bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd));
@@ -89,29 +96,34 @@ MfClassicError mf_classic_poller_get_nt(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt) {
MfClassicNt* nt,
bool backdoor_auth) {
furi_check(instance);
return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, false);
return mf_classic_poller_get_nt_common(
instance, block_num, key_type, nt, false, backdoor_auth);
}
MfClassicError mf_classic_poller_get_nt_nested(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt) {
MfClassicNt* nt,
bool backdoor_auth) {
furi_check(instance);
return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, true);
return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, true, backdoor_auth);
}
static MfClassicError mf_classic_poller_auth_common(
MfClassicError mf_classic_poller_auth_common(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data,
bool is_nested) {
bool is_nested,
bool backdoor_auth,
bool early_ret) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
@@ -122,14 +134,16 @@ static MfClassicError mf_classic_poller_auth_common(
MfClassicNt nt = {};
if(is_nested) {
ret = mf_classic_poller_get_nt_nested(instance, block_num, key_type, &nt);
ret =
mf_classic_poller_get_nt_nested(instance, block_num, key_type, &nt, backdoor_auth);
} else {
ret = mf_classic_poller_get_nt(instance, block_num, key_type, &nt);
ret = mf_classic_poller_get_nt(instance, block_num, key_type, &nt, backdoor_auth);
}
if(ret != MfClassicErrorNone) break;
if(data) {
data->nt = nt;
}
if(early_ret) break;
uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);
uint64_t key_num = bit_lib_bytes_to_num_be(key->data, sizeof(MfClassicKey));
@@ -182,10 +196,12 @@ MfClassicError mf_classic_poller_auth(
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data) {
MfClassicAuthContext* data,
bool backdoor_auth) {
furi_check(instance);
furi_check(key);
return mf_classic_poller_auth_common(instance, block_num, key, key_type, data, false);
return mf_classic_poller_auth_common(
instance, block_num, key, key_type, data, false, backdoor_auth, false);
}
MfClassicError mf_classic_poller_auth_nested(
@@ -193,10 +209,13 @@ MfClassicError mf_classic_poller_auth_nested(
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data) {
MfClassicAuthContext* data,
bool backdoor_auth,
bool early_ret) {
furi_check(instance);
furi_check(key);
return mf_classic_poller_auth_common(instance, block_num, key, key_type, data, true);
return mf_classic_poller_auth_common(
instance, block_num, key, key_type, data, true, backdoor_auth, early_ret);
}
MfClassicError mf_classic_poller_halt(MfClassicPoller* instance) {

View File

@@ -3,13 +3,39 @@
#include "mf_classic_poller.h"
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h>
#include <bit_lib/bit_lib.h>
#include "nfc/helpers/iso14443_crc.h"
#include <nfc/helpers/crypto1.h>
#include <stream/stream.h>
#include <stream/buffered_file_stream.h>
#include "keys_dict.h"
#include "helpers/nfc_util.h"
#ifdef __cplusplus
extern "C" {
#endif
#define MF_CLASSIC_FWT_FC (60000)
#define MF_CLASSIC_FWT_FC (60000)
#define NFC_FOLDER EXT_PATH("nfc")
#define NFC_ASSETS_FOLDER EXT_PATH("nfc/assets")
#define MF_CLASSIC_NESTED_ANALYZE_NT_COUNT (5)
#define MF_CLASSIC_NESTED_NT_HARD_MINIMUM (3)
#define MF_CLASSIC_NESTED_RETRY_MAXIMUM (60)
#define MF_CLASSIC_NESTED_HARD_RETRY_MAXIMUM (3)
#define MF_CLASSIC_NESTED_CALIBRATION_COUNT (21)
#define MF_CLASSIC_NESTED_LOGS_FILE_NAME ".nested.log"
#define MF_CLASSIC_NESTED_SYSTEM_DICT_FILE_NAME "mf_classic_dict_nested.nfc"
#define MF_CLASSIC_NESTED_USER_DICT_FILE_NAME "mf_classic_dict_user_nested.nfc"
#define MF_CLASSIC_NESTED_LOGS_FILE_PATH (NFC_FOLDER "/" MF_CLASSIC_NESTED_LOGS_FILE_NAME)
#define MF_CLASSIC_NESTED_SYSTEM_DICT_PATH \
(NFC_ASSETS_FOLDER "/" MF_CLASSIC_NESTED_SYSTEM_DICT_FILE_NAME)
#define MF_CLASSIC_NESTED_USER_DICT_PATH \
(NFC_ASSETS_FOLDER "/" MF_CLASSIC_NESTED_USER_DICT_FILE_NAME)
#define SET_PACKED_BIT(arr, bit) ((arr)[(bit) / 8] |= (1 << ((bit) % 8)))
#define GET_PACKED_BIT(arr, bit) ((arr)[(bit) / 8] & (1 << ((bit) % 8)))
extern const MfClassicKey auth1_backdoor_key;
extern const MfClassicKey auth2_backdoor_key;
extern const uint16_t valid_sums[19];
typedef enum {
MfClassicAuthStateIdle,
@@ -21,6 +47,44 @@ typedef enum {
MfClassicCardStateLost,
} MfClassicCardState;
typedef enum {
MfClassicNestedPhaseNone,
MfClassicNestedPhaseAnalyzePRNG,
MfClassicNestedPhaseDictAttack,
MfClassicNestedPhaseDictAttackResume,
MfClassicNestedPhaseCalibrate,
MfClassicNestedPhaseCollectNtEnc,
MfClassicNestedPhaseFinished,
} MfClassicNestedPhase;
typedef enum {
MfClassicPrngTypeUnknown, // Tag not yet tested
MfClassicPrngTypeNoTag, // No tag detected during test
MfClassicPrngTypeWeak, // Weak PRNG, standard Nested
MfClassicPrngTypeHard, // Hard PRNG, Hardnested
} MfClassicPrngType;
typedef enum {
MfClassicBackdoorUnknown, // Tag not yet tested
MfClassicBackdoorNone, // No observed backdoor
MfClassicBackdoorAuth1, // Tag responds to v1 auth backdoor
MfClassicBackdoorAuth2, // Tag responds to v2 auth backdoor (static encrypted nonce)
} MfClassicBackdoor;
typedef struct {
uint32_t cuid; // Card UID
uint8_t key_idx; // Key index
uint32_t nt; // Nonce
uint32_t nt_enc; // Encrypted nonce
uint8_t par; // Parity
uint16_t dist; // Distance
} MfClassicNestedNonce;
typedef struct {
MfClassicNestedNonce* nonces;
size_t count;
} MfClassicNestedNonceArray;
typedef enum {
MfClassicPollerStateDetectType,
MfClassicPollerStateStart,
@@ -38,17 +102,29 @@ typedef enum {
// Dict attack states
MfClassicPollerStateNextSector,
MfClassicPollerStateAnalyzeBackdoor,
MfClassicPollerStateBackdoorReadSector,
MfClassicPollerStateRequestKey,
MfClassicPollerStateReadSector,
MfClassicPollerStateAuthKeyA,
MfClassicPollerStateAuthKeyB,
MfClassicPollerStateKeyReuseStart,
MfClassicPollerStateKeyReuseStartNoOffset,
MfClassicPollerStateKeyReuseAuthKeyA,
MfClassicPollerStateKeyReuseAuthKeyB,
MfClassicPollerStateKeyReuseReadSector,
MfClassicPollerStateSuccess,
MfClassicPollerStateFail,
// Enhanced dictionary attack states
MfClassicPollerStateNestedAnalyzePRNG,
MfClassicPollerStateNestedCalibrate,
MfClassicPollerStateNestedCollectNt,
MfClassicPollerStateNestedController,
MfClassicPollerStateNestedCollectNtEnc,
MfClassicPollerStateNestedDictAttack,
MfClassicPollerStateNestedLog,
MfClassicPollerStateNum,
} MfClassicPollerState;
@@ -70,6 +146,28 @@ typedef struct {
bool auth_passed;
uint16_t current_block;
uint8_t reuse_key_sector;
MfClassicBackdoor backdoor;
// Enhanced dictionary attack and nested nonce collection
MfClassicNestedPhase nested_phase;
MfClassicKey nested_known_key;
MfClassicKeyType nested_known_key_type;
bool current_key_checked;
uint8_t nested_known_key_sector;
uint16_t nested_target_key;
MfClassicNestedNonceArray nested_nonce;
MfClassicPrngType prng_type;
bool static_encrypted;
uint32_t static_encrypted_nonce;
bool calibrated;
uint16_t d_min;
uint16_t d_max;
uint8_t attempt_count;
KeysDict* mf_classic_system_dict;
KeysDict* mf_classic_user_dict;
// Hardnested
uint8_t nt_enc_msb
[32]; // Bit-packed array to track which unique most significant bytes have been seen (256 bits = 32 bytes)
uint16_t msb_par_sum; // Sum of parity bits for each unique most significant byte
} MfClassicPollerDictAttackContext;
typedef struct {

View File

@@ -37,7 +37,8 @@ static MfClassicError mf_classic_poller_collect_nt_handler(
poller,
data->collect_nt_context.block,
data->collect_nt_context.key_type,
&data->collect_nt_context.nt);
&data->collect_nt_context.nt,
false);
}
static MfClassicError
@@ -47,7 +48,8 @@ static MfClassicError
data->auth_context.block_num,
&data->auth_context.key,
data->auth_context.key_type,
&data->auth_context);
&data->auth_context,
false);
}
static MfClassicError mf_classic_poller_read_block_handler(
@@ -61,7 +63,8 @@ static MfClassicError mf_classic_poller_read_block_handler(
data->read_block_context.block_num,
&data->read_block_context.key,
data->read_block_context.key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) break;
error = mf_classic_poller_read_block(
@@ -87,7 +90,8 @@ static MfClassicError mf_classic_poller_write_block_handler(
data->read_block_context.block_num,
&data->read_block_context.key,
data->read_block_context.key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) break;
error = mf_classic_poller_write_block(
@@ -113,7 +117,8 @@ static MfClassicError mf_classic_poller_read_value_handler(
data->read_value_context.block_num,
&data->read_value_context.key,
data->read_value_context.key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) break;
MfClassicBlock block = {};
@@ -144,7 +149,8 @@ static MfClassicError mf_classic_poller_change_value_handler(
data->change_value_context.block_num,
&data->change_value_context.key,
data->change_value_context.key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) break;
error = mf_classic_poller_value_cmd(