-
Notifications
You must be signed in to change notification settings - Fork 15
Home
POWER Firmware Signing Tool
Users and Signers Guide
Version 0.4 draft 1
Author: Dave Heller (hellerda at us.ibm.com)
Document contributors:
- Tim Block
- Nick Bofferding
- Pam Eggler
- Chris Engel
- Stewart Smith
- Elaine Palmer
- 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.
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.
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.
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.
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.
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.
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:
- Adds a (SHA512) hash of the firmware image (the container payload) to a region of the header known as the firmware header.
- 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.
- Calculates a hash of the three FW keys and adds this to a region of the header known as the prefix header.
- 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.)
- 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:
- Verify the signature over the payload by the three FW keys.
- Verify the signature over the FW keys by the three HW keys.
- Verify that the hash of the HW keys matches the hash stored in CPU SEEPROM.
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
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.
This section gives an overview of the OpenPOWER firmware signing tool, a.k.a. 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.
- 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.
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 and architectures, in big and little endian.
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.
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.
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:
- A property set via shell environment, when running under op-build or running stand-alone, takes the lowest precedence.
- 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.
- A property set via INI file will override a property set via op-build configuration or command-line, or via shell environment.
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.
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_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_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 ...
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)
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 |
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.
There are a few properties that may be set via op-build _defconfig, or in some cases, must be set via the _defconfig:
Option | Attributes | |
BR2_OPENPOWER_SECUREBOOT_SIGN_MODE | Description | same as SB_SIGN_MODE |
BR2_OPENPOWER_SECUREBOOT_CONTAINER_VALIDATE | Description | same as SB_VALIDATE |
BR2_OPENPOWER_SECUREBOOT_CONTAINER_VERIFY | Description | same as SB_VERIFY |
BR2_OPENPOWER_SECUREBOOT_PASS_ON_VALIDATION_ERROR | Description | same as SB_PASS_ON_ERROR |
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 must be set in the _defconfig, if changed from the default value, because several op-build components outside of signtool need to have awareness of these options, and signtool does not consume these properties directly.
- BR2_OPENPOWER_SECUREBOOT_CONTAINER_VALIDATE, BR2_OPENPOWER_SECUREBOOT_CONTAINER_VERIFY and BR2_OPENPOWER_SECUREBOOT_PASS_ON_VALIDATION_ERROR may be set in the _defconfig, if changed from the default value.
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.)
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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
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.
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:
- 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 the user to enter a password, so in production mode, signing is interactive.
- 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.
- 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.
- 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 other POWER firmware uses ad-hoc signing for HW key signers, and simple signing for FW key signers
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
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.
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.
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 |
code-start-offset | Ensure proper value to --code-start-offset command-line option |
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
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.
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.
- Install the signtool (sb-signing-utils) and signframework (sb-signing-framework) packages.
-
Make sure the installed executables are in the search PATH. For example:
$ PATH=/usr/local/bin:$PATH
- 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.
-
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 \ --code-start-offset 0x00000180 \ --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 \ --code-start-offset 0x00000180 \ --flags 0x80000000 \ --hwKeyA __get \ --swKeyP __getkey \ --archiveOut /tmp/
- Deliver the archive file created by --archiveOut to the FW key signer(s).
- 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.
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.
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.
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.
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.
- Install the signtool (sb-signing-utils) and signframework (sb-signing-framework) packages.
-
Make sure the installed executables are in the search PATH. For example:
$ PATH=/usr/local/bin:$PATH
- 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.
-
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 \ --code-start-offset 0x00000180 \ --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.
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:
- Create a project INI file with the appropriate values for the FW key signer's account and credentials.
-
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
-
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"�
-
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
-
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
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.
- 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.
- 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).
- 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/.
Independent signing decouples the signing operation from the firmware build operation under op-build. The process works like this:
- 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.
- The blobs are signed, and the signatures returned to op-build.
- The signatures are imported back to op-build for a final run that creates a completed container.
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).
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
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.
-
Select a directory to output the signing requests:
$ mkdir /tmp/archive.requested/
-
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
-
There are no archives to import; export archives 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
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
-
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
-
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.
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.
See previous section from Production signing: Ensuring a correct Prefix header
- Install the signtool (sb-signing-utils) and signframework (sb-signing-framework) packages.
-
Make sure the installed executables are in the search PATH. For example:
$ PATH=/usr/local/bin:$PATH
-
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 \ --code-start-offset 0x00000180 \ --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 \ --code-start-offset 0x00000180 \ --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
-
Deliver the archive file created by --archiveOut to the FW key signer(s).
-
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.
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
- Install the signtool (sb-signing-utils) and signframework (sb-signing-framework) packages.
-
Make sure the installed executables are in the search PATH. For example:
$ PATH=/usr/local/bin:$PATH
-
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
-
The directory specified by --archiveOut should now contain an export archive for every singing 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
The signatures from the singing 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.
-
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
-
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/, $//')</p> $ 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
-
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 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
-
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.
- op-build will place the completed PNOR image in the output directory. You should now have a secure-bootable image in ./output/images/.
All images should now pass validity check and verification check.
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.
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 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.
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, the 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
To install signtool and signframework for execution outside of op-build, build the projects from the upstream source code.
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/
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/
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
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=$HOME/op-build/output/host/usr/bin/:$PATH
-
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.
- 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.
- If failure is due to "Container verification check FAILED", check the value of the --verify option.
- 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.
- Submit bugs via https://github.com/open-power/sb-signing-utils/issues as required.
-
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.
- 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.
- If failure is due to connectivity to signing server, check values in the [server] section of INI.
- If failure is due to signing server credentials, check values in the [signer] section of INI.
- If failure is due to signing server project names, check values in the [signproject] section of INI.
- 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.
- 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.
- Submit bugs via https://github.com/open-power/sb-signing-utils/issues as required.
- 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.
- 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.
-
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
- If failure is due to "Container verification check FAILED", check value of "verify" in the [signtool] section of INI.
-
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
-
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
- Submit bugs via https://github.com/open-power/sb-signing-utils/issues as required.
-
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
-
If successful, now run just the final PNOR rebuild step (should be fast):
$ op-build openpower-pnor-rebuild
-
Now try re enabling the INI and rebuild the PNOR in production mode:
$ op-build BR2_OPENPOWER_SECUREBOOT_SIGN_MODE=production \ openpower-pnor-rebuild
- 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.
- If failure is due to connectivity to signing server, check values in the [server] section of INI.
- If failure is due to signing server credentials, check values in the [signer] section of INI.
- If failure is due to signing server project names, check values in the [signproject] section of INI.
- If failure is due to "Container verification check FAILED", check value of "verify" in the [signtool] section of INI.
-
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
-
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
- Submit bugs via https://github.com/open-power/sb-signing-utils/issues as required.
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.
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.
- 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 the the HW key signer, i.e. an insider attack.
- 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.
- 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.
- FW signer should always use the container VALIDATION function to check HW key signatures against provided keys. VALIDATION will fail on any mismatch.
- 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.
- 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: N/A (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.
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.
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.
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 singer'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.
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.