Preden Baby Monitor Timmy prenaša zvok in video, se morata napravi najti in si zaupati. Ta korak seznanjanja je najbolj kritičen trenutek celotnega postopka. Tukaj pojasnjujem, kako Timmy seznani napravi, katera kriptografija je v ozadju in zakaj bližnji napadalec ne more neopazno prevzeti povezave.
Težava: Kako moja naprava ve, s kom se pogovarja?
Ko se dve napravi prvič povežeta, je osrednje vprašanje: ali se naprava A res pogovarja z napravo B ali je nekdo vmes? V kriptografiji se temu reče napad s posrednikom (MITM).
Timmy to rešuje z izmenjavo ključev Diffie-Hellman na eliptičnih krivuljah (ECDH) prek storitve Firebase v kombinaciji z uporabnikovim vizualnim preverjanjem..
Naslednji diagram na kratko prikazuje celoten postopek seznanjanja:
sequenceDiagram
autonumber
participant A as 📱 Device A
participant F as ☁️ Firebase
participant B as 📱 Device B
Note over A,B: Phase 1 — Discovery
A->>A: Generate ECDH key pair (P-256)
B->>B: Generate ECDH key pair (P-256)
alt Auto-Pairing (Nearby BLE)
A-->>B: BLE broadcast: SBM:XKQM
B-->>A: BLE broadcast: SBM:R7NP
Note over A,B: Lower code wins → determines creator/joiner
else Manual Pairing
A->>A: Display 4-char code
Note right of A: User reads code
B->>B: User enters code
end
Note over A,B: Phase 2 — ECDH Key Exchange
A->>F: Write public key (PubA) to meeting doc
B->>F: Write public key (PubB) to meeting doc
F-->>B: Read PubA
F-->>A: Read PubB
Note over A,B: Phase 3 — Shared Secret
A->>A: sharedSecret = ECDH(privA, PubB)
B->>B: sharedSecret = ECDH(privB, PubA)
Note over A,B: Both compute identical 32-byte secret
A->>A: SAS = SHA-256("sas:" + sort(PubA,PubB) + secret) → 2-digit number
B->>B: SAS = SHA-256("sas:" + sort(PubA,PubB) + secret) → 2-digit number
Note over A,B: Phase 4 — Visual Verification
A->>A: Display SAS: 42
B->>B: Display SAS: 42
Note over A,B: 👤 User compares numbers on both screens
A->>A: User confirms ✓
B->>B: User confirms ✓
Note over A,B: Phase 5 — Key Derivation
A->>A: pairingKey = SHA-256("pair:" + secret)
B->>B: pairingKey = SHA-256("pair:" + secret)
A->>A: docKey = SHA-256("doc:" + pairingKey)
A->>A: encKey = SHA-256("enc:" + pairingKey)
Note over A,B: ✅ Paired — all future signaling encrypted with AES-256-GCM
Celotno zaporedje protokola seznanjanja — vir, ki ga lahko urejate: docs/diagrams/pairing-sequence.mmd
1. korak: Vsaka naprava ustvari par ključev
Ob odprtju zaslona za seznanjanje vsaka naprava ustvari začasni par ključev ECDH na krivulji P-256 (secp256r1):
- Zasebni ključ — ostane izključno v napravi
- Javni ključ — izmenja se prek storitve Firebase
Ključi so ustvarjeni z generatorjem kriptografsko varnih naključnih števil (Random.secure())
in so veljavni samo za ta poskus seznanjanja. Za vsak nov poskus se ustvarijo novi ključi.
2. korak: Izmenjava javnih ključev prek storitve Firebase
Da se napravi lahko najdeta, Timmy uporablja 4-znakovno kodo kot skupno točko. To kodo je mogoče samodejno odkriti prek Nearby Connections (Bluetooth Low Energy) ali vnesti ročno. Nima nobene kriptografske vrednosti; napravi le usmeri k istemu dokumentu v storitvi Firebase Firestore.
Ko obe napravi poznata kodo, vsaka zapiše svoj javni ključ ECDH v skupni dokument Firestore. Nato vsaka naprava iz tega dokumenta prebere javni ključ druge naprave.
Ključno je: pošlje se samo javni ključ. Zasebni ključ nikoli ne zapusti naprave. Kdor spremlja promet Firebase, vidi javne ključe, vendar iz njih ne more izračunati skupne skrivnosti. To temelji na zahtevnosti problema diskretnega logaritma na eliptičnih krivuljah (ECDLP).
3. korak: Izračun skupne skrivnosti
Ko obe napravi odkrijeta javni ključ druga druge, neodvisno izračunata isto skupno skrivnost:
sharedSecret = ECDH(myPrivateKey, remotePublicKey)
→ 32 bytes (identical on both devices)
Matematika eliptičnih krivulj zagotavlja, da oba izračuna dasta enak rezultat, čeprav vsaka naprava pozna le svoj zasebni ključ in javni ključ druge naprave.
4. korak: Preverjevalna številka (SAS)
Iz skupne skrivnosti se izpelje kratki niz za preverjanje pristnosti (SAS) — dvomestna številka, prikazana na obeh napravah:
hash = SHA-256("sas:" + sort(pubkeyA, pubkeyB) + sharedSecret)
number = (hash[0] × 256 + hash[1]) mod 100 → 00 to 99
Obe napravi prikažeta isto številko — na primer 42. Uporabnik vizualno preveri, ali se številki na obeh zaslonih ujemata, nato pa potrdi na vsaki napravi posebej.
Zakaj napadalec tega ne more ponarediti
Napadalec, ki izvaja napad s posrednikom, bi moral prestreči izmenjavo ključev v storitvi Firebase. Natančneje, moral bi:
- Zamenjati prava javna ključa, shranjena v dokumentu Firestore, s svojima
- Z vsako napravo vzpostaviti ločeno skupno skrivnost
sequenceDiagram
autonumber
participant A as 📱 Device A
participant M as 🕵️ Attacker (MITM)
participant B as 📱 Device B
Note over A,B: Attacker intercepts the Firebase key exchange
A->>A: Generate key pair (privA, PubA)
B->>B: Generate key pair (privB, PubB)
M->>M: Generate TWO key pairs (privM1, PubM1) + (privM2, PubM2)
A->>M: Write PubA to Firebase
M->>M: Replace PubA with PubM1
M->>B: B reads PubM1 (thinks it is PubA)
B->>M: Write PubB to Firebase
M->>M: Replace PubB with PubM2
M->>A: A reads PubM2 (thinks it is PubB)
Note over A,B: Each device computes a DIFFERENT shared secret
A->>A: secret_A = ECDH(privA, PubM2)
M->>M: secret_A = ECDH(privM2, PubA)
M->>M: secret_B = ECDH(privM1, PubB)
B->>B: secret_B = ECDH(privB, PubM1)
Note over A,M: secret_A ≠ secret_B
A->>A: SAS_A = SHA-256("sas:" + sort(PubA,PubM2) + secret_A) → 73
B->>B: SAS_B = SHA-256("sas:" + sort(PubM1,PubB) + secret_B) → 18
rect rgb(255, 230, 230)
Note over A,B: ❌ User sees DIFFERENT numbers!
A->>A: Display: 73
B->>B: Display: 18
Note over A,B: 👤 User notices mismatch → cancels pairing
end
Note over A,B: 🛡️ Attack detected — MITM cannot force SAS match (P = 1/100)
Zaznavanje napada s posrednikom prek neujemanja SAS — vir, ki ga lahko urejate: docs/diagrams/mitm-detection.mmd
V tem primeru napadalec izračuna skupno skrivnost S_A z napravo A in
drugo skupno skrivnost S_B z napravo B. Ker S_A ≠ S_B,
napravi izračunata različni preverjevalni številki.
Napadalec ne more doseči, da bi se številki ujemali, ker:
- Ne pozna zasebnih ključev naprav
- SHA-256 ni obrnljiv
- Verjetnost naključnega ujemanja je le 1 od 100
Uporabnik na zaslonih vidi različni številki in prekine povezovanje. Napad je takrat postal viden.
5. korak: Dokončanje seznanjanja
Šele ko uporabnik potrdi preverjanje na obeh napravah se seznanjanje dokonča:
- Ključ za seznanjanje, dolg 64 znakov (256 bitov), se izpelje iz skupne skrivnosti:
SHA-256("pair:" + sharedSecret) → pairingKey - Ključ dokumenta je izpeljan kot
SHA-256("doc:" + pairingKey)in se uporablja kot ključ dokumenta Firestore - Šifrirni ključ je izpeljan kot
SHA-256("enc:" + pairingKey)in zagotavlja ključ AES-256-GCM za šifrirano signalizacijo - Obe napravi shranita isti ključ za seznanjanje in preideta na izbiro načina
Od tega trenutka so vsi nadaljnji poskusi povezave (signalizacija prek storitve Firestore in nastavitev WebRTC) šifrirani s skupnim ključem AES-256-GCM. Ključ za seznanjanje se nikoli ne pošlje v zaledni sistem; kot identifikator dokumenta se uporablja le njegova zgoščena vrednost SHA-256.
Arhitektura sistema
Naslednji diagram prikazuje komponente, vključene v seznanjanje in komunikacijo:
flowchart TB
BABY["📱 Baby Phone
Baby Mode"]
PARENT["📱 Parent Phone
Parent Mode"]
BABY <==>|"🔒 WebRTC Peer-to-Peer · DTLS-SRTP
Audio · Video · DataChannel"| PARENT
BABY -.-|"🔵 Bluetooth LE · Nearby
Auto-Discovery"| PARENT
subgraph FIREBASE["☁️ Firebase (Google Cloud)"]
direction LR
AUTH["🪪 Anonymous
Authentication"]
FS["📄 Firestore
Pairing + Signaling"]
CF["⚡ Cloud Functions
getTurnCredentials"]
end
BABY <-->|"🔐 AES-256-GCM encrypted
SDP · ICE · ECDH keys"| FS
FS <-->|"🔐 AES-256-GCM encrypted
SDP · ICE · ECDH keys"| PARENT
BABY -.->|Token| AUTH
PARENT -.->|Token| AUTH
STUN["📡 STUN server
stun.cloudflare.com:3478"]
TURN["🔄 TURN relay
local or Cloudflare"]
BABY & PARENT -->|Short-lived credentials| CF
CF -->|local first, Cloudflare fallback| TURN
BABY & PARENT -.->|NAT Traversal| STUN
BABY -.->|"Relay Fallback"| TURN
TURN -.->|"Relay Fallback"| PARENT
style BABY fill:#FBF6F0,stroke:#B5734A,stroke-width:2px
style PARENT fill:#FBF6F0,stroke:#B5734A,stroke-width:2px
style FIREBASE fill:#fff5f5,stroke:#E9B44C,stroke-width:2px
style AUTH fill:#E9B44C,stroke:#2B2D42
style FS fill:#E9B44C,stroke:#2B2D42
style CF fill:#E9B44C,stroke:#2B2D42
style STUN fill:#F6E3D2,stroke:#B5734A
style TURN fill:#7BC47F,stroke:#2B2D42
Pregled arhitekture sistema — vir, ki ga lahko urejate: docs/diagrams/pairing-architecture.mmd
Komunikacijske poti podrobneje:
- WebRTC med napravama (debela črta): Zvok, video in DataChannel tečejo neposredno med napravama — šifrirano z DTLS-SRTP. Noben strežnik ne vidi teh podatkov.
- Firebase Firestore (polna črta): Podatki za seznanjanje (ključi ECDH) in signalizacija (SDP/ICE) potekajo prek storitve Firestore ter so od konca do konca šifrirani z AES-256-GCM. Firebase teh podatkov ne more dešifrirati.
- STUN strežnik: Obe napravi odkrijeta svoj javni naslov IP, da se lahko vzpostavi neposredna povezava med napravama.
- TURN posrednik: Če neposredna povezava ni mogoča (npr. pri uporabi mobilnih podatkov), izbrani lokalni strežnik TURN ali strežnik Cloudflare TURN posreduje šifrirane medijske podatke. Kratkotrajne poverilnice (24 h) se pridobijo prek storitve Firebase Cloud Functions.
- Bluetooth LE (črtkana črta): Nearby Connections samodejno odkrije bližnje naprave — prenese se le koda za vzpostavitev stika, brez podatkov o ključih.
Rezervna možnost: ročni vnos kode
Če Bluetooth ni na voljo (npr. na starejših napravah), lahko 4-znakovno kodo vnesete tudi ročno. Ročni vnos uporablja isto izmenjavo ključev ECDH in isto preverjanje SAS kot samodejno seznanjanje. Edina razlika je, da uporabnik kodo prebere in vnese, namesto da bi jo naprava odkrila prek BLE.
Ker izmenjava ključev ECDH v obeh primerih poteka prek storitve Firebase, je varnost enaka. 4-znakovna koda je le skupna točka; dejansko šifriranje temelji na 256-bitnem ključu, izpeljanem iz ECDH.
Povzetek
| Varnostni mehanizem | Ščiti pred |
|---|---|
| Izmenjava ključev ECDH (P-256) | Prisluškovanjem prometu pri izmenjavi ključev |
| Začasni pari ključev | Tajnost za naprej — pretekla seznanjanja ostanejo varna |
| Vizualna preverjevalna številka (SAS) | Napadom s posrednikom (MITM) med izmenjavo ključev |
| Zgoščena vrednost SHA-256 kot ključ dokumenta | Pridobivanjem kode iz storitve Firestore |
| Šifriranje AES-256-GCM | Prisluškovanjem podatkom signalizacije |
| Potrditev na obeh straneh | Enostranskemu seznanjanju brez vednosti uporabnika |
| DTLS-SRTP (WebRTC) | Prisluškovanjem zvoku/videu |
Te plasti se dopolnjujejo: ECDH ščiti izmenjavo ključev, preverjevalna številka ščiti pred MITM, AES-256-GCM ščiti signalizacijo, WebRTC pa medije. Napadalec bi moral to verigo prebiti na več mestih, ne da bi naprave ali starši to opazili.