def test_entrylist_first_by_id(self): el = tlv8.EntryList([ tlv8.Entry(2, b'\x23'), tlv8.Entry(2, b'\x42') ]) self.assertEqual(el.first_by_id(1), None) self.assertEqual(el.first_by_id(2), el[0])
def do_char_write(self, tid, value): """The value is actually a TLV with a command to perform""" request = { entry.type_id: entry.data for entry in tlv8.decode( value, { TlvTypes.State: tlv8.DataType.INTEGER, TlvTypes.Method: tlv8.DataType.INTEGER, TlvTypes.Identifier: tlv8.DataType.BYTES, }) } logging.debug('%s', request) assert request[TlvTypes.State] == States.M1 if request[TlvTypes.Method] == Methods.RemovePairing: ident = request[TlvTypes.Identifier].decode() self.service.device.peers.pop(ident, None) # If ident == this session then disconnect it # self.service.device.disconnect() response = bytearray([0x02, tid, 0x00]) inner = tlv8.encode([ tlv8.Entry(TlvTypes.State, States.M2), ]) outer = tlv8.encode( [tlv8.Entry(AdditionalParameterTypes.Value, inner)]) response.extend(len(outer).to_bytes(length=2, byteorder='little')) response.extend(outer) self.queue_read_response(self.encrypt_value(bytes(response)))
def test_decode_example_3(self): input_data = b'\x01\x15\x01\x10e\xad\x8b\xe8\xb3fD\xcb\xbde#\xccc\n\xb8\xef\x02\x01\x01' structure = { 1: { 1: tlv8.DataType.BYTES, 2: tlv8.DataType.INTEGER, }, 2: { 1: tlv8.DataType.INTEGER, 2: tlv8.DataType.BYTES, 3: tlv8.DataType.BYTES, 4: tlv8.DataType.BYTES }, 3: { 1: tlv8.DataType.INTEGER, 2: tlv8.DataType.INTEGER, 3: tlv8.DataType.INTEGER, 4: tlv8.DataType.INTEGER, 5: tlv8.DataType.INTEGER } } result = tlv8.decode(input_data, structure) expected = tlv8.EntryList([ tlv8.Entry(1, tlv8.EntryList([ tlv8.Entry(1, b'e\xad\x8b\xe8\xb3fD\xcb\xbde#\xccc\n\xb8\xef'), tlv8.Entry(2, 1), ])), ]) self.assertEqual(expected, result)
def test_decode_missing_separator_nonstrict(self): data = b'\x01\x01\x02\x01\x01\x02' result = tlv8.deep_decode(data, strict_mode=False) self.assertEqual( result, tlv8.EntryList([tlv8.Entry(1, b'\x02'), tlv8.Entry(1, b'\x02')]))
def add_pairing(self, additional_controller_pairing_identifier, ios_device_ltpk, permissions): if not self.session: self.session = IpSession(self.pairing_data) if permissions == 'User': permissions = TlvTypes.Permission_RegularUser elif permissions == 'Admin': permissions = TlvTypes.Permission_AdminUser else: print('UNKNOWN') request_tlv = tlv8.encode([ tlv8.Entry(TlvTypes.State, States.M1), tlv8.Entry(TlvTypes.Method, Methods.AddPairing), tlv8.Entry(TlvTypes.Identifier, additional_controller_pairing_identifier.encode()), tlv8.Entry(TlvTypes.PublicKey, bytes.fromhex(ios_device_ltpk)), tlv8.Entry(TlvTypes.Permissions, permissions) ]) response = self.session.sec_http.post('/pairings', request_tlv) data = response.read() data = tlv8.decode( data, { TlvTypes.State: tlv8.DataType.INTEGER, TlvTypes.Error: tlv8.DataType.BYTES, }) # TODO handle the response properly self.session.close()
def test_entrylist_encode_same_sep_type(self): el = tlv8.EntryList([ tlv8.Entry(2, b'\x23'), tlv8.Entry(2, b'\x42') ]) result = el.encode(1) self.assertEqual(b'\x02\x01\x23\x01\x00\x02\x01\x42', result)
def test_entrylist_index(self): el = tlv8.EntryList() e1 = tlv8.Entry(1, 2) e2 = tlv8.Entry(3, 4) el.append(e1) el.append(e2) self.assertEqual(e2, el[1])
def test_entrylist_length(self): el = tlv8.EntryList() self.assertEqual(0, len(el)) el.append(tlv8.Entry(1, 2)) self.assertEqual(1, len(el)) el.append(tlv8.Entry(1, 2)) self.assertEqual(2, len(el))
def test_entrylist_by_id(self): el = tlv8.EntryList([ tlv8.Entry(2, b'\x23'), tlv8.Entry(2, b'\x42') ]) self.assertEqual(el.by_id(1), tlv8.EntryList()) self.assertEqual(el.by_id(2), el)
def test_encode_different(self): data = [ tlv8.Entry(23, b'23', tlv8.DataType.BYTES), tlv8.Entry(22, '23', tlv8.DataType.STRING) ] result = tlv8.encode(data) expected_data = data[0].encode() + data[1].encode() self.assertEqual(result, expected_data)
def test_encode_same_set_sep_type(self): data = [ tlv8.Entry(23, b'23', tlv8.DataType.BYTES), tlv8.Entry(23, '23', tlv8.DataType.STRING) ] result = tlv8.encode(data, 0) expected_data = data[0].encode() + b'\x00\x00' + data[1].encode() self.assertEqual(result, expected_data)
def test_encode_supported_rtp_configs(self): data = [tlv8.Entry(2, 0), tlv8.Entry(2, 1)] result = tlv8.encode(data, separator_type_id=0x00) expected_data = \ b'\x02\x01\x00' + \ b'\x00\x00' + \ b'\x02\x01\x01' self.assertEqual(result, expected_data)
def test_decode_misinterpretation(self): """This show how data may be misinterpreted by deep_decode""" data = tlv8.encode([tlv8.Entry(1, 16843330), tlv8.Entry(2, b'\x01')]) result = tlv8.deep_decode(data) expected_data = tlv8.EntryList([ tlv8.Entry(1, tlv8.EntryList([tlv8.Entry(66, b'\x01\x01')])), tlv8.Entry(2, b'\x01') ]) self.assertEqual(result, expected_data)
def test_autodetection_of_types(self): data = [ tlv8.Entry(1, 3.141), tlv8.Entry(2, [tlv8.Entry(3, 'hello'), tlv8.Entry(4, 'world')]), tlv8.Entry(1, 2) ] result = tlv8.encode(data) expected_data = b'\x01\x04%\x06I@\x02\x0e\x03\x05hello\x04\x05world\x01\x01\x02' self.assertEqual(result, expected_data)
def test_format_string(self): data = [ tlv8.Entry(1, 3.141), tlv8.Entry(2, [ tlv8.Entry(3, 'hello'), tlv8.Entry(4, 'world'), ]), tlv8.Entry(1, 2) ] print(tlv8.format_string(data))
def test_decode_4(self): input_data = b'\x01\x01\x23\x02\x03\x04\x01\x42\x01\x01\x23' result = tlv8.deep_decode(input_data) expected = tlv8.EntryList([ tlv8.Entry(1, b'#'), tlv8.Entry(2, tlv8.EntryList([ tlv8.Entry(4, b'B'), ])), tlv8.Entry(1, b'#'), ]) self.assertEqual(expected, result)
def write(request, expected): # TODO document me body = tlv8.encode(request) logger.debug('entering write function %s', tlv8.format_string(tlv8.decode(body))) request_tlv = tlv8.encode([ tlv8.Entry(AdditionalParameterTypes.ParamReturnResponse, bytearray(b'\x01')), tlv8.Entry(AdditionalParameterTypes.Value, body) ]) transaction_id = random.randrange(0, 255) # construct a hap characteristic write request following chapter 7.3.4.4 page 94 spec R2 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()) # write the request to the characteristic characteristic.write_value(value=data) # reading hap characteristic write response following chapter 7.3.4.5 page 95 spec R2 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 = tlv8.decode( bytes([int(a) for a in resp_data[5:]]), expected={AdditionalParameterTypes.Value: tlv8.DataType.BYTES}) result = tlv8.decode( resp_tlv.first_by_id(AdditionalParameterTypes.Value).data, expected) logger.debug('leaving write function %s', tlv8.format_string(result)) return result
def perform_pair_setup_part1(): """ Performs a pair setup operation as described in chapter 4.7 page 39 ff. :return: a tuple of salt and server's public key :raises UnavailableError: if the device is already paired :raises MaxTriesError: if the device received more than 100 unsuccessful pairing attempts :raises BusyError: if a parallel pairing is ongoing :raises AuthenticationError: if the verification of the device's SRP proof fails :raises MaxPeersError: if the device cannot accept an additional pairing :raises IllegalData: if the verification of the accessory's data fails """ # # Step #1 ios --> accessory (send SRP start Request) (see page 39) # logging.debug('#1 ios -> accessory: send SRP start request') request_tlv = [ tlv8.Entry(TlvTypes.State, States.M1), tlv8.Entry(TlvTypes.Method, Methods.PairSetup) ] step2_expectations = { TlvTypes.State: tlv8.DataType.INTEGER, TlvTypes.Error: tlv8.DataType.INTEGER, TlvTypes.PublicKey: tlv8.DataType.BYTES, TlvTypes.Salt: tlv8.DataType.BYTES } response_tlv = yield (request_tlv, step2_expectations) # # Step #3 ios --> accessory (send SRP verify request) (see page 41) # logging.debug('#3 ios -> accessory: send SRP verify request') assert response_tlv.first_by_id( TlvTypes.State) and response_tlv.first_by_id( TlvTypes.State ).data == States.M2, 'perform_pair_setup: State not M2' # the errors here can be: # * kTLVError_Unavailable: Device is paired # * kTLVError_MaxTries: More than 100 unsuccessful attempts # * kTLVError_Busy: There is already a pairing going on error = response_tlv.first_by_id(TlvTypes.Error) if error: error_handler(error.data, 'step 3') assert response_tlv.first_by_id( TlvTypes.PublicKey), 'perform_pair_setup: Not a public key' assert response_tlv.first_by_id( TlvTypes.Salt), 'perform_pair_setup: Not a salt' return response_tlv.first_by_id(TlvTypes.Salt).data, \ response_tlv.first_by_id(TlvTypes.PublicKey).data
def test_decode_supported_rtp_configs(self): data = \ b'\x02\x01\x00' + \ b'\x00\x00' + \ b'\x02\x01\x01' result = tlv8.decode(data, expected={2: tlv8.DataType.INTEGER}) expected_data = tlv8.EntryList([ tlv8.Entry(2, 0), tlv8.Entry(2, 1) ]) self.assertEqual(result, expected_data)
def test_encode_3same(self): data = [ tlv8.Entry(23, b'23', tlv8.DataType.BYTES), tlv8.Entry(23, '23', tlv8.DataType.STRING), tlv8.Entry(23, '23', tlv8.DataType.STRING) ] result = tlv8.encode(data) expected_data = \ data[0].encode() + b'\xff\x00' + \ data[1].encode() + b'\xff\x00' + \ data[2].encode() self.assertEqual(result, expected_data)
def test_decode_supported_rtp_configs(self): data = \ b'\x02\x01\x00' + \ b'\x00\x00' + \ b'\x02\x01\x01' result = tlv8.deep_decode(data, ) expected_data = tlv8.EntryList([ tlv8.Entry(2, b'\x00'), tlv8.Entry(0, tlv8.EntryList()), tlv8.Entry(2, b'\x01') ]) self.assertEqual(result, expected_data)
def test_decode_example_3(self): input_data = b'\x01\x15\x01\x10e\xad\x8b\xe8\xb3fD\xcb\xbde#\xccc\n\xb8\xef\x02\x01\x01' result = tlv8.deep_decode(input_data) expected = tlv8.EntryList([ tlv8.Entry( 1, tlv8.EntryList([ tlv8.Entry( 1, b'e\xad\x8b\xe8\xb3fD\xcb\xbde#\xccc\n\xb8\xef'), tlv8.Entry(2, b'\x01'), ])), ]) self.assertEqual(expected, result)
def test_decode_example_2(self): input_data = b'\x01\x01\x00' result = tlv8.deep_decode(input_data) expected = tlv8.EntryList([ tlv8.Entry(1, b'\x00'), ]) self.assertEqual(expected, result)
def test_decode_2_entries_1_expected(self): input_data = b'\x02\x01\x23\x03\x01\x42' structure = { 2: tlv8.DataType.INTEGER } result = tlv8.decode(input_data, structure) self.assertEqual(tlv8.EntryList([tlv8.Entry(2, 0x23)]), result)
def test_decode_error_4(self): data = b'\x01\x02Hi' result = tlv8.decode(data, {1: tlv8.DataType.STRING}) expected_data = tlv8.EntryList([ tlv8.Entry(1, 'Hi') ]) self.assertEqual(result, expected_data)
def test_decode_float(self): input_data = b'\x01\x04' + pack('<f', 3.141) result = tlv8.deep_decode(input_data) expected = tlv8.EntryList([ tlv8.Entry(1, b'%\x06I@'), ]) self.assertEqual(expected, result)
def test_decode_int1(self): input_data = b'\x01\x01' + pack('<b', -123) result = tlv8.deep_decode(input_data) expected = tlv8.EntryList([ tlv8.Entry(1, b'\x85'), ]) self.assertEqual(expected, result)
def test_decode_int2(self): input_data = b'\x01\x02' + pack('<h', 12345) result = tlv8.deep_decode(input_data) expected = tlv8.EntryList([ tlv8.Entry(1, b'90'), ]) self.assertEqual(expected, result)
def test_bytes_257bytes(self): data = b'' for i in range(0, 257): data += pack('<B', i % 256) entry = tlv8.Entry(23, data, tlv8.DataType.BYTES) expected_data = b'\x17\xff' + data[0:255] + b'\x17\x02' + data[255:] self.assertEqual(entry.encode(), expected_data)
def test_decode_int4(self): input_data = b'\x01\x08' + pack('<q', 4611686018427387904) result = tlv8.deep_decode(input_data) expected = tlv8.EntryList([ tlv8.Entry(1, b'\x00\x00\x00\x00\x00\x00\x00@'), ]) self.assertEqual(expected, result)