Home | History | Annotate | Download | only in image_signing
      1 #!/bin/sh
      2 #
      3 # Copyright (c) 2012 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.
      6 #
      7 # This script can change key (usually developer keys) in a firmware binary
      8 # image or system live firmware (EEPROM), and assign proper HWID, FLAGS as well.
      9 
     10 SCRIPT_BASE="$(dirname "$0")"
     11 . "$SCRIPT_BASE/common_minimal.sh"
     12 load_shflags || exit 1
     13 
     14 # Constants used by DEFINE_*
     15 VBOOT_BASE='/usr/share/vboot'
     16 DEFAULT_KEYS_FOLDER="$VBOOT_BASE/devkeys"
     17 DEFAULT_BACKUP_FOLDER='/mnt/stateful_partition/backups'
     18 
     19 # DEFINE_string name default_value description flag
     20 DEFINE_string from "" "Path of input file (empty for system live firmware)" "f"
     21 DEFINE_string to "" "Path of output file (empty for system live firmware)" "t"
     22 DEFINE_string keys "$DEFAULT_KEYS_FOLDER" "Path to folder of dev keys" "k"
     23 DEFINE_string preamble_flags "" "Override preamble flags value. Known values:
     24                         0: None. (Using RW to boot in normal. aka, two-stop)
     25                         1: VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL (one-stop)" "p"
     26 DEFINE_boolean mod_gbb_flags \
     27   $FLAGS_TRUE "Modify GBB flags to enable developer friendly features" ""
     28 DEFINE_boolean force_backup \
     29   $FLAGS_TRUE "Create backup even if source is not live" ""
     30 DEFINE_string backup_dir \
     31   "$DEFAULT_BACKUP_FOLDER" "Path of directory to store firmware backups" ""
     32 
     33 # Parse command line
     34 FLAGS "$@" || exit 1
     35 eval set -- "$FLAGS_ARGV"
     36 
     37 # Globals
     38 # ----------------------------------------------------------------------------
     39 set -e
     40 
     41 # the image we are (temporary) working with
     42 IMAGE="$(make_temp_file)"
     43 IMAGE="$(readlink -f "$IMAGE")"
     44 
     45 # a log file to keep the output results of executed command
     46 EXEC_LOG="$(make_temp_file)"
     47 
     48 # Functions
     49 # ----------------------------------------------------------------------------
     50 
     51 # Disables write protection status registers
     52 disable_write_protection() {
     53   # No need to change WP status in file mode
     54   if [ -n "$FLAGS_to" ]; then
     55     return $FLAGS_TRUE
     56   fi
     57 
     58   # --wp-disable command may return success even if WP is still enabled,
     59   # so we should use --wp-status to verify the results.
     60   echo "Disabling system software write protection status..."
     61   (flashrom --wp-disable && flashrom --wp-status) 2>&1 |
     62     tee "$EXEC_LOG" |
     63     grep -q '^WP: .* is disabled\.$'
     64 }
     65 
     66 # Reads $IMAGE from $FLAGS_from
     67 read_image() {
     68   if [ -z "$FLAGS_from" ]; then
     69     echo "Reading system live firmware..."
     70     if is_debug_mode; then
     71       flashrom -V -r "$IMAGE"
     72     else
     73       flashrom -r "$IMAGE" >"$EXEC_LOG" 2>&1
     74     fi
     75   else
     76     debug_msg "reading from file: $FLAGS_from"
     77     cp -f "$FLAGS_from" "$IMAGE"
     78   fi
     79 }
     80 
     81 # Writes $IMAGE to $FLAGS_to
     82 write_image() {
     83   if [ -z "$FLAGS_to" ]; then
     84     echo "Writing system live firmware..."
     85     # TODO(hungte) we can enable partial write to make this faster
     86     if is_debug_mode; then
     87       flashrom -V -w "$IMAGE"
     88     else
     89       flashrom -w "$IMAGE" >"$EXEC_LOG" 2>&1
     90     fi
     91   else
     92     debug_msg "writing to file: $FLAGS_to"
     93     cp -f "$IMAGE" "$FLAGS_to"
     94     chmod a+r "$FLAGS_to"
     95   fi
     96 }
     97 
     98 # Converts HWID from $1 to proper format with "DEV" extension
     99 echo_dev_hwid() {
    100   local hwid="$1"
    101   local hwid_no_dev="${hwid% DEV}"
    102 
    103   # NOTE: Some DEV firmware image files may put GUID in HWID.
    104   # These are not officially supported and they will see "{GUID} DEV".
    105 
    106   if [ "$hwid" != "$hwid_no_dev" ]; then
    107     hwid="$hwid_no_dev"
    108   fi
    109   local hwid_dev="$hwid DEV"
    110   debug_msg "echo_dev_hwid: [$1] -> [$hwid_dev]"
    111   echo "$hwid_dev"
    112 }
    113 
    114 # Main
    115 # ----------------------------------------------------------------------------
    116 main() {
    117   # Check parameters
    118   local root_pubkey="$FLAGS_keys/root_key.vbpubk"
    119   local recovery_pubkey="$FLAGS_keys/recovery_key.vbpubk"
    120   local firmware_keyblock="$FLAGS_keys/firmware.keyblock"
    121   local firmware_prvkey="$FLAGS_keys/firmware_data_key.vbprivk"
    122   local dev_firmware_keyblock="$FLAGS_keys/dev_firmware.keyblock"
    123   local dev_firmware_prvkey="$FLAGS_keys/dev_firmware_data_key.vbprivk"
    124   local kernel_sub_pubkey="$FLAGS_keys/kernel_subkey.vbpubk"
    125   local is_from_live=0
    126   local backup_image=
    127 
    128   debug_msg "Prerequisite check"
    129   ensure_files_exist \
    130     "$root_pubkey" \
    131     "$recovery_pubkey" \
    132     "$firmware_keyblock" \
    133     "$firmware_prvkey" \
    134     "$kernel_sub_pubkey" ||
    135     exit 1
    136 
    137   if [ -z "$FLAGS_from" ]; then
    138     is_from_live=1
    139   else
    140     ensure_files_exist "$FLAGS_from" || exit 1
    141   fi
    142 
    143   debug_msg "Checking software write protection status"
    144   disable_write_protection ||
    145     if is_debug_mode; then
    146       err_die "Failed to disable WP. Diagnose Message: $(cat "$EXEC_LOG")"
    147     else
    148       err_die "Write protection is still enabled. " \
    149               "Please verify that hardware write protection is disabled."
    150     fi
    151 
    152   debug_msg "Pulling image to $IMAGE"
    153   (read_image && [ -s "$IMAGE" ]) ||
    154     err_die "Failed to read image. Error message: $(cat "$EXEC_LOG")"
    155 
    156   debug_msg "Prepare to backup the file"
    157   if [ -n "$is_from_live" -o $FLAGS_force_backup = $FLAGS_TRUE ]; then
    158     backup_image="$(make_temp_file)"
    159     debug_msg "Creating backup file to $backup_image..."
    160     cp -f "$IMAGE" "$backup_image"
    161   fi
    162 
    163   debug_msg "Detecting developer firmware keyblock"
    164   local expanded_firmware_dir="$(make_temp_dir)"
    165   local use_devfw_keyblock="$FLAGS_FALSE"
    166   (cd "$expanded_firmware_dir"; dump_fmap -x "$IMAGE" >/dev/null 2>&1) ||
    167     err_die "Failed to extract firmware image."
    168   if [ -f "$expanded_firmware_dir/VBLOCK_A" ]; then
    169     local has_dev=$FLAGS_TRUE has_norm=$FLAGS_TRUE
    170     # In output of vbutil_keyblock, "!DEV" means "bootable on normal mode" and
    171     # "DEV" means "bootable on developer mode". Here we try to match the pattern
    172     # in output of vbutil_block, and disable the flags (has_dev, has_norm) if
    173     # the pattern was not found.
    174     vbutil_keyblock --unpack "$expanded_firmware_dir/VBLOCK_A" |
    175       grep -qw '!DEV' || has_norm=$FLAGS_FALSE
    176     vbutil_keyblock --unpack "$expanded_firmware_dir/VBLOCK_A" |
    177       grep -qw '[^!]DEV' || has_dev=$FLAGS_FALSE
    178     if [ "$has_norm" = "$FLAGS_FALSE" -a "$has_dev" = "$FLAGS_TRUE" ]; then
    179       use_devfw_keyblock=$FLAGS_TRUE
    180     fi
    181   fi
    182 
    183   if [ "$use_devfw_keyblock" = "$FLAGS_TRUE" ]; then
    184     echo "Using keyblocks (developer, normal)..."
    185   else
    186     echo "Using keyblocks (normal, normal)..."
    187     dev_firmware_prvkey="$firmware_prvkey"
    188     dev_firmware_keyblock="$firmware_keyblock"
    189   fi
    190 
    191   # TODO(hungte) We can use vbutil_firmware to check if the current firmware is
    192   # valid so that we know keys and vbutil_firmware are all working fine.
    193 
    194   echo "Preparing new firmware image..."
    195 
    196   debug_msg "Resign the firmware code (A/B) with new keys"
    197   # Note resign_firmwarefd.sh needs the original rootkey to determine firmware
    198   # body size, so we must resign image before changing GBB rootkey.
    199 
    200   local unsigned_image="$(make_temp_file)"
    201   local optional_opts=""
    202   if [ -n "$FLAGS_preamble_flags" ]; then
    203     # optional_opts: VERSION FLAGS
    204     debug_msg "Setting new VERSION=1, FLAGS=$FLAGS_preamble_flags"
    205     optional_opts="1 $FLAGS_preamble_flags"
    206   fi
    207   cp -f "$IMAGE" "$unsigned_image"
    208   "$SCRIPT_BASE/resign_firmwarefd.sh" \
    209     "$unsigned_image" \
    210     "$IMAGE" \
    211     "$firmware_prvkey" \
    212     "$firmware_keyblock" \
    213     "$dev_firmware_prvkey" \
    214     "$dev_firmware_keyblock" \
    215     "$kernel_sub_pubkey" \
    216     $optional_opts >"$EXEC_LOG" 2>&1 ||
    217     err_die "Failed to re-sign firmware. (message: $(cat "$EXEC_LOG"))"
    218     if is_debug_mode; then
    219       cat "$EXEC_LOG"
    220     fi
    221 
    222   debug_msg "Extract current HWID"
    223   local old_hwid
    224   old_hwid="$(gbb_utility --get --hwid "$IMAGE" 2>"$EXEC_LOG" |
    225               sed -rne 's/^hardware_id: (.*)$/\1/p')"
    226 
    227   debug_msg "Decide new HWID"
    228   [ -z "$old_hwid" ] &&
    229     err_die "Cannot find current HWID. (message: $(cat "$EXEC_LOG"))"
    230   local new_hwid="$(echo_dev_hwid "$old_hwid")"
    231 
    232   local old_gbb_flags
    233   old_gbb_flags="$(gbb_utility --get --flags "$IMAGE" 2>"$EXEC_LOG" |
    234                    sed -rne 's/^flags: (.*)$/\1/p')"
    235   debug_msg "Decide new GBB flags from: $old_gbb_flags"
    236   [ -z "$old_gbb_flags" ] &&
    237     err_die "Cannot find GBB flags. (message: $(cat "$EXEC_LOG"))"
    238   # 0x30: GBB_FLAG_FORCE_DEV_BOOT_USB | GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK
    239   local new_gbb_flags="$((old_gbb_flags | 0x30))"
    240 
    241   debug_msg "Replace GBB parts (gbb_utility allows changing on-the-fly)"
    242   gbb_utility --set \
    243     --hwid="$new_hwid" \
    244     --rootkey="$root_pubkey" \
    245     --recoverykey="$recovery_pubkey" \
    246     "$IMAGE" >"$EXEC_LOG" 2>&1 ||
    247     err_die "Failed to change GBB Data. (message: $(cat "$EXEC_LOG"))"
    248 
    249   # Old firmware does not support GBB flags, so let's make it an exception.
    250   if [ "$FLAGS_mod_gbb_flags" = "$FLAGS_TRUE" ]; then
    251     debug_msg "Changing GBB flags from $old_gbb_flags to $new_gbb_flags"
    252     gbb_utility --set \
    253       --flags="$new_gbb_flags" \
    254       "$IMAGE" >"$EXEC_LOG" 2>&1 ||
    255       echo "Warning: GBB flags ($old_gbb_flags -> $new_gbb_flags) can't be set."
    256   fi
    257 
    258   # TODO(hungte) compare if the image really needs to be changed.
    259 
    260   debug_msg "Check if we need to make backup file(s)"
    261   if [ -n "$backup_image" ]; then
    262     local backup_hwid_name="$(echo "$old_hwid" | sed 's/ /_/g')"
    263     local backup_date_time="$(date +'%Y%m%d_%H%M%S')"
    264     local backup_file_name="firmware_${backup_hwid_name}_${backup_date_time}.fd"
    265     local backup_file_path="$FLAGS_backup_dir/$backup_file_name"
    266     if mkdir -p "$FLAGS_backup_dir" &&
    267        cp -f "$backup_image" "$backup_file_path"; then
    268       true
    269     elif cp -f "$backup_image" "/tmp/$backup_file_name"; then
    270       backup_file_path="/tmp/$backup_file_name"
    271     else
    272       backup_file_path=''
    273     fi
    274     if [ -n "$backup_file_path" -a -s "$backup_file_path" ]; then
    275       # TODO(hungte) maybe we can wrap the flashrom by 'make_dev_firmware.sh -r'
    276       # so that the only command to remember would be make_dev_firmware.sh.
    277       echo "
    278       Backup of current firmware image is stored in:
    279         $backup_file_path
    280       Please copy the backup file to a safe place ASAP.
    281 
    282       To stop using devkeys and restore original firmware, execute command:
    283         flashrom -w [PATH_TO_BACKUP_IMAGE]
    284       Ex: flashrom -w $backup_file_path
    285       "
    286     else
    287       echo "WARNING: Cannot create file in $FLAGS_backup_dir... Ignore backups."
    288     fi
    289   fi
    290 
    291   # TODO(hungte) use vbutil_firmware to check if the new firmware is valid.
    292   # Or, do verification in resign_firmwarefd.sh and trust it.
    293 
    294   debug_msg "Write the image"
    295   write_image ||
    296     err_die "Failed to write image. Error message: $(cat "$EXEC_LOG")"
    297 
    298   debug_msg "Complete."
    299   if [ -z "$FLAGS_to" ]; then
    300     echo "Successfully changed firmware to Developer Keys. New HWID: $new_hwid"
    301   else
    302     echo "Firmware '$FLAGS_to' now uses Developer Keys. New HWID: $new_hwid"
    303   fi
    304 }
    305 
    306 main
    307