Two minutes. That is how long it took a team of researchers to break the age verification app that the Brussels government had just launched as a flagship piece of EU digital infrastructure. Not two weeks of patient fuzzing. Not two days of reverse engineering the Android bytecode. Two minutes, because the “cryptographic proof” the app was supposed to produce turned out to be a client-side signed JWT whose signature the server never actually verified against a fresh nonce. The researchers flipped a flag in the token payload, resent it, and the backend cheerfully confirmed that a thirteen-year-old was over eighteen. The app had been advertised as zero-knowledge. It was not zero-knowledge. It was not knowledge-resistant. It was not, by any reasonable interpretation of the word, cryptography.
I have been auditing government identity systems for the better part of a decade. I have sat in procurement meetings where civil servants confidently explained to me that “we are using AES, so it is secure.” I have read tender documents that specified “must use blockchain” as a functional requirement with no further elaboration. I have watched a member state sign a contract with a web development consultancy whose most advanced prior work was a restaurant reservation platform, and then hand them the mandate to build the country’s eIDAS 2.0 wallet. The Brussels incident is not an outlier. It is the modal outcome of how European governments procure cryptographic systems in 2026, and unless something changes at the procurement layer, we are going to watch this exact movie replay across the continent every time a new regulation demands age verification, identity attestation, or privacy-preserving authentication.
This piece is going to be uncomfortable because I am going to name the pattern. The pattern is that governments treat cryptography as a checkbox, hire implementation partners who think “zero-knowledge” is a marketing term, and then act surprised when the result gets broken by a postdoc with a Burp Suite license. Meanwhile, mature, deployable, peer-reviewed zero-knowledge proof systems exist. They have existed for years. They are not used because the people writing the RFPs do not know they exist, and the people bidding on the RFPs have no incentive to propose them.
What Actually Happened in Brussels
Let me reconstruct the failure mode based on what has been published and what I have seen in parallel systems I cannot name here. The Brussels age verification app was designed to let a user prove to a website — typically an adult content platform, a gambling site, or a social network subject to the new EU CSAM regulation’s age gating requirements — that they were above a certain age threshold, without revealing their date of birth, identity, or government ID number. That is a textbook use case for a zero-knowledge range proof. You have a committed value (date of birth) signed by a trusted issuer (the state civil registry), and you want to prove that today minus that value is greater than eighteen years, without revealing the value itself.
What was actually shipped was something entirely different. The app, on first launch, authenticated the user against the national eID gateway. That part worked. The gateway returned a signed assertion containing the user’s full identity, including date of birth. The app stored this assertion locally. When a relying party (a website) requested age verification, the app generated a JSON Web Token containing a claim like {"over_18": true, "iat": 1745234567}, signed it with a key the app itself held, and forwarded it to the relying party. The relying party sent the token to the Brussels backend, which was supposed to verify the signature.
There were at least four things wrong with this design, any one of which would have been disqualifying in a competent security review:
- The
over_18claim was computed client-side. The app decided whether the user was over eighteen and simply asserted it. A modified app, or a direct API call bypassing the app entirely, could set the claim to anything. - The signing key lived inside the app on the user’s device, derived from a device-bound secret that was itself generated and stored in the Android keystore with default (non-StrongBox) attestation. A rooted device could extract it. A forked app could generate its own.
- The backend verification endpoint accepted tokens with a
kidheader and looked up the corresponding public key from a registry — a registry that the researchers were able to populate by re-registering the app with a fresh keypair, because the registration endpoint had no rate limiting and no binding between the registered key and the underlying eID identity. - Most egregiously, the nonce used to prevent replay was generated client-side and echoed back. A captured token could be replayed, and a forged token needed no live challenge from the relying party.
The researchers bypassed the entire chain by simply calling the registration endpoint with an arbitrary keypair, associating it with a synthetic eID that had not been validated, and then issuing a valid-looking token for any age they wanted. Two minutes, as advertised. The cryptographic primitives used — RSA, SHA-256, JWT — were all individually fine. The composition was the disaster. The system had no notion of a trusted computation on the user’s attributes. It just had the app asking itself whether the user was old enough and then telling the server the answer.
Why This Is Not a Bug, It Is an Architecture
I want to be precise here because the Brussels press office has already started using language like “a minor implementation flaw that will be patched in the next release.” That framing is dishonest. A minor implementation flaw is a missing null check. What happened here is that the system was designed around a trust model that does not exist. The designers assumed that the app running on the user’s phone was trusted, in the same way a card-reader at a bank is trusted. But the user is precisely the adversary in age verification. The whole point of the system is to prevent the user from lying about their age. If your trust boundary puts the attacker inside the trusted zone, no amount of patching is going to save you. You have to throw the architecture out.
The Wider EU Context: CSAM, DSA, and the Age Verification Mandate
The Brussels app did not appear in a vacuum. It is one visible artifact of a broader regulatory push that has been building across Europe since roughly 2022. The draft CSAM regulation — the one that went through multiple rounds of revision over client-side scanning — contains age verification obligations for platforms that host user-generated content likely to be accessed by minors. The Digital Services Act’s codes of conduct have been extended with age-assurance provisions. France’s Arcom has been enforcing age verification on adult sites since 2024 under the SREN law, and has levied fines against platforms relying on self-declaration. Germany’s KJM has similar requirements going back further. The UK, though not EU, passed the Online Safety Act with its own highly prescriptive age-assurance requirements, and Ofcom has published guidance that explicitly calls out “document-and-selfie” and “email-age-estimation” as acceptable methods — a bar so low that any competent privacy engineer should find it alarming.
On the other side of the Atlantic, the American approach has devolved into state-level legislation that is arguably worse. Utah, Texas, Louisiana, Arkansas, Mississippi, and a growing list of others have passed laws requiring adult content platforms to verify age using “government-issued identification or commercially reasonable verification methods.” The phrase “commercially reasonable” has been interpreted by compliance vendors as permitting document upload to a third-party verification service, which means that a user who wants to access a legal adult platform is now required to upload their driver’s license to an intermediary whose security posture is, charitably, unknown. Pornhub pulled out of Texas entirely rather than comply. The Supreme Court heard arguments in Free Speech Coalition v. Paxton and the resulting ruling did not foreclose this regulatory approach.
The pattern across jurisdictions is identical: legislators decide that something — access to pornography, social media accounts for minors, online gambling — requires age gating. They write requirements that presuppose a verification technology exists. They do not specify, or do not understand, the privacy properties that technology must have. They hand implementation to whichever vendors win the tender. The vendors build the cheapest thing that passes the literal text of the regulation. And then we get Brussels.
France: A Slightly Better Attempt
France deserves partial credit for taking the privacy question seriously, at least on paper. The CNIL and Arcom jointly published a referential in 2022 that explicitly required a “double-blind” architecture: the age verifier should not know the identity of the user, and the relying party should not know the identity of the verifier’s customer. This is the correct privacy model. In practice, the implementations that have been certified under this framework are mostly identity document scanning services that promise not to store data, which is a promise rather than a cryptographic guarantee. France’s more ambitious proposal — a proof-of-age credential issued through the France Identité wallet, built on ISO/IEC 18013-5 mobile driving licence standards — is technically sound, but adoption is limited and most sites are accepting simpler document-upload solutions instead.
What “Zero-Knowledge” Actually Means, and Why Governments Keep Getting It Wrong
Let me take a detour into the actual cryptography, because the abuse of the term “zero-knowledge” in government communications is now at a point where I think it needs to be addressed directly. A zero-knowledge proof is a protocol by which a prover convinces a verifier that a statement is true, without revealing any information beyond the truth of the statement itself. The classical example is the graph three-colouring problem: I prove to you that a graph can be three-coloured by committing to a colouring, letting you pick an edge, revealing the two colours on that edge, and repeating enough times that the probability of me cheating becomes negligible. You learn nothing about the colouring. You only learn that one exists.
Modern zero-knowledge systems — zk-SNARKs, zk-STARKs, Bulletproofs, Groth16, PLONK, Halo2 — let us prove arbitrary statements about secret values in a non-interactive way, with proof sizes measured in hundreds of bytes and verification times in milliseconds. For age verification the statement is trivially simple by the standards of the field: given a commitment C to a date d, and a current date t, prove that t - d >= 18 years. This can be done with a range proof in Bulletproofs in under five hundred bytes of proof and maybe ten milliseconds of verification on a laptop. The issuer — the state — signs the commitment once, at enrolment. Every subsequent verification produces a fresh proof that reveals nothing but the single bit that answers the question.
When a government communication says an app is “zero-knowledge” and the app is in fact sending a signed assertion that contains a boolean, this is not an approximation or a simplification for laypeople. It is a category error. The app is maximum knowledge in the relevant sense: it has the data and is simply declining to send parts of it. Zero-knowledge is a mathematical property of a protocol, not a UX description of what fields appear on a screen.
Available Primitives Governments Could Be Using
Here is a non-exhaustive list of cryptographic building blocks that are production-ready, open-source, and peer-reviewed, that a competently designed age verification system would use some combination of:
- BBS+ signatures. A signature scheme that allows selective disclosure and zero-knowledge proofs of knowledge of a signature. An issuer signs a set of attributes; the holder can prove “I have a signature on a message that contains a date of birth earlier than 2008-04-21” without revealing the date, the full signature, or any other attribute. Standardised in the IRTF Crypto Forum Research Group. Implemented in MATTR’s libraries, in Hyperledger AnonCreds, in the DIF’s implementations.
- Bulletproofs range proofs. Compact, no-trusted-setup zero-knowledge proofs of range membership. Originally from Blockstream’s confidential transactions work, widely implemented.
- Privacy Pass. An IETF-standardised anonymous credential system originally built for Cloudflare’s captcha-bypass token. A user redeems a signed attestation from an issuer, then presents unlinkable tokens to relying parties. Perfect fit for “this user has been age-verified by an issuer” without linking issuance to use.
- ISO/IEC 18013-5 mobile driving licence (MDL). The international standard for mobile IDs, supported by Apple Wallet, Google Wallet, and most US state DMVs that have rolled out mobile licences. Includes selective disclosure via salted attribute hashes, and is moving toward full ZK proofs in the 18013-7 online presentation profile.
- OpenID for Verifiable Credentials (OID4VC) with SD-JWT-VC or mdoc formats. The protocol layer that would let an eIDAS 2.0 wallet present an age proof to a relying party over the web.
- BrightID, Self Protocol, Worldcoin’s Semaphore, zk-eID. Self-sovereign identity and proof-of-personhood systems that demonstrate the primitives work at scale.
None of this is exotic. None of it requires novel cryptographic research. All of it has reference implementations, many in memory-safe languages, several with formal verification. A competent team could integrate a BBS+ credential into the Brussels app in a matter of weeks. They did not, because they were not that team.
A Comparison of Approaches
I want to put the various approaches side by side, because I think seeing them in a table makes the governance failure more vivid than any amount of prose. Each approach is rated against the two properties that matter: does it actually verify age (security guarantee), and does it preserve the user’s privacy against the verifier, the relying party, and a network observer (privacy guarantee). I am rating on a four-point scale from “broken” to “strong” for each axis.
| Approach | How It Works | Security | Privacy | Real-World Examples |
|---|---|---|---|---|
| Self-declaration checkbox | User clicks “I am over 18” | Broken | Strong | Most websites pre-2023 |
| Credit card check | Charge and refund small amount | Weak | Weak | Historical US adult sites |
| Email age estimation | Third party guesses age from email reputation | Weak | Weak | Ofcom-approved UK methods |
| Document upload to relying party | User uploads ID directly to site | Moderate | Broken | Many Utah/Texas-compliant sites |
| Document upload to intermediary | Third party verifies and passes token | Moderate | Weak | Yoti, Jumio, AU10TIX integrations |
| Face age estimation | ML model estimates age from selfie | Weak | Weak | Yoti age estimation, Privately |
| Brussels-style signed assertion app | App decides, signs JWT, sends to backend | Broken | Moderate | Brussels 2026, several EU pilots |
| eID with selective disclosure | Government wallet reveals age bracket only | Strong | Moderate | France Identité, German ID card eID function |
| MDL with ISO 18013-7 proof | Mobile licence proves predicate to relying party | Strong | Strong | Apple Wallet ID in US states, pilot deployments |
| BBS+ credential with ZK age proof | Issuer signs birth date; holder proves range | Strong | Strong | MATTR, Hyperledger AnonCreds pilots |
| Privacy Pass anonymous token | Issuer issues age-verified tokens; holder redeems unlinkably | Strong | Strong | Apple Private Access Tokens, Cloudflare |
The top half of this table is what most jurisdictions are currently deploying. The bottom half is what they should be deploying. The gap between the two is not a gap of capability. It is a gap of procurement competence.
How Procurement Goes Wrong
Let me describe a typical procurement process, based on what I have observed in three different member states I will not name. A ministry is handed a regulatory deadline: within eighteen months, a national age verification capability must be operational. The ministry has no cryptographers on staff. It has policy officers, lawyers, and possibly one or two IT coordinators whose background is in procurement rather than engineering. The ministry drafts a tender document. The tender document specifies functional requirements (the app must work on iOS and Android, the user must be able to verify age in under thirty seconds, the system must handle ten million users) and regulatory requirements (must be GDPR compliant, must allow audit by national DPA, must integrate with the existing eID gateway). It does not specify cryptographic properties, because nobody at the ministry knows which ones to specify.
The tender goes out. It is answered by a handful of consultancies and system integrators. The winning bid is evaluated on price, on demonstrated capacity to deliver at the required scale, and on the quality of the proposal narrative. The crypto architecture, if it is mentioned at all, is mentioned at a level of abstraction — “we will use industry-standard cryptography, including TLS 1.3 and AES-256” — that tells you nothing about whether the system will be secure. The winning bidder is usually a large consultancy with a strong delivery track record in government IT, which typically means they have delivered tax portals and benefits administration systems. These are skills, but they are not the skills that produce a secure age verification system.
The consultancy assigns the project to a team. The team lead is a project manager. There is a solution architect, usually with a background in enterprise Java. There are developers. There may be a security person, whose role is primarily to run SAST tools over the codebase and fill in an ISO 27001 checklist. There is, in my experience, almost never a cryptographer. If there is a cryptographer, they are a consultant brought in for a two-week design review at the end of the project, by which time all the fundamental architectural decisions have been made and cannot be changed without blowing the deadline.
The developers build what they know how to build. They know how to build web services with JWTs. They know how to sign things with RSA. They know how to call HSMs. They do not know how to build a BBS+ credential issuer. They have never heard of Privacy Pass. When a requirement says “the system must be privacy-preserving,” they interpret this as “we should minimise the data we log.” When a requirement says “zero-knowledge,” they think this is marketing language and ignore it.
The app ships. A researcher breaks it in two minutes. The ministry issues a statement promising to patch the flaw. The consultancy invoices for the patch. The underlying architecture remains unchanged because the architecture is the flaw, and fixing it would require throwing away the current build. And this cycle will repeat with the next regulation, in the next country, with a different consultancy and the same outcome, until something in the procurement process changes.
The Procurement Fixes That Would Actually Help
I have written variants of this list in three different reports for three different governments, so I am going to write it here publicly in case a fourth or fifth government finds it useful:
- Require reference to specific cryptographic primitives in the tender document. Not “zero-knowledge” as a buzzword, but “selective disclosure using BBS+ signatures as specified in the IRTF CFRG draft, or equivalent with documented justification.” Make the bidder demonstrate they know what they are bidding on.
- Require a named cryptographic architect on the bid team, with credentials. Published work, conference talks, standards body participation. Not a certification. Certifications do not tell you whether someone can design a protocol; published work does.
- Mandate an independent cryptographic review by a third-party auditor who is not employed by the winning bidder, before deployment, with the review published. Not after deployment. Not as a closed report. Before, and published.
- Separate the credential issuance from the credential verification. A single vendor should not build both. This forces a clean interface between the two, which in turn forces the interface to be a published specification, which in turn makes the system interoperable.
- Require a documented threat model, with the user listed as an adversary. This single requirement would have prevented the Brussels failure. The Brussels designers wrote a threat model that treated the user as trusted.
- Fund a public red team. Pay competent researchers to break the system before it ships. Pay them well. Treat them as allies. Publish their findings.
A Worked Example: What Brussels Could Have Built
Let me sketch what a competently designed version of the Brussels app would look like, concretely enough that a reader with a cryptography background could evaluate it. The system has three parties: the issuer (the national civil registry), the holder (the citizen with a phone), and the verifier (a website that needs to gate content by age).
At enrolment, the citizen authenticates to the civil registry through the existing eID infrastructure. The registry issues a BBS+ credential signed with its public key. The credential contains a set of attributes, one of which is the citizen’s date of birth. The credential is stored in the citizen’s wallet app on their phone. The wallet app holds the BBS+ signature and the attribute set. Crucially, the wallet does not need the civil registry’s help to produce proofs later — the credential is self-contained.
When the citizen visits a website that needs age verification, the website sends a presentation request specifying what it needs proven: for example, “a BBS+ proof of a credential signed by the Belgian civil registry’s public key (fingerprint X), proving that the attribute ‘date_of_birth’ is earlier than 2008-04-21, bound to a fresh challenge C.” The wallet app constructs a BBS+ zero-knowledge proof that reveals only the predicate: the holder has a valid signature from the specified issuer on some credential, and the date_of_birth attribute of that credential satisfies the range. The proof is bound to the fresh challenge so it cannot be replayed.
The website verifies the proof locally using the civil registry’s public key, which is a well-known value. The verification runs entirely on the website’s server and requires no callback to the registry. The registry does not learn which websites its citizens are visiting. The website does not learn the citizen’s identity, date of birth, or anything else beyond the predicate. The proof is unlinkable: two proofs presented to two different websites cannot be correlated by a third party observing both.
A sketch of what the proof generation code looks like, using a notional BBS+ library:
// Holder side: generate proof
const credential = wallet.getCredential("eidas.gov.be.civilregistry");
const disclosureSpec = {
// Reveal nothing directly
revealed: [],
// Prove a predicate about date_of_birth
predicates: [{
attribute: "date_of_birth",
operator: "lessThan",
threshold: dateEighteenYearsAgo(),
}],
// Bind to the verifier's nonce
challenge: verifierChallenge,
};
const proof = bbs.proveWithPredicates(
credential.signature,
credential.attributes,
issuerPublicKey,
disclosureSpec,
);
sendToVerifier(proof);
// Verifier side: check proof
const ok = bbs.verifyProof(
proof,
issuerPublicKey,
disclosureSpec,
);
if (!ok) reject();
This is not speculative. Libraries that implement exactly this interface exist today: MATTR’s pairing-crypto, Hyperledger’s ursa, Decentralized Identity Foundation reference implementations. The cryptography has been standardised through the IRTF. The protocol layer (OID4VP, SD-JWT-VC, mdoc) has been standardised through the OpenID Foundation and ISO. The entire stack is production ready. What is missing is not technology. What is missing is the will to require it in procurement.
The Common Objections, Addressed
I have had versions of the following conversation many times with policy officers, so let me address the standard objections.
“ZK proofs are too slow for mobile devices.” This was true in 2015. It has not been true since around 2020. A BBS+ proof takes tens of milliseconds on a modern phone. A Groth16 proof takes longer but is usually unnecessary for this use case. Users will not notice.
“If we use ZK proofs, we cannot audit the system.” You can. You can audit the issuer’s signing logs. You can audit the verifier’s proof acceptance logs. You can audit the underlying cryptographic library. What you cannot do is link a specific verification event back to a specific citizen, which is exactly the privacy property you wanted.
“What if a credential is revoked?” Revocation in privacy-preserving credentials is a solved problem, with several approaches: accumulator-based revocation (RSA accumulators or Merkle-tree accumulators), status list 2021-style published revocation lists that the holder proves non-membership of in the ZK proof, or time-limited credentials with short lifetimes and frequent re-issuance. Each has tradeoffs, but all are implementable.
“What about parents wanting to verify on behalf of children?” Delegation in verifiable credentials is also a solved problem — delegation credentials can be issued that allow one holder to present predicates on another’s behalf, with explicit guardianship semantics.
“If we use anonymous credentials, how do we catch fraudsters?” You catch them at issuance. The fraud detection problem is “is this a real person with a real birth certificate,” and that happens when the civil registry signs the credential. Once the credential is issued to a real identity, there is no further fraud surface at verification time. An issued credential cannot lie about its attributes because those attributes are committed to and signed.
The International Picture
It is worth briefly noting how this looks in other jurisdictions, because I think the EU is not uniquely bad here, just uniquely visible this week.
United Kingdom
The Online Safety Act’s age-assurance regime, enforced by Ofcom, has explicitly refused to mandate any specific cryptographic properties. Ofcom’s guidance lists “document verification,” “facial age estimation,” “credit card checks,” “email-based age estimation,” “bank account verification,” and “photo ID matching” as acceptable. The guidance does not require that the verification be privacy-preserving against the verifier. The result is a thriving market of third-party “age-assurance providers” to whom users must entrust copies of their identity documents. Yoti is the largest of these, and to its credit Yoti has invested in reasonable security practices and has pushed for the mobile driving licence standard. But the structural position is one in which every adult UK citizen is expected to hand a copy of their passport to a private company in order to read a website, which is the opposite of privacy-by-design.
United States
The patchwork of state laws has created a compliance race to the bottom. A Texas-compliant verification is typically a document upload to a third-party verifier, who then returns a simple token to the relying party. Mobile driving licences are rolling out slowly — Apple Wallet supports them in a growing number of states — but the protocol used for web verification is still maturing, and most adult sites are not equipped to accept MDL presentations. The result is that the default path for a Texan accessing a legal adult site is still document upload.
India
India’s Aadhaar system has a feature called “offline eKYC” and an age-verification API that relying parties can call. The privacy model is not what Europeans would accept — UIDAI sees all verifications, and the relying party sees the user’s Aadhaar-linked identity. But the infrastructure is in place and operates at scale. A retrofit to add ZK-based selective disclosure on top of Aadhaar is technically feasible and has been proposed in academic work. Whether political will exists is another question.
Singapore
Singapore’s Singpass platform and its Myinfo attribute service support selective disclosure. The user can release individual attributes to relying parties without releasing the full identity bundle. It is not zero-knowledge — the government still sees all verifications — but it is a meaningful step ahead of the EU’s current implementations. Singapore has historically been faster at shipping well-engineered government digital infrastructure, in part because its procurement process is more centralised and more technically informed.
What a Well-Designed System Looks Like
Let me close by describing, in properties rather than product names, what a correctly designed national age verification system would look like in 2026. I will offer this as a checklist against which any deployed system — Brussels’s, Ofcom’s, Utah’s — can be measured.
- The issuer does not see verifications. When a citizen presents an age proof to a website, the national registry is not in the loop. The citizen’s wallet holds a long-lived credential and generates proofs locally. The registry does not log which websites its citizens are authenticating to.
- The verifier does not learn identity. The website learns exactly the predicate it asked about — “over 18” — and nothing else. It does not learn the citizen’s name, date of birth, ID number, or a persistent pseudonym.
- Proofs are unlinkable. Two proofs presented to two different verifiers cannot be correlated, even by an observer who sees both. Two proofs presented to the same verifier at different times cannot be correlated. The wallet generates a fresh proof each time, and the proof contains no persistent identifier.
- The trust computation happens in a cryptographic protocol, not in the app. The wallet does not decide whether the user is over 18 and then tell the verifier. The wallet produces a cryptographic proof that the predicate holds, verifiable without any trust in the wallet’s honest execution. A malicious wallet cannot forge a valid proof. It can refuse to produce one, but it cannot lie.
- Cryptographic keys are hardware-protected. The issuer’s signing key lives in an HSM. The holder’s binding key lives in the phone’s secure element (StrongBox on Android, Secure Enclave on iOS), not in the keystore with software-only attestation.
- The protocol is standardised and interoperable. A wallet issued by one member state can present proofs to a verifier operated in another. A verifier built on open standards can accept proofs from any compliant wallet. No single vendor owns the stack.
- Revocation is timely and privacy-preserving. A revoked credential is rejected by verifiers, without the revocation mechanism itself leaking information about which credentials were revoked or which holders are checking.
- The system has been publicly red-teamed before deployment. Findings have been published. Fixes have been verified. A standing bounty is in place.
- There is a working audit mechanism that does not undermine privacy. For example, a DPA can audit the issuer’s enrolment logs and the verifier’s proof acceptance counters, but cannot reconstruct a link between the two.
- The user can use the system without a smartphone. A physical ID card with NFC, or a desktop browser extension with a hardware security key, should be viable fallbacks. Age-verification infrastructure that excludes the fifteen percent of the population without a suitable smartphone is bad infrastructure.
This is not utopian. Every bullet on that list is supported by technology that exists today, in open-source implementations, with peer-reviewed security. The gap between this and the Brussels app is not a research gap. It is a governance gap.
The problem with the Brussels app is not that its developers did not know cryptography. The problem is that nobody in the procurement chain was in a position to tell that the developers did not know cryptography. That is a problem about how governments buy software. It is not a problem about what is possible with software.
Where This Goes Next
The eIDAS 2.0 regulation mandates that every EU member state roll out a European Digital Identity Wallet by 2027. These wallets will be used for age verification, for qualified electronic signatures, for travel, for employment records, for educational credentials. They will hold the most sensitive attributes a citizen possesses. The Architecture and Reference Framework (ARF) that specifies the wallet technical requirements does call out selective disclosure, and does reference SD-JWT-VC and mdoc as acceptable formats. But the ARF leaves substantial discretion to member states in how they implement issuance, and the Brussels app is an early preview of what that discretion produces when exercised badly.
Over the next eighteen months, every member state is going to make procurement decisions that determine the privacy properties of its wallet. Some will get this right. France is likely to get it right; Germany, which has been building BSI-reviewed eID components for two decades, will probably get it right. Others will hand the tender to the cheapest web development consultancy, and we will be reading the incident writeups for the next five years. The researchers who broke the Brussels app in two minutes have done the public a service. They have made it possible to talk about this pattern concretely, with a named artifact, rather than in the abstract.
If you work in government procurement, the question I want you to carry away is not “how do we prevent this specific bug in the next app.” It is “how do we restructure our procurement process so that the next app is architecturally incapable of having this class of bug.” That is a harder question, because it requires admitting that the current procurement process is not fit for purpose when the output is cryptographic infrastructure. But it is the only question worth answering. The next Brussels is already being built, somewhere, by a team that has never heard of BBS+ and will not hear of it unless someone in the tender process makes hearing of it a requirement.
The cryptography is ready. The standards are ready. The libraries are ready. What is missing is the institutional capacity to ask for them. That is the real story of the Brussels app, and of every government crypto failure that will follow it until procurement catches up with the state of the art.




Discussion