sign_nv_cbootimage: Update signing scripts for nv bootloader and lp0

Currently, nvidia tools do not provide the support to perform signing
of bootloader or lp0 image. Thus, the signing script is updated to
support this. Once, we have the required tools, this script can be
simplified again.

CQ-DEPEND=CL:*244234
BUG=chrome-os-partner:43018
BRANCH=none
TEST=Verified that signed bootloader and lp0 work fine on dev-PKC
fused devices.

Reference:
https://code.google.com/p/chrome-os-partner/issues/attachmentText?id=43018&aid=430180067000&name=sign-lp0.sh&token=ABZ6GAcjz4b2mEk55WqREzwTnWWpEyE_7A%3A1452631308268
https://code.google.com/p/chrome-os-partner/issues/attachmentText?id=43018&aid=430180060000&name=sign.sh&token=ABZ6GAcRky67XrzMOgKZfkJxr70qm-tTAg%3A1452631308240

Change-Id: Icb024a1d9a61714df6e52d94c96cf43481cac869
Signed-off-by: Furquan Shaikh <furquan@google.com>
Reviewed-on: https://chromium-review.googlesource.com/321459
Commit-Ready: Furquan Shaikh <furquan@chromium.org>
Tested-by: Furquan Shaikh <furquan@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/scripts/image_signing/sign_nv_cbootimage.sh b/scripts/image_signing/sign_nv_cbootimage.sh
index 71be794..da978e8 100755
--- a/scripts/image_signing/sign_nv_cbootimage.sh
+++ b/scripts/image_signing/sign_nv_cbootimage.sh
@@ -26,11 +26,132 @@
   exit 1
 }
 
-main() {
-  if [[ $# -ne 4 ]]; then
-    usage
-  fi
+# Signs bootloader image using pkc_key provided for given soc
+# Args: TYPE=bootloader PKC_KEY FIRMWARE_IMAGE SOC
+sign_bootloader() {
+  local type=$1
+  local pkc_key="$(readlink -f "$2")"
+  local firmware_image="$(readlink -f "$3")"
+  local soc=$4
 
+  local work_dir=$(make_temp_dir)
+  local config_file=$(make_temp_file)
+  local signed_fw=$(make_temp_file)
+
+  pushd "${work_dir}" >/dev/null
+
+  # Get bootloader length.
+  #
+  # Example:
+  # $ bct_dump image.fastboot.bin
+  # Version       = 0x00210001;
+  # BlockSize     = 0x00008000;
+  # ...
+  # ...
+  # # Bootloader[0].Length       = 69324;
+  # ...
+  # ...
+  #
+  # then, bl_length=69324 (size of bootloader that needs to be signed)
+  local bl_length=$(bct_dump "${firmware_image}" | \
+      sed -n '/Bootloader\[0\].Length/{ s/.*=\s*//; s/;//; p; q}')
+
+  # Extract bootloader to sign.
+  dd \
+    if="${firmware_image}" \
+    of="${signed_fw}.bl.tosig" \
+    count="${bl_length}" \
+    ibs=1 \
+    skip=32768 >/dev/null 2>&1
+
+  # Calculate rsa signature for bootloader.
+  openssl \
+    dgst -sha256 \
+    -sigopt rsa_padding_mode:pss \
+    -sigopt rsa_pss_saltlen:-1 \
+    -sign "${pkc_key}" \
+    -out "${signed_fw}.bl.sig" \
+    "${signed_fw}.bl.tosig"
+
+  # Update bootloader's rsa signature, aes hash and bct's aes hash.
+  echo "RsaPssSigBlFile = ${signed_fw}.bl.sig;" > "${config_file}"
+  echo "RehashBl;" >> "${config_file}"
+  cbootimage \
+    -s "${soc}" \
+    -u "${config_file}" \
+    "${firmware_image}" \
+    "${signed_fw}.tmp" >/dev/null
+
+  # Extract the part of bct which needs to be rsa signed.
+  dd \
+    if="${signed_fw}.tmp" \
+    of="${signed_fw}.bct.tosig" \
+    count=8944 \
+    ibs=1 \
+    skip=1296 >/dev/null 2>&1
+
+  # Calculate rsa signature for bct.
+  openssl \
+    dgst -sha256 \
+    -sigopt rsa_padding_mode:pss \
+    -sigopt rsa_pss_saltlen:-1 \
+    -sign "${pkc_key}" \
+    -out "${signed_fw}.bct.sig" \
+    "${signed_fw}.bct.tosig"
+
+  # Create public key modulus from key file.
+  openssl \
+    rsa -in "${pkc_key}" \
+    -noout \
+    -modulus \
+    -out "${signed_fw}.key.mod"
+
+  # Remove prefix.
+  cut \
+    -d= \
+    -f2 "${signed_fw}.key.mod" > "${signed_fw}.key.mod.tmp1"
+  dd \
+    if="${signed_fw}.key.mod.tmp1" \
+    of="${signed_fw}.key.mod.tmp" \
+    count=512 \
+    ibs=1 >/dev/null 2>&1
+
+  # Convert from hexdecimal to binary.
+  perl -pe 's/([0-9a-f]{2})/chr hex $1/gie' \
+    < "${signed_fw}.key.mod.tmp" \
+    > "${signed_fw}.key.mod.bin"
+
+  # Update bct's rsa signature and modulus.
+  echo "RsaPssSigBctFile = ${signed_fw}.bct.sig;" > "${config_file}"
+  echo "RsaKeyModulusFile = ${signed_fw}.key.mod.bin;" >> "${config_file}"
+  cbootimage \
+    -s "${soc}" \
+    -u "${config_file}" \
+    "${signed_fw}.tmp" \
+    "${signed_fw}" >/dev/null
+
+  # Calculate hash of public key modulus.
+  objcopy \
+    -I binary \
+    --reverse-bytes=256 \
+    "${signed_fw}.key.mod.bin" \
+    "${signed_fw}.key.mod.bin.rev"
+  openssl \
+    dgst -sha256 \
+    -binary \
+    -out "${signed_fw}.key.sha" \
+    "${signed_fw}.key.mod.bin.rev"
+
+  popd >/dev/null
+
+  # Copy signed firmware image and public key hash to current directory..
+  mv "${signed_fw}" "${firmware_image}"
+  mv "${signed_fw}.key.sha" "${firmware_image}.pubkey.sha"
+}
+
+# Signs lp0 firmware image using pkc_key provided for given soc
+# Args: TYPE=lp0_firmware PKC_KEY FIRMWARE_IMAGE SOC
+sign_lp0_firmware() {
   local type=$1
   local pkc_key="$(readlink -f "$2")"
   local firmware_image="$(readlink -f "$3")"
@@ -39,43 +160,103 @@
   local work_dir=$(make_temp_dir)
   local signed_fw=$(make_temp_file)
 
-  if [[ "${type}" == "bootloader" ]]; then
+  pushd "${work_dir}" >/dev/null
 
-    pushd "${work_dir}" >/dev/null
+  cp "${firmware_image}" "${signed_fw}"
 
-    cat >update.cfg <<EOF
-PkcKey = ${pkc_key}, --save;
-ReSignBl;
-EOF
+  # Extract the part of the binary which needs to be signed.
+  dd \
+    if="${firmware_image}" \
+    of="${signed_fw}.tosig" \
+    ibs=1 \
+    skip=544 >/dev/null 2>&1
 
-    # This also generates a file pubkey.sha which contains the hash of public
-    # key required by factory to burn into PKC fuses. Move pubkey.sha into
-    # ${firmware_image}.pubkey.sha.
-    cbootimage -s "${soc}" -u update.cfg "${firmware_image}" \
-      "${signed_fw}"
+  # Calculate rsa-pss signature.
+  openssl \
+    dgst -sha256 \
+    -sigopt rsa_padding_mode:pss \
+    -sigopt rsa_pss_saltlen:-1 \
+    -sign "${pkc_key}" \
+    -out "${signed_fw}.rsa.sig" \
+    "${signed_fw}.tosig"
 
-    popd >/dev/null
-    # Copy signed firmware image and public key hash to current directory.
-    mv "${work_dir}/pubkey.sha" "${firmware_image}.pubkey.sha"
-    mv "${signed_fw}" "${firmware_image}"
+  # Reverse rsa signature to meet tegra soc ordering requirement.
+  objcopy \
+    -I binary \
+    --reverse-bytes=256 \
+    "${signed_fw}.rsa.sig" \
+    "${signed_fw}.rsa.sig.rev"
 
-  elif [[ "${type}" == "lp0_firmware" ]]; then
+  # Inject rsa-pss signature into the binary image's header.
+  dd \
+    if="${signed_fw}.rsa.sig.rev" \
+    of="${signed_fw}" \
+    count=256 \
+    obs=1 \
+    seek=288 \
+    conv=notrunc >/dev/null 2>&1
 
-    pushd "${work_dir}" >/dev/null
+  # Generate public key modulus from key file.
+  openssl \
+    rsa -in "${pkc_key}" \
+    -noout \
+    -modulus \
+    -out "${signed_fw}.key.mod"
 
-    cat >update.cfg <<EOF
-PkcKey = ${pkc_key};
-RsaSign = 0x220,, 288, 16, Complete;
-EOF
+  # Remove prefix.
+  cut \
+    -d= \
+    -f2 "${signed_fw}.key.mod" > "${signed_fw}.key.mod.tmp1"
 
-    cbootimage --sign update.cfg "${firmware_image}" "${signed_fw}"
+  dd \
+    if="${signed_fw}.key.mod.tmp1" \
+    of="${signed_fw}.key.mod.tmp" \
+    count=512 \
+    ibs=1 >/dev/null 2>&1
 
-    popd >/dev/null
-    mv "${signed_fw}" "${firmware_image}"
+  # Convert from hexdecimal to binary.
+  perl -pe 's/([0-9a-f]{2})/chr hex $1/gie' \
+    < "${signed_fw}.key.mod.tmp" \
+    > "${signed_fw}.key.mod.bin"
 
-  else
+  # Reverse byte order.
+  objcopy \
+    -I binary \
+    --reverse-bytes=256 \
+    "${signed_fw}.key.mod.bin" \
+    "${signed_fw}.key.mod.bin.rev"
+
+  # Inject public key modulus into the binary image's header.
+  dd \
+    if="${signed_fw}.key.mod.bin.rev" \
+    of="${signed_fw}" \
+    count=256 \
+    obs=1 \
+    seek=16 \
+    conv=notrunc >/dev/null 2>&1
+
+  popd >/dev/null
+  mv "${signed_fw}" "${firmware_image}"
+}
+
+main() {
+  if [[ $# -ne 4 ]]; then
     usage
   fi
+
+  local type=$1
+
+  case ${type} in
+    bootloader)
+      sign_bootloader "$@"
+      ;;
+    lp0_firmware)
+      sign_lp0_firmware "$@"
+      ;;
+    *)
+      usage
+      ;;
+  esac
 }
 
 main "$@"