コード例 #1
0
    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
コード例 #2
0
 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)
コード例 #3
0
def makePartitionCsv(bytes_data, csvFile):
    dict = {}
    containerId = ""
    for entry in tlv8.decode(bytes_data):
        if entry.type_id == HAP_KEYSTORE_TYPE.HAP_KEYSTORE_TYPE_CONTAINER_ID.value:
            containerId = entry.data.__str__()

        if entry.type_id in dict:
            dict[entry.type_id] = dict[entry.type_id] + entry.data
        else:
            dict[entry.type_id] = entry.data

    lines = []
    lines.append("key,type,encoding,value")
    lines.append("keystore,namespace,,")
    lines.append("isValid,data,u8,1")

    for key, value in dict.items():
        if key is HAP_KEYSTORE_TYPE.HAP_KEYSTORE_TYPE_SIGNATURE.value:
            pass
        elif key is HAP_KEYSTORE_TYPE.HAP_KEYSTORE_TYPE_CONTAINER_ID.value:
            lines.append("0x{:02X},data,u8,{}".format(key,
                                                      int(value.hex(), 16)))
        else:
            lines.append("0x{:02X},data,hex2bin,{}".format(key, value.hex()))

    with open(csvFile, "w") as fp:
        for line in lines:
            fp.write("%s\n" % line)

    return containerId
コード例 #4
0
 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)
コード例 #5
0
 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)
コード例 #6
0
def decoder(bytes_data):
    return tlv8.decode(
        bytes_data, {
            SupportedVideoStreamConfigurationKeys.VIDEO_CODEC_CONFIGURATION: {
                VideoCodecConfigurationKeys.VIDEO_CODEC_TYPE:
                VideoCodecTypeValues,
                VideoCodecConfigurationKeys.VIDEO_CODEC_PARAMETERS: {
                    VideoCodecParametersKeys.PROFILE_ID: ProfileIdValues,
                    VideoCodecParametersKeys.LEVEL: LevelValues,
                    VideoCodecParametersKeys.PACKETIZATION_MODE:
                    PacketizationModeValues,
                    VideoCodecParametersKeys.CVO_ENABLED:
                    PacketizationModeValues,
                    VideoCodecParametersKeys.CVO_ID:
                    tlv8.DataType.UNSIGNED_INTEGER
                },
                VideoCodecConfigurationKeys.VIDEO_ATTRIBUTES: {
                    VideoAttributesKeys.IMAGE_WIDTH:
                    tlv8.DataType.UNSIGNED_INTEGER,
                    VideoAttributesKeys.IMAGE_HEIGHT:
                    tlv8.DataType.UNSIGNED_INTEGER,
                    VideoAttributesKeys.FRAME_RATE:
                    tlv8.DataType.UNSIGNED_INTEGER,
                },
            }
        })
コード例 #7
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)))
コード例 #8
0
    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()
コード例 #9
0
def check_convert_value(val, target_type):
    """
    Checks if the given value is of the given type or is convertible into the type. If the value is not convertible, a
    HomeKitTypeException is thrown.

    :param val: the original value
    :param target_type: the target type of the conversion
    :return: the converted value
    :raises FormatError: if the input value could not be converted to the target type
    """
    if target_type == CharacteristicFormats.bool:
        try:
            val = strtobool(str(val))
        except ValueError:
            raise FormatError('"{v}" is no valid "{t}"!'.format(v=val,
                                                                t=target_type))
    if target_type in [
            CharacteristicFormats.uint64, CharacteristicFormats.uint32,
            CharacteristicFormats.uint16, CharacteristicFormats.uint8,
            CharacteristicFormats.int
    ]:
        try:
            val = int(val)
        except ValueError:
            raise FormatError('"{v}" is no valid "{t}"!'.format(v=val,
                                                                t=target_type))
    if target_type == CharacteristicFormats.float:
        try:
            val = float(val)
        except ValueError:
            raise FormatError('"{v}" is no valid "{t}"!'.format(v=val,
                                                                t=target_type))
    if target_type == CharacteristicFormats.data:
        try:
            base64.decodebytes(val.encode())
        except binascii.Error:
            raise FormatError('"{v}" is no valid "{t}"!'.format(v=val,
                                                                t=target_type))
    if target_type == CharacteristicFormats.tlv8:
        try:
            tmp_bytes = base64.decodebytes(val.encode())
            tlv8.decode(tmp_bytes, {})
        except (binascii.Error, ValueError):
            raise FormatError('"{v}" is no valid "{t}"!'.format(v=val,
                                                                t=target_type))
    return val
コード例 #10
0
    def test_decode_key(self):
        class TestKeys(enum.IntEnum):
            KEY_1 = 1
            KEY_2 = 2

        data = b'\x01\x03foo'
        result = tlv8.decode(data, {TestKeys.KEY_1: tlv8.DataType.STRING})
        expected = tlv8.EntryList([tlv8.Entry(TestKeys.KEY_1, 'foo')])
        self.assertIsInstance(result[0].type_id, TestKeys)
        self.assertEqual(expected, result)
コード例 #11
0
 def test_decode_float(self):
     input_data = b'\x01\x04' + pack('<f', 3.141)
     structure = {
         1: tlv8.DataType.FLOAT,
     }
     result = tlv8.decode(input_data, structure)
     expected = tlv8.EntryList([
         tlv8.Entry(1, 3.141),
     ])
     self.assertEqual(expected, result)
コード例 #12
0
 def test_decode_example_2(self):
     input_data = b'\x01\x01\x00'
     structure = {
         1: tlv8.DataType.INTEGER,
     }
     result = tlv8.decode(input_data, structure)
     expected = tlv8.EntryList([
         tlv8.Entry(1, 0),
     ])
     self.assertEqual(expected, result)
コード例 #13
0
    def test_decode_value(self):
        class TestValues(enum.IntEnum):
            VALUE_1 = 1
            VALUE_2 = 2

        data = b'\x01\x01\x02'
        result = tlv8.decode(data, {1: TestValues})
        expected = tlv8.EntryList([tlv8.Entry(1, 2)])
        self.assertIsInstance(result[0].data, TestValues)
        self.assertEqual(expected, result)
コード例 #14
0
 def test_decode_int2_un(self):
     input_data = b'\x01\x02' + pack('<H', 12345)
     structure = {
         1: tlv8.DataType.UNSIGNED_INTEGER,
     }
     result = tlv8.decode(input_data, structure)
     expected = tlv8.EntryList([
         tlv8.Entry(1, 12345),
     ])
     self.assertEqual(expected, result)
コード例 #15
0
 def test_decode_int1_pos(self):
     input_data = b'\x01\x01' + pack('<b', 123)
     structure = {
         1: tlv8.DataType.INTEGER,
     }
     result = tlv8.decode(input_data, structure)
     expected = tlv8.EntryList([
         tlv8.Entry(1, 123),
     ])
     self.assertEqual(expected, result)
コード例 #16
0
 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)
コード例 #17
0
    def test_decode_int4_neg(self):
        input_data = b'\x01\x04' + pack('<i', -12345)
        structure = {
            1: tlv8.DataType.INTEGER,
        }
        result = tlv8.decode(input_data, structure)

        expected = tlv8.EntryList([
            tlv8.Entry(1, -12345),
        ])
        self.assertEqual(expected, result)
コード例 #18
0
    def test_decode_int8_un(self):
        input_data = b'\x01\x08' + pack('<q', 4611686018427387904)
        structure = {
            1: tlv8.DataType.UNSIGNED_INTEGER,
        }
        result = tlv8.decode(input_data, structure)

        expected = tlv8.EntryList([
            tlv8.Entry(1, 4611686018427387904),
        ])
        self.assertEqual(expected, result)
コード例 #19
0
 def write_http(request, expected):
     body = tlv8.encode(request)
     logging.debug('write message: %s',
                   tlv8.format_string(tlv8.deep_decode(body)))
     connection.putrequest('POST', '/pair-setup', skip_accept_encoding=True)
     connection.putheader('Content-Type', 'application/pairing+tlv8')
     connection.putheader('Content-Length', len(body))
     connection.endheaders(body)
     resp = connection.getresponse()
     response_tlv = tlv8.decode(resp.read(), expected)
     logging.debug('response: %s', tlv8.format_string(response_tlv))
     return response_tlv
コード例 #20
0
 def test_entrylist_decode_nested(self):
     data = b'\x01\x04%\x06I@\x02\x0e\x03\x05hello\x04\x05world\x03\x01\x02'
     result = tlv8.decode(data, {
         1: tlv8.DataType.FLOAT,
         2: {
             3: tlv8.DataType.STRING,
             4: tlv8.DataType.STRING,
         },
         3: tlv8.DataType.INTEGER
     })
     self.assertIsInstance(result, tlv8.EntryList)
     self.assertIsInstance(result[1].data, tlv8.EntryList)
コード例 #21
0
    def test_decode_257bytes(self):
        data = b''
        for i in range(0, 257):
            data += pack('<B', i % 256)

        input_data = b'\x17\xff' + data[0:255] + b'\x17\x02' + data[255:]

        result = tlv8.decode(input_data)

        expected = tlv8.EntryList([
            tlv8.Entry(23, data)
        ])
        self.assertEqual(result, expected)
コード例 #22
0
def decoder(bytes_data):
    return tlv8.decode(
        bytes_data, {
            SupportedAudioStreamConfigurationKeys.AUDIO_CODEC_CONFIGURATION: {
                AudioCodecConfigurationKeys.CODEC_TYPE: AudioCodecTypeValues,
                AudioCodecConfigurationKeys.AUDIO_CODEC_PARAMETERS: {
                    AudioCodecParametersKeys.AUDIO_CHANNELS:
                    tlv8.DataType.UNSIGNED_INTEGER,
                    AudioCodecParametersKeys.BIT_RATE: BitRateValues,
                    AudioCodecParametersKeys.SAMPLE_RATE: SampleRateValues,
                    AudioCodecParametersKeys.RTP_TIME: RtpTimeValues
                }
            },
            SupportedAudioStreamConfigurationKeys.COMFORT_NOISE_SUPPORT: {}
        })
コード例 #23
0
    def test_decode_enum(self):
        class TestKeys(enum.IntEnum):
            KEY_1 = 1
            KEY_2 = 2

        class TestValues(enum.IntEnum):
            VALUE_1 = 1
            VALUE_2 = 2

        data = b'\x01\x01\x02'
        result = tlv8.decode(data, {TestKeys.KEY_1: TestValues})
        expected = tlv8.EntryList(
            [tlv8.Entry(TestKeys.KEY_1, TestValues.VALUE_2)])
        self.assertIsInstance(result[0].type_id, TestKeys)
        self.assertIsInstance(result[0].data, TestValues)
        self.assertEqual(expected, result)
コード例 #24
0
 def test_decode_4(self):
     input_data = b'\x01\x01\x23\x02\x03\x04\x01\x42\x01\x01\x23'
     structure = {
         1: tlv8.DataType.INTEGER,
         2: {
             4: tlv8.DataType.INTEGER,
         }
     }
     result = tlv8.decode(input_data, structure)
     expected = tlv8.EntryList([
         tlv8.Entry(1, 0x23),
         tlv8.Entry(2, tlv8.EntryList([
             tlv8.Entry(4, 0x42),
         ])),
         tlv8.Entry(1, 0x23),
     ])
     self.assertEqual(expected, result)
コード例 #25
0
    def write_value(self, value):
        assert value[0] == 0
        opcode = value[1]

        if opcode == 2:
            outer = {
                entry.type_id: entry.data
                for entry in tlv8.decode(value[7:])
            }
            assert outer[
                AdditionalParameterTypes.ParamReturnResponse] == b'\x01'

            value = self.rh.process_verify(
                value[2], outer[AdditionalParameterTypes.Value])
            self.values.append(value)
        else:
            super().write_value(value)
コード例 #26
0
    def list_pairings(self):
        if not self.session:
            self.session = BleSession(self.pairing_data, self.adapter)
        request_tlv = tlv8.encode([
            tlv8.Entry(TlvTypes.State, States.M1),
            tlv8.Entry(TlvTypes.Method, Methods.ListPairings)
        ])
        request_tlv = tlv8.encode([
            tlv8.Entry(AdditionalParameterTypes.ParamReturnResponse,
                       bytearray(b'\x01')),
            tlv8.Entry(AdditionalParameterTypes.Value, request_tlv)
        ])
        body = len(request_tlv).to_bytes(length=2,
                                         byteorder='little') + request_tlv

        cid = -1
        for a in self.pairing_data['accessories']:
            for s in a['services']:
                for c in s['characteristics']:
                    if CharacteristicsTypes.get_short_uuid(c['type'].upper(
                    )) == CharacteristicsTypes.PAIRING_PAIRINGS:
                        cid = c['iid']
        fc, _ = self.session.find_characteristic_by_iid(cid)
        response = self.session.request(fc, cid, HapBleOpCodes.CHAR_WRITE,
                                        body)
        response = tlv8.decode(
            response.first_by_id(AdditionalParameterTypes.Value).data)
        tmp = []
        r = {}
        for d in response[1:]:
            if d.type_id == TlvTypes.Identifier:
                r = {}
                tmp.append(r)
                r['pairingId'] = d.data.decode()
            if d.type_id == TlvTypes.PublicKey:
                r['publicKey'] = d.data.hex()
            if d.type_id == TlvTypes.Permissions:
                controller_type = 'regular'
                if d.data == b'\x01':
                    controller_type = 'admin'
                r['permissions'] = int.from_bytes(d.data, byteorder='little')
                r['controllerType'] = controller_type
        tmp.sort(key=lambda x: x['pairingId'])
        return tmp
コード例 #27
0
def decoder(bytes_data):
    return tlv8.decode(bytes_data, {
        SelectedRtpStreamConfigurationKeys.SESSION_CONTROL: {
            SessionControlKeys.SESSION_IDENTIFIER: tlv8.DataType.BYTES,
            SessionControlKeys.COMMAND: CommandValues,
        },
        SelectedRtpStreamConfigurationKeys.SELECTED_AUDIO_PARAMS: {
            SelectedAudioParametersKeys.SELECTED_AUDIO_CODEC_TYPE: AudioCodecTypeValues,
            SelectedAudioParametersKeys.SELECTED_AUDIO_CODEC_PARAMETERS: {
                AudioCodecParametersKeys.AUDIO_CHANNELS: tlv8.DataType.INTEGER,
                AudioCodecParametersKeys.BIT_RATE: BitRateValues,
                AudioCodecParametersKeys.SAMPLE_RATE: SampleRateValues,
                AudioCodecParametersKeys.RTP_TIME: RtpTimeValues,
            },
            SelectedAudioParametersKeys.SELECTED_AUDIO_RTP_PARAMETERS: {
                AudioRtpParametersKeys.PAYLOAD_TYPE: tlv8.DataType.INTEGER,
                AudioRtpParametersKeys.SSRC_FOR_AUDIO: tlv8.DataType.BYTES,
                AudioRtpParametersKeys.MAX_BITRATE: tlv8.DataType.INTEGER,
                AudioRtpParametersKeys.MIN_RTCP: tlv8.DataType.FLOAT,
                AudioRtpParametersKeys.COMFORT_NOISE: tlv8.DataType.INTEGER,
            },
            SelectedAudioParametersKeys.COMFORT_NOISE: tlv8.DataType.INTEGER,
        },
        SelectedRtpStreamConfigurationKeys.SELECTED_VIDEO_PARAMS: {
            SelectedVideoParametersKeys.SELECTED_VIDEO_CODEC_TYPE: VideoCodecTypeValues,
            SelectedVideoParametersKeys.SELECTED_VIDEO_CODEC_PARAMETERS: {
                VideoCodecParametersKeys.PROFILE_ID: ProfileIdValues,
                VideoCodecParametersKeys.LEVEL: LevelValues,
                VideoCodecParametersKeys.PACKETIZATION_MODE: PacketizationModeValues,
                VideoCodecParametersKeys.CVO_ENABLED: CVOEnabledValue,
            },
            SelectedVideoParametersKeys.SELECTED_VIDEO_ATTRIBUTES: {
                VideoAttributesKeys.IMAGE_WIDTH: tlv8.DataType.INTEGER,
                VideoAttributesKeys.IMAGE_HEIGHT: tlv8.DataType.INTEGER,
                VideoAttributesKeys.FRAME_RATE: tlv8.DataType.INTEGER,
            },
            SelectedVideoParametersKeys.SELECTED_VIDEO_RTP_PARAMETERS: {
                VideoRTPParametersKeys.PAYLOAD_TYPE: tlv8.DataType.INTEGER,
                VideoRTPParametersKeys.SSRC_FOR_VIDEO: tlv8.DataType.BYTES,
                VideoRTPParametersKeys.MAX_BITRATE: tlv8.DataType.INTEGER,
                VideoRTPParametersKeys.MIN_RTCP: tlv8.DataType.FLOAT,
            }
        }
    })
コード例 #28
0
def decoder(bytes_data):
    srtp_params = {
        SrtpParameterKeys.SRTP_CRYPTO_SUITE: CameraSRTPCryptoSuiteValues,
        SrtpParameterKeys.SRTP_MASTER_KEY: tlv8.DataType.BYTES,
        SrtpParameterKeys.SRTP_MASTER_SALT: tlv8.DataType.BYTES,
    }
    return tlv8.decode(bytes_data, {
        SetupEndpointsKeys.SESSION_ID: tlv8.DataType.BYTES,
        SetupEndpointsKeys.STATUS: EndpointStatusValues,
        SetupEndpointsKeys.ADDRESS: {
            ControllerAddressKeys.IP_ADDRESS_VERSION: IPVersionValues,
            ControllerAddressKeys.IP_ADDRESS: tlv8.DataType.STRING,
            ControllerAddressKeys.VIDEO_RTP_PORT: tlv8.DataType.UNSIGNED_INTEGER,
            ControllerAddressKeys.AUDIO_RTP_PORT: tlv8.DataType.UNSIGNED_INTEGER,
        },
        SetupEndpointsKeys.SRTP_PARAMETERS_FOR_VIDEO: srtp_params,
        SetupEndpointsKeys.SRTP_PARAMETERS_FOR_AUDIO: srtp_params,
        SetupEndpointsKeys.VIDEO_RTP_SSRC: tlv8.DataType.BYTES,
        SetupEndpointsKeys.AUDIO_RTP_SSRC: tlv8.DataType.BYTES,
    })
コード例 #29
0
def parse_sig_read_response(data, expected_tid):
    # TODO document me
    # 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, expected_tid)
    status = data[2]
    logger.debug('status code %d (%s)', status, HapBleStatusCodes[status])
    assert cf == 0x02
    assert tid == expected_tid
    assert status == HapBleStatusCodes.SUCCESS

    # 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 = tlv8.decode(bytes([int(a) for a in data[5:]]))

    description = ''
    characteristic_format = ''
    characteristic_range = None
    characteristic_step = None
    for t in tlv:
        if t.type_id == AdditionalParameterTypes.CharacteristicType:
            chr_type = [int(a) for a in t.data]
            chr_type.reverse()
            chr_type = str(uuid.UUID(''.join('%02x' % b for b in chr_type)))
        if t.type_id == AdditionalParameterTypes.ServiceInstanceId:
            svc_id = int.from_bytes(t.data, byteorder='little')
        if t.type_id == AdditionalParameterTypes.ServiceType:
            svc_type = [int(a) for a in t.data]
            svc_type.reverse()
            svc_type = str(uuid.UUID(''.join('%02x' % b for b in svc_type)))
        if t.type_id == AdditionalParameterTypes.HAPCharacteristicPropertiesDescriptor:
            chr_prop_int = int.from_bytes(t.data, byteorder='little')
        if t.type_id == AdditionalParameterTypes.GATTUserDescriptionDescriptor:
            description = t.data.decode()
        if t.type_id == AdditionalParameterTypes.HAPValidValuesDescriptor:
            print('valid values', t.data)
        if t.type_id == AdditionalParameterTypes.HAPValidValuesRangeDescriptor:
            print('valid values range', t.data)
        if t.type_id == AdditionalParameterTypes.GATTPresentationFormatDescriptor:
            unit_bytes = bytearray(t.data[2:4])
            unit_bytes.reverse()
            characteristic_format = BleCharacteristicFormats.get(
                int(t.data[0]), 'unknown')
            unit = BleCharacteristicUnits.get(
                int.from_bytes(unit_bytes, byteorder='big'), 'unknown')
        if t.type_id == AdditionalParameterTypes.GATTValidRange:
            logger.debug('range: %s', t.data.hex())
            lower = None
            upper = None
            if characteristic_format == 'int32' or characteristic_format == 'int':
                (lower, upper) = struct.unpack('ii', t.data)
            if characteristic_format == 'uint8':
                (lower, upper) = struct.unpack('BB', t.data)
            if characteristic_format == 'float':
                (lower, upper) = struct.unpack('ff', t.data)
            # TODO include all formats!
            characteristic_range = (lower, upper)
        if t.type_id == AdditionalParameterTypes.HAPStepValueDescriptor:
            characteristic_step = None
            if characteristic_format == 'int32':
                characteristic_step = struct.unpack('i', t.data)[0]
            if characteristic_format == 'uint8':
                characteristic_step = struct.unpack('B', t.data)[0]
            # TODO include all formats!

    # parse permissions
    # TODO refactor!
    perms = []
    if (chr_prop_int & 0x0001) > 0:
        perms.append('r')
    if (chr_prop_int & 0x0002) > 0:
        perms.append('w')
    if (chr_prop_int & 0x0004) > 0:
        perms.append('aad')
    if (chr_prop_int & 0x0008) > 0:
        perms.append('tw')
    if (chr_prop_int & 0x0010) > 0:
        perms.append('pr')
    if (chr_prop_int & 0x0020) > 0:
        perms.append('pw')
    if (chr_prop_int & 0x0040) > 0:
        perms.append('hd')
    if (chr_prop_int & 0x0080) > 0:
        perms.append('evc')
    if (chr_prop_int & 0x0100) > 0:
        perms.append('evd')

    result = {
        'description': description,
        'perms': perms,
        'format': characteristic_format,
        'unit': unit,
        'range': characteristic_range,
        'step': characteristic_step,
        'type': chr_type.upper(),
        'sid': svc_id,
        'service_type': svc_type
    }
    logger.debug('result: %s', str(result))

    return result
コード例 #30
0
    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 = tlv8.decode(data[5:])
        logger.debug('received TLV: %s', tlv8.format_string(tlv))
        return tlv