diff --git a/doc/mfdes_dump_format.md b/doc/mfdes_dump_format.md index dd44e3975..bd6520954 100644 --- a/doc/mfdes_dump_format.md +++ b/doc/mfdes_dump_format.md @@ -25,6 +25,11 @@ built on. - [Keys](#keys) - [The Read flag](#the-read-flag) - [Token spellings](#token-spellings) +- [A complete example](#a-complete-example) + - [The card](#the-card) + - [The key file](#the-key-file) + - [The dump file](#the-dump-file) + - [What `hf mfdes view` makes of it](#what-hf-mfdes-view-makes-of-it) - [In-memory representation](#in-memory-representation) - [Not in v1](#not-in-v1) @@ -40,6 +45,10 @@ hf mfdes dump -f myfile save under a name of your choosing hf mfdes view -f print a dump file, no device needed ``` +With no `--keys`, the dump looks for `hf-mfdes--keys.json` by itself, so a +`hf mfdes chk -j hf-mfdes--keys` run is picked up on the next dump without +naming the file again. + With no `--aid` / `--isoid` / `--dfname` the command walks every application the PICC lists. The default filename is `hf-mfdes--dump.json`; only json is written, there is no `.bin` companion. @@ -74,22 +83,35 @@ Everything under `$.Card` describes the PICC itself. | `Version` | hex, 28 bytes | raw `GetVersion` answer, hardware + software + production details concatenated | | `Signature` | hex, 56 bytes | NXP originality signature, absent when the card refused it | | `FreeMem` | int | bytes reported by `GetFreeMem`, absent when the card refused it | -| `KeySettings` | hex, 1 byte | PICC master key settings | -| `NumKeysRaw` | hex, 1 byte | raw key-count byte, carries the key type in the top bits | -| `NumKeys` | int | key count decoded from `NumKeysRaw` | -| `KeyVersion0` | hex, 1 byte | version of the PICC master key | -| `Keys` | object | see [Keys](#keys) | Absent means the field is not written at all. Do not read a missing field as a zero. +Nothing key related lives here. The PICC master key settings and keys belong to +application `000000` -- see [Applications](#applications) -- so every key in the +file states which AID it opens. + ## Applications ^[Top](#top) `$.Applications` is an object keyed by the 3-byte AID as 6 uppercase hex digits. +**The PICC level is application `000000`.** It carries the PICC master key +settings and keys and has no files. Giving it an AID of its own is the point: a +key in this file always says which application it opens. + ```json "Applications": { + "000000": { + "KeySettings": "0F", + "NumKeysRaw": "01", + "NumKeys": 1, + "KeyType": "2tdea", + "Authenticated": true, + "Keys": { + "0": { "Version": "00", "Key": "00000000000000000000000000000000" } + } + }, "F51800": { "ISOFileID": "DF01", "DFName": "D2760000850100", @@ -97,7 +119,6 @@ zero. "NumKeysRaw": "81", "NumKeys": 1, "KeyType": "aes", - "KeyVersions": { "0": "00" }, "Authenticated": true, "Keys": { }, "Files": { } @@ -110,10 +131,9 @@ zero. | `ISOFileID` | hex, 2 bytes | ISO DF ID, absent when the application has none | | `DFName` | hex, 1-16 bytes | ISO DF name, absent when the application has none | | `KeySettings` | hex, 1 byte | | -| `NumKeysRaw` | hex, 1 byte | | -| `NumKeys` | int | | -| `KeyType` | token | `des`, `2tdea`, `3tdea` or `aes` | -| `KeyVersions` | object | key number -> 1 byte version | +| `NumKeysRaw` | hex, 1 byte | raw key-count byte, carries the key type in the top bits and the ISO-file-id flag in bit 5 | +| `NumKeys` | int | key count decoded from `NumKeysRaw` | +| `KeyType` | token | `des`, `2tdea`, `3tdea` or `aes`. One per application -- every key in an application shares it | | `Authenticated` | bool | whether the dump ran authenticated against this application | | `Keys` | object | see [Keys](#keys) | | `Files` | object | see [Files](#files) | @@ -194,21 +214,42 @@ Keys are part of the image, not a side file. A simulator cannot answer an authentication without them, and splitting them across two files means a card image that silently cannot be used. -Both `$.Card` and each application carry the same shape: +Every application, `000000` included, carries a `Keys` object keyed by key +number in decimal: ```json "Keys": { - "0": { "Algo": "aes", "Key": "E757178E13516A4F3171BC6EA85E165A" } + "0": { "Version": "00", "Key": "E757178E13516A4F3171BC6EA85E165A" }, + "1": { "Version": "03" } } ``` -Keyed by key number in decimal. `Algo` fixes the key length: `des` 8 bytes, -`2tdea` 16, `3tdea` 24, `aes` 16. Only key numbers actually known are written, -so an absent entry means "we do not have this key", never "the key is zero". +| Field | Meaning | +|---|---| +| `Version` | the key version, as read from the card. Present whenever we managed to read it | +| `Key` | the key itself. Present only when we actually have it | -`$.Card.Keys` holds the PICC master keys, which are the keys of AID `000000`. -Note the difference from the `mfdes v2` key recovery format, where PICC keys sit -under `Applications.000000` and are indexed by algorithm as well as key number. +A key version is readable without knowing the key, so the two are independent: + +* **`Version` and `Key`** -- we read the version and we have the key. +* **`Version` only** -- the key exists, we know its version, we could not + recover it. This is the normal case for every key but the one we authenticated + with. A missing `Key` never means the key is all zeros. +* **entry absent entirely** -- we know nothing about that key number. + +There is no per-key algorithm. Every key in an application uses the +application's `KeyType`, which fixes the length: `des` 8 bytes, `2tdea` 16, +`3tdea` 24, `aes` 16. + +Note the difference from the `mfdes v2` key recovery format written by +`hf mfdes chk`, which nests keys under an algorithm name +(`Applications.112233.AES.0.Key`) because during recovery the key type is not +yet known. In a dump it is, so the algorithm is stated once per application. + +`hf mfdes dump` reads a `mfdes v2` key file with `--keys `, and with no +`--keys` it looks for `hf-mfdes--keys.json` on its own -- the same filename +template it saves the dump under. Nothing is reported if that file does not +exist. ## The Read flag ^[Top](#top) @@ -251,7 +292,7 @@ of a dump file can be handed straight back to a command. `plain_rfu` is the card's second encoding for plain. It is kept distinct from `plain` so the file records what the card actually said. -| `Algo` / `KeyType` | key length | +| `KeyType` | key length | |---|---| | `des` | 8 | | `2tdea` | 16 | @@ -262,6 +303,324 @@ Hex strings are uppercase with no separators. Byte order is as the bytes travel on the wire, except `AccessRights` and the ISO file IDs, which are written big endian the way they are printed. +## A complete example +^[Top](#top) + +A real dump of a MIFARE DESFire EV2 2K, set up from blank with the commands +below. Every construct in this document appears in it: the PICC as application +`000000`, an AES application and a 2TDEA one, a standard data file, a value +file, a record file, and keys that are known by version but not by value. + +### The card +^[Top](#top) + +``` +# two applications, one AES with 3 keys, one 2TDEA with 2 +hf mfdes createapp -t 2tdea -k 00000000000000000000000000000000 --aid 112233 --fid 1122 --dfname pm3test1 --ks1 0F --ks2 A3 +hf mfdes createapp -t 2tdea -k 00000000000000000000000000000000 --aid 445566 --fid 4455 --dfname pm3test2 --ks1 0F --ks2 22 + +# ks2 carries the key type in the top two bits (0 = 2TDEA, 1 = 3TDEA, 2 = AES), +# the ISO-file-id flag in bit 5 and the key count in the low five. It overrides +# --dstalgo and --numkeys, so A3 is "AES, ISO file ids, 3 keys" + +# files in the AES application +hf mfdes createfile --aid 112233 -t aes -k 00000000000000000000000000000000 --fid 01 --isofid 0001 --size 000020 --amode encrypt --rrights key0 --wrights key0 --rwrights key0 --chrights key0 +hf mfdes createvaluefile --aid 112233 -t aes -k 00000000000000000000000000000000 --fid 02 --amode mac --lower 00000000 --upper 000003E8 --value 0000002A --lcredit 1 --rrights key0 --wrights key0 --rwrights key0 --chrights key0 +hf mfdes createrecordfile --aid 112233 -t aes -k 00000000000000000000000000000000 --fid 03 --isofid 0003 --size 000008 --maxrecord 000005 --amode plain --rrights free --wrights key0 --rwrights key0 --chrights key0 + +# and one in the 2TDEA application +hf mfdes createfile --aid 445566 -t 2tdea -k 00000000000000000000000000000000 --fid 00 --isofid 0010 --size 000010 --amode plain --rrights free --wrights key0 --rwrights key0 --chrights key0 + +# contents +hf mfdes write --aid 112233 -t aes -k 00000000000000000000000000000000 --fid 01 -d 50726F786D61726B33204445534669726520746573742064617461212121 +hf mfdes write --aid 112233 -t aes -k 00000000000000000000000000000000 --fid 03 --type record -d 5245434F52443031 +hf mfdes write --aid 112233 -t aes -k 00000000000000000000000000000000 --fid 03 --type record -d 5245434F52443032 +hf mfdes write --aid 445566 -t 2tdea -k 00000000000000000000000000000000 --fid 00 -d 48454C4C4F2050524F584D41524B2133 + +# recover the application keys, saved under the name the dump will look for +hf mfdes chk -k 00000000000000000000000000000000 -j hf-mfdes-043240CAE45380-keys + +# and dump it +hf mfdes dump -t 2tdea -k 00000000000000000000000000000000 +``` + +Without that `chk` run, application `112233` cannot be opened with the 2TDEA +PICC key. The dump does not pretend otherwise: it records the application with +`"Authenticated": false` and no files, rather than inventing empty ones. + +### The key file +^[Top](#top) + +`hf-mfdes-043240CAE45380-keys.json`, written by `hf mfdes chk -j`. This is the +separate `mfdes v2` format, not part of the card image -- `hf mfdes dump` reads +it and folds the keys it contains into the dump. + +```json +{ + "Created": "proxmark3", + "FileType": "mfdes v2", + "Card": { + "UID": "043240CAE45380", + "SAK": "20", + "ATQA": "4403", + "ATS": "06757781028002F0" + }, + "Applications": { + "112233": { + "AES": { + "0": { + "Key": "00000000000000000000000000000000" + } + } + }, + "445566": { + "2TDEA": { + "0": { + "Key": "00000000000000000000000000000000" + } + } + } + } +} +``` + +Two differences from the way the dump stores keys: + +* Keys are nested under an **algorithm name** (`Applications.112233.AES.0.Key`) + because during key recovery the application's key type is not yet known. A + dump knows it, so it states `KeyType` once per application instead. +* PICC keys are not in this file at all. `hf mfdes chk` only walks the + application list; the PICC master key is the one you hand it with `-k`. + +The dump picks this file up without `--keys` because its name follows the same +`hf-mfdes--` template the dump itself is saved under: + +``` +[=] Loaded keys for 2 application(s) from `hf-mfdes-043240CAE45380-keys` +``` + +### The dump file +^[Top](#top) + +```json +{ + "Created": "proxmark3", + "FileType": "mfdes v1", + "Version": 1, + "Card": { + "UID": "043240CAE45380", + "ATQA": "4403", + "SAK": "20", + "ATS": "06757781028002F0", + "Version": "0401011200160504010102001605043240CAE45380CD651745414216", + "Signature": "035766BD56631ED57B614CE01A24372BDC29028A310937BC42EE998C66E56E6251083B723099D80EB77291B1A8BDB055C614EC944A11E38E", + "FreeMem": 896 + }, + "Applications": { + "000000": { + "KeySettings": "0F", + "NumKeysRaw": "01", + "NumKeys": 1, + "KeyType": "2tdea", + "Authenticated": true, + "Keys": { + "0": { + "Version": "00", + "Key": "00000000000000000000000000000000" + } + } + }, + "112233": { + "ISOFileID": "1122", + "DFName": "706D337465737431", + "KeySettings": "0F", + "NumKeysRaw": "83", + "NumKeys": 3, + "KeyType": "aes", + "Authenticated": true, + "Keys": { + "0": { + "Version": "00", + "Key": "00000000000000000000000000000000" + }, + "1": { + "Version": "00" + }, + "2": { + "Version": "00" + } + }, + "Files": { + "01": { + "Type": "standard", + "TypeRaw": "00", + "CommMode": "encrypt", + "AccessRights": "0000", + "ISOFileID": "0001", + "FileSize": 32, + "Read": true, + "Data": "50726F786D61726B332044455346697265207465737420646174612121210000" + }, + "02": { + "Type": "value", + "TypeRaw": "02", + "CommMode": "mac", + "AccessRights": "0000", + "LowerLimit": 0, + "UpperLimit": 1000, + "LimitedCredit": "01", + "Value": 42, + "Read": true + }, + "03": { + "Type": "linear_record", + "TypeRaw": "03", + "CommMode": "plain", + "AccessRights": "E000", + "ISOFileID": "0003", + "RecordSize": 8, + "MaxRecords": 5, + "CurRecords": 2, + "Read": true, + "Records": { + "0": "5245434F52443031", + "1": "5245434F52443032" + } + } + } + }, + "445566": { + "ISOFileID": "4455", + "DFName": "706D337465737432", + "KeySettings": "0F", + "NumKeysRaw": "02", + "NumKeys": 2, + "KeyType": "2tdea", + "Authenticated": true, + "Keys": { + "0": { + "Version": "00", + "Key": "00000000000000000000000000000000" + }, + "1": { + "Version": "00" + } + }, + "Files": { + "00": { + "Type": "standard", + "TypeRaw": "00", + "CommMode": "plain", + "AccessRights": "E000", + "ISOFileID": "0010", + "FileSize": 16, + "Read": true, + "Data": "48454C4C4F2050524F584D41524B2133" + } + } + } + } +} +``` + +Things to read out of it: + +* **`FreeMem` is 896**, not the 2080 a blank card reports. The applications and + files took the rest. +* **`NumKeysRaw` `83`** decodes to `KeyType: aes` (top bits 10), ISO file ids + enabled (bit 5) and `NumKeys: 3`. +* **Keys 1 and 2 of `112233` have a `Version` but no `Key`.** They exist, we + read their version, we never recovered them. Key 0 is the one `hf mfdes chk` + found. +* **The value file has no `Data`**, only `Value`, `LowerLimit`, `UpperLimit` + and `LimitedCredit`. +* **The record file has `Records`, not `Data`** -- two records of 8 bytes in a + file with room for five. +* **File `01` reads 32 bytes** where only 30 were written; the card zero-pads to + the file size, and the dump stores what the card returned. + +### What `hf mfdes view` makes of it +^[Top](#top) + +``` +[=] --- Tag Information --------------------------- +[+] UID.............. 043240CAE45380 +[+] ATQA............. 03 44 +[+] SAK.............. 20 +[+] ATS.............. 06757781028002F0 +[+] Version.......... 0401011200160504010102001605043240CAE45380CD651745414216 +[+] Signature........ 035766BD56631ED57B614CE01A24372BDC29028A310937BC42EE998C66E56E62... +[+] Free memory...... 896 bytes + +[=] --- Applications ------------------------------ +[+] 2 application(s) plus the PICC level + +[=] --- AID 000000 ( PICC level ) --------------------- +[+] Key settings. 0F, 1 2tdea key(s) +[+] Authenticated yes +[+] Key 00 (ver 00). 00000000000000000000000000000000 +[+] Files........ 0 + +[=] --- AID 112233 -------------------------------- +[+] ISO DF ID.... 1122 +[+] DF name...... pm3test1 ( 706D337465737431 ) +[+] Key settings. 0F, 3 aes key(s) +[+] Authenticated yes +[+] Key 00 (ver 00). 00000000000000000000000000000000 +[+] Key 01 (ver 00). not recovered +[+] Key 02 (ver 00). not recovered +[+] Files........ 3 + +[+] File 01 - Standard data +[+] ISO file ID.. 0001 +[+] Comm mode.... encrypt +[+] Access rights 0000 +[+] File size.... 32 + +[=] Offset | Data | Ascii +[=] ---------------------------------------------------------------------------- +[=] 0/0x00 | 50 72 6F 78 6D 61 72 6B 33 20 44 45 53 46 69 72 | Proxmark3 DESFir +[=] 16/0x10 | 65 20 74 65 73 74 20 64 61 74 61 21 21 21 00 00 | e test data!!!.. + +[+] File 02 - Value +[+] Comm mode.... mac +[+] Access rights 0000 +[+] Limits....... 0 ... 1000 +[+] Value........ 42 (0x0000002A) + +[+] File 03 - Linear Record +[+] ISO file ID.. 0003 +[+] Comm mode.... plain +[+] Access rights E000 +[+] Records...... 2 of 5, 8 bytes each + +[+] Record 0 +[=] Offset | Data | Ascii +[=] ---------------------------------------------------------------------------- +[=] 0/0x00 | 52 45 43 4F 52 44 30 31 | RECORD01 +[+] Record 1 +[=] 0/0x00 | 52 45 43 4F 52 44 30 32 | RECORD02 + +[=] --- AID 445566 -------------------------------- +[+] ISO DF ID.... 4455 +[+] DF name...... pm3test2 ( 706D337465737432 ) +[+] Key settings. 0F, 2 2tdea key(s) +[+] Authenticated yes +[+] Key 00 (ver 00). 00000000000000000000000000000000 +[+] Key 01 (ver 00). not recovered +[+] Files........ 1 + +[+] File 00 - Standard data +[+] ISO file ID.. 0010 +[+] Comm mode.... plain +[+] Access rights E000 +[+] File size.... 16 + +[=] Offset | Data | Ascii +[=] ---------------------------------------------------------------------------- +[=] 0/0x00 | 48 45 4C 4C 4F 20 50 52 4F 58 4D 41 52 4B 21 33 | HELLO PROXMARK!3 +``` + ## In-memory representation ^[Top](#top)