blob: 9a1a8ad6b8feab821a275ea828782e5d04174cbe [file] [edit]
# 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)