1# Use of this source code is governed by a BSD-style license that can be
2# found in the LICENSE file.
3
4import hashlib
5
6from autotest_lib.client.common_lib import error
7from autotest_lib.client.common_lib.cros import g2f_utils
8from autotest_lib.client.common_lib.cros import tpm_utils
9from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
10
11U2F_AUTH_ENFORCE=3
12
13class firmware_Cr50U2fPowerwash(FirmwareTest):
14    """
15    A test that runs confidence checks for U2F register and authenticate
16    functions, and checks that key handles are invalidated after TPM clear.
17    """
18    version = 1
19
20    TEST_CHALLENGE_DIGEST = hashlib.sha256(b'test_challenge').hexdigest()
21    TEST_APPLICATION_DIGEST = hashlib.sha256(b'test_application').hexdigest()
22
23    def _safe_power_short_press(self):
24        """Stop powerd before pressing the power button."""
25        # Validating U2F requires pressing the power button. If those power button
26        # presses power off the AP, stop powerd before the test to ignore them.
27        if self.faft_config.ec_forwards_short_pp_press:
28            self.stop_powerd()
29        self.servo.power_short_press()
30
31    def parse_g2ftool_output(self, stdout):
32        """Parses the key-value pairs returned by g2ftool
33
34        @param stdout: g2ftool output.
35        """
36        return dict((k, v)
37                    for k,v in (line.split('=')
38                                for line in stdout.strip().split('\n')))
39
40    def run_once(self, host=None):
41        """Tests that U2F keys are invalidated by powerwash."""
42        self.client = host
43
44        # Start by clearing TPM to make sure the device is in a known state.
45        tpm_utils.ClearTPMOwnerRequest(self.client, wait_for_ready=True)
46
47        # u2fd reads files from the user's home dir, so we need to log in.
48        g2f_utils.ChromeOSLogin(self.client);
49
50        # U2fd does will not start normally if the device has not gone
51        # through OOBE. Force it to startup.
52        cr50_dev = g2f_utils.StartU2fd(self.client)
53
54        # Register requires physical presence.
55        self._safe_power_short_press()
56
57        # Register to create a new key handle.
58        g2f_reg = g2f_utils.G2fRegister(self.client, cr50_dev,
59                                        self.TEST_CHALLENGE_DIGEST,
60                                        self.TEST_APPLICATION_DIGEST,
61                                        U2F_AUTH_ENFORCE)
62
63        # Check that we managed to register.
64        if not g2f_reg.exit_status == 0:
65            raise error.TestError('Register failed.')
66
67        # Extract newly created key handle.
68        key_handle = self.parse_g2ftool_output(g2f_reg.stdout)['key_handle']
69
70        # Auth requires physical presence.
71        self._safe_power_short_press()
72
73        # Check that we can authenticate with the new key handle.
74        g2f_auth = g2f_utils.G2fAuth(self.client, cr50_dev,
75                                     self.TEST_CHALLENGE_DIGEST,
76                                     self.TEST_APPLICATION_DIGEST, key_handle,
77                                     U2F_AUTH_ENFORCE)
78
79        if not g2f_auth.exit_status == 0:
80            raise error.TestError('Authenticate failed.')
81
82        # Clear TPM. We should no longer be able to authenticate with the
83        # key handle after this.
84        tpm_utils.ClearTPMOwnerRequest(self.client, wait_for_ready=True)
85
86        # u2fd reads files from the user's home dir, so we need to log in.
87        g2f_utils.ChromeOSLogin(self.client)
88
89        # U2fd does will not start normally if the device has not gone
90        # through OOBE. Force it to startup.
91        cr50_dev = g2f_utils.StartU2fd(self.client)
92
93        # Check the key handle is no longer valid.
94        self._safe_power_short_press()
95        g2f_auth_clear = g2f_utils.G2fAuth(self.client, cr50_dev,
96                                           self.TEST_CHALLENGE_DIGEST,
97                                           self.TEST_APPLICATION_DIGEST,
98                                           key_handle, U2F_AUTH_ENFORCE)
99
100        if g2f_auth_clear.exit_status == 0:
101            raise error.TestError('Authenticate succeeded; should have failed')
102
103
104    def cleanup(self):
105        """Leave the device in a predictable state"""
106        g2f_utils.ChromeOSLogout(self.client)
107
108        super(firmware_Cr50U2fPowerwash, self).cleanup()
109