diff options
author | Eric Betts <bettse@fastmail.fm> | 2022-07-25 18:36:38 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-25 18:36:38 +0300 |
commit | cd77b93f26fd05996dc0a2381eaf23c37ef5e394 (patch) | |
tree | 09cb26bf8489537c6eac66d7ace294ec317e8bd3 | |
parent | f8e0ec42c53b7b0710d17f5efcd264c8b4c94927 (diff) |
Picopass: dump full card, extract some details (#1408)
* Dump entire picopass card
* Capture more iClass details
* facility code bugfix
Co-authored-by: あく <alleteam@gmail.com>
-rw-r--r-- | applications/picopass/picopass_device.c | 58 | ||||
-rw-r--r-- | applications/picopass/picopass_device.h | 14 | ||||
-rw-r--r-- | applications/picopass/picopass_worker.c | 54 | ||||
-rw-r--r-- | applications/picopass/scenes/picopass_scene_read_card_success.c | 13 | ||||
-rw-r--r-- | lib/ST25RFAL002/include/rfal_picopass.h | 4 |
5 files changed, 87 insertions, 56 deletions
diff --git a/applications/picopass/picopass_device.c b/applications/picopass/picopass_device.c index 8cce5288..75d9e290 100644 --- a/applications/picopass/picopass_device.c +++ b/applications/picopass/picopass_device.c @@ -10,6 +10,9 @@ static const uint32_t picopass_file_version = 1; PicopassDevice* picopass_device_alloc() { PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice)); + picopass_dev->dev_data.pacs.legacy = false; + picopass_dev->dev_data.pacs.se_enabled = false; + picopass_dev->dev_data.pacs.pin_length = 0; picopass_dev->storage = furi_record_open("storage"); picopass_dev->dialogs = furi_record_open("dialogs"); return picopass_dev; @@ -32,7 +35,7 @@ static bool picopass_device_save_file( bool saved = false; FlipperFormat* file = flipper_format_file_alloc(dev->storage); PicopassPacs* pacs = &dev->dev_data.pacs; - ApplicationArea* AA1 = &dev->dev_data.AA1; + PicopassBlock* AA1 = dev->dev_data.AA1; string_t temp_str; string_init(temp_str); @@ -54,40 +57,40 @@ static bool picopass_device_save_file( if(!flipper_format_file_open_always(file, string_get_cstr(temp_str))) break; if(dev->format == PicopassDeviceSaveFormatHF) { + uint32_t fc = pacs->record.FacilityCode; + uint32_t cn = pacs->record.CardNumber; // Write header if(!flipper_format_write_header_cstr(file, picopass_file_header, picopass_file_version)) break; if(pacs->record.valid) { - if(!flipper_format_write_uint32( - file, "Facility Code", (uint32_t*)&pacs->record.FacilityCode, 1)) - break; - if(!flipper_format_write_uint32( - file, "Card Number", (uint32_t*)&pacs->record.CardNumber, 1)) - break; + if(!flipper_format_write_uint32(file, "Facility Code", &fc, 1)) break; + if(!flipper_format_write_uint32(file, "Card Number", &cn, 1)) break; if(!flipper_format_write_hex( file, "Credential", pacs->credential, PICOPASS_BLOCK_LEN)) break; - if(!flipper_format_write_hex(file, "PIN", pacs->pin0, PICOPASS_BLOCK_LEN)) break; - if(!flipper_format_write_hex(file, "PIN(cont.)", pacs->pin1, PICOPASS_BLOCK_LEN)) - break; - - if(!flipper_format_write_comment_cstr(file, "Picopass blocks")) break; - // TODO: Save CSN, CFG, AA1, etc - bool block_saved = true; - for(size_t i = 0; i < 4; i++) { - string_printf(temp_str, "Block %d", i + 6); + if(pacs->pin_length > 0) { + if(!flipper_format_write_hex(file, "PIN\t\t", pacs->pin0, PICOPASS_BLOCK_LEN)) + break; if(!flipper_format_write_hex( - file, - string_get_cstr(temp_str), - AA1->block[i].data, - PICOPASS_BLOCK_LEN)) { - block_saved = false; + file, "PIN(cont.)\t", pacs->pin1, PICOPASS_BLOCK_LEN)) break; - } } - if(!block_saved) break; - if(!flipper_format_write_comment_cstr(file, "This is currently incomplete")) break; } + if(!flipper_format_write_comment_cstr(file, "Picopass blocks")) break; + bool block_saved = true; + + size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ? + AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] : + PICOPASS_MAX_APP_LIMIT; + for(size_t i = 0; i < app_limit; i++) { + string_printf(temp_str, "Block %d", i); + if(!flipper_format_write_hex( + file, string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { + block_saved = false; + break; + } + } + if(!block_saved) break; } else if(dev->format == PicopassDeviceSaveFormatLF) { const char* lf_header = "Flipper RFID key"; // Write header @@ -142,5 +145,10 @@ void picopass_device_free(PicopassDevice* picopass_dev) { } void picopass_device_data_clear(PicopassDeviceData* dev_data) { - memset(&dev_data->AA1, 0, sizeof(ApplicationArea)); + for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) { + memset(dev_data->AA1[i].data, 0, sizeof(dev_data->AA1[i].data)); + } + dev_data->pacs.legacy = false; + dev_data->pacs.se_enabled = false; + dev_data->pacs.pin_length = 0; } diff --git a/applications/picopass/picopass_device.h b/applications/picopass/picopass_device.h index a0f7a667..326e58e6 100644 --- a/applications/picopass/picopass_device.h +++ b/applications/picopass/picopass_device.h @@ -10,6 +10,11 @@ #define PICOPASS_DEV_NAME_MAX_LEN 22 #define PICOPASS_READER_DATA_MAX_SIZE 64 #define PICOPASS_BLOCK_LEN 8 +#define PICOPASS_MAX_APP_LIMIT 32 + +#define PICOPASS_CSN_BLOCK_INDEX 0 +#define PICOPASS_CONFIG_BLOCK_INDEX 1 +#define PICOPASS_AIA_BLOCK_INDEX 5 #define PICOPASS_APP_FOLDER "/any/picopass" #define PICOPASS_APP_EXTENSION ".picopass" @@ -35,7 +40,10 @@ typedef struct { } PicopassWiegandRecord; typedef struct { + bool legacy; + bool se_enabled; bool biometrics; + uint8_t pin_length; PicopassEncryption encryption; uint8_t credential[8]; uint8_t pin0[8]; @@ -44,7 +52,11 @@ typedef struct { } PicopassPacs; typedef struct { - ApplicationArea AA1; + uint8_t data[PICOPASS_BLOCK_LEN]; +} PicopassBlock; + +typedef struct { + PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT]; PicopassPacs pacs; } PicopassDeviceData; diff --git a/applications/picopass/picopass_worker.c b/applications/picopass/picopass_worker.c index 645a1bce..7ecfbc3b 100644 --- a/applications/picopass/picopass_worker.c +++ b/applications/picopass/picopass_worker.c @@ -55,12 +55,11 @@ static ReturnCode picopass_worker_parse_wiegand(uint8_t* data, PicopassWiegandRe if(record->bitLength == 26) { uint8_t* v4 = data + 4; - v4[0] = 0; - uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); record->CardNumber = (bot >> 1) & 0xFFFF; record->FacilityCode = (bot >> 17) & 0xFF; + FURI_LOG_D(TAG, "FC:%u CN: %u\n", record->FacilityCode, record->CardNumber); record->valid = true; } else { record->CardNumber = 0; @@ -165,7 +164,7 @@ ReturnCode picopass_detect_card(int timeout) { return ERR_NONE; } -ReturnCode picopass_read_card(ApplicationArea* AA1) { +ReturnCode picopass_read_card(PicopassBlock* AA1) { rfalPicoPassIdentifyRes idRes; rfalPicoPassSelectRes selRes; rfalPicoPassReadCheckRes rcRes; @@ -205,10 +204,20 @@ ReturnCode picopass_read_card(ApplicationArea* AA1) { return err; } - for(size_t i = 0; i < 4; i++) { - FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i + 6); + rfalPicoPassReadBlockRes csn; + err = rfalPicoPassPollerReadBlock(PICOPASS_CSN_BLOCK_INDEX, &csn); + memcpy(AA1[PICOPASS_CSN_BLOCK_INDEX].data, csn.data, sizeof(csn.data)); + + rfalPicoPassReadBlockRes cfg; + err = rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg); + memcpy(AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, cfg.data, sizeof(cfg.data)); + + size_t app_limit = cfg.data[0] < PICOPASS_MAX_APP_LIMIT ? cfg.data[0] : PICOPASS_MAX_APP_LIMIT; + + for(size_t i = 2; i < app_limit; i++) { + FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i); rfalPicoPassReadBlockRes block; - err = rfalPicoPassPollerReadBlock(i + 6, &block); + err = rfalPicoPassPollerReadBlock(i, &block); if(err != ERR_NONE) { FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err); return err; @@ -217,7 +226,7 @@ ReturnCode picopass_read_card(ApplicationArea* AA1) { FURI_LOG_D( TAG, "rfalPicoPassPollerReadBlock %d %02x%02x%02x%02x%02x%02x%02x%02x", - i + 6, + i, block.data[0], block.data[1], block.data[2], @@ -227,7 +236,7 @@ ReturnCode picopass_read_card(ApplicationArea* AA1) { block.data[6], block.data[7]); - memcpy(&(AA1->block[i]), &block, sizeof(block)); + memcpy(AA1[i].data, block.data, sizeof(block.data)); } return ERR_NONE; @@ -251,7 +260,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { picopass_device_data_clear(picopass_worker->dev_data); PicopassDeviceData* dev_data = picopass_worker->dev_data; - ApplicationArea* AA1 = &dev_data->AA1; + PicopassBlock* AA1 = dev_data->AA1; PicopassPacs* pacs = &dev_data->pacs; ReturnCode err; @@ -263,34 +272,39 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { FURI_LOG_E(TAG, "picopass_read_card error %d", err); } - pacs->biometrics = AA1->block[0].data[4]; - pacs->encryption = AA1->block[0].data[7]; + // Thank you proxmark! + pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); + pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); + + pacs->biometrics = AA1[6].data[4]; + pacs->pin_length = AA1[6].data[6] & 0x0F; + pacs->encryption = AA1[6].data[7]; - if(pacs->encryption == 0x17) { + if(pacs->encryption == PicopassDeviceEncryption3DES) { FURI_LOG_D(TAG, "3DES Encrypted"); - err = picopass_worker_decrypt(AA1->block[1].data, pacs->credential); + err = picopass_worker_decrypt(AA1[7].data, pacs->credential); if(err != ERR_NONE) { FURI_LOG_E(TAG, "decrypt error %d", err); break; } - err = picopass_worker_decrypt(AA1->block[2].data, pacs->pin0); + err = picopass_worker_decrypt(AA1[8].data, pacs->pin0); if(err != ERR_NONE) { FURI_LOG_E(TAG, "decrypt error %d", err); break; } - err = picopass_worker_decrypt(AA1->block[3].data, pacs->pin1); + err = picopass_worker_decrypt(AA1[9].data, pacs->pin1); if(err != ERR_NONE) { FURI_LOG_E(TAG, "decrypt error %d", err); break; } - } else if(pacs->encryption == 0x14) { + } else if(pacs->encryption == PicopassDeviceEncryptionNone) { FURI_LOG_D(TAG, "No Encryption"); - memcpy(pacs->credential, AA1->block[1].data, RFAL_PICOPASS_MAX_BLOCK_LEN); - memcpy(pacs->pin0, AA1->block[2].data, RFAL_PICOPASS_MAX_BLOCK_LEN); - memcpy(pacs->pin1, AA1->block[3].data, RFAL_PICOPASS_MAX_BLOCK_LEN); - } else if(pacs->encryption == 0x15) { + memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN); + memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN); + memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN); + } else if(pacs->encryption == PicopassDeviceEncryptionDES) { FURI_LOG_D(TAG, "DES Encrypted"); } else { FURI_LOG_D(TAG, "Unknown encryption"); diff --git a/applications/picopass/scenes/picopass_scene_read_card_success.c b/applications/picopass/scenes/picopass_scene_read_card_success.c index 96a08031..3866d201 100644 --- a/applications/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/picopass/scenes/picopass_scene_read_card_success.c @@ -29,14 +29,17 @@ void picopass_scene_read_card_success_on_enter(void* context) { PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; + size_t bytesLength = 1 + pacs->record.bitLength / 8; string_set_str(credential_str, ""); - for(uint8_t i = 0; i < RFAL_PICOPASS_MAX_BLOCK_LEN; i++) { + for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { string_cat_printf(credential_str, " %02X", pacs->credential[i]); } if(pacs->record.valid) { string_cat_printf( - wiegand_str, "FC: %03u CN: %05u", pacs->record.FacilityCode, pacs->record.CardNumber); + wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber); + } else { + string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); } widget_add_button_element( @@ -53,10 +56,8 @@ void picopass_scene_read_card_success_on_enter(void* context) { picopass_scene_read_card_success_widget_callback, picopass); - if(pacs->record.valid) { - widget_add_string_element( - widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str)); - } + widget_add_string_element( + widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str)); widget_add_string_element( widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(credential_str)); diff --git a/lib/ST25RFAL002/include/rfal_picopass.h b/lib/ST25RFAL002/include/rfal_picopass.h index baa8ea6f..5b815025 100644 --- a/lib/ST25RFAL002/include/rfal_picopass.h +++ b/lib/ST25RFAL002/include/rfal_picopass.h @@ -51,10 +51,6 @@ typedef struct { uint8_t crc[2]; } rfalPicoPassReadBlockRes; -typedef struct { - rfalPicoPassReadBlockRes block[4]; -} ApplicationArea; - ReturnCode rfalPicoPassPollerInitialize(void); ReturnCode rfalPicoPassPollerCheckPresence(void); ReturnCode rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes); |