134 lines
5.7 KiB
Python
134 lines
5.7 KiB
Python
# Copyright 2018 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
from hashlib import sha256
|
|
import logging
|
|
from pprint import pformat
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.client.common_lib.cros import pinweaver_client
|
|
from autotest_lib.server import test
|
|
|
|
|
|
def compute_empty_tree_auxilary_hashes(bits_per_level=2, height=6):
|
|
"""Returns a binary string representation of the auxilary digests of an
|
|
empty path in a Merkle tree with the specified parameters.
|
|
"""
|
|
num_siblings = 2 ^ bits_per_level - 1
|
|
child = '\0' * 32
|
|
result = ''
|
|
for _ in range(height):
|
|
part = child * num_siblings
|
|
child = sha256(part + child).digest()
|
|
result += part
|
|
return result
|
|
|
|
class firmware_Cr50PinWeaverServer(test.test):
|
|
"""Tests the PinWeaver functionality on Cr50 using pinweaver_client through
|
|
trunksd.
|
|
"""
|
|
|
|
version = 1
|
|
|
|
RESULT_CODE_SUCCESS = 'EC_SUCCESS'
|
|
RESULT_CODE_AUTH_FAILED = 'PW_ERR_LOWENT_AUTH_FAILED'
|
|
RESULT_CODE_RATE_LIMITED = 'PW_ERR_RATE_LIMIT_REACHED'
|
|
|
|
def run_once(self, host):
|
|
"""Runs the firmware_Cr50PinWeaverServer test.
|
|
This test is made up of the pinweaver_client self test, and a test that
|
|
checks that PinWeaver works as expected across device reboots.
|
|
"""
|
|
|
|
# Run "pinweaver_client selftest".
|
|
try:
|
|
if not pinweaver_client.SelfTest(host):
|
|
raise error.TestFail('Failed SelfTest: %s' %
|
|
self.__class__.__name__)
|
|
except pinweaver_client.PinWeaverNotAvailableError:
|
|
logging.info('PinWeaver not supported!')
|
|
raise error.TestNAError('PinWeaver is not available')
|
|
|
|
# Check PinWeaver logic across reboots including the reboot counter.
|
|
# Insert an entry.
|
|
#
|
|
# Label 0 is guaranteed to be empty because the self test above resets
|
|
# the tree and removes the leaf it adds.
|
|
label = 0
|
|
h_aux = compute_empty_tree_auxilary_hashes().encode('hex')
|
|
le_secret = sha256('1234').hexdigest()
|
|
he_secret = sha256('ag3#l4Z9').hexdigest()
|
|
reset_secret = sha256('W8oE@Ja2mq.R1').hexdigest()
|
|
delay_schedule = '5 %d' % 0x00ffffffff
|
|
result = pinweaver_client.InsertLeaf(host, label, h_aux, le_secret,
|
|
he_secret, reset_secret,
|
|
delay_schedule)
|
|
logging.info('Insert: %s', pformat(result))
|
|
if (result['result_code']['name'] !=
|
|
firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS):
|
|
raise error.TestFail('Failed InsertLeaf: %s' %
|
|
self.__class__.__name__)
|
|
cred_metadata = result['cred_metadata']
|
|
|
|
# Exhaust the allowed number of attempts.
|
|
for i in range(6):
|
|
result = pinweaver_client.TryAuth(host, h_aux, '0' * 64,
|
|
cred_metadata)
|
|
if result['cred_metadata']:
|
|
cred_metadata = result['cred_metadata']
|
|
logging.info('TryAuth: %s', pformat(result))
|
|
if ((i <= 4 and result['result_code']['name'] !=
|
|
firmware_Cr50PinWeaverServer.RESULT_CODE_AUTH_FAILED) or
|
|
(i > 4 and result['result_code']['name'] !=
|
|
firmware_Cr50PinWeaverServer.RESULT_CODE_RATE_LIMITED)):
|
|
raise error.TestFail('Failed TryAuth: %s' %
|
|
self.__class__.__name__)
|
|
|
|
if result['seconds_to_wait'] == 0:
|
|
raise error.TestFail('Failed TryAuth: %s' %
|
|
self.__class__.__name__)
|
|
|
|
# Reboot the device. This calls TPM_startup() which reloads the Merkle
|
|
# tree from NVRAM. Note that this doesn't reset the timer on Cr50, so
|
|
# restart_count doesn't increment.
|
|
host.reboot()
|
|
|
|
# Verify that the lockout is still enforced.
|
|
result = pinweaver_client.TryAuth(host, h_aux, le_secret, cred_metadata)
|
|
logging.info('TryAuth: %s', pformat(result))
|
|
if (result['result_code']['name'] !=
|
|
firmware_Cr50PinWeaverServer.RESULT_CODE_RATE_LIMITED):
|
|
raise error.TestFail('Failed TryAuth: %s' %
|
|
self.__class__.__name__)
|
|
if result['seconds_to_wait'] == 0:
|
|
raise error.TestFail('Failed TryAuth: %s' %
|
|
self.__class__.__name__)
|
|
|
|
# Perform a reset.
|
|
result = pinweaver_client.ResetAuth(host, h_aux, reset_secret,
|
|
cred_metadata)
|
|
if (result['result_code']['name'] !=
|
|
firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS):
|
|
raise error.TestFail('Failed ResetAuth: %s' %
|
|
self.__class__.__name__)
|
|
cred_metadata = result['cred_metadata']
|
|
logging.info('ResetAuth: %s', pformat(result))
|
|
|
|
# Verify that using a PIN would work.
|
|
result = pinweaver_client.TryAuth(host, h_aux, le_secret, cred_metadata)
|
|
mac = result['mac']
|
|
logging.info('TryAuth: %s', pformat(result))
|
|
if (result['result_code']['name'] !=
|
|
firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS):
|
|
raise error.TestFail('Failed TryAuth: %s' %
|
|
self.__class__.__name__)
|
|
|
|
# Remove the leaf.
|
|
result = pinweaver_client.RemoveLeaf(host, label, h_aux, mac)
|
|
logging.info('RemoveLeaf: %s', pformat(result))
|
|
if (result['result_code']['name'] !=
|
|
firmware_Cr50PinWeaverServer.RESULT_CODE_SUCCESS):
|
|
raise error.TestFail('Failed RemoveLeaf: %s' %
|
|
self.__class__.__name__)
|