Ejemplo n.º 1
0
    def test_imu_sustain(self):
        current_time = 0
        _mock_time_func = lambda: current_time

        ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
        ds4key = ds4.DS4Key(ds4key_io)
        features = ds4.FeatureConfiguration()
        tracker = ds4.DS4StateTracker(ds4key, features, imu_time_func=_mock_time_func)
        # Put into attitude-position mode
        tracker.imu.use_attitude_position = True

        # (30, 30, 30), (0, 0, 0) @ 50ms
        current_time = 0.05
        tracker.imu.set_attitude_position(current_time, 30, 30, 30, 0, 0, 0)
        report = tracker.prepare_for_report_submission()
        actual = bytes(report)[13:25].hex()
        expected = '6c266c266c26000000000000'
        self.assertEqual(actual, expected)

        # Sustain @ 100ms
        current_time = 0.10
        report = tracker.prepare_for_report_submission()
        actual = bytes(report)[13:25].hex()
        expected = '000000000000000000000000'
        self.assertEqual(actual, expected)

        # (45, 45, 45), (3, 4, 5) @ 150ms
        current_time = 0.15
        tracker.imu.set_attitude_position(current_time, 45, 45, 45, 3, 4, 5)
        report = tracker.prepare_for_report_submission()
        actual = bytes(report)[13:25].hex()
        expected = '361336133613eb0339058806'
        self.assertEqual(actual, expected)
Ejemplo n.º 2
0
    def test_ds4key_sign(self):
        '''
        Challenge signing with DS4Key object directly.
        '''
        ds4key_bytes = base64.b64decode(TEST_DS4KEY)
        ds4key_io = io.BytesIO(ds4key_bytes)
        ds4id_expected = ds4key_bytes[:0x310]

        ds4key = ds4.DS4Key(ds4key_io)

        challenge = os.urandom(256)
        response = ds4key.sign_challenge(challenge)

        resp_bytes = bytes(response)
        sig = resp_bytes[:0x100]
        ds4id = resp_bytes[0x100:]

        self.assertEqual(ds4id.hex(), ds4id_expected.hex(), 'DS4ID mismatch.')
        ds4key_key = RSA.import_key(TEST_DS4KEY_JUST_KEY)
        ds4key_pss = pss.new(ds4key_key)
        sha = SHA256.new(challenge)
        try:
            ds4key_pss.verify(sha, sig)
        except ValueError as e:
            self.fail(f'Response verification failed. ({str(e)})')
Ejemplo n.º 3
0
    def test_imu_set_linear_acceleration(self):
        ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
        ds4key = ds4.DS4Key(ds4key_io)
        features = ds4.FeatureConfiguration()
        tracker = ds4.DS4StateTracker(ds4key, features)

        tracker.imu.set_linear_acceleration(1, 1, 1)

        report = tracker.prepare_for_report_submission()

        actual = bytes(report)[13:25].hex()
        # (0, 0, 0), (8192, 8192, 8192)
        expected = '000000000000002000200020'
        self.assertEqual(actual, expected)
Ejemplo n.º 4
0
    def test_imu_set_angular_velocity(self):
        ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
        ds4key = ds4.DS4Key(ds4key_io)
        features = ds4.FeatureConfiguration()
        tracker = ds4.DS4StateTracker(ds4key, features)

        tracker.imu.set_angular_velocity(90, 90, 90)

        report = tracker.prepare_for_report_submission()

        actual = bytes(report)[13:25].hex()
        # 1000 * 90(deg) / 61 = approx. 1475
        # (1475, 1475, 1475), (0, 0, 0)
        expected = 'c305c305c305000000000000'
        self.assertEqual(actual, expected)
Ejemplo n.º 5
0
    def test_input_touchpad_one(self):
        '''
        One frame 2 point touch.
        '''
        ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
        ds4key = ds4.DS4Key(ds4key_io)
        features = ds4.FeatureConfiguration()
        tracker = ds4.DS4StateTracker(ds4key, features)

        tracker.touch.queue_touchpad((100, 200), (200, 300))

        report = tracker.prepare_for_report_submission()
        actual = bytes(report)[33:61].hex()
        expected = TP_SET_ONE.hex()
        self.assertEqual(actual, expected)
Ejemplo n.º 6
0
    def test_input_touchpad_nohold_after_release(self):
        '''
        Touch shouldn't be held when they are released. Instead they should be invalidated.
        '''
        ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
        ds4key = ds4.DS4Key(ds4key_io)
        features = ds4.FeatureConfiguration()
        tracker = ds4.DS4StateTracker(ds4key, features)

        tracker.touch.queue_touchpad((100, 200), (200, 300))
        tracker.prepare_for_report_submission()
        tracker.touch.queue_touchpad()

        report = tracker.prepare_for_report_submission()
        actual = bytes(report)[33:61].hex()
        expected = TP_SET_ONE_INVALIDATED.hex()
        self.assertEqual(actual, expected)
Ejemplo n.º 7
0
 def test_tracker_edit_context(self):
     '''
     Report editing context and buffer swapping.
     '''
     ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
     ds4key = ds4.DS4Key(ds4key_io)
     features = ds4.FeatureConfiguration()
     tracker = ds4.DS4StateTracker(ds4key, features)
     with tracker.start_modify_report() as report:
         report: ds4.InputReport
         report.set_button(ds4.ButtonType.ps, True)
     with tracker.input_report_lock:
         tracker.swap_buffer_nolock()
         tracker.sync_buffer_nolock()
     actual = tracker.input_report_submitting_buf[5:8].hex()
     actual_next = tracker.input_report_submitting_buf[5:8].hex()
     expected = '080001'
     self.assertEqual(actual, expected)
     self.assertEqual(actual_next, expected)
Ejemplo n.º 8
0
    def test_input_touchpad_release_both(self):
        '''
        Release both points will cause both points to be invalidated. Only
        checks the invalidation states.
        '''
        ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
        ds4key = ds4.DS4Key(ds4key_io)
        features = ds4.FeatureConfiguration()
        tracker = ds4.DS4StateTracker(ds4key, features)

        tracker.touch.queue_touchpad((100, 200), (200, 300))
        tracker.prepare_for_report_submission()
        tracker.touch.queue_touchpad()

        report = tracker.prepare_for_report_submission()
        tp_report = bytes(report)[33:61]

        actual = (bool(tp_report[2] & 0x80), bool(tp_report[6] & 0x80))
        expected = (True, True)
        self.assertEqual(actual, expected)
Ejemplo n.º 9
0
    def test_input_touchpad_nohold(self):
        '''
        Touch shouldn't be held when there are actual new points.
        '''
        ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
        ds4key = ds4.DS4Key(ds4key_io)
        features = ds4.FeatureConfiguration()
        tracker = ds4.DS4StateTracker(ds4key, features)

        tracker.touch.queue_touchpad((230, 125), (500, 100))
        tracker.prepare_for_report_submission()
        tracker.touch.queue_touchpad((100, 200), (200, 300))

        report = tracker.prepare_for_report_submission() # this should do nothing to the touchpad frames now
        actual = bytes(report)[33:61].hex()
        expected_bytes = bytearray(TP_SET_ONE)
        # Frame sequence should be 2 now
        expected_bytes[1] = 2
        expected = expected_bytes.hex()
        # Exactly as before with empty unused frames
        self.assertEqual(actual, expected)
Ejemplo n.º 10
0
    def test_input_touchpad_nohold_after_release_2(self):
        '''
        Invalidated touches will continue to be available despite invalidated.

        This mimics official DS4 behavior.
        '''
        ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
        ds4key = ds4.DS4Key(ds4key_io)
        features = ds4.FeatureConfiguration()
        tracker = ds4.DS4StateTracker(ds4key, features)

        # Initial frame
        tracker.touch.queue_touchpad((100, 200), (200, 300))
        tracker.prepare_for_report_submission()
        # Release - This is one packet despite all points are invalidated.
        tracker.touch.queue_touchpad()
        tracker.prepare_for_report_submission()
        # Sustain the invalidated packet. No value should be incrementing.
        report = tracker.prepare_for_report_submission()
        actual = bytes(report)[33:61].hex()
        expected = TP_SET_ONE_INVALIDATED.hex()
        self.assertEqual(actual, expected)
Ejemplo n.º 11
0
    def test_input_touchpad_hold(self):
        '''
        Not updating the points will cause them to be held at position with
        frame seq incrementing.
        '''
        ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
        ds4key = ds4.DS4Key(ds4key_io)
        features = ds4.FeatureConfiguration()
        tracker = ds4.DS4StateTracker(ds4key, features)

        tracker.touch.queue_touchpad((100, 200), (200, 300))
        # Hold for 2 frames
        tracker.prepare_for_report_submission()
        tracker.prepare_for_report_submission()

        report = tracker.prepare_for_report_submission()
        actual = bytes(report)[33:61].hex()
        expected_bytes = bytearray(TP_SET_ONE)
        # Frame sequence should be 3 now
        expected_bytes[1] = 3
        expected = expected_bytes.hex()
        self.assertEqual(actual, expected)
Ejemplo n.º 12
0
    def test_input_touchpad_touch_seq_inc(self):
        '''
        Touch sequence should be increased when different touches are
        registered.
        '''
        ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
        ds4key = ds4.DS4Key(ds4key_io)
        features = ds4.FeatureConfiguration()
        tracker = ds4.DS4StateTracker(ds4key, features)

        # Initial frame
        tracker.touch.queue_touchpad((100, 200), (200, 300))
        tracker.prepare_for_report_submission()
        # Release - This is one packet despite all points are invalidated.
        tracker.touch.queue_touchpad()
        tracker.prepare_for_report_submission()
        # Second frame with 2 different points
        tracker.touch.queue_touchpad((100, 200), (200, 300))

        report = tracker.prepare_for_report_submission()
        actual = bytes(report)[33:61].hex()
        expected = TP_SET_ONE_AFTER_RELEASE.hex()
        self.assertEqual(actual, expected)
Ejemplo n.º 13
0
    def test_tracker_auth_full(self):
        '''
        Authentication with state tracker (API-level DS4 protocol mockup).
        '''
        ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
        ds4key = ds4.DS4Key(ds4key_io)
        ds4id_expected = ds4key_io.getvalue()[:0x310]
        features = ds4.FeatureConfiguration()
        tracker = ds4.DS4StateTracker(ds4key, features)

        challenge = io.BytesIO(b'\x00' * 256)
        page = 0
        tracker.auth.reset()
        while challenge.tell() < 256:
            data = bytearray(56)
            challenge.readinto(data)
            packet = b'\xf0\x01' + page.to_bytes(1, 'big') + b'\x00' + bytes(data)
            crc = zlib.crc32(packet)
            packet += crc.to_bytes(4, 'little')
            assert len(packet) == 64, 'Test case bug: invalid challenge packet length.'
            packet_io = io.BytesIO(packet)
            tracker.auth.set_challenge(packet_io)
            page += 1
        wait_count = 0
        while True:
            response_io = io.BytesIO()
            tracker.auth.get_status(response_io)
            response = response_io.getvalue()
            self.assertEqual(len(response), 16, 'Wrong GetAuthStatus length.')
            self.assertEqual(response[0], 0xf2, 'Invalid GetAuthStatus magic.')
            self.assertEqual(response[1], 0x1, 'Wrong GetAuthStatus sequence counter.')
            self.assertIn(response[2], (0x01, 0x00), 'Invalid auth status.')
            if response[2] == 0x01:
                wait_count += 1
                time.sleep(0.05)
                if wait_count > 100:
                    self.fail('Auth took too long to respond.')
            elif response[2] == 0x00:
                break
        remaining = 0x410
        page = 0
        response_io_full = io.BytesIO()
        while remaining > 0:
            response_io = io.BytesIO()
            tracker.auth.get_response(response_io)
            response = response_io.getvalue()
            self.assertEqual(len(response), 64, 'Wrong GetResponse length.')
            self.assertEqual(response[0], 0xf1, 'Invalid GetResponse magic.')
            self.assertEqual(response[1], 0x1, 'Wrong GetResponse sequence counter.')
            self.assertEqual(response[2], page, 'Wrong GetResponse page counter.')
            data = response[4:4+min(56, remaining)]
            response_io_full.write(data)  
            remaining = max(0, remaining - 56)
            page += 1
        response_full = response_io_full.getvalue()
        assert len(response_full) == 0x410, 'Test case bug: wrong full response length.'
        sig = response_full[:0x100]
        ds4id = response_full[0x100:]

        self.assertEqual(ds4id.hex(), ds4id_expected.hex(), 'DS4ID mismatch.')
        ds4key_key = RSA.import_key(TEST_DS4KEY_JUST_KEY)
        ds4key_pss = pss.new(ds4key_key)
        sha = SHA256.new(challenge.getvalue())
        try:
            ds4key_pss.verify(sha, sig)
        except ValueError as e:
            self.fail(f'Response verification failed. ({str(e)})')
Ejemplo n.º 14
0
 def test_ds4key_load(self):
     '''
     DS4Key loading.
     '''
     ds4key_io = io.BytesIO(base64.b64decode(TEST_DS4KEY))
     _ds4key = ds4.DS4Key(ds4key_io)