| # Copyright 2026 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Sign image artifacts |
| |
| Mostly just a nice wrapper for sign_official_cos_build.sh. |
| This script is intended as syntax sugar for local developers, and as such |
| does not expose all functionalities of the underlying shell script. |
| |
| Examples: |
| cros sign-image --board=<board> --ima-mode=hash test - sign a test image with IMA hash. |
| cros sign-image --board=<board> --ima-mode=sign - sign a dev image with IMA signature. |
| """ |
| |
| import argparse |
| import glob |
| import logging |
| import os |
| import sys |
| |
| from chromite.cli import command |
| from chromite.lib import build_target_lib |
| from chromite.lib import commandline |
| from chromite.lib import constants |
| from chromite.lib import cros_build_lib |
| |
| |
| @command.command_decorator("sign-image") |
| class SignImageCommand(command.CliCommand): |
| """Sign a ChromiumOS image with dev/local keys (including IMA).""" |
| |
| @classmethod |
| def AddParser(cls, parser: commandline.ArgumentParser): |
| """Build the parser.""" |
| super().AddParser(parser) |
| parser.description = __doc__ |
| |
| parser.add_argument( |
| "-b", |
| "--board", |
| "--build-target", |
| dest="board", |
| default=cros_build_lib.GetDefaultBoard(), |
| help="The board of the image to sign.", |
| ) |
| parser.add_argument( |
| "--keys-dir", |
| default="/usr/share/vboot/devkeys", |
| help="Directory containing developer/local keys (Default: %(default)s).", |
| ) |
| parser.add_argument( |
| "--key-origin", |
| default="local", |
| help="Key origin: local or kms (Default: %(default)s).", |
| ) |
| parser.add_argument( |
| "--type", |
| default="base", |
| help="The signing script TYPE argument (Default: %(default)s).", |
| ) |
| parser.add_argument( |
| "--ima-mode", |
| choices=["disabled", "hash", "sign"], |
| default="disabled", |
| help="IMA signing mode: disabled, hash (digest-based file labeling), " |
| "or sign (signature-based file labeling) (Default: %(default)s).", |
| ) |
| parser.add_argument( |
| "--service-account", |
| default="", |
| help="Name of GCP service account to use when signing with Cloud KMS (Default: %(default)s).", |
| ) |
| parser.add_argument( |
| "images", |
| nargs="*", |
| default=["dev"], |
| help="List of images to sign (e.g. test, base, dev) (Default: %(default)s).", |
| ) |
| |
| def Run(self): |
| # Require that the command is executed inside the chroot |
| commandline.RunInsideChroot() |
| |
| if not self.options.board: |
| self.options.parser.error("--board is required") |
| |
| if self.options.type == "update_payload": |
| cros_build_lib.Die("update_payload not supported - use sign_official_cos_build.sh directly.") |
| script_path = os.path.join( |
| constants.SOURCE_ROOT, |
| "src/platform/vboot_reference/scripts/image_signing/sign_official_cos_build.sh", |
| ) |
| |
| # NOTE: The input and output paths are currently hardcoded to the board's build |
| # directory. This is sufficient for local developer usage. However, if we ever |
| # want to support `--type update_payload`, these paths will need to be made |
| # configurable (e.g. via command line options) to allow passing a custom 32-byte |
| # update payload hash file. |
| image_dir = os.path.join(constants.DEFAULT_BUILD_ROOT, "images", self.options.board, "latest") |
| |
| for image_name in self.options.images: |
| image_file = f"chromiumos_{image_name}_image.bin" |
| input_image = os.path.join(image_dir, image_file) |
| output_image = os.path.join(image_dir, f"chromiumos_{image_name}_image_signed.bin") |
| |
| if not os.path.exists(input_image): |
| cros_build_lib.Die(f"Image not found at {input_image}") |
| |
| # Advanced parameters of the underlying script, such as version_file are currently |
| # omitted because local developers do not typically need to sign images with |
| # custom/incremented rollback protection versions. |
| cmd = [ |
| "sudo", |
| script_path, |
| self.options.type, |
| self.options.key_origin, |
| input_image, |
| self.options.keys_dir, |
| output_image, |
| self.options.service_account, |
| f"ima_{self.options.ima_mode}", |
| ] |
| |
| cros_build_lib.run(cmd) |
| |
| logging.info("Successfully signed %s image.", image_name) |
| logging.info("Output image: %s", output_image) |