Skip to content

HF MF Changes and Fixes - #12

Open
amec0e wants to merge 16 commits into
lab-401:mainfrom
amec0e:main
Open

HF MF Changes and Fixes#12
amec0e wants to merge 16 commits into
lab-401:mainfrom
amec0e:main

Conversation

@amec0e

@amec0e amec0e commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

HF MF Changes and Fixes

This PR brings a lot of changes to the HF MF1 side, introducing a new saveLearnedKeys function to grow a curated and custom users dictionary, along with a full nested recovery function, L1 and L2 verification fixes, erase now using the same recovery pipeline, and default keys. Apologies it is not as well written as some of my other PRs!

Whats Changed?

  • Fixed nested 4K recovery sizing which produced the wrong sectors to target.
  • Added full nested recovery function, this is run conditionally if under half of all keys found then run full nested on the card.
  • Initial fchk on reading now removed so it falls to the hfmfkeys recovery pipeline
  • Added saveLearnedKeys function which combines a user dict + DEFAULT_KEYS + 61 hard coded PM3 keys and any key recovered that was not found in this combined dictionary gets added to /keys/mf1/mfc_users_keys.dic
  • Added New option for manage dictionaries plugin to clear user keys.
  • Added mfc_users_keys.dic to the backup dictionaries option in manage dictionaries.
  • Fixed timer bug on ChkDIC Label to correctly show mm:ss instead of mm'ss'' in both activity_main.py and activity_read.py
  • erase.py now uses the same key recovery pipeline as the main read flow (fchk -> nested -> new key from nested -> fchk new key ) so erase may take a bit longer but ensures the keys are found.
  • Key recovery pipeline now uses user dict + DEFAULT_KEYS + 61 hard coded PM3 Keys to seed nested instead of running the full iceman dict which can lock up on cards with a lot of A keys missing.
  • Key recovery pipeline now runs an additional fchk after nested has found a key, this can help speed up the key recovery where a single key is reused among multiple sectors and blocks.
  • Fixed verify failed when writing gen2 cards (M1-4B L2 iCopy Cards/Gen2), UID and count was not measured explicitly for gen2 so write_with_standard expects UID to not change and it does because gen2 has no special writing.
  • Fixed verify failed when writing gen1a cards (M1-4B L1 iCopy Cards/Gen1a), now uses the UID from the dump or previous card to compare the UID has changed. Prior it was expected to not change producing a verification failed message as the UID does change.
  • New Plugin: MFC Recovery -> this has 3 options, the full option includes using mfc_default_keys.dic (icemans 2.5k keys dict)
  • Removed write_with_gen2 function as this function was now not needed as its is shared (it still uses write_with_standard as it should)
  • Fixed explicitly Gen1a SAK and ATQA normalization in the event a dump has the SAK as 88.
  • Darkside recovery is now gated so if 0 keys are found in fchk then it will run darkside.
  • Added Missing Salto Key
  • Added Missing NSP Global Key
  • Added University of Portsmouth Library Card Keys
  • Added ACL Decoder for save_json so this gets the correct AccessConditionsText
  • Fixed Sector trailer KEY B from not being wrote in the bin/json

NOTES:

Dict
Iceman's mfc_default_keys.dic - was too big to use and on a card in which the keys are difficult (only 2 A keys found throughout the entire dict) this causes the icopy to essentially hang as fchk runs well beyond the command timeout limits.

This now lives in the MFC Recovery plugin separate from the main flow, keys are not checked against this dict for saveLearnedKeys as it is not utilized in the main flow anymore. So this can still be used but it is not longer apart of the core key recovery pipeline.

Recovery Pipeline
The new recovery pipeline is fchk (mfc_users_keys.dic) + DEFAULT_KEYS + 61 PM3 default keys -> if less than half of all keys found -> full nested recovery, else nested single key recovery -> single key recovered -> fchk -k (checks for key re-use) -> nested single key recovery if still not recovered all.

This PR should help to address Issue #11 Points 1, 3 and 4.

Issue Notes:

The changes here address Points 1, 3 and 4 but there is a subtlety to Point 1 which needs addressing even with the current changes here.

I was also getting reading failed on a M4-4B, in my testing I have noticed that with the 4K gen2, it needs a very specific distance away from the reader. I did not have this issue with the M1-4B it seems the M4-4B needs a higher distance from the reader.

Too close or too far and fchk will get chkKey: Failed at fast selecting the card! constantly, this also applies to reading and writing (missed block write/reading).

I was able to successfully write and verify a M4-4B (4K gen2 / CUID) on the icopy, but if you place the card on the reader it will not work or you will only get partial writes (which will show as a write failed as there is no toast for a partial write succeeding only for reading).

This is just cosmetic, the write process does not prematurely end if a sector or block cannot be written to and will continue on, adding a partial write success toast might be a good solution so the users are informed that it did indeed write but only partially. This can be added at a later date as a QoL improvement.

Nothing to change on the reading/writing side further on this PR, but it should be made more prominent that the reading distance has increased and it can result in failed block writes. While this is stated, it is indeed stated at the bottom of the repository.

amec0e added 16 commits July 26, 2026 16:49
- Gen1a now normalizes the SAK and ATQA for M1-4B L1 iCopy Cards (Gen1a)
- Removed write_with_gen2 function
- Fixed Gen2 UID Verification failed
- Fixed Gen1a UID Verification failed
- Added a Full Nested function conditional on less than half of all keys found
- Added saveLearnedKeys function to save keys that are not in the combined dict to the mfc_users_keys.dic 
- Fixed 4K sizing issue
- fchk now propagates nested correctly and a new key recovered with nested single key recovery is fed back to fchk to check for key reuse accross sectors.
- Removed icemans mfc_default_keys.dic as apart of the main flow as it was too big and causing issues
erase now uses the same hfmfkeys recovery pipeline to source keys for the card being erased (L1/L2/L3)
read now no longer does an initial fchk and instead just falls straight into the key recovery pipeline
Updated version and enhanced description for clarity.
Updated dictionary management to include a new user key type and added functionality to clear user keys.
This plugin uses 3 options 1 of which is the full dict (2.5K keys) for chk, this was removed from the main flow and added here as it can present some issues now and then with problematic cards, also has a option to skip fchk entirely and usee a nested attack with a manual key provided
Added Missing NSP Global Key
Added Missing Salto Key
Added University of Portsmouth Library Cards
Added ACL Decoding for the save_json function as well as fixed trailer sector Key B's not being wrote to bin and json
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant