Documentation
¶
Overview ¶
Package mifare decodes Mifare Classic 1K / 4K data dumps — manufacturer block (sector 0 block 0), sector trailer (last block of each sector), value blocks (recognized by their value+complement structure), and plain data blocks. Pure offline parser; no transport, no hardware.
Wrap-vs-native judgement: Mifare Classic's block layouts are public — NXP application notes AN10833 (sector trailer and access conditions), AN10834 (value blocks), AN10927 (UID formats), and ISO/IEC 14443-3 (ATQA / SAK). Wrapping a FAP for this would require an SD-card install + a firmware-fork dependency for a pure parser. We implement natively so operators can paste a 16-byte block (or a 64-block 1K dump / 256-block 4K dump) and decode it offline.
What this package covers:
- Block-kind classification (manufacturer, trailer, value, data) based on block index + structural heuristics
- Sector-trailer decode: Key A, access bits, GPB, Key B, plus the per-block permission expansion (read / write / increment / decrement / transfer / restore allowed for Key A only, Key B only, both, or neither)
- Value-block decode: value + complement + duplicate value integrity check, signed 32-bit value, address byte + complement check
- Manufacturer-block decode: NUID (4-byte UID), BCC, SAK, ATQA, and the 8 trailing manufacturer-data bytes
- Dump walker that classifies every block of a 1K / 4K dump in one pass
What this package does NOT cover (deliberately out of scope):
- Reading the actual card (operators bring dumps from Flipper / Proxmark3 / etc.)
- Cryptogram derivation or key recovery (covered by the internal/crypto1 package's mfoc / mfcuk / mfkey32 paths)
- Mifare Plus / DESFire — separate specs with separate layouts
- Re-encode (round-tripping a decoded view back to 16 bytes — happy to add if a caller materialises)
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AccessBits ¶
type AccessBits struct {
// Blocks holds the per-block access view. For a 1K sector
// the slice has 4 entries (blocks 0-3); for 4K large
// sectors the slice has 4 entries representing the four
// block groups (5-block chunks); we render the layout in
// the small-sector form since 1K is the common case and
// the bit-packing matches.
Blocks [4]BlockAccess `json:"blocks"`
}
AccessBits is the structured view of a sector's access conditions — three bits per block (C1, C2, C3), expanded into the documented permission catalog per NXP AN10833 Table 6 (data blocks) and Table 7 (sector trailer).
type Block ¶
type Block struct {
// Index is the block's absolute position in the dump (0..63
// for 1K, 0..255 for 4K). -1 when decoding a single block
// with no dump context.
Index int `json:"index"`
// Sector is the sector number containing this block. -1 when
// Index is -1.
Sector int `json:"sector"`
// Kind is the recognised block role.
Kind BlockKind `json:"kind"`
// Hex is the operator-facing hex rendering of the raw 16
// bytes, uppercase, no separators.
Hex string `json:"hex"`
// ASCII is the printable-ASCII rendering of the raw bytes
// with '.' for non-printable. Useful for spotting strings in
// data blocks.
ASCII string `json:"ascii"`
// Trailer is populated when Kind == KindTrailer.
Trailer *Trailer `json:"trailer,omitempty"`
// Value is populated when Kind == KindValue.
Value *Value `json:"value,omitempty"`
// Manufacturer is populated when Kind == KindManufacturer.
Manufacturer *Manufacturer `json:"manufacturer,omitempty"`
}
Block is the structured decoded view of a 16-byte Mifare Classic block.
func DecodeBlock ¶
DecodeBlock decodes a single hex-encoded 16-byte Mifare Classic block. When index >= 0 the caller is telling us the block's position in the dump (so the manufacturer / trailer classification can use it); when index < 0 we classify from the block's structure alone (no manufacturer recognition without an index).
func DecodeDump ¶
DecodeDump decodes a full 1K (64 blocks = 1024 bytes) or 4K (256 blocks = 4096 bytes) Mifare Classic dump. Returns one Block per 16-byte chunk; rejects inputs whose length isn't a 16-byte multiple. Accepts ':' / '-' / '_' / whitespace separators.
type BlockAccess ¶
type BlockAccess struct {
// C1, C2, C3 are the three access bits.
C1 int `json:"c1"`
C2 int `json:"c2"`
C3 int `json:"c3"`
// Read, Write, Increment, Decrement enumerate the operations
// allowed for each key. Values: "A", "B", "A|B", "never".
// For the trailer block (Blocks[3]) the names map differently
// (read/write of Key A, access bits, Key B); see TrailerAccess.
Read string `json:"read,omitempty"`
Write string `json:"write,omitempty"`
Increment string `json:"increment,omitempty"`
Decrement string `json:"decrement,omitempty"`
// TrailerAccess is non-nil only for the trailer block
// (Blocks[3]), carrying the four trailer-specific permission
// slots from AN10833 Table 7.
TrailerAccess *TrailerAccess `json:"trailer_access,omitempty"`
}
BlockAccess names the access bit triplet for one block.
type BlockKind ¶
type BlockKind string
BlockKind enumerates the block roles we recognise.
const ( // KindManufacturer is sector 0 block 0 — the read-only NUID // / BCC / SAK / ATQA / manufacturer-data block. KindManufacturer BlockKind = "manufacturer" // KindTrailer is the last block of each sector — Key A, // access bits, GPB, Key B. KindTrailer BlockKind = "sector_trailer" // KindValue is a value-formatted block (value + complement + // duplicate value + address byte + complement structure). // We classify a block as value when the complement integrity // check passes. KindValue BlockKind = "value" // KindData is the catch-all for ordinary data blocks. KindData BlockKind = "data" )
type Manufacturer ¶
type Manufacturer struct {
NUIDHex string `json:"nuid_hex"`
BCC int `json:"bcc"`
BCCValid bool `json:"bcc_valid"`
SAK int `json:"sak"`
ATQA string `json:"atqa_hex"`
ManufacturerHex string `json:"manufacturer_data_hex"`
ICManufacturer string `json:"ic_manufacturer,omitempty"`
}
Manufacturer is the decoded manufacturer-block view. NUID can be either 4-byte (single-size UID) or part of a 7-byte UID chained across two CT (cascade tag) reads — this dump format only shows the first 4 bytes, so we render them and the BCC without trying to reconstruct a 7-byte UID. ATQA / SAK / IC manufacturer code are surfaced for cross-reference with the well-known tag-type tables.
type Trailer ¶
type Trailer struct {
KeyAHex string `json:"key_a_hex"`
AccessBytes string `json:"access_bytes_hex"`
GeneralByte int `json:"general_purpose_byte"`
KeyBHex string `json:"key_b_hex"`
AccessValid bool `json:"access_bits_valid"`
AccessBits *AccessBits `json:"access_bits,omitempty"`
}
Trailer is the decoded sector-trailer view. KeyA / KeyB are hex-rendered for the JSON shape; AccessBits expands into the per-block permission table.
type TrailerAccess ¶
type TrailerAccess struct {
KeyAWrite string `json:"key_a_write"`
AccessBitsRead string `json:"access_bits_read"`
AccessBitsWrite string `json:"access_bits_write"`
KeyBRead string `json:"key_b_read"`
KeyBWrite string `json:"key_b_write"`
}
TrailerAccess is the trailer-block permission table — what each key can do with Key A, the access bits themselves, and Key B.
type Value ¶
type Value struct {
// Value is the signed 32-bit value extracted from bytes 0-3.
Value int32 `json:"value"`
// ValueValid is true iff the value at bytes 0-3, its
// complement at bytes 4-7, and the duplicate at bytes 8-11
// all satisfy the complement integrity check.
ValueValid bool `json:"value_integrity_valid"`
// Address is the address byte (byte 12).
Address int `json:"address"`
// AddressValid mirrors ValueValid for the 4-byte address
// structure (byte 12 / ~byte 13 / byte 14 / ~byte 15).
AddressValid bool `json:"address_integrity_valid"`
}
Value is the decoded value-block view.