咱們已經完成了 IDS Server,可是 GATT Client 不須配對即可對它進行讀寫,這表示讀寫的資料都沒有經過加密,是裸露在外,任人觀看,這實在是蠻危險的。
為了解決這問題,咱們來稍微改變一下每個 Characteristic 的讀寫屬性。
class IddFeatures(ble.mixin.ReadMixin, ble.stack.Characteristic):
def __init__(self, config: config.Config):
ble.stack.Characteristic.__init__(self, 0x2B23, read=True, read_enc=True)
class BaseCP(ble.mixin.WriteMixin, ble.mixin.IndicateMixin, ble.stack.Characteristic):
def __init__(self, uuid: int | str):
ble.stack.Characteristic.__init__(
self, uuid, write=True, write_enc=True, indicate=True
)
Read
屬性,加上 read_enc
;而具備 Write
屬性的,則加上 write_enc
。read_enc
和 write_enc
的作用,請參考第 6 天的 封裝 BLE 相關功能 所附的原始碼。MicroPython bluetooth 支援以下屬性:
_FLAG_BROADCAST = const(0x0001)
_FLAG_READ = const(0x0002)
_FLAG_WRITE_NO_RESPONSE = const(0x0004)
_FLAG_WRITE = const(0x0008)
_FLAG_NOTIFY = const(0x0010)
_FLAG_INDICATE = const(0x0020)
_FLAG_AUTHENTICATED_SIGNED_WRITE = const(0x0040)
_FLAG_AUX_WRITE = const(0x0100)
_FLAG_READ_ENCRYPTED = const(0x0200)
_FLAG_READ_AUTHENTICATED = const(0x0400)
_FLAG_READ_AUTHORIZED = const(0x0800)
_FLAG_WRITE_ENCRYPTED = const(0x1000)
_FLAG_WRITE_AUTHENTICATED = const(0x2000)
_FLAG_WRITE_AUTHORIZED = const(0x4000)
不過 MicroPython 目前還沒完整支援所有功能,比如 Authorized
便還不能正常運作。所以如果要讓 Characteristic 使用前必須配對,須使用 Encrypted 或 Authenticated。
咱們為每個 Characteristic 加上配對要求了,但卻出現一個問題,那就是每當 IDS Server 與 GATT Client 斷線、重新連線後,要讀寫 Characteristics 前,須要再進行一次配對!!!
為什麼會這樣呢?這是因為咱們並沒有處理兩個事件:
_IRQ_GET_SECRET = const(29)
_IRQ_SET_SECRET = const(30)
在 MicroPython bluetooth 的流程裡,當 Characteristic 具有 Encrypted
、Authenticated
或 Authorized
屬性時:
_IRQ_GET_SECRET
給 GATT Server 來取得先前的配對資訊_IRQ_SET_SECRET
來通知 GATT Server,GATT Server 可以儲存此資訊。這樣當下一次需要時,GATT Server 可以在中斷事件 _IRQ_GET_SECRET
中,將其傳回給系統。為了儲存配對資訊,咱們在 ble/stack.py 增加以下變數:
_BLE_STORE_OBJ_TYPE_OUR_SEC = micropython.const(1)
_our_sec = bytearray(88)
_has_our_sec = False
然後處理中斷事件 _IRQ_GET_SECRET
來取得配對資訊:
def _on_get_secret(sec_type: int, index: int, key: bytes) -> bytes | None:
if index != 0:
return None
if sec_type == _BLE_STORE_OBJ_TYPE_OUR_SEC:
if _has_our_sec:
return _our_sec
return None
index
為 0
時的配對資料,所以當 index
不為 0
時,即返回 None
,表示沒有此配對資訊。_BLE_STORE_OBJ_TYPE_OUR_SEC
型態的配對資料,所以其餘的配對資訊都回覆 None
。接下來是儲存配對資訊:
def _on_set_secret(sec_type: int, key: bytes, value: bytes):
if sec_type == _BLE_STORE_OBJ_TYPE_OUR_SEC:
global _has_our_sec
if value:
_our_sec[:] = value
_has_our_sec = True
else:
_has_our_sec = False
return True
如此,便不用每次斷線都必須重新配對了。