Python Mesh
Pykonik 2019
czyli zdalnie sterowane żarówki
Pobożne życzenia
Smutna rzeczywistość
Retransmisje
Relay
Bezpieczeństwo
Idempotentność
listonosz nie
czyta poczty
powtórzenie komendy
nie ma skutków ubocznych
Network
Transport
Application
Bearer
IP
TCP
HTTP
Ethernet
Preamble | Access Address | Header | MAC Address | Payload | CRC |
---|---|---|---|---|---|
1 byte | 4 bytes | 2 bytes | 6 bytes | 0-31 bytes | 3 bytes |
Length | Type | Data | ... | Length | Type | Data |
---|---|---|---|---|---|---|
1 byte | 1 byte | 0-29 bytes | ... | 1 byte | 1 byte | 0-29 bytes |
Losowy adres
0x2a
IVI | NID | CTL | TTL | SEQ | SRC | DST | PDU | MIC |
---|---|---|---|---|---|---|---|---|
1 bit | 7 bits | 1 bit | 7 bits | 24 bits | 16 bits | 16 bits | 8-128 bits | 32-64 bits |
Adresy
Podpis
Numer pakietu
Ramka
transportowa
Ukryte
Tajne
Jawne
Jawne
Identyfikator klucza
Plaintext
Nonce
Key
AES
Ciphertext
MIC
IV Index | SEQ | SRC | ... |
---|---|---|---|
32bit | 24bit | 16 bit |
Ogólnie znane
Część pakietu
Numer pakietu jest niepowtarzalny!
DST | PDU |
---|
from bitstring import pack
from crypto import aes_ccm, aes_ecb
network_nonce = pack('uint:8, uint:1, uint:7, '
'uintbe:24, uintbe:16, pad:16, uintbe:32',
0x00, ctl, ttl, seq, src, iv_index)
encrypted_pdu = aes_ccm(encryption_key, nonce,
bitstring.pack('uintbe:16, bytes', dst, transport_pdu))
network_header = pack('uint:1, uint:7, uintbe:24, uintbe:16',
ctl, ttl, seq, src)
# nie całkiem prawda ;-)
privacy_nonce = pack('pad:40, uintbe:32, bytes:7',
iv_index, encrypted_pdu[:7])
obfuscated_header = aes_ecb(privacy_key, privacy_nonce,
network_header)
network_pdu = pack('uint:1, uint:7, bits, bytes',
iv_index & 1, nid, obfuscated_header, network_pdu)
SEG | AKF | AID | PDU | MIC |
---|---|---|---|---|
1 bit | 1 bit | 6 bits | 0-88 bits | 32-64 bits |
Identyfikator klucza
Fragmentacja
Podpis
Ramka aplikacyjna
Tajne
Jawne
Jawne
from bitstring import pack
from crypto import aes_ccm
transport_nonce = pack('uint:8, uint:1, pad:7, '
'uintbe:24, uintbe:16, pad:16, uintbe:32',
0x01, long_mic, seq, src, dst, iv_index)
encrypted_pdu = aes_ccm(application_key, application_nonce,
application_pdu)
seg = len(encrypted_pdu) > 15
if not seg:
transport_pdu = pack('uint:1, uint:1, uint:6, bytes',
seg, akf, aid, encrypted_pdu)
else:
raise NotImplementedError # ;-)
OPCODE | PARAMETERS |
---|---|
1-3 bytes | 0-379 bytes |
'Assigned
Numbers'
application_pdu = pack('bits, bits',
opcode, parameters)
opcode = '\x80\x05' # "health: attention set"
parameters = pack('uint:8', timeout)
opcode = '\x82\x03' # "generic on-off: set unacknowledged"
parameters = pack('uint:8, uint:8, uint:2, uint:6, uint:8',
trigger, transaction, resolution, steps, delay)
opcode = '\x80\x1b' # "config: subscription add"
parameters = ...
0 | 15 bits |
1 | 0 | 14 bits |
1 | 1 | 14 bits |
0 | 0 |
---|
Unassigned
Unicast
Virtual
Group
Adres | Element | Modele |
---|---|---|
0x0010 | #0 | Health Config Scene Server Presence Sensor |
0x0011 | #1 | Lightness Controller Ambient Light Sensor |
0x0012 | #2 | Scene Translator |
#0 | #1 | #2 | #3 |
---|---|---|---|
OnOff | OnOff | OnOff | OnOff |
0x0010 | 0x0011 | 0x0012 | 0x0013 |
0x8000
0x8001
0x0010
0x0020
0x0030
0x0040
0x8000
0x8001
0x8000
0x0050
0x8001
0x8002
0x8000
0x8001
0x0010
0x0030
0x8001
0x0050
0x8001
relay
włącz się
za 90ms
włącz się
za 30ms
włącz się
za 60ms
czas
Proxy
GATT
0x0050
GATT
BlueZ
DBus
Python
BLUETOOTH_SIG = \
'0000-1000-8000-00805f9b34fb'
proxy_service = \
'00001828-' + BLUETOOTH_SIG
write_char = \
'00002add-' + BLUETOOTH_SIG
read_char = \
'00002ade-' + BLUETOOTH_SIG
Preamble | Access Address | Header | MAC Address | Payload | CRC |
---|---|---|---|---|---|
1 byte | 4 bytes | 2 bytes | 6 bytes | 0-31 bytes | 3 bytes |
Length | Type | Data | ... | Length | Type | Data |
---|---|---|---|---|---|---|
1 byte | 1 byte | 0-29 bytes | ... | 1 byte | 1 byte | 0-29 bytes |
Prawdziwy adres
0x03
0x16
from pydbus import SystemBus
bus = SystemBus()
adapter = bus.get('org.bluez',
'/org/bluez/hci0')
adapter.StartDiscovery()
...
def device_discovered(device):
network_id = device.ServiceData.get(ServiceId.MESH_PROXY)
proxy_mac = device.Address
from pydbus import SystemBus
bus = SystemBus()
adapter = bus.get('org.bluez',
'/org/bluez/hci0')
adapter.StartDiscovery()
device = bus.get('org.bluez',
'/org/bluez/hci0/dev_%s' % proxy_mac)
device.Connect()
service = bus.get('org.bluez',
'/org/bluez/hci0/dev_%s/service_%s' % (proxy_mac,
proxy_service))
send = bus.get('org.bluez',
'/org/bluez/hci0/dev_%s/char_%s' % (proxy_mac,
write_char))
recv = bus.get('org.bluez',
'/org/bluez/hci0/dev_%s/char_%s' % (proxy_mac,
read_char))
send = bus.get('org.bluez',
'/org/bluez/hci0/dev_%s/char_%s' % (proxy_mac,
write_char))
recv = bus.get('org.bluez',
'/org/bluez/hci0/dev_%s/char_%s' % (proxy_mac,
read_char))
def packet_send(packet):
send.WriteValue(bytes(....))
def properties_changed(properties):
value = properties.get('Value')
if value is not None:
packet_receive(bytes(value))
recv.PropertiesChanged.connect(packet_received)
recv.StartNotify()
GATT
BlueZ
DBus
Python
Relay
Proxy
SilvairGit/python-bluetooth-mesh
GPL 2.0
Dziękuję za uwagę!
Pytania?