PIN Block Encoder / Decoder
Encode and decode ISO 9564-1 PIN blocks (Format 0, 1, 2, 3, and 4). Supports clear and encrypted PIN blocks (3DES for Formats 0–3, AES for Format 4), XOR with PAN block, and step-by-step breakdown.
Use test data only. All calculations run locally in your browser — PayProbe never sees, transmits, or stores your PAN, CVV, keys, PINs, or cryptographic inputs. How we handle data →
Inputs
ISO 9564-1 PIN block formats
0 | len | PIN | FFFF…FF XOR PAN block. Most common (ANSI X9.8).1 | len | PIN | random fill No PAN XOR — uniqueness via random bytes.2 | len | PIN | FFFF…FF No PAN XOR, deterministic. Offline PIN to the chip (EMV) and PIN change — never for transmission over networks.3 | len | PIN | random fill XOR PAN block. Visa variant.4 | len | PIN | 0xA fill | 8 random bytes AES-encrypt, XOR 16-byte PAN field, AES-encrypt again. Required for AES key environments (TR-31 / X9.24-3).Formats 0/3: PAN block = 0000 || 12 rightmost PAN digits excluding check digit, final block encrypted with 3DES. Format 4: PAN field = (PAN length − 12) || full PAN || zero fill over 16 bytes, encrypted with AES-128/192/256.
# ISO 9564 Format 0 PIN Block encode/decode
# No external dependencies required
def encode_pin_block(pin: str, pan: str) -> str:
"""Encode ISO 9564-1 Format 0 PIN block."""
# PIN field: 0 | len(pin) | pin digits | F padding to 14 nibbles
pin_field = "0" + format(len(pin), "X") + pin + "F" * (14 - len(pin))
# PAN field: 0000 | 12 rightmost PAN digits excluding check digit
pan_field = "0000" + pan[-13:-1]
# XOR the two 8-byte fields
result = int(pin_field, 16) ^ int(pan_field, 16)
return format(result, "016X")
def decode_pin_block(pin_block: str, pan: str) -> str:
"""Decode ISO 9564-1 Format 0 PIN block, return PIN digits."""
pan_field = "0000" + pan[-13:-1]
pin_field = int(pin_block, 16) ^ int(pan_field, 16)
s = format(pin_field, "016X")
pin_len = int(s[1], 16)
return s[2:2 + pin_len]
# Example
pin = "1234"
pan = "4111111111111111"
block = encode_pin_block(pin, pan)
print(f"PIN block : {block}")
print(f"Decoded : {decode_pin_block(block, pan)}")
# PIN block : 0412AC9AEEB2D4B0 (varies by PAN)
# Decoded : 1234
What a PIN block is
A PIN block is the 8-byte (64-bit) structure that carries a PIN securely between devices. A PIN is never transmitted as plain digits; it is first formatted into a block — combined with the PAN or a fill pattern depending on the format — and then encrypted under a PIN key inside a hardware security module. This tool shows how the most common formats (ISO 9564 Formats 0, 1, 2, 3, and 4) are constructed and parsed, which is essential for anyone building or debugging issuing, acquiring, or HSM integrations.
Format 0 XORs the formatted PIN with the PAN, so the same PIN produces different blocks on different cards. Format 1 uses a random fill instead of the PAN (useful when no PAN is available). Format 2 is deterministic with F-fill and no PAN binding — reserved for offline PIN verification against the chip and PIN change operations, never for transmitting PINs across a network. Format 3 adds random padding to Format 0, and Format 4 is the AES-based format introduced for modern key standards: the PIN field is a full 16-byte block (with 0xA fill and 8 random bytes) that is AES-enciphered, XORed with a 16-byte PAN field carrying the full PAN, then enciphered again. PCI PIN requirements mandate ISO Format 4 wherever AES PIN keys are used. Choosing the wrong format, or the wrong PAN-digit selection, is one of the most common causes of "invalid PIN" errors between two correctly keyed systems.
Test data only
This tool is for understanding and testing PIN-block formatting with test PINs and test PANs. The clear PIN and encryption all happen locally in your browser — nothing is transmitted or stored. Never enter a real cardholder PIN; in production, PIN blocks are only ever formed and encrypted inside a certified HSM.