Áður en Baby Monitor Timmy sendir hljóð og mynd þurfa tvö tæki að finna hvort annað og treysta hvort öðru. Þetta pörunar skref er mikilvægasta augnablikið í öllu ferlinu. Hér útskýri ég hvernig Timmy parar tæki, hvaða dulmál býr að baki og hvers vegna árásaraðili í nágrenninu getur ekki tekið yfir tenginguna án þess að eftir því sé tekið.
Vandinn: Hvernig veit tækið mitt við hvern það er að tala?
Þegar tvö tæki tengjast í fyrsta sinn er meginspurningin: Talar tæki A í raun við tæki B, eða er einhver á milli þeirra? Í dulmálsfræði kallast það milliliðaárás (MITM).
Timmy leysir þetta með lyklaskiptum með Diffie-Hellman á sporgerli (ECDH) í gegnum Firebase, ásamt sjónrænni staðfestingu notandans.
Skýringarmyndin hér að neðan sýnir allt pörunarferlið í einu yfirliti:
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
Heildarröð pörunarsamskiptareglunnar — breytanleg frumskrá: docs/diagrams/pairing-sequence.mmd
Skref 1: Hvort tæki býr til lyklapar
Þegar pörunarskjárinn er opnaður býr hvort tæki til tímabundið ECDH-lyklapar á P-256-ferlinum (secp256r1):
- Einn einkalykill — helst eingöngu í tækinu
- Einn opinber lykill — er skipt á í gegnum Firebase
Lyklarnir eru búnir til með dulmálslega öruggum slembitölugjafa (Random.secure())
og eru aðeins gildir í þessari einu pörunartilraun. Nýir lyklar eru búnir til
fyrir hverja nýja tilraun.
Skref 2: Skipst á opinberum lyklum í gegnum Firebase
Til að tvö tæki geti fundið hvort annað notar Timmy 4-stafa kóða sem fundarstað. Þennan kóða er hægt að finna sjálfkrafa með Nearby Connections (Bluetooth Low Energy) eða slá hann inn handvirkt. Hann hefur ekkert dulmálslegt gildi; hann gerir tækjunum aðeins kleift að finna sama skjalið í Firebase Firestore.
Þegar bæði tækin þekkja kóðann skrifar hvort þeirra sinn opinbera ECDH-lykil í sameiginlegt Firestore-skjal. Síðan les hvort tæki opinbera lykil hins tækisins úr skjalinu.
Mikilvægt: aðeins opinberi lykillinn er sendur. Einkalykillinn fer aldrei úr tækinu. Sá sem fylgist með Firebase-umferð sér opinbera lykla en getur ekki reiknað út sameiginlega leyndarmálið út frá þeim. Það byggir á erfiðleikanum við að reikna strjála lógaritma á sporgerlum (ECDLP).
Skref 3: Reikna sameiginlega leyndarmálið
Þegar bæði tækin hafa fundið opinbera lykil hvors annars reikna þau hvort um sig út sama sameiginlega leyndarmál:
sharedSecret = ECDH(myPrivateKey, remotePublicKey)
→ 32 bytes (identical on both devices)
Stærðfræði sporöskjulaga ferla tryggir að báðir útreikningar gefi sömu niðurstöðu, þótt hvort tæki þekki aðeins sinn einkalykil og opinbera lykil hins.
Skref 4: Staðfestingarnúmerið (SAS)
Úr sameiginlega leyndarmálinu er stuttur auðkenningarstrengur (SAS) leiddur — tveggja tölustafa tala sem birtist á báðum tækjum:
hash = SHA-256("sas:" + sort(pubkeyA, pubkeyB) + sharedSecret)
number = (hash[0] × 256 + hash[1]) mod 100 → 00 to 99
Bæði tækin sýna sama númer — til dæmis 42. Notandinn ber sjónrænt saman hvort númerin á báðum skjám séu eins og staðfestir síðan á hvoru tæki fyrir sig.
Hvers vegna árásaraðili getur ekki falsað þetta
Árásaraðili í miðjunni þyrfti að grípa inn í lyklaskiptin í Firebase. Nánar tiltekið þyrfti hann að:
- Skipta út raunverulegu opinberu lyklunum í Firestore-skjalinu fyrir sína eigin
- Koma á aðskildum sameiginlegum leyndarmálum með hvoru tæki
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)
Greining milliliðaárásar með ósamræmi í SAS — breytanleg frumskrá: docs/diagrams/mitm-detection.mmd
Í því tilviki reiknar árásaraðilinn sameiginlegt leyndarmál S_A með tæki A og
annað sameiginlegt leyndarmál S_B með tæki B. Þar sem S_A ≠ S_B,
reikna tækin út ólík staðfestingarnúmer.
Árásaraðilinn getur ekki látið númerin passa vegna þess að:
- Hann þekkir ekki einkalykla tækjanna
- SHA-256 er ekki afturkræft
- Líkurnar á tilviljanakenndri samsvörun eru aðeins 1 af 100
Notandinn sér ólík númer á skjánum á hvoru tæki og hættir við pörunina. Þá er árásin orðin sýnileg.
Skref 5: Ljúka pöruninni
Aðeins eftir að notandinn hefur staðfest að númerin passi á báðum tækjum lýkur pöruninni:
- Einn 64-stafa pörunarlykill (256 bitar) er leiddur af sameiginlega leyndarmálinu:
SHA-256("pair:" + sharedSecret) → pairingKey - Skjalslykillinn er leiddur sem
SHA-256("doc:" + pairingKey)og er notaður sem Firestore-skjalalykill - Dulkóðunarlykillinn er leiddur sem
SHA-256("enc:" + pairingKey)og gefur AES-256-GCM-lykilinn fyrir dulkóðaðar merkjasendingar - Bæði tækin geyma sama pörunarlykil og fara í val á ham
Frá þessum tímapunkti eru allar frekari tengingartilraunir (Firestore-merkjasendingar, WebRTC-uppsetning) dulkóðaðar með sameiginlega AES-256-GCM-lyklinum. Pörunarlykillinn er aldrei sendur í bakendann; aðeins SHA-256-tætigildi hans er notað sem auðkenni skjalsins.
Kerfisarkitektúr
Skýringarmyndin hér að neðan sýnir þá hluta sem koma að pörun og samskiptum:
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
Yfirlit yfir kerfisarkitektúr — breytanleg frumskrá: docs/diagrams/pairing-architecture.mmd
Samskiptaleiðir í smáatriðum:
- WebRTC jafningi til jafningja (þykk lína): Hljóð, myndskeið og DataChannel-gögn fara beint milli tækjanna — dulkóðuð með DTLS-SRTP. Enginn þjónn hefur aðgang að þessum gögnum.
- Firebase Firestore (heil lína): Pörunargögn (ECDH-lyklar) og merkjaskipti (SDP/ICE) fara í gegnum Firestore — dulkóðuð enda á milli með AES-256-GCM. Firebase getur ekki afkóðað gögnin.
- STUN-þjónn: Bæði tækin finna opinbera IP-tölu sína svo hægt sé að koma á beinni jafningjatengingu.
- TURN-miðlari: Ef bein tenging er ekki möguleg (t.d. í gegnum farsímagögn) miðlar valinn staðbundinn eða Cloudflare TURN-þjónn dulkóðuðum miðlum. Skammtímainnskráningarupplýsingar (24 klst.) eru sóttar í gegnum Firebase Cloud Functions.
- Bluetooth LE (punktalína): Nearby Connections finnur tæki í nágrenninu sjálfkrafa — aðeins fundarkóðinn er sendur, ekkert lykilefni.
Valkostur: Kóði sleginn inn handvirkt
Ef Bluetooth er ekki tiltækt (t.d. í eldri tækjum) er einnig hægt að slá 4-stafa kóðann inn handvirkt. Handvirk innsláttur kóðans notar sömu ECDH-lyklaskipti og sömu SAS-staðfestingu og sjálfvirk pörun. Eini munurinn er að notandinn les og slær inn kóðann í stað þess að hann finnist með BLE.
Þar sem ECDH-lyklaskiptin fara í gegnum Firebase í báðum tilvikum er öryggið eins. 4-stafa kóðinn er aðeins fundarstaður; raunveruleg dulkóðun byggir á 256-bita lykli sem er leiddur af ECDH.
Samantekt
| Öryggisráðstöfun | Verndar gegn |
|---|---|
| ECDH-lyklaskipti (P-256) | Hlerun á umferð lyklaskipta |
| Tímabundin lyklapör | Framvirk leynd — fyrri paranir eru áfram öruggar |
| Sjónrænt staðfestingarnúmer (SAS) | Milliliðaárás (MITM) við lyklaskipti |
| SHA-256-tætigildi sem skjalslykill | Útdráttur kóða úr Firestore |
| AES-256-GCM-dulkóðun | Hlerun á merkjasendingargögnum |
| Staðfesting beggja aðila | Einhliða pörun án vitundar notandans |
| DTLS-SRTP (WebRTC) | Hlerun á hljóði og myndskeiðum |
Þessi lög vinna saman: ECDH verndar lyklaskiptin, staðfestingarnúmerið verndar gegn MITM, AES-256-GCM verndar merkjasendingar og WebRTC verndar miðla. Árásaraðili þyrfti að rjúfa þessa keðju á mörgum stöðum án þess að tækin eða foreldrarnir tækju eftir því.