Skôr než Baby Monitor Timmy začne prenášať zvuk a video, musia sa dve zariadenia navzájom nájsť a dôverovať si. Tento proces párovania je najdôležitejším krokom v celom postupe. Tu vysvetľujem, ako Timmy páruje zariadenia, aká kryptografia za tým stojí a prečo blízky útočník nemôže spojenie nepozorovane prevziať.
Problém: Ako moje zariadenie vie, s kým komunikuje?
Keď sa dve zariadenia pripájajú prvýkrát, hlavná otázka znie: komunikuje zariadenie A naozaj so zariadením B, alebo je medzi nimi niekto ďalší? V kryptografii sa to nazýva útok typu man-in-the-middle (MITM).
Timmy tento problém rieši pomocou výmeny kľúčov Elliptic Curve Diffie-Hellman (ECDH) cez Firebase v kombinácii s vizuálnym overením používateľom.
Nasledujúca schéma zobrazuje celý postup párovania na prvý pohľad:
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
Celá postupnosť párovacieho protokolu — upraviteľný zdroj: docs/diagrams/pairing-sequence.mmd
Krok 1: Každé zariadenie vytvorí pár kľúčov
Po otvorení obrazovky párovania každé zariadenie vytvorí dočasný pár kľúčov ECDH na krivke P-256 (secp256r1):
- Súkromný kľúč — zostáva výhradne v zariadení
- Verejný kľúč — vymieňa sa cez Firebase
Kľúče sa vytvárajú pomocou kryptograficky bezpečného generátora náhodných čísel (Random.secure())
a sú platné len pre tento jediný pokus o párovanie. Pre každý nový pokus sa vytvoria nové kľúče.
Krok 2: Výmena verejných kľúčov cez Firebase
Aby sa dve zariadenia mohli navzájom nájsť, Timmy používa 4-znakový kód ako miesto stretnutia. Tento kód možno automaticky nájsť cez Nearby Connections (Bluetooth Low Energy) alebo zadať ručne. Nemá žiadnu kryptografickú hodnotu; iba umožní, aby obe zariadenia našli ten istý dokument vo Firebase Firestore.
Keď obe zariadenia poznajú kód, každé zapíše svoj verejný kľúč ECDH do zdieľaného dokumentu Firestore. Potom každé zariadenie z tohto dokumentu prečíta verejný kľúč druhého zariadenia.
Dôležité: odosiela sa iba verejný kľúč. Súkromný kľúč nikdy neopustí zariadenie. Každý, kto sleduje komunikáciu Firebase, vidí verejné kľúče, ale nedokáže z nich vypočítať zdieľané tajomstvo To sa spolieha na náročnosť problému diskrétneho logaritmu na eliptických krivkách (ECDLP).
Krok 3: Výpočet zdieľaného tajomstva
Keď obe zariadenia získajú verejný kľúč toho druhého, nezávisle vypočítajú rovnaké zdieľané tajomstvo:
sharedSecret = ECDH(myPrivateKey, remotePublicKey)
→ 32 bytes (identical on both devices)
Matematika eliptických kriviek zaručuje, že oba výpočty prinesú rovnaký výsledok, hoci každé zariadenie pozná len svoj vlastný súkromný kľúč a verejný kľúč druhého zariadenia.
Krok 4: Overovacie číslo (SAS)
Zo zdieľaného tajomstva sa odvodí krátky overovací reťazec (SAS) — dvojciferné číslo zobrazené na oboch zariadeniach:
hash = SHA-256("sas:" + sort(pubkeyA, pubkeyB) + sharedSecret)
number = (hash[0] × 256 + hash[1]) mod 100 → 00 to 99
Obe zariadenia zobrazia rovnaké číslo — napríklad 42. Používateľ vizuálne porovná, či sa čísla na oboch obrazovkách zhodujú, a potom ich potvrdí na každom zariadení zvlášť.
Prečo to útočník nedokáže sfalšovať
Útočník typu man-in-the-middle by musel zachytiť výmenu kľúčov vo Firebase. Konkrétne by musel:
- Nahradiť skutočné verejné kľúče uložené v dokumente Firestore vlastnými
- Vytvoriť samostatné zdieľané tajomstvá s každým zariadením
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)
Detekcia man-in-the-middle pomocou nezhody SAS — upraviteľný zdroj: docs/diagrams/mitm-detection.mmd
V tomto prípade útočník vypočíta zdieľané tajomstvo S_A so zariadením A a
iné zdieľané tajomstvo S_B so zariadením B. Keďže S_A ≠ S_B,
zariadenia vypočítajú odlišné overovacie čísla.
Útočník nedokáže zabezpečiť, aby sa čísla zhodovali, pretože:
- Nepozná súkromné kľúče zariadení
- SHA-256 nie je reverzibilný
- Pravdepodobnosť náhodnej zhody je len 1 zo 100
Používateľ na obrazovkách vidí odlišné čísla a párovanie zruší. V tej chvíli je útok odhalený.
Krok 5: Dokončenie párovania
Až keď používateľ potvrdí overenie na oboch zariadeniach sa párovanie dokončí:
- Odvodí sa 64-znakový párovací kľúč (256 bitov) zo zdieľaného tajomstva:
SHA-256("pair:" + sharedSecret) → pairingKey - Kľúč dokumentu sa odvodí ako
SHA-256("doc:" + pairingKey)a slúži ako kľúč dokumentu Firestore - Šifrovací kľúč sa odvodí ako
SHA-256("enc:" + pairingKey)a poskytuje kľúč AES-256-GCM pre šifrovanú signalizáciu - Obe zariadenia uložia rovnaký párovací kľúč a prejdú na výber režimu
Od tohto momentu sú všetky ďalšie pokusy o pripojenie (signalizácia Firestore, nastavenie WebRTC) šifrované zdieľaným kľúčom AES-256-GCM. Párovací kľúč sa nikdy neodosiela do backendu; ako identifikátor dokumentu sa používa iba jeho hash SHA-256.
Architektúra systému
Nasledujúca schéma ukazuje komponenty zapojené do párovania a komunikácie:
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
Prehľad architektúry systému — upraviteľný zdroj: docs/diagrams/pairing-architecture.mmd
Komunikačné cesty podrobne:
- WebRTC peer-to-peer (hrubá čiara): Zvuk, video a DataChannel prúdia priamo medzi zariadeniami — šifrované pomocou DTLS-SRTP. Žiadny server tieto dáta nevidí.
- Firebase Firestore (plná čiara): Párovacie údaje (kľúče ECDH) a signalizácia (SDP/ICE) prechádzajú cez Firestore — sú end-to-end šifrované pomocou AES-256-GCM. Firebase nemôže tieto údaje dešifrovať.
- Server STUN: Obe zariadenia zistia svoju verejnú IP adresu, aby bolo možné vytvoriť priame peer-to-peer spojenie.
- Relé TURN: Ak priame spojenie nie je možné (napr. cez mobilné dáta), vybraný lokálny alebo Cloudflare TURN server prenáša šifrované médiá. Krátkodobé prihlasovacie údaje (24 h) sa získavajú cez Firebase Cloud Functions.
- Bluetooth LE (bodkovaná čiara): Nearby Connections automaticky objaví blízke zariadenia — prenáša sa len kód stretnutia, žiadny kľúčový materiál.
Záložná možnosť: Ručné zadanie kódu
Ak Bluetooth nie je k dispozícii (napr. na starších zariadeniach), 4-znakový kód možno zadať aj ručne. Ručné zadanie používa rovnakú výmenu kľúčov ECDH a rovnaké overenie SAS ako automatické párovanie. Jediný rozdiel je v tom, že kód používateľ prečíta a zadá namiesto toho, aby bol objavený cez BLE.
Keďže výmena kľúčov ECDH v oboch prípadoch prebieha cez Firebase, bezpečnosť je rovnaká. 4-znakový kód je len miesto stretnutia; skutočné šifrovanie je založené na 256-bitovom kľúči odvodenom z ECDH.
Zhrnutie
| Bezpečnostný mechanizmus | Chráni pred |
|---|---|
| Výmena kľúčov ECDH (P-256) | Odpočúvaním komunikácie pri výmene kľúčov |
| Dočasné páry kľúčov | Dopredné utajenie — predchádzajúce párovania zostávajú v bezpečí |
| Vizuálne overovacie číslo (SAS) | Man-in-the-middle (MITM) počas výmeny kľúčov |
| Hash SHA-256 ako kľúč dokumentu | Získaním kódu z Firestore |
| Šifrovanie AES-256-GCM | Odpočúvaním signalizačných údajov |
| Potvrdenie na oboch stranách | Jednostranným párovaním bez vedomia používateľa |
| DTLS-SRTP (WebRTC) | Odpočúvaním zvuku/videa |
Tieto vrstvy spolu zapadajú: ECDH chráni výmenu kľúčov, overovacie číslo chráni pred MITM, AES-256-GCM chráni signalizáciu a WebRTC chráni médiá. Útočník by musel tento reťazec prelomiť na viacerých miestach bez toho, aby si to zariadenia alebo rodičia všimli.