All Tools
Tools/UDK Calculator
🔑
Cryptography & KeysAvailable
UDK Calculator
Derive EMV Unique Derived Keys (UDK) from an Issuer Master Key (IMK) and PAN/PAN Sequence Number — for EMV card personalisation and transaction key derivation.
Follows EMV 4.4 Book 2 Annex A1.4.1Option A (PAN up to 16 digits: PAN ∥ PSN, rightmost 16 digits); longer PANs rejected, Option B not implemented·verified Oct 2026· double-checked Oct 2026— cross-checked against OpenSSL TDES
Try it as a puzzle: deliver a chip’s cryptogram to the one system that can check it →
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 →
Algorithm (EMV Book 2 A1.4.1, Option A)
- Y = the rightmost 16 digits of PAN ∥ PAN Sequence Number (PSN 00 if absent; left-padded with zeros if shorter)
- Left half = 2TDEA encryption of Y under the IMK
- Right half = 2TDEA encryption of (Y XOR FFFFFFFFFFFFFFFF) under the IMK
- UDK = left ∥ right, with DES odd parity set on each byte
- Option B, required for PANs longer than 16 digits, hashes PAN ∥ PSN with SHA-1 first; this tool implements Option A only and reports an error for longer PANs
- The IMK must be a double-length (32 hex) TDES key
</> Code Implementation
# EMV ICC master key (UDK) derivation — EMV Book 2 A1.4.1, Option A
# Requires: pip install pycryptodome
from Crypto.Cipher import DES3
def adjust_parity(b: int) -> int:
# Force DES odd parity on a byte
return b ^ 1 if bin(b).count("1") % 2 == 0 else b
def des3_encrypt(key: bytes, data: bytes) -> bytes:
return DES3.new(key[:16] + key[:8], DES3.MODE_ECB).encrypt(data)
def derive_udk(imk_hex: str, pan: str, pan_seq: str = "00") -> str:
imk = bytes.fromhex(imk_hex)
# Option A (PAN up to 16 digits); the PSN is always two digits ("1" -> "01").
y = bytes.fromhex((pan + pan_seq.zfill(2))[-16:].zfill(16))
left = des3_encrypt(imk, y)
right = des3_encrypt(imk, bytes(b ^ 0xFF for b in y))
return bytes(adjust_parity(b) for b in left + right).hex().upper()
print(derive_udk("0123456789ABCDEFFEDCBA9876543210", "4111111111111111", "01"))
# A768CE9E1551FDE3A45B4C7C3DE9DC52
Something wrong or missing? Report a problem— we check reports against the specs and fix them.