| # 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) |