Para se Baby Monitor Timmy të transmetojë audio dhe video, dy pajisje duhet të gjejnë dhe t'i besojnë njëra-tjetrës. Ky çiftim është hapi më kritik në të gjithë procesin. Këtu shpjegoj se si çiftohet Timmy, çfarë kriptografie përdor dhe pse një sulmues në afërsi nuk mund ta marrë nën kontroll lidhjen pa u vënë re.
Problemi: Si e di pajisja ime me kë po komunikon?
Kur dy pajisje lidhen për herë të parë, pyetja kryesore është: a po komunikon vërtet Pajisja A me Pajisjen B, apo dikush ndodhet në mes? Në kriptografi, kjo quhet një sulm i ndërmjetësit (MITM).
Timmy e zgjidh këtë duke përdorur një shkëmbim çelësash Elliptic Curve Diffie-Hellman (ECDH) përmes Firebase, të kombinuar me verifikim vizual nga përdoruesi.
Diagrami i mëposhtëm paraqet me një vështrim të gjithë procesin e çiftimit:
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
Rrjedha e plotë e protokollit të çiftimit — burimi i redaktueshëm: docs/diagrams/pairing-sequence.mmd
Hapi 1: Çdo pajisje krijon një çift çelësash
Kur hapet ekrani i çiftimit, secila pajisje krijon një çift të përkohshëm çelësash ECDH në kurbën P-256 (secp256r1):
- Një çelës privat — qëndron vetëm në pajisje
- Një çelës publik — shkëmbehet përmes Firebase
Çelësat krijohen duke përdorur një gjenerues numrash të rastësishëm të sigurt kriptografikisht (Random.secure())
dhe janë të vlefshëm vetëm për këtë përpjekje të vetme çiftimi. Çelësa të rinj krijohen
për çdo përpjekje të re.
Hapi 2: Shkëmbimi i çelësave publikë përmes Firebase
Që dy pajisje të gjejnë njëra-tjetrën, Timmy përdor një kod me 4 karaktere si pikë takimi. Ky kod mund të zbulohet automatikisht përmes Nearby Connections (Bluetooth Low Energy) ose të futet manualisht. Ai nuk ka asnjë vlerë kriptografike; thjesht bën që të dy pajisjet të gjejnë të njëjtin dokument Firebase Firestore.
Sapo të dy pajisjet e dinë kodin, secila shkruan çelësin e vet publik ECDH në një dokument të përbashkët Firestore. Më pas, secila pajisje lexon nga ai dokument çelësin publik të pajisjes tjetër.
Më e rëndësishmja: dërgohet vetëm çelësi publik . Çelësi privat nuk del kurrë nga pajisja. Kushdo që vëzhgon trafikun e Firebase sheh çelësa publikë, por nuk mund të llogarisë sekretin e përbashkët prej tyre. Kjo mbështetet te vështirësia e Problemit të Logaritmit Diskret në Kurbë Eliptike (ECDLP).
Hapi 3: Llogaritja e sekretit të përbashkët
Sapo të dy pajisjet të kenë zbuluar çelësin publik të njëra-tjetrës, ato llogarisin në mënyrë të pavarur të njëjtin sekret të përbashkët:
sharedSecret = ECDH(myPrivateKey, remotePublicKey)
→ 32 bytes (identical on both devices)
Matematika e kurbave eliptike garanton që të dy llogaritjet japin të njëjtin rezultat, edhe pse secila pajisje njeh vetëm çelësin e vet privat dhe çelësin publik të tjetrës.
Hapi 4: Numri i verifikimit (SAS)
Nga sekreti i përbashkët nxirret një Varg i shkurtër vërtetimi (SAS) — një numër dyshifror që shfaqet në të dy pajisjet:
hash = SHA-256("sas:" + sort(pubkeyA, pubkeyB) + sharedSecret)
number = (hash[0] × 256 + hash[1]) mod 100 → 00 to 99
Të dy pajisjet shfaqin të njëjtin numër — për shembull, 42. Përdoruesi krahason me sy nëse numrat në të dy ekranet përputhen, pastaj e konfirmon në secilën pajisje veçmas.
Pse një sulmues nuk mund ta falsifikojë këtë
Një sulmues që kryen një sulm të ndërmjetësit do të duhej të përgjonte shkëmbimin e çelësave në Firebase. Konkretisht, do të duhej të:
- Zëvendësonte çelësat publikë të vërtetë të ruajtur në dokumentin Firestore me të vetët
- Krijonte sekrete të përbashkëta të ndara me secilën pajisje
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)
Zbulimi i sulmit të ndërmjetësit përmes mospërputhjes së SAS-it — burimi i redaktueshëm: docs/diagrams/mitm-detection.mmd
Në këtë rast, sulmuesi llogarit një sekret të përbashkët S_A me Pajisjen A dhe një
sekret të përbashkët të ndryshëm S_B me Pajisjen B. Meqë S_A ≠ S_B,
pajisjet llogarisin numra të ndryshëm verifikimi.
Sulmuesi nuk mund t'i bëjë numrat të përputhen, sepse:
- Nuk i njeh çelësat privatë të pajisjeve
- SHA-256 nuk është i kthyeshëm
- Probabiliteti i një përputhjeje rastësore është vetëm 1 në 100
Përdoruesi sheh numra të ndryshëm në ekrane dhe e anulon çiftimin. Në atë çast, sulmi bëhet i dukshëm.
Hapi 5: Përfundimi i çiftimit
Vetëm pasi përdoruesi të ketë konfirmuar verifikimin në të dy pajisjet përfundon çiftimi:
- Një çelës çiftimi me 64 karaktere (256 bit) nxirret nga sekreti i përbashkët:
SHA-256("pair:" + sharedSecret) → pairingKey - Çelësi i dokumentit nxirret si
SHA-256("doc:" + pairingKey)dhe shërben si çelësi i dokumentit Firestore - Çelësi i enkriptimit nxirret si
SHA-256("enc:" + pairingKey)dhe siguron çelësin AES-256-GCM për sinjalizim të enkriptuar - Të dyja pajisjet ruajnë të njëjtin çelës çiftimi dhe kalojnë te zgjedhja e mënyrës
Që nga kjo pikë, të gjitha përpjekjet e mëtejshme të lidhjes (sinjalizimi Firestore, konfigurimi WebRTC) enkriptohen me çelësin e përbashkët AES-256-GCM. Çelësi i çiftëzimit nuk dërgohet kurrë në sistemin backend; vetëm hashi i tij SHA-256 përdoret si identifikues i dokumentit.
Arkitektura e sistemit
Diagrami i mëposhtëm tregon përbërësit e përfshirë në çiftim dhe komunikim:
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
Përmbledhje e arkitekturës së sistemit — burimi i redaktueshëm: docs/diagrams/pairing-architecture.mmd
Rrugët e komunikimit në detaje:
- WebRTC nga një pajisje te tjetra (vijë e trashë): Audioja, videoja dhe DataChannel kalojnë drejtpërdrejt ndërmjet pajisjeve — të enkriptuara me DTLS-SRTP. Asnjë server nuk i sheh këto të dhëna.
- Firebase Firestore (vijë e plotë): Të dhënat e çiftimit (çelësat ECDH) dhe sinjalizimi (SDP/ICE) kalojnë përmes Firestore — të kriptuara nga skaji në skaj me AES-256-GCM. Firebase nuk mund t'i dekriptojë të dhënat.
- Serveri STUN: Të dy pajisjet zbulojnë adresën e tyre publike IP, që të mund të krijohet një lidhje e drejtpërdrejtë nga një pajisje te tjetra.
- Releja TURN: Nëse një lidhje e drejtpërdrejtë nuk është e mundur (p.sh. në rrjetin celular), serveri TURN lokal i zgjedhur ose ai i Cloudflare transmeton mediat e enkriptuara. Kredencialet jetëshkurtra (24 orë) merren përmes Firebase Cloud Functions.
- Bluetooth LE (vijë me pika): Nearby Connections zbulon automatikisht pajisjet pranë — transmetohet vetëm kodi i takimit, pa material çelësash.
Alternativë: Futja manuale e kodit
Nëse Bluetooth nuk është i disponueshëm (p.sh. në pajisje më të vjetra), kodi me 4 karaktere mund të shkruhet edhe manualisht. Futja manuale përdor të njëjtin shkëmbim çelësash ECDH dhe të njëjtin verifikim SAS si çiftimi automatik. I vetmi ndryshim është se kodi lexohet dhe shkruhet nga përdoruesi, në vend që të zbulohet përmes BLE.
Meqë shkëmbimi i çelësave ECDH bëhet përmes Firebase në të dy rastet, siguria është identike. Kodi me 4 karaktere është vetëm pikë takimi; enkriptimi i vërtetë bazohet në çelësin 256-bit të nxjerrë nga ECDH.
Përmbledhje
| Mekanizmi i sigurisë | Mbron kundër |
|---|---|
| Shkëmbimi i çelësave ECDH (P-256) | Përgjimit të trafikut të shkëmbimit të çelësave |
| Çifte të përkohshme çelësash | Fshehtësi e përparme — çiftimet e mëparshme mbeten të sigurta |
| Numër verifikimi vizual (SAS) | Sulmit të ndërmjetësit (MITM) gjatë shkëmbimit të çelësave |
| Hashi SHA-256 si çelës dokumenti | Nxjerrjes së kodit nga Firestore |
| Enkriptimi AES-256-GCM | Përgjimit të të dhënave të sinjalizimit |
| Konfirmim nga të dyja palët | Çiftimit të njëanshëm pa dijeninë e përdoruesit |
| DTLS-SRTP (WebRTC) | Përgjimit të audios/videos |
Këto shtresa plotësojnë njëra-tjetrën: ECDH mbron shkëmbimin e çelësave, numri i verifikimit mbron nga sulmet e ndërmjetësit, AES-256-GCM mbron sinjalizimin dhe WebRTC mbron mediat. Një sulmues do të duhej ta thyente këtë zinxhir në disa pika pa u vënë re nga pajisjet ose prindërit.