diff --git a/PARITY.md b/PARITY.md index 6cc9542..e4873c5 100644 --- a/PARITY.md +++ b/PARITY.md @@ -45,17 +45,35 @@ everything above the protocol. | Device identity + prekey rotation | ✅ | ✅ | retired prekeys retained past the relay window | | Vault (encryption at rest) | ✅ | ✅ | Android Keystore, AES-GCM, record name bound into the AAD | -| ML-KEM-768 (PQ agreement) | ⚠️ | ⚪ | see below | -| ML-DSA-65 (PQ identity) | ⚠️ | ⚪ | see below | +| ML-KEM-768 (PQ agreement) | ✅ | ✅ | cross-engine verified — see below | +| ML-DSA-65 (PQ identity) | ✅ | ✅ | see below | -⚠️ **The post-quantum gap is not what it looks like.** iOS has `PQKEM.swift` and -`PQSign.swift`, and they are inert: CryptoKit exposes ML-KEM only on iOS 26 and -the app targets iOS 16, so no shipped iOS build does post-quantum anything. -Android has no platform ML-KEM at any API level. So the two clients are -*currently equal in effect* and the honest parity target is v1/v2 negotiation -that degrades cleanly — which the Android engine already does, and the interop -harness asserts. Closing this properly is a separate decision (implement, or -take a vetted library) and it should be made once, for both clients. +🔴 **These two rows said ⚪ and ⚠️, and both were wrong — understating, which +is the same direction relay#45 warns about and the same one that had the +comparison page scoring a competitor unfairly.** The note that used to sit here +said iOS's PQ files were inert because CryptoKit exposes ML-KEM only on iOS 26 +while the app targeted 16, that Android had no ML-KEM at any API level, and so +"neither client does post-quantum anything today". None of that is true now, and +the middle claim was never quite the point. + +- **Android** uses BouncyCastle's `MLKEMExtractor` / `MLKEMGenerator` — FIPS 203, + a real implementation shipped in the engine. There is no *platform* ML-KEM on + Android at any API level, which is what the old note was reaching for, and it + is why the engine carries its own rather than a reason it has none. +- **iOS** uses CryptoKit's `MLKEM768`, and it is **unconditional**: + `project.yml` sets the deployment target to 26.0, so the runtime gate that + made those files inert is gone. `PQKEM.swift` says so at the top. + +⚠️ Corrected by a run, not by reading the source. The cross-engine harness +(`test/interop-swift-kotlin.mjs`) establishes it end to end: *"Android picks v3 +for an iPhone peer"*, *"the first Android envelope carries a PQ ratchet step"*, +*"the iPhone agrees the session is v3"*, and *"a v3 session survives being +archived and restored mid-conversation"* — with the same four in the other +direction. + +⚠️ The honest limit, which has not changed: post-quantum protection counts only +once **both** ends have it, so today that means two Android phones, or an +Android phone and an iPhone running a build that has not shipped yet. ---