Skip to content
hellerda edited this page Jun 28, 2018 · 15 revisions

POWER Firmware Signing Tool

Users and Signers Guide

Version 0.5.2

Author: Dave Heller (hellerda at us.ibm.com)

Table of Contents

Contributors

Document contributors:

  • Tim Block
  • Nick Bofferding
  • Pam Eggler
  • Chris Engel
  • Stewart Smith
  • Sub Swaminathan
Secure container design by:
  • Elaine Palmer

Goals

  • Provide a simple tool to allow secure firmware module signing within the op-build environment, and outside it.
    • Sign by default (with development keys)
  • Infrastructure for simple and flexible separation of build and signing environments
    • Provide integration with the sb-signing-framework, to enable the signing tool to automatically submit request to, and handle response from, the signing server.
    • Support key and signature caching as to minimize the number of requests to the signing server, and the amount of human intervention required for the signing process.
  • Support multi-pass signing, to allow artifacts (keys and signatures) to be introduced as they become available, enabling the most flexible process for the firmware signer.
  • Provide an easy way to import artifacts to the signing process.
  • Support in-situ validation of signed containers, performing all the checks that are done during secure boot.

Secure Boot and Secure Signing overview

Protection of system firmware against malicious attack is paramount to server security. If an attacker is able to inject malicious code at the firmware level, no security measure at the operating system level can fully guarantee the trust of the system. IBM® POWER servers support secure boot of system firmware to ensure the system boots only authorized firmware. When the system boots, each firmware component is verified against a cryptographic signature and integrity-checked against a secure hash of the component. If any check fails, secure boot prevents the system from booting until the problem is corrected.

Secure boot protection of platform firmware

When a POWER server boots, the boot process loads a series of executable firmware components from flash memory. When secure boot is enabled, each component in the boot sequence will verify the integrity and the authenticity of the next before allowing that component to run. The integrity is verified by means a secure cryptographic hash, such as SHA512, and the authenticity is verified by a cryptographic signature, such as ECDSA (elliptic curve digital signature algorithm).

Secure boot works by establishing a chain of trust for all executable components. This chain of trust is anchored in hardware, meaning that secure boot trusts the very first bit of code to execute because it is stored immutably in the machine hardware. Each component that executes subsequently, however, is loaded from an unprotected location in flash memory and so is untrusted until verified. Secure boot stores each of these components in a secure container that includes a cryptographic signature. For every component loaded, secure boot will authenticate the component against this signature, and stop the boot if the check fails.

Secure boot security domains

The POWER secure boot function comprises two security domains, the firmware domain and the operating system (OS) domain. The firmware domain governs the boot of platform firmware up to and including Petitboot (the firmware-level bootloader). The operating system domain governs the boot of the target OS or hypervisor. This is shown in figure 1.

Figure 1: OpenPOWER boot flow

This document focuses on the signing operations for the firmware domain.

Secure boot signing keys

The keys associated with the firmware domain are referred to as the hardware (HW) keys and firmware (FW) keys. As shown in Table 1, the keys forms a two-level hierarchy, with a set of designated root keys (the HW keys), and a set of keys under the authority of the root (the FW keys). In short, the HW keys sign the FW keys, and FW keys sign the firmware components. There are three keys in each set, which allows for a potential separation of duties within the signing process.

Keys Designator Function Root key?
HW keys A, B, C The authority for the server firmware domain; used to sign FW keys Yes
FW keys P, Q, R Used to sign the firmware components. The FW keys are authorized by the HW keys No

Table 1: Firmware key hierarchy

The keys are asymmetric keys, meaning that each has a public portion and a private portion. The private portion, or private key, must be protected (kept secret) by the key owner, stored ideally in a hardware security module (HSM). The public portion, or public key, may be distributed openly. The private keys are typically controlled by the platform vendor: IBM or the ODM (original design manufacturer). Or, the private keys may be controlled by the end-customer (platform owner).

The private keys are never exposed to the secure boot process and are not added to the firmware container. The public keys are added to the firmware container so that they are available for the signature check during boot.

To anchor the key hierarchy in hardware, a SHA512 hash of the public portion of the three HW Root keys is installed in protected flash memory in the POWER processor SEEPROM. The process for installing this hash is referred as the imprinting process, and this is done (normally once) by the platform vendor or platform owner. When secure boot loads containers from PNOR, it checks the hash of the HW keys in each secure container against the value stored in SEEPROM. This is how secure boot verifies that the keys used to sign the firmware are properly authorized.

The current OpenPOWER secure boot implementation specifies the use of 512-bit ECDSA for all keys.

POWER secure boot flow

Secure boot establishes a chain of trust for all executable components by requiring each component to verify the next. We say the chain of trust is anchored in hardware, because the first bits of code to execute are stored immutably, either in ROM, or in protected area of NVRAM that cannot be updated without the authority of the HW Root keys or by asserting physical presence.

The remaining firmware is stored in PNOR, which is an unprotected region of flash memory, and secure boot considers the PNOR to be untrusted. To protect the firmware in PNOR, each component us stored in a secure container that includes a cryptographic signature. As each module is loaded, the secure boot code in the previous module will use this signature to verify the integrity and authenticity of the module to be executed. If any check fails, secure boot halts the system.

Secure firmware containers

Each component of POWER firmware is packaged in a secure container that holds the hash and signatures required to support secure boot verification. The container is in fact a metadata header prefixed to the firmware image.

Figure 2 shows a simplified view of the container layout. The header is created during the firmware build process, or it may be created at some later packaging step. To build the header, the process performs the following steps:

  1. Adds a (SHA512) hash of the firmware image (the container payload) to a region of the header known as the firmware header.
  2. Requests a signature over the firmware header to be created by the holder(s) of the three FW keys, then adds these signatures to an adjacent region immediately following the firmware header.
  3. Calculates a hash of the three FW keys and adds this to a region of the header known as the prefix header.
  4. Requests a signature over the prefix header to be created by the holder(s) of the three HW keys and adds these signatures to a region immediately following the prefix header. (In a manner of speaking, the FW keys are the "payload" signed by the HW keys.)
  5. Adds a copy of the public HW keys to a region called the hardware header, and a copy of the FW public keys to the "payload" region of the prefix header.

Figure 2: How a secure container is constructed

Note that the diagram shows the signing operations as being performed by HW and FW keys in the container header. In fact, the signing operations use the private HW and FW keys, which are kept secure. The keys added to the container header are the public keys, used for verification. The signing flow in the diagram is only a schematic representation.

The header contains no private keys or secrets, only enough public information to establish the trust chain for each signed payload. Secure boot now has enough data to complete the three steps required to validate each payload:

  1. Verify the signature over the payload by the three FW keys.
  2. Verify the signature over the FW keys by the three HW keys.
  3. Verify that the hash of the HW keys matches the hash stored in CPU SEEPROM.
If these three conditions are met, secure boot authorizes the component to run.

OpenPOWER firmware consists of several containers, packaged into a partitioned PNOR image. This PNOR image is what gets updated when you flash new host firmware to an OpenPOWER server.

Figure 3 shows the PNOR image layout. After the component headers are created, the process adds the containerized components to the PNOR image, with any necessary padding, then creates a table of contents (TOC) listing the partition offsets.

Figure 3: PNOR image layout

Key management and Separation of Duties

The private portions of the HW and FW keys will be managed by IBM, the ODM vendor or the end-customer, depending on the procurement method and the application. An OpenPOWER server purchased from IBM will ship with a hash of the IBM HW keys installed in SEEPROM. Customers will obtain their firmware updates from IBM, through IBM Fix Central, for example. When IBM packages the firmware for release they will build the containers using IBM-managed keys. IBM's manufacturing process specifies that these keys are stored in an HSM, where the private keys are never actually exposed outside the HSM. When the server is in secure mode, it will refuse to boot any firmware not signed by IBM.

Similarly, an OpenPOWER server purchased from an ODM vendor will ship with a hash of the ODM's HW keys installed in SEEPROM. Customers will obtain their firmware updates from the ODM, through their supported channels.

As mentioned, the use of (up to) three keys in each set, HW (A,B,C) and FW (P,Q,R), permits a separation of duties for each signing operation. When all three are used, secure boot requires a signature by all three keys to be present in the container header. These means that all three FW key holders must "sign off" on a firmware module to make it bootable, and all three HW key holders must sign off on the FW keys to authorize their respective modules.

Updating the platform HW keys — Key Transition

The holders of the three HW keys are effectively the platform owners, since it is the hash of these keys that is imprinted in SEEPROM. With secure mode in force, the system will refuse to boot any firmware not authorized by these keys.

The platform owner may choose to change the HW root keys by updating the HW key hash in SEEPROM. This is done through the imprinting process. First, the user prepares a special PNOR image that contains a transition container. The transition container is a container that is signed with the current HW keys, but carries a payload signed with the new HW keys. This payload is in fact an embedded container. The embedded container resembles a normal container, only it is signed with the new HW keys rather than the current keys. This is shown in figure 4.

Figure 4: PNOR image with Transition Container

When the system boots it detects the presence of the transition container in PNOR. Specifically, the Hostboot module performs this function. It then checks that a key transition bit is set within the signed portion of the Prefix header. If so, Hostboot enters the key transition code. Note that all executable code to this point has been verified by the current HW keys.

The transition code now reads the embedded container. The HW key fields in this container header hold the new HW keys (the ones to transition to), with corresponding signature fields that were generated by these keys. Before performing the SEEPROM update, the transition code does all the normal validation steps for this container using the new keys, just to verify that the keys are usable. This ensures that SEEPROM won't be updated with an unusable set of keys.

The transition code now has everything it needs to update SEEPROM. The operation is authorized, since only the holder of the current keys could create this package. The new keys are good, since they have been used to sign at least one valid container. The transition code now calculates the (SHA512) hash of the new keys and writes this value to SEEPROM, overwriting the current value. At this point, the system halts and powers down.

Next, the user flashes a new PNOR image signed with the new HW keys. From this point on, all images must be signed with these keys. The system now boots normally. Note that secure mode was enabled the entire time, and physical presence was not required. The user may do all operations remotely through the BMC.

Signtool Overview

This section gives an overview of the OpenPOWER firmware signing tool, a.k.a. signtool.

What is signtool?

Signtool is the OpenPOWER firmware signing tool. It is an open-source project on github (https://github.com/open-power/sb-signing-utils) and is one of the many packages integrated by op-build (https://github.com/open-power/op-build). Signtool is written in C, with some bash scripts as glue. The main programs are:

  • create-container — The basic container-header construction tool
  • print-container — A utility for displaying containers in human-readable format, and for performing the validation and verification functions.
  • crtSignedContainer.sh — The main program: a wrapper script to drive create-container and print-container, to create a signed, verified container in a single operation.
Other utilities include:
  • hashkeys — A tool for generating the HW keys hash, used primarily for verification
  • bulkSign.sh — A wrapper script to drive crtSignedContainer.sh, used to sign images in bulk when signing outside the op-build environment.

How is signtool used?

Signtool enables the creation of secure containers for OpenPOWER firmware components. The secure container consists of a 4 kB container header, followed by the payload. The payload is signed by the FW keys, and the FW keys are signed by the HW keys. The signatures and corresponding public keys are added to the container header. The process is describe in detail above.

Signtool was designed to be run automatically by op-build or to be used stand-alone. Under op-build, the tool generates a secure container for every firmware component in the PNOR — typically ten or more images. When run as a stand-alone tool, the program will generate a single container — for a given, single payload — with each invocation. There is however, a helper tool (bulkSign.sh) to sign multiple images in a single operation, for use with independent mode.

Your usage will depend on your choice of signing mode, and whether you are using signtool under op-build or stand-alone, as described in this document.

Signtool is supported on Linux and AIX, on x86_64 and PPC architectures, in big and little endian.

Signtool modes of operation

Signtool supports three modes of operation, or signing modes:

  • Local mode (a.k.a. development mode) — Build the container and sign using locally available private keys. Signatures are generated using simple openssl operations. Because the private keys are exposed on the local system (the build machine), this mode should be used only for development signing, or when the user is confident that the build machine is secure against unauthorized access.
  • Independent mode — Generate the signing requests locally and export the requests for signing externally. External signing is by user's method of choice: any method capable of generating a ECDSA p521 signature (the built-in support uses openssl). Resulting signatures are re-imported to the container build process, to create the completed container. No private or privileged information is exposed at the build machine.
  • Production mode — Build the container locally and interface with the remote signframework to retrieve signatures and (public) keys as needed. Signing is done remotely on a secure signing server using a hardware security module (HSM). Private keys are stored securely in the HSM and never exposed. Completed signatures are returned by the signframework and integrated into the container.
Note: In production mode, the private signing keys are never exposed on the build machine. However, the signframework interface requires a private ssh key to be installed or cached locally. The ssh key is password-encrypted (using a symmetric algorithm like AES-128) and the user is required to enter a password whenever the key is accessed. While the key is never exposed unencrypted, the local availability makes the key subject it to a brute-force attack if compromised. Also, the requirement for the user to enter a password at the build machine makes the build machine part of the overall attack surface. (See section: Threats and vulnerabilities for more details.)

Preparing the environment for production signing

Signtool depends on several configuration properties to govern its behavior. These properties determine the mode of operation (local, independent, or production mode), which keys are used for signing, whether container validation is enabled, and more. Signtool also recognizes and passes several properties to the signframework; these properties control how signframework interacts with the signing server, when in production mode.

Most configuration properties apply equally to signtool's stand-alone operation or running signtool under op-build. The few exceptions are noted in the following sections.

Precedence of configuration method for setting properties

Most configuration properties are settable via environment variable, command-line option or INI file property. When running under op-build, some properties may be set via the op-build configuration (the _defconfig) as well. In cases where a property is settable by more than one method, the order of precedence is as follows:

  1. A property set via shell environment, when running under op-build or running stand-alone, takes the lowest precedence.
  2. A property set via op-build configuration (running under op-build), or via signtool command line (running stand-alone), will override a property set via shell environment.
  3. A property set via INI file will override a property set via op-build configuration or command-line, or via shell environment.
Note that properties set via op-build _defconfig are passed to signtool as command-line options, and so have the same precedence as command-line.

In most cases, the user has a choice in how to introduce configuration properties, and the method chosen will be largely at the convenience of the user. The few exceptions (i.e. properties that must be set in a particular way) are described in the sections that follow.

Properties set via shell environment

The following is a description of signtool and signframework configuration properties that may be set via environment variable. Since most properties can be set in this way, the following table serves as the reference for most of the common properties. The properties that cannot be set via the environment are described in the sections that follow.

These variables may be set for signtool running under op-build or stand-alone, and will have the same behavior in both environments (assuming they are not overridden by a higher precedence method).

Property Attributes
SB_SIGN_MODE Description Indicates the signing mode for creating the secure container.
type string
Permitted values development — sign with locally available private keys production — request keys and signatures from the signframework
Default development
SB_PROJECT_INI Description Path to project INI file, used to set additional configuration properties
type string
Permitted values valid path to INI file
Default < unset >
SB_PROJECT_INI_TRANS Description Same as above but recognized for the transition "to" container only (SBKTRAND). If set, takes precedence over SB_PROJECT_INI for this container.
type string
Permitted values valid path to INI file
Default < unset >
SB_VALIDATE Description If true, perform all intra-container checks normally done during secure-boot, including validating payload hashes, and signatures
type boolean
Permitted values y / n / true / false
Default FALSE
SB_VERIFY Description If set, verify that the hash of the HW keys A,B,C in the container header matches the provided value, emulating the check done a boot time to compare against the value in system SEEPROM. If the check fails, the build is halted. If the check passes, this container should secure-boot on a machine with this hash installed.
type string
Permitted values a hexascii string containing the hash value, or the path to a file containing this data
Default < unset >
SB_VERIFY_TRANS Description Same as above but recognized for the transition "to" container only (SBKTRAND). If set, takes precedence over SB_VERIFY for this container.
type string
Permitted values a hexascii string containing the hash value, or the path to a file containing this data
Default < unset >
SB_PASS_ON_ERROR Description If true, and either SB_VALIDATE or SB_VERIFY is set, and an error is encountered on either (or both) operation(s), ignore the error and allow the build to continue.
type boolean
Permitted values y / n / true / false
Default FALSE
SB_ARCHIVE_OUT Description Path to a file or directory to write the archive file. The file is is a gzipped, tar archive of artifacts (keys and signatures) retrieved from signframework.
type string
Permitted values Value may be a full path (starts with /) or relative path. If relative, interpreted as relative to current directory. Value may be a directory (ends with /) or a file. If a directory, archive will have a default filename of the form "signtool_<ID>.tgz", where <ID> is a unique identifier. If a file, archive is written to the filename specified.
Default < unset >
SB_ARCHIVE_IN Description Path to archive file to import. Artifacts from the archive are imported into the cache.
type string
Permitted values valid path to archive file
Default < unset >
SB_SCRATCH_DIR Description Path to directory to use as root of signtool filecache
type string
Permitted values valid path to a directory
Default environment variable $TMPDIR; if unset, "/tmp/"
SB_KEEP_CACHE Description If true, do not delete cache on exit
type boolean
Permitted values y / n / true / false
Default TRUE
SB_VERBOSE Description Show verbose output. (also passes -v option to signframework)
type boolean
Permitted values y / n / true / false
Default FALSE
SB_DEBUG Description Show additional debug output. (also passes -d and -stdout options to signframework)
type boolean
Permitted values y / n / true / false
Default FALSE
SB_WRAP Description Column to wrap long output in verbose mode (signtool only). If set to 0, unlimited length (no wrap).
type integer
Permitted values integer value >= 0
Default 100 chars

Table 2: Configuration properties settable by environment variable - signtool

The following environment variables are recognized by signtool and passed as options to signframework. These properties control how signframework interacts with the signing server:

Property Attributes
SF_USER Description userid used by the signframework client to connect to the signframework
type string
Permitted values valid userid for signframework server
Default < unset >
SF_SSHKEY Description Path to the password-encrypted ssh key used to connect to the signframework.
type string
Permitted values valid path to SSH key file
Default < unset >
SF_EPWD Description Path a file containing the (server provisioned) CCA password
type string
Permitted values valid path to EPWD file
Default < unset >
SF_SERVER Description hostname or IP address of the signframework server
type string
Permitted values valid, reachable hostname or IP address
Default < unset >
SF_HW_SIGNING_PROJECT_BASE Description Basename used to construct the hardware signing project name to be passed to signframework
type string
Permitted values valid hardware signing project name as defined on signing server
Default sign_ecc_pwr_hw_key
SF_FW_SIGNING_PROJECT_BASE Description Basename used to construct the firmware signing project name to be passed to signframework
type string
Permitted values valid firmware signing project name as defined on signing server
Default sign_ecc_pwr_fw_key_op_bld
SF_GETPUBKEY_PROJECT_BASE Description Basename used to construct the project name for pub key retrieval to be passed to signframework
type string
Permitted values valid getkey signing project name as defined on signing server
Default getpubkeyecc

Table 3: Configuration properties settable by environment variable - signframework

Notes:

  • Environment variable names are case sensitive
  • Do not use spaces surrounding the assignment operator ('=')
  • Use quotes for values containing spaces
  • You must "export" these variables when setting them, to ensure they are recognized by signtool. Alternately, you may add the variables to the command-line, separated by spaces, preceding the signtool command. For example:
  $ SB_PROJECT_INI=project.ini \
    SB_ARCHIVE_OUT=/tmp/ \
    crtSignedContainer.sh ...

Properties set via signtool command-line

The following is a partial list of signtool command-line options. The options listed here are those corresponding to configuration properties described above. For a full list of command-line options, run crtSignedContainer.sh --help, or see the signtool package documentation at https://github.com/open-power/sb-signing-utils.

Note that when running under op-build, the signtool command-line options are not exposed to the user. The user may set the options via the op-build _defconfig (where applicable) or via the project INI.

Option Attributes
--mode Description same as SB_SIGN_MODE
--sign-project-config Description same as SB_PROJECT_INI
--validate Description same as SB_VALIDATE
--verify Description same as SB_VERIFY
--archiveOut Description same as SB_ARCHIVE_OUT
--archiveIn Description same as SB_ARCHIVE_IN
--scratchDir Description same as SB_SCRATCH_DIR
--verbose Description same as SB_VERBOSE
--debug Description same as SB_DEBUG
--wrap Description same as SB_WRAP
--code-start-offset Description code start offset (software header field) in hex
type string
Permitted values 4-byte (8 char) hexascii value
Default < unset >
--flags Description flags (hardware header field) in hex
type string
Permitted values 4-byte (8 char) hexascii value
Default < unset >
--hwKeyA --hwKeyB --hwKeyC Description In development mode: Path to the file containing HW key A,B,C in PEM format In production mode: special keywords "__get", "__getkey", "__getsig", "__skip"
type string
Permitted values valid path to key file, or one of the above keywords
Default < unset >
--swKeyP --swKeyQ --swKeyR Description In development mode: Path to the file containing FW key P,Q,R in PEM format In production mode: special keywords "__get", "__getkey", "__getsig", "__skip"
type string
Permitted values valid path to key file, or one of the above keywords
Default < unset >

Table 4: Command-line options to signtool (partial list)

Properties set via INI file

The INI file is intended primarily for production mode, since most of the supported properties are for signframework operation. However, there are also a few properties applicable to development mode, so the INI may be used in either mode. In the case where the INI contains a property not applicable to the current mode, the property is ignored.

Following is a list of properties supported by the INI:

Property Section Description
userid [signer] same as SF_USER
ssh_keyfile [signer] same as SF_SSHKEY
epwd_file [signer] same as SF_EPWD
hostname [server] same as SF_SERVER
validate [signtool] same as SB_VALIDATE
verify [signtool] same as SB_VERIFY
verify_trans [signtool] same as SB_VERIFY_TRANS
pass_on_validation_error [signtool] same as SB_PASS_ON_ERROR
hw_signing_project_basename [signproject] same as SF_HW_SIGNING_PROJECT_BASE
fw_signing_project_basename [signproject] same as SF_FW_SIGNING_PROJECT_BASE
getpubkey_project_basename [signproject] same as SF_GETPUBKEY_PROJECT_BASE

Table 5: Configuration properties settable via INI file

The INI file uses a format of [section] headers enclosed in brackets, followed by one or more "property = value" pairs. Here is a sample file:

[signer]
; Properties related to FW key signer
userid = sf_sign
sshkey_file = /path/to/id_rsa.sign
epwd_file = /path/to/private/epwd.txt

[server]
; Properties related to signframework server
hostname = server.mydomain.com

[signproject]
; Properties related to signframework signing project
hw_signing_project_basename = sign_ecc_pwr_hw_key
fw_signing_project_basename = sign_ecc_pwr_fw_key_op_bld
getpubkey_project_basename = getpubkeyecc

[signtool]
; Properties related to signtool
validate=y
verify=/path/to/hw_keys_hash
pass_on_validation_error=n

Notes:

  • Section names and property names are case sensitive.
  • Do not use quotes in the INI file. The parser will properly handle values containing spaces, without the need for quotes.
  • You may optionally use spaces surrounding the assignment operator ('=')
  • Lines beginning with ';' (semicolon) are treated as comments.
One functional advantage to using an INI file: Because of the precedence, any property set in the INI will override the same property set by any other method. Enabling the INI requires the setting of only one configuration property: SB_PROJECT_INI or --project-config-file. This means that the user may toggle the entire configuration within the INI, with just one "switch". This may be useful for testing, or for changing configurations in production.

Properties set via op-build configuration

There are a few properties that may be set via op-build _defconfig:

Option Attributes
BR2_OPENPOWER_SECUREBOOT_SIGN_MODE Description same as SB_SIGN_MODE
BR2_OPENPOWER_SECUREBOOT_NO_KEY_TRANSITION Description Builds a PNOR that does not transition Secure Boot keys
type boolean
Permitted values y / n
Default TRUE
BR2_OPENPOWER_SECUREBOOT_KEY_TRANSITION_TO_DEV Description Builds a PNOR that transitions Secure Boot keys to development keys and powers off the system. Only usable when system security has been disabled ("key recovery" image)
type boolean
Permitted values y / n
Default FALSE
BR2_OPENPOWER_SECUREBOOT_KEY_TRANSITION_TO_PROD Description Builds a PNOR that transitions Secure Boot development keys to vendor supplied production keys and powers off the system. ("key transition" image)
type boolean
Permitted values y / n
Default FALSE

Table 6: Configuration properties settable via op-build _defconfg

  • BR2_OPENPOWER_SECUREBOOT_SIGN_MODE may be set in the _defconfig, or on the op-build command line.
  • BR2_OPENPOWER_SECUREBOOT_NO_KEY_TRANSITION, BR2_OPENPOWER_SECUREBOOT_KEY_TRANSITION_TO_DEV and BR2_OPENPOWER_SECUREBOOT_KEY_TRANSITION_TO_PROD may be set in the _defconfig, or on the op-build command line.

Recommendations on which configuration methods to use

As mentioned, the choice of configuration method is largely up to the user. There are some practical limitations, however:

  • Some properties are not settable by every configuration method. For example, the path to the INI file cannot be set in the INI, so it it must be set by environment or command-line.
  • Some properties are not supported by every configuration method, even if there is no technical reason preventing it. For example, the values for --flags and --code-start-offset must be provided by command line; they are not settable by environment or INI.
  • Running under op-build presents a more restricted environment than running stand-alone, since the signtool command-line options are not exposed to the user. If a property is set by op-build, and the user wants to override, the user must set the property via the INI. (Since environment takes a lower precedence than op-build configuration.)
Within these limitations, the user may do what is most convenient. Here are some suggestions:

Set all properties via environment or command-line

With this approach, the user aims to avoid using the INI file. In the case of stand-alone, the user may set everything through environment or command-line. In the case of op-build, the user may set everything though environment or op-build _defconfig.

Set as many properties as possible via the INI

With this approach, the user aims to place as many properties as possible in the INI file, and set as few as possible by environment. This is well-suited to running under op-build, since op-build handles many of the required properties automatically (by passing them to the signtool command-line) and the remaining properties can mostly be set through the INI.

In the In the case of running stand-alone as well, the user may like the convenience and portability of keeping as many properties as possible in the INI.

Signing in Local mode (a.k.a. Development mode)

Local mode is the default mode of operation for signtool. In this mode the private keys are available on the local filesystem (or a filesystem accessible by signtool) and the signatures are generated directly by signtool. Because the private keys are exposed on the local system, local mode should be used only for development signing, or when the user is confident that the build machine is secure against unauthorized access.

Running signtool stand-alone

When running stand-alone, the main program, crtSignedContainer.sh, accepts a single payload to be signed as input, and writes a single signed container as output. The signed container is the (4 kB) secure header with the signed payload attached.

Here are some examples of stand-alone operation using the test keys provided in the project:

Example 1: Create a complete container signed with HW and FW keys.

Here the user specifies HW private keys A,B,C, and at least one FW private key P. The user also requests the completed container to be validated and verified. Validation performs all internal checks on the container, and verification checks the hash of the three HW keys A,B,C against an independent value. (for more information see section: Using Validate and Verify).

Following is the crtSignedContainer.sh invocation, and output to console:

$ crtSignedContainer.sh \
  -a ./test/keys/hw_key_a.key \
  -b ./test/keys/hw_key_b.key \
  -c ./test/keys/hw_key_c.key \
  -p ./test/keys/fw_key_p.key \
  --protectedPayload secure-payload --out secure-container \
  --validate --verify ./test/keys/hw_keys_hash.md

--> crtSignedContainer.sh: Signing mode: local
--> crtSignedContainer.sh: Creating new cache dir: /tmp/SIGNTOOL_1516648454
--> crtSignedContainer.sh: Creating new cache subdir: /tmp/SIGNTOOL_1516648454/IMAGE
--> crtSignedContainer.sh: Generating signing requests...
--> crtSignedContainer.sh: Generating signature for HW key A...
--> crtSignedContainer.sh: Generating signature for HW key B...
--> crtSignedContainer.sh: Generating signature for HW key C...
--> crtSignedContainer.sh: Generating signature for SW key P...
--> crtSignedContainer.sh: Have signatures for keys A,B,C,P, adding to container...
--> crtSignedContainer.sh: Container IMAGE build completed.

Container validity check PASSED. Container verification check PASSED.

Example 2: Create an (incomplete) container signed with only two HW keys.

Same as above, except the user specified only two of three required HW keys. Verification fails because the hash of the HW keys (only A,B) does not match the independent value:

$ crtSignedContainer.sh \
  -a ./test/keys/hw_key_a.key \
  -b ./test/keys/hw_key_b.key \
  -p ./test/keys/fw_key_p.key \
  --protectedPayload secure-payload --out secure-container \
  --validate --verify ./test/keys/hw_keys_hash.md

--> crtSignedContainer.sh: Signing mode: local
--> crtSignedContainer.sh: Creating new cache dir: /tmp/SIGNTOOL_1516648999
--> crtSignedContainer.sh: Creating new cache subdir: /tmp/SIGNTOOL_1516648999/IMAGE
--> crtSignedContainer.sh: Generating signing requests...
--> crtSignedContainer.sh: Generating signature for HW key A...
--> crtSignedContainer.sh: Generating signature for HW key B...
--> crtSignedContainer.sh: Generating signature for SW key P...
--> crtSignedContainer.sh: Have signatures for keys A,B,P, adding to container...
--> crtSignedContainer.sh: Container IMAGE build completed.

Container validity check PASSED. Container verification check FAILED.

Example 3: Create a “mock” container with no keys or signatures.

The mock container is using only for testing.

Here the user passed no command line options at all. No keys were provided, so no signatures were generated. The user omitted the --protectedPayload and --out options, causing the program to use an empty payload by default, and to discard the result. (Had the user specified --out, the program would have written the 4k container header with no payload attached.) The --validate and --verify options were also omitted.

$ crtSignedContainer.sh
--> crtSignedContainer.sh: Signing mode: local
--> crtSignedContainer.sh: Creating new cache dir: /tmp/SIGNTOOL_1516640395
--> crtSignedContainer.sh: Creating new cache subdir: /tmp/SIGNTOOL_1516640395/IMAGE
--> crtSignedContainer.sh: Generating signing requests...
--> crtSignedContainer.sh: No signatures available.
--> crtSignedContainer.sh: Container IMAGE build completed.

Using --verbose and --debug

With these options added, the program will dump more output to the console showing the details of the container construction process, and details of validation and verification if requested.

The output is from example 2 above, where verification failed. The output shows the reason for the failure. For brevity only --verbose output is shown; --debug is omitted.

$ crtSignedContainer.sh \
  -a ./test/keys/hw_key_a.key \
  -b ./test/keys/hw_key_b.key \
  -p ./test/keys/fw_key_p.key \
  --protectedPayload secure-payload --out secure-container \
  --validate --verify ./test/keys/hw_keys_hash.md \
  --verbose

--> crtSignedContainer.sh: Signing mode: local
--> crtSignedContainer.sh: Key HW_KEY_A is a private ECDSA key
--> crtSignedContainer.sh: Key HW_KEY_B is a private ECDSA key
--> crtSignedContainer.sh: Key SW_KEY_P is a private ECDSA key
--> crtSignedContainer.sh: Creating new cache dir: /tmp/SIGNTOOL_1516656270
--> crtSignedContainer.sh: Creating new cache subdir: /tmp/SIGNTOOL_1516656270/IMAGE
--> crtSignedContainer.sh: Generating signing requests...
--> create-container: pubkey A =  00bb1e087896a09e307274068de7ca8a02a09c55438f50f4de291e63
    379f736cb7c27a1ef277b2781f97d3bd64a5783cde710056ec6a9b5627d4830908ff53cfb36100b8609441
    76473722512c05f860f1f025bb4654819716ed10fc693830fcfed2269e346283f3a781915c7bc7ddb3b34f
    114c4fb284bbc4243a57e75201a60cf90622
--> create-container: pubkey B =  009303c8619e460864aadcd4ed2da2322e179321b3dfd72cca127cf9
    837aa4978365fdb63ab9f7c86d4f9b83594ffd59fac8490f70f35451f9b944bdc63e19cb641101493d874e
    6a9b17d93568370be525e56982c405c1eaf84ce926355d0555b1fbb09887470f913870b94ce9b2f587d01c
    2736b80a889da66a3ca4f3d770fccd00860b
--> create-container: HW keys hash =  3a16e1ecc4337ab9569f6fbd5953213c7eb52f604fc3297880f4
    9047ba44de0199e8adba716726b1c346a62ad40a4c4ddf94f8b90bfdeedc0e7faf9a5b4f90aa
--> create-container: pubkey P =  00fc5ba34cea755fb18a6cef9928d607cdac93bd5ea6d602e52ec850
    3f3bc4b9b4fc153fe6ad1a97383c4fae4c4236e2dee69182da9198671a5990a63af87e3905be00190f085c
    43e8569b35d8101a1b6083e022d262fbc1afc411c166f416aa955645f53d9d23b12d38e36778b776f06742
    90a7c787294ed2093f316e13e917ab12c39d
--> create-container: SW keys hash =  893d5a17868f236fcb0ffc2c3e49c65c74306ae9e8f37b78cc2d
    08cc88f1c076656005e2c8c11d1aababf47397939990ea66d52b08a4f717b6cc8fdace88f223
--> create-container: PR header hash  = d83e117c6c6ef3354d1d3bbe4e04c507f3aafd509f74ff3bec
    63ae2c79936a81b0e2665aebcab73c46ae2aa1189405ac9f8294103104293626a1a2ee55881955
--> create-container: component ID (was reserved) = IMAGE
--> create-container: Payload hash =  2e513804cbcc4d0cec19f233b93918f138896f3aa0272591eac6
    cfcb2d344f2d9c1ba74cb0ea6fd40d03f768d7684c1f75bea097671b0309793d6d1b1e2ff4fe
--> create-container: SW header hash  = 96e949972b3205acd64ce756ddb1f7ab19ae4d6d402259ea58
    0769ea5aa5a4b90350063a3f3bf510952169550cac8e64d993c491a5ab4048132775b3be3b736e
--> create-container: HW header size        =  426 (0x01aa) at offset    0 (000000)
--> create-container: Prefix header size    =   98 (0x0062) at offset  426 (0x01aa)
--> create-container: Prefix data size      =  528 (0x0210) at offset  524 (0x020c)
--> create-container: SW header size        =   98 (0x0062) at offset 1052 (0x041c)
--> create-container: SW signature size     =  132 (0x0084) at offset 1150 (0x047e)
--> create-container: TOTAL HEADER SIZE     = 4096 (0x1000)
--> create-container: PAYLOAD SIZE          = 687295 (0xa7cbf)
--> create-container: TOTAL CONTAINER SIZE  = 691391 (0xa8cbf)
--> crtSignedContainer.sh: Generating signature for HW key A...
--> crtSignedContainer.sh: Generating signature for HW key B...
--> crtSignedContainer.sh: Generating signature for SW key P...
--> crtSignedContainer.sh: Have signatures for keys A,B,P, adding to container...
--> create-container: pubkey A =  00bb1e087896a09e307274068de7ca8a02a09c55438f50f4de291e63
    379f736cb7c27a1ef277b2781f97d3bd64a5783cde710056ec6a9b5627d4830908ff53cfb36100b8609441
    76473722512c05f860f1f025bb4654819716ed10fc693830fcfed2269e346283f3a781915c7bc7ddb3b34f
    114c4fb284bbc4243a57e75201a60cf90622
--> create-container: pubkey B =  009303c8619e460864aadcd4ed2da2322e179321b3dfd72cca127cf9
    837aa4978365fdb63ab9f7c86d4f9b83594ffd59fac8490f70f35451f9b944bdc63e19cb641101493d874e
    6a9b17d93568370be525e56982c405c1eaf84ce926355d0555b1fbb09887470f913870b94ce9b2f587d01c
    2736b80a889da66a3ca4f3d770fccd00860b
--> create-container: HW keys hash =  3a16e1ecc4337ab9569f6fbd5953213c7eb52f604fc3297880f4
    9047ba44de0199e8adba716726b1c346a62ad40a4c4ddf94f8b90bfdeedc0e7faf9a5b4f90aa
--> create-container: signature A = 0075f123f2a27f1115d2418c7597562a52fba5e0257cd749046a45
    27bbfe147e3255b430ea4145ebbc88492b2216f88bceaa1af3c9ee599da81e58a4269182f1764a006d26fa
    f59398c813fd5ae4ce9e66e1c1a20c0121301b444fca7490154de1a9a3fbab1a670db05d4df5d180c257c2
    070b1f78815e5c8efd3fdc54b2daf3cf487eb5
--> create-container: signature B = 006a80575d6fc33338c0f956ce346b2265ce21e95d3bf200253a6c
    9c86768394b9d216001cb8b867a4b46c40326cd2bca81157003b572a18a65306f6461db823b70200a94c04
    52581adf2343292c2467a630baacda19b913ff21cd77dc1dc35e92ff11e2999c6f6b10cc4db3bfbf984c70
    c102ad2ad00b6e36a8776a4a3ab12ec671199b
--> create-container: pubkey P =  00fc5ba34cea755fb18a6cef9928d607cdac93bd5ea6d602e52ec850
    3f3bc4b9b4fc153fe6ad1a97383c4fae4c4236e2dee69182da9198671a5990a63af87e3905be00190f085c
    43e8569b35d8101a1b6083e022d262fbc1afc411c166f416aa955645f53d9d23b12d38e36778b776f06742
    90a7c787294ed2093f316e13e917ab12c39d
--> create-container: SW keys hash =  893d5a17868f236fcb0ffc2c3e49c65c74306ae9e8f37b78cc2d
    08cc88f1c076656005e2c8c11d1aababf47397939990ea66d52b08a4f717b6cc8fdace88f223
--> create-container: component ID (was reserved) = IMAGE
--> create-container: Payload hash =  2e513804cbcc4d0cec19f233b93918f138896f3aa0272591eac6
    cfcb2d344f2d9c1ba74cb0ea6fd40d03f768d7684c1f75bea097671b0309793d6d1b1e2ff4fe
--> create-container: signature P = 003feffb277ec95f800281ceefb12fb763bc1f58b1004a2db26d2c
    ee700145d5c38e41a631e30ed4bf203ef97428f759b53544ba2e1b503c12df2e9065dd38db8b6a01eaf617
    3ec7ef010f3f3bc396cf084479f2a99658f70c1455454a94aac61487da3d12ee1df17a6953c08899557bb5
    66a64dc949f82777e4c51b52c3206715a1d5c8
--> create-container: HW header size        =  426 (0x01aa) at offset    0 (000000)
--> create-container: Prefix header size    =   98 (0x0062) at offset  426 (0x01aa)
--> create-container: Prefix data size      =  528 (0x0210) at offset  524 (0x020c)
--> create-container: SW header size        =   98 (0x0062) at offset 1052 (0x041c)
--> create-container: SW signature size     =  132 (0x0084) at offset 1150 (0x047e)
--> create-container: TOTAL HEADER SIZE     = 4096 (0x1000)
--> create-container: PAYLOAD SIZE          = 687295 (0xa7cbf)
--> create-container: TOTAL CONTAINER SIZE  = 691391 (0xa8cbf)
--> crtSignedContainer.sh: Container IMAGE build completed.

PR header hash = d83e117c6c6ef3354d1d3bbe4e04c507f3aafd509f74ff3bec63ae2c79936a81b0e2665a
                 ebcab73c46ae2aa1189405ac9f8294103104293626a1a2ee55881955
HW_key_A signature is good: VERIFIED ./
HW_key_B signature is good: VERIFIED ./
HW_key_C is NULL, skipping signature check.

SW header hash = 96e949972b3205acd64ce756ddb1f7ab19ae4d6d402259ea580769ea5aa5a4b90350063a
                 3f3bf510952169550cac8e64d993c491a5ab4048132775b3be3b736e
SW_key_P signature is good: VERIFIED ./

Payload hash = 2e513804cbcc4d0cec19f233b93918f138896f3aa0272591eac6cfcb2d344f2d9c1ba74cb0
               ea6fd40d03f768d7684c1f75bea097671b0309793d6d1b1e2ff4fe
Payload hash agrees with value in SW header: VERIFIED ./

SW keys hash = 893d5a17868f236fcb0ffc2c3e49c65c74306ae9e8f37b78cc2d08cc88f1c076656005e2c8
               c11d1aababf47397939990ea66d52b08a4f717b6cc8fdace88f223
SW keys hash agrees with value in Prefix header: VERIFIED ./

HW keys hash = 3a16e1ecc4337ab9569f6fbd5953213c7eb52f604fc3297880f49047ba44de0199e8adba71
               6726b1c346a62ad40a4c4ddf94f8b90bfdeedc0e7faf9a5b4f90aa
HW keys hash does not agree with provided value: MISMATCH

Container validity check PASSED. Container verification check FAILED.

Using print-container to examine a container

The print-container tool will display the contents of the container header in human-readable format:

$ print-container --imagefile /tmp/secure-container
Container:
magic:          0x17082011
version:        0x01
container_size: 0x00003169 (12649)
target_hrmor:   0x00000000
stack_pointer:  0x00000000
hw_pkey_a: 00bb1e087896a09e307274068de7ca8a02a09c55438f50f4de291e63379f736cb7c27a1ef2
           77b2781f97d3bd64a5783cde710056ec6a9b5627d4830908ff53cfb36100b8609441764737
           22512c05f860f1f025bb4654819716ed10fc693830fcfed2269e346283f3a781915c7bc7dd
           b3b34f114c4fb284bbc4243a57e75201a60cf90622
hw_pkey_b: 009303c8619e460864aadcd4ed2da2322e179321b3dfd72cca127cf9837aa4978365fdb63a
           b9f7c86d4f9b83594ffd59fac8490f70f35451f9b944bdc63e19cb641101493d874e6a9b17
           d93568370be525e56982c405c1eaf84ce926355d0555b1fbb09887470f913870b94ce9b2f5
           87d01c2736b80a889da66a3ca4f3d770fccd00860b
hw_pkey_c: 0154042b528d1f4f889a95860fc422c346a74a61bc9f201395c11539e8c7c9fc6be262e1ee
           ebf1469ef5079aea19ef59ae87cc0ba9d4a109025eafa1bca1c88b5a7a00c41d1cb9e94222
           8fd9c355885221789a104967f312d5cf9701fff853a04b5ec4752f75ac7ce4d69cc1ca3a17
           218dc73a6d19d74bfbf76cfeb919d394357c552b59
HW keys hash (calculated):
           40d487ff7380ed6ad54775d5795fea0de2f541fea9db06b8466a42a320e65f75b486654600
           17d907515dc2a5f9fc50954d6ee0c9b67d219dfb7085351d01d6d1

Prefix Header:
ver_alg:
  version:  0001
  hash_alg: 01 (SHA512)
  sig_alg:  01 (SHA512/ECDSA-521)
code_start_offset: 00000000
reserved:          00000000
flags:             80000000
sw_key_count:      03
payload_size:      0000018c
payload_hash:      40d487ff7380ed6ad54775d5795fea0de2f541fea9db06b8466a42a320e65f75b4
                   8665460017d907515dc2a5f9fc50954d6ee0c9b67d219dfb7085351d01d6d1
ecid_count:        00

Prefix Data:
hw_sig_a:  005bf6ca9f76972765bac3cdb425814af2840c8e6e42d36745fa197dc26140e82b6275dda6
           13471a3777f445e25d3f83c5414b34b92986445af9689371a9d3bd6ed6019d181f29bc8a37
           a7785c0945d17ae5e5cd93b658581709ea168b6ee9f2661af049f1bafc615c63a8e0d5aa88
           798defdf8e845b5ba94151743f37811103e3b0ca30
hw_sig_b:  00781d4e5f1d9753ae10f1f67088f1a8b43654064d8f824a6c4ec2c4d18f1009f826599e13
           ddf3513c657d3b5343dd84d9732930d2690e6dfc1c0275c99974e9bdea00fe88023492e50c
           e99a0589a167856486fb59cc6a83ca61d4240c7dad8b4a3c03954b57c3b6b1f52ee0785e0b
           e692b72e193b9e218c7d0fa8a91d85a1ce37442c43
hw_sig_c:  00d63d6df34437a5ba27936e3bf5ac6d5614818bb496d21ac0ab9bd4117f9f83fdc527fe4e
           3ead5df591f63b8501edc250ab4e06125af1f2be4ede5381ae95b685b100114903211add51
           49db0e89a1b5126ac9a73e1789733b6e1f96ee6a767baab60fdba298d2b5eca8c4fdfc1b8e
           5f25a4149f6cae6abbd6d4b2799d430eea60f9846b
sw_pkey_p: 00bb1e087896a09e307274068de7ca8a02a09c55438f50f4de291e63379f736cb7c27a1ef2
           77b2781f97d3bd64a5783cde710056ec6a9b5627d4830908ff53cfb36100b8609441764737
           22512c05f860f1f025bb4654819716ed10fc693830fcfed2269e346283f3a781915c7bc7dd
           b3b34f114c4fb284bbc4243a57e75201a60cf90622
sw_pkey_q: 009303c8619e460864aadcd4ed2da2322e179321b3dfd72cca127cf9837aa4978365fdb63a
           b9f7c86d4f9b83594ffd59fac8490f70f35451f9b944bdc63e19cb641101493d874e6a9b17
           d93568370be525e56982c405c1eaf84ce926355d0555b1fbb09887470f913870b94ce9b2f5
           87d01c2736b80a889da66a3ca4f3d770fccd00860b
sw_pkey_r: 0154042b528d1f4f889a95860fc422c346a74a61bc9f201395c11539e8c7c9fc6be262e1ee
           ebf1469ef5079aea19ef59ae87cc0ba9d4a109025eafa1bca1c88b5a7a00c41d1cb9e94222
           8fd9c355885221789a104967f312d5cf9701fff853a04b5ec4752f75ac7ce4d69cc1ca3a17
           218dc73a6d19d74bfbf76cfeb919d394357c552b59

Software Header:
ver_alg:
  version:  0001
  hash_alg: 01 (SHA512)
  sig_alg:  01 (SHA512/ECDSA-521)
code_start_offset: 00000180
reserved:          4f43430000000000
reserved (ASCII):  OCC
flags:             00000000
reserved_0:        00
payload_size:      00002169 (8553)
payload_hash:      f7775026e49bee18391879a3ada9d3b1119ad127e6a0947fe80da05eaab048f18e
                   3683f06cccbf464c483119c800f783d200131595ad4434b06197806437e429
ecid_count:        00

Software Signatures:
sw_sig_p:  00628b46cf9650bcee9cc9d3a99799a3eebc0ea63123d5fb259abcdce5ba00d967f5eb6e95            
           2edf1639be9d424c77e616a3f2735fa6d7b1dc008521cc23ab6de81d44013b8600f4dfb83a
           d5609a9be9befc2ff50698a0b58b5e3ed1e5d73c51d8e1f96c4098ce0aeb2d102ff76d019c
           bc5cad0377ba8e1bb81a60f8f4712b31dd8a04a4b8
sw_sig_q:  0038b75e8e952917265df2b4d6ef72ee549c127f9fd5cda447b2eceb7373192edeed2ef203
           48646feccfc003af3238c22af5d32f4d3af3c00208f926702496e23979001087219578a2fb
           198130ecc9e2171f26c2da5c0809b3e82c4ed5d45703a8a480fb3d52f3e6718e0b94a4aa58
           9445fa535c25ffb6c7a68a8a81dd03650e9f95c839
sw_sig_r:  0053834878b6a68371e52aeaf2dbda657046153687d3dcc5faab2a0ff10f571400d3e995b9
           c5acdabb63644ab4079e9ad6d6a2dfd8adb26ac4a43b59795dc6effdab00a6a2044e88ba2e
           a2ca0a8d622737ed78081596d836df170f542c4f1e235115815211bc0f91ea2ecfc0b5090d
           2d709929a172d230d67c2ad595d4abaa95e78e8f45

Using print-container to verify a container

To verify the contents of an existing container, use print-container with the --verify and --validate options:

$ print-container -v --no-print --validate --verify /tmp/keys/hw_keys_hash.md \
                  --imagefile /tmp/secure-container

PR header hash = b77cbacc53a0044912fc373b5aea04e5962cdd4cf716006361019969fe9517a28c71
                 b233dc06cac4250fdbe2963278b08ce5ed50edead29a596bdee88217c6fa
HW_key_A signature is good: VERIFIED ./
HW_key_B signature is good: VERIFIED ./
HW_key_C signature is good: VERIFIED ./

SW header hash = 9fe2fe0d6b2c2a62aa4c68a59036f657fc40636c1757629cab6f5c4d72e10da731cc
                 241958083eee623d2bf43ca696d9f951bed3335dea672de23b87940ff97f
SW_key_P signature is good: VERIFIED ./
SW_key_Q signature is good: VERIFIED ./
SW_key_R signature is good: VERIFIED ./

Payload hash = f7775026e49bee18391879a3ada9d3b1119ad127e6a0947fe80da05eaab048f18e3683
               f06cccbf464c483119c800f783d200131595ad4434b06197806437e429
Payload hash agrees with value in SW header: VERIFIED ./

SW keys hash = 40d487ff7380ed6ad54775d5795fea0de2f541fea9db06b8466a42a320e65f75b48665
               460017d907515dc2a5f9fc50954d6ee0c9b67d219dfb7085351d01d6d1
SW keys hash agrees with value in Prefix header: VERIFIED ./

HW keys hash = 40d487ff7380ed6ad54775d5795fea0de2f541fea9db06b8466a42a320e65f75b48665
               460017d907515dc2a5f9fc50954d6ee0c9b67d219dfb7085351d01d6d1
HW keys hash agrees with provided value: VERIFIED ./

Container validity check PASSED. Container verification check PASSED.

Running signtool under op-build

Local mode is the default signing mode under op-build. In local mode, op-build will sign the images with the IBM Imprint keys.

op-build automatically runs signtool for every firmware image added to the PNOR. The signing operations are invoked toward the end of the op-build process, during PNOR image construction.

Running the full op-build

To create a signed PNOR under op-build, clone the project and select a machine config that has signing enabled by default:

$ git clone --recursive https://github.com/open-power/op-build.git
$ cd op-build/
$ source op-build-env
$ op-build witherspoon_defconfig

Now run the full build:

$ op-build

The signed PNOR will be written to ./output/images/, and the output log will go to the console. To see the signtool output, check the log toward the end of the build.

Running the PNOR image rebuild

After the full op-build has completed, it is possible to re-invoke the signing operation by rerunning the final PNOR packaging step. From the same directory as above:

$ op-build openpower-pnor-rebuild

This will rebuild (repackage, really) the PNOR image using the previously built component images. This typically takes less than a minute. The images will be re-signed, and the signtool output should be clearly visible in the op-build output log.

Note that the rebuild step may be run as many times as desired and (assuming no configuration changes) will always produce the same result. The ability to run the PNOR build without having to re-launch the entire op-build is quite helpful for testing and troubleshooting.

Using SB_VERBOSE and SB_DEBUG

As in stand-alone operation, you can ask signtool to dump more detailed output to the console. However, because op-build runs signtool for you, the command line options are not exposed under op-build. So you must set the environment variables SB_VERBOSE and SB_DEBUG to enable these options. See section: Properties set via shell environment for details.

(Note that you can use SB_VERBOSE and SB_DEBUG in stand-alone operation, alternately to setting the options via command line.)

With these options enabled, signtool will dump the verbose and/or debug output to the console, in a manner similar to stand-alone operation shown above. The signtool output should be clearly visible in the op-build output log.

Signing with the IBM Imprint keys

By default, op-build signs the PNOR with the IBM imprint keys. The imprint keys are a well-known, openly-published set of keypairs for HW keys A,B,C, where the private keys are intentionally exposed.

The imprint keys are used by IBM manufacturing when shipping an IBM-manufactured machine to an end-customer or ODM partner who intends to re-imprint the root HW keys with their own set of keys. This allows the customer/partner to change the root authorization, or "take control" of the machine. The re-imprinting is done by updating the hash of the HW keys in system SEEPROM.

The use of the imprint keys (as opposed to shipping the box with no keys, or random keys) allows the user to update the SEEPROM without leaving secure mode. However, the customer/partner must be sure the machine is procured through a trusted channel (i.e. secure supply chain).

*** NOTE ***

It is IMPERATIVE that the imprint keys NOT be used for production. The private keys are intentionally exposed, which is not the typical case for a cryptographic system. If you build a PNOR image signed with imprint keys (the default under op-build) it will not boot on an IBM or ODM-branded machine, since these do not ship with imprint keys in the SEEPROM. However, if you procure a machine with the imprint keys hash installed, it will boot the default op-build image and your machine WILL NOT BE SECURE!!

*** NOTE ***

In op-build, the imprint keys serve merely as a convenient set of test keys to use for development signing.

Signing with your own keys

The IBM imprint keys are stored in the ./openpower/package/sb-signing-utils/keys/ directory under op-build. To use your own keys, simply replace the imprint keys before running op-build.

To generate your own keys you may use the openssl ecparam command:

To generate your own keys using openssl:

$ openssl ecparam -genkey -outform pem -noout -name secp521r1 -out hw_key_a.key
$ openssl ecparam -genkey -outform pem -noout -name secp521r1 -out hw_key_b.key
$ openssl ecparam -genkey -outform pem -noout -name secp521r1 -out hw_key_c.key
$ openssl ecparam -genkey -outform pem -noout -name secp521r1 -out sw_key_p.key
$ hashkeys -a hw_key_a.key -b hw_key_b.key -c hw_key_c.key --outfile hw_keys_hash.md

(This is not required unless you intend to use the file for verification. But it is recommended to do it anyway to ensure consistency of the files and avoid confusion.)

Now when you run op-build it will use your new keys to sign the PNOR.

If you replaced the keys before running op-build for the first time, this is all you need to do. If you want to update the keys after running op-build — to rebuild the PNOR, for example — you must perform an additional step. When op-build first runs it copies the keys from the location above to a new location under ./output/host/etc/keys/. So you must update the keys here if you want op-build to find them for new signing operation.

To do this, you may manually copy your keys to the new location. Or you may run the command to rebuild the sb-signing-utils project (which is fast), and this will copy the keys from ./openpower/package/sb-signing-utils/keys/ to the new location under ./output/host/etc/keys/:

$ op-build host-sb-signing-utils-rebuild

Now when you run the PNOR image rebuild it will pick up the new keys and use them to sign the PNOR:

$ op-build openpower-pnor-rebuild

Signing in Production mode

In production mode, signtool interfaces with signframework to retrieve keys and signatures from the remote signing server. Signing is done on the signing server using a hardware security module (HSM). Private keys are stored securely in the HSM and never exposed. Completed signatures are returned via the signframework and integrated into the container.

Although the method of retrieving keys and signatures is different than local mode, the remainder of signtool operation is very similar, and the usage of the tool for stand-alone operation, and under op-build, is basically the same.

Methods of signing Power firmware images

To understand how signing works in production mode, it is helpful to understand how an image may be signed.

For OpenPOWER firmware: There are three general ways one can sign an image to be included in an op-build generated PNOR:

  1. in situ — Sign directly under op-build, during a full op-build operation, or during the final openpower-pnor-rebuild step. The signframework requires the user to enter a password, so in production mode, signing is interactive.
  2. import/export — op-build is run once to generate the signing requests (blobs to be signed); archives containing these blobs (prefix header or firmware header) are exported from op-build and received by the signer; the signer signs and returns (an archive containing) the signatures; the signatures are imported back to op-build for a final run that creates a completed container.
  3. ad-hoc — Similar to import/export: but rather than receiving a blob exported from op-build, the signer generates the blob to be signed directly, using signtool, during the signing operation. This is supported for HW key signing only (since FW key signing is tied to the image produced by op-build). The signatures are imported back to op-build for a final run that creates a completed container.
For POWER firmware that does not use op-build:
  1. simple — The payload is signed directly, by the FW key signer(s), using signtool stand-alone. The HW key signatures are imported.
Production signing for OpenPOWER uses ad-hoc signing for HW key signers, and in-situ signing for FW key signers.

Production signing for other POWER firmware uses ad-hoc signing for HW key signers, and simple signing for FW key signers

Assumptions and limitations

The process here makes the following assumptions:

  • Separation of authority between HW key signers and FW key signers.
  • Separation of duties for HW key signers: *up to* three independent signers for HW keys A,B,C.
  • No separation of duties for SW key signers, or only one SW key (P) in use.1
  • No separation of authority between firmware components (all components signed by the same FW key) 2 ,3
Notes:

1. Due to the signtool limitation of supporting only a single signer in the INI. This could be fixed by adding support for multiple signers, say: one section per signer. Then signtool would prompt the user for userid prior to calling signframework, look up that signer in the INI to find the credentials, then call sf_client with those credentials.

2. Due pretty much due to the same limitation as 1, and can be fixed with 1 plus some granularity in the INI file properties to permit different signing projects (as defined by the signframework) to be used for different components.

3. This limitation does not apply to firmware built outside of op-build, where images are signed individually.

HW key signing — general instructions

The HW key signer typically runs signtool stand-alone, outside of op-build. The signer creates a signature for any (and all) HW keys A,B,C for which the signer has authority. If separation of duties is in force, there are up to three signers. Signtool exports an archive file containing any HW signature(s) A,B,C generated by that signer. The HW key signer then delivers the archive to the FW key signer, who will import these signatures to the FW signing process.

Ensuring a correct Prefix header

As mentioned, the HW key signature is a signature over the prefix header. The prefix header contains (a hash of) the public FW keys, but it also contains other data fields to be authorized by the HW key signer. When signing under op-build, the prefix header is constructed automatically. When signing stand-alone however, the signing operation must create a prefix header identical to the one created under op-build. To do this, the signer must pass all required options completely and correctly to signtool, so that signtool can properly construct the prefix header. If the prefix header created here does not match the one created under op-build, the resulting signatures will be unusable for completed container.

The following prefix header fields are affected by the stand-alone signing process and so must be set correctly. The table lists the fields and the action to ensure correctness:

Field Action for correctness
flags Ensure proper value to --flags command-line option
FW keys P,Q,R Ensure proper values to [signer] and [server] sections of the INI file Ensure proper value(s) to command-line options -p,-q,-r, governing the keys

Table 7: Actions to ensure correctness of Prefix Header

Obtaining the necessary keys and signatures from signframework

In production mode, signtool interacts with signframework to retrieve the artifacts (keys and signatures) required to build the container. The signer controls which artifacts are retrieved by setting the command-line options governing the keys: --hwKeyA, --hwKeyB, --hwKeyC, --swKeyP, --swKeyQ, --swKeyR; or simply: -a, -b, -c, -p, -q, -r. The signer passes one of the following special values, or keywords, to the option:

Keyword Behavior by signframework
__get Get both the (public) key and signature from the signframework.
__getsig Get only the signature for this key. Do not attempt to get the public key.
__getkey Get only the public key for this key. Do not attempt to get a signature.
__skip Do not attempt to get a key or signature for this key.
< no option provided > Same as "__skip"

Table 8: Special values passed to signtool key options

For FW keys P,Q,R: The HW key signer must retrieve the public keys for any and all FW keys P,Q,R that will be used for production. (These keys must be present in the Prefix header to produce a valid signature.) The HW signer uses the "__getkey" keyword for any key -p, -q, -r required for production; this instructs signtool to retrieve the key and add it to the prefix header. For any key -p, -q, -r not in use for production, the signer uses the "__skip" keyword, or simply omits the option.

For HW keys A,B,C: The HW key signer must retrieve a signature by any and all HW keys A,B,C for which the signer has authority. The signer uses the "__get" keyword for any key -a, -b, -c to request a signature and copy of the public key, for that key. (The signer may optionally use "__getsig" instead; this instructs the framework to retrieve only the signature and not the public key. In this case, however, the signtool will be unable to perform the validation of the signature just created. For this reason it is recommended to use "__get"). For any key -a, b, -c over which the signer has no authority, the signer uses the "__skip" keyword, or simply omits the option.

With the above options, signtool will retrieve the necessary FW keys P,Q,R, build the Prefix header, and request the appropriate signatures by HW keys A,B,C. Any artifacts obtained from signframework will be added to the archive specified by --archiveOut.

Creating an archive for export

The HW key signer uses --archiveOut to specify a file or directory to write the archive file. The file is is a gzipped, tar archive of artifacts retrieved from the signframework.

The value for --archiveOut may be a full path or relative path. If the value starts with '/' it is interpreted as a full path. Otherwise, it is interpreted as a path relative the current directory.

The value for --archiveOut may be a directory or a file. If the value ends with '/' it is interpreted as a directory. Otherwise, it is interpreted as a filename. To have signtool write the default filename to the current directory, use '.' (i.e. --archiveOut .). If --archiveOut is omitted, no file is written.

If the value provided is a directory, signtool will write a default filename of the form "signtool_<ID>.tgz", where <ID> is a unique identifier for this signing operation. If the value is a file, signtool will write the archive to the filename specified. In his case it is suggested to add the extension ".tar.gz" or ".tgz", as signtool adds no default extension. The preferred usage is to specify a directory and let signtool create the filename.

HW key signing — steps for stand-alone signing

  1. Install the signtool (sb-signing-utils) and signframework (sb-signing-framework) packages.
  2. Make sure the installed executables are in the search PATH. For example:

    $ PATH=/usr/local/bin:$PATH
    
  3. Create a project INI file with the appropriate values for the HW key signer's signframework account and credentials, and the address of the signing server.
  4. Run the signtool per the following examples:

    Example 1: HW key signer has authority over all three HW keys A,B,C. FW keys P,Q are in use. The archive containing HW key signatures A,B,C, will be written to /tmp/.

    $ crtSignedContainer.sh \
      --mode production \
      --sign-project-config /tmp/project.ini \
      --flags 0x80000000 \
      --hwKeyA __get \
      --hwKeyB __get \
      --hwKeyC __get \
      --swKeyP __getkey \
      --swKeyQ __getkey \
      --archiveOut /tmp/
    
    Example 2: HW key signer has authority over only HW key A. Only FW key P is in use. The archive containing HW key signature A will be written to /tmp/.
    $ crtSignedContainer.sh \
      --mode production \
      --sign-project-config /tmp/project.ini \
      --flags 0x80000000 \
      --hwKeyA __get \
      --swKeyP __getkey \
      --archiveOut /tmp/
    
  5. Deliver the archive file created by --archiveOut to the FW key signer(s).
  6. If a single signer has authority over all three HW keys, the single export archive should contain all three signatures. If each key is under the authority of a separate signer, there will in fact be three archives produced, each containing a single signature, and all three must be delivered to the FW key signer(s).

Notes:

  • The file generated by --archiveOut contains no private or privileged information. It contains only the public information that will added to the firmware container header, to be shipped with the firmware.
  • It is not necessary to specify an input payload for HW key signing, as the payload is not required for HW signature generation. The signer uses --protectedPayload __none to specify no input file provided, or simply omits the option.
  • It is not necessary to specify an output container for HW key signing, as this is not the step that will produce the completed container . The signer uses --out __none to specify no output file be generated, or simply omits the option.

How FW keys are authorized

This section may be skipped for brevity.

The Firmware (FW) key signer asserts the validity of one or more firmware modules that will run on the machine. A single signer (or group of signers) may have authority over all firmware modules, or, each module may be under the authority of a different signer. In each case, the HW key signers must authorize all FW keys in use. So, while individual containers may be signed by different FW keys (or set of keys), all containers will be signed by the same set of HW keys.

There are three FW keys in the set: P,Q,R. Unlike the HW keys, it is possible to use only one or two FW keys with secure boot. However, the number of FW keys used must exactly match what is signed by the HW keys. In other words, if the HW key signers have authorized a set of n FW keys P,Q,R, where n = 1-3, signatures by n FW keys must be present in the container. There is no such restriction between groups of FW signers, however. If the HW key signers authorize more than one group, one group may employ keys P,Q,R, while another group employs only P, and so on.

The FW key signature is a signature over the firmware header section of the container. The firmware header holds the hash of the container payload. By signing the firmware header, the FW key signer asserts that they trust this payload. With the trust placed in the firmware key signer by the HW key signer, this forms the chain of trust that allows a firmware component to be validated. Neither a firmware signer nor a hardware signer can go rogue or be rubber hosed into signing something, they both have to.

FW key signing — general instructions

The FW key signer may perform the signing under op-build (OpenPOWER firmware) or stand-alone (other POWER firmware). In either case, the FW key signer will import artifacts provided by the HW signer(s), and generate or retrieve some new artifacts from the signframework.

Importing an archive

When the operation is done under op-build the signer must use the SB_ARCHIVE_IN environment variable to specify one or more archives to import. When the operation is done stand-alone, the FW key signer may use the --archiveIn command-line option or may use SB_ARCHIVE_IN. If more than one archive is to be imported, the signer may specify a comma-delimited list.

Obtaining the necessary keys and signatures from signframework

The FW signer must ensure that all the necessary artifacts are retrieved (or imported) to complete the container. When the operation is done stand-alone, the FW signer controls which artifacts are retrieved via the command-line options -a, -b, -c, -p, -q, -r, using the keywords described previously:

For FW keys P,Q,R: The FW key signer must retrieve the public keys and signatures for any and all FW keys P,Q,R in use for production. The FW signer uses the "__get" keyword for any option -p, -q, -r, to instruct signtool to retrieve both the public key and the signature. For any key -p, -q, -r not in use, the signer uses the "__skip" keyword, or simply omits the option.

For HW keys A,B,C: The FW key signer must import a signature, and import or retrieve a public key, for all three HW keys A,B,C. The signer uses the "__getkey" keyword for any key -a, -b, -c to request the public key from the framework. A public key may also be imported from an archive file.

When the operation is done under op-build, the retrieval of artifacts is handled automatically. op-build tells signtool which artifacts to retrieve from the signframework.

FW key signing — steps for stand-alone signing

  1. Install the signtool (sb-signing-utils) and signframework (sb-signing-framework) packages.
  2. Make sure the installed executables are in the search PATH. For example:

    $ PATH=/usr/local/bin:$PATH
    
  3. Create a project INI file with the appropriate values for the SW key signer's signframework account and credentials, and the address of the signing server.
  4. Run the signtool as follows. In this example, only FW key P is in use, and each of the HW key signers A,B,C have provided an archive to import:

    $ crtSignedContainer.sh \
      --mode production \
      --sign-project-config /tmp/project.ini \
      --flags 0x80000000 \
      --hwKeyA __getkey \
      --hwKeyB __getkey \
      --hwKeyC __getkey \
      --swKeyP __get \
      --protectedPayload /path/to/payload/to/sign \
      --out /path/to/secure-container \
      --archiveIn “/path/to/import/fileA.tgz, /path/to/import/fileB.tgz, /path/to/import/fileC.tgz”
    

The file specified by --out should now contain a valid, verifiable secure container with payload attached.

FW key signing — steps for signing under op-build

When signing under op-build, op-build automatically runs signtool when the PNOR image is generated, and op-build provides most of the required configuration properties to signtool automatically. Properties pertaining to the signer's signframework account and credentials must be provided by environment or INI.

When signing under op-build, FW key signer(s) must interactively enter a password for each artifact requested from the signframework. Because the op-build process is rather long (up to 1hr or more), and because the signing operation falls at the end of the build, it is recommended to run op-build once with default settings and allow it to complete. This builds a PNOR with containers signed in development mode, using the default Imprint keys.

Once this is done, the signer may run openpower-pnor-rebuild to rebuild the PNOR in production mode. This step is fast since it uses the existing binaries. The signing operations will be invoked during this step, and signers may enter their password directly. This is more convenient, and minimizes the chance a terminal window will arrive at a password prompt unattended. It also provides the flexibility of allowing the op-build to be performed via some standard process (e.g. an automated test harness), and require user interaction only for the signing part.

To sign under op-build:

  1. Create a project INI file with the appropriate values for the FW key signer's account and credentials.
  2. Set the path to the INI via the platform _defconfig, or in the shell environment. (Use quotes if the path contains spaces):

    $ export SB_PROJECT_INI=/path/to/project.ini
    
  3. Specify the archive file(s) to import:

    $ export SB_ARCHIVE_IN="/path/to/import/fileA.tgz, /path/to/import/fileB.tgz, /path/to/import/fileC.tgz"
    
  4. From a command prompt within the op-build environment, cd to the root of the op-build project and run the following:

    $ cd /path/to/op-build/
    $ source op-build-env
    $ op-build witherspoon_defconfig
    
  5. Run the openpower-pnor-rebuild command to rebuild the PNOR. This will rebuild the PNOR image using production keys instead of development keys:

    $ op-build BR2_OPENPOWER_SECUREBOOT_SIGN_MODE=production \
      openpower-pnor-rebuild
    
  6. When the signtool needs to interact with the signing server it will invoke the signframework client; at these points, the process will pause to allow the signer to enter a password. The password is used to unlock the user's encrypted ssh key.
  7. The signtool will first attempt to retrieve the HW public keys A,B,C and FW public keys P,Q,R (if not already imported). The FW key signer will have to enter a password for each request. The FW key signer should have privilege to retrieve any public key, so the FW signer password should be sufficient for this request.
  8. op-build will build each container in turn, prompting for passwords as required. Signtool reuses as many artifacts as it can from container to container. In most cases, once the first container is complete, the only artifacts required for the remaining containers will be the FW key signatures P,Q,R (which are unique to each container and cannot be reused).
  9. Once all the containers are complete, op-build will rebuild the PNOR image and place it in the output directory. You should now have a secure-bootable image in ./output/images/.

Signing in Independent mode

Independent signing decouples the signing operation from the firmware build operation under op-build. The process works like this:

  1. op-build is run once to generate the signing requests (prefix header or firmware header to be signed); archives containing these blobs are exported from op-build and delivered to the signer, or signing process.
  2. The blobs are signed, and the signatures returned to op-build.
  3. The signatures are imported back to op-build for a final run that creates a completed container.
This allows signing to be done independently of op-build, preferably on a machine other than the op-build machine (although that is not required). If a separate system is used, there will be no private or privileged information to be exposed at the op-build machine. Contrast this to local mode where the private signing keys are exposed to op-build, and production mode where the (encrypted) private ssh keys are exposed.

This makes independent mode an attractive choice for environments where the op-build machine is not sufficiently secure for signing, or simply not purposed for signing. And it is the most flexible, since there is really no restriction on where, or in what sequence, the signing must be done. Each HW key signer or FW key signer could sign on a different system, at his/her convenience.

Independent mode is also intended to provide a way for the signing organization to employ a "method of choice" for signing, i.e. any method capable of generating a ECDSA p521 signature. (The signframework interface is an example of an alternate signing method, although the current implementation only supports signframework in production mode.) Signtool's default signing method for independent mode is openssl, and there is currently no facility to specify a different method (although this is a possible future enhancement for signtool).

Adding your public keys

By default, op-build signs with the Imprint keys stored in ./openpower/package/sb-signing-utils/keys/. To sign in independent mode you will replace the default keys with your own (public) keys.

Here is an example of how to generate your own keys using the openssl ecparam command. This may be done on a system other than the op-build machine:

$ openssl ecparam -genkey -outform pem -noout -name secp521r1 -out hw_key_a.priv
$ openssl ecparam -genkey -outform pem -noout -name secp521r1 -out hw_key_b.priv
$ openssl ecparam -genkey -outform pem -noout -name secp521r1 -out hw_key_c.priv
$ openssl ecparam -genkey -outform pem -noout -name secp521r1 -out sw_key_p.priv

As explained previously, the user should project the private key(s). If separation of duties is in force, each signer will create their own private key.

To extract the public key from the private key:

$ openssl pkey -pubout -inform pem -outform pem -in hw_key_a.priv -out hw_key_a.pub
$ openssl pkey -pubout -inform pem -outform pem -in hw_key_b.priv -out hw_key_b.pub
$ openssl pkey -pubout -inform pem -outform pem -in hw_key_c.priv -out hw_key_c.pub
$ openssl pkey -pubout -inform pem -outform pem -in sw_key_p.priv -out sw_key_p.pub

At the op-build machine, replace the default keys stored in ./openpower/package/sb-signing-utils/keys/ with your own (public) keys. Note the different file extension; the default keys are named *.key:

$ cd ./openpower/package/sb-signing-utils/keys/
$ cp /path/to/my/public/hw_key_a.pub hw_key_a.key
$ cp /path/to/my/public/hw_key_b.pub hw_key_b.key
$ cp /path/to/my/public/hw_key_c.pub hw_key_c.key
$ cp /path/to/my/public/sw_key_p.pub sw_key_p.key

(Note this assumes you replace the keys before running the full op-build. If you want to update the keys after running op-build — to rebuild the PNOR, for example — you must perform an additional step. See section: Signing with your own keys, for details).

It is important that all the public keys be installed under build, else the container built by op-build will be incorrect.

Now use the hashkeys utility to update the HW keys hash file:

$ hashkeys -a hw_key_a.key -b hw_key_b.key -c hw_key_c.key --outfile hw_keys_hash.md

Step 1 — Run op-build to generate signing requests

In this step you will create the signing requests to be deliver to the signers. Only the public keys are available to op-build, so no signatures are generated here.

  1. Select a directory to output the signing requests:

    $ mkdir /tmp/archive.requested/
    
  2. Optionally enable validation and verification. Containers will fail validation as the required signatures are not yet available. But they will pass verification, since verification depends only on the public keys A,B,C, and this is a useful check.

    $ export SB_VERIFY=$HOME/op-build/openpower/package/sb-signing-utils/keys/hw_keys_hash.md
    

    If validation is enabled, so must be SB_PASS_ON_ERROR:

    $ export SB_VALIDATE=y
    $ export SB_PASS_ON_ERROR=y
    
  3. There are no archives to import; export archives go to the selected directory:

    $ unset SB_ARCHIVE_IN
    $ export SB_ARCHIVE_OUT=/tmp/archive.requested/
    
  4. Now run op-build in independent mode:

    $ op-build BR2_OPENPOWER_SECUREBOOT_SIGN_MODE=independent
    

    If op-build has already been run, you may run just the final PNOR rebuild:

    $ op-build BR2_OPENPOWER_SECUREBOOT_SIGN_MODE=independent \
               openpower-pnor-rebuild
    
  5. When signtool runs, it will create the signing requests in the selected directory:

    $ tree /tmp/archive.requested/
    /tmp/archive.requested/
    |-- signtool_1517084120_BOOTKERN.tgz
    |-- signtool_1517084120_CAPP.tgz
    |-- signtool_1517084120_HBBL.tgz
    |-- signtool_1517084120_HBB.tgz
    |-- signtool_1517084120_HBD.tgz
    |-- signtool_1517084120_HBI.tgz
    |-- signtool_1517084120_HBRT.tgz
    |-- signtool_1517084120_HCODE.tgz
    |-- signtool_1517084120_HDAT.tgz
    |-- signtool_1517084120_IMA_CATA.tgz
    |-- signtool_1517084120_MEMD.tgz
    |-- signtool_1517084120_OCC.tgz
    |-- signtool_1517084120_PAYLOAD.tgz
    |-- signtool_1517084120_SBE.tgz
    `-- signtool_1517084120_WOFDATA.tgz
    
  6. The signtool output under op-build looks like this:

    $ TRACE: ./output/host/usr/bin//crtSignedContainer.sh --scratchDir ./output/host/powerpc64le-buildroot-linux-gnu/sysroot/openpower_pnor_scratch/ --mode independent
    --hwKeyA ./output/host/etc/keys//hw_key_a.pub
    --hwKeyB ./output/host/etc/keys//hw_key_b.pub
    --hwKeyC ./output/host/etc/keys//hw_key_c.pub
    --swKeyP ./output/host/etc/keys//sw_key_p.pub
    --flags 0x80080000 --sign-project-FW-token SBE
    --protectedPayload ./output/host/powerpc64le-buildroot-linux-gnu/sysroot/openpower_pnor_scratch//SBE.staged
    --out ./output/host/powerpc64le-buildroot-linux-gnu/sysroot/openpower_pnor_scratch//rand-1814807436.SBE.temp.hdr.bin
    
    --> crtSignedContainer.sh: Signing mode: independent
    --> crtSignedContainer.sh: Using existing cache dir: ./output/host/powerpc64le-buildroot-linux-gnu/sysroot/openpower_pnor_scratch//SIGNTOOL_1517084120, created: Sat Jan 27 15
    --> crtSignedContainer.sh: Creating new cache subdir: ./output/host/powerpc64le-buildroot-linux-gnu/sysroot/openpower_pnor_scratch//SIGNTOOL_1517084120/SBE
    --> crtSignedContainer.sh: Generating signing requests...
    --> crtSignedContainer.sh: No signature found and no private key available for HW key A, skipping.
    --> crtSignedContainer.sh: No signature found and no private key available for HW key B, skipping.
    --> crtSignedContainer.sh: No signature found and no private key available for HW key C, skipping.
    --> crtSignedContainer.sh: No signature found and no private key available for SW key P, skipping.
    --> crtSignedContainer.sh: No signatures available.
    --> crtSignedContainer.sh: Container SBE build completed.
    --> crtSignedContainer.sh: Archive saved to: /tmp/archive.requested/signtool_1517068898_SBE.tgz
    
    Container validity check FAILED. Container verification check PASSED.
    

Step 2 — Create the signatures

HW key signing — general instructions

The HW key signer runs signtool stand-alone, using ad-hoc signing, in a manner similar to production mode.

The signer creates a signature for any (and all) HW keys A,B,C for which the signer has authority. If separation of duties is in force, there are up to three signers. Signtool exports an archive file containing any HW signature(s) A,B,C generated by that signer. The HW key signer then delivers the archive to the FW key signer, who will import these signatures to the FW signing process.

Ensuring a correct Prefix header

See previous section from Production signing: Ensuring a correct Prefix header

HW key signing — steps for stand-alone signing

  1. Install the signtool (sb-signing-utils) and signframework (sb-signing-framework) packages.
  2. Make sure the installed executables are in the search PATH. For example:

    $ PATH=/usr/local/bin:$PATH
    
  3. Run the signtool per the following examples:

    Example 1: HW key signer has authority over all three HW keys A,B,C; the signer must supply the private keys for all. FW keys P,Q are in use; the signer must supply the public keys for P and Q. The archive containing HW key sigs A,B,C, will be written to the file specified.

    $ crtSignedContainer.sh \
      --mode independent \
      --flags 0x80000000 \
      --hwKeyA /tmp/keys/hw_key_a.key \
      --hwKeyB /tmp/keys/hw_key_b.key \
      --hwKeyC /tmp/keys/hw_key_c.key \
      --swKeyP /tmp/keys/sw_key_p.pub \
      --swKeyQ /tmp/keys/sw_key_q.pub \
      --archiveOut /tmp/archive.signed/signtool_HW_key_ABC_sig.tgz
    

    Example 2: HW key signer has authority over only HW key A; the signer must supply the private key for A. Only FW key P is in use; the signer must supply the public key for P. The archive containing HW key signature A will be written to the file specified.

    $ crtSignedContainer.sh \
      --mode independent \
      --flags 0x80000000 \
      --hwKeyA /tmp/keys/hw_key_a.key \
      --swKeyP /tmp/keys/sw_key_p.pub \
      --archiveOut /tmp/archive.signed/signtool_HW_key_A_sig.tgz
    
  4. Deliver the archive file created by --archiveOut to the FW key signer(s).

  5. If a single signer has authority over all three HW keys, the single export archive should contain all three signatures. If each key is under the authority of a separate signer, there will in fact be three archives produced, each containing a single signature, and all three must be delivered to the FW key signer(s).

Notes:

  • The file generated by --archiveOut contains no private or privileged information. It contains only the public information that will added to the firmware container header, to be shipped with the firmware.
  • It is not necessary to specify an input payload for HW key signing, as the payload is not required for HW signature generation. The signer uses --protectedPayload __none to specify no input file provided, or simply omits the option.
  • It is not necessary to specify an output container for HW key signing, as this is not the step that will produce the completed container . The signer uses --out __none to specify no output file be generated, or simply omits the option.

FW key signing — general instructions

The FW key signers also run signtool stand-alone. But because the FW key signature is a signatures over the image payload, it is difficult to use ad-hoc signing. (It could be done, but would require import of the entire payload, or a signtool option to enter the payload hash directly.) For this reason, FW key signing uses signing request archives exported in step 1.

The archives contain the completed firmware header to be signed. The firmware header already contains the correct payload hash (and other important fields), so there is little else that needs to be supplied to the signtool command line, other than the paths to keys.

If each FW image (HBB, SBE, BOOTKERN, etc.) is under the authority of a different signer, the appropriate signing request must be used as input. If all images are under the authority of the same signer, they may be signed in bulk. In this simple example, all images are under the authority of a single signer P. The bulkSign.sh utility will sign all images at once (or generally, however many images are under the authority of a single signer or group).

For the following example, it is assumed all signing request archives from the Step 1 op-build run have been copied to the current system, in a directory by the name:

$ tree /tmp/archive.requested/
/tmp/archive.requested/
|-- signtool_1517084120_BOOTKERN.tgz
|-- signtool_1517084120_CAPP.tgz
|-- signtool_1517084120_HBBL.tgz
|-- signtool_1517084120_HBB.tgz
|-- signtool_1517084120_HBD.tgz
|-- signtool_1517084120_HBI.tgz
|-- signtool_1517084120_HBRT.tgz
|-- signtool_1517084120_HCODE.tgz
|-- signtool_1517084120_HDAT.tgz
|-- signtool_1517084120_IMA_CATA.tgz
|-- signtool_1517084120_MEMD.tgz
|-- signtool_1517084120_OCC.tgz
|-- signtool_1517084120_PAYLOAD.tgz
|-- signtool_1517084120_SBE.tgz
`-- signtool_1517084120_WOFDATA.tgz

FW key signing — steps for stand-alone signing

  1. Install the signtool (sb-signing-utils) and signframework (sb-signing-framework) packages.
  2. Make sure the installed executables are in the search PATH. For example:

    $ PATH=/usr/local/bin:$PATH
    
  3. Run the signing operation using the bulkSign.sh utility. This will sign, using whatever private keys are provided, every archive in the directory specified by --archiveIn. The --archiveOut points to the selected output directory (which must exist).

    $ bulkSign.sh -p /tmp/keys/sw_key_p.key \
      --archiveIn /tmp/archive.requested/ \
      --archiveOut /tmp/archive.signed/
    

    Here is the output, one stanza per file:

    Handling signing request "signtool_1517086312_BOOTKERN.tgz" with label: BOOTKERN
    --> crtSignedContainer.sh: Signing mode: independent
    --> crtSignedContainer.sh: Creating new cache dir: /tmp/SIGNTOOL_1517089956
    --> crtSignedContainer.sh: Creating new cache subdir: /tmp/SIGNTOOL_1517089956/BOOTKERN
    --> crtSignedContainer.sh: Importing archive: signtool_1517086312_BOOTKERN.tgz...
    --> crtSignedContainer.sh: Attempting to re-use existing signing requests...
    --> crtSignedContainer.sh: Generating signature for SW key P...
    --> crtSignedContainer.sh: Have signatures for keys P, adding to container...
    --> crtSignedContainer.sh: Container BOOTKERN build completed.
    --> crtSignedContainer.sh: Archive saved to: /tmp/archive.signed/signtool_1517089956_BOOTKERN.tgz
    
    Handling signing request "signtool_1517086312_CAPP.tgz" with label: CAPP
    --> crtSignedContainer.sh: Signing mode: independent
    --> crtSignedContainer.sh: Using existing cache dir: /tmp/SIGNTOOL_1517089956, created: Sat Jan 27 16:52:36 EST 2018
    --> crtSignedContainer.sh: Creating new cache subdir: /tmp/SIGNTOOL_1517089956/CAPP
    --> crtSignedContainer.sh: Importing archive: signtool_1517086312_CAPP.tgz...
    --> crtSignedContainer.sh: Attempting to re-use existing signing requests...
    --> crtSignedContainer.sh: Generating signature for SW key P...
    --> crtSignedContainer.sh: Have signatures for keys P, adding to container...
    --> crtSignedContainer.sh: Container CAPP build completed.
    --> crtSignedContainer.sh: Archive saved to: /tmp/archive.signed/signtool_1517089956_CAPP.tgz
    ...
    
    Handling signing request "signtool_1517086312_WOFDATA.tgz" with label: WOFDATA
    --> crtSignedContainer.sh: Signing mode: independent
    --> crtSignedContainer.sh: Using existing cache dir: /tmp/SIGNTOOL_1517089956, created: Sat Jan 27 16:52:36 EST 2018
    --> crtSignedContainer.sh: Creating new cache subdir: /tmp/SIGNTOOL_1517089956/WOFDATA
    --> crtSignedContainer.sh: Importing archive: signtool_1517086312_WOFDATA.tgz...
    --> crtSignedContainer.sh: Attempting to re-use existing signing requests...
    --> crtSignedContainer.sh: Generating signature for SW key P...
    --> crtSignedContainer.sh: Have signatures for keys P, adding to container...
    --> crtSignedContainer.sh: Container WOFDATA build completed.
    --> crtSignedContainer.sh: Archive saved to: /tmp/archive.signed/signtool_1517089956_WOFDATA.tgz
    
  4. The directory specified by --archiveOut should now contain an export archive for every signing request that was input. The archives contain the FW key signatures:

    $ tree /tmp/archive.signed /
    /tmp/archive.signed/
    |-- signtool_1517089956_BOOTKERN.tgz
    |-- signtool_1517089956_CAPP.tgz
    |-- signtool_1517089956_HBBL.tgz
    |-- signtool_1517089956_HBB.tgz
    |-- signtool_1517089956_HBD.tgz
    |-- signtool_1517089956_HBI.tgz
    |-- signtool_1517089956_HBRT.tgz
    |-- signtool_1517089956_HCODE.tgz
    |-- signtool_1517089956_HDAT.tgz
    |-- signtool_1517089956_IMA_CATA.tgz
    |-- signtool_1517089956_MEMD.tgz
    |-- signtool_1517089956_OCC.tgz
    |-- signtool_1517089956_PAYLOAD.tgz
    |-- signtool_1517089956_SBE.tgz
    `-- signtool_1517089956_WOFDATA.tgz
    

Step 3 — Import signatures into op-build

The signatures from the signing operations in step 2 are returned to op-build for the final container creation. The signatures are contained in the archive files. There will be up to three archives containing the HW key signatures, depending on how many independent signers A,B,C. There will be up to three archives per firmware image, depending on how many independent signers P,Q,R.

  1. For simplicity we'll assume the archives are copied back to the op-build machine to a directory with the same name:

    $ tree /tmp/archive.signed /
    /tmp/archive.signed/
    |-- signtool_1517089956_BOOTKERN.tgz
    |-- signtool_1517089956_CAPP.tgz
    |-- signtool_1517089956_HBBL.tgz
    |-- signtool_1517089956_HBB.tgz
    |-- signtool_1517089956_HBD.tgz
    |-- signtool_1517089956_HBI.tgz
    |-- signtool_1517089956_HBRT.tgz
    |-- signtool_1517089956_HCODE.tgz
    |-- signtool_1517089956_HDAT.tgz
    |-- signtool_1517089956_IMA_CATA.tgz
    |-- signtool_1517089956_MEMD.tgz
    |-- signtool_1517089956_OCC.tgz
    |-- signtool_1517089956_PAYLOAD.tgz
    |-- signtool_1517089956_SBE.tgz
    |-- signtool_1517089956_WOFDATA.tgz
    |-- signtool_HW_key_A_sig.tgz
    |-- signtool_HW_key_B_sig.tgz
    `-- signtool_HW_key_C_sig.tgz
    
  2. All the above archives are to be imported. The following a shell snippet will set SB_ARCHIVE_IN to a comma-delimited list of all files in the directory:

    $ export SB_ARCHIVE_IN=$(find /tmp/archive.signed/ -name \*.tgz -exec echo -n "{}, " \; | sed 's/, $//')
    $ echo $SB_ARCHIVE_IN
    /tmp/archive.signed/signtool_1517089956_WOFDATA.tgz, /tmp/archive.signed/signtool_1517089956_SBE.tgz, /tmp/archive.signed/signtool_1517089956_PAYLOAD.tgz, /tmp/archive.signed/signtool_1517089956_OCC.tgz, /tmp/archive.signed/signtool_1517089956_MEMD.tgz, /tmp/archive.signed/signtool_1517089956_IMA_CATA.tgz, /tmp/archive.signed/signtool_1517089956_HDAT.tgz, /tmp/archive.signed/signtool_1517089956_HCODE.tgz, /tmp/archive.signed/signtool_1517089956_HBRT.tgz, /tmp/archive.signed/signtool_1517089956_HBI.tgz, /tmp/archive.signed/signtool_1517089956_HBD.tgz, /tmp/archive.signed/signtool_1517089956_HBB.tgz, /tmp/archive.signed/signtool_1517089956_HBBL.tgz, /tmp/archive.signed/signtool_1517089956_CAPP.tgz, /tmp/archive.signed/signtool_1517089956_BOOTKERN.tgz, /tmp/archive.signed/signtool_HW_key_C_sig.tgz, /tmp/archive.signed/signtool_HW_key_B_sig.tgz, /tmp/archive.signed/signtool_HW_key_A_sig.tgz
    
  3. There are no archives to export, so:

    $ unset SB_ARCHIVE_OUT
    
  4. Now run op-build in independent mode:

    $ op-build BR2_OPENPOWER_SECUREBOOT_SIGN_MODE=independent
    

    If op-build has already been run, you may run just the final PNOR rebuild:

    $ op-build BR2_OPENPOWER_SECUREBOOT_SIGN_MODE=independent \
               openpower-pnor-rebuild
    
  5. The signtool output under op-build looks like this:

    $ TRACE: ./output/host/usr/bin//crtSignedContainer.sh --scratchDir ./output/host/powerpc64le-buildroot-linux-gnu/sysroot/openpower_pnor_scratch/ --mode independent
    --hwKeyA ./output/host/etc/keys//hw_key_a.pub
    --hwKeyB ./output/host/etc/keys//hw_key_b.pub
    --hwKeyC ./output/host/etc/keys//hw_key_c.pub
    --swKeyP ./output/host/etc/keys//sw_key_p.pub
    --flags 0x80080000 --sign-project-FW-token SBE
    --protectedPayload ./output/host/powerpc64le-buildroot-linux-gnu/sysroot/openpower_pnor_scratch//SBE.staged
    --out ./output/host/powerpc64le-buildroot-linux-gnu/sysroot/openpower_pnor_scratch//rand-1814807436.SBE.temp.hdr.bin
    
    --> crtSignedContainer.sh: Signing mode: independent
    --> crtSignedContainer.sh: Using existing cache dir: ./output/host/powerpc64le-buildroot-linux-gnu/sysroot/openpower_pnor_scratch//SIGNTOOL_1517086446, created: Sat Jan 27 15:54:06 EST 2018
    --> crtSignedContainer.sh: Using existing cache subdir: ./output/host/powerpc64le-buildroot-linux-gnu/sysroot/openpower_pnor_scratch//SIGNTOOL_1517086446/HBD
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_WOFDATA.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_SBE.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_PAYLOAD.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_OCC.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_MEMD.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_IMA_CATA.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_HDAT.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_HCODE.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_HBRT.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_HBI.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_HBD.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_HBB.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_HBBL.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_CAPP.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_1517086396_BOOTKERN.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_HW_key_C_sig.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_HW_key_B_sig.tgz...
    --> crtSignedContainer.sh: Importing archive: /tmp/archive.signed/signtool_HW_key_A_sig.tgz...
    --> crtSignedContainer.sh: Attempting to re-use existing signing requests...
    --> crtSignedContainer.sh: Found signature for HW key A.
    --> crtSignedContainer.sh: Found signature for HW key B.
    --> crtSignedContainer.sh: Found signature for HW key C.
    --> crtSignedContainer.sh: Found signature for SW key P.
    --> crtSignedContainer.sh: Have signatures for keys A,B,C,P, adding to container...
    --> crtSignedContainer.sh: Container SBE build completed.
    
    Container validity check PASSED. Container verification check PASSED.
    
  6. All images should now pass validity check and verification check.

  7. op-build will place the completed PNOR image in the output directory. You should now have a secure-bootable image in ./output/images/.

Notes:

  • All files are imported for every module, due to a limitation in signtool's import function. This is not necessary but shouldn't cause any problems. It will be fixed in a future release of signtool.

Step 4 — Create the completed PNOR under op-build

In the previous operation, all component containers should pass validation and verification, except for HBI. This is because the HBI component has a dependency on a signature from a different module, HBB. (Part of the signature data from HBB is used as a seed for secure hashing operations in the build of HBI.) For this reason, you must re-sign HBI and import the new signature to op-build for an additional, final pass of openpower-pnor-rebuild.

To do this you must generate a new signing request for HBI, sign that request individually, and return the result to op-build. It's essentially the same sequence as the previous three steps, but the result is a new signature for only the one module.

Step 4.1 — Regenerate the signing request for HBI

Although the one is all you need, it's easier to regenerate all. This is the same as the original Step 1, with a few additional tasks. First, you must delete the HBI software key signature(s) P,Q,R from the signtool cache, to force signtool to regenerate them:

$ find ./output/host/ -type d -name HBI -exec find {} -type f -name SW_key_*.sig \; | xargs rm

Now delete all the signing requests from the original Step 1 (they are no longer needed):

$ rm /tmp/archive.requested/*

Now you may proceed as in the original Step 1. There are no archives to import, and the new export archives will go to the selected directory:

$ unset SB_ARCHIVE_IN
$ export SB_ARCHIVE_OUT=/tmp/archive.requested/

Now run op-build in independent mode:

$ op-build BR2_OPENPOWER_SECUREBOOT_SIGN_MODE=independent openpower-pnor-rebuild

When signtool runs, it will recreate all the signing requests and export them to: /tmp/archive.requested/. The only request of interest is for HBI.

Step 4.2 — Create the new signature

Now you must generate a new signature for HBI. As before, the request may be copied to different machine for signing. Since there is only one request to sign, it's easiest to just run crtSignedContainer.sh stand-alone. Assuming the same directory structure as before:

$ crtSignedContainer.sh -m independent -p /tmp/keys/sw_key_p.key --label HBI \
  --archiveIn /tmp/archive.requested/signtool_*_HBI.tgz
  --archiveOut /tmp/archive.signed/

Step 4.3 — Import the new signature back to op-build

As before, copy the new signature archive back to the op-build machine and place it in /tmp/archive.signed. Be sure to delete any previous signature archive for HBI, but only for HBI, when installing the new file. The contents of /tmp/archive.signed will be identical to before, except the HBI file will be newer. The contents of /tmp/archive.requested are no longer relevant.

Again run the shell snippet to set SB_ARCHIVE_IN to a comma-delimited list. The result is the same as before, except the HBI file will be newer:

$ export SB_ARCHIVE_IN=$(find /tmp/archive.signed/ -name \*.tgz -exec echo -n "{}, " \; | sed 's/, $//')
$ echo $SB_ARCHIVE_IN
/tmp/archive.signed/signtool_1517089956_WOFDATA.tgz, /tmp/archive.signed/signtool_1517089956_SBE.tgz, /tmp/archive.signed/signtool_1517089956_PAYLOAD.tgz, /tmp/archive.signed/signtool_1517089956_OCC.tgz, /tmp/archive.signed/signtool_1517089956_MEMD.tgz, /tmp/archive.signed/signtool_1517089956_IMA_CATA.tgz, /tmp/archive.signed/signtool_1517089956_HDAT.tgz, /tmp/archive.signed/signtool_1517089956_HCODE.tgz, /tmp/archive.signed/signtool_1517089956_HBRT.tgz, /tmp/archive.signed/signtool_1517093333_HBI.tgz, /tmp/archive.signed/signtool_1517089956_HBD.tgz, /tmp/archive.signed/signtool_1517089956_HBB.tgz, /tmp/archive.signed/signtool_1517089956_HBBL.tgz, /tmp/archive.signed/signtool_1517089956_CAPP.tgz, /tmp/archive.signed/signtool_1517089956_BOOTKERN.tgz, /tmp/archive.signed/signtool_HW_key_C_sig.tgz, /tmp/archive.signed/signtool_HW_key_B_sig.tgz, /tmp/archive.signed/signtool_HW_key_A_sig.tgz

There are no archives to export, so:

$ unset SB_ARCHIVE_OUT

If you previously set SB_PASS_ON_ERROR you may unset it now:

$ unset SB_PASS_ON_ERROR

Now run op-build one more time in independent mode:

$ op-build BR2_OPENPOWER_SECUREBOOT_SIGN_MODE=independent openpower-pnor-rebuild

All containers should now pass the VALIDATION and VERIFICATION checks, an you should have a secure-bootable image in ./output/images/.

Using VALIDATE and VERIFY

Signtool includes a built-in container validation and verification function that may be used when signtool stand-alone or under op-build. The operation is controlled by three configuration properties, SB_VALIDATE, SB_VERIFY and SB_PASS_ON_ERROR. These properties may be set by environment, command-line or INI.

The VALIDATE function emulates all intra-container checks normally done during secure-boot. This includes:

  • Validate all HW key signatures in the container, using the public keys from the container header.
  • Validate all FW key signatures in the container, using the public keys from the container header.
  • Validate the hashes of the protected regions of the container (FW public keys and image payload) to ensure they match the values in the signed portions of the container (Software and Prefix headers).
The VERIFY function checks the hash of the HW keys A,B,C in the container header against the value provided, emulating the check done at boot time to compare with the machine-installed hash in SEEPROM. If this check passes (and VALIDATE passes as well), the container should successfully secure-boot on a machine with this hash installed.

The PASS_ON_ERROR property determines the error handling. If true, and either SB_VALIDATE or SB_VERIFY is set, and an error is encountered on either (or both) operation(s), signtool will ignore the error. If false (the default), signtool will return a non-zero exit code; if running under op-build this will cause the current operation to abort. When running stand-alone, there is no change to signtool behavior besides the exit code. It is up to the user to check, and take possible action based on the exit code.

Generating the HW keys hash for with use with SB_VERIFY

The SB_VERIFY property holds the hash of the HW keys to compare to. The value should be the same as the value expected to be installed SEEPROM on the target machine on which the firmware is intended to boot.

In development mode, the value will be the hash of the IBM Imprint keys. To obtain this value, use the hashkeys utility to calculate the value from the Imprint keys installed under op-build.

$ cd openpower/package/sb-signing-utils/keys/
$ hashkeys -a hw_key_a.key -b hw_key_b.key -c hw_key_c.key
40d487ff7380ed6ad54775d5795fea0de2f541fea9db06b8466a42a320e65f75b48665460017d907515dc2a5f9fc50954d6ee0c9b67d219dfb7085351d01d6d1

In production mode, the value will be the hash of the HW keys retrieved from the signframework. To obtain the value, request the HW public keys A,B,C from the signframework and use hashkeys to calculate the hash:

In production mode, the value will be the hash of the HW keys retrieved from the signframework.  To obtain the value, request the HW public keys A,B,C from the signframework and use hashkeys to calculate the hash:
$ sf_client -project getpubkeyecc \
  -param "-signproject sign_ecc_pwr_hw_key_a" \
  -pkey $HOME/.ssh/id_rsa.sign -epwd $HOME/private/epwd.txt \
  -comments "Requesting sign_ecc_pwr_hw_key_a public key" \
  -url sftp://sf_user@[email protected] -o sf_hw_key_a.raw
$ sf_client -project getpubkeyecc \
  -param "-signproject sign_ecc_pwr_hw_key_b" \
  -pkey $HOME/.ssh/id_rsa.sign -epwd $HOME/private/epwd.txt \
  -comments "Requesting sign_ecc_pwr_hw_key_b public key" \
  -url sftp://sf_user@[email protected] -o sf_hw_key_b.raw
$ sf_client -project getpubkeyecc \
  -param "-signproject sign_ecc_pwr_hw_key_c" \
  -pkey $HOME/.ssh/id_rsa.sign -epwd $HOME/private/epwd.txt \
  -comments "Requesting sign_ecc_pwr_hw_key_c public key" \
  -url sftp://sf_user@[email protected] -o sf_hw_key_c.raw

$ hashkeys -a sf_hw_key_a.raw -b sf_hw_key_b.raw -c sf_hw_key_c.raw 
45d4a868decdac5f41d5fa78f60005c3f78eedf381cbdf1ea2415c133cd8919d19eca8d6dada672eb1abe08e81087562cd91ec9bbcda878c7e969a1dc2f20537

*** WARNING ***

It is IMPERATIVE to get the HW keys hash from an independent, trusted source. Always use keys obtained directly from the signframework to calculate the value to be used for verification. (See section: Threats and vulnerabilities for more details.)

*** WARNING ***

The value of SB_VERIFY may be the HW keys hash in hexascii, or the path to a file containing the hexascii string. To write the data directly to a file:

$ hashkeys -a hw_key_a.key -b hw_key_b.key -c hw_key_c.key --outfile hw_keys_hash.md

Then, either of the following values for SB_VERIFY will work:

export SB_VERIFY=\
40d487ff7380ed6ad54775d5795fea0de2f541fea9db06b8466a42a320e65f75b48665460017d907515dc2a5f9fc50954d6ee0c9b67d219dfb7085351d01d6d1

export SB_VERIFY=\
$HOME/openpower/package/sb-signing-utils/keys/hw_keys_hash.md 

Building transition PNOR images

The transition container is used to re-program, or re-imprint the HW keys hash in SEEPROM. The two types of transition PNOR image supported under op-build are the development-to-development transition PNOR, and the development-to-production transition PNOR.

Building a development-to-development transition PNOR

The development-to-development transition PNOR is used mainly for testing. It will re-imprint a SEEPROM value currently under the authority of the Imprint keys with a SEEPROM value under the authority of the same keys. In other words, it will update the existing HW keys hash in SEEPROM with the identical value. This is effectively a no-op, since it ultimately results in no configuration change. The operation will occur however, if the development-to-development transaction container is booted on a machine in secure mode.

The development-to-development transition PNOR is useful for testing the transition container build under op-build, and for testing the key transition process on the hardware.

To build this image:

  1. The operation is similar to building in local mode. Follow the local mode instructions to clone the op-build project and select the machine config.
  2. Optionally enable validation and verification. All containers should pass both tests.
    $ export SB_VALIDATE=y
    $ export SB_VERIFY=$HOME/op-build/openpower/package/sb-signing-utils/keys/hw_keys_hash.md
    
  3. There are no archives to import or export:
    $ unset SB_ARCHIVE_IN SB_ARCHIVE_OUT
  4. Run op-build as follows:
    $ op-build BR2_OPENPOWER_SECUREBOOT_KEY_TRANSITION_TO_DEV=y
    
    If op-build has already been run, you may run just the final PNOR rebuild:
    $ op-build BR2_OPENPOWER_SECUREBOOT_KEY_TRANSITION_TO_DEV=y \
               openpower-pnor-rebuild
    
  5. You should now have a completed transition PNOR in ./output/images/.

Building a development-to-production transition PNOR

The development-to-production transition PNOR will re-imprint a SEEPROM value currently under the authority of the Imprint keys with a SEEPROM value under the authority of the Production keys. In other words, it will update the HW keys hash in SEEPROM with a new value, which is the HW keys hash of the new production keys. This done by the ODM vendor, for example, to establish authority over machines to be branded or resold by the vendor.

As mentioned previously, the use of the imprint keys (as opposed to receiving a box with no keys, or random keys) allows the user to update the SEEPROM without leaving secure mode. However, the customer must be sure the machine is procured through a trusted channel (i.e. secure supply chain).

When op-build creates a development-to-production transition PNOR it will sign all containers with the Imprint keys except for the transition container itself (which is the inner container of the component "SBKT", for secure-boot key transition). The transition container will be signed with production keys, which are retrieved from the signframework in the same way as the Production mode operation describe previously. When the operation runs, the user is prompted for a PW for every artifact retrieved from the signframework.

Steps for HW key signers

HW key signers typically prepare signatures in advance, using a process like that described in the Production mode section, "HW key signing — steps for stand-alone signing". The only difference is that the key transition bit must be set in the prefix header. The transition bit is the least significant (right-most) bit in the --flags field. So the operation will be the same as the one described for production mode, except that --flags will reflect the setting of this bit. For example, in the case where the HW key signer has authority over only HW key A, and only FW key P is in use:

$ crtSignedContainer.sh \
  --mode production \
  --sign-project-config /tmp/project.ini \
  --flags 0x80000001 \
  --hwKeyA __get \
  --swKeyP __getkey \
  --archiveOut /tmp/signtool_HW_key_A_sig_transition.tgz

The HW key signer then delivers the file created by --archiveOut to the FW key signers. See the production mode instructions for additional notes and details.

Steps for FW key signers

The FW keys signers' operation is similar to the process described in the Production mode section, "FW key signing — steps for signing under op-build". The only difference is that op-build only uses production mode to sign the transition container, so the FW key signer(s) will be prompted for PW for that module only.

For FW key signing under op-build:

  1. Prepare an INI file in the manner described for production mode operation, and set the path to the INI via the platform _defconfig, or in the shell environment.
    $ export SB_PROJECT_INI=/path/to/project.ini
    
  2. Optionally enable validation and verification. Because the operation is a hybrid of local and production mode operations you must set SB_VERIFY for the local mode operations and set SB_VERIFY_TRANS for production mode operations. The SB_VERIFY_TRANS value will be used to verify the transition "to" container (SBKTRAND) and the SB_VERIFY value will be used to verify all other containers, i.e. the transition "from" containers. (If SB_VERIFY_TRANS is not set, SB_VERIFY will be used for the "to" container, which will result in a verification failure in this case as the "to" container is signed by different HW keys.) For example:
    $ export SB_VALIDATE=y
    $ export SB_VERIFY=$HOME/op-build/openpower/package/sb-signing-utils/keys/hw_keys_hash_Imprint.md
    $ export SB_VERIFY_TRANS=$HOME/op-build/openpower/package/sb-signing-utils/keys/hw_keys_hash_Production.md
    
    Alternately you may set these values in your INI:
    $ cat /tmp/project.ini
    ...
    [signtool]
    validate=y
    verify=/path/to/hw_keys_hash_Imprint.md
    verify_trans=/path/to/hw_keys_hash_Production.md
    
  3. If there are signatures to import, set SB_ARCHIVE_IN accordingly:
    $ export SB_ARCHIVE_IN="signtool_HW_key_A_sig_transition.tgz, signtool_HW_key_B_sig_transition.tgz, signtool_HW_key_C_sig_transition.tgz"
    
  4. There are no archives to export:
    $ unset SB_ARCHIVE_OUT
    
  5. Run op-build as follows:
    $ op-build BR2_OPENPOWER_SECUREBOOT_KEY_TRANSITION_TO_PROD=y
    
    If op-build has already been run, you may run just the final PNOR rebuild:
    $ op-build BR2_OPENPOWER_SECUREBOOT_KEY_TRANSITION_TO_PROD=y \
               openpower-pnor-rebuild
    
  6. You should now have a completed transition PNOR in ./output/images/.

See the production mode instructions for additional notes and details.

A note on terminology

The term imprint is a bit overloaded: The imprint keys are the default set of private signing keys provided by IBM, which have their private keys intentionally exposed. The imprinting process refers to reprogramming the SEEPROM, which may or may not be transitioning from the imprint keys. The reason for the overlap is that the imprint keys are typically shipped in a box intended to be re-imprinted by the recipient, although that is not the only use for the imprint keys.

Installing signtool for stand-alone use

To install signtool and signframework for execution outside of op-build, build the projects from the upstream source code.

Build and install sb-signing-utils

To "make install" the signtool:

git clone https://github.com/open-power/sb-signing-utils.git
cd ./sb-signing-utils/

./build_all.sh
  -- OR --
./build_all.sh gnu

make install
make uninstall
  -- OR --
make install bindir=/preferred/install/path/
make uninstall bindir=/preferred/install/path/

Build and install sb-signing-framework

To "make install" the signframework client:

git clone https://github.com/open-power/sb-signing-framework.git
cd ./sb-signing-framework/src/client/

make

make install
make uninstall
  -- OR --
make install bindir=/preferred/install/path/
make uninstall bindir=/preferred/install/path/

Ensure signtool and signframework executables are in PATH

After installing the packages, make sure the installed executables are in the execution PATH. For example:

PATH=/usr/local/bin/:$PATH
  -- OR --
PATH=/preferred/install/path/:$PATH

Running signtool using op-build binaries

To run signtool and signframework on an op-build system, but outside the op-build environment, you may use the executables built under op-build. Simply set your execution PATH to include the op-build "host" executable directory. For example:

$ PATH=$PATH:$HOME/op-build/output/host/usr/bin

Problem Determination

Troubleshooting Development mode operation stand-alone

  1. Following is an example of successful signtool operation in development (local) mode:

    $ crtSignedContainer.sh --validate --verify hw_keys_hash_Imprint.md \
      --hwKeyA hw_key_a.key --hwKeyB hw_key_b.key --hwKeyC hw_key_c.key \
      --swKeyP fw_key_p.key --swKeyQ fw_key_q.key --swKeyR fw_key_r.key \
      --flags 0x80000000 --code-start-offset 0x00000180 \
      --protectedPayload secure-payload --out secure-container
    --> crtSignedContainer.sh: Signing mode: local
    --> crtSignedContainer.sh: Creating new cache dir: /tmp/SIGNTOOL_1509151149
    --> crtSignedContainer.sh: Creating new cache subdir: /tmp/SIGNTOOL_1509151149/IMAGE
    --> crtSignedContainer.sh: Generating signing requests...
    --> crtSignedContainer.sh: Generating signature for HW key A...
    --> crtSignedContainer.sh: Generating signature for HW key B...
    --> crtSignedContainer.sh: Generating signature for HW key C...
    --> crtSignedContainer.sh: Generating signature for SW key P...
    --> crtSignedContainer.sh: Generating signature for SW key Q...
    --> crtSignedContainer.sh: Generating signature for SW key R...
    --> crtSignedContainer.sh: Have signatures for keys A,B,C,P,Q,R, adding to container...
    --> crtSignedContainer.sh: Container IMAGE build completed.
    
    Container validity check PASSED. Container verification check PASSED.
    
  2. In case of failure, corresponding error messages should appear in signtool output. Try to determine if the problem is in container construction or in validation or verification. Look for the presence of the Container IMAGE build completed message.
  3. If failure is due to "Container verification check FAILED", check the value of the --verify option.
  4. If failure is due to "Container validity check FAILED", or any other error, re-run the command with --verbose and --debug options added to the command line. Much more detailed output will be written to the console, and errors should be apparent.
  5. Submit bugs via https://github.com/open-power/sb-signing-utils/issues as required.

Troubleshooting Production mode operation stand-alone

  1. Following is an example of successful signtool operation in production mode:

    $ crtSignedContainer.sh --mode production \
      -–sign-project-config project.ini \
      --hwKeyA __get --hwKeyB __get --hwKeyC __get \
      --swKeyP __get \
      --flags 0x80000000 --code-start-offset 0x00000180 \
      --protectedPayload secure-payload --out secure-container
    --> crtSignedContainer.sh: Signing mode: production
    --> crtSignedContainer.sh: Parsing INI file: project.ini
    --> crtSignedContainer.sh: Creating new cache dir: /tmp/SIGNTOOL_1509151554
    --> crtSignedContainer.sh: Creating new cache subdir: /tmp/SIGNTOOL_1509151554/IMAGE
    --> crtSignedContainer.sh: Requesting public key for HW key A...
    Password: ********
    --> crtSignedContainer.sh: Retrieved public key for HW key A.
    --> crtSignedContainer.sh: Requesting public key for HW key B...
    Password: ********
    --> crtSignedContainer.sh: Retrieved public key for HW key B.
    --> crtSignedContainer.sh: Requesting public key for HW key C...
    Password: ********
    --> crtSignedContainer.sh: Retrieved public key for HW key C.
    --> crtSignedContainer.sh: Requesting public key for SW key P...
    Password: ********
    --> crtSignedContainer.sh: Retrieved public key for SW key P.
    --> crtSignedContainer.sh: Generating signing requests...
    --> crtSignedContainer.sh: Requesting signature for HW key A...
    Password: ********
    --> crtSignedContainer.sh: Retrieved signature for HW key A.
    --> crtSignedContainer.sh: Requesting signature for HW key B...
    Password: ********
    --> crtSignedContainer.sh: Retrieved signature for HW key B.
    --> crtSignedContainer.sh: Requesting signature for HW key C...
    Password: ********
    --> crtSignedContainer.sh: Retrieved signature for HW key C.
    --> crtSignedContainer.sh: Requesting signature for SW key P...
    Password: ********
    --> crtSignedContainer.sh: Retrieved signature for SW key P.
    --> crtSignedContainer.sh: Have signatures for keys A,B,C,P, 
        adding to container...
    --> crtSignedContainer.sh: Container IMAGE build completed.
    --> crtSignedContainer.sh: Archive saved to: /tmp/signtool_1509151554_IMAGE.tgz
    
    Container validity check PASSED. Container verification check PASSED.
    
  2. In case of failure, corresponding error messages should appear in signtool output. Try to determine if the problem is in container construction or in validation or verification. Look for the presence of the Container IMAGE build completed message.
  3. If failure is due to connectivity to signing server, check values in the [server] section of INI.
  4. If failure is due to signing server credentials, check values in the [signer] section of INI.
  5. If failure is due to signing server project names, check values in the [signproject] section of INI.
  6. If failure is due to "Container verification check FAILED", check value of "verify" in the [signtool] section of INI, or the --verify command line option.
  7. If failure is due to "Container validity check FAILED", or other error, re-run the command with --verbose and --debug options added to the command line. Much more detailed output will be written to the console for both signtool and signframework. If the problem is in the signframework operation, errors should be apparent. If problem is in container construction, or VALIDATION or VERIFICATION, errors should be apparent.
  8. Submit bugs via https://github.com/open-power/sb-signing-utils/issues as required.

Troubleshooting Development mode operation in op-build

  1. In op-build, the container signing operations are invoked toward the end of the op-build process, during PNOR image construction. If a failure is signing-related, the failure will likely occur in this phase. Output from the signtool, similar to that shown in the previous sections, will be seen on the op-build console for every container signed.
  2. In case of failure, corresponding error messages should appear in signtool output. Try to determine if the problem is in container construction or in validation or verification. Look for the presence of the Container IMAGE build completed message.
  3. If a failure is occurs during the PNOR build phase, you should be able to reproduce the error by running just the final PNOR rebuild step. This step is fast, as it repackages the binaries already built:

    $ op-build openpower-pnor-rebuild
    
  4. If failure is due to "Container verification check FAILED", check value of "verify" in the [signtool] section of INI.
  5. If failure is due to "Container validity check FAILED", or other error, run signtool / signframework in verbose and debug mode by setting the following environment vars:

    $ export SB_VERBOSE=y
    $ export SB_DEBUG=y
    
  6. Now try rerunning the operation. Much more detailed output will be written to the op-build console, and errors should be apparent.

    $ op-build BR2_OPENPOWER_SECUREBOOT_SIGN_MODE=production \
      openpower-pnor-rebuild
    
  7. Submit bugs via https://github.com/open-power/sb-signing-utils/issues as required.

Troubleshooting Production mode operation in op-build

  1. If a failure is determined to be signing-related, try running op-build with default settings in development mode:

    $ cd op-build/
    $ source op-build-env
    $ unset SB_PROJECT_INI
    $ op-build witherspoon_defconfig
    $ op-build
    
  2. If successful, now run just the final PNOR rebuild step (should be fast):

    $ op-build openpower-pnor-rebuild
    
  3. Now try re enabling the INI and rebuild the PNOR in production mode:

    $ op-build BR2_OPENPOWER_SECUREBOOT_SIGN_MODE=production \
      openpower-pnor-rebuild
    
  4. In case of failure, corresponding error messages should appear in signtool output. Try to determine if the problem is in container construction or in validation or verification. Look for the presence of the Container IMAGE build completed message.
  5. If failure is due to connectivity to signing server, check values in the [server] section of INI.
  6. If failure is due to signing server credentials, check values in the [signer] section of INI.
  7. If failure is due to signing server project names, check values in the [signproject] section of INI.
  8. If failure is due to "Container verification check FAILED", check value of "verify" in the [signtool] section of INI.
  9. If failure is due to "Container validity check FAILED", or other error, run signtool / signframework in verbose and debug mode by setting the following environment vars:

    $ export SB_VERBOSE=y
    $ export SB_DEBUG=y
    
  10. Now try rerunning the operation. Much more detailed output will be written to the console for both signtool and signframework. If the problem is in the signframework operation, errors should be apparent. If problem is in container VALIDATION or VERIFICATION, errors should be apparent.

    $ op-build BR2_OPENPOWER_SECUREBOOT_SIGN_MODE=production \
      openpower-pnor-rebuild
    
  11. Submit bugs via https://github.com/open-power/sb-signing-utils/issues as required.

Threats and vulnerabilities

The POWER firmware signing process aims to produce an installable firmware package that that can be trusted as authentic, regardless of the method by which the package was obtained (by download, from install media, etc.). The POWER firmware Secure Boot function can provide this assurance by checking cryptographic signatures contained in the firmware package (the component container metadata), at the point the firmware is loaded, and comparing the signing keys to a known-good value "burned" into to system NVRAM. It is important for the end-user to be able to trust any firmware that passes Secure Boot. And so it is critical for IBM, and it's ODM vendor partners, to release only legitimate packages as-signed. Any breach of this model may erode customer confidence in IBM products.

To ensure the legitimacy of packages released as "signed firmware", IBM (or the ODM) must be confident that the signing process is free from vulnerabilities. If an attacker is able to subvert the signing process, it may be possible for the attacker to inject rogue code into a legitimately signed package, or to be able to obtain legitimate signatures for rogue code that the attacker may distribute independently. While these threats are relatively difficult to carry out, since the firmware signing process typically occurs "in-house", close (in time) to the point of manufacture, and (typically) by a small number of individuals, the threats must be understood, and mitigated as needed. This section attempts to identify these threats and vulnerabilities.

Vulnerabilities in the hand-off of HW key signatures to FW key signers

In the production signing process, the HW key signing may be done as a stand-alone operation, with the completed signatures "handed off" to the FW key signers. The hand-off archive contains artifacts that will be imported by the FW key signers, including. This section considers the potential vulnerabilities in the hand-off process.

Type of attack: Replace legitimate artifacts in the archive with rogue artifacts, to fool FW signer into importing them.

Target operation: FW key signing operation, where artifacts are imported.

Good actors:

  • Target actor: The FW key signer, who might unknowingly import rogue artifacts to FW signing operation.
Bad actors:
  • Intermediary who is able to intercept and alter the archive during the hand-off, i.e. a man-in-the-middle attack.
  • A trusted (but rogue) HW key signer who provides a compromised archive to the HW key signer, i.e. an insider attack.
Summary of attacks:
  • ATTACK 1: Attacker attempts to replace HW keys in archive with his own set of keys.
  • ATTACK 2: Attacker attempts to replace HW key signatures in archive with his own set signatures.
  • ATTACK 3: Attacker attempts to introduce a rogue set of FW keys in the archive, in hopes FW signer will import them.
  • ATTACK 4: Attacker attempts to introduce a rogue set of FW key signatures in the archive, in hopes FW signer will import them.
  • ATTACK 5: Attacker attempts to introduce a rogue Hardware or Prefix header in the archive, in hopes FW signer will import it and sign it.
  • ATTACK 6: Attacker attempts to introduce a rogue Container Payload in the archive, in hopes FW signer will import it and sign it.
Mitigations:
  1. FW signer should always use the container VERIFICATION function to check the HW keys hash against the expected value, and obtain the expected value using keys retrieved *directly* from the signframework. VERIFICATION will fail on any mismatch.
  2. FW signer should always use the container VALIDATION function to check HW key signatures against provided keys. VALIDATION will fail on any mismatch.
Conclusions:
  • As long as above mitigations are in place, there is NO EXPLOIT that will succeed undetected, and so, NO VULNERABILITY.
  • There is little to be gained by any of these attacks, since none of them achieve the truly valuable exploits:
    • Fooling a signer into signing a rogue payload, OR:
    • Fooling a signer into releasing some private credential that would allow the attacker to sign a rogue payload.

Attacks and Mitigations

ATTACK 1: Attacker attempts to replace HW keys in archive with his own set of keys.
Target operation: FW key signing operation, where artifacts are imported.
Goal: The only valuable exploit is to trick the FW signer into importing a rogue set of HW keys and signatures for inclusion in the final container. Even if this is accomplished, however, it would produce a container not bootable on a machine with secure boot enabled.
Variations (and result):
a. Attacker replaces keys and signatures (so that they match). Container will fail (HW keys) VERIFICATION.
b. Attacker replaces only keys (creating a mismatch). Container will fail (HW keys) VERIFICATION and (signature) VALIDATION.

ATTACK 2: Attacker attempts to replace HW key signatures in archive with his own set signatures.
Target operation: FW key signing operation, where artifacts are imported.
Goal: See ATTACK 1
Variations (and result):
a. Attacker replaces keys and signatures (so that they match). Container will fail (HW keys) VERIFICATION.
b. Attacker replaces only signatures (creating a mismatch). Container will fail (signature) VALIDATION.

ATTACK 3: Attacker attempts to introduce a rogue set of FW keys in the archive, in hopes FW signer will import them.
See ATTACK 4.

ATTACK 4: Attacker attempts to introduce a rogue set of FW key signatures in the archive, in hopes FW signer will import them.
Target operation: FW key signing operation, where artifacts are imported
NOTE: This attack would be ineffective unless the attacker introduced rogue FW key signatures (ATTACK 4), in addition to rogue FW keys (ATTACK 3), to create a matching (but rogue) set. Otherwise, VALIDATION would fail the first time the FW key signer retrieved a FW signature from signframework, during container construction. So we assume these attacks WILL ALWAYS OCCUR IN CONJUNCTION.
Goal: Presumably, the goal is to trick the FW signer into importing a rogue set of FW keys and signatures, for inclusion in the final container. Even if this is accomplished, however, the container would fail VERIFICATION or VALIDATION, or both. And there seems to be little point to doing this, unless the attacker could also introduce a rogue Container Payload, for which there is currently *no mechanism* for doing via import.
Variations (and result):
a. Attacker replaces only keys (creating a mismatch). Container will fail (FW key signature) VALIDATION.
b. Attacker replaces only signatures (creating a mismatch). Container will fail (FW key signature) VALIDATION.
c. Attacker replaces keys and signatures (so that they match). Container will pass (FW key signature) VALIDATION, but:
  • Since attacker altered one or more FW keys, the HW signatures will fail to match, since the HW key signatures protect the FW keys. Container will fail (HW key signature) VALIDATION.
  • Since attacker altered one or more FW key signatures, the FW signatures will fail to match, since the HW key signatures protect the container payload. Container will fail (FW key signature over payload) VALIDATION.
  • NOTE: there are two variations of the previous: Rogue FW keys fail to match the Software Header, OR, if attacker is able to additionally replace the Software Header (which THERE IS CURRENTLY NO WAY TO DO VIA ARCHIVE IMPORT IN PRODUCTION MODE), the rogue FW header will fail to match the (hash of) the actual payload that the FW signer is signing. So either way, container will fail (FW key signature over payload) VALIDATION.

ATTACK 5: Attacker attempts to introduce a rogue Hardware or Prefix header in the archive, in hopes FW signer will import it.
Target operation: FW key signing operation, where artifacts are imported
Disposition: Not applicable: There is currently *no mechanism* for replacing the Hardware or Prefix headers via import, in Production mode. These headers are always regenerated.

ATTACK 6: Attacker attempts to introduce a rogue Container Payload in the archive, in hopes FW signer will import it.
Target operation: FW key signing operation, where artifacts are imported
Disposition: Not applicable: There is currently no mechanism for replacing the container payload via import; it is not an artifact that gets imported.

Conclusions

Even if the attacker is able to introduce or replace *all importable artifacts* in the archive (all keys and sigs), AND the FW key signer is running with NO PROTECTIONS ENABLED (i.e. no VALIDATION or VERIFICATION), and unknowingly imports all these artifacts to the container, it would produce a container *not bootable* on a machine with secure boot enabled.

Further: there is little to be gained by any of these attacks, since none of them achieve the truly valuable exploits:

  • Fooling a signer into signing a rogue payload, OR:
  • Fooling a signer into releasing some private credential that would allow the attacker to sign a rogue payload.

Vulnerabilities in the operation of the Signframework Client

How signer authorization works with signframework

The signframework client provides an interface to the signing server. The signing server is the machine physically hosting the Hardware Security Module (HSM, a.k.a. crypto card).

To retrieve the necessary artifacts (keys and signatures) to build the container, the signtool makes a request to the signframework via the signframework client. The signframework, in turn, passes the request to the signing server, and then passes the returned artifacts back to signtool. Only public keys are returned by the signing server; the private keys are generated and stored within the HSM and are never exposed. The requested signatures are generated on the signing server by the HSM.

The signframework employs a "SFTP dropbox" method of message passing, which the signframework client may use to make requests to the signing server. The signing server finds the requests in the dropbox, processes them, and returns the responses back to the dropbox where the client can pick them up. This provides a simple message queuing implementation, although there is no "publish/subscribe" mechanism: both the client and the server must poll the dropbox to find requests and responses.

To provide authorization to the signframework, the client uses a standard, asymmetric SSH key. The public part is registered at the signing server, to allow the client to access the dropbox. The private part is held by an individual user (the "signer"), who initiates the connection to the SFTP dropbox. To provide a higher level of security, this private SSH key must be password-encyrpted as stored on disk. The signframework client enforces this restriction, stubbornly refusing to handle any key that is not in encrypted format. To use the key, the signer enters the password upon every request to the dropbox. The signframework client prompts for the password, accepting the input via (echo-suppressed) keyboard entry, reading the password in plaintext. The plaintext password is held only in the signframework client process memory; it is never written to disk [are] and is discarded as soon as the private key is unlocked and read by the client.

Exploits and vulnerabilities of the signframework client

The system where the signframework client runs becomes a significant part of the overall attack surface. If an attacker is able to compromise this system, he may be able to fool the signer into signing some unauthorized payload, or worse: fool the signer into entering the password so it may be captured in plaintext. If the attacker can get the password, and the encrypted SSH key file (which is likely stored on disk, on or accessible by, the signframework machine), he may then be able to make requests to the signing server to sign some unauthorized payload with the signer's credential. This would then allow the attacker to introduce the unauthorized payload to a POWER server running in secure mode, and start the execution of some code that would otherwise not be permitted.

The most direct way for an attacker to achieve this is hack the signtool code, to sign some arbitrary payload (or hash thereof); or to hack the signframework code, to capture the signer's password in plaintext. This may be mitigated by instructing (and/or enforcing) the signer to always pull the signtool and signframework code directly from a trusted source (e.g. the upstream repository) and rebuilt it, immediately prior to the signing operation; OR to otherwise check the integrity of the code, for example, by comparing the (hash of) the executable files to a "reference manifest" of trusted code. Or, the signtool and signframework packages could be themselves packaged in some secure container (i.e. "code signed") to better ensure their integrity.

But the vulnerably runs deeper than this, and touches the very essence of secure computing: If the machine on which the signtool or signframework code runs cannot be trusted, then "all bets are off" with regard to code integrity. If an attacker can gain control of the machine (or at least, the signer's login to the machine), the attacker could alter the environment to make it *appear* that the above integrity checks were successful, when in fact it is the attacker's code executing (or likely, circumventing) these checks. Simply put, if the signer cannot trust the machine where the signframework client runs, there is no way for the signer to be sure that the correct artifacts were retrieved, a legitimate payload was signed, or that his/her SSH key password is not exposed.

Conclusions

In sum: if the signframework client is run from the machine where the container is constructed (i.e. the "build" machine), then that machine should be protected using the strongest security methods available to ensure it's integrity: This includes: secure boot of firmware and OS; driver signing (kernel space); codesigning of packages (user space); selinux, apparmor or equivalent; enforcement of any standard administrative policy to ensure integrity, e.g. password aging, restricting root login, use of su or sudo, etc.

Appendix A: Special cases

This section covers special cases for signing operations that deviate from, or have additional steps to, the operations described previously.

Production mode HW key signing when keyholders are not co-located

Co-located here means public keys A,B,C and P,Q,R can all be retrieved from the same signing server. If one or more keys resides in a different signing server, a "key import" operation is required.

If a HW key signer A,B,C needs to create a HW key signature over a FW key P,Q,R, and the FW key cannot be retrieved from the same signing server as the HW key, the HW key signer must import the FW key (or keys). The import is performed during the HW key signing, using an import file prepared in advance.

The import file is prepared by the FW key holder (or anyone with authority to retrieve the public key). A FW key signature is not required here, only the public key.

In short: the operation is identical to that described in "HW key signing – steps for stand-alone signing" for Production mode, but with an extra step that must be performed first.

Step 0: Export the FW public key(s) to an archive

This step is performed by the FW key holder, or anyone with authority to retrieve the public key.

  1. Prepare a project INI with the appropriate values for the FW key holder's signframework account and credentials, and the address of the signing server.
  2. Run signtool per the following examples:

    Example 1: FW keys P,Q are in use. The archive containing FW public keys P,Q will be written to /tmp/.

    $ crtSignedContainer.sh \
      --mode production \
      --sign-project-config /tmp/project.ini \
      --swKeyP __getkey \
      --swKeyQ __getkey \
      --archiveOut /tmp/
    

    Example 2: Only FW key P is in use. The archive containing FW public key P will be written to /tmp/.

    $ crtSignedContainer.sh \
      --mode production \
      --sign-project-config /tmp/project.ini \
      --swKeyP __getkey \
      --archiveOut /tmp/
    
  3. Deliver the archive file created by --archiveOut to the HW key signer(s).

Step 1: Import and sign the FW public key(s), and generate an archive containing the HW key signature(s)

This step is performed by the HW key signer. It's identical to the operation described in "HW key signing – steps for stand-alone signing", adding --archiveIn pointing to the file created in step 0.

  1. Prepare a project INI with the appropriate values for the HW key holder's signframework account and credentials, and the address of the signing server.
  2. Run signtool per the following examples:

    Example 1: HW key signer has authority over all three HW keys A,B,C. FW keys P,Q,R are all in use. One or more FW keys P,Q,R is expected to be in the archive specified by --archiveIn. The others will be retrieved from the signing server. The archive containing HW key signatures A,B,C, will be written to /tmp/.

    $ crtSignedContainer.sh \
      --mode production \
      --sign-project-config /tmp/project.ini \
      --archiveIn /tmp/signtool_TIMESTAMP_IMAGE.tgz \
      --flags 0x80000000 \
      --hwKeyA __get \
      --hwKeyB __get \
      --hwKeyC __get \
      --swKeyP __getkey \
      --swKeyQ __getkey \
      --swKeyR __getkey \
      --archiveOut /tmp/
    

    Example 2: HW key signer has authority over only HW key A. Only FW key P is in use. The FW key P is expected to be in the archive specified by --archiveIn. No FW keys are retrieved from the signing server. The archive containing HW key signature A will be written to /tmp/.

    $ crtSignedContainer.sh \
      --mode production \
      --sign-project-config /tmp/project.ini \
      --archiveIn /tmp/signtool_TIMESTAMP_IMAGE.tgz \
      --flags 0x80000000 \
      --hwKeyA __get \
      --swKeyP __getkey \
      --archiveOut /tmp/
    
  3. Deliver the archive file created by --archiveOut to the FW key signer(s). See previous Production mode instructions to continue the process.

Notes:

  • Step 0 performs no operations with HW keys at all.
  • In Step 1, the command line options --swKeyP, --swKeyQ, --swkeyR are identical regardless of whether the key comes from the signing server or the from import.
  • The archive generated by Step 1 will be the same whether the FW keys come from the signing server or from the import.
Requirements for sign project basenames:

Recall the project basenames are specified in the INI like this:

[signproject]
hw_signing_project_basename = sign_ecc_pwr_hw_key
fw_signing_project_basename = sign_ecc_pwr_fw_key_op_bld
getpubkey_project_basename = getpubkeyecc

In the INI file used in Step 0:

  • The value of fw_signing_project_basename is the correct value to retrieve the FW keys from the signframework.
  • The value of hw_signing_project_basename doesn't matter here, and may be omitted
In the INI file used in Step 1:
  • The value of hw_signing_project_basename is the correct value to retrieve the HW keys and signatures from the signframework.
  • The value of fw_signing_project_basename must match the value used in Step 0. If in Step 1, there is a mix of FW keys coming from the signing server and from import, it means the value of fw_signing_project_basename must be the same for both signing frameworks. If however, Step 1 only imports keys via --archiveIn, the value of fw_signing_project_basename can simply be set to whatever is used for the Step 0 signframework.
  • That said: if any of the HW key signing operations A,B,C retrieve FW keys from the signframework, again the values must be the same between signframeworks. The reason is: at the op-build operation, where multiple "Step 1" archives are imported, a mismatch in the value of fw_signing_project_basename will be problematic at that point.
Clone this wiki locally