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):
- Gako pribatu bat — gailuan bakarrik geratzen da
- Gako publiko bat — Firebase bidez trukatzen da
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:
- Firestore dokumentuan gordetako benetako gako publikoak bereekin ordezkatu
- 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:
- Ez ditu gailuen gako pribatuak ezagutzen
- SHA-256 ez da alderantzizkagarria
- Ausaz bat etortzeko probabilitatea 100etik 1
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:
- Ondoren, 64 karaktereko parekatze-gako bat (256 bit) eratorriko da sekretu partekatutik:
SHA-256("pair:" + sharedSecret) → pairingKey - Dokumentu-gakoa honela eratorriko da:
SHA-256("doc:" + pairingKey)eta Firestore dokumentuaren gako gisa erabiltzen da - Zifratze-gakoa honela eratorriko da:
SHA-256("enc:" + pairingKey)eta zifratutako seinaleztapenerako AES-256-GCM gakoa eskaintzen du - 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:
- WebRTC puntutik puntura (lerro lodia): audioa, bideoa eta DataChannel zuzenean doaz gailuen artean — DTLS-SRTP bidez zifratuta. Zerbitzari batek ere ez ditu datu horiek ikusten.
- Firebase Firestore (lerro jarraitua): parekatze-datuak (ECDH gakoak) eta seinaleztapena (SDP/ICE) Firestore bidez doaz — muturretik muturrera AES-256-GCM bidez zifratuta. Firebasek ezin ditu datuak deszifratu.
- STUN zerbitzaria: bi gailuek beren IP helbide publikoa aurkitzen dute, zuzeneko puntutik punturako konexioa ezarri ahal izateko.
- TURN errelea: zuzeneko konexioa posible ez bada (adibidez, datu mugikorrekin), aukeratutako tokiko TURN zerbitzariak edo Cloudflareren TURN zerbitzariak zifratutako multimedia-trafikoa birbidaltzen du. Iraupen laburreko kredentzialak (24 h) eskuratzen dira Firebase Cloud Functions bidez.
- Bluetooth LE (lerro puntukatua): Nearby Connections-ek hurbileko gailuak automatikoki aurkitzen ditu — elkarguneko kodea bakarrik transmititzen da, ez gako-materialik.
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.