diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 60655544418..8604dac5cdc 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -29,32 +29,34 @@ on: - main - release-* -permissions: read-all +permissions: {} jobs: build: name: build runs-on: ubuntu-latest + if: github.repository == 'sigstore/cosign' + permissions: id-token: write contents: read steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0 + - uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: '1.21' + go-version: '1.22' check-latest: true # will use the latest release available for ko - - uses: ko-build/setup-ko@ace48d793556083a76f1e3e6068850c1f4a369aa # v0.6 + - uses: ko-build/setup-ko@3aebd0597dc1e9d1a26bcfdb7cbeb19c131d3037 # v0.7 - name: Set up Cloud SDK - uses: google-github-actions/auth@55bd3a7c6e2ae7cf1877fd1ccb9d54c0503c457c # v2.1.2 + uses: google-github-actions/auth@f112390a2df9932162083945e46d439060d66ec2 # v2.1.4 with: workload_identity_provider: 'projects/498091336538/locations/global/workloadIdentityPools/githubactions/providers/sigstore-cosign' service_account: 'github-actions@projectsigstore.iam.gserviceaccount.com' @@ -62,8 +64,15 @@ jobs: - name: creds run: gcloud auth configure-docker --quiet + - name: Login to GitHub Container Registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: containers-cosign run: make sign-ci-containers env: - KO_PREFIX: gcr.io/projectsigstore/cosign/ci + KO_PREFIX: ghcr.io/sigstore/cosign/cosign/ci COSIGN_PASSWORD: "${{secrets.COSIGN_PASSWORD}}" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4b29a2d1dde..b79d9be8170 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,11 +30,15 @@ on: env: CODEQL_EXTRACTOR_GO_BUILD_TRACING: true +permissions: {} + jobs: analyze: name: Analyze runs-on: ubuntu-latest + if: github.repository == 'sigstore/cosign' + permissions: security-events: write actions: read @@ -47,7 +51,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Utilize Go Module Cache uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -60,9 +64,9 @@ jobs: ${{ runner.os }}-go- - name: Set correct version of Golang to use during CodeQL run - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: '1.21' + go-version: '1.22' check-latest: true # Initializes the CodeQL tools for scanning. diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml new file mode 100644 index 00000000000..70f7e463444 --- /dev/null +++ b/.github/workflows/conformance.yml @@ -0,0 +1,42 @@ +# Copyright 2024 The Sigstore Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Conformance Tests + +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + conformance: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + go-version: '1.22' + check-latest: true + + - run: make cosign conformance + + - uses: sigstore/sigstore-conformance@ee4de0e602873beed74cf9e49d5332529fe69bf6 # v0.0.11 + with: + entrypoint: ${{ github.workspace }}/conformance diff --git a/.github/workflows/cut-release.yml b/.github/workflows/cut-release.yml index 41fc01b999a..57e83b80061 100644 --- a/.github/workflows/cut-release.yml +++ b/.github/workflows/cut-release.yml @@ -1,3 +1,18 @@ +# +# Copyright 2024 The Sigstore Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + name: Cut Release on: @@ -21,7 +36,7 @@ concurrency: cut-release jobs: cut-release: name: Cut release - uses: sigstore/sigstore/.github/workflows/reusable-release.yml@main + uses: sigstore/community/.github/workflows/reusable-release.yml@main permissions: id-token: write contents: read diff --git a/.github/workflows/depsreview.yml b/.github/workflows/depsreview.yml index e7d5dea8816..9f8b95adc29 100644 --- a/.github/workflows/depsreview.yml +++ b/.github/workflows/depsreview.yml @@ -15,10 +15,15 @@ name: 'Dependency Review' on: [pull_request] -permissions: - contents: read -# +permissions: {} + jobs: dependency-review: name: License and Vulnerability Scan + + if: github.repository == 'sigstore/cosign' + + permissions: + contents: read + uses: sigstore/community/.github/workflows/reusable-dependency-review.yml@9b1b5aca605f92ec5b1bf3681b1e61b3dbc420cc diff --git a/.github/workflows/donotsubmit.yaml b/.github/workflows/donotsubmit.yaml index 066cae98dd7..9e30c7a316c 100644 --- a/.github/workflows/donotsubmit.yaml +++ b/.github/workflows/donotsubmit.yaml @@ -1,20 +1,41 @@ +# +# Copyright 2024 The Sigstore Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + name: Do Not Submit on: pull_request: - branches: [ 'main', 'release-*' ] + branches: + - 'main' + - 'release-*' -permissions: read-all +permissions: {} jobs: - donotsubmit: name: Do Not Submit runs-on: ubuntu-latest + if: github.repository == 'sigstore/cosign' + + permissions: + contents: read + steps: - name: Check out code - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v2.4.0 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v2.4.0 - name: Do Not Submit uses: chainguard-dev/actions/donotsubmit@84c993eaf02da1c325854fb272a4df9184bd80fc # main diff --git a/.github/workflows/e2e-tests-kms.yml b/.github/workflows/e2e-tests-kms.yml deleted file mode 100644 index c3b96cf8ccf..00000000000 --- a/.github/workflows/e2e-tests-kms.yml +++ /dev/null @@ -1,72 +0,0 @@ -# -# Copyright 2022 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: e2e-tests - -# Run on every push, and allow it to be run manually. -on: - push: - paths: - - '**' - - '!**.md' - - '!doc/**' - - '!**.txt' - - '!images/**' - - '!LICENSE' - - 'test/**' - branches: - - "main" - pull_request: - workflow_dispatch: - -jobs: - e2e-kms: - runs-on: ubuntu-latest - services: - vault: - image: hashicorp/vault:latest - env: - VAULT_DEV_ROOT_TOKEN_ID: root - options: >- - --health-cmd "VAULT_ADDR=http://127.0.0.1:8200 vault status" - --health-interval 1s - --health-timeout 5s - --health-retries 5 - ports: - - 8200:8200 - - env: - VAULT_TOKEN: "root" - VAULT_ADDR: "http://localhost:8200" - COSIGN_YES: "true" - steps: - - name: Checkout - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - uses: cpanato/vault-installer@df0775e6f6ee38dee09eaf57ede66ac6a414b70e # v1.0.2 - with: - vault-release: '1.14.1' - - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 - with: - go-version: '1.21' - check-latest: true - - - uses: imjasonh/setup-crane@00c9e93efa4e1138c9a7a5c594acd6c75a2fbf0c # v0.3 - - - name: enable vault transit - run: vault secrets enable transit - - name: Acceptance Tests - run: | - ./test/e2e_test_secrets_kms.sh diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 9e3ad84fe04..652dc49c777 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -39,10 +39,10 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: '1.21' + go-version: '1.22' check-latest: true - name: Run cross platform e2e tests @@ -52,12 +52,164 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: '1.21' + go-version: '1.22' check-latest: true - name: Run pkcs11 end-to-end tests shell: bash run: ./test/e2e_test_pkcs11.sh + + e2e-kms: + runs-on: ubuntu-latest + services: + vault: + image: hashicorp/vault:latest + env: + VAULT_DEV_ROOT_TOKEN_ID: root + options: >- + --health-cmd "VAULT_ADDR=http://127.0.0.1:8200 vault status" + --health-interval 1s + --health-timeout 5s + --health-retries 5 + --restart always + ports: + - 8200:8200 + + env: + VAULT_TOKEN: "root" + VAULT_ADDR: "http://localhost:8200" + COSIGN_YES: "true" + SCAFFOLDING_RELEASE_VERSION: "v0.7.5" + steps: + - name: Checkout + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: setup vault + uses: cpanato/vault-installer@892767a16fcd6afa5c4cceb557a6aacb73427ebb # v1.1.0 + + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + go-version: '1.22' + check-latest: true + + - uses: imjasonh/setup-crane@31b88efe9de28ae0ffa220711af4b60be9435f6e # v0.4 + + - name: Install cluster + sigstore + uses: sigstore/scaffolding/actions/setup@main + with: + version: ${{ env.SCAFFOLDING_RELEASE_VERSION }} + + - name: enable vault transit + run: vault secrets enable transit + + - name: Acceptance Tests + run: go test -tags=e2e,kms -v ./test/... + + e2e-registry: + runs-on: ubuntu-latest + + env: + SCAFFOLDING_RELEASE_VERSION: "v0.7.5" + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + go-version: '1.22' + check-latest: true + + - name: Setup mirror + uses: chainguard-dev/actions/setup-mirror@main + with: + mirror: mirror.gcr.io + + - name: Install cluster + sigstore + uses: sigstore/scaffolding/actions/setup@main + with: + version: ${{ env.SCAFFOLDING_RELEASE_VERSION }} + + - name: Setup local insecure registry + run: | + # Create a self-signed SSL cert + mkdir -p insecure-certs + openssl req \ + -subj "/C=US/ST=WA/L=Flavorton/O=Tests-R-Us/OU=Dept. of Insecurity/CN=example.com/emailAddress=testing@example.com" \ + -newkey rsa:4096 -nodes -sha256 -keyout insecure-certs/domain.key \ + -x509 -days 365 -out insecure-certs/domain.crt + # Run a registry. + docker run -d --restart=always \ + --name $INSECURE_REGISTRY_NAME \ + -v "$(pwd)"/insecure-certs:/insecure-certs \ + -e REGISTRY_HTTP_ADDR=0.0.0.0:$INSECURE_REGISTRY_PORT \ + -e REGISTRY_HTTP_TLS_CERTIFICATE=/insecure-certs/domain.crt \ + -e REGISTRY_HTTP_TLS_KEY=/insecure-certs/domain.key \ + -p $INSECURE_REGISTRY_PORT:$INSECURE_REGISTRY_PORT \ + registry:2 + sudo echo "127.0.0.1 $INSECURE_REGISTRY_NAME" | sudo tee -a /etc/hosts + env: + # https://github.com/google/go-containerregistry/pull/125 allows insecure registry for + # '*.local' hostnames. + INSECURE_REGISTRY_NAME: insecure-registry.notlocal + INSECURE_REGISTRY_PORT: 5001 + + - name: Run Insecure Registry Tests + run: go test -tags=e2e,registry -v ./test/... + env: + COSIGN_TEST_REPO: insecure-registry.notlocal:5001 + + - name: Setup local insecure OCI 1.1 registry + run: | + # Create a self-signed SSL cert + mkdir -p insecure-certs + openssl req \ + -subj "/C=US/ST=WA/L=Flavorton/O=Tests-R-Us/OU=Dept. of Insecurity/CN=example.com/emailAddress=testing@example.com" \ + -newkey rsa:4096 -nodes -sha256 -keyout insecure-certs/domain.key \ + -x509 -days 365 -out insecure-certs/domain.crt + cat > config.json << EOF + { + "distSpecVersion": "1.1.0-dev", + "storage": { + "rootDirectory": "/tmp/zot" + }, + "http": { + "address": "0.0.0.0", + "port": "5002", + "realm": "zot", + "tls": { + "cert": "/insecure-certs/domain.crt", + "key": "/insecure-certs/domain.key" + } + }, + "log": { + "level": "debug" + } + } + EOF + # Run a registry. + docker run -d --restart=always \ + --name $INSECURE_OCI_REGISTRY_NAME \ + -v "$(pwd)"/insecure-certs:/insecure-certs \ + -v "$(pwd)"/config.json:/etc/zot/config.json \ + -p $INSECURE_OCI_REGISTRY_PORT:$INSECURE_OCI_REGISTRY_PORT \ + ghcr.io/project-zot/zot-minimal-linux-amd64:$ZOT_VERSION + sudo echo "127.0.0.1 $INSECURE_OCI_REGISTRY_NAME" | sudo tee -a /etc/hosts + env: + ZOT_VERSION: v2.0.0-rc6 + # https://github.com/google/go-containerregistry/pull/125 allows insecure registry for + # '*.local' hostnames. + INSECURE_OCI_REGISTRY_NAME: insecure-oci-registry.notlocal + INSECURE_OCI_REGISTRY_PORT: 5002 + + + - name: Run Insecure OCI 1.1 Registry Tests + run: go test -tags=e2e,registry -v ./test/... + env: + OCI11: yes + COSIGN_TEST_REPO: insecure-oci-registry.notlocal:5002 + + - name: Collect diagnostics + if: ${{ failure() }} + uses: chainguard-dev/actions/kind-diag@84c993eaf02da1c325854fb272a4df9184bd80fc # main diff --git a/.github/workflows/e2e-with-binary.yml b/.github/workflows/e2e-with-binary.yml index 23e0643dae5..f91ae046f05 100644 --- a/.github/workflows/e2e-with-binary.yml +++ b/.github/workflows/e2e-with-binary.yml @@ -28,6 +28,8 @@ on: branches: [ 'main' ] workflow_dispatch: +permissions: {} + jobs: e2e-tests-with-binary: # Skip if running in a fork that might not have secrets configured. @@ -41,14 +43,15 @@ jobs: permissions: id-token: write contents: read + env: COSIGN_YES: "true" steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: '1.21' + go-version: '1.22' check-latest: true - name: build cosign and check sign-blob and verify-blob shell: bash diff --git a/.github/workflows/github-oidc.yaml b/.github/workflows/github-oidc.yaml index 50f785ea7a6..4b229eac2f1 100644 --- a/.github/workflows/github-oidc.yaml +++ b/.github/workflows/github-oidc.yaml @@ -28,29 +28,35 @@ on: - cron: '0 1 * * *' # 1AM UTC workflow_dispatch: +permissions: {} + jobs: build: + runs-on: ubuntu-latest + if: github.repository == 'sigstore/cosign' + permissions: id-token: write packages: write contents: read + env: GIT_HASH: ${{ github.sha }} GIT_VERSION: unstable GITHUB_RUN_ID: ${{ github.run_id }} GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }} KO_PREFIX: ghcr.io/${{ github.repository }} - runs-on: ubuntu-latest + steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: '1.21' + go-version: '1.22' check-latest: true cache: true # Install tools. - - uses: ko-build/setup-ko@ace48d793556083a76f1e3e6068850c1f4a369aa # v0.6 + - uses: ko-build/setup-ko@3aebd0597dc1e9d1a26bcfdb7cbeb19c131d3037 # v0.7 - name: build cosign from the HEAD run: | @@ -62,8 +68,3 @@ jobs: set -e # Build and publish an image. make sign-ci-keyless-containers - - - name: Build and sign a blob - run: | - set -e - make sign-blob-experimental diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 00000000000..54d50d31d2a --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,62 @@ +# Copyright 2024 The Sigstore Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: golangci-lint + +on: + push: + branches: + - 'main' + pull_request: + +permissions: {} + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + go-version: '1.22' + check-latest: true + - name: golangci-lint + uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0 + with: + version: v1.59 + args: --timeout=5m + + golangci-test-e2e: + name: lint-test-e2e + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + with: + go-version: '1.22' + check-latest: true + - name: golangci-lint + uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0 + with: + version: v1.59 + args: --timeout=5m --build-tags e2e ./test diff --git a/.github/workflows/kind-e2e-insecure-registry.yaml b/.github/workflows/kind-e2e-insecure-registry.yaml deleted file mode 100644 index 8ba44e1c0e6..00000000000 --- a/.github/workflows/kind-e2e-insecure-registry.yaml +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: Insecure Registry KinD E2E - -on: - pull_request: - branches: [ 'main', 'release-*' ] - -permissions: read-all - -jobs: - e2e-tests: - name: e2e tests - runs-on: ubuntu-latest - - strategy: - fail-fast: false # Keep running if one leg fails. - matrix: - k8s-version: - - v1.25.x - - v1.26.x - - v1.27.x - - env: - # https://github.com/google/go-containerregistry/pull/125 allows insecure registry for - # '*.local' hostnames. - REGISTRY_NAME: registry.local - REGISTRY_PORT: 5000 - INSECURE_REGISTRY_NAME: insecure-registry.notlocal - INSECURE_REGISTRY_PORT: 5001 - INSECURE_OCI_REGISTRY_NAME: insecure-oci-registry.notlocal - INSECURE_OCI_REGISTRY_PORT: 5002 - KO_DOCKER_REPO: registry.local:5000/policy-controller - - steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 - with: - go-version: '1.21' - check-latest: true - - - uses: imjasonh/setup-ko@ace48d793556083a76f1e3e6068850c1f4a369aa # v0.6 - - - uses: imranismail/setup-kustomize@2ba527d4d055ab63514ba50a99456fc35684947f # v2.1.0 - - - name: Install yq - uses: mikefarah/yq@c35ec752e38ea0c096d3c44e13cfc0797ac394d8 # v4.43.1 - - - name: Install Cosign - run: | - go install ./cmd/cosign - - - name: Setup mirror - uses: chainguard-dev/actions/setup-mirror@main - with: - mirror: mirror.gcr.io - - - name: Setup kind cluster - uses: chainguard-dev/actions/setup-kind@main - with: - k8s-version: ${{ matrix.k8s-version }} - cluster-suffix: c${{ github.run_id }}.local - - - name: Setup local insecure registry - run: | - # Create a self-signed SSL cert - mkdir -p insecure-certs - openssl req \ - -subj "/C=US/ST=WA/L=Flavorton/O=Tests-R-Us/OU=Dept. of Insecurity/CN=example.com/emailAddress=testing@example.com" \ - -newkey rsa:4096 -nodes -sha256 -keyout insecure-certs/domain.key \ - -x509 -days 365 -out insecure-certs/domain.crt - # Run a registry. - docker run -d --restart=always \ - --name $INSECURE_REGISTRY_NAME \ - -v "$(pwd)"/insecure-certs:/insecure-certs \ - -e REGISTRY_HTTP_ADDR=0.0.0.0:$INSECURE_REGISTRY_PORT \ - -e REGISTRY_HTTP_TLS_CERTIFICATE=/insecure-certs/domain.crt \ - -e REGISTRY_HTTP_TLS_KEY=/insecure-certs/domain.key \ - -p $INSECURE_REGISTRY_PORT:$INSECURE_REGISTRY_PORT \ - registry:2 - # Connect the registry to the KinD network. - docker network connect "kind" $INSECURE_REGISTRY_NAME - # Make the $INSECURE_REGISTRY_NAME -> 127.0.0.1, to tell `ko` to publish to - # local registry, even when pushing $INSECURE_REGISTRY_NAME:$INSECURE_REGISTRY_NAME/some/image - sudo echo "127.0.0.1 $INSECURE_REGISTRY_NAME" | sudo tee -a /etc/hosts - - - name: Run Insecure Registry Tests - run: | - go install github.com/google/go-containerregistry/cmd/crane - ./test/e2e_test_insecure_registry.sh - - - name: Setup local insecure OCI registry - run: | - # Create a self-signed SSL cert - mkdir -p insecure-certs - openssl req \ - -subj "/C=US/ST=WA/L=Flavorton/O=Tests-R-Us/OU=Dept. of Insecurity/CN=example.com/emailAddress=testing@example.com" \ - -newkey rsa:4096 -nodes -sha256 -keyout insecure-certs/domain.key \ - -x509 -days 365 -out insecure-certs/domain.crt - cat > config.json << EOF - { - "distSpecVersion": "1.1.0-dev", - "storage": { - "rootDirectory": "/tmp/zot" - }, - "http": { - "address": "0.0.0.0", - "port": "5000", - "realm": "zot", - "tls": { - "cert": "/insecure-certs/domain.crt", - "key": "/insecure-certs/domain.key" - } - }, - "log": { - "level": "debug" - } - } - EOF - # Run a registry. - docker run -d --restart=always \ - --name $INSECURE_OCI_REGISTRY_NAME \ - -v "$(pwd)"/insecure-certs:/insecure-certs \ - -v "$(pwd)"/config.json:/etc/zot/config.json \ - -p $INSECURE_OCI_REGISTRY_PORT:$REGISTRY_PORT \ - ghcr.io/project-zot/zot-minimal-linux-amd64:$ZOT_VERSION - # Connect the registry to the KinD network. - docker network connect "kind" $INSECURE_OCI_REGISTRY_NAME - # Make the $INSECURE_REGISTRY_NAME -> 127.0.0.1, to tell `ko` to publish to - # local registry, even when pushing $INSECURE_REGISTRY_NAME:$INSECURE_REGISTRY_NAME/some/image - sudo echo "127.0.0.1 $INSECURE_OCI_REGISTRY_NAME" | sudo tee -a /etc/hosts - env: - ZOT_VERSION: v2.0.0-rc6 - - - name: Run Insecure OCI Registry Tests - run: | - go install github.com/google/go-containerregistry/cmd/crane - ./test/e2e_test_insecure_oci_registry.sh - - - name: Collect diagnostics - if: ${{ failure() }} - uses: chainguard-dev/actions/kind-diag@84c993eaf02da1c325854fb272a4df9184bd80fc # main diff --git a/.github/workflows/kind-verify-attestation.yaml b/.github/workflows/kind-verify-attestation.yaml index cb36298fb7d..572cc98dddd 100644 --- a/.github/workflows/kind-verify-attestation.yaml +++ b/.github/workflows/kind-verify-attestation.yaml @@ -23,7 +23,7 @@ defaults: run: shell: bash -permissions: read-all +permissions: {} jobs: cip-test: @@ -33,31 +33,34 @@ jobs: strategy: matrix: k8s-version: - - v1.25.x + - v1.27.x tuf-root: - remote - air-gap + permissions: + contents: read + env: KO_DOCKER_REPO: "registry.local:5000/policy-controller" - SCAFFOLDING_RELEASE_VERSION: "v0.6.14" + SCAFFOLDING_RELEASE_VERSION: "v0.7.5" GO111MODULE: on GOFLAGS: -ldflags=-s -ldflags=-w KOCACHE: ~/ko COSIGN_YES: "true" steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: '1.21' + go-version: '1.22' check-latest: true # will use the latest release available for ko - - uses: ko-build/setup-ko@ace48d793556083a76f1e3e6068850c1f4a369aa # v0.6 + - uses: ko-build/setup-ko@3aebd0597dc1e9d1a26bcfdb7cbeb19c131d3037 # v0.7 - name: Install yq - uses: mikefarah/yq@c35ec752e38ea0c096d3c44e13cfc0797ac394d8 # v4.43.1 + uses: mikefarah/yq@bbdd97482f2d439126582a59689eb1c855944955 # v4.44.3 - name: build cosign run: | diff --git a/.github/workflows/milestone.yaml b/.github/workflows/milestone.yaml deleted file mode 100644 index 176530e308d..00000000000 --- a/.github/workflows/milestone.yaml +++ /dev/null @@ -1,53 +0,0 @@ -name: Milestone - -on: - pull_request_target: - types: [closed] - branches: - - main - -jobs: - milestone: - runs-on: ubuntu-latest - - permissions: - actions: none - checks: none - contents: read - deployments: none - issues: write - packages: none - pull-requests: write - repository-projects: none - security-events: none - statuses: none - - steps: - - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - with: - script: | - if (!context.payload.pull_request.merged) { - console.log('PR was not merged, skipping.'); - return; - } - if (!!context.payload.pull_request.milestone) { - console.log('PR has existing milestone, skipping.'); - return; - } - milestones = await github.rest.issues.listMilestones({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - sort: 'due_on', - direction: 'asc' - }) - if (milestones.data.length === 0) { - console.log('There are no milestones, skipping.'); - return; - } - await github.rest.issues.update({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - milestone: milestones.data[0].number - }); diff --git a/.github/workflows/scorecard-action.yml b/.github/workflows/scorecard-action.yml index 20e13f7c94f..3e6439758bb 100644 --- a/.github/workflows/scorecard-action.yml +++ b/.github/workflows/scorecard-action.yml @@ -1,3 +1,18 @@ +# +# Copyright 2024 The Sigstore Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + name: Scorecards supply-chain security on: # Only the default branch is supported. @@ -8,27 +23,29 @@ on: push: branches: [ main ] -# Declare default permissions as read only. -permissions: read-all +permissions: {} jobs: analysis: name: Scorecards analysis runs-on: ubuntu-latest + if: github.repository == 'sigstore/cosign' + permissions: # Needed to upload the results to code-scanning dashboard. security-events: write actions: read contents: read id-token: write + steps: - name: "Checkout code" - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 with: results_file: results.sarif results_format: sarif @@ -44,7 +61,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 with: name: SARIF file path: results.sarif diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index fdb7ea40423..de5ab337360 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -31,7 +31,7 @@ on: permissions: read-all env: - GO_VERSION: '1.21' + GO_VERSION: '1.22' jobs: unit-tests: @@ -46,7 +46,7 @@ jobs: OS: ${{ matrix.os }} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 # https://github.com/mvdan/github-actions-golang#how-do-i-set-up-caching-between-builds - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: @@ -63,7 +63,7 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: ${{ env.GO_VERSION }} check-latest: true @@ -73,7 +73,7 @@ jobs: GODEBUG: x509sha1=1 run: go test -tags=sct -covermode atomic -coverprofile coverage.txt $(go list ./... | grep -v third_party/) - name: Upload Coverage Report - uses: codecov/codecov-action@7afa10ed9b269c561c2336fd862446844e0cbf71 # v4.2.0 + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 with: env_vars: OS - name: Run Go tests w/ `-race` @@ -88,10 +88,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 # Related to https://github.com/sigstore/cosign/issues/3149 - name: free up disk space run: | + rm -rf /opt/hostedtoolcache/CodeQL rm -rf /usr/share/dotnet/ rm -rf "$AGENT_TOOLSDIRECTORY" rm -rf "/usr/local/share/boost" @@ -135,6 +136,7 @@ jobs: php-igbinary php-memcache php-memcached php-mongodb php-redis php-xdebug \ php-zmq snmp pollinate libpq-dev postgresql-client powershell ruby-full \ sphinxsearch subversion mongodb-org -yq >/dev/null 2>&1 || true + apt-get remove -y '^llvm.*' || true apt-get remove -y 'php.*' || true apt-get autoremove -y >/dev/null 2>&1 || true apt-get autoclean -y >/dev/null 2>&1 || true @@ -156,11 +158,11 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: ${{ env.GO_VERSION }} check-latest: true - - uses: ko-build/setup-ko@ace48d793556083a76f1e3e6068850c1f4a369aa # v0.6 + - uses: ko-build/setup-ko@3aebd0597dc1e9d1a26bcfdb7cbeb19c131d3037 # v0.7 - name: setup kind cluster run: | # Used to test: cosign generate-key-pair k8s://... @@ -178,8 +180,8 @@ jobs: name: Run PowerShell E2E tests runs-on: windows-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: ${{ env.GO_VERSION }} check-latest: true @@ -204,8 +206,8 @@ jobs: name: license boilerplate check runs-on: ubuntu-latest steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: ${{ env.GO_VERSION }} check-latest: true @@ -215,19 +217,3 @@ jobs: run: | set -e addlicense -check -l apache -c 'The Sigstore Authors' -ignore "third_party/**" -v * - - golangci: - name: lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 - with: - go-version: '1.21' - check-latest: true - - name: golangci-lint - uses: golangci/golangci-lint-action@3cfe3a4abbb849e10058ce4af15d205b6da42804 # v4.0.0 - with: - # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. - version: v1.56 - args: --timeout=5m diff --git a/.github/workflows/validate-release.yml b/.github/workflows/validate-release.yml index 305b5e5774c..eda0467e7ab 100644 --- a/.github/workflows/validate-release.yml +++ b/.github/workflows/validate-release.yml @@ -26,14 +26,14 @@ jobs: check-signature: runs-on: ubuntu-latest container: - image: gcr.io/projectsigstore/cosign:v2.2.3-dev@sha256:0d795fa145b03026b7bc2a35e33068cdb75e1c1f974e604c17408bf7bd174967 + image: gcr.io/projectsigstore/cosign:v2.2.4-dev@sha256:13efd4c62710d75f07d12d8aad36a8657eeffd4f5f3a40bcbc207d8aafa67d41 steps: - name: Check Signature run: | - cosign verify ghcr.io/gythialy/golang-cross:v1.21.8-0@sha256:9c86fc6c6763cd5cd9a07f25083fc5a87f3525b5f8d7ff886822e2153f0c8405 \ + cosign verify ghcr.io/gythialy/golang-cross:v1.22.5-0@sha256:5cf8fca7fe80392c8d1597fe89d291d49120507390f25507746f73d4b7f8a8f2 \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ - --certificate-identity "https://github.com/gythialy/golang-cross/.github/workflows/release-golang-cross.yml@refs/tags/v1.21.8-0" + --certificate-identity "https://github.com/gythialy/golang-cross/.github/workflows/release-golang-cross.yml@refs/tags/v1.22.5-0" env: TUF_ROOT: /tmp @@ -43,12 +43,15 @@ jobs: - check-signature container: - image: ghcr.io/gythialy/golang-cross:v1.21.8-0@sha256:9c86fc6c6763cd5cd9a07f25083fc5a87f3525b5f8d7ff886822e2153f0c8405 + image: ghcr.io/gythialy/golang-cross:v1.22.5-0@sha256:5cf8fca7fe80392c8d1597fe89d291d49120507390f25507746f73d4b7f8a8f2 + volumes: + - /usr:/host_usr + - /opt:/host_opt permissions: {} steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 # Error: fatal: detected dubious ownership in repository at '/__w/cosign/cosign' # To add an exception for this directory, call: @@ -62,54 +65,22 @@ jobs: - run: git config --system --add safe.directory /__w/cosign/cosign # Related to https://github.com/sigstore/cosign/issues/3149 + # note this runs from within the golang-cross container, so if we want to free up space + # on the host, we have to reach through the bind mounts to clean up unused content on the runners - name: free up disk space for the release run: | - rm -rf /usr/share/dotnet/ - rm -rf "$AGENT_TOOLSDIRECTORY" - rm -rf "/usr/local/share/boost" - rm -rf /opt/ghc - docker rmi $(docker image ls -aq) || true - swapoff /swapfile || true - rm -rf /swapfile /usr/share/dotnet /usr/local/lib/android /opt/ghc || true - apt purge aria2 ansible hhvm mono-devel azure-cli shellcheck rpm xorriso zsync \ - clang-6.0 lldb-6.0 lld-6.0 clang-format-6.0 clang-8 lldb-8 lld-8 clang-format-8 \ - clang-9 lldb-9 lld-9 clangd-9 clang-format-9 dotnet-sdk-3.0 dotnet-sdk-3.1=3.1.101-1 \ - esl-erlang firefox g++-8 g++-9 gfortran-8 gfortran-9 google-chrome-stable \ - google-cloud-sdk ghc-8.0.2 ghc-8.2.2 ghc-8.4.4 ghc-8.6.2 ghc-8.6.3 ghc-8.6.4 \ - ghc-8.6.5 ghc-8.8.1 ghc-8.8.2 ghc-8.8.3 ghc-8.10.1 cabal-install-2.0 cabal-install-2.2 \ - cabal-install-2.4 cabal-install-3.0 cabal-install-3.2 heroku imagemagick \ - libmagickcore-dev libmagickwand-dev libmagic-dev ant ant-optional kubectl \ - mercurial apt-transport-https mono-complete mysql-client libmysqlclient-dev \ - mysql-server mssql-tools unixodbc-dev yarn bazel chrpath libssl-dev libxft-dev \ - libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev php7.1 php7.1-bcmath \ - php7.1-bz2 php7.1-cgi php7.1-cli php7.1-common php7.1-curl php7.1-dba php7.1-dev \ - php7.1-enchant php7.1-fpm php7.1-gd php7.1-gmp php7.1-imap php7.1-interbase php7.1-intl \ - php7.1-json php7.1-ldap php7.1-mbstring php7.1-mcrypt php7.1-mysql php7.1-odbc \ - php7.1-opcache php7.1-pgsql php7.1-phpdbg php7.1-pspell php7.1-readline php7.1-recode \ - php7.1-snmp php7.1-soap php7.1-sqlite3 php7.1-sybase php7.1-tidy php7.1-xml \ - php7.1-xmlrpc php7.1-xsl php7.1-zip php7.2 php7.2-bcmath php7.2-bz2 php7.2-cgi \ - php7.2-cli php7.2-common php7.2-curl php7.2-dba php7.2-dev php7.2-enchant php7.2-fpm \ - php7.2-gd php7.2-gmp php7.2-imap php7.2-interbase php7.2-intl php7.2-json php7.2-ldap \ - php7.2-mbstring php7.2-mysql php7.2-odbc php7.2-opcache php7.2-pgsql php7.2-phpdbg \ - php7.2-pspell php7.2-readline php7.2-recode php7.2-snmp php7.2-soap php7.2-sqlite3 \ - php7.2-sybase php7.2-tidy php7.2-xml php7.2-xmlrpc php7.2-xsl php7.2-zip php7.3 \ - php7.3-bcmath php7.3-bz2 php7.3-cgi php7.3-cli php7.3-common php7.3-curl php7.3-dba \ - php7.3-dev php7.3-enchant php7.3-fpm php7.3-gd php7.3-gmp php7.3-imap php7.3-interbase \ - php7.3-intl php7.3-json php7.3-ldap php7.3-mbstring php7.3-mysql php7.3-odbc \ - php7.3-opcache php7.3-pgsql php7.3-phpdbg php7.3-pspell php7.3-readline php7.3-recode \ - php7.3-snmp php7.3-soap php7.3-sqlite3 php7.3-sybase php7.3-tidy php7.3-xml \ - php7.3-xmlrpc php7.3-xsl php7.3-zip php7.4 php7.4-bcmath php7.4-bz2 php7.4-cgi \ - php7.4-cli php7.4-common php7.4-curl php7.4-dba php7.4-dev php7.4-enchant php7.4-fpm \ - php7.4-gd php7.4-gmp php7.4-imap php7.4-interbase php7.4-intl php7.4-json php7.4-ldap \ - php7.4-mbstring php7.4-mysql php7.4-odbc php7.4-opcache php7.4-pgsql php7.4-phpdbg \ - php7.4-pspell php7.4-readline php7.4-snmp php7.4-soap php7.4-sqlite3 php7.4-sybase \ - php7.4-tidy php7.4-xml php7.4-xmlrpc php7.4-xsl php7.4-zip php-amqp php-apcu \ - php-igbinary php-memcache php-memcached php-mongodb php-redis php-xdebug \ - php-zmq snmp pollinate libpq-dev postgresql-client powershell ruby-full \ - sphinxsearch subversion mongodb-org -yq >/dev/null 2>&1 || true - apt-get remove -y 'php.*' || true - apt-get autoremove -y >/dev/null 2>&1 || true - apt-get autoclean -y >/dev/null 2>&1 || true + rm -rf /host_usr/share/dotnet || true + rm -rf /host_usr/share/swift || true + rm -rf /host_usr/local/lib/android || true + rm -rf /host_usr/local/.ghcup || true + rm -rf /host_usr/local/graalvm/ || true + rm -rf /host_usr/local/share/powershell || true + rm -rf /host_usr/local/share/chromium || true + rm -rf /host_usr/local/lib/node_modules || true + rm -rf /host_usr/lib/google-cloud-sdk || true + rm -rf /host_usr/local/share/boost || true + rm -rf /host_opt/hostedtoolcache/ || true + rm -rf /host_opt/ghc || true - name: check disk space run: df -h diff --git a/.github/workflows/verify-docgen.yaml b/.github/workflows/verify-docgen.yaml index ed8f35a869f..d6a7a2a47a5 100644 --- a/.github/workflows/verify-docgen.yaml +++ b/.github/workflows/verify-docgen.yaml @@ -18,22 +18,27 @@ name: Docgen on: workflow_dispatch: push: - branches: ['main', 'release-*'] + branches: + - 'main' + - 'release-*' pull_request: -permissions: read-all +permissions: {} jobs: docgen: name: Verify Docgen runs-on: ubuntu-latest + permissions: + contents: read + steps: - name: deps run: sudo apt-get update && sudo apt-get install -yq libpcsclite-dev - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - go-version: '1.21' + go-version: '1.22' check-latest: true - run: ./cmd/help/verify.sh diff --git a/.github/workflows/whitespace.yaml b/.github/workflows/whitespace.yaml index d4c585cc1f2..533dce92072 100644 --- a/.github/workflows/whitespace.yaml +++ b/.github/workflows/whitespace.yaml @@ -1,10 +1,27 @@ +# +# Copyright 2024 The Sigstore Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + name: Whitespace on: pull_request: - branches: [ 'main', 'release-*' ] + branches: + - 'main' + - 'release-*' -permissions: read-all +permissions: {} jobs: @@ -12,12 +29,15 @@ jobs: name: Check Whitespace runs-on: ubuntu-latest + permissions: + contents: read + steps: - name: Check out code - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: chainguard-dev/actions/trailing-space@7071df0659dbd4a79804731f0da2d0f1dba0b356 # main if: ${{ always() }} - - uses: chainguard-dev/actions/eof-newline@84c993eaf02da1c325854fb272a4df9184bd80fc # main + - uses: chainguard-dev/actions/eof-newline@7071df0659dbd4a79804731f0da2d0f1dba0b356 # main if: ${{ always() }} diff --git a/.golangci.yml b/.golangci.yml index eff17a670d9..0c11ef40e40 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -41,7 +41,7 @@ linters-settings: # Reference: https://github.com/sigstore/cosign/issues/2236 - 'os\.Getenv.*' - 'os\.LookupEnv.*' - exclude_godoc_examples: false + exclude-godoc-examples: false output: uniq-by-line: false issues: diff --git a/.goreleaser.yml b/.goreleaser.yml index f0da7efa543..fda598af27c 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,11 +1,11 @@ project_name: cosign +version: 2 env: - GO111MODULE=on - CGO_ENABLED=1 - DOCKER_CLI_EXPERIMENTAL=enabled - COSIGN_YES=true - - LATEST_TAG=,latest # Prevents parallel builds from stepping on each others toes downloading modules before: @@ -115,8 +115,8 @@ builds: binary: cosign-darwin-arm64 no_unique_dist_dir: true env: - - CC=aarch64-apple-darwin22-clang - - CXX=aarch64-apple-darwin22-clang++ + - CC=aarch64-apple-darwin23-clang + - CXX=aarch64-apple-darwin23-clang++ main: ./cmd/cosign flags: - -trimpath diff --git a/.tekton/cosign-pull-request.yaml b/.tekton/cosign-pull-request.yaml index a54b673ff7c..7557bfaf7d7 100644 --- a/.tekton/cosign-pull-request.yaml +++ b/.tekton/cosign-pull-request.yaml @@ -30,12 +30,8 @@ spec: value: . - name: revision value: '{{revision}}' - - name: hermetic - value: "true" - name: build-source-image value: "true" - - name: prefetch-input - value: '{"type": "gomod", "path": "."}' pipelineSpec: finally: - name: show-sbom diff --git a/.tekton/cosign-push.yaml b/.tekton/cosign-push.yaml index 0810d016102..b8427f53aeb 100644 --- a/.tekton/cosign-push.yaml +++ b/.tekton/cosign-push.yaml @@ -27,10 +27,6 @@ spec: value: . - name: revision value: '{{revision}}' - - name: prefetch-input - value: '{"type": "gomod", "path": "."}' - - name: hermetic - value: "true" - name: build-source-image value: "true" pipelineSpec: diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e883d2ed62..595c022dc31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,50 @@ +# v2.3.0 + +## Features + +* Add PayloadProvider interface to decouple AttestationToPayloadJSON from oci.Signature interface (#3693) +* add registry options to cosign save (#3645) +* Add debug providers command. (#3728) +* Make config layers in ociremote mountable (#3741) +* upgrade to go1.22 (#3739) +* adds tsa cert chain check for env var or tuf targets. (#3600) +* add --ca-roots and --ca-intermediates flags to 'cosign verify' (#3464) +* add handling of keyless verification for all verify commands (#3761) + +## Bug Fixes + +* fix: close attestationFile (#3679) +* Set `bundleVerified` to true after Rekor verification (Resolves #3740) (#3745) + +## Documentation + +* Document ImportKeyPair and LoadPrivateKey functions in pkg/cosign (#3776) + +## Testing + +* Refactor KMS E2E tests (#3684) +* Remove sign\_blob\_test.sh test (#3707) +* Remove KMS E2E test script (#3702) +* Refactor insecure registry E2E tests (#3701) + +## Contributors + +* Billy Lynch +* bminahan73 +* Bob Callaway +* Carlos Tadeu Panato Junior +* Cody Soyland +* Colleen Murphy +* Dmitry Savintsev +* guangwu +* Hayden B +* Hector Fernandez +* ian hundere +* Jason Power +* Jon Johnson +* Max Lambrecht +* Meeki1l + # v2.2.4 ## Bug Fixes diff --git a/Dockerfile.cosign.rh b/Dockerfile.cosign.rh index 231bf84a683..94790095d6b 100644 --- a/Dockerfile.cosign.rh +++ b/Dockerfile.cosign.rh @@ -5,11 +5,11 @@ WORKDIR /cosign COPY . . USER root RUN git config --global --add safe.directory /cosign && \ - git stash && \ + # git stash && \ export GIT_VERSION=$(git describe --tags --always --dirty) && \ export GIT_HASH=$(git rev-parse HEAD) && \ export BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') && \ - git stash pop && \ + # git stash pop && \ go mod vendor && \ make -f Build.mak cross-platform && \ cp cosign-linux-amd64 cosign && \ diff --git a/Makefile b/Makefile index 76671f60936..c67b4032fb9 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ export KO_DOCKER_REPO=$(KO_PREFIX) GHCR_PREFIX ?= ghcr.io/sigstore/cosign LATEST_TAG ?= -.PHONY: all lint test clean cosign cross +.PHONY: all lint test clean cosign conformance cross all: cosign log-%: @@ -106,6 +106,9 @@ lint: golangci-lint ## Run golangci-lint linter test: $(GOEXE) test $(shell $(GOEXE) list ./... | grep -v third_party/) +conformance: + $(GOEXE) build -trimpath -ldflags "$(LDFLAGS)" -o $@ ./cmd/conformance + clean: rm -rf cosign rm -rf dist/ @@ -144,7 +147,7 @@ ko-cosign-dev: $(create_kocache_path) LDFLAGS="$(LDFLAGS)" GIT_HASH=$(GIT_HASH) GIT_VERSION=$(GIT_VERSION) \ KOCACHE=$(KOCACHE_PATH) KO_DEFAULTBASEIMAGE=gcr.io/distroless/static-debian12:debug-nonroot ko build --base-import-paths \ - --platform=all --tags $(GIT_VERSION)-dev --tags $(GIT_HASH)-dev \ + --platform=all --tags $(GIT_VERSION)-dev --tags $(GIT_HASH)-dev$(LATEST_TAG)-dev \ $(ARTIFACT_HUB_LABELS) --image-refs cosignDevImagerefs \ github.com/sigstore/cosign/v2/cmd/cosign diff --git a/README.md b/README.md index 62538fbb928..9ce921678d0 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ For Linux and macOS binaries see the [GitHub release assets](https://github.com/ ## Developer Installation -If you have Go 1.19+, you can setup a development environment: +If you have Go 1.22+, you can setup a development environment: ```shell $ git clone https://github.com/sigstore/cosign @@ -763,6 +763,6 @@ process](https://github.com/sigstore/.github/blob/main/SECURITY.md) ## PEM files in GitHub Release Assets -The GitHub release assets for cosign contain a PEM file produced by [GoReleaser](https://github.com/sigstore/cosign/blob/ac999344eb381ae91455b0a9c5c267e747608d76/.goreleaser.yml#L166) while signing the cosign blob that is used to verify the integrity of the release binaries. This file is not used by cosign itself, but is provided for users who wish to verify the integrity of the release binaries. +The GitHub release assets for cosign contain a PEM file produced by [GoReleaser](https://github.com/sigstore/cosign/blob/ac999344eb381ae91455b0a9c5c267e747608d76/.goreleaser.yml#L166) while signing the cosign blob that is used to verify the integrity of the release binaries. This file is not used by cosign itself, but is provided for users who wish to verify the integrity of the release binaries. By default, cosign output these PEM files in [base64 encoded format](https://github.com/sigstore/cosign/blob/main/doc/cosign_sign-blob.md#options), this approach might be good for air-gapped environments where the PEM file is stored in a file system. So, you should decode these PEM files before using them to verify the blobs. diff --git a/cmd/conformance/main.go b/cmd/conformance/main.go new file mode 100644 index 00000000000..420e16bd631 --- /dev/null +++ b/cmd/conformance/main.go @@ -0,0 +1,261 @@ +// Copyright 2024 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "crypto/sha256" + "encoding/base64" + "encoding/pem" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + + protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" + protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" + "github.com/sigstore/sigstore-go/pkg/bundle" + "google.golang.org/protobuf/encoding/protojson" +) + +var bundlePath *string +var certPath *string +var certOIDC *string +var certSAN *string +var identityToken *string +var signaturePath *string +var trustedRootPath *string + +func usage() { + fmt.Println("Usage:") + fmt.Printf("\t%s sign --identity-token TOKEN --signature FILE --certificate FILE FILE\n", os.Args[0]) + fmt.Printf("\t%s sign-bundle --identity-token TOKEN --bundle FILE FILE\n", os.Args[0]) + fmt.Printf("\t%s verify --signature FILE --certificate FILE --certificate-identity IDENTITY --certificate-oidc-issuer URL [--trusted-root FILE] FILE\n", os.Args[0]) + fmt.Printf("\t%s verify-bundle --bundle FILE --certificate-identity IDENTITY --certificate-oidc-issuer URL [--trusted-root FILE] FILE\n", os.Args[0]) +} + +func parseArgs() { + for i := 2; i < len(os.Args); { + switch os.Args[i] { + // TODO: support staging (see https://github.com/sigstore/cosign/issues/2434) + // + // Today cosign signing does not yet use sigstore-go, and so we would + // need to make some clever invocation of `cosign initialize` to + // support staging. Instead it might make sense to wait for cosign + // signing to use sigstore-go. + case "--bundle": + bundlePath = &os.Args[i+1] + i += 2 + case "--certificate": + certPath = &os.Args[i+1] + i += 2 + case "--certificate-oidc-issuer": + certOIDC = &os.Args[i+1] + i += 2 + case "--certificate-identity": + certSAN = &os.Args[i+1] + i += 2 + case "--identity-token": + identityToken = &os.Args[i+1] + i += 2 + case "--signature": + signaturePath = &os.Args[i+1] + i += 2 + case "--trusted-root": + trustedRootPath = &os.Args[i+1] + i += 2 + default: + i++ + } + } +} + +func main() { + if len(os.Args) < 2 { + usage() + os.Exit(1) + } + + parseArgs() + + args := []string{} + + switch os.Args[1] { + case "sign": + args = append(args, "sign-blob") + if signaturePath != nil { + args = append(args, "--output-signature", *signaturePath) + } + if certPath != nil { + args = append(args, "--output-certificate", *certPath) + } + args = append(args, "-y") + + case "sign-bundle": + args = append(args, "sign-blob") + args = append(args, "-y") + + case "verify": + args = append(args, "verify-blob") + + // TODO: for now, we handle `verify` by constructing a bundle + // (see https://github.com/sigstore/cosign/issues/3700) + // + // Today cosign only supports `--trusted-root` with the new bundle + // format. When cosign supports `--trusted-root` with detached signed + // material, we can supply this content with `--certificate` + // and `--signature` instead. + fileBytes, err := os.ReadFile(os.Args[len(os.Args)-1]) + if err != nil { + log.Fatal(err) + } + + fileDigest := sha256.Sum256(fileBytes) + + pb := protobundle.Bundle{ + MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.1", + } + + if signaturePath != nil { + sig, err := os.ReadFile(*signaturePath) + if err != nil { + log.Fatal(err) + } + + sigBytes, err := base64.StdEncoding.DecodeString(string(sig)) + if err != nil { + log.Fatal(err) + } + + pb.Content = &protobundle.Bundle_MessageSignature{ + MessageSignature: &protocommon.MessageSignature{ + MessageDigest: &protocommon.HashOutput{ + Algorithm: protocommon.HashAlgorithm_SHA2_256, + Digest: fileDigest[:], + }, + Signature: sigBytes, + }, + } + } + if certPath != nil { + cert, err := os.ReadFile(*certPath) + if err != nil { + log.Fatal(err) + } + + pemCert, _ := pem.Decode(cert) + if pemCert == nil { + log.Fatalf("unable to load cerficate from %s", *certPath) + } + + signingCert := protocommon.X509Certificate{ + RawBytes: pemCert.Bytes, + } + + pb.VerificationMaterial = &protobundle.VerificationMaterial{ + Content: &protobundle.VerificationMaterial_X509CertificateChain{ + X509CertificateChain: &protocommon.X509CertificateChain{ + Certificates: []*protocommon.X509Certificate{&signingCert}, + }, + }, + } + } + + bundleFile, err := os.CreateTemp(os.TempDir(), "bundle.sigstore.json") + if err != nil { + log.Fatal(err) + } + bundleFileName := bundleFile.Name() + pbBytes, err := protojson.Marshal(&pb) + if err != nil { + log.Fatal(err) + } + if err := os.WriteFile(bundleFileName, pbBytes, 0600); err != nil { + log.Fatal(err) + } + bundlePath = &bundleFileName + args = append(args, "--insecure-ignore-tlog") + + case "verify-bundle": + args = append(args, "verify-blob") + + // How do we know if we should expect signed timestamps or not? + // Let's crack open the bundle + if bundlePath != nil { + b, err := bundle.LoadJSONFromPath(*bundlePath) + if err != nil { + log.Fatal(err) + } + ts, err := b.Timestamps() + if err != nil { + log.Fatal(err) + } + if len(ts) > 0 { + args = append(args, "--use-signed-timestamps") + } + } + + default: + log.Fatalf("Unsupported command %s", os.Args[1]) + } + + if bundlePath != nil { + args = append(args, "--bundle", *bundlePath) + args = append(args, "--new-bundle-format") + } + if identityToken != nil { + args = append(args, "--identity-token", *identityToken) + } + if certSAN != nil { + args = append(args, "--certificate-identity", *certSAN) + } + if certOIDC != nil { + args = append(args, "--certificate-oidc-issuer", *certOIDC) + } + if trustedRootPath != nil { + args = append(args, "--trusted-root", *trustedRootPath) + } + args = append(args, os.Args[len(os.Args)-1]) + + dir := filepath.Dir(os.Args[0]) + cmd := exec.Command(filepath.Join(dir, "cosign"), args...) // #nosec G204 + var out strings.Builder + cmd.Stdout = &out + cmd.Stderr = &out + err := cmd.Run() + + fmt.Println(out.String()) + + if err != nil { + log.Fatal(err) + } + + if os.Args[1] == "sign" && certPath != nil { + // We want the signature to be base64 encoded, but not the certificate + // So base64 decode the certificate + cert, err := os.ReadFile(*certPath) + if err != nil { + log.Fatal(err) + } + certB64Decode, err := base64.StdEncoding.DecodeString(string(cert)) + if err != nil { + log.Fatal(err) + } + if err := os.WriteFile(*certPath, certB64Decode, 0600); err != nil { + log.Fatal(err) + } + } +} diff --git a/cmd/cosign/cli/attach/attach.go b/cmd/cosign/cli/attach/attach.go index 9b645422add..dcd3a290f4c 100644 --- a/cmd/cosign/cli/attach/attach.go +++ b/cmd/cosign/cli/attach/attach.go @@ -51,6 +51,7 @@ func attachAttestation(ctx context.Context, remoteOpts []ociremote.Option, signe if err != nil { return err } + defer attestationFile.Close() env := ssldsse.Envelope{} decoder := json.NewDecoder(attestationFile) diff --git a/cmd/cosign/cli/attest.go b/cmd/cosign/cli/attest.go index 644b85bf352..66334471780 100644 --- a/cmd/cosign/cli/attest.go +++ b/cmd/cosign/cli/attest.go @@ -31,7 +31,7 @@ func Attest() *cobra.Command { cmd := &cobra.Command{ Use: "attest", Short: "Attest the supplied container image.", - Example: ` cosign attest --key | [--predicate ] [--a key=value] [--no-upload=true|false] [--f] [--r] + Example: ` cosign attest --key | [--predicate ] [--a key=value] [--no-upload=true|false] [--record-creation-timestamp=true|false] [--f] [--r] # attach an attestation to a container image Google sign-in cosign attest --timeout 90s --predicate --type @@ -58,7 +58,10 @@ func Attest() *cobra.Command { COSIGN_DOCKER_MEDIA_TYPES=1 cosign attest --predicate --type --key cosign.key legacy-registry.example.com/my/image # supply attestation via stdin - echo | cosign attest --predicate - `, + echo | cosign attest --predicate - + + # attach an attestation to a container image and honor the creation timestamp of the signature + cosign attest --predicate --type --key cosign.key --record-creation-timestamp `, Args: cobra.MinimumNArgs(1), PersistentPreRun: options.BindViper, @@ -86,17 +89,18 @@ func Attest() *cobra.Command { TSAServerURL: o.TSAServerURL, } attestCommand := attest.AttestCommand{ - KeyOpts: ko, - RegistryOptions: o.Registry, - CertPath: o.Cert, - CertChainPath: o.CertChain, - NoUpload: o.NoUpload, - PredicatePath: o.Predicate.Path, - PredicateType: o.Predicate.Type, - Replace: o.Replace, - Timeout: ro.Timeout, - TlogUpload: o.TlogUpload, - RekorEntryType: o.RekorEntryType, + KeyOpts: ko, + RegistryOptions: o.Registry, + CertPath: o.Cert, + CertChainPath: o.CertChain, + NoUpload: o.NoUpload, + PredicatePath: o.Predicate.Path, + PredicateType: o.Predicate.Type, + Replace: o.Replace, + Timeout: ro.Timeout, + TlogUpload: o.TlogUpload, + RekorEntryType: o.RekorEntryType, + RecordCreationTimestamp: o.RecordCreationTimestamp, } for _, img := range args { diff --git a/cmd/cosign/cli/attest/attest.go b/cmd/cosign/cli/attest/attest.go index 0f1be95ae91..abfb25e8324 100644 --- a/cmd/cosign/cli/attest/attest.go +++ b/cmd/cosign/cli/attest/attest.go @@ -71,16 +71,17 @@ func uploadToTlog(ctx context.Context, sv *sign.SignerVerifier, rekorURL string, type AttestCommand struct { options.KeyOpts options.RegistryOptions - CertPath string - CertChainPath string - NoUpload bool - PredicatePath string - PredicateType string - Replace bool - Timeout time.Duration - TlogUpload bool - TSAServerURL string - RekorEntryType string + CertPath string + CertChainPath string + NoUpload bool + PredicatePath string + PredicateType string + Replace bool + Timeout time.Duration + TlogUpload bool + TSAServerURL string + RekorEntryType string + RecordCreationTimestamp bool } // nolint @@ -226,6 +227,7 @@ func (c *AttestCommand) Exec(ctx context.Context, imageRef string) error { signOpts := []mutate.SignOption{ mutate.WithDupeDetector(dd), + mutate.WithRecordCreationTimestamp(c.RecordCreationTimestamp), } if c.Replace { diff --git a/cmd/cosign/cli/attest/attest_blob.go b/cmd/cosign/cli/attest/attest_blob.go index a8a69362146..50795215e66 100644 --- a/cmd/cosign/cli/attest/attest_blob.go +++ b/cmd/cosign/cli/attest/attest_blob.go @@ -18,6 +18,8 @@ import ( "bytes" "context" "crypto" + "crypto/sha256" + "crypto/x509" "encoding/base64" "encoding/hex" "encoding/json" @@ -30,6 +32,9 @@ import ( "time" "github.com/pkg/errors" + "github.com/secure-systems-lab/go-securesystemslib/dsse" + "google.golang.org/protobuf/encoding/protojson" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" "github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor" "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" @@ -39,10 +44,12 @@ import ( "github.com/sigstore/cosign/v2/pkg/cosign/attestation" cbundle "github.com/sigstore/cosign/v2/pkg/cosign/bundle" "github.com/sigstore/cosign/v2/pkg/types" + protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" + protodsse "github.com/sigstore/protobuf-specs/gen/pb-go/dsse" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" - "github.com/sigstore/sigstore/pkg/signature/dsse" + sigstoredsse "github.com/sigstore/sigstore/pkg/signature/dsse" signatureoptions "github.com/sigstore/sigstore/pkg/signature/options" ) @@ -88,8 +95,8 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error defer cancelFn() } - if c.TSAServerURL != "" && c.RFC3161TimestampPath == "" { - return errors.New("expected an rfc3161-timestamp path when using a TSA server") + if c.TSAServerURL != "" && c.RFC3161TimestampPath == "" && !c.NewBundleFormat { + return errors.New("expected either new bundle or an rfc3161-timestamp path when using a TSA server") } var artifact []byte @@ -129,7 +136,7 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error return fmt.Errorf("getting signer: %w", err) } defer sv.Close() - wrapped := dsse.WrapSigner(sv, types.IntotoPayloadType) + wrapped := sigstoredsse.WrapSigner(sv, types.IntotoPayloadType) base := path.Base(artifactPath) @@ -154,28 +161,34 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error } var rfc3161Timestamp *cbundle.RFC3161Timestamp + var timestampBytes []byte + var rekorEntry *models.LogEntryAnon + if c.TSAServerURL != "" { - respBytes, err := tsa.GetTimestampedSignature(sig, client.NewTSAClient(c.TSAServerURL)) + timestampBytes, err = tsa.GetTimestampedSignature(sig, client.NewTSAClient(c.TSAServerURL)) if err != nil { return err } - rfc3161Timestamp = cbundle.TimestampToRFC3161Timestamp(respBytes) + rfc3161Timestamp = cbundle.TimestampToRFC3161Timestamp(timestampBytes) // TODO: Consider uploading RFC3161 TS to Rekor if rfc3161Timestamp == nil { return fmt.Errorf("rfc3161 timestamp is nil") } - ts, err := json.Marshal(rfc3161Timestamp) - if err != nil { - return err - } - if err := os.WriteFile(c.RFC3161TimestampPath, ts, 0600); err != nil { - return fmt.Errorf("create RFC3161 timestamp file: %w", err) + + if c.RFC3161TimestampPath != "" { + ts, err := json.Marshal(rfc3161Timestamp) + if err != nil { + return err + } + if err := os.WriteFile(c.RFC3161TimestampPath, ts, 0600); err != nil { + return fmt.Errorf("create RFC3161 timestamp file: %w", err) + } + fmt.Fprintln(os.Stderr, "RFC3161 timestamp bundle written to file ", c.RFC3161TimestampPath) } - fmt.Fprintln(os.Stderr, "RFC3161 timestamp bundle written to file ", c.RFC3161TimestampPath) } - rekorBytes, err := sv.Bytes(ctx) + signer, err := sv.Bytes(ctx) if err != nil { return err } @@ -189,28 +202,36 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error if err != nil { return err } - var entry *models.LogEntryAnon if c.RekorEntryType == "intoto" { - entry, err = cosign.TLogUploadInTotoAttestation(ctx, rekorClient, sig, rekorBytes) + rekorEntry, err = cosign.TLogUploadInTotoAttestation(ctx, rekorClient, sig, signer) } else { - entry, err = cosign.TLogUploadDSSEEnvelope(ctx, rekorClient, sig, rekorBytes) + rekorEntry, err = cosign.TLogUploadDSSEEnvelope(ctx, rekorClient, sig, signer) } if err != nil { return err } - fmt.Fprintln(os.Stderr, "tlog entry created with index:", *entry.LogIndex) - signedPayload.Bundle = cbundle.EntryToBundle(entry) + fmt.Fprintln(os.Stderr, "tlog entry created with index:", *rekorEntry.LogIndex) + signedPayload.Bundle = cbundle.EntryToBundle(rekorEntry) } if c.BundlePath != "" { - signedPayload.Base64Signature = base64.StdEncoding.EncodeToString(sig) - signedPayload.Cert = base64.StdEncoding.EncodeToString(rekorBytes) + var contents []byte + if c.NewBundleFormat { + contents, err = makeNewBundle(sv, rekorEntry, payload, sig, signer, timestampBytes) + if err != nil { + return err + } + } else { + signedPayload.Base64Signature = base64.StdEncoding.EncodeToString(sig) + signedPayload.Cert = base64.StdEncoding.EncodeToString(signer) - contents, err := json.Marshal(signedPayload) - if err != nil { - return err + contents, err = json.Marshal(signedPayload) + if err != nil { + return err + } } + if err := os.WriteFile(c.BundlePath, contents, 0600); err != nil { return fmt.Errorf("create bundle file: %w", err) } @@ -258,3 +279,64 @@ func (c *AttestBlobCommand) Exec(ctx context.Context, artifactPath string) error return nil } + +func makeNewBundle(sv *sign.SignerVerifier, rekorEntry *models.LogEntryAnon, payload, sig, signer, timestampBytes []byte) ([]byte, error) { + // Determine if signature is certificate or not + var hint string + var rawCert []byte + + cert, err := cryptoutils.UnmarshalCertificatesFromPEM(signer) + if err != nil || len(cert) == 0 { + pubKey, err := sv.PublicKey() + if err != nil { + return nil, err + } + pkixPubKey, err := x509.MarshalPKIXPublicKey(pubKey) + if err != nil { + return nil, err + } + hashedBytes := sha256.Sum256(pkixPubKey) + hint = base64.StdEncoding.EncodeToString(hashedBytes[:]) + } else { + rawCert = cert[0].Raw + } + + bundle, err := cbundle.MakeProtobufBundle(hint, rawCert, rekorEntry, timestampBytes) + if err != nil { + return nil, err + } + + var envelope dsse.Envelope + err = json.Unmarshal(sig, &envelope) + if err != nil { + return nil, err + } + + if len(envelope.Signatures) == 0 { + return nil, fmt.Errorf("no signature in DSSE envelope") + } + + sigBytes, err := base64.StdEncoding.DecodeString(envelope.Signatures[0].Sig) + if err != nil { + return nil, err + } + + bundle.Content = &protobundle.Bundle_DsseEnvelope{ + DsseEnvelope: &protodsse.Envelope{ + Payload: payload, + PayloadType: envelope.PayloadType, + Signatures: []*protodsse.Signature{ + { + Sig: sigBytes, + }, + }, + }, + } + + contents, err := protojson.Marshal(bundle) + if err != nil { + return nil, err + } + + return contents, nil +} diff --git a/cmd/cosign/cli/attest/attest_blob_test.go b/cmd/cosign/cli/attest/attest_blob_test.go index dd695d0e4ad..f563c7e6f33 100644 --- a/cmd/cosign/cli/attest/attest_blob_test.go +++ b/cmd/cosign/cli/attest/attest_blob_test.go @@ -122,6 +122,7 @@ func TestAttestBlobCmdLocalKeyAndCert(t *testing.T) { keyref string certref string certchainref string + newBundle bool errString string }{ { @@ -133,6 +134,12 @@ func TestAttestBlobCmdLocalKeyAndCert(t *testing.T) { keyref: keyRef, certref: certRef, }, + { + name: "new bundle generation", + keyref: keyRef, + certref: certRef, + newBundle: true, + }, { name: "fail: cert no match key", keyref: keyRef, @@ -160,8 +167,12 @@ func TestAttestBlobCmdLocalKeyAndCert(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { + keyOpts := options.KeyOpts{KeyRef: tc.keyref} + if tc.newBundle { + keyOpts.NewBundleFormat = true + } at := AttestBlobCommand{ - KeyOpts: options.KeyOpts{KeyRef: tc.keyref}, + KeyOpts: keyOpts, CertPath: tc.certref, CertChainPath: tc.certchainref, PredicatePath: predicatePath, diff --git a/cmd/cosign/cli/attest_blob.go b/cmd/cosign/cli/attest_blob.go index 3cefa61ba53..d9a7108b458 100644 --- a/cmd/cosign/cli/attest_blob.go +++ b/cmd/cosign/cli/attest_blob.go @@ -73,6 +73,7 @@ func AttestBlob() *cobra.Command { TSAServerURL: o.TSAServerURL, RFC3161TimestampPath: o.RFC3161TimestampPath, BundlePath: o.BundlePath, + NewBundleFormat: o.NewBundleFormat, } v := attest.AttestBlobCommand{ KeyOpts: ko, diff --git a/cmd/cosign/cli/commands.go b/cmd/cosign/cli/commands.go index bc6c47594d3..6c67e890c40 100644 --- a/cmd/cosign/cli/commands.go +++ b/cmd/cosign/cli/commands.go @@ -96,6 +96,7 @@ func New() *cobra.Command { cmd.AddCommand(Attest()) cmd.AddCommand(AttestBlob()) cmd.AddCommand(Clean()) + cmd.AddCommand(Debug()) cmd.AddCommand(Tree()) cmd.AddCommand(Completion()) cmd.AddCommand(Copy()) diff --git a/cmd/cosign/cli/debug.go b/cmd/cosign/cli/debug.go new file mode 100644 index 00000000000..277c85a2830 --- /dev/null +++ b/cmd/cosign/cli/debug.go @@ -0,0 +1,43 @@ +// Copyright 2024 The Sigstore Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cli + +import ( + "github.com/sigstore/cosign/v2/cmd/cosign/cli/debug" + "github.com/spf13/cobra" +) + +func Debug() *cobra.Command { + cmd := &cobra.Command{ + Use: "debug", + Hidden: true, + RunE: func(cmd *cobra.Command, _ []string) error { + return cmd.Help() + }, + } + cmd.AddCommand(debugProviders()) + return cmd +} + +func debugProviders() *cobra.Command { + cmd := &cobra.Command{ + Use: "providers", + Short: "Show enabled/disabled OIDC providers.", + RunE: func(cmd *cobra.Command, _ []string) error { + return debug.ProviderCmd(cmd.Context(), cmd.OutOrStdout()) + }, + } + return cmd +} diff --git a/cmd/cosign/cli/debug/provider.go b/cmd/cosign/cli/debug/provider.go new file mode 100644 index 00000000000..debf57d1bd1 --- /dev/null +++ b/cmd/cosign/cli/debug/provider.go @@ -0,0 +1,30 @@ +// Copyright 2024 The Sigstore Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package debug + +import ( + "context" + "fmt" + "io" + + "github.com/sigstore/cosign/v2/pkg/providers" +) + +func ProviderCmd(ctx context.Context, w io.Writer) error { + for _, p := range providers.Providers() { + fmt.Fprintf(w, "%s: %t\n", p.Name, p.Provider.Enabled(ctx)) + } + return nil +} diff --git a/cmd/cosign/cli/options/attest.go b/cmd/cosign/cli/options/attest.go index 9090ce1d9c6..8139cddaefa 100644 --- a/cmd/cosign/cli/options/attest.go +++ b/cmd/cosign/cli/options/attest.go @@ -21,16 +21,17 @@ import ( // AttestOptions is the top level wrapper for the attest command. type AttestOptions struct { - Key string - Cert string - CertChain string - NoUpload bool - Recursive bool - Replace bool - SkipConfirmation bool - TlogUpload bool - TSAServerURL string - RekorEntryType string + Key string + Cert string + CertChain string + NoUpload bool + Recursive bool + Replace bool + SkipConfirmation bool + TlogUpload bool + TSAServerURL string + RekorEntryType string + RecordCreationTimestamp bool Rekor RekorOptions Fulcio FulcioOptions @@ -86,4 +87,7 @@ func (o *AttestOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&o.TSAServerURL, "timestamp-server-url", "", "url to the Timestamp RFC3161 server, default none. Must be the path to the API to request timestamp responses, e.g. https://freetsa.org/tsr") + + cmd.Flags().BoolVar(&o.RecordCreationTimestamp, "record-creation-timestamp", false, + "set the createdAt timestamp in the attestation artifact to the time it was created; by default, cosign sets this to the zero value") } diff --git a/cmd/cosign/cli/options/attest_blob.go b/cmd/cosign/cli/options/attest_blob.go index 593c787eee5..b6f48b49671 100644 --- a/cmd/cosign/cli/options/attest_blob.go +++ b/cmd/cosign/cli/options/attest_blob.go @@ -36,6 +36,7 @@ type AttestBlobOptions struct { OutputAttestation string OutputCertificate string BundlePath string + NewBundleFormat bool RekorEntryType string @@ -85,6 +86,10 @@ func (o *AttestBlobOptions) AddFlags(cmd *cobra.Command) { "write everything required to verify the blob to a FILE") _ = cmd.Flags().SetAnnotation("bundle", cobra.BashCompFilenameExt, []string{}) + // TODO: have this default to true as a breaking change + cmd.Flags().BoolVar(&o.NewBundleFormat, "new-bundle-format", false, + "output bundle in new format that contains all verification material") + cmd.Flags().StringVar(&o.Hash, "hash", "", "hash of blob in hexadecimal (base16). Used if you want to sign an artifact stored elsewhere and have the hash") diff --git a/cmd/cosign/cli/options/certificate.go b/cmd/cosign/cli/options/certificate.go index e89f257f9e3..3df7b4b962e 100644 --- a/cmd/cosign/cli/options/certificate.go +++ b/cmd/cosign/cli/options/certificate.go @@ -33,6 +33,8 @@ type CertVerifyOptions struct { CertGithubWorkflowName string CertGithubWorkflowRepository string CertGithubWorkflowRef string + CAIntermediates string + CARoots string CertChain string SCT string IgnoreSCT bool @@ -75,12 +77,25 @@ func (o *CertVerifyOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&o.CertGithubWorkflowRef, "certificate-github-workflow-ref", "", "contains the ref claim from the GitHub OIDC Identity token that contains the git ref that the workflow run was based upon.") // -- Cert extensions end -- + cmd.Flags().StringVar(&o.CAIntermediates, "ca-intermediates", "", + "path to a file of intermediate CA certificates in PEM format which will be needed "+ + "when building the certificate chains for the signing certificate. "+ + "The flag is optional and must be used together with --ca-roots, conflicts with "+ + "--certificate-chain.") + _ = cmd.Flags().SetAnnotation("ca-intermediates", cobra.BashCompFilenameExt, []string{"cert"}) + cmd.Flags().StringVar(&o.CARoots, "ca-roots", "", + "path to a bundle file of CA certificates in PEM format which will be needed "+ + "when building the certificate chains for the signing certificate. Conflicts with --certificate-chain.") + _ = cmd.Flags().SetAnnotation("ca-roots", cobra.BashCompFilenameExt, []string{"cert"}) + cmd.Flags().StringVar(&o.CertChain, "certificate-chain", "", "path to a list of CA certificates in PEM format which will be needed "+ "when building the certificate chain for the signing certificate. "+ "Must start with the parent intermediate CA certificate of the "+ - "signing certificate and end with the root certificate") + "signing certificate and end with the root certificate. Conflicts with --ca-roots and --ca-intermediates.") _ = cmd.Flags().SetAnnotation("certificate-chain", cobra.BashCompFilenameExt, []string{"cert"}) + cmd.MarkFlagsMutuallyExclusive("ca-roots", "certificate-chain") + cmd.MarkFlagsMutuallyExclusive("ca-intermediates", "certificate-chain") cmd.Flags().StringVar(&o.SCT, "sct", "", "path to a detached Signed Certificate Timestamp, formatted as a RFC6962 AddChainResponse struct. "+ diff --git a/cmd/cosign/cli/options/key.go b/cmd/cosign/cli/options/key.go index 6af69afda37..634911fdb59 100644 --- a/cmd/cosign/cli/options/key.go +++ b/cmd/cosign/cli/options/key.go @@ -32,6 +32,7 @@ type KeyOpts struct { OIDCDisableProviders bool // Disable OIDC credential providers in keyless signer OIDCProvider string // Specify which OIDC credential provider to use for keyless signer BundlePath string + NewBundleFormat bool SkipConfirmation bool TSAClientCACert string TSAClientCert string diff --git a/cmd/cosign/cli/options/save.go b/cmd/cosign/cli/options/save.go index 58d449172b4..7c4f623de61 100644 --- a/cmd/cosign/cli/options/save.go +++ b/cmd/cosign/cli/options/save.go @@ -22,12 +22,14 @@ import ( // SaveOptions is the top level wrapper for the load command. type SaveOptions struct { Directory string + Registry RegistryOptions } var _ Interface = (*SaveOptions)(nil) // AddFlags implements Interface func (o *SaveOptions) AddFlags(cmd *cobra.Command) { + o.Registry.AddFlags(cmd) cmd.Flags().StringVar(&o.Directory, "dir", "", "path to dir where the signed image should be stored on disk") _ = cmd.Flags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{}) diff --git a/cmd/cosign/cli/options/signblob.go b/cmd/cosign/cli/options/signblob.go index 7cddde63dfc..d6326690684 100644 --- a/cmd/cosign/cli/options/signblob.go +++ b/cmd/cosign/cli/options/signblob.go @@ -33,6 +33,7 @@ type SignBlobOptions struct { OIDC OIDCOptions Registry RegistryOptions BundlePath string + NewBundleFormat bool SkipConfirmation bool TlogUpload bool TSAClientCACert string @@ -75,6 +76,10 @@ func (o *SignBlobOptions) AddFlags(cmd *cobra.Command) { "write everything required to verify the blob to a FILE") _ = cmd.Flags().SetAnnotation("bundle", cobra.BashCompFilenameExt, []string{}) + // TODO: have this default to true as a breaking change + cmd.Flags().BoolVar(&o.NewBundleFormat, "new-bundle-format", false, + "output bundle in new format that contains all verification material") + cmd.Flags().BoolVarP(&o.SkipConfirmation, "yes", "y", false, "skip confirmation prompts for non-destructive operations") diff --git a/cmd/cosign/cli/options/verify.go b/cmd/cosign/cli/options/verify.go index 8c3c241618a..3cdbb0e8a62 100644 --- a/cmd/cosign/cli/options/verify.go +++ b/cmd/cosign/cli/options/verify.go @@ -30,6 +30,7 @@ type CommonVerifyOptions struct { // it for other verify options. ExperimentalOCI11 bool PrivateInfrastructure bool + UseSignedTimestamps bool } func (o *CommonVerifyOptions) AddFlags(cmd *cobra.Command) { @@ -40,6 +41,9 @@ func (o *CommonVerifyOptions) AddFlags(cmd *cobra.Command) { "path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. "+ "Optionally may contain intermediate CA certificates, and may contain the leaf TSA certificate if not present in the timestamp") + cmd.Flags().BoolVar(&o.UseSignedTimestamps, "use-signed-timestamps", false, + "use signed timestamps if available") + cmd.Flags().BoolVar(&o.IgnoreTlog, "insecure-ignore-tlog", false, "ignore transparency log verification, to be used when an artifact signature has not been uploaded to the transparency log. Artifacts "+ "cannot be publicly verified when not included in a log") @@ -154,9 +158,11 @@ func (o *VerifyAttestationOptions) AddFlags(cmd *cobra.Command) { // VerifyBlobOptions is the top level wrapper for the `verify blob` command. type VerifyBlobOptions struct { - Key string - Signature string - BundlePath string + Key string + Signature string + BundlePath string + NewBundleFormat bool + TrustedRootPath string SecurityKey SecurityKeyOptions CertVerify CertVerifyOptions @@ -184,6 +190,13 @@ func (o *VerifyBlobOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&o.BundlePath, "bundle", "", "path to bundle FILE") + // TODO: have this default to true as a breaking change + cmd.Flags().BoolVar(&o.NewBundleFormat, "new-bundle-format", false, + "output bundle in new format that contains all verification material") + + cmd.Flags().StringVar(&o.TrustedRootPath, "trusted-root", "", + "path to trusted root FILE") + cmd.Flags().StringVar(&o.RFC3161TimestampPath, "rfc3161-timestamp", "", "path to RFC3161 timestamp FILE") } @@ -206,9 +219,11 @@ func (o *VerifyDockerfileOptions) AddFlags(cmd *cobra.Command) { // VerifyBlobAttestationOptions is the top level wrapper for the `verify-blob-attestation` command. type VerifyBlobAttestationOptions struct { - Key string - SignaturePath string - BundlePath string + Key string + SignaturePath string + BundlePath string + NewBundleFormat bool + TrustedRootPath string PredicateOptions CheckClaims bool @@ -240,6 +255,13 @@ func (o *VerifyBlobAttestationOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().StringVar(&o.BundlePath, "bundle", "", "path to bundle FILE") + // TODO: have this default to true as a breaking change + cmd.Flags().BoolVar(&o.NewBundleFormat, "new-bundle-format", false, + "output bundle in new format that contains all verification material") + + cmd.Flags().StringVar(&o.TrustedRootPath, "trusted-root", "", + "path to trusted root FILE") + cmd.Flags().BoolVar(&o.CheckClaims, "check-claims", true, "if true, verifies the provided blob's sha256 digest exists as an in-toto subject within the attestation. If false, only the DSSE envelope is verified.") diff --git a/cmd/cosign/cli/save.go b/cmd/cosign/cli/save.go index 336fc8413da..018bd9f76ed 100644 --- a/cmd/cosign/cli/save.go +++ b/cmd/cosign/cli/save.go @@ -47,19 +47,25 @@ func Save() *cobra.Command { return cmd } -func SaveCmd(_ context.Context, opts options.SaveOptions, imageRef string) error { - ref, err := name.ParseReference(imageRef) +func SaveCmd(ctx context.Context, opts options.SaveOptions, imageRef string) error { + regOpts := opts.Registry + regClientOpts, err := regOpts.ClientOpts(ctx) + if err != nil { + return fmt.Errorf("constructing client options: %w", err) + } + + ref, err := name.ParseReference(imageRef, opts.Registry.NameOptions()...) if err != nil { return fmt.Errorf("parsing image name %s: %w", imageRef, err) } - se, err := ociremote.SignedEntity(ref) + se, err := ociremote.SignedEntity(ref, regClientOpts...) if err != nil { return fmt.Errorf("signed entity: %w", err) } if _, ok := se.(oci.SignedImage); ok { - si, err := ociremote.SignedImage(ref) + si, err := ociremote.SignedImage(ref, regClientOpts...) if err != nil { return fmt.Errorf("getting signed image: %w", err) } @@ -67,7 +73,7 @@ func SaveCmd(_ context.Context, opts options.SaveOptions, imageRef string) error } if _, ok := se.(oci.SignedImageIndex); ok { - sii, err := ociremote.SignedImageIndex(ref) + sii, err := ociremote.SignedImageIndex(ref, regClientOpts...) if err != nil { return fmt.Errorf("getting signed image index: %w", err) } diff --git a/cmd/cosign/cli/sign/sign_blob.go b/cmd/cosign/cli/sign/sign_blob.go index b60db5b7a28..5a7b960b927 100644 --- a/cmd/cosign/cli/sign/sign_blob.go +++ b/cmd/cosign/cli/sign/sign_blob.go @@ -18,12 +18,15 @@ package sign import ( "context" "crypto/sha256" + "crypto/x509" "encoding/base64" "encoding/json" "fmt" "os" "path/filepath" + "google.golang.org/protobuf/encoding/protojson" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" "github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor" internal "github.com/sigstore/cosign/v2/internal/pkg/cosign" @@ -32,6 +35,9 @@ import ( "github.com/sigstore/cosign/v2/internal/ui" "github.com/sigstore/cosign/v2/pkg/cosign" cbundle "github.com/sigstore/cosign/v2/pkg/cosign/bundle" + protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" + protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" + "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/sigstore/pkg/cryptoutils" signatureoptions "github.com/sigstore/sigstore/pkg/signature/options" ) @@ -49,6 +55,7 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string } else { ui.Infof(ctx, "Using payload from: %s", payloadPath) f, err := os.Open(filepath.Clean(payloadPath)) + defer f.Close() if err != nil { return nil, err } @@ -68,23 +75,25 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string if err != nil { return nil, fmt.Errorf("signing blob: %w", err) } + digest := payload.Sum(nil) signedPayload := cosign.LocalSignedPayload{} - + var rekorEntry *models.LogEntryAnon var rfc3161Timestamp *cbundle.RFC3161Timestamp + var timestampBytes []byte + if ko.TSAServerURL != "" { - if ko.RFC3161TimestampPath == "" { - return nil, fmt.Errorf("timestamp output path must be set") + if ko.RFC3161TimestampPath == "" && !ko.NewBundleFormat { + return nil, fmt.Errorf("must use protobuf bundle or set timestamp output path") } - var respBytes []byte var err error if ko.TSAClientCACert == "" && ko.TSAClientCert == "" { // no mTLS params or custom CA - respBytes, err = tsa.GetTimestampedSignature(sig, client.NewTSAClient(ko.TSAServerURL)) + timestampBytes, err = tsa.GetTimestampedSignature(sig, client.NewTSAClient(ko.TSAServerURL)) if err != nil { return nil, err } } else { - respBytes, err = tsa.GetTimestampedSignature(sig, client.NewTSAClientMTLS(ko.TSAServerURL, + timestampBytes, err = tsa.GetTimestampedSignature(sig, client.NewTSAClientMTLS(ko.TSAServerURL, ko.TSAClientCACert, ko.TSAClientCert, ko.TSAClientKey, @@ -95,20 +104,23 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string } } - rfc3161Timestamp = cbundle.TimestampToRFC3161Timestamp(respBytes) + rfc3161Timestamp = cbundle.TimestampToRFC3161Timestamp(timestampBytes) // TODO: Consider uploading RFC3161 TS to Rekor if rfc3161Timestamp == nil { return nil, fmt.Errorf("rfc3161 timestamp is nil") } - ts, err := json.Marshal(rfc3161Timestamp) - if err != nil { - return nil, err - } - if err := os.WriteFile(ko.RFC3161TimestampPath, ts, 0600); err != nil { - return nil, fmt.Errorf("create RFC3161 timestamp file: %w", err) + + if ko.RFC3161TimestampPath != "" { + ts, err := json.Marshal(rfc3161Timestamp) + if err != nil { + return nil, err + } + if err := os.WriteFile(ko.RFC3161TimestampPath, ts, 0600); err != nil { + return nil, fmt.Errorf("create RFC3161 timestamp file: %w", err) + } + ui.Infof(ctx, "RFC3161 timestamp written to file %s\n", ko.RFC3161TimestampPath) } - ui.Infof(ctx, "RFC3161 timestamp written to file %s\n", ko.RFC3161TimestampPath) } shouldUpload, err := ShouldUploadToTlog(ctx, ko, nil, tlogUpload) if err != nil { @@ -123,28 +135,76 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string if err != nil { return nil, err } - entry, err := cosign.TLogUpload(ctx, rekorClient, sig, &payload, rekorBytes) + rekorEntry, err = cosign.TLogUpload(ctx, rekorClient, sig, &payload, rekorBytes) if err != nil { return nil, err } - ui.Infof(ctx, "tlog entry created with index: %d", *entry.LogIndex) - signedPayload.Bundle = cbundle.EntryToBundle(entry) + ui.Infof(ctx, "tlog entry created with index: %d", *rekorEntry.LogIndex) + signedPayload.Bundle = cbundle.EntryToBundle(rekorEntry) } // if bundle is specified, just do that and ignore the rest if ko.BundlePath != "" { - signedPayload.Base64Signature = base64.StdEncoding.EncodeToString(sig) + var contents []byte + if ko.NewBundleFormat { + // Determine if signature is certificate or not + var hint string + var rawCert []byte - certBytes, err := extractCertificate(ctx, sv) - if err != nil { - return nil, err - } - signedPayload.Cert = base64.StdEncoding.EncodeToString(certBytes) + signer, err := sv.Bytes(ctx) + if err != nil { + return nil, fmt.Errorf("error getting signer: %w", err) + } + cert, err := cryptoutils.UnmarshalCertificatesFromPEM(signer) + if err != nil || len(cert) == 0 { + pubKey, err := sv.PublicKey() + if err != nil { + return nil, err + } + pkixPubKey, err := x509.MarshalPKIXPublicKey(pubKey) + if err != nil { + return nil, err + } + hashedBytes := sha256.Sum256(pkixPubKey) + hint = base64.StdEncoding.EncodeToString(hashedBytes[:]) + } else { + rawCert = cert[0].Raw + } - contents, err := json.Marshal(signedPayload) - if err != nil { - return nil, err + bundle, err := cbundle.MakeProtobufBundle(hint, rawCert, rekorEntry, timestampBytes) + if err != nil { + return nil, err + } + + bundle.Content = &protobundle.Bundle_MessageSignature{ + MessageSignature: &protocommon.MessageSignature{ + MessageDigest: &protocommon.HashOutput{ + Algorithm: protocommon.HashAlgorithm_SHA2_256, + Digest: digest, + }, + Signature: sig, + }, + } + + contents, err = protojson.Marshal(bundle) + if err != nil { + return nil, err + } + } else { + signedPayload.Base64Signature = base64.StdEncoding.EncodeToString(sig) + + certBytes, err := extractCertificate(ctx, sv) + if err != nil { + return nil, err + } + signedPayload.Cert = base64.StdEncoding.EncodeToString(certBytes) + + contents, err = json.Marshal(signedPayload) + if err != nil { + return nil, err + } } + if err := os.WriteFile(ko.BundlePath, contents, 0600); err != nil { return nil, fmt.Errorf("create bundle file: %w", err) } diff --git a/cmd/cosign/cli/sign/sign_blob_test.go b/cmd/cosign/cli/sign/sign_blob_test.go new file mode 100644 index 00000000000..2f0853589a4 --- /dev/null +++ b/cmd/cosign/cli/sign/sign_blob_test.go @@ -0,0 +1,62 @@ +// Copyright 2024 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sign + +import ( + "os" + "path/filepath" + "testing" + + "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" + "github.com/sigstore/cosign/v2/pkg/cosign" +) + +func TestSignBlobCmd(t *testing.T) { + td := t.TempDir() + bundlePath := filepath.Join(td, "bundle.sigstore.json") + + keys, _ := cosign.GenerateKeyPair(nil) + keyRef := writeFile(t, td, string(keys.PrivateBytes), "key.pem") + + blob := []byte("foo") + blobPath := writeFile(t, td, string(blob), "foo.txt") + + rootOpts := &options.RootOptions{} + keyOpts := options.KeyOpts{KeyRef: keyRef, BundlePath: bundlePath} + + // Test happy path + _, err := SignBlobCmd(rootOpts, keyOpts, blobPath, true, "", "", false) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + // Test file outputs + keyOpts.NewBundleFormat = true + sigPath := filepath.Join(td, "output.sig") + certPath := filepath.Join(td, "output.pem") + _, err = SignBlobCmd(rootOpts, keyOpts, blobPath, false, sigPath, certPath, false) + if err != nil { + t.Fatalf("unexpected error %v", err) + } +} + +func writeFile(t *testing.T, td string, blob string, name string) string { + // Write blob to disk + blobPath := filepath.Join(td, name) + if err := os.WriteFile(blobPath, []byte(blob), 0644); err != nil { + t.Fatal(err) + } + return blobPath +} diff --git a/cmd/cosign/cli/signblob.go b/cmd/cosign/cli/signblob.go index 956edc6ef81..e1a2cbf8b0e 100644 --- a/cmd/cosign/cli/signblob.go +++ b/cmd/cosign/cli/signblob.go @@ -84,6 +84,7 @@ func SignBlob() *cobra.Command { OIDCRedirectURL: o.OIDC.RedirectURL, OIDCDisableProviders: o.OIDC.DisableAmbientProviders, BundlePath: o.BundlePath, + NewBundleFormat: o.NewBundleFormat, SkipConfirmation: o.SkipConfirmation, TSAClientCACert: o.TSAClientCACert, TSAClientCert: o.TSAClientCert, diff --git a/cmd/cosign/cli/verify.go b/cmd/cosign/cli/verify.go index d8c7ed9cd06..703898523b7 100644 --- a/cmd/cosign/cli/verify.go +++ b/cmd/cosign/cli/verify.go @@ -62,6 +62,10 @@ against the transparency log.`, # verify image with local certificate and certificate chain cosign verify --cert cosign.crt --cert-chain chain.crt + # verify image with local certificate and certificate bundles of CA roots + # and (optionally) CA intermediates + cosign verify --cert cosign.crt --ca-roots ca-roots.pem --ca-intermediates ca-intermediates.pem + # verify image using keyless verification with the given certificate # chain and identity parameters, without Fulcio roots (for BYO PKI): cosign verify --cert-chain chain.crt --certificate-oidc-issuer https://issuer.example.com --certificate-identity foo@example.com @@ -110,12 +114,14 @@ against the transparency log.`, CheckClaims: o.CheckClaims, KeyRef: o.Key, CertRef: o.CertVerify.Cert, + CertChain: o.CertVerify.CertChain, + CAIntermediates: o.CertVerify.CAIntermediates, + CARoots: o.CertVerify.CARoots, CertGithubWorkflowTrigger: o.CertVerify.CertGithubWorkflowTrigger, CertGithubWorkflowSha: o.CertVerify.CertGithubWorkflowSha, CertGithubWorkflowName: o.CertVerify.CertGithubWorkflowName, CertGithubWorkflowRepository: o.CertVerify.CertGithubWorkflowRepository, CertGithubWorkflowRef: o.CertVerify.CertGithubWorkflowRef, - CertChain: o.CertVerify.CertChain, IgnoreSCT: o.CertVerify.IgnoreSCT, SCTRef: o.CertVerify.SCT, Sk: o.SecurityKey.Use, @@ -217,6 +223,8 @@ against the transparency log.`, CertVerifyOptions: o.CertVerify, CertRef: o.CertVerify.Cert, CertChain: o.CertVerify.CertChain, + CAIntermediates: o.CertVerify.CAIntermediates, + CARoots: o.CertVerify.CARoots, CertGithubWorkflowTrigger: o.CertVerify.CertGithubWorkflowTrigger, CertGithubWorkflowSha: o.CertVerify.CertGithubWorkflowSha, CertGithubWorkflowName: o.CertVerify.CertGithubWorkflowName, @@ -275,6 +283,12 @@ The blob may be specified as a path to a file or - for stdin.`, # Verify a simple blob and message cosign verify-blob --key cosign.pub (--signature | msg) +# Verify a signature with certificate and CA certificate chain + cosign verify-blob --certificate cert.pem --certificate-chain certchain.pem --signature $sig + + # Verify a signature with CA roots and optional intermediate certificates + cosign verify-blob --certificate cert.pem --ca-roots caroots.pem [--ca-intermediates caintermediates.pem] --signature $sig + # Verify a signature from an environment variable cosign verify-blob --key cosign.pub --signature $sig msg @@ -319,6 +333,7 @@ The blob may be specified as a path to a file or - for stdin.`, Slot: o.SecurityKey.Slot, RekorURL: o.Rekor.URL, BundlePath: o.BundlePath, + NewBundleFormat: o.NewBundleFormat, RFC3161TimestampPath: o.RFC3161TimestampPath, TSACertChainPath: o.CommonVerifyOptions.TSACertChainPath, } @@ -327,7 +342,10 @@ The blob may be specified as a path to a file or - for stdin.`, CertVerifyOptions: o.CertVerify, CertRef: o.CertVerify.Cert, CertChain: o.CertVerify.CertChain, + CARoots: o.CertVerify.CARoots, + CAIntermediates: o.CertVerify.CAIntermediates, SigRef: o.Signature, + TrustedRootPath: o.TrustedRootPath, CertGithubWorkflowTrigger: o.CertVerify.CertGithubWorkflowTrigger, CertGithubWorkflowSHA: o.CertVerify.CertGithubWorkflowSha, CertGithubWorkflowName: o.CertVerify.CertGithubWorkflowName, @@ -337,6 +355,7 @@ The blob may be specified as a path to a file or - for stdin.`, SCTRef: o.CertVerify.SCT, Offline: o.CommonVerifyOptions.Offline, IgnoreTlog: o.CommonVerifyOptions.IgnoreTlog, + UseSignedTimestamps: o.CommonVerifyOptions.UseSignedTimestamps, } ctx, cancel := context.WithTimeout(cmd.Context(), ro.Timeout) @@ -385,6 +404,7 @@ The blob may be specified as a path to a file.`, Slot: o.SecurityKey.Slot, RekorURL: o.Rekor.URL, BundlePath: o.BundlePath, + NewBundleFormat: o.NewBundleFormat, RFC3161TimestampPath: o.RFC3161TimestampPath, TSACertChainPath: o.CommonVerifyOptions.TSACertChainPath, } @@ -394,8 +414,11 @@ The blob may be specified as a path to a file.`, CheckClaims: o.CheckClaims, SignaturePath: o.SignaturePath, CertVerifyOptions: o.CertVerify, + TrustedRootPath: o.TrustedRootPath, CertRef: o.CertVerify.Cert, CertChain: o.CertVerify.CertChain, + CARoots: o.CertVerify.CARoots, + CAIntermediates: o.CertVerify.CAIntermediates, CertGithubWorkflowTrigger: o.CertVerify.CertGithubWorkflowTrigger, CertGithubWorkflowSHA: o.CertVerify.CertGithubWorkflowSha, CertGithubWorkflowName: o.CertVerify.CertGithubWorkflowName, @@ -405,6 +428,7 @@ The blob may be specified as a path to a file.`, SCTRef: o.CertVerify.SCT, Offline: o.CommonVerifyOptions.Offline, IgnoreTlog: o.CommonVerifyOptions.IgnoreTlog, + UseSignedTimestamps: o.CommonVerifyOptions.UseSignedTimestamps, } // We only use the blob if we are checking claims. if len(args) == 0 && o.CheckClaims { diff --git a/cmd/cosign/cli/verify/verify.go b/cmd/cosign/cli/verify/verify.go index de91d9229a8..17fd63e8330 100644 --- a/cmd/cosign/cli/verify/verify.go +++ b/cmd/cosign/cli/verify/verify.go @@ -34,7 +34,6 @@ import ( "github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor" "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" cosignError "github.com/sigstore/cosign/v2/cmd/cosign/errors" - "github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa" "github.com/sigstore/cosign/v2/internal/ui" "github.com/sigstore/cosign/v2/pkg/blob" "github.com/sigstore/cosign/v2/pkg/cosign" @@ -60,6 +59,8 @@ type VerifyCommand struct { CertGithubWorkflowName string CertGithubWorkflowRepository string CertGithubWorkflowRef string + CAIntermediates string + CARoots string CertChain string CertOidcProvider string IgnoreSCT bool @@ -77,11 +78,23 @@ type VerifyCommand struct { NameOptions []name.Option Offline bool TSACertChainPath string + UseSignedTimestamps bool IgnoreTlog bool MaxWorkers int ExperimentalOCI11 bool } +func (c *VerifyCommand) loadTSACertificates(ctx context.Context) (*cosign.TSACertificates, error) { + if c.TSACertChainPath == "" && !c.UseSignedTimestamps { + return nil, fmt.Errorf("TSA certificate chain path not provided and use-signed-timestamps not set") + } + tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets) + if err != nil { + return nil, fmt.Errorf("unable to load TSA certificates: %w", err) + } + return tsaCertificates, nil +} + // Exec runs the verification command func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { if len(images) == 0 { @@ -136,29 +149,14 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { co.ClaimVerifier = cosign.SimpleClaimVerifier } - if c.TSACertChainPath != "" { - _, err := os.Stat(c.TSACertChainPath) + if c.TSACertChainPath != "" || c.UseSignedTimestamps { + tsaCertificates, err := c.loadTSACertificates(ctx) if err != nil { - return fmt.Errorf("unable to open timestamp certificate chain file: %w", err) + return fmt.Errorf("unable to load TSA certificates: %w", err) } - // TODO: Add support for TUF certificates. - pemBytes, err := os.ReadFile(filepath.Clean(c.TSACertChainPath)) - if err != nil { - return fmt.Errorf("error reading certification chain path file: %w", err) - } - - leaves, intermediates, roots, err := tsa.SplitPEMCertificateChain(pemBytes) - if err != nil { - return fmt.Errorf("error splitting certificates: %w", err) - } - if len(leaves) > 1 { - return fmt.Errorf("certificate chain must contain at most one TSA certificate") - } - if len(leaves) == 1 { - co.TSACertificate = leaves[0] - } - co.TSAIntermediateCertificates = intermediates - co.TSARootCertificates = roots + co.TSACertificate = tsaCertificates.LeafCert + co.TSARootCertificates = tsaCertificates.RootCert + co.TSAIntermediateCertificates = tsaCertificates.IntermediateCerts } if !c.IgnoreTlog { @@ -177,32 +175,11 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { } } if keylessVerification(c.KeyRef, c.Sk) { - if c.CertChain != "" { - chain, err := loadCertChainFromFileOrURL(c.CertChain) - if err != nil { - return err - } - co.RootCerts = x509.NewCertPool() - co.RootCerts.AddCert(chain[len(chain)-1]) - if len(chain) > 1 { - co.IntermediateCerts = x509.NewCertPool() - for _, cert := range chain[:len(chain)-1] { - co.IntermediateCerts.AddCert(cert) - } - } - } else { - // This performs an online fetch of the Fulcio roots. This is needed - // for verifying keyless certificates (both online and offline). - co.RootCerts, err = fulcio.GetRoots() - if err != nil { - return fmt.Errorf("getting Fulcio roots: %w", err) - } - co.IntermediateCerts, err = fulcio.GetIntermediates() - if err != nil { - return fmt.Errorf("getting Fulcio intermediates: %w", err) - } + if err := loadCertsKeylessVerification(c.CertChain, c.CARoots, c.CAIntermediates, co); err != nil { + return err } } + keyRef := c.KeyRef certRef := c.CertRef @@ -241,8 +218,9 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { if err != nil { return err } - if c.CertChain == "" { - // If no certChain is passed, the Fulcio root certificate will be used + switch { + case c.CertChain == "" && co.RootCerts == nil: + // If no certChain and no CARoots are passed, the Fulcio root certificate will be used co.RootCerts, err = fulcio.GetRoots() if err != nil { return fmt.Errorf("getting Fulcio roots: %w", err) @@ -255,7 +233,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { if err != nil { return err } - } else { + case c.CertChain != "": // Verify certificate with chain chain, err := loadCertChainFromFileOrURL(c.CertChain) if err != nil { @@ -265,7 +243,16 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { if err != nil { return err } + case co.RootCerts != nil: + // Verify certificate with root (and if given, intermediate) certificate + pubKey, err = cosign.ValidateAndUnpackCert(cert, co) + if err != nil { + return err + } + default: + return errors.New("no certificate chain provided to verify certificate") } + if c.SCTRef != "" { sct, err := os.ReadFile(filepath.Clean(c.SCTRef)) if err != nil { @@ -273,6 +260,9 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) { } co.SCT = sct } + default: + // Do nothing. Neither keyRef, c.Sk, nor certRef were set - can happen for example when using Fulcio and TSA. + // For an example see the TestAttachWithRFC3161Timestamp test in test/e2e_test.go. } co.SigVerifier = pubKey @@ -521,3 +511,66 @@ func shouldVerifySCT(ignoreSCT bool, keyRef string, sk bool) bool { } return true } + +// loadCertsKeylessVerification loads certificates provided as a certificate chain or CA roots + CA intermediate +// certificate files. If both certChain and caRootsFile are empty strings, the Fulcio roots are loaded. +// +// The co *cosign.CheckOpts is both input and output parameter - it gets updated +// with the root and intermediate certificates needed for verification. +func loadCertsKeylessVerification(certChainFile string, + caRootsFile string, + caIntermediatesFile string, + co *cosign.CheckOpts) error { + var err error + switch { + case certChainFile != "": + chain, err := loadCertChainFromFileOrURL(certChainFile) + if err != nil { + return err + } + co.RootCerts = x509.NewCertPool() + co.RootCerts.AddCert(chain[len(chain)-1]) + if len(chain) > 1 { + co.IntermediateCerts = x509.NewCertPool() + for _, cert := range chain[:len(chain)-1] { + co.IntermediateCerts.AddCert(cert) + } + } + case caRootsFile != "": + caRoots, err := loadCertChainFromFileOrURL(caRootsFile) + if err != nil { + return err + } + co.RootCerts = x509.NewCertPool() + if len(caRoots) > 0 { + for _, cert := range caRoots { + co.RootCerts.AddCert(cert) + } + } + if caIntermediatesFile != "" { + caIntermediates, err := loadCertChainFromFileOrURL(caIntermediatesFile) + if err != nil { + return err + } + if len(caIntermediates) > 0 { + co.IntermediateCerts = x509.NewCertPool() + for _, cert := range caIntermediates { + co.IntermediateCerts.AddCert(cert) + } + } + } + default: + // This performs an online fetch of the Fulcio roots from a TUF repository. + // This is needed for verifying keyless certificates (both online and offline). + co.RootCerts, err = fulcio.GetRoots() + if err != nil { + return fmt.Errorf("getting Fulcio roots: %w", err) + } + co.IntermediateCerts, err = fulcio.GetIntermediates() + if err != nil { + return fmt.Errorf("getting Fulcio intermediates: %w", err) + } + } + + return nil +} diff --git a/cmd/cosign/cli/verify/verify_attestation.go b/cmd/cosign/cli/verify/verify_attestation.go index a9ff6dd9982..93c27690455 100644 --- a/cmd/cosign/cli/verify/verify_attestation.go +++ b/cmd/cosign/cli/verify/verify_attestation.go @@ -28,7 +28,6 @@ import ( "github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" "github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor" - "github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa" "github.com/sigstore/cosign/v2/internal/ui" "github.com/sigstore/cosign/v2/pkg/cosign" "github.com/sigstore/cosign/v2/pkg/cosign/cue" @@ -53,6 +52,8 @@ type VerifyAttestationCommand struct { CertGithubWorkflowName string CertGithubWorkflowRepository string CertGithubWorkflowRef string + CAIntermediates string + CARoots string CertChain string IgnoreSCT bool SCTRef string @@ -68,6 +69,18 @@ type VerifyAttestationCommand struct { TSACertChainPath string IgnoreTlog bool MaxWorkers int + UseSignedTimestamps bool +} + +func (c *VerifyAttestationCommand) loadTSACertificates(ctx context.Context) (*cosign.TSACertificates, error) { + if c.TSACertChainPath == "" && !c.UseSignedTimestamps { + return nil, fmt.Errorf("TSA certificate chain path not provided and use-signed-timestamps not set") + } + tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets) + if err != nil { + return nil, fmt.Errorf("unable to load TSA certificates: %w", err) + } + return tsaCertificates, nil } // Exec runs the verification command @@ -118,30 +131,16 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e } } - if c.TSACertChainPath != "" { - _, err := os.Stat(c.TSACertChainPath) - if err != nil { - return fmt.Errorf("unable to open timestamp certificate chain file '%s: %w", c.TSACertChainPath, err) - } - // TODO: Add support for TUF certificates. - pemBytes, err := os.ReadFile(filepath.Clean(c.TSACertChainPath)) - if err != nil { - return fmt.Errorf("error reading certification chain path file: %w", err) - } - - leaves, intermediates, roots, err := tsa.SplitPEMCertificateChain(pemBytes) + if c.TSACertChainPath != "" || c.UseSignedTimestamps { + tsaCertificates, err := c.loadTSACertificates(ctx) if err != nil { - return fmt.Errorf("error splitting certificates: %w", err) + return fmt.Errorf("unable to load TSA certificates: %w", err) } - if len(leaves) > 1 { - return fmt.Errorf("certificate chain must contain at most one TSA certificate") - } - if len(leaves) == 1 { - co.TSACertificate = leaves[0] - } - co.TSAIntermediateCertificates = intermediates - co.TSARootCertificates = roots + co.TSACertificate = tsaCertificates.LeafCert + co.TSARootCertificates = tsaCertificates.RootCert + co.TSAIntermediateCertificates = tsaCertificates.IntermediateCerts } + if !c.IgnoreTlog { if c.RekorURL != "" { rekorClient, err := rekor.NewClient(c.RekorURL) @@ -157,18 +156,13 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e return fmt.Errorf("getting Rekor public keys: %w", err) } } + if keylessVerification(c.KeyRef, c.Sk) { - // This performs an online fetch of the Fulcio roots. This is needed - // for verifying keyless certificates (both online and offline). - co.RootCerts, err = fulcio.GetRoots() - if err != nil { - return fmt.Errorf("getting Fulcio roots: %w", err) - } - co.IntermediateCerts, err = fulcio.GetIntermediates() - if err != nil { - return fmt.Errorf("getting Fulcio intermediates: %w", err) + if err := loadCertsKeylessVerification(c.CertChain, c.CARoots, c.CAIntermediates, co); err != nil { + return err } } + keyRef := c.KeyRef // Keys are optional! @@ -229,6 +223,9 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e } co.SCT = sct } + case c.CARoots != "": + // CA roots + possible intermediates are already loaded into co.RootCerts with the call to + // loadCertsKeylessVerification above. } // NB: There are only 2 kinds of verification right now: diff --git a/cmd/cosign/cli/verify/verify_blob.go b/cmd/cosign/cli/verify/verify_blob.go index bd172a3aae1..79475c90d80 100644 --- a/cmd/cosign/cli/verify/verify_blob.go +++ b/cmd/cosign/cli/verify/verify_blob.go @@ -28,10 +28,8 @@ import ( "os" "path/filepath" - "github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" "github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor" - "github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa" "github.com/sigstore/cosign/v2/internal/ui" "github.com/sigstore/cosign/v2/pkg/blob" "github.com/sigstore/cosign/v2/pkg/cosign" @@ -54,8 +52,11 @@ type VerifyBlobCmd struct { options.KeyOpts options.CertVerifyOptions CertRef string + CAIntermediates string + CARoots string CertChain string SigRef string + TrustedRootPath string CertGithubWorkflowTrigger string CertGithubWorkflowSHA string CertGithubWorkflowName string @@ -64,14 +65,23 @@ type VerifyBlobCmd struct { IgnoreSCT bool SCTRef string Offline bool + UseSignedTimestamps bool IgnoreTlog bool } +func (c *VerifyBlobCmd) loadTSACertificates(ctx context.Context) (*cosign.TSACertificates, error) { + if c.TSACertChainPath == "" && !c.UseSignedTimestamps { + return nil, fmt.Errorf("either TSA certificate chain path must be provided or use-signed-timestamps must be set") + } + tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets) + if err != nil { + return nil, fmt.Errorf("unable to load TSA certificates: %w", err) + } + return tsaCertificates, nil +} + // nolint func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { - var cert *x509.Certificate - opts := make([]static.Option, 0) - // Require a certificate/key OR a local bundle file that has the cert. if options.NOf(c.KeyRef, c.CertRef, c.Sk, c.BundlePath) == 0 { return fmt.Errorf("provide a key with --key or --sk, a certificate to verify against with --certificate, or a bundle with --bundle") @@ -82,6 +92,22 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { return &options.PubKeyParseError{} } + if c.KeyOpts.NewBundleFormat { + if options.NOf(c.RFC3161TimestampPath, c.TSACertChainPath, c.RekorURL, c.CertChain, c.CARoots, c.CAIntermediates, c.CertRef, c.SigRef, c.SCTRef) > 1 { + return fmt.Errorf("when using --new-bundle-format, please supply signed content with --bundle and verification content with --trusted-root") + } + err := verifyNewBundle(ctx, c.BundlePath, c.TrustedRootPath, c.KeyRef, c.Slot, c.CertVerifyOptions.CertOidcIssuer, c.CertVerifyOptions.CertOidcIssuerRegexp, c.CertVerifyOptions.CertIdentity, c.CertVerifyOptions.CertIdentityRegexp, c.CertGithubWorkflowTrigger, c.CertGithubWorkflowSHA, c.CertGithubWorkflowName, c.CertGithubWorkflowRepository, c.CertGithubWorkflowRef, blobRef, c.Sk, c.IgnoreTlog, c.UseSignedTimestamps, c.IgnoreSCT) + if err == nil { + ui.Infof(ctx, "Verified OK") + } + return err + } else if c.TrustedRootPath != "" { + return fmt.Errorf("--trusted-root only supported with --new-bundle-format") + } + + var cert *x509.Certificate + opts := make([]static.Option, 0) + var identities []cosign.Identity var err error if c.KeyRef == "" { @@ -112,32 +138,17 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { Offline: c.Offline, IgnoreTlog: c.IgnoreTlog, } - if c.RFC3161TimestampPath != "" && c.KeyOpts.TSACertChainPath == "" { - return fmt.Errorf("timestamp-certificate-chain is required to validate a RFC3161 timestamp") + if c.RFC3161TimestampPath != "" && !(c.TSACertChainPath != "" || c.UseSignedTimestamps) { + return fmt.Errorf("either TSA certificate chain path must be provided or use-signed-timestamps must be set when using RFC3161 timestamp path") } - if c.KeyOpts.TSACertChainPath != "" { - _, err := os.Stat(c.KeyOpts.TSACertChainPath) + if c.TSACertChainPath != "" || c.UseSignedTimestamps { + tsaCertificates, err := c.loadTSACertificates(ctx) if err != nil { - return fmt.Errorf("unable to open timestamp certificate chain file '%s: %w", c.KeyOpts.TSACertChainPath, err) - } - // TODO: Add support for TUF certificates. - pemBytes, err := os.ReadFile(filepath.Clean(c.KeyOpts.TSACertChainPath)) - if err != nil { - return fmt.Errorf("error reading certification chain path file: %w", err) - } - - leaves, intermediates, roots, err := tsa.SplitPEMCertificateChain(pemBytes) - if err != nil { - return fmt.Errorf("error splitting certificates: %w", err) - } - if len(leaves) > 1 { - return fmt.Errorf("certificate chain must contain at most one TSA certificate") - } - if len(leaves) == 1 { - co.TSACertificate = leaves[0] + return err } - co.TSAIntermediateCertificates = intermediates - co.TSARootCertificates = roots + co.TSACertificate = tsaCertificates.LeafCert + co.TSARootCertificates = tsaCertificates.RootCert + co.TSAIntermediateCertificates = tsaCertificates.IntermediateCerts } if !c.IgnoreTlog { @@ -155,19 +166,10 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { return fmt.Errorf("getting Rekor public keys: %w", err) } } + if keylessVerification(c.KeyRef, c.Sk) { - // Use default TUF roots if a cert chain is not provided. - // This performs an online fetch of the Fulcio roots. This is needed - // for verifying keyless certificates (both online and offline). - if c.CertChain == "" { - co.RootCerts, err = fulcio.GetRoots() - if err != nil { - return fmt.Errorf("getting Fulcio roots: %w", err) - } - co.IntermediateCerts, err = fulcio.GetIntermediates() - if err != nil { - return fmt.Errorf("getting Fulcio intermediates: %w", err) - } + if err := loadCertsKeylessVerification(c.CertChain, c.CARoots, c.CAIntermediates, co); err != nil { + return err } } @@ -253,7 +255,8 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { } // Set a cert chain if provided. var chainPEM []byte - if c.CertChain != "" { + switch { + case c.CertChain != "": chain, err := loadCertChainFromFileOrURL(c.CertChain) if err != nil { return err @@ -273,6 +276,9 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error { if err != nil { return err } + case c.CARoots != "": + // CA roots + possible intermediates are already loaded into co.RootCerts with the call to + // loadCertsKeylessVerification above. } // Gather the cert for the signature and add the cert along with the diff --git a/cmd/cosign/cli/verify/verify_blob_attestation.go b/cmd/cosign/cli/verify/verify_blob_attestation.go index 63983eb4c2d..3f2c33cc63b 100644 --- a/cmd/cosign/cli/verify/verify_blob_attestation.go +++ b/cmd/cosign/cli/verify/verify_blob_attestation.go @@ -30,12 +30,10 @@ import ( "path/filepath" v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" "github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor" internal "github.com/sigstore/cosign/v2/internal/pkg/cosign" payloadsize "github.com/sigstore/cosign/v2/internal/pkg/cosign/payload/size" - "github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa" "github.com/sigstore/cosign/v2/pkg/blob" "github.com/sigstore/cosign/v2/pkg/cosign" "github.com/sigstore/cosign/v2/pkg/cosign/bundle" @@ -53,8 +51,11 @@ type VerifyBlobAttestationCommand struct { options.KeyOpts options.CertVerifyOptions - CertRef string - CertChain string + CertRef string + CertChain string + CAIntermediates string + CARoots string + TrustedRootPath string CertGithubWorkflowTrigger string CertGithubWorkflowSHA string @@ -71,7 +72,8 @@ type VerifyBlobAttestationCommand struct { PredicateType string // TODO: Add policies - SignaturePath string // Path to the signature + SignaturePath string // Path to the signature + UseSignedTimestamps bool } // Exec runs the verification command @@ -90,6 +92,19 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st return &options.KeyParseError{} } + if c.KeyOpts.NewBundleFormat { + if options.NOf(c.RFC3161TimestampPath, c.TSACertChainPath, c.RekorURL, c.CertChain, c.CARoots, c.CAIntermediates, c.CertRef, c.SCTRef) > 1 { + return fmt.Errorf("when using --new-bundle-format, please supply signed content with --bundle and verification content with --trusted-root") + } + err = verifyNewBundle(ctx, c.BundlePath, c.TrustedRootPath, c.KeyRef, c.Slot, c.CertVerifyOptions.CertOidcIssuer, c.CertVerifyOptions.CertOidcIssuerRegexp, c.CertVerifyOptions.CertIdentity, c.CertVerifyOptions.CertIdentityRegexp, c.CertGithubWorkflowTrigger, c.CertGithubWorkflowSHA, c.CertGithubWorkflowName, c.CertGithubWorkflowRepository, c.CertGithubWorkflowRef, artifactPath, c.Sk, c.IgnoreTlog, c.UseSignedTimestamps, c.IgnoreSCT) + if err == nil { + fmt.Fprintln(os.Stderr, "Verified OK") + } + return err + } else if c.TrustedRootPath != "" { + return fmt.Errorf("--trusted-root only supported with --new-bundle-format") + } + var identities []cosign.Identity if c.KeyRef == "" { identities, err = c.Identities() @@ -140,32 +155,18 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st } // Set up TSA, Fulcio roots and tlog public keys and clients. - if c.RFC3161TimestampPath != "" && c.KeyOpts.TSACertChainPath == "" { - return fmt.Errorf("timestamp-cert-chain is required to validate a rfc3161 timestamp bundle") + if c.RFC3161TimestampPath != "" && !(c.TSACertChainPath != "" || c.UseSignedTimestamps) { + return fmt.Errorf("either TSA certificate chain path must be provided or use-signed-timestamps must be set when using RFC3161 timestamp path") } - if c.KeyOpts.TSACertChainPath != "" { - _, err := os.Stat(c.TSACertChainPath) - if err != nil { - return fmt.Errorf("unable to open timestamp certificate chain file: %w", err) - } - // TODO: Add support for TUF certificates. - pemBytes, err := os.ReadFile(filepath.Clean(c.TSACertChainPath)) - if err != nil { - return fmt.Errorf("error reading certification chain path file: %w", err) - } - leaves, intermediates, roots, err := tsa.SplitPEMCertificateChain(pemBytes) + if c.TSACertChainPath != "" || c.UseSignedTimestamps { + tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets) if err != nil { - return fmt.Errorf("error splitting certificates: %w", err) + return fmt.Errorf("unable to load or get TSA certificates: %w", err) } - if len(leaves) > 1 { - return fmt.Errorf("certificate chain must contain at most one TSA certificate") - } - if len(leaves) == 1 { - co.TSACertificate = leaves[0] - } - co.TSAIntermediateCertificates = intermediates - co.TSARootCertificates = roots + co.TSACertificate = tsaCertificates.LeafCert + co.TSARootCertificates = tsaCertificates.RootCert + co.TSAIntermediateCertificates = tsaCertificates.IntermediateCerts } if !c.IgnoreTlog { @@ -184,20 +185,11 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st } } if keylessVerification(c.KeyRef, c.Sk) { - // Use default TUF roots if a cert chain is not provided. - // This performs an online fetch of the Fulcio roots. This is needed - // for verifying keyless certificates (both online and offline). - if c.CertChain == "" { - co.RootCerts, err = fulcio.GetRoots() - if err != nil { - return fmt.Errorf("getting Fulcio roots: %w", err) - } - co.IntermediateCerts, err = fulcio.GetIntermediates() - if err != nil { - return fmt.Errorf("getting Fulcio intermediates: %w", err) - } + if err := loadCertsKeylessVerification(c.CertChain, c.CARoots, c.CAIntermediates, co); err != nil { + return err } } + // Ignore Signed Certificate Timestamp if the flag is set or a key is provided if shouldVerifySCT(c.IgnoreSCT, c.KeyRef, c.Sk) { co.CTLogPubKeys, err = cosign.GetCTLogPubs(ctx) @@ -242,6 +234,9 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st if err != nil { return err } + case c.CARoots != "": + // CA roots + possible intermediates are already loaded into co.RootCerts with the call to + // loadCertsKeylessVerification above. } if c.BundlePath != "" { b, err := cosign.FetchLocalSignedPayloadFromPath(c.BundlePath) diff --git a/cmd/cosign/cli/verify/verify_blob_attestation_test.go b/cmd/cosign/cli/verify/verify_blob_attestation_test.go index 2d87efeb451..58121544420 100644 --- a/cmd/cosign/cli/verify/verify_blob_attestation_test.go +++ b/cmd/cosign/cli/verify/verify_blob_attestation_test.go @@ -18,9 +18,15 @@ import ( "context" "encoding/base64" "os" + "path/filepath" "testing" + protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" + protodsse "github.com/sigstore/protobuf-specs/gen/pb-go/dsse" + "google.golang.org/protobuf/encoding/protojson" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" + "github.com/sigstore/cosign/v2/pkg/cosign/bundle" ) const pubkey = `-----BEGIN PUBLIC KEY----- @@ -53,6 +59,7 @@ func TestVerifyBlobAttestation(t *testing.T) { tests := []struct { description string blobPath string + bundlePath string signature string predicateType string env map[string]string @@ -107,6 +114,17 @@ func TestVerifyBlobAttestation(t *testing.T) { blobPath: hugeBlobPath, env: map[string]string{"COSIGN_MAX_ATTACHMENT_SIZE": "128"}, shouldErr: true, + }, { + description: "verify new bundle with public key", + // From blobSLSAProvenanceSignature + bundlePath: makeLocalAttestNewBundle(t, "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsInN1YmplY3QiOlt7Im5hbWUiOiJibG9iIiwiZGlnZXN0Ijp7InNoYTI1NiI6IjY1ODc4MWNkNGVkOWJjYTYwZGFjZDA5ZjdiYjkxNGJiNTE1MDJlOGI1ZDYxOWY1N2YzOWExZDY1MjU5NmNjMjQifX1dLCJwcmVkaWNhdGUiOnsiYnVpbGRlciI6eyJpZCI6IjIifSwiYnVpbGRUeXBlIjoieCIsImludm9jYXRpb24iOnsiY29uZmlnU291cmNlIjp7fX19fQ==", "application/vnd.in-toto+json", "MEUCIA8KjZqkrt90fzBojSwwtj3Bqb41E6ruxQk97TLnpzdYAiEAzOAjOTzyvTHqbpFDAn6zhrg6EZv7kxK5faRoVGYMh2c="), + blobPath: blobPath, + }, { + description: "verify new bundle with public key - bad sig", + // From blobSLSAProvenanceSignature + bundlePath: makeLocalAttestNewBundle(t, "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsInN1YmplY3QiOlt7Im5hbWUiOiJibG9iIiwiZGlnZXN0Ijp7InNoYTI1NiI6IjY1ODc4MWNkNGVkOWJjYTYwZGFjZDA5ZjdiYjkxNGJiNTE1MDJlOGI1ZDYxOWY1N2YzOWExZDY1MjU5NmNjMjQifX1dLCJwcmVkaWNhdGUiOnsiYnVpbGRlciI6eyJpZCI6IjIifSwiYnVpbGRUeXBlIjoieCIsImludm9jYXRpb24iOnsiY29uZmlnU291cmNlIjp7fX19fQ==", "application/vnd.in-toto+json", "c29tZXRoaW5nCg=="), + blobPath: blobPath, + shouldErr: true, }, } @@ -128,6 +146,11 @@ func TestVerifyBlobAttestation(t *testing.T) { CheckClaims: true, PredicateType: test.predicateType, } + if test.bundlePath != "" { + cmd.KeyOpts.BundlePath = test.bundlePath + cmd.KeyOpts.NewBundleFormat = true + cmd.TrustedRootPath = writeTrustedRootFile(t, td, "{\"mediaType\":\"application/vnd.dev.sigstore.trustedroot+json;version=0.1\"}") + } err = cmd.Exec(ctx, test.blobPath) if (err != nil) != test.shouldErr { @@ -191,3 +214,45 @@ func TestVerifyBlobAttestationNoCheckClaims(t *testing.T) { }) } } + +func makeLocalAttestNewBundle(t *testing.T, payload, payloadType, sig string) string { + b, err := bundle.MakeProtobufBundle("hint", []byte{}, nil, []byte{}) + if err != nil { + t.Fatal(err) + } + + decodedPayload, err := base64.StdEncoding.DecodeString(payload) + if err != nil { + t.Fatal(err) + } + + decodedSig, err := base64.StdEncoding.DecodeString(sig) + if err != nil { + t.Fatal(err) + } + + b.Content = &protobundle.Bundle_DsseEnvelope{ + DsseEnvelope: &protodsse.Envelope{ + Payload: decodedPayload, + PayloadType: payloadType, + Signatures: []*protodsse.Signature{ + { + Sig: decodedSig, + }, + }, + }, + } + + contents, err := protojson.Marshal(b) + if err != nil { + t.Fatal(err) + } + + // write bundle to disk + td := t.TempDir() + bundlePath := filepath.Join(td, "bundle.sigstore.json") + if err := os.WriteFile(bundlePath, contents, 0644); err != nil { + t.Fatal(err) + } + return bundlePath +} diff --git a/cmd/cosign/cli/verify/verify_blob_test.go b/cmd/cosign/cli/verify/verify_blob_test.go index 1b8b3a3429d..6b1b127052c 100644 --- a/cmd/cosign/cli/verify/verify_blob_test.go +++ b/cmd/cosign/cli/verify/verify_blob_test.go @@ -45,6 +45,8 @@ import ( sigs "github.com/sigstore/cosign/v2/pkg/signature" ctypes "github.com/sigstore/cosign/v2/pkg/types" "github.com/sigstore/cosign/v2/test" + protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" + protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" @@ -57,6 +59,7 @@ import ( "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/dsse" signatureoptions "github.com/sigstore/sigstore/pkg/signature/options" + "google.golang.org/protobuf/encoding/protojson" ) func TestSignaturesRef(t *testing.T) { @@ -235,6 +238,7 @@ func TestVerifyBlob(t *testing.T) { key []byte cert *x509.Certificate bundlePath string + newBundle bool // The rekor entry response when Rekor is enabled rekorEntry []*models.LogEntry skipTlogVerify bool @@ -320,6 +324,26 @@ func TestVerifyBlob(t *testing.T) { pubKeyBytes, true), shouldErr: true, }, + { + name: "valid signature with public key - new bundle", + blob: blobBytes, + signature: blobSignature, + key: pubKeyBytes, + bundlePath: makeLocalNewBundle(t, []byte(blobSignature), sha256.Sum256(blobBytes)), + newBundle: true, + skipTlogVerify: true, + shouldErr: false, + }, + { + name: "invalid signature with public key - new bundle", + blob: blobBytes, + signature: otherSignature, + key: pubKeyBytes, + bundlePath: makeLocalNewBundle(t, []byte(blobSignature), sha256.Sum256(blobBytes)), + newBundle: true, + skipTlogVerify: true, + shouldErr: false, + }, { name: "invalid signature with public key", blob: blobBytes, @@ -563,6 +587,7 @@ func TestVerifyBlob(t *testing.T) { cmd := VerifyBlobCmd{ KeyOpts: options.KeyOpts{ BundlePath: tt.bundlePath, + NewBundleFormat: tt.newBundle, RekorURL: testServer.URL, RFC3161TimestampPath: tt.tsPath, TSACertChainPath: tt.tsChainPath, @@ -592,6 +617,13 @@ func TestVerifyBlob(t *testing.T) { keyPath := writeBlobFile(t, td, string(tt.key), "key.pem") cmd.KeyRef = keyPath } + if tt.newBundle { + cmd.TrustedRootPath = writeTrustedRootFile(t, td, "{\"mediaType\":\"application/vnd.dev.sigstore.trustedroot+json;version=0.1\"}") + cmd.KeyOpts.RekorURL = "" + cmd.KeyOpts.RFC3161TimestampPath = "" + cmd.KeyOpts.TSACertChainPath = "" + cmd.CertChain = "" + } err := cmd.Exec(context.Background(), blobPath) if (err != nil) != tt.shouldErr { @@ -757,6 +789,36 @@ func makeLocalBundleWithoutRekorBundle(t *testing.T, sig []byte, svBytes []byte) return bundlePath } +func makeLocalNewBundle(t *testing.T, sig []byte, digest [32]byte) string { + b, err := bundle.MakeProtobufBundle("hint", []byte{}, nil, []byte{}) + if err != nil { + t.Fatal(err) + } + + b.Content = &protobundle.Bundle_MessageSignature{ + MessageSignature: &protocommon.MessageSignature{ + MessageDigest: &protocommon.HashOutput{ + Algorithm: protocommon.HashAlgorithm_SHA2_256, + Digest: digest[:], + }, + Signature: sig, + }, + } + + contents, err := protojson.Marshal(b) + if err != nil { + t.Fatal(err) + } + + // write bundle to disk + td := t.TempDir() + bundlePath := filepath.Join(td, "bundle.sigstore.json") + if err := os.WriteFile(bundlePath, contents, 0644); err != nil { + t.Fatal(err) + } + return bundlePath +} + func TestVerifyBlobCmdWithBundle(t *testing.T) { keyless := newKeylessStack(t) defer os.RemoveAll(keyless.td) @@ -1574,3 +1636,11 @@ func writeTimestampFile(t *testing.T, td string, ts *bundle.RFC3161Timestamp, na } return path } + +func writeTrustedRootFile(t *testing.T, td, contents string) string { + path := filepath.Join(td, "trusted_root.json") + if err := os.WriteFile(path, []byte(contents), 0644); err != nil { + t.Fatal(err) + } + return path +} diff --git a/cmd/cosign/cli/verify/verify_bundle.go b/cmd/cosign/cli/verify/verify_bundle.go new file mode 100644 index 00000000000..01921be7ffb --- /dev/null +++ b/cmd/cosign/cli/verify/verify_bundle.go @@ -0,0 +1,163 @@ +// +// Copyright 2024 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package verify + +import ( + "bytes" + "context" + "fmt" + "time" + + sgbundle "github.com/sigstore/sigstore-go/pkg/bundle" + "github.com/sigstore/sigstore-go/pkg/fulcio/certificate" + "github.com/sigstore/sigstore-go/pkg/root" + "github.com/sigstore/sigstore-go/pkg/verify" + + "github.com/sigstore/cosign/v2/pkg/cosign/pivkey" + sigs "github.com/sigstore/cosign/v2/pkg/signature" +) + +type verifyTrustedMaterial struct { + root.TrustedMaterial + keyTrustedMaterial root.TrustedMaterial +} + +func (v *verifyTrustedMaterial) PublicKeyVerifier(hint string) (root.TimeConstrainedVerifier, error) { + return v.keyTrustedMaterial.PublicKeyVerifier(hint) +} + +func verifyNewBundle(ctx context.Context, bundlePath, trustedRootPath, keyRef, slot, certOIDCIssuer, certOIDCIssuerRegex, certIdentity, certIdentityRegexp, githubWorkflowTrigger, githubWorkflowSHA, githubWorkflowName, githubWorkflowRepository, githubWorkflowRef, artifactRef string, sk, ignoreTlog, useSignedTimestamps, ignoreSCT bool) error { + bundle, err := sgbundle.LoadJSONFromPath(bundlePath) + if err != nil { + return err + } + + var trustedroot *root.TrustedRoot + + if trustedRootPath == "" { + // Assume we're using public good instance; fetch via TUF + trustedroot, err = root.FetchTrustedRoot() + if err != nil { + return err + } + } else { + trustedroot, err = root.NewTrustedRootFromPath(trustedRootPath) + if err != nil { + return err + } + } + + trustedmaterial := &verifyTrustedMaterial{TrustedMaterial: trustedroot} + + // See if we need to wrap trusted root with provided key + if keyRef != "" { + signatureVerifier, err := sigs.PublicKeyFromKeyRef(ctx, keyRef) + if err != nil { + return err + } + + newExpiringKey := root.NewExpiringKey(signatureVerifier, time.Time{}, time.Time{}) + trustedmaterial.keyTrustedMaterial = root.NewTrustedPublicKeyMaterial(func(_ string) (root.TimeConstrainedVerifier, error) { + return newExpiringKey, nil + }) + } else if sk { + s, err := pivkey.GetKeyWithSlot(slot) + if err != nil { + return fmt.Errorf("opening piv token: %w", err) + } + defer s.Close() + signatureVerifier, err := s.Verifier() + if err != nil { + return fmt.Errorf("loading public key from token: %w", err) + } + + newExpiringKey := root.NewExpiringKey(signatureVerifier, time.Time{}, time.Time{}) + trustedmaterial.keyTrustedMaterial = root.NewTrustedPublicKeyMaterial(func(_ string) (root.TimeConstrainedVerifier, error) { + return newExpiringKey, nil + }) + } + + identityPolicies := []verify.PolicyOption{} + + verificationMaterial := bundle.GetVerificationMaterial() + + if verificationMaterial == nil { + return fmt.Errorf("no verification material in bundle") + } + + if verificationMaterial.GetPublicKey() != nil { + identityPolicies = append(identityPolicies, verify.WithKey()) + } else { + sanMatcher, err := verify.NewSANMatcher(certIdentity, certIdentityRegexp) + if err != nil { + return err + } + + issuerMatcher, err := verify.NewIssuerMatcher(certOIDCIssuer, certOIDCIssuerRegex) + if err != nil { + return err + } + + extensions := certificate.Extensions{ + GithubWorkflowTrigger: githubWorkflowTrigger, + GithubWorkflowSHA: githubWorkflowSHA, + GithubWorkflowName: githubWorkflowName, + GithubWorkflowRepository: githubWorkflowRepository, + GithubWorkflowRef: githubWorkflowRef, + } + + certIdentity, err := verify.NewCertificateIdentity(sanMatcher, issuerMatcher, extensions) + if err != nil { + return err + } + + identityPolicies = append(identityPolicies, verify.WithCertificateIdentity(certIdentity)) + } + + // Make some educated guesses about verification policy + verifierConfig := []verify.VerifierOption{} + + if len(trustedroot.RekorLogs()) > 0 && !ignoreTlog { + verifierConfig = append(verifierConfig, verify.WithTransparencyLog(1), verify.WithIntegratedTimestamps(1)) + } + + if len(trustedroot.TimestampingAuthorities()) > 0 && useSignedTimestamps { + verifierConfig = append(verifierConfig, verify.WithSignedTimestamps(1)) + } + + if !ignoreSCT { + verifierConfig = append(verifierConfig, verify.WithSignedCertificateTimestamps(1)) + } + + if ignoreTlog && !useSignedTimestamps { + verifierConfig = append(verifierConfig, verify.WithoutAnyObserverTimestampsUnsafe()) + } + + // Perform verification + payload, err := payloadBytes(artifactRef) + if err != nil { + return err + } + buf := bytes.NewBuffer(payload) + + sev, err := verify.NewSignedEntityVerifier(trustedmaterial, verifierConfig...) + if err != nil { + return err + } + + _, err = sev.Verify(bundle, verify.NewPolicy(verify.WithArtifact(buf), identityPolicies...)) + return err +} diff --git a/cmd/cosign/cli/verify/verify_test.go b/cmd/cosign/cli/verify/verify_test.go index 6eca32e5537..620c841b6f9 100644 --- a/cmd/cosign/cli/verify/verify_test.go +++ b/cmd/cosign/cli/verify/verify_test.go @@ -18,6 +18,7 @@ import ( "bytes" "context" "crypto" + "crypto/ecdsa" "crypto/rand" "crypto/sha256" "crypto/x509" @@ -35,7 +36,9 @@ import ( "github.com/google/go-containerregistry/pkg/name" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" + "github.com/sigstore/cosign/v2/internal/pkg/cosign/fulcio/fulcioroots" "github.com/sigstore/cosign/v2/internal/ui" + "github.com/sigstore/cosign/v2/pkg/cosign" "github.com/sigstore/cosign/v2/pkg/oci" "github.com/sigstore/cosign/v2/pkg/oci/static" "github.com/sigstore/cosign/v2/test" @@ -43,6 +46,81 @@ import ( "github.com/stretchr/testify/assert" ) +type certData struct { + RootCert *x509.Certificate + RootKey *ecdsa.PrivateKey + SubCert *x509.Certificate + SubKey *ecdsa.PrivateKey + LeafCert *x509.Certificate + PrivKey *ecdsa.PrivateKey + RootCertPEM []byte + SubCertPEM []byte + LeafCertPEM []byte +} + +func getTestCerts(t *testing.T) *certData { + t.Helper() + eexts := []pkix.Extension{ + {Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 2}, Value: []byte("myWorkflowTrigger")}, + {Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 3}, Value: []byte("myWorkflowSha")}, + {Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 4}, Value: []byte("myWorkflowName")}, + {Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 5}, Value: []byte("myWorkflowRepository")}, + {Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 6}, Value: []byte("myWorkflowRef")}, + } + cd := &certData{} + var err error + if cd.RootCert, cd.RootKey, err = test.GenerateRootCa(); err != nil { + t.Fatal(err) + } + if cd.SubCert, cd.SubKey, err = test.GenerateSubordinateCa(cd.RootCert, cd.RootKey); err != nil { + t.Fatal(err) + } + if cd.LeafCert, cd.PrivKey, err = test.GenerateLeafCert("subject", "oidc-issuer", cd.SubCert, cd.SubKey, eexts...); err != nil { + t.Fatal(err) + } + cd.RootCertPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cd.RootCert.Raw}) + cd.SubCertPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cd.SubCert.Raw}) + cd.LeafCertPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cd.LeafCert.Raw}) + return cd +} + +func makeCertChainFile(t *testing.T, rootCert, subCert, leafCert []byte) string { + t.Helper() + f, err := os.CreateTemp("", "certchain") + if err != nil { + t.Fatal(err) + } + defer f.Close() + _, err = f.Write(append(append(rootCert, subCert...), leafCert...)) + if err != nil { + t.Fatal(err) + } + return f.Name() +} + +func makeRootsIntermediatesFiles(t *testing.T, roots, intermediates []byte) (string, string) { + t.Helper() + rootF, err := os.CreateTemp("", "roots") + if err != nil { + t.Fatal(err) + } + defer rootF.Close() + _, err = rootF.Write(roots) + if err != nil { + t.Fatal(err) + } + intermediateF, err := os.CreateTemp("", "intermediates") + if err != nil { + t.Fatal(err) + } + defer intermediateF.Close() + _, err = intermediateF.Write(intermediates) + if err != nil { + t.Fatal(err) + } + return rootF.Name(), intermediateF.Name() +} + func TestPrintVerification(t *testing.T) { // while we are adding a more human-readable output for cert extensions, on the other hand // we want as backward compatible as possible, so we are keeping the old OIDs field names as well. @@ -76,22 +154,9 @@ func TestPrintVerification(t *testing.T) { } ] ` - eexts := []pkix.Extension{ - {Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 2}, Value: []byte("myWorkflowTrigger")}, - {Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 3}, Value: []byte("myWorkflowSha")}, - {Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 4}, Value: []byte("myWorkflowName")}, - {Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 5}, Value: []byte("myWorkflowRepository")}, - {Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 6}, Value: []byte("myWorkflowRef")}, - } - rootCert, rootKey, _ := test.GenerateRootCa() - subCert, subKey, _ := test.GenerateSubordinateCa(rootCert, rootKey) - leafCert, privKey, _ := test.GenerateLeafCert("subject", "oidc-issuer", subCert, subKey, eexts...) - pemRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Raw}) - pemSub := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: subCert.Raw}) - pemLeaf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert.Raw}) - + certs := getTestCerts(t) rootPool := x509.NewCertPool() - rootPool.AddCert(rootCert) + rootPool.AddCert(certs.RootCert) // Generate the payload for the image, and check the digest. b := bytes.Buffer{} @@ -107,11 +172,11 @@ func TestPrintVerification(t *testing.T) { p := b.Bytes() h := sha256.Sum256(p) - signature, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256) + signature, _ := certs.PrivKey.Sign(rand.Reader, h[:], crypto.SHA256) ociSig, _ := static.NewSignature(p, base64.StdEncoding.EncodeToString(signature), - static.WithCertChain(pemLeaf, appendSlices([][]byte{pemSub, pemRoot}))) + static.WithCertChain(certs.LeafCertPEM, appendSlices([][]byte{certs.SubCertPEM, certs.RootCertPEM}))) captureOutput := func(f func()) string { reader, writer, err := os.Pipe() @@ -198,3 +263,86 @@ func TestVerifyCertMissingIssuer(t *testing.T) { t.Fatal("verify expected 'need --certificate-oidc-issuer'") } } + +func TestLoadCertsKeylessVerification(t *testing.T) { + certs := getTestCerts(t) + certChainFile := makeCertChainFile(t, certs.RootCertPEM, certs.SubCertPEM, certs.LeafCertPEM) + rootsFile, intermediatesFile := makeRootsIntermediatesFiles(t, certs.RootCertPEM, certs.SubCertPEM) + tests := []struct { + name string + certChain string + caRoots string + caIntermediates string + co *cosign.CheckOpts + sigstoreRootFile string + wantErr bool + }{ + { + name: "default fulcio", + wantErr: false, + }, + { + name: "non-existent SIGSTORE_ROOT_FILE", + sigstoreRootFile: "tesdata/nosuch-asdfjkl.pem", + wantErr: true, + }, + { + name: "good certchain", + certChain: certChainFile, + wantErr: false, + }, + { + name: "bad certchain", + certChain: "testdata/nosuch-certchain-file.pem", + wantErr: true, + }, + { + name: "roots", + caRoots: rootsFile, + wantErr: false, + }, + { + name: "bad roots", + caRoots: "testdata/nosuch-roots-file.pem", + wantErr: true, + }, + { + name: "roots and intermediate", + caRoots: rootsFile, + caIntermediates: intermediatesFile, + wantErr: false, + }, + { + name: "bad roots good intermediate", + caRoots: "testdata/nosuch-roots-file.pem", + caIntermediates: intermediatesFile, + wantErr: true, + }, + { + name: "good roots bad intermediate", + caRoots: rootsFile, + caIntermediates: "testdata/nosuch-intermediates-file.pem", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.sigstoreRootFile != "" { + os.Setenv("SIGSTORE_ROOT_FILE", tt.sigstoreRootFile) + } else { + t.Setenv("SIGSTORE_ROOT_FILE", "") + } + fulcioroots.ReInit() + if tt.co == nil { + tt.co = &cosign.CheckOpts{} + } + + err := loadCertsKeylessVerification(tt.certChain, tt.caRoots, tt.caIntermediates, tt.co) + if err == nil && tt.wantErr { + t.Fatalf("expected error but got none") + } else if err != nil && !tt.wantErr { + t.Fatalf("unexpected error: %v", err) + } + }) + } +} diff --git a/doc/cosign_attest-blob.md b/doc/cosign_attest-blob.md index f9c50468be0..4849bb93287 100644 --- a/doc/cosign_attest-blob.md +++ b/doc/cosign_attest-blob.md @@ -43,6 +43,7 @@ cosign attest-blob [flags] --identity-token string identity token to use for certificate from fulcio. the token or a path to a file containing the token is accepted. --insecure-skip-verify skip verifying fulcio published to the SCT (this should only be used for testing). --key string path to the private key file, KMS URI or Kubernetes Secret + --new-bundle-format output bundle in new format that contains all verification material --oidc-client-id string OIDC client ID for application (default "sigstore") --oidc-client-secret-file string Path to file containing OIDC client secret for application --oidc-disable-ambient-providers Disable ambient OIDC providers. When true, ambient credentials will not be read diff --git a/doc/cosign_attest.md b/doc/cosign_attest.md index ccd9bd8043f..19e201291d6 100644 --- a/doc/cosign_attest.md +++ b/doc/cosign_attest.md @@ -9,7 +9,7 @@ cosign attest [flags] ### Examples ``` - cosign attest --key | [--predicate ] [--a key=value] [--no-upload=true|false] [--f] [--r] + cosign attest --key | [--predicate ] [--a key=value] [--no-upload=true|false] [--record-creation-timestamp=true|false] [--f] [--r] # attach an attestation to a container image Google sign-in cosign attest --timeout 90s --predicate --type @@ -37,6 +37,9 @@ cosign attest [flags] # supply attestation via stdin echo | cosign attest --predicate - + + # attach an attestation to a container image and honor the creation timestamp of the signature + cosign attest --predicate --type --key cosign.key --record-creation-timestamp ``` ### Options @@ -62,6 +65,7 @@ cosign attest [flags] --oidc-provider string Specify the provider to get the OIDC token from (Optional). If unset, all options will be tried. Options include: [spiffe, google, github-actions, filesystem, buildkite-agent] --oidc-redirect-url string OIDC redirect URL (Optional). The default oidc-redirect-url is 'http://localhost:0/auth/callback'. --predicate string path to the predicate file. + --record-creation-timestamp set the createdAt timestamp in the attestation artifact to the time it was created; by default, cosign sets this to the zero value -r, --recursive if a multi-arch image is specified, additionally sign each discrete image --registry-password string registry basic auth password --registry-token string registry bearer auth token diff --git a/doc/cosign_dockerfile_verify.md b/doc/cosign_dockerfile_verify.md index 9ceb113ce20..aea4d4aadde 100644 --- a/doc/cosign_dockerfile_verify.md +++ b/doc/cosign_dockerfile_verify.md @@ -55,8 +55,10 @@ cosign dockerfile verify [flags] --attachment string DEPRECATED, related image attachment to verify (sbom), default none --attachment-tag-prefix [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] optional custom prefix to use for attached image tags. Attachment images are tagged as: [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] --base-image-only only verify the base image (the last FROM image in the Dockerfile) + --ca-intermediates string path to a file of intermediate CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. The flag is optional and must be used together with --ca-roots, conflicts with --certificate-chain. + --ca-roots string path to a bundle file of CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. Conflicts with --certificate-chain. --certificate string path to the public certificate. The certificate will be verified against the Fulcio roots if the --certificate-chain option is not passed. - --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate + --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate. Conflicts with --ca-roots and --ca-intermediates. --certificate-github-workflow-name string contains the workflow claim from the GitHub OIDC Identity token that contains the name of the executed workflow. --certificate-github-workflow-ref string contains the ref claim from the GitHub OIDC Identity token that contains the git ref that the workflow run was based upon. --certificate-github-workflow-repository string contains the repository claim from the GitHub OIDC Identity token that contains the repository that the workflow run was based upon @@ -89,6 +91,7 @@ cosign dockerfile verify [flags] --sk whether to use a hardware security key --slot string security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management) --timestamp-certificate-chain string path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. Optionally may contain intermediate CA certificates, and may contain the leaf TSA certificate if not present in the timestamp + --use-signed-timestamps use signed timestamps if available ``` ### Options inherited from parent commands diff --git a/doc/cosign_manifest_verify.md b/doc/cosign_manifest_verify.md index ae29314c6d5..dc1af148c48 100644 --- a/doc/cosign_manifest_verify.md +++ b/doc/cosign_manifest_verify.md @@ -49,8 +49,10 @@ cosign manifest verify [flags] -a, --annotations strings extra key=value pairs to sign --attachment string DEPRECATED, related image attachment to verify (sbom), default none --attachment-tag-prefix [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] optional custom prefix to use for attached image tags. Attachment images are tagged as: [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] + --ca-intermediates string path to a file of intermediate CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. The flag is optional and must be used together with --ca-roots, conflicts with --certificate-chain. + --ca-roots string path to a bundle file of CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. Conflicts with --certificate-chain. --certificate string path to the public certificate. The certificate will be verified against the Fulcio roots if the --certificate-chain option is not passed. - --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate + --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate. Conflicts with --ca-roots and --ca-intermediates. --certificate-github-workflow-name string contains the workflow claim from the GitHub OIDC Identity token that contains the name of the executed workflow. --certificate-github-workflow-ref string contains the ref claim from the GitHub OIDC Identity token that contains the git ref that the workflow run was based upon. --certificate-github-workflow-repository string contains the repository claim from the GitHub OIDC Identity token that contains the repository that the workflow run was based upon @@ -83,6 +85,7 @@ cosign manifest verify [flags] --sk whether to use a hardware security key --slot string security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management) --timestamp-certificate-chain string path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. Optionally may contain intermediate CA certificates, and may contain the leaf TSA certificate if not present in the timestamp + --use-signed-timestamps use signed timestamps if available ``` ### Options inherited from parent commands diff --git a/doc/cosign_save.md b/doc/cosign_save.md index d0acf98847a..53cc93eb419 100644 --- a/doc/cosign_save.md +++ b/doc/cosign_save.md @@ -19,8 +19,15 @@ cosign save [flags] ### Options ``` - --dir string path to dir where the signed image should be stored on disk - -h, --help help for save + --allow-http-registry whether to allow using HTTP protocol while connecting to registries. Don't use this for anything but testing + --allow-insecure-registry whether to allow insecure connections to registries (e.g., with expired or self-signed TLS certificates). Don't use this for anything but testing + --attachment-tag-prefix [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] optional custom prefix to use for attached image tags. Attachment images are tagged as: [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] + --dir string path to dir where the signed image should be stored on disk + -h, --help help for save + --k8s-keychain whether to use the kubernetes keychain instead of the default keychain (supports workload identity). + --registry-password string registry basic auth password + --registry-token string registry bearer auth token + --registry-username string registry basic auth username ``` ### Options inherited from parent commands diff --git a/doc/cosign_sign-blob.md b/doc/cosign_sign-blob.md index a53f34e4f9d..d78f9fb250e 100644 --- a/doc/cosign_sign-blob.md +++ b/doc/cosign_sign-blob.md @@ -45,6 +45,7 @@ cosign sign-blob [flags] --insecure-skip-verify skip verifying fulcio published to the SCT (this should only be used for testing). --issue-certificate issue a code signing certificate from Fulcio, even if a key is provided --key string path to the private key file, KMS URI or Kubernetes Secret + --new-bundle-format output bundle in new format that contains all verification material --oidc-client-id string OIDC client ID for application (default "sigstore") --oidc-client-secret-file string Path to file containing OIDC client secret for application --oidc-disable-ambient-providers Disable ambient OIDC providers. When true, ambient credentials will not be read diff --git a/doc/cosign_verify-attestation.md b/doc/cosign_verify-attestation.md index caa19214723..9b747ab9ef0 100644 --- a/doc/cosign_verify-attestation.md +++ b/doc/cosign_verify-attestation.md @@ -59,8 +59,10 @@ cosign verify-attestation [flags] --allow-http-registry whether to allow using HTTP protocol while connecting to registries. Don't use this for anything but testing --allow-insecure-registry whether to allow insecure connections to registries (e.g., with expired or self-signed TLS certificates). Don't use this for anything but testing --attachment-tag-prefix [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] optional custom prefix to use for attached image tags. Attachment images are tagged as: [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] + --ca-intermediates string path to a file of intermediate CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. The flag is optional and must be used together with --ca-roots, conflicts with --certificate-chain. + --ca-roots string path to a bundle file of CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. Conflicts with --certificate-chain. --certificate string path to the public certificate. The certificate will be verified against the Fulcio roots if the --certificate-chain option is not passed. - --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate + --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate. Conflicts with --ca-roots and --ca-intermediates. --certificate-github-workflow-name string contains the workflow claim from the GitHub OIDC Identity token that contains the name of the executed workflow. --certificate-github-workflow-ref string contains the ref claim from the GitHub OIDC Identity token that contains the git ref that the workflow run was based upon. --certificate-github-workflow-repository string contains the repository claim from the GitHub OIDC Identity token that contains the repository that the workflow run was based upon @@ -92,6 +94,7 @@ cosign verify-attestation [flags] --slot string security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management) --timestamp-certificate-chain string path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. Optionally may contain intermediate CA certificates, and may contain the leaf TSA certificate if not present in the timestamp --type string specify a predicate type (slsaprovenance|slsaprovenance02|slsaprovenance1|link|spdx|spdxjson|cyclonedx|vuln|openvex|custom) or an URI (default "custom") + --use-signed-timestamps use signed timestamps if available ``` ### Options inherited from parent commands diff --git a/doc/cosign_verify-blob-attestation.md b/doc/cosign_verify-blob-attestation.md index f08675bb163..a7c293359d6 100644 --- a/doc/cosign_verify-blob-attestation.md +++ b/doc/cosign_verify-blob-attestation.md @@ -29,8 +29,10 @@ cosign verify-blob-attestation [flags] ``` --bundle string path to bundle FILE + --ca-intermediates string path to a file of intermediate CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. The flag is optional and must be used together with --ca-roots, conflicts with --certificate-chain. + --ca-roots string path to a bundle file of CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. Conflicts with --certificate-chain. --certificate string path to the public certificate. The certificate will be verified against the Fulcio roots if the --certificate-chain option is not passed. - --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate + --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate. Conflicts with --ca-roots and --ca-intermediates. --certificate-github-workflow-name string contains the workflow claim from the GitHub OIDC Identity token that contains the name of the executed workflow. --certificate-github-workflow-ref string contains the ref claim from the GitHub OIDC Identity token that contains the git ref that the workflow run was based upon. --certificate-github-workflow-repository string contains the repository claim from the GitHub OIDC Identity token that contains the repository that the workflow run was based upon @@ -47,6 +49,7 @@ cosign verify-blob-attestation [flags] --insecure-ignore-tlog ignore transparency log verification, to be used when an artifact signature has not been uploaded to the transparency log. Artifacts cannot be publicly verified when not included in a log --key string path to the public key file, KMS URI or Kubernetes Secret --max-workers int the amount of maximum workers for parallel executions (default 10) + --new-bundle-format output bundle in new format that contains all verification material --offline only allow offline verification --private-infrastructure skip transparency log verification when verifying artifacts in a privately deployed infrastructure --rekor-url string address of rekor STL server (default "https://rekor.sigstore.dev") @@ -56,7 +59,9 @@ cosign verify-blob-attestation [flags] --sk whether to use a hardware security key --slot string security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management) --timestamp-certificate-chain string path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. Optionally may contain intermediate CA certificates, and may contain the leaf TSA certificate if not present in the timestamp + --trusted-root string path to trusted root FILE --type string specify a predicate type (slsaprovenance|slsaprovenance02|slsaprovenance1|link|spdx|spdxjson|cyclonedx|vuln|openvex|custom) or an URI (default "custom") + --use-signed-timestamps use signed timestamps if available ``` ### Options inherited from parent commands diff --git a/doc/cosign_verify-blob.md b/doc/cosign_verify-blob.md index dc0b13e4bcd..92655cccbed 100644 --- a/doc/cosign_verify-blob.md +++ b/doc/cosign_verify-blob.md @@ -23,6 +23,12 @@ cosign verify-blob [flags] # Verify a simple blob and message cosign verify-blob --key cosign.pub (--signature | msg) +# Verify a signature with certificate and CA certificate chain + cosign verify-blob --certificate cert.pem --certificate-chain certchain.pem --signature $sig + + # Verify a signature with CA roots and optional intermediate certificates + cosign verify-blob --certificate cert.pem --ca-roots caroots.pem [--ca-intermediates caintermediates.pem] --signature $sig + # Verify a signature from an environment variable cosign verify-blob --key cosign.pub --signature $sig msg @@ -59,8 +65,10 @@ cosign verify-blob [flags] ``` --bundle string path to bundle FILE + --ca-intermediates string path to a file of intermediate CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. The flag is optional and must be used together with --ca-roots, conflicts with --certificate-chain. + --ca-roots string path to a bundle file of CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. Conflicts with --certificate-chain. --certificate string path to the public certificate. The certificate will be verified against the Fulcio roots if the --certificate-chain option is not passed. - --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate + --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate. Conflicts with --ca-roots and --ca-intermediates. --certificate-github-workflow-name string contains the workflow claim from the GitHub OIDC Identity token that contains the name of the executed workflow. --certificate-github-workflow-ref string contains the ref claim from the GitHub OIDC Identity token that contains the git ref that the workflow run was based upon. --certificate-github-workflow-repository string contains the repository claim from the GitHub OIDC Identity token that contains the repository that the workflow run was based upon @@ -76,6 +84,7 @@ cosign verify-blob [flags] --insecure-ignore-tlog ignore transparency log verification, to be used when an artifact signature has not been uploaded to the transparency log. Artifacts cannot be publicly verified when not included in a log --key string path to the public key file, KMS URI or Kubernetes Secret --max-workers int the amount of maximum workers for parallel executions (default 10) + --new-bundle-format output bundle in new format that contains all verification material --offline only allow offline verification --private-infrastructure skip transparency log verification when verifying artifacts in a privately deployed infrastructure --rekor-url string address of rekor STL server (default "https://rekor.sigstore.dev") @@ -85,6 +94,8 @@ cosign verify-blob [flags] --sk whether to use a hardware security key --slot string security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management) --timestamp-certificate-chain string path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. Optionally may contain intermediate CA certificates, and may contain the leaf TSA certificate if not present in the timestamp + --trusted-root string path to trusted root FILE + --use-signed-timestamps use signed timestamps if available ``` ### Options inherited from parent commands diff --git a/doc/cosign_verify.md b/doc/cosign_verify.md index 749e565342c..0852f21fc81 100644 --- a/doc/cosign_verify.md +++ b/doc/cosign_verify.md @@ -38,6 +38,10 @@ cosign verify [flags] # verify image with local certificate and certificate chain cosign verify --cert cosign.crt --cert-chain chain.crt + # verify image with local certificate and certificate bundles of CA roots + # and (optionally) CA intermediates + cosign verify --cert cosign.crt --ca-roots ca-roots.pem --ca-intermediates ca-intermediates.pem + # verify image using keyless verification with the given certificate # chain and identity parameters, without Fulcio roots (for BYO PKI): cosign verify --cert-chain chain.crt --certificate-oidc-issuer https://issuer.example.com --certificate-identity foo@example.com @@ -72,8 +76,10 @@ cosign verify [flags] -a, --annotations strings extra key=value pairs to sign --attachment string DEPRECATED, related image attachment to verify (sbom), default none --attachment-tag-prefix [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] optional custom prefix to use for attached image tags. Attachment images are tagged as: [AttachmentTagPrefix]sha256-[TargetImageDigest].[AttachmentName] + --ca-intermediates string path to a file of intermediate CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. The flag is optional and must be used together with --ca-roots, conflicts with --certificate-chain. + --ca-roots string path to a bundle file of CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. Conflicts with --certificate-chain. --certificate string path to the public certificate. The certificate will be verified against the Fulcio roots if the --certificate-chain option is not passed. - --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate + --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate. Conflicts with --ca-roots and --ca-intermediates. --certificate-github-workflow-name string contains the workflow claim from the GitHub OIDC Identity token that contains the name of the executed workflow. --certificate-github-workflow-ref string contains the ref claim from the GitHub OIDC Identity token that contains the git ref that the workflow run was based upon. --certificate-github-workflow-repository string contains the repository claim from the GitHub OIDC Identity token that contains the repository that the workflow run was based upon @@ -106,6 +112,7 @@ cosign verify [flags] --sk whether to use a hardware security key --slot string security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management) --timestamp-certificate-chain string path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. Optionally may contain intermediate CA certificates, and may contain the leaf TSA certificate if not present in the timestamp + --use-signed-timestamps use signed timestamps if available ``` ### Options inherited from parent commands diff --git a/go.mod b/go.mod index 95d8aa81c21..71b51bf35ab 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ module github.com/sigstore/cosign/v2 -go 1.21 +go 1.22.5 require ( - cuelang.org/go v0.8.1 + cuelang.org/go v0.9.2 github.com/ThalesIgnite/crypto11 v1.2.5 github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 - github.com/buildkite/agent/v3 v3.62.0 + github.com/buildkite/agent/v3 v3.76.2 github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936 @@ -16,9 +16,9 @@ require ( github.com/go-openapi/strfmt v0.23.0 github.com/go-openapi/swag v0.23.0 github.com/go-piv/piv-go v1.11.0 - github.com/google/certificate-transparency-go v1.1.8 + github.com/google/certificate-transparency-go v1.2.1 github.com/google/go-cmp v0.6.0 - github.com/google/go-containerregistry v0.19.1 + github.com/google/go-containerregistry v0.20.1 github.com/google/go-github/v55 v55.0.0 github.com/in-toto/in-toto-golang v0.9.0 github.com/kelseyhightower/envconfig v1.4.0 @@ -28,50 +28,56 @@ require ( github.com/moby/term v0.5.0 github.com/mozillazg/docker-credential-acr-helper v0.3.0 github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 - github.com/open-policy-agent/opa v0.63.0 + github.com/open-policy-agent/opa v0.67.0 github.com/pkg/errors v0.9.1 github.com/secure-systems-lab/go-securesystemslib v0.8.0 - github.com/sigstore/fulcio v1.4.5 + github.com/sigstore/fulcio v1.5.1 + github.com/sigstore/protobuf-specs v0.3.2 github.com/sigstore/rekor v1.3.6 - github.com/sigstore/sigstore v1.8.3 - github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.3 - github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.3 - github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.3 - github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.3 + github.com/sigstore/sigstore v1.8.8 + github.com/sigstore/sigstore-go v0.5.1 + github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.8 + github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.8 + github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.8 + github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.8 github.com/sigstore/timestamp-authority v1.2.2 - github.com/spf13/cobra v1.8.0 + github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.18.2 - github.com/spiffe/go-spiffe/v2 v2.2.0 + github.com/spf13/viper v1.19.0 + github.com/spiffe/go-spiffe/v2 v2.3.0 github.com/stretchr/testify v1.9.0 github.com/transparency-dev/merkle v0.0.2 github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 - github.com/xanzy/go-gitlab v0.102.0 - go.step.sm/crypto v0.44.2 - golang.org/x/crypto v0.22.0 - golang.org/x/oauth2 v0.19.0 - golang.org/x/sync v0.7.0 - golang.org/x/term v0.19.0 - google.golang.org/api v0.172.0 + github.com/xanzy/go-gitlab v0.107.0 + go.step.sm/crypto v0.51.1 + golang.org/x/crypto v0.25.0 + golang.org/x/oauth2 v0.22.0 + golang.org/x/sync v0.8.0 + golang.org/x/term v0.22.0 + google.golang.org/api v0.190.0 + google.golang.org/protobuf v1.34.2 k8s.io/api v0.28.3 k8s.io/apimachinery v0.28.3 k8s.io/client-go v0.28.3 - k8s.io/utils v0.0.0-20230726121419-3b25d923346b - sigs.k8s.io/release-utils v0.7.7 + k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 + sigs.k8s.io/release-utils v0.8.4 ) require ( - cloud.google.com/go/compute v1.25.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.1.6 // indirect - cloud.google.com/go/kms v1.15.8 // indirect - cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e // indirect + cloud.google.com/go v0.115.0 // indirect + cloud.google.com/go/auth v0.7.3 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect + cloud.google.com/go/iam v1.1.12 // indirect + cloud.google.com/go/kms v1.18.4 // indirect + cloud.google.com/go/longrunning v0.5.11 // indirect + cuelabs.dev/go/oci/ociregistry v0.0.0-20240404174027-a39bec0462d2 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect @@ -84,7 +90,7 @@ require ( github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect - github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect github.com/agnivade/levenshtein v1.1.1 // indirect @@ -100,37 +106,38 @@ require ( github.com/alibabacloud-go/tea-xml v1.1.3 // indirect github.com/aliyun/credentials-go v1.3.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go v1.51.6 // indirect - github.com/aws/aws-sdk-go-v2 v1.26.0 // indirect - github.com/aws/aws-sdk-go-v2/config v1.27.9 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.9 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4 // indirect + github.com/aws/aws-sdk-go v1.55.5 // indirect + github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect + github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/service/ecr v1.20.2 // indirect github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.18.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6 // indirect - github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.20.3 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 // indirect - github.com/aws/smithy-go v1.20.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect + github.com/aws/aws-sdk-go-v2/service/kms v1.35.3 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect + github.com/aws/smithy-go v1.20.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect - github.com/buildkite/go-pipeline v0.3.2 // indirect - github.com/buildkite/interpolate v0.0.0-20200526001904-07f35b4ae251 // indirect + github.com/buildkite/go-pipeline v0.10.0 // indirect + github.com/buildkite/interpolate v0.1.3 // indirect + github.com/buildkite/roko v1.2.0 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/cockroachdb/apd/v3 v3.2.1 // indirect github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect - github.com/coreos/go-oidc/v3 v3.10.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/coreos/go-oidc/v3 v3.11.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect @@ -146,8 +153,8 @@ require ( github.com/go-chi/chi v4.1.2+incompatible // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v3 v3.0.3 // indirect - github.com/go-jose/go-jose/v4 v4.0.1 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/go-jose/go-jose/v4 v4.0.2 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.23.0 // indirect github.com/go-openapi/errors v0.22.0 // indirect @@ -167,22 +174,22 @@ require ( github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/s2a-go v0.1.7 // indirect + github.com/google/s2a-go v0.1.8 // indirect github.com/google/tink/go v1.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.3 // indirect + github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-retryablehttp v0.7.5 // indirect + github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.5 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hashicorp/vault/api v1.12.2 // indirect + github.com/hashicorp/vault/api v1.14.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect @@ -190,9 +197,9 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/compress v1.17.8 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 // indirect + github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -206,17 +213,17 @@ require ( github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pborman/uuid v1.2.1 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.19.0 // indirect - github.com/prometheus/client_model v0.6.0 // indirect - github.com/prometheus/common v0.51.1 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect - github.com/rs/cors v1.10.1 // indirect + github.com/rs/cors v1.11.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -234,6 +241,7 @@ require ( github.com/tchap/go-patricia/v2 v2.3.1 // indirect github.com/thales-e-security/pool v0.0.2 // indirect github.com/theupdateframework/go-tuf v0.7.0 // indirect + github.com/theupdateframework/go-tuf/v2 v2.0.0 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect github.com/urfave/negroni v1.0.0 // indirect @@ -244,27 +252,25 @@ require ( github.com/zeebo/errs v1.3.0 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.24.0 // indirect - go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/sdk v1.24.0 // indirect - go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect - golang.org/x/mod v0.16.0 // indirect - golang.org/x/net v0.22.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect + golang.org/x/mod v0.19.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.19.0 // indirect - google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect - google.golang.org/grpc v1.62.1 // indirect - google.golang.org/protobuf v1.33.0 // indirect - gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect + golang.org/x/tools v0.23.0 // indirect + google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect + google.golang.org/grpc v1.65.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index c91c1ccffc0..e5d2ef4e200 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,22 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= -cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= -cloud.google.com/go/compute v1.25.0 h1:H1/4SqSUhjPFE7L5ddzHOfY2bCAvjwNRZPNl6Ni5oYU= -cloud.google.com/go/compute v1.25.0/go.mod h1:GR7F0ZPZH8EhChlMo9FkLd7eUTwEymjqQagxzilIxIE= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= -cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= -cloud.google.com/go/kms v1.15.8 h1:szIeDCowID8th2i8XE4uRev5PMxQFqW+JjwYxL9h6xs= -cloud.google.com/go/kms v1.15.8/go.mod h1:WoUHcDjD9pluCg7pNds131awnH429QGvRM3N/4MyoVs= -cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e h1:GwCVItFUPxwdsEYnlUcJ6PJxOjTeFFCKOh6QWg4oAzQ= -cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e/go.mod h1:ApHceQLLwcOkCEXM1+DyCXTHEJhNGDpJ2kmV6axsx24= -cuelang.org/go v0.8.1 h1:VFYsxIFSPY5KgSaH1jQ2GxHOrbu6Ga3kEI70yCZwnOg= -cuelang.org/go v0.8.1/go.mod h1:CoDbYolfMms4BhWUlhD+t5ORnihR7wvjcfgyO9lL5FI= +cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= +cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU= +cloud.google.com/go/auth v0.7.3 h1:98Vr+5jMaCZ5NZk6e/uBgf60phTk/XN84r8QEWB9yjY= +cloud.google.com/go/auth v0.7.3/go.mod h1:HJtWUx1P5eqjy/f6Iq5KeytNpbAcGolPhOgyop2LlzA= +cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI= +cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +cloud.google.com/go/iam v1.1.12 h1:JixGLimRrNGcxvJEQ8+clfLxPlbeZA6MuRJ+qJNQ5Xw= +cloud.google.com/go/iam v1.1.12/go.mod h1:9LDX8J7dN5YRyzVHxwQzrQs9opFFqn0Mxs9nAeB+Hhg= +cloud.google.com/go/kms v1.18.4 h1:dYN3OCsQ6wJLLtOnI8DGUwQ5shMusXsWCCC+s09ATsk= +cloud.google.com/go/kms v1.18.4/go.mod h1:SG1bgQ3UWW6/KdPo9uuJnzELXY5YTTMJtDYvajiQ22g= +cloud.google.com/go/longrunning v0.5.11 h1:Havn1kGjz3whCfoD8dxMLP73Ph5w+ODyZB9RUsDxtGk= +cloud.google.com/go/longrunning v0.5.11/go.mod h1:rDn7//lmlfWV1Dx6IB4RatCPenTwwmqXuiP0/RgoEO4= +cuelabs.dev/go/oci/ociregistry v0.0.0-20240404174027-a39bec0462d2 h1:BnG6pr9TTr6CYlrJznYUDj6V7xldD1W+1iXPum0wT/w= +cuelabs.dev/go/oci/ociregistry v0.0.0-20240404174027-a39bec0462d2/go.mod h1:pK23AUVXuNzzTpfMCA06sxZGeVQ/75FdVtW249de9Uo= +cuelang.org/go v0.9.2 h1:pfNiry2PdRBr02G/aKm5k2vhzmqbAOoaB4WurmEbWvs= +cuelang.org/go v0.9.2/go.mod h1:qpAYsLOf7gTM1YdEg6cxh553uZ4q9ZDWlPbtZr9q1Wk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg= @@ -21,12 +25,12 @@ github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0/go.mod h1:GgeIE+1be8Ivm7Sh4RgwI42aTtC9qrcj+Y9Y6CjJhJs= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 h1:n1DH8TPV4qqPTje2RcUBYwtrTWlabVp4n46+74X2pn4= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0/go.mod h1:HDcZnuGbiyppErN6lB+idp4CKhjbc8gwjto6OPpyggM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0 h1:GJHeeA2N7xrG3q30L2UXDyuWRzDM900/65j70wcM4Ww= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0 h1:DRiANoJTiW6obBQe3SqZizkuV1PEgfiiGivmVocDy64= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0/go.mod h1:qLIye2hwb/ZouqhpSD9Zn3SJipvpEnz1Ywl3VUk9Y0s= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80= @@ -59,8 +63,8 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= @@ -118,44 +122,44 @@ github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdK github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.51.6 h1:Ld36dn9r7P9IjU8WZSaswQ8Y/XUCRpewim5980DwYiU= -github.com/aws/aws-sdk-go v1.51.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= -github.com/aws/aws-sdk-go-v2 v1.26.0 h1:/Ce4OCiM3EkpW7Y+xUnfAFpchU78K7/Ug01sZni9PgA= -github.com/aws/aws-sdk-go-v2 v1.26.0/go.mod h1:35hUlJVYd+M++iLI3ALmVwMOyRYMmRqUXpTtRGW+K9I= -github.com/aws/aws-sdk-go-v2/config v1.27.9 h1:gRx/NwpNEFSk+yQlgmk1bmxxvQ5TyJ76CWXs9XScTqg= -github.com/aws/aws-sdk-go-v2/config v1.27.9/go.mod h1:dK1FQfpwpql83kbD873E9vz4FyAxuJtR22wzoXn3qq0= -github.com/aws/aws-sdk-go-v2/credentials v1.17.9 h1:N8s0/7yW+h8qR8WaRlPQeJ6czVMNQVNtNdUqf6cItao= -github.com/aws/aws-sdk-go-v2/credentials v1.17.9/go.mod h1:446YhIdmSV0Jf/SLafGZalQo+xr2iw7/fzXGDPTU1yQ= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 h1:af5YzcLf80tv4Em4jWVD75lpnOHSBkPUZxZfGkrI3HI= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0/go.mod h1:nQ3how7DMnFMWiU1SpECohgC82fpn4cKZ875NDMmwtA= +github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= +github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= +github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= +github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43/go.mod h1:auo+PiyLl0n1l8A0e8RIeR8tOzYPfZZH/JNlrJ8igTQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4 h1:0ScVK/4qZ8CIW0k8jOeFVsyS/sAiXpYxRBLolMkuLQM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.4/go.mod h1:84KyjNZdHC6QZW08nfHI6yZgPd+qRgaWcYsyLUo3QY8= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37/go.mod h1:Qe+2KtKml+FEsQF/DHmDV+xjtche/hwoF75EG4UlHW8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4 h1:sHmMWWX5E7guWEFQ9SVo6A3S4xpPrWnd77a6y4WM6PU= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.4/go.mod h1:WjpDrhWisWOIoS9n3nk67A3Ll1vfULJ9Kq6h29HTD48= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/service/ecr v1.20.2 h1:y6LX9GUoEA3mO0qpFl1ZQHj1rFyPWVphlzebiSt2tKE= github.com/aws/aws-sdk-go-v2/service/ecr v1.20.2/go.mod h1:Q0LcmaN/Qr8+4aSBrdrXXePqoX0eOuYpJLbYpilmWnA= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.18.2 h1:PpbXaecV3sLAS6rjQiaKw4/jyq3Z8gNzmoJupHAoBp0= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.18.2/go.mod h1:fUHpGXr4DrXkEDpGAjClPsviWf+Bszeb0daKE0blxv8= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 h1:EyBZibRTVAs6ECHZOw5/wlylS9OcTzwyjeQMudmREjE= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1/go.mod h1:JKpmtYhhPs7D97NL/ltqz7yCkERFW5dOlHyVl66ZYF8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6 h1:b+E7zIUHMmcB4Dckjpkapoy47W6C9QBv/zoUP+Hn8Kc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6/go.mod h1:S2fNV0rxrP78NhPbCZeQgY8H9jdDMeGtwcfZIRxzBqU= -github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 h1:yS0JkEdV6h9JOo8sy2JSpjX+i7vsKifU8SIeHrqiDhU= -github.com/aws/aws-sdk-go-v2/service/kms v1.30.0/go.mod h1:+I8VUUSVD4p5ISQtzpgSva4I8cJ4SQ4b1dcBcof7O+g= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.3 h1:mnbuWHOcM70/OFUlZZ5rcdfA8PflGXXiefU/O+1S3+8= -github.com/aws/aws-sdk-go-v2/service/sso v1.20.3/go.mod h1:5HFu51Elk+4oRBZVxmHrSds5jFXmFj8C3w7DVF2gnrs= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 h1:uLq0BKatTmDzWa/Nu4WO0M1AaQDaPpwTKAeByEc6WFM= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3/go.mod h1:b+qdhjnxj8GSR6t5YfphOffeoQSQ1KmpoVVuBn+PWxs= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 h1:J/PpTf/hllOjx8Xu9DMflff3FajfLxqM5+tepvVXmxg= -github.com/aws/aws-sdk-go-v2/service/sts v1.28.5/go.mod h1:0ih0Z83YDH/QeQ6Ori2yGE2XvWYv/Xm+cZc01LC6oK0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= +github.com/aws/aws-sdk-go-v2/service/kms v1.35.3 h1:UPTdlTOwWUX49fVi7cymEN6hDqCwe3LNv1vi7TXUutk= +github.com/aws/aws-sdk-go-v2/service/kms v1.35.3/go.mod h1:gjDP16zn+WWalyaUqwCCioQ8gU8lzttCCc9jYsiQI/8= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw= -github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= +github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 h1:SoFYaT9UyGkR0+nogNyD/Lj+bsixB+SNuAS4ABlEs6M= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8/go.mod h1:2JF49jcDOrLStIXN/j/K1EKRq8a8R2qRnlZA6/o/c7c= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -163,24 +167,26 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/buildkite/agent/v3 v3.62.0 h1:yvzSjI8Lgifw883I8m9u8/L/Thxt4cLFd5aWPn3gg70= -github.com/buildkite/agent/v3 v3.62.0/go.mod h1:jN6SokGXrVNNIpI0BGQ+j5aWeI3gin8F+3zwA5Q6gqM= -github.com/buildkite/go-pipeline v0.3.2 h1:SW4EaXNwfjow7xDRPGgX0Rcx+dPj5C1kV9LKCLjWGtM= -github.com/buildkite/go-pipeline v0.3.2/go.mod h1:iY5jzs3Afc8yHg6KDUcu3EJVkfaUkd9x/v/OH98qyUA= -github.com/buildkite/interpolate v0.0.0-20200526001904-07f35b4ae251 h1:k6UDF1uPYOs0iy1HPeotNa155qXRWrzKnqAaGXHLZCE= -github.com/buildkite/interpolate v0.0.0-20200526001904-07f35b4ae251/go.mod h1:gbPR1gPu9dB96mucYIR7T3B7p/78hRVSOuzIWLHK2Y4= +github.com/buildkite/agent/v3 v3.76.2 h1:SweFq3e0N20RikWsVeOXzTjfr0AoOskxm9c0bcNyI0E= +github.com/buildkite/agent/v3 v3.76.2/go.mod h1:9ffbmJD7d7C/nOcElj6Qm+uIj1QoYh3NNvka4rkKkss= +github.com/buildkite/go-pipeline v0.10.0 h1:EDffu+LfMY2k5u+iEdo6Jn3obGKsrL5wicc1O/yFeRs= +github.com/buildkite/go-pipeline v0.10.0/go.mod h1:eMH1kiav5VeiTiu0Mk2/M7nZhKyFeL4iGj7Y7rj4f3w= +github.com/buildkite/interpolate v0.1.3 h1:OFEhqji1rNTRg0u9DsSodg63sjJQEb1uWbENq9fUOBM= +github.com/buildkite/interpolate v0.1.3/go.mod h1:UNVe6A+UfiBNKbhAySrBbZFZFxQ+DXr9nWen6WVt/A8= +github.com/buildkite/roko v1.2.0 h1:hbNURz//dQqNl6Eo9awjQOVOZwSDJ8VEbBDxSfT9rGQ= +github.com/buildkite/roko v1.2.0/go.mod h1:23R9e6nHxgedznkwwfmqZ6+0VJZJZ2Sg/uVcp2cP46I= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 h1:krfRl01rzPzxSxyLyrChD+U+MzsBXbm0OwYYB67uF+4= github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589/go.mod h1:OuDyvmLnMCwa2ep4Jkm6nyA0ocJuZlGyk2gGseVzERM= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -208,12 +214,12 @@ github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= -github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU= -github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac= -github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= -github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= +github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.19 h1:tUN6H7LWqNx4hQVxomd0CVsDwaDr9gaRQaI4GpSmrsA= +github.com/creack/pty v1.1.19/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 h1:2Dx4IHfC1yHWI12AxQDJM1QbRCDfk6M+blLzlZCXdrc= github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= @@ -237,8 +243,6 @@ github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1G github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg= github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= @@ -281,11 +285,11 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= -github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= -github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= +github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= +github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= @@ -312,13 +316,15 @@ github.com/go-piv/piv-go v1.11.0 h1:5vAaCdRTFSIW4PeqMbnsDlUZ7odMYWnHBDGdmtU/Zhg= github.com/go-piv/piv-go v1.11.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM6fJZZM= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= -github.com/go-rod/rod v0.114.7 h1:h4pimzSOUnw7Eo41zdJA788XsawzHjJMyzCE3BrBww0= -github.com/go-rod/rod v0.114.7/go.mod h1:aiedSEFg5DwG/fnNbUOTPMTTWX3MRj6vIs/a684Mthw= +github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA= +github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= -github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -332,8 +338,8 @@ github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= -github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= +github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -355,8 +361,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/certificate-transparency-go v1.1.8 h1:LGYKkgZF7satzgTak9R4yzfJXEeYVAjV6/EAEJOf1to= -github.com/google/certificate-transparency-go v1.1.8/go.mod h1:bV/o8r0TBKRf1X//iiiSgWrvII4d7/8OiA+3vG26gI8= +github.com/google/certificate-transparency-go v1.2.1 h1:4iW/NwzqOqYEEoCBEFP+jPbBXbLqMpq3CifMyOnDUME= +github.com/google/certificate-transparency-go v1.2.1/go.mod h1:bvn/ytAccv+I6+DGkqpvSsEdiVGramgaSC6RD3tEmeE= github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= @@ -373,8 +379,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.19.1 h1:yMQ62Al6/V0Z7CqIrrS1iYoA5/oQCm88DeNujc7C1KY= -github.com/google/go-containerregistry v0.19.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI= +github.com/google/go-containerregistry v0.20.1 h1:eTgx9QNYugV4DN5mz4U8hiAGTi1ybXn0TPi4Smd8du0= +github.com/google/go-containerregistry v0.20.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI= github.com/google/go-github/v55 v55.0.0 h1:4pp/1tNMB9X/LuAhs5i0KQAE40NmiR/y6prLNb9x9cg= github.com/google/go-github/v55 v55.0.0/go.mod h1:JLahOTA1DnXzhxEymmFF5PP2tSS9JVNj68mSZNDwskA= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -385,8 +391,8 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM= github.com/google/trillian v1.6.0 h1:jMBeDBIkINFvS2n6oV5maDqfRlxREAc6CW9QYWQ0qT4= @@ -397,28 +403,27 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA= -github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= +github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= +github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= -github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= @@ -429,10 +434,13 @@ github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25L github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU= github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hashicorp/vault/api v1.12.2 h1:7YkCTE5Ni90TcmYHDBExdt4WGJxhpzaHqR6uGbQb/rE= -github.com/hashicorp/vault/api v1.12.2/go.mod h1:LSGf1NGT1BnvFFnKVtnvcaLBM2Lz+gJdpL6HUYed8KE= +github.com/hashicorp/vault/api v1.14.0 h1:Ah3CFLixD5jmjusOgm8grfN9M0d+Y8fVR2SW0K6pJLU= +github.com/hashicorp/vault/api v1.14.0/go.mod h1:pV9YLxBGSz+cItFDd8Ii4G17waWOQ32zVjMWHe/cOqk= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -463,8 +471,8 @@ github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dv github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -473,8 +481,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 h1:WGrKdjHtWC67RX96eTkYD2f53NDHhrq/7robWTAfk4s= -github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491/go.mod h1:o158RFmdEbYyIZmXAbrvmJWesbyxlLKee6X64VPVuOc= +github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec h1:2tTW6cDth2TSgRbAhD7yjZzTQmcN25sDRPEeinR51yQ= +github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec/go.mod h1:TmwEoGCwIti7BCeJ9hescZgRtatxRE+A72pCoPfmcfk= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -489,8 +497,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= -github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= @@ -520,8 +528,9 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5/zdSLo48H9Iaq0UQGthrhWC6pCxzE= github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481/go.mod h1:yKZQO8QE2bHlgozqWDiRVqTFlLQSj30K/6SAK8EeYFw= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM= @@ -540,8 +549,8 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= -github.com/open-policy-agent/opa v0.63.0 h1:ztNNste1v8kH0/vJMJNquE45lRvqwrM5mY9Ctr9xIXw= -github.com/open-policy-agent/opa v0.63.0/go.mod h1:9VQPqEfoB2N//AToTxzZ1pVTVPUoF2Mhd64szzjWPpU= +github.com/open-policy-agent/opa v0.67.0 h1:FOdsO9yNhfmrh+72oVK7ImWmzruG+VSpfbr5IBqEWVs= +github.com/open-policy-agent/opa v0.67.0/go.mod h1:aqKlHc8E2VAAylYE9x09zJYr/fYzGX+JKne89UGqFzk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -550,8 +559,8 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -561,23 +570,23 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= -github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= -github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= -github.com/prometheus/common v0.51.1 h1:eIjN50Bwglz6a/c3hAgSMcofL3nD+nFQkV6Dd4DsQCw= -github.com/prometheus/common v0.51.1/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf h1:014O62zIzQwvoD7Ekj3ePDF5bv9Xxy0w6AZk0qYbjUk= github.com/protocolbuffers/txtpbfmt v0.0.0-20231025115547-084445ff1adf/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= -github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= +github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -595,22 +604,28 @@ github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbm github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= -github.com/sigstore/fulcio v1.4.5 h1:WWNnrOknD0DbruuZWCbN+86WRROpEl3Xts+WT2Ek1yc= -github.com/sigstore/fulcio v1.4.5/go.mod h1:oz3Qwlma8dWcSS/IENR/6SjbW4ipN0cxpRVfgdsjMU8= +github.com/sigstore/fulcio v1.5.1 h1:Iasy1zfNjaq8BV4S8o6pXspLDU28PQC2z07GmOu9zpM= +github.com/sigstore/fulcio v1.5.1/go.mod h1:W1A/UHrTopy1IBZPMtHmxg7GPYAu+vt5dRXM3W6yjPo= +github.com/sigstore/protobuf-specs v0.3.2 h1:nCVARCN+fHjlNCk3ThNXwrZRqIommIeNKWwQvORuRQo= +github.com/sigstore/protobuf-specs v0.3.2/go.mod h1:RZ0uOdJR4OB3tLQeAyWoJFbNCBFrPQdcokntde4zRBA= github.com/sigstore/rekor v1.3.6 h1:QvpMMJVWAp69a3CHzdrLelqEqpTM3ByQRt5B5Kspbi8= github.com/sigstore/rekor v1.3.6/go.mod h1:JDTSNNMdQ/PxdsS49DJkJ+pRJCO/83nbR5p3aZQteXc= -github.com/sigstore/sigstore v1.8.3 h1:G7LVXqL+ekgYtYdksBks9B38dPoIsbscjQJX/MGWkA4= -github.com/sigstore/sigstore v1.8.3/go.mod h1:mqbTEariiGA94cn6G3xnDiV6BD8eSLdL/eA7bvJ0fVs= -github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.3 h1:LTfPadUAo+PDRUbbdqbeSl2OuoFQwUFTnJ4stu+nwWw= -github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.3/go.mod h1:QV/Lxlxm0POyhfyBtIbTWxNeF18clMlkkyL9mu45y18= -github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.3 h1:xgbPRCr2npmmsuVVteJqi/ERw9+I13Wou7kq0Yk4D8g= -github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.3/go.mod h1:G4+I83FILPX6MtnoaUdmv/bRGEVtR3JdLeJa/kXdk/0= -github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.3 h1:vDl2fqPT0h3D/k6NZPlqnKFd1tz3335wm39qjvpZNJc= -github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.3/go.mod h1:9uOJXbXEXj+M6QjMKH5PaL5WDMu43rHfbIMgXzA8eKI= -github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.3 h1:h9G8j+Ds21zqqulDbA/R/ft64oQQIyp8S7wJYABYSlg= -github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.3/go.mod h1:zgCeHOuqF6k7A7TTEvftcA9V3FRzB7mrPtHOhXAQBnc= +github.com/sigstore/sigstore v1.8.8 h1:B6ZQPBKK7Z7tO3bjLNnlCMG+H66tO4E/+qAphX8T/hg= +github.com/sigstore/sigstore v1.8.8/go.mod h1:GW0GgJSCTBJY3fUOuGDHeFWcD++c4G8Y9K015pwcpDI= +github.com/sigstore/sigstore-go v0.5.1 h1:5IhKvtjlQBeLnjKkzMELNG4tIBf+xXQkDzhLV77+/8Y= +github.com/sigstore/sigstore-go v0.5.1/go.mod h1:TuOfV7THHqiDaUHuJ5+QN23RP/YoKmsbwJpY+aaYPN0= +github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.8 h1:2zHmUvaYCwV6LVeTo+OAkTm8ykOGzA9uFlAjwDPAUWM= +github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.8/go.mod h1:OEhheBplZinUsm7W9BupafztVZV3ldkAxEHbpAeC0Pk= +github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.8 h1:RKk4Z+qMaLORUdT7zntwMqKiYAej1VQlCswg0S7xNSY= +github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.8/go.mod h1:dMJdlBWKHMu2xf0wIKpbo7+QfG+RzVkBB3nHP8EMM5o= +github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.8 h1:89Xtxj8oqZt3UlSpCP4wApFvnQ2Z/dgowW5QOVhQigI= +github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.8/go.mod h1:Wa4xn/H3pU/yW/6tHiMXTpObBtBSGC5q29KYFEPKN6o= +github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.8 h1:Zte3Oogkd8m+nu2oK3yHtGmN++TZWh2Lm6q2iSprT1M= +github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.8/go.mod h1:j00crVw6ki4/WViXflw0zWgNALrAzZT+GbIK8v7Xlz4= github.com/sigstore/timestamp-authority v1.2.2 h1:X4qyutnCQqJ0apMewFyx+3t7Tws00JQ/JonBiu3QvLE= github.com/sigstore/timestamp-authority v1.2.2/go.mod h1:nEah4Eq4wpliDjlY342rXclGSO7Kb9hoRrl9tqLW13A= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -628,19 +643,19 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= -github.com/spiffe/go-spiffe/v2 v2.2.0 h1:9Vf06UsvsDbLYK/zJ4sYsIsHmMFknUD+feA7IYoWMQY= -github.com/spiffe/go-spiffe/v2 v2.2.0/go.mod h1:Urzb779b3+IwDJD2ZbN8fVl3Aa8G4N/PiUe6iXC0XxU= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spiffe/go-spiffe/v2 v2.3.0 h1:g2jYNb/PDMB8I7mBGL2Zuq/Ur6hUhoroxGQFyD6tTj8= +github.com/spiffe/go-spiffe/v2 v2.3.0/go.mod h1:Oxsaio7DBgSNqhAO9i/9tLClaVlfRok7zvJnTV8ZyIY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -663,6 +678,8 @@ github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gt github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI= github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug= +github.com/theupdateframework/go-tuf/v2 v2.0.0 h1:rD8d9RotYBprZVgC+9oyTZ5MmawepnTSTqoDuxjWgbs= +github.com/theupdateframework/go-tuf/v2 v2.0.0/go.mod h1:baB22nBHeHBCeuGZcIlctNq4P61PcOdyARlplg5xmLA= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= @@ -676,8 +693,8 @@ github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinC github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1 h1:+dBg5k7nuTE38VVdoroRsT0Z88fmvdYrI2EjzJst35I= github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1/go.mod h1:nmuySobZb4kFgFy6BptpXp/BBw+xFSyvVPP6auoJB4k= -github.com/xanzy/go-gitlab v0.102.0 h1:ExHuJ1OTQ2yt25zBMMj0G96ChBirGYv8U7HyUiYkZ+4= -github.com/xanzy/go-gitlab v0.102.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI= +github.com/xanzy/go-gitlab v0.107.0 h1:P2CT9Uy9yN9lJo3FLxpMZ4xj6uWcpnigXsjvqJ6nd2Y= +github.com/xanzy/go-gitlab v0.107.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= @@ -688,12 +705,12 @@ github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= -github.com/ysmood/got v0.34.1 h1:IrV2uWLs45VXNvZqhJ6g2nIhY+pgIG1CUoOcqfXFl1s= -github.com/ysmood/got v0.34.1/go.mod h1:yddyjq/PmAf08RMLSwDjPyCvHvYed+WjHnQxpH851LM= +github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q= +github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg= github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= -github.com/ysmood/leakless v0.8.0 h1:BzLrVoiwxikpgEQR0Lk8NyBN5Cit2b1z+u0mgL4ZJak= -github.com/ysmood/leakless v0.8.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= +github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU= +github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -706,26 +723,26 @@ go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ= -go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= -go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= -go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= -go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= -go.step.sm/crypto v0.44.2 h1:t3p3uQ7raP2jp2ha9P6xkQF85TJZh+87xmjSLaib+jk= -go.step.sm/crypto v0.44.2/go.mod h1:x1439EnFhadzhkuaGX7sz03LEMQ+jV4gRamf5LCZJQQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0 h1:vS1Ao/R55RNV4O7TA2Qopok8yN+X0LIP6RVWLFkprck= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.52.0/go.mod h1:BMsdeOxN04K0L5FNUBfjFdvwWGNe/rkmSwH4Aelu/X0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.step.sm/crypto v0.51.1 h1:ktUg/2hetEMiBAqgz502ktZDGoDoGrcHFg3XpkmkvvA= +go.step.sm/crypto v0.51.1/go.mod h1:PdrhttNU/tG9/YsVd4fdlysBN+UV503p0o2irFZQlAw= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -746,11 +763,11 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58 golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w= -golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -758,8 +775,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -785,11 +802,11 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= -golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -798,8 +815,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -832,8 +849,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -842,8 +859,8 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -854,8 +871,9 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -871,34 +889,34 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.172.0 h1:/1OcMZGPmW1rX2LCu2CmGUD1KXK1+pfzxotxyRUCCdk= -google.golang.org/api v0.172.0/go.mod h1:+fJZq6QXWfa9pXhnIzsjx4yI22d4aI9ZpLb58gvXjis= +google.golang.org/api v0.190.0 h1:ASM+IhLY1zljNdLu19W1jTmU6A+gMk6M46Wlur61s+Q= +google.golang.org/api v0.190.0/go.mod h1:QIr6I9iedBLnfqoD6L6Vze1UvS5Hzj5r2aUBOaZnLHo= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 h1:ImUcDPHjTrAqNhlOkSocDLfG9rrNHH7w7uoKWPaWZ8s= -google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7/go.mod h1:/3XmxOjePkvmKrHuBy4zNFw7IzxJXtAgdpXi8Ll990U= -google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 h1:oqta3O3AnlWbmIE3bFnWbu4bRxZjfbWCp0cKSuZh01E= -google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf h1:OqdXDEakZCVtDiZTjcxfwbHPCT11ycCEsTKesBVKvyY= +google.golang.org/genproto v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:mCr1K1c8kX+1iSBREvU3Juo11CB+QOEWxbRS01wWl5M= +google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f h1:b1Ln/PG8orm0SsBbHZWke8dDp2lrCD4jSmfglFpTZbk= +google.golang.org/genproto/googleapis/api v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:AHT0dDg3SoMOgZGnZk29b5xTbPHMoEC8qthmBLJCpys= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -910,15 +928,13 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs= -gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -949,12 +965,12 @@ k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= -k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak= +k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/release-utils v0.7.7 h1:JKDOvhCk6zW8ipEOkpTGDH/mW3TI+XqtPp16aaQ79FU= -sigs.k8s.io/release-utils v0.7.7/go.mod h1:iU7DGVNi3umZJ8q6aHyUFzsDUIaYwNnNKGHo3YE5E3s= +sigs.k8s.io/release-utils v0.8.4 h1:4QVr3UgbyY/d9p74LBhg0njSVQofUsAZqYOzVZBhdBw= +sigs.k8s.io/release-utils v0.8.4/go.mod h1:m1bHfscTemQp+z+pLCZnkXih9n0+WukIUU70n6nFnU0= sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/pkg/cosign/bundle/protobundle.go b/pkg/cosign/bundle/protobundle.go new file mode 100644 index 00000000000..a26e2fb2bb7 --- /dev/null +++ b/pkg/cosign/bundle/protobundle.go @@ -0,0 +1,65 @@ +// Copyright 2024 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bundle + +import ( + protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" + protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" + protorekor "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" + "github.com/sigstore/rekor/pkg/generated/models" + "github.com/sigstore/rekor/pkg/tle" +) + +const bundleV03MediaType = "application/vnd.dev.sigstore.bundle.v0.3+json" + +func MakeProtobufBundle(hint string, rawCert []byte, rekorEntry *models.LogEntryAnon, timestampBytes []byte) (*protobundle.Bundle, error) { + bundle := &protobundle.Bundle{MediaType: bundleV03MediaType} + + if hint != "" { + bundle.VerificationMaterial = &protobundle.VerificationMaterial{ + Content: &protobundle.VerificationMaterial_PublicKey{ + PublicKey: &protocommon.PublicKeyIdentifier{ + Hint: hint, + }, + }, + } + } else if len(rawCert) > 0 { + bundle.VerificationMaterial = &protobundle.VerificationMaterial{ + Content: &protobundle.VerificationMaterial_Certificate{ + Certificate: &protocommon.X509Certificate{ + RawBytes: rawCert, + }, + }, + } + } + + if len(timestampBytes) > 0 { + bundle.VerificationMaterial.TimestampVerificationData = &protobundle.TimestampVerificationData{ + Rfc3161Timestamps: []*protocommon.RFC3161SignedTimestamp{ + {SignedTimestamp: timestampBytes}, + }, + } + } + + if rekorEntry != nil { + tlogEntry, err := tle.GenerateTransparencyLogEntry(*rekorEntry) + if err != nil { + return nil, err + } + bundle.VerificationMaterial.TlogEntries = []*protorekor.TransparencyLogEntry{tlogEntry} + } + + return bundle, nil +} diff --git a/pkg/cosign/bundle/protobundle_test.go b/pkg/cosign/bundle/protobundle_test.go new file mode 100644 index 00000000000..dea1bce59bb --- /dev/null +++ b/pkg/cosign/bundle/protobundle_test.go @@ -0,0 +1,92 @@ +// Copyright 2024 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bundle + +import ( + "testing" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/sigstore/rekor/pkg/generated/models" + _ "github.com/sigstore/rekor/pkg/types/hashedrekord" + _ "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1" +) + +func TestMakeProtobufBundle(t *testing.T) { + testCases := []struct { + name string + hint string + rawCert []byte + rekorEntry *models.LogEntryAnon + timestampBytes []byte + }{ + { + name: "hint with timestamp", + hint: "asdf", + rawCert: []byte{}, + rekorEntry: nil, + timestampBytes: []byte("timestamp"), + }, + { + name: "only cert", + hint: "", + rawCert: []byte("cert stuff"), + rekorEntry: nil, + timestampBytes: []byte{}, + }, + { + name: "cert with rekor entry", + hint: "", + rawCert: []byte("cert stuff"), + rekorEntry: &models.LogEntryAnon{ + Body: "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2MmQwOGYyOGM2OWNhZGE3YjQyYTQ1Nzk0YjQ3ZWU2YzgxYTdkZmE3MTY4NDZiMzljODhmMGFkMTljMjA2OTk3In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJQm14U0N1TW1HSzhNQWRMd1FWZ21TZjVXKzlkdU5iQXN1cUNQNlNucUxCUkFpRUFvNGtGRVdDTG9HcTVUaysrUEhtTEgrb3N1emVTRjN4OTdBbmVicTRlbVRvPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUk1ha05EUVhKVFowRjNTVUpCWjBsVVRWQkRlVXdyYmxOb2MycHdaa2hZYUZkYVRVWkNUVUZIUlVSQlMwSm5aM0ZvYTJwUFVGRlJSRUY2UVhFS1RWSlZkMFYzV1VSV1VWRkxSWGQ0ZW1GWFpIcGtSemw1V2xNMWExcFlXWGhGVkVGUVFtZE9Wa0pCVFZSRFNFNXdXak5PTUdJelNteE5RalJZUkZSSmVRcE5SRWwzVFdwRmQwNUVXWGhOVm05WVJGUkplVTFFU1hkTmFrVjNUbFJaZUUxR2IzZEZla1ZTVFVFNFIwRXhWVVZEYUUxSll6SnNibU16VW5aamJWVjNDbGRVUVZSQ1oyTnhhR3RxVDFCUlNVSkNaMmR4YUd0cVQxQlJUVUpDZDA1RFFVRlVVMVJ2VEhWS2N5OTFSV05IU2tRME5VWmFiVE5wWmxKTU4yOXVRVWNLWlZaNWJuWkhVbmN6WnpKMU0wbFhTREZuU2tSamNERjRSWFI2UVZCUWJYQmhlVGRtTmxCNE1XeFpNa0ZyWnpsMGEyb3dRa1J2UTNkdk5FbENlbXBEUXdwQlkyOTNSR2RaUkZaU01GQkJVVWd2UWtGUlJFRm5aVUZOUWsxSFFURlZaRXBSVVUxTlFXOUhRME56UjBGUlZVWkNkMDFFVFVGM1IwRXhWV1JGZDBWQ0NpOTNVVU5OUVVGM1NGRlpSRlpTTUU5Q1FsbEZSazlSYTNZNVoyMVpXVFpWU0doQ1pWSnJMMWx4VlVsaU1WRldiMDFDT0VkQk1WVmtTWGRSV1UxQ1lVRUtSa1pxUVVoc0sxSlNZVlp0Y1ZoeVRXdExSMVJKZEVGeGVHTllOazFIVFVkQk1WVmtSVkZTWTAxR2NVZFhSMmd3WkVoQ2VrOXBPSFphTW13d1lVaFdhUXBNYlU1MllsTTVjbGx1VGpCTU0xSnNZMjVLYUZwdE9YbGlVekZ5WkZkS2JHTXpVbWhaTW5OMlRHMWtjR1JIYURGWmFUa3pZak5LY2xwdGVIWmtNMDEyQ21KWFJuQmlhVFUxWWxkNFFXTnRWbTFqZVRsdldsZEdhMk41T1hSWldFNHdXbGhKZDBWbldVdExkMWxDUWtGSFJIWjZRVUpCWjFGRlkwaFdlbUZFUVcwS1FtZHZja0puUlVWQldVOHZUVUZGUmtKQ2FISlpiazR3VEROU2JHTnVTbWhhYlRsNVlsTXhjbVJYU214ak0xSm9XVEp6ZDA1bldVdExkMWxDUWtGSFJBcDJla0ZDUVhkUmIxcHFSVEZPVjFGNVdXMUplRTU2V210TlZFVTFXV3BHYlU5VVNUSk9WRlV6V1hwTk5WbDZTbWxQVkdzMVdtcFNhRnBxWjNkWmVrRTFDa0puYjNKQ1owVkZRVmxQTDAxQlJVSkNRM1J2WkVoU2QyTjZiM1pNTTFKMllUSldkVXh0Um1wa1IyeDJZbTVOZFZveWJEQmhTRlpwWkZoT2JHTnRUbllLWW01U2JHSnVVWFZaTWpsMFRVSTRSME5wYzBkQlVWRkNaemM0ZDBGUldVVkZXRXBzV201TmRtRkhWbWhhU0UxMllsZEdlbVJIVm5sTlEwRkhRMmx6UndwQlVWRkNaemM0ZDBGUlVVVkZhMG94WVZkNGEwbEdVbXhqTTFGblZVaFdhV0pIYkhwaFJFRkxRbWRuY1docmFrOVFVVkZFUVhkT2IwRkVRbXhCYWtWQkNtdDJORTFLYUdGRGFFMUJaMHBWVTNWWll6bFBWRWt3WTB0bU9XTnlObU14Y1RreVYyOXFMM1ZsV0RKRFR6Z3JMMDQyU25SM1FVNTRVSElyTjNWNlpGQUtRV3BDYVhwR2NHZEVMelJzWW5aa1NuRnplWE5HYlVSeU1TdFNNSGhKWjI1S1N5c3JaWGROYmtKaVMxQkVMemd3VTNJelFYTTVMMWxxV1U5M05EVjRkUXA2ZVdzOUNpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19", + IntegratedTime: swag.Int64(123), + LogID: swag.String("deadbeef"), + LogIndex: swag.Int64(2), + Verification: &models.LogEntryAnonVerification{ + InclusionProof: &models.InclusionProof{ + Checkpoint: swag.String("checkpoint"), + Hashes: []string{"deadbeef", "abcdefaa"}, + LogIndex: swag.Int64(1), + RootHash: swag.String("abcdefaa"), + TreeSize: swag.Int64(2), + }, + SignedEntryTimestamp: strfmt.Base64("set"), + }, + }, + timestampBytes: []byte{}, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + bundle, err := MakeProtobufBundle(tc.hint, tc.rawCert, tc.rekorEntry, tc.timestampBytes) + if err != nil { + t.Errorf("unexpected err %s", err) + } + if tc.hint != "" && bundle.VerificationMaterial.GetPublicKey() == nil { + t.Errorf("Verification material should be public key") + } + if len(tc.rawCert) > 0 && bundle.VerificationMaterial.GetCertificate() == nil { + t.Errorf("Verification material should be certificate") + } + if tc.rekorEntry != nil && len(bundle.VerificationMaterial.GetTlogEntries()) == 0 { + t.Errorf("Verification material should contain Tlog Entries") + } + if len(tc.timestampBytes) > 0 && bundle.VerificationMaterial.GetTimestampVerificationData() == nil { + t.Errorf("Verification material should have timestamp") + } + }) + } +} diff --git a/pkg/cosign/env/env.go b/pkg/cosign/env/env.go index a2960e08143..016687acb8e 100644 --- a/pkg/cosign/env/env.go +++ b/pkg/cosign/env/env.go @@ -58,6 +58,7 @@ const ( VariableSigstoreRootFile Variable = "SIGSTORE_ROOT_FILE" VariableSigstoreRekorPublicKey Variable = "SIGSTORE_REKOR_PUBLIC_KEY" VariableSigstoreIDToken Variable = "SIGSTORE_ID_TOKEN" //nolint:gosec + VariableSigstoreTSACertificateFile Variable = "SIGSTORE_TSA_CERTIFICATE_FILE" // Other external environment variables VariableGitHubHost Variable = "GITHUB_HOST" @@ -138,7 +139,12 @@ var ( Sensitive: false, External: true, }, - + VariableSigstoreTSACertificateFile: { + Description: "path to the concatenated PEM-encoded TSA certificate file (leaf, intermediate(s), root) used by Sigstore", + Expects: "path to the TSA certificate file", + Sensitive: false, + External: true, + }, VariableGitHubHost: { Description: "is URL of the GitHub Enterprise instance", Expects: "string with the URL of GitHub Enterprise instance", diff --git a/pkg/cosign/keys.go b/pkg/cosign/keys.go index 9adc22525f8..ed5bbc16d4b 100644 --- a/pkg/cosign/keys.go +++ b/pkg/cosign/keys.go @@ -58,7 +58,6 @@ type Keys struct { public crypto.PublicKey } -// TODO(jason): Move this to an internal package. type KeysBytes struct { PrivateBytes []byte PublicBytes []byte @@ -69,12 +68,17 @@ func (k *KeysBytes) Password() []byte { return k.password } -// TODO(jason): Move this to an internal package. +// GeneratePrivateKey generates an ECDSA private key with the P-256 curve. func GeneratePrivateKey() (*ecdsa.PrivateKey, error) { return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) } -// TODO(jason): Move this to the only place it's used in cmd/cosign/cli/importkeypair, and unexport it. +// ImportKeyPair imports a key pair from a file containing a PEM-encoded +// private key encoded with a password provided by the 'pf' function. +// The private key can be in one of the following formats: +// - RSA private key (PKCS #1) +// - ECDSA private key +// - PKCS #8 private key (RSA, ECDSA or ED25519). func ImportKeyPair(keyPath string, pf PassFunc) (*KeysBytes, error) { kb, err := os.ReadFile(filepath.Clean(keyPath)) if err != nil { @@ -180,7 +184,6 @@ func marshalKeyPair(ptype string, keypair Keys, pf PassFunc) (key *KeysBytes, er }, nil } -// TODO(jason): Move this to an internal package. func GenerateKeyPair(pf PassFunc) (*KeysBytes, error) { priv, err := GeneratePrivateKey() if err != nil { @@ -191,7 +194,7 @@ func GenerateKeyPair(pf PassFunc) (*KeysBytes, error) { return marshalKeyPair(SigstorePrivateKeyPemType, Keys{priv, priv.Public()}, pf) } -// TODO(jason): Move this to an internal package. +// PemToECDSAKey marshals and returns the PEM-encoded ECDSA public key. func PemToECDSAKey(pemBytes []byte) (*ecdsa.PublicKey, error) { pub, err := cryptoutils.UnmarshalPEMToPublicKey(pemBytes) if err != nil { @@ -204,7 +207,8 @@ func PemToECDSAKey(pemBytes []byte) (*ecdsa.PublicKey, error) { return ecdsaPub, nil } -// TODO(jason): Move this to pkg/signature, the only place it's used, and unimport it. +// LoadPrivateKey loads a cosign PEM private key encrypted with the given passphrase, +// and returns a SignerVerifier instance. The private key must be in the PKCS #8 format. func LoadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) { // Decrypt first p, _ := pem.Decode(key) @@ -219,7 +223,6 @@ func LoadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) { if err != nil { return nil, fmt.Errorf("decrypt: %w", err) } - pk, err := x509.ParsePKCS8PrivateKey(x509Encoded) if err != nil { return nil, fmt.Errorf("parsing private key: %w", err) diff --git a/pkg/cosign/keys_test.go b/pkg/cosign/keys_test.go index 3fe9994433b..f1408da27e5 100644 --- a/pkg/cosign/keys_test.go +++ b/pkg/cosign/keys_test.go @@ -53,6 +53,35 @@ pGAaLxggvtvuncMuTrG+cdmsR9SafSFKRS92NCxhOUonQ+NP6mLskIGzJZoQ5JvQ qGzRVIDGbNkrVHM0IsAtHRpC0rYrtZY+9OwiraGcsqUMLwwQdCA= -----END RSA PRIVATE KEY-----` +// RSA 2048 key encoded with PCKS#1 +const validrsapkcs1 = `-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAqvVkqzBrMzl4TC5VsBgXnQiCo861QcB1TqdwgGzYkrNdF6fr +UisPWgjMJixolmpwHv+088rsbiSlD9hc9DmzgCJPto7wvGeTILa9cNHCGKm12q7K +TFnVUTH9z6D4E3F/IsI22Pg8+cSyeDn+LKxMadTohlcXJ8jqcH75KezoGngMp5OJ +vqs93lkLep+UJMspV029z7PzF9uoT8gI02Adfq5Zkfu8VmIy8gkpYgTBCpNnD01u +vo6HoAYG/mHqgPYivWBwi221GPjJWmCPB1rIJHlpAYUETA5jUY2UwP1oQx54ybcj +eNjMRP5J0cGyOI8MT0j9ul7/DJde3Ds5A7BA9QIDAQABAoIBAQCJ3Q5rhsZMLsI2 +HP943FTei+heFOnStlNjNF/jEOOtmfsugnmgb50XrBSFjDZjZj44oVjZaQE06VQ6 +7O44/PcmE4VY4Ph91sCtFvC6NE1j+ifuzBnTbHY73iah81tawqIV86yrV7REbzzE ++29fsyqEBe/ltgG0Ua/NPHfOOYALJwZVx8ozkz7xOyU23kNxSzp3T0FBnYYIuzrI +a4h7FVxGLbIJQ3xWBU5xkd4m7EqgFYkWCfSXAVoLT2z7eJSYAmITuiQXl8uDz1XY +lWKgOwkRJrMVVD8hDME7Hoc/RlKmYX64IZ3lv70NuyKDPTuhmoIQRJ49mVaqdPtH +v0Z9L0tBAoGBAMPFcJFaR+VdmsZ2DXQlsPQNAB064SYbIXx/pxNVoDYkHyMfZsf3 +vjf4gMKNsHTM4u812UpsE5762OqdVKmWXQc60mkuEk7N55iXuBJiJxSpuj6IbiLw +ogV+B40UC9luOISQpDYdY1Km1ho4HRngNkXMlJ48tFuwIP3lwwz3FtFZAoGBAN+N +wVssBvNhHzGfcUMxxCwJKfHCx1ANWuTe+AsDtpZRTExMcX1PH1euxUV9aII9Klg7 +A7FN1It78pDrQBNQJoeMON+5N53//geY6stDfhPkOoT8Zqg2VEz4WRihUgAUHESk +pUVYSvEXG7J7AG5iGgn0B3P9PMvvReIHnTeQ1rz9AoGAWAR31NHrSyMniBzhdZvQ +kBkcOQgU3AYMqyXVXyr7KfxZh3gBxNwMyKtQcKg1cn3/dZ8XP4+RzsNnLSxpOQni +b3Kx0RomnwmSG5fy6Uj52x9oHd9G7SyVG7UK/hHKNgqJHIjPW4kg87MQxZ7+7nhQ +zlbpZq9SQ3rPind3l2er+ZkCgYASFs9ZiEN7uBUlF8i7bjB4e7lYJbGpCZucP2qE +waUpnqR03A6m3BsmJi8yQ0aMm1Rs1UGkPC8BpmLnVRHXPjoP58nGWJ9meotcpAQD +tI9kHqiZkC7iV5sUq1fSRWN0PCxZZZU1+kH+JieIlqlfRTLkMUnVGd2shsz50DHp +iB/IJQKBgAO3kRRVszif2jdo7gzDsiSQ+fSyD6yEE9eP+uNwLZw9bQhyW5wlXF+t +dR5olNrc0bP542MHL5vigRnezq9hT1hbLkQg/MA2k5FrMHIZshfWITnI5B5I2sw6 +wu/XEVtNr8RincoHXjov4DiqgbLPWubM7FHLN5CW6nRLXhGkb4+7 +-----END RSA PRIVATE KEY-----` + // RSA 2048 key encoded with PKCS#8 const validrsapkcs8 = `-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwDtRl4McMhk4Q @@ -257,7 +286,7 @@ const ed25519key = `-----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIALEbo1EFnWFqBK/wC+hhypG/8hXEerwdNetAoFoFVdv -----END PRIVATE KEY-----` -// COSIGN labeled key +// COSIGN labeled RSA key const pemcosignkey = `-----BEGIN ENCRYPTED COSIGN PRIVATE KEY----- eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6 OCwicCI6MX0sInNhbHQiOiJ4WWdoc09JTUxUWGNOT0RsclNIOUNKc1FlOVFnZmN1 @@ -270,6 +299,19 @@ Y1pmbEJheXZMV3pXblo4d2NDZ2ZpT1o1VXlRTEFJMHh0dnR6dEh3cTdDV1Vhd3V4 RlhlNDZzck9TUE9SNHN6bytabWErUGovSFE9PSJ9 -----END ENCRYPTED COSIGN PRIVATE KEY-----` +// COSIGN labeled EC key +const pemcosigneckey = `-----BEGIN ENCRYPTED COSIGN PRIVATE KEY----- +eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjo2NTUzNiwiciI6 +OCwicCI6MX0sInNhbHQiOiJHK3F5WTYrNzhNS0JzMXNGTGs1ajYwcS9kS3Z1czBW +VkhlSHZybC9POTF3PSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94 +Iiwibm9uY2UiOiJRc2JGdG13WDRDK2ttV3ZCcVRaMEFGOUFYdk1jRmg1SCJ9LCJj +aXBoZXJ0ZXh0IjoiREM5T28zeldiYVQzSXYwdFVnWEdycjUxYW1samwwNlQ5MTNP +VkxPbWpuMWhnK2o2WXRUbWg3SGhZSlY1N2J5eGE0Q281bE9YYmRqbTJ3aklubEd1 +Um5aZCt5OExnekpSNzFSeEhKVzgrWmRlcFJmYWJMTjdHbDgrSFZEcERVQ3NxQnRh +VngyblpGbFEwWUl1anZwbFphblNGaUVvdERLVGkxZ3VhUXIwUHNzYU01NXZxbTRY +WS9rPSJ9 +-----END ENCRYPTED COSIGN PRIVATE KEY-----` + // SIGSTORE labeled key const pemsigstorekey = `-----BEGIN ENCRYPTED SIGSTORE PRIVATE KEY----- eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6 @@ -317,16 +359,22 @@ func TestLoadECDSAPrivateKey(t *testing.T) { } func TestReadingPrivatePemTypes(t *testing.T) { + pemECErrMsg := "parsing private key: x509: failed to parse private key (use ParseECPrivateKey instead for this key format)" testCases := []struct { pemType string pemData []byte expected error }{ { - pemType: "COSIGN PEM Type", + pemType: "COSIGN PEM RSA Type", pemData: []byte(pemcosignkey), expected: nil, }, + { + pemType: "COSIGN PEM EC Type", + pemData: []byte(pemcosigneckey), + expected: errors.New(pemECErrMsg), + }, { pemType: "SISTORE PEM Type", pemData: []byte(pemsigstorekey), @@ -337,7 +385,11 @@ func TestReadingPrivatePemTypes(t *testing.T) { for _, tc := range testCases { t.Run(tc.pemType, func(t *testing.T) { _, err := LoadPrivateKey(tc.pemData, []byte("hello")) - require.Equal(t, tc.expected, err) + if tc.expected == nil { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tc.expected.Error()) + } }) } } @@ -363,6 +415,11 @@ func TestImportPrivateKey(t *testing.T) { pemData: validrsa, expected: nil, }, + { + fileName: "validrsapkcs1.key", + pemData: validrsapkcs1, + expected: nil, + }, { fileName: "validrsapkcs8.key", pemData: validrsapkcs8, @@ -407,7 +464,7 @@ func TestImportPrivateKey(t *testing.T) { { fileName: "invalidecp224.key", pemData: invalidecp224, - expected: errors.New("error validating ecdsa key: unsupported ec curve, expected NIST P-256, P-384, or P-521"), + expected: errors.New("error validating ecdsa key: ECDSA curve P-224 not allowed"), }, { fileName: "invalidecunsupported.key", diff --git a/pkg/cosign/tsa.go b/pkg/cosign/tsa.go new file mode 100644 index 00000000000..9d1c17a3339 --- /dev/null +++ b/pkg/cosign/tsa.go @@ -0,0 +1,154 @@ +// Copyright 2024 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cosign + +import ( + "bytes" + "context" + "crypto/x509" + "fmt" + "os" + + "github.com/sigstore/cosign/v2/pkg/cosign/env" + "github.com/sigstore/sigstore/pkg/cryptoutils" + "github.com/sigstore/sigstore/pkg/tuf" +) + +const ( + tsaLeafCertStr = `tsa_leaf.crt.pem` + tsaRootCertStr = `tsa_root.crt.pem` + tsaIntermediateCertStrPattern = `tsa_intermediate_%d.crt.pem` +) + +type TSACertificates struct { + LeafCert *x509.Certificate + IntermediateCerts []*x509.Certificate + RootCert []*x509.Certificate +} + +type GetTargetStub func(ctx context.Context, usage tuf.UsageKind, names []string) ([]byte, error) + +func GetTufTargets(ctx context.Context, usage tuf.UsageKind, names []string) ([]byte, error) { + tufClient, err := tuf.NewFromEnv(ctx) + if err != nil { + return nil, fmt.Errorf("error creating TUF client: %w", err) + } + targets, err := tufClient.GetTargetsByMeta(usage, names) + if err != nil { + return nil, fmt.Errorf("error fetching targets by metadata with usage %v: %w", usage, err) + } + + var buffer bytes.Buffer + for _, target := range targets { + buffer.Write(target.Target) + buffer.WriteByte('\n') + } + return buffer.Bytes(), nil +} + +func isTufTargetExist(ctx context.Context, name string) (bool, error) { + tufClient, err := tuf.NewFromEnv(ctx) + if err != nil { + return false, fmt.Errorf("error creating TUF client: %w", err) + } + _, err = tufClient.GetTarget(name) + if err != nil { + return false, nil + } + return true, nil +} + +// GetTSACerts retrieves trusted TSA certificates from the embedded or cached +// TUF root. If expired, makes a network call to retrieve the updated targets. +// By default, the certificates come from TUF, but you can override this for test +// purposes by using an env variable `SIGSTORE_TSA_CERTIFICATE_FILE` or a file path +// specified in `TSACertChainPath`. If using an alternate, the file should be in PEM format. +func GetTSACerts(ctx context.Context, certChainPath string, fn GetTargetStub) (*TSACertificates, error) { + altTSACert := env.Getenv(env.VariableSigstoreTSACertificateFile) + + var raw []byte + var err error + var exists bool + switch { + case altTSACert != "": + raw, err = os.ReadFile(altTSACert) + case certChainPath != "": + raw, err = os.ReadFile(certChainPath) + default: + certNames := []string{tsaLeafCertStr, tsaRootCertStr} + for i := 0; ; i++ { + intermediateCertStr := fmt.Sprintf(tsaIntermediateCertStrPattern, i) + exists, err = isTufTargetExist(ctx, intermediateCertStr) + if err != nil { + return nil, fmt.Errorf("error fetching TSA certificates: %w", err) + } + if !exists { + break + } + certNames = append(certNames, intermediateCertStr) + } + raw, err = fn(ctx, tuf.TSA, certNames) + if err != nil { + return nil, fmt.Errorf("error fetching TSA certificates: %w", err) + } + } + + if err != nil { + return nil, fmt.Errorf("error reading TSA certificate file: %w", err) + } + + leaves, intermediates, roots, err := splitPEMCertificateChain(raw) + if err != nil { + return nil, fmt.Errorf("error splitting TSA certificates: %w", err) + } + + if len(leaves) != 1 { + return nil, fmt.Errorf("TSA certificate chain must contain exactly one leaf certificate") + } + + if len(roots) == 0 { + return nil, fmt.Errorf("TSA certificate chain must contain at least one root certificate") + } + + return &TSACertificates{ + LeafCert: leaves[0], + IntermediateCerts: intermediates, + RootCert: roots, + }, nil +} + +// splitPEMCertificateChain returns a list of leaf (non-CA) certificates, a certificate pool for +// intermediate CA certificates, and a certificate pool for root CA certificates +func splitPEMCertificateChain(pem []byte) (leaves, intermediates, roots []*x509.Certificate, err error) { + certs, err := cryptoutils.UnmarshalCertificatesFromPEM(pem) + if err != nil { + return nil, nil, nil, err + } + + for _, cert := range certs { + if !cert.IsCA { + leaves = append(leaves, cert) + } else { + // root certificates are self-signed + if bytes.Equal(cert.RawSubject, cert.RawIssuer) { + roots = append(roots, cert) + } else { + intermediates = append(intermediates, cert) + } + } + } + + return leaves, intermediates, roots, nil +} diff --git a/pkg/cosign/tsa_test.go b/pkg/cosign/tsa_test.go new file mode 100644 index 00000000000..9487d1d25fa --- /dev/null +++ b/pkg/cosign/tsa_test.go @@ -0,0 +1,119 @@ +// Copyright 2024 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cosign + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +const ( + testLeafCert = `-----BEGIN CERTIFICATE----- +MIIBjzCCATSgAwIBAgIRAOoa5khdNMW26Nz0VCvjbBAwCgYIKoZIzj0EAwIwGzEZ +MBcGA1UEAxMQaHR0cHM6Ly9ibGFoLmNvbTAgFw0yNDA2MDMyMDE2MDFaGA8yMTI0 +MDUxMDIwMTYwMFowGzEZMBcGA1UEAxMQaHR0cHM6Ly9ibGFoLmNvbTBZMBMGByqG +SM49AgEGCCqGSM49AwEHA0IABL7w/TW5lOU9KwnGQRIyZp/ReNQF1eA2rKC582Jo +nMomwCk2bA8c5dHrvvHe+mI8JeMNEg3lkIsVQp46dKGlgYujVzBVMA4GA1UdDwEB +/wQEAwIBBjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSA7lVsQm5OUzvYi+o8PuBs +CrAnljAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAKBggqhkjOPQQDAgNJADBGAiEA +oJSZgJPX2tqXhfvLm+5UR399+E6+rgUnSRUf4+p+K5gCIQCmtfuv8IkUIYE5ybtx ++bn5E95xINfDMSPBa+0PEbB5RA== +-----END CERTIFICATE-----` + testRootCert = `-----BEGIN CERTIFICATE----- +MIIBezCCASKgAwIBAgIRAMvdlXw/uuYvsJaCTa02uW4wCgYIKoZIzj0EAwIwGzEZ +MBcGA1UEAxMQaHR0cHM6Ly9ibGFoLmNvbTAgFw0yNDA2MDMyMDE1NTFaGA8yMTI0 +MDUxMDIwMTU1MFowGzEZMBcGA1UEAxMQaHR0cHM6Ly9ibGFoLmNvbTBZMBMGByqG +SM49AgEGCCqGSM49AwEHA0IABLziRBPdWUTx9x3Z7zIMyo/C9cqsLK+hqnWDQS7K +TA38mZhMHnJ0vSaEA4g9J2ccI1x4G/HegCi9LkJG/EZLBjyjRTBDMA4GA1UdDwEB +/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBQuDQqo97s/5Lc5 +IxmFcVg3arCV2DAKBggqhkjOPQQDAgNHADBEAiAJOr0GnYaqVxShSEgVJKi/hYXf +PH5bKk0M9ceasS7VwQIgMkxzlWr+m10OELtAbOlI8faN/5WFKm8m8rrwnhmHzjw= +-----END CERTIFICATE-----` +) + +func MockGetTufTargets(name string) ([]byte, error) { + if name == `tsa_leaf.crt.pem` { + return []byte(testLeafCert), nil + } else if name == `tsa_root.crt.pem` { + return []byte(testRootCert), nil + } + + return nil, errors.New("no intermediates") +} + +func TestGetTSACertsFromEnv(t *testing.T) { + tempFile, err := os.CreateTemp("", "tsa_cert_chain.pem") + require.NoError(t, err) + defer os.Remove(tempFile.Name()) + + _, err = tempFile.Write([]byte(testLeafCert + "\n" + testRootCert)) + require.NoError(t, err) + + os.Setenv("SIGSTORE_TSA_CERTIFICATE_FILE", tempFile.Name()) + defer os.Unsetenv("SIGSTORE_TSA_CERTIFICATE_FILE") + + tsaCerts, err := GetTSACerts(context.Background(), tempFile.Name(), GetTufTargets) + if err != nil { + t.Fatalf("Failed to get TSA certs from env: %v", err) + } + require.NotNil(t, tsaCerts) + require.NotNil(t, tsaCerts.LeafCert) + require.NotNil(t, tsaCerts.RootCert) + require.Len(t, tsaCerts.RootCert, 1) +} + +func TestGetTSACertsFromPath(t *testing.T) { + tempFile, err := os.CreateTemp("", "tsa_cert_chain_path.pem") + require.NoError(t, err) + defer os.Remove(tempFile.Name()) + + _, err = tempFile.Write([]byte(testLeafCert + "\n" + testRootCert)) + require.NoError(t, err) + + tsaCerts, err := GetTSACerts(context.Background(), tempFile.Name(), GetTufTargets) + if err != nil { + t.Fatalf("Failed to get TSA certs from path: %v", err) + } + require.NotNil(t, tsaCerts) + require.NotNil(t, tsaCerts.LeafCert) + require.NotNil(t, tsaCerts.RootCert) + require.Len(t, tsaCerts.RootCert, 1) +} + +func TestGetTSACertsFromTUF(t *testing.T) { + originalValue := os.Getenv("SIGSTORE_TSA_CERTIFICATE_FILE") + os.Unsetenv("SIGSTORE_TSA_CERTIFICATE_FILE") + defer os.Setenv("SIGSTORE_TSA_CERTIFICATE_FILE", originalValue) + + tempFile, err := os.CreateTemp("", "tsa_cert_chain.pem") + require.NoError(t, err) + defer os.Remove(tempFile.Name()) + + _, err = tempFile.Write([]byte(testLeafCert + "\n" + testRootCert)) + require.NoError(t, err) + + tsaCerts, err := GetTSACerts(context.Background(), tempFile.Name(), GetTufTargets) + if err != nil { + t.Fatalf("Failed to get TSA certs from TUF: %v", err) + } + require.NotNil(t, tsaCerts) + require.NotNil(t, tsaCerts.LeafCert) + require.NotNil(t, tsaCerts.RootCert) + require.Len(t, tsaCerts.RootCert, 1) +} diff --git a/pkg/cosign/verify.go b/pkg/cosign/verify.go index abd551d8028..3ab5d76026a 100644 --- a/pkg/cosign/verify.go +++ b/pkg/cosign/verify.go @@ -710,6 +710,7 @@ func verifyInternal(ctx context.Context, sig oci.Signature, h v1.Hash, } t := time.Unix(*e.IntegratedTime, 0) acceptableRekorBundleTime = &t + bundleVerified = true } } diff --git a/pkg/cosign/verify_sct.go b/pkg/cosign/verify_sct.go index 934b2d97941..1b904c2c4fd 100644 --- a/pkg/cosign/verify_sct.go +++ b/pkg/cosign/verify_sct.go @@ -108,7 +108,7 @@ func VerifySCT(_ context.Context, certPEM, chainPEM, rawSCT []byte, pubKeys *Tru } err = ctutil.VerifySCT(pubKeyMetadata.PubKey, []*ctx509.Certificate{cert, certChain[0]}, sct, true) if err != nil { - return fmt.Errorf("error verifying embedded SCT") + return fmt.Errorf("error verifying embedded SCT: %w", err) } if pubKeyMetadata.Status != tuf.Active { fmt.Fprintf(os.Stderr, "**Info** Successfully verified embedded SCT using an expired verification key\n") diff --git a/pkg/cosign/verify_test.go b/pkg/cosign/verify_test.go index 03d88842d4e..30586d4a759 100644 --- a/pkg/cosign/verify_test.go +++ b/pkg/cosign/verify_test.go @@ -18,6 +18,7 @@ import ( "bytes" "context" "crypto" + "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" @@ -38,6 +39,7 @@ import ( "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/in-toto/in-toto-golang/in_toto" "github.com/secure-systems-lab/go-securesystemslib/dsse" @@ -51,12 +53,15 @@ import ( "github.com/sigstore/cosign/v2/pkg/types" "github.com/sigstore/cosign/v2/test" "github.com/sigstore/rekor/pkg/generated/client" + "github.com/sigstore/rekor/pkg/generated/client/entries" "github.com/sigstore/rekor/pkg/generated/models" rtypes "github.com/sigstore/rekor/pkg/types" + hashedrekord_v001 "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" "github.com/sigstore/sigstore/pkg/tuf" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/transparency-dev/merkle/rfc6962" ) @@ -524,41 +529,111 @@ func uuid(e models.LogEntryAnon) string { return hex.EncodeToString(rfc6962.DefaultHasher.HashLeaf(entryBytes)) } -// This test ensures that image signature validation fails properly if we are -// using a SigVerifier with Rekor. -// In other words, we require checking against RekorPubKeys when verifying -// image signature. -// This could be made more robust with supplying a mismatched trusted RekorPubKeys -// rather than none. -// See https://github.com/sigstore/cosign/v2/issues/1816 for more details. -func TestVerifyImageSignatureWithSigVerifierAndRekor(t *testing.T) { - sv, privKey, err := signature.NewDefaultECDSASignerVerifier() - if err != nil { - t.Fatalf("error generating verifier: %v", err) +func TestImageSignatureVerificationWithRekor(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Generate ECDSA signer and public key for signing the blob. + signer, publicKey := generateSigner(t) + blob, blobSignature, blobSignatureBase64 := generateBlobSignature(t, signer) + + // Create an OCI signature which will be verified. + ociSignature, err := static.NewSignature(blob, blobSignatureBase64) + require.NoError(t, err, "error creating OCI signature") + + // Set up mock Rekor signer and log ID. + rekorSigner, rekorPublicKey := generateSigner(t) + logID := calculateLogID(t, rekorPublicKey) + + // Create a mock Rekor log entry to simulate Rekor behavior. + rekorEntry := createRekorEntry(ctx, t, logID, rekorSigner, blob, blobSignature, publicKey) + + // Mock Rekor client to return the mock log entry for verification. + mockClient := &client.Rekor{ + Entries: &mockEntriesClient{ + searchLogQueryFunc: func(_ *entries.SearchLogQueryParams, _ ...entries.ClientOption) (*entries.SearchLogQueryOK, error) { + return &entries.SearchLogQueryOK{ + Payload: []models.LogEntry{*rekorEntry}, + }, nil + }, + }, } - payload := []byte{1, 2, 3, 4} - h := sha256.Sum256(payload) - sig, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256) - ociSig, _ := static.NewSignature(payload, base64.StdEncoding.EncodeToString(sig)) + // Define trusted Rekor public keys for verification. + trustedRekorPubKeys := &TrustedTransparencyLogPubKeys{ + Keys: map[string]TransparencyLogPubKey{ + logID: { + PubKey: rekorPublicKey, + Status: tuf.Active, + }, + }, + } - // Add a fake rekor client - this makes it look like there's a matching - // tlog entry for the signature during validation (even though it does not - // match the underlying data / key) - mClient := new(client.Rekor) - mClient.Entries = &mock.EntriesClient{ - Entries: []*models.LogEntry{&data}, + // Generate non-matching public key for failure test cases. + _, nonMatchingPublicKey := generateSigner(t) + nonMatchingRekorPubKeys := &TrustedTransparencyLogPubKeys{ + Keys: map[string]TransparencyLogPubKey{ + logID: { + PubKey: nonMatchingPublicKey, + Status: tuf.Active, + }, + }, } - if _, err := VerifyImageSignature(context.TODO(), ociSig, v1.Hash{}, &CheckOpts{ - SigVerifier: sv, - RekorClient: mClient, - Identities: []Identity{{Subject: "subject@mail.com", Issuer: "oidc-issuer"}}, - }); err == nil || !strings.Contains(err.Error(), "no valid tlog entries found no trusted rekor public keys provided") { - // This is failing to validate the Rekor public key itself. - // At the very least this ensures - // that we're hitting tlog validation during signature checking. - t.Fatalf("expected error while verifying signature, got %s", err) + tests := []struct { + name string + checkOpts CheckOpts + rekorClient *client.Rekor + expectError bool + errorMsg string + }{ + { + name: "Verification succeeds with valid Rekor public keys", + checkOpts: CheckOpts{ + SigVerifier: signer, + RekorClient: mockClient, + RekorPubKeys: trustedRekorPubKeys, + Identities: []Identity{{Subject: "subject@mail.com", Issuer: "oidc-issuer"}}, + }, + rekorClient: mockClient, + expectError: false, + }, + { + name: "Verification fails with no Rekor public keys", + checkOpts: CheckOpts{ + SigVerifier: signer, + RekorClient: mockClient, + Identities: []Identity{{Subject: "subject@mail.com", Issuer: "oidc-issuer"}}, + }, + rekorClient: mockClient, + expectError: true, + errorMsg: "no valid tlog entries found no trusted rekor public keys provided", + }, + { + name: "Verification fails with non-matching Rekor public keys", + checkOpts: CheckOpts{ + SigVerifier: signer, + RekorClient: mockClient, + RekorPubKeys: nonMatchingRekorPubKeys, + Identities: []Identity{{Subject: "subject@mail.com", Issuer: "oidc-issuer"}}, + }, + rekorClient: mockClient, + expectError: true, + errorMsg: "verifying signedEntryTimestamp: unable to verify SET", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + bundleVerified, err := VerifyImageSignature(ctx, ociSignature, v1.Hash{}, &tt.checkOpts) + if tt.expectError { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errorMsg) + } else { + assert.NoError(t, err) + assert.True(t, bundleVerified, "bundle verification failed") + } + }) } } @@ -1499,3 +1574,108 @@ func TestVerifyRFC3161Timestamp(t *testing.T) { t.Fatalf("expected error verifying without a root certificate, got: %v", err) } } + +// Mock Rekor client +type mockEntriesClient struct { + entries.ClientService + searchLogQueryFunc func(params *entries.SearchLogQueryParams, opts ...entries.ClientOption) (*entries.SearchLogQueryOK, error) +} + +func (m *mockEntriesClient) SearchLogQuery(params *entries.SearchLogQueryParams, opts ...entries.ClientOption) (*entries.SearchLogQueryOK, error) { + if m.searchLogQueryFunc != nil { + return m.searchLogQueryFunc(params, opts...) + } + return nil, nil +} + +// createRekorEntry creates a mock Rekor log entry. +func createRekorEntry(ctx context.Context, t *testing.T, logID string, signer signature.Signer, payload, signature []byte, publicKey crypto.PublicKey) *models.LogEntry { + payloadHash := sha256.Sum256(payload) + + publicKeyBytes, err := cryptoutils.MarshalPublicKeyToPEM(publicKey) + require.NoError(t, err) + + artifactProperties := rtypes.ArtifactProperties{ + ArtifactHash: hex.EncodeToString(payloadHash[:]), + SignatureBytes: signature, + PublicKeyBytes: [][]byte{publicKeyBytes}, + PKIFormat: "x509", + } + + // Create and canonicalize Rekor entry + entryProps, err := hashedrekord_v001.V001Entry{}.CreateFromArtifactProperties(ctx, artifactProperties) + require.NoError(t, err) + + rekorEntry, err := rtypes.UnmarshalEntry(entryProps) + require.NoError(t, err) + + canonicalEntry, err := rekorEntry.Canonicalize(ctx) + require.NoError(t, err) + + // Create log entry + integratedTime := time.Now().Unix() + logEntry := models.LogEntryAnon{ + Body: base64.StdEncoding.EncodeToString(canonicalEntry), + IntegratedTime: swag.Int64(integratedTime), + LogIndex: swag.Int64(0), + LogID: swag.String(logID), + } + + // Canonicalize the log entry and sign it + jsonLogEntry, err := json.Marshal(logEntry) + require.NoError(t, err) + + canonicalPayload, err := jsoncanonicalizer.Transform(jsonLogEntry) + require.NoError(t, err) + + signedEntryTimestamp, err := signer.SignMessage(bytes.NewReader(canonicalPayload)) + require.NoError(t, err) + + // Calculate leaf hash and add verification + entryUUID, err := ComputeLeafHash(&logEntry) + require.NoError(t, err) + + logEntry.Verification = &models.LogEntryAnonVerification{ + SignedEntryTimestamp: signedEntryTimestamp, + InclusionProof: &models.InclusionProof{ + LogIndex: swag.Int64(0), + TreeSize: swag.Int64(1), + RootHash: swag.String(hex.EncodeToString(entryUUID)), + Hashes: []string{}, + }, + } + + // Return the constructed log entry + return &models.LogEntry{hex.EncodeToString(entryUUID): logEntry} +} + +// generateSigner creates an ECDSA signer and public key. +func generateSigner(t *testing.T) (signature.SignerVerifier, crypto.PublicKey) { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err, "error generating private key") + + signer, err := signature.LoadECDSASignerVerifier(privateKey, crypto.SHA256) + require.NoError(t, err, "error loading signer") + + publicKey, err := signer.PublicKey() + require.NoError(t, err, "error getting public key") + + return signer, publicKey +} + +// generateBlobSignature signs a blob and returns the blob, its signature, and the base64-encoded signature. +func generateBlobSignature(t *testing.T, signer signature.Signer) ([]byte, []byte, string) { + blob := []byte("foo") + blobSignature, err := signer.SignMessage(bytes.NewReader(blob)) + require.NoError(t, err, "error signing blob") + blobSignatureBase64 := base64.StdEncoding.EncodeToString(blobSignature) + return blob, blobSignature, blobSignatureBase64 +} + +// calculateLogID generates a SHA-256 hash of the given public key and returns it as a hexadecimal string. +func calculateLogID(t *testing.T, pub crypto.PublicKey) string { + pubBytes, err := x509.MarshalPKIXPublicKey(pub) + require.NoError(t, err, "error marshalling public key") + digest := sha256.Sum256(pubBytes) + return hex.EncodeToString(digest[:]) +} diff --git a/pkg/oci/remote/image.go b/pkg/oci/remote/image.go index 58798339418..8c6eda5ff0e 100644 --- a/pkg/oci/remote/image.go +++ b/pkg/oci/remote/image.go @@ -21,6 +21,7 @@ import ( "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/sigstore/cosign/v2/pkg/oci" ) @@ -48,6 +49,12 @@ type image struct { opt *options } +// The wrapped Image implements ConfigLayer, but the wrapping hides that from typechecks in pkg/v1/remote. +// Make image explicitly implement ConfigLayer so that this returns a mountable config layer for pkg/v1/remote. +func (i *image) ConfigLayer() (v1.Layer, error) { + return partial.ConfigLayer(i.Image) +} + var _ oci.SignedImage = (*image)(nil) // Signatures implements oci.SignedImage diff --git a/pkg/oci/remote/signatures.go b/pkg/oci/remote/signatures.go index f8a53e0f8ea..825d80727f9 100644 --- a/pkg/oci/remote/signatures.go +++ b/pkg/oci/remote/signatures.go @@ -21,6 +21,7 @@ import ( "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/sigstore/cosign/v2/pkg/oci" "github.com/sigstore/cosign/v2/pkg/oci/empty" @@ -52,6 +53,12 @@ type sigs struct { v1.Image } +// The wrapped Image implements ConfigLayer, but the wrapping hides that from typechecks in pkg/v1/remote. +// Make sigs explicitly implement ConfigLayer so that this returns a mountable config layer for pkg/v1/remote. +func (s *sigs) ConfigLayer() (v1.Layer, error) { + return partial.ConfigLayer(s.Image) +} + var _ oci.Signatures = (*sigs)(nil) // Get implements oci.Signatures diff --git a/pkg/policy/attestation.go b/pkg/policy/attestation.go index 9d32d173e86..1fa82bf8a6f 100644 --- a/pkg/policy/attestation.go +++ b/pkg/policy/attestation.go @@ -29,6 +29,17 @@ import ( "github.com/sigstore/cosign/v2/pkg/cosign/attestation" ) +// PayloadProvider is a subset of oci.Signature that only provides the +// Payload() method. +type PayloadProvider interface { + // Payload fetches the opaque data that is being signed. + // This will always return data when there is no error. + Payload() ([]byte, error) +} + +// Assert that oci.Signature implements PayloadProvider +var _ PayloadProvider = (oci.Signature)(nil) + // AttestationToPayloadJSON takes in a verified Attestation (oci.Signature) and // marshals it into a JSON depending on the payload that's then consumable // by policy engine like cue, rego, etc. @@ -45,7 +56,7 @@ import ( // or the predicateType is not the one they are looking for. Without returning // this, it's hard for users to know which attestations/predicateTypes were // inspected. -func AttestationToPayloadJSON(_ context.Context, predicateType string, verifiedAttestation oci.Signature) ([]byte, string, error) { +func AttestationToPayloadJSON(_ context.Context, predicateType string, verifiedAttestation PayloadProvider) ([]byte, string, error) { if predicateType == "" { return nil, "", errors.New("missing predicate type") } diff --git a/pkg/policy/attestation_test.go b/pkg/policy/attestation_test.go index 017bec3c589..908f0696081 100644 --- a/pkg/policy/attestation_test.go +++ b/pkg/policy/attestation_test.go @@ -16,6 +16,7 @@ package policy import ( + "bytes" "context" "crypto/x509" "encoding/json" @@ -166,6 +167,50 @@ func TestAttestationToPayloadJson(t *testing.T) { } } +type myPayloadProvider struct { + payload []byte +} + +func (m *myPayloadProvider) Payload() ([]byte, error) { + return m.payload, nil +} + +// assert that myPayloadProvider implements PayloadProvider +var _ PayloadProvider = &myPayloadProvider{} + +// TestPayloadProvider tests that the PayloadProvider interface is working as expected. +func TestPayloadProvider(t *testing.T) { + // Control: oci.Signature + attestationBytes := readAttestationFromTestFile(t, "valid", "vuln") + ociSig, err := static.NewSignature(attestationBytes, "") + if err != nil { + t.Fatal("Failed to create static.NewSignature: ", err) + } + jsonBytes, gotPredicateType, err := AttestationToPayloadJSON(context.TODO(), "vuln", ociSig) + if err != nil { + t.Fatalf("Failed to convert : %s", err) + } + if len(jsonBytes) == 0 { + t.Fatalf("Failed to get jsonBytes") + } + if gotPredicateType != attestation.CosignVulnProvenanceV01 { + t.Fatalf("Did not get expected predicateType, want: %s got: %s", attestation.CosignVulnProvenanceV01, gotPredicateType) + } + + // Test: myPayloadProvider + provider := &myPayloadProvider{payload: attestationBytes} + jsonBytes2, gotPredicateType2, err := AttestationToPayloadJSON(context.TODO(), "vuln", provider) + if err != nil { + t.Fatalf("Failed to convert : %s", err) + } + if !bytes.Equal(jsonBytes, jsonBytes2) { + t.Fatalf("Expected same jsonBytes, got different") + } + if gotPredicateType != gotPredicateType2 { + t.Fatalf("Expected same predicateType, got different") + } +} + func checkPredicateType(t *testing.T, want, got string) { t.Helper() if want != got { diff --git a/pkg/providers/interface.go b/pkg/providers/interface.go index d85f067f0ac..000b94908b5 100644 --- a/pkg/providers/interface.go +++ b/pkg/providers/interface.go @@ -24,12 +24,12 @@ import ( var ( m sync.Mutex - providers []providerEntry + providers []ProviderEntry ) -type providerEntry struct { - name string - p Interface +type ProviderEntry struct { + Name string + Provider Interface } // Interface is what providers need to implement to participate in furnishing OIDC tokens. @@ -47,11 +47,11 @@ func Register(name string, p Interface) { defer m.Unlock() for _, pe := range providers { - if pe.name == name { - panic(fmt.Sprintf("duplicate provider for name %q, %T and %T", name, pe.p, p)) + if pe.Name == name { + panic(fmt.Sprintf("duplicate provider for name %q, %T and %T", name, pe.Provider, p)) } } - providers = append(providers, providerEntry{name: name, p: p}) + providers = append(providers, ProviderEntry{Name: name, Provider: p}) } // Enabled checks whether any of the registered providers are enabled in this execution context. @@ -59,8 +59,8 @@ func Enabled(ctx context.Context) bool { m.Lock() defer m.Unlock() - for _, provider := range providers { - if provider.p.Enabled(ctx) { + for _, pe := range providers { + if pe.Provider.Enabled(ctx) { return true } } @@ -74,11 +74,12 @@ func Provide(ctx context.Context, audience string) (string, error) { var id string var err error - for _, provider := range providers { - if !provider.p.Enabled(ctx) { + for _, pe := range providers { + p := pe.Provider + if !p.Enabled(ctx) { continue } - id, err = provider.p.Provide(ctx, audience) + id, err = p.Provide(ctx, audience) if err == nil { return id, nil } @@ -97,9 +98,16 @@ func ProvideFrom(_ context.Context, provider string) (Interface, error) { defer m.Unlock() for _, p := range providers { - if p.name == provider { - return p.p, nil + if p.Name == provider { + return p.Provider, nil } } return nil, fmt.Errorf("%s is not a valid provider", provider) } + +func Providers() []ProviderEntry { + m.Lock() + defer m.Unlock() + + return providers +} diff --git a/release/cloudbuild.yaml b/release/cloudbuild.yaml index c64e3d2cd0d..37dace885d2 100644 --- a/release/cloudbuild.yaml +++ b/release/cloudbuild.yaml @@ -32,20 +32,20 @@ steps: echo "Checking out ${_GIT_TAG}" git checkout ${_GIT_TAG} - - name: 'gcr.io/projectsigstore/cosign:v2.2.3-dev@sha256:0d795fa145b03026b7bc2a35e33068cdb75e1c1f974e604c17408bf7bd174967' + - name: 'gcr.io/projectsigstore/cosign:v2.2.4-dev@sha256:13efd4c62710d75f07d12d8aad36a8657eeffd4f5f3a40bcbc207d8aafa67d41' dir: "go/src/sigstore/cosign" env: - TUF_ROOT=/tmp args: - 'verify' - - 'ghcr.io/gythialy/golang-cross:v1.21.8-0@sha256:9c86fc6c6763cd5cd9a07f25083fc5a87f3525b5f8d7ff886822e2153f0c8405' + - 'ghcr.io/gythialy/golang-cross:v1.22.5-0@sha256:5cf8fca7fe80392c8d1597fe89d291d49120507390f25507746f73d4b7f8a8f2' - '--certificate-oidc-issuer' - "https://token.actions.githubusercontent.com" - '--certificate-identity' - - "https://github.com/gythialy/golang-cross/.github/workflows/release-golang-cross.yml@refs/tags/v1.21.8-0" + - "https://github.com/gythialy/golang-cross/.github/workflows/release-golang-cross.yml@refs/tags/v1.22.5-0" # maybe we can build our own image and use that to be more in a safe side - - name: ghcr.io/gythialy/golang-cross:v1.21.8-0@sha256:9c86fc6c6763cd5cd9a07f25083fc5a87f3525b5f8d7ff886822e2153f0c8405 + - name: ghcr.io/gythialy/golang-cross:v1.22.5-0@sha256:5cf8fca7fe80392c8d1597fe89d291d49120507390f25507746f73d4b7f8a8f2 entrypoint: /bin/sh dir: "go/src/sigstore/cosign" env: @@ -68,7 +68,7 @@ steps: gcloud auth configure-docker \ && make release - - name: ghcr.io/gythialy/golang-cross:v1.21.8-0@sha256:9c86fc6c6763cd5cd9a07f25083fc5a87f3525b5f8d7ff886822e2153f0c8405 + - name: ghcr.io/gythialy/golang-cross:v1.22.5-0@sha256:5cf8fca7fe80392c8d1597fe89d291d49120507390f25507746f73d4b7f8a8f2 entrypoint: 'bash' dir: "go/src/sigstore/cosign" env: @@ -84,6 +84,7 @@ steps: - COSIGN_YES=true - GOOGLE_SERVICE_ACCOUNT_NAME=keyless@${PROJECT_ID}.iam.gserviceaccount.com - GITHUB_USER=${_GITHUB_USER} + - LATEST_TAG=",latest" secretEnv: - GITHUB_TOKEN args: diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000000..e78e65d2f52 --- /dev/null +++ b/test/README.md @@ -0,0 +1,60 @@ +Cosign E2E Tests +================ + +How to add end-to-end tests for cosign: + +General-purpose tests +--------------------- + +If the test is only testing cosign itself or only needs access to Rekor and/or +Fulcio, add it to `e2e_test.go`. This test suite is run by the `e2e_test.sh` +script, which sets up Rekor, Fulcio, and an OIDC provider, so this can easily be +run in a developer environment with no additional setup steps. + +In your tests, where cosign calls out to Fulcio or Rekor, make sure to set the +service URLs to the constants `rekorURL` or `fulcioURL`, which point to the +local instances of Rekor and Fulcio. Also have the test call the `setLocalEnv` +which downloads keys from the ephemeral Sigstore services and configures cosign +to trust them. + +Environment-specific tests +-------------------------- + +If the test needs a more specific kind of environment, such as a KMS or an image +registry, create the test in its own file and designate a `go:build` tag for it. +In `e2e_test.go`, add a negation for that build tag. + +In your tests, set the Rekor and Fulcio URLs to the contents of the environment +variables `REKOR_URL` and `FULCIO_URL`, if Rekor and Fulcio are needed for the +test. + +Add a job for the new test in the ``e2e-test.yml`` GitHub workflow file. The job +should include the following: + +- If the test needs access to Sigstore services, a step that calls the + `sigstore/scaffolding/actions/setup` action +- Steps to do whatever other custom configuration the scenario needs +- A step to run the test with `go test -tags=e2e, -v ./test/...` + +To run these kinds of tests tests locally, you'll have to [setup +scaffolding](https://github.com/sigstore/scaffolding/blob/main/getting-started.md) +and do the other setup steps manually. + +Dos +--- + +Add E2E tests to cover any interaction that cosign has with an external service, +such as Rekor, Fulcio, a TSA, etc, or for any case where it's not possible to +cover the functionality with a unit test. + +Don'ts +------ + +- Do not add shell scripts that call the cosign binary directly unless the + functionality under test is the CLI itself and absolutely cannot be tested any + other way. As much as possible, tests should be runnable with `go test`. + +- Do not allow the tests to call the production or staging instances of the + Sigstore services, i.e. any `*.sigstore.dev` or `*.sigstage.dev` services. + They should use the local CI instances to avoid spamming the real + infrastructure. diff --git a/test/ci.mk b/test/ci.mk index 246ed3395aa..3742446c1df 100644 --- a/test/ci.mk +++ b/test/ci.mk @@ -9,7 +9,3 @@ sign-ci-containers: ko .PHONY: sign-ci-keyless-containers sign-ci-keyless-containers: ko ./scripts/sign-images-ci.sh - -.PHONY: sign-blob-experimental -sign-blob-experimental: - ./test/sign_blob_test.sh diff --git a/test/e2e_attach_test.go b/test/e2e_attach_test.go index fb3b7cbdd44..7385da4f4a6 100644 --- a/test/e2e_attach_test.go +++ b/test/e2e_attach_test.go @@ -81,10 +81,10 @@ func TestAttachSignature(t *testing.T) { pemSub1 := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: subCert1.Raw}) pemLeaf1 := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert1.Raw}) pemLeafRef1 := mkfile(string(pemLeaf1), td, t) - certChainRef1 := mkfile(string(append(pemSub1[:], pemRoot1[:]...)), td, t) + certChainRef1 := mkfile(string(append(pemSub1, pemRoot1...)), td, t) signature1, _ := privKey1.Sign(rand.Reader, hash[:], crypto.SHA256) - b64signature1 := base64.StdEncoding.EncodeToString([]byte(signature1)) + b64signature1 := base64.StdEncoding.EncodeToString(signature1) sigRef1 := mkfile(b64signature1, td, t) err := attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef1, payloadRef, pemLeafRef1, certChainRef1, "", "", imgName) @@ -130,10 +130,10 @@ func TestAttachSignature(t *testing.T) { pemSub2 := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: subCert2.Raw}) pemLeaf2 := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert2.Raw}) pemLeafRef2 := mkfile(string(pemLeaf2), td, t) - certChainRef2 := mkfile(string(append(pemSub2[:], pemRoot2[:]...)), td, t) + certChainRef2 := mkfile(string(append(pemSub2, pemRoot2...)), td, t) signature2, _ := privKey2.Sign(rand.Reader, hash[:], crypto.SHA256) - b64signature2 := base64.StdEncoding.EncodeToString([]byte(signature2)) + b64signature2 := base64.StdEncoding.EncodeToString(signature2) sigRef2 := mkfile(b64signature2, td, t) err = attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef2, payloadRef, pemLeafRef2, certChainRef2, "", "", imgName) @@ -198,12 +198,12 @@ func TestAttachWithRFC3161Timestamp(t *testing.T) { h := sha256.Sum256(b.Bytes()) signature, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256) - b64signature := base64.StdEncoding.EncodeToString([]byte(signature)) + b64signature := base64.StdEncoding.EncodeToString(signature) sigRef := mkfile(b64signature, td, t) pemleafRef := mkfile(string(pemLeaf), td, t) pemrootRef := mkfile(string(pemRoot), td, t) - certchainRef := mkfile(string(append(pemSub[:], pemRoot[:]...)), td, t) + certchainRef := mkfile(string(append(pemSub, pemRoot...)), td, t) t.Setenv("SIGSTORE_ROOT_FILE", pemrootRef) @@ -268,14 +268,14 @@ func TestAttachWithRekorBundle(t *testing.T) { h := sha256.Sum256(b.Bytes()) signature, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256) - b64signature := base64.StdEncoding.EncodeToString([]byte(signature)) + b64signature := base64.StdEncoding.EncodeToString(signature) sigRef := mkfile(b64signature, td, t) pemleafRef := mkfile(string(pemLeaf), td, t) pemrootRef := mkfile(string(pemRoot), td, t) t.Setenv("SIGSTORE_ROOT_FILE", pemrootRef) - certchainRef := mkfile(string(append(pemSub[:], pemRoot[:]...)), td, t) + certchainRef := mkfile(string(append(pemSub, pemRoot...)), td, t) localPayload := cosign.LocalSignedPayload{ Base64Signature: b64signature, @@ -350,12 +350,12 @@ func TestUploadDownload(t *testing.T) { restoreStdin := func() {} var sigRef string - if testCase.signatureType == attach.FileSignature { + switch { + case testCase.signatureType == attach.FileSignature: sigRef = mkfile(signature, td, t) - } else if testCase.signatureType == attach.StdinSignature { - sigRef = "-" - restoreStdin = mockStdin(signature, td, t) - } else { + case testCase.signatureType == attach.StdinSignature: + sigRef = mkfile(signature, td, t) + default: sigRef = signature } // Upload it! @@ -444,12 +444,13 @@ func TestAttachSBOM_bom_flag(t *testing.T) { img, _, cleanup := mkimage(t, imgName) var sbomRef string restoreStdin := func() {} - if testCase.bomType == attach.FileSignature { + switch { + case testCase.bomType == attach.FileSignature: sbomRef = mkfile(testCase.bom, td, t) - } else if testCase.bomType == attach.StdinSignature { + case testCase.bomType == attach.StdinSignature: sbomRef = "-" restoreStdin = mockStdin(testCase.bom, td, t) - } else { + default: sbomRef = testCase.bom } diff --git a/test/e2e_insecure_registry_test.go b/test/e2e_insecure_registry_test.go new file mode 100644 index 00000000000..0da6ec6380d --- /dev/null +++ b/test/e2e_insecure_registry_test.go @@ -0,0 +1,125 @@ +// Copyright 2024 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build e2e && registry + +package test + +import ( + "context" + "crypto/tls" + "net/http" + "os" + "path" + "testing" + + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/random" + "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" + cliverify "github.com/sigstore/cosign/v2/cmd/cosign/cli/verify" + "github.com/sigstore/cosign/v2/pkg/cosign/env" + ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote" +) + +const ( + oci11Var = "OCI11" + rekorURLVar = "REKOR_URL" +) + +func TestInsecureRegistry(t *testing.T) { + if os.Getenv("COSIGN_TEST_REPO") == "" { + t.Fatal("COSIGN_TEST_REPO must be set to an insecure registry for this test") + } + repo, stop := reg(t) + defer stop() + td := t.TempDir() + + imgName := path.Join(repo, "cosign-registry-e2e") + cleanup := makeImageIndexWithInsecureRegistry(t, imgName) + defer cleanup() + + _, privKey, pubKey := keypair(t, td) + + useOCI11 := os.Getenv("oci11Var") != "" + + rekorURL := os.Getenv(rekorURLVar) + must(downloadAndSetEnv(t, rekorURL+"/api/v1/log/publicKey", env.VariableSigstoreRekorPublicKey.String(), td), t) + + ko := options.KeyOpts{ + KeyRef: privKey, + PassFunc: passFunc, + RekorURL: rekorURL, + SkipConfirmation: true, + } + so := options.SignOptions{ + Upload: true, + TlogUpload: true, + } + mustErr(sign.SignCmd(ro, ko, so, []string{imgName}), t) + so.Registry = options.RegistryOptions{ + AllowInsecure: true, + } + if useOCI11 { + so.RegistryExperimental = options.RegistryExperimentalOptions{ + RegistryReferrersMode: options.RegistryReferrersModeOCI11, + } + } + must(sign.SignCmd(ro, ko, so, []string{imgName}), t) + mustErr(verify(pubKey, imgName, true, nil, "", false), t) + cmd := cliverify.VerifyCommand{ + KeyRef: pubKey, + CheckClaims: true, + RegistryOptions: options.RegistryOptions{ + AllowInsecure: true, + }, + } + if useOCI11 { + cmd.ExperimentalOCI11 = true + } + must(cmd.Exec(context.Background(), []string{imgName}), t) +} + +func makeImageIndexWithInsecureRegistry(t *testing.T, n string) func() { + ref, err := name.ParseReference(n, name.WeakValidation) + if err != nil { + t.Fatal(err) + } + index, err := random.Index(512, 1, 0) + if err != nil { + t.Fatal(err) + } + regClientOpts := registryClientOpts(context.Background()) + // Add TLS config to allow us to push the image to the insecure registry + insecureTransport := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + } + regClientOpts = append(regClientOpts, remote.WithTransport(insecureTransport)) + if err := remote.WriteIndex(ref, index, regClientOpts...); err != nil { + t.Fatal(err) + } + remoteImage, err := remote.Get(ref, regClientOpts...) + if err != nil { + t.Fatal(err) + } + cleanup := func() { + _ = remote.Delete(ref, regClientOpts...) + ref, _ := ociremote.SignatureTag(ref.Context().Digest(remoteImage.Descriptor.Digest.String()), ociremote.WithRemoteOptions(regClientOpts...)) + _ = remote.Delete(ref, regClientOpts...) + } + return cleanup +} diff --git a/test/e2e_kms_test.go b/test/e2e_kms_test.go new file mode 100644 index 00000000000..0050c2a39d4 --- /dev/null +++ b/test/e2e_kms_test.go @@ -0,0 +1,98 @@ +// Copyright 2024 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build e2e && kms + +package test + +import ( + "context" + "os" + "path" + "testing" + + "github.com/sigstore/cosign/v2/cmd/cosign/cli/generate" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" + "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" + "github.com/sigstore/cosign/v2/pkg/cosign/env" + _ "github.com/sigstore/sigstore/pkg/signature/kms/hashivault" +) + +const ( + rekorURLVar = "REKOR_URL" + testKMSVar = "TEST_KMS" + defaultKMS = "hashivault://transit" +) + +func TestSecretsKMS(t *testing.T) { + ctx := context.Background() + + repo, stop := reg(t) + defer stop() + td := t.TempDir() + + imgName := path.Join(repo, "cosign-kms-e2e") + _, _, cleanup := mkimage(t, imgName) + defer cleanup() + + kms := os.Getenv(testKMSVar) + if kms == "" { + kms = defaultKMS + } + + prefix := path.Join(td, "test-kms") + + must(generate.GenerateKeyPairCmd(ctx, kms, prefix, nil), t) + + pubKey := prefix + ".pub" + privKey := kms + + // Verify should fail at first + mustErr(verify(pubKey, imgName, true, nil, "", false), t) + + rekorURL := os.Getenv(rekorURLVar) + + must(downloadAndSetEnv(t, rekorURL+"/api/v1/log/publicKey", env.VariableSigstoreRekorPublicKey.String(), td), t) + + // Now sign and verify with the KMS key + ko := options.KeyOpts{ + KeyRef: privKey, + RekorURL: rekorURL, + SkipConfirmation: true, + } + so := options.SignOptions{ + Upload: true, + TlogUpload: true, + } + must(sign.SignCmd(ro, ko, so, []string{imgName}), t) + must(verify(pubKey, imgName, true, nil, "", false), t) + + // Sign and verify with annotations + mustErr(verify(pubKey, imgName, true, map[string]any{"foo": "bar"}, "", false), t) + soAnno := options.SignOptions{ + Upload: true, + TlogUpload: true, + AnnotationOptions: options.AnnotationOptions{ + Annotations: []string{"foo=bar"}, + }, + } + must(sign.SignCmd(ro, ko, soAnno, []string{imgName}), t) + must(verify(pubKey, imgName, true, map[string]any{"foo": "bar"}, "", false), t) + + // Store signatures in a different repo + t.Setenv("COSIGN_REPOSITORY", path.Join(repo, "subbedrepo")) + must(sign.SignCmd(ro, ko, so, []string{imgName}), t) + must(verify(pubKey, imgName, true, nil, "", false), t) + os.Unsetenv("COSIGN_REPOSITORY") +} diff --git a/test/e2e_test.go b/test/e2e_test.go index abbb5a3df04..230531bf1f0 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build e2e && !cross +//go:build e2e && !cross && !kms && !registry package test @@ -123,6 +123,67 @@ func TestSignVerify(t *testing.T) { mustErr(verify(pubKeyPath, imgName, true, map[string]interface{}{"foo": "bar", "baz": "bat"}, "", false), t) } +func TestSignVerifyCertBundle(t *testing.T) { + td := t.TempDir() + err := downloadAndSetEnv(t, rekorURL+"/api/v1/log/publicKey", env.VariableSigstoreRekorPublicKey.String(), td) + if err != nil { + t.Fatal(err) + } + + repo, stop := reg(t) + defer stop() + + imgName := path.Join(repo, "cosign-e2e") + + _, _, cleanup := mkimage(t, imgName) + defer cleanup() + + _, privKeyPath, pubKeyPath := keypair(t, td) + caCertFile, _ /* caPrivKeyFile */, caIntermediateCertFile, _ /* caIntermediatePrivKeyFile */, certFile, certChainFile, err := generateCertificateBundleFiles(td, true, "foobar") + must(err, t) + + ctx := context.Background() + // Verify should fail at first + mustErr(verifyCertBundle(pubKeyPath, caCertFile, caIntermediateCertFile, imgName, true, nil, "", true), t) + // So should download + mustErr(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t) + + // Now sign the image + ko := options.KeyOpts{ + KeyRef: privKeyPath, + PassFunc: passFunc, + RekorURL: rekorURL, + SkipConfirmation: true, + } + so := options.SignOptions{ + Upload: true, + TlogUpload: true, + } + must(sign.SignCmd(ro, ko, so, []string{imgName}), t) + + // Now verify and download should work! + ignoreTlog := true + must(verifyCertBundle(pubKeyPath, caCertFile, caIntermediateCertFile, imgName, true, nil, "", ignoreTlog), t) + // verification with certificate chain instead of root/intermediate files should work as well + must(verifyCertChain(pubKeyPath, certChainFile, certFile, imgName, true, nil, "", ignoreTlog), t) + must(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t) + + // Look for a specific annotation + mustErr(verifyCertBundle(pubKeyPath, caCertFile, caIntermediateCertFile, imgName, true, map[string]interface{}{"foo": "bar"}, "", ignoreTlog), t) + + so.AnnotationOptions = options.AnnotationOptions{ + Annotations: []string{"foo=bar"}, + } + // Sign the image with an annotation + must(sign.SignCmd(ro, ko, so, []string{imgName}), t) + + // It should match this time. + must(verifyCertBundle(pubKeyPath, caCertFile, caIntermediateCertFile, imgName, true, map[string]interface{}{"foo": "bar"}, "", ignoreTlog), t) + + // But two doesn't work + mustErr(verifyCertBundle(pubKeyPath, caCertFile, caIntermediateCertFile, imgName, true, map[string]interface{}{"foo": "bar", "baz": "bat"}, "", ignoreTlog), t) +} + func TestSignVerifyClean(t *testing.T) { td := t.TempDir() err := downloadAndSetEnv(t, rekorURL+"/api/v1/log/publicKey", env.VariableSigstoreRekorPublicKey.String(), td) @@ -179,7 +240,7 @@ func TestImportSignVerifyClean(t *testing.T) { _, _, _ = mkimage(t, imgName) - _, privKeyPath, pubKeyPath := importKeyPair(t, td) + _, privKeyPath, pubKeyPath := importSampleKeyPair(t, td) ctx := context.Background() @@ -548,7 +609,7 @@ func TestAttestationDownloadWithBadPredicateType(t *testing.T) { } must(attestCommand.Exec(ctx, imgName), t) - // Call download.AttestationCmd() to ensure failure with non-existant --predicate-type + // Call download.AttestationCmd() to ensure failure with non-existent --predicate-type attOpts := options.AttestationDownloadOptions{ PredicateType: "vuln", } @@ -791,6 +852,246 @@ func TestAttestationRFC3161Timestamp(t *testing.T) { must(verifyAttestation.Exec(ctx, []string{imgName}), t) } +func TestVerifyWithCARoots(t *testing.T) { + ctx := context.Background() + // TSA server needed to create timestamp + viper.Set("timestamp-signer", "memory") + viper.Set("timestamp-signer-hash", "sha256") + apiServer := server.NewRestAPIServer("localhost", 0, []string{"http"}, false, 10*time.Second, 10*time.Second) + server := httptest.NewServer(apiServer.GetHandler()) + t.Cleanup(server.Close) + + repo, stop := reg(t) + defer stop() + td := t.TempDir() + + imgName := path.Join(repo, "cosign-verify-caroots-e2e") + _, _, cleanup := mkimage(t, imgName) + defer cleanup() + blob := "someblob2sign" + + b := bytes.Buffer{} + blobRef := filepath.Join(td, blob) + if err := os.WriteFile(blobRef, []byte(blob), 0644); err != nil { + t.Fatal(err) + } + must(generate.GenerateCmd(context.Background(), options.RegistryOptions{}, imgName, nil, &b), t) + + rootCert, rootKey, _ := GenerateRootCa() + subCert, subKey, _ := GenerateSubordinateCa(rootCert, rootKey) + leafCert, privKey, _ := GenerateLeafCert("subject@mail.com", "oidc-issuer", subCert, subKey) + privKeyRef := importECDSAPrivateKey(t, privKey, td, "cosign-test-key.pem") + pemRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Raw}) + pemSub := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: subCert.Raw}) + pemLeaf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert.Raw}) + + rootCert02, rootKey02, _ := GenerateRootCa() + subCert02, subKey02, _ := GenerateSubordinateCa(rootCert02, rootKey02) + leafCert02, _, _ := GenerateLeafCert("subject02@mail.com", "oidc-issuer02", subCert02, subKey02) + pemRoot02 := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rootCert02.Raw}) + pemSub02 := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: subCert02.Raw}) + pemLeaf02 := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert02.Raw}) + pemsubRef02 := mkfile(string(pemSub02), td, t) + pemrootRef02 := mkfile(string(pemRoot02), td, t) + pemleafRef02 := mkfile(string(pemLeaf02), td, t) + + rootPool := x509.NewCertPool() + rootPool.AddCert(rootCert) + + payloadref := mkfile(b.String(), td, t) + + h := sha256.Sum256(b.Bytes()) + signature, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256) + b64signature := base64.StdEncoding.EncodeToString(signature) + sigRef := mkfile(b64signature, td, t) + pemsubRef := mkfile(string(pemSub), td, t) + pemrootRef := mkfile(string(pemRoot), td, t) + pemleafRef := mkfile(string(pemLeaf), td, t) + certchainRef := mkfile(string(append(pemSub, pemRoot...)), td, t) + + pemrootBundleRef := mkfile(string(append(pemRoot, pemRoot02...)), td, t) + pemsubBundleRef := mkfile(string(append(pemSub, pemSub02...)), td, t) + + tsclient, err := tsaclient.GetTimestampClient(server.URL) + if err != nil { + t.Error(err) + } + + chain, err := tsclient.Timestamp.GetTimestampCertChain(nil) + if err != nil { + t.Fatalf("unexpected error getting timestamp chain: %v", err) + } + + tsaChainRef, err := os.CreateTemp(os.TempDir(), "tempfile") + if err != nil { + t.Fatalf("error creating temp file: %v", err) + } + defer os.Remove(tsaChainRef.Name()) + _, err = tsaChainRef.WriteString(chain.Payload) + if err != nil { + t.Fatalf("error writing chain payload to temp file: %v", err) + } + + tsBytes, err := tsa.GetTimestampedSignature(signature, client.NewTSAClient(server.URL+"/api/v1/timestamp")) + if err != nil { + t.Fatalf("unexpected error creating timestamp: %v", err) + } + rfc3161TSRef := mkfile(string(tsBytes), td, t) + + // Upload it! + err = attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadref, pemleafRef, certchainRef, rfc3161TSRef, "", imgName) + if err != nil { + t.Fatal(err) + } + + // Now sign the blob with one key + ko := options.KeyOpts{ + KeyRef: privKeyRef, + PassFunc: passFunc, + } + blobSig, err := sign.SignBlobCmd(ro, ko, blobRef, true, "", "", false) + if err != nil { + t.Fatal(err) + } + // the following fields with non-changing values are logically "factored out" for brevity + // and passed to verifyKeylessTSAWithCARoots in the testing loop: + // imageName string + // tsaCertChainRef string + // skipSCT bool + // skipTlogVerify bool + tests := []struct { + name string + rootRef string + subRef string + leafRef string + skipBlob bool // skip the verify-blob test (for cases that need the image) + wantError bool + }{ + { + "verify with root, intermediate and leaf certificates", + pemrootRef, + pemsubRef, + pemleafRef, + false, + false, + }, + // NB - "confusely" switching the root and intermediate PEM files does _NOT_ (currently) produce an error + // - the Go crypto/x509 package doesn't strictly verify that the certificate chain is anchored + // in a self-signed root certificate. In this case, only the chain up to the intermediate + // certificate is verified, and the root certificate is ignored. + // See also https://gist.github.com/dmitris/15160f703b3038b1b00d03d3c7b66ce0 and in particular + // https://gist.github.com/dmitris/15160f703b3038b1b00d03d3c7b66ce0#file-main-go-L133-L135 as an example. + { + "switch root and intermediate no error", + pemsubRef, + pemrootRef, + pemleafRef, + false, + false, + }, + { + "leave out the root certificate", + "", + pemsubRef, + pemleafRef, + false, + true, + }, + { + "leave out the intermediate certificate", + pemrootRef, + "", + pemleafRef, + false, + true, + }, + { + "leave out the codesigning leaf certificate which is extracted from the image", + pemrootRef, + pemsubRef, + "", + true, + false, + }, + { + "wrong leaf certificate", + pemrootRef, + pemsubRef, + pemleafRef02, + false, + true, + }, + { + "root and intermediates bundles", + pemrootBundleRef, + pemsubBundleRef, + pemleafRef, + false, + false, + }, + { + "wrong root and intermediates bundles", + pemrootRef02, + pemsubRef02, + pemleafRef, + false, + true, + }, + { + "wrong root bundle", + pemrootRef02, + pemsubBundleRef, + pemleafRef, + false, + true, + }, + { + "wrong intermediates bundle", + pemrootRef, + pemsubRef02, + pemleafRef, + false, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := verifyKeylessTSAWithCARoots(imgName, + tt.rootRef, + tt.subRef, + tt.leafRef, + tsaChainRef.Name(), + true, + true) + hasErr := (err != nil) + if hasErr != tt.wantError { + if tt.wantError { + t.Errorf("%s - no expected error", tt.name) + } else { + t.Errorf("%s - unexpected error: %v", tt.name, err) + } + } + if !tt.skipBlob { + err = verifyBlobKeylessWithCARoots(blobRef, + string(blobSig), + tt.rootRef, + tt.subRef, + tt.leafRef, + true, + true) + hasErr = (err != nil) + if hasErr != tt.wantError { + if tt.wantError { + t.Errorf("%s - no expected error", tt.name) + } else { + t.Errorf("%s - unexpected error: %v", tt.name, err) + } + } + } + }) + } +} + func TestRekorBundle(t *testing.T) { td := t.TempDir() err := downloadAndSetEnv(t, rekorURL+"/api/v1/log/publicKey", env.VariableSigstoreRekorPublicKey.String(), td) @@ -1311,6 +1612,50 @@ func TestSignBlobBundle(t *testing.T) { must(verifyBlobCmd.Exec(ctx, bp), t) } +func TestSignBlobNewBundle(t *testing.T) { + td1 := t.TempDir() + + blob := "someblob" + blobPath := filepath.Join(td1, blob) + if err := os.WriteFile(blobPath, []byte(blob), 0644); err != nil { + t.Fatal(err) + } + + bundlePath := filepath.Join(td1, "bundle.sigstore.json") + + ctx := context.Background() + _, privKeyPath, pubKeyPath := keypair(t, td1) + + ko1 := options.KeyOpts{ + KeyRef: pubKeyPath, + BundlePath: bundlePath, + NewBundleFormat: true, + } + + verifyBlobCmd := cliverify.VerifyBlobCmd{ + KeyOpts: ko1, + IgnoreTlog: true, + } + + // Verify should fail before bundle is written + mustErr(verifyBlobCmd.Exec(ctx, blobPath), t) + + // Produce signed bundle + ko := options.KeyOpts{ + KeyRef: privKeyPath, + PassFunc: passFunc, + BundlePath: bundlePath, + NewBundleFormat: true, + } + + if _, err := sign.SignBlobCmd(ro, ko, blobPath, true, "", "", false); err != nil { + t.Fatal(err) + } + + // Verify should succeed now that bundle is written + must(verifyBlobCmd.Exec(ctx, blobPath), t) +} + func TestSignBlobRFC3161TimestampBundle(t *testing.T) { td := t.TempDir() err := downloadAndSetEnv(t, rekorURL+"/api/v1/log/publicKey", env.VariableSigstoreRekorPublicKey.String(), td) @@ -1921,6 +2266,8 @@ func TestOffline(t *testing.T) { must(err, t) sigsTag, err := ociremote.SignatureTag(imgRef) + must(err, t) + if err := remote.Delete(sigsTag); err != nil { t.Fatal(err) } @@ -2201,6 +2548,7 @@ func getOIDCToken() (string, error) { } return string(body), nil } +<<<<<<< HEAD func setLocalEnv(t *testing.T, dir string) error { // fulcio repo is downloaded to the user's home directory by e2e_test.sh @@ -2238,3 +2586,5 @@ func downloadAndSetEnv(t *testing.T, url, envVar, dir string) error { t.Setenv(envVar, f.Name()) return nil } +======= +>>>>>>> v2.4.0 diff --git a/test/e2e_test.sh b/test/e2e_test.sh index 3c60c76da5e..28eefea0450 100755 --- a/test/e2e_test.sh +++ b/test/e2e_test.sh @@ -16,10 +16,15 @@ set -ex +docker_compose="docker compose" +if ! ${docker_compose} version >/dev/null 2>&1; then + docker_compose="docker-compose" +fi + echo "setting up OIDC provider" pushd ./test/fakeoidc oidcimg=$(ko build main.go --local) -docker network ls | grep fulcio_default || docker network create fulcio_default +docker network ls | grep fulcio_default || docker network create fulcio_default --label "com.docker.compose.network=fulcio_default" docker run -d --rm -p 8080:8080 --network fulcio_default --name fakeoidc $oidcimg cleanup_oidc() { echo "cleaning up oidc" @@ -59,10 +64,14 @@ export FULCIO_METRICS_PORT=2113 export FULCIO_CONFIG=/tmp/fulcio-config.json for repo in rekor fulcio; do pushd $repo - docker-compose up -d + if [ "$repo" == "fulcio" ]; then + yq -i e '.networks={"default":{ "name":"fulcio_default","external":true }}' docker-compose.yml + yq -i e '.services.fulcio-server.networks=["default"]' docker-compose.yml + fi + ${docker_compose} up -d echo -n "waiting up to 60 sec for system to start" count=0 - until [ $(docker-compose ps | grep -c "(healthy)") == 3 ]; + until [ $(${docker_compose} ps | grep -c "(healthy)") == 3 ]; do if [ $count -eq 6 ]; then echo "! timeout reached" @@ -80,7 +89,7 @@ cleanup_services() { cleanup_oidc for repo in rekor fulcio; do pushd $HOME/$repo - docker-compose down + ${docker_compose} down popd done } diff --git a/test/e2e_test_insecure_oci_registry.sh b/test/e2e_test_insecure_oci_registry.sh deleted file mode 100755 index 02ec2818585..00000000000 --- a/test/e2e_test_insecure_oci_registry.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright 2023 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -o errexit -set -o nounset -set -o pipefail - -go build -o cosign ./cmd/cosign -tmp=$(mktemp -d) -cp cosign $tmp/ - -INSECURE_REGISTRY_NAME=${INSECURE_OCI_REGISTRY_NAME:-insecure-oci-registry.notlocal} -INSECURE_REGISTRY_PORT=${INSECURE_OCI_REGISTRY_PORT:-5002} - -pushd $tmp - -pass="$RANDOM" -export COSIGN_PASSWORD=$pass -export COSIGN_YES="true" -export COSIGN_EXPERIMENTAL=1 - -./cosign generate-key-pair -signing_key=cosign.key -verification_key=cosign.pub - -img="${INSECURE_REGISTRY_NAME}:${INSECURE_REGISTRY_PORT}/test" -(crane delete $(./cosign triangulate $img)) || true -crane cp ghcr.io/distroless/static $img --insecure - -# Operations with insecure registries should fail by default, then succeed -# with `--allow-insecure-registry` -if (./cosign sign --key ${signing_key} $img); then false; fi -./cosign sign --allow-insecure-registry --registry-referrers-mode=oci-1-1 --key ${signing_key} $img -if (./cosign verify --key ${verification_key} $img); then false; fi -./cosign verify --allow-insecure-registry --experimental-oci11=true --key ${verification_key} $img - -echo "SUCCESS" diff --git a/test/e2e_test_insecure_registry.sh b/test/e2e_test_insecure_registry.sh deleted file mode 100755 index 33631232bfc..00000000000 --- a/test/e2e_test_insecure_registry.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -ex - -go build -o cosign ./cmd/cosign -tmp=$(mktemp -d) -cp cosign $tmp/ - -INSECURE_REGISTRY_NAME=${INSECURE_REGISTRY_NAME:-insecure-registry.notlocal} -INSECURE_REGISTRY_PORT=${INSECURE_REGISTRY_PORT:-5001} - -pushd $tmp - -pass="$RANDOM" -export COSIGN_PASSWORD=$pass -export COSIGN_YES="true" - -./cosign generate-key-pair -signing_key=cosign.key -verification_key=cosign.pub - -img="${INSECURE_REGISTRY_NAME}:${INSECURE_REGISTRY_PORT}/test" -(crane delete $(./cosign triangulate $img)) || true -crane cp ghcr.io/distroless/static $img --insecure - -# Operations with insecure registries should fail by default, then succeed -# with `--allow-insecure-registry` -if (./cosign sign --key ${signing_key} $img); then false; fi -./cosign sign --allow-insecure-registry --key ${signing_key} $img -if (./cosign verify --key ${verification_key} $img); then false; fi -./cosign verify --allow-insecure-registry --key ${verification_key} $img - -echo "SUCCESS" diff --git a/test/e2e_test_pkcs11.sh b/test/e2e_test_pkcs11.sh index 7ea933513fa..703ce7c7beb 100755 --- a/test/e2e_test_pkcs11.sh +++ b/test/e2e_test_pkcs11.sh @@ -19,14 +19,19 @@ set -o nounset set -o pipefail # Test pkcs11 token signing -CONTAINER_ID=$(docker run -dit --name softhsm -v $(pwd):/root/cosign -p 2345:2345 vegardit/softhsm2-pkcs11-proxy@sha256:557a65d2a14e3986f2389d36ddce75609cbd8fb7ee6cf08a78adcc8236c2a80e) +# using a fork of https://github.com/vegardit/docker-softhsm2-pkcs11-proxy that stopped to build 5 months ago +CONTAINER_ID=$(docker run -dit --name softhsm -v $(pwd):/root/cosign -p 2345:2345 ghcr.io/cpanato/softhsm2-pkcs11-proxy:latest@sha256:2614345f73f9432d85365f0ac450c4bf0abac51b205b54241a94f9cf9e671772) docker exec -i $CONTAINER_ID /bin/bash << 'EOF' +# to install the latest go that is not available in the alpine repository +echo "@edge http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories apk update # add make pcsc-lite-libs go command -apk add make build-base go +apk add make build-base + +apk add go@edge cd /root/cosign diff --git a/test/e2e_test_secrets_kms.sh b/test/e2e_test_secrets_kms.sh deleted file mode 100755 index ca540cc2763..00000000000 --- a/test/e2e_test_secrets_kms.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright 2022 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -ex - -go build -o cosign ./cmd/cosign -tmp=$(mktemp -d -t cosign-e2e-secrets.XXXXXX) -cp cosign $tmp/ - -cd $tmp - -pass="$RANDOM" -export COSIGN_PASSWORD=$pass - -BASE_TEST_REPO=${BASE_TEST_REPO:-ttl.sh/cosign-ci} -TEST_INSTANCE_REPO="${BASE_TEST_REPO}/$(date +'%Y/%m/%d')/$RANDOM" - -img="${TEST_INSTANCE_REPO}/test-${RANDOM}" -crane cp busybox "${img}" - -## KMS using env variables! -TEST_KMS=${TEST_KMS:-hashivault://transit} -(crane delete $(./cosign triangulate $img)) || true -COSIGN_KMS=$TEST_KMS ./cosign generate-key-pair -signing_key=$TEST_KMS -verification_key=cosign.pub - -if (./cosign verify --key ${verification_key} $img); then false; fi -COSIGN_KEY=${signing_key} ./cosign sign --tlog-upload=true $img -COSIGN_KEY=${verification_key} ./cosign verify $img - -if (./cosign verify -a foo=bar --key ${verification_key} $img); then false; fi -COSIGN_KEY=${signing_key} ./cosign sign -a foo=bar --tlog-upload=true $img -COSIGN_KEY=${verification_key} ./cosign verify -a foo=bar $img - -# store signatures in a different repo -export COSIGN_REPOSITORY=${TEST_INSTANCE_REPO}/subbedrepo -(crane delete $(./cosign triangulate $img)) || true -COSIGN_KEY=${signing_key} ./cosign sign --tlog-upload=true $img -COSIGN_KEY=${verification_key} ./cosign verify $img -unset COSIGN_REPOSITORY - -# test stdin interaction for private key password -stdin_password=${COSIGN_PASSWORD} -unset COSIGN_PASSWORD -(crane delete $(./cosign triangulate $img)) || true -echo $stdin_password | ./cosign sign --key ${signing_key} --output-signature interactive.sig --output-payload interactive.payload $img -COSIGN_KEY=${verification_key} COSIGN_SIGNATURE=interactive.sig ./cosign verify --payload interactive.payload $img -export COSIGN_PASSWORD=${stdin_password} - -# What else needs auth? -echo "SUCCESS" diff --git a/test/helpers.go b/test/helpers.go index 86f7cdd9296..2db7092674f 100644 --- a/test/helpers.go +++ b/test/helpers.go @@ -18,13 +18,28 @@ package test import ( + "bytes" "context" "crypto" + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "io" + "log" + "math/big" + "net" + "net/http" "net/http/httptest" "net/url" "os" + "path" "path/filepath" "testing" + "time" "github.com/google/go-cmp/cmp" "github.com/google/go-containerregistry/pkg/authn" @@ -32,6 +47,7 @@ import ( "github.com/google/go-containerregistry/pkg/registry" "github.com/google/go-containerregistry/pkg/v1/random" "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/secure-systems-lab/go-securesystemslib/encrypted" // Initialize all known client auth plugins _ "k8s.io/client-go/plugin/pkg/client/auth" @@ -39,6 +55,7 @@ import ( "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" cliverify "github.com/sigstore/cosign/v2/cmd/cosign/cli/verify" "github.com/sigstore/cosign/v2/pkg/cosign" + "github.com/sigstore/cosign/v2/pkg/cosign/env" ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote" sigs "github.com/sigstore/cosign/v2/pkg/signature" ) @@ -72,6 +89,50 @@ var verify = func(keyRef, imageRef string, checkClaims bool, annotations map[str return cmd.Exec(context.Background(), args) } +var verifyCertChain = func(keyRef, certChain, certFile, imageRef string, checkClaims bool, annotations map[string]interface{}, attachment string, skipTlogVerify bool) error { + cmd := cliverify.VerifyCommand{ + KeyRef: keyRef, + RekorURL: rekorURL, + CheckClaims: checkClaims, + Annotations: sigs.AnnotationsMap{Annotations: annotations}, + Attachment: attachment, + HashAlgorithm: crypto.SHA256, + MaxWorkers: 10, + IgnoreTlog: skipTlogVerify, + CertVerifyOptions: options.CertVerifyOptions{ + Cert: certFile, + CertChain: certChain, + }, + } + + args := []string{imageRef} + + return cmd.Exec(context.Background(), args) +} + +var verifyCertBundle = func(keyRef, caCertFile, caIntermediateCertFile, imageRef string, checkClaims bool, annotations map[string]interface{}, attachment string, skipTlogVerify bool) error { + cmd := cliverify.VerifyCommand{ + KeyRef: keyRef, + RekorURL: rekorURL, + CheckClaims: checkClaims, + Annotations: sigs.AnnotationsMap{Annotations: annotations}, + Attachment: attachment, + HashAlgorithm: crypto.SHA256, + MaxWorkers: 10, + IgnoreTlog: skipTlogVerify, + CertVerifyOptions: options.CertVerifyOptions{ + CAIntermediates: caIntermediateCertFile, + CARoots: caCertFile, + CertOidcIssuerRegexp: ".*", + CertIdentityRegexp: ".*", + }, + } + + args := []string{imageRef} + + return cmd.Exec(context.Background(), args) +} + var verifyTSA = func(keyRef, imageRef string, checkClaims bool, annotations map[string]interface{}, attachment, tsaCertChain string, skipTlogVerify bool) error { cmd := cliverify.VerifyCommand{ KeyRef: keyRef, @@ -90,7 +151,7 @@ var verifyTSA = func(keyRef, imageRef string, checkClaims bool, annotations map[ return cmd.Exec(context.Background(), args) } -var verifyKeylessTSA = func(imageRef string, tsaCertChain string, skipSCT bool, skipTlogVerify bool) error { +var verifyKeylessTSA = func(imageRef string, tsaCertChain string, skipSCT bool, skipTlogVerify bool) error { //nolint: unused cmd := cliverify.VerifyCommand{ CertVerifyOptions: options.CertVerifyOptions{ CertOidcIssuerRegexp: ".*", @@ -109,6 +170,55 @@ var verifyKeylessTSA = func(imageRef string, tsaCertChain string, skipSCT bool, return cmd.Exec(context.Background(), args) } +var verifyKeylessTSAWithCARoots = func(imageRef string, + caroots string, // filename of a PEM file with CA Roots certificates + intermediates string, // empty or filename of a PEM file with Intermediate certificates + certFile string, // filename of a PEM file with the codesigning certificate + tsaCertChain string, + skipSCT bool, + skipTlogVerify bool) error { + cmd := cliverify.VerifyCommand{ + CertVerifyOptions: options.CertVerifyOptions{ + CertOidcIssuerRegexp: ".*", + CertIdentityRegexp: ".*", + }, + CertRef: certFile, + CARoots: caroots, + CAIntermediates: intermediates, + RekorURL: rekorURL, + HashAlgorithm: crypto.SHA256, + TSACertChainPath: tsaCertChain, + IgnoreSCT: skipSCT, + IgnoreTlog: skipTlogVerify, + MaxWorkers: 10, + } + args := []string{imageRef} + + return cmd.Exec(context.Background(), args) +} + +var verifyBlobKeylessWithCARoots = func(blobRef string, + sig string, + caroots string, // filename of a PEM file with CA Roots certificates + intermediates string, // empty or filename of a PEM file with Intermediate certificates + certFile string, // filename of a PEM file with the codesigning certificate + skipSCT bool, + skipTlogVerify bool) error { + cmd := cliverify.VerifyBlobCmd{ + CertVerifyOptions: options.CertVerifyOptions{ + CertOidcIssuerRegexp: ".*", + CertIdentityRegexp: ".*", + }, + SigRef: sig, + CertRef: certFile, + CARoots: caroots, + CAIntermediates: intermediates, + IgnoreSCT: skipSCT, + IgnoreTlog: skipTlogVerify, + } + return cmd.Exec(context.Background(), blobRef) +} + // Used to verify local images stored on disk var verifyLocal = func(keyRef, path string, checkClaims bool, annotations map[string]interface{}, attachment string) error { cmd := cliverify.VerifyCommand{ @@ -155,7 +265,7 @@ func keypair(t *testing.T, td string) (*cosign.KeysBytes, string, string) { t.Fatal(err) } defer func() { - os.Chdir(wd) + _ = os.Chdir(wd) }() keys, err := cosign.GenerateKeyPair(passFunc) if err != nil { @@ -174,8 +284,25 @@ func keypair(t *testing.T, td string) (*cosign.KeysBytes, string, string) { return keys, privKeyPath, pubKeyPath } -func importKeyPair(t *testing.T, td string) (*cosign.KeysBytes, string, string) { +// convert the given ecdsa.PrivateKey to a PEM encoded string, import into sigstore format, +// and write to the given file path. Returns the path to the imported key (/) +func importECDSAPrivateKey(t *testing.T, privKey *ecdsa.PrivateKey, td, fname string) string { + t.Helper() + x509Encoded, _ := x509.MarshalPKCS8PrivateKey(privKey) + encBytes, _ := encrypted.Encrypt(x509Encoded, keyPass) + keyPEM := pem.EncodeToMemory(&pem.Block{ + Type: cosign.CosignPrivateKeyPemType, + Bytes: encBytes}) + + cosignKeyPath := filepath.Join(td, fname) + if err := os.WriteFile(cosignKeyPath, keyPEM, 0600); err != nil { + t.Fatal(err) + } + return cosignKeyPath +} +func importSampleKeyPair(t *testing.T, td string) (*cosign.KeysBytes, string, string) { + //nolint: gosec const validrsa1 = `-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAx5piWVlE62NnZ0UzJ8Z6oKiKOC4dbOZ1HsNhIRtqkM+Oq4G+ 25yq6P+0JU/Qvr9veOGEb3R/J9u8JBo+hv2i5X8OtgvP2V2pi6f1s6vK7L0+6uRb @@ -212,7 +339,7 @@ qGzRVIDGbNkrVHM0IsAtHRpC0rYrtZY+9OwiraGcsqUMLwwQdCA= t.Fatal(err) } defer func() { - os.Chdir(wd) + _ = os.Chdir(wd) }() err = os.WriteFile("validrsa1.key", []byte(validrsa1), 0600) @@ -234,11 +361,11 @@ qGzRVIDGbNkrVHM0IsAtHRpC0rYrtZY+9OwiraGcsqUMLwwQdCA= if err := os.WriteFile(pubKeyPath, keys.PublicBytes, 0600); err != nil { t.Fatal(err) } - return keys, privKeyPath, pubKeyPath + return keys, privKeyPath, pubKeyPath } -func mockStdin(contents, td string, t *testing.T) func() { +func mockStdin(contents, td string, t *testing.T) func() { //nolint: unused origin := os.Stdin p := mkfile(contents, td, t) @@ -352,7 +479,7 @@ func equals(v1, v2 interface{}, t *testing.T) { } func reg(t *testing.T) (string, func()) { - repo := os.Getenv("COSIGN_TEST_REPO") + repo := os.Getenv("COSIGN_TEST_REPO") //nolint: forbidigo if repo != "" { return repo, func() {} } @@ -372,3 +499,272 @@ func registryClientOpts(ctx context.Context) []remote.Option { remote.WithContext(ctx), } } + +// setLocalEnv sets SIGSTORE_CT_LOG_PUBLIC_KEY_FILE, SIGSTORE_ROOT_FILE, and SIGSTORE_REKOR_PUBLIC_KEY for the locally running sigstore deployment. +func setLocalEnv(t *testing.T, dir string) error { + // fulcio repo is downloaded to the user's home directory by e2e_test.sh + home, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("error getting home directory: %w", err) + } + t.Setenv(env.VariableSigstoreCTLogPublicKeyFile.String(), path.Join(home, "fulcio/config/ctfe/pubkey.pem")) + err = downloadAndSetEnv(t, fulcioURL+"/api/v1/rootCert", env.VariableSigstoreRootFile.String(), dir) + if err != nil { + return fmt.Errorf("error setting %s env var: %w", env.VariableSigstoreRootFile.String(), err) + } + err = downloadAndSetEnv(t, rekorURL+"/api/v1/log/publicKey", env.VariableSigstoreRekorPublicKey.String(), dir) + if err != nil { + return fmt.Errorf("error setting %s env var: %w", env.VariableSigstoreRekorPublicKey.String(), err) + } + return nil +} + +// downloadAndSetEnv fetches a URL and sets the given environment variable to point to the downloaded file path. +func downloadAndSetEnv(t *testing.T, url, envVar, dir string) error { + resp, err := http.Get(url) //nolint: gosec + if err != nil { + return fmt.Errorf("error downloading file: %w", err) + } + defer resp.Body.Close() + f, err := os.CreateTemp(dir, "") + if err != nil { + return fmt.Errorf("error creating temp file: %w", err) + } + defer f.Close() + _, err = io.Copy(f, resp.Body) + if err != nil { + return fmt.Errorf("error writing to file: %w", err) + } + t.Setenv(envVar, f.Name()) + return nil +} + +func generateCertificateBundleFiles(td string, genIntermediate bool, outputSuffix string) ( + caCertFile string, + caPrivKeyFile string, + caIntermediateCertFile string, + caIntermediatePrivKeyFile string, + certFile string, + certChainFile string, + err error, +) { + caCertBuf, caPrivKeyBuf, caIntermediateCertBuf, caIntermediatePrivKeyBuf, certBuf, certChainBuf, err := generateCertificateBundle(genIntermediate) + if err != nil { + err = fmt.Errorf("error generating certificate bundle: %w", err) + return + } + caCertFile = filepath.Join(td, fmt.Sprintf("caCert%s.pem", outputSuffix)) + err = os.WriteFile(caCertFile, caCertBuf.Bytes(), 0600) + if err != nil { + err = fmt.Errorf("error writing caCert to file %s: %w", caCertFile, err) + return + } + caPrivKeyFile = filepath.Join(td, fmt.Sprintf("caPrivKey%s.pem", outputSuffix)) + err = os.WriteFile(caPrivKeyFile, caPrivKeyBuf.Bytes(), 0600) + if err != nil { + err = fmt.Errorf("error writing caPrivKey to file %s: %w", caPrivKeyFile, err) + return + } + if genIntermediate { + caIntermediateCertFile = filepath.Join(td, fmt.Sprintf("caIntermediateCert%s.pem", outputSuffix)) + err = os.WriteFile(caIntermediateCertFile, caIntermediateCertBuf.Bytes(), 0600) + if err != nil { + err = fmt.Errorf("error writing caIntermediateCert to file %s: %w", caIntermediateCertFile, err) + return + } + caIntermediatePrivKeyFile = filepath.Join(td, fmt.Sprintf("caIntermediatePrivKey%s.pem", outputSuffix)) + err = os.WriteFile(caIntermediatePrivKeyFile, caIntermediatePrivKeyBuf.Bytes(), 0600) + if err != nil { + err = fmt.Errorf("error writing caIntermediatePrivKey to file %s: %w", caIntermediatePrivKeyFile, err) + return + } + } + certFile = filepath.Join(td, fmt.Sprintf("cert%s.pem", outputSuffix)) + err = os.WriteFile(certFile, certBuf.Bytes(), 0600) + if err != nil { + err = fmt.Errorf("error writing cert to file %s: %w", certFile, err) + return + } + + // write the contents of certChainBuf to a file + certChainFile = filepath.Join(td, fmt.Sprintf("certchain%s.pem", outputSuffix)) + err = os.WriteFile(certChainFile, certChainBuf.Bytes(), 0600) + if err != nil { + err = fmt.Errorf("error writing certificate chain to file %s: %w", certFile, err) + return + } + return +} + +func generateCertificateBundle(genIntermediate bool) ( + caCertBuf *bytes.Buffer, + caPrivKeyBuf *bytes.Buffer, + caIntermediateCertBuf *bytes.Buffer, + caIntermediatePrivKeyBuf *bytes.Buffer, + certBuf *bytes.Buffer, + certBundleBuf *bytes.Buffer, + err error, //nolint: unparam +) { + // set up our CA certificate + ca := &x509.Certificate{ + SerialNumber: big.NewInt(2019), + Subject: pkix.Name{ + Organization: []string{"CA Company, INC."}, + OrganizationalUnit: []string{"CA Root Team"}, + Country: []string{"US"}, + Province: []string{""}, + Locality: []string{"San Francisco"}, + StreetAddress: []string{"Golden Gate Bridge"}, + PostalCode: []string{"94016"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + IsCA: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning /*, x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth */}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + EmailAddresses: []string{"ca@example.com"}, + } + + // create our private and public key + caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + log.Fatal(err) + } + // create the CA + caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey) + if err != nil { + log.Fatal(err) + } + + caCertBuf = &bytes.Buffer{} + err = pem.Encode(caCertBuf, &pem.Block{ + Type: "CERTIFICATE", + Bytes: caBytes, + }) + if err != nil { + log.Fatalf("unable to write PEM encode: %v", err) + } + + caPrivKeyBuf = &bytes.Buffer{} + err = pem.Encode(caPrivKeyBuf, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(caPrivKey), + }) + if err != nil { + log.Fatalf("unable to PEM encode private key to buffer: %v", err) //nolint:gocritic + } + + // generate intermediate CA if requested + var caIntermediate *x509.Certificate + var caIntermediatePrivKey *rsa.PrivateKey + if genIntermediate { + caIntermediate = &x509.Certificate{ + SerialNumber: big.NewInt(2019), + Subject: pkix.Name{ + Organization: []string{"CA Company, INC."}, + OrganizationalUnit: []string{"CA Intermediate Team"}, + Country: []string{"US"}, + Province: []string{""}, + Locality: []string{"San Francisco"}, + StreetAddress: []string{"Golden Gate Bridge"}, + PostalCode: []string{"94016"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + IsCA: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning /*, x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth */}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + EmailAddresses: []string{"ca@example.com"}, + } + // create our private and public key + caIntermediatePrivKey, err = rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + log.Fatal(err) + } + + // create the Intermediate CA + caIntermediateBytes, err := x509.CreateCertificate(rand.Reader, caIntermediate, ca, &caIntermediatePrivKey.PublicKey, caPrivKey) + if err != nil { + log.Fatal(err) + } + + caIntermediateCertBuf = &bytes.Buffer{} + err = pem.Encode(caIntermediateCertBuf, &pem.Block{ + Type: "CERTIFICATE", + Bytes: caIntermediateBytes, + }) + if err != nil { + log.Fatalf("unable to write to buffer: %v", err) + } + caIntermediatePrivKeyBuf = &bytes.Buffer{} + err = pem.Encode(caIntermediatePrivKeyBuf, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(caIntermediatePrivKey), + }) + if err != nil { + log.Fatalf("unable to PEM encode caIntermediatePrivKey: %v", err) + } + } + // set up our server certificate + cert := &x509.Certificate{ + SerialNumber: big.NewInt(2019), + Subject: pkix.Name{ + Organization: []string{"End User"}, + OrganizationalUnit: []string{"End Node Team"}, + Country: []string{"US"}, + Province: []string{""}, + Locality: []string{"San Francisco"}, + StreetAddress: []string{"Golden Gate Bridge"}, + PostalCode: []string{"94016"}, + }, + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback}, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning /* x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth */}, + KeyUsage: x509.KeyUsageDigitalSignature, + EmailAddresses: []string{"xyz@nosuchprovider.com"}, + DNSNames: []string{"next.hugeunicorn.xyz"}, + } + + certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + log.Fatal(err) + } + + var certBytes []byte + if !genIntermediate { + certBytes, err = x509.CreateCertificate(rand.Reader, cert, ca, &certPrivKey.PublicKey, caPrivKey) + } else { + certBytes, err = x509.CreateCertificate(rand.Reader, cert, caIntermediate, &caIntermediatePrivKey.PublicKey, caIntermediatePrivKey) + } + if err != nil { + log.Fatal(err) + } + + certBuf = &bytes.Buffer{} + err = pem.Encode(certBuf, &pem.Block{ + Type: "CERTIFICATE", + Bytes: certBytes, + }) + if err != nil { + log.Fatalf("failed to encode cert: %v", err) + } + + // concatenate into certChainBuf the contents of caIntermediateCertBuf and caCertBuf + certBundleBuf = &bytes.Buffer{} + if genIntermediate { + _, err = certBundleBuf.Write(caIntermediateCertBuf.Bytes()) + if err != nil { + log.Fatalf("failed to write caIntermediateCertBuf to certChainBuf: %v", err) + } + } + _, err = certBundleBuf.Write(caCertBuf.Bytes()) + if err != nil { + log.Fatalf("failed to write caCertBuf to certChainBuf: %v", err) + } + + return caCertBuf, caPrivKeyBuf, caIntermediateCertBuf, caIntermediatePrivKeyBuf, certBuf, certBundleBuf, nil +} diff --git a/test/helpers_test.go b/test/helpers_test.go new file mode 100644 index 00000000000..3ee522e8f43 --- /dev/null +++ b/test/helpers_test.go @@ -0,0 +1,174 @@ +// +// Copyright 2024 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build e2e + +package test + +import ( + "crypto/x509" + "encoding/pem" + "log" + "os" + "testing" +) + +func TestGenerateCertificateBundleFiles(t *testing.T) { + for _, tt := range []struct { + name string + genIntermediate bool + }{ + { + name: "without intermediate", + genIntermediate: false, + }, + { + name: "with intermediate", + genIntermediate: true, + }, + } { + t.Run(tt.name, func(t *testing.T) { + td := t.TempDir() + suffix := "foo" + caCertFile, caPrivKeyFile, caIntermediateCertFile, caIntermediatePrivKeyFile, + certFile, certChainFile, err := generateCertificateBundleFiles(td, true, suffix) + if err != nil { + t.Fatalf("Error generating certificate bundle: %v", err) + } + verifyCertificate(t, caCertFile) + if tt.genIntermediate { + verifyCertificate(t, caIntermediateCertFile) + } + verifyCertificate(t, certFile) + + verifyPrivateKey(t, caPrivKeyFile) + if tt.genIntermediate { + verifyPrivateKey(t, caIntermediatePrivKeyFile) + verifyCertificateChain(t, certChainFile) + } + }) + } +} + +func verifyCertificate(t *testing.T, certFile string) { + t.Helper() + // open and parse certFile, ensure it is a TLS certificate + data, err := os.ReadFile(certFile) + if err != nil { + t.Fatalf("Error reading certificate file %s: %v\n", certFile, err) + return + } + + // Check if the file contents are a PEM-encoded TLS certificate + if !isPEMEncodedCert(data) { + t.Fatalf("file %s doesn't contain a valid PEM-encoded TLS certificate", certFile) + } +} + +func verifyCertificateChain(t *testing.T, certChainFile string) { + t.Helper() + // open and parse certChainFile, ensure it is a TLS certificate chain + data, err := os.ReadFile(certChainFile) + if err != nil { + t.Fatalf("Error reading certificate file %s: %v\n", certChainFile, err) + } + + // Check if the file contents are a PEM-encoded TLS certificate + if !isPEMEncodedCertChain(data) { + t.Fatalf("file %s doesn't contain a valid PEM-encoded TLS certificate chain", certChainFile) + } +} + +// isPEMEncodedCert checks if the provided data is a PEM-encoded certificate +func isPEMEncodedCert(data []byte) bool { + // Decode the PEM data + block, _ := pem.Decode(data) + if block == nil || block.Type != "CERTIFICATE" { + return false + } + + // Parse the certificate to ensure it is valid + _, err := x509.ParseCertificate(block.Bytes) + return err == nil +} + +func verifyPrivateKey(t *testing.T, privKeyFile string) { + t.Helper() + // open and parse certFile, ensure it is a TLS certificate + data, err := os.ReadFile(privKeyFile) + if err != nil { + t.Fatalf("Error reading private key file %s: %v\n", privKeyFile, err) + return + } + + // Check if the file contents are a PEM-encoded private key + if !isPEMEncodedPrivateKey(data) { + t.Fatalf("file %s doesn't contain a valid PEM-encoded private key", privKeyFile) + } +} + +// isPEMEncodedPrivateKey checks if the provided data is a PEM-encoded private key +func isPEMEncodedPrivateKey(data []byte) bool { + // Decode the PEM data + block, _ := pem.Decode(data) + if block == nil { + return false + } + var err error + + switch block.Type { + case "PRIVATE KEY": + _, err = x509.ParsePKCS8PrivateKey(block.Bytes) + case "RSA PRIVATE KEY": + _, err = x509.ParsePKCS1PrivateKey(block.Bytes) + case "EC PRIVATE KEY": + _, err = x509.ParseECPrivateKey(block.Bytes) + default: + return false + } + if err != nil { + log.Printf("isPEMEncodedPrivateKey: %v", err) + return false + } + + return true +} + +// isPEMEncodedCertChain checks if the provided data is a concatenation of a PEM-encoded +// intermediate certificate followed by a root certificate +func isPEMEncodedCertChain(data []byte) bool { + // Decode the PEM blocks one by one + blockCnt := 0 + for len(data) > 0 { + var block *pem.Block + block, data = pem.Decode(data) + if block == nil { + break + } + if block.Type != "CERTIFICATE" { + return false + } + + // Parse the certificate to ensure it is valid + _, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return false + } + + blockCnt++ + } + // we want exactly two blocks in the certificate chain - intermediate and root + return blockCnt == 2 +} diff --git a/test/sign_blob_test.sh b/test/sign_blob_test.sh deleted file mode 100755 index a82601e89b2..00000000000 --- a/test/sign_blob_test.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This test checks that verify-blob will iterate over all entries and check for at least one valid entry before erroring out -# This is to prevent verify-blob from only checking the most recent entry, which could result -# in a "denial of service" type attack if someone signs a piece of software -# with their own certificate which doesn't chain up to Sigstore - -set -ex - -COSIGN_CLI=./cosign - -echo "Creating a unique blob" -BLOB=verify-experimental-blob -date > $BLOB -cat $BLOB - -echo "Sign the blob with cosign first and upload to rekor" -$COSIGN_CLI sign-blob --yes --output-certificate blob.cert --output-signature blob.sig $BLOB - -echo "Verifying ..." -$COSIGN_CLI verify-blob --signature blob.sig --cert blob.cert --certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*' $BLOB -echo "Verifying using cosign ENV variables..." -COSIGN_SIGNATURE=blob.sig COSIGN_CERTIFICATE=blob.cert $COSIGN_CLI verify-blob --certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*' $BLOB - -# Now, sign the blob with a self-signed certificate and upload to rekor -SIG_FILE=verify-experimental-signature -PRIV_KEY=./test/testdata/test_blob_private_key -PUB_KEY=./test/testdata/test_blob_public_key -CERT_FILE=./test/testdata/test_blob_cert.pem - -openssl dgst -sha256 -sign $PRIV_KEY -out $SIG_FILE $BLOB -openssl dgst -sha256 -verify $PUB_KEY -signature $SIG_FILE $BLOB - -SHA256HASH=$(sha256sum $BLOB | cut -f1 -d' ') - -SIGNATURE=$(cat $SIG_FILE | base64) -echo "Signature: $SIGNATURE" - -CERT=$(cat $CERT_FILE | base64) -echo "Cert: $CERT" - -JSON_BODY_FILE=verify-experimental-blob-http-body.json -cat < $JSON_BODY_FILE -{ - "apiVersion": "0.0.1", - "spec": { - "data": { - "hash": { - "algorithm": "sha256", - "value": "$SHA256HASH" - } - }, - "signature": { - "content": "$SIGNATURE", - "publicKey": { - "content": "$CERT" - } - } - }, - "kind": "hashedrekord" -} -EOF - -curl -X POST https://rekor.sigstore.dev/api/v1/log/entries -H 'Content-Type: application/json' -d @$JSON_BODY_FILE - -# Verifying should still work -echo "Verifying ..." -$COSIGN_CLI verify-blob --signature "$SIG_FILE" --cert "$CERT_FILE" --certificate-chain "$CERT_FILE" --insecure-ignore-sct --certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*' "$BLOB" - -echo "Verifying using cosign ENV variables ..." -COSIGN_SIGNATURE="$SIG_FILE" COSIGN_CERTIFICATE_CHAIN="$CERT_FILE" COSIGN_CERTIFICATE="$CERT_FILE" $COSIGN_CLI verify-blob --insecure-ignore-sct --certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*' "$BLOB" diff --git a/test/testdata/README.md b/test/testdata/README.md deleted file mode 100644 index 2469736fcf4..00000000000 --- a/test/testdata/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Generate Certificate - -If the `test/testdata/test_blob_cert.pem` expire you can generate a new certificate to use in the tests running the -following command: - -```shell -$ openssl req -key test/testdata/test_blob_private_key -x509 -days 3650 -out cert.pem -new -nodes -subj "/" -addext "subjectAltName = email:foo@example.com" -``` - -and then you replace the old `test/testdata/test_blob_cert.pem` with the new certificate. diff --git a/test/testdata/test_blob_cert.pem b/test/testdata/test_blob_cert.pem deleted file mode 100644 index 72d042b4983..00000000000 --- a/test/testdata/test_blob_cert.pem +++ /dev/null @@ -1,10 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBdDCCARqgAwIBAgIUZw7gQ6T/IgmiMD1AWB2OTIIVH1owCgYIKoZIzj0EAwIw -ADAeFw0yMjEyMjEwMDIwNThaFw0zMjEyMTgwMDIwNThaMAAwWTATBgcqhkjOPQIB -BggqhkjOPQMBBwNCAAR1Q4hB1jtagrdsVxygtDa/rli00U7n/1I/NSw8yoMRQ+MO -AjRhg3gtcV0tha34L6150qJirQHbfocsao8X6wFmo3IwcDAdBgNVHQ4EFgQUx3Wb -0LwCWoGsl0FUpeQb3M4MukkwHwYDVR0jBBgwFoAUx3Wb0LwCWoGsl0FUpeQb3M4M -ukkwEgYDVR0TAQH/BAgwBgEB/wIBATAaBgNVHREEEzARgQ9mb29AZXhhbXBsZS5j -b20wCgYIKoZIzj0EAwIDSAAwRQIhALXG7XS5TIFLp+jLSxjuRk1Tj5MfE+y9x92Z -YPMbi9GZAiAmfEe0+q5l3PnI6zliOG5kG6EcS80QQgQmPcFvRZWOvw== ------END CERTIFICATE----- diff --git a/test/testdata/test_blob_private_key b/test/testdata/test_blob_private_key deleted file mode 100644 index 149cd04cd04..00000000000 --- a/test/testdata/test_blob_private_key +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN EC PARAMETERS----- -BggqhkjOPQMBBw== ------END EC PARAMETERS----- ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIMJRdxVQ7AH6z1BlE9ucEMSAQhY431IFHe0oVCH7Iw49oAoGCCqGSM49 -AwEHoUQDQgAEdUOIQdY7WoK3bFccoLQ2v65YtNFO5/9SPzUsPMqDEUPjDgI0YYN4 -LXFdLYWt+C+tedKiYq0B236HLGqPF+sBZg== ------END EC PRIVATE KEY----- diff --git a/test/testdata/test_blob_public_key b/test/testdata/test_blob_public_key deleted file mode 100644 index 7a3ffe8e47b..00000000000 --- a/test/testdata/test_blob_public_key +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdUOIQdY7WoK3bFccoLQ2v65YtNFO -5/9SPzUsPMqDEUPjDgI0YYN4LXFdLYWt+C+tedKiYq0B236HLGqPF+sBZg== ------END PUBLIC KEY-----