| # 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" |
| } |
| ] |
| } |
| ''') |
| ) |
| ) |