NEURLFilter PIR status check fails with CipherML errors (Code 1100/1800)

Hello !

following up from a related thread (account transfer issue, now resolved: https://developer.apple.com/forums/thread/843546?answerId=911820022), but this is a distinct, new issue so starting fresh as suggested.

The extension launches fine, XPC handshake succeeds, and PIR registration completes successfully. But the subsequent NEPIRChecker status check fails every time (retried repeatedly over several minutes, same result each time):

PIR status returned error <Error Domain=com.apple.CipherML Code=1100 "Unable to query status due to errors: Error details were logged and redacted." UserInfo={NSLocalizedDescription=Unable to query status due to errors: Error details were logged and redacted., NSUnderlyingError=0x677037cf0 {Error Domain=com.apple.CipherML Code=1800 "Error details were logged and redacted." UserInfo={NSLocalizedDescription=Error details were logged and redacted.}}}>

Config being registered (confirmed from the logs):

pirServerURL = https://pir.dropbet.app/
pirPrivacyPassIssuerURL = https://pir.dropbet.app/issue

Both endpoints are live and were already validated as part of the original NEURL Filter configuration approval. I've captured a sysdiagnose from the device right when this was happening, so I have that ready to attach to a bug report if useful — just let me know and I'll file one.

Any idea what CipherML error 1100/1800 typically indicates?

Unfortunately those error codes don’t tell you much:

  • 1100 is basically a something failed error.
  • 1800 means something failed but we’re pretty sure that the underlying details went into the system log.

So the next step is to reproduce the problem, grab a system log snapshot, and then look up the for other log entries in the com.apple.CipherML subsystem.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi @DTS Engineer

Following up with a much more precise finding after digging into server-side logs.

The failure is happening in our PIR server's OHTTP gateway, specifically in HPKE request decapsulation:

[OHTTP] decapsulateRequest failed: Crypto.CryptoKitError.underlyingCoreCryptoError(error: 503316581)

This is thrown from HPKE.Recipient(privateKey:ciphersuite:info:encapsulatedKey:) / .open(ct, authenticating:) (DHKEM(X25519, HKDF-SHA256) / HKDF-SHA256 / AES-128-GCM, per RFC 9458).

What I've confirmed:

  • Not an entitlement/config issue
    The NEURL Filter configuration is correctly onboarded, and the client reaches this point successfully (handshake, registration all succeed first).

  • Reproducible independently of the iOS client
    I wrote a standalone Python script using a real HPKE library (pyhpke), fetched our server's published /.well-known/ohttp-gateway key, and built a byte-for-byte RFC 9458-compliant OHTTP request against it. It fails with the exact same CryptoKitError.underlyingCoreCryptoError(error: 503316581). So this isn't specific to anything the iOS client does.

  • Failure rate seems to fluctuate over server uptime
    Right after restarting the server process, a handful of real device requests briefly succeeded decryption (progressing to a later, unrelated parsing error), then it reverted to failing on essentially every attempt a few minutes later. This pattern makes me suspect a concurrency/thread-safety issue around the shared Curve25519.KeyAgreement.PrivateKey being used to construct a new HPKE.Recipient on every incoming request, rather than a static configuration problem.

Any idea what corecrypto error 503316581 typically indicates, or whether there's a known gotcha with concurrent HPKE.Recipient construction from a shared private key in swift-crypto? Happy to share the Python repro script and/or file a bug with logs attached if that would help narrow it down.

Thanks again for sticking with this one.

@RomainDropB do you see any more logs in the console other than PIR status returned error <Error Domain=com.apple.CipherML Code=1100 "Unable to query status due to errors: Error details were logged and redacted." UserInfo={NSLocalizedDescription=Unable to query status due to errors: Error details were logged and redacted., NSUnderlyingError=0x677037cf0 {Error Domain=com.apple.CipherML Code=1800 "Error details were logged and redacted." UserInfo={NSLocalizedDescription=Error details were logged and redacted.}}}>

Because I am also facing same issue and i found some more logs along with this error.

@Pushpak-Ambadkar123 hello,

yes, I've dug a lot further on this. The full client-side sequence (on-device, Console.app filtered by NEPIRChecker) looks like this before that error:

<NEPIRChecker>: -[NEPIRChecker start:responseQueue:completionHandler:] - Register with PIR Server (...) completed registration with PIR for Group ...
<NEPIRChecker>: -[NEPIRChecker start:responseQueue:completionHandler:]_block_invoke - PIR status returned error <Error Domain=com.apple.CipherML Code=1100 ...>

But the real root cause, if you also run your own PIR server and have access to its logs: on my server side, this traces back to an HPKE decapsulation failure in the OHTTP gateway

Crypto.CryptoKitError.underlyingCoreCryptoError(error: 503316581)

thrown from HPKE.Recipient(...)/.open(ct, authenticating:) when decrypting the incoming OHTTP request. I confirmed it's reproducible independently of the real client using a standalone HPKE test script, and that the failure rate seems to fluctuate with server uptime (worse after the process has been running a while) - pointing toward a possible concurrency issue around shared key material.

Are you running your own PIR server, or hitting this purely from the client side? If you have server logs too, it'd be great to compare notes; might help build an even stronger case for @DTS Engineer Quinn.

Written by RomainDropB in 912402022

Any idea what corecrypto error 503316581 typically indicates … ?

Well, that was fun.

I presume you’re running this server not on the Mac, and thus it’s using OpenSSL-based crypto. If that’s the case then you can follow this logic…

Error 503316581 is 0x1e000065. Within that, the first byte is the library and the bottom 12 bits are the error code. That means the library is 0x1e, or 30, or ERR_LIB_CIPHER, and the code is 0x065, or 101, or CIPHER_R_BAD_DECRYPT. My understanding is that this is coming out of an authenticated decryption algorithm, so I’m gonna presume that it means that the authentication part of that failed.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi @DTS Engineer Quinn

made real progress based on your BAD_DECRYPT lead, with a new, more specific finding.

I bypassed swift-crypto's HPKE.Recipient/HPKE.Sender entirely and implemented RFC 9180 base-mode HPKE manually using the lower-level primitives (Curve25519 key agreement, HKDF, AES-GCM) instead. Confirmed this resolves the platform-specific bug: a standalone HPKE test request I construct now decrypts successfully both locally on macOS and on the actual Fly.io/Linux deployment - identical result on both platforms now, where before it only worked on macOS.

However, real requests from the iOS device still fail - now with a cleaner, more specific error: a genuine AEAD authentication failure (HPKE open failed), not the generic library crash from before. So my implementation correctly handles a standard RFC 9180 request, but something about the actual request the device sends doesn't match what I'm computing.

Is there anything non-standard about how NEURLFilterManager/the PIR client constructs or routes these OHTTP requests that might not be obvious from the RFC - e.g., does it go through an Apple-operated OHTTP relay that re-encapsulates in transit, or is there a PIR-specific wrinkle in how info/aad are built that differs from generic OHTTP? Trying to figure out what to compare my reimplementation against, since I don't have visibility into the real device's raw request bytes.

Thanks again - your BAD_DECRYPT read got us this far.