How to Compute EMV Cryptograms — ARQC, ARPC, AAC, TC Step-by-Step

EMV transaction security hinges on the Application Cryptogram. The card generates an ARQC (Authorization Request Cryptogram) using a session key derived from the card master key + ATC. The issuer validates it and returns an ARPC. This guide walks through the full flow: CDOL1 data assembly, session key derivation, 3DES MAC computation, and CID/CVR decoding.

1. EMV Cryptogram Types

CryptogramCID ValueMeaning
AAC0x00Application Authentication Cryptogram — transaction declined by card
TC0x40Transaction Certificate — transaction approved offline by card
ARQC0x80Authorization Request Cryptogram — card requests online authorization
ARPCN/AAuthorization Response Cryptogram — issuer's response (ARQC validation proof)

2. CDOL1 — Card Data for ARQC

The CDOL1 (Card Data Object List 1) tells the terminal which data elements to include in the GENERATE AC command. Typical CDOL1 from EMV tag 0x8C:

# CDOL1 example (from Visa card):
# 9F02 06   → Amount, Authorized (6 bytes, numeric)
# 9F03 06   → Amount, Other (6 bytes)
# 9F1A 02   → Terminal Country Code (2 bytes)
# 95    05   → Terminal Verification Results (5 bytes)
# 5F2A 02   → Transaction Currency Code (2 bytes)
# 9A    03   → Transaction Date (3 bytes)
# 9C    01   → Transaction Type (1 byte)
# 9F37 04   → Unpredictable Number (4 bytes)

# CDOL1 data (29 bytes assembled for GENERATE AC):
# 000000001000 000000000000 0840 0000000000 0840 250701 00 A1B2C3D4
# └─Amount────┘ └─Other Amt┘ └TCC┘ └─TVR─────┘ └TCC┘ └Date┘ └TT┘ └─UN────┘

3. GENERATE AC Command — Requesting the ARQC

# GENERATE AC command (EMV INS = 0xAE):
# CLA=0x80, INS=0xAE, P1=0x80 (ARQC), P2=0x00
# Data = CDOL1 data + optional Le

cdol1_data = bytes.fromhex(
    "000000001000"  # Amount Authorized: 100.00
    "000000000000"  # Amount Other: 0
    "0840"          # Terminal Country Code: USA (840)
    "0000000000"    # TVR: all zeros
    "0840"          # Currency Code: USD (840)
    "250701"        # Date: July 1, 2025
    "00"            # Transaction Type: Goods & Services
    "A1B2C3D4"      # Unpredictable Number
)

GENERATE_AC_ARQC = [0x80, 0xAE, 0x80, 0x00, len(cdol1_data)] + list(cdol1_data) + [0x00]
# → 80 AE 80 00 1D [29 bytes CDOL1 data] 00

4. The ARQC Response — Parsing

# Response to GENERATE AC:
# [Cryptogram Information Data (1 byte)] [ATC (2 bytes)] [ARQC (8 bytes)] [IAD] [SW1 SW2]

# Example: 80 00 01  C1 A2 B3 C4 D5 E6 F7 88  ...
#           │  └ATC┘  └────── ARQC (8 bytes) ──────┘
#           CID=0x80 (ARQC)

def parse_generate_ac_response(response):
    cid = response[0]
    atc = (response[1] << 8) | response[2]
    arqc = response[3:11]
    iad = response[11:]  # Issuer Application Data (variable length)
    return {
        'cryptogram_type': 'ARQC' if cid == 0x80 else 'TC' if cid == 0x40 else 'AAC',
        'atc': atc,
        'arqc': arqc.hex().upper(),
        'iad': iad.hex().upper() if iad else None
    }

5. Computing the ARQC (Issuer-Side Verification)

The issuer verifies the ARQC by computing the same cryptogram from the session key and CDOL1+CDOL2 data:

from Crypto.Cipher import DES3

def compute_arqc(session_key, cdol1_data, cdol2_data, atc):
    """Compute ARQC using 3DES Retail MAC."""
    # Step 1: Derive session key from MK + ATC
    # SK = 3DES_MK( ATC || 0xF8 0x00 0x00 0x00 0x00 0x00 )

    # Step 2: Build input data
    # input = CDOL1_data || CDOL2_data || (CID || ATC || ...)
    input_data = cdol1_data + cdol2_data

    # Step 3: 3DES CBC-MAC with initial chaining value = 0x00...00
    # Pad input to 8-byte boundary (EMV padding: 0x80 + 0x00s)
    pad_len = 8 - (len(input_data) % 8)
    input_data += b'\x80' + b'\x00' * (pad_len - 1)

    # CBC encrypt with session key, IV=0
    cipher = DES3.new(session_key, DES3.MODE_CBC, iv=b'\x00'*8)
    encrypted = cipher.encrypt(input_data)

    # ARQC = last 8 bytes of encrypted output
    arqc = encrypted[-8:]
    return arqc

6. ARPC — Issuer Response Cryptogram

The issuer validates the ARQC and returns an ARPC (Authorization Response Cryptogram):

# ARPC generation uses the same session key:
# ARPC_Method_1: MAC over (ARC || ARQC || ...)
# ARPC_Method_2: proprietary to payment scheme

def compute_arpc_method1(session_key, arqc, arc):
    """Compute ARPC Method 1."""
    # ARC (Authorization Response Code): 2 bytes (e.g., "00" = approved)
    input_data = arc.encode() + arqc
    # Pad to 8-byte boundary with 0x80 0x00...
    pad_len = 8 - (len(input_data) % 8)
    input_data += b'\x80' + b'\x00' * (pad_len - 1)

    cipher = DES3.new(session_key, DES3.MODE_CBC, iv=b'\x00'*8)
    encrypted = cipher.encrypt(input_data)
    return encrypted[-8:]  # ARPC = last 8 bytes

7. Cryptogram Information Data (CID) and CVR

CID byte: [00] [CVR bit] [RFU] [...] [Cryptogram Type]
  Bit 7-6: Cryptogram Type (00=AAC, 01=TC, 10=ARQC)
  Bit 5-0: Application-specific

CVR (Card Verification Results): 4 bytes in IAD
# CVR Byte 1:
# Bit 8: Offline data auth performed
# Bit 7: SDA failed
# Bit 6: DDA failed
# Bit 5: Card appears on terminal exception list
# Bit 4: CDA failed
Test this yourself: Our EMV Cryptogram Visualizer shows the full ARQC generation flow step-by-step: session key derivation, data concatenation, 3DES MAC. Input your CDOL data, MK, and ATC to see each intermediate value. The Session Key Visualizer handles the key derivation part.

Related Tools

EMV Cryptogram Visualizer — Step-by-step ARQC generation | Session Key Visualizer — 3DES/AES key derivation | EMV TLV Parser — Decode EMV card responses | EMV Tag Reference — Full data object list