Skip to content

Commit

Permalink
Mifare Classic nested auth support (#3238)
Browse files Browse the repository at this point in the history
Co-authored-by: Aleksandr Kutuzov <[email protected]>
  • Loading branch information
augustozanellato and skotopes authored Dec 1, 2023
1 parent c1e0d02 commit b51a754
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 19 deletions.
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
40 changes: 40 additions & 0 deletions lib/nfc/protocols/mf_classic/mf_classic_poller.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,25 @@ MfClassicError mf_classic_poller_get_nt(
MfClassicKeyType key_type,
MfClassicNt* nt);

/**
* @brief Collect tag nonce during nested authentication.
*
* Must ONLY be used inside the callback function.
*
* Starts nested authentication procedure and collects tag nonce.
*
* @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_type key type to be used for authentication.
* @param[out] nt pointer to the MfClassicNt structure to be filled with nonce data.
* @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);

/**
* @brief Perform authentication.
*
Expand All @@ -200,6 +219,27 @@ MfClassicError mf_classic_poller_auth(
MfClassicKeyType key_type,
MfClassicAuthContext* data);

/**
* @brief Perform nested authentication.
*
* Must ONLY be used inside the callback function.
*
* 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.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_auth_nested(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data);

/**
* @brief Halt the tag.
*
Expand Down
89 changes: 75 additions & 14 deletions lib/nfc/protocols/mf_classic/mf_classic_poller_i.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ MfClassicError mf_classic_process_error(Iso14443_3aError error) {
return ret;
}

MfClassicError mf_classic_poller_get_nt(
static MfClassicError mf_classic_poller_get_nt_common(
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_poller_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 @@ -69,12 +85,29 @@ MfClassicError mf_classic_poller_get_nt(
return ret;
}

MfClassicError mf_classic_poller_auth(
MfClassicError mf_classic_poller_get_nt(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt) {
return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, false);
}

MfClassicError mf_classic_poller_get_nt_nested(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt) {
return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, true);
}

static MfClassicError mf_classic_poller_auth_common(
MfClassicPoller* instance,
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 +117,11 @@ MfClassicError mf_classic_poller_auth(
iso14443_3a_poller_get_data(instance->iso14443_3a_poller));

MfClassicNt nt = {};
ret = mf_classic_poller_get_nt(instance, block_num, key_type, &nt);
if(is_nested) {
ret = mf_classic_poller_get_nt_nested(instance, block_num, key_type, &nt);
} else {
ret = mf_classic_poller_get_nt(instance, block_num, key_type, &nt);
}
if(ret != MfClassicErrorNone) break;
if(data) {
data->nt = nt;
Expand All @@ -96,7 +133,13 @@ MfClassicError mf_classic_poller_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 Expand Up @@ -130,6 +173,24 @@ MfClassicError mf_classic_poller_auth(
return ret;
}

MfClassicError mf_classic_poller_auth(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data) {
return mf_classic_poller_auth_common(instance, block_num, key, key_type, data, false);
}

MfClassicError mf_classic_poller_auth_nested(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data) {
return mf_classic_poller_auth_common(instance, block_num, key, key_type, data, true);
}

MfClassicError mf_classic_poller_halt(MfClassicPoller* instance) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
Expand Down
2 changes: 1 addition & 1 deletion targets/f18/api_symbols.csv
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,49.0,,
Version,+,49.1,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,,
Header,+,applications/services/cli/cli_vcp.h,,
Expand Down
4 changes: 3 additions & 1 deletion targets/f7/api_symbols.csv
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,49.0,,
Version,+,49.1,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/cli/cli.h,,
Expand Down Expand Up @@ -2288,6 +2288,8 @@ Function,+,mf_classic_is_sector_trailer,_Bool,uint8_t
Function,+,mf_classic_is_value_block,_Bool,"MfClassicSectorTrailer*, uint8_t"
Function,+,mf_classic_load,_Bool,"MfClassicData*, FlipperFormat*, uint32_t"
Function,+,mf_classic_poller_auth,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*"
Function,+,mf_classic_poller_auth_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*"
Function,+,mf_classic_poller_get_nt_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*"
Function,+,mf_classic_poller_get_nt,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*"
Function,+,mf_classic_poller_halt,MfClassicError,MfClassicPoller*
Function,+,mf_classic_poller_read_block,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicBlock*"
Expand Down

0 comments on commit b51a754

Please sign in to comment.