| #!/usr/bin/env python3 |
| # 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. |
| |
| """Verify pairing between base and lid |
| |
| During pairing, the base computes it's public key from base_private key |
| and authenticator is derived from shared secret. For the lid to verify the |
| challenge, it needs to check whether base_public corresponds to one of |
| the previously seen bases, and that the returned authenticator matches. |
| """ |
| |
| from __future__ import print_function |
| |
| |
| import ctypes |
| import sys |
| import time |
| |
| import common |
| import hammerd_api |
| |
| PAIRING_RUNS = 10 |
| INJECTION_RUNS = 10 |
| PUBLIC_KEY_SIZE = 32 |
| |
| # Before this test, please flash staff.bin using servo |
| |
| def main(argv): |
| if argv: |
| sys.exit('Test takes no args!') |
| updater = hammerd_api.FirmwareUpdater(common.BASE_VENDOR_ID, |
| common.BASE_PRODUCT_ID, |
| common.BASE_USB_PATH) |
| public_key_first = (ctypes.c_ubyte * PUBLIC_KEY_SIZE)() |
| # Load EC image. |
| with open(common.IMAGE, 'rb') as f: |
| ec_image = f.read() |
| updater.LoadEcImage(ec_image) |
| |
| common.disable_hammerd() |
| |
| print('Connect to base EC.') |
| common.connect_usb(updater) |
| print('EC information:') |
| print('PDU Response: %s' % updater.GetFirstResponsePdu().contents) |
| print('RO: %s' % updater.GetSectionVersion(hammerd_api.SectionName.RO)) |
| print('RW: %s' % updater.GetSectionVersion(hammerd_api.SectionName.RW)) |
| print('Current section : %s' % updater.CurrentSection()) |
| assert updater.CurrentSection() == 1, 'Running section should be 1 (RW)' |
| |
| # Sends 'Need to inject entropy' message if base is new |
| pair_manager = hammerd_api.PairManager() |
| challenge_status = pair_manager.PairChallenge(updater.object, |
| public_key_first) |
| print('Challenge status: %d' % challenge_status) |
| # assert challenge_status == 9, 'Need to inject the entropy' |
| |
| for iteratn in range(INJECTION_RUNS): |
| print('Jumping back to RO to inject entropy. Iteratn: %d' % (iteratn + 1)) |
| updater.SendSubcommand(hammerd_api.UpdateExtraCommand.UnlockRollback) |
| updater.SendSubcommand(hammerd_api.UpdateExtraCommand.ImmediateReset) |
| updater.CloseUsb() |
| time.sleep(0.5) |
| updater.TryConnectUsb() |
| updater.SendSubcommand(hammerd_api.UpdateExtraCommand.StayInRO) |
| # Wait for RO to run, else SendFirstPdu() picks up RW |
| time.sleep(1) |
| # Verify that we're in RO |
| assert updater.SendFirstPdu() is True, 'Error sending first PDU' |
| updater.SendDone() |
| assert updater.CurrentSection() == 0, 'Not in RO: Cannot inject entropy' |
| |
| print('Inject entropy and sys jump to RW') |
| updater.InjectEntropyWithPayload(b'\x87' * hammerd_api.ENTROPY_SIZE) |
| updater.SendSubcommand(hammerd_api.UpdateExtraCommand.ImmediateReset) |
| updater.CloseUsb() |
| time.sleep(0.5) |
| common.connect_usb(updater) |
| updater.SendSubcommand(hammerd_api.UpdateExtraCommand.JumpToRW) |
| # Wait for RW to run |
| time.sleep(1) |
| updater.CloseUsb() |
| time.sleep(0.5) |
| # Jump to RW resets the base. Need to reconnect |
| common.connect_usb(updater) |
| print('PDU Response: %s' % updater.GetFirstResponsePdu().contents) |
| |
| # Check that RW is running |
| assert updater.SendFirstPdu() is True, 'Error sending first PDU' |
| updater.SendDone() |
| print('Current running section after jumping to RW: %s' |
| % updater.CurrentSection()) |
| assert updater.CurrentSection() == 1, 'Running section should be 1 (RW)' |
| # Autheticator should match for each pairing run |
| for i in range(PAIRING_RUNS): |
| public_key = (ctypes.c_ubyte * PUBLIC_KEY_SIZE)() |
| pair_manager = hammerd_api.PairManager() |
| challenge_status = pair_manager.PairChallenge(updater.object, public_key) |
| print('Challenge status: %d' % challenge_status) |
| assert challenge_status == 0, 'Pairing challenge failed!' |
| if i == 0: |
| same = True |
| for j in range(0, PUBLIC_KEY_SIZE-1): |
| if public_key_first[j] != public_key[j]: |
| same = False |
| assert not same, 'The key did not change after entropy injection!' |
| public_key_first = public_key |
| else: |
| for j in range(0, PUBLIC_KEY_SIZE-1): |
| assert public_key_first[j] == public_key[j], 'Key changed!' |
| |
| # Reset the base |
| common.sim_disconnect_connect(updater) |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |