eIDAS LabEUDI Wallet · QES
Essay

What an AI agent found in a wallet verifier (and what it couldn't touch)

The flagship experiment in this lab was simple to state and cheap to run: point a red-team agent at the verifier and see what breaks. Four attempts, no LLM in the loop for this pass (the heuristic agent costs $0 and makes the point cleanly enough that a model wasn't needed to prove it — a real LLM red-teamer is the natural next iteration, not a requirement for the finding). Two attempts aimed at the deterministic core — checks two through six, the ones covering issuer signature, trust path, revocation, disclosure integrity, key binding. Two aimed at the policy layer — checks seven and eight, registration purpose and claim consistency.

Crypto family: 0% success. The agent tried altering a disclosed claim's value without the issuer's private key — caught, digest mismatch, exactly where the taxonomy says it should be caught. It tried replaying a key-binding JWT signed over a stale nonce instead of the verifier's fresh one, hoping nonce freshness wasn't actually enforced — caught. Neither attempt is subtle or clever. That's the point. There's no cleverness available. A digest either matches or it doesn't; a nonce either matches the one the verifier issued or it doesn't. An LLM has nothing to negotiate with here — it can't argue a hash into matching, and "try harder" isn't a strategy against SHA-256.

Policy family: 50% success. The first attempt — volunteer an unrequested claim, hoping only the DCQL query gets checked and not the actual disclosure — failed, because registration_purpose checks the union of requested and revealed claims, not just requested. That one's a genuine near-miss for a lazier implementation. The second attempt is where the hole actually is: issue age_over_18=True for a credential belonging to an actual minor, disclose only age_over_18, never birth_date. The consistency check that's supposed to catch a mismatch between the boolean claim and the underlying age has nothing to compare against, because the underlying age was never disclosed. Accepted.

This isn't a bug in the sense of "someone forgot a line of code." It's structural. Selective disclosure is the credential's headline feature — the entire reason SD-JWT VC exists instead of a flat, always-fully-disclosed credential is so a holder can reveal age_over_18 without also handing over their exact birth date. The same mechanism that makes selective disclosure privacy-preserving is what removes the verifier's ability to cross-check a derived claim against the claim it was derived from. You cannot patch this by writing a stricter policy check, because the information the stricter check would need was never in the presentation to begin with — the issuer or a colluding holder controls what got baked into age_over_18 at issuance time, and the verifier only ever sees the output of that computation, not its inputs. The fix, if there is one, sits upstream of the verifier entirely: the issuer's own attestation-time verification (did the person born on this date actually satisfy this predicate) has to be trustworthy on its own, because the wallet-side presentation cannot independently confirm it after the fact.

The asymmetry is the real finding, more than either number in isolation. eIDAS's deterministic trust chain — issuer signatures, revocation, key binding, the whole cryptographic apparatus checks two through six exercise — is provably AI-unbluffable. Not "hard to bluff." Structurally, mathematically closed to it. That's a genuine, load-bearing property of the wallet architecture, not marketing. But it means the entire residual fraud surface an adversary — human or AI — actually has room to work in is the policy layer: what a relying party chooses to ask for, what it accepts as sufficient, whether it cross-checks a derived claim against its source. eIDAS doesn't make wallet-based fraud impossible. It relocates it, completely, out of the cryptography and into decisions the relying party's own policy team wrote. For a bank standing up a verifier ahead of the December 2027 deadline, that's the actual scope of the risk review: not "is the crypto library correct" — assume it is, or use one that's been audited — but "did we write a policy layer that can be reasoned about the same way we reason about the crypto." Most banks are not currently set up to ask that question, because until now nobody in the product organization owned a verifier at all.