MIFARE Classic is the most widely deployed contactless smart card chip in history. Introduced by Philips Semiconductors (now NXP) in 1994, it is estimated that over a billion MIFARE Classic cards have been produced. Despite being over 30 years old and having well-documented cryptographic weaknesses, it continues to be deployed in transit systems, campus cards, and building access worldwide.
Understanding MIFARE Classic in detail is valuable for multiple reasons: it teaches the fundamentals of contactless smart card memory organisation and authentication that carry over to more secure chips; it explains the legacy systems you will encounter in the field; and it illustrates the consequences of weak cryptographic design.
The MIFARE Classic family has three members:
| Variant | Memory | Sectors | Blocks per sector | Total blocks |
|---|---|---|---|---|
| MIFARE Classic Mini | 320 bytes | 5 | 4 (sectors 0–4) | 20 |
| MIFARE Classic 1K | 1024 bytes | 16 | 4 per sector | 64 |
| MIFARE Classic 4K | 4096 bytes | 40 | 4 (sectors 0–31) + 16 (sectors 32–39) | 256 |
In the 4K variant, sectors 32–39 have 16 blocks each instead of 4. The Mini is rarely seen outside specialised applications.
The ATQA / SAK for identification:
| Chip | ATQA | SAK |
|---|---|---|
| MIFARE Classic 1K | 0x0004 |
0x08 |
| MIFARE Classic 4K | 0x0002 |
0x18 |
| MIFARE Classic Mini | 0x0004 |
0x09 |
The memory of a MIFARE Classic 1K card is organised as follows:
Sector 0: Block 0 (Manufacturer block — read-only)
Block 1 (Data block)
Block 2 (Data block)
Block 3 (Sector trailer)
Sector 1: Block 4 (Data block)
Block 5 (Data block)
Block 6 (Data block)
Block 7 (Sector trailer)
...
Sector 15: Block 60 (Data block)
Block 61 (Data block)
Block 62 (Data block)
Block 63 (Sector trailer)
Each block is 16 bytes.
Block 0 is the manufacturer data block. It is written during manufacturing and is read-only:
Bytes 0–3: UID (4 bytes for single-size UID)
Byte 4: BCC (Block Check Character — XOR of UID bytes)
Byte 5: SAK
Bytes 6–7: ATQA
Bytes 8–15: Manufacturer data (chip type, etc.)
For 7-byte UID chips the layout is slightly different (UID occupies bytes 0–6).
Standard data blocks can be used for arbitrary data storage. There is one special data block variant: the Value Block.
A Value Block is a data block configured (via access bits) to support atomic increment, decrement, and restore operations — useful for storing a monetary value. A Value Block has a specific 16-byte encoding:
Bytes 0–3: Value (32-bit little-endian signed integer)
Bytes 4–7: ~Value (bitwise complement of Value, for error checking)
Bytes 8–11: Value (second copy)
Byte 12: Address (block address for restore)
Byte 13: ~Address
Byte 14: Address
Byte 15: ~Address
The sector trailer is the most important structural element of MIFARE Classic. It is always the last block of a sector (block 3 for sector 0, block 7 for sector 1, etc.) and contains the two keys and the access configuration for the entire sector.
Bytes 0–5: Key A (6 bytes)
Bytes 6–8: Access Bits (3 bytes)
Byte 9: User Byte (GPB — General Purpose Byte, freely writable)
Bytes 10–15: Key B (6 bytes)
Key A and Key B are each 6 bytes (48 bits). They are used for authentication before any read or write operation in the sector. Key A is always readable as 0x000000000000 in a read-back (the bits are masked by the chip’s hardware). Whether Key B is readable depends on the access bits.
Default keys: The factory default for both Key A and Key B is 0xFFFFFFFFFFFF (all 0xFF). Another commonly used default is 0xA0A1A2A3A4A5 (Key A) and 0xB0B1B2B3B4B5 (Key B). Many poorly configured cards in the field still use factory defaults.
The 3-byte Access Bits field controls what operations are permitted on each block in the sector, and under which key. Decoding it is the most tricky part of MIFARE Classic.
The three bytes encode a 4×3 matrix of C bits (C1, C2, C3) for each of the four blocks in the sector (three data blocks + the sector trailer). The encoding deliberately stores each bit twice — once normal and once inverted — so the chip can detect corruption.
Byte 6 (Access Byte 0): ~C2_3 ~C2_2 ~C2_1 ~C2_0 ~C1_3 ~C1_2 ~C1_1 ~C1_0
Byte 7 (Access Byte 1): C1_3 C1_2 C1_1 C1_0 ~C3_3 ~C3_2 ~C3_1 ~C3_0
Byte 8 (Access Byte 2): C3_3 C3_2 C3_1 C3_0 C2_3 C2_2 C2_1 C2_0
Where subscript _n means “for block n within the sector” (0–3, where 3 = sector trailer).
The factory default access bytes are 0xFF 0x07 0x80 (with user byte 0x69), which decode to all C bits = 0 for data blocks — meaning Key A or Key B can read and write data blocks, and only Key A can read/write the sector trailer.
Each combination of C1, C2, C3 for a data block specifies read/write/increment/decrement permissions under Key A, Key B, or both:
| C1 | C2 | C3 | Read | Write | Increment | Dec/Restore | Notes |
|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | A|B | A|B | A|B | A|B | Default (transport) |
| 0 | 1 | 0 | A|B | never | never | never | Read-only |
| 1 | 0 | 0 | A|B | B | never | never | Read with A or B; write with B only |
| 1 | 1 | 0 | A|B | B | B | A|B | Value block with access by B |
| 0 | 0 | 1 | A|B | never | never | A|B | Value block, read-only with restore |
| 0 | 1 | 1 | B | B | never | never | Read and write with B only |
| 1 | 0 | 1 | B | never | never | never | Read with B only |
| 1 | 1 | 1 | never | never | never | never | No access |
The C bits for block 3 (the sector trailer itself) control who can read/write the keys and access bits:
| C1 | C2 | C3 | Key A read | Key A write | AC read | AC write | Key B read | Key B write | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | never | A | A | never | A | A | Default (transport) |
| 0 | 1 | 0 | never | never | A | never | A | never | Frozen config |
| 1 | 0 | 0 | never | B | A|B | never | A|B | B | |
| 1 | 1 | 0 | never | never | A|B | never | A|B | never | Read-only config |
| 0 | 0 | 1 | never | A | A | A | never | A | |
| 0 | 1 | 1 | never | B | A|B | B | never | B | |
| 1 | 0 | 1 | never | never | A|B | B | never | B | |
| 1 | 1 | 1 | never | never | A|B | never | never | never | Config locked |
never means the field is masked (Key A is always masked from reads regardless of AC bits).
In practice you almost never compute access bits by hand. Tools like libnfc, mfoc, and various online calculators convert between human-readable permission tables and the 3-byte encoding. Python example:
def encode_access_bits(c1, c2, c3):
"""Encode access bits for all 4 blocks. c1/c2/c3 are 4-bit integers."""
b6 = ((~c2 & 0xF) << 4) | (~c1 & 0xF)
b7 = ((c1 & 0xF) << 4) | (~c3 & 0xF)
b8 = ((c3 & 0xF) << 4) | (c2 & 0xF)
return bytes([b6 & 0xFF, b7 & 0xFF, b8 & 0xFF])
# Factory default: all C1=0, C2=0, C3=0
print(encode_access_bits(0b0000, 0b0000, 0b0000).hex())
# → ff 07 80
MIFARE Classic uses a proprietary challenge-response authentication before any sector access. The protocol has three passes:
Pass 1 — Reader requests authentication: The reader sends an AUTH command specifying the block number and which key (A or B) to use.
Pass 2 — Tag issues a nonce: The tag generates a random 32-bit nonce (NT) and sends it to the reader. All subsequent communication is encrypted using CRYPTO1.
Pass 3 — Mutual authentication: The reader uses the key and NT to generate its own random nonce (NR) and an encrypted response (AR). The tag verifies AR, then sends its own response (AT). The reader verifies AT.
After successful authentication the session is established and all read/write commands in the sector are transmitted encrypted using CRYPTO1.
Reader Tag
|--- Auth (block, key_id) ---> |
|<-- NT (4 bytes, plaintext)-- |
|--- NR + AR (encrypted) ----> |
|<-- AT (encrypted) ----------|
(session established)
|--- Read (encrypted) -------> |
|<-- Block data (encrypted) -- |
CRYPTO1 is NXP’s proprietary 48-bit stream cipher, used exclusively in MIFARE Classic. It was reverse-engineered in 2008 by Nohl, Evans, Starbug, and Plötz, whose paper “Reverse-Engineering a Cryptographic RFID Tag” triggered a significant body of security research.
Weak RNG. The tag’s random number generator is not truly random — it is a simple LFSR seeded from a power-on counter. If you know how long the card has been powered (which is observable from the field timing), the RNG state is predictable. This enables nested authentication attacks where a known-plaintext block (e.g. Block 0, which you can read without any key) is used to constrain the keyspace.
Darkside attack. Discovered by Courtois (2008): if a reader sends an incorrect key guess, the PRNG-encrypted NACK response leaks information about the LFSR state. By sending many guesses and collecting NACK responses, an attacker can determine CRYPTO1 key bits. Does not require any known key.
Hardnested attack. An improvement by Hess, Kasper, and Kasper (2016): given one known key for any sector, you can recover all other keys on the card in seconds by exploiting the PRNG correlation between nested authentication sessions.
Card-only attack (Staticnested / Staticencrypted). Some MIFARE Classic clones have fixed UID and a static RNG (always returns the same nonce). This makes key recovery trivial.
Any MIFARE Classic card for which you know one sector key — or for which default keys have not been changed — can have all its sectors fully dumped with free tools (mfoc, mfcuk, Proxmark3) in seconds to minutes.
The purpose of explaining these weaknesses here is to help developers understand the risk profile of deploying MIFARE Classic in new systems. For any application involving financial value, identity, or physical security, MIFARE Classic is not appropriate. Use MIFARE DESFire EV2/EV3 or a JCOP/Java Card-based solution instead.
For legacy system analysis, penetration testing of your own infrastructure, or CTF challenges, the tools below are relevant (with proper authorisation):
Once authenticated to a sector, the reader can:
| Command | Code | Description |
|---|---|---|
| READ | 0x30 |
Read one block (16 bytes) |
| WRITE | 0xA0 |
Write one block (16 bytes) |
| INCREMENT | 0xC1 |
Add value to Value Block |
| DECREMENT | 0xC0 |
Subtract value from Value Block |
| RESTORE | 0xC2 |
Copy Value Block to transfer buffer |
| TRANSFER | 0xB0 |
Write transfer buffer to Value Block |
| HALT | 0x50 |
Put card to sleep |
All commands after authentication are CRYPTO1-encrypted. You do not manipulate the encryption manually — the library handles it.
Public transit (legacy). Many transit agencies issue MIFARE Classic 1K cards. A typical layout:
The transit operator assigns a unique Key A (and sometimes Key B) per sector, derived from the card UID and a master secret. This key diversification is the main security layer — it means stealing the master key is more valuable than cracking one card.
Building access. MIFARE Classic is used in access control readers for door badges. Typically only sector 0 or 1 is used, storing an employee or access ID. Key A is often the same system-wide (or per-building) — a significant weakness.
Hotel key cards. Many hotel systems (ASSA ABLOY VingCard, Dormakaba, etc.) run on MIFARE Classic 1K. Each room lock has the key programmed in; the card carries the room number and check-out date/time in an authenticated sector.
A well-designed MIFARE Classic system:
In reality many deployed systems use the same static key on all cards, making physical access to any one reader sufficient to compromise the entire fleet.
| ← Chapter 2: NFC Standards | Table of Contents | Chapter 4: MIFARE Ultralight → |