def __init__(self): self.protocol_header = b'\x00' self.message_format = Struct( "fields" / RawCopy( Struct("NetworkID" / Bytes(32), "DeviceID" / Int8sb, "Sequence" / Int16ub, "Payload_length" / Byte, "Payload" / Bytes(this.Payload_length))), "checksum" / Checksum(Bytes(4), lambda data: hashlib.sha256(data).digest()[0:4], this.fields.data), )
decrypted = decrypted.rstrip(b"\x00") except Exception as ex: _LOGGER.debug("Unable to decrypt, returning raw bytes.") return obj try: jsoned = json.loads(decrypted.decode('utf-8')) except: _LOGGER.error("unable to parse json, was: %s", decrypted) jsoned = b'{}' raise return jsoned Message = Struct( # for building we need data before anything else. "data" / Pointer(32, RawCopy(EncryptionAdapter(GreedyBytes))), "header" / RawCopy( Struct( Const(Int16ub, 0x2131), "length" / Rebuild(Int16ub, Utils.get_length), "unknown" / Default(Int32ub, 0x00000000), "devtype" / Enum(Default(Int16ub, 0x02c1), default=Pass, **xiaomi_devices), "serial" / Default(Int16ub, 0xa40d), "ts" / TimeAdapter(Default(Int32ub, datetime.datetime.utcnow())))), "checksum" / IfThenElse(Utils.is_hello, Bytes(16), Checksum(Bytes(16), Utils.md5, Utils.checksum_field_bytes)), )
RequestedEvent = Struct( "fields" / RawCopy( Struct( "po" / BitStruct( "command" / Const(0xE, Nibble), "status" / Struct("reserved" / Flag, "alarm_reporting_pending" / Flag, "Winload_connected" / Flag, "NeWare_connected" / Flag)), "length" / PacketLength(Int8ub), "_not_used0" / Bytes(1), "requested_event_nr" / Const(0x00, Int8ub), "event_nr" / Int16ub, # "data" / Bytes(lambda this: this.length - 7) "data" / Array(lambda x: int( (x.length - 7) / 12), CompressedEvent))), "checksum" / Checksum(Bytes(1), lambda data: calculate_checksum(data), this.fields.data)) CloseConnection = Struct( "fields" / RawCopy( Struct( "po" / Struct("command" / Const(0x70, Int8ub)), "length" / PacketLength(Int8ub), "message" / Default( Enum(Int8ub, requested_command_failed=0x00, invalid_user_code=0x01, partition_in_code_lockout=0x02, panel_will_disconnect=0x05, panel_not_connected=0x10, panel_already_connected=0x11, invalid_pc_password=0x12,
hashlib.sha256).digest() # -------------------- Payload Decryption/Decompression -------------------- # encrypted payload is split into multiple data blocks with hashes EncryptedPayloadBlock = Struct( "hmac_hash_offset" / Tell, Padding(32), "block_data" / Prefixed(Int32ul, GreedyBytes), # hmac_hash has to be at the end with a pointer because it needs to # come after other fields "hmac_hash" / Pointer( this.hmac_hash_offset, Checksum( Bytes(32), compute_payload_block_hash, this, # exception=PayloadChecksumError ))) EncryptedPayload = Concatenated( RepeatUntil(lambda item, a, b: len(item.block_data) == 0, EncryptedPayloadBlock)) DecryptedPayload = Switch( this._.header.value.dynamic_header.cipher_id.data, { 'aes256': AES256Payload(EncryptedPayload), 'chacha20': ChaCha20Payload(EncryptedPayload), 'twofish': TwoFishPayload(EncryptedPayload) }) InnerHeaderItem = Struct(
'compression_flags': CompressionFlags, 'cipher_id': CipherId, 'transform_rounds': Int32ul, 'protected_stream_id': ProtectedStreamId }, default=GreedyBytes)), ) DynamicHeader = DynamicDict( 'id', RepeatUntil(lambda item, a, b: item.id == 'end', DynamicHeaderItem)) # -------------------- Payload Verification -------------------- # encrypted payload is split into multiple data blocks with hashes PayloadBlock = Struct( "block_index" / Checksum(Int32ul, lambda this: this._index, this), "block_hash_offset" / Tell, Padding(32), "block_data" / Prefixed(Int32ul, GreedyBytes), # block_hash has to be at the end with a pointer because it needs to # come after other fields "block_hash" / Pointer( this.block_hash_offset, IfThenElse( len_(this.block_data) == 0, Checksum(Bytes(32), lambda _: b'\x00' * 32, this), Checksum( Bytes(32), lambda block_data: hashlib.sha256(block_data).digest(), this.block_data, # exception=PayloadChecksumError
"random_state" / Pointer(this.dir.dir_size + this.dir.offset[2], FixedSized(this.dir.uncompr_size[2], GreedyBytes)), # 2004 "bmap_size" / Pointer( this.dir.dir_size + this.dir.offset[3], Struct( "uncompr_size" / Int32ul, "uncompr_size" / Computed(this._.dir.uncompr_size[3]), "compr_size" / Int32ul, "compr_size" / Computed(this._.dir.compr_size[3] - 12), "crc32_offset" / Tell, "crc32" / Int32ul, "data" / FixedSized( this.compr_size, Compressed(Bytes(this.uncompr_size), "blast")), "crc32" / Pointer(this.crc32_offset, Checksum(Int32ul, lambda l: crc32(l), this.data)))), # 2005 "bmap_tile" / Pointer( this.dir.dir_size + this.dir.offset[4], Struct( "uncompr_size" / Int32ul, "uncompr_size" / Computed(this._.dir.uncompr_size[4]), "compr_size" / Int32ul, "compr_size" / Computed(this._.dir.compr_size[4] - 12), "crc32_offset" / Tell, "crc32" / Int32ul, "data" / FixedSized( this.compr_size, Compressed(GreedyBytes, "blast")), "crc32" / Pointer(this.crc32_offset, Checksum(Int32ul, lambda l: crc32(l), this.data)))), # 2013 "tmap" / Pointer(
'width' / Int16ul, 'height' / Int16ul, 'format' / Const(0x02, Int8ul), 'palette_entries' / PaletteCountAdapter(Int8ul), 'palette' / Array(this.palette_entries, struct_blit_pixel), 'bit_length' / Computed(compute_bit_length), 'data_length' / Computed(compute_data_length), 'data' / Array(this.data_length, Int8ul) ) struct_blit_meta = Struct( 'header' / Const(b'BLITMETA'), 'data' / Prefixed(Int16ul, Struct( 'checksum' / Checksum( Int32ul, lambda data: binascii.crc32(data), this._._.bin.data ), 'date' / PaddedString(16, 'ascii'), 'title' / PaddedString(25, 'ascii'), 'description' / PaddedString(129, 'ascii'), 'version' / PaddedString(17, 'ascii'), 'author' / PaddedString(17, 'ascii'), Const(b'BLITTYPE'), 'category' / PaddedString(17, 'ascii'), 'url' / PaddedString(129, 'ascii'), 'filetypes' / PrefixedArray(Int8ul, PaddedString(5, 'ascii')), 'icon' / struct_blit_image, 'splash' / struct_blit_image )) )
def PacketChecksum(subcons): return Checksum(subcons, lambda data: calculate_checksum(data), this.fields.data)
WriteCommand = Struct("address" / Int16ul, "value" / Int16ul) WriteMessageRequest = Struct( "fields" / RawCopy( Struct( "length" / Rebuild( Int16ul, len_(this.items) * WriteCommand.sizeof() // Int16ul.sizeof() + 2, ), "command" / Const(vlxDevConstants.WS_WEB_UI_COMMAND_WRITE_DATA, Int16ul), "items" / WriteCommand[len_(this.items)], )), "checksum" / Checksum(Int16ul, lambda data: checksum_16(data), this.fields.data), ) ReadTableResponse = GreedyRange(Int16ub) ReadTableRequest = Struct( "fields" / RawCopy( Struct( "length" / Const(3, Int16ul), "command" / Const(vlxDevConstants.WS_WEB_UI_COMMAND_READ_TABLES, Int16ul), "items" / Const(0, Int16ul), )), "checksum" / Checksum(Int16ul, lambda data: checksum_16(data), this.fields.data), )
TIME_FORMAT = '%Y-%m-%d;%H:%M' crc16 = lambda val: '{:04x}'.format(CRCCCITT().calculate(val)).upper() TabTerminated = CString(terminators=b'\x09', encoding='cp1250') PosnetParameter = Struct( 'name' / Select(Const('@'), Const('?'), String(length=2)), 'value' / TabTerminated) PosnetFrame = Struct( Const(b'\x02'), 'summed' / RawCopy( Struct('instruction' / TabTerminated, 'parameters' / GreedyRange(PosnetParameter))), Const('#'), 'crc' / Checksum(Bytes(4), crc16, 'summed'), Const('\x03'), 'instruction' / Computed(this.summed.value.instruction), 'parameters' / Computed(this.summed.value.parameters)) def build_frame(instruction, *params): ''' Helper for building Posnet protocol frames out of instructions and params passed as (name, value) tuples. Can't use **kwargs: Posnet protocol uses reserved chars (such as @ and ?) ''' data = Container(summed=Container( value=Container(instruction=instruction, parameters=[ Container(name=name, value=value) for name, value in params