| Index: scripts/image_signing/sign_official_build.sh
|
| diff --git a/scripts/image_signing/sign_official_build.sh b/scripts/image_signing/sign_official_build.sh
|
| index cdc1837af02251e48681aeb20c4ff4630b108665..d7140acc64de660223ae159924903c914b8b6cfc 100755
|
| --- a/scripts/image_signing/sign_official_build.sh
|
| +++ b/scripts/image_signing/sign_official_build.sh
|
| @@ -13,25 +13,27 @@
|
| # cgpt (from src/platform/vboot_reference)
|
| # dump_kernel_config (from src/platform/vboot_reference)
|
| # verity (from src/platform/verity)
|
| -#
|
| -# Usage: sign_for_ssd.sh <type> input_image /path/to/keys/dir output_image
|
| -#
|
| -# where <type> is one of:
|
| -# ssd (sign an SSD image)
|
| -# recovery (sign a USB recovery image)
|
| -# install (sign a factory install image)
|
| +# load_kernel_test (from src/platform/vboot_reference)
|
|
|
| # Load common constants and variables.
|
| . "$(dirname "$0")/common.sh"
|
|
|
| -if [ $# -ne 4 ]; then
|
| +# Print usage string
|
| +usage() {
|
| cat <<EOF
|
| -Usage: $0 <type> input_image /path/to/keys/dir output_image"
|
| +Usage: $PROG <type> input_image /path/to/keys/dir [output_image]
|
| where <type> is one of:
|
| ssd (sign an SSD image)
|
| - recovery (sign a USB recovery image)
|
| - install (sign a factory install image)
|
| + recovery (sign a USB recovery image)
|
| + install (sign a factory install image)
|
| + verify (verify an image including rootfs hashes)
|
| +
|
| +If you are signing an image, you must specify an [output_image].
|
| EOF
|
| +}
|
| +
|
| +if [ $# -ne 3 ] && [ $# -ne 4 ]; then
|
| + usage
|
| exit 1
|
| fi
|
|
|
| @@ -39,7 +41,9 @@ fi
|
| set -e
|
|
|
| # Make sure the tools we need are available.
|
| -for prereqs in gbb_utility vbutil_kernel cgpt dump_kernel_config verity; do
|
| +for prereqs in gbb_utility vbutil_kernel cgpt dump_kernel_config verity \
|
| + load_kernel_test;
|
| +do
|
| type -P "${prereqs}" &>/dev/null || \
|
| { echo "${prereqs} tool not found."; exit 1; }
|
| done
|
| @@ -49,19 +53,35 @@ INPUT_IMAGE=$2
|
| KEY_DIR=$3
|
| OUTPUT_IMAGE=$4
|
|
|
| -# Re-calculate rootfs hash, update rootfs and kernel command line.
|
| -# Args: IMAGE KEYBLOCK PRIVATEKEY
|
| -recalculate_rootfs_hash() {
|
| - echo "Recalculating rootfs"
|
| - local image=$1 # Input image.
|
| - local keyblock=$2 # Keyblock for re-generating signed kernel partition
|
| - local signprivate=$3 # Private key to use for signing.
|
| -
|
| - # First, grab the existing kernel partition and get the kernel config.
|
| +# Get current rootfs hash and kernel command line
|
| +# ARGS: IMAGE
|
| +grab_kernel_config() {
|
| + local image=$1
|
| + # Grab the existing kernel partition and get the kernel config.
|
| temp_kimage=$(make_temp_file)
|
| extract_image_partition ${image} 2 ${temp_kimage}
|
| - local kernel_config=$(sudo dump_kernel_config ${temp_kimage})
|
| - local dm_config=$(echo $kernel_config |
|
| + dump_kernel_config ${temp_kimage}
|
| +}
|
| +
|
| +# Get the hash from a kernel config command line
|
| +get_hash_from_config() {
|
| + local kernel_config=$1
|
| + echo ${kernel_config} | sed -e 's/.*dm="\([^"]*\)".*/\1/g' | \
|
| + cut -f2- -d, | cut -f9 -d ' '
|
| +}
|
| +
|
| +# Calculate rootfs hash of an image
|
| +# Args: ROOTFS_IMAGE KERNEL_CONFIG HASH_IMAGE
|
| +#
|
| +# rootfs calculation parameters are grabbed from KERNEL_CONFIG
|
| +#
|
| +# Returns an updated kernel config command line with the new hash.
|
| +# and writes the new hash image to the file HASH_IMAGE
|
| +calculate_rootfs_hash() {
|
| + local rootfs_image=$1
|
| + local kernel_config=$2
|
| + local hash_image=$3
|
| + local dm_config=$(echo ${kernel_config} |
|
| sed -e 's/.*dm="\([^"]*\)".*/\1/g' |
|
| cut -f2- -d,)
|
| # We extract dm=... portion of the config command line. Here's an example:
|
| @@ -72,7 +92,7 @@ recalculate_rootfs_hash() {
|
|
|
| if [ -z "${dm_config}" ]; then
|
| echo "WARNING: Couldn't grab dm_config. Aborting rootfs hash calculation"
|
| - return
|
| + exit 1
|
| fi
|
| local rootfs_sectors=$(echo ${dm_config} | cut -f2 -d' ')
|
| local root_dev=$(echo ${dm_config} | cut -f4 -d ' ')
|
| @@ -80,28 +100,49 @@ recalculate_rootfs_hash() {
|
| local verity_depth=$(echo ${dm_config} | cut -f7 -d' ')
|
| local verity_algorithm=$(echo ${dm_config} | cut -f8 -d' ')
|
|
|
| - # Mount the rootfs and run the verity tool on it.
|
| - local hash_image=$(make_temp_file)
|
| - local rootfs_img=$(make_temp_file)
|
| - extract_image_partition ${image} 3 ${rootfs_img}
|
| + # Run the verity tool on the rootfs partition.
|
| local table="vroot none ro,"$(sudo verity create \
|
| ${verity_depth} \
|
| ${verity_algorithm} \
|
| - ${rootfs_img} \
|
| + ${rootfs_image} \
|
| $((rootfs_sectors / 8)) \
|
| ${hash_image})
|
| # Reconstruct new kernel config command line and replace placeholders.
|
| table="$(echo "$table" |
|
| sed -s "s|ROOT_DEV|${root_dev}|g;s|HASH_DEV|${hash_dev}|")"
|
| - kernel_config=$(echo ${kernel_config} |
|
| - sed -e 's#\(.*dm="\)\([^"]*\)\(".*\)'"#\1${table}\3#g")
|
| + echo ${kernel_config} | sed -e 's#\(.*dm="\)\([^"]*\)\(".*\)'"#\1${table}\3#g"
|
| +}
|
| +
|
| +# Re-calculate rootfs hash, update rootfs and kernel command line.
|
| +# Args: IMAGE KEYBLOCK PRIVATEKEY
|
| +update_rootfs_hash() {
|
| + echo "Recalculating rootfs"
|
| + local image=$1 # Input image.
|
| + local keyblock=$2 # Keyblock for re-generating signed kernel partition
|
| + local signprivate=$3 # Private key to use for signing.
|
| +
|
| + local rootfs_image=$(make_temp_file)
|
| + extract_image_partition ${image} 3 ${rootfs_image}
|
| + local kernel_config=$(grab_kernel_config "${image}")
|
| + local hash_image=$(make_temp_file)
|
| +
|
| + local new_kernel_config=$(calculate_rootfs_hash "${rootfs_image}" \
|
| + "${kernel_config}" "${hash_image}")
|
| +
|
| + local rootfs_blocks=$(dumpe2fs "${rootfs_image}" 2> /dev/null |
|
| + grep "Block count" |
|
| + tr -d ' ' |
|
| + cut -f2 -d:)
|
| + local rootfs_sectors=$((rootfs_blocks * 8))
|
|
|
| # Overwrite the appended hashes in the rootfs
|
| local temp_config=$(make_temp_file)
|
| - echo ${kernel_config} >${temp_config}
|
| - dd if=${hash_image} of=${rootfs_img} bs=512 \
|
| + echo ${new_kernel_config} >${temp_config}
|
| + dd if=${hash_image} of=${rootfs_image} bs=512 \
|
| seek=${rootfs_sectors} conv=notrunc
|
|
|
| + local temp_kimage=$(make_temp_file)
|
| + extract_image_partition ${image} 2 ${temp_kimage}
|
| # Re-calculate kernel partition signature and command line.
|
| local updated_kimage=$(make_temp_file)
|
| vbutil_kernel --repack ${updated_kimage} \
|
| @@ -111,7 +152,7 @@ recalculate_rootfs_hash() {
|
| --config ${temp_config}
|
|
|
| replace_image_partition ${image} 2 ${updated_kimage}
|
| - replace_image_partition ${image} 3 ${rootfs_img}
|
| + replace_image_partition ${image} 3 ${rootfs_image}
|
| }
|
|
|
| # Extracts the firmware update binaries from the a firmware update
|
| @@ -119,7 +160,7 @@ recalculate_rootfs_hash() {
|
| # Args: INPUT_SCRIPT OUTPUT_DIR
|
| get_firmwarebin_from_shellball() {
|
| local input=$1
|
| - local output_dir=$2
|
| + local output_dir=$2
|
| uudecode -o - ${input} | tar -C ${output_dir} -zxf - 2>/dev/null || \
|
| echo "Extracting firmware autoupdate failed." && exit 1
|
| }
|
| @@ -132,7 +173,7 @@ resign_firmware_payload() {
|
| # Grab firmware image from the autoupdate shellball.
|
| local rootfs_dir=$(make_temp_dir)
|
| mount_image_partition ${image} 3 ${rootfs_dir}
|
| -
|
| +
|
| local shellball_dir=$(make_temp_dir)
|
| get_firmwarebin_from_shellball \
|
| ${rootfs_dir}/usr/sbin/chromeos-firmwareupdate ${shellball_dir}
|
| @@ -154,7 +195,7 @@ resign_firmware_payload() {
|
| # Replace MD5 checksum in the firmware update payload
|
| newfd_checksum=$(md5sum ${shellball_dir}/bios.bin | cut -f 1 -d ' ')
|
| temp_version=$(make_temp_file)
|
| - cat ${shellball_dir}/VERSION |
|
| + cat ${shellball_dir}/VERSION |
|
| sed -e "s#\(.*\)\ \(.*bios.bin.*\)#${newfd_checksum}\ \2#" > ${temp_version}
|
| sudo cp ${temp_version} ${shellball_dir}/VERSION
|
|
|
| @@ -173,6 +214,57 @@ resign_firmware_payload() {
|
| echo "Re-signed firmware AU payload in $image"
|
| }
|
|
|
| +# Verify an image including rootfs hash using the specified keys.
|
| +verify_image() {
|
| + local kernel_config=$(grab_kernel_config ${INPUT_IMAGE})
|
| + local rootfs_image=$(make_temp_file)
|
| + extract_image_partition ${INPUT_IMAGE} 3 ${rootfs_image}
|
| + local hash_image=$(make_temp_file)
|
| + local type=""
|
| +
|
| +
|
| + # First, perform RootFS verification
|
| + echo "Verifying RootFS hash..."
|
| + local new_kernel_config=$(calculate_rootfs_hash "${rootfs_image}" \
|
| + "${kernel_config}" "${hash_image}")
|
| + local expected_hash=$(get_hash_from_config "${new_kernel_config}")
|
| + local got_hash=$(get_hash_from_config "${kernel_config}")
|
| +
|
| + if [ ! "${got_hash}" = "${expected_hash}" ]; then
|
| + cat <<EOF
|
| +FAILED: RootFS hash is incorrect.
|
| +Expected: ${expected_hash}
|
| +Got: ${got_hash}
|
| +EOF
|
| + else
|
| + echo "PASS: RootFS hash is correct (${expected_hash})"
|
| + fi
|
| +
|
| + # Now try and verify kernel partition signature.
|
| + set +e
|
| + local try_key=${KEY_DIR}/recovery_key.vbpubk
|
| + echo "Testing key verification..."
|
| + # The recovery key is only used in the recovery mode.
|
| + echo -n "With Recovery Key (Recovery Mode ON, Dev Mode OFF): " && \
|
| + { load_kernel_test "${INPUT_IMAGE}" "${try_key}" -b 2 >/dev/null 2>&1 && \
|
| + echo "YES"; } || echo "NO"
|
| + echo -n "With Recovery Key (Recovery Mode ON, Dev Mode ON): " && \
|
| + { load_kernel_test "${INPUT_IMAGE}" "${try_key}" -b 3 >/dev/null 2>&1 && \
|
| + echo "YES"; } || echo "NO"
|
| +
|
| + try_key=${KEY_DIR}/kernel_subkey.vbpubk
|
| + # The SSD key is only used in non-recovery mode.
|
| + echo -n "With SSD Key (Recovery Mode OFF, Dev Mode OFF): " && \
|
| + { load_kernel_test "${INPUT_IMAGE}" "${try_key}" -b 0 >/dev/null 2>&1 && \
|
| + echo "YES"; } || echo "NO"
|
| + echo -n "With SSD Key (Recovery Mode OFF, Dev Mode ON): " && \
|
| + { load_kernel_test "${INPUT_IMAGE}" "${try_key}" -b 1 >/dev/null 2>&1 && \
|
| + echo "YES"; } || echo "NO"
|
| + set -e
|
| +
|
| + # TODO(gauravsh): Check embedded firmware AU signatures.
|
| +}
|
| +
|
| # Generate the SSD image
|
| sign_for_ssd() {
|
| ${SCRIPT_DIR}/resign_image.sh ${INPUT_IMAGE} ${OUTPUT_IMAGE} \
|
| @@ -185,7 +277,7 @@ sign_for_ssd() {
|
| sign_for_recovery() {
|
| ${SCRIPT_DIR}/resign_image.sh ${INPUT_IMAGE} ${OUTPUT_IMAGE} \
|
| ${KEY_DIR}/recovery_kernel_data_key.vbprivk \
|
| - ${KEY_DIR}/recovery_kernel.keyblock
|
| + ${KEY_DIR}/recovery_kernel.keyblock
|
|
|
| # Now generate the installer vblock with the SSD keys.
|
| temp_kimage=$(make_temp_file)
|
| @@ -217,18 +309,31 @@ if [ "${FW_UPDATE}" == "1" ]; then
|
| resign_firmware_payload ${INPUT_IMAGE}
|
| fi
|
|
|
| +# Verification
|
| +if [ "${TYPE}" == "verify" ]; then
|
| + verify_image
|
| + exit 1
|
| +fi
|
| +
|
| +
|
| +# Signing requires an output image name
|
| +if [ -z "${OUTPUT_IMAGE}" ]; then
|
| + usage
|
| + exit 1
|
| +fi
|
| +
|
| if [ "${TYPE}" == "ssd" ]; then
|
| - recalculate_rootfs_hash ${INPUT_IMAGE} \
|
| + update_rootfs_hash ${INPUT_IMAGE} \
|
| ${KEY_DIR}/kernel.keyblock \
|
| ${KEY_DIR}/kernel_data_key.vbprivk
|
| sign_for_ssd
|
| elif [ "${TYPE}" == "recovery" ]; then
|
| - recalculate_rootfs_hash ${INPUT_IMAGE} \
|
| + update_rootfs_hash ${INPUT_IMAGE} \
|
| ${KEY_DIR}/recovery_kernel.keyblock \
|
| ${KEY_DIR}/recovery_kernel_data_key.vbprivk
|
| sign_for_recovery
|
| elif [ "${TYPE}" == "install" ]; then
|
| - recalculate_rootfs_hash ${INPUT_IMAGE} \
|
| + update_rootfs_hash ${INPUT_IMAGE} \
|
| ${KEY_DIR}/installer_kernel.keyblock \
|
| ${KEY_DIR}/recovery_kernel_data_key.vbprivk
|
| sign_for_factory_install
|
|
|