BAC and PACE key calculator
Derive the keys an inspection system uses to open an ePassport chip: BAC K_seed, K_enc and K_mac, and the PACE password key K_π for 3DES and AES. Calculated as defined in ICAO Doc 9303 Part 11.
How BAC keys are derived
Basic Access Control uses three fields from the MRZ, each followed by its check digit: the document number, the date of birth and the date of expiry. Joined together they form the MRZ information, for example L898902C<369080619406236. A document number shorter than nine characters is padded with <.
The SHA-1 hash of the MRZ information is calculated, and its first 16 bytes become Kseed. Two session keys are derived from it with the key derivation function KDF(K, c): the SHA-1 hash of K followed by a 32-bit counter c. Counter 1 gives the encryption key Kenc, counter 2 gives the MAC key Kmac. Both are two-key 3DES keys, so the first 16 bytes of each hash are taken and the parity bit of every byte is adjusted.
How the PACE password key is derived
PACE replaces BAC on newer documents and accepts two kinds of password. With the MRZ, the password is the full 20-byte SHA-1 hash of the same MRZ information. With the CAN, a six-digit number printed on the document, the password is the CAN itself. In both cases the key Kπ is KDF(password, 3).
| Cipher | Hash | Key length |
|---|---|---|
| 3DES | SHA-1 | 16 bytes, parity adjusted |
| AES-128 | SHA-1 | 16 bytes |
| AES-192 | SHA-256 | 24 bytes |
| AES-256 | SHA-256 | 32 bytes |
Which cipher a chip uses is announced in its PACEInfo in EF.CardAccess.
ICAO test vectors
The calculator reproduces the worked examples from ICAO Doc 9303 Part 11.
| MRZ information | L898902C<369080619406236 |
|---|---|
| Kseed | 239AB9CB282DAF66231DC5A4DF6BFBAE |
| Kenc | AB94FDECF2674FDFB9B391F85D7F76F2 |
| Kmac | 7962D9ECE03D1ACD4C76089DCE131543 |
| PACE MRZ information | T22000129364081251010318 |
| Kπ AES-128 | 89DED1B26624EC1E634C1989302849DD |
Code
import hashlib
def kdf(seed: bytes, counter: int, cipher: str = "3DES") -> bytes:
data = seed + counter.to_bytes(4, "big")
if cipher in ("3DES", "AES128"):
key = hashlib.sha1(data).digest()[:16]
else:
key = hashlib.sha256(data).digest()[:24 if cipher == "AES192" else 32]
if cipher == "3DES": # set odd parity on every byte
key = bytes((b & 0xFE) | (bin(b & 0xFE).count("1") % 2 == 0) for b in key)
return key
info = b"L898902C<369080619406236"
k_seed = hashlib.sha1(info).digest()[:16]
k_enc, k_mac = kdf(k_seed, 1), kdf(k_seed, 2)
k_pi = kdf(hashlib.sha1(b"T22000129364081251010318").digest(), 3, "AES128")
Questions
- Is it safe to enter real passport data?
- The keys are calculated in your browser and nothing leaves your device. Keep in mind that BAC and PACE keys only open the chip of a document that is physically in front of the reader, and anyone holding the document can read the MRZ anyway. For development, the ICAO examples or generated data are the better choice.
- Why does my reader still fail to open the chip?
- Most often the MRZ was read with an error, so one of the three check digits or a character of the document number is wrong. Check the MRZ first with the MRZ checker. Other common causes are a chip that supports only PACE with a cipher your reader does not offer, or a document number longer than nine characters.
- What is the difference between BAC and PACE?
- BAC derives its session keys directly from the MRZ, so their strength is limited by the entropy of the MRZ. PACE uses the password only to run a key agreement (Diffie–Hellman), which produces strong session keys even from a short password such as the CAN.
- Where is the CAN printed?
- On ID cards it is usually a six-digit number on the front. Many passports issued in recent years print it on the data page as well.