Source code for testcases.SecureBoot

#!/usr/bin/env python3
# OpenPOWER Automated Test Project
#
# Contributors Listed Below - COPYRIGHT 2017
# [+] International Business Machines Corp.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.
#

'''
Secureboot IPL Tests
--------------------
'''

import unittest
import pexpect

import OpTestConfiguration
from common.OpTestSystem import OpSystemState
from common.OpTestConstants import OpTestConstants as BMC_CONST
from common.Exceptions import CommandFailed
from testcases.OpTestFlash import PNORFLASH
from testcases.OpTestFlash import OpalLidsFLASH

import logging
import OpTestLogger
log = OpTestLogger.optest_logger_glob.get_logger(__name__)


[docs]class SecureBoot(unittest.TestCase):
[docs] def setUp(self): conf = OpTestConfiguration.conf self.cv_SYSTEM = conf.system() self.cv_BMC = conf.bmc() self.cv_REST = self.cv_BMC.get_rest_api() self.cv_HOST = conf.host() self.cv_IPMI = conf.ipmi() self.bmc_type = conf.args.bmc_type self.pupdate_binary = conf.args.pupdate self.pflash = conf.args.pflash self.pupdate = conf.args.pupdate self.securemode = conf.args.secure_mode
def verify_boot_pass(self): self.cv_SYSTEM.goto_state(OpSystemState.PETITBOOT_SHELL) def verify_boot_fail(self): raw_pty = self.cv_SYSTEM.console.get_console() self.cv_SYSTEM.sys_power_on() count = 4 boot = False while count != 0: try: raw_pty.expect("ISTEP", timeout=30) boot = True except pexpect.TIMEOUT: count -= 1 if boot: return False return True def wait_for_secureboot_enforce(self): raw_pty = self.cv_SYSTEM.console.get_console() self.cv_SYSTEM.sys_power_on() raw_pty.expect("STB: secure mode enforced, aborting.", timeout=300) raw_pty.expect("secondary_wait", timeout=20) raw_pty.expect("host_voltage_config", timeout=100) def wait_for_sb_kt_start(self): raw_pty = self.cv_SYSTEM.console.get_console() raw_pty.expect( "sbe|Performing Secure Boot key transition", timeout=300) def wait_for_shutdown(self): raw_pty = self.cv_SYSTEM.console.get_console() raw_pty.expect("shutdown complete", timeout=100) def verify_opal_sb(self): c = self.cv_SYSTEM.console self.cpu = ''.join(c.run_command( "grep '^cpu' /proc/cpuinfo |uniq|sed -e 's/^.*: //;s/[,]* .*//;'")) log.debug(self.cpu) if self.cpu in ["POWER9", "POWER9P"]: part_list = ["CAPP", "IMA_CATALOG", "BOOTKERNEL", "VERSION"] elif self.cpu in ["POWER8"]: part_list = ["CAPP", "BOOTKERNEL"] else: self.skipTest("OPAL SB test not supported on %s" % self.cpu) data = " ".join(c.run_command( "cat /sys/firmware/opal/msglog | grep -i stb")) if self.securemode: if not "secure mode on" in data: self.assertTrue( False, "OPAL-SB: Secure mode is detected as OFF") for part in part_list: msg = "STB: %s verified" % part if not msg in data: self.assertTrue( False, "OPAL-SB: %s verification failed or not happened" % part) def verify_dt_sb(self): c = self.cv_SYSTEM.console # Check for STB support - /ibm,secureboot DT node should exist try: c.run_command("ls --color=never /proc/device-tree/ibm,secureboot") except CommandFailed: if self.securemode: self.assertTrue( False, "OPAL-SB: ibm,secureboot DT node not created") else: self.skipTest("Secureboot not supported on this system") c.run_command("lsprop /proc/device-tree/ibm,secureboot") if self.securemode: c.run_command( "ls --color=never /proc/device-tree/ibm,secureboot/secure-enabled") c.run_command( "ls --color=never /proc/device-tree/ibm,secureboot/compatible") c.run_command( "ls --color=never /proc/device-tree/ibm,secureboot/hw-key-hash") c.run_command("ls --color=never /proc/device-tree/ibm,secureboot/name") value = c.run_command( "cat /proc/device-tree/ibm,secureboot/compatible")[-1] if "ibm,secureboot-v2" in value: c.run_command( "ls --color=never /proc/device-tree/ibm,secureboot/hw-key-hash-size") c.run_command( "ls --color=never /proc/device-tree/ibm,secureboot/ibm,cvc") c.run_command( "ls --color=never /proc/device-tree/ibm,secureboot/ibm,cvc/compatible") c.run_command( "ls --color=never /proc/device-tree/ibm,secureboot/ibm,cvc/memory-region") elif "ibm,secureboot-v1" in value: c.run_command( "ls --color=never /proc/device-tree/ibm,secureboot/hash-algo")
[docs]class VerifyOPALSecureboot(SecureBoot):
[docs] def setUp(self): conf = OpTestConfiguration.conf super(VerifyOPALSecureboot, self).setUp()
def runTest(self): self.cv_SYSTEM.goto_state(OpSystemState.PETITBOOT_SHELL) self.verify_dt_sb() self.verify_opal_sb()
[docs]class UnSignedPNOR(SecureBoot, PNORFLASH): ''' Secure boot IPL test: Ensure prevention of boot with improperly signed PNOR. ======= ========= =========== SB Mode sign mode Boot result ======= ========= =========== ON Unsigned Fail OFF Unsigned Pass ======= ========= =========== '''
[docs] def setUp(self): conf = OpTestConfiguration.conf self.pnor = conf.args.un_signed_pnor super(UnSignedPNOR, self).setUp()
def runTest(self): if not self.pnor: self.skipTest("Un-signed/improper signed PNOR image not provided") PNORFLASH.pnor = self.pnor super(UnSignedPNOR, self).runTest() if not self.securemode: self.verify_boot_pass() self.verify_dt_sb() self.verify_opal_sb() else: self.assertTrue(self.verify_boot_fail(), "Unexpected system boot") self.cv_SYSTEM.set_state(OpSystemState.UNKNOWN_BAD) self.cv_SYSTEM.goto_state(OpSystemState.OFF)
[docs]class SignedPNOR(SecureBoot, PNORFLASH): ''' Secure boot IPL test: Ensure successful boot with properly signed PNOR. ======= ========= =========== SB Mode sign mode Boot result ======= ========= =========== ON Signed Pass OFF Signed Pass ======= ========= =========== '''
[docs] def setUp(self): conf = OpTestConfiguration.conf self.pnor = conf.args.signed_pnor super(SignedPNOR, self).setUp()
def runTest(self): if not self.pnor: self.skipTest("signed PNOR image not provided") PNORFLASH.pnor = self.pnor super(SignedPNOR, self).runTest() self.verify_boot_pass() self.verify_dt_sb() self.verify_opal_sb()
[docs]class SignedToPNOR(SecureBoot, PNORFLASH): ''' Secure boot IPL test: Ensure successful boot with properly signed to PNOR(To match the keys which are got replaced after KeyTransitionPNOR flash test). ======= ========= =========== SB Mode sign mode Boot result ======= ========= =========== ON Signed Pass OFF Signed Pass ======= ========= =========== '''
[docs] def setUp(self): conf = OpTestConfiguration.conf self.pnor = conf.args.signed_to_pnor super(SignedToPNOR, self).setUp()
def runTest(self): if not self.pnor: self.skipTest("signed PNOR image not provided") PNORFLASH.pnor = self.pnor super(SignedToPNOR, self).runTest() self.verify_boot_pass() self.verify_dt_sb() self.verify_opal_sb()
[docs]class KeyTransitionPNOR(SecureBoot, PNORFLASH): ''' Secure boot key transition test: Ensure successful key transition in SEEPROM Types of Key Transition PNOR images: - dev to dev - dev to prod - prod to dev - prod to prod '''
[docs] def setUp(self): conf = OpTestConfiguration.conf self.kt_pnor = conf.args.key_transition_pnor super(KeyTransitionPNOR, self).setUp()
def runTest(self): if not self.kt_pnor: self.skipTest("No key transition PNOR image is provided") console = self.cv_SYSTEM.console PNORFLASH.pnor = self.kt_pnor super(KeyTransitionPNOR, self).runTest() self.cv_SYSTEM.sys_power_on() self.cv_SYSTEM.set_state(OpSystemState.IPLing) self.wait_for_sb_kt_start() # system should power off automatically after key transition finishes self.cv_SYSTEM.set_state(OpSystemState.POWERING_OFF) self.wait_for_shutdown() log.debug("set state, going to off") self.cv_SYSTEM.goto_state(OpSystemState.OFF)
[docs]class OPALContainerTest(SecureBoot, OpalLidsFLASH): ''' System having Signed PNOR(either production or developement mode) Flash signed containers with wrong keys Secureboot verify should fail and boot should abort '''
[docs] def setUp(self): conf = OpTestConfiguration.conf self.skiboot = None self.skiroot_kernel = None self.skiroot_initramfs = None self.test_containers = conf.args.test_container super(OPALContainerTest, self).setUp()
def runTest(self): test_list = [] if not self.test_containers: self.skipTest("No test containers provided") known_part_list = ["BOOTKERNEL", "CAPP", "IMA_CATALOG", "VERSION"] # sort list with known order list for item in known_part_list: for pair in self.test_containers: if item in pair[0]: test_list.append(pair) for container in test_list: OpalLidsFLASH.flash_part_list = [container] self.cv_SYSTEM.goto_state(OpSystemState.OFF) super(OPALContainerTest, self).runTest() if self.securemode: self.wait_for_secureboot_enforce() self.cv_SYSTEM.set_state(OpSystemState.UNKNOWN_BAD) self.cv_SYSTEM.goto_state(OpSystemState.OFF) else: self.cv_SYSTEM.goto_state(OpSystemState.PETITBOOT)
def secureboot_suite(): s = unittest.TestSuite() s.addTest(UnSignedPNOR()) s.addTest(SignedPNOR()) s.addTest(OPALContainerTest()) s.addTest(SignedPNOR()) s.addTest(KeyTransitionPNOR()) s.addTest(SignedToPNOR()) return s