Blog

Çiftim i sigurt në Baby Monitor Timmy

Si bashkëpunojnë ECDH, numri SAS dhe sinjalizimi i enkriptuar.

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):

Ç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ë:

  1. Zëvendësonte çelësat publikë të vërtetë të ruajtur në dokumentin Firestore me të vetët
  2. 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:

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:

  1. Një çelës çiftimi me 64 karaktere (256 bit) nxirret nga sekreti i përbashkët: SHA-256("pair:" + sharedSecret) → pairingKey
  2. Çelësi i dokumentit nxirret si SHA-256("doc:" + pairingKey) dhe shërben si çelësi i dokumentit Firestore
  3. Çelësi i enkriptimit nxirret si SHA-256("enc:" + pairingKey) dhe siguron çelësin AES-256-GCM për sinjalizim të enkriptuar
  4. 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:

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.


Më shumë artikuj