Home | History | Annotate | Download | only in firmware_Cr50U2fCommands
      1 # Copyright 2019 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import time
      6 
      7 from autotest_lib.client.common_lib import error
      8 from autotest_lib.client.common_lib.cros import tpm_utils
      9 from autotest_lib.server import test
     10 
     11 # TPM vendor commands have the following header structure:
     12 
     13 # 8001      TPM_ST_NO_SESSIONS
     14 # 00000000  Command/response size
     15 # 20000000  Cr50 Vendor Command (Constant, TPM Command Code)
     16 # 0000      Vendor Command Code (VENDOR_CC_ enum)
     17 
     18 TPM_TAG_SIZE_BYTES = 2
     19 VENDOR_CC_SIZE_BYTES = 2
     20 VENDOR_CMD_HEADER_SIZE_BYTES = 12
     21 
     22 # Responses to TPM vendor commands have the following header structure:
     23 
     24 # 8001      TPM_ST_NO_SESSIONS
     25 # 00000000  Response size
     26 # 00000000  Response code
     27 # 0000      Vendor Command Code
     28 
     29 VENDOR_CMD_RESPONSE_SIZE_OFFSET = TPM_TAG_SIZE_BYTES
     30 VENDOR_CMD_RESPONSE_SIZE_BYTES = 4
     31 VENDOR_CMD_RESPONSE_CODE_OFFSET = (
     32     VENDOR_CMD_RESPONSE_SIZE_OFFSET + VENDOR_CMD_RESPONSE_SIZE_BYTES)
     33 VENDOR_CMD_RESPONSE_CODE_SIZE_BYTES = 4
     34 VENDOR_CMD_RESPONSE_CC_OFFSET = (
     35     VENDOR_CMD_RESPONSE_CODE_OFFSET + VENDOR_CMD_RESPONSE_CODE_SIZE_BYTES)
     36 
     37 # Vendor command codes being tested
     38 
     39 VENDOR_CC_U2F_GENERATE = '002C'
     40 VENDOR_CC_U2F_SIGN = '002D'
     41 VENDOR_CC_U2F_ATTEST = '002E'
     42 
     43 # Expected response sizes (body only)
     44 
     45 VENDOR_CC_U2F_SIGN_RESPONSE_SIZE_BYTES = 64
     46 VENDOR_CC_U2F_GENERATE_RESPONSE_SIZE_BYTES = 129
     47 VENDOR_CC_U2F_ATTEST_RESPONSE_SIZE_BYTES = 64
     48 
     49 # Response Codes
     50 
     51 VENDOR_CMD_RESPONSE_SUCCESS = '00000000'
     52 VENDOR_CMD_RESPONSE_NOT_ALLOWED = '00000507'
     53 VENDOR_CMD_RESPONSE_PASSWORD_REQUIRED = '0000050A'
     54 
     55 # U2F Attest constants
     56 
     57 U2F_ATTEST_FORMAT_REG_RESP = '00'
     58 U2F_ATTEST_REG_RESP_SIZE_BYTES = 194
     59 
     60 # Some 'random' input to simulate actual inputs.
     61 APP_ID = '699abb209a23ec31dcef298064a92ed9829e70a1bc873b272db321fe1644feae'
     62 APP_ID_2 = '3c67e46408ec57dc6e4fb46fd0aecddadcf10c7b856446986ef67544a00530fa'
     63 USER_SECRET_1 = ('1b6e854dcc052dfff2b5ece48c60a9db'
     64                  'c69d27315c5f3ef8031abab60aa24d61')
     65 USER_SECRET_2 = ('26398186431b14de9a6b99f849d71d342'
     66                  'a1ec246d413aed42b7f2ac98846f24d')
     67 HASH_TO_SIGN = ('91f93c8d88ed6168d07a36de53bd62b6'
     68                 '649e84d343dd417ed6062775739b6e65')
     69 RANDOM_32 = '0fd2bf886fa8c036d069adf321bf1390859da4d615034c3a81ca3812a210ce0d'
     70 
     71 
     72 def get_bytes(tpm_str, start, length):
     73   return tpm_str[(start * 2):(start * 2 + length * 2)]
     74 
     75 
     76 def assert_byte_length(str, len_bytes):
     77   """Assert str represents a byte sequence len_bytes long"""
     78   assert (len(str) / 2) == len_bytes
     79 
     80 
     81 def get_str_length_as_hex(str, additional_len=0):
     82   """Get the length of str plus any additional_len as a hex string."""
     83   assert (len(str) % 2) == 0
     84   length_bytes = len(str) / 2
     85   # hex() returns strings with a '0x' prefix, which we remove.
     86   return hex(length_bytes + additional_len)[2:]
     87 
     88 
     89 def check_response_size(response, expected_response, success_size):
     90   """If the response is expected to be success, check it's size is as expected,
     91 
     92      otherwise, check it is 0.
     93   """
     94   response_size = response['length']
     95   if expected_response == VENDOR_CMD_RESPONSE_SUCCESS:
     96     if response_size != success_size:
     97       raise error.TestFail(
     98           'Invalid successful response size: {}'.format(response_size))
     99   elif response_size != 0:
    100     raise error.TestFail(
    101         'Non-zero response size on failure: {}'.format(response_size))
    102 
    103 
    104 class firmware_Cr50U2fCommands(test.test):
    105   """Tests the custom U2F commands in cr50"""
    106 
    107   version = 1
    108 
    109   def __send_vendor_cmd(self,
    110                         vendor_cc,
    111                         cmd_body,
    112                         expected_response_code=VENDOR_CMD_RESPONSE_SUCCESS):
    113     assert_byte_length(vendor_cc, VENDOR_CC_SIZE_BYTES)
    114 
    115     cmd_size_str = get_str_length_as_hex(cmd_body, VENDOR_CMD_HEADER_SIZE_BYTES)
    116 
    117     cmd = (
    118         '8001'  # TPM_ST_NO_SESSIONS
    119         '{:0>8}'  # Command Size (UINT32)
    120         '20000000'  # CR50 Vendor Command (TPM CC)
    121         '{}'  # Vendor Command Code (Subcommand Code, UINT16)
    122         '{}'  # Command Body
    123     ).format(cmd_size_str, vendor_cc, cmd_body)
    124 
    125     result = self.client.run('trunks_send --raw {}'.format(cmd)).stdout.strip()
    126 
    127     if get_bytes(result, 0, TPM_TAG_SIZE_BYTES) != '8001':
    128       raise error.TestFail(
    129           'Unexpected response tag from vendor command: {}'.format(result))
    130 
    131     response_size_bytes = int(
    132         get_bytes(result, VENDOR_CMD_RESPONSE_SIZE_OFFSET,
    133                   VENDOR_CMD_RESPONSE_SIZE_BYTES), 16)
    134 
    135     if response_size_bytes < VENDOR_CMD_HEADER_SIZE_BYTES:
    136       raise error.TestFail(
    137           'Unexpected response length from vendor command: {}'.format(result))
    138 
    139     response_code = get_bytes(result, VENDOR_CMD_RESPONSE_CODE_OFFSET,
    140                               VENDOR_CMD_RESPONSE_CODE_SIZE_BYTES)
    141 
    142     if response_code != expected_response_code:
    143       raise error.TestFail(
    144           'Unexpected response received from vendor command: {}'.format(
    145               response_code))
    146 
    147     response_vendor_cc = get_bytes(result, VENDOR_CMD_RESPONSE_CC_OFFSET,
    148                                    VENDOR_CC_SIZE_BYTES)
    149 
    150     if response_vendor_cc != vendor_cc:
    151       raise error.TestFail(
    152           'Received response for unexpected vendor command code: {}'.format(
    153               response_vendor_cc))
    154 
    155     response_body_size_bytes = (
    156         response_size_bytes - VENDOR_CMD_HEADER_SIZE_BYTES)
    157 
    158     return {
    159         'length':
    160             response_body_size_bytes,
    161         'value':
    162             get_bytes(result, VENDOR_CMD_HEADER_SIZE_BYTES,
    163                       response_body_size_bytes)
    164     }
    165 
    166   def __u2f_sign(self, app_id, user_secret, key_handle, hash, flags,
    167                  expected_response):
    168     assert_byte_length(app_id, 32)
    169     assert_byte_length(user_secret, 32)
    170     assert_byte_length(key_handle, 64)
    171     assert_byte_length(flags, 1)
    172 
    173     response = self.__send_vendor_cmd(
    174         VENDOR_CC_U2F_SIGN, '{}{}{}{}{}'.format(app_id, user_secret, key_handle,
    175                                                 hash, flags), expected_response)
    176 
    177     expected_response_size = VENDOR_CC_U2F_SIGN_RESPONSE_SIZE_BYTES
    178     # 'check-only' requests don't have a response body.
    179     if flags == '07':
    180       expected_response_size = 0
    181 
    182     check_response_size(response, expected_response, expected_response_size)
    183 
    184   def __u2f_generate(self,
    185                      app_id,
    186                      user_secret,
    187                      flags,
    188                      expected_response=VENDOR_CMD_RESPONSE_SUCCESS):
    189     assert_byte_length(app_id, 32)
    190     assert_byte_length(user_secret, 32)
    191     assert_byte_length(flags, 1)
    192 
    193     response = self.__send_vendor_cmd(
    194         VENDOR_CC_U2F_GENERATE, '{}{}{}'.format(app_id, user_secret, flags),
    195         expected_response)
    196 
    197     check_response_size(response, expected_response,
    198                         VENDOR_CC_U2F_GENERATE_RESPONSE_SIZE_BYTES)
    199 
    200     return {
    201         'pubKey': response['value'][0:130],
    202         'keyHandle': response['value'][130:258]
    203     }
    204 
    205   def __u2f_attest(self,
    206                    user_secret,
    207                    format,
    208                    data,
    209                    expected_response=VENDOR_CMD_RESPONSE_SUCCESS,
    210                    pad=False):
    211     assert_byte_length(user_secret, 32)
    212     assert_byte_length(format, 1)
    213 
    214     data_len_str = get_str_length_as_hex(data)
    215 
    216     if pad:
    217       # Max data size is 256 bytes
    218       data = data + '0' * (512 - len(data))
    219 
    220     response = self.__send_vendor_cmd(
    221         VENDOR_CC_U2F_ATTEST, '{}{}{}{}'.format(
    222             user_secret, format, data_len_str, data), expected_response)
    223 
    224     check_response_size(response, expected_response,
    225                         VENDOR_CC_U2F_ATTEST_RESPONSE_SIZE_BYTES)
    226 
    227   def __test_generate_unique(self):
    228     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    229     registration_2 = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    230 
    231     if registration['pubKey'] == registration_2['pubKey']:
    232       raise error.TestFail('Public keys not unique')
    233 
    234     if registration['keyHandle'] == registration_2['keyHandle']:
    235       raise error.TestFail('Key handles not unique')
    236 
    237   def __test_generate_sign_simple(self):
    238     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    239 
    240     self.servo.power_short_press()
    241 
    242     self.__u2f_sign(APP_ID, USER_SECRET_1, registration['keyHandle'],
    243                     HASH_TO_SIGN, '00', VENDOR_CMD_RESPONSE_SUCCESS)
    244 
    245   def __test_generate_with_presence(self):
    246     # Wait 11 seconds to ensure no presence.
    247 
    248     time.sleep(11)
    249 
    250     self.__u2f_generate(
    251         APP_ID,
    252         USER_SECRET_1,
    253         '01',  # U2F_AUTH_FLAG_TUP
    254         VENDOR_CMD_RESPONSE_NOT_ALLOWED)
    255 
    256     self.servo.power_short_press()
    257 
    258     self.__u2f_generate(
    259         APP_ID,
    260         USER_SECRET_1,
    261         '01',  # U2F_AUTH_FLAG_TUP
    262         VENDOR_CMD_RESPONSE_SUCCESS)
    263 
    264   def __test_generate_consume_presence(self):
    265     self.servo.power_short_press()
    266 
    267     self.__u2f_generate(
    268         APP_ID,
    269         USER_SECRET_1,
    270         '03',  # U2F_AUTH_FLAG_TUP | G2F_CONSUME
    271         VENDOR_CMD_RESPONSE_SUCCESS)
    272 
    273     self.__u2f_generate(
    274         APP_ID,
    275         USER_SECRET_1,
    276         '01',  # U2F_AUTH_FLAG_TUP
    277         VENDOR_CMD_RESPONSE_NOT_ALLOWED)
    278 
    279   def __test_sign_requires_presence(self):
    280     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    281 
    282     # U2F asserts presence by checking for a power button press within the
    283     # last 10 seconds, sleep so that we are sure there was not one.
    284 
    285     time.sleep(11)
    286 
    287     self.__u2f_sign(APP_ID, USER_SECRET_1, registration['keyHandle'],
    288                     HASH_TO_SIGN, '00', VENDOR_CMD_RESPONSE_NOT_ALLOWED)
    289 
    290   def __test_sign_multiple_no_consume(self):
    291     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    292 
    293     self.servo.power_short_press()
    294 
    295     self.__u2f_sign(APP_ID, USER_SECRET_1, registration['keyHandle'],
    296                     HASH_TO_SIGN, '00', VENDOR_CMD_RESPONSE_SUCCESS)
    297 
    298     # We should be able to sign again, as this will happen within 10
    299     # seconds of the power button press, and we did not consume.
    300 
    301     self.__u2f_sign(APP_ID, USER_SECRET_1, registration['keyHandle'],
    302                     HASH_TO_SIGN, '00', VENDOR_CMD_RESPONSE_SUCCESS)
    303 
    304   def __test_sign_consume(self):
    305     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    306 
    307     self.servo.power_short_press()
    308 
    309     self.__u2f_sign(
    310         APP_ID,
    311         USER_SECRET_1,
    312         registration['keyHandle'],
    313         HASH_TO_SIGN,
    314         '02',  # G2F_CONSUME
    315         VENDOR_CMD_RESPONSE_SUCCESS)
    316 
    317     # We should have consumed the power button press, so we should not be
    318     # able to sign again.
    319 
    320     self.__u2f_sign(APP_ID, USER_SECRET_1, registration['keyHandle'],
    321                     HASH_TO_SIGN, '00', VENDOR_CMD_RESPONSE_NOT_ALLOWED)
    322 
    323   def __test_sign_wrong_user_secret(self):
    324     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    325 
    326     self.servo.power_short_press()
    327 
    328     # Sanity check.
    329     self.__u2f_sign(APP_ID, USER_SECRET_1, registration['keyHandle'],
    330                     HASH_TO_SIGN, '00', VENDOR_CMD_RESPONSE_SUCCESS)
    331 
    332     self.__u2f_sign(APP_ID, USER_SECRET_2, registration['keyHandle'],
    333                     HASH_TO_SIGN, '00', VENDOR_CMD_RESPONSE_PASSWORD_REQUIRED)
    334 
    335   def __test_sign_wrong_app_id(self):
    336     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    337 
    338     self.servo.power_short_press()
    339 
    340     # Sanity check.
    341     self.__u2f_sign(APP_ID, USER_SECRET_1, registration['keyHandle'],
    342                     HASH_TO_SIGN, '00', VENDOR_CMD_RESPONSE_SUCCESS)
    343 
    344     self.__u2f_sign(APP_ID_2, USER_SECRET_1, registration['keyHandle'],
    345                     HASH_TO_SIGN, '00', VENDOR_CMD_RESPONSE_PASSWORD_REQUIRED)
    346 
    347   def __test_sign_invalid_kh(self):
    348     self.__u2f_sign(
    349         APP_ID,
    350         USER_SECRET_1,
    351         RANDOM_32 + RANDOM_32,  # KH is 64 bytes long
    352         HASH_TO_SIGN,
    353         '00',
    354         VENDOR_CMD_RESPONSE_PASSWORD_REQUIRED)
    355 
    356   def __test_sign_check_only(self):
    357     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    358 
    359     # U2F asserts presence by checking for a power button press within the
    360     # last 10 seconds, sleep so that we are sure there was not one.
    361 
    362     time.sleep(11)
    363 
    364     self.__u2f_sign(APP_ID, USER_SECRET_1, registration['keyHandle'],
    365                     HASH_TO_SIGN, '07', VENDOR_CMD_RESPONSE_SUCCESS)
    366 
    367   def __test_sign_check_only_with_presence(self):
    368     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    369 
    370     self.servo.power_short_press()
    371 
    372     self.__u2f_sign(APP_ID, USER_SECRET_1, registration['keyHandle'],
    373                     HASH_TO_SIGN, '07', VENDOR_CMD_RESPONSE_SUCCESS)
    374 
    375   def __test_sign_check_only_invalid_kh(self):
    376     # U2F asserts presence by checking for a power button press within the
    377     # last 10 seconds, sleep so that we are sure there was not one.
    378 
    379     time.sleep(11)
    380 
    381     self.__u2f_sign(APP_ID,
    382                     USER_SECRET_1,
    383                     RANDOM_32 + RANDOM_32,  # KH is 64 bytes long
    384                     HASH_TO_SIGN,
    385                     '07',
    386                     VENDOR_CMD_RESPONSE_PASSWORD_REQUIRED)
    387 
    388   def __test_sign_check_only_invalid_kh_with_presence(self):
    389     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    390 
    391     self.servo.power_short_press()
    392 
    393     self.__u2f_sign(APP_ID,
    394                     USER_SECRET_1,
    395                     RANDOM_32 + RANDOM_32,  # KH is 64 bytes long
    396                     HASH_TO_SIGN,
    397                     '07',
    398                     VENDOR_CMD_RESPONSE_PASSWORD_REQUIRED)
    399 
    400   def __check_attest_reg_resp(self,
    401                               app_id,
    402                               key_handle,
    403                               user_secret,
    404                               expected_response,
    405                               pad=False):
    406     register_resp = '00{}{}{}{}'.format(
    407         app_id,
    408         RANDOM_32,  # challenge
    409         key_handle,
    410         'ff' * 65)  # public key (not verified)
    411 
    412     self.__u2f_attest(user_secret, U2F_ATTEST_FORMAT_REG_RESP, register_resp,
    413                       expected_response, pad)
    414 
    415   def __test_attest_simple(self):
    416     # Attest does not require user presence
    417     time.sleep(11)
    418 
    419     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    420 
    421     self.__check_attest_reg_resp(APP_ID, registration['keyHandle'],
    422                                  USER_SECRET_1, VENDOR_CMD_RESPONSE_SUCCESS)
    423 
    424   def __test_attest_simple_padded(self):
    425     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    426 
    427     self.__check_attest_reg_resp(
    428         APP_ID,
    429         registration['keyHandle'],
    430         USER_SECRET_1,
    431         VENDOR_CMD_RESPONSE_SUCCESS,
    432         pad=True)
    433 
    434   def __test_attest_wrong_user(self):
    435     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    436 
    437     self.__check_attest_reg_resp(APP_ID, registration['keyHandle'],
    438                                  USER_SECRET_2, VENDOR_CMD_RESPONSE_NOT_ALLOWED)
    439 
    440   def __test_attest_wrong_app_id(self):
    441     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    442 
    443     self.__check_attest_reg_resp(APP_ID_2, registration['keyHandle'],
    444                                  USER_SECRET_1, VENDOR_CMD_RESPONSE_NOT_ALLOWED)
    445 
    446   def __test_attest_garbage_data(self):
    447     self.__u2f_attest(USER_SECRET_1, U2F_ATTEST_FORMAT_REG_RESP,
    448                       'ff' * U2F_ATTEST_REG_RESP_SIZE_BYTES,
    449                       VENDOR_CMD_RESPONSE_NOT_ALLOWED)
    450 
    451   def __test_attest_invalid_format(self):
    452     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    453 
    454     register_resp = '00{}{}{}{}'.format(
    455         APP_ID,
    456         RANDOM_32,  # challenge
    457         registration['keyHandle'],
    458         'ff' * 65)  # public key (not verified)
    459 
    460     # Attempt to attest to valid data with invalid format.
    461     self.__u2f_attest(USER_SECRET_1, 'ff', register_resp,
    462                       VENDOR_CMD_RESPONSE_NOT_ALLOWED)
    463 
    464   def __test_kh_invalidated_by_powerwash(self):
    465     registration = self.__u2f_generate(APP_ID, USER_SECRET_1, '00')
    466 
    467     self.servo.power_short_press()
    468 
    469     # Sanity check
    470     self.__u2f_sign(APP_ID, USER_SECRET_1, registration['keyHandle'],
    471                     HASH_TO_SIGN, '00', VENDOR_CMD_RESPONSE_SUCCESS)
    472 
    473     # Clear TPM. We should no longer be able to authenticate with the
    474     # key handle after this.
    475     tpm_utils.ClearTPMOwnerRequest(self.client, wait_for_ready=True)
    476 
    477     self.servo.power_short_press()
    478 
    479     self.__u2f_sign(APP_ID, USER_SECRET_1, registration['keyHandle'],
    480                     HASH_TO_SIGN, '00', VENDOR_CMD_RESPONSE_PASSWORD_REQUIRED)
    481 
    482   def run_once(self, host=None):
    483     """Run the tests."""
    484 
    485     self.client = host
    486     self.servo = host.servo
    487     self.servo.initialize_dut()
    488 
    489     # Basic functionality
    490     self.__test_generate_unique()
    491     self.__test_generate_sign_simple()
    492 
    493     # Generate - presence
    494     self.__test_generate_with_presence()
    495     self.__test_generate_consume_presence()
    496 
    497     # Sign - presence
    498     self.__test_sign_requires_presence()
    499     self.__test_sign_multiple_no_consume()
    500     self.__test_sign_consume()
    501 
    502     # Sign - key handle
    503     self.__test_sign_wrong_user_secret()
    504     self.__test_sign_wrong_app_id()
    505     self.__test_sign_invalid_kh()
    506 
    507     # Sign - check only
    508     self.__test_sign_check_only()
    509     self.__test_sign_check_only_with_presence()
    510     self.__test_sign_check_only_invalid_kh()
    511     self.__test_sign_check_only_invalid_kh_with_presence()
    512 
    513     # Attest
    514     self.__test_attest_simple()
    515     self.__test_attest_simple_padded()
    516     self.__test_attest_wrong_user()
    517     self.__test_attest_wrong_app_id()
    518     self.__test_attest_garbage_data()
    519     self.__test_attest_invalid_format()
    520 
    521     # Powerwash
    522     self.__test_kh_invalidated_by_powerwash()
    523