3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
7 # Sign the final build image using the "official" keys.
9 # Prerequisite tools needed in the system path:
11 # gbb_utility (from src/platform/vboot_reference)
12 # vbutil_kernel (from src/platform/vboot_reference)
13 # cgpt (from src/platform/vboot_reference)
14 # dump_kernel_config (from src/platform/vboot_reference)
15 # verity (from src/platform/verity)
16 # load_kernel_test (from src/platform/vboot_reference)
20 # Load common constants and variables.
21 . "$(dirname "$0")/common.sh"
26 Usage: $PROG <type> input_image /path/to/keys/dir [output_image] [version_file]
27 where <type> is one of:
28 ssd (sign an SSD image)
29 base (sign a base image, similar to an SSD image)
30 recovery (sign a USB recovery image)
31 factory (sign a factory install image)
32 install (old alias to "factory")
33 update_payload (sign a delta update hash)
34 kernel (sign a kernel image)
35 recovery_kernel (sign a recovery_kernel image)
36 firmware (sign a firmware image)
37 usb (sign an image to boot directly from USB)
38 verify (verify an image including rootfs hashes)
39 nv_lp0_firmware (sign nvidia lp0 firmware)
40 accessory_usbpd (sign USB-PD accessory firmware)
41 accessory_rwsig (sign accessory RW firmware)
43 output_image: File name of the signed output image
44 version_file: File name of where to read the kernel and firmware versions.
46 If you are signing an image, you must specify an [output_image] and
47 optionally, a [version_file].
50 if [[ $# -gt 0 ]]; then
57 # Verify we have as many arguments as we expect, else show usage & quit.
59 # check_argc <number args> <exact number>
60 # check_argc <number args> <lower bound> <upper bound>
64 if [[ $1 -ne $2 ]]; then
65 usage "command takes exactly $2 args"
69 if [[ $1 -lt $2 || $1 -gt $3 ]]; then
70 usage "command takes $2 to $3 args"
74 die "check_argc: incorrect number of arguments"
81 # Add to the path since some tools reside here and may not be in the non-root
83 PATH=$PATH:/usr/sbin:/sbin
85 # Make sure the tools we need are available.
86 for prereqs in gbb_utility vbutil_kernel cgpt dump_kernel_config verity \
87 load_kernel_test dumpe2fs sha1sum e2fsck; do
88 type -P "${prereqs}" &>/dev/null || \
89 { echo "${prereqs} tool not found."; exit 1; }
101 # Get current rootfs hash and kernel command line
102 # ARGS: IMAGE KERNELPART
103 grab_kernel_config() {
105 local kernelpart=$2 # Kernel partition number to grab.
106 # Grab the existing kernel partition and get the kernel config.
107 temp_kimage=$(make_temp_file)
108 extract_image_partition ${image} ${kernelpart} ${temp_kimage}
109 dump_kernel_config ${temp_kimage}
112 # TODO(gauravsh): These are duplicated from chromeos-setimage. We need
113 # to move all signing and rootfs code to one single place where it can be
114 # reused. crosbug.com/19543
116 # get_verity_arg <commandline> <key> -> <value>
118 echo "$1" | sed -n "s/.*\b$2=\([^ \"]*\).*/\1/p"
121 is_old_verity_argv() {
122 local depth=$(echo "$1" | cut -f7 -d' ')
123 if [ "$depth" = "0" ]; then
129 # Get the dmparams parameters from a kernel config.
130 get_dmparams_from_config() {
131 local kernel_config=$1
132 echo ${kernel_config} | sed -nre 's/.*dm="([^"]*)".*/\1/p'
134 # Get the verity root digest hash from a kernel config command line.
135 get_hash_from_config() {
136 local kernel_config=$1
137 local dm_config=$(get_dmparams_from_config "${kernel_config}")
138 local vroot_dev=$(get_dm_slave "${dm_config}" vroot)
139 if is_old_verity_argv "${vroot_dev}"; then
140 echo ${vroot_dev} | cut -f9 -d ' '
142 echo $(get_verity_arg "${vroot_dev}" root_hexdigest)
146 # Get the slave device and its args
147 # get_dm_ags $dm_config [vboot|vroot]
148 # Assumes we have only one slave device per device
152 echo $(echo "${dm}" | sed -nre "s/.*${device}[^,]*,([^,]*).*/\1/p")
155 # Set the slave device and its args for a device
156 # get_dm_ags $dm_config [vboot|vroot] args
157 # Assumes we have only one slave device per device
162 echo $(echo "${dm}" |
163 sed -nre "s#(.*${device}[^,]*,)([^,]*)(.*)#\1${slave}\3#p")
166 CALCULATED_KERNEL_CONFIG=
168 # Calculate rootfs hash of an image
169 # Args: ROOTFS_IMAGE KERNEL_CONFIG HASH_IMAGE
171 # rootfs calculation parameters are grabbed from KERNEL_CONFIG
173 # Updated dm-verity arguments (to be replaced in kernel config command line)
174 # with the new hash is stored in $CALCULATED_DM_ARGS and the new hash image is
175 # written to the file HASH_IMAGE.
176 calculate_rootfs_hash() {
177 local rootfs_image=$1
178 local kernel_config=$2
180 local dm_config=$(get_dmparams_from_config "${kernel_config}")
182 if [ -z "${dm_config}" ]; then
183 echo "WARNING: Couldn't grab dm_config. Aborting rootfs hash calculation."
186 local vroot_dev=$(get_dm_slave "${dm_config}" vroot)
190 local verity_algorithm
193 local verity_bin="verity"
194 if is_old_verity_argv "${vroot_dev}"; then
195 # dm="0 2097152 verity ROOT_DEV HASH_DEV 2097152 1 \
196 # sha1 63b7ad16cb9db4b70b28593f825aa6b7825fdcf2"
197 rootfs_sectors=$(echo ${vroot_dev} | cut -f2 -d' ')
198 verity_depth=$(echo ${vroot_dev} | cut -f7 -d' ')
199 verity_algorithm=$(echo ${vroot_dev} | cut -f8 -d' ')
200 root_dev=$(echo ${vroot_dev} | cut -f4 -d ' ')
201 hash_dev=$(echo ${vroot_dev} | cut -f5 -d ' ')
202 # Hack around the fact that the signer needs to use the old version of
203 # verity to generate legacy verity kernel parameters. If we find it,
205 type -P "verity-old" &>/dev/null && verity_bin="verity-old"
207 # Key-value parameters.
208 rootfs_sectors=$(get_verity_arg "${vroot_dev}" hashstart)
210 verity_algorithm=$(get_verity_arg "${vroot_dev}" alg)
211 root_dev=$(get_verity_arg "${vroot_dev}" payload)
212 hash_dev=$(get_verity_arg "${vroot_dev}" hashtree)
213 salt=$(get_verity_arg "${vroot_dev}" salt)
217 if [ -n "$salt" ]; then
218 salt_arg="salt=$salt"
221 # Run the verity tool on the rootfs partition.
222 local slave=$(sudo ${verity_bin} mode=create \
223 alg=${verity_algorithm} \
224 payload="${rootfs_image}" \
225 payload_blocks=$((rootfs_sectors / 8)) \
226 hashtree="${hash_image}" ${salt_arg})
227 # Reconstruct new kernel config command line and replace placeholders.
228 slave="$(echo "${slave}" |
229 sed -s "s|ROOT_DEV|${root_dev}|g;s|HASH_DEV|${hash_dev}|")"
230 CALCULATED_DM_ARGS="$(set_dm_slave "${dm_config}" vroot "${slave}")"
231 CALCULATED_KERNEL_CONFIG="$(echo "${kernel_config}" |
232 sed -e 's#\(.*dm="\)\([^"]*\)\(".*\)'"#\1${CALCULATED_DM_ARGS}\3#g")"
235 # Re-calculate rootfs hash, update rootfs and kernel command line(s).
236 # Args: IMAGE DM_PARTNO KERN_A_KEYBLOCK KERN_A_PRIVKEY KERN_B_KEYBLOCK \
239 # The rootfs is hashed by tool 'verity', and the hash data is stored after the
240 # rootfs. A hash of those hash data (also known as final verity hash) may be
241 # contained in kernel 2 or kernel 4 command line.
243 # This function reads dm-verity configuration from DM_PARTNO, rebuilds rootfs
244 # hash, and then resigns kernel A & B by their keyblock and private key files.
245 update_rootfs_hash() {
246 local image=$1 # Input image.
247 local dm_partno="$2" # Partition number of kernel that contains verity args.
248 local kern_a_keyblock="$3" # Keyblock file for kernel A.
249 local kern_a_privkey="$4" # Private key file for kernel A.
250 local kern_b_keyblock="$5" # Keyblock file for kernel B.
251 local kern_b_privkey="$6" # Private key file for kernel A.
253 # Note even though there are two kernels, there is one place (after rootfs)
254 # for hash data, so we must assume both kernel use same hash algorithm (i.e.,
256 echo "Updating rootfs hash and updating config for Kernel partitions"
258 # If we can't find dm parameters in the kernel config, bail out now.
259 local kernel_config=$(grab_kernel_config "${image}" "${dm_partno}")
260 local dm_config=$(get_dmparams_from_config "${kernel_config}")
261 if [ -z "${dm_config}" ]; then
262 echo "ERROR: Couldn't grab dm_config from kernel partition ${dm_partno}"
263 echo " (config: ${kernel_config})"
267 # check and clear need_to_resign tag
268 local rootfs_dir=$(make_temp_dir)
269 mount_image_partition_ro "${image}" 3 "${rootfs_dir}"
270 if has_needs_to_be_resigned_tag "${rootfs_dir}"; then
272 sudo umount "${rootfs_dir}"
273 mount_image_partition "${image}" 3 "${rootfs_dir}"
274 sudo rm -f "${rootfs_dir}/${TAG_NEEDS_TO_BE_SIGNED}"
276 sudo umount "${rootfs_dir}"
278 local rootfs_image=$(make_temp_file)
279 extract_image_partition ${image} 3 ${rootfs_image}
280 local hash_image=$(make_temp_file)
282 # Disable rw mount support prior to hashing.
283 disable_rw_mount "${rootfs_image}"
285 if ! calculate_rootfs_hash "${rootfs_image}" "${kernel_config}" \
286 "${hash_image}"; then
287 echo "calculate_rootfs_hash failed!"
288 echo "Aborting rootfs hash update!"
292 local rootfs_blocks=$(sudo dumpe2fs "${rootfs_image}" 2> /dev/null |
296 local rootfs_sectors=$((rootfs_blocks * 8))
298 # Overwrite the appended hashes in the rootfs
299 dd if=${hash_image} of=${rootfs_image} bs=512 \
300 seek=${rootfs_sectors} conv=notrunc 2>/dev/null
301 replace_image_partition ${image} 3 ${rootfs_image}
303 # Update kernel command lines
304 local dm_args="${CALCULATED_DM_ARGS}"
305 local temp_config=$(make_temp_file)
306 local temp_kimage=$(make_temp_file)
307 local updated_kimage=$(make_temp_file)
311 local new_kernel_config=
313 for kernelpart in 2 4; do
314 if ! new_kernel_config="$(
315 grab_kernel_config "${image}" "${kernelpart}" 2>/dev/null)" &&
316 [[ "${kernelpart}" == 4 ]]; then
317 # Legacy images don't have partition 4.
318 echo "Skipping empty kernel partition 4 (legacy images)."
321 new_kernel_config="$(echo "${new_kernel_config}" |
322 sed -e 's#\(.*dm="\)\([^"]*\)\(".*\)'"#\1${dm_args}\3#g")"
323 echo "New config for kernel partition ${kernelpart} is:"
324 echo "${new_kernel_config}" | tee "${temp_config}"
325 extract_image_partition "${image}" "${kernelpart}" "${temp_kimage}"
326 # Re-calculate kernel partition signature and command line.
327 if [[ "$kernelpart" == 2 ]]; then
328 keyblock="${kern_a_keyblock}"
329 priv_key="${kern_a_privkey}"
331 keyblock="${kern_b_keyblock}"
332 priv_key="${kern_b_privkey}"
334 vbutil_kernel --repack ${updated_kimage} \
335 --keyblock ${keyblock} \
336 --signprivate ${priv_key} \
337 --version "${KERNEL_VERSION}" \
338 --oldblob ${temp_kimage} \
339 --config ${temp_config}
340 replace_image_partition ${image} ${kernelpart} ${updated_kimage}
344 # Update the SSD install-able vblock file on stateful partition.
346 # This is deprecated because all new images should have a SSD boot-able kernel
347 # in partition 4. However, the signer needs to be able to sign new & old images
348 # (crbug.com/449450#c13) so we will probably never remove this.
349 update_stateful_partition_vblock() {
351 local kernb_image="$(make_temp_file)"
352 local temp_out_vb="$(make_temp_file)"
354 extract_image_partition "${image}" 4 "${kernb_image}"
355 if [[ "$(dump_kernel_config "${kernb_image}" 2>/dev/null)" == "" ]]; then
356 echo "Building vmlinuz_hd.vblock from legacy image partition 2."
357 extract_image_partition "${image}" 2 "${kernb_image}"
360 # vblock should always use kernel keyblock.
361 vbutil_kernel --repack "${temp_out_vb}" \
362 --keyblock "${KEY_DIR}/kernel.keyblock" \
363 --signprivate "${KEY_DIR}/kernel_data_key.vbprivk" \
364 --oldblob "${kernb_image}" \
367 # Copy the installer vblock to the stateful partition.
368 local stateful_dir=$(make_temp_dir)
369 mount_image_partition "${image}" 1 "${stateful_dir}"
370 sudo cp ${temp_out_vb} ${stateful_dir}/vmlinuz_hd.vblock
371 sudo umount "${stateful_dir}"
374 # Do a sanity check on the image's rootfs
376 verify_image_rootfs() {
378 local rootfs_image=$(make_temp_file)
379 extract_image_partition ${image} 3 ${rootfs_image}
380 # This flips the read-only compatibility flag, so that e2fsck does not
381 # complain about unknown file system capabilities.
382 enable_rw_mount ${rootfs_image}
383 echo "Running e2fsck to check root file system for errors"
384 sudo e2fsck -fn "${rootfs_image}" ||
385 { echo "Root file system has errors!" && exit 1;}
388 # Extracts a firmware updater bundle (for firmware image binaries) file
389 # (generated by src/platform/firmware/pack_firmware.sh).
390 # Args: INPUT_FILE OUTPUT_DIR
391 extract_firmware_bundle() {
392 local input="$(readlink -f "$1")"
393 local output_dir="$2"
394 if [ ! -s "${input}" ]; then
396 elif grep -q '^##CUTHERE##' "${input}"; then
397 # Bundle supports self-extraction.
398 "$input" --sb_extract "${output_dir}" ||
399 die "Extracting firmware autoupdate (--sb_extract) failed."
401 # Legacy bundle - try uudecode.
402 uudecode -o - ${input} | tar -C ${output_dir} -zxf - 2>/dev/null ||
403 die "Extracting firmware autoupdate failed."
407 # Repacks firmware updater bundle content from given folder.
408 # Args: INPUT_DIR TARGET_SCRIPT
409 repack_firmware_bundle() {
411 local target="$(readlink -f "$2")"
413 if [ ! -s "${target}" ]; then
415 elif grep -q '^##CUTHERE##' "${target}"; then
416 # Bundle supports repacking.
417 # Workaround issue crosbug.com/p/33719
419 's/shar -Q -q -x -m -w/shar -Q -q -x -m --no-character-count/' \
421 "$target" --sb_repack "${input_dir}" ||
422 die "Updating firmware autoupdate (--sb_repack) failed."
424 # Legacy bundle using uuencode + tar.gz.
425 # Replace MD5 checksum in the firmware update payload.
426 local newfd_checksum="$(md5sum ${input_dir}/bios.bin | cut -f 1 -d ' ')"
427 local temp_version="$(make_temp_file)"
428 cat ${input_dir}/VERSION |
429 sed -e "s#\(.*\)\ \(.*bios.bin.*\)#${newfd_checksum}\ \2#" > ${temp_version}
430 mv ${temp_version} ${input_dir}/VERSION
432 # Re-generate firmware_update.tgz and copy over encoded archive in
433 # the original shell ball.
434 sed -ine '/^begin .*firmware_package/,/end/D' "$target"
435 tar zcf - -C "${input_dir}" . |
436 uuencode firmware_package.tgz >>"${target}"
440 # Sign a firmware in-place with the given keys.
441 # Args: FIRMWARE_IMAGE KEY_DIR FIRMWARE_VERSION [LOEM_OUTPUT_DIR]
445 local firmware_version=$3
446 local loem_output_dir=${4:-}
448 # Resign the firmware with new keys, also replacing the root and recovery
449 # public keys in the GBB.
450 "${SCRIPT_DIR}/sign_firmware.sh" "${image}" "${key_dir}" "${image}" \
451 "${firmware_version}" "${loem_output_dir}"
452 echo "Signed firmware image output to ${image}"
455 # Sign nvidia lp0 firmware with the given keys.
456 # Args: NV_LP0_FIRMWARE_IMAGE KEY_DIR
457 sign_nv_lp0_firmware() {
458 local nv_lp0_fw_image=$1
461 "${SCRIPT_DIR}/sign_nv_cbootimage.sh" "lp0_firmware" \
462 "${key_dir%/}/nv_pkc.pem" "${nv_lp0_fw_image}" "tegra210"
463 echo "Signed nvidia lp0 firmware image output to ${nv_lp0_fw_image}"
466 # Sign a kernel in-place with the given keys.
467 # Args: KERNEL_IMAGE KEY_DIR KERNEL_VERSION
471 local kernel_version=$3
473 # Note: Although vbutil_kernel may correctly handle specifying the same
474 # output file as the input file, we do not want to rely on it correctly
475 # handing that. Hence, the use of a temporary file.
476 local temp_kernel=$(make_temp_file)
478 # Resign the kernel with new keys.
479 vbutil_kernel --repack "${temp_kernel}" \
480 --keyblock "${key_dir}/kernel.keyblock" \
481 --signprivate "${key_dir}/kernel_data_key.vbprivk" \
482 --version "${kernel_version}" \
485 mv "${temp_kernel}" "${image}"
486 echo "Signed kernel image output to ${image}"
489 # Sign a recovery kernel in-place with the given keys.
490 # Args: KERNEL_IMAGE KEY_DIR KERNEL_VERSION
491 sign_recovery_kernel() {
494 local kernel_version=$3
496 # Note: Although vbutil_kernel may correctly handle specifying the same
497 # output file as the input file, we do not want to rely on it correctly
498 # handing that. Hence, the use of a temporary file.
499 local temp_kernel=$(make_temp_file)
501 # Resign the kernel with new recovery keys.
502 vbutil_kernel --repack "${temp_kernel}" \
503 --keyblock "${key_dir}/recovery_kernel.keyblock" \
504 --signprivate "${key_dir}/recovery_kernel_data_key.vbprivk" \
505 --version "${kernel_version}" \
508 mv "${temp_kernel}" "${image}"
509 echo "Signed recovery_kernel image output to ${image}"
512 # Sign a delta update payload (usually created by paygen).
513 # Args: INPUT_IMAGE KEY_DIR OUTPUT_IMAGE
514 sign_update_payload() {
518 local key_size key_file="${key_dir}/update_key.pem"
519 # Maps key size to verified boot's algorithm id (for pad_digest_utility).
520 # Hashing algorithm is always SHA-256.
528 key_size=$(openssl rsa -text -noout -in "${key_file}" | \
529 sed -n -r '1{s/Private-Key: \(([0-9]*) bit\)/\1/p}')
530 algo=${algos[${key_size}]}
531 if [[ -z ${algo} ]]; then
532 die "Unknown algorithm specified by key_size=${key_size}"
535 pad_digest_utility ${algo} "${image}" | \
536 openssl rsautl -sign -pkcs -inkey "${key_file}" -out "${output}"
539 # Re-sign the firmware AU payload inside the image rootfs with a new keys.
541 resign_firmware_payload() {
544 if [ -n "${NO_FWUPDATE}" ]; then
545 echo "Skipping firmware update."
549 # Grab firmware image from the autoupdate bundle (shellball).
550 local rootfs_dir=$(make_temp_dir)
551 mount_image_partition ${image} 3 ${rootfs_dir}
552 local firmware_bundle="${rootfs_dir}/usr/sbin/chromeos-firmwareupdate"
553 local shellball_dir=$(make_temp_dir)
555 # extract_firmware_bundle can fail if the image has no firmware update.
556 if ! extract_firmware_bundle "${firmware_bundle}" "${shellball_dir}"; then
557 # Unmount now to prevent changes.
558 sudo umount "${rootfs_dir}"
559 echo "Didn't find a firmware update. Not signing firmware."
562 echo "Found a valid firmware update shellball."
564 local image_file sign_args=() loem_sfx loem_output_dir
565 for image_file in "${shellball_dir}"/bios*.bin; do
566 if [[ -e "${KEY_DIR}/loem.ini" ]]; then
567 # Extract the extended details from "bios.bin" and use that in the
568 # subdir for the keyset.
569 loem_sfx=$(sed -r 's:.*/bios([^/]*)[.]bin$:\1:' <<<"${image_file}")
570 loem_output_dir="${shellball_dir}/keyset${loem_sfx}"
571 sign_args=( "${loem_output_dir}" )
572 mkdir -p "${loem_output_dir}"
574 sign_firmware "${image_file}" "${KEY_DIR}" "${FIRMWARE_VERSION}" \
578 local signer_notes="${shellball_dir}/VERSION.signer"
579 echo "" >"$signer_notes"
580 echo "Signed with keyset in $(readlink -f "${KEY_DIR}") ." >>"$signer_notes"
582 new_shellball=$(make_temp_file)
583 cp -f "${firmware_bundle}" "${new_shellball}"
584 chmod a+rx "${new_shellball}"
585 repack_firmware_bundle "${shellball_dir}" "${new_shellball}"
586 sudo cp -f "${new_shellball}" "${firmware_bundle}"
587 sudo chmod a+rx "${firmware_bundle}"
588 # Unmount now to flush changes.
589 sudo umount "${rootfs_dir}"
590 echo "Re-signed firmware AU payload in $image"
593 # Verify an image including rootfs hash using the specified keys.
595 local rootfs_image=$(make_temp_file)
596 extract_image_partition ${INPUT_IMAGE} 3 ${rootfs_image}
598 echo "Verifying RootFS hash..."
599 # What we get from image.
601 # What we calculate from the rootfs.
602 local new_kernel_config
603 # Depending on the type of image, the verity parameters may
604 # exist in either kernel partition 2 or kernel partition 4
606 for partnum in 2 4; do
607 echo "Considering Kernel partition $partnum"
608 kernel_config=$(grab_kernel_config ${INPUT_IMAGE} $partnum)
609 local hash_image=$(make_temp_file)
610 if ! calculate_rootfs_hash "${rootfs_image}" "${kernel_config}" \
611 "${hash_image}"; then
612 echo "Trying next kernel partition."
615 new_kernel_config="$CALCULATED_KERNEL_CONFIG"
619 # Note: If calculate_rootfs_hash succeeded above, these should
621 expected_hash=$(get_hash_from_config "${new_kernel_config}")
622 got_hash=$(get_hash_from_config "${kernel_config}")
624 if [ -z "${expected_hash}" ] || [ -z "${got_hash}" ]; then
625 echo "FAILURE: Couldn't verify RootFS hash on the image."
629 if [ ! "${got_hash}" = "${expected_hash}" ]; then
631 FAILED: RootFS hash is incorrect.
632 Expected: ${expected_hash}
637 echo "PASS: RootFS hash is correct (${expected_hash})"
640 # Now try and verify kernel partition signature.
642 local try_key=${KEY_DIR}/recovery_key.vbpubk
643 echo "Testing key verification..."
644 # The recovery key is only used in the recovery mode.
645 echo -n "With Recovery Key (Recovery Mode ON, Dev Mode OFF): " && \
646 { load_kernel_test "${INPUT_IMAGE}" "${try_key}" -b 2 >/dev/null 2>&1 && \
647 echo "YES"; } || echo "NO"
648 echo -n "With Recovery Key (Recovery Mode ON, Dev Mode ON): " && \
649 { load_kernel_test "${INPUT_IMAGE}" "${try_key}" -b 3 >/dev/null 2>&1 && \
650 echo "YES"; } || echo "NO"
652 try_key=${KEY_DIR}/kernel_subkey.vbpubk
653 # The SSD key is only used in non-recovery mode.
654 echo -n "With SSD Key (Recovery Mode OFF, Dev Mode OFF): " && \
655 { load_kernel_test "${INPUT_IMAGE}" "${try_key}" -b 0 >/dev/null 2>&1 && \
656 echo "YES"; } || echo "NO"
657 echo -n "With SSD Key (Recovery Mode OFF, Dev Mode ON): " && \
658 { load_kernel_test "${INPUT_IMAGE}" "${try_key}" -b 1 >/dev/null 2>&1 && \
659 echo "YES"; } || echo "NO"
662 verify_image_rootfs "${INPUT_IMAGE}"
664 # TODO(gauravsh): Check embedded firmware AU signatures.
667 # Re-calculate recovery kernel hash.
669 update_recovery_kernel_hash() {
672 # Update the Kernel B hash in Kernel A command line
673 local old_kerna_config=$(grab_kernel_config "${image_bin}" 2)
674 local new_kernb=$(make_temp_file)
675 extract_image_partition ${image_bin} 4 ${new_kernb}
676 local new_kernb_hash=$(sha1sum ${new_kernb} | cut -f1 -d' ')
678 new_kerna_config=$(make_temp_file)
679 echo "$old_kerna_config" |
680 sed -e "s#\(kern_b_hash=\)[a-z0-9]*#\1${new_kernb_hash}#" \
681 > ${new_kerna_config}
682 echo "New config for kernel partition 2 is"
683 cat ${new_kerna_config}
685 local temp_kimagea=$(make_temp_file)
686 extract_image_partition ${image_bin} 2 ${temp_kimagea}
688 # Re-calculate kernel partition signature and command line.
689 local updated_kimagea=$(make_temp_file)
690 vbutil_kernel --repack ${updated_kimagea} \
691 --keyblock ${KEY_DIR}/recovery_kernel.keyblock \
692 --signprivate ${KEY_DIR}/recovery_kernel_data_key.vbprivk \
693 --version "${KERNEL_VERSION}" \
694 --oldblob ${temp_kimagea} \
695 --config ${new_kerna_config}
697 replace_image_partition ${image_bin} 2 ${updated_kimagea}
700 # Update the legacy bootloader templates in EFI partition if available.
701 # Args: IMAGE_BIN DM_PARTNO
702 update_legacy_bootloader() {
707 local esp_offset=$(( $(partoffset "${image}" "${esp_partnum}") * 512 ))
708 # Check if the image has an ESP partition.
709 if [[ "${esp_offset}" == "0" ]]; then
710 info "Not updating legacy bootloader configs: ${image}"
714 local esp_dir="$(make_temp_dir)"
715 # We use the 'unsafe' variant because the EFI system partition is vfat type
716 # and can be mounted in RW mode.
717 if ! _mount_image_partition_retry "${image}" "${esp_partnum}" \
719 error "Could not mount EFI partition for updating legacy bootloader cfg."
723 # If we can't find the dm parameter in the kernel config, bail out now.
724 local kernel_config=$(grab_kernel_config "${image}" "${dm_partno}")
725 local root_hexdigest="$(get_hash_from_config "${kernel_config}")"
726 if [[ -z "${root_hexdigest}" ]]; then
727 error "Couldn't grab root_digest from kernel partition ${dm_partno}"
728 error " (config: ${kernel_config})"
731 # Update syslinux configs for legacy BIOS systems.
732 if [[ -d "${esp_dir}/syslinux" ]]; then
733 local cfg=("${esp_dir}"/syslinux/*.cfg)
734 if ! sudo sed -i -r \
735 "s/\broot_hexdigest=[a-z0-9]+/root_hexdigest=${root_hexdigest}/g" \
737 error "Updating syslinux configs failed: '${cfg[*]}'"
741 # Update grub configs for EFI systems.
742 local grub_cfg="${esp_dir}/efi/boot/grub.cfg"
743 if [[ -f "${grub_cfg}" ]]; then
744 if ! sudo sed -i -r \
745 "s/\broot_hexdigest=[a-z0-9]+/root_hexdigest=${root_hexdigest}/g" \
747 error "Updating grub config failed: '${grub_cfg}'"
753 # Sign an image file with proper keys.
754 # Args: IMAGE_TYPE INPUT OUTPUT DM_PARTNO KERN_A_KEYBLOCK KERN_A_PRIVKEY \
755 # KERN_B_KEYBLOCK KERN_B_PRIVKEY
757 # A ChromiumOS image file (INPUT) always contains 2 partitions (kernel A & B).
758 # This function will rebuild hash data by DM_PARTNO, resign kernel partitions by
759 # their KEYBLOCK and PRIVKEY files, and then write to OUTPUT file. Note some
760 # special images (specified by IMAGE_TYPE, like 'recovery' or 'factory_install')
761 # may have additional steps (ex, tweaking verity hash or not stripping files)
762 # when generating output file.
764 local image_type="$1"
768 local kernA_keyblock="$5"
769 local kernA_privkey="$6"
770 local kernB_keyblock="$7"
771 local kernB_privkey="$8"
772 echo "Preparing ${image_type} image..."
773 cp --sparse=always "${input}" "${output}"
774 resign_firmware_payload "${output}"
775 # We do NOT strip /boot for factory installer, since some devices need it to
776 # boot EFI. crbug.com/260512 would obsolete this requirement.
778 # We also do NOT strip /boot for legacy BIOS or EFI devices. This is because
779 # "cros_installer postinst" on BIOS or EFI systems relies on presence of
780 # /boot in rootfs to update kernel. We infer the BIOS type from the kernel
782 local kerna_config="$(grab_kernel_config "${input}" 2)"
783 if [[ "${image_type}" != "factory_install" &&
784 " ${kerna_config} " != *" cros_legacy "* &&
785 " ${kerna_config} " != *" cros_efi "* ]]; then
786 "${SCRIPT_DIR}/strip_boot_from_image.sh" --image "${output}"
788 update_rootfs_hash "${output}" "${dm_partno}" \
789 "${kernA_keyblock}" "${kernA_privkey}" \
790 "${kernB_keyblock}" "${kernB_privkey}"
791 update_stateful_partition_vblock "${output}"
792 if [[ "${image_type}" == "recovery" ]]; then
793 update_recovery_kernel_hash "${output}"
795 if ! update_legacy_bootloader "${output}" "${dm_partno}"; then
796 # Error is already logged.
799 echo "Signed ${image_type} image output to ${output}"
806 for partnum in 2 4; do
807 echo "kernel config in partition number ${partnum}:"
808 grab_kernel_config "${INPUT_IMAGE}" ${partnum}
819 # All other signing commands take 4 to 5 args.
820 if [ -z "${OUTPUT_IMAGE}" ]; then
821 # Friendlier message.
822 usage "Missing output image name"
828 # If a version file was specified, read the firmware and kernel
829 # versions from there.
830 if [ -n "${VERSION_FILE}" ]; then
831 FIRMWARE_VERSION=$(sed -n 's#^firmware_version=\(.*\)#\1#pg' ${VERSION_FILE})
832 KERNEL_VERSION=$(sed -n 's#^kernel_version=\(.*\)#\1#pg' ${VERSION_FILE})
834 echo "Using firmware version: ${FIRMWARE_VERSION}"
835 echo "Using kernel version: ${KERNEL_VERSION}"
837 # Make all modifications on output copy.
838 if [[ "${TYPE}" == "ssd" || "${TYPE}" == "base" ]]; then
839 sign_image_file "SSD" "${INPUT_IMAGE}" "${OUTPUT_IMAGE}" 2 \
840 "${KEY_DIR}/kernel.keyblock" "${KEY_DIR}/kernel_data_key.vbprivk" \
841 "${KEY_DIR}/kernel.keyblock" "${KEY_DIR}/kernel_data_key.vbprivk"
842 elif [[ "${TYPE}" == "usb" ]]; then
843 sign_image_file "USB" "${INPUT_IMAGE}" "${OUTPUT_IMAGE}" 2 \
844 "${KEY_DIR}/recovery_kernel.keyblock" \
845 "${KEY_DIR}/recovery_kernel_data_key.vbprivk" \
846 "${KEY_DIR}/kernel.keyblock" \
847 "${KEY_DIR}/kernel_data_key.vbprivk"
848 elif [[ "${TYPE}" == "recovery" ]]; then
849 sign_image_file "recovery" "${INPUT_IMAGE}" "${OUTPUT_IMAGE}" 4 \
850 "${KEY_DIR}/recovery_kernel.keyblock" \
851 "${KEY_DIR}/recovery_kernel_data_key.vbprivk" \
852 "${KEY_DIR}/kernel.keyblock" \
853 "${KEY_DIR}/kernel_data_key.vbprivk"
854 elif [[ "${TYPE}" == "factory" ]] || [[ "${TYPE}" == "install" ]]; then
855 sign_image_file "factory_install" "${INPUT_IMAGE}" "${OUTPUT_IMAGE}" 2 \
856 "${KEY_DIR}/installer_kernel.keyblock" \
857 "${KEY_DIR}/installer_kernel_data_key.vbprivk" \
858 "${KEY_DIR}/kernel.keyblock" \
859 "${KEY_DIR}/kernel_data_key.vbprivk"
860 elif [[ "${TYPE}" == "firmware" ]]; then
861 if [[ -e "${KEY_DIR}/loem.ini" ]]; then
862 echo "LOEM signing not implemented yet for firmware images"
865 cp ${INPUT_IMAGE} ${OUTPUT_IMAGE}
866 sign_firmware ${OUTPUT_IMAGE} ${KEY_DIR} ${FIRMWARE_VERSION}
867 elif [[ "${TYPE}" == "nv_lp0_firmware" ]]; then
868 if [[ -e "${KEY_DIR}/loem.ini" ]]; then
869 echo "LOEM signing not implemented yet for nv_lp0_firmware images"
872 cp "${INPUT_IMAGE}" "${OUTPUT_IMAGE}"
873 sign_nv_lp0_firmware "${OUTPUT_IMAGE}" "${KEY_DIR}"
874 elif [[ "${TYPE}" == "kernel" ]]; then
875 if [[ -e "${KEY_DIR}/loem.ini" ]]; then
876 echo "LOEM signing not implemented yet for kernel images"
879 cp "${INPUT_IMAGE}" "${OUTPUT_IMAGE}"
880 sign_kernel "${OUTPUT_IMAGE}" "${KEY_DIR}" "${KERNEL_VERSION}"
881 elif [[ "${TYPE}" == "recovery_kernel" ]]; then
882 if [[ -e "${KEY_DIR}/loem.ini" ]]; then
883 echo "LOEM signing not implemented yet for recovery_kernel images"
886 cp "${INPUT_IMAGE}" "${OUTPUT_IMAGE}"
887 sign_recovery_kernel "${OUTPUT_IMAGE}" "${KEY_DIR}" "${KERNEL_VERSION}"
888 elif [[ "${TYPE}" == "update_payload" ]]; then
889 sign_update_payload ${INPUT_IMAGE} ${KEY_DIR} ${OUTPUT_IMAGE}
890 elif [[ "${TYPE}" == "accessory_usbpd" ]]; then
891 KEY_NAME="${KEY_DIR}/key_$(basename $(dirname ${INPUT_IMAGE}))"
892 if [[ ! -e "${KEY_NAME}.pem" ]]; then
893 KEY_NAME="${KEY_DIR}/key"
895 cp "${INPUT_IMAGE}" "${OUTPUT_IMAGE}"
896 futility sign --type usbpd1 --pem "${KEY_NAME}.pem" "${OUTPUT_IMAGE}"
897 elif [[ "${TYPE}" == "accessory_rwsig" ]]; then
898 KEY_NAME="${KEY_DIR}/key_$(basename $(dirname ${INPUT_IMAGE}))"
899 if [[ ! -e "${KEY_NAME}.vbprik2" ]]; then
900 KEY_NAME="${KEY_DIR}/key"
902 cp "${INPUT_IMAGE}" "${OUTPUT_IMAGE}"
903 futility sign --type rwsig --prikey "${KEY_NAME}.vbprik2" "${OUTPUT_IMAGE}"
905 echo "Invalid type ${TYPE}"