diff --git a/apps/ble_rpa_tool.py b/apps/ble_rpa_tool.py new file mode 100644 index 0000000..07d89a3 --- /dev/null +++ b/apps/ble_rpa_tool.py @@ -0,0 +1,63 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import click +from bumble.colors import color +from bumble.hci import Address +from bumble.helpers import generate_irk, verify_rpa_with_irk + + +@click.group() +def cli(): + ''' + This is a tool for generating IRK, RPA, + and verifying IRK/RPA pairs + ''' + + +@click.command() +def gen_irk() -> None: + print(generate_irk().hex()) + + +@click.command() +@click.argument("irk", type=str) +def gen_rpa(irk: str) -> None: + irk_bytes = bytes.fromhex(irk) + rpa = Address.generate_private_address(irk_bytes) + print(rpa.to_string(with_type_qualifier=False)) + + +@click.command() +@click.argument("irk", type=str) +@click.argument("rpa", type=str) +def verify_rpa(irk: str, rpa: str) -> None: + address = Address(rpa) + irk_bytes = bytes.fromhex(irk) + if verify_rpa_with_irk(address, irk_bytes): + print(color("Verified", "green")) + else: + print(color("Not Verified", "red")) + + +def main(): + cli.add_command(gen_irk) + cli.add_command(gen_rpa) + cli.add_command(verify_rpa) + cli() + + +# ----------------------------------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/bumble/helpers.py b/bumble/helpers.py index 6174851..762601c 100644 --- a/bumble/helpers.py +++ b/bumble/helpers.py @@ -37,6 +37,7 @@ from bumble.l2cap import ( L2CAP_Connection_Response, ) from bumble.hci import ( + Address, HCI_EVENT_PACKET, HCI_ACL_DATA_PACKET, HCI_DISCONNECTION_COMPLETE_EVENT, @@ -48,6 +49,7 @@ from bumble.hci import ( ) from bumble.rfcomm import RFCOMM_Frame, RFCOMM_PSM from bumble.sdp import SDP_PDU, SDP_PSM +from bumble import crypto # ----------------------------------------------------------------------------- # Logging @@ -232,3 +234,15 @@ class PacketTracer: ) self.host_to_controller_analyzer.peer = self.controller_to_host_analyzer self.controller_to_host_analyzer.peer = self.host_to_controller_analyzer + + +def generate_irk() -> bytes: + return crypto.r() + + +def verify_rpa_with_irk(rpa: Address, irk: bytes) -> bool: + rpa_bytes = bytes(rpa) + prand_given = rpa_bytes[3:] + hash_given = rpa_bytes[:3] + hash_local = crypto.ah(irk, prand_given) + return hash_local[:3] == hash_given diff --git a/setup.cfg b/setup.cfg index 4b811a5..c9ab4c8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -56,6 +56,7 @@ install_requires = [options.entry_points] console_scripts = + bumble-ble-rpa-tool = bumble.apps.ble_rpa_tool:main bumble-console = bumble.apps.console:main bumble-controller-info = bumble.apps.controller_info:main bumble-gatt-dump = bumble.apps.gatt_dump:main