Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/nfc/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ env.Append(
# Sync API
File("protocols/iso14443_3a/iso14443_3a_poller_sync_api.h"),
File("protocols/mf_ultralight/mf_ultralight_poller_sync_api.h"),
File("protocols/mf_classic/mf_classic_poller_ll_sync_api.h"),
File("protocols/mf_classic/mf_classic_poller_sync_api.h"),
# Misc
File("helpers/nfc_util.h"),
Expand Down
9 changes: 7 additions & 2 deletions lib/nfc/protocols/mf_classic/crypto1.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ void crypto1_encrypt_reader_nonce(
uint32_t cuid,
uint8_t* nt,
uint8_t* nr,
BitBuffer* out) {
BitBuffer* out,
bool is_nested) {
furi_assert(crypto);
furi_assert(nt);
furi_assert(nr);
Expand All @@ -153,7 +154,11 @@ void crypto1_encrypt_reader_nonce(
uint32_t nt_num = nfc_util_bytes2num(nt, sizeof(uint32_t));

crypto1_init(crypto, key);
crypto1_word(crypto, nt_num ^ cuid, 0);
if(is_nested) {
nt_num = crypto1_word(crypto, nt_num ^ cuid, 1) ^ nt_num;
} else {
crypto1_word(crypto, nt_num ^ cuid, 0);
}

for(size_t i = 0; i < 4; i++) {
uint8_t byte = crypto1_byte(crypto, nr[i], 0) ^ nr[i];
Expand Down
3 changes: 2 additions & 1 deletion lib/nfc/protocols/mf_classic/crypto1.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ void crypto1_encrypt_reader_nonce(
uint32_t cuid,
uint8_t* nt,
uint8_t* nr,
BitBuffer* out);
BitBuffer* out,
bool is_nested);

uint32_t prng_successor(uint32_t x, uint32_t n);

Expand Down
1 change: 1 addition & 0 deletions lib/nfc/protocols/mf_classic/mf_classic.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ typedef struct {
MfClassicNr nr;
MfClassicAr ar;
MfClassicAt at;
bool is_nested;
} MfClassicAuthContext;

typedef union {
Expand Down
36 changes: 23 additions & 13 deletions lib/nfc/protocols/mf_classic/mf_classic_poller.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ NfcCommand mf_classic_poller_handler_detect_type(MfClassicPoller* instance) {
iso14443_3a_copy(
instance->data->iso14443_3a_data,
iso14443_3a_poller_get_data(instance->iso14443_3a_poller));
MfClassicError error = mf_classic_async_get_nt(instance, 254, MfClassicKeyTypeA, NULL);
MfClassicError error =
mf_classic_async_get_nt(instance, 254, MfClassicKeyTypeA, NULL, false);
if(error == MfClassicErrorNone) {
instance->data->type = MfClassicType4k;
instance->state = MfClassicPollerStateStart;
Expand All @@ -95,7 +96,8 @@ NfcCommand mf_classic_poller_handler_detect_type(MfClassicPoller* instance) {
instance->current_type_check = MfClassicType1k;
}
} else if(instance->current_type_check == MfClassicType1k) {
MfClassicError error = mf_classic_async_get_nt(instance, 62, MfClassicKeyTypeA, NULL);
MfClassicError error =
mf_classic_async_get_nt(instance, 62, MfClassicKeyTypeA, NULL, false);
if(error == MfClassicErrorNone) {
instance->data->type = MfClassicType1k;
FURI_LOG_D(TAG, "1K detected");
Expand Down Expand Up @@ -235,7 +237,7 @@ NfcCommand mf_classic_poller_handler_read_block(MfClassicPoller* instance) {
do {
// Authenticate to sector
error = mf_classic_async_auth(
instance, write_ctx->current_block, auth_key, write_ctx->key_type_read, NULL);
instance, write_ctx->current_block, auth_key, write_ctx->key_type_read, NULL, false);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to auth to block %d", write_ctx->current_block);
instance->state = MfClassicPollerStateFail;
Expand Down Expand Up @@ -293,7 +295,12 @@ NfcCommand mf_classic_poller_handler_write_block(MfClassicPoller* instance) {
// Reauth if necessary
if(write_ctx->need_halt_before_write) {
error = mf_classic_async_auth(
instance, write_ctx->current_block, auth_key, write_ctx->key_type_write, NULL);
instance,
write_ctx->current_block,
auth_key,
write_ctx->key_type_write,
NULL,
false);
if(error != MfClassicErrorNone) {
FURI_LOG_D(
TAG, "Failed to auth to block %d for writing", write_ctx->current_block);
Expand Down Expand Up @@ -402,8 +409,8 @@ NfcCommand mf_classic_poller_handler_write_value_block(MfClassicPoller* instance
MfClassicKey* key = (auth_key_type == MfClassicKeyTypeA) ? &write_ctx->sec_tr.key_a :
&write_ctx->sec_tr.key_b;

MfClassicError error =
mf_classic_async_auth(instance, write_ctx->current_block, key, auth_key_type, NULL);
MfClassicError error = mf_classic_async_auth(
instance, write_ctx->current_block, key, auth_key_type, NULL, false);
if(error != MfClassicErrorNone) break;

error = mf_classic_async_value_cmd(instance, write_ctx->current_block, value_cmd, diff);
Expand Down Expand Up @@ -467,7 +474,8 @@ NfcCommand mf_classic_poller_handler_request_read_sector_blocks(MfClassicPoller*
sec_read_ctx->current_block,
&sec_read_ctx->key,
sec_read_ctx->key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) break;

sec_read_ctx->auth_passed = true;
Expand Down Expand Up @@ -533,7 +541,7 @@ NfcCommand mf_classic_poller_handler_auth_a(MfClassicPoller* instance) {
FURI_LOG_D(TAG, "Auth to block %d with key A: %06llx", block, key);

MfClassicError error = mf_classic_async_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL);
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL, false);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key A found");
mf_classic_set_key_found(
Expand Down Expand Up @@ -571,7 +579,7 @@ NfcCommand mf_classic_poller_handler_auth_b(MfClassicPoller* instance) {
FURI_LOG_D(TAG, "Auth to block %d with key B: %06llx", block, key);

MfClassicError error = mf_classic_async_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL);
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL, false);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key B found");
mf_classic_set_key_found(
Expand Down Expand Up @@ -626,7 +634,8 @@ NfcCommand mf_classic_poller_handler_read_sector(MfClassicPoller* instance) {
block_num,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) {
instance->state = MfClassicPollerStateNextSector;
FURI_LOG_W(TAG, "Failed to re-auth. Go to next sector");
Expand Down Expand Up @@ -714,7 +723,7 @@ NfcCommand mf_classic_poller_handler_key_reuse_auth_key_a(MfClassicPoller* insta
FURI_LOG_D(TAG, "Key attack auth to block %d with key A: %06llx", block, key);

MfClassicError error = mf_classic_async_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL);
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL, false);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key A found");
mf_classic_set_key_found(
Expand Down Expand Up @@ -749,7 +758,7 @@ NfcCommand mf_classic_poller_handler_key_reuse_auth_key_b(MfClassicPoller* insta
FURI_LOG_D(TAG, "Key attack auth to block %d with key B: %06llx", block, key);

MfClassicError error = mf_classic_async_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL);
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL, false);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key B found");
mf_classic_set_key_found(
Expand Down Expand Up @@ -788,7 +797,8 @@ NfcCommand mf_classic_poller_handler_key_reuse_read_sector(MfClassicPoller* inst
block_num,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) {
instance->state = MfClassicPollerStateKeyReuseStart;
break;
Expand Down
47 changes: 35 additions & 12 deletions lib/nfc/protocols/mf_classic/mf_classic_poller_i.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ MfClassicError mf_classic_async_get_nt(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt) {
MfClassicNt* nt,
bool is_nested) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;

Expand All @@ -47,14 +48,29 @@ MfClassicError mf_classic_async_get_nt(
uint8_t auth_cmd[2] = {auth_type, block_num};
bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd));

error = iso14443_3a_poller_send_standard_frame(
instance->iso14443_3a_poller,
instance->tx_plain_buffer,
instance->rx_plain_buffer,
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorWrongCrc) {
ret = mf_classic_process_error(error);
break;
if(is_nested) {
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_plain_buffer, // NT gets decrypted by mf_classic_async_auth
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorNone) {
ret = mf_classic_process_error(error);
break;
}
} else {
error = iso14443_3a_poller_send_standard_frame(
instance->iso14443_3a_poller,
instance->tx_plain_buffer,
instance->rx_plain_buffer,
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorWrongCrc) {
ret = mf_classic_process_error(error);
break;
}
}
if(bit_buffer_get_size_bytes(instance->rx_plain_buffer) != sizeof(MfClassicNt)) {
ret = MfClassicErrorProtocol;
Expand All @@ -74,7 +90,8 @@ MfClassicError mf_classic_async_auth(
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data) {
MfClassicAuthContext* data,
bool is_nested) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;

Expand All @@ -84,7 +101,7 @@ MfClassicError mf_classic_async_auth(
iso14443_3a_poller_get_data(instance->iso14443_3a_poller));

MfClassicNt nt = {};
ret = mf_classic_async_get_nt(instance, block_num, key_type, &nt);
ret = mf_classic_async_get_nt(instance, block_num, key_type, &nt, is_nested);
if(ret != MfClassicErrorNone) break;
if(data) {
data->nt = nt;
Expand All @@ -96,7 +113,13 @@ MfClassicError mf_classic_async_auth(
furi_hal_random_fill_buf(nr.data, sizeof(MfClassicNr));

crypto1_encrypt_reader_nonce(
instance->crypto, key_num, cuid, nt.data, nr.data, instance->tx_encrypted_buffer);
instance->crypto,
key_num,
cuid,
nt.data,
nr.data,
instance->tx_encrypted_buffer,
is_nested);
error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
Expand Down
11 changes: 9 additions & 2 deletions lib/nfc/protocols/mf_classic/mf_classic_poller_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ typedef struct {
int32_t new_value;
} MfClassicChangeValueContext;

typedef struct {
uint8_t block_num;
} MfClassicTransferValueContext;

typedef struct {
MfClassicDeviceKeys keys;
uint8_t current_sector;
Expand All @@ -160,6 +164,7 @@ typedef union {
MfClassicWriteBlockContext write_block_context;
MfClassicReadValueContext read_value_context;
MfClassicChangeValueContext change_value_context;
MfClassicTransferValueContext transfer_value_context;
MfClassicReadContext read_context;
} MfClassicPollerContextData;

Expand All @@ -173,14 +178,16 @@ MfClassicError mf_classic_async_get_nt(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt);
MfClassicNt* nt,
bool is_nested);

MfClassicError mf_classic_async_auth(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data);
MfClassicAuthContext* data,
bool is_nested);

MfClassicError mf_classic_async_halt(MfClassicPoller* instance);

Expand Down
Loading