bumble 0.0.147__py3-none-any.whl → 0.0.149__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bumble/_version.py +2 -2
- bumble/apps/bench.py +4 -2
- bumble/apps/console.py +213 -17
- bumble/apps/gg_bridge.py +3 -4
- bumble/apps/pair.py +21 -4
- bumble/att.py +4 -4
- bumble/device.py +128 -100
- bumble/gap.py +2 -2
- bumble/gatt.py +61 -51
- bumble/gatt_client.py +63 -10
- bumble/gatt_server.py +20 -2
- bumble/host.py +12 -17
- bumble/keys.py +27 -11
- bumble/pairing.py +184 -0
- bumble/profiles/asha_service.py +6 -5
- bumble/profiles/battery_service.py +1 -1
- bumble/profiles/device_information_service.py +5 -3
- bumble/profiles/heart_rate_service.py +3 -3
- bumble/rfcomm.py +1 -1
- bumble/smp.py +21 -88
- {bumble-0.0.147.dist-info → bumble-0.0.149.dist-info}/LICENSE +19 -0
- {bumble-0.0.147.dist-info → bumble-0.0.149.dist-info}/METADATA +3 -1
- {bumble-0.0.147.dist-info → bumble-0.0.149.dist-info}/RECORD +26 -25
- {bumble-0.0.147.dist-info → bumble-0.0.149.dist-info}/WHEEL +0 -0
- {bumble-0.0.147.dist-info → bumble-0.0.149.dist-info}/entry_points.txt +0 -0
- {bumble-0.0.147.dist-info → bumble-0.0.149.dist-info}/top_level.txt +0 -0
bumble/pairing.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Copyright 2021-2023 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
|
+
# -----------------------------------------------------------------------------
|
|
18
|
+
import enum
|
|
19
|
+
from typing import Optional, Tuple
|
|
20
|
+
|
|
21
|
+
from .hci import (
|
|
22
|
+
HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
|
|
23
|
+
HCI_DISPLAY_ONLY_IO_CAPABILITY,
|
|
24
|
+
HCI_DISPLAY_YES_NO_IO_CAPABILITY,
|
|
25
|
+
HCI_KEYBOARD_ONLY_IO_CAPABILITY,
|
|
26
|
+
)
|
|
27
|
+
from .smp import (
|
|
28
|
+
SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
|
|
29
|
+
SMP_KEYBOARD_ONLY_IO_CAPABILITY,
|
|
30
|
+
SMP_DISPLAY_ONLY_IO_CAPABILITY,
|
|
31
|
+
SMP_DISPLAY_YES_NO_IO_CAPABILITY,
|
|
32
|
+
SMP_KEYBOARD_DISPLAY_IO_CAPABILITY,
|
|
33
|
+
SMP_ENC_KEY_DISTRIBUTION_FLAG,
|
|
34
|
+
SMP_ID_KEY_DISTRIBUTION_FLAG,
|
|
35
|
+
SMP_SIGN_KEY_DISTRIBUTION_FLAG,
|
|
36
|
+
SMP_LINK_KEY_DISTRIBUTION_FLAG,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# -----------------------------------------------------------------------------
|
|
41
|
+
class PairingDelegate:
|
|
42
|
+
"""Abstract base class for Pairing Delegates."""
|
|
43
|
+
|
|
44
|
+
# I/O Capabilities.
|
|
45
|
+
# These are defined abstractly, and can be mapped to specific Classic pairing
|
|
46
|
+
# and/or SMP constants.
|
|
47
|
+
class IoCapability(enum.IntEnum):
|
|
48
|
+
NO_OUTPUT_NO_INPUT = SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY
|
|
49
|
+
KEYBOARD_INPUT_ONLY = SMP_KEYBOARD_ONLY_IO_CAPABILITY
|
|
50
|
+
DISPLAY_OUTPUT_ONLY = SMP_DISPLAY_ONLY_IO_CAPABILITY
|
|
51
|
+
DISPLAY_OUTPUT_AND_YES_NO_INPUT = SMP_DISPLAY_YES_NO_IO_CAPABILITY
|
|
52
|
+
DISPLAY_OUTPUT_AND_KEYBOARD_INPUT = SMP_KEYBOARD_DISPLAY_IO_CAPABILITY
|
|
53
|
+
|
|
54
|
+
# Direct names for backward compatibility.
|
|
55
|
+
NO_OUTPUT_NO_INPUT = IoCapability.NO_OUTPUT_NO_INPUT
|
|
56
|
+
KEYBOARD_INPUT_ONLY = IoCapability.KEYBOARD_INPUT_ONLY
|
|
57
|
+
DISPLAY_OUTPUT_ONLY = IoCapability.DISPLAY_OUTPUT_ONLY
|
|
58
|
+
DISPLAY_OUTPUT_AND_YES_NO_INPUT = IoCapability.DISPLAY_OUTPUT_AND_YES_NO_INPUT
|
|
59
|
+
DISPLAY_OUTPUT_AND_KEYBOARD_INPUT = IoCapability.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT
|
|
60
|
+
|
|
61
|
+
# Key Distribution [LE only]
|
|
62
|
+
class KeyDistribution(enum.IntFlag):
|
|
63
|
+
DISTRIBUTE_ENCRYPTION_KEY = SMP_ENC_KEY_DISTRIBUTION_FLAG
|
|
64
|
+
DISTRIBUTE_IDENTITY_KEY = SMP_ID_KEY_DISTRIBUTION_FLAG
|
|
65
|
+
DISTRIBUTE_SIGNING_KEY = SMP_SIGN_KEY_DISTRIBUTION_FLAG
|
|
66
|
+
DISTRIBUTE_LINK_KEY = SMP_LINK_KEY_DISTRIBUTION_FLAG
|
|
67
|
+
|
|
68
|
+
DEFAULT_KEY_DISTRIBUTION: int = (
|
|
69
|
+
SMP_ENC_KEY_DISTRIBUTION_FLAG | SMP_ID_KEY_DISTRIBUTION_FLAG
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Default mapping from abstract to Classic I/O capabilities.
|
|
73
|
+
# Subclasses may override this if they prefer a different mapping.
|
|
74
|
+
CLASSIC_IO_CAPABILITIES_MAP = {
|
|
75
|
+
NO_OUTPUT_NO_INPUT: HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
|
|
76
|
+
KEYBOARD_INPUT_ONLY: HCI_KEYBOARD_ONLY_IO_CAPABILITY,
|
|
77
|
+
DISPLAY_OUTPUT_ONLY: HCI_DISPLAY_ONLY_IO_CAPABILITY,
|
|
78
|
+
DISPLAY_OUTPUT_AND_YES_NO_INPUT: HCI_DISPLAY_YES_NO_IO_CAPABILITY,
|
|
79
|
+
DISPLAY_OUTPUT_AND_KEYBOARD_INPUT: HCI_DISPLAY_YES_NO_IO_CAPABILITY,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
io_capability: IoCapability
|
|
83
|
+
local_initiator_key_distribution: KeyDistribution
|
|
84
|
+
local_responder_key_distribution: KeyDistribution
|
|
85
|
+
|
|
86
|
+
def __init__(
|
|
87
|
+
self,
|
|
88
|
+
io_capability=NO_OUTPUT_NO_INPUT,
|
|
89
|
+
local_initiator_key_distribution=DEFAULT_KEY_DISTRIBUTION,
|
|
90
|
+
local_responder_key_distribution=DEFAULT_KEY_DISTRIBUTION,
|
|
91
|
+
) -> None:
|
|
92
|
+
self.io_capability = io_capability
|
|
93
|
+
self.local_initiator_key_distribution = local_initiator_key_distribution
|
|
94
|
+
self.local_responder_key_distribution = local_responder_key_distribution
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def classic_io_capability(self) -> int:
|
|
98
|
+
"""Map the abstract I/O capability to a Classic constant."""
|
|
99
|
+
|
|
100
|
+
# pylint: disable=line-too-long
|
|
101
|
+
return self.CLASSIC_IO_CAPABILITIES_MAP.get(
|
|
102
|
+
self.io_capability, HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def smp_io_capability(self) -> int:
|
|
107
|
+
"""Map the abstract I/O capability to an SMP constant."""
|
|
108
|
+
|
|
109
|
+
# This is just a 1-1 direct mapping
|
|
110
|
+
return self.io_capability
|
|
111
|
+
|
|
112
|
+
async def accept(self) -> bool:
|
|
113
|
+
"""Accept or reject a Pairing request."""
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
async def confirm(self) -> bool:
|
|
117
|
+
"""Respond yes or no to a Pairing confirmation question."""
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
# pylint: disable-next=unused-argument
|
|
121
|
+
async def compare_numbers(self, number: int, digits: int) -> bool:
|
|
122
|
+
"""Compare two numbers."""
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
async def get_number(self) -> Optional[int]:
|
|
126
|
+
"""
|
|
127
|
+
Return an optional number as an answer to a passkey request.
|
|
128
|
+
Returning `None` will result in a negative reply.
|
|
129
|
+
"""
|
|
130
|
+
return 0
|
|
131
|
+
|
|
132
|
+
async def get_string(self, max_length) -> Optional[str]:
|
|
133
|
+
"""
|
|
134
|
+
Return a string whose utf-8 encoding is up to max_length bytes.
|
|
135
|
+
"""
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
# pylint: disable-next=unused-argument
|
|
139
|
+
async def display_number(self, number: int, digits: int) -> None:
|
|
140
|
+
"""Display a number."""
|
|
141
|
+
|
|
142
|
+
# [LE only]
|
|
143
|
+
async def key_distribution_response(
|
|
144
|
+
self, peer_initiator_key_distribution: int, peer_responder_key_distribution: int
|
|
145
|
+
) -> Tuple[int, int]:
|
|
146
|
+
"""
|
|
147
|
+
Return the key distribution response in an SMP protocol context.
|
|
148
|
+
|
|
149
|
+
NOTE: since it is only used by the SMP protocol, this method's input and output
|
|
150
|
+
are directly as integers, using the SMP constants, rather than the abstract
|
|
151
|
+
KeyDistribution enums.
|
|
152
|
+
"""
|
|
153
|
+
return (
|
|
154
|
+
int(
|
|
155
|
+
peer_initiator_key_distribution & self.local_initiator_key_distribution
|
|
156
|
+
),
|
|
157
|
+
int(
|
|
158
|
+
peer_responder_key_distribution & self.local_responder_key_distribution
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# -----------------------------------------------------------------------------
|
|
164
|
+
class PairingConfig:
|
|
165
|
+
"""Configuration for the Pairing protocol."""
|
|
166
|
+
|
|
167
|
+
def __init__(
|
|
168
|
+
self,
|
|
169
|
+
sc: bool = True,
|
|
170
|
+
mitm: bool = True,
|
|
171
|
+
bonding: bool = True,
|
|
172
|
+
delegate: Optional[PairingDelegate] = None,
|
|
173
|
+
) -> None:
|
|
174
|
+
self.sc = sc
|
|
175
|
+
self.mitm = mitm
|
|
176
|
+
self.bonding = bonding
|
|
177
|
+
self.delegate = delegate or PairingDelegate()
|
|
178
|
+
|
|
179
|
+
def __str__(self) -> str:
|
|
180
|
+
return (
|
|
181
|
+
f'PairingConfig(sc={self.sc}, '
|
|
182
|
+
f'mitm={self.mitm}, bonding={self.bonding}, '
|
|
183
|
+
f'delegate[{self.delegate.io_capability}])'
|
|
184
|
+
)
|
bumble/profiles/asha_service.py
CHANGED
|
@@ -103,7 +103,7 @@ class AshaService(TemplateService):
|
|
|
103
103
|
|
|
104
104
|
self.read_only_properties_characteristic = Characteristic(
|
|
105
105
|
GATT_ASHA_READ_ONLY_PROPERTIES_CHARACTERISTIC,
|
|
106
|
-
Characteristic.READ,
|
|
106
|
+
Characteristic.Properties.READ,
|
|
107
107
|
Characteristic.READABLE,
|
|
108
108
|
bytes(
|
|
109
109
|
[
|
|
@@ -120,19 +120,20 @@ class AshaService(TemplateService):
|
|
|
120
120
|
|
|
121
121
|
self.audio_control_point_characteristic = Characteristic(
|
|
122
122
|
GATT_ASHA_AUDIO_CONTROL_POINT_CHARACTERISTIC,
|
|
123
|
-
Characteristic.WRITE
|
|
123
|
+
Characteristic.Properties.WRITE
|
|
124
|
+
| Characteristic.Properties.WRITE_WITHOUT_RESPONSE,
|
|
124
125
|
Characteristic.WRITEABLE,
|
|
125
126
|
CharacteristicValue(write=on_audio_control_point_write),
|
|
126
127
|
)
|
|
127
128
|
self.audio_status_characteristic = Characteristic(
|
|
128
129
|
GATT_ASHA_AUDIO_STATUS_CHARACTERISTIC,
|
|
129
|
-
Characteristic.READ | Characteristic.NOTIFY,
|
|
130
|
+
Characteristic.Properties.READ | Characteristic.Properties.NOTIFY,
|
|
130
131
|
Characteristic.READABLE,
|
|
131
132
|
bytes([0]),
|
|
132
133
|
)
|
|
133
134
|
self.volume_characteristic = Characteristic(
|
|
134
135
|
GATT_ASHA_VOLUME_CHARACTERISTIC,
|
|
135
|
-
Characteristic.WRITE_WITHOUT_RESPONSE,
|
|
136
|
+
Characteristic.Properties.WRITE_WITHOUT_RESPONSE,
|
|
136
137
|
Characteristic.WRITEABLE,
|
|
137
138
|
CharacteristicValue(write=on_volume_write),
|
|
138
139
|
)
|
|
@@ -151,7 +152,7 @@ class AshaService(TemplateService):
|
|
|
151
152
|
self.psm = self.device.register_l2cap_channel_server(self.psm, on_coc, 8)
|
|
152
153
|
self.le_psm_out_characteristic = Characteristic(
|
|
153
154
|
GATT_ASHA_LE_PSM_OUT_CHARACTERISTIC,
|
|
154
|
-
Characteristic.READ,
|
|
155
|
+
Characteristic.Properties.READ,
|
|
155
156
|
Characteristic.READABLE,
|
|
156
157
|
struct.pack('<H', self.psm),
|
|
157
158
|
)
|
|
@@ -36,7 +36,7 @@ class BatteryService(TemplateService):
|
|
|
36
36
|
self.battery_level_characteristic = PackedCharacteristicAdapter(
|
|
37
37
|
Characteristic(
|
|
38
38
|
GATT_BATTERY_LEVEL_CHARACTERISTIC,
|
|
39
|
-
Characteristic.READ | Characteristic.NOTIFY,
|
|
39
|
+
Characteristic.Properties.READ | Characteristic.Properties.NOTIFY,
|
|
40
40
|
Characteristic.READABLE,
|
|
41
41
|
CharacteristicValue(read=read_battery_level),
|
|
42
42
|
),
|
|
@@ -63,7 +63,9 @@ class DeviceInformationService(TemplateService):
|
|
|
63
63
|
# TODO: pnp_id
|
|
64
64
|
):
|
|
65
65
|
characteristics = [
|
|
66
|
-
Characteristic(
|
|
66
|
+
Characteristic(
|
|
67
|
+
uuid, Characteristic.Properties.READ, Characteristic.READABLE, field
|
|
68
|
+
)
|
|
67
69
|
for (field, uuid) in (
|
|
68
70
|
(manufacturer_name, GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC),
|
|
69
71
|
(model_number, GATT_MODEL_NUMBER_STRING_CHARACTERISTIC),
|
|
@@ -79,7 +81,7 @@ class DeviceInformationService(TemplateService):
|
|
|
79
81
|
characteristics.append(
|
|
80
82
|
Characteristic(
|
|
81
83
|
GATT_SYSTEM_ID_CHARACTERISTIC,
|
|
82
|
-
Characteristic.READ,
|
|
84
|
+
Characteristic.Properties.READ,
|
|
83
85
|
Characteristic.READABLE,
|
|
84
86
|
self.pack_system_id(*system_id),
|
|
85
87
|
)
|
|
@@ -89,7 +91,7 @@ class DeviceInformationService(TemplateService):
|
|
|
89
91
|
characteristics.append(
|
|
90
92
|
Characteristic(
|
|
91
93
|
GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC,
|
|
92
|
-
Characteristic.READ,
|
|
94
|
+
Characteristic.Properties.READ,
|
|
93
95
|
Characteristic.READABLE,
|
|
94
96
|
ieee_regulatory_certification_data_list,
|
|
95
97
|
)
|
|
@@ -152,7 +152,7 @@ class HeartRateService(TemplateService):
|
|
|
152
152
|
self.heart_rate_measurement_characteristic = DelegatedCharacteristicAdapter(
|
|
153
153
|
Characteristic(
|
|
154
154
|
GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC,
|
|
155
|
-
Characteristic.NOTIFY,
|
|
155
|
+
Characteristic.Properties.NOTIFY,
|
|
156
156
|
0,
|
|
157
157
|
CharacteristicValue(read=read_heart_rate_measurement),
|
|
158
158
|
),
|
|
@@ -164,7 +164,7 @@ class HeartRateService(TemplateService):
|
|
|
164
164
|
if body_sensor_location is not None:
|
|
165
165
|
self.body_sensor_location_characteristic = Characteristic(
|
|
166
166
|
GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC,
|
|
167
|
-
Characteristic.READ,
|
|
167
|
+
Characteristic.Properties.READ,
|
|
168
168
|
Characteristic.READABLE,
|
|
169
169
|
bytes([int(body_sensor_location)]),
|
|
170
170
|
)
|
|
@@ -182,7 +182,7 @@ class HeartRateService(TemplateService):
|
|
|
182
182
|
self.heart_rate_control_point_characteristic = PackedCharacteristicAdapter(
|
|
183
183
|
Characteristic(
|
|
184
184
|
GATT_HEART_RATE_CONTROL_POINT_CHARACTERISTIC,
|
|
185
|
-
Characteristic.WRITE,
|
|
185
|
+
Characteristic.Properties.WRITE,
|
|
186
186
|
Characteristic.WRITEABLE,
|
|
187
187
|
CharacteristicValue(write=write_heart_rate_control_point_value),
|
|
188
188
|
),
|
bumble/rfcomm.py
CHANGED
bumble/smp.py
CHANGED
|
@@ -31,7 +31,16 @@ from typing import Dict, Optional, Type
|
|
|
31
31
|
from pyee import EventEmitter
|
|
32
32
|
|
|
33
33
|
from .colors import color
|
|
34
|
-
from .hci import
|
|
34
|
+
from .hci import (
|
|
35
|
+
HCI_DISPLAY_ONLY_IO_CAPABILITY,
|
|
36
|
+
HCI_DISPLAY_YES_NO_IO_CAPABILITY,
|
|
37
|
+
HCI_KEYBOARD_ONLY_IO_CAPABILITY,
|
|
38
|
+
HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY,
|
|
39
|
+
Address,
|
|
40
|
+
HCI_LE_Enable_Encryption_Command,
|
|
41
|
+
HCI_Object,
|
|
42
|
+
key_with_value,
|
|
43
|
+
)
|
|
35
44
|
from .core import (
|
|
36
45
|
BT_BR_EDR_TRANSPORT,
|
|
37
46
|
BT_CENTRAL_ROLE,
|
|
@@ -476,7 +485,7 @@ class AddressResolver:
|
|
|
476
485
|
address_bytes = bytes(address)
|
|
477
486
|
hash_part = address_bytes[0:3]
|
|
478
487
|
prand = address_bytes[3:6]
|
|
479
|
-
for
|
|
488
|
+
for irk, resolved_address in self.resolving_keys:
|
|
480
489
|
local_hash = crypto.ah(irk, prand)
|
|
481
490
|
if local_hash == hash_part:
|
|
482
491
|
# Match!
|
|
@@ -491,86 +500,6 @@ class AddressResolver:
|
|
|
491
500
|
return None
|
|
492
501
|
|
|
493
502
|
|
|
494
|
-
# -----------------------------------------------------------------------------
|
|
495
|
-
class PairingDelegate:
|
|
496
|
-
NO_OUTPUT_NO_INPUT = SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY
|
|
497
|
-
KEYBOARD_INPUT_ONLY = SMP_KEYBOARD_ONLY_IO_CAPABILITY
|
|
498
|
-
DISPLAY_OUTPUT_ONLY = SMP_DISPLAY_ONLY_IO_CAPABILITY
|
|
499
|
-
DISPLAY_OUTPUT_AND_YES_NO_INPUT = SMP_DISPLAY_YES_NO_IO_CAPABILITY
|
|
500
|
-
DISPLAY_OUTPUT_AND_KEYBOARD_INPUT = SMP_KEYBOARD_DISPLAY_IO_CAPABILITY
|
|
501
|
-
DEFAULT_KEY_DISTRIBUTION: int = (
|
|
502
|
-
SMP_ENC_KEY_DISTRIBUTION_FLAG | SMP_ID_KEY_DISTRIBUTION_FLAG
|
|
503
|
-
)
|
|
504
|
-
|
|
505
|
-
def __init__(
|
|
506
|
-
self,
|
|
507
|
-
io_capability: int = NO_OUTPUT_NO_INPUT,
|
|
508
|
-
local_initiator_key_distribution: int = DEFAULT_KEY_DISTRIBUTION,
|
|
509
|
-
local_responder_key_distribution: int = DEFAULT_KEY_DISTRIBUTION,
|
|
510
|
-
) -> None:
|
|
511
|
-
self.io_capability = io_capability
|
|
512
|
-
self.local_initiator_key_distribution = local_initiator_key_distribution
|
|
513
|
-
self.local_responder_key_distribution = local_responder_key_distribution
|
|
514
|
-
|
|
515
|
-
async def accept(self) -> bool:
|
|
516
|
-
return True
|
|
517
|
-
|
|
518
|
-
async def confirm(self) -> bool:
|
|
519
|
-
return True
|
|
520
|
-
|
|
521
|
-
# pylint: disable-next=unused-argument
|
|
522
|
-
async def compare_numbers(self, number: int, digits: int) -> bool:
|
|
523
|
-
return True
|
|
524
|
-
|
|
525
|
-
async def get_number(self) -> Optional[int]:
|
|
526
|
-
'''
|
|
527
|
-
Returns an optional number as an answer to a passkey request.
|
|
528
|
-
Returning `None` will result in a negative reply.
|
|
529
|
-
'''
|
|
530
|
-
return 0
|
|
531
|
-
|
|
532
|
-
async def get_string(self, max_length) -> Optional[str]:
|
|
533
|
-
'''
|
|
534
|
-
Returns a string whose utf-8 encoding is up to max_length bytes.
|
|
535
|
-
'''
|
|
536
|
-
return None
|
|
537
|
-
|
|
538
|
-
# pylint: disable-next=unused-argument
|
|
539
|
-
async def display_number(self, number: int, digits: int) -> None:
|
|
540
|
-
pass
|
|
541
|
-
|
|
542
|
-
async def key_distribution_response(
|
|
543
|
-
self, peer_initiator_key_distribution, peer_responder_key_distribution
|
|
544
|
-
):
|
|
545
|
-
return (
|
|
546
|
-
(peer_initiator_key_distribution & self.local_initiator_key_distribution),
|
|
547
|
-
(peer_responder_key_distribution & self.local_responder_key_distribution),
|
|
548
|
-
)
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
# -----------------------------------------------------------------------------
|
|
552
|
-
class PairingConfig:
|
|
553
|
-
def __init__(
|
|
554
|
-
self,
|
|
555
|
-
sc: bool = True,
|
|
556
|
-
mitm: bool = True,
|
|
557
|
-
bonding: bool = True,
|
|
558
|
-
delegate: Optional[PairingDelegate] = None,
|
|
559
|
-
) -> None:
|
|
560
|
-
self.sc = sc
|
|
561
|
-
self.mitm = mitm
|
|
562
|
-
self.bonding = bonding
|
|
563
|
-
self.delegate = delegate or PairingDelegate()
|
|
564
|
-
|
|
565
|
-
def __str__(self):
|
|
566
|
-
io_capability_str = SMP_Command.io_capability_name(self.delegate.io_capability)
|
|
567
|
-
return (
|
|
568
|
-
f'PairingConfig(sc={self.sc}, '
|
|
569
|
-
f'mitm={self.mitm}, bonding={self.bonding}, '
|
|
570
|
-
f'delegate[{io_capability_str}])'
|
|
571
|
-
)
|
|
572
|
-
|
|
573
|
-
|
|
574
503
|
# -----------------------------------------------------------------------------
|
|
575
504
|
class Session:
|
|
576
505
|
# Pairing methods
|
|
@@ -645,7 +574,7 @@ class Session:
|
|
|
645
574
|
},
|
|
646
575
|
}
|
|
647
576
|
|
|
648
|
-
def __init__(self, manager, connection, pairing_config):
|
|
577
|
+
def __init__(self, manager, connection, pairing_config, is_initiator):
|
|
649
578
|
self.manager = manager
|
|
650
579
|
self.connection = connection
|
|
651
580
|
self.preq = None
|
|
@@ -684,7 +613,7 @@ class Session:
|
|
|
684
613
|
self.ctkd_task = None
|
|
685
614
|
|
|
686
615
|
# Decide if we're the initiator or the responder
|
|
687
|
-
self.is_initiator =
|
|
616
|
+
self.is_initiator = is_initiator
|
|
688
617
|
self.is_responder = not self.is_initiator
|
|
689
618
|
|
|
690
619
|
# Listen for connection events
|
|
@@ -1662,12 +1591,12 @@ class Manager(EventEmitter):
|
|
|
1662
1591
|
Implements the Initiator and Responder roles of the Security Manager Protocol
|
|
1663
1592
|
'''
|
|
1664
1593
|
|
|
1665
|
-
def __init__(self, device):
|
|
1594
|
+
def __init__(self, device, pairing_config_factory):
|
|
1666
1595
|
super().__init__()
|
|
1667
1596
|
self.device = device
|
|
1668
1597
|
self.sessions = {}
|
|
1669
1598
|
self._ecc_key = None
|
|
1670
|
-
self.pairing_config_factory =
|
|
1599
|
+
self.pairing_config_factory = pairing_config_factory
|
|
1671
1600
|
|
|
1672
1601
|
def send_command(self, connection, command):
|
|
1673
1602
|
logger.debug(
|
|
@@ -1680,6 +1609,8 @@ class Manager(EventEmitter):
|
|
|
1680
1609
|
def on_smp_pdu(self, connection, pdu):
|
|
1681
1610
|
# Look for a session with this connection, and create one if none exists
|
|
1682
1611
|
if not (session := self.sessions.get(connection.handle)):
|
|
1612
|
+
if connection.role == BT_CENTRAL_ROLE:
|
|
1613
|
+
logger.warning('Remote starts pairing as Peripheral!')
|
|
1683
1614
|
pairing_config = self.pairing_config_factory(connection)
|
|
1684
1615
|
if pairing_config is None:
|
|
1685
1616
|
# Pairing disabled
|
|
@@ -1688,7 +1619,7 @@ class Manager(EventEmitter):
|
|
|
1688
1619
|
SMP_Pairing_Failed_Command(reason=SMP_PAIRING_NOT_SUPPORTED_ERROR),
|
|
1689
1620
|
)
|
|
1690
1621
|
return
|
|
1691
|
-
session = Session(self, connection, pairing_config)
|
|
1622
|
+
session = Session(self, connection, pairing_config, is_initiator=False)
|
|
1692
1623
|
self.sessions[connection.handle] = session
|
|
1693
1624
|
|
|
1694
1625
|
# Parse the L2CAP payload into an SMP Command object
|
|
@@ -1709,10 +1640,12 @@ class Manager(EventEmitter):
|
|
|
1709
1640
|
|
|
1710
1641
|
async def pair(self, connection):
|
|
1711
1642
|
# TODO: check if there's already a session for this connection
|
|
1643
|
+
if connection.role != BT_CENTRAL_ROLE:
|
|
1644
|
+
logger.warning('Start pairing as Peripheral!')
|
|
1712
1645
|
pairing_config = self.pairing_config_factory(connection)
|
|
1713
1646
|
if pairing_config is None:
|
|
1714
1647
|
raise ValueError('pairing config must not be None when initiating')
|
|
1715
|
-
session = Session(self, connection, pairing_config)
|
|
1648
|
+
session = Session(self, connection, pairing_config, is_initiator=True)
|
|
1716
1649
|
self.sessions[connection.handle] = session
|
|
1717
1650
|
return await session.pair()
|
|
1718
1651
|
|
|
@@ -200,3 +200,22 @@
|
|
|
200
200
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
201
|
See the License for the specific language governing permissions and
|
|
202
202
|
limitations under the License.
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
Files: bumble/colors.py
|
|
209
|
+
Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com>
|
|
210
|
+
|
|
211
|
+
Permission to use, copy, modify, and distribute this software for any
|
|
212
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
213
|
+
copyright notice and this permission notice appear in all copies.
|
|
214
|
+
|
|
215
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
216
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
217
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
218
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
219
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
220
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
221
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bumble
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.149
|
|
4
4
|
Summary: Bluetooth Stack for Apps, Emulation, Test and Experimentation
|
|
5
5
|
Home-page: https://github.com/google/bumble
|
|
6
6
|
Author: Google
|
|
@@ -11,6 +11,8 @@ License-File: LICENSE
|
|
|
11
11
|
Requires-Dist: appdirs (>=1.4)
|
|
12
12
|
Requires-Dist: protobuf (>=3.12.4)
|
|
13
13
|
Requires-Dist: pyee (>=8.2.2)
|
|
14
|
+
Requires-Dist: prettytable (>=3.6.0)
|
|
15
|
+
Requires-Dist: humanize (>=4.6.0)
|
|
14
16
|
Requires-Dist: click (>=7.1.2) ; platform_system != "Emscripten"
|
|
15
17
|
Requires-Dist: cryptography (==35) ; platform_system != "Emscripten"
|
|
16
18
|
Requires-Dist: grpcio (>=1.46) ; platform_system != "Emscripten"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
bumble/__init__.py,sha256=Q8jkz6rgl95IMAeInQVt_2GLoJl3DcEP2cxtrQ-ho5c,110
|
|
2
|
-
bumble/_version.py,sha256=
|
|
2
|
+
bumble/_version.py,sha256=j0GOlQtZQxiYwB0F8H5T6P-quL9GhBu9Qr0FPc6jw70,164
|
|
3
3
|
bumble/a2dp.py,sha256=MyCieGd2rA--nwpEdi1PsMXxfcki06ut6x7RXZUcO-w,21904
|
|
4
|
-
bumble/att.py,sha256=
|
|
4
|
+
bumble/att.py,sha256=qXwC5gtC-4Bwjqul0sCm_bWA_BbK4NVf5xNlOCRE7LI,30231
|
|
5
5
|
bumble/avdtp.py,sha256=LVX536sds4M5IJoN46pbX7wZIcJG5DzunZ55KPjquiQ,71471
|
|
6
6
|
bumble/bridge.py,sha256=T6es5oS1dy8QgkxQ8iOD-YcZ0SWOv8jaqC7TGxqodk4,3003
|
|
7
7
|
bumble/colors.py,sha256=9H-qzGgMr-YoWdIFpcGPaiRvTvkCzz7FPIkpwqKyKug,3033
|
|
@@ -10,35 +10,36 @@ bumble/controller.py,sha256=YwOVfcsPD8BvzXqfswY-wUECym2BX7_8zEBjIjUnhWw,44266
|
|
|
10
10
|
bumble/core.py,sha256=KE_ymW8JLQ3X2R0xvwk0ClRD1hM4R97YuM7Az0xEbMI,48410
|
|
11
11
|
bumble/crypto.py,sha256=JX3wg6kd-aFMxkxJz2FYFvyXHOukD-z0QcGH9jpEYvk,8679
|
|
12
12
|
bumble/decoder.py,sha256=N9nMvuVhuwpnfw7EDVuNe9uYY6B6c3RY2dh8RhRPC1U,9608
|
|
13
|
-
bumble/device.py,sha256=
|
|
14
|
-
bumble/gap.py,sha256=
|
|
15
|
-
bumble/gatt.py,sha256=
|
|
16
|
-
bumble/gatt_client.py,sha256=
|
|
17
|
-
bumble/gatt_server.py,sha256=
|
|
13
|
+
bumble/device.py,sha256=Ymyvq1fiHpmF_nkO-nnW2dlhxVvKqJ0tZTz-wnXpOnw,120201
|
|
14
|
+
bumble/gap.py,sha256=axlOZIv99357Ehq2vMokeioU85z81qdQvplGn0pF70Q,2137
|
|
15
|
+
bumble/gatt.py,sha256=s7FFeIuN0VRAWZ3qeobW7zjvluY66ytQIRnh6lszhP0,27074
|
|
16
|
+
bumble/gatt_client.py,sha256=mAkYXeYKdr2PyGYsA9ZmrYonSYi9mc0ZeFm-CAcM1FA,37848
|
|
17
|
+
bumble/gatt_server.py,sha256=W1bzSToxJipqFBK9rPoU4ylCzrJMo5wE9fWMOUgR3WQ,34759
|
|
18
18
|
bumble/hci.py,sha256=NTLA7l5SI312Gp77yZNRsaduV95Az_UeUBnA4IH_NBE,209326
|
|
19
19
|
bumble/helpers.py,sha256=AgtOigAqQzzTEMMwHQJ2EO7dOTp77qhnyfHQ2LCDd-k,8896
|
|
20
20
|
bumble/hfp.py,sha256=GUOkQ2_earE9IiLXWa8uKugpEIyavEgm1tCSm8naGnI,3187
|
|
21
|
-
bumble/host.py,sha256=
|
|
22
|
-
bumble/keys.py,sha256=
|
|
21
|
+
bumble/host.py,sha256=ZeKsZg0qwqb-DmIZu5ZY6eMOGby0kXbcQHKvQDPit9g,33678
|
|
22
|
+
bumble/keys.py,sha256=1KO2Jo1_h7z5gk_WUu1EU93VBRTOGMWMnswQTVBRpaw,9945
|
|
23
23
|
bumble/l2cap.py,sha256=Md_1af1jEP0dXlXpVkaXINnnmElq423o5m9OJFBaiYk,72106
|
|
24
24
|
bumble/link.py,sha256=dvb3fdmV8689A4rLhFVHHKMR3u4wQbUapFi-GeheK0M,20220
|
|
25
|
+
bumble/pairing.py,sha256=4XVgjWc50kOIdDwTpJVi7675saYhdhpRBG22djzRmm4,6798
|
|
25
26
|
bumble/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
-
bumble/rfcomm.py,sha256=
|
|
27
|
+
bumble/rfcomm.py,sha256=r8Sl76HY-rQpctz7QYAKoEvg8VPcOtJngWdLjmRcrsA,29883
|
|
27
28
|
bumble/sdp.py,sha256=h0DyXOYpQ5OLzwVZsE4b5YZq_jxxu7m_QSbFkaPy9mQ,42464
|
|
28
|
-
bumble/smp.py,sha256=
|
|
29
|
+
bumble/smp.py,sha256=w9y8-1vGAMoIU3UsLYVuQPPtymhfRD8uhzO9hCzWtYQ,64702
|
|
29
30
|
bumble/snoop.py,sha256=_QfF36eylBW6Snd-_KYOwKaGiM8i_Ed-B5XoFIPt3Dg,5631
|
|
30
31
|
bumble/utils.py,sha256=x7AKW6QD35Dl1OkROLJ6TX4dNN6ajwFjW8w_BZGGcA4,10195
|
|
31
32
|
bumble/apps/README.md,sha256=XTwjRAY-EJWDXpl1V8K3Mw8B7kIqzUIUizRjVBVhoIE,1769
|
|
32
33
|
bumble/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
-
bumble/apps/bench.py,sha256=
|
|
34
|
-
bumble/apps/console.py,sha256=
|
|
34
|
+
bumble/apps/bench.py,sha256=Nm22A1PjrJFgjfHCTyw3crlMJFHRwyVgADa2uKtJgbE,38542
|
|
35
|
+
bumble/apps/console.py,sha256=Zko0fw4ByYsdGDOrtrXLX0id001Rq45nVImdYHxNKsQ,46055
|
|
35
36
|
bumble/apps/controller_info.py,sha256=h6tBnpVe02vvoF-cyrdvE4DqGcxSfE5jcEuvCKMlr_g,6507
|
|
36
37
|
bumble/apps/controllers.py,sha256=R6XJ1XpyuXlyqSCmI7PromVIcoYTcYfpmO-TqTYXnUI,2326
|
|
37
38
|
bumble/apps/gatt_dump.py,sha256=-dCvCgjuHAp0h1zxm-gmqB4lVlSdas1Kp4cpzzx4gGw,4245
|
|
38
|
-
bumble/apps/gg_bridge.py,sha256=
|
|
39
|
+
bumble/apps/gg_bridge.py,sha256=G32XdCqYzt8Kqv9jZZDY29-ENjOxDMX_7CPolA8Jm1U,14555
|
|
39
40
|
bumble/apps/hci_bridge.py,sha256=KISv352tKnsQsoxjkDiCQbMFmhnPWdnug5wSFAAXxEs,4033
|
|
40
41
|
bumble/apps/l2cap_bridge.py,sha256=9914J7UomNtdWyE3VPgvzfJvZ304OM9AW9jzO_zRwNY,12604
|
|
41
|
-
bumble/apps/pair.py,sha256=
|
|
42
|
+
bumble/apps/pair.py,sha256=vorcWV98u0ZRVVhh-lF2-QOuTutaJ94GO_jCgXdIJZc,14776
|
|
42
43
|
bumble/apps/scan.py,sha256=TBjeEWkyLDDdIYeq2HRA9oZ2xv5-_Ja18Mt_nG9WkvA,7467
|
|
43
44
|
bumble/apps/show.py,sha256=qvCY0RcACws8iU5EKPQ3GcURJkykvVvBi8EMAu5GOus,4385
|
|
44
45
|
bumble/apps/unbond.py,sha256=-PXf6Jk4v-CQiogxhol6CjJ4mMWCApLFkbNHDuclUNk,2096
|
|
@@ -47,10 +48,10 @@ bumble/apps/link_relay/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
|
47
48
|
bumble/apps/link_relay/link_relay.py,sha256=GOESYPzkMJFrz5VOI_BSmnmgz4Y8EdSLHMWgdA63aDg,10066
|
|
48
49
|
bumble/apps/link_relay/logging.yml,sha256=t-P72RAHsTZOESw0M4I3CQPonymcjkd9SLE0eY_ZNiM,311
|
|
49
50
|
bumble/profiles/__init__.py,sha256=yBGC8Ti5LvZuoh1F42XtfrBilb39T77_yuxESZeX2yI,581
|
|
50
|
-
bumble/profiles/asha_service.py,sha256=
|
|
51
|
-
bumble/profiles/battery_service.py,sha256=
|
|
52
|
-
bumble/profiles/device_information_service.py,sha256=
|
|
53
|
-
bumble/profiles/heart_rate_service.py,sha256=
|
|
51
|
+
bumble/profiles/asha_service.py,sha256=C7-xwG1DgdPbt6IKLglIP1VYAg1X5s61YPAiOzHHgVw,7089
|
|
52
|
+
bumble/profiles/battery_service.py,sha256=w-uF4jLoDozJOoykimb2RkrKjVyCke6ts2-h-F1PYyc,2292
|
|
53
|
+
bumble/profiles/device_information_service.py,sha256=H1Db4BAOnsC-rRtfpoAIsDETYT4F9yM_WgByn_3LfRQ,5658
|
|
54
|
+
bumble/profiles/heart_rate_service.py,sha256=Xv-KzgXlagOwu8NfiUGl1RJtR6hJC9P8WefynSIShiw,8593
|
|
54
55
|
bumble/profiles/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
56
|
bumble/transport/__init__.py,sha256=wlqIXIsTcZxISlPBYeJFPMj7IruOJVUHmlQX6AjZpkM,5647
|
|
56
57
|
bumble/transport/android_emulator.py,sha256=aGbv4PKKBPUv7Ecx5UuxMz23gexUEY_TEGQ4RTXkPhc,4023
|
|
@@ -77,9 +78,9 @@ bumble/transport/usb.py,sha256=PWTrvdcyQ3OWXgygbuDMMZJleGFaxG6lSsz2gScLVwk,20901
|
|
|
77
78
|
bumble/transport/vhci.py,sha256=MbsRB1I1hPo-EytP_riNyKLtOZXRI9VkB_U4CRi8-1Y,2116
|
|
78
79
|
bumble/transport/ws_client.py,sha256=dtSMDWu2JQGUJYoXygAWuLgrIclGmT5onHJ8ggslY50,1684
|
|
79
80
|
bumble/transport/ws_server.py,sha256=H0g6ZvHvR-O0m4ptXmZLInN7N-JpxI_M65ZTvMYUjxc,3330
|
|
80
|
-
bumble-0.0.
|
|
81
|
-
bumble-0.0.
|
|
82
|
-
bumble-0.0.
|
|
83
|
-
bumble-0.0.
|
|
84
|
-
bumble-0.0.
|
|
85
|
-
bumble-0.0.
|
|
81
|
+
bumble-0.0.149.dist-info/LICENSE,sha256=FvaYh4NRWIGgS_OwoBs5gFgkCmAghZ-DYnIGBZPuw-s,12142
|
|
82
|
+
bumble-0.0.149.dist-info/METADATA,sha256=8QUOEPXZGGZeaCv8GN4pH-vPb9-R1SL6WhdGy4ZOh-o,5046
|
|
83
|
+
bumble-0.0.149.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
|
84
|
+
bumble-0.0.149.dist-info/entry_points.txt,sha256=hYqaVAX36lT2pak04gtRx5C6WQWdMFZqFwJnbE_7YRw,555
|
|
85
|
+
bumble-0.0.149.dist-info/top_level.txt,sha256=tV6JJKaHPYMFiJYiBYFW24PCcfLxTJZdlu6BmH3Cb00,7
|
|
86
|
+
bumble-0.0.149.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|