pax_global_header00006660000000000000000000000064151116220550014510gustar00rootroot0000000000000052 comment=d9ed8f35825692b50c9cde6077002ac384d1eed7 golang-github-sigstore-rekor-tiles-2.0.1/000077500000000000000000000000001511162205500203325ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/.gitattributes000066400000000000000000000001551511162205500232260ustar00rootroot00000000000000/api/openapi/** linguist-generated /docs/** linguist-generated /pkg/generated/protobuf/** linguist-generated golang-github-sigstore-rekor-tiles-2.0.1/.github/000077500000000000000000000000001511162205500216725ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/.github/dependabot.yml000066400000000000000000000027411511162205500245260ustar00rootroot00000000000000# Copyright 2025 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. version: 2 updates: - package-ecosystem: "gomod" directory: "/" schedule: interval: "weekly" groups: go-patch-updates: update-types: - "patch" cooldown: default-days: 7 - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" groups: actions-patch-updates: update-types: - "patch" cooldown: default-days: 7 - package-ecosystem: "docker" directories: - "/" schedule: interval: "weekly" groups: docker-patch-updates: update-types: - "patch" cooldown: default-days: 7 - package-ecosystem: "docker-compose" directories: - "/" - "./tests/sharding/" schedule: interval: "weekly" groups: docker-compose-patch-updates: update-types: - "patch" cooldown: default-days: 7golang-github-sigstore-rekor-tiles-2.0.1/.github/workflows/000077500000000000000000000000001511162205500237275ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/.github/workflows/build_container.yml000066400000000000000000000136301511162205500276160ustar00rootroot00000000000000# Copyright 2025 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. # # From https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-docker-images#publishing-images-to-github-packages name: Create and publish Docker image on: push: branches: - main workflow_call: # Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds. env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} permissions: {} # Prevent concurrent runs of this workflow, but do not cancel any in-progress concurrency: group: "build-container" cancel-in-progress: false # There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu. jobs: build-and-push-image: name: Build and push Docker image runs-on: ubuntu-latest # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. permissions: contents: read packages: write attestations: write id-token: write steps: - name: Checkout repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here. - name: Log in to the Container registry uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} # This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels. - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} # Set up builder for multi-platform builds - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 with: cache-binary: false # avoid cache-poisoning attacks # Initialize build arguments - name: Set build arguments run: | LDFLAGS=$(make ldflags) echo "LDFLAGS=$LDFLAGS" >> $GITHUB_ENV # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages. # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see [Usage](https://github.com/docker/build-push-action#usage) in the README of the `docker/build-push-action` repository. # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. - name: Build and push per-commit Docker image if: ${{ !startsWith(github.ref, 'refs/tags/') }} id: push-per-commit uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: . push: true platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} build-args: | SERVER_LDFLAGS=${{ env.LDFLAGS }} - name: Build and push per-tag Docker image if: ${{ startsWith(github.ref, 'refs/tags/') }} id: push-per-tag uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: . file: Dockerfile.release push: true platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} build-args: | SERVER_LDFLAGS=${{ env.LDFLAGS }} # This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see [Using artifact attestations to establish provenance for builds](https://docs.github.com/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds). - name: Generate artifact attestation if: ${{ !startsWith(github.ref, 'refs/tags/') }} uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0 with: subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}} subject-digest: ${{ steps.push-per-commit.outputs.digest }} push-to-registry: true - name: Generate artifact attestation if: ${{ startsWith(github.ref, 'refs/tags/') }} uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0 with: subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}} subject-digest: ${{ steps.push-per-tag.outputs.digest }} push-to-registry: true golang-github-sigstore-rekor-tiles-2.0.1/.github/workflows/codeql.yml000066400000000000000000000032601511162205500257220ustar00rootroot00000000000000# Copyright 2025 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. # https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#changing-the-languages-that-are-analyzed name: CodeQL on: push: branches: [ main ] pull_request: # The branches below must be a subset of the branches above branches: [ main ] schedule: - cron: '45 10 * * 1' permissions: contents: read security-events: write jobs: analyze: name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: [ 'go' ] steps: - name: Checkout repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 with: languages: ${{ matrix.language }} build-mode: autobuild - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 golang-github-sigstore-rekor-tiles-2.0.1/.github/workflows/license_check.yml000066400000000000000000000026361511162205500272400ustar00rootroot00000000000000# Copyright 2025 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: License Check on: [pull_request] permissions: contents: read jobs: license-check: name: license boilerplate check runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version-file: './go.mod' check-latest: true - name: Check license headers run: | set -e go tool addlicense -check -l apache -c 'The Sigstore Authors' -ignore "third_party/**" -ignore "tests/testdata/**" -v * dependency-license-review: name: License and Vulnerability Scan uses: sigstore/community/.github/workflows/reusable-dependency-review.yml@9b1b5aca605f92ec5b1bf3681b1e61b3dbc420cc golang-github-sigstore-rekor-tiles-2.0.1/.github/workflows/lint.yml000066400000000000000000000043601511162205500254230ustar00rootroot00000000000000# Copyright 2025 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: Lint on: [push, pull_request] permissions: contents: read jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version-file: './go.mod' check-latest: true - name: "Extract golangci-lint version from Dockerfile.golangci-lint" id: "golangci-lint-version" run: | awk -F '[@:]' '/FROM golangci\/golangci-lint/{print "version="$2; exit}' Dockerfile.golangci-lint >> "$GITHUB_OUTPUT" - name: golangci-lint uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 with: version: ${{ steps.golangci-lint-version.outputs.version }} args: --timeout=5m --verbose shellcheck: name: shellcheck runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Run ShellCheck uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0 with: scandir: ./tests/sharding severity: warning zizmor: name: zizmor runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Run Zizmor run: | ZIZMOR=$(grep FROM Dockerfile.zizmor | cut -d' ' -f 2) docker run --rm -v $PWD:/source $ZIZMOR /source golang-github-sigstore-rekor-tiles-2.0.1/.github/workflows/protoc_diff_check.yml000066400000000000000000000023501511162205500301050ustar00rootroot00000000000000# # Copyright 2025 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: Check generated code for changes permissions: contents: read on: push: branches: - main pull_request: branches: - main jobs: check_generated_protos: name: Check generated protobufs runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Clean run: | make clean - name: Compile protobufs run: | make protos - name: Ensure no files were modified as a result of the codegen run: git update-index --refresh && git diff-index --quiet HEAD -- || git diff --exit-code golang-github-sigstore-rekor-tiles-2.0.1/.github/workflows/release.yml000066400000000000000000000051031511162205500260710ustar00rootroot00000000000000# Copyright 2025 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: Create release, push container image on: push: tags: - "v*" # Prevent concurrent runs of this workflow, but do not cancel any in-progress concurrency: group: "create-release" cancel-in-progress: false permissions: {} jobs: approval: # Require deployment review environment: release runs-on: ubuntu-latest steps: - run: echo "approved!" release: name: Create GitHub release runs-on: ubuntu-latest needs: approval permissions: contents: write attestations: write id-token: write steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version-file: './go.mod' check-latest: true cache: false # avoid cache-poisoning attacks # Syft is used by GoReleaser to generate SBOMs - uses: anchore/sbom-action/download-syft@8e94d75ddd33f69f691467e42275782e4bfefe84 # v0.20.9 # Initialize build arguments - name: Set build arguments run: | LDFLAGS=$(make ldflags) echo "LDFLAGS=$LDFLAGS" >> $GITHUB_ENV - name: Run GoReleaser uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 with: version: latest args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} LDFLAGS: ${{ env.LDFLAGS }} - name: Generate artifact attestation uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0 with: subject-path: ./dist/rekor-server-* build-container: name: Build and push container image uses: ./.github/workflows/build_container.yml # zizmor: ignore[secrets-inherit] secrets: inherit needs: approval permissions: contents: read packages: write attestations: write id-token: write golang-github-sigstore-rekor-tiles-2.0.1/.github/workflows/scorecard.yml000066400000000000000000000031621511162205500264210ustar00rootroot00000000000000# Copyright 2025 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: OpenSSF Scorecard on: # (Optional) For Branch-Protection check. Only the default branch is supported. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection # branch_protection_rule: # To guarantee Maintained check is occasionally updated. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained schedule: # Weekly on Saturdays. - cron: '30 1 * * 6' push: branches: [ main ] # Declare default permissions as read only. permissions: read-all jobs: analysis: name: Scorecards analysis permissions: # Needed to upload the results to code-scanning dashboard. security-events: write # Needed to publish results and get a badge (see publish_results below). id-token: write uses: sigstore/community/.github/workflows/reusable-scorecard.yml@main # (Optional) Disable publish results: # with: # publish_results: false # (Optional) Enable Branch-Protection check: # secrets: # scorecard_token: ${{ secrets.SCORECARD_TOKEN }} golang-github-sigstore-rekor-tiles-2.0.1/.github/workflows/test.yml000066400000000000000000000120771511162205500254400ustar00rootroot00000000000000# Copyright 2025 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: CI Tests on: push: branches: [ main ] pull_request: branches: [ main ] workflow_dispatch: permissions: contents: read jobs: build: name: Build CLI runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Extract version of Go to use run: echo "GOVERSION=$(awk -F'[:@]' '/FROM golang/{print $2; exit}' Dockerfile)" >> $GITHUB_ENV - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version: ${{ env.GOVERSION }} - name: Build run: make -C $GITHUB_WORKSPACE all - name: Ensure no files were modified as a result of the build run: git update-index --refresh && git diff-index --quiet HEAD -- || git diff --exit-code container-build: name: Build container runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Extract version of Go to use run: echo "GOVERSION=$(awk -F'[:@]' '/FROM golang/{print $2; exit}' Dockerfile)" >> $GITHUB_ENV - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version: ${{ env.GOVERSION }} - uses: ko-build/setup-ko@d006021bd0c28d1ce33a07e7943d48b079944c8d # v0.9 - name: container run: | make ko-local docker run --rm $(cat rekorImagerefs) version unit-tests: name: Run unit tests permissions: contents: read id-token: write # to authenticate with codecov runs-on: ubuntu-latest env: OS: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version-file: './go.mod' check-latest: true - name: Run Go tests run: go test -covermode atomic -coverprofile coverage.txt $(go list ./... | grep -v third_party/) - name: Workaround buggy Codecov OIDC auth run: | # only set CODECOV_TOKEN if OIDC token is available [ -z $ACTIONS_ID_TOKEN_REQUEST_TOKEN ] && exit 0 TOKEN_RESPONSE=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://codecov.io") CODECOV_TOKEN=$(echo $TOKEN_RESPONSE | jq -r .value) echo "CODECOV_TOKEN=$CODECOV_TOKEN" >> "$GITHUB_ENV" - name: Upload Coverage Report uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 with: env_vars: OS fail_ci_if_error: true # When github.com/codecov/codecov-action/issues/1791 is fixed, # remove workaround step above and uncomment: # use_oidc: true - name: Run Go tests w/ `-race` if: ${{ runner.os == 'Linux' }} run: go test -race $(go list ./... | grep -v third_party/) e2e-tests: name: Run E2E tests permissions: contents: read runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Run docker compose run: docker compose -f compose.yml up -d --build --wait --wait-timeout 60 - name: Run e2e tests run: go test -v -tags=e2e ./tests/ sharding-freeze: name: Run freeze log tests permissions: contents: read runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: Run freeze tests run: ./tests/freeze-test.sh sharding-e2e: name: Run sharding E2E tests permissions: contents: read runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 - name: Install faketime run: sudo apt install faketime -y - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version-file: './go.mod' check-latest: true - name: Run tests working-directory: ./tests/sharding run: ./test.sh golang-github-sigstore-rekor-tiles-2.0.1/.gitignore000066400000000000000000000001311511162205500223150ustar00rootroot00000000000000rekorImagerefs /rekor-server .idea tools/bin/ .vscode/ # Added by goreleaser init: dist/ golang-github-sigstore-rekor-tiles-2.0.1/.golangci.yml000066400000000000000000000023151511162205500227170ustar00rootroot00000000000000# Copyright 2025 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. version: "2" run: issues-exit-code: 1 linters: enable: - errcheck - gocritic - gosec - misspell - revive - unused exclusions: generated: lax presets: - comments - common-false-positives - legacy - std-error-handling rules: - linters: - errcheck - gosec path: _test\.go paths: - third_party$ - builtin$ - examples$ issues: max-issues-per-linter: 0 max-same-issues: 0 formatters: enable: - gofmt - goimports exclusions: generated: lax paths: - third_party$ - builtin$ - examples$ golang-github-sigstore-rekor-tiles-2.0.1/.goreleaser.yaml000066400000000000000000000030051511162205500234220ustar00rootroot00000000000000# Copyright 2025 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. version: 2 # Prevents parallel builds from stepping on each other while downloading modules before: hooks: - go mod tidy - /bin/bash -c 'if [ -n "$(git --no-pager diff --exit-code go.mod go.sum)" ]; then exit 1; fi' gomod: proxy: true sboms: - artifacts: binary builds: - id: rekor-server binary: rekor-server-{{ .Os }}-{{ .Arch }} main: ./cmd/rekor-server no_unique_dist_dir: true goos: - linux - darwin goarch: - amd64 - arm64 flags: - -trimpath mod_timestamp: '{{ .CommitTimestamp }}' env: - CGO_ENABLED=0 ldflags: - "{{ .Env.LDFLAGS }}" archives: - formats: [ "binary" ] name_template: "{{ .Binary }}" checksum: name_template: "{{ .ProjectName }}_checksums.txt" snapshot: version_template: SNAPSHOT-{{ .ShortCommit }} release: prerelease: auto draft: true # allow for manual edits github: owner: sigstore name: rekor-tiles golang-github-sigstore-rekor-tiles-2.0.1/.vscode/000077500000000000000000000000001511162205500216735ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/.vscode/settings.json000066400000000000000000000000461511162205500244260ustar00rootroot00000000000000{ "go.buildTags": "e2e,freeze", } golang-github-sigstore-rekor-tiles-2.0.1/CLIENTS.md000066400000000000000000000646431511162205500217720ustar00rootroot00000000000000# Client Changes for Rekor v2 This document outlines the changes clients need to make to support Rekor v2. ## Rekor v2 API Rekor v2 supports HTTP or gRPC, like Fulcio. For Go, we have implemented a client already. For other languages, they can either use the [OpenAPI docs](https://github.com/sigstore/rekor-tiles/tree/main/docs/openapi), [gRPC service proto](https://github.com/sigstore/rekor-tiles/tree/main/api/proto), or create their own client. The service implements one write API, `/api/v2/log/entries`. Example JSON request bodies below: ```jsonc // Request with a Fulcio certificate { "hashedRekordRequestV002": { // Must use hash algorithm from key_details "digest": "", "signature": { "content": "", "verifier": { "x509Certificate": { "rawBytes": "" }, // Must match signing algorithm "keyDetails": "PKIX_ECDSA_P256_SHA_256" } } } } // Request with a self-managed key { "hashedRekordRequestV002": { "digest": "", "signature": { "content": "", "verifier": { "publicKey": { "rawBytes": "" }, "keyDetails": "PKIX_ECDSA_P256_SHA_256" } } } } // Request with an attestation { "dsseRequestV002": { "envelope": { "payload": "", "payloadType": "", "signatures": [ { "sig": "", "keyid": "" } ] }, "verifier": { "x509Certificate": { "rawBytes": "" }, // Must match signing algorithm "keyDetails": "PKIX_ECDSA_P256_SHA_256" } } } // Request with an attestation with a self-managed key { "dsseRequestV002": { "envelope": { "payload": "", "payloadType": "", "signatures": [ { "sig": "", "keyid": "" } ] }, "verifier": { "publicKey": { "rawBytes": "" }, // Must match signing algorithm "keyDetails": "PKIX_ECDSA_P256_SHA_256" } } } ``` The response will be a [`TransparencyLogEntry` message](https://github.com/sigstore/protobuf-specs/blob/5296f13d62e7fad428581d969f664c30cc52f549/protos/sigstore_rekor.proto#L94), which should be persisted in a bundle (example at the bottom of this doc). Clients no longer need to transform the Rekor response into a `TLE` message to store in the bundle. The `canonicalized_body`, aka the entry stored in the log, matches what Rekor v1 stores as the canonicalized body. Clients MUST use `kind` and `apiVersion` to parse the `spec`. The client MUST gracefully fail when the client is not aware of a `kind` and `apiVersion`. ```jsonc // HashedRekord entry with public key { "apiVersion": "0.0.2", "kind": "hashedrekord", "spec": { "hashedRekordV002": { "data": { "algorithm": "", "digest": "" }, "signature": { "content": "" } } } } } } // DSSE entry { "apiVersion": "0.0.2", "kind": "dsse", "spec": { "dsseV002": { "data": { "algorithm": "SHA2_256", // Note that we no longer include the envelope hash, since there is no // canonicalization scheme for a DSSE envelope "digest": "" }, "signatures": [ { "content": "" } } } ] } } } ``` ### Two Entry Types Rekor v2 only supports `hashedrekord` (`HashedRekordLogEntryV002`) and `dsse` (`DSSELogEntryV002`) entry types, dropping a number of unused types such as `jar`, `alpine`, `rpm`, and the older types `rekord` and `intoto`. Additional types may be added in the future if there is demand, but this will require updating the client specification so that all clients implement support for these types. As with Rekor v1, the entry, aka `canonicalized_body`, will include the entry's kind (`hashedrekord`, `dsse`) and version (`0.0.2`) along with the entry itself in a `spec` field. Clients MUST gracefully fail when given a bundle with a kind or version that the client doesn't know how to parse. This is necessary so that clients gracefully fail when given a Rekor v2 entry. There are three possible scenarios for how clients should use kind and version to parse an entry: 1. With a verification bundle with `canonicalized_body`, clients can use the bundle's `kind` and `version` or `canonicalized_body.kind` and `canonicalized_body.api_version` to parse `canonicalized_body.spec`. 2. Clients that do not persist `canonicalized_body` must reconstruct the body in order to compute the leaf hash to verify the inclusion proof. Clients can use the bundle's `kind` and `version` to reconstruct the log entry given the artifact. Remember to JSON-canonicalize the entry before computing the leaf hash. 3. Monitors reading the entry from a tiled entry bundle will not have the verification bundle, but will have the canonicalized body. A monitor must use the `kind` and `apiVersion` to parse `spec`. #### DSSE For DSSE entries, the entry payload hash will always be SHA-256, regardless of the signature hashing algorithm specified in the key details. If non-default signing key algorithms (e.g. not ECDSA-P256) are supported, when verifying an entry, clients will need to handle computing the digest of the payload using SHA-256 and computing the digest to verify based on the key details. ### Certificate and Public Key Verifiers Rekor v2 only supports signature verification using a certificate or a public key, dropping support for PGP, minisign, pkcs7, SSH and TUF. Additional verifiers may be added in the future, but this will also require updating the client specification. ### Handling Longer Requests Clients need to increase request timeouts when creating entries to at least 20 seconds. Rekor now blocks on returning a response until a checkpoint has been published whose tree size includes the newly uploaded entry index. Checkpoint intervals have been increased to reduce the frequency of checkpoint publishing. Additionally, we plan to support synchronous witnessing, where third-party witnesses independently verify the consistency of the log and Rekor provides co-signed checkpoints with each upload response. If a client needs to create multiple entries, it is recommended to upload those entries in parallel. ## Signed RFC 3161 Timestamps Rekor will no longer return SignedEntryTimestamps or include integrated time in the response. Clients must fetch an RFC 3161 signed timestamp from a trusted timestamp authority and include the signed timestamp in the bundle. Sigstore now operates a timestamp authority at `timestamp.sigstore.dev` and `timestamp.sigstage.dev` for staging, and the roots for these services will be included in the TrustedRoot distributed via TUF. Clients may request timestamps from other trusted timestamp authorities as well. As with other services, users should specify the verification material for the additional timestamp authorities in the TrustedRoot. ## Miscellaneous verification bundle changes In addition to all of the changes listed above, there are a few smaller updates that clients may need to handle: * Multiple fields in `TransparencyLogEntry.inclusion_proof` are repeated elsewhere and should be ignored, including `inclusion_proof.log_index`, `inclusion_proof.root_hash`, and `inclusion_proof.tree_size`. * The log index should be read from the top-level `TransparencyLogEntry.log_index`. It is duplicated because in Rekor v1, there were two log indices - a global index and a shard-specific index. This is no longer true in Rekor v2 due to simplified sharding. * The root hash and tree size should be parsed from a verified checkpoint, after verifying `TransparencyLogEntry.inclusion_proof.checkpoint`. Do not use the unverified root hash and tree size unless the client compares these values to the values in the verified checkpoint (which is needlessly repetitive). Note that `root_hash` is no longer hex-encoded. * `TransparencyLogEntry.log_id.key_id` is no longer hex-encoded. * Reiterating, `TransparencyLogEntry.log_id.key_d` will be the non-truncated checkpoint key ID as documented in the C2SP signed-note [spec](https://github.com/C2SP/C2SP/blob/main/signed-note.md#signatures). If clients treat `log_id` as an opaque string to lookup the correct instance in the TrustedRoot, then no changes are needed. * `TransparencyLogEntry.integrated_time` will always be `0`. It MUST be ignored and clients MUST use an RFC3161 timestamp. Clients MUST already be ignoring `integrated_time` if `inclusion_promise` is not set. * The log index is a uint64. `TransparencyLogEntry.log_index` contains an int64, so Rekor v2 will never have a log index larger than `2^63 - 1`. When verifying an inclusion proof, the log index may need to be cast to a uint64. ## TrustedRoot changes ### TrustedRoot lookup by checkpoint key ID rather than log ID Log ID, the SHA-256 digest of the log's public key, is used as a "unique" identifier for a log. The TransparencyLogEntry message includes it in a bundle, and clients use that log ID to lookup the correct log public key in the TrustedRoot to verify the bundle. A checkpoint key ID is a truly unique log identifier, which incorporates the log origin, public key, and the signature type as per the C2SP signed-note spec linked above. Rekor will specify a log ID value that matches the non-truncated checkpoint key ID as specified in the [C2SP spec](https://github.com/C2SP/C2SP/blob/main/signed-note.md#signatures). In the trust root, the `log_id` and `checkpoint_key_id` fields will be set to the same value for a Rekor v2 log. A client can use one of the following algorithms to select the correct instance of a transparency log to verify a bundle's checkpoint and proof: 1. Using the `log_id` from the `TransparencyLogEntry` in the verification bundle, look up the corresponding `TransparencyLogInstance` in the trust root matching on `checkpoint_key_id` or `log_id` (`log_id` will be deprecated) 2. Enumerate all `TransparencyLogInstance` entries in the `TrustedRoot`. For a checkpoint, compare the key ID from a signature line on the checkpoint to `checkpoint_key_id[0:4]` 3. Enumerate all `TransparencyLogInstance` entries in the the `TrustedRoot`. Calculate the checkpoint key ID from each instance using the instance's public key and the origin (the origin should be the scheme-less log URL) following the [C2SP spec](https://github.com/C2SP/C2SP/blob/main/signed-note.md#signatures). Compare that computed key ID truncated to 4 bytes to the key ID on the checkpoint signature line. Below is a copy from the TrustedRoot documentation: ```proto For Ed25519 signatures, the key ID is computed per the C2SP spec: * key ID = SHA-256(key name || 0x0A || 0x01 || 32-byte Ed25519 public key)[:4] For ECDSA signatures, the key ID is computed per the C2SP spec: * key ID = SHA-256(PKIX ASN.1 DER-encoded public key, in SubjectPublicKeyInfo format)[:4] For RSA signatures, the signature type will be 0xFF with an appended identifier for the format, "PKIX-RSA-PKCS#1v1.5": * key ID = SHA-256(key name || 0x0A || 0xFF || PKIX-RSA-PKCS#1v1.5 || PKIX ASN.1 DER-encoded public key)[:4] ``` Rekor will no longer include log IDs in the response, and instead clients should use the checkpoint key ID as specified in the [C2SP spec](https://github.com/C2SP/C2SP/blob/main/signed-note.md#signatures) to lookup the correct log public key to verify a bundle. Each transparency log instance in the TrustedRoot will include a `checkpoint_key_id` instead of a `log_id`. For more information, log IDs are not necessarily unique identifiers for a log, since a log may reuse its public key among instances. Additionally, the log origin is not necessarily a unique identifier, because multiple logs may be hosted by one origin. Even the combination of both is not necessarily unique, as a log may create signatures for different purposes with the same key, though log deployments SHOULD NOT reuse origins and keys. For the public instance, we will always create a new key and origin for each log deployment. ### TrustedRoot With Multiple Logs Clients must verify that verification works with a TrustedRoot with multiple logs with overlapping validity windows. Confirm that the client will fetch the correct verification key from the TrustedRoot by using the above algorithms. Also confirm that the client does not assume only one transparency log in the TrustedRoot. ## SigningConfig support Clients must implement support for the SigningConfig message, which specifies the list of URLs that clients should use during signing. Since Rekor shards will now have unique URLs, we will use the SigningConfig to distribute the URLs for new shards. We have published a v2 SigningConfig message to support handling log sharding, with validity windows (which will prevent a client from writing to a log before the TrustedRoot is fully distributed) and services with different API versions (for the transition between Rekor v1 and v2). A more detailed description of how clients must, should and may handle the SigningConfig message is in the [protobuf-specs repo](https://github.com/sigstore/protobuf-specs/blob/a4c70fea6889694dfbceea11e0f9147335a95d79/protos/sigstore_trustroot.proto#L180), along with documentation on [service selection](https://github.com/sigstore/protobuf-specs/blob/a4c70fea6889694dfbceea11e0f9147335a95d79/protos/sigstore_trustroot.proto#L262-L275). An example SigningConfig with annotations is below: ```jsonc { // Clients do not need to support v0.1 "mediaType": "application/vnd.dev.sigstore.signingconfig.v0.2+json", // Fulcio service URLs. // Clients must select the first service from the list whose validity window // is active and the API version is supported by the client. Clients must // select the highest supported API version. // Clients can assume that this list is sorted from most recent to oldest. "caUrls": [ { "url": "https://fulcio.sigstore.dev", "majorApiVersion": 1, "validFor": { // When clients should start using this service "start": "", // Optional, when a service is turned down and clients should // treat the service as offline "end": "" }, "operator": "sigstore.dev", } ], // OIDC service URLs "oidcUrls": [ { "url": "https://oauth2.sigstore.dev/auth", "majorApiVersion": 1, "validFor": { "start": "" }, "operator": "sigstore.dev", } ], // Rekor service URLs. This example shows multiple active logs. // The client must select the log with the highest API version // it supports. // The client must group log deployments by operator and, when // selecting multiple, select at most one from an operator. "rekorTlogUrls": [ { "url": "https://log2025-01.rekor.sigstore.dev", "majorApiVersion": 2, "validFor": { "start": "" }, "operator": "sigstore.dev", }, { "url": "https://rekor.sigstore.dev", "majorApiVersion": 1, "validFor": { "start": "" }, "operator": "sigstore.dev", } ], // Rekor service selection // "Valid" is defined above to mean the service's validity window is // active and the API version is supported by the client. "rekorTlogConfig": { // EXACT specifies that a client must upload entries to exactly // "count" number of valid logs. Clients must throw an error // if less than "count" logs are valid. Clients should select // logs from the highest available API version, even if "count" // logs are not available. // May also be ANY, meaning the client should select exactly // one valid log. The client can decide how to select it, e.g. random // or round-robin if the client tracks state. // May also be ALL, which should be all valid logs. "selector": "EXACT", // Optional, only when EXACT is specified "count": 2 }, // Timestamp authority URLs // Like Rekor, clients should use the TSA config which dictates // how many TSAs should be used to request timestamps from. "tsaUrls": [ { "url": "https://oauth2.sigstore.dev/auth", "majorApiVersion": 1, "validFor": { "start": "" }, "operator": "sigstore.dev", } ], // Timestamp authority service selection "tsaConfig": { "selector": "ANY" } } ``` ## Removing Online Verification and Search Rekor no longer provides an API for online verification and search. This includes the APIs for requesting inclusion proofs by index, by leaf hash and by entry, and searching for an entry by artifact hash or identity. In the near future, we will spin up a separate service to support search. Clients must be given the inclusion proof and checkpoint for an entry, which must be stored in a bundle. This should have no impact on clients as inclusion proofs were already required in bundles. This will only impact monitors, and the read API changes are detailed below. ## No Attestation Storage Rekor v1's `intoto` type persisted attestations. Rekor v1's `dsse` type removed attestation storage as Rekor was not designed to be used as storage for verification metadata. In Rekor v2, the `DSSELogEntryV0_0_2` type will also not support attestation storage. Attestations should be persisted alongside an artifact, e.g. in OCI or a package registry, or in a dedicated attestation storage service. ## C2SP Checkpoints The checkpoints the log provides will conform to the [C2SP checkpoint spec](https://github.com/C2SP/C2SP/blob/main/tlog-checkpoint.md). Clients must check that their checkpoint verification implementation properly handles these checkpoints, which could include: * Multiple signatures * Optional extension lines * Updated key names, which will match the shard URL * Key IDs calculated per the [signed note spec](https://github.com/C2SP/C2SP/blob/main/signed-note.md#signatures) * Ed25519 and ECDSA key IDs are based on the spec * Ed25519: `key ID = SHA-256(key name || 0x0A || 0x01 || 32-byte Ed25519 public key)[:4]` * ECDSA: `key ID = SHA-256(PKIX ASN.1 DER-encoded public key, in SubjectPublicKeyInfo format)[:4]` * For RSA, the signature type is `0xFF` and we append `PKIX-RSA-PKCS#1v1.5`, `key ID = SHA-256(key name || 0x0A || 0xFF || PKIX-RSA-PKCS#1v1.5 || PKIX ASN.1 DER-encoded public key)[:4]` ## Monitoring/Auditing One of the more significant changes in a tile-backed log is the changes to the read API. Inclusion and consistency proofs are not served via an API, rather the client requests the set of tiles necessary to compute the inclusion or consistency proof. When monitoring the log searching for entries, the monitor will not request entries by index, but by tile. Monitors can choose to only fetch complete tiles or request [partial tiles](https://github.com/C2SP/C2SP/blob/main/tlog-tiles.md#partial-tiles), which are the rightmost tiles in a tree that may contain somewhere between 1 and 255 hashes. It is recommended to request partial tiles, or else the monitor might lag behind if tiles are not filled frequently. The APIs to request checkpoints, tiles, and entry bundles are defined in the [tlog-tiles spec](https://github.com/C2SP/C2SP/blob/main/tlog-tiles.md#apis). For Go, [trillian-tessera](https://github.com/transparency-dev/trillian-tessera/tree/main/client) provides a client to compute proofs and fetch tiles. ## Future: Witnessing Witnessing provides independent verification that the log remains consistent (append-only). Witnessing can either be asynchronous, where a client requests witnesses verify consistency proofs, or synchronous, where the log requests witnesses verify proofs for every checkpoint issued. This results in a longer request times and a dependency on third-party witnesses, but results in a strong offline proof of log inclusion. We will publish a doc later on with more details. In the initial launch of Rekor v2, checkpoints will not be witnessed, while we wait for the launch of a public witness network. Clients do not need to implement verification of witness signatures initially, but clients should increase request timeouts to account for the additional time to sign checkpoints, which we estimate to be <10s. To verify cosignatures, see the [spec](https://github.com/C2SP/C2SP/blob/main/tlog-cosignature.md). The log will determine which set of witnesses is trusted and the M-of-N witnesses to fetch signatures from, and this policy will be distributed by Sigstore's TUF root. Co-signed checkpoints will also be timestamped, so they can serve as an independent signed timestamp instead of an RFC 3161 timestamp. # Rekor v2, the bash way Clone the service if you haven't already: `git clone https://github.com/sigstore/rekor-tiles.git` Spin up the service: `docker compose up --build --wait` Create a signed artifact: ```bash openssl ecparam -genkey -name prime256v1 > ec_private.pem && openssl ec -in ec_private.pem -pubout > ec_public.pem head -c 128 < /dev/urandom > artifact openssl dgst -sha256 -sign ec_private.pem artifact > artifact.sig openssl dgst -sha256 -verify ec_public.pem -signature artifact.sig artifact # Should return Verified OK ``` Generate artifact digest: ```bash cat artifact | openssl dgst -binary -sha256 > artifact_dgst ``` Post an entry: ```bash curl -H \ "Accept: application/json" -X \ POST http://localhost:3003/api/v2/log/entries -o rekor_response -d "{ \"hashedRekordRequestV002\":{ \"digest\":\"$(cat artifact_dgst | base64 | tr -d '\n')\", \"signature\":{ \"content\": \"$(cat artifact.sig | base64 | tr -d '\n')\", \"verifier\": { \"key_details\": \"PKIX_ECDSA_P256_SHA_256\", \"public_key\": { \"raw_bytes\": \"$(openssl base64 -d -in ec_public.pem | base64 | tr -d '\n')\" } } } } }" ``` View the response with `cat rekor_response | jq .` Example: ```json { "logIndex": "0", "logId": { "keyId": "2AtEIMfG6Y41yK0tcwRTBS2tjhOrjKGIpDkHFgp65g0=" }, "kindVersion": { "kind": "hashedrekord", "version": "0.0.2" }, "integratedTime": "0", "inclusionPromise": null, "inclusionProof": { "logIndex": "0", "rootHash": "OWI5MjU1MWIxZTA0NGIzNmQ0ZGE0MTU0M2UwNjEwZjVhNzQyMDNiN2JiOWEzOWYwNDExOTZlMTI2MTg5NmM3NQ==", "treeSize": "1", "hashes": [], "checkpoint": { "envelope": "rekor-local\n1\nm5JVGx4ESzbU2kFUPgYQ9adCA7e7mjnwQRluEmGJbHU=\n\n— rekor-local 2AtEIIwnbtxrneJ7L1lQebfBRl7TxK84DTmx+kcZi7A25cBDgESI23f9ylThAlOireJ7U+H8eZF/4kJQcn9o5Qt8mQU=\n" } }, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJoYXNoZWRSZWtvcmRWMF8wXzIiOnsiZGF0YSI6eyJhbGdvcml0aG0iOiJTSEEyXzI1NiIsImRpZ2VzdCI6ImR5ajRlZG5ZSGpONC96c2pqQmVlTGFoUzlzbHA5N1o2N0xUQVZ4anJqWHc9In0sInNpZ25hdHVyZSI6eyJjb250ZW50IjoiTUVRQ0lCK1lQYTlvM1NOMHNRNHVkdUdmK21aeHdGZk9oRlowQ2d5K3A3VnQxbzJTQWlBUEZESHFPQUpMWW12dENXT3NEeU5ZMUg0VjN6bTRORURZczNOeXZIaDFQZz09IiwidmVyaWZpZXIiOnsia2V5RGV0YWlscyI6IlBLSVhfRUNEU0FfUDI1Nl9TSEFfMjU2IiwicHVibGljS2V5Ijp7InJhd0J5dGVzIjoiTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFMnNsT2Y4ZVpjajJtb1cydDRVRmo3dkNMNlFwRHprRHFxU1VtbTRPSkNWdklhdUtMeG0wYUdzM1ZNUFBmYXVNUGFNdXRuMC9zM2pnMHJyb0Z4b2ljeWc9PSJ9fX19fX0=" } ``` Parse the entry (i.e. canonicalized body) with `cat rekor_response | jq -r .canonicalizedBody | base64 -d` ```json { "apiVersion": "0.0.2", "kind": "hashedrekord", "spec": { "hashedRekordV002": { "data": { "algorithm": "SHA2_256", "digest": "dyj4ednYHjN4/zsjjBeeLahS9slp97Z67LTAVxjrjXw=" }, "signature": { "content": "MEQCIB+YPa9o3SN0sQ4uduGf+mZxwFfOhFZ0Cgy+p7Vt1o2SAiAPFDHqOAJLYmvtCWOsDyNY1H4V3zm4NEDYs3NyvHh1Pg==", "verifier": { "keyDetails": "PKIX_ECDSA_P256_SHA_256", "publicKey": { "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2slOf8eZcj2moW2t4UFj7vCL6QpDzkDqqSUmm4OJCVvIauKLxm0aGs3VMPPfauMPaMutn0/s3jg0rroFxoicyg==" } } } } } } ``` ## gRPC To query the server using gRPC, follow the above instructions to set up the server and generate an artifact. Install grpcurl to query the server: ```bash brew install grpcurl ``` List available methods: ```bash grpcurl -plaintext localhost:3001 list dev.sigstore.rekor.v2.Rekor ``` Only `CreateEntry` is implemented currently. Post an entry: ```bash grpcurl -d "{ \"hashed_rekord_request_v002\":{ \"digest\":\"$(cat artifact_dgst|base64)\", \"signature\":{ \"content\": \"$(cat artifact.sig | base64)\", \"verifier\": { \"key_details\": \"PKIX_ECDSA_P256_SHA_256\", \"public_key\": { \"raw_bytes\": \"$(openssl base64 -d -in ec_public.pem | base64)\" } } } } }" -plaintext localhost:3001 dev.sigstore.rekor.v2.Rekor.CreateEntry > rekor_response ``` View the response with `cat rekor_response | jq .`. golang-github-sigstore-rekor-tiles-2.0.1/CODEOWNERS000066400000000000000000000001031511162205500217170ustar00rootroot00000000000000* @sigstore/rekor-tiles-codeowners @sigstore/rekor-tiles-reviewers golang-github-sigstore-rekor-tiles-2.0.1/COPYRIGHT.txt000066400000000000000000000010621511162205500224420ustar00rootroot00000000000000Copyright 2025 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. golang-github-sigstore-rekor-tiles-2.0.1/Dockerfile000066400000000000000000000051241511162205500223260ustar00rootroot00000000000000# # Copyright 2025 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. FROM --platform=$BUILDPLATFORM golang:1.25.3@sha256:6d4e5e74f47db00f7f24da5f53c1b4198ae46862a47395e30477365458347bf2 AS builder ARG TARGETOS ARG TARGETARCH ENV APP_ROOT=/opt/app-root ENV GOPATH=$APP_ROOT WORKDIR $APP_ROOT/src/ ADD go.mod go.sum $APP_ROOT/src/ RUN go mod download # Add source code ADD ./cmd/ $APP_ROOT/src/cmd/ ADD ./pkg/ $APP_ROOT/src/pkg/ ADD ./internal/ $APP_ROOT/src/internal/ ARG SERVER_LDFLAGS # Build server for deployment RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=0 go build -ldflags "${SERVER_LDFLAGS}" ./cmd/rekor-server # Build server for debugger RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=0 go build -gcflags "all=-N -l" -ldflags "${SERVER_LDFLAGS}" -o rekor-server_debug ./cmd/rekor-server # Multi-stage deployment build FROM golang:1.25.3@sha256:6d4e5e74f47db00f7f24da5f53c1b4198ae46862a47395e30477365458347bf2 AS deploy # Retrieve the binary from the previous stage COPY --from=builder /opt/app-root/src/rekor-server /usr/local/bin/rekor-server COPY --from=builder /usr/bin/sleep /usr/bin/sleep # Set the binary as the entrypoint of the container CMD ["rekor-server", "serve"] # Cross-compile dlv for the debug stage FROM builder AS dlvbuilder ARG TARGETOS ARG TARGETARCH ENV APP_ROOT=/opt/app-root ENV GOPATH=$APP_ROOT # Create a directory where 'go install' may install the cross-compiled binary, # if the build and target platform differ. RUN mkdir -p /opt/app-root/bin/${TARGETOS}_${TARGETARCH} # dlv v1.25.1 RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go install github.com/go-delve/delve/cmd/dlv@f498dc8c5a8ad01334a9d782893c10bd0addb510 # Multi-stage debugger build FROM deploy AS debug ARG TARGETOS ARG TARGETARCH # Copy dlv binary, either from bin/ when the build and target platform are the same, or # from bin/TARGETOS_TARGETARCH when the platforms are different. COPY --from=dlvbuilder /opt/app-root/bin/dlv* /opt/app-root/bin/${TARGETOS}_${TARGETARCH}/dlv* /usr/local/bin/ # Overwrite server binary COPY --from=builder /opt/app-root/src/rekor-server_debug /usr/local/bin/rekor-server golang-github-sigstore-rekor-tiles-2.0.1/Dockerfile.emulator_init000066400000000000000000000015021511162205500251740ustar00rootroot00000000000000# # Copyright 2025 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. FROM gcr.io/google.com/cloudsdktool/google-cloud-cli:545.0.0-stable@sha256:3f8d865cc48811eff6162f8c9ce844da1d042182848e9a447cfd7e9c7c06455b ADD ./config/emulator_init.sh /root/ RUN chmod +x /root/emulator_init.sh CMD ["/root/emulator_init.sh"] golang-github-sigstore-rekor-tiles-2.0.1/Dockerfile.golangci-lint000066400000000000000000000002111511162205500250440ustar00rootroot00000000000000FROM golangci/golangci-lint:v2.6.0@sha256:cc8c1277eefdb5f88ba1381ee30a8bdf709e3615db9c843c9fcc04d9ac1d27a8 AS golangci-lint ENV FOO=BAR golang-github-sigstore-rekor-tiles-2.0.1/Dockerfile.protobuf-specs000066400000000000000000000003511511162205500252750ustar00rootroot00000000000000# A dockerfile to keep the protobuf-specs based builder up to date, we don't actually bulid from here FROM ghcr.io/sigstore/protobuf-specs-service-builder:0.5.1@sha256:8ded1f5ac6ef952eb882090f48da5048e7d00e04a4682c94837527c785522824 golang-github-sigstore-rekor-tiles-2.0.1/Dockerfile.release000066400000000000000000000032211511162205500237410ustar00rootroot00000000000000# # Copyright 2025 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. # Debian 12 (Bookworm) image to build binary for distroless/static-debian12 FROM --platform=$BUILDPLATFORM golang:1.25.3-bookworm@sha256:4f43b271f9673eb7bd0cb3a49cc17b08d8d6ee110277e26dbacc93c43a5a7793 AS builder ARG TARGETOS ARG TARGETARCH ENV APP_ROOT=/opt/app-root ENV GOPATH=$APP_ROOT WORKDIR $APP_ROOT/src/ ADD go.mod go.sum $APP_ROOT/src/ RUN go mod download # Add source code ADD ./cmd/ $APP_ROOT/src/cmd/ ADD ./pkg/ $APP_ROOT/src/pkg/ ADD ./internal/ $APP_ROOT/src/internal/ ARG SERVER_LDFLAGS # Build server for deployment. Build without cgo since distroless/static-debian12 doesn't include lib/cgo RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=0 go build -ldflags "${SERVER_LDFLAGS}" ./cmd/rekor-server # Multi-stage deployment build FROM gcr.io/distroless/static-debian12:nonroot@sha256:e8a4044e0b4ae4257efa45fc026c0bc30ad320d43bd4c1a7d5271bd241e386d0 AS deploy # Retrieve the binary from the previous stage COPY --from=builder /opt/app-root/src/rekor-server /usr/local/bin/rekor-server # Set the binary as the entrypoint of the container CMD ["rekor-server", "serve"] golang-github-sigstore-rekor-tiles-2.0.1/Dockerfile.zizmor000066400000000000000000000002671511162205500236620ustar00rootroot00000000000000# Not built, only used to get dependabot to update the version FROM ghcr.io/zizmorcore/zizmor:1.16.2@sha256:f49ba23d190f90cb837e7e117803cbf96f5632bc65417d7e2fdb04f606e19c17 AS zizmor golang-github-sigstore-rekor-tiles-2.0.1/LICENSE000066400000000000000000000261351511162205500213460ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. golang-github-sigstore-rekor-tiles-2.0.1/Makefile000066400000000000000000000110151511162205500217700ustar00rootroot00000000000000# # Copyright 2025 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. .PHONY: all test clean lint gosec ko-local tools ldflags all: protos rekor-server GIT_VERSION ?= $(shell git describe --tags --always --dirty) GIT_HASH ?= $(shell git rev-parse HEAD) DATE_FMT = +%Y-%m-%dT%H:%M:%SZ SOURCE_DATE_EPOCH ?= $(shell git log -1 --pretty=%ct) ifdef SOURCE_DATE_EPOCH BUILD_DATE ?= $(shell date -u -d "@$(SOURCE_DATE_EPOCH)" "$(DATE_FMT)" 2>/dev/null || date -u -r "$(SOURCE_DATE_EPOCH)" "$(DATE_FMT)" 2>/dev/null || date -u "$(DATE_FMT)") else BUILD_DATE ?= $(shell date "$(DATE_FMT)") endif GIT_TREESTATE = "clean" DIFF = $(shell git diff --quiet >/dev/null 2>&1; if [ $$? -eq 1 ]; then echo "1"; fi) ifeq ($(DIFF), 1) GIT_TREESTATE = "dirty" endif PROTO_DIRS = pkg/generated/protobuf/ api/proto/ SRC = $(shell find . -iname "*.go" | grep -v -e $(subst $() $(), -e ,$(strip $(PROTO_DIRS)))) PROTO_SRC = $(shell find $(PROTO_DIRS)) SIGSTORE_PROTO_BUILDER = $(shell grep FROM Dockerfile.protobuf-specs | cut -d' ' -f 2) ZIZMOR = $(shell grep FROM Dockerfile.zizmor | cut -d' ' -f 2) # for docker protobuf build GO_MODULE = github.com/sigstore/rekor-tiles/v2 PROTOS = $(shell find api/proto/ -iname "*.proto" | sed 's|^|/project_dir/|') PROTO_OUT = pkg/generated/protobuf OPENAPI_OUT = docs/openapi MOUNT_POINT = /project_dir PLATFORM ?= linux/amd64 UID ?= $(shell id -u) GID ?= $(shell id -g) DOCKER_RUN = docker run --platform ${PLATFORM} --user ${UID}:${GID} REKOR_LDFLAGS=-buildid= \ -X sigs.k8s.io/release-utils/version.gitVersion=$(GIT_VERSION) \ -X sigs.k8s.io/release-utils/version.gitCommit=$(GIT_HASH) \ -X sigs.k8s.io/release-utils/version.gitTreeState=$(GIT_TREESTATE) \ -X sigs.k8s.io/release-utils/version.buildDate=$(BUILD_DATE) SERVER_LDFLAGS=$(REKOR_LDFLAGS) GOBIN = $(abspath ./tools/bin) lint: go tool addlicense -l apache -c "The Sigstore Authors" -ignore "third_party/**" -v * go tool goimports -w $(SRC) docker run -t --rm -v $(PWD):/app -w /app \ --user $(shell id -u):$(shell id -g) \ -v $(shell go env GOCACHE):/.cache/go-build -e GOCACHE=/.cache/go-build \ -v $(shell go env GOMODCACHE):/.cache/mod -e GOMODCACHE=/.cache/mod \ -v ~/.cache/golangci-lint:/.cache/golangci-lint -e GOLANGCI_LINT_CACHE=/.cache/golangci-lint \ $(shell awk -F '[ @]' '/FROM golangci\/golangci-lint/{print $$2; exit}' Dockerfile.golangci-lint) golangci-lint run -v ./... docker run -t --rm -v $(PWD):/source $(ZIZMOR) /source gosec: ## Run gosec security scanner $(GOBIN)/gosec ./... rekor-server: $(SRC) $(PROTO_SRC) CGO_ENABLED=0 go build -trimpath -ldflags "$(SERVER_LDFLAGS)" -o rekor-server ./cmd/rekor-server ldflags: ## Print ldflags @echo $(SERVER_LDFLAGS) test: ## Run all tests go test ./... ko-local: ## Build container images locally using ko KO_DOCKER_REPO=ko.local LDFLAGS="$(SERVER_LDFLAGS)" GIT_HASH=$(GIT_HASH) GIT_VERSION=$(GIT_VERSION) \ ko publish --base-import-paths \ --tags $(GIT_VERSION) --tags $(GIT_HASH) --image-refs rekorImagerefs \ github.com/sigstore/rekor-tiles/v2/cmd/rekor-server # generate Go protobuf code protos: @echo "Generating go protobuf files" @mkdir -p ${OPENAPI_OUT} ${DOCKER_RUN} -v ${PWD}:${MOUNT_POINT} ${SIGSTORE_PROTO_BUILDER} \ -I/opt/include -I/googleapis -I/grpc-gateway -I/protobuf-specs -I${MOUNT_POINT}/api/proto \ --go_out=${MOUNT_POINT} \ --go_opt=module=${GO_MODULE} \ --go-grpc_opt=module=${GO_MODULE} --go-grpc_out=${MOUNT_POINT} \ --grpc-gateway_opt=module=${GO_MODULE} --grpc-gateway_opt=logtostderr=true --grpc-gateway_out=${MOUNT_POINT} \ --openapiv2_out=${MOUNT_POINT}/${OPENAPI_OUT} \ ${PROTOS} clean: ## Remove built binaries and artifacts rm -rf docs/openapi/* rm -rf pkg/generated/protobuf/* rm -rf dist rm -rf hack/tools/bin rm -rf rekor-server ################## # help ################## help: ## Display this help message @echo "Usage: make " @echo "" @echo "Targets:" @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) golang-github-sigstore-rekor-tiles-2.0.1/README.md000066400000000000000000000074221511162205500216160ustar00rootroot00000000000000# Rekor v2 Rekor v2, aka rekor-tiles or Rekor on Tiles, is a redesigned and modernized [Rekor](https://github.com/sigstore/rekor), Sigstore's signature transparency log, transitioning its backend to a modern, [tile-backed transparency log](https://transparency.dev/articles/tile-based-logs/) implementation to simplify maintenance and lower operational costs. More information (documents are shared with [sigstore-dev](https://groups.google.com/g/sigstore-dev), join the group to get access): * [Proposal](https://docs.google.com/document/d/1Mi9OhzrucIyt-UCLk_FxO2_xSQZW9ow9U3Lv0ZB_PpM/edit?resourcekey=0-4rPbZPyCS7QDj26Hk0UyvA&tab=t.0#heading=h.bjitqo6lwsmn) * [Design doc](https://docs.google.com/document/d/1ZYlt_VFB-lxbZCcTZHN-6KVDox3h7-ePp85pNpOUF1U/edit?resourcekey=0-V3WqDB22nOJfI4lTs59RVQ&tab=t.0#heading=h.xzptrog8pyxf) ## Public-good instance The Sigstore community hosts a productionized instance of Rekor v2 with a 99.5% availability SLO. See the [status page](https://status.sigstore.dev/) for uptime metrics. Use the public-good instance's TUF repository to determine the URL of the active instance. Note that the community instance's URL will change approximately every 6 months when we "shard" the log, creating a new log instance to keep the size of the log maintainable. Sigstore clients will pull the latest log shard URL from the TUF-distributed [SigningConfig](https://github.com/sigstore/root-signing/blob/main/targets/signing_config.v0.2.json), and will fetch both active and inactive shard public keys from the [TrustedRoot](https://github.com/sigstore/root-signing/blob/main/targets/trusted_root.json). As of October 2025, we have not yet distributed the current Rekor v2 URL in the SigningConfig, to give users adequate time to update their clients to support verifying entries from Rekor v2. We are planning to distribute the latest Rekor v2 URL by end of 2025/early 2026. If you want to start using Rekor v2, construct a signing config, using the [TUF-distributed signing config](https://github.com/sigstore/root-signing/blob/main/targets/signing_config.v0.2.json) as a base, and adding the following instance as the first entry in the `rekorTlogUrls` list: ``` { "url": "https://log2025-1.rekor.sigstore.dev", "majorApiVersion": 2, "validFor": { "start": "2025-10-06T00:00:00Z" }, "operator": "sigstore.dev" }, ``` **Note**: We will eventually turn down the 2025 Rekor v2 instance when we deploy a 2026 instance. We strongly advise against hardcoding this URL into any pipelines that cannot be easily updated. ## Installation We provide prebuilt binaries and containers for private deployments. * Download the latest binary from [Releases](https://github.com/sigstore/rekor-tiles/releases) * Pull the latest container from [GHCR](https://github.com/sigstore/rekor-tiles/pkgs/container/rekor-tiles) * Install Rekor v2 via [Helm](https://github.com/sigstore/helm-charts/tree/main/charts/rekor-tiles) ## Security Reports If you find any issues, follow Sigstore's [security policy](https://github.com/sigstore/rekor-tiles/security/policy) to report them. ## Local Development ### Deployment Run `docker compose up --build --wait` to start the service along with emulated Google Cloud Storage and Spanner instances. Run `docker compose down` to turn down the service, or `docker compose down --volumes` to turn down the service and delete persisted tiles. ### Making a request Follow the [client documentation](https://github.com/sigstore/rekor-tiles/blob/main/CLIENTS.md#rekor-v2-the-bash-way) for constructing a request and parsing a response. ### Testing Run unit tests with `go test ./...`. Follow the [end-to-end test documentation](https://github.com/sigstore/rekor-tiles/blob/main/tests/README.md) for how to run integration tests against a local instance. golang-github-sigstore-rekor-tiles-2.0.1/RELEASE.md000066400000000000000000000031541511162205500217370ustar00rootroot00000000000000# Releasing rekor-tiles ## Publishing updated binary and container This guide assumes that `upstream` references github.com/sigstore/rekor-tiles. Create and push a new tag. Note that you can't create a tag without creating a release in the GitHub UI, so you'll need to do this locally. ``` git pull upstream main export RELEASE_TAG=v1.2.3 # Specify version git tag -s ${RELEASE_TAG} -m "${RELEASE_TAG}" git push upstream ${RELEASE_TAG} ``` After the tag is pushed, two workflows will kick off: * [Create release](https://github.com/sigstore/rekor-tiles/actions/workflows/release.yml) * [Create and publish Docker image](https://github.com/sigstore/rekor-tiles/actions/workflows/build_container.yml) Once "Create release" finishes, go to [Releases](https://github.com/sigstore/rekor-tiles/releases), select the latest draft release, and publish it. If the release is pre-1.0, select "Set as a pre-release" before publishing. ## Updating the infrastructure Update the [Helm chart](https://github.com/sigstore/helm-charts/tree/main/charts/rekor-tiles) to the latest version. Follow [this example](https://github.com/sigstore/helm-charts/pull/989/). Get the container digest: ``` crane digest ghcr.io/sigstore/rekor-tiles:v1.2.3 # Specify version ``` Update `charts/rekor-tiles/values.yaml` with the new container version and digest. Update `charts/rekor-tiles/Chart.yaml`, bumping `version`, `appVersion`, and the image reference. Run `helm-docs -g charts/rekor-tiles` to update the README. Create a PR and wait for an oncall engineer to approve and merge. An oncall engineer also needs to update the chart version on the PGI repo. golang-github-sigstore-rekor-tiles-2.0.1/SHARDING.md000066400000000000000000000717661511162205500220740ustar00rootroot00000000000000# Sharding Playbook This playbook walks through the steps to create a new log shard for Rekor v2. Roughly, the steps are: 1. Create the GCP resources with Terraform 2. Generate key material 3. Spin up the new infrastructure with Helm and Terraform 4. Verify the health of the infrastructure with manual testing 5. Run prober tests 6. Update TrustedRoot with the new key material and SigningConfig with the new shard URL 7. Turn down the old shard ## Terraform Module Changes New shards are declared as additional modules under the respective environment. This is a different pattern than other modules, which are all initialized through the [`sigstore` module](https://github.com/sigstore/terraform-modules/tree/main/gcp/modules/sigstore). This design allowed deployers to initialize this one module and set all required variables, at which point you'd have a complete Sigstore stack on GCP. The downside of this approach is private deployers that don't need all GCP infrastructure have to initialize all resources, though deployers can always initialize each module referenced in `sigstore` individually. The other downside to referencing the `sigstore` module is that an update to a sub-module will concurrently update all deployed shards. Declaring each shard as its own module allows us to make updates to shards one-by-one. The tiled transparency log module we'll reference in Terraform is [`tiles_tlog`](https://github.com/sigstore/terraform-modules/tree/main/gcp/modules/tiles_tlog). ## Steps ### Temporarily Remove Org Restriction We are restricted from creating public buckets, which are required for the storage backend for Rekor v2 and Tessera. Temporarily disable the `iam.allowedPolicyMemberDomains` constraint. [Example PR](https://github.com/sigstore/public-good-instance/pull/2938) For production, update the `projects_project-rekor` module. For staging, update the `projects_projectsigstore-staging` module. These modules come from [this file](https://github.com/sigstore/public-good-instance/blob/main/iam/resource_hierarchy/main.tf). Run `terraform plan` and `terraform apply` using [Provision sigstore.dev organization](https://github.com/sigstore/public-good-instance/actions/workflows/iam-resource-hierarchy.yml). ### Create GCP resources To add a new shard, create a module under either [`staging.tf`](https://github.com/sigstore/public-good-instance/blob/main/terraform/environments/staging/1-infrastructure/staging.tf) or [`production.tf`](https://github.com/sigstore/public-good-instance/blob/main/terraform/environments/production/1-infrastructure/production.tf). Follow the [example PR](https://github.com/sigstore/public-good-instance/pull/3144), which will create the required resources. Note: Do not include the monitoring module `gcp/modules/monitoring/rekorv2` from the example. This will be added in a later step. The service must be deployed before creating metrics. Note: You should omit `key_name`, which will be set to a default value of `checkpoint-signer-key-encryption-key`. Note: You should omit `bucket_id_length`, which will append a random ID to the bucket name to make it unguessable, so that read traffic must go through the load balancer. Modules should be named based on the year and how many shards have been created in the year. For example, the module should be named `tiles_tlog_log2026_1` for the first shard in 2026. We'll use `log2026-1` as the name of the shard itself for this document. Update `general.auto.tfvars` to include the shard variable values: ``` rekor_tiles_shards = { "log2026-1" = { cluster_namespace_suffix = "rekor-tiles-system" bucket_name_suffix = "sigstore-dev-rekor-tiles" spanner_processing_units = 100 spanner_instance_name_suffix = "rekor-tiles" spanner_instance_display_name_suffix = "rekor-tiles" network_endpoint_group_http_name_suffix = "rekor-tiles-neg" network_endpoint_group_grpc_name_suffix = "rekor-tiles-neg-grpc" // network_endpoint_group_zones = ["us-central1-c", "us-central1-f", "us-central1-b"] } } ``` Note that `network_endpoint_group_zones` needs to remain commented out. Terraform will error out trying to discover the network endpoint group (NEG) resources if we declare the zones but the NEGs haven't been created by GKE through the Helm chart yet. Update `production.tf` or `staging.tf` based on the example. Make sure to update the `module` names, `shard_name` variables, and the name of the map key for `var.rekor_tiles_shards`. Ignore the code scanning and defsec alerts for the public bucket and load balancer, as that's expected. After merging, run `terraform plan` through the GitHub Actions workflow for [staging](https://github.com/sigstore/public-good-instance/actions/workflows/env-staging.yml) or [prod](https://github.com/sigstore/public-good-instance/actions/workflows/env-prod.yml). Confirm the expected resources are created, then run `terraform apply` through the workflow. ### Create Key Material The public-good instance uses a Tink keyset to sign checkpoints. Tink encrypts a signing key using a "key encryption key" (KEK) managed by GCP KMS, and loads the key into memory on service startup. Tink is beneficial for reliability and cost reduction - KMS is now only a startup dependency rather than a runtime dependency, and signing happens exclusively in memory. To create a shard signing key: 1. Grant yourself access to use the new KEK. Update the `staging.tf` or `production.tf` config to include the following IAM resource. The shard keyring name will be `-rekor-tiles-keyring`, e.g. `log2026-1-rekor-tiles-keyring`. ``` resource "google_kms_key_ring_iam_member" "rekor-tiles-keyring" { key_ring_id = "/global/" role = "roles/cloudkms.cryptoOperator" member = "user:@sigstore.dev" } ``` Request a review from an infrastructure maintainer and merge. [Example PR](https://github.com/sigstore/public-good-instance/pull/2942) 2. Run `terraform plan` and `terraform apply` using the GHA workflow. 3. [Install the gcloud CLI](https://cloud.google.com/sdk/docs/install) if not already installed. 4. Authenticate to gcloud with application credentials: `gcloud auth application-default login` 5. Checkout [`scaffolding`](https://github.com/sigstore/scaffolding), and using [`create-tink-keyset`](https://github.com/sigstore/scaffolding/tree/main/cmd/create-tink-keyset), create an encrypted Tink keyset: ``` go run ./cmd/create-tink-keyset \ --origin \ --key-template ED25519 \ --out enc-keyset.cfg \ --key-encryption-key-uri gcp-kms://projects//locations//keyRings//cryptoKeys/checkpoint-signer-key-encryption-key \ --public-key-out public.b64 \ --key-id-out keyid.b64 ``` Example: ``` go run ./cmd/create-tink-keyset \ --origin log2026-1.rekor.sigstore.dev \ --key-template ED25519 \ --out enc-keyset.cfg \ --key-encryption-key-uri gcp-kms://projects/projectsigstore-staging/locations/global/keyRings/log2026-1-rekor-tiles-keyring/cryptoKeys/checkpoint-signer-key-encryption-key \ --public-key-out public.b64 \ --key-id-out keyid.b64 ``` If there's an error that mentions `invalid_grant`, make sure you've run `gcloud auth application-default login`. IAM bindings sometimes take a few minutes to propagate as well. Note we won't use [tinkey](https://developers.google.com/tink/tinkey-overview#installation) since that requires a Java runtime environment and won't output the public key. Save the output file `enc-keyset.cfg`, which will be used as a value in the Helm chart, and `public.b64` and `keyid.b64`, which will be distributed in the TUF repo. 6. **Revoke IAM access** by reverting the PR and removing `google_kms_key_ring_iam_member`, and run `terraform plan` and `terraform apply`. ### Create Kubernetes namespace Each shard is isolated in its own Kubernetes namespace. For either [production](https://github.com/sigstore/public-good-instance/blob/main/terraform/environments/production/helm-charts-values/argocd-apps.yaml) or [staging](https://github.com/sigstore/public-good-instance/blob/main/terraform/environments/staging/helm-charts-values/argocd-apps.yaml), add a destination namespace: ``` projects: utilities: ... destinations: ... - namespace: log2026-1-rekor-tiles-system server: https://kubernetes.default.svc ``` [Example PR](https://github.com/sigstore/public-good-instance/pull/2953) Run `terraform apply` with the "Stage of terraform to run" being `all`, not `infra-only`, because you're updating ArgoCD's configuration. ### Create Kubernetes Resources Update the ArgoCD configuration for either [production](https://github.com/sigstore/public-good-instance/blob/main/argocd/bootstrap-utilities/production/values.yaml) or [staging](https://github.com/sigstore/public-good-instance/blob/main/argocd/bootstrap-utilities/staging/values.yaml) to include the new shard. Follow the [example PR](https://github.com/sigstore/public-good-instance/pull/3151), which will set up the deployment, pod monitoring, and gRPC secret. More information is below. Under `rekorTiles.shards`, copy the previous shard instantiation, and update the values accordingly based on the Terraform resource names. In particular: * Update `shardName` * For `signer.tink.keyset`, copy the contents of `enc-keyset.cfg` (Since this value is encrypted, it's safe to include it in the config rather than use GCP Secrets). Make sure `signer.tink.key` is set to either the KMS `key_name` or the default value `checkpoint-signer-key-encryption-key`. * Update `gcp.bucket` to be the name of the bucket without the `shardName` prefix. Note that this will have a random ID appended to it, e.g. `sigstore-dev-rekor-tiles-7dd04f40ece69668c26c`. You'll need to retrieve the random ID by looking at the bucket name on GCP. Additionally, create the `PodMonitoring` collector for gathering metrics from the pod. Add a new [`PodMonitoring` collector](https://github.com/sigstore/public-good-instance/blob/main/argocd/utilities/manifest/staging/prometheus-monitoring/rekor-tiles.yaml), updating `metadata.name` and `metadata.namespace` for the collector. To be able to route gRPC traffic to the Kubernetes backend, you'll need to provide a TLS certificate to the service. The certificate can be expired and will be shared between all shards, as its only purpose is to allow the load balancer to send encrypted traffic (a requirement imposed by the load balancer for HTTP2/gRPC traffic). Create an ExternalSecret configuration based on the example. Update the name of the file and `metadata.namespace`. All other values will remain the same. Note: If this is the first time you're deploying a shard in an environment, you'll need to do a [one time initialization of the GCP secret](#one-time-grpc-gcp-secret-initialization). Merge, and wait for ArgoCD to create the resources and spin up the service. You can monitor the GKE UI, or view the ArgoCD dashboard. To get access, follow the [playbook](https://github.com/sigstore/public-good-instance/blob/main/playbooks/argo-access.md). You may have to manually "sync" if there's any errors. Note that the namespace must exist before any resources can be created. Also note that the gRPC secret must be created before the service pods can be started. Once this completes, all Kubernetes resources will have been created. However, the service will not yet be available for traffic. ### Update Network Endpoint Groups In the last step, NEGs were created by Kubernetes. Now we need to update Terraform to reference these NEGs to be able to route traffic from the frontend load balancer to the backend Kubernetes resources. Uncomment `network_endpoint_group_zones` in `general.auto.tfvars` or set `network_endpoint_group_zones` to `["us-central1-c", "us-central1-f", "us-central1-b"]`. Note that these zones are based on where the VM instances are, which are currently the same for staging and production. [Example PR](https://github.com/sigstore/public-good-instance/pull/2955) Merge, `terraform plan` and `terraform apply`. The service should now be live! We'll verify everything's working as expected in a moment. ### Reapply Org Restriction Finally, reapply the org restriction to prevent public buckets. [Example PR](https://github.com/sigstore/public-good-instance/pull/2978) Run `terraform apply` using [Provision sigstore.dev organization](https://github.com/sigstore/public-good-instance/actions/workflows/iam-resource-hierarchy.yml). ### Create GCP Monitoring resources Now that the service is deployed, create the metrics, alerts and dashboards for the service. Follow the example below, updating the `shard_name`. [Example PR](https://github.com/sigstore/public-good-instance/pull/3282) Merge, `terraform plan`, and `terraform apply`. ## Verify Shard Health In GCP, make sure you're viewing the correct project for either production or staging. ### Verify the load balancer Go to [Load Balancing](https://console.cloud.google.com/net-services/loadbalancing/list/loadBalancers). You should see an application load balancer for Rekor, e.g. `log2026-1-rekor-lb`. It should have a green checkmark, with 2 backend services (0 instance groups, 6 network endpoint groups), and 1 backend bucket. Click on it. Under Frontend, you should see HTTPS with an SSL certificate. Under Routing Rules, there should be two rules, one for the domain, and a default rule. Under Backend and Backend services, you should see two backend services. One should be suffixed by `rekor-k8s-grpc-neg-backend-service` and the other `k8s-neg-backend-service`. For each backend, e.g. `log2026-rekor-tiles-neg-grpc`, you should see a green checkmark with "1 of 1" under Healthy. This means that every backend connection from the frontend load balancer to the Compute VM is functional, as verified by a healthcheck for HTTP and HTTP/2 traffic. Under Backend buckets, you should see a storage bucket and Cloud CDN should be Enabled. ### Verify the read path Verify that a checkpoint is served from the Storage bucket, e.g. ``` curl https://log2026-1.rekor.sigstage.dev/api/v2/checkpoint log2026-1.rekor.sigstage.dev 0 YR3vjr9qTpYPib0tUJT/7/pAPuK9xDOibMncp2aQAJE= — log2026-1.rekor.sigstage.dev 8w1amdxKovmc7H9+4lnx/vSeAQo9zo8cr8EHFCVviWMtTmm/Xw+zGDnBni9/mHHa1hFXIQlC29+YyaOXRxduvbE20Qg= ``` ### Verify the write path Following [these instructions](https://github.com/sigstore/rekor-tiles/blob/main/CLIENTS.md#rekor-v2-the-bash-way), test the write path for the new shard. This will sign an entry with a private key and upload the signing event to the log. ## WIP: Update Prober We will need a mechanism for running prober tests against a new deployment before we've distributed an updated SigningConfig and TrustedRoot. Assuming this is a parameter to a GitHub Actions workflow, a deployer should provide the URL to the new shard and run prober tests. Tracked in https://github.com/sigstore/rekor-tiles/issues/46. The prober will pick up new shards from the SigningConfig, with the latest active shard having no end date. Only the active shard will be used to test write traffic. All other shards in the SigningConfig will still support read traffic. ## Update TUF Verification Material Now that the shard is up and we've successfully signed and verified using it, we will update the TrustedRoot and SigningConfig files distributed via TUF so that clients can verify proofs from the shard and use the shard during signing. For TrustedRoot, the validity window for the shard should be from the point when the shard was spun up. For SigningConfig, the validity window should start after the TUF timestamp expires to guarantee all clients will have picked up the latest TrustedRoot. In practice for our TUF root with timestamp validity of one week, this means setting the validity window to be at least one week past when the TUF targets are signed. You will need four timestamps: * New shard's start time for TrustedRoot, which should be when the shard was spun up. * Previous shard's end for TrustedRoot: Clients will consider this shard's entry timestamps invalid if they are after this date. This must be after the start of the new shard. It doesn't have to be overly precise, and can be updated in a future TUF signing event. * New shard's start time for SigningConfig, which should be one week past when the root signing PR is merged and new TUF repository is published. Overestimate by at least a week of the expected merge date, as you don't want clients to start using the new shard before all clients have picked up the latest TrustedRoot. * Previous shard's end time for SigningConfig: This should be the start time of the new shard. ### Get shard public key You should have the log public key saved in `public.b64` from when you generated the key, and the log's checkpoint key ID in `keyid.b64`. If you don't have the public key, look at the service logs to find the public key, which is logged on service startup. If you don't have the key ID, you can use [this script](https://go.dev/play/p/oiE6LjeqLnj) to generate the ID given the key. The key and ID are formatted such that you just need to copy them into a new instance in the TrustedRoot. ### Update TrustedRoot Update the TrustedRoot for the new shard's key material, with the latest shard added to the **end** of the list: ``` { "mediaType": "...", "tlogs": [ // previous shard { "baseUrl": "...", "hashAlgorithm": "SHA2_256", "publicKey": { "rawBytes": "...", "keyDetails": "...", "validFor": { "start": "...", "end": "" } }, "logId": { "keyId": "..." } }, // new shard { "baseUrl": "https://.rekor.(sigstore|sigstage).dev", "hashAlgorithm": "SHA2_256", "publicKey": { "rawBytes": "", "keyDetails": "ED25519", "validFor": { "start": "" } }, "logId": { "keyId": "" } } ], ... } ``` Update the SigningConfig for the new shard, with the latest shard added to the **beginning** of the list: ``` { "media_type": "...", "ca_urls": [...], "oidc_urls": [...], // sorted from newest to oldest "rekor_tlog_urls": [ // new shard { "url": ".rekor.(sigstore|sigstage).dev", "major_api_version": 2, "valid_for": { "start": "" } }, // previous shard { ..., "valid_for": { "start": "...", "end": "" } } ], "rekor_tlog_config": {...}, "tsa_urls": [...], "tsa_config": {...}, } ``` Follow the playbooks in https://github.com/sigstore/root-signing and https://github.com/sigstore/root-signing-staging to orchestrate a new signing event. ## Update transparency-dev configuration We need to manually update the witness under the transparency-dev organization so that the shard will be monitored. Update [omniwitness](https://github.com/transparency-dev/witness/blob/main/omniwitness/logs.yaml), whose list is used by [Armored Witness](https://github.com/transparency-dev/armored-witness). [Example PR](https://github.com/transparency-dev/witness/pull/424/) ## Turn Down Old Shard Wait a week, until all clients have received updated SigningConfigs and started signing using the new shard, and are using the updated TrustedRoot to verify inclusion proofs. ### Verify write traffic Verify write traffic to the previous shard has gone down to ~0 QPS. If there is significant traffic, look at the user agent and see if you can figure out who's calling the service. [Example link](https://console.cloud.google.com/monitoring/metrics-explorer;duration=P1D?pageState=%7B%22xyChart%22:%7B%22constantLines%22:%5B%5D,%22dataSets%22:%5B%7B%22plotType%22:%22LINE%22,%22pointConnectionMethod%22:%22GAP_DETECTION%22,%22targetAxis%22:%22Y1%22,%22timeSeriesFilter%22:%7B%22aggregations%22:%5B%7B%22alignmentPeriod%22:%2260s%22,%22crossSeriesReducer%22:%22REDUCE_SUM%22,%22groupByFields%22:%5B%22resource.label.%5C%22namespace%5C%22%22%5D,%22perSeriesAligner%22:%22ALIGN_RATE%22%7D%5D,%22apiSource%22:%22DEFAULT_CLOUD%22,%22crossSeriesReducer%22:%22REDUCE_SUM%22,%22filter%22:%22metric.type%3D%5C%22prometheus.googleapis.com%2Frekor_v2_new_hashedrekord_entries%2Fcounter%5C%22%20resource.type%3D%5C%22prometheus_target%5C%22%22,%22groupByFields%22:%5B%22resource.label.%5C%22namespace%5C%22%22%5D,%22minAlignmentPeriod%22:%2260s%22,%22perSeriesAligner%22:%22ALIGN_RATE%22%7D%7D,%7B%22plotType%22:%22LINE%22,%22pointConnectionMethod%22:%22GAP_DETECTION%22,%22targetAxis%22:%22Y1%22,%22timeSeriesFilter%22:%7B%22aggregations%22:%5B%7B%22alignmentPeriod%22:%2260s%22,%22crossSeriesReducer%22:%22REDUCE_SUM%22,%22groupByFields%22:%5B%22resource.label.%5C%22namespace%5C%22%22%5D,%22perSeriesAligner%22:%22ALIGN_RATE%22%7D%5D,%22apiSource%22:%22DEFAULT_CLOUD%22,%22crossSeriesReducer%22:%22REDUCE_SUM%22,%22filter%22:%22metric.type%3D%5C%22prometheus.googleapis.com%2Frekor_v2_new_dsse_entries%2Fcounter%5C%22%20resource.type%3D%5C%22prometheus_target%5C%22%22,%22groupByFields%22:%5B%22resource.label.%5C%22namespace%5C%22%22%5D,%22minAlignmentPeriod%22:%2260s%22,%22perSeriesAligner%22:%22ALIGN_RATE%22%7D%7D%5D,%22options%22:%7B%22mode%22:%22COLOR%22%7D,%22y1Axis%22:%7B%22label%22:%22%22,%22scale%22:%22LINEAR%22%7D%7D%7D&project=projectsigstore-staging) ### Post on Slack Make a post on Slack letting the community know we'll be freezing and turning down the log. Include that the turndown should cause no issues for anyone using Sigstore clients. ### Update monitor for status page Log into [Betterstack](https://betterstack.com/), which hosts Sigstore's status page ([prod](https://status.sigstore.dev/), [staging](https://status.sigstage.dev/)). You'll need to work with someone in the oncall rotation to login with the magic link, using `oncall@sigstore.dev` to email a login link to the oncall group. Update the Rekor v2 [monitor](https://uptime.betterstack.com/team/t59712/monitors) for either staging or prod. Click the three dots on the right side and click "Configure". Change the "URL to monitor" to be for the new shard, and click "Save changes". ### WIP: Disable alerts for old shard Make sure to either disable or remove via Terraform alerts for the old shard for failing healthchecks for the write path or missing metrics. As we turn down the service and delete resources, we expect that healthchecks will start to fail. Remove the monitoring module from Terraform, either from `staging.tf` or `production.tf`. Merge and run `terraform plan` and `terraform apply`. [Example PR](https://github.com/sigstore/public-good-instance/pull/3175) As we create more alerting, we'll finalize this section with specific alerts to remove. We may need to add a `freeze_shard` variable that disables write-only metrics. For now, we'll remove the entire monitoring module. ### Remove Kubernetes backend services from Load Balancer The Kubernetes backend services must be removed from the load balancer URL map before deleting the backend services. Terraform isn't smart enough to do this in a single `apply`, as it tries to delete the backend services while they're still in use, which throws a resource-in-use error. In the Terraform configuration `staging.tf` or `production.tf` for the respective `tiles_tlog` module, set `lb_backend_turndown = true`. Merge the PR, and `terraform plan` and `terraform apply`. [Example PR](https://github.com/sigstore/public-good-instance/pull/3284) ### Delete Kubernetes backends from backend services Kubernetes manages the creation and deletion of network endpoint groups (NEGs), while Terraform manages the creation and deletion of backend services which reference the NEGs. We must first remove all Kubernetes backends from the backend services before deleting the NEGs, otherwise ArgoCD will stall on the deletion of the namespace since there will still be a reference to an existing Kubernetes object. In the Terraform configuration `staging.tf` or `production.tf` for the respective `tiles_tlog` module, comment out `network_endpoint_group_zones` or set it to `[]`, effectively reverting this [example PR](https://github.com/sigstore/public-good-instance/pull/2955). [Example PR](https://github.com/sigstore/public-good-instance/pull/3285/) commenting out NEG zones Merge, `terraform plan` and `terraform apply`. After this is merged, there will still be Kubernetes backend service resources in GCP, but they won't be referenced in the load balancer and will contain no NEG backends. The backend service resources will be cleaned up later. ### Delete Kubernetes resources Turn down the previous shard by removing the previous shard from `rekorTiles.shards`. We can turn down the Kubernetes pods because the server only serves the write path - the read path is exclusively served via GCP resources (Load Balancer, Storage). Remove the `PodMonitoring` collector and the Kubernetes gRPC secret as well for the shard. [Example PR](https://github.com/sigstore/public-good-instance/pull/3176) Merge. ArgoCD will not automatically delete the resources since we have disabled automatic pruning. You'll need to access ArgoCD and manually sync and prune. First, access ArgoCD following [the instructions](https://github.com/sigstore/public-good-instance/blob/main/playbooks/argo-access.md). You'll need to sync and prune three applications. The recommended order is: * `prometheus-monitoring`, to clean up the pod monitor * `private-key-secret`, to clean up the gRPC TLS secret * `bootstrap-utilities`, which should kick off the removal of `rekor-tiles-` * Note: If deletion stalls, under the `rekor-tiles-` application, you may need to manually sync the namespace to trigger it to be pruned. This may be due to the namespace getting recreated. To sync and prune, for each application, click "SYNC", select the "PRUNE" checkbox, and click "SYNCHRONIZE". ### Delete GCP resources We only need a subset of GCP resources to serve read traffic. To save costs, we can turn down the databases and destroy the KEK (indirectly destroying the signing key), along with deleting the Compute backend services for routing to the deleted pods. #### Remove database protection First, remove the protection bit on the databases. In the Terraform configuration `staging.tf` or `production.tf` for the respective `tiles_tlog` module, set `spanner_database_sequencer_deletion_protection` and `spanner_database_antispam_deletion_protection` to `false`. Create a PR, merge, `terraform plan`, and `terraform apply`. [Example PR](https://github.com/sigstore/public-good-instance/pull/3177) #### Delete resources To delete the KEK, databases, and Compute resources, set `freeze_shard` to `true`. Leave all variables as they are - even though their values won't be used, most are still required. [Example PR](https://github.com/sigstore/public-good-instance/pull/3180) Merge, `terraform plan`, and `terraform apply`. The Terraform plan should show deletion of: * Compute resources * Kubernetes gRPC and HTTP backend services * Backend service health checks * Firewall rules for the health checks * KMS resources * Key encryption key (KEK) * IAM decrypter and KMS member roles for the workload identity * Spanner * Sequencer and antispam databases * Instance (backups should be automatically deleted) * IAM DB admin role for the workload identity * GCS * IAM role for managing the GCS bucket for the workload identity * Monitoring * IAM roles for timeseries and descriptors creation for the workload identity ### Prober The prober should have stopped writing to the shard already since the SigningConfig will specify the new shard. The prober will continue testing read traffic, discovering the shard path via the TrustedRoot. # Appendix ## One-time gRPC GCP secret initialization gRPC traffic between the load balancer and the K8s backend service is encrypted over TLS. Since we'd prefer to have TLS terminate at the load balancer, we can create a single TLS certificate and key to be shared across all shards. It can even expire as per the documentation. We create a GCP secret once per environment, which is mounted by [External Secrets Operator](https://external-secrets.io/latest/). To create the secret, grant yourself `roles/secretmanager.admin`, and run: ``` openssl req -new -newkey rsa:2048 -days 1825 -nodes -x509 -keyout rekor-grpc.key -out rekor-grpc.crt -subj "/C=US/ST=WA/L=Kirkland/O=Sigstore/OU=Rekor/CN=localhost" -addext "subjectAltName = IP:127.0.0.1" -addext "keyUsage = critical,digitalSignature,keyEncipherment" openssl pkcs12 -passout "pass:" -export -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -macalg sha1 -out rekor-grpc.p12 -inkey rekor-grpc.key -in rekor-grpc.crt gcloud secrets create rekor-grpc-tls --replication-policy="automatic" --data-file="rekor-grpc.p12" --project projectsigstore-staging ``` This will generate a TLS key and self-signed certificate, merge them into a PKCS #12 archive, and upload that archive. golang-github-sigstore-rekor-tiles-2.0.1/api/000077500000000000000000000000001511162205500211035ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/api/proto/000077500000000000000000000000001511162205500222465ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/api/proto/rekor/000077500000000000000000000000001511162205500233705ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/api/proto/rekor/v2/000077500000000000000000000000001511162205500237175ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/api/proto/rekor/v2/dsse.proto000066400000000000000000000032771511162205500257530ustar00rootroot00000000000000// Copyright 2025 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. syntax = "proto3"; package dev.sigstore.rekor.v2; import "google/api/field_behavior.proto"; import "sigstore_common.proto"; import "envelope.proto"; import "rekor/v2/verifier.proto"; option go_package = "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf"; option java_package = "dev.sigstore.proto.rekor.v2"; option java_multiple_files = true; option java_outer_classname = "RekorV2Dsse"; option ruby_package = "Sigstore::Rekor::V2"; // A request to add a DSSE v0.0.2 entry to the log message DSSERequestV002 { // A DSSE envelope io.intoto.Envelope envelope = 1 [(google.api.field_behavior) = REQUIRED]; // All necessary verification material to verify all signatures embedded in the envelope repeated Verifier verifiers = 2 [(google.api.field_behavior) = REQUIRED]; } message DSSELogEntryV002 { // The hash of the DSSE payload dev.sigstore.common.v1.HashOutput payloadHash = 1 [(google.api.field_behavior) = REQUIRED]; // Signatures and their associated verification material used to verify the payload repeated Signature signatures = 2 [(google.api.field_behavior) = REQUIRED]; } golang-github-sigstore-rekor-tiles-2.0.1/api/proto/rekor/v2/entry.proto000066400000000000000000000042101511162205500261420ustar00rootroot00000000000000// Copyright 2025 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. syntax = "proto3"; package dev.sigstore.rekor.v2; import "google/api/field_behavior.proto"; import "rekor/v2/dsse.proto"; import "rekor/v2/hashedrekord.proto"; option go_package = "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf"; option java_package = "dev.sigstore.proto.rekor.v2"; option java_multiple_files = true; option java_outer_classname = "RekorV2Entry"; option ruby_package = "Sigstore::Rekor::V2"; // Entry is the message that is canonicalized and uploaded to the log. // This format is meant to be compliant with Rekor v1 entries in that // the `apiVersion` and `kind` can be parsed before parsing the spec. // Clients are expected to understand and handle the differences in the // contents of `spec` between Rekor v1 (a polymorphic OpenAPI defintion) // and Rekor v2 (a typed proto defintion). message Entry { string kind = 1 [(google.api.field_behavior) = REQUIRED]; string api_version = 2 [(google.api.field_behavior) = REQUIRED]; Spec spec = 3 [(google.api.field_behavior) = REQUIRED]; } // Spec contains one of the Rekor entry types. message Spec { oneof spec { HashedRekordLogEntryV002 hashed_rekord_v002 = 1 [(google.api.field_behavior) = REQUIRED]; DSSELogEntryV002 dsse_v002 = 2 [(google.api.field_behavior) = REQUIRED]; } } // Create a new HashedRekord or DSSE message CreateEntryRequest { oneof spec { HashedRekordRequestV002 hashed_rekord_request_v002 = 1 [(google.api.field_behavior) = REQUIRED]; DSSERequestV002 dsse_request_v002 = 2 [(google.api.field_behavior) = REQUIRED]; } } golang-github-sigstore-rekor-tiles-2.0.1/api/proto/rekor/v2/hashedrekord.proto000066400000000000000000000032031511162205500274450ustar00rootroot00000000000000// Copyright 2025 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. syntax = "proto3"; package dev.sigstore.rekor.v2; import "google/api/field_behavior.proto"; import "sigstore_common.proto"; import "rekor/v2/verifier.proto"; option go_package = "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf"; option java_package = "dev.sigstore.proto.rekor.v2"; option java_multiple_files = true; option java_outer_classname = "RekorV2HashedRekord"; option ruby_package = "Sigstore::Rekor::V2"; // A request to add a hashedrekord v0.0.2 to the log message HashedRekordRequestV002 { // The hashed data bytes digest = 1 [(google.api.field_behavior) = REQUIRED]; // A single signature over the hashed data with the verifier needed to validate it Signature signature = 2 [(google.api.field_behavior) = REQUIRED]; } message HashedRekordLogEntryV002 { // The hashed data dev.sigstore.common.v1.HashOutput data = 1 [(google.api.field_behavior) = REQUIRED]; // A single signature over the hashed data with the verifier needed to validate it Signature signature = 2 [(google.api.field_behavior) = REQUIRED]; } golang-github-sigstore-rekor-tiles-2.0.1/api/proto/rekor/v2/rekor_service.proto000066400000000000000000000066101511162205500276510ustar00rootroot00000000000000// Copyright 2025 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. syntax = "proto3"; package dev.sigstore.rekor.v2; import "google/api/annotations.proto"; import "google/api/httpbody.proto"; import "google/protobuf/empty.proto"; import "sigstore_rekor.proto"; import "protoc-gen-openapiv2/options/annotations.proto"; import "rekor/v2/entry.proto"; option go_package = "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf"; option java_package = "dev.sigstore.proto.rekor.v2"; option java_multiple_files = true; option java_outer_classname = "RekorV2Service"; option ruby_package = "Sigstore::Rekor::V2"; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { info: { title: "Rekor v2"; version: "2.0"; contact: { name: "Rekor v2 project"; url: "https://github.com/sigstore/rekor-tiles"; email: "sigstore-dev@googlegroups.com"; }; license: { name: "Apache License 2.0"; url: "https://github.com/sigstore/rekor-tiles/blob/main/LICENSE"; }; }; host: "*.rekor.sigstore.dev"; external_docs: { url: "https://github.com/sigstore/rekor-tiles"; description: "More about Rekor v2"; }; schemes: HTTP; consumes: "application/json"; produces: "application/json"; }; // A service for sigstore clients to connect to to create log entries // and for log monitors and witnesses to audit/inspect the log service Rekor { // Create an entry in the log rpc CreateEntry (CreateEntryRequest) returns (dev.sigstore.rekor.v1.TransparencyLogEntry) { option (google.api.http) = { post: "/api/v2/log/entries" body: "*" }; } // Get a tile from the log rpc GetTile (TileRequest) returns (google.api.HttpBody) { option (google.api.http) = { get: "/api/v2/tile/{L}/{N=**}" }; } // Get an entry bundle from the log rpc GetEntryBundle (EntryBundleRequest) returns (google.api.HttpBody) { option (google.api.http) = { get: "/api/v2/tile/entries/{N=**}" }; } // Get a checkpoint from the log rpc GetCheckpoint (google.protobuf.Empty) returns (google.api.HttpBody) { option (google.api.http) = { get: "/api/v2/checkpoint" }; } } // Request for a full or partial tile (see https://github.com/C2SP/C2SP/blob/main/tlog-tiles.md#merkle-tree) message TileRequest { uint32 L = 1; // N must be either an index encoded as zero-padded 3-digit path elements, e.g. "x123/x456/789", // and may end with ".p/", where "" is a uint8 string N = 2; } // Request for a full or partial entry bundle (see https://github.com/C2SP/C2SP/blob/main/tlog-tiles.md#log-entries) message EntryBundleRequest { // N must be either an index encoded as zero-padded 3-digit path elements, e.g. "x123/x456/789", // and may end with ".p/", where "" is a uint8 string N = 1; } golang-github-sigstore-rekor-tiles-2.0.1/api/proto/rekor/v2/verifier.proto000066400000000000000000000036361511162205500266270ustar00rootroot00000000000000// Copyright 2025 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. syntax = "proto3"; package dev.sigstore.rekor.v2; import "sigstore_common.proto"; import "google/api/field_behavior.proto"; option go_package = "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf"; option java_package = "dev.sigstore.proto.rekor.v2"; option java_multiple_files = true; option java_outer_classname = "RekorV2Verifier"; option ruby_package = "Sigstore::Rekor::V2"; // PublicKey contains an encoded public key message PublicKey { // DER-encoded public key bytes raw_bytes = 1 [(google.api.field_behavior) = REQUIRED]; } // Either a public key or a X.509 cerificiate with an embedded public key message Verifier { oneof verifier { // DER-encoded public key. Encoding method is specified by the key_details attribute PublicKey public_key = 1 [(google.api.field_behavior) = REQUIRED]; // DER-encoded certificate dev.sigstore.common.v1.X509Certificate x509_certificate = 2 [(google.api.field_behavior) = REQUIRED]; } // Key encoding and signature algorithm to use for this key dev.sigstore.common.v1.PublicKeyDetails key_details = 3 [(google.api.field_behavior) = REQUIRED]; } // A signature and an associated verifier message Signature { bytes content = 1 [(google.api.field_behavior) = REQUIRED]; Verifier verifier = 2 [(google.api.field_behavior) = REQUIRED]; } golang-github-sigstore-rekor-tiles-2.0.1/cmd/000077500000000000000000000000001511162205500210755ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/cmd/freeze-checkpoint/000077500000000000000000000000001511162205500245025ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/cmd/freeze-checkpoint/app/000077500000000000000000000000001511162205500252625ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/cmd/freeze-checkpoint/app/root.go000066400000000000000000000203461511162205500266010ustar00rootroot00000000000000// // Copyright 2025 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 app import ( "bytes" "context" "crypto" "fmt" "io" "log/slog" "os" "strings" "time" gcs "cloud.google.com/go/storage" grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" "github.com/sigstore/rekor-tiles/v2/internal/signerverifier" rekornote "github.com/sigstore/rekor-tiles/v2/pkg/note" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/kms/gcp" "github.com/spf13/cobra" "github.com/spf13/viper" logformat "github.com/transparency-dev/formats/log" "github.com/transparency-dev/tessera/api/layout" "golang.org/x/mod/sumdb/note" "google.golang.org/api/option" "google.golang.org/grpc" ) const ( frozenString = "Log frozen — " ) var rootCmd = &cobra.Command{ Use: "freeze-checkpoint", Short: "Freeze the log checkpoint", Long: `Add an extension line to the final checkpoint to indicate to consumers that no more checkpoints are going to be published. Only supported for GCP.`, Run: func(cmd *cobra.Command, _ []string) { ctx := cmd.Context() if viper.GetString("gcp-bucket") == "" { slog.Error("must provide --gcs-bucket") os.Exit(1) } if viper.GetString("hostname") == "" { slog.Error("must provide --hostname for the rekor server's identity") os.Exit(1) } sv, err := getSignerVerifier(ctx) if err != nil { slog.Error(err.Error()) os.Exit(1) } noteSigner, err := getNoteSigner(ctx, sv) if err != nil { slog.Error(err.Error()) os.Exit(1) } noteVerifier, err := getNoteVerifier(sv) if err != nil { slog.Error(err.Error()) os.Exit(1) } objReader, objWriter, err := objectAccess(ctx) if err != nil { slog.Error(err.Error()) os.Exit(1) } checkpoint, err := getCheckpoint(objReader, noteVerifier) if err != nil { slog.Error(err.Error()) os.Exit(1) } if checkpoint == nil { slog.Info("log is already frozen") return } err = updateCheckpoint(objWriter, noteSigner, checkpoint) if err != nil { slog.Error(err.Error()) os.Exit(1) } if err := objWriter.Close(); err != nil { slog.Error("closing object writer", "error", err.Error()) os.Exit(1) } slog.Info("Log frozen") }, } func Execute() { if err := rootCmd.Execute(); err != nil { slog.Error(err.Error()) os.Exit(1) } } func init() { rootCmd.Flags().String("gcp-bucket", "", "GCS bucket for tile and checkpoint storage") rootCmd.Flags().String("hostname", "", "public hostname, used as the checkpoint origin") rootCmd.Flags().String("signer-filepath", "", "path to the signing key") rootCmd.Flags().String("signer-password", "", "password to decrypt the signing key") rootCmd.Flags().String("signer-kmskey", "", "URI of the KMS key, in the form of awskms://keyname, azurekms://keyname, gcpkms://keyname, or hashivault://keyname") rootCmd.Flags().String("signer-kmshash", "sha256", "hash algorithm used by the KMS") rootCmd.Flags().String("signer-tink-kek-uri", "", "encryption key for decrypting Tink keyset. Valid options are [aws-kms://keyname, gcp-kms://keyname]") rootCmd.Flags().String("signer-tink-keyset-path", "", "path to encrypted Tink keyset") rootCmd.Flags().Uint("gcp-kms-retries", 0, "number of retries for GCP KMS requests") rootCmd.Flags().Uint32("gcp-kms-timeout", 0, "sets the RPC timeout per call for GCP KMS requests in seconds, defaults to 0 (no timeout)") if err := viper.BindPFlags(rootCmd.Flags()); err != nil { slog.Error(err.Error()) os.Exit(1) } } var hashAlgMap = map[string]crypto.Hash{ "sha256": crypto.SHA256, "sha384": crypto.SHA384, "sha512": crypto.SHA512, } func getSignerVerifier(ctx context.Context) (signature.SignerVerifier, error) { var opts []signerverifier.Option switch { case viper.GetString("signer-filepath") != "": opts = []signerverifier.Option{signerverifier.WithFile(viper.GetString("signer-filepath"), viper.GetString("signer-password"))} case viper.GetString("signer-kmskey") != "": kmshash := viper.GetString("signer-kmshash") hashAlg, ok := hashAlgMap[kmshash] if !ok { return nil, fmt.Errorf("invalid hash algorithm for --signer-kmshash: %s", kmshash) } rpcOpts := make([]signature.RPCOption, 0) callOpts := []grpc_retry.CallOption{grpc_retry.WithMax(viper.GetUint("gcp-kms-retries")), grpc_retry.WithPerRetryTimeout(time.Duration(viper.GetUint32("gcp-kms-timeout")) * time.Second)} rpcOpts = append(rpcOpts, gcp.WithGoogleAPIClientOption(option.WithGRPCDialOption(grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(callOpts...))))) opts = []signerverifier.Option{signerverifier.WithKMS(viper.GetString("signer-kmskey"), hashAlg, rpcOpts)} case viper.GetString("signer-tink-kek-uri") != "": opts = []signerverifier.Option{signerverifier.WithTink(viper.GetString("signer-tink-kek-uri"), viper.GetString("signer-tink-keyset-path"))} default: return nil, fmt.Errorf("must provide a signer using a file, KMS, or Tink") } signerVerifier, err := signerverifier.New(ctx, opts...) if err != nil { return nil, fmt.Errorf("initializing key signer: %w", err) } return signerVerifier, nil } func getNoteSigner(ctx context.Context, signer signature.Signer) (note.Signer, error) { origin := viper.GetString("hostname") noteSigner, err := rekornote.NewNoteSigner(ctx, origin, signer) if err != nil { return nil, fmt.Errorf("initializing note signer: %w", err) } return noteSigner, nil } func getNoteVerifier(verifier signature.Verifier) (note.Verifier, error) { origin := viper.GetString("hostname") noteVerifier, err := rekornote.NewNoteVerifier(origin, verifier) if err != nil { return nil, fmt.Errorf("initializing note verifier: %w", err) } return noteVerifier, nil } func objectAccess(ctx context.Context) (*gcs.Reader, *gcs.Writer, error) { client, err := gcs.NewClient(ctx, gcs.WithJSONReads()) if err != nil { return nil, nil, fmt.Errorf("getting GCS client: %w", err) } bucketName := viper.GetString("gcp-bucket") object := client.Bucket(bucketName).Object(layout.CheckpointPath) objReader, err := object.NewReader(ctx) if err != nil { return nil, nil, fmt.Errorf("getting object reader: %w", err) } contentType := "text/plain; charset=utf-8" objWriter := object.NewWriter(ctx) objWriter.ContentType = contentType return objReader, objWriter, nil } func getCheckpoint(objReader *gcs.Reader, noteVerifier note.Verifier) (*logformat.Checkpoint, error) { rawCheckpoint, err := io.ReadAll(objReader) if err != nil { return nil, fmt.Errorf("reading object: %w", err) } noteObj, err := note.Open(rawCheckpoint, note.VerifierList(noteVerifier)) if err != nil { return nil, fmt.Errorf("opening checkpoint: %w", err) } checkpoint := logformat.Checkpoint{} rest, err := checkpoint.Unmarshal([]byte(noteObj.Text)) if err != nil { return nil, fmt.Errorf("parsing checkpoint: %w", err) } if strings.Contains(string(rest), frozenString) { return nil, nil } return &checkpoint, nil } // updateCheckpoint writes an extension line to the checkpoint note to indicate the checkpoint is frozen, // re-signs it and re-uploads it to the GCS backend. func updateCheckpoint(objWriter *gcs.Writer, noteSigner note.Signer, checkpoint *logformat.Checkpoint) error { // Marshaled checkpoint contains the origin, size, and hash of the checkpoint, not the signatures. // The final checkpoint object will contain the origin, size, hash, extension line, and signature. buf := bytes.NewBuffer(checkpoint.Marshal()) buf.WriteString(frozenString) now := time.Now().UTC().Format(time.UnixDate) buf.WriteString(now) buf.WriteString("\n") signedNote, err := note.Sign(¬e.Note{Text: buf.String()}, noteSigner) if err != nil { return fmt.Errorf("re-signing checkpoint: %w", err) } if _, err := objWriter.Write(signedNote); err != nil { return fmt.Errorf("writing checkpoint: %w", err) } return nil } golang-github-sigstore-rekor-tiles-2.0.1/cmd/freeze-checkpoint/main.go000066400000000000000000000013161511162205500257560ustar00rootroot00000000000000// // Copyright 2025 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 "github.com/sigstore/rekor-tiles/v2/cmd/freeze-checkpoint/app" func main() { app.Execute() } golang-github-sigstore-rekor-tiles-2.0.1/cmd/rekor-server/000077500000000000000000000000001511162205500235235ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/cmd/rekor-server/app/000077500000000000000000000000001511162205500243035ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/cmd/rekor-server/app/root.go000066400000000000000000000024341511162205500256200ustar00rootroot00000000000000// // Copyright 2025 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 app import ( "log/slog" "os" "github.com/spf13/cobra" ) var rootCmd = &cobra.Command{ Use: "rekor-server", Short: "Rekor signature transparency log server", Long: `Rekor fulfills the signature transparency role of sigstore's software signing infrastructure. It can also be run on its own and is designed to be extensible to work with different manifest schemas and PKI tooling`, } // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { if err := rootCmd.Execute(); err != nil { slog.Error("failed to execute root command", "error", err) os.Exit(1) } } golang-github-sigstore-rekor-tiles-2.0.1/cmd/rekor-server/app/serve.go000066400000000000000000000317411511162205500257640ustar00rootroot00000000000000// // Copyright 2025 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 app import ( "context" "crypto" "crypto/x509" "encoding/base64" "fmt" "log/slog" "os" "sort" "strings" "time" clog "github.com/chainguard-dev/clog/gcp" grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" "google.golang.org/api/option" "google.golang.org/grpc" "k8s.io/klog/v2" "github.com/spf13/cobra" "github.com/spf13/viper" "sigs.k8s.io/release-utils/version" "github.com/sigstore/rekor-tiles/v2/internal/algorithmregistry" "github.com/sigstore/rekor-tiles/v2/internal/server" "github.com/sigstore/rekor-tiles/v2/internal/signerverifier" "github.com/sigstore/rekor-tiles/v2/internal/tessera" "github.com/sigstore/rekor-tiles/v2/pkg/note" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/kms/gcp" "github.com/sigstore/sigstore/pkg/signature/options" ) var serveCmd = &cobra.Command{ Use: "serve", Short: "start the Rekor server", Long: "start the Rekor server", Run: func(cmd *cobra.Command, _ []string) { ctx := cmd.Context() logLevel := slog.LevelInfo if err := logLevel.UnmarshalText([]byte(viper.GetString("log-level"))); err != nil { slog.Error("invalid log-level specified; must be one of 'debug', 'info', 'error', or 'warn'") os.Exit(1) } slog.SetDefault(slog.New(clog.NewHandler(logLevel))) // tessera uses klog so pipe all klog messages to be written through slog klog.SetSlogLogger(slog.Default()) slog.Info("starting rekor-server", "version", version.GetVersionInfo()) var signerOpts []signerverifier.Option switch { case viper.GetString("signer-filepath") != "": signerOpts = []signerverifier.Option{signerverifier.WithFile(viper.GetString("signer-filepath"), viper.GetString("signer-password"))} case viper.GetString("signer-kmskey") != "": kmshash := viper.GetString("signer-kmshash") hashAlg, ok := hashAlgMap[kmshash] if !ok { slog.Error("invalid hash algorithm for --signer-kmshash", "algorithm", kmshash) os.Exit(1) } // initialize optional RPC options for GCP KMS rpcOpts := make([]signature.RPCOption, 0) callOpts := []grpc_retry.CallOption{grpc_retry.WithMax(viper.GetUint("gcp-kms-retries")), grpc_retry.WithPerRetryTimeout(time.Duration(viper.GetUint32("gcp-kms-timeout")) * time.Second)} rpcOpts = append(rpcOpts, gcp.WithGoogleAPIClientOption(option.WithGRPCDialOption(grpc.WithUnaryInterceptor(grpc_retry.UnaryClientInterceptor(callOpts...))))) signerOpts = []signerverifier.Option{signerverifier.WithKMS(viper.GetString("signer-kmskey"), hashAlg, rpcOpts)} case viper.GetString("signer-tink-kek-uri") != "": signerOpts = []signerverifier.Option{signerverifier.WithTink(viper.GetString("signer-tink-kek-uri"), viper.GetString("signer-tink-keyset-path"))} default: slog.Error("no signer configured; must provide a signer using a file, KMS, or Tink") os.Exit(1) } signer, err := signerverifier.New(ctx, signerOpts...) if err != nil { slog.Error("failed to initialize signer", "error", err) os.Exit(1) } pubkey, err := signer.PublicKey() if err != nil { slog.Error("failed to get public key from signing key", "error", err) os.Exit(1) } der, err := x509.MarshalPKIXPublicKey(pubkey) if err != nil { slog.Error("failed to marshal public key to DER", "error", err) os.Exit(1) } slog.Info("Loaded signing key", "pubkey in base64 DER", base64.StdEncoding.EncodeToString(der)) appendOptions, err := tessera.NewAppendOptions(ctx, viper.GetString("hostname"), signer) if err != nil { slog.Error("failed to initialize append options", "error", err) os.Exit(1) } // Compute log ID for TransparencyLogEntry, to be used by clients to look up // the correct instance in a trust root. Log ID is equivalent to the non-truncated // hash of the public key and origin per the signed-note C2SP spec. pubKey, err := signer.PublicKey(options.WithContext(ctx)) if err != nil { slog.Error("failed to get public key", "error", err) os.Exit(1) } _, logID, err := note.KeyHash(viper.GetString("hostname"), pubKey) if err != nil { slog.Error("failed to get log ID", "error", err) os.Exit(1) } readOnly := viper.GetBool("read-only") var tesseraStorage tessera.Storage shutdownFn := func(_ context.Context) error { return nil } // if in read-only mode, don't start the appender, because we don't want new checkpoints being published. if !readOnly { driverConfig := tessera.DriverConfiguration{ Hostname: viper.GetString("hostname"), GCPBucket: viper.GetString("gcp-bucket"), GCPSpannerDB: viper.GetString("gcp-spanner"), PersistentAntispam: viper.GetBool("persistent-antispam"), ASMaxBatchSize: viper.GetUint("antispam-max-batch-size"), ASPushbackThreshold: viper.GetUint("antispam-pushback-threshold"), } tesseraDriver, persistentAntispam, err := tessera.NewDriver(ctx, driverConfig) if err != nil { slog.Error("failed to initialize driver", "error", err) os.Exit(1) } appendOptions = tessera.WithLifecycleOptions(appendOptions, viper.GetUint("batch-max-size"), viper.GetDuration("batch-max-age"), viper.GetDuration("checkpoint-interval"), viper.GetUint("pushback-max-outstanding")) appendOptions = tessera.WithAntispamOptions(appendOptions, persistentAntispam) if wpf := viper.GetString("witness-policy-path"); wpf != "" { f, err := os.ReadFile(wpf) if err != nil { slog.Error("failed to read witness policy file", "file", wpf, "error", err) os.Exit(1) } appendOptions, err = tessera.WithWitnessing(appendOptions, f) if err != nil { slog.Error("failed to initialize witnessing", "error", err) os.Exit(1) } } tesseraStorage, shutdownFn, err = tessera.NewStorage(ctx, viper.GetString("hostname"), tesseraDriver, appendOptions) if err != nil { slog.Error("failed to initialize tessera storage", "error", err) os.Exit(1) } } algorithmRegistry, err := algorithmregistry.AlgorithmRegistry(viper.GetStringSlice("client-signing-algorithms")) if err != nil { slog.Error("failed to get algorithm registry", "error", err) os.Exit(1) } rekorServer := server.NewServer(tesseraStorage, readOnly, algorithmRegistry, logID) server.Serve( ctx, server.NewHTTPConfig( server.WithHTTPPort(viper.GetInt("http-port")), server.WithHTTPHost(viper.GetString("http-address")), server.WithHTTPTimeout(viper.GetDuration("server-timeout")), server.WithHTTPMaxRequestBodySize(viper.GetInt("max-request-body-size")), server.WithHTTPMetricsPort(viper.GetInt("http-metrics-port")), server.WithHTTPTLSCredentials(viper.GetString("http-tls-cert-file"), viper.GetString("http-tls-key-file")), server.WithGRPCTLSCredentials(viper.GetString("grpc-tls-cert-file")), ), server.NewGRPCConfig( server.WithGRPCPort(viper.GetInt("grpc-port")), server.WithGRPCHost(viper.GetString("grpc-address")), server.WithGRPCTimeout(viper.GetDuration("server-timeout")), server.WithGRPCMaxMessageSize(viper.GetInt("max-request-body-size")), server.WithGRPCLogLevel(logLevel, viper.GetBool("request-response-logging")), server.WithTLSCredentials(viper.GetString("grpc-tls-cert-file"), viper.GetString("grpc-tls-key-file")), ), viper.GetDuration("tlog-timeout"), rekorServer, shutdownFn, ) }, } func init() { // server configs serveCmd.Flags().Bool("read-only", false, "whether the log should accept new entries") serveCmd.Flags().Int("http-port", 3000, "HTTP port to bind to") serveCmd.Flags().String("http-address", "127.0.0.1", "HTTP address to bind to") serveCmd.Flags().Int("http-metrics-port", 2112, "HTTP port to bind metrics to") serveCmd.Flags().Int("grpc-port", 3001, "GRPC port to bind to") serveCmd.Flags().String("grpc-address", "127.0.0.1", "GRPC address to bind to") serveCmd.Flags().Duration("server-timeout", 20*time.Second, "timeout settings for gRPC and HTTP connections") serveCmd.Flags().Int("max-request-body-size", 4*1024*1024, "maximum request body size in bytes") serveCmd.Flags().String("log-level", "info", "log level for the process. options are [debug, info, warn, error]") serveCmd.Flags().Bool("request-response-logging", false, "enables logging of request and response content; log-level must be 'debug' for this to take effect") serveCmd.Flags().String("grpc-tls-cert-file", "", "optional TLS certificate for serving gRPC over TLS") serveCmd.Flags().String("grpc-tls-key-file", "", "optional TLS private key for serving gRPC over TLS") serveCmd.Flags().String("http-tls-cert-file", "", "optional TLS certificate for serving HTTP over TLS") serveCmd.Flags().String("http-tls-key-file", "", "optional TLS private key for serving HTTP over TLS") // hostname hostname, err := os.Hostname() if err != nil { hostname = "localhost" } serveCmd.Flags().String("hostname", hostname, "public hostname, used as the checkpoint origin") // gcp configs serveCmd.Flags().String("gcp-bucket", "", "GCS bucket for tile and checkpoint storage") serveCmd.Flags().String("gcp-spanner", "", "Spanner database URI") // checkpoint signing configs serveCmd.Flags().String("signer-filepath", "", "path to the signing key") serveCmd.Flags().String("signer-password", "", "password to decrypt the signing key") serveCmd.Flags().String("signer-kmskey", "", "URI of the KMS key, in the form of awskms://keyname, azurekms://keyname, gcpkms://keyname, or hashivault://keyname") serveCmd.Flags().String("signer-kmshash", "sha256", "hash algorithm used by the KMS") serveCmd.Flags().String("signer-tink-kek-uri", "", "encryption key for decrypting Tink keyset. Valid options are [aws-kms://keyname, gcp-kms://keyname]") serveCmd.Flags().String("signer-tink-keyset-path", "", "path to encrypted Tink keyset") serveCmd.Flags().Uint("gcp-kms-retries", 0, "number of retries for GCP KMS requests") serveCmd.Flags().Uint32("gcp-kms-timeout", 0, "sets the RPC timeout per call for GCP KMS requests in seconds, defaults to 0 (no timeout)") // tessera lifecycle configs serveCmd.Flags().Uint("batch-max-size", tessera.DefaultBatchMaxSize, "the maximum number of entries that will accumulated before being sent to the sequencer") serveCmd.Flags().Duration("batch-max-age", tessera.DefaultBatchMaxAge, "the maximum amount of time a batch of entries will wait before being sent to the sequencer") serveCmd.Flags().Duration("checkpoint-interval", tessera.DefaultCheckpointInterval, "the frequency at which a checkpoint will be published") serveCmd.Flags().Uint("pushback-max-outstanding", tessera.DefaultPushbackMaxOutstanding, "the maximum number of 'in-flight' add requests") serveCmd.Flags().Duration("tlog-timeout", 30*time.Second, "timeout for terminating the tiles log queue") // antispam configs serveCmd.Flags().Bool("persistent-antispam", false, "whether to enable persistent antispam measures; only available for GCP storage backend and not supported by the Spanner storage emulator") serveCmd.Flags().Uint("antispam-max-batch-size", 0, "maximum batch size for deduplication operations; will default to Tessera recommendation if unset; for Spanner, recommend around 1500 with 300 or more PU, or around 64 for smaller (e.g. 100 PU) instances") serveCmd.Flags().Uint("antispam-pushback-threshold", 0, "maximum number of 'in-flight' add requests the antispam operator will allow before pushing back; will default to Tessera recommendation if unset") // witness configs serveCmd.Flags().String("witness-policy-path", "", "optional path to witness policy file") // allowed entry signing algorithms keyAlgorithmTypes, err := defaultKeyAlgorithms() if err != nil { slog.Error(err.Error()) os.Exit(1) } keyAlgorithmHelp := fmt.Sprintf("signing algorithm to use for signing/hashing (allowed %s)", strings.Join(keyAlgorithmTypes, ", ")) serveCmd.Flags().StringSlice("client-signing-algorithms", keyAlgorithmTypes, keyAlgorithmHelp) if err := viper.BindPFlags(serveCmd.Flags()); err != nil { slog.Error(err.Error()) os.Exit(1) } rootCmd.AddCommand(serveCmd) } var hashAlgMap = map[string]crypto.Hash{ "sha256": crypto.SHA256, "sha384": crypto.SHA384, "sha512": crypto.SHA512, } func defaultKeyAlgorithms() ([]string, error) { allowedClientSigningAlgorithms := algorithmregistry.AllowedClientSigningAlgorithms keyAlgorithmTypes := []string{} for _, keyAlgorithm := range allowedClientSigningAlgorithms { keyFlag, err := signature.FormatSignatureAlgorithmFlag(keyAlgorithm) if err != nil { return nil, err } keyAlgorithmTypes = append(keyAlgorithmTypes, keyFlag) } sort.Strings(keyAlgorithmTypes) return keyAlgorithmTypes, nil } golang-github-sigstore-rekor-tiles-2.0.1/cmd/rekor-server/app/version.go000066400000000000000000000013171511162205500263210ustar00rootroot00000000000000// // Copyright 2025 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 app import ( "sigs.k8s.io/release-utils/version" ) func init() { rootCmd.AddCommand(version.Version()) } golang-github-sigstore-rekor-tiles-2.0.1/cmd/rekor-server/main.go000066400000000000000000000013111511162205500247720ustar00rootroot00000000000000// // Copyright 2025 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 "github.com/sigstore/rekor-tiles/v2/cmd/rekor-server/app" func main() { app.Execute() } golang-github-sigstore-rekor-tiles-2.0.1/codecov.yml000066400000000000000000000011761511162205500225040ustar00rootroot00000000000000# Copyright 2025 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. coverage: status: patch: off project: off golang-github-sigstore-rekor-tiles-2.0.1/compose.yml000066400000000000000000000123631511162205500225270ustar00rootroot00000000000000# # Copyright 2025 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. services: spanner: image: gcr.io/cloud-spanner-emulator/emulator:1.5.43@sha256:a0e788c709a2746cba1b8d27e913b952809b92b23a6325e940e07c038d2d07d3 gcs: image: fsouza/fake-gcs-server:1.52.3@sha256:666f86b873120818b10a5e68d99401422fcf8b00c1f27fe89599c35236f48b4c volumes: - bucket:/data/tiles:rw command: - "-scheme=http" - "-port=7080" - "-public-host=localhost:7080" ports: - "7080:7080" rekor_init: build: context: . dockerfile: Dockerfile.emulator_init environment: - GCP_PROJECT=rekor-tiles-e2e - SPANNER_INSTANCE=rekor-tiles - SPANNER_DB=sequencer - SPANNER_EMULATOR_REST_HOST=http://spanner:9020/ - SPANNER_EMULATOR_HOST=spanner:9010 healthcheck: test: - CMD-SHELL - "test -f /root/finished" timeout: 10s retries: 4 depends_on: - spanner - gcs rekor: build: context: . target: deploy environment: - SPANNER_EMULATOR_HOST=spanner:9010 - STORAGE_EMULATOR_HOST=gcs:7080 command: - "rekor-server" - "serve" - "--http-address=0.0.0.0" - "--grpc-address=0.0.0.0" - "--hostname=rekor-local" - "--gcp-bucket=tiles" - "--gcp-spanner=projects/rekor-tiles-e2e/instances/rekor-tiles/databases/sequencer" - "--signer-filepath=/pki/ed25519-priv-key.pem" - "--checkpoint-interval=2s" - "--log-level=debug" - "--request-response-logging=true" - "--persistent-antispam" - "--witness-policy-path=/witness/policy.yaml" ports: - "3003:3000" # http port - "3001:3001" # grpc port - "2114:2112" # metrics port healthcheck: test: - CMD-SHELL - curl http://localhost:3000/healthz | grep '{"status":"SERVING"}' timeout: 30s retries: 10 interval: 3s # requires docker engine >= v25 # start_period: 5s # start_interval: 1s volumes: - ./tests/testdata/pki:/pki - ./tests/testdata/witness:/witness depends_on: rekor_init: condition: service_completed_successfully witness: condition: service_healthy witness: build: context: https://github.com/transparency-dev/witness.git#main dockerfile: cmd/omniwitness/Dockerfile volumes: - witness_data:/witness_data:rw - ./tests/testdata/witness:/witness_config command: - "--listen=:8100" - "--db_file=/witness_data/witness.sqlite" - "--private_key=PRIVATE+KEY+rekor-witness-test+a478f5cd+AT5yCADn4hz2PNT790E8q7VkAXuBdy+HFv9tP0qvKyFV" - "--additional_logs=/witness_config/config.yaml" - "--poll_interval=0" - "--logtostderr" - "--v=2" restart: always healthcheck: test: - CMD-SHELL - wget http://localhost:8081/metrics || exit 1 timeout: 30s retries: 10 interval: 3s ports: - "8100:8100" # Launch with `docker compose up rekor-debug` # or `docker compose up rekor-debug --watch`. # This service runs with the dlv debugger already in the Dockerfile. # If using VSCode, add this debugger launch configuration # { # "version": "0.2.0", # "configurations": [ # { # "name": "Attach to Process", # "type": "go", # "request": "attach", # "mode": "remote", # "port": 2345 # }, # ] # } rekor-debug: profiles: - debug develop: watch: - path: ./ action: rebuild build: context: . target: debug environment: - SPANNER_EMULATOR_HOST=spanner:9010 - STORAGE_EMULATOR_HOST=gcs:7080 command: - "dlv" - "exec" - "--continue" # launch rekor-server without waiting for the dubbger client to connect. - "--accept-multiclient" - "--listen=:2345" - "--headless=true" - "--log=true" - "--api-version=2" - "--" - "/usr/local/bin/rekor-server" - "serve" - "--http-address=0.0.0.0" - "--grpc-address=0.0.0.0" - "--hostname=rekor-local" - "--gcp-bucket=tiles" - "--gcp-spanner=projects/rekor-tiles-e2e/instances/rekor-tiles/databases/sequencer" - "--signer-filepath=/pki/ed25519-priv-key.pem" - "--checkpoint-interval=2s" ports: - 2345:2345 - "3003:3000" # http port - "3001:3001" # grpc port - "2114:2112" # metrics port healthcheck: test: - CMD-SHELL - curl http://localhost:3000/healthz | grep '{"status":"SERVING"}' timeout: 30s retries: 10 interval: 3s # requires docker engine >= v25 # start_period: 5s # start_interval: 1s volumes: - ./tests/testdata/pki:/pki depends_on: rekor_init: condition: service_completed_successfully volumes: bucket: {} witness_data: {} golang-github-sigstore-rekor-tiles-2.0.1/config/000077500000000000000000000000001511162205500215775ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/config/emulator_init.sh000066400000000000000000000032441511162205500250110ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2025 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. PS4='+\D{%Y-%m-%d %H:%M:%S} $LINENO: ' set -eux gcloud config configurations list | grep emulator | grep True || gcloud config configurations create emulator gcloud config set auth/disable_credentials true gcloud config set project $GCP_PROJECT gcloud config set api_endpoint_overrides/spanner $SPANNER_EMULATOR_REST_HOST gcloud spanner instances list | grep $SPANNER_INSTANCE || \ gcloud spanner instances create $SPANNER_INSTANCE \ --no-user-output-enabled \ --nodes=1 \ --description="test spanner instance for rekor tiles" \ --config=emulator-config gcloud spanner databases list --instance $SPANNER_INSTANCE | grep $SPANNER_DB || \ gcloud spanner databases create $SPANNER_DB \ --no-user-output-enabled \ --instance $SPANNER_INSTANCE spanner_antispam_db=$SPANNER_DB-antispam gcloud spanner databases list --instance $SPANNER_INSTANCE | grep $spanner_antispam_db || \ gcloud spanner databases create $spanner_antispam_db \ --no-user-output-enabled \ --instance $SPANNER_INSTANCE echo "done" > /root/finished golang-github-sigstore-rekor-tiles-2.0.1/docs/000077500000000000000000000000001511162205500212625ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/docs/openapi/000077500000000000000000000000001511162205500227155ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/docs/openapi/rekor/000077500000000000000000000000001511162205500240375ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/docs/openapi/rekor/v2/000077500000000000000000000000001511162205500243665ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/docs/openapi/rekor/v2/dsse.swagger.json000066400000000000000000000110721511162205500276560ustar00rootroot00000000000000{ "swagger": "2.0", "info": { "title": "rekor/v2/dsse.proto", "version": "version not set" }, "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": {}, "definitions": { "protobufAny": { "type": "object", "properties": { "@type": { "type": "string", "description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com. As of May 2023, there are no widely used type server\nimplementations and no plans to implement one.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics." } }, "additionalProperties": {}, "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n // or ...\n if (any.isSameTypeAs(Foo.getDefaultInstance())) {\n foo = any.unpack(Foo.getDefaultInstance());\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "type": "object", "$ref": "#/definitions/protobufAny" } } } } } } golang-github-sigstore-rekor-tiles-2.0.1/docs/openapi/rekor/v2/entry.swagger.json000066400000000000000000000110731511162205500300620ustar00rootroot00000000000000{ "swagger": "2.0", "info": { "title": "rekor/v2/entry.proto", "version": "version not set" }, "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": {}, "definitions": { "protobufAny": { "type": "object", "properties": { "@type": { "type": "string", "description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com. As of May 2023, there are no widely used type server\nimplementations and no plans to implement one.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics." } }, "additionalProperties": {}, "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n // or ...\n if (any.isSameTypeAs(Foo.getDefaultInstance())) {\n foo = any.unpack(Foo.getDefaultInstance());\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "type": "object", "$ref": "#/definitions/protobufAny" } } } } } } golang-github-sigstore-rekor-tiles-2.0.1/docs/openapi/rekor/v2/hashedrekord.swagger.json000066400000000000000000000111021511162205500313550ustar00rootroot00000000000000{ "swagger": "2.0", "info": { "title": "rekor/v2/hashedrekord.proto", "version": "version not set" }, "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": {}, "definitions": { "protobufAny": { "type": "object", "properties": { "@type": { "type": "string", "description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com. As of May 2023, there are no widely used type server\nimplementations and no plans to implement one.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics." } }, "additionalProperties": {}, "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n // or ...\n if (any.isSameTypeAs(Foo.getDefaultInstance())) {\n foo = any.unpack(Foo.getDefaultInstance());\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "type": "object", "$ref": "#/definitions/protobufAny" } } } } } } golang-github-sigstore-rekor-tiles-2.0.1/docs/openapi/rekor/v2/rekor_service.swagger.json000066400000000000000000000650451511162205500315730ustar00rootroot00000000000000{ "swagger": "2.0", "info": { "title": "Rekor v2", "version": "2.0", "contact": { "name": "Rekor v2 project", "url": "https://github.com/sigstore/rekor-tiles", "email": "sigstore-dev@googlegroups.com" }, "license": { "name": "Apache License 2.0", "url": "https://github.com/sigstore/rekor-tiles/blob/main/LICENSE" } }, "tags": [ { "name": "Rekor" } ], "host": "*.rekor.sigstore.dev", "schemes": [ "http" ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": { "/api/v2/checkpoint": { "get": { "summary": "Get a checkpoint from the log", "operationId": "Rekor_GetCheckpoint", "responses": { "200": { "description": "A successful response.", "schema": { "$ref": "#/definitions/apiHttpBody" } }, "default": { "description": "An unexpected error response.", "schema": { "$ref": "#/definitions/rpcStatus" } } }, "tags": [ "Rekor" ] } }, "/api/v2/log/entries": { "post": { "summary": "Create an entry in the log", "operationId": "Rekor_CreateEntry", "responses": { "200": { "description": "A successful response.", "schema": { "$ref": "#/definitions/v1TransparencyLogEntry" } }, "default": { "description": "An unexpected error response.", "schema": { "$ref": "#/definitions/rpcStatus" } } }, "parameters": [ { "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/v2CreateEntryRequest" } } ], "tags": [ "Rekor" ] } }, "/api/v2/tile/entries/{N}": { "get": { "summary": "Get an entry bundle from the log", "operationId": "Rekor_GetEntryBundle", "responses": { "200": { "description": "A successful response.", "schema": { "$ref": "#/definitions/apiHttpBody" } }, "default": { "description": "An unexpected error response.", "schema": { "$ref": "#/definitions/rpcStatus" } } }, "parameters": [ { "name": "N", "description": "N must be either an index encoded as zero-padded 3-digit path elements, e.g. \"x123/x456/789\",\nand may end with \".p/\u003cW\u003e\", where \"\u003cW\u003e\" is a uint8", "in": "path", "required": true, "type": "string", "pattern": ".+" } ], "tags": [ "Rekor" ] } }, "/api/v2/tile/{L}/{N}": { "get": { "summary": "Get a tile from the log", "operationId": "Rekor_GetTile", "responses": { "200": { "description": "A successful response.", "schema": { "$ref": "#/definitions/apiHttpBody" } }, "default": { "description": "An unexpected error response.", "schema": { "$ref": "#/definitions/rpcStatus" } } }, "parameters": [ { "name": "L", "in": "path", "required": true, "type": "integer", "format": "int64" }, { "name": "N", "description": "N must be either an index encoded as zero-padded 3-digit path elements, e.g. \"x123/x456/789\",\nand may end with \".p/\u003cW\u003e\", where \"\u003cW\u003e\" is a uint8", "in": "path", "required": true, "type": "string", "pattern": ".+" } ], "tags": [ "Rekor" ] } } }, "definitions": { "apiHttpBody": { "type": "object", "properties": { "contentType": { "type": "string", "description": "The HTTP Content-Type header value specifying the content type of the body." }, "data": { "type": "string", "format": "byte", "description": "The HTTP request/response body as raw binary." }, "extensions": { "type": "array", "items": { "type": "object", "$ref": "#/definitions/protobufAny" }, "description": "Application specific response metadata. Must be set in the first response\nfor streaming APIs." } }, "description": "Message that represents an arbitrary HTTP body. It should only be used for\npayload formats that can't be represented as JSON, such as raw binary or\nan HTML page.\n\n\nThis message can be used both in streaming and non-streaming API methods in\nthe request as well as the response.\n\nIt can be used as a top-level request field, which is convenient if one\nwants to extract parameters from either the URL or HTTP template into the\nrequest fields and also want access to the raw HTTP body.\n\nExample:\n\n message GetResourceRequest {\n // A unique request id.\n string request_id = 1;\n\n // The raw HTTP body is bound to this field.\n google.api.HttpBody http_body = 2;\n\n }\n\n service ResourceService {\n rpc GetResource(GetResourceRequest)\n returns (google.api.HttpBody);\n rpc UpdateResource(google.api.HttpBody)\n returns (google.protobuf.Empty);\n\n }\n\nExample with streaming methods:\n\n service CaldavService {\n rpc GetCalendar(stream google.api.HttpBody)\n returns (stream google.api.HttpBody);\n rpc UpdateCalendar(stream google.api.HttpBody)\n returns (stream google.api.HttpBody);\n\n }\n\nUse of this type only changes how the request and response bodies are\nhandled, all other features will continue to work unchanged." }, "intotoEnvelope": { "type": "object", "properties": { "payload": { "type": "string", "format": "byte", "description": "Message to be signed. (In JSON, this is encoded as base64.)\nREQUIRED." }, "payloadType": { "type": "string", "description": "String unambiguously identifying how to interpret payload.\nREQUIRED." }, "signatures": { "type": "array", "items": { "type": "object", "$ref": "#/definitions/iointotoSignature" }, "description": "Signature over:\n PAE(type, payload)\nWhere PAE is defined as:\nPAE(type, payload) = \"DSSEv1\" + SP + LEN(type) + SP + type + SP + LEN(payload) + SP + payload\n+ = concatenation\nSP = ASCII space [0x20]\n\"DSSEv1\" = ASCII [0x44, 0x53, 0x53, 0x45, 0x76, 0x31]\nLEN(s) = ASCII decimal encoding of the byte length of s, with no leading zeros\nREQUIRED (length \u003e= 1)." } }, "description": "An authenticated message of arbitrary type." }, "iointotoSignature": { "type": "object", "properties": { "sig": { "type": "string", "format": "byte", "description": "Signature itself. (In JSON, this is encoded as base64.)\nREQUIRED." }, "keyid": { "type": "string", "description": "*Unauthenticated* hint identifying which public key was used.\nOPTIONAL." } } }, "protobufAny": { "type": "object", "properties": { "@type": { "type": "string", "description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com. As of May 2023, there are no widely used type server\nimplementations and no plans to implement one.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics." } }, "additionalProperties": {}, "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n // or ...\n if (any.isSameTypeAs(Foo.getDefaultInstance())) {\n foo = any.unpack(Foo.getDefaultInstance());\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" }, "rekorv2PublicKey": { "type": "object", "properties": { "rawBytes": { "type": "string", "format": "byte", "title": "DER-encoded public key" } }, "title": "PublicKey contains an encoded public key", "required": [ "rawBytes" ] }, "rekorv2Signature": { "type": "object", "properties": { "content": { "type": "string", "format": "byte" }, "verifier": { "$ref": "#/definitions/v2Verifier" } }, "title": "A signature and an associated verifier", "required": [ "content", "verifier" ] }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "type": "object", "$ref": "#/definitions/protobufAny" } } } }, "v1Checkpoint": { "type": "object", "properties": { "envelope": { "type": "string" } }, "title": "The checkpoint MUST contain an origin string as a unique log identifier,\nthe tree size, and the root hash. It MAY also be followed by optional data,\nand clients MUST NOT assume optional data. The checkpoint MUST also contain\na signature over the root hash (tree head). The checkpoint MAY contain additional\nsignatures, but the first SHOULD be the signature from the log. Checkpoint contents\nare concatenated with newlines into a single string.\nThe checkpoint format is described in\nhttps://github.com/transparency-dev/formats/blob/main/log/README.md\nand https://github.com/C2SP/C2SP/blob/main/tlog-checkpoint.md.\nAn example implementation can be found in https://github.com/sigstore/rekor/blob/main/pkg/util/signed_note.go", "required": [ "envelope" ] }, "v1InclusionPromise": { "type": "object", "properties": { "signedEntryTimestamp": { "type": "string", "format": "byte" } }, "description": "The inclusion promise is calculated by Rekor. It's calculated as a\nsignature over a canonical JSON serialization of the persisted entry, the\nlog ID, log index and the integration timestamp.\nSee https://github.com/sigstore/rekor/blob/a6e58f72b6b18cc06cefe61808efd562b9726330/pkg/api/entries.go#L54\nThe format of the signature depends on the transparency log's public key.\nIf the signature algorithm requires a hash function and/or a signature\nscheme (e.g. RSA) those has to be retrieved out-of-band from the log's\noperators, together with the public key.\nThis is used to verify the integration timestamp's value and that the log\nhas promised to include the entry.", "required": [ "signedEntryTimestamp" ] }, "v1InclusionProof": { "type": "object", "properties": { "logIndex": { "type": "string", "format": "int64", "description": "The index of the entry in the tree it was written to." }, "rootHash": { "type": "string", "format": "byte", "description": "The hash digest stored at the root of the merkle tree at the time\nthe proof was generated." }, "treeSize": { "type": "string", "format": "int64", "description": "The size of the merkle tree at the time the proof was generated." }, "hashes": { "type": "array", "items": { "type": "string", "format": "byte" }, "description": "A list of hashes required to compute the inclusion proof, sorted\nin order from leaf to root.\nNote that leaf and root hashes are not included.\nThe root hash is available separately in this message, and the\nleaf hash should be calculated by the client." }, "checkpoint": { "$ref": "#/definitions/v1Checkpoint", "description": "Signature of the tree head, as of the time of this proof was\ngenerated. See above info on 'Checkpoint' for more details." } }, "description": "InclusionProof is the proof returned from the transparency log. Can\nbe used for offline or online verification against the log.", "required": [ "logIndex", "rootHash", "treeSize", "hashes", "checkpoint" ] }, "v1KindVersion": { "type": "object", "properties": { "kind": { "type": "string", "title": "Kind is the type of entry being stored in the log.\nSee here for a list: https://github.com/sigstore/rekor/tree/main/pkg/types" }, "version": { "type": "string", "description": "The specific api version of the type." } }, "description": "KindVersion contains the entry's kind and api version.", "required": [ "kind", "version" ] }, "v1LogId": { "type": "object", "properties": { "keyId": { "type": "string", "format": "byte", "description": "The unique identity of the log, represented by its public key." } }, "description": "LogId captures the identity of a transparency log.", "required": [ "keyId" ] }, "v1PublicKeyDetails": { "type": "string", "enum": [ "PUBLIC_KEY_DETAILS_UNSPECIFIED", "PKCS1_RSA_PKCS1V5", "PKCS1_RSA_PSS", "PKIX_RSA_PKCS1V5", "PKIX_RSA_PSS", "PKIX_RSA_PKCS1V15_2048_SHA256", "PKIX_RSA_PKCS1V15_3072_SHA256", "PKIX_RSA_PKCS1V15_4096_SHA256", "PKIX_RSA_PSS_2048_SHA256", "PKIX_RSA_PSS_3072_SHA256", "PKIX_RSA_PSS_4096_SHA256", "PKIX_ECDSA_P256_HMAC_SHA_256", "PKIX_ECDSA_P256_SHA_256", "PKIX_ECDSA_P384_SHA_384", "PKIX_ECDSA_P521_SHA_512", "PKIX_ED25519", "PKIX_ED25519_PH", "PKIX_ECDSA_P384_SHA_256", "PKIX_ECDSA_P521_SHA_256", "LMS_SHA256", "LMOTS_SHA256", "ML_DSA_65", "ML_DSA_87" ], "default": "PUBLIC_KEY_DETAILS_UNSPECIFIED", "description": "Details of a specific public key, capturing the the key encoding method,\nand signature algorithm.\n\nPublicKeyDetails captures the public key/hash algorithm combinations\nrecommended in the Sigstore ecosystem.\n\nThis is modelled as a linear set as we want to provide a small number of\nopinionated options instead of allowing every possible permutation.\n\nAny changes to this enum MUST be reflected in the algorithm registry.\n\nSee: \u003chttps://github.com/sigstore/architecture-docs/blob/main/algorithm-registry.md\u003e\n\nTo avoid the possibility of contradicting formats such as PKCS1 with\nED25519 the valid permutations are listed as a linear set instead of a\ncartesian set (i.e one combined variable instead of two, one for encoding\nand one for the signature algorithm).\n\n - PKCS1_RSA_PKCS1V5: RSA\n\nSee RFC8017\n - PKCS1_RSA_PSS: See RFC8017\n - PKIX_RSA_PKCS1V15_2048_SHA256: RSA public key in PKIX format, PKCS#1v1.5 signature\n - PKIX_RSA_PSS_2048_SHA256: RSA public key in PKIX format, RSASSA-PSS signature\n\nSee RFC4055\n - PKIX_ECDSA_P256_HMAC_SHA_256: ECDSA\n\nSee RFC6979\n - PKIX_ECDSA_P256_SHA_256: See NIST FIPS 186-4\n - PKIX_ED25519: Ed 25519\n\nSee RFC8032\n - PKIX_ECDSA_P384_SHA_256: These algorithms are deprecated and should not be used, but they\nwere/are being used by most Sigstore clients implementations.\n - LMS_SHA256: LMS and LM-OTS\n\nThese algorithms are deprecated and should not be used.\nKeys and signatures MAY be used by private Sigstore\ndeployments, but will not be supported by the public\ngood instance.\n\nUSER WARNING: LMS and LM-OTS are both stateful signature schemes.\nUsing them correctly requires discretion and careful consideration\nto ensure that individual secret keys are not used more than once.\nIn addition, LM-OTS is a single-use scheme, meaning that it\nMUST NOT be used for more than one signature per LM-OTS key.\nIf you cannot maintain these invariants, you MUST NOT use these\nschemes.\n - ML_DSA_65: ML-DSA\n\nThese ML_DSA_65 and ML-DSA_87 algorithms are the pure variants that\ntake data to sign rather than the prehash variants (HashML-DSA), which\ntake digests. While considered quantum-resistant, their usage\ninvolves tradeoffs in that signatures and keys are much larger, and\nthis makes deployments more costly.\n\nUSER WARNING: ML_DSA_65 and ML_DSA_87 are experimental algorithms. \nIn the future they MAY be used by private Sigstore deployments, but\nthey are not yet fully functional. This warning will be removed when \nthese algorithms are widely supported by Sigstore clients and servers, \nbut care should still be taken for production environments.\n\nSee NIST FIPS 204" }, "v1TransparencyLogEntry": { "type": "object", "properties": { "logIndex": { "type": "string", "format": "int64", "description": "The global index of the entry, used when querying the log by index." }, "logId": { "$ref": "#/definitions/v1LogId", "description": "The unique identifier of the log." }, "kindVersion": { "$ref": "#/definitions/v1KindVersion", "description": "The kind (type) and version of the object associated with this\nentry. These values are required to construct the entry during\nverification." }, "integratedTime": { "type": "string", "format": "int64", "description": "The UNIX timestamp from the log when the entry was persisted.\nThe integration time MUST NOT be trusted if inclusion_promise\nis omitted." }, "inclusionPromise": { "$ref": "#/definitions/v1InclusionPromise", "description": "The inclusion promise/signed entry timestamp from the log.\nRequired for v0.1 bundles, and MUST be verified.\nOptional for \u003e= v0.2 bundles if another suitable source of\ntime is present (such as another source of signed time,\nor the current system time for long-lived certificates).\nMUST be verified if no other suitable source of time is present,\nand SHOULD be verified otherwise." }, "inclusionProof": { "$ref": "#/definitions/v1InclusionProof", "description": "The inclusion proof can be used for offline or online verification\nthat the entry was appended to the log, and that the log has not been\naltered." }, "canonicalizedBody": { "type": "string", "format": "byte", "description": "Optional. The canonicalized transparency log entry, used to\nreconstruct the Signed Entry Timestamp (SET) during verification.\nThe contents of this field are the same as the `body` field in\na Rekor response, meaning that it does **not** include the \"full\"\ncanonicalized form (of log index, ID, etc.) which are\nexposed as separate fields. The verifier is responsible for\ncombining the `canonicalized_body`, `log_index`, `log_id`,\nand `integrated_time` into the payload that the SET's signature\nis generated over.\nThis field is intended to be used in cases where the SET cannot be\nproduced determinisitically (e.g. inconsistent JSON field ordering,\ndiffering whitespace, etc).\n\nIf set, clients MUST verify that the signature referenced in the\n`canonicalized_body` matches the signature provided in the\n`Bundle.content`.\nIf not set, clients are responsible for constructing an equivalent\npayload from other sources to verify the signature." } }, "description": "TransparencyLogEntry captures all the details required from Rekor to\nreconstruct an entry, given that the payload is provided via other means.\nThis type can easily be created from the existing response from Rekor.\nFuture iterations could rely on Rekor returning the minimal set of\nattributes (excluding the payload) that are required for verifying the\ninclusion promise. The inclusion promise (called SignedEntryTimestamp in\nthe response from Rekor) is similar to a Signed Certificate Timestamp\nas described here https://www.rfc-editor.org/rfc/rfc6962.html#section-3.2.", "required": [ "logIndex", "logId", "kindVersion", "integratedTime", "inclusionProof" ] }, "v1X509Certificate": { "type": "object", "properties": { "rawBytes": { "type": "string", "format": "byte", "description": "DER-encoded X.509 certificate." } }, "required": [ "rawBytes" ] }, "v2CreateEntryRequest": { "type": "object", "properties": { "hashedRekordRequestV002": { "$ref": "#/definitions/v2HashedRekordRequestV002" }, "dsseRequestV002": { "$ref": "#/definitions/v2DSSERequestV002" } }, "title": "Create a new HashedRekord or DSSE", "required": [ "hashedRekordRequestV002", "dsseRequestV002" ] }, "v2DSSERequestV002": { "type": "object", "properties": { "envelope": { "$ref": "#/definitions/intotoEnvelope", "title": "A DSSE envelope" }, "verifiers": { "type": "array", "items": { "type": "object", "$ref": "#/definitions/v2Verifier" }, "title": "All necessary verification material to verify all signatures embedded in the envelope" } }, "title": "A request to add a DSSE v0.0.2 entry to the log", "required": [ "envelope", "verifiers" ] }, "v2HashedRekordRequestV002": { "type": "object", "properties": { "digest": { "type": "string", "format": "byte", "title": "The hashed data" }, "signature": { "$ref": "#/definitions/rekorv2Signature", "title": "A single signature over the hashed data with the verifier needed to validate it" } }, "title": "A request to add a hashedrekord v0.0.2 to the log", "required": [ "digest", "signature" ] }, "v2Verifier": { "type": "object", "properties": { "publicKey": { "$ref": "#/definitions/rekorv2PublicKey", "title": "DER-encoded public key. Encoding method is specified by the key_details attribute" }, "x509Certificate": { "$ref": "#/definitions/v1X509Certificate", "title": "DER-encoded certificate" }, "keyDetails": { "$ref": "#/definitions/v1PublicKeyDetails", "title": "Key encoding and signature algorithm to use for this key" } }, "title": "Either a public key or a X.509 cerificiate with an embedded public key", "required": [ "publicKey", "x509Certificate", "keyDetails" ] } }, "externalDocs": { "description": "More about Rekor v2", "url": "https://github.com/sigstore/rekor-tiles" } } golang-github-sigstore-rekor-tiles-2.0.1/docs/openapi/rekor/v2/verifier.swagger.json000066400000000000000000000110761511162205500305370ustar00rootroot00000000000000{ "swagger": "2.0", "info": { "title": "rekor/v2/verifier.proto", "version": "version not set" }, "consumes": [ "application/json" ], "produces": [ "application/json" ], "paths": {}, "definitions": { "protobufAny": { "type": "object", "properties": { "@type": { "type": "string", "description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com. As of May 2023, there are no widely used type server\nimplementations and no plans to implement one.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics." } }, "additionalProperties": {}, "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n // or ...\n if (any.isSameTypeAs(Foo.getDefaultInstance())) {\n foo = any.unpack(Foo.getDefaultInstance());\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" }, "rpcStatus": { "type": "object", "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" }, "details": { "type": "array", "items": { "type": "object", "$ref": "#/definitions/protobufAny" } } } } } } golang-github-sigstore-rekor-tiles-2.0.1/go.mod000066400000000000000000000270331511162205500214450ustar00rootroot00000000000000module github.com/sigstore/rekor-tiles/v2 go 1.24.0 require ( cloud.google.com/go/spanner v1.86.1 cloud.google.com/go/storage v1.57.1 github.com/chainguard-dev/clog v1.7.0 github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 github.com/go-test/deep v1.1.1 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 github.com/prometheus/client_golang v1.23.2 github.com/secure-systems-lab/go-securesystemslib v0.9.1 github.com/sigstore/protobuf-specs v0.5.0 github.com/sigstore/sigstore v1.9.6-0.20250729224751-181c5d3339b3 github.com/sigstore/sigstore-go v1.1.3 github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.5 github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.5 github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.6-0.20250729224751-181c5d3339b3 github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.5 github.com/spf13/cobra v1.10.1 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 github.com/tink-crypto/tink-go-awskms/v2 v2.1.0 github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0 github.com/tink-crypto/tink-go/v2 v2.5.0 github.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c github.com/transparency-dev/merkle v0.0.2 github.com/transparency-dev/tessera v1.0.1-0.20251104110637-ba6c65c4ae73 go.opentelemetry.io/otel v1.38.0 go.opentelemetry.io/otel/exporters/prometheus v0.60.0 go.opentelemetry.io/otel/sdk/metric v1.38.0 go.step.sm/crypto v0.73.0 golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b golang.org/x/mod v0.29.0 golang.org/x/sync v0.18.0 google.golang.org/api v0.254.0 google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 google.golang.org/grpc v1.76.0 google.golang.org/protobuf v1.36.10 k8s.io/klog/v2 v2.130.1 sigs.k8s.io/release-utils v0.12.2 ) require ( cel.dev/expr v0.24.0 // indirect cloud.google.com/go v0.121.6 // indirect cloud.google.com/go/auth v0.17.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect cloud.google.com/go/iam v1.5.2 // indirect cloud.google.com/go/kms v1.23.2 // indirect cloud.google.com/go/longrunning v0.6.7 // indirect cloud.google.com/go/monitoring v1.24.2 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aws/aws-sdk-go v1.55.7 // indirect github.com/aws/aws-sdk-go-v2 v1.39.5 // indirect github.com/aws/aws-sdk-go-v2/config v1.31.16 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.18.20 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.12 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.12 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.12 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.12 // indirect github.com/aws/aws-sdk-go-v2/service/kms v1.46.2 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.0 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.39.0 // indirect github.com/aws/smithy-go v1.23.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/bmatcuk/doublestar/v4 v4.0.2 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect github.com/coreos/go-oidc/v3 v3.14.1 // 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/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-chi/chi/v5 v5.2.3 // indirect github.com/go-jose/go-jose/v4 v4.1.2 // indirect github.com/go-logr/logr v1.4.3 // 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.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/loads v0.22.0 // indirect github.com/go-openapi/runtime v0.28.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect github.com/go-openapi/strfmt v0.23.0 // indirect github.com/go-openapi/swag v0.24.1 // indirect github.com/go-openapi/swag/cmdutils v0.24.0 // indirect github.com/go-openapi/swag/conv v0.24.0 // indirect github.com/go-openapi/swag/fileutils v0.24.0 // indirect github.com/go-openapi/swag/jsonname v0.24.0 // indirect github.com/go-openapi/swag/jsonutils v0.24.0 // indirect github.com/go-openapi/swag/loading v0.24.0 // indirect github.com/go-openapi/swag/mangling v0.24.0 // indirect github.com/go-openapi/swag/netutils v0.24.0 // indirect github.com/go-openapi/swag/stringutils v0.24.0 // indirect github.com/go-openapi/swag/typeutils v0.24.0 // indirect github.com/go-openapi/swag/yamlutils v0.24.0 // indirect github.com/go-openapi/validate v0.24.0 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/addlicense v1.1.1 // indirect github.com/google/certificate-transparency-go v1.3.2 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-containerregistry v0.20.6 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.15.0 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // 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.8 // 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.2 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/vault/api v1.16.0 // indirect github.com/in-toto/attestation v1.1.2 // indirect github.com/in-toto/in-toto-golang v0.9.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b // indirect github.com/jellydator/ttlcache/v3 v3.4.0 // indirect github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/otlptranslator v0.0.2 // indirect github.com/prometheus/procfs v0.17.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/sassoftware/relic v7.2.1+incompatible // indirect github.com/segmentio/ksuid v1.0.4 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/sigstore/rekor v1.4.2 // indirect github.com/sigstore/rekor-tiles v0.1.11 // indirect github.com/sigstore/timestamp-authority v1.2.9 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/theupdateframework/go-tuf v0.7.0 // indirect github.com/theupdateframework/go-tuf/v2 v2.2.0 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/zeebo/errs v1.4.0 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.38.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect go.opentelemetry.io/otel/sdk v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.43.0 // indirect golang.org/x/net v0.46.0 // indirect golang.org/x/oauth2 v0.32.0 // indirect golang.org/x/sys v0.37.0 // indirect golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 // indirect golang.org/x/term v0.36.0 // indirect golang.org/x/text v0.30.0 // indirect golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.37.0 // indirect google.golang.org/genproto v0.0.0-20250922171735-9219d122eba9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) tool ( github.com/google/addlicense golang.org/x/tools/cmd/goimports ) golang-github-sigstore-rekor-tiles-2.0.1/go.sum000066400000000000000000006113741511162205500215010ustar00rootroot00000000000000cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4= cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= cloud.google.com/go/kms v1.23.2 h1:4IYDQL5hG4L+HzJBhzejUySoUOheh3Lk5YT4PCyyW6k= cloud.google.com/go/kms v1.23.2/go.mod h1:rZ5kK0I7Kn9W4erhYVoIRPtpizjunlrfU4fUkumUp8g= cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= cloud.google.com/go/spanner v1.86.1 h1:lSeVPwUotuKTpf8K6BPitzneQfGu73QcDFIca2lshG8= cloud.google.com/go/spanner v1.86.1/go.mod h1:bbwCXbM+zljwSPLZ44wZOdzcdmy89hbUGmM/r9sD0ws= cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= cloud.google.com/go/storage v1.57.1 h1:gzao6odNJ7dR3XXYvAgPK+Iw4fVPPznEPPyNjbaVkq8= cloud.google.com/go/storage v1.57.1/go.mod h1:329cwlpzALLgJuu8beyJ/uvQznDHpa2U5lGjWednkzg= cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 h1:KpMC6LFL7mqpExyMC9jVOYRiVhLmamjeZfRsUpB7l4s= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0 h1:E4MgwLBGeVB5f2MdcIVD3ELVAWpr+WD6MUe1i+tM/PA= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0/go.mod h1:Y2b/1clN4zsAoUd/pgNAQHjLDnTis/6ROkUfyob6psM= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 h1:XkkQbfMyuH2jTSjQjSoihryI8GINRcs4xp8lNawg0FI= github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3 h1:2afWGsMzkIcN8Qm4mgPJKZWyroE5QBszMiDMYEBrnfw= github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 h1:UQUsRi8WTzhZntp5313l+CHIAT95ojUI2lpP/ExlZa4= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 h1:lhhYARPUu3LmHysQ/igznQphfzynnqI3D75oUyw1HXk= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0/go.mod h1:l9rva3ApbBpEJxSNYnwT9N4CDLrWgtq3u8736C5hyJw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.54.0 h1:xfK3bbi6F2RDtaZFtUdKO3osOBIhNb+xTs8lFW6yx9o= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.54.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 h1:s0WlVbf9qpvkh1c/uDAPElam0WrL7fHRIidgZJ7UqZI= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= 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.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.39.5 h1:e/SXuia3rkFtapghJROrydtQpfQaaUgd1cUvyO1mp2w= github.com/aws/aws-sdk-go-v2 v1.39.5/go.mod h1:yWSxrnioGUZ4WVv9TgMrNUeLV3PFESn/v+6T/Su8gnM= github.com/aws/aws-sdk-go-v2/config v1.31.16 h1:E4Tz+tJiPc7kGnXwIfCyUj6xHJNpENlY11oKpRTgsjc= github.com/aws/aws-sdk-go-v2/config v1.31.16/go.mod h1:2S9hBElpCyGMifv14WxQ7EfPumgoeCPZUpuPX8VtW34= github.com/aws/aws-sdk-go-v2/credentials v1.18.20 h1:KFndAnHd9NUuzikHjQ8D5CfFVO+bgELkmcGY8yAw98Q= github.com/aws/aws-sdk-go-v2/credentials v1.18.20/go.mod h1:9mCi28a+fmBHSQ0UM79omkz6JtN+PEsvLrnG36uoUv0= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.12 h1:VO3FIM2TDbm0kqp6sFNR0PbioXJb/HzCDW6NtIZpIWE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.12/go.mod h1:6C39gB8kg82tx3r72muZSrNhHia9rjGkX7ORaS2GKNE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.12 h1:p/9flfXdoAnwJnuW9xHEAFY22R3A6skYkW19JFF9F+8= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.12/go.mod h1:ZTLHakoVCTtW8AaLGSwJ3LXqHD9uQKnOcv1TrpO6u2k= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.12 h1:2lTWFvRcnWFFLzHWmtddu5MTchc5Oj2OOey++99tPZ0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.12/go.mod h1:hI92pK+ho8HVcWMHKHrK3Uml4pfG7wvL86FzO0LVtQQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.2 h1:xtuxji5CS0JknaXoACOunXOYOQzgfTvGAc9s2QdCJA4= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.2/go.mod h1:zxwi0DIR0rcRcgdbl7E2MSOvxDyyXGBlScvBkARFaLQ= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.12 h1:MM8imH7NZ0ovIVX7D2RxfMDv7Jt9OiUXkcQ+GqywA7M= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.12/go.mod h1:gf4OGwdNkbEsb7elw2Sy76odfhwNktWII3WgvQgQQ6w= github.com/aws/aws-sdk-go-v2/service/kms v1.46.2 h1:hz2rJseQXnVQtVbByFpeSCNJBBU7oFN+yenW4biJtvs= github.com/aws/aws-sdk-go-v2/service/kms v1.46.2/go.mod h1:E4ink1KCQgqIe2pHFD9E+b5CNXovm50rQbWFuh0cM+I= github.com/aws/aws-sdk-go-v2/service/sso v1.30.0 h1:xHXvxst78wBpJFgDW07xllOx0IAzbryrSdM4nMVQ4Dw= github.com/aws/aws-sdk-go-v2/service/sso v1.30.0/go.mod h1:/e8m+AO6HNPPqMyfKRtzZ9+mBF5/x1Wk8QiDva4m07I= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.4 h1:tBw2Qhf0kj4ZwtsVpDiVRU3zKLvjvjgIjHMKirxXg8M= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.4/go.mod h1:Deq4B7sRM6Awq/xyOBlxBdgW8/Z926KYNNaGMW2lrkA= github.com/aws/aws-sdk-go-v2/service/sts v1.39.0 h1:C+BRMnasSYFcgDw8o9H5hzehKzXyAb9GY5v/8bP9DUY= github.com/aws/aws-sdk-go-v2/service/sts v1.39.0/go.mod h1:4EjU+4mIx6+JqKQkruye+CaigV7alL3thVPfDd9VlMs= github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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/bmatcuk/doublestar/v4 v4.0.2 h1:X0krlUVAVmtr2cRoTqR8aDMrDqnB36ht8wpWTiQ3jsA= github.com/bmatcuk/doublestar/v4 v4.0.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= 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/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/chainguard-dev/clog v1.7.0 h1:guPznsK8vLHvzz1QJe2yU6MFeYaiSOFOQBYw4OXu+g8= github.com/chainguard-dev/clog v1.7.0/go.mod h1:4+WFhRMsGH79etYXY3plYdp+tCz/KCkU8fAr0HoaPvs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk= github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q= github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1GUYL7P0MlNa00M67axePTq+9nBSGddR8I= github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI= github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/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= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.22.2 h1:rdxhzcBUazEcGccKqbY1Y7NS8FDcMyIRr0934jrYnZg= github.com/go-openapi/errors v0.22.2/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= github.com/go-openapi/swag v0.24.1 h1:DPdYTZKo6AQCRqzwr/kGkxJzHhpKxZ9i/oX0zag+MF8= github.com/go-openapi/swag v0.24.1/go.mod h1:sm8I3lCPlspsBBwUm1t5oZeWZS0s7m/A+Psg0ooRU0A= github.com/go-openapi/swag/cmdutils v0.24.0 h1:KlRCffHwXFI6E5MV9n8o8zBRElpY4uK4yWyAMWETo9I= github.com/go-openapi/swag/cmdutils v0.24.0/go.mod h1:uxib2FAeQMByyHomTlsP8h1TtPd54Msu2ZDU/H5Vuf8= github.com/go-openapi/swag/conv v0.24.0 h1:ejB9+7yogkWly6pnruRX45D1/6J+ZxRu92YFivx54ik= github.com/go-openapi/swag/conv v0.24.0/go.mod h1:jbn140mZd7EW2g8a8Y5bwm8/Wy1slLySQQ0ND6DPc2c= github.com/go-openapi/swag/fileutils v0.24.0 h1:U9pCpqp4RUytnD689Ek/N1d2N/a//XCeqoH508H5oak= github.com/go-openapi/swag/fileutils v0.24.0/go.mod h1:3SCrCSBHyP1/N+3oErQ1gP+OX1GV2QYFSnrTbzwli90= github.com/go-openapi/swag/jsonname v0.24.0 h1:2wKS9bgRV/xB8c62Qg16w4AUiIrqqiniJFtZGi3dg5k= github.com/go-openapi/swag/jsonname v0.24.0/go.mod h1:GXqrPzGJe611P7LG4QB9JKPtUZ7flE4DOVechNaDd7Q= github.com/go-openapi/swag/jsonutils v0.24.0 h1:F1vE1q4pg1xtO3HTyJYRmEuJ4jmIp2iZ30bzW5XgZts= github.com/go-openapi/swag/jsonutils v0.24.0/go.mod h1:vBowZtF5Z4DDApIoxcIVfR8v0l9oq5PpYRUuteVu6f0= github.com/go-openapi/swag/loading v0.24.0 h1:ln/fWTwJp2Zkj5DdaX4JPiddFC5CHQpvaBKycOlceYc= github.com/go-openapi/swag/loading v0.24.0/go.mod h1:gShCN4woKZYIxPxbfbyHgjXAhO61m88tmjy0lp/LkJk= github.com/go-openapi/swag/mangling v0.24.0 h1:PGOQpViCOUroIeak/Uj/sjGAq9LADS3mOyjznmHy2pk= github.com/go-openapi/swag/mangling v0.24.0/go.mod h1:Jm5Go9LHkycsz0wfoaBDkdc4CkpuSnIEf62brzyCbhc= github.com/go-openapi/swag/netutils v0.24.0 h1:Bz02HRjYv8046Ycg/w80q3g9QCWeIqTvlyOjQPDjD8w= github.com/go-openapi/swag/netutils v0.24.0/go.mod h1:WRgiHcYTnx+IqfMCtu0hy9oOaPR0HnPbmArSRN1SkZM= github.com/go-openapi/swag/stringutils v0.24.0 h1:i4Z/Jawf9EvXOLUbT97O0HbPUja18VdBxeadyAqS1FM= github.com/go-openapi/swag/stringutils v0.24.0/go.mod h1:5nUXB4xA0kw2df5PRipZDslPJgJut+NjL7D25zPZ/4w= github.com/go-openapi/swag/typeutils v0.24.0 h1:d3szEGzGDf4L2y1gYOSSLeK6h46F+zibnEas2Jm/wIw= github.com/go-openapi/swag/typeutils v0.24.0/go.mod h1:q8C3Kmk/vh2VhpCLaoR2MVWOGP8y7Jc8l82qCTd1DYI= github.com/go-openapi/swag/yamlutils v0.24.0 h1:bhw4894A7Iw6ne+639hsBNRHg9iZg/ISrOVr+sJGp4c= github.com/go-openapi/swag/yamlutils v0.24.0/go.mod h1:DpKv5aYuaGm/sULePoeiG8uwMpZSfReo1HR3Ik0yaG8= github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= 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.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 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/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 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.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/addlicense v1.1.1 h1:jpVf9qPbU8rz5MxKo7d+RMcNHkqxi4YJi/laauX4aAE= github.com/google/addlicense v1.1.1/go.mod h1:Sm/DHu7Jk+T5miFHHehdIjbi4M5+dJDRS3Cq0rncIxA= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/certificate-transparency-go v1.3.2 h1:9ahSNZF2o7SYMaKaXhAumVEzXB2QaayzII9C8rv7v+A= github.com/google/certificate-transparency-go v1.3.2/go.mod h1:H5FpMUaGa5Ab2+KCYsxg6sELw3Flkl7pGZzWdBoYLXs= github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/trillian v1.7.2 h1:EPBxc4YWY4Ak8tcuhyFleY+zYlbCDCa4Sn24e1Ka8Js= github.com/google/trillian v1.7.2/go.mod h1:mfQJW4qRH6/ilABtPYNBerVJAJ/upxHLX81zxNQw05s= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= 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 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.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= 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= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 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.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4= github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA= 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/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/in-toto/attestation v1.1.2 h1:MBFn6lsMq6dptQZJBhalXTcWMb/aJy3V+GX3VYj/V1E= github.com/in-toto/attestation v1.1.2/go.mod h1:gYFddHMZj3DiQ0b62ltNi1Vj5rC879bTmBbrv9CRHpM= github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b h1:ZGiXF8sz7PDk6RgkP+A/SFfUD0ZR/AgG6SpRNEDKZy8= github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b/go.mod h1:hQmNrgofl+IY/8L+n20H6E6PWBBTokdsv+q49j0QhsU= github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY= github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= 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/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 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= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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-20240620165639-de9c06129bec h1:2tTW6cDth2TSgRbAhD7yjZzTQmcN25sDRPEeinR51yQ= github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec/go.mod h1:TmwEoGCwIti7BCeJ9hescZgRtatxRE+A72pCoPfmcfk= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 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/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 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/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= github.com/prometheus/otlptranslator v0.0.2 h1:+1CdeLVrRQ6Psmhnobldo0kTp96Rj80DRXRd5OSnMEQ= github.com/prometheus/otlptranslator v0.0.2/go.mod h1:P8AwMgdD7XEr6QRUJ2QWLpiAZTgTE2UYgjlu3svompI= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4= github.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k= github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g= github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU= 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/protobuf-specs v0.5.0 h1:F8YTI65xOHw70NrvPwJ5PhAzsvTnuJMGLkA4FIkofAY= github.com/sigstore/protobuf-specs v0.5.0/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= github.com/sigstore/rekor v1.4.2 h1:Lx2xby7loviFYdg2C9pB1mESk2QU/LqcYSGsqqZwmg8= github.com/sigstore/rekor v1.4.2/go.mod h1:nX/OYaLqpTeCOuMEt7ELE0+5cVjZWFnFKM+cZ+3hQRA= github.com/sigstore/rekor-tiles v0.1.11 h1:0NAJ2EhD1r6DH95FUuDTqUDd+c31LSKzoXGW5ZCzFq0= github.com/sigstore/rekor-tiles v0.1.11/go.mod h1:eGIeqASh52pgWpmp/j5KZDjmKdVwob7eTYskVVRCu5k= github.com/sigstore/sigstore v1.9.6-0.20250729224751-181c5d3339b3 h1:IEhSeWfhTd0kaBpHUXniWU2Tl5K5OUACN69mi1WGd+8= github.com/sigstore/sigstore v1.9.6-0.20250729224751-181c5d3339b3/go.mod h1:JuqyPRJYnkNl6OTnQiG503EUnKih4P5EV6FUw+1B0iA= github.com/sigstore/sigstore-go v1.1.3 h1:5lKcbXZa5JC7wb/UVywyCulccfYTUju1D5h4tkn+fXE= github.com/sigstore/sigstore-go v1.1.3/go.mod h1:3jKC4IDh7TEVtCSJCjx0lpq5YfJbDJmfp65WsMvY2mg= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.5 h1:qp2VFyKuFQvTGmZwk5Q7m5nE4NwnF9tHwkyz0gtWAck= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.5/go.mod h1:DKlQjjr+GsWljEYPycI0Sf8URLCk4EbGA9qYjF47j4g= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.5 h1:CRZcdYn5AOptStsLRAAACudAVmb1qUbhMlzrvm7ju3o= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.5/go.mod h1:b9rFfITq2fp1M3oJmq6lFFhSrAz5vOEJH1qzbMsZWN4= github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.6-0.20250729224751-181c5d3339b3 h1:a7Yz8C0aBa/LjeiTa9ZLYi9B74GNhFRnUIUdvN6ddVk= github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.6-0.20250729224751-181c5d3339b3/go.mod h1:tRtJzSZ48MXJV9bmS8pkb3mP36PCad/Cs+BmVJ3Z4O4= github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.5 h1:S2ukEfN1orLKw2wEQIUHDDlzk0YcylhcheeZ5TGk8LI= github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.5/go.mod h1:m7sQxVJmDa+rsmS1m6biQxaLX83pzNS7ThUEyjOqkCU= github.com/sigstore/timestamp-authority v1.2.9 h1:L9Fj070/EbMC8qUk8BchkrYCS1BT5i93Bl6McwydkFs= github.com/sigstore/timestamp-authority v1.2.9/go.mod h1:QyRnZchz4o+xdHyK5rvCWacCHxWmpX+mgvJwB1OXcLY= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 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= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 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.2.0 h1:Hmb+Azgd7IKOZeNJFT2C91y+YZ+F+TeloSIvQIaXCQw= github.com/theupdateframework/go-tuf/v2 v2.2.0/go.mod h1:CubcJiJlBHQ2YkA5j9hlBO4B+tHFlLjRbWCJCT7EIKU= github.com/tink-crypto/tink-go-awskms/v2 v2.1.0 h1:N9UxlsOzu5mttdjhxkDLbzwtEecuXmlxZVo/ds7JKJI= github.com/tink-crypto/tink-go-awskms/v2 v2.1.0/go.mod h1:PxSp9GlOkKL9rlybW804uspnHuO9nbD98V/fDX4uSis= github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0 h1:3B9i6XBXNTRspfkTC0asN5W0K6GhOSgcujNiECNRNb0= github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0/go.mod h1:jY5YN2BqD/KSCHM9SqZPIpJNG/u3zwfLXHgws4x2IRw= github.com/tink-crypto/tink-go-hcvault/v2 v2.3.0 h1:6nAX1aRGnkg2SEUMwO5toB2tQkP0Jd6cbmZ/K5Le1V0= github.com/tink-crypto/tink-go-hcvault/v2 v2.3.0/go.mod h1:HOC5NWW1wBI2Vke1FGcRBvDATkEYE7AUDiYbXqi2sBw= github.com/tink-crypto/tink-go/v2 v2.5.0 h1:B8KLF6AofxdBIE4UJIaFbmoj5/1ehEtt7/MmzfI4Zpw= github.com/tink-crypto/tink-go/v2 v2.5.0/go.mod h1:2WbBA6pfNsAfBwDCggboaHeB2X29wkU8XHtGwh2YIk8= 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/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c h1:5a2XDQ2LiAUV+/RjckMyq9sXudfrPSuCY4FuPC1NyAw= github.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c/go.mod h1:g85IafeFJZLxlzZCDRu4JLpfS7HKzR+Hw9qRh3bVzDI= github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= github.com/transparency-dev/tessera v1.0.1-0.20251104110637-ba6c65c4ae73 h1:XVu3/5rI/3okvRH7uFDiiB6/g4HM7eZN1tTvOEU2vl8= github.com/transparency-dev/tessera v1.0.1-0.20251104110637-ba6c65c4ae73/go.mod h1:hxs+XmMCxM44pskCyfRFhEuUkpETNcfl6fTNOFsh7O8= 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.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.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU= github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/detectors/gcp v1.38.0 h1:ZoYbqX7OaA/TAikspPl3ozPI6iY6LiIY9I8cUfm+pJs= go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel/exporters/prometheus v0.60.0 h1:cGtQxGvZbnrWdC2GyjZi0PDKVSLWP/Jocix3QWfXtbo= go.opentelemetry.io/otel/exporters/prometheus v0.60.0/go.mod h1:hkd1EekxNo69PTV4OWFGZcKQiIqg0RfuWExcPKFvepk= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.step.sm/crypto v0.73.0 h1:SNFpslZJa+kTNADpWYJJeMsQqzwDiuxFq0ei5OPLIUg= go.step.sm/crypto v0.73.0/go.mod h1:pw2MKw7aPgx3bVjVwYrKbpMIawLRwth/5cyhZf6QnBM= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= 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.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= 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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 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.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= 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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/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.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 h1:dHQOQddU4YHS5gY33/6klKjq7Gp3WwMyOXGNp5nzRj8= golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE= 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= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= 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-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= google.golang.org/api v0.254.0 h1:jl3XrGj7lRjnlUvZAbAdhINTLbsg5dbjmR90+pTQvt4= google.golang.org/api v0.254.0/go.mod h1:5BkSURm3D9kAqjGvBNgf0EcbX6Rnrf6UArKkwBzAyqQ= 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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20250922171735-9219d122eba9 h1:LvZVVaPE0JSqL+ZWb6ErZfnEOKIqqFWUJE2D0fObSmc= google.golang.org/genproto v0.0.0-20250922171735-9219d122eba9/go.mod h1:QFOrLhdAe2PsTp3vQY4quuLKTi9j3XG3r6JPPaw7MSc= google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 h1:8XJ4pajGwOlasW+L13MnEGA8W4115jJySQtVfS2/IBU= google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4/go.mod h1:NnuHhy+bxcg30o7FnVAZbXsPHUDQ9qKWAQKCD7VxFtk= google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk= google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 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.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/release-utils v0.12.2 h1:H06v3FuLElAkf7Ikkd9ll8hnhdtQ+OgktJAni3iIAl8= sigs.k8s.io/release-utils v0.12.2/go.mod h1:Ab9Lb/FpGUw4lUXj1QYbUcF2TRzll+GS7Md54W1G7sA= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= golang-github-sigstore-rekor-tiles-2.0.1/internal/000077500000000000000000000000001511162205500221465ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/internal/algorithmregistry/000077500000000000000000000000001511162205500257255ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/internal/algorithmregistry/algorithmregistry.go000066400000000000000000000072101511162205500320330ustar00rootroot00000000000000// Copyright 2025 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 algorithmregistry import ( "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" "fmt" "reflect" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" "github.com/sigstore/sigstore/pkg/signature" ) var ( // AllowedClientSigningAlgorithms is the default set of supported signing // algorithms for log entry signatures. AllowedClientSigningAlgorithms = []v1.PublicKeyDetails{ v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_2048_SHA256, v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_3072_SHA256, v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_4096_SHA256, v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384, v1.PublicKeyDetails_PKIX_ECDSA_P521_SHA_512, v1.PublicKeyDetails_PKIX_ED25519, v1.PublicKeyDetails_PKIX_ED25519_PH, } ) type UnsupportedAlgorithm struct { Pub crypto.PublicKey Alg crypto.Hash } func (e *UnsupportedAlgorithm) Error() string { hash := e.Alg.String() switch v := e.Pub.(type) { case *rsa.PublicKey: bits := v.Size() * 8 return fmt.Sprintf("unsupported entry algorithm for RSA key, size %d, digest %s", bits, hash) case *ecdsa.PublicKey: name := v.Curve.Params().Name return fmt.Sprintf("unsupported entry algorithm for ECDSA key, curve %s, digest %s", name, hash) case ed25519.PublicKey: return fmt.Sprintf("unsupported entry algorithm for Ed25519 key, digest %s", hash) default: return fmt.Sprintf("unsupported key type %s, digest %s", reflect.TypeOf(v), hash) } } // AlgorithmRegistry accepts a list of algorithms as strings, parses and formats them into a registry. func AlgorithmRegistry(algorithmOptions []string) (*signature.AlgorithmRegistryConfig, error) { var algorithms []v1.PublicKeyDetails if algorithmOptions == nil { algorithms = AllowedClientSigningAlgorithms } else { for _, a := range algorithmOptions { algorithm, err := signature.ParseSignatureAlgorithmFlag(a) if err != nil { return nil, fmt.Errorf("parsing signature algorithm flag: %w", err) } algorithms = append(algorithms, algorithm) } } algorithmsStr := make([]string, len(algorithms)) var err error for i, a := range algorithms { algorithmsStr[i], err = signature.FormatSignatureAlgorithmFlag(a) if err != nil { return nil, fmt.Errorf("formatting signature algorithm flag: %w", err) } } algorithmRegistry, err := signature.NewAlgorithmRegistryConfig(algorithms) if err != nil { return nil, fmt.Errorf("getting algorithm registry: %w", err) } return algorithmRegistry, nil } // CheckEntryAlgorithms checks that the combination public key and message // digest algorithm are allowed given an algorithm registry. func CheckEntryAlgorithms(pubKey crypto.PublicKey, alg crypto.Hash, algorithmRegistry *signature.AlgorithmRegistryConfig) (bool, error) { // Check if all the verifiers public keys (together with the // artifactHashValue) are allowed according to the policy isPermitted, err := algorithmRegistry.IsAlgorithmPermitted(pubKey, alg) if err != nil { return false, fmt.Errorf("checking if algorithm is permitted: %w", err) } if !isPermitted { return false, nil } return true, nil } golang-github-sigstore-rekor-tiles-2.0.1/internal/algorithmregistry/algorithmregistry_test.go000066400000000000000000000124321511162205500330740ustar00rootroot00000000000000// Copyright 2025 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 algorithmregistry import ( "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" "strings" "testing" "github.com/sigstore/sigstore/pkg/signature" "github.com/stretchr/testify/assert" ) func TestAlgorithmRegistry(t *testing.T) { tests := []struct { name string algorithmOptions []string wantErr bool }{ { name: "defaults", algorithmOptions: nil, }, { name: "valid algorithms", algorithmOptions: []string{ "ecdsa-sha2-384-nistp384", "ecdsa-sha2-512-nistp521", "ed25519", "rsa-sign-pkcs1-3072-sha256", "rsa-sign-pkcs1-4096-sha256", }, }, { name: "invalid algorithms", algorithmOptions: []string{ "foo", "bar", }, wantErr: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { got, gotErr := AlgorithmRegistry(test.algorithmOptions) if test.wantErr { assert.Error(t, gotErr) return } assert.NoError(t, gotErr) assert.NotNil(t, got) }) } } func generateRSAKey(t *testing.T, bits int) *rsa.PublicKey { t.Helper() priv, err := rsa.GenerateKey(rand.Reader, bits) if err != nil { t.Fatalf("failed to generate RSA key: %v", err) } return &priv.PublicKey } func generateECDSAKey(t *testing.T, curve elliptic.Curve) *ecdsa.PublicKey { t.Helper() priv, err := ecdsa.GenerateKey(curve, rand.Reader) if err != nil { t.Fatalf("failed to generate ECDSA key: %v", err) } return &priv.PublicKey } func generateEd25519Key(t *testing.T) ed25519.PublicKey { t.Helper() pub, _, err := ed25519.GenerateKey(rand.Reader) if err != nil { t.Fatalf("failed to generate Ed25519 key: %v", err) } return pub } func TestUnsupportedAlgorithm_Error(t *testing.T) { rsaKey2048 := generateRSAKey(t, 2048) rsaKey4096 := generateRSAKey(t, 4096) ecdsaKeyP256 := generateECDSAKey(t, elliptic.P256()) ecdsaKeyP384 := generateECDSAKey(t, elliptic.P384()) ed25519Key := generateEd25519Key(t) testCases := []struct { name string err UnsupportedAlgorithm want string }{ { name: "RSA 2048 with SHA256", err: UnsupportedAlgorithm{ Pub: rsaKey2048, Alg: crypto.SHA256, }, want: "unsupported entry algorithm for RSA key, size 2048, digest SHA-256", }, { name: "RSA 4096 with SHA512", err: UnsupportedAlgorithm{ Pub: rsaKey4096, Alg: crypto.SHA512, }, want: "unsupported entry algorithm for RSA key, size 4096, digest SHA-512", }, { name: "ECDSA P256 with SHA256", err: UnsupportedAlgorithm{ Pub: ecdsaKeyP256, Alg: crypto.SHA256, }, want: "unsupported entry algorithm for ECDSA key, curve P-256, digest SHA-256", }, { name: "ECDSA P384 with SHA384", err: UnsupportedAlgorithm{ Pub: ecdsaKeyP384, Alg: crypto.SHA384, }, want: "unsupported entry algorithm for ECDSA key, curve P-384, digest SHA-384", }, { name: "Ed25519 with SHA256", err: UnsupportedAlgorithm{ Pub: ed25519Key, Alg: crypto.SHA256, }, want: "unsupported entry algorithm for Ed25519 key, digest SHA-256", }, { name: "nil public key", err: UnsupportedAlgorithm{ Pub: nil, Alg: crypto.SHA256, }, want: "unsupported key type %!s(), digest SHA-256", }, { name: "unknown public key type", err: UnsupportedAlgorithm{ Pub: int(1), Alg: crypto.SHA256, }, want: "unsupported key type int, digest SHA-256", }, { name: "zero hash value", err: UnsupportedAlgorithm{ Pub: rsaKey2048, Alg: crypto.Hash(0), }, want: "unsupported entry algorithm for RSA key, size 2048, digest unknown hash value 0", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { got := tc.err.Error() if got != tc.want { t.Errorf("Error() mismatch:\ngot = %q\nwant= %q", got, tc.want) } }) } } func TestUnsupportedAlgorithmForAllAllowedAlgs(t *testing.T) { for _, a := range AllowedClientSigningAlgorithms { details, err := signature.GetAlgorithmDetails(a) if err != nil { t.Fatal(err) } var pub crypto.PublicKey switch details.GetKeyType() { case signature.RSA: priv, err := rsa.GenerateKey(rand.Reader, 2048) // bit size doesn't matter for error if err != nil { t.Fatal(err) } pub = priv.Public() case signature.ECDSA: priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // curve doesn't matter for error if err != nil { t.Fatal(err) } pub = priv.Public() case signature.ED25519: pub, _, err = ed25519.GenerateKey(rand.Reader) if err != nil { t.Fatal(err) } } hash := details.GetHashType() uaErr := UnsupportedAlgorithm{Pub: pub, Alg: hash} if strings.Contains(uaErr.Error(), "unsupported key type") { t.Errorf("%v unexpected unsupported", a) } } } golang-github-sigstore-rekor-tiles-2.0.1/internal/safeint/000077500000000000000000000000001511162205500235775ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/internal/safeint/safeint.go000066400000000000000000000037511511162205500255650ustar00rootroot00000000000000// // Copyright 2025 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 safeint import ( "fmt" "math" ) // SafeInt64 holds equivalent int64 and uint64 integers. type SafeInt64 struct { u uint64 i int64 } // NewSafeInt64 returns a safeInt64 struct as long as the number is either an // int64 or uint64 and the value can safely be converted in either direction // without overflowing, i.e. is not greater than MaxInt64 and not negative. // // This has implications for its usage, e.g. when used for the tree size, a new // tree must be created to replace the old tree before its size reaches // math.MaxInt64. // // This is needed for compatibility with TransparencyLogEntry // (https://github.com/sigstore/protobuf-specs/blob/e871d3e6fd06fa73a1524ef0efaf1452d3304cf6/protos/sigstore_rekor.proto#L86-L138). func NewSafeInt64(number any) (*SafeInt64, error) { var result SafeInt64 switch n := number.(type) { case uint64: if n > math.MaxInt64 { return nil, fmt.Errorf("exceeded max int64: %d", n) } result.u = n result.i = int64(n) //nolint:gosec case int64: if n < 0 { return nil, fmt.Errorf("negative integer: %d", n) } result.u = uint64(n) //nolint:gosec result.i = n default: return nil, fmt.Errorf("only uint64 and int64 are supported") } return &result, nil } // U returns the uint64 value of the integer. func (s *SafeInt64) U() uint64 { return s.u } // I returns the int64 value of the integer. func (s *SafeInt64) I() int64 { return s.i } golang-github-sigstore-rekor-tiles-2.0.1/internal/safeint/safeint_test.go000066400000000000000000000035241511162205500266220ustar00rootroot00000000000000// // Copyright 2025 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 safeint import ( "fmt" "math" "testing" "github.com/stretchr/testify/assert" ) func TestNewSafeInt64(t *testing.T) { tests := []struct { name string number any expect *SafeInt64 expectErr error }{ { name: "small uint", number: uint64(42), expect: &SafeInt64{u: uint64(42), i: int64(42)}, }, { name: "small positive int", number: int64(42), expect: &SafeInt64{u: uint64(42), i: int64(42)}, }, { name: "too large uint", number: uint64(math.MaxUint64 - 77), expect: nil, expectErr: fmt.Errorf("exceeded max int64: %d", uint64(math.MaxUint64-77)), }, { name: "too small int", number: int64(-1), expect: nil, expectErr: fmt.Errorf("negative integer: -1"), }, { name: "wrong type", number: 1, expect: nil, expectErr: fmt.Errorf("only uint64 and int64 are supported"), }, { name: "really wrong type", number: "forty-two", expect: nil, expectErr: fmt.Errorf("only uint64 and int64 are supported"), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { got, gotErr := NewSafeInt64(test.number) assert.Equal(t, test.expect, got) assert.Equal(t, test.expectErr, gotErr) }) } } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/000077500000000000000000000000001511162205500234545ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/internal/server/grpc.go000066400000000000000000000107051511162205500247410ustar00rootroot00000000000000// Copyright 2025 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. // 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 server import ( "crypto/tls" "fmt" "log/slog" "net" "os" "os/signal" "runtime/debug" "sync" "syscall" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/keepalive" "google.golang.org/grpc/reflection" "google.golang.org/grpc/status" ) type grpcServer struct { *grpc.Server serverEndpoint string } // newGRPCServer starts a new grpc server and registers the services. func newGRPCServer(config *GRPCConfig, server rekorServer) *grpcServer { var opts []grpc.ServerOption grpcPanicRecoveryHandler := func(p any) (err error) { getMetrics().panicsTotal.Inc() slog.Error("recovered from panic", "panic", p, "stack", debug.Stack()) return status.Errorf(codes.Internal, "%s", p) } opts = append(opts, grpc.ChainUnaryInterceptor( logging.UnaryServerInterceptor(interceptorLogger(slog.Default()), loggingOpts(config.logLevel, config.requestResponseLogging)...), getMetrics().serverMetrics.UnaryServerInterceptor(), recovery.UnaryServerInterceptor(recovery.WithRecoveryHandler(grpcPanicRecoveryHandler)), // panic handler should be last ), grpc.ConnectionTimeout(config.timeout), grpc.KeepaliveParams(keepalive.ServerParameters{MaxConnectionIdle: config.timeout}), grpc.MaxRecvMsgSize(config.maxMessageSize), grpc.StatsHandler(NewGrpcStatsHandler()), ) if config.HasTLS() { creds, err := loadTLSCredentials(config.certFile, config.keyFile) if err != nil { slog.Error("failed to load TLS credentials", "error", err) os.Exit(1) } opts = append(opts, grpc.Creds(creds)) } s := grpc.NewServer(opts...) pb.RegisterRekorServer(s, server) grpc_health_v1.RegisterHealthServer(s, server) reflection.Register(s) getMetrics().serverMetrics.InitializeMetrics(s) getMetrics().InitializeCustomGrpcMetrics(s) return &grpcServer{ Server: s, serverEndpoint: config.GRPCTarget(), } } func (gs *grpcServer) start(wg *sync.WaitGroup) { slog.Info("Starting gRPC server", "address", gs.serverEndpoint) lis, err := net.Listen("tcp", gs.serverEndpoint) if err != nil { slog.Error("failed to create listener:", "error", err) os.Exit(1) } // update the endpoint to standardize gs.serverEndpoint = lis.Addr().String() waitToClose := make(chan struct{}) go func() { // capture interrupts and shutdown Server sigint := make(chan os.Signal, 1) signal.Notify(sigint, syscall.SIGINT, syscall.SIGTERM) <-sigint gs.GracefulStop() close(waitToClose) slog.Info("stopped gRPC server") }() wg.Add(1) go func() { if err := gs.Serve(lis); err != nil { slog.Error("error shutting down gRPC server", "error", err) os.Exit(1) } <-waitToClose wg.Done() slog.Info("gRPC Server shutdown") }() } func loadTLSCredentials(certFile, keyFile string) (credentials.TransportCredentials, error) { cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { return nil, fmt.Errorf("failed to load key pair: %w", err) } tlsConfig := &tls.Config{ Certificates: []tls.Certificate{cert}, MinVersion: tls.VersionTLS13, } return credentials.NewTLS(tlsConfig), nil } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/grpc_test.go000066400000000000000000000135421511162205500260020ustar00rootroot00000000000000// Copyright 2025 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 server import ( "context" "crypto/tls" "crypto/x509" "os" "testing" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" "google.golang.org/genproto/googleapis/api/httpbody" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/emptypb" ) func TestServe_grpcSmoke(t *testing.T) { // To debug set slog to output to stdout // slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil))) server := MockServer{} server.Start(t) defer server.Stop(t) // check if we can hit grpc endpoints conn, err := grpc.NewClient( server.gc.GRPCTarget(), grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { t.Fatal(err) } client := pb.NewRekorClient(conn) defer conn.Close() defer server.Stop(t) testEndpoints(t, client) } func TestServe_grpcTLS(t *testing.T) { server := MockServer{} server.StartTLS(t) defer server.Stop(t) certPool := x509.NewCertPool() pemServerCert, err := os.ReadFile(server.gc.certFile) if err != nil { t.Fatalf("failed to read server certificate: %v", err) } if !certPool.AppendCertsFromPEM(pemServerCert) { t.Fatal("failed to add server certificate to pool") } creds := credentials.NewTLS(&tls.Config{ RootCAs: certPool, ServerName: "localhost", }) conn, err := grpc.NewClient( server.gc.GRPCTarget(), grpc.WithTransportCredentials(creds)) if err != nil { t.Fatal(err) } defer conn.Close() client := pb.NewRekorClient(conn) testEndpoints(t, client) } func checkGRPCCreateEntry(t *testing.T, client pb.RekorClient) { if entry, err := client.CreateEntry(context.Background(), &pb.CreateEntryRequest{}); err != nil { t.Fatal(err) } else if !proto.Equal(entry, &testEntry) { t.Errorf("got entry %q, want %q", entry, &testEntry) } } func checkGRPC(t *testing.T, resp *httpbody.HttpBody, err error, expectedBody string) { if err != nil { t.Fatal(err) } if string(resp.Data) != expectedBody { t.Errorf("got body %q, want %q", resp.Data, expectedBody) } } func testEndpoints(t *testing.T, client pb.RekorClient) { t.Helper() checkGRPCCreateEntry(t, client) t.Run("get checkpoint", func(t *testing.T) { body, err := client.GetCheckpoint(context.Background(), &emptypb.Empty{}) checkGRPC(t, body, err, "test-checkpoint") }) t.Run("get tile", func(t *testing.T) { body, err := client.GetTile(context.Background(), &pb.TileRequest{L: 1, N: "002"}) checkGRPC(t, body, err, "test-tile:1,2,0") }) t.Run("get tile with larger index", func(t *testing.T) { body, err := client.GetTile(context.Background(), &pb.TileRequest{L: 1, N: "x123/456"}) checkGRPC(t, body, err, "test-tile:1,123456,0") }) t.Run("get partial tile", func(t *testing.T) { body, err := client.GetTile(context.Background(), &pb.TileRequest{L: 1, N: "123.p/45"}) checkGRPC(t, body, err, "test-tile:1,123,45") }) t.Run("get tile with larger index and partial", func(t *testing.T) { body, err := client.GetTile(context.Background(), &pb.TileRequest{L: 1, N: "x123/456.p/7"}) checkGRPC(t, body, err, "test-tile:1,123456,7") }) t.Run("get entry bundle", func(t *testing.T) { body, err := client.GetEntryBundle(context.Background(), &pb.EntryBundleRequest{N: "001"}) checkGRPC(t, body, err, "test-entries:1,0") }) t.Run("get entry bundle with larger index", func(t *testing.T) { body, err := client.GetEntryBundle(context.Background(), &pb.EntryBundleRequest{N: "x123/456"}) checkGRPC(t, body, err, "test-entries:123456,0") }) t.Run("get partial entry bundle", func(t *testing.T) { body, err := client.GetEntryBundle(context.Background(), &pb.EntryBundleRequest{N: "123.p/45"}) checkGRPC(t, body, err, "test-entries:123,45") }) t.Run("get entry bundle with larger index and partial", func(t *testing.T) { body, err := client.GetEntryBundle(context.Background(), &pb.EntryBundleRequest{N: "x123/456.p/7"}) checkGRPC(t, body, err, "test-entries:123456,7") }) } func TestLoadTLSCredentials(t *testing.T) { certFile, keyFile := generateSelfSignedCert(t) t.Run("successful credentials loading", func(t *testing.T) { creds, err := loadTLSCredentials(certFile, keyFile) if err != nil { t.Fatalf("loadTLSCredentials failed: %v", err) } if creds == nil { t.Fatal("loadTLSCredentials returned nil credentials") } }) t.Run("invalid certificate path", func(t *testing.T) { _, err := loadTLSCredentials("non-existent-cert.pem", keyFile) if err == nil { t.Fatal("expected error for invalid certificate path, got nil") } }) t.Run("invalid key path", func(t *testing.T) { _, err := loadTLSCredentials(certFile, "non-existent-key.pem") if err == nil { t.Fatal("expected error for invalid key path, got nil") } }) t.Run("invalid certificate content", func(t *testing.T) { invalidCertFile, err := os.CreateTemp("", "invalid-cert-*.pem") if err != nil { t.Fatalf("failed to create temp file: %v", err) } defer os.Remove(invalidCertFile.Name()) if err := os.WriteFile(invalidCertFile.Name(), []byte("invalid certificate content"), 0644); err != nil { t.Fatalf("failed to write invalid cert: %v", err) } _, err = loadTLSCredentials(invalidCertFile.Name(), keyFile) if err == nil { t.Fatal("expected error for invalid certificate content, got nil") } }) } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/grpcconfig.go000066400000000000000000000060761511162205500261350ustar00rootroot00000000000000// Copyright 2025 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 server import ( "log/slog" "strconv" "time" ) const ( // defaultMaxSize is the default max size in bytes of payloads to both the http and grpc servers. defaultMaxSize = 4 * 1024 * 1024 // 4MB https://github.com/grpc/grpc-go/blob/cdbdb759dd67c89544f9081f854c284493b5461c/server.go#L59C39-L59C54. // defaultTimeout is the default connection and request timeout for both the http and grpc servers. defaultTimeout = 60 * time.Second ) // GRPCConfig contains options for the GRPC server from the CLI. type GRPCConfig struct { port int host string timeout time.Duration maxMessageSize int certFile string keyFile string logLevel slog.Level requestResponseLogging bool } type GRPCOption func(config *GRPCConfig) // NewGRPCConfig creates a new GRPCConfig with some default options. func NewGRPCConfig(options ...func(config *GRPCConfig)) *GRPCConfig { config := &GRPCConfig{ port: 8081, host: "localhost", timeout: defaultTimeout, maxMessageSize: defaultMaxSize, logLevel: slog.LevelInfo, requestResponseLogging: false, } for _, opt := range options { opt(config) } return config } func WithGRPCPort(port int) GRPCOption { return func(config *GRPCConfig) { config.port = port } } func WithGRPCHost(host string) GRPCOption { return func(config *GRPCConfig) { config.host = host } } // WithGRPCTimeout specifies the value to be used in grpc.ConnectionTimeout() // and keepalive.ServerParameters.MaxConnectionIdle. func WithGRPCTimeout(timeout time.Duration) GRPCOption { return func(config *GRPCConfig) { config.timeout = timeout } } // WithGRPCMaxMessageSize specifies the maximum size in bytes for a grpc message. func WithGRPCMaxMessageSize(maxMessageSize int) GRPCOption { return func(config *GRPCConfig) { config.maxMessageSize = maxMessageSize } } func (gc GRPCConfig) GRPCTarget() string { return gc.host + ":" + strconv.Itoa(gc.port) } func (gc GRPCConfig) HasTLS() bool { return gc.certFile != "" && gc.keyFile != "" } func WithTLSCredentials(certFile, keyFile string) GRPCOption { return func(config *GRPCConfig) { config.certFile = certFile config.keyFile = keyFile } } func WithGRPCLogLevel(logLevel slog.Level, requestResponseLogging bool) GRPCOption { return func(config *GRPCConfig) { config.logLevel = logLevel config.requestResponseLogging = requestResponseLogging } } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/grpcconfig_test.go000066400000000000000000000071301511162205500271640ustar00rootroot00000000000000// Copyright 2025 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 server import ( "testing" "time" ) func TestNewGRPCConfig(t *testing.T) { // Test default configuration config := NewGRPCConfig() if config.host != "localhost" { t.Errorf("Expected host to be localhost, got %s", config.host) } if config.port != 8081 { t.Errorf("Expected port to be 8081, got %d", config.port) } if config.timeout != 60*time.Second { t.Errorf("Expected timeout to be 60 seconds, got %v", config.timeout) } if config.maxMessageSize != 4*1024*1024 { t.Errorf("Expected maxMessageSize) to be 4MB, got %d", config.maxMessageSize) } } func TestWithGRPCPort(t *testing.T) { config := NewGRPCConfig(WithGRPCPort(9000)) if config.port != 9000 { t.Errorf("Expected port to be 9000, got %d", config.port) } } func TestWithGRPCHost(t *testing.T) { config := NewGRPCConfig(WithGRPCHost("example.com")) if config.host != "example.com" { t.Errorf("Expected host to be example.com, got %s", config.host) } } func TestWithGRPCTimeout(t *testing.T) { config := NewGRPCConfig(WithGRPCTimeout(10 * time.Second)) if config.timeout != 10*time.Second { t.Errorf("Expected timeout to be 10 seconds, got %v", config.timeout) } } func TestWithGRPCMaxMessageSize(t *testing.T) { config := NewGRPCConfig(WithGRPCMaxMessageSize(8 * 1024 * 1024)) if config.maxMessageSize != 8*1024*1024 { t.Errorf("Expected maxMessageSize to be 8MB, got %d", config.maxMessageSize) } } func TestMultipleGRPCOptions(t *testing.T) { config := NewGRPCConfig( WithGRPCPort(9090), WithGRPCHost("test.example.com"), WithGRPCTimeout(5*time.Second), WithGRPCMaxMessageSize(2*1024*1024), ) if config.port != 9090 { t.Errorf("Expected port to be 9090, got %d", config.port) } if config.host != "test.example.com" { t.Errorf("Expected host to be test.example.com, got %s", config.host) } if config.timeout != 5*time.Second { t.Errorf("Expected timeout to be 5 seconds, got %v", config.timeout) } if config.maxMessageSize != 2*1024*1024 { t.Errorf("Expected maxMessageSize to be 2MB, got %d", config.maxMessageSize) } } func TestWithTLSCredentials(t *testing.T) { config := NewGRPCConfig(WithTLSCredentials("cert.pem", "key.pem")) if config.certFile != "cert.pem" { t.Errorf("Expected certFile to be cert.pem, got %s", config.certFile) } if config.keyFile != "key.pem" { t.Errorf("Expected keyFile to be key.pem, got %s", config.keyFile) } } func TestGrpcHasTLS(t *testing.T) { config := NewGRPCConfig() if config.HasTLS() { t.Error("Expected HasTLS to return false when no TLS credentials are set") } config = NewGRPCConfig(func(c *GRPCConfig) { c.certFile = "cert.pem" }) if config.HasTLS() { t.Error("Expected HasTLS to return false when only certFile is set") } config = NewGRPCConfig(func(c *GRPCConfig) { c.keyFile = "key.pem" }) if config.HasTLS() { t.Error("Expected HasTLS to return false when only keyFile is set") } config = NewGRPCConfig(WithTLSCredentials("cert.pem", "key.pem")) if !config.HasTLS() { t.Error("Expected HasTLS to return true when both certFile and keyFile are set") } } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/grpcstats.go000066400000000000000000000043371511162205500260240ustar00rootroot00000000000000// Copyright 2025 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 server import ( "context" "strings" "google.golang.org/grpc/stats" ) // GrpcStatsHandler consumes grpc stats and converts them to metrics type GrpcStatsHandler struct{} func (st *GrpcStatsHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo) context.Context { // this is a no-op we don't care about connections for now return ctx } func (st *GrpcStatsHandler) HandleConn(_ context.Context, _ stats.ConnStats) { // this is a no-op we don't care about connections for now } type rpcStatCtxKey struct{} func (st *GrpcStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context { // add the rpc info to the context so we can access the method name later return context.WithValue(ctx, rpcStatCtxKey{}, info) } // HandleRPC processes the RPC stats. Note: All stat fields are read-only. func (st *GrpcStatsHandler) HandleRPC(ctx context.Context, stat stats.RPCStats) { info, ok := ctx.Value(rpcStatCtxKey{}).(*stats.RPCTagInfo) if !ok { return } service, method := splitFullMethodName(info.FullMethodName) switch t := stat.(type) { case *stats.InPayload: size := t.WireLength getMetrics().grpcRequestSize.WithLabelValues(service, method, "payload").Observe(float64(size)) default: // do nothing } } func NewGrpcStatsHandler() *GrpcStatsHandler { return &GrpcStatsHandler{} } // splitFullMethodName returns the service and method name of the RPC. func splitFullMethodName(fullMethod string) (string, string) { fullMethod = strings.TrimPrefix(fullMethod, "/") // remove leading slash if i := strings.Index(fullMethod, "/"); i >= 0 { return fullMethod[:i], fullMethod[i+1:] } return "unknown", "unknown" } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/http.go000066400000000000000000000161611511162205500247670ustar00rootroot00000000000000// Copyright 2025 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 server import ( "context" "crypto/tls" "errors" "log/slog" "net" "net/http" "os" "os/signal" "strconv" "strings" "sync" "syscall" clog "github.com/chainguard-dev/clog/gcp" "github.com/prometheus/client_golang/prometheus/promhttp" "google.golang.org/protobuf/encoding/protojson" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/protobuf/proto" ) const ( httpStatusCodeHeader = "x-http-code" httpErrorMessageHeader = "x-http-error-message" ) type httpProxy struct { *http.Server serverEndpoint string } // newHTTProxy creates a mux for each of the service grpc methods, including the grpc heatlhcheck. func newHTTPProxy(ctx context.Context, config *HTTPConfig, grpcServer *grpcServer) *httpProxy { // configure a custom marshaler to fail on unknown fields strictMarshaler := runtime.HTTPBodyMarshaler{ Marshaler: &runtime.JSONPb{ MarshalOptions: protojson.MarshalOptions{ EmitUnpopulated: true, }, UnmarshalOptions: protojson.UnmarshalOptions{ DiscardUnknown: false, }, }, } var opts []grpc.DialOption if config.HasGRPCTLS() { // InsecureSkipVerify is only used for the HTTP server to call the TLS-enabled local gRPC endpoint. opts = []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}))} // #nosec G402 } else { opts = []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} } // gRPC client connection so the HTTP mux's healthz endpoint can reach the gRPC healthcheck service. // See https://grpc-ecosystem.github.io/grpc-gateway/docs/operations/health_check/#adding-healthz-endpoint-to-runtimeservemux. cc, err := grpc.NewClient(grpcServer.serverEndpoint, opts...) if err != nil { slog.Error("failed to connect to grpc server", "error", err) os.Exit(1) } mux := runtime.NewServeMux( runtime.WithMarshalerOption(runtime.MIMEWildcard, &strictMarshaler), runtime.WithErrorHandler(customHTTPErrorHandler), runtime.WithForwardResponseOption(httpResponseModifier), runtime.WithHealthzEndpoint(grpc_health_v1.NewHealthClient(cc)), // localhost:[port]/healthz ) err = pb.RegisterRekorHandlerFromEndpoint(ctx, mux, grpcServer.serverEndpoint, opts) if err != nil { slog.Error("failed to register gateway:", "errors", err) os.Exit(1) } metrics := getMetrics() handler := promhttp.InstrumentMetricHandler(metrics.reg, mux) handler = promhttp.InstrumentHandlerDuration(metrics.httpLatency, handler) handler = promhttp.InstrumentHandlerCounter(metrics.httpRequestsCount, handler) handler = promhttp.InstrumentHandlerRequestSize(metrics.httpRequestSize, handler) handler = clog.WithCloudTraceContext(handler) handler = http.MaxBytesHandler(handler, int64(config.maxRequestBodySize)) server := &http.Server{ Addr: config.HTTPTarget(), Handler: handler, ReadTimeout: config.timeout, ReadHeaderTimeout: config.timeout, WriteTimeout: config.timeout, IdleTimeout: config.timeout, // by default MaxHeaderBytes is 1MB, so no need to set. } if config.HasTLS() { cert, err := tls.LoadX509KeyPair(config.certFile, config.keyFile) if err != nil { slog.Error("failed to load TLS certificates:", "errors", err) os.Exit(1) } server.TLSConfig = &tls.Config{ Certificates: []tls.Certificate{cert}, MinVersion: tls.VersionTLS13, } } return &httpProxy{ Server: server, serverEndpoint: config.HTTPTarget(), } } func (hp *httpProxy) start(wg *sync.WaitGroup) { lis, err := net.Listen("tcp", hp.serverEndpoint) if err != nil { slog.Error("failed to create listener:", "errors", err) os.Exit(1) } hp.serverEndpoint = lis.Addr().String() var protocol string if hp.TLSConfig != nil { protocol = "HTTPS" slog.Info("starting HTTPS proxy", "address", hp.serverEndpoint) } else { protocol = "HTTP" slog.Info("starting HTTP proxy", "address", hp.serverEndpoint) } waitToClose := make(chan struct{}) go func() { // capture interrupts and shutdown Server sigint := make(chan os.Signal, 1) signal.Notify(sigint, syscall.SIGINT, syscall.SIGTERM) <-sigint if err := hp.Shutdown(context.Background()); err != nil { slog.Info("http server shutdown returned an error", "error", err) } close(waitToClose) slog.Info("stopped server", "protocol", protocol) }() wg.Add(1) go func() { var err error if hp.TLSConfig != nil { err = hp.ServeTLS(lis, "", "") // skip cert and key as they are already set in TLSConfig } else { err = hp.Serve(lis) } if err != nil && !errors.Is(err, http.ErrServerClosed) { slog.Error("could not start server", "protocol", protocol, "error", err) os.Exit(1) } <-waitToClose wg.Done() slog.Info("server shutdown complete", "protocol", protocol) }() } func httpResponseModifier(ctx context.Context, w http.ResponseWriter, _ proto.Message) error { md, ok := runtime.ServerMetadataFromContext(ctx) if !ok { return nil } for header := range w.Header() { if strings.HasPrefix(header, "Grpc-") { delete(w.Header(), header) } } // set http status code if vals := md.HeaderMD.Get(httpStatusCodeHeader); len(vals) > 0 { code, err := strconv.Atoi(vals[0]) if err != nil { return err } // delete the headers to not expose any grpc-metadata in http response delete(md.HeaderMD, httpStatusCodeHeader) w.WriteHeader(code) } // set the response body if needed if vals := md.HeaderMD.Get(httpErrorMessageHeader); len(vals) > 0 { msg := vals[0] delete(md.HeaderMD, httpErrorMessageHeader) _, err := w.Write([]byte(msg + "\n")) if err != nil { return err } } return nil } // customHTTPErrorHandler remaps gRPC errors codes to HTTP status codes provided in an internal header. // This is needed to remap a gRPC code, such as Unimplemented, in certain instances to a more precise // HTTP status code. func customHTTPErrorHandler(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) { md, ok := runtime.ServerMetadataFromContext(ctx) if !ok { runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w, r, err) return } if vals := md.HeaderMD.Get(httpStatusCodeHeader); len(vals) == 0 { runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w, r, err) return } err = httpResponseModifier(ctx, w, nil) if err != nil { runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w, r, err) } } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/http_test.go000066400000000000000000000126621511162205500260300ustar00rootroot00000000000000// Copyright 2025 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 server import ( "bytes" "crypto/tls" "crypto/x509" "fmt" "io" "net/http" "os" "strings" "testing" pbs "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" ) var endpointTests = []struct { path string expectedBody string }{ {"/api/v2/checkpoint", "test-checkpoint"}, {"/api/v2/tile/1/002", "test-tile:1,2,0"}, {"/api/v2/tile/1/x123/456", "test-tile:1,123456,0"}, {"/api/v2/tile/1/002.p/3", "test-tile:1,2,3"}, {"/api/v2/tile/entries/001", "test-entries:1,0"}, {"/api/v2/tile/entries/x123/456", "test-entries:123456,0"}, {"/api/v2/tile/entries/001.p/2", "test-entries:1,2"}, {"/healthz", `{"status":"SERVING"}` + "\n"}, } func TestServe_httpSmoke(t *testing.T) { // To debug set slog to output to stdout // slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil))) server := MockServer{} server.Start(t) defer server.Stop(t) // Check if we can hit HTTP endpoints httpBaseURL := fmt.Sprintf("http://%s", server.hc.HTTPTarget()) t.Run("check success", func(t *testing.T) { checkHTTPPost(t, httpBaseURL) checkAllEndpoints(t, httpBaseURL, nil) }) t.Run("check failures", func(t *testing.T) { checkExtraJSONFieldsErrors(t, httpBaseURL, nil) }) } func TestServe_httpstls(t *testing.T) { server := MockServer{} server.StartTLS(t) defer server.Stop(t) client := createHTTPSClient(t, server.gc.certFile) httpsBaseURL := fmt.Sprintf("https://%s", server.hc.HTTPTarget()) t.Run("check success over HTTPS", func(t *testing.T) { checkHTTPPostWithClient(t, httpsBaseURL, client) checkAllEndpoints(t, httpsBaseURL, client) }) t.Run("check failures over HTTPS", func(t *testing.T) { checkExtraJSONFieldsErrors(t, httpsBaseURL, client) }) } func checkAllEndpoints(t *testing.T, baseURL string, client *http.Client) { for _, tt := range endpointTests { url := baseURL + tt.path if client != nil { checkHTTPGetWithClient(t, url, tt.expectedBody, client) } else { checkHTTPGet(t, url, tt.expectedBody) } } } func createHTTPSClient(t *testing.T, certFile string) *http.Client { caCertPool, err := x509.SystemCertPool() if err != nil || caCertPool == nil { t.Log("system cert pool unavailable, creating a new cert pool") caCertPool = x509.NewCertPool() } serverCert, err := os.ReadFile(certFile) if err != nil { t.Fatalf("failed to read server certificate: %v", err) } if !caCertPool.AppendCertsFromPEM(serverCert) { t.Fatal("failed to append server certificate") } tlsConfig := &tls.Config{ RootCAs: caCertPool, ServerName: "localhost", } transport := &http.Transport{ TLSClientConfig: tlsConfig, } return &http.Client{Transport: transport} } func checkHTTPPostWithClient(t *testing.T, baseURL string, client *http.Client) { resp, err := client.Post(baseURL+"/api/v2/log/entries", "application/json", nil) if err != nil { t.Fatal(err) } defer resp.Body.Close() for header := range resp.Header { if strings.HasPrefix(header, "Grpc-") { t.Errorf("found gRPC header in HTTP response: %s", header) } } if resp.StatusCode != http.StatusOK { t.Errorf("got %d, want %d", resp.StatusCode, http.StatusOK) return } entryJSON, err := io.ReadAll(resp.Body) if err != nil { t.Fatal(err) } var entry pbs.TransparencyLogEntry if err = protojson.Unmarshal(entryJSON, &entry); err != nil { t.Fatal(err) } if !proto.Equal(&entry, &testEntry) { t.Errorf("\ngot :%+v\nwant :%+v", &entry, &testEntry) } } func checkHTTPGetWithClient(t *testing.T, url, expectedBody string, client *http.Client) { resp, err := client.Get(url) if err != nil { t.Fatalf("%s: %v", url, err) } defer resp.Body.Close() for header := range resp.Header { if strings.HasPrefix(header, "Grpc-") { t.Errorf("found gRPC header in HTTP response: %s", header) } } if resp.StatusCode != http.StatusOK { t.Errorf("%s: got %d want %d", url, resp.StatusCode, http.StatusOK) } body, err := io.ReadAll(resp.Body) if err != nil { t.Fatal(err) } if string(body) != expectedBody { t.Errorf("%s\ngot :%q\nwant :%q", url, body, expectedBody) } } func checkExtraJSONFieldsErrors(t *testing.T, baseURL string, client *http.Client) { var resp *http.Response var err error data := bytes.NewBufferString("{\"foo\":\"bar\"}") if client != nil { resp, err = client.Post(baseURL+"/api/v2/log/entries", "application/json", data) } else { resp, err = http.Post(baseURL+"/api/v2/log/entries", "application/json", data) } if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusBadRequest { t.Errorf("got %d, want %d", resp.StatusCode, http.StatusBadRequest) } } func checkHTTPPost(t *testing.T, baseURL string) { checkHTTPPostWithClient(t, baseURL, http.DefaultClient) } func checkHTTPGet(t *testing.T, url, expectedBody string) { checkHTTPGetWithClient(t, url, expectedBody, http.DefaultClient) } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/httpconfig.go000066400000000000000000000050141511162205500261500ustar00rootroot00000000000000// Copyright 2025 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 server import ( "strconv" "time" ) type HTTPConfig struct { host string timeout time.Duration port int metricsPort int maxRequestBodySize int certFile string keyFile string grpcCertFile string } type HTTPOption func(config *HTTPConfig) func NewHTTPConfig(options ...func(config *HTTPConfig)) *HTTPConfig { config := &HTTPConfig{ host: "localhost", timeout: defaultTimeout, port: 8080, metricsPort: 2112, maxRequestBodySize: defaultMaxSize, } for _, opt := range options { opt(config) } return config } func WithHTTPPort(port int) HTTPOption { return func(config *HTTPConfig) { config.port = port } } func WithHTTPHost(host string) HTTPOption { return func(config *HTTPConfig) { config.host = host } } func WithHTTPTimeout(timeout time.Duration) HTTPOption { return func(config *HTTPConfig) { config.timeout = timeout } } // WithHTTPMaxRequestBodySize specifies the maximum size of a requests's body. func WithHTTPMaxRequestBodySize(size int) HTTPOption { return func(config *HTTPConfig) { config.maxRequestBodySize = size } } func WithHTTPMetricsPort(port int) HTTPOption { return func(config *HTTPConfig) { config.metricsPort = port } } func (hc HTTPConfig) HTTPTarget() string { return hc.host + ":" + strconv.Itoa(hc.port) } func (hc HTTPConfig) HTTPMetricsTarget() string { return hc.host + ":" + strconv.Itoa(hc.metricsPort) } func (hc HTTPConfig) HasTLS() bool { return hc.certFile != "" && hc.keyFile != "" } func WithHTTPTLSCredentials(certFile, keyFile string) HTTPOption { return func(config *HTTPConfig) { config.certFile = certFile config.keyFile = keyFile } } func (hc HTTPConfig) HasGRPCTLS() bool { return hc.grpcCertFile != "" } func WithGRPCTLSCredentials(certFile string) HTTPOption { return func(config *HTTPConfig) { config.grpcCertFile = certFile } } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/httpconfig_test.go000066400000000000000000000122661511162205500272160ustar00rootroot00000000000000// Copyright 2025 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 server import ( "testing" "time" ) func TestNewHTTPConfig(t *testing.T) { // Test default configuration config := NewHTTPConfig() if config.host != "localhost" { t.Errorf("expected host to be localhost, got %s", config.host) } if config.timeout != 60*time.Second { t.Errorf("expected idleTimeout to be 60s, got %v", config.timeout) } if config.maxRequestBodySize != 4*1024*1024 { t.Errorf("expected maxSize)} to be 4MB, got %d", config.maxRequestBodySize) } if config.port != 8080 { t.Errorf("expected port to be 8080, got %d", config.port) } if config.metricsPort != 2112 { t.Errorf("expected metricsport to be 2112, got %d", config.port) } if config.HTTPTarget() != "localhost:8080" { t.Errorf("expected http target to be localhost:8080, got %s", config.HTTPTarget()) } if config.HTTPMetricsTarget() != "localhost:2112" { t.Errorf("expected http metrics target to be localhost:2112, got %s", config.HTTPTarget()) } } func TestWithHTTPPort(t *testing.T) { config := NewHTTPConfig(WithHTTPPort(9000)) if config.port != 9000 { t.Errorf("expected port to be 9000, got %d", config.port) } } func TestWithHTTPHost(t *testing.T) { config := NewHTTPConfig(WithHTTPHost("example.com")) if config.host != "example.com" { t.Errorf("expected host to be example.com, got %s", config.host) } } func TestWithHTTTimeout(t *testing.T) { config := NewHTTPConfig(WithHTTPTimeout(30 * time.Second)) if config.timeout != 30*time.Second { t.Errorf("expected idleTimeout to be 30s, got %v", config.timeout) } } func TestWithHTTPMaxRequestBodySize(t *testing.T) { config := NewHTTPConfig(WithHTTPMaxRequestBodySize(2 * 1024 * 1024)) if config.maxRequestBodySize != 2*1024*1024 { t.Errorf("expected maxSize to be) 2MB, got %d", config.maxRequestBodySize) } } func TestWithHTTPMetricsPort(t *testing.T) { config := NewHTTPConfig(WithHTTPMetricsPort(9001)) if config.metricsPort != 9001 { t.Errorf("expected metrics port to be 9001, got %d", config.port) } } func TestMultipleOptions(t *testing.T) { config := NewHTTPConfig( WithHTTPPort(9090), WithHTTPHost("test.example.com"), WithHTTPTimeout(10*time.Second), WithHTTPMaxRequestBodySize(1*1024*1024), WithHTTPMetricsPort(9091), ) if config.port != 9090 { t.Errorf("expected port to be 9090, got %d", config.port) } if config.metricsPort != 9091 { t.Errorf("expected port to be 9091, got %d", config.port) } if config.host != "test.example.com" { t.Errorf("expected host to be test.example.com, got %s", config.host) } if config.timeout != 10*time.Second { t.Errorf("expected idleTimeout to be 10s, got %v", config.timeout) } if config.maxRequestBodySize != 1*1024*1024 { t.Errorf("expected maxSize to be 1MB, got %d", config.maxRequestBodySize) } if config.HTTPTarget() != "test.example.com:9090" { t.Errorf("expected http target to be test.example.com:9090, got %s", config.HTTPTarget()) } if config.HTTPMetricsTarget() != "test.example.com:9091" { t.Errorf("expected http metrics target to be test.example.com:9091, got %s", config.HTTPTarget()) } } func TestWithHTTPTLSCredentials(t *testing.T) { config := NewHTTPConfig(WithHTTPTLSCredentials("cert.pem", "key.pem")) if config.certFile != "cert.pem" { t.Errorf("expected certFile to be cert.pem, got %s", config.certFile) } if config.keyFile != "key.pem" { t.Errorf("expected keyFile to be key.pem, got %s", config.keyFile) } if !config.HasTLS() { t.Error("expected HasTLS to return true when TLS credentials are set") } } func TestHasTLS(t *testing.T) { config := NewHTTPConfig() if config.HasTLS() { t.Error("expected HasTLS to return false when no TLS credentials are set") } config = NewHTTPConfig(func(c *HTTPConfig) { c.certFile = "cert.pem" }) if config.HasTLS() { t.Error("expected HasTLS to return false when only certFile is set") } config = NewHTTPConfig(func(c *HTTPConfig) { c.keyFile = "key.pem" }) if config.HasTLS() { t.Error("expected HasTLS to return false when only keyFile is set") } config = NewHTTPConfig(WithHTTPTLSCredentials("cert.pem", "key.pem")) if !config.HasTLS() { t.Error("expected HasTLS to return true when both certFile and keyFile are set") } } func TestWithGRPCTLSCredentials(t *testing.T) { config := NewHTTPConfig() if config.HasGRPCTLS() { t.Error("expected HasGRPCTLS to return false when no gRPC TLS credentials are set") } config = NewHTTPConfig(WithGRPCTLSCredentials("cert.pem")) if config.grpcCertFile != "cert.pem" { t.Errorf("expected gRPC cert file to be cert.pem, got %s", config.certFile) } if !config.HasGRPCTLS() { t.Error("expected HasGRPCTLS to return true when both certFile and keyFile are set") } } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/logging.go000066400000000000000000000032261511162205500254340ustar00rootroot00000000000000// Copyright 2025 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 server import ( "context" "log/slog" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" ) func interceptorLogger(l *slog.Logger) logging.Logger { return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) { switch msg { case "request received", "request sent", "response received", "response sent", "finished call": for i := range len(fields) - 1 { if key, ok := fields[i].(string); ok && key == "grpc.service" { if value, ok := fields[i+1].(string); ok && value == "grpc.health.v1.Health" { // skip logging anything for health check return } } } } l.Log(ctx, slog.Level(lvl), msg, fields...) }) } func loggingOpts(level slog.Level, requestResponseLogging bool) []logging.Option { events := []logging.LoggableEvent{} if level == slog.LevelDebug { events = append(events, logging.FinishCall) if requestResponseLogging { events = append(events, logging.PayloadReceived, logging.PayloadSent) } } return []logging.Option{logging.WithLogOnEvents(events...)} } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/metrics.go000066400000000000000000000154271511162205500254620ustar00rootroot00000000000000// Copyright 2025 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 server import ( "context" "errors" "log/slog" "net" "net/http" "os" "os/signal" "sync" "syscall" "google.golang.org/grpc/reflection" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" "go.opentelemetry.io/otel" otelprom "go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/sdk/metric" "sigs.k8s.io/release-utils/version" ) type metrics struct { reg *prometheus.Registry serverMetrics *grpc_prometheus.ServerMetrics // metrics newHashedRekordEntries prometheus.Counter newDsseEntries prometheus.Counter httpLatency *prometheus.HistogramVec httpRequestsCount *prometheus.CounterVec httpRequestSize *prometheus.HistogramVec panicsTotal prometheus.Counter inclusionProofFailureCount prometheus.Counter grpcRequestSize *prometheus.HistogramVec otelShutdown func(context.Context) error } // Metrics provides the singleton metrics instance func getMetrics() *metrics { return _initMetricsFunc() } var _initMetricsFunc = sync.OnceValue[*metrics](func() *metrics { m := metrics{ reg: prometheus.NewRegistry(), serverMetrics: grpc_prometheus.NewServerMetrics(grpc_prometheus.WithServerHandlingTimeHistogram()), } m.reg.MustRegister(m.serverMetrics) f := promauto.With(m.reg) m.newHashedRekordEntries = f.NewCounter(prometheus.CounterOpts{ Name: "rekor_v2_new_hashedrekord_entries", Help: "The total number of new dsse log entries", }) m.newDsseEntries = f.NewCounter(prometheus.CounterOpts{ Name: "rekor_v2_new_dsse_entries", Help: "The total number of new dsse log entries", }) // grpc_packet_part should always be "payload" but we can measure "header" or "trailer" in the future // if we so desire m.grpcRequestSize = f.NewHistogramVec(prometheus.HistogramOpts{ Name: "rekor_v2_grpc_api_request_size", Help: "API Request size on GRPC calls", }, []string{"grpc_service", "grpc_method", "grpc_packet_part"}) m.httpLatency = f.NewHistogramVec(prometheus.HistogramOpts{ Name: "rekor_v2_http_api_latency", Help: "API Latency on HTTP calls", }, []string{"code", "method"}) m.httpRequestsCount = f.NewCounterVec(prometheus.CounterOpts{ Name: "rekor_v2_http_requests_total", Help: "Count all HTTP requests", }, []string{"code", "method"}) m.httpRequestSize = f.NewHistogramVec(prometheus.HistogramOpts{ Name: "rekor_v2_http_api_request_size", Help: "API Request size on HTTP calls", }, []string{"code", "method"}) m.panicsTotal = f.NewCounter(prometheus.CounterOpts{ Name: "rekor_v2_grpc_req_panics_recovered_total", Help: "Total number of gRPC requests recovered from internal panic.", }) m.inclusionProofFailureCount = f.NewCounter(prometheus.CounterOpts{ Name: "rekor_v2_inclusion_proof_failure_total", Help: "Total number of inclusion proof verification failures, which should always be zero. Likely catastrophic failure if not zero.", }) _ = f.NewGaugeFunc( prometheus.GaugeOpts{ Namespace: "rekor_v2", Name: "build_info", Help: "A metric with a constant '1' value labeled by version, revision, branch, and goversion from which rekor was built.", ConstLabels: prometheus.Labels{ "version": version.GetVersionInfo().GitVersion, "revision": version.GetVersionInfo().GitCommit, "build_date": version.GetVersionInfo().BuildDate, "goversion": version.GetVersionInfo().GoVersion, }, }, func() float64 { return 1 }, ) otelExporter, err := otelprom.New( otelprom.WithRegisterer(m.reg), ) if err != nil { slog.Error("failed to register opentelemetry metrics for prometheus:", "errors", err) os.Exit(1) } provider := metric.NewMeterProvider(metric.WithReader(otelExporter)) otel.SetMeterProvider(provider) m.otelShutdown = provider.Shutdown return &m }) type httpMetrics struct { *http.Server serverEndpoint string otelShutdown func(context.Context) error } func newHTTPMetrics(_ context.Context, config *HTTPConfig) *httpMetrics { mux := http.NewServeMux() mux.Handle("/", promhttp.HandlerFor(getMetrics().reg, promhttp.HandlerOpts{ Timeout: config.timeout, })) endpoint := config.HTTPMetricsTarget() return &httpMetrics{ Server: &http.Server{ Addr: endpoint, Handler: mux, ReadTimeout: config.timeout, ReadHeaderTimeout: config.timeout, WriteTimeout: config.timeout, IdleTimeout: config.timeout, }, serverEndpoint: endpoint, otelShutdown: getMetrics().otelShutdown, } } func (hp *httpMetrics) start(wg *sync.WaitGroup) { lis, err := net.Listen("tcp", hp.serverEndpoint) if err != nil { slog.Error("failed to create listener:", "errors", err) os.Exit(1) } hp.serverEndpoint = lis.Addr().String() slog.Info("starting http metrics", "address", hp.serverEndpoint) ctx := context.Background() waitToClose := make(chan struct{}) go func() { // capture interrupts and shutdown Server sigint := make(chan os.Signal, 1) signal.Notify(sigint, syscall.SIGINT, syscall.SIGTERM) <-sigint if err := hp.Shutdown(ctx); err != nil { slog.Info("http metrics Server Shutdown error", "errors", err) } close(waitToClose) slog.Info("stopped http metrics Server") }() wg.Add(1) go func() { if err := hp.Serve(lis); err != nil && !errors.Is(err, http.ErrServerClosed) { slog.Error("could not start http metrics server", "errors", err) os.Exit(1) } <-waitToClose wg.Done() _ = hp.otelShutdown(ctx) slog.Info("http metrics Server shutdown") }() } // InitializeCustomGrpcMetrics mirrors the functionality of prometheus.ServerMetrics InitializeMetrics but for our own // custom grpc metrics func (m *metrics) InitializeCustomGrpcMetrics(server reflection.ServiceInfoProvider) { serviceInfo := server.GetServiceInfo() for serviceName, info := range serviceInfo { for _, mInfo := range info.Methods { methodName := mInfo.Name // These are just references (no increments), as just referencing will create the labels but not set values. _, _ = m.grpcRequestSize.GetMetricWithLabelValues(serviceName, methodName, "payload") } } } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/metrics_test.go000066400000000000000000000044031511162205500265110ustar00rootroot00000000000000// Copyright 2025 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 server import ( "fmt" "io" "net/http" "strings" "testing" ) func TestServe_httpMetricsSmoke(t *testing.T) { // To debug set slog to output to stdout // slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil))) server := MockServer{} server.Start(t) defer server.Stop(t) // Check if we can hit the metrics endpoint metricsURL := fmt.Sprintf("http://%s", server.hc.HTTPMetricsTarget()) resp, err := http.Get(metricsURL) if err != nil { t.Fatalf("fetching metrics from %s, %v", metricsURL, err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("%s: got %d want %d", metricsURL, resp.StatusCode, http.StatusOK) } body, err := io.ReadAll(resp.Body) if err != nil { t.Fatal(err) } b := string(body) // we ping healthz in our MockServer that will initialize some http stats for us. On a raw // server with no requests ever recorded, rekor_http_* statistics may be uninitialized expectedMetrics := []string{ "rekor_v2_new_hashedrekord_entries", "rekor_v2_new_dsse_entries", "build_info", "rekor_v2_http_api_latency", "rekor_v2_http_requests_total", "rekor_v2_http_api_request_size", "rekor_v2_grpc_req_panics_recovered_total", "rekor_v2_grpc_api_request_size", "grpc_server_started_total", // should imply we have the default set of grpc server metrics "grpc_server_handling_seconds", // should imply we have the default set of latency stats on grpc servers "promhttp_metric_handler", // should imply we have the default set of promhttp metrics } for _, metric := range expectedMetrics { if !strings.Contains(b, metric) { t.Errorf("metrics target body did not contain %s, \n %s", metric, body) } } } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/serve.go000066400000000000000000000032621511162205500251320ustar00rootroot00000000000000// Copyright 2025 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 server import ( "context" "log/slog" "os" "sync" "time" ) // Serve starts the grpc server and its http proxy. func Serve(ctx context.Context, hc *HTTPConfig, gc *GRPCConfig, tesseraTimeout time.Duration, s rekorServer, tesseraShutdownFn func(context.Context) error) { var wg sync.WaitGroup if hc.port == 0 || gc.port == 0 { slog.Error("dynamic port allocation '0' is not supported", "http port", hc.port, "grpc port", gc.port) os.Exit(1) } if hc.port == gc.port && hc.host == gc.host { slog.Error("http and grpc cannot serve at the same address", "host", hc.host, "port", hc.port) os.Exit(1) } grpcServer := newGRPCServer(gc, s) grpcServer.start(&wg) httpProxy := newHTTPProxy(ctx, hc, grpcServer) httpProxy.start(&wg) httpMetrics := newHTTPMetrics(ctx, hc) httpMetrics.start(&wg) wg.Wait() slog.Info("shutting down Tessera sequencer") tesseraCtx, cancel := context.WithTimeout(ctx, tesseraTimeout) defer cancel() if err := tesseraShutdownFn(tesseraCtx); err != nil { slog.Error("error shutting down Tessera", "error", err) } slog.Info("stopped Tessera sequencer") } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/serve_test.go000066400000000000000000000027471511162205500262000ustar00rootroot00000000000000// Copyright 2025 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 server import ( "context" "sync" "sync/atomic" "syscall" "testing" "time" ) func TestServe(t *testing.T) { wg := sync.WaitGroup{} var pid atomic.Uint64 shutdownFn := func(context.Context) error { wg.Done() return nil } go func() { pid.Store(uint64(syscall.Getpid())) // Process IDs are positive ints Serve(context.Background(), NewHTTPConfig(), NewGRPCConfig(), 1*time.Second, nil, shutdownFn) wg.Done() }() // One for Serve returning, one for shutdown function being invoked wg.Add(2) i := 0 for { if i == 5 { t.Fatalf("could not get process ID in 5 seconds") } if pid.Load() != 0 { break } i++ time.Sleep(1 * time.Second) } // Shutdown server gracefully to test that Serve shuts down gRPC and HTTP servers and Tessera connection if err := syscall.Kill(int(pid.Load()), syscall.SIGTERM); err != nil { t.Fatalf("Could not kill server") } wg.Wait() } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/service.go000066400000000000000000000154031511162205500254460ustar00rootroot00000000000000// Copyright 2025 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 server import ( "context" "errors" "log/slog" "github.com/prometheus/client_golang/prometheus" "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" pbs "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "github.com/sigstore/rekor-tiles/v2/internal/tessera" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" "github.com/sigstore/rekor-tiles/v2/pkg/types/dsse" "github.com/sigstore/rekor-tiles/v2/pkg/types/hashedrekord" "github.com/sigstore/sigstore/pkg/signature" ttessera "github.com/transparency-dev/tessera" "google.golang.org/genproto/googleapis/api/httpbody" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/emptypb" ) // rekorServer is the collection of methods that our grpc server must implement. type rekorServer interface { pb.RekorServer grpc_health_v1.HealthServer } type Server struct { pb.UnimplementedRekorServer grpc_health_v1.UnimplementedHealthServer storage tessera.Storage readOnly bool algorithmRegistry *signature.AlgorithmRegistryConfig logID []byte // Non-truncated digest of C2SP signed-note key ID } func NewServer(storage tessera.Storage, readOnly bool, algorithmRegistry *signature.AlgorithmRegistryConfig, logID []byte) *Server { if readOnly { return &Server{ readOnly: readOnly, logID: logID, } } return &Server{ storage: storage, algorithmRegistry: algorithmRegistry, logID: logID, } } func (s *Server) CreateEntry(ctx context.Context, req *pb.CreateEntryRequest) (*pbs.TransparencyLogEntry, error) { if s.readOnly { slog.WarnContext(ctx, "rekor is in read-only mode, cannot create new entry") _ = grpc.SetHeader(ctx, metadata.Pairs(httpStatusCodeHeader, "405")) _ = grpc.SetHeader(ctx, metadata.Pairs(httpErrorMessageHeader, "This log has been frozen, please switch to the latest log.")) return nil, status.Errorf(codes.Unimplemented, "log frozen") } var serialized []byte var err error var metricsCounter prometheus.Counter var kv *pbs.KindVersion switch req.GetSpec().(type) { case *pb.CreateEntryRequest_HashedRekordRequestV002: hr := req.GetHashedRekordRequestV002() entry, err := hashedrekord.ToLogEntry(hr, s.algorithmRegistry) if err != nil { slog.WarnContext(ctx, "failed validating hashedrekord request", "error", err.Error()) return nil, status.Errorf(codes.InvalidArgument, "invalid hashedrekord request") } kv = &pbs.KindVersion{ Kind: entry.Kind, Version: entry.ApiVersion, } serialized, err = protojson.Marshal(entry) if err != nil { slog.WarnContext(ctx, "failed marshaling hashedrekord request", "error", err.Error()) return nil, status.Errorf(codes.InvalidArgument, "invalid hashedrekord request") } metricsCounter = getMetrics().newHashedRekordEntries case *pb.CreateEntryRequest_DsseRequestV002: ds := req.GetDsseRequestV002() entry, err := dsse.ToLogEntry(ds, s.algorithmRegistry) if err != nil { slog.WarnContext(ctx, "failed validating dsse request", "error", err.Error()) return nil, status.Errorf(codes.InvalidArgument, "invalid dsse request") } kv = &pbs.KindVersion{ Kind: entry.Kind, Version: entry.ApiVersion, } serialized, err = protojson.Marshal(entry) if err != nil { slog.WarnContext(ctx, "failed marshaling dsse request", "error", err.Error()) return nil, status.Errorf(codes.InvalidArgument, "invalid dsse request") } metricsCounter = getMetrics().newDsseEntries default: return nil, status.Errorf(codes.InvalidArgument, "invalid type, must be either hashedrekord or dsse") } canonicalized, err := jsoncanonicalizer.Transform(serialized) if err != nil { slog.WarnContext(ctx, "failed canonicalizing request", "error", err.Error()) return nil, status.Errorf(codes.InvalidArgument, "invalid entry") } entry := ttessera.NewEntry(canonicalized) tle, err := s.storage.Add(ctx, entry) if errors.Is(err, ttessera.ErrPushback) { return nil, status.Errorf(codes.Unavailable, "reached max pushback; retry") } if errors.Is(err, context.Canceled) { // Returns a 499 Client Closed Request return nil, status.Error(codes.Canceled, err.Error()) } if errors.As(err, &tessera.DuplicateError{}) { return nil, status.Error(codes.AlreadyExists, err.Error()) } if errors.As(err, &tessera.InclusionProofVerificationError{}) { getMetrics().inclusionProofFailureCount.Inc() } if err != nil { slog.WarnContext(ctx, "failed to integrate entry", "error", err.Error()) return nil, status.Errorf(codes.Unknown, "failed to integrate entry") } // Set bundle's kind and version, which clients that do not persist the // canonicalized body will use to reconstruct the entry leaf hash tle.KindVersion = kv // Set log ID, to be used by clients to look up the corresponding instance // in a trust root. Will be removed in the future, as clients should use // the checkpoint's key ID as a unique log identifier. tle.LogId = &v1.LogId{KeyId: s.logID} _ = grpc.SetHeader(ctx, metadata.Pairs(httpStatusCodeHeader, "201")) metricsCounter.Inc() return tle, nil } func (s *Server) GetTile(context.Context, *pb.TileRequest) (*httpbody.HttpBody, error) { return nil, status.Errorf(codes.Unimplemented, "method GetTile not implemented") } func (s *Server) GetEntryBundle(context.Context, *pb.EntryBundleRequest) (*httpbody.HttpBody, error) { return nil, status.Errorf(codes.Unimplemented, "method GetEntryBundle not implemented") } func (s *Server) GetCheckpoint(context.Context, *emptypb.Empty) (*httpbody.HttpBody, error) { return nil, status.Errorf(codes.Unimplemented, "method GetCheckpoint not implemented") } // Check implements the Healthcheck protocol to report the health of the service. // See https://grpc-ecosystem.github.io/grpc-gateway/docs/operations/health_check/. func (s Server) Check(_ context.Context, _ *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) { return &grpc_health_v1.HealthCheckResponse{Status: grpc_health_v1.HealthCheckResponse_SERVING}, nil } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/service_mock.go000066400000000000000000000176661511162205500264740ustar00rootroot00000000000000// Copyright 2025 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 server import ( "context" "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "fmt" "io" "math/big" "net" "net/http" "os" "path/filepath" "strconv" "strings" "sync" "syscall" "testing" "time" pbsc "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" pbs "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" "github.com/transparency-dev/tessera/api/layout" "google.golang.org/genproto/googleapis/api/httpbody" "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/protobuf/types/known/emptypb" ) // A testing mock that wraps server.Server to Start and defer Stop a server type MockServer struct { gc *GRPCConfig hc *HTTPConfig wg *sync.WaitGroup cleanupFiles []string } func (ms *MockServer) Start(t *testing.T) { ms.gc = NewGRPCConfig( WithGRPCHost("localhost"), WithGRPCPort(8081), ) ms.hc = NewHTTPConfig( WithHTTPHost("localhost"), WithHTTPPort(8080), ) s := &mockRekorServer{} shutdownFn := func(context.Context) error { return nil } // Start the server ms.wg = &sync.WaitGroup{} go func() { Serve(context.Background(), ms.hc, ms.gc, 1*time.Second, s, shutdownFn) ms.wg.Done() }() ms.wg.Add(1) // Healthcheck: Ping the /healthz endpoint until healthy for i := range 6 { if i == 5 { t.Fatal("mock server failed to startup") } resp, err := http.Get("http://localhost:8080/healthz") if err == nil && resp.StatusCode == http.StatusOK { defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("parsing response body: %v", err) } if strings.Contains(string(body), `{"status":"SERVING"}`) { break } } time.Sleep(1 * time.Second) } } func (ms *MockServer) StartTLS(t *testing.T) { certFile, keyFile := generateSelfSignedCert(t) ms.cleanupFiles = append(ms.cleanupFiles, certFile, keyFile) ms.gc = NewGRPCConfig( WithGRPCHost("localhost"), WithGRPCPort(8081), WithTLSCredentials(certFile, keyFile), ) ms.hc = NewHTTPConfig( WithHTTPHost("localhost"), WithHTTPPort(8080), WithHTTPTLSCredentials(certFile, keyFile), WithGRPCTLSCredentials(certFile), ) s := &mockRekorServer{} shutdownFn := func(context.Context) error { return nil } // Start the server ms.wg = &sync.WaitGroup{} go func() { Serve(context.Background(), ms.hc, ms.gc, 1*time.Second, s, shutdownFn) ms.wg.Done() }() ms.wg.Add(1) // Healthcheck: Ping the /healthz endpoint until it returns an expected error // because we're not initializing a connection over TLS. for i := range 6 { if i == 5 { t.Fatal("mock TLS server failed to startup") } resp, err := http.Get("http://localhost:8080/healthz") if err == nil && resp.StatusCode == http.StatusBadRequest { defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { t.Fatalf("parsing response body: %v", err) } if strings.Contains(string(body), "Client sent an HTTP request to an HTTPS server") { break } } time.Sleep(1 * time.Second) } } func (ms *MockServer) Stop(t *testing.T) { // Simulate SIGTERM to trigger graceful shutdown if err := syscall.Kill(syscall.Getpid(), syscall.SIGTERM); err != nil { t.Fatalf("could not kill server") } ms.wg.Wait() for _, file := range ms.cleanupFiles { if err := os.Remove(file); err != nil { t.Logf("Failed to remove temp file %s: %v", file, err) } } } type mockRekorServer struct { pb.UnimplementedRekorServer grpc_health_v1.UnimplementedHealthServer } var testEntry = pbs.TransparencyLogEntry{ LogIndex: 0, LogId: &pbsc.LogId{ KeyId: []byte("abc"), }, KindVersion: &pbs.KindVersion{ Kind: "placeholder", Version: "1.2.3", }, IntegratedTime: 0, InclusionPromise: nil, InclusionProof: &pbs.InclusionProof{ LogIndex: 0, RootHash: []byte("abc"), TreeSize: 0, Hashes: [][]byte{[]byte("def"), []byte("ghi")}, Checkpoint: &pbs.Checkpoint{ Envelope: "placeholder", }, }, CanonicalizedBody: []byte("abcd"), } func (s *mockRekorServer) CreateEntry(_ context.Context, _ *pb.CreateEntryRequest) (*pbs.TransparencyLogEntry, error) { return &testEntry, nil } func (s *mockRekorServer) GetTile(_ context.Context, in *pb.TileRequest) (*httpbody.HttpBody, error) { // Verifies and parses level, index, and optional width for partial tile l, i, w, err := layout.ParseTileLevelIndexPartial(strconv.FormatUint(uint64(in.L), 10), in.N) if err != nil { return nil, err } return &httpbody.HttpBody{ ContentType: "application/octet-stream", Data: []byte(fmt.Sprintf("test-tile:%d,%d,%d", l, i, w)), Extensions: nil, }, nil } func (s *mockRekorServer) GetEntryBundle(_ context.Context, in *pb.EntryBundleRequest) (*httpbody.HttpBody, error) { // Parses index, and optional width for partial tile i, w, err := layout.ParseTileIndexPartial(in.N) if err != nil { return nil, err } return &httpbody.HttpBody{ ContentType: "application/octet-stream", Data: []byte(fmt.Sprintf("test-entries:%d,%d", i, w)), Extensions: nil, }, nil } func (s *mockRekorServer) GetCheckpoint(_ context.Context, _ *emptypb.Empty) (*httpbody.HttpBody, error) { return &httpbody.HttpBody{ ContentType: "application/octet-stream", Data: []byte("test-checkpoint"), Extensions: nil, }, nil } func (s mockRekorServer) Check(_ context.Context, _ *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) { return &grpc_health_v1.HealthCheckResponse{Status: grpc_health_v1.HealthCheckResponse_SERVING}, nil } func generateSelfSignedCert(t testing.TB) (certFile, keyFile string) { t.Helper() tempDir := t.TempDir() priv, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatalf("failed to generate private key: %v", err) } template := x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ Organization: []string{"Test Org"}, }, NotBefore: time.Now(), NotAfter: time.Now().Add(24 * time.Hour), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{ x509.ExtKeyUsageServerAuth, }, BasicConstraintsValid: true, DNSNames: []string{"localhost"}, IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, } derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) if err != nil { t.Fatalf("failed to create certificate: %v", err) } certFile = filepath.Join(tempDir, "cert.pem") certOut, err := os.Create(certFile) if err != nil { t.Fatalf("failed to open cert.pem for writing: %v", err) } if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { t.Fatalf("failed to write data to cert.pem: %v", err) } if err := certOut.Close(); err != nil { t.Fatalf("error closing cert.pem: %v", err) } keyFile = filepath.Join(tempDir, "key.pem") keyOut, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { t.Fatalf("failed to open key.pem for writing: %v", err) } privBytes, err := x509.MarshalPKCS8PrivateKey(priv) if err != nil { t.Fatalf("unable to marshal private key: %v", err) } if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil { t.Fatalf("failed to write data to key.pem: %v", err) } if err := keyOut.Close(); err != nil { t.Fatalf("error closing key.pem: %v", err) } return certFile, keyFile } golang-github-sigstore-rekor-tiles-2.0.1/internal/server/service_test.go000066400000000000000000000300621511162205500265030ustar00rootroot00000000000000// Copyright 2025 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 server import ( "context" "encoding/base64" "encoding/hex" "fmt" "testing" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" "github.com/sigstore/protobuf-specs/gen/pb-go/dsse" rekor_pb "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "github.com/sigstore/rekor-tiles/v2/internal/algorithmregistry" "github.com/sigstore/rekor-tiles/v2/internal/tessera" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" "github.com/stretchr/testify/assert" ttessera "github.com/transparency-dev/tessera" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) func TestNewServer(t *testing.T) { storage := &mockStorage{} algReg, err := algorithmregistry.AlgorithmRegistry([]string{"ecdsa-sha2-256-nistp256"}) if err != nil { t.Fatal(err) } server := NewServer(storage, false, algReg, []byte{1}) assert.NoError(t, err) assert.NotNil(t, server.storage) assert.NotNil(t, server.algorithmRegistry) } func TestCreateEntry(t *testing.T) { tests := []struct { name string req *pb.CreateEntryRequest addFn func() (*rekor_pb.TransparencyLogEntry, error) clientSigningAlgorithms []string expectError error expectedCode codes.Code }{ { name: "valid hashedrekord", req: &pb.CreateEntryRequest{ Spec: &pb.CreateEntryRequest_HashedRekordRequestV002{ HashedRekordRequestV002: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: b64DecodeOrDie(t, "MEYCIQC59oLS3MsCqm0xCxPOy+8FdQK4RYCZE036s3q1ECfcagIhAJ4ATXlCSdFrklKAS8No0PsAE9uLi37TCbIfRXASJTTb"), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: b64DecodeOrDie(t, "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeLw7gX40qy1z7JUhGMAaaDITbV7p2D+C5G9xPEsy/PVAo9H0mgS4NYzpGirkXxBht+IvvL19WR1X9ANXha5ldQ=="), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: hexDecodeOrDie(t, "5b3513f580c8397212ff2c8f459c199efc0c90e4354a5f3533adf0a3fff3a530"), }, }, }, addFn: func() (*rekor_pb.TransparencyLogEntry, error) { return &rekor_pb.TransparencyLogEntry{}, nil }, clientSigningAlgorithms: []string{"ecdsa-sha2-256-nistp256"}, }, { name: "valid dsse", req: &pb.CreateEntryRequest{ Spec: &pb.CreateEntryRequest_DsseRequestV002{ DsseRequestV002: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: b64DecodeOrDie(t, "cGF5bG9hZA=="), PayloadType: "application/vnd.in-toto+json", Signatures: []*dsse.Signature{ { Sig: b64DecodeOrDie(t, "MEUCIQCSWas1Y9bI7aDNrBdHlzrFH8ch7B7IM+pJK86mtjkbJAIgaeCltz6vs20DP2sJ7IBihvcrdqGn3ivuV/KNPlMOetk="), Keyid: "", }, }, }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: b64DecodeOrDie(t, "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE850nB+WrwXzivt7yFbhFKw/8M2paqSTHiQhkA4/0ZAsJtmzn/v4HdeZKTCQcsHq5IwM/LtbmEdv9ChO9M3cg9g=="), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, }, }, addFn: func() (*rekor_pb.TransparencyLogEntry, error) { return &rekor_pb.TransparencyLogEntry{}, nil }, clientSigningAlgorithms: []string{"ecdsa-sha2-256-nistp256"}, }, { name: "invalid hashedrekord", req: &pb.CreateEntryRequest{ Spec: &pb.CreateEntryRequest_HashedRekordRequestV002{ HashedRekordRequestV002: &pb.HashedRekordRequestV002{}, }, }, addFn: func() (*rekor_pb.TransparencyLogEntry, error) { return &rekor_pb.TransparencyLogEntry{}, nil }, clientSigningAlgorithms: []string{"ecdsa-sha2-256-nistp256"}, expectError: fmt.Errorf("invalid hashedrekord request"), expectedCode: codes.InvalidArgument, }, { name: "invalid dsse", req: &pb.CreateEntryRequest{ Spec: &pb.CreateEntryRequest_DsseRequestV002{ DsseRequestV002: &pb.DSSERequestV002{}, }, }, addFn: func() (*rekor_pb.TransparencyLogEntry, error) { return &rekor_pb.TransparencyLogEntry{}, nil }, clientSigningAlgorithms: []string{"ecdsa-sha2-256-nistp256"}, expectError: fmt.Errorf("invalid dsse request"), expectedCode: codes.InvalidArgument, }, { name: "context canceled", req: &pb.CreateEntryRequest{ Spec: &pb.CreateEntryRequest_HashedRekordRequestV002{ HashedRekordRequestV002: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: b64DecodeOrDie(t, "MEYCIQC59oLS3MsCqm0xCxPOy+8FdQK4RYCZE036s3q1ECfcagIhAJ4ATXlCSdFrklKAS8No0PsAE9uLi37TCbIfRXASJTTb"), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: b64DecodeOrDie(t, "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeLw7gX40qy1z7JUhGMAaaDITbV7p2D+C5G9xPEsy/PVAo9H0mgS4NYzpGirkXxBht+IvvL19WR1X9ANXha5ldQ=="), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: hexDecodeOrDie(t, "5b3513f580c8397212ff2c8f459c199efc0c90e4354a5f3533adf0a3fff3a530"), }, }, }, addFn: func() (*rekor_pb.TransparencyLogEntry, error) { return nil, context.Canceled }, clientSigningAlgorithms: []string{"ecdsa-sha2-256-nistp256"}, expectError: fmt.Errorf("context canceled"), expectedCode: codes.Canceled, }, { name: "duplicate entry", req: &pb.CreateEntryRequest{ Spec: &pb.CreateEntryRequest_HashedRekordRequestV002{ HashedRekordRequestV002: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: b64DecodeOrDie(t, "MEYCIQC59oLS3MsCqm0xCxPOy+8FdQK4RYCZE036s3q1ECfcagIhAJ4ATXlCSdFrklKAS8No0PsAE9uLi37TCbIfRXASJTTb"), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: b64DecodeOrDie(t, "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeLw7gX40qy1z7JUhGMAaaDITbV7p2D+C5G9xPEsy/PVAo9H0mgS4NYzpGirkXxBht+IvvL19WR1X9ANXha5ldQ=="), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: hexDecodeOrDie(t, "5b3513f580c8397212ff2c8f459c199efc0c90e4354a5f3533adf0a3fff3a530"), }, }, }, addFn: func() (*rekor_pb.TransparencyLogEntry, error) { return nil, tessera.DuplicateError{} }, clientSigningAlgorithms: []string{"ecdsa-sha2-256-nistp256"}, expectError: fmt.Errorf("an equivalent entry already exists in the transparency log"), expectedCode: codes.AlreadyExists, }, { name: "inclusion proof verification failure", req: &pb.CreateEntryRequest{ Spec: &pb.CreateEntryRequest_HashedRekordRequestV002{ HashedRekordRequestV002: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: b64DecodeOrDie(t, "MEYCIQC59oLS3MsCqm0xCxPOy+8FdQK4RYCZE036s3q1ECfcagIhAJ4ATXlCSdFrklKAS8No0PsAE9uLi37TCbIfRXASJTTb"), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: b64DecodeOrDie(t, "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeLw7gX40qy1z7JUhGMAaaDITbV7p2D+C5G9xPEsy/PVAo9H0mgS4NYzpGirkXxBht+IvvL19WR1X9ANXha5ldQ=="), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: hexDecodeOrDie(t, "5b3513f580c8397212ff2c8f459c199efc0c90e4354a5f3533adf0a3fff3a530"), }, }, }, addFn: func() (*rekor_pb.TransparencyLogEntry, error) { return nil, tessera.InclusionProofVerificationError{} }, clientSigningAlgorithms: []string{"ecdsa-sha2-256-nistp256"}, expectError: fmt.Errorf("failed to integrate entry"), expectedCode: codes.Unknown, }, { name: "failed integration", req: &pb.CreateEntryRequest{ Spec: &pb.CreateEntryRequest_HashedRekordRequestV002{ HashedRekordRequestV002: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: b64DecodeOrDie(t, "MEYCIQC59oLS3MsCqm0xCxPOy+8FdQK4RYCZE036s3q1ECfcagIhAJ4ATXlCSdFrklKAS8No0PsAE9uLi37TCbIfRXASJTTb"), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: b64DecodeOrDie(t, "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeLw7gX40qy1z7JUhGMAaaDITbV7p2D+C5G9xPEsy/PVAo9H0mgS4NYzpGirkXxBht+IvvL19WR1X9ANXha5ldQ=="), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: hexDecodeOrDie(t, "5b3513f580c8397212ff2c8f459c199efc0c90e4354a5f3533adf0a3fff3a530"), }, }, }, addFn: func() (*rekor_pb.TransparencyLogEntry, error) { return nil, fmt.Errorf("timed out") }, clientSigningAlgorithms: []string{"ecdsa-sha2-256-nistp256"}, expectError: fmt.Errorf("failed to integrate entry"), expectedCode: codes.Unknown, }, { name: "hashedrekord signed with disallowed algorithm", req: &pb.CreateEntryRequest{ Spec: &pb.CreateEntryRequest_HashedRekordRequestV002{ HashedRekordRequestV002: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: b64DecodeOrDie(t, "MEYCIQC59oLS3MsCqm0xCxPOy+8FdQK4RYCZE036s3q1ECfcagIhAJ4ATXlCSdFrklKAS8No0PsAE9uLi37TCbIfRXASJTTb"), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(`-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeLw7gX40qy1z7JUhGMAaaDITbV7p 2D+C5G9xPEsy/PVAo9H0mgS4NYzpGirkXxBht+IvvL19WR1X9ANXha5ldQ== -----END PUBLIC KEY-----`), }, }, }, }, Digest: hexDecodeOrDie(t, "5b3513f580c8397212ff2c8f459c199efc0c90e4354a5f3533adf0a3fff3a530"), }, }, }, addFn: func() (*rekor_pb.TransparencyLogEntry, error) { return &rekor_pb.TransparencyLogEntry{}, nil }, clientSigningAlgorithms: []string{"rsa-sign-pkcs1-4096-sha256"}, expectError: fmt.Errorf("invalid hashedrekord request"), expectedCode: codes.InvalidArgument, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { storage := &mockStorage{addFn: test.addFn} algReg, err := algorithmregistry.AlgorithmRegistry(test.clientSigningAlgorithms) if err != nil { t.Fatal(err) } server := NewServer(storage, false, algReg, []byte{1}) gotTle, gotErr := server.CreateEntry(context.Background(), test.req) if test.expectError == nil { assert.NoError(t, gotErr) assert.NotNil(t, gotTle) // Check that the fields set by the service are populated assert.NotNil(t, gotTle.KindVersion) assert.NotNil(t, gotTle.LogId) } else { s, ok := status.FromError(gotErr) assert.True(t, ok) assert.Equal(t, s.Code(), test.expectedCode) assert.ErrorContains(t, gotErr, test.expectError.Error()) } }) } } type mockStorage struct { addFn func() (*rekor_pb.TransparencyLogEntry, error) } func (s *mockStorage) Add(_ context.Context, _ *ttessera.Entry) (*rekor_pb.TransparencyLogEntry, error) { return s.addFn() } func (s *mockStorage) ReadTile(_ context.Context, _, _ uint64, _ uint8) ([]byte, error) { return nil, nil } func hexDecodeOrDie(t *testing.T, hash string) []byte { decoded, err := hex.DecodeString(hash) if err != nil { t.Fatal(err) } return decoded } func b64DecodeOrDie(t *testing.T, msg string) []byte { decoded, err := base64.StdEncoding.DecodeString(msg) if err != nil { t.Fatal(err) } return decoded } golang-github-sigstore-rekor-tiles-2.0.1/internal/signerverifier/000077500000000000000000000000001511162205500251715ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/internal/signerverifier/file.go000066400000000000000000000027141511162205500264430ustar00rootroot00000000000000/* Copyright 2025 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. */ // Copied from https://github.com/sigstore/rekor/blob/c820fcaf3afdc91f0acf6824d55c1ac7df249df1/pkg/signer/file.go package signerverifier import ( "fmt" "github.com/sigstore/sigstore/pkg/signature" "go.step.sm/crypto/pemutil" ) // File is a file-based signer/verifier. type File struct { signature.SignerVerifier } // NewFileSignerVerifier returns an file-based signer-verifier, used for spinning up local instances. func NewFileSignerVerifier(keyPath, keyPass string) (*File, error) { opaqueKey, err := pemutil.Read(keyPath, pemutil.WithPassword([]byte(keyPass))) if err != nil { return nil, fmt.Errorf("file: provide a valid signer, %s is not valid: %w", keyPath, err) } signerVerifier, err := signature.LoadDefaultSignerVerifier(opaqueKey) if err != nil { return nil, fmt.Errorf(`file: loaded private key from %s can't be used to sign: %w`, keyPath, err) } return &File{signerVerifier}, nil } golang-github-sigstore-rekor-tiles-2.0.1/internal/signerverifier/file_test.go000066400000000000000000000036061511162205500275030ustar00rootroot00000000000000/* Copyright 2025 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. */ // Copied from https://github.com/sigstore/rekor/blob/c820fcaf3afdc91f0acf6824d55c1ac7df249df1/pkg/signer/file_test.go package signerverifier import ( "os" "path/filepath" "testing" ) const testEcdsaKey = ` -----BEGIN EC PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-256-CBC,1ee56fe067d83265fe430391edfa6586 W5NqqRe5rOVe4OvxehYKm6wscR1JFoyRyd8M+Rutp8Q2lxPuKFhR4FZ61b0yy6pr LGJGQWOTIZxrNZ8g4JeS9I3huDWGloZRI2fbTg69HK4EiQQWUc1wS1TWAVoaf4fr LclBWxp2UzqHDaNJ0/2DoGFZhaeMU84VA1O41lO+p5Cx4bms0yWeEHwOrf2AmnNY l5Zm9zoPpXxaDEPSTs5c1loRmmxPHKgb68oZPxEnsCg= -----END EC PRIVATE KEY-----` func TestFile(t *testing.T) { testKeyPass := `password123` td := t.TempDir() keyFile := filepath.Join(td, "ecdsa-key.pem") if err := os.WriteFile(keyFile, []byte(testEcdsaKey), 0644); err != nil { t.Fatal(err) } tests := []struct { name string keyPath string keyPass string wantErr bool }{ { name: "valid ecdsa", keyPath: keyFile, keyPass: testKeyPass, wantErr: false, }, { name: "invalid pass", keyPath: keyFile, keyPass: "123", wantErr: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { tc := tc _, err := NewFileSignerVerifier(tc.keyPath, tc.keyPass) if tc.wantErr != (err != nil) { t.Errorf("NewFile() expected %t, got err %s", tc.wantErr, err) } }) } } golang-github-sigstore-rekor-tiles-2.0.1/internal/signerverifier/signerverifier.go000066400000000000000000000054611511162205500305510ustar00rootroot00000000000000/* Copyright 2025 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. */ // Copied from https://github.com/sigstore/rekor/blob/c820fcaf3afdc91f0acf6824d55c1ac7df249df1/pkg/signer/signer.go package signerverifier import ( "context" "crypto" "fmt" "strings" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/kms" "golang.org/x/exp/slices" // these are imported to load the providers via init() calls _ "github.com/sigstore/sigstore/pkg/signature/kms/aws" _ "github.com/sigstore/sigstore/pkg/signature/kms/azure" _ "github.com/sigstore/sigstore/pkg/signature/kms/gcp" _ "github.com/sigstore/sigstore/pkg/signature/kms/hashivault" ) // New returns a SignerVerifier for the given KMS provider, Tink, or a private key file on disk. func New(ctx context.Context, opts ...Option) (signature.SignerVerifier, error) { sc := &signerVerifierConfig{} for _, o := range opts { o(sc) } switch { case slices.ContainsFunc(kms.SupportedProviders(), func(s string) bool { return strings.HasPrefix(sc.kms, s) }): return kms.Get(ctx, sc.kms, sc.kmsHash, sc.kmsRPCOpts...) case sc.tinkKEKURI != "": return NewTinkSignerVerifier(ctx, sc.tinkKEKURI, sc.tinkKeysetPath) case sc.filePath != "": return NewFileSignerVerifier(sc.filePath, sc.password) default: return nil, fmt.Errorf("insufficient signing parameters provided, must configure one of file, KMS, or Tink signer-verifiers") } } type signerVerifierConfig struct { filePath string password string kms string kmsHash crypto.Hash kmsRPCOpts []signature.RPCOption tinkKEKURI string tinkKeysetPath string } type Option func(*signerVerifierConfig) // WithFile configures a file-based signer-verifier with an optional password. func WithFile(filePath, password string) Option { return func(sc *signerVerifierConfig) { sc.filePath = filePath sc.password = password } } // WithKMS configures a KMS signer-verifier. func WithKMS(kms string, hash crypto.Hash, rpcOpts []signature.RPCOption) Option { return func(sc *signerVerifierConfig) { sc.kms = kms sc.kmsHash = hash sc.kmsRPCOpts = rpcOpts } } // WithTink configures a Tink signer-verifier. func WithTink(kekURI, keysetPath string) Option { return func(sc *signerVerifierConfig) { sc.tinkKEKURI = kekURI sc.tinkKeysetPath = keysetPath } } golang-github-sigstore-rekor-tiles-2.0.1/internal/signerverifier/tink.go000066400000000000000000000060371511162205500264730ustar00rootroot00000000000000// Copyright 2025 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. // Copied from https://github.com/sigstore/rekor/blob/c820fcaf3afdc91f0acf6824d55c1ac7df249df1/pkg/signer/tink.go package signerverifier import ( "context" "errors" "fmt" "os" "path/filepath" "strings" tinkUtils "github.com/sigstore/sigstore/pkg/signature/tink" "github.com/tink-crypto/tink-go-awskms/v2/integration/awskms" "github.com/tink-crypto/tink-go-gcpkms/v2/integration/gcpkms" "github.com/tink-crypto/tink-go/v2/core/registry" "github.com/tink-crypto/tink-go/v2/keyset" "github.com/tink-crypto/tink-go/v2/tink" "github.com/sigstore/sigstore/pkg/signature" ) const TinkScheme = "tink" // NewTinkSignerVerifier returns a signature.SignerVerifier that wraps crypto.Signer and a hash function. // Provide a path to the encrypted keyset and cloud KMS key URI for decryption func NewTinkSignerVerifier(ctx context.Context, kekURI, keysetPath string) (signature.SignerVerifier, error) { if kekURI == "" || keysetPath == "" { return nil, fmt.Errorf("key encryption key URI or keyset path unset") } kek, err := getKeyEncryptionKey(ctx, kekURI) if err != nil { return nil, err } return NewTinkSignerVerifierWithHandle(kek, keysetPath) } // NewTinkSignerVerifierWithHandle returns a signature.SignerVerifier that wraps crypto.Signer and a hash function. // Provide a path to the encrypted keyset and a key handle for decrypting the keyset func NewTinkSignerVerifierWithHandle(kek tink.AEAD, keysetPath string) (signature.SignerVerifier, error) { f, err := os.Open(filepath.Clean(keysetPath)) if err != nil { return nil, err } defer f.Close() kh, err := keyset.Read(keyset.NewJSONReader(f), kek) if err != nil { return nil, err } signer, err := tinkUtils.KeyHandleToSigner(kh) if err != nil { return nil, err } return signature.LoadDefaultSignerVerifier(signer) } // getKeyEncryptionKey returns a Tink AEAD encryption key from KMS // Supports GCP and AWS func getKeyEncryptionKey(ctx context.Context, kmsKey string) (tink.AEAD, error) { switch { case strings.HasPrefix(kmsKey, "gcp-kms://"): gcpClient, err := gcpkms.NewClientWithOptions(ctx, kmsKey) if err != nil { return nil, err } registry.RegisterKMSClient(gcpClient) return gcpClient.GetAEAD(kmsKey) case strings.HasPrefix(kmsKey, "aws-kms://"): awsClient, err := awskms.NewClientWithOptions(kmsKey) if err != nil { return nil, err } registry.RegisterKMSClient(awsClient) return awsClient.GetAEAD(kmsKey) default: return nil, errors.New("unsupported KMS key type") } } golang-github-sigstore-rekor-tiles-2.0.1/internal/signerverifier/tink_test.go000066400000000000000000000055361511162205500275350ustar00rootroot00000000000000// Copyright 2025 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. // Copied from https://github.com/sigstore/rekor/blob/c820fcaf3afdc91f0acf6824d55c1ac7df249df1/pkg/signer/tink_test.go package signerverifier import ( "os" "path/filepath" "strings" "testing" "github.com/sigstore/sigstore/pkg/cryptoutils" _ "github.com/sigstore/sigstore/pkg/signature/kms/fake" tinkUtils "github.com/sigstore/sigstore/pkg/signature/tink" "github.com/tink-crypto/tink-go/v2/aead" "github.com/tink-crypto/tink-go/v2/keyset" "github.com/tink-crypto/tink-go/v2/signature" ) func TestNewTinkCA(t *testing.T) { aeskh, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) if err != nil { t.Fatalf("error creating AEAD key handle: %v", err) } a, err := aead.New(aeskh) if err != nil { t.Fatalf("error creating AEAD key: %v", err) } kh, err := keyset.NewHandle(signature.ECDSAP256KeyTemplate()) if err != nil { t.Fatalf("error creating ECDSA key handle: %v", err) } khsigner, err := tinkUtils.KeyHandleToSigner(kh) if err != nil { t.Fatalf("error converting ECDSA key handle to signer: %v", err) } dir := t.TempDir() keysetPath := filepath.Join(dir, "keyset.json.enc") f, err := os.Create(keysetPath) if err != nil { t.Fatalf("error creating file: %v", err) } defer f.Close() jsonWriter := keyset.NewJSONWriter(f) if err := kh.Write(jsonWriter, a); err != nil { t.Fatalf("error writing enc keyset: %v", err) } signer, err := NewTinkSignerVerifierWithHandle(a, keysetPath) if err != nil { t.Fatalf("unexpected error creating signer: %v", err) } // Expect keyset signer and created signer public key match pubKey, err := signer.PublicKey() if err != nil { t.Fatalf("unexpected error creating signer public key: %v", err) } if err := cryptoutils.EqualKeys(pubKey, khsigner.Public()); err != nil { t.Fatalf("keys between CA and signer do not match: %v", err) } // Failure: Unable to decrypt keyset aeskhAlt, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) if err != nil { t.Fatalf("error creating AEAD key handle: %v", err) } aeadAlt, err := aead.New(aeskhAlt) if err != nil { t.Fatalf("error creating AEAD key: %v", err) } _, err = NewTinkSignerVerifierWithHandle(aeadAlt, keysetPath) if err == nil || !strings.Contains(err.Error(), "decryption failed") { t.Fatalf("expected error decrypting keyset, got %v", err) } } golang-github-sigstore-rekor-tiles-2.0.1/internal/tessera/000077500000000000000000000000001511162205500236145ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/internal/tessera/gcp.go000066400000000000000000000045101511162205500247140ustar00rootroot00000000000000// // Copyright 2025 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 tessera import ( "context" "fmt" "runtime" "cloud.google.com/go/spanner" gcs "cloud.google.com/go/storage" "github.com/transparency-dev/tessera" "github.com/transparency-dev/tessera/storage/gcp" antispam "github.com/transparency-dev/tessera/storage/gcp/antispam" "google.golang.org/api/option" "sigs.k8s.io/release-utils/version" ) // NewGCPDriver returns a GCP Tessera Driver for the given bucket and spanner URI. func NewGCPDriver(ctx context.Context, bucket, spannerDB, hostname string) (tessera.Driver, error) { userAgent := userAgent(hostname) gcsClient, err := gcs.NewClient(ctx, gcs.WithJSONReads(), option.WithUserAgent(userAgent)) if err != nil { return nil, fmt.Errorf("creating storage client: %w", err) } spannerClient, err := spanner.NewClient(ctx, spannerDB, option.WithUserAgent(userAgent)) if err != nil { return nil, fmt.Errorf("creating spanner client: %w", err) } cfg := gcp.Config{ Bucket: bucket, Spanner: spannerDB, GCSClient: gcsClient, SpannerClient: spannerClient, } driver, err := gcp.New(ctx, cfg) if err != nil { return nil, fmt.Errorf("getting tessera GCP driver: %w", err) } return driver, nil } // NewGCPAntispam initializes a Spanner database to store recent entries func NewGCPAntispam(ctx context.Context, spannerDb string, maxBatchSize, pushbackThreshold uint) (tessera.Antispam, error) { asOpts := antispam.AntispamOpts{ MaxBatchSize: maxBatchSize, PushbackThreshold: pushbackThreshold, } dbName := fmt.Sprintf("%s-antispam", spannerDb) return antispam.NewAntispam(ctx, dbName, asOpts) } func userAgent(hostname string) string { return fmt.Sprintf("rekor-tiles/%s (%s; %s) %s", version.GetVersionInfo().GitVersion, runtime.GOOS, runtime.GOARCH, hostname) } golang-github-sigstore-rekor-tiles-2.0.1/internal/tessera/tessera.go000066400000000000000000000221121511162205500256070ustar00rootroot00000000000000// // Copyright 2025 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 tessera import ( "context" "fmt" "log/slog" "time" rekor_pb "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "github.com/sigstore/rekor-tiles/v2/internal/safeint" "github.com/sigstore/rekor-tiles/v2/pkg/note" "github.com/sigstore/sigstore/pkg/signature" logformat "github.com/transparency-dev/formats/log" "github.com/transparency-dev/merkle/proof" "github.com/transparency-dev/merkle/rfc6962" "github.com/transparency-dev/tessera" "github.com/transparency-dev/tessera/client" ) const ( DefaultBatchMaxSize = tessera.DefaultBatchMaxSize DefaultBatchMaxAge = tessera.DefaultBatchMaxAge DefaultCheckpointInterval = tessera.DefaultCheckpointInterval DefaultPushbackMaxOutstanding = tessera.DefaultPushbackMaxOutstanding ) type DuplicateError struct { index uint64 } func (e DuplicateError) Error() string { return fmt.Sprintf("an equivalent entry already exists in the transparency log with index %d", e.index) } type InclusionProofVerificationError struct { index uint64 err error } func (e InclusionProofVerificationError) Error() string { return fmt.Sprintf("verifying inclusion proof for index %d: %v", e.index, e.err) } // Storage provides the functions to add entries to a Tessera log. type Storage interface { Add(ctx context.Context, entry *tessera.Entry) (*rekor_pb.TransparencyLogEntry, error) ReadTile(ctx context.Context, level, index uint64, p uint8) ([]byte, error) } type storage struct { origin string awaiter *tessera.PublicationAwaiter addFn tessera.AddFn readTileFn client.TileFetcherFunc } // NewAppendOptions initializes the Tessera append options with a checkpoint signer, which is the only non-optional append option. func NewAppendOptions(ctx context.Context, origin string, signer signature.Signer) (*tessera.AppendOptions, error) { opts := tessera.NewAppendOptions() noteSigner, err := note.NewNoteSigner(ctx, origin, signer) if err != nil { return nil, fmt.Errorf("getting note signer: %w", err) } opts = opts.WithCheckpointSigner(noteSigner) return opts, nil } // WithLifecycleOptions accepts an initialized AppendOptions and adds batching, checkpoint, and pushback options to it. // It returns the mutated options object for readability. func WithLifecycleOptions(opts *tessera.AppendOptions, batchMaxSize uint, baxMaxAge time.Duration, checkpointInterval time.Duration, pushback uint) *tessera.AppendOptions { opts = opts.WithBatching(batchMaxSize, baxMaxAge) opts = opts.WithCheckpointInterval(checkpointInterval) opts = opts.WithPushback(pushback) return opts } // WithAntispamOptions accepts an initialized AppendOptions and adds antispam options to it. // Accepts an optional persistent antispam provider. If nil, antispam does not persist between // server restarts. Returns the mutated options object for readability. func WithAntispamOptions(opts *tessera.AppendOptions, as tessera.Antispam) *tessera.AppendOptions { inMemoryLRUSize := uint(256) // There's no documentation providing guidance on this cache size. Use a hard-coded value for now and consider exposing it as a configuration option later. opts = opts.WithAntispam(inMemoryLRUSize, as) return opts } func WithWitnessing(opts *tessera.AppendOptions, witnessPolicy []byte) (*tessera.AppendOptions, error) { wg, err := tessera.NewWitnessGroupFromPolicy(witnessPolicy) if err != nil { return nil, fmt.Errorf("creating witness group from policy: %w", err) } // Don't block if witnesses are unavailable. wOpts := &tessera.WitnessOptions{FailOpen: true} opts.WithWitnesses(wg, wOpts) return opts, nil } // DriverConfiguration contains storage-specific configuration for each supported storage backend. type DriverConfiguration struct { // Server origin Hostname string // GCP configuration GCPBucket string GCPSpannerDB string // Antispam configuration PersistentAntispam bool ASMaxBatchSize uint ASPushbackThreshold uint } // NewDriver creates a Tessera driver and optional persistent antispam for a given storage backend. func NewDriver(ctx context.Context, config DriverConfiguration) (tessera.Driver, tessera.Antispam, error) { switch { case config.GCPBucket != "" && config.GCPSpannerDB != "": driver, err := NewGCPDriver(ctx, config.GCPBucket, config.GCPSpannerDB, config.Hostname) if err != nil { return nil, nil, fmt.Errorf("failed to initialize GCP driver: %v", err.Error()) } var persistentAntispam tessera.Antispam if config.PersistentAntispam { as, err := NewGCPAntispam(ctx, config.GCPSpannerDB, config.ASMaxBatchSize, config.ASPushbackThreshold) if err != nil { return nil, nil, fmt.Errorf("failed to initialize GCP antispam: %v", err.Error()) } persistentAntispam = as } return driver, persistentAntispam, nil default: return nil, nil, fmt.Errorf("no flags provided to initialize Tessera driver") } } // NewStorage creates a Tessera storage object for the provided driver and signer. // Returns the storage object and a function that must be called when shutting down the server. func NewStorage(ctx context.Context, origin string, driver tessera.Driver, appendOptions *tessera.AppendOptions) (Storage, func(context.Context) error, error) { appender, shutdown, reader, err := tessera.NewAppender(ctx, driver, appendOptions) if err != nil { return nil, nil, fmt.Errorf("getting tessera appender: %w", err) } slog.Info("starting Tessera sequencer") awaiter := tessera.NewPublicationAwaiter(ctx, reader.ReadCheckpoint, 1*time.Second) return &storage{ origin: origin, awaiter: awaiter, addFn: appender.Add, readTileFn: reader.ReadTile, }, shutdown, nil } // Add adds a Tessera entry to the log, waits for it to be sequenced into the log, // and returns the log index and inclusion proof as a TransparencyLogEntry object. func (s *storage) Add(ctx context.Context, entry *tessera.Entry) (*rekor_pb.TransparencyLogEntry, error) { idx, dup, checkpointBody, err := s.addEntry(ctx, entry) if err != nil { return nil, fmt.Errorf("add entry: %w", err) } if dup { return nil, DuplicateError{index: idx.U()} } inclusionProof, err := s.buildProof(ctx, idx, checkpointBody, entry.LeafHash()) if err != nil { return nil, fmt.Errorf("building inclusion proof: %w", err) } return &rekor_pb.TransparencyLogEntry{ LogIndex: idx.I(), InclusionProof: inclusionProof, CanonicalizedBody: entry.Data(), }, nil } // ReadTile looks up the tile at the given level, index within the level, and // width of the tile if partial, and returns the raw bytes of the tile. func (s *storage) ReadTile(ctx context.Context, level, index uint64, p uint8) ([]byte, error) { tile, err := s.readTileFn(ctx, level, index, p) if err != nil { return nil, fmt.Errorf("reading tile level %d index %d p %d: %w", level, index, p, err) } return tile, nil } func (s *storage) addEntry(ctx context.Context, entry *tessera.Entry) (*safeint.SafeInt64, bool, []byte, error) { idx, checkpointBody, err := s.awaiter.Await(ctx, s.addFn(ctx, entry)) if err != nil { return nil, false, nil, fmt.Errorf("await: %w", err) } safeIdx, err := safeint.NewSafeInt64(idx.Index) if err != nil { return nil, false, nil, fmt.Errorf("invalid index: %w", err) } return safeIdx, idx.IsDup, checkpointBody, nil } func (s *storage) buildProof(ctx context.Context, idx *safeint.SafeInt64, signedCheckpoint, leafHash []byte) (*rekor_pb.InclusionProof, error) { checkpoint, err := unmarshalCheckpoint(signedCheckpoint) if err != nil { return nil, fmt.Errorf("unmarshalling checkpoint: %w", err) } proofBuilder, err := client.NewProofBuilder(ctx, checkpoint.Size, s.ReadTile) if err != nil { return nil, fmt.Errorf("new proof builder: %w", err) } inclusionProof, err := proofBuilder.InclusionProof(ctx, idx.U()) if err != nil { return nil, fmt.Errorf("generating inclusion proof: %w", err) } safeCheckpointSize, err := safeint.NewSafeInt64(checkpoint.Size) if err != nil { return nil, fmt.Errorf("invalid tree size: %d", checkpoint.Size) } if err := proof.VerifyInclusion(rfc6962.DefaultHasher, idx.U(), safeCheckpointSize.U(), leafHash, inclusionProof, checkpoint.Hash); err != nil { return nil, InclusionProofVerificationError{idx.U(), err} } return &rekor_pb.InclusionProof{ LogIndex: idx.I(), RootHash: checkpoint.Hash, TreeSize: safeCheckpointSize.I(), Hashes: inclusionProof, Checkpoint: &rekor_pb.Checkpoint{ Envelope: string(signedCheckpoint), }, }, nil } func unmarshalCheckpoint(checkpointBody []byte) (logformat.Checkpoint, error) { checkpoint := logformat.Checkpoint{} _, err := checkpoint.Unmarshal(checkpointBody) return checkpoint, err } golang-github-sigstore-rekor-tiles-2.0.1/internal/tessera/tessera_test.go000066400000000000000000000122751511162205500266570ustar00rootroot00000000000000// // Copyright 2025 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 tessera import ( "context" "encoding/hex" "fmt" "testing" "time" "github.com/sigstore/sigstore/pkg/signature" "github.com/stretchr/testify/assert" "github.com/transparency-dev/tessera" ) func TestAdd(t *testing.T) { ctx := context.Background() tileHash := hexDecodeOrDie(t, "81bfc09c412c04da53a1b0ddb94dce48d6a24e9ea58987f7d45a04e8007fb3ca") readCheckpoint := func(_ context.Context) ([]byte, error) { <-time.After(5 * time.Millisecond) return []byte(`test.origin 1 gb/AnEEsBNpTobDduU3OSNaiTp6liYf31FoE6AB/s8o= — test.origin AAAAAW5vb3AKMQpnYi9BbkVFc0JOcFRvYkRkdVUzT1NOYWlUcDZsaVlmMzFGb0U2QUIvczhvPQo=`), nil } s := storage{ awaiter: tessera.NewPublicationAwaiter(ctx, readCheckpoint, 10*time.Millisecond), readTileFn: func(_ context.Context, _, _ uint64, _ uint8) ([]byte, error) { return tileHash, nil }, } entry := tessera.NewEntry([]byte("stuff")) tests := []struct { name string addFn func(context.Context, *tessera.Entry) tessera.IndexFuture expectErr error expectLogIndex int64 expectTreeSize int64 expectHash []byte expectBody []byte }{ { name: "success", addFn: func(_ context.Context, _ *tessera.Entry) tessera.IndexFuture { return func() (tessera.Index, error) { return tessera.Index{Index: 0}, nil } }, expectLogIndex: int64(0), expectTreeSize: int64(1), expectHash: tileHash, expectBody: []byte("stuff"), }, { name: "integration failed", addFn: func(_ context.Context, _ *tessera.Entry) tessera.IndexFuture { return func() (tessera.Index, error) { return tessera.Index{Index: 0}, fmt.Errorf("server error") } }, expectErr: fmt.Errorf("add entry: await: server error"), }, { name: "duplicate entry", addFn: func(_ context.Context, _ *tessera.Entry) tessera.IndexFuture { return func() (tessera.Index, error) { return tessera.Index{Index: 0, IsDup: true}, nil } }, expectErr: fmt.Errorf("an equivalent entry already exists in the transparency log with index 0"), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { s.addFn = test.addFn got, gotErr := s.Add(ctx, entry) if test.expectErr != nil { assert.ErrorContains(t, gotErr, test.expectErr.Error()) return } assert.NoError(t, gotErr) assert.Equal(t, test.expectLogIndex, got.LogIndex) assert.Equal(t, test.expectTreeSize, got.InclusionProof.TreeSize) assert.Equal(t, test.expectHash, got.InclusionProof.RootHash) assert.Equal(t, test.expectBody, got.CanonicalizedBody) }) } } func TestReadTile(t *testing.T) { ctx := context.Background() tileHash := hexDecodeOrDie(t, "81bfc09c412c04da53a1b0ddb94dce48d6a24e9ea58987f7d45a04e8007fb3ca") s := storage{ readTileFn: func(_ context.Context, level, index uint64, _ uint8) ([]byte, error) { if level != 0 && index != 1 { return nil, fmt.Errorf("not found") } return tileHash, nil }, } tests := []struct { name string level uint64 index uint64 p uint8 expectHash []byte expectErr error }{ { name: "tile exists", level: 0, index: 1, p: 0, expectHash: tileHash, }, { name: "tile doesn't exist", level: 1, index: 2, p: 0, expectErr: fmt.Errorf("reading tile level 1 index 2 p 0: not found"), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { got, gotErr := s.ReadTile(ctx, test.level, test.index, test.p) assert.Equal(t, test.expectHash, got) if test.expectErr != nil { assert.ErrorContains(t, gotErr, test.expectErr.Error()) } else { assert.NoError(t, gotErr) } }) } } func TestAppendOptions(t *testing.T) { sv, _, err := signature.NewDefaultECDSASignerVerifier() if err != nil { t.Fatal(err) } ao, err := NewAppendOptions(context.Background(), "test", sv) assert.NoError(t, err) ao = WithLifecycleOptions(ao, 42, 42*time.Millisecond, 42*time.Second, 42) assert.Equal(t, uint(42), ao.BatchMaxSize()) assert.Equal(t, 42*time.Millisecond, ao.BatchMaxAge()) assert.Equal(t, 42*time.Second, ao.CheckpointInterval()) assert.Equal(t, uint(42), ao.PushbackMaxOutstanding()) ao = WithAntispamOptions(ao, nil) // initializes non-persistent antispam examplePolicy := `witness o1 transparency.dev/DEV:witness-little-garden+4b7fca75+AStusOxINQNUTN5Oj8HObRkh2yHf/MwYaGX4CPdiVEPM https://api.transparency.dev/dev/witness/little-garden quorum o1` _, err = WithWitnessing(ao, []byte(examplePolicy)) assert.NoError(t, err) } func hexDecodeOrDie(t *testing.T, text string) []byte { decoded, err := hex.DecodeString(text) if err != nil { t.Fatal(err) } return decoded } golang-github-sigstore-rekor-tiles-2.0.1/pkg/000077500000000000000000000000001511162205500211135ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/client/000077500000000000000000000000001511162205500223715ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/client/option.go000066400000000000000000000021021511162205500242230ustar00rootroot00000000000000// // Copyright 2025 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 client import "time" // Config contains connection options for the client. type Config struct { UserAgent string Timeout time.Duration } // Option customizes the client Config. type Option func(*Config) // WithUserAgent sets the user agent. func WithUserAgent(agent string) Option { return func(c *Config) { c.UserAgent = agent } } // WithTimeout sets the timeout. func WithTimeout(timeout time.Duration) Option { return func(c *Config) { c.Timeout = timeout } } golang-github-sigstore-rekor-tiles-2.0.1/pkg/client/read/000077500000000000000000000000001511162205500233045ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/client/read/read.go000066400000000000000000000063061511162205500245530ustar00rootroot00000000000000// // Copyright 2025 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 read import ( "context" "fmt" "net/http" "net/url" "github.com/sigstore/rekor-tiles/v2/pkg/client" rekornote "github.com/sigstore/rekor-tiles/v2/pkg/note" "github.com/sigstore/sigstore/pkg/signature" "github.com/transparency-dev/formats/log" tclient "github.com/transparency-dev/tessera/client" "golang.org/x/mod/sumdb/note" ) // Client reads checkpoints, tiles, and entry bundles from the tile storage service. type Client interface { ReadCheckpoint(context.Context) (*log.Checkpoint, *note.Note, error) ReadTile(context.Context, uint64, uint64, uint8) ([]byte, error) ReadEntryBundle(context.Context, uint64, uint8) ([]byte, error) } type readClient struct { baseURL *url.URL client *tclient.HTTPFetcher origin string verifier note.Verifier } // NewReader creates a new reader client. func NewReader(readURL, origin string, verifier signature.Verifier, opts ...client.Option) (Client, error) { cfg := &client.Config{} for _, o := range opts { o(cfg) } baseURL, err := url.Parse(readURL) if err != nil { return nil, fmt.Errorf("parsing url %s: %w", readURL, err) } noteVerifier, err := rekornote.NewNoteVerifier(origin, verifier) if err != nil { return nil, fmt.Errorf("creating note verifier: %w", err) } httpClient := &http.Client{ Transport: client.CreateRoundTripper(http.DefaultTransport, cfg.UserAgent), Timeout: cfg.Timeout, } tileClient, err := tclient.NewHTTPFetcher(baseURL, httpClient) if err != nil { return nil, fmt.Errorf("creating tile client: %w", err) } return &readClient{ baseURL: baseURL, client: tileClient, origin: origin, verifier: noteVerifier, }, nil } // ReadCheckpoint returns the current checkpoint. func (r *readClient) ReadCheckpoint(ctx context.Context) (*log.Checkpoint, *note.Note, error) { readCheckpoint := r.client.ReadCheckpoint cp, _, n, err := tclient.FetchCheckpoint(ctx, readCheckpoint, r.verifier, r.origin) if err != nil { return nil, nil, fmt.Errorf("fetching checkpoint: %w", err) } return cp, n, nil } // ReadTile returns the tile at the given level, index, and tile segment. func (r *readClient) ReadTile(ctx context.Context, level, index uint64, p uint8) ([]byte, error) { tile, err := r.client.ReadTile(ctx, level, index, p) if err != nil { return nil, fmt.Errorf("reading tile: %w", err) } return tile, nil } // ReadEntryBundle returns the entries at the given index. func (r *readClient) ReadEntryBundle(ctx context.Context, index uint64, p uint8) ([]byte, error) { bundle, err := r.client.ReadEntryBundle(ctx, index, p) if err != nil { return nil, fmt.Errorf("reading entry bundle: %w", err) } return bundle, nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/client/read/read_test.go000066400000000000000000000160531511162205500256120ustar00rootroot00000000000000// // Copyright 2025 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 read import ( "context" "crypto/ed25519" "crypto/x509" "encoding/pem" "net/http" "net/http/httptest" "net/url" "testing" "time" "github.com/sigstore/rekor-tiles/v2/pkg/client" "github.com/sigstore/sigstore/pkg/signature" "github.com/stretchr/testify/assert" ) var ed25519PrivKey = ` -----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIGuZ8UWTFmXi/26ZgF4VYL8HfLSuW12TN5XMFQRt1Loc -----END PRIVATE KEY----- ` func TestNewReader(t *testing.T) { readURL := "http://localhost:7080" origin := "rekor-local" verifier, err := getVerifier(ed25519PrivKey) if err != nil { t.Fatal(err) } tests := []struct { name string opts []client.Option expected *readClient }{ { name: "no options", expected: &readClient{ baseURL: &url.URL{Scheme: "http", Host: "localhost:7080", Path: "/"}, origin: "rekor-local", }, }, { name: "with user agent", opts: []client.Option{ client.WithUserAgent("test"), }, expected: &readClient{ baseURL: &url.URL{Scheme: "http", Host: "localhost:7080", Path: "/"}, origin: "rekor-local", }, }, { name: "with timeout", opts: []client.Option{ client.WithTimeout(1 * time.Second), }, expected: &readClient{ baseURL: &url.URL{Scheme: "http", Host: "localhost:7080", Path: "/"}, origin: "rekor-local", }, }, { name: "with both", opts: []client.Option{ client.WithUserAgent("test"), client.WithTimeout(1 * time.Second), }, expected: &readClient{ baseURL: &url.URL{Scheme: "http", Host: "localhost:7080", Path: "/"}, origin: "rekor-local", }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { got, gotErr := NewReader(readURL, origin, verifier, test.opts...) assert.NoError(t, gotErr) assert.Equal(t, test.expected.baseURL, got.(*readClient).baseURL) assert.Equal(t, test.expected.origin, got.(*readClient).origin) }) } } func TestReadCheckpoint(t *testing.T) { tests := []struct { name string respBody []byte respCode int expectErr bool }{ { name: "valid checkpoint", respBody: []byte(`rekor-local 2 vABc4Xj1G9UUySBRYDvTZpYtdDqbKN9XthAbY4Nqd/Y= — rekor-local 2AtEIJwBlAY6KMMNAqcWRKgPZDhP6/bpBmefw4mD89JwL3KozxrLgz7MA8G5pM4UrGNoTOxxpW2bbdv/A5l22ymMLAU= `), respCode: http.StatusOK, expectErr: false, }, { name: "server error", respBody: []byte("unexpected server error"), respCode: http.StatusInternalServerError, expectErr: true, }, { name: "invalid checkpoint", respBody: []byte(`wrong-origin 2 vABc4Xj1G9UUySBRYDvTZpYtdDqbKN9XthAbY4Nqd/Y= — wrong-origin 2AtEIJwBlAY6KMMNAqcWRKgPZDhP6/bpBmefw4mD89JwL3KozxrLgz7MA8G5pM4UrGNoTOxxpW2bbdv/A5l22ymMLAU= `), respCode: http.StatusOK, expectErr: true, }, } verifier, err := getVerifier(ed25519PrivKey) if err != nil { t.Fatal(err) } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctx := context.Background() server := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(test.respCode) w.Write(test.respBody) })) defer server.Close() client, err := NewReader(server.URL, "rekor-local", verifier) if err != nil { t.Fatal(err) } gotCP, gotNote, gotErr := client.ReadCheckpoint(ctx) if test.expectErr { assert.Error(t, gotErr) return } assert.NoError(t, gotErr) assert.NotNil(t, gotCP) assert.NotNil(t, gotNote) }) } } func TestReadTile(t *testing.T) { tests := []struct { name string tileIndex uint64 serverErr bool expectErr bool }{ { name: "server success", tileIndex: 1, expectErr: false, }, { name: "server error", tileIndex: 1, serverErr: true, expectErr: true, }, { name: "out of range", tileIndex: 42, expectErr: true, }, } verifier, err := getVerifier(ed25519PrivKey) if err != nil { t.Fatal(err) } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctx := context.Background() server := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { treeSize := uint64(10) if test.serverErr { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("unexpected server error")) return } if test.tileIndex > treeSize { w.WriteHeader(http.StatusNotFound) w.Write([]byte("not found")) return } w.WriteHeader(http.StatusOK) w.Write([]byte("mozaics")) })) defer server.Close() client, err := NewReader(server.URL, "rekor-local", verifier) if err != nil { t.Fatal(err) } gotTile, gotErr := client.ReadTile(ctx, 0, test.tileIndex, 0) if test.expectErr { assert.Error(t, gotErr) return } assert.NoError(t, gotErr) assert.NotNil(t, gotTile) }) } } func TestReadEntryBundle(t *testing.T) { tests := []struct { name string tileIndex uint64 serverErr bool expectErr bool }{ { name: "server success", tileIndex: 1, expectErr: false, }, { name: "server error", tileIndex: 1, serverErr: true, expectErr: true, }, { name: "out of range", tileIndex: 42, expectErr: true, }, } verifier, err := getVerifier(ed25519PrivKey) if err != nil { t.Fatal(err) } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctx := context.Background() server := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { treeSize := uint64(10) if test.serverErr { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("unexpected server error")) return } if test.tileIndex > treeSize { w.WriteHeader(http.StatusNotFound) w.Write([]byte("not found")) return } w.WriteHeader(http.StatusOK) w.Write([]byte("mozaics")) })) defer server.Close() client, err := NewReader(server.URL, "rekor-local", verifier) if err != nil { t.Fatal(err) } gotTile, gotErr := client.ReadEntryBundle(ctx, test.tileIndex, 0) if test.expectErr { assert.Error(t, gotErr) return } assert.NoError(t, gotErr) assert.NotNil(t, gotTile) }) } } func getVerifier(privKey string) (signature.Verifier, error) { block, _ := pem.Decode([]byte(privKey)) priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { return nil, err } verifier, err := signature.LoadDefaultSignerVerifier(priv.(ed25519.PrivateKey)) if err != nil { return nil, err } return verifier, nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/client/transport.go000066400000000000000000000023511511162205500247550ustar00rootroot00000000000000// // Copyright 2025 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 client import ( "net/http" ) type roundTripper struct { http.RoundTripper userAgent string } // RoundTrip implements http.RoundTripper. func (rt *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if rt.userAgent != "" { req.Header.Set("User-Agent", rt.userAgent) } return rt.RoundTripper.RoundTrip(req) } // CreateRoundTripper creates an http.RoundTripper. func CreateRoundTripper(inner http.RoundTripper, userAgent string) http.RoundTripper { if inner == nil { inner = http.DefaultTransport } if userAgent == "" { return inner } return &roundTripper{ RoundTripper: inner, userAgent: userAgent, } } golang-github-sigstore-rekor-tiles-2.0.1/pkg/client/write/000077500000000000000000000000001511162205500235235ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/client/write/write.go000066400000000000000000000071411511162205500252070ustar00rootroot00000000000000// // Copyright 2025 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 write import ( "bytes" "context" "fmt" "io" "net/http" "net/url" "path" pbs "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "github.com/sigstore/rekor-tiles/v2/pkg/client" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" "google.golang.org/protobuf/encoding/protojson" ) const ( addPath = "/api/v2/log/entries" ) // Client writes entries to rekor. type Client interface { Add(context.Context, any) (*pbs.TransparencyLogEntry, error) } type writeClient struct { baseURL *url.URL client *http.Client } // NewWriter creates a new writer client. func NewWriter(writeURL string, opts ...client.Option) (Client, error) { cfg := &client.Config{} for _, o := range opts { o(cfg) } baseURL, err := url.Parse(writeURL) if err != nil { return nil, fmt.Errorf("parsing url %s: %w", writeURL, err) } httpClient := &http.Client{ Transport: client.CreateRoundTripper(http.DefaultTransport, cfg.UserAgent), Timeout: cfg.Timeout, } return &writeClient{ baseURL: baseURL, client: httpClient, }, nil } // Add uploads a hashedrekord or DSSE log entry and returns the TransparencyLogEntry proving the entry's inclusion in the log. func (w *writeClient) Add(ctx context.Context, entry any) (*pbs.TransparencyLogEntry, error) { cer, err := createRequest(entry) if err != nil { return nil, fmt.Errorf("creating request: %w", err) } endpoint := *w.baseURL endpoint.Path = path.Join(endpoint.Path, addPath) payload, err := protojson.Marshal(cer) if err != nil { return nil, fmt.Errorf("marshaling request: %w", err) } req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), bytes.NewBuffer(payload)) if err != nil { return nil, fmt.Errorf("creating request: %w", err) } req.Header.Set("Content-Type", "application/json") resp, err := w.client.Do(req) if err != nil { return nil, fmt.Errorf("getting response: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("reading response: %w", err) } if resp.StatusCode != http.StatusCreated { return nil, fmt.Errorf("unexpected response: %v %v", resp.StatusCode, string(body)) } tle := pbs.TransparencyLogEntry{} err = protojson.Unmarshal(body, &tle) if err != nil { return nil, fmt.Errorf("unmarshaling response body: %w", err) } return &tle, nil } func createRequest(entry any) (*pb.CreateEntryRequest, error) { switch e := entry.(type) { case *pb.HashedRekordRequestV002: return createHashedRekordRequest(e), nil case *pb.DSSERequestV002: return createDSSERequest(e), nil default: return nil, fmt.Errorf("unsupported entry type: %T", entry) } } func createHashedRekordRequest(h *pb.HashedRekordRequestV002) *pb.CreateEntryRequest { return &pb.CreateEntryRequest{ Spec: &pb.CreateEntryRequest_HashedRekordRequestV002{ HashedRekordRequestV002: h, }, } } func createDSSERequest(d *pb.DSSERequestV002) *pb.CreateEntryRequest { return &pb.CreateEntryRequest{ Spec: &pb.CreateEntryRequest_DsseRequestV002{ DsseRequestV002: d, }, } } golang-github-sigstore-rekor-tiles-2.0.1/pkg/client/write/write_test.go000066400000000000000000000171221511162205500262460ustar00rootroot00000000000000// // Copyright 2025 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 write import ( "context" "encoding/base64" "encoding/json" "fmt" "net/http" "net/http/httptest" "net/url" "testing" "time" "github.com/sigstore/protobuf-specs/gen/pb-go/dsse" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" pbs "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "github.com/sigstore/rekor-tiles/v2/pkg/client" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" "github.com/stretchr/testify/assert" ) func TestNewWriter(t *testing.T) { writeURL := "http://localhost:3003" tests := []struct { name string opts []client.Option expected *writeClient }{ { name: "no options", expected: &writeClient{ baseURL: &url.URL{Scheme: "http", Host: "localhost:3003"}, client: &http.Client{Transport: http.DefaultTransport}, }, }, { name: "with user agent", opts: []client.Option{ client.WithUserAgent("test"), }, expected: &writeClient{ baseURL: &url.URL{Scheme: "http", Host: "localhost:3003"}, client: &http.Client{Transport: client.CreateRoundTripper(nil, "test")}, }, }, { name: "with timeout", opts: []client.Option{ client.WithTimeout(1 * time.Second), }, expected: &writeClient{ baseURL: &url.URL{Scheme: "http", Host: "localhost:3003"}, client: &http.Client{Transport: http.DefaultTransport, Timeout: 1 * time.Second}, }, }, { name: "with both", opts: []client.Option{ client.WithUserAgent("test"), client.WithTimeout(1 * time.Second), }, expected: &writeClient{ baseURL: &url.URL{Scheme: "http", Host: "localhost:3003"}, client: &http.Client{Transport: client.CreateRoundTripper(nil, "test"), Timeout: 1 * time.Second, }, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { got, gotErr := NewWriter(writeURL, test.opts...) assert.NoError(t, gotErr) assert.Equal(t, test.expected.baseURL, got.(*writeClient).baseURL) assert.Equal(t, test.expected.client, got.(*writeClient).client) }) } } func TestAdd(t *testing.T) { tests := []struct { name string entry any respBody []byte respCode int expectErr error }{ { name: "valid hashedrekord", entry: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: []byte("sign"), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte("key"), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: []byte("digest"), }, respBody: marshalJSONOrDie(t, pbs.TransparencyLogEntry{ LogIndex: 1, InclusionProof: &pbs.InclusionProof{ LogIndex: 1, RootHash: b64DecodeOrDie(t, "NmVmNWM2YzY2NzcxNjI0YmZmMDI1ZDRmMGNkODRkYWVmYjI0MzIzMzk4MmU5MDA5M2Y0NDBkNDM5ODliY2ZiYgo="), TreeSize: 2, Hashes: [][]byte{ b64DecodeOrDie(t, "d/XeBMMsM7fy/gMiRBFow5u6dOud2RFlKQy20qNSB8w="), }, Checkpoint: &pbs.Checkpoint{ Envelope: "rekor-local\n2\nbvXGxmdxYkv/Al1PDNhNrvskMjOYLpAJP0QNQ5ibz7s=\n\n— rekor-local 2AtEIKMQNqn1+JpFEwM4yv/nDtVtu0B6yGRkOLpbHP9tQMF493hYOgRKUhp9ZNylWSJebqOThUpdO03LzJn3/6K5HQc=\n", }, }, CanonicalizedBody: []byte(`{"data":{"algorithm":"SHA2_256","digest":"ZGlnZXN0"},"signature":"c2lnbg==","verifier":{"publicKey":{"rawBytes":"a2V5"}}}`), }), respCode: http.StatusCreated, expectErr: nil, }, { name: "valid dsse", entry: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: []byte("some payload"), PayloadType: "", Signatures: []*dsse.Signature{ { Sig: []byte("some signature"), Keyid: "abcd", }, }, }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte("key"), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, respBody: marshalJSONOrDie(t, pbs.TransparencyLogEntry{ LogIndex: 1, InclusionProof: &pbs.InclusionProof{ RootHash: b64DecodeOrDie(t, "YmMwMDVjZTE3OGY1MWJkNTE0YzkyMDUxNjAzYmQzNjY5NjJkNzQzYTliMjhkZjU3YjYxMDFiNjM4MzZhNzdmNg=="), TreeSize: 2, Hashes: [][]byte{ b64DecodeOrDie(t, "wWB4RFtzi4KkguQYjzcUge9No4fwgGMVdtQt6ls5B0I="), }, Checkpoint: &pbs.Checkpoint{ Envelope: "rekor-local\n2\nvABc4Xj1G9UUySBRYDvTZpYtdDqbKN9XthAbY4Nqd/Y=\n\n— rekor-local 2AtEIJwBlAY6KMMNAqcWRKgPZDhP6/bpBmefw4mD89JwL3KozxrLgz7MA8G5pM4UrGNoTOxxpW2bbdv/A5l22ymMLAU=\n", }, }, CanonicalizedBody: []byte(`{"envelope":"dsse","verifier":[{"publicKey":{"rawBytes":"a2V5"}}]}`), }), respCode: http.StatusCreated, expectErr: nil, }, { name: "invalid entry type", entry: "intoto entry", expectErr: fmt.Errorf("unsupported entry type: string"), }, { name: "server error", entry: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: []byte("some payload"), PayloadType: "", Signatures: []*dsse.Signature{ { Sig: []byte("some signature"), Keyid: "abcd", }, }, }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte("key"), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, respBody: []byte("server died"), respCode: http.StatusInternalServerError, expectErr: fmt.Errorf("unexpected response: 500 server died"), }, { name: "unexpected response body from server", entry: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: []byte("some payload"), PayloadType: "", Signatures: []*dsse.Signature{ { Sig: []byte("some signature"), Keyid: "abcd", }, }, }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte("key"), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, respBody: []byte("i love ice cream"), respCode: http.StatusCreated, expectErr: fmt.Errorf("unmarshaling response body: proto"), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctx := context.Background() server := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(test.respCode) w.Write([]byte(test.respBody)) })) defer server.Close() client, err := NewWriter(server.URL) if err != nil { t.Fatal(err) } _, gotErr := client.Add(ctx, test.entry) if test.expectErr == nil { assert.NoError(t, gotErr) } else { assert.ErrorContains(t, gotErr, test.expectErr.Error()) } }) } } func b64DecodeOrDie(t *testing.T, text string) []byte { decoded, err := base64.StdEncoding.DecodeString(text) if err != nil { t.Fatal(err) } return decoded } func marshalJSONOrDie(t *testing.T, obj any) []byte { marshaledResp, err := json.Marshal(obj) if err != nil { t.Fatal(err) } return marshaledResp } golang-github-sigstore-rekor-tiles-2.0.1/pkg/generated/000077500000000000000000000000001511162205500230515ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/generated/protobuf/000077500000000000000000000000001511162205500247115ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/generated/protobuf/dsse.pb.go000066400000000000000000000244551511162205500266100ustar00rootroot00000000000000// Copyright 2025 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.5 // protoc v6.30.2 // source: rekor/v2/dsse.proto package protobuf import ( v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" dsse "github.com/sigstore/protobuf-specs/gen/pb-go/dsse" _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // A request to add a DSSE v0.0.2 entry to the log type DSSERequestV002 struct { state protoimpl.MessageState `protogen:"open.v1"` // A DSSE envelope Envelope *dsse.Envelope `protobuf:"bytes,1,opt,name=envelope,proto3" json:"envelope,omitempty"` // All necessary verification material to verify all signatures embedded in the envelope Verifiers []*Verifier `protobuf:"bytes,2,rep,name=verifiers,proto3" json:"verifiers,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DSSERequestV002) Reset() { *x = DSSERequestV002{} mi := &file_rekor_v2_dsse_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DSSERequestV002) String() string { return protoimpl.X.MessageStringOf(x) } func (*DSSERequestV002) ProtoMessage() {} func (x *DSSERequestV002) ProtoReflect() protoreflect.Message { mi := &file_rekor_v2_dsse_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DSSERequestV002.ProtoReflect.Descriptor instead. func (*DSSERequestV002) Descriptor() ([]byte, []int) { return file_rekor_v2_dsse_proto_rawDescGZIP(), []int{0} } func (x *DSSERequestV002) GetEnvelope() *dsse.Envelope { if x != nil { return x.Envelope } return nil } func (x *DSSERequestV002) GetVerifiers() []*Verifier { if x != nil { return x.Verifiers } return nil } type DSSELogEntryV002 struct { state protoimpl.MessageState `protogen:"open.v1"` // The hash of the DSSE payload PayloadHash *v1.HashOutput `protobuf:"bytes,1,opt,name=payloadHash,proto3" json:"payloadHash,omitempty"` // Signatures and their associated verification material used to verify the payload Signatures []*Signature `protobuf:"bytes,2,rep,name=signatures,proto3" json:"signatures,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DSSELogEntryV002) Reset() { *x = DSSELogEntryV002{} mi := &file_rekor_v2_dsse_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DSSELogEntryV002) String() string { return protoimpl.X.MessageStringOf(x) } func (*DSSELogEntryV002) ProtoMessage() {} func (x *DSSELogEntryV002) ProtoReflect() protoreflect.Message { mi := &file_rekor_v2_dsse_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DSSELogEntryV002.ProtoReflect.Descriptor instead. func (*DSSELogEntryV002) Descriptor() ([]byte, []int) { return file_rekor_v2_dsse_proto_rawDescGZIP(), []int{1} } func (x *DSSELogEntryV002) GetPayloadHash() *v1.HashOutput { if x != nil { return x.PayloadHash } return nil } func (x *DSSELogEntryV002) GetSignatures() []*Signature { if x != nil { return x.Signatures } return nil } var File_rekor_v2_dsse_proto protoreflect.FileDescriptor var file_rekor_v2_dsse_proto_rawDesc = string([]byte{ 0x0a, 0x13, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x64, 0x73, 0x73, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0e, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8b, 0x01, 0x0a, 0x0f, 0x44, 0x53, 0x53, 0x45, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x30, 0x30, 0x32, 0x12, 0x34, 0x0a, 0x08, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x69, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x08, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x42, 0x0a, 0x09, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x09, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x22, 0xa4, 0x01, 0x0a, 0x10, 0x44, 0x53, 0x53, 0x45, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x56, 0x30, 0x30, 0x32, 0x12, 0x49, 0x0a, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x45, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x42, 0x7d, 0x0a, 0x1b, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x42, 0x0b, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x56, 0x32, 0x44, 0x73, 0x73, 0x65, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2d, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0xea, 0x02, 0x13, 0x53, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x3a, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( file_rekor_v2_dsse_proto_rawDescOnce sync.Once file_rekor_v2_dsse_proto_rawDescData []byte ) func file_rekor_v2_dsse_proto_rawDescGZIP() []byte { file_rekor_v2_dsse_proto_rawDescOnce.Do(func() { file_rekor_v2_dsse_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_rekor_v2_dsse_proto_rawDesc), len(file_rekor_v2_dsse_proto_rawDesc))) }) return file_rekor_v2_dsse_proto_rawDescData } var file_rekor_v2_dsse_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_rekor_v2_dsse_proto_goTypes = []any{ (*DSSERequestV002)(nil), // 0: dev.sigstore.rekor.v2.DSSERequestV002 (*DSSELogEntryV002)(nil), // 1: dev.sigstore.rekor.v2.DSSELogEntryV002 (*dsse.Envelope)(nil), // 2: io.intoto.Envelope (*Verifier)(nil), // 3: dev.sigstore.rekor.v2.Verifier (*v1.HashOutput)(nil), // 4: dev.sigstore.common.v1.HashOutput (*Signature)(nil), // 5: dev.sigstore.rekor.v2.Signature } var file_rekor_v2_dsse_proto_depIdxs = []int32{ 2, // 0: dev.sigstore.rekor.v2.DSSERequestV002.envelope:type_name -> io.intoto.Envelope 3, // 1: dev.sigstore.rekor.v2.DSSERequestV002.verifiers:type_name -> dev.sigstore.rekor.v2.Verifier 4, // 2: dev.sigstore.rekor.v2.DSSELogEntryV002.payloadHash:type_name -> dev.sigstore.common.v1.HashOutput 5, // 3: dev.sigstore.rekor.v2.DSSELogEntryV002.signatures:type_name -> dev.sigstore.rekor.v2.Signature 4, // [4:4] is the sub-list for method output_type 4, // [4:4] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_rekor_v2_dsse_proto_init() } func file_rekor_v2_dsse_proto_init() { if File_rekor_v2_dsse_proto != nil { return } file_rekor_v2_verifier_proto_init() type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_rekor_v2_dsse_proto_rawDesc), len(file_rekor_v2_dsse_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_rekor_v2_dsse_proto_goTypes, DependencyIndexes: file_rekor_v2_dsse_proto_depIdxs, MessageInfos: file_rekor_v2_dsse_proto_msgTypes, }.Build() File_rekor_v2_dsse_proto = out.File file_rekor_v2_dsse_proto_goTypes = nil file_rekor_v2_dsse_proto_depIdxs = nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/generated/protobuf/entry.pb.go000066400000000000000000000364451511162205500270150ustar00rootroot00000000000000// Copyright 2025 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.5 // protoc v6.30.2 // source: rekor/v2/entry.proto package protobuf import ( _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Entry is the message that is canonicalized and uploaded to the log. // This format is meant to be compliant with Rekor v1 entries in that // the `apiVersion` and `kind` can be parsed before parsing the spec. // Clients are expected to understand and handle the differences in the // contents of `spec` between Rekor v1 (a polymorphic OpenAPI defintion) // and Rekor v2 (a typed proto defintion). type Entry struct { state protoimpl.MessageState `protogen:"open.v1"` Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` ApiVersion string `protobuf:"bytes,2,opt,name=api_version,json=apiVersion,proto3" json:"api_version,omitempty"` Spec *Spec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Entry) Reset() { *x = Entry{} mi := &file_rekor_v2_entry_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Entry) String() string { return protoimpl.X.MessageStringOf(x) } func (*Entry) ProtoMessage() {} func (x *Entry) ProtoReflect() protoreflect.Message { mi := &file_rekor_v2_entry_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Entry.ProtoReflect.Descriptor instead. func (*Entry) Descriptor() ([]byte, []int) { return file_rekor_v2_entry_proto_rawDescGZIP(), []int{0} } func (x *Entry) GetKind() string { if x != nil { return x.Kind } return "" } func (x *Entry) GetApiVersion() string { if x != nil { return x.ApiVersion } return "" } func (x *Entry) GetSpec() *Spec { if x != nil { return x.Spec } return nil } // Spec contains one of the Rekor entry types. type Spec struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Spec: // // *Spec_HashedRekordV002 // *Spec_DsseV002 Spec isSpec_Spec `protobuf_oneof:"spec"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Spec) Reset() { *x = Spec{} mi := &file_rekor_v2_entry_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Spec) String() string { return protoimpl.X.MessageStringOf(x) } func (*Spec) ProtoMessage() {} func (x *Spec) ProtoReflect() protoreflect.Message { mi := &file_rekor_v2_entry_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Spec.ProtoReflect.Descriptor instead. func (*Spec) Descriptor() ([]byte, []int) { return file_rekor_v2_entry_proto_rawDescGZIP(), []int{1} } func (x *Spec) GetSpec() isSpec_Spec { if x != nil { return x.Spec } return nil } func (x *Spec) GetHashedRekordV002() *HashedRekordLogEntryV002 { if x != nil { if x, ok := x.Spec.(*Spec_HashedRekordV002); ok { return x.HashedRekordV002 } } return nil } func (x *Spec) GetDsseV002() *DSSELogEntryV002 { if x != nil { if x, ok := x.Spec.(*Spec_DsseV002); ok { return x.DsseV002 } } return nil } type isSpec_Spec interface { isSpec_Spec() } type Spec_HashedRekordV002 struct { HashedRekordV002 *HashedRekordLogEntryV002 `protobuf:"bytes,1,opt,name=hashed_rekord_v002,json=hashedRekordV002,proto3,oneof"` } type Spec_DsseV002 struct { DsseV002 *DSSELogEntryV002 `protobuf:"bytes,2,opt,name=dsse_v002,json=dsseV002,proto3,oneof"` } func (*Spec_HashedRekordV002) isSpec_Spec() {} func (*Spec_DsseV002) isSpec_Spec() {} // Create a new HashedRekord or DSSE type CreateEntryRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Spec: // // *CreateEntryRequest_HashedRekordRequestV002 // *CreateEntryRequest_DsseRequestV002 Spec isCreateEntryRequest_Spec `protobuf_oneof:"spec"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CreateEntryRequest) Reset() { *x = CreateEntryRequest{} mi := &file_rekor_v2_entry_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CreateEntryRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*CreateEntryRequest) ProtoMessage() {} func (x *CreateEntryRequest) ProtoReflect() protoreflect.Message { mi := &file_rekor_v2_entry_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CreateEntryRequest.ProtoReflect.Descriptor instead. func (*CreateEntryRequest) Descriptor() ([]byte, []int) { return file_rekor_v2_entry_proto_rawDescGZIP(), []int{2} } func (x *CreateEntryRequest) GetSpec() isCreateEntryRequest_Spec { if x != nil { return x.Spec } return nil } func (x *CreateEntryRequest) GetHashedRekordRequestV002() *HashedRekordRequestV002 { if x != nil { if x, ok := x.Spec.(*CreateEntryRequest_HashedRekordRequestV002); ok { return x.HashedRekordRequestV002 } } return nil } func (x *CreateEntryRequest) GetDsseRequestV002() *DSSERequestV002 { if x != nil { if x, ok := x.Spec.(*CreateEntryRequest_DsseRequestV002); ok { return x.DsseRequestV002 } } return nil } type isCreateEntryRequest_Spec interface { isCreateEntryRequest_Spec() } type CreateEntryRequest_HashedRekordRequestV002 struct { HashedRekordRequestV002 *HashedRekordRequestV002 `protobuf:"bytes,1,opt,name=hashed_rekord_request_v002,json=hashedRekordRequestV002,proto3,oneof"` } type CreateEntryRequest_DsseRequestV002 struct { DsseRequestV002 *DSSERequestV002 `protobuf:"bytes,2,opt,name=dsse_request_v002,json=dsseRequestV002,proto3,oneof"` } func (*CreateEntryRequest_HashedRekordRequestV002) isCreateEntryRequest_Spec() {} func (*CreateEntryRequest_DsseRequestV002) isCreateEntryRequest_Spec() {} var File_rekor_v2_entry_proto protoreflect.FileDescriptor var file_rekor_v2_entry_proto_rawDesc = string([]byte{ 0x0a, 0x14, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x13, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x64, 0x73, 0x73, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7c, 0x0a, 0x05, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x17, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x24, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x70, 0x65, 0x63, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0xc1, 0x01, 0x0a, 0x04, 0x53, 0x70, 0x65, 0x63, 0x12, 0x64, 0x0a, 0x12, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x5f, 0x76, 0x30, 0x30, 0x32, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x56, 0x30, 0x30, 0x32, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x48, 0x00, 0x52, 0x10, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x56, 0x30, 0x30, 0x32, 0x12, 0x4b, 0x0a, 0x09, 0x64, 0x73, 0x73, 0x65, 0x5f, 0x76, 0x30, 0x30, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x53, 0x53, 0x45, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x56, 0x30, 0x30, 0x32, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x48, 0x00, 0x52, 0x08, 0x64, 0x73, 0x73, 0x65, 0x56, 0x30, 0x30, 0x32, 0x42, 0x06, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0xeb, 0x01, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x72, 0x0a, 0x1a, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x76, 0x30, 0x30, 0x32, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x30, 0x30, 0x32, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x48, 0x00, 0x52, 0x17, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x30, 0x30, 0x32, 0x12, 0x59, 0x0a, 0x11, 0x64, 0x73, 0x73, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x76, 0x30, 0x30, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x53, 0x53, 0x45, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x30, 0x30, 0x32, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x48, 0x00, 0x52, 0x0f, 0x64, 0x73, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x30, 0x30, 0x32, 0x42, 0x06, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x42, 0x7e, 0x0a, 0x1b, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x42, 0x0c, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x56, 0x32, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2d, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0xea, 0x02, 0x13, 0x53, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x3a, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( file_rekor_v2_entry_proto_rawDescOnce sync.Once file_rekor_v2_entry_proto_rawDescData []byte ) func file_rekor_v2_entry_proto_rawDescGZIP() []byte { file_rekor_v2_entry_proto_rawDescOnce.Do(func() { file_rekor_v2_entry_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_rekor_v2_entry_proto_rawDesc), len(file_rekor_v2_entry_proto_rawDesc))) }) return file_rekor_v2_entry_proto_rawDescData } var file_rekor_v2_entry_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_rekor_v2_entry_proto_goTypes = []any{ (*Entry)(nil), // 0: dev.sigstore.rekor.v2.Entry (*Spec)(nil), // 1: dev.sigstore.rekor.v2.Spec (*CreateEntryRequest)(nil), // 2: dev.sigstore.rekor.v2.CreateEntryRequest (*HashedRekordLogEntryV002)(nil), // 3: dev.sigstore.rekor.v2.HashedRekordLogEntryV002 (*DSSELogEntryV002)(nil), // 4: dev.sigstore.rekor.v2.DSSELogEntryV002 (*HashedRekordRequestV002)(nil), // 5: dev.sigstore.rekor.v2.HashedRekordRequestV002 (*DSSERequestV002)(nil), // 6: dev.sigstore.rekor.v2.DSSERequestV002 } var file_rekor_v2_entry_proto_depIdxs = []int32{ 1, // 0: dev.sigstore.rekor.v2.Entry.spec:type_name -> dev.sigstore.rekor.v2.Spec 3, // 1: dev.sigstore.rekor.v2.Spec.hashed_rekord_v002:type_name -> dev.sigstore.rekor.v2.HashedRekordLogEntryV002 4, // 2: dev.sigstore.rekor.v2.Spec.dsse_v002:type_name -> dev.sigstore.rekor.v2.DSSELogEntryV002 5, // 3: dev.sigstore.rekor.v2.CreateEntryRequest.hashed_rekord_request_v002:type_name -> dev.sigstore.rekor.v2.HashedRekordRequestV002 6, // 4: dev.sigstore.rekor.v2.CreateEntryRequest.dsse_request_v002:type_name -> dev.sigstore.rekor.v2.DSSERequestV002 5, // [5:5] is the sub-list for method output_type 5, // [5:5] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name } func init() { file_rekor_v2_entry_proto_init() } func file_rekor_v2_entry_proto_init() { if File_rekor_v2_entry_proto != nil { return } file_rekor_v2_dsse_proto_init() file_rekor_v2_hashedrekord_proto_init() file_rekor_v2_entry_proto_msgTypes[1].OneofWrappers = []any{ (*Spec_HashedRekordV002)(nil), (*Spec_DsseV002)(nil), } file_rekor_v2_entry_proto_msgTypes[2].OneofWrappers = []any{ (*CreateEntryRequest_HashedRekordRequestV002)(nil), (*CreateEntryRequest_DsseRequestV002)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_rekor_v2_entry_proto_rawDesc), len(file_rekor_v2_entry_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_rekor_v2_entry_proto_goTypes, DependencyIndexes: file_rekor_v2_entry_proto_depIdxs, MessageInfos: file_rekor_v2_entry_proto_msgTypes, }.Build() File_rekor_v2_entry_proto = out.File file_rekor_v2_entry_proto_goTypes = nil file_rekor_v2_entry_proto_depIdxs = nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/generated/protobuf/hashedrekord.pb.go000066400000000000000000000244251511162205500303120ustar00rootroot00000000000000// Copyright 2025 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.5 // protoc v6.30.2 // source: rekor/v2/hashedrekord.proto package protobuf import ( v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // A request to add a hashedrekord v0.0.2 to the log type HashedRekordRequestV002 struct { state protoimpl.MessageState `protogen:"open.v1"` // The hashed data Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"` // A single signature over the hashed data with the verifier needed to validate it Signature *Signature `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HashedRekordRequestV002) Reset() { *x = HashedRekordRequestV002{} mi := &file_rekor_v2_hashedrekord_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HashedRekordRequestV002) String() string { return protoimpl.X.MessageStringOf(x) } func (*HashedRekordRequestV002) ProtoMessage() {} func (x *HashedRekordRequestV002) ProtoReflect() protoreflect.Message { mi := &file_rekor_v2_hashedrekord_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HashedRekordRequestV002.ProtoReflect.Descriptor instead. func (*HashedRekordRequestV002) Descriptor() ([]byte, []int) { return file_rekor_v2_hashedrekord_proto_rawDescGZIP(), []int{0} } func (x *HashedRekordRequestV002) GetDigest() []byte { if x != nil { return x.Digest } return nil } func (x *HashedRekordRequestV002) GetSignature() *Signature { if x != nil { return x.Signature } return nil } type HashedRekordLogEntryV002 struct { state protoimpl.MessageState `protogen:"open.v1"` // The hashed data Data *v1.HashOutput `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` // A single signature over the hashed data with the verifier needed to validate it Signature *Signature `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HashedRekordLogEntryV002) Reset() { *x = HashedRekordLogEntryV002{} mi := &file_rekor_v2_hashedrekord_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HashedRekordLogEntryV002) String() string { return protoimpl.X.MessageStringOf(x) } func (*HashedRekordLogEntryV002) ProtoMessage() {} func (x *HashedRekordLogEntryV002) ProtoReflect() protoreflect.Message { mi := &file_rekor_v2_hashedrekord_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HashedRekordLogEntryV002.ProtoReflect.Descriptor instead. func (*HashedRekordLogEntryV002) Descriptor() ([]byte, []int) { return file_rekor_v2_hashedrekord_proto_rawDescGZIP(), []int{1} } func (x *HashedRekordLogEntryV002) GetData() *v1.HashOutput { if x != nil { return x.Data } return nil } func (x *HashedRekordLogEntryV002) GetSignature() *Signature { if x != nil { return x.Signature } return nil } var File_rekor_v2_hashedrekord_proto protoreflect.FileDescriptor var file_rekor_v2_hashedrekord_proto_rawDesc = string([]byte{ 0x0a, 0x1b, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7b, 0x0a, 0x17, 0x48, 0x61, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x30, 0x30, 0x32, 0x12, 0x1b, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x9c, 0x01, 0x0a, 0x18, 0x48, 0x61, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x56, 0x30, 0x30, 0x32, 0x12, 0x3b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x43, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x85, 0x01, 0x0a, 0x1b, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x42, 0x13, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x56, 0x32, 0x48, 0x61, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2d, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0xea, 0x02, 0x13, 0x53, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x3a, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( file_rekor_v2_hashedrekord_proto_rawDescOnce sync.Once file_rekor_v2_hashedrekord_proto_rawDescData []byte ) func file_rekor_v2_hashedrekord_proto_rawDescGZIP() []byte { file_rekor_v2_hashedrekord_proto_rawDescOnce.Do(func() { file_rekor_v2_hashedrekord_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_rekor_v2_hashedrekord_proto_rawDesc), len(file_rekor_v2_hashedrekord_proto_rawDesc))) }) return file_rekor_v2_hashedrekord_proto_rawDescData } var file_rekor_v2_hashedrekord_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_rekor_v2_hashedrekord_proto_goTypes = []any{ (*HashedRekordRequestV002)(nil), // 0: dev.sigstore.rekor.v2.HashedRekordRequestV002 (*HashedRekordLogEntryV002)(nil), // 1: dev.sigstore.rekor.v2.HashedRekordLogEntryV002 (*Signature)(nil), // 2: dev.sigstore.rekor.v2.Signature (*v1.HashOutput)(nil), // 3: dev.sigstore.common.v1.HashOutput } var file_rekor_v2_hashedrekord_proto_depIdxs = []int32{ 2, // 0: dev.sigstore.rekor.v2.HashedRekordRequestV002.signature:type_name -> dev.sigstore.rekor.v2.Signature 3, // 1: dev.sigstore.rekor.v2.HashedRekordLogEntryV002.data:type_name -> dev.sigstore.common.v1.HashOutput 2, // 2: dev.sigstore.rekor.v2.HashedRekordLogEntryV002.signature:type_name -> dev.sigstore.rekor.v2.Signature 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_rekor_v2_hashedrekord_proto_init() } func file_rekor_v2_hashedrekord_proto_init() { if File_rekor_v2_hashedrekord_proto != nil { return } file_rekor_v2_verifier_proto_init() type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_rekor_v2_hashedrekord_proto_rawDesc), len(file_rekor_v2_hashedrekord_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_rekor_v2_hashedrekord_proto_goTypes, DependencyIndexes: file_rekor_v2_hashedrekord_proto_depIdxs, MessageInfos: file_rekor_v2_hashedrekord_proto_msgTypes, }.Build() File_rekor_v2_hashedrekord_proto = out.File file_rekor_v2_hashedrekord_proto_goTypes = nil file_rekor_v2_hashedrekord_proto_depIdxs = nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/generated/protobuf/rekor_service.pb.go000066400000000000000000000354451511162205500305150ustar00rootroot00000000000000// Copyright 2025 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.5 // protoc v6.30.2 // source: rekor/v2/rekor_service.proto package protobuf import ( _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" _ "google.golang.org/genproto/googleapis/api/annotations" httpbody "google.golang.org/genproto/googleapis/api/httpbody" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Request for a full or partial tile (see https://github.com/C2SP/C2SP/blob/main/tlog-tiles.md#merkle-tree) type TileRequest struct { state protoimpl.MessageState `protogen:"open.v1"` L uint32 `protobuf:"varint,1,opt,name=L,proto3" json:"L,omitempty"` // N must be either an index encoded as zero-padded 3-digit path elements, e.g. "x123/x456/789", // and may end with ".p/", where "" is a uint8 N string `protobuf:"bytes,2,opt,name=N,proto3" json:"N,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TileRequest) Reset() { *x = TileRequest{} mi := &file_rekor_v2_rekor_service_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TileRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*TileRequest) ProtoMessage() {} func (x *TileRequest) ProtoReflect() protoreflect.Message { mi := &file_rekor_v2_rekor_service_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TileRequest.ProtoReflect.Descriptor instead. func (*TileRequest) Descriptor() ([]byte, []int) { return file_rekor_v2_rekor_service_proto_rawDescGZIP(), []int{0} } func (x *TileRequest) GetL() uint32 { if x != nil { return x.L } return 0 } func (x *TileRequest) GetN() string { if x != nil { return x.N } return "" } // Request for a full or partial entry bundle (see https://github.com/C2SP/C2SP/blob/main/tlog-tiles.md#log-entries) type EntryBundleRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // N must be either an index encoded as zero-padded 3-digit path elements, e.g. "x123/x456/789", // and may end with ".p/", where "" is a uint8 N string `protobuf:"bytes,1,opt,name=N,proto3" json:"N,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EntryBundleRequest) Reset() { *x = EntryBundleRequest{} mi := &file_rekor_v2_rekor_service_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EntryBundleRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*EntryBundleRequest) ProtoMessage() {} func (x *EntryBundleRequest) ProtoReflect() protoreflect.Message { mi := &file_rekor_v2_rekor_service_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EntryBundleRequest.ProtoReflect.Descriptor instead. func (*EntryBundleRequest) Descriptor() ([]byte, []int) { return file_rekor_v2_rekor_service_proto_rawDescGZIP(), []int{1} } func (x *EntryBundleRequest) GetN() string { if x != nil { return x.N } return "" } var File_rekor_v2_rekor_service_proto protoreflect.FileDescriptor var file_rekor_v2_rekor_service_proto_rawDesc = string([]byte{ 0x0a, 0x1c, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x62, 0x6f, 0x64, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x29, 0x0a, 0x0b, 0x54, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0c, 0x0a, 0x01, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x01, 0x4c, 0x12, 0x0c, 0x0a, 0x01, 0x4e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x01, 0x4e, 0x22, 0x22, 0x0a, 0x12, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0c, 0x0a, 0x01, 0x4e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x01, 0x4e, 0x32, 0xc8, 0x03, 0x0a, 0x05, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x12, 0x85, 0x01, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x29, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x3a, 0x01, 0x2a, 0x22, 0x13, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x6c, 0x6f, 0x67, 0x2f, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x64, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x54, 0x69, 0x6c, 0x65, 0x12, 0x22, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x74, 0x69, 0x6c, 0x65, 0x2f, 0x7b, 0x4c, 0x7d, 0x2f, 0x7b, 0x4e, 0x3d, 0x2a, 0x2a, 0x7d, 0x12, 0x76, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x29, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x74, 0x69, 0x6c, 0x65, 0x2f, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x2f, 0x7b, 0x4e, 0x3d, 0x2a, 0x2a, 0x7d, 0x12, 0x59, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0xc0, 0x03, 0x92, 0x41, 0xbc, 0x02, 0x12, 0xbc, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x20, 0x76, 0x32, 0x22, 0x5a, 0x0a, 0x10, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x20, 0x76, 0x32, 0x20, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x27, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2d, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x1a, 0x1d, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2d, 0x64, 0x65, 0x76, 0x40, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2a, 0x4f, 0x0a, 0x12, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x32, 0x2e, 0x30, 0x12, 0x39, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2d, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x03, 0x32, 0x2e, 0x30, 0x1a, 0x14, 0x2a, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x64, 0x65, 0x76, 0x2a, 0x01, 0x01, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3e, 0x0a, 0x13, 0x4d, 0x6f, 0x72, 0x65, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x20, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x20, 0x76, 0x32, 0x12, 0x27, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2d, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x0a, 0x1b, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x42, 0x0e, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x56, 0x32, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2d, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0xea, 0x02, 0x13, 0x53, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x3a, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( file_rekor_v2_rekor_service_proto_rawDescOnce sync.Once file_rekor_v2_rekor_service_proto_rawDescData []byte ) func file_rekor_v2_rekor_service_proto_rawDescGZIP() []byte { file_rekor_v2_rekor_service_proto_rawDescOnce.Do(func() { file_rekor_v2_rekor_service_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_rekor_v2_rekor_service_proto_rawDesc), len(file_rekor_v2_rekor_service_proto_rawDesc))) }) return file_rekor_v2_rekor_service_proto_rawDescData } var file_rekor_v2_rekor_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_rekor_v2_rekor_service_proto_goTypes = []any{ (*TileRequest)(nil), // 0: dev.sigstore.rekor.v2.TileRequest (*EntryBundleRequest)(nil), // 1: dev.sigstore.rekor.v2.EntryBundleRequest (*CreateEntryRequest)(nil), // 2: dev.sigstore.rekor.v2.CreateEntryRequest (*emptypb.Empty)(nil), // 3: google.protobuf.Empty (*v1.TransparencyLogEntry)(nil), // 4: dev.sigstore.rekor.v1.TransparencyLogEntry (*httpbody.HttpBody)(nil), // 5: google.api.HttpBody } var file_rekor_v2_rekor_service_proto_depIdxs = []int32{ 2, // 0: dev.sigstore.rekor.v2.Rekor.CreateEntry:input_type -> dev.sigstore.rekor.v2.CreateEntryRequest 0, // 1: dev.sigstore.rekor.v2.Rekor.GetTile:input_type -> dev.sigstore.rekor.v2.TileRequest 1, // 2: dev.sigstore.rekor.v2.Rekor.GetEntryBundle:input_type -> dev.sigstore.rekor.v2.EntryBundleRequest 3, // 3: dev.sigstore.rekor.v2.Rekor.GetCheckpoint:input_type -> google.protobuf.Empty 4, // 4: dev.sigstore.rekor.v2.Rekor.CreateEntry:output_type -> dev.sigstore.rekor.v1.TransparencyLogEntry 5, // 5: dev.sigstore.rekor.v2.Rekor.GetTile:output_type -> google.api.HttpBody 5, // 6: dev.sigstore.rekor.v2.Rekor.GetEntryBundle:output_type -> google.api.HttpBody 5, // 7: dev.sigstore.rekor.v2.Rekor.GetCheckpoint:output_type -> google.api.HttpBody 4, // [4:8] is the sub-list for method output_type 0, // [0:4] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_rekor_v2_rekor_service_proto_init() } func file_rekor_v2_rekor_service_proto_init() { if File_rekor_v2_rekor_service_proto != nil { return } file_rekor_v2_entry_proto_init() type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_rekor_v2_rekor_service_proto_rawDesc), len(file_rekor_v2_rekor_service_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 1, }, GoTypes: file_rekor_v2_rekor_service_proto_goTypes, DependencyIndexes: file_rekor_v2_rekor_service_proto_depIdxs, MessageInfos: file_rekor_v2_rekor_service_proto_msgTypes, }.Build() File_rekor_v2_rekor_service_proto = out.File file_rekor_v2_rekor_service_proto_goTypes = nil file_rekor_v2_rekor_service_proto_depIdxs = nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/generated/protobuf/rekor_service.pb.gw.go000066400000000000000000000433661511162205500311320ustar00rootroot00000000000000// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. // source: rekor/v2/rekor_service.proto /* Package protobuf is a reverse proxy. It translates gRPC into RESTful JSON APIs. */ package protobuf import ( "context" "errors" "io" "net/http" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/grpclog" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/emptypb" ) // Suppress "imported and not used" errors var ( _ codes.Code _ io.Reader _ status.Status _ = errors.New _ = runtime.String _ = utilities.NewDoubleArray _ = metadata.Join ) func request_Rekor_CreateEntry_0(ctx context.Context, marshaler runtime.Marshaler, client RekorClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq CreateEntryRequest metadata runtime.ServerMetadata ) if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := client.CreateEntry(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func local_request_Rekor_CreateEntry_0(ctx context.Context, marshaler runtime.Marshaler, server RekorServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq CreateEntryRequest metadata runtime.ServerMetadata ) if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } msg, err := server.CreateEntry(ctx, &protoReq) return msg, metadata, err } func request_Rekor_GetTile_0(ctx context.Context, marshaler runtime.Marshaler, client RekorClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq TileRequest metadata runtime.ServerMetadata err error ) io.Copy(io.Discard, req.Body) val, ok := pathParams["L"] if !ok { return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "L") } protoReq.L, err = runtime.Uint32(val) if err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "L", err) } val, ok = pathParams["N"] if !ok { return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "N") } protoReq.N, err = runtime.String(val) if err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "N", err) } msg, err := client.GetTile(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func local_request_Rekor_GetTile_0(ctx context.Context, marshaler runtime.Marshaler, server RekorServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq TileRequest metadata runtime.ServerMetadata err error ) val, ok := pathParams["L"] if !ok { return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "L") } protoReq.L, err = runtime.Uint32(val) if err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "L", err) } val, ok = pathParams["N"] if !ok { return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "N") } protoReq.N, err = runtime.String(val) if err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "N", err) } msg, err := server.GetTile(ctx, &protoReq) return msg, metadata, err } func request_Rekor_GetEntryBundle_0(ctx context.Context, marshaler runtime.Marshaler, client RekorClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq EntryBundleRequest metadata runtime.ServerMetadata err error ) io.Copy(io.Discard, req.Body) val, ok := pathParams["N"] if !ok { return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "N") } protoReq.N, err = runtime.String(val) if err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "N", err) } msg, err := client.GetEntryBundle(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func local_request_Rekor_GetEntryBundle_0(ctx context.Context, marshaler runtime.Marshaler, server RekorServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq EntryBundleRequest metadata runtime.ServerMetadata err error ) val, ok := pathParams["N"] if !ok { return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "N") } protoReq.N, err = runtime.String(val) if err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "N", err) } msg, err := server.GetEntryBundle(ctx, &protoReq) return msg, metadata, err } func request_Rekor_GetCheckpoint_0(ctx context.Context, marshaler runtime.Marshaler, client RekorClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq emptypb.Empty metadata runtime.ServerMetadata ) io.Copy(io.Discard, req.Body) msg, err := client.GetCheckpoint(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } func local_request_Rekor_GetCheckpoint_0(ctx context.Context, marshaler runtime.Marshaler, server RekorServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var ( protoReq emptypb.Empty metadata runtime.ServerMetadata ) msg, err := server.GetCheckpoint(ctx, &protoReq) return msg, metadata, err } // RegisterRekorHandlerServer registers the http handlers for service Rekor to "mux". // UnaryRPC :call RekorServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. // Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterRekorHandlerFromEndpoint instead. // GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call. func RegisterRekorHandlerServer(ctx context.Context, mux *runtime.ServeMux, server RekorServer) error { mux.Handle(http.MethodPost, pattern_Rekor_CreateEntry_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/dev.sigstore.rekor.v2.Rekor/CreateEntry", runtime.WithHTTPPathPattern("/api/v2/log/entries")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_Rekor_CreateEntry_0(annotatedContext, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } forward_Rekor_CreateEntry_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle(http.MethodGet, pattern_Rekor_GetTile_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/dev.sigstore.rekor.v2.Rekor/GetTile", runtime.WithHTTPPathPattern("/api/v2/tile/{L}/{N=**}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_Rekor_GetTile_0(annotatedContext, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } forward_Rekor_GetTile_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle(http.MethodGet, pattern_Rekor_GetEntryBundle_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/dev.sigstore.rekor.v2.Rekor/GetEntryBundle", runtime.WithHTTPPathPattern("/api/v2/tile/entries/{N=**}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_Rekor_GetEntryBundle_0(annotatedContext, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } forward_Rekor_GetEntryBundle_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle(http.MethodGet, pattern_Rekor_GetCheckpoint_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() var stream runtime.ServerTransportStream ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/dev.sigstore.rekor.v2.Rekor/GetCheckpoint", runtime.WithHTTPPathPattern("/api/v2/checkpoint")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := local_request_Rekor_GetCheckpoint_0(annotatedContext, inboundMarshaler, server, req, pathParams) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } forward_Rekor_GetCheckpoint_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) return nil } // RegisterRekorHandlerFromEndpoint is same as RegisterRekorHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterRekorHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { conn, err := grpc.NewClient(endpoint, opts...) if err != nil { return err } defer func() { if err != nil { if cerr := conn.Close(); cerr != nil { grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) } return } go func() { <-ctx.Done() if cerr := conn.Close(); cerr != nil { grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr) } }() }() return RegisterRekorHandler(ctx, mux, conn) } // RegisterRekorHandler registers the http handlers for service Rekor to "mux". // The handlers forward requests to the grpc endpoint over "conn". func RegisterRekorHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { return RegisterRekorHandlerClient(ctx, mux, NewRekorClient(conn)) } // RegisterRekorHandlerClient registers the http handlers for service Rekor // to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "RekorClient". // Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "RekorClient" // doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in // "RekorClient" to call the correct interceptors. This client ignores the HTTP middlewares. func RegisterRekorHandlerClient(ctx context.Context, mux *runtime.ServeMux, client RekorClient) error { mux.Handle(http.MethodPost, pattern_Rekor_CreateEntry_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/dev.sigstore.rekor.v2.Rekor/CreateEntry", runtime.WithHTTPPathPattern("/api/v2/log/entries")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_Rekor_CreateEntry_0(annotatedContext, inboundMarshaler, client, req, pathParams) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } forward_Rekor_CreateEntry_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle(http.MethodGet, pattern_Rekor_GetTile_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/dev.sigstore.rekor.v2.Rekor/GetTile", runtime.WithHTTPPathPattern("/api/v2/tile/{L}/{N=**}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_Rekor_GetTile_0(annotatedContext, inboundMarshaler, client, req, pathParams) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } forward_Rekor_GetTile_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle(http.MethodGet, pattern_Rekor_GetEntryBundle_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/dev.sigstore.rekor.v2.Rekor/GetEntryBundle", runtime.WithHTTPPathPattern("/api/v2/tile/entries/{N=**}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_Rekor_GetEntryBundle_0(annotatedContext, inboundMarshaler, client, req, pathParams) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } forward_Rekor_GetEntryBundle_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) mux.Handle(http.MethodGet, pattern_Rekor_GetCheckpoint_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/dev.sigstore.rekor.v2.Rekor/GetCheckpoint", runtime.WithHTTPPathPattern("/api/v2/checkpoint")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } resp, md, err := request_Rekor_GetCheckpoint_0(annotatedContext, inboundMarshaler, client, req, pathParams) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) if err != nil { runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) return } forward_Rekor_GetCheckpoint_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) return nil } var ( pattern_Rekor_CreateEntry_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v2", "log", "entries"}, "")) pattern_Rekor_GetTile_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 3, 0, 4, 1, 5, 4}, []string{"api", "v2", "tile", "L", "N"}, "")) pattern_Rekor_GetEntryBundle_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 3, 0, 4, 1, 5, 4}, []string{"api", "v2", "tile", "entries", "N"}, "")) pattern_Rekor_GetCheckpoint_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v2", "checkpoint"}, "")) ) var ( forward_Rekor_CreateEntry_0 = runtime.ForwardResponseMessage forward_Rekor_GetTile_0 = runtime.ForwardResponseMessage forward_Rekor_GetEntryBundle_0 = runtime.ForwardResponseMessage forward_Rekor_GetCheckpoint_0 = runtime.ForwardResponseMessage ) golang-github-sigstore-rekor-tiles-2.0.1/pkg/generated/protobuf/rekor_service_grpc.pb.go000066400000000000000000000237541511162205500315300ustar00rootroot00000000000000// Copyright 2025 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. // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 // - protoc v6.30.2 // source: rekor/v2/rekor_service.proto package protobuf import ( context "context" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" httpbody "google.golang.org/genproto/googleapis/api/httpbody" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( Rekor_CreateEntry_FullMethodName = "/dev.sigstore.rekor.v2.Rekor/CreateEntry" Rekor_GetTile_FullMethodName = "/dev.sigstore.rekor.v2.Rekor/GetTile" Rekor_GetEntryBundle_FullMethodName = "/dev.sigstore.rekor.v2.Rekor/GetEntryBundle" Rekor_GetCheckpoint_FullMethodName = "/dev.sigstore.rekor.v2.Rekor/GetCheckpoint" ) // RekorClient is the client API for Rekor service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // A service for sigstore clients to connect to to create log entries // and for log monitors and witnesses to audit/inspect the log type RekorClient interface { // Create an entry in the log CreateEntry(ctx context.Context, in *CreateEntryRequest, opts ...grpc.CallOption) (*v1.TransparencyLogEntry, error) // Get a tile from the log GetTile(ctx context.Context, in *TileRequest, opts ...grpc.CallOption) (*httpbody.HttpBody, error) // Get an entry bundle from the log GetEntryBundle(ctx context.Context, in *EntryBundleRequest, opts ...grpc.CallOption) (*httpbody.HttpBody, error) // Get a checkpoint from the log GetCheckpoint(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*httpbody.HttpBody, error) } type rekorClient struct { cc grpc.ClientConnInterface } func NewRekorClient(cc grpc.ClientConnInterface) RekorClient { return &rekorClient{cc} } func (c *rekorClient) CreateEntry(ctx context.Context, in *CreateEntryRequest, opts ...grpc.CallOption) (*v1.TransparencyLogEntry, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(v1.TransparencyLogEntry) err := c.cc.Invoke(ctx, Rekor_CreateEntry_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *rekorClient) GetTile(ctx context.Context, in *TileRequest, opts ...grpc.CallOption) (*httpbody.HttpBody, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(httpbody.HttpBody) err := c.cc.Invoke(ctx, Rekor_GetTile_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *rekorClient) GetEntryBundle(ctx context.Context, in *EntryBundleRequest, opts ...grpc.CallOption) (*httpbody.HttpBody, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(httpbody.HttpBody) err := c.cc.Invoke(ctx, Rekor_GetEntryBundle_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *rekorClient) GetCheckpoint(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*httpbody.HttpBody, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(httpbody.HttpBody) err := c.cc.Invoke(ctx, Rekor_GetCheckpoint_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // RekorServer is the server API for Rekor service. // All implementations must embed UnimplementedRekorServer // for forward compatibility. // // A service for sigstore clients to connect to to create log entries // and for log monitors and witnesses to audit/inspect the log type RekorServer interface { // Create an entry in the log CreateEntry(context.Context, *CreateEntryRequest) (*v1.TransparencyLogEntry, error) // Get a tile from the log GetTile(context.Context, *TileRequest) (*httpbody.HttpBody, error) // Get an entry bundle from the log GetEntryBundle(context.Context, *EntryBundleRequest) (*httpbody.HttpBody, error) // Get a checkpoint from the log GetCheckpoint(context.Context, *emptypb.Empty) (*httpbody.HttpBody, error) mustEmbedUnimplementedRekorServer() } // UnimplementedRekorServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedRekorServer struct{} func (UnimplementedRekorServer) CreateEntry(context.Context, *CreateEntryRequest) (*v1.TransparencyLogEntry, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateEntry not implemented") } func (UnimplementedRekorServer) GetTile(context.Context, *TileRequest) (*httpbody.HttpBody, error) { return nil, status.Errorf(codes.Unimplemented, "method GetTile not implemented") } func (UnimplementedRekorServer) GetEntryBundle(context.Context, *EntryBundleRequest) (*httpbody.HttpBody, error) { return nil, status.Errorf(codes.Unimplemented, "method GetEntryBundle not implemented") } func (UnimplementedRekorServer) GetCheckpoint(context.Context, *emptypb.Empty) (*httpbody.HttpBody, error) { return nil, status.Errorf(codes.Unimplemented, "method GetCheckpoint not implemented") } func (UnimplementedRekorServer) mustEmbedUnimplementedRekorServer() {} func (UnimplementedRekorServer) testEmbeddedByValue() {} // UnsafeRekorServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to RekorServer will // result in compilation errors. type UnsafeRekorServer interface { mustEmbedUnimplementedRekorServer() } func RegisterRekorServer(s grpc.ServiceRegistrar, srv RekorServer) { // If the following call pancis, it indicates UnimplementedRekorServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&Rekor_ServiceDesc, srv) } func _Rekor_CreateEntry_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateEntryRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(RekorServer).CreateEntry(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: Rekor_CreateEntry_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(RekorServer).CreateEntry(ctx, req.(*CreateEntryRequest)) } return interceptor(ctx, in, info, handler) } func _Rekor_GetTile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(TileRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(RekorServer).GetTile(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: Rekor_GetTile_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(RekorServer).GetTile(ctx, req.(*TileRequest)) } return interceptor(ctx, in, info, handler) } func _Rekor_GetEntryBundle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(EntryBundleRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(RekorServer).GetEntryBundle(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: Rekor_GetEntryBundle_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(RekorServer).GetEntryBundle(ctx, req.(*EntryBundleRequest)) } return interceptor(ctx, in, info, handler) } func _Rekor_GetCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(RekorServer).GetCheckpoint(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: Rekor_GetCheckpoint_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(RekorServer).GetCheckpoint(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } // Rekor_ServiceDesc is the grpc.ServiceDesc for Rekor service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var Rekor_ServiceDesc = grpc.ServiceDesc{ ServiceName: "dev.sigstore.rekor.v2.Rekor", HandlerType: (*RekorServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "CreateEntry", Handler: _Rekor_CreateEntry_Handler, }, { MethodName: "GetTile", Handler: _Rekor_GetTile_Handler, }, { MethodName: "GetEntryBundle", Handler: _Rekor_GetEntryBundle_Handler, }, { MethodName: "GetCheckpoint", Handler: _Rekor_GetCheckpoint_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "rekor/v2/rekor_service.proto", } golang-github-sigstore-rekor-tiles-2.0.1/pkg/generated/protobuf/verifier.pb.go000066400000000000000000000316401511162205500274570ustar00rootroot00000000000000// Copyright 2025 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. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.5 // protoc v6.30.2 // source: rekor/v2/verifier.proto package protobuf import ( v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // PublicKey contains an encoded public key type PublicKey struct { state protoimpl.MessageState `protogen:"open.v1"` // DER-encoded public key RawBytes []byte `protobuf:"bytes,1,opt,name=raw_bytes,json=rawBytes,proto3" json:"raw_bytes,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PublicKey) Reset() { *x = PublicKey{} mi := &file_rekor_v2_verifier_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PublicKey) String() string { return protoimpl.X.MessageStringOf(x) } func (*PublicKey) ProtoMessage() {} func (x *PublicKey) ProtoReflect() protoreflect.Message { mi := &file_rekor_v2_verifier_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PublicKey.ProtoReflect.Descriptor instead. func (*PublicKey) Descriptor() ([]byte, []int) { return file_rekor_v2_verifier_proto_rawDescGZIP(), []int{0} } func (x *PublicKey) GetRawBytes() []byte { if x != nil { return x.RawBytes } return nil } // Either a public key or a X.509 cerificiate with an embedded public key type Verifier struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Verifier: // // *Verifier_PublicKey // *Verifier_X509Certificate Verifier isVerifier_Verifier `protobuf_oneof:"verifier"` // Key encoding and signature algorithm to use for this key KeyDetails v1.PublicKeyDetails `protobuf:"varint,3,opt,name=key_details,json=keyDetails,proto3,enum=dev.sigstore.common.v1.PublicKeyDetails" json:"key_details,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Verifier) Reset() { *x = Verifier{} mi := &file_rekor_v2_verifier_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Verifier) String() string { return protoimpl.X.MessageStringOf(x) } func (*Verifier) ProtoMessage() {} func (x *Verifier) ProtoReflect() protoreflect.Message { mi := &file_rekor_v2_verifier_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Verifier.ProtoReflect.Descriptor instead. func (*Verifier) Descriptor() ([]byte, []int) { return file_rekor_v2_verifier_proto_rawDescGZIP(), []int{1} } func (x *Verifier) GetVerifier() isVerifier_Verifier { if x != nil { return x.Verifier } return nil } func (x *Verifier) GetPublicKey() *PublicKey { if x != nil { if x, ok := x.Verifier.(*Verifier_PublicKey); ok { return x.PublicKey } } return nil } func (x *Verifier) GetX509Certificate() *v1.X509Certificate { if x != nil { if x, ok := x.Verifier.(*Verifier_X509Certificate); ok { return x.X509Certificate } } return nil } func (x *Verifier) GetKeyDetails() v1.PublicKeyDetails { if x != nil { return x.KeyDetails } return v1.PublicKeyDetails(0) } type isVerifier_Verifier interface { isVerifier_Verifier() } type Verifier_PublicKey struct { // DER-encoded public key. Encoding method is specified by the key_details attribute PublicKey *PublicKey `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3,oneof"` } type Verifier_X509Certificate struct { // DER-encoded certificate X509Certificate *v1.X509Certificate `protobuf:"bytes,2,opt,name=x509_certificate,json=x509Certificate,proto3,oneof"` } func (*Verifier_PublicKey) isVerifier_Verifier() {} func (*Verifier_X509Certificate) isVerifier_Verifier() {} // A signature and an associated verifier type Signature struct { state protoimpl.MessageState `protogen:"open.v1"` Content []byte `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` Verifier *Verifier `protobuf:"bytes,2,opt,name=verifier,proto3" json:"verifier,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Signature) Reset() { *x = Signature{} mi := &file_rekor_v2_verifier_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Signature) String() string { return protoimpl.X.MessageStringOf(x) } func (*Signature) ProtoMessage() {} func (x *Signature) ProtoReflect() protoreflect.Message { mi := &file_rekor_v2_verifier_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Signature.ProtoReflect.Descriptor instead. func (*Signature) Descriptor() ([]byte, []int) { return file_rekor_v2_verifier_proto_rawDescGZIP(), []int{2} } func (x *Signature) GetContent() []byte { if x != nil { return x.Content } return nil } func (x *Signature) GetVerifier() *Verifier { if x != nil { return x.Verifier } return nil } var File_rekor_v2_verifier_proto protoreflect.FileDescriptor var file_rekor_v2_verifier_proto_rawDesc = string([]byte{ 0x0a, 0x17, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x1a, 0x15, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2d, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x08, 0x72, 0x61, 0x77, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x89, 0x02, 0x0a, 0x08, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x48, 0x00, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x59, 0x0a, 0x10, 0x78, 0x35, 0x30, 0x39, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x58, 0x35, 0x30, 0x39, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x48, 0x00, 0x52, 0x0f, 0x78, 0x35, 0x30, 0x39, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x0b, 0x6b, 0x65, 0x79, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x0a, 0x6b, 0x65, 0x79, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x6c, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x08, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x42, 0x81, 0x01, 0x0a, 0x1b, 0x64, 0x65, 0x76, 0x2e, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2e, 0x76, 0x32, 0x42, 0x0f, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x56, 0x32, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x2d, 0x74, 0x69, 0x6c, 0x65, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0xea, 0x02, 0x13, 0x53, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x3a, 0x52, 0x65, 0x6b, 0x6f, 0x72, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( file_rekor_v2_verifier_proto_rawDescOnce sync.Once file_rekor_v2_verifier_proto_rawDescData []byte ) func file_rekor_v2_verifier_proto_rawDescGZIP() []byte { file_rekor_v2_verifier_proto_rawDescOnce.Do(func() { file_rekor_v2_verifier_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_rekor_v2_verifier_proto_rawDesc), len(file_rekor_v2_verifier_proto_rawDesc))) }) return file_rekor_v2_verifier_proto_rawDescData } var file_rekor_v2_verifier_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_rekor_v2_verifier_proto_goTypes = []any{ (*PublicKey)(nil), // 0: dev.sigstore.rekor.v2.PublicKey (*Verifier)(nil), // 1: dev.sigstore.rekor.v2.Verifier (*Signature)(nil), // 2: dev.sigstore.rekor.v2.Signature (*v1.X509Certificate)(nil), // 3: dev.sigstore.common.v1.X509Certificate (v1.PublicKeyDetails)(0), // 4: dev.sigstore.common.v1.PublicKeyDetails } var file_rekor_v2_verifier_proto_depIdxs = []int32{ 0, // 0: dev.sigstore.rekor.v2.Verifier.public_key:type_name -> dev.sigstore.rekor.v2.PublicKey 3, // 1: dev.sigstore.rekor.v2.Verifier.x509_certificate:type_name -> dev.sigstore.common.v1.X509Certificate 4, // 2: dev.sigstore.rekor.v2.Verifier.key_details:type_name -> dev.sigstore.common.v1.PublicKeyDetails 1, // 3: dev.sigstore.rekor.v2.Signature.verifier:type_name -> dev.sigstore.rekor.v2.Verifier 4, // [4:4] is the sub-list for method output_type 4, // [4:4] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_rekor_v2_verifier_proto_init() } func file_rekor_v2_verifier_proto_init() { if File_rekor_v2_verifier_proto != nil { return } file_rekor_v2_verifier_proto_msgTypes[1].OneofWrappers = []any{ (*Verifier_PublicKey)(nil), (*Verifier_X509Certificate)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_rekor_v2_verifier_proto_rawDesc), len(file_rekor_v2_verifier_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_rekor_v2_verifier_proto_goTypes, DependencyIndexes: file_rekor_v2_verifier_proto_depIdxs, MessageInfos: file_rekor_v2_verifier_proto_msgTypes, }.Build() File_rekor_v2_verifier_proto = out.File file_rekor_v2_verifier_proto_goTypes = nil file_rekor_v2_verifier_proto_depIdxs = nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/note/000077500000000000000000000000001511162205500220605ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/note/note.go000066400000000000000000000141661511162205500233640ustar00rootroot00000000000000/* Copyright 2025 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. */ // Heavily borrowed from https://gist.githubusercontent.com/AlCutter/c6c69076dc55652e2d278900ccc1a5e7/raw/aac2bafc17a8efa162bd99b4453070b724779307/ecdsa_note.go - thanks, Al package note import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/binary" "fmt" "strings" "unicode" "unicode/utf8" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" "golang.org/x/mod/sumdb/note" ) const ( algEd25519 = 1 algUndef = 255 rsaID = "PKIX-RSA-PKCS#1v1.5" ) // noteSigner uses an arbitrary sigstore signer to implement golang.org/x/mod/sumdb/note.Signer, // which is used in Tessera to sign checkpoints in the signed notes format // (https://github.com/C2SP/C2SP/blob/main/signed-note.md). type noteSigner struct { name string hash uint32 sign func(msg []byte) ([]byte, error) } // Name returns the server name associated with the key. func (n *noteSigner) Name() string { return n.name } // KeyHash returns the key hash. func (n *noteSigner) KeyHash() uint32 { return n.hash } // Sign returns a signature for the given message. func (n *noteSigner) Sign(msg []byte) ([]byte, error) { return n.sign(msg) } type noteVerifier struct { name string hash uint32 verify func(msg, sig []byte) bool } // Name implements note.Verifier. func (n *noteVerifier) Name() string { return n.name } // Keyhash implements note.Verifier. func (n *noteVerifier) KeyHash() uint32 { return n.hash } // Verify implements note.Verifier. func (n *noteVerifier) Verify(msg, sig []byte) bool { return n.verify(msg, sig) } // isValidName reports whether the name conforms to the spec for the origin string of the note text // as defined in https://github.com/C2SP/C2SP/blob/main/tlog-checkpoint.md#note-text. func isValidName(name string) bool { return name != "" && utf8.ValidString(name) && strings.IndexFunc(name, unicode.IsSpace) < 0 && !strings.Contains(name, "+") } // genConformantKeyHash generates a truncated (4-byte) and non-truncated // identifier for typical (non-ECDSA) keys. func genConformantKeyHash(name string, sigType, key []byte) (uint32, []byte) { hash := sha256.New() hash.Write([]byte(name)) hash.Write([]byte("\n")) hash.Write(sigType) hash.Write(key) sum := hash.Sum(nil) return binary.BigEndian.Uint32(sum), sum } // ed25519KeyHash generates the 4-byte key ID for an Ed25519 public key. // Ed25519 keys are the only key type compatible with witnessing. func ed25519KeyHash(name string, key []byte) (uint32, []byte) { return genConformantKeyHash(name, []byte{algEd25519}, key) } // ecdsaKeyHash generates the 4-byte key ID for an ECDSA public key. // ECDSA key IDs do not conform to the note standard for other key type // (see https://github.com/C2SP/C2SP/blob/8991f70ddf8a11de3a68d5a081e7be27e59d87c8/signed-note.md#signature-types). func ecdsaKeyHash(key *ecdsa.PublicKey) (uint32, []byte, error) { marshaled, err := x509.MarshalPKIXPublicKey(key) if err != nil { return 0, nil, fmt.Errorf("marshaling public key: %w", err) } hash := sha256.Sum256(marshaled) return binary.BigEndian.Uint32(hash[:]), hash[:], nil } // rsaKeyhash generates the 4-byte key ID for an RSA public key. func rsaKeyHash(name string, key *rsa.PublicKey) (uint32, []byte, error) { marshaled, err := x509.MarshalPKIXPublicKey(key) if err != nil { return 0, nil, fmt.Errorf("marshaling public key: %w", err) } rsaAlg := append([]byte{algUndef}, []byte(rsaID)...) id, hash := genConformantKeyHash(name, rsaAlg, marshaled) return id, hash, nil } // KeyHash generates a truncated (4-byte) and non-truncated identifier for a // public key/origin func KeyHash(origin string, key crypto.PublicKey) (uint32, []byte, error) { var keyID uint32 var logID []byte var err error switch pk := key.(type) { case *ecdsa.PublicKey: keyID, logID, err = ecdsaKeyHash(pk) if err != nil { return 0, nil, fmt.Errorf("getting ECDSA key hash: %w", err) } case ed25519.PublicKey: keyID, logID = ed25519KeyHash(origin, pk) case *rsa.PublicKey: keyID, logID, err = rsaKeyHash(origin, pk) if err != nil { return 0, nil, fmt.Errorf("getting RSA key hash: %w", err) } default: return 0, nil, fmt.Errorf("unsupported key type: %T", key) } return keyID, logID, nil } // NewNoteSigner converts a sigstore/sigstore/pkg/signature.Signer into a note.Signer. func NewNoteSigner(ctx context.Context, origin string, signer signature.Signer) (note.Signer, error) { if !isValidName(origin) { return nil, fmt.Errorf("invalid name %s", origin) } pubKey, err := signer.PublicKey() if err != nil { return nil, fmt.Errorf("getting public key: %w", err) } keyID, _, err := KeyHash(origin, pubKey) if err != nil { return nil, err } sign := func(msg []byte) ([]byte, error) { return signer.SignMessage(bytes.NewReader(msg), options.WithContext(ctx)) } return ¬eSigner{ name: origin, hash: keyID, sign: sign, }, nil } // NewNoteVerifier converts a sigstore/sigstore/pkg/signature.Verifier into a note.Verifier. func NewNoteVerifier(origin string, verifier signature.Verifier) (note.Verifier, error) { if !isValidName(origin) { return nil, fmt.Errorf("invalid name %s", origin) } pubKey, err := verifier.PublicKey() if err != nil { return nil, fmt.Errorf("getting public key: %w", err) } keyID, _, err := KeyHash(origin, pubKey) if err != nil { return nil, err } return ¬eVerifier{ name: origin, hash: keyID, verify: func(msg, sig []byte) bool { if err := verifier.VerifySignature(bytes.NewReader(sig), bytes.NewReader(msg)); err != nil { return false } return true }, }, nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/note/note_test.go000066400000000000000000000141431511162205500244160ustar00rootroot00000000000000/* Copyright 2025 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 note import ( "context" "encoding/hex" "fmt" "os" "path/filepath" "testing" "github.com/sigstore/rekor-tiles/v2/internal/signerverifier" "github.com/stretchr/testify/assert" ) var ( // ed25519-priv-key.pem ed25519PrivKey = []byte(` -----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIGuZ8UWTFmXi/26ZgF4VYL8HfLSuW12TN5XMFQRt1Loc -----END PRIVATE KEY----- `) // ec-secp256r1-priv-key.pem ecdsaPrivKey = []byte(` -----BEGIN EC PRIVATE KEY----- MHcCAQEEIOMZNOBbRU3CHBjZoc13R8HYNUoqsvce5UlOmRzlOZqUoAoGCCqGSM49 AwEHoUQDQgAEeLw7gX40qy1z7JUhGMAaaDITbV7p2D+C5G9xPEsy/PVAo9H0mgS4 NYzpGirkXxBht+IvvL19WR1X9ANXha5ldQ== -----END EC PRIVATE KEY----- `) // rsa-priv-key.pem // minimum supported RSA key size is 2048 rsaPrivKey = []byte(` -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzuJ5Nonw8CTMK Sz765sM1bg1ziGLv78lSZK+qrqkhYB+MatVcj9npE5Q6c2C+ORbqk4/8qQ7VhB+4 u0/zpfksPjdoix8f/dVDQvxfhbrjH8mCYD1NZBr7Dg55doV0bD6UScbLauUAveQh if8EBuagBmHJZdAWQ9iDq+XKk3QfqpAfyFy3RJefvLpxqwiTsYHtNC3/46qSRDnm iMz3VxdSrphiy8CMGNDAS8YZeou/A6G1sP54xzZ+nDsaUCCKJCmriSHNau25knQd 38EVk2kcf5QB11Dgm+/t1tcOKdq9XMRu8myxAA32cHf1Q8elRCf2aiMC6H+qD68M 3IQfeWIhAgMBAAECggEAALYAAFAefDFQH6ANE3qCtq0lBfo54/eXnQkTbdIrjpt9 /HT2u5tBEiw256VJsm7w4YQsa2Qy0BLp+jXZet4C9pMXboUwXhTHuXCkJW+WvevE BQ1C6NG2zpvvYDkhDYUZGUaSb/8QAVj+9EgCt34cfvEPhXeu40uo6VpuegbrzG1D Wr5Ad2JhQi8LZMCqlRBLvBSRPaMHv6KpkII6pPJyeVa99tVk2ivLBsoIPOLmi7i8 GDcedUXJY1r8AArH4ahxPM1QQF/CVxd4zxUUETb5LQ4XwZKxm6y7LMThv6scWFBD +ey/GUS+u3krpGaBW2AoxS8ubYr5mjvCr0Gg24oZ/QKBgQDX4XUaQE+7RwcUnEVx KPJj+gsrbsST82KUAR1EB/3XVK2HQweSYk14feQvo1VJL0uAc58BtZwi30IKHBNr EUvGCee/6CxsvsHYrn4ukvVo4sx1DdjH83ByaACMjLJKDySarBuNcH+L0VCbw699 7n6MaBk+u6QJOmOhJRmtn7HjRQKBgQDVHt5I/uJznMrNSR4p1AgkPmcmaF4m/z9C fLSz9mra/QzdNSMKTbF5wH7ENlFE6M7GtSVRi2G44eKaoCaQ/xFFhPGPoJrd8tqU eCH65FYGH0Q3uQryOJ9rQSb/p9697orS7Fb46UQGfxRj5mLoUcmpComPYccod7UF vfqEX7ojLQKBgQCcWCz96QuVXxhSpeZo0LXTEBicyOjXGZIQDEqMpZkCJNJTvoiy TD09ATeMBKdUjEsK6TGEBGnim3vxZGnvxaPx9eSACH579x7edWjvBAF1h6N5NqEE FlsekBGtuIu6tQLWfcSqh4nn0ZymYm8rLdk6iH2YAD8Ja98RqpPROXGUXQKBgHSy uGkkGH/SBUKvnPKuhd7CxNzFHXkhH1Sa0KASeKR9GAQwyoUj0eCnRULPs2SWTlOE cXDbb6E7H0goFbYj1SNKDrPk76hFCOdveLvXODuV91bD3niQBMtIG8C1/UPnUOD6 RQ17PaKJB3NEfjhUQKZtfLGkitdf5SHFy12cxWHpAoGAHV6Smruaku5GTLmftp7x D0+b3FrkQeBT3OFpUVtp0iSixhoBA2YHSofC/SAnqoY7Hw23VUtWHfhPdVowNa2T cvxSmCAcfkrJHKF1remfUOtN6Ncu9Dyr/rieoxFDqz31lezTAx3T6lLfgtspxplR fycQ0e/nw1JkypGoL1j4cwM= -----END PRIVATE KEY----- `) ) func TestKeyHash(t *testing.T) { origin := "testkey" tests := []struct { name string key []byte expectedKeyID uint32 expectedLogID []byte }{ { name: "ed25519", key: ed25519PrivKey, expectedKeyID: 3839787747, // echo $((0x$(printf "%s%b%b%s" "testkey" "\x0A" "\x01" "$(openssl pkey -in ed25519-priv-key.pem -pubout -out - | openssl pkey -pubin -in /dev/stdin -outform DER -out - | tail -c32)" | sha256sum | cut -d ' ' -f 1 | head -c 8))) expectedLogID: hexDecodeOrDie(t, "e4de82e34c8c270d87612a3d6a1e297e2cd0ed9cb89ea7aeec8ce4d0b54e6775"), // echo $(printf "%s%b%b%s" "testkey" "\x0A" "\x01" "$(openssl pkey -in ed25519-priv-key.pem -pubout -out - | openssl pkey -pubin -in /dev/stdin -outform DER -out - | tail -c32)" | sha256sum | cut -d ' ' -f 1) }, { name: "ecdsa", key: ecdsaPrivKey, expectedKeyID: 2408765216, // echo $((0x$(openssl ec -in ec-secp256r1-priv-key.pem -pubout - | openssl ec -pubin -in /dev/stdin -outform DER -out - | sha256sum | cut -d ' ' -f 1 | head -c 8))) expectedLogID: hexDecodeOrDie(t, "8f92d720f56c219e34895a76a636a3353ddbf7813f9fa317e19982eafa945328"), // echo $(openssl ec -in ec-secp256r1-priv-key.pem -pubout - | openssl ec -pubin -in /dev/stdin -outform DER -out - | sha256sum | cut -d ' ' -f 1) }, { name: "rsa", key: rsaPrivKey, expectedKeyID: 2918460683, // echo $((0x$({ printf "%s%b%b%s" "testkey" "\x0A" "\xFF" 'PKIX-RSA-PKCS#1v1.5' ; openssl rsa -in rsa-priv-key.pem -pubout -out - | openssl rsa -in /dev/stdin -pubin -outform DER -out - ; } | sha256sum | cut -d ' ' -f 1 | head -c8))) expectedLogID: hexDecodeOrDie(t, "adf42d0b4478f2d07e5c8c3e63640a7e41b440c19dc47010c3a4936af75d85b6"), // echo $({ printf "%s%b%b%s" "testkey" "\x0A" "\xFF" 'PKIX-RSA-PKCS#1v1.5' ; openssl rsa -in rsa-priv-key.pem -pubout -out - | openssl rsa -in /dev/stdin -pubin -outform DER -out - ; } | sha256sum | cut -d ' ' -f 1) }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { ctx := context.Background() td := t.TempDir() file := fmt.Sprintf("%s-priv.pem", test.name) keyFile := filepath.Join(td, file) if err := os.WriteFile(keyFile, []byte(test.key), 0644); err != nil { t.Fatal(err) } signer, err := signerverifier.New(ctx, signerverifier.WithFile(keyFile, "")) if err != nil { t.Fatal(err) } noteSigner, err := NewNoteSigner(ctx, origin, signer) if err != nil { t.Fatal(err) } pubKey, err := signer.PublicKey() if err != nil { t.Fatal(err) } keyID, logID, err := KeyHash(origin, pubKey) if err != nil { t.Fatal(err) } assert.Equal(t, test.expectedKeyID, noteSigner.KeyHash()) assert.Equal(t, test.expectedKeyID, keyID) assert.Equal(t, test.expectedLogID, logID) }) } } func hexDecodeOrDie(t *testing.T, text string) []byte { decoded, err := hex.DecodeString(text) if err != nil { t.Fatal(err) } return decoded } golang-github-sigstore-rekor-tiles-2.0.1/pkg/types/000077500000000000000000000000001511162205500222575ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/types/dsse/000077500000000000000000000000001511162205500232155ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/types/dsse/dsse.go000066400000000000000000000176541511162205500245170ustar00rootroot00000000000000// Copyright 2025 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 dsse import ( "bytes" "context" "crypto/sha256" "encoding/base64" "errors" "fmt" "slices" "github.com/secure-systems-lab/go-securesystemslib/dsse" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" pbdsse "github.com/sigstore/protobuf-specs/gen/pb-go/dsse" "github.com/sigstore/rekor-tiles/v2/internal/algorithmregistry" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" pbverifier "github.com/sigstore/rekor-tiles/v2/pkg/types/verifier" "github.com/sigstore/rekor-tiles/v2/pkg/verifier" "github.com/sigstore/rekor-tiles/v2/pkg/verifier/certificate" "github.com/sigstore/rekor-tiles/v2/pkg/verifier/publickey" "github.com/sigstore/sigstore/pkg/signature" sigdsse "github.com/sigstore/sigstore/pkg/signature/dsse" ) // ToLogEntry validates a request, verifies all envelope signatures, and converts it to a log entry type for inclusion in the log func ToLogEntry(ds *pb.DSSERequestV002, algorithmRegistry *signature.AlgorithmRegistryConfig) (*pb.Entry, error) { if err := validate(ds); err != nil { return nil, err } verifiers, err := extractVerifiers(ds) if err != nil { return nil, err } signerVerifiers, err := verifyEnvelopeAndSupportedAlgs(verifiers, ds.Envelope, algorithmRegistry) if err != nil { return nil, err } // Canonicalize the order of the signatures. Signatures are sorted in ascending order by the stringified // representation of the raw (e.g. not base64-encoded) signature. var sortedSigs []string for sig := range signerVerifiers { sortedSigs = append(sortedSigs, sig) } slices.Sort(sortedSigs) var canonicalizedSigs []*pb.Signature for _, s := range sortedSigs { canonicalizedSigs = append(canonicalizedSigs, &pb.Signature{Content: []byte(s), Verifier: signerVerifiers[s]}) } // Use a hardcoded SHA-256 hashing algorithm for the payload hash, // since each signature digest algorithm might be different. Clients // must not use the payload hash when verifying signatures. payloadHash := sha256.Sum256(ds.Envelope.Payload) return &pb.Entry{ Kind: "dsse", ApiVersion: "0.0.2", Spec: &pb.Spec{ Spec: &pb.Spec_DsseV002{ DsseV002: &pb.DSSELogEntryV002{ PayloadHash: &v1.HashOutput{ Algorithm: v1.HashAlgorithm_SHA2_256, Digest: payloadHash[:], }, Signatures: canonicalizedSigs, }, }, }, }, nil } // validate validates there are no missing fields in a DSSERequestV002 protobuf func validate(ds *pb.DSSERequestV002) error { if ds.Envelope == nil { return fmt.Errorf("missing envelope") } if len(ds.Verifiers) == 0 { return fmt.Errorf("missing verifiers") } for _, v := range ds.Verifiers { if err := pbverifier.Validate(v); err != nil { return fmt.Errorf("invalid verifier: %v", err) } } if len(ds.Envelope.Signatures) == 0 { return fmt.Errorf("envelope missing signatures") } for _, s := range ds.Envelope.Signatures { if s == nil || len(s.Sig) == 0 { return fmt.Errorf("envelope signature empty") } } return nil } // extractVerifiers returns a map of protobuf verifiers to verifier interface func extractVerifiers(ds *pb.DSSERequestV002) (map[*pb.Verifier]verifier.Verifier, error) { verifiers := make(map[*pb.Verifier]verifier.Verifier, 0) for _, v := range ds.Verifiers { pubKey := v.GetPublicKey() cert := v.GetX509Certificate() switch { case pubKey != nil: vf, err := publickey.NewVerifier(bytes.NewReader(pubKey.RawBytes)) if err != nil { return nil, fmt.Errorf("parsing public key: %v", err) } verifiers[v] = vf case cert != nil: vf, err := certificate.NewVerifier(bytes.NewReader(cert.RawBytes)) if err != nil { return nil, fmt.Errorf("parsing certificate: %v", err) } verifiers[v] = vf default: return nil, fmt.Errorf("must contain either a public key or X.509 certificate") } } return verifiers, nil } // verifyEnvelopeAndSupportedAlgs takes in verifiers, a map of key details to the signature verifier. Verifiers are used to // to verify the envelope's signatures. Returns a map of signatures to their verifiers. func verifyEnvelopeAndSupportedAlgs(verifiers map[*pb.Verifier]verifier.Verifier, pbenv *pbdsse.Envelope, algorithmRegistry *signature.AlgorithmRegistryConfig) (map[string]*pb.Verifier, error) { env := FromProto(pbenv) savs := make(map[string]*pb.Verifier, len(verifiers)) // generate a fake id for these keys so we can get back to the key bytes and match them to their corresponding signature allSigs := make(map[string]struct{}) for _, sig := range env.Signatures { allSigs[sig.Sig] = struct{}{} } for v, verifierKey := range verifiers { if len(allSigs) == 0 { break // if all signatures have been verified, do not attempt anymore } algDetails, err := signature.GetAlgorithmDetails(v.KeyDetails) if err != nil { return nil, fmt.Errorf("getting key algorithm details: %w", err) } alg := algDetails.GetHashType() // check if signing algorithm is supported by this Rekor instance valid, err := algorithmregistry.CheckEntryAlgorithms(verifierKey.PublicKey(), alg, algorithmRegistry) if err != nil { return nil, fmt.Errorf("checking entry algorithm: %w", err) } if !valid { return nil, &algorithmregistry.UnsupportedAlgorithm{Pub: verifierKey.PublicKey(), Alg: alg} } vfr, err := signature.LoadVerifier(verifierKey.PublicKey(), alg) if err != nil { return nil, fmt.Errorf("could not load verifier: %w", err) } dsseVfr, err := dsse.NewEnvelopeVerifier(&sigdsse.VerifierAdapter{SignatureVerifier: vfr}) if err != nil { return nil, fmt.Errorf("could not use public key as a dsse verifier: %w", err) } accepted, err := dsseVfr.Verify(context.Background(), env) if err != nil { return nil, fmt.Errorf("could not verify envelope: %w", err) } for _, accept := range accepted { delete(allSigs, accept.Sig.Sig) sigBytes, err := base64.StdEncoding.DecodeString(accept.Sig.Sig) if err != nil { // this should be unreachable return nil, fmt.Errorf("could not decode base64 signature: %w", err) } savs[string(sigBytes)] = v } } if len(allSigs) > 0 { return nil, errors.New("all signatures must have a key that verifies it") } return savs, nil } // FromProto converts a dsse proto message to a dsse struct func FromProto(env *pbdsse.Envelope) *dsse.Envelope { var newEnv dsse.Envelope newEnv.PayloadType = env.PayloadType newEnv.Payload = base64.StdEncoding.EncodeToString(env.Payload) newEnv.Signatures = make([]dsse.Signature, 0, len(env.Signatures)) for _, s := range env.Signatures { ns := dsse.Signature{ KeyID: s.Keyid, Sig: base64.StdEncoding.EncodeToString(s.Sig), } newEnv.Signatures = append(newEnv.Signatures, ns) } return &newEnv } // ToProto converts a dsse struct to a dsse proto message func ToProto(env *dsse.Envelope) (*pbdsse.Envelope, error) { var newEnv pbdsse.Envelope newEnv.PayloadType = env.PayloadType payloadBytes, err := base64.StdEncoding.DecodeString(env.Payload) if err != nil { return nil, fmt.Errorf("failed to decode dsse payload: %w", err) } newEnv.Payload = payloadBytes newEnv.Signatures = make([]*pbdsse.Signature, 0, len(env.Signatures)) for _, s := range env.Signatures { sigBytes, err := base64.StdEncoding.DecodeString(s.Sig) if err != nil { return nil, fmt.Errorf("failed to decode dsse signature: %w", err) } ns := &pbdsse.Signature{ Keyid: s.KeyID, Sig: sigBytes, } newEnv.Signatures = append(newEnv.Signatures, ns) } return &newEnv, nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/types/dsse/dsse_test.go000066400000000000000000000374441511162205500255550ustar00rootroot00000000000000// Copyright 2025 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 dsse import ( "crypto/sha256" "encoding/base64" "encoding/json" "encoding/pem" "fmt" "testing" "github.com/go-test/deep" dsset "github.com/secure-systems-lab/go-securesystemslib/dsse" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" "github.com/sigstore/protobuf-specs/gen/pb-go/dsse" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" "github.com/sigstore/sigstore/pkg/signature" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/encoding/protojson" ) var ( pemPublicKey = `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE850nB+WrwXzivt7yFbhFKw/8M2pa qSTHiQhkA4/0ZAsJtmzn/v4HdeZKTCQcsHq5IwM/LtbmEdv9ChO9M3cg9g== -----END PUBLIC KEY-----` pemx509Cert = `-----BEGIN CERTIFICATE----- MIICGTCCAb+gAwIBAgIUWi7MFKfQ+/QSDFb0RjUBmyvOCu0wCgYIKoZIzj0EAwIw YTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEaMBgGA1UEAwwRdGVzdC5zaWdzdG9yZS5k ZXYwIBcNMjUwMzI3MTgwNjAwWhgPMjEyNTAzMDMxODA2MDBaMGExCzAJBgNVBAYT AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn aXRzIFB0eSBMdGQxGjAYBgNVBAMMEXRlc3Quc2lnc3RvcmUuZGV2MFkwEwYHKoZI zj0CAQYIKoZIzj0DAQcDQgAEYJSwH/PInqkK+Um3iMPswCJg9SgypKpWY9onmsAJ Sj/nGF5ZiEOLfD7KJ747MtBrQ/lRJTXW5aEs9brVKOwrXqNTMFEwHQYDVR0OBBYE FAFlFaiDwXiV0qh7PILjNrp1zdYGMB8GA1UdIwQYMBaAFAFlFaiDwXiV0qh7PILj Nrp1zdYGMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhALrgqHZR 5glHunRCQ60XVtn7xEUvHIkyWdhQvocrEQ+KAiAlucBaXZ5NQ9viz1ATrdSyuj+a atI4zS+80vbts4NEFA== -----END CERTIFICATE-----` pemPublicKeyP384 = `-----BEGIN PUBLIC KEY----- MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEhM6AR/E5+rwuiWx5YE07ZpSNlG9NCFLb m+gjNn0q5uByc7GmCwH3fUF3SFyTDCm6+lm9DMiSQHpFqt1IP6HpnAzMwseTOsS7 cc1SxluRyLGYAJEFcNxc01Y/9cT79mf/ -----END PUBLIC KEY-----` ) func TestToLogEntry(t *testing.T) { block, rest := pem.Decode([]byte(pemPublicKey)) if len(rest) != 0 { t.Fatal("public key decoding had extra data") } publicKey := block.Bytes block, rest = pem.Decode([]byte(pemx509Cert)) if len(rest) != 0 { t.Fatal("certificate decoding had extra data") } x509Cert := block.Bytes block, rest = pem.Decode([]byte(pemPublicKeyP384)) if len(rest) != 0 { t.Fatal("ECDSA-P384 public key decoding had extra data") } publicKeyP384 := block.Bytes var payload = []byte("payload") var payloadHash = sha256.Sum256(payload) var keySignature = b64DecodeOrDie(t, "MEUCIQCSWas1Y9bI7aDNrBdHlzrFH8ch7B7IM+pJK86mtjkbJAIgaeCltz6vs20DP2sJ7IBihvcrdqGn3ivuV/KNPlMOetk=") var certSignature = b64DecodeOrDie(t, "MEUCIQDoYuLoinEz/gM6B+hEn/0d47lmRDitQ3LfL9vH0sF/gQIgPqVgoBTRsMSPYMXYuJYYCIaTpnuppqQaTSTRn0ubwLI=") var keySignatureP384 = b64DecodeOrDie(t, "MGYCMQDdKEzOCt71AzF+KKxrDQgCcPtsnfPZORmPlFZutXFqM8y/fi77sEAOjYkVdc4xxJwCMQC/4JuQ/bDWQV4QzPRA/u03pG49iTUDskoCFIrmabe0XyC9JkY1yyeuNS2LixMCaCI=") tests := []struct { name string dsse *pb.DSSERequestV002 allowedAlgorithms []v1.PublicKeyDetails expectErr error expectedEntry *pb.Entry }{ { name: "valid dsse", dsse: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: payload, PayloadType: "application/vnd.in-toto+json", Signatures: []*dsse.Signature{ { Sig: keySignature, Keyid: "", }, }, }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, expectedEntry: &pb.Entry{ Kind: "dsse", ApiVersion: "0.0.2", Spec: &pb.Spec{ Spec: &pb.Spec_DsseV002{ DsseV002: &pb.DSSELogEntryV002{ PayloadHash: &v1.HashOutput{ Algorithm: v1.HashAlgorithm_SHA2_256, Digest: payloadHash[:], }, Signatures: []*pb.Signature{ { Content: keySignature, Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, }, }, }, }, }, { name: "missing envelope", dsse: &pb.DSSERequestV002{ Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, expectErr: fmt.Errorf("missing envelope"), }, { name: "missing verifiers", dsse: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: payload, PayloadType: "application/vnd.in-toto+json", Signatures: []*dsse.Signature{ { Sig: keySignature, Keyid: "", }, }, }, }, expectErr: fmt.Errorf("missing verifiers"), }, { name: "invalid verifier", dsse: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: []byte("payload"), PayloadType: "application/vnd.in-toto+json", Signatures: []*dsse.Signature{ { Sig: []byte("sig"), Keyid: "", }, }, }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{}, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, expectErr: fmt.Errorf("invalid verifier"), }, { name: "missing signatures", dsse: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: payload, PayloadType: "application/vnd.in-toto+json", }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, expectErr: fmt.Errorf("envelope missing signatures"), }, { name: "empty signatures", dsse: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: payload, PayloadType: "application/vnd.in-toto+json", Signatures: []*dsse.Signature{}, }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, expectErr: fmt.Errorf("envelope missing signatures"), }, { name: "invalid signature", dsse: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: payload, PayloadType: "application/vnd.in-toto+json", Signatures: []*dsse.Signature{ { Sig: b64DecodeOrDie(t, "Zm9vYmFyCg=="), Keyid: "", }, }, }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, expectErr: fmt.Errorf("could not verify envelope: accepted signatures do not match threshold, Found: 0, Expected 1"), }, { name: "valid dsse with X.509 cert", dsse: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: payload, PayloadType: "application/vnd.in-toto+json", Signatures: []*dsse.Signature{ { Sig: certSignature, Keyid: "", }, }, }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_X509Certificate{ X509Certificate: &v1.X509Certificate{ RawBytes: []byte(x509Cert), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, expectedEntry: &pb.Entry{ Kind: "dsse", ApiVersion: "0.0.2", Spec: &pb.Spec{ Spec: &pb.Spec_DsseV002{ DsseV002: &pb.DSSELogEntryV002{ PayloadHash: &v1.HashOutput{ Algorithm: v1.HashAlgorithm_SHA2_256, Digest: payloadHash[:], }, Signatures: []*pb.Signature{ { Content: certSignature, Verifier: &pb.Verifier{ Verifier: &pb.Verifier_X509Certificate{ X509Certificate: &v1.X509Certificate{ RawBytes: []byte(x509Cert), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, }, }, }, }, }, { name: "mismatched key algorithm", dsse: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: payload, PayloadType: "application/vnd.in-toto+json", Signatures: []*dsse.Signature{ { Sig: keySignature, Keyid: "", }, }, }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, allowedAlgorithms: []v1.PublicKeyDetails{v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_4096_SHA256, v1.PublicKeyDetails_PKIX_ED25519_PH}, expectErr: fmt.Errorf("unsupported entry algorithm for ECDSA key, curve P-256, digest SHA-256"), }, { name: "valid DSSE with multiple signatures, different algorithm", dsse: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: payload, PayloadType: "application/vnd.in-toto+json", Signatures: []*dsse.Signature{ { Sig: keySignature, Keyid: "", }, { Sig: keySignatureP384, Keyid: "", }, }, }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKeyP384), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384, }, }, }, expectedEntry: &pb.Entry{ Kind: "dsse", ApiVersion: "0.0.2", Spec: &pb.Spec{ Spec: &pb.Spec_DsseV002{ DsseV002: &pb.DSSELogEntryV002{ PayloadHash: &v1.HashOutput{ Algorithm: v1.HashAlgorithm_SHA2_256, Digest: payloadHash[:], }, Signatures: []*pb.Signature{ { Content: keySignature, Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, { Content: keySignatureP384, Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKeyP384), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384, }, }, }, }, }, }, }, }, { // test input is same as "valid DSSE with multiple signatures, different algorithm", // but with signature input swapped, to show that response signature order is canonicalized name: "valid DSSE with multiple signatures in consistent order", dsse: &pb.DSSERequestV002{ Envelope: &dsse.Envelope{ Payload: payload, PayloadType: "application/vnd.in-toto+json", Signatures: []*dsse.Signature{ { Sig: keySignatureP384, Keyid: "", }, { Sig: keySignature, Keyid: "", }, }, }, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKeyP384), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384, }, }, }, expectedEntry: &pb.Entry{ Kind: "dsse", ApiVersion: "0.0.2", Spec: &pb.Spec{ Spec: &pb.Spec_DsseV002{ DsseV002: &pb.DSSELogEntryV002{ PayloadHash: &v1.HashOutput{ Algorithm: v1.HashAlgorithm_SHA2_256, Digest: payloadHash[:], }, Signatures: []*pb.Signature{ { Content: keySignature, Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, { Content: keySignatureP384, Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKeyP384), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384, }, }, }, }, }, }, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { allowedAlgs := test.allowedAlgorithms if allowedAlgs == nil { allowedAlgs = []v1.PublicKeyDetails{v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384} } algReg, err := signature.NewAlgorithmRegistryConfig(allowedAlgs) if err != nil { t.Fatal(err) } entry, gotErr := ToLogEntry(test.dsse, algReg) if test.expectErr == nil { assert.NoError(t, gotErr) if diff := deep.Equal(test.expectedEntry, entry); diff != nil { t.Errorf("ToLogEntry() mismatch (-want +got):\n%s", diff) } } else { assert.ErrorContains(t, gotErr, test.expectErr.Error()) } }) } } func TestConverters(t *testing.T) { tests := []struct { name string dsseB []byte }{ { name: "single signature with key ids", dsseB: []byte("{\"payload\":\"cGF5bG9hZAo=\",\"payloadType\":\"application/vnd.in-toto+json\",\"signatures\":[{\"sig\":\"c2lnbmF0dXJlCg==\", \"keyid\": \"id1\"}]}"), }, { name: "single signature with no key ids", dsseB: []byte("{\"payload\":\"cGF5bG9hZAo=\",\"payloadType\":\"application/vnd.in-toto+json\",\"signatures\":[{\"sig\":\"c2lnbmF0dXJlCg==\"}]}"), }, { name: "multi signature with key ids", dsseB: []byte("{\"payload\":\"cGF5bG9hZAo=\",\"payloadType\":\"application/vnd.in-toto+json\",\"signatures\":[{\"sig\":\"c2lnbmF0dXJlCg==\", \"keyid\": \"id1\"}, {\"sig\":\"c2lnbmF0dXJlMgo=\", \"keyid\": \"id2\"}]}"), }, } for _, test := range tests { env := &dsset.Envelope{} if err := json.Unmarshal(test.dsseB, env); err != nil { t.Fatal(err) } protoEnv := &dsse.Envelope{} if err := protojson.Unmarshal(test.dsseB, protoEnv); err != nil { t.Fatal(err) } t.Run("testToProto "+test.name, func(t *testing.T) { convertedEnv, err := ToProto(env) if err != nil { t.Fatal(err) } if diff := deep.Equal(protoEnv, convertedEnv); diff != nil { t.Errorf("ToProto() mismatch (-want +got):\n%s", diff) } }) t.Run("testFromProto "+test.name, func(t *testing.T) { convertedEnv := FromProto(protoEnv) if diff := deep.Equal(env, convertedEnv); diff != nil { t.Errorf("FromProto() mismatch (-want +got):\n%s", diff) } }) } } func b64DecodeOrDie(t *testing.T, msg string) []byte { decoded, err := base64.StdEncoding.DecodeString(msg) if err != nil { t.Fatal(err) } return decoded } golang-github-sigstore-rekor-tiles-2.0.1/pkg/types/hashedrekord/000077500000000000000000000000001511162205500247225ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/types/hashedrekord/hashedrekord.go000066400000000000000000000111361511162205500277160ustar00rootroot00000000000000// Copyright 2025 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 hashedrekord import ( "bytes" "crypto" "fmt" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" "github.com/sigstore/rekor-tiles/v2/internal/algorithmregistry" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" pbverifier "github.com/sigstore/rekor-tiles/v2/pkg/types/verifier" "github.com/sigstore/rekor-tiles/v2/pkg/verifier" "github.com/sigstore/rekor-tiles/v2/pkg/verifier/certificate" "github.com/sigstore/rekor-tiles/v2/pkg/verifier/publickey" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" ) // ToLogEntry validates a request, verifies its signature, and converts it to a log entry type for inclusion in the log func ToLogEntry(hr *pb.HashedRekordRequestV002, algorithmRegistry *signature.AlgorithmRegistryConfig) (*pb.Entry, error) { if err := validate(hr); err != nil { return nil, err } v, err := extractVerifier(hr) if err != nil { return nil, err } algDetails, err := verifySupportedAlgorithm(hr.Signature.Verifier.KeyDetails, v, algorithmRegistry) if err != nil { return nil, err } if err := verifySignature(hr, v, algDetails.GetHashType()); err != nil { return nil, err } return &pb.Entry{ Kind: "hashedrekord", ApiVersion: "0.0.2", Spec: &pb.Spec{ Spec: &pb.Spec_HashedRekordV002{ HashedRekordV002: &pb.HashedRekordLogEntryV002{ Signature: hr.Signature, Data: &v1.HashOutput{Digest: hr.Digest, Algorithm: algDetails.GetProtoHashType()}, }, }, }, }, nil } // validate validates there are no missing fields in a HashedRekordRequestV002 protobuf func validate(hr *pb.HashedRekordRequestV002) error { if hr.Signature == nil || len(hr.Signature.Content) == 0 { return fmt.Errorf("missing signature") } if hr.Signature.Verifier == nil { return fmt.Errorf("missing verifier") } if len(hr.Digest) == 0 { return fmt.Errorf("missing digest") } if err := pbverifier.Validate(hr.Signature.Verifier); err != nil { return fmt.Errorf("invalid verifier: %v", err) } return nil } func extractVerifier(hr *pb.HashedRekordRequestV002) (verifier.Verifier, error) { var v verifier.Verifier var err error if pubKey := hr.Signature.Verifier.GetPublicKey(); pubKey != nil { v, err = publickey.NewVerifier(bytes.NewReader(pubKey.RawBytes)) } else if cert := hr.Signature.Verifier.GetX509Certificate(); cert != nil { v, err = certificate.NewVerifier(bytes.NewReader(cert.RawBytes)) } else { return nil, fmt.Errorf("must contain either a public key or X.509 certificate") } if err != nil { return nil, fmt.Errorf("parsing verifier: %w", err) } return v, nil } // verifySupportedAlgorithm confirms that the signature and digest algorithm pair is supported by this server // instance, and returns details about the signing algorithm to be used while verifying the entry signature. func verifySupportedAlgorithm(keyDetails v1.PublicKeyDetails, v verifier.Verifier, algorithmRegistry *signature.AlgorithmRegistryConfig) (signature.AlgorithmDetails, error) { algDetails, err := signature.GetAlgorithmDetails(keyDetails) if err != nil { return signature.AlgorithmDetails{}, fmt.Errorf("getting key algorithm details: %w", err) } alg := algDetails.GetHashType() valid, err := algorithmregistry.CheckEntryAlgorithms(v.PublicKey(), alg, algorithmRegistry) if err != nil { return signature.AlgorithmDetails{}, fmt.Errorf("checking entry algorithm: %w", err) } if !valid { return signature.AlgorithmDetails{}, &algorithmregistry.UnsupportedAlgorithm{Pub: v.PublicKey(), Alg: alg} } return algDetails, nil } func verifySignature(hr *pb.HashedRekordRequestV002, v verifier.Verifier, hashAlg crypto.Hash) error { sigVerifier, err := signature.LoadVerifierWithOpts(v.PublicKey(), options.WithED25519ph()) if err != nil { return fmt.Errorf("loading verifier: %v", err) } if err := sigVerifier.VerifySignature( bytes.NewReader(hr.Signature.Content), nil, options.WithDigest(hr.Digest), options.WithCryptoSignerOpts(hashAlg)); err != nil { return fmt.Errorf("verifying signature: %w", err) } return nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/types/hashedrekord/hashedrekord_test.go000066400000000000000000000251741511162205500307640ustar00rootroot00000000000000// Copyright 2025 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 hashedrekord import ( "encoding/base64" "encoding/hex" "encoding/pem" "fmt" "testing" "github.com/go-test/deep" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" "github.com/sigstore/sigstore/pkg/signature" "github.com/stretchr/testify/assert" ) var ( b64EncodedSignature = "MEYCIQC59oLS3MsCqm0xCxPOy+8FdQK4RYCZE036s3q1ECfcagIhAJ4ATXlCSdFrklKAS8No0PsAE9uLi37TCbIfRXASJTTb" hexEncodedDigest = "5b3513f580c8397212ff2c8f459c199efc0c90e4354a5f3533adf0a3fff3a530" pemPublicKey = `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeLw7gX40qy1z7JUhGMAaaDITbV7p 2D+C5G9xPEsy/PVAo9H0mgS4NYzpGirkXxBht+IvvL19WR1X9ANXha5ldQ== -----END PUBLIC KEY-----` pemx509Cert = `-----BEGIN CERTIFICATE----- MIICGTCCAb+gAwIBAgIUbzTFcv75teYBpaXsDOoYUv5GgWgwCgYIKoZIzj0EAwIw YTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEaMBgGA1UEAwwRdGVzdC5zaWdzdG9yZS5k ZXYwIBcNMjUwMzI3MTc1MDQ2WhgPMjEyNTAzMDMxNzUwNDZaMGExCzAJBgNVBAYT AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn aXRzIFB0eSBMdGQxGjAYBgNVBAMMEXRlc3Quc2lnc3RvcmUuZGV2MFkwEwYHKoZI zj0CAQYIKoZIzj0DAQcDQgAEeLw7gX40qy1z7JUhGMAaaDITbV7p2D+C5G9xPEsy /PVAo9H0mgS4NYzpGirkXxBht+IvvL19WR1X9ANXha5ldaNTMFEwHQYDVR0OBBYE FJvvzmcqq8f+AiXCgAlE4IgdwTq+MB8GA1UdIwQYMBaAFJvvzmcqq8f+AiXCgAlE 4IgdwTq+MA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIgAqzN4Noq eH2jEARoIeXY0SRKnaNhVvullmremGvvd6QCIQDrL1WI3a7m8rlHN/7vvCCGtep1 fRnK+CuN46tvzGu+9A== -----END CERTIFICATE-----` pemPublicKeyP384 = `-----BEGIN PUBLIC KEY----- MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEhM6AR/E5+rwuiWx5YE07ZpSNlG9NCFLb m+gjNn0q5uByc7GmCwH3fUF3SFyTDCm6+lm9DMiSQHpFqt1IP6HpnAzMwseTOsS7 cc1SxluRyLGYAJEFcNxc01Y/9cT79mf/ -----END PUBLIC KEY-----` b64EncodedSignatureP384 = "MGUCMAq2KPdc07xIaNXOX8xNZGn11HFb5OIL049K1I5loaIiogGUunGwFWh/Ae00YBybNwIxALXYkptfZCa+fUfwIW3rbXWAs7vo+DfMyGPcddXfpej1m4i3z+4vL8OJtnrV6kc7lg==" hexEncodedDigest384 = "fe23e15e1b7ee8f48a7f878fedbee8f72a57fdf7c6141ddb0b00d23056c9da30e6c0c51588f0f888c830cfce8f29604c" ) func TestToLogEntry(t *testing.T) { block, rest := pem.Decode([]byte(pemPublicKey)) if len(rest) != 0 { t.Fatal("public key decoding had extra data") } publicKey := block.Bytes block, rest = pem.Decode([]byte(pemx509Cert)) if len(rest) != 0 { t.Fatal("certificate decoding had extra data") } x509Cert := block.Bytes block, rest = pem.Decode([]byte(pemPublicKeyP384)) if len(rest) != 0 { t.Fatal("ECDSA-P384 public key decoding had extra data") } publicKeyP384 := block.Bytes tests := []struct { name string hashedrekord *pb.HashedRekordRequestV002 allowedAlgorithms []v1.PublicKeyDetails expectErr error expectedEntry *pb.Entry }{ { name: "valid hashedrekord", hashedrekord: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: b64DecodeOrDie(t, b64EncodedSignature), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: hexDecodeOrDie(t, hexEncodedDigest), }, expectedEntry: &pb.Entry{ Kind: "hashedrekord", ApiVersion: "0.0.2", Spec: &pb.Spec{ Spec: &pb.Spec_HashedRekordV002{ HashedRekordV002: &pb.HashedRekordLogEntryV002{ Data: &v1.HashOutput{ Digest: hexDecodeOrDie(t, hexEncodedDigest), Algorithm: v1.HashAlgorithm_SHA2_256, }, Signature: &pb.Signature{ Content: b64DecodeOrDie(t, b64EncodedSignature), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, }, }, }, }, { name: "missing signature", hashedrekord: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: hexDecodeOrDie(t, hexEncodedDigest), }, expectErr: fmt.Errorf("missing signature"), }, { name: "missing verifier", hashedrekord: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: b64DecodeOrDie(t, b64EncodedSignature), }, Digest: hexDecodeOrDie(t, hexEncodedDigest), }, expectErr: fmt.Errorf("missing verifier"), }, { name: "missing digest", hashedrekord: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: b64DecodeOrDie(t, b64EncodedSignature), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, expectErr: fmt.Errorf("missing digest"), }, { name: "invalid verifier", hashedrekord: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: []byte("sig"), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{}, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: []byte("digest"), }, expectErr: fmt.Errorf("invalid verifier"), }, { name: "invalid signature", hashedrekord: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: []byte("foobar"), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: hexDecodeOrDie(t, hexEncodedDigest), }, expectErr: fmt.Errorf("verifying signature: "), }, { name: "valid hashedrekord with X.509 cert", hashedrekord: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: b64DecodeOrDie(t, b64EncodedSignature), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_X509Certificate{ X509Certificate: &v1.X509Certificate{ RawBytes: []byte(x509Cert), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: hexDecodeOrDie(t, hexEncodedDigest), }, expectedEntry: &pb.Entry{ Kind: "hashedrekord", ApiVersion: "0.0.2", Spec: &pb.Spec{ Spec: &pb.Spec_HashedRekordV002{ HashedRekordV002: &pb.HashedRekordLogEntryV002{ Data: &v1.HashOutput{ Digest: hexDecodeOrDie(t, hexEncodedDigest), Algorithm: v1.HashAlgorithm_SHA2_256, }, Signature: &pb.Signature{ Content: b64DecodeOrDie(t, b64EncodedSignature), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_X509Certificate{ X509Certificate: &v1.X509Certificate{ RawBytes: []byte(x509Cert), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, }, }, }, }, { name: "mismatched key algorithm", hashedrekord: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: b64DecodeOrDie(t, b64EncodedSignature), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKey), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: hexDecodeOrDie(t, hexEncodedDigest), }, allowedAlgorithms: []v1.PublicKeyDetails{v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_4096_SHA256, v1.PublicKeyDetails_PKIX_ED25519_PH}, expectErr: fmt.Errorf("unsupported entry algorithm for ECDSA key, curve P-256, digest SHA-256"), }, { name: "valid hashedrekord with different algorithm", hashedrekord: &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: b64DecodeOrDie(t, b64EncodedSignatureP384), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKeyP384), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384, }, }, Digest: hexDecodeOrDie(t, hexEncodedDigest384), }, expectedEntry: &pb.Entry{ Kind: "hashedrekord", ApiVersion: "0.0.2", Spec: &pb.Spec{ Spec: &pb.Spec_HashedRekordV002{ HashedRekordV002: &pb.HashedRekordLogEntryV002{ Data: &v1.HashOutput{ Digest: hexDecodeOrDie(t, hexEncodedDigest384), Algorithm: v1.HashAlgorithm_SHA2_384, }, Signature: &pb.Signature{ Content: b64DecodeOrDie(t, b64EncodedSignatureP384), Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte(publicKeyP384), }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384, }, }, }, }, }, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { allowedAlgs := test.allowedAlgorithms if allowedAlgs == nil { allowedAlgs = []v1.PublicKeyDetails{v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384} } algReg, err := signature.NewAlgorithmRegistryConfig(allowedAlgs) if err != nil { t.Fatal(err) } entry, gotErr := ToLogEntry(test.hashedrekord, algReg) if test.expectErr == nil { assert.NoError(t, gotErr) if diff := deep.Equal(test.expectedEntry, entry); diff != nil { t.Errorf("ToLogEntry() mismatch (-want +got):\n%s", diff) } } else { assert.ErrorContains(t, gotErr, test.expectErr.Error()) } }) } } func hexDecodeOrDie(t *testing.T, hash string) []byte { decoded, err := hex.DecodeString(hash) if err != nil { t.Fatal(err) } return decoded } func b64DecodeOrDie(t *testing.T, msg string) []byte { decoded, err := base64.StdEncoding.DecodeString(msg) if err != nil { t.Fatal(err) } return decoded } golang-github-sigstore-rekor-tiles-2.0.1/pkg/types/verifier/000077500000000000000000000000001511162205500240725ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/types/verifier/verifier.go000066400000000000000000000023561511162205500262420ustar00rootroot00000000000000// Copyright 2025 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 verifier import ( "fmt" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" ) // Validate validates there are no missing field in a Verifier protobuf func Validate(v *pb.Verifier) error { publicKey := v.GetPublicKey() x509Cert := v.GetX509Certificate() if publicKey == nil && x509Cert == nil { return fmt.Errorf("missing signature public key or X.509 certificate") } if publicKey != nil { if len(publicKey.GetRawBytes()) == 0 { return fmt.Errorf("missing public key raw bytes") } } if x509Cert != nil { if len(x509Cert.GetRawBytes()) == 0 { return fmt.Errorf("missing X.509 certificate raw bytes") } } return nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/types/verifier/verifier_test.go000066400000000000000000000042351511162205500272770ustar00rootroot00000000000000// Copyright 2025 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 verifier import ( "fmt" "testing" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" "github.com/stretchr/testify/assert" ) func TestValidate(t *testing.T) { tests := []struct { name string verifier *pb.Verifier expectErr error }{ { name: "valid public key verifier", verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: []byte("abcd"), }, }, }, }, { name: "valid x.509 verifier", verifier: &pb.Verifier{ Verifier: &pb.Verifier_X509Certificate{ X509Certificate: &v1.X509Certificate{ RawBytes: []byte("abcd"), }, }, }, }, { name: "no valid verifier", verifier: &pb.Verifier{}, expectErr: fmt.Errorf("missing signature public key or X.509 certificate"), }, { name: "public key missing content", verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{}, }, }, expectErr: fmt.Errorf("missing public key raw bytes"), }, { name: "x.509 cert missing content", verifier: &pb.Verifier{ Verifier: &pb.Verifier_X509Certificate{ X509Certificate: &v1.X509Certificate{}, }, }, expectErr: fmt.Errorf("missing X.509 certificate raw bytes"), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { gotErr := Validate(test.verifier) if test.expectErr == nil { assert.NoError(t, gotErr) } else { assert.ErrorContains(t, gotErr, test.expectErr.Error()) } }) } } golang-github-sigstore-rekor-tiles-2.0.1/pkg/verifier/000077500000000000000000000000001511162205500227265ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/verifier/certificate/000077500000000000000000000000001511162205500252105ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/verifier/certificate/certificate.go000066400000000000000000000033321511162205500300220ustar00rootroot00000000000000// Copyright 2025 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 certificate import ( "crypto" "crypto/sha256" "crypto/x509" "encoding/hex" "errors" "fmt" "io" "github.com/sigstore/rekor-tiles/v2/pkg/verifier/identity" "github.com/sigstore/sigstore/pkg/cryptoutils" ) // Certificate implements verifier.Verifier type Certificate struct { cert *x509.Certificate } func NewVerifier(r io.Reader) (*Certificate, error) { if r == nil { return nil, errors.New("certificate reader is nil") } derVerifier, err := io.ReadAll(r) if err != nil { return nil, err } cert, err := x509.ParseCertificate(derVerifier) if err != nil { return nil, fmt.Errorf("parsing certificate: %v", err) } return &Certificate{cert: cert}, nil } func (c Certificate) String() string { encoded, err := cryptoutils.MarshalCertificateToPEM(c.cert) if err != nil { return "" } return string(encoded) } func (c Certificate) PublicKey() crypto.PublicKey { return c.cert.PublicKey } func (c Certificate) Identity() (identity.Identity, error) { digest := sha256.Sum256(c.cert.Raw) return identity.Identity{ Crypto: c.cert, Raw: c.cert.Raw, Fingerprint: hex.EncodeToString(digest[:]), }, nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/verifier/certificate/certificate_test.go000066400000000000000000000140371511162205500310650ustar00rootroot00000000000000// Copyright 2025 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 certificate import ( "bytes" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/x509" "crypto/x509/pkix" "encoding/hex" "encoding/pem" "io" "math/big" "reflect" "strings" "testing" "time" ) func TestNewVerifier(t *testing.T) { validCert, validDER, err := generateTestCertificate() if err != nil { t.Fatalf("Failed to generate test certificate: %v", err) } tests := []struct { name string reader io.Reader wantErr bool wantErrMsg string wantCertRaw []byte }{ { name: "Success", reader: bytes.NewReader(validDER), wantErr: false, wantCertRaw: validDER, }, { name: "Invalid DER Data", reader: bytes.NewReader([]byte("this is not a certificate")), wantErr: true, wantErrMsg: "parsing certificate", }, { name: "Empty Reader", reader: bytes.NewReader([]byte{}), wantErr: true, wantErrMsg: "parsing certificate", }, { name: "Nil Reader", reader: nil, wantErr: true, wantErrMsg: "certificate reader is nil", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := NewVerifier(tt.reader) if (err != nil) != tt.wantErr { t.Fatalf("NewVerifier() error = %v, wantErr %v", err, tt.wantErr) } if tt.wantErr { if err != nil && tt.wantErrMsg != "" && !strings.Contains(err.Error(), tt.wantErrMsg) { t.Errorf("NewVerifier() error = %q, want error containing %q", err.Error(), tt.wantErrMsg) } if got != nil { t.Errorf("NewVerifier() got = %v, want nil on error", got) } } else { if got == nil { t.Fatalf("NewVerifier() got nil, want non-nil") } if got.cert == nil { t.Fatalf("NewVerifier() internal cert is nil") } if !bytes.Equal(got.cert.Raw, tt.wantCertRaw) { t.Errorf("NewVerifier() certificate raw bytes do not match input") } if !reflect.DeepEqual(got.cert, validCert) { t.Errorf("NewVerifier() internal cert mismatch. got = %+v, want = %+v", got.cert, validCert) } } }) } } func TestCertificate_String(t *testing.T) { cert, der, err := generateTestCertificate() if err != nil { t.Fatalf("failed to generate test certificate: %v", err) } verifier := &Certificate{cert: cert} pemStr := verifier.String() if pemStr == "" { t.Fatalf("String() returned empty string") } if !strings.HasPrefix(pemStr, "-----BEGIN CERTIFICATE-----") { t.Errorf("String() output does not start with PEM header") } if !strings.HasSuffix(strings.TrimSpace(pemStr), "-----END CERTIFICATE-----") { t.Errorf("String() output does not end with PEM footer") } block, rest := pem.Decode([]byte(pemStr)) if block == nil { t.Fatalf("failed to decode PEM output from String()") } if len(rest) > 0 { t.Errorf("String() output contained trailing data after PEM block: %q", rest) } if block.Type != "CERTIFICATE" { t.Errorf("String() output PEM block type mismatch: got %q, want %q", block.Type, "CERTIFICATE") } if !bytes.Equal(block.Bytes, der) { t.Errorf("String() PEM decoded bytes do not match original DER bytes") } } func TestCertificate_PublicKey(t *testing.T) { cert, _, err := generateTestCertificate() if err != nil { t.Fatalf("failed to generate test certificate: %v", err) } verifier := &Certificate{cert: cert} pubKey := verifier.PublicKey() if pubKey == nil { t.Fatalf("PublicKey() returned nil") } if !reflect.DeepEqual(pubKey, cert.PublicKey) { t.Errorf("PublicKey() returned key does not match certificate's public key. Got: %v, Want: %v", pubKey, cert.PublicKey) } } func TestCertificate_Identity(t *testing.T) { cert, der, err := generateTestCertificate() if err != nil { t.Fatalf("failed to generate test certificate: %v", err) } verifier := &Certificate{cert: cert} id, err := verifier.Identity() if err != nil { t.Fatalf("Identity() returned unexpected error: %v", err) } if !reflect.DeepEqual(id.Crypto, cert) { t.Errorf("Identity Crypto field mismatch. Got: %+v, Want: %+v", id.Crypto, cert) } if !bytes.Equal(id.Raw, der) { t.Errorf("Identity Raw field mismatch.") } expectedDigest := sha256.Sum256(der) expectedFingerprint := hex.EncodeToString(expectedDigest[:]) if id.Fingerprint != expectedFingerprint { t.Errorf("Identity Fingerprint mismatch. Got: %s, Want: %s", id.Fingerprint, expectedFingerprint) } } // generateTestCertificate creates a self-signed certificate for testing. // Returns the parsed certificate, its DER encoding, and the private key. func generateTestCertificate() (*x509.Certificate, []byte, error) { priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, err } template := x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ Organization: []string{"test org"}, CommonName: "Test Name", }, NotBefore: time.Now().Add(-1 * time.Hour), NotAfter: time.Now().Add(1 * time.Hour), KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, BasicConstraintsValid: true, IsCA: true, } // Self-sign, we don't validate that the certificate is a leaf derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) if err != nil { return nil, nil, err } cert, err := x509.ParseCertificate(derBytes) if err != nil { return nil, nil, err } return cert, derBytes, nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/verifier/identity/000077500000000000000000000000001511162205500245575ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/verifier/identity/identity.go000066400000000000000000000023221511162205500267360ustar00rootroot00000000000000// Copyright 2025 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. // Copied from https://github.com/sigstore/rekor/blob/73dba7c07d0747f00119417fc0ff994a393f97b2/pkg/pki/pki.go package identity type Identity struct { // Types include: // - *rsa.PublicKey // - *ecdsa.PublicKey // - ed25519.PublicKey // - *x509.Certificate Crypto any // Raw key or certificate extracted from Crypto. Values include: // - PKIX ASN.1 DER-encoded public key // - ASN.1 DER-encoded certificate Raw []byte // Contains hex-encoded SHA-256 digest of Raw. Values include: // - SHA-256 digest of the PKIX ASN.1 DER-encoded public key // - SHA-256 digest of the ASN.1 DER-encoded certificate Fingerprint string } golang-github-sigstore-rekor-tiles-2.0.1/pkg/verifier/publickey/000077500000000000000000000000001511162205500247155ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/verifier/publickey/publickey.go000066400000000000000000000034421511162205500272360ustar00rootroot00000000000000// Copyright 2025 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 publickey import ( "crypto" "crypto/sha256" "crypto/x509" "encoding/hex" "errors" "fmt" "io" "github.com/sigstore/rekor-tiles/v2/pkg/verifier/identity" "github.com/sigstore/sigstore/pkg/cryptoutils" ) // PublicKey implements verifier.Verifier type PublicKey struct { key crypto.PublicKey } func NewVerifier(r io.Reader) (*PublicKey, error) { if r == nil { return nil, errors.New("public key reader is nil") } derVerifier, err := io.ReadAll(r) if err != nil { return nil, err } key, err := x509.ParsePKIXPublicKey(derVerifier) if err != nil { return nil, fmt.Errorf("parsing public key: %v", err) } return &PublicKey{key: key}, nil } func (k PublicKey) String() string { encoded, err := cryptoutils.MarshalPublicKeyToPEM(k.key) if err != nil { return "" } return string(encoded) } func (k PublicKey) PublicKey() crypto.PublicKey { return k.key } func (k PublicKey) Identity() (identity.Identity, error) { pkixKey, err := cryptoutils.MarshalPublicKeyToDER(k.key) if err != nil { return identity.Identity{}, err } digest := sha256.Sum256(pkixKey) return identity.Identity{ Crypto: k.key, Raw: pkixKey, Fingerprint: hex.EncodeToString(digest[:]), }, nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/verifier/publickey/publickey_test.go000066400000000000000000000122601511162205500302730ustar00rootroot00000000000000// Copyright 2025 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 publickey import ( "bytes" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/x509" "encoding/hex" "encoding/pem" "fmt" "io" "reflect" "strings" "testing" ) func TestNewVerifier(t *testing.T) { goodPubKey, goodPKDER, err := generateTestPublicKey() if err != nil { t.Fatalf("failed to generate test public key: %v", err) } tests := []struct { name string reader io.Reader wantErr bool wantErrMsg string wantKey crypto.PublicKey }{ { name: "Success", reader: bytes.NewReader(goodPKDER), wantErr: false, wantKey: goodPubKey, }, { name: "Nil Reader", reader: nil, wantErr: true, wantErrMsg: "public key reader is nil", }, { name: "Invalid DER Data", reader: bytes.NewReader([]byte("this is not a public key")), wantErr: true, wantErrMsg: "parsing public key", }, { name: "Empty Reader", reader: bytes.NewReader([]byte{}), wantErr: true, wantErrMsg: "parsing public key", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := NewVerifier(tt.reader) if (err != nil) != tt.wantErr { t.Fatalf("NewVerifier() error = %v, wantErr %v", err, tt.wantErr) } if tt.wantErr { if err != nil && tt.wantErrMsg != "" && !strings.Contains(err.Error(), tt.wantErrMsg) { t.Errorf("NewVerifier() error = %q, want error containing %q", err.Error(), tt.wantErrMsg) } if got != nil { t.Errorf("NewVerifier() got = %v, want nil on error", got) } } else { if got == nil { t.Fatalf("NewVerifier() got nil, want non-nil") } if got.key == nil { t.Errorf("NewVerifier() internal key is nil") } if !reflect.DeepEqual(got.key, tt.wantKey) { t.Errorf("NewVerifier() key = %v, want %v", got.key, tt.wantKey) } } }) } } func TestPublicKey_String(t *testing.T) { pubKey, _, err := generateTestPublicKey() if err != nil { t.Fatalf("failed to generate test public key: %v", err) } verifier := &PublicKey{key: pubKey} pemStr := verifier.String() if pemStr == "" { t.Fatalf("String() returned empty string") } if !strings.HasPrefix(pemStr, "-----BEGIN PUBLIC KEY-----") { t.Errorf("String() output does not start with correct PEM header") } if !strings.HasSuffix(strings.TrimSpace(pemStr), "-----END PUBLIC KEY-----") { t.Errorf("String() output does not end with correct PEM footer") } block, rest := pem.Decode([]byte(pemStr)) if block == nil { t.Fatalf("Failed to decode PEM output from String()") } if len(rest) > 0 { t.Errorf("String() output contained trailing data after PEM block: %q", rest) } if block.Type != "PUBLIC KEY" { t.Errorf("String() output PEM block type mismatch: got %q, want %q", block.Type, "PUBLIC KEY") } } func TestPublicKey_PublicKey(t *testing.T) { pubKey, _, err := generateTestPublicKey() if err != nil { t.Fatalf("failed to generate test public key: %v", err) } verifier := &PublicKey{key: pubKey} retrievedKey := verifier.PublicKey() if retrievedKey == nil { t.Fatalf("PublicKey() returned nil") } if !reflect.DeepEqual(retrievedKey, pubKey) { t.Errorf("PublicKey() returned key does not match original key. Got: %v, Want: %v", retrievedKey, pubKey) } } func TestPublicKey_Identity(t *testing.T) { pubKey, pkixDER, err := generateTestPublicKey() if err != nil { t.Fatalf("failed to generate test public key: %v", err) } verifier := &PublicKey{key: pubKey} id, err := verifier.Identity() if err != nil { t.Fatalf("Identity() returned unexpected error: %v", err) } if !reflect.DeepEqual(id.Crypto, pubKey) { t.Errorf("Identity Crypto field mismatch. Got: %+v, Want: %+v", id.Crypto, pubKey) } if !bytes.Equal(id.Raw, pkixDER) { t.Errorf("Identity Raw field mismatch. Got %d bytes, want %d bytes.", len(id.Raw), len(pkixDER)) } expectedDigest := sha256.Sum256(pkixDER) expectedFingerprint := hex.EncodeToString(expectedDigest[:]) if id.Fingerprint != expectedFingerprint { t.Errorf("Identity Fingerprint mismatch. Got: %s, Want: %s", id.Fingerprint, expectedFingerprint) } } // generateTestPublicKey creates an ECDSA key pair and returns the public key // and its DER-encoded PKIX representation. func generateTestPublicKey() (crypto.PublicKey, []byte, error) { priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, fmt.Errorf("failed to generate key pair: %w", err) } derBytes, err := x509.MarshalPKIXPublicKey(priv.Public()) if err != nil { return nil, nil, fmt.Errorf("failed to marshal public key: %w", err) } return priv.Public(), derBytes, nil } golang-github-sigstore-rekor-tiles-2.0.1/pkg/verifier/verifier.go000066400000000000000000000021731511162205500250730ustar00rootroot00000000000000// Copyright 2025 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 verifier import ( "crypto" "github.com/sigstore/rekor-tiles/v2/pkg/verifier/identity" ) // Verifier represents a structure that can verify a signature, e.g. a public key or certificate type Verifier interface { // PublicKey returns the underlying public key for signature verification PublicKey() crypto.PublicKey // Identity returns the identity of the verifier from a key or certificate Identity() (identity.Identity, error) // String returns a human-readable representation of the verifier, e.g. PEM-encoded String() string } golang-github-sigstore-rekor-tiles-2.0.1/pkg/verify/000077500000000000000000000000001511162205500224175ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/pkg/verify/verify.go000066400000000000000000000100411511162205500242460ustar00rootroot00000000000000// // Copyright 2025 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 ( "fmt" pbs "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "github.com/sigstore/rekor-tiles/v2/internal/safeint" f_log "github.com/transparency-dev/formats/log" "github.com/transparency-dev/merkle/proof" "github.com/transparency-dev/merkle/rfc6962" sumdb_note "golang.org/x/mod/sumdb/note" ) // VerifyInclusionProof verifies an entry's inclusion proof func VerifyInclusionProof(entry *pbs.TransparencyLogEntry, cp *f_log.Checkpoint) error { //nolint: revive leafHash := rfc6962.DefaultHasher.HashLeaf(entry.CanonicalizedBody) index, err := safeint.NewSafeInt64(entry.LogIndex) if err != nil { return fmt.Errorf("invalid index: %w", err) } if err := proof.VerifyInclusion(rfc6962.DefaultHasher, index.U(), cp.Size, leafHash, entry.InclusionProof.Hashes, cp.Hash); err != nil { return fmt.Errorf("verifying inclusion: %w", err) } return nil } // VerifyCheckpoint verifies the signature on the entry's inclusion proof checkpoint func VerifyCheckpoint(unverifiedCp string, verifier sumdb_note.Verifier) (*f_log.Checkpoint, error) { //nolint: revive cp, _, _, err := f_log.ParseCheckpoint([]byte(unverifiedCp), verifier.Name(), verifier) if err != nil { return nil, fmt.Errorf("unverified checkpoint signature: %v", err) } return cp, nil } // VerifyWitnessedCheckpoint verifies the signature on the entry's inclusion proof checkpoint in addition to witness cosignatures. // This returns the underlying note which contains all verified signatures. func VerifyWitnessedCheckpoint(unverifiedCp string, verifier sumdb_note.Verifier, otherVerifiers ...sumdb_note.Verifier) (*f_log.Checkpoint, *sumdb_note.Note, error) { //nolint: revive cp, _, n, err := f_log.ParseCheckpoint([]byte(unverifiedCp), verifier.Name(), verifier, otherVerifiers...) if err != nil { return nil, nil, fmt.Errorf("unverified checkpoint signature: %v", err) } return cp, n, nil } // VerifyLogEntry verifies the log entry. This includes verifying the signature on the entry's // inclusion proof checkpoint and verifying the entry inclusion proof func VerifyLogEntry(entry *pbs.TransparencyLogEntry, verifier sumdb_note.Verifier) error { //nolint: revive cp, err := VerifyCheckpoint(entry.GetInclusionProof().GetCheckpoint().GetEnvelope(), verifier) if err != nil { return err } return VerifyInclusionProof(entry, cp) } // VerifyConsistencyProof verifies the latest checkpoint signature and the consistency proof between a previous log size // and root hash and the latest checkpoint's size and root hash. This may be used by a C2SP witness. func VerifyConsistencyProof(consistencyProof [][]byte, oldSize uint64, oldRootHash []byte, newUnverifiedCp string, verifier sumdb_note.Verifier) error { //nolint: revive newCp, err := VerifyCheckpoint(newUnverifiedCp, verifier) if err != nil { return err } return proof.VerifyConsistency(rfc6962.DefaultHasher, oldSize, newCp.Size, consistencyProof, oldRootHash, newCp.Hash) } // VerifyConsistencyProofWithCheckpoints verifies previous and latest checkpoint signatures and the consistency proof // between these checkpoints. This may be used by a monitor that persists checkpoints. func VerifyConsistencyProofWithCheckpoints(consistencyProof [][]byte, oldUnverifiedCp, newUnverifiedCp string, verifier sumdb_note.Verifier) error { //nolint: revive oldCp, err := VerifyCheckpoint(oldUnverifiedCp, verifier) if err != nil { return err } return VerifyConsistencyProof(consistencyProof, oldCp.Size, oldCp.Hash, newUnverifiedCp, verifier) } golang-github-sigstore-rekor-tiles-2.0.1/pkg/verify/verify_test.go000066400000000000000000000250371511162205500253200ustar00rootroot00000000000000// // Copyright 2025 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 ( "context" "crypto/rand" "crypto/sha256" "encoding/hex" "log" "testing" pbs "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" rekornote "github.com/sigstore/rekor-tiles/v2/pkg/note" "github.com/sigstore/sigstore/pkg/signature" "github.com/stretchr/testify/assert" f_log "github.com/transparency-dev/formats/log" f_note "github.com/transparency-dev/formats/note" note "golang.org/x/mod/sumdb/note" ) func TestVerifyInclusionProof(t *testing.T) { hash := []byte{89, 165, 117, 241, 87, 39, 71, 2, 195, 141, 227, 171, 30, 23, 132, 34, 111, 57, 31, 183, 149, 0, 235, 249, 240, 43, 68, 57, 251, 119, 87, 76} rootHash := []byte{91, 225, 117, 141, 210, 34, 138, 207, 175, 37, 70, 180, 182, 206, 138, 164, 12, 130, 163, 116, 143, 61, 203, 85, 14, 13, 103, 186, 52, 240, 42, 69} body := []byte("{\"apiVersion\":\"0.0.1\",\"kind\":\"rekord\",\"spec\":{\"data\":{\"hash\":{\"algorithm\":\"sha256\",\"value\":\"ecdc5536f73bdae8816f0ea40726ef5e9b810d914493075903bb90623d97b1d8\"}},\"signature\":{\"content\":\"MEYCIQD/PdPQmKWC1+0BNEd5gKvQGr1xxl3ieUffv3jk1zzJKwIhALBj3xfAyWxlz4jpoIEIV1UfK9vnkUUOSoeZxBZPHKPC\",\"format\":\"x509\",\"publicKey\":{\"content\":\"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFTU9jVGZSQlM5amlYTTgxRlo4Z20vMStvbWVNdwptbi8zNDcvNTU2Zy9scmlTNzJ1TWhZOUxjVCs1VUo2ZkdCZ2xyNVo4TDBKTlN1YXN5ZWQ5T3RhUnZ3PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==\"}}}}") for _, test := range []struct { name string proof *pbs.InclusionProof logSize uint64 wantErr bool }{ { name: "valid inclusionproof", proof: &pbs.InclusionProof{ LogIndex: 1, TreeSize: 2, Hashes: [][]byte{ []byte(hash), }, }, logSize: 2, wantErr: false, }, { name: "invalid hash", proof: &pbs.InclusionProof{ LogIndex: 1, TreeSize: 2, Hashes: [][]byte{ []byte([]byte{0, 165, 117, 241, 87, 39, 71, 2, 195, 141, 227, 171, 30, 23, 132, 34, 111, 57, 31, 183, 149, 0, 235, 249, 240, 43, 68, 57, 251, 119, 87, 76}), }, }, logSize: 2, wantErr: true, }, { name: "inclusion index beyond log size", proof: &pbs.InclusionProof{ LogIndex: 1, TreeSize: 2, Hashes: [][]byte{ []byte(hash), }, }, logSize: 1, wantErr: true, }, { name: "wrong proof size", proof: &pbs.InclusionProof{ LogIndex: 1, TreeSize: 2, Hashes: [][]byte{ []byte(hash), }, }, logSize: 3, wantErr: true, }, } { t.Run(string(test.name), func(t *testing.T) { checkpoint := &f_log.Checkpoint{ Size: test.logSize, Hash: rootHash, } entry := &pbs.TransparencyLogEntry{ LogIndex: 1, InclusionProof: test.proof, CanonicalizedBody: body, } gotErr := VerifyInclusionProof(entry, checkpoint) if (gotErr != nil) != test.wantErr { t.Fatalf("VerifyCheckpoint = %t, wantErr %t", gotErr, test.wantErr) } }) } } func getTestEntry(t *testing.T, signer signature.Signer, hostname string, otherSigners ...note.Signer) *pbs.TransparencyLogEntry { noteSigner, err := rekornote.NewNoteSigner(context.Background(), hostname, signer) if err != nil { t.Fatal(err) } rootHash := sha256.Sum256([]byte{1, 2, 3}) cpRaw := f_log.Checkpoint{ Origin: hostname, Size: uint64(2), Hash: rootHash[:], }.Marshal() n, err := note.Sign(¬e.Note{Text: string(cpRaw)}, append([]note.Signer{noteSigner}, otherSigners...)...) if err != nil { t.Fatal(err) } return &pbs.TransparencyLogEntry{ InclusionProof: &pbs.InclusionProof{ Checkpoint: &pbs.Checkpoint{ Envelope: string(n), }, }, } } func TestVerifyCheckpoint(t *testing.T) { hostname := "rekor.localhost" sv, _, err := signature.NewDefaultECDSASignerVerifier() if err != nil { t.Fatal(err) } otherSigner, _, err := signature.NewDefaultECDSASignerVerifier() if err != nil { t.Fatal(err) } noteVerifier, err := rekornote.NewNoteVerifier(hostname, sv) if err != nil { t.Fatal(err) } for _, test := range []struct { name string checkpoint string wantErr bool }{ { name: "valid checkpoint", checkpoint: getTestEntry(t, sv, hostname).GetInclusionProof().GetCheckpoint().GetEnvelope(), wantErr: false, }, { name: "hostname mismatch", checkpoint: getTestEntry(t, sv, "other.host").GetInclusionProof().GetCheckpoint().GetEnvelope(), wantErr: true, }, { name: "signature mismatch", checkpoint: getTestEntry(t, otherSigner, hostname).GetInclusionProof().GetCheckpoint().GetEnvelope(), wantErr: true, }, } { t.Run(string(test.name), func(t *testing.T) { _, gotErr := VerifyCheckpoint(test.checkpoint, noteVerifier) if (gotErr != nil) != test.wantErr { t.Fatalf("VerifyCheckpoint = %t, wantErr %t", gotErr, test.wantErr) } }) } } func TestVerifyWitnessedCheckpoint(t *testing.T) { hostname := "rekor.localhost" sv, _, err := signature.NewDefaultECDSASignerVerifier() if err != nil { t.Fatal(err) } witnessOrigin := "test-witness" privKey, pubKey, err := note.GenerateKey(rand.Reader, witnessOrigin) if err != nil { log.Fatalf("error generating key: %v", err) } witnessSigner, err := f_note.NewSignerForCosignatureV1(privKey) if err != nil { t.Fatal(err) } witnessVerifier, err := f_note.NewVerifierForCosignatureV1(pubKey) if err != nil { t.Fatal(err) } otherSigner, _, err := signature.NewDefaultECDSASignerVerifier() if err != nil { t.Fatal(err) } otherNoteSigner, err := rekornote.NewNoteSigner(context.Background(), hostname, otherSigner) if err != nil { t.Fatal(err) } noteVerifier, err := rekornote.NewNoteVerifier(hostname, sv) if err != nil { t.Fatal(err) } for _, test := range []struct { name string checkpoint string wantErr bool wantLength int }{ { name: "valid witnessed checkpoint", checkpoint: getTestEntry(t, sv, hostname, witnessSigner).GetInclusionProof().GetCheckpoint().GetEnvelope(), wantErr: false, wantLength: 2, }, { name: "mismatched log verifier with witness", checkpoint: getTestEntry(t, otherSigner, hostname, witnessSigner).GetInclusionProof().GetCheckpoint().GetEnvelope(), wantErr: true, }, { name: "only one valid signature, from log", checkpoint: getTestEntry(t, sv, hostname, otherNoteSigner).GetInclusionProof().GetCheckpoint().GetEnvelope(), wantErr: false, wantLength: 1, }, } { t.Run(string(test.name), func(t *testing.T) { _, note, gotErr := VerifyWitnessedCheckpoint(test.checkpoint, noteVerifier, witnessVerifier) if (gotErr != nil) != test.wantErr { t.Fatalf("VerifyCheckpoint = %t, wantErr %t", gotErr, test.wantErr) } if gotErr == nil { assert.Len(t, note.Sigs, test.wantLength) } }) } } func TestVerifyLogEntry(t *testing.T) { hostname := "rekor.localhost" hash := []byte{89, 165, 117, 241, 87, 39, 71, 2, 195, 141, 227, 171, 30, 23, 132, 34, 111, 57, 31, 183, 149, 0, 235, 249, 240, 43, 68, 57, 251, 119, 87, 76} rootHash := []byte{91, 225, 117, 141, 210, 34, 138, 207, 175, 37, 70, 180, 182, 206, 138, 164, 12, 130, 163, 116, 143, 61, 203, 85, 14, 13, 103, 186, 52, 240, 42, 69} body := []byte("{\"apiVersion\":\"0.0.1\",\"kind\":\"rekord\",\"spec\":{\"data\":{\"hash\":{\"algorithm\":\"sha256\",\"value\":\"ecdc5536f73bdae8816f0ea40726ef5e9b810d914493075903bb90623d97b1d8\"}},\"signature\":{\"content\":\"MEYCIQD/PdPQmKWC1+0BNEd5gKvQGr1xxl3ieUffv3jk1zzJKwIhALBj3xfAyWxlz4jpoIEIV1UfK9vnkUUOSoeZxBZPHKPC\",\"format\":\"x509\",\"publicKey\":{\"content\":\"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFTU9jVGZSQlM5amlYTTgxRlo4Z20vMStvbWVNdwptbi8zNDcvNTU2Zy9scmlTNzJ1TWhZOUxjVCs1VUo2ZkdCZ2xyNVo4TDBKTlN1YXN5ZWQ5T3RhUnZ3PT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==\"}}}}") sv, _, err := signature.NewDefaultECDSASignerVerifier() if err != nil { t.Fatal(err) } noteVerifier, err := rekornote.NewNoteVerifier(hostname, sv) if err != nil { t.Fatal(err) } noteSigner, err := rekornote.NewNoteSigner(context.Background(), hostname, sv) if err != nil { t.Fatal(err) } cpRaw := f_log.Checkpoint{ Origin: hostname, Size: uint64(2), Hash: rootHash, }.Marshal() n, err := note.Sign(¬e.Note{Text: string(cpRaw)}, noteSigner) if err != nil { t.Fatal(err) } proof := &pbs.InclusionProof{ LogIndex: 1, TreeSize: 2, Hashes: [][]byte{ []byte(hash), }, Checkpoint: &pbs.Checkpoint{ Envelope: string(n), }, } entry := &pbs.TransparencyLogEntry{ CanonicalizedBody: body, InclusionProof: proof, LogIndex: 1, } gotErr := VerifyLogEntry(entry, noteVerifier) assert.NoError(t, gotErr) } func TestVerifyConsistencyProof(t *testing.T) { hostname := "rekor.localhost" root1, err := hex.DecodeString("59a575f157274702c38de3ab1e1784226f391fb79500ebf9f02b4439fb77574c") if err != nil { t.Fatal(err) } root2, err := hex.DecodeString("5be1758dd2228acfaf2546b4b6ce8aa40c82a3748f3dcb550e0d67ba34f02a45") if err != nil { t.Fatal(err) } consistencyHashes, err := hex.DecodeString("d3be742c8d73e2dd3c5635843e987ad3dfb3837616f412a07bf730c3ad73f5cb") if err != nil { t.Fatal(err) } sv, _, err := signature.NewDefaultECDSASignerVerifier() if err != nil { t.Fatal(err) } noteVerifier, err := rekornote.NewNoteVerifier(hostname, sv) if err != nil { t.Fatal(err) } noteSigner, err := rekornote.NewNoteSigner(context.Background(), hostname, sv) if err != nil { t.Fatal(err) } oldCpRaw := f_log.Checkpoint{ Origin: hostname, Size: 1, Hash: root1, }.Marshal() oldCp, err := note.Sign(¬e.Note{Text: string(oldCpRaw)}, noteSigner) if err != nil { t.Fatal(err) } newCpRaw := f_log.Checkpoint{ Origin: hostname, Size: 2, Hash: root2, }.Marshal() newCp, err := note.Sign(¬e.Note{Text: string(newCpRaw)}, noteSigner) if err != nil { t.Fatal(err) } gotErr := VerifyConsistencyProofWithCheckpoints([][]byte{consistencyHashes}, string(oldCp), string(newCp), noteVerifier) assert.NoError(t, gotErr) gotErr = VerifyConsistencyProof([][]byte{consistencyHashes}, 1, root1, string(newCp), noteVerifier) assert.NoError(t, gotErr) } golang-github-sigstore-rekor-tiles-2.0.1/tests/000077500000000000000000000000001511162205500214745ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/tests/README.md000066400000000000000000000005321511162205500227530ustar00rootroot00000000000000# End to End tests ## Running the End to End tests Start the Docker containers from the top level directory: ```sh docker compose -f compose.yml up -d --build --wait --wait-timeout 60 ``` Run the tests: ```sh go test -v -tags=e2e ./tests/ ``` When finished, you can clean up the Docker containers if desired: ```sh docker compose down ``` golang-github-sigstore-rekor-tiles-2.0.1/tests/e2e_test.go000066400000000000000000000401511511162205500235360ustar00rootroot00000000000000// Copyright 2025 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 main import ( "context" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/x509" "encoding/base64" "fmt" "os/exec" "strings" "testing" "time" "github.com/secure-systems-lab/go-securesystemslib/dsse" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" pbdsse "github.com/sigstore/protobuf-specs/gen/pb-go/dsse" pbs "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "github.com/sigstore/rekor-tiles/v2/internal/safeint" "github.com/sigstore/rekor-tiles/v2/pkg/client/read" "github.com/sigstore/rekor-tiles/v2/pkg/client/write" pb "github.com/sigstore/rekor-tiles/v2/pkg/generated/protobuf" "github.com/sigstore/rekor-tiles/v2/pkg/note" dsset "github.com/sigstore/rekor-tiles/v2/pkg/types/dsse" "github.com/sigstore/rekor-tiles/v2/pkg/verify" "github.com/sigstore/sigstore/pkg/signature" sigdsse "github.com/sigstore/sigstore/pkg/signature/dsse" "github.com/stretchr/testify/assert" f_note "github.com/transparency-dev/formats/note" "github.com/transparency-dev/merkle/proof" "github.com/transparency-dev/merkle/rfc6962" "github.com/transparency-dev/tessera/api" "github.com/transparency-dev/tessera/api/layout" "go.step.sm/crypto/pemutil" signednote "golang.org/x/mod/sumdb/note" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/encoding/protojson" ) const ( defaultRekorURL = "http://localhost:3003" defaultRekorHostname = "rekor-local" defaultWitnessVKey = "rekor-witness-test+a478f5cd+AUtKAvrTeY7srtAMfP5JCUOkZoU+A7F5VA094y5LGr89" defaultGCSURL = "http://localhost:7080/tiles" defaultServerPublicKey = "./testdata/pki/ed25519-pub-key.pem" ) func TestReadWrite(t *testing.T) { ctx := context.Background() // get verifier needed for both read and write serverPubKey, err := pemutil.Read(defaultServerPublicKey) if err != nil { t.Fatal(err) } verifier, err := signature.LoadDefaultVerifier(serverPubKey) if err != nil { t.Fatal(err) } noteVerifier, err := note.NewNoteVerifier(defaultRekorHostname, verifier) if err != nil { t.Fatal(err) } // load witness verifier witnessNoteVerifier, err := f_note.NewVerifierForCosignatureV1(defaultWitnessVKey) if err != nil { t.Fatal(err) } noteVerifiers := []signednote.Verifier{noteVerifier, witnessNoteVerifier} // reader client reader, err := read.NewReader(defaultGCSURL, defaultRekorHostname, verifier) if err != nil { t.Fatal(err) } // writer client writer, err := write.NewWriter(defaultRekorURL) if err != nil { t.Fatal(err) } // log ID _, logID, err := note.KeyHash(defaultRekorHostname, serverPubKey) if err != nil { t.Fatal(err) } // Get the current checkpoint checkpoint, note, err := reader.ReadCheckpoint(ctx) assert.NoError(t, err) assert.NotNil(t, checkpoint) assert.NotNil(t, note) initialTreeSize := checkpoint.Size clientPrivKey, clientPubKey, err := genKeys() if err != nil { t.Fatal(err) } // Add new entries - more than one tile's worth numNewEntries := uint64(260) group := new(errgroup.Group) // Limit number of concurrent requests, as e2e tests fail on macOS // without a reasonable limit set group.SetLimit(50) for i := uint64(1); i <= numNewEntries; i++ { i := i group.Go(func() error { hr, err := newHashedRekordRequest(clientPrivKey, clientPubKey, i) if err != nil { return err } tle, err := writer.Add(ctx, hr) assert.NoError(t, err) assertHashedRekordTLE(t, tle, initialTreeSize, numNewEntries, logID, noteVerifiers, hr) return nil }) } if err := group.Wait(); err != nil { t.Fatal(err) } // Add one more entry outside of the errgroup so we know it's the last one. numNewEntries++ hr, err := newHashedRekordRequest(clientPrivKey, clientPubKey, numNewEntries) if err != nil { t.Fatal(err) } tle, err := writer.Add(ctx, hr) assert.NoError(t, err) assertHashedRekordTLE(t, tle, initialTreeSize, numNewEntries, logID, noteVerifiers, hr) // Check the checkpoint again checkpoint, note, err = reader.ReadCheckpoint(ctx) assert.NoError(t, err) assert.NotNil(t, checkpoint) assert.NotNil(t, note) latestTreeSize := checkpoint.Size assert.GreaterOrEqual(t, latestTreeSize, initialTreeSize+numNewEntries) // Get the first tile, this should be full as long as more than 256 entries have been added. tileLevel := uint64(0) tileIndex := uint64(0) tilePart := uint8(0) firstTileBytes, err := reader.ReadTile(ctx, tileLevel, tileIndex, tilePart) assert.NoError(t, err) assert.NotEmpty(t, firstTileBytes) entryBundle, err := reader.ReadEntryBundle(ctx, tileIndex, tilePart) assert.NoError(t, err) assert.NotEmpty(t, entryBundle) // Get the latest tile on the lowest level. Since we added >256 entries, this index should be at least 1. tileIndex = latestTreeSize / layout.TileWidth assert.GreaterOrEqual(t, tileIndex, uint64(1)) tilePart = layout.PartialTileSize(tileLevel, latestTreeSize-1, latestTreeSize) lastTileBytes, err := reader.ReadTile(ctx, tileLevel, tileIndex, tilePart) assert.NoError(t, err) assert.NotEmpty(t, lastTileBytes) assert.NotEqual(t, firstTileBytes, lastTileBytes) entryBundle, err = reader.ReadEntryBundle(ctx, tileIndex, tilePart) assert.NoError(t, err) assert.Contains(t, string(entryBundle), base64.StdEncoding.EncodeToString(artifactDigest(numNewEntries))) // Parse a HashedRekord entry from the latest entry bundle bundle := api.EntryBundle{} err = bundle.UnmarshalText(entryBundle) assert.NoError(t, err) assert.NotEmpty(t, bundle.Entries) e := &pb.Entry{} err = protojson.Unmarshal(bundle.Entries[0], e) assert.NoError(t, err) assert.Equal(t, "hashedrekord", e.Kind) assert.Equal(t, "0.0.2", e.ApiVersion) hrEntry := e.Spec.GetHashedRekordV002() assert.NotNil(t, hrEntry) // Add a DSSE entry numNewEntries++ dr, err := newDSSERequest(clientPrivKey, clientPubKey) if err != nil { t.Fatal(err) } tle, err = writer.Add(ctx, dr) assert.NoError(t, err) assertDSSETLE(t, tle, initialTreeSize+numNewEntries-1, logID, noteVerifiers, dr) safeLogSize, err := safeint.NewSafeInt64(tle.InclusionProof.TreeSize) if err != nil { t.Fatal(err) } latestTreeSize = safeLogSize.U() tileIndex = latestTreeSize / layout.TileWidth tilePart = layout.PartialTileSize(tileLevel, latestTreeSize-1, latestTreeSize) entryBundle, err = reader.ReadEntryBundle(ctx, tileIndex, tilePart) assert.NoError(t, err) expectedPayloadHash := sha256.Sum256([]byte("payload")) expectedB64PayloadHash := base64.StdEncoding.EncodeToString(expectedPayloadHash[:]) assert.Contains(t, string(entryBundle), expectedB64PayloadHash) // Parse a DSSE entry from the latest entry bundle bundle = api.EntryBundle{} err = bundle.UnmarshalText(entryBundle) assert.NoError(t, err) assert.NotEmpty(t, bundle.Entries) e = &pb.Entry{} // last entry in the bundle should be a DSSE entry err = protojson.Unmarshal(bundle.Entries[len(bundle.Entries)-1], e) assert.NoError(t, err) assert.Equal(t, "dsse", e.Kind) assert.Equal(t, "0.0.2", e.ApiVersion) dsseEntry := e.Spec.GetDsseV002() assert.NotNil(t, dsseEntry) } func TestUnimplementedReadMethods(t *testing.T) { ctx := context.Background() serverPubKey, err := pemutil.Read(defaultServerPublicKey) if err != nil { t.Fatal(err) } verifier, err := signature.LoadDefaultVerifier(serverPubKey) if err != nil { t.Fatal(err) } reader, err := read.NewReader(defaultRekorURL+"/api/v2", defaultRekorHostname, verifier) if err != nil { t.Fatal(err) } _, _, err = reader.ReadCheckpoint(ctx) assert.ErrorContains(t, err, "501") // the reader client drops the request body, hence why we only check the status code _, err = reader.ReadTile(ctx, 0, 0, 0) assert.ErrorContains(t, err, "501") _, err = reader.ReadEntryBundle(ctx, 0, 0) assert.ErrorContains(t, err, "501") } func TestPersistentDeduplication(t *testing.T) { ctx := context.Background() path, err := exec.LookPath("docker") if err != nil { t.Skip("skipping persistent deduplication test because docker is not installed") } output, err := exec.Command(path, "compose", "ps", "rekor").Output() if err != nil || !strings.Contains(string(output), "rekor-tiles-rekor-1") { t.Skip("skipping persistent deduplication test because rekor-tiles is not running as a local docker container") } // writer client writer, err := write.NewWriter(defaultRekorURL) if err != nil { t.Fatal(err) } clientPrivKey, clientPubKey, err := genKeys() if err != nil { t.Fatal(err) } // add one entry hr, err := newHashedRekordRequest(clientPrivKey, clientPubKey, 0) if err != nil { t.Fatal(err) } _, err = writer.Add(ctx, hr) assert.NoError(t, err) // add the same entry and check for in-memory deduplication _, err = writer.Add(ctx, hr) assert.Error(t, err) assert.ErrorContains(t, err, "unexpected response: 409") assert.ErrorContains(t, err, "an equivalent entry already exists in the transparency log with index") // restart rekor-tiles and check for persistent deduplication err = exec.Command(path, "compose", "restart", "rekor").Run() if err != nil { t.Fatal(err) } for i := 0; i <= 3; i++ { out, err := exec.Command(path, "compose", "ps", "rekor", "--format='{{print .Status}}'").Output() if err != nil { t.Fatal(err) } if strings.Contains(string(out), "(healthy)") { break } if i == 3 { t.Fatal("docker container took too long to restart") } time.Sleep(1 * time.Second) } _, err = writer.Add(ctx, hr) assert.Error(t, err) assert.ErrorContains(t, err, "unexpected response: 409") assert.ErrorContains(t, err, "an equivalent entry already exists in the transparency log with index") } func artifactDigest(idx uint64) []byte { baseArtifact := "testartifact" artifact := []byte(fmt.Sprintf("%s%d", baseArtifact, idx)) digest := sha256.Sum256(artifact) return digest[:] } func genKeys() (*ecdsa.PrivateKey, []byte, error) { privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, err } pubKey, err := x509.MarshalPKIXPublicKey(privKey.Public()) if err != nil { return nil, nil, err } return privKey, pubKey, nil } func newHashedRekordRequest(privKey *ecdsa.PrivateKey, pubKey []byte, idx uint64) (*pb.HashedRekordRequestV002, error) { digest := artifactDigest(idx) sig, err := ecdsa.SignASN1(rand.Reader, privKey, digest) if err != nil { return nil, err } return &pb.HashedRekordRequestV002{ Signature: &pb.Signature{ Content: sig, Verifier: &pb.Verifier{ Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: pubKey, }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, Digest: digest, }, nil } func newDSSEEnvelope(privKey *ecdsa.PrivateKey) (*pbdsse.Envelope, error) { ecdsaSigner, err := signature.LoadECDSASigner(privKey, crypto.SHA256) if err != nil { return nil, err } envelopeSigner, err := dsse.NewEnvelopeSigner(&sigdsse.SignerAdapter{ SignatureSigner: ecdsaSigner, }) if err != nil { return nil, err } payload := "payload" payloadType := "application/vnd.in-toto+json" envelope, err := envelopeSigner.SignPayload(context.Background(), payloadType, []byte(payload)) if err != nil { return nil, err } return dsset.ToProto(envelope) } func newDSSERequest(privKey *ecdsa.PrivateKey, pubKey []byte) (*pb.DSSERequestV002, error) { envelope, err := newDSSEEnvelope(privKey) if err != nil { return nil, err } return &pb.DSSERequestV002{ Envelope: envelope, Verifiers: []*pb.Verifier{ { Verifier: &pb.Verifier_PublicKey{ PublicKey: &pb.PublicKey{ RawBytes: pubKey, }, }, KeyDetails: v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, }, }, }, nil } func assertHashedRekordTLE(t *testing.T, tle *pbs.TransparencyLogEntry, initialTreeSize, numNewEntries uint64, logID []byte, verifiers []signednote.Verifier, hr *pb.HashedRekordRequestV002) { assert.NotNil(t, tle) // Check server does not set deprecated fields assert.Zero(t, tle.IntegratedTime) assert.Nil(t, tle.InclusionPromise) // Check populated fields // Assert log index is [initialTreeSize, initialTreeSize+numNewEntries). We can't know the precise index since // entry upload is done in parallel. assert.GreaterOrEqual(t, tle.LogIndex, int64(initialTreeSize)) assert.Less(t, tle.LogIndex, int64(initialTreeSize+numNewEntries)) // Assert log IDs are equivalent assert.Equal(t, tle.LogId.KeyId, logID) // Assert kind and version match expected values assert.Equal(t, tle.KindVersion.Kind, "hashedrekord") assert.Equal(t, tle.KindVersion.Version, "0.0.2") // Verify checkpoint and inclusion proof verifyInclusionProof(t, tle, verifiers) // Parse canonicalized body and assert entry matches request e := &pb.Entry{} assert.NotNil(t, tle.CanonicalizedBody) err := protojson.Unmarshal(tle.CanonicalizedBody, e) assert.NoError(t, err) assert.Equal(t, "hashedrekord", e.Kind) assert.Equal(t, "0.0.2", e.ApiVersion) hrEntry := e.Spec.GetHashedRekordV002() assert.NotNil(t, hrEntry) assert.Equal(t, hrEntry.Signature, hr.Signature) assert.Equal(t, hrEntry.Data.Algorithm, v1.HashAlgorithm_SHA2_256) assert.Equal(t, hrEntry.Data.Digest, hr.Digest) } func assertDSSETLE(t *testing.T, tle *pbs.TransparencyLogEntry, index uint64, logID []byte, verifiers []signednote.Verifier, dr *pb.DSSERequestV002) { assert.NotNil(t, tle) // Check server does not set deprecated fields assert.Zero(t, tle.IntegratedTime) assert.Nil(t, tle.InclusionPromise) // Check populated fields // Assert log index. There is a single DSSE upload so the exact index is known assert.Equal(t, tle.LogIndex, int64(index)) // Assert log IDs are equivalent assert.Equal(t, tle.LogId.KeyId, logID) // Assert kind and version match expected values assert.Equal(t, tle.KindVersion.Kind, "dsse") assert.Equal(t, tle.KindVersion.Version, "0.0.2") // Verify checkpoint and inclusion proof verifyInclusionProof(t, tle, verifiers) // Parse canonicalized body and assert entry matches request e := &pb.Entry{} assert.NotNil(t, tle.CanonicalizedBody) err := protojson.Unmarshal(tle.CanonicalizedBody, e) assert.NoError(t, err) assert.Equal(t, "dsse", e.Kind) assert.Equal(t, "0.0.2", e.ApiVersion) dsseEntry := e.Spec.GetDsseV002() assert.NotNil(t, dsseEntry) // Assert payload hash is as expected assert.Equal(t, dsseEntry.PayloadHash.Algorithm, v1.HashAlgorithm_SHA2_256) expectedPayloadHash := sha256.Sum256(dr.Envelope.Payload) assert.Equal(t, dsseEntry.PayloadHash.Digest, expectedPayloadHash[:]) // Assert signature matches envelope's signature assert.Len(t, dsseEntry.Signatures, 1) assert.Equal(t, dsseEntry.Signatures[0].Content, dr.Envelope.Signatures[0].Sig) assert.Equal(t, dsseEntry.Signatures[0].Verifier, dr.Verifiers[0]) } func verifyInclusionProof(t *testing.T, tle *pbs.TransparencyLogEntry, verifiers []signednote.Verifier) { // Server also verifies inclusion proof before returning response assert.NotNil(t, tle.InclusionProof) // Verify checkpoint signature, assuming the first verifier in the list is for the log and the remaining verifiers are for witnesses checkpoint, note, err := verify.VerifyWitnessedCheckpoint(tle.InclusionProof.Checkpoint.Envelope, verifiers[0], verifiers[1:]...) assert.NoError(t, err) // Expect 2 valid signatures, from the log and witness assert.Len(t, note.Sigs, 2) // Verify duplicated tle.inclusion_proof fields match bundle and parsed checkpoint values assert.Equal(t, tle.InclusionProof.LogIndex, tle.LogIndex) assert.Equal(t, tle.InclusionProof.TreeSize, int64(checkpoint.Size)) assert.Equal(t, tle.InclusionProof.RootHash, checkpoint.Hash) // Verify inclusion proof leafHash := rfc6962.DefaultHasher.HashLeaf(tle.CanonicalizedBody) assert.NoError(t, proof.VerifyInclusion(rfc6962.DefaultHasher, uint64(tle.LogIndex), checkpoint.Size, leafHash, tle.InclusionProof.Hashes, checkpoint.Hash)) } golang-github-sigstore-rekor-tiles-2.0.1/tests/freeze-test.sh000077500000000000000000000026271511162205500242770ustar00rootroot00000000000000# /usr/bin/env bash # Copyright 2025 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 -e export STORAGE_EMULATOR_HOST=localhost:7080 docker compose up -d --build --wait --wait-timeout 60 cleanup() { echo "cleaning up" docker compose down } trap cleanup EXIT echo "running pre-freeze tests" go test -v -tags=e2e,freeze -run TestPreFreeze ./tests echo "setting rekor to read-only" composefile=compose.yml.tmp sed -e '/"serve"/a\' -e ' - "--read-only"' compose.yml > $composefile cleanup_tmp() { cleanup rm $composefile } trap cleanup_tmp EXIT docker compose down rekor && docker compose -f $composefile up -d rekor --wait --wait-timeout 60 echo "freezing checkpoint" go run cmd/freeze-checkpoint/main.go --gcp-bucket "tiles" --signer-filepath tests/testdata/pki/ed25519-priv-key.pem --hostname rekor-local echo "running post-freeze tests" go test -v -tags=e2e,freeze -run TestPostFreeze ./tests golang-github-sigstore-rekor-tiles-2.0.1/tests/freeze_test.go000066400000000000000000000053241511162205500243460ustar00rootroot00000000000000// // Copyright 2025 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 && freeze package main import ( "context" "testing" "github.com/sigstore/rekor-tiles/v2/internal/signerverifier" "github.com/sigstore/rekor-tiles/v2/pkg/client/read" "github.com/sigstore/rekor-tiles/v2/pkg/client/write" "github.com/stretchr/testify/assert" ) const ( defaultServerPrivateKey = "./testdata/pki/ed25519-priv-key.pem" ) func setup(ctx context.Context) (read.Client, write.Client, error) { // get verifier needed for both read and write verifier, err := signerverifier.New(ctx, signerverifier.WithFile(defaultServerPrivateKey, "")) if err != nil { return nil, nil, err } // reader client reader, err := read.NewReader(defaultGCSURL, defaultRekorHostname, verifier) if err != nil { return nil, nil, err } // writer client writer, err := write.NewWriter(defaultRekorURL) if err != nil { return nil, nil, err } return reader, writer, nil } func TestPreFreeze(t *testing.T) { ctx := context.Background() reader, writer, err := setup(ctx) if err != nil { t.Fatal(err) } checkpoint, note, err := reader.ReadCheckpoint(ctx) assert.NoError(t, err) assert.NotNil(t, checkpoint) assert.NotNil(t, note) assert.NotContains(t, string(note.Text), "Log frozen —") clientPrivKey, clientPubKey, err := genKeys() if err != nil { t.Fatal(err) } hr, err := newHashedRekordRequest(clientPrivKey, clientPubKey, 1) if err != nil { t.Fatal(err) } _, err = writer.Add(ctx, hr) assert.NoError(t, err) } func TestPostFreeze(t *testing.T) { ctx := context.Background() reader, writer, err := setup(ctx) if err != nil { t.Fatal(err) } checkpoint, note, err := reader.ReadCheckpoint(ctx) assert.NoError(t, err) assert.NotNil(t, checkpoint) assert.NotNil(t, note) assert.Contains(t, string(note.Text), "Log frozen —") clientPrivKey, clientPubKey, err := genKeys() if err != nil { t.Fatal(err) } hr, err := newHashedRekordRequest(clientPrivKey, clientPubKey, 1) if err != nil { t.Fatal(err) } tle, err := writer.Add(ctx, hr) assert.Nil(t, tle) assert.Error(t, err) assert.Contains(t, err.Error(), "unexpected response: 405 This log has been frozen, please switch to the latest log.") } golang-github-sigstore-rekor-tiles-2.0.1/tests/loadtest/000077500000000000000000000000001511162205500233135ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/tests/loadtest/README.md000066400000000000000000000041031511162205500245700ustar00rootroot00000000000000# Rekor Load Test This directory contains a [k6](https://k6.io) script for load testing a Rekor v2 server. The test focuses on the write path, submitting `hashedrekord` entries to the transparency log. ## Prerequisites 1. **Install k6**: Follow the official k6 installation guide. 2. **Running Rekor v2 Instance**: The load test requires a running instance of `rekor-tiles`. You can start a local instance using Docker Compose from the root of this repository: ```sh docker compose up --wait --build ``` The services will be available at their default ports, which the k6 script is pre-configured to use. ## Running the Test To execute the load test, navigate to this directory (`tests/loadtest`) and run the following command: ```sh k6 run k6_rekor_load_test.js ``` ### Configuration The test can be configured using environment variables if you need to target a non-default deployment: * `REKOR_URL`: The URL for the Rekor write API. Defaults to `http://localhost:3003/api/v2`. * `GCS_URL`: The URL for the Rekor read API, which is used for the initial health check against the `/checkpoint` endpoint. Defaults to `http://localhost:7080/tiles`. ## Test Scenario The script simulates a ramp-up of virtual users (VUs) to stress the Rekor server's write endpoint (`/log/entries`). The test scenario is defined as a `ramping-vus` executor with the following stages: 1. Ramp up to 5 VUs over 30 seconds. 2. Ramp up to 20 VUs over 1 minute. 3. Ramp up to 100 VUs over 30 seconds. 4. Stay at 100 VUs for 2 minutes. 5. Ramp down to 0 VUs over 30 seconds. Each virtual user continuously generates and submits a new `hashedrekord` entry signed with a unique ECDSA P-256 key (generated once per VU). ### Configuration The number of virtual users and the duration can be configured as well: ``` k6 run --vus 1000 --duration 60s k6_rekor_load_test.js ``` ## Metrics and Thresholds The test will fail if either of these conditions are met: * The 95th percentile of request duration (`http_req_duration`) exceeds 10 seconds. * The failure rate (`errors`) is greater than 10%. golang-github-sigstore-rekor-tiles-2.0.1/tests/loadtest/k6_rekor_load_test.js000066400000000000000000000154431511162205500274400ustar00rootroot00000000000000// Copyright 2025 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. import http from 'k6/http'; import { check, sleep } from 'k6'; import { Rate, Trend } from 'k6/metrics'; import { b64encode } from 'k6/encoding'; // Custom metrics const errorRate = new Rate('errors'); const latency = new Trend('latency', true); // Store keys per VU to avoid re-importing const vuKeys = {}; // Test configuration export const options = { scenarios: { load_test: { executor: 'ramping-vus', stages: [ { duration: '30s', target: 5 }, { duration: '1m', target: 20 }, { duration: '30s', target: 100 }, { duration: '2m', target: 100 }, { duration: '30s', target: 0 }, ], }, }, thresholds: { http_req_duration: ['p(95)<10000'], errors: ['rate<0.1'], }, }; const BASE_URL = __ENV.REKOR_URL || 'http://localhost:3003/api/v2'; const BASE_READ_URL = __ENV.GCS_URL || 'http://localhost:7080/tiles'; async function generateKeyPair() { return await crypto.subtle.generateKey( { name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify'] ); } async function createUniqueDataHash() { const uniqueContent = `test-artifact-${Date.now()}-${__VU}-${__ITER}-${Math.random()}`; // k6 does not have a global TextEncoder. We can manually encode the string // to a Uint8Array as it only contains ASCII characters. const bytes = []; for (let i = 0; i < uniqueContent.length; i++) { bytes.push(uniqueContent.charCodeAt(i)); } const data = new Uint8Array(bytes); const hashBuffer = await crypto.subtle.digest('SHA-256', data); // Return the raw digest (for the API) and the original data (for signing) return { hash: hashBuffer, originalData: data }; } /** * Converts a raw ECDSA signature (r and s values concatenated) to ASN.1 DER format. * @param {Uint8Array} rawSignature The 64-byte raw signature. * @returns {Uint8Array} The DER-encoded signature. */ function rawSignatureToDER(rawSignature) { const r = rawSignature.slice(0, 32); const s = rawSignature.slice(32, 64); // Helper to encode an integer in DER format const encodeInteger = (integer) => { // Remove leading zeros let offset = 0; while (offset < integer.length && integer[offset] === 0) { offset++; } // Handle case where integer is all zeros if (offset === integer.length) { return new Uint8Array([0x02, 0x01, 0x00]); } const trimmed = integer.slice(offset); // If the high bit is set, prepend a zero byte to keep it positive const needsPadding = trimmed[0] >= 0x80; const length = trimmed.length + (needsPadding ? 1 : 0); const result = new Uint8Array(2 + length); result[0] = 0x02; // INTEGER tag result[1] = length; // length let pos = 2; if (needsPadding) { result[pos++] = 0x00; } result.set(trimmed, pos); return result; }; const rDer = encodeInteger(r); const sDer = encodeInteger(s); // Create SEQUENCE containing the two INTEGERs const totalLength = rDer.length + sDer.length; const result = new Uint8Array(2 + totalLength); result[0] = 0x30; // SEQUENCE tag result[1] = totalLength; // length result.set(rDer, 2); result.set(sDer, 2 + rDer.length); return result; } async function signDataECDSA(originalData, privateKey) { // Sign the original data - crypto.subtle.sign will hash it automatically const signature = await crypto.subtle.sign( { name: 'ECDSA', hash: 'SHA-256' }, privateKey, originalData ); const derSignature = rawSignatureToDER(new Uint8Array(signature)); return b64encode(derSignature); } async function createHashedRekordEntry(privateKey, publicKeyContent) { const { hash, originalData } = await createUniqueDataHash(); const signature = await signDataECDSA(originalData, privateKey); return { proposedEntry: { digest: b64encode(hash), signature: { content: signature, verifier: { publicKey: { rawBytes: publicKeyContent, }, keyDetails: 'PKIX_ECDSA_P256_SHA_256', }, } }, }; } export default async function(data) { const startTime = Date.now(); try { // Import private key once per VU if (!vuKeys[__VU]) { vuKeys[__VU] = await crypto.subtle.importKey( 'jwk', data.privateKeyJwk, { name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign'] ); } // Use pre-computed public key content from setup const { proposedEntry } = await createHashedRekordEntry(vuKeys[__VU], data.publicKeyContent) const response = http.post( `${BASE_URL}/log/entries`, JSON.stringify({ hashedRekordRequestV002: proposedEntry }), { headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' } } ); const duration = Date.now() - startTime; latency.add(duration); const success = check(response, { 'status is 201 or 409': (r) => r.status === 201 || r.status === 409 }); errorRate.add(!success); if (!success) { console.log(`❌ Failed: ${response.status} ${response.body}`); } else if (response.status === 201) { console.log(`✅ Created (${duration}ms)`); } } catch (e) { errorRate.add(1); console.log(`💥 Exception: ${e.message}`); } sleep(0.05); } export async function setup() { console.log(`🚀 Starting Rekor Load Test | Target: ${BASE_URL}`); const health = http.get(`${BASE_READ_URL}/checkpoint`); if (health.status !== 200) throw new Error(`Health check failed`); console.log('✅ Health check passed'); console.log('🔑 Generating key pair...'); const keyPair = await generateKeyPair(); // Pre-compute the expensive public key operations once const privateKeyJwk = await crypto.subtle.exportKey('jwk', keyPair.privateKey); const publicKeyBuffer = await crypto.subtle.exportKey('spki', keyPair.publicKey); const publicKeyContent = b64encode(publicKeyBuffer); console.log('🔑 Key pair generated and processed.'); return { startTime: Date.now(), privateKeyJwk, publicKeyContent }; } export function teardown(data) { const duration = (Date.now() - data.startTime) / 1000; console.log(`🏁 Test completed in ${duration.toFixed(1)}s`); } golang-github-sigstore-rekor-tiles-2.0.1/tests/sharding/000077500000000000000000000000001511162205500232735ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/tests/sharding/client/000077500000000000000000000000001511162205500245515ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/tests/sharding/client/sign/000077500000000000000000000000001511162205500255115ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/tests/sharding/client/sign/sign.go000066400000000000000000000113611511162205500270020ustar00rootroot00000000000000// // Copyright 2025 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" "crypto/x509" "encoding/pem" "flag" "fmt" "log" "os" "time" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/sign" "github.com/sigstore/sigstore/pkg/signature" "google.golang.org/protobuf/encoding/protojson" ) var ( rekorURL *string bundleOut *string keyOut *string trustedRootPath *string signingConfigPath *string ) func init() { rekorURL = flag.String("rekor-url", "", "rekor URL to bypass signing config discovery") bundleOut = flag.String("bundle-out", "", "output path to bundle") keyOut = flag.String("key-out", "", "output path to generated public key") trustedRootPath = flag.String("trusted-root", "", "path to trusted root") signingConfigPath = flag.String("signing-config", "", "path to signing config") flag.Parse() } func main() { if flag.NArg() == 0 { log.Fatal("expected artifact to sign") } artifact, err := os.ReadFile(flag.Arg(0)) if err != nil { log.Fatal(err) } content := &sign.PlainData{ Data: artifact, } keypair, err := key() if err != nil { log.Fatal(err) } opts := sign.BundleOptions{} trustedRoot, err := trustedMaterialForKey(keypair) if err != nil { log.Fatal(err) } opts.TrustedRoot = trustedRoot signingConfig, err := signingConfig() if err != nil { log.Fatal(err) } rekorURLs, err := discoverRekorURLs(signingConfig, *rekorURL) if err != nil { log.Fatal(err) } err = setRekorOpts(&opts, rekorURLs) if err != nil { log.Fatal(err) } signedBundle, err := sign.Bundle(content, keypair, opts) if err != nil { log.Fatal(err) } bundleJSON, err := protojson.Marshal(signedBundle) if err != nil { log.Fatal(err) } err = os.WriteFile(*bundleOut, bundleJSON, 0600) if err != nil { log.Fatal(err) } fmt.Println("SIGNED") } func key() (*sign.EphemeralKeypair, error) { keypair, err := sign.NewEphemeralKeypair(nil) if err != nil { return nil, err } pubPem, err := keypair.GetPublicKeyPem() if err != nil { return nil, err } err = os.WriteFile(*keyOut, []byte(pubPem), 0600) if err != nil { return nil, err } return keypair, nil } func trustedMaterialForKey(keypair *sign.EphemeralKeypair) (root.TrustedMaterial, error) { pubPem, err := keypair.GetPublicKeyPem() if err != nil { return nil, err } block, _ := pem.Decode([]byte(pubPem)) pubKey, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, err } verifier, err := signature.LoadVerifier(pubKey, crypto.SHA256) if err != nil { return nil, err } key := root.NewExpiringKey(verifier, time.Time{}, time.Time{}) trustedRoot, err := root.NewTrustedRootFromPath(*trustedRootPath) if err != nil { return nil, err } trustedMaterial := &verifyTrustedMaterial{ TrustedMaterial: trustedRoot, keyTrustedMaterial: root.NewTrustedPublicKeyMaterial(func(_ string) (root.TimeConstrainedVerifier, error) { return key, nil }), } return trustedMaterial, nil } func signingConfig() (*root.SigningConfig, error) { return root.NewSigningConfigFromPath(*signingConfigPath) } func discoverRekorURLs(signingConfig *root.SigningConfig, url string) ([]string, error) { fakeTime := os.Getenv("NOW") now := time.Now() if fakeTime != "" { var err error now, err = time.Parse(time.RFC3339, fakeTime) if err != nil { return nil, err } } if url != "" { return []string{url}, nil } services, err := root.SelectServices(signingConfig.RekorLogURLs(), signingConfig.RekorLogURLsConfig(), []uint32{2}, now) if err != nil { return nil, err } var urls []string for _, s := range services { urls = append(urls, s.URL) } return urls, nil } func setRekorOpts(opts *sign.BundleOptions, urls []string) error { for _, url := range urls { rekorOpts := &sign.RekorOptions{ BaseURL: url, Timeout: time.Duration(90 * time.Second), Retries: 0, Version: 2, } opts.TransparencyLogs = append(opts.TransparencyLogs, sign.NewRekor(rekorOpts)) } return nil } type verifyTrustedMaterial struct { root.TrustedMaterial keyTrustedMaterial root.TrustedMaterial } func (v *verifyTrustedMaterial) PublicKeyVerifier(hint string) (root.TimeConstrainedVerifier, error) { return v.keyTrustedMaterial.PublicKeyVerifier(hint) } golang-github-sigstore-rekor-tiles-2.0.1/tests/sharding/client/verify/000077500000000000000000000000001511162205500260555ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/tests/sharding/client/verify/verify.go000066400000000000000000000063301511162205500277120ustar00rootroot00000000000000// // Copyright 2025 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" "crypto/sha256" "crypto/x509" "encoding/pem" "flag" "fmt" "log" "os" "time" "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/verify" "github.com/sigstore/sigstore/pkg/signature" ) var ( bundlePath *string keyPath *string trustedRootPath *string ) func init() { bundlePath = flag.String("bundle", "", "path to bundle") keyPath = flag.String("key", "", "path to public key") trustedRootPath = flag.String("trusted-root", "", "path to trusted root") flag.Parse() } func main() { if flag.NArg() == 0 { log.Fatal("expected artifact to verify") } artifact, err := os.ReadFile(flag.Arg(0)) if err != nil { log.Fatal(err) } trustedRoot, err := trustedMaterialForKey() if err != nil { log.Fatal(err) } verifierConfig := []verify.VerifierOption{ verify.WithNoObserverTimestamps(), verify.WithTransparencyLog(1), } policy := verifyPolicy(artifact) b, err := getBundle() if err != nil { log.Fatal(err) } sev, err := verify.NewVerifier(trustedRoot, verifierConfig...) if err != nil { log.Fatal(err) } _, err = sev.Verify(b, policy) if err != nil { log.Fatal(err) } fmt.Println("VERIFIED") } func trustedMaterialForKey() (root.TrustedMaterial, error) { pubPem, err := os.ReadFile(*keyPath) if err != nil { return nil, err } block, _ := pem.Decode([]byte(pubPem)) pubKey, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, err } verifier, err := signature.LoadVerifier(pubKey, crypto.SHA256) if err != nil { return nil, err } key := root.NewExpiringKey(verifier, time.Time{}, time.Time{}) trustedRoot, err := root.NewTrustedRootFromPath(*trustedRootPath) if err != nil { return nil, err } trustedMaterial := &verifyTrustedMaterial{ TrustedMaterial: trustedRoot, keyTrustedMaterial: root.NewTrustedPublicKeyMaterial(func(_ string) (root.TimeConstrainedVerifier, error) { return key, nil }), } return trustedMaterial, nil } func getBundle() (*bundle.Bundle, error) { return bundle.LoadJSONFromPath(*bundlePath) } func verifyPolicy(artifact []byte) verify.PolicyBuilder { digest := sha256.Sum256(artifact) artifactPolicy := verify.WithArtifactDigest("sha256", digest[:]) identityPolicies := []verify.PolicyOption{verify.WithKey()} return verify.NewPolicy(artifactPolicy, identityPolicies...) } type verifyTrustedMaterial struct { root.TrustedMaterial keyTrustedMaterial root.TrustedMaterial } func (v *verifyTrustedMaterial) PublicKeyVerifier(hint string) (root.TimeConstrainedVerifier, error) { return v.keyTrustedMaterial.PublicKeyVerifier(hint) } golang-github-sigstore-rekor-tiles-2.0.1/tests/sharding/compose.yml000066400000000000000000000114621511162205500254670ustar00rootroot00000000000000# # Copyright 2025 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. services: spanner1: image: gcr.io/cloud-spanner-emulator/emulator:1.5.43@sha256:a0e788c709a2746cba1b8d27e913b952809b92b23a6325e940e07c038d2d07d3 profiles: - shard1 networks: - shard1 spanner2: image: gcr.io/cloud-spanner-emulator/emulator:1.5.43@sha256:a0e788c709a2746cba1b8d27e913b952809b92b23a6325e940e07c038d2d07d3 profiles: - shard2 networks: - shard2 gcs1: image: fsouza/fake-gcs-server:1.52.3@sha256:666f86b873120818b10a5e68d99401422fcf8b00c1f27fe89599c35236f48b4c volumes: - bucket:/data/tiles:rw command: - "-scheme=http" - "-port=7080" - "-public-host=localhost:7080" ports: - "7080:7080" profiles: - shard1 networks: - shard1 gcs2: image: fsouza/fake-gcs-server:1.52.3@sha256:666f86b873120818b10a5e68d99401422fcf8b00c1f27fe89599c35236f48b4c volumes: - bucket:/data/tiles:rw command: - "-scheme=http" - "-port=7080" - "-public-host=localhost:7081" ports: - "7081:7080" profiles: - shard2 networks: - shard2 rekor_init1: build: context: ../.. dockerfile: Dockerfile.emulator_init environment: - GCP_PROJECT=rekor-tiles-e2e - SPANNER_INSTANCE=rekor-tiles - SPANNER_DB=sequencer - SPANNER_EMULATOR_REST_HOST=http://spanner1:9020/ - SPANNER_EMULATOR_HOST=spanner1:9010 healthcheck: test: - CMD-SHELL - "test -f /root/finished" timeout: 10s retries: 4 profiles: - shard1 networks: - shard1 depends_on: - spanner1 - gcs1 rekor_init2: build: context: ../.. dockerfile: Dockerfile.emulator_init environment: - GCP_PROJECT=rekor-tiles-e2e - SPANNER_INSTANCE=rekor-tiles - SPANNER_DB=sequencer - SPANNER_EMULATOR_REST_HOST=http://spanner2:9020/ - SPANNER_EMULATOR_HOST=spanner2:9010 healthcheck: test: - CMD-SHELL - "test -f /root/finished" timeout: 10s retries: 4 profiles: - shard2 networks: - shard2 depends_on: - spanner2 - gcs2 rekor1: build: context: ../.. target: deploy environment: - SPANNER_EMULATOR_HOST=spanner1:9010 - STORAGE_EMULATOR_HOST=gcs1:7080 command: - "rekor-server" - "serve" - "--http-address=0.0.0.0" - "--grpc-address=0.0.0.0" - "--hostname=shard1.rekor.local" - "--gcp-bucket=tiles" - "--gcp-spanner=projects/rekor-tiles-e2e/instances/rekor-tiles/databases/sequencer" - "--signer-filepath=/pki/ed25519-priv-1.pem" - "--checkpoint-interval=2s" - "--log-level=debug" - "--request-response-logging=true" ports: - "3003:3000" # http port - "3001:3001" # grpc port healthcheck: test: - CMD-SHELL - curl http://localhost:3000/healthz | grep '{"status":"SERVING"}' timeout: 30s retries: 10 interval: 3s # requires docker engine >= v25 # start_period: 5s # start_interval: 1s volumes: - ${WORKDIR}/pki:/pki depends_on: rekor_init1: condition: service_completed_successfully profiles: - shard1 networks: - shard1 rekor2: build: context: ../.. target: deploy environment: - SPANNER_EMULATOR_HOST=spanner2:9010 - STORAGE_EMULATOR_HOST=gcs2:7080 command: - "rekor-server" - "serve" - "--http-address=0.0.0.0" - "--grpc-address=0.0.0.0" - "--hostname=shard2.rekor.local" - "--gcp-bucket=tiles" - "--gcp-spanner=projects/rekor-tiles-e2e/instances/rekor-tiles/databases/sequencer" - "--signer-filepath=/pki/ed25519-priv-2.pem" - "--checkpoint-interval=2s" - "--log-level=debug" - "--request-response-logging=true" ports: - "3030:3000" # http port - "3002:3001" # grpc port healthcheck: test: - CMD-SHELL - curl http://localhost:3000/healthz | grep '{"status":"SERVING"}' timeout: 30s retries: 10 interval: 3s # requires docker engine >= v25 # start_period: 5s # start_interval: 1s volumes: - ${WORKDIR}/pki:/pki depends_on: rekor_init2: condition: service_completed_successfully profiles: - shard2 networks: - shard2 volumes: bucket: {} networks: shard1: {} shard2: {} golang-github-sigstore-rekor-tiles-2.0.1/tests/sharding/step2-test.sh000077500000000000000000000041341511162205500256460ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2025 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. source ./util.sh TEST_validate_shard1_entry_for_second_validity_window() { local id id=1.1 local artifact_path artifact_path=$WORKDIR/data.$id.txt go run client/verify/verify.go -bundle "$WORKDIR/bundle.$id.json" -key "$WORKDIR/entry-key.$id.pem" -trusted-root "$WORKDIR/targets/trusted_root.json" "$artifact_path" } TEST_validate_shard2_active_for_second_validity_window() { local id id=2.1 local artifact_path artifact_path=$WORKDIR/data.$id.txt make_artifact "$artifact_path" go run client/sign/sign.go -bundle-out "$WORKDIR/bundle.$id.json" -key-out "$WORKDIR/entry-key.$id.pem" -trusted-root "$WORKDIR/targets/trusted_root.json" -signing-config "$WORKDIR/targets/signing_config.json" "$artifact_path" assert_log_index "$WORKDIR/bundle.$id.json" 1 assert_shard "$WORKDIR/bundle.$id.json" shard2.rekor.local go run client/verify/verify.go -bundle "$WORKDIR/bundle.$id.json" -key "$WORKDIR/entry-key.$id.pem" -trusted-root "$WORKDIR/targets/trusted_root.json" "$artifact_path" } step2() { # Test that the second service is selected by signing config. # Test that verifying bundles from the old service still works. message "TEST: shard 1 verification works during second validity window" TEST_validate_shard1_entry_for_second_validity_window message "TEST: shard2 works during second validity window" TEST_validate_shard2_active_for_second_validity_window } # Go runtime doesn't trust the fake time, have it look up time from this variable. NOW=$(date --iso-8601=sec) export NOW step2 golang-github-sigstore-rekor-tiles-2.0.1/tests/sharding/test.sh000077500000000000000000000112201511162205500246050ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2025 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 -e source ./util.sh # Validate that the first shard is online and produces valid bundles. # The first shard is discovered through signing_config.json which only contains one shard at this point. SETUP_validate_shard1_initialized() { local id id=1.0 local artifact_path artifact_path=$WORKDIR/data.$id.txt make_artifact "$artifact_path" go run client/sign/sign.go -bundle-out "$WORKDIR/bundle.$id.json" -key-out "$WORKDIR/entry-key.$id.pem" -trusted-root "$WORKDIR/targets/trusted_root.json" -signing-config "$WORKDIR/targets/signing_config.json" "$artifact_path" assert_log_index "$WORKDIR/bundle.$id.json" 0 go run client/verify/verify.go -bundle "$WORKDIR/bundle.$id.json" -key "$WORKDIR/entry-key.$id.pem" -trusted-root "$WORKDIR/targets/trusted_root.json" "$artifact_path" } # Validate that the second shard is online and produces valid bundles. # The second shard is not discoverable through signing_config.json because its validity period has not started, so use its URL directly SETUP_validate_shard2_initialized() { local id id=2.0 local artifact_path artifact_path=$WORKDIR/data.$id.txt make_artifact "$artifact_path" go run client/sign/sign.go --rekor-url "$SHARD2_URL" -bundle-out "$WORKDIR/bundle.$id.json" -key-out "$WORKDIR/entry-key.$id.pem" -trusted-root "$WORKDIR/targets/trusted_root.json" -signing-config "$WORKDIR/targets/signing_config.json" "$artifact_path" assert_log_index "$WORKDIR/bundle.$id.json" 0 assert_shard "$WORKDIR/bundle.$id.json" shard2.rekor.local go run client/verify/verify.go -bundle "$WORKDIR/bundle.$id.json" -key "$WORKDIR/entry-key.$id.pem" -trusted-root "$WORKDIR/targets/trusted_root.json" "$artifact_path" } TEST_validate_shard1_active_for_first_validity_window() { local id id=1.1 local artifact_path artifact_path=$WORKDIR/data.$id.txt make_artifact "$artifact_path" go run client/sign/sign.go -bundle-out "$WORKDIR/bundle.$id.json" -key-out "$WORKDIR/entry-key.$id.pem" -trusted-root "$WORKDIR/targets/trusted_root.json" -signing-config "$WORKDIR/targets/signing_config.json" "$artifact_path" assert_log_index "$WORKDIR/bundle.$id.json" 1 assert_shard "$WORKDIR/bundle.$id.json" shard1.rekor.local go run client/verify/verify.go -bundle "$WORKDIR/bundle.$id.json" -key "$WORKDIR/entry-key.$id.pem" -trusted-root "$WORKDIR/targets/trusted_root.json" "$artifact_path" } run() { # Bring up the first shard. Set up trusted_root.json and signing_config.json # to point to it. Signing and verifying should work normally. message "SETUP: starting first shard" local shard1_key shard1_key=$(new_key 1) start_shard1 "$shard1_key" # Test that the one service works. SETUP_validate_shard1_initialized # Bring up the second shard. Add the second shard to trusted_root.json and # signing_config.json to be chosen for signing in the future but be valid for # verification now. Signing should upload entries to the first shard, but # verification should work for both. message "SETUP: starting second shard" local shard2_key shard2_key=$(new_key 2) start_shard2 "$shard1_key" "$shard2_key" SETUP_validate_shard2_initialized # Test that the second service works, using its URL explicitly, and check that # verifying works with the new TUF keys. # Test that the original service is still selected by signing config and still works. message "TEST: shard 1 works during first validity window" TEST_validate_shard1_active_for_first_validity_window # Turn off the old shard to demonstrate that there is no longer any reliance on the old shard. # In reality, the old shard would remain running and accepting write requests until the end of its validity window. docker_down shard1 # Run the tests for the second validity window. # faketime can only run commands, not functions or expressions, # so they are contained in a second script. # The second shard becomes valid in signing_config.json 1 minute in the future, so # jump ahead to land in that window. faketime '65 seconds' ./step2-test.sh # Shut down all the containers. message "CLEANUP: turning down services" cleanup } run golang-github-sigstore-rekor-tiles-2.0.1/tests/sharding/util.sh000066400000000000000000000126271511162205500246140ustar00rootroot00000000000000#!/usr/bin/env bash # # Copyright 2025 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. export WORKDIR=${WORKDIR:-$(mktemp -d)} export SHARD1_URL=http://localhost:3003 export SHARD2_URL=http://localhost:3030 new_trusted_root() { local flags while [[ "$#" -gt 0 ]]; do case $1 in -rekor) local key=$2 local origin=$3 local start=$4 local end if [ "$#" -gt 4 ] && [ "${5:0:1}" != "-" ]; then end=$5 shift fi shift 4 flags="$flags --rekor-key $key,$origin --rekor-url http://${origin} --rekor-start-time $start" if [ -n "$end" ]; then flags="$flags --rekor-end-time $end" fi ;; esac done local targetdir targetdir=${WORKDIR}/targets mkdir -p "$targetdir" cosign trusted-root create $flags \ --out "${targetdir}/trusted_root.json" } new_signing_config() { local shard1_tlog shard1_tlog=$1 local shard1_start shard1_start=$2 local shard1_end local shard2_tlog local shard2_start if [ "$#" -gt 2 ] ; then shard1_end=$3 shard2_tlog=$4 shard2_start=$5 fi local shard1_tlog_cfg shard1_tlog_cfg=$(tlog_configs "$shard1_tlog" "$shard1_start" "$shard2_start") local shard2_tlog_cfg if [ -n "$shard2_tlog" ] ; then shard2_tlog_cfg=$(tlog_configs "$shard2_tlog" "$shard2_start") fi local signing_config signing_config=$(cat < "${WORKDIR}/targets/signing_config.json" } tlog_configs() { local url url=$1 local start start=$2 local end end=$3 local inner inner=$(jq -n --arg "start" "$start" '$ARGS.named') if [ -n "$end" ] ; then inner=$(echo "$inner" | jq '. += { "end": "'"$end"'"}') fi jq -n --arg "url" "$url" --arg "majorApiVersion" 2 \ --argjson "validFor" "$inner" \ --arg "operator" "sharding-test" '$ARGS.named' } new_key() { local id id=$1 local private private=ed25519-priv-${id}.pem local public public=ed25519-pub-${id}.pem local keydir keydir=${WORKDIR}/pki mkdir -p "$keydir" openssl genpkey -algorithm ed25519 -out "${keydir}/${private}" openssl pkey -in "${keydir}/${private}" -pubout -out "${keydir}/${public}" echo "${keydir}/${public}" } start_shard1() { local rekorkey_shard1 rekorkey_shard1=$1 # generate trusted root local shard1_start shard1_start=$(date --iso-8601=sec) new_trusted_root -rekor "$rekorkey_shard1" shard1.rekor.local "$shard1_start" # generate signing config new_signing_config http://localhost:3003 "$shard1_start" # start the service docker compose --profile shard1 up -d --wait } start_shard2() { local rekorkey_shard1 rekorkey_shard1=$1 local rekorkey_shard2 rekorkey_shard2=$2 local shard1_start shard1_start=$(date -d '1 minute ago' --iso-8601=sec) local shard2_start shard2_start=$(date -d '1 minute' --iso-8601=sec) local shard1_end shard1_end=$(date -d '1 week' --iso-8601=sec) new_trusted_root -rekor "$rekorkey_shard1" shard1.rekor.local "$shard1_start" "$shard1_end" -rekor "$rekorkey_shard2" shard2.rekor.local "$(date --iso-8601=sec)" new_signing_config http://localhost:3003 "$shard1_start" "$shard2_start" http://localhost:3030 "$shard2_start" docker compose --profile shard2 up -d --wait } assert_log_index() { local bundle_path bundle_path=$1 local expect_log_index expect_log_index=$2 local got_log_index got_log_index=$(jq -r .verificationMaterial.tlogEntries[0].logIndex "$bundle_path") if [[ $expect_log_index -ne $got_log_index ]] ; then echo "Unexpected log index for entry: expected $expect_log_index, got $got_log_index" exit 1 fi } assert_shard() { local bundle_path bundle_path=$1 local expect_shard_origin expect_shard_origin=$2 local got_checkpoint_envelope got_checkpoint_envelope=$(jq -r .verificationMaterial.tlogEntries[0].inclusionProof.checkpoint.envelope "$bundle_path") if ! echo "$got_checkpoint_envelope" | grep "$expect_shard_origin" >/dev/null ; then echo "Unexpected origin for server, entry was sent to incorrect server: expected $expect_shard_origin, got $got_checkpoint_envelope" exit 1 fi } make_artifact() { local artifact_path artifact_path=$1 echo "$RAND" > "$artifact_path" } message() { local content content=$1 local length (( length=${#content}+4 )) printf "#%.0s" $(seq $length) printf "\n" printf "# %s #" "$content" printf "\n" printf "#%.0s" $(seq $length) printf "\n" } docker_down() { local shard=$1 docker compose --profile $shard down } cleanup() { docker_down shard1 # should already be off docker_down shard2 } golang-github-sigstore-rekor-tiles-2.0.1/tests/testdata/000077500000000000000000000000001511162205500233055ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/tests/testdata/pki/000077500000000000000000000000001511162205500240705ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/tests/testdata/pki/ed25519-priv-key.pem000066400000000000000000000001671511162205500273410ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIGuZ8UWTFmXi/26ZgF4VYL8HfLSuW12TN5XMFQRt1Loc -----END PRIVATE KEY----- golang-github-sigstore-rekor-tiles-2.0.1/tests/testdata/pki/ed25519-pub-key.pem000066400000000000000000000001611511162205500271410ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAREvJyNZGjX6B3DAIuD3BTg9rIwV00GY8Xg5FU+IFDUQ= -----END PUBLIC KEY----- golang-github-sigstore-rekor-tiles-2.0.1/tests/testdata/witness/000077500000000000000000000000001511162205500250015ustar00rootroot00000000000000golang-github-sigstore-rekor-tiles-2.0.1/tests/testdata/witness/config.yaml000066400000000000000000000002371511162205500271340ustar00rootroot00000000000000Logs: - Origin: rekor-local URL: http://gcs:7080/tiles PublicKey: rekor-local+d80b4420+AURLycjWRo1+gdwwCLg9wU4PayMFdNBmPF4ORVPiBQ1E Feeder: tilesgolang-github-sigstore-rekor-tiles-2.0.1/tests/testdata/witness/policy.yaml000066400000000000000000000001621511162205500271630ustar00rootroot00000000000000witness o1 rekor-witness-test+a478f5cd+AUtKAvrTeY7srtAMfP5JCUOkZoU+A7F5VA094y5LGr89 http://witness:8100 quorum o1