blob: 5430d0f3816bd4f2b0ea2f57dc0e8d9d19af837c [file] [log] [blame]
# Copyright 2023 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import print_function
import logging
import re
from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib.cros import tpm_utils
from autotest_lib.server.cros.faft.cr50_test import Cr50Test
class firmware_GSCPinweaverUpdate(Cr50Test):
"""Verify pinweaver data survives GSC Update."""
OLD_VERSIONS = {
# Cr50 image with the old version of Pinweaver
'cr50': '0.5.160',
# Ti50 always used platform/pinweaver. This is just a ti50 image with
# the same rollback era.
'ti50': '0.23.21'
}
USER = 'name1'
PWD = 'passwd1'
PIN = '12345'
WRONG_PIN = '012345'
PWD_LABEL = 'pwd1'
PIN_LABEL = 'pin1'
AUTH_SESSION_ID_RE = r'auth_session_id: ([0-9A-F]*)\s'
CMD_START_AUTH = 'cryptohome --action=start_auth_session --user=%s'
CMD_CREATE_USER = ('cryptohome --action=create_persistent_user '
'--auth_session_id=%s')
CMD_AUTH = 'cryptohome --action=%s --auth_session_id=%s --key_label=%s %s%s'
PWD_ARG = ' --password='
PIN_ARG = ' --pin='
SUCCESS = 0
# Wrong pin/password.
CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED = 3
# Too many pin attempts. The pin is locked until the password is used.
CRYPTOHOME_ERROR_TPM_DEFEND_LOCK = 8
# One more pin attempt will lockout the credential
CRYPTOHOME_ERROR_CREDENTIAL_LOCKED = 59
def initialize(self, host, cmdline_args, full_args):
"""Setup the test to restore the release image."""
self.ran_test = False
if not host.servo:
raise error.TestNAError('No valid servo found')
if host.servo.main_device_is_ccd():
raise error.TestNAError('Use a flex cable instead of CCD cable.')
# Restore the original image and board id during cleanup.
super().initialize(host,
cmdline_args,
full_args,
restore_cr50_image=True,
restore_cr50_board_id=True)
if self.gsc.NAME not in self.OLD_VERSIONS:
raise error.TestNAError(
'%r is unsupported. Add image version to OLD_VERSIONS' %
self.gsc.NAME)
self.remove_gsc_firmware_images()
old_path = full_args.get('old_pinweaver_gsc_path', None)
if old_path:
self._old_release_path = old_path
else:
self._old_release_path = self.download_cr50_release_image(
self.OLD_VERSIONS[self.gsc.NAME])[0]
def create_user(self, session):
"""Create a persistent user in the given session."""
return self.host.run(self.CMD_CREATE_USER % session)
def start_auth(self, user):
"""Create a persistent user in the given session."""
logging.info('Start auth: %s', user)
result = self.host.run(self.CMD_START_AUTH % user)
match = re.search(self.AUTH_SESSION_ID_RE, result.stdout)
if not match:
raise error.TestFail('Did not find auth session id in %r' %
result.stdout)
logging.info('Session Id: %s', match.group(1))
return match.group(1)
def run_auth_cmd(self, action, session, label, auth_type, secret,
ignore_status):
"""Run the auth command.
@param action: auth action string: add_auth_factor or authenticate_auth_factor'
@param session: the session id string from the start auth session output.
@param label: key label string.
@param auth_type: " --password" or " --pin="
@param secret: the pin or password value
@param ignore_status: True if the command should fail.
"""
cmd = self.CMD_AUTH % (action, session, label, auth_type, secret)
logging.info('Run %s', cmd)
result = self.host.run(cmd, ignore_status=ignore_status)
logging.info('Result %s', result)
return result
def create_pin(self, session, pin):
"""Create a pin"""
logging.info('Create pin: %s', pin)
return self.run_auth_cmd('add_auth_factor', session, self.PIN_LABEL,
self.PIN_ARG, pin, False)
def create_password(self, session, pwd):
"""Create a password"""
logging.info('Create password: %s', pwd)
return self.run_auth_cmd('add_auth_factor', session, self.PWD_LABEL,
self.PWD_ARG, pwd, False)
def authenticate_pin(self, session, pin, expected_status):
"""Create a password"""
logging.info('Auth pin: %s', pin)
result = self.run_auth_cmd('authenticate_auth_factor', session,
self.PIN_LABEL, self.PIN_ARG, pin,
bool(expected_status))
if expected_status != result.exit_status:
raise error.TestFail(
'Unexpected Auth Result: expected %d. Got %r' %
(expected_status, result))
def authenticate_password(self, session, pwd):
"""Create a password"""
logging.info('Auth password: %s', pwd)
return self.run_auth_cmd('authenticate_auth_factor', session,
self.PWD_LABEL, self.PWD_ARG, pwd, False)
def cleanup(self):
"""Clear the factory config."""
try:
if self.ran_test:
self.cr50_update(self.get_saved_cr50_original_path())
self._restore_device_images_and_running_cr50_firmware()
finally:
super().cleanup()
def run_once(self):
"""Verify the Cr50 BID response of each test bid."""
self.ran_test = True
# Rollback to the old gsc image
logging.info('Update to old gsc release')
self.eraseflashinfo_and_restore_image(self._old_release_path)
# Do a powerwash to clear the state.
logging.info('Powerwash')
self.host.run(
"echo 'clobber' > /mnt/stateful_partition/.update_available")
tpm_utils.ClearTPMOwnerRequest(self.host, wait_for_ready=True)
# Initialize Pinweaver
session_id = self.start_auth(self.USER)
self.create_user(session_id)
self.create_password(session_id, self.PWD)
self.create_pin(session_id, self.PIN)
logging.info('Verify the wrong pin does not work')
self.authenticate_pin(session_id, self.WRONG_PIN,
self.CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED)
logging.info('Verify the pin works')
self.authenticate_pin(session_id, self.PIN, self.SUCCESS)
# Use the wrong pin twice.
logging.info('Sending 2 invalid pin attempts')
self.authenticate_pin(session_id, self.WRONG_PIN,
self.CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED)
self.authenticate_pin(session_id, self.WRONG_PIN,
self.CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED)
# Update to the release image from the AP to mimic the normal update
# path.
logging.info('Update to the current release')
self.cr50_update(self.get_saved_cr50_original_path())
session_id = self.start_auth(self.USER)
# Verify pinweaver lockout still works after the update.
# Use the wrong pin three more times to lockout the node. It gets
# locked after 5 attempts.
logging.info('Verify pin lockout')
self.authenticate_pin(session_id, self.WRONG_PIN,
self.CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED)
self.authenticate_pin(session_id, self.WRONG_PIN,
self.CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED)
logging.info('Last wrong pin attempt before lockout')
self.authenticate_pin(session_id, self.WRONG_PIN,
self.CRYPTOHOME_ERROR_CREDENTIAL_LOCKED)
logging.info('Verify the incorrect pin is locked out.')
self.authenticate_pin(session_id, self.WRONG_PIN,
self.CRYPTOHOME_ERROR_TPM_DEFEND_LOCK)
logging.info('Verify the correct pin is locked out.')
self.authenticate_pin(session_id, self.PIN,
self.CRYPTOHOME_ERROR_TPM_DEFEND_LOCK)
# Use the password to clear the pin lockout
self.authenticate_password(session_id, self.PWD)
self.authenticate_pin(session_id, self.WRONG_PIN,
self.CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED)
logging.info('Verify pin works again after the password has been used')
self.authenticate_pin(session_id, self.PIN, self.SUCCESS)