Technische Übersicht des Protokolls
BLE Notfall-Mesh · Kompaktfassung · English version below
Dieses Dokument beschreibt in Grundzügen, wie BLE Notfall-Mesh technisch arbeitet — die Prinzipien, nicht die Bauanleitung. Die Sicherheit der App beruht bewusst nicht auf Geheimhaltung des Verfahrens, sondern auf etablierter, öffentlich geprüfter Kryptografie.
Grundidee
Es gibt keinen Server. Jedes Gerät ist gleichzeitig Sender, Empfänger und Brücke. Nachrichten springen über Bluetooth Low Energy (BLE) von Gerät zu Gerät, bis sie ihr Ziel erreichen. Ein Gerät, das gerade keinen Nachbarn hat, hält Pakete vor und gibt sie beim nächsten Treffen weiter. So entsteht Reichweite aus vielen kurzen Sprüngen.
Kryptografische Bausteine
Es kommen ausschließlich standardisierte, breit analysierte Verfahren zum Einsatz:
| Baustein | Verfahren | Wofür |
|---|---|---|
| Signaturen | Ed25519 | Beweist, von wem eine Nachricht stammt (Echtheit & Unverfälschtheit) |
| Schlüsseltausch | X25519 | Gemeinsamer Schlüssel für Direktnachrichten, pro Nachricht frisch |
| Verschlüsselung | ChaCha20-Poly1305 | Inhalt verschlüsseln und gegen Veränderung sichern (AEAD) |
| Hash | BLAKE2b | Fingerabdrücke, Paket-IDs, Kanalkennungen |
| Passwort → Schlüssel | Argon2id | Kanalschlüssel aus Name + Passwort; Schutz verschlüsselter Sicherungen |
| Schlüsselableitung | HKDF (SHA-256) | Sitzungs- und Nachrichtenschlüssel ableiten; saubere Domänentrennung |
| Ruheverschlüsselung | AES-256-GCM (Android-Schlüsselspeicher) | Sensible Felder auf dem Gerät verschlüsseln: Identität, Kanalschlüssel und -passwörter, Nachrichtentexte, Kontaktnotizen |
Jede Identität besteht aus zwei Schlüsselpaaren (Ed25519 zum Signieren, X25519 zum Verschlüsseln). Sie entstehen beim ersten Start auf dem Gerät, werden zusätzlich im Android-Schlüsselspeicher gesichert und verlassen das Gerät nie. Ihr Fingerabdruck (die „Sicherheitsnummer") lässt sich mit einem Gegenüber vergleichen, um einen Kontakt zweifelsfrei zu bestätigen.
Auf dem Gerät gespeichert (Ruheverschlüsselung)
Die empfindlichsten Daten liegen auf dem Gerät nicht im Klartext: die privaten Identitätsschlüssel, die Kanalschlüssel samt Passwörtern, die Nachrichtentexte sowie deine Kontaktnotizen und Feldwerte werden mit einem gerätegebundenen Schlüssel aus dem Android-Schlüsselspeicher feldverschlüsselt. Eine erbeutete Datenbankdatei gibt daraus weder Inhalte noch Schlüssel preis. Metadaten (Zeitstempel, Paket-IDs, Absenderkennung) bleiben offen, und die Datenbank selbst bleibt entsperrt, damit ein gesperrtes Gerät weiter weiterleitet. Gegen ein laufendes, entsperrtes Gerät hilft das nicht — dafür gibt es App-Sperre, optionale Auto-Löschung nach mehreren Fehlversuchen und Panik-Löschung.
Zwei Wege: Direktnachricht und Kanal
Direktnachricht („Sealed Sender")
In einem Netz, in dem jedes Gerät jedes Paket weiterträgt, darf nirgends im Klartext stehen, für wen ein Paket ist. Deshalb:
- Jede Direktnachricht erhält ein frisches, einmaliges Schlüsselpaar (X25519).
- Der Empfänger steht nirgends im Paket. Wer eine Nachricht öffnen kann, ist der Empfänger — das findet jedes Gerät durch einen billigen Probeversuch heraus.
- Der Absender steht innerhalb der Verschlüsselung und ist signiert. Auch die Art der Nachricht (Text, Bild, Quittung …) liegt innen.
Ein weiterleitendes Gerät sieht dadurch weder Inhalt noch Absender noch Empfänger — nur ein verschlüsseltes Bündel, das es blind weiterträgt.
Kanäle
Ein Kanal ist durch einen Namen und ein Passwort definiert. Daraus wird lokal per Argon2id ein gemeinsamer Schlüssel abgeleitet; auf dem Gerät wird nichts registriert und niemand verwaltet Mitglieder. Wer Name und Passwort kennt, kann mitlesen und schreiben. Einladungen bestehen aus Name + Passwort (als Text oder QR-Code) — es gibt keine Instanz, bei der man sie hinterlegen könnte. Ein Kanal kann auch offen sein (ohne Passwort): dann entsteht der Schlüssel aus dem Namen und einem festen, öffentlich bekannten Passwort — jeder mit dem Namen liest mit. Der eingebaute Notruf-Kanal ist so ein offener Kanal, damit sich Fremde im Ernstfall ohne Vorabsprache erreichen können; offene Kanäle sind ausdrücklich nicht vertraulich.
Fluten und Vorhalten (Store-and-Forward)
Jedes Gerät bietet Nachbarn eine Liste der Paket-Kennungen an, die es hat; der Nachbar fordert nur an, was ihm fehlt. Jede Paket-ID ist der Hash des Pakets selbst (inhaltsadressiert), sodass dasselbe Paket nie doppelt gespeichert oder endlos im Kreis geschickt wird. Fremde, unlesbare Pakete werden eine begrenzte Zeit vorgehalten (einstellbar 2–72 Stunden) und dann verworfen, ebenso bei vollem Speicher — Notrufe bleiben dabei am längsten erhalten.
Wovor das Verfahren schützt
- Inhalt: Ende-zu-Ende-verschlüsselt; Zwischenstationen können nichts lesen.
- Echtheit: Jede Nachricht ist signiert; ein gefälschter Absender fällt auf und wird verworfen.
- Empfänger-Anonymität: Bei Direktnachrichten ist nicht erkennbar, für wen sie sind.
- Kein Angriffspunkt Server: Es gibt keine zentrale Stelle, die abgeschaltet, beschlagnahmt oder zum Mitschneiden gezwungen werden könnte.
Grenzen
- Metadaten: Weiterleitende Geräte sehen Länge, Zeitpunkt und Paket-ID eines Pakets. Über Verkehrsanalyse lassen sich daraus Muster ableiten, auch ohne den Inhalt zu kennen.
- Anwesenheit: Wer in Funkreichweite danach sucht, kann erkennen, dass in der Nähe ein Gerät mit dieser App läuft. Ein Gerät, das gefunden werden will, muss sich zu erkennen geben.
- Funkstörung & Überlastung: BLE lässt sich stören (jammen), und ein Flooding-Netz lässt sich mit Müll fluten. Vorhaltezeit, Spam-Abwurf und Vorrang für Notrufe dämpfen das, verhindern es aber nicht.
- Öffentlicher Kanal: Der Notruf-Kanal ist bewusst für alle lesbar — sein Passwort ist bekannt.
- Keine Forward Secrecy: Wird ein langlebiger Schlüssel (etwa dein X25519-Schlüssel oder ein Kanalpasswort) später kompromittiert, lassen sich zuvor aufgezeichnete Nachrichten entschlüsseln. Einen fortlaufend wechselnden Sitzungsschlüssel gibt es (noch) nicht.
- Keine Zustellgarantie: Ob eine Nachricht ankommt, hängt davon ab, ob genügend Geräte in Reichweite mitmachen.
Offline-Karten
Kartenpakete sind .mbtiles-Dateien — SQLite-Datenbanken mit Kacheln. Verwendet werden
Vektorkacheln: Statt fertiger Bilder enthalten sie Geometrien, die auf dem Gerät
gezeichnet werden. Das ergibt Straßennamen, die dem Straßenverlauf folgen, saubere
Beschriftungsverteilung und bei jeder Zoomstufe scharfe Darstellung.
Gezeichnet wird mit MapLibre. Weil diese Bibliothek Kacheln nur über URLs lädt, läuft in der App
ein Server, der ausschließlich an 127.0.0.1 gebunden ist: Er liefert Kacheln aus der
lokalen Datei, den Stil und die Schriften. Diese Verbindungen verlassen das Gerät nicht. Die
Netzwerkregeln der App verbieten unverschlüsselte Verbindungen grundsätzlich und lassen sie nur
für diese Rückschleife zu.
Das Herunterladen eines Kartenpakets ist der einzige echte Netzzugriff der App. Er geschieht nur auf ausdrückliche Aktion des Nutzers, läuft über HTTPS und nimmt nach einem Abbruch per Bereichsanfrage die Übertragung wieder auf. Alternativ importiert man eine Datei, die außerhalb der App geladen wurde.
Erzeugt werden die Pakete mit planetiler aus OpenStreetMap-Daten (ODbL, Kachelschema OpenMapTiles,
CC-BY). Enthalten sind Straßen, Straßennamen, Gewässer, Bahnlinien, Ortsnamen und Grenzen bis
Zoomstufe 14; Gebäude, Geschäfte und Landbedeckung werden weggelassen, was die Pakete etwa
halbiert. Sie müssen im internen App-Speicher liegen: Auf dem FUSE-Speicher unter
/sdcard fehlt das Dateisperren, das SQLite zum Öffnen braucht — die App kopiert
importierte Pakete deshalb dorthin. Ältere Raster-Pakete bleiben weiterhin lesbar.
Offen und prüfbar
Bei Fragen zur Technik: support@stahldata.de.
Technical Protocol Overview
BLE Notfall-Mesh · Compact edition
This document outlines how BLE Notfall-Mesh works technically — the principles, not a build manual. The app's security deliberately does not rely on keeping the method secret, but on established, publicly analysed cryptography.
Core idea
There is no server. Every device is sender, receiver and bridge at once. Messages hop from device to device over Bluetooth Low Energy (BLE) until they reach their destination. A device with no neighbour in range holds packets and passes them on at the next encounter. Range is built from many short hops.
Cryptographic building blocks
Only standardised, widely analysed primitives are used:
| Block | Primitive | Purpose |
|---|---|---|
| Signatures | Ed25519 | Proves who a message is from (authenticity & integrity) |
| Key exchange | X25519 | Shared key for direct messages, fresh for each message |
| Encryption | ChaCha20-Poly1305 | Encrypts content and protects it from tampering (AEAD) |
| Hash | BLAKE2b | Fingerprints, packet IDs, channel identifiers |
| Password → key | Argon2id | Channel key from name + password; protects encrypted backups |
| Key derivation | HKDF (SHA-256) | Derives session and per-message keys; clean domain separation |
| Encryption at rest | AES-256-GCM (Android keystore) | Encrypts sensitive fields on the device: identity, channel keys and passwords, message texts, contact notes |
Each identity consists of two key pairs (Ed25519 for signing, X25519 for encryption). They are created on the device at first start, additionally protected by the Android keystore, and never leave the device. Their fingerprint (the “safety number”) can be compared with the other person to confirm a contact beyond doubt.
Stored on the device (encryption at rest)
The most sensitive data does not sit in plaintext on the device: the private identity keys, the channel keys with their passwords, the message texts and your contact notes and field values are field-encrypted with a device-bound key from the Android keystore. A captured database file yields neither contents nor keys. Metadata (timestamps, packet IDs, sender id) stays open, and the database itself stays unlocked so a locked device keeps relaying. This does not help against a running, unlocked device — for that there are the app lock, the optional auto-erase after several wrong attempts, and the panic erase.
Two paths: direct message and channel
Direct message (“sealed sender”)
In a network where every device relays every packet, the recipient must never appear in the clear. Therefore:
- Every direct message gets a fresh, one-time key pair (X25519).
- The recipient is nowhere in the packet. Whoever can open a message is the recipient — each device finds this out with a cheap trial decryption.
- The sender sits inside the encryption and is signed. The message type (text, image, receipt …) is inside too.
A relaying device therefore sees neither content nor sender nor recipient — only an encrypted bundle it carries onward blindly.
Channels
A channel is defined by a name and a password. A shared key is derived from them locally via Argon2id; nothing is registered on any device and no one manages members. Anyone who knows the name and password can read and write. Invitations are just name + password (as text or a QR code) — there is nowhere to lodge them. A channel can also be open (no password): the key is then derived from the name and a fixed, publicly known password, so anyone with the name reads along. The built-in emergency channel is such an open channel, so strangers can reach each other with no prior arrangement; open channels are explicitly not confidential.
Flooding and store-and-forward
Each device offers neighbours a list of the packet IDs it holds; the neighbour requests only what it is missing. Every packet ID is the hash of the packet itself (content-addressed), so the same packet is never stored twice or sent around in circles. Foreign, unreadable packets are held for a limited time (configurable 2–72 hours) and then dropped, as they are when storage fills — emergency calls are kept the longest.
What the design protects
- Content: end-to-end encrypted; intermediate hops can read nothing.
- Authenticity: every message is signed; a forged sender is detected and discarded.
- Recipient anonymity: direct messages do not reveal who they are for.
- No server to attack: there is no central point to shut down, seize or compel to log.
Limits
- Metadata: relaying devices see a packet's length, time and ID. Traffic analysis can derive patterns from this without knowing the content.
- Presence: anyone within radio range who looks can tell that a device running this app is nearby. A device that wants to be found has to make itself known.
- Jamming & overload: BLE can be jammed, and a flooding network can be spammed. Retention time, spam dropping and priority for emergency calls dampen this but do not prevent it.
- Public channel: the emergency channel is deliberately readable by everyone — its password is known.
- No forward secrecy: if a long-lived key (such as your X25519 key or a channel password) is later compromised, previously recorded messages can be decrypted. There is (not yet) a continuously rotating session key.
- No delivery guarantee: whether a message arrives depends on enough devices being in range and taking part.
Offline maps
Map packages are .mbtiles files — SQLite databases holding tiles. They contain
vector tiles: instead of ready-made images they hold geometry that is drawn on the
device. This yields street names following the course of the road, proper label placement and a
sharp picture at every zoom level.
Rendering is done by MapLibre. Since that library only loads tiles via URLs, the app runs a server
bound exclusively to 127.0.0.1: it serves tiles from the local file, the style and the
fonts. These connections never leave the device. The app's network rules forbid cleartext
connections in general and permit them only for this loopback.
Downloading a map package is the app's only real network access. It happens on explicit user action, over HTTPS, and resumes via range requests after an interruption. Alternatively a file fetched outside the app can be imported.
Packages are produced with planetiler from OpenStreetMap data (ODbL, OpenMapTiles schema, CC-BY).
They contain roads, street names, water, railways, place names and boundaries up to zoom level 14;
buildings, shops and land cover are omitted, which roughly halves their size. They must live in
internal app storage: the FUSE storage under /sdcard lacks the file locking SQLite
needs — which is why the app copies imported packages there. Older raster packages remain readable.
Open and auditable
Questions about the technology: support@stahldata.de.