quantsini / pymoof

Connect to your Vanmoof SX3 using python
MIT License
37 stars 8 forks source link

Connection Rejected Due To Security Reasons #2

Closed oischinger closed 2 years ago

oischinger commented 2 years ago

Hi there! Thanks for this nice project. I was starting to play with it but stumbled upon an issue when accessing e.g. battery level.

I can successfully discover and authenticate my bike but then it fails with this exception:

bleak.exc.BleakDBusError: [org.bluez.Error.Failed] Operation failed with ATT error: 0x0e (Connection Rejected Due To Security Reasons)

First of all here's my setup: Version of pymoof: Latest master Hardware: Raspberry PI 3B OS: Home Assistant OS (I'm trying to create a docker container/Home Assistant Addon which gives me the battery lavel via mqtt) Bike: Vanmoof S3

I ran this simple python code:

from pymoof.clients.sx3 import SX3Client
import bleak
import asyncio

device = "F8:94:XX:XX:XX:XX"
key = "XXXXXXXXXXXXXXXXXXX"

async def example():
    async with bleak.BleakClient(device) as bleak_client:
        client = SX3Client(bleak_client, key)
        await client.authenticate()
        battery = await client.get_battery_level()
        print(str(battery))

asyncio.run(example())

Here's the full exception:

Traceback (most recent call last):
  File "test.py", line 16, in <module>
    asyncio.run(example())
  File "/usr/lib/python3.7/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete
    return future.result()
  File "test.py", line 13, in example
    battery = await client.get_battery_level()
  File "/usr/local/lib/python3.7/dist-packages/pymoof/clients/sx3.py", line 197, in get_battery_level
    self._bike_profile.BikeInfo.MOTOR_BATTERY_LEVEL,
  File "/usr/local/lib/python3.7/dist-packages/pymoof/clients/sx3.py", line 86, in _read
    characteristic_uuid,
  File "/usr/local/lib/python3.7/dist-packages/pymoof/util/bleak_utils.py", line 28, in read_from_characteristic
    return await gatt_client.read_gatt_char(characteristic)
  File "/usr/local/lib/python3.7/dist-packages/bleak/backends/bluezdbus/client.py", line 678, in read_gatt_char
    assert_reply(reply)
  File "/usr/local/lib/python3.7/dist-packages/bleak/backends/bluezdbus/utils.py", line 23, in assert_reply
    raise BleakDBusError(reply.error_name, reply.body)
bleak.exc.BleakDBusError: [org.bluez.Error.Failed] Operation failed with ATT error: 0x0e (Connection Rejected Due To Security Reasons)

Any suggestions what to try?

quantsini commented 2 years ago

Hi! Thanks for the detailed bug report. This seems to happen if the supplied key was not correct, or there needs further tweaks to the authentication code in pymoof. A couple questions: Is your key a 32 character hexadecimal string? What firmware is your bike using?

If you're feeling adventurous, would it be possible to check if the auth payload sent from your phone to authenticate matches the pattern in the code? I suspect the last four bytes may not actually be a static 00 00 00 02. Happy to connect in a more synchronous fashion to walk through the process.

If you're using an iPhone, you can use Apple's built in tools to sniff bluetooth payloads.

quantsini commented 2 years ago

Actually, upon investigating the payload returned from vanmoof servers, the key object has a userKeyId that suspiciously matches the static 00 00 00 02. I wonder if this is different for different bikes?

I can make a separate branch and see if the addition of userKeyId works with your bike.

quantsini commented 2 years ago

I created a branch named develop, would you be able to check out that branch and try out the example.py in that branch?

I'm relatively certain this is the identifier for an authorized user for the bike. I went ahead and merged the change to main and uploaded a new version (0.0.6) onto pypi, try updating the package and see if it works? You would need to get a user_key_id which you can get from the retrieve_encryption_key tool

oischinger commented 2 years ago

you were absolutely right. The user_key_id did the trick! Thanks a lot for the quick support!