11.13. צימוד וקישור (Pairing and bonding)

כל מה שכוסה עד כאן מעביר בתים מעל הרדיו ללא הצפנה. כל מי שברשותו מחשב נייד התומך ב-BLE באותו חדר יכול להאזין לערוצי הפרסום, לעקוב אחר רצף הקפיצות של חיבור פתוח, ולקרוא כל פעולת קריאה, כתיבה והתראה שעוברת. עבור רוב נתוני החיישנים הציבוריים (רמת סוללה, טמפרטורת סביבה) זה בסדר גמור. עבור כל דבר ששני קצוות החיבור רוצים לשמור בפרטיות – אוגר בקרה שמזיין ממסר, סיסמה, מדידה שאסור לשדר באופן נרחב – החיבור צריך להיות מוצפן, ובאופן אידיאלי המצלמה צריכה לדעת עם מי היא מדברת.

BLE מספק את שניהם דרך צימוד (pairing) וקישור (bonding).

11.13.1. צימוד, קישור, הצפנה

שלושה מושגים קרובים זה לזה:

  • הצפנה היא המטרה הסופית. ברגע שהחיבור מוצפן, כל מנה בערוצי הנתונים ניתנת לפענוח רק על ידי שני קצוות החיבור; מאזין סמוי רואה רעש.

  • צימוד (pairing) הוא ההליך ששני הקצוות מריצים כדי להסכים על המפתחות שההצפנה משתמשת בהם. זהו חילופי דברים חד-פעמיים המייצרים חומר מפתח משותף ששכבת החיבור מחברת למנוע ההצפנה שלה.

  • קישור (bonding) הוא הבחירה לשמר את המפתחות באחסון בלתי נדיף לאחר שהצימוד מסתיים, כך שהחיבור הבא בין אותם שני התקנים מדלג על הצימוד וניגש ישירות להצפנה.

בעברית פשוטה: צימוד הוא ”הציגו את עצמכם“; קישור הוא ”זכרו את ההיכרות הזו“; הצפנה היא ”דברו בפרטיות מעתה והלאה“.

Two columns labelled "Central" and "Peripheral". A dashed line near the top labelled "BLE connection open (unencrypted)". Below it, three arrows: "pairing request" from central to peripheral, "key exchange" both directions, "pairing complete" forward. A second dashed line below labelled "link encrypted". Two thick bidirectional arrows carry "encrypted GATT traffic". An optional "store keys to flash" box on the side, labelled "bonding".

זרימת הצימוד על גבי חיבור BLE פתוח. ברגע שחילופי המפתחות מסתיימים, שכבת החיבור מצפינה כל מנה שלאחר מכן. קישור הוא השלב הנוסף של כתיבת המפתחות לזיכרון פלאש (flash).

11.13.2. LE Secure Connections

חילופי המפתחות המודרניים שבהם BLE משתמש הם LE Secure Connections, הבנויים על Elliptic Curve Diffie-Hellman. שני הצדדים מייצרים זוג מפתחות זמני, מחליפים את החצאים הציבוריים, ומשלבים את התוצאה עם המפתחות הפרטיים שלהם כדי להגיע לאותו סוד משותף – סוד שמאזין סמוי אינו יכול לחשב אפילו עם תיעוד מלא של החילופין.

השיטה הישנה יותר, LE Legacy, פחות בטוחה (מאזין סמוי עם החילופין המלאים יכול בדרך כלל לשחזר את המפתח) וקיימת רק לתאימות לאחור עם התקנים היקפיים ישנים. ברירת המחדל של aioble היא השיטה המודרנית (le_secure=True); השאר אותה.

11.13.3. התחלת צימוד

התקן מרכזי (central) מבצע צימוד על ידי קריאה ל-aioble.DeviceConnection.pair() על חיבור שכבר פתוח:

async with await device.connect() as connection:
    await connection.pair(bond=True, le_secure=True, mitm=False)
    # ... GATT work, now over an encrypted link ...

לאחר ש-pair חוזרת, התכונות encrypted, authenticated, bonded, ו-key_size בחיבור משקפות את מה שנוהל במשא ומתן.

ארבעת ארגומנטי מילות המפתח השימושיים ביותר:

  • bond=True – שמור את המפתחות שנוצרו לזיכרון פלאש כך שהחיבור הבא בין אותם שני התקנים ידלג על לחיצת היד של הצימוד. ברירת מחדל True.

  • le_secure=True – השתמש ב-LE Secure Connections. ברירת מחדל True. השאר זאת מופעל.

  • mitm=False – האם לדרוש הגנת man-in-the-middle. זה דורש ערוץ מחוץ-לפס (קוד מספרי המוצג בצד אחד ומאושר בצד השני, מפתח גישה שמוקלד, …) כך שהמשתמש יכול לוודא ששני ההתקנים בלחיצת היד של הצימוד הם אכן אלה שהוא חושב. ברירת המחדל היא False (ללא הגנת MITM – מאזין סמוי פסיבי אינו יכול לקרוא את החיבור, אך תוקף שמפנה חיבורים באופן פעיל יכול לצמד את עצמו פנימה). הגדר ל-True עבור כל דבר רגיש, אך שים לב שזה דורש שההתקן ההיקפי אכן יתמוך ביכולת קלט/פלט.

  • io=3 – יכולת הקלט/פלט שההתקן מצהיר עליה. מפרט Bluetooth מגדיר חמש: 0 תצוגה בלבד, 1 תצוגה + כן/לא, 2 מקלדת בלבד, 3 ללא קלט ללא פלט, 4 מקלדת + תצוגה. מצלמה ללא ממשק משתמש מדווחת בדרך כלל על 3; אם למצלמה עצמה יש תצוגה, היישום יכול להציג את האישור המספרי ולהשתמש ב-1. השילוב של יכולות הקלט/פלט של שני הצדדים מכריע האם ניתן להשיג הגנת MITM אמיתית.

התקנים היקפיים אינם קוראים ל-pair בעצמם – הם מגיבים לכל מה שההתקן המרכזי יוזם. האם נדרשת הצפנה עבור מאפיין נתון היא תכונה של אופן ההכרזה עליו במסד הנתונים של GATT; ביטי הגישה הדורשים הצפנה הם חלק מ-API ברמה הנמוכה של bluetooth ואינם חשופים כעת דרך בנאי המאפיין של aioble.

11.13.4. קישור – והיכן המפתחות נמצאים

כאשר bond=True, aioble כותב את המפתחות לקובץ JSON במערכת הקבצים המקומית. שם הקובץ של ברירת המחדל הוא ble_secrets.json, נכתב יחסית לתיקיית העבודה הנוכחית. במצלמה שזה עתה אותחלה, _boot.py כבר בחר את אותה תיקייה: /sdcard כאשר כרטיס מותקן, /flash אחרת – כך שהקובץ נוחת ב-/sdcard/ble_secrets.json או ב-/flash/ble_secrets.json. הקובץ מחזיק את הערכים הדרושים להצפנה מחדש של החיבור בפעם הבאה שהעמית המקושר מתחבר מחדש, כולל כתובת הזהות של העמית.

אסימטריה אחת שכדאי לזכור: שמירה מתרחשת אוטומטית כאשר המפתחות משתנים, אך טעינה של הקובץ באתחול הבא לא. קרא ל-aioble.security.load_secrets() פעם אחת בעת ההפעלה (לפני כל צימוד או פרסום) כך שעמיתים שקושרו בעבר יזוהו:

import aioble
aioble.security.load_secrets()        # default path: ble_secrets.json

לאחר מכן, בפעם הבאה שעמית מקושר מופיע, aioble משתמש מחדש במפתחות השמורים והחיבור עובר להצפנה ללא לחיצת יד נוספת.

שתי השלכות מעשיות של אחסון מפתחות בזיכרון פלאש:

  • שכחת התקן. מחק את ble_secrets.json (או הסר את הערך הרלוונטי) כדי לשכוח את כל העמיתים המקושרים, ואז צמד מחדש מאפס.

  • גישה פיזית מדליפה מפתחות. כל מי שיש לו גישה למערכת הקבצים של המצלמה יכול לקרוא את ה-JSON. זוהי אותה סוג של מגבלה שעלתה בצד הרשת עם מפתחות TLS (תפעול: מפתחות, פקיעת תוקף ופתרון תקלות): השתמש במפתחות ייחודיים לכל התקן, התייחס לכל מפתח שמור כניתן לשחזור, והסתמך על היכולת לבטל (כאן, הסרת הקישור בצד המרכזי) במקום על כך שהמפתח יישאר סודי.

11.13.5. מה ההצפנה מבטיחה – ומה לא

חיבור של צימוד-ואז-הצפנה מספק, לפי סדר העוצמה:

  • סודיות. תמיד. מאזין סמוי אינו יכול לקרוא את הבתים.

  • שלמות. תמיד. מנות ששונו נכשלות בבדיקת ההצפנה-המאומתת של שכבת החיבור ונזרקות.

  • אימות. רק עם mitm=True ויכולת קלט/פלט מתאימה. בלעדיו, man-in-the-middle שיירט את חילופי הצימוד המקוריים יכול היה להכניס את עצמו; ללא הגנת MITM אין דרך לשני הצדדים לדעת.

עבור רוב מקרי השימוש של המצלמה – טלפון המצמד עם המצלמה פעם אחת, ואז מתחבר שוב מאוחר יותר – mitm=False הוא בדרך כלל מספיק, מכיוון שהצימוד המקורי מתרחש בסביבה מבוקרת (המשתמש מחזיק בשני ההתקנים באותו חדר). עבור יישומים שבהם התקן מצומד עשוי להיתקל במצלמה לראשונה ממרחק רב או דרך מתווך לא מהימן, MITM היא ההגדרה הנכונה.

11.13.6. מתי צימוד הוא התשובה הלא נכונה

לצימוד יש עלות אמיתית: מספר שניות של חילופין בחיבור הראשון, שימוש מתמשך בזיכרון פלאש עבור כל התקן מקושר, וסיפור השחזור של ”שכח את הקישור“ אם משהו משתבש. עבור נתונים ציבוריים באמת – קריאות חיישן סביבתי המתפרסמות כמשואה (beacon), שלט המציג את שמו, כל דבר שאינו משנה את העולם בכך שהוא נקרא או נכתב – התשובה הנכונה היא לא להצפין כלל, ולאפשר לכל סורק סמוך לקרוא את הערכים.

עבור כל דבר אחר, connection.pair(bond=True) בצד המרכזי היא התוספת בת שורה אחת שהופכת את החיבור מערוץ ציבורי לפרטי.