How to Verify Smart Card Checksums — CRC, LRC, CMAC, MAC Explained
Smart card protocols rely on checksums and MACs (Message Authentication Codes) at multiple layers: CRC-16 protects anti-collision bytes, Retail MAC authenticates APDU commands, CMAC secures SCP03 sessions, and XOR LRC validates ATR and data blocks. This guide covers all common smart card checksum algorithms with ready-to-use code for each.
1. CRC-16 Variants — Quick Reference
| Name | Poly | Init | RefIn/Out | XorOut | Used In |
|---|---|---|---|---|---|
| CRC-16/CCITT | 0x1021 | 0xFFFF | No/No | 0x0000 | MIFARE, ISO 14443-3 |
| CRC-16/XMODEM | 0x1021 | 0x0000 | No/No | 0x0000 | Bootloaders, T=0 |
| CRC-16/ANSI | 0x8005 | 0xFFFF | Yes/Yes | 0x0000 | Modbus, some cards |
| CRC-16/ARC | 0x8005 | 0x0000 | Yes/Yes | 0x0000 | LHA, some NFC |
| CRC-16/KERMIT | 0x1021 | 0x0000 | Yes/Yes | 0x0000 | KERMIT protocol |
2. CRC-16/CCITT — MIFARE and ISO 14443-3
This is the most common NFC/contactless CRC. All MIFARE and ISO 14443-3 commands append a 2-byte CRC:
def crc16_ccitt(data):
"""MIFARE / ISO 14443-3 CRC-16 (CCITT, polynomial 0x1021)."""
crc = 0xFFFF
for byte in data:
crc ^= (byte << 8)
for _ in range(8):
if crc & 0x8000:
crc = ((crc << 1) ^ 0x1021) & 0xFFFF
else:
crc = (crc << 1) & 0xFFFF
return crc
# Example: MIFARE REQA command = [0x26]
# CRC-16 = 0x4B 0x99 → transmitted as CRC[0]=0x4B, CRC[1]=0x99
# MIFARE authenticate command:
# [0x60, 0x00] → CRC = crc16_ccitt(bytes([0x60, 0x00])) → check against received CRC
3. XOR LRC — Longitudinal Redundancy Check
Simple XOR of all data bytes. Common in ATR historical bytes, EMV records, and proprietary card formats:
def xor_lrc(data):
"""XOR Longitudinal Redundancy Check."""
lrc = 0x00
for byte in data:
lrc ^= byte
return lrc
def verify_lrc(data, expected_lrc):
"""Verify LRC on data + LRC byte should be 0x00."""
return (xor_lrc(data) ^ expected_lrc) == 0x00
# Example: data=[0x01, 0x02, 0x03], LRC=0x00 (1^2^3=0)
# Verify: xor all bytes including LRC → must be 0
4. 3DES Retail MAC — ISO 9797-1 Algorithm 3
The Retail MAC is the workhorse of smart card security: EMV GENERATE AC, SCP02 APDU authentication, M/Chip, and Visa CVV/CVC all use it:
from Crypto.Cipher import DES3
def retail_mac(key_16bytes, data):
"""ISO 9797-1 Algorithm 3 (Retail MAC).
key = 16 bytes (2TDEA: k1=k2 for first 8, k3 for last 8)
"""
k1 = key_16bytes[:8] # DES key left
k2 = key_16bytes[8:16] # DES key right
k3 = k1 # Retail MAC: k3 = k1
# Padding: ISO 9797-1 Method 2 (0x80 + 0x00...)
pad_len = 8 - (len(data) % 8)
padded = data + b'\x80' + b'\x00' * (pad_len - 1)
# Step 1: DES-CBC encrypt with k1, IV=0x00...00
cipher1 = DES3.new(k1 + k2, DES3.MODE_CBC, iv=b'\x00'*8)
# Triple-DES first 8 bytes with k1+k2, rest with k1 only (Retail)
encrypted = bytearray()
for i in range(0, len(padded), 8):
block = padded[i:i+8]
# Triple-DES encrypt block using k1+k2
c = DES3.new(k1 + k2, DES3.MODE_ECB)
encrypted.extend(c.encrypt(block))
# Step 2: Decrypt last block with k3 (k1 in Retail)
last = encrypted[-8:]
cipher_dec = DES3.new(k3, DES3.MODE_ECB) # Single DES decrypt
last_dec = cipher_dec.decrypt(last)
# Step 3: Encrypt again with k1
cipher_enc = DES3.new(k1, DES3.MODE_ECB)
mac = cipher_enc.encrypt(last_dec)
return mac # 8 bytes MAC
5. AES-CMAC — NIST SP 800-38B (SCP03, DESFire)
from Crypto.Hash import CMAC
from Crypto.Cipher import AES
def aes_cmac(key, data):
"""AES-CMAC-128 per NIST SP 800-38B / RFC 4493."""
mac = CMAC.new(key, ciphermod=AES)
mac.update(data)
return mac.digest() # 16 bytes
# SCP03 command MAC: CMAC over (APDU header + data, padded)
# scp03_mac = aes_cmac(S_MAC_key, command_data)
# APDU: [CLA INS P1 P2 Lc Data MAC(8_bytes) Le]
6. When to Use Each Checksum/MAC
| Algorithm | Output | Security | Use Case |
|---|---|---|---|
| CRC-16/CCITT | 2 bytes | Error detection only | Anti-collision, transport integrity |
| XOR LRC | 1 byte | Error detection only | ATR validation, record integrity |
| Retail MAC (3DES) | 8 bytes | Cryptographic auth | EMV, SCP02, CVV/CVC |
| AES-CMAC | 8-16 bytes | Cryptographic auth | SCP03, DESFire, FIDO2 |
| ISO 7816-4 SM MAC | 8 bytes | Command auth | Secure Messaging APDU |
Related Tools
Crypto Checksum Verifier — Calculate all checksums in-browser | Key Diversification Calculator — Derive MAC keys | Session Key Visualizer — SCP02/SCP03 session keys | GP SCP02 vs SCP03 Guide