Blog

Associaziun segira en Baby Monitor Timmy

Co ECDH, il numer SAS e la signalaziun criptada funcziunan ensemen.

Avant che Baby Monitor Timmy transmettia audio e video, ston dus apparats sa chattar e sa fidar in da l'auter. Quest pass d'associaziun è il mument il pli critic en l'entir process. Qua decler jau co Timmy associescha apparats, tge criptografia ch'è davosvart e pertge in attatgader vischin na po betg surpigliar la colliaziun senza vegnir remartgà.

Il problem: Co sa mes apparat cun tgi ch'el communitgescha?

Cur che dus apparats sa collian per l'emprima giada, è la dumonda centrala: communitgescha l'apparat A propi cun l'apparat B, u sa chatta insatgi tranteren? En criptografia vegn quai numnà in' attatga man-in-the-middle (MITM).

Timmy schlia quai cun in barat da clavs Elliptic Curve Diffie-Hellman (ECDH) sur Firebase, cumbinà cun ina verificaziun visuala tras l'utilisader..

Il diagram suandant mussa l'entir process d'associaziun a prima vista:

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
      

Sequenza cumpletta dal protocol d'associaziun — funtauna editabla: docs/diagrams/pairing-sequence.mmd

Pass 1: Mintga apparat generescha in pèr da clavs

Cun avrir la pagina d'associaziun generescha mintga apparat in pèr da clavs ECDH temporaras sin la curva P-256 (secp256r1):

Las clavs vegnan creadas cun in generatur da numers casuals criptograficamain segir (Random.secure()) ed èn valaivlas mo per questa singula emprova d'associaziun. Per mintga nova emprova vegnan generadas clavs novas.

Pass 2: Barattar las clavs publicas sur Firebase

Per che dus apparats possian sa chattar, utilisescha Timmy in code da 4 caracters cuntegn sco punct da scuntrada. Quest code po vegnir chattà automaticamain via Nearby Connections (Bluetooth Low Energy) u endatà manualmain. El ha nagina valur criptografica; el gida mo omadus apparats a chattar il medem document Firebase Firestore.

Uschespert che omadus apparats enconuschan il code, scriva mintgin sia clav ECDH publica en in document Firestore communabel. Suenter legia mintga apparat la clav publica da l'auter apparat da quest document.

Impurtant: mo la clav publica vegn tramessa. La clav privata na banduna mai l'apparat. Tgi che observescha il traffic da Firebase vesa las clavs publicas, ma na po betg calcular il secret communabel a partir da quellas. Quai sa basa sin la difficultad dal problem dal logaritmus discret sin curvas ellipticas (ECDLP).

Pass 3: Calcular il secret communabel

Uschespert che omadus apparats han survegnì la clav publica in da l'auter, calculescha mintgin independentamain il medem secret communabel.:

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

La matematica da las curvas ellipticas garantescha che omaduas calculaziuns dattan il medem resultat, era sche mintga apparat enconuscha mo sia atgna clav privata e la clav publica da l'auter.

Pass 4: Il numer da verificaziun (SAS)

A partir dal secret communabel vegn derivada ina Short Authentication String (SAS) — in numer da duas cifras mussà sin omadus apparats:

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

Omadus apparats mussan il medem numer — per exempel, 42. L'utilisader cumpareglia ils numers sin omadus visurs e, sch'els correspundan, conferma sin mintga apparat separadamain.

Co la cumparegliaziun SAS gida a scuvrir in'attatga

In attatgader man-in-the-middle stuess interceptar il barat da clavs en Firebase. Concretamain stuess el:

  1. Remplazzar las clavs publicas originalas memorisadas en il document Firestore cun sias atgnas
  2. Stabilir secrets communabels separads cun mintga apparat
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)
      

Identificaziun d'ina attatga man-in-the-middle tras numers SAS differents — funtauna editabla: docs/diagrams/mitm-detection.mmd

En quest cas calculescha l'attatgader in secret communabel S_A cun l'apparat A ed in auter secret communabel S_B cun l'apparat B. Perquai che S_A ≠ S_B, calculeschan ils apparats numers da verificaziun differents..

L'attatgader na po betg controllar ils numers cun segirezza, perquai che:

Normalmain vesa l'utilisader numers differents sin ils visurs ed interrumpa l'associaziun. Ina concordanza casuala è dentant pussaivla en 1 da 100 emprovas, ed emprovas repetidas augmentan il ristg. Perquai èsi impurtant da cumparegliar ils numers sin omadus apparats e d'interrumper l'associaziun tar mintga differenza.

Pass 5: Cumplettar l'associaziun

Pir suenter che l'utilisader ha confermà la verificaziun sin omadus apparats vegn l'associaziun terminada:

  1. Ina clav d'associaziun da 64 caracters (256 bits) vegn derivada dal secret communabel: SHA-256("pair:" + sharedSecret) → pairingKey
  2. La clav dal document vegn derivada sco SHA-256("doc:" + pairingKey) e serva sco clav dal document Firestore
  3. La clav da criptaziun vegn derivada sco SHA-256("enc:" + pairingKey) e furnescha la clav AES-256-GCM per la signalaziun criptada
  4. Omadus apparats memoriseschan la medema clav d'associaziun e midan tar la selecziun dal modus.

A partir da quest mument vegnan tut las ulteriuras emprovas da colliaziun — la signalaziun Firestore e la configuraziun WebRTC — criptadas cun la clav AES-256-GCM communabla. La clav d'associaziun na vegn mai tramessa al backend; mo ses hash SHA-256 vegn duvrà sco identifitgader dal document.

Architectura dal sistem

Il diagram suandant mussa las cumponentas che participeschan a l'associaziun ed a la communicaziun:

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

Survista da l'architectura dal sistem — funtauna editabla: docs/diagrams/pairing-architecture.mmd

Vias da communicaziun en detagl:

Alternativa: Endatar il code manualmain

Sche Bluetooth n'è betg disponibel, per exempel sin apparats pli vegls, po il code da 4 caracters era vegnir endatà manualmain. L'endataziun manuala utilisescha il medem barat da clavs ECDH e la medema verificaziun SAS sco l'associaziun automatica. L'unica differenza è che l'utilisader legia ed endatescha il code, enstagl che quel vegnia chattà tras BLE.

Perquai ch'il barat da clavs ECDH succeda sur Firebase en omadus cas, è la segirezza identica. Il code da 4 caracters è mo in punct da scuntrada; la criptaziun effectiva sa basa sin la clav da 256 bits derivada cun ECDH.

Resumaziun

Mecanissem da segirezza Protegia cunter
Barat da clavs ECDH (P-256) Spiunadi dal traffic durant il barat da clavs
Pèrs da clavs temporaras Reutilisaziun da las clavs privatas d'ina emprova d'associaziun; quai na garantescha dentant betg forward secrecy per las clavs d'associaziun memorisadas
Numer da verificaziun visual (SAS) Attatgas man-in-the-middle (MITM) durant il barat da clavs, sche l'utilisader cumpareglia omadus numers
Hash SHA-256 sco clav dal document Extragir il code da Firestore
Criptaziun AES-256-GCM Spiunadi da las datas da signalaziun
Conferma sin omadus apparats Associaziun unilaterala senza la savida da l'utilisader
DTLS-SRTP (WebRTC) Spiunadi dad audio e video

Quests nivels sa cumpletteschan: ECDH protegia il barat da clavs, il numer da verificaziun gida a scuvrir attatgas MITM, AES-256-GCM protegia la signalaziun e WebRTC protegia las medias. In attatgader stuess superar pliras da questas protecziuns senza che l'apparat u ils geniturs remartgian quai.


Dapli artitgels