Skip to main content

Chip Authentication

Chip Authentication (CA, BSI TR-03110) cryptographically binds the MAI-signed identity to the card's physical chip. It is required for any server-side verification flow.

Why it is necessary

Passive Authentication proves the data was signed by MAI. Active Authentication (AA) proves the chip can sign a challenge. But the two verifications are independent.

The attack vector without CA: an attacker who knows the victim's CAN can read the ICAO data (SOD + DG1) without a PIN — the CAN is visible on the front of the card, and these files are accessible without authentication. The attacker can then use their own chip and their own PIN for the AA signature. A backend verifying only passive + active sees both chains as valid and cannot distinguish the attacker from the victim.

CA closes the attack: the server performs an ECDH key agreement with Q_chip — the chip's public key from DG14, signed by MAI and covered by the SOD. Only the chip holding the corresponding private key d_chip can produce the correct shared secret. The identity and the physical chip cannot be separated.

Enabling CA

CA shares the PACE session with personal data reading — withPersonalData(pin:) must also be called.

val result = EidKit.reader(can = userEnteredCan)
.withPersonalData(pin = userEnteredPin)
.withActiveAuth()
.withChipAuth()
.read(isoDep)

val proof = result.claim?.chipAuthProof
// Send proof to your backend together with the rest of the claim

Proof material

chipAuthProof contains everything your backend needs for independent verification:

FieldDescription
terminalPublicKeyEphemeral terminal public key — 65 bytes, uncompressed point
ephemeralPrivateKeyEphemeral private key scalar — 32 bytes; single-use, safe to send to your own server
sharedSecretXX-coordinate of the ECDH shared secret — 32 bytes
rawDg14Raw DG14 bytes — contains Q_chip, covered by the MAI-signed SOD hash

Server-side verification

The server recomputes K_ca = ECDH(d_terminal, Q_chip_from_DG14) and compares it to the sharedSecretX received from the app. If they match, the chip that signed the AA challenge holds exactly the private key corresponding to the MAI-signed identity.

// Node.js example (EidKit SSO webhook)
const ecdh = crypto.createECDH('brainpoolP256r1');
ecdh.setPrivateKey(Buffer.from(caEphemeralPrivateKey, 'base64'));
const recomputed = ecdh.computeSecret(qChipFromDg14);
if (!recomputed.slice(0, 32).equals(Buffer.from(caSharedSecretX, 'base64'))) {
throw new Error('CA binding mismatch — split-proof attack detected');
}
Full technical detail

All 8 verification conditions and the SSO flow diagram: Security Overview.