Chapter 3: MIFARE Classic — Memory Layout, Auth & Security

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.


3.1 Chip Variants

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

3.2 Memory Layout

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.

Manufacturer Block (Block 0, Sector 0)

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).

Data Blocks

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

Sector Trailer (last block of each sector)

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.


3.3 Access Bits in Detail

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.

Structure

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.

Access Conditions for Data Blocks

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

Access Conditions for the Sector Trailer (Block 3)

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).

Computing Access Bytes

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

3.4 Authentication Protocol

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) -- |

3.5 CRYPTO1 and Its Weaknesses

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.

The Vulnerabilities

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.

Practical Consequence

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.

Educational Note

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):


3.6 Read and Write Operations

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.


3.7 Typical Use Cases and Deployment Patterns

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.


3.8 Key Management in Practice

A well-designed MIFARE Classic system:

  1. Uses unique keys per card (key diversification): derive Key A = f(master_secret, UID) using a KDF (key derivation function)
  2. Never uses default keys on deployed cards
  3. Sets appropriate access bits: data sectors read-only with Key A, write-only with Key B; sector trailer write-protected
  4. Rotates master secrets on a schedule

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.


Summary


← Chapter 2: NFC Standards Table of Contents Chapter 4: MIFARE Ultralight →