| 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 | 
|  |