You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ja...@apache.org on 2022/08/04 06:57:28 UTC
[mynewt-nimble] 01/02: tools/hci_throughput: testing over encrypted link
This is an automated email from the ASF dual-hosted git repository.
janc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git
commit 82dd24d8257b2d66fb2f5b447e19c6493fb781a7
Author: Jakub <ja...@codecoup.pl>
AuthorDate: Wed Aug 3 17:38:47 2022 +0200
tools/hci_throughput: testing over encrypted link
Added option enable_encryption to run throughput
tests over encrypted link.
---
tools/hci_throughput/config.yaml.sample | 1 +
tools/hci_throughput/hci.py | 37 +++++++++++++++
tools/hci_throughput/hci_commands.py | 84 +++++++++++++++++++++++++++++++++
tools/hci_throughput/hci_device.py | 7 ++-
tools/hci_throughput/main.py | 8 +++-
5 files changed, 135 insertions(+), 2 deletions(-)
diff --git a/tools/hci_throughput/config.yaml.sample b/tools/hci_throughput/config.yaml.sample
index 7a833007..68068e3e 100644
--- a/tools/hci_throughput/config.yaml.sample
+++ b/tools/hci_throughput/config.yaml.sample
@@ -43,6 +43,7 @@ adv:
peer_address: 00:00:00:00:00:00
advertising_channel_map: 7
advertising_filter_policy: 0
+enable_encryption: true
conn:
le_scan_interval: 2400
le_scan_window: 2400
diff --git a/tools/hci_throughput/hci.py b/tools/hci_throughput/hci.py
index d0a0b8a1..e5eb1179 100644
--- a/tools/hci_throughput/hci.py
+++ b/tools/hci_throughput/hci.py
@@ -35,6 +35,7 @@ HCI_EVENT_PACKET = 0x04
L2CAP_HDR_BYTES = 4
HCI_EV_CODE_DISCONN_CMP = 0x05
+HCI_EV_CODE_ENCRYPTION_CHANGE = 0x08
HCI_EV_CODE_CMD_CMP = 0x0e
HCI_EV_CODE_CMD_STATUS = 0x0f
HCI_EV_CODE_LE_META_EVENT = 0x3e
@@ -42,6 +43,7 @@ HCI_SUBEV_CODE_LE_ENHANCED_CONN_CMP = 0x0a
HCI_SUBEV_CODE_LE_DATA_LEN_CHANGE = 0x07
HCI_SUBEV_CODE_LE_PHY_UPDATE_CMP = 0x0c
HCI_SUBEV_CODE_LE_CHAN_SEL_ALG = 0x14
+HCI_SUBEV_CODE_LE_LONG_TERM_KEY_REQUEST = 0x05
HCI_EV_NUM_COMP_PKTS = 0x13
CONN_FAILED_TO_BE_ESTABLISHED = 0x3e
@@ -66,6 +68,8 @@ OCF_LE_SET_ADVERTISE_ENABLE = 0x000a
OCF_LE_SET_SCAN_PARAMETERS = 0x000b
OCF_LE_SET_SCAN_ENABLE = 0x000c
OCF_LE_CREATE_CONN = 0x000d
+OCF_LE_ENABLE_ENCRYPTION = 0x0019
+OCF_LE_LONG_TERM_KEY_REQUEST_REPLY = 0x001A
OCF_LE_SET_DATA_LEN = 0x0022
OCF_LE_READ_SUGGESTED_DFLT_DATA_LEN = 0x0023
OCF_LE_READ_MAX_DATA_LEN = 0x002f
@@ -106,6 +110,7 @@ num_of_completed_packets_cnt = 0
num_of_completed_packets_time = 0
read_local_commands = None
le_read_local_supported_features = None
+ltk = None
############
# FUNCTIONS
@@ -303,6 +308,21 @@ class HCI_Ev_Cmd_Status:
self.opcode = opcode
+@dataclass
+class HCI_Ev_LE_Encryption_Change():
+ status: int
+ connection_handle: int
+ encryption_enabled: int
+
+ def __init__(self):
+ self.set()
+
+ def set(self, status=0, connection_handle=0, encryption_enabled=0):
+ self.status = status
+ self.connection_handle = connection_handle
+ self.encryption_enabled = encryption_enabled
+
+
@dataclass
class HCI_Ev_LE_Meta:
subevent_code: int
@@ -374,6 +394,23 @@ class HCI_Ev_LE_Data_Length_Change(HCI_Ev_LE_Meta):
self.triggered = triggered
+@dataclass
+class HCI_Ev_LE_Long_Term_Key_Request(HCI_Ev_LE_Meta):
+ conn_handle: int
+ random_number: int
+ encrypted_diversifier: int
+
+ def __init__(self):
+ self.set()
+
+ def set(self, subevent_code=0, conn_handle=0, random_number=0,
+ encrypted_diversifier=0):
+ super().set(subevent_code)
+ self.conn_handle = conn_handle
+ self.random_number = random_number
+ self.encrypted_diversifier = encrypted_diversifier
+
+
@dataclass
class HCI_Ev_LE_PHY_Update_Complete(HCI_Ev_LE_Meta):
status: int
diff --git a/tools/hci_throughput/hci_commands.py b/tools/hci_throughput/hci_commands.py
index 83e6284f..31bed375 100644
--- a/tools/hci_throughput/hci_commands.py
+++ b/tools/hci_throughput/hci_commands.py
@@ -47,6 +47,7 @@ class HCI_Commands():
self.async_sem_cmd = asyncio.Semaphore()
self.async_ev_cmd_end = asyncio.Event()
self.async_ev_connected = asyncio.Event()
+ self.async_ev_encryption_change = asyncio.Event()
self.async_ev_set_data_len = asyncio.Event()
self.async_ev_update_phy = asyncio.Event()
self.async_ev_num_cmp_pckts = asyncio.Event()
@@ -219,6 +220,41 @@ class HCI_Commands():
await self.async_ev_cmd_end.wait()
self.async_ev_cmd_end.clear()
+ async def cmd_le_enable_encryption(self, conn_handle: int, random_number: int, ediv: int, ltk: int):
+ async with self.async_sem_cmd:
+ hci.ltk = ltk
+ random_number_bytes = random_number.to_bytes(8, byteorder='little')
+ ltk_bytes = ltk.to_bytes(16, byteorder='little')
+ data_bytes = struct.pack("<H", conn_handle) + random_number_bytes + \
+ struct.pack("<H", ediv) + ltk_bytes
+ self.hci_send_cmd.set(
+ hci.OGF_LE_CTL,
+ hci.OCF_LE_ENABLE_ENCRYPTION,
+ data_bytes)
+ logging.debug("%s %s", self.cmd_le_enable_encryption.__name__,
+ self.hci_send_cmd)
+ await self.send(self.hci_send_cmd.ba_full_message)
+ await self.async_ev_cmd_end.wait()
+ self.async_ev_cmd_end.clear()
+
+ async def cmd_le_long_term_key_request_reply(self, conn_handle: int, ltk: int):
+ async with self.async_sem_cmd:
+ ltk_bytes = ltk.to_bytes(16, byteorder='little')
+ data_bytes = struct.pack('<H', conn_handle) + ltk_bytes
+ self.hci_send_cmd.set(
+ hci.OGF_LE_CTL,
+ hci.OCF_LE_LONG_TERM_KEY_REQUEST_REPLY,
+ data_bytes)
+ logging.debug(
+ "%s %s",
+ self.cmd_le_long_term_key_request_reply.__name__,
+ self.hci_send_cmd)
+ await self.send(self.hci_send_cmd.ba_full_message)
+ # Is run from another command,
+ # don't need to wait for cmd complete.
+ # await self.async_ev_cmd_end.wait()
+ # self.async_ev_cmd_end.clear()
+
async def cmd_le_set_data_len(self, conn_handle: int, tx_octets: int, tx_time: int):
""" conn_handle: Range 0x0000 to 0x0EFF
tx_octets: Range 0x001B to 0x00FB
@@ -358,6 +394,11 @@ class HCI_Commands():
ev_cmd_stat.set(*struct.unpack('<BBH', bytes(data[:4])))
return ev_cmd_stat
+ def parse_ev_encryption_change(self, data: bytes):
+ ev_encryption_change = hci.HCI_Ev_LE_Encryption_Change()
+ ev_encryption_change.set(*struct.unpack('<BHB', bytes(data[:4])))
+ return ev_encryption_change
+
def parse_ev_le_meta(self, data: bytes):
ev_le_meta = hci.HCI_Ev_LE_Meta()
ev_le_meta.set(data[0])
@@ -377,6 +418,12 @@ class HCI_Commands():
ev_le_data_len_change.set(*struct.unpack('<BHHHHH', bytes(data[:11])))
return ev_le_data_len_change
+ def parse_subev_le_long_term_key_request(self, data: bytes):
+ ev_le_long_term_key_request = hci.HCI_Ev_LE_Long_Term_Key_Request()
+ ev_le_long_term_key_request.set(
+ *struct.unpack('<BHQH', bytes(data[:13])))
+ return ev_le_long_term_key_request
+
def parse_subev_le_phy_update_cmp(self, data: bytes):
le_phy_update_cmp = hci.HCI_Ev_LE_PHY_Update_Complete()
le_phy_update_cmp.set(*struct.unpack('<BBHBB', data))
@@ -533,6 +580,15 @@ class HCI_Commands():
self.hci_recv_ev_packet.current_event))
return hci.HCI_SUBEV_CODE_LE_ENHANCED_CONN_CMP
+ elif subev_code == hci.HCI_SUBEV_CODE_LE_LONG_TERM_KEY_REQUEST:
+ self.hci_recv_ev_packet.current_event = \
+ self.parse_subev_le_long_term_key_request(
+ self.hci_recv_ev_packet.recv_data)
+ hci.events_list.append(
+ (hci.HCI_SUBEV_CODE_LE_LONG_TERM_KEY_REQUEST,
+ self.hci_recv_ev_packet.current_event))
+ return hci.HCI_SUBEV_CODE_LE_LONG_TERM_KEY_REQUEST
+
elif subev_code == hci.HCI_SUBEV_CODE_LE_DATA_LEN_CHANGE:
self.hci_recv_ev_packet.current_event = \
self.parse_subev_le_data_len_change(
@@ -584,6 +640,14 @@ class HCI_Commands():
self.hci_recv_ev_packet.current_event))
return hci.HCI_EV_CODE_CMD_STATUS
+ elif self.hci_recv_ev_packet.ev_code == hci.HCI_EV_CODE_ENCRYPTION_CHANGE:
+ self.hci_recv_ev_packet.current_event = \
+ self.parse_ev_encryption_change(
+ self.hci_recv_ev_packet.recv_data)
+ hci.events_list.append((hci.HCI_EV_CODE_ENCRYPTION_CHANGE,
+ self.hci_recv_ev_packet.current_event))
+ return hci.HCI_EV_CODE_ENCRYPTION_CHANGE
+
elif self.hci_recv_ev_packet.ev_code == hci.HCI_EV_CODE_LE_META_EVENT:
self.hci_recv_ev_packet.current_event = \
self.parse_ev_le_meta(self.hci_recv_ev_packet.recv_data)
@@ -647,6 +711,19 @@ class HCI_Commands():
logging.error("Status: %s for event: %s", status, curr_ev)
self.async_ev_cmd_end.set()
+ elif event_code == hci.HCI_EV_CODE_ENCRYPTION_CHANGE:
+ logging.debug(
+ "Received code: %s - HCI_EV_CODE_ENCRYPTION_CHANGE",
+ event_code)
+ status = curr_ev.status
+ encryption_enabled = curr_ev.encryption_enabled
+ if (status == 0 and encryption_enabled != 0):
+ self.async_ev_encryption_change.set()
+ else:
+ raise Exception(
+ "Encryption failed. Status: %d, encryption enabled: %d",
+ status, encryption_enabled)
+
elif event_code == hci.HCI_EV_CODE_LE_META_EVENT:
logging.debug(
"Received code: %s - HCI_EV_CODE_LE_META_EVENT", event_code)
@@ -678,6 +755,13 @@ class HCI_Commands():
"Received subev code: %s - HCI_SUBEV_CODE_LE_CHAN_SEL_ALG",
subev_code)
+ elif subev_code == hci.HCI_SUBEV_CODE_LE_LONG_TERM_KEY_REQUEST:
+ logging.debug(
+ "Received subev code: %s - HCI_SUBEV_CODE_LE_LONG_TERM_KEY_REQUEST",
+ subev_code)
+ await self.cmd_le_long_term_key_request_reply(
+ hci.conn_handle, hci.ltk)
+
elif subev_code < 0:
logging.warning(f"Unknown received subevent: {buffer}\n")
diff --git a/tools/hci_throughput/hci_device.py b/tools/hci_throughput/hci_device.py
index dacb391c..b054b705 100644
--- a/tools/hci_throughput/hci_device.py
+++ b/tools/hci_throughput/hci_device.py
@@ -218,6 +218,10 @@ async def async_main_tx(bt_dev: hci_commands.HCI_Commands, ini: dict, cfg: dict)
hci.le_read_local_supported_features.le_features)
await hci_commands.wait_for_event(bt_dev.async_ev_update_phy, hci.WAIT_FOR_EVENT_TIMEOUT)
+ if cfg["enable_encryption"]:
+ await bt_dev.cmd_le_enable_encryption(hci.conn_handle, random_number=0, ediv=0, ltk=hci.ltk)
+ await hci_commands.wait_for_event(bt_dev.async_ev_encryption_change, 10)
+
############
# L2CAP SEND
############
@@ -289,9 +293,10 @@ def parse_cfg_files(args) -> dict:
with open(args.init_file, "r") as file:
init_file = yaml.safe_load(file)
ini = init_file[args.mode]
- global test_dir, transport_directory
+ global test_dir, transport_directory, ltk
test_dir = init_file["test_dir"]
transport_directory = init_file["transport_directory"]
+ hci.ltk = int(init_file["ltk"], 16)
with open(args.config_file) as f:
cfg = yaml.safe_load(f)
diff --git a/tools/hci_throughput/main.py b/tools/hci_throughput/main.py
index b00780a0..565cce42 100644
--- a/tools/hci_throughput/main.py
+++ b/tools/hci_throughput/main.py
@@ -29,6 +29,7 @@ import csv
import util
import os
import math
+import random
PROCESS_TIMEOUT = 500 # seconds, adjust if necessary
@@ -94,6 +95,10 @@ def change_config_var(filename: str, group: str, variable: str,
yaml.safe_dump(cfg, file, indent=1, sort_keys=False,
default_style=None, default_flow_style=False)
+def generate_long_term_key():
+ rand_val = random.getrandbits(128)
+ return rand_val.to_bytes(16, byteorder='little')
+
def get_init_dict(filename: str, args_list: list, modes: list, dir: str,
transport_directory: str):
@@ -115,7 +120,8 @@ def get_init_dict(filename: str, args_list: list, modes: list, dir: str,
"peer_address": args_list[0][2]
},
"test_dir": dir,
- "transport_directory": transport_directory
+ "transport_directory": transport_directory,
+ "ltk": hex(random.getrandbits(128))
}
with open(filename, 'w') as file: