diff options
author | Mike Crute <mcrute@gmail.com> | 2013-09-07 18:39:43 -0400 |
---|---|---|
committer | Mike Crute <mcrute@gmail.com> | 2013-09-07 18:39:43 -0400 |
commit | ef2a2420c9602d36c1d85df09e3f4b0b55203771 (patch) | |
tree | 4adb67d7c66a4122edbe85878a6af9303ec578df | |
parent | 62bcc93047f677f322422fb1c9a0fab3f1923008 (diff) | |
download | ubntmfi-ef2a2420c9602d36c1d85df09e3f4b0b55203771.tar.bz2 ubntmfi-ef2a2420c9602d36c1d85df09e3f4b0b55203771.tar.xz ubntmfi-ef2a2420c9602d36c1d85df09e3f4b0b55203771.zip |
Adding inform reader
-rw-r--r-- | inform.py | 102 | ||||
-rw-r--r-- | inform_protocol.txt | 30 |
2 files changed, 132 insertions, 0 deletions
diff --git a/inform.py b/inform.py new file mode 100644 index 0000000..8c4b3e0 --- /dev/null +++ b/inform.py | |||
@@ -0,0 +1,102 @@ | |||
1 | import struct | ||
2 | import binascii | ||
3 | from Crypto.Cipher import AES | ||
4 | |||
5 | |||
6 | class BinaryDataStream(object): | ||
7 | |||
8 | def __init__(self, data): | ||
9 | self.data = data | ||
10 | |||
11 | def read_int(self): | ||
12 | return struct.unpack(">i", self.data.read(4))[0] | ||
13 | |||
14 | def read_short(self): | ||
15 | return struct.unpack(">h", self.data.read(2))[0] | ||
16 | |||
17 | def read_string(self, length): | ||
18 | return self.data.read(length) | ||
19 | |||
20 | |||
21 | class Cryptor(object): | ||
22 | |||
23 | def __init__(self, key, iv): | ||
24 | self.iv = iv | ||
25 | self.key = key | ||
26 | self.cipher = AES.new(key.decode("hex"), AES.MODE_CBC, iv) | ||
27 | |||
28 | @staticmethod | ||
29 | def unpad(s): | ||
30 | return s[0:-ord(s[-1])] | ||
31 | |||
32 | @staticmethod | ||
33 | def pad(s, BS=16): | ||
34 | return s + (BS - len(s) % BS) * chr(BS - len(s) % BS) | ||
35 | |||
36 | def decrypt(self, payload): | ||
37 | return self.unpad(self.cipher.decrypt(payload)) | ||
38 | |||
39 | |||
40 | class InformParser(object): | ||
41 | |||
42 | PROTOCOL_MAGIC = 1414414933 | ||
43 | MAX_VERSION = 1 | ||
44 | |||
45 | ENCRYPTED_FLAG = 0x1 | ||
46 | COMPRESSED_FLAG = 0x2 | ||
47 | |||
48 | def __init__(self, input_stream, key): | ||
49 | self.input_stream = input_stream | ||
50 | self.key = key | ||
51 | |||
52 | self.magic_number = None | ||
53 | self.version = None | ||
54 | self.mac_addr = None | ||
55 | self.flags = None | ||
56 | self.iv = None | ||
57 | self.data_version = None | ||
58 | self.data_length = None | ||
59 | self.payload = None | ||
60 | |||
61 | @classmethod | ||
62 | def open(cls, filename, key): | ||
63 | return cls(BinaryDataStream(open(filename, "rb")), key) | ||
64 | |||
65 | @staticmethod | ||
66 | def _format_mac_addr(mac_bytes): | ||
67 | return "-".join([binascii.hexlify(i) for i in mac_bytes]) | ||
68 | |||
69 | def _has_flag(self, flag): | ||
70 | return self.flags & flag != 0 | ||
71 | |||
72 | @property | ||
73 | def formatted_mac_addr(self): | ||
74 | return self._format_mac_addr(self.mac_addr) | ||
75 | |||
76 | @property | ||
77 | def is_encrypted(self): | ||
78 | return self._has_flag(self.ENCRYPTED_FLAG) | ||
79 | |||
80 | @property | ||
81 | def is_compressed(self): | ||
82 | return self._has_flag(self.COMPRESSED_FLAG) | ||
83 | |||
84 | @property | ||
85 | def decrypted_payload(self): | ||
86 | return Cryptor(self.key, self.iv).decrypt(self.payload) | ||
87 | |||
88 | def parse(self): | ||
89 | self.magic = self.input_stream.read_int() | ||
90 | assert self.magic == self.PROTOCOL_MAGIC | ||
91 | |||
92 | self.version = self.input_stream.read_int() | ||
93 | assert self.version < self.MAX_VERSION | ||
94 | |||
95 | self.mac_addr = self.input_stream.read_string(6) | ||
96 | self.flags = self.input_stream.read_short() | ||
97 | self.iv = self.input_stream.read_string(16) | ||
98 | self.data_version = self.input_stream.read_int() | ||
99 | self.data_length = self.input_stream.read_int() | ||
100 | self.payload = self.input_stream.read_string(self.data_length) | ||
101 | |||
102 | return self | ||
diff --git a/inform_protocol.txt b/inform_protocol.txt new file mode 100644 index 0000000..f4d9c41 --- /dev/null +++ b/inform_protocol.txt | |||
@@ -0,0 +1,30 @@ | |||
1 | Ubiquiti Inform Protocol | ||
2 | ======================== | ||
3 | |||
4 | These are just some unstructured notes about the inform protocol at this point. | ||
5 | |||
6 | Packet Structure | ||
7 | ---------------- | ||
8 | 4 bytes magic number integer | ||
9 | 4 bytes version integer | ||
10 | 6 bytes hwaddr string | ||
11 | 2 bytes flags short | ||
12 | 16 bytes initialization vector string | ||
13 | 4 bytes data version integer | ||
14 | 4 bytes data length integer | ||
15 | n bytes encrypted payload string | ||
16 | |||
17 | |||
18 | magic must == 1414414933 | ||
19 | data version must < 1 | ||
20 | flags & 0x1 != 0 means encrypted | ||
21 | flags & 0x2 != 0 means compressed | ||
22 | |||
23 | |||
24 | The payload is AES encrypted in CBC mode using PKCS5 padding. They key is the | ||
25 | device auth key from the database or a master key that is hard coded if the | ||
26 | device hasn't been provisioned yet. The master key is hard coded in the | ||
27 | controller code in the DeviceManager class and pretty easy to find. | ||
28 | |||
29 | |||
30 | MASTER_KEY = "ba86f2bbe107c7c57eb5f2690775c712" | ||