Blog

Veilige koppeling in Baby Monitor Timmy

Hoe ECDH, die SAS-nommer en geënkripteerde seinboodskappe saamwerk.

Voordat Baby Monitor Timmy klank en video oordra, moet twee toestelle mekaar vind en mekaar vertrou. Hierdie koppeling stap is die belangrikste oomblik in die hele proses. Hier verduidelik ek hoe Timmy koppel, watter kriptografie daaragter sit en hoekom ’n aanvaller naby nie ongemerk die verbinding kan oorneem nie.

Die probleem: Hoe weet my toestel met wie dit praat?

Wanneer twee toestelle vir die eerste keer verbind, is die sentrale vraag: praat Toestel A regtig met Toestel B, of sit iemand tussenin? In kriptografie word dit ’n man-in-the-middle-aanval (MITM) genoem.

Timmy los dit op met ’n Elliptic Curve Diffie-Hellman (ECDH)-sleuteluitruiling oor Firebase, saam met visuele verifikasie deur die gebruiker.

Die volgende diagram wys die volledige koppelingsproses in ’n oogopslag:

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
      

Volledige volgorde van die koppelingsprotokol — wysigbare bron: docs/diagrams/pairing-sequence.mmd

Stap 1: Elke toestel skep ’n sleutelpaar

Wanneer die koppelskerm oopmaak, skep elke toestel ’n tydelike ECDH-sleutelpaar op die P-256-kurwe (secp256r1):

Die sleutels word geskep met ’n kriptografies veilige ewekansigegetalgenerator (Random.secure()) en is slegs geldig vir hierdie enkele koppelingspoging. Vars sleutels word vir elke nuwe poging geskep.

Stap 2: Ruil openbare sleutels oor Firebase uit

Om twee toestelle mekaar te laat vind, gebruik Timmy ’n 4-karakter-kode as ’n ontmoetingspunt. Hierdie kode kan outomaties ontdek word via Nearby Connections (Bluetooth Low Energy) of met die hand ingevoer word. Dit het geen kriptografiese waarde nie; dit laat net albei toestelle dieselfde Firebase Firestore-dokument vind.

Sodra albei toestelle die kode ken, skryf elkeen sy openbare ECDH-sleutel na ’n gedeelde Firestore-dokument. Daarna lees elke toestel die ander toestel se openbare sleutel uit daardie dokument.

Belangrik: slegs die openbare sleutel word gestuur. Die privaat sleutel verlaat nooit die toestel nie. Enigiemand wat Firebase-verkeer dophou, sien openbare sleutels, maar kan nie die gedeelde geheim bereken nie daaruit. Dit berus op die moeilikheid van die Elliptic Curve Discrete Logarithm Problem (ECDLP).

Stap 3: Bereken die gedeelde geheim

Sodra albei toestelle mekaar se openbare sleutel ontdek het, bereken hulle onafhanklik dieselfde gedeelde geheim:

sharedSecret = ECDH(myPrivateKey, remotePublicKey)
             → 32 bytes (identical on both devices)

Die wiskunde van elliptiese kurwes waarborg dat albei berekeninge dieselfde resultaat gee, al ken elke toestel net sy eie privaat sleutel en die ander een se openbare sleutel.

Stap 4: Die verifikasienommer (SAS)

Uit die gedeelde geheim word ’n Short Authentication String (SAS) afgelei — ’n tweesyfernommer wat op albei toestelle vertoon word:

hash   = SHA-256("sas:" + sort(pubkeyA, pubkeyB) + sharedSecret)
number = (hash[0] × 256 + hash[1]) mod 100   → 00 to 99

Albei toestelle wys dieselfde nommer — byvoorbeeld 42. Die gebruiker vergelyk visueel of die nommers op albei skerms ooreenstem en bevestig dan op elke toestel afsonderlik.

Hoekom ’n aanvaller dit nie kan namaak nie

’n Man-in-the-middle sou die sleuteluitruiling in Firebase moes onderskep. Spesifiek, sou hulle moes:

  1. Die regte openbare sleutels in die Firestore-dokument met hul eie vervang
  2. Afsonderlike gedeelde geheime met elke toestel vestig
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)
      

Opsporing van man-in-the-middle via ’n SAS-wanpassing — wysigbare bron: docs/diagrams/mitm-detection.mmd

In hierdie geval bereken die aanvaller ’n gedeelde geheim S_A met Toestel A en ’n ander gedeelde geheim S_B met Toestel B. Aangesien S_A ≠ S_B, bereken die toestelle verskillende verifikasienommers.

Die aanvaller kan nie die nommers laat ooreenstem nie, want:

Die gebruiker sien verskillende nommers op die skerms en kanselleer die koppeling. Op daardie punt het die aanval sigbaar geword.

Stap 5: Voltooi die koppeling

Eers nadat die gebruiker verifikasie op albei toestelle bevestig het, word die koppeling voltooi:

  1. ’n 64-karakter-koppelingsleutel (256 bisse) word uit die gedeelde geheim afgelei: SHA-256("pair:" + sharedSecret) → pairingKey
  2. Die dokumentsleutel word afgelei as SHA-256("doc:" + pairingKey) en dien as die Firestore-dokumentsleutel
  3. Die enkripsiesleutel word afgelei as SHA-256("enc:" + pairingKey) en verskaf die AES-256-GCM-sleutel vir geënkripteerde seinboodskappe
  4. Albei toestelle stoor dieselfde koppelingsleutel en gaan na moduskeuse

Van hierdie punt af word alle verdere verbindingspogings (Firestore-seinboodskappe, WebRTC-opstelling) met die gedeelde AES-256-GCM-sleutel geënkripteer. Die koppelingsleutel word nooit na die backend gestuur nie; slegs die SHA-256-hutswaarde daarvan word as die dokumentidentifiseerder gebruik.

Stelselargitektuur

Die volgende diagram wys die komponente wat by koppeling en kommunikasie betrokke is:

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

Oorsig van die stelselargitektuur — wysigbare bron: docs/diagrams/pairing-architecture.mmd

Kommunikasiepaaie in detail:

Terugval: Handmatige kode-invoer

As Bluetooth nie beskikbaar is nie (bv. op ouer toestelle), kan die 4-karakter-kode ook met die hand ingetik word. Handmatige invoer gebruik dieselfde ECDH-sleuteluitruiling en dieselfde SAS-verifikasie as outomatiese koppeling. Die enigste verskil is dat die gebruiker die kode lees en intik, eerder as dat dit deur BLE ontdek word.

Omdat die ECDH-sleuteluitruiling in albei gevalle oor Firebase plaasvind, is die sekuriteit identies. Die 4-karakter-kode is net ’n ontmoetingspunt; die werklike enkripsie berus op die 256-bis-sleutel wat uit ECDH afgelei word.

Opsomming

Sekuriteitsmeganisme Beskerm teen
ECDH-sleuteluitruiling (P-256) Afluister van sleuteluitruilverkeer
Tydelike sleutelpare Voorwaartse geheimhouding — vorige koppelings bly veilig
Visuele verifikasienommer (SAS) Man-in-the-middle (MITM) tydens sleuteluitruiling
SHA-256-hutswaarde as dokumentsleutel Kode-onttrekking uit Firestore
AES-256-GCM-enkripsie Afluister van seinboodskapdata
Bevestiging aan albei kante Eensydige koppeling sonder die gebruiker se medewete
DTLS-SRTP (WebRTC) Afluister van klank/video

Hierdie lae pas saam: ECDH beskerm die sleuteluitruiling, die verifikasienommer beskerm teen MITM, AES-256-GCM beskerm seinboodskappe en WebRTC beskerm media. ’n Aanvaller sou hierdie ketting op verskeie plekke moes breek sonder dat die toestelle of ouers dit agterkom.


Meer artikels