Blog

Ligtas na Pagpapares sa Baby Monitor Timmy

Paano nagtutulungan ang ECDH, numero ng SAS, at naka-encrypt na signaling.

Bago magpadala ng audio at video ang Baby Monitor Timmy, kailangang mahanap at pagkatiwalaan ng dalawang device ang isa't isa. Ang pagpapares na hakbang ay ang pinakamahalagang sandali sa buong proseso. Dito ko ipapaliwanag kung paano nagpapares ang Timmy, anong cryptography ang nasa likod nito, at bakit hindi kayang agawin nang hindi napapansin ng malapit na attacker ang koneksyon.

Ang problema: Paano malalaman ng device ko kung sino ang kausap nito?

Kapag unang nagkonekta ang dalawang device, ito ang pangunahing tanong: talagang Device B ba ang kausap ng Device A, o may ibang taong nakasingit sa pagitan? Sa cryptography, tinatawag itong man-in-the-middle attack (MITM).

Nilulutas ito ng Timmy gamit ang Elliptic Curve Diffie-Hellman (ECDH) key exchange sa Firebase, na may kasamang biswal na pag-verify ng user.

Ipinapakita ng sumusunod na diagram ang buong daloy ng pagpapares sa isang tingin:

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
      

Buong pagkakasunod-sunod ng pairing protocol — nae-edit na source: docs/diagrams/pairing-sequence.mmd

Hakbang 1: Gumagawa ng key pair ang bawat device

Kapag binuksan ang pairing screen, gumagawa ang bawat device ng pansamantalang ECDH key pair sa P-256 curve (secp256r1):

Ginagawa ang mga key gamit ang cryptographically secure random number generator (Random.secure()) at valid lang para sa iisang pagtatangkang magpares. Gumagawa ng mga bagong key sa bawat bagong pagtatangka.

Hakbang 2: Magpalitan ng public key sa Firebase

Para mahanap ng dalawang device ang isa't isa, gumagamit ang Timmy ng 4-character na code bilang tagpuan. Maaaring awtomatikong matuklasan ang code na ito sa pamamagitan ng Nearby Connections (Bluetooth Low Energy) o manu-manong ilagay. Wala itong cryptographic na halaga; tinutulungan lang nitong mahanap ng dalawang device ang parehong Firebase Firestore document.

Kapag alam na ng parehong device ang code, isinusulat ng bawat isa ang public ECDH key nito sa isang pinagsasaluhang Firestore document. Pagkatapos, binabasa ng bawat device ang public key ng kabilang device mula sa document na iyon.

Mahalaga: ang public key lang ang ipinapadala. Hindi kailanman umaalis sa device ang private key. Makikita ng sinumang nagmamasid sa Firebase traffic ang mga public key, pero hindi nila makukuha ang shared secret mula rito. Nakasalalay ito sa hirap ng Elliptic Curve Discrete Logarithm Problem (ECDLP).

Hakbang 3: Pagkalkula ng shared secret

Kapag natuklasan na ng parehong device ang public key ng isa't isa, nakapag-iisa nilang kinakalkula ang parehong shared secret:

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

Ginagarantiya ng matematika ng elliptic curves na pareho ang resulta ng dalawang kalkulasyon, kahit sarili nitong private key at public key lang ng kabila ang alam ng bawat device.

Hakbang 4: Ang verification number (SAS)

Mula sa shared secret, kinukuha ang isang Short Authentication String (SAS) — isang dalawang-digit na numero na ipinapakita sa parehong device:

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

Parehong numero ang ipinapakita ng dalawang device — halimbawa, 42. Biswal na ikinukumpara ng user kung tugma ang mga numero sa dalawang screen, saka kinukumpirma sa bawat device nang magkahiwalay.

Bakit hindi ito mapeke ng attacker

Kailangang harangin ng man-in-the-middle ang key exchange sa Firebase. Partikular, kailangan nitong:

  1. Palitan ang totoong public key na naka-store sa Firestore document ng sarili nitong mga key
  2. Magtatag ng magkahiwalay na shared secret sa bawat device
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)
      

Pagtukoy ng man-in-the-middle sa pamamagitan ng hindi pagtugma ng SAS — nae-edit na source: docs/diagrams/mitm-detection.mmd

Sa kasong ito, kumukuwenta ang attacker ng shared secret S_A kasama ang Device A at ng ibang shared secret S_B kasama ang Device B. Dahil S_A ≠ S_B, kumukuwenta ang mga device ng magkaibang verification number.

Hindi mapagtutugma ng attacker ang mga numero dahil:

Makakakita ang user ng magkaibang numero sa mga screen at kakanselahin ang pagpapares. Sa puntong iyon, naging kapansin-pansin na ang pag-atake.

Hakbang 5: Pagtatapos ng pagpapares

Kapag nakumpirma na ng user ang verification sa parehong device saka lang matatapos ang pagpapares:

  1. Isang 64-character na pairing key (256 bits) ang kinukuha mula sa shared secret: SHA-256("pair:" + sharedSecret) → pairingKey
  2. Kinukuha ang document key bilang SHA-256("doc:" + pairingKey) at ginagamit bilang Firestore document key
  3. Kinukuha ang encryption key bilang SHA-256("enc:" + pairingKey) at nagbibigay ng AES-256-GCM key para sa naka-encrypt na signaling
  4. Parehong sine-save ng dalawang device ang pairing key at lilipat sa pagpili ng mode

Mula rito, lahat ng susunod na pagtatangkang kumonekta (Firestore signaling, WebRTC setup) ay naka-encrypt gamit ang pinagsasaluhang AES-256-GCM key. Ang pairing key ay hindi kailanman ipinapadala sa backend; ang SHA-256 hash lang nito ang ginagamit bilang document identifier.

Arkitektura ng system

Ipinapakita ng sumusunod na diagram ang mga component na kasangkot sa pagpapares at komunikasyon:

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

Pangkalahatang-ideya ng arkitektura ng system — nae-edit na source: docs/diagrams/pairing-architecture.mmd

Mga communication path nang detalyado:

Fallback: Manu-manong paglalagay ng code

Kung hindi available ang Bluetooth (hal., sa mga lumang device), maaari ring manu-manong i-type ang 4-character na code. Ginagamit sa manu-manong paglalagay ang parehong ECDH key exchange at parehong SAS verification gaya ng awtomatikong pagpapares. Ang kaibahan lang ay binabasa at tina-type ng user ang code sa halip na matuklasan ito sa BLE.

Dahil nangyayari ang ECDH key exchange sa Firebase sa parehong kaso, magkaparehoang seguridad. Tagpuan lang ang 4-character na code; nakabatay ang totoong encryption sa 256-bit na key na nagmula sa ECDH.

Buod

Mekanismo ng seguridad Pinoprotektahan laban sa
ECDH key exchange (P-256) Pakikinig sa key exchange traffic
Pansamantalang key pair Forward secrecy — nananatiling ligtas ang mga dating pagpapares
Biswal na verification number (SAS) Man-in-the-middle (MITM) habang nagpapalitan ng key
SHA-256 hash bilang document key Pagkuha ng code mula sa Firestore
AES-256-GCM encryption Pakikinig sa signaling data
Kumpirmasyon sa magkabilang panig Isahang panig na pagpapares nang hindi alam ng user
DTLS-SRTP (WebRTC) Pakikinig sa audio/video

Magkakaugnay ang mga layer na ito: pinoprotektahan ng ECDH ang key exchange, pinoprotektahan ng verification number laban sa MITM, pinoprotektahan ng AES-256-GCM ang signaling, at pinoprotektahan ng WebRTC ang media. Kailangang sirain ng attacker ang chain na ito sa ilang bahagi nang hindi napapansin ng mga device o magulang.


Higit pang artikulo