Commit Graph
9 Commits
Author SHA1 Message Date
c079d2ab5d Harden what a locked or seized device gives away (#1484)
* Harden what a locked or seized device gives away

The realistic compromise for many of the people this app is built for is
not interception but a phone taken, and often unlocked under coercion.
Content encryption is in good shape; these are the gaps around it.

Hide notification previews, by default. Notification content is rendered
by the system on the lock screen, so it was readable without unlocking:
DM alerts carried the sender's nickname and the full message body, and
geohash alerts put the geohash in the title. Alerts now state that a DM,
mention, or location-channel activity arrived and withhold the rest until
the app is opened. userInfo still carries the routing peer ID and deep
link, neither of which the system displays, so taps land where they did.
A settings toggle restores full previews for anyone who wants them.
Default-on is the deliberate part: a phone face-up on a table should not
narrate conversations, and someone who wants previews can say so.

Cover the window on willResignActive, so the snapshot iOS stores for the
app switcher shows a placeholder rather than an open conversation. Opaque
rather than blurred, because blurred large text stays partly legible and
the snapshot goes to disk. Added synchronously from a UIKit notification
with queue: nil, since the capture follows shortly after and an
OperationQueue hop or a SwiftUI state change can lose that race. Panic
wipe already deleted snapshots already on disk; this stops new ones from
being worth deleting.

Bound media by age as well as size. The 100 MB quota only ever considered
incoming files, so outgoing media had no lifetime at all and a received
photo could outlive its conversation indefinitely. A launch-time sweep now
deletes managed media older than seven days, incoming and outgoing, with
the same exemptions quota eviction honors: in-flight live captures and
files reserved by a delivery or deletion in progress.

Make /clear tell the truth on the mesh timeline. It recorded an echo
watermark and left the gossip archive on disk for up to 6 hours, so
someone who cleared before a police stop had deleted nothing. Clearing now
erases the archive too. The watermark still matters: it suppresses
pre-clear messages this device hears again from peers. The cost is that
the device stops serving recent public backlog until it hears fresh
traffic, which is a fair reading of what clearing a timeline means.

Documented in PRIVACY_POLICY.md and the privacy assessment, including a
new section on what is deliberately NOT addressed: there is still no
duress mechanism of any kind (no decoy passphrase, no wipe-on-failed-auth,
no app lock), macOS gets no file-protection classes, and media is not
sealed at the app layer. The duress question is a product decision as much
as an engineering one, since in some jurisdictions destroying data on
demand is itself an offence and hiding may protect someone better than
destroying, so it is called out rather than guessed at.

Three findings from the audit that prompted this work turned out to be
already fixed on main and are not included: keychain accessibility is
AfterFirstUnlockThisDeviceOnly with a retrying migration, the panic media
wipe uses a two-location durable marker transaction, and panic already
discards staged share-extension content.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Inject the previews preference instead of reading shared defaults

CI caught a real problem this introduced. `NotificationServiceTests`
already asserted the full-preview title and body, and both it and the new
redaction tests read `NotificationPrivacySettings` from
`UserDefaults.standard` in the same process. Whichever ran second
depended on the other's cleanup, so it passed locally and failed in CI:

  XCTAssertEqual failed: ("🔒 new dm") is not equal to ("🔒 DM from Alice")

Fixed at the source rather than by ordering or serialization.
`NotificationService` now takes a `hidePreviewsProvider`, defaulting to
the real preference, so each test states which behavior it asserts. The
pre-existing test asks for previews shown and gains a redacted
counterpart; the redaction tests no longer touch the shared store.

`NotificationPrivacySettings` also gained store-injecting accessors so
the default-value and round-trip assertions can use an isolated suite
rather than mutating preferences other tests read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 19:26:29 +02:00
229a41557e docs: correct inaccurate privacy and metadata claims (#1485)
* docs: correct inaccurate privacy and metadata claims

Several documented guarantees did not match the implementation. These
matter more than ordinary doc drift: someone deciding whether to carry
this phone to a protest reads these sentences as the threat model.

- Peer IDs were described as "short ephemeral IDs derived per session"
  that "rotate periodically" and "prevent tracking". They are the first
  8 bytes of the Noise static key fingerprint, stable across sessions
  and reboots, and replaced only by a panic wipe. Corrected in the
  whitepaper (§3, §8), IdentityModels, and BitchatProtocol, whose
  header notes claimed "no persistent identifiers in protocol headers"
  while every header carries exactly one.
- "No plaintext message content is ever written to disk" was false for
  accepted media, which is stored unsealed under the platform's
  data-protection class. Narrowed to what actually holds.
- Padding was described as applying to all packets but fragments. Only
  noiseEncrypted and noiseHandshake frames are padded; the pad bytes
  equal the pad length rather than being random; and because that
  length must fit one byte, a frame needing over 255 bytes of padding
  is emitted unpadded. Documented in the whitepaper (§4.1) and
  MessagePadding.
- The gossip archive window is 6 hours in production, not the
  15 minutes claimed in PRIVACY_POLICY.md and the privacy assessment.
  The 15-minute figure is the struct default that BLEService overrides.
- The privacy assessment credited iOS BLE address randomization without
  noting that stable app-layer identifiers defeat it.

The whitepaper's future-work list now names the changes these
corrections imply: rotating on-air identity, padding for non-Noise
types, and making the announce neighbor list optional.

No behavior change; comments and documentation only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs: correct the same claims in the README

The README repeats two of the claims corrected elsewhere in this PR, and
it is the document people actually read before deciding to trust the app.

- "no persistent identifiers" is the inverse of what the mesh does; it
  now points at the whitepaper's identity and metadata sections.
- "end-to-end encryption with forward secrecy" holds for live Noise
  sessions but not for sealed store-and-forward mail, which the
  whitepaper already flags as its main cryptographic trade-off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 19:02:26 +02:00
jackandGitHub e9275cb3d8 Relabel private Nostr envelopes honestly in docs; harden legacy envelope validation (#1480)
Split-out safe half of #1437 (the kind-1402 wire migration stays held for Android coordination). Docs (README/WHITEPAPER/PRIVACY_POLICY/privacy-assessment) now describe the actual proprietary DM construction — kind 1059 gift wrap carrying XChaCha20-Poly1305 with a 24-byte nonce, base64url v2: framing, and an HKDF that borrows the nip44-v2 info label but is not the NIP-44 key schedule — instead of claiming NIP-17/44/59.

Hardens the existing legacy inbound path: 64 KiB ciphertext cap before decode (~46x the largest producible legacy envelope), outer kind/recipient-tag/signature binding, tagless kind-13 seal binding, unsigned kind-14 inner binding, inner tags restricted to the two shapes deployed clients emit (verified against every historical iOS release and a fixture frozen from Android production), SecRandomCopyBytes failure now throws, non-UTF-8 plaintext now throws instead of returning empty. Adds frozen cross-platform fixtures with hash-pinned generators; interop-reviewed with no rejection surface for deployed clients.
2026-07-26 12:50:17 +02:00
jackandGitHub bcb21f2116 Require review before importing shared content (#1430)
Replaces the share-extension handoff that auto-sent shared text/URLs into the current channel with a validated, single-slot 24h envelope that shows the destination and a bounded preview and requires an explicit tap to copy into the composer — it never sends. Rejects malformed/oversized/control-character/non-HTTP(S)/expired payloads (including bidi-override U+202E), and clears on consume/cancel/expiry/panic.

Rebased onto main with Persian strings added for all new keys (30-locale coverage verified), plus a panic-recovery clear so an envelope staged during an interrupted wipe cannot survive relaunch.
2026-07-26 11:40:17 +02:00
cd727c6867 Make panic wipe deterministic and device-bound (#1431)
* Make panic wipe deterministic and device-bound

* Scope install markers to iOS

* Harden panic recovery and service shutdown

* Invalidate queued BLE ingress during panic

* Harden panic keychain and media cleanup

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: jack <jack@deck.local>
2026-07-26 10:28:50 +02:00
jackandGitHub 820c933958 Harden PTT audio and the 1.7.1 release (#1423)
Centralize PTT and voice audio-session ownership, harden courier/bridge/outbox delivery and recovery, correct location and delivery-state races, add privacy/release metadata, and ship reproducible universal Arti slices with Release CI coverage.

Validated by the full iOS suite, repeated audio/fragment/performance regressions, BitFoundation tests, strict lint and dead-code analysis, universal iOS Release builds, and iOS/macOS archives.
2026-07-10 14:04:09 +02:00
jackandClaude Fable 5 4093ee6733 Rebuild Arti from audited source with enforced provenance
Vendored arti.xcframework rebuilt from source (Rust 1.96.0, normalized
archive metadata for reproducible hashes). New ARTI-BINARY-PROVENANCE.md
records toolchain, rebuild steps, and a SHA256 manifest for every file
in the xcframework. A new CI workflow turns that policy into a gate:
PRs must keep the binary matching the manifest, and binary changes must
ship with source/lockfile/build-script evidence.

Also raises TorManager.awaitReady's default timeout from 25s to 75s to
match the bootstrap monitor deadline - a shorter wait reported "not
ready" while Arti was still legitimately bootstrapping, silently
stranding queued relay work.

Privacy policy, Tor integration doc, and privacy assessment updated to
match the current implementation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 16:22:15 +01:00
Vignesh SkandaandGitHub c0f7b1903a Update PRIVACY_POLICY.md
Proper link wasn't added in the contact section, replaced it with the correct link.
2025-07-10 21:00:12 +05:30
jack b1f67a105c Add comprehensive privacy policy
- Explains no data collection, no servers, no tracking
- Documents local-only storage including identity key for favorites
- Details encryption methods and user rights
- Emphasizes privacy-first philosophy
- Written in plain language for accessibility
2025-07-06 19:31:55 +02:00