bggardner / simplisafe-rf

The application-layer protocol used by SimpliSafe devices over RF
The Unlicense
20 stars 13 forks source link

Input checking for RFutils.py and SimpliSafe.py #6

Open alandtse opened 7 years ago

alandtse commented 7 years ago

When the signal is not fully received, both RFUtils.py and Simplisafe.py may occassionally generate errors that will kill the calling program. In this case I have created a small test program based off the readme:

#!/usr/bin/python3
import RFUtils
import SimpliSafe

RX_315MHZ_GPIO = 17 # Connected to DATA pin of 315MHz receiver
RX_433MHZ_GPIO = 27 # Connected to DATA pin of 433MHz receiver
TX_315MHZ_GPIO = 16 # Connected to DATA pin of 315MHz transmitter
TX_433MHZ_GPIO = 20 # Connected to DATA pin of 433MHz transmitter

# 433MHz traffic monitor:
while True:
    msg = RFUtils.recv(RX_433MHZ_GPIO) # Returns when a valid message is received and parsed
    print(str(msg))

Here are examples of crashes I have generated.

Ascii decode errors:

Message received: 001100111010000010001000100011000110010011100001100100010110001101010100000001110101
Decoded: CC5011136278986CA20EA
Swapped: CC051131268789C62AE0
Traceback (most recent call last):
  File "test.py", line 14, in <module>
    msg = RFUtils.recv(RX_433MHZ_GPIO) # Returns when a valid message is received and parsed
  File "/home/pi/simplisafe-rf/RFUtils.py", line 122, in recv
    return SimpliSafe.Message.factory(swapped)
  File "/home/pi/simplisafe-rf/SimpliSafe.py", line 130, in factory
    sn = b[3:8].decode('ascii')
UnicodeDecodeError: 'ascii' codec can't decode byte 0x87 in position 2: ordinal not in range(128)

Int decode errors

Message received: 0011001
Decoded: C4
Traceback (most recent call last):
  File "test.py", line 14, in <module>
    msg = RFUtils.recv(RX_433MHZ_GPIO) # Returns when a valid message is received and parsed
  File "/home/pi/simplisafe-rf/RFUtils.py", line 104, in recv
    origin = int(decoded[16:18][::-1], 16)
ValueError: invalid literal for int() with base 16: ''
Message received: 001100111010
Decoded: CC5
Traceback (most recent call last):
  File "test.py", line 14, in <module>
    msg = RFUtils.recv(RX_433MHZ_GPIO) # Returns when a valid message is received and parsed
  File "/home/pi/simplisafe-rf/RFUtils.py", line 104, in recv
    origin = int(decoded[16:18][::-1], 16)
ValueError: invalid literal for int() with base 16: ''

I did note that there is code that will raise InvalidMessage errors that need to be caught by the calling program. I'm not sure if that is the mechanism that should be raised for the above errors or if it makes sense for the library to gracefully ignore those types of messages.

Message received: 00101011101000001000100010001100100111000000101000101010001100100010111001000000011011101111
Decoded: 4D501113930545C4472067F
Swapped: D40511313950544C740276
Traceback (most recent call last):
  File "test.py", line 14, in <module>
    msg = RFUtils.recv(RX_433MHZ_GPIO) # Returns when a valid message is received and parsed
  File "/home/pi/simplisafe-rf/RFUtils.py", line 122, in recv
    return SimpliSafe.Message.factory(swapped)
  File "/home/pi/simplisafe-rf/SimpliSafe.py", line 126, in factory
    raise InvalidMessageBytesError("Invalid Vendor Code: 0x{:04X}".format(vc))
SimpliSafe.InvalidMessageBytesError: Invalid Vendor Code: 0xD405
bggardner commented 7 years ago

The RFUtils.py file was really a quick-and-dirty method of capturing the data on a Raspberry Pi, and is just meant to test SimpliSafe.py. Feel free to improve if you feel it is necessary.

The last error is interesting, as I assumed all messages started with CC05, which I assumed to be some sort of "vendor code". I could be wrong on this, as the rest of the message looks correct, so let me know if it is repeatable. The first byte may have just been corrupted, in which case the error is valid. FYI, that message has the following characteristics:

Vendor Code: 0xD405
PLC: 0x11 (2 bytes)
Serial: 19PTL
Origin Type: 0x4 (Motion Sensor)
Sequence: 0x7
Event Type: 0x02 (Motion)
Checksum: 0x76