[
  {
    "path": "LICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n"
  },
  {
    "path": "README.md",
    "content": "Mifare dumps parser \n=======\n\nMifare Classic 1k/4k and Mifare Mini (320 bytes) dumps parser in human readable format.  \nDumps can be grabbed with [mfterm](https://github.com/4ZM/mfterm), [mfoc](https://github.com/nfc-tools/mfoc) or nfc-mfclassic tools from libnfc.org  \nDump file size must be 1024 or 4096 bytes.\n\nIncluded ```dump.mfd``` -- Mifare 4k dump for testing.  \n\n## Another tools\n - [010 Editor](https://www.sweetscape.com/010editor/) — hex editor that has Mifare template. Very handy for editing `mfd` files.\n - [mfterm](https://github.com/4ZM/mfterm) — Mifare terminal. Also can view and edit `mfd` dumps.\n\n#### Dependencies:\n```easy_install bitstring```  \nor  \n```pip install bitstring```  \n\n#### Usage:\n```mfdread.py ./dump.mfd```\n\n![Mifare mfd dump parser](https://zhovner.com/forever/mfdread1.png)\n\nThe total memory of 1024 bytes in Mifare Classic (1k) and 4096 bytes in Mifare 4k is divided into 16 sectors of 64 bytes, each of the sectors is divided into 4 blocks of 16 bytes. Blocks 0, 1 and 2 of each sector can store data and block 3 is used to store keys and access bits (the exception is the ‘Manufacturer Block’ which can not store data).\n![Mifare memory structure](https://zhovner.com/forever/MiFare_Memory_Structure.png)\n\nThe memory of 1KB and 4KB MIFARE Classic cards is ordered in a similar way. On both cards the first block (block 0) contains the UID, BCC, SAK, ATQA and Manufacturer data. This block is locked and cannot be altered. But some times it can be ;)  \n![Mifare zero block structure](https://zhovner.com/forever/0blockmifare.gif)\n\n##### Access bits\nAccess bits define the way the data in the sector trailer and the data blocks can be accessed. Access bits are stored twice – inverted and non-inverted in the sector trailer as shown in the images.\n![Mifare zero block structure](https://zhovner.com/forever/MiFare_Access_Bits.gif)\n\nSome examples:\n\nData stored in the sector trailer:  \n01 02 03 04 05 06 FF 07 80 69 11 12 13 14 15 16  \n01 02 03 04 05 06 – Key A  \nFF 07 80 69 – Access bits  \n11 12 13 14 15 16 – Key B (or data if Key B is not used)  \n\nBytes 6, 7, 8 are access data  \nFF 07 80  \n\nBinary representation:  \n**1**111**1**111 = FF  \n**(0)**000**0**111 = 07  \n**(1)**000**(0)**000 = 80  \n\nThe bits that are bolded and in parentheses are the ones that define access to keys (C13, C23, C33 in the image above) and they form the 001 sequence. The bits that are bolded and not in parentheses are the same bits inverted. They form, as expected, the sequence 110.\n\nFrom the table above I can see that 001 means that Key A can not be read, but can be written and Key B may be read. This is the \"transport configuration\" and was read from the card that was never used.\n\nAnother example where access bits 6,7,8 are 0x78 0x77 0x88  \n![mifare access bits explanation](http://i.imgur.com/zTKl6j3.png)\n\n#### Terms\nAbbreviation  | Meaning \n------------- | -------------\nT=CL | ISO 14443-4 protocol\nT=0  | ISO 7816-3 character-level protocol\nT=1  | ISO 7816-3 block-level protocol\nUID  | Unique Identifier, Type A\nRID  | Random ID, typically dynamically generated at Power-on Reset (UID0 = “0x08”, Random number in UID1… UID3)\nNUID  | Non-Unique Identifier\nATQA  | Answer To Request, type A \nATQB  | Answer To Request, type B\nSAK  | Select Acknowledge, Type A\nRATS | Request for Answer To Select\nATS  | Answer To Select \nATR  | Anser To Reset [What's really ATR means](#ATR)\nAPDU  | Application Protocol Data Unit\nDIF  | Dual Interface (cards)\nCOS  | Card Operating System\nCL  | Cascade Level acc. to ISO/IEC 14443-3\nCT  | Cascade Tag, Type A\nNFC  | Near Field Communication\nPCD  | Proximity Coupling Device (“Contactless Reader”)\nPICC  | Proximity Integrated Circuit (“Contactless Card”)\nPKE  | Public Key Encryption (like RSA or ECC)\nREQA  | Request Command, Type A (command 0x26)\nWUPA | Wake-up type A (command 0x52)\nSEL  | Select Command, Type A\nRFU  | Reserved for future use\n\n\n### SAK (Select Acknowledge, Type A) parsing\nSAK response is 1 bytes length and 2 bytes CRC16.  \n<img width=\"300\" src=\"https://i.imgur.com/NmUAYR4.png\" />\n\nBit 3 is cascade bit indicates that UID is not complete and additional select needed.  \n<img width=\"500\" src=\"https://i.imgur.com/5BxyYcm.png\" />\n\n\nThe bit 6 in the SAK indicates, whether the PICC is compliant to the ISO/IEC14443-4 or not. However, it\ndoes not necessarily indicate, whether the PICC supports the MIFARE Protocol or not.  \n<img width=\"500\" src=\"https://i.imgur.com/TUPcVxn.png\" />\n\nOther bits in SAK (b1, b2, b4, b5, b7, b8) is not described in ISO 14443-3.  \n\n\n### <a name=\"ATR\"></a>What's really ATR means\nATR is for contact cards and is specified in ISO 7816. For contacless cards, it is the PC/SC reader (IFD) that generates the ATR.\n\nThe ATR is constructed based on:\n\nATS (Answer to Select) for ISO 14443 Type A cards\nATQB and ATTRIB bytes for ISO 14443 Type B cards\nThe ATR will be of the form 3B 8X 80 01 HB_ATS Parity_Byte where X is the number of bytes of Historical Bytes of ATS (HB_ATS).\n\nThe exact construction of ATR for contactless cards is given in section 3.1.3.2.3 of the PC/SC spec.\n\nGiven that the only variable is ATS, it should be the same regardless of the reader.\n\n\n\n"
  },
  {
    "path": "mfdread.py",
    "content": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n#  mfdread.py - Mifare dumps parser in human readable format\n#  Pavel Zhovner <pavel@zhovner.com>\n#  https://github.com/zhovner/mfdread\n\n\nimport codecs\nimport copy\nimport sys\nfrom collections import defaultdict\n\nfrom bitstring import BitArray\n\n\nclass Options:\n    FORCE_1K = False\n\n\nif len(sys.argv) == 1:\n    sys.exit('''\n------------------\nUsage: mfdread.py ./dump.mfd\nMifare dumps reader.\n''')\n\n\ndef decode(bytes):\n    decoded = codecs.decode(bytes, \"hex\")\n    try:\n        return str(decoded, \"utf-8\").rstrip(b'\\0')\n    except:\n        return \"\"\n\n\nclass bashcolors:\n    BLUE = '\\033[34m'\n    RED = '\\033[91m'\n    GREEN = '\\033[32m'\n    WARNING = '\\033[93m'\n    ENDC = '\\033[0m'\n\n\ndef accbits_for_blocknum(accbits_str, blocknum):\n    '''\n    Decodes the access bit string for block \"blocknum\".\n    Returns the three access bits for the block or False if the\n    inverted bits do not match the access bits.\n    '''\n    bits = BitArray([0])\n    inverted = BitArray([0])\n\n    # Block 0 access bits\n    if blocknum == 0:\n        bits = BitArray([accbits_str[11], accbits_str[23], accbits_str[19]])\n        inverted = BitArray([accbits_str[7], accbits_str[3], accbits_str[15]])\n\n    # Block 0 access bits\n    elif blocknum == 1:\n        bits = BitArray([accbits_str[10], accbits_str[22], accbits_str[18]])\n        inverted = BitArray([accbits_str[6], accbits_str[2], accbits_str[14]])\n    # Block 0 access bits\n    elif blocknum == 2:\n        bits = BitArray([accbits_str[9], accbits_str[21], accbits_str[17]])\n        inverted = BitArray([accbits_str[5], accbits_str[1], accbits_str[13]])\n    # Sector trailer / Block 3 access bits\n    elif blocknum in (3, 15):\n        bits = BitArray([accbits_str[8], accbits_str[20], accbits_str[16]])\n        inverted = BitArray([accbits_str[4], accbits_str[0], accbits_str[12]])\n\n    # Check the decoded bits\n    inverted.invert()\n    if bits.bin == inverted.bin:\n        return bits\n    else:\n        return False\n\n\ndef accbits_to_permission_sector(accbits):\n    permissions = {\n        '000': \"- A | A   - | A A [read B]\",\n        '010': \"- - | A   - | A - [read B]\",\n        '100': \"- B | A/B - | - B\",\n        '110': \"- - | A/B - | - -\",\n        '001': \"- A | A   A | A A [transport]\",\n        '011': \"- B | A/B B | - B\",\n        '101': \"- - | A/B B | - -\",\n        '111': \"- - | A/B - | - -\",\n    }\n    if isinstance(accbits, BitArray):\n        return permissions.get(accbits.bin, \"unknown\")\n    else:\n        return \"\"\n\n\ndef accbits_to_permission_data(accbits):\n    permissions = {\n        '000': \"A/B | A/B   | A/B | A/B [transport]\",\n        '010': \"A/B |  -    |  -  |  -  [r/w]\",\n        '100': \"A/B |   B   |  -  |  -  [r/w]\",\n        '110': \"A/B |   B   |   B | A/B [value]\",\n        '001': \"A/B |  -    |  -  | A/B [value]\",\n        '011': \"  B |   B   |  -  |  -  [r/w]\",\n        '101': \"  B |  -    |  -  |  -  [r/w]\",\n        '111': \" -  |  -    |  -  |  -  [r/w]\",\n    }\n    if isinstance(accbits, BitArray):\n        return permissions.get(accbits.bin, \"unknown\")\n    else:\n        return \"\"\n\n\ndef accbit_info(accbits, sector_size):\n    '''\n    Returns  a dictionary of a access bits for all three blocks in a sector.\n    If the access bits for block could not be decoded properly, the value is set to False.\n    '''\n    access_bits = defaultdict(lambda: False)\n\n    if sector_size == 15:\n        access_bits[sector_size] = accbits_for_blocknum(accbits, sector_size)\n        return access_bits\n\n    # Decode access bits for all 4 blocks of the sector\n    for i in range(0, 4):\n        access_bits[i] = accbits_for_blocknum(accbits, i)\n    return access_bits\n\n\ndef print_info(data):\n    blocksmatrix = []\n    blockrights = {}\n    block_number = 0\n\n    data_size = len(data)\n\n    if data_size not in {4096, 1024, 320}:\n        sys.exit(\"Wrong file size: %d bytes.\\nOnly 320, 1024 or 4096 bytes allowed.\" % len(data))\n\n    if Options.FORCE_1K:\n        data_size = 1024\n\n    # read all sectors\n    sector_number = 0\n    start = 0\n    end = 64\n    while True:\n        sector = data[start:end]\n        sector = codecs.encode(sector, 'hex')\n        if not isinstance(sector, str):\n            sector = str(sector, 'ascii')\n        sectors = [sector[x:x + 32] for x in range(0, len(sector), 32)]\n\n        blocksmatrix.append(sectors)\n\n        # after 32 sectors each sector has 16 blocks instead of 4\n        sector_number += 1\n        if sector_number < 32:\n            start += 64\n            end += 64\n        elif sector_number == 32:\n            start += 64\n            end += 256\n        else:\n            start += 256\n            end += 256\n\n        if start == data_size:\n            break\n\n    blocksmatrix_clear = copy.deepcopy(blocksmatrix)\n\n    # add colors for each keyA, access bits, KeyB\n    for c in range(0, len(blocksmatrix)):\n        sector_size = len(blocksmatrix[c]) - 1\n\n        # Fill in the access bits\n        blockrights[c] = accbit_info(BitArray('0x' + blocksmatrix[c][sector_size][12:20]), sector_size)\n\n        # Prepare colored output of the sector trailor\n        keyA = bashcolors.RED + blocksmatrix[c][sector_size][0:12] + bashcolors.ENDC\n        accbits = bashcolors.GREEN + blocksmatrix[c][sector_size][12:20] + bashcolors.ENDC\n        keyB = bashcolors.BLUE + blocksmatrix[c][sector_size][20:32] + bashcolors.ENDC\n\n        blocksmatrix[c][sector_size] = keyA + accbits + keyB\n\n    print(\"File size: %d bytes. Expected %d sectors\" % (len(data), sector_number))\n    print(\"\\n\\tUID:  \" + blocksmatrix[0][0][0:8])\n    print(\"\\tBCC:  \" + blocksmatrix[0][0][8:10])\n    print(\"\\tSAK:  \" + blocksmatrix[0][0][10:12])\n    print(\"\\tATQA: \" + blocksmatrix[0][0][12:14])\n    print(\"                   %sKey A%s    %sAccess Bits%s    %sKey B%s\" % (\n        bashcolors.RED, bashcolors.ENDC, bashcolors.GREEN, bashcolors.ENDC, bashcolors.BLUE, bashcolors.ENDC))\n    print(\"╔═════════╦═══════╦══════════════════════════════════╦════════╦═════════════════════════════════════╗\")\n    print(\"║  Sector ║ Block ║            Data                  ║ Access ║   A | Acc.  | B                     ║\")\n    print(\"║         ║       ║                                  ║        ║ r w | r   w | r w [info]            ║\")\n    print(\"║         ║       ║                                  ║        ║  r  |  w    |  i  | d/t/r           ║\")\n\n    for q in range(0, len(blocksmatrix)):\n        print(\"╠═════════╬═══════╬══════════════════════════════════╬════════╬═════════════════════════════════════╣\")\n        n_blocks = len(blocksmatrix[q])\n\n        # z is the block in each sector\n        for z in range(0, len(blocksmatrix[q])):\n\n            # Format the access bits. Print ERR in case of an error\n            if isinstance(blockrights[q][z], BitArray):\n                accbits = bashcolors.GREEN + blockrights[q][z].bin + bashcolors.ENDC\n            else:\n                accbits = bashcolors.WARNING + \"ERR\" + bashcolors.ENDC\n\n            if q == 0 and z == 0:\n                permissions = \"-\"\n\n            elif z == n_blocks - 1:\n                permissions = accbits_to_permission_sector(blockrights[q][z])\n            else:\n                permissions = accbits_to_permission_data(blockrights[q][z])\n\n            # Print the sector number in the second third row\n            if z == 2:\n                qn = q\n            else:\n                qn = \"\"\n\n            print(\"║    %-5s║  %-3d  ║ %s ║  %s   ║ %-35s ║ %s\" % (qn, block_number, blocksmatrix[q][z],\n                                                                   accbits, permissions,\n                                                                   decode(blocksmatrix_clear[q][z])))\n\n            block_number += 1\n\n    print(\"╚═════════╩═══════╩══════════════════════════════════╩════════╩═════════════════════════════════════╝\")\n\n\ndef main(args):\n    if args[0] == '-n':\n        args.pop(0)\n        bashcolors.BLUE = \"\"\n        bashcolors.RED = \"\"\n        bashcolors.GREEN = \"\"\n        bashcolors.WARNING = \"\"\n        bashcolors.ENDC = \"\"\n\n    if args[0] == '-1':\n        args.pop(0)\n        Options.FORCE_1K = True\n\n    filename = args[0]\n    with open(filename, \"rb\") as f:\n        data = f.read()\n        print_info(data)\n\n\nif __name__ == \"__main__\":\n    main(sys.argv[1:])\n"
  }
]