def test_to_string_for_dict(self): example = {1: 'hello'} res = TLV.to_string(example) self.assertEqual(res, '{\n 1: (5 bytes) hello\n}\n') example = {1: 'hello', 2: 'world'} res = TLV.to_string(example) self.assertEqual(res, '{\n 1: (5 bytes) hello\n 2: (5 bytes) world\n}\n')
def write_http(request, expected): logging.debug('write message: %s', TLV.to_string(TLV.decode_bytes(request))) headers = {'Content-Type': 'application/pairing+tlv8'} connection.request('POST', '/pair-verify', request, headers) resp = connection.getresponse() response_tlv = TLV.decode_bytes(resp.read(), expected) logging.debug('response: %s', TLV.to_string(response_tlv)) return response_tlv
def write_http(request, expected): logging.debug('write message: %s', TLV.to_string(TLV.decode_bytes(request))) connection.putrequest('POST', '/pair-verify', skip_accept_encoding=True) connection.putheader('Content-Type', 'application/pairing+tlv8') connection.putheader('Content-Length', len(request)) connection.endheaders(request) resp = connection.getresponse() response_tlv = TLV.decode_bytes(resp.read(), expected) logging.debug('response: %s', TLV.to_string(response_tlv)) return response_tlv
def write(request, expected): # TODO document me body = TLV.encode_list(request) logger.debug('entering write function %s', TLV.to_string(TLV.decode_bytes(body))) request_tlv = TLV.encode_list([(TLV.kTLVHAPParamParamReturnResponse, bytearray(b'\x01')), (TLV.kTLVHAPParamValue, body)]) transaction_id = random.randrange(0, 255) data = bytearray([0x00, HapBleOpCodes.CHAR_WRITE, transaction_id]) data.extend(characteristic_id.to_bytes(length=2, byteorder='little')) data.extend(len(request_tlv).to_bytes(length=2, byteorder='little')) data.extend(request_tlv) logger.debug('sent %s', bytes(data).hex()) characteristic.write_value(value=data) data = [] while len(data) == 0: time.sleep(1) logger.debug('reading characteristic') data = characteristic.read_value() resp_data = [b for b in data] expected_length = int.from_bytes(bytes(resp_data[3:5]), byteorder='little') logger.debug( 'control field: {c:x}, tid: {t:x}, status: {s:x}, length: {length}' .format(c=resp_data[0], t=resp_data[1], s=resp_data[2], length=expected_length)) while len(resp_data[3:]) < expected_length: time.sleep(1) logger.debug('reading characteristic') data = characteristic.read_value() resp_data.extend([b for b in data]) logger.debug('data %s of %s', len(resp_data[3:]), expected_length) logger.debug('received %s', bytes(resp_data).hex()) logger.debug('decode %s', bytes(resp_data[5:]).hex()) resp_tlv = TLV.decode_bytes(bytes([int(a) for a in resp_data[5:]]), expected=[TLV.kTLVHAPParamValue]) result = TLV.decode_bytes(resp_tlv[0][1], expected) logger.debug('leaving write function %s', TLV.to_string(result)) return result
def test_to_string_for_list(self): example = [ ( 1, 'hello', ), ] res = TLV.to_string(example) self.assertEqual(res, '[\n 1: (5 bytes) hello\n]\n') example = [ ( 1, 'hello', ), ( 2, 'world', ), ] res = TLV.to_string(example) self.assertEqual(res, '[\n 1: (5 bytes) hello\n 2: (5 bytes) world\n]\n')
def test_to_string_for_list_bytearray(self): example = [[1, bytearray([0x42, 0x23])]] res = TLV.to_string(example) self.assertEqual(res, '[\n 1: (2 bytes) 0x4223\n]\n')
def test_to_string_for_dict_bytearray(self): example = {1: bytearray([0x42, 0x23])} res = TLV.to_string(example) self.assertEqual(res, '{\n 1: (2 bytes) 0x4223\n}\n')
def request(self, feature_char, feature_char_id, op, body=None): transaction_id = random.randrange(0, 255) data = bytearray([0x00, op, transaction_id]) data.extend(feature_char_id.to_bytes(length=2, byteorder='little')) if body: logger.debug('body: %s', body) data.extend(body) logger.debug('data: %s', data) cnt_bytes = self.c2a_counter.to_bytes(8, byteorder='little') cipher_and_mac = chacha20_aead_encrypt(bytes(), self.c2a_key, cnt_bytes, bytes([0, 0, 0, 0]), data) cipher_and_mac[0].extend(cipher_and_mac[1]) data = cipher_and_mac[0] logger.debug('cipher and mac %s', cipher_and_mac[0].hex()) result = feature_char.write_value(value=data) logger.debug('write resulted in: %s', result) self.c2a_counter += 1 data = [] while not data or len(data) == 0: time.sleep(1) logger.debug('reading characteristic') data = feature_char.read_value() if not data and not self.device.is_connected(): raise AccessoryDisconnectedError('Characteristic read failed') resp_data = bytearray([b for b in data]) logger.debug('read: %s', bytearray(resp_data).hex()) data = chacha20_aead_decrypt(bytes(), self.a2c_key, self.a2c_counter.to_bytes(8, byteorder='little'), bytes([0, 0, 0, 0]), resp_data) logger.debug('decrypted: %s', bytearray(data).hex()) if not data: return {} # parse header and check stuff logger.debug('parse sig read response %s', bytes([int(a) for a in data]).hex()) # handle the header data cf = data[0] logger.debug('control field %d', cf) tid = data[1] logger.debug('transaction id %d (expected was %d)', tid, transaction_id) status = data[2] logger.debug('status code %d (%s)', status, HapBleStatusCodes[status]) assert cf == 0x02 assert tid == transaction_id if status != HapBleStatusCodes.SUCCESS: raise RequestRejected(status, HapBleStatusCodes[status]) self.a2c_counter += 1 # get body length length = int.from_bytes(data[3:5], byteorder='little') logger.debug('expected body length %d (got %d)', length, len(data[5:])) # parse tlvs and analyse information tlv = TLV.decode_bytes(data[5:]) logger.debug('received TLV: %s', TLV.to_string(tlv)) return dict(tlv)