Project 3: Point-to-Point Encryption (P2PE) Simulator
Project 3: Point-to-Point Encryption (P2PE) Simulator
Project Overview
| Attribute | Value |
|---|---|
| Difficulty | Level 4: Expert |
| Time Estimate | 2-3 weeks |
| Programming Language | C |
| Knowledge Area | Payment Security / Key Management |
| Key Technologies | DUKPT, HSM Simulation, 3DES/AES |
| Coolness Level | Level 4: Hardcore Tech Flex |
| Business Potential | 3. The โService & Supportโ Model |
Learning Objectives
By completing this project, you will:
- Understand P2PE architecture - Learn why encryption โat the point of swipeโ is the gold standard
- Implement DUKPT key derivation - Master the Derived Unique Key Per Transaction algorithm
- Simulate HSM boundaries - Understand how trust boundaries work in payment systems
- Handle key injection ceremonies - Learn how devices get their initial keys
- Manage transaction counters - Understand key exhaustion and device lifecycle
- Apply defense-in-depth principles - See why multiple security layers matter
The Core Question Youโre Answering
โHow can card data travel through merchant systems without ever being decryptable by the merchant?โ
This is the fundamental problem P2PE solves:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE P2PE SECURITY MODEL โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ WHAT THE MERCHANT SEES: โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Customer swipes card โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Card Reader โ โ
โ โ (P2PE Device) โ โ
โ โ โ โ
โ โ PAN: 4532...0366 โ โ Encrypted inside the reader โ
โ โ โโโโโโโโโโโโโโโโ โ โ
โ โ Output: โ โ
โ โ "A7F3B2C1D4E5..." โ โ Merchant only sees ciphertext โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Merchant POS โ Passes encrypted blob โ
โ โ System โ โโโโโโโโโโโโโโโโโบ โ
โ โ โ CANNOT decrypt โ
โ โ Key? โ None! โ (no key access) โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Payment โ โ
โ โ Processor HSM โ โ
โ โ โ โ
โ โ Key: โ Has DEK โ โ ONLY entity that can decrypt โ
โ โ Decrypts inside โ โ
โ โ HSM boundary โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ RESULT: Even if merchant is completely compromised, โ
โ attacker cannot recover card numbers from transaction logs โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Why this is the โgold standardโ:
- Merchant systems NEVER touch plaintext card data
- Even with full network access, attackers see only ciphertext
- Unique key per transaction prevents mass compromise
- HSM boundary ensures keys never exist in regular memory
Deep Theoretical Foundation
1. What is P2PE?
Point-to-Point Encryption encrypts card data at the moment of capture (the โpointโ of swipe/dip/tap) and keeps it encrypted until it reaches a secure decryption environment (the processorโs HSM).
PCI P2PE Standard:
- Defined by PCI Security Standards Council
- Requires validated P2PE solutions
- Dramatically reduces merchant PCI scope
The Security Chain:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ P2PE SECURITY CHAIN โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ Card โ โ Terminal โ โ Merchant โ โProcessor โ โ
โ โ โโโโโบโ (P2PE) โโโโโบโ Network โโโโโบโ HSM โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ
โ โ
โ โ โ โ โ โ
โ โผ โผ โผ โผ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ Plaintextโ โ Encrypted โ โ Encrypted โ โ Plaintext โ โ
โ โ PAN โ โ (DUKPT key) โ โ (passing โ โ (inside HSM โ โ
โ โ โ โ โ โ through) โ โ only) โ โ
โ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ
โ Trust Boundary: Card โ Terminal (data enters protected channel) โ
โ Trust Boundary: Processor HSM (data exits protected channel) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
2. DUKPT: Derived Unique Key Per Transaction
DUKPT is THE algorithm that makes P2PE work. It generates a unique encryption key for every transaction from a base key.
The Problem DUKPT Solves:
- If every terminal uses the same key โ compromise one, compromise all
- If terminals store individual keys โ key distribution nightmare
- If terminals derive keys โ need secure derivation mechanism
DUKPT Solution: Inject a โBase Derivation Keyโ (BDK) once. Terminal derives unique keys forever.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ DUKPT KEY HIERARCHY โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Base Derivation Key โ Known only to HSM โ
โ โ (BDK) โ 128 bits (2-key 3DES) โ
โ โ โ or 256 bits (AES) โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ Key Derivation โ One-way function โ
โ KSN[0:9] XOR mask โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Initial PIN โ Unique per terminal โ
โ โ Encryption Key โ Injected into device โ
โ โ (IPEK) โ โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ Future Key Tree โ 21 "future keys" derived โ
โ (pre-computed) โ from counter bits โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โ โผ โผ โผ โผ โ
โ โโโโโโโโโ โโโโโโโโโ โโโโโโโโโ โโโโโโโโโ โ
โ โ FK[1] โ โ FK[2] โ โ FK[3] โ ... โFK[21] โ โ
โ โโโโโฌโโโโ โโโโโฌโโโโ โโโโโฌโโโโ โโโโโฌโโโโ โ
โ โ โ โ โ โ
โ โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโ โ
โ โ โ
โ Transaction Key โ Derived from appropriate โ
โ Derivation โ future key based on counter โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Transaction Key โ Unique per transaction โ
โ โ (PIN or Data) โ Used once, then forgotten โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Key Serial Number (KSN):
The KSN is a 10-byte identifier that tracks which key to use:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ KEY SERIAL NUMBER (KSN) FORMAT โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโ โ
โ โ BDK ID (5B) โ Device ID (4B) โ Counter (21 bits) โ โ
โ โ โ โ โ โ
โ โ Identifies โ Identifies โ Tracks which โ โ
โ โ which BDK โ which terminal โ transaction โ โ
โ โ was used โ โ (max 1M) โ โ
โ โโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Example: FFFF 9876543210 00001 โ
โ ^^^^ ^^^^^^^^^^ ^^^^^ โ
โ BDK Device Counter=1 (first transaction) โ
โ โ
โ The KSN is sent WITH the encrypted data so the HSM knows โ
โ which key to derive for decryption! โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
3. DUKPT Key Derivation Algorithm
Simplified DUKPT Derivation (the actual spec is in ANSI X9.24):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ DUKPT DERIVATION STEPS โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ INITIAL KEY INJECTION (happens once at manufacturing): โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ 1. Extract device ID from KSN: FFFF 9876543210 00000 โ
โ ^^^^^^^^^^ โ
โ 2. Create registration value: 9876543210 || 0000000 (padded) โ
โ โ
โ 3. IPEK = 3DES_encrypt(registration, BDK) โ
โ โ
โ 4. Store IPEK in device's secure memory โ
โ (BDK is NEVER stored in device!) โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ TRANSACTION KEY DERIVATION (happens per transaction): โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Given: Counter = 0x00005 (binary: 0000 0000 0101) โ
โ โ
โ 1. Find set bits in counter: bits 0 and 2 โ
โ โ
โ 2. Start with current_key = IPEK โ
โ โ
โ 3. For each set bit (from left to right): โ
โ - Create key_register = counter with only this bit set โ
โ - current_key = derive_key(current_key, key_register) โ
โ โ
โ 4. Result: transaction_key for this counter โ
โ โ
โ derive_key(): โ
โ - XOR key_register into current_key_right_half โ
โ - Encrypt with current_key_left_half โ
โ - Result is new key โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Why this design?:
- Forward secrecy: Compromised key doesnโt reveal past keys
- No reverse derivation: Canโt derive IPEK from transaction key
- Efficient: Only 21 derivations needed for 1M transactions
- Stateless HSM: HSM derives key on-demand from KSN + BDK
4. Hardware Security Modules (HSMs)
An HSM is a dedicated cryptographic processor with physical security protections.
HSM Properties:
- Tamper-resistant: Physical attacks destroy keys
- FIPS 140-2/3 certified: Validated security
- Key management: Keys generated, stored, and used inside HSM
- Audit logging: All operations logged
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HSM TRUST BOUNDARY โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ HSM โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ SECURE ENCLAVE โ โ โ
โ โ โ โ โ โ
โ โ โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ โ โ
โ โ โ โ BDK โ โ KEK โ โ Other โ โ โ โ
โ โ โ โ Store โ โ Store โ โ Keys โ โ โ โ
โ โ โ โโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโ โ โ โ
โ โ โ โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โ โ CRYPTO ENGINE โ โ โ โ
โ โ โ โ โ โ โ โ
โ โ โ โ โข 3DES / AES operations โ โ โ โ
โ โ โ โ โข DUKPT key derivation โ โ โ โ
โ โ โ โ โข PIN translation โ โ โ โ
โ โ โ โ โ โ โ โ
โ โ โ โ Keys NEVER leave this boundary in plaintext โ โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ API LAYER โ โ โ
โ โ โ โ โ โ
โ โ โ decrypt_with_dukpt(encrypted_data, ksn) โ plaintext โ โ โ
โ โ โ encrypt_with_dukpt(plaintext, ksn) โ encrypted_data โ โ โ
โ โ โ translate_pin_block(encrypted_pin, ksn) โ translated_pin โ โ โ
โ โ โ โ โ โ
โ โ โ Input: encrypted data goes IN โ โ โ
โ โ โ Output: plaintext comes OUT (or processed result) โ โ โ
โ โ โ Keys: NEVER come out โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ PHYSICAL SECURITY: โ
โ โข Tamper-evident seals โ
โ โข Intrusion detection (zeroizes keys if opened) โ
โ โข Environmental sensors (voltage, temperature) โ
โ โข FIPS 140-2 Level 3 or Level 4 โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
5. Transaction Counter and Key Exhaustion
The Counter Problem:
- DUKPT counter is 21 bits โ max 2,097,152 transactions
- After exhaustion, device must be re-keyed
Counter Management:
Counter: 0x00000 โ First transaction
Counter: 0x00001 โ Second transaction
Counter: 0x00002 โ Third transaction
...
Counter: 0x1FFFFF โ Last transaction (key exhausted!)
What happens at exhaustion:
- Device refuses to encrypt
- Alert sent to management system
- Device must be returned for re-keying (new IPEK injection)
Project Specification
What Youโll Build
A P2PE simulator with:
- Simulated Terminal: Encrypts โcard swipesโ using DUKPT
- Simulated HSM: Decrypts using BDK and KSN
- Trust Boundary Enforcement: Code that โcannotโ access plaintext outside HSM
- Key Injection Ceremony: Initial device provisioning
- Transaction Flow Visualization: See encryption state at each step
Expected Output
# Initialize the system
$ ./p2pe init
[HSM] Generating Base Derivation Key...
[HSM] BDK created: FFFF (identifier)
[KEY CEREMONY] Starting key injection for terminal TRM001...
[KEY CEREMONY] Deriving IPEK from BDK + Device ID...
[TERMINAL TRM001] IPEK received and stored securely
[SYSTEM] P2PE system initialized
# Simulate a card swipe
$ ./p2pe swipe --terminal TRM001 --pan 4532015112830366
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ P2PE TRANSACTION FLOW โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
โ โ
โ STEP 1: CARD READ โ
โ โโโโโโโโโโโโโโโโโ โ
โ Terminal TRM001 reads: 4532015112830366 โ
โ Track 2 data captured โ
โ โ
โ STEP 2: KEY DERIVATION (inside terminal) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Current counter: 0x00017 โ
โ KSN: FFFF9876543210000017 โ
โ Deriving transaction key from IPEK... โ
โ Transaction key: [PROTECTED - inside terminal secure memory] โ
โ โ
โ STEP 3: ENCRYPTION (inside terminal) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Plaintext: 4532015112830366 โ
โ Algorithm: 3DES-CBC โ
โ Output: A7F3B2C1D4E5F687H9I0J1K2L3M4N5O6 โ
โ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ โ
โ This is all the merchant system sees! โ
โ โ
โ STEP 4: TRANSMISSION โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ TRANSACTION REQUEST โ โ
โ โ KSN: FFFF9876543210000017 โ โ
โ โ Encrypted Data: A7F3B2C1D4E5F687H9I0J1K2L3M4N5O6 โ โ
โ โ Merchant: MERCH001 โ โ
โ โ Amount: $50.00 โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ STEP 5: HSM PROCESSING โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ [HSM] Received KSN: FFFF9876543210000017 โ
โ [HSM] Extracting BDK ID: FFFF โ
โ [HSM] Looking up BDK...found โ
โ [HSM] Deriving transaction key from BDK + KSN... โ
โ [HSM] Decrypting inside secure boundary... โ
โ [HSM] Plaintext recovered (inside HSM only): 4532015112830366 โ
โ [HSM] Forwarding to issuer for authorization... โ
โ โ
โ STEP 6: RESPONSE โ
โ โโโโโโโโโโโโโโโโ โ
โ Authorization: APPROVED โ
โ Auth Code: 123456 โ
โ โ
โ Counter incremented: 0x00017 โ 0x00018 โ
โ Remaining transactions: 2,097,128 โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
# Verify merchant cannot decrypt
$ ./p2pe merchant-view --transaction 12345
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MERCHANT SYSTEM VIEW โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฃ
โ โ
โ Transaction ID: 12345 โ
โ Terminal: TRM001 โ
โ Timestamp: 2024-01-15 14:32:17 โ
โ โ
โ Card Data: A7F3B2C1D4E5F687H9I0J1K2L3M4N5O6 (encrypted) โ
โ KSN: FFFF9876543210000017 โ
โ โ
โ โ ๏ธ Decryption key: NOT AVAILABLE โ
โ โ ๏ธ Plaintext PAN: NOT AVAILABLE โ
โ โ
โ The merchant system has NO access to: โ
โ โข The Base Derivation Key (BDK) โ
โ โข The Initial PIN Encryption Key (IPEK) โ
โ โข Any transaction key โ
โ โ
โ Even with full database access, the card number cannot be recovered! โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Project Structure
p2pe_simulator/
โโโ src/
โ โโโ main.c # CLI entry point
โ โโโ terminal/
โ โ โโโ terminal.c # Terminal simulation
โ โ โโโ terminal.h
โ โ โโโ dukpt_encrypt.c # Encryption with DUKPT
โ โ โโโ secure_memory.c # Simulated secure memory
โ โโโ hsm/
โ โ โโโ hsm.c # HSM simulation
โ โ โโโ hsm.h
โ โ โโโ dukpt_decrypt.c # Decryption with DUKPT
โ โ โโโ key_store.c # BDK storage
โ โ โโโ trust_boundary.c # Boundary enforcement
โ โโโ key_management/
โ โ โโโ key_ceremony.c # Key injection
โ โ โโโ dukpt_derive.c # DUKPT algorithm
โ โ โโโ counter.c # Transaction counter
โ โโโ transport/
โ โ โโโ message.c # Transaction message format
โ โ โโโ protocol.c # Simulated network
โ โโโ crypto/
โ โโโ des3.c # 3DES implementation
โ โโโ aes.c # AES for modern DUKPT
โโโ tests/
โ โโโ test_dukpt.c
โ โโโ test_terminal.c
โ โโโ test_hsm.c
โ โโโ test_vectors/ # ANSI X9.24 test vectors
โโโ docs/
โ โโโ dukpt_explanation.md
โโโ Makefile
โโโ README.md
Core API Design
// terminal.h
typedef struct {
char terminal_id[16];
unsigned char ipek[16]; // Initial PIN Encryption Key
unsigned char ksn_base[10]; // KSN without counter
uint32_t counter; // Current transaction counter
} Terminal;
// Encrypt card data (happens inside terminal's secure boundary)
typedef struct {
unsigned char ciphertext[64];
size_t ciphertext_len;
unsigned char ksn[10]; // Full KSN with counter
} EncryptedTrack;
EncryptedTrack terminal_encrypt(Terminal* term, const char* track_data);
// hsm.h
typedef struct {
char hsm_id[16];
// BDK is stored inside and NEVER exposed
} HSM;
// Initialize HSM (loads BDK from secure storage)
HSM* hsm_init(const char* bdk_path);
// Decrypt (plaintext only exists inside this function)
// Returns authorization result, NOT the plaintext
typedef struct {
bool success;
char auth_code[8];
char error[64];
} AuthResult;
AuthResult hsm_process_transaction(HSM* hsm, const EncryptedTrack* track,
const char* merchant_id, int amount_cents);
// key_ceremony.h
// Generate IPEK for a new terminal (happens in secure facility)
bool key_ceremony_inject(HSM* hsm, Terminal* term, const char* device_id);
Solution Architecture
System Design
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ P2PE SIMULATOR ARCHITECTURE โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ TERMINAL SIMULATOR โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ SECURE ELEMENT (simulated) โ โ โ
โ โ โ โ โ โ
โ โ โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ โ โ
โ โ โ โ IPEK โ โ Counter โ โ KSN Base โ โ โ โ
โ โ โ โ (secret) โ โ Manager โ โ โ โ โ โ
โ โ โ โโโโโโโฌโโโโโโโ โโโโโโโฌโโโโโโโ โโโโโโโฌโโโโโโโ โ โ โ
โ โ โ โ โ โ โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โ โ โ โ โ
โ โ โ โผ โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โ โ DUKPT Engine โ โ โ โ
โ โ โ โ (key derive + โ โ โ โ
โ โ โ โ encrypt) โ โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ
โ โ โ Encrypted output only โ โ
โ โ โผ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ TERMINAL APPLICATION โ โ โ
โ โ โ โข Receives encrypted blob โ โ โ
โ โ โ โข Formats transaction message โ โ โ
โ โ โ โข NO access to plaintext or keys โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โ {KSN, Encrypted Data, Amount} โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ MERCHANT SYSTEM (simulated) โ โ
โ โ โ โ
โ โ โข Stores encrypted transaction โ โ
โ โ โข Forwards to processor โ โ
โ โ โข CANNOT decrypt (no keys) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โ {KSN, Encrypted Data, Amount} โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ HSM SIMULATOR โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ SECURE BOUNDARY โ โ โ
โ โ โ โ โ โ
โ โ โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ โ โ
โ โ โ โ BDK โ โ DUKPT โ โ โ โ
โ โ โ โ Storage โโโโโบโ Derive & โ โ โ โ
โ โ โ โ โ โ Decrypt โ โ โ โ
โ โ โ โโโโโโโโโโโโโโ โโโโโโโฌโโโโโโโ โ โ โ
โ โ โ โ โ โ โ
โ โ โ โผ โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โ โ PLAINTEXT โ โ Exists only here โ โ โ
โ โ โ โ (in memory, โ โ โ โ
โ โ โ โ for auth) โ โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โ โ โ โ โ
โ โ โ โผ โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โ โ Forward to โ โ โ โ
โ โ โ โ Card Network โ โ โ โ
โ โ โ โโโโโโโโโโโโโโโโโโ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ
โ โ โ Auth result (no plaintext) โ โ
โ โ โผ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Trust Boundary Enforcement
How to simulate HSM trust boundary in software:
// trust_boundary.c
// All code that touches plaintext must be in this file
// This simulates "inside the HSM"
// Private - never expose
static unsigned char bdk[16];
// The ONLY function that can see plaintext
// Notice: it returns AuthResult, NOT the plaintext
static AuthResult process_inside_hsm(const EncryptedTrack* track) {
// 1. Derive key from KSN
unsigned char transaction_key[16];
dukpt_derive_key(bdk, track->ksn, transaction_key);
// 2. Decrypt
char plaintext[64];
des3_decrypt(track->ciphertext, transaction_key, plaintext);
// 3. Process (send to card network)
AuthResult result = send_to_issuer(plaintext);
// 4. CRITICAL: Zero plaintext before returning
explicit_bzero(plaintext, sizeof(plaintext));
explicit_bzero(transaction_key, sizeof(transaction_key));
// 5. Return only the auth result
return result;
}
// Public API - what outside code can call
AuthResult hsm_process_transaction(HSM* hsm, const EncryptedTrack* track,
const char* merchant_id, int amount_cents) {
// Validate inputs
// Log the request (without plaintext!)
// Call the internal function
return process_inside_hsm(track);
}
Implementation Guide
Phase 1: DUKPT Key Derivation
Goal: Implement the core DUKPT algorithm.
Start with test vectors from ANSI X9.24:
// Known test vectors
const char* BDK = "0123456789ABCDEFFEDCBA9876543210";
const char* KSN = "FFFF9876543210E00001";
// Expected IPEK: 6AC292FAA1315B4D858AB3A3D7D5933A
// Expected transaction key: depends on counter
Key derivation steps:
void dukpt_derive_ipek(const unsigned char* bdk,
const unsigned char* ksn,
unsigned char* ipek) {
// 1. Mask KSN: zero out counter bits
unsigned char masked_ksn[10];
memcpy(masked_ksn, ksn, 10);
masked_ksn[7] &= 0xE0; // Clear counter bits
masked_ksn[8] = 0x00;
masked_ksn[9] = 0x00;
// 2. Left half of IPEK
unsigned char data[8];
memcpy(data, masked_ksn, 8);
des3_encrypt(data, bdk, ipek); // Left 8 bytes
// 3. Right half of IPEK
unsigned char bdk_variant[16];
xor_with_constant(bdk, 0xC0C0C0C000000000C0C0C0C000000000, bdk_variant);
des3_encrypt(data, bdk_variant, ipek + 8); // Right 8 bytes
}
Phase 2: Terminal Simulation
Goal: Build terminal that encrypts card data.
Terminal secure memory simulation:
typedef struct {
unsigned char ipek[16];
uint32_t counter;
unsigned char ksn_base[10];
bool initialized;
} SecureMemory;
// Simulated secure memory - in real terminal this is in hardware
static SecureMemory secure_mem;
// Initialize with key ceremony
void terminal_init(const unsigned char* ipek, const unsigned char* ksn_base) {
memcpy(secure_mem.ipek, ipek, 16);
memcpy(secure_mem.ksn_base, ksn_base, 10);
secure_mem.counter = 0;
secure_mem.initialized = true;
}
Phase 3: HSM Simulation
Goal: Build HSM that decrypts but enforces trust boundary.
Key insight: The HSM function should NEVER return plaintext.
// BAD - exposes plaintext
char* hsm_decrypt(const EncryptedTrack* track) {
// This would violate the trust boundary!
return plaintext; // NEVER do this
}
// GOOD - processes but doesn't expose
AuthResult hsm_process(const EncryptedTrack* track) {
char plaintext[64];
decrypt_internally(track, plaintext);
// Do something with plaintext (auth check)
AuthResult result = authorize(plaintext);
// Zero before return
explicit_bzero(plaintext, 64);
return result; // Only return auth result
}
Phase 4: Key Injection Ceremony
Goal: Simulate initial device provisioning.
bool key_ceremony(HSM* hsm, Terminal* term, const char* device_id) {
// In reality, this happens in a secure room with split knowledge
// 1. Generate IPEK from BDK + device ID
unsigned char ipek[16];
unsigned char ksn_base[10];
// Build KSN base from BDK ID + Device ID
build_ksn_base(hsm->bdk_id, device_id, ksn_base);
// Derive IPEK (happens inside HSM)
hsm_derive_ipek(hsm, ksn_base, ipek);
// 2. "Inject" IPEK into terminal
// In reality: secure cable, no network, audited room
terminal_inject_ipek(term, ipek, ksn_base);
// 3. Zero the IPEK outside HSM
explicit_bzero(ipek, 16);
return true;
}
Phase 5: Transaction Flow
Goal: Full end-to-end transaction demonstration.
void demo_transaction(Terminal* term, HSM* hsm,
const char* pan, int amount_cents) {
printf("=== P2PE Transaction Demo ===\n");
// Step 1: Terminal encrypts
printf("[TERMINAL] Encrypting PAN: %s\n", pan);
EncryptedTrack encrypted = terminal_encrypt(term, pan);
printf("[TERMINAL] KSN: ");
print_hex(encrypted.ksn, 10);
printf("[TERMINAL] Ciphertext: ");
print_hex(encrypted.ciphertext, encrypted.ciphertext_len);
// Step 2: "Send" to processor (just pass the struct)
printf("[MERCHANT] Forwarding encrypted data (cannot decrypt!)\n");
// Step 3: HSM processes
printf("[HSM] Processing transaction...\n");
AuthResult result = hsm_process_transaction(hsm, &encrypted,
"MERCH001", amount_cents);
// Step 4: Show result
if (result.success) {
printf("[HSM] APPROVED - Auth code: %s\n", result.auth_code);
} else {
printf("[HSM] DECLINED - %s\n", result.error);
}
}
Testing Strategy
DUKPT Test Vectors
Use ANSI X9.24 test vectors to verify your implementation:
// test_dukpt.c
void test_ipek_derivation() {
// ANSI X9.24 test vector
unsigned char bdk[] = {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,
0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10};
unsigned char ksn[] = {0xFF,0xFF,0x98,0x76,0x54,0x32,0x10,0xE0,0x00,0x00};
unsigned char ipek[16];
dukpt_derive_ipek(bdk, ksn, ipek);
unsigned char expected[] = {0x6A,0xC2,0x92,0xFA,0xA1,0x31,0x5B,0x4D,
0x85,0x8A,0xB3,0xA3,0xD7,0xD5,0x93,0x3A};
assert(memcmp(ipek, expected, 16) == 0);
}
Trust Boundary Tests
// Verify plaintext never escapes HSM
void test_plaintext_not_exposed() {
// Set up terminal and HSM
// Process a transaction
// Check that no function returned plaintext
// Check that logs don't contain plaintext
}
Counter Management Tests
void test_counter_increments() {
Terminal term;
terminal_init(&term, test_ipek, test_ksn);
assert(term.counter == 0);
terminal_encrypt(&term, "4111111111111111");
assert(term.counter == 1);
}
void test_key_exhaustion() {
Terminal term;
terminal_init(&term, test_ipek, test_ksn);
term.counter = 0x1FFFFF; // Max counter
EncryptedTrack result = terminal_encrypt(&term, "4111111111111111");
assert(result.ciphertext_len == 0); // Should fail
}
Common Pitfalls & Debugging
Pitfall 1: Wrong Bit Ordering in KSN
Symptom: Derived keys donโt match test vectors.
Cause: DUKPT uses big-endian for KSN, your system might be little-endian.
Debug: Print each step and compare with manual calculation.
Pitfall 2: Counter Bit Extraction
Symptom: Keys derive incorrectly for counters > 1.
Cause: Misunderstanding which bits to use for each derivation step.
Fix: Counter bits are used right-to-left, each set bit triggers a derivation.
Pitfall 3: Exposing Plaintext
Symptom: Your โHSMโ returns decrypted data.
Cause: Wrong API designโHSM should return auth result, not plaintext.
Fix: Refactor so all plaintext handling is inside one function.
Extensions & Challenges
Extension 1: AES-DUKPT
Implement the newer AES-based DUKPT (ANSI X9.24-3):
- 256-bit keys
- AES-128 block cipher
- Different derivation algorithm
Extension 2: PIN Blocks
Implement PIN encryption and translation:
- ISO Format 0/1/3 PIN blocks
- PIN translation between zones
Extension 3: EMV Cryptograms
Simulate EMV chip card cryptogram generation:
- Application Cryptogram (AC)
- Authorization Request Cryptogram (ARQC)
Extension 4: Multi-HSM
Implement HSM clustering:
- Key synchronization
- Failover handling
Interview Questions This Prepares You For
- โWhat is DUKPT and why is it used?โ
- Derived Unique Key Per Transactionโenables unique encryption keys without distributing new keys
- โHow does P2PE reduce PCI scope?โ
- Merchant never has access to plaintext or keys, so their systems are out of scope
- โWhatโs an HSM and why is it necessary?โ
- Hardware Security Moduleโprovides physical security for keys, ensures keys never exist in regular memory
- โWhat happens when a DUKPT counter exhausts?โ
- Device can no longer encrypt, must be re-keyed (new IPEK injection)
- โHow do you derive a transaction key from IPEK?โ
- Walk through the future key tree based on counter bits
Resources
Specifications
- ANSI X9.24-1: DUKPT (2017 version)
- ANSI X9.24-3: AES DUKPT
- PCI P2PE Standard
Books
| Topic | Book | Chapter |
|---|---|---|
| Key Derivation | Serious Cryptography (Aumasson) | Ch. 8: KDFs |
| HSM Concepts | Security in Computing (Pfleeger) | HSM section |
| Block Ciphers | Practical Cryptography (Ferguson) | Ch. 3-4 |
Articles
- โUnderstanding DUKPTโ - Thales blog
- PCI P2PE Implementation Guide
Self-Assessment Checklist
- DUKPT IPEK derivation matches test vectors
- Transaction key derivation works for all counter values
- Terminal encrypts without exposing keys
- HSM decrypts without exposing plaintext
- Counter management prevents key reuse
- Key exhaustion is handled gracefully
- Can explain the security model to others
- Understand why merchant canโt decrypt
Whatโs Next?
You now understand card-present security. For e-commerce (card-not-present), move to Project 4: 3D Secure Authentication Flow to learn how online transactions add cardholder authentication.