App Store Connect rejected the iOS upload with 'Invalid Export Compliance Code' because ITSAppUsesNonExemptEncryption was <true/> without an accompanying ITSEncryptionExportComplianceCode. Apply a TestFlight-only workaround now and document the full production path that needs to land before any public App Store / Mac App Store release. Info.plist (ios + macos) - ITSAppUsesNonExemptEncryption set to <false/> on both platforms to unblock TestFlight internal-test uploads. This is NOT correct for public release: Chanora ships non-system crypto (chacha20poly1305 in chanora_storage, custom Ed25519/P-256/EAX in tsclientlib, bundled TLS in rustls), so the value MUST be flipped back to <true/> with a valid ITSEncryptionExportComplianceCode before any public submission. docs/release/export-compliance.md (new) - Closed-source mass-market path under EAR §740.17(b)(1) with Note 3 to Category 5, Part 2 of the CCL (reclassifies the app from ECCN 5D002.c.1 to ECCN 5D992.c). - BIS annual self-classification report as CSV per Supplement No. 8 to Part 742, with authorization type MMKT; recipients crypt@bis.doc .gov + enc@nsa.gov; deadline Feb 1 for prior calendar year; no fee, no pre-approval, file within 30 days of first export. - Apple App Store Connect questionnaire answers for Chanora, with the supporting-documentation upload (sent BIS CSV + cover sheet). - Step-by-step revert plan once Apple issues the UUID, including the optional CHANORA_IOS_EXPORT_COMPLIANCE_CODE build-variable pattern (procivis/one-wallet style) to keep the UUID out of git. - Annual maintenance plus the fallback to CCATS / SNAP-R if Chanora ever adds non-standard crypto or E2EE DMs. - References to Apple docs, eCFR §740.17, Supplement No. 8, and real- world closed-source iOS Info.plist examples (Keybase, Status.im, Cryptomator) confirming the UUID format and key placement. Verification - plutil -lint apps/chanora_flutter/ios/Runner/Info.plist: OK - plutil -lint apps/chanora_flutter/macos/Runner/Info.plist: OK Follow-up - README.md / LICENSE-{APACHE,MIT} / DEC-020 still declare Chanora as Apache-2.0/MIT dual-licensed. The closed-source export-compliance doc assumes proprietary distribution; the license decision needs reconciliation before submission so the BIS CSV and Apple questionnaire answers match the actual distribution model.
149 lines
11 KiB
Markdown
149 lines
11 KiB
Markdown
# iOS / macOS Export Compliance
|
|
|
|
Status: TestFlight workaround applied. Production submission requires the steps below.
|
|
|
|
## Current state (2026-06-08)
|
|
|
|
Both `apps/chanora_flutter/ios/Runner/Info.plist` and `apps/chanora_flutter/macos/Runner/Info.plist` declare:
|
|
|
|
```xml
|
|
<key>ITSAppUsesNonExemptEncryption</key>
|
|
<false/>
|
|
```
|
|
|
|
This is a **temporary workaround** to unblock TestFlight uploads. It is NOT correct for public App Store release because Chanora ships non-exempt encryption (see "Why Chanora is not exempt" below). The workaround is acceptable for internal testing under TestFlight while the BIS self-classification and Apple export-compliance documentation are being prepared.
|
|
|
|
## Why Chanora is not exempt
|
|
|
|
Chanora is a **closed-source proprietary** application that uses encryption beyond Apple's "standard system encryption" exemption:
|
|
|
|
1. `chacha20poly1305` in `crates/chanora_storage` — AEAD encryption of the local Data Encryption Key (DEK) for secret storage.
|
|
2. `tsclientlib` in `crates/chanora_protocol` and `crates/chanora_audio` — implements TeamSpeak's custom Ed25519 / P-256 / EAX cryptographic handshake. This is NOT standard IETF TLS.
|
|
3. `rustls` in `crates/chanora_protocol` and `crates/chanora_resolver` — ships its own TLS implementation rather than using the system stack.
|
|
|
|
Apple's exemption (HTTPS via system networking only) does not apply. Because Chanora is closed-source, the "publicly available source code" path (§734.3(b)(3) + §742.15(b)) is also unavailable — that route is reserved for open-source distributions whose cryptographic source is publicly accessible without restriction.
|
|
|
|
Chanora's path is therefore the **mass-market self-classification** route under §740.17(b)(1) with Note 3 to Category 5, Part 2 of the CCL — closed-source consumer apps with standard published algorithms qualify as mass-market and self-classify under **ECCN 5D992.c**. `ITSAppUsesNonExemptEncryption` must be `<true/>` for any public release, and `ITSEncryptionExportComplianceCode` must accompany it.
|
|
|
|
## The submission flow (in order)
|
|
|
|
### Step 1: File the BIS annual self-classification report
|
|
|
|
US Department of Commerce, Bureau of Industry and Security (BIS), requires an annual self-classification report for mass-market encryption software under EAR §740.17(b)(1). Since Chanora is **closed-source** but distributed as a mass-market consumer app with standard cryptographic algorithms, it qualifies for self-classification under the **mass-market note (Note 3 to Category 5, Part 2 of the CCL)**, which reclassifies it from ECCN 5D002.c.1 to **ECCN 5D992.c**.
|
|
|
|
There is **no fee**, **no review**, **no pre-approval needed**. You export immediately under (b)(1) and file the report within 30 days.
|
|
|
|
The format is a **CSV file** per Supplement No. 8 to Part 742, not a prose email body. Send it as an attachment:
|
|
|
|
**Recipients** (CC both):
|
|
- `crypt@bis.doc.gov`
|
|
- `enc@nsa.gov`
|
|
|
|
**Subject**: `Self-Classification Report — Chanora — [YEAR]`
|
|
|
|
**CSV template** (fill in `[BRACKETS]`):
|
|
|
|
```csv
|
|
Submitter Name,Submitter Email,Submitter Phone,Submitter Address,Item Type,Item Name,Item Version,ECCN,Authorization Type,Encryption Algorithm,Key Length,Item Description
|
|
[YOUR NAME],[YOU@EMAIL.COM],[PHONE],[ADDRESS],Mass Market Encryption Software,Chanora,[VERSION e.g. v1.0.0-rc.1],5D992.c,MMKT,ChaCha20-Poly1305; Ed25519; P-256 ECDH; EAX-AES; TLS 1.2/1.3,256,Cross-platform voice communication client for TeamSpeak-compatible servers. Cryptographic algorithms are all published standards; the application is closed-source proprietary software distributed via mass-market channels (Apple App Store).
|
|
```
|
|
|
|
**Notes on the CSV**:
|
|
- **Authorization Type** `MMKT` signals the mass-market self-classification under Note 3.
|
|
- **ECCN** `5D992.c` — the mass-market note reclassifies closed-source mass-market software with standard crypto from 5D002.c.1 to 5D992.c, removing it from "EI" and "NS" controls.
|
|
- The CSV is a **single row** for one product. For multiple products, add one row per product.
|
|
- Send it **once per calendar year** (BIS deadline: February 1 for prior calendar year).
|
|
- Keep the sent-mail receipt — Apple may ask to see proof during the App Store review.
|
|
- BIS does not reply. Filing the report is the compliance event; no approval or confirmation is needed.
|
|
|
|
### Step 2: App Store Connect — encryption questionnaire
|
|
|
|
Path: `App Store Connect → My Apps → Chanora → App Information → Encryption` (or during version submission, the same questionnaire appears).
|
|
|
|
Answer the questions as follows for Chanora:
|
|
|
|
1. "Does your app use encryption?" → **Yes**
|
|
2. "Does your app qualify for any of the exemptions provided in Category 5, Part 2 of the U.S. Export Administration Regulations?" → **No**
|
|
(We do use non-exempt crypto — see "Why Chanora is not exempt".)
|
|
3. "Does your app implement any encryption algorithms that are proprietary or not accepted as standard by international standard bodies (IEEE, IETF, ITU, etc.)?" → **No**
|
|
(ChaCha20-Poly1305 is RFC 8439; Ed25519 is RFC 8032; P-256 is FIPS 186-4. TeamSpeak's handshake uses standard primitives even though the orchestration is custom.)
|
|
4. "Does your app implement any standard encryption algorithms instead of, or in addition to, using or accessing the encryption in Apple's operating system?" → **Yes**
|
|
5. "Is your app made available on the French App Store?" → answer per your distribution plan (Yes if you want EU/FR availability).
|
|
|
|
Apple will then ask you to upload supporting documentation. Upload:
|
|
- A PDF copy of your sent email from Step 1 (showing the date sent and both recipients), with the CSV attachment shown or attached.
|
|
- A short cover sheet identifying: product name (Chanora), ECCN (`5D992.c`), authorization basis (`§740.17(b)(1)` mass-market self-classification under Note 3 to Category 5, Part 2 of the CCL), and the date the report was sent to BIS.
|
|
|
|
### Step 3: Receive the code from Apple
|
|
|
|
After review (typical turnaround: a few business days to ~2 weeks), Apple sends an email with the export compliance code. The code is a UUID, lowercase, 36 characters, e.g. `aeafaa12-a017-48c8-a29a-bdd3061f93e3` (this is Keybase's actual code, used here as a format example only).
|
|
|
|
The code is **per-app, not per-version**. Once issued it can be reused for every subsequent build until your encryption usage materially changes.
|
|
|
|
### Step 4: Revert the workaround and patch in the code
|
|
|
|
Edit `apps/chanora_flutter/ios/Runner/Info.plist`:
|
|
|
|
```xml
|
|
<key>ITSAppUsesNonExemptEncryption</key>
|
|
<true/>
|
|
<key>ITSEncryptionExportComplianceCode</key>
|
|
<string>PASTE_APPLE_ISSUED_UUID_HERE</string>
|
|
```
|
|
|
|
Do the same for `apps/chanora_flutter/macos/Runner/Info.plist`.
|
|
|
|
Optionally — to avoid committing the literal UUID to git — use a build variable instead (matches the `procivis/one-wallet` pattern):
|
|
|
|
```xml
|
|
<key>ITSAppUsesNonExemptEncryption</key>
|
|
<true/>
|
|
<key>ITSEncryptionExportComplianceCode</key>
|
|
<string>$(CHANORA_IOS_EXPORT_COMPLIANCE_CODE)</string>
|
|
```
|
|
|
|
Then set `CHANORA_IOS_EXPORT_COMPLIANCE_CODE` as a build setting (in `Runner.xcodeproj` user-defined settings, in a non-tracked `.xcconfig`, or via the CI/Fastlane environment).
|
|
|
|
Validate after editing:
|
|
|
|
```bash
|
|
plutil -lint apps/chanora_flutter/ios/Runner/Info.plist
|
|
plutil -lint apps/chanora_flutter/macos/Runner/Info.plist
|
|
```
|
|
|
|
### Step 5: Submit and confirm
|
|
|
|
Re-upload the build via Xcode → Organizer → Distribute App, or via Fastlane / CI. App Store Connect should no longer reject the build with the "Invalid Export Compliance Code" error.
|
|
|
|
If you use Fastlane, you can also pass `uses_non_exempt_encryption: true` to `upload_to_testflight` / `upload_to_app_store` as belt-and-suspenders; the Info.plist value is authoritative.
|
|
|
|
## Annual maintenance
|
|
|
|
- Every January, re-send the BIS self-classification CSV (Step 1) with the current year and the latest released version. Deadline: **February 1** for the prior calendar year. Keep the receipt.
|
|
- The Apple-issued code does NOT need re-issuance unless Chanora adds a fundamentally new encryption capability (e.g. end-to-end encrypted DMs would qualify; bumping an existing AEAD library version would not).
|
|
- If you add E2EE for direct messages, change the at-rest encryption algorithm, or introduce proprietary (non-standard) cryptography, the mass-market classification may no longer apply — you would need to either re-self-classify or file a CCATS (Commodity Classification) request via SNAP-R, and Apple will need updated documentation and a new code.
|
|
|
|
## References
|
|
|
|
- Apple — `ITSAppUsesNonExemptEncryption` key: <https://developer.apple.com/documentation/bundleresources/information-property-list/itsappusesnonexemptencryption>
|
|
- Apple — `ITSEncryptionExportComplianceCode` key: <https://developer.apple.com/documentation/bundleresources/information-property-list/itsencryptionexportcompliancecode>
|
|
- Apple — Complying with encryption export regulations: <https://developer.apple.com/documentation/security/complying-with-encryption-export-regulations>
|
|
- Apple — App Store Connect Help, export compliance overview: <https://developer.apple.com/help/app-store-connect/manage-app-information/overview-of-export-compliance/>
|
|
- Apple — App Store Connect Help, determine and upload encryption documentation: <https://developer.apple.com/help/app-store-connect/manage-app-information/determine-and-upload-app-encryption-documentation/>
|
|
- BIS — Encryption FAQs: <https://www.bis.doc.gov/index.php/policy-guidance/encryption>
|
|
- BIS — EAR §740.17 (License Exception ENC, self-classification): <https://www.ecfr.gov/current/title-15/subtitle-B/chapter-VII/subchapter-C/part-740/section-740.17>
|
|
- BIS — Supplement No. 8 to Part 742 (self-classification report CSV format): <https://www.ecfr.gov/current/title-15/subtitle-B/chapter-VII/subchapter-C/part-742>
|
|
- BIS — Note 3 to Category 5, Part 2 of the CCL (mass-market note): <https://www.ecfr.gov/current/title-15/subtitle-B/chapter-VII/subchapter-C/part-774>
|
|
- Real-world examples (closed-source iOS apps with `ITSEncryptionExportComplianceCode`):
|
|
- Keybase: <https://github.com/keybase/client/blob/master/shared/ios/Keybase/Info.plist>
|
|
- Status.im: <https://github.com/status-im/status-mobile/blob/develop/ios/StatusIm/Info.plist>
|
|
- Cryptomator: <https://github.com/cryptomator/ios/blob/develop/Cryptomator/Info.plist>
|
|
- Build-variable pattern (avoid committing UUID to git):
|
|
- Procivis One Wallet: <https://github.com/procivis/one-wallet/blob/main/ios/Wallet/Info.plist>
|
|
|
|
## Decision log
|
|
|
|
| Date | Decision | Rationale |
|
|
|---|---|---|
|
|
| 2026-06-08 | Temporarily set `ITSAppUsesNonExemptEncryption=<false/>` on both iOS and macOS Info.plist | Unblock TestFlight internal testing while BIS / Apple paperwork is being prepared. Re-do per Steps 1-5 above before any public App Store release. |
|