mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2025-02-17 05:48:33 +00:00
Mifare Classic nested auth support (#3238)
Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
parent
c1e0d02afc
commit
b51a754fd9
6 changed files with 128 additions and 19 deletions
|
@ -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);
|
||||
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
|
|
@ -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,,
|
||||
|
|
|
|
@ -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,,
|
||||
|
@ -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*"
|
||||
|
|
|
Loading…
Add table
Reference in a new issue