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 |