#!/bin/bash # # Copyright 2016 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # This script is a utility which allows to create differently signed CR50 # images from different sources. # set -e set -u progname=$(basename $0) OD="/usr/bin/od" tmpf="/tmp/bs_manifest.$$" trap "{ rm -rf [01].flat ${tmpf} ; }" EXIT usage() { local rv="${1}" cat < - sign the supplied elf files instead of the default ones. Handy if the builder generated files need to be signed help - print this message hex - generate hex output instead of binary (place in 0.signed.hex and 1.signed.hex in the local directory) prod - sign with prod key (no debug image will be signed) This script also allows to sign dev images for running on prod RO. To do that invoke this script as follows: H1_DEVIDS=' ' ${progname} [other options, if any] where are values reported by sysinfo command in the DEV_ID: line when run on the CR50 for which the image is built. The same values can be obtained in the lsusb command output: lsusb -vd 18d1:5014 | grep -i serial note that the lsusb reported values are in hex and need to be prefixed with 0x. Finally, this script also allows to specify the board ID fields of the RW headers. The fields come from the evironment variable CR50_BOARD_ID, which is required to include three colon separated fields. The first field is a four letter board RLZ code, the second field is board id mask in hex, no 0x prefix, and the third field - board ID flags, again, hex, no 0x prefix. CR50_BOARD_ID='XXYY:ffffff00:ff00' ${progname} [other options, if any] both H1_DEVIDS and CR50_BOARD_ID can be defined independently. EOF exit "${rv}" } # This function modifies the manifest to include device ID and board ID nodes, # if H1_DEVIDS and CR50_BOARD_ID are defined in the environment, respectively, tweak_manifest () { local sub # If defined, plug in dev ID nodes before the 'fuses' node. if [[ -z "${do_prod}" && -n "${H1_DEVIDS}" ]]; then echo "creating a customized DEV image for DEV IDS ${H1_DEVIDS}" sub=$(printf "\\\n \"DEV_ID0\": %s,\\\n \"DEV_ID1\": %s," ${H1_DEVIDS}) sed -i "s/\"fuses\": {/\"fuses\": {${sub}/" "${tmpf}" fi if [[ -z "${CR50_BOARD_ID}" ]]; then return fi # CR50_BOARD_ID is set, let's parse it and plug in the board ID related # nodes into manifest before the 'fuses' node. local bid_params local rlz bid_params=( $(echo $CR50_BOARD_ID | sed 's/:/ /g') ) # A very basic sanity check: it needs to consist of three colon separated # fields. if [[ ${#bid_params[@]} != 3 ]]; then echo "Wrong board ID string \"$CR50_BOARD_ID\"}" >&2 exit 1 fi # Convert board RLZ code from ASCII to hex rlz="0x$(echo -n ${bid_params[0]} | hexdump -ve '/1 "%02x"')" # Prepare text of all three board ID related nodes sub="$(printf "\\\n\"board_id\": %s,\\\n" "${rlz}")" sub+="$(printf "\"board_id_mask\": %s,\\\n" "0x${bid_params[1]}")" sub+="$(printf "\"board_id_flags\": %s,\\\n" "0x${bid_params[2]}")" sed -i "s/\"fuses\": {/${sub}\"fuses\": {/" "${tmpf}" } # This function accepts two arguments, names of two binary files. # # It searches the first passed in file for the first 8 bytes of the second # passed in file. The od utility is used to generate full hex dump of the # first file (16 bytes per line) and the first 8 bytes of the second file. # # grep is used to check if the pattern is present in the full dump. If the # pattern is not found, the first file is dumped again, this time with an 8 # byte offset into the file. This makes sure that if the match is present, but # is spanning two lines of the original hex dump, it is in a single dump line # the second time around. find_blob_in_blob() { local main_blob="${1}" local pattern_blob="${2}" local pattern local od_options="-An -tx1" # Get the first 8 bytes of the pattern blob. pattern="$(${OD} ${od_options} -N8 "${pattern_blob}")" if "${OD}" ${od_options} "${main_blob}" | grep "${pattern}" > /dev/null; then return 0 fi # Just in case pattern was wrapped in the previous od output, let's do it # again with an 8 bytes offset if "${OD}" ${od_options} -j8 "${main_blob}" | grep "${pattern}" > /dev/null; then return 0 fi return 1 } # This function accepts two arguments, names of the two elf files. # # The files are searched for test RMA public key patterns - x25519 or p256, # both files are supposed to have pattern of one of these keys and not the # other. If this holds true the function prints the public key base name. If # not both files include the same key, or include more than one key, the # function reports failure and exits the script. determine_rma_key_base() { local base_name="${EC_ROOT}/board/cr50/rma_key_blob" local curve local curves=( "x25519" "p256" ) local elf local elves=( "$1" "$2" ) local key_file local mask=1 local result=0 for curve in ${curves[@]}; do key_file="${base_name}.${curve}.test" for elf in ${elves[@]}; do if find_blob_in_blob "${elf}" "${key_file}"; then result=$(( result | mask )) fi mask=$(( mask << 1 )) done done case "${result}" in (3) curve="x25519";; (12) curve="p256";; (*) echo "could not determine key type in the elves" >&2 exit 1 ;; esac echo "${base_name}.${curve}" } SIGNER="cr50-codesigner" if ! which "${SIGNER}" 2>/dev/null > /dev/null; then echo "${SIGNER} is not available, try running 'sudo emerge cr50-utils'" >&2 exit 1 fi # This is where the new signed image will be pasted into. : ${RESULT_FILE=build/cr50/ec.bin} TMP_RESULT_FILE="${RESULT_FILE}.tmp" if [[ -z "${CROS_WORKON_SRCROOT}" ]]; then echo "${progname}: This script must run inside Chrome OS chroot" >&2 exit 1 fi : ${CR50_BOARD_ID=} : ${H1_DEVIDS=} EC_ROOT="${CROS_WORKON_SRCROOT}/src/platform/ec" EC_BIN_ROOT="${EC_ROOT}/util/signer" do_hex= do_b1= do_prod= # Prepare the default manifest. cp "${EC_BIN_ROOT}/ec_RW-manifest-dev.json" "${tmpf}" elves=( build/cr50/RW/ec.RW.elf build/cr50/RW/ec.RW_B.elf ) cd "${EC_ROOT}" while (( $# )); do param="${1}" case "${param}" in (hex) do_hex='true';; (b1) do_b1='true' sed -i 's/\(.*FW_DEFINED_DATA_BLK0.*\): 2/\1: 0/' "${tmpf}" ;; (elves) if [[ (( $# < 3 )) ]]; then echo "two elf file names are required" >&2 exit 1 fi elves=( $2 $3 ) shift shift ;; (prod) do_prod='true' ;; (help) usage 0 ;; (*) usage 1 ;; esac shift done if [[ -z "${do_hex}" && ! -f "${RESULT_FILE}" ]]; then echo "${RESULT_FILE} not found. Run 'make BOARD=cr50' first" >&2 exit 1 fi if [[ -n "${do_prod}" && -n "${do_b1}" ]]; then echo "can not build prod images for B1, sorry..." exit 1 fi rma_key_base="$(determine_rma_key_base ${elves[@]})" signer_command_params=() signer_command_params+=(--b -x ${EC_BIN_ROOT}/fuses.xml) if [[ -z "${do_prod}" ]]; then signer_command_params+=(-k ${EC_BIN_ROOT}/cr50_rom0-dev-blsign.pem.pub) else cp "${EC_BIN_ROOT}/ec_RW-manifest-prod.json" "${tmpf}" signer_command_params+=(-k ${EC_BIN_ROOT}/cr50_RW-prod.pem.pub) # Swap test public RMA server key with the prod version. signer_command_params+=(-S "${rma_key_base}.test","${rma_key_base}.prod") fi signer_command_params+=(-j ${tmpf}) if [[ -n "${do_hex}" ]]; then dst_suffix='signed.hex' else signer_command_params+=(--format=bin) dst_suffix='flat' fi tweak_manifest count=0 for elf in ${elves[@]}; do if [[ -n "${do_prod}" ]]; then if strings "${elf}" | grep -q "DBG/cr50"; then echo "Will not sign debug image with prod keys" >&2 exit 1 fi fi signed_file="${count}.${dst_suffix}" # Make sure output file is not owned by root touch "${signed_file}" if ! "${SIGNER}" ${signer_command_params[@]} \ -i ${elf} -o "${signed_file}"; then echo "${progname}: ${SIGNER} failed" >&2 exit 1 fi if find_blob_in_blob "${signed_file}" "${rma_key_base}.test"; then echo "${progname}: test RMA key in the signed image!" >&2 rm *."${dst_suffix}" exit 1 fi if ! find_blob_in_blob "${signed_file}" "${rma_key_base}.prod"; then echo "${progname}: prod RMA key not in the signed image!" >&2 rm *."${dst_suffix}" exit 1 fi : $(( count++ )) done if [[ -z "${do_hex}" ]]; then # Full binary image is required, paste the newly signed blobs into the # output image, preserving it in case dd fails for whatever reason. cp "${RESULT_FILE}" "${TMP_RESULT_FILE}" dd if="0.flat" of="${TMP_RESULT_FILE}" seek=16384 bs=1 conv=notrunc dd if="1.flat" of="${TMP_RESULT_FILE}" seek=278528 bs=1 conv=notrunc rm [01].flat mv "${TMP_RESULT_FILE}" "${RESULT_FILE}" fi echo "SUCCESS!!!"