Bloga

Baby Monitor Timmy aplikazioko parekatze segurua

Nola elkarlotzen diren ECDH, SAS zenbakia eta zifratutako seinaleztapena.

Baby Monitor Timmy aplikazioak audioa eta bideoa transmititu aurretik, bi gailuek elkar aurkitu eta elkarrengan konfiantza izan behar dute. Parekatze-urrats hori prozesu osoko unerik kritikoena da. Hemen azaltzen dut Timmyk gailuak nola parekatzen dituen, zer kriptografia dagoen horren atzean eta zergatik hurbileko erasotzaile batek ezin duen konexioa inor ohartu gabe bereganatu.

Arazoa: nola daki nire gailuak norekin ari den hitz egiten?

Bi gailu lehen aldiz konektatzen direnean, galdera nagusia hau da: A gailua benetan B gailuarekin ari da hitz egiten, ala norbait tartean dago? Kriptografian, horri tarteko erasoa (MITM) esaten zaio.

Timmyk hau kurba eliptikoko Diffie-Hellman (ECDH) gako-truke baten bidez konpontzen du Firebase erabilita, eta erabiltzailearen ikusizko egiaztapenarekin.

Hurrengo diagramak parekatze-prozesu osoa erakusten du begiratu hutsean:

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
      

Parekatze-protokoloaren sekuentzia osoa — iturri editagarria: docs/diagrams/pairing-sequence.mmd

1. urratsa: gailu bakoitzak gako-pare bat sortzen du

Parekatze-pantaila irekitzean, gailu bakoitzak aldi baterako ECDH gako-pare bat sortzen du P-256 kurban (secp256r1):

Gakoak kriptografikoki segurua den ausazko zenbaki-sorgailu baten bidez sortzen dira (Random.secure()) eta parekatze-saio bakar honetarako baino ez dira baliozkoak. Saiakera berri bakoitzerako gako berriak sortzen dira.

2. urratsa: gako publikoak Firebase bidez trukatzea

Bi gailuek elkar aurki dezaten, Timmyk 4 karaktereko kode bat erabiltzen du elkargune gisa. Kode hau automatikoki aurki daiteke Nearby Connections bidez (Bluetooth Low Energy), edo eskuz sar daiteke. Ez du balio kriptografikorik; bi gailuek Firebase Firestore dokumentu bera aurkitzeko baino ez du balio.

Bi gailuek kodea ezagutzen dutenean, bakoitzak bere ECDH gako publikoa partekatutako Firestore dokumentu batean idazten du. Ondoren, gailu bakoitzak beste gailuaren gako publikoa irakurtzen du dokumentu horretatik.

Funtsezkoa: gako publikoa bakarrik bidaltzen da. Gako pribatuak ez du inoiz gailua uzten. Firebaseko trafikoa ikusten duenak gako publikoak ikusten ditu, baina ezin du sekretu partekatua kalkulatu haiekin. Horren oinarria kurba eliptikoaren logaritmo diskretuaren arazoaren (ECDLP) zailtasuna da.

3. urratsa: sekretu partekatua kalkulatzea

Bi gailuek elkarren gako publikoa aurkitu ondoren, modu independentean kalkulatzen dute sekretu partekatu:

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

bera. Kurba eliptikoen matematikak bermatzen du bi kalkuluek emaitza bera ematea, nahiz eta gailu bakoitzak bere gako pribatua eta bestearen gako publikoa baino ez ezagutu.

4. urratsa: egiaztapen-zenbakia (SAS)

Sekretu partekatutik autentifikazio-kate labur (SAS) bat eratortzen da — bi gailuetan erakusten den bi digituko zenbakia:

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

Bi gailuek zenbaki bera erakusten dute — adibidez, 42. Erabiltzaileak pantaila bietako zenbakiak bat datozen begiratzen du, eta ondoren gailu bakoitzean banaka baieztatzen du.

Zergatik ezin duen erasotzaileak hau faltsutu

Tarteko erasotzaile batek Firebaseko gako-trukea atzeman beharko luke. Zehazki, honako hau egin beharko luke:

  1. Firestore dokumentuan gordetako benetako gako publikoak bereekin ordezkatu
  2. Gailu bakoitzarekin sekretu partekatu bereiziak ezarri
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)
      

Tarteko erasoa SAS zenbakien desadostasunaren bidez hautematea — iturri editagarria: docs/diagrams/mitm-detection.mmd

Kasu honetan, erasotzaileak sekretu partekatu bat kalkulatzen du S_A A gailuarekin eta beste sekretu partekatu bat S_B B gailuarekin. S_A ≠ S_BOndorioz, gailuek egiaztapen-zenbaki desberdinak.

kalkulatzen dituzte. Erasotzaileak ezin ditu zenbakiak berdin egin, zeren:

baino ez da. Erabiltzaileak zenbaki desberdinak ikusten ditu bi pantailetan eta parekatzea ezeztatzen du. Une horretan, erasoa agerian geratu da.

5. urratsa: parekatzea osatzea

Erabiltzaileak egiaztapena bi gailuetan baieztatu ondoren bakarrik osatzen da parekatzea:

  1. Ondoren, 64 karaktereko parekatze-gako bat (256 bit) eratorriko da sekretu partekatutik: SHA-256("pair:" + sharedSecret) → pairingKey
  2. Dokumentu-gakoa honela eratorriko da: SHA-256("doc:" + pairingKey) eta Firestore dokumentuaren gako gisa erabiltzen da
  3. Zifratze-gakoa honela eratorriko da: SHA-256("enc:" + pairingKey) eta zifratutako seinaleztapenerako AES-256-GCM gakoa eskaintzen du
  4. Bi gailuek parekatze-gako bera gordetzen dute eta modua aukeratzeko pantailara joaten dira

Une honetatik aurrera, hurrengo konexio-saiakera guztiak (Firestore seinaleztapena, WebRTC konfigurazioa) partekatutako AES-256-GCM gakoarekin zifratuta daude. Parekatze-gakoa ez da inoiz backend-era bidaltzen; bere SHA-256 hasha baino ez da erabiltzen dokumentuaren identifikatzaile gisa.

Sistemaren arkitektura

Hurrengo diagramak parekatzean eta komunikazioan parte hartzen duten osagaiak erakusten ditu:

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

Sistemaren arkitekturaren ikuspegi orokorra — iturri editagarria: docs/diagrams/pairing-architecture.mmd

Komunikazio-bideak zehatz-mehatz:

Ordezko aukera: kodea eskuz sartzea

Bluetootha erabilgarri ez badago (adibidez, gailu zaharragoetan), 4 karaktereko kodea eskuz ere idatz daiteke. Eskuz sartzeak ECDH gako-truke bera eta SAS egiaztapen bera erabiltzen ditu, parekatze automatikoan bezala. Alde bakarra da erabiltzaileak kodea irakurri eta idazten duela, BLE bidez aurkitu beharrean.

ECDH gako-trukea bi kasuetan Firebase bidez egiten denez, segurtasuna berdina da. 4 karaktereko kodea elkargune bat baino ez da; benetako zifratzea ECDHtik eratorritako 256 biteko gakoan oinarritzen da.

Laburpena

Segurtasun-mekanismoa Zeren aurka babesten duen
ECDH gako-trukea (P-256) Gako-trukeko trafikoa atzematea
Aldi baterako gako-pareak Aurreranzko sekretutasuna — aurreko parekatzeek babestuta jarraitzen dute
Ikusizko egiaztapen-zenbakia (SAS) Tarteko erasoa (MITM) gako-trukean
SHA-256 hasha dokumentu-gako gisa Firestoretik kodea ateratzea
AES-256-GCM zifratzea Seinaleztapen-datuak atzematea
Bi aldeetako baieztapena Erabiltzaileak jakin gabe alde bakarreko parekatzea
DTLS-SRTP (WebRTC) Audioa eta bideoa atzematea

Geruza hauek elkar osatzen dute: ECDHk gako-trukea babesten du, egiaztapen-zenbakiak MITM erasoen aurka babesten du, AES-256-GCMk seinaleztapena babesten du, eta WebRTCk multimedia babesten du. Erasotzaile batek kate hau hainbat tokitan hautsi beharko luke, gailuek edo gurasoek ohartu gabe.


Artikulu gehiago