blob: dce0c3db7b323e30ca1ae84f52bd037400230ecc [file] [log] [blame]
# Copyright 2020 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
from datetime import datetime
DEPS = [
'flutter/repo_util',
'flutter/flutter_deps',
'recipe_engine/cipd',
'recipe_engine/context',
'recipe_engine/path',
'recipe_engine/platform',
'recipe_engine/properties',
'recipe_engine/raw_io',
'recipe_engine/step',
]
# This recipe builds the cosign CIPD package.
def RunSteps(api):
env = {}
env_prefixes = {}
api.flutter_deps.required_deps(
env, env_prefixes, api.properties.get('dependencies', [])
)
cosign_default_dir = api.path['start_dir'].join('cosign')
cosign_download_uris = GetLatestCosignDownloadUris(api)
for platform in ['darwin', 'linux', 'windows']:
cosign_dir = cosign_default_dir.join(platform)
DownloadCosignArtifacts(api, cosign_dir, platform, cosign_download_uris)
with api.context(env=env, env_prefixes=env_prefixes):
VerifyCosignArtifactSignature(api, cosign_dir, platform)
UploadCosignToCipd(api, cosign_dir, platform)
def GetLatestCosignDownloadUris(api):
"""Gets the list of latest sigstore/cosign binary urls.
Queries Github for a list of cosign releases, then picks the latest release,
queries the artifacts for that release, and returns a list of cosign binary
urls for that release.
Args:
api: luci api object.
"""
cosign_releases_raw_response = api.step('Get cosign releases from github',
cmd=['curl', 'https://api.github.com/repos/sigstore/cosign/releases'],
stdout=api.raw_io.output_text()
).stdout
cosign_releases = json.loads(cosign_releases_raw_response)
latest_release = max(
cosign_releases,
key=lambda release: datetime.strptime(
release.get('published_at'), '%Y-%m-%dT%H:%M:%SZ')
).get('url')
release_artifacts_raw_response = api.step(
'Get artifacts from sigstore/cosign for a specific release version',
cmd=['curl', latest_release],
stdout=api.raw_io.output_text()
).stdout
release_artifacts = json.loads(release_artifacts_raw_response)
release_artifacts_download_uris = list(
map(
lambda asset:
asset.get('browser_download_url'),
release_artifacts.get('assets')
)
)
return release_artifacts_download_uris
def DownloadCosignArtifacts(api, cosign_dir, platform, cosign_download_uris):
"""Downloads the latest cosign binary, certificate, and signature.
Takes a list of cosign download uris and finds the binary, certificate, and
signature urls based on the platform. Then, the three files are downloaded to
the cosign directory.
Args:
api: luci api object.
cosign_dir(str): the folder where the cosign binary/certificate/signature
will be downloaded.
platform(str): the platform of the binary that needs to be downloaded
(windows, linux, darwin)
cosign_download_uris(list(str)): a list of all the download uris for a
specific cosign release.
"""
exe = '.exe' if platform == 'windows' else ''
cosign_base_name = 'cosign-%s-amd64%s' % (platform, exe)
cosign_binary_download_uri = next(
filter(
lambda uri:
uri.endswith(cosign_base_name),
cosign_download_uris
)
)
cosign_certificate_download_uri = next(
filter(
lambda uri:
uri.endswith('%s-keyless.pem' % cosign_base_name),
cosign_download_uris
)
)
cosign_signature_download_uri = next(
filter(
lambda uri:
uri.endswith('%s-keyless.sig' % cosign_base_name),
cosign_download_uris
)
)
api.step(
'Download %s cosign binary' % platform,
[
'curl', '-L', cosign_binary_download_uri,
'-o', cosign_dir.join('bin', 'cosign%s' % exe),
'--create-dirs'
],
infra_step=True
)
api.step(
'Download %s cosign certificate' % platform,
[
'curl', '-L', cosign_certificate_download_uri,
'-o', cosign_dir.join("certificate", "cosign-cert%s.pem" % exe),
'--create-dirs'
],
infra_step=True
)
api.step(
'Download %s cosign signature' % platform,
[
'curl', '-L', cosign_signature_download_uri,
'-o', cosign_dir.join("certificate", "cosign-sig%s.sig" % exe),
'--create-dirs'
],
infra_step=True
)
if platform == 'linux' or platform == 'darwin':
api.step(
'Make %s cosign binary executable' % platform,
[
'chmod',
'755',
cosign_dir.join('bin', 'cosign%s' % exe)
]
)
def VerifyCosignArtifactSignature(api, cosign_dir, platform):
"""Verifies the cosign artifact is legitimate
Uses the cosign release signature and certificate to ensure that the cosign
binary is legitimate. This is done using the current version of cosign in
CIPD.
Args:
api: luci api object.
cosign_dir(str): the folder where the cosign binary/certificate/signature
is located.
platform(str): the platform of the binary (windows, linux, darwin)
"""
exe = '.exe' if platform == 'windows' else ''
api.step(
'Verify %s cosign binary is legitimate' % platform,
[
'cosign',
'verify-blob',
'--cert',
cosign_dir.join("certificate", "cosign-cert%s.pem" % exe),
'--signature',
cosign_dir.join("certificate", "cosign-sig%s.sig" % exe),
cosign_dir.join("bin", "cosign%s" % exe)
]
)
def UploadCosignToCipd(api, cosign_dir, platform):
"""Uploads cosign to CIPD.
Upload the cosign binary, certificate, and signature to CIPD and adds the
latest tag to this version.
Args:
api: luci api object.
cosign_dir(str): the folder where the cosign binary/certificate/signature
is located.
platform(str): the platform of the binary (windows, linux, darwin)
"""
cipd_platform = 'mac' if platform == 'darwin' else platform
cipd_package_name = 'flutter/tools/cosign/%s-amd64' % cipd_platform
cipd_zip_path = 'cosign.zip'
api.cipd.build(cosign_dir, cipd_zip_path, cipd_package_name)
api.cipd.register(cipd_package_name, cipd_zip_path, refs=['latest'])
def GenTests(api):
yield api.test(
'cosign',
api.properties(cosign_version='v1.0'),
api.platform('linux', 64),
api.step_data(
'Get cosign releases from github',
stdout=api.raw_io.output_text('''
[
{
"url": "https://api.github.com/releases/1",
"published_at": "2022-06-03T14:08:35Z"
},
{
"url": "https://api.github.com/releases/2",
"published_at": "2022-06-02T14:08:35Z"
}
]
''')
) +
api.step_data(
'Get artifacts from sigstore/cosign for a specific release version',
stdout=api.raw_io.output_text('''
{
"assets":[
{
"browser_download_url":"cosign-linux-amd64"
},
{
"browser_download_url":"cosign-linux-amd64-keyless.pem"
},
{
"browser_download_url":"cosign-linux-amd64-keyless.sig"
},
{
"browser_download_url":"cosign-darwin-amd64"
},
{
"browser_download_url":"cosign-darwin-amd64-keyless.pem"
},
{
"browser_download_url":"cosign-darwin-amd64-keyless.sig"
},
{
"browser_download_url":"cosign-windows-amd64.exe"
},
{
"browser_download_url":"cosign-windows-amd64.exe-keyless.pem"
},
{
"browser_download_url":"cosign-windows-amd64.exe-keyless.sig"
},
{
"browser_download_url":"some-other-artifact"
}
]
}
''')
)
)