blob: 03a1c34535b8557e4e3b75c108c0189b906e7075 [file] [log] [blame]
# Copyright 2019 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
The signing module defines the various binary pieces of the Chrome application
bundle that need to be signed, as well as providing utilities to sign them.
"""
import os.path
import re
from . import commands
def _linker_signed_arm64_needs_force(path):
"""Detects linker-signed arm64 code that can only be signed with --force
on this system.
Args:
path: A path to a code object to test.
Returns:
True if --force must be used with codesign --sign to successfully sign
the code, False otherwise.
"""
# On macOS 11.0 and later, codesign handles linker-signed code properly
# without the --force hand-holding. Check OS >= 10.16 because that's what
# Python will think the OS is if it wasn't built with the 11.0 SDK or later.
if commands.macos_version() >= [10, 16]:
return False
# Look just for --arch=arm64 because that's the only architecture that has
# linker-signed code by default. If this were used with universal code (if
# there were any), --display without --arch would default to the native
# architecture, which almost certainly wouldn't be arm64 and therefore would
# be wrong.
(returncode, stdout, stderr) = commands.lenient_run_command_output(
['codesign', '--display', '--verbose', '--arch=arm64', '--', path])
if returncode != 0:
# Problem running codesign? Don't make the error about this confusing
# function. Just return False and let some less obscure codesign
# invocation be the error. Not signed at all? No problem. No arm64 code?
# No problem either. Not code at all? File not found? Well, those don't
# count as linker-signed either.
return False
# Yes, codesign --display puts all of this on stderr.
match = re.search(b'^CodeDirectory .* flags=(0x[0-9a-f]+)( |\().*$', stderr,
re.MULTILINE)
if not match:
return False
flags = int(match.group(1), 16)
# This constant is from MacOSX11.0.sdk <Security/CSCommon.h>
# SecCodeSignatureFlags kSecCodeSignatureLinkerSigned.
LINKER_SIGNED_FLAG = 0x20000
return (flags & LINKER_SIGNED_FLAG) != 0
def sign_part(paths, config, part):
"""Code signs a part.
Args:
paths: A |model.Paths| object.
conifg: The |model.CodeSignConfig| object.
part: The |model.CodeSignedProduct| to sign. The product's |path| must
be in |paths.work|.
"""
command = ['codesign', '--sign', config.identity]
path = os.path.join(paths.work, part.path)
if _linker_signed_arm64_needs_force(path):
command.append('--force')
if config.notarize.should_notarize():
# If the products will be notarized, the signature requires a secure
# timestamp.
command.append('--timestamp')
if part.sign_with_identifier:
command.extend(['--identifier', part.identifier])
reqs = part.requirements_string(config)
if reqs:
command.extend(['--requirements', '=' + reqs])
if part.options:
command.extend(['--options', part.options.to_comma_delimited_string()])
if part.entitlements:
command.extend(
['--entitlements',
os.path.join(paths.work, part.entitlements)])
command.append(path)
commands.run_command(command)
def verify_part(paths, part):
"""Displays and verifies the code signature of a part.
Args:
paths: A |model.Paths| object.
part: The |model.CodeSignedProduct| to verify. The product's |path|
must be in |paths.work|.
"""
verify_options = part.verify_options.to_list(
) if part.verify_options else []
part_path = os.path.join(paths.work, part.path)
commands.run_command([
'codesign', '--display', '--verbose=5', '--requirements', '-', part_path
])
commands.run_command(['codesign', '--verify', '--verbose=6'] +
verify_options + [part_path])
def validate_app(paths, config, part):
"""Displays and verifies the signature of a CodeSignedProduct.
Args:
paths: A |model.Paths| object.
conifg: The |model.CodeSignConfig| object.
part: The |model.CodeSignedProduct| for the outer application bundle.
"""
app_path = os.path.join(paths.work, part.path)
commands.run_command([
'codesign', '--display', '--requirements', '-', '--verbose=5', app_path
])
if config.run_spctl_assess:
commands.run_command(['spctl', '--assess', '-vv', app_path])