# EXPORT-COMPLIANCE.md — US encryption export controls

**Status: measured, 2026-09-01.** Checked against primary sources on this date — not recalled from
training. Re-verify before submission; export regulations change and this file will go stale.

**Not legal advice.** The ambiguity in §3 needs export counsel before first submission.

---

> ## ✅ SETTLED — 5 September 2026: exempt, on a good-faith reading
>
> **`app/project.yml` sets `ITSAppUsesNonExemptEncryption: false`**, and that is the answer every
> shipped build carries. This banner previously claimed the opposite; it was wrong about the code.
>
> Don, 5 Sep 2026: *"we already discussed the non-exempt thing ... the controversy was whether our
> protocols were somehow not standard but our protocols don't speak to the method of encryption so we
> want our best good faith estimate as to our compliance posture without creating undue burden by
> over broad interpretation of what is exempt vs non exempt."*
>
> **The reasoning.** The app defines no cryptography of its own. It uses X25519, Ed25519,
> ChaCha20-Poly1305, SHA-256 and AES-GCM, all standard and published. `PROTOCOL.md` specifies
> routing, addressing, tokens and retention -- it does not specify a cipher, a mode, or a key
> schedule of its own invention. Implementing a published algorithm in Rust rather than calling the
> operating system's copy does not make the algorithm non-standard; the earlier sections of this
> document treated it as if it did, and that was the over-broad reading Don is rejecting.
>
> The functionality is ordinary confidentiality for personal communication: not special-purpose, not
> for government or military use, no proprietary cryptography.
>
> **What must stay in agreement.** Three places say this, and they must never disagree again:
> `app/project.yml` (`false`), `web/encryption.html` §6, and this banner. The website said `true`
> until 5 Sep 2026, which is how the disagreement was found.
>
> **What would reopen it.** Shipping a primitive of our own, a non-published algorithm, or a
> special-purpose mode. Sections 3 to 6 below are kept as the record of the earlier analysis and its
> stricter reading; where they conflict with this banner, this banner is the decision.

---

---

## 1. Strength is not the axis

Key-length thresholds were the 1990s regime — the 40-bit and 56-bit limits, the PGP prosecution,
Bernstein. Those are gone. Using X25519 and ChaCha20-Poly1305 does not put this project in a worse
position than weaker choices would, and deliberately weakening anything would buy nothing.

What determines obligations is **what kind of item it is, whether the source is public, and where it
goes.**

Framework: Export Administration Regulations (EAR), administered by the Commerce Department's Bureau
of Industry and Security. Encryption software sits in Category 5 Part 2 of the Commerce Control List.
This app classifies as **ECCN 5D002**.

---

## 2. Two routes, and open source is dramatically simpler

### Route A — publicly available source code

> **15 CFR 742.15(b)(1):** "Publicly available encryption source code classified under ECCN 5D002 is
> not subject to the EAR."

Not "exempt." **Not subject.** No annual report, no classification request, no filing — provided the
source is genuinely publicly available.

> **15 CFR 742.15(b)(2):** notification to BIS and the ENC Encryption Request Coordinator by email is
> required **only** for publicly available source code that "provides or performs *non-standard
> cryptography*."

**Discrepancy on record.** BIS's own plain-language guidance page states that publicly available
source "is not subject to the EAR once the email notification per section 742.15(b) is sent" —
presenting notification as a blanket condition. The regulation limits it to non-standard
cryptography. **BIS's summary is stricter than BIS's rule.**

**Resolution: if we open-source, send the notification anyway.** One email to `crypt@bis.doc.gov` and
`enc@nsa.gov` with the repository URL. It costs nothing and moots §3's ambiguity entirely, whichever
reading is correct.

### Route B — closed source

Export under **License Exception ENC §740.17(b)(1)**, which carries a recurring obligation:

| | |
|---|---|
| **What** | Annual self-classification report |
| **To** | ENC Encryption Request Coordinator |
| **By** | **1 February**, covering the prior calendar year (1 Jan – 31 Dec) |
| **Format** | CSV only, twelve fields: product name, model number, manufacturer, ECCN, authorization type, item type, submitter name, telephone, email, mailing address, non-US components, non-US manufacturing locations |
| **Waived if** | A CCATS classification has been submitted for the item |
| **Not required if** | No applicable exports occurred that year |

Each product is reported once, in the year it was self-classified. If nothing changed since the prior
report, an email saying so suffices.

The second year is where this gets forgotten. Put it in a calendar, not in someone's head.

---

## 3. The open question — is our *protocol* "non-standard cryptography"?

EAR Part 772 defines non-standard cryptography as proprietary or unpublished cryptographic
functionality, *"including encryption algorithms **or protocols** that have not been adopted or
approved by a duly recognized international standards body"* — IEEE, IETF, ISO, ITU, ETSI, 3GPP, TIA,
GSMA.

**Our primitives are unambiguously standard.** X25519, ChaCha20-Poly1305, HKDF-SHA256, HMAC-SHA256 —
all published IETF specifications (`PROTOCOL.md` §4.1). The project rule against inventing
cryptography settles the algorithm question completely.

**Our protocol is not standards-body adopted, and will not be.** The Envelope, the fixed-layout AAD,
the token derivation, the route-discovery state machine — all bespoke.

Two readings:

- **Narrow** (non-standard means *secret or proprietary primitives*): we are clearly fine. This is
  also how the industry operates — the Signal Protocol is not an IETF standard, and Signal and
  WhatsApp ship without CCATS.
- **Literal** ("or protocols"): a custom protocol assembled from public parts is arguably reached.

### Working determination, 2 Sep 2026

**We self-classify as mass market using standard cryptography, and record the basis here.** Don's
reading, which drives this: *"the nature of the encryption is what their concern is... the content of
what is encrypted is not really a protocol that affects the nature of the encryption."*

That is right about the axis. Envelope framing, transport selection and fragment reassembly do not
change what X25519 or ChaCha20-Poly1305 do, or how hard either is to break. §1 already says strength
is not the axis; capability is.

**But the argument must not rest on "ours is not a cryptographic protocol,"** because parts of it are
and a knowledgeable reviewer would say so: the handshake is a station-to-station exchange with a
signed transcript, the HKDF info strings bind specific material, the AAD layout is fixed for security
reasons, and destination tokens are derived from keys and time to be unlinkable. Those determine
security properties. A flawed handshake breaks everything regardless of primitive strength.

**The basis we actually rely on** is the definition's own wording — non-standard cryptography is
*proprietary or unpublished* functionality:

| Test | Us |
|---|---|
| Proprietary primitives? | **No.** X25519, Ed25519, ChaCha20-Poly1305, HKDF-SHA256, SHA-256 — all published IETF/FIPS, none invented here. The project rule against inventing cryptography settles it. |
| Unpublished? | **No, as of 2 Sep 2026.** <https://channelmessenger.netlify.app/encryption.html> documents the algorithms and their use, and `PROTOCOL.md` goes to anyone who asks. |
| Novel construction? | **No.** A signed ephemeral exchange with HKDF-derived keys is textbook. Signal's Double Ratchet is substantially more inventive and ships as mass market. |

#### Prior art for every mechanism — the concrete basis for "not novel"

Don asked the right question directly: *"Novel? Are we novel?"* The test is whether each mechanism can
be traced to existing published work. Every one can:

| Mechanism | Prior art |
|---|---|
| Ed25519, X25519, ChaCha20-Poly1305, HKDF-SHA256, SHA-256 | Published IETF/FIPS specifications. `PROTOCOL.md` §4.1: no primitive outside that table appears anywhere. |
| Handshake v1 — signed ephemeral DH, three flights | Station-to-Station; Diffie, van Oorschot & Wiener, 1992. |
| HMAC-SHA256 truncated to 16 bytes for tokens | HMAC as a PRF, RFC 2104. Truncation is standard practice. |
| Domain separation by label (`mesh/presence-v1` vs `mesh/destination-v1`) | The purpose of HKDF/HMAC labels; TLS 1.3's key schedule does the same. |
| Direction byte, so both parties derive different tokens from one secret | TLS deriving `client_write_key` and `server_write_key` from one master secret. |
| Epoch rotation, `floor(unixTime / 600)` | TOTP (RFC 6238) is `floor(T/X)`. BLE Resolvable Private Addresses rotate identically. **Apple and Google's Exposure Notification rolling proximity identifiers are the same shape**, published and analysed at global scale. |
| Independent keys for routing and content | Key separation; textbook. |
| Sender identity inside the ciphertext | Sealed sender; Signal, 2018. |
| Mandatory padding | Standard traffic-analysis defence. |

**Bespoke, not novel** — and that is the distinction the regulation turns on. Nothing here is an
invention; it is assembled from parts that each carry a citation.

Note also that being un-novel is a **security** property and not merely a regulatory convenience.
Novel cryptography in a shipping product is almost always a mistake, and §4.1's rules — never invent
cryptography, and no negotiable cipher suites because negotiation is a downgrade surface — are why
this table can be written at all.

The Exposure Notification comparison is the one to lead with: Apple and Google shipped HMAC-derived,
epoch-rotating, unlinkable identifiers to billions of devices. That mechanism is not treated as
non-standard cryptography.

**Why this is a determination we are entitled to make.** Self-classification is not a permission
granted to us; it is an assessment we make in good faith and document. Certainty about how a third
party would read "or protocols" is not the standard and never was. Don: *"we cannot know how somebody
else will interpret the answer, but we have to answer in good faith."* This section, and the published
page, are that record.

**Still get export counsel before external distribution**, and before any French availability (§4).
This determination is the working basis for internal testing and for answering Apple honestly; it is
not a substitute for review when the stakes rise. Open-sourcing would moot the question entirely via
the publicly-available route, which remains the cleanest resolution and is one more argument for it.

---

## 3a. What Apple's own validation told us, 2 Sep 2026

Two upload attempts and two API refusals settled how this app is actually handled, and the answer is
less onerous than §4 implies.

**Setting `ITSAppUsesNonExemptEncryption: true` in Info.plist breaks the upload:**

```
Invalid Export Compliance Code. The export compliance key value [] in the app's
Info.plist doesn't match the key value of the app's export compliance documentation.
```

Apple looks for a companion `ITSEncryptionExportComplianceCode`, which exists only for apps holding
encryption-declaration paperwork.

**Attempting to file that paperwork is refused, and the refusal is the useful part:**

```
Cannot create appEncryptionDeclarations unless either containsProprietaryCryptography
is True or containsThirdPartyCryptography and availableOnFrenchStore are both True.
```

Our honest answers are proprietary **No**, third-party **Yes**, French store **No** — a combination
Apple will not let you file a declaration for, **because that combination does not need one.** Apple's
own validation is confirming the §3 working determination from the other direction: an app using only
published third-party standard algorithms, not sold in France, files nothing.

**Therefore:** the key is omitted from Info.plist and the truthful answer is given **per build** via
`PATCH /v1/builds/{id}` with `usesNonExemptEncryption: true`. That is the same honest answer in the
place Apple actually wants it for this app's situation.

> **Never set the key to `false` to silence the prompt.** That is the false declaration builds 1–4
> carried, and it is what this document exists to prevent. Absent ≠ false: absent means "answered per
> build", and the per-build answer is *yes*.

## 3b. The question Apple actually asks — §3 is moot in their flow

Answered in App Store Connect for build 5 on 2 Sep 2026. The wording matters, because it is **not**
the EAR wording this document has been wrestling with:

> **What type of encryption algorithms does your app implement?**
> 1. Encryption algorithms that are proprietary or not accepted as standard by international standard
>    bodies (IEEE, IETF, ITU, etc.)
> 2. Standard encryption algorithms instead of, or in addition to, using or accessing the encryption
>    within Apple's operating system
> 3. Both algorithms mentioned above
> 4. None of the algorithms mentioned above

**Apple asks about ALGORITHMS. The phrase "or protocols" does not appear.** The §3 ambiguity — whether
a bespoke protocol assembled from standard parts counts as non-standard cryptography — simply does not
arise here. Our algorithms are X25519, Ed25519, ChaCha20-Poly1305, HKDF-SHA256 and SHA-256, every one
an IETF or FIPS publication, so **option 2** is unambiguously correct and required no interpretation.

Second and final question: *"Is your app going to be available for distribution in France?"* Answered
**No**, which keeps §4's separate French declaration out of scope for the beta. **Revisit this before
any App Store release that includes France.**

Result: build 5 moved from *Missing Compliance* to *Ready to Submit* and was assigned to the tester
group. No CCATS, no ERN, no BIS filing was requested at any point.

**Note for future uploads.** Apple's own dialog says the answers can be baked into Info.plist to avoid
answering per submission — but that is the route that produced the "Invalid Export Compliance Code"
upload failure in §3a, because `ITSAppUsesNonExemptEncryption: true` alone makes Apple demand a
companion code. **Answering per build in the TestFlight UI is the working path for this app.** The API
does not substitute: `PATCH /v1/builds/{id}` with `usesNonExemptEncryption` returns HTTP 200 and does
not persist.

## 4. Apple's gate

| Encryption in use | Upload to App Store Connect |
|---|---|
| Limited to what iOS provides | Nothing |
| **Industry-standard algorithms, own implementation** ← *us* | **French declaration only** |
| Proprietary algorithms not accepted by IEEE/IETF/ITU | CCATS **and** French declaration |

- `ITSAppUsesNonExemptEncryption` = **YES**. We implement a protocol; we are not limited to
  OS-provided encryption.
- The **French declaration** is required only if distributing on the App Store in France.
- **No CCATS** on the industry-standard-algorithm reading (§3).
- `ITSEncryptionExportComplianceCode` is added to Info.plist **only after** Apple approves
  documentation and issues a code — not before.
- Declare in Info.plist rather than answering the questionnaire per upload, or it reappears on every
  build.

Note for later: a dependency can change the answer even when our own code does not. Every Rust crate
we link is part of what we ship.

---

## 5. Destination restrictions

Independent of any filing. No export to embargoed destinations — currently Cuba, Iran, North Korea,
Syria, and specified Ukrainian regions. App Store territory availability handles this operationally,
but the obligation is the developer's, not Apple's.

---

## 6. Decision — Route B, closed source for v1

**Settled by Don on 2026-09-01** (`DECISIONS.md` F7). We are on Route B.

An earlier draft of this file recommended Route A. That recommendation did not account for a possible
future sale, and publishing is irreversible: every released version stays licensed forever, while
closed → open remains available at any time. Route B costs one CSV filing a year and preserves the
choice; Route A saves that filing and forecloses it.

**Live obligations from first export (App Store release):**

- [ ] Annual self-classification report — **due 1 February**, prior calendar year, twelve-field CSV,
      to the ENC Encryption Request Coordinator. Calendared, not remembered.
- [ ] `ITSAppUsesNonExemptEncryption` = YES in Info.plist
- [ ] French encryption declaration, if distributing in France
- [ ] No CCATS, on the industry-standard-algorithm reading (§3, and §4's middle row)
- [ ] Confirm §3's "or protocols" question with export counsel before first submission

**If this is revisited later**, Route A remains fully available, and the §742.15(b)(2) email should be
sent regardless of which reading of §3 applies — it costs nothing and moots the ambiguity.

**Licence caution if Route A is ever taken:** a source-available licence (Business Source, PolyForm,
or similar) may not qualify as "publicly available," since that turns on unrestricted dissemination.
Choosing one could take every downside of publishing with none of the relief. **Unverified** — check
before selecting a licence.

---

## 7. Sources — retrieved 2026-09-01

- 15 CFR 742.15 — https://www.law.cornell.edu/cfr/text/15/742.15
- BIS, annual self-classification — https://www.bis.gov/learn-support/encryption-controls/annual-self-classification
- BIS, encryption items not subject to the EAR — https://www.bis.gov/learn-support/encryption-controls/encryption-items-not-subject-to-ear
- BIS, Supplement No. 6 to Part 742 — https://www.bis.gov/ear/title-15/subtitle-b/chapter-vii/subchapter-c/part-742/supplement-no-6-part-742-technical
- Apple, export compliance documentation — https://developer.apple.com/help/app-store-connect/reference/app-information/export-compliance-documentation-for-encryption/
