mirror of
https://github.com/google/bumble.git
synced 2026-04-16 00:25:31 +00:00
83 lines
2.6 KiB
Python
83 lines
2.6 KiB
Python
# Copyright 2021-2025 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.
|
|
|
|
from __future__ import annotations
|
|
|
|
import functools
|
|
|
|
from cryptography.hazmat.primitives import ciphers, cmac
|
|
from cryptography.hazmat.primitives.asymmetric import ec
|
|
from cryptography.hazmat.primitives.ciphers import algorithms, modes
|
|
|
|
|
|
def e(key: bytes, data: bytes) -> bytes:
|
|
'''
|
|
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 = ciphers.Cipher(algorithms.AES(key[::-1]), modes.ECB())
|
|
encryptor = cipher.encryptor()
|
|
return encryptor.update(data[::-1])[::-1]
|
|
|
|
|
|
class EccKey:
|
|
def __init__(self, private_key: ec.EllipticCurvePrivateKey) -> None:
|
|
self.private_key = private_key
|
|
|
|
@classmethod
|
|
def generate(cls) -> EccKey:
|
|
return EccKey(ec.generate_private_key(ec.SECP256R1()))
|
|
|
|
@classmethod
|
|
def from_private_key_bytes(cls, d_bytes: bytes) -> EccKey:
|
|
d = int.from_bytes(d_bytes, byteorder='big', signed=False)
|
|
return EccKey(ec.derive_private_key(d, ec.SECP256R1()))
|
|
|
|
@functools.cached_property
|
|
def x(self) -> bytes:
|
|
return (
|
|
self.private_key.public_key()
|
|
.public_numbers()
|
|
.x.to_bytes(32, byteorder='big')
|
|
)
|
|
|
|
@functools.cached_property
|
|
def y(self) -> bytes:
|
|
return (
|
|
self.private_key.public_key()
|
|
.public_numbers()
|
|
.y.to_bytes(32, byteorder='big')
|
|
)
|
|
|
|
def dh(self, public_key_x: bytes, public_key_y: bytes) -> bytes:
|
|
x = int.from_bytes(public_key_x, byteorder='big', signed=False)
|
|
y = int.from_bytes(public_key_y, byteorder='big', signed=False)
|
|
return self.private_key.exchange(
|
|
ec.ECDH(),
|
|
ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1()).public_key(),
|
|
)
|
|
|
|
|
|
def aes_cmac(m: bytes, k: bytes) -> bytes:
|
|
'''
|
|
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()
|