1# Copyright 2021-2022 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15# -----------------------------------------------------------------------------
16# Imports
17# -----------------------------------------------------------------------------
18import asyncio
19import os
20import logging
21import click
22
23from bumble.device import Device
24from bumble.keys import JsonKeyStore
25from bumble.transport import open_transport
26
27
28# -----------------------------------------------------------------------------
29async def unbond_with_keystore(keystore, address):
30    if address is None:
31        return await keystore.print()
32
33    try:
34        await keystore.delete(address)
35    except KeyError:
36        print('!!! pairing not found')
37
38
39# -----------------------------------------------------------------------------
40async def unbond(keystore_file, device_config, hci_transport, address):
41    # With a keystore file, we can instantiate the keystore directly
42    if keystore_file:
43        return await unbond_with_keystore(JsonKeyStore(None, keystore_file), address)
44
45    # Without a keystore file, we need to obtain the keystore from the device
46    async with await open_transport(hci_transport) as (hci_source, hci_sink):
47        # Create a device to manage the host
48        device = Device.from_config_file_with_hci(device_config, hci_source, hci_sink)
49
50        # Power-on the device to ensure we have a key store
51        await device.power_on()
52
53        return await unbond_with_keystore(device.keystore, address)
54
55
56# -----------------------------------------------------------------------------
57@click.command()
58@click.option('--keystore-file', help='File in which the pairing keys are stored')
59@click.option('--hci-transport', help='HCI transport for the controller')
60@click.argument('device-config', required=False)
61@click.argument('address', required=False)
62def main(keystore_file, hci_transport, device_config, address):
63    """
64    Remove pairing keys for a device, given its address.
65
66    If no keystore file is specified, the --hci-transport option must be used to
67    connect to a controller, so that the keystore for that controller can be
68    instantiated.
69    If no address is passed, the existing pairing keys for all addresses are printed.
70    """
71    logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper())
72
73    if not keystore_file and not hci_transport:
74        print('either --keystore-file or --hci-transport must be specified.')
75        return
76
77    asyncio.run(unbond(keystore_file, device_config, hci_transport, address))
78
79
80# -----------------------------------------------------------------------------
81if __name__ == '__main__':
82    main()
83