mirror of
https://github.com/google/bumble.git
synced 2026-04-16 00:25:31 +00:00
Squashed commits: [cd479ba] formatting and linting automation [7fbfabb] formatting and linting automation [c4f9505] fix after rebase [f506ad4] rename job [441d517] update doc (+7 squashed commits) [2e1b416] fix invoke and github action [6ae5bb4] doc for git blame [44b5461] add GitHub action [b07474f] add docs [4cd9a6f] more linter fixes [db71901] wip [540dc88] wip
278 lines
8.5 KiB
Python
278 lines
8.5 KiB
Python
# Copyright 2021-2022 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.
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Crypto support
|
|
#
|
|
# See Bluetooth spec Vol 3, Part H - 2.2 CRYPTOGRAPHIC TOOLBOX
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Imports
|
|
# -----------------------------------------------------------------------------
|
|
import logging
|
|
import operator
|
|
import platform
|
|
|
|
if platform.system() != 'Emscripten':
|
|
import secrets
|
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
from cryptography.hazmat.primitives.asymmetric.ec import (
|
|
generate_private_key,
|
|
ECDH,
|
|
EllipticCurvePublicNumbers,
|
|
EllipticCurvePrivateNumbers,
|
|
SECP256R1,
|
|
)
|
|
from cryptography.hazmat.primitives import cmac
|
|
else:
|
|
# TODO: implement stubs
|
|
pass
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Logging
|
|
# -----------------------------------------------------------------------------
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Classes
|
|
# -----------------------------------------------------------------------------
|
|
class EccKey:
|
|
def __init__(self, private_key):
|
|
self.private_key = private_key
|
|
|
|
@classmethod
|
|
def generate(cls):
|
|
private_key = generate_private_key(SECP256R1())
|
|
return cls(private_key)
|
|
|
|
@classmethod
|
|
def from_private_key_bytes(cls, d_bytes, x_bytes, y_bytes):
|
|
d = int.from_bytes(d_bytes, byteorder='big', signed=False)
|
|
x = int.from_bytes(x_bytes, byteorder='big', signed=False)
|
|
y = int.from_bytes(y_bytes, byteorder='big', signed=False)
|
|
private_key = EllipticCurvePrivateNumbers(
|
|
d, EllipticCurvePublicNumbers(x, y, SECP256R1())
|
|
).private_key()
|
|
return cls(private_key)
|
|
|
|
@property
|
|
def x(self):
|
|
return (
|
|
self.private_key.public_key()
|
|
.public_numbers()
|
|
.x.to_bytes(32, byteorder='big')
|
|
)
|
|
|
|
@property
|
|
def y(self):
|
|
return (
|
|
self.private_key.public_key()
|
|
.public_numbers()
|
|
.y.to_bytes(32, byteorder='big')
|
|
)
|
|
|
|
def dh(self, public_key_x, public_key_y):
|
|
x = int.from_bytes(public_key_x, byteorder='big', signed=False)
|
|
y = int.from_bytes(public_key_y, byteorder='big', signed=False)
|
|
public_key = EllipticCurvePublicNumbers(x, y, SECP256R1()).public_key()
|
|
shared_key = self.private_key.exchange(ECDH(), public_key)
|
|
|
|
return shared_key
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Functions
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def xor(x, y):
|
|
assert len(x) == len(y)
|
|
return bytes(map(operator.xor, x, y))
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def r():
|
|
'''
|
|
Generate 16 bytes of random data
|
|
'''
|
|
return secrets.token_bytes(16)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def e(key, data):
|
|
'''
|
|
AES-128 ECB, expecting byte-swapped inputs and producing a byte-swapped output.
|
|
|
|
See Bluetooth spec Vol 3, Part H - 2.2.1 Security function e
|
|
'''
|
|
|
|
cipher = Cipher(algorithms.AES(bytes(reversed(key))), modes.ECB())
|
|
encryptor = cipher.encryptor()
|
|
return bytes(reversed(encryptor.update(bytes(reversed(data)))))
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def ah(k, r): # pylint: disable=redefined-outer-name
|
|
'''
|
|
See Bluetooth spec Vol 3, Part H - 2.2.2 Random Address Hash function ah
|
|
'''
|
|
|
|
padding = bytes(13)
|
|
r_prime = r + padding
|
|
return e(k, r_prime)[0:3]
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def c1(k, r, preq, pres, iat, rat, ia, ra): # pylint: disable=redefined-outer-name
|
|
'''
|
|
See Bluetooth spec, Vol 3, Part H - 2.2.3 Confirm value generation function c1 for
|
|
LE Legacy Pairing
|
|
'''
|
|
|
|
p1 = bytes([iat, rat]) + preq + pres
|
|
p2 = ra + ia + bytes([0, 0, 0, 0])
|
|
return e(k, xor(e(k, xor(r, p1)), p2))
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def s1(k, r1, r2):
|
|
'''
|
|
See Bluetooth spec, Vol 3, Part H - 2.2.4 Key generation function s1 for LE Legacy
|
|
Pairing
|
|
'''
|
|
|
|
return e(k, r2[0:8] + r1[0:8])
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def aes_cmac(m, k):
|
|
'''
|
|
See Bluetooth spec, Vol 3, Part H - 2.2.5 FunctionAES-CMAC
|
|
|
|
NOTE: the input and output of this internal function are in big-endian byte order
|
|
'''
|
|
mac = cmac.CMAC(algorithms.AES(k))
|
|
mac.update(m)
|
|
return mac.finalize()
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def f4(u, v, x, z):
|
|
'''
|
|
See Bluetooth spec, Vol 3, Part H - 2.2.6 LE Secure Connections Confirm Value
|
|
Generation Function f4
|
|
'''
|
|
return bytes(
|
|
reversed(
|
|
aes_cmac(bytes(reversed(u)) + bytes(reversed(v)) + z, bytes(reversed(x)))
|
|
)
|
|
)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def f5(w, n1, n2, a1, a2):
|
|
'''
|
|
See Bluetooth spec, Vol 3, Part H - 2.2.7 LE Secure Connections Key Generation
|
|
Function f5
|
|
|
|
NOTE: this returns a tuple: (MacKey, LTK) in little-endian byte order
|
|
'''
|
|
salt = bytes.fromhex('6C888391AAF5A53860370BDB5A6083BE')
|
|
t = aes_cmac(bytes(reversed(w)), salt)
|
|
key_id = bytes([0x62, 0x74, 0x6C, 0x65])
|
|
return (
|
|
bytes(
|
|
reversed(
|
|
aes_cmac(
|
|
bytes([0])
|
|
+ key_id
|
|
+ bytes(reversed(n1))
|
|
+ bytes(reversed(n2))
|
|
+ bytes(reversed(a1))
|
|
+ bytes(reversed(a2))
|
|
+ bytes([1, 0]),
|
|
t,
|
|
)
|
|
)
|
|
),
|
|
bytes(
|
|
reversed(
|
|
aes_cmac(
|
|
bytes([1])
|
|
+ key_id
|
|
+ bytes(reversed(n1))
|
|
+ bytes(reversed(n2))
|
|
+ bytes(reversed(a1))
|
|
+ bytes(reversed(a2))
|
|
+ bytes([1, 0]),
|
|
t,
|
|
)
|
|
)
|
|
),
|
|
)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def f6(w, n1, n2, r, io_cap, a1, a2): # pylint: disable=redefined-outer-name
|
|
'''
|
|
See Bluetooth spec, Vol 3, Part H - 2.2.8 LE Secure Connections Check Value
|
|
Generation Function f6
|
|
'''
|
|
return bytes(
|
|
reversed(
|
|
aes_cmac(
|
|
bytes(reversed(n1))
|
|
+ bytes(reversed(n2))
|
|
+ bytes(reversed(r))
|
|
+ bytes(reversed(io_cap))
|
|
+ bytes(reversed(a1))
|
|
+ bytes(reversed(a2)),
|
|
bytes(reversed(w)),
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def g2(u, v, x, y):
|
|
'''
|
|
See Bluetooth spec, Vol 3, Part H - 2.2.9 LE Secure Connections Numeric Comparison
|
|
Value Generation Function g2
|
|
'''
|
|
return int.from_bytes(
|
|
aes_cmac(
|
|
bytes(reversed(u)) + bytes(reversed(v)) + bytes(reversed(y)),
|
|
bytes(reversed(x)),
|
|
)[-4:],
|
|
byteorder='big',
|
|
)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def h6(w, key_id):
|
|
'''
|
|
See Bluetooth spec, Vol 3, Part H - 2.2.10 Link key conversion function h6
|
|
'''
|
|
return aes_cmac(key_id, w)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
def h7(salt, w):
|
|
'''
|
|
See Bluetooth spec, Vol 3, Part H - 2.2.11 Link key conversion function h7
|
|
'''
|
|
return aes_cmac(w, salt)
|