Блог

Baby Monitor Timmy-дегі қауіпсіз жұптастыру

ECDH, SAS нөмірі және шифрланған сигнал алмасу қалай бірге жұмыс істейді.

Baby Monitor Timmy аудио мен бейнені жібермес бұрын, екі құрылғы бір-бірін тауып, бір-біріне сенуі керек. Бұл жұптастыру кезеңі — бүкіл үдерістегі ең маңызды сәт. Мұнда Timmy қалай жұптасатынын, оның артында қандай криптография барын және жақын маңдағы шабуылдаушының қосылымды байқатпай басқара алмайтынын түсіндіремін.

Мәселе: менің құрылғым кіммен сөйлесіп тұрғанын қайдан біледі?

Екі құрылғы алғаш рет қосылғанда, басты сұрақ мынау: А құрылғысы шынымен B құрылғысымен сөйлесіп тұр ма, әлде арада біреу бар ма? Криптографияда бұл ортадағы адам шабуылы (MITM) деп аталады.

Timmy бұл мәселені эллиптикалық қисықтардағы Диффи — Хеллман (ECDH) кілт алмасуын Firebase арқылы жүргізіп, оны пайдаланушының көзбен тексеруімен үйлестіру арқылы шешеді..

Төмендегі диаграмма жұптастырудың толық барысын бірден көрсетеді:

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
      

Толық жұптастыру хаттамасының тізбегі — өңдеуге болатын бастапқы файл: docs/diagrams/pairing-sequence.mmd

1-қадам: Әр құрылғы кілттер жұбын жасайды

Жұптастыру экранын ашқанда, әр құрылғы уақытша ECDH кілттер жұбын P-256 (secp256r1) қисығында жасайды:

Кілттер криптографиялық тұрғыдан қауіпсіз кездейсоқ сандар генераторының көмегімен жасалады (Random.secure()) және олар тек осы бір жұптастыру әрекеті үшін жарамды. Әр жаңа әрекет үшін жаңа кілттер жасалады.

2-қадам: Ашық кілттерді Firebase арқылы алмасу

Екі құрылғының бір-бірін табуы үшін Timmy 4 таңбалы кодты кездесу нүктесі ретінде пайдаланады. Бұл кодты Nearby Connections (Bluetooth Low Energy) арқылы автоматты түрде табуға немесе қолмен енгізуге болады. Оның криптографиялық мәні жоқ; ол тек екі құрылғының бір Firebase Firestore құжатын табуына көмектеседі.

Екі құрылғы да кодты білген соң, әрқайсысы өзінің ашық ECDH кілтін ортақ Firestore құжатынажазады. Содан кейін әр құрылғы сол құжаттан екіншісінің ашық кілтін оқиды.

Ең маңыздысы: тек ашық кілт жіберіледі. Жеке кілт құрылғыдан ешқашан шықпайды. Firebase трафигін бақылаған адам ашық кілттерді көреді, бірақ олардан ортақ құпияны есептей алмайды . Бұл эллиптикалық қисықтағы дискретті логарифм мәселесінің (ECDLP) күрделілігіне негізделген.

3-қадам: Ортақ құпияны есептеу

Екі құрылғы бір-бірінің ашық кілтін тапқаннан кейін, олар бірдей ортақ құпияны:

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

өздігінен есептейді. Эллиптикалық қисықтар математикасы екі есептеу де бірдей нәтиже беретінін қамтамасыз етеді, әр құрылғы тек өзінің жеке кілтін және екіншісінің ашық кілтін білсе де.

4-қадам: Тексеру нөмірі (SAS)

Ортақ құпиядан қысқа аутентификация жолы (SAS) алынады — екі құрылғыда да көрсетілетін екі таңбалы нөмір:

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

Екі құрылғыда бірдей нөмір көрсетіледі — мысалы, 42. Пайдаланушы екі экрандағы нөмірлердің сәйкес келетінін көзбен салыстырып, кейін әрбір құрылғыда жеке-жеке растайды.

Шабуылдаушы мұны неге қолдан жасай алмайды

Ортадағы адам шабуылын жасаушы Firebase-тегі кілт алмасуына араласуы керек. Нақты айтқанда, ол:

  1. Firestore құжатында сақталған шынайы ашық кілттерді өз кілттерімен ауыстыруы
  2. Әр құрылғымен бөлек ортақ құпия орнатуы
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)
      

SAS мәндерінің сәйкес келмеуі арқылы ортадағы адам шабуылын анықтау — өңдеуге болатын бастапқы файл: docs/diagrams/mitm-detection.mmd

Бұл жағдайда шабуылдаушы бір ортақ құпияны S_A A құрылғысымен, ал басқа ортақ құпияны S_B B құрылғысымен есептейді. Себебі S_A ≠ S_B, құрылғылар әртүрлі тексеру нөмірлерін.

есептейді. Шабуылдаушы нөмірлерді бірдей ете алмайды, өйткені:

Пайдаланушы экрандарда әртүрлі нөмірлерді көріп, жұптастырудан бас тартады. Сол сәтте шабуыл байқалады.

5-қадам: Жұптастыруды аяқтау

Пайдаланушы екі құрылғыда да тексеруді растағаннан кейін ғана жұптастыру аяқталады:

  1. Ортақ құпиядан 64 таңбалы жұптастыру кілті (256 бит) ортақ құпиядан алынады: SHA-256("pair:" + sharedSecret) → pairingKey
  2. Құжат кілті мынадан алынады: SHA-256("doc:" + pairingKey) және Firestore құжатының кілті ретінде қызмет етеді
  3. Шифрлау кілті мынадан алынады: SHA-256("enc:" + pairingKey) және шифрланған сигнал алмасуға арналған AES-256-GCM кілтін береді
  4. Екі құрылғы да бірдей жұптастыру кілтін сақтап, режим таңдау бетіне өтеді

Осы сәттен бастап барлық келесі қосылу әрекеттері (Firestore сигнал алмасуы, WebRTC орнатуы) ортақ AES-256-GCM кілтімен шифрланады. Жұптастыру кілті ешқашан серверлік бөлікке жіберілмейді; құжат идентификаторы ретінде тек оның SHA-256 хэші қолданылады.

Жүйе архитектурасы

Төмендегі диаграмма жұптастыру мен байланысқа қатысатын құрамдастарды көрсетеді:

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

Жүйе архитектурасына шолу — өңдеуге болатын бастапқы файл: docs/diagrams/pairing-architecture.mmd

Байланыс жолдары егжей-тегжейлі:

Балама: кодты қолмен енгізу

Bluetooth қолжетімсіз болса (мысалы, ескі құрылғыларда), 4 таңбалы кодты қолмен де теруге болады. Қолмен енгізуде автоматты жұптастырудағыдай ECDH кілт алмасуы мен SAS тексеруі қолданылады . Жалғыз айырмашылығы: код BLE арқылы табылмайды, оны пайдаланушы оқып, өзі тереді.

ECDH кілт алмасуы екі жағдайда да Firebase арқылы өтетіндіктен, қауіпсіздік деңгейі бірдей. 4 таңбалы код — тек кездесу нүктесі; нақты шифрлау ECDH-ден алынған 256 биттік кілтке негізделген.

Қорытынды

Қауіпсіздік механизмі Мыналардан қорғайды
ECDH кілт алмасуы (P-256) Кілт алмасу трафигін тыңдау
Уақытша кілттер жұбы Алға бағытталған құпиялылық — бұрынғы жұптастырулар қауіпсіз болып қалады
Көзбен тексерілетін нөмір (SAS) Кілт алмасу кезіндегі ортадағы адам шабуылы (MITM)
Құжат кілті ретінде SHA-256 хэші Firestore-дан кодты шығарып алу
AES-256-GCM шифрлауы Сигнал алмасу деректерін тыңдау
Екі жақтың растауы Пайдаланушы білмейтін біржақты жұптастыру
DTLS-SRTP (WebRTC) Аудио/бейнені тыңдау

Бұл қабаттар бір-бірін толықтырады: ECDH кілт алмасуды, тексеру нөмірі MITM-нен, AES-256-GCM сигнал алмасуды, ал WebRTC медианы қорғайды. Шабуылдаушы құрылғылар не ата-аналар байқамайтындай бұл тізбекті бірнеше жерден бұзуы керек болар еді.


Тағы мақалалар