Cover photo

A Curious Problem of Modern Auth

Or how the Philippines might accidentally build a world-class surveillance machine while trying to catch a few trolls

Ok frends. Here’s the pitch as reported: the Philippine government and Meta are going to sit down and figure out whether Facebook can ask PhilSys a yes or no question about you.

That's it, really. You type in a number, Meta sends it to PhilSys, PhilSys says "yep, that's a human" or "nope," then Meta does something with that answer. They’ve been quite careful to say Meta will never see your name, your address, your face, or anything else sitting in the PhilSys database. Just the yes, or just the no.

And I want to be fair to this framing before I spend the rest of this post ranting about it: it is true. Meta really won't get your demographic data through this specific mechanism. But that's also irrelevant because it leaks through other means, and I'd like to explain why using our national language of trustlessness: pano kaya kung…? 🐴

Before I go about, I just want to let you know that I’ve done the job of speculating four different approaches on how they may do this because I don’t want this to be a rage-fueled, internet noise with no substance and specificity, so:

  1. The naive approach: Existence Oracles

Facebook makes a PCN query to PhilSys, PhilSys checks if there’s a match and gives Facebook an answer. Since you need to give Meta your real PCN, it sure is sybil-resistant in the crudest sense. But also very privacy-leaky (you got a persistent correlatable identifier on Meta's servers), and a national-security liability (government is handing unthrottled existence-check capability to a foreign platform with no visible audit trail). 

post image

The security problem has a cheap patch (that will cause a bigger problem but more on that later): just do per-caller rate limits, mandatory consent-binding (query must be tied to a logged user session, not a bare API call from Facebook), and independent audit logging of every query. 

Does it prevent unearned queries from Meta? Sure. But audit logs are only as trustworthy as who has access to them. If PSA logs every query, that log itself becomes a sensitive asset.. Because now you have a real-time record of which Filipino citizens are creating/verifying Meta accounts, when, and from which IP. Even if Meta behaves, this is now a huge domestic liability. I am not one to trust this won’t be hacked into, based on our previous patterns...

And it’s still a privacy nightmare for the Filipino of course, but I won’t dwell on that anymore as I think neither Meta nor PhilSys will want to go with this approach if they’re bare-minimum smart.

  1. PhilSys-controlled federated login with a pairwise pseudonymous id (OIDC-style)

This is the likeliest design they’ll go for because hey, slapping standards on it feel authoritative, secure, and fair. But I’m telling you right now, it’s none of that. And let me explain the design first before telling you why.

post image

In an OIDC based auth, Meta redirects the user to a PhilSys-hosted verification flow, and PhilSys returns a signed assertion to Meta containing a pseudonymous identifier (or a sub) that is hopefully not derivable or linkable to the PCN.

Conceptually, it will look something like this:

{
  "issuer": "Philippine Statistics Authority",
  "audience": "facebook.com",
  "subject": "psa-meta-random-pseudonym-8d32f",
  "verified_human": true,
  "age_over_16": true,
  "expires": "2026-09-04T10:05:00Z"
}

Then Meta receives a random, one-time code to redirect the user back to their page.

Now for pairwise identifiers. This is the thing that will refer to your user on the platforms’ databases. 

Please never ever create subs like this:

sub = SHA256(PCN)

This value would remain the same everywhere. Different platforms can compare and identify the same person. So at the very least a good OIDC implementation will provide different subs for different platforms, even if they’re derived from the same PCN (but again, even a good implementation is a terrible idea, and I will tell you why, but first…)

sub_for_meta  = PRF(PSA_secret, PCN + "facebook-something-something")
sub_for_gcash = PRF(PSA_secret, PCN + "gcash-something-something")

This is clean for apps, done exactly how OIDC "pairwise" subject claims work, and it's the same exact model behind Sign in with Apple privacy. Honestly, this would have worked if Meta is the only potential threat actor in the system. But remember, we have PhilSys too. They could link the citizen’s ID to their Meta activities.

Because the user authenticates with PhilSys in real time, PSA can potentially record: 

Danki authenticated for Meta at 9:43 p.m.

And they can point out that yes, it's the Danki who lives on that street in Cebu, with a birthday of…

And narrowing accounts who might be her, she posted yet another flood-control meme…

Congrats, your OIDC is now a ✨government-mediated login✨

This is not even threat modelling mass surveillance collusion between Meta and PSA yet. Not saying it will happen, but nothing stops the two from sharing notes. Let’s visualize what they both have:

PSA: Legal identity, Meta’s pairwise identifier, Authentication time, IP and device metadata

Meta: Accounts and pseudonyms, Same pairwise identifier, contacts and social graph, activities, behavioral profile

Oof. These two can actually now piece it all together through the pairwise identifier. Again, not saying that they will, but I’m sorry I don’t believe in pinky swears.

  1. Recommendation #1: Device-held Credential

post image

How this works: Instead of PSA answering questions in real time, PSA issues you a cryptographically signed credential you keep on your own device, like "PSA vouches: this holder is a real, currently valid, age-verified PhilSys registrant," sitting in your phone and not on any server. 

So we’ll have no Danki verified at 9:43 PM sitting in a government database waiting for a subpoena, another hack, or an ambitious junior analyst with database access and a grudge.

But let me attack this, too.

If a device-held credential that gets handed to Meta at verification time is the same static value every single time you check in, then I’m just suggesting a PCN 2.0. 

It’s still slightly more secure because it doesn’t live in a government API that can be targeted and compromise millions, but still not something that I’d like to use.

Then a second problem: nothing about a signed certificate proves that the credential wasn’t stolen from the original device. Fixing this needs the presentation bound to something fresh, like a one-time challenge or a nonce your phone signs on the spot so a copied credential can't just get replayed forever.

And then there's revocation. If PSA needs to kill your credential because you lost your phone or whatever, then how does Meta find out, given that PSA isn't watching the transaction anymore?

So ok, our honest menu:

  • Short-lived credentials that expire in days and need re-issuing. But then PSA checks you again every time it reissues.

  • A public revoked-list Meta can check against. Works, but a big dumb list of revoked serials is its own unruly little dataset

  • A cryptographic accumulator — a compact bit of math that lets you prove "I'm not on the revoked list" without revealing which credential you're even asking about.

That third one is the actually elegant answer. And conveniently, it's most of the way to where I want to go next anyway.

  1. Recommendation #2: Prove the ID Without Showing It (ZK proof + nullifier combo)

post image

Y’all know I’m fond of ZK and blockchain. I’m fond of them, but 99% of the time I don’t recommend using them. Today I’ll make an exception because it is made especially for these moments. 

Instead of handing Meta the certificate itself, your phone generates a zero-knowledge proof that says, mathematically: "I possess a validly-signed, currently-unrevoked PSA credential, and yes, I clear the 16+ bar." Full stop. 

There shall be no signature travels. No providing PCN to Meta, no "which citizen is this"... nothing PSA would recognize even if it saw the proof land on its own desk.

Danki checks in on Facebook. Meta verifies the proof and learn: a valid, age-cleared Filipino did this. That's the whole receipt. Danki remains, cryptographically, nobody in particular.

Obviously we need some way to stop the user from doing this fifty times to make fifty accounts, otherwise we've built the world's most secure troll factory. This is what a nullifier is for.  

Meta can say "this nullifier already has an account, denied" without ever learning who's behind it, and without that nullifier being usable to link the user’s Instagram, WhatsApp, or the political ad they bought last week.

I made a tradeoff though, but it’s worth it. Wanting total unlinkability of online activities and "one person, exactly one account, forever" genuinely fight each other. So you get to pick where on that dial you land… narrower nullifiers (per feature, per election cycle) buy more privacy and looser enforcement. Meanwhile broader ones do the opposite but I assume we need a bit of both.

What I actually want before I support your architecture

If any of these ever leaves the working group and touches an actual Facebook login screen, here's the shortlist I'd want answered before I trust it:

  1. What exact field crosses each wire: PCN, a hash, a token, a proof? Tell me what each actor sees, precisely.

  2. Is that value the same every time, or fresh every time? "Same every time" is a tracking number regardless of pseudonymity

  3. Does PSA log the verification event? For how long? Who can read that log without a court order?

  4. Is Meta contractually blocked from using this for ads, recommendations, or "growth" or is that just another pinky promise?

  5. Is there a quiet server-to-server backdoor sitting underneath whatever polished flow gets demoed to the press?

If the answers are good, genuinely, I will eat my words in a follow-up post, deadpan and all. If the answer to any of these is "we'll figure it out later", or a non-answer answer well…

TL;DR

Where I land: Build the device-held credential, the ZK proof, the nullifier, the whole stack in recommendations 1 and 2. Not because it sounds cool (though sue me, it does), but because the actual gap between "Philippine online surveillance system” and "National troll filter" just needs engineering efforts, not cookie cutter standards that don’t really fit the problem. It cannot be done better through any other methods.

I've built working circuits myself so I know that the exact primitive and the math for scaling that pattern to a registry the size of PhilSys is doable. Client-side proving time is one number I'd actually want benchmarked, and that's just weeks of work, not a moonshot.

IDK, we can build this slowly and properly, or we can ship the yes/no oracle now and spend the next decade explaining to its own citizens why a foreign platform has a permanent, correlatable ID system sitting on its servers. I know which option I'm rooting for.

PS. To the Danki haters

I built some of the circuits I just cited. I'm aware someone's going to say it out loud regardless of what I write here, so let's just do this now.

"She's just selling NulSet" is not a rebuttal. It's a pretty old move, actually.. where instead of arguing with what someone said, you argue with why they might have said it, because that's easier than arguing math. Saying "is 30 hash computations inside a Groth16 circuit a small proof" requires knowing what a hash computation is. Saying "she sells the thing, so of course she'd say it’s buildable" requires exactly zero skills.

So here’s a standard I hold to myself and same as anyone: if my claim is "zk can be used for proofs of non-membership" the correct rebuttal is a broken proof, a wrong estimate, a benchmark that doesn't hold up and not a sentence about my incentives. An engineer who built a bridge saying "bridges like this are buildable" isn't pitching you their specific bridge. They're telling you this new way of building is doable. If you want to argue the load calculations, I'm listening. If your argument is "well, she built a bridge, so obviously she'd say that", well, that's just you telling on yourself.