Skip to content

Commit

Permalink
Picopass block tracking (#125)
Browse files Browse the repository at this point in the history
Co-authored-by: あく <alleteam@gmail.com>
  • Loading branch information
bettse and skotopes authored Mar 6, 2024
1 parent 6c50747 commit 5e7ae5b
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 66 deletions.
1 change: 1 addition & 0 deletions picopass_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ bool picopass_file_select(PicopassDevice* dev) {
void picopass_device_data_clear(PicopassDeviceData* dev_data) {
for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) {
memset(dev_data->card_data[i].data, 0, sizeof(dev_data->card_data[i].data));
dev_data->card_data[i].valid = false;
}
dev_data->pacs.legacy = false;
dev_data->pacs.se_enabled = false;
Expand Down
1 change: 1 addition & 0 deletions picopass_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ typedef struct {

typedef struct {
uint8_t data[PICOPASS_BLOCK_LEN];
bool valid;
} PicopassBlock;

typedef struct {
Expand Down
24 changes: 11 additions & 13 deletions protocol/picopass_listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,12 @@ static void picopass_listener_loclass_update_csn(PicopassListener* instance) {
// collect LOCLASS_NUM_PER_CSN nonces in a row for each CSN
const uint8_t* csn =
loclass_csns[(instance->key_block_num / LOCLASS_NUM_PER_CSN) % LOCLASS_NUM_CSNS];
memcpy(instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data, csn, sizeof(PicopassBlock));
memcpy(instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data, csn, PICOPASS_BLOCK_LEN);

uint8_t key[PICOPASS_BLOCK_LEN] = {};
loclass_iclass_calc_div_key(csn, picopass_iclass_key, key, false);
memcpy(
instance->data->card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].data,
key,
sizeof(PicopassBlock));
instance->data->card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].data, key, PICOPASS_BLOCK_LEN);

picopass_listener_init_cipher_state_key(instance, key);
}
Expand Down Expand Up @@ -122,7 +120,7 @@ PicopassListenerCommand
bit_buffer_copy_bytes(
instance->tmp_buffer,
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data,
sizeof(PicopassBlock));
PICOPASS_BLOCK_LEN);
} else {
picopass_listener_write_anticoll_csn(instance, instance->tmp_buffer);
}
Expand All @@ -143,7 +141,7 @@ PicopassListenerCommand
bit_buffer_copy_bytes(
instance->tx_buffer,
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data,
sizeof(PicopassBlock));
PICOPASS_BLOCK_LEN);

PicopassError error = picopass_listener_send_frame(instance, instance->tx_buffer);
if(error != PicopassErrorNone) {
Expand Down Expand Up @@ -181,7 +179,7 @@ PicopassListenerCommand
bit_buffer_copy_bytes(
instance->tx_buffer,
instance->data->card_data[block_num].data,
sizeof(PicopassBlock));
PICOPASS_BLOCK_LEN);
}
PicopassError error = picopass_listener_send_frame(instance, instance->tx_buffer);
if(error != PicopassErrorNone) {
Expand Down Expand Up @@ -216,7 +214,7 @@ static PicopassListenerCommand

// DATA(8)
bit_buffer_copy_bytes(
instance->tx_buffer, instance->data->card_data[block_num].data, sizeof(PicopassBlock));
instance->tx_buffer, instance->data->card_data[block_num].data, PICOPASS_BLOCK_LEN);
NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_D(TAG, "Failed to tx read check response: %d", error);
Expand Down Expand Up @@ -508,15 +506,15 @@ PicopassListenerCommand
case PICOPASS_SECURE_KC_BLOCK_INDEX:
if(!pers_mode && secured) {
new_block = instance->data->card_data[block_num];
for(size_t i = 0; i < sizeof(PicopassBlock); i++) {
for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
new_block.data[i] ^= rx_data[i + 2];
}
break;
}
// Use default case when in personalisation mode
// fallthrough
default:
memcpy(new_block.data, &rx_data[2], sizeof(PicopassBlock));
memcpy(new_block.data, &rx_data[2], PICOPASS_BLOCK_LEN);
break;
}

Expand All @@ -537,7 +535,7 @@ PicopassListenerCommand
bit_buffer_copy_bytes(
instance->tx_buffer,
instance->data->card_data[block_num].data,
sizeof(PicopassBlock));
PICOPASS_BLOCK_LEN);
}

PicopassError error = picopass_listener_send_frame(instance, instance->tx_buffer);
Expand Down Expand Up @@ -572,12 +570,12 @@ PicopassListenerCommand
for(uint8_t i = block_start; i < block_start + 4; i++) {
if(secured &&
((i == PICOPASS_SECURE_KD_BLOCK_INDEX) || (i == PICOPASS_SECURE_KC_BLOCK_INDEX))) {
for(size_t j = 0; j < sizeof(PicopassBlock); j++) {
for(size_t j = 0; j < PICOPASS_BLOCK_LEN; j++) {
bit_buffer_append_byte(instance->tx_buffer, 0xff);
}
} else {
bit_buffer_append_bytes(
instance->tx_buffer, instance->data->card_data[i].data, sizeof(PicopassBlock));
instance->tx_buffer, instance->data->card_data[i].data, PICOPASS_BLOCK_LEN);
}
}

Expand Down
4 changes: 2 additions & 2 deletions protocol/picopass_listener_i.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ void picopass_listener_init_cipher_state_key(PicopassListener* instance, const u
memcpy(
cc,
instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data,
sizeof(PicopassBlock));
PICOPASS_BLOCK_LEN);

instance->cipher_state = loclass_opt_doTagMAC_1(cc, key);
}

void picopass_listener_init_cipher_state(PicopassListener* instance) {
uint8_t key[PICOPASS_BLOCK_LEN] = {};
memcpy(key, instance->data->card_data[instance->key_block_num].data, sizeof(PicopassBlock));
memcpy(key, instance->data->card_data[instance->key_block_num].data, PICOPASS_BLOCK_LEN);

picopass_listener_init_cipher_state_key(instance, key);
}
Expand Down
82 changes: 35 additions & 47 deletions protocol/picopass_poller.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,20 @@ NfcCommand picopass_poller_select_handler(PicopassPoller* instance) {
return command;
}

void picopass_poller_print_block(char* str, PicopassBlock block) {
FURI_LOG_D(
TAG,
str,
block.data[0],
block.data[1],
block.data[2],
block.data[3],
block.data[4],
block.data[5],
block.data[6],
block.data[7]);
}

NfcCommand picopass_poller_pre_auth_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
PicopassError error = PicopassErrorNone;
Expand All @@ -79,17 +93,10 @@ NfcCommand picopass_poller_pre_auth_handler(PicopassPoller* instance) {
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data,
instance->serial_num.data,
sizeof(PicopassSerialNum));
FURI_LOG_D(
TAG,
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].valid = true;
picopass_poller_print_block(
"csn %02x%02x%02x%02x%02x%02x%02x%02x",
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data[0],
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data[1],
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data[2],
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data[3],
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data[4],
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data[5],
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data[6],
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX].data[7]);
instance->data->card_data[PICOPASS_CSN_BLOCK_INDEX]);

PicopassBlock block = {};
error = picopass_poller_read_block(instance, PICOPASS_CONFIG_BLOCK_INDEX, &block);
Expand All @@ -100,18 +107,11 @@ NfcCommand picopass_poller_pre_auth_handler(PicopassPoller* instance) {
memcpy(
instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data,
block.data,
sizeof(PicopassBlock));
FURI_LOG_D(
TAG,
PICOPASS_BLOCK_LEN);
instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].valid = true;
picopass_poller_print_block(
"config %02x%02x%02x%02x%02x%02x%02x%02x",
instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data[0],
instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data[1],
instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data[2],
instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data[3],
instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data[4],
instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data[5],
instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data[6],
instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data[7]);
instance->data->card_data[PICOPASS_CONFIG_BLOCK_INDEX]);

error = picopass_poller_read_block(instance, PICOPASS_SECURE_EPURSE_BLOCK_INDEX, &block);
if(error != PicopassErrorNone) {
Expand All @@ -121,18 +121,11 @@ NfcCommand picopass_poller_pre_auth_handler(PicopassPoller* instance) {
memcpy(
instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data,
block.data,
sizeof(PicopassBlock));
FURI_LOG_D(
TAG,
PICOPASS_BLOCK_LEN);
instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].valid = true;
picopass_poller_print_block(
"epurse %02x%02x%02x%02x%02x%02x%02x%02x",
instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data[0],
instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data[1],
instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data[2],
instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data[3],
instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data[4],
instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data[5],
instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data[6],
instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data[7]);
instance->data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX]);

error = picopass_poller_read_block(instance, PICOPASS_SECURE_AIA_BLOCK_INDEX, &block);
if(error != PicopassErrorNone) {
Expand All @@ -142,18 +135,11 @@ NfcCommand picopass_poller_pre_auth_handler(PicopassPoller* instance) {
memcpy(
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data,
block.data,
sizeof(PicopassBlock));
FURI_LOG_D(
TAG,
PICOPASS_BLOCK_LEN);
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].valid = true;
picopass_poller_print_block(
"aia %02x%02x%02x%02x%02x%02x%02x%02x",
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[0],
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[1],
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[2],
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[3],
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[4],
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[5],
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[6],
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[7]);
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX]);

instance->state = PicopassPollerStateCheckSecurity;
} while(false);
Expand Down Expand Up @@ -188,20 +174,20 @@ NfcCommand picopass_poller_check_security(PicopassPoller* instance) {

// Thank you proxmark!
PicopassBlock temp_block = {};
memset(temp_block.data, 0xff, sizeof(PicopassBlock));
memset(temp_block.data, 0xff, PICOPASS_BLOCK_LEN);
instance->data->pacs.legacy =
(memcmp(
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data,
temp_block.data,
sizeof(PicopassBlock)) == 0);
PICOPASS_BLOCK_LEN) == 0);

temp_block.data[3] = 0x00;
temp_block.data[4] = 0x06;
instance->data->pacs.se_enabled =
(memcmp(
instance->data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data,
temp_block.data,
sizeof(PicopassBlock)) == 0);
PICOPASS_BLOCK_LEN) == 0);

if(instance->data->pacs.se_enabled) {
FURI_LOG_D(TAG, "SE enabled");
Expand Down Expand Up @@ -401,6 +387,7 @@ NfcCommand picopass_poller_auth_handler(PicopassPoller* instance) {
if(instance->mode == PicopassPollerModeRead) {
memcpy(
instance->data->pacs.key, instance->event_data.req_key.key, PICOPASS_KEY_LEN);
instance->data->card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].valid = true;
instance->data->pacs.elite_kdf = instance->event_data.req_key.is_elite_key;
picopass_poller_prepare_read(instance);
instance->state = PicopassPollerStateReadBlock;
Expand Down Expand Up @@ -457,7 +444,8 @@ NfcCommand picopass_poller_read_block_handler(PicopassPoller* instance) {
memcpy(
instance->data->card_data[instance->current_block].data,
block.data,
sizeof(PicopassBlock));
PICOPASS_BLOCK_LEN);
instance->data->card_data[instance->current_block].valid = true;
instance->current_block++;
} while(false);

Expand Down
6 changes: 3 additions & 3 deletions protocol/picopass_poller_i.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ PicopassError
instance, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
if(ret != PicopassErrorNone) break;

if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(PicopassBlock)) {
if(bit_buffer_get_size_bytes(instance->rx_buffer) != PICOPASS_BLOCK_LEN) {
ret = PicopassErrorProtocol;
break;
}
bit_buffer_write_bytes(instance->rx_buffer, block->data, sizeof(PicopassBlock));
bit_buffer_write_bytes(instance->rx_buffer, block->data, PICOPASS_BLOCK_LEN);
} while(false);

return ret;
Expand Down Expand Up @@ -206,7 +206,7 @@ PicopassError picopass_poller_write_block(
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_UPDATE);
bit_buffer_append_byte(instance->tx_buffer, block_num);
bit_buffer_append_bytes(instance->tx_buffer, block->data, sizeof(PicopassBlock));
bit_buffer_append_bytes(instance->tx_buffer, block->data, PICOPASS_BLOCK_LEN);
bit_buffer_append_bytes(instance->tx_buffer, mac->data, sizeof(PicopassMac));

NfcError error = nfc_poller_trx(
Expand Down
5 changes: 4 additions & 1 deletion scenes/picopass_scene_loclass.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,15 @@ void picopass_scene_loclass_on_enter(void* context) {
const uint8_t config_block[PICOPASS_BLOCK_LEN] = {
0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C};
memcpy(data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].data, config_block, sizeof(config_block));
data->card_data[PICOPASS_CONFIG_BLOCK_INDEX].valid = true;

const uint8_t epurse[PICOPASS_BLOCK_LEN] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
memcpy(data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data, epurse, sizeof(epurse));
data->card_data[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].valid = true;

const uint8_t aia[PICOPASS_BLOCK_LEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
memcpy(data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].data, aia, sizeof(aia));
data->card_data[PICOPASS_SECURE_AIA_BLOCK_INDEX].valid = true;

picopass->listener = picopass_listener_alloc(picopass->nfc, data);
free(data);
Expand Down Expand Up @@ -100,4 +103,4 @@ void picopass_scene_loclass_on_exit(void* context) {
picopass_blink_stop(picopass);

loclass_reset(picopass->loclass);
}
}

0 comments on commit 5e7ae5b

Please sign in to comment.