Reverse engineering Orvibo S20 socket

I have bought Orvibo S20 (picture) smart socket. You can find them on Amazon, eBay and probably many other online shops. The socket comes with an app for Android and iOS that is used to control the socket. Unfortunately, the app is proprietary and also not available for normal computers. I wanted to have some free software solution. Also, the original app didn’t work too reliably, and of course you can’t fix it without having a code. Again, this shows why it is important to have free software…

After searching a bit I found some code on GitHub written for Ninja Blocks. It also came with some reverse engineering data (I will refer to this as the original reverse engineering later and will assume that you briefly looked at it). That file might look slightly scary initially if you are not used to that kind of stuff but actually everything is quite simple. It seems that the socket can be controlled by simply sending UDP packets over the network and listening for replies. You can even try to play a bit with netcat but of course it is not too convenient. So I decided to write my own program using Qt 5. It provides a lot of convenient functions in its QtNetwork module and also it would make it easy to write GUI if I ever decide to do so in the future. Also, I’m familiar with Qt because it is used by KDE Applications. Another reason for choosing Qt was it’s support for sockets and slots and I expected them to be useful.

As you can see in the reverse engineering file, there are commands to discover the socket, subscribe to it (which is required before doing anything else), power it on/off and read some tables (Socket Data contains information about the socket and Timing Data stores when to turn the socket on or off). They all follow similar pattern. You have to send

Magic Key+ message length + command id + rest of the message

where Magic Key is 68 64 (hexadecimals) and is used to distinguish these UDP packets from any other packets that are send over UDP port 10000. Every time you send a message the socket replies with another message confirming the action of the first message. Or the socket doesn’t reply. We are using UDP protocol for networking. So there is no guarantee that message is received and later I had to write some code to make sure packets are received. Hence, I implemented message queue and resend every command until I get a proper reply before sending another command. This finally made my program more reliable.

Writing Socket Data

I quickly managed to get some basic stuff working (for example powering it on and off) and soon I implemented most of the commands from the file with reversed engineered commands (reading Timing Table is still not completed but shouldn’t be too hard). Since I wanted to do more than that, I started Wireshark and analyzed a few more packets. I quickly learned how to write Socket Data Table too. Apparently, you send command very similar to what you receive when you request Socket Data but with different Command ID (74 6d instead of 72 74 in hexadecimals).

So to write Socket Data I send the following packet

Magic Key + message length + 74 6d + mac + mac padding + 00 00 00 00  + AA 00 BB + recordLength + record;

where

record = 01 00 /* record number = 1*/ + versionID + mac + mac padding + reversed mac + mac padding + remote password + socket name + icon + hardwareVersion + firmwareVersion + wifiFirmwareVersion + port + staticServerIP + port + domainServerName + localIP + localGatewayIP + localNetMask + dhcpNode + discoverable + timeZoneSet + timezone + countdownStatus + countdown + 00 (repeated twelve times) + 30 (repeated 30 times, note that hex 30 corresponds to 0 in ASCII);

countdownStatus is 00 ff when countdown is disabled and 01 00 when countdown is enabled.

AA 00 BB is actually table number and version flag. E.g. 04 00 01. 4 stands for table number (Socket Data is Table number 4) I don’t completely understand what is version flag, so if you know please tell me in the comments.

Then socket replies with:

Magic Key + message length + 74 6d + mac + mac padding + 01 00 00 00 00;

Now just send an already documented Socket Table packet (see: http://pastebin.com/LfUhsbcS) to update your variables.

Writing Timing Data

Writing Timing Data is exactly the same (even Command ID is still 74 6d) as writing Socket Data but you must specify 03 a a table number

Magic Key + message length + 74 6d + mac + mac padding + 00 00 00 00  + AA 00 BB + record1Length + record 1+ record2Length + record2 + record3Length + record3 + …;

record again contains the same data as what socket sends when you request timing data.

Initial pairing of the socket

The authors of the original Ninja Blocks orvibo-allone driver assume that socket was already paired using the proprietary Android/iOS application. Their original reverse engineering also contains no information how to do that. I expected that this might be a bit tricky to do because unpaired socket is not connected to the router and you have to somehow transmit your wifi configuration into the socket. I think there are at least two ways to pair that proprietary Android/iOS app implements. If you press the socket button for a few second it switches to a rapidly blinking red led mode. Then long press it again and it switches to rapidly blinking blue led and the socket creates an unencrypted wifi network (it was called WiWo-S20).

Then I created the wifi network with the same name on my laptop and tricked the proprietary app into believing that it is the socket’s wifi network. I was able to intercept the following message on UDP port 48899 (everything is in ASCII in the section, not in hex):

HF-A11ASSISTHREAD

So apparently, Orvibo S20 has HF-LPB100 Wifi chip inside. This chip can be controlled by the AT+ commands (you can find them online but I will write a brief summary here) and I was able to do initial socket configuration!

  • Switch S20 to rapidly blinking blue led more. Connect your computer to WiWo-S20 network.
  •  Send “HF-A11ASSISTHREAD” on UDP port 48899 to the broadcast address (don’t include ” in the message).
  •  S20 will reply with “IP address,MAC Address,Hostname”. The socket always replies to the same port as the source port of your message.
  • Acknowledge that you got the previous message by sending “+ok”.
  • Send “AT+WSSSID=ssid\r” where you replace ssid with your WiFi network name. \r is the carriage return (CR) symbol.
  • The socket will reply with “+ok\n\n” (\n in this case is carriage return + line feed) if everything is correct or “+ERR\n\n” if something is wrong.
  • Send your Wifi security settings: “AT+WSKEY=WPA2PSK,AES,PASSWORD\r”.  The socket will again reply with “+ok\n\n”.
  • Switch HF-A11 chip to station mode by sending “AT+WMODE=STA\r”. Again, wait for “+ok\n\n”
  • Reboot your socket with “AT+Z\r”.
  • Connect your computer back to your router. Wait until the socket boots. Now you can find it using normal discovery packet on port 10000, then change socket name, timezone, etc. with Write Socket Data packet…

I did some investigation and it seems that we need to send a slightly modified Table Data and Socket Data packets immediately after pairing to set them to default values. More information will be published later.

Another way to pair the socket from the rapidly blinking red mode. It is slighly less reliable than this method but on the other hand does not require you to disconnect from your wireless. Actually, it doesn’t require your computer to have any wireless at all.

Unfortunately, it seems that either way WPA encryption key is transferred in an insecure way, i.e. the socket is not able to use any public key cryptography. Slightly safer way to do it manually is to first change your WPA key to something temporary and pair the socket. Then use AT+ commands to change WPA password to the real one and change your router’s WPA password back.

Also, note that this socket doesn’t support WPA Enterprise. So if you would like to use it at home then create two Wifi networks: WPA-PSK for the socket and WPA-EAP for everything else (OpenWrt can do this easily).

Code

I released all code under the GNU General Public License version 3, so the code is freely available to everybody. Git repository is available at:

https://git.stikonas.eu/andrius/s20 (feel free to create an account here and fork the code)

Windows binaries

I compiled windows binaries. There is also a very limited GUI that does not yet have all the features of console app but if you prefer GUI then it might be useful.

Console 64-bit 32-bit
GUI 64-bit 32-bit

Donations

If you find this work useful then tips are very welcome. You can send Bitcoin tips to bc1qe2dfqjwgse5v6cl6rhtk352ru90t0hnve45f2c.

I was able to buy the second socket which already resulted in improved multiple socket support. Thanks for the donations!
Become a patron Donate using Liberapay Bitcoin: bc1qe2dfqjwgse5v6cl6rhtk352ru90t0hnve45f2c

Bugs

Bugs are tracked in the Gitlab issue tracker.

See also

Go code by Grayda (basic support for S20 but also supports Orvibo AllOne devices)

PHP code by Fernano Silva (supports most features of S20). See also his technical data file.

Perl code by Branislav Vartik

KDE Partition Managers 1.1.1 and 1.2.1

I would like to announce two releases of KDE Partition Manager. Version 1.1.1 is a bugfix release. It was ported away from Solid to avoid the unfortunate udisks2 bug and uses libparted directly (partitionmanager was using libparted already anyway). That means packagers do not have to select -DENABLE_UDISKS2 cmake option anymore.

Version 1.2.0 is a new feature release. It doesn’t have that many new features but the port to KDE Frameworks is complete.

Download links can be found in usual locations on download.kde.org:

http://download.kde.org/stable/partitionmanager/1.1.1/src/partitionmanager-1.1.1.tar.xz

SHA256: 4cf3fa71bd20b06755d36bb9aa7692fa4770a1d84ec8eb72654cb58fdea563de

http://download.kde.org/stable/partitionmanager/1.2.0/src/partitionmanager-1.2.0.tar.xz

SHA256: d7300b1c918a1426533906ef9c64d9df923025fa5be349e0b9d4fc5e1480cce3

In the next release there are some plans to separate core part of KDE Partition Manager and the GUI part, so that other projects like distribution-independent installer Calamares would be able to reuse Partition Manager code easier.

Edit: It looks like 1.2.0 version have a broken docs that adds unintentional dependency on kdelibs4support (bug #344256). I will try to release 1.2.1 soon that fixes this issue.

Edit: KDE Partition Manager 1.2.1 is now released. docs are fixed and don’t require kdelibs4support anymore. KDE_INSTALL_USE_QT_SYS_PATHS is also enabled by default.

SHA256: eb5b8b00ab6f0a2c5ef494b1c70441100ff70bf691006c4aa95f39d00b43e4e0

KDE Partition Manager 1.1.0

The long awaited KDE Partition Manager 1.1.0 is now released. However, there are some sad news first. This release is dedicated to the memory of Volker Lanz who passed away this April. He was the main developer of KDE Partition Manager who wrote almost all of its code and maintained it for 5 years.

The most important changes in this release are support for Btrfs file system and GPT partition table. This release also adds support for exFAT, NILFS 2 and very basic support for ZFS, encrypted LUKS containers and LVM2 physical volumes. There are some nice GUI improvements as well, partition widget design was improved by Hugo Pereira Da Costa.

On the other hand, there are still missing features and bugs. It would be nice to have a better support for LUKS and LVM volumes. There is also a slightly annoying bug #311408 which prevents detection of disks without any partition table when Solid is used with udisks2  backend. It seems that udisks2 does not let you easily obtain a block device corresponding to a particular drive, so Solid backend has to guess it (usually incorrectly) and then partition manager fails to find any disks. I have implemented a workaround to look for partition tables instead of drives (then Partition Manager has to be compiled with -DENABLE_UDISKS2).

As you can see Partition Manager really needs a few more developers. Its code is less scary then it might seem. After all it is a GUI and most of low level stuff is done my external programs.

Port to KDE Frameworks 5 is also almost complete and will be released once all issues are fixed. There is still some unported code, e.g. at src/backend/corebackenmanager.cpp. If any KF5 porting experts know how to fix this it would be very appreciated.

KDE Partition Manager can be downloaded from http://download.kde.org/stable/partitionmanager/1.1.0/src/partitionmanager-1.1.0.tar.xz.mirrorlist

SHA256Sum: 30ac75f85bb61e6ec9b6f5d80dee4a2c8501a0a0c62997f6d734e63459802cc8 partitionmanager-1.1.0.tar.xz

SHA1Sum: 797964522d077ec9daae752bd2d40f1bd284d149 partitionmanager-1.1.0.tar.xz

MD5Sum: 4a752c250fc5c98e22aba1d2ea309bec partitionmanager-1.1.0.tar.xz

GPG signature can be found at https://stikonas.eu/files/partitionmanager-1.1.0.tar.xz.sig