mill1000 / midea-msmart

Python library for local control of Midea (and associated brands) smart air conditioners.
MIT License
54 stars 2 forks source link

Configure WLAN from AP mode #151

Open mweinelt opened 3 months ago

mweinelt commented 3 months ago

Hi, thanks for maintaining this software!

I'm wondering if the cloud binding can be skipped entirely, since my AC, a Midea Portasplit, is discoverable on 192.168.1.1 in AP mode.

https://gist.github.com/mweinelt/0addd575269467e8a1ad27b8c4d08fb5

The app would need to configure the WLAN credentials on the device, since otherwise the AC wouldn't be able to pair with the cloud.

mill1000 commented 3 months ago

Hi there.

That's cool! Never thought to try to querying the device while in AP mode.

I would like to eliminate the cloud component from this library. It still relies on the cloud to determine the token/key pair for the V3 protocol.

As for provisioning the WiFi without the app, any chance you could setup a packet capture?

mweinelt commented 3 months ago

Sure, just tried onboarding it onto a rather restricted quarantine WLAN I have at home (DNS/NTP DNAT, and only HTTPS outgoing), and it made it join the WLAN. I have a PCAP of when the phone joined the AP of the AC for four seconds, before returning to the previous WLAN.

https://shells.darmstadt.ccc.de/~hexa/portasplit-pairing.pcapng

Unfortunately the only exchange between phone and AC seems to be TLS encrypted.

I then ran discovery, to which it responded once, and for some reason interacted with the cloud. After that it stopped being discoverable, but it remains pingable.

 ./msmart-ng discover -d 192.168.121.202
DEBUG:asyncio:Using selector: EpollSelector
INFO:msmart.cli:Discovering 192.168.121.202 on local network.
DEBUG:msmart.discover:Discovery sent to 192.168.121.202:6445.
DEBUG:msmart.discover:Discovery sent to 192.168.121.202:20086.
DEBUG:msmart.discover:Waiting 5 seconds for responses...
DEBUG:msmart.discover:Discovery response from 192.168.121.202: 837000c8200f00005a5a0111b8007a80000000000000000000000000ca6da8dd72ff00000000000000000180000000003eeb02751427b9cf925e5e6f77e3a9131eb4add3f5f71f70ffd1806456d9511e3b825cf56e84f78d5c136eb663484b06c22ca8752eed0e940e8241e605c60e0e4ab5b028b105970803db5c62e6d3613d8321eeb7860067759d3cee1b455e8c267f7f6c5939a7eb7c724f8e7b1eaad44d0bb19369bf0317b24d3a4de9e6a13106b90186fda67f0ca6e7ad4936ac025e8200000000000000000000000000000000
DEBUG:msmart.discover:Decrypted data from 192.168.121.202: ca79a8c02c19000030303030303050303030303030305131414337324444413836444341303030300b6e65745f61635f364443410000bf0302000000000000000000ac000c0200000000ac72dda86dca150067012236000300000000000000000000000000000000000000000018000000000000000000000000
INFO:msmart.cloud:Using Midea cloud server: https://mp-prod.appsmb.com (China: False).
INFO:httpx:HTTP Request: POST https://mp-prod.appsmb.com/mas/v5/app/proxy?alias=/v1/user/login/id/get "HTTP/1.1 200 "
DEBUG:msmart.cloud:API response: {"code":"0","msg":"ok","data":{"loginId":"cc19aa32-dfb1-4c12-90b4-94fd05cd"}}
DEBUG:msmart.cloud:Received loginId: cc19aa32-dfb1-4c12-90b4-94fd05cd
DEBUG:msmart.discover:Discovered 1 devices.
INFO:httpx:HTTP Request: POST https://mp-prod.appsmb.com/mas/v5/app/proxy?alias=/mj/user/login "HTTP/1.1 200 OK"
DEBUG:msmart.cloud:API response: {"code":0,"msg":"成功","data":{"randomData":"fe119674796878f8b67bd229ff5880b3fcc85e9e20b2fa5e35c1157dbc2969bd","uid":"83947250d2448ffdf02e111c74bad51e","accountId":"1023665078","nickname":"midea","mdata":{"tokenPwdInfo":{"tokenPwd":"8b94ffdde444400eb0029a077c107ee3","expiredDate":1724026286065,"createDate":1721434286065},"userInfo":{"sourceId":"mj_12345","empId":"5456771537545216","address":"","gender":"0","mobile":"midea@mailinator.com","userDeptInfoList":null,"extras":null,"nameEn":null,"employeeNumber":null,"headPhoto":null,"uid":"83947250d2448ffdf02e111c74bad51e","name":"midea@mailinator.com","email":null},"doDeviceBind":null,"accessToken":"T1m0w9ju9czk39vn6","signUnlockEnabled":null},"accessToken":"7f92bf61d3a68ae4c3bfdc91021128cb35fb0d2c0c1caea4afed3f06f46aa8fb","userId":"89056511576065","email":"midea@mailinator.com"}}
DEBUG:msmart.cloud:Received accessToken: T1m0w9ju9czk39vn6
DEBUG:msmart.discover:Fetching token and key for udpid 'd51fa7950ef935ac400a7e45cdd2025b' (little).
INFO:httpx:HTTP Request: POST https://mp-prod.appsmb.com/mas/v5/app/proxy?alias=/v1/iot/secure/getToken "HTTP/1.1 200 OK"
DEBUG:msmart.cloud:API response: {"code":"0","msg":"ok","data":{"tokenlist":[{"udpId":"d51fa7950ef935ac400a7e45cdd2025b","key":"d69ca01e24b643d6b34eb31bbd7b08e63f92f69c83e9430ea5457377b3c58360","token":"1FB6C9A7FA24EAF7D2DB41741E14327121160D86E5EAE9B31506C90028BA1E8527A91C69728DDE9FABF7FFEB42B66AB37890AD8D6F5420C2646D5D5F0C7FEE6D"}]}}
INFO:msmart.lan:Creating new connection to 192.168.121.202:6444.
DEBUG:msmart.lan:Connected to 192.168.121.202:6444.
INFO:msmart.lan:Authenticating with 192.168.121.202:6444.
DEBUG:msmart.lan:Sending data to 192.168.121.202:6444: 83700040200000001fb6c9a7fa24eaf7d2db41741e14327121160d86e5eae9b31506c90028ba1e8527a91c69728dde9fabf7ffeb42b66ab37890ad8d6f5420c2646d5d5f0c7fee6d
DEBUG:msmart.lan:Received data from 192.168.121.202:6444: 83700005200fa5a54552524f52
DEBUG:msmart.discover:Fetching token and key for udpid '50e4760c8157626d39458b9d3ddea07b' (big).
INFO:httpx:HTTP Request: POST https://mp-prod.appsmb.com/mas/v5/app/proxy?alias=/v1/iot/secure/getToken "HTTP/1.1 200 OK"
DEBUG:msmart.cloud:API response: {"code":"0","msg":"ok","data":{"tokenlist":[{"udpId":"50e4760c8157626d39458b9d3ddea07b","key":"a874f04fc93c406da05e9bf6596ab458f2bc22b656bd4545874fec88f8ed8980","token":"BF050DD1177DDCB38F5EEFA280F5CE4B6C117BA59D99897BB39B68C1E70A59FB157573077AD7738426A30471AED55CA14895DBBF5F03322A02B8E424E432029D"}]}}
INFO:msmart.lan:Authenticating with 192.168.121.202:6444.
DEBUG:msmart.lan:Sending data to 192.168.121.202:6444: 8370004020000001bf050dd1177ddcb38f5eefa280f5ce4b6c117ba59d99897bb39b68c1e70a59fb157573077ad7738426a30471aed55ca14895dbbf5f03322a02b8e424e432029d
DEBUG:msmart.lan:Received data from 192.168.121.202:6444: 83700005200fa5a54552524f52
INFO:msmart.cli:Found 1 devices.
INFO:msmart.cli:Found device:
{'ip': '192.168.121.202', 'port': 6444, 'id': 280868810157514, 'online': False, 'supported': False, 'type': <DeviceType.AIR_CONDITIONER: 172>, 'name': 'net_ac_6DCA', 'sn': '000000P0000000Q1AC72DDA86DCA0000', 'key': None, 'token': None}

Oh and it now wants to establish a TCP session with some AWS host on port 28850.


00:30:02.083757 IP 192.168.121.202.60746 > 18.159.82.49.28850: Flags [S], seq 122393, win 2920, options [mss 1460], length 0
mill1000 commented 3 months ago

Thanks for collecting the capture. I'll take a look.

I then ran discovery, to which it responded once, and for some reason interacted with the cloud. After that it stopped being discoverable, but it remains pingable.

The cloud interaction comes from the library. It's trying to determine the token/key pair necessary for the "V3" protocol. Interesting that your first query of the device used the "V2" protocol.

mill1000 commented 3 months ago

Oh another thing to keep in mind. If you're totally blocking the device from connecting outside there's a watchdog that will kick and reboot the device.

mill1000 commented 3 months ago

Took a quick glance at your capture and it does appear you've capture some packets while it was in AP mode. I think they'll have to be decrypted but might be something useful in there.

agreen commented 3 months ago

I'm interested in this too, I have a brand new (to me) portable Midea AC. Happy to do a packet capture or whatever I can do to assist. Also,. any idea what the watchdog is looking for? wonder if a simple fake server can be setup just to keep it content.

mill1000 commented 3 months ago

Also,. any idea what the watchdog is looking for? wonder if a simple fake server can be setup just to keep it content.

I don't exactly. I think I've tried a DNS redirect to a valid host and it needs a little more than that. Haven't investigated any further

mill1000 commented 3 months ago

I've filtered down the packet capture to what I believe are the provisioning packets portasplit-pairing-provisioning.zip

Most interesting a No 9 & 10 which I believe are a provisioning command and an ACK.

The decrypted contents dumped as hex and ASCII in 16 byte chunks

ee308e61ebdda41d67a79d2fee7c529f
1664697374727573742e6c6f7373792e
6e6574776f726b0b6368616e67656d65
31323306e6956e41f7a0166469737472
7573742e6c6f7373792e6e6574776f72
6b09010101021444450204010d140024
04170034041401640b1b010304543a00
0004116d6f64756c652e617070736d62
2e636f6d0502bb010601020703020601

b'\xee0\x8ea\xeb\xdd\xa4\x1dg\xa7\x9d/\xee|R\x9f'
b'\x16distrust.lossy.'
b'network\x0bchangeme'
b'123\x06\xe6\x95nA\xf7\xa0\x16distr'
b'ust.lossy.networ'
b'k\t\x01\x01\x01\x02\x14DE\x02\x04\x01\r\x14\x00$'
b'\x04\x17\x004\x04\x14\x01d\x0b\x1b\x01\x03\x04T:\x00'
b'\x00\x04\x11module.appsmb'
b'.com\x05\x02\xbb\x01\x06\x01\x02\x07\x03\x02\x06\x01'

Here's a guess at some of the contents:

mweinelt commented 3 months ago

SSID and PSK are correct.

mill1000 commented 3 months ago

Any chance either of you could perform additional captures while provisioning on networks with different security type. (e.g. None, WPA, WPA2).

It just occurred to me that some part of the provision packet probably includes a security type byte,

Including the MAC address of the network's AP might also be beneficial.

mweinelt commented 3 months ago

The MAC for the AP in my case was e6:95:6e:41:f7:a0. I can check later if it even supports other modes.

\xe6\x95nA\xf7\xa0

here we are!

mill1000 commented 3 months ago

Cool cool.

In that case, bytes 16 - 80 probably break down like so

16 - SSID Length = 22
64697374727573742e6c6f7373792e6e6574776f726b - SSID = distrust.lossy.network
0b - PSK Length = 11
6368616e67656d65313233 - PSK = changeme123
06 - Channel? Security Type?
e6956e41f7a0 - AP MAC
16 - SSID Length 2 = 22
64697374727573742e6c6f7373792e6e6574776f726b - SSID 2
mweinelt commented 3 months ago
config wifi-iface 'radio0_vlan121'
    option ifname 'radio0_vlan121'
    option device 'radio0'
    option network 'vlan121'
    option mode 'ap'
    option ssid 'distrust.lossy.network'
    option encryption 'psk2'
    option key 'changeme123'
    option ieee80211w '0'
    option ieee80211v '1'

phy#0
    Interface radio0_vlan121
        ifindex 15
        wdev 0x3
        addr e6:95:6e:41:f7:a0
        ssid distrust.lossy.network
        type AP
        channel 13 (2472 MHz), width: 20 MHz, center1: 2472 MHz
        txpower 20.00 dBm
mweinelt commented 3 months ago

Pairing with an open network:

https://shells.darmstadt.ccc.de/~hexa/midea-portasplit-open.pcapng

config wifi-iface 'radio0_vlan121'
    option ifname 'radio0_vlan121'
    option device 'radio0'
    option network 'vlan121'
    option mode 'ap'
    option ssid 'open.lossy.network'
    option encryption 'none'
phy#0
    Interface radio0_vlan121
        ifindex 15
        wdev 0x3
        addr e6:95:6e:41:f7:a0
        ssid open.lossy.network
        type AP
        channel 9 (2452 MHz), width: 20 MHz, center1: 2452 MHz
        txpower 20.00 dBm
mweinelt commented 3 months ago

Pairing with a WPA-TKIP network:

https://shells.darmstadt.ccc.de/~hexa/midea-portasplit-wpa-tkip.pcapng

config wifi-iface 'radio0_vlan121'
    option ifname 'radio0_vlan121'
    option device 'radio0'
    option network 'vlan121'
    option mode 'ap'
    option encryption 'psk+tkip'
    option key 'changeme123'
    option ssid 'wpa-tkip.lossy.network'
phy#0
    Interface radio0_vlan121
        ifindex 26
        wdev 0xd
        addr e6:95:6e:41:f7:a0
        ssid wpa-tkip.lossy.network
        type AP
        channel 9 (2452 MHz), width: 20 MHz, center1: 2452 MHz
        txpower 20.00 dBm
mweinelt commented 3 months ago

I would have tried SAE as well, but the old rooted Android has no support for it. The trick for it to become capturable is to disable Bluetooth, or else it will try to do the setup that way.

The three provided captures are WPA2, Open and WPA.

mill1000 commented 3 months ago

Thanks.

WPA and WPA2 are nearly identical. Aside from SSID, the only difference is the first 2 bytes.

Open has a zero length PSK (obviously). The first 16 bytes are also different, but after the 2nd SSID field the packet is identical

WPA

Hex View  00 01 02 03 04 05 06 07  08 09 0A 0B 0C 0D 0E 0F

00000000  35 39 8E 61 EB DD A4 1D  67 A7 9D 2F EE 7C 52 9F  59.a....g../.|R.
00000010  16 77 70 61 2D 74 6B 69  70 2E 6C 6F 73 73 79 2E  .wpa-tkip.lossy.
00000020  6E 65 74 77 6F 72 6B 0B  63 68 61 6E 67 65 6D 65  network.changeme
00000030  31 32 33 06 E6 95 6E 41  F7 A0 16 77 70 61 2D 74  123...nA...wpa-t
00000040  6B 69 70 2E 6C 6F 73 73  79 2E 6E 65 74 77 6F 72  kip.lossy.networ
00000050  6B 09 01 01 01 02 14 44  45 02 04 01 0D 14 00 24  k......DE......$
00000060  04 17 00 34 04 14 01 64  0B 1B 01 03 04 54 3A 00  ...4...d.....T:.
00000070  00 04 11 6D 6F 64 75 6C  65 2E 61 70 70 73 6D 62  ...module.appsmb
00000080  2E 63 6F 6D 05 02 BB 01  06 01 02 07 03 02 06 01  .com............

WPA2

Hex View  00 01 02 03 04 05 06 07  08 09 0A 0B 0C 0D 0E 0F

00000000  EE 30 8E 61 EB DD A4 1D  67 A7 9D 2F EE 7C 52 9F  .0.a....g../.|R.
00000010  16 64 69 73 74 72 75 73  74 2E 6C 6F 73 73 79 2E  .distrust.lossy.
00000020  6E 65 74 77 6F 72 6B 0B  63 68 61 6E 67 65 6D 65  network.changeme
00000030  31 32 33 06 E6 95 6E 41  F7 A0 16 64 69 73 74 72  123...nA...distr
00000040  75 73 74 2E 6C 6F 73 73  79 2E 6E 65 74 77 6F 72  ust.lossy.networ
00000050  6B 09 01 01 01 02 14 44  45 02 04 01 0D 14 00 24  k......DE......$
00000060  04 17 00 34 04 14 01 64  0B 1B 01 03 04 54 3A 00  ...4...d.....T:.
00000070  00 04 11 6D 6F 64 75 6C  65 2E 61 70 70 73 6D 62  ...module.appsmb
00000080  2E 63 6F 6D 05 02 BB 01  06 01 02 07 03 02 06 01  .com............

Open

Hex View  00 01 02 03 04 05 06 07  08 09 0A 0B 0C 0D 0E 0F

00000000  9D 4F 6B 64 7B E5 79 F7  49 BF 14 4A 38 39 6D 95  .Okd{.y.I..J89m.
00000010  12 6F 70 65 6E 2E 6C 6F  73 73 79 2E 6E 65 74 77  .open.lossy.netw
00000020  6F 72 6B 00 06 E6 95 6E  41 F7 A0 12 6F 70 65 6E  ork....nA...open
00000030  2E 6C 6F 73 73 79 2E 6E  65 74 77 6F 72 6B 09 01  .lossy.network..
00000040  01 01 02 14 44 45 02 04  01 0D 14 00 24 04 17 00  ....DE......$...
00000050  34 04 14 01 64 0B 1B 01  03 04 54 3A 00 00 04 11  4...d.....T:....
00000060  6D 6F 64 75 6C 65 2E 61  70 70 73 6D 62 2E 63 6F  module.appsmb.co
00000070  6D 05 02 BB 01 06 01 02  07 03 02 06 01           m............

Open w/ zero padding to match WPA length

Hex View  00 01 02 03 04 05 06 07  08 09 0A 0B 0C 0D 0E 0F

00000000  9D 4F 6B 64 7B E5 79 F7  49 BF 14 4A 38 39 6D 95  .Okd{.y.I..J89m.
00000010  12 00 00 00 00 6F 70 65  6E 2E 6C 6F 73 73 79 2E  .....open.lossy.
00000020  6E 65 74 77 6F 72 6B 00  00 00 00 00 00 00 00 00  network.........
00000030  00 00 00 06 E6 95 6E 41  F7 A0 12 00 00 00 00 6F  ......nA.......o
00000040  70 65 6E 2E 6C 6F 73 73  79 2E 6E 65 74 77 6F 72  pen.lossy.networ
00000050  6B 09 01 01 01 02 14 44  45 02 04 01 0D 14 00 24  k......DE......$
00000060  04 17 00 34 04 14 01 64  0B 1B 01 03 04 54 3A 00  ...4...d.....T:.
00000070  00 04 11 6D 6F 64 75 6C  65 2E 61 70 70 73 6D 62  ...module.appsmb
00000080  2E 63 6F 6D 05 02 BB 01  06 01 02 07 03 02 06 01  .com............