소스 검색

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>
Eric Betts 3 년 전
부모
커밋
cd77b93f26

+ 33 - 25
applications/picopass/picopass_device.c

@@ -10,6 +10,9 @@ static const uint32_t picopass_file_version = 1;
 
 
 PicopassDevice* picopass_device_alloc() {
 PicopassDevice* picopass_device_alloc() {
     PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice));
     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->storage = furi_record_open("storage");
     picopass_dev->dialogs = furi_record_open("dialogs");
     picopass_dev->dialogs = furi_record_open("dialogs");
     return picopass_dev;
     return picopass_dev;
@@ -32,7 +35,7 @@ static bool picopass_device_save_file(
     bool saved = false;
     bool saved = false;
     FlipperFormat* file = flipper_format_file_alloc(dev->storage);
     FlipperFormat* file = flipper_format_file_alloc(dev->storage);
     PicopassPacs* pacs = &dev->dev_data.pacs;
     PicopassPacs* pacs = &dev->dev_data.pacs;
-    ApplicationArea* AA1 = &dev->dev_data.AA1;
+    PicopassBlock* AA1 = dev->dev_data.AA1;
     string_t temp_str;
     string_t temp_str;
     string_init(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(!flipper_format_file_open_always(file, string_get_cstr(temp_str))) break;
 
 
         if(dev->format == PicopassDeviceSaveFormatHF) {
         if(dev->format == PicopassDeviceSaveFormatHF) {
+            uint32_t fc = pacs->record.FacilityCode;
+            uint32_t cn = pacs->record.CardNumber;
             // Write header
             // Write header
             if(!flipper_format_write_header_cstr(file, picopass_file_header, picopass_file_version))
             if(!flipper_format_write_header_cstr(file, picopass_file_header, picopass_file_version))
                 break;
                 break;
             if(pacs->record.valid) {
             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(
                 if(!flipper_format_write_hex(
                        file, "Credential", pacs->credential, PICOPASS_BLOCK_LEN))
                        file, "Credential", pacs->credential, PICOPASS_BLOCK_LEN))
                     break;
                     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(
                     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;
                         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) {
         } else if(dev->format == PicopassDeviceSaveFormatLF) {
             const char* lf_header = "Flipper RFID key";
             const char* lf_header = "Flipper RFID key";
             // Write header
             // Write header
@@ -142,5 +145,10 @@ void picopass_device_free(PicopassDevice* picopass_dev) {
 }
 }
 
 
 void picopass_device_data_clear(PicopassDeviceData* dev_data) {
 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;
 }
 }

+ 13 - 1
applications/picopass/picopass_device.h

@@ -10,6 +10,11 @@
 #define PICOPASS_DEV_NAME_MAX_LEN 22
 #define PICOPASS_DEV_NAME_MAX_LEN 22
 #define PICOPASS_READER_DATA_MAX_SIZE 64
 #define PICOPASS_READER_DATA_MAX_SIZE 64
 #define PICOPASS_BLOCK_LEN 8
 #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_FOLDER "/any/picopass"
 #define PICOPASS_APP_EXTENSION ".picopass"
 #define PICOPASS_APP_EXTENSION ".picopass"
@@ -35,7 +40,10 @@ typedef struct {
 } PicopassWiegandRecord;
 } PicopassWiegandRecord;
 
 
 typedef struct {
 typedef struct {
+    bool legacy;
+    bool se_enabled;
     bool biometrics;
     bool biometrics;
+    uint8_t pin_length;
     PicopassEncryption encryption;
     PicopassEncryption encryption;
     uint8_t credential[8];
     uint8_t credential[8];
     uint8_t pin0[8];
     uint8_t pin0[8];
@@ -44,7 +52,11 @@ typedef struct {
 } PicopassPacs;
 } PicopassPacs;
 
 
 typedef struct {
 typedef struct {
-    ApplicationArea AA1;
+    uint8_t data[PICOPASS_BLOCK_LEN];
+} PicopassBlock;
+
+typedef struct {
+    PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT];
     PicopassPacs pacs;
     PicopassPacs pacs;
 } PicopassDeviceData;
 } PicopassDeviceData;
 
 

+ 34 - 20
applications/picopass/picopass_worker.c

@@ -55,12 +55,11 @@ static ReturnCode picopass_worker_parse_wiegand(uint8_t* data, PicopassWiegandRe
 
 
     if(record->bitLength == 26) {
     if(record->bitLength == 26) {
         uint8_t* v4 = data + 4;
         uint8_t* v4 = data + 4;
-        v4[0] = 0;
-
         uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24);
         uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24);
 
 
         record->CardNumber = (bot >> 1) & 0xFFFF;
         record->CardNumber = (bot >> 1) & 0xFFFF;
         record->FacilityCode = (bot >> 17) & 0xFF;
         record->FacilityCode = (bot >> 17) & 0xFF;
+        FURI_LOG_D(TAG, "FC:%u CN: %u\n", record->FacilityCode, record->CardNumber);
         record->valid = true;
         record->valid = true;
     } else {
     } else {
         record->CardNumber = 0;
         record->CardNumber = 0;
@@ -165,7 +164,7 @@ ReturnCode picopass_detect_card(int timeout) {
     return ERR_NONE;
     return ERR_NONE;
 }
 }
 
 
-ReturnCode picopass_read_card(ApplicationArea* AA1) {
+ReturnCode picopass_read_card(PicopassBlock* AA1) {
     rfalPicoPassIdentifyRes idRes;
     rfalPicoPassIdentifyRes idRes;
     rfalPicoPassSelectRes selRes;
     rfalPicoPassSelectRes selRes;
     rfalPicoPassReadCheckRes rcRes;
     rfalPicoPassReadCheckRes rcRes;
@@ -205,10 +204,20 @@ ReturnCode picopass_read_card(ApplicationArea* AA1) {
         return err;
         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;
         rfalPicoPassReadBlockRes block;
-        err = rfalPicoPassPollerReadBlock(i + 6, &block);
+        err = rfalPicoPassPollerReadBlock(i, &block);
         if(err != ERR_NONE) {
         if(err != ERR_NONE) {
             FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err);
             FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err);
             return err;
             return err;
@@ -217,7 +226,7 @@ ReturnCode picopass_read_card(ApplicationArea* AA1) {
         FURI_LOG_D(
         FURI_LOG_D(
             TAG,
             TAG,
             "rfalPicoPassPollerReadBlock %d %02x%02x%02x%02x%02x%02x%02x%02x",
             "rfalPicoPassPollerReadBlock %d %02x%02x%02x%02x%02x%02x%02x%02x",
-            i + 6,
+            i,
             block.data[0],
             block.data[0],
             block.data[1],
             block.data[1],
             block.data[2],
             block.data[2],
@@ -227,7 +236,7 @@ ReturnCode picopass_read_card(ApplicationArea* AA1) {
             block.data[6],
             block.data[6],
             block.data[7]);
             block.data[7]);
 
 
-        memcpy(&(AA1->block[i]), &block, sizeof(block));
+        memcpy(AA1[i].data, block.data, sizeof(block.data));
     }
     }
 
 
     return ERR_NONE;
     return ERR_NONE;
@@ -251,7 +260,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
     picopass_device_data_clear(picopass_worker->dev_data);
     picopass_device_data_clear(picopass_worker->dev_data);
     PicopassDeviceData* dev_data = 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;
     PicopassPacs* pacs = &dev_data->pacs;
     ReturnCode err;
     ReturnCode err;
 
 
@@ -263,34 +272,39 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
                 FURI_LOG_E(TAG, "picopass_read_card error %d", err);
                 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");
                 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) {
                 if(err != ERR_NONE) {
                     FURI_LOG_E(TAG, "decrypt error %d", err);
                     FURI_LOG_E(TAG, "decrypt error %d", err);
                     break;
                     break;
                 }
                 }
 
 
-                err = picopass_worker_decrypt(AA1->block[2].data, pacs->pin0);
+                err = picopass_worker_decrypt(AA1[8].data, pacs->pin0);
                 if(err != ERR_NONE) {
                 if(err != ERR_NONE) {
                     FURI_LOG_E(TAG, "decrypt error %d", err);
                     FURI_LOG_E(TAG, "decrypt error %d", err);
                     break;
                     break;
                 }
                 }
 
 
-                err = picopass_worker_decrypt(AA1->block[3].data, pacs->pin1);
+                err = picopass_worker_decrypt(AA1[9].data, pacs->pin1);
                 if(err != ERR_NONE) {
                 if(err != ERR_NONE) {
                     FURI_LOG_E(TAG, "decrypt error %d", err);
                     FURI_LOG_E(TAG, "decrypt error %d", err);
                     break;
                     break;
                 }
                 }
-            } else if(pacs->encryption == 0x14) {
+            } else if(pacs->encryption == PicopassDeviceEncryptionNone) {
                 FURI_LOG_D(TAG, "No Encryption");
                 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");
                 FURI_LOG_D(TAG, "DES Encrypted");
             } else {
             } else {
                 FURI_LOG_D(TAG, "Unknown encryption");
                 FURI_LOG_D(TAG, "Unknown encryption");

+ 7 - 6
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;
     PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
     Widget* widget = picopass->widget;
     Widget* widget = picopass->widget;
 
 
+    size_t bytesLength = 1 + pacs->record.bitLength / 8;
     string_set_str(credential_str, "");
     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]);
         string_cat_printf(credential_str, " %02X", pacs->credential[i]);
     }
     }
 
 
     if(pacs->record.valid) {
     if(pacs->record.valid) {
         string_cat_printf(
         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(
     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_scene_read_card_success_widget_callback,
         picopass);
         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_add_string_element(
         widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(credential_str));
         widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(credential_str));
 
 

+ 0 - 4
lib/ST25RFAL002/include/rfal_picopass.h

@@ -51,10 +51,6 @@ typedef struct {
     uint8_t crc[2];
     uint8_t crc[2];
 } rfalPicoPassReadBlockRes;
 } rfalPicoPassReadBlockRes;
 
 
-typedef struct {
-    rfalPicoPassReadBlockRes block[4];
-} ApplicationArea;
-
 ReturnCode rfalPicoPassPollerInitialize(void);
 ReturnCode rfalPicoPassPollerInitialize(void);
 ReturnCode rfalPicoPassPollerCheckPresence(void);
 ReturnCode rfalPicoPassPollerCheckPresence(void);
 ReturnCode rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes);
 ReturnCode rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes);