ბლოგი

უსაფრთხო დაწყვილება Baby Monitor Timmy-ში

როგორ მუშაობენ ერთად ECDH, SAS-ის ნომერი და დაშიფრული სიგნალების გაცვლა.

სანამ Baby Monitor Timmy აუდიოსა და ვიდეოს გადასცემს, ორმა მოწყობილობამ უნდა იპოვოს ერთმანეთი და ენდოს ერთმანეთს. ეს დაწყვილების ეტაპი მთელი პროცესის ყველაზე კრიტიკული მომენტია. აქ ავხსნი, როგორ აწყვილებს Timmy მოწყობილობებს, რა კრიპტოგრაფია უდევს ამას საფუძვლად და რატომ ვერ ჩაიგდებს ახლომყოფი თავდამსხმელი კავშირს შეუმჩნევლად ხელში.

პრობლემა: საიდან იცის ჩემმა მოწყობილობამ, ვის ესაუბრება?

როდესაც ორი მოწყობილობა პირველად უკავშირდება ერთმანეთს, მთავარი კითხვაა: მართლა B მოწყობილობას ესაუბრება A მოწყობილობა თუ ვიღაც შუაშია? კრიპტოგრაფიაში ამას ეწოდება შუამავალის შეტევა (MITM).

Timmy ამას აგვარებს ელიფსური მრუდის Diffie-Hellman (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 — მედიას. თავდამსხმელს ამ ჯაჭვის რამდენიმე რგოლის გარღვევა მოუწევდა ისე, რომ მოწყობილობებს ან მშობლებს ეს ვერ შეემჩნიათ.


სხვა სტატიები