Skip to content

Commit c8d9c00

Browse files
committed
Add forget_clients()
1 parent e79cdd4 commit c8d9c00

3 files changed

Lines changed: 92 additions & 31 deletions

File tree

hid_keystores.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,28 @@ def add_json_secrets(self, entries):
4747
for sec_type, key, value in entries:
4848
self.secrets[sec_type, binascii.a2b_base64(key)] = binascii.a2b_base64(value)
4949

50+
# Empty key store.
51+
def clear_secrets(self):
52+
self.secrets = {}
53+
5054
def load_secrets(self):
5155
return
5256

5357
def save_secrets(self):
5458
return
5559

5660

57-
# Class that uses a JSON file as keystore
61+
# Class that uses a JSON file to save the keystore
5862
class JSONKeyStore(KeyStore):
5963

6064
def __init__(self):
6165
super(JSONKeyStore, self).__init__()
6266

67+
# Empty key store and save.
68+
def clear_secrets(self):
69+
super(JSONKeyStore, self).clear_secrets()
70+
self.save_secrets()
71+
6372
# Load bonding keys from JSON file.
6473
def load_secrets(self):
6574
try:
@@ -77,13 +86,18 @@ def save_secrets(self):
7786
print("Failed to save secrets")
7887

7988

80-
# Class that uses non-volatile storage as keystore
89+
# Class that uses non-volatile storage to save the keystore
8190
class NVSKeyStore(KeyStore):
8291

8392
def __init__(self):
8493
super(NVSKeyStore, self).__init__()
8594
self.nvsdata = esp32.NVS("BLE")
8695

96+
# Empty key store and save.
97+
def clear_secrets(self):
98+
super(NVSKeyStore, self).clear_secrets()
99+
self.save_secrets()
100+
87101
# Load bonding keys from non-volatile storage.
88102
def load_secrets(self):
89103
data = bytearray()

hid_services.py

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ def __init__(self, device_name="Generic HID Device"):
193193
self.device_state = HumanInterfaceDevice.DEVICE_STOPPED # The initial device state.
194194
self.conn_handle = None # The handle of the connected client. HID devices can only have a single connection.
195195
self.state_change_callback = None # The user defined callback function which gets called when the device state changes.
196+
self.passkey_callback = None # The user defined callback function which gets called when a passkey is prompted.
196197
self.io_capability = _IO_CAPABILITY_NO_INPUT_OUTPUT # The IO capability of the device. This is used to allow for different ways of identification during pairing.
197198
self.bond = True # Do we wish to bond with connecting clients? Normally True. Not supported by older Micropython versions.
198199
self.le_secure = True # Do we wish to use a secure connection? Normally True. Not supported by older Micropython versions.
@@ -393,8 +394,7 @@ def start(self):
393394
print("BLE on with", "random" if addr_type else "public", "mac address", addr)
394395

395396
# After registering the DIS and BAS services, write their characteristic values.
396-
# Must be overwritten by subclass, and called in
397-
# the overwritten function by using
397+
# Must be overwritten by subclass, and called in the overwritten function by using
398398
# super(Subclass, self).save_service_characteristics(handles).
399399
def save_service_characteristics(self, handles):
400400
print("Writing service characteristics")
@@ -522,59 +522,82 @@ def set_battery_level(self, level):
522522
else:
523523
self.battery_level = level
524524

525-
# Set device information.
526-
# Must be called before calling Start().
527-
# Variables must be Strings.
525+
# Set device information. Variables must be Strings.
528526
def set_device_information(self, manufacture_name="Homebrew", model_number="1", serial_number="1"):
529-
self.manufacture_name = manufacture_name
530-
self.model_number = model_number
531-
self.serial_number = serial_number
527+
if self.device_state == self.DEVICE_STOPPED:
528+
self.manufacture_name = manufacture_name
529+
self.model_number = model_number
530+
self.serial_number = serial_number
531+
else:
532+
print("Failed to set device information: device must be stopped.")
532533

533-
# Set device revision.
534-
# Must be called before calling Start().
535-
# Variables must be Strings.
534+
# Set device revision. Variables must be Strings.
536535
def set_device_revision(self, firmware_revision="1", hardware_revision="1", software_revision="1"):
537-
self.firmware_revision = firmware_revision
538-
self.hardware_revision = hardware_revision
539-
self.software_revision = software_revision
536+
if self.device_state == self.DEVICE_STOPPED:
537+
self.firmware_revision = firmware_revision
538+
self.hardware_revision = hardware_revision
539+
self.software_revision = software_revision
540+
else:
541+
print("Failed to set device revision: device must be stopped.")
540542

541543
# Set device pnp information.
542-
# Must be called before calling Start().
543544
# Must use the following format:
544545
# pnp_manufacturer_source: 0x01 for manufacturers uuid from the Bluetooth uuid list OR 0x02 from the USBs id list.
545546
# pnp_manufacturer_uuid: 0xFEB2 for Microsoft, 0xFE61 for Logitech, 0xFD65 for Razer with source 0x01.
546547
# pnp_product_id: One byte, user defined.
547548
# pnp_product_version: Two bytes, user defined, format as 0xJJMN which corresponds to version JJ.M.N.
548549
def set_device_pnp_information(self, pnp_manufacturer_source=0x01, pnp_manufacturer_uuid=0xFE61, pnp_product_id=0x01, pnp_product_version=0x0123):
549-
self.pnp_manufacturer_source = pnp_manufacturer_source
550-
self.pnp_manufacturer_uuid = pnp_manufacturer_uuid
551-
self.pnp_product_id = pnp_product_id
552-
self.pnp_product_version = pnp_product_version
550+
if self.device_state == self.DEVICE_STOPPED:
551+
self.pnp_manufacturer_source = pnp_manufacturer_source
552+
self.pnp_manufacturer_uuid = pnp_manufacturer_uuid
553+
self.pnp_product_id = pnp_product_id
554+
self.pnp_product_version = pnp_product_version
555+
else:
556+
print("Failed to set pnp information: device must be stopped.")
553557

554558
# Set whether to use Bluetooth bonding.
555559
def set_bonding(self, bond=True):
556-
self.bond = bond
560+
if self.device_state == self.DEVICE_STOPPED:
561+
self.bond = bond
562+
else:
563+
print("Failed to set bonding: device must be stopped.")
557564

558565
# Set whether to use LE secure pairing.
559566
def set_le_secure(self, le_secure=True):
560-
self.le_secure = le_secure
567+
if self.device_state == self.DEVICE_STOPPED:
568+
self.le_secure = le_secure
569+
else:
570+
print("Failed to set LE secure pairing: device must be stopped.")
561571

562572
# Set input/output capability of this device.
563573
# Determines the pairing procedure, e.g., accept connection/passkey entry/just works.
564-
# Must be called before calling Start().
565574
# Must use the following values:
566575
# _IO_CAPABILITY_DISPLAY_ONLY,
567576
# _IO_CAPABILITY_DISPLAY_YESNO,
568577
# _IO_CAPABILITY_KEYBOARD_ONLY,
569578
# _IO_CAPABILITY_NO_INPUT_OUTPUT, or
570579
# _IO_CAPABILITY_KEYBOARD_DISPLAY.
571580
def set_io_capability(self, io_capability):
572-
self.io_capability = io_capability
581+
if self.device_state == self.DEVICE_STOPPED:
582+
self.io_capability = io_capability
583+
else:
584+
print("Failed to set IO capability: device must be stopped.")
573585

574586
# Set the keystore class to use.
575-
# Must be called before calling Start().
587+
# Make sure to remove device from client first.
576588
def set_keystore(self, keystore):
577-
self.secrets = keystore
589+
if self.device_state == self.DEVICE_STOPPED:
590+
self.secrets = keystore
591+
else:
592+
print("Failed to set keystore: device must be stopped.")
593+
594+
# Removes saved keys from the keystore.
595+
# Make sure to remove device from client first.
596+
def forget_clients(self):
597+
if self.device_state == self.DEVICE_STOPPED:
598+
self.secrets.clear_secrets()
599+
else:
600+
print("Failed to remove keys: device must be stopped.")
578601

579602
# Set callback function for pairing events.
580603
# Depending on the I/O capability used, the callback function should return either a

readme.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ The reason for this is that such functionality is entirely dependent on the inte
134134

135135
The library consists of five classes with the following functions:
136136

137-
* `HumanInterfaceDevice` (superclass for the HID service classes, implements the Device Information and Battery services, and sets up BLE and advertisement)
137+
* `HumanInterfaceDevice` (Superclass for the HID service classes, implements the Device Information and Battery services, and sets up BLE and advertisement)
138138
* `__init__(device_name)` (Initialize the superclass)
139139
* `ble_irq(event, data)` (Internal callback function that catches BLE interrupt requests)
140140
* `start()` (Starts Device Information and Battery services)
@@ -163,19 +163,20 @@ The library consists of five classes with the following functions:
163163
* `set_passkey_callback(passkey_callback)` (Set callback function for pairing events. Callback function should return boolean to accept connection or passkey depending on I/O capability used)
164164
* `set_passkey(passkey)` (Set the passkey to use for pairing)
165165
* `set_keystore(keystore)` (Sets the key store to use from `hid_keystores.py`. Default `JSONKeyStore`)
166+
* `forget_clients()` (Removes all client keys from the key store)
166167
* `set_battery_level(level)` (Sets the battery level internally)
167168
* `notify_battery_level()` (Notifies the client of the current battery level. Call after setting battery level)
168169
* `notify_hid_report()` (Function for subclasses to override)
169170

170-
* `Joystick` (subclass of `HumanInterfaceDevice`, implements joystick service)
171+
* `Joystick` (Subclass of `HumanInterfaceDevice`, implements joystick service)
171172
* `__init__(name)` (Initialize the joystick)
172173
* `start()` (Starts the HID service using joystick characteristics. Calls `HumanInterfaceDevice.start()`)
173174
* `write_service_characteristics(handles)` (Writes the joystick HID service characteristics. Calls `HumanInterfaceDevice.write_service_characteristics(handles)`)
174175
* `notify_hid_report()` (Notifies the client of the internal HID joystick status)
175176
* `set_axes(x, y)` (Sets the joystick axes internally)
176177
* `set_buttons(b1, b2, b3, b4, b5, b6, b7, b8)` (Sets the joystick buttons internally)
177178

178-
* `Mouse` (subclass of `HumanInterfaceDevice`, implements mouse service)
179+
* `Mouse` (Subclass of `HumanInterfaceDevice`, implements mouse service)
179180
* `__init__(name)` (Initialize the mouse)
180181
* `start()` (Starts the HID service using mouse characteristics. Calls `HumanInterfaceDevice.start()`)
181182
* `write_service_characteristics(handles)` (Writes the mouse HID service characteristics. Calls `HumanInterfaceDevice.write_service_characteristics(handles)`)
@@ -184,7 +185,7 @@ The library consists of five classes with the following functions:
184185
* `set_wheel(w)` (Sets the mouse wheel movement internally)
185186
* `set_buttons(b1, b2, b3)` (Sets the mouse buttons internally)
186187

187-
* `Keyboard` (subclass of `HumanInterfaceDevice`, implements keyboard service)
188+
* `Keyboard` (Subclass of `HumanInterfaceDevice`, implements keyboard service)
188189
* `__init__(name)` (Initialize the keyboard)
189190
* `start()` (Starts the HID service using keyboard characteristics. Calls `HumanInterfaceDevice.start()`)
190191
* `write_service_characteristics(handles)` (Writes the keyboard HID service characteristics. Calls `HumanInterfaceDevice.write_service_characteristics(handles)`)
@@ -203,6 +204,29 @@ The library consists of five classes with the following functions:
203204
* `start_advertising()` (Used internally)
204205
* `stop_advertising()` (Used internally)
205206

207+
* `KeyStore` (Superclass for the key store classes)
208+
* `__init__()` (Initialize the key store)
209+
* `add_secret(type, key, value)` (Adds a type-key-value triplet)
210+
* `get_secret(type, index, key)` (Returns the value for the given type-key pair, or the value at the given index for the given type)
211+
* `remove_secret(type, key)` (Removes the type-key-value triplet of the given type-key pair)
212+
* `has_secret(type, key)` (Return true iff there exists a value for the given type-key pair)
213+
* `get_json_secrets()` (Returns the added type-key-value triplets as a JSON string)
214+
* `add_json_secrets(entries)` (Adds all type-key-value triplets contained in the given JSON string)
215+
* `clear_secrets()` (Removes all type-key-value triplets)
216+
* `load_secrets()` (Function for subclasses to override)
217+
* `save_secrets()` (Function for subclasses to override)
218+
219+
* `JSONKeyStore` (Subclass of `KeyStore` that saves the type-key-value triplets as a JSON file)
220+
* `__init__()` (Initialize the key store)
221+
* `clear_secrets()` (Removes all type-key-value triplets and calls `save_secrets()`)
222+
* `load_secrets()` (Loads the JSON file and calls `add_json_secrets(entries)`)
223+
* `save_secrets()` (Calls `get_json_secrets()` and saves the result to a file)
224+
225+
* `NVSKeyStore` (Subclass of `KeyStore` that saves the type-key-value triplets in non-volatile storage)
226+
* `__init__()` (Initialize the key store)
227+
* `clear_secrets()` (Removes all type-key-value triplets and calls `save_secrets()`)
228+
* `load_secrets()` (Loads the JSON blob from non-volatile storage and calls `add_json_secrets(entries)`)
229+
* `save_secrets()` (Calls `get_json_secrets()` and saves the result to a non-volatile storage blob)
206230

207231
<p align="right">(<a href="#top">back to top</a>)</p>
208232

0 commit comments

Comments
 (0)