11.13. الإقران والربط

كل ما تناولناه حتى الآن ينقل البايتات عبر الراديو دون تشفير. أي شخص يمتلك حاسوبًا محمولًا قادرًا على BLE في الغرفة نفسها يمكنه التنصت على قنوات الإعلان، ومتابعة تسلسل القفز لاتصال مفتوح، وقراءة كل عملية قراءة وكتابة وإشعار تمر عبره. وبالنسبة لمعظم بيانات المستشعرات العامة (مستوى البطارية، درجة الحرارة المحيطة) فهذا أمر مقبول. أما بالنسبة لأي شيء يريد الطرفان إبقاءه خاصًا -- سجل تحكم يُفعّل مُرحّلًا، أو كلمة مرور، أو قياس لا ينبغي بثه على نطاق واسع -- فإن الرابط بحاجة إلى التشفير، ومن الأمثل أن تعرف الكاميرا مع من تتحدث.

يوفر BLE كليهما من خلال الإقران والربط.

11.13.1. الإقران والربط والتشفير

ثلاثة مفاهيم وثيقة الصلة:

  • التشفير هو الهدف النهائي. بمجرد تشفير الرابط، يصبح كل حزمة على قنوات البيانات قابلة للفك فقط من قِبل الطرفين؛ ويرى المتنصت ضوضاء.

  • الإقران هو الإجراء الذي ينفّذه الطرفان للاتفاق على المفاتيح التي يستخدمها التشفير. وهو تبادل يحدث مرة واحدة ينتج عنه مادة مفتاح مشتركة تُدخلها طبقة الرابط في محرك التشفير الخاص بها.

  • الربط هو اختيار حفظ المفاتيح في تخزين غير متطاير بعد انتهاء الإقران، بحيث يتخطى الاتصال التالي بين الجهازين نفسيهما عملية الإقران وينتقل مباشرة إلى التشفير.

بعبارات بسيطة: الإقران هو "عرّفا بنفسيكما"؛ والربط هو "تذكّر هذا التعريف"؛ والتشفير هو "تحدثا في خصوصية من الآن فصاعدًا".

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 مفتوح. بمجرد اكتمال تبادل المفاتيح، تشفّر طبقة الرابط كل حزمة لاحقة. والربط هو الخطوة الإضافية المتمثلة في كتابة المفاتيح إلى ذاكرة فلاش.

11.13.2. LE Secure Connections

تبادل المفاتيح الحديث الذي يستخدمه BLE هو LE Secure Connections، المبني على Elliptic Curve Diffie-Hellman. يولّد كلا الطرفين زوج مفاتيح مؤقتًا، ويتبادلان النصفين العامين، ويدمجان النتيجة مع مفاتيحهما الخاصة للوصول إلى السر المشترك نفسه -- وهو سرّ لا يستطيع المتنصت حسابه حتى مع سجل كامل للتبادل.

أما الطريقة الأقدم LE Legacy فهي أقل أمانًا (يمكن للمتنصت الذي يملك التبادل الكامل أن يستعيد المفتاح عادةً) وهي موجودة فقط للتوافق مع الطرفيات القديمة. الإعداد الافتراضي في aioble هو الطريقة الحديثة (le_secure=True)؛ فأبقِ عليه.

11.13.3. بدء الإقران

يقوم الجهاز المركزي بالإقران عن طريق استدعاء 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؛ وبتات الوصول التي تتطلب التشفير جزء من واجهة 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 وقدرة إدخال/إخراج قادرة. وبدونها، فإن وسيطًا اعترض تبادل الإقران الأصلي كان بإمكانه أن يدسّ نفسه؛ ودون حماية MITM لا توجد طريقة لكي يعرف الطرفان ذلك.

بالنسبة لمعظم حالات استخدام الكاميرا -- هاتف يُقرن مع الكاميرا مرة واحدة، ثم يتصل مرة أخرى لاحقًا -- يكون mitm=False كافيًا عادةً، لأن الإقران الأصلي يحدث في بيئة محكومة (يمسك المستخدم بكلا الجهازين في الغرفة نفسها). أما بالنسبة للتطبيقات التي قد يلتقي فيها جهاز مقترن بالكاميرا أول مرة عبر مسافة طويلة أو من خلال وسيط غير موثوق، فإن MITM هو الإعداد الصحيح.

11.13.6. متى يكون الإقران هو الإجابة الخاطئة

للإقران تكلفة حقيقية: بضع ثوانٍ من التبادل عند الاتصال الأول، واستخدام مستمر لذاكرة فلاش لكل جهاز مربوط، وقصة الاسترداد المتمثلة في "نسيان الربط" إذا حدث خطأ ما. أما بالنسبة للبيانات العامة فعلًا -- قراءات مستشعر محيط تُنشر كمنارة، أو لافتة تعرض اسمها، أو أي شيء لا يغيّر العالم بقراءته أو الكتابة إليه -- فإن الإجابة الصحيحة هي عدم التشفير على الإطلاق، والسماح لأي ماسح قريب بقراءة القيم.

وبالنسبة لكل ما عدا ذلك، فإن connection.pair(bond=True) على الجهاز المركزي هو الإضافة من سطر واحد التي تحوّل الرابط من قناة عامة إلى قناة خاصة.