sign_official_cos_build: Support signature-based IMA

Previously, the script only supported hash-based IMA. This commit
replaces the "ima" option with "ima_hash" and "ima_sign" options. To
maintain backwards compatibility, "ima" is an aliase for "ima_hash".
The "ima_sign" option requires an ima_key.pem in the key dir.

BUG=b/465211305
TEST=presubmit
RELEASE_NOTE=None

Change-Id: I61440922cd1689534fc01c46145e2d080a64c1db
Reviewed-on: https://cos-review.googlesource.com/c/third_party/platform/vboot_reference/+/153404
Reviewed-by: Robert Kolchmeyer <rkolchmeyer@google.com>
Tested-by: Cusky Presubmit Bot <presubmit@cos-infra-prod.iam.gserviceaccount.com>
diff --git a/scripts/image_signing/sign_official_cos_build.sh b/scripts/image_signing/sign_official_cos_build.sh
index 3b22d4e..c98297d 100755
--- a/scripts/image_signing/sign_official_cos_build.sh
+++ b/scripts/image_signing/sign_official_cos_build.sh
@@ -32,7 +32,8 @@
 key_origin: "local" for local keys, or "kms" for Cloud KMS keys.
 output_image: File name of the signed output image
 service_account: Name of GCP service account to use when signing with Cloud KMS
-ima: Set to "ima" to sign the root file system for IMA
+ima: Set to "ima_hash" for hash-based IMA, or "ima_sign" for signature-based IMA.
+     ima_sign requires an IMA private key at /path/to/keys/dir/ima_key.pem.
 version_file: File name of where to read the kernel and firmware versions.
 
 If you are signing an image, you must specify an [output_image] and
@@ -83,6 +84,26 @@
 IMA=$7
 VERSION_FILE=$8
 
+case "${IMA}" in
+  ima_sign)
+    [[ -f "${KEY_DIR}/ima_key.pem" ]] || die "ima_sign requires an IMA signing key at ${KEY_DIR}/ima_key.pem"
+    ;;
+
+  ima_hash | ima_disabled | "")
+    # Valid options; do nothing
+    :
+    ;;
+
+  ima)
+    warn "The 'ima' option is deprecated, use 'ima_hash' instead."
+    IMA="ima_hash"
+    ;;
+
+  *)
+    die "Invalid IMA option: '${IMA}'. Allowed values: ima_hash, ima_sign, ima_disabled, or empty string."
+    ;;
+esac
+
 FIRMWARE_VERSION=1
 KERNEL_VERSION=1
 
@@ -92,7 +113,7 @@
 if [[ "${KEY_ORIGIN}" == "kms" ]]; then
   prereqs+=(kms_signer)
 fi
-if [[ "${IMA}" == "ima" ]]; then
+if [[ "${IMA}" == "ima_hash" || "${IMA}" == "ima_sign" ]]; then
   prereqs+=(evmctl)
 fi
 for prereq in ${prereqs[@]}; do
@@ -534,10 +555,11 @@
 }
 
 # Set security.ima xattr on all files in given file system.
-# Currently sets hashes instead of signatures.
-# Args: LOOPDEV
+# Supports both hash-based IMA (ima_hash) and signature-based IMA (ima_sign).
+# Args: LOOPDEV IMA_MODE
 sign_ima() {
   local loopdev="$1"
+  local ima_mode="$2"
   local rootfs_dir="$(make_temp_dir)"
   mount_loop_image_partition "${loopdev}" 3 "${rootfs_dir}"
   # `evmctl sign -r` does more than try to set the security.ima xattr, and spams
@@ -548,7 +570,11 @@
   sudo find "${rootfs_dir}" -type f | while read -r f; do
     if ! getfattr -n security.ima "$f" > /dev/null 2>&1 ; then
       echo "Signing $f"
-      sudo evmctl ima_hash $f
+      if [[ "${ima_mode}" == "ima_sign" ]]; then
+        sudo evmctl ima_sign --hashalgo sha256 --key "${KEY_DIR}/ima_key.pem" "$f"
+      else
+        sudo evmctl ima_hash --hashalgo sha256 "$f"
+      fi
       echo "Done signing $f"
     else
       echo "security.ima xattr already present for $f"
@@ -791,8 +817,8 @@
 
   sign_uefi_binaries "${loopdev}" "${service_account}"
 
-  if [[ "${ima}" == "ima" ]]; then
-    sign_ima "${loopdev}"
+  if [[ "${ima}" == "ima_hash" || "${ima}" == "ima_sign" ]]; then
+    sign_ima "${loopdev}" "${ima}"
   fi
 
   # We do NOT strip /boot for factory installer, since some devices need it to