summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Hyunkoo Jee <edjee@google.com>2018-04-04 01:33:10 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-04-18 02:07:42 -0700
commit1493e938e45535f86b7132a83123c6319eacb217 (patch)
tree2855cc4f1e419608cf9c4019294de79bebee98a2
parent2051af0c80c9a6cc82aa96fab7371a9475a107fe (diff)
downloadvboot-1493e938e45535f86b7132a83123c6319eacb217.tar.gz
image_signing: sign UEFI binaries
BUG=b:62189155 TEST=See CL:*601769 BRANCH=none Change-Id: Id9569616bae0d5f44c1c96e18522ace244a5aae8 Reviewed-on: https://chromium-review.googlesource.com/995175 Commit-Ready: Edward Jee <edjee@google.com> Tested-by: Edward Jee <edjee@google.com> Reviewed-by: Jason Clinton <jclinton@chromium.org>
-rw-r--r--scripts/image_signing/common_minimal.sh27
-rwxr-xr-xscripts/image_signing/install_gsetup_certs.sh70
-rwxr-xr-xscripts/image_signing/sign_official_build.sh71
-rwxr-xr-xscripts/image_signing/sign_uefi.sh107
-rwxr-xr-xscripts/image_signing/verify_uefi.sh79
5 files changed, 343 insertions, 11 deletions
diff --git a/scripts/image_signing/common_minimal.sh b/scripts/image_signing/common_minimal.sh
index 43dfd109..d7ecc10e 100644
--- a/scripts/image_signing/common_minimal.sh
+++ b/scripts/image_signing/common_minimal.sh
@@ -252,6 +252,33 @@ mount_image_partition() {
fi
}
+# Mount the image's ESP (EFI System Partition) on a newly created temporary
+# directory.
+# Prints out the newly created temporary directory path if succeeded, prints
+# out nothing if ESP doesn't exist, print out "MOUNT_FAILED" if mount failed.
+# Args: IMAGE ESP_PARTNUM
+mount_image_esp() {
+ local image="$1"
+ local ESP_PARTNUM=12
+
+ local esp_offset=$(( $(partoffset "${image}" "${ESP_PARTNUM}") ))
+ # Check if the image has an ESP partition.
+ if [[ "${esp_offset}" == "0" ]]; then
+ return
+ fi
+
+ local esp_dir="$(make_temp_dir)"
+ # We use the 'unsafe' variant because the EFI system partition is vfat type
+ # and can be mounted in RW mode.
+ if ! $(_mount_image_partition_retry "${image}" "${ESP_PARTNUM}" \
+ "${esp_dir}" > /dev/null); then
+ echo "MOUNT_FAILED"
+ return
+ fi
+
+ echo "${esp_dir}"
+}
+
# Extract a partition to a file
# Args: IMAGE PARTNUM OUTPUTFILE
extract_image_partition() {
diff --git a/scripts/image_signing/install_gsetup_certs.sh b/scripts/image_signing/install_gsetup_certs.sh
new file mode 100755
index 00000000..d515b790
--- /dev/null
+++ b/scripts/image_signing/install_gsetup_certs.sh
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+. "$(dirname "$0")/common.sh"
+
+set -e
+
+usage() {
+ cat <<EOF
+Usage: $PROG /path/to/esp/dir /path/to/keys/dir
+
+Sign UEFI binaries in ESP.
+EOF
+ if [[ $# -gt 0 ]]; then
+ error "$*"
+ exit 1
+ fi
+ exit 0
+}
+
+install_gsetup_cert() {
+ local key_type="$1"
+ local cert="$2"
+ local gsetup_dir="$3"
+ if [[ -f "$cert" ]]; then
+ info "Putting ${key_type} cert: ${cert}"
+ local cert_basename="$(basename "${cert}")"
+ local der_filename="${cert_basename%.*}.der"
+ sudo mkdir -p "${gsetup_dir}/${key_type}"
+ sudo openssl x509 -in "${cert}" -inform PEM \
+ -out "${gsetup_dir}/${key_type}/${der_filename}" -outform DER
+ else
+ info "No ${key_type} cert: ${cert}"
+ fi
+}
+
+main() {
+ local esp_dir="$1"
+ local key_dir="$2"
+
+ if [[ $# -ne 2 ]]; then
+ usage "command takes exactly 2 args"
+ fi
+
+ local gsetup_dir="${esp_dir}/EFI/Google/GSetup"
+
+ local pk_cert="${key_dir}/pk/pk.pem"
+ if [[ ! -f "${pk_cert}" ]]; then
+ die "No PK cert: ${pk_cert}"
+ fi
+ install_gsetup_cert pk "${pk_cert}" "${gsetup_dir}"
+
+ local db_cert="${key_dir}/db/db.pem"
+ if [[ ! -f "${db_cert}" ]]; then
+ die "No DB cert: ${db_cert}"
+ fi
+ install_gsetup_cert db "${db_cert}" "${gsetup_dir}"
+
+ local kek_cert="${key_dir}/kek/kek.pem"
+ install_gsetup_cert kek "${kek_cert}" "${gsetup_dir}"
+
+ for dbx_cert in "${key_dir}/dbx/"*".pem"; do
+ install_gsetup_cert dbx "${dbx_cert}" "${gsetup_dir}"
+ done
+}
+
+main "$@"
diff --git a/scripts/image_signing/sign_official_build.sh b/scripts/image_signing/sign_official_build.sh
index 7969179a..90fbed84 100755
--- a/scripts/image_signing/sign_official_build.sh
+++ b/scripts/image_signing/sign_official_build.sh
@@ -737,6 +737,60 @@ resign_android_image_if_exists() {
info "Re-signed Android image"
}
+# Sign UEFI binaries, if possible.
+sign_uefi_binaries() {
+ local image="$1"
+
+ if [[ ! -d "${KEY_DIR}/uefi" ]]; then
+ return 0
+ fi
+
+ local esp_dir="$(mount_image_esp "${image}")"
+ if [[ -z "${esp_dir}" ]]; then
+ return 0
+ elif [[ "${esp_dir}" == "MOUNT_FAILED" ]]; then
+ error "Could not mount EFI partition for signing UEFI binaries"
+ return 1
+ fi
+ "${SCRIPT_DIR}/install_gsetup_certs.sh" "${esp_dir}" "${KEY_DIR}/uefi"
+ "${SCRIPT_DIR}/sign_uefi.sh" "${esp_dir}" "${KEY_DIR}/uefi"
+ sudo umount "${esp_dir}"
+
+ local rootfs_dir="$(make_temp_dir)"
+ mount_image_partition "${image}" 3 "${rootfs_dir}"
+ "${SCRIPT_DIR}/sign_uefi.sh" "${rootfs_dir}/boot" "${KEY_DIR}/uefi"
+ sudo umount "${rootfs_dir}"
+
+ info "Signed UEFI binaries"
+ return 0
+}
+
+verify_uefi_signatures() {
+ local image="$1"
+ local succeeded=1
+
+ local esp_dir="$(mount_image_esp "${image}")"
+ if [[ -z "${esp_dir}" ]]; then
+ return 0
+ elif [[ "${esp_dir}" == "MOUNT_FAILED" ]]; then
+ error "Could not mount EFI partition for verifying UEFI signatures"
+ return 1
+ fi
+ "${SCRIPT_DIR}/verify_uefi.sh" "${esp_dir}" "${esp_dir}" || succeeded=0
+
+ local rootfs_dir="$(make_temp_dir)"
+ mount_image_partition_ro "${image}" 3 "${rootfs_dir}"
+ "${SCRIPT_DIR}/verify_uefi.sh" "${rootfs_dir}/boot" "${esp_dir}" || \
+ succeeded=0
+ sudo umount "${rootfs_dir}"
+
+ sudo umount "${esp_dir}"
+
+ if [[ "${succeeded}" == "0" ]]; then
+ die "UEFI signature verification failed"
+ fi
+}
+
# Sign an oci container with the given keys.
# Args: CONTAINER KEY_DIR [OUTPUT_CONTAINER]
sign_oci_container() {
@@ -818,6 +872,8 @@ EOF
verify_image_rootfs "${loop_rootfs}"
+ verify_uefi_signatures "${INPUT_IMAGE}"
+
# TODO(gauravsh): Check embedded firmware AU signatures.
}
@@ -854,19 +910,11 @@ update_legacy_bootloader() {
local image="$1"
local loop_kern="$2"
- local esp_partnum=12
- local esp_offset=$(( $(partoffset "${image}" "${esp_partnum}") * 512 ))
- # Check if the image has an ESP partition.
- if [[ "${esp_offset}" == "0" ]]; then
+ local esp_dir="$(mount_image_esp "${image}")"
+ if [[ -z "${esp_dir}" ]]; then
info "Not updating legacy bootloader configs: ${image}"
return 0
- fi
-
- local esp_dir="$(make_temp_dir)"
- # We use the 'unsafe' variant because the EFI system partition is vfat type
- # and can be mounted in RW mode.
- if ! _mount_image_partition_retry "${image}" "${esp_partnum}" \
- "${esp_dir}"; then
+ elif [[ "${esp_dir}" == "MOUNT_FAILED" ]]; then
error "Could not mount EFI partition for updating legacy bootloader cfg."
return 1
fi
@@ -929,6 +977,7 @@ sign_image_file() {
resign_firmware_payload "${output}"
resign_android_image_if_exists "${output}"
+ sign_uefi_binaries "${output}"
# We do NOT strip /boot for factory installer, since some devices need it to
# boot EFI. crbug.com/260512 would obsolete this requirement.
#
diff --git a/scripts/image_signing/sign_uefi.sh b/scripts/image_signing/sign_uefi.sh
new file mode 100755
index 00000000..4cef5a50
--- /dev/null
+++ b/scripts/image_signing/sign_uefi.sh
@@ -0,0 +1,107 @@
+#!/bin/bash
+
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+. "$(dirname "$0")/common.sh"
+
+set -e
+
+usage() {
+ cat <<EOF
+Usage: $PROG /path/to/target/dir /path/to/keys/dir
+
+Sign UEFI binaries in the target directory.
+EOF
+ if [[ $# -gt 0 ]]; then
+ error "$*"
+ exit 1
+ fi
+ exit 0
+}
+
+sign_efi_file() {
+ local target="$1"
+ local temp_dir="$2"
+ local priv_key="$3"
+ local sign_cert="$4"
+ local verify_cert="$5"
+ if [[ -z "${verify_cert}" ]]; then
+ verify_cert="${sign_cert}"
+ fi
+
+ info "Signing efi file ${target}"
+ sudo sbattach --remove "${target}" || true
+ local signed_file="${temp_dir}/$(basename "${target}")"
+ sbsign --key="${priv_key}" --cert="${sign_cert}" \
+ --output="${signed_file}" "${target}" || warn "Cannot sign ${target}"
+ if [[ -f "${signed_file}" ]]; then
+ sudo cp -f "${signed_file}" "${target}"
+ sbverify --cert "${verify_cert}" "${target}" || die "Verification failed"
+ fi
+}
+
+main() {
+ local target_dir="$1"
+ local key_dir="$2"
+
+ if [[ $# -ne 2 ]]; then
+ usage "command takes exactly 2 args"
+ fi
+
+ if ! type -P sbattach &>/dev/null; then
+ die "Skip signing UEFI binaries (sbattach not found)."
+ fi
+ if ! type -P sbsign &>/dev/null; then
+ die "Skip signing UEFI binaries (sbsign not found)."
+ fi
+ if ! type -P sbverify &>/dev/null; then
+ die "Skip signing UEFI binaries (sbverify not found)."
+ fi
+
+ local bootloader_dir="${target_dir}/efi/boot"
+ local syslinux_dir="${target_dir}/syslinux"
+ local kernel_dir="${target_dir}"
+
+ local verify_cert="${key_dir}/db/db.pem"
+ if [[ ! -f "$verify_cert" ]]; then
+ die "No verification cert: ${verify_cert}"
+ fi
+
+ local sign_cert="${key_dir}/db/db.children/db_child.pem"
+ if [[ ! -f "${sign_cert}" ]]; then
+ die "No signing cert: ${sign_cert}"
+ fi
+
+ local sign_key="${key_dir}/db/db.children/db_child.rsa"
+ if [[ ! -f "${sign_key}" ]]; then
+ die "No signing key: ${sign_key}"
+ fi
+
+ local working_dir="$(make_temp_dir)"
+
+ for efi_file in "${bootloader_dir}/"*".efi"; do
+ if [[ ! -f "${efi_file}" ]]; then
+ continue
+ fi
+ sign_efi_file "${efi_file}" "${working_dir}" \
+ "${sign_key}" "${sign_cert}" "${verify_cert}"
+ done
+
+ for syslinux_kernel_file in "${syslinux_dir}/vmlinuz."?; do
+ if [[ ! -f "${syslinux_kernel_file}" ]]; then
+ continue
+ fi
+ sign_efi_file "${syslinux_kernel_file}" "${working_dir}" \
+ "${sign_key}" "${sign_cert}" "${verify_cert}"
+ done
+
+ local kernel_file="$(readlink -f "${kernel_dir}/vmlinuz")"
+ if [[ -f "${kernel_file}" ]]; then
+ sign_efi_file "${kernel_file}" "${working_dir}" \
+ "${sign_key}" "${sign_cert}" "${verify_cert}"
+ fi
+}
+
+main "$@"
diff --git a/scripts/image_signing/verify_uefi.sh b/scripts/image_signing/verify_uefi.sh
new file mode 100755
index 00000000..959b5b8f
--- /dev/null
+++ b/scripts/image_signing/verify_uefi.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+. "$(dirname "$0")/common.sh"
+
+set -e
+
+usage() {
+ cat <<EOF
+Usage: $PROG /path/to/target/dir /path/to/esp/dir
+
+Verify signatures of UEFI binaries in the target directory.
+EOF
+ if [[ $# -gt 0 ]]; then
+ error "$*"
+ exit 1
+ fi
+ exit 0
+}
+
+main() {
+ local target_dir="$1"
+ local esp_dir="$2"
+
+ if [[ $# -ne 2 ]]; then
+ usage "command takes exactly 1 args"
+ fi
+
+ if ! type -P sbverify &>/dev/null; then
+ die "Cannot verify UEFI signatures (sbverify not found)."
+ fi
+
+ local bootloader_dir="${target_dir}/efi/boot"
+ local syslinux_dir="${target_dir}/syslinux"
+ local kernel_dir="${target_dir}"
+ local gsetup_dir="${esp_dir}/EFI/Google/GSetup"
+
+ if [[ ! -f "${gsetup_dir}/pk/pk.der" ]]; then
+ warn "No PK cert"
+ exit 0
+ fi
+
+ local db_cert_der="${gsetup_dir}/db/db.der"
+ if [[ ! -f "${db_cert_der}" ]]; then
+ warn "No DB cert"
+ exit 0
+ fi
+
+ local working_dir="$(make_temp_dir)"
+ local cert="${working_dir}/cert.pem"
+ openssl x509 -in "${db_cert_der}" -inform DER -out "${cert}" -outform PEM
+
+ for efi_file in "${bootloader_dir}/"*".efi"; do
+ if [[ ! -f "${efi_file}" ]]; then
+ continue
+ fi
+ sbverify --cert "${cert}" "${efi_file}" ||
+ die "Verification failed: ${efi_file}"
+ done
+
+ for syslinux_kernel_file in "${syslinux_dir}/vmlinuz."?; do
+ if [[ ! -f "${syslinux_kernel_file}" ]]; then
+ continue
+ fi
+ sbverify --cert "${cert}" "${syslinux_kernel_file}" ||
+ warn "Verification failed: ${syslinux_kernel_file}"
+ done
+
+ local kernel_file="$(readlink -f "${kernel_dir}/vmlinuz")"
+ if [[ -f "${kernel_file}" ]]; then
+ sbverify --cert "${cert}" "${kernel_file}" ||
+ warn "Verification failed: ${kernel_file}"
+ fi
+}
+
+main "$@"