ບລັອກ

ການຈັບຄູ່ຢ່າງປອດໄພໃນ Baby Monitor Timmy

ECDH, ຕົວເລກ SAS ແລະການສົ່ງສັນຍານທີ່ເຂົ້າລະຫັດເຮັດວຽກຮ່ວມກັນແນວໃດ.

ກ່ອນທີ່ Baby Monitor Timmy ຈະສົ່ງສຽງ ແລະວິດີໂອ, ອຸປະກອນສອງເຄື່ອງຕ້ອງຊອກຫາກັນ ແລະໄວ້ໃຈກັນ. ຂັ້ນຕອນ ການຈັບຄູ່ ນີ້ແມ່ນຊ່ວງເວລາສຳຄັນທີ່ສຸດໃນຂັ້ນຕອນທັງໝົດ. ທີ່ນີ້ຂ້ອຍອະທິບາຍວ່າ Timmy ຈັບຄູ່ແນວໃດ, ມີການເຂົ້າລະຫັດໃດຢູ່ເບື້ອງຫຼັງ ແລະເປັນຫຍັງຜູ້ໂຈມຕີທີ່ຢູ່ໃກ້ຈຶ່ງບໍ່ສາມາດຍຶດການເຊື່ອມຕໍ່ໄດ້ໂດຍບໍ່ຖືກສັງເກດ.

ບັນຫາ: ອຸປະກອນຂອງຂ້ອຍຮູ້ໄດ້ແນວໃດວ່າກຳລັງສື່ສານກັບໃຜ?

ເມື່ອອຸປະກອນສອງເຄື່ອງເຊື່ອມຕໍ່ກັນຄັ້ງທຳອິດ, ຄຳຖາມຫຼັກແມ່ນ: ອຸປະກອນ A ກຳລັງສື່ສານກັບອຸປະກອນ B ແທ້ບໍ, ຫຼືມີຜູ້ໃດໜຶ່ງຢູ່ກາງ? ໃນວິຊາການເຂົ້າລະຫັດ ນີ້ເອີ້ນວ່າການໂຈມຕີ ຜູ້ຢູ່ກາງ (MITM).

Timmy ແກ້ໄຂບັນຫານີ້ດ້ວຍ ການແລກປ່ຽນກະແຈ Elliptic Curve 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 ທີ່ໃຊ້ຮ່ວມກັນ. ກະແຈຈັບຄູ່ ບໍ່ເຄີຍຖືກສົ່ງໄປຫາລະບົບຫຼັງບ້ານ (backend); ຈະໃຊ້ພຽງຄ່າແຮຊ 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 ຕົວອັກສອນເປັນພຽງຈຸດນັດພົບ; ການເຂົ້າລະຫັດທີ່ແທ້ຈິງອາໄສກະແຈ 256 ບິດທີ່ໄດ້ຈາກ ECDH.

ສະຫຼຸບ

ກົນໄກຄວາມປອດໄພ ປ້ອງກັນຈາກ
ການແລກປ່ຽນກະແຈ ECDH (P-256) ການດັກຟັງຂໍ້ມູນການແລກປ່ຽນກະແຈ
ຄູ່ກະແຈຊົ່ວຄາວ ຄວາມລັບໄປຂ້າງໜ້າ — ການຈັບຄູ່ທີ່ຜ່ານມາຍັງຄົງປອດໄພ
ຕົວເລກຢືນຢັນດ້ວຍຕາ (SAS) ຜູ້ຢູ່ກາງ (MITM) ໃນລະຫວ່າງການແລກປ່ຽນກະແຈ
SHA-256 hash ເປັນກະແຈເອກະສານ ການດຶງລະຫັດອອກຈາກ Firestore
ການເຂົ້າລະຫັດ AES-256-GCM ການດັກຟັງຂໍ້ມູນສົ່ງສັນຍານ
ການຢືນຢັນຈາກທັງສອງຝ່າຍ ການຈັບຄູ່ຝ່າຍດຽວໂດຍຜູ້ໃຊ້ບໍ່ຮູ້
DTLS-SRTP (WebRTC) ການດັກຟັງສຽງ/ວິດີໂອ

ຊັ້ນປ້ອງກັນເຫຼົ່ານີ້ເຮັດວຽກຮ່ວມກັນ: ECDH ປົກປ້ອງການແລກປ່ຽນກະແຈ, ຕົວເລກຢືນຢັນປ້ອງກັນ MITM, AES-256-GCM ປົກປ້ອງການສົ່ງສັນຍານ, ແລະ WebRTC ປົກປ້ອງສື່. ຜູ້ໂຈມຕີຈະຕ້ອງທຳລາຍຫ່ວງໂສ້ນີ້ຫຼາຍຈຸດ ໂດຍບໍ່ໃຫ້ອຸປະກອນ ຫຼືພໍ່ແມ່ຮູ້ຕົວ.


ບົດຄວາມເພີ່ມເຕີມ