pax_global_header00006660000000000000000000000064145572724560014533gustar00rootroot0000000000000052 comment=488eb9782d8d95c83ac70bfb2f5049928504127e rekor-1.3.5/000077500000000000000000000000001455727245600126635ustar00rootroot00000000000000rekor-1.3.5/.gitattributes000066400000000000000000000001521455727245600155540ustar00rootroot00000000000000/pkg/generated/** linguist-generated /pkg/generated/restapi/configure_rekor_server.go -linguist-generated rekor-1.3.5/.github/000077500000000000000000000000001455727245600142235ustar00rootroot00000000000000rekor-1.3.5/.github/dependabot.yml000066400000000000000000000026401455727245600170550ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "gomod" directory: "/" # Location of package manifests schedule: interval: "weekly" groups: all: update-types: - "patch" - package-ecosystem: "gomod" directory: "hack/tools" schedule: interval: "weekly" groups: all: update-types: - "minor" - "patch" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" groups: all: update-types: - "minor" - "patch" - package-ecosystem: "docker" directory: "/" schedule: interval: "weekly" groups: all: update-types: - "minor" - "patch" rekor-1.3.5/.github/workflows/000077500000000000000000000000001455727245600162605ustar00rootroot00000000000000rekor-1.3.5/.github/workflows/build.yml000066400000000000000000000040731455727245600201060ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: CI-Container-Build on: workflow_dispatch: push: branches: - main - 'release-**' tags: - '*' jobs: build: name: build runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: sigstore/cosign-installer@9614fae9e5c5eddabb09f90a270fcb487c9f7149 # v3.3.0 - name: Extract version of Go to use run: echo "GOVERSION=$(cat Dockerfile|grep golang | awk ' { print $2 } ' | cut -d '@' -f 1 | cut -d ':' -f 2 | uniq)" >> $GITHUB_ENV - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version: ${{ env.GOVERSION }} - name: deps run: sudo apt-get update && sudo apt-get install -yq libpcsclite-dev - uses: ko-build/setup-ko@ace48d793556083a76f1e3e6068850c1f4a369aa # v0.6 - name: Set up Cloud SDK uses: google-github-actions/auth@5a50e581162a13f4baa8916d01180d2acbc04363 # v2.1.0 with: workload_identity_provider: 'projects/498091336538/locations/global/workloadIdentityPools/githubactions/providers/sigstore-rekor' service_account: 'github-actions-rekor@projectsigstore.iam.gserviceaccount.com' - name: creds run: gcloud auth configure-docker --quiet - name: container run: KO_PREFIX=gcr.io/projectsigstore/rekor/ci/rekor make sign-keyless-ci env: COSIGN_YES: true rekor-1.3.5/.github/workflows/codeql-analysis.yml000066400000000000000000000037761455727245600221100ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed name: CodeQL on: push: branches: - main - 'release-**' pull_request: branches: - main - 'release-**' 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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Extract version of Go to use run: echo "GOVERSION=$(cat Dockerfile|grep golang | awk ' { print $2 } ' | cut -d '@' -f 1 | cut -d ':' -f 2 | uniq)" >> $GITHUB_ENV - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version: ${{ env.GOVERSION }} # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 with: languages: ${{ matrix.language }} - name: Autobuild uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 rekor-1.3.5/.github/workflows/cosign.pub000066400000000000000000000002621455727245600202520ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEm8IPMv4eqH6+t8T18QEtG3aziut1 3QVroceRyBtKOFpFLhPLpFOwznV2rHaXPmYjVTmy0uwiRvBVTinrmyITfQ== -----END PUBLIC KEY----- rekor-1.3.5/.github/workflows/cut-release.yml000066400000000000000000000017301455727245600212150ustar00rootroot00000000000000name: Cut Release on: workflow_dispatch: inputs: release_tag: required: true type: string description: 'Release tag' key_ring: required: true type: string description: 'Key ring for cosign key' key_name: required: true type: string description: 'Key name for cosign key' concurrency: cut-release jobs: cut-release: name: Cut release uses: sigstore/sigstore/.github/workflows/reusable-release.yml@main permissions: id-token: write contents: read with: release_tag: ${{ github.event.inputs.release_tag }} key_ring: ${{ github.event.inputs.key_ring }} key_name: ${{ github.event.inputs.key_name }} workload_identity_provider: 'projects/498091336538/locations/global/workloadIdentityPools/githubactions/providers/sigstore-rekor' service_account: 'github-actions-rekor@projectsigstore.iam.gserviceaccount.com' repo: 'rekor' rekor-1.3.5/.github/workflows/depsreview.yml000066400000000000000000000015231455727245600211610ustar00rootroot00000000000000# # Copyright 2022 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: 'Dependency Review' on: [pull_request] permissions: contents: read jobs: dependency-review: name: License and Vulnerability Scan uses: sigstore/community/.github/workflows/reusable-dependency-review.yml@9b1b5aca605f92ec5b1bf3681b1e61b3dbc420cc rekor-1.3.5/.github/workflows/main.yml000066400000000000000000000153771455727245600177440ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: CI on: push: branches: - main - 'release-**' pull_request: branches: - main - 'release-**' permissions: contents: read jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Extract version of Go to use run: echo "GOVERSION=$(cat Dockerfile|grep golang | awk ' { print $2 } ' | cut -d '@' -f 1 | cut -d ':' -f 2 | uniq)" >> $GITHUB_ENV - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version: ${{ env.GOVERSION }} - name: Build run: make -C $GITHUB_WORKSPACE all - name: Fuzz-Build run: make -C $GITHUB_WORKSPACE fuzz - name: Test run: go test -v -coverprofile=coverage.txt -covermode=atomic ./... - name: Upload Coverage Report uses: codecov/codecov-action@4fe8c5f003fae66aa5ebb77cfd3e7bfbbda0b6b0 # v3.1.5 with: flags: unittests - 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: runs-on: ubuntu-latest steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Extract version of Go to use run: echo "GOVERSION=$(cat Dockerfile|grep golang | awk ' { print $2 } ' | cut -d '@' -f 1 | cut -d ':' -f 2 | uniq)" >> $GITHUB_ENV - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version: ${{ env.GOVERSION }} - uses: ko-build/setup-ko@ace48d793556083a76f1e3e6068850c1f4a369aa # v0.6 - name: container run: | make ko-local docker run --rm $(cat rekorImagerefs) version docker run --rm $(cat cliImagerefs) version docker run --rm $(cat redisImagerefs) --version e2e: runs-on: ubuntu-latest needs: build steps: - name: download minisign # run: sudo add-apt-repository ppa:dysfunctionalprogramming/minisign && sudo apt-get update && sudo apt-get install minisign run: sudo add-apt-repository ppa:savoury1/minisign && sudo apt-get update && sudo apt-get install minisign - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Extract version of Go to use run: echo "GOVERSION=$(cat Dockerfile|grep golang | awk ' { print $2 } ' | cut -d '@' -f 1 | cut -d ':' -f 2 | uniq)" >> $GITHUB_ENV - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version: ${{ env.GOVERSION }} - name: install gocovmerge run: make gocovmerge - name: CLI run: ./tests/e2e-test.sh - name: Refactor-e2e # this will a WIP to move all the tests to respective packages run: ./e2e-test.sh - name: Upload logs if they exist uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: failure() with: name: E2E Docker Compose logs path: /tmp/docker-compose.log - name: Upload Coverage Report uses: codecov/codecov-action@4fe8c5f003fae66aa5ebb77cfd3e7bfbbda0b6b0 # v3.1.5 with: files: /tmp/rekor-merged.cov,/tmp/pkg-rekor-merged.cov flags: e2etests sharding-e2e: runs-on: ubuntu-latest needs: build steps: - name: download minisign # run: sudo add-apt-repository ppa:dysfunctionalprogramming/minisign && sudo apt-get update && sudo apt-get install minisign run: sudo add-apt-repository ppa:savoury1/minisign && sudo apt-get update && sudo apt-get install minisign - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Docker Build run: docker-compose build - name: Extract version of Go to use run: echo "GOVERSION=$(cat Dockerfile|grep golang | awk ' { print $2 } ' | cut -d '@' -f 1 | cut -d ':' -f 2 | uniq)" >> $GITHUB_ENV - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version: ${{ env.GOVERSION }} - name: Sharding Test run: ./tests/sharding-e2e-test.sh - name: Upload logs if they exist uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: failure() with: name: Sharding E2E Docker Compose logs path: /tmp/docker-compose.log issue-872-e2e: runs-on: ubuntu-latest needs: build steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Docker Build run: docker-compose build - name: Extract version of Go to use run: echo "GOVERSION=$(cat Dockerfile|grep golang | awk ' { print $2 } ' | cut -d '@' -f 1 | cut -d ':' -f 2 | uniq)" >> $GITHUB_ENV - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version: ${{ env.GOVERSION }} - name: Test for Attestation begin returned that was previously persisted in tlog run: ./tests/issue-872-e2e-test.sh - name: Upload logs if they exist uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: failure() with: name: Docker Compose logs path: /tmp/*docker-compose.log harness: runs-on: ubuntu-latest needs: build steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Create git branch run: git switch -c harness-test-branch - name: Extract version of Go to use run: echo "GOVERSION=$(cat Dockerfile|grep golang | awk ' { print $2 } ' | cut -d '@' -f 1 | cut -d ':' -f 2 | uniq)" >> $GITHUB_ENV - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version: ${{ env.GOVERSION }} - name: Run test harness run: ./tests/rekor-harness.sh - name: Upload logs if they exist uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0 if: failure() with: name: E2E Docker Compose logs path: /tmp/docker-compose.log rekor-1.3.5/.github/workflows/milestone.yml000066400000000000000000000027531455727245600210110ustar00rootroot00000000000000name: Milestone on: pull_request_target: types: [closed] branches: - main - 'release-**' jobs: milestone: runs-on: ubuntu-latest permissions: actions: none checks: none contents: read deployments: none issues: write packages: none pull-requests: write repository-projects: none security-events: none statuses: none steps: - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | if (!context.payload.pull_request.merged) { console.log('PR was not merged, skipping.'); return; } if (!!context.payload.pull_request.milestone) { console.log('PR has existing milestone, skipping.'); return; } milestones = await github.rest.issues.listMilestones({ owner: context.repo.owner, repo: context.repo.repo, state: 'open', sort: 'due_on', direction: 'asc' }) if (milestones.data.length === 0) { console.log('There are no milestones, skipping.'); return; } await github.rest.issues.update({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.payload.pull_request.number, milestone: milestones.data[0].number }); rekor-1.3.5/.github/workflows/scorecard_action.yml000066400000000000000000000015431455727245600223100ustar00rootroot00000000000000name: Scorecards supply-chain security on: # Only the default branch is supported. branch_protection_rule: schedule: # Weekly on Saturdays. - cron: '30 1 * * 6' push: branches: - main - 'release-**' # Declare default permissions as none. permissions: {} jobs: analysis: name: Scorecard 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@d0c95c8803672313d0bf72e1a44021be5b583c24 # main # (Optional) Disable publish results: # with: # publish_results: false # (Optional) Enable Branch-Protection check: secrets: scorecard_token: ${{ secrets.SCORECARD_TOKEN }} rekor-1.3.5/.github/workflows/validate-release.yml000066400000000000000000000051451455727245600222170ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: CI-Validate-Release-Job on: push: branches: - main - 'release-**' pull_request: permissions: {} jobs: check-signature: runs-on: ubuntu-latest container: image: gcr.io/projectsigstore/cosign:v2.2.2-dev@sha256:1a49e2f6cf3580935863d9d8d46066db9aad3dbd673ca24cb83d143221c6e64b steps: - name: Check Signature run: | cosign verify ghcr.io/gythialy/golang-cross:v1.21.6-0@sha256:c00bdb060aff03e8042f41ed0c11a0bbbb01e2ea3f65733ce037497fcb83d5d7 \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ --certificate-identity "https://github.com/gythialy/golang-cross/.github/workflows/release-golang-cross.yml@refs/tags/v1.21.6-0" env: TUF_ROOT: /tmp validate-release-job: runs-on: ubuntu-latest needs: - check-signature container: image: ghcr.io/gythialy/golang-cross:v1.21.6-0@sha256:c00bdb060aff03e8042f41ed0c11a0bbbb01e2ea3f65733ce037497fcb83d5d7 steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 # Error: fatal: detected dubious ownership in repository at '/__w/rekor/rekor' # To add an exception for this directory, call: # git config --system --add safe.directory /__w/rekor/rekor # Reason: Recent versions of git require the .git folder to be owned # by the same user (see https://github.blog/2022-04-12-git-security-vulnerability-announced/). # Related # - https://github.com/actions/runner/issues/2033 # - https://github.com/actions/checkout/issues/1048 # - https://github.com/actions/runner-images/issues/6775 - run: git config --system --add safe.directory /__w/rekor/rekor - name: goreleaser snapshot run: make snapshot env: PROJECT_ID: honk-fake-project RUNTIME_IMAGE: gcr.io/distroless/static:debug-nonroot - name: check binaries run: | ./dist/rekor-server-linux-amd64 version ./dist/rekor-cli-linux-amd64 version rekor-1.3.5/.github/workflows/verify.yml000066400000000000000000000040661455727245600203150ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: Verify on: push: branches: - main pull_request: permissions: contents: read jobs: license-check: name: license boilerplate check runs-on: ubuntu-latest steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Extract version of Go to use run: echo "GOVERSION=$(cat Dockerfile|grep golang | awk ' { print $2 } ' | cut -d '@' -f 1 | cut -d ':' -f 2 | uniq)" >> $GITHUB_ENV - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version: ${{ env.GOVERSION }} - name: Install addlicense run: go install github.com/google/addlicense@v1.0.0 - name: Check license headers run: | set -e addlicense -l apache -c 'The Sigstore Authors' -v * git diff --exit-code golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Extract version of Go to use run: echo "GOVERSION=$(cat Dockerfile|grep golang | awk ' { print $2 } ' | cut -d '@' -f 1 | cut -d ':' -f 2 | uniq)" >> $GITHUB_ENV - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 with: go-version: ${{ env.GOVERSION }} - name: golangci-lint uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 timeout-minutes: 5 with: version: v1.55 rekor-1.3.5/.gitignore000066400000000000000000000005721455727245600146570ustar00rootroot00000000000000.DS_Store .idea/* .vscode/* /cli logid rekor-cli !rekor-cli/ rekor-server !rekor-server/ /tests/rekor-server /server swagger dist/* hack/tools/bin/* *fuzz.zip docker-compose-sharding.yaml sharding-config.yaml rekorServerImagerefs rekorCliImagerefs trillianServerImagerefs trillianSignerImagerefs rekorImagerefs cliImagerefs redisImagerefs cosign.* signature rekor.pub *~ *.test rekor-1.3.5/.golangci.yml000066400000000000000000000017741455727245600152600ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. linters: enable: - unused - errcheck - gofmt - goimports - gosec - gocritic - revive - misspell output: uniq-by-line: false issues: exclude-rules: - path: _test\.go linters: - errcheck - gosec # TODO, tracked in #286 - text: "SA1019: package golang.org/x/crypto/openpgp" linters: - staticcheck max-issues-per-linter: 0 max-same-issues: 0 run: issues-exit-code: 1 timeout: 10m rekor-1.3.5/.goreleaser.yml000066400000000000000000000060751455727245600156240ustar00rootroot00000000000000project_name: rekor env: - GO111MODULE=on - CGO_ENABLED=0 - DOCKER_CLI_EXPERIMENTAL=enabled - COSIGN_YES=true # Prevents parallel builds from stepping on eachothers toes 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' # if running a release we will generate the images in this step # if running in the CI the CI env va is set by github action runner and we dont run the ko steps # this is needed because we are generating files that goreleaser was not aware to push to GH project release - /bin/bash -c 'if [ -z "$CI" ]; then make sign-container-release; fi' gomod: proxy: true sboms: - artifacts: binary builds: - id: rekor-server-linux binary: rekor-server-linux-{{ .Arch }} no_unique_dist_dir: true main: ./cmd/rekor-server goos: - linux goarch: - amd64 - arm64 - arm - s390x - ppc64le goarm: - 7 flags: - -trimpath mod_timestamp: '{{ .CommitTimestamp }}' ldflags: - "{{ .Env.SERVER_LDFLAGS }}" - id: rekor-cli binary: rekor-cli-{{ .Os }}-{{ .Arch }} no_unique_dist_dir: true main: ./cmd/rekor-cli goos: - linux - darwin - windows goarch: - amd64 - arm64 - arm - s390x - ppc64le goarm: - 7 ignore: - goos: windows goarch: arm64 - goos: windows goarch: arm - goos: windows goarch: s390x - goos: windows goarch: ppc64le flags: - -trimpath mod_timestamp: '{{ .CommitTimestamp }}' ldflags: - "{{ .Env.CLI_LDFLAGS }}" signs: - id: rekor signature: "${artifact}.sig" cmd: cosign args: ["sign-blob", "--output-signature", "${artifact}.sig", "--key", "gcpkms://projects/{{ .Env.PROJECT_ID }}/locations/{{ .Env.KEY_LOCATION }}/keyRings/{{ .Env.KEY_RING }}/cryptoKeys/{{ .Env.KEY_NAME }}/versions/{{ .Env.KEY_VERSION }}", "${artifact}"] artifacts: binary # Keyless - id: rekor-keyless signature: "${artifact}-keyless.sig" certificate: "${artifact}-keyless.pem" cmd: cosign args: ["sign-blob", "--output-signature", "${artifact}-keyless.sig", "--output-certificate", "${artifact}-keyless.pem", "${artifact}"] artifacts: binary - id: checksum-keyless signature: "${artifact}-keyless.sig" certificate: "${artifact}-keyless.pem" cmd: cosign args: ["sign-blob", "--output-signature", "${artifact}-keyless.sig", "--output-certificate", "${artifact}-keyless.pem", "${artifact}"] artifacts: checksum archives: - format: binary name_template: "{{ .Binary }}" allow_different_binary_count: true checksum: name_template: "{{ .ProjectName }}_checksums.txt" snapshot: name_template: SNAPSHOT-{{ .ShortCommit }} release: prerelease: allow # remove this when we start publishing non-prerelease or set to auto draft: true # allow for manual edits github: owner: sigstore name: rekor footer: | ### Thanks for all contributors! extra_files: - glob: "./rekor*.yaml" rekor-1.3.5/.ko.yaml000066400000000000000000000025451455727245600142440ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. defaultBaseImage: gcr.io/distroless/static-debian12:nonroot builds: - id: rekor-server dir: . main: ./cmd/rekor-server env: - CGO_ENABLED=0 flags: - -trimpath - -tags - "{{ .Env.GIT_HASH }}" - -tags - "{{ .Env.GIT_VERSION }}" ldflags: - -extldflags "-static" - "{{ .Env.LDFLAGS }}" - id: rekor-cli dir: . main: ./cmd/rekor-cli env: - CGO_ENABLED=0 flags: - -trimpath - --tags - "{{ .Env.GIT_HASH }}" - --tags - "{{ .Env.GIT_VERSION }}" ldflags: - -extldflags "-static" - "{{ .Env.LDFLAGS }}" - id: backfill-redis dir: . main: ./cmd/backfill-redis env: - CGO_ENABLED=0 flags: - -trimpath - --tags - "{{ .Env.GIT_HASH }}" - --tags - "{{ .Env.GIT_VERSION }}" ldflags: - -extldflags "-static" - "{{ .Env.LDFLAGS }}" rekor-1.3.5/CHANGELOG.md000066400000000000000000001524201455727245600145000ustar00rootroot00000000000000# v1.3.5 ## New Features * output trace in slog and override correlation header name (#1986) * give log timestamps nanosecond precision (#1985) * Added support for sha384/sha512 hash algorithms in hashedrekords (#1959) * Change Redis value for locking mechanism (#1957) ## Bug Fixes * Fix panic for DSSE canonicalization (#1923) * Drop conditional when verifying entry checkpoint (#1917) * Remove timestamp from checkpoint (#1888) * Additional unique index correction (#1885) ## Quality Enhancements * bump trillian images to v1.6.0 (#1984) * remove trillian images from release process (#1983) * update builder to use go1.21 ## Contributors * Andrew Block * Bob Callaway * Carlos Tadeu Panato Junior * Hayden Blauzvern * Riccardo Schirone # v1.3.4 ## New Features * add mysql indexstorage backend * add s3 storage for attestations ## Bug Fixes * fix: Do not check for pubsub.topics.get on initialization (#1853) * fix optional field in cose schema ## Quality Enhancements * Update ranges.go (#1852) * update indexstorage interface to reduce roundtrips (#1838) * use a single validator library in rekor-cli (#1818) * Remove go-playground/validator dependency from pkg/pki (#1817) ## Contributors * Bob Callaway * Carlos Tadeu Panato Junior * Hayden B * James Alseth * Kenny Leung * Noah Kreiger * Zach Steindler # v1.3.3 ## New Features * update trillian to 1.5.3 (#1803) * adds redis_auth (#1627) * Add method to get artifact hash for an entry (#1777) ## Bug Fixes * Update signer flag description (#1804) * install go at correct version for codeql (#1762) ## Quality Enhancements * make e2e tests more usable with docker-compose (#1770) ## Contributors * Bob Callaway * Carlos Tadeu Panato Junior * Hayden B * ian hundere * Kenny Leung # v1.3.2 * move to go 1.21.3 to pick up fixes for CVE-2023-39325 ## Bug Fixes * build(deps): Bump golang.org/x/net from 0.16.0 to 0.17.0 (#1753) * build(deps): Bump github.com/google/go-cmp from 0.5.9 to 0.6.0 (#1755) * build(deps): Bump google/cloud-sdk from 449.0.0 to 450.0.0 (#1757) * build(deps): Bump google.golang.org/grpc from 1.58.2 to 1.58.3 (#1754) * update Dockerfile for go 1.21.3 (#1752) * update builder image to use go1.21.3 (#1751) ## Contributors * Carlos Tadeu Panato Junior # v1.3.1 ## New Features * enable GCP cloud profiling on rekor-server (#1746) * move index storage into interface (#1741) * add info to readme to denote additional documentation sources (#1722) * Add type of ed25519 key for TUF (#1677) * Allow parsing base64-encoded TUF metadata and root content (#1671) ## Quality Enhancements * disable quota in trillian in test harness (#1680) ## Bug Fixes * Update contact for code of conduct (#1720) * fix: typo (#1711) * Fix panic when parsing SSH SK pubkeys (#1712) * Correct index creation (#1708) * Update .ko.yaml (#1682) * docs: fixzes a small typo on the readme (#1686) * chore: fix `backfill-redis` Makefile target (#1685) ## Contributors * Andres Galante * Andrew Block * Appu * Bob Callaway * Carlos Tadeu Panato Junior * guangwu * Hayden B * jonvnadelberg * Lance Ball # v1.3.0 ## New Features * feat: Support publishing new log entries to Pub/Sub topics (#1580) * Change values of Identity.Raw, add fingerprints (#1628) * Extract all subjects from SANs for x509 verifier (#1632) * Fix type comment for Identity struct (#1619) * Refactor Identities API (#1611) * Refactor Verifiers to return multiple keys (#1601) ## Quality Enhancements * set min go version to 1.21 (#1651) * Upgrade to go1.21 (#1636) ## Bug Fixes * Update openapi.yaml (#1655) * pass transient errors through retrieveLogEntry (#1653) * return full entryID on HTTP 409 responses (#1650) * Update checkpoint link (#1597) * Use correct log index in inclusion proof (#1599) * remove instrumentation library (#1595) * pki: clean up fuzzer (#1594) * alpine: add max metadata size to fuzzer (#1571) ## Contributors * AdamKorcz * Appu * Bob Callaway * Carlos Tadeu Panato Junior * Ceridwen Coghlan * Hayden B * James Alseth # v1.2.2 ## Quality Enhancements * swap killswitch for 'docker-compose restart' (#1562) * pass treeSize and rootHash to avoid trillian import (#1513) * Move github.com/sigstore/protobuf-specs users into a separate subpackage (#1511) ## Bug Fixes * pass down error with message instead of nil (#1560) ## Contributors * Bob Callaway * Carlos Tadeu Panato Junior * Eng Zer Jun * Miloslav Trmač # v1.2.1 ## Bug Fixes * run go mod tidy in hack/tools (#1510) ## Contributors * Bob Callaway # v1.2.0 ## Functional Enhancements * add client method to generate TLE struct (#1498) * add dsse type (#1487) * support other KMS providers (AWS, Azure, Hashicorp) in addition to GCP (#1488) * Add concurrency to backfill-redis (#1504) * omit informational message if machine-parseable output has been requested (#1486) * Publish stable checkpoint periodically to Redis (#1461) * Add intoto v0.0.2 to backfill script (#1500) * add new method to test insertability of proposed entries into log (#1410) ## Quality Enhancements * use t.Skip() in fuzzers (#1506) * improve fuzzing coverage (#1499) * Remove watcher script (#1484) ## Bug Fixes * Merge pull request from GHSA-frqx-jfcm-6jjr * Remove requirement of PayloadHash for intoto 0.0.1 (#1490) * fix lint errors, bump linter up to 1.52 (#1485) * Remove dependencies from pkg/util (#1469) ## Contributors * Bob Callaway * Carlos Tadeu Panato Junior * Ceridwen Coghlan * Cody Soyland * Hayden B * Miloslav Trmač # v1.1.1 ## Functional Enhancements * Refactor Trillian client with exported methods (#1454) * Switch to official redis-go client (#1459) * Remove replace in go.mod (#1444) * Add Rekor OID info. (#1390) ## Quality Enhancements * remove legacy encrypted cosign key (#1446) * swap cjson dependency (#1441) * Update release readme (#1456) ## Bug Fixes * Merge pull request from GHSA-2h5h-59f5-c5x9 ## Contributors * Billy Lynch * Bob Callaway * Carlos Tadeu Panato Junior * Ceridwen Coghlan * Hayden B # v1.1.0 ## Functional Enhancements * improve validation on intoto v0.0.2 type (#1351) * add feature to limit HTTP request body length to process (#1334) * add information about the file size limit (#1313) * Add script to backfill Redis from Rekor (#1163) * Feature: add search support for sha512 (#1142) ## Quality Enhancements * fuzzing: refactor OSS-Fuzz build script (#1377) * Update cloudbuild for cosign 2.0 (#1375) * Tests - Additional sharding tests (#1180) * jar type: add fuzzer for 3rd-party dep (#1360) * update cosign to 2.0.0 and builder image and also cosign flags (#1368) * fuzzing: move alpine utils to fuzz utils (#1335) * fuzzing: add seed for alpine fuzzer (#1342) * jar: add v001 fuzzer (#1327) * fuzzing: open writer later in fuzz utils (#1326) * fuzzing: remove tar operations in alpine fuzzer (#1322) * alpine: add v001 fuzzer (#1316) * hashedrekord: add v001 fuzzer (#1315) * fuzzing: add call to IndexKeys in multiple fuzzers (#1302) * fuzzing: improve cose fuzzer (#1300) * fuzzing: improve fuzz utils (#1298) * fuzzing: improve alpine fuzzer (#1273) * fuzzing: go mod edit go-fuzz-headers (#1272) * fuzzing: add .options file (#1271) * fuzzing: build helm fuzzer from correct dir (#1264) * types: refactor multiple fuzzers (#1258) * helm: add fuzzer for provenance unmarshalling (#1243) * pki: add fuzzer (#1256) * Fuzzing: Add more bug detectors (#1253) * Refactor e2e - part 5 (#1236) * Removed unused tool/deps (#1244) * Fixed the invalid path (#1245) * Run latest fuzzers in OSS-Fuzz (#1221) * Fuzz tests - hashedrekord (#1224) * Update builder (#1228) * Revamping rekor e2e - part 4 of N (#1218) * types: add fuzzers (#1225) * jar type: add fuzzer (#1215) * Revamping rekor e2e - part 3 of N (#1177) * modify OSS-Fuzz build script (#1214) * move over oss-fuzz build script (#1204) * wrap redis client errors to aid debugging (#1176) * don't test release candidate builds in harness (#1183) * types/alpine: add fuzzer (#1200) * logging tweaks to improve usability (#1235) * Add backfill-redis to the release artifacts (#1174) * ensure jobs run on release branches (#1181) * update builder image and cosign (#1165) * Refactor e2e tests - x509 apk (#1152) * Sharding - Additional tests (#1156) * Ran gofmt and cleaned up (#1157) * Fuzz - Fuzz tests for sharding (#1147) * Revamping rekor e2e - part 1 of N (#1089) ## Bug Fixes * remove goroutine usage from SearchLogQuery (#1407) * drop log messages regarding attestation storage to debug (#1408) * fix ko-local build (#1381) * disable blocking checks (#1353) * fix validation for proposed vs committed log entries for intoto v0.0.1 (#1309) * fix: fix regex for multi-digit counts (#1321) * return NotFound if treesize is 0 rather than calling trillian (#1311) * enumerate slice to get sugared logs (#1312) * put a reasonable size limit on ssh key reader (#1288) * CLIENT: Fix Custom Host and Path Issue (#1306) * do not persist local state if log is empty; fail consistency proofs from 0 size (#1290) * correctly handle invalid or missing pki format (#1281) * Add Verifier to get public key/cert and identities for entry type (#1210) * fix goroutine leak in client; add insecure TLS option (#1238) * Fix - Remove the force-recreate flag (#1179) * trim whitespace around public keys before parsing (#1175) * stop inserting envelope hash for intoto:0.0.2 types into index (#1171) * Revert "remove double encoding of payload and signature fields for intoto (#1150)" (#1158) * remove double encoding of payload and signature fields for intoto (#1150) * fix SearchLogQuery behavior to conform to openapi spec (#1145) * Remove pem-certificate-chain from client (#1138) * fix flag type for operator in search (#1136) * use sigstore/community dep review (#1132) ## Contributors * AdamKorcz * Batuhan Apaydın * Bob Callaway * Carlos Tadeu Panato Junior * Fabian Kammel * Fredrik Skogman * Hayden B * Joyce * Naveen * Noah Kreiger * Priya Wadhwa # v1.0.1 ## Enhancements * stop inserting envelope hash for intoto:0.0.2 types into index (#1171) (#1172) ## Bug Fixes * ensure jobs run on release branches (#1181) (#1182) ## Contributors * Bob Callaway # v1.0.0 Rekor is 1.0! No changes, as this is tagged at the same commit as v1.0.0-rc.1. Thank you to all of the contributors to Rekor in the past couple years who helped make Rekor 1.0 possible! ## Contributors * Aastha Bist * Aditya Sirish * Ahmet Alp Balkan * Andrew Block * Appu * Asra Ali * axel simon * Azeem Shaikh * Batuhan Apaydın * Bob Callaway * Carlos Tadeu Panato Junior * Ceridwen Driskill * Christian Rebischke * Dan Lorenc * Dan Luhring * Eddie Zaneski * Efe Barlas * Fredrik Skogman * Harry Fallows * Hayden B * Hector Fernandez * Jake Sanders * Jason Hall * Jehan Shah * John Speed Meyers * Kenny Leung * Koichi Shiraishi * Lily Sturmann * Luke Hinds * Mikhail Swift * Morten Linderud * Nathan Smith * Naveen * Olivier Cedric Barbier * Parth Patel * Priya Wadhwa * Robert James Hernandez * Romain Aviolat * Samsondeen * Sascha Grunert * Scott Nichols * Shiwei Zhang * Simon Kent * Sylvestre Ledru * Tiziano Santoro * Trishank Karthik Kuppusamy * Ville Aikas * dhaus67 * endorama * kpcyrd # v1.0.0-rc.1 ## Enhancements * add retry command line flag on rekor-cli (#1097) * Add some info and debug logging to commonly used funcs (#1106) ## Contributors * Bob Callaway * Priya Wadhwa # v1.0-rc ## Enhancements * update swagger API version to 1.0.0 (#1102) * verify: verify checkpoint's STH against the inclusion proof root hash (#1092) * add ability to enable/disable specific rekor API endpoints (#1080) * enable configurable client retries with backoff in RekorClient (#1096) ## Bug Fixes * remove unused RekorVersion API definition (#1101) * remove unused api-key and timestamp references (#1098) ## Contributors * Bob Callaway * asraa # v0.12.2 ## Enhancements * add changelog for 0.12.0 and 0.12.1 (#1064) * add description on /api/v1/index/retrieve endpoint (#1073) * Adding e2e test coverage (#1071) * export rekor build/version information (#1074) ## Bug Fixes * Search through all shards when searching by hash (#1082) * Use POST instead of GET for /api/log/entries/retrieve metrics (#1083) ## Contributors * Bob Callaway * Carlos Tadeu Panato Junior * Ceridwen Driskill * Simon Kent * Priya Wadhwa # v0.12.1 > ** Rekor `v0.12.1` comes with a breaking change to `rekor-cli v0.12.1`. Users of rekor-cli MUST upgrade to the latest version ** > The addition of the intotov2 created a breaking change for the `rekor-cli` ## Enhancements * Adds new rekor metrics for latency and QPS. (https://github.com/sigstore/rekor/pull/1059) * feat: add file based signer and password (https://github.com/sigstore/rekor/pull/1049) ## Bug Fixes * fix: fix harness tests with intoto v0.0.2 (https://github.com/sigstore/rekor/pull/1052) ## Contributors * Asra Ali (@asraa) * Simon Kent (@var-sdk) # v0.12.0 ## Enhancements * remove /api/v1/version endpoint (https://github.com/sigstore/rekor/pull/1022) * Include checkpoint (STH) in entry upload and retrieve responses (https://github.com/sigstore/rekor/pull/1015) * Validate tree ID on calls to /api/v1/log/entries/retrieve (https://github.com/sigstore/rekor/pull/1017) * feat: add verification functions (https://github.com/sigstore/rekor/pull/986) * Change Checkpoint origin to be "Hostname - Tree ID" (https://github.com/sigstore/rekor/pull/1013) * Add bounds on number of elements in api/v1/log/entries/retrieve (https://github.com/sigstore/rekor/pull/1011) * Intoto v0.0.2 (https://github.com/sigstore/rekor/pull/973) * api.SearchLogQueryHandler thread safety (https://github.com/sigstore/rekor/pull/1006) * enable blocking specific pluggable type versions from being inserted into the log (https://github.com/sigstore/rekor/pull/1004) * check supportedVersions list rather than directly reading from version map (https://github.com/sigstore/rekor/pull/1003) ## Bug Fixes * fix retrieve endpoint response code and add testing (https://github.com/sigstore/rekor/pull/1043) * Fix harness tests @ main (https://github.com/sigstore/rekor/pull/1038) * Fix rekor-cli backwards incompatibility & run harness tests against HEAD (https://github.com/sigstore/rekor/pull/1030) * fix: use entry uuid uniformly (https://github.com/sigstore/rekor/pull/1012) ## Others * Fetch all tags in harness tests (https://github.com/sigstore/rekor/pull/1039) ## Contributors * Asra Ali (@asraa) * Bob Callaway (@bobcallaway) * Carlos Tadeu Panato Junior (@cpanato) * Ceridwen Driskill (@cdris) * Hayden Blauzvern (@haydentherapper) * Kenny Leung (@k4leung4) * Mikhail Swift (@mikhailswift) * Parth Patel (@pxp928) * Priya Wadhwa (@priyawadhwa) # v0.11.0 ## Enhancements * add support for `intersection` & `union` in search operations (https://github.com/sigstore/rekor/pull/968) * Allow sharding config to be written in yaml or json (https://github.com/sigstore/rekor/pull/974) * update field documentation on publicKey for hashedrekord (https://github.com/sigstore/rekor/pull/969) * compute payload and envelope hashes upon validating intoto proposed entries (https://github.com/sigstore/rekor/pull/967) * Add prometheus summary to track metric latency (https://github.com/sigstore/rekor/pull/966) * Add harness test for getting all entries by UUID and EntryID (https://github.com/sigstore/rekor/pull/957) * Persist and check attestations across harness tests (https://github.com/sigstore/rekor/pull/952) * Add rekor harness tests for adding and getting entries from previous versions (https://github.com/sigstore/rekor/pull/945) ## Bug Fixes * fix: make rekor verify work with sharded uuids (https://github.com/sigstore/rekor/pull/970) * fix incorrect schema id for cose type (https://github.com/sigstore/rekor/pull/979) * fix nil-pointer error when artifact-hash is passed without artifact (https://github.com/sigstore/rekor/pull/965) * change default value for rekor_server.hostname to server's hostname (https://github.com/sigstore/rekor/pull/963) * api: fix inclusion proof verification flake (https://github.com/sigstore/rekor/pull/956) ## Others * Update sccorecard-action to v2:alpha (https://github.com/sigstore/rekor/pull/987) * add changelog for v0.11.0 release (https://github.com/sigstore/rekor/pull/982) * remove trailing slash on directories (https://github.com/sigstore/rekor/pull/984) * update builder and cosign images (https://github.com/sigstore/rekor/pull/981) * Bump github.com/go-openapi/spec from 0.20.6 to 0.20.7 (https://github.com/sigstore/rekor/pull/976) * Bump github.com/go-openapi/loads from 0.21.1 to 0.21.2 (https://github.com/sigstore/rekor/pull/977) * Bump github.com/go-openapi/swag from 0.22.0 to 0.22.1 (https://github.com/sigstore/rekor/pull/978) * Bump sigstore/cosign-installer from 2.5.0 to 2.5.1 (https://github.com/sigstore/rekor/pull/975) * Bump github.com/mediocregopher/radix/v4 from 4.1.0 to 4.1.1 (https://github.com/sigstore/rekor/pull/972) * Bump actions/github-script from 6.1.0 to 6.1.1 (https://github.com/sigstore/rekor/pull/971) * Bump github.com/go-openapi/errors from 0.20.2 to 0.20.3 (https://github.com/sigstore/rekor/pull/964) * Bump gopkg.in/ini.v1 from 1.66.6 to 1.67.0 (https://github.com/sigstore/rekor/pull/960) * Bump go.uber.org/zap from 1.21.0 to 1.22.0 (https://github.com/sigstore/rekor/pull/961) * Bump github.com/prometheus/client_golang from 1.12.2 to 1.13.0 (https://github.com/sigstore/rekor/pull/959) * Bump github.com/go-openapi/swag from 0.21.1 to 0.22.0 (https://github.com/sigstore/rekor/pull/958) * Bump github/codeql-action from 2.1.17 to 2.1.18 (https://github.com/sigstore/rekor/pull/955) * Bump golang from 1.18.4 to 1.18.5 (https://github.com/sigstore/rekor/pull/950) * Bump golang from `6e10f44` to `8a62670` (https://github.com/sigstore/rekor/pull/948) * Bump google.golang.org/protobuf from 1.28.0 to 1.28.1 (https://github.com/sigstore/rekor/pull/947) ## Contributors * Asra Ali (@asraa) * Azeem Shaikh (@azeemshaikh38) * Bob Callaway (@bobcallaway) * Carlos Tadeu Panato Junior (@cpanato) * Samsondeen (@dsa0x) * Priya Wadhwa (@priyawadhwa) # v0.10.0 ** Note: Rekor will not send `application/yaml` responses anymore only `application/json` responses ## Enhancements * Drop application/yaml content type (https://github.com/sigstore/rekor/pull/933) * Return 404 if entry isn't found in log (https://github.com/sigstore/rekor/pull/915) * reuse dsse signature wrappers instead of having a copy (https://github.com/sigstore/rekor/pull/912) ## Others * update go mod in hack/tools to go1.18 (https://github.com/sigstore/rekor/pull/935) * Enable Scorecard badge (https://github.com/sigstore/rekor/pull/941) * Add rekor test harness to presubmit tests (https://github.com/sigstore/rekor/pull/921) * Bump imjasonh/setup-ko from 0.4 to 0.5 (https://github.com/sigstore/rekor/pull/940) * update go builder and cosign image (https://github.com/sigstore/rekor/pull/934) * Bump sigs.k8s.io/release-utils from 0.7.2 to 0.7.3 (https://github.com/sigstore/rekor/pull/937) * Bump github.com/google/trillian from 1.4.1 to 1.4.2 in /hack/tools (https://github.com/sigstore/rekor/pull/939) * Bump sigstore/cosign-installer from 2.4.1 to 2.5.0 (https://github.com/sigstore/rekor/pull/936) * Bump github.com/go-openapi/strfmt from 0.21.2 to 0.21.3 (https://github.com/sigstore/rekor/pull/930) * Update cosign image in validate-release job (https://github.com/sigstore/rekor/pull/931) * Bump sigs.k8s.io/release-utils from 0.7.1 to 0.7.2 (https://github.com/sigstore/rekor/pull/927) * Bump github.com/veraison/go-cose from 1.0.0-alpha.1 to 1.0.0-rc.1 (https://github.com/sigstore/rekor/pull/928) * Bump actions/dependency-review-action from 2.0.2 to 2.0.4 (https://github.com/sigstore/rekor/pull/925) * Bump github/codeql-action from 2.1.15 to 2.1.16 (https://github.com/sigstore/rekor/pull/924) * Bump golang from 1.18.3 to 1.18.4 (https://github.com/sigstore/rekor/pull/919) * Bump google.golang.org/grpc from 1.47.0 to 1.48.0 (https://github.com/sigstore/rekor/pull/920) * Bump actions/setup-go from 3.2.0 to 3.2.1 (https://github.com/sigstore/rekor/pull/916) * Updates on the release job/makefile cleanup (https://github.com/sigstore/rekor/pull/914) * add changelog for v0.9.1 (https://github.com/sigstore/rekor/pull/911) ## Contributors * Azeem Shaikh (@azeemshaikh38) * Bob Callaway (@bobcallaway) * Carlos Tadeu Panato Junior (@cpanato) * Hayden Blauzvern (@haydentherapper) * Priya Wadhwa (@priyawadhwa) # v0.9.1 ## Enhancements * Optimize lookup of attestation from storage layer (https://github.com/sigstore/rekor/pull/909) * feat: add subject URIs to index for x509 certificates (https://github.com/sigstore/rekor/pull/897) * ensure log messages have requestID where possible (https://github.com/sigstore/rekor/pull/907) * Check inactive shards for UUID for /retrieve endpoint (https://github.com/sigstore/rekor/pull/905) ## Bug Fixes * Fix bug where /retrieve endpoint returns wrong logIndex across shards (https://github.com/sigstore/rekor/pull/908) * fix: sql syntax in dbcreate script (https://github.com/sigstore/rekor/pull/903) ## Others * cleanup makefile with generated code; cleanup unused files (https://github.com/sigstore/rekor/pull/910) * Bump github.com/theupdateframework/go-tuf from 0.3.0 to 0.3.1 (https://github.com/sigstore/rekor/pull/906) * Pin release-utils to v0.7.1 (https://github.com/sigstore/rekor/pull/904) * Bump sigstore/cosign-installer from 2.4.0 to 2.4.1 (https://github.com/sigstore/rekor/pull/898) ## Contributors * Asra Ali (@asraa) * Bob Callaway (@bobcallaway) * Priya Wadhwa (@priyawadhwa) * Romain Aviolat (@xens) * Sascha Grunert (@saschagrunert) # v0.9.0 ## Enhancements * Add COSE support to Rekor (https://github.com/sigstore/rekor/pull/867) ## Bug Fixes * Resolve virtual log index when calling /api/v1/log/entries/retrieve endpoint (https://github.com/sigstore/rekor/pull/894) * Fix intoto index keys (https://github.com/sigstore/rekor/pull/889) * ensure fallback logic executes if attestation key is empty when fetching attestation (https://github.com/sigstore/rekor/pull/878) ## Others * Bump github/codeql-action from 2.1.14 to 2.1.15 (https://github.com/sigstore/rekor/pull/893) * Bump ossf/scorecard-action from 1.1.1 to 1.1.2 (https://github.com/sigstore/rekor/pull/888) * Bump github/codeql-action from 2.1.13 to 2.1.14 (https://github.com/sigstore/rekor/pull/885) * add changelog for v0.8.2 (https://github.com/sigstore/rekor/pull/882) * Bump github/codeql-action from 2.1.12 to 2.1.13 (https://github.com/sigstore/rekor/pull/880) * Bump github.com/spf13/cobra from 1.4.0 to 1.5.0 (https://github.com/sigstore/rekor/pull/881) ## Contributors * Bob Callaway (@bobcallaway) * Carlos Tadeu Panato Junior (@cpanato) * Fredrik Skogman (@kommendorkapten) * Priya Wadhwa (@priyawadhwa) # v0.8.2 ## Bug Fixes * ensure fallback logic executes if attestation key is empty when fetching attestation (https://github.com/sigstore/rekor/pull/878) ## Others * Bump github/codeql-action from 2.1.12 to 2.1.13 (https://github.com/sigstore/rekor/pull/880) * Bump github.com/spf13/cobra from 1.4.0 to 1.5.0 (https://github.com/sigstore/rekor/pull/881) * collect docker-compose logs if sharding tests fail, also trim IDs (https://github.com/sigstore/rekor/pull/869) ## Contributors * Bob Callaway (@bobcallaway) # v0.8.1 ## Bug Fixes * Allow an expired certificate chain to be uploaded and verified (https://github.com/sigstore/rekor/pull/873) * Fix indexing bug for intoto attestations (https://github.com/sigstore/rekor/pull/870) ## Others * Bump actions/dependency-review-action from 1.0.2 to 2 (https://github.com/sigstore/rekor/pull/871) * Bump sigstore/cosign-installer from 2.3.0 to 2.4.0 (https://github.com/sigstore/rekor/pull/868) * add changelog for v0.8.0 (https://github.com/sigstore/rekor/pull/866) ## Contributors * Carlos Tadeu Panato Junior (@cpanato) * Hayden Blauzvern (@haydentherapper) * Priya Wadhwa (@priyawadhwa) # v0.8.0 ## Enhancements * Print total tree size, including inactive shards in `rekor-cli loginfo` (https://github.com/sigstore/rekor/pull/864) * Allow retrieving entryIDs or UUIDs via `/api/v1/log/entries/retrieve` endpoint (https://github.com/sigstore/rekor/pull/859) * Improve error message when using ED25519 with HashedRekord type (https://github.com/sigstore/rekor/pull/862) ## Others * Bump github.com/spf13/viper from 1.11.0 to 1.12.0 (https://github.com/sigstore/rekor/pull/844) * Bump github.com/go-openapi/validate from 0.21.0 to 0.22.0 (https://github.com/sigstore/rekor/pull/863) * update go.mod to go1.17 (https://github.com/sigstore/rekor/pull/861) * update cross-builder image to use go1.17.11 and dockerfile base image (https://github.com/sigstore/rekor/pull/860) * Bump github/codeql-action from 2.1.11 to 2.1.12 (https://github.com/sigstore/rekor/pull/858) * Bump ossf/scorecard-action from 1.1.0 to 1.1.1 (https://github.com/sigstore/rekor/pull/857) * Bump google.golang.org/grpc from 1.46.2 to 1.47.0 (https://github.com/sigstore/rekor/pull/852) * Bump github.com/secure-systems-lab/go-securesystemslib (https://github.com/sigstore/rekor/pull/853) * Configure rekor server in e2e tests via env variable (https://github.com/sigstore/rekor/pull/850) * Bump gopkg.in/ini.v1 from 1.66.5 to 1.66.6 (https://github.com/sigstore/rekor/pull/848) * Update go-tuf and sigstore/sigstore to non-vulnerable go-tuf version. (https://github.com/sigstore/rekor/pull/847) * Bump gopkg.in/ini.v1 from 1.66.4 to 1.66.5 (https://github.com/sigstore/rekor/pull/846) ## Contributors * Carlos Tadeu Panato Junior (@cpanato) * dhaus67 (@dhaus67) * Hayden Blauzvern (@haydentherapper) * Priya Wadhwa (@priyawadhwa) # v0.7.0 **Breaking Change**: Removed timestamping authority API. This is a breaking API change. If you are relying on the timestamping authority to issue signed timestamps, create signed timestamps using either OpenSSL or a service such as FreeTSA. ## Enhancements * Remove timestamping authority (https://github.com/sigstore/rekor/pull/813) * Limit the number of certificates parsed in a chain (https://github.com/sigstore/rekor/pull/823) * Retrieve shard tree length if it isn't provided in the config (https://github.com/sigstore/rekor/pull/810) * Don't try to index on hash for intoto obj if one isn't available (https://github.com/sigstore/rekor/pull/800) * intoto: add index on materials digest of slsa provenance (https://github.com/sigstore/rekor/pull/793) * remove URL fetch of keys/artifacts server-side (https://github.com/sigstore/rekor/pull/735) ## Others * all: remove dependency on deprecated github.com/pkg/errors (https://github.com/sigstore/rekor/pull/834) * Add back owners for rfc3161 package type (https://github.com/sigstore/rekor/pull/833) * Bump google-github-actions/auth from 0.7.2 to 0.7.3 (https://github.com/sigstore/rekor/pull/832) * Bump github/codeql-action from 2.1.10 to 2.1.11 (https://github.com/sigstore/rekor/pull/829) * Bump google-github-actions/auth from 0.7.1 to 0.7.2 (https://github.com/sigstore/rekor/pull/830) * Bump google.golang.org/grpc from 1.46.0 to 1.46.2 (https://github.com/sigstore/rekor/pull/828) * Bump actions/dependency-review-action (https://github.com/sigstore/rekor/pull/825) * Bump actions/github-script from 6.0.0 to 6.1.0 (https://github.com/sigstore/rekor/pull/826) * Bump github.com/prometheus/client_golang from 1.12.1 to 1.12.2 (https://github.com/sigstore/rekor/pull/827) * update go to 1.17.10 in the dockerfile (https://github.com/sigstore/rekor/pull/819) * Bump github.com/google/trillian from 1.4.0 to 1.4.1 in /hack/tools (https://github.com/sigstore/rekor/pull/818) * Bump github.com/google/trillian from 1.4.0 to 1.4.1 (https://github.com/sigstore/rekor/pull/817) * Bump actions/setup-go from 3.0.0 to 3.1.0 (https://github.com/sigstore/rekor/pull/822) * Bump github/codeql-action (https://github.com/sigstore/rekor/pull/821) * update release builder images to use go 1.17.10 and cosign image to 1.18.0 (https://github.com/sigstore/rekor/pull/820) * Bump golangci/golangci-lint-action from 3.1.0 to 3.2.0 (https://github.com/sigstore/rekor/pull/815) * Bump github/codeql-action from 2.1.9 to 2.1.10 (https://github.com/sigstore/rekor/pull/816) * Bump github.com/go-openapi/runtime from 0.24.0 to 0.24.1 (https://github.com/sigstore/rekor/pull/811) * Bump github.com/go-openapi/spec from 0.20.5 to 0.20.6 (https://github.com/sigstore/rekor/pull/802) * Move trillian/merkly to transparency-dev (https://github.com/sigstore/rekor/pull/807) * Bump github.com/go-playground/validator/v10 from 10.10.1 to 10.11.0 (https://github.com/sigstore/rekor/pull/803) * chore(deps): Included dependency review (https://github.com/sigstore/rekor/pull/788) * Bump github.com/go-openapi/runtime from 0.23.3 to 0.24.0 (https://github.com/sigstore/rekor/pull/799) * Bump github.com/google/go-cmp from 0.5.7 to 0.5.8 (https://github.com/sigstore/rekor/pull/794) * Bump sigstore/cosign-installer from 2.2.1 to 2.3.0 (https://github.com/sigstore/rekor/pull/795) * Bump github/codeql-action from 2.1.8 to 2.1.9 (https://github.com/sigstore/rekor/pull/796) * Bump google.golang.org/grpc from 1.45.0 to 1.46.0 (https://github.com/sigstore/rekor/pull/791) * Bump google-github-actions/auth from 0.7.0 to 0.7.1 (https://github.com/sigstore/rekor/pull/790) * Bump actions/checkout from 3.0.1 to 3.0.2 (https://github.com/sigstore/rekor/pull/786) * Bump codecov/codecov-action from 3.0.0 to 3.1.0 (https://github.com/sigstore/rekor/pull/785) * Bump github.com/mitchellh/mapstructure from 1.4.3 to 1.5.0 (https://github.com/sigstore/rekor/pull/782) * Bump github.com/mediocregopher/radix/v4 from 4.0.0 to 4.1.0 (https://github.com/sigstore/rekor/pull/781) * Bump anchore/sbom-action from 0.10.0 to 0.11.0 (https://github.com/sigstore/rekor/pull/779) * Bump actions/checkout from 3.0.0 to 3.0.1 (https://github.com/sigstore/rekor/pull/778) * Bump github.com/spf13/viper from 1.10.1 to 1.11.0 (https://github.com/sigstore/rekor/pull/777) * Bump sigstore/cosign-installer from 2.2.0 to 2.2.1 (https://github.com/sigstore/rekor/pull/776) ## Contributors * Asra Ali (@asraa) * Bob Callaway (@bobcallaway) * Carlos Tadeu Panato Junior (@cpanato) * Hayden Blauzvern (@haydentherapper) * Koichi Shiraishi (@zchee) * Naveen Srinivasan (@naveensrinivasan) * Priya Wadhwa (@priyawadhwa) # v0.6.0 Notice: The server side remote fetching of resources will be removed in the next release ## Enhancements * Create EntryID for new artifacts and return EntryID to user (https://github.com/sigstore/rekor/pull/623) * Add search through inactive shards for GET by UUID (https://github.com/sigstore/rekor/pull/750) * Add in configmap to release for sharding config (https://github.com/sigstore/rekor/pull/766) * set p.Block after parsing; other cleanup (https://github.com/sigstore/rekor/pull/759) * Add index to hashed intoto envelope (https://github.com/sigstore/rekor/pull/761) * Add the SHA256 digest of the intoto payload into the rekor entry (https://github.com/sigstore/rekor/pull/764) * Add support for providing certificate chain for X509 signature types (https://github.com/sigstore/rekor/pull/747) * Specify public key for inactive shards in shard config (https://github.com/sigstore/rekor/pull/746) * Use active tree on server startup (https://github.com/sigstore/rekor/pull/727) * Require tlog_id when inactive shard config file is passed in (https://github.com/sigstore/rekor/pull/739) * Replace `trillian_log_server.log_id_ranges` flag with a config file (https://github.com/sigstore/rekor/pull/742) * Update loginfo API endpoint to return information about inactive shards (https://github.com/sigstore/rekor/pull/738) * Refactor rekor-cli loginfo (https://github.com/sigstore/rekor/pull/734) * Get log proofs by Tree ID (https://github.com/sigstore/rekor/pull/733) * Return virtual index when creating and getting a log entry (https://github.com/sigstore/rekor/pull/725) * Clearer logging for createAndInitTree (https://github.com/sigstore/rekor/pull/724) * Change TreeID to be of type `string` instead of `int64` (https://github.com/sigstore/rekor/pull/712) * Switch to using the swag library for pointer manipulation. (https://github.com/sigstore/rekor/pull/719) * Make the loginfo command a bit more future/backwards proof. (https://github.com/sigstore/rekor/pull/718) * Use logRangesFlag in API, route reads based on TreeID (https://github.com/sigstore/rekor/pull/671) * Set rekor-cli User-Agent header on requests (https://github.com/sigstore/rekor/pull/684) * create namespace for rekor config in yaml. (https://github.com/sigstore/rekor/pull/680) * add securityContext to deployment. (https://github.com/sigstore/rekor/pull/678) * Move k8s objects out of the default namespace (https://github.com/sigstore/rekor/pull/674) ## Bug Fixes * Fix search without sha prefix (https://github.com/sigstore/rekor/pull/767) * Fix link in types README (https://github.com/sigstore/rekor/pull/765) * fix typo in filename (https://github.com/sigstore/rekor/pull/758) * fix build date format for version command (https://github.com/sigstore/rekor/pull/745) * fix merge conflict (https://github.com/sigstore/rekor/pull/720) ## Documentation * Add documentation about Alpine type (https://github.com/sigstore/rekor/pull/697) * update security process link (https://github.com/sigstore/rekor/pull/685) * Add intoto type documentation (https://github.com/sigstore/rekor/pull/679) * Add docs about API stabilitly and deprecation policy (https://github.com/sigstore/rekor/pull/661) ## Others * Bump github.com/go-openapi/spec from 0.20.4 to 0.20.5 (https://github.com/sigstore/rekor/pull/768) * Bump anchore/sbom-action from 0.9.0 to 0.10.0 (https://github.com/sigstore/rekor/pull/763) * Bump github/codeql-action from 2.1.7 to 2.1.8 (https://github.com/sigstore/rekor/pull/762) * Update release jobs and trillian images (https://github.com/sigstore/rekor/pull/756) * Bump sigstore/cosign-installer from 2.1.0 to 2.2.0 (https://github.com/sigstore/rekor/pull/757) * Bump anchore/sbom-action from 0.8.0 to 0.9.0 (https://github.com/sigstore/rekor/pull/754) * Bump codecov/codecov-action from 2.1.0 to 3 (https://github.com/sigstore/rekor/pull/753) * Bump github/codeql-action from 2.1.6 to 2.1.7 (https://github.com/sigstore/rekor/pull/752) * Bump google-github-actions/auth from 0.6.0 to 0.7.0 (https://github.com/sigstore/rekor/pull/751) * Bump github/codeql-action from 1.1.5 to 2.1.6 (https://github.com/sigstore/rekor/pull/748) * Bump anchore/sbom-action from 0.7.0 to 0.8.0 (https://github.com/sigstore/rekor/pull/743) * Bump google.golang.org/protobuf from 1.27.1 to 1.28.0 (https://github.com/sigstore/rekor/pull/744) * Bump github.com/go-openapi/runtime from 0.23.2 to 0.23.3 (https://github.com/sigstore/rekor/pull/740) * Bump github/codeql-action from 1.1.4 to 1.1.5 (https://github.com/sigstore/rekor/pull/736) * Use reusuable release workflow in sigstore/sigstore (https://github.com/sigstore/rekor/pull/729) * Fix copy/paste mistake in repo name. (https://github.com/sigstore/rekor/pull/730) * Bump github.com/spf13/cobra from 1.3.0 to 1.4.0 (https://github.com/sigstore/rekor/pull/728) * Bump golang from `ca70980` to `c7c9458` (https://github.com/sigstore/rekor/pull/722) * Bump google.golang.org/grpc from 1.44.0 to 1.45.0 (https://github.com/sigstore/rekor/pull/723) * Add sharding e2e test to Github Actions (https://github.com/sigstore/rekor/pull/714) * Bump github.com/go-playground/validator/v10 from 10.10.0 to 10.10.1 (https://github.com/sigstore/rekor/pull/717) * Bump github/codeql-action from 1.1.3 to 1.1.4 (https://github.com/sigstore/rekor/pull/716) * Add trillian container to existing release. (https://github.com/sigstore/rekor/pull/715) * Bump golang from `0168c35` to `ca70980` (https://github.com/sigstore/rekor/pull/707) * Mirror signed release images from GCR to GHCR as part of release (https://github.com/sigstore/rekor/pull/701) * Bump anchore/sbom-action from 0.6.0 to 0.7.0 (https://github.com/sigstore/rekor/pull/709) * Bump github.com/go-openapi/runtime from 0.23.1 to 0.23.2 (https://github.com/sigstore/rekor/pull/710) * Bump sigstore/cosign-installer from 2.0.1 to 2.1.0 (https://github.com/sigstore/rekor/pull/708) * Generate release yaml artifact. (https://github.com/sigstore/rekor/pull/702) * Bump actions/upload-artifact from 2.3.1 to 3 (https://github.com/sigstore/rekor/pull/704) * Go update to 1.17.8 and cosign to 1.6.0 (https://github.com/sigstore/rekor/pull/705) * Consistent parenthesis use in Makefile (https://github.com/sigstore/rekor/pull/700) * add code coverage to pull request. (https://github.com/sigstore/rekor/pull/676) * Bump actions/checkout from 2.4.0 to 3 (https://github.com/sigstore/rekor/pull/698) * Bump goreleaser/goreleaser-action from 2.9.0 to 2.9.1 (https://github.com/sigstore/rekor/pull/696) * Bump actions/setup-go from 2.2.0 to 3.0.0 (https://github.com/sigstore/rekor/pull/694) * Bump github.com/secure-systems-lab/go-securesystemslib (https://github.com/sigstore/rekor/pull/695) * Bump golangci/golangci-lint-action from 3.0.0 to 3.1.0 (https://github.com/sigstore/rekor/pull/693) * Bump goreleaser/goreleaser-action from 2.8.1 to 2.9.0 (https://github.com/sigstore/rekor/pull/692) * Bump golangci/golangci-lint-action from 2.5.2 to 3 (https://github.com/sigstore/rekor/pull/691) * Bump github/codeql-action from 1.1.2 to 1.1.3 (https://github.com/sigstore/rekor/pull/690) * Bump github.com/go-openapi/runtime from 0.23.0 to 0.23.1 (https://github.com/sigstore/rekor/pull/689) * explicitly set permissions for github actions (https://github.com/sigstore/rekor/pull/687) * Bump sigstore/cosign-installer from 2.0.0 to 2.0.1 (https://github.com/sigstore/rekor/pull/686) * Bump ossf/scorecard-action from 1.0.3 to 1.0.4 (https://github.com/sigstore/rekor/pull/683) * Bump github/codeql-action from 1.1.0 to 1.1.2 (https://github.com/sigstore/rekor/pull/682) * Bump actions/github-script from 5.1.0 to 6 (https://github.com/sigstore/rekor/pull/669) * Bump github/codeql-action from 1.0.32 to 1.1.0 (https://github.com/sigstore/rekor/pull/668) * update cross-build and dockerfile to use go 1.17.7 (https://github.com/sigstore/rekor/pull/666) * Bump gopkg.in/ini.v1 from 1.66.3 to 1.66.4 (https://github.com/sigstore/rekor/pull/664) * Bump actions/setup-go from 2.1.5 to 2.2.0 (https://github.com/sigstore/rekor/pull/663) * Bump golang from `301609e` to `fff998d` (https://github.com/sigstore/rekor/pull/662) * use upstream k8s version lib (https://github.com/sigstore/rekor/pull/657) * Bump github/codeql-action from 1.0.31 to 1.0.32 (https://github.com/sigstore/rekor/pull/659) * Bump go.uber.org/zap from 1.20.0 to 1.21.0 (https://github.com/sigstore/rekor/pull/660) * Bump github.com/go-openapi/strfmt from 0.21.1 to 0.21.2 (https://github.com/sigstore/rekor/pull/656) * Bump github.com/go-openapi/runtime from 0.22.0 to 0.23.0 (https://github.com/sigstore/rekor/pull/655) * Update the warning text for the GA release. (https://github.com/sigstore/rekor/pull/654) * attempting to fix codeowners file (https://github.com/sigstore/rekor/pull/653) * update release job (https://github.com/sigstore/rekor/pull/651) * Bump google-github-actions/auth from 0.5.0 to 0.6.0 (https://github.com/sigstore/rekor/pull/652) ## Contributors * Asra Ali (@asraa) * Bob Callaway (@bobcallaway) * Carlos Tadeu Panato Junior (@cpanato) * Dan Lorenc (@dlorenc) * Eddie Zaneski (@eddiezane) * Hayden Blauzvern (@haydentherapper) * John Speed Meyers * Kenny Leung (@k4leung4) * Lily Sturmann (@lkatalin) * Priya Wadhwa (@priyawadhwa) * Scott Nichols (@n3wscott) # v0.5.0 ## Highlights * Add Rekor logo to README (https://github.com/sigstore/rekor/pull/650) * update API calls to v5 (https://github.com/sigstore/rekor/pull/591) * Refactor helm type to remove intermediate state. (https://github.com/sigstore/rekor/pull/575) * Refactor the shard map parsing so we can pass it down into the API object. (https://github.com/sigstore/rekor/pull/564) * Refactor the alpine type to reduce intermediate state. (https://github.com/sigstore/rekor/pull/573) ## Enhancements * Add logic to GET artifacts via old or new UUID (https://github.com/sigstore/rekor/pull/587) * helpful error message for hashedrekord types (https://github.com/sigstore/rekor/pull/605) * Set Accept header in dynamic counter requests (https://github.com/sigstore/rekor/pull/594) * Add sharding package and update validators (https://github.com/sigstore/rekor/pull/583) * rekor-cli: show the url in case of error (https://github.com/sigstore/rekor/pull/581) * Enable parsing of incomplete minisign keys, to enable re-indexing. (https://github.com/sigstore/rekor/pull/567) * Cleanups on the TUF pluggable type. (https://github.com/sigstore/rekor/pull/563) * Refactor the RPM type to remove more intermediate state. (https://github.com/sigstore/rekor/pull/566) * Do some cleanups of the jar type to remove intermediate state. (https://github.com/sigstore/rekor/pull/561) ## Others * Update Makefile (https://github.com/sigstore/rekor/pull/621) * update version comments since dependabot doesn't do it (https://github.com/sigstore/rekor/pull/617) * Use workload identity provider instead of GitHub Secret for GCR access (https://github.com/sigstore/rekor/pull/600) * add OSSF scorecard action (https://github.com/sigstore/rekor/pull/599) * enable the sbom for rekor releases (https://github.com/sigstore/rekor/pull/586) * Point to the official website (instead of a 404) (https://github.com/sigstore/rekor/pull/580) * add milestone to closed prs (https://github.com/sigstore/rekor/pull/574) * Add a Makefile target for the "ko apply" step. (https://github.com/sigstore/rekor/pull/572) * types/README.md: Corrected documentation link (https://github.com/sigstore/rekor/pull/568) ## Dependencies Updates * Bump github.com/prometheus/client_golang from 1.12.0 to 1.12.1 (https://github.com/sigstore/rekor/pull/636) * Bump github.com/go-openapi/runtime from 0.21.1 to 0.22.0 (https://github.com/sigstore/rekor/pull/635) * Bump github.com/go-openapi/swag from 0.19.15 to 0.20.0 (https://github.com/sigstore/rekor/pull/634) * Bump golang from `f71d4ca` to `301609e` (https://github.com/sigstore/rekor/pull/627) * Bump golang from `0fa6504` to `f71d4ca` (https://github.com/sigstore/rekor/pull/624) * Bump google.golang.org/grpc from 1.43.0 to 1.44.0 (https://github.com/sigstore/rekor/pull/622) * Bump github/codeql-action from 1.0.29 to 1.0.30 (https://github.com/sigstore/rekor/pull/619) * Bump ossf/scorecard-action from 1.0.1 to 1.0.2 (https://github.com/sigstore/rekor/pull/618) * bump swagger and go mod tidy (https://github.com/sigstore/rekor/pull/616) * Bump github.com/go-openapi/runtime from 0.21.0 to 0.21.1 (https://github.com/sigstore/rekor/pull/614) * Bump github.com/go-openapi/errors from 0.20.1 to 0.20.2 (https://github.com/sigstore/rekor/pull/613) * Bump google-github-actions/auth from 0.4.4 to 0.5.0 (https://github.com/sigstore/rekor/pull/612) * Bump github/codeql-action from 1.0.28 to 1.0.29 (https://github.com/sigstore/rekor/pull/611) * Bump gopkg.in/ini.v1 from 1.66.2 to 1.66.3 (https://github.com/sigstore/rekor/pull/608) * Bump github.com/google/go-cmp from 0.5.6 to 0.5.7 (https://github.com/sigstore/rekor/pull/609) * Update github/codeql-action requirement to 8a4b243fbf9a03a93e93a71c1ec257347041f9c4 (https://github.com/sigstore/rekor/pull/606) * Bump github.com/prometheus/client_golang from 1.11.0 to 1.12.0 (https://github.com/sigstore/rekor/pull/607) * Bump ossf/scorecard-action from 0fe1afdc40f536c78e3dc69147b91b3ecec2cc8a to 1.0.1 (https://github.com/sigstore/rekor/pull/603) * Bump goreleaser/goreleaser-action from 2.8.0 to 2.8.1 (https://github.com/sigstore/rekor/pull/602) * Bump golang from `8c0269d` to `0fa6504` (https://github.com/sigstore/rekor/pull/597) * Pin dependencies in github action workflows and Dockerfile (https://github.com/sigstore/rekor/pull/595) * update release image to use go 1.17.6 (https://github.com/sigstore/rekor/pull/589) * Bump golang from 1.17.5 to 1.17.6 (https://github.com/sigstore/rekor/pull/588) * Bump go.uber.org/goleak from 1.1.11 to 1.1.12 (https://github.com/sigstore/rekor/pull/585) * Bump go.uber.org/zap from 1.19.1 to 1.20.0 (https://github.com/sigstore/rekor/pull/584) * Bump github.com/go-playground/validator/v10 from 10.9.0 to 10.10.0 (https://github.com/sigstore/rekor/pull/579) * Bump actions/github-script from 4 to 5 (https://github.com/sigstore/rekor/pull/577) ## Contributors * Asra Ali (@asraa) * Bob Callaway (@bobcallaway) * Carlos Tadeu Panato Junior (@cpanato) * Dan Lorenc (@dlorenc) * Jason Hall (@imjasonh) * Lily Sturmann (@lkatalin) * Morten Linderud (@Foxboron) * Nathan Smith (@nsmith5) * Sylvestre Ledru (@sylvestre) * Trishank Karthik Kuppusamy (@trishankatdatadog) # v0.4.0 ## Highlights * Adds hashed rekord type that can be used to upload signatures along with the hashed content signed (https://github.com/sigstore/rekor/pull/501) ## Enhancements * Update the schema to match that of Trillian repo. The map specific (https://github.com/sigstore/rekor/pull/528) * allow setting the user-agent string sent from the client (https://github.com/sigstore/rekor/pull/521) * update key usage for ts cert (https://github.com/sigstore/rekor/pull/504) * api/index/retrieve: allow searching on indicies with sha1 hashes (https://github.com/sigstore/rekor/pull/499) * Only include Attestation data if attestation storage enabled (https://github.com/sigstore/rekor/pull/494) * Fuzzing RequestFromRekor API (https://github.com/sigstore/rekor/pull/488) * Included pprof for profiling the application. (https://github.com/sigstore/rekor/pull/485) * refactor release and add signing (https://github.com/sigstore/rekor/pull/483) * More verbose error message for redis connection failure (https://github.com/sigstore/rekor/pull/479) (https://github.com/sigstore/rekor/pull/480) * Fixed modtime for reproducible goreleaser (https://github.com/sigstore/rekor/pull/473) * add goreleaser and cloudbuild for releases (https://github.com/sigstore/rekor/pull/443) * Add dynamic JS tree size counter (https://github.com/sigstore/rekor/pull/468) * check that entry UUID == leafHash of returned entry (https://github.com/sigstore/rekor/pull/469) * chore: upgrade cosign version (https://github.com/sigstore/rekor/pull/465) * Reproducible builds with trimpath (https://github.com/sigstore/rekor/pull/464) * correct links, add Table of Contents of sorts (https://github.com/sigstore/rekor/pull/449) * update go tuf for rsa key impl (https://github.com/sigstore/rekor/pull/446) * Canonicalize JSON before inserting into trillian (https://github.com/sigstore/rekor/pull/445) * Export search UUIDs field (https://github.com/sigstore/rekor/pull/438) * Add a flag to start specifying log index ranges for virtual indices. (https://github.com/sigstore/rekor/pull/435) * Cleanup some initialization/flag parsing in rekor-server. (https://github.com/sigstore/rekor/pull/433) * Drop 404 errors down to a warning. (https://github.com/sigstore/rekor/pull/426) * Cleanup the output of search (the text goes to stderr not stdout). (https://github.com/sigstore/rekor/pull/421) * remove extradata field from types (https://github.com/sigstore/rekor/pull/418) * Update usage of ./cmd/rekor-cli/ from `rekor` to `rekor-cli` (https://github.com/sigstore/rekor/pull/417) * Add TUF type (https://github.com/sigstore/rekor/pull/383) * Updates to INSTALLATION.md notes (https://github.com/sigstore/rekor/pull/415) * Update snippets to use `console` type for snippets (https://github.com/sigstore/rekor/pull/410) * version: add way to display a version when using go get or go install (https://github.com/sigstore/rekor/pull/405) * Use an in memory timestamping key (https://github.com/sigstore/rekor/pull/402) * Links are case sensitive (https://github.com/sigstore/rekor/pull/401) * Installation guide (https://github.com/sigstore/rekor/pull/400) * Add a SignedTimestampNote (https://github.com/sigstore/rekor/pull/397) * Provide instructions on verifying releases (https://github.com/sigstore/rekor/pull/399) * rekor-server: add html page when humans reach the server via the browser (https://github.com/sigstore/rekor/pull/394) * use go modules to track tools (https://github.com/sigstore/rekor/pull/395) ## Bug Fixes * bug: fix minisign prehashed entries (https://github.com/sigstore/rekor/pull/639) * fix timestamp addition and unmarshal (https://github.com/sigstore/rekor/pull/525) * Correct & parallelize tests (https://github.com/sigstore/rekor/pull/522) * Fix fuzz go.sum issue (https://github.com/sigstore/rekor/pull/509) * fix validation error (https://github.com/sigstore/rekor/pull/503) * Correct Helm index keys (https://github.com/sigstore/rekor/pull/474) * Fix a bug in x509 certificate handling. (https://github.com/sigstore/rekor/pull/461) * Fix a conflict from parallel dependabot merges. (https://github.com/sigstore/rekor/pull/456) * fix tuf metadata marshalling (https://github.com/sigstore/rekor/pull/447) * Switch DSSE provider to go-securesystemslib (https://github.com/sigstore/rekor/pull/442) * fix unmarshalling sth (https://github.com/sigstore/rekor/pull/409) * Fix port flag override (https://github.com/sigstore/rekor/pull/396) * makefile: small fix on the makefile for the rekor-server (https://github.com/sigstore/rekor/pull/393) ## Dependencies Updates * Bump github.com/spf13/viper from 1.9.0 to 1.10.0 (https://github.com/sigstore/rekor/pull/531) * Bump sigstore/cosign-installer from 1.3.1 to 1.4.1 (https://github.com/sigstore/rekor/pull/530) * Bump the DSSE signing library. (https://github.com/sigstore/rekor/pull/529) * Bump golang from 1.17.4 to 1.17.5 (https://github.com/sigstore/rekor/pull/527) * Bump golang from 1.17.3 to 1.17.4 (https://github.com/sigstore/rekor/pull/523) * Bump gopkg.in/ini.v1 from 1.66.0 to 1.66.2 (https://github.com/sigstore/rekor/pull/520) * Bump github.com/mitchellh/mapstructure from 1.4.2 to 1.4.3 (https://github.com/sigstore/rekor/pull/517) * Bump github.com/secure-systems-lab/go-securesystemslib (https://github.com/sigstore/rekor/pull/516) * Bump gopkg.in/ini.v1 from 1.64.0 to 1.66.0 (https://github.com/sigstore/rekor/pull/513) * Upgraded go-playground/validator module to v10 (https://github.com/sigstore/rekor/pull/507) * Bump gopkg.in/ini.v1 from 1.63.2 to 1.64.0 (https://github.com/sigstore/rekor/pull/495) * Bump github.com/go-openapi/strfmt from 0.21.0 to 0.21.1 (https://github.com/sigstore/rekor/pull/510) * Bump the trillian import to v1.4.0. (https://github.com/sigstore/rekor/pull/502) * Bump the trillian versions to v1.4.0 in our docker-compose setup. (https://github.com/sigstore/rekor/pull/500) * update go.mod for go-fuzz (https://github.com/sigstore/rekor/pull/496) * Bump sigstore/cosign-installer from 1.3.0 to 1.3.1 (https://github.com/sigstore/rekor/pull/491) * Bump golang from 1.17.2 to 1.17.3 (https://github.com/sigstore/rekor/pull/482) * Bump google.golang.org/grpc from 1.41.0 to 1.42.0 (https://github.com/sigstore/rekor/pull/478) * Bump actions/checkout from 2.3.5 to 2.4.0 (https://github.com/sigstore/rekor/pull/477) * Bump github.com/go-openapi/runtime from 0.20.0 to 0.21.0 (https://github.com/sigstore/rekor/pull/470) * bump go-swagger to v0.28.0 (https://github.com/sigstore/rekor/pull/463) * Bump github.com/in-toto/in-toto-golang from 0.3.2 to 0.3.3 (https://github.com/sigstore/rekor/pull/459) * Bump actions/checkout from 2.3.4 to 2.3.5 (https://github.com/sigstore/rekor/pull/458) * Bump github.com/mediocregopher/radix/v4 from 4.0.0-beta.1 to 4.0.0 (https://github.com/sigstore/rekor/pull/460) * Bump github.com/go-openapi/runtime from 0.19.31 to 0.20.0 (https://github.com/sigstore/rekor/pull/451) * Bump github.com/go-openapi/spec from 0.20.3 to 0.20.4 (https://github.com/sigstore/rekor/pull/454) * Bump github.com/go-openapi/validate from 0.20.2 to 0.20.3 (https://github.com/sigstore/rekor/pull/453) * Bump github.com/go-openapi/strfmt from 0.20.2 to 0.20.3 (https://github.com/sigstore/rekor/pull/452) * Bump github.com/go-openapi/loads from 0.20.2 to 0.20.3 (https://github.com/sigstore/rekor/pull/450) * Bump golang from 1.17.1 to 1.17.2 (https://github.com/sigstore/rekor/pull/448) * Bump google.golang.org/grpc from 1.40.0 to 1.41.0 (https://github.com/sigstore/rekor/pull/441) * Bump golang.org/x/mod from 0.5.0 to 0.5.1 (https://github.com/sigstore/rekor/pull/440) * Bump github.com/spf13/viper from 1.8.1 to 1.9.0 (https://github.com/sigstore/rekor/pull/439) * Bump gopkg.in/ini.v1 from 1.63.0 to 1.63.2 (https://github.com/sigstore/rekor/pull/437) * Bump github.com/mitchellh/mapstructure from 1.4.1 to 1.4.2 (https://github.com/sigstore/rekor/pull/436) * Bump gocloud to v0.24.0. (https://github.com/sigstore/rekor/pull/434) * Bump golang from 1.17.0 to 1.17.1 (https://github.com/sigstore/rekor/pull/432) * Bump go.uber.org/zap from 1.19.0 to 1.19.1 (https://github.com/sigstore/rekor/pull/431) * Bump gopkg.in/ini.v1 from 1.62.0 to 1.63.0 (https://github.com/sigstore/rekor/pull/429) * Bump github.com/go-openapi/runtime from 0.19.30 to 0.19.31 (https://github.com/sigstore/rekor/pull/425) * Bump github.com/go-openapi/errors from 0.20.0 to 0.20.1 (https://github.com/sigstore/rekor/pull/423) * Bump github.com/go-openapi/strfmt from 0.20.1 to 0.20.2 (https://github.com/sigstore/rekor/pull/422) * Bump golang from 1.16.7 to 1.17.0 (https://github.com/sigstore/rekor/pull/413) * Bump golang.org/x/mod from 0.4.2 to 0.5.0 (https://github.com/sigstore/rekor/pull/412) * Bump google.golang.org/grpc from 1.39.1 to 1.40.0 (https://github.com/sigstore/rekor/pull/411) * Bump github.com/go-openapi/runtime from 0.19.29 to 0.19.30 (https://github.com/sigstore/rekor/pull/408) * Bump go.uber.org/zap from 1.18.1 to 1.19.0 (https://github.com/sigstore/rekor/pull/407) * Bump golang from 1.16.6 to 1.16.7 (https://github.com/sigstore/rekor/pull/403) * Bump google.golang.org/grpc from 1.39.0 to 1.39.1 (https://github.com/sigstore/rekor/pull/404) ## Contributors * Aditya Sirish (@adityasaky) * Andrew Block (@sabre1041) * Asra Ali (@asraa) * Axel Simon (@axelsimon) * Batuhan Apaydın (@developer-guy) * Bob Callaway (@bobcallaway) * Carlos Panato (@cpanato) * Dan Lorenc (@dlorenc) * Dan Luhring (@luhring) * Harry Fallows (@harryfallows) * Hector Fernandez (@hectorj2f) * Jake Sanders (@dekkagaijin) * Jason Hall (@imjasonh) * Lily Sturmann (@lkatalin) * Luke Hinds (@lukehinds) * Marina Moore (@mnm678) * Mikhail Swift (@mikhailswift) * Naveen Srinivasan (@naveensrinivasan) * Robert James Hernandez (@sarcasticadmin) * Santiago Torres (@SantiagoTorres) * Tiziano Santoro (@tiziano88) * Trishank Karthik Kuppusamy (@trishankatdatadog) * Ville Aikas (@vaikas) * kpcyrd (@kpcyrd) rekor-1.3.5/CODEOWNERS000066400000000000000000000004361455727245600142610ustar00rootroot00000000000000* @sigstore/rekor-codeowners /.github/ @cpanato /release/ @cpanato /pkg/types/ @bobcallaway /pkg/types/rfc3161/ @asraa @loosebazooka # The CODEOWNERS are managed via a GitHub team, but the current list is (in alphabetical order): # asraa # bobcallaway # dlorenc # lukehinds rekor-1.3.5/CODE_OF_CONDUCT.md000066400000000000000000000062071455727245600154670ustar00rootroot00000000000000# Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ rekor-1.3.5/CONTRIBUTORS.md000066400000000000000000000103601455727245600151420ustar00rootroot00000000000000# Contributing When contributing to this repository, please first discuss the change you wish to make via an [issue](https://github.com/sigstore/rekor/issues). ## Pull Request Process 1. Create an [issue](https://github.com/sigstore/rekor/issues) outlining the fix or feature. 2. Fork the rekor repository to your own github account and clone it locally. 3. Hack on your changes. 4. Update the README.md with details of changes to any interface, this includes new environment variables, exposed ports, useful file locations, CLI parameters and new or changed configuration values. 5. Correctly format your commit message see [Commit Messages](#Commit Message Guidelines) below. 6. Ensure that CI passes, if it fails, fix the failures. 7. Every pull request requires a review from the [core rekor team](https://github.com/orgs/github.com/sigstore/teams/core-team) before merging. 8. If your pull request consists of more than one commit, please squash your commits as described in [Squash Commits](#Squash Commits) ## Commit Message Guidelines We follow the commit formatting recommendations found on [Chris Beams' How to Write a Git Commit Message article]((https://chris.beams.io/posts/git-commit/). Well formed commit messages not only help reviewers understand the nature of the Pull Request, but also assists the release process where commit messages are used to generate release notes. A good example of a commit message would be as follows: ``` Summarize changes in around 50 characters or less More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of the commit and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); various tools like `log`, `shortlog` and `rebase` can get confused if you run the two together. Explain the problem that this commit is solving. Focus on why you are making this change as opposed to how (the code explains that). Are there side effects or other unintuitive consequences of this change? Here's the place to explain them. Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here If you use an issue tracker, put references to them at the bottom, like this: Resolves: #123 See also: #456, #789 ``` Note the `Resolves #123` tag, this references the issue raised and allows us to ensure issues are associated and closed when a pull request is merged. Please refer to [the github help page on message types](https://help.github.com/articles/closing-issues-using-keywords/) for a complete list of issue references. ## Squash Commits Should your pull request consist of more than one commit (perhaps due to a change being requested during the review cycle), please perform a git squash once a reviewer has approved your pull request. A squash can be performed as follows. Let's say you have the following commits: initial commit second commit final commit Run the command below with the number set to the total commits you wish to squash (in our case 3 commits): git rebase -i HEAD~3 You default text editor will then open up and you will see the following:: pick eb36612 initial commit pick 9ac8968 second commit pick a760569 final commit # Rebase eb1429f..a760569 onto eb1429f (3 commands) We want to rebase on top of our first commit, so we change the other two commits to `squash`: pick eb36612 initial commit squash 9ac8968 second commit squash a760569 final commit After this, should you wish to update your commit message to better summarise all of your pull request, run: git commit --amend You will then need to force push (assuming your initial commit(s) were posted to github): git push origin your-branch --force Alternatively, a core member can squash your commits within Github. ## Code of Conduct Rekor adheres to and enforces the [Contributor Covenant](http://contributor-covenant.org/version/1/4/) Code of Conduct. Please take a moment to read the [CODE_OF_CONDUCT.md](https://github.com/sigstore/rekor/blob/master/CODE_OF_CONDUCT.md) document. rekor-1.3.5/COPYRIGHT.txt000066400000000000000000000010631455727245600147740ustar00rootroot00000000000000 Copyright 2021 The Sigstore Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. rekor-1.3.5/DEPRECATIONS.md000066400000000000000000000012571455727245600150720ustar00rootroot00000000000000# Deprecations This doc lists deprecated features in `rekor`. You can read more about Sigstore's deprecation policy [here](https://docs.sigstore.dev/api-stability)! | **Feature Being Deprecated** | **API Stability Level** | **Earliest Date of Removal** | |--------------------------------------------|-------------------------|------------------------------| | Specifying URLs inline in proposed entries | Experimental/Beta/GA | DD/MM/YY | | | | | | | | | rekor-1.3.5/Dockerfile000066400000000000000000000036461455727245600146660ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM golang:1.21.6@sha256:76aadd914a29a2ee7a6b0f3389bb2fdb87727291d688e1d972abe6c0fa6f2ee0 AS builder 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/ ARG SERVER_LDFLAGS RUN go build -ldflags "${SERVER_LDFLAGS}" ./cmd/rekor-server RUN CGO_ENABLED=0 go build -gcflags "all=-N -l" -ldflags "${SERVER_LDFLAGS}" -o rekor-server_debug ./cmd/rekor-server RUN go test -c -ldflags "${SERVER_LDFLAGS}" -cover -covermode=count -coverpkg=./... -o rekor-server_test ./cmd/rekor-server # Multi-Stage production build FROM golang:1.21.6@sha256:76aadd914a29a2ee7a6b0f3389bb2fdb87727291d688e1d972abe6c0fa6f2ee0 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"] # debug compile options & debugger FROM deploy as debug RUN go install github.com/go-delve/delve/cmd/dlv@v1.21.0 # overwrite server and include debugger COPY --from=builder /opt/app-root/src/rekor-server_debug /usr/local/bin/rekor-server FROM deploy as test # overwrite server with test build with code coverage COPY --from=builder /opt/app-root/src/rekor-server_test /usr/local/bin/rekor-server rekor-1.3.5/Dockerfile.pubsub-emulator000066400000000000000000000003241455727245600200010ustar00rootroot00000000000000# gcloud sdk for pubsub emulator with netcat added for the startup health check FROM google/cloud-sdk:461.0.0@sha256:4c91d8afa2013f71bdbd5e64f5c0a5b2fed6a6a7fe43c79e7bbc46ddff7c86a8 RUN apt-get install -y netcat rekor-1.3.5/FEATURES.md000066400000000000000000000005551455727245600144300ustar00rootroot00000000000000# Feature Stability This doc covers feature stability in `rekor` as described in the [API Stability Policy](https://docs.sigstore.dev/api-stability) for Sigstore. ## Experimental ## Beta * Rekor API, defined [here](https://github.com/sigstore/rekor/blob/main/openapi.yaml) * `rekor-cli` CLI tool * The `rekor/pkg/client` client library ## General Availability rekor-1.3.5/LICENSE000066400000000000000000000261361455727245600137000ustar00rootroot00000000000000 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. rekor-1.3.5/MAINTAINERS.md000066400000000000000000000002311455727245600147530ustar00rootroot00000000000000## Maintainers * [Luke Hinds](https://github.com/lukehinds) * [Dan Lorenc](https://github.com/dlorenc) * [Bob Callaway](https://github.com/bobcallaway) rekor-1.3.5/Makefile000066400000000000000000000173161455727245600143330ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .PHONY: all test clean clean-gen lint gosec ko ko-local cross-cli gocovmerge all: rekor-cli rekor-server include Makefile.swagger OPENAPIDEPS = openapi.yaml $(shell find pkg/types -iname "*.json") SRCS = $(shell find cmd -iname "*.go") $(shell find pkg -iname "*.go"|grep -v pkg/generated) pkg/generated/restapi/configure_rekor_server.go $(SWAGGER_GEN) TOOLS_DIR := hack/tools TOOLS_BIN_DIR := $(abspath $(TOOLS_DIR)/bin) BIN_DIR := $(abspath $(ROOT_DIR)/bin) FUZZ_DURATION ?= 10s PROJECT_ID ?= projectsigstore RUNTIME_IMAGE ?= gcr.io/distroless/static # Set version variables for LDFLAGS 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 KO_PREFIX ?= gcr.io/projectsigstore export KO_DOCKER_REPO=$(KO_PREFIX) REKOR_YAML ?= rekor-$(GIT_VERSION).yaml GHCR_PREFIX ?= ghcr.io/sigstore/rekor GOBIN ?= $(shell go env GOPATH)/bin # Binaries SWAGGER := $(TOOLS_BIN_DIR)/swagger GO-FUZZ-BUILD := $(TOOLS_BIN_DIR)/go-fuzz-build GOCOVMERGE := $(TOOLS_BIN_DIR)/gocovmerge REKOR_LDFLAGS=-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) CLI_LDFLAGS=$(REKOR_LDFLAGS) SERVER_LDFLAGS=$(REKOR_LDFLAGS) Makefile.swagger: $(SWAGGER) $(OPENAPIDEPS) $(SWAGGER) validate openapi.yaml $(SWAGGER) generate client -f openapi.yaml -q -r COPYRIGHT.txt -t pkg/generated --additional-initialism=TUF --additional-initialism=DSSE $(SWAGGER) generate server -f openapi.yaml -q -r COPYRIGHT.txt -t pkg/generated --exclude-main -A rekor_server --flag-strategy=pflag --default-produces application/json --additional-initialism=TUF --additional-initialism=DSSE @echo "# This file is generated after swagger runs as part of the build; do not edit!" > Makefile.swagger @echo "SWAGGER_GEN=`find pkg/generated/client pkg/generated/models pkg/generated/restapi -iname '*.go' | grep -v 'configure_rekor_server' | sort -d | tr '\n' ' ' | sed 's/ $$//'`" >> Makefile.swagger; lint: $(GOBIN)/golangci-lint run -v ./... gosec: $(GOBIN)/gosec ./... rekor-cli: $(SRCS) CGO_ENABLED=0 go build -trimpath -ldflags "$(CLI_LDFLAGS)" -o rekor-cli ./cmd/rekor-cli rekor-server: $(SRCS) CGO_ENABLED=0 go build -trimpath -ldflags "$(SERVER_LDFLAGS)" -o rekor-server ./cmd/rekor-server backfill-redis: $(SRCS) CGO_ENABLED=0 go build -trimpath -ldflags "$(SERVER_LDFLAGS)" -o backfill-redis ./cmd/backfill-redis test: go test ./... gocovmerge: $(GOCOVMERGE) clean: rm -rf dist rm -rf hack/tools/bin rm -rf rekor-cli rekor-server rm -f *fuzz.zip clean-gen: clean rm -rf $(SWAGGER_GEN) up: docker-compose -f docker-compose.yml build --build-arg SERVER_LDFLAGS="$(SERVER_LDFLAGS)" docker-compose -f docker-compose.yml up debug: docker-compose -f docker-compose.yml -f docker-compose.debug.yml build --build-arg SERVER_LDFLAGS="$(SERVER_LDFLAGS)" rekor-server-debug docker-compose -f docker-compose.yml -f docker-compose.debug.yml up rekor-server-debug ko: # rekor-server LDFLAGS="$(SERVER_LDFLAGS)" GIT_HASH=$(GIT_HASH) GIT_VERSION=$(GIT_VERSION) \ KO_DOCKER_REPO=$(KO_PREFIX)/rekor-server ko resolve --bare \ --platform=all --tags $(GIT_VERSION) --tags $(GIT_HASH) \ --image-refs rekorServerImagerefs --filename config/ > $(REKOR_YAML) # rekor-cli LDFLAGS="$(CLI_LDFLAGS)" GIT_HASH=$(GIT_HASH) GIT_VERSION=$(GIT_VERSION) \ ko publish --base-import-paths \ --platform=all --tags $(GIT_VERSION) --tags $(GIT_HASH) \ --image-refs rekorCliImagerefs github.com/sigstore/rekor/cmd/rekor-cli # backfill-redis LDFLAGS="$(SERVER_LDFLAGS)" GIT_HASH=$(GIT_HASH) GIT_VERSION=$(GIT_VERSION) \ ko publish --base-import-paths \ --platform=all --tags $(GIT_VERSION) --tags $(GIT_HASH) \ --image-refs bRedisImagerefs github.com/sigstore/rekor/cmd/backfill-redis deploy: LDFLAGS="$(SERVER_LDFLAGS)" GIT_HASH=$(GIT_HASH) GIT_VERSION=$(GIT_VERSION) ko apply -f config/ e2e: go test -c -tags=e2e ./tests go test -c -tags=e2e ./pkg/pki/x509 go test -c -tags=e2e ./pkg/pki/tuf go test -c -tags=e2e ./pkg/types/rekord .PHONY: sign-keyless-ci sign-keyless-ci: ko cosign sign --yes -a GIT_HASH=$(GIT_HASH) $(KO_DOCKER_REPO)/rekor-server:$(GIT_HASH) cosign sign --yes -a GIT_HASH=$(GIT_HASH) $(KO_DOCKER_REPO)/rekor-cli:$(GIT_HASH) .PHONY: ko-local ko-local: 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/cmd/rekor-server KO_DOCKER_REPO=ko.local LDFLAGS="$(CLI_LDFLAGS)" GIT_HASH=$(GIT_HASH) GIT_VERSION=$(GIT_VERSION) \ ko publish --base-import-paths \ --tags $(GIT_VERSION) --tags $(GIT_HASH) --image-refs cliImagerefs \ github.com/sigstore/rekor/cmd/rekor-cli 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 redisImagerefs \ github.com/sigstore/rekor/cmd/backfill-redis .PHONY: fuzz # This runs the fuzz tests for a short period of time to ensure they don't crash. fuzz: go test -fuzz FuzzCreateEntryIDFromParts -fuzztime $(FUZZ_DURATION) ./pkg/sharding go test -fuzz FuzzGetUUIDFromIDString -fuzztime $(FUZZ_DURATION) ./pkg/sharding go test -fuzz FuzzGetTreeIDFromIDString -fuzztime $(FUZZ_DURATION) ./pkg/sharding go test -fuzz FuzzPadToTreeIDLen -fuzztime $(FUZZ_DURATION) ./pkg/sharding go test -fuzz FuzzReturnEntryIDString -fuzztime $(FUZZ_DURATION) ./pkg/sharding go test -fuzz FuzzTreeID -fuzztime $(FUZZ_DURATION) ./pkg/sharding go test -fuzz FuzzValidateUUID -fuzztime $(FUZZ_DURATION) ./pkg/sharding go test -fuzz FuzzValidateTreeID -fuzztime $(FUZZ_DURATION) ./pkg/sharding go test -fuzz FuzzValidateEntryID -fuzztime $(FUZZ_DURATION) ./pkg/sharding ## -------------------------------------- ## Tooling Binaries ## -------------------------------------- $(GO-FUZZ-BUILD): $(TOOLS_DIR)/go.mod cd $(TOOLS_DIR); go build -trimpath -tags=tools -o $(TOOLS_BIN_DIR)/go-fuzz-build github.com/dvyukov/go-fuzz/go-fuzz-build $(SWAGGER): $(TOOLS_DIR)/go.mod cd $(TOOLS_DIR); go build -trimpath -tags=tools -o $(TOOLS_BIN_DIR)/swagger github.com/go-swagger/go-swagger/cmd/swagger $(GOCOVMERGE): $(TOOLS_DIR)/go.mod cd $(TOOLS_DIR); go build -trimpath -tags=tools -o $(TOOLS_BIN_DIR)/gocovmerge github.com/wadey/gocovmerge ################## # help ################## help: # Display help @awk -F ':|##' \ '/^[^\t].+?:.*?##/ (\ printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ )' $(MAKEFILE_LIST) | sort include release/release.mk rekor-1.3.5/Makefile.swagger000066400000000000000000000120321455727245600157570ustar00rootroot00000000000000# This file is generated after swagger runs as part of the build; do not edit! SWAGGER_GEN=pkg/generated/client/entries/create_log_entry_parameters.go pkg/generated/client/entries/create_log_entry_responses.go pkg/generated/client/entries/entries_client.go pkg/generated/client/entries/get_log_entry_by_index_parameters.go pkg/generated/client/entries/get_log_entry_by_index_responses.go pkg/generated/client/entries/get_log_entry_by_uuid_parameters.go pkg/generated/client/entries/get_log_entry_by_uuid_responses.go pkg/generated/client/entries/search_log_query_parameters.go pkg/generated/client/entries/search_log_query_responses.go pkg/generated/client/index/index_client.go pkg/generated/client/index/search_index_parameters.go pkg/generated/client/index/search_index_responses.go pkg/generated/client/pubkey/get_public_key_parameters.go pkg/generated/client/pubkey/get_public_key_responses.go pkg/generated/client/pubkey/pubkey_client.go pkg/generated/client/rekor_client.go pkg/generated/client/tlog/get_log_info_parameters.go pkg/generated/client/tlog/get_log_info_responses.go pkg/generated/client/tlog/get_log_proof_parameters.go pkg/generated/client/tlog/get_log_proof_responses.go pkg/generated/client/tlog/tlog_client.go pkg/generated/models/alpine.go pkg/generated/models/alpine_schema.go pkg/generated/models/alpine_v001_schema.go pkg/generated/models/consistency_proof.go pkg/generated/models/cose.go pkg/generated/models/cose_schema.go pkg/generated/models/cose_v001_schema.go pkg/generated/models/dsse.go pkg/generated/models/dsse_schema.go pkg/generated/models/dsse_v001_schema.go pkg/generated/models/error.go pkg/generated/models/hashedrekord.go pkg/generated/models/hashedrekord_schema.go pkg/generated/models/hashedrekord_v001_schema.go pkg/generated/models/helm.go pkg/generated/models/helm_schema.go pkg/generated/models/helm_v001_schema.go pkg/generated/models/inactive_shard_log_info.go pkg/generated/models/inclusion_proof.go pkg/generated/models/intoto.go pkg/generated/models/intoto_schema.go pkg/generated/models/intoto_v001_schema.go pkg/generated/models/intoto_v002_schema.go pkg/generated/models/jar.go pkg/generated/models/jar_schema.go pkg/generated/models/jar_v001_schema.go pkg/generated/models/log_entry.go pkg/generated/models/log_info.go pkg/generated/models/proposed_entry.go pkg/generated/models/rekord.go pkg/generated/models/rekord_schema.go pkg/generated/models/rekord_v001_schema.go pkg/generated/models/rfc3161.go pkg/generated/models/rfc3161_schema.go pkg/generated/models/rfc3161_v001_schema.go pkg/generated/models/rpm.go pkg/generated/models/rpm_schema.go pkg/generated/models/rpm_v001_schema.go pkg/generated/models/search_index.go pkg/generated/models/search_log_query.go pkg/generated/models/tuf.go pkg/generated/models/tuf_schema.go pkg/generated/models/tuf_v001_schema.go pkg/generated/restapi/doc.go pkg/generated/restapi/embedded_spec.go pkg/generated/restapi/operations/entries/create_log_entry.go pkg/generated/restapi/operations/entries/create_log_entry_parameters.go pkg/generated/restapi/operations/entries/create_log_entry_responses.go pkg/generated/restapi/operations/entries/create_log_entry_urlbuilder.go pkg/generated/restapi/operations/entries/get_log_entry_by_index.go pkg/generated/restapi/operations/entries/get_log_entry_by_index_parameters.go pkg/generated/restapi/operations/entries/get_log_entry_by_index_responses.go pkg/generated/restapi/operations/entries/get_log_entry_by_index_urlbuilder.go pkg/generated/restapi/operations/entries/get_log_entry_by_uuid.go pkg/generated/restapi/operations/entries/get_log_entry_by_uuid_parameters.go pkg/generated/restapi/operations/entries/get_log_entry_by_uuid_responses.go pkg/generated/restapi/operations/entries/get_log_entry_by_uuid_urlbuilder.go pkg/generated/restapi/operations/entries/search_log_query.go pkg/generated/restapi/operations/entries/search_log_query_parameters.go pkg/generated/restapi/operations/entries/search_log_query_responses.go pkg/generated/restapi/operations/entries/search_log_query_urlbuilder.go pkg/generated/restapi/operations/index/search_index.go pkg/generated/restapi/operations/index/search_index_parameters.go pkg/generated/restapi/operations/index/search_index_responses.go pkg/generated/restapi/operations/index/search_index_urlbuilder.go pkg/generated/restapi/operations/pubkey/get_public_key.go pkg/generated/restapi/operations/pubkey/get_public_key_parameters.go pkg/generated/restapi/operations/pubkey/get_public_key_responses.go pkg/generated/restapi/operations/pubkey/get_public_key_urlbuilder.go pkg/generated/restapi/operations/rekor_server_api.go pkg/generated/restapi/operations/tlog/get_log_info.go pkg/generated/restapi/operations/tlog/get_log_info_parameters.go pkg/generated/restapi/operations/tlog/get_log_info_responses.go pkg/generated/restapi/operations/tlog/get_log_info_urlbuilder.go pkg/generated/restapi/operations/tlog/get_log_proof.go pkg/generated/restapi/operations/tlog/get_log_proof_parameters.go pkg/generated/restapi/operations/tlog/get_log_proof_responses.go pkg/generated/restapi/operations/tlog/get_log_proof_urlbuilder.go pkg/generated/restapi/server.go rekor-1.3.5/OWNERS.md000066400000000000000000000002241455727245600142200ustar00rootroot00000000000000## Owners * [Luke Hinds](https://github.com/lukehinds) * [Dan Lorenc](https://github.com/dlorenc) * [Bob Callaway](https://github.com/bobcallaway) rekor-1.3.5/README.md000066400000000000000000000073031455727245600141450ustar00rootroot00000000000000[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/sigstore/rekor/badge)](https://api.securityscorecards.dev/projects/github.com/sigstore/rekor)

Rekor logo

# Rekor Rekór - Greek for “Record” Rekor's goals are to provide an immutable tamper resistant ledger of metadata generated within a software projects supply chain. Rekor will enable software maintainers and build systems to record signed metadata to an immutable record. Other parties can then query said metadata to enable them to make informed decisions on trust and non-repudiation of an object's lifecycle. For more details visit the [sigstore website](https://sigstore.dev). The Rekor project provides a restful API based server for validation and a transparency log for storage. A CLI application is available to make and verify entries, query the transparency log for inclusion proof, integrity verification of the transparency log or retrieval of entries by either public key or artifact. Rekor fulfils the signature transparency role of sigstore's software signing infrastructure. However, Rekor can be run on its own and is designed to be extensible to working with different manifest schemas and PKI tooling. [Official Documentation](https://docs.sigstore.dev/rekor/overview). ## Public Instance Rekor is officially Generally Available with a 1.0.0 release, and follows [semver rules](https://semver.org/) for API stability. This means production workloads can rely on the Rekor public instance, which has a 24/7 oncall rotation supporting it and offers a 99.5% availability SLO for the following API endpoints: * `/api/v1/log` * `/api/v1/log/publicKey` * `/api/v1/log/proof` * `/api/v1/log/entries` * `/api/v1/log/entries/retrieve` For uptime data on the Rekor public instance, see [https://status.sigstore.dev](https://status.sigstore.dev). More details on the public instance can be found at [docs.sigstore.dev](https://docs.sigstore.dev/rekor/public-instance). The attestation size limit for uploads to the public instance is [100KB](https://github.com/sigstore/rekor/blob/18c81d9f4def67c72f630c5406e26d5e568bc83b/cmd/rekor-server/app/root.go#L104). If you need to upload larger files, please run your own instance of Rekor. You can find instructions for doing so in the [installation](https://docs.sigstore.dev/rekor/overview#usage-and-installation) documentation. ### Installation Please see the [installation](https://docs.sigstore.dev/rekor/overview#usage-and-installation) page for details on how to install the rekor CLI and set up / run the rekor server ### Usage For examples of uploading signatures for all the supported types to rekor, see [the types documentation](types.md). ## Extensibility ### Custom schemas / manifests (rekor type) Rekor allows customized manifests (which term them as types), [type customization is outlined here](https://github.com/sigstore/rekor/tree/main/pkg/types). ### API If you're interested in integration with Rekor, we have an [OpenAPI swagger editor](https://sigstore.dev/swagger/) ## Security Should you discover any security issues, please refer to sigstore's [security process](https://github.com/sigstore/.github/blob/main/SECURITY.md) ## Contributions We welcome contributions from anyone and are especially interested to hear from users of Rekor. ## Additional Documentation In addition to this README file, this folder contains the additional documentation: - **oid-info.md**. Rekor OID values. - **types.md**. Information about how to sign and upload data in different pluggable types. rekor-1.3.5/cmd/000077500000000000000000000000001455727245600134265ustar00rootroot00000000000000rekor-1.3.5/cmd/backfill-redis/000077500000000000000000000000001455727245600163015ustar00rootroot00000000000000rekor-1.3.5/cmd/backfill-redis/main.go000066400000000000000000000175221455727245600175630ustar00rootroot00000000000000// Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /* backfill-redis is a script to populate the Redis index with entries from Rekor. This is sometimes necessary because Redis caching is best effort. If Redis returns an error, Rekor will not, and so sometimes we need to backfill missing entries into Redis for the search API. To run: go run cmd/backfill-redis/main.go --rekor-address
\ --hostname --port --concurrency \ --start --end [--dry-run] */ package main import ( "bytes" "context" "encoding/base64" "errors" "flag" "fmt" "log" "os" "os/signal" "syscall" "github.com/go-openapi/runtime" "github.com/redis/go-redis/v9" "golang.org/x/sync/errgroup" "sigs.k8s.io/release-utils/version" "github.com/sigstore/rekor/pkg/client" "github.com/sigstore/rekor/pkg/generated/client/entries" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" // these imports are to call the packages' init methods _ "github.com/sigstore/rekor/pkg/types/alpine/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/cose/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/dsse/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/helm/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/intoto/v0.0.2" _ "github.com/sigstore/rekor/pkg/types/jar/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/rekord/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/rfc3161/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/rpm/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/tuf/v0.0.1" ) var ( redisHostname = flag.String("hostname", "", "Hostname for Redis application") redisPort = flag.String("port", "", "Port to Redis application") redisPassword = flag.String("password", "", "Password for Redis authentication") startIndex = flag.Int("start", -1, "First index to backfill") endIndex = flag.Int("end", -1, "Last index to backfill") rekorAddress = flag.String("rekor-address", "", "Address for Rekor, e.g. https://rekor.sigstore.dev") versionFlag = flag.Bool("version", false, "Print the current version of Backfill Redis") concurrency = flag.Int("concurrency", 1, "Number of workers to use for backfill") dryRun = flag.Bool("dry-run", false, "Dry run - don't actually insert into Redis") ) func main() { flag.Parse() versionInfo := version.GetVersionInfo() if *versionFlag { fmt.Println(versionInfo.String()) os.Exit(0) } if *redisHostname == "" { log.Fatal("address must be set") } if *redisPort == "" { log.Fatal("port must be set") } if *startIndex == -1 { log.Fatal("start must be set to >=0") } if *endIndex == -1 { log.Fatal("end must be set to >=0") } if *rekorAddress == "" { log.Fatal("rekor-address must be set") } log.Printf("running backfill redis Version: %s GitCommit: %s BuildDate: %s", versionInfo.GitVersion, versionInfo.GitCommit, versionInfo.BuildDate) redisClient := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%s", *redisHostname, *redisPort), Password: *redisPassword, Network: "tcp", DB: 0, // default DB }) rekorClient, err := client.GetRekorClient(*rekorAddress) if err != nil { log.Fatalf("creating rekor client: %v", err) } ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) group, ctx := errgroup.WithContext(ctx) group.SetLimit(*concurrency) type result struct { index int parseErrs []error insertErrs []error } var resultChan = make(chan result) parseErrs := make([]int, 0) insertErrs := make([]int, 0) go func() { for r := range resultChan { if len(r.parseErrs) > 0 { parseErrs = append(parseErrs, r.index) } if len(r.insertErrs) > 0 { insertErrs = append(insertErrs, r.index) } } }() for i := *startIndex; i <= *endIndex; i++ { index := i // capture loop variable for closure group.Go(func() error { params := entries.NewGetLogEntryByIndexParamsWithContext(ctx) params.SetLogIndex(int64(index)) resp, err := rekorClient.Entries.GetLogEntryByIndex(params) if err != nil { // in case of sigterm, just return to exit gracefully if errors.Is(err, context.Canceled) { return nil } log.Fatalf("retrieving log uuid by index: %v", err) } var parseErrs []error var insertErrs []error for uuid, entry := range resp.Payload { // uuid is the global UUID - tree ID and entry UUID e, _, _, err := unmarshalEntryImpl(entry.Body.(string)) if err != nil { parseErrs = append(parseErrs, fmt.Errorf("error unmarshalling entry for %s: %v", uuid, err)) continue } keys, err := e.IndexKeys() if err != nil { parseErrs = append(parseErrs, fmt.Errorf("error building index keys for %s: %v", uuid, err)) continue } for _, key := range keys { // remove the key-value pair from the index in case it already exists if err := removeFromIndex(ctx, redisClient, key, uuid); err != nil { insertErrs = append(insertErrs, fmt.Errorf("error removing UUID %s with key %s: %v", uuid, key, err)) } if err := addToIndex(ctx, redisClient, key, uuid); err != nil { insertErrs = append(insertErrs, fmt.Errorf("error inserting UUID %s with key %s: %v", uuid, key, err)) } fmt.Printf("Uploaded Redis entry %s, index %d, key %s\n", uuid, index, key) } } if len(insertErrs) != 0 || len(parseErrs) != 0 { fmt.Printf("Errors with log index %d:\n", index) for _, e := range insertErrs { fmt.Println(e) } for _, e := range parseErrs { fmt.Println(e) } } else { fmt.Printf("Completed log index %d\n", index) } resultChan <- result{ index: index, parseErrs: parseErrs, insertErrs: insertErrs, } return nil }) } err = group.Wait() if err != nil { log.Fatalf("error running backfill: %v", err) } close(resultChan) fmt.Println("Backfill complete") if len(parseErrs) > 0 { fmt.Printf("Failed to parse %d entries: %v\n", len(parseErrs), parseErrs) } if len(insertErrs) > 0 { fmt.Printf("Failed to insert/remove %d entries: %v\n", len(insertErrs), insertErrs) } } // unmarshalEntryImpl decodes the base64-encoded entry to a specific entry type (types.EntryImpl). // Taken from Cosign func unmarshalEntryImpl(e string) (types.EntryImpl, string, string, error) { b, err := base64.StdEncoding.DecodeString(e) if err != nil { return nil, "", "", err } pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { return nil, "", "", err } entry, err := types.UnmarshalEntry(pe) if err != nil { return nil, "", "", err } return entry, pe.Kind(), entry.APIVersion(), nil } // removeFromIndex removes all occurrences of a value from a given key. This guards against // multiple invocations of backfilling creating duplicates. func removeFromIndex(ctx context.Context, redisClient *redis.Client, key, value string) error { if *dryRun { return nil } _, err := redisClient.LRem(ctx, key, 0, value).Result() return err } // addToIndex pushes a value onto a key of type list. func addToIndex(ctx context.Context, redisClient *redis.Client, key, value string) error { if *dryRun { return nil } _, err := redisClient.LPush(ctx, key, value).Result() return err } rekor-1.3.5/cmd/rekor-cli/000077500000000000000000000000001455727245600153155ustar00rootroot00000000000000rekor-1.3.5/cmd/rekor-cli/app/000077500000000000000000000000001455727245600160755ustar00rootroot00000000000000rekor-1.3.5/cmd/rekor-cli/app/format/000077500000000000000000000000001455727245600173655ustar00rootroot00000000000000rekor-1.3.5/cmd/rekor-cli/app/format/wrap.go000066400000000000000000000035701455727245600206720ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package format import ( "encoding/json" "fmt" rekor_pb "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "github.com/sigstore/rekor/pkg/log" tleutils "github.com/sigstore/rekor/pkg/tle" "github.com/spf13/cobra" "github.com/spf13/viper" ) type CobraCmd func(cmd *cobra.Command, args []string) type formatCmd func(args []string) (interface{}, error) func WrapCmd(f formatCmd) CobraCmd { return func(cmd *cobra.Command, args []string) { obj, err := f(args) if err != nil { log.CliLogger.Fatal(err) } // TODO: add flags to control output formatting (JSON, plaintext, etc.) format := viper.GetString("format") switch format { case "default": if s, ok := obj.(fmt.Stringer); ok { fmt.Print(s.String()) } else { fmt.Println(toJSON(s)) } case "json": fmt.Println(toJSON(obj)) case "tle": if tle, ok := obj.(*rekor_pb.TransparencyLogEntry); ok { json, err := tleutils.MarshalTLEToJSON(tle) if err != nil { log.CliLogger.Fatalf("error converting to transparency log entry: %v", err) } fmt.Println(string(json)) } else { log.CliLogger.Fatal("unable to print transparency log entry") } } } } func toJSON(i interface{}) string { b, err := json.Marshal(i) if err != nil { log.CliLogger.Fatal(err) } return string(b) } rekor-1.3.5/cmd/rekor-cli/app/get.go000066400000000000000000000160471455727245600172130ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "bytes" "context" "encoding/base64" "encoding/json" "errors" "fmt" "strconv" "time" "github.com/go-openapi/runtime" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/sigstore/rekor/cmd/rekor-cli/app/format" "github.com/sigstore/rekor/pkg/client" "github.com/sigstore/rekor/pkg/generated/client/entries" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/sharding" "github.com/sigstore/rekor/pkg/tle" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/verify" ) type getCmdOutput struct { Attestation string AttestationType string Body interface{} LogIndex int IntegratedTime int64 UUID string LogID string } func (g *getCmdOutput) String() string { s := fmt.Sprintf("LogID: %v\n", g.LogID) if g.Attestation != "" { s += fmt.Sprintf("Attestation: %s\n", g.Attestation) } s += fmt.Sprintf("Index: %d\n", g.LogIndex) dt := time.Unix(g.IntegratedTime, 0).UTC().Format(time.RFC3339) s += fmt.Sprintf("IntegratedTime: %s\n", dt) s += fmt.Sprintf("UUID: %s\n", g.UUID) var b bytes.Buffer e := json.NewEncoder(&b) e.SetIndent("", " ") _ = e.Encode(g.Body) s += fmt.Sprintf("Body: %s\n", b.Bytes()) return s } // getCmd represents the get command var getCmd = &cobra.Command{ Use: "get", Short: "Rekor get command", Long: `Get information regarding entries in the transparency log`, PreRun: func(cmd *cobra.Command, args []string) { // these are bound here so that they are not overwritten by other commands if err := viper.BindPFlags(cmd.Flags()); err != nil { log.CliLogger.Fatal("Error initializing cmd line args: ", err) } }, Run: format.WrapCmd(func(args []string) (interface{}, error) { ctx := context.Background() rekorClient, err := client.GetRekorClient(viper.GetString("rekor_server"), client.WithUserAgent(UserAgent()), client.WithRetryCount(viper.GetUint("retry")), client.WithLogger(log.CliLogger)) if err != nil { return nil, err } logIndex := viper.GetString("log-index") uuid := viper.GetString("uuid") if logIndex == "" && uuid == "" { return nil, errors.New("either --uuid or --log-index must be specified") } // retrieve rekor pubkey for verification verifier, err := loadVerifier(rekorClient) if err != nil { return nil, fmt.Errorf("retrieving rekor public key") } if logIndex != "" { params := entries.NewGetLogEntryByIndexParams() params.SetTimeout(viper.GetDuration("timeout")) logIndexInt, err := strconv.ParseInt(logIndex, 10, 0) if err != nil { return nil, fmt.Errorf("error parsing --log-index: %w", err) } params.LogIndex = logIndexInt resp, err := rekorClient.Entries.GetLogEntryByIndex(params) if err != nil { return nil, err } var e models.LogEntryAnon for ix, entry := range resp.Payload { // verify log entry e = entry if err := verify.VerifyLogEntry(ctx, &e, verifier); err != nil { return nil, fmt.Errorf("unable to verify entry was added to log: %w", err) } // verify checkpoint if entry.Verification.InclusionProof.Checkpoint != nil { if err := verify.VerifyCheckpointSignature(&e, verifier); err != nil { return nil, err } } return parseEntry(ix, entry) } } // Note: this UUID may be an EntryID if uuid != "" { params := entries.NewGetLogEntryByUUIDParams() params.SetTimeout(viper.GetDuration("timeout")) params.EntryUUID = uuid resp, err := rekorClient.Entries.GetLogEntryByUUID(params) if err != nil { return nil, err } var e models.LogEntryAnon for k, entry := range resp.Payload { if err := compareEntryUUIDs(params.EntryUUID, k); err != nil { return nil, err } // verify log entry e = entry if err := verify.VerifyLogEntry(ctx, &e, verifier); err != nil { return nil, fmt.Errorf("unable to verify entry was added to log: %w", err) } return parseEntry(k, entry) } } return nil, errors.New("entry not found") }), } // TODO: Move this to the verify pkg, but only after sharding cleans // up it's Trillian client dependency. func compareEntryUUIDs(requestEntryUUID string, responseEntryUUID string) error { requestUUID, err := sharding.GetUUIDFromIDString(requestEntryUUID) if err != nil { return err } responseUUID, err := sharding.GetUUIDFromIDString(responseEntryUUID) if err != nil { return err } // Compare UUIDs. if requestUUID != responseUUID { return fmt.Errorf("unexpected entry returned from rekor server: expected %s, got %s", requestEntryUUID, responseEntryUUID) } // If the request contains a Tree ID, then compare that. requestTreeID, err := sharding.GetTreeIDFromIDString(requestEntryUUID) if err != nil { if errors.Is(err, sharding.ErrPlainUUID) { // The request did not contain a Tree ID, we're good. return nil } // The request had a bad Tree ID, error out. return err } // We requested an entry from a given Tree ID. responseTreeID, err := sharding.GetTreeIDFromIDString(responseEntryUUID) if err != nil { if errors.Is(err, sharding.ErrPlainUUID) { // The response does not contain a Tree ID, we can only do so much. // Old rekor instances may not have returned one. return nil } return err } // We have Tree IDs. Compare. if requestTreeID != responseTreeID { return fmt.Errorf("unexpected entry returned from rekor server: expected %s, got %s", requestEntryUUID, responseEntryUUID) } return nil } func parseEntry(uuid string, e models.LogEntryAnon) (interface{}, error) { if viper.GetString("format") == "tle" { return tle.GenerateTransparencyLogEntry(e) } b, err := base64.StdEncoding.DecodeString(e.Body.(string)) if err != nil { return nil, err } pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { return nil, err } eimpl, err := types.UnmarshalEntry(pe) if err != nil { return nil, err } obj := getCmdOutput{ Body: eimpl, UUID: uuid, IntegratedTime: *e.IntegratedTime, LogIndex: int(*e.LogIndex), LogID: *e.LogID, } if e.Attestation != nil { obj.Attestation = string(e.Attestation.Data) } return &obj, nil } func init() { initializePFlagMap() if err := addUUIDPFlags(getCmd, false); err != nil { log.CliLogger.Fatal("Error parsing cmd line args: ", err) } if err := addLogIndexFlag(getCmd, false); err != nil { log.CliLogger.Fatal("Error parsing cmd line args: ", err) } rootCmd.AddCommand(getCmd) } rekor-1.3.5/cmd/rekor-cli/app/log_info.go000066400000000000000000000134061455727245600202240ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "context" "crypto" "crypto/x509" "encoding/pem" "errors" "fmt" "github.com/go-openapi/swag" rclient "github.com/sigstore/rekor/pkg/generated/client" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/verify" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/sigstore/rekor/cmd/rekor-cli/app/format" "github.com/sigstore/rekor/cmd/rekor-cli/app/state" "github.com/sigstore/rekor/pkg/client" "github.com/sigstore/rekor/pkg/generated/client/tlog" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/util" "github.com/sigstore/sigstore/pkg/signature" ) type logInfoCmdOutput struct { ActiveTreeSize int64 TotalTreeSize int64 RootHash string TreeID string } func (l *logInfoCmdOutput) String() string { // Verification is always successful if we return an object. return fmt.Sprintf(`Verification Successful! Active Tree Size: %v Total Tree Size: %v Root Hash: %s TreeID: %s `, l.ActiveTreeSize, l.TotalTreeSize, l.RootHash, l.TreeID) } // logInfoCmd represents the current information about the transparency log var logInfoCmd = &cobra.Command{ Use: "loginfo", Short: "Rekor loginfo command", Long: `Prints info about the transparency log`, Run: format.WrapCmd(func(args []string) (interface{}, error) { serverURL := viper.GetString("rekor_server") ctx := context.Background() rekorClient, err := client.GetRekorClient(serverURL, client.WithUserAgent(UserAgent()), client.WithRetryCount(viper.GetUint("retry")), client.WithLogger(log.CliLogger)) if err != nil { return nil, err } params := tlog.GetLogInfoParams{} params.SetTimeout(viper.GetDuration("timeout")) result, err := rekorClient.Tlog.GetLogInfo(¶ms) if err != nil { return nil, err } logInfo := result.GetPayload() // Verify inactive shards if err := verifyInactiveTrees(ctx, rekorClient, serverURL, logInfo.InactiveShards); err != nil { return nil, err } // Verify the active tree sth := util.SignedCheckpoint{} signedTreeHead := swag.StringValue(logInfo.SignedTreeHead) if err := sth.UnmarshalText([]byte(signedTreeHead)); err != nil { return nil, err } treeID := swag.StringValue(logInfo.TreeID) if err := verifyTree(ctx, rekorClient, signedTreeHead, serverURL, treeID); err != nil { return nil, err } cmdOutput := &logInfoCmdOutput{ ActiveTreeSize: swag.Int64Value(logInfo.TreeSize), TotalTreeSize: totalTreeSize(logInfo, logInfo.InactiveShards), RootHash: swag.StringValue(logInfo.RootHash), TreeID: swag.StringValue(logInfo.TreeID), } return cmdOutput, nil }), } func verifyInactiveTrees(ctx context.Context, rekorClient *rclient.Rekor, serverURL string, inactiveShards []*models.InactiveShardLogInfo) error { if inactiveShards == nil { return nil } log.CliLogger.Infof("Validating inactive shards...") for _, shard := range inactiveShards { signedTreeHead := swag.StringValue(shard.SignedTreeHead) treeID := swag.StringValue(shard.TreeID) if err := verifyTree(ctx, rekorClient, signedTreeHead, serverURL, treeID); err != nil { return fmt.Errorf("verifying inactive shard with ID %s: %w", treeID, err) } } log.CliLogger.Infof("Successfully validated inactive shards") return nil } func verifyTree(ctx context.Context, rekorClient *rclient.Rekor, signedTreeHead, serverURL, treeID string) error { oldState := state.Load(serverURL) if treeID != "" { oldState = state.Load(treeID) } sth := util.SignedCheckpoint{} if err := sth.UnmarshalText([]byte(signedTreeHead)); err != nil { return err } verifier, err := loadVerifier(rekorClient) if err != nil { return err } if !sth.Verify(verifier) { return errors.New("signature on tree head did not verify") } if oldState != nil { if err := verify.ProveConsistency(ctx, rekorClient, oldState, &sth, treeID); err != nil { return err } } else { log.CliLogger.Infof("No previous log state stored, unable to prove consistency") } if viper.GetBool("store_tree_state") { if treeID != "" { if err := state.Dump(treeID, &sth); err != nil { log.CliLogger.Infof("Unable to store previous state: %v", err) } } if err := state.Dump(serverURL, &sth); err != nil { log.CliLogger.Infof("Unable to store previous state: %v", err) } } return nil } func loadVerifier(rekorClient *rclient.Rekor) (signature.Verifier, error) { publicKey := viper.GetString("rekor_server_public_key") if publicKey == "" { // fetch key from server keyResp, err := rekorClient.Pubkey.GetPublicKey(nil) if err != nil { return nil, err } publicKey = keyResp.Payload } block, _ := pem.Decode([]byte(publicKey)) if block == nil { return nil, errors.New("failed to decode public key of server") } pub, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, err } return signature.LoadVerifier(pub, crypto.SHA256) } func totalTreeSize(activeShard *models.LogInfo, inactiveShards []*models.InactiveShardLogInfo) int64 { total := swag.Int64Value(activeShard.TreeSize) for _, i := range inactiveShards { total += swag.Int64Value(i.TreeSize) } return total } func init() { initializePFlagMap() rootCmd.AddCommand(logInfoCmd) } rekor-1.3.5/cmd/rekor-cli/app/log_proof.go000066400000000000000000000066251455727245600204230ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "errors" "fmt" "os" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/sigstore/rekor/cmd/rekor-cli/app/format" "github.com/sigstore/rekor/pkg/client" "github.com/sigstore/rekor/pkg/generated/client/tlog" "github.com/sigstore/rekor/pkg/log" ) type logProofOutput struct { RootHash string Hashes []string } func (l *logProofOutput) String() string { s := fmt.Sprintf("Current Root Hash: %v\n", l.RootHash) s += "Hashes: [" for i, hash := range l.Hashes { if i+1 == len(l.Hashes) { s += hash } else { s += fmt.Sprintf("%v,", hash) } } s += "]\n" return s } // logProof represents the consistency proof var logProofCmd = &cobra.Command{ Use: "logproof", Short: "Rekor logproof command", Long: `Prints information required to compute the consistency proof of the transparency log`, PreRunE: func(cmd *cobra.Command, args []string) error { // these are bound here so that they are not overwritten by other commands if err := viper.BindPFlags(cmd.Flags()); err != nil { return fmt.Errorf("error initializing cmd line args: %s", err) } if viper.GetUint64("first-size") == 0 { return errors.New("first-size must be > 0") } if !viper.IsSet("last-size") { return errors.New("last-size must be specified") } if viper.GetUint64("last-size") == 0 { return errors.New("last-size must be > 0") } if viper.GetUint64("first-size") > viper.GetUint64("last-size") { return errors.New("last-size must be >= to first-size") } return nil }, Run: format.WrapCmd(func(args []string) (interface{}, error) { rekorClient, err := client.GetRekorClient(viper.GetString("rekor_server"), client.WithUserAgent(UserAgent()), client.WithRetryCount(viper.GetUint("retry")), client.WithLogger(log.CliLogger)) if err != nil { return nil, err } firstSize := int64(viper.GetUint64("first-size")) lastSize := int64(viper.GetUint64("last-size")) treeID := viper.GetString("tree-id") params := tlog.NewGetLogProofParams() params.FirstSize = &firstSize params.LastSize = lastSize params.TreeID = &treeID params.SetTimeout(viper.GetDuration("timeout")) result, err := rekorClient.Tlog.GetLogProof(params) if err != nil { return nil, err } consistencyProof := result.GetPayload() return &logProofOutput{ RootHash: *consistencyProof.RootHash, Hashes: consistencyProof.Hashes, }, nil }), } func init() { initializePFlagMap() logProofCmd.Flags().Uint64("first-size", 1, "the size of the log where the proof should begin") logProofCmd.Flags().Uint64("last-size", 0, "the size of the log where the proof should end") logProofCmd.Flags().String("tree-id", "", "the tree id of the log (defaults to active tree)") if err := logProofCmd.MarkFlagRequired("last-size"); err != nil { fmt.Println(err) os.Exit(1) } rootCmd.AddCommand(logProofCmd) } rekor-1.3.5/cmd/rekor-cli/app/pflag_groups.go000066400000000000000000000130061455727245600211140ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "encoding/base64" "errors" "fmt" "net/url" "strings" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" "github.com/spf13/cobra" "github.com/spf13/viper" ) // addFlagToCmd adds the specified command of a specified type to the command's flag set func addFlagToCmd(cmd *cobra.Command, required bool, flagType FlagType, flag, desc string) error { cmd.Flags().Var(NewFlagValue(flagType, ""), flag, desc) if required { return cmd.MarkFlagRequired(flag) } return nil } // addLogIndexFlag adds the "log-index" command to the command's flag set func addLogIndexFlag(cmd *cobra.Command, required bool) error { return addFlagToCmd(cmd, required, logIndexFlag, "log-index", "the index of the entry in the transparency log") } // addUUIDPFlags adds the "uuid" command to the command's flag set func addUUIDPFlags(cmd *cobra.Command, required bool) error { return addFlagToCmd(cmd, required, uuidFlag, "uuid", "UUID of entry in transparency log (if known)") } func addArtifactPFlags(cmd *cobra.Command) error { flags := map[string]struct { flagType FlagType desc string required bool }{ "signature": { fileOrURLFlag, "path or URL to detached signature file", false, }, "type": { typeFlag, fmt.Sprintf("type of entry expressed as type(:version)?; supported types = %v", types.ListImplementedTypes()), false, }, "pki-format": { pkiFormatFlag, fmt.Sprintf("format of the signature and/or public key; options = %v", pki.SupportedFormats()), false, }, "public-key": { multiFileOrURLFlag, "path or URL to public key file", false, }, "artifact": { fileOrURLFlag, "path or URL to artifact file", false, }, "artifact-hash": { shaFlag, "hex encoded SHA256 hash of artifact (when using URL)", false, }, "entry": { fileOrURLFlag, "path or URL to pre-formatted entry file", false, }, "aad": { base64Flag, "base64 encoded additional authenticated data", false, }, } for flag, flagVal := range flags { if err := addFlagToCmd(cmd, flagVal.required, flagVal.flagType, flag, flagVal.desc); err != nil { return err } } return nil } func validateArtifactPFlags(uuidValid, indexValid bool) error { uuidGiven := false if uuidValid && viper.GetString("uuid") != "" { uuidGiven = true } indexGiven := false if indexValid && viper.GetString("log-index") != "" { indexGiven = true } // if neither --entry or --artifact were given, then a reference to a uuid or index is needed if viper.GetString("entry") == "" && viper.GetString("artifact") == "" && viper.GetString("artifact-hash") == "" { if (uuidGiven && uuidValid) || (indexGiven && indexValid) { return nil } return errors.New("either 'entry' or 'artifact' or 'artifact-hash' must be specified") } return nil } func CreatePropsFromPflags() *types.ArtifactProperties { props := &types.ArtifactProperties{} artifactString := viper.GetString("artifact") if artifactString != "" { if isURL(artifactString) { props.ArtifactPath, _ = url.Parse(artifactString) } else { props.ArtifactPath = &url.URL{Path: artifactString} } } props.ArtifactHash = viper.GetString("artifact-hash") signatureString := viper.GetString("signature") if signatureString != "" { if isURL(signatureString) { props.SignaturePath, _ = url.Parse(signatureString) } else { props.SignaturePath = &url.URL{Path: signatureString} } } publicKeyString := viper.GetString("public-key") splitPubKeyString := strings.Split(publicKeyString, ",") if len(splitPubKeyString) > 0 { collectedKeys := []*url.URL{} for _, key := range splitPubKeyString { if isURL(key) { keyPath, _ := url.Parse(key) collectedKeys = append(collectedKeys, keyPath) } else { collectedKeys = append(collectedKeys, &url.URL{Path: key}) } } props.PublicKeyPaths = collectedKeys } props.PKIFormat = viper.GetString("pki-format") b64aad := viper.GetString("aad") if b64aad != "" { props.AdditionalAuthenticatedData, _ = base64.StdEncoding.DecodeString(b64aad) } return props } // ParseTypeFlag validates the requested type (and optional version) are supported func ParseTypeFlag(typeStr string) (string, string, error) { // typeStr can come in as: // type -> use default version for this kind // type:version_string -> attempt to use specified version string typeStrings := strings.SplitN(typeStr, ":", 2) tf, ok := types.TypeMap.Load(typeStrings[0]) if !ok { return "", "", fmt.Errorf("unknown type %v", typeStrings[0]) } ti := tf.(func() types.TypeImpl)() if ti == nil { return "", "", fmt.Errorf("type %v is not implemented", typeStrings[0]) } switch len(typeStrings) { case 1: return typeStrings[0], "", nil case 2: if !ti.IsSupportedVersion(typeStrings[1]) { return "", "", fmt.Errorf("type %v does not support version %v", typeStrings[0], typeStrings[1]) } return typeStrings[0], typeStrings[1], nil } return "", "", errors.New("malformed type string") } rekor-1.3.5/cmd/rekor-cli/app/pflags.go000066400000000000000000000246741455727245600177150ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "encoding/base64" "errors" "fmt" "log" "os" "path/filepath" "strconv" "strings" "time" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/sharding" "github.com/spf13/pflag" validator "github.com/asaskevich/govalidator" ) type FlagType string const ( uuidFlag FlagType = "uuid" shaFlag FlagType = "sha" emailFlag FlagType = "email" operatorFlag FlagType = "operator" logIndexFlag FlagType = "logIndex" pkiFormatFlag FlagType = "pkiFormat" typeFlag FlagType = "type" fileFlag FlagType = "file" urlFlag FlagType = "url" fileOrURLFlag FlagType = "fileOrURL" multiFileOrURLFlag FlagType = "multiFileOrURL" oidFlag FlagType = "oid" formatFlag FlagType = "format" timeoutFlag FlagType = "timeout" base64Flag FlagType = "base64" uintFlag FlagType = "uint" ) type newPFlagValueFunc func() pflag.Value var pflagValueFuncMap map[FlagType]newPFlagValueFunc // TODO: unit tests for all of this func initializePFlagMap() { pflagValueFuncMap = map[FlagType]newPFlagValueFunc{ uuidFlag: func() pflag.Value { // this validates a UUID with or without a prepended TreeID; // the UUID corresponds to the merkle leaf hash of entries, // which is represented by a 64 character hexadecimal string return valueFactory(uuidFlag, validateID, "") }, shaFlag: func() pflag.Value { // this validates a valid sha256 checksum which is optionally prefixed with 'sha256:' return valueFactory(shaFlag, validateSHAValue, "") }, operatorFlag: func() pflag.Value { // this validates a valid operator name operatorFlagValidator := func(val string) error { o := struct { Value string `valid:"in(and|or)"` }{val} _, err := validator.ValidateStruct(o) return err } return valueFactory(operatorFlag, operatorFlagValidator, "") }, emailFlag: func() pflag.Value { // this validates an email address emailValidator := func(val string) error { if !validator.IsEmail(val) { return fmt.Errorf("'%v' is not a valid email address", val) } return nil } return valueFactory(emailFlag, emailValidator, "") }, logIndexFlag: func() pflag.Value { // this checks for a valid integer >= 0 return valueFactory(logIndexFlag, validateUint, "") }, pkiFormatFlag: func() pflag.Value { // this ensures a PKI implementation exists for the requested format pkiFormatValidator := func(val string) error { if !validator.IsIn(val, pki.SupportedFormats()...) { return fmt.Errorf("'%v' is not a valid pki format", val) } return nil } return valueFactory(pkiFormatFlag, pkiFormatValidator, "pgp") }, typeFlag: func() pflag.Value { // this ensures the type of the log entry matches a type supported in the CLI return valueFactory(typeFlag, validateTypeFlag, "rekord") }, fileFlag: func() pflag.Value { // this validates that the file exists and can be opened by the current uid return valueFactory(fileFlag, validateFile, "") }, urlFlag: func() pflag.Value { // this validates that the string is a valid http/https URL httpHTTPSValidator := func(val string) error { if !validator.IsURL(val) { return fmt.Errorf("'%v' is not a valid url", val) } if !(strings.HasPrefix(val, "http") || strings.HasPrefix(val, "https")) { return errors.New("URL must be for http or https scheme") } return nil } return valueFactory(urlFlag, httpHTTPSValidator, "") }, fileOrURLFlag: func() pflag.Value { // applies logic of fileFlag OR urlFlag validators from above return valueFactory(fileOrURLFlag, validateFileOrURL, "") }, multiFileOrURLFlag: func() pflag.Value { // applies logic of fileFlag OR urlFlag validators from above for multi file and URL return multiValueFactory(multiFileOrURLFlag, validateFileOrURL, []string{}) }, oidFlag: func() pflag.Value { // this validates for an OID, which is a sequence of positive integers separated by periods return valueFactory(oidFlag, validateOID, "") }, formatFlag: func() pflag.Value { // this validates the output format requested formatValidator := func(val string) error { if !validator.IsIn(val, "json", "default", "tle") { return fmt.Errorf("'%v' is not a valid output format", val) } return nil } return valueFactory(formatFlag, formatValidator, "") }, timeoutFlag: func() pflag.Value { // this validates the timeout is >= 0 return valueFactory(formatFlag, validateTimeout, "") }, base64Flag: func() pflag.Value { // This validates the string is in base64 format return valueFactory(base64Flag, validateBase64, "") }, uintFlag: func() pflag.Value { // This validates the string is in base64 format return valueFactory(uintFlag, validateUint, "") }, } } // NewFlagValue creates a new pflag.Value for the specified type with the specified default value. // If a default value is not desired, pass "" for defaultVal. func NewFlagValue(flagType FlagType, defaultVal string) pflag.Value { valFunc := pflagValueFuncMap[flagType] val := valFunc() if defaultVal != "" { if err := val.Set(defaultVal); err != nil { log.Fatal(fmt.Errorf("initializing flag: %w", err)) } } return val } type validationFunc func(string) error func valueFactory(flagType FlagType, v validationFunc, defaultVal string) pflag.Value { return &baseValue{ flagType: flagType, validationFunc: v, value: defaultVal, } } func multiValueFactory(flagType FlagType, v validationFunc, defaultVal []string) pflag.Value { return &multiBaseValue{ flagType: flagType, validationFunc: v, value: defaultVal, } } // multiBaseValue implements pflag.Value type multiBaseValue struct { flagType FlagType value []string validationFunc validationFunc } func (b *multiBaseValue) String() string { return strings.Join(b.value, ",") } // Type returns the type of this Value func (b multiBaseValue) Type() string { return string(b.flagType) } func (b *multiBaseValue) Set(value string) error { if err := b.validationFunc(value); err != nil { return err } b.value = append(b.value, value) return nil } // baseValue implements pflag.Value type baseValue struct { flagType FlagType value string validationFunc validationFunc } // Type returns the type of this Value func (b baseValue) Type() string { return string(b.flagType) } // String returns the string representation of this Value func (b baseValue) String() string { return b.value } // Set validates the provided string against the appropriate validation rule // for b.flagType; if the string validates, it is stored in the Value and nil is returned. // Otherwise the validation error is returned but the state of the Value is not changed. func (b *baseValue) Set(s string) error { if err := b.validationFunc(s); err != nil { return err } b.value = s return nil } // isURL returns true if the supplied value is a valid URL and false otherwise func isURL(v string) bool { valGen := pflagValueFuncMap[urlFlag] return valGen().Set(v) == nil } // validateSHAValue ensures that the supplied string matches the following formats: // [sha512:]<128 hexadecimal characters> // [sha256:]<64 hexadecimal characters> // [sha1:]<40 hexadecimal characters> // where [sha256:] and [sha1:] are optional func validateSHAValue(v string) error { err := validateSHA1Value(v) if err == nil { return nil } err = validateSHA256Value(v) if err == nil { return nil } err = validateSHA512Value(v) if err == nil { return nil } return fmt.Errorf("error parsing %v flag: %w", shaFlag, err) } // validateFileOrURL ensures the provided string is either a valid file path that can be opened or a valid URL func validateFileOrURL(v string) error { valGen := pflagValueFuncMap[fileFlag] if valGen().Set(v) == nil { return nil } valGen = pflagValueFuncMap[urlFlag] return valGen().Set(v) } // validateID ensures the ID is either an EntryID (TreeID + UUID) or a UUID func validateID(v string) error { if len(v) != sharding.EntryIDHexStringLen && len(v) != sharding.UUIDHexStringLen { return fmt.Errorf("ID len error, expected %v (EntryID) or %v (UUID) but got len %v for ID %v", sharding.EntryIDHexStringLen, sharding.UUIDHexStringLen, len(v), v) } if !validator.IsHexadecimal(v) { return fmt.Errorf("invalid uuid: %v", v) } return nil } // validateOID ensures that the supplied string is a valid ASN.1 object identifier func validateOID(v string) error { values := strings.Split(v, ".") for _, value := range values { if !validator.IsNumeric(value) { return fmt.Errorf("field '%v' is not a valid number", value) } } return nil } // validateTimeout ensures that the supplied string is a valid time.Duration value >= 0 func validateTimeout(v string) error { duration, err := time.ParseDuration(v) if err != nil { return err } if duration < 0 { return errors.New("timeout must be a positive value") } return nil } // validateBase64 ensures that the supplied string is valid base64 encoded data func validateBase64(v string) error { _, err := base64.StdEncoding.DecodeString(v) return err } // validateTypeFlag ensures that the string is in the format type(\.version)? and // that one of the types requested is implemented func validateTypeFlag(v string) error { _, _, err := ParseTypeFlag(v) return err } // validateUint ensures that the supplied string is a valid unsigned integer >= 0 func validateUint(v string) error { i, err := strconv.Atoi(v) if err != nil { return err } if i < 0 { return fmt.Errorf("invalid unsigned int: %v", v) } return nil } // validateFile ensures that the supplied string is a valid path to a file that exists func validateFile(v string) error { fileInfo, err := os.Stat(filepath.Clean(v)) if err != nil { return err } if fileInfo.IsDir() { return errors.New("path to a directory was provided") } return nil } rekor-1.3.5/cmd/rekor-cli/app/pflags_test.go000066400000000000000000000635241455727245600207510ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "context" "errors" "net/http" "net/http/httptest" "os" "testing" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/sigstore/rekor/pkg/types" ) func TestArtifactPFlags(t *testing.T) { type test struct { caseDesc string typeStr string entry string artifact string signature string publicKey string multiPublicKey []string uuid string aad string uuidRequired bool logIndex string logIndexRequired bool expectParseSuccess bool expectValidateSuccess bool } testServer := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { file := []byte{} var err error switch r.URL.Path { case "/artifact": file, err = os.ReadFile("tests/test_file.txt") case "/signature": file, err = os.ReadFile("tests/test_file.sig") case "/publicKey": file, err = os.ReadFile("tests/test_public_key.key") case "/rekord": file, err = os.ReadFile("tests/rekor.json") case "/rpmEntry": file, err = os.ReadFile("tests/rpm.json") case "/rpm": file, err = os.ReadFile("tests/test.rpm") case "/rpmPublicKey": file, err = os.ReadFile("tests/test_rpm_public_key.key") case "/alpine": file, err = os.ReadFile("tests/test_alpine.apk") case "/alpinePublicKey": file, err = os.ReadFile("tests/test_alpine.pub") case "/alpineEntry": file, err = os.ReadFile("tests/alpine.json") case "/helmEntry": file, err = os.ReadFile("tests/helm.json") case "/not_found": err = errors.New("file not found") } if err != nil { w.WriteHeader(http.StatusNotFound) return } w.WriteHeader(http.StatusOK) _, _ = w.Write(file) })) defer testServer.Close() tests := []test{ { caseDesc: "valid rekord file", entry: "tests/rekor.json", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid rekord URL", entry: testServer.URL + "/rekord", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid rekord file, wrong type", typeStr: "rpm", entry: "tests/rekor.json", expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "valid rpm file", entry: "tests/rpm.json", typeStr: "rpm", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid rpm URL", entry: testServer.URL + "/rpmEntry", typeStr: "rpm", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid alpine URL", entry: testServer.URL + "/alpineEntry", typeStr: "alpine", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid helm URL", entry: testServer.URL + "/helmEntry", typeStr: "helm", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid rpm file, wrong type", typeStr: "rekord", entry: "tests/rpm.json", expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "non-existent rekord file", entry: "tests/not_there.json", expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "non-existent rekord url", entry: testServer.URL + "/not_found", expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "valid rekord - local artifact with required flags", artifact: "tests/test_file.txt", signature: "tests/test_file.sig", publicKey: "tests/test_public_key.key", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid rpm - local artifact with required flags", typeStr: "rpm", artifact: "tests/test.rpm", publicKey: "tests/test_rpm_public_key.key", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid alpine - local artifact with required flags", typeStr: "alpine", artifact: "tests/test_alpine.apk", publicKey: "tests/test_alpine.pub", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "nonexistent local artifact", artifact: "tests/not_a_file", signature: "tests/test_file.sig", publicKey: "tests/test_public_key.key", expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "nonexistent remote artifact", artifact: testServer.URL + "/not_found", signature: "tests/test_file.sig", publicKey: "tests/test_public_key.key", expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "local artifact without required local signature", artifact: "tests/test_file.txt", publicKey: "tests/test_public_key.key", expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "local artifact with missing remote signature", artifact: "tests/test_file.txt", publicKey: "tests/test_public_key.key", signature: testServer.URL + "/not_found", expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "local artifact with invalid local signature", artifact: "tests/test_file.txt", signature: "tests/not_a_file", publicKey: "tests/test_public_key.key", expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "local artifact with invalid remote signature", artifact: "tests/test_file.txt", signature: testServer.URL + "/artifact", publicKey: "tests/test_public_key.key", expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "local artifact without required public key", artifact: "tests/test_file.txt", signature: "tests/test_file.sig", expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "local artifact with invalid local public key", artifact: "tests/test_file.txt", signature: "tests/test_file.sig", publicKey: "tests/not_a_file", expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "local artifact with invalid remote public key", artifact: "tests/test_file.txt", signature: "tests/test_file.sig", publicKey: testServer.URL + "/artifact", expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "valid rekord - remote artifact with required flags", artifact: testServer.URL + "/artifact", signature: "tests/test_file.sig", publicKey: "tests/test_public_key.key", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid rpm - remote artifact with required flags", typeStr: "rpm", artifact: testServer.URL + "/rpm", publicKey: "tests/test_rpm_public_key.key", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid alpine - remote artifact with required flags", typeStr: "alpine", artifact: testServer.URL + "/alpine", publicKey: "tests/test_alpine.pub", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "remote artifact with invalid URL", artifact: "hteeteep%**/test_file.txt", signature: "tests/test_file.sig", publicKey: "tests/test_public_key.key", expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "valid uuid", uuid: "3030303030303030303030303030303030303030303030303030303030303030", uuidRequired: true, expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "invalid uuid", uuid: "not_a_uuid", uuidRequired: true, expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "unwanted uuid", uuid: "3030303030303030303030303030303030303030303030303030303030303030", uuidRequired: false, expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "valid log index", logIndex: "1", logIndexRequired: true, expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "invalid log index", logIndex: "not_a_int", logIndexRequired: true, expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "invalid log index - less than 0", logIndex: "-1", logIndexRequired: true, expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "unwanted log index", logIndex: "1", logIndexRequired: false, expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "no flags when either uuid, rekord, or artifact++ are needed", uuidRequired: false, expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "missing uuid flag when it is needed", uuidRequired: true, expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "missing log index flag when it is needed", logIndexRequired: true, expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "valid cose, with aad", typeStr: "cose", artifact: "tests/test_cose.cbor", publicKey: "tests/test_cose.pub", expectParseSuccess: true, expectValidateSuccess: true, aad: "dGVzdCBhYWQ=", }, { caseDesc: "valid cose, malformed base64 aad", typeStr: "cose", artifact: "tests/test_cose.cbor", publicKey: "tests/test_cose.pub", expectParseSuccess: false, expectValidateSuccess: true, aad: "dGVzdCBhYWQ]", }, { caseDesc: "valid cose, missing aad", typeStr: "cose", artifact: "tests/test_cose.cbor", publicKey: "tests/test_cose.pub", expectParseSuccess: true, expectValidateSuccess: false, }, { caseDesc: "valid intoto - one keys", typeStr: "intoto", artifact: "tests/intoto_dsse.json", publicKey: "tests/intoto_dsse.pem", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid intoto - multi keys", typeStr: "intoto", artifact: "tests/intoto_multi_dsse.json", multiPublicKey: []string{"tests/intoto_dsse.pem", "tests/intoto_multi_pub2.pem"}, expectParseSuccess: true, expectValidateSuccess: true, }, } for _, tc := range tests { initializePFlagMap() var blankCmd = &cobra.Command{} if err := addArtifactPFlags(blankCmd); err != nil { t.Fatalf("unexpected error adding flags in '%v': %v", tc.caseDesc, err) } if err := addUUIDPFlags(blankCmd, tc.uuidRequired); err != nil { t.Fatalf("unexpected error adding uuid flags in '%v': %v", tc.caseDesc, err) } if err := addLogIndexFlag(blankCmd, tc.logIndexRequired); err != nil { t.Fatalf("unexpected error adding log index flags in '%v': %v", tc.caseDesc, err) } args := []string{} if tc.entry != "" { args = append(args, "--entry", tc.entry) } if tc.typeStr != "" { args = append(args, "--type", tc.typeStr) } if tc.artifact != "" { args = append(args, "--artifact", tc.artifact) } if tc.signature != "" { args = append(args, "--signature", tc.signature) } if tc.publicKey != "" { args = append(args, "--public-key", tc.publicKey) } if len(tc.multiPublicKey) > 0 { for _, key := range tc.multiPublicKey { args = append(args, "--public-key", key) } } if tc.uuid != "" { args = append(args, "--uuid", tc.uuid) } if tc.logIndex != "" { args = append(args, "--log-index", tc.logIndex) } if tc.aad != "" { args = append(args, "--aad", tc.aad) } if err := blankCmd.ParseFlags(args); (err == nil) != tc.expectParseSuccess { t.Errorf("unexpected result parsing '%v': %v", tc.caseDesc, err) continue } if tc.expectValidateSuccess { if err := viper.BindPFlags(blankCmd.Flags()); err != nil { t.Fatalf("unexpected result initializing viper in '%v': %v", tc.caseDesc, err) } if err := validateArtifactPFlags(tc.uuidRequired, tc.logIndexRequired); (err == nil) != tc.expectValidateSuccess { t.Errorf("unexpected result validating '%v': %v", tc.caseDesc, err) continue } if !tc.uuidRequired && !tc.logIndexRequired && tc.entry == "" { typeStr, versionStr, err := ParseTypeFlag(viper.GetString("type")) if err != nil { t.Fatalf("error parsing typeStr: %v", err) } props := CreatePropsFromPflags() if _, err := types.NewProposedEntry(context.Background(), typeStr, versionStr, *props); err != nil { t.Errorf("unexpected result in '%v' building entry: %v", tc.caseDesc, err) } } } } } func TestValidateRekorServerURL(t *testing.T) { type test struct { caseDesc string rekorServer string expectSuccess bool } tests := []test{ { caseDesc: "value not specified", expectSuccess: false, }, { caseDesc: "valid rekor_server value", rekorServer: "http://localhost:3000", expectSuccess: true, }, { caseDesc: "valid URL, invalid scheme", rekorServer: "ldap://localhost:3000", expectSuccess: false, }, { caseDesc: "invalid URL", rekorServer: "hteeteepeeColonSlashSlashlocalhost:3000", expectSuccess: false, }, { caseDesc: "local path", rekorServer: "/localhost", expectSuccess: false, }, } for _, tc := range tests { if err := rootCmd.PersistentFlags().Set("rekor_server", tc.rekorServer); (err == nil) != tc.expectSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } } } // TestValidateRetryCount tests the validation of the retry count flag func TestValidateRetryCount(t *testing.T) { type test struct { caseDesc string retryCount string expectSuccess bool } tests := []test{ { caseDesc: "value not specified", expectSuccess: false, }, { caseDesc: "valid retry_count value: 0", retryCount: "0", expectSuccess: true, }, { caseDesc: "valid retry_count value: 1", retryCount: "1", expectSuccess: true, }, { caseDesc: "invalid retry_count value: asdf", retryCount: "asdf", expectSuccess: false, }, { caseDesc: "invalid retry_count value: -1", retryCount: "-1", expectSuccess: false, }, } for _, tc := range tests { if err := rootCmd.PersistentFlags().Set("retry", tc.retryCount); (err == nil) != tc.expectSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } } } func TestSearchPFlags(t *testing.T) { type test struct { caseDesc string artifact string publicKey string sha string email string pkiFormat string expectParseSuccess bool expectValidateSuccess bool } testServer := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { file := []byte{} var err error switch r.URL.Path { case "/artifact": file, err = os.ReadFile("tests/test_file.txt") case "/publicKey": file, err = os.ReadFile("tests/test_public_key.key") case "/not_found": err = errors.New("file not found") } if err != nil { w.WriteHeader(http.StatusNotFound) return } w.WriteHeader(http.StatusOK) _, _ = w.Write(file) })) defer testServer.Close() tests := []test{ { caseDesc: "valid local artifact", artifact: "tests/test_file.txt", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid remote artifact", artifact: testServer.URL + "/artifact", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "nonexistent local artifact", artifact: "tests/not_a_file", expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "nonexistent remote artifact", artifact: testServer.URL + "/not_found", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid local public key", publicKey: "tests/test_public_key.key", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid local minisign public key", publicKey: "../../../pkg/pki/minisign/testdata/minisign.pub", pkiFormat: "minisign", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid remote public key", publicKey: testServer.URL + "/publicKey", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "nonexistent local public key", publicKey: "tests/not_a_file", expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "nonexistent remote public key", publicKey: testServer.URL + "/not_found", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid SHA1", sha: "84374135959eacf60cf3fed7520a01b336332efe", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid SHA1 with prefix", sha: "sha1:84374135959eacf60cf3fed7520a01b336332efe", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid SHA256", sha: "45c7b11fcbf07dec1694adecd8c5b85770a12a6c8dfdcf2580a2db0c47c31779", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid SHA256 with prefix", sha: "sha256:45c7b11fcbf07dec1694adecd8c5b85770a12a6c8dfdcf2580a2db0c47c31779", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid SHA512", sha: "a5d575f245588b64bcec78a1bb9d92a66bfb4d68d7de1aea4162ad0b232753860cb764fd0645ada1f5d935163522987359e515e0594068d7bc108f0584d6da29", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "valid SHA512 with prefix", sha: "sha512:a5d575f245588b64bcec78a1bb9d92a66bfb4d68d7de1aea4162ad0b232753860cb764fd0645ada1f5d935163522987359e515e0594068d7bc108f0584d6da29", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "invalid SHA prefix", sha: "sha257:45c7b11fcbf07dec1694adecd8c5b85770a12a6c8dfdcf2580a2db0c47c31779", expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "invalid SHA", sha: "45c7b11fcbf", expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "invalid hash alg", sha: "md5:d408f34c27cf5930be6394a455f23d40", expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "valid email", email: "cat@foo.com", expectParseSuccess: true, expectValidateSuccess: true, }, { caseDesc: "invalid email", email: "SignaMeseCat", expectParseSuccess: false, expectValidateSuccess: false, }, { caseDesc: "no flags when either artifact, sha, public key, or email are needed", expectParseSuccess: true, expectValidateSuccess: false, }, } for _, tc := range tests { initializePFlagMap() var blankCmd = &cobra.Command{} if err := addSearchPFlags(blankCmd); err != nil { t.Fatalf("unexpected error adding flags in '%v': %v", tc.caseDesc, err) } args := []string{} if tc.artifact != "" { args = append(args, "--artifact", tc.artifact) } if tc.publicKey != "" { args = append(args, "--public-key", tc.publicKey) } if tc.pkiFormat != "" { args = append(args, "--pki-format", tc.pkiFormat) } if tc.sha != "" { args = append(args, "--sha", tc.sha) } if tc.email != "" { args = append(args, "--email", tc.email) } if err := blankCmd.ParseFlags(args); (err == nil) != tc.expectParseSuccess { t.Errorf("unexpected result parsing '%v': %v", tc.caseDesc, err) continue } if err := viper.BindPFlags(blankCmd.Flags()); err != nil { t.Fatalf("unexpected result initializing viper in '%v': %v", tc.caseDesc, err) } if err := validateSearchPFlags(); (err == nil) != tc.expectValidateSuccess { t.Errorf("unexpected result validating '%v': %v", tc.caseDesc, err) continue } } } func TestParseTypeFlag(t *testing.T) { type test struct { caseDesc string typeStr string expectSuccess bool } tests := []test{ { caseDesc: "bogus", typeStr: "bogus", expectSuccess: false, }, { caseDesc: "rekord", typeStr: "rekord", expectSuccess: true, }, { caseDesc: "explicit rekord v0.0.1", typeStr: "rekord:0.0.1", expectSuccess: true, }, { caseDesc: "non-existent rekord v0.0.0", typeStr: "rekord:0.0.0", expectSuccess: false, }, { caseDesc: "hashedrekord", typeStr: "hashedrekord", expectSuccess: true, }, { caseDesc: "explicit hashedrekord v0.0.1", typeStr: "hashedrekord:0.0.1", expectSuccess: true, }, { caseDesc: "non-existent hashedrekord v0.0.0", typeStr: "hashedrekord:0.0.0", expectSuccess: false, }, { caseDesc: "alpine", typeStr: "alpine", expectSuccess: true, }, { caseDesc: "explicit alpine v0.0.1", typeStr: "alpine:0.0.1", expectSuccess: true, }, { caseDesc: "non-existent alpine v0.0.0", typeStr: "alpine:0.0.0", expectSuccess: false, }, { caseDesc: "cose", typeStr: "cose", expectSuccess: true, }, { caseDesc: "explicit cose v0.0.1", typeStr: "cose:0.0.1", expectSuccess: true, }, { caseDesc: "non-existent cose v0.0.0", typeStr: "cose:0.0.0", expectSuccess: false, }, { caseDesc: "helm", typeStr: "helm", expectSuccess: true, }, { caseDesc: "explicit helm v0.0.1", typeStr: "helm:0.0.1", expectSuccess: true, }, { caseDesc: "non-existent helm v0.0.0", typeStr: "helm:0.0.0", expectSuccess: false, }, { caseDesc: "intoto", typeStr: "intoto", expectSuccess: true, }, { caseDesc: "explicit intoto v0.0.1", typeStr: "intoto:0.0.1", expectSuccess: true, }, { caseDesc: "explicit intoto v0.0.2", typeStr: "intoto:0.0.2", expectSuccess: true, }, { caseDesc: "non-existent intoto v0.0.0", typeStr: "intoto:0.0.0", expectSuccess: false, }, { caseDesc: "jar", typeStr: "jar", expectSuccess: true, }, { caseDesc: "explicit jar v0.0.1", typeStr: "jar:0.0.1", expectSuccess: true, }, { caseDesc: "non-existent jar v0.0.0", typeStr: "jar:0.0.0", expectSuccess: false, }, { caseDesc: "rfc3161", typeStr: "rfc3161", expectSuccess: true, }, { caseDesc: "explicit rfc3161 v0.0.1", typeStr: "rfc3161:0.0.1", expectSuccess: true, }, { caseDesc: "non-existent rfc3161 v0.0.0", typeStr: "rfc3161:0.0.0", expectSuccess: false, }, { caseDesc: "rpm", typeStr: "rpm", expectSuccess: true, }, { caseDesc: "explicit rpm v0.0.1", typeStr: "rpm:0.0.1", expectSuccess: true, }, { caseDesc: "non-existent rpm v0.0.0", typeStr: "rpm:0.0.0", expectSuccess: false, }, { caseDesc: "tuf", typeStr: "tuf", expectSuccess: true, }, { caseDesc: "explicit tuf v0.0.1", typeStr: "tuf:0.0.1", expectSuccess: true, }, { caseDesc: "non-existent tuf v0.0.0", typeStr: "tuf:0.0.0", expectSuccess: false, }, } for _, tc := range tests { if _, _, err := ParseTypeFlag(tc.typeStr); (err == nil) != tc.expectSuccess { t.Fatalf("unexpected error parsing type flag in '%v': %v", tc.caseDesc, err) } } } rekor-1.3.5/cmd/rekor-cli/app/root.go000066400000000000000000000074761455727245600174250ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "fmt" "strings" homedir "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/spf13/viper" "github.com/sigstore/rekor/pkg/client" "github.com/sigstore/rekor/pkg/log" // these imports are to call the packages' init methods _ "github.com/sigstore/rekor/pkg/types/alpine/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/cose/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/dsse/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/helm/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/intoto/v0.0.2" _ "github.com/sigstore/rekor/pkg/types/jar/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/rekord/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/rfc3161/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/rpm/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/tuf/v0.0.1" ) var rootCmd = &cobra.Command{ Use: "rekor-cli", Short: "Rekor CLI", Long: `Rekor command line interface tool`, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { return initConfig(cmd) }, } // Execute runs the base CLI func Execute() { if err := rootCmd.Execute(); err != nil { log.CliLogger.Fatal(err) } } func init() { initializePFlagMap() rootCmd.PersistentFlags().String("config", "", "config file (default is $HOME/.rekor.yaml)") rootCmd.PersistentFlags().Bool("store_tree_state", true, "whether to store tree state in between invocations for additional verification") rootCmd.PersistentFlags().Var(NewFlagValue(urlFlag, "https://rekor.sigstore.dev"), "rekor_server", "Server address:port") rootCmd.PersistentFlags().Var(NewFlagValue(formatFlag, "default"), "format", "Command output format") rootCmd.PersistentFlags().Var(NewFlagValue(timeoutFlag, "30s"), "timeout", "HTTP timeout") rootCmd.PersistentFlags().Var(NewFlagValue(uintFlag, fmt.Sprintf("%d", client.DefaultRetryCount)), "retry", "Number of times to retry HTTP requests") // these are bound here and not in PreRun so that all child commands can use them if err := viper.BindPFlags(rootCmd.PersistentFlags()); err != nil { log.CliLogger.Fatal(err) } } func initConfig(cmd *cobra.Command) error { viper.SetEnvPrefix("rekor") viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) viper.AutomaticEnv() // manually set all values provided from viper through pflag validation logic var changedFlags []string cmd.Flags().VisitAll(func(f *pflag.Flag) { if !f.Changed && viper.IsSet(f.Name) { changedFlags = append(changedFlags, f.Name) } }) for _, flag := range changedFlags { val := viper.Get(flag) if err := cmd.Flags().Set(flag, fmt.Sprintf("%v", val)); err != nil { return err } } if viper.GetString("config") != "" { viper.SetConfigFile(viper.GetString("config")) } else { // Find home directory. home, err := homedir.Dir() if err != nil { return err } viper.AddConfigPath(home) viper.SetConfigName(".rekor") } if err := viper.ReadInConfig(); err != nil { switch err.(type) { case viper.ConfigFileNotFoundError: default: return err } } else if viper.GetString("format") == "default" { log.CliLogger.Infof("Using config file:", viper.ConfigFileUsed()) } return nil } rekor-1.3.5/cmd/rekor-cli/app/search.go000066400000000000000000000153631455727245600177010ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "crypto/sha256" "encoding/hex" "errors" "fmt" "io" "net/http" "os" "path/filepath" "strings" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/sigstore/rekor/cmd/rekor-cli/app/format" "github.com/sigstore/rekor/pkg/client" "github.com/sigstore/rekor/pkg/generated/client/index" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/util" ) type searchCmdOutput struct { UUIDs []string } func (s *searchCmdOutput) String() string { return strings.Join(s.UUIDs, "\n") + "\n" // one extra /n to terminate the list } func addSearchPFlags(cmd *cobra.Command) error { cmd.Flags().Var(NewFlagValue(pkiFormatFlag, ""), "pki-format", "format of the signature and/or public key") cmd.Flags().Var(NewFlagValue(fileOrURLFlag, ""), "public-key", "path or URL to public key file") cmd.Flags().Var(NewFlagValue(fileOrURLFlag, ""), "artifact", "path or URL to artifact file") cmd.Flags().Var(NewFlagValue(shaFlag, ""), "sha", "the SHA512, SHA256 or SHA1 sum of the artifact") cmd.Flags().Var(NewFlagValue(emailFlag, ""), "email", "email associated with the public key's subject") cmd.Flags().Var(NewFlagValue(operatorFlag, ""), "operator", "operator to use for the search. supported values are 'and' and 'or'") return nil } func validateSearchPFlags() error { artifactStr := viper.GetString("artifact") publicKey := viper.GetString("public-key") sha := viper.GetString("sha") email := viper.GetString("email") if artifactStr == "" && publicKey == "" && sha == "" && email == "" { return errors.New("either 'sha' or 'artifact' or 'public-key' or 'email' must be specified") } if publicKey != "" { if viper.GetString("pki-format") == "" { return errors.New("pki-format must be specified if searching by public-key") } } return nil } // searchCmd represents the get command var searchCmd = &cobra.Command{ Use: "search", Short: "Rekor search command", Long: `Searches the Rekor index to find entries by sha, artifact, public key, or e-mail`, PreRun: func(cmd *cobra.Command, args []string) { // these are bound here so that they are not overwritten by other commands if err := viper.BindPFlags(cmd.Flags()); err != nil { log.CliLogger.Fatal("Error initializing cmd line args: ", err) } if err := validateSearchPFlags(); err != nil { log.CliLogger.Error(err) _ = cmd.Help() os.Exit(1) } }, Run: format.WrapCmd(func(args []string) (interface{}, error) { log := log.CliLogger rekorClient, err := client.GetRekorClient(viper.GetString("rekor_server"), client.WithUserAgent(UserAgent()), client.WithRetryCount(viper.GetUint("retry")), client.WithLogger(log)) if err != nil { return nil, err } params := index.NewSearchIndexParams() params.SetTimeout(viper.GetDuration("timeout")) params.Query = &models.SearchIndex{} artifactStr := viper.GetString("artifact") sha := viper.GetString("sha") if sha != "" { params.Query.Hash = util.PrefixSHA(sha) } else if artifactStr != "" { hasher := sha256.New() var tee io.Reader if isURL(artifactStr) { /* #nosec G107 */ resp, err := http.Get(artifactStr) if err != nil { return nil, fmt.Errorf("error fetching '%v': %w", artifactStr, err) } defer resp.Body.Close() tee = io.TeeReader(resp.Body, hasher) } else { file, err := os.Open(filepath.Clean(artifactStr)) if err != nil { return nil, fmt.Errorf("error opening file '%v': %w", artifactStr, err) } defer func() { if err := file.Close(); err != nil { log.Error(err) } }() tee = io.TeeReader(file, hasher) } if _, err := io.ReadAll(tee); err != nil { return nil, fmt.Errorf("error processing '%v': %w", artifactStr, err) } hashVal := strings.ToLower(hex.EncodeToString(hasher.Sum(nil))) params.Query.Hash = "sha256:" + hashVal } params.Query.Operator = viper.GetString("operator") publicKeyStr := viper.GetString("public-key") if publicKeyStr != "" { params.Query.PublicKey = &models.SearchIndexPublicKey{} pkiFormat := viper.GetString("pki-format") switch pkiFormat { case "pgp": params.Query.PublicKey.Format = swag.String(models.SearchIndexPublicKeyFormatPgp) case "minisign": params.Query.PublicKey.Format = swag.String(models.SearchIndexPublicKeyFormatMinisign) case "x509": params.Query.PublicKey.Format = swag.String(models.SearchIndexPublicKeyFormatX509) case "ssh": params.Query.PublicKey.Format = swag.String(models.SearchIndexPublicKeyFormatSSH) case "tuf": params.Query.PublicKey.Format = swag.String(models.SearchIndexPublicKeyFormatTUF) default: return nil, fmt.Errorf("unknown pki-format %v", pkiFormat) } splitPubKeyString := strings.Split(publicKeyStr, ",") if len(splitPubKeyString) == 1 { if isURL(splitPubKeyString[0]) { params.Query.PublicKey.URL = strfmt.URI(splitPubKeyString[0]) } else { keyBytes, err := os.ReadFile(filepath.Clean(splitPubKeyString[0])) if err != nil { return nil, fmt.Errorf("error reading public key file: %w", err) } params.Query.PublicKey.Content = strfmt.Base64(keyBytes) } } else { return nil, errors.New("only one public key must be provided") } } emailStr := viper.GetString("email") if emailStr != "" { params.Query.Email = strfmt.Email(emailStr) } resp, err := rekorClient.Index.SearchIndex(params) if err != nil { switch t := err.(type) { case *index.SearchIndexDefault: if t.Code() == http.StatusNotImplemented { return nil, fmt.Errorf("search index not enabled on %v", viper.GetString("rekor_server")) } return nil, err default: return nil, err } } if len(resp.Payload) == 0 { return nil, fmt.Errorf("no matching entries found") } if viper.GetString("format") != "json" { fmt.Fprintln(os.Stderr, "Found matching entries (listed by UUID):") } return &searchCmdOutput{ UUIDs: resp.GetPayload(), }, nil }), } func init() { initializePFlagMap() if err := addSearchPFlags(searchCmd); err != nil { log.CliLogger.Fatal("Error parsing cmd line args:", err) } rootCmd.AddCommand(searchCmd) } rekor-1.3.5/cmd/rekor-cli/app/state/000077500000000000000000000000001455727245600172155ustar00rootroot00000000000000rekor-1.3.5/cmd/rekor-cli/app/state/state.go000066400000000000000000000037731455727245600206760ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package state import ( "encoding/json" "errors" "os" "path/filepath" "github.com/mitchellh/go-homedir" "github.com/sigstore/rekor/pkg/util" ) type persistedState map[string]*util.SignedCheckpoint func Dump(key string, sth *util.SignedCheckpoint) error { if sth.Size == 0 { return errors.New("do not persist state for empty logs") } rekorDir, err := getRekorDir() if err != nil { return err } statePath := filepath.Join(rekorDir, "state.json") state := loadStateFile() if state == nil { state = make(persistedState) } state[key] = sth b, err := json.Marshal(&state) if err != nil { return err } return os.WriteFile(statePath, b, 0600) } func loadStateFile() persistedState { rekorDir, err := getRekorDir() if err != nil { return nil } fp := filepath.Join(rekorDir, "state.json") b, err := os.ReadFile(filepath.Clean(fp)) if err != nil { return nil } result := persistedState{} if err := json.Unmarshal(b, &result); err != nil { return nil } return result } func Load(key string) *util.SignedCheckpoint { if state := loadStateFile(); state != nil { return state[key] } return nil } func getRekorDir() (string, error) { home, err := homedir.Dir() if err != nil { return "", err } rekorDir := filepath.Join(home, ".rekor") if _, err := os.Stat(rekorDir); os.IsNotExist(err) { if err := os.MkdirAll(rekorDir, 0750); err != nil { return "", err } } return rekorDir, nil } rekor-1.3.5/cmd/rekor-cli/app/tests/000077500000000000000000000000001455727245600172375ustar00rootroot00000000000000rekor-1.3.5/cmd/rekor-cli/app/tests/alpine.json000066400000000000000000000106701455727245600214060ustar00rootroot00000000000000{"kind":"alpine","apiVersion":"0.0.1","spec":{"package":{"content":"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"},"publicKey":{"content":"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUExeUhKeFFnc0hRUkVjbFF1NE9oZQpxeFR4ZDF0SGNObnZuUVR1L1VyVGt5OHdXdmdYVCtqcHZlcm9lV1duem1zWWxESTkzZUxJMk9SYWt4YjNnQTJPClEwUnk0d3M4dmhheExRR0M3NHVRUjUrL3lZckx1VEt5ZEZ6dVBhUzFkSzE5cUpQWEI4R01kbUZPaWpuWFg0U0EKaml4dUhMZTFXVzdrWlZ0akw3bnVmdnBYa1dCR2pzZnJ2c2tkTkEvNU1meEFlQmJxUGdhcTBRTUVmeE1BbjYvUgpMNWtOZXBpL1ZyNFMzOVh2ZjJEeldrVExFSzhwY25qTmt0OS9hYWZoV3FGVlc3bTNIQ0FJSTZoL3FsUU5RS1NvCkd1SDM0UThHc0ZHMzBpelVFTlY5YXZZN2hTTHE3bmdnc3ZrbmxOQlp0RlVjbUdvUXJ0eDNGbXlZc0lDOC9SK0IKeXdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="}}} rekor-1.3.5/cmd/rekor-cli/app/tests/helm.json000066400000000000000000000066131455727245600210650ustar00rootroot00000000000000{ "kind": "helm", "apiVersion": "0.0.1", "spec": { "publicKey": { "content": "LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tCgptUUVOQkdEWDdyY0JDQUNkSERBb1ljNFp4cnhhVk5ma1BoSHdOalVES01Ua1FJQy9sYVU2NHlVYVI0WnlIdTlXCjVPSDFkVTYreUNGZXlzeU5wd2EwdjE3MDFRNmdvSmtkNEJMVk5LN0pFS1RwN2dIUHpuU0prZEZzRkFON1A3Z3EKVUIrWkpnVlJyOWc5RjgzZk5KWGZ6UzVEVnhDNjkxeW4xTzVmbTlzR2xYUllzcWhrdHVoT1g4Q0tnUXBkdllOdwphTzhnZmRLMnRCNWVic2s3eUpVdldqMGgxMmZuUWd4UmxCdENpaE44SGYrdG1YVnpLWlpiMjd0TjBTZmRYbWhYCmFTdUhOTVV1blMvaHozeGpjM3JYa2FXWjRsU3FxWk9YMDBDMGhTUklleTRMNnZOQkhpNzVvRnpLRkNlc1pjaEIKVlNHcy8vaVBsbFFNSGlWR0d5WFJ1T3ZvQzFCam5EVDg0V0lkQUJFQkFBRzBERzV2ZEVCeVpXRnNMbU52YllrQgpWQVFUQVFnQVBoWWhCSTFvL1dQMUxjOStxbkpXVzhQSUtLKzJhQndYQlFKZzErNjNBaHNEQlFrRHdtY0FCUXNKCkNBY0NCaFVLQ1FnTEFnUVdBZ01CQWg0QkFoZUFBQW9KRU1QSUtLKzJhQndYbFE4SUFKUHBramlBQ3Q5YWFKSUYKRG41RFlWdTJXRXA5NkZ0OVN3VjRFZ2UyY1BLUXk4SlZ0eXdlU0YrbGdsbDBOMHIvRzN1YWptdWFBR3hHbzl4NAppdWhSZ0dlRFdoZU85VEZLU01MYjBGajdGeVJnMVhPSElYdWpoN1dsbm5ZVm5HM2hnOExZaHpKZW5meU5MQXNPCjZDdEVSMm5hYWRoSk1iS3doTmw2WDg2SUx0akMrTEt5UVBOSzlFQ01WVXdaSFVMekVza29KTHNPRlc2WS9JMkYKcThxb0FTUUs3ZG1NMFpKSEFrd1pUZUpnQXhRdkJRdEhrVEE3THhCWE5ZWGJFNElhTUFVV015TDlybmxrVW41ZQppQXI5VytPQXlVUHdTOFduNXMvYXMwWUc3dW9GdmZBWWp4NURMc0wxTGYzVUJ1MUZYTk52aGczMFdha1RFRFlwCmt5VE8zUUc1QVEwRVlOZnV0d0VJQU1wcGxaeXlIaGNJK013OE1XeUtvdlJxSG9hRVVlRzc1NFhTNWMvWUVpK1QKaDU1ZzRCa1dmMFRxK1paMUMwUUdkTC9oNnV6S2VEL2h3cVJKazZ0U0VkV0VWMTFzNmFnbFM3cHM5WjNrYWR5OAo3RGl3REM3R09QdWVWUnRMZnlqUUNHZnB1b0tyaURFVjE1NVcxM0JPVEdpTWluOWV2V2ZVVGVaNVhGTzVVSTVTCkNIRFk4dS96a2VwVkZIRS92TS9HWDVlbzJOVGtlWldCd3pEdkFkMHBtaEx5Q0t4ZkpkTEFTSUtqU21GMmFQbnUKV0NYVDczMkFqM0pZMnZMNlozazFyRlQzZ2JTY1RGRkwrSHh5dFA1UUFvUjBJd1ZGL1E1Rncyd0tkLzFQSHQ4SQp1d0RkMmVxQnJ0MzJKUFlTdWdtZVpuRVpGRFNJWVh5dWk0akZGaXBmMHZFQUVRRUFBWWtCUEFRWUFRZ0FKaFloCkJJMW8vV1AxTGM5K3FuSldXOFBJS0srMmFCd1hCUUpnMSs2M0Foc01CUWtEd21jQUFBb0pFTVBJS0srMmFCd1gKWTZnSC8ycDZFS0VBMFhubkljU2FlMnVtcTFYejM5SXZRSE9jdlJQZmd5YnRNZDJ1VnFodXAxN09OaE9Ya3pwYQpxeGxURjNkakR6dTRlaXNLNVhwZUMvNWRIbm96SFBpZXkzQXFPdlZwZFBQenZmYjhPOHBBU3UrdW8rZUUwLzlHCmR3dzQwMU9qTUtpM25WZnZMaXRjdHRjY2JzeldvTXBDR3liSnZvS0JoNGJzalNmZWoxT1hvckVKTkFUWGF6bE4KQ1ZVaEJORHo4bUIrZTNEbXovM3Z6UXJ5ZmtzaTBMeFUxWXBYMWYyc1d4TXE4a0xwM3lKV25qUklZSlE5QTdWcwpITXY2NWViS2xmcG9VR0N0V2lLRDJiRjVseXNGb2lMK2dETnpzYmI5eWxXZmxqbVlPRncrd05DRnZmL3lIWXhYCnhmbXJNZFowbmJ2UmZqdU1yMjdlT1kvdk8rTT0KPUlxSHUKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQo=" }, "chart": { "provenance": { "content": "LS0tLS1CRUdJTiBQR1AgU0lHTkVEIE1FU1NBR0UtLS0tLQpIYXNoOiBTSEE1MTIKCmFwaVZlcnNpb246IHYyCmRlc2NyaXB0aW9uOiBUZXN0IEhlbG0gQ2hhcnQKbmFtZTogdGVzdAp0eXBlOiBhcHBsaWNhdGlvbgp2ZXJzaW9uOiAwLjEuMAoKLi4uCmZpbGVzOgogIHRlc3QtMC4xLjAudGd6OiBzaGEyNTY6NmRlYzdlYTIxZTY1NWQ1Nzk2YzFlMjE0Y2ZiNzViNzM0MjhiMmFiZmEyZTY2YzhmN2JjNjRmZjRhN2IzYjI5ZgotLS0tLUJFR0lOIFBHUCBTSUdOQVRVUkUtLS0tLQoKd3NCY0JBRUJDZ0FRQlFKZzEvNzFDUkREeUNpdnRtZ2NGd0FBTUVjSUFCYVJGcUhoYjZpYlFxSUM3eGtuMFU3agpTREVLb0RQdDdsS0psQldlLzlRMWhFMUw3QXNsY3prQ0JCZExJNjdsSEpuRVFINmRvVDJuNnd3M3lvSkp3N2d3Cnc0WmtOcWhkWUFZYllERThGbHdnTWRpM1ZxUHJDQUJaQTJHOXpoOVBjbWpyUzFTK0dyem51aksyNk9MQTNvb2MKZ2lCVExKNXdzVnVQMmFRcHIrYjNjeks0cExtV2pzMWxEeXdvRXRwdHhnSytPb3l0MlhxVHprbVdEWnZTaVlFUQp2cmJiNzFtdVBiSlZuMCsxbVc4OEV5QUNvVWlxTlpMdTE1a3lOZ1NaQzZCeS9DNThSUGF0WXo5RGlleVpvU0lRCmtKZVRYL05lSGZ3QkhicHk3ZmVuRGZuTWJqNExxanZMeGV5b2Q3TmMvNTdkVWVzaW1SKysyWkM1QTRia3ErYz0KPUJOVGsKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0t" } } } }rekor-1.3.5/cmd/rekor-cli/app/tests/intoto_dsse.json000066400000000000000000000010201455727245600224550ustar00rootroot00000000000000{"payloadType":"application/vnd.in-toto+json","payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsInN1YmplY3QiOlt7Im5hbWUiOiJmb29iYXIiLCJkaWdlc3QiOnsiZm9vIjoiYmFyIn19XSwicHJlZGljYXRlIjp7ImJ1aWxkZXIiOnsiaWQiOiJmb29ISzFiZ2Y1WC8xckNxZz09In0sImJ1aWxkVHlwZSI6IiIsImludm9jYXRpb24iOnsiY29uZmlnU291cmNlIjp7fX19fQ==","signatures":[{"keyid":"","sig":"MEQCIAIlnxHC3eU4jmUsqJExxfzqyy8bk+61btgnRiGcRDxgAiBwmdnJ/GX1yCYhYAvwAtkuYN0yFlVPQVAx9R6JpUUBiA=="}]}rekor-1.3.5/cmd/rekor-cli/app/tests/intoto_dsse.pem000066400000000000000000000002611455727245600222730ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEx+ikqUxXurlxZltajRBV2ju31j32 baT2ax2dXBcpInWaFESqGF35KISflP1EmMvEnfG+AzHecQ0WQp5QzNId+w== -----END PUBLIC KEY-----rekor-1.3.5/cmd/rekor-cli/app/tests/intoto_multi_dsse.json000066400000000000000000000016141455727245600237000ustar00rootroot00000000000000{"payloadType":"application/vnd.in-toto+json", "payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsInN1YmplY3QiOlt7Im5hbWUiOiJmb29iYXIiLCJkaWdlc3QiOnsiZm9vIjoiYmFyIn19XSwicHJlZGljYXRlIjp7ImJ1aWxkZXIiOnsiaWQiOiJmb29BNi9QWW1CNmNCQXRZQT09In0sImJ1aWxkVHlwZSI6IiIsImludm9jYXRpb24iOnsiY29uZmlnU291cmNlIjp7fX19fQ==", "signatures":[{ "keyid":"","sig":"MEUCIQC4/1MwOahpGT/oh6DZFuwZiG9yXpfo22wh+7mGn9lQ4gIgIL2LFt0lxYbFngf4ze/FNvkybYzwtmkX2XrcnGMpOqo="}, {"keyid":"","sig":"PhNAdsvKEVqv+cF/QvjEOz+fTtoNWQfa9gCWnOpm5yWVFRiu6he5jvw6A8ESRXxV3KnEcyBFCfCbITNK2fpXQEOc0gNDcsil1m/Pzv/JonhtH/TwIjdJ8zy4WEUVHZMfj5IIeibp3U2ACvmCc9HEUPCc6VM0hq7ri/VnKLcCCGbKmxwHrVXArv1hKBrcP7s52tuxCsVr5+3Z7eKPx5WkycOhpUhSVhMHjOCj9e3mveiw4dYuwdrgQHtqZJhUg0WFlVCQTQdcLxIII1g7BudA38yzfOgkbwgfoZ2Dh3iF7uRHIUdFxxpqF2oQeU2uDv3dT9cCfRd/kDtU6hbuVgixgw=="}] } rekor-1.3.5/cmd/rekor-cli/app/tests/intoto_multi_pub2.pem000066400000000000000000000007031455727245600234200ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3wqI/TysUiKTgY1bz+wd JfEOil4MEsRASKGzJddZ6x9hb+rn2UVoJmuxN62XI0TMoMn4mukgfCgY6jgTB58V +/LaeSA8Wz1p4gOxhk1mcgbF4HyxR+xlRgYfH4iSbXy+Ez/8ZjM2OO68fKr4JZEA 5LXZkhJr32JqH+UiFw/wgSPWA8aV0AfRAXHdekJ48B1ChxJTrOJWSPTnj/E0lfLV srJKtXDuC8T0vFmVU726tI6fODsEE6VrSahvw1ENUHzI34sbfrmrggwPO4iMAQvq wu2gn2lx6ajWsh806FItiXN+DuizMnx4KMBI0IJynoQpWOFbstGiV0LygZkQ6soz vwIDAQAB -----END PUBLIC KEY----- rekor-1.3.5/cmd/rekor-cli/app/tests/rekor.json000066400000000000000000000105011455727245600212510ustar00rootroot00000000000000{ "kind": "rekord", "apiVersion": "0.0.1", "spec": { "signature": { "format": "pgp", "content": "iQHKBAABCAA0FiEEcgCUXG78adj6hGUJJrfBoJ04pHoFAl+86RwWHGxoaW5kc0Bwcm90b25tYWlsLmNvbQAKCRAmt8GgnTikejcHC/9yyGEPh2D+MnNR8I8w0sfWChc6pGAQoS6qk/sfC/9GvF4OC7RIy6OwLr/lxyEZbOP2ngYjh/s5KjKxhZyApwwg13LmcbazGnXc3E76J55LoTfwoRa9fupH/M6HI56VFKwnu+AbMNW1s+DM47r7i5nIN6IX9kMpDe3B9XTUULff/yNUv0XtXU+VAf8ndF1w117YVWxf8TnU/HWvX74URQPN+syuyqK/NO1H1KhBVTzcIYd5H6kJu300jgkDypyyqQpd/pJYVwfeY8fCOaeCpfIPjKQ/4enCsAeBgKsAwfIbor8WiE86KoANYqROaW7uqiN+VPadbWVeN6bMpRIdEq8+NKQGlepSCRqbkVg4VKGOPgB3h5WbY9U1O1FVDnXyt7kWdEPEZjBX+V4DawshvNe5LIyqH5hJ1QNAFd0UStqKQt8EUZ/gAtQiXSGbxM1ACoYL9HblKW5b+kj/onKghekFoCoAfhMwRRqR5g/TS/Pc2/ztwYTIuhpQQfMXziTm64g=", "publicKey": { "content":"LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tCgptUUdOQkYrY0lNMEJEQUNhOEc3UkQydjNtaXdNdHhWYVppM0pCVnVlVkFxSEtDNGVLb01TNUhNQ1JvK0haVlJBCjcwWG1zVHBYMVoxZ1pRdXVDMEdEWTI2aEJoZWpBcTNoeDJydjYvOHE5MEJ2V0dIOXRWZUdwTDFzYUltNTJnRVIKWHlWZ2d6NWtBQzBTNnZNbjdkcjJldEJrV1dQK09qMDVTMDJZWkJUWWd4cE9ieWVjVVNjcUtOVGpzbFpRQkgyZApTSHVrM28yWjdoTTQ5VTBsN3piV3c0b0lUK2xBUmNzYWpRVHdXamxpYVBEL0hSalQyblJPaEloaXRlZC93Z3l6CnlkSXE1ZTZzMThWTGNUNzVxV25yWlhOUFdGd2YyNVJYWTN1dGtXK0dXNW5RZU44MFEya1JFZ2t4RnM1QWQ1V1oKdkU3dDgvaHg1em1zbFo0dGZGNHNpM1FaZUlRQmFjWWJ3eE1QU0RmOW9GR3hkR0ZUODg5d0pMR2dXbXIxVGtQTQpjTjA2d3hBUkd0eE4wejYxRkpUaWpMV1JialczdW5JOWhjUWNVbE4vUSsxNm90SHBlS1ZnNG9XMDBDcnZXT0Q2CnFrTGNNRDQ5eVVEOGZTR0IrUkVuaUJhODlDOWtRVTRTS2Rnc0xML1ErSksrU3k5S21JRHJtMWE0RGZQMXBzZmUKTGphcnpzVlpmS1VIZndjQUVRRUFBYlFpVEhWclpTQklhVzVrY3lBOGJHaHBibVJ6UUhCeWIzUnZibTFoYVd3dQpZMjl0UG9rQjFBUVRBUWdBUGhZaEJISUFsRnh1L0duWStvUmxDU2Ezd2FDZE9LUjZCUUpmbkNETkFoc0RCUWtECndtY0FCUXNKQ0FjQ0JoVUtDUWdMQWdRV0FnTUJBaDRCQWhlQUFBb0pFQ2Ezd2FDZE9LUjZaMWtMLzFJSzB2ZGUKWlg1cjVTZWJOeFRJTlNBQXZZa3JLUnlKNWY3bE9NOWdMR0l1YzJGb05VbmpWUVQwcklHOTAxOWg0OHBDeTkxZgpYakREUk1ZOWd6RldXQ2dHblhoMWhXSTNNN0JKRjZZRTZ1NkRYR3N2dVVwR3JOZVpBRzZra2F6QXVBbm5WMGtDCjA4em9SckFaQ3ZscGFacnlkOGl0YityVitRS3A3QXcybEFJSDFlNmR3TTRSTEZqdmZrOExKWHhqSkFvUG13NmwKTHcxOGM3b1c2UkxPOVFYUThlTTZyMnZISHBtMFR1ZHZaeWFmTnVDMzJHRGxNWTR1MFYxRGI4THN5bVBzQWh1QQoySno0L0tQcTZ1S3dJdG1WSzRwbmRmRUR1NkQxVG9vRFlYaXB0WWFmZHZVMzNwVVF4d0hvZlRUZkU1elp3MlBlCmxIM25aZHNnSFhHUHhKTExNcU9wVzRDL2NNNlpRVmdZU3RWcjBudlU2NitRalF2c2tVWlIwNmRkRXpuQnBHSnMKdHBtajlBZS9HUlk4RU5uTjkvMkdmRXVydHozZEtOVVpvak15MTUzamNHMFUxenpoMTE1V0o3dDh3SEJ1NFM0cAowZ0UrUkFxeXRBY0laRGQyTlNOcno4VnI5RkU5eCtmYXQ5RVJsYm5kQUJFNWlWOHNLMCtGYW5Xd2dia0JqUVJmCm5DRE5BUXdBdEJvdGhmY1J6cjN4cjNQOXA3UUNNd0t1aW9udk1DbThXZ3dOUzRDcGhxbzVOT3IyaU1qa0xQMEoKb21nSkxWWDVOK2Jydjh5NEg4cllQd0tCMTZvL2hBOEliR2JwWXltM0ZjeWtUd2NiV2J0UFRMRXRkQ1VQTFlURApOQzVMR0pwZzNlODZZZlF0QU42L01uWnlZT21sRHgyV0d0dExkbXNBU0dWdXg2QVZKcUl2K3gwNlVLSkVtSzN0CmpsRVZLeWcxMlJFenllNUlUNnFFU0dwT3pvMllsV1VxSVR3L0FhUFEyWnhVYXh2WUZvVU9jd2djZG5Ia2dzaEkKT245aC9OSFVtUDMyV1F2cWtRTXVVYVBJTlJzQzgzS3ZUREdseWZTSFZGek1hNGhETWhFY1h6NGFjaW5kNVdUZQp6eUxnWmhPYjdjTmVDeDR4Y3J0UEI2VTdCUi9GVkx6TEJsQXp1emppRWhZd0pvM0FPTXFGb1I1bUFxaGx1dE5PCnNzeW9mYnFUZ0diU0xkamJYUC9hRXRnejJNVjluL29jMVNCOEhlWk8vMTdKeWduenJ1SUt5Ky9sT1dPenQralYKVkZwVnloMXVlOGxGN3ltS1I0dHNsK2lJVmJxblB2cE1oTE9JQnFYRm4yZ01Da0dvSkx5N09IbzJXQUVKR2x0MwpTd3BicmpqMUFCRUJBQUdKQWJ3RUdBRUlBQ1lXSVFSeUFKUmNidnhwMlBxRVpRa210OEdnblRpa2VnVUNYNXdnCnpRSWJEQVVKQThKbkFBQUtDUkFtdDhHZ25UaWtlaW5pREFDRUFma1pxLzRScDJhTkE0ZGJvSjdVRlhET2FSa1YKOU1Lb0VaRnFUTU5vdkRMNXhoTWxnbFBQdS9sK2RoVGd4ZGVKOUVWSG9lenRiODk2VS9wT3VCUnNuOVZ0VzRZLwpqZWlXN0V5TlhBZC9PcnZuRmJ4KzdpWExxdXBaSkpGVGkvajlSaFZZTnNtbDdzZWJUUGVCbkdEQTkxcWJDNHhICnBRVkRDdWp4NjlWeE81RTFMU29oQ00rTy81dkxCbThpMW8vbmJGbWJ5N1ZDeUtlUkRmaHRmOW5DODRxc0U5R3EKVTcvTFNpazliZnhNV2JwcTh5a250bVMzYTBzemM0YlZGcGV6QnBtTmIwQVZjQitUbTlnV21FemhpTHM2RktBTgpJbnFOdVh1Qkw5UENhYzcrbVUrYzJtQmdHT1JHZDFkWk8zUkM4OXpGM3hCQlluQ09lNWNBTUZsYzFYR3NsbHNJCmR6ZHJkWHZiTkJ6L2o3MXB1TjhvRlltL1hiVmNpZU8wVGZRaURjVHQ4S2lpUjlUQUQ5L1A1OTNSTWxMT0dTOHAKaHZKYmlGb1pmWEhjbHNaRkhtOERRUWE5NElad1RCOG00Z0JWME0yWFN2ZEhvMzBsc3FqdFphWmlTclJoNHJzaApuMTRwYkFhVGRhS0VQY3Z0dWZiVXVXMElqWWQya3BJVC90Zz0KPU9naHIKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQo=" } }, "data": { "url": "https://raw.githubusercontent.com/sigstore/rekor/main/tests/test_file.txt", "hash": { "algorithm": "sha256", "value": "45c7b11fcbf07dec1694adecd8c5b85770a12a6c8dfdcf2580a2db0c47c31779" } } } } rekor-1.3.5/cmd/rekor-cli/app/tests/rpm.json000066400000000000000000006671351455727245600207520ustar00rootroot00000000000000{"kind":"rpm","apiVersion":"0.0.1","spec":{"package":{"content":"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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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AAAQAAAAAAByb290AHJvb3QAcm9vdAByb290AHJvb3QAcm9vdAByb290AHJvb3QATmV0d29ya01hbmFnZXItMS4yNi4wLTguZWw4LnNyYy5ycG0A/////////////////////05ldHdvcmtNYW5hZ2VyLWJsdWV0b290aABOZXR3b3JrTWFuYWdlci1ibHVldG9vdGgoeDg2LTY0KQBsaWJubS1kZXZpY2UtcGx1Z2luLWJsdWV0b290aC5zbygpKDY0Yml0KQAAAAAIAAAACAAAAAwAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAABAAAKAQAACgEAAAoBAAAKAABAAE5ldHdvcmtNYW5hZ2VyKHg4Ni02NCkATmV0d29ya01hbmFnZXItd3dhbgBibHVlegBsaWJibHVldG9vdGguc28uMygpKDY0Yml0KQBsaWJjLnNvLjYoKSg2NGJpdCkAbGliYy5zby42KEdMSUJDXzIuMTQpKDY0Yml0KQBsaWJjLnNvLjYoR0xJQkNfMi4yLjUpKDY0Yml0KQBsaWJjLnNvLjYoR0xJQkNfMi40KSg2NGJpdCkAbGliZGwuc28uMigpKDY0Yml0KQBsaWJnY2Nfcy5zby4xKCkoNjRiaXQpAGxpYmdjY19zLnNvLjEoR0NDXzMuMCkoNjRiaXQpAGxpYmdjY19zLnNvLjEoR0NDXzMuMy4xKSg2NGJpdCkAbGliZ2lvLTIuMC5zby4wKCkoNjRiaXQpAGxpYmdsaWItMi4wLnNvLjAoKSg2NGJpdCkAbGliZ21vZHVsZS0yLjAuc28uMCgpKDY0Yml0KQBsaWJnb2JqZWN0LTIuMC5zby4wKCkoNjRiaXQpAGxpYm1tLWdsaWIuc28uMCgpKDY0Yml0KQBsaWJubS13d2FuLnNvKCkoNjRiaXQpAGxpYnB0aHJlYWQuc28uMCgpKDY0Yml0KQBsaWJwdGhyZWFkLnNvLjAoR0xJQkNfMi4yLjUpKDY0Yml0KQBsaWJzeXN0ZW1kLnNvLjAoKSg2NGJpdCkAcnBtbGliKENvbXByZXNzZWRGaWxlTmFtZXMpAHJwbWxpYihGaWxlRGlnZXN0cykAcnBtbGliKFBheWxvYWRGaWxl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IEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS4yNi4wLTEAQmVuaWFtaW5vIEdhbHZhbmkgPGJnYWx2YW5pQHJlZGhhdC5jb20+IC0gMToxLjI2LjAtMC4yLjEAQmVuaWFtaW5vIEdhbHZhbmkgPGJnYWx2YW5pQHJlZGhhdC5jb20+IC0gMToxLjI2LjAtMC4yAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuMjYuMC0wLjEAQmVuaWFtaW5vIEdhbHZhbmkgPGJnYWx2YW5pQHJlZGhhdC5jb20+IC0gMToxLjI1LjItMQBUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjI1LjEtMQBUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjIyLjgtNABCZW5pYW1pbm8gR2FsdmFuaSA8YmdhbHZhbmlAcmVkaGF0LmNvbT4gLSAxOjEuMjIuOC0zAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuMjIuOC0yAEFudG9uaW8gQ2FyZGFjZSA8YWNhcmRhY2VAcmVkaGF0LmNvbT4gLSAxOjEuMjIuOC0xAEFudG9uaW8gQ2FyZGFjZSA8YWNhcmRhY2VAcmVkaGF0LmNvbT4gLSAxOjEuMjIuNi0xAEJlbmlhbWlubyBHYWx2YW5pIDxiZ2FsdmFuaUByZWRoYXQuY29tPiAtIDE6MS4yMi40LTEAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS4yMi4yLTEAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS4yMi4wLTIAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS4yMi4wLTEAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS4yMi4wLTAuMgBCZW5pYW1pbm8gR2FsdmFuaSA8YmdhbHZhbmlAcmVkaGF0LmNvbT4gLSAxOjEuMjIuMC0wLjEATHVib21pciBSaW50ZWwgPGxyaW50ZWxAcmVkaGF0LmNvbT4gLSAxOjEuMjAuMC00AFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuMjAuMC0zAEx1Ym9taXIgUmludGVsIDxscmludGVsQHJlZGhhdC5jb20+IC0gMToxLjIwLjAtMgBUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjIwLjAtMQBUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjIwLjAtMC40AEx1Ym9taXIgUmludGVsIDxscmludGVsQHJlZGhhdC5jb20+IC0gMToxLjIwLjAtMC4zAEx1Ym9taXIgUmludGVsIDxscmludGVsQHJlZGhhdC5jb20+IC0gMToxLjIwLjAtMC4yAEx1Ym9taXIgUmludGVsIDxscmludGVsQHJlZGhhdC5jb20+IC0gMToxLjIwLjAtMC4xAEJlbmlhbWlubyBHYWx2YW5pIDxiZ2FsdmFuaUByZWRoYXQuY29tPiAtIDE6MS4xNC4wLTE0AEZyYW5jZXNjbyBHaXVkaWNpIDxmZ2l1ZGljaUByZWRoYXQuY29tPiAtIDE6MS4xNC4wLTEzAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuMTQuMC0xMgBCZW5pYW1pbm8gR2FsdmFuaSA8YmdhbHZhbmlAcmVkaGF0LmNvbT4gLSAxOjEuMTQuMC0xMQBCZW5pYW1pbm8gR2FsdmFuaSA8YmdhbHZhbmlAcmVkaGF0LmNvbT4gLSAxOjEuMTQuMC0xMABUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjE0LjAtOQBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjE0LjAtOABUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjE0LjAtNwBUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjE0LjAtNgBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjE0LjAtNQBCZW5pYW1pbm8gR2FsdmFuaSA8YmdhbHZhbmlAcmVkaGF0LmNvbT4gLSAxOjEuMTQuMC00AFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuMTQuMC0zAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuMTQuMC0yAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuMTQuMC0xAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuMTQuMC0wLjQAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS4xNC4wLTAuMwBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjE0LjAtMC4yAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuMTQuMC0wLjEATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS4xMi4wLTAuNABUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjEyLjAtMC4zAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuMTIuMC0wLjIAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS4xMi4wLTAuMQBUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjEwLjItMQBCasO2cm4gRXNzZXIgPGJlc3NlcjgyQGZlZG9yYXByb2plY3Qub3JnPiAtIDE6MS44LjQtNwBUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjguNC02AEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuOC40LTUAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS44LjQtNABUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjguNC0zAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuOC40LTIAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS44LjQtMQBGZWRvcmEgUmVsZWFzZSBFbmdpbmVlcmluZyA8cmVsZW5nQGZlZG9yYXByb2plY3Qub3JnPiAtIDE6MS44LjItMy4yAEZlZG9yYSBSZWxlYXNlIEVuZ2luZWVyaW5nIDxyZWxlbmdAZmVkb3JhcHJvamVjdC5vcmc+IC0gMToxLjguMi0zLjEATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS44LjItMwBTdGVwaGVuIEdhbGxhZ2hlciA8c2dhbGxhZ2hAcmVkaGF0LmNvbT4gLSAxOjEuOC4yLTIAQmVuaWFtaW5vIEdhbHZhbmkgPGJnYWx2YW5pQHJlZGhhdC5jb20+IC0gMToxLjguMi0xAEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuOC4wLTYATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS44LjAtNQBUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjguMC00AFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuOC4wLTMAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS44LjAtMgBUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjguMC0xAEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuOC4wLTAuMi5yYzMATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS44LjAtMC4yLnJjMgBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjguMC0wLjEATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS42LjItMQBGZWRvcmEgUmVsZWFzZSBFbmdpbmVlcmluZyA8cmVsZW5nQGZlZG9yYXByb2plY3Qub3JnPiAtIDE6MS42LjAtMS4xAEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuNi4wLTEAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS42LTAuMi5yYzEATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS42LTAuMS5yYzEAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS41LjMtNQBJZ29yIEduYXRlbmtvIDxpZ25hdGVua29AcmVkaGF0LmNvbT4gLSAxOjEuNS4zLTQuMQBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjUuMy00AFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuNS4yLTQAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS41LjItMwBUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjUuMi0yAEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuNS4yLTEATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS40LjItMQBUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMToxLjQuMC00AFRob2FtcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuNC4wLTMAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS40LjAtMgBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjQuMC0xAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuNC4wLTAuNS5naXQyMDE2MDYyMS4wNzIzNThkYQBNYXR0aGlhcyBDbGFzZW4gPG1jbGFzZW5AcmVkaGF0LmNvbT4gLSAxOjEuNC4wLTAuNC5naXQyMDE2MDYyMS4wNzIzNThkYQBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjQuMC0wLjMuZ2l0MjAxNjA2MjEuMDcyMzU4ZGEAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS4yLjItMgBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjIuMi0xAEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuMi4wLTEATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS4yLjAtMC43LnJjMgBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjIuMC0wLjcucmMxAEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuMi4wLTAuOC5iZXRhMwBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjIuMC0wLjcuYmV0YTMATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS4yLjAtMC43LmJldGEyAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MS4yLjAtMC42LmJldGEyLjEATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS4yLjAtMC42LmJldGEyAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuMi4wLTAuNi5iZXRhMQBGZWRvcmEgUmVsZWFzZSBFbmdpbmVlcmluZyA8cmVsZW5nQGZlZG9yYXByb2plY3Qub3JnPiAtIDE6MS4yLjAtMC41LmJldGExLjEATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS4yLjAtMC41LmJldGExAERhdmlkIEtpbmcgPGFtaWdhZGF2ZUBhbWlnYWRhdmUuY29tPiAtIDE6MS4yLjAtMC40LjIwMTUxMDA3Z2l0ZTczZTU1YwBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjIuMC0wLjMuMjAxNTExMTJnaXRlYzRkNjUzAEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuMi4wLTAuMy4yMDE1MTAyM2dpdGUwMWMxNzUATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS4yLjAtMC4yLjIwMTUxMDA3Z2l0ZTczZTU1YwBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjIuMC0wLjIuMjAxNTA5MDNnaXRkZTVkOTgxAEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuMi4wLTAuMS4yMDE1MDkwM2dpdGRlNWQ5ODEATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS4wLjYtMgBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjAuNi0xAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAxOjEuMC42LTAuMi4yMDE1MDgxM2dpdDdlMmNhYTIATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS4wLjYtMC4xLjIwMTUwODEzZ2l0N2UyY2FhMgBMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjAuNC0yAEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuMC40LTEARGFuIEhvcsOhayA8ZGFuW2F0XWRhbm55LmN6PiAtIDE6MS4wLjQtMC41LmdpdDIwMTUwNzEzLjM4YmYyY2IwAEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuMC40LTAuNC5naXQyMDE1MDcxMy4zOGJmMmNiMABMdWJvbWlyIFJpbnRlbCA8bGt1bmRyYWtAdjMuc2s+IC0gMToxLjAuNC0wLjMuZ2l0MjAxNTA3MDcuZTNiZDRlMQBKacWZw60gS2xpbWXFoSA8amtsaW1lc0ByZWRoYXQuY29tPiAtIDE6MS4wLjQtMC4yLmdpdDIwMTUwNzA3LmNmMTVmMmEATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAtIDE6MS4wLjQtMC4xLmdpdDIwMTYwNjI0LmYyNDViNDlhAEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuMC40LTAuMS5naXQyMDE1MDYxOC44Y2ZmYWYzYmY1AEZlZG9yYSBSZWxlYXNlIEVuZ2luZWVyaW5nIDxyZWwtZW5nQGxpc3RzLmZlZG9yYXByb2plY3Qub3JnPiAtIDE6MS4wLjItMS4xAEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gLSAxOjEuMC4yLTEASmnFmcOtIEtsaW1lxaEgPGprbGltZXNAcmVkaGF0LmNvbT4gLSAxOjEuMC4xLTIuZ2l0MjAxNTA0MjkARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMToxLjAuMS0xLmdpdDIwMTUwMzA1AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MS4wLjAtNwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjEuMC4wLTYARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMToxLjAuMC01AEFkYW0gV2lsbGlhbXNvbiA8YXdpbGxpYW1AcmVkaGF0LmNvbT4gLSAxOjEuMC4wLTQAVGhvbWFzIEhhbGxlciA8dGhhbGxlckByZWRoYXQuY29tPiAtIDE6MS4wLjAtMwBEYW4gV2luc2hpcCA8ZGFud0ByZWRoYXQuY29tPiAtIDE6MS4wLjAtMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjEuMC4wLTEASmnFmcOtIEtsaW1lxaEgPGprbGltZXNAcmVkaGF0LmNvbT4gLSAxOjAuOS4xMC4wLTE0LmdpdDIwMTQwNzA0AEppxZnDrSBLbGltZcWhIDxqa2xpbWVzQHJlZGhhdC5jb20+IC0gMTowLjkuMTAuMC0xMy5naXQyMDE0MDcwNABEYW4gV2luc2hpcCA8ZGFud0ByZWRoYXQuY29tPiAtIDE6MC45LjEwLjAtMTIuZ2l0MjAxNDA3MDQATHVib21pciBSaW50ZWwgPGxrdW5kcmFrQHYzLnNrPiAxOjAuOS4xMC4wLTExLmdpdDIwMTQwNzA0AEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gMTowLjkuMTAuMC0xMC5naXQyMDE0MDcwNABBZGFtIFdpbGxpYW1zb24gPGF3aWxsaWFtQHJlZGhhdC5jb20+IC0gMTowLjkuMTAuMC05LmdpdDIwMTQwNzA0AEx1Ym9taXIgUmludGVsIDxsa3VuZHJha0B2My5zaz4gMTowLjkuMTAuMC04LmdpdDIwMTQwNzA0AFN0ZWYgV2FsdGVyIDxzdGVmd0ByZWRoYXQuY29tPiAtIDE6MC45LjEwLjAtNy5naXQyMDE0MDcwNABKacWZw60gS2xpbWXFoSA8amtsaW1lc0ByZWRoYXQuY29tPiAtIDE6MC45LjEwLjAtNi5naXQyMDE0MDcwNABQZXRlciBSb2JpbnNvbiA8cGJyb2JpbnNvbkBmZWRvcmFwcm9qZWN0Lm9yZz4gMTowLjkuMTAuMC01LmdpdDIwMTQwNzA0AERhbiBIb3LDoWsgPGRhblthdF1kYW5ueS5jej4gLSAxOjAuOS4xMC4wLTQuZ2l0MjAxNDA3MDQARmVkb3JhIFJlbGVhc2UgRW5naW5lZXJpbmcgPHJlbC1lbmdAbGlzdHMuZmVkb3JhcHJvamVjdC5vcmc+IC0gMTowLjkuMTAuMC0zLmdpdDIwMTQwNzA0LjEAS2FsZXYgTGVtYmVyIDxrYWxldmxlbWJlckBnbWFpbC5jb20+IC0gMTowLjkuMTAuMC0zLmdpdDIwMTQwNzA0AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC45LjEwLjAtMi5naXQyMDE0MDcwNABLYWxldiBMZW1iZXIgPGthbGV2bGVtYmVyQGdtYWlsLmNvbT4gLSAxOjAuOS4xMC4wLTEuZ2l0MjAxNDA3MDQuMQBUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMC45LjEwLjAtMS5naXQyMDE0MDcwNABUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMC45LjkuOTgtMS5naXQyMDE0MDYyMABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjkuOS45NS0xLmdpdDIwMTQwNjA5AEZlZG9yYSBSZWxlYXNlIEVuZ2luZWVyaW5nIDxyZWwtZW5nQGxpc3RzLmZlZG9yYXByb2plY3Qub3JnPiAtIDE6MC45LjkuMS02LmdpdDIwMTQwMzE5AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOS45LjEtNS5naXQyMDE0MDMxOQBEYW4gV2luc2hpcCA8ZGFud0ByZWRoYXQuY29tPiAtIDAuOS45LjEtNC5naXQyMDE0MDMxOQBKacWZw60gS2xpbWXFoSA8amtsaW1lc0ByZWRoYXQuY29tPiAtIDAuOS45LjEtMy5naXQyMDE0MDMxNwBKacWZw60gS2xpbWXFoSA8amtsaW1lc0ByZWRoYXQuY29tPiAtIDAuOS45LjEtMi5naXQyMDE0MDMxNABKacWZw60gS2xpbWXFoSA8amtsaW1lc0ByZWRoYXQuY29tPiAtIDAuOS45LjEtMS5naXQyMDE0MDMxMABUaG9tYXMgSGFsbGVyIDx0aGFsbGVyQHJlZGhhdC5jb20+IC0gMC45LjkuMS0wLmdpdDIwMTQwMjI4AFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAwLjkuOS4wLTI4LmdpdDIwMTQwMTMxAFRob21hcyBIYWxsZXIgPHRoYWxsZXJAcmVkaGF0LmNvbT4gLSAwLjkuOS4wLTI3LmdpdDIwMTQwMTMxAEppxZnDrSBLbGltZcWhIDxqa2xpbWVzQHJlZGhhdC5jb20+IC0gMC45LjkuMC0yNi5naXQyMDE0MDEzMQBKacWZw60gS2xpbWXFoSA8amtsaW1lc0ByZWRoYXQuY29tPiAtIDAuOS45LjAtMjUuZ2l0MjAxNDAxMTcASmnFmcOtIEtsaW1lxaEgPGprbGltZXNAcmVkaGF0LmNvbT4gLSAwLjkuOS4wLTI0LmdpdDIwMTQwMTE0AERhbiBXaW5zaGlwIDxkYW53QHJlZGhhdC5jb20+IC0gMC45LjkuMC0yMy5naXQyMDEzMTAwMwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjkuOS4wLTIyLmdpdDIwMTMxMDAzAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOS45LjAtMjEuZ2l0MjAxMzEwMDMARGFuIFdpbnNoaXAgPGRhbndAcmVkaGF0LmNvbT4gLSAwLjkuOS4wLTIwLmdpdDIwMTMxMDAzAEppxZnDrSBLbGltZcWhIDxqa2xpbWVzQHJlZGhhdC5jb20+IC0gMC45LjkuMC0xOS5naXQyMDEzMTAwMwBEYW4gV2luc2hpcCA8ZGFud0ByZWRoYXQuY29tPiAtIDAuOS45LjAtMTguZ2l0MjAxMzEwMDMARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC45LjkuMC0xNy5naXQyMDEzMTAwMwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjkuOS4wLTE2LmdpdDIwMTMxMDAzAEppxZnDrSBLbGltZcWhIDxqa2xpbWVzQHJlZGhhdC5jb20+IC0gMC45LjkuMC0xNS5naXQyMDEzMTAwMwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjkuOS4wLTE0LmdpdDIwMTMxMDAzAERhbiBXaW5zaGlwIDxkYW53QHJlZGhhdC5jb20+IC0gMC45LjkuMC0xMy5naXQyMDEzMTAwMQBCaWxsIE5vdHRpbmdoYW0gPG5vdHRpbmdAcmVkaGF0LmNvbT4gLSAwLjkuOS4wLTEyLmdpdDIwMTMwOTEzAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOS45LjAtMTEuZ2l0MjAxMzA5MTMARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC45LjkuMC0xMC5naXQyMDEzMDkwNgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjkuOS4wLTkuZ2l0MjAxMzA4MDcARGFuIFdpbnNoaXAgPGRhbndAcmVkaGF0LmNvbT4gLSAwLjkuOS4wLTguZ2l0MjAxMzA3MjQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC45LjkuMC03LmdpdDIwMTMwNzI0AERhbiBXaW5zaGlwIDxkYW53QHJlZGhhdC5jb20+IC0gMC45LjkuMC02AERhbiBXaW5zaGlwIDxkYW53QHJlZGhhdC5jb20+IC0gMC45LjkuMC01AEppxZnDrSBLbGltZcWhIDxqa2xpbWVzQHJlZGhhdC5jb20+IC0gMC45LjkuMC00LmdpdDIwMTMwNjAzAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOS45LjAtMy5naXQyMDEzMDYwMwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjkuOS4wLTIuZ2l0MjAxMzA1MTUARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC45LjkuMC0xLmdpdDIwMTMwNTE0AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOS44LjEtMi5naXQyMDEzMDUwNwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjkuOC4xLTEuZ2l0MjAxMzAzMjcASmnFmcOtIEtsaW1lxaEgPGprbGltZXNAcmVkaGF0LmNvbT4gLSAwLjkuOC4wLTEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC45LjcuOTk3LTIARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC45LjcuOTk3LTEARGFuIFdpbnNoaXAgPGRhbndAcmVkaGF0LmNvbT4gLSAwLjkuNy4wLTEyLmdpdDIwMTIxMDA0AEppxZnDrSBLbGltZcWhIDxqa2xpbWVzQHJlZGhhdC5jb20+IC0gMC45LjcuMC0xMS5naXQyMDEyMTAwNABEYW4gV2luc2hpcCA8ZGFud0ByZWRoYXQuY29tPiAtIDAuOS43LjAtMTAuZ2l0MjAxMjEwMDQARGFuIFdpbnNoaXAgPGRhbndAcmVkaGF0LmNvbT4gLSAwLjkuNy4wLTkuZ2l0MjAxMjEwMDQASmnFmcOtIEtsaW1lxaEgPGprbGltZXNAcmVkaGF0LmNvbT4gLSAwLjkuNy4wLTguZ2l0MjAxMjEwMDQARGFuaWVsIERyYWtlIDxkc2RAbGFwdG9wLm9yZz4gLSAwLjkuNy4wLTcuZ2l0MjAxMjEwMDQARGFuIFdpbnNoaXAgPGRhbndAcmVkaGF0LmNvbT4gLSAwLjkuNy4wLTYuZ2l0MjAxMjEwMDQARGFuIFdpbnNoaXAgPGRhbndAcmVkaGF0LmNvbT4gLSAwLjkuNy4wLTUuZ2l0MjAxMjEwMDQARGFuIFdpbnNoaXAgPGRhbndAcmVkaGF0LmNvbT4gLSAwLjkuNy4wLTQuZ2l0MjAxMjEwMDQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC45LjcuMC0zLmdpdDIwMTIxMDA0AERhbiBXaW5zaGlwIDxkYW53QHJlZGhhdC5jb20+IC0gMC45LjcuMC0yLmdpdDIwMTIxMDA0AERhbiBXaW5zaGlwIDxkYW53QHJlZGhhdC5jb20+IC0gMC45LjcuMC0xLmdpdDIwMTIwODIwAEZlZG9yYSBSZWxlYXNlIEVuZ2luZWVyaW5nIDxyZWwtZW5nQGxpc3RzLmZlZG9yYXByb2plY3Qub3JnPiAtIDE6MC45LjUuOTYtMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjkuNS45Ni0xAEppxZnDrSBLbGltZcWhIDxqa2xpbWVzQHJlZGhhdC5jb20+IC0gMC45LjUuOTUtMS5naXQyMDEyMDcxMwBKacWZw60gS2xpbWXFoSA8amtsaW1lc0ByZWRoYXQuY29tPiAtIDAuOS40LTUuZ2l0MjAxMjA1MjEARGFuIFdpbnNoaXAgPGRhbndAcmVkaGF0LmNvbT4gLSAwLjkuNC00LmdpdDIwMTIwNTAyAEppxZnDrSBLbGltZcWhIDxqa2xpbWVzQHJlZGhhdC5jb20+IC0gMC45LjQtMy5naXQyMDEyMDUwMgBDb2xpbiBXYWx0ZXJzIDx3YWx0ZXJzQHZlcmJ1bS5vcmc+IC0gMTowLjkuNC0yLmdpdDIwMTIwMzI4XzIASmnFmcOtIEtsaW1lxaEgPGprbGltZXNAcmVkaGF0LmNvbT4gLSAwLjkuNC0xLmdpdDIwMTIwMzI4XzIARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC45LjMuOTk3LTIARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC45LjMuOTk3LTEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC45LjMuOTk3LTAuNwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjkuMy45OTUtMC42AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOS4zLjk5NS0wLjUARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC45LjMuOTk1LTAuNABEYW4gSG9yw6FrIDxkYW5bYXRdZGFubnkuY3o+IC0gMC45LjMtMC4zAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOS4zLTAuMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjkuMy0wLjEATWF0dGhpYXMgQ2xhc2VuIDxtY2xhc2VuQHJlZGhhdC5jb20+IC0gMC45LjItNABGZWRvcmEgUmVsZWFzZSBFbmdpbmVlcmluZyA8cmVsLWVuZ0BsaXN0cy5mZWRvcmFwcm9qZWN0Lm9yZz4gLSAxOjAuOS4yLTMARGFuaWVsIERyYWtlIDxkc2RAbGFwdG9wLm9yZz4gLSAwLjkuMi0yAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOS4yLTEAQWRhbSBXaWxsaWFtc29uIDxhd2lsbGlhbUByZWRoYXQuY29tPiAtIDE6MC45LjEuOTAtNS5naXQyMDExMDkyNwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjkuMS45MC0zLmdpdDIwMTEwOTI3AEppxZnDrSBLbGltZcWhIDxqa2xpbWVzQHJlZGhhdC5jb20+IC0gMC45LjEuOTAtMi5naXQyMDExMDkyNwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjkuMS45MC0xAFRvbSBDYWxsYXdheSA8c3BvdEBmZWRvcmFwcm9qZWN0Lm9yZz4gLSAwLjkuMC0yAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOS4wLTEAUmF5IFN0cm9kZSA8cnN0cm9kZUByZWRoYXQuY29tPiAwLjguOTk5Ny03LmdpdDIwMTEwNzIxAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC45OTk3LTYuZ2l0MjAxMTA3MjEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44Ljk5OTctNS5naXQyMDExMDcwMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguOTk5Ny00LmdpdDIwMTEwNjIwAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC45OTk3LTMuZ2l0MjAxMTA2MTMARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44Ljk5OTctMi5naXQyMDExMDUzMQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguOTk5Ny0xLmdpdDIwMTEwNTMxAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC45OTktMy5naXQyMDExMDUyNgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguOTk5LTIuZ2l0MjAxMTA1MDkARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44Ljk5OS0xAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC45OTgtNC5naXQyMDExMDQyNwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguOTk4LTMuZ2l0MjAxMTA0MTkARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44Ljk5OC0yLmdpdDIwMTEwNDA2AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC45OTgtMQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguOTk3LTguZ2l0MjAxMTAzMzEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44Ljk5Ny03LmdpdDIwMTEwMzMwAENocmlzdG9waGVyIEFpbGxvbiA8Y2FpbGxvbkByZWRoYXQuY29tPiAtIDAuOC45OTctNi5naXQyMDExMDMyOABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguOTk3LTUuZ2l0MjAxMTAzMjgARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44Ljk5Ny00LmdpdDIwMTEwMzI1AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC45OTctMy5naXQyMDExMDMyNABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguOTk3LTIuZ2l0MjAxMTAzMjQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44Ljk5Ny0xAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC45OTYtMQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguOTk1LTQuZ2l0MjAxMTAzMDgARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44Ljk5NS0zLmdpdDIwMTEwMzA4AE1hdHRoaWFzIENsYXNlbiA8bWNsYXNlbkByZWRoYXQuY29tPiAtIDAuOC45OTUtMi5naXQyMDExMDMwOABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguOTk1LTEuZ2l0MjAxMTAzMDgATWF0dGhpYXMgQ2xhc2VuIDxtY2xhc2VuQHJlZGhhdC5jb20+IC0gMC44LjItOC5naXQyMDEwMTExNwBGZWRvcmEgUmVsZWFzZSBFbmdpbmVlcmluZyA8cmVsLWVuZ0BsaXN0cy5mZWRvcmFwcm9qZWN0Lm9yZz4gLSAxOjAuOC4yLTcuZ2l0MjAxMDExMTcATWF0dGhpYXMgQ2xhc2VuIDxtY2xhc2VuQHJlZGhhdC5jb20+IC0gMC44LjItNi5naXQyMDEwMTExNwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguMi01LmdpdDIwMTAxMTE3AE1hdHRoaWFzIENsYXNlbiA8bWNsYXNlbkByZWRoYXQuY29tPiAtIDAuOC4yLTQuZ2l0MjAxMDExMTcARGFuIEhvcsOhayA8ZGFuW2F0XWRhbm55LmN6PiAtIDAuOC4yLTMuZ2l0MjAxMDExMTcATWF0dGhpYXMgQ2xhc2VuIDxtY2xhc2VuQHJlZGhhdC5jb20+IC0gMC44LjItMi5naXQyMDEwMTExNwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguMi0xLmdpdDIwMTAxMTE3AE1hdHRoaWFzIENsYXNlbiA8bWNsYXNlbkByZWRoYXQuY29tPiAtIDAuOC4xLTEwLjEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44LjEtMTAARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44LjEtOQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguMS04AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC4xLTcARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44LjEtNgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguMS01AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC4xLTQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44LjEtMwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguMS0yAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC4xLTEATWF0dGhpYXMgQ2xhc2VuIDxtY2xhc2VuQHJlZGhhdGNvbT4gLSAwLjguMS0wLjUARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44LjEtMC40AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC4xLTAuMwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjguMS0wLjIuZ2l0MjAxMDA1MTkARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44LjEtMC4xLmdpdDIwMTAwNTEwAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC0xMy5naXQyMDEwMDUwOQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjgtMTIuZ2l0MjAxMDA1MDQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44LTExLmdpdDIwMTAwNTAzAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC0xMC5naXQyMDEwMDUwMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjgtOS5naXQyMDEwMDQyOQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjgtOC5naXQyMDEwMDQyNgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjgtNy5naXQyMDEwMDQyMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjgtNi5naXQyMDEwMDQwOABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjgtNS5naXQyMDEwMDQwOABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjgtNC5naXQyMDEwMDMyNQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjgtMy5naXQyMDEwMDMyMwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjgtMi5naXQyMDEwMDMxNwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjgtMS5naXQyMDEwMDIxOQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjgtMC40LmdpdDIwMTAwMjExAEtldmluIEtvZmxlciA8S2V2aW5AdGlnY2MudGljYWxjLm9yZz4gLSAwLjgtMC4zLmdpdDIwMTAwMTI5AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuOC0wLjIuZ2l0MjAxMDAxMjkARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC44LTAuMS5naXQyMDEwMDEyMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjcuOTk5LTIuZ2l0MjAxMDAxMjAARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC43Ljk5OS0xLmdpdDIwMTAwMTIwAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNy45OTgtMS5naXQyMDEwMDEwNgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjcuOTk3LTIuZ2l0MjAwOTEyMTQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC43Ljk5Ny0xAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNy45OTYtNy5naXQyMDA5MTExMwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjcuOTk2LTYuZ2l0MjAwOTEwMjEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC43Ljk5Ni01LmdpdDIwMDkxMDIxAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNy45OTYtNC5naXQyMDA5MTAwMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjcuOTk2LTMuZ2l0MjAwOTA5MjgATWF0dGhpYXMgQ2xhc2VuIDxtY2xhc2VuQHJlZGhhdC5jb20+IC0gMC43Ljk5Ni0zLmdpdDIwMDkwOTIxAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNy45OTYtMi5naXQyMDA5MDkyMQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjcuOTk2LTEuZ2l0MjAwOTA4MjYARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC43Ljk5NS0zLmdpdDIwMDkwODEzAEJhc3RpZW4gTm9jZXJhIDxibm9jZXJhQHJlZGhhdC5jb20+IDAuNy45OTUtMi5naXQyMDA5MDgwNABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjcuOTk1LTEuZ2l0MjAwOTA4MDQATWF0dGhpYXMgQ2xhc2VuIDxtY2xhc2VuQHJlZGhhdC5jb20+IC0gMC43Ljk5NS0xLmdpdDIwMDkwNzI4AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNy45OTUtMC5naXQyMDA5MDcyOABGZWRvcmEgUmVsZWFzZSBFbmdpbmVlcmluZyA8cmVsLWVuZ0BsaXN0cy5mZWRvcmFwcm9qZWN0Lm9yZz4gLSAxOjAuNy4xLTkuZ2l0MjAwOTA3MDgARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC43LjEtOC5naXQyMDA5MDcwOABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjcuMS03LmdpdDIwMDkwNzA4AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNy4xLTYuZ2l0MjAwOTA2MTcARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC43LjEtNS5naXQyMDA5MDYxNwBLYXJzdGVuIEhvcHAgPGthcnN0ZW5AcmVkaGF0LmNvbT4gMC43LjEtNC5naXQyMDA5MDQxNC4xAEFkYW0gSmFja3NvbiA8YWpheEByZWRoYXQuY29tPiAxOjAuNy4xLTQuZ2l0MjAwOTA0MTQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMS0zLmdpdDIwMDkwNDE0AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjEtMi5naXQyMDA5MDQxNABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4xLTEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC4xMDAtMi5naXQyMDA5MDQwOABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLjEwMC0xAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAuOTktNQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLjk5LTQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC45OS0zLjUARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC45OS0zAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAuOTktMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLjk5LTEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC45OC0xLmdpdDIwMDkwMjI1AEZlZG9yYSBSZWxlYXNlIEVuZ2luZWVyaW5nIDxyZWwtZW5nQGxpc3RzLmZlZG9yYXByb2plY3Qub3JnPiAtIDE6MC43LjAuOTctNi5naXQyMDA5MDIyMABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLjk3LTUuZ2l0MjAwOTAyMjAARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC45Ny00LmdpdDIwMDkwMjE5AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAuOTctMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLjk3LTEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0yLmdpdDIwMDkwMjA3AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMS5naXQyMDA5MDEwMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMTIuc3ZuNDMyNgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMTIuc3ZuNDI5NgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMTIuc3ZuNDI5NQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMTIuc3ZuNDI5MwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMTEuc3ZuNDIyOQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMTEuc3ZuNDIwMQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMTEuc3ZuNDE3NQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMTEuc3ZuNDE3NABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMTEuc3ZuNDAyMi40AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4xMS5zdm40MDIyLjMARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjExLnN2bjQwMjIuMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMTEuc3ZuNDAyMi4xAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4xMS5zdm40MDIyAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4xMS5zdm4zOTMwAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4xMS5zdm4zOTI3AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4xMS5zdm4zODQ2AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4xMS5zdm4zODMwAE1hdHRoaWFzIENsYXNlbiA8bWNsYXNlbkByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4xMC5zdm4zODAxAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4xMC5zdm4zODAxAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4xMC5zdm4zNzQ3AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC45LjQuc3ZuMzY3NQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOS4zLnN2bjM2NzUARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjkuMy5zdm4zNjY5AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC45LjMuc3ZuMzY2NwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOS4zLnN2bjM2NjUARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjkuMy5zdm4zNjIzAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC45LjIuc3ZuMzYyMwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOS4yLnN2bjM2MjIARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjkuMi5zdm4zNjIwAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC45LjIuc3ZuMzYxOQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOS4yLnN2bjM2MTQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjkuMi5zdm4zNTkwAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC45LjIuc3ZuMzU3OABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOS4yLnN2bjM1NzEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjkuMi5zdm4zNTcwAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC45LjIuc3ZuMzU2NgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOS4xLnN2bjM1NjYARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjkuMS5zdm4zNTQ5AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC45LjEuc3ZuMzU0OABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOS4xLnN2bjM1NDcARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjkuMS5zdm4zNTI3AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC45LjEuc3ZuMzUyMQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOS4xLnN2bjM0NzYARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjkuMS5zdm4zNDczAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC45LjEuc3ZuMzQ3MgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOS4xLnN2bjM0NDAARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjkuMS5zdm4zNDE3AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC44LnN2bjM0MTcARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjguc3ZuMzM3MABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOC5zdm4zMzY5AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC44LnN2bjMzMTkARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjguc3ZuMzMxMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOC5zdm4zMzAyAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC44LnN2bjMyNjEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjguc3ZuMzIzNQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOC5zdm4zMjA0AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC44LnN2bjMxODEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjguc3ZuMzE4MABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOC5zdm4zMTM4AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC44LnN2bjMxMzQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjguc3ZuMzEzMwBKZXJlbXkgS2F0eiA8a2F0empAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuOC5zdm4zMTA5AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC42LjYuc3ZuMzEwOQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuNi41LnN2bjMxMDkARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjYuNS5zdm4zMDk2AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC42LjQuc3ZuMzA5NgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuNi4zLnN2bjMwOTYARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjYuMy5zdm4zMDk0AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC42LjIuc3ZuMzA4MABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuNi4xLnN2bjMwMzAARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjUuc3ZuMzAzMABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuNC5zdm4zMDMwAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC40LnN2bjMwMjAARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjMuc3ZuMzAyMABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMy5zdm4zMDE2AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4zLnN2bjMwMTQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjMuc3ZuMzAwOABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMy5zdm4yOTk1AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4zLnN2bjI5OTQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjMuc3ZuMjk4MwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMy5zdm4yOTcwAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4zLnN2bjI5NjIARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjMuc3ZuMjk2MQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMy5zdm4yOTE0AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4zLnN2bjI5MDcARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjMuc3ZuMjg4NgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMy5zdm4yODgwAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4zLnN2bjI4NTIARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjMuc3ZuMjg0OQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNy4wLTAuMy5zdm4yODQ0AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC43LjAtMC4yLnN2bjI4MzMARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjcuMC0wLjEuc3ZuMjczNgBDaHJpc3RvcGhlciBBaWxsb24gPGNhaWxsb25AcmVkaGF0LmNvbT4gMTowLjYuNS05AENocmlzdG9waGVyIEFpbGxvbiA8Y2FpbGxvbkByZWRoYXQuY29tPiAxOjAuNi41LTgARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IDE6MC42LjUtNwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gMTowLjYuNS02AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAxOjAuNi41LTUARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IDE6MC42LjUtNABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gMTowLjYuNS0zAENocmlzdG9waGVyIEFpbGxvbiA8Y2FpbGxvbkByZWRoYXQuY29tPiAxOjAuNi41LTIAQ2hyaXN0b3BoZXIgQWlsbG9uIDxjYWlsbG9uQHJlZGhhdC5jb20+IDE6MC42LjUtMQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNi41LTAuNy5zdm4yNTQ3AE1hdHRoZXcgQmFybmVzICA8bWJhcm5lc0ByZWRoYXQuY29tPiAxOjAuNi41LTAuNi5zdm4yNDc0AE1hdHRoaWFzIENsYXNlbiA8bWNsYXNlbkByZWRoYXQuY29tPiAxOjAuNi41LTAuNS5zdm4yNDc0AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC42LjUtMC40LnN2bjI0NzQAQ2hyaXN0b3BoZXIgQWlsbG9uIDxjYWlsbG9uQHJlZGhhdC5jb20+IC0gMTowLjYuNS0wLjMuY3ZzMjAwNjEwMjUAQ2hyaXN0b3BoZXIgQWlsbG9uIDxjYWlsbG9uQHJlZGhhdC5jb20+IC0gMTowLjYuNS0wLjIuY3ZzMjAwNjEwMjUATWF0dGhpYXMgQ2xhc2VuIDxtY2xhc2VuQHJlZGhhdC5jb20+AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDE6MC42LjUtMC5jdnMyMDA2MTAyNQBDaHJpc3RvcGhlciBBaWxsb24gPGNhaWxsb25AcmVkaGF0LmNvbT4gLSAxOjAuNi40LTYAQ2hyaXN0b3BoZXIgQWlsbG9uIDxjYWlsbG9uQHJlZGhhdC5jb20+IC0gMTowLjYuNC01AEJpbGwgTm90dGluZ2hhbSA8bm90dGluZ0ByZWRoYXQuY29tPiAtIDE6MC42LjQtNABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAxOjAuNi40LTMARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMTowLjYuNC0yAENocmlzdG9waGVyIEFpbGxvbiA8Y2FpbGxvbkByZWRoYXQuY29tPiAtIDAuNy4wLTAuY3ZzMjAwNjA1MjkuNwBSYXkgU3Ryb2RlIDxyc3Ryb2RlQHJlZGhhdC5jb20+IC0gMC43LjAtMC5jdnMyMDA2MDUyOS42AFJheSBTdHJvZGUgPHJzdHJvZGVAcmVkaGF0LmNvbT4gLSAwLjcuMC0wLmN2czIwMDYwNTI5LjUAUmF5IFN0cm9kZSA8cnN0cm9kZUByZWRoYXQuY29tPiAtIDAuNy4wLTAuY3ZzMjAwNjA1MjkuNABKb2huIChKNSkgUGFsbWllcmkgPGpvaG5wQHJlZGhhdC5jb20+IC0gMC43LjAtMC5jdnMyMDA2MDUyOS4zAEpvaG4gKEo1KSBQYWxtaWVyaSA8am9obnBAcmVkaGF0LmNvbT4gLSAwLjcuMC0wLmN2czIwMDYwNTI5LjIASmVzc2UgS2VhdGluZyA8amtlYXRpbmdAcmVkaGF0LmNvbT4gLSAwLjcuMC0wLmN2czIwMDYwNTI5LjEuMQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjcuMC0wLmN2czIwMDYwNTI5AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNy4wLTAuY3ZzMjAwNjA1MjEAQmlsbCBOb3R0aW5naGFtIDxub3R0aW5nQHJlZGhhdC5jb20+IC0gMC42LjItMy5mYzYASmVyZW15IEthdHogPGthdHpqQHJlZGhhdC5jb20+IC0gMC42LjItMi5mYzYARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC42LjItMQBQZXRlciBKb25lcyA8cGpvbmVzQHJlZGhhdC5jb20+IC0gMC42LjAtMwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gMC42LjAtMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gMC42LjAtMQBKZXJlbXkgS2F0eiA8a2F0empAcmVkaGF0LmNvbT4gLSAwLjUuMS0xOC5jdnMyMDA2MDMwMgBDaHJpc3RvcGhlciBBaWxsb24gPGNhaWxsb25AcmVkaGF0LmNvbT4ARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IDAuNS4xLTE4LmN2czIwMDYwMzAxAENocmlzdG9waGVyIEFpbGxvbiA8Y2FpbGxvbkByZWRoYXQuY29tPiAwLjUuMS0xNy5jdnMyMDA2MDIyOABDaHJpc3RvcGhlciBBaWxsb24gPGNhaWxsb25AcmVkaGF0LmNvbT4gMC41LjEtMTYuY3ZzMjAwNjAyMjcAQ2hyaXN0b3BoZXIgQWlsbG9uIDxjYWlsbG9uQHJlZGhhdC5jb20+IDAuNS4xLTE1LmN2czIwMDYwMjI3AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29NPiAwLjUuMS0xNC5jdnMyMDA2MDIyMQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gMC41LjEtMTMuY3ZzMjAwNjAyMjEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IDAuNS4xLTEyLmN2czIwMDYwMjEzAENocmlzdG9waGVyIEFpbGxvbiA8Y2FpbGxvbkByZWRoYXQuY29tPiAwLjUuMS0xMS5jdnMyMDA2MDIwNQBKZXNzZSBLZWF0aW5nIDxqa2VhdGluZ0ByZWRoYXQuY29tPiAwLjUuMS0xMC5jdnMyMDA2MDIwNS4xAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAwLjUuMS0xMC5jdnMyMDA2MDIwNQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gMC41LjEtOS5jdnMyMDA2MDIwMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gMC41LjEtOC5jdnMyMDA2MDEzMQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gMC41LjEtNy5jdnMyMDA2MDEzMQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gMC41LjEtNi5jdnMyMDA2MDEyNwBKZXNzZSBLZWF0aW5nIDxqa2VhdGluZ0ByZWRoYXQuY29tPgBKb2huIChKNSkgUGFsbWllcmkgPGpvaG5wQHJlZGhhdC5jb20+IC0gMC41LjEtNQBQZXRlciBKb25lcyA8cGpvbmVzQHJlZGhhdC5jb20+IC0gMC41LjEtNABDaHJpc3RvcGhlciBBaWxsb24gPGNhaWxsb25AcmVkaGF0LmNvbT4gLSAwLjUuMS0zAENocmlzdG9waGVyIEFpbGxvbiA8Y2FpbGxvbkByZWRoYXQuY29tPiAtIDAuNS4xLTIAQ2hyaXN0b3BoZXIgQWlsbG9uIDxjYWlsbG9uQHJlZGhhdC5jb20+IC0gMC41LjAtMgBEYW4gV2lsbGlhbXMgPGRjYndAcmVkYWh0LmNvbT4gLSAwLjQuMS01LmN2czIwMDUxMDEwAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNC4xLTQuY3ZzMjAwNTEwMDkARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC40LjEtMy5jdnMyMDA1MDkyMgBKZXJlbXkgS2F0eiA8a2F0empAcmVkaGF0LmNvbT4gLSAwLjQuMS0yLmN2czIwMDUwOTEyAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNC4xLTIuY3ZzMjAwNTA4MTkARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC40LjEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC40LTM2LmN2czIwMDUwODExAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNC0zNS5jdnMyMDA1MDgxMQBSYXkgU3Ryb2RlICA8cnN0cm9kZUByZWRoYXQuY29tPiAtIDAuNC0zNC5jdnMyMDA1MDcyOQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjQtMzQuY3ZzMjAwNTA2MjkARGF2aWQgWmV1dGhlbiA8ZGF2aWR6QHJlZGhhdC5jb20+IC0gMC40LTMzLmN2czIwMDUwNjI5AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNC0zMi5jdnMyMDA1MDYxNwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjQtMzEuY3ZzMjAwNTA2MTYARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC40LTMwLmN2czIwMDUwNjE1AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNC0xNS5jdnMzMDA1MDQwNABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjQtMTQuY3ZzMzAwNTA0MDQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC40LTEzLmN2czMwMDUwNDA0AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNC0xMi5jdnMyMDA1MDQwNABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gLSAwLjQtMTEuY3ZzMjAwNTA0MDQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IC0gMC40LTEwLmN2czIwMDUwNDA0AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAtIDAuNC05LmN2czIwMDUwNDA0AEplcmVteSBLYXR6IDxrYXR6akByZWRoYXQuY29tPiAtIDAuNC04LmN2czIwMDUwNDA0AEplcmVteSBLYXR6IDxrYXR6akByZWRoYXQuY29tPiAtIDAuNC03LmN2czIwMDUwNDA0AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAwLjQtNi5jdnMyMDA1MDQwNABEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gMC40LTUuY3ZzMjAwNTA0MDIAQ2hyaXN0b3BoZXIgQWlsbG9uIDxjYWlsbG9uQHJlZGhhdC5jb20+IDAuNC00LmN2czIwMDUwMzE1AFJheSBTdHJvZGUgPHJzdHJvZGVAcmVkaGF0LmNvbT4gMC40LTMuY3ZzMjAwNTAzMTUAUmF5IFN0cm9kZSA8cnN0cm9kZUByZWRoYXQuY29tPiAwLjQtMi5jdnMyMDA1MDMxNQBSYXkgU3Ryb2RlIDxyc3Ryb2RlQHJlZGhhdC5jb20+IDAuNC0xLmN2czIwMDUwMzE1AFJheSBTdHJvZGUgPHJzdHJvZGVAcmVkaGF0LmNvbT4gMC40LTEuY3ZzMjAwNTAzMDcARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IDAuMy40LTEuY3ZzMjAwNTAzMDQARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IDAuMy4zLTIuY3ZzMjAwNTAyMjIARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IDAuMy4zLTIuY3ZzMjAwNTAyMTQueC4xAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAwLjMuMy0yLmN2czIwMDUwMjE0AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAwLjMuMy0xLmN2czIwMDUwMjAyAERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAwLjMuMy0xLmN2czIwMDUwMTI1AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAwLjMuMy0xLmN2czIwMDUwMTI0AFRoYW4gTmdvIDx0aGFuQHJlZGhhdC5jb20+IDAuMy4zLTEuY3ZzMjAwNTAxMTIuNAA8ZGNid0ByZWRoYXQuY29tPiAtIDAuMy4zLTEuY3ZzMjAwNTAxMTgAPGRjYndAcmVkaGF0LmNvbT4gLSAwLjMuMy0xLmN2czIwMDUwMTEyADxkY2J3QHJlZGhhdC5jb20+IC0gMC4zLjItNC4zLmN2czIwMDQxMjA4ADxkY2J3QHJlZGhhdC5jb20+IC0gMC4zLjItMy5jdnMyMDA0MTExNwA8ZGNid0ByZWRoYXQuY29tPiAtIDAuMy4yLTIuY3ZzMjAwNDExMTUAPGRjYndAcmVkaGF0LmNvbT4gLSAwLjMuMi0yLmN2czIwMDQxMDI5ADxkY2J3QHJlZGhhdC5jb20+IC0gMC4zLjEtMgA8anJiQHJlZGhhdC5jb20+IC0gMC4zLjEtMQBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gMC4zLTEARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IDAuMi00AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAwLjItMwBEYW4gV2lsbGlhbXMgPGRjYndAcmVkaGF0LmNvbT4gMC4yLTIARGFuIFdpbGxpYW1zIDxkY2J3QHJlZGhhdC5jb20+IDAuMi0xAEZsb3JpYW4gTGEgUm9jaGUgPEZsb3JpYW4uTGFSb2NoZUByZWRoYXQuZGU+AERhbiBXaWxsaWFtcyA8ZGNid0ByZWRoYXQuY29tPiAwLjEtMwAtIGluaXRyZDogYWNjZXB0IG1hYyBhZGRyZXNzIGFzIGludGVyZmFjZSBzcGVjaWZpZXIgKHJoICMxODc5Nzk1KQotIGluaXRyZDogZml4IHBhcnNpbmcgSVB2NiBwcmVmaXggbGVuZ3RoIChyaCAjMTg3OTc5NSkALSBkaGNwOiBhZGQgZGhjcC12ZW5kb3ItY2xhc3MtaWRlbnRpZmllciBvcHRpb24gKHJoICMxODcxMDQyKQotIGluaXRyZDogcGFyc2UgJ3JkLm5ldC5kaGNwLnZlbmRvci1jbGFzcycga2VybmVsIGNtZGxpbmUgYXJnIChyaCAjMTg3MjI5OSkALSBjb3JlOiBmaXggaGFuZGxpbmcgb2YgbG9jYWwgcm91dGVzIGFzIGRlZmF1bHQgcm91dGUgYW5kIG9uIEQtQnVzIChyaCAjMTg2ODk4MikALSBjb3JlOiBmaXggd2FpdC1kZXZpY2UtdGltZW91dCByYWNlIGFuZCBzdXBwb3J0IGdlbmVyYWwgZGV2aWNlIG1hdGNoZXMgKHJoICMxODUzMzQ4KQAtIGJvbmQ6IGZpeCBSZWFwcGx5IGRvZXMgbm90IHVwZGF0ZSBib25kIG9wdGlvbnMgKHJoICMxODQ3ODE0KQotIGRoY3A6IHN1cHBvcnQgREhDUHY2IGZxZG5fZnFkbiBvcHRpb24gZm9yIGhvc3RuYW1lIChyaCAjMTg1ODM0NCkALSBjb3JlOiBmaXggbWFuYWdpbmcgZGV2aWNlcyBhZnRlciByZXN1bWluZyBmcm9tIHNsZWVwIChyaCAjMTg1NTU2MykKLSBkaGNwOiBmaXggQlBGIGZpbHRlciBmb3IgaW50ZXJuYWwgY2xpZW50IG9uIGJpZyBlbmRpYW4gYXJjaCAocmggIzE4NjE0ODgpCi0gY29yZTogc3VwcG9ydCB3YXJuaW5nIGxvZyBzZXR0aW5nIElQdjYgTVRVIHdpdGggSVB2NiBkaXNhYmxlZCAocmggIzE4NDA5ODkpCi0gd2lmaTogZml4IGNyYXNoIHBhcnNpbmcgaW5jb21wbGV0ZSBCU1MgaW5mbyAocmggIzE4NjYzOTUpAC0gY29yZTogZml4IGdlbmVyYXRpb24gb2YgbG9jYWwgcm91dGVzIGZvciBWUkYgZGV2aWNlcyAocmggIzE4NTcxMzMpCi0gdGVhbTogZml4IGNyYXNoIG9uIGZhaWx1cmUgdG8gY29ubmVjdCB0byB0ZWFtZCAocmggIzE4NTY3MjMpCi0gY29yZTogZml4IGRldGVjdGluZyBmYWlsdXJlIG9mIG1hc3RlciBhY3RpdmUtY29ubmVjdGlvbiAocmggIzE4NDUwMTgpCi0gY29yZTogZml4IHdhcm5pbmcgYWJvdXQgc2V0dGluZyBhY3RpdmVfc2xhdmUgb2YgYm9uZCB3aGVuIGFjdGl2YXRpbmcgbWFzdGVyIChyaCAjMTg1ODMyNikKLSBpbXBvcnQgdHJhbnNsYXRpb25zIChyaCAjMTgyMDU1MikALSB1cGRhdGUgdG8gMS4yNi4wCi0gZGV2aWNlOiByZXNldCBTUi1JT1YgcGFyYW1ldGVycyBvbiBhY3RpdmF0aW9uIGZhaWx1cmUgKHJoICMxODE5NTg3KQotIGluaXRyZDogZW5hYmxlIGlwdjYubWV0aG9kPWF1dG8gd2l0aCBpcD1kaGNwNiAocmggIzE4NTQzMjMpCi0gY29yZTogYWRkICJubS1zaGFyZWQiIHpvbmUgZm9yIGZpcmV3YWxsZCBmb3Igc2hhcmVkIG1vZGUgKHJoICMxODM0OTA3KQotIHBwcDogZml4IHRha2luZyBjb250cm9sIG9mIGxpbmsgKHJoICMxODQ5Mzg2KQAtIGRldmljZTogcmVzdGFydCBESENQIG9ubHkgZm9yIGRldmljZXMgdGhhdCBhcmUgYWN0aXZlIG9yIGFjdGl2YXRpbmcgKHJoICMxODUyNjEyKQotIGluaXRyZDogZml4IGdlbmVyYXRpbmcgZGVmYXVsdCBCT09USUY9IGNvbm5lY3Rpb24gKHJoICMxODUzMjc3KQotIG92czogZml4IHJhY2UgY29uZGl0aW9uIHdoZW4gc2V0dGluZyBNQUMgYWRkcmVzcyBmb3Igb3ZzIGludGVyZmFjZXMgKHJoICMxODUyMTA2KQAtIHVwZGF0ZSB0byAxLjI2LXJjMiAoMS4yNS45MSkKLSBpbml0cmQ6IHNldCBpcHY2Lm1ldGhvZD1hdXRvIHdoZW4gdXNpbmcgSVB2NCBzdGF0aWMgY29uZmlndXJhdGlvbiAocmggIzE4NDg5NDMpCi0gY2xvdWQtc2V0dXA6IGFkZCBzdXBwb3J0IGZvciBHb29nbGUgQ2xvdWQgbG9hZC1iYWxhbmNpbmcgcm91dGVzIChyaCAjMTgyMTc4NykALSB1cGRhdGUgdG8gMS4yNi1yYzEgKDEuMjUuOTApCi0gY29yZTogc3VwcG9ydCBtb3JlIHRjIHFkaXNjcyAodGJmIGFuZCBzZnEpIChyaCAjMTU0NjgwMikKLSBjb3JlOiBzdXBwb3J0IG1hdGNoIGRldmljZXMgZm9yIGNvbm5lY3Rpb24gcHJvZmlsZSBieSBQQ0kgYWRkcmVzcyAoSURfUEFUSCkgKHJoICMxNjczMzIxKQotIG92czogZml4IHBlZXIgcHJvcGVydHkgZm9yIE9WUyBwYXRjaCBpbnRlcmZhY2UgKHJoICMxODQ1MjE2KQotIGRvYzogYWRkIG1hbnVhbCBwYWdlcyBubS1zZXR0aW5ncy1kYnVzIGFuZCBubS1zZXR0aW5ncy1ubWNsaSAocmggIzE2MTQ3MjYpCi0gd2lmaTogZG9uJ3QgYmxvY2sgYXV0b2Nvbm5lY3QgZm9yIHByb2ZpbGVzIHRoYXQgbmV2ZXIgc3VjY2VlZGVkIHRvIGNvbm5lY3QgKHJoICMxNzgxMjUzKQotIGRidXMsbm1jbGk6IGhpZ2hsaWdodCBleHRlcm5hbGx5IG1hbmFnZWQgZGV2aWNlcyAocmggIzE4MTYyMDIpAC0gdXBkYXRlIHRvIDEuMjUuMiAoZGV2ZWxvcG1lbnQpCi0gc3VwcG9ydCBldGh0b29sIGNvYWxlc2NlIGFuZCByaW5nIG9wdGlvbnMgKHJoICMxNjE0NzAwKQotIGNvcmU6IGltcHJvdmUgc3luY2hyb25pemF0aW9uIG9mIHFkaXNjcyB3aXRoIGtlcm5lbCAocmggIzE4MTU4NzUpCi0gdGVhbTogc3VwcG9ydCBydW5uaW5nIHdpdGhvdXQgRC1CdXMgKHJoICMxNzg0MzYzKQotIGNvcmU6IGZpeCBwb3RlbnRpYWwgY3Jhc2ggd2hlbiBhdXRvYWN0aXZhdGluZyBjaGlsZCBjb25uZWN0aW9ucyAocmggIzE3NzgwNzMpCi0gZXRoZXJuZXQ6IHJlc2V0IG9yaWdpbmFsIGF1dG9uZWdvdGlhdGlvbi9zcGVlZC9kdXBsZXggc2V0dGluZ3Mgb24gZGVhY3RpdmF0aW9uIChyaCAjMTgwNzE3MSkKLSBjb3JlOiBmaXggc2V0dGluZyBJUHY2IHRva2VuIGluIGtlcm5lbCAocmggIzE4MTk2ODApAC0gdXBkYXRlIHRvIDEuMjUuMSAoZGV2ZWxvcG1lbnQpCi0gaW1wcm92ZSBkb2N1bWVudGF0aW9uIChyaCAjMTY1MTU5NCwgcmggIzE4MTkyNTkpCi0gdnJmOiBhZGQgc3VwcG9ydCAocmggIzE3NzM5MDgpCi0gYm9uZDogaW1wcm92ZSBzZXR0aW5nIGRlZmF1bHQgb3B0aW9ucyBmb3IgbWlpbW9uIGFuZCB1cGRlbGF5IChyaCAjMTgwNTE4NCwgcmggIzE4MDY1NDkpCi0gYmx1ZXRvb3RoOiBmaXggY3Jhc2ggaGFuZGxpbmcgRFVOIG1vZGVtIChyaCAjMTgyNjYzNSkKLSBjb3JlOiBmaXggcG90ZW50aWFsIGluZmluaXRlIGxvb3Agd2l0aCBwcmVmaXggZGVsZWdhdGlvbiAocmggIzE0ODgwMzApCi0gaW5pdHJkOiBmaXhlcyBmb3IgcnVubmluZyBOZXR3b3JrTWFuYWdlciBpbiBpbml0cmQgKHJoICMxNjI3ODIwLCAjMTcxMDkzNSwgIzE3NDQ5MzUsICMxNzcxNzkyKQotIGNvcmU6IHByZXZlbnQgbXVsdGlwbGUgYXR0ZW1wdHMgdG8gY3JlYXRlIGRlZmF1bHQgd2lyZWQgY29ubmVjdGlvbiAocmggIzE2ODc5MzcpCi0gYnJpZGdlOiBzdXBwb3J0IG1vcmUgb3B0aW9ucyAocmggIzE3NTU3NjgpCi0gbGlibm0sZGJ1czogZXhwb3NlIEh3QWRkcmVzcyBmb3IgYWxsIGRldmljZSB0eXBlcyAocmggIzE3ODY5MzcpCi0gY29yZTogZml4IHJvdXRlIHByaW9yaXR5IGZvciBJUHY2IChyaCAjMTgxNDU1NykKLSBjb3JlOiBmaXggY3Jhc2ggZHVyaW5nIHJlYXBwbHkgKHJoICMxODE2MDY3KQotIGNvcmU6IGNsZWFyIElQIGFkZHJlc3MgZnJvbSBicmlkZ2Ugc2xhdmUgKHJoICMxODE2NTE3KQotIG92czogc3VwcG9ydCBjaGFuZ2luZyBNVFUgb2YgT1ZTIGludGVyZmFjZXMgKHJoICMxODIwMDUyKQotIG5tLW9ubGluZTogc3VwcG9ydCBzZXR0aW5nIHRpbWVvdXQgZm9yIE5ldHdvcmtNYW5hZ2VyLXdhaXQtb25saW5lIChyaCAjMTgyODQ1OCkALSBjb3JlOiBmaXggbGVha2luZyBkZXZpY2Ugc3RhdGUgZmlsZXMgaW4gL3J1biAocmggIzE4MTAxNTMpCi0gZGhjcDogZml4IGNyYXNoIGluIG5ldHRvb2xzIGNsaWVudCB3aGVuIGxlYWtpbmcgR1NvdXJjZSAocmggIzE4MTAxODgpAC0gZGhjcDoga2VlcCB0cnlpbmcgYWZ0ZXIgYSBzZW5kIGZhaWx1cmUgKHJoICMxODA2NTE2KQotIG92czogZmFpbCBwb3J0IGVuc2xhdmVtZW50IHdoZW4gdGhlIGJyaWRnZSBpcyBub3QgZm91bmQgKHJoICMxNzk3Njk2KQAtIGJvbmQ6IGZpeCBzZXR0aW5nIGFycF92YWxpZGF0ZSBvcHRpb24gZm9yIG90aGVyIGJvbmRpbmcgbW9kZXMgKHJoICMxNzg5NDM3KQAtIFVwZGF0ZSB0byAxLjIyLjgKLSBBZGRlZCBjb25maWd1cmF0aW9uIG9wdGlvbiB0byBjdXN0b21pemUgSVB2NiBSQSB0aW1lb3V0IChyaCAjMTgwMTE1OCkKLSBSZW1vdmVkIGxlbmd0aCBsaW1pdGF0aW9uIGZvciBPVlMgQnJpZGdlLCBQYXRjaGVzIGFuZCBJbnRlcmZhY2VzIChvbmx5IFBhdGNoIHR5cGVzKSBuYW1lcyAocmggIzE3ODg0MzIpCi0gUmV3b3JrZWQgYXN5bmNocm9ub3VzIGRlYWN0aXZhdGlvbiBvZiBPVlMgaW50ZXJmYWNlcyAocmggIzE3ODc5ODksIHJoICMxNzgyNzAxKQotIEZpeGVkIGZhaWx1cmUgd2hlbiBjcmVhdGluZyB0ZWFtIGludGVyZmFjZXMgKHJoICMxNzk4OTQ3KQotIGlmY2ZnLXJoOiBmaXggY2xlYXJpbmcgb3ZzIHNsYXZlIHR5cGUgZnJvbSBpZmNmZy1yaCBmaWxlIChyaCAjMTgwNDE2NykKLSBGaXhlZCBidWcgY2F1c2luZyB2aXJ0dWFsIGRldmljZXMgdG8gbm90IGJlIGF2YWlsYWJsZSBhZnRlciBBZGRDb25uZWN0aW9uKCkvVXBkYXRlKCkgKHJoICMxODA0MzUwKQAtIFVwZGF0ZSB0byAxLjIyLjYKLSBubS1kZXZpY2U6IGFkZCBuZXcgcGVuZGluZyBhY3Rpb24gdG8ga2VlcCB0aGUgZGV2aWNlIGJ1c3kgd2hlbiBpbiBiZXR3ZWVuIHN0YXRlcyAocmggIzE3NTk5NTYpCi0gY2xvdWQtc2V0dXA6IGF2b2lkIHVuc3VwcG9ydGVkIHNldHRpbmdzIGluIHN5c3RlbWQgc2VydmljZSB1bml0IChyaCAjMTc5MTc1OCkKLSBkbyBub3QgY3JlYXRlIHZpcnR1YWwgZGV2aWNlIGlmIG1hc3RlciBpcyBub3QgcHJlc2VudCAocmggIzE3OTU5MTkpCi0gYWxsb3cgSVB2NiBSQSB0aW1lb3V0IHRvIGJlIHNldCB0byBhIHZhbHVlIGhpZ2hlciB0aGFuIDEyMCBzZWNvbmRzIChyaCAjMTc5NTk1NykKLSBmaXggYmVoYXZpb3VyIHdoZW4gJ2lwdjQuZGhjcC10aW1lb3V0JyBvcHRpb24gaXMgc2V0IHRvICdpbmZpbml0eScgKHJoICMxNzkxMzc4KQAtIFVwZGF0ZSB0byAxLjIyLjQKLSBkaGNwOiBmaXggYmVoYXZpb3Igb2YgaW50ZXJuYWwgREhDUCBjbGllbnQgd2hlbiB0aGUgc2VydmVyIHNlbmRzIGEgTkFLIChyaCAjMTc4NzIxOSkALSBVcGRhdGUgdG8gMS4yMi4yCi0gY29yZSxsaWJubTogZXhwb3NlIGNhcGFiaWxpdHkgZm9yIE9WUyBzdXBwb3J0IChyaCAjMTc4NTE0NykKLSBkaGNwOiB2YXJpb3VzIGJ1Z2ZpeGVzIGZvciBuZXR0b29scyBuLWRoY3A0IHBsdWdpbgAtIGRoY3A6IGZpeCBwYXJzaW5nIG9mIEROUyBzZWFyY2ggZG9tYWluIHdpdGggbmV0dG9vbHMgcGx1Z2luIChyaCAjMTc4Mzk4MSkALSBVcGRhdGUgdG8gMS4yMi4wCi0gc3VwcG9ydCBtYWluLmF1dGgtcG9sa2l0PXJvb3Qtb25seSBzZXR0aW5nIHRvIGFsbG93IHJvb3Qgb25seSAocmggIzE3NjIwMTEpAC0gVXBkYXRlIHRvIDEuMjItcmMxICgxLjIxLjkwKQotIGxhcmdlIGludGVybmFsIHJld29yayBvZiBsaWJubSdzIE5NQ2xpZW50Ci0gZGhjcDogc3dpdGNoIGltcGxlbWVudGF0aW9uIG9mICJpbnRlcm5hbCIgREhDUCB0byBuZXR0b29scycgbi1kaGNwNAotIGFkZCBzdXBwb3J0IGZvciBjYXJyaWVyIHN0YXRlIG9mIGRldmljZXMgb24gRC1CdXMvbGlibm0gKHJoICMxNzIyMDI0KQotIGNsb3VkLXNldHVwOiBhZGQgaW5pdGlhbCBhbmQgZXhwZXJpbWVudGFsIHRvb2wgZm9yIGNvbmZpZ3VyaW5nIGluIGNsb3VkIChyaCAjMTY0MjQ2MSkKLSBkaGNwOiBzdXBwb3J0IGNvbmZpZ3VyaW5nIEZRRE4gaG9zdG5hbWUgZmxhZ3MgKHJoICMxNjQ5MzY4KQAtIFVwZGF0ZSB0byAxLjIxLjMsIGEgZGV2ZWxvcG1lbnQgc25hcHNob3Qgb2YgTmV0d29ya01hbmFnZXIgMS4yMgotIHN1cHBvcnQgY29uZmlndXJpbmcgZGVmYXVsdCByb3V0ZSBhcyBhIHJlZ3VsYXIsIHN0YXRpYyByb3V0ZSAocmggIzE3MTQ0MzgpAC0gaW5pdHJkOiByZS1lbmFibGUgdGhlIGdlbmVyYXRvciAocmggIzE2MjYzNDgpAC0gd2lmaTogZGV0ZWN0IEZUIHN1cHBvcnQgcGVyIGRldmljZSB0byBmaXggaXNzdWVzIHdpdGggZHJpdmVyIHN1cHBvcnQgKHJoICMxNzQzNzMwKQotIGRvYzogZml4IGRlZmF1bHQgdmFsdWVzIGluIHByZS1nZW5lcmF0ZWQgZG9jdW1lbnRhdGlvbiAocmggIzE3Mzc5NDUpAC0gSW1wb3J0IHRyYW5zbGF0aW9ucyAocmggIzE2ODk5OTkpAC0gVXBkYXRlIHRvIDEuMjAuMCByZWxlYXNlCi0gZml4IGxpY2Vuc2UgY29tbWVudHMgZm9yIFJQTSBwYWNrYWdlIChyaCAjMTcyMzM5NSkKLSBkaGNwOiBkaXNhYmxlIGV4cGVyaW1lbnRhbCBuZXR0b29scyBESENQIHBsdWdpbgAtIFVwZGF0ZSB0byAxLjIwLXJjMSBzbmFwc2hvdAotIHNldHRpbmdzOiBzdXBwb3J0IHJlYWQtb25seSBkaXJlY3RvcnkgZm9yIGtleWZpbGUgcHJvZmlsZXMgKHJoICMxNjc0NTQ1KQotIHNldHRpbmdzOiBhZGQgQWRkQ29ubmVjdGlvbjIgRC1CdXMgQVBJIHRvIHN1cHByZXNzIGF1dG9jb25uZWN0IChyaCAjMTY3NzA2OCkKLSBzZXR0aW5nczogYWRkIG5vLXJlYXBwbHkgZmxhdCB0byBVcGRhdGUyIEQtQnVzIEFQSSAocmggIzE2NzcwNzApCi0gb3BlbnZzd2l0Y2g6IGRvbid0IHJlbGVhc2Ugc2xhdmVzIG9uIHF1aXQgKHJoICMxNzMzNzA5KQotIGRoY3A6IGV4cG9zZSBwcml2YXRlIG9wdGlvbnMgZm9yIGludGVybmFsIERIQ1AgcGx1Z2luIChyaCAjMTY2MzI1MykKLSBkZXZpY2U6IGZpeCByb3V0ZSB0YWJsZSBzZXR0aW5nIHdoZW4gcmUtYWN0aXZhdGluZyBkZXZpY2UgKHJoICMxNzE5MzE4KQotIG1hbjogY2xhcmlmeSBleGFtcGxlIGluIG5tLW9wZW52c3dpdGNoIG1hbnVhbCBwYWdlIChyaCAjMTYzODAzOCkKLSBtYW46IHZhcmlvdXMgaW1wcm92ZW1lbnRzIG9mIG1hbnVhbCBwYWdlcyAocmggIzE2MTI1NTQpAC0gaW5pdHJkOiBkaXNhYmxlIHRoZSBnZW5lcmF0b3IgYWdhaW4ALSBVcGRhdGUgdG8gYSBuZXdlciAxLjIwIHNuYXBzaG90Ci0gb3ZzOiBzdXBwb3J0IGRwZGsgaW50ZXJmYWNlcyAocmggIzE2MTI1MDMpCi0gbGlibm0tY29yZTogY2hhbmdlIHVuc3VwcG9ydGVkIG1vZGVzIGZvciBhcnBfaXBfdGFyZ2V0cyBib25kIG9wdGlvbiAocmggIzE3MTgxNzMpCi0gaXB2NjogYWRkICdkaXNhYmxlZCcgbWV0aG9kIChyaCAjMTY0Mzg0MSkKLSBkZXZpY2U6IGZpeCBtYXRjaGluZyBwYXJlbnQgZGV2aWNlIGJ5IGNvbm5lY3Rpb24gVVVJRCAocmggIzE3MTY0MzgpCi0gY2xpOiBmaXggZGVmYXVsdCB2YWx1ZSBmb3IgdGVhbS5ydW5uZXItbWluLXBvcnRzIChyaCAjMTcxNjk4NykKLSBpbml0cmQ6IHJlLWVuYWJsZSB0aGUgZ2VuZXJhdG9yIChyaCAjMTYyNjM0OCkALSBVcGRhdGUgdG8gYSAxLjIwIHNuYXBzaG90Ci0gY29yZTogZml4IGEgcG9zc2libGUgY3Jhc2ggb24gZGV2aWNlIHJlbW92YWwgKHJoICMxNjU5NzkwKQotIGNvcmU6IGZpeCBhdXRvbWF0aWMgYWN0aXZhdGlvbiBvZiBzb2Z0d2FyZSBkZXZpZWNzIChyaCAjMTY2Nzg3NCkKLSB0ZWFtOiB1c2Ugc3RyaWN0IEpTT04gcGFyc2luZyBmb3IgY29uZmlndXJhdGlvbiAocmggIzE2OTE2MTkpCi0gdGVhbTogZG9uJ3Qga2lsbCB0ZWFtZCBmb3IgZXh0ZXJuYWwgZGV2aWNlcyAocmggIzE2OTMxNDIpCi0gbG9nZ2luZzogZG9uJ3QgbWlzdXNlIFNZU0xPR19GQUNJTElUWSBmaWVsZCBpbiBqb3VybmFsIChyaCAjMTcwOTc0MSkALSBjbGllbnRzOiBmaXggc3RyaW5nIGxpc3Qgc2V0dGVyIChyaCAjMTY3MTIwMCkALSBkZXZpY2U6IGltcHJvdmUgYXNzdW1pbmcgYnJpZGdlcyBvbiBzdGFydHVwIChyaCAjMTU5MzkzOSkALSBkaGNwOiBmaXggY2xpZW50LWlkIGFuZCBEVUlEIGZvciBpbmZpbmliYW5kICgyKSAocmggIzE2NTgwNTcpAC0gZGV2aWNlOiBlbnN1cmUgSVAgY29uZmlndXJhdGlvbiBpcyByZXN0b3JlZCB3aGVuIGxpbmsgZ29lcyB1cCAocmggIzE2MzY3MTUpCi0gZGhjcDogZml4IGNsaWVudC1pZCBhbmQgRFVJRCBmb3IgaW5maW5pYmFuZCAocmggIzE2NTgwNTcpCi0gZGhjcDogY2hhbmdlIGludGVybmFsIERIQ1AgcGx1Z2luJ3MgaXB2NC5kaGNwLWNsaWVudC1pZCBzZXR0aW5nIHRvICJtYWMiIChyaCAjMTY2MTE2NSkALSBpZmNmZy1yaDogZml4IHJlYWRpbmcgU1ItSU9WIHNldHRpbmdzCi0gZGhjcDogc3VwcG9ydCBjbGllbnQtaWQgYW5kIERVSUQgZm9yIGluZmluaWJhbmQgKHJoICMxNjU4MDU3KQAtIGRoY3A6IGZpeCBkZWZhdWx0IGNsaWVudC1pZCBmb3IgTmV0d29ya01hbmFnZXItY29uZmlnLXNlcnZlciAocmggIzE2NTgwNTcpCi0gY29ubmVjdGl2aXR5OiBmaXggY3Jhc2ggYW5kIHBvcnRhbCBkZXRlY3Rpb24gKHJoICMxNjU4MjE3KQotIGNvcmU6IGNvbWJpbmUgc2VjcmV0LWtleSB3aXRoIG1hY2hpbmUtaWQgZm9yIGhvc3QgaWRlbnRpdHkgKHJoICMxNjQyMDIzKQotIFNSLUlPViByZWxhdGVkIGZpeGVzIChyaCAjMTY1MTU3OCwgcmggIzE2NTE1NzYsIHJoICMxNjUxOTc5KQotIGNvcmU6IGZpeCB1cGRhdGluZyBhZ2VudC1vd25lZCBzZWNyZXRzIChyaCAjMTY1ODc3MSkKLSBjb3JlOiBubyBsb25nZXIgc2V0IHJwX2ZpbHRlciBzeXNjdGwgKHJoICMxNjUxMDk3KQotIGRldmljZTogZG9uJ3QgdGFrZSBkZXZpY2UgZG93biB3aGVuIGNoYW5naW5nIE1BQyBhZGRyZXNzIChyaCAjMTY1OTA2MykKLSBkb2M6IHVzZSBwcmVnZW5lcmF0ZWQgbWFudWFsIHBhZ2VzIGFuZCBndGstZG9jIGZyb20gc291cmNlIHRhcmJhbGwALSBVcGRhdGUgdHJhbnNsYXRpb25zIChyaCAjMTYwODMyMykALSBkZXZpY2U6IGltcHJvdmUgYXV0byBzZWxlY3Rpb24gb2YgZGV2aWNlIHdoZW4gYWN0aXZhdGluZyBwcm9maWxlIChyaCAjMTYzOTI1NCkALSBkaGNwOiBmaXggb3V0LW9mLWJvdW5kcyBoZWFwIHdyaXRlIGZvciBESENQdjYgd2l0aCBpbnRlcm5hbCBwbHVnaW4gKENWRS0yMDE4LTE1Njg4KQotIGRoY3A6IHJldmVydCBsZXR0aW5nIGludGVybmFsIERIQ1AgZ2VuZXJhdGUgZGVmYXVsdCBjbGllbnQtaWQgYmFzZWQgb24gTUFDIGFkZHJlc3MgKHJoICMxNjQwNDY0KQotIGRoY3A6IHN1cHBvcnQgImR1aWQiIHNldHRpbmcgZm9yIGlwdjQuZGhjcC1jbGllbnQtaWQKLSBkaGNwOiBzdXBwb3J0ICIke01BQ30iIGlkZW50aWZpZXIgZm9yIGNvbm5lY3Rpb24uc3RhYmxlLWlkCi0gZGhjcDogc3VwcG9ydCBkaGNwLXBsdWdpbiBkZXZpY2Ugc3BlYyBmb3IgbWF0Y2hpbmcgZGV2aWNlcyBpbiBOZXR3b3JrTWFuYWdlci5jb25mCi0gZGhjcDogaW5zdGFsbCBjb25maWd1cmF0aW9uIHNuaXBwZXQgaW4gY29uZmlnLXNlcnZlciBwYWNrYWdlIGZvciBpcHY0LmRoY3AtY2xpZW50LWlkPW1hYyAocmggIzE2NDA0OTQpCi0gZG5zOiByZW1vdmUgbGltaXRhdGlvbiBmb3Igc2l4IEROUyBzZWFyY2ggZW50cmllcyAocmggIzE2NDk3MDQpCi0gbGlibm06IGZpeCBjcmFzaCBjYW5jZWxsaW5nIGFjdGl2YXRpb24gZnJvbSB3aXRoaW4gY2FsbGJhY2sgKHJoICMxNjQzMDg1KQAtIFVwZGF0ZSB0cmFuc2xhdGlvbnMgKHJoICMxNjA4MzIzKQAtIERvbid0IGRlcGVuZCBvbiBvcGVudnN3aXRjaCAocmggIzE2MjkxNzgpCi0gZGV2aWNlOiBkb24ndCByZW1vdmUgcm91dGVzIHdoZW4gdGhlIGludGVyZmFjZSBpcyBkb3duIChyaCAjMTYzNjcxNSkALSBkaGNwOiBsZXQgaW50ZXJuYWwgREhDUCBnZW5lcmF0ZSBkZWZhdWx0IGNsaWVudC1pZCBiYXNlZCBvbiBNQUMgYWRkcmVzcyAoMikALSBkaGNwOiBsZXQgaW50ZXJuYWwgREhDUCBnZW5lcmF0ZSBkZWZhdWx0IGNsaWVudC1pZCBiYXNlZCBvbiBNQUMgYWRkcmVzcwAtIFVwZGF0ZSB0byAxLjE0LjAgcmVsZWFzZQAtIGRoY3A6IHN3aXRjaCBkZWZhdWx0IERIQ1AgcGx1Z2luIGZyb20gZGhjbGllbnQgdG8gaW50ZXJuYWwgKHJoICMxNTcxNjU1KQAtIFVwZGF0ZSB0byAxLjEzLjMsIGEgZGV2ZWxvcG1lbnQgc25hcHNob3Qgb2YgTmV0d29ya01hbmFnZXIgMS4xNAAtIFVwZGF0ZSB0byAxLjEzLjIsIGEgZGV2ZWxvcG1lbnQgc25hcHNob3Qgb2YgTmV0d29ya01hbmFnZXIgMS4xNAAtIFVwZGF0ZSB0byAxLjEzLjAsIGEgZGV2ZWxvcG1lbnQgc25hcHNob3Qgb2YgTmV0d29ya01hbmFnZXIgMS4xNAAtIFVwZGF0ZSB0byAxLjExLjQsIGEgZGV2ZWxvcG1lbnQgc25hcHNob3Qgb2YgTmV0d29ya01hbmFnZXIgMS4xMgotIFN3aXRjaCB0byBQeXRob24gMy1vbmx5IGJ1aWxkIHJvb3QALSBjb3JlOiB1c2UgZ251dGxzIGNyeXB0byBsaWJyYXJ5IGluc3RlYWQgb2YgbnNzIChyaCAjMTU4MTY5MykALSBjb3JlOiBmaXggZXJyb3IgZGVzdHJveWluZyBjaGVja3BvaW50cyAocmgjMTU3NDU2NSkALSBVcGRhdGUgdG8gMS4xMS4zIHJlbGVhc2UALSBVcGRhdGUgdG8gMS4xMC4yIHJlbGVhc2UALSBBcHBseSBwYXRjaCBmcm9tIHByZXZpb3VzIGNvbW1pdAAtIHN5c3RlbWQ6IGxldCBOTS13LW8uc2VydmljZSByZXF1aXJlIE5ldHdvcmtNYW5hZ2VyIHNlcnZpY2UgKHJoICMxNDUyODY2KQotIHBsYXRmb3JtOiByZWFsbHkgdHJlYXQgZHNhIGRldmljZXMgYXMgcmVndWxhciB3aXJlZCBldGhlcm5ldCAocmggIzEzNzEyODkpCi0gbGlibm06IGZpeCBhY2Nlc3NpbmcgZW5hYmxlZCBhbmQgbWV0ZXJlZCBwcm9wZXJ0aWVzAC0gcGxhdGZvcm06IHRyZWF0IGRzYSBkZXZpY2VzIGFzIHJlZ3VsYXIgd2lyZWQgZXRoZXJuZXQgKHJoICMxMzcxMjg5KQAtIGRldmljZTogZml4IGZyb3plbiBub3RpZnkgc2lnbmFscyBvbiB1bnJlYWxpemUgZXJyb3IgcGF0aAotIGRldmljZTogZml4IGRlbGF5IHN0YXJ0dXAgY29tcGxldGUgZm9yIHVucmVhbGl6ZWQgZGV2aWNlcwotIGtleWZpbGU6IGZpeCBoYW5kbGluZyByb3V0ZXMgd2l0aCBtZXRyaWMgemVybwAtIGNsaTogZml4IGNyYXNoIGluIGludGVyYWN0aXZlIG1vZGUgZm9yICJkZXNjcmliZSAuIgotIGxpYm5tL3t2cG4scmVtb3RlfS1jb25uZWN0aW9uOiBkaXNjb25uZWN0IHNpZ25hbCBoYW5kbGVycyB3aGVuIGRpc3Bvc2VkCi0gbGlibm0vbWFuYWdlcjogZGlzY29ubmVjdCBmcm9tIHNpZ25hbHMgb24gdGhlIHByb3h5IHdoZW4gd2UncmUgZGlzcG9zZWQALSBlbmFibGUgTmV0d29ya01hbmFnZXItd2FpdC1vbmxpbmUuc2VydmljZSBvbiBwYWNrYWdlIHVwZ3JhZGUgKHJoIzE0NTU3MDQpAC0gVXBkYXRlIHRvIDEuOC40IHJlbGVhc2UKLSBkb24ndCBpbnN0YWxsIE5ldHdvcmtNYW5hZ2VyLXdhaXQtb25saW5lIGluIG5ldHdvcmstb25saW5lLnRhcmdldC53YW50cyAocmgjMTQ1NTcwNCkALSBSZWJ1aWx0IGZvciBodHRwczovL2ZlZG9yYXByb2plY3Qub3JnL3dpa2kvRmVkb3JhXzI3X0JpbnV0aWxzX01hc3NfUmVidWlsZAAtIFJlYnVpbHQgZm9yIGh0dHBzOi8vZmVkb3JhcHJvamVjdC5vcmcvd2lraS9GZWRvcmFfMjdfTWFzc19SZWJ1aWxkAC0gcHJvdmlkZSBOZXR3b3JrTWFuYWdlci1kZXZlbAAtIE5ldHdvcmtNYW5hZ2VyLXdpZmkgYW5kIE5ldHdvcmtNYW5hZ2VyLWdsaWItZGV2ZWwgc2hvdWxkIHJlcXVpcmUKICBOZXR3b3JrTWFuYWdlciwgbm90IHByb3ZpZGUgaXQuAC0gVXBkYXRlIHRvIDEuOC4yIHJlbGVhc2UKLSBkaGNwL2RoY2xpZW50OiBpbXByb3ZlICJpbnRlcmZhY2UiIHN0YXRlbWVudCBwYXJzaW5nCi0gZG5zOiBmaXggcHVibGljIHN1ZmZpeCBjaGVjayBvbiBzZWFyY2ggZG9tYWlucyAocmggIzE0MDQzNTApAC0gZGV2aWNlOiBkb24ndCBjaGFuZ2UgTVRVIHVubGVzcyBleHBsaWNpdGx5IGNvbmZpZ3VyZWQgKHJoICMxNDYwNzYwKQotIGNvcmU6IGRvbid0IHJlbW92ZSBleHRlcm5hbCBJUHY0IGFkZHJlc3NlcyAocmggIzE0NTk4MTMpCi0gY2xpOiBmaXggb3V0cHV0IG9mIGlmYWNlIGluIG92ZXJ2aWV3IG91dHB1dCAocmgjMTQ2MDIxOSkKLSBwcHA6IHVuZXhwb3J0IE5NUFBQTWFuYWdlciBpbnN0YW5jZSBvbiBkaXNwb3NlIChyaCMxNDU5NTc5KQotIGNsaTogcmVtb3ZlIHNwdXJpb3VzIGRldmljZSBuYW1lcyBmcm9tIHdpZmkgc3ViY29tbWFuZHMgb3V0cHV0IChyaCMxNDYwNTI3KQAtIGJvbmQ6IGZpeCBjcmFzaCBjb21wYXJpbmcgbW9kZSB3aGlsZSBnZW5lcmF0aW5nIGJvbmQgY29ubmVjdGlvbiAocmggIzE0NTk1ODApCi0gY29ubmVjdGl2aXR5OiBmaXggcm91dGUgcGVuYWx0eSBpZiBXV0FOIGFuZCBCVCBkZXZpY2UgdXNpbmcgaXAtaWZpbmRleCAocmggIzE0NTk5MzIpCi0gZGV2aWNlOiBwZXJzaXN0IG5tLW93bmVkIGluIHJ1biBzdGF0ZSAocmggIzEzNzYxOTkpCi0gZGV2aWNlOiBmaXggYXNzdW1pbmcgbWFzdGVyIGRldmljZSBvbiByZXN0YXJ0IChyaCAjMTQ1MjA2MikKLSBkZXZpY2U6IGFwcGx5IHJvdXRlIG1ldHJpYyBwZW5hbGl0eSBvbmx5IHdoZW4gdGhlIGRlZmF1bHQgcm91dGUgZXhpc3RzIChyaCAjMTQ1OTYwNCkKLSBjb25uZWN0aXZpdHk6IGZpeCBwZXJpb2RpYyBjb25uZWN0aXZpdHkgY2hlY2sgKHJoICMxNDU4Mzk5KQotIGJvbmQ6IGltcHJvdmUgb3B0aW9uIG1hdGNoaW5nIG9uIGRhZW1vbiByZXN0YXJ0IChyaCAjMTQ1NzkwOSkKLSBkZXZpY2U6IGZpeCB0b3VjaGluZyBkZXZpY2UgYWZ0ZXIgZXh0ZXJuYWwgYWN0aXZhdGlvbiAocmggIzE0NTcyNDIpAC0gaWZjZmctcmg6IGZpeCB3cml0aW5nIGxlZ2FjeSBORVRNQVNLIHZhbHVlIChyaCAjMTQ0NTQxNCkKLSB0dWk6IGZpeCBjcmFzaCBkdXJpbmcgY29ubmVjdCAocmggIzE0NTY4MjYpCi0gbGlibm06IGZpeCBsaWJubSByZWplY3RpbmcgVkxBTiBJRCA0MDk1IChyaCAjMTQ1NjkxMSkKLSBibHVldG9vdGg6IGZpeCBjcmFzaCBvbiBjb25uZWN0aW5nIHRvIGEgTkFQIChyaCAjMTQ1NDM4NSkKLSBkZXZpY2U6IHJlbGVhc2UgcmVtb3ZlZCBkZXZpY2VzIGZyb20gbWFzdGVyIG9uIGNsZWFudXAgKHJoICMxNDQ4OTA3KQotIG5tY2xpOiBmaXggY3Jhc2ggd2hlbiBzZXR0aW5nIDgwMi0xeC5wYXNzd29yZC1yYXcgKHJoICMxNDU2MzYyKQAtIGRldmljZTogdXBkYXRlIGV4dGVybmFsIGNvbmZpZ3VyYXRpb24gYmVmb3JlIGNvbW1pdCAoZml4IGJ1ZykgKHJoICMxNDQ5ODczKQAtIGRoY3A6IGRvbid0IGFkZCByb3V0ZSB0byBESENQNCBzZXJ2ZXIgKHJoICMxNDQ4OTg3KQotIGRldmljZTogdXBkYXRlIGV4dGVybmFsIGNvbmZpZ3VyYXRpb24gYmVmb3JlIGNvbW1pdCAocmggIzE0NDk4NzMpCi0gbGlibm06IGZpeCBOVUwgdGVybWluYXRpb24gb2YgZGV2aWNlJ3MgZGVzY3JpcHRpb24gKHJoICMxNDQzMTE0KQotIGxpYm5tLCBjb3JlOiBlbnN1cmUgdmFsaWQgVVRGLTggaW4gZGV2aWNlIHByb3BlcnRpZXMgKHJoICMxNDQzMTE0KQotIGNvcmU6IGZpeCBkZXZpY2UncyBVREkgcHJvcGVydHkgb24gRC1CdXMgKHJoICMxNDQzMTE0KQotIGlmY2ZnLXJoOiBvbWl0IGVtcHR5IG5leHQgaG9wIGZvciByb3V0ZXMgaW4gbGVnYWN5IGZvcm1hdCAocmggIzE0NTI2NDgpCi0gY29yZTogZml4IHBlcnNpc3RpbmcgbWFuYWdlZCBzdGF0ZSBvZiBkZXZpY2UgKHJoICMxNDQwMTcxKQotIHByb3h5OiBmaXggdXNlLWFmdGVyLWZyZWUgKHJoICMxNDUwNDU5KQotIGRldmljZTogZG9uJ3Qgd3JvbmdseSBkZWxheSBzdGFydHVwIGNvbXBsZXRlIHdhaXRpbmcgZm9yIGNhcnJpZXIgKHJoICMxNDUwNDQ0KQAtIFVwZGF0ZSB0byAxLjguMCByZWxlYXNlAC0gVXBkYXRlIHRvIHRoaXJkIFJlbGVhc2UgQ2FuZGlkYXRlIG9mIE5ldHdvcmtNYW5hZ2VyIDEuOAAtIFVwZGF0ZSB0byBzZWNvbmQgUmVsZWFzZSBDYW5kaWRhdGUgb2YgTmV0d29ya01hbmFnZXIgMS44AC0gVXBkYXRlIHRvIGEgc25hcHNob3Qgb2YgMS44Lnggc2VyaWVzAC0gVXBkYXRlIHRvIGEgMS42LjIgcmVsZWFzZQAtIFJlYnVpbHQgZm9yIGh0dHBzOi8vZmVkb3JhcHJvamVjdC5vcmcvd2lraS9GZWRvcmFfMjZfTWFzc19SZWJ1aWxkAC0gVXBkYXRlIHRvIGEgMS42LjAgcmVsZWFzZQAtIFVwZGF0ZSB3aXRoIGZpeGVzIGZyb20gdXBzdHJlYW0gbm0tMS02IGJyYW5jaAotIGJ1aWxkOiBsZXQgbGlibm0gYW5kIGdsaWIgcGFja2FnZSBjb25mbGljdCAocmggIzE0MDY0NTQpAC0gVXBkYXRlIHRvIGEgMS42LXJjMQAtIGZpeCBidWlsZCBmYWlsdXJlIGR1ZSB0byBjbGFzaCBvZiBiaXR3aXNlIGRlZmluZXMALSBSZWJ1aWxkIGZvciByZWFkbGluZSA3LngALSBVcGRhdGUgdG8gYSBuZXdlciBkZXZlbG9wbWVudCBzbmFwc2hvdAAtIFJlYnVpbGQgcGFja2FnZSBmb3IgdmFsYSBnZW5lcmF0aW9uIGVycm9yIChyaCMxMzk4NzM4KQAtIGZpeCBlbmFibGluZyBpZmNmZy1yaCBwbHVnaW4gYnkgZGVmYXVsdCBmb3IgKz0vLT0gb3BlcmF0aW9ucyAocmgjMTM5NzkzOCkKLSBmaXggbWlzc2luZyBzeW1ib2wgX25tX2RldmljZV9mYWN0b3J5X25vX2RlZmF1bHRfc2V0dGluZ3MALSBmaXggZW5hYmxpbmcgaWZjZmctcmggcGx1Z2luIGJ5IGRlZmF1bHQgKHJoIzEzOTc5MzgpCi0gbW92ZSB0cmFuc2xhdGlvbiBmaWxlcyBmcm9tIGNvcmUgdG8gbGlibm0vZ2xpYiBzdWJwYWNrYWdlcwAtIFVwZGF0ZSB0byBhIGRldmVsb3BtZW50IHNuYXBzaG90AC0gVXBkYXRlIHRvIDEuNC4yAC0gd2lmaTogZml4IGFub3RoZXIgYWN0aXZhdGlvbiBmYWlsdXJlIHdoZW4gY2hhbmdpbmcgTUFDIGFkZHJlc3MgKHJoIzEzNzE0NzgsIGJnbyM3NzA0NTYsIGJnbyM3NzA1MDQpAC0gZGhjcDogZml4IHJhY2UgdG8gbWlzcyBESENQIGxlYXNlIGV2ZW50IChyaCMxMzcyODU0KQAtIHdpZmk6IGZpeCBhY3RpdmF0aW9uIGZhaWx1cmUgZHVlIHRvIGVycm9yIGNoYW5naW5nIE1BQyBhZGRyZXNzIChyaCMxMzcxNDc4LCBiZ28jNzcwNDU2KQAtIFVwZGF0ZSB0byBOZXR3b3JrTWFuYWdlciAxLjQuMCByZWxlYXNlAC0gZml4IHN0YWxlIFdpLUZpIGFmdGVyIHJlc3VtZSBmcm9tIHN1c3BlbmQgKHJoIzEzNjIxNjUpAC0gUmVidWlsZCBhZ2FpbnN0IG5ld2VyIEdMaWIgdG8gb3ZlcmNvbWUgbG9nZ2luZyBwcm9ibGVtcyBvbiBpNjg2AC0gVXBkYXRlIHRvIGEgbGF0ZXIgR2l0IHNuYXBzaG90AC0gZG5zOiBjbGVhciBjYWNoZSBvZiBkbnNtYXNxIHdoZW4gdXBkYXRpbmcgRE5TIGNvbmZpZ3VyYXRpb24gKHJoIzEzMzg3MzEpCi0gZG5zOiBmaXggcmVzdGFydGluZyBkbnNtYXNxIGluc3RhbmNlCi0gc3BlYzogZGVwZW5kIGJsdWV0b290aCBzdWJwYWNrYWdlIG9uIGV4YWN0IHd3YW4gdmVyc2lvbgotIGFsbDogZml4IHNvbWUgbWVtbGVha3MALSBVcGRhdGUgdG8gTmV0d29ya01hbmFnZXIgMS4yLjIgcmVsZWFzZQAtIFVwZGF0ZSB0byBOZXR3b3JrTWFuYWdlciAxLjIuMCByZWxlYXNlAC0gVXBkYXRlIHRvIE5ldHdvcmtNYW5hZ2VyIDEuMi1yYzIALSBVcGRhdGUgdG8gTmV0d29ya01hbmFnZXIgMS4yLXJjMQAtIEZpeCBsaW5rIGRldGVjdGlvbiBvbiA0LjUgd2hlbiBidWlsZCB3aXRoIDQuNiBrZXJuZWwALSBVcGRhdGUgdG8gTmV0d29ya01hbmFnZXIgMS4yLWJldGEzAC0gRml4IG9idGFpbmluZyB0aGUgaG9zdG5hbWUgZnJvbSBETlMgKHJoICMxMzA4OTc0KQAtIEZpeCBhY3RpdmF0aW5nIGNvbm5lY3Rpb25zIGluIHNvbWUgY2FzZXMgKHJoICMxMzE2NDg4KQAtIFVwZGF0ZSB0byBOZXR3b3JrTWFuYWdlciAxLjItYmV0YTIKLSBSZXN5bmMgd2l0aCBjb250cmliL3JwbQAtIHNwZWNmaWxlOiByZW1vdmUgbm8gbG9uZ2VyIG5lZWRlZCAxMC1pYmZ0LXBsdWdpbi5jb25mIGFuZCBzeW5jIHdpdGggY29udHJpYi9ycG0KLSBjb3JlOiBiYWNrcG9ydCBmaXggZm9yIG1pc3NpbmcgYnJhY2VzIGJ1ZyBpbiBwbGF0Zm9ybQAtIFJlYnVpbHQgZm9yIGh0dHBzOi8vZmVkb3JhcHJvamVjdC5vcmcvd2lraS9GZWRvcmFfMjRfTWFzc19SZWJ1aWxkAC0gVXBkYXRlIHRvIE5ldHdvcmtNYW5hZ2VyIDEuMi1iZXRhMQAtIEFkZCB1cHN0cmVhbSBmaXggZm9yIEFQIGxpc3QgaGFzaCBmdW5jdGlvbiAoIzEyODg4NjcpAC0gVXBkYXRlIHRvIGEgbGF0ZXIgc25hcHNob3QKLSBFbmFibGVzIFJGQzcyMTcgYWRkcmVzc2luZyBmb3IgbmV3IElQdjYgY29ubmVjdGlvbnMALSBEcm9wIHRoZSBOZXR3b3JrTWFuYWdlci1kZXZlbCBzdWJwYWNrYWdlIChmb2xkZWQgaW50byBsaWJubS1nbGliLWRldmVsKQotIFVwZGF0ZSB0byBhIGxhdGVyIHNuYXBzaG90AC0gSW1wb3J0IGEgbmV3ZXIgMS4yIGdpdCBzbmFwc2hvdAAtIEZpeCB0ZXN0IHJ1bgAtIEltcG9ydCBhIDEuMiBnaXQgc25hcHNob3QALSBGaXggY29tbWFuZCBsaW5lIHBhcnNpbmcALSBVcGRhdGUgdG8gMS4wLjYgcmVsZWFzZQAtIGZpeCBjcmFzaCB3aGVuIGRlYWN0aXZhdGluZyBhc3N1bWVkIGRldmljZSAocmggIzEyNTM5NDkpCi0gYmFja3BvcnQgd2lmaSBzY2FuIG9wdGlvbnMgZm9yIHNzaWQKLSB1c2UgcGxhaW4gSFRUUCBVUkkgZm9yIGNvbm5lY3Rpdml0eSBjaGVjawAtIFVwZGF0ZSB0byBhIEdpdCBzbmFwc2hvdAAtIEZpeCBhbiBhc3NlcnRpb24gZmFpbHVyZSBpbiBubWNsaSAocmggIzEyNDQwNDgpCi0gRml4IGRlZmF1bHQgcm91dGUgaGFuZGxpbmcgb24gYXNzdW1lZCBjb25uZWN0aW9ucyAocmggIzEyNDU2NDgpAC0gVXBkYXRlIHRvIDEuMC40IHJlbGVhc2UALSBXRVhUIGRlcGVuZHMgb24gZW5hYmxlZCB3aWZpAC0gQSBiaXQgbW9yZSByZWNlbnQgR2l0IHNuYXBzaG90AC0gQSBiaXQgbW9yZSByZWNlbnQgR2l0IHNuYXBzaG90Ci0gVGhpcyBvbmUgZml4ZXMgYSByZWdyZXNzaW9uIHdpdGggZGVmYXVsdCByb3V0ZSBtYW5hZ2VtZW50AC0gVXBkYXRlIHRvIGEgbmV3IDEuMC4zIGRldmVsb3BtZW50IHNuYXBzaG90IChnaXQyMDE1MDcwNykKLSBjb3JlOiBmaXggaGFuZGxpbmcgb2YgaWdub3JlLWF1dG8tKiBwcm9wZXJ0aWVzIChyaCAjMTIzOTE4NCkALSBBIGJpdCBtb3JlIHJlY2VudCBHaXQgc25hcHNob3QALSBVcGRhdGUgdG8gYSByZWNlbnQgR2l0IHNuYXBzaG90AC0gUmVidWlsdCBmb3IgaHR0cHM6Ly9mZWRvcmFwcm9qZWN0Lm9yZy93aWtpL0ZlZG9yYV8yM19NYXNzX1JlYnVpbGQALSBVcGRhdGUgdG8gMS4wLjIgcmVsZWFzZQAtIFVwZGF0ZSB0byAxLjAuMiBkZXZlbG9wbWVudCBzbmFwc2hvdCAoZ2l0MjAxNTA0MjkpAC0gVXBkYXRlIHRvIDEuMC4yIGRldmVsb3BtZW50IHNuYXBzaG90AC0gZG5zOiByZXZlcnQgcmVzb2x2LmNvbmYgc3ltbGluayBzdHVmZiAoc2hvdWxkIG9ubHkgYmUgaW4gRjIzKywgbm90IEYyMikALSBjb25uZWN0aXZpdHk6IGZpeCBjaGVja2luZyB3aGVuIG5vIHZhbGlkIEROUyBzZXJ2ZXJzIGFyZSBwcmVzZW50IChyaCAjMTE5OTA5OCkALSBjb3JlOiBmbHVzaCBJUHY2TEwgYWRkcmVzcyB3aGVuIGRlY29uZmlndXJpbmcgbWFuYWdlZCBkZXZpY2VzIChyaCAjMTE5MzEyNykgKHJoICMxMTg0OTk3KQAtIGNvcmU6IHJlc3VtZSBicmlkZ2VkIGNvbm5lY3Rpb25zIHByb3Blcmx5IChyaCAjMTE2MjYzNiwgYmFja3BvcnQgZnJvbSBtYXN0ZXIpAC0gZG5zOiBtYW5hZ2UgcmVzb2x2LmNvbmYgYXMgc3ltbGluayB0byBwcml2YXRlIGZpbGUgaW4gL3J1biBkaXJlY3RvcnkgKHJoICMxMTE2OTk5KQAtIGJ1aWxkOiBmaXggTmV0d29ya01hbmFnZXItYmx1ZXRvb3RoIGRlcCBvbiBOZXR3b3JrTWFuYWdlci13d2FuCi0gYnVpbGQ6IHJlLWVuYWJsZSBoYXJkd2FyZSBwbHVnaW5zIG9uIHMzOTAALSBVcGRhdGUgdG8gMS4wAC0gdnBuOiBwcm9wYWdhdGUgZGFlbW9uIGV4ZWMgZXJyb3IgY29ycmVjdGx5IChiZ28gIzczOTQzNikKLSBjb3JlOiBkbyBub3QgYXNzZXJ0IHdoZW4gYSBkZXZpY2UgaXMgZW5zbGF2ZWQgZXh0ZXJuYWxseSAocmggIzExNjczNDUpAC0gY2xpOiBmaXggY3Jhc2ggaW4gYG5tY2xpIGRldmljZSB3aWZpYCB3aXRoIG11bHRpcGxlIHdpZmkgZGV2aWNlcyAocmggIzExNTk0MDgpAC0gcGxhdGZvcm06IGZpeCBhIHJvdXRpbmctcmVsYXRlZCBidWcgdGhhdCBjb3VsZCBjYXVzZSBOTSBhbmQgb3RoZXIgYXBwcyB0byBzcGluIChyaCAjMTE1MTY2NSkALSBGaXggSVB2NiBuZXh0IGhvcCBkZWZhdWx0IHNldHRpbmcALSBBdm9pZCB1bm93bmVkIC9ldGMvTmV0d29ya01hbmFnZXIgaW4gY29uZmlnLWNvbm5lY3Rpdml0eS1mZWRvcmEALSBjb25uZWN0aXZpdHktZmVkb3JhOiBkb24ndCByZXF1aXJlIE5ldHdvcmtNYW5hZ2VyICgjMTE1NjE5OCkALSBibHVldG9vdGg6IFJlc3RvcmUgRFVOIHN1cHBvcnQgKHJoICMxMDU1NjI4KQAtIEFsbG93IG5vbi1sb2NhbCB1c2VycyBuZXR3b3JrIGNvbnRyb2wgYWZ0ZXIgUG9saWN5S2l0IGF1dGhlbnRpY2F0aW9uIChyaCAjMTE0NTY0NikALSBjb25uZWN0aXZpdHk6IHVzZSBIVFRQUyBmb3IgY29ubmVjdGl2aXR5IGNoZWNraW5nIChyaCAjMTEzNTc3KQAtIGFkc2wgcGx1Z2luIG5lZWRzIHJwLXBwcG9lIHRvIHdvcmsALSBhbHdheXMgaW5jbHVkZSBNb2RlbU1hbmFnZXItZ2xpYi1kZXZlbCAoIzExMjk2MzIpAC0gUmVidWlsdCBmb3IgaHR0cHM6Ly9mZWRvcmFwcm9qZWN0Lm9yZy93aWtpL0ZlZG9yYV8yMV8yMl9NYXNzX1JlYnVpbGQALSBSZWJ1aWx0IGZvciBwcHAgMi40LjcALSBjb25uZWN0aXZpdHk6IGVuc3VyZSBpbnRlcnZhbCBpcyBzZXQgdG8gZW5hYmxlIGNvbm5lY3Rpdml0eSBjaGVja2luZyAocmggIzExMjM3NzIpAC0gUmVidWlsdCBmb3IgZ29iamVjdC1pbnRyb3NwZWN0aW9uIDEuNDEuNAAtIFVwZGF0ZSB0byB1cHN0cmVhbSAwLjkuMTAuMCByZWxlYXNlIHNuYXBzaG90AC0gVXBkYXRlIHRvIHVwc3RyZWFtIDAuOS45Ljk4ICgwLjkuMTAtcmMxKSByZWxlYXNlIHNuYXBzaG90AC0gVXBkYXRlIHRvIHVwc3RyZWFtIDAuOS45Ljk1ICgwLjkuMTAtYmV0YTEpIHJlbGVhc2Ugc25hcHNob3QALSBSZWJ1aWx0IGZvciBodHRwczovL2ZlZG9yYXByb2plY3Qub3JnL3dpa2kvRmVkb3JhXzIxX01hc3NfUmVidWlsZAAtIFJlYnVpbGQgYWdhaW5zdCBwcHBkIDIuNC42AC0gVXBkYXRlIHRvIGEgZ2l0IHNuYXBzaG90IChnaXQyMDE0MDMxOSBnaXQ6Mzk4MDgwNikKLSBSZW5hbWUgTmV0d29ya01hbmFnZXItYXRtIHBhY2thZ2UgdG8gTmV0d29ya01hbmFnZXItYWRzbAotIFJlbmFtZSBOZXR3b3JrTWFuYWdlci1idCBwYWNrYWdlIHRvIE5ldHdvcmtNYW5hZ2VyLWJsdWV0b290aAAtIFVwZGF0ZSB0byBhIGdpdCBzbmFwc2hvdCAoZ2l0MjAxNDAzMTcgZ2l0OmExZTg5YjQpCi0gcGxhdGZvcm06IGZpeCBOTSBjcmFzaCBpZiBsaW5rIGhhcyBubyBuYW1lIChlLmcuIGZvciBmYWlsZWQgVlBOIGNvbm5lY3Rpb24pCi0gbGlibm0tdXRpbC9jbGk6IGZpeCBicmlkZ2UgcHJpb3JpdHkgZGVmYXVsdCB2YWx1ZSAocmggIzEwNzM2NjQpAC0gVXBkYXRlIHRvIGEgZ2l0IHNuYXBzaG90IChnaXQyMDE0MDMxNCBnaXQ6NDVhMzI2ZCkKLSBGaXggT2Jzb2xldGVzIGFuZCBSZXF1aXJlcyB0byBwZXJmb3JtIHVwZGF0ZXMgY29ycmVjdGx5AC0gVXBkYXRlIHRvIGEgZ2l0IHNuYXBzaG90IChnaXQyMDE0MDMxMCBnaXQ6MzUwYjZkNikALSBuZXcgdXBzdHJlYW0gc25hcHNob3Qgd2l0aCBkZXZlbG9wbWVudCB2ZXJzaW9uIDAuOS45LjEALSBhZGQgbm10dWkgcGFja2FnZQotIGJ1Z2ZpeCBjYWNoaW5nIG9mIGxpYm5sIG9iamVjdHMgKGNhdXNlZCBlcnJvciB3aXRoIG5ldyBsaWJubDMgdmVyc2lvbiB3aGVuIGFjdGl2YXRpbmcgYnJpZGdlcykgKHJoICMxMDYzMjkwKQotIGZpeCBOTU1hbmFnZXI6c3RhcnR1cCB0cmFja2luZyAocGVuZGluZyBhY3Rpb24pIChyaCAjMTAzMDU4MykALSBjb3JlOiBmaXggY3Jhc2ggZ2V0dGluZyBzZWNyZXRzIGluIGxpYm5tLWdsaWIALSBVcGRhdGUgdG8gYSBnaXQgc25hcHNob3QgKGdpdDIwMTQwMTMxKQAtIFVwZGF0ZSB0byBhIGdpdCBzbmFwc2hvdCAoZ2l0MjAxNDAxMTcpAC0gVXBkYXRlIHRvIGEgZ2l0IHNuYXBzaG90IChnaXQyMDE0MDExNCkALSBibHVlei1tYW5hZ2VyOiBmaXggYSBjcmFzaCAocmggIzEwNDg3MTEpAC0gY29yZTogZml4IElQdjYgcm91dGVyIHNvbGljaXRhdGlvbiBsb29wIChyaCAjMTA0NDc1NykALSBjb3JlOiB3YWl0IGZvciBsaW5rIGJlZm9yZSBkZWNsYXJpbmcgc3RhcnR1cCBjb21wbGV0ZSAocmggIzEwMzQ5MjEpCi0gY29yZTogaWdub3JlIFJBLXByb3ZpZGVkIElQdjYgZGVmYXVsdCByb3V0ZXMgKHJoICMxMDI5MjEzKQotIGNvcmU6IHNldCBJUHY0IGJyb2FkY2FzdCBhZGRyZXNzIGNvcnJlY3RseSAocmggIzEwMzI4MTkpAC0gY29yZTogRml4IFB0UC9wZWVyIGFkZHJlc3Mgc3VwcG9ydCwgZm9yIE9wZW5WUE4gKHJoICMxMDE4MzE3KQAtIGRpc3BhdGNoZXI6IGZpeCBjcmFzaCBvbiBleGl0IHdoaWxlIGxvZ2dpbmcgZnJvbSBzaWduYWwgaGFuZGxlciAocmggIzEwMTc4ODQpCi0gY29yZTogd29ya2Fyb3VuZCBjcmFzaCB3aGVuIGNvbm5lY3RpbmcgdG8gd2lmaSAocmggIzEwMjUzNzEpCi0gZXRoZXJuZXQ6IGRvbid0IGNyYXNoIGlmIGRldmljZSBkb2Vzbid0IGhhdmUgYSBNQUMgYWRkcmVzcyAocmggIzEwMjkwNTMpCi0gbGlibm0tZ2xpYjogZml4IGNyYXNoIGJ5IHRha2luZyBhZGRpdGlvbmFsIHJlZiBpbiByZXN1bHRfY2IoKSAocmggIzEwMzA0MDMpCi0gaWZjZmctcmg6IGZpeCBpZ25vcmluZyB1cGRhdGVzIHRoYXQgZG9uJ3QgY2hhbmdlIGFueXRoaW5nAC0gbm1jbGk6IGFkZCAiY29uIGxvYWQiIHRvIG1hbnVhbGx5IGxvYWQgYW4gaWZjZmcgZmlsZQotIHZwbjogZml4IGxvZ2dpbmcgdG8gaGVscCBkZWJ1ZyByaCAjMTAxODMxNwotIGJyaWRnZTogZml4IGNyYXNoIHdpdGggYnJpZGdlIHBvcnRzIHdpdGggZW1wdHkgc2V0dGluZ3MgKHJoICMxMDMxMTcwKQAtIGNvcmU6IGZpeCBkZXRlY3Rpb24gb2Ygbm9uLW1hYzgwMjExIGRldmljZXMgdGhhdCBkbyBub3Qgc2V0IERFVlRZUEUgKHJoICMxMDE1NTk4KQAtIGNvcmU6IGFkZCBzb21lIGRlYnVnZ2luZyB0byBoZWxwIGRpYWdub3NlIG5ldGxpbmsgZXJyb3JzIChyaCAjMTAyOTIxMykALSBpZmNmZy1yaDogZml4IGNyYXNoIGluIGlmY2ZnLXJoIHBsdWdpbiB3aGVuIHJlbG9hZGluZyBjb25uZWN0aW9ucyAocmggIzEwMjM1NzEpCi0gaWZjZmctcmg6IGZpeCBjcmFzaCB3aGVuIGhhdmluZyBjb25uZWN0aW9ucyB3aXRoIE5FVkVSX0RFRkFVTFQgKHJoICMxMDIxMTEyKQotIGNvcmU6IGZpeCBzZWdmYXVsdCBpbiBubS1wb2xpY3kgd2hlbiBzZXR0aW5nIGRlZmF1bHQgcm91dGUgZm9yIHZwbiAocmggIzEwMTkwMjEpCi0gaWZjZmctcmg6IGZpeCBjcmFzaCB3aGVuIHJlYWRpbmcgY29ubmVjdGlvbiAoYXNzZXJ0KSAocmggIzEwMjUwMDcpCi0gY29yZTogYWxsb3cgSVB2NCB0byBwcm9jZWVkIGlmIElQdjYgaXMgZ2xvYmFsbHkgZGlzYWJsZWQgYnV0IHNldCB0byAiYXV0byIgKHJoICMxMDEyMTUxKQAtIGNvcmU6IGZpeCBESENQdjYgYWRkcmVzcyBwcmVmaXggbGVuZ3RoIChyaCAjMTAxMzU4MykKLSBjbGk6IGVuaGFuY2UgYm9uZGluZyBxdWVzdGlvbmFpcmUgKHJoICMxMDA3MzU1KQotIGNvcmU6IGZpeCBjcmFzaCB3aXRoIEJsdWV6NSBpZiBQQU4gY29ubmVjdGlvbiBpcyBub3QgZGVmaW5lZCAocmggIzEwMTQ3NzApCi0gbGlibm0tZ2xpYjogZml4IHZhcmlvdXMgbWVtb3J5IGxlYWtzIHRoYXQgY291bGQgY2F1c2UgVUlzIHRvIG1pcy1yZXBvcnQgc3RhdGUKLSBjb3JlOiBmaXggaXNzdWVzIHdpdGggbWlzLWNvbmZpZ3VyZWQgSVB2NiByb3V0ZXIgYWR2ZXJ0aXNlbWVudHMgKHJoICMxMDA4MTA0KQotIGNsaTogZml4IHBvdGVudGlhbCBjcmFzaCBlZGl0aW5nIGNvbm5lY3Rpb25zIChyaCAjMTAxMTk0MikALSBjb3JlOiBmaXggYnJpZGdlIGRldmljZSBjcmVhdGlvbiAoIzEwMTI1MzIpCi0gY29yZSxzZXR0aW5nczogZG8gbm90IGNhbGwgZnVuY3Rpb25zIHdpdGggY29ubmVjdGlvbj09TlVMTCAocmggIzEwMDgxNTEpCi0gY2xpOiBhY2NlcHQgZ2F0ZXdheSBpbiB0aGUgSVAgcXVlc3Rpb25uYWlyZSBvZiAnbm1jbGkgLWEgY29uIGFkZCcgKHJoICMxMDA3MzY4KQotIGNsaTogYWx3YXlzIHByaW50IHN1Y2Nlc3MgbWVzc2FnZSAobm90IG9ubHkgaW4gLS1wcmV0dHkgbW9kZSkgKHJoICMxMDA2NDQ0KQotIGNsaTogZml4IGJvbmQgcXVlc3Rpb25uYWlyZSB0byBiZSBhYmxlIHRvIHNldCBtaWltb24gKHJoICMxMDA3MzU1KQotIGlmY2ZnLXJoOiBpZiBJUHY0IGlzIGRpc2FibGVkIHB1dCBETlMgZG9tYWlucyAoRE9NQUlOKSBpbnRvIElQdjYgKHJoICMxMDA0ODY2KQotIHBsYXRmb3JtOiBmaXggYSBjcmFzaCB3aGVuIG5tX3BsYXRmb3JtX3N5c2N0bF9nZXQoKSByZXR1cm5zIE5VTEwgKHJoICMxMDEwNTIyKQotIHBsYXRmb3JtOiBmaXggSW5maW5pQmFuZCBwYXJ0aXRpb24gaGFuZGxpbmcgKHJoICMxMDA4NTY4KQotIGluZmluaWJhbmQ6IG9ubHkgY2hlY2sgdGhlIGxhc3QgOCBieXRlcyB3aGVuIGRvaW5nIGh3YWRkciBtYXRjaGVzIChyaCAjMTAwODU2NikKLSBibHVlejogbWVyZ2UgYWRkaW5nIHN1cHBvcnQgZm9yIEJsdWVaIDUgKGJnbyAjNzAxMDc4KQotIGFwaTogY2xhcmlmeSBsaWZldGltZSBhbmQgYmVoYXZpb3Igb2YgQWN0aXZlQ29ubmVjdGlvbidzIFNwZWNpZmljT2JqZWN0IHByb3BlcnR5IChyaCAjMTAxMjMwOSkKLSB2cG46IGZpeCBjb25uZWN0aW5nIHRvIFZQTiAoYmdvICM3MDgyNTUpIChyaCAjMTAxNDcxNikKLSByZGlzYzogZG8gbm90IGNyYXNoIG9uIE5EUCBpbml0IGZhaWx1cmVzIChyaCAjMTAxMjE1MSkKLSBjbGk6IGJlIG1vcmUgdmVyYm9zZSB3aGVuIGFkZGluZyBJUCBhZGRyZXNzZXMgaW4gcXVlc3Rpb25uYWlyZSAocmggIzEwMDY0NTApCi0gdGVhbTogY2hhaW4gdXAgcGFyZW50IGRpc3Bvc2UoKSBpbiBOTURldmljZVRlYW0gZGlzcG9zZSgpIChyaCAjMTAxMzU5MykKLSB0cmFuc2xhdGlvbiB1cGRhdGVzAC0gZHJvcCB3aW1heCBzdWJwYWNrYWdlAC0gY29yZTogYWN0dWFsbHkgZW5hYmxlIE1vZGVtTWFuYWdlciAxLjAgc3VwcG9ydAotIGxpYm5tLWdsaWI6IGZpeCBubV9yZW1vdGVfY29ubmVjdGlvbl9kZWxldGUoKSBub3QgY2FsbGluZyBjYWxsYmFjayAocmggIzk5NzU2OCkKLSBjbGk6IGVuc3VyZSB0ZXJtaW5hbCBpcyByZXNldCBhZnRlciBxdWl0dGluZwotIGNsaTogc2V0IHdlcC1rZXktdHlwZSBwcm9wZXJseSB3aGVuIGVkaXRpbmcgKHJoICMxMDAzOTQ1KQotIG1hbjogZml4IHR5cG8gaW4gbm1jbGkgZXhhbXBsZXMgbWFucGFnZSAocmggIzEwMDQxMTcpCi0gY29yZTogZml4IHNldHRpbmcgVkxBTiBpbmdyZXNzL2VncmVzcyBtYXBwaW5ncwotIGNvcmU6IGFsbG93IGNyZWF0aW5nIFZMQU5zIGZyb20gaW50ZXJmYWNlcyBvdGhlciB0aGFuIEV0aGVybmV0IChyaCAjMTAwMzE4MCkKLSBjbGk6IGZpeCBpbnB1dC9vdXRwdXQgZm9ybWF0IGNvbnZlcnNpb24gKHJoICM5OTg5MjkpAC0gY29yZTogZml4IGJ1ZyB3aGljaCBkaXNhbGxvd2VkIGRlbGV0aW5nIGNvbm5lY3Rpb25zIChyaCAjOTk3NTY4KQotIGNvcmU6IGFkZCBzdXBwb3J0IGZvciBUZWFtIGRldmljZXMKLSBjb3JlOiBlbmFibGUgTmV0d29ya01hbmFnZXItd2FpdC1vbmxpbmUgYnkgZGVmYXVsdCAocmggIzgxNjY1NSkKLSBjb3JlOiBmaXggY3Jhc2ggd2hlbiAnZ3JlJyBhbmQgJ21hY3ZsYW4nIGxpbmtzIGNoYW5nZSAocmggIzk5NzM5NikKLSBjb3JlOiBmYWlsIGFjdGl2YXRpb24gd2hlbiBpbnZhbGlkIHN0YXRpYyByb3V0ZXMgYXJlIGNvbmZpZ3VyZWQgKHJoICM5OTk1NDQpCi0gY29yZTogZW5oYW5jZSBjb25uZWN0aXZpdHkgY2hlY2tpbmcgdG8gaW5jbHVkZSBwb3J0YWwgZGV0ZWN0aW9uCi0gY29yZTogYWxsb3cgaHlwaGVucyBmb3IgTUFDIGFkZHJlc3NlcyAocmggIzEwMDI1NTMpCi0gY29yZTogcmVtb3ZlIE5ldHdvcmtNYW5hZ2VyLWNyZWF0ZWQgc29mdHdhcmUgZGV2aWNlcyB3aGVuIHRoZXkgYXJlIGRlYWN0aXZhdGVkIChyaCAjOTUzMzAwKQotIGNvcmU6IGZpeCBoYW5kbGluZyBvZiBzb21lIERIQ1AgY2xpZW50IGlkZW50aWZpZXJzIChyaCAjOTk5NTAzKQotIGNvcmU6IGNvcnJlY3RseSBoYW5kbGUgT3BlbiB2U3dpdGNoIGludGVyZmFjZXMgYXMgZ2VuZXJpYyBpbnRlcmZhY2VzIChyaCAjMTAwNDM1NikKLSBjb3JlOiBiZXR0ZXIgaGFuZGxlIExheWVyLTItb25seSBjb25uZWN0aW9ucyAocmggIzk3OTI4OCkKLSBjbGk6IGVuaGFuY2VkIGJhc2ggY29tcGxldGlvbgotIGNsaTogbWFrZSB0aGUgJ2Rlc2NyaWJlJyBjb21tYW5kIG1vcmUgdmlzaWJsZSAocmggIzk5ODAwMikKLSBjbGk6IGZpeCBidWcgcmVqZWN0aW5nIGNoYW5nZXMgdG8gV2ktRmkgY2hhbm5lbHMgKHJoICM5OTk5OTkpCi0gY2xpOiB1cGRhdGUgYmFzaCBjb21wbGV0aW9uIHRvIHN1Z2dlc3QgY29ubmVjdGlvbiBuYW1lcyAocmggIzk5Nzk5NykKLSBjbGk6IGZpeCB0YWIgY29tcGxldGlvbiBmb3IgYWxpYXNlcyBpbiBlZGl0IG1vZGUKLSBjbGk6IGFzayB3aGV0aGVyIHRvIHN3aXRjaCBJUCBtZXRob2QgdG8gJ2F1dG8nIHdoZW4gYWxsIGFkZHJlc3NlcyBhcmUgZGVsZXRlZCAocmggIzk5ODEzNykKLSBjbGk6IHJlcXVlc3QgbWlzc2luZyBpbmZvcm1hdGlvbiB3aGVuIC0tYXNrIGlzIHBhc3NlZCAocmggIzk1MzI5MSkKLSBjbGk6IGFkZCAncmVtb3ZlJyBjb21tYW5kIHRvIGVkaXQgbW9kZQotIGNsaTogZml4IGNyZWF0aW9uIG9mIHNlY3VyZSBXaS1GaSBjb25uZWN0aW9ucyAocmggIzk5Nzk2OSkgKHJoICM5OTc1NTUpCi0gY2xpOiBkZWZhdWx0IGF1dG9jb25uZWN0IHRvIG5vIGFuZCBhc2sgd2hldGhlciB0byBhY3RpdmF0ZSBvbiBzYXZlIChyaCAjOTUzMjk2KQotIG1hbjogY2xhcmlmeSBtYW5wYWdlIHRleHQgKHJoICM5NjAwNzEpIChyaCAjOTUzMjk5KQotIG1hbjogZml4IGVycm9ycyBpbiB0aGUgbm1jbGkgaGVscCBvdXRwdXQgYW5kIG1hbnBhZ2UgKHJoICM5OTc1NjYpCi0gaWZjZmctcmg6IG9ubHkgd3JpdGUgSVBWNl9ERUZBVUxUR1cgd2hlbiB0aGVyZSdzIGFjdHVhbGx5IGEgZGVmYXVsdCBnYXRld2F5IChyaCAjOTk3NzU5KQotIGlmY2ZnLXJoOiBmaXggaGFuZGxpbmcgb2YgbGVnYWN5LWZvcm1hdCByb3V0ZXMgZmlsZSB3aXRoIG1pc3NpbmcgZ2F0ZXdheQAtIGNvcmU6IGZpeCBhc3NlcnQgb24gbXVsdGktaG9wIHJvdXRlcyAocmggIzk4OTAyMikKLSBjb3JlOiBmaXggZGlzcGF0Y2hlciBzeXN0ZW1kIHVuaXQgZW5hYmxpbmcgKHJoICM5NDg0MzMpCi0gaWZjZmctcmg6IGlnbm9yZSBlbWFjcyB0ZW1wb3JhcnkgbG9ja2ZpbGVzIChyaCAjOTg3NjI5KQotIGNvcmU6IGZpeCB2YXJpb3VzIHJvdXRpbmcgaXNzdWVzIGFuZCBpbnRlcmFjdGlvbiB3aXRoIGtlcm5lbCBldmVudHMKLSBjbGk6IGNvbmZpcm0gc2F2aW5nIGNvbm5lY3Rpb25zIHdoZW4gYXV0b2Nvbm5lY3QgaXMgZW5hYmxlZCAocmggIzk1MzI5NikKLSBjbGk6IGF1dG9tYXRpY2FsbHkgY2hhbmdlIG1ldGhvZCB3aGVuIHN0YXRpYyBJUCBhZGRyZXNzZXMgYXJlIGFkZGVkCi0gY29yZTogcHJlc2VydmUgZXh0ZXJuYWxseSBhZGRlZCBJUHY0IHJvdXRlcyBhbmQgYWRkcmVzc2VzAC0gQ3JlYXRlIE5ldHdvcmtNYW5hZ2VyLWNvbmZpZy1zZXJ2ZXIgcGFja2FnZQAtIFVwZGF0ZSB0byBnaXQgc25hcHNob3QALSBCZWxhdGVkbHkgdXBkYXRlIHVkZXYgZGlyZWN0b3J5IGZvciBVc3JNb3ZlCi0gRml4IGluY29ycmVjdCBkYXRlcyBpbiBvbGQgY2hhbmdlbG9nIGVudHJpZXMgdG8gYXZvaWQgcnBtIHdhcm5pbmdzAC0gYnVpbGQgc3VwcG9ydCBmb3IgY29ubmVjdGl2aXR5IGNoZWNraW5nIChyaCAjODEwNDU3KQAtIGRpc2FibGUgYnVpbGRpbmcgV2lNYXggZm9yIFJIRUwALSBVcGRhdGUgdG8gbmV3IDAuOS4xMCBzbmFwc2hvdAAtIFVwZGF0ZSBmb3Igc3lzdGVtZCBuZXR3b3JrLW9ubGluZS50YXJnZXQgKHJoICM3ODczMTQpCi0gQWRkIHN5c3RlbSBzZXJ2aWNlIGZvciB0aGUgc2NyaXB0IGRpc3BhdGNoZXIgKHJoICM5NDg0MzMpAC0gRW5hYmxlIGhhcmRlbmVkIGJ1aWxkCi0gVXBkYXRlIHRvIDAuOS4xMCBzbmFwc2hvdAotIGNsaTogbmV3IGNhcGFiaWxpdGllcyBhbmQgc29tZXdoYXQgcmUtYXJyYW5nZWQgc3ludGF4Ci0gY29yZTogZ2VuZXJpYyBpbnRlcmZhY2Ugc3VwcG9ydAotIGNvcmU6IHNwbGl0IGNvbmZpZyBzdXBwb3J0OyBuZXcgInNlcnZlciBtb2RlIiBvcHRpb25zCi0gY29yZTogYWxsb3cgbG9ja2luZyBjb25uZWN0aW9ucyB0byBpbnRlcmZhY2UgbmFtZXMALSBjb3JlOiBmaXggaXNzdWUgd2l0aCBVSSBub3Qgc2hvd2luZyBkaXNjb25uZWN0ZWQgb24gcmZraWxsCi0gY29yZTogbWVtb3J5IGxlYWsgZml4ZXMKLSBjb3JlOiBzaWxlbmNlIHdhcm5pbmcgYWJvdXQgZmFpbHVyZSByZWFkaW5nIHBlcm1hbmVudCBNQUMgYWRkcmVzcyAocmggIzkwNzkxMikKLSBjb3JlOiB3YWl0IHVwIHRvIDEyMHMgZm9yIHNsb3ctY29ubmVjdGluZyBtb2RlbXMKLSBjb3JlOiBkb24ndCBjcmFzaCBvbiBQUFBvRSBjb25uZWN0aW9ucyB3aXRob3V0IGEgd2lyZWQgc2V0dGluZwotIGNvcmU6IGVuc3VyZSB0aGUgQXZhaWxhYmxlQ29ubmVjdGlvbnMgcHJvcGVydHkgaXMgYWx3YXlzIGNvcnJlY3QKLSBrZXlmaWxlOiBlbnN1cmUgYWxsLWRlZmF1bHQgVkxBTiBjb25uZWN0aW9ucyBhcmUgcmVhZCBjb3JyZWN0bHkKLSBjb3JlOiBzdXBwcmVzcyBrZXJuZWwncyBhdXRvbWF0aWMgY3JlYXRpb24gb2YgYm9uZDAgKHJoICM5NTM0NjYpCi0gbGlibm0tZ2xpYjogbWFrZSBOTVNlY3JldEFnZW50IHVzYWJsZSB3aXRoIEdPYmplY3QgSW50cm9zcGVjdGlvbgotIGxpYm5tLXV0aWw6IGZpeCBHT2JqZWN0IEludHJvc3BlY3Rpb24gYW5ub3RhdGlvbnMgb2Ygbm1fY29ubmVjdGlvbl9uZWVkX3NlY3JldHMoKQotIGNvcmU6IGRvY3VtZW50YXRpb24gdXBkYXRlcwAtIFVwZGF0ZSB0byAwLjkuOC4yIHNuYXBzaG90Ci0gY29yZTogZml4IFZMQU4gcGFyZW50IGhhbmRsaW5nIHdoZW4gaWRlbnRpZmllZCBieSBVVUlECi0gY29yZTogcXVpZXQgd2FybmluZyBhYm91dCBpbnZhbGlkIGludGVyZmFjZSBpbmRleCAocmggIzkyMDE0NSkKLSBjb3JlOiByZXF1ZXN0ICdzdGF0aWMtcm91dGVzJyBmcm9tIERIQ1Agc2VydmVycyAocmggIzkyMjU1OCkKLSBjb3JlOiBmaXggY3Jhc2ggd2hlbiBkYnVzLWRhZW1vbiBpcyByZXN0YXJ0ZWQgKHJoICM5MTgyNzMpCi0gY29yZTogY29weSBsZWFzZWZpbGVzIGZyb20gL3Zhci9saWIvZGhjbGllbnQgdG8gZml4IG5ldGJvb3QgKHJoICM5MTYyMzMpCi0gY29yZTogbWVtb3J5IGxlYWsgYW5kIHBvdGVudGlhbCBjcmFzaCBmaXhlcwotIGlmY2ZnLXJoOiBlbnN1cmUgbWlzc2luZyBTVFAgcHJvcGVydHkgaXMgaW50ZXJwcmV0ZWQgYXMgb2ZmIChyaCAjOTIyNzAyKQAtIFVwZGF0ZSB0byB0aGUgMC45LjguMCByZWxlYXNlCi0gY2xpOiBmaXggYSBwb3NzaWJsZSBjcmFzaAAtIGNvcmU6IHVzZSBzeXN0ZW1kIGZvciBzdXNwZW5kL3Jlc3VtZSwgbm90IHVwb3dlcgAtIFVwZGF0ZSB0byAwLjkuOC1iZXRhMgotIGNvcmU6IGlnbm9yZSBicmlkZ2VzIG1hbmFnZWQgYnkgb3RoZXIgdG9vbHMgKHJoICM5MDUwMzUpCi0gY29yZTogZml4IGxpYm5sIGFzc2VydCAocmggIzg5NDY1MykKLSB3aWZpOiBhbHdheXMgdXNlIFByb2FjdGl2ZSBLZXkgQ2FjaGluZyB3aXRoIFdQQSBFbnRlcnByaXNlIChyaCAjODM0NDQ0KQotIGNvcmU6IGRvbid0IGNyYXNoIHdoZW4gSW50ZXJuZXQgY29ubmVjdGlvbiBzaGFyaW5nIGZhaWxzIHRvIHN0YXJ0IChyaCAjODgzMTQyKQAtIFNldCBjb3JyZWN0IHN5c3RlbWQgS2lsbE1vZGUgdG8gZml4IGFuYWNvbmRhIHNodXRkb3duIGhhbmdzIChyaCAjODc2MjE4KQAtIGlmY2ZnLXJoOiB3cml0ZSBtaXNzaW5nIElQdjYgc2V0dGluZyBhcyBJUHY2IHdpdGggImF1dG8iIG1ldGhvZCAocmggIzgzMDQzNCkALSBCdWlsZCB2YXBpIGZpbGVzIGFuZCBhZGQgdGhlbSB0byB0aGUgZGV2ZWwgcGFja2FnZQAtIEFwcGx5IHBhdGNoIGZyb20gbWFzdGVyIHRvIHJlYWQgaG9zdG5hbWUgZnJvbSAvZXRjL2hvc3RuYW1lIChyaCAjODMxNzM1KQAtIEFwcGx5IHBhdGNoIGZyb20gbWFzdGVyIHRvIHVwZGF0ZSBob3N0bmFtZSAocmggIzg3NTA4NSkKLSBzcGVjOiBjcmVhdGUgL2V0Yy9OZXR3b3JrTWFuYWdlci9kbnNtYXNxLmQgKHJoICM4NzM2MjEpAC0gRG9uJ3QgYnJpbmcgdXAgdW5pbml0aWFsaXplZCBkZXZpY2VzIChmZCAjNTY5MjkpAC0gQWN0dWFsbHkgYXBwbHkgdGhlIHBhdGNoIGZyb20gdGhlIHByZXZpb3VzIGNvbW1pdC4uLgAtIEFwcGx5IHBhdGNoIGZyb20gbWFzdGVyIHRvIGZpeCBhIGNyYXNoIChyaCAjODY1MDA5KQAtIEFwcGx5IHBhdGNoIGZyb20gbWFzdGVyIHNvIGNvbm5lY3Rpb25zIGZpbmlzaCBjb25uZWN0aW5nIHByb3Blcmx5IChiZ28gIzY4NTU4MSkALSBGb3J3YXJkLXBvcnQgc29tZSBmb3Jnb3R0ZW4gZml4ZXMgZnJvbSBGMTcKLSBGaXggbmV0d29ya2VkLWZpbGVzeXN0ZW0gc3lzdGVtZCBkZXBlbmRlbmNpZXMgKHJoICM3ODczMTQpCi0gRG9uJ3QgcmVzdGFydCBOTSBvbiB1cGdyYWRlLCBkb24ndCBzdG9wIE5NIG9uIHVuaW5zdGFsbCAocmggIzgxMTIwMCkALSBVcGRhdGUgdG8gZ2l0IHNuYXBzaG90AC0gVXBkYXRlIHRvIDAuOS43LjAgc25hcHNob3QALSBSZWJ1aWx0IGZvciBodHRwczovL2ZlZG9yYXByb2plY3Qub3JnL3dpa2kvRmVkb3JhXzE4X01hc3NfUmVidWlsZAAtIFVwZGF0ZSB0byAwLjkuNi1yYzIKLSBjb3JlOiBmaXggcmFjZSBiZXR3ZWVuIHBhcmFsbGVsIERIQ1AgY2xpZW50IGludm9jYXRpb25zCi0gY29yZTogc3VwcHJlc3MgYSB1c2VsZXNzIHdhcm5pbmcgKHJoICM4NDA1ODApCi0gaWZjZmctcmg6IGZpeCBzZWdmYXVsdCB3aXRoIG1hbGZvcm1lZCB2YWx1ZXMgKHJoICM4NDEzOTEpCi0gaWZjZmctcmg6IGlnbm9yZSBJUCBjb25maWcgb24gYm9uZCBzbGF2ZSBjb25maWd1cmF0aW9ucyAocmggIzgzODkwNykALSBVcGRhdGUgdG8gMC45LjUuOTUgKDAuOS42LXJjMSkgc25hcHNob3QKLSBjb3JlOiBhZGQgYXV0b2Nvbm5lY3QsIGRyaXZlci12ZXJzaW9uaSBhbmQgZmlybXdhcmUtdmVyc2lvbiBwcm9wZXJ0aWVzIHRvIE5NRGV2aWNlCi0gY29yZTogdmFyaW91cyBJUHY2IGltcHJvdmVtZW50cwotIGNvcmU6IHJlZHVjZSBudW1iZXIgb2YgY2hhbmdlcyBtYWRlIHRvIEROUyBpbmZvcm1hdGlvbiBkdXJpbmcgY29ubmVjdGlvbiBzZXR1cAotIGNvcmU6IGFkZCBWYWxhIGxhbmd1YWdlIGJpbmRpbmdzCi0gdnBuOiBzdXBwb3J0IElQdjYgb3ZlciBWUE5zCi0gd2lmaTogYWRkIG9uLWRlbWFuZCBXaUZpIHNjYW4gc3VwcG9ydAAtIFVwZGF0ZSB0byBnaXQgc25hcHNob3QALSBOTSBubyBsb25nZXIgdXNlcyAvdmFyL3J1bi9OZXR3b3JrTWFuYWdlciwgc28gZG9uJ3QgY2xhaW0gdG8gb3duIGl0LgogIChyaCAjNjU2NjM4KQAtIFVwZGF0ZSB0byBnaXQgc25hcHNob3QALSBBZGQgX2lzYSBmb3IgaW50ZXJuYWwgcmVxdWlyZXM7IG90aGVyd2lzZSBkZXBzb2x2aW5nIG1heSBwdWxsIGluIGFuCiAgYXJiaXRyYXJ5IGFyY2hpdGVjdHVyZS4ALSBVcGRhdGUgdG8gMC45LjQALSBsaWJubS1nbGliOiB1cGRhdGVkIGZvciBuZXcgc3ltYm9scyB0aGUgYXBwbGV0IHdhbnRzAC0gYXBwbGV0OiBtb3ZlIHRvIG5ldHdvcmstbWFuYWdlci1hcHBsZXQgUlBNCi0gZWRpdG9yOiBtb3ZlIHRvIG5tLWNvbm5lY3Rpb24tZWRpdG9yIFJQTQotIGxpYm5tLWd0azogbW92ZSB0byBsaWJubS1ndGsgUlBNAC0gVXBkYXRlIHRvIDAuOS4zLjk5NyAoMC45LjQtcmMxKQotIGNvcmU6IGZpeCBwb3NzaWJsZSBXaUZpIGhhbmcgd2hlbiBjb25uZWN0aW5nIHRvIEFkLUhvYyBuZXR3b3JrcwotIGNvcmU6IGVuaGFuY2VkIElQdjYgY29tcGF0aWJpbGl0eQotIGNvcmU6IHByb3h5IEROU1NFQyBkYXRhIHdoZW4gdXNpbmcgdGhlICdkbnNtYXNxJyBjYWNoaW5nIG5hbWVzZXJ2ZXIgcGx1Z2luCi0gY29yZTogYWxsb3cgVlBOcyB0byBzcGVjaWZ5IG11bHRpcGxlIGRvbWFpbiBuYW1lcyBnaXZlbiBieSB0aGUgc2VydmVyCi0gY29yZTogZml4IGFuIGlzc3VlIGNyZWF0aW5nIG5ldyBJbmZpbmlCYW5kIGNvbm5lY3Rpb25zCi0gY29yZS9hcHBsZXQvZWRpdG9yOiBkaXNhYmxlIFdpRmkgQWQtSG9jIFdQQSBjb25uZWN0aW9ucyB1bnRpbCBrZXJuZWwgYnVncyBhcmUgZml4ZWQALSBjb3JlOiBmaXggaXNzdWUgd2l0aCBjYXJyaWVyIGNoYW5nZXMgbm90IGJlaW5nIHJlY29nbml6ZWQgKHJoICM4MDA2OTApCi0gZWRpdG9yOiB3YXJuIHVzZXIgaWYgQ0EgY2VydGlmaWNhdGUgaXMgbGVmdCBibGFuawAtIGNvcmU6IGZpeCBhIGNyYXNoIHdpdGggaXB3MjIwMCBkZXZpY2VzIGFuZCBhZGhvYyBuZXR3b3JrcwotIGNvcmU6IGZpeCBJUHY2IGFkZHJlc3Npbmcgb24gbmV3ZXIga2VybmVscwotIGNvcmU6IGZpeCBpc3N1ZSB3aXRoIFZQTiBwbHVnaW4gcGFzc3dvcmRzIChyaCAjODAyNTQwKQotIGNsaTogZW5oYW5jZW1lbnRzIGZvciBCb25kaW5nLCBWTEFOLCBhbmQgT0xQQyBtZXNoIGRldmljZXMKLSBpZmNmZy1yaDogZml4IHF1b3RpbmcgV1BBIHBhc3NwaHJhc2VzIHRoYXQgaW5jbHVkZSBxdW90ZXMgKHJoICM3OTgxMDIpCi0gbGlibm0tZ2xpYjogZml4IHNvbWUgaXNzdWVzIHdpdGggZHVwbGljYXRlIGRldmljZXMgc2hvd24gaW4gbWVudXMALSBVcGRhdGUgdG8gMC45LjMuOTk1ICgwLjkuNC1iZXRhMSkKLSBjb3JlOiBhZGQgc3VwcG9ydCBmb3IgYm9uZGluZyBhbmQgVkxBTiBpbnRlcmZhY2VzCi0gY29yZTogYWRkIHN1cHBvcnQgZm9yIEludGVybmV0IGNvbm5lY3Rpdml0eSBkZXRlY3Rpb24KLSBjb3JlOiBhZGQgc3VwcG9ydCBmb3IgSVB2NiBQcml2YWN5IEV4dGVuc2lvbnMKLSBjb3JlOiBmaXggaW50ZXJhY3Rpb24gd2l0aCBmaXJld2FsbGQgcmVzdGFydHMALSBkaXNhYmxlIFdpTUFYIHBsdWdpbiBvbiBzMzkwKHgpAC0gUHV0IFdpTUFYIHBsdWdpbiBmaWxlcyBpbiB0aGUgcmlnaHQgc3VicGFja2FnZQAtIFVwZGF0ZSB0byAwLjkuNCBzbmFwc2hvdAotIHdpbWF4OiBlbmFibGUgb3B0aW9uYWwgc3VwcG9ydCBmb3IgSW50ZWwgV2lNQVggZGV2aWNlcwotIGNvcmU6IHVzZSBubDgwMjExIGZvciBXaUZpIGRldmljZSBjb250cm9sCi0gY29yZTogYWRkIGJhc2ljIHN1cHBvcnQgZm9yIEluZmluaWJhbmQgSVAgaW50ZXJmYWNlcwotIGNvcmU6IGFkZCBiYXNpYyBzdXBwb3J0IGZvciBib25kZWQgaW50ZXJmYWNlcwotIGNvcmU6IGluLXByb2Nlc3MgSVAgY29uZmlndXJhdGlvbiBubyBsb25nZXIgYmxvY2tzIGNvbm5lY3RlZCBzdGF0ZQAtIFJlYnVpbGQALSBSZWJ1aWx0IGZvciBodHRwczovL2ZlZG9yYXByb2plY3Qub3JnL3dpa2kvRmVkb3JhXzE3X01hc3NfUmVidWlsZAAtIFJlYnVpbGQgZm9yIGxpYmdub21lLWJsdWV0b290aC5zby45AC0gY29yZTogZml4IHBvc3NpYmxlIGNyYXNoIHdoZW4gdGFsa2luZyB0byBNb2RlbU1hbmFnZXIKLSBjb3JlOiBpbXByb3ZlIGhhbmRsaW5nIG9mIHJma2lsbCBvbiBzb21lIG1hY2hpbmVzIChlZWVwYyAxMDA1SEEgYW5kIG90aGVycykKLSBpZmNmZy1yaDogZG9uJ3QgdXNlIHNwYWNlcyBpbiBpZmNmZyBmaWxlIG5hbWVzIChyaCAjNzQyMjczKQotIGNvcmU6IGFjY2VwdCBJUHY2IFJvdXRlciBBZHZlcnRpc2VtZW50cyB3aGVuIGZvcndhcmRpbmcgaXMgb24KLSBjb3JlOiBidW1wIGRuc21hc3EgY2FjaGUgc2l6ZSB0byA0MDAgZW50cmllcwotIGNvcmU6IGVuc3VyZSBJUHY2IHN0YXRpYyByb3V0ZXMgYXJlIGZsdXNoZWQgd2hlbiBkZXZpY2UgaXMgZGVhY3RpdmF0ZWQKLSBpZmNmZy1yaDogZml4IGNoYW5naW5nIFdQQSBjb25uZWN0aW9ucyB0byBXRVAKLSBjb3JlOiBmaXggc2V0dGluZyBob3N0bmFtZSBmcm9tIERIQ1AgKHJoICM3MTkxMDApCi0gbGlibm0tZ2xpYjogZml4IHZhcmlvdXMgR09iamVjdCBpbnRyb3NwZWN0aW9uIGlzc3VlcyAocmggIzc0NzMwMikKLSBjb3JlOiBkb24ndCBjaGFuZ2Ugcm91dGluZyBvciBETlMgaWYgbm8gZGV2aWNlcyBhcmUgbWFuYWdlZAotIGNvcmU6IGVuc3VyZSBJUHY2IFJBLXByb3ZpZGVkIHJvdXRlcyBhcmUgaG9ub3JlZAAtIFJlYnVpbHQgZm9yIGdsaWJjIChyaCAjNzQ3Mzc3KQotIGNvcmU6IGZpeCBzZXR0aW5nIGhvc3RuYW1lIGZyb20gREhDUCBvcHRpb25zIChyaCAjNzE5MTAwKQotIHNraXAgYSByZWxlYXNlIHRvIGtlZXAgdXAgd2l0aCBGMTYALSBjb3JlOiBmaXggbG9jYXRpb24gb2Ygd2lmaS51aSAocmggIzc0MTQ0OCkALSBjb3JlOiBpZmNmZy1yaDogcmVtb3ZlIG5ld2xpbmVzIHdoZW4gd3JpdGluZyB0byBpZmNmZyBmaWxlcyAoQ1ZFLTIwMTEtMzM2NCkgKHJoICM3MzczMzgpCi0gY29yZTogY2hhbmdlIGlzY3NpYWRtIHBhdGggdG8gL3NiaW4vaXNjc2lhZG0gaW4gaWZjZmctcmggcGx1Z2luIChyaCAjNzQwNzUzKQotIGNvcmU6IGZpeCByZWZjb3VudGluZyB3aGVuIGRlbGV0aW5nIGEgZGVmYXVsdCB3aXJlZCBjb25uZWN0aW9uIChscDo3OTc4NjgpAC0gVXBkYXRlIHRvIDAuOS4xLjkwICgwLjkuMi1iZXRhMSkKLSBjb3JlOiBmaXggSVB2NiBsaW5rLWxvY2FsIEROUyBzZXJ2ZXJzIGluIHRoZSBkbnNtYXNxIEROUyBwbHVnaW4KLSBjbGk6IGFkZCBhYmlsaXR5IHRvIGRlbGV0ZSBjb25uZWN0aW9ucwotIGtleWZpbGU6IGZpeCBhbiBpc3N1ZSB3aXRoIGR1cGxpY2F0ZWQga2V5ZmlsZSBjb25uZWN0aW9ucwotIGNvcmU6IGVuc3VyZSB0aGUgJ25vdmonIG9wdGlvbiBpcyBwYXNzZWQgdGhyb3VnaCB0byBwcHBkCi0gY29yZTogc3RvcmUgdGltZXN0YW1wcyBmb3IgVlBOIGNvbm5lY3Rpb25zIHRvbyAocmggIzcyNTM1MykALSBmaXggc3lzdGVtZCBzY3JpcHRsZXRzIGFuZCB0cmlnZ2VyAC0gVXBkYXRlIHRvIDAuOSByZWxlYXNlCi0gY29yZTogZml4IGlzc3VlIHdoZXJlIHNjYW4gcmVzdWx0cyBjb3VsZCBiZSBpZ25vcmVkCi0gY29yZTogZW5zdXJlIGFnZW50IHNlY3JldHMgYXJlIHByZXNlcnZlZCB3aGVuIHVwZGF0aW5nIGNvbm5lY3Rpb25zCi0gY29yZTogZG9uJ3QgYXV0b2Nvbm5lY3QgZGlzYWJsZWQgbW9kZW1zCi0gY29yZTogZml4IHJhY2Ugd2hlbiBjaGVja2luZyBtb2RlbSBlbmFibGVkL2Rpc2FibGVkIHN0YXR1cyBhZnRlciBkaXNhYmxpbmcKLSBjb3JlOiBlbnN1cmUgbmV3bHkgaW5zdGFsbGVkIFZQTiBwbHVnaW5zIGNhbiBhY3R1YWxseSB0YWxrIHRvIE5NCi0gY29yZTogYWRkIHN1cHBvcnQgZm9yIDgwMi4xWCBjZXJ0aWZpY2F0ZSBzdWJqZWN0IG1hdGNoaW5nCi0gbGlibm0tZ2xpYjogdmFyaW91cyBpbnRyb3NwZWN0aW9uIGZpeGVzCi0gYXBwbGV0L2VkaXRvcjogdXBkYXRlZCB0cmFuc2xhdGlvbnMALSBBZGQgc29tZSBwYXRjaGVzIGZvciBzb21lIGJsb2NrZXIgKHJoICM3Mjc1MDEpAC0gY29yZTogdXBkYXRlZCBSdXNzaWFuIHRyYW5zbGF0aW9uIChyaCAjNjUyOTA0KQotIGNvcmU6IGZpeCBwb3NzaWJsZSBjcmFzaCBpZiBzZWNyZXRzIGFyZSBtaXNzaW5nCi0gY29yZTogYXBwZW5kIGludGVyZmFjZSBuYW1lIGZvciBJUHY2IGxpbmstbG9jYWwgRE5TIHNlcnZlciBhZGRyZXNzZXMgKHJoICM3MjAwMDEpCi0gY29yZTogZml4IHNldHRpbmcgaG9zdG5hbWUgZnJvbSBESENQIG9wdGlvbnMgKHJoICM3MTkxMDApCi0gbGlibm0tdXRpbDogR09iamVjdCBpbnRyb3NwZWN0aW9uIGFubm90YXRpb24gZml4ZXMKLSBsaWJubS11dGlsOiBlbnN1cmUgSVAgYWRkcmVzcy9yb3V0ZSBwcmVmaXhlcyBhcmUgdmFsaWQKLSBpZmNmZy1yaDogcmVhZCBhbm9ueW1vdXMgaWRlbnRpdHkgZm9yIDgwMi4xeCBQRUFQIGNvbm5lY3Rpb25zIChyaCAjNzA4NDM2KQotIGFwcGxldDogc2hvdyBub3RpZmljYXRpb25zIG9uIENETUEgaG9tZS9yb2FtaW5nIGNoYW5nZXMKLSBhcHBsZXQ6IGZpeCB2YXJpb3VzIGlzc3VlcyBzYXZpbmcgVlBOIHNlY3JldHMKLSBlZGl0b3I6IGFsbG93IGV4cG9ydGluZyBWUE4gc2VjcmV0cwotIGVkaXRvcjogZGVmYXVsdCB0byBJUHY2ICJhdXRvbWF0aWMiIGFkZHJlc3NpbmcgbW9kZQAtIGNvcmU6IGVuc3VyZSB1c2VycyBhcmUgYXV0aG9yaXplZCBmb3Igc2hhcmVkIHdpZmkgY29ubmVjdGlvbnMgKENWRS0yMDExLTIxNzYpIChyaCAjNzE1NDkyKQotIGNvcmU6IHJldHJ5IGZhaWxlZCBjb25uZWN0aW9ucyBhZnRlciA1IG1pbnV0ZSB0aW1lb3V0Ci0gY29yZTogaW1tZWRpYXRlbHkgcmVxdWVzdCBuZXcgODAyLjF4ICdhbHdheXMgYXNrJyBwYXNzd29yZHMgaWYgdGhleSBmYWlsCi0gY29yZTogYWRkIE1BQyBibGFja2xpc3RpbmcgY2FwYWJpbGl0eSBmb3IgV2lGaSBhbmQgV2lyZWQgY29ubmVjdGlvbnMKLSBjb3JlOiByZXRyeSBmYWlsZWQgY29ubmVjdGlvbnMgd2hlbiBuZXcgdXNlcnMgbG9nIGluIChyaCAjNzA2MjA0KQotIGFwcGxldDogdXBkYXRlZCB0cmFuc2xhdGlvbnMKLSBjb3JlOiBkcm9wIGNvbXBhdCBpbnRlcmZhY2Ugbm93IHRoYXQgS0RFIGJpdHMgYXJlIHVwZGF0ZWQgdG8gTk0gMC45IEFQSQAtIGNvcmU6IGRvbid0IGNhY2hlICIobm9uZSkiIGhvc3RuYW1lIGF0IHN0YXJ0dXAgKHJoICM3MDYwOTQpCi0gY29yZTogZml4IGhhbmRsaW5nIG9mIFZQTiBjb25uZWN0aW9ucyB3aXRoIG9ubHkgc3lzdGVtLW93bmVkIHNlY3JldHMKLSBjb3JlOiBmaXggb3B0aW9uYWwgd2FpdGluZyBmb3IgbmV0d29ya2luZyBhdCBzdGFydHVwIGJlaGF2aW9yIChyaCAjNzEwNTAyKQotIGlmY2ZnLXJoOiBmaXggcG9zc2libGUgY3Jhc2hlcyBpbiBlcnJvciBjYXNlcwotIGlmY2ZnLXJoOiBmaXggdmFyaW91cyBJUHY0IGFuZCBJUHY2IGhhbmRsaW5nIGlzc3VlcwotIGFwcGxldDogYWRkIG5vdGlmaWNhdGlvbnMgb2YgR1NNIG1vYmlsZSBicm9hZGJhbmQgcmVnaXN0cmF0aW9uIHN0YXR1cwotIGVkaXRvcjogbW92ZSBzZWNyZXRzIHdoZW4gbWFraW5nIGNvbm5lY3Rpb25zIGF2YWlsYWJsZSB0byBhbGwgdXNlcnMgb3IgcHJpdmF0ZQotIGFwcGxldDogZG9uJ3Qgc2hvdyBpcnJlbGV2YW50IG9wdGlvbnMgd2hlbiBhc2tpbmcgZm9yIHBhc3N3b3JkcwAtIGtleWZpbGU6IGJldHRlciBoYW5kbGluZyBvZiBtaXNzaW5nIGNlcnRpZmljYXRlcy9wcml2YXRlIGtleXMKLSBjb3JlOiBmaXggaXNzdWVzIGhhbmRsaW5nICJhbHdheXMtYXNrIiB3aXJlZCBhbmQgV2lGaSA4MDIuMXggY29ubmVjdGlvbnMgKHJoICM3MDM3ODUpCi0gY29yZTogZml4IGF1dG9tYXRpYyBoYW5kbGluZyBvZiBoaWRkZW4gV2lGaSBuZXR3b3JrcyAocmggIzcwNzQwNikKLSBlZGl0b3I6IGZpeCBwb3NzaWJsZSBjcmFzaCBhZnRlciByZWFkaW5nIG5ldHdvcmsgY29ubmVjdGlvbnMgKHJoICM3MDY5MDYpCi0gZWRpdG9yOiBtYWtlIEVudGVyL1JldHVybiBrZXkgY2xvc2UgV2lGaSBwYXNzd29yZCBkaWFsb2dzIChyaCAjNzA4NjY2KQAtIEJ1bXAgZm9yIENWRS0yMDExLTE5NDMgKG5vIGNoYW5nZXMsIG9ubHkgYSByZWJ1aWxkKQAtIGVkaXRvcjogZml4IHJlc2l6aW5nIG9mIFVJIGVsZW1lbnRzIChyaCAjNzA3MjY5KQotIGNvcmU6IHJldHJ5IHdpcmVkIGNvbm5lY3Rpb25zIHdoZW4gY2FibGUgaXMgcmVwbHVnZ2VkCi0gY29yZTogZml4IGEgZmV3IHdhcm5pbmdzIGFuZCByZW1vdmUgc29tZSBsZWZ0LW92ZXIgZGVidWdnaW5nIGNvZGUALSBjb21wYXQ6IGZpeCBhY3RpdmF0aW9uL2RlYWN0aXZhdGlvbiBvZiBWUE4gY29ubmVjdGlvbnMgKHJoICM2OTk3ODYpCi0gY29yZTogZml4IGF1dG9kZXRlY3Rpb24gb2YgcHJldmlvdXNseS11c2VkIGhpZGRlbiB3aWZpIG5ldHdvcmtzCi0gY29yZTogc2lsZW5jZSBlcnJvciBpZiBDb25zb2xlS2l0IGRhdGFiYXNlIGRvZXMgbm90IHlldCBleGlzdCAocmggIzY5NTYxNykKLSBjb3JlOiBmaXggQWQtSG9jIGZyZXF1ZW5jeSBoYW5kbGluZyAocmggIzY5OTIwMykKLSBjb3JlOiBmaXhlcyBmb3IgbWlncmF0ZWQgT3BlbkNvbm5lY3QgVlBOIHBsdWdpbiBjb25uZWN0aW9ucwotIGNvcmU6IHZhcmlvdXMgZml4ZXMgZm9yIFZQTiBjb25uZWN0aW9uIHNlY3JldHMgaGFuZGxpbmcKLSBjb3JlOiBzZW5kIG9ubHkgc2hvcnQgaG9zdG5hbWUgdG8gREhDUCBzZXJ2ZXJzIChyaCAjNjk0NzU4KQotIGNvcmU6IGJldHRlciBoYW5kbGluZyBvZiBQS0NTIzggcHJpdmF0ZSBrZXlzCi0gY29yZTogZml4IGRpc3BhdGNoZXIgc2NyaXB0IGludGVyZmFjZSBuYW1lIGhhbmRsaW5nCi0gZWRpdG9yOiBmaXggcG90ZW50aWFsIGNyYXNoIHdoZW4gY29ubmVjdGlvbiBpcyBpbnZhbGlkIChyaCAjNzA0ODQ4KQotIGVkaXRvcjogYWxsb3cgXyBhcyBhIHZhbGlkIGNoYXJhY3RlciBmb3IgR1NNIEFQTnMALSBjb3JlOiBmaXggcG9zc2libGUgY3Jhc2ggd2hlbiBjb25uZWN0aW9ucyBhcmUgZGVsZXRlZAotIGNvcmU6IGZpeCBleHBvcnRlZCBzeW1ib2xzIGluIGxpYm5tLXV0aWwgYW5kIGxpYm5tLWdsaWIKLSBjb3JlL2FwcGxldDogdXBkYXRlZCB0cmFuc2xhdGlvbnMALSBjb3JlOiBlbnN1cmUgREVSIGZvcm1hdCBjZXJ0aWZpY2F0ZXMgYXJlIGNvcnJlY3RseSByZWNvZ25pemVkIChyaCAjNjk5NTkxKQotIGNvcmU6IGZpeCBXSU5TIHNlcnZlciBoYW5kbGluZyBpbiBjbGllbnQgaGVscGVyIGxpYnJhcmllcwotIGNvcmU6IGVuaGFuY2UgZGlzcGF0Y2hlciBzY3JpcHQgZW52aXJvbm1lbnQgdG8gaW5jbHVkZSBJUHY2IGFuZCBWUE4gZGV0YWlscwotIGFwcGxldDogbWlncmF0ZSBvcGVuc3dhbiBjb25uZWN0aW9ucyB0byAwLjkKLSBlZGl0b3I6IGltcHJvdmUgdXNhYmlsaXR5IG9mIGVkaXRpbmcgSVAgYWRkcmVzc2VzIChyaCAjNjk4MTk5KQAtIGNvcmU6IGVuYWJsZSBvcHRpbWl6ZWQgYmFja2dyb3VuZCByb2FtaW5nIGZvciBXUEEgRW50ZXJwcmlzZSBjb25maWdzCi0gY29yZTogYmV0dGVyIGhhbmRsaW5nIG9mIFdpRmkgYW5kIFdpTUFYIHJma2lsbCAocmggIzU5OTAwMikKLSBhcHBsZXQ6IGZpeCBjcmFzaCBkZXRlY3RpbmcgQmx1ZXRvb3RoIERVTiBkZXZpY2VzIGEgc2Vjb25kIHRpbWUKLSBpZmNmZy1yaDogZml4IG1hbmFnZWQvdW5tYW5hZ2VkIGNoYW5nZXMgd2hlbiByZW1vdmluZyBjb25uZWN0aW9ucyAocmggIzY5ODIwMikALSBjb3JlOiBzeXN0ZW1kIGFuZCBzdGFydHVwIGVuaGFuY2VtZW50cyBmb3IgTkZTIG1vdW50cwotIGNvcmU6IG1vcmUgZWZmaWNpZW50IHN0YXJ0dXAgcHJvY2VzcwotIGNvcmU6IGZpeCBoYW5kbGluZyBvZiBtdWx0aXBsZSBsb2dpbnMgd2hlbiBvbmUgaXMgaW5hY3RpdmUKLSBjb3JlOiBmaXggaGFuZGxpbmcgb2YgUzM5MC9IZXJjdWxlcyBDVEMgbmV0d29yayBpbnRlcmZhY2VzIChyaCAjNjQxOTg2KQotIGNvcmU6IHN1cHBvcnQgRWFzeXRldGhlciBpbnRlcmZhY2VzIGZvciBBbmRyb2lkIHBob25lcwotIGNvcmU6IGZpeCBoYW5kbGluZyBvZiBXV0FOIGVuYWJsZS9kaXNhYmxlIHN0YXRlcwotIGlmY2ZnLXJoOiBoYXJtb25pemUgaGFuZGxpbmcgaWYgSVBBRERSL1BSRUZJWC9ORVRNQVNLIHdpdGggaW5pdHNjcmlwdHMgKHJoICM2NTg5MDcpCi0gYXBwbGV0OiBmaXggY29ubmVjdGlvbiB0byBXUEEgRW50ZXJwcmlzZSBuZXR3b3JrcyAocmggIzY5NDc2NSkALSBjb3JlOiBmaXggaGFuZGxpbmcgb2YgaW5maW5pdGUgSVB2NiBSRE5TUyB0aW1lb3V0cyAocmggIzY4OTI5MSkALSBVcGRhdGUgdG8gMC44Ljk5OCAoMC45LjAtcmMxKQotIGNvcmU6IGZpeCBuZWFyLWluZmluaXRlIHJlcXVlc3RzIGZvciBwYXNzd29yZHMgKHJoICM2OTI3ODMpCi0gY29yZTogZml4IGhhbmRsaW5nIG9mIHdpcmVkIDgwMi4xeCBjb25uZWN0aW9ucwotIGNvcmU6IGlnbm9yZSBOb2tpYSBQQy1TdWl0ZSBldGhlcm5ldCBkZXZpY2VzIHdlIGNhbid0IHVzZSB5ZXQKLSBhcHBsZXQ6IG1pZ3JhdGUgMC44IE9wZW5WUE4gcGFzc3dvcmRzIHRvIDAuOSBmb3JtYXRzAC0gY29yZTogcmVzdXJyZWN0IGRlZmF1bHQgVlBOIHVzZXJuYW1lCi0gY29yZTogZG9uJ3Qgc3RvbXAgb24gY3J5cHRvIGxpYnJhcnkgdXNlcnMgYnkgZGUtaW5pdGluZyB0aGUgY3J5cHRvIGxpYnJhcnkALSBjb3JlOiBmaXggY3JlYXRpb24gb2YgZGVmYXVsdCB3aXJlZCBjb25uZWN0aW9ucwotIGNvcmU6IGZpeCByZXF1ZXN0aW5nIG5ldyBzZWNyZXRzIHdoZW4gb2xkIG9uZXMgZmFpbCAoZXggY2hhbmdpbmcgV0VQIGtleXMpCi0gZWRpdG9yOiBlbnN1cmUgYWxsIHBhZ2VzIGFyZSBzZW5zaXRpdmUgYWZ0ZXIgcmV0cmlldmluZyBzZWNyZXRzCi0gZWRpdG9yOiBmaXggY3Jhc2ggd2hlbiBzY3JvbGxpbmcgdGhyb3VnaCBjb25uZWN0aW9uIGxpc3RzIChyaCAjNjkzNDQ2KQotIGFwcGxldDogZml4IGNyYXNoIGFmdGVyIHVzaW5nIHRoZSB3aWZpIG9yIHdpcmVkIHNlY3JldHMgZGlhbG9ncyAocmggIzY5MzQ0NikALSBGaXggdHJpZ2dlciB0byBlbmFibGUgdGhlIHN5c3RlbWQgc2VydmljZSBmb3IgdXBncmFkZXMgKHJoICM2Nzg1NTMpAC0gY29yZTogZml4IGNvbm5lY3Rpb24gZGVhY3RpdmF0aW9uIG9uIHRoZSBjb21wYXQgaW50ZXJmYWNlCi0gY29yZTogZ2l2ZSBkZWZhdWx0IHdpcmVkIGNvbm5lY3Rpb25zIGEgbW9yZSBmcmllbmRseSBuYW1lCi0gY29yZTogZml4IGJhc2UgdHlwZSBvZiBuZXdseSBjcmVhdGVkIHdpcmVkIGNvbm5lY3Rpb25zCi0gYXBwbGV0OiBtYW55IHVwZGF0ZWQgdHJhbnNsYXRpb25zAC0gY29yZTogZml4IHBvc3NpYmxlIGxpYm5tLWdsaWIgY3Jhc2ggd2hlbiBhY3RpdmF0aW5nIGNvbm5lY3Rpb25zCi0gYXBwbGV0OiBmaXggdmFyaW91cyBuYW1pbmcgYW5kIGRpYWxvZyB0aXRsZSBpc3N1ZXMALSBubS12ZXJzaW9uLmggc2hvdWxkIGJlIGluIE5ldHdvcmtNYW5hZ2VyLWRldmVsLCBub3QgLWdsaWItZGV2ZWwgKHJoICM2ODU0NDIpAC0gY29yZTogYWRkIGNvbXBhdGliaWxpdHkgbGF5ZXIgZm9yIEtERSBQbGFzbWEgbmV0d29yayBpbmZyYXN0cnVjdHVyZQAtIFVwZGF0ZSB0byAwLjguOTk3ICgwLjktYmV0YTMpCi0gaWZjZmctcmg6IGZpeCByZWFkaW5nIGFuZCB3cml0aW5nIG9mIER5bmFtaWMgV0VQIGNvbm5lY3Rpb25zIHVzaW5nIExFQVAgYXMgdGhlIGVhcCBtZXRob2QKLSB3aWZpOiBmaXggc2lnbmFsIHN0cmVuZ3RoIGZvciBzY2FubmVkIGFjY2VzcyBwb2ludHMgd2l0aCBzb21lIGRyaXZlcnMKLSBhcHBsZXQ6IHRyYW5zbGF0aW9uIHVwZGF0ZXMALSBVcGRhdGUgdG8gMC44Ljk5NiAoMC45LWJldGEyKQAtIGFwcGxldDogZml4IGJ1cyBuYW1lIG1vcmUALSBhcHBsZXQ6IGZpeCBidXMgbmFtZQAtIEZpeCBzeXN0ZW1kIHJlcXVpcmVzAC0gVXBkYXRlIHRvIE5ldHdvcmtNYW5hZ2VyIDAuOS1iZXRhMQotIGNvcmU6IGNvbnNvbGlkYXRlIHVzZXIgYW5kIHN5c3RlbSBzZXR0aW5ncyBzZXJ2aWNlcyBpbnRvIE5NIGl0c2VsZgotIGNvcmU6IGFkZCBXaU1BWCBzdXBwb3J0Ci0gYXBwbGV0OiBzdXBwb3J0IEZhc3QgVXNlciBTd2l0Y2hpbmcALSBSZWJ1aWxkIGFnYWluc3QgbmV3ZXIgZ3RrAC0gUmVidWlsdCBmb3IgaHR0cHM6Ly9mZWRvcmFwcm9qZWN0Lm9yZy93aWtpL0ZlZG9yYV8xNV9NYXNzX1JlYnVpbGQALSBSZWJ1aWxkIGFnYWluc3QgbmV3IGd0awAtIEhhbmRsZSBtb2RlbSBJUCBpbnRlcmZhY2UgY2hhbmdlcyBhZnRlciBkZXZpY2UgaXMgcmVjb2duaXplZAAtIFJlYnVpbGQgYWdhaW5zdCBuZXcgZ3RrMwAtIHVzZSAtLWZvcmNlIGluIGF1dG9yZWNvbmYgdG8gZml4IEZUQkZTAC0gUmVidWlsZCBhZ2FpbnN0IG5ld2VyIGd0awAtIFVwZGF0ZSB0byAwLjguMgAtIFJlYnVpbGQgYWdhaW5zdCBsaWJub3RpZnkgMC43Ci0gbWlzYyBndGsgYnVpbGQgZml4ZXMALSBjb3JlOiBwcmVzZXJ2ZSBXaUZpIEVuYWJsZWQgc3RhdGUgYWNyb3NzIHJlYm9vdCBhbmQgc3VzcGVuZC9yZXN1bWUALSBjb3JlOiBmaXggc3VzcGVuZC9yZXN1bWUgcmVncmVzc2lvbiAocmggIzYzODY0MCkKLSBjb3JlOiBmaXggaXNzdWUgY2F1c2luZyBzb21lIG5tY2xpIHJlcXVlc3RzIHRvIGJlIGlnbm9yZWQALSBjb3JlOiBwcmVzZXJ2ZSBjdXN0b20gbG9jYWwtbWFwcGVkIGhvc3RuYW1lcyBpbiAvZXRjL2hvc3RzIChyaCAjNjI3MjY5KQAtIGNvcmU6IHJlbW92ZSBzdGFsZSAvZXRjL2hvc3RzIG1hcHBpbmdzIChyaCAjNjMwMTQ2KQAtIGNvcmU6IGFkZCBkaXNwYXRjaGVyIGV2ZW50cyBvbiBESENQdjQgYW5kIERIQ1B2NiBsZWFzZSBjaGFuZ2VzCi0gY29yZTogZW5mb3JjZSBhY2Nlc3MgcGVybWlzc2lvbnMgd2hlbiBlbmFibGluZy9kaXNhYmxpbmcgV2lGaSBhbmQgV1dBTiAocmggIzYyNjMzNykKLSBjb3JlOiBsaXN0ZW4gZm9yIFVQb3dlciBzdXNwZW5kL3Jlc3VtZSBzaWduYWxzCi0gYXBwbGV0OiBmaXggZGlzYWJsZWQgRW5hYmxlIE5ldHdvcmtpbmcgYW5kIEVuYWJsZSBXaXJlbGVzcyBtZW51IGl0ZW1zIChyaCAjNjI3MzY1KQotIGFwcGxldDogdXBkYXRlZCB0cmFuc2xhdGlvbnMKLSBhcHBsZXQ6IG9ic2N1cmUgTW9iaWxlIEJyb2FkYmFuZCBQSU4gaW4gc2Vjb25kYXJ5IHVubG9jayBkaWFsb2cALSBjb3JlOiBmaXggc29tZSBzeXN0ZW1kIGludGVyYWN0aW9uIGlzc3VlcwAtIGNvcmU6IHJlYnVpbGQgdG8gZml4IHBvbGtpdCAwLjk3IGJ1aWxkIGlzc3VlCi0gYXBwbGV0OiB1cGRhdGVkIHRyYW5zbGF0aW9ucwAtIGNvcmU6IHJlYnVpbGQgdG8gZml4IGRidXMtZ2xpYiBzZWN1cml0eSBpc3N1ZSAoQ1ZFLTIwMTAtMTE3MikgKHJoICM1ODUzOTQpAC0gY29yZTogcXVpZXQgYW5ub3lpbmcgd2FybmluZ3MgKHJoICM2MTI5OTEpCi0gY29yZTogZml4IHJldHJpZXZhbCBvZiB2YXJpb3VzIElQIG9wdGlvbnMgaW4gbGlibm0tZ2xpYiAocmggIzYxMTE0MSkKLSBjb3JlOiBzaGlwIE5ldHdvcmtNYW5hZ2VyLmNvbmYgaW5zdGVhZCBvZiBkZXByZWNhdGVkIG5tLXN5c3RlbS1zZXR0aW5ncy5jb25mIChyaCAjNjA2MTYwKQotIGNvcmU6IGFkZCBzaG9ydCBob3N0bmFtZSB0byAvZXRjL2hvc3RzIHRvbyAocmggIzYyMTkxMCkKLSBjb3JlOiByZWNoZWNrIGF1dG9hY3RpdmF0aW9uIHdoZW4gbmV3IHN5c3RlbSBjb25uZWN0aW9ucyBhcHBlYXIKLSBjb3JlOiBlbmFibGUgREhDUHY2LW9ubHkgY29uZmlndXJhdGlvbnMgKHJoICM2MTI0NDUpCi0gY29yZTogZG9uJ3QgZmFpbCBjb25uZWN0aW9uIGltbWVkaWF0ZWx5IGlmIERIQ1AgbGVhc2UgZXhwaXJlcyAocmggIzYxNjA4NCkgKHJoICM1OTA4NzQpCi0gY29yZTogZml4IGVkaXRpbmcgb2YgUFBQb0Ugc3lzdGVtIGNvbm5lY3Rpb25zCi0gY29yZTogd29yayBhcm91bmQgdHdpdGNoeSBmcmVxdWVuY3kgcmVwb3J0aW5nIG9mIHZhcmlvdXMgd2lmaSBkcml2ZXJzCi0gY29yZTogZG9uJ3QgdGVhciBkb3duIHVzZXIgY29ubmVjdGlvbnMgb24gY29uc29sZSBjaGFuZ2VzIChyaCAjNjE0NTU2KQotIGNsaTogd2FpdCBhIGJpdCBmb3IgTk0ncyBwZXJtaXNzaW9ucyBjaGVjayB0byBjb21wbGV0ZSAocmggIzYxNDg2NikKLSBpZmNmZy1yaDogaWdub3JlIEJSSURHRSBhbmQgVkxBTiBjb25maWdzIGFuZCB0cmVhdCBhcyB1bm1hbmFnZWQgKHJoICM2MTk4NjMpCi0gbWFuOiBhZGQgbWFucGFnZSBmb3Igbm0tb25saW5lCi0gYXBwbGV0OiBmaXggY3Jhc2ggc2F2aW5nIGlnbm9yZS1taXNzaW5nLUNBLWNlcnQgcHJlZmVyZW5jZSAocmggIzYxOTc3NSkKLSBhcHBsZXQ6IGhpZGUgUElOL1BVSyBieSBkZWZhdWx0IGluIHRoZSBtb2JpbGUgUElOL1BVSyBkaWFsb2cgKHJoICM2MTUwODUpCi0gYXBwbGV0OiBlbnN1cmUgRW50ZXIgY2xvc2VzIHRoZSBQSU4vUFVLIGRpYWxvZyAocmggIzYxMTgzMSkKLSBhcHBsZXQ6IGZpeCBhbm90aGVyIGNyYXNoIGluIGlnbm9yZS1DQS1jZXJ0aWZpY2F0ZSBoYW5kbGluZyAocmggIzU1NzQ5NSkKLSBlZGl0b3I6IGZpeCBoYW5kbGluZyBvZiBXaXJlZC9zMzkwIGNvbm5lY3Rpb25zIChyaCAjNjE4NjIwKQotIGVkaXRvcjogZml4IGNyYXNoIHdoZW4gY2FuY2VsaW5nIGVkaXRpbmcgaW4gSVAgYWRkcmVzcyBwYWdlcyAocmggIzYxMDg5MSkKLSBlZGl0b3I6IGZpeCBoYW5kbGluZyBvZiBzMzkwLXNwZWNpZmljIG9wdGlvbnMKLSBlZGl0b3I6IHJlYWxseSBmaXggY3Jhc2ggd2hlbiBjaGFuZ2luZyBzeXN0ZW0gY29ubmVjdGlvbnMgKHJoICM2MDM1NjYpAC0gY29yZTogcmVhZCBubS1zeXN0ZW0tc2V0dGluZ3MuY29uZiBiZWZvcmUgTmV0d29ya01hbmFnZXIuY29uZiAocmggIzYwNjE2MCkKLSBjb3JlOiBmaXggZWRpdGluZyBzeXN0ZW0gRFNMIGNvbm5lY3Rpb25zIHdoZW4gdXNpbmcga2V5ZmlsZSBwbHVnaW4KLSBjb3JlOiB3b3JrIGFyb3VuZCBpbmNvbnNpc3RlbnQgcHJvcHJpZXRhcnkgZHJpdmVyIGFzc29jaWF0ZWQgQVAgcmVwb3J0aW5nCi0gY29yZTogZW5zdXJlIGVtcHR5IFZQTiBzZWNyZXRzIGFyZSBub3QgdXNlZCAocmggIzU4Nzc4NCkKLSBjb3JlOiBkb24ndCByZXF1ZXN0IFdpRmkgc2NhbnMgd2hlbiBjb25uZWN0aW9uIGlzIGxvY2tlZCB0byBhIHNwZWNpZmljIEJTU0lECi0gY2xpOiBzaG93IElQdjYgc2V0dGluZ3MgYW5kIGNvbmZpZ3VyYXRpb24KLSBhcHBsZXQ6IHVwZGF0ZWQgdHJhbnNsYXRpb25zCi0gZWRpdG9yOiBmaXggYSBQb2xpY3lLaXQtcmVsYXRlZCBjcmFzaCBlZGl0aW5nIGNvbm5lY3Rpb25zIChyaCAjNjAzNTY2KQotIGFwcGxldDogZml4IHNhdmluZyB0aGUgaWdub3JlLW1pc3NpbmctQ0EtY2VydCBwcmVmZXJlbmNlIChyaCAjNjEwMDg0KQotIGVkaXRvcjogZml4IGxpc3RpbmcgY29ubmVjdGlvbnMgb24gUFBDNjQgKHJoICM2MDg2NjMpCi0gZWRpdG9yOiBlbnN1cmUgZWRpdG9yIHdpbmRvd3MgYXJlIGRlc3Ryb3llZCB3aGVuIGNsb3NlZCAocmggIzU3MjQ2NikALSBSZWJ1aWxkIGFnYWluc3QgbmV3IGdub21lLWJsdWV0b290aAAtIFVwZGF0ZSB0byAwLjguMSByZWxlYXNlIGNhbmRpZGF0ZQotIGNvcmU6IGZpeCBXV0FOIGhhcmR3YXJlIGVuYWJsZSBzdGF0ZSB0cmFja2luZyAocmggIzU5MTYyMikKLSBjb3JlOiBmaXggUmVkIEhhdCBpbml0c2NyaXB0IHJldHVybiB2YWx1ZSBvbiBkb3VibGUtc3RhcnQgKHJoICM1ODQzMjEpCi0gY29yZTogYWRkIG11bHRpY2FzdCByb3V0ZSBlbnRyeSBmb3IgSVB2NCBsaW5rLWxvY2FsIGNvbm5lY3Rpb25zCi0gY29yZTogZml4IGNvbm5lY3Rpb24gc2hhcmluZyBpbiBjYXNlcyB3aGVyZSBhIGRuc21hc3EgY29uZmlnIGZpbGUgZXhpc3RzCi0gY29yZTogZml4IGhhbmRsaW5nIG9mIEFkLUhvYyB3aWZpIGNvbm5lY3Rpb25zIHRvIGluZGljYXRlIGNvcnJlY3QgbmV0d29yawotIGNvcmU6IGVuc3VyZSBWUE4gaW50ZXJmYWNlIG5hbWUgaXMgcGFzc2VkIHRvIGRpc3BhdGNoZXIgd2hlbiBWUE4gZ29lcyBkb3duCi0gaWZjZmctcmg6IGZpeCBoYW5kbGluZyBvZiBBU0NJSSBXRVAga2V5cwotIGlmY2ZnLXJoOiBmaXggZG91YmxlLXF1b3Rpbmcgb2Ygc29tZSBTU0lEcyAocmggIzYwNjUxOCkKLSBhcHBsZXQ6IGVuc3VyZSBkZWxldGVkIGNvbm5lY3Rpb25zIGFyZSBhY3R1YWxseSBmb3Jnb3R0ZW4gKHJoICM2MTg5NzMpCi0gYXBwbGV0OiBkb24ndCBjcmFzaCBpZiB0aGUgQVAncyBCU1NJRCBpc24ndCBhdmFpbGFiZSAocmggIzYwMzIzNikKLSBlZGl0b3I6IGRvbid0IGNyYXNoIG9uIFBvbGljeUtpdCBldmVudHMgYWZ0ZXIgd2luZG93cyBhcmUgY2xvc2VkIChyaCAjNTcyNDY2KQAtIGNvcmU6IGZpeCBubS1vbmxpbmUgY3Jhc2ggKHJoICM1OTM2NzcpCi0gY29yZTogZml4IGZhaWxlZCBzdXNwZW5kIGRpc2FibGVzIG5ldHdvcmsgKHJoICM1ODkxMDgpCi0gY29yZTogcHJpbnQgb3V0IG1pc3NpbmcgZmlybXdhcmUgZXJyb3JzIChyaCAjNTk0NTc4KQotIGFwcGxldDogZml4IGRldmljZSBkZXNjcmlwdGlvbnMgZm9yIHNvbWUgbW9iaWxlIGJyb2FkYmFuZCBkZXZpY2VzCi0ga2V5ZmlsZTogYmx1ZXRvb3RoIGZpeGVzCi0gYXBwbGV0OiB1cGRhdGVkIHRyYW5zbGF0aW9ucyAocmggIzU4OTIzMCkALSBjb3JlOiB1c2UgR0lPIGluIGxvY2FsIG1vZGUgb25seSAocmggIzU4ODc0NSkKLSBjb3JlOiB1cGRhdGVkIHRyYW5zbGF0aW9ucyAocmggIzU4OTIzMCkKLSBjb3JlOiBiZSBtb3JlIGxlbmllbnQgaW4gSVB2NiBSRE5TUyBzZXJ2ZXIgZXhwaXJ5IChyaCAjNTkwMjAyKQotIGNvcmU6IGZpeCBoZWFkZXJzIHRvIGJlIEMrKyBjb21wYXRpYmxlIChyaCAjNTkyNzgzKQotIGFwcGxldDogdXBkYXRlZCB0cmFuc2xhdGlvbnMgKHJoICM1ODkyMzApCi0gYXBwbGV0OiBsb2NrIGNvbm5lY3Rpb25zIHdpdGggd2VsbC1rbm93biBTU0lEcyB0byB0aGVpciBzcGVjaWZpYyBBUAAtIGNvcmU6IGZpeCBoYW5kbGluZyBvZiBJUHY2IFJBIGZsYWdzIHdoZW4gcm91dGVyIGdvZXMgYXdheSAocmggIzU4ODU2MCkKLSBibHVldG9vdGg6IGZpeCBjcmFzaCBjb25maWd1cmluZyBEVU4gY29ubmVjdGlvbnMgZnJvbSB0aGUgd2l6YXJkIChyaCAjNTkwNjY2KQAtIGNvcmU6IHJlc3RvcmUgaW5pdGlhbCBhY2NlcHRfcmEgdmFsdWUgZm9yIElQdjYgaWdub3JlZCBjb25uZWN0aW9ucyAocmggIzU4ODYxOSkKLSBibHVldG9vdGg6IGZpeCBiYWQgdGltZW91dCBvbiBQQU4gY29ubmVjdGlvbnMgKHJoICM1ODY5NjEpCi0gYXBwbGV0OiB1cGRhdGVkIHRyYW5zbGF0aW9ucwAtIGNvcmU6IHRyZWF0IG1pc3NpbmcgSVB2NiBjb25maWd1cmF0aW9uIGFzIGlnbm9yZWQgKHJoICM1ODg4MTQpCi0gY29yZTogZG9uJ3QgZmx1c2ggSVB2NiBsaW5rLWxvY2FsIHJvdXRlcyAocmggIzU4NzgzNikKLSBjbGk6IHVwZGF0ZSBvdXRwdXQgZm9ybWF0dGluZwAtIGNvcmU6IGFsbG93IElQIGNvbmZpZ3VyYXRpb24gYXMgbG9uZyBhcyBvbmUgbWV0aG9kIGNvbXBsZXRlcyAocmggIzU2Nzk3OCkKLSBjb3JlOiBkb24ndCBwcmVtYXR1cmVseSByZW1vdmUgSVB2NiBSRE5TUyBuYW1lc2VydmVycyAocmggIzU4ODE5MikKLSBjb3JlOiBlbnN1cmUgcm91dGVyIGFkdmVydGlzZW1lbnRzIGFyZSBvbmx5IHVzZWQgd2hlbiBuZWVkZWQgKHJoICM1ODg2MTMpCi0gZWRpdG9yOiBhZGQgSVB2NiBnYXRld2F5IGVkaXRpbmcgY2FwYWJpbGl0eQAtIGNvcmU6IElQdjYgYXV0b2NvbmYsIERIQ1AsIGxpbmstbG9jYWwsIGFuZCBtYW51YWwgbW9kZSBmaXhlcwotIGVkaXRvcjogZml4IHNhdmluZyBJUHY2IGFkZHJlc3MgaW4gdXNlciBjb25uZWN0aW9ucwAtIGNvcmU6IGZpeCBjcmFzaCB3aGVuIElQdjYgaXMgZW5hYmxlZCBhbmQgaW50ZXJmYWNlIGlzIGRlYWN0aXZhdGVkAC0gY29yZTogZml4IGlzc3VlcyB3aXRoIElQdjYgcm91dGVyIGFkdmVydGlzZW1lbnQgbWlzaGFuZGxpbmcgKHJoICM1MzA2NzApCi0gY29yZTogbWFueSBmaXhlcyBmb3IgSVB2NiBSQSBhbmQgREhDUCBoYW5kbGluZyAocmggIzUzODQ5OSkKLSBjb3JlOiBpZ25vcmUgV1dBTiBldGhlcm5ldCBkZXZpY2VzIHVudGlsIHVzYWJsZSAocmggIzU4NTIxNCkKLSBpZmNmZy1yaDogZml4IGhhbmRsaW5nIG9mIFdFUCBwYXNzcGhyYXNlcyAocmggIzU4MTcxOCkKLSBhcHBsZXQ6IGZpeCBjcmFzaGVzIChyaCAjNTgyOTM4KSAocmggIzU4MjQyOCkKLSBhcHBsZXQ6IGZpeCBjcmFzaCB3aXRoIG11bHRpcGxlIGNvbmN1cnJlbnQgYXV0aG9yaXphdGlvbiByZXF1ZXN0cyAocmggIzU4NTQwNSkKLSBlZGl0b3I6IGFsbG93IGRpc2FibGluZyBJUHY0IG9uIGEgcGVyLWNvbm5lY3Rpb24gYmFzaXMKLSBlZGl0b3I6IGFkZCBzdXBwb3J0IGZvciBJUHY2IERIQ1Atb25seSBjb25maWd1cmF0aW9ucwAtIGNvcmU6IGZpeCBjcmFzaCBkdXJpbmcgaW5zdGFsbCAocmggIzU4MTc5NCkKLSB3aWZpOiBmaXggY3Jhc2ggd2hlbiBzdXBwbGljYW50IHNlZ2ZhdWx0cyBhZnRlciByZXN1bWUgKHJoICM1Mzg3MTcpCi0gaWZjZmctcmg6IGZpeCBNVFUgaGFuZGxpbmcgZm9yIHdpcmVkIGNvbm5lY3Rpb25zIChyaCAjNTY5MzE5KQotIGFwcGxldDogZml4IGRpc3BsYXkgb2YgZGlzYWJsZWQgbW9iaWxlIGJyb2FkYmFuZCBkZXZpY2VzAC0gY29yZTogZml4IGF1dG9tYXRpYyBXaUZpIGNvbm5lY3Rpb25zIG9uIHJlc3VtZSAocmggIzU3ODE0MSkALSBjb3JlOiBtb3JlIGZsZXhpYmxlIGxvZ2dpbmcKLSBjb3JlOiBmaXggY3Jhc2ggd2l0aCBPTFBDIG1lc2ggZGV2aWNlcyBhZnRlciBzdXNwZW5kCi0gYXBwbGV0OiB1cGRhdGVkIHRyYW5zbGF0aW9ucwotIGFwcGxldDogc2hvdyBtb2JpbGUgYnJvYWRiYW5kIHNpZ25hbCBzdHJlbmd0aCBhbmQgdGVjaG5vbG9neSBpbiB0aGUgaWNvbgotIGFwcGxldDogZml4IGNvbnRpbnVvdXMgcGFzc3dvcmQgcmVxdWVzdHMgZm9yIDgwMi4xeCBjb25uZWN0aW9ucyAocmggIzU3NjkyNSkKLSBhcHBsZXQ6IG1hbnkgdXBkYXRlZCB0cmFuc2xhdGlvbnMALSBjb3JlOiBmaXggbW9kZW0gZW5hYmxlL2Rpc2FibGUKLSBjb3JlOiBmaXggbW9kZW0gZGVmYXVsdCByb3V0ZSBoYW5kbGluZwAtIGNvcmU6IGRvbid0IGV4aXQgZWFybHkgb24gbm9uLWZhdGFsIHN0YXRlIGZpbGUgZXJyb3JzCi0gY29yZTogZml4IEJsdWV0b290aCBjb25uZWN0aW9uIGlzc3VlcyAocmggIzU3MjM0MCkKLSBhcHBsZXQ6IGZpeCBzb21lIHRyYW5zbGF0aW9ucyAocmggIzU3NjA1NikKLSBhcHBsZXQ6IGJldHRlciBmZWVkYmFjayB3aGVuIHdyb25nIFBJTi9QVUsgaXMgZW50ZXJlZAotIGFwcGxldDogbWFueSB1cGRhdGVkIHRyYW5zbGF0aW9ucwotIGFwcGxldDogUElOMiB1bmxvY2sgbm90IHJlcXVpcmVkIGZvciBub3JtYWwgbW9kZW0gZnVuY3Rpb25hbGl0eQotIGFwcGxldDogZml4IHdpcmVsZXNzIHNlY3JldHMgZGlhbG9nIGRpc3BsYXkALSBtYW46IG1hbnkgbWFucGFnZSB1cGRhdGVzCi0gY29yZTogZGV0ZXJtaW5lIGNsYXNzZnVsIHByZWZpeCBpZiBub24gaXMgZ2l2ZW4gdmlhIERIQ1AKLSBjb3JlOiBlbnN1cmUgL2V0Yy9ob3N0cyBpcyBhbHdheXMgdXAtdG8tZGF0ZSBhbmQgY29ycmVjdCAocmggIzU2OTkxNCkKLSBjb3JlOiBzdXBwb3J0IEdTTSBuZXR3b3JrIGFuZCByb2FtaW5nIHByZWZlcmVuY2VzCi0gYXBwbGV0OiBzdGFydHVwIHNwZWVkIGVuaGFuY2VtZW50cwotIGFwcGxldDogYmV0dGVyIHN1cHBvcnQgZm9yIE9UUC90b2tlbi1iYXNlZCBXaUZpIGNvbm5lY3Rpb25zIChyaCAjNTI2MzgzKQotIGFwcGxldDogc2hvdyBHU00gYW5kIENETUEgcmVnaXN0cmF0aW9uIHN0YXR1cyBhbmQgc2lnbmFsIHN0cmVuZ3RoIHdoZW4gYXZhaWxhYmxlCi0gYXBwbGV0OiBmaXggem9tYmllIEdTTSBhbmQgQ0RNQSBkZXZpY2VzIGluIHRoZSBtZW51Ci0gYXBwbGV0OiByZW1vdmUgNC1jaGFyYWN0ZXIgR1NNIFBJTi9QVUsgY29kZSBsaW1pdAotIGFwcGxldDogZml4IGluc2Vuc2l0aXZlIFdpRmkgQ3JlYXRlLi4uIGJ1dHRvbiAocmggIzU0MTE2MykKLSBhcHBsZXQ6IGFsbG93IHVubG9ja2luZyBvZiBtb2JpbGUgZGV2aWNlcyBpbW1lZGlhdGVseSB3aGVuIHBsdWdnZWQgaW4ALSBjb3JlOiB1cGRhdGUgdG8gZmluYWwgMC44IHJlbGVhc2UKLSBjb3JlOiBmaXggQmx1ZXRvb3RoIERVTiBjb25uZWN0aW9ucyB3aGVuIHNlY3JldHMgYXJlIG5lZWRlZAotIGlmY2ZnLXJoOiBhZGQgaGVscGVyIGZvciBpbml0c2NyaXB0cyB0byBkZXRlcm1pbmUgaWZjZmcgY29ubmVjdGlvbiBVVUlEcwotIGFwcGxldDogZml4IEJsdWV0b290aCBjb25uZWN0aW9uIHNlY3JldHMgcmVxdWVzdHMKLSBhcHBsZXQ6IGZpeCByYXJlIGNvbmZsaWN0IHdpdGggb3RoZXIgZ25vbWUtYmx1ZXRvb3RoIHBsdWdpbnMALSBjb3JlOiBmaXggbW9iaWxlIGJyb2FkYmFuZCBQSU4gaGFuZGxpbmcgKHJoICM1NDMwODgpIChyaCAjNTYwNzQyKQotIGNvcmU6IGJldHRlciBoYW5kbGluZyBvZiAvZXRjL2hvc3RzIGlmIGhvc3RuYW1lIHdhcyBhbHJlYWR5IGFkZGVkIGJ5IHRoZSB1c2VyCi0gYXBwbGV0OiBjcmFzaCBsZXNzIG9uIEQtQnVzIHByb3BlcnR5IGVycm9ycyAocmggIzU1NzAwNykKLSBhcHBsZXQ6IGZpeCBjcmFzaCBlbnRlcmluZyB3aXJlZCA4MDIuMXggY29ubmVjdGlvbiBkZXRhaWxzIChyaCAjNTU2NzYzKQAtIGNvcmU6IHZhbGlkYXRlIHRoZSBhdXRvc3RhcnQgLmRlc2t0b3AgZmlsZQotIGJ1aWxkOiBmaXggbm1jbGkgZm9yIHRoZSBzdHJpY3RlciBsZCAoZml4ZXMgRlRCRlMpCi0gYnVpbGQ6IGZpeCBubS1jb25uZWN0aW9uLWVkaXRvciBmb3IgdGhlIHN0cmljdGVyIGxkIChmaXhlcyBGVEJGUykKLSBhcHBsZXQ6IGRvbid0IGF1dG9zdGFydCBpbiBLREUgb24gRjEzKyAoIzU0MTM1MykALSBjb3JlOiBhZGQgQmx1ZXRvb3RoIERpYWwtVXAgTmV0d29ya2luZyAoRFVOKSBzdXBwb3J0IChyaCAjMTM2NjYzKQotIGNvcmU6IHN0YXJ0IERIQ1B2NiBvbiByZWNlaXB0IG9mIFJBICdvdGhlcmNvbmYnLydtYW5hZ2VkJyBiaXRzCi0gbm1jbGk6IGFsbG93IGVuYWJsZS9kaXNhYmxlIG9mIFdpRmkgYW5kIFdXQU4ALSBpZmNmZy1yaDogcmVhZCBhbmQgd3JpdGUgREhDUHY2IGVuYWJsZWQgY29ubmVjdGlvbnMgKHJoICM0Mjk3MTApCi0gbm1jbGk6IHVwZGF0ZQAtIGNvcmU6IGNsZWFuIE5TUyB1cCBsYXRlciB0byBwcmVzZXJ2ZSBlcnJvcnMgZnJvbSBjcnlwdG9faW5pdCgpAC0gY29yZTogc3VwcG9ydCBmb3IgbWFuYWdlZC1tb2RlIERIQ1B2NiAocmggIzQyOTcxMCkKLSBpZmNmZy1yaDogZ3JhY2VmdWxseSBoYW5kbGUgbWlzc2luZyBQUkVGSVgvTkVUTUFTSwotIGNsaTogaW5pdGlhbCBwcmV2aWV3IG9mIGNvbW1hbmQtbGluZSBjbGllbnQKLSBhcHBsZXQ6IGFkZCAtLWhlbHAgdG8gZXhwbGFpbiB3aGF0IHRoZSBhcHBsZXQgaXMgKHJoICM0OTQ2NDEpAC0gYnVpbGQ6IGZpeCBmb3IgbmV3IHBwcGQgKHJoICM1NDg1MjApCi0gY29yZTogYWRkIFdXQU4gZW5hYmxlL2Rpc2FibGUgZnVuY3Rpb25hbGl0eQotIGlmY2ZnLXJoOiBJUHY2IGFkZHJlc3NpbmcgYW5kIHJvdXRlcyBzdXBwb3J0IChyaCAjNTIzMjg4KQotIGlmY2ZnLXJoOiBlbnN1cmUgY29ubmVjdGlvbiBpcyB1cGRhdGVkIHdoZW4gcm91dGUva2V5IGZpbGVzIGNoYW5nZQotIGFwcGxldDogZml4IGNyYXNoIHdoZW4gYWN0aXZlIEFQIGlzbid0IGZvdW5kIChyaCAjNTQ2OTAxKQotIGVkaXRvcjogZml4IGNyYXNoIHdoZW4gZWRpdGluZyBjb25uZWN0aW9ucyAocmggIzU0OTU3OSkALSBjb3JlOiBmaXggcmVjb2duaXRpb24gb2Ygc3RhbmRhbG9uZSA4MDIuMXggcHJpdmF0ZSBrZXlzCi0gYXBwbGV0OiBjbGVhbiBub3RpZmljYXRpb24gdGV4dCB0byBlbnN1cmUgaXQgcGFzc2VzIGxpYm5vdGlmeSB2YWxpZGF0aW9uAC0gY29yZTogcmVtb3ZlIGhhbGRhZW1vbiBmcm9tIGluaXRzY3JpcHQgZGVwZW5kZW5jaWVzIChyaCAjNTQyMDc4KQotIGNvcmU6IGhhbmRsZSBQRU0gY2VydGlmaWNhdGVzIHdpdGhvdXQgYW4gZW5kaW5nIG5ld2xpbmUgKHJoICM1MDczMTUpCi0gY29yZTogZml4IHJma2lsbCByZXBvcnRpbmcgZm9yIGlwdzJ4MDAgZGV2aWNlcwotIGNvcmU6IGluY3JlYXNlIFBQUG9FIHRpbWVvdXQgdG8gMzAgc2Vjb25kcwotIGNvcmU6IGZpeCByZS1hY3RpdmF0aW5nIHN5c3RlbSBjb25uZWN0aW9ucyB3aXRoIHNlY3JldHMKLSBjb3JlOiBmaXggY3Jhc2ggd2hlbiBkZWxldGluZyBhdXRvbWF0aWNhbGx5IGNyZWF0ZWQgd2lyZWQgY29ubmVjdGlvbnMKLSBjb3JlOiBlbnN1cmUgdGhhdCBhIFZQTidzIEROUyBzZXJ2ZXJzIGFyZSB1c2VkIHdoZW4gc2hhcmluZyB0aGUgVlBOIGNvbm5lY3Rpb24KLSBpZmNmZy1yaDogc3VwcG9ydCByb3V0ZXMgZmlsZXMgKHJoICM1MDczMDcpCi0gaWZjZmctcmg6IHdhcm4gd2hlbiBkZXZpY2Ugd2lsbCBiZSBtYW5hZ2VkIGR1ZSB0byBtaXNzaW5nIEhXQUREUiAocmggIzU0NTAwMykKLSBpZmNmZy1yaDogaW50ZXJwcmV0IERFRlJPVVRFIGFzIG5ldmVyLWRlZmF1bHQgKHJoICM1MjgyODEpCi0gaWZjZmctcmg6IGhhbmRsZSBNT0RFPUF1dG8gY29ycmVjdGx5Ci0gcnBtOiBmaXggcnBtbGludCBlcnJvcnMKLSBhcHBsZXQ6IGRvbid0IGNyYXNoIG9uIHZhcmlvdXMgRC1CdXMgYW5kIG90aGVyIGVycm9ycyAocmggIzU0NTAxMSkgKHJoICM1NDI2MTcpCi0gZWRpdG9yOiBmaXggdmFyaW91cyBQb2xpY3lLaXQtcmVsYXRlZCBjcmFzaGVzIChyaCAjNDYyOTQ0KQotIGFwcGxldCtlZGl0b3I6IG5vdGlmeSB1c2VyIHRoYXQgcHJpdmF0ZSBrZXlzIG11c3QgYmUgcHJvdGVjdGVkAC0gbm06IGJldHRlciBwaWRmaWxlIGhhbmRpbmcgKHJoICM1MTczNjIpCi0gbm06IHNhdmUgV2lGaSBhbmQgTmV0d29ya2luZyBlbmFibGVkL2Rpc2FibGVkIHN0YXRlcyBhY3Jvc3MgcmVib290Ci0gbm06IGZpeCBjcmFzaCB3aXRoIG1pc3NpbmcgVlBOIHNlY3JldHMgKHJoICM1MzIwODQpCi0gYXBwbGV0OiBmaXggc3lzdGVtIGNvbm5lY3Rpb24gdXNhZ2UgZnJvbSB0aGUgIkNvbm5lY3QgdG8gaGlkZGVuLi4uIiBkaWFsb2cKLSBhcHBsZXQ6IHNob3cgQmx1ZXRvb3RoIGNvbm5lY3Rpb25zIHdoZW4gbm8gb3RoZXIgZGV2aWNlcyBhcmUgYXZhaWxhYmxlIChyaCAjNTMyMDQ5KQotIGFwcGxldDogZG9uJ3QgZGllIHdoZW4gYXV0b2NvbmZpZ3VyZWQgY29ubmVjdGlvbnMgY2FuJ3QgYmUgbWFkZSAocmggIzUzMjY4MCkKLSBhcHBsZXQ6IGFsbG93IHN5c3RlbSBhZG1pbmlzdHJhdG9ycyB0byBkaXNhYmxlIHRoZSAiQ3JlYXRlIG5ldyB3aXJlbGVzcyBuZXR3b3JrLi4uIiBtZW51IGl0ZW0KLSBhcHBsZXQ6IGZpeCBtaXNzaW5nIHVzZXJuYW1lIGNvbm5lY3RpbmcgdG8gVlBOcyB0aGUgc2Vjb25kIHRpbWUKLSBhcHBsZXQ6IHJlYWxseSBmaXggYW5pbWF0aW9uIHN0dXR0ZXJpbmcKLSBlZGl0b3I6IGZpeCBJUCBjb25maWcgd2lkZ2V0IHRvb2x0aXBzCi0gZWRpdG9yOiBhbGxvdyB1bmxpc3RlZCBjb3VudHJpZXMgaW4gdGhlIG1vYmlsZSBicm9hZGJhbmQgd2l6YXJkIChyaCAjNTMwOTgxKQotIGlmY2ZnLXJoOiBpZ25vcmUgLnJwbW5ldyBmaWxlcyAocmggIzUwOTYyMSkALSBubTogZml4IFBQUG9FIGNvbm5lY3Rpb24gYXV0aGVudGljYXRpb24gKHJoICM1MzI4NjIpAC0gaW5zdGFsbDogYmV0dGVyIGZpeCBmb3IgKHJoICM1MjY1MTkpCi0gaW5zdGFsbDogZG9uJ3QgYnVpbGQgQmx1ZXRvb3RoIGJpdHMgb24gczM5MCAocmggIzUyOTg1NCkKLSBubTogd2lyZWQgODAyLjF4IGNvbm5lY3Rpb24gYWN0aXZhdGlvbiBmaXhlcwotIG5tOiBmaXggY3Jhc2ggYWZ0ZXIgbW9kaWZ5aW5nIGRlZmF1bHQgd2lyZWQgY29ubmVjdGlvbnMgbGlrZSAiQXV0byBldGgwIgotIG5tOiBlbnN1cmUgVlBOIHNlY3JldHMgYXJlIHJlcXVlc3RlZCBhZ2FpbiBhZnRlciBjb25uZWN0aW9uIGZhaWx1cmUKLSBubTogcmVzZXQgJ2FjY2VwdF9yYScgdG8gcHJldmlvdXMgdmFsdWUgYWZ0ZXIgZGVhY3RpdmF0aW5nIElQdjYgY29ubmVjdGlvbnMKLSBubTogZW5zdXJlIHJhbmRvbSBuZXRsaW5rIGV2ZW50cyBkb24ndCBpbnRlcmZlcmUgd2l0aCBJUHY2IGNvbm5lY3Rpb24gYWN0aXZhdGlvbgotIGlmY2ZnLXJoOiBmaXggd3JpdGluZyBvdXQgTEVBUCBjb25uZWN0aW9ucwotIGlmY2ZnLXJoOiByZWNvZ25pemUgJ3N0YXRpYycgYXMgYSB2YWxpZCBCT09UUFJPVE8gKHJoICM1MjgwNjgpCi0gYXBwbGV0OiBmaXggImNvdWxkIG5vdCBmaW5kIHJlcXVpcmVkIHJlc291cmNlcyIgZXJyb3IgKHJoICM1Mjk3NjYpAC0gaW5zdGFsbDogZml4IC1nbm9tZSBwYWNrYWdlIHByZSBzY3JpcHQgZmFpbHVyZXMgKHJoICM1MjY1MTkpCi0gbm06IGZpeCBmYWlsdXJlcyB2YWxpZGF0aW5nIHByaXZhdGUga2V5cyB3aGVuIHVzaW5nIHRoZSBOU1MgY3J5cHRvIGJhY2tlbmQKLSBhcHBsZXQ6IGZpeCBjcmFzaGVzIHdoZW4gY2xpY2tpbmcgb24gbWVudSBidXQgbm90IGFzc29jaWF0ZWQgKHJoICM1MjY1MzUpCi0gZWRpdG9yOiBmaXggY3Jhc2ggZWRpdGluZyB3aXJlZCA4MDIuMXggc2V0dGluZ3MKLSBlZGl0b3I6IGZpeCBzZWNyZXRzIHJldHJpZXZhbCB3aGVuIGVkaXRpbmcgY29ubmVjdGlvbnMALSBubTogZml4IGNvbm5lY3Rpb24gdGFrZW92ZXIgd2hlbiBjYXJyaWVyIGlzIG5vdCBvbgotIG5tOiBoYW5kbGUgY2VydGlmaWNhdGUgcGF0aHMgKENBIGNoYWluIFBFTSBmaWxlcyBhcmUgbm93IGZ1bGx5IHVzYWJsZSkKLSBubTogZGVmZXIgYWN0aW9uIGZvciA0IHNlY29uZHMgd2hlbiB3aXJlZCBjYXJyaWVyIGRyb3BzCi0gaWZjZmctcmg6IGZpeCB3cml0aW5nIFdQQSBwYXNzcGhyYXNlcyB3aXRoIG9kZCBjaGFyYWN0ZXJzCi0gZWRpdG9yOiBmaXggZWRpdGluZyBvZiBJUHY0IHNldHRpbmdzIHdpdGggbmV3IGNvbm5lY3Rpb25zIChyaCAjNTI1ODE5KQotIGVkaXRvcjogZml4IHJhbmRvbSBjcmFzaGVzIHdoZW4gZWRpdGluZyBkdWUgdG8gYmFkIHdpZGdldCByZWZjb3VudGluZwotIGFwcGxldDogZGVidXQgcmV3b3JrZWQgbWVudSBsYXlvdXQgKG5vdCBmaW5hbCB5ZXQuLi4pAC0gSW5zdGFsbCBHQ29uZiBzY2hlbWFzAC0gbm06IGFsbG93IGRpc2Nvbm5lY3Rpb24gb2YgYWxsIGRldmljZSB0eXBlcwotIG5tOiBlbnN1cmUgdGhhdCB3aXJlZCBjb25uZWN0aW9ucyBhcmUgdG9ybiBkb3duIHdoZW4gdGhlaXIgaGFyZHdhcmUgZ29lcyBhd2F5Ci0gbm06IGZpeCBjcmFzaCB3aGVuIGNhbmNlbGluZyBhIFZQTidzIHJlcXVlc3QgZm9yIHNlY3JldHMKLSBlZGl0b3I6IGZpeCBpc3N1ZXMgY2hhbmdpbmcgY29ubmVjdGlvbnMgYmV0d2VlbiBzeXN0ZW0gYW5kIHVzZXIgc2NvcGVzCi0gZWRpdG9yOiBlbnN1cmUgY2hhbmdlcyBhcmUgdGhyb3duIGF3YXkgd2hlbiBlZGl0aW5nIGlzIGNhbmNlbGVkCi0gYXBwbGV0OiBlbnN1cmUgY29ubmVjdGlvbiBjaGFuZ2VzIGFyZSBub3RpY2VkIGJ5IE5ldHdvcmtNYW5hZ2VyCi0gYXBwbGV0OiBmaXggY3Jhc2ggd2hlbiBjcmVhdGluZyBuZXcgY29ubmVjdGlvbnMKLSBhcHBsZXQ6IGFjdHVhbGx5IHVzZSB3aXJlZCA4MDIuMXggc2VjcmV0cyBhZnRlciB0aGV5IGFyZSByZXF1ZXN0ZWQALSBubTogSVB2NiB6ZXJvY29uZiBzdXBwb3J0IGFuZCBmaXhlcwotIG5tOiBwb3J0IHRvIHBvbGtpdCAocmggIzQ5OTk2NSkKLSBubTogZml4ZXMgZm9yIGVoZWEgZGV2aWNlcyAocmggIzUxMTMwNCkgKHJoICM1MTY1OTEpCi0gbm06IHdvcmsgYXJvdW5kIFBQUCBidWcgY2F1c2luZyBib2d1cyBuYW1lc2VydmVycyBmb3IgbW9iaWxlIGJyb2FkYmFuZCBjb25uZWN0aW9ucwotIGVkaXRvcjogZml4IHNlZ2ZhdWx0IHdpdGggIlVubGlzdGVkIiBwbGFucyBpbiB0aGUgbW9iaWxlIGJyb2FkYmFuZCBhc3Npc3RhbnQALSBubTogYWRkIGlTQ1NJIHN1cHBvcnQKLSBubTogYWRkIGNvbm5lY3Rpb24gYXNzdW1lL3Rha2VvdmVyIHN1cHBvcnQgZm9yIGV0aGVybmV0IChyaCAjNTE3MzMzKQotIG5tOiBJUHY2IGZpeGVzCi0gbm06IHJlLWFkZCBPTFBDIFhPLTEgbWVzaCBkZXZpY2Ugc3VwcG9ydCAocmVtb3ZlZCB3aXRoIDAuNy4wKQotIGFwcGxldDogYmV0dGVyIFdpRmkgZGlhbG9nIGZvY3VzIGhhbmRsaW5nAC0gQWRkIHBhdGNoIHRvIGZpeCBzZXJ2aWNlIGRldGVjdGlvbiBvbiBwaG9uZXMALSBubTogSVB2NiBzdXBwb3J0IGZvciBtYW51YWwgJiByb3V0ZXItYWR2ZXJ0aXNlbWVudCBtb2RlcwAtIE1vdmUgc29tZSBiaWcgZG9jcyB0byAtZGV2ZWwgdG8gc2F2ZSBzcGFjZQAtIFVwZGF0ZSB0byB1cHN0cmVhbSAnbWFzdGVyJyBicmFuY2gKLSBVc2UgbW9kZW0tbWFuYWdlciBmb3IgYmV0dGVyIDNHIG1vZGVtIHN1cHBvcnQKLSBJbnRlZ3JhdGVkIHN5c3RlbSBzZXR0aW5ncyB3aXRoIE5ldHdvcmtNYW5hZ2VyIGl0c2VsZgotIFVzZSB1ZGV2IGluc3RlYWQgb2YgSEFMAC0gUmVidWlsdCBmb3IgaHR0cHM6Ly9mZWRvcmFwcm9qZWN0Lm9yZy93aWtpL0ZlZG9yYV8xMl9NYXNzX1JlYnVpbGQALSBhcHBsZXQ6IGZpeCBjZXJ0aWZpY2F0ZSB2YWxpZGF0aW9uIGluIGhpZGRlbiB3aWZpIG5ldHdvcmtzIGRpYWxvZyAocmggIzUwODIwNykALSBubTogZml4ZXMgZm9yIFpURS9PbmRhIG1vZGVtIGRldGVjdGlvbgotIG5tOiBwcmV2ZW50IHJlLW9wZW5pbmcgc2VyaWFsIHBvcnQgd2hlbiB0aGUgU0lNIGhhcyBhIFBJTgotIGFwcGxldDogdXBkYXRlZCB0cmFuc2xhdGlvbnMKLSBlZGl0b3I6IHNob3cgbGlzdCBjb2x1bW4gaGVhZGVycwAtIG5tOiBmaXggc2VyaWFsIHBvcnQgc2V0dGluZ3MALSBubTogZml4IEFUJlQgUXVpY2tzaWx2ZXIgbW9kZW0gY29ubmVjdGlvbnMgKHJoICM1MDIwMDIpCi0gbm06IGZpeCBzdXBwb3J0IGZvciBzMzkwIGJ1cyB0eXBlcyAocmggIzQ5NjgyMCkKLSBubTogZml4IGRldGVjdGlvbiBvZiBzb21lIENNT3RlY2ggbW9kZW1zCi0gbm06IGhhbmRsZSB1bnNvbGljaXRlZCB3aWZpIHNjYW5zIGJldHRlcgotIG5tOiByZXNvbHYuY29uZiBmaXhlcyB3aGVuIHVzaW5nIERIQ1AgYW5kIG92ZXJyaWRpbmcgc2VhcmNoIGRvbWFpbnMKLSBubTogaGFuZGxlIFdFUCBhbmQgV1BBIHBhc3NwaHJhc2VzIChyaCAjNDQxMDcwKQotIG5tOiBmaXggcmVtb3ZhbCBvZiBvbGQgQVBzIHdoZW4gbm9uZSBhcmUgc2Nhbm5lZAotIG5tOiBmaXggSHVhd2VpIEVDMTIxIGFuZCBFQzE2OEMgZGV0ZWN0aW9uIGFuZCBoYW5kbGluZyAocmggIzQ5NjQyNikKLSBhcHBsZXQ6IHNhdmUgV0VQIGFuZCBXUEEgcGFzc3BocmFzZXMgaW5zdGVhZCBvZiBoYXNoZWQga2V5cyAocmggIzQ0MTA3MCkKLSBhcHBsZXQ6IGZpeCBicm9rZW4gbm90aWZpY2F0aW9uIGJ1YmJsZSBhY3Rpb25zCi0gYXBwbGV0OiBkZWZhdWx0IHRvIFdFUCBlbmNyeXB0aW9uIGZvciBBZC1Ib2MgbmV0d29yayBjcmVhdGlvbgotIGFwcGxldDogZml4IGNyYXNoIHdoZW4gY29ubmVjdGlvbiBlZGl0b3IgZGlhbG9ncyBhcmUgY2FuY2VsZWQKLSBhcHBsZXQ6IGFkZCBhIG1vYmlsZSBicm9hZGJhbmQgcHJvdmlkZXIgd2l6YXJkAC0gZHJvcCBFeGNsdWRlQXJjaCBzMzkwIHMzOTB4LCB3ZSBuZWVkIGF0IGxlYXN0IHRoZSBoZWFkZXIgZmlsZXMALSBubS1zYXZlLXRoZS1sZWFzZXMucGF0Y2g6IFVzZSBwZXItY29ubmVjdGlvbiBsZWFzZSBmaWxlcywgYW5kIGRvbid0IGRlbGV0ZQogIHRoZW0gb24gaW50ZXJmYWNlIGRlYWN0aXZhdGUuAC0gaWZjZmctcmg6IGZpeCBwcm9ibGVtcyBub3RpY2luZyBjaGFuZ2VzIHZpYSBpbm90aWZ5IChyaCAjNDk1ODg0KQAtIGlmY2ZnLXJoOiBlbmFibGUgd3JpdGUgc3VwcG9ydCBmb3Igd2lyZWQgYW5kIHdpZmkgY29ubmVjdGlvbnMALSBubTogdXBkYXRlIHRvIDAuNy4xCi0gbm06IGZpeCBzdGFydHVwIHJhY2Ugd2l0aCBIQUwgY2F1c2luZyB1bm1hbmFnZWQgZGV2aWNlcyB0byBzb21ldGltZXMgYmUgbWFuYWdlZCAocmggIzQ5NDUyNykALSBubTogZml4IHJlY29nbml0aW9uIG9mIE9wdGlvbiBHVCBGdXNpb24gYW5kIE9wdGlvbiBHVCBIU0RQQSAobm96b21pKSBkZXZpY2VzIChyaCAjNDk0MDY5KQotIG5tOiBmaXggaGFuZGxpbmcgb2Ygc3BhY2VzIGluIERIQ1AgJ2RvbWFpbi1zZWFyY2gnIG9wdGlvbgotIG5tOiBmaXggZGV0ZWN0aW9uIG9mIG5ld2VyIE9wdGlvbiAnaHNvJyBkZXZpY2VzCi0gbm06IGlnbm9yZSBsb3cgTVRVcyByZXR1cm5lZCBieSBicm9rZW4gREhDUCBzZXJ2ZXJzAC0gVXBkYXRlIHRvIDAuNy4xLXJjNAotIG5tOiB1c2UgUG9saWN5S2l0IGZvciBzeXN0ZW0gY29ubmVjdGlvbiBzZWNyZXRzIHJldHJpZXZhbAotIG5tOiBjb3JyZWN0bHkgaW50ZXJwcmV0IGVycm9ycyByZXR1cm5lZCBmcm9tIGNobW9kKDIpIHdoZW4gc2F2aW5nIGtleWZpbGUgc3lzdGVtIGNvbm5lY3Rpb25zCi0gZWRpdG9yOiB1c2UgUG9saWN5S2l0IHRvIGdldCBzeXN0ZW0gY29ubmVjdGlvbiBzZWNyZXRzAC0gbm06IGZpeCBjcmFzaGVzIHdpdGggb3V0LW9mLXRyZWUgbW9kdWxlcyB0aGF0IHByb3ZpZGUgbm8gZHJpdmVyIGxpbmsgKHJoICM0OTIyNDYpCi0gbm06IGZpeCBVU0IgbW9kZW0gcHJvYmluZyBvbiByZWNlbnQgdWRldiB2ZXJzaW9ucwAtIG5tOiBmaXggY29tbXVuaWNhdGlvbiB3aXRoIE9wdGlvbiBHVCBNYXggMy42IG1vYmlsZSBicm9hZGJhbmQgY2FyZHMKLSBubTogZml4IGNvbW11bmljYXRpb24gd2l0aCBIdWF3ZWkgbW9iaWxlIGJyb2FkYmFuZCBjYXJkcyAocmggIzQ4NzY2MykKLSBubTogZG9uJ3QgbG9vayB1cCBob3N0bmFtZSB3aGVuIEhPU1ROQU1FPWxvY2FsaG9zdCB1bmxlc3MgYXNrZWQgKHJoICM0OTAxODQpCi0gbm06IGZpeCBjcmFzaCBkdXJpbmcgSVA0IGNvbmZpZ3VyYXRpb24gKHJoICM0OTE2MjApCi0gbm06IGlnbm9yZSBPTkJPT1Q9bm8gZm9yIG1pbmltYWwgaWZjZmcgZmlsZXMgKGY5ICYgZjEwIG9ubHkpIChyaCAjNDg5Mzk4KQotIGFwcGxldDogdXBkYXRlZCB0cmFuc2xhdGlvbnMALSBubTogd29yayBhcm91bmQgdW5oYW5kbGVkIGRldmljZSByZW1vdmFscyBkdWUgdG8gbWlzc2luZyBIQUwgZXZlbnRzIChyaCAjNDg0NTMwKQotIG5tOiBpbXByb3ZlIGhhbmRsaW5nIG9mIG11bHRpcGxlIG1vZGVtIHBvcnRzCi0gbm06IHN1cHBvcnQgZm9yIFNvbnkgRXJpY3Nzb24gRjM1MDdnIC8gTUQzMDAgYW5kIERlbGwgNTUzMAotIGFwcGxldDogdXBkYXRlZCB0cmFuc2xhdGlvbnMALSBNaXNzaW5nIE9OQk9PVCBzaG91bGQgYWN0dWFsbHkgbWVhbiBPTkJPT1Q9eWVzIChyaCAjNDg5NDIyKQAtIEZpeCBjb25mbGljdCB3aXRoIE5ldHdvcmtNYW5hZ2VyLW9wZW5jb25uZWN0IChyaCAjNDg5MjcxKQotIEZpeCBwb3NzaWJsZSBjcmFzaCB3aGVuIHJlc3luY2hyb25pemluZyBkZXZpY2VzIGlmIEhBTCByZXN0YXJ0cwAtIG5tOiBtYWtlIGRlZmF1bHQgd2lyZWQgIkF1dG8gZXRoWCIgY29ubmVjdGlvbiBtb2RpZmlhYmxlIGlmIGFuIGVuYWJsZWQgc3lzdGVtIHNldHRpbmdzCiAgICBwbHVnaW4gc3VwcG9ydHMgbW9kaWZ5aW5nIGNvbm5lY3Rpb25zIChyaCAjNDg1NTU1KQotIG5tOiBtYW5wYWdlIGZpeGVzIChyaCAjNDQ3MjMzKQotIG5tOiBDVkUtMjAwOS0wMzY1IC0gR2V0U2VjcmV0cyBkaXNjbG9zdXJlCi0gYXBwbGV0OiBDVkUtMjAwOS0wNTc4IC0gbG9jYWwgdXNlcnMgY2FuIG1vZGlmeSB0aGUgY29ubmVjdGlvbiBzZXR0aW5ncwotIGFwcGxldDogZml4IGluYWJpbGl0eSB0byBjaG9vc2UgV1BBIEFkLUhvYyBuZXR3b3JrcyBmcm9tIHRoZSBtZW51Ci0gaWZjZmctcmg6IGFkZCByZWFkLW9ubHkgc3VwcG9ydCBmb3IgV1BBLVBTSyBjb25uZWN0aW9ucwAtIEZpeCBnZXR0aW5nIHNlY3JldHMgZm9yIHN5c3RlbSBjb25uZWN0aW9ucyAocmggIzQ4NjY5NikKLSBNb3JlIGNvbXBhdGlibGUgbW9kZW0gYXV0b2RldGVjdGlvbgotIEJldHRlciBoYW5kbGUgbWluaW1hbCBpZmNmZyBmaWxlcwAtIFJlYnVpbHQgZm9yIGh0dHBzOi8vZmVkb3JhcHJvamVjdC5vcmcvd2lraS9GZWRvcmFfMTFfTWFzc19SZWJ1aWxkAC0gVXNlIElGRl9MT1dFUl9VUCBmb3IgY2FycmllciBkZXRlY3QgaW5zdGVhZCBvZiBJRkZfUlVOTklORwotIEFkZCBzbWFsbCBkZWxheSBiZWZvcmUgcHJvYmluZyBjZGMtYWNtIGRyaXZlbiBtb2JpbGUgYnJvYWRiYW5kIGRldmljZXMALSBGaXggUEVBUCB2ZXJzaW9uIHNlbGVjdGlvbiBpbiB0aGUgYXBwbGV0IChyaCAjNDY4ODQ0KQotIE1hdGNoIGhvc3RuYW1lIGJlaGF2aW9yIHRvICduZXR3b3JrJyBzZXJ2aWNlIHdoZW4gaG9zdG5hbWUgaXMgbG9jYWxob3N0IChyaCAjNDQxNDUzKQAtIEZpeCAnbm9yZXBsYWNlJyBmb3Igbm0tc3lzdGVtLXNldHRpbmdzLmNvbmYALSBVcGRhdGUgdG8gMC43LjFyYzEKLSBubTogc3VwcG9ydCBmb3IgSHVhd2VpIEUxNjBHIG1vYmlsZSBicm9hZGJhbmQgZGV2aWNlcyAocmggIzQ2NjE3NykKLSBubTogZml4IG1pc2xlYWRpbmcgcm91dGluZyBlcnJvciBtZXNzYWdlIChyaCAjNDc3OTE2KQotIG5tOiBmaXggaXNzdWVzIHdpdGggMzItY2hhcmFjdGVyIFNTSURzIChyaCAjNDg1MzEyKQotIG5tOiBhbGxvdyByb290IHRvIGFjdGl2YXRlIHVzZXIgY29ubmVjdGlvbnMKLSBubTogYXV0b21hdGljIG1vZGVtIGRldGVjdGlvbiB3aXRoIHVkZXYtZXh0cmFzCi0gbm06IG1hc3NpdmUgbWFucGFnZSByZXdyaXRlCi0gYXBwbGV0OiBmaXggY3Jhc2ggd2hlbiBzaG93aW5nIHRoZSBDQSBjZXJ0aWZpY2F0ZSBpZ25vcmUgZGlhbG9nIGEgc2Vjb25kIHRpbWUKLSBhcHBsZXQ6IGNsZWFyIGtleXJpbmcgaXRlbXMgd2hlbiBkZWxldGluZyBhIGNvbm5lY3Rpb24KLSBhcHBsZXQ6IGZpeCBtYXggc2lnbmFsIHN0cmVuZ3RoIGNhbGN1bGF0aW9uIGluIG1lbnUgKHJoICM0NzUxMjMpCi0gYXBwbGV0OiBmaXggVlBOIGV4cG9ydCAocmggIzQ4MDQ5NikALSBhcHBsZXQ6IGZpeCBibGFuayBWUE4gY29ubmVjdGlvbiBtZXNzYWdlIGJ1YmJsZXMKLSBhcHBsZXQ6IGJldHRlciBoYW5kbGluZyBvZiBWUE4gcm91dGluZyBvbiB1cGRhdGUKLSBhcHBsZXQ6IHNpbGVuY2UgcG9pbnRsZXNzIHdhcm5pbmcgKHJoICM0ODQxMzYpCi0gYXBwbGV0OiBkZXNlbnNpdGl6ZSBkZXZpY2VzIGluIHRoZSBtZW51IHVudGlsIHRoZXkgYXJlIHJlYWR5IChyaCAjNDgzODc5KQotIG5tOiBFeHBvc2UgV0lOUyBzZXJ2ZXJzIGluIHRoZSBJUDRDb25maWcgb3ZlciBELUJ1cwotIG5tOiBCZXR0ZXIgaGFuZGxpbmcgb2YgR1NNIE1vYmlsZSBCcm9hZGJhbmQgbW9kZW0gaW5pdGlhbGl6YXRpb24KLSBubTogSGFuZGxlIERIQ1AgQ2xhc3NsZXNzIFN0YXRpYyBSb3V0ZXMgKFJGQyAzNDQyKQotIG5tOiBGaXggTW9iaWxlIEJyb2FkYmFuZCBhbmQgUFBQb0UgdG8gYWx3YXlzIHVzZSAnbm9hdXRoJwotIG5tOiBCZXR0ZXIgY29tcGF0aWJpbGl0eSB3aXRoIG9sZGVyIGR1YWwtU1NJRCBBUCBjb25maWd1cmF0aW9ucyAocmggIzQ0NTM2OSkKLSBubTogTWFyayBubS1zeXN0ZW0tc2V0dGluZ3MuY29uZiBhcyBjb25maWcgKHJoICM0NjU2MzMpCi0gbm0tdG9vbDogU2hvdyBWUE4gY29ubmVjdGlvbiBpbmZvcm1hdGlvbgotIGlmY2ZnLXJoOiBTaWxlbmNlIG1lc3NhZ2UgYWJvdXQgaWdub3JpbmcgbG9vcGJhY2sgY29uZmlnIChyaCAjNDg0MDYwKQotIGlmY2ZnLXJoOiBGaXggaXNzdWUgd2l0aCB3cm9uZyBnYXRld2F5IGZvciBzeXN0ZW0gY29ubmVjdGlvbnMgKHJoICM0NzYwODkpAC0gVXBkYXRlIHRvIDAuNy4xIHByZS1yZWxlYXNlCi0gQWxsb3cgY29ubmVjdGlvbnMgdG8gYmUgaWdub3JlZCB3aGVuIGRldGVybWluaW5nIHRoZSBkZWZhdWx0IHJvdXRlIChyaCAjNDc2MDg5KQotIE93biAvdXNyL3NoYXJlL2dub21lLXZwbi1wcm9wZXJ0aWVzIChyaCAjNDc3MTU1KQotIEZpeCBsb2cgZmxvb2RpbmcgZHVlIHRvIG5ldGxpbmsgZXJyb3JzIChyaCAjNDU5MjA1KQotIFBhc3MgY29ubmVjdGlvbiBVVUlEIHRvIGRpc3BhdGNoZXIgc2NyaXB0cyB2aWEgdGhlIGVudmlyb25tZW50Ci0gRml4IHBvc3NpYmxlIGNyYXNoIGFmdGVyIGRlYWN0aXZhdGluZyBhIFZQTiBjb25uZWN0aW9uCi0gRml4IGlzc3VlcyB3aXRoIGVkaXRpbmcgd2lyZWQgODAyLjF4IGNvbm5lY3Rpb25zCi0gRml4IGlzc3VlcyB3aGVuIHVzaW5nIFBLQ1MjMTIgY2VydGlmaWNhdGVzIHdpdGggODAyLjF4IGNvbm5lY3Rpb25zAC0gQVBJIGFuZCBkb2N1bWVudGF0aW9uIHVwZGF0ZXMKLSBGaXggUElOIGhhbmRsaW5nIG9uICdoc28nIG1vYmlsZSBicm9hZGJhbmQgZGV2aWNlcwAtIEZpeCBQSU4vUFVLIGlzc3VlcyB3aXRoIGhpZ2gtc3BlZWQgT3B0aW9uIEhTRFBBIG1vYmlsZSBicm9hZGJhbmQgY2FyZHMKLSBGaXggZGVzZW5zaXRpemVkIE9LIGJ1dHRvbiB3aGVuIGFza2luZyBmb3Igd2lyZWxlc3Mga2V5cwAtIEZpeCBpc3N1ZXMgcmVhZGluZyBpZmNmZyBmaWxlcwotIFByZXZpb3VzbHkgZml4ZWQ6Ci0gRG9lc24ndCBzZW5kIERIQ1AgaG9zdG5hbWUgKHJoICM0NjkzMzYpCi0gJ0F1dG8gZXRoMCcgZm9yZ2V0cyBzZXR0aW5ncyAocmggIzQ2ODYxMikKLSBESENQIHJlbmV3YWwgc29tZXRpbWVzIGJyZWFrcyBWUE4gKHJoICM0NzE4NTIpCi0gQ29ubmVjdGlvbiBlZGl0b3IgbWVudSBpdGVtIGluIHRoZSB3cm9uZyBwbGFjZSAocmggIzQ3MTQ5NSkKLSBDYW5ub3QgbWFrZSBzeXN0ZW0td2lkZSBjb25uZWN0aW9ucyAocmggIzQ3MTMwOCkALSBVcGRhdGUgdG8gTmV0d29ya01hbmFnZXIgMC43LjAgUkMyCi0gSGFuZGxlIGdhdGV3YXlzIG9uIGEgZGlmZmVyZW50IHN1Ym5ldCBmcm9tIHRoZSBpbnRlcmZhY2UKLSBDbGVhciBWUE4gc2VjcmV0cyBvbiBjb25uZWN0aW9uIGZhaWx1cmUgdG8gZW5zdXJlIHRoZXkgYXJlIHJlcXVlc3RlZCBhZ2FpbiAocmggIzQyOTI4NykKLSBBZGQgc3VwcG9ydCBmb3IgUEtDUyMxMiBwcml2YXRlIGtleXMgKHJoICM0NjI3MDUpCi0gRml4IG1hbmdsaW5nIG9mIFZQTidzIGRlZmF1bHQgcm91dGUgb24gREhDUCByZW5ldwotIEZpeCB0eXBlIGRldGVjdGlvbiBvZiBxZW11L2t2bSBuZXR3b3JrIGRldmljZXMgKHJoICM0NjYzNDApCi0gQ2xlYXIgdXAgbmV0bWFzay9wcmVmaXggY29uZnVzaW9uIGluIHRoZSBjb25uZWN0aW9uIGVkaXRvcgotIE1ha2UgdGhlIHNlY3JldHMgZGlhbG9nIGdvIGF3YXkgd2hlbiBpdCdzIG5vdCBuZWVkZWQKLSBGaXggaW5hYmlsaXR5IHRvIGFkZCBzeXN0ZW0gY29ubmVjdGlvbnMgKHJoICM0NzEzMDgpAC0gTW9yZSByZWxpYWJsZSBtb2JpbGUgYnJvYWRiYW5kIGNhcmQgaW5pdGlhbGl6YXRpb24KLSBIYW5kbGUgbW9iaWxlIGJyb2FkYmFuZCBQSU5zIGNvcnJlY3RseSB3aGVuIFBQUCBwYXNzd29yZHMgYXJlIGFsc28gdXNlZAotIEFkZGl0aW9uYWwgUG9saWN5S2l0IGludGVncmF0aW9uIGZvciBlZGl0aW5nIHN5c3RlbSBjb25uZWN0aW9ucwotIENsb3NlIHRoZSBhcHBsZXQgbWVudSBpZiBhIGtleXJpbmcgcGFzc3dvcmQgaXMgbmVlZGVkIChyaCAjMzUzNDUxKQAtIEZpeCBpc3N1ZXMgd2l0aCBob3N0bmFtZSBkdXJpbmcgYW5hY29uZGEgaW5zdGFsbGF0aW9uIChyaCAjNDYxOTMzKQotIEZpeCBBZC1Ib2MgV1BBIGNvbm5lY3Rpb25zIChyaCAjNDYxMTk3KQotIERvbid0IHJlcXVpcmUgZ25vbWUtcGFuZWwgb3IgZ25vbWUtcGFuZWwtZGV2ZWwgKHJoICM0Mjc4MzQpCi0gRml4IGRldGVybWluYXRpb24gb2YgV1BBIGVuY3J5cHRpb24gY2FwYWJpbGl0aWVzIG9uIHNvbWUgY2FyZHMKLSBGaXggY29uZmxpY3RzIHdpdGggUFBUUCBhbmQgdnBuYyBwbHVnaW5zCi0gQWxsb3cgLmNlciBmaWxlIGV4dGVuc2lvbnMgd2hlbiBjaG9vc2luZyBjZXJ0aWZpY2F0ZXMALSBGaXggY29uZmxpY3RzIGZvciBvbGRlciBQUFRQIFZQTiBwbHVnaW5zAC0gRW5zdXJlIHRoYXQgbW9iaWxlIGJyb2FkYmFuZCBjYXJkcyBhcmUgcG93ZXJlZCB1cCBiZWZvcmUgdHJ5aW5nIHRvIHVzZSB0aGVtCi0gSG9zdG5hbWUgY2hhbmdpbmcgc3VwcG9ydCAocmggIzQ0MTQ1MykKLSBGaXggbW9iaWxlIGJyb2FkYmFuZCBzZWNyZXQgcmVxdWVzdHMgdG8gaGFwcGVuIGxlc3Mgb2Z0ZW4KLSBCZXR0ZXIgaGFuZGxpbmcgb2YgZGVmYXVsdCBkZXZpY2VzIGFuZCBkZWZhdWx0IHJvdXRlcwotIEJldHRlciBpbmZvcm1hdGlvbiBpbiB0b29sdGlwcyBhbmQgbm90aWZpY2F0aW9ucwotIFZhcmlvdXMgVUkgY2xlYW51cHM7IGhpZGUgd2lkZ2V0cyB0aGF0IGFyZW4ndCB1c2VkIChyaCAjNDY1Mzk3LCByaCAjNDY1Mzk1KQotIEFjY2VwdCBkaWZmZXJlbnQgc2VwYXJhdG9ycyBmb3IgRE5TIHNlcnZlcnMgYW5kIHNlYXJjaGVzCi0gTWFrZSBhcHBsZXQncyBpY29uIGFjY3VyYXRlbHkgcmVmbGVjdCBzaWduYWwgc3RyZW5ndGggb2YgdGhlIGN1cnJlbnQgQVAALSBGaXggY29ubmVjdGlvbiBjb21wYXJpc29uIHRoYXQgY291bGQgY2F1c2UgY2hhbmdlcyB0byBnZXQgb3ZlcndyaXR0ZW4gKHJoICM0NjQ0MTcpAC0gRml4IGhhbmRsaW5nIG9mIFZQTiBzZXR0aW5ncyBvbiB1cGdyYWRlIChyaCAjNDYwNzMwLCBiZ28gIzU1MzQ2NSkALSBGaXggaGFuZyB3aGVuIHJlYWRpbmcgc3lzdGVtIGNvbm5lY3Rpb25zIGZyb20gaWZjZmcgZmlsZXMALSBGaXggV1BBIEFkLUhvYyBjb25uZWN0aW9ucwAtIEZpeCBwYXJzaW5nIG9mIERPTUFJTiBpbiBpZmNmZyBmaWxlcyAocmggIzQ1OTM3MCkKLSBGaXggcmVjb25uZWN0aW9uIHRvIG1vYmlsZSBicm9hZGJhbmQgbmV0d29ya3MgYWZ0ZXIgYW4gYXV0aCBmYWlsdXJlCi0gRml4IHJlY29nbml0aW9uIG9mIHRpbWVvdXRzIG9mIFBQUCBkdXJpbmcgbW9iaWxlIGJyb2FkYmFuZCBjb25uZWN0aW9uCi0gTW9yZSBjb21wYXRpYmxlIGNvbm5lY3Rpb24gc2hhcmluZyAocmggIzQ1ODYyNSkKLSBGaXggREhDUCBpbiBtaW5pbWFsIGVudmlyb25tZW50cyB3aXRob3V0IGdsaWJjIGxvY2FsZSBpbmZvcm1hdGlvbiBpbnN0YWxsZWQKLSBBZGQgc3VwcG9ydCBmb3IgT3B0aW9uIG1vYmlsZSBicm9hZGJhbmQgZGV2aWNlcyAobGlrZSBpQ09OIDIyNSBhbmQgaUNPTiA3LjIpCi0gQWRkIElQNCBjb25maWcgaW5mb3JtYXRpb24gdG8gZGlzcGF0Y2hlciBzY3JpcHQgZW52aXJvbm1lbnQKLSBNZXJnZSBXRVAgQVNDSUkgYW5kIEhleCBrZXkgdHlwZXMgZm9yIGNsZWFuZXIgVUkKLSBQcmUtZmlsbCBQUFBvRSBwYXNzd29yZCB3aGVuIGF1dGhlbnRpY2F0aW9uIGZhaWxzCi0gRml4ZWQgc29tZSBjaGFuZ2VzIG5vdCBnZXR0aW5nIHNhdmVkIGluIHRoZSBjb25uZWN0aW9uIGVkaXRvcgotIEFjY2VwdCBib3RoIHByZWZpeCBhbmQgbmV0bWFzayBpbiB0aGUgY29uZWN0aW9uIGVkaXRvcidzIElQdjQgcGFnZQAtIEZpeCBpc3N1ZSB3aXRoIG1vYmlsZSBicm9hZGJhbmQgY29ubmVjdGlvbnMgdGhhdCBkb24ndCByZXF1aXJlIGF1dGhlbnRpY2F0aW9uAC0gRXhwb3NlIERIQ1AtcmV0dXJuZWQgb3B0aW9ucyBvdmVyIEQtQnVzIGFuZCB0byBkaXNwYXRjaGVyIHNjcmlwdHMKLSBBZGQgc3VwcG9ydCBmb3IgY3VzdG9taXplZCBzdGF0aWMgcm91dGVzCi0gSGFuZGxlIG11bHRpcGxlIGNvbmN1cnJlbnQgM0cgb3IgUFBQb0UgY29ubmVjdGlvbnMKLSBGaXggR1NNL0NETUEgdXNlcm5hbWUgYW5kIHBhc3N3b3JkIGlzc3VlcwotIEJldHRlciBoYW5kbGluZyBvZiB1bm1hbmFnZWQgZGV2aWNlcyBmcm9tIGlmY2ZnIGZpbGVzCi0gRml4IHRpbWVvdXQgaGFuZGxpbmcgb2YgZXJyb3JzIGR1cmluZyAzRyBjb25uZWN0aW9ucwotIEZpeCBzb21lIHJvdXRpbmcgaXNzdWVzIChyaCAjNDU2Njg1KQotIEZpeCBhcHBsZXQgY3Jhc2hlcyBhZnRlciByZW1vdmluZyBhIGRldmljZSAocmggIzQ1NzM4MCkALSBDb252ZXJ0IHN0b3JlZCBJUHY0IHN0YXRpYyBJUCBhZGRyZXNzZXMgdG8gbmV3IHByZWZpeC1iYXNlZCBzY2hlbWUgYXV0b21hdGljYWxseQotIEZpeCBwcHBkIGNvbm5lY3Rpb25zIHRvIHNvbWUgM0cgcHJvdmlkZXJzIChyaCAjNDU1MzQ4KQotIE1ha2UgUFBQb0UgIlNob3cgUGFzc3dvcmQiIG9wdGlvbiB3b3JrCi0gSGlkZSBJUHY0IGNvbmZpZyBvcHRpb25zIHRoYXQgZG9uJ3QgbWFrZSBzZW5zZSBpbiBjZXJ0YWluIGNvbmZpZ3VyYXRpb25zAC0gRXhwb3NlIHNlcnZlci1yZXR1cm5lZCBESENQIG9wdGlvbnMgdmlhIEQtQnVzCi0gVXNlIGF2YWhpLWF1dG9pcGQgcmF0aGVyIHRoYW4gb2xkIGJ1aWx0LWluIElQdjRMTCBpbXBsZW1lbnRhdGlvbgotIFNlbmQgaG9zdG5hbWUgdG8gREhDUCBzZXJ2ZXIgaWYgcHJvdmlkZWQgKERIQ1BfSE9TVE5BTUUgaWZjZmcgb3B0aW9uKQotIFN1cHBvcnQgc2VuZGluZyBESENQIENsaWVudCBJZGVudGlmaWVyIHRvIERIQ1Agc2VydmVyCi0gQWxsb3cgZm9yY2luZyA4MDIuMXggUEVBUCBMYWJlbCB0byAnMCcKLSBNYWtlIGNvbm5lY3Rpb24gc2hhcmluZyBtb3JlIHJvYnVzdAotIFNob3cgc3RhdHVzIGZvciBzaGFyZWQgYW5kIEFkLUhvYyBjb25uZWN0aW9ucyBpZiBubyBvdGhlciBjb25uZWN0aW9uIGlzIGFjdGl2ZQAtIERyb3AgZXhwbGljaXQgaGFsIGRlcCBpbiAtZ25vbWUALSBNb3ZlIFZQTiBjb25maWd1cmF0aW9uIGludG8gY29ubmVjdGlvbiBlZGl0b3IKLSBGaXggbW9iaWxlIGJyb2FkYmFuZCB1c2VybmFtZS9wYXNzd29yZCBpc3N1ZXMKLSBGaXggaXNzdWVzIHdpdGggYnJva2VuIHJma2lsbCBzZXR1cHMgKHJoICM0NDg4ODkpCi0gSG9ub3IgQVBOIHNldHRpbmcgZm9yIEdTTSBtb2JpbGUgYnJvYWRiYW5kIGNvbmZpZ3VyYXRpb25zCi0gRml4IGFkZGluZyBDRE1BIGNvbm5lY3Rpb25zIGluIHRoZSBjb25uZWN0aW9uIGVkaXRvcgAtIFVwZGF0ZSB0byBsYXRlc3QgU1ZOCi0gRW5hYmxlIGNvbm5lY3Rpb24gc2hhcmluZwotIFJlc3BlY3QgVlBOLXByb3ZpZGVkIHJvdXRlcwAtIE1vdmUgTk0gbGF0ZXIgaW4gdGhlIHNodXRkb3duIHByb2Nlc3MgKHJoICM0NDkwNzApCi0gTW92ZSBsaWJubS11dGlsIGludG8gYSBzdWJwYWNrYWdlIHRvIGFsbG93IE5NIHRvIGJlIHJlbW92ZWQgbW9yZSBlYXNpbHkgKHJoICMzNTExMDEpAC0gUmVhZCBnbG9iYWwgZ2F0ZXdheSBmcm9tIC9ldGMvc3lzY29uZmlnL25ldHdvcmsgaWYgbWlzc2luZyAocmggIzQ0NjUyNykKLSBubS1zeXN0ZW0tc2V0dGluZ3Mgbm93IHRlcm1pbmF0ZXMgd2hlbiBkYnVzIGdvZXMgYXdheSAocmggIzQ0NDk3NikALSBGaXggaW5pdGlhbCBjYXJyaWVyIHN0YXRlIGRldGVjdGlvbiBvbiBkZXZpY2VzIHRoYXQgYXJlIGFscmVhZHkgdXAgKHJoICMxMzQ4ODYpAC0gUmVzdG9yZSBiZWhhdmlvciBvZiBtYXJraW5nIHdpZmkgZGV2aWNlcyBhcyAiZG93biIgd2hlbiBkaXNhYmxpbmcgd2lyZWxlc3MKLSBGaXggYSBjcmFzaCBvbiByZXN1bWUgd2hlbiBhIFZQTiB3YXMgYWN0aXZlIHdoZW4gZ29pbmcgdG8gc2xlZXAALSBGaXggaXNzdWVzIHdpdGggdGhlIEZlZG9yYSBwbHVnaW4gbm90IG5vdGljaW5nIGNoYW5nZXMgbWFkZSBieQogICAgc3lzdGVtLWNvbmZpZy1uZXR3b3JrIChyaCAjNDQ0NTAyKQotIEFsbG93IGF1dG9jb25uZWN0aW9uIG9mIEdTTSBhbmQgQ0RNQSBjb25uZWN0aW9ucwotIE11bHRpcGxlIElQIGFkZHJlc3Mgc3VwcG9ydCBmb3IgdXNlciBjb25uZWN0aW9ucwotIEZpeGVzIGZvciBNb2JpbGUgQnJvYWRiYW5kIGNhcmRzIHRoYXQgcmV0dXJuIGxpbmUgc3BlZWQgb24gY29ubmVjdAotIEltcGxlbWVudCBQSU4gZW50cnkgZm9yIEdTTSBtb2JpbGUgYnJvYWRiYW5kIGNvbm5lY3Rpb25zCi0gRml4IGNyYXNoIHdoZW4gZWRpdGluZyB1bmVuY3J5cHRlZCBXaUZpIGNvbm5lY3Rpb25zIGluIHRoZSBjb25uZWN0aW9uIGVkaXRvcgAtIENsZWFuIHVwIHRoZSBkaXNwYXRjaGVyIG5vdyB0aGF0IGl0J3Mgc2VydmljZSBpcyBnb25lIChyaCAjNDQ0Nzk4KQAtIEZpeCBhc2tpbmcgYXBwbGV0cyBmb3IgdGhlIEdTTSBQSU4vUFVLAC0gR3Vlc3MgV0VQIGtleSB0eXBlIGluIGFwcGxldCB3aGVuIGFza2luZyBmb3IgbmV3IGtleXMKLSBDb3JyZWN0IE9LIGJ1dHRvbiBzZW5zaXRpdml0eSBpbiBhcHBsZXQgd2hlbiBhc2tpbmcgZm9yIG5ldyBXRVAga2V5cwAtIEZpeCBpc3N1ZXMgd2l0aCBNb2JpbGUgQnJvYWRiYW5kIGNvbm5lY3Rpb25zIGNhdXNlZCBieSBkZXZpY2UgaW5pdCByYWNlIHBhdGNoAC0gRml4IGRldmljZSBpbml0aWFsaXphdGlvbiByYWNlIHRoYXQgY2F1c2VkIGV0aGVybmV0IGRldmljZXMgdG8gZ2V0IHN0dWNrIG9uCiAgICBzdGFydHVwCi0gRml4IFBQUG9FIGNvbm5lY3Rpb25zIG5vdCBzaG93aW5nIHVwIGluIHRoZSBhcHBsZXQKLSBGaXggZGlzYWJsZWQgT0sgYnV0dG9uIGluIGNvbm5lY3Rpb24gZWRpdG9yIHNvbWUgd2lyZWxlc3MgYW5kIElQNCBzZXR0aW5ncwotIERvbid0IGV4aXQgaWYgSEFMIGlzbid0IHVwIHlldDsgd2FpdCBmb3IgaXQKLSBGaXggYSBzdXNwZW5kL3Jlc3VtZSBjcmFzaAAtIERvbid0IGFzayBmb3Igd2lyZWxlc3Mga2V5cyB3aGVuIHRoZSBkcml2ZXIgc2VuZHMgZGlzY29ubmVjdCBldmVudHMgZHVyaW5nCglhc3NvY2lhdGlvbjsgd2FpdCB1bnRpbCB0aGUgZW50aXJlIGFzc29jYXRpb24gdGltZXMgb3V0Ci0gUmVwbGFjZSBkaXNwYXRjaGVyIGRhZW1vbiB3aXRoIEQtQnVzIGFjdGl2YXRlZCBjYWxsb3V0Ci0gRml4IHBhcnNpbmcgb2YgRE5TMiBhbmQgRE5TMyBpZmNmZyBmaWxlIGl0ZW1zCi0gRXhlY3V0ZSBkaXNwYXRjaGVyIHNjcmlwdHMgaW4gYWxwaGFiZXRpY2FsIG9yZGVyCi0gQmUgYWN0aXZlIGF0IHJ1bmxldmVsIDIKLSBIb29rIHVwIE1BQyBhZGRyZXNzIHdpZGdldHMgZm9yIHdpcmVkICYgd2lyZWxlc3M7IGFuZCBCU1NJRCB3aWRnZXQgZm9yIHdpcmVsZXNzCi0gUHJlLXBvcHVsYXRlIGFub255bW91cyBpZGVudGl0eSBhbmQgcGhhc2UyIHdpZGdldHMgY29ycmVjdGx5Ci0gQ2xlYXIgb3V0IHVudXNlZCBjb25uZWN0aW9uIGtleXMgZnJvbSBHQ29uZgAtIERvbid0IHNlbGVjdCBkZXZpY2VzIHdpdGhvdXQgYSBkZWZhdWx0IGdhdGV3YXkgYXMgdGhlIGRlZmF1bHQgcm91dGUgKHJoICM0MzczMzgpCi0gRmlsbCBpbiBicm9hZGNhc3QgYWRkcmVzcyBpZiBub3Qgc3BlY2lmaWVkIChyaCAjNDQzNDc0KQotIFJlc3BlY3QgbWFudWFsIFZQTiBJUHY0IGNvbmZpZ3VyYXRpb24gb3B0aW9ucwotIFNob3cgQ29ubmVjdGlvbiBJbmZvcm1hdGlvbiBmb3IgdGhlIGRldmljZSB3aXRoIHRoZSBkZWZhdWx0IHJvdXRlIG9ubHkALSBBZGQgZGJ1cy1nbGliLWRldmVsIEJ1aWxkUmVxdWlyZXMgZm9yIE5ldHdvcmtNYW5hZ2VyLWdsaWItZGV2ZWwgKHJoICM0NDI5NzgpCi0gQWRkIFBQUCBzZXR0aW5ncyBwYWdlIHRvIGNvbm5lY3Rpb24gZWRpdG9yCi0gRml4IGEgZmV3IGNyYXNoZXMgd2l0aCBQUFBvRQotIEZpeCBhY3RpdmUgY29ubmVjdGlvbiBzdGF0ZSBjaGFuZ2VzIHRoYXQgY29uZnVzZWQgY2xpZW50cwAtIEZpeCBidWlsZCBpbiBwcHBkLXBsdWdpbgAtIFBQUG9FIGF1dGhlbnRpY2F0aW9uIGZpeGVzCi0gTW9yZSByb2J1c3QgaGFuZGluZyBvZiBtb2JpbGUgYnJvYWRiYW5kIGRldmljZSBjb21tdW5pY2F0aW9ucwAtIEhvbm9yIG9wdGlvbnMgZnJvbSAvZXRjL3N5c2NvbmZpZy9uZXR3b3JrIGZvciBibG9ja2luZyB1bnRpbCBuZXR3b3JrIGlzIHVwAC0gVHVybiBvbiBBZGQvRWRpdCBpbiB0aGUgY29ubmVjdGlvbiBlZGl0b3IKLSBEb24ndCBmbHVzaCBvciBjaGFuZ2UgSVB2NiBhZGRyZXNzZXMgb3Igcm91dGVzCi0gRW5oYW5jZSBubS1vbmxpbmUgdG9vbAotIFNvbWUgc2VyaWFsIGNvbW11bmljYXRpb24gZml4ZXMgZm9yIG1vYmlsZSBicm9hZGJhbmQALSBGaXggaXNzdWVzIHdpdGggVlBOIHBhc3N3b3JkcyBub3QgZ2V0dGluZyBmb3VuZAAtIEZpeCBidWlsZHMgZHVlIHRvIGdsaWIyIGJyZWFrYWdlIG9mIEdTdGF0aWNNdXRleCB3aXRoIGdjYyA0LjMALSBGaXggV0VQIGtleSBpbmRleCBoYW5kbGluZyBpbiBVSQotIEZpeCBoYW5kbGluZyBvZiBOTV9DT05UUk9MTEVEIGluIGlmY2ZnIGZpbGVzCi0gU2hvdyBkZXZpY2UgbWFuYWdlZCBzdGF0ZSBpbiBhcHBsZXQgbWVudQotIFNob3cgd2lyZWxlc3MgZW5hYmxlZCBzdGF0ZSBpbiBhcHBsZXQgbWVudQotIEJldHRlciBoYW5kbGluZyBvZiBkZWZhdWx0IERIQ1AgY29ubmVjdGlvbnMgZm9yIHdpcmVkIGRldmljZXMKLSBGaXggbG9hZGluZyBvZiBjb25uZWN0aW9uIGVkaXRvciBvbiBLREUgKHJoICM0MzUzNDQpAC0gSG9ub3IgTUFDIGFkZHJlc3MgbG9ja2luZyBmb3Igd2lyZWQgJiB3aXJlbGVzcyBkZXZpY2VzAC0gU2hvdyBWUE4gZmFpbHVyZXMKLSBTdXBwb3J0IFN0YXRpYyBXRVAga2V5IGluZGV4ZXMKLSBGaXggcGFyc2luZyBvZiBXRVAga2V5cyBmcm9tIGlmY2ZnIGZpbGVzCi0gUHJlLWZpbGwgd2lyZWxlc3Mgc2VjdXJpdHkgVUkgYml0cyBpbiBjb25uZWN0aW9uIGVkaXRvciBhbmQgYXBwbGV0AC0gR3JhYiBzeXN0ZW0gc2V0dGluZ3MgZnJvbSAvZXRjL3N5c2NvbmZpZy9uZXR3b3JrLXNjcmlwdHMsIG5vdCBmcm9tIHByb2ZpbGVzAC0gRml4IGNyYXNoZXMgd2hlbiByZXR1cm5pbmcgVlBOIHNlY3JldHMgZnJvbSB0aGUgYXBwbGV0IHRvIE5NAC0gRml4IGNyYXNoZXMgb24gc3VzcGVuZC9yZXN1bWUgYW5kIGV4aXQgKHJoICM0Mzc0MjYpCi0gRW5zdXJlIHRoZXJlJ3MgYWx3YXlzIGFuIG9wdGlvbiB0byBjaG9zZSB0aGUgd2lyZWQgZGV2aWNlCi0gTmV2ZXIgc2V0IGRlZmF1bHQgcm91dGUgdmlhIGFuIElQdjQgbGluay1sb2NhbCBhZGRyZXNzZWQgZGV2aWNlIChyaCAjNDM3MzM4KQAtIEZpeCBESENQIHJlYmluZCBiZWhhdmlvcgotIFByZWxpbWluYXJ5IFBQUG9FIHN1cHBvcnQALSBGaXggZ25vbWUtaWNvbi10aGVtZSBSZXF1aXJlcywgc2hvdWxkIGJlIG9uIGdub21lIHN1YnBhY2thZ2UALSBIb25vciBESENQIHJlYmluZHMKLSBNdWx0aXBsZSBhY3RpdmUgZGV2aWNlIHN1cHBvcnQKLSBCZXR0ZXIgZXJyb3IgaGFuZGxpbmcgb2YgbW9iaWxlIGJyb2FkYmFuZCBjb25uZWN0aW9uIGZhaWx1cmVzCi0gQWxsb3cgdXNlIG9mIGludGVyZmFjZS1zcGVjaWZpYyBkaGNsaWVudCBjb25maWcgZmlsZXMKLSBSZWNvZ25pemUgc3lzdGVtIHNldHRpbmdzIHdoaWNoIGhhdmUgbm8gVFlQRSBpdGVtAC0gRml4IGNyYXNoIG9mIG5tLXN5c3RlbS1zZXR0aW5ncyBvbiBtYWxmb3JtZWQgaWZjZmcgZmlsZXMgKHJoICM0MzQ5MTkpCi0gUmVxdWlyZSBnbm9tZS1pY29uLXRoZW1lIHRvIHBpY2sgdXAgbG9jay5wbmcgKHJoICM0MzUzNDQpCi0gRml4IGFwcGxldCBzZWdmYXVsdCBhZnRlciBjb25uZWN0aW9uIHJlbW92YWwgdmlhIGNvbm5lY3Rpb24gZWRpdG9yIG9yIEdDb25mAC0gRG9uJ3QgY3JlYXRlIG11bHRpcGxlIGNvbm5lY3Rpb25zIGZvciBoaWRkZW4gYWNjZXNzIHBvaW50cwotIEZpeCBzY2FubmluZyBiZWhhdmlvcgAtIFJld29yayBjb25uZWN0aW9uIGVkaXRvciBjb25uZWN0aW9uIGxpc3QALSBCZXR0ZXIgaGFuZGxpbmcgb2YgY2hhbmdlcyBpbiB0aGUgcHJvZmlsZSBkaXJlY3RvcnkgYnkgdGhlIHN5c3RlbSBzZXR0aW5ncwoJc2VyaXZjZQAtIEVuYWJsZSBzeXN0ZW0gc2V0dGluZ3Mgc2VydmljZQotIEFsbG93IGV4cGxpY2l0IGRpc2Nvbm5lY3Rpb24gb2YgbW9iaWxlIGJyb2FkYmFuZCBkZXZpY2VzCi0gRml4IGFwcGxldCBtZW1vcnkgbGVha3MgKHJoICM0MzAxNzgpCi0gQXBwbGV0IENvbm5lY3Rpb24gSW5mb3JtYXRpb24gZGlhbG9nIHR3ZWFrcyAoZ25vbWUub3JnICM1MDU4OTkpCi0gRmlsdGVyIGlucHV0IGNoYXJhY3RlcnMgdG8gcGFzc3BocmFzZS9rZXkgZW50cnkgKGdub21lLm9yZyAjMzMyOTUxKQotIEZpeCBhcHBsZXQgZm9jdXMgc3RlYWxpbmcgcHJldmVudGlvbiBiZWhhdmlvcgAtIEFkZCBDRE1BIG1vYmlsZSBicm9hZGJhbmQgc3VwcG9ydCAoaWYgc3VwcG9ydGVkIGJ5IEhBTCkKLSBSZXdvcmsgYXBwbGV0IGNvbm5lY3Rpb24gYW5kIGljb24gaGFuZGxpbmcKLSBFbmFibGUgY29ubmVjdGlvbiBlZGl0b3IgKG9ubHkgZm9yIGRlbGV0aW5nIGNvbm5lY3Rpb25zKQAtIEZpeCBjcmFzaCB3aGVuIGFjdGl2YXRpbmcgYSBtb2JpbGUgYnJvYWRiYW5kIGNvbm5lY3Rpb24KLSBCZXR0ZXIgaGFuZGxpbmcgb2Ygbm9uLVNTSUQtYnJvYWRjYXN0aW5nIEFQcyBvbiBrZXJuZWxzIHRoYXQgc3VwcG9ydCBpdAogICAgKGdub21lLm9yZyAjNDY0MjE1KSAocmggIzM3Mzg0MSkKLSBIb25vciBESENQLXNlcnZlciBwcm92aWRlZCBNVFUgaWYgcHJlc2VudCAoZ25vbWUub3JnICMzMzI5NTMpCi0gVXNlIHByZXZpb3VzIEROUyBzZXR0aW5ncyBpZiB0aGUgVlBOIGNvbmNlbnRyYXRvciBkb2Vzbid0IHByb3ZpZGUgYW55CiAgICAoZ25vbWUub3JnICMzNDY4MzMpAC0gRml4IFdQQSBwYXNzcGhyYXNlIGhhc2hpbmcgb24gYmlnIGVuZGlhbiAoUFBDLCBTcGFyYywgZXRjKSAocmggIzQyNjIzMykALSBGaXhlcyB0byB3b3JrIGJldHRlciB3aXRoIG5ldyBsaWJubCAocmggIzQwMTc2MSkALSBGaXggV1BBL1dQQTIgRW50ZXJwcmlzZSBQaGFzZTIgY29ubmVjdGlvbnMgKHJoICMzODg0NzEpAC0gRml4IGFwcGxldCBjb25uZWN0aW9uIGNvbXBhcmlzb24gd2hpY2ggZmFpbGVkIHRvIHNlbmQgY29ubmVjdGlvbiB1cGRhdGVkCiAgICBzaWduYWxzIHRvIE5NIGluIHNvbWUgY2FzZXMKLSBNYWtlIFZQTiBjb25uZWN0aW9uIGFwcGxldCBtb3JlIHJvYnVzdCBhZ2FpbnN0IHBsdWdpbiBmYWlsdXJlcwAtIDY0LWJpdCAtV2FsbCBjb21waWxlIGZpeGVzAC0gRml4IGFwcGxldCBjcmFzaCB3aGVuIGNob29zaW5nIHRvIGlnbm9yZSB0aGUgQ0EgY2VydGlmaWNhdGUgKHJoICMzNTkwMDEpCi0gRml4IGFwcGxldCBjcmFzaCB3aGVuIGVkaXRpbmcgVlBOIHByb3BlcnRpZXMgYW5kIFZQTiBjb25uZWN0aW9uIGZhaWx1cmVzIChyaCAjNDA5MzUxKQotIEFkZCBmaWxlIGZpbHRlciBuYW1lIGluIGNlcnRpZmljYXRlIGZpbGUgcGlja2VyIGRpYWxvZyAocmggIzQxMDIwMSkKLSBObyBsb25nZXIgc3RhcnQgbmFtZWQgd2hlbiBzdGFydGluZyBOTSAocmggIzM4MTU3MSkALSBGaXggdXBncmFkaW5nIGZyb20gYW4gZWFybGllciByYXdoaWRlIHNuYXAALSBGaXggZGV2aWNlIGRlc2NyaXB0aW9ucyBzaG93biBpbiBhcHBsZXQgbWVudQAtIEZpeCBjcmFzaCB3aGVuIGRlYWN0aXZhdGluZyBWUE4gY29ubmVjdGlvbnMALSBGaXggY3Jhc2ggYW5kIHBvdGVudGlhbCBpbmZpbml0ZSBuYWcgZGlhbG9nIGxvb3Agd2hlbiBpZ25vcmluZyBDQSBjZXJ0aWZpY2F0ZXMALSBGaXggY3Jhc2ggd2hlbiBpZ25vcmluZyBDQSBjZXJ0aWZpY2F0ZSBmb3IgRUFQLVRMUywgRUFQLVRUTFMsIGFuZCBFQVAtUEVBUAAtIEZpeCBjb25uZWN0aW9ucyB3aGVuIHBpY2tpbmcgYSBXUEEgRW50ZXJwcmlzZSBBUCBmcm9tIHRoZSBtZW51Ci0gRml4IGlzc3VlIHdoZXJlIGFwcGxldCB3b3VsZCBwcm92aWRlIG11bHRpcGxlIHNhbWUgY29ubmVjdGlvbnMgdG8gTk0ALSBBZGQgc3VwcG9ydCBmb3IgRUFQLVBFQVAgKHJoICMzNjIyNTEpCi0gRml4IEVBUC1UTFMgcHJpdmF0ZSBrZXkgaGFuZGxpbmcALSBDbGFyaWZ5IG5hbWluZyBvZiBXUEEgJiBXUEEyIFBlcnNvbmFsIGVuY3J5cHRpb24gb3B0aW9ucyAocmggIzM3NDg2MSwgcmggIzM3MzgzMSkKLSBEb24ndCByZXF1aXJlIGEgQ0EgY2VydGlmaWNhdGUgZm9yIGFwcGxpY2FibGUgRUFQIG1ldGhvZHMgKHJoICMzNTkwMDEpCi0gRml4IGNlcnRpZmljYXRlIGFuZCBwcml2YXRlIGtleSBoYW5kbGluZyBmb3IgRUFQLVRUTFMgYW5kIEVBUC1UTFMgKHJoICMzMjMzNzEpCi0gRml4IGFwcGxldCBjcmFzaCB3aXRoIFVTQiBkZXZpY2VzIChyaCAjMzM3MTkxKQotIFN1cHBvcnQgdXBncmFkZXMgZnJvbSBOTSAwLjYueCBHQ29uZiBzZXR0aW5ncwAtIEZpeCBhcHBsZXQgY3Jhc2ggd2l0aCBVU0IgZGV2aWNlcyB0aGF0IGRvbid0IGFkdmVydGlzZSBhIHByb2R1Y3Qgb3IgdmVuZG9yCiAgICAocmggIzMzNzE5MSkALSBGaXggY3Jhc2ggd2hlbiBnZXR0aW5nIFdQQSBzZWNyZXRzIChyaCAjMzU1MDQxKQAtIEJyaW5nIHVwIGV0aGVybmV0IGRldmljZXMgYnkgZGVmYXVsdCBpZiBubyBjb25uZWN0aW9ucyBhcmUgZGVmaW5lZCAocmggIzMzOTIwMSkKLSBGaXggY3Jhc2ggd2hlbiBzd2l0Y2hpbmcgbmV0d29ya3Mgb3IgYnJpbmdpbmcgdXAgc2VjcmV0cyBkaWFsb2cgKHJoICMzNTMwOTEpCi0gRml4IGNyYXNoIHdoZW4gZWRpdGluZyBWUE4gY29ubmVjdGlvbiBwcm9wZXJ0aWVzIGEgc2Vjb25kIHRpbWUKLSBGaXggY3Jhc2ggd2hlbiBjYW5jZWxsaW5nIHRoZSBzZWNyZXRzIGRpYWxvZyBpZiBhbm90aGVyIGNvbm5lY3Rpb24gd2FzCiAgICBhY3RpdmF0ZWQgaW4gdGhlIG1lYW4gdGltZQotIEZpeCBkaXNlbWJvZGllZCBub3RpZmljYXRpb24gYnViYmxlcyAocmggIzMzMzM5MSkALSBIYW5kbGUgUEVNIGNlcnRpZmljYXRlcwotIEhpZGUgV1BBLVBTSyBUeXBlIGNvbWJvIHNpbmNlIGl0J3MgYXMgeWV0IHVudXNlZAotIEZpeCBhcHBsZXQgY3Jhc2ggd2hlbiBBUCBzZWN1cml0eSBvcHRpb25zIGNoYW5nZWQgYW5kIG9sZCBzZWNyZXRzIGFyZSBzdGlsbAogICAgaW4gdGhlIGtleXJpbmcKLSBGaXggYXBwbGV0IGNyYXNoIGNvbm5lY3RpbmcgdG8gdW5lbmNyeXB0ZWQgQVBzIHZpYSB0aGUgb3RoZXIgbmV0d29yayBkaWFsb2cALSBGaXggV1BBIEVudGVycHJpc2UgY29ubmVjdGlvbnMgdGhhdCB1c2UgY2VydGlmaWNhdGVzCi0gQmV0dGVyIGRpc3BsYXkgb2YgU1NJRHMgaW4gdGhlIG1lbnUALSBGaXggZ2V0dGluZyBjdXJyZW50IGFjY2VzcyBwb2ludAotIEZpeCBXUEEgRW50ZXJwcmlzZSBjb25uZWN0aW9ucwotIFdpcmVsZXNzIGRpYWxvZyBub3cgZGVmYXVsdHMgdG8gc2Vuc2libGUgY2hvaWNlcyBiYXNlZCBvbiB0aGUgY29ubmVjdGlvbgotIFRlbGwgbnNjZCB0byByZXN0YXJ0IGlmIG5lZWRlZCwgZG9uJ3Qgc2lsZW50bHkga2lsbCBpdAAtIFN1cHByZXNzIGV4Y2Vzc2l2ZSBHQ29uZiB1cGRhdGVzIHdoaWNoIHNvbWV0aW1lcyBjYXVzZWQgc2VjcmV0cyB0byBiZSBjbGVhcmVkCiAgICBhdCB0aGUgd3JvbmcgdGltZXMsIGNhdXNpbmcgY29ubmVjdGlvbnMgdG8gZmFpbAotIFZhcmlvdXMgRUFQIGFuZCBMRUFQIHJlbGF0ZWQgZml4ZXMALSBNYWtlIFdQQS1FQVAgYW5kIER5bmFtaWMgV0VQIG9wdGlvbnMgY29ubmVjdCBzdWNjZXNzZnVsbHkKLSBTdGF0aWMgSVBzIGFyZSBub3cgaGFuZGxlZCBjb3JyZWN0bHkgaW4gTk0gaXRzZWxmAC0gQWRkIER5bmFtaWMgV0VQIGFzIGEgc3VwcG9ydGVkIGF1dGhlbnRpY2F0aW9uL3NlY3VyaXR5IG9wdGlvbgAtIFJlLWVuYWJsZSAiQ29ubmVjdCB0byBvdGhlciBuZXR3b3JrIgotIFN3aXRjaCB0byBuZXcgR1VJIGJpdHMgZm9yIHdpcmVsZXNzIHNlY3VyaXR5IGNvbmZpZyBhbmQgcGFzc3dvcmQgZW50cnkALSBBZGQgcmZraWxsIGZ1bmN0aW9uYWxpdHkKLSBGaXggYXBwbGV0IGNyYXNoIHdoZW4gY2hvb3Npbmcgd2lyZWQgbmV0d29ya3MgZnJvbSB0aGUgbWVudQAtIEZpeCBzZWdmYXVsdCB3aXRoIGRlZmVycmVkIGNvbm5lY3Rpb25zCi0gRml4IGRlZmF1bHQgdXNlcm5hbWUgd2l0aCB2cG5jIFZQTiBwbHVnaW4KLSBIaWRkZW4gU1NJRCBmaXhlcwAtIEZpeCBtZXJnaW5nIG9mIG5vbi1TU0lELWJyb2FkY2FzdGluZyBBUHMgaW50byBhIGRldmljZSdzIHNjYW4gbGlzdAotIFNwZWVkIHVwIG9wZW5pbmcgb2YgdGhlIGFwcGxldCBtZW51AC0gTmV3IHNuYXBzaG90CgktIEFkZCB0aW1lc3RhbXBzIHRvIG5ldHdvcmtzIHRvIGNvbm5lY3QgdG8gbGFzdCB1c2VkIHdpcmVsZXNzIG5ldHdvcmsKCS0gVHVybiBhdXRvY29ubmVjdCBvbiBpbiB0aGUgYXBwbGV0CgktIEhpZGRlbiBTU0lEIHN1cHBvcnQKCS0gSW52YWxpZGF0ZSBmYWlsZWQgb3IgY2FuY2VsbGVkIGNvbm5lY3Rpb25zIGFnYWluCgktIEZpeCBpc3N1ZXMgd2l0aCByZWFjdGl2YXRpb24gb2YgdGhlIHNhbWUgZGV2aWNlCgktIEhhbmRsZSBjb25uZWN0aW9uIHVwZGF0ZXMgaW4gdGhlIGFwcGxldCAoZXguIGZpbmQgbmV3IFZQTiBjb25uZWN0aW9ucykKCS0gRml4IHZlcnRpY2FsIHNpemluZyBvZiBtZW51IGl0ZW1zCgktIEZpeCBBUCBsaXN0IG9uIHdpcmVsZXNzIGRldmljZXMgb3RoZXIgdGhhbiB0aGUgZmlyc3QgZGV2aWNlIGluIHRoZSBhcHBsZXQKCS0gRml4IG1hdGNoaW5nIG9mIGN1cnJlbnQgQVAgd2l0aCB0aGUgcmlnaHQgbWVudSBpdGVtAC0gTmV3IHNuYXBzaG90CgktIEFkZCBXUEEgcGFzc3BocmFzZSBzdXBwb3J0IHRvIHBhc3N3b3JkIGRpYWxvZwoJLSBBcHBsZXQgbm93IHJlZmxlY3RzIGFjdHVhbCBWUE4gYmVoYXZpb3Igb2Ygb25lIGFjdGl2ZSBjb25uZWN0aW9uCgktIEFwcGxldCBub3cgbm90aWNlcyBWUE4gYWN0aXZlIGNvbm5lY3Rpb25zIG9uIHN0YXJ0dXAKCS0gRml4IGNvbm5lY3Rpb25zIHdpdGggc29tZSBXUEEgYW5kIFdFUCBrZXlzAC0gTmV3IHNuYXBzaG90CgktIFZQTiBzdXBwb3J0IChvbmx5IHZwbmMgcGx1Z2luIHBvcnRlZCBhdCB0aGlzIHRpbWUpAC0gTmV3IHNuYXBzaG90CgktIE1ha2Ugd2lyZWQgZGV2aWNlIGNhcnJpZXIgc3RhdGUgd29yayBpbiB0aGUgYXBwbGV0CgktIEZpeCBoYW5kbGluZyBvZiBlcnJvcnMgd2l0aCB1bmVuY3J5cHRlZCBBUHMKCS0gRml4ICJmcm96ZW4iIGFwcGxldCBpY29uIGJ5IHJlcG9ydGluZyBOTSBzdGF0ZSBiZXR0ZXIKCS0gRml4IG91dHB1dCBvZiBBUCBmcmVxdWVuY3kgaW4gbm0tdG9vbAAtIE5ldyBzbmFwc2hvdAoJLSBGaXggYXBwbGV0IGljb24gc2l6aW5nIG9uIHN0YXJ0IChtY2xhc2VuKQoJLSBGaXggbm0tdG9vbCBpbnN0YWxsYXRpb24gKG1jbGFzZW4pCgktIEZpeCAnc3RhdGUnIG1ldGhvZCBjYWxsIHJldHVybiAoIzMwMzI3MSkKCS0gRml4IDQwLWJpdCBXRVAga2V5cyAoYWdhaW4pCgktIEZpeCBsb29wIHdoZW4gc2VjcmV0cyB3ZXJlIHdyb25nL2ludmFsaWQKCS0gRml4IGFwcGxldCBjcmFzaCB3aGVuIGNsaWNraW5nIENhbmNlbCBpbiB0aGUgcGFzc3dvcmQgZGlhbG9nCgktIEVuc3VyZSBOTSBkb2Vzbid0IGdldCBzdHVjayB3YWl0aW5nIGZvciB0aGUgc3VwcGxpY2FudCB0byByZS1hcHBlYXIKCQlpZiBpdCBjcmFzaGVzIG9yIGdvZXMgYXdheQoJLSBNYWtlIFZQTiBwcm9wZXJ0aWVzIGFwcGxldCB3b3JrIGFnYWluCgktIEluY3JlYXNlIHRpbWVvdXQgZm9yIG5ldHdvcmsgcGFzc3dvcmQgZW50cnkALSBOZXcgc25hcHNob3QgKGZpeCB1bmVuY3J5cHRlZCAmIDQwIGJpdCBXRVApAC0gTmV3IHNuYXBzaG90AC0gTmV3IHNuYXBzaG90AC0gTmV3IFNWTiBzbmFwc2hvdCBvZiAwLjcgdGhhdCBzdWNrcyBsZXNzAC0gVXBkYXRlIHRvIFNWTiBzbmFwc2hvdCBvZiAwLjcALSBVcGRhdGUgdGhlIGxpY2Vuc2UgdGFnAC0gT3duIC9ldGMvTmV0d29ya01hbmFnZXIvZGlzcGF0Y2hlci5kIGFuZCAvZXRjL05ldHdvcmtNYW5hZ2VyL1ZQTiAoIzIzNDAwNCkALSBGaXggV2lyZWxlc3MgRW5hYmxlZCBjaGVja2JveCB3aGVuIG5vIGtpbGxzd2l0Y2hlcyBhcmUgcHJlc2VudAAtIFVwZGF0ZSB0byBzdGFibGUgYnJhbmNoIHNuYXBzaG90OgogICAgLSBNb3JlIGZpeGVzIGZvciBldGhlcm5ldCBsaW5rIGRldGVjdGlvbiAoZ25vbWUgIzM1NDU2NSwgcmggIzE5NDEyNCkKICAgIC0gU3VwcG9ydCBmb3IgSEFMLWRldGVjdGVkIHJma2lsbCBzd2l0Y2hlcwAtIEZpeCBhcHBsZXQgY3Jhc2ggb24gNjQtYml0IHBsYXRmb3JtcyB3aGVuIGNob29zaW5nCiAgICAiQ29ubmVjdCB0byBvdGhlciB3aXJlbGVzcyBuZXR3b3JrLi4uIiAoZ25vbWUub3JnICM0MzUwMzYpCi0gQWRkIGRlYnVnIG91dHB1dCBmb3IgZXRoZXJuZXQgZGV2aWNlIGxpbmsgY2hhbmdlcwAtIEZpeCBldGhlcm5ldCBsaW5rIGRldGVjdGlvbiAoZ25vbWUgIzM1NDU2NSwgcmggIzE5NDEyNCkKLSBGaXggcGVycGV0dWFsIGNyZWRlbnRpYWxzIHJlcXVlc3Qgd2l0aCBwcml2YXRlIGtleSBwYXNzd29yZHMgaW4gdGhlIGFwcGxldAotIFNsZWVwIGEgYml0IGJlZm9yZSBhY3RpdmF0aW5nIHdpcmVsZXNzIGNhcmRzIHRvIHdvcmsgYXJvdW5kIGRyaXZlciBidWdzAC0gRG9uJ3Qgc3Bhd24gd3BhX3N1cHBsaWNhbnQgd2l0aCAtbwAtIEZpeCByZXF1aXJlcyBtYWNybyAoMjM3ODA2KQAtIFVwZGF0ZSB0byAwLjYuNSBmaW5hbAotIERvbid0IGxvc2Ugc2Nhbm5lZCBzZWN1cml0eSBpbmZvcm1hdGlvbgAtIFVwZGF0ZSBmcm9tIHRydW5rCgkqIFVwZGF0ZWQgdHJhbnNsYXRpb25zCgkqIENsZWFuZWQtdXAgVlBOIHByb3BlcnRpZXMgZGlhbG9ncwoJKiBGaXggNjQtYml0IGtlcm5lbCBsZWFrYWdlIGlzc3VlcyBpbiBXRVhUCgkqIERvbid0IGNhcHR1cmUgYW5kIHJlZGlyZWN0IHdwYV9zdXBwbGljYW50IGxvZyBvdXRwdXQALSBDbG9zZSBwcml2YXRlIEQtQnVzIGNvbm5lY3Rpb25zLiAoIzIzMjY5MSkALSBGaXggYSBkaXJlY3Rvcnkgb3duZXJzaGlwIGlzc3VlLiAgKCMyMzM3NjMpAC0gVXBkYXRlIHRvIHByZS0wLjYuNSBzbmFwc2hvdAAtIEd1YXJkIGFnYWluc3QgRC1CdXMgTGltaXRFeGNlZWRlZCBtZXNzYWdlcwAtIE1vdmUgLnNvIGZpbGUgdG8gLWRldmVsIHBhY2thZ2UALSBPd24gdGhlIC9ldGMvTmV0d29ya01hbmFnZXIvZGlzcGF0Y2hlci5kIGRpcmVjdG9yeQotIFJlcXVpcmUgcGtnY29uZmlnIGZvciB0aGUgLWRldmVsIHBhY2thZ2VzCi0gRml4IGNvbXBpbGF0aW9uIHdpdGggZGJ1cyAxLjAALSBVcGRhdGUgdG8gYSBzdGFibGUgYnJhbmNoIHNuYXBzaG90CiAgICAtIEdub21lIGFwcGxldCB0aW1lb3V0L3JlZHJhdyBzdXBwcmVzc2lvbiB3aGVuIGlkbGUKICAgIC0gQmFja3BvcnQgb2YgTEVBUCBwYXRjaCBmcm9tIEhFQUQgKGZyb20gVGhpYWdvIEJhdWVybWFubikKICAgIC0gQmFja3BvcnQgb2YgYXN5bmNocm9ub3VzIHNjYW5uaW5nIHBhdGNoIGZyb20gSEVBRAogICAgLSBNYWtlIHJlbmFtaW5nIG9mIFZQTiBjb25uZWN0aW9ucyB3b3JrIChmcm9tIFRhbWJldCBJbmdvKQogICAgLSBEaWFsIGRvd24gd3BhX3N1cHBsaWNhbnQgZGVidWcgc3BldwogICAgLSBDbGVhbnVwIG9mIGtleS9wYXNzcGhyYXNlIHJlcXVlc3Qgc2NlbmFyaW9zIChmcm9tIFZhbGVudGluZSBTaW5pdHN5bikKICAgIC0gU2h1dCBkb3duIFZQTiBjb25uZWN0aW9ucyBvbiBsb2dvdXQgKGZyb20gUm9iZXJ0IExvdmUpCiAgICAtIEZpeCBXUEEgcGFzc3BocmFzZSBoYXNoaW5nIG9uIFBQQwAtIE93biAvdXNyL3NoYXJlL05ldHdvcmtNYW5hZ2VyIGFuZCAvdXNyL2luY2x1ZGUvTmV0d29ya01hbmFnZXIALSBEb24ndCB3YWtlIHVwIHRvIHJlZHJhdyBpZiBOTSBpcyBpbmFjdGl2ZSAoIzIwNDg1MCkALSBhZGQgZXBvY2hzIGluIHJlcXVpcmVtZW50cwAtIEZpeCBGQy01IGJ1aWxkcmVxcwAtIFJldmVydCBGQzYgdG8gbGF0ZXN0IHN0YWJsZSBOTQotIFVwZGF0ZSB0byBzdGFibGUgc25hcHNob3QKLSBSZW1vdmUgYmluZC9jYWNoaW5nLW5hbWVzZXJ2ZXIgaGFyZCByZXF1aXJlbWVudAAtIEJ1aWxkUmVxdWlyZSB3aXJlbGVzcy10b29scy1kZXZlbCBhbmQgcGVybC1YTUwtUGFyc2VyCi0gVXBkYXRlIHRoZSBCdWlsZFJvb3QgdGFnAC0gYWRkIHBhdGNoIHRvIG1ha2UgbmV0d29ya21hbmFnZXIgbGVzcyB2ZXJib3NlIChidWcgMjAyODMyKQAtIGFjdHVhbGx5IG1ha2UgdGhlIHBhdGNoIGluIDAuNy4wLTAuY3ZzMjAwNjA1MjkuNCBhcHBseQAtIERvbid0IGV2ZXIgZWxlY3QgaW5hY3RpdmUgd2lyZWQgZGV2aWNlcyAoYnVnIDE5NDEyNCkuAC0gQWRkIHBhdGNoIHRvIGZpeCBkZXByZWNhdGVkIGRidXMgZnVuY3Rpb25zAC0gQWRkIEJSIGZvciBkYnVzLWdsaWItZGV2ZWwALSByZWJ1aWxkAC0gVXBkYXRlIHRvIGxhdGVzdCBDVlMKCW8gR25vbWUub3JnICMzMzM0MjA6IGRpYWxvZyBkbyBub3QgaGF2ZSB3aW5kb3cgaWNvbnMKCW8gR25vbWUub3JnICMzMzY5MTM6IEhJRyB0d2Vha3MgZm9yIHZwbiBwcm9wZXJ0aWVzIHBhZ2VzCglvIEdub21lLm9yZyAjMzM2ODQ2OiBISUcgdHdlYWtzIGZvciBubS12cG4tcHJvcGVydGllcwoJbyBHbm9tZS5vcmcgIzMzNjg0Nzogc29tZSBidWdzIGluIG5tLXZwbi1wcm9wZXJ0aWVzIGFyZ3MgcGFyc2luZwoJbyBHbm9tZS5vcmcgIzM0MTMwNjogbm0tdnBuLXByb3BlcnRpZXMgY3Jhc2hlcyBvbiBzdGFydHVwCglvIEdub21lLm9yZyAjMzQxMjYzOiBWZXJzaW9uIDAuNi4yLTB1YnVudHU1IGNyYXNoZXMgb24gbm1fZGV2aWNlXzgwMl8xMV93aXJlbGVzc19nZXRfdHlwZQoJbyBHbm9tZS5vcmcgIzM0MTI5NzogZGlzcGxheXMgcmVwZWF0ZWQga2V5cmluZyBkaWFsb2dzIG9uIHJlc3VtZSBmcm9tIHN1c3BlbmQKCW8gR25vbWUub3JnICMzNDI0MDA6IEJ1aWxkaW5nIGxpYm5tLXV0aWwgLS13aXRob3V0LWdjcnlwdCByZXN1bHRzIGluIGxpbmtlciBlcnJvcgoJbyBHbm9tZS5vcmcgIzM0MjM5ODogRWxlbWluYXRlIEdub21lIGRlcGVuZGVuY3kgZm9yIE5ldHdvcmtNYW5hZ2VyCglvIEdub21lLm9yZyAjMzM2NTMyOiBkZWNsYXJhdGlvbiBvZiAnbGluaycgc2hhZG93cyBhIGdsb2JhbCBkZWNsYXJhdGlvbgotIFNwZWNmaWxlIGZpeGVzICgjcmgxODc0ODkjKQAtIFVwZGF0ZSB0byBsYXRlc3QgQ1ZTCi0gRHJvcCBzcGVjaWFsLWNhc2UtbWFkd2lmaS5wYXRjaCwgc2luY2UgV0VYVCBjb2RlIGlzIGluIG1hZHdpZmktbmcgdHJ1bmsgbm93AC0gdXNlIHRoZSBzYW1lIDAuNi4yIHRhcmJhbGwgYXMgRkM1LCBzbyB3ZSBoYXZlIHRoZSBzYW1lIFZQTiBpbnRlcmZhY2UKICAoZGlkIGhlIGZpcmUgdGVuIGFyZ3MsIG9yIG9ubHkgbmluZT8pAC0gdXNlIHRoZSBoYWwgZGV2aWNlIHR5cGUgaW5zdGVhZCBvZiBwb2tpbmcgdmlhIGlvY3RsIHNvIHRoYXQgd2lyZWxlc3MKICBkZXZpY2VzIGFyZSBwcm9wZXJseSBkZXRlY3RlZCBldmVuIGlmIHRoZSBraWxsIHN3aXRjaCBoYXMgYmVlbiB1c2VkAC0gVXBkYXRlIHRvIDAuNi4yOgoJKiBGaXggdmFyaW91cyBXUEEtcmVsYXRlZCBidWdzCgkqIENsZWFuIHVwIGxlYWtzCgkqIEluY3JlYXNlZCBESENQIHRpbWVvdXQgdG8gYWNjb3VudCBmb3Igc2xvdyBESENQIHNlcnZlcnMsIG9yIFNUUC1lbmFibGVkCgkJc3dpdGNoZXMKCSogQWxsb3cgYXBwbGV0IHRvIHJlY29ubmVjdCBvbiBkYnVzIHJlc3RhcnRzCgkqIEFkZCAiRHluYW1pYyBXRVAiIHN1cHBvcnQKCSogQWxsb3cgaGlkaW5nIG9mIHBhc3N3b3JkL2tleSBlbnRyeSB0ZXh0CgkqIE1vcmUgcmVzcG9uc2l2ZSBjb25uZWN0aW9uIHN3aXRjaGluZwAtIEZpeCBkZXZpY2UgYnJpbmd1cCBvbiByZXN1bWUALSBEb24ndCBsZXQgd3BhX3N1cHBsaWNhbnQgcGVyZm9ybSBzY2FubmluZyB3aXRoIG5vbi1XUEEgZHJpdmVycwAtIFVwZGF0ZSB0byAwLjYuMCByZWxlYXNlCi0gTW92ZSBhdXRvc3RhcnQgZmlsZSB0byAvdXNyL3NoYXJlL2dub21lL2F1dG9zdGFydAAtIHVwZGF0ZWQgY3ZzIHNuYXBzaG90LiAgc2VlbXMgdG8gbWFrZSBhaXJvIG11Y2ggbGVzcyBuZXVyb3RpYwAtIE1vdmUgdGhlIHVudmVyc2lvbmVkIGxpYm5tX2dsaWIuc28gdG8gdGhlIC1kZXZlbCBwYWNrYWdlAC0gRml4IFZQTi1yZWxhdGVkIGNyYXNoCi0gRml4IGlzc3VlIHdoZXJlIE5NIHdvdWxkIHJlZnVzZSB0byBhY3RpdmF0ZSBhIFZQTiBjb25uZWN0aW9uIG9uY2UgaXQgaGFkIHRpbWVkIG91dAotIExvZyB3cGFfc3VwcGxpY2FudCBvdXRwdXQgZm9yIGJldHRlciBkZWJ1Z2dpbmcALSBUd2VhayB0aHJlZS1zY2FuLXBydW5lLnBhdGNoAC0gRG9uJ3QgcHJ1bmUgbmV0d29ya3MgdW50aWwgdGhleSd2ZSBnb25lIE1JQSBmb3IgdGhyZWUgc2NhbnMsIG5vdCBvbmUuAC0gVXBkYXRlIHNuYXBzaG90LCB3aGljaCBmaXhlcyB1cCB0aGUgbGlibm90aWZ5IHN0dWZmLgAtIE1vdmUgbGlibm90aWZ5IHJlcXVpcmVzIHRvIE5ldHdvcmtNYW5hZ2VyLWdub21lLCBub3QgY29yZSBOTSBwYWNrYWdlAC0gQWRkIEJ1aWxkUmVxdWlyZXM6IGxpYm5sLWRldmVsICgjcmgxNzk0MzgjKQotIEZpeCBsaWJubV9nbGliIHRvIG5vdCBjbG9iYmVyIGFuIGFwcGxpY2F0aW9uJ3MgZXhpc3RpbmcgZGJ1cyBjb25uZWN0aW9uCgkoI3JoMTc3NTQ2IywgZ25vbWUub3JnICMzMjY1NzIpCi0gbGlibm90aWZ5IHN1cHBvcnQKLSBBUCBjb21wYXRpYmlsaXR5IGZpeGVzAC0gTWlub3IgYnVnIGZpeGVzCi0gVXBkYXRlIHRvIFZQTiBkYnVzIEFQSSBmb3IgcGFzc2luZyB1c2VyLWRlZmluZWQgcm91dGVzIHRvIHZwbiBzZXJ2aWNlAC0gUmVidWlsZAAtIHJlYnVpbHQgZm9yIG5ldyBnY2M0LjEgc25hcHNob3QgYW5kIGdsaWJjIGNoYW5nZXMALSBXb3JrYXJvdW5kcyBmb3IgbWFkd2lmaS9BdGhlcm9zIGNhcmRzCi0gRG8gYmV0dGVyIHdpdGggbm9uLVNTSUQtYnJvYWRjYXN0aW5nIGFjY2VzcyBwb2ludHMKLSBGaXggaGFuZ3Mgd2hlbiBhY2Nlc3MgcG9pbnRzIGNoYW5nZSBzZXR0aW5ncwAtIE93biAvdmFyL3J1bi9OZXR3b3JrTWFuYWdlciwgZml4IFNFTGludXggaXNzdWVzAC0gU3dpdGNoIHRvIGF1dG9zdGFydGluZyB0aGUgYXBwbGV0IGluc3RlYWQgb2YgaGF2aW5nIGl0IGJlIHNlc3Npb24tbWFuYWdlZAotIFdvcmsgYmV0dGVyIHdpdGggbm9uLWJyb2FkY2FzdGluZyBhY2Nlc3MgcG9pbnRzCi0gQWRkIG1vcmUgbWFudWZhY3R1cmVyIGRlZmF1bHQgU1NJRHMgdG8gdGhlIGJsYWNrbGlzdAAtIExvbmdlciBhc3NvY2lhdGlvbiB0aW1lb3V0Ci0gRml4IHNvbWUgU0VMaW51eCBpc3N1ZXMKLSBHZW5lcmFsIGJ1ZyBhbmQgY29zbWV0aWMgZml4ZXMALSBTbmFwc2hvdCBmcm9tIENWUwotIFdQQSBTdXBwb3J0ISAgV29vaG9vIQAtIHJlYnVpbHQALSByZWJ1aWxkIGZvciBuZXcgZGJ1cwAtIERvbid0IGtpbGwgdGhlIG5ldHdvcmsgY29ubmVjdGlvbiB3aGVuIHlvdSB1cGdyYWRlIHRoZSBwYWNrYWdlLgAtIFNwbGl0IG91dCB0aGUgLWdsaWIgc3VicGFja2FnZSB0byBoYXZlIGEgLWdsaWItZGV2ZWwgcGFja2FnZSBhcyB3ZWxsCi0gQWRkIGVwb2NoIHRvIHZlcnNpb24gcmVxdWlyZW1lbnRzIGZvciBiaW5kIGFuZCB3aXJlbGVzcy10b29scwotIFVwZGF0ZSBVUkwgb2YgcHJvamVjdAAtIE5ldHdvcmtNYW5hZ2VyIDAuNS4xAC0gTmV0d29ya01hbmFnZXIgMC41LjAALSBGaXggYXV0b21hdGljIHdpcmVsZXNzIGNvbm5lY3Rpb25zCi0gUmVtb3ZlIHVzYWdlIG9mIE5NTG9hZE1vZHVsZXMgY2FsbG91dCwgbm8gbG9uZ2VyIG5lZWRlZAotIFRyeSB0byBmaXggZGVhZGxvY2sgd2hlbiBtZW51IGlzIGRvd24gYW5kIGtleXJpbmcgZGlhbG9nIHBvcHMgdXAALSBVcGRhdGUgdG8gbGF0ZXN0IENWUwoJbyBJbnRlZ3JhdGUgY29ubmVjdGlvbiBwcm9ncmVzcyB3aXRoIGFwcGxldCBpY29uIChDaHJpcyBBaWxsb24pCglvIE1vcmUgaW5mb3JtYXRpb24gaW4gIkNvbm5lY3Rpb24gSW5mb3JtYXRpb24iIGRpYWxvZyAoUm9iZXJ0IExvdmUpCglvIFNob3J0ZW4gdGltZSB0YWtlbiB0byBzbGVlcAoJbyBNYWtlIGFwcGxldCBpY29uIHdpcmVsZXNzIHN0cmVuZ3RoIGxldmVscyBhIGJpdCBtb3JlIHJlYWxpc3RpYwoJbyBUYWxrIHRvIG5hbWVkIHVzaW5nIERCVVMgcmF0aGVyIHRoYW4gc3Bhd25pbmcgb3VyIG93bgoJCS0gWW91IG5lZWQgdG8gYWRkICItRCIgdG8gdGhlIE9QVElPTlMgbGluZSBpbiAvZXRjL3N5c2NvbmZpZy9uYW1lZAoJCS0gWW91IG5lZWQgdG8gc2V0IG5hbWVkIHRvIHN0YXJ0IGFzIGEgc2VydmljZSBvbiBzdGFydHVwAC0gVXBkYXRlIHRvIGN1cnJlbnQgQ1ZTIHRvIGZpeCBpc3N1ZXMgd2l0aCByb3V0aW5nIHRhYmxlIGFuZCAvc2Jpbi9pcAAtIHVwZGF0ZSB0byBjdXJyZW50IENWUyBhbmQgcmVidWlsZCAod29ya2Fyb3VuZCBmb3IgIzE2ODEyMCkALSBGaXggb2NjYXNpb25hbCBoYW5nIGluIE5NIGNhdXNlZCBieSB0aGUgYXBwbGV0AC0gVXBkYXRlIHRvIE5ldHdvcmtNYW5hZ2VyIDAuNC4xAC0gUmVidWlsZCBhZ2FpbnN0IG5ldyBjYWlyby9ndGsALSBVcGRhdGUgdG8gbGF0ZXN0IENWUwoJbyBVc2UgREhDUCBzZXJ2ZXIgYWRkcmVzcyBhcyBnYXRld2F5IGFkZHJlc3MgaWYgdGhlIERIQ1Agc2VydmVyIGRvZXNuJ3QgZ2l2ZQoJCXVzIGEgZ2F0ZXdheSBhZGRyZXNzICNyaDE2NTY5OCMKCW8gRml4ZXMgdG8gdGhlIGFwcGxldCAoUm9iZXJ0IExvdmUpCglvIEJldHRlciBjYWNoaW5nIG9mIGluZm9ybWF0aW9uIGluIHRoZSBhcHBsZXQgKEJpbGwgTW9zcykKCW8gR2VuZXJhdGUgYXV0b21hdGljIHN1Z2dlc3RlZCBBZC1Ib2MgbmV0d29yayBuYW1lIGZyb20gbWFjaGluZSdzIGhvc3RuYW1lCgkJKFJvYmVydCBMb3ZlKQoJbyBVcGRhdGUgYWxsIG5ldHdvcmsgaW5mb3JtYXRpb24gb24gc3VjY2Vzc2Z1bGwgY29ubmVjdCwgbm90IGp1c3QKCQlhdXRoZW50aWNhdGlvbiBtZXRob2QALSBVcGRhdGUgdG8gbGF0ZXN0IENWUyB0byBnZXQgZml4IGZvciBidWcgMTY1NjgzLgAtIE1vdmUgcGtnY29uZmlnIGZpbGUgdG8gZGV2ZWwgcGFja2FnZSAoIzE2MjMxNiwgdGhhbmtzIHRvIE1pY2hhZWwgU2Nod2VuZHQpAC0gVXBkYXRlIHRvIGxhdGVzdCBDVlMgdG8gZ2V0IGxhdGVzdCBWUE4gaW50ZXJmYWNlIHNldHRpbmdzIHRvIHNhdGlzZnkKICBCdWlsZFJlcSBmb3IgTmV0d29ya01hbmFnZXItdnBuYyBpbiBGZWRvcmEgRXh0cmFzIERldmVsb3BtZW50Ci0gTGF0ZXN0IENWUyBhbHNvIGNvbnRhaW5zIHZhcmlvdXMgYnVnLSBhbmQgVUktZml4ZXMALSBVcGRhdGUgdG8gbGF0ZXN0IENWUwoJbyBWUE4gY29ubmVjdGlvbiBpbXBvcnQvZXhwb3J0IGNhcGFiaWxpdHkKCW8gRml4IHVwIHNvbWUgbWVudSBpdGVtIG5hbWVzCi0gTW92ZSBubS12cG4tcHJvcGVydGllcy5nbGFkZSB0byB0aGUgZ25vbWUgc3VicGFja2FnZQAtIFVwZGF0ZSB0byBsYXRlc3QgQ1ZTCglvIENsZWFuIHVwIHdvcmRpbmcgaW4gV2lyZWxlc3MgTmV0d29yayBEaXNjb3ZlcnkgbWVudQoJbyBSb2JlcnQgTG92ZSdzIGFwcGxldCBiZWF1dGlmeSBwYXRjaAAtIFVwZGF0ZSB0byBsYXRlc3QgQ1ZTAC0gRml4IGRpc3BhdGNoZXIgYW5kIGFwcGxldCBDRkxBR1Mgc28gdGhleSBnZXRzIGNvbXBpbGVkIHdpdGggRk9SVElGWV9TT1VSQ0UALSBGaXggc2VnZmF1bHQgaW4gTmV0d29ya01hbmFnZXJEaXNwYXRjaGVyLCBhZGQgYW4gaW5pdHNjcmlwdCBmb3IgaXQALSBGaXggY29uZGl0aW9uIHRoYXQgbWF5IGhhdmUgcmVzdWx0ZWQgaW4gREhDUCBjbGllbnQgcmV0dXJuaW5nIHN1Y2Nlc3MKCXdoZW4gaXQgcmVhbGx5IHRpbWVkIG91dAAtIEVuYWJsZSBPSyBidXR0b24gY29ycmVjdGx5IGluIFBhc3NwaHJhc2UgYW5kIE90aGVyIE5ldHdvcmtzIGRpYWxvZ3Mgd2hlbgoJdXNpbmcgQVNDSUkgb3IgSGV4IFdFUCBrZXlzAC0gI3JoMTU0MzkxIyBOZXR3b3JrTWFuYWdlciBkaWVzIG9uIHN0YXJ0dXAgKGRvbid0IGZvcmNlLWtpbGwgbmlmZCkALSBGaXggbGVhayBvZiBhIHNvY2tldCBpbiBESENQIGNvZGUALSBGaXggc29tZSBtZW1vcnkgbGVha3MgKFRvbSBQYXJrZXIpCi0gSm9pbiB0byB0aHJlYWRzIHJhdGhlciB0aGFuIHNwaW5uaW5nIGZvciB0aGVpciBjb21wbGV0aW9uIChUb20gUGFya2VyKQotIEZpeCBtaXN1c2Ugb2YgYSBnX2Fzc2VydCgpIChDb2xpbiBXYWx0ZXJzKQotIEZpeCByZXR1cm4gY2hlY2tpbmcgb2YgYW4gaW9jdGwoKSAoQmlsbCBNb3NzKQotIEJldHRlciBkZXRlY3Rpb24gYW5kIG1hdGNoaW5nIG9mIGhpZGRlbiBhY2Nlc3MgcG9pbnRzIChCaWxsIE1vc3MpCi0gRG9uJ3QgdXNlIHZhcmFyZ3MsIGFuZCB0aGVyZWZvcmUgZG9uJ3QgY3Jhc2ggb24gUFBDIChQZXRlciBKb25lcykALSBmaXggYnVpbGQgd2l0aCBuZXdlciBkYnVzAC0gc2lsZW5jZSAlcG9zdAAtICNyaDE1MzIzNCMgTmV0d29ya01hbmFnZXIgcXVpdHMvY29yZXMganVzdCBhcyBhIGNvbm5lY3Rpb24gaXMgbWFkZQAtIFVwZGF0ZSBmcm9tIGxhdGVzdCBDVlMgSEVBRAAtIFVwZGF0ZSB0aGUgR1RLKyB0aGVtZSBpY29uIGNhY2hlIG9uICh1bilpbnN0YWxsAC0gUHVsbCBmcm9tIGxhdGVzdCBDVlMgSEVBRAAtIFVwbG9hZCBuZXcgc291cmNlIHRhcmJhbGwgKHdvb3BzKQAtIFB1bGwgZnJvbSBsYXRlc3QgQ1ZTIEhFQUQgKGhvcGVmdWxseSB3b3JrcyBhZ2FpbikALSBQdWxsIGZyb20gbGF0ZXN0IENWUyBIRUFECi0gQ29tbWl0IGJyb2tlbiBOZXR3b3JrTWFuYWdlciB0byBzYXRpc2Z5IHRvIGRidXMgZGVwZW5kZW5jeQAtIFB1bGwgZnJvbSBsYXRlc3QgQ1ZTIEhFQUQKLSBSZWJ1aWxkIGZvciBnY2MgNC4wAC0gVXBkYXRlIGZyb20gQ1ZTAC0gRml4IGZyZWUgb2YgaW52YWxpZCBwb2ludGVyIGZvciBtdWx0aXBsZSBzZWFyY2ggZG9tYWlucwAtIE5ldmVyIGF1dG9tYXRpY2FsbHkgY2hvb3NlIGEgZGV2aWNlIHRoYXQgZG9lc24ndCBzdXBwb3J0IGNhcnJpZXIgZGV0ZWN0aW9uCi0gQWRkIHJpZ2h0LWNsaWNrIG1lbnUgdG8gYXBwbGV0LCBjYW4gbm93ICJQYXVzZS9SZXN1bWUiIHNjYW5uaW5nIHRocm91Z2ggaXQKLSBGaXggREhDUCBSZW5ldy9SZWJpbmQgdGltZW91dHMKLSBGaXggZnJlcXVlbmN5IGN5Y2xpbmcgcHJvYmxlbSBvbiBzb21lIGNhcmRzLCBldmVuIHdoZW4gc2Nhbm5pbmcgd2FzIG9mZgotIFBsYXkgYmV0dGVyIHdpdGggSVB2NgotIERvbid0IHNlbmQga2VybmVsIHZlcnNpb24gaW4gREhDUCBwYWNrZXRzLCBhbmQgZW5zdXJlIERIQ1AgcGFja2V0cyBhcmUgYXQKCWxlYXN0IDMwMCBieXRlcyBpbiBsZW5ndGggdG8gd29yayBhcm91bmQgYnJva2VuIHJvdXRlcgotIE5ldyBESENQIG9wdGlvbnMgRC1CVVMgQVBJIGJ5IERhbiBSZWVkCi0gSGFuZGxlIG11bHRpcGxlIGRvbWFpbiBzZWFyY2ggb3B0aW9ucyBpbiBESENQIHJlc3BvbnNlcwAtIERpc3BsYXkgd2lyZWxlc3MgbmV0d29yayBuYW1lIGluIGFwcGxldCB0b29sdGlwCi0gSG9wZWZ1bGx5IGZpeCBkb3VibGUtZGVmYXVsdC1yb3V0ZSBwcm9ibGVtCi0gV3JpdGUgb3V0IHZhbGlkIHJlc29sdi5jb25mIHdoZW4gd2UgZXhpdAotIE1ha2UgbXVsdGktZG9tYWluIHNlYXJjaCBvcHRpb25zIHdvcmsKLSBSZXdvcmsgc2lnbmFsIHN0cmVuZ3RoIGNvZGUgdG8gYmUgV0VYVCBjb25mb3JtYW50LCBpZiBzdHJlbmd0aCBpcwoJc3RpbGwgd2llcmQgdGhlbiBpdHMgOTUlIHN1cmVseSBhIGRyaXZlciBwcm9ibGVtCi0gRml4IGFubm95aW5nIGluc3RhbmNlcyBvZiBzdWRkZW5seSBkcm9wcGluZyBhbmQgcmVhY3RpdmF0aW5nIGEKCXdpcmVsZXNzIGRldmljZSAoQ2lzY28gY2FyZHMgd2VyZSB3b3JzdCBvZmZlbmRlcnMgaGVyZSkKLSBGaXggc29tZSBpbnN0YW5jZXMgb2YgTmV0d29ya01hbmFnZXIgbm90IHJlbWVtYmVyaW5nIHlvdXIgV0VQIGtleQotIEZpeCBzb21lIHJhY2VzIGJldHdlZW4gTmV0d29ya01hbmFnZXIgYW5kIE5ldHdvcmtNYW5hZ2VySW5mbyB3aGVyZQoJTmV0d29ya01hbmFnZXIgd291bGRuJ3QgcmVjb2duaXplIGNoYW5nZXMgaW4gdGhlIGFsbG93ZWQgbGlzdAotIERvbid0IHNob3ZlIEFkLUhvYyBBY2Nlc3MgUG9pbnQgTUFDIGFkZHJlc3NlcyBpbnRvIEdDb25mAC0gUGxheSBuaWNlIHdpdGggZGJ1cyAwLjIzCi0gVXBkYXRlIG91ciBsaXN0IG9mIEFsbG93ZWQgV2lyZWxlc3MgTmV0d29ya3MgbW9yZSBxdWlja2x5AC0gVXBkYXRlIHRvIGxhdGVzdCBDVlMKLSBNYWtlIHN1cmUgd2Ugc3RhcnQgYXMgbGF0ZSBhcyBwb3NzaWJsZSBzbyB0aGF0IHdlIGVuc3VyZSBkYnVzICYgSEFMCglhcmUgYWxyZWFkeSBhcm91bmQKLSBGaXggcmFjZSBpbiBpbml0aWFsIGRldmljZSBhY3RpdmF0aW9uAC0gcmVidWlsdCBhZ2FpbnN0IG5ldyB3aXJlbGVzcyB0b29sAC0gRml4IGlzc3VlIHdoZXJlIE5NIHdvdWxkbid0IHJlY29nbml6ZSB0aGF0IGFjY2VzcyBwb2ludHMgd2VyZQoJZW5jcnlwdGVkLCBhbmQgdGhlbiB3b3VsZCB0cnkgdG8gY29ubmVjdCB3aXRob3V0IGVuY3J5cHRpb24KLSBSZWZpbmUgcGFja2FnaW5nIHRvIHB1dCBjbGllbnQgbGlicmFyeSBpbiBzZXBhcmF0ZSBwYWNrYWdlCi0gUmVtb3ZlIGJpbmQrY2FjaGluZy1uYW1lc2VydmVyIGRlcCBmb3IgRkMtMywgdXNlICduc2NkIC1pIGhvc3RzJwoJaW5zdGVhZC4gIEROUyBxdWVyaWVzIG1heSB0aW1lb3V0IG5vdyByaWdodCBhZnRlciBkZXZpY2UKCWFjdGl2YXRpb24gZHVlIHRvIHRoaXMgY2hhbmdlLgAtIFVwZGF0ZSB0byBsYXRlc3QgQ1ZTCi0gRml4ZXMgdG8gREhDUCBjb2RlCi0gTGluay1Mb2NhbCAoWmVyb0NvbmYvUmVuZGV6dm91cykgc3VwcG9ydAotIFVzZSBiaW5kIGluICJjYWNoaW5nLW5hbWVzZXJ2ZXIiIG1vZGUgdG8gd29yayBhcm91bmQgc3R1cGlkaXR5CglpbiBnbGliYydzIHJlc29sdmVyIGxpYnJhcnkgbm90IHJlY29nbml6aW5nIHJlc29sdi5jb25mIGNoYW5nZXMKLSAjcmgxNDQ4MTgjIENsZWFuIHVwIHRoZSBzcGVjZmlsZSAoUGF0Y2ggZnJvbSBNYXR0aGlhcyBTYW91KQotIEFkLUhvYyBtb2RlIHN1cHBvcnQgd2l0aCBMaW5rLUxvY2FsIGFkZHJlc3Npbmcgb25seSAoZm9yIG5vdykKLSBGaXhlcyBmb3IgZGV2aWNlIGFjdGl2YXRpb24gcmFjZSBjb25kaXRpb25zCi0gV2lyZWxlc3Mgc2Nhbm5pbmcgaW4gc2VwYXJhdGUgdGhyZWFkAC0gVXBkYXRlIHRvIENWUwotIFVwZGF0ZXMgdG8gbGluayBkZXRlY3Rpb24sIERIQ1AgY29kZQotIFJlbW92ZSBOTUxhdW5jaEhlbHBlciBzbyB3ZSBzdGFydCB1cCBmYXN0ZXIgYW5kIGRvbid0CglibG9jayBmb3IgYSBjb25uZWN0aW9uLiAgVGhpcyBtZWFucyBzZXJ2aWNlcyB0aGF0IGRlcGVuZAoJb24gdGhlIG5ldHdvcmsgbWF5IGZhaWwgaWYgdGhleSBzdGFydCByaWdodCBhZnRlciBOTQotIE1ha2Ugc3VyZSBESENQIHJlbmV3L3JlYmluZGluZyB3b3JrcwAtIFVwZGF0ZSB0byBDVlMKLSBGaXhlcyB0byBsaW5rIGRldGVjdGlvbgotIEJldHRlciBkZXRlY3Rpb24gb2Ygbm9uLUVTU0lELWJyb2FkY2FzdGluZyBhY2Nlc3MgcG9pbnRzCi0gRG9uJ3QgZGlhbG9nLXNwYW0gdGhlIHVzZXIgaWYgYSBjb25uZWN0aW9uIGZhaWxzAC0gVXBkYXRlIHRvIENWUwotIE11Y2ggYmV0dGVyIGxpbmsgZGV0ZWN0aW9uLCB3b3JrcyB3aXRoIE9wZW4gU3lzdGVtIGF1dGhlbnRpY2F0aW9uCi0gQmxhY2tsaXN0IHdpcmVsZXNzIGNhcmRzIHJhdGhlciB0aGFuIHdoaXRlbGlzdGluZyB0aGVtAC0gI3JoMTM0ODkzIyBOZXR3b3JrTWFuYWdlckluZm8gYW5kIHRoZSBwYW5lbC1pY29uIGxpZmUtY3ljbGUKLSAjcmgxMzQ4OTUjIFN0YXR1cyBpY29uIHNob3VsZCBoaWRlIHdoZW4gaW4gV2lyZWQtb25seSBtb2RlCi0gI3JoMTM0ODk2IyBJY29uIGNvZGUgbmVlZHMgcmV3cml0ZQotICNyaDEzNDg5NyMgIk90aGVyIE5ldHdvcmtzLi4uIiBkaWFsb2cgbmVlZHMgaW1wbGVtZW50aW5nCi0gI3JoMTM1MDU1IyBNZW51IGhpZ2hsaWdodHMgaW5jb3JyZWN0bHkgaW4gTk0KLSAjcmgxMzU2NDgjIHNlZ2ZhdWx0IHdpdGggY2lwc2VjMAotICNyaDEzNTcyMiMgTmV0d29ya01hbmFnZXIgd2lsbCBub3QgYWxsb3cgemF1cnVzIHRvIHN5bmMgdmlhIHVzYjAKLSAjcmgxMzU5OTkjIE5ldHdvcmtNYW5hZ2VyLTAuMy4xIHdpbGwgbm90IGNvbm5lY3QgdG8gMTI4IHdlcAotICNyaDEzNjg2NiMgYXBwbGV0IG5lZWRzIHRvb2x0aXBzCi0gI3JoMTM3MDQ3IyBsb3RzIG9mIGFwcGxldHMsIHlheSEKLSAjcmgxMzczNDEjIE5ldHdvcmsgTWFuYWdlciBkaWVzIGFmdGVyIGRpc2Nvbm5lY3RpbmcgZnJvbSB3aXJlZCBuZXR3b3JrIHNlY29uZCB0aW1lCi0gQmV0dGVyIGNoZWNraW5nIGZvciB3aXJlbGVzcyBkZXZpY2VzCi0gRml4IHNvbWUgbWVtbGVha3MKLSBGaXggaXNzdWVzIHdpdGggZGhjbGllbnQgZGVjbGluaW5nIGFuIG9mZmVyZWQgYWRkcmVzcwotIEZpeCBhbiBhY3RpdmF0aW9uIHRocmVhZCBkZWFkbG9jawotIE1vcmUgYWNjdXJhdGVseSBkZXRlY3QgIk90aGVyIHdpcmVsZXNzIG5ldHdvcmtzIiB0aGF0IGFyZSBlbmNyeXB0ZWQKLSBEb24ndCBicmluZyBkZXZpY2VzIGRvd24gYXMgbXVjaCwgd29uJ3QgaG90cGx1Zy1zcGFtIGFzIG11Y2ggYW55bW9yZQoJYWJvdXQgZmlybXdhcmUKLSBBZGQgYSAibmV0d29yayBub3QgZm91bmQiIGRpYWxvZyB3aGVuIHRoZSB1c2VyIGNob29zZXMgYSBuZXR3b3JrIHRoYXQgY291bGQKCW5vdCBiZSBjb25uZWN0ZWQgdG8ALSBGaXggZXNjYXBpbmcgb2YgRVNTSURzIGluIGdjb25mAC0gbWlub3IgcG9pbnQgcmVsZWFzZSB0byBpbXByb3ZlIGVycm9yIGhhbmRsaW5nIGFuZCB0cmFuc2xhdGlvbnMALSBVcGRhdGUgZnJvbSBDVlMsIHZlcnNpb24gMC4zAC0gVXBkYXRlIGZyb20gQ1ZTCi0gSW1wcm92ZW1lbnRzOgoJbyBCZXR0ZXIgbGluayBjaGVja2luZyBvbiB3aXJlbGVzcyBjYXJkcwoJbyBQYW5lbCBhcHBsZXQgbm93IGEgTm90aWZpY2F0aW9uIEFyZWEgaWNvbgoJbyBTdGF0aWMgSVAgY29uZmlndXJhdGlvbiBzdXBwb3J0AC0gVXBkYXRlIGZyb20gQ1ZTAC0gUmVxdWlyZSBnbm9tZS1wYW5lbCwgbm90IGdub21lLXBhbmVsLWRldmVsCi0gVHVybiBvZmYgYnkgZGVmYXVsdAAtIFVwZGF0ZSB0byAwLjIALSBzcGVjLWNoYW5nZXMgdG8gcmVxIGdsaWIyIGluc3RlYWQgb2YgZ2xpYgAtIEZpcnN0IHB1YmxpYyByZWxlYXNlAE5ldHdvcmtNYW5hZ2VyAE5ldHdvcmtNYW5hZ2VyLWJ0AAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAIAAAADAAAABAAAAAAAAAAIAAAACAAAgAAxOjEuMjYuMC04LmVsOAAxOjEuMjYuMC04LmVsOAAAAAAAAAIAAAAAMTowLjkuOS45NS0xAAAAAAAAAAAAAAABAAAAAgAAAAMuYnVpbGQtaWQAODcAMDI5MjViZTRmMDczN2VjODczOWYyMmZkZDQ3Y2VmZjVjMjgyMDAAbGlibm0tZGV2aWNlLXBsdWdpbi1ibHVldG9vdGguc28AL3Vzci9saWIvAC91c3IvbGliLy5idWlsZC1pZC8AL3Vzci9saWIvLmJ1aWxkLWlkLzg3LwAvdXNyL2xpYjY0L05ldHdvcmtNYW5hZ2VyLzEuMjYuMC04LmVsOC8ALU8yIC1nIC1waXBlIC1XYWxsIC1XZXJyb3I9Zm9ybWF0LXNlY3VyaXR5IC1XcCwtRF9GT1JUSUZZX1NPVVJDRT0yIC1XcCwtRF9HTElC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"},"publicKey":{"content":"LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdudVBHIHYyLjAuMjIgKEdOVS9MaW51eCkKCm1RSU5CRnpNV3hrQkVBREhyc2twQmdOOU9waG1oUmtjN1AvWXJzQUdTdnZsN2tmdStlOUtBYVU2ZjVNZUFWeW4KcklvTTQzc3l5R2tnRnlXZ2paTTgvcnVyN0VNUFkyeXQrMnEvMVpmTFZDUm45ODU2SnFUSXEwWFJwRFVlNG5LUQo4QmxBN3dEVlpvU0R4VVprU3VUSXlFeGJEZjBjcHc4OVRjZjYyTXhtaThqaDc0dlJsUHkxUGdqV0w1NDk0YjNYCjVmeERpZEg0YnFQWnl4VEJxUHJVRnVvK0VmVVZFcWlHRjk0UHBxNlpVdnJCR09WbzFWMStJZm05Q0dFSzU5N2MKYWV2Y0djMVJGbGd4SWdOODRVcHVEalBSOS96U25kd0o3WHNYWXZaNkhYY0tHYWdSS3NmWURXR1BrQTVjT0wvZQpmK3lPYk9uQzQzeVBVdnBnZ1E0S2FOSjYrU01UWk9LaWtNOHljaXlCd0xxd3JqbzhGbEpna3Y4VmZhZy8yVVI3CkpJTmJ5cUhIb0xVaFEybTZIWFN3SzRZanR3aWRGOUVVa2FCWldycnNrWVIzSVJaTFhsV3FlT2kvK2V6WU9XMG0KdnVmcmtjdnNoK1RLbFZWbnV3bUVQako4bXdVU3BzTGRmUEpvMURIc2Q4RlMwM1NDS1BhWEZkRDdlUGZFamlZawpuSHBRYUtFMDFhV1ZTTFVpeWduN0Y3clllbUdxVjlWdDd0Qnc1cHowdnFTQzcyYTVFM3pGeklJdUh4NmFBTnJ5CkdhdDNhcVUzcXRCWE9yQS9kUGtYOWNXRStVUjV3by9BMlVkS0paTGxHaE0yV1JKM2x0bUdUNDhWOUNlUzZOOVkKbTRDS2R6dmc3RVdqbFRsRnJkLzhXSjJLb3FPRTlsZURQZVhSUG5jdWJKZko2TExJSHlHMDloOWtLUUFSQVFBQgp0RHBEWlc1MFQxTWdLRU5sYm5SUFV5QlBabVpwWTJsaGJDQlRhV2R1YVc1bklFdGxlU2tnUEhObFkzVnlhWFI1ClFHTmxiblJ2Y3k1dmNtYytpUUkzQkJNQkFnQWhCUUpjekZzWkFoc0RCZ3NKQ0FjREFnWVZDQUlKQ2dzREZnSUIKQWg0QkFoZUFBQW9KRUFXMVZiT0VnOFpkak9zUC8yeWdTeEg5anFmZk9VOVNLeUpEbHJhTDJnSXV0cVozQjhwbApHeS9RbmI5UUQxRUpWYjRaeE9FaGNZMlc5VkpmSXBuZjN5QnVBdG83enZLZS9HMW54SDRCdDZXVEpRQ2tVamNzCk4zcVBXc3gxVnNsc0FFejdiWEdpSHltNkF5NHhGMjhiUTlYWUlva0lRWGQwVDJyRDMvbE5HeE50T1JaMmJLakQKdk96WXp2aDJpZFVJWTFEZ0dXSjExZ3RIRklBOUN2SGNXK1NNUEVoa2NLWkpBTzUxYXlGQnFUU1NwaW9yVndUcQphMGNCK2NnbUNRT0k0L01ZK2tJdnpvZXhmRzd4aGtVcWUwd3htcGg5UlFReGxUYk5RRENkYXhTZ3diRjJUK2d3CmJ5YUR2a1M0eHRSNlNvajdCS2pLQW1jbmY1Zm40QzVPcjBLTFVxTXpCdERNYmZRUWlobjYyaVpKTjZaWi80ZGcKcTRIVHF5VnB5dXpNWHNGcEo5TC9GcUgyREo0ZXhHR3BCdjAwYmEvWmF1eTdHc3FPYzVQbk5Cc1lhSENwbHkwWAo0MDdEUng1MXQ5WXdZSS90dFZhbHVlaHE5K2dSSnBPVFRLcDZBalpuL2E1WXQzaDZqRGdwTmZNL0V5TEZJWTl6ClY2Q1hxUVEvOEpSdmFpay9Kc0dDZitlZUxaT3c0a29JalpHRUFnMDRpdXlOVGpoeDBlL1FIRVZjWUFxTkxoWEcKckNUVGJDbjNOU1VPOXF4RVhDK0svMW0xa2FYb0NHQTBVV2xWR1oxSlNpZmJiTXgweXhxL2JycEVaUFVZbSszMgpvOFhmYm9jQldsakZVSis2YWxqVHZaM0xRTEtUU1BXN1RGTytHWHljQU9tQ0dobFhoMnRsYzZpVGM0MVBBQ3F5Cnl5K21IbVN2Cj1ra0g3Ci0tLS0tRU5EIFBHUCBQVUJMSUMgS0VZIEJMT0NLLS0tLS0K"}}} rekor-1.3.5/cmd/rekor-cli/app/tests/test.rpm000066400000000000000000005057501455727245600207520ustar00rootroot00000000000000NetworkManager-bluetooth-1:1.26.0-8.el8 >  A _U]q3Q8D GXŋ#׳( ~ a(yy|)DZ)x/hz\fTTqNM%˃p1m9sLj9=p:d>l_206%sHO$gǻ!S({:xn=‰ddb^JIĎkǶ(U'Mу=FTK6k,7=⛣7oVޢ5j߯NͦenVm 6 MDxhCb]l i1x( гUW |0& 3-5Y #& n!RU,3!*Ib)=pdLwbf <8@/ʁG^fUTzF&qB:vHh&}11 m|R f7fd4b5809a81d6bb7d009dcbb1b49c5d37e912c79773482caec9c2f5a9a80ce1a7b22fa8fb5b87db6c31f6d1bd8712fcb57ac8fv_U]M$/P h~]`Z` Vmtiy,IB5A,Mά"X YŁ- X ,Dc49_vr[IV?z2y ,Yi^F2#p\@i{m;v!DL/sϢO;gf+"A#r7F:F߶M_x̥ `])xC2}Z*\(.'d.Y*dќhzFx*vJV$қg "Ah ՄA>jDkw2ЊѬ4_3j0:%TblZ ΪmBؿ;p6wofA߁ʰ@Q< 7>,2#9m8%TV4B+'n㐎 g}֊D Z $ x k9%˲fӱW oJ&-r# ^gd?t盝kX[Zh>Jp X>p@h?Xd"( , W EKTd l t    0DX|X(188'9':$'B"GDHTIdXhYtZԔ[Ԝ\Ԭ]Լ^bnd־efltuvwטxרy׸ TCNetworkManager-bluetooth1.26.08.el8Bluetooth device plugin for NetworkManagerThis package contains NetworkManager support for Bluetooth devices._x86-02.mbox.centos.orgCentOSCentOSGPLv2+ and LGPLv2+CentOS Buildsys System Environment/Basehttp://www.gnome.org/projects/NetworkManager/linuxx86_64RAA____5fd94a8d397f9ca8c4df09d88216d94f192fefad28fa90f058e0fb126d3ea15c../../../../usr/lib64/NetworkManager/1.26.0-8.el8/libnm-device-plugin-bluetooth.sorootrootrootrootrootrootrootrootNetworkManager-1.26.0-8.el8.src.rpmNetworkManager-bluetoothNetworkManager-bluetooth(x86-64)libnm-device-plugin-bluetooth.so()(64bit) @@@@@@@@@@@@@@@@@@    @NetworkManager(x86-64)NetworkManager-wwanbluezlibbluetooth.so.3()(64bit)libc.so.6()(64bit)libc.so.6(GLIBC_2.14)(64bit)libc.so.6(GLIBC_2.2.5)(64bit)libc.so.6(GLIBC_2.4)(64bit)libdl.so.2()(64bit)libgcc_s.so.1()(64bit)libgcc_s.so.1(GCC_3.0)(64bit)libgcc_s.so.1(GCC_3.3.1)(64bit)libgio-2.0.so.0()(64bit)libglib-2.0.so.0()(64bit)libgmodule-2.0.so.0()(64bit)libgobject-2.0.so.0()(64bit)libmm-glib.so.0()(64bit)libnm-wwan.so()(64bit)libpthread.so.0()(64bit)libpthread.so.0(GLIBC_2.2.5)(64bit)libsystemd.so.0()(64bit)rpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rpmlib(PayloadIsXz)rtld(GNU_HASH)1:1.26.0-8.el81:1.26.0-8.el84.101-53.0.4-14.6.0-14.0-15.2-14.14.2_l@_N7_:q@_5+@_2@_+__ L_@^@^b^@^I^b;@^S^M#@^K^4^g@^C]@]@]@]@]?]e@]UI@]Ik]:@] u@]@\\]o@\73\5@\4\@\I\U@[[@[[F[[[[s[qr[Y[(@[ZZZZ3@Z@YYdYY5Y˒YX@YYx@YqYp@YlYK@Y:Y3@Y"Y /YXX-X @X@X@X@XX~@XwoXwoXRXF@X8'X5X@WW@WRWu@W@WhW@W@WPW3Wo@W@WVVn@V3V@VՄ@VVV%@VVD@V @V @UU6@UM@UU@ŬUUUU@U@U@UUUUHU@TE@TE@TE@TT TTTTsT[bTPTPTJ?@THT?@T2@T TSS@S@S@SR@S@SS@S@SG@S)S&S"@S@Sz@S@R3@R@R@R&RʚRR@Rv@R@R@R@RiR|@RM\@RJ@R<8R2@R)R6Q@QQQQɆ@Q@QxQ'@QQR@Q-@Q:@QP@PZP7@P7@P@P@P{P{Pp@PnPmz@P3x@PP - 1:1.26.0-8Antonio Cardace - 1:1.26.0-7Thomas Haller - 1:1.26.0-6Thomas Haller - 1:1.26.0-5Antonio Cardace - 1:1.26.0-4Thomas Haller - 1:1.26.0-3Antonio Cardace - 1:1.26.0-2Thomas Haller - 1:1.26.0-1Beniamino Galvani - 1:1.26.0-0.2.1Beniamino Galvani - 1:1.26.0-0.2Thomas Haller - 1:1.26.0-0.1Beniamino Galvani - 1:1.25.2-1Thomas Haller - 1:1.25.1-1Thomas Haller - 1:1.22.8-4Beniamino Galvani - 1:1.22.8-3Thomas Haller - 1:1.22.8-2Antonio Cardace - 1:1.22.8-1Antonio Cardace - 1:1.22.6-1Beniamino Galvani - 1:1.22.4-1Thomas Haller - 1:1.22.2-1Thomas Haller - 1:1.22.0-2Thomas Haller - 1:1.22.0-1Thomas Haller - 1:1.22.0-0.2Beniamino Galvani - 1:1.22.0-0.1Lubomir Rintel - 1:1.20.0-4Thomas Haller - 1:1.20.0-3Lubomir Rintel - 1:1.20.0-2Thomas Haller - 1:1.20.0-1Thomas Haller - 1:1.20.0-0.4Lubomir Rintel - 1:1.20.0-0.3Lubomir Rintel - 1:1.20.0-0.2Lubomir Rintel - 1:1.20.0-0.1Beniamino Galvani - 1:1.14.0-14Francesco Giudici - 1:1.14.0-13Thomas Haller - 1:1.14.0-12Beniamino Galvani - 1:1.14.0-11Beniamino Galvani - 1:1.14.0-10Thomas Haller - 1:1.14.0-9Lubomir Rintel - 1:1.14.0-8Thomas Haller - 1:1.14.0-7Thomas Haller - 1:1.14.0-6Lubomir Rintel - 1:1.14.0-5Beniamino Galvani - 1:1.14.0-4Thomas Haller - 1:1.14.0-3Thomas Haller - 1:1.14.0-2Thomas Haller - 1:1.14.0-1Thomas Haller - 1:1.14.0-0.4Thomas Haller - 1:1.14.0-0.3Lubomir Rintel - 1:1.14.0-0.2Thomas Haller - 1:1.14.0-0.1Lubomir Rintel - 1:1.12.0-0.4Thomas Haller - 1:1.12.0-0.3Thomas Haller - 1:1.12.0-0.2Thomas Haller - 1:1.12.0-0.1Thomas Haller - 1:1.10.2-1Björn Esser - 1:1.8.4-7Thomas Haller - 1:1.8.4-6Lubomir Rintel - 1:1.8.4-5Thomas Haller - 1:1.8.4-4Thomas Haller - 1:1.8.4-3Thomas Haller - 1:1.8.4-2Thomas Haller - 1:1.8.4-1Fedora Release Engineering - 1:1.8.2-3.2Fedora Release Engineering - 1:1.8.2-3.1Lubomir Rintel - 1:1.8.2-3Stephen Gallagher - 1:1.8.2-2Beniamino Galvani - 1:1.8.2-1Lubomir Rintel - 1:1.8.0-6Lubomir Rintel - 1:1.8.0-5Thomas Haller - 1:1.8.0-4Thomas Haller - 1:1.8.0-3Thomas Haller - 1:1.8.0-2Thomas Haller - 1:1.8.0-1Lubomir Rintel - 1:1.8.0-0.2.rc3Lubomir Rintel - 1:1.8.0-0.2.rc2Lubomir Rintel - 1:1.8.0-0.1Lubomir Rintel - 1:1.6.2-1Fedora Release Engineering - 1:1.6.0-1.1Lubomir Rintel - 1:1.6.0-1Thomas Haller - 1:1.6-0.2.rc1Lubomir Rintel - 1:1.6-0.1.rc1Thomas Haller - 1:1.5.3-5Igor Gnatenko - 1:1.5.3-4.1Lubomir Rintel - 1:1.5.3-4Thomas Haller - 1:1.5.2-4Thomas Haller - 1:1.5.2-3Thomas Haller - 1:1.5.2-2Lubomir Rintel - 1:1.5.2-1Lubomir Rintel - 1:1.4.2-1Thomas Haller - 1:1.4.0-4Thoams Haller - 1:1.4.0-3Thomas Haller - 1:1.4.0-2Lubomir Rintel - 1:1.4.0-1Thomas Haller - 1:1.4.0-0.5.git20160621.072358daMatthias Clasen - 1:1.4.0-0.4.git20160621.072358daLubomir Rintel - 1:1.4.0-0.3.git20160621.072358daThomas Haller - 1:1.2.2-2Lubomir Rintel - 1:1.2.2-1Lubomir Rintel - 1:1.2.0-1Lubomir Rintel - 1:1.2.0-0.7.rc2Lubomir Rintel - 1:1.2.0-0.7.rc1Lubomir Rintel - 1:1.2.0-0.8.beta3Lubomir Rintel - 1:1.2.0-0.7.beta3Lubomir Rintel - 1:1.2.0-0.7.beta2Dan Williams - 1:1.2.0-0.6.beta2.1Lubomir Rintel - 1:1.2.0-0.6.beta2Thomas Haller - 1:1.2.0-0.6.beta1Fedora Release Engineering - 1:1.2.0-0.5.beta1.1Lubomir Rintel - 1:1.2.0-0.5.beta1David King - 1:1.2.0-0.4.20151007gite73e55cLubomir Rintel - 1:1.2.0-0.3.20151112gitec4d653Lubomir Rintel - 1:1.2.0-0.3.20151023gite01c175Lubomir Rintel - 1:1.2.0-0.2.20151007gite73e55cLubomir Rintel - 1:1.2.0-0.2.20150903gitde5d981Lubomir Rintel - 1:1.2.0-0.1.20150903gitde5d981Lubomir Rintel - 1:1.0.6-2Lubomir Rintel - 1:1.0.6-1Thomas Haller - 1:1.0.6-0.2.20150813git7e2caa2Lubomir Rintel - 1:1.0.6-0.1.20150813git7e2caa2Lubomir Rintel - 1:1.0.4-2Lubomir Rintel - 1:1.0.4-1Dan Horák - 1:1.0.4-0.5.git20150713.38bf2cb0Lubomir Rintel - 1:1.0.4-0.4.git20150713.38bf2cb0Lubomir Rintel - 1:1.0.4-0.3.git20150707.e3bd4e1Jiří Klimeš - 1:1.0.4-0.2.git20150707.cf15f2aLubomir Rintel - 1:1.0.4-0.1.git20160624.f245b49aLubomir Rintel - 1:1.0.4-0.1.git20150618.8cffaf3bf5Fedora Release Engineering - 1:1.0.2-1.1Lubomir Rintel - 1:1.0.2-1Jiří Klimeš - 1:1.0.1-2.git20150429Dan Williams - 1:1.0.1-1.git20150305Dan Williams - 1:1.0.0-7Dan Williams - 1:1.0.0-6Dan Williams - 1:1.0.0-5Adam Williamson - 1:1.0.0-4Thomas Haller - 1:1.0.0-3Dan Winship - 1:1.0.0-2Dan Williams - 1:1.0.0-1Jiří Klimeš - 1:0.9.10.0-14.git20140704Jiří Klimeš - 1:0.9.10.0-13.git20140704Dan Winship - 1:0.9.10.0-12.git20140704Lubomir Rintel 1:0.9.10.0-11.git20140704Lubomir Rintel 1:0.9.10.0-10.git20140704Adam Williamson - 1:0.9.10.0-9.git20140704Lubomir Rintel 1:0.9.10.0-8.git20140704Stef Walter - 1:0.9.10.0-7.git20140704Jiří Klimeš - 1:0.9.10.0-6.git20140704Peter Robinson 1:0.9.10.0-5.git20140704Dan Horák - 1:0.9.10.0-4.git20140704Fedora Release Engineering - 1:0.9.10.0-3.git20140704.1Kalev Lember - 1:0.9.10.0-3.git20140704Dan Williams - 1:0.9.10.0-2.git20140704Kalev Lember - 1:0.9.10.0-1.git20140704.1Thomas Haller - 0.9.10.0-1.git20140704Thomas Haller - 0.9.9.98-1.git20140620Dan Williams - 0.9.9.95-1.git20140609Fedora Release Engineering - 1:0.9.9.1-6.git20140319Dan Williams - 0.9.9.1-5.git20140319Dan Winship - 0.9.9.1-4.git20140319Jiří Klimeš - 0.9.9.1-3.git20140317Jiří Klimeš - 0.9.9.1-2.git20140314Jiří Klimeš - 0.9.9.1-1.git20140310Thomas Haller - 0.9.9.1-0.git20140228Thomas Haller - 0.9.9.0-28.git20140131Thomas Haller - 0.9.9.0-27.git20140131Jiří Klimeš - 0.9.9.0-26.git20140131Jiří Klimeš - 0.9.9.0-25.git20140117Jiří Klimeš - 0.9.9.0-24.git20140114Dan Winship - 0.9.9.0-23.git20131003Dan Williams - 0.9.9.0-22.git20131003Dan Williams - 0.9.9.0-21.git20131003Dan Winship - 0.9.9.0-20.git20131003Jiří Klimeš - 0.9.9.0-19.git20131003Dan Winship - 0.9.9.0-18.git20131003Dan Williams - 0.9.9.0-17.git20131003Dan Williams - 0.9.9.0-16.git20131003Jiří Klimeš - 0.9.9.0-15.git20131003Dan Williams - 0.9.9.0-14.git20131003Dan Winship - 0.9.9.0-13.git20131001Bill Nottingham - 0.9.9.0-12.git20130913Dan Williams - 0.9.9.0-11.git20130913Dan Williams - 0.9.9.0-10.git20130906Dan Williams - 0.9.9.0-9.git20130807Dan Winship - 0.9.9.0-8.git20130724Dan Williams - 0.9.9.0-7.git20130724Dan Winship - 0.9.9.0-6Dan Winship - 0.9.9.0-5Jiří Klimeš - 0.9.9.0-4.git20130603Dan Williams - 0.9.9.0-3.git20130603Dan Williams - 0.9.9.0-2.git20130515Dan Williams - 0.9.9.0-1.git20130514Dan Williams - 0.9.8.1-2.git20130507Dan Williams - 0.9.8.1-1.git20130327Jiří Klimeš - 0.9.8.0-1Dan Williams - 0.9.7.997-2Dan Williams - 0.9.7.997-1Dan Winship - 0.9.7.0-12.git20121004Jiří Klimeš - 0.9.7.0-11.git20121004Dan Winship - 0.9.7.0-10.git20121004Dan Winship - 0.9.7.0-9.git20121004Jiří Klimeš - 0.9.7.0-8.git20121004Daniel Drake - 0.9.7.0-7.git20121004Dan Winship - 0.9.7.0-6.git20121004Dan Winship - 0.9.7.0-5.git20121004Dan Winship - 0.9.7.0-4.git20121004Dan Williams - 0.9.7.0-3.git20121004Dan Winship - 0.9.7.0-2.git20121004Dan Winship - 0.9.7.0-1.git20120820Fedora Release Engineering - 1:0.9.5.96-2Dan Williams - 0.9.5.96-1Jiří Klimeš - 0.9.5.95-1.git20120713Jiří Klimeš - 0.9.4-5.git20120521Dan Winship - 0.9.4-4.git20120502Jiří Klimeš - 0.9.4-3.git20120502Colin Walters - 1:0.9.4-2.git20120328_2Jiří Klimeš - 0.9.4-1.git20120328_2Dan Williams - 0.9.3.997-2Dan Williams - 0.9.3.997-1Dan Williams - 0.9.3.997-0.7Dan Williams - 0.9.3.995-0.6Dan Williams - 0.9.3.995-0.5Dan Williams - 0.9.3.995-0.4Dan Horák - 0.9.3-0.3Dan Williams - 0.9.3-0.2Dan Williams - 0.9.3-0.1Matthias Clasen - 0.9.2-4Fedora Release Engineering - 1:0.9.2-3Daniel Drake - 0.9.2-2Dan Williams - 0.9.2-1Adam Williamson - 1:0.9.1.90-5.git20110927Dan Williams - 0.9.1.90-3.git20110927Jiří Klimeš - 0.9.1.90-2.git20110927Dan Williams - 0.9.1.90-1Tom Callaway - 0.9.0-2Dan Williams - 0.9.0-1Ray Strode 0.8.9997-7.git20110721Dan Williams - 0.8.9997-6.git20110721Dan Williams - 0.8.9997-5.git20110702Dan Williams - 0.8.9997-4.git20110620Dan Williams - 0.8.9997-3.git20110613Dan Williams - 0.8.9997-2.git20110531Dan Williams - 0.8.9997-1.git20110531Dan Williams - 0.8.999-3.git20110526Dan Williams - 0.8.999-2.git20110509Dan Williams - 0.8.999-1Dan Williams - 0.8.998-4.git20110427Dan Williams - 0.8.998-3.git20110419Dan Williams - 0.8.998-2.git20110406Dan Williams - 0.8.998-1Dan Williams - 0.8.997-8.git20110331Dan Williams - 0.8.997-7.git20110330Christopher Aillon - 0.8.997-6.git20110328Dan Williams - 0.8.997-5.git20110328Dan Williams - 0.8.997-4.git20110325Dan Williams - 0.8.997-3.git20110324Dan Williams - 0.8.997-2.git20110324Dan Williams - 0.8.997-1Dan Williams - 0.8.996-1Dan Williams - 0.8.995-4.git20110308Dan Williams - 0.8.995-3.git20110308Matthias Clasen - 0.8.995-2.git20110308Dan Williams - 0.8.995-1.git20110308Matthias Clasen - 0.8.2-8.git20101117Fedora Release Engineering - 1:0.8.2-7.git20101117Matthias Clasen - 0.8.2-6.git20101117Dan Williams - 0.8.2-5.git20101117Matthias Clasen - 0.8.2-4.git20101117Dan Horák - 0.8.2-3.git20101117Matthias Clasen - 0.8.2-2.git20101117Dan Williams - 0.8.2-1.git20101117Matthias Clasen - 0.8.1-10.1Dan Williams - 0.8.1-10Dan Williams - 0.8.1-9Dan Williams - 0.8.1-8Dan Williams - 0.8.1-7Dan Williams - 0.8.1-6Dan Williams - 0.8.1-5Dan Williams - 0.8.1-4Dan Williams - 0.8.1-3Dan Williams - 0.8.1-2Dan Williams - 0.8.1-1Matthias Clasen - 0.8.1-0.5Dan Williams - 0.8.1-0.4Dan Williams - 0.8.1-0.3Dan Williams - 0.8.1-0.2.git20100519Dan Williams - 0.8.1-0.1.git20100510Dan Williams - 0.8-13.git20100509Dan Williams - 0.8-12.git20100504Dan Williams - 0.8-11.git20100503Dan Williams - 0.8-10.git20100502Dan Williams - 0.8-9.git20100429Dan Williams - 0.8-8.git20100426Dan Williams - 0.8-7.git20100422Dan Williams - 0.8-6.git20100408Dan Williams - 0.8-5.git20100408Dan Williams - 0.8-4.git20100325Dan Williams - 0.8-3.git20100323Dan Williams - 0.8-2.git20100317Dan Williams - 0.8-1.git20100219Dan Williams - 0.8-0.4.git20100211Kevin Kofler - 0.8-0.3.git20100129Dan Williams - 0.8-0.2.git20100129Dan Williams - 0.8-0.1.git20100122Dan Williams - 0.7.999-2.git20100120Dan Williams - 0.7.999-1.git20100120Dan Williams - 0.7.998-1.git20100106Dan Williams - 0.7.997-2.git20091214Dan Williams - 0.7.997-1Dan Williams - 0.7.996-7.git20091113Dan Williams - 0.7.996-6.git20091021Dan Williams - 0.7.996-5.git20091021Dan Williams - 0.7.996-4.git20091002Dan Williams - 0.7.996-3.git20090928Matthias Clasen - 0.7.996-3.git20090921Dan Williams - 0.7.996-2.git20090921Dan Williams - 0.7.996-1.git20090826Dan Williams - 0.7.995-3.git20090813Bastien Nocera 0.7.995-2.git20090804Dan Williams - 0.7.995-1.git20090804Matthias Clasen - 0.7.995-1.git20090728Dan Williams - 0.7.995-0.git20090728Fedora Release Engineering - 1:0.7.1-9.git20090708Dan Williams - 0.7.1-8.git20090708Dan Williams - 0.7.1-7.git20090708Dan Williams - 0.7.1-6.git20090617Dan Williams - 0.7.1-5.git20090617Karsten Hopp 0.7.1-4.git20090414.1Adam Jackson 1:0.7.1-4.git20090414Dan Williams - 1:0.7.1-3.git20090414Dan Williams - 1:0.7.1-2.git20090414Dan Williams - 1:0.7.1-1Dan Williams - 1:0.7.0.100-2.git20090408Dan Williams - 1:0.7.0.100-1Dan Williams - 1:0.7.0.99-5Dan Williams - 1:0.7.0.99-4Dan Williams - 1:0.7.0.99-3.5Dan Williams - 1:0.7.0.99-3Dan Williams - 1:0.7.0.99-2Dan Williams - 1:0.7.0.99-1Dan Williams - 1:0.7.0.98-1.git20090225Fedora Release Engineering - 1:0.7.0.97-6.git20090220Dan Williams - 1:0.7.0.97-5.git20090220Dan Williams - 1:0.7.0.97-4.git20090219Dan Williams - 1:0.7.0.97-2Dan Williams - 1:0.7.0.97-1Dan Williams - 1:0.7.0-2.git20090207Dan Williams - 1:0.7.0-1.git20090102Dan Williams - 1:0.7.0-0.12.svn4326Dan Williams - 1:0.7.0-0.12.svn4296Dan Williams - 1:0.7.0-0.12.svn4295Dan Williams - 1:0.7.0-0.12.svn4293Dan Williams - 1:0.7.0-0.11.svn4229Dan Williams - 1:0.7.0-0.11.svn4201Dan Williams - 1:0.7.0-0.11.svn4175Dan Williams - 1:0.7.0-0.11.svn4174Dan Williams - 1:0.7.0-0.11.svn4022.4Dan Williams - 1:0.7.0-0.11.svn4022.3Dan Williams - 1:0.7.0-0.11.svn4022.2Dan Williams - 1:0.7.0-0.11.svn4022.1Dan Williams - 1:0.7.0-0.11.svn4022Dan Williams - 1:0.7.0-0.11.svn3930Dan Williams - 1:0.7.0-0.11.svn3927Dan Williams - 1:0.7.0-0.11.svn3846Dan Williams - 1:0.7.0-0.11.svn3830Matthias Clasen - 1:0.7.0-0.10.svn3801Dan Williams - 1:0.7.0-0.10.svn3801Dan Williams - 1:0.7.0-0.10.svn3747Dan Williams - 1:0.7.0-0.9.4.svn3675Dan Williams - 1:0.7.0-0.9.3.svn3675Dan Williams - 1:0.7.0-0.9.3.svn3669Dan Williams - 1:0.7.0-0.9.3.svn3667Dan Williams - 1:0.7.0-0.9.3.svn3665Dan Williams - 1:0.7.0-0.9.3.svn3623Dan Williams - 1:0.7.0-0.9.2.svn3623Dan Williams - 1:0.7.0-0.9.2.svn3622Dan Williams - 1:0.7.0-0.9.2.svn3620Dan Williams - 1:0.7.0-0.9.2.svn3619Dan Williams - 1:0.7.0-0.9.2.svn3614Dan Williams - 1:0.7.0-0.9.2.svn3590Dan Williams - 1:0.7.0-0.9.2.svn3578Dan Williams - 1:0.7.0-0.9.2.svn3571Dan Williams - 1:0.7.0-0.9.2.svn3570Dan Williams - 1:0.7.0-0.9.2.svn3566Dan Williams - 1:0.7.0-0.9.1.svn3566Dan Williams - 1:0.7.0-0.9.1.svn3549Dan Williams - 1:0.7.0-0.9.1.svn3548Dan Williams - 1:0.7.0-0.9.1.svn3547Dan Williams - 1:0.7.0-0.9.1.svn3527Dan Williams - 1:0.7.0-0.9.1.svn3521Dan Williams - 1:0.7.0-0.9.1.svn3476Dan Williams - 1:0.7.0-0.9.1.svn3473Dan Williams - 1:0.7.0-0.9.1.svn3472Dan Williams - 1:0.7.0-0.9.1.svn3440Dan Williams - 1:0.7.0-0.9.1.svn3417Dan Williams - 1:0.7.0-0.8.svn3417Dan Williams - 1:0.7.0-0.8.svn3370Dan Williams - 1:0.7.0-0.8.svn3369Dan Williams - 1:0.7.0-0.8.svn3319Dan Williams - 1:0.7.0-0.8.svn3312Dan Williams - 1:0.7.0-0.8.svn3302Dan Williams - 1:0.7.0-0.8.svn3261Dan Williams - 1:0.7.0-0.8.svn3235Dan Williams - 1:0.7.0-0.8.svn3204Dan Williams - 1:0.7.0-0.8.svn3181Dan Williams - 1:0.7.0-0.8.svn3180Dan Williams - 1:0.7.0-0.8.svn3138Dan Williams - 1:0.7.0-0.8.svn3134Dan Williams - 1:0.7.0-0.8.svn3133Jeremy Katz - 1:0.7.0-0.8.svn3109Dan Williams - 1:0.7.0-0.6.6.svn3109Dan Williams - 1:0.7.0-0.6.5.svn3109Dan Williams - 1:0.7.0-0.6.5.svn3096Dan Williams - 1:0.7.0-0.6.4.svn3096Dan Williams - 1:0.7.0-0.6.3.svn3096Dan Williams - 1:0.7.0-0.6.3.svn3094Dan Williams - 1:0.7.0-0.6.2.svn3080Dan Williams - 1:0.7.0-0.6.1.svn3030Dan Williams - 1:0.7.0-0.5.svn3030Dan Williams - 1:0.7.0-0.4.svn3030Dan Williams - 1:0.7.0-0.4.svn3020Dan Williams - 1:0.7.0-0.3.svn3020Dan Williams - 1:0.7.0-0.3.svn3016Dan Williams - 1:0.7.0-0.3.svn3014Dan Williams - 1:0.7.0-0.3.svn3008Dan Williams - 1:0.7.0-0.3.svn2995Dan Williams - 1:0.7.0-0.3.svn2994Dan Williams - 1:0.7.0-0.3.svn2983Dan Williams - 1:0.7.0-0.3.svn2970Dan Williams - 1:0.7.0-0.3.svn2962Dan Williams - 1:0.7.0-0.3.svn2961Dan Williams - 1:0.7.0-0.3.svn2914Dan Williams - 1:0.7.0-0.3.svn2907Dan Williams - 1:0.7.0-0.3.svn2886Dan Williams - 1:0.7.0-0.3.svn2880Dan Williams - 1:0.7.0-0.3.svn2852Dan Williams - 1:0.7.0-0.3.svn2849Dan Williams - 1:0.7.0-0.3.svn2844Dan Williams - 1:0.7.0-0.2.svn2833Dan Williams - 1:0.7.0-0.1.svn2736Christopher Aillon 1:0.6.5-9Christopher Aillon 1:0.6.5-8Dan Williams 1:0.6.5-7Dan Williams 1:0.6.5-6Dan Williams 1:0.6.5-5Dan Williams 1:0.6.5-4Dan Williams 1:0.6.5-3Christopher Aillon 1:0.6.5-2Christopher Aillon 1:0.6.5-1Dan Williams - 1:0.6.5-0.7.svn2547Matthew Barnes 1:0.6.5-0.6.svn2474Matthias Clasen 1:0.6.5-0.5.svn2474Dan Williams - 1:0.6.5-0.4.svn2474Christopher Aillon - 1:0.6.5-0.3.cvs20061025Christopher Aillon - 1:0.6.5-0.2.cvs20061025Matthias Clasen Dan Williams - 1:0.6.5-0.cvs20061025Christopher Aillon - 1:0.6.4-6Christopher Aillon - 1:0.6.4-5Bill Nottingham - 1:0.6.4-4Dan Williams - 1:0.6.4-3Dan Williams - 1:0.6.4-2Christopher Aillon - 0.7.0-0.cvs20060529.7Ray Strode - 0.7.0-0.cvs20060529.6Ray Strode - 0.7.0-0.cvs20060529.5Ray Strode - 0.7.0-0.cvs20060529.4John (J5) Palmieri - 0.7.0-0.cvs20060529.3John (J5) Palmieri - 0.7.0-0.cvs20060529.2Jesse Keating - 0.7.0-0.cvs20060529.1.1Dan Williams - 0.7.0-0.cvs20060529Dan Williams - 0.7.0-0.cvs20060521Bill Nottingham - 0.6.2-3.fc6Jeremy Katz - 0.6.2-2.fc6Dan Williams - 0.6.2-1Peter Jones - 0.6.0-3Dan Williams 0.6.0-2Dan Williams 0.6.0-1Jeremy Katz - 0.5.1-18.cvs20060302Christopher Aillon Dan Williams 0.5.1-18.cvs20060301Christopher Aillon 0.5.1-17.cvs20060228Christopher Aillon 0.5.1-16.cvs20060227Christopher Aillon 0.5.1-15.cvs20060227Dan Williams 0.5.1-14.cvs20060221Dan Williams 0.5.1-13.cvs20060221Dan Williams 0.5.1-12.cvs20060213Christopher Aillon 0.5.1-11.cvs20060205Jesse Keating 0.5.1-10.cvs20060205.1Dan Williams 0.5.1-10.cvs20060205Dan Williams 0.5.1-9.cvs20060202Dan Williams 0.5.1-8.cvs20060131Dan Williams 0.5.1-7.cvs20060131Dan Williams 0.5.1-6.cvs20060127Jesse Keating John (J5) Palmieri - 0.5.1-5Peter Jones - 0.5.1-4Christopher Aillon - 0.5.1-3Christopher Aillon - 0.5.1-2Christopher Aillon - 0.5.0-2Dan Williams - 0.4.1-5.cvs20051010Dan Williams - 0.4.1-4.cvs20051009Dan Williams - 0.4.1-3.cvs20050922Jeremy Katz - 0.4.1-2.cvs20050912Dan Williams - 0.4.1-2.cvs20050819Dan Williams - 0.4.1Dan Williams - 0.4-36.cvs20050811Dan Williams - 0.4-35.cvs20050811Ray Strode - 0.4-34.cvs20050729Dan Williams - 0.4-34.cvs20050629David Zeuthen - 0.4-33.cvs20050629Dan Williams - 0.4-32.cvs20050617Dan Williams - 0.4-31.cvs20050616Dan Williams - 0.4-30.cvs20050615Dan Williams - 0.4-15.cvs30050404Dan Williams - 0.4-14.cvs30050404Dan Williams - 0.4-13.cvs30050404Dan Williams - 0.4-12.cvs20050404Dan Williams - 0.4-11.cvs20050404Dan Williams - 0.4-10.cvs20050404Dan Williams - 0.4-9.cvs20050404Jeremy Katz - 0.4-8.cvs20050404Jeremy Katz - 0.4-7.cvs20050404Dan Williams 0.4-6.cvs20050404Dan Williams 0.4-5.cvs20050402Christopher Aillon 0.4-4.cvs20050315Ray Strode 0.4-3.cvs20050315Ray Strode 0.4-2.cvs20050315Ray Strode 0.4-1.cvs20050315Ray Strode 0.4-1.cvs20050307Dan Williams 0.3.4-1.cvs20050304Dan Williams 0.3.3-2.cvs20050222Dan Williams 0.3.3-2.cvs20050214.x.1Dan Williams 0.3.3-2.cvs20050214Dan Williams 0.3.3-1.cvs20050202Dan Williams 0.3.3-1.cvs20050125Dan Williams 0.3.3-1.cvs20050124Than Ngo 0.3.3-1.cvs20050112.4 - 0.3.3-1.cvs20050118 - 0.3.3-1.cvs20050112 - 0.3.2-4.3.cvs20041208 - 0.3.2-3.cvs20041117 - 0.3.2-2.cvs20041115 - 0.3.2-2.cvs20041029 - 0.3.1-2 - 0.3.1-1Dan Williams 0.3-1Dan Williams 0.2-4Dan Williams 0.2-3Dan Williams 0.2-2Dan Williams 0.2-1Florian La Roche Dan Williams 0.1-3- initrd: accept mac address as interface specifier (rh #1879795) - initrd: fix parsing IPv6 prefix length (rh #1879795)- dhcp: add dhcp-vendor-class-identifier option (rh #1871042) - initrd: parse 'rd.net.dhcp.vendor-class' kernel cmdline arg (rh #1872299)- core: fix handling of local routes as default route and on D-Bus (rh #1868982)- core: fix wait-device-timeout race and support general device matches (rh #1853348)- bond: fix Reapply does not update bond options (rh #1847814) - dhcp: support DHCPv6 fqdn_fqdn option for hostname (rh #1858344)- core: fix managing devices after resuming from sleep (rh #1855563) - dhcp: fix BPF filter for internal client on big endian arch (rh #1861488) - core: support warning log setting IPv6 MTU with IPv6 disabled (rh #1840989) - wifi: fix crash parsing incomplete BSS info (rh #1866395)- core: fix generation of local routes for VRF devices (rh #1857133) - team: fix crash on failure to connect to teamd (rh #1856723) - core: fix detecting failure of master active-connection (rh #1845018) - core: fix warning about setting active_slave of bond when activating master (rh #1858326) - import translations (rh #1820552)- update to 1.26.0 - device: reset SR-IOV parameters on activation failure (rh #1819587) - initrd: enable ipv6.method=auto with ip=dhcp6 (rh #1854323) - core: add "nm-shared" zone for firewalld for shared mode (rh #1834907) - ppp: fix taking control of link (rh #1849386)- device: restart DHCP only for devices that are active or activating (rh #1852612) - initrd: fix generating default BOOTIF= connection (rh #1853277) - ovs: fix race condition when setting MAC address for ovs interfaces (rh #1852106)- update to 1.26-rc2 (1.25.91) - initrd: set ipv6.method=auto when using IPv4 static configuration (rh #1848943) - cloud-setup: add support for Google Cloud load-balancing routes (rh #1821787)- update to 1.26-rc1 (1.25.90) - core: support more tc qdiscs (tbf and sfq) (rh #1546802) - core: support match devices for connection profile by PCI address (ID_PATH) (rh #1673321) - ovs: fix peer property for OVS patch interface (rh #1845216) - doc: add manual pages nm-settings-dbus and nm-settings-nmcli (rh #1614726) - wifi: don't block autoconnect for profiles that never succeeded to connect (rh #1781253) - dbus,nmcli: highlight externally managed devices (rh #1816202)- update to 1.25.2 (development) - support ethtool coalesce and ring options (rh #1614700) - core: improve synchronization of qdiscs with kernel (rh #1815875) - team: support running without D-Bus (rh #1784363) - core: fix potential crash when autoactivating child connections (rh #1778073) - ethernet: reset original autonegotiation/speed/duplex settings on deactivation (rh #1807171) - core: fix setting IPv6 token in kernel (rh #1819680)- update to 1.25.1 (development) - improve documentation (rh #1651594, rh #1819259) - vrf: add support (rh #1773908) - bond: improve setting default options for miimon and updelay (rh #1805184, rh #1806549) - bluetooth: fix crash handling DUN modem (rh #1826635) - core: fix potential infinite loop with prefix delegation (rh #1488030) - initrd: fixes for running NetworkManager in initrd (rh #1627820, #1710935, #1744935, #1771792) - core: prevent multiple attempts to create default wired connection (rh #1687937) - bridge: support more options (rh #1755768) - libnm,dbus: expose HwAddress for all device types (rh #1786937) - core: fix route priority for IPv6 (rh #1814557) - core: fix crash during reapply (rh #1816067) - core: clear IP address from bridge slave (rh #1816517) - ovs: support changing MTU of OVS interfaces (rh #1820052) - nm-online: support setting timeout for NetworkManager-wait-online (rh #1828458)- core: fix leaking device state files in /run (rh #1810153) - dhcp: fix crash in nettools client when leaking GSource (rh #1810188)- dhcp: keep trying after a send failure (rh #1806516) - ovs: fail port enslavement when the bridge is not found (rh #1797696)- bond: fix setting arp_validate option for other bonding modes (rh #1789437)- Update to 1.22.8 - Added configuration option to customize IPv6 RA timeout (rh #1801158) - Removed length limitation for OVS Bridge, Patches and Interfaces (only Patch types) names (rh #1788432) - Reworked asynchronous deactivation of OVS interfaces (rh #1787989, rh #1782701) - Fixed failure when creating team interfaces (rh #1798947) - ifcfg-rh: fix clearing ovs slave type from ifcfg-rh file (rh #1804167) - Fixed bug causing virtual devices to not be available after AddConnection()/Update() (rh #1804350)- Update to 1.22.6 - nm-device: add new pending action to keep the device busy when in between states (rh #1759956) - cloud-setup: avoid unsupported settings in systemd service unit (rh #1791758) - do not create virtual device if master is not present (rh #1795919) - allow IPv6 RA timeout to be set to a value higher than 120 seconds (rh #1795957) - fix behaviour when 'ipv4.dhcp-timeout' option is set to 'infinity' (rh #1791378)- Update to 1.22.4 - dhcp: fix behavior of internal DHCP client when the server sends a NAK (rh #1787219)- Update to 1.22.2 - core,libnm: expose capability for OVS support (rh #1785147) - dhcp: various bugfixes for nettools n-dhcp4 plugin- dhcp: fix parsing of DNS search domain with nettools plugin (rh #1783981)- Update to 1.22.0 - support main.auth-polkit=root-only setting to allow root only (rh #1762011)- Update to 1.22-rc1 (1.21.90) - large internal rework of libnm's NMClient - dhcp: switch implementation of "internal" DHCP to nettools' n-dhcp4 - add support for carrier state of devices on D-Bus/libnm (rh #1722024) - cloud-setup: add initial and experimental tool for configuring in cloud (rh #1642461) - dhcp: support configuring FQDN hostname flags (rh #1649368)- Update to 1.21.3, a development snapshot of NetworkManager 1.22 - support configuring default route as a regular, static route (rh #1714438)- initrd: re-enable the generator (rh #1626348)- wifi: detect FT support per device to fix issues with driver support (rh #1743730) - doc: fix default values in pre-generated documentation (rh #1737945)- Import translations (rh #1689999)- Update to 1.20.0 release - fix license comments for RPM package (rh #1723395) - dhcp: disable experimental nettools DHCP plugin- Update to 1.20-rc1 snapshot - settings: support read-only directory for keyfile profiles (rh #1674545) - settings: add AddConnection2 D-Bus API to suppress autoconnect (rh #1677068) - settings: add no-reapply flat to Update2 D-Bus API (rh #1677070) - openvswitch: don't release slaves on quit (rh #1733709) - dhcp: expose private options for internal DHCP plugin (rh #1663253) - device: fix route table setting when re-activating device (rh #1719318) - man: clarify example in nm-openvswitch manual page (rh #1638038) - man: various improvements of manual pages (rh #1612554)- initrd: disable the generator again- Update to a newer 1.20 snapshot - ovs: support dpdk interfaces (rh #1612503) - libnm-core: change unsupported modes for arp_ip_targets bond option (rh #1718173) - ipv6: add 'disabled' method (rh #1643841) - device: fix matching parent device by connection UUID (rh #1716438) - cli: fix default value for team.runner-min-ports (rh #1716987) - initrd: re-enable the generator (rh #1626348)- Update to a 1.20 snapshot - core: fix a possible crash on device removal (rh #1659790) - core: fix automatic activation of software deviecs (rh #1667874) - team: use strict JSON parsing for configuration (rh #1691619) - team: don't kill teamd for external devices (rh #1693142) - logging: don't misuse SYSLOG_FACILITY field in journal (rh #1709741)- clients: fix string list setter (rh #1671200)- device: improve assuming bridges on startup (rh #1593939)- dhcp: fix client-id and DUID for infiniband (2) (rh #1658057)- device: ensure IP configuration is restored when link goes up (rh #1636715) - dhcp: fix client-id and DUID for infiniband (rh #1658057) - dhcp: change internal DHCP plugin's ipv4.dhcp-client-id setting to "mac" (rh #1661165)- ifcfg-rh: fix reading SR-IOV settings - dhcp: support client-id and DUID for infiniband (rh #1658057)- dhcp: fix default client-id for NetworkManager-config-server (rh #1658057) - connectivity: fix crash and portal detection (rh #1658217) - core: combine secret-key with machine-id for host identity (rh #1642023) - SR-IOV related fixes (rh #1651578, rh #1651576, rh #1651979) - core: fix updating agent-owned secrets (rh #1658771) - core: no longer set rp_filter sysctl (rh #1651097) - device: don't take device down when changing MAC address (rh #1659063) - doc: use pregenerated manual pages and gtk-doc from source tarball- Update translations (rh #1608323)- device: improve auto selection of device when activating profile (rh #1639254)- dhcp: fix out-of-bounds heap write for DHCPv6 with internal plugin (CVE-2018-15688) - dhcp: revert letting internal DHCP generate default client-id based on MAC address (rh #1640464) - dhcp: support "duid" setting for ipv4.dhcp-client-id - dhcp: support "${MAC}" identifier for connection.stable-id - dhcp: support dhcp-plugin device spec for matching devices in NetworkManager.conf - dhcp: install configuration snippet in config-server package for ipv4.dhcp-client-id=mac (rh #1640494) - dns: remove limitation for six DNS search entries (rh #1649704) - libnm: fix crash cancelling activation from within callback (rh #1643085)- Update translations (rh #1608323)- Don't depend on openvswitch (rh #1629178) - device: don't remove routes when the interface is down (rh #1636715)- dhcp: let internal DHCP generate default client-id based on MAC address (2)- dhcp: let internal DHCP generate default client-id based on MAC address- Update to 1.14.0 release- dhcp: switch default DHCP plugin from dhclient to internal (rh #1571655)- Update to 1.13.3, a development snapshot of NetworkManager 1.14- Update to 1.13.2, a development snapshot of NetworkManager 1.14- Update to 1.13.0, a development snapshot of NetworkManager 1.14- Update to 1.11.4, a development snapshot of NetworkManager 1.12 - Switch to Python 3-only build root- core: use gnutls crypto library instead of nss (rh #1581693)- core: fix error destroying checkpoints (rh#1574565)- Update to 1.11.3 release- Update to 1.10.2 release- Apply patch from previous commit- systemd: let NM-w-o.service require NetworkManager service (rh #1452866) - platform: really treat dsa devices as regular wired ethernet (rh #1371289) - libnm: fix accessing enabled and metered properties- platform: treat dsa devices as regular wired ethernet (rh #1371289)- device: fix frozen notify signals on unrealize error path - device: fix delay startup complete for unrealized devices - keyfile: fix handling routes with metric zero- cli: fix crash in interactive mode for "describe ." - libnm/{vpn,remote}-connection: disconnect signal handlers when disposed - libnm/manager: disconnect from signals on the proxy when we're disposed- enable NetworkManager-wait-online.service on package upgrade (rh#1455704)- Update to 1.8.4 release - don't install NetworkManager-wait-online in network-online.target.wants (rh#1455704)- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild- provide NetworkManager-devel- NetworkManager-wifi and NetworkManager-glib-devel should require NetworkManager, not provide it.- Update to 1.8.2 release - dhcp/dhclient: improve "interface" statement parsing - dns: fix public suffix check on search domains (rh #1404350)- device: don't change MTU unless explicitly configured (rh #1460760) - core: don't remove external IPv4 addresses (rh #1459813) - cli: fix output of iface in overview output (rh#1460219) - ppp: unexport NMPPPManager instance on dispose (rh#1459579) - cli: remove spurious device names from wifi subcommands output (rh#1460527)- bond: fix crash comparing mode while generating bond connection (rh #1459580) - connectivity: fix route penalty if WWAN and BT device using ip-ifindex (rh #1459932) - device: persist nm-owned in run state (rh #1376199) - device: fix assuming master device on restart (rh #1452062) - device: apply route metric penality only when the default route exists (rh #1459604) - connectivity: fix periodic connectivity check (rh #1458399) - bond: improve option matching on daemon restart (rh #1457909) - device: fix touching device after external activation (rh #1457242)- ifcfg-rh: fix writing legacy NETMASK value (rh #1445414) - tui: fix crash during connect (rh #1456826) - libnm: fix libnm rejecting VLAN ID 4095 (rh #1456911) - bluetooth: fix crash on connecting to a NAP (rh #1454385) - device: release removed devices from master on cleanup (rh #1448907) - nmcli: fix crash when setting 802-1x.password-raw (rh #1456362)- device: update external configuration before commit (fix bug) (rh #1449873)- dhcp: don't add route to DHCP4 server (rh #1448987) - device: update external configuration before commit (rh #1449873) - libnm: fix NUL termination of device's description (rh #1443114) - libnm, core: ensure valid UTF-8 in device properties (rh #1443114) - core: fix device's UDI property on D-Bus (rh #1443114) - ifcfg-rh: omit empty next hop for routes in legacy format (rh #1452648) - core: fix persisting managed state of device (rh #1440171) - proxy: fix use-after-free (rh #1450459) - device: don't wrongly delay startup complete waiting for carrier (rh #1450444)- Update to 1.8.0 release- Update to third Release Candidate of NetworkManager 1.8- Update to second Release Candidate of NetworkManager 1.8- Update to a snapshot of 1.8.x series- Update to a 1.6.2 release- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild- Update to a 1.6.0 release- Update with fixes from upstream nm-1-6 branch - build: let libnm and glib package conflict (rh #1406454)- Update to a 1.6-rc1- fix build failure due to clash of bitwise defines- Rebuild for readline 7.x- Update to a newer development snapshot- Rebuild package for vala generation error (rh#1398738)- fix enabling ifcfg-rh plugin by default for +=/-= operations (rh#1397938) - fix missing symbol _nm_device_factory_no_default_settings- fix enabling ifcfg-rh plugin by default (rh#1397938) - move translation files from core to libnm/glib subpackages- Update to a development snapshot- Update to 1.4.2- wifi: fix another activation failure when changing MAC address (rh#1371478, bgo#770456, bgo#770504)- dhcp: fix race to miss DHCP lease event (rh#1372854)- wifi: fix activation failure due to error changing MAC address (rh#1371478, bgo#770456)- Update to NetworkManager 1.4.0 release- fix stale Wi-Fi after resume from suspend (rh#1362165)- Rebuild against newer GLib to overcome logging problems on i686- Update to a later Git snapshot- dns: clear cache of dnsmasq when updating DNS configuration (rh#1338731) - dns: fix restarting dnsmasq instance - spec: depend bluetooth subpackage on exact wwan version - all: fix some memleaks- Update to NetworkManager 1.2.2 release- Update to NetworkManager 1.2.0 release- Update to NetworkManager 1.2-rc2- Update to NetworkManager 1.2-rc1- Fix link detection on 4.5 when build with 4.6 kernel- Update to NetworkManager 1.2-beta3- Fix obtaining the hostname from DNS (rh #1308974)- Fix activating connections in some cases (rh #1316488)- Update to NetworkManager 1.2-beta2 - Resync with contrib/rpm- specfile: remove no longer needed 10-ibft-plugin.conf and sync with contrib/rpm - core: backport fix for missing braces bug in platform- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild- Update to NetworkManager 1.2-beta1- Add upstream fix for AP list hash function (#1288867)- Update to a later snapshot - Enables RFC7217 addressing for new IPv6 connections- Drop the NetworkManager-devel subpackage (folded into libnm-glib-devel) - Update to a later snapshot- Import a newer 1.2 git snapshot- Fix test run- Import a 1.2 git snapshot- Fix command line parsing- Update to 1.0.6 release- fix crash when deactivating assumed device (rh #1253949) - backport wifi scan options for ssid - use plain HTTP URI for connectivity check- Update to a Git snapshot- Fix an assertion failure in nmcli (rh #1244048) - Fix default route handling on assumed connections (rh #1245648)- Update to 1.0.4 release- WEXT depends on enabled wifi- A bit more recent Git snapshot- A bit more recent Git snapshot - This one fixes a regression with default route management- Update to a new 1.0.3 development snapshot (git20150707) - core: fix handling of ignore-auto-* properties (rh #1239184)- A bit more recent Git snapshot- Update to a recent Git snapshot- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild- Update to 1.0.2 release- Update to 1.0.2 development snapshot (git20150429)- Update to 1.0.2 development snapshot- dns: revert resolv.conf symlink stuff (should only be in F23+, not F22)- connectivity: fix checking when no valid DNS servers are present (rh #1199098)- core: flush IPv6LL address when deconfiguring managed devices (rh #1193127) (rh #1184997)- core: resume bridged connections properly (rh #1162636, backport from master)- dns: manage resolv.conf as symlink to private file in /run directory (rh #1116999)- build: fix NetworkManager-bluetooth dep on NetworkManager-wwan - build: re-enable hardware plugins on s390- Update to 1.0- vpn: propagate daemon exec error correctly (bgo #739436) - core: do not assert when a device is enslaved externally (rh #1167345)- cli: fix crash in `nmcli device wifi` with multiple wifi devices (rh #1159408)- platform: fix a routing-related bug that could cause NM and other apps to spin (rh #1151665)- Fix IPv6 next hop default setting- Avoid unowned /etc/NetworkManager in config-connectivity-fedora- connectivity-fedora: don't require NetworkManager (#1156198)- bluetooth: Restore DUN support (rh #1055628)- Allow non-local users network control after PolicyKit authentication (rh #1145646)- connectivity: use HTTPS for connectivity checking (rh #113577)- adsl plugin needs rp-pppoe to work- always include ModemManager-glib-devel (#1129632)- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild- Rebuilt for ppp 2.4.7- connectivity: ensure interval is set to enable connectivity checking (rh #1123772)- Rebuilt for gobject-introspection 1.41.4- Update to upstream 0.9.10.0 release snapshot- Update to upstream 0.9.9.98 (0.9.10-rc1) release snapshot- Update to upstream 0.9.9.95 (0.9.10-beta1) release snapshot- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild- Rebuild against pppd 2.4.6- Update to a git snapshot (git20140319 git:3980806) - Rename NetworkManager-atm package to NetworkManager-adsl - Rename NetworkManager-bt package to NetworkManager-bluetooth- Update to a git snapshot (git20140317 git:a1e89b4) - platform: fix NM crash if link has no name (e.g. for failed VPN connection) - libnm-util/cli: fix bridge priority default value (rh #1073664)- Update to a git snapshot (git20140314 git:45a326d) - Fix Obsoletes and Requires to perform updates correctly- Update to a git snapshot (git20140310 git:350b6d6)- new upstream snapshot with development version 0.9.9.1- add nmtui package - bugfix caching of libnl objects (caused error with new libnl3 version when activating bridges) (rh #1063290) - fix NMManager:startup tracking (pending action) (rh #1030583)- core: fix crash getting secrets in libnm-glib- Update to a git snapshot (git20140131)- Update to a git snapshot (git20140117)- Update to a git snapshot (git20140114)- bluez-manager: fix a crash (rh #1048711)- core: fix IPv6 router solicitation loop (rh #1044757)- core: wait for link before declaring startup complete (rh #1034921) - core: ignore RA-provided IPv6 default routes (rh #1029213) - core: set IPv4 broadcast address correctly (rh #1032819)- core: Fix PtP/peer address support, for OpenVPN (rh #1018317)- dispatcher: fix crash on exit while logging from signal handler (rh #1017884) - core: workaround crash when connecting to wifi (rh #1025371) - ethernet: don't crash if device doesn't have a MAC address (rh #1029053) - libnm-glib: fix crash by taking additional ref in result_cb() (rh #1030403) - ifcfg-rh: fix ignoring updates that don't change anything- nmcli: add "con load" to manually load an ifcfg file - vpn: fix logging to help debug rh #1018317 - bridge: fix crash with bridge ports with empty settings (rh #1031170)- core: fix detection of non-mac80211 devices that do not set DEVTYPE (rh #1015598)- core: add some debugging to help diagnose netlink errors (rh #1029213)- ifcfg-rh: fix crash in ifcfg-rh plugin when reloading connections (rh #1023571) - ifcfg-rh: fix crash when having connections with NEVER_DEFAULT (rh #1021112) - core: fix segfault in nm-policy when setting default route for vpn (rh #1019021) - ifcfg-rh: fix crash when reading connection (assert) (rh #1025007) - core: allow IPv4 to proceed if IPv6 is globally disabled but set to "auto" (rh #1012151)- core: fix DHCPv6 address prefix length (rh #1013583) - cli: enhance bonding questionaire (rh #1007355) - core: fix crash with Bluez5 if PAN connection is not defined (rh #1014770) - libnm-glib: fix various memory leaks that could cause UIs to mis-report state - core: fix issues with mis-configured IPv6 router advertisements (rh #1008104) - cli: fix potential crash editing connections (rh #1011942)- core: fix bridge device creation (#1012532) - core,settings: do not call functions with connection==NULL (rh #1008151) - cli: accept gateway in the IP questionnaire of 'nmcli -a con add' (rh #1007368) - cli: always print success message (not only in --pretty mode) (rh #1006444) - cli: fix bond questionnaire to be able to set miimon (rh #1007355) - ifcfg-rh: if IPv4 is disabled put DNS domains (DOMAIN) into IPv6 (rh #1004866) - platform: fix a crash when nm_platform_sysctl_get() returns NULL (rh #1010522) - platform: fix InfiniBand partition handling (rh #1008568) - infiniband: only check the last 8 bytes when doing hwaddr matches (rh #1008566) - bluez: merge adding support for BlueZ 5 (bgo #701078) - api: clarify lifetime and behavior of ActiveConnection's SpecificObject property (rh #1012309) - vpn: fix connecting to VPN (bgo #708255) (rh #1014716) - rdisc: do not crash on NDP init failures (rh #1012151) - cli: be more verbose when adding IP addresses in questionnaire (rh #1006450) - team: chain up parent dispose() in NMDeviceTeam dispose() (rh #1013593) - translation updates- drop wimax subpackage- core: actually enable ModemManager 1.0 support - libnm-glib: fix nm_remote_connection_delete() not calling callback (rh #997568) - cli: ensure terminal is reset after quitting - cli: set wep-key-type properly when editing (rh #1003945) - man: fix typo in nmcli examples manpage (rh #1004117) - core: fix setting VLAN ingress/egress mappings - core: allow creating VLANs from interfaces other than Ethernet (rh #1003180) - cli: fix input/output format conversion (rh #998929)- core: fix bug which disallowed deleting connections (rh #997568) - core: add support for Team devices - core: enable NetworkManager-wait-online by default (rh #816655) - core: fix crash when 'gre' and 'macvlan' links change (rh #997396) - core: fail activation when invalid static routes are configured (rh #999544) - core: enhance connectivity checking to include portal detection - core: allow hyphens for MAC addresses (rh #1002553) - core: remove NetworkManager-created software devices when they are deactivated (rh #953300) - core: fix handling of some DHCP client identifiers (rh #999503) - core: correctly handle Open vSwitch interfaces as generic interfaces (rh #1004356) - core: better handle Layer-2-only connections (rh #979288) - cli: enhanced bash completion - cli: make the 'describe' command more visible (rh #998002) - cli: fix bug rejecting changes to Wi-Fi channels (rh #999999) - cli: update bash completion to suggest connection names (rh #997997) - cli: fix tab completion for aliases in edit mode - cli: ask whether to switch IP method to 'auto' when all addresses are deleted (rh #998137) - cli: request missing information when --ask is passed (rh #953291) - cli: add 'remove' command to edit mode - cli: fix creation of secure Wi-Fi connections (rh #997969) (rh #997555) - cli: default autoconnect to no and ask whether to activate on save (rh #953296) - man: clarify manpage text (rh #960071) (rh #953299) - man: fix errors in the nmcli help output and manpage (rh #997566) - ifcfg-rh: only write IPV6_DEFAULTGW when there's actually a default gateway (rh #997759) - ifcfg-rh: fix handling of legacy-format routes file with missing gateway- core: fix assert on multi-hop routes (rh #989022) - core: fix dispatcher systemd unit enabling (rh #948433) - ifcfg-rh: ignore emacs temporary lockfiles (rh #987629) - core: fix various routing issues and interaction with kernel events - cli: confirm saving connections when autoconnect is enabled (rh #953296) - cli: automatically change method when static IP addresses are added - core: preserve externally added IPv4 routes and addresses- Create NetworkManager-config-server package- Update to git snapshot- Belatedly update udev directory for UsrMove - Fix incorrect dates in old changelog entries to avoid rpm warnings- build support for connectivity checking (rh #810457)- disable building WiMax for RHEL- Update to new 0.9.10 snapshot- Update for systemd network-online.target (rh #787314) - Add system service for the script dispatcher (rh #948433)- Enable hardened build - Update to 0.9.10 snapshot - cli: new capabilities and somewhat re-arranged syntax - core: generic interface support - core: split config support; new "server mode" options - core: allow locking connections to interface names- core: fix issue with UI not showing disconnected on rfkill - core: memory leak fixes - core: silence warning about failure reading permanent MAC address (rh #907912) - core: wait up to 120s for slow-connecting modems - core: don't crash on PPPoE connections without a wired setting - core: ensure the AvailableConnections property is always correct - keyfile: ensure all-default VLAN connections are read correctly - core: suppress kernel's automatic creation of bond0 (rh #953466) - libnm-glib: make NMSecretAgent usable with GObject Introspection - libnm-util: fix GObject Introspection annotations of nm_connection_need_secrets() - core: documentation updates- Update to 0.9.8.2 snapshot - core: fix VLAN parent handling when identified by UUID - core: quiet warning about invalid interface index (rh #920145) - core: request 'static-routes' from DHCP servers (rh #922558) - core: fix crash when dbus-daemon is restarted (rh #918273) - core: copy leasefiles from /var/lib/dhclient to fix netboot (rh #916233) - core: memory leak and potential crash fixes - ifcfg-rh: ensure missing STP property is interpreted as off (rh #922702)- Update to the 0.9.8.0 release - cli: fix a possible crash- core: use systemd for suspend/resume, not upower- Update to 0.9.8-beta2 - core: ignore bridges managed by other tools (rh #905035) - core: fix libnl assert (rh #894653) - wifi: always use Proactive Key Caching with WPA Enterprise (rh #834444) - core: don't crash when Internet connection sharing fails to start (rh #883142)- Set correct systemd KillMode to fix anaconda shutdown hangs (rh #876218)- ifcfg-rh: write missing IPv6 setting as IPv6 with "auto" method (rh #830434)- Build vapi files and add them to the devel package- Apply patch from master to read hostname from /etc/hostname (rh #831735)- Apply patch from master to update hostname (rh #875085) - spec: create /etc/NetworkManager/dnsmasq.d (rh #873621)- Don't bring up uninitialized devices (fd #56929)- Actually apply the patch from the previous commit...- Apply patch from master to fix a crash (rh #865009)- Apply patch from master so connections finish connecting properly (bgo #685581)- Forward-port some forgotten fixes from F17 - Fix networked-filesystem systemd dependencies (rh #787314) - Don't restart NM on upgrade, don't stop NM on uninstall (rh #811200)- Update to git snapshot- Update to 0.9.7.0 snapshot- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild- Update to 0.9.6-rc2 - core: fix race between parallel DHCP client invocations - core: suppress a useless warning (rh #840580) - ifcfg-rh: fix segfault with malformed values (rh #841391) - ifcfg-rh: ignore IP config on bond slave configurations (rh #838907)- Update to 0.9.5.95 (0.9.6-rc1) snapshot - core: add autoconnect, driver-versioni and firmware-version properties to NMDevice - core: various IPv6 improvements - core: reduce number of changes made to DNS information during connection setup - core: add Vala language bindings - vpn: support IPv6 over VPNs - wifi: add on-demand WiFi scan support- Update to git snapshot- NM no longer uses /var/run/NetworkManager, so don't claim to own it. (rh #656638)- Update to git snapshot- Add _isa for internal requires; otherwise depsolving may pull in an arbitrary architecture.- Update to 0.9.4- libnm-glib: updated for new symbols the applet wants- applet: move to network-manager-applet RPM - editor: move to nm-connection-editor RPM - libnm-gtk: move to libnm-gtk RPM- Update to 0.9.3.997 (0.9.4-rc1) - core: fix possible WiFi hang when connecting to Ad-Hoc networks - core: enhanced IPv6 compatibility - core: proxy DNSSEC data when using the 'dnsmasq' caching nameserver plugin - core: allow VPNs to specify multiple domain names given by the server - core: fix an issue creating new InfiniBand connections - core/applet/editor: disable WiFi Ad-Hoc WPA connections until kernel bugs are fixed- core: fix issue with carrier changes not being recognized (rh #800690) - editor: warn user if CA certificate is left blank- core: fix a crash with ipw2200 devices and adhoc networks - core: fix IPv6 addressing on newer kernels - core: fix issue with VPN plugin passwords (rh #802540) - cli: enhancements for Bonding, VLAN, and OLPC mesh devices - ifcfg-rh: fix quoting WPA passphrases that include quotes (rh #798102) - libnm-glib: fix some issues with duplicate devices shown in menus- Update to 0.9.3.995 (0.9.4-beta1) - core: add support for bonding and VLAN interfaces - core: add support for Internet connectivity detection - core: add support for IPv6 Privacy Extensions - core: fix interaction with firewalld restarts- disable WiMAX plugin on s390(x)- Put WiMAX plugin files in the right subpackage- Update to 0.9.4 snapshot - wimax: enable optional support for Intel WiMAX devices - core: use nl80211 for WiFi device control - core: add basic support for Infiniband IP interfaces - core: add basic support for bonded interfaces - core: in-process IP configuration no longer blocks connected state- Rebuild- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild- Rebuild for libgnome-bluetooth.so.9- core: fix possible crash when talking to ModemManager - core: improve handling of rfkill on some machines (eeepc 1005HA and others) - ifcfg-rh: don't use spaces in ifcfg file names (rh #742273) - core: accept IPv6 Router Advertisements when forwarding is on - core: bump dnsmasq cache size to 400 entries - core: ensure IPv6 static routes are flushed when device is deactivated - ifcfg-rh: fix changing WPA connections to WEP - core: fix setting hostname from DHCP (rh #719100) - libnm-glib: fix various GObject introspection issues (rh #747302) - core: don't change routing or DNS if no devices are managed - core: ensure IPv6 RA-provided routes are honored- Rebuilt for glibc (rh #747377) - core: fix setting hostname from DHCP options (rh #719100) - skip a release to keep up with F16- core: fix location of wifi.ui (rh #741448)- core: ifcfg-rh: remove newlines when writing to ifcfg files (CVE-2011-3364) (rh #737338) - core: change iscsiadm path to /sbin/iscsiadm in ifcfg-rh plugin (rh #740753) - core: fix refcounting when deleting a default wired connection (lp:797868)- Update to 0.9.1.90 (0.9.2-beta1) - core: fix IPv6 link-local DNS servers in the dnsmasq DNS plugin - cli: add ability to delete connections - keyfile: fix an issue with duplicated keyfile connections - core: ensure the 'novj' option is passed through to pppd - core: store timestamps for VPN connections too (rh #725353)- fix systemd scriptlets and trigger- Update to 0.9 release - core: fix issue where scan results could be ignored - core: ensure agent secrets are preserved when updating connections - core: don't autoconnect disabled modems - core: fix race when checking modem enabled/disabled status after disabling - core: ensure newly installed VPN plugins can actually talk to NM - core: add support for 802.1X certificate subject matching - libnm-glib: various introspection fixes - applet/editor: updated translations- Add some patches for some blocker (rh #727501)- core: updated Russian translation (rh #652904) - core: fix possible crash if secrets are missing - core: append interface name for IPv6 link-local DNS server addresses (rh #720001) - core: fix setting hostname from DHCP options (rh #719100) - libnm-util: GObject introspection annotation fixes - libnm-util: ensure IP address/route prefixes are valid - ifcfg-rh: read anonymous identity for 802.1x PEAP connections (rh #708436) - applet: show notifications on CDMA home/roaming changes - applet: fix various issues saving VPN secrets - editor: allow exporting VPN secrets - editor: default to IPv6 "automatic" addressing mode- core: ensure users are authorized for shared wifi connections (CVE-2011-2176) (rh #715492) - core: retry failed connections after 5 minute timeout - core: immediately request new 802.1x 'always ask' passwords if they fail - core: add MAC blacklisting capability for WiFi and Wired connections - core: retry failed connections when new users log in (rh #706204) - applet: updated translations - core: drop compat interface now that KDE bits are updated to NM 0.9 API- core: don't cache "(none)" hostname at startup (rh #706094) - core: fix handling of VPN connections with only system-owned secrets - core: fix optional waiting for networking at startup behavior (rh #710502) - ifcfg-rh: fix possible crashes in error cases - ifcfg-rh: fix various IPv4 and IPv6 handling issues - applet: add notifications of GSM mobile broadband registration status - editor: move secrets when making connections available to all users or private - applet: don't show irrelevant options when asking for passwords- keyfile: better handling of missing certificates/private keys - core: fix issues handling "always-ask" wired and WiFi 802.1x connections (rh #703785) - core: fix automatic handling of hidden WiFi networks (rh #707406) - editor: fix possible crash after reading network connections (rh #706906) - editor: make Enter/Return key close WiFi password dialogs (rh #708666)- Bump for CVE-2011-1943 (no changes, only a rebuild)- editor: fix resizing of UI elements (rh #707269) - core: retry wired connections when cable is replugged - core: fix a few warnings and remove some left-over debugging code- compat: fix activation/deactivation of VPN connections (rh #699786) - core: fix autodetection of previously-used hidden wifi networks - core: silence error if ConsoleKit database does not yet exist (rh #695617) - core: fix Ad-Hoc frequency handling (rh #699203) - core: fixes for migrated OpenConnect VPN plugin connections - core: various fixes for VPN connection secrets handling - core: send only short hostname to DHCP servers (rh #694758) - core: better handling of PKCS#8 private keys - core: fix dispatcher script interface name handling - editor: fix potential crash when connection is invalid (rh #704848) - editor: allow _ as a valid character for GSM APNs- core: fix possible crash when connections are deleted - core: fix exported symbols in libnm-util and libnm-glib - core/applet: updated translations- core: ensure DER format certificates are correctly recognized (rh #699591) - core: fix WINS server handling in client helper libraries - core: enhance dispatcher script environment to include IPv6 and VPN details - applet: migrate openswan connections to 0.9 - editor: improve usability of editing IP addresses (rh #698199)- core: enable optimized background roaming for WPA Enterprise configs - core: better handling of WiFi and WiMAX rfkill (rh #599002) - applet: fix crash detecting Bluetooth DUN devices a second time - ifcfg-rh: fix managed/unmanaged changes when removing connections (rh #698202)- core: systemd and startup enhancements for NFS mounts - core: more efficient startup process - core: fix handling of multiple logins when one is inactive - core: fix handling of S390/Hercules CTC network interfaces (rh #641986) - core: support Easytether interfaces for Android phones - core: fix handling of WWAN enable/disable states - ifcfg-rh: harmonize handling if IPADDR/PREFIX/NETMASK with initscripts (rh #658907) - applet: fix connection to WPA Enterprise networks (rh #694765)- core: fix handling of infinite IPv6 RDNSS timeouts (rh #689291)- Update to 0.8.998 (0.9.0-rc1) - core: fix near-infinite requests for passwords (rh #692783) - core: fix handling of wired 802.1x connections - core: ignore Nokia PC-Suite ethernet devices we can't use yet - applet: migrate 0.8 OpenVPN passwords to 0.9 formats- core: resurrect default VPN username - core: don't stomp on crypto library users by de-initing the crypto library- core: fix creation of default wired connections - core: fix requesting new secrets when old ones fail (ex changing WEP keys) - editor: ensure all pages are sensitive after retrieving secrets - editor: fix crash when scrolling through connection lists (rh #693446) - applet: fix crash after using the wifi or wired secrets dialogs (rh #693446)- Fix trigger to enable the systemd service for upgrades (rh #678553)- core: fix connection deactivation on the compat interface - core: give default wired connections a more friendly name - core: fix base type of newly created wired connections - applet: many updated translations- core: fix possible libnm-glib crash when activating connections - applet: fix various naming and dialog title issues- nm-version.h should be in NetworkManager-devel, not -glib-devel (rh #685442)- core: add compatibility layer for KDE Plasma network infrastructure- Update to 0.8.997 (0.9-beta3) - ifcfg-rh: fix reading and writing of Dynamic WEP connections using LEAP as the eap method - wifi: fix signal strength for scanned access points with some drivers - applet: translation updates- Update to 0.8.996 (0.9-beta2)- applet: fix bus name more- applet: fix bus name- Fix systemd requires- Update to NetworkManager 0.9-beta1 - core: consolidate user and system settings services into NM itself - core: add WiMAX support - applet: support Fast User Switching- Rebuild against newer gtk- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild- Rebuild against new gtk- Handle modem IP interface changes after device is recognized- Rebuild against new gtk3- use --force in autoreconf to fix FTBFS- Rebuild against newer gtk- Update to 0.8.2- Rebuild against libnotify 0.7 - misc gtk build fixes- core: preserve WiFi Enabled state across reboot and suspend/resume- core: fix suspend/resume regression (rh #638640) - core: fix issue causing some nmcli requests to be ignored- core: preserve custom local-mapped hostnames in /etc/hosts (rh #627269)- core: remove stale /etc/hosts mappings (rh #630146)- core: add dispatcher events on DHCPv4 and DHCPv6 lease changes - core: enforce access permissions when enabling/disabling WiFi and WWAN (rh #626337) - core: listen for UPower suspend/resume signals - applet: fix disabled Enable Networking and Enable Wireless menu items (rh #627365) - applet: updated translations - applet: obscure Mobile Broadband PIN in secondary unlock dialog- core: fix some systemd interaction issues- core: rebuild to fix polkit 0.97 build issue - applet: updated translations- core: rebuild to fix dbus-glib security issue (CVE-2010-1172) (rh #585394)- core: quiet annoying warnings (rh #612991) - core: fix retrieval of various IP options in libnm-glib (rh #611141) - core: ship NetworkManager.conf instead of deprecated nm-system-settings.conf (rh #606160) - core: add short hostname to /etc/hosts too (rh #621910) - core: recheck autoactivation when new system connections appear - core: enable DHCPv6-only configurations (rh #612445) - core: don't fail connection immediately if DHCP lease expires (rh #616084) (rh #590874) - core: fix editing of PPPoE system connections - core: work around twitchy frequency reporting of various wifi drivers - core: don't tear down user connections on console changes (rh #614556) - cli: wait a bit for NM's permissions check to complete (rh #614866) - ifcfg-rh: ignore BRIDGE and VLAN configs and treat as unmanaged (rh #619863) - man: add manpage for nm-online - applet: fix crash saving ignore-missing-CA-cert preference (rh #619775) - applet: hide PIN/PUK by default in the mobile PIN/PUK dialog (rh #615085) - applet: ensure Enter closes the PIN/PUK dialog (rh #611831) - applet: fix another crash in ignore-CA-certificate handling (rh #557495) - editor: fix handling of Wired/s390 connections (rh #618620) - editor: fix crash when canceling editing in IP address pages (rh #610891) - editor: fix handling of s390-specific options - editor: really fix crash when changing system connections (rh #603566)- core: read nm-system-settings.conf before NetworkManager.conf (rh #606160) - core: fix editing system DSL connections when using keyfile plugin - core: work around inconsistent proprietary driver associated AP reporting - core: ensure empty VPN secrets are not used (rh #587784) - core: don't request WiFi scans when connection is locked to a specific BSSID - cli: show IPv6 settings and configuration - applet: updated translations - editor: fix a PolicyKit-related crash editing connections (rh #603566) - applet: fix saving the ignore-missing-CA-cert preference (rh #610084) - editor: fix listing connections on PPC64 (rh #608663) - editor: ensure editor windows are destroyed when closed (rh #572466)- Rebuild against new gnome-bluetooth- Update to 0.8.1 release candidate - core: fix WWAN hardware enable state tracking (rh #591622) - core: fix Red Hat initscript return value on double-start (rh #584321) - core: add multicast route entry for IPv4 link-local connections - core: fix connection sharing in cases where a dnsmasq config file exists - core: fix handling of Ad-Hoc wifi connections to indicate correct network - core: ensure VPN interface name is passed to dispatcher when VPN goes down - ifcfg-rh: fix handling of ASCII WEP keys - ifcfg-rh: fix double-quoting of some SSIDs (rh #606518) - applet: ensure deleted connections are actually forgotten (rh #618973) - applet: don't crash if the AP's BSSID isn't availabe (rh #603236) - editor: don't crash on PolicyKit events after windows are closed (rh #572466)- core: fix nm-online crash (rh #593677) - core: fix failed suspend disables network (rh #589108) - core: print out missing firmware errors (rh #594578) - applet: fix device descriptions for some mobile broadband devices - keyfile: bluetooth fixes - applet: updated translations (rh #589230)- core: use GIO in local mode only (rh #588745) - core: updated translations (rh #589230) - core: be more lenient in IPv6 RDNSS server expiry (rh #590202) - core: fix headers to be C++ compatible (rh #592783) - applet: updated translations (rh #589230) - applet: lock connections with well-known SSIDs to their specific AP- core: fix handling of IPv6 RA flags when router goes away (rh #588560) - bluetooth: fix crash configuring DUN connections from the wizard (rh #590666)- core: restore initial accept_ra value for IPv6 ignored connections (rh #588619) - bluetooth: fix bad timeout on PAN connections (rh #586961) - applet: updated translations- core: treat missing IPv6 configuration as ignored (rh #588814) - core: don't flush IPv6 link-local routes (rh #587836) - cli: update output formatting- core: allow IP configuration as long as one method completes (rh #567978) - core: don't prematurely remove IPv6 RDNSS nameservers (rh #588192) - core: ensure router advertisements are only used when needed (rh #588613) - editor: add IPv6 gateway editing capability- core: IPv6 autoconf, DHCP, link-local, and manual mode fixes - editor: fix saving IPv6 address in user connections- core: fix crash when IPv6 is enabled and interface is deactivated- core: fix issues with IPv6 router advertisement mishandling (rh #530670) - core: many fixes for IPv6 RA and DHCP handling (rh #538499) - core: ignore WWAN ethernet devices until usable (rh #585214) - ifcfg-rh: fix handling of WEP passphrases (rh #581718) - applet: fix crashes (rh #582938) (rh #582428) - applet: fix crash with multiple concurrent authorization requests (rh #585405) - editor: allow disabling IPv4 on a per-connection basis - editor: add support for IPv6 DHCP-only configurations- core: fix crash during install (rh #581794) - wifi: fix crash when supplicant segfaults after resume (rh #538717) - ifcfg-rh: fix MTU handling for wired connections (rh #569319) - applet: fix display of disabled mobile broadband devices- core: fix automatic WiFi connections on resume (rh #578141)- core: more flexible logging - core: fix crash with OLPC mesh devices after suspend - applet: updated translations - applet: show mobile broadband signal strength and technology in the icon - applet: fix continuous password requests for 802.1x connections (rh #576925) - applet: many updated translations- core: fix modem enable/disable - core: fix modem default route handling- core: don't exit early on non-fatal state file errors - core: fix Bluetooth connection issues (rh #572340) - applet: fix some translations (rh #576056) - applet: better feedback when wrong PIN/PUK is entered - applet: many updated translations - applet: PIN2 unlock not required for normal modem functionality - applet: fix wireless secrets dialog display- man: many manpage updates - core: determine classful prefix if non is given via DHCP - core: ensure /etc/hosts is always up-to-date and correct (rh #569914) - core: support GSM network and roaming preferences - applet: startup speed enhancements - applet: better support for OTP/token-based WiFi connections (rh #526383) - applet: show GSM and CDMA registration status and signal strength when available - applet: fix zombie GSM and CDMA devices in the menu - applet: remove 4-character GSM PIN/PUK code limit - applet: fix insensitive WiFi Create... button (rh #541163) - applet: allow unlocking of mobile devices immediately when plugged in- core: update to final 0.8 release - core: fix Bluetooth DUN connections when secrets are needed - ifcfg-rh: add helper for initscripts to determine ifcfg connection UUIDs - applet: fix Bluetooth connection secrets requests - applet: fix rare conflict with other gnome-bluetooth plugins- core: fix mobile broadband PIN handling (rh #543088) (rh #560742) - core: better handling of /etc/hosts if hostname was already added by the user - applet: crash less on D-Bus property errors (rh #557007) - applet: fix crash entering wired 802.1x connection details (rh #556763)- core: validate the autostart .desktop file - build: fix nmcli for the stricter ld (fixes FTBFS) - build: fix nm-connection-editor for the stricter ld (fixes FTBFS) - applet: don't autostart in KDE on F13+ (#541353)- core: add Bluetooth Dial-Up Networking (DUN) support (rh #136663) - core: start DHCPv6 on receipt of RA 'otherconf'/'managed' bits - nmcli: allow enable/disable of WiFi and WWAN- ifcfg-rh: read and write DHCPv6 enabled connections (rh #429710) - nmcli: update- core: clean NSS up later to preserve errors from crypto_init()- core: support for managed-mode DHCPv6 (rh #429710) - ifcfg-rh: gracefully handle missing PREFIX/NETMASK - cli: initial preview of command-line client - applet: add --help to explain what the applet is (rh #494641)- build: fix for new pppd (rh #548520) - core: add WWAN enable/disable functionality - ifcfg-rh: IPv6 addressing and routes support (rh #523288) - ifcfg-rh: ensure connection is updated when route/key files change - applet: fix crash when active AP isn't found (rh #546901) - editor: fix crash when editing connections (rh #549579)- core: fix recognition of standalone 802.1x private keys - applet: clean notification text to ensure it passes libnotify validation- core: remove haldaemon from initscript dependencies (rh #542078) - core: handle PEM certificates without an ending newline (rh #507315) - core: fix rfkill reporting for ipw2x00 devices - core: increase PPPoE timeout to 30 seconds - core: fix re-activating system connections with secrets - core: fix crash when deleting automatically created wired connections - core: ensure that a VPN's DNS servers are used when sharing the VPN connection - ifcfg-rh: support routes files (rh #507307) - ifcfg-rh: warn when device will be managed due to missing HWADDR (rh #545003) - ifcfg-rh: interpret DEFROUTE as never-default (rh #528281) - ifcfg-rh: handle MODE=Auto correctly - rpm: fix rpmlint errors - applet: don't crash on various D-Bus and other errors (rh #545011) (rh #542617) - editor: fix various PolicyKit-related crashes (rh #462944) - applet+editor: notify user that private keys must be protected- nm: better pidfile handing (rh #517362) - nm: save WiFi and Networking enabled/disabled states across reboot - nm: fix crash with missing VPN secrets (rh #532084) - applet: fix system connection usage from the "Connect to hidden..." dialog - applet: show Bluetooth connections when no other devices are available (rh #532049) - applet: don't die when autoconfigured connections can't be made (rh #532680) - applet: allow system administrators to disable the "Create new wireless network..." menu item - applet: fix missing username connecting to VPNs the second time - applet: really fix animation stuttering - editor: fix IP config widget tooltips - editor: allow unlisted countries in the mobile broadband wizard (rh #530981) - ifcfg-rh: ignore .rpmnew files (rh #509621)- nm: fix PPPoE connection authentication (rh #532862)- install: better fix for (rh #526519) - install: don't build Bluetooth bits on s390 (rh #529854) - nm: wired 802.1x connection activation fixes - nm: fix crash after modifying default wired connections like "Auto eth0" - nm: ensure VPN secrets are requested again after connection failure - nm: reset 'accept_ra' to previous value after deactivating IPv6 connections - nm: ensure random netlink events don't interfere with IPv6 connection activation - ifcfg-rh: fix writing out LEAP connections - ifcfg-rh: recognize 'static' as a valid BOOTPROTO (rh #528068) - applet: fix "could not find required resources" error (rh #529766)- install: fix -gnome package pre script failures (rh #526519) - nm: fix failures validating private keys when using the NSS crypto backend - applet: fix crashes when clicking on menu but not associated (rh #526535) - editor: fix crash editing wired 802.1x settings - editor: fix secrets retrieval when editing connections- nm: fix connection takeover when carrier is not on - nm: handle certificate paths (CA chain PEM files are now fully usable) - nm: defer action for 4 seconds when wired carrier drops - ifcfg-rh: fix writing WPA passphrases with odd characters - editor: fix editing of IPv4 settings with new connections (rh #525819) - editor: fix random crashes when editing due to bad widget refcounting - applet: debut reworked menu layout (not final yet...)- Install GConf schemas- nm: allow disconnection of all device types - nm: ensure that wired connections are torn down when their hardware goes away - nm: fix crash when canceling a VPN's request for secrets - editor: fix issues changing connections between system and user scopes - editor: ensure changes are thrown away when editing is canceled - applet: ensure connection changes are noticed by NetworkManager - applet: fix crash when creating new connections - applet: actually use wired 802.1x secrets after they are requested- nm: IPv6 zeroconf support and fixes - nm: port to polkit (rh #499965) - nm: fixes for ehea devices (rh #511304) (rh #516591) - nm: work around PPP bug causing bogus nameservers for mobile broadband connections - editor: fix segfault with "Unlisted" plans in the mobile broadband assistant- nm: add iSCSI support - nm: add connection assume/takeover support for ethernet (rh #517333) - nm: IPv6 fixes - nm: re-add OLPC XO-1 mesh device support (removed with 0.7.0) - applet: better WiFi dialog focus handling- Add patch to fix service detection on phones- nm: IPv6 support for manual & router-advertisement modes- Move some big docs to -devel to save space- Update to upstream 'master' branch - Use modem-manager for better 3G modem support - Integrated system settings with NetworkManager itself - Use udev instead of HAL- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild- applet: fix certificate validation in hidden wifi networks dialog (rh #508207)- nm: fixes for ZTE/Onda modem detection - nm: prevent re-opening serial port when the SIM has a PIN - applet: updated translations - editor: show list column headers- nm: fix serial port settings- nm: fix AT&T Quicksilver modem connections (rh #502002) - nm: fix support for s390 bus types (rh #496820) - nm: fix detection of some CMOtech modems - nm: handle unsolicited wifi scans better - nm: resolv.conf fixes when using DHCP and overriding search domains - nm: handle WEP and WPA passphrases (rh #441070) - nm: fix removal of old APs when none are scanned - nm: fix Huawei EC121 and EC168C detection and handling (rh #496426) - applet: save WEP and WPA passphrases instead of hashed keys (rh #441070) - applet: fix broken notification bubble actions - applet: default to WEP encryption for Ad-Hoc network creation - applet: fix crash when connection editor dialogs are canceled - applet: add a mobile broadband provider wizard- drop ExcludeArch s390 s390x, we need at least the header files- nm-save-the-leases.patch: Use per-connection lease files, and don't delete them on interface deactivate.- ifcfg-rh: fix problems noticing changes via inotify (rh #495884)- ifcfg-rh: enable write support for wired and wifi connections- nm: update to 0.7.1 - nm: fix startup race with HAL causing unmanaged devices to sometimes be managed (rh #494527)- nm: fix recognition of Option GT Fusion and Option GT HSDPA (nozomi) devices (rh #494069) - nm: fix handling of spaces in DHCP 'domain-search' option - nm: fix detection of newer Option 'hso' devices - nm: ignore low MTUs returned by broken DHCP servers- Update to 0.7.1-rc4 - nm: use PolicyKit for system connection secrets retrieval - nm: correctly interpret errors returned from chmod(2) when saving keyfile system connections - editor: use PolicyKit to get system connection secrets- nm: fix crashes with out-of-tree modules that provide no driver link (rh #492246) - nm: fix USB modem probing on recent udev versions- nm: fix communication with Option GT Max 3.6 mobile broadband cards - nm: fix communication with Huawei mobile broadband cards (rh #487663) - nm: don't look up hostname when HOSTNAME=localhost unless asked (rh #490184) - nm: fix crash during IP4 configuration (rh #491620) - nm: ignore ONBOOT=no for minimal ifcfg files (f9 & f10 only) (rh #489398) - applet: updated translations- nm: work around unhandled device removals due to missing HAL events (rh #484530) - nm: improve handling of multiple modem ports - nm: support for Sony Ericsson F3507g / MD300 and Dell 5530 - applet: updated translations- Missing ONBOOT should actually mean ONBOOT=yes (rh #489422)- Fix conflict with NetworkManager-openconnect (rh #489271) - Fix possible crash when resynchronizing devices if HAL restarts- nm: make default wired "Auto ethX" connection modifiable if an enabled system settings plugin supports modifying connections (rh #485555) - nm: manpage fixes (rh #447233) - nm: CVE-2009-0365 - GetSecrets disclosure - applet: CVE-2009-0578 - local users can modify the connection settings - applet: fix inability to choose WPA Ad-Hoc networks from the menu - ifcfg-rh: add read-only support for WPA-PSK connections- Fix getting secrets for system connections (rh #486696) - More compatible modem autodetection - Better handle minimal ifcfg files- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild- Use IFF_LOWER_UP for carrier detect instead of IFF_RUNNING - Add small delay before probing cdc-acm driven mobile broadband devices- Fix PEAP version selection in the applet (rh #468844) - Match hostname behavior to 'network' service when hostname is localhost (rh #441453)- Fix 'noreplace' for nm-system-settings.conf- Update to 0.7.1rc1 - nm: support for Huawei E160G mobile broadband devices (rh #466177) - nm: fix misleading routing error message (rh #477916) - nm: fix issues with 32-character SSIDs (rh #485312) - nm: allow root to activate user connections - nm: automatic modem detection with udev-extras - nm: massive manpage rewrite - applet: fix crash when showing the CA certificate ignore dialog a second time - applet: clear keyring items when deleting a connection - applet: fix max signal strength calculation in menu (rh #475123) - applet: fix VPN export (rh #480496)- applet: fix blank VPN connection message bubbles - applet: better handling of VPN routing on update - applet: silence pointless warning (rh #484136) - applet: desensitize devices in the menu until they are ready (rh #483879) - nm: Expose WINS servers in the IP4Config over D-Bus - nm: Better handling of GSM Mobile Broadband modem initialization - nm: Handle DHCP Classless Static Routes (RFC 3442) - nm: Fix Mobile Broadband and PPPoE to always use 'noauth' - nm: Better compatibility with older dual-SSID AP configurations (rh #445369) - nm: Mark nm-system-settings.conf as config (rh #465633) - nm-tool: Show VPN connection information - ifcfg-rh: Silence message about ignoring loopback config (rh #484060) - ifcfg-rh: Fix issue with wrong gateway for system connections (rh #476089)- Update to 0.7.1 pre-release - Allow connections to be ignored when determining the default route (rh #476089) - Own /usr/share/gnome-vpn-properties (rh #477155) - Fix log flooding due to netlink errors (rh #459205) - Pass connection UUID to dispatcher scripts via the environment - Fix possible crash after deactivating a VPN connection - Fix issues with editing wired 802.1x connections - Fix issues when using PKCS#12 certificates with 802.1x connections- API and documentation updates - Fix PIN handling on 'hso' mobile broadband devices- Fix PIN/PUK issues with high-speed Option HSDPA mobile broadband cards - Fix desensitized OK button when asking for wireless keys- Fix issues reading ifcfg files - Previously fixed: - Doesn't send DHCP hostname (rh #469336) - 'Auto eth0' forgets settings (rh #468612) - DHCP renewal sometimes breaks VPN (rh #471852) - Connection editor menu item in the wrong place (rh #471495) - Cannot make system-wide connections (rh #471308)- Update to NetworkManager 0.7.0 RC2 - Handle gateways on a different subnet from the interface - Clear VPN secrets on connection failure to ensure they are requested again (rh #429287) - Add support for PKCS#12 private keys (rh #462705) - Fix mangling of VPN's default route on DHCP renew - Fix type detection of qemu/kvm network devices (rh #466340) - Clear up netmask/prefix confusion in the connection editor - Make the secrets dialog go away when it's not needed - Fix inability to add system connections (rh #471308)- More reliable mobile broadband card initialization - Handle mobile broadband PINs correctly when PPP passwords are also used - Additional PolicyKit integration for editing system connections - Close the applet menu if a keyring password is needed (rh #353451)- Fix issues with hostname during anaconda installation (rh #461933) - Fix Ad-Hoc WPA connections (rh #461197) - Don't require gnome-panel or gnome-panel-devel (rh #427834) - Fix determination of WPA encryption capabilities on some cards - Fix conflicts with PPTP and vpnc plugins - Allow .cer file extensions when choosing certificates- Fix conflicts for older PPTP VPN plugins- Ensure that mobile broadband cards are powered up before trying to use them - Hostname changing support (rh #441453) - Fix mobile broadband secret requests to happen less often - Better handling of default devices and default routes - Better information in tooltips and notifications - Various UI cleanups; hide widgets that aren't used (rh #465397, rh #465395) - Accept different separators for DNS servers and searches - Make applet's icon accurately reflect signal strength of the current AP- Fix connection comparison that could cause changes to get overwritten (rh #464417)- Fix handling of VPN settings on upgrade (rh #460730, bgo #553465)- Fix hang when reading system connections from ifcfg files- Fix WPA Ad-Hoc connections- Fix parsing of DOMAIN in ifcfg files (rh #459370) - Fix reconnection to mobile broadband networks after an auth failure - Fix recognition of timeouts of PPP during mobile broadband connection - More compatible connection sharing (rh #458625) - Fix DHCP in minimal environments without glibc locale information installed - Add support for Option mobile broadband devices (like iCON 225 and iCON 7.2) - Add IP4 config information to dispatcher script environment - Merge WEP ASCII and Hex key types for cleaner UI - Pre-fill PPPoE password when authentication fails - Fixed some changes not getting saved in the connection editor - Accept both prefix and netmask in the conection editor's IPv4 page- Fix issue with mobile broadband connections that don't require authentication- Expose DHCP-returned options over D-Bus and to dispatcher scripts - Add support for customized static routes - Handle multiple concurrent 3G or PPPoE connections - Fix GSM/CDMA username and password issues - Better handling of unmanaged devices from ifcfg files - Fix timeout handling of errors during 3G connections - Fix some routing issues (rh #456685) - Fix applet crashes after removing a device (rh #457380)- Convert stored IPv4 static IP addresses to new prefix-based scheme automatically - Fix pppd connections to some 3G providers (rh #455348) - Make PPPoE "Show Password" option work - Hide IPv4 config options that don't make sense in certain configurations- Expose server-returned DHCP options via D-Bus - Use avahi-autoipd rather than old built-in IPv4LL implementation - Send hostname to DHCP server if provided (DHCP_HOSTNAME ifcfg option) - Support sending DHCP Client Identifier to DHCP server - Allow forcing 802.1x PEAP Label to '0' - Make connection sharing more robust - Show status for shared and Ad-Hoc connections if no other connection is active- Drop explicit hal dep in -gnome- Move VPN configuration into connection editor - Fix mobile broadband username/password issues - Fix issues with broken rfkill setups (rh #448889) - Honor APN setting for GSM mobile broadband configurations - Fix adding CDMA connections in the connection editor- Update to latest SVN - Enable connection sharing - Respect VPN-provided routes- Move NM later in the shutdown process (rh #449070) - Move libnm-util into a subpackage to allow NM to be removed more easily (rh #351101)- Read global gateway from /etc/sysconfig/network if missing (rh #446527) - nm-system-settings now terminates when dbus goes away (rh #444976)- Fix initial carrier state detection on devices that are already up (rh #134886)- Restore behavior of marking wifi devices as "down" when disabling wireless - Fix a crash on resume when a VPN was active when going to sleep- Fix issues with the Fedora plugin not noticing changes made by system-config-network (rh #444502) - Allow autoconnection of GSM and CDMA connections - Multiple IP address support for user connections - Fixes for Mobile Broadband cards that return line speed on connect - Implement PIN entry for GSM mobile broadband connections - Fix crash when editing unencrypted WiFi connections in the connection editor- Clean up the dispatcher now that it's service is gone (rh #444798)- Fix asking applets for the GSM PIN/PUK- Guess WEP key type in applet when asking for new keys - Correct OK button sensitivity in applet when asking for new WEP keys- Fix issues with Mobile Broadband connections caused by device init race patch- Fix device initialization race that caused ethernet devices to get stuck on startup - Fix PPPoE connections not showing up in the applet - Fix disabled OK button in connection editor some wireless and IP4 settings - Don't exit if HAL isn't up yet; wait for it - Fix a suspend/resume crash- Don't ask for wireless keys when the driver sends disconnect events during association; wait until the entire assocation times out - Replace dispatcher daemon with D-Bus activated callout - Fix parsing of DNS2 and DNS3 ifcfg file items - Execute dispatcher scripts in alphabetical order - Be active at runlevel 2 - Hook up MAC address widgets for wired & wireless; and BSSID widget for wireless - Pre-populate anonymous identity and phase2 widgets correctly - Clear out unused connection keys from GConf- Don't select devices without a default gateway as the default route (rh #437338) - Fill in broadcast address if not specified (rh #443474) - Respect manual VPN IPv4 configuration options - Show Connection Information for the device with the default route only- Add dbus-glib-devel BuildRequires for NetworkManager-glib-devel (rh #442978) - Add PPP settings page to connection editor - Fix a few crashes with PPPoE - Fix active connection state changes that confused clients- Fix build in pppd-plugin- PPPoE authentication fixes - More robust handing of mobile broadband device communications- Honor options from /etc/sysconfig/network for blocking until network is up- Turn on Add/Edit in the connection editor - Don't flush or change IPv6 addresses or routes - Enhance nm-online tool - Some serial communication fixes for mobile broadband- Fix issues with VPN passwords not getting found- Fix builds due to glib2 breakage of GStaticMutex with gcc 4.3- Fix WEP key index handling in UI - Fix handling of NM_CONTROLLED in ifcfg files - Show device managed state in applet menu - Show wireless enabled state in applet menu - Better handling of default DHCP connections for wired devices - Fix loading of connection editor on KDE (rh #435344)- Honor MAC address locking for wired & wireless devices- Show VPN failures - Support Static WEP key indexes - Fix parsing of WEP keys from ifcfg files - Pre-fill wireless security UI bits in connection editor and applet- Grab system settings from /etc/sysconfig/network-scripts, not from profiles- Fix crashes when returning VPN secrets from the applet to NM- Fix crashes on suspend/resume and exit (rh #437426) - Ensure there's always an option to chose the wired device - Never set default route via an IPv4 link-local addressed device (rh #437338)- Fix DHCP rebind behavior - Preliminary PPPoE support- Fix gnome-icon-theme Requires, should be on gnome subpackage- Honor DHCP rebinds - Multiple active device support - Better error handling of mobile broadband connection failures - Allow use of interface-specific dhclient config files - Recognize system settings which have no TYPE item- Fix crash of nm-system-settings on malformed ifcfg files (rh #434919) - Require gnome-icon-theme to pick up lock.png (rh #435344) - Fix applet segfault after connection removal via connection editor or GConf- Don't create multiple connections for hidden access points - Fix scanning behavior- Rework connection editor connection list- Better handling of changes in the profile directory by the system settings serivce- Enable system settings service - Allow explicit disconnection of mobile broadband devices - Fix applet memory leaks (rh #430178) - Applet Connection Information dialog tweaks (gnome.org #505899) - Filter input characters to passphrase/key entry (gnome.org #332951) - Fix applet focus stealing prevention behavior- Add CDMA mobile broadband support (if supported by HAL) - Rework applet connection and icon handling - Enable connection editor (only for deleting connections)- Fix crash when activating a mobile broadband connection - Better handling of non-SSID-broadcasting APs on kernels that support it (gnome.org #464215) (rh #373841) - Honor DHCP-server provided MTU if present (gnome.org #332953) - Use previous DNS settings if the VPN concentrator doesn't provide any (gnome.org #346833)- Fix WPA passphrase hashing on big endian (PPC, Sparc, etc) (rh #426233)- Fixes to work better with new libnl (rh #401761)- Fix WPA/WPA2 Enterprise Phase2 connections (rh #388471)- Fix applet connection comparison which failed to send connection updated signals to NM in some cases - Make VPN connection applet more robust against plugin failures- 64-bit -Wall compile fixes- Fix applet crash when choosing to ignore the CA certificate (rh #359001) - Fix applet crash when editing VPN properties and VPN connection failures (rh #409351) - Add file filter name in certificate file picker dialog (rh #410201) - No longer start named when starting NM (rh #381571)- Fix upgrading from an earlier rawhide snap- Fix device descriptions shown in applet menu- Fix crash when deactivating VPN connections- Fix crash and potential infinite nag dialog loop when ignoring CA certificates- Fix crash when ignoring CA certificate for EAP-TLS, EAP-TTLS, and EAP-PEAP- Fix connections when picking a WPA Enterprise AP from the menu - Fix issue where applet would provide multiple same connections to NM- Add support for EAP-PEAP (rh #362251) - Fix EAP-TLS private key handling- Clarify naming of WPA & WPA2 Personal encryption options (rh #374861, rh #373831) - Don't require a CA certificate for applicable EAP methods (rh #359001) - Fix certificate and private key handling for EAP-TTLS and EAP-TLS (rh #323371) - Fix applet crash with USB devices (rh #337191) - Support upgrades from NM 0.6.x GConf settings- Fix applet crash with USB devices that don't advertise a product or vendor (rh #337191)- Fix crash when getting WPA secrets (rh #355041)- Bring up ethernet devices by default if no connections are defined (rh #339201) - Fix crash when switching networks or bringing up secrets dialog (rh #353091) - Fix crash when editing VPN connection properties a second time - Fix crash when cancelling the secrets dialog if another connection was activated in the mean time - Fix disembodied notification bubbles (rh #333391)- Handle PEM certificates - Hide WPA-PSK Type combo since it's as yet unused - Fix applet crash when AP security options changed and old secrets are still in the keyring - Fix applet crash connecting to unencrypted APs via the other network dialog- Fix WPA Enterprise connections that use certificates - Better display of SSIDs in the menu- Fix getting current access point - Fix WPA Enterprise connections - Wireless dialog now defaults to sensible choices based on the connection - Tell nscd to restart if needed, don't silently kill it- Suppress excessive GConf updates which sometimes caused secrets to be cleared at the wrong times, causing connections to fail - Various EAP and LEAP related fixes- Make WPA-EAP and Dynamic WEP options connect successfully - Static IPs are now handled correctly in NM itself- Add Dynamic WEP as a supported authentication/security option- Re-enable "Connect to other network" - Switch to new GUI bits for wireless security config and password entry- Add rfkill functionality - Fix applet crash when choosing wired networks from the menu- Fix segfault with deferred connections - Fix default username with vpnc VPN plugin - Hidden SSID fixes- Fix merging of non-SSID-broadcasting APs into a device's scan list - Speed up opening of the applet menu- New snapshot - Add timestamps to networks to connect to last used wireless network - Turn autoconnect on in the applet - Hidden SSID support - Invalidate failed or cancelled connections again - Fix issues with reactivation of the same device - Handle connection updates in the applet (ex. find new VPN connections) - Fix vertical sizing of menu items - Fix AP list on wireless devices other than the first device in the applet - Fix matching of current AP with the right menu item- New snapshot - Add WPA passphrase support to password dialog - Applet now reflects actual VPN behavior of one active connection - Applet now notices VPN active connections on startup - Fix connections with some WPA and WEP keys- New snapshot - VPN support (only vpnc plugin ported at this time)- New snapshot - Make wired device carrier state work in the applet - Fix handling of errors with unencrypted APs - Fix "frozen" applet icon by reporting NM state better - Fix output of AP frequency in nm-tool- New snapshot - Fix applet icon sizing on start (mclasen) - Fix nm-tool installation (mclasen) - Fix 'state' method call return (#303271) - Fix 40-bit WEP keys (again) - Fix loop when secrets were wrong/invalid - Fix applet crash when clicking Cancel in the password dialog - Ensure NM doesn't get stuck waiting for the supplicant to re-appear if it crashes or goes away - Make VPN properties applet work again - Increase timeout for network password entry- New snapshot (fix unencrypted & 40 bit WEP)- New snapshot- New snapshot- New SVN snapshot of 0.7 that sucks less- Update to SVN snapshot of 0.7- Update the license tag- Own /etc/NetworkManager/dispatcher.d and /etc/NetworkManager/VPN (#234004)- Fix Wireless Enabled checkbox when no killswitches are present- Update to stable branch snapshot: - More fixes for ethernet link detection (gnome #354565, rh #194124) - Support for HAL-detected rfkill switches- Fix applet crash on 64-bit platforms when choosing "Connect to other wireless network..." (gnome.org #435036) - Add debug output for ethernet device link changes- Fix ethernet link detection (gnome #354565, rh #194124) - Fix perpetual credentials request with private key passwords in the applet - Sleep a bit before activating wireless cards to work around driver bugs- Don't spawn wpa_supplicant with -o- Fix requires macro (237806)- Update to 0.6.5 final - Don't lose scanned security information- Update from trunk * Updated translations * Cleaned-up VPN properties dialogs * Fix 64-bit kernel leakage issues in WEXT * Don't capture and redirect wpa_supplicant log output- Close private D-Bus connections. (#232691)- Fix a directory ownership issue. (#233763)- Update to pre-0.6.5 snapshot- Guard against D-Bus LimitExceeded messages- Move .so file to -devel package- Own the /etc/NetworkManager/dispatcher.d directory - Require pkgconfig for the -devel packages - Fix compilation with dbus 1.0- Update to a stable branch snapshot - Gnome applet timeout/redraw suppression when idle - Backport of LEAP patch from HEAD (from Thiago Bauermann) - Backport of asynchronous scanning patch from HEAD - Make renaming of VPN connections work (from Tambet Ingo) - Dial down wpa_supplicant debug spew - Cleanup of key/passphrase request scenarios (from Valentine Sinitsyn) - Shut down VPN connections on logout (from Robert Love) - Fix WPA passphrase hashing on PPC- Own /usr/share/NetworkManager and /usr/include/NetworkManager- Don't wake up to redraw if NM is inactive (#204850)- add epochs in requirements- Fix FC-5 buildreqs- Revert FC6 to latest stable NM - Update to stable snapshot - Remove bind/caching-nameserver hard requirement- BuildRequire wireless-tools-devel and perl-XML-Parser - Update the BuildRoot tag- add patch to make networkmanager less verbose (bug 202832)- actually make the patch in 0.7.0-0.cvs20060529.4 apply- Don't ever elect inactive wired devices (bug 194124).- Add patch to fix deprecated dbus functions- Add BR for dbus-glib-devel- rebuild- Update to latest CVS o Gnome.org #333420: dialog do not have window icons o Gnome.org #336913: HIG tweaks for vpn properties pages o Gnome.org #336846: HIG tweaks for nm-vpn-properties o Gnome.org #336847: some bugs in nm-vpn-properties args parsing o Gnome.org #341306: nm-vpn-properties crashes on startup o Gnome.org #341263: Version 0.6.2-0ubuntu5 crashes on nm_device_802_11_wireless_get_type o Gnome.org #341297: displays repeated keyring dialogs on resume from suspend o Gnome.org #342400: Building libnm-util --without-gcrypt results in linker error o Gnome.org #342398: Eleminate Gnome dependency for NetworkManager o Gnome.org #336532: declaration of 'link' shadows a global declaration - Specfile fixes (#rh187489#)- Update to latest CVS - Drop special-case-madwifi.patch, since WEXT code is in madwifi-ng trunk now- use the same 0.6.2 tarball as FC5, so we have the same VPN interface (did he fire ten args, or only nine?)- use the hal device type instead of poking via ioctl so that wireless devices are properly detected even if the kill switch has been used- Update to 0.6.2: * Fix various WPA-related bugs * Clean up leaks * Increased DHCP timeout to account for slow DHCP servers, or STP-enabled switches * Allow applet to reconnect on dbus restarts * Add "Dynamic WEP" support * Allow hiding of password/key entry text * More responsive connection switching- Fix device bringup on resume- Don't let wpa_supplicant perform scanning with non-WPA drivers- Update to 0.6.0 release - Move autostart file to /usr/share/gnome/autostart- updated cvs snapshot. seems to make airo much less neurotic- Move the unversioned libnm_glib.so to the -devel package- Fix VPN-related crash - Fix issue where NM would refuse to activate a VPN connection once it had timed out - Log wpa_supplicant output for better debugging- Tweak three-scan-prune.patch- Don't prune networks until they've gone MIA for three scans, not one.- Update snapshot, which fixes up the libnotify stuff.- Move libnotify requires to NetworkManager-gnome, not core NM package- Add BuildRequires: libnl-devel (#rh179438#) - Fix libnm_glib to not clobber an application's existing dbus connection (#rh177546#, gnome.org #326572) - libnotify support - AP compatibility fixes- Minor bug fixes - Update to VPN dbus API for passing user-defined routes to vpn service- Rebuild- rebuilt for new gcc4.1 snapshot and glibc changes- Workarounds for madwifi/Atheros cards - Do better with non-SSID-broadcasting access points - Fix hangs when access points change settings- Own /var/run/NetworkManager, fix SELinux issues- Switch to autostarting the applet instead of having it be session-managed - Work better with non-broadcasting access points - Add more manufacturer default SSIDs to the blacklist- Longer association timeout - Fix some SELinux issues - General bug and cosmetic fixes- Snapshot from CVS - WPA Support! Woohoo!- rebuilt- rebuild for new dbus- Don't kill the network connection when you upgrade the package.- Split out the -glib subpackage to have a -glib-devel package as well - Add epoch to version requirements for bind and wireless-tools - Update URL of project- NetworkManager 0.5.1- NetworkManager 0.5.0- Fix automatic wireless connections - Remove usage of NMLoadModules callout, no longer needed - Try to fix deadlock when menu is down and keyring dialog pops up- Update to latest CVS o Integrate connection progress with applet icon (Chris Aillon) o More information in "Connection Information" dialog (Robert Love) o Shorten time taken to sleep o Make applet icon wireless strength levels a bit more realistic o Talk to named using DBUS rather than spawning our own - You need to add "-D" to the OPTIONS line in /etc/sysconfig/named - You need to set named to start as a service on startup- Update to current CVS to fix issues with routing table and /sbin/ip- update to current CVS and rebuild (workaround for #168120)- Fix occasional hang in NM caused by the applet- Update to NetworkManager 0.4.1- Rebuild against new cairo/gtk- Update to latest CVS o Use DHCP server address as gateway address if the DHCP server doesn't give us a gateway address #rh165698# o Fixes to the applet (Robert Love) o Better caching of information in the applet (Bill Moss) o Generate automatic suggested Ad-Hoc network name from machine's hostname (Robert Love) o Update all network information on successfull connect, not just authentication method- Update to latest CVS to get fix for bug 165683.- Move pkgconfig file to devel package (#162316, thanks to Michael Schwendt)- Update to latest CVS to get latest VPN interface settings to satisfy BuildReq for NetworkManager-vpnc in Fedora Extras Development - Latest CVS also contains various bug- and UI-fixes- Update to latest CVS o VPN connection import/export capability o Fix up some menu item names - Move nm-vpn-properties.glade to the gnome subpackage- Update to latest CVS o Clean up wording in Wireless Network Discovery menu o Robert Love's applet beautify patch- Update to latest CVS- Fix dispatcher and applet CFLAGS so they gets compiled with FORTIFY_SOURCE- Fix segfault in NetworkManagerDispatcher, add an initscript for it- Fix condition that may have resulted in DHCP client returning success when it really timed out- Enable OK button correctly in Passphrase and Other Networks dialogs when using ASCII or Hex WEP keys- #rh154391# NetworkManager dies on startup (don't force-kill nifd)- Fix leak of a socket in DHCP code- Fix some memory leaks (Tom Parker) - Join to threads rather than spinning for their completion (Tom Parker) - Fix misuse of a g_assert() (Colin Walters) - Fix return checking of an ioctl() (Bill Moss) - Better detection and matching of hidden access points (Bill Moss) - Don't use varargs, and therefore don't crash on PPC (Peter Jones)- fix build with newer dbus- silence %post- #rh153234# NetworkManager quits/cores just as a connection is made- Update from latest CVS HEAD- Update the GTK+ theme icon cache on (un)install- Pull from latest CVS HEAD- Upload new source tarball (woops)- Pull from latest CVS HEAD (hopefully works again)- Pull from latest CVS HEAD - Commit broken NetworkManager to satisfy to dbus dependency- Pull from latest CVS HEAD - Rebuild for gcc 4.0- Update from CVS- Fix free of invalid pointer for multiple search domains- Never automatically choose a device that doesn't support carrier detection - Add right-click menu to applet, can now "Pause/Resume" scanning through it - Fix DHCP Renew/Rebind timeouts - Fix frequency cycling problem on some cards, even when scanning was off - Play better with IPv6 - Don't send kernel version in DHCP packets, and ensure DHCP packets are at least 300 bytes in length to work around broken router - New DHCP options D-BUS API by Dan Reed - Handle multiple domain search options in DHCP responses- Display wireless network name in applet tooltip - Hopefully fix double-default-route problem - Write out valid resolv.conf when we exit - Make multi-domain search options work - Rework signal strength code to be WEXT conformant, if strength is still wierd then its 95% surely a driver problem - Fix annoying instances of suddenly dropping and reactivating a wireless device (Cisco cards were worst offenders here) - Fix some instances of NetworkManager not remembering your WEP key - Fix some races between NetworkManager and NetworkManagerInfo where NetworkManager wouldn't recognize changes in the allowed list - Don't shove Ad-Hoc Access Point MAC addresses into GConf- Play nice with dbus 0.23 - Update our list of Allowed Wireless Networks more quickly- Update to latest CVS - Make sure we start as late as possible so that we ensure dbus & HAL are already around - Fix race in initial device activation- rebuilt against new wireless tool- Fix issue where NM wouldn't recognize that access points were encrypted, and then would try to connect without encryption - Refine packaging to put client library in separate package - Remove bind+caching-nameserver dep for FC-3, use 'nscd -i hosts' instead. DNS queries may timeout now right after device activation due to this change.- Update to latest CVS - Fixes to DHCP code - Link-Local (ZeroConf/Rendezvous) support - Use bind in "caching-nameserver" mode to work around stupidity in glibc's resolver library not recognizing resolv.conf changes - #rh144818# Clean up the specfile (Patch from Matthias Saou) - Ad-Hoc mode support with Link-Local addressing only (for now) - Fixes for device activation race conditions - Wireless scanning in separate thread- Update to CVS - Updates to link detection, DHCP code - Remove NMLaunchHelper so we start up faster and don't block for a connection. This means services that depend on the network may fail if they start right after NM - Make sure DHCP renew/rebinding works- Update to CVS - Fixes to link detection - Better detection of non-ESSID-broadcasting access points - Don't dialog-spam the user if a connection fails- Update to CVS - Much better link detection, works with Open System authentication - Blacklist wireless cards rather than whitelisting them- #rh134893# NetworkManagerInfo and the panel-icon life-cycle - #rh134895# Status icon should hide when in Wired-only mode - #rh134896# Icon code needs rewrite - #rh134897# "Other Networks..." dialog needs implementing - #rh135055# Menu highlights incorrectly in NM - #rh135648# segfault with cipsec0 - #rh135722# NetworkManager will not allow zaurus to sync via usb0 - #rh135999# NetworkManager-0.3.1 will not connect to 128 wep - #rh136866# applet needs tooltips - #rh137047# lots of applets, yay! - #rh137341# Network Manager dies after disconnecting from wired network second time - Better checking for wireless devices - Fix some memleaks - Fix issues with dhclient declining an offered address - Fix an activation thread deadlock - More accurately detect "Other wireless networks" that are encrypted - Don't bring devices down as much, won't hotplug-spam as much anymore about firmware - Add a "network not found" dialog when the user chooses a network that could not be connected to- Fix escaping of ESSIDs in gconf- minor point release to improve error handling and translations- Update from CVS, version 0.3- Update from CVS - Improvements: o Better link checking on wireless cards o Panel applet now a Notification Area icon o Static IP configuration support- Update from CVS- Require gnome-panel, not gnome-panel-devel - Turn off by default- Update to 0.2- spec-changes to req glib2 instead of glib- First public releaseNetworkManagerNetworkManager-bt1:1.26.0-8.el81:1.26.0-8.el81:0.9.9.95-1.build-id8702925be4f0737ec8739f22fdd47ceff5c28200libnm-device-plugin-bluetooth.so/usr/lib//usr/lib/.build-id//usr/lib/.build-id/87//usr/lib64/NetworkManager/1.26.0-8.el8/-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protectioncpioxz2x86_64-redhat-linux-gnudirectoryELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=8702925be4f0737ec8739f22fdd47ceff5c28200, strippedPRRRRR R RRRRR RRR RR RRRutf-8919f7a9ccffab9b7cf4b617adc64cdae87aeab1472e1cd71906d13aacbc7d962?7zXZ !#,W/] b2u jӫ`(xy0QxVjCԎFg>噧<@N@4$D&l6}M(#ڥX  56B2 H2ɿ`RuWᄴ̯b)̠_ָ Z"8ܩ먛Y*Y pCqMq)^ uZ͙%M64tԵtZghg gQ^$fqPo`g|]ur;Y*L#ЉC֚Tɢ=XnsÅM},pt$h#VICu*)'k̼TKFCK$\^-b{U_V [>hǭh Z%Z$FˢT4 XS?_r22FX6%Ei zh$Cv1n=yruJA P~HBs.s6oMMV $ԣs}1tݒys'O[S[=]Xm՘s$TY96Fdom_vItE3du{ˁl @*1(+JHa#`XXnڝ0,E?Y .*-׉'Hl:,Wa/'$2QӝuRB[e`_]L=? ވErI4 r5Ilk 颣ͩك1wߴyћ!,w>P%iݱ }Ng&`7~SwC$K_bZ+nD]]zF*iGYKSʽE_wE3,n3shnz[(x`]"Q-#^OGh i~vL^(BngR/t7@z8i)AFL~$' C;hSqM&Ԑ`%=QOkُY%}JpJhLlLs2ē$vjS+K`Ҽ+pp&һ 5lMW0nfQ@押sڴN;W`䅽/MYVD/"wvp@juIsaE#g *BcgzS3 k414(T!/]Z)}ƘLr 'fM6ϕs1B-L4V//Qg1Ga߰܉ bvVBܠ GR_nr za8tgͽ!wJ~6C^|* cM>L_m ŖKne%Oo6֘\Dܣ΋ʘ*)&"D,$76|Ӷ"<C\ dæ2~Ⱥ(xWbi -$B fxg^ [6*Ʉ@˶A!RKZ)On3Кg8t񀁶%T>Beok N#\p,j=У)x\O(:"OGM*} Ha8'tmLxL9ɷ`Pa @_ kI3j`t73z <>9`SY2!0I=6~ۣřrj$r2bC΁W ʝh{LJ*+oMw[/-/޻|hA#e!=?fudlg *hEB k9ߊT!ǁ[o6E_o->hZT# ^~P&d#Mz%^#uRd{m时hT,$kE8~hn n p,!c ;$v41n$d2-C-]EBj2v='!?)XX^p]i:}ZGSӒ'Jyl3p<ܤ;oH)w{VjsLa*sׅk j嘳։ͦ޾UUQs}㌣n"ҪS!V /+F" EFpd~$ߑ0I-<K}+x#b 3UA1[< ku V;&Qz>sdYoExE|yqn9Mte3D;_Y h}>kYST~t`dXjm3 lAyoq4YkˏϜGI\*.DA .Dhϗ>H%6N^Z= y8'#h5?lGS.c⽗Q٢K1Up|Z[@U_W|u VBöǩA;BcY { c0PAnK+׭"WHr-!tp%g/:? jPj$QؾW*TɊ G02juWFDb~-޶ouɗSW 'lm-tDCZb!Ջ_Tߪ@[!򦾦%a=_1[qSޕ4䏾@4Zn&gV6"L;0uHqtV z4<Ry~$R>nJ^Z,#'г=25Т"R~UHDNt`b@$c?:f cUN82y:s({.wV#LS=znWM;z-5} 2묻 -Q/c6}3e ;?~Q4@ bwȱ¼p.Ԙ67O~pͷtBםf@\dq.tRk7= Peyr=#Gn f1cܺQh"e?<\[ <:fμ1a 6z~Vf8bZ4in!:Vjzuez/΂~_[6+UxӆĽRX*y*;7!NH2e im(H`ς;|_#A ;ApJ[Khc uMx~:5RC~/s^UC^W [QƊ fwLzk^ߝmn&f}U=/{3\R$Nz􌳊2Db$eQ8< G*ks3I/ bxYf0w~$;5²kŸB νʹjgְҺU66lwWDT=?$v^7Ygbȷ@!jCs%[C#~s A|"m[,V(-;nB"%vo[hϢy"?A |; 2ͦ|ssDݹz c, YL =%, wgDc5І)T!~Жz+wIk ُ4G~O2,eQB6ui>J$wt\6[ 3;b|FaWE#YFKU+㢣Jc`ƴ)vTP~ЁEs+'1Оĝn??~?221x՚"4OM{Ҏ45c1Ҍ_Q0C Tl؜9wt9kvזx6E?`JBP\ D[(OOLgS2x6r!beĘV'*-j~."nbxpfsm( ikxzg.\cj mǍ&d4be18%1TWci;=VeݱLl$d?[f]S2 R/Uq,J9~Lǿ/" Od5c3*|U!P2j{3ko^W;a0Z o6OtX E[ ,GR*c|ӹZ87+EOp8ͬYo!m nUfM\(HyEI`yx׎=3f5m%!=O|᭍Sdk ' ,:@+NBE}rlix Ky11Sr”"J Evv)< #,zEV|mZչLAh9'zk BmaO9gSV% } &KFSyԇ=_@X!T=èʩ1_* u`2zhkW6x cq.W*)~f{vmv /Ou='jlǸ\]g;<=X$砲 >Q^(_0@PcV IoĊ˖1A<rJZל^f=C\`:bt9\~ "ryI؆[I &F+Y=uNR:km|y!`nLˌ"bcæd,i3ٍ6p{B*d @׆jB픪 6$6F+8ՕL],-Q3׶DX sb["`Nj٤KYq$X&dMIs$n%^b,rd;k|^ؕјAɡ}q N>+1 7NIivI$00LDX[.,;B%̱y^oO6,Wl9gYϼD*'qwb㵍rvxSCd"A]:/ o@Ղl:2{RC w_&̒~\vP[H^>HūɡRTdhgv ')7mu.ݾkǒ`V<0w?GRn Z55E7zw2iזm$29ͬ;eVQB´k఺?Jaaw<X8IW}~iz6w.qEI:;;z*-r4Tؕަ\] i~S4` Ve[my+f&&kk@ޥM'eeܦAsO,QŽr}DQ#zCMɍ}GQӏJ(>hUwDƩ> (m2a~'Xp)棠ZSIzL[IpV*ӡA[b#BMd{7L T8Nޅ=kڸQ= ضZϟl{ȫ K'/)]/fnk?x@ p$[j d{#TnJzZӿj43#M!i2ƗP0sA?>o[U` 3 N\Kd'v2,mCPYdl ·+{قt Q~/ <]co֠^e E̩{MՌt9X|9_p}z5wjTʣ@-L_ʮnXֽzdg 6hG]y @ a5O{nX2!}?Eq9).O.w۲ȍN nIlAL#-./}h] Ne>p`?V8CWh+h&*F_AF䭬NLYb9p3k˶ߣ=_4ؗ)ïqMAĨN5٪^͵1":70ix-X toMh+t4h7I$- hۈ5R|s{GrO3)l*P-,{f0 YO 4-ʮ/kG݇nvxcr5vpԼv=%\EGc{|/, KO / ̉;um~#c+{= m\l;b?]ޜA$z24,v$m> 4YJtIdLarDz+>F_5LoAbD`m2#;L,l@):'G0)q1W0҂ܱ_k"\J(]f)r#M;j0UՏ [M~n _v͍4p`$Q je-8 )|Q-^8@V-}p |`LWTPA۸[bIAv4ܟ7TL+gX>p=.Qq4bJGIRvdMWt]{Rؓ%s ny'/lesɨ89)guEi3dO7? B3GNeUPN_g9nj@Zm'uS;6oMa1eSjӸGIE^aɑ~_j)5(N˽cѥ8LT|;y]#Qzҹ=g?ǯr3-%d{U7he7ȎJ`~&{U>QPc;HS>̀>V3 s~c勞Bb0A&=r=V QQWR 5? P#uܝ&scn_o}gxQ-vIgG8J} g3hclI5\%.x&H+%U~-2oabLדա]XM@ cݲ(2Qm͡ekgYwn6L@8tҊVU\꺿X5'nX8 L_N_h8.HH+7ff}k9ht hqSA[&(@)&VyA°z!ɐrwT [uw% ўuaG^@#ȗ@T}E{}Zrp+'~F<]i$pa"-A/n4__ $Wf̶6~)Ɲs 7&D,`>،Ng.*:cHbQ%P1lC" 2/ rC.\eӄ :[R [T0U%M-ۢE ?Y'Ìd@ұLeV E?G-Dk)do$g]7a \"JQ'6C>~?;-Q@lC:(15guU lHtfEHl0VI7-E̡!dN^3B}eODGOe1il} =\ oL^c}.[tb%j"`Q^Pʰ"I砖 K ť:a4ClFst=6;/?۩C@ҟF״1up0kG#AR܍1:.G]3dve%7~GDzT/]D,/BvY ڊ}[#ge"wK]20<[nh=&6ʍ`7pLlA;"buS}eOtĭڟ&9ZIk8-뒾o}p +9Z 9 @ X f AHB ƁuzP&m@5A4AP#+'yeu3Ķ9CҚA01k ,fVfY2N~=V0IR@n)S-y/C^p4GsƟWkS'?jwe&J6 ;ܫ$@ N}cȹIC CA_Y_ǧ9DGi#*HBRc `:(+[ppx)`|jfcvuAdzJ֘9o|$M;:)aʪq of.z R0} yw*;D3#a=Xz ~]# -yq9n,qm.Z &I\(㣣WW zى^e;I=~RS&\?ț :Y #gc]I w#.O!K,o%X!( qIZ/a? &LuKzacb[..yC^)7B9/ 8ԭ?];I[) B,.Dʑ6l4RN[UQO- #~O2$]4OO 9rG2[S8&ݍm.oVk~I%ȿd}lԦ8]f$& Ym#9>cX0MӉrode{i+YȤ g~TQW3D㺊mȚ/9ub@kFHRB}u8>}"jT6&~30(۱imWts`CRkF&(8NYGj xiWz;&- Ӕ?,d[Ù!Z$>M"Goېrɽ/|; VA\U6av;E{a)b߻z Zy=it##@n6,)ă uB:AimZړU,~LX~`wD#]hǦN[Nb~d" 6 O^ `pMy 麧dB81̲e%8l,57Μ{Aװy96v㓡u]azBP&P+\,P ;2\8uq!'tҿBI2W).wheGmv"KQAwvbt> 2kbnx_lt뾦P{wiOe!bQMM)r0HQϡ7%EX*3f{4::-]e^KZ1}fˠ>ls 8 _ɺ>zg0]S_Y mA$&`p}{* YzvآuA܈wm-XGiAfa `e 0inW웗8a3rL!C^ ?=:Bdn}Nys@ןꫬ)@<^F]lwRVÖHfZ۾1[T6bv&-I~'~յȭI \dPVa;LNIRV5bRY.}CF\ю$S>gzVбxrϐ>/ז+h4ǵ4o.֡4䂟d.elL/'w_~ . ~bx[d֪vS 4w2 -j!%+K^جnyT|C A ,OԬ|PhȔU6"gݎ8溒 *#-*"ۛ368&rϹ WO{[*s;W>®'&æg]!#2VJ3~ֶ\j}mv\N"_ n_c5%'w⫕Mi]w+ {@J^/MѦ`[T)QÉvdyOM a+ۨg6_sjQɂӔhij_ YNFN<$[LJCڧƟ1Dk '\o?ߐfmScX<:ibƍj#!?8Q!#>Uf$sQ T_) xl#\v!eYƾ2z2~Afv|[ʆRa-ߌQX Y4^2"@#0Utko2~?,33u?HB"=2'xU83!ŻWYQn(wSN{ѣ Dԁvg+Lbۂ0(:ᤛ*L~| |gL'Xz/Wp\ [I,l`*ה ߮VSqZx*=u!` Gԯ/U:al Lf =\-*jW_ATD2D Lי`#tx9Dwr0~)$HgKYw.ZY/e7ކ#$RUljs0G;@ 6kj RO*J2TeehЄtEq ϭNW]Z@Ҩ7_w]33v3J-]Bǝ Ma'勮ǥ)׊ͫXA2nK`[ɹ Ry239~ vlryccHRJWyYpmU;ʁ=V?<+e4Џ[>է'll)#h6o1wWU .BdtP$p?#1RtG_< .e䞾,*[^,=ǻdӏzϐ V6]sr^oG% -QΐɛkŢ8娊W'!?t07@$aO+k'#7Y(s< &L7U m3ǭl+@ YoxZ3mIh[5Ⱥ Ol.b.[. `g17͊fcKWm"Q:Ga5e7d+f{= 37+J}mKAoncOh9UpDP3ԼxT1U"YtLs!UςԭGGT,iwfLg RmUTrjQbz]Ţ`^rPe\pT*k>DL=ڭx> AYxh&faY}3Y %\ca4rTVMg/!.aR鐧TOr~p% 'Irgbn D _q NKyҢqdDp#*Ƨ% (@zL Qq Fh{_ ʐTן%< >,Eѷ9)_U5vStjq_'z=[C c0Xd{Iai+YĵSnK.hc<-s0^* /PFCfIvqT*U1L;rz]{DI_YCftSu8e}My J:,۷ X{wFG;=y݁Cp+w=kʹԎ||TYS]Ψa`-#Y]lSD1c9~&i 0@0 ᦷĎTim_Uh.S(&qۇǂ#N )=MqMꞒ5U\G{rb+Eߣ6DRxJ[EiA[~f~1jW =iY0@lP`| !'b6~&\!'Ioc胴\h[V'" 2xwP$].'-lS:Ʃ h!=O0]@8 a!uoKJ9۸`Dt86o>8h] ƍiz25Q2넧fcʹXm6-p?6r0;S}\k6|0bFTЋ(,K>QU()_"|4@DE84H*~ 0C@q ƛ[AͼuA?W䫭L#/2q =-\oi UfOV1`]!i~7鰰UFRh o53>Ӊ/dcI;h82f0/T-D2ە6s饽ƭVvPG6%R;DeN%c}zQ$EB4M A?8Jy=/ ׾r -mF CItj"heSs,TJgFʸ4~H!:N?2u%,!$M57k: o|M!aJ]^>ylPM`euuQ [E\2H{ snA^Ilv-l&ӨQD5k3VױgM"z3PX/r CITQA">.N`t=]Z$:Jv~6,l~ߒKJ;ќ1ji"aBX7t*{_ W3"l.3e A5vQH$|)Wd}Mo"e=~¹+%<Hr&okxy<2ȯV&oJ!d7mKj)jҭ|355wC<P9U#2ё O Z@`X6;F5 bx5&n o4t-h59v F0uNG[5R""Jqҽ $,vEʓZ xo$b!,9eުwCoN" @^g5RK K@Q+L^m*ҵXC\OjN*:?}^B\s 2G5̠:yrJ+,TN`2;y Iٌ|NA$vBX[EKө'kW)7/[6ta#mts׈trӝU\Mrł٦M#{|?ĊG#,+π$ o;?W#̕+^Hf—JthU* <~>x"8gn* 6]$z 4#| bWֶ4vp}= &hnژO~_.$e L:J,Pī%a*!qVge!aIyc&Zka{5sE[K?R<5P"8IjLJ "HHʡmߌ53F"@}nQvDRg2. 툷;iV{ `P㏭L]&ťL<؀s _K@JH/׳Tν,N"_P%?&g46Jze`1e{SĠЊ0Qs&)@өNY0L~wXLϵ'健}b:#5#8BPd bp660^a;UoN!Eg}:5\U}Q. qeV i\%kw1^e{r"EZT;:Dq*;&\ib.F¥X2]F/[kAauDZ^FRX8roA#cZMc~Ɨ8tP\pg?MxBU 螕i412 D^E 3^Gb[GS9Q L$̢NJ/8@[iDn8C@0=TQEjpWƎ߿v?^ȅ-c7o3 p,5]%`+. yxw̪ZZ)+'|/tzVݴ{i8fCgCC{CᇭA _S6xPY2/QCSiNzN0CdyxȪ&a0+H}ׁht{QEoi%1 WSY<6w}Y XA8"=$U5$#h6qxz]&'1CI)fku lt6si6M}k/ %RDa8W,xGrSEq5\DO^bŎID6nc45e^S{ܨT1h!tI6#Li"wNյG+u4WrdC=w-r#N̲N(6KbSeEy18;zvo6jfϝ| Hdo1.+s:50}I xGhfb4`,ҥJjUr?RR}LƧ7k<} R{VZnt8Ex))ZCᅳˑcǔ ;?_k93VImo=и,06R!δ0"XhQud' a+ޣ\G˵I)əƮuhCQfDE"S⟳Rv:75'f'0VA~\zdW'Kwp[Xj)JQo5.5 ƤDV]ͳa׺+wnxxoT T!EG`C? ! %@v_/D8{I`/RFU$*BxSƒv*,WuOo<0'L';PV ڜes.SǕ O! tv(6b_iVݹ6 ֧bV}Nø;1\C6) JAr i;)D{fn<ꨤ@ŕ4Aд1EnrSGoǕObyeg:5@m)_"`K<~9'HLhSZ*BIO,J&8]CRqۄ;gkPZhYw LR!  ju:t㉘Oy;\^|8Du/t@w} { z!3Zt~΋:ZHT'Kp7FheJCĐm_Itۊ|]|aC+'fDSQ,%5QabeRx#UG;iuے@f 5.HVtzc @a '( sq>SGhF1J L{|8;)Fa]&{UؼDo,Ͷ#|v.8,ut mmsYVnvP9VyGwm֖`?/̡khq=7UC"7nh6K*=&qa|:m%R0GL0La?^Ը6$J ZT||"yR^w7C;$*Q{^Δ _n!Bϲ>Z$X\2䩡eV*l8d\{ m:Mj_lL)R)bJ`8^W6OC=3[yӿt‹s\*h\gA~s&ETs %O>&Qg&,%k)c?!Xlbb,D;qi~x,P %? TS\bJdwV|ٱ'R,s.X$eڨLN[jgfnslH 36XDxhmN{^||^zqS*= OĬcbIz3N2'7Ghcf*"ԱA"lsPh`~C_<j[۷Gr*ˆhz%_%Hwq/MDמ]-$c*Y]@6@TCWCz.S'edMyX2tr$tqY"uiM?.' b!{f̷q_V*oYit%+ U4g+^6"9u2I%,:E9W7F[$W9O#d#8LZrbb]bfnetc&$.EA\Nr*+s?x 68=AjE䬭}~;mÅ4F'wO.Fe2r"DȋCh!•OHoOsjog_4.L+ֳ;[J($콲M|U9⟥]<6 ] ׬KBܕ+ ,+)Q"TeRJ5S|n8+,%h{MRQI&.~y] @E݄QY 9[AVH%bpmO`q̒G4ed$d\WZ]*ٚ הEɖvz784Xyx 8{F?8kW ?i W7ulBHDCD%ø_ouWT~WGN8 ylG0R[z=ͣeaX!t\slX5~X Wcuɶ]oÂMiA4WuæX.ՔZ+_Cs:_%n8/* _[.>B{wϵCj} ,cz >#҃^|n$֘HQum!(|p)]00U1 K}"Fm>!B,㦒pZ]ʵ@ݟ/v |LQΣVA)7gcsB2p8`-uMW\\I [đ!"&dm7ts]j-vYS,HXU}3Dotگ[)*D9`Jk Yl:zzH1.&8CδB*D ڇbD־v`Z? :hSF]("Y#\Ji\af/2k.3*2M-nQH e_`d{A\lPp&j&GVK],@DhN3gm ALj7UMpߦA:M*oq-DP* 3sT" ~|/ϬO|jHX4kcJd0T~궚4J  GMPWL]t\Ƿ3k=>z,|:޹Z;d=$*$| sW1ơ;frb}]£mJ v;Ph0ņcXkioP:Q1`m[kTKB3:w̒8QPeP`_So~:cIqGl " ҲVnj팥 aBIlY9+;%6 Bp^1 Xq=Qw8_v֞G'u/vŝ .24| -Z6N?Ay+03~Tt0][h&v郳QrS 5.yqXZ@fxgduPb#?;w$BН_B9#&[1}'tTC^A%ɩ.pv?^J5۽О+c-Uz4L aϺb'q9SaCս 5 o;s`X#nKi"C XiwԋRq0eTB(D ŞL3AgJ,vPR}kV0ӫ5ZmKѲ'!d5J"RX)YZMtovn$oBFčb{/L&kUp#ehTh0rȸq$&Uύ37]s[&bTċM(?ۺC̬h5 `gلxlts_O ŽZ nyx$n_XR}ڼxǩ4_, aZcY9uBH7XOP*C;%jNcmKǣg># NBH57F;;EU lx joqCiѮzYD`U e{v430VA5pwQB݇<>qdHJ'ۢ?S6|ys0j )۾[ *0Z7YpS$&-_ |60W̝mC yW;qhԆAr&6BbA#A[kW^I&Md'g4r l0|X2 /95 %SP6=x)/.Oy - w5Ŀ tMVh\@S wTz)S* XzٷblHn*r9` {ː^f#=*?Tފl d,1r*d֘˿̉"M\L^' } Aܡ4=e{I56#Ȝ8ѱk%5ВqDl{@`pДbo8 puN0)| 9]vv^oT`*$E_:? aI/P?[tv(1 <_^P*ljufNfq1S s^GHҧ? ٧U}<Ӽ7-R$6t\Ɇ_j,A$7^eFrQ߾`s2)G=A6vg/8Qya Y|5Dj8UP$9Q*J )Dm)n\syH9zEUrD7B ~|0Hpj\9±^yb>֞~HNϙL;/+&`L,h6^J̓\q}ؓ˺[WܨHdžAH8b\%bwnL6muJ|ދZܟE^jJx&A uU WiU/&ZM)O ٢ߞ@XL G:1 QtɬqrU{lAd3@RNZ3GiK؊-jɻXlDT^<h)s2Y~ݱb,{J?":9|${`$`3B}0RϹ`F~ ǖWЇ[tj*$ZcwA-5=ݛz(Ugc fx}M _ 暪. X+֛9SYvyL1+}d Q~(S?\hOhp9 po{fɉU~83:V@)u'ٓq~}dl].,DP$ȓ[ի*¨zz@oRHPBkR#=DyN#VHubJr:f2׭ )mZ9gD;Lބč&| ̠:!Q ޿\&'}BZ3,4:śl_>4NY8Ooŗݿ-"|&8:|$w_4F'#I,.&"0iPhݳ6_5'ŏ}C'!]iZw&'KRkё,<_YޡĻ;ḣK rHA@> V~ Xml}2mCr:b!h'HL{9oΔ5/+fM~: /[|P/Ua6)X+8tTrf!BeiO!T, ]u I!O)5Oe/ߞq"} \Idf-z1lvV@8]IC{ (}FWgU]7C9Xϼ2u?3&֬iZsl`-H1am8jAF!œ1) YDthgr*>75$59@ěqVlmdPD>jg%$) =Vʜj+?1,n.=Hf'eͿ]3a'_!pu ڏ׬p etdːTD~ҁ#dp(Hni9C9+i;x!!QI4 l ,5s8SOSYJ<*wD7b_y͛¥H7~R?Q \l胟0:Pd紉QgDf|tϷ!Z ͗4/<ᚸ ?fփ@m۳Ihdajg;^ya&j<kUNiTIILUnt,Fqaaa۶fԩ|&MYeI)a_Ġ0nf*/wqCm.nI(#owbBs%NOq:hY>,S~,j*X2r" qDC~=wh}XPP$j.sXkeKN0LgZ#LeN1ߡ]h*?LtZ^.6{;׼6Ф9ea=ȾhjVQ}i &G pх<܈W `76,,=6ic .5<\:LE2ۯ rIc\=] $RfaݟK)4OĿ}&$"x' M*6Y[:5(W\d+#v+vdnP.CV~HpӢdE19zc C7W~DR@z-ЪЅb 6ZmE~+;_޽xHŗ*G$WQ-~4:v"rm<] #ʅ}$I9JW#Im69"q ;1FϵW-~F0 /(S"kW׸FI-Q{4]A>}qvO)|tO/t }lS]8/ r*" ,b:qJTv !A-Ͳ,zHwȇ Hx?Oס7'G TOEU6H5+ȮW PFFlG-Qe ?@3T[?Tc&\1^#UiGpi9fyrL ʲG;oXe:\FU-he~EWqKZM;^5Rb9a}2P1n2:>?s+<9pco߀]Ջ 6lCT0עY'8|rJi>|q!\y^tA_zZ,R >^Zُ}^#|.io#w3j12(Q"sϛۂk_,G-*3wr[oZ0"dvNi]_j' ,S0OG /GTK|m-M7 xa8'=:x;+/>ݮF?Kj ~cnN3jzn»Ґ~LO(JDP2%}m2*Nr~UԊ)pa*mQExjy\y;xA*kA$U]%I Unpk_=MMeA.;݉ySSQ;PErCyl7 H%Oq^'F'\[?@B0Q!Ȍoۘ«HQsb~Zl+k 7*r^Vs8ѓ]b^? O\[Z2PjSN;ieцI%^Uf6LBgXG!-1# c ]pP{FB(`V!-0qų˙Xvr/A~r!.e},h/̭q'j 1 %s01?7\Wbb;SK5 >?0XDbBrU%KxGZauC>q7# ,[*}-t,O/;] v>s[îO=2W`iFPGI 1nw)P Rl@aJ=K^qi"r(FT4̮kHW~2/T;c/]ޫ ~#i١FbgqdU{I $wTTEQ{2=o|-"MuN-5Vx]7LI&$ Q0?;|;+>%1xx`b Ʉ)RUL$JؖD7(\b˰!Υ]2,B.b6C$9p 5.0hDl`.lR%[=q hi2уsvd)[ǝߘ|7t FYXLxH)VqfT+FlAyzsiwjjXtgDP C1Y~r|:Pu^*:zL d~gQ=虩ľ!ھB3??:6(g[Xk(|hCh[̘u5UF7](9 XÓg"ª 'xOb<|c~{7,Zf!sqsDR;e@XP݆#NFsֳDtQ_uU<+˃@UZUGL@j +"<.ұ˗Vn+, >g=9e抬Oy{JHmI:J> ǘkRxU*/' 1`6SBcoIB ezFb[ce:2&ef`K5 xCQ98*)P[u[:~W*9 fYM/yN\H̟]L xmcFf?dabjܠ2eмE:OPBi[g4ZBoOXkIw/yK5qO&]Nr\:~m+q g-p)bkG?A{SӱZfϧkx鉱D׀L[hix}]ՂЙzǕ% |tWP5jj&jJX/y[^VlK!*3ҹl=c] v5 UpFIzxC}+ߑ0X@b}w #@h2opA4G5 V!+kN']1 ={ usBKf*j0ȡ"ހs>a}F0 FcS@T ,`s1gLŒ>?~fZVcAI~=0+!ߋ'KbfHja!V7s#.k'p`sZk<|%#ϓwU $\vZQitjRAgGK:}bj_6_N'@@#jc(8F}fh/(0!HV Dq=!H. k.0u.BdK+kf_Y u05x"%^|6,?7TRbky-VuRMhwL(`[az6#)ֈncE-Va b ϪcLȶ#;H0TO!f(` ҀKT^ۣ4k3D-*6!SWTV5 oI+a[gcDZEBރ, cbem@P\cN`F짾b10hn] ;)c D{I F ~cB,ү>DY`GZs$6E&iH>|vYK#o /}ׂfW:7mn$6j4’#2V`j_"O-)r$E X%91vY4 ٬IbE:F yh{1,˜#\6O $;dZiiky>'go >CpwJ3᎖A̶}0d:Th9%g*p,8^R3AY?ػ 8`1ypmv&]m+bҙ 9̩/GZ#Fm!0L .ޕӕbNE-IC,aZIKWa5^MEI~f\ڰh$3@PVo*k㴵XɎ&az(O6gk b-E*zZx.EKHiONpY/>deٸ"^ >`\AAиLW}6b:oD  hwtx ZT[Mz^%#cW|{B;Y+0aN<"^x')r$ƖĜfs!qYgٰrJ@i]BY8m|`7ͲM(VLݏwq [. ]s h[%n9h\µFB2o#znȱ_hX"(>qeZUFl]VB`,);c~t_6N$[jG,O3+K 䘽{g@-5KR+p?I辢tTԨ ־&دJB|H(?74iepj H" T&s_@TݖҰyrwOt: xgɌ^ߑ^6ao$B})<"V çZ8NMݘK(R cuBj0K:RȯtarRVи P$+q8<԰0}r3|H]F4) NM#yYX؆"' >vh|kTF`~H/D?dD6>=~oYW<\wrHw1s& =Jc41KJ5w #¸(>ܨ^Yd#O9)ȫa*>=A"Eb@'[{ғ` ΂;Uů<'Mc({QU֨L>1Bjo9tLxSwã)b )9|~V0G p5)G_SvF-d4B}!~4Aʠ<7Z2wD`b:/ J?m'LM35X"^+Jjy75cIQF NGB@^$aR^eeFJfiPY]Yc٬! bP;d>̀l RÀgԚ< #q1 ]KBZJm-+Q9)$+ i@iXTdmXk`\8Y~ñ=I%3j.-)x+vDP!sJHk هkaSk$JiB~ȷDhP%{|.Oȼe9:IB#OAZzHWg)F1.FbL8IPйy|;h6B^8kN2׿O}a bY:63>~͘@D$5-".Ƽː&&FSW~hM&)i.хCBcwm+n2*<ŔD`HVfiY jR wo4 _HjG6 .]?k'Vn vY7yt 5!_TBW)Xqαn 3)ԧDS\Xy&Ѷ ,J}j>#5}DvMe=6A[;/^!x0ċYIX^5:`*OS3BT;Z I-j2ZNZAJIwXONXY")&_{e!t/H%~3w/5O/GjRy1(OX@7L Lݝ.}5ĥ9ח㰇 m~ ]os~#"%.]@`hc6Z+U.e? `YS _Y-KAA:b fH|l{'#mhtI Snߒb<ǎL65Ƕ prkg@ ~.QjR-yfW좡8^¼})gꞪ VHX]ѧKHV3txx6ΩJg 8E:Rfl pM=&ѩgyղmX}9n6T.u|(\Ω"cL_|؋qK&qZ^F86hqd~QVY)} R5mp~)Ani2 {i4J"Pb{5-ytV iN8C|n"$Qm}]\UIoEU\\"h'8 kxo2Mzc/r~h~%{ӭ*?w4N0jыEO6^_es5}2}`wh~W p *~$7G2F')w~<ҏ6Q¾e N0ZOvR;.tĹv:l1'8hJ'30{O|@g[{! _&ۙh {f'(M]BRJ/>9hw P!Vmᆢci7-;> CL7bP8墦SͣBE"ـLi3T `x'bv/|W[nTD3}42gwQe~yE1Fpa5:Vz/8/ˡrAgID kP$_c=M |&z'㳿óD6IMf3-;7p) Z Q飇شy YZrekor-1.3.5/cmd/rekor-cli/app/tests/test_alpine.apk000066400000000000000000000054721455727245600222530ustar00rootroot00000000000000 t vK)KMI-Kq,.) dV$%X'&1 L:m`d3(0$)(0SGH~gW_*.yf2{[`iAM|eJٴX&OY%N Z=sN[^d2Q+>.Z;G.2))~f#妪OoeΆvCrmY*Bʬ,qZ q>{>Ħ)wHi7s٥d5[.mDZ馮)Z/g3}߀\#;4fʻ0悷ltK-UA4ڜJGw'fsϸ[ ie$D+ư\H=DKGMYNܑ/hжSs;aZӤ%ΟÉS;H΀gn1oVtvs0e>쳪q[zFֹw AQXk (4JW Pl/u%Xneç< NxeKZKIZHms443=IDN>`kzԍ[6`ϭNթʕlqل+]vZVhh+תSz8f䪎Ӭ6r=Ja ]zB#Bufk5itvG_-Sk=ۮ/`aW>fja7Cr?ap_&Y!:~3&'w?A=e/?xGCg F]Gf}t@w-S\$,=;FFZ7=U-錫N=Vk4Z7HOH?"kԓXa׹rW_x"?w_[׋+//` <i>#z(S3gÑL {+ OFFqbU6S<2 rnoa*S%|. "B\+%6ژ "|n(>cj"n/<1拸VF{Df{d1 7BG3I\EKߐH!XxKT"i exlQ*"߆s>!dž8aC_U*vlYYe\e =uKcT95fωPXylnlNYp,\aYt:Z mVaw0_,Bυ[ܾ[D QЅ!9;=gt]sEtRP ]*z1TxW4K،L^sQu\Bݛ@"ҏ*SP([2)N(M 99K;Dh[BD)(( ȝ.mxD0JkWJY0hwG@M-'#H" f'Bb7s`i}㚠^OKAcAZ2Tt_V3-mNiKsP /5$`JIoB$#nhO 8ZI( (4|f3S/}GT̒,QzXezG$0ɞ%mgGE`iZ0U2m *XKXJn=A\@ޜt ;N!~=޽.Xn6,pGሚG {ݨJ|pmH"}mU N,E K2N WL!T=<=VgkÍwI] %#BĒ:F )BQc(? [ӂm?8!!cTP/pld<:@ڮh$QAZDr- +N"ăe(/3q)Ŭ$kjӋn4I8|8X]svQUN.M!X\h?p"[ЂϮ9i&h%LMds7y,ѹv ٜ _jSNROVhYJolG?"LBԍpLٙf'T:%ueL Iw94)={ӍLR=O9FX|ԥqRd/wqyBbg\(E~(|N  k(< !Ì./`ݗĨ/ٰ[]"6,}u)O`X +oIM6nV#a}S/SW,%A ~]~jR%FK{XIfw$&íۀ8S&-S٦?bX=`Pu[Kisa R|'*$\MIGΚ vUNՁg3Lf+{rͼ|c_خڮڮڮ/hU(rekor-1.3.5/cmd/rekor-cli/app/tests/test_alpine.pub000066400000000000000000000007031455727245600222560ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1yHJxQgsHQREclQu4Ohe qxTxd1tHcNnvnQTu/UrTky8wWvgXT+jpveroeWWnzmsYlDI93eLI2ORakxb3gA2O Q0Ry4ws8vhaxLQGC74uQR5+/yYrLuTKydFzuPaS1dK19qJPXB8GMdmFOijnXX4SA jixuHLe1WW7kZVtjL7nufvpXkWBGjsfrvskdNA/5MfxAeBbqPgaq0QMEfxMAn6/R L5kNepi/Vr4S39Xvf2DzWkTLEK8pcnjNkt9/aafhWqFVW7m3HCAII6h/qlQNQKSo GuH34Q8GsFG30izUENV9avY7hSLq7nggsvknlNBZtFUcmGoQrtx3FmyYsIC8/R+B ywIDAQAB -----END PUBLIC KEY----- rekor-1.3.5/cmd/rekor-cli/app/tests/test_cose.cbor000066400000000000000000000001251455727245600220740ustar00rootroot00000000000000҄C&Khello worldX@U='g`5dgG{;Q|R+s SrÁAZcrekor-1.3.5/cmd/rekor-cli/app/tests/test_cose.pub000066400000000000000000000002621455727245600217370ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7IEW48UTgci4iVRMGQZA0CsDABAB 1ijWhU182cy9blVGfEkCNGay1wSGjiYmnrZedXh49YI7u8l9xZ5bu0rK7w== -----END PUBLIC KEY----- rekor-1.3.5/cmd/rekor-cli/app/tests/test_file.sig000066400000000000000000000007151455727245600217240ustar00rootroot000000000000004!r\nie &8z_lhinds@protonmail.com &8z7 ra`2sQ0 :`. F^ Hˣ.!l#9*2 rquN'K7~G·#'0յ7C) tP#TE]O't]p^Ul_9u_E̮ʢ4GԨAU4R X8T>wc5;QUutCf0W^k !׹,I@JڊBQ"]!@ v)n[Hr*~0EKȺPA$rekor-1.3.5/cmd/rekor-cli/app/tests/test_file.txt000066400000000000000000000000141455727245600217510ustar00rootroot00000000000000hello rekor rekor-1.3.5/cmd/rekor-cli/app/tests/test_public_key.key000066400000000000000000000046301455727245600231410ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- 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 n14pbAaTdaKEPcvtufbUuW0IjYd2kpIT/tg= =Oghr -----END PGP PUBLIC KEY BLOCK----- rekor-1.3.5/cmd/rekor-cli/app/tests/test_rpm_public_key.key000066400000000000000000000032231455727245600240140ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v2.0.22 (GNU/Linux) mQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn rIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ 8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X 5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c aevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e f+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7 JINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m vufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk nHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry Gat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y m4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB tDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5 QGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB Ah4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl Gy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs N3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD vOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq a0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw byaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg q4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X 407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z V6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG rCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32 o8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy yy+mHmSv =kkH7 -----END PGP PUBLIC KEY BLOCK----- rekor-1.3.5/cmd/rekor-cli/app/upload.go000066400000000000000000000131731455727245600177150ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "context" "fmt" "io" "net/http" "net/url" "os" "path/filepath" "github.com/go-openapi/runtime" "github.com/go-openapi/swag" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/sigstore/rekor/cmd/rekor-cli/app/format" "github.com/sigstore/rekor/pkg/client" gen_client "github.com/sigstore/rekor/pkg/generated/client" "github.com/sigstore/rekor/pkg/generated/client/entries" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/verify" ) type uploadCmdOutput struct { AlreadyExists bool Location string Index int64 } func (u *uploadCmdOutput) String() string { if u.AlreadyExists { return fmt.Sprintf("Entry already exists; available at: %v%v\n", viper.GetString("rekor_server"), u.Location) } return fmt.Sprintf("Created entry at index %d, available at: %v%v\n", u.Index, viper.GetString("rekor_server"), u.Location) } // uploadCmd represents the upload command var uploadCmd = &cobra.Command{ Use: "upload", Short: "Upload an artifact to Rekor", PreRunE: func(cmd *cobra.Command, args []string) error { // these are bound here so that they are not overwritten by other commands if err := viper.BindPFlags(cmd.Flags()); err != nil { return err } return validateArtifactPFlags(false, false) }, Long: `This command takes the public key, signature and URL of the release artifact and uploads it to the rekor server.`, Run: format.WrapCmd(func(args []string) (interface{}, error) { ctx := context.Background() rekorClient, err := client.GetRekorClient(viper.GetString("rekor_server"), client.WithUserAgent(UserAgent()), client.WithRetryCount(viper.GetUint("retry")), client.WithLogger(log.CliLogger)) if err != nil { return nil, err } var entry models.ProposedEntry entryStr := viper.GetString("entry") if entryStr != "" { var entryReader io.Reader entryURL, err := url.Parse(entryStr) if err == nil && entryURL.IsAbs() { /* #nosec G107 */ entryResp, err := http.Get(entryStr) if err != nil { return nil, fmt.Errorf("error fetching entry: %w", err) } defer entryResp.Body.Close() entryReader = entryResp.Body } else { entryReader, err = os.Open(filepath.Clean(entryStr)) if err != nil { return nil, fmt.Errorf("error processing entry file: %w", err) } } entry, err = models.UnmarshalProposedEntry(entryReader, runtime.JSONConsumer()) if err != nil { return nil, fmt.Errorf("error parsing entry file: %w", err) } } else { typeStr, versionStr, err := ParseTypeFlag(viper.GetString("type")) if err != nil { return nil, err } props := CreatePropsFromPflags() entry, err = types.NewProposedEntry(context.Background(), typeStr, versionStr, *props) if err != nil { return nil, fmt.Errorf("error: %w", err) } } resp, err := tryUpload(rekorClient, entry) if err != nil { switch e := err.(type) { case *entries.CreateLogEntryConflict: return &uploadCmdOutput{ Location: e.Location.String(), AlreadyExists: true, }, nil default: return nil, err } } var newIndex int64 var logEntry models.LogEntryAnon for _, entry := range resp.Payload { newIndex = swag.Int64Value(entry.LogIndex) logEntry = entry } // verify log entry verifier, err := loadVerifier(rekorClient) if err != nil { return nil, fmt.Errorf("retrieving rekor public key") } if err := verify.VerifySignedEntryTimestamp(ctx, &logEntry, verifier); err != nil { return nil, fmt.Errorf("unable to verify entry was added to log: %w", err) } // TODO: Remove conditional once inclusion proof/checkpoint is always returned by server. if logEntry.Verification.InclusionProof != nil { // verify inclusion proof if err := verify.VerifyInclusion(ctx, &logEntry); err != nil { return nil, fmt.Errorf("error verifying inclusion proof: %w", err) } // verify checkpoint if err := verify.VerifyCheckpointSignature(&logEntry, verifier); err != nil { return nil, err } } return &uploadCmdOutput{ Location: string(resp.Location), Index: newIndex, }, nil }), } func tryUpload(rekorClient *gen_client.Rekor, entry models.ProposedEntry) (*entries.CreateLogEntryCreated, error) { params := entries.NewCreateLogEntryParams() params.SetTimeout(viper.GetDuration("timeout")) if pei, ok := entry.(types.ProposedEntryIterator); ok { params.SetProposedEntry(pei.Get()) } else { params.SetProposedEntry(entry) } resp, err := rekorClient.Entries.CreateLogEntry(params) if err != nil { if e, ok := err.(*entries.CreateLogEntryBadRequest); ok { if pei, ok := entry.(types.ProposedEntryIterator); ok { if pei.HasNext() { log.CliLogger.Errorf("failed to upload entry: %v", e) return tryUpload(rekorClient, pei.GetNext()) } } } return nil, err } return resp, nil } func init() { initializePFlagMap() if err := addArtifactPFlags(uploadCmd); err != nil { log.CliLogger.Fatal("Error parsing cmd line args:", err) } rootCmd.AddCommand(uploadCmd) } rekor-1.3.5/cmd/rekor-cli/app/useragent.go000066400000000000000000000021211455727245600204150ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "fmt" "runtime" "sigs.k8s.io/release-utils/version" ) var ( // uaString is meant to resemble the User-Agent sent by browsers with requests. // See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent uaString = fmt.Sprintf("rekor-cli/%s (%s; %s)", version.GetVersionInfo().GitVersion, runtime.GOOS, runtime.GOARCH) ) // UserAgent returns the User-Agent string which `rekor-cli` should send with HTTP requests. func UserAgent() string { return uaString } rekor-1.3.5/cmd/rekor-cli/app/validate.go000066400000000000000000000043731455727245600202240ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "errors" "fmt" "strings" validator "github.com/asaskevich/govalidator" ) // validateSHA512Value ensures that the supplied string matches the // following format: [sha512:]<128 hexadecimal characters> // where [sha512:] is optional func validateSHA512Value(v string) error { var prefix, hash string split := strings.SplitN(v, ":", 2) switch len(split) { case 1: hash = split[0] case 2: prefix = split[0] hash = split[1] } if strings.TrimSpace(prefix) != "" && prefix != "sha512" { return fmt.Errorf("invalid prefix '%v'", prefix) } if !validator.IsSHA512(strings.ToLower(hash)) { return errors.New("invalid SHA512 value") } return nil } // validateSHA256Value ensures that the supplied string matches the following format: // [sha256:]<64 hexadecimal characters> // where [sha256:] is optional func validateSHA256Value(v string) error { var prefix, hash string split := strings.SplitN(v, ":", 2) switch len(split) { case 1: hash = split[0] case 2: prefix = split[0] hash = split[1] } if strings.TrimSpace(prefix) != "" && prefix != "sha256" { return fmt.Errorf("invalid prefix '%v'", prefix) } if !validator.IsSHA256(strings.ToLower(hash)) { return errors.New("invalid SHA256 value") } return nil } func validateSHA1Value(v string) error { var prefix, hash string split := strings.SplitN(v, ":", 2) switch len(split) { case 1: hash = split[0] case 2: prefix = split[0] hash = split[1] } if strings.TrimSpace(prefix) != "" && prefix != "sha1" { return fmt.Errorf("invalid prefix '%v'", prefix) } if !validator.IsSHA1(strings.ToLower(hash)) { return errors.New("invalid SHA1 value") } return nil } rekor-1.3.5/cmd/rekor-cli/app/validate_test.go000066400000000000000000000071461455727245600212640ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "testing" ) func TestSHA1(t *testing.T) { var tests = []struct { value string expectFail bool }{ { value: "", expectFail: true, }, // to short { value: "sha1:7c65d0f10d62751f84469873d6c595423eee8b0", expectFail: true, }, // too long { value: "7c65d0f10d62751f84469873d6c595423eee8b078", expectFail: true, }, // invalid char g { value: "gc65d0f10d62751f84469873d6c595423eee8c07", expectFail: true, }, { value: "sha1:ac65d0f10d62751f84469873d6c595423eee8c07", expectFail: false, }, { value: "AC65d0f10d62751f84469873d6c595423eee8c07", expectFail: false, }, } for _, tr := range tests { err := validateSHA1Value(tr.value) if tr.expectFail == (err == nil) { t.Errorf("Failure validating '%s': %s", tr.value, err) } } } func TestSHA256(t *testing.T) { var tests = []struct { value string expectFail bool }{ { value: "", expectFail: true, }, // to short { value: "sha256:db1abb0bc57a17623cf1181f67b922fc6b868eee27ae81e43efeba68a2002ec", expectFail: true, }, // too long { value: "db1abb0bc57a17623cf1181f67b922fc6b868eee27ae81e43efeba68a2002ec5fa", expectFail: true, }, // invalid char g { value: "gb1abb0bc57a17623cf1181f67b922fc6b868eee27ae81e43efeba68a2002ec5", expectFail: true, }, { value: "sha256:db1abb0bc57a17623cf1181f67b922fc6b868eee27ae81e43efeba68a2002ec5", expectFail: false, }, { value: "DB1abb0bc57a17623cf1181f67b922fc6b868eee27ae81e43efeba68a2002ec5", expectFail: false, }, } for _, tr := range tests { err := validateSHA256Value(tr.value) if tr.expectFail == (err == nil) { t.Errorf("Failure validating '%s': %s", tr.value, err) } } } func TestSHA512(t *testing.T) { var tests = []struct { value string expectFail bool }{ { value: "", expectFail: true, }, // to short { value: "sha512:6ae8d43391b2adb187e211a7619d11e8f696321cf5e9266ab51423e79d5974cbc679cd8ad15431b32b4d3ed53a6da3be446dd5bb270defe5f4e940fc018040c", expectFail: true, }, // too long { value: "6ae8d43391b2adb187e211a7619d11e8f696321cf5e9266ab51423e79d5974cbc679cd8ad15431b32b4d3ed53a6da3be446dd5bb270defe5f4e940fc018040c71", expectFail: true, }, // invalid char h { value: "hae8d43391b2adb187e211a7619d11e8f696321cf5e9266ab51423e79d5974cbc679cd8ad15431b32b4d3ed53a6da3be446dd5bb270defe5f4e940fc018040c7", expectFail: true, }, { value: "sha512:6ae8d43391b2adb187e211a7619d11e8f696321cf5e9266ab51423e79d5974cbc679cd8ad15431b32b4d3ed53a6da3be446dd5bb270defe5f4e940fc018040c7", expectFail: false, }, { value: "ABe8d43391b2adb187e211a7619d11e8f696321cf5e9266ab51423e79d5974cbc679cd8ad15431b32b4d3ed53a6da3be446dd5bb270defe5f4e940fc018040c7", expectFail: false, }, } for _, tr := range tests { err := validateSHA512Value(tr.value) if tr.expectFail == (err == nil) { t.Errorf("Failure validating '%s': %s", tr.value, err) } } } rekor-1.3.5/cmd/rekor-cli/app/verify.go000066400000000000000000000132421455727245600177320ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "context" "encoding/hex" "fmt" "math/bits" "strconv" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/transparency-dev/merkle/rfc6962" "github.com/sigstore/rekor/cmd/rekor-cli/app/format" "github.com/sigstore/rekor/pkg/client" "github.com/sigstore/rekor/pkg/generated/client/entries" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/verify" ) type verifyCmdOutput struct { RootHash string EntryUUID string Index int64 Size int64 Hashes []string Checkpoint string } func (v *verifyCmdOutput) String() string { s := fmt.Sprintf("Current Root Hash: %v\n", v.RootHash) s += fmt.Sprintf("Entry Hash: %v\n", v.EntryUUID) s += fmt.Sprintf("Entry Index: %v\n", v.Index) s += fmt.Sprintf("Current Tree Size: %v\n", v.Size) if len(v.Checkpoint) > 0 { s += fmt.Sprintf("Checkpoint:\n%v\n\n", v.Checkpoint) } else { s += "\n" } s += "Inclusion Proof:\n" hasher := rfc6962.DefaultHasher inner := bits.Len64(uint64(v.Index ^ (v.Size - 1))) var left, right []byte result, _ := hex.DecodeString(v.EntryUUID) for i, h := range v.Hashes { if i < inner && (v.Index>>uint(i))&1 == 0 { left = result right, _ = hex.DecodeString(h) } else { left, _ = hex.DecodeString(h) right = result } result = hasher.HashChildren(left, right) s += fmt.Sprintf("SHA256(0x01 | %v | %v) =\n\t%v\n\n", hex.EncodeToString(left), hex.EncodeToString(right), hex.EncodeToString(result)) } return s } // verifyCmd represents the get command var verifyCmd = &cobra.Command{ Use: "verify", Short: "Rekor verify command", Long: `Verifies an entry exists in the transparency log through an inclusion proof`, PreRunE: func(cmd *cobra.Command, args []string) error { // these are bound here so that they are not overwritten by other commands if err := viper.BindPFlags(cmd.Flags()); err != nil { return fmt.Errorf("error initializing cmd line args: %s", err) } return validateArtifactPFlags(true, true) }, Run: format.WrapCmd(func(args []string) (interface{}, error) { ctx := context.Background() rekorClient, err := client.GetRekorClient(viper.GetString("rekor_server"), client.WithUserAgent(UserAgent()), client.WithRetryCount(viper.GetUint("retry")), client.WithLogger(log.CliLogger)) if err != nil { return nil, err } searchParams := entries.NewSearchLogQueryParams() searchParams.SetTimeout(viper.GetDuration("timeout")) searchLogQuery := models.SearchLogQuery{} uuid := viper.GetString("uuid") logIndex := viper.GetString("log-index") if uuid != "" { searchLogQuery.EntryUUIDs = append(searchLogQuery.EntryUUIDs, uuid) } else if logIndex != "" { logIndexInt, err := strconv.ParseInt(logIndex, 10, 0) if err != nil { return nil, fmt.Errorf("error parsing --log-index: %w", err) } searchLogQuery.LogIndexes = []*int64{&logIndexInt} } else { typeStr, versionStr, err := ParseTypeFlag(viper.GetString("type")) if err != nil { return nil, err } props := CreatePropsFromPflags() entry, err := types.NewProposedEntry(context.Background(), typeStr, versionStr, *props) if err != nil { return nil, fmt.Errorf("error: %w", err) } entries := []models.ProposedEntry{entry} searchLogQuery.SetEntries(entries) } searchParams.SetEntry(&searchLogQuery) resp, err := rekorClient.Entries.SearchLogQuery(searchParams) if err != nil { return nil, err } if len(resp.Payload) == 0 { return nil, fmt.Errorf("entry in log cannot be located") } else if len(resp.Payload) > 1 { return nil, fmt.Errorf("multiple entries returned; this should not happen") } logEntry := resp.Payload[0] var o *verifyCmdOutput var entry models.LogEntryAnon for k, v := range logEntry { o = &verifyCmdOutput{ RootHash: *v.Verification.InclusionProof.RootHash, EntryUUID: k, Index: *v.LogIndex, Size: *v.Verification.InclusionProof.TreeSize, Hashes: v.Verification.InclusionProof.Hashes, } if v.Verification.InclusionProof.Checkpoint != nil { o.Checkpoint = *v.Verification.InclusionProof.Checkpoint } entry = v } if viper.IsSet("uuid") { if err := compareEntryUUIDs(viper.GetString("uuid"), o.EntryUUID); err != nil { return nil, err } } // Get Rekor Pub // TODO(asraa): Replace with sigstore's GetRekorPubs to use TUF. verifier, err := loadVerifier(rekorClient) if err != nil { return nil, err } // verify inclusion proof, checkpoint, and SET if err := verify.VerifyLogEntry(ctx, &entry, verifier); err != nil { return nil, fmt.Errorf("validating entry: %w", err) } return o, err }), } func init() { initializePFlagMap() if err := addArtifactPFlags(verifyCmd); err != nil { log.CliLogger.Fatal("Error parsing cmd line args:", err) } if err := addUUIDPFlags(verifyCmd, false); err != nil { log.CliLogger.Fatal("Error parsing cmd line args:", err) } if err := addLogIndexFlag(verifyCmd, false); err != nil { log.CliLogger.Fatal("Error parsing cmd line args:", err) } rootCmd.AddCommand(verifyCmd) } rekor-1.3.5/cmd/rekor-cli/app/version.go000066400000000000000000000013171455727245600201130ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "sigs.k8s.io/release-utils/version" ) func init() { rootCmd.AddCommand(version.Version()) } rekor-1.3.5/cmd/rekor-cli/get_e2e_test.go000066400000000000000000000021461455727245600202200ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package main import ( "testing" "github.com/sigstore/rekor/pkg/util" ) func TestGetNonExistentIndex(t *testing.T) { // this index is extremely likely to not exist out := util.RunCliErr(t, "get", "--log-index", "100000000") util.OutputContains(t, out, "404") } func TestGetNonExistentUUID(t *testing.T) { // this uuid is extremely likely to not exist out := util.RunCliErr(t, "get", "--uuid", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") util.OutputContains(t, out, "404") } rekor-1.3.5/cmd/rekor-cli/loginfo_e2e_test.go000066400000000000000000000017521455727245600211000ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package main import ( "sync" "testing" "github.com/sigstore/rekor/pkg/util" ) var ( once sync.Once ) func TestLogInfo(t *testing.T) { once.Do(func() { util.SetupTestData(t) }) // TODO: figure out some way to check the length, add something, and make sure the length increments! out := util.RunCli(t, "loginfo") util.OutputContains(t, out, "Verification Successful!") } rekor-1.3.5/cmd/rekor-cli/logproof_e2e_test.go000066400000000000000000000017421455727245600212710ustar00rootroot00000000000000// // Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package main import ( "testing" "github.com/sigstore/rekor/pkg/util" ) // TestLogProofLastSizeBiggerThanCurrentLog tests that asking for a consistency proof for a size greater than the current log func TestLogProofLastSizeBiggerThanCurrentLog(t *testing.T) { out := util.RunCliErr(t, "logproof", "--last-size", "14212414124124124") util.OutputContains(t, out, "400") } rekor-1.3.5/cmd/rekor-cli/main.go000066400000000000000000000012751455727245600165750ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import "github.com/sigstore/rekor/cmd/rekor-cli/app" func main() { app.Execute() } rekor-1.3.5/cmd/rekor-cli/main_test.go000066400000000000000000000013371455727245600176330ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "testing" "github.com/sigstore/rekor/cmd/rekor-cli/app" ) func TestCover(_ *testing.T) { app.Execute() } rekor-1.3.5/cmd/rekor-cli/verify_e2e_test.go000066400000000000000000000016271455727245600207500ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package main import ( "testing" "github.com/sigstore/rekor/pkg/util" ) func TestVerifyNonExistentIndex(t *testing.T) { // this index is extremely likely to not exist out := util.RunCliErr(t, "verify", "--log-index", "100000000") util.OutputContains(t, out, "entry in log cannot be located") } rekor-1.3.5/cmd/rekor-server/000077500000000000000000000000001455727245600160545ustar00rootroot00000000000000rekor-1.3.5/cmd/rekor-server/app/000077500000000000000000000000001455727245600166345ustar00rootroot00000000000000rekor-1.3.5/cmd/rekor-server/app/root.go000066400000000000000000000213501455727245600201470ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "fmt" "net/http" "net/http/pprof" "os" "time" "github.com/go-chi/chi/middleware" homedir "github.com/mitchellh/go-homedir" "github.com/sigstore/rekor/pkg/log" "github.com/spf13/cobra" "github.com/spf13/viper" "sigs.k8s.io/release-utils/version" ) var ( cfgFile string logType string enablePprof bool // these map to the operationId as defined in openapi.yaml file operationIds = []string{ "searchIndex", "getLogInfo", "getPublicKey", "getLogProof", "createLogEntry", "getLogEntryByIndex", "getLogEntryByUUID", "searchLogQuery", } ) // rootCmd represents the base command when called without any subcommands 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`, // Uncomment the following line if your bare application // has an action associated with it: // Run: func(cmd *cobra.Command, args []string) { }, } // 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 { log.Logger.Error(err) os.Exit(1) } } func init() { cobra.OnInitialize(initConfig) rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.rekor-server.yaml)") rootCmd.PersistentFlags().StringVar(&logType, "log_type", "dev", "logger type to use (dev/prod)") rootCmd.PersistentFlags().BoolVar(&enablePprof, "enable_pprof", false, "enable pprof for profiling on port 6060") rootCmd.PersistentFlags().Bool("gcp_cloud_profiling.enabled", false, "enable GCP Cloud Profiling") rootCmd.PersistentFlags().String("gcp_cloud_profiling.service", "rekor-server", "a name for the service being profiled") rootCmd.PersistentFlags().String("gcp_cloud_profiling.service_version", version.GetVersionInfo().GitVersion, "the version of the service being profiled") rootCmd.PersistentFlags().String("gcp_cloud_profiling.project_id", "", "GCP project ID") rootCmd.PersistentFlags().Bool("gcp_cloud_profiling.enable_oc_telemetry", false, "enable Profiler spans in Cloud Tracing & Cloud Monitoring") rootCmd.PersistentFlags().String("trillian_log_server.address", "127.0.0.1", "Trillian log server address") rootCmd.PersistentFlags().Uint16("trillian_log_server.port", 8090, "Trillian log server port") rootCmd.PersistentFlags().Uint("trillian_log_server.tlog_id", 0, "Trillian tree id") rootCmd.PersistentFlags().String("trillian_log_server.sharding_config", "", "path to config file for inactive shards, in JSON or YAML") rootCmd.PersistentFlags().Bool("enable_stable_checkpoint", true, "publish stable checkpoints to Redis. When disabled, gossiping may not be possible if the log checkpoint updates too frequently") rootCmd.PersistentFlags().Uint("publish_frequency", 5, "how often to publish a new checkpoint, in minutes") hostname, err := os.Hostname() if err != nil { hostname = "localhost" } rootCmd.PersistentFlags().String("rekor_server.hostname", hostname, "public hostname of instance") rootCmd.PersistentFlags().String("rekor_server.address", "127.0.0.1", "Address to bind to") rootCmd.PersistentFlags().String("rekor_server.signer", "memory", `Rekor signer to use. Valid options are: [awskms://keyname, azurekms://keyname, gcpkms://keyname, hashivault://keyname, memory, ]. Memory and file-based signers should only be used for testing.`) rootCmd.PersistentFlags().String("rekor_server.signer-passwd", "", "Password to decrypt signer private key") rootCmd.PersistentFlags().String("rekor_server.new_entry_publisher", "", "URL for pub/sub queue to send messages to when new entries are added to the log. Ignored if not set. Supported providers: [gcppubsub]") rootCmd.PersistentFlags().Bool("rekor_server.publish_events_protobuf", false, "Whether to publish events in Protobuf wire format. Applies to all enabled event types.") rootCmd.PersistentFlags().Bool("rekor_server.publish_events_json", false, "Whether to publish events in CloudEvents JSON format. Applies to all enabled event types.") rootCmd.PersistentFlags().Uint16("port", 3000, "Port to bind to") rootCmd.PersistentFlags().Bool("enable_retrieve_api", true, "enables Redis-based index API endpoint") _ = rootCmd.PersistentFlags().MarkDeprecated("enable_retrieve_api", "this flag is deprecated in favor of enabled_api_endpoints (searchIndex)") rootCmd.PersistentFlags().String("search_index.storage_provider", "redis", `Index Storage provider to use. Valid options are: [redis, mysql].`) rootCmd.PersistentFlags().String("redis_server.address", "127.0.0.1", "Redis server address") rootCmd.PersistentFlags().Uint16("redis_server.port", 6379, "Redis server port") rootCmd.PersistentFlags().String("redis_server.password", "", "Redis server password") rootCmd.PersistentFlags().Bool("enable_attestation_storage", false, "enables rich attestation storage") rootCmd.PersistentFlags().String("attestation_storage_bucket", "", "url for attestation storage bucket") rootCmd.PersistentFlags().Int("max_attestation_size", 100*1024, "max size for attestation storage, in bytes") rootCmd.PersistentFlags().StringSlice("enabled_api_endpoints", operationIds, "list of API endpoints to enable using operationId from openapi.yaml") rootCmd.PersistentFlags().Uint64("max_request_body_size", 0, "maximum size for HTTP request body, in bytes; set to 0 for unlimited") rootCmd.PersistentFlags().Uint64("max_jar_metadata_size", 1048576, "maximum permitted size for jar META-INF/ files, in bytes; set to 0 for unlimited") rootCmd.PersistentFlags().Uint64("max_apk_metadata_size", 1048576, "maximum permitted size for apk .SIGN and .PKGINFO files, in bytes; set to 0 for unlimited") rootCmd.PersistentFlags().String("search_index.mysql.dsn", "", "DSN for index storage using MySQL") rootCmd.PersistentFlags().Duration("search_index.mysql.conn_max_idletime", 0*time.Second, "maximum connection idle time") rootCmd.PersistentFlags().Duration("search_index.mysql.conn_max_lifetime", 0*time.Second, "maximum connection lifetime") rootCmd.PersistentFlags().Int("search_index.mysql.max_open_connections", 0, "maximum open connections") rootCmd.PersistentFlags().Int("search_index.mysql.max_idle_connections", 0, "maximum idle connections") rootCmd.PersistentFlags().String("http-request-id-header-name", middleware.RequestIDHeader, "name of HTTP Request Header to use as request correlation ID") rootCmd.PersistentFlags().String("trace-string-prefix", "", "if set, this will be used to prefix the 'trace' field when outputting structured logs") if err := viper.BindPFlags(rootCmd.PersistentFlags()); err != nil { log.Logger.Fatal(err) } rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") log.Logger.Debugf("pprof enabled %v", enablePprof) // Enable pprof if enablePprof { go func() { mux := http.NewServeMux() mux.HandleFunc("/debug/pprof/", pprof.Index) mux.HandleFunc("/debug/pprof/{action}", pprof.Index) mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) srv := &http.Server{ Addr: ":6060", ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, Handler: mux, } if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Logger.Fatalf("Error when starting or running http server: %v", err) } }() } } // initConfig reads in config file and ENV variables if set. func initConfig() { if cfgFile != "" { // Use config file from the flag. viper.SetConfigFile(cfgFile) } else { // Find home directory. home, err := homedir.Dir() if err != nil { fmt.Println(err) os.Exit(1) } viper.AddConfigPath(home) viper.AddConfigPath(".") viper.SetConfigName("rekor-server") viper.SetConfigType("yaml") } viper.AutomaticEnv() // read in environment variables that match // If a config file is found, read it in. if err := viper.ReadInConfig(); err == nil { log.Logger.Infof("Using config file: %s", viper.ConfigFileUsed()) } } rekor-1.3.5/cmd/rekor-server/app/serve.go000066400000000000000000000127361455727245600203200ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "flag" "net/http" "time" "cloud.google.com/go/profiler" "github.com/go-chi/chi/middleware" "github.com/go-openapi/loads" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/cobra" "github.com/spf13/viper" "sigs.k8s.io/release-utils/version" "github.com/sigstore/rekor/pkg/api" "github.com/sigstore/rekor/pkg/generated/restapi" "github.com/sigstore/rekor/pkg/generated/restapi/operations" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/types/alpine" alpine_v001 "github.com/sigstore/rekor/pkg/types/alpine/v0.0.1" "github.com/sigstore/rekor/pkg/types/cose" cose_v001 "github.com/sigstore/rekor/pkg/types/cose/v0.0.1" "github.com/sigstore/rekor/pkg/types/dsse" dsse_v001 "github.com/sigstore/rekor/pkg/types/dsse/v0.0.1" hashedrekord "github.com/sigstore/rekor/pkg/types/hashedrekord" hashedrekord_v001 "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1" "github.com/sigstore/rekor/pkg/types/helm" helm_v001 "github.com/sigstore/rekor/pkg/types/helm/v0.0.1" "github.com/sigstore/rekor/pkg/types/intoto" intoto_v001 "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1" intoto_v002 "github.com/sigstore/rekor/pkg/types/intoto/v0.0.2" "github.com/sigstore/rekor/pkg/types/jar" jar_v001 "github.com/sigstore/rekor/pkg/types/jar/v0.0.1" "github.com/sigstore/rekor/pkg/types/rekord" rekord_v001 "github.com/sigstore/rekor/pkg/types/rekord/v0.0.1" "github.com/sigstore/rekor/pkg/types/rfc3161" rfc3161_v001 "github.com/sigstore/rekor/pkg/types/rfc3161/v0.0.1" "github.com/sigstore/rekor/pkg/types/rpm" rpm_v001 "github.com/sigstore/rekor/pkg/types/rpm/v0.0.1" "github.com/sigstore/rekor/pkg/types/tuf" tuf_v001 "github.com/sigstore/rekor/pkg/types/tuf/v0.0.1" ) // serveCmd represents the serve command var serveCmd = &cobra.Command{ Use: "serve", Short: "start http server with configured api", Long: `Starts a http server and serves the configured api`, Run: func(cmd *cobra.Command, args []string) { // Setup the logger to dev/prod log.ConfigureLogger(viper.GetString("log_type"), viper.GetString("trace-string-prefix")) // workaround for https://github.com/sigstore/rekor/issues/68 // from https://github.com/golang/glog/commit/fca8c8854093a154ff1eb580aae10276ad6b1b5f _ = flag.CommandLine.Parse([]string{}) vi := version.GetVersionInfo() viStr, err := vi.JSONString() if err != nil { viStr = vi.String() } log.Logger.Infof("starting rekor-server @ %v", viStr) // overrides the correlation ID printed in logs, if config is set middleware.RequestIDHeader = viper.GetString("http-request-id-header-name") if viper.GetBool("gcp_cloud_profiling.enabled") { cfg := profiler.Config{ Service: viper.GetString("gcp_cloud_profiling.service"), ServiceVersion: viper.GetString("gcp_cloud_profiling.service_version"), ProjectID: viper.GetString("gcp_cloud_profiling.project_id"), EnableOCTelemetry: viper.GetBool("gcp_cloud_profiling.enable_oc_telemetry"), } if err := profiler.Start(cfg); err != nil { log.Logger.Warnf("Error configuring GCP Cloud Profiling: %v", err) } } doc, _ := loads.Embedded(restapi.SwaggerJSON, restapi.FlatSwaggerJSON) server := restapi.NewServer(operations.NewRekorServerAPI(doc)) defer func() { if err := server.Shutdown(); err != nil { log.Logger.Error(err) } }() //TODO: make this a config option for server to load via viper field //TODO: add command line option to print versions supported in binary // these trigger loading of package and therefore init() methods to run pluggableTypeMap := map[string][]string{ rekord.KIND: {rekord_v001.APIVERSION}, rpm.KIND: {rpm_v001.APIVERSION}, jar.KIND: {jar_v001.APIVERSION}, intoto.KIND: {intoto_v001.APIVERSION, intoto_v002.APIVERSION}, cose.KIND: {cose_v001.APIVERSION}, rfc3161.KIND: {rfc3161_v001.APIVERSION}, alpine.KIND: {alpine_v001.APIVERSION}, helm.KIND: {helm_v001.APIVERSION}, tuf.KIND: {tuf_v001.APIVERSION}, hashedrekord.KIND: {hashedrekord_v001.APIVERSION}, dsse.KIND: {dsse_v001.APIVERSION}, } for k, v := range pluggableTypeMap { log.Logger.Infof("Loading support for pluggable type '%v'", k) log.Logger.Infof("Loading version '%v' for pluggable type '%v'", v, k) } server.Host = viper.GetString("rekor_server.address") server.Port = int(viper.GetUint("port")) server.EnabledListeners = []string{"http"} treeID := viper.GetUint("trillian_log_server.tlog_id") api.ConfigureAPI(treeID) server.ConfigureAPI() http.Handle("/metrics", promhttp.Handler()) go func() { srv := &http.Server{ Addr: ":2112", ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, } _ = srv.ListenAndServe() }() if err := server.Serve(); err != nil { log.Logger.Fatal(err) } }, } func init() { rootCmd.AddCommand(serveCmd) } rekor-1.3.5/cmd/rekor-server/app/version.go000066400000000000000000000013171455727245600206520ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package app import ( "sigs.k8s.io/release-utils/version" ) func init() { rootCmd.AddCommand(version.Version()) } rekor-1.3.5/cmd/rekor-server/e2e_test.go000066400000000000000000000263341455727245600201250ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package main import ( "bufio" "bytes" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "io" "io/ioutil" "math/rand" "net/http" "os" "path/filepath" "regexp" "strconv" "strings" "testing" "github.com/sigstore/rekor/pkg/sharding" "github.com/sigstore/rekor/pkg/util" ) func TestDuplicates(t *testing.T) { artifactPath := filepath.Join(t.TempDir(), "artifact") sigPath := filepath.Join(t.TempDir(), "signature.asc") util.CreatedPGPSignedArtifact(t, artifactPath, sigPath) // Write the public key to a file pubPath := filepath.Join(t.TempDir(), "pubKey.asc") if err := ioutil.WriteFile(pubPath, []byte(util.PubKey), 0644); err != nil { t.Fatal(err) } // Now upload to rekor! out := util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) util.OutputContains(t, out, "Created entry at") // Now upload the same one again, we should get a dupe entry. out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) util.OutputContains(t, out, "Entry already exists") // Now do a new one, we should get a new entry util.CreatedPGPSignedArtifact(t, artifactPath, sigPath) out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) util.OutputContains(t, out, "Created entry at") } // Smoke test to ensure we're publishing and recording metrics when an API is // called. // TODO: use a more robust test approach here e.g. prometheus client-based? // TODO: cover all endpoints to make sure none are dropped. func TestMetricsCounts(t *testing.T) { latencyMetric := "rekor_latency_by_api_count{method=\"GET\",path=\"/api/v1/log\"}" qpsMetric := "rekor_qps_by_api{code=\"200\",method=\"GET\",path=\"/api/v1/log\"}" latencyCount, err := getRekorMetricCount(latencyMetric, t) if err != nil { t.Fatal(err) } qpsCount, err := getRekorMetricCount(qpsMetric, t) if err != nil { t.Fatal(err) } resp, err := http.Get("http://localhost:3000/api/v1/log") if err != nil { t.Fatal(err) } resp.Body.Close() latencyCount2, err := getRekorMetricCount(latencyMetric, t) if err != nil { t.Fatal(err) } qpsCount2, err := getRekorMetricCount(qpsMetric, t) if err != nil { t.Fatal(err) } if latencyCount2-latencyCount != 1 { t.Error("rekor_latency_by_api_count did not increment") } if qpsCount2-qpsCount != 1 { t.Error("rekor_qps_by_api did not increment") } } func getRekorMetricCount(metricLine string, t *testing.T) (int, error) { t.Helper() re, err := regexp.Compile(fmt.Sprintf("^%s\\s*([0-9]+)$", regexp.QuoteMeta(metricLine))) if err != nil { return 0, err } resp, err := http.Get("http://localhost:2112/metrics") if err != nil { return 0, err } defer resp.Body.Close() scanner := bufio.NewScanner(resp.Body) for scanner.Scan() { match := re.FindStringSubmatch(scanner.Text()) if len(match) != 2 { continue } result, err := strconv.Atoi(match[1]) if err != nil { return 0, err } t.Log("Matched metric line: " + scanner.Text()) return result, nil } return 0, nil } func TestEnvVariableValidation(t *testing.T) { os.Setenv("REKOR_FORMAT", "bogus") defer os.Unsetenv("REKOR_FORMAT") util.RunCliErr(t, "loginfo") } func TestGetCLI(t *testing.T) { // Create something and add it to the log artifactPath := filepath.Join(t.TempDir(), "artifact") sigPath := filepath.Join(t.TempDir(), "signature.asc") t.Cleanup(func() { os.Remove(artifactPath) os.Remove(sigPath) }) util.CreatedPGPSignedArtifact(t, artifactPath, sigPath) // Write the public key to a file pubPath := filepath.Join(t.TempDir(), "pubKey.asc") if err := ioutil.WriteFile(pubPath, []byte(util.PubKey), 0644); err != nil { t.Error(err) } t.Cleanup(func() { os.Remove(pubPath) }) out := util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) util.OutputContains(t, out, "Created entry at") uuid, err := sharding.GetUUIDFromIDString(util.GetUUIDFromUploadOutput(t, out)) if err != nil { t.Error(err) } // since we at least have 1 valid entry, check the log at index 0 util.RunCli(t, "get", "--log-index", "0") out = util.RunCli(t, "get", "--format=json", "--uuid", uuid) // The output here should be in JSON with this structure: g := util.GetOut{} if err := json.Unmarshal([]byte(out), &g); err != nil { t.Error(err) } if g.IntegratedTime == 0 { t.Errorf("Expected IntegratedTime to be set. Got %s", out) } // Get it with the logindex as well util.RunCli(t, "get", "--format=json", "--log-index", strconv.Itoa(g.LogIndex)) // check index via the file and public key to ensure that the index has updated correctly out = util.RunCli(t, "search", "--artifact", artifactPath) util.OutputContains(t, out, uuid) out = util.RunCli(t, "search", "--public-key", pubPath) util.OutputContains(t, out, uuid) artifactBytes, err := ioutil.ReadFile(artifactPath) if err != nil { t.Error(err) } sha := sha256.Sum256(artifactBytes) out = util.RunCli(t, "search", "--sha", fmt.Sprintf("sha256:%s", hex.EncodeToString(sha[:]))) util.OutputContains(t, out, uuid) // Exercise GET with the new EntryID (TreeID + UUID) tid := getTreeID(t) entryID, err := sharding.CreateEntryIDFromParts(fmt.Sprintf("%x", tid), uuid) if err != nil { t.Error(err) } out = util.RunCli(t, "get", "--format=json", "--uuid", entryID.ReturnEntryIDString()) } func getTreeID(t *testing.T) int64 { t.Helper() out := util.RunCli(t, "loginfo") tidStr := strings.TrimSpace(strings.Split(out, "TreeID: ")[1]) tid, err := strconv.ParseInt(tidStr, 10, 64) if err != nil { t.Errorf(err.Error()) } t.Log("Tree ID:", tid) return tid } func TestSearchNoEntriesRC1(t *testing.T) { util.RunCliErr(t, "search", "--email", "noone@internetz.com") } func TestHostnameInSTH(t *testing.T) { // get ID of container rekorContainerID := strings.Trim(util.Run(t, "", "docker", "ps", "-q", "-f", "name=rekor-server"), "\n") resp, err := http.Get(fmt.Sprintf("%s/api/v1/log", rekorServer())) if err != nil { t.Fatal(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal(err) } if !strings.Contains(string(body), fmt.Sprintf(" %s ", rekorContainerID)) { t.Errorf("logInfo does not contain the hostname (%v) of the rekor-server container: %v", rekorContainerID, string(body)) } if strings.Contains(string(body), "rekor.sigstore.dev") { t.Errorf("logInfo contains rekor.sigstore.dev which should not be set by default") } } func rekorServer() string { if s := os.Getenv("REKOR_SERVER"); s != "" { return s } return "http://localhost:3000" } func TestSearchSHA512(t *testing.T) { sha512 := "c7694a1112ea1404a3c5852bdda04c2cc224b3567ef6ceb8204dbf2b382daacfc6837ee2ed9d5b82c90b880a3c7289778dbd5a8c2c08193459bcf7bd44581ed0" var out string out = util.RunCli(t, "upload", "--type", "intoto:0.0.2", "--artifact", "tests/envelope.sha512", "--pki-format", "x509", "--public-key", "tests/test_sha512.pub") util.OutputContains(t, out, "Created entry at") uuid := util.GetUUIDFromTimestampOutput(t, out) out = util.RunCli(t, "search", "--sha", fmt.Sprintf("sha512:%s", sha512)) util.OutputContains(t, out, uuid) } func TestVerifyNonExistentUUID(t *testing.T) { // this uuid is extremely likely to not exist out := util.RunCliErr(t, "verify", "--uuid", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") util.OutputContains(t, out, "entry in log cannot be located") // Check response code tid := getTreeID(t) h := sha256.Sum256([]byte("123")) entryID, err := sharding.CreateEntryIDFromParts(fmt.Sprintf("%x", tid), hex.EncodeToString(h[:])) if err != nil { t.Fatal(err) } body := fmt.Sprintf("{\"entryUUIDs\":[\"%s\"]}", entryID.ReturnEntryIDString()) resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewReader([]byte(body))) if err != nil { t.Fatal(err) } c, _ := ioutil.ReadAll(resp.Body) if resp.StatusCode != 200 { t.Fatalf("expected status 200, got %d instead", resp.StatusCode) } if strings.TrimSpace(string(c)) != "[]" { t.Fatalf("expected empty JSON array as response, got %s instead", string(c)) } } func TestSearchQueryLimit(t *testing.T) { tests := []struct { description string limit int shouldErr bool }{ { description: "request 6 entries", limit: 6, }, { description: "request 10 entries", limit: 10, }, { description: "request more than max", limit: 12, shouldErr: true, }, } for _, test := range tests { t.Run(test.description, func(t *testing.T) { b := bytes.NewReader(getBody(t, test.limit)) resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", b) if err != nil { t.Fatal(err) } c, _ := ioutil.ReadAll(resp.Body) t.Log(string(c)) if resp.StatusCode != 200 && !test.shouldErr { t.Fatalf("expected test to pass but it failed") } if resp.StatusCode != 422 && test.shouldErr { t.Fatal("expected test to fail but it passed") } if test.shouldErr && !strings.Contains(string(c), "logIndexes in body should have at most 10 items") { t.Fatal("expected max limit error but didn't get it") } }) } } func getBody(t *testing.T, limit int) []byte { t.Helper() s := fmt.Sprintf("{\"logIndexes\": [%d", limit) for i := 1; i < limit; i++ { s = fmt.Sprintf("%s, %d", s, i) } s += "]}" return []byte(s) } func TestSearchQueryMalformedEntry(t *testing.T) { wd, err := os.Getwd() if err != nil { t.Fatal(err) } b, err := ioutil.ReadFile(filepath.Join(wd, "tests/rekor.json")) if err != nil { t.Fatal(err) } body := fmt.Sprintf("{\"entries\":[\"%s\"]}", b) resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewBuffer([]byte(body))) if err != nil { t.Fatal(err) } if resp.StatusCode != 400 { t.Fatalf("expected status 400, got %d instead", resp.StatusCode) } } func TestHTTPMaxRequestBodySize(t *testing.T) { // default value is 32Mb so let's try to propose something bigger pipeR, pipeW := io.Pipe() go func() { _, _ = io.CopyN(base64.NewEncoder(base64.StdEncoding, pipeW), rand.New(rand.NewSource(123)), 33*1024768) pipeW.Close() }() // json parsing will hit first so we need to make sure this is valid JSON bodyReader := io.MultiReader(strings.NewReader("{ \"key\": \""), pipeR, strings.NewReader("\"}")) resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bodyReader) if err != nil { t.Fatal(err) } if resp.StatusCode != http.StatusRequestEntityTooLarge { t.Fatalf("expected status %d, got %d instead", http.StatusRequestEntityTooLarge, resp.StatusCode) } } rekor-1.3.5/cmd/rekor-server/main.go000066400000000000000000000013001455727245600173210ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import "github.com/sigstore/rekor/cmd/rekor-server/app" func main() { app.Execute() } rekor-1.3.5/cmd/rekor-server/main_test.go000066400000000000000000000013421455727245600203660ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "testing" "github.com/sigstore/rekor/cmd/rekor-server/app" ) func TestCover(_ *testing.T) { app.Execute() } rekor-1.3.5/cmd/rekor-server/tests/000077500000000000000000000000001455727245600172165ustar00rootroot00000000000000rekor-1.3.5/cmd/rekor-server/tests/envelope.sha512000066400000000000000000000045121455727245600217620ustar00rootroot00000000000000{ "payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsInN1YmplY3QiOlt7Im5hbWUiOiJmb28tbGludXgtYW1kNjQiLCJkaWdlc3QiOnsic2hhNTEyIjoiYzc2OTRhMTExMmVhMTQwNGEzYzU4NTJiZGRhMDRjMmNjMjI0YjM1NjdlZjZjZWI4MjA0ZGJmMmIzODJkYWFjZmM2ODM3ZWUyZWQ5ZDViODJjOTBiODgwYTNjNzI4OTc3OGRiZDVhOGMyYzA4MTkzNDU5YmNmN2JkNDQ1ODFlZDAifX1dLCJwcmVkaWNhdGUiOnsiYnVpbGRlciI6eyJpZCI6Imh0dHBzOi8vZ2l0aHViLmNvbS9zbHNhLWZyYW1ld29yay9zbHNhLWdpdGh1Yi1nZW5lcmF0b3ItZ28vLmdpdGh1Yi93b3JrZmxvd3Mvc2xzYTNfYnVpbGRlci55bWxAcmVmcy9oZWFkcy9tYWluIn0sImJ1aWxkVHlwZSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9zbHNhLWZyYW1ld29yay9zbHNhLWdpdGh1Yi1nZW5lcmF0b3ItZ29AdjEiLCJpbnZvY2F0aW9uIjp7ImNvbmZpZ1NvdXJjZSI6eyJ1cmkiOiJnaXQraHR0cHM6Ly9naXRodWIuY29tL2tvbW1lbmRvcmthcHRlbi9zcHRAcmVmcy90YWdzLzAuMC4zIiwiZGlnZXN0Ijp7InNoYTEiOiJhMmJlYjU5MGFlODU2NzRiYjNhZmU5ZWY1NDkzZjcxOGNkZmY4ZWU3In0sImVudHJ5UG9pbnQiOiJTTFNBIGdvIHJlbGVhc2VyIn0sInBhcmFtZXRlcnMiOnt9LCJlbnZpcm9ubWVudCI6eyJnaXRodWJfaGVhZF9yZWYiOiIiLCJnaXRodWJfcmVmIjoicmVmcy90YWdzLzAuMC4zIiwiZ2l0aHViX3JlZl90eXBlIjoidGFnIiwiZ2l0aHViX3J1bl9hdHRlbXB0IjoiMSIsImdpdGh1Yl9ydW5faWQiOiIyMjc1NTE0NTQ5IiwiZ2l0aHViX3J1bl9udW1iZXIiOiI0IiwiZ2l0aHViX3NoYTEiOiJhMmJlYjU5MGFlODU2NzRiYjNhZmU5ZWY1NDkzZjcxOGNkZmY4ZWU3Iiwib3MiOiJ1YnVudHUyMCJ9fSwiYnVpbGRDb25maWciOnsidmVyc2lvbiI6MSwic3RlcHMiOlt7ImNvbW1hbmQiOlsiL29wdC9ob3N0ZWR0b29sY2FjaGUvZ28vMS4xNy45L3g2NC9iaW4vZ28iLCJidWlsZCIsIi1tb2Q9dmVuZG9yIiwiLXRyaW1wYXRoIiwiLXRhZ3M9bmV0Z28iLCItbyIsImZvby1saW51eC1hbWQ2NCIsIi4vY21kL2ZvbyJdLCJlbnYiOlsiR09PUz1saW51eCIsIkdPQVJDSD1hbWQ2NCIsIkNHT19FTkFCTEVEPTAiXX1dfSwibWV0YWRhdGEiOnsiYnVpbGRJbnZvY2F0aW9uSUQiOiIyMjc1NTE0NTQ5LTEiLCJjb21wbGV0ZW5lc3MiOnsicGFyYW1ldGVycyI6dHJ1ZSwiZW52aXJvbm1lbnQiOmZhbHNlLCJtYXRlcmlhbHMiOmZhbHNlfSwicmVwcm9kdWNpYmxlIjpmYWxzZX0sIm1hdGVyaWFscyI6W3sidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS9rb21tZW5kb3JrYXB0ZW4vc3B0QHJlZnMvdGFncy8wLjAuMyIsImRpZ2VzdCI6eyJzaGExIjoiYTJiZWI1OTBhZTg1Njc0YmIzYWZlOWVmNTQ5M2Y3MThjZGZmOGVlNyJ9fSx7InVyaSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9hY3Rpb25zL3ZpcnR1YWwtZW52aXJvbm1lbnRzL3JlbGVhc2VzL3RhZy91YnVudHUyMC8yMDIyMDUwMy4xIn1dfX0=", "payloadType":"application/vnd.in-toto+json", "signatures":[ { "keyid":"", "sig": "MEUCIHWBOrLhqz/ff5AYOLPcizNgSpUHXcSbJJelQtg46JjBAiEA/IakDaHox6vYglVaa+vPOlE2F5SovMN15y0MumQpgVQ=" } ] }rekor-1.3.5/cmd/rekor-server/tests/rekor.json000066400000000000000000000105011455727245600212300ustar00rootroot00000000000000{ "kind": "rekord", "apiVersion": "0.0.1", "spec": { "signature": { "format": "pgp", "content": "iQHKBAABCAA0FiEEcgCUXG78adj6hGUJJrfBoJ04pHoFAl+86RwWHGxoaW5kc0Bwcm90b25tYWlsLmNvbQAKCRAmt8GgnTikejcHC/9yyGEPh2D+MnNR8I8w0sfWChc6pGAQoS6qk/sfC/9GvF4OC7RIy6OwLr/lxyEZbOP2ngYjh/s5KjKxhZyApwwg13LmcbazGnXc3E76J55LoTfwoRa9fupH/M6HI56VFKwnu+AbMNW1s+DM47r7i5nIN6IX9kMpDe3B9XTUULff/yNUv0XtXU+VAf8ndF1w117YVWxf8TnU/HWvX74URQPN+syuyqK/NO1H1KhBVTzcIYd5H6kJu300jgkDypyyqQpd/pJYVwfeY8fCOaeCpfIPjKQ/4enCsAeBgKsAwfIbor8WiE86KoANYqROaW7uqiN+VPadbWVeN6bMpRIdEq8+NKQGlepSCRqbkVg4VKGOPgB3h5WbY9U1O1FVDnXyt7kWdEPEZjBX+V4DawshvNe5LIyqH5hJ1QNAFd0UStqKQt8EUZ/gAtQiXSGbxM1ACoYL9HblKW5b+kj/onKghekFoCoAfhMwRRqR5g/TS/Pc2/ztwYTIuhpQQfMXziTm64g=", "publicKey": { "content":"LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tCgptUUdOQkYrY0lNMEJEQUNhOEc3UkQydjNtaXdNdHhWYVppM0pCVnVlVkFxSEtDNGVLb01TNUhNQ1JvK0haVlJBCjcwWG1zVHBYMVoxZ1pRdXVDMEdEWTI2aEJoZWpBcTNoeDJydjYvOHE5MEJ2V0dIOXRWZUdwTDFzYUltNTJnRVIKWHlWZ2d6NWtBQzBTNnZNbjdkcjJldEJrV1dQK09qMDVTMDJZWkJUWWd4cE9ieWVjVVNjcUtOVGpzbFpRQkgyZApTSHVrM28yWjdoTTQ5VTBsN3piV3c0b0lUK2xBUmNzYWpRVHdXamxpYVBEL0hSalQyblJPaEloaXRlZC93Z3l6CnlkSXE1ZTZzMThWTGNUNzVxV25yWlhOUFdGd2YyNVJYWTN1dGtXK0dXNW5RZU44MFEya1JFZ2t4RnM1QWQ1V1oKdkU3dDgvaHg1em1zbFo0dGZGNHNpM1FaZUlRQmFjWWJ3eE1QU0RmOW9GR3hkR0ZUODg5d0pMR2dXbXIxVGtQTQpjTjA2d3hBUkd0eE4wejYxRkpUaWpMV1JialczdW5JOWhjUWNVbE4vUSsxNm90SHBlS1ZnNG9XMDBDcnZXT0Q2CnFrTGNNRDQ5eVVEOGZTR0IrUkVuaUJhODlDOWtRVTRTS2Rnc0xML1ErSksrU3k5S21JRHJtMWE0RGZQMXBzZmUKTGphcnpzVlpmS1VIZndjQUVRRUFBYlFpVEhWclpTQklhVzVrY3lBOGJHaHBibVJ6UUhCeWIzUnZibTFoYVd3dQpZMjl0UG9rQjFBUVRBUWdBUGhZaEJISUFsRnh1L0duWStvUmxDU2Ezd2FDZE9LUjZCUUpmbkNETkFoc0RCUWtECndtY0FCUXNKQ0FjQ0JoVUtDUWdMQWdRV0FnTUJBaDRCQWhlQUFBb0pFQ2Ezd2FDZE9LUjZaMWtMLzFJSzB2ZGUKWlg1cjVTZWJOeFRJTlNBQXZZa3JLUnlKNWY3bE9NOWdMR0l1YzJGb05VbmpWUVQwcklHOTAxOWg0OHBDeTkxZgpYakREUk1ZOWd6RldXQ2dHblhoMWhXSTNNN0JKRjZZRTZ1NkRYR3N2dVVwR3JOZVpBRzZra2F6QXVBbm5WMGtDCjA4em9SckFaQ3ZscGFacnlkOGl0YityVitRS3A3QXcybEFJSDFlNmR3TTRSTEZqdmZrOExKWHhqSkFvUG13NmwKTHcxOGM3b1c2UkxPOVFYUThlTTZyMnZISHBtMFR1ZHZaeWFmTnVDMzJHRGxNWTR1MFYxRGI4THN5bVBzQWh1QQoySno0L0tQcTZ1S3dJdG1WSzRwbmRmRUR1NkQxVG9vRFlYaXB0WWFmZHZVMzNwVVF4d0hvZlRUZkU1elp3MlBlCmxIM25aZHNnSFhHUHhKTExNcU9wVzRDL2NNNlpRVmdZU3RWcjBudlU2NitRalF2c2tVWlIwNmRkRXpuQnBHSnMKdHBtajlBZS9HUlk4RU5uTjkvMkdmRXVydHozZEtOVVpvak15MTUzamNHMFUxenpoMTE1V0o3dDh3SEJ1NFM0cAowZ0UrUkFxeXRBY0laRGQyTlNOcno4VnI5RkU5eCtmYXQ5RVJsYm5kQUJFNWlWOHNLMCtGYW5Xd2dia0JqUVJmCm5DRE5BUXdBdEJvdGhmY1J6cjN4cjNQOXA3UUNNd0t1aW9udk1DbThXZ3dOUzRDcGhxbzVOT3IyaU1qa0xQMEoKb21nSkxWWDVOK2Jydjh5NEg4cllQd0tCMTZvL2hBOEliR2JwWXltM0ZjeWtUd2NiV2J0UFRMRXRkQ1VQTFlURApOQzVMR0pwZzNlODZZZlF0QU42L01uWnlZT21sRHgyV0d0dExkbXNBU0dWdXg2QVZKcUl2K3gwNlVLSkVtSzN0CmpsRVZLeWcxMlJFenllNUlUNnFFU0dwT3pvMllsV1VxSVR3L0FhUFEyWnhVYXh2WUZvVU9jd2djZG5Ia2dzaEkKT245aC9OSFVtUDMyV1F2cWtRTXVVYVBJTlJzQzgzS3ZUREdseWZTSFZGek1hNGhETWhFY1h6NGFjaW5kNVdUZQp6eUxnWmhPYjdjTmVDeDR4Y3J0UEI2VTdCUi9GVkx6TEJsQXp1emppRWhZd0pvM0FPTXFGb1I1bUFxaGx1dE5PCnNzeW9mYnFUZ0diU0xkamJYUC9hRXRnejJNVjluL29jMVNCOEhlWk8vMTdKeWduenJ1SUt5Ky9sT1dPenQralYKVkZwVnloMXVlOGxGN3ltS1I0dHNsK2lJVmJxblB2cE1oTE9JQnFYRm4yZ01Da0dvSkx5N09IbzJXQUVKR2x0MwpTd3BicmpqMUFCRUJBQUdKQWJ3RUdBRUlBQ1lXSVFSeUFKUmNidnhwMlBxRVpRa210OEdnblRpa2VnVUNYNXdnCnpRSWJEQVVKQThKbkFBQUtDUkFtdDhHZ25UaWtlaW5pREFDRUFma1pxLzRScDJhTkE0ZGJvSjdVRlhET2FSa1YKOU1Lb0VaRnFUTU5vdkRMNXhoTWxnbFBQdS9sK2RoVGd4ZGVKOUVWSG9lenRiODk2VS9wT3VCUnNuOVZ0VzRZLwpqZWlXN0V5TlhBZC9PcnZuRmJ4KzdpWExxdXBaSkpGVGkvajlSaFZZTnNtbDdzZWJUUGVCbkdEQTkxcWJDNHhICnBRVkRDdWp4NjlWeE81RTFMU29oQ00rTy81dkxCbThpMW8vbmJGbWJ5N1ZDeUtlUkRmaHRmOW5DODRxc0U5R3EKVTcvTFNpazliZnhNV2JwcTh5a250bVMzYTBzemM0YlZGcGV6QnBtTmIwQVZjQitUbTlnV21FemhpTHM2RktBTgpJbnFOdVh1Qkw5UENhYzcrbVUrYzJtQmdHT1JHZDFkWk8zUkM4OXpGM3hCQlluQ09lNWNBTUZsYzFYR3NsbHNJCmR6ZHJkWHZiTkJ6L2o3MXB1TjhvRlltL1hiVmNpZU8wVGZRaURjVHQ4S2lpUjlUQUQ5L1A1OTNSTWxMT0dTOHAKaHZKYmlGb1pmWEhjbHNaRkhtOERRUWE5NElad1RCOG00Z0JWME0yWFN2ZEhvMzBsc3FqdFphWmlTclJoNHJzaApuMTRwYkFhVGRhS0VQY3Z0dWZiVXVXMElqWWQya3BJVC90Zz0KPU9naHIKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQo=" } }, "data": { "url": "https://raw.githubusercontent.com/sigstore/rekor/main/tests/test_file.txt", "hash": { "algorithm": "sha256", "value": "45c7b11fcbf07dec1694adecd8c5b85770a12a6c8dfdcf2580a2db0c47c31779" } } } } rekor-1.3.5/cmd/rekor-server/tests/test_sha512.pub000066400000000000000000000002621455727245600217700ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEREk5aczUtKOZYY6qZoLSVSwjJN1B Unqr/1uX6LqYnHwBN43kN/ht11kWyl4NJYta3eCw5Aw/DUEU+T0ZfSyJlA== -----END PUBLIC KEY----- rekor-1.3.5/codecov.yml000066400000000000000000000011761455727245600150350ustar00rootroot00000000000000# Copyright 2023 The Sigstore Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. coverage: status: project: off patch: off rekor-1.3.5/config/000077500000000000000000000000001455727245600141305ustar00rootroot00000000000000rekor-1.3.5/config/rekor.yaml000066400000000000000000000061271455727245600161440ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. apiVersion: v1 kind: Namespace metadata: name: rekor-system labels: name: rekor-system --- apiVersion: apps/v1 kind: Deployment metadata: namespace: rekor-system name: rekor-server labels: app: rekor-server spec: replicas: 3 selector: matchLabels: app: rekor-server template: metadata: labels: app: rekor-server annotations: prometheus.io/scrape: "true" prometheus.io/path: /metrics prometheus.io/port: "2112" spec: containers: - name: rekor-server image: ko://github.com/sigstore/rekor/cmd/rekor-server ports: - containerPort: 3000 - containerPort: 2112 # metrics args: [ "serve", "--trillian_log_server.address=trillian-server", "--trillian_log_server.port=8090", "--rekor_server.address=0.0.0.0", "--redis_server.address=10.234.175.59", "--redis_server.port=6379", "--trillian_log_server.tlog_id=3904496407287907110", "--log_type=prod", "--rekor_server.signer=$(KMS)", "--trillian_log_server.sharding_config=/sharding/sharding-config.yaml", "--enable_attestation_storage=$(ENABLE_ATTESTATION_STORAGE)", "--attestation_storage_bucket=$(ATTESTATION_BUCKET)" ] volumeMounts: - name: sharding-config mountPath: /sharding env: - name: KMS valueFrom: configMapKeyRef: name: rekor-config key: kms - name: ENABLE_ATTESTATION_STORAGE valueFrom: configMapKeyRef: name: rekor-config key: enable_attestation_storage - name: ATTESTATION_BUCKET valueFrom: configMapKeyRef: name: rekor-config key: attestation_bucket resources: requests: memory: "1G" cpu: ".5" securityContext: readOnlyRootFilesystem: true runAsNonRoot: true capabilities: drop: - all volumes: - name: sharding-config configMap: name: sharding-config --- apiVersion: v1 kind: Service metadata: namespace: rekor-system name: rekor-server spec: selector: app: rekor-server type: LoadBalancer ports: - protocol: TCP port: 80 targetPort: 3000 --- apiVersion: v1 kind: ConfigMap metadata: name: sharding-config namespace: rekor-system data: sharding-config.yaml: "" --- rekor-1.3.5/docker-compose.debug.yml000066400000000000000000000024561455727245600174140ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. version: '3.4' services: rekor-server-debug: build: context: . target: "debug" command: [ "dlv", "exec", "--listen=:2345", "--headless=true", "--log=true", "--api-version=2", "--", "/usr/local/bin/rekor-server", "serve", "--trillian_log_server.address=trillian-log-server", "--trillian_log_server.port=8090", "--redis_server.address=redis-server", "--redis_server.port=6379", "--rekor_server.address=0.0.0.0", "--rekor_server.signer=memory", ] restart: always # keep the server running ports: - "3000:3000" - "2345:2345" depends_on: - mysql - redis-server - trillian-log-server rekor-1.3.5/docker-compose.test.yml000066400000000000000000000041451455727245600173020ustar00rootroot00000000000000# # Copyright 2022 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. version: '3.4' services: rekor-server: build: context: . target: "test" environment: TMPDIR: /var/run/attestations # workaround for https://github.com/google/go-cloud/issues/3294 PUBSUB_EMULATOR_HOST: gcp-pubsub-emulator:8085 command: [ "rekor-server", "-test.coverprofile=rekor-server.cov", "serve", "--trillian_log_server.address=trillian-log-server", "--trillian_log_server.port=8090", "--redis_server.address=redis-server", "--redis_server.port=6379", "--redis_server.password=test", "--rekor_server.address=0.0.0.0", "--rekor_server.signer=memory", "--enable_attestation_storage", "--attestation_storage_bucket=file:///var/run/attestations", "--max_request_body_size=32792576", "--rekor_server.new_entry_publisher=gcppubsub://projects/test-project/topics/new-entry", "--rekor_server.publish_events_json=true", "--search_index.storage_provider=mysql", "--search_index.mysql.dsn=test:zaphod@tcp(mysql:3306)/test", ] ports: - "3000:3000" - "2112:2112" depends_on: - gcp-pubsub-emulator - mysql gcp-pubsub-emulator: image: gcp-pubsub-emulator ports: - "8085:8085" command: - gcloud - beta - emulators - pubsub - start - --host-port=0.0.0.0:8085 - --project=test-project healthcheck: test: ["CMD", "nc", "-zv", "localhost", "8085"] interval: 10s timeout: 3s retries: 3 start_period: 10s rekor-1.3.5/docker-compose.yml000066400000000000000000000073221455727245600163240ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. version: '3.4' services: mysql: platform: linux/amd64 image: gcr.io/trillian-opensource-ci/db_server:v1.4.0 environment: - MYSQL_ROOT_PASSWORD=zaphod - MYSQL_DATABASE=test - MYSQL_USER=test - MYSQL_PASSWORD=zaphod restart: always # keep the MySQL server running healthcheck: test: ["CMD", "/etc/init.d/mysql", "status"] interval: 30s timeout: 3s retries: 3 start_period: 10s redis-server: image: docker.io/redis:6.2 command: [ "--bind", "0.0.0.0", "--appendonly", "yes", "--requirepass", "test" ] ports: - "6379:6379" restart: always # keep the redis server running healthcheck: test: ["CMD", "redis-cli", "-a", "test", "ping"] interval: 10s timeout: 3s retries: 3 start_period: 5s trillian-log-server: image: ghcr.io/sigstore/scaffolding/trillian_log_server@sha256:beffee16bb07b5cb051dc4e476d3a1063521ed5ae0b670efc7fe6f3507d94d2b # v1.6.0 command: [ "--quota_system=noop", "--storage_system=mysql", "--mysql_uri=test:zaphod@tcp(mysql:3306)/test", "--rpc_endpoint=0.0.0.0:8090", "--http_endpoint=0.0.0.0:8091", "--alsologtostderr", ] restart: always # retry while mysql is starting up ports: - "8090:8090" - "8091:8091" depends_on: - mysql trillian-log-signer: image: ghcr.io/sigstore/scaffolding/trillian_log_signer@sha256:79d57af375cfa997ed5452cc0c02c0396d909fcc91d11065586f119490aa9214 # v1.6.0 command: [ "--quota_system=noop", "--storage_system=mysql", "--mysql_uri=test:zaphod@tcp(mysql:3306)/test", "--rpc_endpoint=0.0.0.0:8090", "--http_endpoint=0.0.0.0:8091", "--force_master", "--alsologtostderr", ] restart: always # retry while mysql is starting up ports: - "8092:8091" depends_on: - mysql rekor-server: build: context: . target: "deploy" environment: - TMPDIR=/var/run/attestations # workaround for https://github.com/google/go-cloud/issues/3294 command: [ "rekor-server", "serve", "--trillian_log_server.address=trillian-log-server", "--trillian_log_server.port=8090", "--redis_server.address=redis-server", "--redis_server.password=test", "--redis_server.port=6379", "--rekor_server.address=0.0.0.0", "--rekor_server.signer=memory", "--enable_attestation_storage", "--attestation_storage_bucket=file:///var/run/attestations", "--enable_stable_checkpoint", "--search_index.storage_provider=mysql", "--search_index.mysql.dsn=test:zaphod@tcp(mysql:3306)/test", # Uncomment this for production logging # "--log_type=prod", ] volumes: - "/var/run/attestations:/var/run/attestations:z" restart: always # keep the server running ports: - "3000:3000" - "2112:2112" depends_on: - mysql - redis-server - trillian-log-server healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/ping"] interval: 10s timeout: 3s retries: 3 start_period: 5s rekor-1.3.5/e2e-test.sh000077500000000000000000000056121455727245600146560ustar00rootroot00000000000000#!/bin/bash # # Copyright 2022 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -e testdir=$(dirname "$0") docker_compose="docker compose -f docker-compose.yml -f docker-compose.test.yml" if ! ${docker_compose} version >/dev/null 2>&1; then docker_compose="docker-compose -f docker-compose.yml -f docker-compose.test.yml" fi rm -f /tmp/pkg-rekor-*.cov echo "installing gocovmerge" make gocovmerge echo "building test-only containers" docker build -t gcp-pubsub-emulator -f Dockerfile.pubsub-emulator . docker kill $(docker ps -q) || true echo "starting services" ${docker_compose} up -d --build echo "building CLI and server" # set the path to the root of the repo dir=$(git rev-parse --show-toplevel) go test -c ./cmd/rekor-cli -o rekor-cli -cover -covermode=count -coverpkg=./... go test -c ./cmd/rekor-server -o rekor-server -covermode=count -coverpkg=./... count=0 echo -n "waiting up to 120 sec for system to start" until [ $(${docker_compose} ps | grep -c "(healthy)") == 4 ]; do if [ $count -eq 12 ]; then echo "! timeout reached" exit 1 else echo -n "." sleep 10 let 'count+=1' fi done echo echo "running tests" REKORTMPDIR="$(mktemp -d -t rekor_test.XXXXXX)" cp $dir/rekor-cli $REKORTMPDIR/rekor-cli touch $REKORTMPDIR.rekor.yaml trap "rm -rf $REKORTMPDIR" EXIT if ! REKORTMPDIR=$REKORTMPDIR go test -tags=e2e $(go list ./... | grep -v ./tests) ; then ${docker_compose} logs --no-color > /tmp/docker-compose.log exit 1 fi if ${docker_compose} logs --no-color | grep -q "panic: runtime error:" ; then # if we're here, we found a panic echo "Failing due to panics detected in logs" ${docker_compose} logs --no-color > /tmp/docker-compose.log exit 1 fi echo "generating code coverage" ${docker_compose} restart rekor-server if ! docker cp $(docker ps -aqf "name=rekor_rekor-server" -f "name=rekor-rekor-server"):go/rekor-server.cov /tmp/pkg-rekor-server.cov ; then # failed to copy code coverage report from server echo "Failed to retrieve server code coverage report" ${docker_compose} logs --no-color > /tmp/docker-compose.log exit 1 fi # merging coverage reports and filtering out /pkg/generated from final report hack/tools/bin/gocovmerge /tmp/pkg-rekor-*.cov | grep -v "/pkg/generated/" > /tmp/pkg-rekor-merged.cov echo "code coverage $(go tool cover -func=/tmp/pkg-rekor-merged.cov | grep -E '^total\:' | sed -E 's/\s+/ /g')" rekor-1.3.5/go.mod000066400000000000000000000232761455727245600140030ustar00rootroot00000000000000module github.com/sigstore/rekor go 1.21 require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/blang/semver v3.5.1+incompatible github.com/cavaliercoder/go-rpm v0.0.0-20200122174316-8cb9fd9c31a8 github.com/go-chi/chi v4.1.2+incompatible github.com/go-openapi/errors v0.21.0 github.com/go-openapi/loads v0.21.5 github.com/go-openapi/runtime v0.27.1 github.com/go-openapi/spec v0.20.14 github.com/go-openapi/strfmt v0.22.0 github.com/go-openapi/swag v0.22.9 github.com/go-openapi/validate v0.22.6 github.com/google/go-cmp v0.6.0 github.com/google/rpmpack v0.5.0 github.com/google/trillian v1.6.0 github.com/in-toto/in-toto-golang v0.9.0 github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.5.0 github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.18.0 github.com/rs/cors v1.10.1 github.com/sassoftware/relic v7.2.1+incompatible github.com/secure-systems-lab/go-securesystemslib v0.8.0 github.com/sigstore/sigstore v1.8.1 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 github.com/theupdateframework/go-tuf v0.7.0 github.com/transparency-dev/merkle v0.0.2 github.com/veraison/go-cose v1.2.0 github.com/zalando/go-keyring v0.2.2 // indirect go.uber.org/goleak v1.3.0 go.uber.org/zap v1.26.0 gocloud.dev v0.36.0 golang.org/x/crypto v0.18.0 golang.org/x/mod v0.14.0 golang.org/x/net v0.20.0 golang.org/x/sync v0.6.0 google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac // indirect google.golang.org/grpc v1.61.0 google.golang.org/protobuf v1.32.0 gopkg.in/ini.v1 v1.67.0 sigs.k8s.io/release-utils v0.7.7 sigs.k8s.io/yaml v1.4.0 ) require ( cloud.google.com/go/profiler v0.4.0 cloud.google.com/go/pubsub v1.36.0 github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 github.com/go-redis/redismock/v9 v9.2.0 github.com/go-sql-driver/mysql v1.7.1 github.com/golang/mock v1.6.0 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-retryablehttp v0.7.5 github.com/jmoiron/sqlx v1.3.5 github.com/redis/go-redis/v9 v9.4.0 github.com/sassoftware/relic/v7 v7.6.1 github.com/sigstore/protobuf-specs v0.2.1 github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.1 github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.1 github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.1 github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.1 golang.org/x/exp v0.0.0-20231006140011-7918f672742d google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac ) require ( cloud.google.com/go/compute/metadata v0.2.3 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect github.com/alessio/shellescape v1.4.1 // indirect github.com/aws/aws-sdk-go v1.49.21 // indirect github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect github.com/aws/aws-sdk-go-v2/config v1.26.6 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 // indirect github.com/aws/aws-sdk-go-v2/service/kms v1.27.9 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect github.com/aws/smithy-go v1.19.0 // indirect github.com/cavaliergopher/cpio v1.0.1 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-jose/go-jose/v3 v3.0.1 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.0 // indirect github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // 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/vault/api v1.10.0 // indirect github.com/jellydator/ttlcache/v3 v3.1.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.17.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect go.opentelemetry.io/otel v1.22.0 // indirect go.opentelemetry.io/otel/metric v1.22.0 // indirect go.opentelemetry.io/otel/trace v1.22.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240122161410-6c6643bf1457 // indirect gopkg.in/go-jose/go-jose.v2 v2.6.1 // indirect k8s.io/klog/v2 v2.120.0 // indirect software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect ) require ( cloud.google.com/go v0.112.0 // indirect cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/iam v1.1.5 // indirect cloud.google.com/go/kms v1.15.5 // indirect cloud.google.com/go/storage v1.36.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cavaliercoder/badio v0.0.0-20160213150051-ce5280129e9e // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect github.com/go-openapi/analysis v0.22.0 // indirect github.com/go-openapi/jsonpointer v0.20.2 // indirect github.com/go-openapi/jsonreference v0.20.4 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-containerregistry v0.17.0 // indirect github.com/google/uuid v1.5.0 // indirect github.com/google/wire v0.5.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/letsencrypt/boulder v0.0.0-20230907030200-6d76a0f91e1e // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // 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.1.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/x448/float16 v0.8.4 // indirect go.mongodb.org/mongo-driver v1.13.1 // indirect go.opencensus.io v0.24.0 // indirect go.step.sm/crypto v0.42.1 go.uber.org/multierr v1.11.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/term v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/api v0.159.0 google.golang.org/appengine v1.6.8 // indirect gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 // indirect ) rekor-1.3.5/go.sum000066400000000000000000001660651455727245600140340ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/kms v1.15.5 h1:pj1sRfut2eRbD9pFRjNnPNg/CzJPuQAzUujMIM1vVeM= cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= cloud.google.com/go/profiler v0.4.0 h1:ZeRDZbsOBDyRG0OiK0Op1/XWZ3xeLwJc9zjkzczUxyY= cloud.google.com/go/profiler v0.4.0/go.mod h1:RvPlm4dilIr3oJtAOeFQU9Lrt5RoySHSDj4pTd6TWeU= cloud.google.com/go/pubsub v1.36.0 h1:cgaJ0mgwEM0YNNATXFXSnLfji6XcMieTc2mRjH1ZYdY= cloud.google.com/go/pubsub v1.36.0/go.mod h1:qQvGW4ANjuYcOpTMTy5+u6HBIoJF00cPfQ/ubMcc/D8= cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg= 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.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo= github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA= github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= 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.49.21 h1:Rl8KW6HqkwzhATwvXhyr7vD4JFUMi7oXGAw9SrxxIFY= github.com/aws/aws-sdk-go v1.49.21/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo= github.com/aws/aws-sdk-go-v2/config v1.26.6 h1:Z/7w9bUqlRI0FFQpetVuFYEsjzE3h7fpU6HuGmfPL/o= github.com/aws/aws-sdk-go-v2/config v1.26.6/go.mod h1:uKU6cnDmYCvJ+pxO9S4cWDb2yWWIH5hra+32hVh1MI4= github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8= github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 h1:FnLf60PtjXp8ZOzQfhJVsqF0OtYKQZWQfqOLshh8YXg= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7/go.mod h1:tDVvl8hyU6E9B8TrnNrZQEVkQlB8hjJwcgpPhgtlnNg= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 h1:n3GDfwqF2tzEkXlv5cuy4iy7LpKDtqDMcNLfZDu9rls= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 h1:ugD6qzjYtB7zM5PN/ZIeaAIyefPaD82G8+SJopgvUpw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9/go.mod h1:YD0aYBWCrPENpHolhKw2XDlTIWae2GKXT1T4o6N6hiM= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 h1:/90OR2XbSYfXucBMJ4U14wrjlfleq/0SB6dZDPncgmo= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9/go.mod h1:dN/Of9/fNZet7UrQQ6kTDo/VSwKPIq94vjlU16bRARc= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 h1:iEAeF6YC3l4FzlJPP9H3Ko1TXpdjdqWffxXjp8SY6uk= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9/go.mod h1:kjsXoK23q9Z/tLBrckZLLyvjhZoS+AGrzqzUfEClvMM= github.com/aws/aws-sdk-go-v2/service/kms v1.27.9 h1:W9PbZAZAEcelhhjb7KuwUtf+Lbc+i7ByYJRuWLlnxyQ= github.com/aws/aws-sdk-go-v2/service/kms v1.27.9/go.mod h1:2tFmR7fQnOdQlM2ZCEPpFnBIQD1U8wmXmduBgZbOag0= github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5 h1:Keso8lIOS+IzI2MkPZyK6G0LYcK3My2LQ+T5bxghEAY= github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5/go.mod h1:vADO6Jn+Rq4nDtfwNjhgR84qkZwiC6FqCaXdw/kYwjA= github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow= github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U= github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= 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/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/cavaliercoder/badio v0.0.0-20160213150051-ce5280129e9e h1:YYUjy5BRwO5zPtfk+aa2gw255FIIoi93zMmuy19o0bc= github.com/cavaliercoder/badio v0.0.0-20160213150051-ce5280129e9e/go.mod h1:V284PjgVwSk4ETmz84rpu9ehpGg7swlIH8npP9k2bGw= github.com/cavaliercoder/go-rpm v0.0.0-20200122174316-8cb9fd9c31a8 h1:jP7ki8Tzx9ThnFPLDhBYAhEpI2+jOURnHQNURgsMvnY= github.com/cavaliercoder/go-rpm v0.0.0-20200122174316-8cb9fd9c31a8/go.mod h1:AZIh1CCnMrcVm6afFf96PBvE2MRpWFco91z8ObJtgDY= github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM= github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/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/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 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/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 h1:vU+EP9ZuFUCYE0NYLwTSob+3LNEJATzNfP/DC7SWGWI= github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 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.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/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.22.0 h1:wQ/d07nf78HNj4u+KiSY0sT234IAyePPbMgpUjUJQR0= github.com/go-openapi/analysis v0.22.0/go.mod h1:acDnkkCI2QxIo8sSIPgmp1wUlRohV7vfGtAIVae73b0= github.com/go-openapi/errors v0.21.0 h1:FhChC/duCnfoLj1gZ0BgaBmzhJC2SL/sJr8a2vAobSY= github.com/go-openapi/errors v0.21.0/go.mod h1:jxNTMUxRCKj65yb/okJGEtahVd7uvWnuWfj53bse4ho= github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= github.com/go-openapi/loads v0.21.5 h1:jDzF4dSoHw6ZFADCGltDb2lE4F6De7aWSpe+IcsRzT0= github.com/go-openapi/loads v0.21.5/go.mod h1:PxTsnFBoBe+z89riT+wYt3prmSBP6GDAQh2l9H1Flz8= github.com/go-openapi/runtime v0.27.1 h1:ae53yaOoh+fx/X5Eaq8cRmavHgDma65XPZuvBqvJYto= github.com/go-openapi/runtime v0.27.1/go.mod h1:fijeJEiEclyS8BRurYE1DE5TLb9/KZl6eAdbzjsrlLU= github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/Do= github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw= github.com/go-openapi/strfmt v0.22.0 h1:Ew9PnEYc246TwrEspvBdDHS4BVKXy/AOVsfqGDgAcaI= github.com/go-openapi/strfmt v0.22.0/go.mod h1:HzJ9kokGIju3/K6ap8jL+OlGAbjpSv27135Yr9OivU4= github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= github.com/go-openapi/validate v0.22.6 h1:+NhuwcEYpWdO5Nm4bmvhGLW0rt1Fcc532Mu3wpypXfo= github.com/go-openapi/validate v0.22.6/go.mod h1:eaddXSqKeTg5XpSmj1dYyFTK/95n/XHwcOY+BMxKMyM= github.com/go-redis/redismock/v9 v9.2.0 h1:ZrMYQeKPECZPjOj5u9eyOjg8Nnb0BS9lkVIZ6IpsKLw= github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9gfYxc94k7rWT0= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= 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 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 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.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 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.5.0/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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.17.0 h1:5p+zYs/R4VGHkhyvgWurWrpJ2hW4Vv9fQI+GzdcwXLk= github.com/google/go-containerregistry v0.17.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/google/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE= github.com/google/go-replayers/grpcreplay v1.1.0/go.mod h1:qzAvJ8/wi57zq7gWqaE6AwLM6miiXUQwP1S+I9icmhk= github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= github.com/google/go-replayers/httpreplay v1.2.0/go.mod h1:WahEFFZZ7a1P4VM1qEeHy+tME4bwyqPcwWbNlUI1Mcg= 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/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/rpmpack v0.5.0 h1:L16KZ3QvkFGpYhmp23iQip+mx1X39foEsqszjMNBm8A= github.com/google/rpmpack v0.5.0/go.mod h1:uqVAUVQLq8UY2hCDfmJ/+rtO3aw7qyhc90rCVEabEfI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/trillian v1.6.0 h1:jMBeDBIkINFvS2n6oV5maDqfRlxREAc6CW9QYWQ0qT4= github.com/google/trillian v1.6.0/go.mod h1:Yu3nIMITzNhhMJEHjAtp6xKiu+H/iHu2Oq5FjV2mCWI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-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/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.10.0 h1:/US7sIjWN6Imp4o/Rj1Ce2Nr5bki/AXi9vAW3p2tOJQ= github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8= 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/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/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.1.1 h1:RCgYJqo3jgvhl+fEWvjNW8thxGWsgxi+TPhRir1Y9y8= github.com/jellydator/ttlcache/v3 v3.1.1/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/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/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= 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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 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-20230907030200-6d76a0f91e1e h1:RLTpX495BXToqxpM90Ws4hXEo4Wfh81jr9DX1n/4WOo= github.com/letsencrypt/boulder v0.0.0-20230907030200-6d76a0f91e1e/go.mod h1:EAuqr9VFWxBi9nD5jc/EA2MT1RFty9288TF6zdtYoCU= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= 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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= 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.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.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk= github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 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.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= 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.1 h1:O5s8ewCgq5QYNpv45dK4u6IpBmDM9RIcsbf/G1uXepQ= github.com/sassoftware/relic/v7 v7.6.1/go.mod h1:NxwtWxWxlUa9as2qZi635Ye6bBT/tGnMALLq7dSfOOU= github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbmfHkLguCE9laoZCUzEEpIZXA= github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU= 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.2.1 h1:KIoM7E3C4uaK092q8YoSj/XSf9720f8dlsbYwwOmgEA= github.com/sigstore/protobuf-specs v0.2.1/go.mod h1:xPqQGnH/HllKuZ4VFPz/g+78epWM/NLRGl7Fuy45UdE= github.com/sigstore/sigstore v1.8.1 h1:mAVposMb14oplk2h/bayPmIVdzbq2IhCgy4g6R0ZSjo= github.com/sigstore/sigstore v1.8.1/go.mod h1:02SL1158BSj15bZyOFz7m+/nJzLZfFd9A8ab3Kz7w/E= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.1 h1:rEDdUefulkIQaMJyzLwtgPDLNXBIltBABiFYfb0YmgQ= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.1/go.mod h1:RCdYCc1IxCYWzh2IdzdA6Yf7JIY0cMRqH08fpQYechw= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.1 h1:DvRWG99QGWZC5mp42SEde2Xke/Q384Idnj2da7yB+Mk= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.1/go.mod h1:s13mo3a0UCQS3+PAUUZfvKe48sMDMsHk2GE1b2YfPcU= github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.1 h1:lwdRsJv1UbBemuk7w5YfXAQilQxMoFevrzamdPbG0wY= github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.1/go.mod h1:2OaSQ80EcdyVRSQ3T4d1lsc6Scopblsiq8U2AEk5K1A= github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.1 h1:9Ki0qudKpc1FQdef7xHO2bkLyTuw+qNUpWRzjBEmF4c= github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.1/go.mod h1:nhIgyu4YwwNgalIwTGsoAzam16jjAn3ADRSWKbWPwGI= github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/stretchr/objx v0.1.0/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 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= 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.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.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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/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/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/veraison/go-cose v1.2.0 h1:Ok0Hr3GMAf8K/1NB4sV65QGgCiukG1w1QD+H5tmt0Ow= github.com/veraison/go-cose v1.2.0/go.mod h1:7ziE85vSq4ScFTg6wyoMXjucIGOf4JkFEZi/an96Ct4= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.3.5/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.2 h1:f0xmpYiSrHtSNAVgwip93Cg8tuF45HJM6rHq/A5RI/4= github.com/zalando/go-keyring v0.2.2/go.mod h1:sI3evg9Wvpw3+n4SqplGSJUMwtDeROfD4nsFz4z9PG0= go.einride.tech/aip v0.65.0 h1:aqKEV1g9diXcR6DAxBVZoJn6ho8SuC+TOZFXzuu7kLU= go.einride.tech/aip v0.65.0/go.mod h1:wcRZ57XFEvERWLPy9VqDBtXc/ZFj7ugsd32F5o8Th+s= go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= go.step.sm/crypto v0.42.1 h1:OmwHm3GJO8S4VGWL3k4+I+Q4P/F2s+j8msvTyGnh1Vg= go.step.sm/crypto v0.42.1/go.mod h1:yNcTLFQBnYCA75fC5bklBoTAT7y0dRZsB1TkinB8JMs= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= gocloud.dev v0.36.0 h1:q5zoXux4xkOZP473e1EZbG8Gq9f0vlg1VNH5Du/ybus= gocloud.dev v0.36.0/go.mod h1:bLxah6JQVKBaIxzsr5BQLYB4IYdWHkMZdzCXlo6F0gg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/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-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-20210510120138-977fb7262007/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-20210819135213-f52c844e1c1c/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/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-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 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-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.159.0 h1:fVTj+7HHiUYz4JEZCHHoRIeQX7h5FMzrA2RF/DzDdbs= google.golang.org/api v0.159.0/go.mod h1:0mu0TpK33qnydLvWqbImq2b1eQ5FHRSDCBzAxX9ZHyw= 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.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg= google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= google.golang.org/genproto/googleapis/api v0.0.0-20240122161410-6c6643bf1457 h1:KHBtwE+eQc3+NxpjmRFlQ3pJQ2FNnhhgB9xOV8kyBuU= google.golang.org/genproto/googleapis/api v0.0.0-20240122161410-6c6643bf1457/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= 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.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.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/go-jose/go-jose.v2 v2.6.1 h1:qEzJlIDmG9q5VO0M/o8tGS65QMHMS1w01TQJB1VPJ4U= gopkg.in/go-jose/go-jose.v2 v2.6.1/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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.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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.28.1 h1:i+0O8k2NPBCPYaMB+uCkseEbawEt/eFaiRqUx8aB108= k8s.io/api v0.28.1/go.mod h1:uBYwID+66wiL28Kn2tBjBYQdEU0Xk0z5qF8bIBqk/Dg= k8s.io/apimachinery v0.28.1 h1:EJD40og3GizBSV3mkIoXQBsws32okPOy+MkRyzh6nPY= k8s.io/apimachinery v0.28.1/go.mod h1:X0xh/chESs2hP9koe+SdIAcXWcQ+RM5hy0ZynB+yEvw= k8s.io/klog/v2 v2.120.0 h1:z+q5mfovBj1fKFxiRzsa2DsJLPIVMk/KFL81LMOfK+8= k8s.io/klog/v2 v2.120.0/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/release-utils v0.7.7 h1:JKDOvhCk6zW8ipEOkpTGDH/mW3TI+XqtPp16aaQ79FU= sigs.k8s.io/release-utils v0.7.7/go.mod h1:iU7DGVNi3umZJ8q6aHyUFzsDUIaYwNnNKGHo3YE5E3s= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ= rekor-1.3.5/hack/000077500000000000000000000000001455727245600135715ustar00rootroot00000000000000rekor-1.3.5/hack/github-oidc-setup.sh000077500000000000000000000066311455727245600174720ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2022 The Sigstore Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Idempotent script. # # Commands based off of Google blog post # https://cloud.google.com/blog/products/identity-security/enabling-keyless-authentication-from-github-actions # # One addition is the attribute.repository=assertion.repository mapping. # This allows it to be pinned to given repo. set -o errexit set -o nounset set -o pipefail set -o verbose set -o xtrace PROJECT_ID="projectsigstore" PROJECT_NUMBER="498091336538" POOL_NAME="githubactions" PROVIDER_NAME="sigstore-rekor" LOCATION="global" REPO="sigstore/rekor" SERVICE_ACCOUNT_ID="github-actions-rekor" SERVICE_ACCOUNT="${SERVICE_ACCOUNT_ID}@${PROJECT_ID}.iam.gserviceaccount.com" # Create workload identity pool if not present. if ! (gcloud iam workload-identity-pools describe "${POOL_NAME}" --location=${LOCATION}); then gcloud iam workload-identity-pools create "${POOL_NAME}" \ --project="${PROJECT_ID}" \ --location="${LOCATION}" \ --display-name="Github Actions Pool" fi # Create workload identity provider if not present. if ! (gcloud iam workload-identity-pools providers describe "${PROVIDER_NAME}" --location="${LOCATION}" --workload-identity-pool="${POOL_NAME}"); then gcloud iam workload-identity-pools providers create-oidc "${PROVIDER_NAME}" \ --project="${PROJECT_ID}" \ --location="${LOCATION}" \ --workload-identity-pool="${POOL_NAME}" \ --display-name="Github Actions Provider Rekor" \ --attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.aud=assertion.aud,attribute.repository=assertion.repository" \ --issuer-uri="https://token.actions.githubusercontent.com" fi # Create service account if not present. if ! (gcloud iam service-accounts describe "${SERVICE_ACCOUNT}"); then gcloud iam service-accounts create ${SERVICE_ACCOUNT_ID} \ --description="Service account for Github Actions Rekor" \ --display-name="Github Actions Rekor" fi # Adding binding is idempotent. gcloud iam service-accounts add-iam-policy-binding "${SERVICE_ACCOUNT}" \ --project="${PROJECT_ID}" \ --role="roles/iam.workloadIdentityUser" \ --member="principalSet://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/${LOCATION}/workloadIdentityPools/${POOL_NAME}/attribute.repository/${REPO}" # Adding binding is idempotent. # Used for kicking off cloud build. gcloud projects add-iam-policy-binding "${PROJECT_ID}" \ --project="${PROJECT_ID}" \ --role="roles/cloudbuild.builds.editor" \ --member="serviceAccount:${SERVICE_ACCOUNT}" # Adding binding is idempotent. # Permission needed to run `gcloud builds` # https://cloud.google.com/build/docs/securing-builds/configure-access-to-resources#granting_permissions_to_run_gcloud_commands gcloud projects add-iam-policy-binding "${PROJECT_ID}" \ --project="${PROJECT_ID}" \ --role="roles/serviceusage.serviceUsageConsumer" \ --member="serviceAccount:${SERVICE_ACCOUNT}" rekor-1.3.5/hack/tools/000077500000000000000000000000001455727245600147315ustar00rootroot00000000000000rekor-1.3.5/hack/tools/go.mod000066400000000000000000000057461455727245600160530ustar00rootroot00000000000000module github.com/sigstore/rekor/hack/tools go 1.20 require ( github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230329111138-12e09aba5ebd github.com/go-swagger/go-swagger v0.30.5 github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad ) require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-openapi/analysis v0.21.4 // indirect github.com/go-openapi/errors v0.20.4 // indirect github.com/go-openapi/inflect v0.19.0 // indirect github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/loads v0.21.2 // indirect github.com/go-openapi/runtime v0.26.0 // indirect github.com/go-openapi/spec v0.20.9 // indirect github.com/go-openapi/strfmt v0.21.7 // indirect github.com/go-openapi/swag v0.22.4 // indirect github.com/go-openapi/validate v0.22.1 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.5.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/jessevdk/go-flags v1.5.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.16.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/toqueteos/webbrowser v1.2.0 // indirect go.mongodb.org/mongo-driver v1.12.0 // indirect golang.org/x/crypto v0.18.0 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.16.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) rekor-1.3.5/hack/tools/go.sum000066400000000000000000002075251455727245600160770ustar00rootroot00000000000000cloud.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/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/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/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/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= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230329111138-12e09aba5ebd h1:1tbEqR4NyQLgiod7vLXSswHteGetAVZrMGCqrJxLKRs= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230329111138-12e09aba5ebd/go.mod h1:0vOOKsOMKPThRu9lQMAxcQ8D60f8U+wHXl07SyUw0+U= 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/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= 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/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 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-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.20.4 h1:unTcVm6PispJsMECE3zWgvG4xTiKda1LIR5rCRWLG6M= github.com/go-openapi/errors v0.20.4/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= github.com/go-openapi/runtime v0.26.0 h1:HYOFtG00FM1UvqrcxbEJg/SwvDRvYLQKGhw2zaQjTcc= github.com/go-openapi/runtime v0.26.0/go.mod h1:QgRGeZwrUcSHdeh4Ka9Glvo0ug1LC5WyE+EV88plZrQ= github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-swagger/go-swagger v0.30.5 h1:SQ2+xSonWjjoEMOV5tcOnZJVlfyUfCBhGQGArS1b9+U= github.com/go-swagger/go-swagger v0.30.5/go.mod h1:cWUhSyCNqV7J1wkkxfr5QmbcnCewetCdvEXqgPvbc/Q= github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 h1:l9rI6sNaZgNC0LnF3MiE+qTmyBA/tZAg1rtyrGbUMK0= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 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/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/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/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 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/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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/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/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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= 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/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 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/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 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/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/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.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/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= 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.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ= github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad h1:W0LEBv82YCGEtcmPA3uNZBI33/qF//HAAs3MawDjRa0= github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad/go.mod h1:Hy8o65+MXnS6EwGElrSRjUzQDLXreJlzYLlWiHtt8hM= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= 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.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE= go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= 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= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/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-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/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/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/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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-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-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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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/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-20190412183630-56d357773e84/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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/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-20190531175056-4c3a928424d2/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-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-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/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-20210615035016-665e8c7367d1/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/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/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-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-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/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-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 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-20191012152004-8de300cfc20a/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-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-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-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= 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= 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/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-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-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-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 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.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/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= 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-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/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-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/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= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rekor-1.3.5/hack/tools/tools.go000066400000000000000000000015751455727245600164300ustar00rootroot00000000000000//go:build tools // +build tools // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // This package imports things required by build scripts, to force `go mod` to see them as dependencies package tools import ( _ "github.com/AdamKorcz/go-fuzz-headers-1" _ "github.com/go-swagger/go-swagger/cmd/swagger" _ "github.com/wadey/gocovmerge" ) rekor-1.3.5/oid-info.md000066400000000000000000000016341455727245600147150ustar00rootroot00000000000000# Rekor OID Information ## Description This document defines Rekor [OID values](https://github.com/sigstore/sigstore/blob/main/docs/oid-info.md). Rekor reserves the `1.3.6.1.4.1.57264.3` OID root for all of its values. ## Directory | OID | Name | Tag Type | Description | | --------------------- | -------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 1.3.6.1.4.1.57264.3.1 | TransparencyLogEntry | `UTF8STRING` | Proto serialized [TransparencyLogEntry](https://github.com/sigstore/protobuf-specs/blob/4dbf10bc287d76f1bfa68c05a78f3f5add5f56fe/protos/sigstore_rekor.proto#L89). | rekor-1.3.5/openapi.yaml000066400000000000000000000502751455727245600152130ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. swagger: "2.0" info: title: Rekor description: Rekor is a cryptographically secure, immutable transparency log for signed software releases. version: 1.0.0 host: rekor.sigstore.dev schemes: - http consumes: - application/json produces: - application/json paths: /api/v1/index/retrieve: post: summary: Searches index by entry metadata description: > EXPERIMENTAL - this endpoint is offered as best effort only and may be changed or removed in future releases. The results returned from this endpoint may be incomplete. deprecated: true operationId: searchIndex tags: - index parameters: - in: body name: query required: true schema: $ref: '#/definitions/SearchIndex' responses: 200: description: Returns zero or more entry UUIDs from the transparency log based on search query schema: type: array items: type: string description: Entry UUID in transparency log pattern: '^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$' 400: $ref: '#/responses/BadContent' default: $ref: '#/responses/InternalServerError' /api/v1/log: get: summary: Get information about the current state of the transparency log description: Returns the current root hash and size of the merkle tree used to store the log entries. operationId: getLogInfo tags: - tlog parameters: - in: query name: stable type: boolean default: false description: Whether to return a stable checkpoint for the active shard responses: 200: description: A JSON object with the root hash and tree size as properties schema: $ref: '#/definitions/LogInfo' default: $ref: '#/responses/InternalServerError' /api/v1/log/publicKey: get: summary: Retrieve the public key that can be used to validate the signed tree head description: Returns the public key that can be used to validate the signed tree head operationId: getPublicKey tags: - pubkey parameters: - in: query name: treeID type: string pattern: '^[0-9]+$' description: The tree ID of the tree you wish to get a public key for produces: - application/x-pem-file responses: 200: description: The public key schema: type: string default: $ref: '#/responses/InternalServerError' /api/v1/log/proof: get: summary: Get information required to generate a consistency proof for the transparency log description: Returns a list of hashes for specified tree sizes that can be used to confirm the consistency of the transparency log operationId: getLogProof tags: - tlog parameters: - in: query name: firstSize type: integer default: 1 minimum: 1 description: > The size of the tree that you wish to prove consistency from (1 means the beginning of the log) Defaults to 1 if not specified - in: query name: lastSize type: integer required: true minimum: 1 description: The size of the tree that you wish to prove consistency to - in: query name: treeID type: string pattern: '^[0-9]+$' description: The tree ID of the tree that you wish to prove consistency for responses: 200: description: All hashes required to compute the consistency proof schema: $ref: '#/definitions/ConsistencyProof' 400: $ref: '#/responses/BadContent' default: $ref: '#/responses/InternalServerError' /api/v1/log/entries: post: summary: Creates an entry in the transparency log description: > Creates an entry in the transparency log for a detached signature, public key, and content. Items can be included in the request or fetched by the server when URLs are specified. operationId: createLogEntry tags: - entries parameters: - in: body name: proposedEntry schema: $ref: '#/definitions/ProposedEntry' required: true responses: 201: description: Returns the entry created in the transparency log headers: ETag: type: string description: UUID of log entry Location: type: string description: URI location of log entry format: uri schema: $ref: '#/definitions/LogEntry' 400: $ref: '#/responses/BadContent' 409: $ref: '#/responses/Conflict' default: $ref: '#/responses/InternalServerError' get: summary: Retrieves an entry and inclusion proof from the transparency log (if it exists) by index operationId: getLogEntryByIndex tags: - entries parameters: - in: query name: logIndex type: integer required: true minimum: 0 description: specifies the index of the entry in the transparency log to be retrieved responses: 200: description: the entry in the transparency log requested along with an inclusion proof schema: $ref: '#/definitions/LogEntry' 404: $ref: '#/responses/NotFound' default: $ref: '#/responses/InternalServerError' /api/v1/log/entries/{entryUUID}: get: summary: Get log entry and information required to generate an inclusion proof for the entry in the transparency log description: Returns the entry, root hash, tree size, and a list of hashes that can be used to calculate proof of an entry being included in the transparency log operationId: getLogEntryByUUID tags: - entries parameters: - in: path name: entryUUID type: string required: true pattern: '^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$' description: the UUID of the entry for which the inclusion proof information should be returned responses: 200: description: Information needed for a client to compute the inclusion proof schema: $ref: '#/definitions/LogEntry' 404: $ref: '#/responses/NotFound' default: $ref: '#/responses/InternalServerError' /api/v1/log/entries/retrieve: post: summary: Searches transparency log for one or more log entries operationId: searchLogQuery tags: - entries parameters: - in: body name: entry required: true schema: $ref: '#/definitions/SearchLogQuery' responses: 200: description: Returns zero or more entries from the transparency log, according to how many were included in request query schema: type: array items: $ref: '#/definitions/LogEntry' 400: $ref: '#/responses/BadContent' 422: $ref: '#/responses/UnprocessableEntity' default: $ref: '#/responses/InternalServerError' definitions: ProposedEntry: type: object discriminator: kind properties: kind: type: string required: - kind rekord: type: object description: Rekord object allOf: - $ref: '#/definitions/ProposedEntry' - properties: apiVersion: type: string pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ spec: type: object $ref: 'pkg/types/rekord/rekord_schema.json' required: - apiVersion - spec additionalProperties: false hashedrekord: type: object description: Hashed Rekord object allOf: - $ref: '#/definitions/ProposedEntry' - properties: apiVersion: type: string pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ spec: type: object $ref: 'pkg/types/hashedrekord/hashedrekord_schema.json' required: - apiVersion - spec additionalProperties: false rpm: type: object description: RPM package allOf: - $ref: '#/definitions/ProposedEntry' - properties: apiVersion: type: string pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ spec: type: object $ref: 'pkg/types/rpm/rpm_schema.json' required: - apiVersion - spec additionalProperties: false tuf: type: object description: TUF metadata allOf: - $ref: '#/definitions/ProposedEntry' - properties: apiVersion: type: string pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ spec: type: object $ref: 'pkg/types/tuf/tuf_schema.json' required: - apiVersion - spec additionalProperties: false alpine: type: object description: Alpine package allOf: - $ref: '#/definitions/ProposedEntry' - properties: apiVersion: type: string pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ spec: type: object $ref: 'pkg/types/alpine/alpine_schema.json' required: - apiVersion - spec additionalProperties: false helm: type: object description: Helm chart allOf: - $ref: '#/definitions/ProposedEntry' - properties: apiVersion: type: string pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ spec: type: object $ref: 'pkg/types/helm/helm_schema.json' required: - apiVersion - spec intoto: type: object description: Intoto object allOf: - $ref: '#/definitions/ProposedEntry' - properties: apiVersion: type: string pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ spec: type: object $ref: 'pkg/types/intoto/intoto_schema.json' required: - apiVersion - spec additionalProperties: false cose: type: object description: COSE object allOf: - $ref: '#/definitions/ProposedEntry' - properties: apiVersion: type: string pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ spec: type: object $ref: 'pkg/types/cose/cose_schema.json' required: - apiVersion - spec additionalProperties: false jar: type: object description: Java Archive (JAR) allOf: - $ref: '#/definitions/ProposedEntry' - properties: apiVersion: type: string pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ spec: type: object $ref: 'pkg/types/jar/jar_schema.json' required: - apiVersion - spec additionalProperties: false rfc3161: type: object description: RFC3161 Timestamp allOf: - $ref: '#/definitions/ProposedEntry' - properties: apiVersion: type: string pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ spec: type: object $ref: 'pkg/types/rfc3161/rfc3161_schema.json' required: - apiVersion - spec additionalProperties: false dsse: type: object description: DSSE envelope allOf: - $ref: '#/definitions/ProposedEntry' - properties: apiVersion: type: string pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ spec: type: object $ref: 'pkg/types/dsse/dsse_schema.json' required: - apiVersion - spec additionalProperties: false LogEntry: type: object additionalProperties: type: object properties: logID: type: string pattern: '^[0-9a-fA-F]{64}$' description: This is the SHA256 hash of the DER-encoded public key for the log at the time the entry was included in the log logIndex: type: integer minimum: 0 body: type: object additionalProperties: true integratedTime: type: integer description: The time the entry was added to the log as a Unix timestamp in seconds attestation: type: object properties: data: format: byte format: byte verification: type: object properties: inclusionProof: $ref: '#/definitions/InclusionProof' signedEntryTimestamp: type: string format: byte # To verify the signedEntryTimestamp: # 1. Remove the Verification object from the JSON Document # 2. Canonicalize the remaining JSON document by following RFC 8785 rules # 3. Verify the canonicalized payload and signedEntryTimestamp against rekor's public key description: Signature over the logID, logIndex, body and integratedTime. required: - "logID" - "logIndex" - "body" - "integratedTime" SearchIndex: type: object properties: email: type: string format: email publicKey: type: object properties: format: type: string enum: ['pgp','x509','minisign', 'ssh', 'tuf'] content: type: string format: byte url: type: string format: uri required: - "format" hash: type: string pattern: '^(sha512:)?[0-9a-fA-F]{128}$|^(sha256:)?[0-9a-fA-F]{64}$|^(sha1:)?[0-9a-fA-F]{40}$' operator: type: string enum: ['and','or'] SearchLogQuery: type: object properties: entryUUIDs: type: array minItems: 1 maxItems: 10 items: type: string pattern: '^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$' logIndexes: type: array minItems: 1 maxItems: 10 items: type: integer minimum: 0 entries: type: array minItems: 1 maxItems: 10 items: $ref: '#/definitions/ProposedEntry' LogInfo: type: object properties: rootHash: type: string description: The current hash value stored at the root of the merkle tree pattern: '^[0-9a-fA-F]{64}$' treeSize: type: integer description: The current number of nodes in the merkle tree minimum: 1 signedTreeHead: type: string format: signedCheckpoint description: The current signed tree head treeID: type: string description: The current treeID pattern: '^[0-9]+$' inactiveShards: type: array items: $ref: '#/definitions/InactiveShardLogInfo' required: - rootHash - treeSize - signedTreeHead - treeID InactiveShardLogInfo: type: object properties: rootHash: type: string description: The current hash value stored at the root of the merkle tree pattern: '^[0-9a-fA-F]{64}$' treeSize: type: integer description: The current number of nodes in the merkle tree minimum: 1 signedTreeHead: type: string format: signedCheckpoint description: The current signed tree head treeID: type: string description: The current treeID pattern: '^[0-9]+$' required: - rootHash - treeSize - signedTreeHead - treeID ConsistencyProof: type: object properties: rootHash: type: string description: The hash value stored at the root of the merkle tree at the time the proof was generated pattern: '^[0-9a-fA-F]{64}$' hashes: type: array items: type: string description: SHA256 hash value expressed in hexadecimal format pattern: '^[0-9a-fA-F]{64}$' required: - rootHash - hashes InclusionProof: type: object properties: logIndex: type: integer description: The index of the entry in the transparency log minimum: 0 rootHash: description: The hash value stored at the root of the merkle tree at the time the proof was generated type: string pattern: '^[0-9a-fA-F]{64}$' treeSize: type: integer description: The size of the merkle tree at the time the inclusion proof was generated minimum: 1 hashes: description: A list of hashes required to compute the inclusion proof, sorted in order from leaf to root type: array items: type: string description: SHA256 hash value expressed in hexadecimal format pattern: '^[0-9a-fA-F]{64}$' checkpoint: type: string format: signedCheckpoint description: The checkpoint (signed tree head) that the inclusion proof is based on required: - logIndex - rootHash - treeSize - hashes - checkpoint Error: type: object properties: code: type: integer message: type: string responses: BadContent: description: The content supplied to the server was invalid schema: $ref: "#/definitions/Error" Conflict: description: The request conflicts with the current state of the transparency log schema: $ref: "#/definitions/Error" headers: Location: type: string format: uri NotFound: description: The content requested could not be found InternalServerError: description: There was an internal error in the server while processing the request schema: $ref: "#/definitions/Error" UnprocessableEntity: description: The server understood the request but is unable to process the contained instructions schema: $ref: "#/definitions/Error" rekor-1.3.5/pkg/000077500000000000000000000000001455727245600134445ustar00rootroot00000000000000rekor-1.3.5/pkg/api/000077500000000000000000000000001455727245600142155ustar00rootroot00000000000000rekor-1.3.5/pkg/api/api.go000066400000000000000000000153401455727245600153200ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package api import ( "context" "crypto/sha256" "crypto/x509" "encoding/hex" "fmt" "time" "github.com/google/trillian" "github.com/redis/go-redis/v9" "github.com/spf13/viper" "golang.org/x/exp/slices" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "github.com/sigstore/rekor/pkg/indexstorage" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pubsub" "github.com/sigstore/rekor/pkg/sharding" "github.com/sigstore/rekor/pkg/signer" "github.com/sigstore/rekor/pkg/storage" "github.com/sigstore/rekor/pkg/trillianclient" "github.com/sigstore/rekor/pkg/witness" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" _ "github.com/sigstore/rekor/pkg/pubsub/gcp" // Load GCP pubsub implementation ) func dial(ctx context.Context, rpcServer string) (*grpc.ClientConn, error) { ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() // Set up and test connection to rpc server creds := insecure.NewCredentials() conn, err := grpc.DialContext(ctx, rpcServer, grpc.WithTransportCredentials(creds)) if err != nil { log.Logger.Fatalf("Failed to connect to RPC server:", err) } return conn, nil } type API struct { logClient trillian.TrillianLogClient logID int64 logRanges sharding.LogRanges pubkey string // PEM encoded public key pubkeyHash string // SHA256 hash of DER-encoded public key signer signature.Signer // stops checkpoint publishing checkpointPublishCancel context.CancelFunc // Publishes notifications when new entries are added to the log. May be // nil if no publisher is configured. newEntryPublisher pubsub.Publisher } func NewAPI(treeID uint) (*API, error) { logRPCServer := fmt.Sprintf("%s:%d", viper.GetString("trillian_log_server.address"), viper.GetUint("trillian_log_server.port")) ctx := context.Background() tConn, err := dial(ctx, logRPCServer) if err != nil { return nil, fmt.Errorf("dial: %w", err) } logAdminClient := trillian.NewTrillianAdminClient(tConn) logClient := trillian.NewTrillianLogClient(tConn) shardingConfig := viper.GetString("trillian_log_server.sharding_config") ranges, err := sharding.NewLogRanges(ctx, logClient, shardingConfig, treeID) if err != nil { return nil, fmt.Errorf("unable get sharding details from sharding config: %w", err) } tid := int64(treeID) if tid == 0 { log.Logger.Info("No tree ID specified, attempting to create a new tree") t, err := trillianclient.CreateAndInitTree(ctx, logAdminClient, logClient) if err != nil { return nil, fmt.Errorf("create and init tree: %w", err) } tid = t.TreeId } log.Logger.Infof("Starting Rekor server with active tree %v", tid) ranges.SetActive(tid) rekorSigner, err := signer.New(ctx, viper.GetString("rekor_server.signer"), viper.GetString("rekor_server.signer-passwd")) if err != nil { return nil, fmt.Errorf("getting new signer: %w", err) } pk, err := rekorSigner.PublicKey(options.WithContext(ctx)) if err != nil { return nil, fmt.Errorf("getting public key: %w", err) } b, err := x509.MarshalPKIXPublicKey(pk) if err != nil { return nil, fmt.Errorf("marshalling public key: %w", err) } pubkeyHashBytes := sha256.Sum256(b) pubkey := cryptoutils.PEMEncode(cryptoutils.PublicKeyPEMType, b) var newEntryPublisher pubsub.Publisher if p := viper.GetString("rekor_server.new_entry_publisher"); p != "" { if !viper.GetBool("rekor_server.publish_events_protobuf") && !viper.GetBool("rekor_server.publish_events_json") { return nil, fmt.Errorf("%q is configured but neither %q or %q are enabled", "new_entry_publisher", "publish_events_protobuf", "publish_events_json") } newEntryPublisher, err = pubsub.Get(ctx, p) if err != nil { return nil, fmt.Errorf("init event publisher: %w", err) } log.ContextLogger(ctx).Infof("Initialized new entry event publisher: %s", p) } return &API{ // Transparency Log Stuff logClient: logClient, logID: tid, logRanges: ranges, // Signing/verifying fields pubkey: string(pubkey), pubkeyHash: hex.EncodeToString(pubkeyHashBytes[:]), signer: rekorSigner, // Utility functionality not required for operation of the core service newEntryPublisher: newEntryPublisher, }, nil } var ( api *API attestationStorageClient storage.AttestationStorage indexStorageClient indexstorage.IndexStorage redisClient *redis.Client ) func ConfigureAPI(treeID uint) { var err error api, err = NewAPI(treeID) if err != nil { log.Logger.Panic(err) } if viper.GetBool("enable_retrieve_api") || viper.GetBool("enable_stable_checkpoint") || slices.Contains(viper.GetStringSlice("enabled_api_endpoints"), "searchIndex") { indexStorageClient, err = indexstorage.NewIndexStorage(viper.GetString("search_index.storage_provider")) if err != nil { log.Logger.Panic(err) } } if viper.GetBool("enable_attestation_storage") { attestationStorageClient, err = storage.NewAttestationStorage() if err != nil { log.Logger.Panic(err) } } if viper.GetBool("enable_stable_checkpoint") { redisClient = redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%v:%v", viper.GetString("redis_server.address"), viper.GetUint64("redis_server.port")), Password: viper.GetString("redis_server.password"), Network: "tcp", DB: 0, // default DB }) checkpointPublisher := witness.NewCheckpointPublisher(context.Background(), api.logClient, api.logRanges.ActiveTreeID(), viper.GetString("rekor_server.hostname"), api.signer, redisClient, viper.GetUint("publish_frequency"), CheckpointPublishCount) // create context to cancel goroutine on server shutdown ctx, cancel := context.WithCancel(context.Background()) api.checkpointPublishCancel = cancel checkpointPublisher.StartPublisher(ctx) } } func StopAPI() { api.checkpointPublishCancel() if api.newEntryPublisher != nil { if err := api.newEntryPublisher.Close(); err != nil { log.Logger.Errorf("shutting down newEntryPublisher: %v", err) } } if indexStorageClient != nil { if err := indexStorageClient.Shutdown(); err != nil { log.Logger.Errorf("shutting down indexStorageClient: %v", err) } } } rekor-1.3.5/pkg/api/entries.go000066400000000000000000000603551455727245600162260ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package api import ( "bytes" "context" "encoding/hex" "errors" "fmt" "net/http" "net/url" "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/google/trillian" ttypes "github.com/google/trillian/types" "github.com/spf13/viper" "github.com/transparency-dev/merkle/rfc6962" "google.golang.org/genproto/googleapis/rpc/code" "google.golang.org/grpc/codes" "github.com/sigstore/rekor/pkg/events" "github.com/sigstore/rekor/pkg/events/newentry" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/generated/restapi/operations/entries" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pubsub" "github.com/sigstore/rekor/pkg/sharding" "github.com/sigstore/rekor/pkg/tle" "github.com/sigstore/rekor/pkg/trillianclient" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/util" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" ) const ( maxSearchQueries = 10 ) func signEntry(ctx context.Context, signer signature.Signer, entry models.LogEntryAnon) ([]byte, error) { payload, err := entry.MarshalBinary() if err != nil { return nil, fmt.Errorf("marshalling error: %v", err) } canonicalized, err := jsoncanonicalizer.Transform(payload) if err != nil { return nil, fmt.Errorf("canonicalizing error: %v", err) } signature, err := signer.SignMessage(bytes.NewReader(canonicalized), options.WithContext(ctx)) if err != nil { return nil, fmt.Errorf("signing error: %v", err) } return signature, nil } // logEntryFromLeaf creates a signed LogEntry struct from trillian structs func logEntryFromLeaf(ctx context.Context, signer signature.Signer, _ trillianclient.TrillianClient, leaf *trillian.LogLeaf, signedLogRoot *trillian.SignedLogRoot, proof *trillian.Proof, tid int64, ranges sharding.LogRanges) (models.LogEntry, error) { log.ContextLogger(ctx).Debugf("log entry from leaf %d", leaf.GetLeafIndex()) root := &ttypes.LogRootV1{} if err := root.UnmarshalBinary(signedLogRoot.LogRoot); err != nil { return nil, err } hashes := []string{} for _, hash := range proof.Hashes { hashes = append(hashes, hex.EncodeToString(hash)) } virtualIndex := sharding.VirtualLogIndex(leaf.GetLeafIndex(), tid, ranges) logEntryAnon := models.LogEntryAnon{ LogID: swag.String(api.pubkeyHash), LogIndex: &virtualIndex, Body: leaf.LeafValue, IntegratedTime: swag.Int64(leaf.IntegrateTimestamp.AsTime().Unix()), } signature, err := signEntry(ctx, signer, logEntryAnon) if err != nil { return nil, fmt.Errorf("signing entry error: %w", err) } scBytes, err := util.CreateAndSignCheckpoint(ctx, viper.GetString("rekor_server.hostname"), tid, root.TreeSize, root.RootHash, api.signer) if err != nil { return nil, err } inclusionProof := models.InclusionProof{ TreeSize: swag.Int64(int64(root.TreeSize)), RootHash: swag.String(hex.EncodeToString(root.RootHash)), LogIndex: swag.Int64(proof.GetLeafIndex()), Hashes: hashes, Checkpoint: stringPointer(string(scBytes)), } uuid := hex.EncodeToString(leaf.MerkleLeafHash) treeID := fmt.Sprintf("%x", tid) entryIDstruct, err := sharding.CreateEntryIDFromParts(treeID, uuid) if err != nil { return nil, fmt.Errorf("error creating EntryID from active treeID %v and uuid %v: %w", treeID, uuid, err) } entryID := entryIDstruct.ReturnEntryIDString() if viper.GetBool("enable_attestation_storage") { pe, err := models.UnmarshalProposedEntry(bytes.NewReader(leaf.LeafValue), runtime.JSONConsumer()) if err != nil { return nil, err } eimpl, err := types.UnmarshalEntry(pe) if err != nil { return nil, err } if entryWithAtt, ok := eimpl.(types.EntryWithAttestationImpl); ok { var att []byte var fetchErr error attKey := entryWithAtt.AttestationKey() // if we're given a key by the type logic, let's try that first if attKey != "" { att, fetchErr = attestationStorageClient.FetchAttestation(ctx, attKey) if fetchErr != nil { log.ContextLogger(ctx).Debugf("error fetching attestation by key, trying by UUID: %s %v", attKey, fetchErr) } } // if looking up by key failed or we weren't able to generate a key, try looking up by uuid if attKey == "" || fetchErr != nil { att, fetchErr = attestationStorageClient.FetchAttestation(ctx, entryIDstruct.UUID) if fetchErr != nil { log.ContextLogger(ctx).Debugf("error fetching attestation by uuid: %s %v", entryIDstruct.UUID, fetchErr) } } if fetchErr == nil { logEntryAnon.Attestation = &models.LogEntryAnonAttestation{ Data: att, } } } } logEntryAnon.Verification = &models.LogEntryAnonVerification{ InclusionProof: &inclusionProof, SignedEntryTimestamp: strfmt.Base64(signature), } return models.LogEntry{ entryID: logEntryAnon}, nil } // GetLogEntryAndProofByIndexHandler returns the entry and inclusion proof for a specified log index func GetLogEntryByIndexHandler(params entries.GetLogEntryByIndexParams) middleware.Responder { ctx := params.HTTPRequest.Context() logEntry, err := retrieveLogEntryByIndex(ctx, int(params.LogIndex)) if err != nil { if errors.Is(err, ErrNotFound) { return handleRekorAPIError(params, http.StatusNotFound, fmt.Errorf("grpc error: %w", err), "") } return handleRekorAPIError(params, http.StatusInternalServerError, err, err.Error()) } return entries.NewGetLogEntryByIndexOK().WithPayload(logEntry) } func createLogEntry(params entries.CreateLogEntryParams) (models.LogEntry, middleware.Responder) { ctx := params.HTTPRequest.Context() entry, err := types.CreateVersionedEntry(params.ProposedEntry) if err != nil { return nil, handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf(validationError, err)) } leaf, err := types.CanonicalizeEntry(ctx, entry) if err != nil { if _, ok := (err).(types.ValidationError); ok { return nil, handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf(validationError, err)) } return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, failedToGenerateCanonicalEntry) } tc := trillianclient.NewTrillianClient(ctx, api.logClient, api.logID) resp := tc.AddLeaf(leaf) // this represents overall GRPC response state (not the results of insertion into the log) if resp.Status != codes.OK { return nil, handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("grpc error: %w", resp.Err), trillianUnexpectedResult) } // this represents the results of inserting the proposed leaf into the log; status is nil in success path insertionStatus := resp.GetAddResult.QueuedLeaf.Status if insertionStatus != nil { switch insertionStatus.Code { case int32(code.Code_OK): case int32(code.Code_ALREADY_EXISTS), int32(code.Code_FAILED_PRECONDITION): existingUUID := hex.EncodeToString(rfc6962.DefaultHasher.HashLeaf(leaf)) activeTree := fmt.Sprintf("%x", api.logID) entryIDstruct, err := sharding.CreateEntryIDFromParts(activeTree, existingUUID) if err != nil { err := fmt.Errorf("error creating EntryID from active treeID %v and uuid %v: %w", activeTree, existingUUID, err) return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, fmt.Sprintf(validationError, err)) } existingEntryID := entryIDstruct.ReturnEntryIDString() err = fmt.Errorf("grpc error: %v", insertionStatus.String()) return nil, handleRekorAPIError(params, http.StatusConflict, err, fmt.Sprintf(entryAlreadyExists, existingEntryID), "entryURL", getEntryURL(*params.HTTPRequest.URL, existingEntryID)) default: err := fmt.Errorf("grpc error: %v", insertionStatus.String()) return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, trillianUnexpectedResult) } } // We made it this far, that means the entry was successfully added. metricNewEntries.Inc() queuedLeaf := resp.GetAddResult.QueuedLeaf.Leaf uuid := hex.EncodeToString(queuedLeaf.GetMerkleLeafHash()) activeTree := fmt.Sprintf("%x", api.logID) entryIDstruct, err := sharding.CreateEntryIDFromParts(activeTree, uuid) if err != nil { err := fmt.Errorf("error creating EntryID from active treeID %v and uuid %v: %w", activeTree, uuid, err) return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, fmt.Sprintf(validationError, err)) } entryID := entryIDstruct.ReturnEntryIDString() // The log index should be the virtual log index across all shards virtualIndex := sharding.VirtualLogIndex(queuedLeaf.LeafIndex, api.logRanges.ActiveTreeID(), api.logRanges) logEntryAnon := models.LogEntryAnon{ LogID: swag.String(api.pubkeyHash), LogIndex: swag.Int64(virtualIndex), Body: queuedLeaf.GetLeafValue(), IntegratedTime: swag.Int64(queuedLeaf.IntegrateTimestamp.AsTime().Unix()), } if indexStorageClient != nil { go func() { keys, err := entry.IndexKeys() if err != nil { log.ContextLogger(ctx).Errorf("getting entry index keys: %v", err) return } if err := addToIndex(context.Background(), keys, entryID); err != nil { log.ContextLogger(ctx).Errorf("adding keys to index: %v", err) } }() } if viper.GetBool("enable_attestation_storage") { if entryWithAtt, ok := entry.(types.EntryWithAttestationImpl); ok { attKey, attVal := entryWithAtt.AttestationKeyValue() if attVal != nil { go func() { if err := storeAttestation(context.Background(), attKey, attVal); err != nil { // entryIDstruct.UUID log.ContextLogger(ctx).Debugf("error storing attestation: %s", err) } else { log.ContextLogger(ctx).Debugf("stored attestation for uuid %s with filename %s", entryIDstruct.UUID, attKey) } }() } else { log.ContextLogger(ctx).Infof("no attestation returned for %s", uuid) } } } signature, err := signEntry(ctx, api.signer, logEntryAnon) if err != nil { return nil, handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("signing entry error: %v", err), signingError) } root := &ttypes.LogRootV1{} if err := root.UnmarshalBinary(resp.GetLeafAndProofResult.SignedLogRoot.LogRoot); err != nil { return nil, handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("error unmarshalling log root: %v", err), sthGenerateError) } hashes := []string{} for _, hash := range resp.GetLeafAndProofResult.Proof.Hashes { hashes = append(hashes, hex.EncodeToString(hash)) } scBytes, err := util.CreateAndSignCheckpoint(ctx, viper.GetString("rekor_server.hostname"), api.logID, root.TreeSize, root.RootHash, api.signer) if err != nil { return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, sthGenerateError) } inclusionProof := models.InclusionProof{ TreeSize: swag.Int64(int64(root.TreeSize)), RootHash: swag.String(hex.EncodeToString(root.RootHash)), LogIndex: swag.Int64(queuedLeaf.LeafIndex), Hashes: hashes, Checkpoint: swag.String(string(scBytes)), } logEntryAnon.Verification = &models.LogEntryAnonVerification{ InclusionProof: &inclusionProof, SignedEntryTimestamp: strfmt.Base64(signature), } logEntry := models.LogEntry{ entryID: logEntryAnon, } if api.newEntryPublisher != nil { // Publishing notifications should not block the API response. go func() { verifiers, err := entry.Verifiers() if err != nil { incPublishEvent(newentry.Name, "", false) log.ContextLogger(ctx).Errorf("Could not get verifiers for log entry %s: %v", entryID, err) return } var subjects []string for _, v := range verifiers { subjects = append(subjects, v.Subjects()...) } pbEntry, err := tle.GenerateTransparencyLogEntry(logEntryAnon) if err != nil { incPublishEvent(newentry.Name, "", false) log.ContextLogger(ctx).Error(err) return } event, err := newentry.New(entryID, pbEntry, subjects) if err != nil { incPublishEvent(newentry.Name, "", false) log.ContextLogger(ctx).Error(err) return } if viper.GetBool("rekor_server.publish_events_protobuf") { go publishEvent(ctx, api.newEntryPublisher, event, events.ContentTypeProtobuf) } if viper.GetBool("rekor_server.publish_events_json") { go publishEvent(ctx, api.newEntryPublisher, event, events.ContentTypeJSON) } }() } return logEntry, nil } func publishEvent(ctx context.Context, publisher pubsub.Publisher, event *events.Event, contentType events.EventContentType) { err := publisher.Publish(context.WithoutCancel(ctx), event, contentType) incPublishEvent(event.Type().Name(), contentType, err == nil) if err != nil { log.ContextLogger(ctx).Error(err) } } func incPublishEvent(event string, contentType events.EventContentType, ok bool) { status := "SUCCESS" if !ok { status = "ERROR" } labels := map[string]string{ "event": event, "status": status, "content_type": string(contentType), } metricPublishEvents.With(labels).Inc() } // CreateLogEntryHandler creates new entry into log func CreateLogEntryHandler(params entries.CreateLogEntryParams) middleware.Responder { httpReq := params.HTTPRequest logEntry, err := createLogEntry(params) if err != nil { return err } var uuid string for location := range logEntry { uuid = location } return entries.NewCreateLogEntryCreated().WithPayload(logEntry).WithLocation(getEntryURL(*httpReq.URL, uuid)).WithETag(uuid) } // getEntryURL returns the absolute path to the log entry in a RESTful style func getEntryURL(locationURL url.URL, uuid string) strfmt.URI { // remove API key from output query := locationURL.Query() query.Del("apiKey") locationURL.RawQuery = query.Encode() locationURL.Path = fmt.Sprintf("%v/%v", locationURL.Path, uuid) return strfmt.URI(locationURL.String()) } // GetLogEntryByUUIDHandler gets log entry and inclusion proof for specified UUID aka merkle leaf hash func GetLogEntryByUUIDHandler(params entries.GetLogEntryByUUIDParams) middleware.Responder { logEntry, err := retrieveLogEntry(params.HTTPRequest.Context(), params.EntryUUID) if err != nil { if errors.Is(err, ErrNotFound) { return handleRekorAPIError(params, http.StatusNotFound, err, "") } if _, ok := (err).(types.ValidationError); ok { return handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf("incorrectly formatted uuid %s", params.EntryUUID), params.EntryUUID) } return handleRekorAPIError(params, http.StatusInternalServerError, err, trillianCommunicationError) } return entries.NewGetLogEntryByUUIDOK().WithPayload(logEntry) } // SearchLogQueryHandler searches log by index, UUID, or proposed entry and returns array of entries found with inclusion proofs func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Responder { httpReqCtx := params.HTTPRequest.Context() resultPayload := []models.LogEntry{} totalQueries := len(params.Entry.EntryUUIDs) + len(params.Entry.Entries()) + len(params.Entry.LogIndexes) if totalQueries > maxSearchQueries { return handleRekorAPIError(params, http.StatusUnprocessableEntity, fmt.Errorf(maxSearchQueryLimit, maxSearchQueries), fmt.Sprintf(maxSearchQueryLimit, maxSearchQueries)) } if len(params.Entry.EntryUUIDs) > 0 || len(params.Entry.Entries()) > 0 { var searchHashes [][]byte for _, entryID := range params.Entry.EntryUUIDs { // if we got this far, then entryID is either a 64 or 80 character hex string err := sharding.ValidateEntryID(entryID) if err == nil { logEntry, err := retrieveLogEntry(httpReqCtx, entryID) if err != nil && !errors.Is(err, ErrNotFound) { return handleRekorAPIError(params, http.StatusInternalServerError, err, fmt.Sprintf("error getting log entry for %s", entryID)) } else if err == nil { resultPayload = append(resultPayload, logEntry) } continue } else if len(entryID) == sharding.EntryIDHexStringLen { // if ValidateEntryID failed and this is a full length entryID, then we can't search for it return handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf("invalid entryID %s", entryID)) } // At this point, check if we got a uuid instead of an EntryID, so search for the hash later uuid := entryID if err := sharding.ValidateUUID(uuid); err != nil { return handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf("invalid uuid %s", uuid)) } hash, err := hex.DecodeString(uuid) if err != nil { return handleRekorAPIError(params, http.StatusBadRequest, err, malformedUUID) } searchHashes = append(searchHashes, hash) } entries := params.Entry.Entries() for _, e := range entries { entry, err := types.UnmarshalEntry(e) if err != nil { return handleRekorAPIError(params, http.StatusBadRequest, fmt.Errorf("unmarshalling entry: %w", err), err.Error()) } leaf, err := types.CanonicalizeEntry(httpReqCtx, entry) if err != nil { return handleRekorAPIError(params, http.StatusBadRequest, fmt.Errorf("canonicalizing entry: %w", err), err.Error()) } hasher := rfc6962.DefaultHasher leafHash := hasher.HashLeaf(leaf) searchHashes = append(searchHashes, leafHash) } searchByHashResults := make([]map[int64]*trillian.GetEntryAndProofResponse, len(searchHashes)) for i, hash := range searchHashes { var results map[int64]*trillian.GetEntryAndProofResponse for _, shard := range api.logRanges.AllShards() { tcs := trillianclient.NewTrillianClient(httpReqCtx, api.logClient, shard) resp := tcs.GetLeafAndProofByHash(hash) switch resp.Status { case codes.OK: leafResult := resp.GetLeafAndProofResult if leafResult != nil && leafResult.Leaf != nil { if results == nil { results = map[int64]*trillian.GetEntryAndProofResponse{} } results[shard] = resp.GetLeafAndProofResult } case codes.NotFound: // do nothing here, do not throw 404 error continue default: return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("error getLeafAndProofByHash(%s): code: %v, msg %v", hex.EncodeToString(hash), resp.Status, resp.Err), trillianCommunicationError) } } searchByHashResults[i] = results } for _, hashMap := range searchByHashResults { for shard, leafResp := range hashMap { if leafResp == nil { continue } tcs := trillianclient.NewTrillianClient(httpReqCtx, api.logClient, shard) logEntry, err := logEntryFromLeaf(httpReqCtx, api.signer, tcs, leafResp.Leaf, leafResp.SignedLogRoot, leafResp.Proof, shard, api.logRanges) if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, err, err.Error()) } resultPayload = append(resultPayload, logEntry) } } } if len(params.Entry.LogIndexes) > 0 { for _, logIndex := range params.Entry.LogIndexes { logEntry, err := retrieveLogEntryByIndex(httpReqCtx, int(swag.Int64Value(logIndex))) if err != nil && !errors.Is(err, ErrNotFound) { return handleRekorAPIError(params, http.StatusInternalServerError, err, err.Error()) } else if err == nil { resultPayload = append(resultPayload, logEntry) } } } return entries.NewSearchLogQueryOK().WithPayload(resultPayload) } var ErrNotFound = errors.New("grpc returned 0 leaves with success code") func retrieveLogEntryByIndex(ctx context.Context, logIndex int) (models.LogEntry, error) { log.ContextLogger(ctx).Infof("Retrieving log entry by index %d", logIndex) tid, resolvedIndex := api.logRanges.ResolveVirtualIndex(logIndex) tc := trillianclient.NewTrillianClient(ctx, api.logClient, tid) log.ContextLogger(ctx).Debugf("Retrieving resolved index %v from TreeID %v", resolvedIndex, tid) resp := tc.GetLeafAndProofByIndex(resolvedIndex) switch resp.Status { case codes.OK: case codes.NotFound, codes.OutOfRange, codes.InvalidArgument: return models.LogEntry{}, ErrNotFound default: return models.LogEntry{}, fmt.Errorf("grpc err: %w: %s", resp.Err, trillianCommunicationError) } result := resp.GetLeafAndProofResult leaf := result.Leaf if leaf == nil { return models.LogEntry{}, ErrNotFound } return logEntryFromLeaf(ctx, api.signer, tc, leaf, result.SignedLogRoot, result.Proof, tid, api.logRanges) } // Retrieve a Log Entry // If a tree ID is specified, look in that tree // Otherwise, look through all inactive and active shards func retrieveLogEntry(ctx context.Context, entryUUID string) (models.LogEntry, error) { log.ContextLogger(ctx).Debugf("Retrieving log entry %v", entryUUID) uuid, err := sharding.GetUUIDFromIDString(entryUUID) if err != nil { return nil, sharding.ErrPlainUUID } // Get the tree ID and check that shard for the entry tid, err := sharding.TreeID(entryUUID) if err == nil { return retrieveUUIDFromTree(ctx, uuid, tid) } // If we got a UUID instead of an EntryID, search all shards if errors.Is(err, sharding.ErrPlainUUID) { trees := []sharding.LogRange{{TreeID: api.logRanges.ActiveTreeID()}} trees = append(trees, api.logRanges.GetInactive()...) for _, t := range trees { logEntry, err := retrieveUUIDFromTree(ctx, uuid, t.TreeID) if err != nil { if errors.Is(err, ErrNotFound) { continue } return nil, err } return logEntry, nil } return nil, ErrNotFound } return nil, err } func retrieveUUIDFromTree(ctx context.Context, uuid string, tid int64) (models.LogEntry, error) { log.ContextLogger(ctx).Debugf("Retrieving log entry %v from tree %d", uuid, tid) hashValue, err := hex.DecodeString(uuid) if err != nil { return models.LogEntry{}, types.ValidationError(err) } tc := trillianclient.NewTrillianClient(ctx, api.logClient, tid) log.ContextLogger(ctx).Debugf("Attempting to retrieve UUID %v from TreeID %v", uuid, tid) resp := tc.GetLeafAndProofByHash(hashValue) switch resp.Status { case codes.OK: result := resp.GetLeafAndProofResult if resp.Err != nil { // this shouldn't be possible since GetLeafAndProofByHash verifies the inclusion proof using a computed leaf hash // so this is just a defensive check if result.Leaf == nil { return models.LogEntry{}, ErrNotFound } return models.LogEntry{}, err } logEntry, err := logEntryFromLeaf(ctx, api.signer, tc, result.Leaf, result.SignedLogRoot, result.Proof, tid, api.logRanges) if err != nil { return models.LogEntry{}, fmt.Errorf("could not create log entry from leaf: %w", err) } return logEntry, nil case codes.NotFound: return models.LogEntry{}, ErrNotFound default: log.ContextLogger(ctx).Errorf("Unexpected response code while attempting to retrieve UUID %v from TreeID %v: %v", uuid, tid, resp.Status) return models.LogEntry{}, errors.New("unexpected error") } } // handlers for APIs that may be disabled in a given instance func CreateLogEntryNotImplementedHandler(_ entries.CreateLogEntryParams) middleware.Responder { err := &models.Error{ Code: http.StatusNotImplemented, Message: "Create Entry API not enabled in this Rekor instance", } return entries.NewCreateLogEntryDefault(http.StatusNotImplemented).WithPayload(err) } func GetLogEntryByIndexNotImplementedHandler(_ entries.GetLogEntryByIndexParams) middleware.Responder { err := &models.Error{ Code: http.StatusNotImplemented, Message: "Get Log Entry by Index API not enabled in this Rekor instance", } return entries.NewGetLogEntryByIndexDefault(http.StatusNotImplemented).WithPayload(err) } func GetLogEntryByUUIDNotImplementedHandler(_ entries.GetLogEntryByUUIDParams) middleware.Responder { err := &models.Error{ Code: http.StatusNotImplemented, Message: "Get Log Entry by UUID API not enabled in this Rekor instance", } return entries.NewGetLogEntryByUUIDDefault(http.StatusNotImplemented).WithPayload(err) } func SearchLogQueryNotImplementedHandler(_ entries.SearchLogQueryParams) middleware.Responder { err := &models.Error{ Code: http.StatusNotImplemented, Message: "Search Log Query API not enabled in this Rekor instance", } return entries.NewSearchLogQueryDefault(http.StatusNotImplemented).WithPayload(err) } rekor-1.3.5/pkg/api/error.go000066400000000000000000000141271455727245600157020ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package api import ( "fmt" "net/http" "regexp" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" "github.com/mitchellh/mapstructure" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/generated/restapi/operations/entries" "github.com/sigstore/rekor/pkg/generated/restapi/operations/index" "github.com/sigstore/rekor/pkg/generated/restapi/operations/pubkey" "github.com/sigstore/rekor/pkg/generated/restapi/operations/tlog" "github.com/sigstore/rekor/pkg/log" ) const ( trillianCommunicationError = "unexpected error communicating with transparency log" trillianUnexpectedResult = "unexpected result from transparency log" validationError = "error processing entry: %v" failedToGenerateCanonicalEntry = "error generating canonicalized entry" entryAlreadyExists = "an equivalent entry already exists in the transparency log with UUID %v" firstSizeLessThanLastSize = "firstSize(%d) must be less than lastSize(%d)" malformedUUID = "UUID must be a 64-character hexadecimal string" malformedPublicKey = "public key provided could not be parsed" failedToGenerateCanonicalKey = "error generating canonicalized public key" indexStorageUnexpectedResult = "unexpected result from searching index" lastSizeGreaterThanKnown = "the tree size requested(%d) was greater than what is currently observable(%d)" signingError = "error signing" sthGenerateError = "error generating signed tree head" unsupportedPKIFormat = "the PKI format requested is not supported by this server" unexpectedInactiveShardError = "unexpected error communicating with inactive shard" maxSearchQueryLimit = "more than max allowed %d entries in request" ) func errorMsg(message string, code int) *models.Error { return &models.Error{ Code: int64(code), Message: message, } } func handleRekorAPIError(params interface{}, code int, err error, message string, fields ...interface{}) middleware.Responder { if message == "" { message = http.StatusText(code) } re := regexp.MustCompile("^(.*)Params$") typeStr := fmt.Sprintf("%T", params) handler := re.FindStringSubmatch(typeStr)[1] logMsg := func(r *http.Request) { ctx := r.Context() fields := append([]interface{}{"handler", handler, "statusCode", code, "clientMessage", message}, fields...) if code >= 500 { log.ContextLogger(ctx).Errorw(err.Error(), fields...) } else { log.ContextLogger(ctx).Warnw(err.Error(), fields...) } paramsFields := map[string]interface{}{} if err := mapstructure.Decode(params, ¶msFields); err == nil { log.ContextLogger(ctx).Debug(paramsFields) } } switch params := params.(type) { case entries.GetLogEntryByIndexParams: logMsg(params.HTTPRequest) switch code { case http.StatusNotFound: return entries.NewGetLogEntryByIndexNotFound() default: return entries.NewGetLogEntryByIndexDefault(code).WithPayload(errorMsg(message, code)) } case entries.GetLogEntryByUUIDParams: logMsg(params.HTTPRequest) switch code { case http.StatusNotFound: return entries.NewGetLogEntryByUUIDNotFound() default: return entries.NewGetLogEntryByUUIDDefault(code).WithPayload(errorMsg(message, code)) } case entries.CreateLogEntryParams: switch code { // We treat "duplicate entry" as an error, but it's not really an error, so we don't need to log it as one. case http.StatusBadRequest: logMsg(params.HTTPRequest) return entries.NewCreateLogEntryBadRequest().WithPayload(errorMsg(message, code)) case http.StatusConflict: resp := entries.NewCreateLogEntryConflict().WithPayload(errorMsg(message, code)) locationFound := false for _, field := range fields { if locationFound { existingURL := field.(strfmt.URI) resp.SetLocation(existingURL) break } else if field.(string) == "entryURL" { locationFound = true continue } } return resp default: logMsg(params.HTTPRequest) return entries.NewCreateLogEntryDefault(code).WithPayload(errorMsg(message, code)) } case entries.SearchLogQueryParams: logMsg(params.HTTPRequest) switch code { case http.StatusBadRequest: return entries.NewSearchLogQueryBadRequest().WithPayload(errorMsg(message, code)) case http.StatusUnprocessableEntity: return entries.NewSearchLogQueryUnprocessableEntity().WithPayload(errorMsg(message, code)) default: return entries.NewSearchLogQueryDefault(code).WithPayload(errorMsg(message, code)) } case tlog.GetLogInfoParams: logMsg(params.HTTPRequest) return tlog.NewGetLogInfoDefault(code).WithPayload(errorMsg(message, code)) case tlog.GetLogProofParams: logMsg(params.HTTPRequest) switch code { case http.StatusBadRequest: return tlog.NewGetLogProofBadRequest().WithPayload(errorMsg(message, code)) default: return tlog.NewGetLogProofDefault(code).WithPayload(errorMsg(message, code)) } case pubkey.GetPublicKeyParams: logMsg(params.HTTPRequest) return pubkey.NewGetPublicKeyDefault(code).WithPayload(errorMsg(message, code)) case index.SearchIndexParams: logMsg(params.HTTPRequest) switch code { case http.StatusBadRequest: return index.NewSearchIndexBadRequest().WithPayload(errorMsg(message, code)) default: return index.NewSearchIndexDefault(code).WithPayload(errorMsg(message, code)) } default: log.Logger.Errorf("unable to find method for type %T; error: %v", params, err) return middleware.Error(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } } rekor-1.3.5/pkg/api/index.go000066400000000000000000000143601455727245600156570ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package api import ( "context" "crypto/sha256" "encoding/hex" "fmt" "net/http" "strings" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/generated/restapi/operations/index" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/util" ) func SearchIndexHandler(params index.SearchIndexParams) middleware.Responder { httpReqCtx := params.HTTPRequest.Context() queryOperator := params.Query.Operator // default to "or" if no operator is specified if params.Query.Operator == "" { queryOperator = "or" } var result = NewCollection(queryOperator) var lookupKeys []string if params.Query.Hash != "" { // This must be a valid hash sha := strings.ToLower(util.PrefixSHA(params.Query.Hash)) if queryOperator == "or" { lookupKeys = append(lookupKeys, sha) } else { resultUUIDs, err := indexStorageClient.LookupIndices(httpReqCtx, []string{sha}) if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("index storage error: %w", err), indexStorageUnexpectedResult) } result.Add(resultUUIDs) } } if params.Query.PublicKey != nil { af, err := pki.NewArtifactFactory(pki.Format(swag.StringValue(params.Query.PublicKey.Format))) if err != nil { return handleRekorAPIError(params, http.StatusBadRequest, err, unsupportedPKIFormat) } keyReader, err := util.FileOrURLReadCloser(httpReqCtx, params.Query.PublicKey.URL.String(), params.Query.PublicKey.Content) if err != nil { return handleRekorAPIError(params, http.StatusBadRequest, err, malformedPublicKey) } defer keyReader.Close() key, err := af.NewPublicKey(keyReader) if err != nil { return handleRekorAPIError(params, http.StatusBadRequest, err, malformedPublicKey) } canonicalKey, err := key.CanonicalValue() if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, err, failedToGenerateCanonicalKey) } keyHash := sha256.Sum256(canonicalKey) keyHashStr := strings.ToLower(hex.EncodeToString(keyHash[:])) if queryOperator == "or" { lookupKeys = append(lookupKeys, keyHashStr) } else { resultUUIDs, err := indexStorageClient.LookupIndices(httpReqCtx, []string{keyHashStr}) if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("index storage error: %w", err), indexStorageUnexpectedResult) } result.Add(resultUUIDs) } } if params.Query.Email != "" { emailStr := strings.ToLower(params.Query.Email.String()) if queryOperator == "or" { lookupKeys = append(lookupKeys, emailStr) } else { resultUUIDs, err := indexStorageClient.LookupIndices(httpReqCtx, []string{emailStr}) if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("index storage error: %w", err), indexStorageUnexpectedResult) } result.Add(resultUUIDs) } } if len(lookupKeys) > 0 { resultUUIDs, err := indexStorageClient.LookupIndices(httpReqCtx, lookupKeys) if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("index storage error: %w", err), indexStorageUnexpectedResult) } result.Add(resultUUIDs) } return index.NewSearchIndexOK().WithPayload(result.Values()) } func SearchIndexNotImplementedHandler(_ index.SearchIndexParams) middleware.Responder { err := models.Error{ Code: http.StatusNotImplemented, Message: "Search Index API not enabled in this Rekor instance", } return index.NewSearchIndexDefault(http.StatusNotImplemented).WithPayload(&err) } func addToIndex(ctx context.Context, keys []string, value string) error { err := indexStorageClient.WriteIndex(ctx, keys, value) if err != nil { return fmt.Errorf("redis client: %w", err) } return nil } func storeAttestation(ctx context.Context, uuid string, attestation []byte) error { return attestationStorageClient.StoreAttestation(ctx, uuid, attestation) } // Uniq is a collection of unique elements. type Uniq map[string]struct{} func NewUniq() Uniq { return make(Uniq) } func (u Uniq) Add(elements ...string) { for _, e := range elements { u[e] = struct{}{} } } func (u Uniq) Values() []string { var result []string for k := range u { result = append(result, k) } return result } // Intersect returns the intersection of two collections. func (u Uniq) Intersect(other Uniq) Uniq { result := make(Uniq) for k := range u { if _, ok := other[k]; ok { result[k] = struct{}{} } } return result } // Union returns the union of two collections. func (u Uniq) Union(other Uniq) Uniq { result := make(Uniq) for k := range u { result[k] = struct{}{} } for k := range other { result[k] = struct{}{} } return result } // Collection is a collection of sets. // // its resulting values is a union or intersection of all the sets, depending on the operator. type Collection struct { subsets []Uniq operator string } // NewCollection creates a new collection. func NewCollection(operator string) *Collection { return &Collection{ subsets: []Uniq{}, operator: operator, } } // Add adds the elements into a new subset in the collection. func (u *Collection) Add(elements []string) { subset := Uniq{} subset.Add(elements...) u.subsets = append(u.subsets, subset) } // Values flattens the subsets using the operator, and returns the collection as a slice of strings. func (u *Collection) Values() []string { if len(u.subsets) == 0 { return []string{} } subset := u.subsets[0] for i := 1; i < len(u.subsets); i++ { if strings.EqualFold(u.operator, "and") { subset = subset.Intersect(u.subsets[i]) } else { subset = subset.Union(u.subsets[i]) } } return subset.Values() } rekor-1.3.5/pkg/api/index_test.go000066400000000000000000000042631455727245600167170ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package api import ( "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" ) func Test_Collection(t *testing.T) { vals := []string{"foo", "bar", "baz", "baz", "baz"} t.Run("Unique", func(t *testing.T) { unq := NewUniq() unq.Add(vals...) if len(unq.Values()) != 3 { t.Errorf("expected 3 unique values, got %d", len(unq.Values())) } expected := []string{"foo", "bar", "baz"} if !testEqualNoOrder(t, expected, unq.Values()) { t.Errorf("expected %v, got %v", expected, unq.Values()) } }) t.Run("Collection", func(t *testing.T) { uniq1 := []string{"foo", "bar", "baz"} uniq2 := []string{"foo", "bar", "baz"} uniq3 := []string{"corge", "grault", "garply", "foo"} tests := []struct { name string operator string expected []string }{ {name: "with 'and' operator", operator: "and", expected: []string{"foo"}, }, {name: "with 'or' operator", operator: "or", expected: []string{"foo", "bar", "baz", "corge", "grault", "garply"}, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { c := NewCollection(test.operator) c.Add(uniq1) c.Add(uniq2) c.Add(uniq3) if !testEqualNoOrder(t, test.expected, c.Values()) { t.Errorf("expected %v, got %v", test.expected, c.Values()) } }) } }) } // testEqualNoOrder compares two slices of strings without considering order. func testEqualNoOrder(t *testing.T, expected, actual []string) bool { t.Helper() less := func(a, b string) bool { return a < b } return cmp.Diff(actual, expected, cmpopts.SortSlices(less)) == "" } rekor-1.3.5/pkg/api/metrics.go000066400000000000000000000052021455727245600162110ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package api import ( "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "sigs.k8s.io/release-utils/version" ) var ( metricNewEntries = promauto.NewCounter(prometheus.CounterOpts{ Name: "rekor_new_entries", Help: "The total number of new log entries", }) metricPublishEvents = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "rekor_publish_events", Help: "The status of publishing events to Pub/Sub", }, []string{"event", "content_type", "status"}) MetricLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{ Name: "rekor_api_latency", Help: "Api Latency on calls", }, []string{"path", "code"}) MetricLatencySummary = promauto.NewSummaryVec(prometheus.SummaryOpts{ Name: "rekor_api_latency_summary", Help: "Api Latency on calls", }, []string{"path", "code"}) MetricRequestLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{ Name: "rekor_latency_by_api", Help: "Api Latency (in ns) by path and method", Buckets: prometheus.ExponentialBucketsRange( float64(time.Millisecond), float64(4*time.Second), 10), }, []string{"path", "method"}) MetricRequestCount = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "rekor_qps_by_api", Help: "Api QPS by path, method, and response code", }, []string{"path", "method", "code"}) CheckpointPublishCount = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "rekor_checkpoint_publish", Help: "Checkpoint publishing by shard and code", }, []string{"shard", "code"}) _ = promauto.NewGaugeFunc( prometheus.GaugeOpts{ Namespace: "rekor", 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 }, ) ) rekor-1.3.5/pkg/api/public_key.go000066400000000000000000000027171455727245600167010ustar00rootroot00000000000000/* Copyright The Rekor 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 api import ( "net/http" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/generated/restapi/operations/pubkey" ) func GetPublicKeyHandler(params pubkey.GetPublicKeyParams) middleware.Responder { treeID := swag.StringValue(params.TreeID) pk, err := api.logRanges.PublicKey(api.pubkey, treeID) if err != nil { return handleRekorAPIError(params, http.StatusBadRequest, err, "") } return pubkey.NewGetPublicKeyOK().WithPayload(pk) } // handlers for APIs that may be disabled in a given instance func GetPublicKeyNotImplementedHandler(_ pubkey.GetPublicKeyParams) middleware.Responder { err := &models.Error{ Code: http.StatusNotImplemented, Message: "Get Public Key API not enabled in this Rekor instance", } return pubkey.NewGetPublicKeyDefault(http.StatusNotImplemented).WithPayload(err) } rekor-1.3.5/pkg/api/tlog.go000066400000000000000000000202171455727245600155130ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package api import ( "context" "encoding/hex" "fmt" "net/http" "strconv" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/swag" "github.com/google/trillian/types" "github.com/spf13/viper" "google.golang.org/grpc/codes" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/generated/restapi/operations/tlog" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/trillianclient" "github.com/sigstore/rekor/pkg/util" ) // GetLogInfoHandler returns the current size of the tree and the STH func GetLogInfoHandler(params tlog.GetLogInfoParams) middleware.Responder { tc := trillianclient.NewTrillianClient(params.HTTPRequest.Context(), api.logClient, api.logID) // for each inactive shard, get the loginfo var inactiveShards []*models.InactiveShardLogInfo for _, shard := range api.logRanges.GetInactive() { if shard.TreeID == api.logRanges.ActiveTreeID() { break } // Get details for this inactive shard is, err := inactiveShardLogInfo(params.HTTPRequest.Context(), shard.TreeID) if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("inactive shard error: %w", err), unexpectedInactiveShardError) } inactiveShards = append(inactiveShards, is) } if swag.BoolValue(params.Stable) && redisClient != nil { // key is treeID/latest key := fmt.Sprintf("%d/latest", api.logRanges.ActiveTreeID()) redisResult, err := redisClient.Get(params.HTTPRequest.Context(), key).Result() if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("error getting checkpoint from redis: %w", err), "error getting checkpoint from redis") } // should not occur, a checkpoint should always be present if redisResult == "" { return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("no checkpoint found in redis: %w", err), "no checkpoint found in redis") } decoded, err := hex.DecodeString(redisResult) if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("error decoding checkpoint from redis: %w", err), "error decoding checkpoint from redis") } checkpoint := util.SignedCheckpoint{} if err := checkpoint.UnmarshalText(decoded); err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("invalid checkpoint: %w", err), "invalid checkpoint") } logInfo := models.LogInfo{ RootHash: stringPointer(hex.EncodeToString(checkpoint.Hash)), TreeSize: swag.Int64(int64(checkpoint.Size)), SignedTreeHead: stringPointer(string(decoded)), TreeID: stringPointer(fmt.Sprintf("%d", api.logID)), InactiveShards: inactiveShards, } return tlog.NewGetLogInfoOK().WithPayload(&logInfo) } resp := tc.GetLatest(0) if resp.Status != codes.OK { return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("grpc error: %w", resp.Err), trillianCommunicationError) } result := resp.GetLatestResult root := &types.LogRootV1{} if err := root.UnmarshalBinary(result.SignedLogRoot.LogRoot); err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, err, trillianUnexpectedResult) } hashString := hex.EncodeToString(root.RootHash) treeSize := int64(root.TreeSize) scBytes, err := util.CreateAndSignCheckpoint(params.HTTPRequest.Context(), viper.GetString("rekor_server.hostname"), api.logRanges.ActiveTreeID(), root.TreeSize, root.RootHash, api.signer) if err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, err, sthGenerateError) } logInfo := models.LogInfo{ RootHash: &hashString, TreeSize: &treeSize, SignedTreeHead: stringPointer(string(scBytes)), TreeID: stringPointer(fmt.Sprintf("%d", api.logID)), InactiveShards: inactiveShards, } return tlog.NewGetLogInfoOK().WithPayload(&logInfo) } func stringPointer(s string) *string { return &s } // GetLogProofHandler returns information required to compute a consistency proof between two snapshots of log func GetLogProofHandler(params tlog.GetLogProofParams) middleware.Responder { if *params.FirstSize > params.LastSize { return handleRekorAPIError(params, http.StatusBadRequest, nil, fmt.Sprintf(firstSizeLessThanLastSize, *params.FirstSize, params.LastSize)) } tc := trillianclient.NewTrillianClient(params.HTTPRequest.Context(), api.logClient, api.logID) if treeID := swag.StringValue(params.TreeID); treeID != "" { id, err := strconv.Atoi(treeID) if err != nil { log.Logger.Infof("Unable to convert %s to string, skipping initializing client with Tree ID: %v", treeID, err) } else { tc = trillianclient.NewTrillianClient(params.HTTPRequest.Context(), api.logClient, int64(id)) } } resp := tc.GetConsistencyProof(*params.FirstSize, params.LastSize) if resp.Status != codes.OK { return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("grpc error: %w", resp.Err), trillianCommunicationError) } result := resp.GetConsistencyProofResult var root types.LogRootV1 if err := root.UnmarshalBinary(result.SignedLogRoot.LogRoot); err != nil { return handleRekorAPIError(params, http.StatusInternalServerError, err, trillianUnexpectedResult) } hashString := hex.EncodeToString(root.RootHash) proofHashes := []string{} if proof := result.GetProof(); proof != nil { for _, hash := range proof.Hashes { proofHashes = append(proofHashes, hex.EncodeToString(hash)) } } else { // The proof field may be empty if the requested tree_size was larger than that available at the server // (e.g. because there is skew between server instances, and an earlier client request was processed by // a more up-to-date instance). root.TreeSize is the maximum size currently observed err := fmt.Errorf(lastSizeGreaterThanKnown, params.LastSize, root.TreeSize) return handleRekorAPIError(params, http.StatusBadRequest, err, err.Error()) } consistencyProof := models.ConsistencyProof{ RootHash: &hashString, Hashes: proofHashes, } return tlog.NewGetLogProofOK().WithPayload(&consistencyProof) } func inactiveShardLogInfo(ctx context.Context, tid int64) (*models.InactiveShardLogInfo, error) { tc := trillianclient.NewTrillianClient(ctx, api.logClient, tid) resp := tc.GetLatest(0) if resp.Status != codes.OK { return nil, fmt.Errorf("resp code is %d", resp.Status) } result := resp.GetLatestResult root := &types.LogRootV1{} if err := root.UnmarshalBinary(result.SignedLogRoot.LogRoot); err != nil { return nil, err } hashString := hex.EncodeToString(root.RootHash) treeSize := int64(root.TreeSize) scBytes, err := util.CreateAndSignCheckpoint(ctx, viper.GetString("rekor_server.hostname"), tid, root.TreeSize, root.RootHash, api.signer) if err != nil { return nil, err } m := models.InactiveShardLogInfo{ RootHash: &hashString, TreeSize: &treeSize, TreeID: stringPointer(fmt.Sprintf("%d", tid)), SignedTreeHead: stringPointer(string(scBytes)), } return &m, nil } // handlers for APIs that may be disabled in a given instance func GetLogInfoNotImplementedHandler(_ tlog.GetLogInfoParams) middleware.Responder { err := &models.Error{ Code: http.StatusNotImplemented, Message: "Get Log Info API not enabled in this Rekor instance", } return tlog.NewGetLogInfoDefault(http.StatusNotImplemented).WithPayload(err) } func GetLogProofNotImplementedHandler(_ tlog.GetLogProofParams) middleware.Responder { err := &models.Error{ Code: http.StatusNotImplemented, Message: "Get Log Proof API not enabled in this Rekor instance", } return tlog.NewGetLogProofDefault(http.StatusNotImplemented).WithPayload(err) } rekor-1.3.5/pkg/client/000077500000000000000000000000001455727245600147225ustar00rootroot00000000000000rekor-1.3.5/pkg/client/options.go000066400000000000000000000046231455727245600167510ustar00rootroot00000000000000// Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package client import ( "net/http" "github.com/hashicorp/go-retryablehttp" ) // Option is a functional option for customizing static signatures. type Option func(*options) type options struct { UserAgent string RetryCount uint InsecureTLS bool Logger interface{} } const ( // DefaultRetryCount is the default number of retries. DefaultRetryCount = 3 ) func makeOptions(opts ...Option) *options { o := &options{ UserAgent: "", RetryCount: DefaultRetryCount, } for _, opt := range opts { opt(o) } return o } // WithUserAgent sets the media type of the signature. func WithUserAgent(userAgent string) Option { return func(o *options) { o.UserAgent = userAgent } } // WithRetryCount sets the number of retries. func WithRetryCount(retryCount uint) Option { return func(o *options) { o.RetryCount = retryCount } } // WithLogger sets the logger; it must implement either retryablehttp.Logger or retryablehttp.LeveledLogger; if not, this will not take effect. func WithLogger(logger interface{}) Option { return func(o *options) { switch logger.(type) { case retryablehttp.Logger, retryablehttp.LeveledLogger: o.Logger = logger } } } func WithInsecureTLS(enabled bool) Option { return func(o *options) { o.InsecureTLS = enabled } } type roundTripper struct { http.RoundTripper UserAgent string } // RoundTrip implements `http.RoundTripper` func (rt *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { req.Header.Set("User-Agent", rt.UserAgent) return rt.RoundTripper.RoundTrip(req) } func createRoundTripper(inner http.RoundTripper, o *options) http.RoundTripper { if inner == nil { inner = http.DefaultTransport } if o.UserAgent == "" { // There's nothing to do... return inner } return &roundTripper{ RoundTripper: inner, UserAgent: o.UserAgent, } } rekor-1.3.5/pkg/client/options_test.go000066400000000000000000000071401455727245600200050ustar00rootroot00000000000000// Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package client import ( "log" "net/http" "os" "testing" "github.com/google/go-cmp/cmp" ) func TestMakeOptions(t *testing.T) { customLogger := log.New(os.Stdout, "", log.LstdFlags) tests := []struct { desc string opts []Option want *options }{{ desc: "no opts", want: &options{RetryCount: DefaultRetryCount}, }, { desc: "WithUserAgent", opts: []Option{WithUserAgent("test user agent")}, want: &options{UserAgent: "test user agent", RetryCount: DefaultRetryCount}, }, { desc: "WithRetryCount", opts: []Option{WithRetryCount(2)}, want: &options{UserAgent: "", RetryCount: 2}, }, { desc: "WithLogger", opts: []Option{WithLogger(customLogger)}, want: &options{UserAgent: "", RetryCount: DefaultRetryCount, Logger: customLogger}, }, { desc: "WithLoggerNil", opts: []Option{WithLogger(nil)}, want: &options{UserAgent: "", RetryCount: DefaultRetryCount}, }, { desc: "WithInsecureTLSEnabled", opts: []Option{WithInsecureTLS(true)}, want: &options{UserAgent: "", RetryCount: DefaultRetryCount, InsecureTLS: true}, }, { desc: "WithInsecureTLSDisabled", opts: []Option{WithInsecureTLS(false)}, want: &options{UserAgent: "", RetryCount: DefaultRetryCount, InsecureTLS: false}, }} for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { got := makeOptions(tc.opts...) if d := cmp.Diff(tc.want, got, cmp.Comparer(func(a, b *log.Logger) bool { return a == b })); d != "" { t.Errorf("makeOptions(%v) returned unexpected result (-want +got): %s", tc.desc, d) } }) } } type mockRoundTripper struct { gotReqs []*http.Request resp *http.Response err error } // RoundTrip implements `http.RoundTripper` func (m *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { m.gotReqs = append(m.gotReqs, req) return m.resp, m.err } func TestCreateRoundTripper(t *testing.T) { t.Run("always returns non-nil", func(t *testing.T) { got := createRoundTripper(nil, &options{}) if got == nil { t.Errorf("createRoundTripper() should never return a nil `http.RoundTripper`") } }) testReq, err := http.NewRequest("GET", "http://www.example.com/test", nil) if err != nil { t.Fatalf("http.NewRequest() failed: %v", err) } testResp := &http.Response{ Status: "OK", StatusCode: 200, Request: testReq, } expectedUserAgent := "test UserAgent" m := &mockRoundTripper{} rt := createRoundTripper(m, &options{ UserAgent: expectedUserAgent, }) m.resp = testResp gotResp, err := rt.RoundTrip(testReq) if err != nil { t.Errorf("RoundTrip() returned error: %v", err) } if len(m.gotReqs) < 1 { t.Fatalf("inner RoundTripper.RoundTrip() was not called") } gotReq := m.gotReqs[0] gotReqUserAgent := gotReq.UserAgent() if gotReqUserAgent != expectedUserAgent { t.Errorf("rt.RoundTrip() did not set the User-Agent properly. Wanted: %q, got: %q", expectedUserAgent, gotReqUserAgent) } if testResp != gotResp { t.Errorf("roundTripper.RoundTrip() should have returned exactly the response of the inner RoundTripper. Wanted %v, got %v", testResp, gotResp) } } rekor-1.3.5/pkg/client/rekor_client.go000066400000000000000000000041461455727245600177360ustar00rootroot00000000000000// Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package client import ( "crypto/tls" "net/http" "net/url" "github.com/go-openapi/runtime" httptransport "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" "github.com/hashicorp/go-cleanhttp" retryablehttp "github.com/hashicorp/go-retryablehttp" "github.com/sigstore/rekor/pkg/generated/client" "github.com/sigstore/rekor/pkg/util" ) func GetRekorClient(rekorServerURL string, opts ...Option) (*client.Rekor, error) { url, err := url.Parse(rekorServerURL) if err != nil { return nil, err } o := makeOptions(opts...) retryableClient := retryablehttp.NewClient() defaultTransport := cleanhttp.DefaultTransport() if o.InsecureTLS { /* #nosec G402 */ defaultTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} } retryableClient.HTTPClient = &http.Client{ Transport: defaultTransport, } retryableClient.RetryMax = int(o.RetryCount) retryableClient.Logger = o.Logger httpClient := retryableClient.StandardClient() httpClient.Transport = createRoundTripper(httpClient.Transport, o) // sanitize path if url.Path == "" { url.Path = client.DefaultBasePath } rt := httptransport.NewWithClient(url.Host, url.Path, []string{url.Scheme}, httpClient) rt.Consumers["application/json"] = runtime.JSONConsumer() rt.Consumers["application/x-pem-file"] = runtime.TextConsumer() rt.Producers["application/json"] = runtime.JSONProducer() registry := strfmt.Default registry.Add("signedCheckpoint", &util.SignedNote{}, util.SignedCheckpointValidator) return client.New(rt, registry), nil } rekor-1.3.5/pkg/client/rekor_client_test.go000066400000000000000000000066521455727245600210010ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package client import ( "net/http" "net/http/httptest" "strings" "testing" "time" "github.com/sigstore/rekor/pkg/generated/client/index" "go.uber.org/goleak" ) func TestGetRekorClientWithUserAgent(t *testing.T) { t.Parallel() expectedUserAgent := "test User-Agent" requestReceived := false testServer := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { requestReceived = true file := []byte{} got := r.UserAgent() if got != expectedUserAgent { t.Errorf("wanted User-Agent %q, got %q", expectedUserAgent, got) } w.WriteHeader(http.StatusOK) _, _ = w.Write(file) })) defer testServer.Close() client, err := GetRekorClient(testServer.URL, WithUserAgent(expectedUserAgent)) if err != nil { t.Error(err) } _, _ = client.Tlog.GetLogInfo(nil) if !requestReceived { t.Fatal("no requests were received") } } func TestGetRekorClientWithCustomPath(t *testing.T) { t.Parallel() requestReceived := false pathAdd := "/custom" testServer := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { requestReceived = true if !strings.HasPrefix(r.URL.Path, pathAdd) { t.Errorf("Expected request to be sent to /test, got %s", r.URL.Path) } w.WriteHeader(http.StatusOK) })) defer testServer.Close() testServer.URL += pathAdd client, err := GetRekorClient(testServer.URL) if err != nil { t.Error(err) } _, _ = client.Tlog.GetLogInfo(nil) if !requestReceived { t.Fatal("no requests were received") } } func TestGetRekorClientWithRetryCount(t *testing.T) { t.Parallel() expectedCount := 2 actualCount := 0 testServer := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { actualCount++ file := []byte{} if actualCount < expectedCount { w.WriteHeader(http.StatusInternalServerError) } else { w.WriteHeader(http.StatusOK) _, _ = w.Write(file) } })) defer testServer.Close() client, err := GetRekorClient(testServer.URL, WithRetryCount(2)) if err != nil { t.Error(err) } _, err = client.Tlog.GetLogInfo(nil) if err != nil { t.Fatalf("unexpected error: %v", err) } } func TestRekorLeakedGoroutine_SearchByHash(t *testing.T) { testServer := httptest.NewUnstartedServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { file := []byte("ok") w.WriteHeader(http.StatusOK) _, _ = w.Write(file) })) testServer.EnableHTTP2 = true testServer.StartTLS() // sleep to allow go routines to start time.Sleep(1 * time.Second) // store the goroutines launched by the testserver opt := goleak.IgnoreCurrent() defer func() { goleak.VerifyNone(t, opt) // this is done after leak detection so that we can test testServer.Close() }() rekor, _ := GetRekorClient(testServer.URL, WithInsecureTLS(true)) rekor.Index.SearchIndex(index.NewSearchIndexParams()) } rekor-1.3.5/pkg/events/000077500000000000000000000000001455727245600147505ustar00rootroot00000000000000rekor-1.3.5/pkg/events/doc.go000066400000000000000000000014251455727245600160460ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package events provides methods for working with CloudEvents. package events // The version of the CloudEvents specification the package adheres to. const CloudEventsSpecVersion = "1.0" rekor-1.3.5/pkg/events/events.go000066400000000000000000000127671455727245600166200ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package events package events import ( "encoding/json" "fmt" "time" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/timestamppb" pb "github.com/sigstore/protobuf-specs/gen/pb-go/events/v1" ) // The content type of an event. type EventContentType string const ( ContentTypeProtobuf EventContentType = "application/protobuf" ContentTypeJSON EventContentType = "application/json" ) // Keys that cannot be used in the optional event attributes map due to // collisions with mandatory fields in the CloudEvents spec. var reservedFields = map[string]struct{}{ "datacontenttype": {}, "data": {}, "id": {}, "source": {}, "specversion": {}, "type": {}, } // Event is an instance of a certain event type. type Event struct { ty *EventType id string msg protoreflect.ProtoMessage attrs map[string]any } // Type returns the underlying event type. func (e Event) Type() *EventType { return e.ty } // ID returns the identifier for the event. func (e Event) ID() string { return e.id } // Attributes returns the attributes attached to the event. These attributes // are optional and this function may return an empty map. func (e Event) Attributes() map[string]any { return e.attrs } // Message returns the underlying message (aka data) of the event. func (e Event) Message() protoreflect.ProtoMessage { return e.msg } // New creates a new event of a given type. // // The "id" and "msg" parameters are required. The "attributes" parameter // supports the following value types: // - string // - int // - bool // - []byte // - time.Time func (t *EventType) New(id string, msg protoreflect.ProtoMessage, attributes map[string]any) (*Event, error) { if id == "" { return nil, fmt.Errorf("id must be set") } if msg == nil { return nil, fmt.Errorf("msg must be set") } ty := msg.ProtoReflect().Descriptor().FullName() if tty := t.Descriptor().FullName(); ty != tty { return nil, fmt.Errorf("msg type %q does not match expected type %q", ty, tty) } for name, value := range attributes { if _, ok := reservedFields[name]; ok { return nil, fmt.Errorf("attribute name %q is one of the reserved CloudEvents names %v", name, reservedFields) } switch v := value.(type) { case string, bool, int, []byte, time.Time: // Allowed types are a no-op. default: return nil, fmt.Errorf("unsupported attribute type for %q: %T", name, v) } } return &Event{ty: t, id: id, msg: msg, attrs: attributes}, nil } // MarshalJSON serializes the event to JSON, following the CloudEvents // specification. func (e *Event) MarshalJSON() ([]byte, error) { data, err := protojson.Marshal(e.msg) if err != nil { return nil, fmt.Errorf("marshal data to JSON: %w", err) } event := map[string]any{ "specversion": CloudEventsSpecVersion, "id": e.ID(), "source": e.Type().Source(), "type": e.Type().Name(), "datacontenttype": ContentTypeJSON, "data": string(data), } for k, v := range e.attrs { event[k] = v } return json.Marshal(event) } // MarshalProto serializes the event to the CloudEvents Protobuf wire format. func (e *Event) MarshalProto() ([]byte, error) { msg, err := e.Proto() if err != nil { return nil, fmt.Errorf("build proto: %w", err) } return proto.Marshal(msg) } // Proto returns the CloudEvents protobuf for the event. func (e *Event) Proto() (*pb.CloudEvent, error) { data, err := proto.Marshal(e.msg) if err != nil { return nil, fmt.Errorf("marshal data: %w", err) } attrs := make(map[string]*pb.CloudEvent_CloudEventAttributeValue) for name, value := range e.attrs { switch v := value.(type) { case string: attrs[name] = &pb.CloudEvent_CloudEventAttributeValue{ Attr: &pb.CloudEvent_CloudEventAttributeValue_CeString{CeString: v}, } case bool: attrs[name] = &pb.CloudEvent_CloudEventAttributeValue{ Attr: &pb.CloudEvent_CloudEventAttributeValue_CeBoolean{CeBoolean: v}, } case int: attrs[name] = &pb.CloudEvent_CloudEventAttributeValue{ Attr: &pb.CloudEvent_CloudEventAttributeValue_CeInteger{CeInteger: int32(v)}, } case time.Time: attrs[name] = &pb.CloudEvent_CloudEventAttributeValue{ Attr: &pb.CloudEvent_CloudEventAttributeValue_CeTimestamp{ CeTimestamp: timestamppb.New(v), }, } case []byte: attrs[name] = &pb.CloudEvent_CloudEventAttributeValue{ Attr: &pb.CloudEvent_CloudEventAttributeValue_CeBytes{CeBytes: v}, } } } event := &pb.CloudEvent{ SpecVersion: CloudEventsSpecVersion, Id: e.ID(), Source: e.Type().Source(), Type: e.Type().Name(), Attributes: attrs, Data: &pb.CloudEvent_ProtoData{ ProtoData: &anypb.Any{ Value: data, TypeUrl: string(e.Type().Descriptor().FullName()), }, }, } return event, nil } rekor-1.3.5/pkg/events/newentry/000077500000000000000000000000001455727245600166235ustar00rootroot00000000000000rekor-1.3.5/pkg/events/newentry/new_entry.go000066400000000000000000000026451455727245600211730ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package newentry import ( "strings" "time" "github.com/sigstore/rekor/pkg/events" "golang.org/x/exp/slices" rekor_pb "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" ) const ( Name = "dev.sigstore.rekor.events.v1.NewEntry" Source = "/createLogEntry" ) var ty *events.EventType func init() { empty := &rekor_pb.TransparencyLogEntry{} ty = events.RegisterType(Name, Source, empty.ProtoReflect().Descriptor()) } func New(id string, entry *rekor_pb.TransparencyLogEntry, subjects []string) (*events.Event, error) { slices.Sort(subjects) // Must be sorted for consistency. attrs := map[string]any{ "time": time.Unix(entry.GetIntegratedTime(), 0), "rekor_entry_kind": entry.GetKindVersion().GetKind(), "rekor_signing_subjects": strings.Join(subjects, ","), } return ty.New(id, entry, attrs) } rekor-1.3.5/pkg/events/newentry/new_entry_test.go000066400000000000000000000063221455727245600222260ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package newentry import ( "math" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/sigstore/rekor/pkg/events" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/timestamppb" events_pb "github.com/sigstore/protobuf-specs/gen/pb-go/events/v1" rekor_pb "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" ) func TestBuildNewEntryEvent(t *testing.T) { t.Parallel() decamillennium := time.Date(9999, 12, 31, 24, 59, 59, math.MaxInt, time.UTC).Unix() testEntry := &rekor_pb.TransparencyLogEntry{ IntegratedTime: decamillennium, KindVersion: &rekor_pb.KindVersion{ Kind: "test_kind", }, } marshalledEntry, err := proto.Marshal(testEntry) if err != nil { t.Fatal(err) } testCases := []struct { desc string entryID string subjects []string entry *rekor_pb.TransparencyLogEntry wantError bool want *events_pb.CloudEvent }{ { desc: "missing ID", subjects: []string{"test@rekor.dev"}, entry: testEntry, wantError: true, }, { desc: "valid", entryID: "test-id", subjects: []string{"test@rekor.dev", "foo@bar.baz"}, // Output should be sorted. entry: testEntry, want: &events_pb.CloudEvent{ SpecVersion: events.CloudEventsSpecVersion, Id: "test-id", Source: Source, Type: Name, Attributes: map[string]*events_pb.CloudEvent_CloudEventAttributeValue{ "time": {Attr: &events_pb.CloudEvent_CloudEventAttributeValue_CeTimestamp{ CeTimestamp: ×tamppb.Timestamp{Seconds: decamillennium}, }}, "rekor_entry_kind": {Attr: &events_pb.CloudEvent_CloudEventAttributeValue_CeString{ CeString: "test_kind", }}, "rekor_signing_subjects": {Attr: &events_pb.CloudEvent_CloudEventAttributeValue_CeString{ CeString: "foo@bar.baz,test@rekor.dev", }}, }, Data: &events_pb.CloudEvent_ProtoData{ ProtoData: &anypb.Any{ Value: marshalledEntry, TypeUrl: string(testEntry.ProtoReflect().Descriptor().FullName()), }, }, }, }, } for _, tc := range testCases { tc := tc t.Run(tc.desc, func(t *testing.T) { t.Parallel() event, err := New(tc.entryID, tc.entry, tc.subjects) gotErr := err != nil if gotErr != tc.wantError { t.Fatalf("New() err = %v, want %v", gotErr, tc.wantError) } if err != nil { return } msg, err := event.Proto() if err != nil { t.Fatal(err) } if diff := cmp.Diff(msg, tc.want, protocmp.Transform()); diff != "" { t.Errorf("New() unexpected diff:\n%s", diff) } }) } } rekor-1.3.5/pkg/events/types.go000066400000000000000000000050061455727245600164440ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package events import ( "fmt" "sync" "google.golang.org/protobuf/reflect/protoreflect" ) var ( registeredEventTypes = map[string]*EventType{} mu sync.Mutex ) // EventType describes the type of an event. type EventType struct { name string source string desc protoreflect.MessageDescriptor } // Name returns the unique name for the event type. func (t EventType) Name() string { return t.name } // Source returns the source for the event type. func (t EventType) Source() string { return t.source } // Descriptor returns the message descriptor for messages of the event type. func (t EventType) Descriptor() protoreflect.MessageDescriptor { return t.desc } // RegisterType registers a new event type. It is intended for use in init() // functions for each event type. It will panic if errors are encountered. func RegisterType(name, source string, desc protoreflect.MessageDescriptor) *EventType { mu.Lock() defer mu.Unlock() if name == "" { panic("event name must be set") } if source == "" { panic("event source must be set") } if desc == nil { panic("event descriptor must be set") } if _, ok := registeredEventTypes[name]; ok { panic("event has already been registered: " + name) } ty := &EventType{ name: name, source: source, desc: desc, } registeredEventTypes[name] = ty return ty } // EventNotFoundError indicates that no matching PubSub provider was found. type EventNotFoundError struct { name string } func (e *EventNotFoundError) Error() string { return fmt.Sprintf("event type not found: %s", e.name) } // Get returns an event type by name. func Get(name string) (*EventType, error) { v, ok := registeredEventTypes[name] if !ok { return nil, &EventNotFoundError{name: name} } return v, nil } // RegisteredTypes returns a map of all registered event types. The key is the // event type name. func RegisteredTypes() map[string]*EventType { return registeredEventTypes } rekor-1.3.5/pkg/fuzz/000077500000000000000000000000001455727245600144425ustar00rootroot00000000000000rekor-1.3.5/pkg/fuzz/alpine_utils.go000066400000000000000000000203361455727245600174650ustar00rootroot00000000000000// // Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package fuzz import ( "archive/tar" "bytes" "compress/gzip" "fmt" "os" "strings" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/sigstore/rekor/pkg/types" ) // Allows the fuzzer to create a .SIGN filename func getSignFilename(ff *fuzz.ConsumeFuzzer) (string, error) { keyName, err := ff.GetString() if err != nil { return "", err } var b strings.Builder b.WriteString(".SIGN.RSA.") b.WriteString(keyName) b.WriteString(".rsa.pub") return b.String(), nil } // createPkgInfoFileContents creates a structured pkginfo file // // .PKGINFO files look like this: // // # Generated by abuild 3.9.0-r2 // # using fakeroot version 1.25.3 // # Wed Jul 6 19:09:49 UTC 2022 // pkgname = busybox // pkgver = 1.35.0-r18 // pkgdesc = Size optimized toolbox of many common UNIX utilities // url = https://busybox.net/ // builddate = 1657134589 // packager = Buildozer // size = 958464 // arch = x86_64 // origin = busybox // commit = 332d2fff53cd4537d415e15e55e8ceb6fe6eaedb // maintainer = Sören Tempel // provider_priority = 100 // license = GPL-2.0-only // replaces = busybox-initscripts // provides = /bin/sh // triggers = /bin /usr/bin /sbin /usr/sbin /lib/modules/* // # automatically detected: // provides = cmd:busybox=1.35.0-r18 // provides = cmd:sh=1.35.0-r18 // depend = so:libc.musl-x86_64.so.1 // datahash = 7d3351ac6c3ebaf18182efb5390061f50d077ce5ade60a15909d91278f70ada7 func createPkgInfoFileContents(ff *fuzz.ConsumeFuzzer) ([]byte, error) { var b strings.Builder noOfRows, err := ff.GetInt() if err != nil { return []byte(""), err } // Comments at the top of the pkginfo file header, err := ff.GetBytes() if err != nil { return []byte(""), err } b.Write(header) for i := 0; i < noOfRows; i++ { key, err := ff.GetBytes() if err != nil { return []byte(""), err } value, err := ff.GetBytes() if err != nil { return []byte(""), err } b.Write(key) b.Write([]byte(" = ")) b.Write(value) b.WriteString("\n") } return []byte(b.String()), nil } // Adds a .SIGN file to tarBytes func addSignFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) ([]*fuzz.TarFile, error) { SIGNFileContents, err := ff.GetBytes() if err != nil { return tarFiles, err } SIGNFileName, err := getSignFilename(ff) if err != nil { return tarFiles, err } signFile := &fuzz.TarFile{ Body: SIGNFileContents, Hdr: &tar.Header{ Name: SIGNFileName, Mode: 0644, Size: int64(len(SIGNFileContents)), Typeflag: tar.TypeReg, Gid: 0, Uid: 0, }, } tarFiles = append(tarFiles, signFile) return tarFiles, nil } // Allows the fuzzer to randomize whether a .SIGN file should // be added to tarBytes func shouldAddSignFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) bool { shouldRequireSIGNFile, err := ff.GetBool() if err != nil { return false } if shouldRequireSIGNFile { for _, tarFile := range tarFiles { if strings.HasPrefix(tarFile.Hdr.Name, ".SIGN") { return false } } return true } return false } // Allows the fuzzer to randomize whether a .PKGINFO file should // be added to tarBytes func shouldAddPkgInfoFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) bool { shouldRequirePKGINFOFile, err := ff.GetBool() if err != nil { return false } if shouldRequirePKGINFOFile { for _, tarFile := range tarFiles { if strings.HasPrefix(tarFile.Hdr.Name, ".PKGINFO") { return false } } return true } return false } // Adds the .PKGINFO file to the tar files func addPkgInfoFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) ([]*fuzz.TarFile, error) { tarFile := &fuzz.TarFile{} PKGINFOFileContents, err := createPkgInfoFileContents(ff) if err != nil { return tarFiles, err } tarFile.Body = PKGINFOFileContents tarFile.Hdr = &tar.Header{ Name: ".PKGINFO", Mode: 0644, Size: int64(len(PKGINFOFileContents)), Typeflag: tar.TypeReg, Gid: 0, Uid: 0, } return tarFiles, nil } func AlpineArtifactBytes(ff *fuzz.ConsumeFuzzer) ([]byte, error) { var tarFiles, tarFiles2 []*fuzz.TarFile var err error tarFiles, err = ff.TarFiles() if err != nil { return []byte(""), err } if shouldAddSignFile(ff, tarFiles) { tarFiles, err = addSignFile(ff, tarFiles) if err != nil { return []byte(""), err } } tarFiles2, err = ff.TarFiles() if err != nil { return []byte(""), err } if shouldAddPkgInfoFile(ff, tarFiles2) { tarFiles2, err = addPkgInfoFile(ff, tarFiles2) if err != nil { return []byte(""), err } } return concatenateTarArchives(tarFiles, tarFiles2) } func concatenateTarArchives(tarFiles1 []*fuzz.TarFile, tarFiles2 []*fuzz.TarFile) ([]byte, error) { var buf1, buf2 bytes.Buffer var err error tw1 := tar.NewWriter(&buf1) for _, tf := range tarFiles1 { err = tw1.WriteHeader(tf.Hdr) if err != nil { return []byte(""), err } _, err = tw1.Write(tf.Body) if err != nil { return []byte(""), err } } tw1.Close() tarBytes := buf1.Bytes() tw2 := tar.NewWriter(&buf2) for _, tf := range tarFiles2 { err = tw2.WriteHeader(tf.Hdr) if err != nil { return []byte(""), err } _, err = tw2.Write(tf.Body) if err != nil { return []byte(""), err } } tw2.Close() tarBytes2 := buf2.Bytes() var b1 bytes.Buffer w1 := gzip.NewWriter(&b1) defer w1.Close() _, err = w1.Write(tarBytes) if err != nil { return []byte(""), err } w1.Close() var b2 bytes.Buffer w2 := gzip.NewWriter(&b2) defer w2.Close() _, err = w2.Write(tarBytes2) if err != nil { return []byte(""), err } w2.Close() concatenated := append(b1.Bytes(), b2.Bytes()...) return concatenated, nil } func setAlpineArtifactFields(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) (func(), error) { cleanup := func() {} err := setArtifactHash(ff, props) if err != nil { return cleanup, err } artifactBytes, err := AlpineArtifactBytes(ff) if err != nil { return cleanup, err } shouldSetArtifactBytes, err := ff.GetBool() if err != nil { return cleanup, err } if shouldSetArtifactBytes { props.ArtifactBytes = artifactBytes return func() { // do nothing }, nil } artifactFile, err := createAbsFile(ff, "ArtifactFile", artifactBytes) cleanup = func() { os.Remove("ArtifactFile") } props.ArtifactPath = artifactFile return cleanup, err } // Creates an ArtifactProperties with values determined by the fuzzer func CreateAlpineProps(ff *fuzz.ConsumeFuzzer) (types.ArtifactProperties, func(), error) { props := &types.ArtifactProperties{} cleanupArtifactFile, err := setAlpineArtifactFields(ff, props) if err != nil { return *props, cleanupArtifactFile, err } if props.ArtifactPath == nil && props.ArtifactBytes == nil { return *props, cleanupArtifactFile, fmt.Errorf("ArtifactPath and ArtifactBytes cannot both be nil") } err = setAdditionalAuthenticatedData(ff, props) if err != nil { return *props, cleanupArtifactFile, fmt.Errorf("Failed setting AdditionalAuthenticatedData") } cleanupSignatureFile, err := setSignatureFields(ff, props) if err != nil { return *props, func() { cleanupArtifactFile() cleanupSignatureFile() }, fmt.Errorf("failed setting signature fields: %v", err) } cleanupPublicKeyFile, err := setPublicKeyFields(ff, props) if err != nil { return *props, func() { cleanupArtifactFile() cleanupSignatureFile() cleanupPublicKeyFile() }, fmt.Errorf("failed setting public key fields: %v", err) } err = setPKIFormat(ff, props) if err != nil { return *props, func() { cleanupArtifactFile() cleanupSignatureFile() cleanupPublicKeyFile() }, fmt.Errorf("failed setting PKI Format: %v", err) } return *props, func() { cleanupArtifactFile() cleanupSignatureFile() cleanupPublicKeyFile() }, nil } rekor-1.3.5/pkg/fuzz/fuzz_utils.go000066400000000000000000000157611455727245600172210ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package fuzz import ( "archive/zip" "bytes" "fmt" "net/url" "os" "path/filepath" "go.uber.org/zap" "go.uber.org/zap/zapcore" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/types" ) // Sets ArtifactHash func setArtifactHash(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) error { artifactHash, err := ff.GetString() if err != nil { return err } props.ArtifactHash = artifactHash return nil } // creates a file on disk and returns the url of it. func createAbsFile(_ *fuzz.ConsumeFuzzer, fileName string, fileContents []byte) (*url.URL, error) { file, err := os.Create(fileName) if err != nil { return nil, err } defer file.Close() filePath, err := filepath.Abs(fileName) if err != nil { return nil, err } fileURL, err := url.Parse(filePath) if err != nil { return nil, err } _, err = file.Write(fileContents) if err != nil { return nil, err } return fileURL, err } // Sets the signature fields of a props. // It either sets SignatureBytes or SignaturePath func setSignatureFields(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) (func(), error) { cleanup := func() {} shouldSetSignatureBytes, err := ff.GetBool() if err != nil { return cleanup, err } signatureBytes, err := ff.GetBytes() if err != nil { return cleanup, err } if shouldSetSignatureBytes { props.SignatureBytes = signatureBytes return cleanup, nil } signatureURL, err := createAbsFile(ff, "SignatureFile", signatureBytes) if err != nil { os.Remove("SignatureFile") return cleanup, err } props.SignaturePath = signatureURL return func() { os.Remove("SignatureFile") }, nil } func setPublicKeyFields(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) (func(), error) { cleanup := func() {} shouldSetPublicKeyBytes, err := ff.GetBool() if err != nil { return cleanup, err } if shouldSetPublicKeyBytes { publicKeyBytes := make([][]byte, 0) err := ff.GenerateStruct(&publicKeyBytes) if err != nil || len(publicKeyBytes) == 0 { return cleanup, err } props.PublicKeyBytes = publicKeyBytes return cleanup, nil } publicKeyBytes, err := ff.GetBytes() if err != nil { return cleanup, err } publicKeyURL, err := createAbsFile(ff, "PublicKeyFile", publicKeyBytes) if err != nil { os.Remove("PublicKeyFile") return cleanup, err } props.PublicKeyPaths = []*url.URL{publicKeyURL} return func() { os.Remove("PublicKeyFile") }, nil } // Sets the "AdditionalAuthenticatedData" field of the props func setAdditionalAuthenticatedData(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) error { shouldSetAdditionalAuthenticatedData, err := ff.GetBool() if err != nil { return err } if shouldSetAdditionalAuthenticatedData { additionalAuthenticatedData, err := ff.GetBytes() if err != nil { return err } props.AdditionalAuthenticatedData = additionalAuthenticatedData } return nil } // Sets the PKI format if the fuzzer decides to. func setPKIFormat(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) error { shouldSetPKIFormat, err := ff.GetBool() if err != nil { return err } if shouldSetPKIFormat { pkiFormat, err := ff.GetString() if err != nil { return err } props.PKIFormat = pkiFormat } return nil } func createArtifactFiles(ff *fuzz.ConsumeFuzzer, artifactType string) ([]*fuzz.TarFile, error) { switch artifactType { case "jarV001": return createJarArtifactFiles(ff) default: return createDefaultArtifactFiles(ff) } } func createDefaultArtifactFiles(ff *fuzz.ConsumeFuzzer) ([]*fuzz.TarFile, error) { var files []*fuzz.TarFile files, err := ff.TarFiles() if err != nil { return files, err } if len(files) <= 1 { return files, err } for _, file := range files { if len(file.Body) == 0 { return files, fmt.Errorf("Created an empty file") } } return files, nil } // Creates an ArtifactProperties with values determined by the fuzzer func CreateProps(ff *fuzz.ConsumeFuzzer, fuzzType string) (types.ArtifactProperties, []func(), error) { var cleanups []func() props := &types.ArtifactProperties{} err := setArtifactHash(ff, props) if err != nil { return *props, cleanups, err } artifactFiles, err := createArtifactFiles(ff, fuzzType) if err != nil { return *props, cleanups, err } err = setAdditionalAuthenticatedData(ff, props) if err != nil { return *props, cleanups, fmt.Errorf("Failed setting AdditionalAuthenticatedData") } cleanupSignatureFile, err := setSignatureFields(ff, props) if err != nil { return *props, cleanups, fmt.Errorf("failed setting signature fields: %v", err) } cleanups = append(cleanups, cleanupSignatureFile) cleanupPublicKeyFile, err := setPublicKeyFields(ff, props) if err != nil { return *props, cleanups, fmt.Errorf("failed setting public key fields: %v", err) } cleanups = append(cleanups, cleanupPublicKeyFile) err = setPKIFormat(ff, props) if err != nil { return *props, cleanups, fmt.Errorf("failed setting PKI Format: %v", err) } artifactBytes, err := tarFilesToBytes(artifactFiles, fuzzType) if err != nil { return *props, cleanups, fmt.Errorf("failed converting artifact bytes: %v", err) } setArtifactBytes, err := ff.GetBool() if err != nil { return *props, cleanups, fmt.Errorf("failed converting artifact bytes: %v", err) } if setArtifactBytes { props.ArtifactBytes = artifactBytes } else { artifactFile, err := createAbsFile(ff, "ArtifactFile", artifactBytes) cleanups = append(cleanups, func() { os.Remove("ArtifactFile") }) if err != nil { return *props, cleanups, fmt.Errorf("failed converting artifact bytes: %v", err) } props.ArtifactPath = artifactFile } props.ArtifactBytes = artifactBytes return *props, cleanups, nil } func tarFilesToBytes(artifactFiles []*fuzz.TarFile, artifactType string) ([]byte, error) { switch artifactType { case "jarV001": return tarfilesToJar(artifactFiles) default: return defaultTarToBytes(artifactFiles) } } func defaultTarToBytes(artifactFiles []*fuzz.TarFile) ([]byte, error) { b := new(bytes.Buffer) w := zip.NewWriter(b) for _, file := range artifactFiles { f, err := w.Create(file.Hdr.Name) if err != nil { continue } _, _ = f.Write(file.Body) } w.Close() return b.Bytes(), nil } func SetFuzzLogger() { config := zap.NewProductionConfig() config.Level = zap.NewAtomicLevelAt(zapcore.FatalLevel) logger, err := config.Build() if err != nil { panic(err) } log.Logger = logger.Named("rekor-fuzz-logger").Sugar() } rekor-1.3.5/pkg/fuzz/jar_utils.go000066400000000000000000000146201455727245600167700ustar00rootroot00000000000000// // Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package fuzz import ( "archive/tar" "archive/zip" "bytes" "context" "crypto" "crypto/rsa" "crypto/x509" "encoding/pem" "fmt" "os" "time" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/sassoftware/relic/lib/zipslicer" "github.com/sassoftware/relic/v7/lib/certloader" "github.com/sassoftware/relic/v7/lib/signjar" ) var ( CertPrivateKey *rsa.PrivateKey Certificate *x509.Certificate ) // copy pasted from rekor/pkg/pki/x509/e2e.go const RSACert = `-----BEGIN CERTIFICATE----- MIIDOjCCAiKgAwIBAgIUEP925shVBKERFCsymdSqESLZFyMwDQYJKoZIhvcNAQEL BQAwHzEdMBsGCSqGSIb3DQEJARYOdGVzdEByZWtvci5kZXYwHhcNMjEwNDIxMjAy ODAzWhcNMjEwNTIxMjAyODAzWjAfMR0wGwYJKoZIhvcNAQkBFg50ZXN0QHJla29y LmRldjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN8KiP08rFIik4GN W8/sHSXxDopeDBLEQEihsyXXWesfYW/q59lFaCZrsTetlyNEzKDJ+JrpIHwoGOo4 EwefFfvy2nkgPFs9aeIDsYZNZnIGxeB8sUfsZUYGHx+Ikm18vhM//GYzNjjuvHyq +CWRAOS12ZISa99iah/lIhcP8IEj1gPGldAH0QFx3XpCePAdQocSU6ziVkj054/x NJXy1bKySrVw7gvE9LxZlVO9urSOnzg7BBOla0mob8NRDVB8yN+LG365q4IMDzuI jAEL6sLtoJ9pcemo1rIfNOhSLYlzfg7oszJ8eCjASNCCcp6EKVjhW7LRoldC8oGZ EOrKM78CAwEAAaNuMGwwHQYDVR0OBBYEFGjs8EHKT3x1itwwptJLuQQg/hQcMB8G A1UdIwQYMBaAFGjs8EHKT3x1itwwptJLuQQg/hQcMA8GA1UdEwEB/wQFMAMBAf8w GQYDVR0RBBIwEIEOdGVzdEByZWtvci5kZXYwDQYJKoZIhvcNAQELBQADggEBAAHE bYuePN3XpM7pHoCz6g4uTHu0VrezqJyK1ohysgWJmSJzzazUeISXk0xWnHPk1Zxi kzoEuysI8b0P7yodMA8e16zbIOL6QbGe3lNXYqRIg+bl+4OPFGVMX8xHNZmeh0kD vX1JVS+y9uyo4/z/pm0JhaSCn85ft/Y5uXMQYn1wFR5DAcJH+iWjNX4fipGxGRE9 Cy0DjFnYJ3SRY4HPQ0oUSQmyhrwe2DiYzeqtbL2KJBXPcFQKWhkf/fupdYFljvcH d9NNfRb0p2oFGG/J0ROg9pEcP1/aZP5k8P2pRdt3y7h1MAtmg2bgEdugZgXwAUmM BmU8k2FeTuqV15piPCE= -----END CERTIFICATE-----` // copy pasted from rekor/pkg/pki/x509/e2e.go const RSAKey = `-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDfCoj9PKxSIpOB jVvP7B0l8Q6KXgwSxEBIobMl11nrH2Fv6ufZRWgma7E3rZcjRMygyfia6SB8KBjq OBMHnxX78tp5IDxbPWniA7GGTWZyBsXgfLFH7GVGBh8fiJJtfL4TP/xmMzY47rx8 qvglkQDktdmSEmvfYmof5SIXD/CBI9YDxpXQB9EBcd16QnjwHUKHElOs4lZI9OeP 8TSV8tWyskq1cO4LxPS8WZVTvbq0jp84OwQTpWtJqG/DUQ1QfMjfixt+uauCDA87 iIwBC+rC7aCfaXHpqNayHzToUi2Jc34O6LMyfHgowEjQgnKehClY4Vuy0aJXQvKB mRDqyjO/AgMBAAECggEBAIHOAs3Gis8+WjRSjXVjh882DG1QsJwXZQYgPT+vpiAl YjKdNpOHRkbd9ARgXY5kEuccxDd7p7E6MM3XFpQf7M51ltpZfWboRgAIgD+WOiHw eSbdytr95C6tj11twTJBH+naGk1sTokxv7aaVdKfIjL49oeBexBFmVe4pW9gkmrE 1z1y1a0RohqbZ0kprYPWjz5UhsNqbCzgkdDqS7IrcOwVg6zvKYFjHnqIHqaJXVif FgIfoNt7tz+12FTHI+6OkKoN3YCJueaxneBhITXm6RLOpQWa9qhdUPbkJ9vQNfph Qqke4faaxKY9UDma+GpEHR016AWufZp92pd9wQkDn0kCgYEA7w/ZizAkefHoZhZ8 Isn/fYu4fdtUaVgrnGUVZobiGxWrHRU9ikbAwR7UwbgRSfppGiJdAMq1lyH2irmb 4OHU64rjuYSlIqUWHLQHWmqUbLUvlDojH/vdmH/Zn0AbrLZaimC5UCjK3Eb7sAMq G0tGeDX2JraQvx7KrbC6peTaaaMCgYEA7tgZBiRCQJ7+mNu+gX9x6OXtjsDCh516 vToRLkxWc7LAbC9LKsuEHl4e3vy1PY/nyuv12Ng2dBq4WDXozAmVgz0ok7rRlIFp w8Yj8o/9KuGZkD/7tw/pLsVc9Q3Wf0ACrnAAh7+3dAvn3yg+WHwXzqWIbrseDPt9 ILCfUoNDpzUCgYAKFCX8y0PObFd67lm/cbq2xUw66iNN6ay1BEH5t5gSwkAbksis ar03pyAbJrJ75vXFZ0t6fBFZ1NG7GYYr3fmHEKz3JlN7+W/MN/7TXgjx6FWgLy9J 6ul1w3YeU6qXBn0ctmU5ru6WiNuVmRyOWAcZjFTbXvkNRbQPzJKh6dsXdwKBgA1D FIihxMf/zBVCxl48bF/JPJqbm3GaTfFp4wBWHsrH1yVqrtrOeCSTh1VMZOfpMK60 0W7b+pIR1cCYJbgGpDWoVLN3QSHk2bGUM/TJB/60jilTVC/DA2ikbtfwj8N7E2sK Lw1amN4ptxNOEcAqC8xepqe3XiDMahNBm2cigMQtAoGBAKwrXvss2BKz+/6poJQU A0c7jhMN8M9Y5S2Ockw07lrQeAgfu4q+/8ztm0NeHJbk01IJvJY5Nt7bSgwgNVlo j7vR2BMAc9U73Ju9aeTl/L6GqmZyA+Ojhl5gA5DPZYqNiqi93ydgRaI6n4+o3dI7 5wnr40AmbuKCDvMOvN7nMybL -----END PRIVATE KEY-----` // copy pasted from rekor/pkg/pki/x509/e2e.go func init() { p, _ := pem.Decode([]byte(RSAKey)) priv, err := x509.ParsePKCS8PrivateKey(p.Bytes) if err != nil { panic(err) } cpk, ok := priv.(*rsa.PrivateKey) if !ok { panic("unsuccessful conversion") } CertPrivateKey = cpk p, _ = pem.Decode([]byte(RSACert)) Certificate, err = x509.ParseCertificate(p.Bytes) if err != nil { panic(err) } } // Creates jar artifact files. func createJarArtifactFiles(ff *fuzz.ConsumeFuzzer) ([]*fuzz.TarFile, error) { var files []*fuzz.TarFile files, err := ff.TarFiles() if err != nil { return files, err } if len(files) <= 1 { return files, err } for _, file := range files { if len(file.Body) == 0 { return files, fmt.Errorf("Created an empty file") } } // add "META-INF/MANIFEST.MF" mfContents, err := ff.GetBytes() if err != nil { return files, err } // check the manifest early. This is an inexpensive check, // so we want to call it before compressing. _, err = signjar.ParseManifest(mfContents) if err != nil { return files, err } files = append(files, &fuzz.TarFile{ Hdr: &tar.Header{ Name: "META-INF/MANIFEST.MF", Size: int64(len(mfContents)), Mode: 0o600, ModTime: time.Unix(int64(123), int64(456)), }, Body: mfContents, }) return files, nil } func tarfilesToJar(artifactFiles []*fuzz.TarFile) ([]byte, error) { var jarBytes []byte f, err := os.Create("artifactFile") if err != nil { return jarBytes, err } defer f.Close() defer os.Remove("artifactFile") zw := zip.NewWriter(f) for _, zipFile := range artifactFiles { jw, err := zw.Create(zipFile.Hdr.Name) if err != nil { zw.Close() return jarBytes, err } _, err = jw.Write(zipFile.Body) if err != nil { continue } } zw.Close() err = f.Sync() if err != nil { return jarBytes, err } buf := bytes.Buffer{} err = zipslicer.ZipToTar(f, &buf) if err != nil { return jarBytes, err } jd, err := signjar.DigestJarStream(&buf, crypto.SHA256) if err != nil { os.Remove("artifactFile") return jarBytes, err } c := certloader.Certificate{ PrivateKey: CertPrivateKey, Leaf: Certificate, } patch, _, err := jd.Sign(context.Background(), &c, "rekor", false, true, false) if err != nil { return jarBytes, err } if err := patch.Apply(f, "artifactFile"); err != nil { return jarBytes, err } f.Close() artifactBytes, err := os.ReadFile("artifactFile") if err != nil { return jarBytes, err } return artifactBytes, nil } rekor-1.3.5/pkg/generated/000077500000000000000000000000001455727245600154025ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/client/000077500000000000000000000000001455727245600166605ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/client/entries/000077500000000000000000000000001455727245600203315ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/client/entries/create_log_entry_parameters.go000066400000000000000000000116171455727245600264360ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "net/http" "time" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" ) // NewCreateLogEntryParams creates a new CreateLogEntryParams object, // with the default timeout for this client. // // Default values are not hydrated, since defaults are normally applied by the API server side. // // To enforce default values in parameter, use SetDefaults or WithDefaults. func NewCreateLogEntryParams() *CreateLogEntryParams { return &CreateLogEntryParams{ timeout: cr.DefaultTimeout, } } // NewCreateLogEntryParamsWithTimeout creates a new CreateLogEntryParams object // with the ability to set a timeout on a request. func NewCreateLogEntryParamsWithTimeout(timeout time.Duration) *CreateLogEntryParams { return &CreateLogEntryParams{ timeout: timeout, } } // NewCreateLogEntryParamsWithContext creates a new CreateLogEntryParams object // with the ability to set a context for a request. func NewCreateLogEntryParamsWithContext(ctx context.Context) *CreateLogEntryParams { return &CreateLogEntryParams{ Context: ctx, } } // NewCreateLogEntryParamsWithHTTPClient creates a new CreateLogEntryParams object // with the ability to set a custom HTTPClient for a request. func NewCreateLogEntryParamsWithHTTPClient(client *http.Client) *CreateLogEntryParams { return &CreateLogEntryParams{ HTTPClient: client, } } /* CreateLogEntryParams contains all the parameters to send to the API endpoint for the create log entry operation. Typically these are written to a http.Request. */ type CreateLogEntryParams struct { // ProposedEntry. ProposedEntry models.ProposedEntry timeout time.Duration Context context.Context HTTPClient *http.Client } // WithDefaults hydrates default values in the create log entry params (not the query body). // // All values with no default are reset to their zero value. func (o *CreateLogEntryParams) WithDefaults() *CreateLogEntryParams { o.SetDefaults() return o } // SetDefaults hydrates default values in the create log entry params (not the query body). // // All values with no default are reset to their zero value. func (o *CreateLogEntryParams) SetDefaults() { // no default values defined for this parameter } // WithTimeout adds the timeout to the create log entry params func (o *CreateLogEntryParams) WithTimeout(timeout time.Duration) *CreateLogEntryParams { o.SetTimeout(timeout) return o } // SetTimeout adds the timeout to the create log entry params func (o *CreateLogEntryParams) SetTimeout(timeout time.Duration) { o.timeout = timeout } // WithContext adds the context to the create log entry params func (o *CreateLogEntryParams) WithContext(ctx context.Context) *CreateLogEntryParams { o.SetContext(ctx) return o } // SetContext adds the context to the create log entry params func (o *CreateLogEntryParams) SetContext(ctx context.Context) { o.Context = ctx } // WithHTTPClient adds the HTTPClient to the create log entry params func (o *CreateLogEntryParams) WithHTTPClient(client *http.Client) *CreateLogEntryParams { o.SetHTTPClient(client) return o } // SetHTTPClient adds the HTTPClient to the create log entry params func (o *CreateLogEntryParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } // WithProposedEntry adds the proposedEntry to the create log entry params func (o *CreateLogEntryParams) WithProposedEntry(proposedEntry models.ProposedEntry) *CreateLogEntryParams { o.SetProposedEntry(proposedEntry) return o } // SetProposedEntry adds the proposedEntry to the create log entry params func (o *CreateLogEntryParams) SetProposedEntry(proposedEntry models.ProposedEntry) { o.ProposedEntry = proposedEntry } // WriteToRequest writes these params to a swagger request func (o *CreateLogEntryParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { if err := r.SetTimeout(o.timeout); err != nil { return err } var res []error if err := r.SetBodyParam(o.ProposedEntry); err != nil { return err } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } rekor-1.3.5/pkg/generated/client/entries/create_log_entry_responses.go000066400000000000000000000263021455727245600263110ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "fmt" "io" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" ) // CreateLogEntryReader is a Reader for the CreateLogEntry structure. type CreateLogEntryReader struct { formats strfmt.Registry } // ReadResponse reads a server response into the received o. func (o *CreateLogEntryReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { switch response.Code() { case 201: result := NewCreateLogEntryCreated() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return result, nil case 400: result := NewCreateLogEntryBadRequest() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return nil, result case 409: result := NewCreateLogEntryConflict() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return nil, result default: result := NewCreateLogEntryDefault(response.Code()) if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } if response.Code()/100 == 2 { return result, nil } return nil, result } } // NewCreateLogEntryCreated creates a CreateLogEntryCreated with default headers values func NewCreateLogEntryCreated() *CreateLogEntryCreated { return &CreateLogEntryCreated{} } /* CreateLogEntryCreated describes a response with status code 201, with default header values. Returns the entry created in the transparency log */ type CreateLogEntryCreated struct { /* UUID of log entry */ ETag string /* URI location of log entry Format: uri */ Location strfmt.URI Payload models.LogEntry } // IsSuccess returns true when this create log entry created response has a 2xx status code func (o *CreateLogEntryCreated) IsSuccess() bool { return true } // IsRedirect returns true when this create log entry created response has a 3xx status code func (o *CreateLogEntryCreated) IsRedirect() bool { return false } // IsClientError returns true when this create log entry created response has a 4xx status code func (o *CreateLogEntryCreated) IsClientError() bool { return false } // IsServerError returns true when this create log entry created response has a 5xx status code func (o *CreateLogEntryCreated) IsServerError() bool { return false } // IsCode returns true when this create log entry created response a status code equal to that given func (o *CreateLogEntryCreated) IsCode(code int) bool { return code == 201 } // Code gets the status code for the create log entry created response func (o *CreateLogEntryCreated) Code() int { return 201 } func (o *CreateLogEntryCreated) Error() string { return fmt.Sprintf("[POST /api/v1/log/entries][%d] createLogEntryCreated %+v", 201, o.Payload) } func (o *CreateLogEntryCreated) String() string { return fmt.Sprintf("[POST /api/v1/log/entries][%d] createLogEntryCreated %+v", 201, o.Payload) } func (o *CreateLogEntryCreated) GetPayload() models.LogEntry { return o.Payload } func (o *CreateLogEntryCreated) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { // hydrates response header ETag hdrETag := response.GetHeader("ETag") if hdrETag != "" { o.ETag = hdrETag } // hydrates response header Location hdrLocation := response.GetHeader("Location") if hdrLocation != "" { vallocation, err := formats.Parse("uri", hdrLocation) if err != nil { return errors.InvalidType("Location", "header", "strfmt.URI", hdrLocation) } o.Location = *(vallocation.(*strfmt.URI)) } // response payload if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { return err } return nil } // NewCreateLogEntryBadRequest creates a CreateLogEntryBadRequest with default headers values func NewCreateLogEntryBadRequest() *CreateLogEntryBadRequest { return &CreateLogEntryBadRequest{} } /* CreateLogEntryBadRequest describes a response with status code 400, with default header values. The content supplied to the server was invalid */ type CreateLogEntryBadRequest struct { Payload *models.Error } // IsSuccess returns true when this create log entry bad request response has a 2xx status code func (o *CreateLogEntryBadRequest) IsSuccess() bool { return false } // IsRedirect returns true when this create log entry bad request response has a 3xx status code func (o *CreateLogEntryBadRequest) IsRedirect() bool { return false } // IsClientError returns true when this create log entry bad request response has a 4xx status code func (o *CreateLogEntryBadRequest) IsClientError() bool { return true } // IsServerError returns true when this create log entry bad request response has a 5xx status code func (o *CreateLogEntryBadRequest) IsServerError() bool { return false } // IsCode returns true when this create log entry bad request response a status code equal to that given func (o *CreateLogEntryBadRequest) IsCode(code int) bool { return code == 400 } // Code gets the status code for the create log entry bad request response func (o *CreateLogEntryBadRequest) Code() int { return 400 } func (o *CreateLogEntryBadRequest) Error() string { return fmt.Sprintf("[POST /api/v1/log/entries][%d] createLogEntryBadRequest %+v", 400, o.Payload) } func (o *CreateLogEntryBadRequest) String() string { return fmt.Sprintf("[POST /api/v1/log/entries][%d] createLogEntryBadRequest %+v", 400, o.Payload) } func (o *CreateLogEntryBadRequest) GetPayload() *models.Error { return o.Payload } func (o *CreateLogEntryBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } // NewCreateLogEntryConflict creates a CreateLogEntryConflict with default headers values func NewCreateLogEntryConflict() *CreateLogEntryConflict { return &CreateLogEntryConflict{} } /* CreateLogEntryConflict describes a response with status code 409, with default header values. The request conflicts with the current state of the transparency log */ type CreateLogEntryConflict struct { Location strfmt.URI Payload *models.Error } // IsSuccess returns true when this create log entry conflict response has a 2xx status code func (o *CreateLogEntryConflict) IsSuccess() bool { return false } // IsRedirect returns true when this create log entry conflict response has a 3xx status code func (o *CreateLogEntryConflict) IsRedirect() bool { return false } // IsClientError returns true when this create log entry conflict response has a 4xx status code func (o *CreateLogEntryConflict) IsClientError() bool { return true } // IsServerError returns true when this create log entry conflict response has a 5xx status code func (o *CreateLogEntryConflict) IsServerError() bool { return false } // IsCode returns true when this create log entry conflict response a status code equal to that given func (o *CreateLogEntryConflict) IsCode(code int) bool { return code == 409 } // Code gets the status code for the create log entry conflict response func (o *CreateLogEntryConflict) Code() int { return 409 } func (o *CreateLogEntryConflict) Error() string { return fmt.Sprintf("[POST /api/v1/log/entries][%d] createLogEntryConflict %+v", 409, o.Payload) } func (o *CreateLogEntryConflict) String() string { return fmt.Sprintf("[POST /api/v1/log/entries][%d] createLogEntryConflict %+v", 409, o.Payload) } func (o *CreateLogEntryConflict) GetPayload() *models.Error { return o.Payload } func (o *CreateLogEntryConflict) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { // hydrates response header Location hdrLocation := response.GetHeader("Location") if hdrLocation != "" { vallocation, err := formats.Parse("uri", hdrLocation) if err != nil { return errors.InvalidType("Location", "header", "strfmt.URI", hdrLocation) } o.Location = *(vallocation.(*strfmt.URI)) } o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } // NewCreateLogEntryDefault creates a CreateLogEntryDefault with default headers values func NewCreateLogEntryDefault(code int) *CreateLogEntryDefault { return &CreateLogEntryDefault{ _statusCode: code, } } /* CreateLogEntryDefault describes a response with status code -1, with default header values. There was an internal error in the server while processing the request */ type CreateLogEntryDefault struct { _statusCode int Payload *models.Error } // IsSuccess returns true when this create log entry default response has a 2xx status code func (o *CreateLogEntryDefault) IsSuccess() bool { return o._statusCode/100 == 2 } // IsRedirect returns true when this create log entry default response has a 3xx status code func (o *CreateLogEntryDefault) IsRedirect() bool { return o._statusCode/100 == 3 } // IsClientError returns true when this create log entry default response has a 4xx status code func (o *CreateLogEntryDefault) IsClientError() bool { return o._statusCode/100 == 4 } // IsServerError returns true when this create log entry default response has a 5xx status code func (o *CreateLogEntryDefault) IsServerError() bool { return o._statusCode/100 == 5 } // IsCode returns true when this create log entry default response a status code equal to that given func (o *CreateLogEntryDefault) IsCode(code int) bool { return o._statusCode == code } // Code gets the status code for the create log entry default response func (o *CreateLogEntryDefault) Code() int { return o._statusCode } func (o *CreateLogEntryDefault) Error() string { return fmt.Sprintf("[POST /api/v1/log/entries][%d] createLogEntry default %+v", o._statusCode, o.Payload) } func (o *CreateLogEntryDefault) String() string { return fmt.Sprintf("[POST /api/v1/log/entries][%d] createLogEntry default %+v", o._statusCode, o.Payload) } func (o *CreateLogEntryDefault) GetPayload() *models.Error { return o.Payload } func (o *CreateLogEntryDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } rekor-1.3.5/pkg/generated/client/entries/entries_client.go000066400000000000000000000161641455727245600236770ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" ) // New creates a new entries API client. func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService { return &Client{transport: transport, formats: formats} } /* Client for entries API */ type Client struct { transport runtime.ClientTransport formats strfmt.Registry } // ClientOption is the option for Client methods type ClientOption func(*runtime.ClientOperation) // ClientService is the interface for Client methods type ClientService interface { CreateLogEntry(params *CreateLogEntryParams, opts ...ClientOption) (*CreateLogEntryCreated, error) GetLogEntryByIndex(params *GetLogEntryByIndexParams, opts ...ClientOption) (*GetLogEntryByIndexOK, error) GetLogEntryByUUID(params *GetLogEntryByUUIDParams, opts ...ClientOption) (*GetLogEntryByUUIDOK, error) SearchLogQuery(params *SearchLogQueryParams, opts ...ClientOption) (*SearchLogQueryOK, error) SetTransport(transport runtime.ClientTransport) } /* CreateLogEntry creates an entry in the transparency log Creates an entry in the transparency log for a detached signature, public key, and content. Items can be included in the request or fetched by the server when URLs are specified. */ func (a *Client) CreateLogEntry(params *CreateLogEntryParams, opts ...ClientOption) (*CreateLogEntryCreated, error) { // TODO: Validate the params before sending if params == nil { params = NewCreateLogEntryParams() } op := &runtime.ClientOperation{ ID: "createLogEntry", Method: "POST", PathPattern: "/api/v1/log/entries", ProducesMediaTypes: []string{"application/json"}, ConsumesMediaTypes: []string{"application/json"}, Schemes: []string{"http"}, Params: params, Reader: &CreateLogEntryReader{formats: a.formats}, Context: params.Context, Client: params.HTTPClient, } for _, opt := range opts { opt(op) } result, err := a.transport.Submit(op) if err != nil { return nil, err } success, ok := result.(*CreateLogEntryCreated) if ok { return success, nil } // unexpected success response unexpectedSuccess := result.(*CreateLogEntryDefault) return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } /* GetLogEntryByIndex retrieves an entry and inclusion proof from the transparency log if it exists by index */ func (a *Client) GetLogEntryByIndex(params *GetLogEntryByIndexParams, opts ...ClientOption) (*GetLogEntryByIndexOK, error) { // TODO: Validate the params before sending if params == nil { params = NewGetLogEntryByIndexParams() } op := &runtime.ClientOperation{ ID: "getLogEntryByIndex", Method: "GET", PathPattern: "/api/v1/log/entries", ProducesMediaTypes: []string{"application/json"}, ConsumesMediaTypes: []string{"application/json"}, Schemes: []string{"http"}, Params: params, Reader: &GetLogEntryByIndexReader{formats: a.formats}, Context: params.Context, Client: params.HTTPClient, } for _, opt := range opts { opt(op) } result, err := a.transport.Submit(op) if err != nil { return nil, err } success, ok := result.(*GetLogEntryByIndexOK) if ok { return success, nil } // unexpected success response unexpectedSuccess := result.(*GetLogEntryByIndexDefault) return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } /* GetLogEntryByUUID gets log entry and information required to generate an inclusion proof for the entry in the transparency log Returns the entry, root hash, tree size, and a list of hashes that can be used to calculate proof of an entry being included in the transparency log */ func (a *Client) GetLogEntryByUUID(params *GetLogEntryByUUIDParams, opts ...ClientOption) (*GetLogEntryByUUIDOK, error) { // TODO: Validate the params before sending if params == nil { params = NewGetLogEntryByUUIDParams() } op := &runtime.ClientOperation{ ID: "getLogEntryByUUID", Method: "GET", PathPattern: "/api/v1/log/entries/{entryUUID}", ProducesMediaTypes: []string{"application/json"}, ConsumesMediaTypes: []string{"application/json"}, Schemes: []string{"http"}, Params: params, Reader: &GetLogEntryByUUIDReader{formats: a.formats}, Context: params.Context, Client: params.HTTPClient, } for _, opt := range opts { opt(op) } result, err := a.transport.Submit(op) if err != nil { return nil, err } success, ok := result.(*GetLogEntryByUUIDOK) if ok { return success, nil } // unexpected success response unexpectedSuccess := result.(*GetLogEntryByUUIDDefault) return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } /* SearchLogQuery searches transparency log for one or more log entries */ func (a *Client) SearchLogQuery(params *SearchLogQueryParams, opts ...ClientOption) (*SearchLogQueryOK, error) { // TODO: Validate the params before sending if params == nil { params = NewSearchLogQueryParams() } op := &runtime.ClientOperation{ ID: "searchLogQuery", Method: "POST", PathPattern: "/api/v1/log/entries/retrieve", ProducesMediaTypes: []string{"application/json"}, ConsumesMediaTypes: []string{"application/json"}, Schemes: []string{"http"}, Params: params, Reader: &SearchLogQueryReader{formats: a.formats}, Context: params.Context, Client: params.HTTPClient, } for _, opt := range opts { opt(op) } result, err := a.transport.Submit(op) if err != nil { return nil, err } success, ok := result.(*SearchLogQueryOK) if ok { return success, nil } // unexpected success response unexpectedSuccess := result.(*SearchLogQueryDefault) return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } // SetTransport changes the transport on the client func (a *Client) SetTransport(transport runtime.ClientTransport) { a.transport = transport } rekor-1.3.5/pkg/generated/client/entries/get_log_entry_by_index_parameters.go000066400000000000000000000122661455727245600276340ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "net/http" "time" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" ) // NewGetLogEntryByIndexParams creates a new GetLogEntryByIndexParams object, // with the default timeout for this client. // // Default values are not hydrated, since defaults are normally applied by the API server side. // // To enforce default values in parameter, use SetDefaults or WithDefaults. func NewGetLogEntryByIndexParams() *GetLogEntryByIndexParams { return &GetLogEntryByIndexParams{ timeout: cr.DefaultTimeout, } } // NewGetLogEntryByIndexParamsWithTimeout creates a new GetLogEntryByIndexParams object // with the ability to set a timeout on a request. func NewGetLogEntryByIndexParamsWithTimeout(timeout time.Duration) *GetLogEntryByIndexParams { return &GetLogEntryByIndexParams{ timeout: timeout, } } // NewGetLogEntryByIndexParamsWithContext creates a new GetLogEntryByIndexParams object // with the ability to set a context for a request. func NewGetLogEntryByIndexParamsWithContext(ctx context.Context) *GetLogEntryByIndexParams { return &GetLogEntryByIndexParams{ Context: ctx, } } // NewGetLogEntryByIndexParamsWithHTTPClient creates a new GetLogEntryByIndexParams object // with the ability to set a custom HTTPClient for a request. func NewGetLogEntryByIndexParamsWithHTTPClient(client *http.Client) *GetLogEntryByIndexParams { return &GetLogEntryByIndexParams{ HTTPClient: client, } } /* GetLogEntryByIndexParams contains all the parameters to send to the API endpoint for the get log entry by index operation. Typically these are written to a http.Request. */ type GetLogEntryByIndexParams struct { /* LogIndex. specifies the index of the entry in the transparency log to be retrieved */ LogIndex int64 timeout time.Duration Context context.Context HTTPClient *http.Client } // WithDefaults hydrates default values in the get log entry by index params (not the query body). // // All values with no default are reset to their zero value. func (o *GetLogEntryByIndexParams) WithDefaults() *GetLogEntryByIndexParams { o.SetDefaults() return o } // SetDefaults hydrates default values in the get log entry by index params (not the query body). // // All values with no default are reset to their zero value. func (o *GetLogEntryByIndexParams) SetDefaults() { // no default values defined for this parameter } // WithTimeout adds the timeout to the get log entry by index params func (o *GetLogEntryByIndexParams) WithTimeout(timeout time.Duration) *GetLogEntryByIndexParams { o.SetTimeout(timeout) return o } // SetTimeout adds the timeout to the get log entry by index params func (o *GetLogEntryByIndexParams) SetTimeout(timeout time.Duration) { o.timeout = timeout } // WithContext adds the context to the get log entry by index params func (o *GetLogEntryByIndexParams) WithContext(ctx context.Context) *GetLogEntryByIndexParams { o.SetContext(ctx) return o } // SetContext adds the context to the get log entry by index params func (o *GetLogEntryByIndexParams) SetContext(ctx context.Context) { o.Context = ctx } // WithHTTPClient adds the HTTPClient to the get log entry by index params func (o *GetLogEntryByIndexParams) WithHTTPClient(client *http.Client) *GetLogEntryByIndexParams { o.SetHTTPClient(client) return o } // SetHTTPClient adds the HTTPClient to the get log entry by index params func (o *GetLogEntryByIndexParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } // WithLogIndex adds the logIndex to the get log entry by index params func (o *GetLogEntryByIndexParams) WithLogIndex(logIndex int64) *GetLogEntryByIndexParams { o.SetLogIndex(logIndex) return o } // SetLogIndex adds the logIndex to the get log entry by index params func (o *GetLogEntryByIndexParams) SetLogIndex(logIndex int64) { o.LogIndex = logIndex } // WriteToRequest writes these params to a swagger request func (o *GetLogEntryByIndexParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { if err := r.SetTimeout(o.timeout); err != nil { return err } var res []error // query param logIndex qrLogIndex := o.LogIndex qLogIndex := swag.FormatInt64(qrLogIndex) if qLogIndex != "" { if err := r.SetQueryParam("logIndex", qLogIndex); err != nil { return err } } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } rekor-1.3.5/pkg/generated/client/entries/get_log_entry_by_index_responses.go000066400000000000000000000177621455727245600275200ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "fmt" "io" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" ) // GetLogEntryByIndexReader is a Reader for the GetLogEntryByIndex structure. type GetLogEntryByIndexReader struct { formats strfmt.Registry } // ReadResponse reads a server response into the received o. func (o *GetLogEntryByIndexReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { switch response.Code() { case 200: result := NewGetLogEntryByIndexOK() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return result, nil case 404: result := NewGetLogEntryByIndexNotFound() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return nil, result default: result := NewGetLogEntryByIndexDefault(response.Code()) if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } if response.Code()/100 == 2 { return result, nil } return nil, result } } // NewGetLogEntryByIndexOK creates a GetLogEntryByIndexOK with default headers values func NewGetLogEntryByIndexOK() *GetLogEntryByIndexOK { return &GetLogEntryByIndexOK{} } /* GetLogEntryByIndexOK describes a response with status code 200, with default header values. the entry in the transparency log requested along with an inclusion proof */ type GetLogEntryByIndexOK struct { Payload models.LogEntry } // IsSuccess returns true when this get log entry by index o k response has a 2xx status code func (o *GetLogEntryByIndexOK) IsSuccess() bool { return true } // IsRedirect returns true when this get log entry by index o k response has a 3xx status code func (o *GetLogEntryByIndexOK) IsRedirect() bool { return false } // IsClientError returns true when this get log entry by index o k response has a 4xx status code func (o *GetLogEntryByIndexOK) IsClientError() bool { return false } // IsServerError returns true when this get log entry by index o k response has a 5xx status code func (o *GetLogEntryByIndexOK) IsServerError() bool { return false } // IsCode returns true when this get log entry by index o k response a status code equal to that given func (o *GetLogEntryByIndexOK) IsCode(code int) bool { return code == 200 } // Code gets the status code for the get log entry by index o k response func (o *GetLogEntryByIndexOK) Code() int { return 200 } func (o *GetLogEntryByIndexOK) Error() string { return fmt.Sprintf("[GET /api/v1/log/entries][%d] getLogEntryByIndexOK %+v", 200, o.Payload) } func (o *GetLogEntryByIndexOK) String() string { return fmt.Sprintf("[GET /api/v1/log/entries][%d] getLogEntryByIndexOK %+v", 200, o.Payload) } func (o *GetLogEntryByIndexOK) GetPayload() models.LogEntry { return o.Payload } func (o *GetLogEntryByIndexOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { // response payload if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { return err } return nil } // NewGetLogEntryByIndexNotFound creates a GetLogEntryByIndexNotFound with default headers values func NewGetLogEntryByIndexNotFound() *GetLogEntryByIndexNotFound { return &GetLogEntryByIndexNotFound{} } /* GetLogEntryByIndexNotFound describes a response with status code 404, with default header values. The content requested could not be found */ type GetLogEntryByIndexNotFound struct { } // IsSuccess returns true when this get log entry by index not found response has a 2xx status code func (o *GetLogEntryByIndexNotFound) IsSuccess() bool { return false } // IsRedirect returns true when this get log entry by index not found response has a 3xx status code func (o *GetLogEntryByIndexNotFound) IsRedirect() bool { return false } // IsClientError returns true when this get log entry by index not found response has a 4xx status code func (o *GetLogEntryByIndexNotFound) IsClientError() bool { return true } // IsServerError returns true when this get log entry by index not found response has a 5xx status code func (o *GetLogEntryByIndexNotFound) IsServerError() bool { return false } // IsCode returns true when this get log entry by index not found response a status code equal to that given func (o *GetLogEntryByIndexNotFound) IsCode(code int) bool { return code == 404 } // Code gets the status code for the get log entry by index not found response func (o *GetLogEntryByIndexNotFound) Code() int { return 404 } func (o *GetLogEntryByIndexNotFound) Error() string { return fmt.Sprintf("[GET /api/v1/log/entries][%d] getLogEntryByIndexNotFound ", 404) } func (o *GetLogEntryByIndexNotFound) String() string { return fmt.Sprintf("[GET /api/v1/log/entries][%d] getLogEntryByIndexNotFound ", 404) } func (o *GetLogEntryByIndexNotFound) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { return nil } // NewGetLogEntryByIndexDefault creates a GetLogEntryByIndexDefault with default headers values func NewGetLogEntryByIndexDefault(code int) *GetLogEntryByIndexDefault { return &GetLogEntryByIndexDefault{ _statusCode: code, } } /* GetLogEntryByIndexDefault describes a response with status code -1, with default header values. There was an internal error in the server while processing the request */ type GetLogEntryByIndexDefault struct { _statusCode int Payload *models.Error } // IsSuccess returns true when this get log entry by index default response has a 2xx status code func (o *GetLogEntryByIndexDefault) IsSuccess() bool { return o._statusCode/100 == 2 } // IsRedirect returns true when this get log entry by index default response has a 3xx status code func (o *GetLogEntryByIndexDefault) IsRedirect() bool { return o._statusCode/100 == 3 } // IsClientError returns true when this get log entry by index default response has a 4xx status code func (o *GetLogEntryByIndexDefault) IsClientError() bool { return o._statusCode/100 == 4 } // IsServerError returns true when this get log entry by index default response has a 5xx status code func (o *GetLogEntryByIndexDefault) IsServerError() bool { return o._statusCode/100 == 5 } // IsCode returns true when this get log entry by index default response a status code equal to that given func (o *GetLogEntryByIndexDefault) IsCode(code int) bool { return o._statusCode == code } // Code gets the status code for the get log entry by index default response func (o *GetLogEntryByIndexDefault) Code() int { return o._statusCode } func (o *GetLogEntryByIndexDefault) Error() string { return fmt.Sprintf("[GET /api/v1/log/entries][%d] getLogEntryByIndex default %+v", o._statusCode, o.Payload) } func (o *GetLogEntryByIndexDefault) String() string { return fmt.Sprintf("[GET /api/v1/log/entries][%d] getLogEntryByIndex default %+v", o._statusCode, o.Payload) } func (o *GetLogEntryByIndexDefault) GetPayload() *models.Error { return o.Payload } func (o *GetLogEntryByIndexDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } rekor-1.3.5/pkg/generated/client/entries/get_log_entry_by_uuid_parameters.go000066400000000000000000000120421455727245600274630ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "net/http" "time" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" ) // NewGetLogEntryByUUIDParams creates a new GetLogEntryByUUIDParams object, // with the default timeout for this client. // // Default values are not hydrated, since defaults are normally applied by the API server side. // // To enforce default values in parameter, use SetDefaults or WithDefaults. func NewGetLogEntryByUUIDParams() *GetLogEntryByUUIDParams { return &GetLogEntryByUUIDParams{ timeout: cr.DefaultTimeout, } } // NewGetLogEntryByUUIDParamsWithTimeout creates a new GetLogEntryByUUIDParams object // with the ability to set a timeout on a request. func NewGetLogEntryByUUIDParamsWithTimeout(timeout time.Duration) *GetLogEntryByUUIDParams { return &GetLogEntryByUUIDParams{ timeout: timeout, } } // NewGetLogEntryByUUIDParamsWithContext creates a new GetLogEntryByUUIDParams object // with the ability to set a context for a request. func NewGetLogEntryByUUIDParamsWithContext(ctx context.Context) *GetLogEntryByUUIDParams { return &GetLogEntryByUUIDParams{ Context: ctx, } } // NewGetLogEntryByUUIDParamsWithHTTPClient creates a new GetLogEntryByUUIDParams object // with the ability to set a custom HTTPClient for a request. func NewGetLogEntryByUUIDParamsWithHTTPClient(client *http.Client) *GetLogEntryByUUIDParams { return &GetLogEntryByUUIDParams{ HTTPClient: client, } } /* GetLogEntryByUUIDParams contains all the parameters to send to the API endpoint for the get log entry by UUID operation. Typically these are written to a http.Request. */ type GetLogEntryByUUIDParams struct { /* EntryUUID. the UUID of the entry for which the inclusion proof information should be returned */ EntryUUID string timeout time.Duration Context context.Context HTTPClient *http.Client } // WithDefaults hydrates default values in the get log entry by UUID params (not the query body). // // All values with no default are reset to their zero value. func (o *GetLogEntryByUUIDParams) WithDefaults() *GetLogEntryByUUIDParams { o.SetDefaults() return o } // SetDefaults hydrates default values in the get log entry by UUID params (not the query body). // // All values with no default are reset to their zero value. func (o *GetLogEntryByUUIDParams) SetDefaults() { // no default values defined for this parameter } // WithTimeout adds the timeout to the get log entry by UUID params func (o *GetLogEntryByUUIDParams) WithTimeout(timeout time.Duration) *GetLogEntryByUUIDParams { o.SetTimeout(timeout) return o } // SetTimeout adds the timeout to the get log entry by UUID params func (o *GetLogEntryByUUIDParams) SetTimeout(timeout time.Duration) { o.timeout = timeout } // WithContext adds the context to the get log entry by UUID params func (o *GetLogEntryByUUIDParams) WithContext(ctx context.Context) *GetLogEntryByUUIDParams { o.SetContext(ctx) return o } // SetContext adds the context to the get log entry by UUID params func (o *GetLogEntryByUUIDParams) SetContext(ctx context.Context) { o.Context = ctx } // WithHTTPClient adds the HTTPClient to the get log entry by UUID params func (o *GetLogEntryByUUIDParams) WithHTTPClient(client *http.Client) *GetLogEntryByUUIDParams { o.SetHTTPClient(client) return o } // SetHTTPClient adds the HTTPClient to the get log entry by UUID params func (o *GetLogEntryByUUIDParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } // WithEntryUUID adds the entryUUID to the get log entry by UUID params func (o *GetLogEntryByUUIDParams) WithEntryUUID(entryUUID string) *GetLogEntryByUUIDParams { o.SetEntryUUID(entryUUID) return o } // SetEntryUUID adds the entryUuid to the get log entry by UUID params func (o *GetLogEntryByUUIDParams) SetEntryUUID(entryUUID string) { o.EntryUUID = entryUUID } // WriteToRequest writes these params to a swagger request func (o *GetLogEntryByUUIDParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { if err := r.SetTimeout(o.timeout); err != nil { return err } var res []error // path param entryUUID if err := r.SetPathParam("entryUUID", o.EntryUUID); err != nil { return err } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } rekor-1.3.5/pkg/generated/client/entries/get_log_entry_by_uuid_responses.go000066400000000000000000000177361455727245600273600ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "fmt" "io" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" ) // GetLogEntryByUUIDReader is a Reader for the GetLogEntryByUUID structure. type GetLogEntryByUUIDReader struct { formats strfmt.Registry } // ReadResponse reads a server response into the received o. func (o *GetLogEntryByUUIDReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { switch response.Code() { case 200: result := NewGetLogEntryByUUIDOK() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return result, nil case 404: result := NewGetLogEntryByUUIDNotFound() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return nil, result default: result := NewGetLogEntryByUUIDDefault(response.Code()) if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } if response.Code()/100 == 2 { return result, nil } return nil, result } } // NewGetLogEntryByUUIDOK creates a GetLogEntryByUUIDOK with default headers values func NewGetLogEntryByUUIDOK() *GetLogEntryByUUIDOK { return &GetLogEntryByUUIDOK{} } /* GetLogEntryByUUIDOK describes a response with status code 200, with default header values. Information needed for a client to compute the inclusion proof */ type GetLogEntryByUUIDOK struct { Payload models.LogEntry } // IsSuccess returns true when this get log entry by Uuid o k response has a 2xx status code func (o *GetLogEntryByUUIDOK) IsSuccess() bool { return true } // IsRedirect returns true when this get log entry by Uuid o k response has a 3xx status code func (o *GetLogEntryByUUIDOK) IsRedirect() bool { return false } // IsClientError returns true when this get log entry by Uuid o k response has a 4xx status code func (o *GetLogEntryByUUIDOK) IsClientError() bool { return false } // IsServerError returns true when this get log entry by Uuid o k response has a 5xx status code func (o *GetLogEntryByUUIDOK) IsServerError() bool { return false } // IsCode returns true when this get log entry by Uuid o k response a status code equal to that given func (o *GetLogEntryByUUIDOK) IsCode(code int) bool { return code == 200 } // Code gets the status code for the get log entry by Uuid o k response func (o *GetLogEntryByUUIDOK) Code() int { return 200 } func (o *GetLogEntryByUUIDOK) Error() string { return fmt.Sprintf("[GET /api/v1/log/entries/{entryUUID}][%d] getLogEntryByUuidOK %+v", 200, o.Payload) } func (o *GetLogEntryByUUIDOK) String() string { return fmt.Sprintf("[GET /api/v1/log/entries/{entryUUID}][%d] getLogEntryByUuidOK %+v", 200, o.Payload) } func (o *GetLogEntryByUUIDOK) GetPayload() models.LogEntry { return o.Payload } func (o *GetLogEntryByUUIDOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { // response payload if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { return err } return nil } // NewGetLogEntryByUUIDNotFound creates a GetLogEntryByUUIDNotFound with default headers values func NewGetLogEntryByUUIDNotFound() *GetLogEntryByUUIDNotFound { return &GetLogEntryByUUIDNotFound{} } /* GetLogEntryByUUIDNotFound describes a response with status code 404, with default header values. The content requested could not be found */ type GetLogEntryByUUIDNotFound struct { } // IsSuccess returns true when this get log entry by Uuid not found response has a 2xx status code func (o *GetLogEntryByUUIDNotFound) IsSuccess() bool { return false } // IsRedirect returns true when this get log entry by Uuid not found response has a 3xx status code func (o *GetLogEntryByUUIDNotFound) IsRedirect() bool { return false } // IsClientError returns true when this get log entry by Uuid not found response has a 4xx status code func (o *GetLogEntryByUUIDNotFound) IsClientError() bool { return true } // IsServerError returns true when this get log entry by Uuid not found response has a 5xx status code func (o *GetLogEntryByUUIDNotFound) IsServerError() bool { return false } // IsCode returns true when this get log entry by Uuid not found response a status code equal to that given func (o *GetLogEntryByUUIDNotFound) IsCode(code int) bool { return code == 404 } // Code gets the status code for the get log entry by Uuid not found response func (o *GetLogEntryByUUIDNotFound) Code() int { return 404 } func (o *GetLogEntryByUUIDNotFound) Error() string { return fmt.Sprintf("[GET /api/v1/log/entries/{entryUUID}][%d] getLogEntryByUuidNotFound ", 404) } func (o *GetLogEntryByUUIDNotFound) String() string { return fmt.Sprintf("[GET /api/v1/log/entries/{entryUUID}][%d] getLogEntryByUuidNotFound ", 404) } func (o *GetLogEntryByUUIDNotFound) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { return nil } // NewGetLogEntryByUUIDDefault creates a GetLogEntryByUUIDDefault with default headers values func NewGetLogEntryByUUIDDefault(code int) *GetLogEntryByUUIDDefault { return &GetLogEntryByUUIDDefault{ _statusCode: code, } } /* GetLogEntryByUUIDDefault describes a response with status code -1, with default header values. There was an internal error in the server while processing the request */ type GetLogEntryByUUIDDefault struct { _statusCode int Payload *models.Error } // IsSuccess returns true when this get log entry by UUID default response has a 2xx status code func (o *GetLogEntryByUUIDDefault) IsSuccess() bool { return o._statusCode/100 == 2 } // IsRedirect returns true when this get log entry by UUID default response has a 3xx status code func (o *GetLogEntryByUUIDDefault) IsRedirect() bool { return o._statusCode/100 == 3 } // IsClientError returns true when this get log entry by UUID default response has a 4xx status code func (o *GetLogEntryByUUIDDefault) IsClientError() bool { return o._statusCode/100 == 4 } // IsServerError returns true when this get log entry by UUID default response has a 5xx status code func (o *GetLogEntryByUUIDDefault) IsServerError() bool { return o._statusCode/100 == 5 } // IsCode returns true when this get log entry by UUID default response a status code equal to that given func (o *GetLogEntryByUUIDDefault) IsCode(code int) bool { return o._statusCode == code } // Code gets the status code for the get log entry by UUID default response func (o *GetLogEntryByUUIDDefault) Code() int { return o._statusCode } func (o *GetLogEntryByUUIDDefault) Error() string { return fmt.Sprintf("[GET /api/v1/log/entries/{entryUUID}][%d] getLogEntryByUUID default %+v", o._statusCode, o.Payload) } func (o *GetLogEntryByUUIDDefault) String() string { return fmt.Sprintf("[GET /api/v1/log/entries/{entryUUID}][%d] getLogEntryByUUID default %+v", o._statusCode, o.Payload) } func (o *GetLogEntryByUUIDDefault) GetPayload() *models.Error { return o.Payload } func (o *GetLogEntryByUUIDDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } rekor-1.3.5/pkg/generated/client/entries/search_log_query_parameters.go000066400000000000000000000114701455727245600264410ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "net/http" "time" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" ) // NewSearchLogQueryParams creates a new SearchLogQueryParams object, // with the default timeout for this client. // // Default values are not hydrated, since defaults are normally applied by the API server side. // // To enforce default values in parameter, use SetDefaults or WithDefaults. func NewSearchLogQueryParams() *SearchLogQueryParams { return &SearchLogQueryParams{ timeout: cr.DefaultTimeout, } } // NewSearchLogQueryParamsWithTimeout creates a new SearchLogQueryParams object // with the ability to set a timeout on a request. func NewSearchLogQueryParamsWithTimeout(timeout time.Duration) *SearchLogQueryParams { return &SearchLogQueryParams{ timeout: timeout, } } // NewSearchLogQueryParamsWithContext creates a new SearchLogQueryParams object // with the ability to set a context for a request. func NewSearchLogQueryParamsWithContext(ctx context.Context) *SearchLogQueryParams { return &SearchLogQueryParams{ Context: ctx, } } // NewSearchLogQueryParamsWithHTTPClient creates a new SearchLogQueryParams object // with the ability to set a custom HTTPClient for a request. func NewSearchLogQueryParamsWithHTTPClient(client *http.Client) *SearchLogQueryParams { return &SearchLogQueryParams{ HTTPClient: client, } } /* SearchLogQueryParams contains all the parameters to send to the API endpoint for the search log query operation. Typically these are written to a http.Request. */ type SearchLogQueryParams struct { // Entry. Entry *models.SearchLogQuery timeout time.Duration Context context.Context HTTPClient *http.Client } // WithDefaults hydrates default values in the search log query params (not the query body). // // All values with no default are reset to their zero value. func (o *SearchLogQueryParams) WithDefaults() *SearchLogQueryParams { o.SetDefaults() return o } // SetDefaults hydrates default values in the search log query params (not the query body). // // All values with no default are reset to their zero value. func (o *SearchLogQueryParams) SetDefaults() { // no default values defined for this parameter } // WithTimeout adds the timeout to the search log query params func (o *SearchLogQueryParams) WithTimeout(timeout time.Duration) *SearchLogQueryParams { o.SetTimeout(timeout) return o } // SetTimeout adds the timeout to the search log query params func (o *SearchLogQueryParams) SetTimeout(timeout time.Duration) { o.timeout = timeout } // WithContext adds the context to the search log query params func (o *SearchLogQueryParams) WithContext(ctx context.Context) *SearchLogQueryParams { o.SetContext(ctx) return o } // SetContext adds the context to the search log query params func (o *SearchLogQueryParams) SetContext(ctx context.Context) { o.Context = ctx } // WithHTTPClient adds the HTTPClient to the search log query params func (o *SearchLogQueryParams) WithHTTPClient(client *http.Client) *SearchLogQueryParams { o.SetHTTPClient(client) return o } // SetHTTPClient adds the HTTPClient to the search log query params func (o *SearchLogQueryParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } // WithEntry adds the entry to the search log query params func (o *SearchLogQueryParams) WithEntry(entry *models.SearchLogQuery) *SearchLogQueryParams { o.SetEntry(entry) return o } // SetEntry adds the entry to the search log query params func (o *SearchLogQueryParams) SetEntry(entry *models.SearchLogQuery) { o.Entry = entry } // WriteToRequest writes these params to a swagger request func (o *SearchLogQueryParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { if err := r.SetTimeout(o.timeout); err != nil { return err } var res []error if o.Entry != nil { if err := r.SetBodyParam(o.Entry); err != nil { return err } } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } rekor-1.3.5/pkg/generated/client/entries/search_log_query_responses.go000066400000000000000000000251601455727245600263200ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "fmt" "io" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" ) // SearchLogQueryReader is a Reader for the SearchLogQuery structure. type SearchLogQueryReader struct { formats strfmt.Registry } // ReadResponse reads a server response into the received o. func (o *SearchLogQueryReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { switch response.Code() { case 200: result := NewSearchLogQueryOK() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return result, nil case 400: result := NewSearchLogQueryBadRequest() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return nil, result case 422: result := NewSearchLogQueryUnprocessableEntity() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return nil, result default: result := NewSearchLogQueryDefault(response.Code()) if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } if response.Code()/100 == 2 { return result, nil } return nil, result } } // NewSearchLogQueryOK creates a SearchLogQueryOK with default headers values func NewSearchLogQueryOK() *SearchLogQueryOK { return &SearchLogQueryOK{} } /* SearchLogQueryOK describes a response with status code 200, with default header values. Returns zero or more entries from the transparency log, according to how many were included in request query */ type SearchLogQueryOK struct { Payload []models.LogEntry } // IsSuccess returns true when this search log query o k response has a 2xx status code func (o *SearchLogQueryOK) IsSuccess() bool { return true } // IsRedirect returns true when this search log query o k response has a 3xx status code func (o *SearchLogQueryOK) IsRedirect() bool { return false } // IsClientError returns true when this search log query o k response has a 4xx status code func (o *SearchLogQueryOK) IsClientError() bool { return false } // IsServerError returns true when this search log query o k response has a 5xx status code func (o *SearchLogQueryOK) IsServerError() bool { return false } // IsCode returns true when this search log query o k response a status code equal to that given func (o *SearchLogQueryOK) IsCode(code int) bool { return code == 200 } // Code gets the status code for the search log query o k response func (o *SearchLogQueryOK) Code() int { return 200 } func (o *SearchLogQueryOK) Error() string { return fmt.Sprintf("[POST /api/v1/log/entries/retrieve][%d] searchLogQueryOK %+v", 200, o.Payload) } func (o *SearchLogQueryOK) String() string { return fmt.Sprintf("[POST /api/v1/log/entries/retrieve][%d] searchLogQueryOK %+v", 200, o.Payload) } func (o *SearchLogQueryOK) GetPayload() []models.LogEntry { return o.Payload } func (o *SearchLogQueryOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { // response payload if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { return err } return nil } // NewSearchLogQueryBadRequest creates a SearchLogQueryBadRequest with default headers values func NewSearchLogQueryBadRequest() *SearchLogQueryBadRequest { return &SearchLogQueryBadRequest{} } /* SearchLogQueryBadRequest describes a response with status code 400, with default header values. The content supplied to the server was invalid */ type SearchLogQueryBadRequest struct { Payload *models.Error } // IsSuccess returns true when this search log query bad request response has a 2xx status code func (o *SearchLogQueryBadRequest) IsSuccess() bool { return false } // IsRedirect returns true when this search log query bad request response has a 3xx status code func (o *SearchLogQueryBadRequest) IsRedirect() bool { return false } // IsClientError returns true when this search log query bad request response has a 4xx status code func (o *SearchLogQueryBadRequest) IsClientError() bool { return true } // IsServerError returns true when this search log query bad request response has a 5xx status code func (o *SearchLogQueryBadRequest) IsServerError() bool { return false } // IsCode returns true when this search log query bad request response a status code equal to that given func (o *SearchLogQueryBadRequest) IsCode(code int) bool { return code == 400 } // Code gets the status code for the search log query bad request response func (o *SearchLogQueryBadRequest) Code() int { return 400 } func (o *SearchLogQueryBadRequest) Error() string { return fmt.Sprintf("[POST /api/v1/log/entries/retrieve][%d] searchLogQueryBadRequest %+v", 400, o.Payload) } func (o *SearchLogQueryBadRequest) String() string { return fmt.Sprintf("[POST /api/v1/log/entries/retrieve][%d] searchLogQueryBadRequest %+v", 400, o.Payload) } func (o *SearchLogQueryBadRequest) GetPayload() *models.Error { return o.Payload } func (o *SearchLogQueryBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } // NewSearchLogQueryUnprocessableEntity creates a SearchLogQueryUnprocessableEntity with default headers values func NewSearchLogQueryUnprocessableEntity() *SearchLogQueryUnprocessableEntity { return &SearchLogQueryUnprocessableEntity{} } /* SearchLogQueryUnprocessableEntity describes a response with status code 422, with default header values. The server understood the request but is unable to process the contained instructions */ type SearchLogQueryUnprocessableEntity struct { Payload *models.Error } // IsSuccess returns true when this search log query unprocessable entity response has a 2xx status code func (o *SearchLogQueryUnprocessableEntity) IsSuccess() bool { return false } // IsRedirect returns true when this search log query unprocessable entity response has a 3xx status code func (o *SearchLogQueryUnprocessableEntity) IsRedirect() bool { return false } // IsClientError returns true when this search log query unprocessable entity response has a 4xx status code func (o *SearchLogQueryUnprocessableEntity) IsClientError() bool { return true } // IsServerError returns true when this search log query unprocessable entity response has a 5xx status code func (o *SearchLogQueryUnprocessableEntity) IsServerError() bool { return false } // IsCode returns true when this search log query unprocessable entity response a status code equal to that given func (o *SearchLogQueryUnprocessableEntity) IsCode(code int) bool { return code == 422 } // Code gets the status code for the search log query unprocessable entity response func (o *SearchLogQueryUnprocessableEntity) Code() int { return 422 } func (o *SearchLogQueryUnprocessableEntity) Error() string { return fmt.Sprintf("[POST /api/v1/log/entries/retrieve][%d] searchLogQueryUnprocessableEntity %+v", 422, o.Payload) } func (o *SearchLogQueryUnprocessableEntity) String() string { return fmt.Sprintf("[POST /api/v1/log/entries/retrieve][%d] searchLogQueryUnprocessableEntity %+v", 422, o.Payload) } func (o *SearchLogQueryUnprocessableEntity) GetPayload() *models.Error { return o.Payload } func (o *SearchLogQueryUnprocessableEntity) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } // NewSearchLogQueryDefault creates a SearchLogQueryDefault with default headers values func NewSearchLogQueryDefault(code int) *SearchLogQueryDefault { return &SearchLogQueryDefault{ _statusCode: code, } } /* SearchLogQueryDefault describes a response with status code -1, with default header values. There was an internal error in the server while processing the request */ type SearchLogQueryDefault struct { _statusCode int Payload *models.Error } // IsSuccess returns true when this search log query default response has a 2xx status code func (o *SearchLogQueryDefault) IsSuccess() bool { return o._statusCode/100 == 2 } // IsRedirect returns true when this search log query default response has a 3xx status code func (o *SearchLogQueryDefault) IsRedirect() bool { return o._statusCode/100 == 3 } // IsClientError returns true when this search log query default response has a 4xx status code func (o *SearchLogQueryDefault) IsClientError() bool { return o._statusCode/100 == 4 } // IsServerError returns true when this search log query default response has a 5xx status code func (o *SearchLogQueryDefault) IsServerError() bool { return o._statusCode/100 == 5 } // IsCode returns true when this search log query default response a status code equal to that given func (o *SearchLogQueryDefault) IsCode(code int) bool { return o._statusCode == code } // Code gets the status code for the search log query default response func (o *SearchLogQueryDefault) Code() int { return o._statusCode } func (o *SearchLogQueryDefault) Error() string { return fmt.Sprintf("[POST /api/v1/log/entries/retrieve][%d] searchLogQuery default %+v", o._statusCode, o.Payload) } func (o *SearchLogQueryDefault) String() string { return fmt.Sprintf("[POST /api/v1/log/entries/retrieve][%d] searchLogQuery default %+v", o._statusCode, o.Payload) } func (o *SearchLogQueryDefault) GetPayload() *models.Error { return o.Payload } func (o *SearchLogQueryDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } rekor-1.3.5/pkg/generated/client/index/000077500000000000000000000000001455727245600177675ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/client/index/index_client.go000066400000000000000000000056361455727245600227750ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package index // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" ) // New creates a new index API client. func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService { return &Client{transport: transport, formats: formats} } /* Client for index API */ type Client struct { transport runtime.ClientTransport formats strfmt.Registry } // ClientOption is the option for Client methods type ClientOption func(*runtime.ClientOperation) // ClientService is the interface for Client methods type ClientService interface { SearchIndex(params *SearchIndexParams, opts ...ClientOption) (*SearchIndexOK, error) SetTransport(transport runtime.ClientTransport) } /* SearchIndex searches index by entry metadata EXPERIMENTAL - this endpoint is offered as best effort only and may be changed or removed in future releases. The results returned from this endpoint may be incomplete. */ func (a *Client) SearchIndex(params *SearchIndexParams, opts ...ClientOption) (*SearchIndexOK, error) { // TODO: Validate the params before sending if params == nil { params = NewSearchIndexParams() } op := &runtime.ClientOperation{ ID: "searchIndex", Method: "POST", PathPattern: "/api/v1/index/retrieve", ProducesMediaTypes: []string{"application/json"}, ConsumesMediaTypes: []string{"application/json"}, Schemes: []string{"http"}, Params: params, Reader: &SearchIndexReader{formats: a.formats}, Context: params.Context, Client: params.HTTPClient, } for _, opt := range opts { opt(op) } result, err := a.transport.Submit(op) if err != nil { return nil, err } success, ok := result.(*SearchIndexOK) if ok { return success, nil } // unexpected success response unexpectedSuccess := result.(*SearchIndexDefault) return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } // SetTransport changes the transport on the client func (a *Client) SetTransport(transport runtime.ClientTransport) { a.transport = transport } rekor-1.3.5/pkg/generated/client/index/search_index_parameters.go000066400000000000000000000112171455727245600251770ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package index // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "net/http" "time" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" ) // NewSearchIndexParams creates a new SearchIndexParams object, // with the default timeout for this client. // // Default values are not hydrated, since defaults are normally applied by the API server side. // // To enforce default values in parameter, use SetDefaults or WithDefaults. func NewSearchIndexParams() *SearchIndexParams { return &SearchIndexParams{ timeout: cr.DefaultTimeout, } } // NewSearchIndexParamsWithTimeout creates a new SearchIndexParams object // with the ability to set a timeout on a request. func NewSearchIndexParamsWithTimeout(timeout time.Duration) *SearchIndexParams { return &SearchIndexParams{ timeout: timeout, } } // NewSearchIndexParamsWithContext creates a new SearchIndexParams object // with the ability to set a context for a request. func NewSearchIndexParamsWithContext(ctx context.Context) *SearchIndexParams { return &SearchIndexParams{ Context: ctx, } } // NewSearchIndexParamsWithHTTPClient creates a new SearchIndexParams object // with the ability to set a custom HTTPClient for a request. func NewSearchIndexParamsWithHTTPClient(client *http.Client) *SearchIndexParams { return &SearchIndexParams{ HTTPClient: client, } } /* SearchIndexParams contains all the parameters to send to the API endpoint for the search index operation. Typically these are written to a http.Request. */ type SearchIndexParams struct { // Query. Query *models.SearchIndex timeout time.Duration Context context.Context HTTPClient *http.Client } // WithDefaults hydrates default values in the search index params (not the query body). // // All values with no default are reset to their zero value. func (o *SearchIndexParams) WithDefaults() *SearchIndexParams { o.SetDefaults() return o } // SetDefaults hydrates default values in the search index params (not the query body). // // All values with no default are reset to their zero value. func (o *SearchIndexParams) SetDefaults() { // no default values defined for this parameter } // WithTimeout adds the timeout to the search index params func (o *SearchIndexParams) WithTimeout(timeout time.Duration) *SearchIndexParams { o.SetTimeout(timeout) return o } // SetTimeout adds the timeout to the search index params func (o *SearchIndexParams) SetTimeout(timeout time.Duration) { o.timeout = timeout } // WithContext adds the context to the search index params func (o *SearchIndexParams) WithContext(ctx context.Context) *SearchIndexParams { o.SetContext(ctx) return o } // SetContext adds the context to the search index params func (o *SearchIndexParams) SetContext(ctx context.Context) { o.Context = ctx } // WithHTTPClient adds the HTTPClient to the search index params func (o *SearchIndexParams) WithHTTPClient(client *http.Client) *SearchIndexParams { o.SetHTTPClient(client) return o } // SetHTTPClient adds the HTTPClient to the search index params func (o *SearchIndexParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } // WithQuery adds the query to the search index params func (o *SearchIndexParams) WithQuery(query *models.SearchIndex) *SearchIndexParams { o.SetQuery(query) return o } // SetQuery adds the query to the search index params func (o *SearchIndexParams) SetQuery(query *models.SearchIndex) { o.Query = query } // WriteToRequest writes these params to a swagger request func (o *SearchIndexParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { if err := r.SetTimeout(o.timeout); err != nil { return err } var res []error if o.Query != nil { if err := r.SetBodyParam(o.Query); err != nil { return err } } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } rekor-1.3.5/pkg/generated/client/index/search_index_responses.go000066400000000000000000000173601455727245600250620ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package index // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "fmt" "io" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" ) // SearchIndexReader is a Reader for the SearchIndex structure. type SearchIndexReader struct { formats strfmt.Registry } // ReadResponse reads a server response into the received o. func (o *SearchIndexReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { switch response.Code() { case 200: result := NewSearchIndexOK() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return result, nil case 400: result := NewSearchIndexBadRequest() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return nil, result default: result := NewSearchIndexDefault(response.Code()) if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } if response.Code()/100 == 2 { return result, nil } return nil, result } } // NewSearchIndexOK creates a SearchIndexOK with default headers values func NewSearchIndexOK() *SearchIndexOK { return &SearchIndexOK{} } /* SearchIndexOK describes a response with status code 200, with default header values. Returns zero or more entry UUIDs from the transparency log based on search query */ type SearchIndexOK struct { Payload []string } // IsSuccess returns true when this search index o k response has a 2xx status code func (o *SearchIndexOK) IsSuccess() bool { return true } // IsRedirect returns true when this search index o k response has a 3xx status code func (o *SearchIndexOK) IsRedirect() bool { return false } // IsClientError returns true when this search index o k response has a 4xx status code func (o *SearchIndexOK) IsClientError() bool { return false } // IsServerError returns true when this search index o k response has a 5xx status code func (o *SearchIndexOK) IsServerError() bool { return false } // IsCode returns true when this search index o k response a status code equal to that given func (o *SearchIndexOK) IsCode(code int) bool { return code == 200 } // Code gets the status code for the search index o k response func (o *SearchIndexOK) Code() int { return 200 } func (o *SearchIndexOK) Error() string { return fmt.Sprintf("[POST /api/v1/index/retrieve][%d] searchIndexOK %+v", 200, o.Payload) } func (o *SearchIndexOK) String() string { return fmt.Sprintf("[POST /api/v1/index/retrieve][%d] searchIndexOK %+v", 200, o.Payload) } func (o *SearchIndexOK) GetPayload() []string { return o.Payload } func (o *SearchIndexOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { // response payload if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { return err } return nil } // NewSearchIndexBadRequest creates a SearchIndexBadRequest with default headers values func NewSearchIndexBadRequest() *SearchIndexBadRequest { return &SearchIndexBadRequest{} } /* SearchIndexBadRequest describes a response with status code 400, with default header values. The content supplied to the server was invalid */ type SearchIndexBadRequest struct { Payload *models.Error } // IsSuccess returns true when this search index bad request response has a 2xx status code func (o *SearchIndexBadRequest) IsSuccess() bool { return false } // IsRedirect returns true when this search index bad request response has a 3xx status code func (o *SearchIndexBadRequest) IsRedirect() bool { return false } // IsClientError returns true when this search index bad request response has a 4xx status code func (o *SearchIndexBadRequest) IsClientError() bool { return true } // IsServerError returns true when this search index bad request response has a 5xx status code func (o *SearchIndexBadRequest) IsServerError() bool { return false } // IsCode returns true when this search index bad request response a status code equal to that given func (o *SearchIndexBadRequest) IsCode(code int) bool { return code == 400 } // Code gets the status code for the search index bad request response func (o *SearchIndexBadRequest) Code() int { return 400 } func (o *SearchIndexBadRequest) Error() string { return fmt.Sprintf("[POST /api/v1/index/retrieve][%d] searchIndexBadRequest %+v", 400, o.Payload) } func (o *SearchIndexBadRequest) String() string { return fmt.Sprintf("[POST /api/v1/index/retrieve][%d] searchIndexBadRequest %+v", 400, o.Payload) } func (o *SearchIndexBadRequest) GetPayload() *models.Error { return o.Payload } func (o *SearchIndexBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } // NewSearchIndexDefault creates a SearchIndexDefault with default headers values func NewSearchIndexDefault(code int) *SearchIndexDefault { return &SearchIndexDefault{ _statusCode: code, } } /* SearchIndexDefault describes a response with status code -1, with default header values. There was an internal error in the server while processing the request */ type SearchIndexDefault struct { _statusCode int Payload *models.Error } // IsSuccess returns true when this search index default response has a 2xx status code func (o *SearchIndexDefault) IsSuccess() bool { return o._statusCode/100 == 2 } // IsRedirect returns true when this search index default response has a 3xx status code func (o *SearchIndexDefault) IsRedirect() bool { return o._statusCode/100 == 3 } // IsClientError returns true when this search index default response has a 4xx status code func (o *SearchIndexDefault) IsClientError() bool { return o._statusCode/100 == 4 } // IsServerError returns true when this search index default response has a 5xx status code func (o *SearchIndexDefault) IsServerError() bool { return o._statusCode/100 == 5 } // IsCode returns true when this search index default response a status code equal to that given func (o *SearchIndexDefault) IsCode(code int) bool { return o._statusCode == code } // Code gets the status code for the search index default response func (o *SearchIndexDefault) Code() int { return o._statusCode } func (o *SearchIndexDefault) Error() string { return fmt.Sprintf("[POST /api/v1/index/retrieve][%d] searchIndex default %+v", o._statusCode, o.Payload) } func (o *SearchIndexDefault) String() string { return fmt.Sprintf("[POST /api/v1/index/retrieve][%d] searchIndex default %+v", o._statusCode, o.Payload) } func (o *SearchIndexDefault) GetPayload() *models.Error { return o.Payload } func (o *SearchIndexDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } rekor-1.3.5/pkg/generated/client/pubkey/000077500000000000000000000000001455727245600201575ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/client/pubkey/get_public_key_parameters.go000066400000000000000000000115501455727245600257200ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pubkey // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "net/http" "time" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" ) // NewGetPublicKeyParams creates a new GetPublicKeyParams object, // with the default timeout for this client. // // Default values are not hydrated, since defaults are normally applied by the API server side. // // To enforce default values in parameter, use SetDefaults or WithDefaults. func NewGetPublicKeyParams() *GetPublicKeyParams { return &GetPublicKeyParams{ timeout: cr.DefaultTimeout, } } // NewGetPublicKeyParamsWithTimeout creates a new GetPublicKeyParams object // with the ability to set a timeout on a request. func NewGetPublicKeyParamsWithTimeout(timeout time.Duration) *GetPublicKeyParams { return &GetPublicKeyParams{ timeout: timeout, } } // NewGetPublicKeyParamsWithContext creates a new GetPublicKeyParams object // with the ability to set a context for a request. func NewGetPublicKeyParamsWithContext(ctx context.Context) *GetPublicKeyParams { return &GetPublicKeyParams{ Context: ctx, } } // NewGetPublicKeyParamsWithHTTPClient creates a new GetPublicKeyParams object // with the ability to set a custom HTTPClient for a request. func NewGetPublicKeyParamsWithHTTPClient(client *http.Client) *GetPublicKeyParams { return &GetPublicKeyParams{ HTTPClient: client, } } /* GetPublicKeyParams contains all the parameters to send to the API endpoint for the get public key operation. Typically these are written to a http.Request. */ type GetPublicKeyParams struct { /* TreeID. The tree ID of the tree you wish to get a public key for */ TreeID *string timeout time.Duration Context context.Context HTTPClient *http.Client } // WithDefaults hydrates default values in the get public key params (not the query body). // // All values with no default are reset to their zero value. func (o *GetPublicKeyParams) WithDefaults() *GetPublicKeyParams { o.SetDefaults() return o } // SetDefaults hydrates default values in the get public key params (not the query body). // // All values with no default are reset to their zero value. func (o *GetPublicKeyParams) SetDefaults() { // no default values defined for this parameter } // WithTimeout adds the timeout to the get public key params func (o *GetPublicKeyParams) WithTimeout(timeout time.Duration) *GetPublicKeyParams { o.SetTimeout(timeout) return o } // SetTimeout adds the timeout to the get public key params func (o *GetPublicKeyParams) SetTimeout(timeout time.Duration) { o.timeout = timeout } // WithContext adds the context to the get public key params func (o *GetPublicKeyParams) WithContext(ctx context.Context) *GetPublicKeyParams { o.SetContext(ctx) return o } // SetContext adds the context to the get public key params func (o *GetPublicKeyParams) SetContext(ctx context.Context) { o.Context = ctx } // WithHTTPClient adds the HTTPClient to the get public key params func (o *GetPublicKeyParams) WithHTTPClient(client *http.Client) *GetPublicKeyParams { o.SetHTTPClient(client) return o } // SetHTTPClient adds the HTTPClient to the get public key params func (o *GetPublicKeyParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } // WithTreeID adds the treeID to the get public key params func (o *GetPublicKeyParams) WithTreeID(treeID *string) *GetPublicKeyParams { o.SetTreeID(treeID) return o } // SetTreeID adds the treeId to the get public key params func (o *GetPublicKeyParams) SetTreeID(treeID *string) { o.TreeID = treeID } // WriteToRequest writes these params to a swagger request func (o *GetPublicKeyParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { if err := r.SetTimeout(o.timeout); err != nil { return err } var res []error if o.TreeID != nil { // query param treeID var qrTreeID string if o.TreeID != nil { qrTreeID = *o.TreeID } qTreeID := qrTreeID if qTreeID != "" { if err := r.SetQueryParam("treeID", qTreeID); err != nil { return err } } } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } rekor-1.3.5/pkg/generated/client/pubkey/get_public_key_responses.go000066400000000000000000000130641455727245600256000ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pubkey // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "fmt" "io" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" ) // GetPublicKeyReader is a Reader for the GetPublicKey structure. type GetPublicKeyReader struct { formats strfmt.Registry } // ReadResponse reads a server response into the received o. func (o *GetPublicKeyReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { switch response.Code() { case 200: result := NewGetPublicKeyOK() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return result, nil default: result := NewGetPublicKeyDefault(response.Code()) if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } if response.Code()/100 == 2 { return result, nil } return nil, result } } // NewGetPublicKeyOK creates a GetPublicKeyOK with default headers values func NewGetPublicKeyOK() *GetPublicKeyOK { return &GetPublicKeyOK{} } /* GetPublicKeyOK describes a response with status code 200, with default header values. The public key */ type GetPublicKeyOK struct { Payload string } // IsSuccess returns true when this get public key o k response has a 2xx status code func (o *GetPublicKeyOK) IsSuccess() bool { return true } // IsRedirect returns true when this get public key o k response has a 3xx status code func (o *GetPublicKeyOK) IsRedirect() bool { return false } // IsClientError returns true when this get public key o k response has a 4xx status code func (o *GetPublicKeyOK) IsClientError() bool { return false } // IsServerError returns true when this get public key o k response has a 5xx status code func (o *GetPublicKeyOK) IsServerError() bool { return false } // IsCode returns true when this get public key o k response a status code equal to that given func (o *GetPublicKeyOK) IsCode(code int) bool { return code == 200 } // Code gets the status code for the get public key o k response func (o *GetPublicKeyOK) Code() int { return 200 } func (o *GetPublicKeyOK) Error() string { return fmt.Sprintf("[GET /api/v1/log/publicKey][%d] getPublicKeyOK %+v", 200, o.Payload) } func (o *GetPublicKeyOK) String() string { return fmt.Sprintf("[GET /api/v1/log/publicKey][%d] getPublicKeyOK %+v", 200, o.Payload) } func (o *GetPublicKeyOK) GetPayload() string { return o.Payload } func (o *GetPublicKeyOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { // response payload if err := consumer.Consume(response.Body(), &o.Payload); err != nil && err != io.EOF { return err } return nil } // NewGetPublicKeyDefault creates a GetPublicKeyDefault with default headers values func NewGetPublicKeyDefault(code int) *GetPublicKeyDefault { return &GetPublicKeyDefault{ _statusCode: code, } } /* GetPublicKeyDefault describes a response with status code -1, with default header values. There was an internal error in the server while processing the request */ type GetPublicKeyDefault struct { _statusCode int Payload *models.Error } // IsSuccess returns true when this get public key default response has a 2xx status code func (o *GetPublicKeyDefault) IsSuccess() bool { return o._statusCode/100 == 2 } // IsRedirect returns true when this get public key default response has a 3xx status code func (o *GetPublicKeyDefault) IsRedirect() bool { return o._statusCode/100 == 3 } // IsClientError returns true when this get public key default response has a 4xx status code func (o *GetPublicKeyDefault) IsClientError() bool { return o._statusCode/100 == 4 } // IsServerError returns true when this get public key default response has a 5xx status code func (o *GetPublicKeyDefault) IsServerError() bool { return o._statusCode/100 == 5 } // IsCode returns true when this get public key default response a status code equal to that given func (o *GetPublicKeyDefault) IsCode(code int) bool { return o._statusCode == code } // Code gets the status code for the get public key default response func (o *GetPublicKeyDefault) Code() int { return o._statusCode } func (o *GetPublicKeyDefault) Error() string { return fmt.Sprintf("[GET /api/v1/log/publicKey][%d] getPublicKey default %+v", o._statusCode, o.Payload) } func (o *GetPublicKeyDefault) String() string { return fmt.Sprintf("[GET /api/v1/log/publicKey][%d] getPublicKey default %+v", o._statusCode, o.Payload) } func (o *GetPublicKeyDefault) GetPayload() *models.Error { return o.Payload } func (o *GetPublicKeyDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } rekor-1.3.5/pkg/generated/client/pubkey/pubkey_client.go000066400000000000000000000055701455727245600233520ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pubkey // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" ) // New creates a new pubkey API client. func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService { return &Client{transport: transport, formats: formats} } /* Client for pubkey API */ type Client struct { transport runtime.ClientTransport formats strfmt.Registry } // ClientOption is the option for Client methods type ClientOption func(*runtime.ClientOperation) // ClientService is the interface for Client methods type ClientService interface { GetPublicKey(params *GetPublicKeyParams, opts ...ClientOption) (*GetPublicKeyOK, error) SetTransport(transport runtime.ClientTransport) } /* GetPublicKey retrieves the public key that can be used to validate the signed tree head Returns the public key that can be used to validate the signed tree head */ func (a *Client) GetPublicKey(params *GetPublicKeyParams, opts ...ClientOption) (*GetPublicKeyOK, error) { // TODO: Validate the params before sending if params == nil { params = NewGetPublicKeyParams() } op := &runtime.ClientOperation{ ID: "getPublicKey", Method: "GET", PathPattern: "/api/v1/log/publicKey", ProducesMediaTypes: []string{"application/x-pem-file"}, ConsumesMediaTypes: []string{"application/json"}, Schemes: []string{"http"}, Params: params, Reader: &GetPublicKeyReader{formats: a.formats}, Context: params.Context, Client: params.HTTPClient, } for _, opt := range opts { opt(op) } result, err := a.transport.Submit(op) if err != nil { return nil, err } success, ok := result.(*GetPublicKeyOK) if ok { return success, nil } // unexpected success response unexpectedSuccess := result.(*GetPublicKeyDefault) return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } // SetTransport changes the transport on the client func (a *Client) SetTransport(transport runtime.ClientTransport) { a.transport = transport } rekor-1.3.5/pkg/generated/client/rekor_client.go000066400000000000000000000102021455727245600216620ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package client // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "github.com/go-openapi/runtime" httptransport "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/client/entries" "github.com/sigstore/rekor/pkg/generated/client/index" "github.com/sigstore/rekor/pkg/generated/client/pubkey" "github.com/sigstore/rekor/pkg/generated/client/tlog" ) // Default rekor HTTP client. var Default = NewHTTPClient(nil) const ( // DefaultHost is the default Host // found in Meta (info) section of spec file DefaultHost string = "rekor.sigstore.dev" // DefaultBasePath is the default BasePath // found in Meta (info) section of spec file DefaultBasePath string = "/" ) // DefaultSchemes are the default schemes found in Meta (info) section of spec file var DefaultSchemes = []string{"http"} // NewHTTPClient creates a new rekor HTTP client. func NewHTTPClient(formats strfmt.Registry) *Rekor { return NewHTTPClientWithConfig(formats, nil) } // NewHTTPClientWithConfig creates a new rekor HTTP client, // using a customizable transport config. func NewHTTPClientWithConfig(formats strfmt.Registry, cfg *TransportConfig) *Rekor { // ensure nullable parameters have default if cfg == nil { cfg = DefaultTransportConfig() } // create transport and client transport := httptransport.New(cfg.Host, cfg.BasePath, cfg.Schemes) return New(transport, formats) } // New creates a new rekor client func New(transport runtime.ClientTransport, formats strfmt.Registry) *Rekor { // ensure nullable parameters have default if formats == nil { formats = strfmt.Default } cli := new(Rekor) cli.Transport = transport cli.Entries = entries.New(transport, formats) cli.Index = index.New(transport, formats) cli.Pubkey = pubkey.New(transport, formats) cli.Tlog = tlog.New(transport, formats) return cli } // DefaultTransportConfig creates a TransportConfig with the // default settings taken from the meta section of the spec file. func DefaultTransportConfig() *TransportConfig { return &TransportConfig{ Host: DefaultHost, BasePath: DefaultBasePath, Schemes: DefaultSchemes, } } // TransportConfig contains the transport related info, // found in the meta section of the spec file. type TransportConfig struct { Host string BasePath string Schemes []string } // WithHost overrides the default host, // provided by the meta section of the spec file. func (cfg *TransportConfig) WithHost(host string) *TransportConfig { cfg.Host = host return cfg } // WithBasePath overrides the default basePath, // provided by the meta section of the spec file. func (cfg *TransportConfig) WithBasePath(basePath string) *TransportConfig { cfg.BasePath = basePath return cfg } // WithSchemes overrides the default schemes, // provided by the meta section of the spec file. func (cfg *TransportConfig) WithSchemes(schemes []string) *TransportConfig { cfg.Schemes = schemes return cfg } // Rekor is a client for rekor type Rekor struct { Entries entries.ClientService Index index.ClientService Pubkey pubkey.ClientService Tlog tlog.ClientService Transport runtime.ClientTransport } // SetTransport changes the transport on the client and all its subresources func (c *Rekor) SetTransport(transport runtime.ClientTransport) { c.Transport = transport c.Entries.SetTransport(transport) c.Index.SetTransport(transport) c.Pubkey.SetTransport(transport) c.Tlog.SetTransport(transport) } rekor-1.3.5/pkg/generated/client/tlog/000077500000000000000000000000001455727245600176255ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/client/tlog/get_log_info_parameters.go000066400000000000000000000116701455727245600250370ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "net/http" "time" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" ) // NewGetLogInfoParams creates a new GetLogInfoParams object, // with the default timeout for this client. // // Default values are not hydrated, since defaults are normally applied by the API server side. // // To enforce default values in parameter, use SetDefaults or WithDefaults. func NewGetLogInfoParams() *GetLogInfoParams { return &GetLogInfoParams{ timeout: cr.DefaultTimeout, } } // NewGetLogInfoParamsWithTimeout creates a new GetLogInfoParams object // with the ability to set a timeout on a request. func NewGetLogInfoParamsWithTimeout(timeout time.Duration) *GetLogInfoParams { return &GetLogInfoParams{ timeout: timeout, } } // NewGetLogInfoParamsWithContext creates a new GetLogInfoParams object // with the ability to set a context for a request. func NewGetLogInfoParamsWithContext(ctx context.Context) *GetLogInfoParams { return &GetLogInfoParams{ Context: ctx, } } // NewGetLogInfoParamsWithHTTPClient creates a new GetLogInfoParams object // with the ability to set a custom HTTPClient for a request. func NewGetLogInfoParamsWithHTTPClient(client *http.Client) *GetLogInfoParams { return &GetLogInfoParams{ HTTPClient: client, } } /* GetLogInfoParams contains all the parameters to send to the API endpoint for the get log info operation. Typically these are written to a http.Request. */ type GetLogInfoParams struct { /* Stable. Whether to return a stable checkpoint for the active shard */ Stable *bool timeout time.Duration Context context.Context HTTPClient *http.Client } // WithDefaults hydrates default values in the get log info params (not the query body). // // All values with no default are reset to their zero value. func (o *GetLogInfoParams) WithDefaults() *GetLogInfoParams { o.SetDefaults() return o } // SetDefaults hydrates default values in the get log info params (not the query body). // // All values with no default are reset to their zero value. func (o *GetLogInfoParams) SetDefaults() { var ( stableDefault = bool(false) ) val := GetLogInfoParams{ Stable: &stableDefault, } val.timeout = o.timeout val.Context = o.Context val.HTTPClient = o.HTTPClient *o = val } // WithTimeout adds the timeout to the get log info params func (o *GetLogInfoParams) WithTimeout(timeout time.Duration) *GetLogInfoParams { o.SetTimeout(timeout) return o } // SetTimeout adds the timeout to the get log info params func (o *GetLogInfoParams) SetTimeout(timeout time.Duration) { o.timeout = timeout } // WithContext adds the context to the get log info params func (o *GetLogInfoParams) WithContext(ctx context.Context) *GetLogInfoParams { o.SetContext(ctx) return o } // SetContext adds the context to the get log info params func (o *GetLogInfoParams) SetContext(ctx context.Context) { o.Context = ctx } // WithHTTPClient adds the HTTPClient to the get log info params func (o *GetLogInfoParams) WithHTTPClient(client *http.Client) *GetLogInfoParams { o.SetHTTPClient(client) return o } // SetHTTPClient adds the HTTPClient to the get log info params func (o *GetLogInfoParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } // WithStable adds the stable to the get log info params func (o *GetLogInfoParams) WithStable(stable *bool) *GetLogInfoParams { o.SetStable(stable) return o } // SetStable adds the stable to the get log info params func (o *GetLogInfoParams) SetStable(stable *bool) { o.Stable = stable } // WriteToRequest writes these params to a swagger request func (o *GetLogInfoParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { if err := r.SetTimeout(o.timeout); err != nil { return err } var res []error if o.Stable != nil { // query param stable var qrStable bool if o.Stable != nil { qrStable = *o.Stable } qStable := swag.FormatBool(qrStable) if qStable != "" { if err := r.SetQueryParam("stable", qStable); err != nil { return err } } } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } rekor-1.3.5/pkg/generated/client/tlog/get_log_info_responses.go000066400000000000000000000127731455727245600247220ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "fmt" "io" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" ) // GetLogInfoReader is a Reader for the GetLogInfo structure. type GetLogInfoReader struct { formats strfmt.Registry } // ReadResponse reads a server response into the received o. func (o *GetLogInfoReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { switch response.Code() { case 200: result := NewGetLogInfoOK() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return result, nil default: result := NewGetLogInfoDefault(response.Code()) if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } if response.Code()/100 == 2 { return result, nil } return nil, result } } // NewGetLogInfoOK creates a GetLogInfoOK with default headers values func NewGetLogInfoOK() *GetLogInfoOK { return &GetLogInfoOK{} } /* GetLogInfoOK describes a response with status code 200, with default header values. A JSON object with the root hash and tree size as properties */ type GetLogInfoOK struct { Payload *models.LogInfo } // IsSuccess returns true when this get log info o k response has a 2xx status code func (o *GetLogInfoOK) IsSuccess() bool { return true } // IsRedirect returns true when this get log info o k response has a 3xx status code func (o *GetLogInfoOK) IsRedirect() bool { return false } // IsClientError returns true when this get log info o k response has a 4xx status code func (o *GetLogInfoOK) IsClientError() bool { return false } // IsServerError returns true when this get log info o k response has a 5xx status code func (o *GetLogInfoOK) IsServerError() bool { return false } // IsCode returns true when this get log info o k response a status code equal to that given func (o *GetLogInfoOK) IsCode(code int) bool { return code == 200 } // Code gets the status code for the get log info o k response func (o *GetLogInfoOK) Code() int { return 200 } func (o *GetLogInfoOK) Error() string { return fmt.Sprintf("[GET /api/v1/log][%d] getLogInfoOK %+v", 200, o.Payload) } func (o *GetLogInfoOK) String() string { return fmt.Sprintf("[GET /api/v1/log][%d] getLogInfoOK %+v", 200, o.Payload) } func (o *GetLogInfoOK) GetPayload() *models.LogInfo { return o.Payload } func (o *GetLogInfoOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.LogInfo) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } // NewGetLogInfoDefault creates a GetLogInfoDefault with default headers values func NewGetLogInfoDefault(code int) *GetLogInfoDefault { return &GetLogInfoDefault{ _statusCode: code, } } /* GetLogInfoDefault describes a response with status code -1, with default header values. There was an internal error in the server while processing the request */ type GetLogInfoDefault struct { _statusCode int Payload *models.Error } // IsSuccess returns true when this get log info default response has a 2xx status code func (o *GetLogInfoDefault) IsSuccess() bool { return o._statusCode/100 == 2 } // IsRedirect returns true when this get log info default response has a 3xx status code func (o *GetLogInfoDefault) IsRedirect() bool { return o._statusCode/100 == 3 } // IsClientError returns true when this get log info default response has a 4xx status code func (o *GetLogInfoDefault) IsClientError() bool { return o._statusCode/100 == 4 } // IsServerError returns true when this get log info default response has a 5xx status code func (o *GetLogInfoDefault) IsServerError() bool { return o._statusCode/100 == 5 } // IsCode returns true when this get log info default response a status code equal to that given func (o *GetLogInfoDefault) IsCode(code int) bool { return o._statusCode == code } // Code gets the status code for the get log info default response func (o *GetLogInfoDefault) Code() int { return o._statusCode } func (o *GetLogInfoDefault) Error() string { return fmt.Sprintf("[GET /api/v1/log][%d] getLogInfo default %+v", o._statusCode, o.Payload) } func (o *GetLogInfoDefault) String() string { return fmt.Sprintf("[GET /api/v1/log][%d] getLogInfo default %+v", o._statusCode, o.Payload) } func (o *GetLogInfoDefault) GetPayload() *models.Error { return o.Payload } func (o *GetLogInfoDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } rekor-1.3.5/pkg/generated/client/tlog/get_log_proof_parameters.go000066400000000000000000000146321455727245600252320ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "net/http" "time" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" cr "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" ) // NewGetLogProofParams creates a new GetLogProofParams object, // with the default timeout for this client. // // Default values are not hydrated, since defaults are normally applied by the API server side. // // To enforce default values in parameter, use SetDefaults or WithDefaults. func NewGetLogProofParams() *GetLogProofParams { return &GetLogProofParams{ timeout: cr.DefaultTimeout, } } // NewGetLogProofParamsWithTimeout creates a new GetLogProofParams object // with the ability to set a timeout on a request. func NewGetLogProofParamsWithTimeout(timeout time.Duration) *GetLogProofParams { return &GetLogProofParams{ timeout: timeout, } } // NewGetLogProofParamsWithContext creates a new GetLogProofParams object // with the ability to set a context for a request. func NewGetLogProofParamsWithContext(ctx context.Context) *GetLogProofParams { return &GetLogProofParams{ Context: ctx, } } // NewGetLogProofParamsWithHTTPClient creates a new GetLogProofParams object // with the ability to set a custom HTTPClient for a request. func NewGetLogProofParamsWithHTTPClient(client *http.Client) *GetLogProofParams { return &GetLogProofParams{ HTTPClient: client, } } /* GetLogProofParams contains all the parameters to send to the API endpoint for the get log proof operation. Typically these are written to a http.Request. */ type GetLogProofParams struct { /* FirstSize. The size of the tree that you wish to prove consistency from (1 means the beginning of the log) Defaults to 1 if not specified Default: 1 */ FirstSize *int64 /* LastSize. The size of the tree that you wish to prove consistency to */ LastSize int64 /* TreeID. The tree ID of the tree that you wish to prove consistency for */ TreeID *string timeout time.Duration Context context.Context HTTPClient *http.Client } // WithDefaults hydrates default values in the get log proof params (not the query body). // // All values with no default are reset to their zero value. func (o *GetLogProofParams) WithDefaults() *GetLogProofParams { o.SetDefaults() return o } // SetDefaults hydrates default values in the get log proof params (not the query body). // // All values with no default are reset to their zero value. func (o *GetLogProofParams) SetDefaults() { var ( firstSizeDefault = int64(1) ) val := GetLogProofParams{ FirstSize: &firstSizeDefault, } val.timeout = o.timeout val.Context = o.Context val.HTTPClient = o.HTTPClient *o = val } // WithTimeout adds the timeout to the get log proof params func (o *GetLogProofParams) WithTimeout(timeout time.Duration) *GetLogProofParams { o.SetTimeout(timeout) return o } // SetTimeout adds the timeout to the get log proof params func (o *GetLogProofParams) SetTimeout(timeout time.Duration) { o.timeout = timeout } // WithContext adds the context to the get log proof params func (o *GetLogProofParams) WithContext(ctx context.Context) *GetLogProofParams { o.SetContext(ctx) return o } // SetContext adds the context to the get log proof params func (o *GetLogProofParams) SetContext(ctx context.Context) { o.Context = ctx } // WithHTTPClient adds the HTTPClient to the get log proof params func (o *GetLogProofParams) WithHTTPClient(client *http.Client) *GetLogProofParams { o.SetHTTPClient(client) return o } // SetHTTPClient adds the HTTPClient to the get log proof params func (o *GetLogProofParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } // WithFirstSize adds the firstSize to the get log proof params func (o *GetLogProofParams) WithFirstSize(firstSize *int64) *GetLogProofParams { o.SetFirstSize(firstSize) return o } // SetFirstSize adds the firstSize to the get log proof params func (o *GetLogProofParams) SetFirstSize(firstSize *int64) { o.FirstSize = firstSize } // WithLastSize adds the lastSize to the get log proof params func (o *GetLogProofParams) WithLastSize(lastSize int64) *GetLogProofParams { o.SetLastSize(lastSize) return o } // SetLastSize adds the lastSize to the get log proof params func (o *GetLogProofParams) SetLastSize(lastSize int64) { o.LastSize = lastSize } // WithTreeID adds the treeID to the get log proof params func (o *GetLogProofParams) WithTreeID(treeID *string) *GetLogProofParams { o.SetTreeID(treeID) return o } // SetTreeID adds the treeId to the get log proof params func (o *GetLogProofParams) SetTreeID(treeID *string) { o.TreeID = treeID } // WriteToRequest writes these params to a swagger request func (o *GetLogProofParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { if err := r.SetTimeout(o.timeout); err != nil { return err } var res []error if o.FirstSize != nil { // query param firstSize var qrFirstSize int64 if o.FirstSize != nil { qrFirstSize = *o.FirstSize } qFirstSize := swag.FormatInt64(qrFirstSize) if qFirstSize != "" { if err := r.SetQueryParam("firstSize", qFirstSize); err != nil { return err } } } // query param lastSize qrLastSize := o.LastSize qLastSize := swag.FormatInt64(qrLastSize) if qLastSize != "" { if err := r.SetQueryParam("lastSize", qLastSize); err != nil { return err } } if o.TreeID != nil { // query param treeID var qrTreeID string if o.TreeID != nil { qrTreeID = *o.TreeID } qTreeID := qrTreeID if qTreeID != "" { if err := r.SetQueryParam("treeID", qTreeID); err != nil { return err } } } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } rekor-1.3.5/pkg/generated/client/tlog/get_log_proof_responses.go000066400000000000000000000174131455727245600251100ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "fmt" "io" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" ) // GetLogProofReader is a Reader for the GetLogProof structure. type GetLogProofReader struct { formats strfmt.Registry } // ReadResponse reads a server response into the received o. func (o *GetLogProofReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { switch response.Code() { case 200: result := NewGetLogProofOK() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return result, nil case 400: result := NewGetLogProofBadRequest() if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } return nil, result default: result := NewGetLogProofDefault(response.Code()) if err := result.readResponse(response, consumer, o.formats); err != nil { return nil, err } if response.Code()/100 == 2 { return result, nil } return nil, result } } // NewGetLogProofOK creates a GetLogProofOK with default headers values func NewGetLogProofOK() *GetLogProofOK { return &GetLogProofOK{} } /* GetLogProofOK describes a response with status code 200, with default header values. All hashes required to compute the consistency proof */ type GetLogProofOK struct { Payload *models.ConsistencyProof } // IsSuccess returns true when this get log proof o k response has a 2xx status code func (o *GetLogProofOK) IsSuccess() bool { return true } // IsRedirect returns true when this get log proof o k response has a 3xx status code func (o *GetLogProofOK) IsRedirect() bool { return false } // IsClientError returns true when this get log proof o k response has a 4xx status code func (o *GetLogProofOK) IsClientError() bool { return false } // IsServerError returns true when this get log proof o k response has a 5xx status code func (o *GetLogProofOK) IsServerError() bool { return false } // IsCode returns true when this get log proof o k response a status code equal to that given func (o *GetLogProofOK) IsCode(code int) bool { return code == 200 } // Code gets the status code for the get log proof o k response func (o *GetLogProofOK) Code() int { return 200 } func (o *GetLogProofOK) Error() string { return fmt.Sprintf("[GET /api/v1/log/proof][%d] getLogProofOK %+v", 200, o.Payload) } func (o *GetLogProofOK) String() string { return fmt.Sprintf("[GET /api/v1/log/proof][%d] getLogProofOK %+v", 200, o.Payload) } func (o *GetLogProofOK) GetPayload() *models.ConsistencyProof { return o.Payload } func (o *GetLogProofOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.ConsistencyProof) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } // NewGetLogProofBadRequest creates a GetLogProofBadRequest with default headers values func NewGetLogProofBadRequest() *GetLogProofBadRequest { return &GetLogProofBadRequest{} } /* GetLogProofBadRequest describes a response with status code 400, with default header values. The content supplied to the server was invalid */ type GetLogProofBadRequest struct { Payload *models.Error } // IsSuccess returns true when this get log proof bad request response has a 2xx status code func (o *GetLogProofBadRequest) IsSuccess() bool { return false } // IsRedirect returns true when this get log proof bad request response has a 3xx status code func (o *GetLogProofBadRequest) IsRedirect() bool { return false } // IsClientError returns true when this get log proof bad request response has a 4xx status code func (o *GetLogProofBadRequest) IsClientError() bool { return true } // IsServerError returns true when this get log proof bad request response has a 5xx status code func (o *GetLogProofBadRequest) IsServerError() bool { return false } // IsCode returns true when this get log proof bad request response a status code equal to that given func (o *GetLogProofBadRequest) IsCode(code int) bool { return code == 400 } // Code gets the status code for the get log proof bad request response func (o *GetLogProofBadRequest) Code() int { return 400 } func (o *GetLogProofBadRequest) Error() string { return fmt.Sprintf("[GET /api/v1/log/proof][%d] getLogProofBadRequest %+v", 400, o.Payload) } func (o *GetLogProofBadRequest) String() string { return fmt.Sprintf("[GET /api/v1/log/proof][%d] getLogProofBadRequest %+v", 400, o.Payload) } func (o *GetLogProofBadRequest) GetPayload() *models.Error { return o.Payload } func (o *GetLogProofBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } // NewGetLogProofDefault creates a GetLogProofDefault with default headers values func NewGetLogProofDefault(code int) *GetLogProofDefault { return &GetLogProofDefault{ _statusCode: code, } } /* GetLogProofDefault describes a response with status code -1, with default header values. There was an internal error in the server while processing the request */ type GetLogProofDefault struct { _statusCode int Payload *models.Error } // IsSuccess returns true when this get log proof default response has a 2xx status code func (o *GetLogProofDefault) IsSuccess() bool { return o._statusCode/100 == 2 } // IsRedirect returns true when this get log proof default response has a 3xx status code func (o *GetLogProofDefault) IsRedirect() bool { return o._statusCode/100 == 3 } // IsClientError returns true when this get log proof default response has a 4xx status code func (o *GetLogProofDefault) IsClientError() bool { return o._statusCode/100 == 4 } // IsServerError returns true when this get log proof default response has a 5xx status code func (o *GetLogProofDefault) IsServerError() bool { return o._statusCode/100 == 5 } // IsCode returns true when this get log proof default response a status code equal to that given func (o *GetLogProofDefault) IsCode(code int) bool { return o._statusCode == code } // Code gets the status code for the get log proof default response func (o *GetLogProofDefault) Code() int { return o._statusCode } func (o *GetLogProofDefault) Error() string { return fmt.Sprintf("[GET /api/v1/log/proof][%d] getLogProof default %+v", o._statusCode, o.Payload) } func (o *GetLogProofDefault) String() string { return fmt.Sprintf("[GET /api/v1/log/proof][%d] getLogProof default %+v", o._statusCode, o.Payload) } func (o *GetLogProofDefault) GetPayload() *models.Error { return o.Payload } func (o *GetLogProofDefault) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { o.Payload = new(models.Error) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { return err } return nil } rekor-1.3.5/pkg/generated/client/tlog/tlog_client.go000066400000000000000000000103211455727245600224540ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" ) // New creates a new tlog API client. func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService { return &Client{transport: transport, formats: formats} } /* Client for tlog API */ type Client struct { transport runtime.ClientTransport formats strfmt.Registry } // ClientOption is the option for Client methods type ClientOption func(*runtime.ClientOperation) // ClientService is the interface for Client methods type ClientService interface { GetLogInfo(params *GetLogInfoParams, opts ...ClientOption) (*GetLogInfoOK, error) GetLogProof(params *GetLogProofParams, opts ...ClientOption) (*GetLogProofOK, error) SetTransport(transport runtime.ClientTransport) } /* GetLogInfo gets information about the current state of the transparency log Returns the current root hash and size of the merkle tree used to store the log entries. */ func (a *Client) GetLogInfo(params *GetLogInfoParams, opts ...ClientOption) (*GetLogInfoOK, error) { // TODO: Validate the params before sending if params == nil { params = NewGetLogInfoParams() } op := &runtime.ClientOperation{ ID: "getLogInfo", Method: "GET", PathPattern: "/api/v1/log", ProducesMediaTypes: []string{"application/json"}, ConsumesMediaTypes: []string{"application/json"}, Schemes: []string{"http"}, Params: params, Reader: &GetLogInfoReader{formats: a.formats}, Context: params.Context, Client: params.HTTPClient, } for _, opt := range opts { opt(op) } result, err := a.transport.Submit(op) if err != nil { return nil, err } success, ok := result.(*GetLogInfoOK) if ok { return success, nil } // unexpected success response unexpectedSuccess := result.(*GetLogInfoDefault) return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } /* GetLogProof gets information required to generate a consistency proof for the transparency log Returns a list of hashes for specified tree sizes that can be used to confirm the consistency of the transparency log */ func (a *Client) GetLogProof(params *GetLogProofParams, opts ...ClientOption) (*GetLogProofOK, error) { // TODO: Validate the params before sending if params == nil { params = NewGetLogProofParams() } op := &runtime.ClientOperation{ ID: "getLogProof", Method: "GET", PathPattern: "/api/v1/log/proof", ProducesMediaTypes: []string{"application/json"}, ConsumesMediaTypes: []string{"application/json"}, Schemes: []string{"http"}, Params: params, Reader: &GetLogProofReader{formats: a.formats}, Context: params.Context, Client: params.HTTPClient, } for _, opt := range opts { opt(op) } result, err := a.transport.Submit(op) if err != nil { return nil, err } success, ok := result.(*GetLogProofOK) if ok { return success, nil } // unexpected success response unexpectedSuccess := result.(*GetLogProofDefault) return nil, runtime.NewAPIError("unexpected success response: content available as default response in error", unexpectedSuccess, unexpectedSuccess.Code()) } // SetTransport changes the transport on the client func (a *Client) SetTransport(transport runtime.ClientTransport) { a.transport = transport } rekor-1.3.5/pkg/generated/models/000077500000000000000000000000001455727245600166655ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/models/alpine.go000066400000000000000000000117451455727245600204740ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // Alpine Alpine package // // swagger:model alpine type Alpine struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec AlpineSchema `json:"spec"` } // Kind gets the kind of this subtype func (m *Alpine) Kind() string { return "alpine" } // SetKind sets the kind of this subtype func (m *Alpine) SetKind(val string) { } // UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure func (m *Alpine) UnmarshalJSON(raw []byte) error { var data struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec AlpineSchema `json:"spec"` } buf := bytes.NewBuffer(raw) dec := json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&data); err != nil { return err } var base struct { /* Just the base type fields. Used for unmashalling polymorphic types.*/ Kind string `json:"kind"` } buf = bytes.NewBuffer(raw) dec = json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&base); err != nil { return err } var result Alpine if base.Kind != result.Kind() { /* Not the type we're looking for. */ return errors.New(422, "invalid kind value: %q", base.Kind) } result.APIVersion = data.APIVersion result.Spec = data.Spec *m = result return nil } // MarshalJSON marshals this object with a polymorphic type to a JSON structure func (m Alpine) MarshalJSON() ([]byte, error) { var b1, b2, b3 []byte var err error b1, err = json.Marshal(struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec AlpineSchema `json:"spec"` }{ APIVersion: m.APIVersion, Spec: m.Spec, }) if err != nil { return nil, err } b2, err = json.Marshal(struct { Kind string `json:"kind"` }{ Kind: m.Kind(), }) if err != nil { return nil, err } return swag.ConcatJSON(b1, b2, b3), nil } // Validate validates this alpine func (m *Alpine) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAPIVersion(formats); err != nil { res = append(res, err) } if err := m.validateSpec(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *Alpine) validateAPIVersion(formats strfmt.Registry) error { if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil { return err } if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil { return err } return nil } func (m *Alpine) validateSpec(formats strfmt.Registry) error { if m.Spec == nil { return errors.Required("spec", "body", nil) } return nil } // ContextValidate validate this alpine based on the context it is used func (m *Alpine) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *Alpine) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *Alpine) UnmarshalBinary(b []byte) error { var res Alpine if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/alpine_schema.go000066400000000000000000000016611455727245600220100ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command // AlpineSchema Alpine Package Schema // // # Schema for Alpine package objects // // swagger:model alpineSchema type AlpineSchema interface{} rekor-1.3.5/pkg/generated/models/alpine_v001_schema.go000066400000000000000000000263001455727245600225530ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // AlpineV001Schema Alpine v0.0.1 Schema // // # Schema for Alpine Package entries // // swagger:model alpineV001Schema type AlpineV001Schema struct { // package // Required: true Package *AlpineV001SchemaPackage `json:"package"` // public key // Required: true PublicKey *AlpineV001SchemaPublicKey `json:"publicKey"` } // Validate validates this alpine v001 schema func (m *AlpineV001Schema) Validate(formats strfmt.Registry) error { var res []error if err := m.validatePackage(formats); err != nil { res = append(res, err) } if err := m.validatePublicKey(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *AlpineV001Schema) validatePackage(formats strfmt.Registry) error { if err := validate.Required("package", "body", m.Package); err != nil { return err } if m.Package != nil { if err := m.Package.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("package") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("package") } return err } } return nil } func (m *AlpineV001Schema) validatePublicKey(formats strfmt.Registry) error { if err := validate.Required("publicKey", "body", m.PublicKey); err != nil { return err } if m.PublicKey != nil { if err := m.PublicKey.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("publicKey") } return err } } return nil } // ContextValidate validate this alpine v001 schema based on the context it is used func (m *AlpineV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidatePackage(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidatePublicKey(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *AlpineV001Schema) contextValidatePackage(ctx context.Context, formats strfmt.Registry) error { if m.Package != nil { if err := m.Package.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("package") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("package") } return err } } return nil } func (m *AlpineV001Schema) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error { if m.PublicKey != nil { if err := m.PublicKey.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("publicKey") } return err } } return nil } // MarshalBinary interface implementation func (m *AlpineV001Schema) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *AlpineV001Schema) UnmarshalBinary(b []byte) error { var res AlpineV001Schema if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // AlpineV001SchemaPackage Information about the package associated with the entry // // swagger:model AlpineV001SchemaPackage type AlpineV001SchemaPackage struct { // Specifies the package inline within the document // Format: byte Content strfmt.Base64 `json:"content,omitempty"` // hash Hash *AlpineV001SchemaPackageHash `json:"hash,omitempty"` // Values of the .PKGINFO key / value pairs // Read Only: true Pkginfo map[string]string `json:"pkginfo,omitempty"` } // Validate validates this alpine v001 schema package func (m *AlpineV001SchemaPackage) Validate(formats strfmt.Registry) error { var res []error if err := m.validateHash(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *AlpineV001SchemaPackage) validateHash(formats strfmt.Registry) error { if swag.IsZero(m.Hash) { // not required return nil } if m.Hash != nil { if err := m.Hash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("package" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("package" + "." + "hash") } return err } } return nil } // ContextValidate validate this alpine v001 schema package based on the context it is used func (m *AlpineV001SchemaPackage) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateHash(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidatePkginfo(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *AlpineV001SchemaPackage) contextValidateHash(ctx context.Context, formats strfmt.Registry) error { if m.Hash != nil { if swag.IsZero(m.Hash) { // not required return nil } if err := m.Hash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("package" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("package" + "." + "hash") } return err } } return nil } func (m *AlpineV001SchemaPackage) contextValidatePkginfo(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *AlpineV001SchemaPackage) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *AlpineV001SchemaPackage) UnmarshalBinary(b []byte) error { var res AlpineV001SchemaPackage if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // AlpineV001SchemaPackageHash Specifies the hash algorithm and value for the package // // swagger:model AlpineV001SchemaPackageHash type AlpineV001SchemaPackageHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The hash value for the package // Required: true Value *string `json:"value"` } // Validate validates this alpine v001 schema package hash func (m *AlpineV001SchemaPackageHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var alpineV001SchemaPackageHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { alpineV001SchemaPackageHashTypeAlgorithmPropEnum = append(alpineV001SchemaPackageHashTypeAlgorithmPropEnum, v) } } const ( // AlpineV001SchemaPackageHashAlgorithmSha256 captures enum value "sha256" AlpineV001SchemaPackageHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *AlpineV001SchemaPackageHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, alpineV001SchemaPackageHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *AlpineV001SchemaPackageHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("package"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("package"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *AlpineV001SchemaPackageHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("package"+"."+"hash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validate this alpine v001 schema package hash based on the context it is used func (m *AlpineV001SchemaPackageHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *AlpineV001SchemaPackageHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *AlpineV001SchemaPackageHash) UnmarshalBinary(b []byte) error { var res AlpineV001SchemaPackageHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // AlpineV001SchemaPublicKey The public key that can verify the package signature // // swagger:model AlpineV001SchemaPublicKey type AlpineV001SchemaPublicKey struct { // Specifies the content of the public key inline within the document // Required: true // Format: byte Content *strfmt.Base64 `json:"content"` } // Validate validates this alpine v001 schema public key func (m *AlpineV001SchemaPublicKey) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *AlpineV001SchemaPublicKey) validateContent(formats strfmt.Registry) error { if err := validate.Required("publicKey"+"."+"content", "body", m.Content); err != nil { return err } return nil } // ContextValidate validates this alpine v001 schema public key based on context it is used func (m *AlpineV001SchemaPublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *AlpineV001SchemaPublicKey) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *AlpineV001SchemaPublicKey) UnmarshalBinary(b []byte) error { var res AlpineV001SchemaPublicKey if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/consistency_proof.go000066400000000000000000000055631455727245600227730ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "strconv" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // ConsistencyProof consistency proof // // swagger:model ConsistencyProof type ConsistencyProof struct { // hashes // Required: true Hashes []string `json:"hashes"` // The hash value stored at the root of the merkle tree at the time the proof was generated // Required: true // Pattern: ^[0-9a-fA-F]{64}$ RootHash *string `json:"rootHash"` } // Validate validates this consistency proof func (m *ConsistencyProof) Validate(formats strfmt.Registry) error { var res []error if err := m.validateHashes(formats); err != nil { res = append(res, err) } if err := m.validateRootHash(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *ConsistencyProof) validateHashes(formats strfmt.Registry) error { if err := validate.Required("hashes", "body", m.Hashes); err != nil { return err } for i := 0; i < len(m.Hashes); i++ { if err := validate.Pattern("hashes"+"."+strconv.Itoa(i), "body", m.Hashes[i], `^[0-9a-fA-F]{64}$`); err != nil { return err } } return nil } func (m *ConsistencyProof) validateRootHash(formats strfmt.Registry) error { if err := validate.Required("rootHash", "body", m.RootHash); err != nil { return err } if err := validate.Pattern("rootHash", "body", *m.RootHash, `^[0-9a-fA-F]{64}$`); err != nil { return err } return nil } // ContextValidate validates this consistency proof based on context it is used func (m *ConsistencyProof) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *ConsistencyProof) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *ConsistencyProof) UnmarshalBinary(b []byte) error { var res ConsistencyProof if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/cose.go000066400000000000000000000116701455727245600201520ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // Cose COSE object // // swagger:model cose type Cose struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec CoseSchema `json:"spec"` } // Kind gets the kind of this subtype func (m *Cose) Kind() string { return "cose" } // SetKind sets the kind of this subtype func (m *Cose) SetKind(val string) { } // UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure func (m *Cose) UnmarshalJSON(raw []byte) error { var data struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec CoseSchema `json:"spec"` } buf := bytes.NewBuffer(raw) dec := json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&data); err != nil { return err } var base struct { /* Just the base type fields. Used for unmashalling polymorphic types.*/ Kind string `json:"kind"` } buf = bytes.NewBuffer(raw) dec = json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&base); err != nil { return err } var result Cose if base.Kind != result.Kind() { /* Not the type we're looking for. */ return errors.New(422, "invalid kind value: %q", base.Kind) } result.APIVersion = data.APIVersion result.Spec = data.Spec *m = result return nil } // MarshalJSON marshals this object with a polymorphic type to a JSON structure func (m Cose) MarshalJSON() ([]byte, error) { var b1, b2, b3 []byte var err error b1, err = json.Marshal(struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec CoseSchema `json:"spec"` }{ APIVersion: m.APIVersion, Spec: m.Spec, }) if err != nil { return nil, err } b2, err = json.Marshal(struct { Kind string `json:"kind"` }{ Kind: m.Kind(), }) if err != nil { return nil, err } return swag.ConcatJSON(b1, b2, b3), nil } // Validate validates this cose func (m *Cose) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAPIVersion(formats); err != nil { res = append(res, err) } if err := m.validateSpec(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *Cose) validateAPIVersion(formats strfmt.Registry) error { if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil { return err } if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil { return err } return nil } func (m *Cose) validateSpec(formats strfmt.Registry) error { if m.Spec == nil { return errors.Required("spec", "body", nil) } return nil } // ContextValidate validate this cose based on the context it is used func (m *Cose) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *Cose) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *Cose) UnmarshalBinary(b []byte) error { var res Cose if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/cose_schema.go000066400000000000000000000016271455727245600214730ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command // CoseSchema COSE Schema // // # COSE for Rekord objects // // swagger:model coseSchema type CoseSchema interface{} rekor-1.3.5/pkg/generated/models/cose_v001_schema.go000066400000000000000000000317441455727245600222440ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // CoseV001Schema cose v0.0.1 Schema // // # Schema for cose object // // swagger:model coseV001Schema type CoseV001Schema struct { // data Data *CoseV001SchemaData `json:"data,omitempty"` // The COSE Sign1 Message // Format: byte Message strfmt.Base64 `json:"message,omitempty"` // The public key that can verify the signature // Required: true // Format: byte PublicKey *strfmt.Base64 `json:"publicKey"` } // Validate validates this cose v001 schema func (m *CoseV001Schema) Validate(formats strfmt.Registry) error { var res []error if err := m.validateData(formats); err != nil { res = append(res, err) } if err := m.validatePublicKey(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *CoseV001Schema) validateData(formats strfmt.Registry) error { if swag.IsZero(m.Data) { // not required return nil } if m.Data != nil { if err := m.Data.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data") } return err } } return nil } func (m *CoseV001Schema) validatePublicKey(formats strfmt.Registry) error { if err := validate.Required("publicKey", "body", m.PublicKey); err != nil { return err } return nil } // ContextValidate validate this cose v001 schema based on the context it is used func (m *CoseV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateData(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *CoseV001Schema) contextValidateData(ctx context.Context, formats strfmt.Registry) error { if m.Data != nil { if swag.IsZero(m.Data) { // not required return nil } if err := m.Data.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data") } return err } } return nil } // MarshalBinary interface implementation func (m *CoseV001Schema) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *CoseV001Schema) UnmarshalBinary(b []byte) error { var res CoseV001Schema if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // CoseV001SchemaData Information about the content associated with the entry // // swagger:model CoseV001SchemaData type CoseV001SchemaData struct { // Specifies the additional authenticated data required to verify the signature // Format: byte Aad strfmt.Base64 `json:"aad,omitempty"` // envelope hash EnvelopeHash *CoseV001SchemaDataEnvelopeHash `json:"envelopeHash,omitempty"` // payload hash PayloadHash *CoseV001SchemaDataPayloadHash `json:"payloadHash,omitempty"` } // Validate validates this cose v001 schema data func (m *CoseV001SchemaData) Validate(formats strfmt.Registry) error { var res []error if err := m.validateEnvelopeHash(formats); err != nil { res = append(res, err) } if err := m.validatePayloadHash(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *CoseV001SchemaData) validateEnvelopeHash(formats strfmt.Registry) error { if swag.IsZero(m.EnvelopeHash) { // not required return nil } if m.EnvelopeHash != nil { if err := m.EnvelopeHash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data" + "." + "envelopeHash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data" + "." + "envelopeHash") } return err } } return nil } func (m *CoseV001SchemaData) validatePayloadHash(formats strfmt.Registry) error { if swag.IsZero(m.PayloadHash) { // not required return nil } if m.PayloadHash != nil { if err := m.PayloadHash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data" + "." + "payloadHash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data" + "." + "payloadHash") } return err } } return nil } // ContextValidate validate this cose v001 schema data based on the context it is used func (m *CoseV001SchemaData) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateEnvelopeHash(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidatePayloadHash(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *CoseV001SchemaData) contextValidateEnvelopeHash(ctx context.Context, formats strfmt.Registry) error { if m.EnvelopeHash != nil { if swag.IsZero(m.EnvelopeHash) { // not required return nil } if err := m.EnvelopeHash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data" + "." + "envelopeHash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data" + "." + "envelopeHash") } return err } } return nil } func (m *CoseV001SchemaData) contextValidatePayloadHash(ctx context.Context, formats strfmt.Registry) error { if m.PayloadHash != nil { if swag.IsZero(m.PayloadHash) { // not required return nil } if err := m.PayloadHash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data" + "." + "payloadHash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data" + "." + "payloadHash") } return err } } return nil } // MarshalBinary interface implementation func (m *CoseV001SchemaData) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *CoseV001SchemaData) UnmarshalBinary(b []byte) error { var res CoseV001SchemaData if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // CoseV001SchemaDataEnvelopeHash Specifies the hash algorithm and value for the COSE envelope // // swagger:model CoseV001SchemaDataEnvelopeHash type CoseV001SchemaDataEnvelopeHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The hash value for the envelope // Required: true Value *string `json:"value"` } // Validate validates this cose v001 schema data envelope hash func (m *CoseV001SchemaDataEnvelopeHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var coseV001SchemaDataEnvelopeHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { coseV001SchemaDataEnvelopeHashTypeAlgorithmPropEnum = append(coseV001SchemaDataEnvelopeHashTypeAlgorithmPropEnum, v) } } const ( // CoseV001SchemaDataEnvelopeHashAlgorithmSha256 captures enum value "sha256" CoseV001SchemaDataEnvelopeHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *CoseV001SchemaDataEnvelopeHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, coseV001SchemaDataEnvelopeHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *CoseV001SchemaDataEnvelopeHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("data"+"."+"envelopeHash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("data"+"."+"envelopeHash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *CoseV001SchemaDataEnvelopeHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("data"+"."+"envelopeHash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validate this cose v001 schema data envelope hash based on the context it is used func (m *CoseV001SchemaDataEnvelopeHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *CoseV001SchemaDataEnvelopeHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *CoseV001SchemaDataEnvelopeHash) UnmarshalBinary(b []byte) error { var res CoseV001SchemaDataEnvelopeHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // CoseV001SchemaDataPayloadHash Specifies the hash algorithm and value for the content // // swagger:model CoseV001SchemaDataPayloadHash type CoseV001SchemaDataPayloadHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The hash value for the content // Required: true Value *string `json:"value"` } // Validate validates this cose v001 schema data payload hash func (m *CoseV001SchemaDataPayloadHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var coseV001SchemaDataPayloadHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { coseV001SchemaDataPayloadHashTypeAlgorithmPropEnum = append(coseV001SchemaDataPayloadHashTypeAlgorithmPropEnum, v) } } const ( // CoseV001SchemaDataPayloadHashAlgorithmSha256 captures enum value "sha256" CoseV001SchemaDataPayloadHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *CoseV001SchemaDataPayloadHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, coseV001SchemaDataPayloadHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *CoseV001SchemaDataPayloadHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("data"+"."+"payloadHash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("data"+"."+"payloadHash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *CoseV001SchemaDataPayloadHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("data"+"."+"payloadHash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validate this cose v001 schema data payload hash based on the context it is used func (m *CoseV001SchemaDataPayloadHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *CoseV001SchemaDataPayloadHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *CoseV001SchemaDataPayloadHash) UnmarshalBinary(b []byte) error { var res CoseV001SchemaDataPayloadHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/dsse.go000066400000000000000000000116721455727245600201610ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // DSSE DSSE envelope // // swagger:model dsse type DSSE struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec DSSESchema `json:"spec"` } // Kind gets the kind of this subtype func (m *DSSE) Kind() string { return "dsse" } // SetKind sets the kind of this subtype func (m *DSSE) SetKind(val string) { } // UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure func (m *DSSE) UnmarshalJSON(raw []byte) error { var data struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec DSSESchema `json:"spec"` } buf := bytes.NewBuffer(raw) dec := json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&data); err != nil { return err } var base struct { /* Just the base type fields. Used for unmashalling polymorphic types.*/ Kind string `json:"kind"` } buf = bytes.NewBuffer(raw) dec = json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&base); err != nil { return err } var result DSSE if base.Kind != result.Kind() { /* Not the type we're looking for. */ return errors.New(422, "invalid kind value: %q", base.Kind) } result.APIVersion = data.APIVersion result.Spec = data.Spec *m = result return nil } // MarshalJSON marshals this object with a polymorphic type to a JSON structure func (m DSSE) MarshalJSON() ([]byte, error) { var b1, b2, b3 []byte var err error b1, err = json.Marshal(struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec DSSESchema `json:"spec"` }{ APIVersion: m.APIVersion, Spec: m.Spec, }) if err != nil { return nil, err } b2, err = json.Marshal(struct { Kind string `json:"kind"` }{ Kind: m.Kind(), }) if err != nil { return nil, err } return swag.ConcatJSON(b1, b2, b3), nil } // Validate validates this dsse func (m *DSSE) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAPIVersion(formats); err != nil { res = append(res, err) } if err := m.validateSpec(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *DSSE) validateAPIVersion(formats strfmt.Registry) error { if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil { return err } if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil { return err } return nil } func (m *DSSE) validateSpec(formats strfmt.Registry) error { if m.Spec == nil { return errors.Required("spec", "body", nil) } return nil } // ContextValidate validate this dsse based on the context it is used func (m *DSSE) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *DSSE) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *DSSE) UnmarshalBinary(b []byte) error { var res DSSE if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/dsse_schema.go000066400000000000000000000016411455727245600214740ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command // DSSESchema DSSE Schema // // log entry schema for dsse envelopes // // swagger:model dsseSchema type DSSESchema interface{} rekor-1.3.5/pkg/generated/models/dsse_v001_schema.go000066400000000000000000000431151455727245600222440ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "encoding/json" "strconv" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // DSSEV001Schema DSSE v0.0.1 Schema // // # Schema for DSSE envelopes // // swagger:model dsseV001Schema type DSSEV001Schema struct { // envelope hash EnvelopeHash *DSSEV001SchemaEnvelopeHash `json:"envelopeHash,omitempty"` // payload hash PayloadHash *DSSEV001SchemaPayloadHash `json:"payloadHash,omitempty"` // proposed content ProposedContent *DSSEV001SchemaProposedContent `json:"proposedContent,omitempty"` // extracted collection of all signatures of the envelope's payload; elements will be sorted by lexicographical order of the base64 encoded signature strings // Read Only: true // Min Items: 1 Signatures []*DSSEV001SchemaSignaturesItems0 `json:"signatures"` } // Validate validates this dsse v001 schema func (m *DSSEV001Schema) Validate(formats strfmt.Registry) error { var res []error if err := m.validateEnvelopeHash(formats); err != nil { res = append(res, err) } if err := m.validatePayloadHash(formats); err != nil { res = append(res, err) } if err := m.validateProposedContent(formats); err != nil { res = append(res, err) } if err := m.validateSignatures(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *DSSEV001Schema) validateEnvelopeHash(formats strfmt.Registry) error { if swag.IsZero(m.EnvelopeHash) { // not required return nil } if m.EnvelopeHash != nil { if err := m.EnvelopeHash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("envelopeHash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("envelopeHash") } return err } } return nil } func (m *DSSEV001Schema) validatePayloadHash(formats strfmt.Registry) error { if swag.IsZero(m.PayloadHash) { // not required return nil } if m.PayloadHash != nil { if err := m.PayloadHash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("payloadHash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("payloadHash") } return err } } return nil } func (m *DSSEV001Schema) validateProposedContent(formats strfmt.Registry) error { if swag.IsZero(m.ProposedContent) { // not required return nil } if m.ProposedContent != nil { if err := m.ProposedContent.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("proposedContent") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("proposedContent") } return err } } return nil } func (m *DSSEV001Schema) validateSignatures(formats strfmt.Registry) error { if swag.IsZero(m.Signatures) { // not required return nil } iSignaturesSize := int64(len(m.Signatures)) if err := validate.MinItems("signatures", "body", iSignaturesSize, 1); err != nil { return err } for i := 0; i < len(m.Signatures); i++ { if swag.IsZero(m.Signatures[i]) { // not required continue } if m.Signatures[i] != nil { if err := m.Signatures[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signatures" + "." + strconv.Itoa(i)) } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signatures" + "." + strconv.Itoa(i)) } return err } } } return nil } // ContextValidate validate this dsse v001 schema based on the context it is used func (m *DSSEV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateEnvelopeHash(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidatePayloadHash(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidateProposedContent(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidateSignatures(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *DSSEV001Schema) contextValidateEnvelopeHash(ctx context.Context, formats strfmt.Registry) error { if m.EnvelopeHash != nil { if swag.IsZero(m.EnvelopeHash) { // not required return nil } if err := m.EnvelopeHash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("envelopeHash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("envelopeHash") } return err } } return nil } func (m *DSSEV001Schema) contextValidatePayloadHash(ctx context.Context, formats strfmt.Registry) error { if m.PayloadHash != nil { if swag.IsZero(m.PayloadHash) { // not required return nil } if err := m.PayloadHash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("payloadHash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("payloadHash") } return err } } return nil } func (m *DSSEV001Schema) contextValidateProposedContent(ctx context.Context, formats strfmt.Registry) error { if m.ProposedContent != nil { if swag.IsZero(m.ProposedContent) { // not required return nil } if err := m.ProposedContent.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("proposedContent") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("proposedContent") } return err } } return nil } func (m *DSSEV001Schema) contextValidateSignatures(ctx context.Context, formats strfmt.Registry) error { if err := validate.ReadOnly(ctx, "signatures", "body", []*DSSEV001SchemaSignaturesItems0(m.Signatures)); err != nil { return err } for i := 0; i < len(m.Signatures); i++ { if m.Signatures[i] != nil { if swag.IsZero(m.Signatures[i]) { // not required return nil } if err := m.Signatures[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signatures" + "." + strconv.Itoa(i)) } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signatures" + "." + strconv.Itoa(i)) } return err } } } return nil } // MarshalBinary interface implementation func (m *DSSEV001Schema) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *DSSEV001Schema) UnmarshalBinary(b []byte) error { var res DSSEV001Schema if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // DSSEV001SchemaEnvelopeHash Specifies the hash algorithm and value encompassing the entire envelope sent to Rekor // // swagger:model DSSEV001SchemaEnvelopeHash type DSSEV001SchemaEnvelopeHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The value of the computed digest over the entire envelope // Required: true Value *string `json:"value"` } // Validate validates this DSSE v001 schema envelope hash func (m *DSSEV001SchemaEnvelopeHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var dsseV001SchemaEnvelopeHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { dsseV001SchemaEnvelopeHashTypeAlgorithmPropEnum = append(dsseV001SchemaEnvelopeHashTypeAlgorithmPropEnum, v) } } const ( // DSSEV001SchemaEnvelopeHashAlgorithmSha256 captures enum value "sha256" DSSEV001SchemaEnvelopeHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *DSSEV001SchemaEnvelopeHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, dsseV001SchemaEnvelopeHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *DSSEV001SchemaEnvelopeHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("envelopeHash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("envelopeHash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *DSSEV001SchemaEnvelopeHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("envelopeHash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validate this DSSE v001 schema envelope hash based on the context it is used func (m *DSSEV001SchemaEnvelopeHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *DSSEV001SchemaEnvelopeHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *DSSEV001SchemaEnvelopeHash) UnmarshalBinary(b []byte) error { var res DSSEV001SchemaEnvelopeHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // DSSEV001SchemaPayloadHash Specifies the hash algorithm and value covering the payload within the DSSE envelope // // swagger:model DSSEV001SchemaPayloadHash type DSSEV001SchemaPayloadHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The value of the computed digest over the payload within the envelope // Required: true Value *string `json:"value"` } // Validate validates this DSSE v001 schema payload hash func (m *DSSEV001SchemaPayloadHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var dsseV001SchemaPayloadHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { dsseV001SchemaPayloadHashTypeAlgorithmPropEnum = append(dsseV001SchemaPayloadHashTypeAlgorithmPropEnum, v) } } const ( // DSSEV001SchemaPayloadHashAlgorithmSha256 captures enum value "sha256" DSSEV001SchemaPayloadHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *DSSEV001SchemaPayloadHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, dsseV001SchemaPayloadHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *DSSEV001SchemaPayloadHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("payloadHash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("payloadHash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *DSSEV001SchemaPayloadHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("payloadHash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validate this DSSE v001 schema payload hash based on the context it is used func (m *DSSEV001SchemaPayloadHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *DSSEV001SchemaPayloadHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *DSSEV001SchemaPayloadHash) UnmarshalBinary(b []byte) error { var res DSSEV001SchemaPayloadHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // DSSEV001SchemaProposedContent DSSE v001 schema proposed content // // swagger:model DSSEV001SchemaProposedContent type DSSEV001SchemaProposedContent struct { // DSSE envelope specified as a stringified JSON object // Required: true Envelope *string `json:"envelope"` // collection of all verification material (e.g. public keys or certificates) used to verify signatures over envelope's payload, specified as base64-encoded strings // Required: true // Min Items: 1 Verifiers []strfmt.Base64 `json:"verifiers"` } // Validate validates this DSSE v001 schema proposed content func (m *DSSEV001SchemaProposedContent) Validate(formats strfmt.Registry) error { var res []error if err := m.validateEnvelope(formats); err != nil { res = append(res, err) } if err := m.validateVerifiers(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *DSSEV001SchemaProposedContent) validateEnvelope(formats strfmt.Registry) error { if err := validate.Required("proposedContent"+"."+"envelope", "body", m.Envelope); err != nil { return err } return nil } func (m *DSSEV001SchemaProposedContent) validateVerifiers(formats strfmt.Registry) error { if err := validate.Required("proposedContent"+"."+"verifiers", "body", m.Verifiers); err != nil { return err } iVerifiersSize := int64(len(m.Verifiers)) if err := validate.MinItems("proposedContent"+"."+"verifiers", "body", iVerifiersSize, 1); err != nil { return err } return nil } // ContextValidate validates this DSSE v001 schema proposed content based on context it is used func (m *DSSEV001SchemaProposedContent) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *DSSEV001SchemaProposedContent) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *DSSEV001SchemaProposedContent) UnmarshalBinary(b []byte) error { var res DSSEV001SchemaProposedContent if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // DSSEV001SchemaSignaturesItems0 a signature of the envelope's payload along with the verification material for the signature // // swagger:model DSSEV001SchemaSignaturesItems0 type DSSEV001SchemaSignaturesItems0 struct { // base64 encoded signature of the payload // Required: true // Pattern: ^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$ Signature *string `json:"signature"` // verification material that was used to verify the corresponding signature, specified as a base64 encoded string // Required: true // Format: byte Verifier *strfmt.Base64 `json:"verifier"` } // Validate validates this DSSE v001 schema signatures items0 func (m *DSSEV001SchemaSignaturesItems0) Validate(formats strfmt.Registry) error { var res []error if err := m.validateSignature(formats); err != nil { res = append(res, err) } if err := m.validateVerifier(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *DSSEV001SchemaSignaturesItems0) validateSignature(formats strfmt.Registry) error { if err := validate.Required("signature", "body", m.Signature); err != nil { return err } if err := validate.Pattern("signature", "body", *m.Signature, `^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$`); err != nil { return err } return nil } func (m *DSSEV001SchemaSignaturesItems0) validateVerifier(formats strfmt.Registry) error { if err := validate.Required("verifier", "body", m.Verifier); err != nil { return err } return nil } // ContextValidate validates this DSSE v001 schema signatures items0 based on context it is used func (m *DSSEV001SchemaSignaturesItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *DSSEV001SchemaSignaturesItems0) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *DSSEV001SchemaSignaturesItems0) UnmarshalBinary(b []byte) error { var res DSSEV001SchemaSignaturesItems0 if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/error.go000066400000000000000000000032231455727245600203450ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" ) // Error error // // swagger:model Error type Error struct { // code Code int64 `json:"code,omitempty"` // message Message string `json:"message,omitempty"` } // Validate validates this error func (m *Error) Validate(formats strfmt.Registry) error { return nil } // ContextValidate validates this error based on context it is used func (m *Error) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *Error) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *Error) UnmarshalBinary(b []byte) error { var res Error if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/hashedrekord.go000066400000000000000000000121511455727245600216570ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // Hashedrekord Hashed Rekord object // // swagger:model hashedrekord type Hashedrekord struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec HashedrekordSchema `json:"spec"` } // Kind gets the kind of this subtype func (m *Hashedrekord) Kind() string { return "hashedrekord" } // SetKind sets the kind of this subtype func (m *Hashedrekord) SetKind(val string) { } // UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure func (m *Hashedrekord) UnmarshalJSON(raw []byte) error { var data struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec HashedrekordSchema `json:"spec"` } buf := bytes.NewBuffer(raw) dec := json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&data); err != nil { return err } var base struct { /* Just the base type fields. Used for unmashalling polymorphic types.*/ Kind string `json:"kind"` } buf = bytes.NewBuffer(raw) dec = json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&base); err != nil { return err } var result Hashedrekord if base.Kind != result.Kind() { /* Not the type we're looking for. */ return errors.New(422, "invalid kind value: %q", base.Kind) } result.APIVersion = data.APIVersion result.Spec = data.Spec *m = result return nil } // MarshalJSON marshals this object with a polymorphic type to a JSON structure func (m Hashedrekord) MarshalJSON() ([]byte, error) { var b1, b2, b3 []byte var err error b1, err = json.Marshal(struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec HashedrekordSchema `json:"spec"` }{ APIVersion: m.APIVersion, Spec: m.Spec, }) if err != nil { return nil, err } b2, err = json.Marshal(struct { Kind string `json:"kind"` }{ Kind: m.Kind(), }) if err != nil { return nil, err } return swag.ConcatJSON(b1, b2, b3), nil } // Validate validates this hashedrekord func (m *Hashedrekord) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAPIVersion(formats); err != nil { res = append(res, err) } if err := m.validateSpec(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *Hashedrekord) validateAPIVersion(formats strfmt.Registry) error { if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil { return err } if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil { return err } return nil } func (m *Hashedrekord) validateSpec(formats strfmt.Registry) error { if m.Spec == nil { return errors.Required("spec", "body", nil) } return nil } // ContextValidate validate this hashedrekord based on the context it is used func (m *Hashedrekord) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *Hashedrekord) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *Hashedrekord) UnmarshalBinary(b []byte) error { var res Hashedrekord if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/hashedrekord_schema.go000066400000000000000000000016621455727245600232040ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command // HashedrekordSchema Rekor Schema // // # Schema for Rekord objects // // swagger:model hashedrekordSchema type HashedrekordSchema interface{} rekor-1.3.5/pkg/generated/models/hashedrekord_v001_schema.go000066400000000000000000000331741455727245600237550ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // HashedrekordV001Schema Hashed Rekor v0.0.1 Schema // // # Schema for Hashed Rekord object // // swagger:model hashedrekordV001Schema type HashedrekordV001Schema struct { // data // Required: true Data *HashedrekordV001SchemaData `json:"data"` // signature // Required: true Signature *HashedrekordV001SchemaSignature `json:"signature"` } // Validate validates this hashedrekord v001 schema func (m *HashedrekordV001Schema) Validate(formats strfmt.Registry) error { var res []error if err := m.validateData(formats); err != nil { res = append(res, err) } if err := m.validateSignature(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HashedrekordV001Schema) validateData(formats strfmt.Registry) error { if err := validate.Required("data", "body", m.Data); err != nil { return err } if m.Data != nil { if err := m.Data.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data") } return err } } return nil } func (m *HashedrekordV001Schema) validateSignature(formats strfmt.Registry) error { if err := validate.Required("signature", "body", m.Signature); err != nil { return err } if m.Signature != nil { if err := m.Signature.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signature") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signature") } return err } } return nil } // ContextValidate validate this hashedrekord v001 schema based on the context it is used func (m *HashedrekordV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateData(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidateSignature(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HashedrekordV001Schema) contextValidateData(ctx context.Context, formats strfmt.Registry) error { if m.Data != nil { if err := m.Data.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data") } return err } } return nil } func (m *HashedrekordV001Schema) contextValidateSignature(ctx context.Context, formats strfmt.Registry) error { if m.Signature != nil { if err := m.Signature.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signature") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signature") } return err } } return nil } // MarshalBinary interface implementation func (m *HashedrekordV001Schema) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *HashedrekordV001Schema) UnmarshalBinary(b []byte) error { var res HashedrekordV001Schema if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // HashedrekordV001SchemaData Information about the content associated with the entry // // swagger:model HashedrekordV001SchemaData type HashedrekordV001SchemaData struct { // hash Hash *HashedrekordV001SchemaDataHash `json:"hash,omitempty"` } // Validate validates this hashedrekord v001 schema data func (m *HashedrekordV001SchemaData) Validate(formats strfmt.Registry) error { var res []error if err := m.validateHash(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HashedrekordV001SchemaData) validateHash(formats strfmt.Registry) error { if swag.IsZero(m.Hash) { // not required return nil } if m.Hash != nil { if err := m.Hash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data" + "." + "hash") } return err } } return nil } // ContextValidate validate this hashedrekord v001 schema data based on the context it is used func (m *HashedrekordV001SchemaData) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateHash(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HashedrekordV001SchemaData) contextValidateHash(ctx context.Context, formats strfmt.Registry) error { if m.Hash != nil { if swag.IsZero(m.Hash) { // not required return nil } if err := m.Hash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data" + "." + "hash") } return err } } return nil } // MarshalBinary interface implementation func (m *HashedrekordV001SchemaData) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *HashedrekordV001SchemaData) UnmarshalBinary(b []byte) error { var res HashedrekordV001SchemaData if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // HashedrekordV001SchemaDataHash Specifies the hash algorithm and value for the content // // swagger:model HashedrekordV001SchemaDataHash type HashedrekordV001SchemaDataHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256 sha384 sha512] Algorithm *string `json:"algorithm"` // The hash value for the content, as represented by a lower case hexadecimal string // Required: true Value *string `json:"value"` } // Validate validates this hashedrekord v001 schema data hash func (m *HashedrekordV001SchemaDataHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256","sha384","sha512"]`), &res); err != nil { panic(err) } for _, v := range res { hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum = append(hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum, v) } } const ( // HashedrekordV001SchemaDataHashAlgorithmSha256 captures enum value "sha256" HashedrekordV001SchemaDataHashAlgorithmSha256 string = "sha256" // HashedrekordV001SchemaDataHashAlgorithmSha384 captures enum value "sha384" HashedrekordV001SchemaDataHashAlgorithmSha384 string = "sha384" // HashedrekordV001SchemaDataHashAlgorithmSha512 captures enum value "sha512" HashedrekordV001SchemaDataHashAlgorithmSha512 string = "sha512" ) // prop value enum func (m *HashedrekordV001SchemaDataHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, hashedrekordV001SchemaDataHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *HashedrekordV001SchemaDataHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("data"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("data"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *HashedrekordV001SchemaDataHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("data"+"."+"hash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validates this hashedrekord v001 schema data hash based on context it is used func (m *HashedrekordV001SchemaDataHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *HashedrekordV001SchemaDataHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *HashedrekordV001SchemaDataHash) UnmarshalBinary(b []byte) error { var res HashedrekordV001SchemaDataHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // HashedrekordV001SchemaSignature Information about the detached signature associated with the entry // // swagger:model HashedrekordV001SchemaSignature type HashedrekordV001SchemaSignature struct { // Specifies the content of the signature inline within the document // Format: byte Content strfmt.Base64 `json:"content,omitempty"` // public key PublicKey *HashedrekordV001SchemaSignaturePublicKey `json:"publicKey,omitempty"` } // Validate validates this hashedrekord v001 schema signature func (m *HashedrekordV001SchemaSignature) Validate(formats strfmt.Registry) error { var res []error if err := m.validatePublicKey(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HashedrekordV001SchemaSignature) validatePublicKey(formats strfmt.Registry) error { if swag.IsZero(m.PublicKey) { // not required return nil } if m.PublicKey != nil { if err := m.PublicKey.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signature" + "." + "publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signature" + "." + "publicKey") } return err } } return nil } // ContextValidate validate this hashedrekord v001 schema signature based on the context it is used func (m *HashedrekordV001SchemaSignature) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidatePublicKey(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HashedrekordV001SchemaSignature) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error { if m.PublicKey != nil { if swag.IsZero(m.PublicKey) { // not required return nil } if err := m.PublicKey.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signature" + "." + "publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signature" + "." + "publicKey") } return err } } return nil } // MarshalBinary interface implementation func (m *HashedrekordV001SchemaSignature) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *HashedrekordV001SchemaSignature) UnmarshalBinary(b []byte) error { var res HashedrekordV001SchemaSignature if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // HashedrekordV001SchemaSignaturePublicKey The public key that can verify the signature; this can also be an X509 code signing certificate that contains the raw public key information // // swagger:model HashedrekordV001SchemaSignaturePublicKey type HashedrekordV001SchemaSignaturePublicKey struct { // Specifies the content of the public key or code signing certificate inline within the document // Format: byte Content strfmt.Base64 `json:"content,omitempty"` } // Validate validates this hashedrekord v001 schema signature public key func (m *HashedrekordV001SchemaSignaturePublicKey) Validate(formats strfmt.Registry) error { return nil } // ContextValidate validates this hashedrekord v001 schema signature public key based on context it is used func (m *HashedrekordV001SchemaSignaturePublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *HashedrekordV001SchemaSignaturePublicKey) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *HashedrekordV001SchemaSignaturePublicKey) UnmarshalBinary(b []byte) error { var res HashedrekordV001SchemaSignaturePublicKey if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/helm.go000066400000000000000000000116671455727245600201540ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // Helm Helm chart // // swagger:model helm type Helm struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec HelmSchema `json:"spec"` } // Kind gets the kind of this subtype func (m *Helm) Kind() string { return "helm" } // SetKind sets the kind of this subtype func (m *Helm) SetKind(val string) { } // UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure func (m *Helm) UnmarshalJSON(raw []byte) error { var data struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec HelmSchema `json:"spec"` } buf := bytes.NewBuffer(raw) dec := json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&data); err != nil { return err } var base struct { /* Just the base type fields. Used for unmashalling polymorphic types.*/ Kind string `json:"kind"` } buf = bytes.NewBuffer(raw) dec = json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&base); err != nil { return err } var result Helm if base.Kind != result.Kind() { /* Not the type we're looking for. */ return errors.New(422, "invalid kind value: %q", base.Kind) } result.APIVersion = data.APIVersion result.Spec = data.Spec *m = result return nil } // MarshalJSON marshals this object with a polymorphic type to a JSON structure func (m Helm) MarshalJSON() ([]byte, error) { var b1, b2, b3 []byte var err error b1, err = json.Marshal(struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec HelmSchema `json:"spec"` }{ APIVersion: m.APIVersion, Spec: m.Spec, }) if err != nil { return nil, err } b2, err = json.Marshal(struct { Kind string `json:"kind"` }{ Kind: m.Kind(), }) if err != nil { return nil, err } return swag.ConcatJSON(b1, b2, b3), nil } // Validate validates this helm func (m *Helm) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAPIVersion(formats); err != nil { res = append(res, err) } if err := m.validateSpec(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *Helm) validateAPIVersion(formats strfmt.Registry) error { if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil { return err } if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil { return err } return nil } func (m *Helm) validateSpec(formats strfmt.Registry) error { if m.Spec == nil { return errors.Required("spec", "body", nil) } return nil } // ContextValidate validate this helm based on the context it is used func (m *Helm) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *Helm) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *Helm) UnmarshalBinary(b []byte) error { var res Helm if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/helm_schema.go000066400000000000000000000016271455727245600214670ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command // HelmSchema Helm Schema // // # Schema for Helm objects // // swagger:model helmSchema type HelmSchema interface{} rekor-1.3.5/pkg/generated/models/helm_v001_schema.go000066400000000000000000000410171455727245600222320ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // HelmV001Schema Helm v0.0.1 Schema // // # Schema for Helm object // // swagger:model helmV001Schema type HelmV001Schema struct { // chart // Required: true Chart *HelmV001SchemaChart `json:"chart"` // public key // Required: true PublicKey *HelmV001SchemaPublicKey `json:"publicKey"` } // Validate validates this helm v001 schema func (m *HelmV001Schema) Validate(formats strfmt.Registry) error { var res []error if err := m.validateChart(formats); err != nil { res = append(res, err) } if err := m.validatePublicKey(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HelmV001Schema) validateChart(formats strfmt.Registry) error { if err := validate.Required("chart", "body", m.Chart); err != nil { return err } if m.Chart != nil { if err := m.Chart.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("chart") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("chart") } return err } } return nil } func (m *HelmV001Schema) validatePublicKey(formats strfmt.Registry) error { if err := validate.Required("publicKey", "body", m.PublicKey); err != nil { return err } if m.PublicKey != nil { if err := m.PublicKey.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("publicKey") } return err } } return nil } // ContextValidate validate this helm v001 schema based on the context it is used func (m *HelmV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateChart(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidatePublicKey(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HelmV001Schema) contextValidateChart(ctx context.Context, formats strfmt.Registry) error { if m.Chart != nil { if err := m.Chart.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("chart") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("chart") } return err } } return nil } func (m *HelmV001Schema) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error { if m.PublicKey != nil { if err := m.PublicKey.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("publicKey") } return err } } return nil } // MarshalBinary interface implementation func (m *HelmV001Schema) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *HelmV001Schema) UnmarshalBinary(b []byte) error { var res HelmV001Schema if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // HelmV001SchemaChart Information about the Helm chart associated with the entry // // swagger:model HelmV001SchemaChart type HelmV001SchemaChart struct { // hash Hash *HelmV001SchemaChartHash `json:"hash,omitempty"` // provenance // Required: true Provenance *HelmV001SchemaChartProvenance `json:"provenance"` } // Validate validates this helm v001 schema chart func (m *HelmV001SchemaChart) Validate(formats strfmt.Registry) error { var res []error if err := m.validateHash(formats); err != nil { res = append(res, err) } if err := m.validateProvenance(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HelmV001SchemaChart) validateHash(formats strfmt.Registry) error { if swag.IsZero(m.Hash) { // not required return nil } if m.Hash != nil { if err := m.Hash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("chart" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("chart" + "." + "hash") } return err } } return nil } func (m *HelmV001SchemaChart) validateProvenance(formats strfmt.Registry) error { if err := validate.Required("chart"+"."+"provenance", "body", m.Provenance); err != nil { return err } if m.Provenance != nil { if err := m.Provenance.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("chart" + "." + "provenance") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("chart" + "." + "provenance") } return err } } return nil } // ContextValidate validate this helm v001 schema chart based on the context it is used func (m *HelmV001SchemaChart) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateHash(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidateProvenance(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HelmV001SchemaChart) contextValidateHash(ctx context.Context, formats strfmt.Registry) error { if m.Hash != nil { if swag.IsZero(m.Hash) { // not required return nil } if err := m.Hash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("chart" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("chart" + "." + "hash") } return err } } return nil } func (m *HelmV001SchemaChart) contextValidateProvenance(ctx context.Context, formats strfmt.Registry) error { if m.Provenance != nil { if err := m.Provenance.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("chart" + "." + "provenance") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("chart" + "." + "provenance") } return err } } return nil } // MarshalBinary interface implementation func (m *HelmV001SchemaChart) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *HelmV001SchemaChart) UnmarshalBinary(b []byte) error { var res HelmV001SchemaChart if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // HelmV001SchemaChartHash Specifies the hash algorithm and value for the chart // // swagger:model HelmV001SchemaChartHash type HelmV001SchemaChartHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The hash value for the chart // Required: true Value *string `json:"value"` } // Validate validates this helm v001 schema chart hash func (m *HelmV001SchemaChartHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var helmV001SchemaChartHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { helmV001SchemaChartHashTypeAlgorithmPropEnum = append(helmV001SchemaChartHashTypeAlgorithmPropEnum, v) } } const ( // HelmV001SchemaChartHashAlgorithmSha256 captures enum value "sha256" HelmV001SchemaChartHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *HelmV001SchemaChartHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, helmV001SchemaChartHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *HelmV001SchemaChartHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("chart"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("chart"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *HelmV001SchemaChartHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("chart"+"."+"hash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validate this helm v001 schema chart hash based on the context it is used func (m *HelmV001SchemaChartHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *HelmV001SchemaChartHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *HelmV001SchemaChartHash) UnmarshalBinary(b []byte) error { var res HelmV001SchemaChartHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // HelmV001SchemaChartProvenance The provenance entry associated with the signed Helm Chart // // swagger:model HelmV001SchemaChartProvenance type HelmV001SchemaChartProvenance struct { // Specifies the content of the provenance file inline within the document // Format: byte Content strfmt.Base64 `json:"content,omitempty"` // signature Signature *HelmV001SchemaChartProvenanceSignature `json:"signature,omitempty"` } // Validate validates this helm v001 schema chart provenance func (m *HelmV001SchemaChartProvenance) Validate(formats strfmt.Registry) error { var res []error if err := m.validateSignature(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HelmV001SchemaChartProvenance) validateSignature(formats strfmt.Registry) error { if swag.IsZero(m.Signature) { // not required return nil } if m.Signature != nil { if err := m.Signature.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("chart" + "." + "provenance" + "." + "signature") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("chart" + "." + "provenance" + "." + "signature") } return err } } return nil } // ContextValidate validate this helm v001 schema chart provenance based on the context it is used func (m *HelmV001SchemaChartProvenance) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateSignature(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HelmV001SchemaChartProvenance) contextValidateSignature(ctx context.Context, formats strfmt.Registry) error { if m.Signature != nil { if swag.IsZero(m.Signature) { // not required return nil } if err := m.Signature.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("chart" + "." + "provenance" + "." + "signature") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("chart" + "." + "provenance" + "." + "signature") } return err } } return nil } // MarshalBinary interface implementation func (m *HelmV001SchemaChartProvenance) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *HelmV001SchemaChartProvenance) UnmarshalBinary(b []byte) error { var res HelmV001SchemaChartProvenance if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // HelmV001SchemaChartProvenanceSignature Information about the included signature in the provenance file // // swagger:model HelmV001SchemaChartProvenanceSignature type HelmV001SchemaChartProvenanceSignature struct { // Specifies the signature embedded within the provenance file // Required: true // Read Only: true // Format: byte Content strfmt.Base64 `json:"content"` } // Validate validates this helm v001 schema chart provenance signature func (m *HelmV001SchemaChartProvenanceSignature) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HelmV001SchemaChartProvenanceSignature) validateContent(formats strfmt.Registry) error { if err := validate.Required("chart"+"."+"provenance"+"."+"signature"+"."+"content", "body", strfmt.Base64(m.Content)); err != nil { return err } return nil } // ContextValidate validate this helm v001 schema chart provenance signature based on the context it is used func (m *HelmV001SchemaChartProvenanceSignature) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateContent(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HelmV001SchemaChartProvenanceSignature) contextValidateContent(ctx context.Context, formats strfmt.Registry) error { if err := validate.ReadOnly(ctx, "chart"+"."+"provenance"+"."+"signature"+"."+"content", "body", strfmt.Base64(m.Content)); err != nil { return err } return nil } // MarshalBinary interface implementation func (m *HelmV001SchemaChartProvenanceSignature) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *HelmV001SchemaChartProvenanceSignature) UnmarshalBinary(b []byte) error { var res HelmV001SchemaChartProvenanceSignature if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // HelmV001SchemaPublicKey The public key that can verify the package signature // // swagger:model HelmV001SchemaPublicKey type HelmV001SchemaPublicKey struct { // Specifies the content of the public key inline within the document // Required: true // Format: byte Content *strfmt.Base64 `json:"content"` } // Validate validates this helm v001 schema public key func (m *HelmV001SchemaPublicKey) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *HelmV001SchemaPublicKey) validateContent(formats strfmt.Registry) error { if err := validate.Required("publicKey"+"."+"content", "body", m.Content); err != nil { return err } return nil } // ContextValidate validates this helm v001 schema public key based on context it is used func (m *HelmV001SchemaPublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *HelmV001SchemaPublicKey) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *HelmV001SchemaPublicKey) UnmarshalBinary(b []byte) error { var res HelmV001SchemaPublicKey if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/inactive_shard_log_info.go000066400000000000000000000073271455727245600240640ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // InactiveShardLogInfo inactive shard log info // // swagger:model InactiveShardLogInfo type InactiveShardLogInfo struct { // The current hash value stored at the root of the merkle tree // Required: true // Pattern: ^[0-9a-fA-F]{64}$ RootHash *string `json:"rootHash"` // The current signed tree head // Required: true SignedTreeHead *string `json:"signedTreeHead"` // The current treeID // Required: true // Pattern: ^[0-9]+$ TreeID *string `json:"treeID"` // The current number of nodes in the merkle tree // Required: true // Minimum: 1 TreeSize *int64 `json:"treeSize"` } // Validate validates this inactive shard log info func (m *InactiveShardLogInfo) Validate(formats strfmt.Registry) error { var res []error if err := m.validateRootHash(formats); err != nil { res = append(res, err) } if err := m.validateSignedTreeHead(formats); err != nil { res = append(res, err) } if err := m.validateTreeID(formats); err != nil { res = append(res, err) } if err := m.validateTreeSize(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *InactiveShardLogInfo) validateRootHash(formats strfmt.Registry) error { if err := validate.Required("rootHash", "body", m.RootHash); err != nil { return err } if err := validate.Pattern("rootHash", "body", *m.RootHash, `^[0-9a-fA-F]{64}$`); err != nil { return err } return nil } func (m *InactiveShardLogInfo) validateSignedTreeHead(formats strfmt.Registry) error { if err := validate.Required("signedTreeHead", "body", m.SignedTreeHead); err != nil { return err } return nil } func (m *InactiveShardLogInfo) validateTreeID(formats strfmt.Registry) error { if err := validate.Required("treeID", "body", m.TreeID); err != nil { return err } if err := validate.Pattern("treeID", "body", *m.TreeID, `^[0-9]+$`); err != nil { return err } return nil } func (m *InactiveShardLogInfo) validateTreeSize(formats strfmt.Registry) error { if err := validate.Required("treeSize", "body", m.TreeSize); err != nil { return err } if err := validate.MinimumInt("treeSize", "body", *m.TreeSize, 1, false); err != nil { return err } return nil } // ContextValidate validates this inactive shard log info based on context it is used func (m *InactiveShardLogInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *InactiveShardLogInfo) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *InactiveShardLogInfo) UnmarshalBinary(b []byte) error { var res InactiveShardLogInfo if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/inclusion_proof.go000066400000000000000000000104671455727245600224340ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "strconv" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // InclusionProof inclusion proof // // swagger:model InclusionProof type InclusionProof struct { // The checkpoint (signed tree head) that the inclusion proof is based on // Required: true Checkpoint *string `json:"checkpoint"` // A list of hashes required to compute the inclusion proof, sorted in order from leaf to root // Required: true Hashes []string `json:"hashes"` // The index of the entry in the transparency log // Required: true // Minimum: 0 LogIndex *int64 `json:"logIndex"` // The hash value stored at the root of the merkle tree at the time the proof was generated // Required: true // Pattern: ^[0-9a-fA-F]{64}$ RootHash *string `json:"rootHash"` // The size of the merkle tree at the time the inclusion proof was generated // Required: true // Minimum: 1 TreeSize *int64 `json:"treeSize"` } // Validate validates this inclusion proof func (m *InclusionProof) Validate(formats strfmt.Registry) error { var res []error if err := m.validateCheckpoint(formats); err != nil { res = append(res, err) } if err := m.validateHashes(formats); err != nil { res = append(res, err) } if err := m.validateLogIndex(formats); err != nil { res = append(res, err) } if err := m.validateRootHash(formats); err != nil { res = append(res, err) } if err := m.validateTreeSize(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *InclusionProof) validateCheckpoint(formats strfmt.Registry) error { if err := validate.Required("checkpoint", "body", m.Checkpoint); err != nil { return err } return nil } func (m *InclusionProof) validateHashes(formats strfmt.Registry) error { if err := validate.Required("hashes", "body", m.Hashes); err != nil { return err } for i := 0; i < len(m.Hashes); i++ { if err := validate.Pattern("hashes"+"."+strconv.Itoa(i), "body", m.Hashes[i], `^[0-9a-fA-F]{64}$`); err != nil { return err } } return nil } func (m *InclusionProof) validateLogIndex(formats strfmt.Registry) error { if err := validate.Required("logIndex", "body", m.LogIndex); err != nil { return err } if err := validate.MinimumInt("logIndex", "body", *m.LogIndex, 0, false); err != nil { return err } return nil } func (m *InclusionProof) validateRootHash(formats strfmt.Registry) error { if err := validate.Required("rootHash", "body", m.RootHash); err != nil { return err } if err := validate.Pattern("rootHash", "body", *m.RootHash, `^[0-9a-fA-F]{64}$`); err != nil { return err } return nil } func (m *InclusionProof) validateTreeSize(formats strfmt.Registry) error { if err := validate.Required("treeSize", "body", m.TreeSize); err != nil { return err } if err := validate.MinimumInt("treeSize", "body", *m.TreeSize, 1, false); err != nil { return err } return nil } // ContextValidate validates this inclusion proof based on context it is used func (m *InclusionProof) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *InclusionProof) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *InclusionProof) UnmarshalBinary(b []byte) error { var res InclusionProof if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/intoto.go000066400000000000000000000117441455727245600205370ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // Intoto Intoto object // // swagger:model intoto type Intoto struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec IntotoSchema `json:"spec"` } // Kind gets the kind of this subtype func (m *Intoto) Kind() string { return "intoto" } // SetKind sets the kind of this subtype func (m *Intoto) SetKind(val string) { } // UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure func (m *Intoto) UnmarshalJSON(raw []byte) error { var data struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec IntotoSchema `json:"spec"` } buf := bytes.NewBuffer(raw) dec := json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&data); err != nil { return err } var base struct { /* Just the base type fields. Used for unmashalling polymorphic types.*/ Kind string `json:"kind"` } buf = bytes.NewBuffer(raw) dec = json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&base); err != nil { return err } var result Intoto if base.Kind != result.Kind() { /* Not the type we're looking for. */ return errors.New(422, "invalid kind value: %q", base.Kind) } result.APIVersion = data.APIVersion result.Spec = data.Spec *m = result return nil } // MarshalJSON marshals this object with a polymorphic type to a JSON structure func (m Intoto) MarshalJSON() ([]byte, error) { var b1, b2, b3 []byte var err error b1, err = json.Marshal(struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec IntotoSchema `json:"spec"` }{ APIVersion: m.APIVersion, Spec: m.Spec, }) if err != nil { return nil, err } b2, err = json.Marshal(struct { Kind string `json:"kind"` }{ Kind: m.Kind(), }) if err != nil { return nil, err } return swag.ConcatJSON(b1, b2, b3), nil } // Validate validates this intoto func (m *Intoto) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAPIVersion(formats); err != nil { res = append(res, err) } if err := m.validateSpec(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *Intoto) validateAPIVersion(formats strfmt.Registry) error { if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil { return err } if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil { return err } return nil } func (m *Intoto) validateSpec(formats strfmt.Registry) error { if m.Spec == nil { return errors.Required("spec", "body", nil) } return nil } // ContextValidate validate this intoto based on the context it is used func (m *Intoto) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *Intoto) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *Intoto) UnmarshalBinary(b []byte) error { var res Intoto if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/intoto_schema.go000066400000000000000000000016411455727245600220520ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command // IntotoSchema Intoto Schema // // # Intoto for Rekord objects // // swagger:model intotoSchema type IntotoSchema interface{} rekor-1.3.5/pkg/generated/models/intoto_v001_schema.go000066400000000000000000000320241455727245600226170ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // IntotoV001Schema intoto v0.0.1 Schema // // # Schema for intoto object // // swagger:model intotoV001Schema type IntotoV001Schema struct { // content // Required: true Content *IntotoV001SchemaContent `json:"content"` // The public key that can verify the signature // Required: true // Format: byte PublicKey *strfmt.Base64 `json:"publicKey"` } // Validate validates this intoto v001 schema func (m *IntotoV001Schema) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if err := m.validatePublicKey(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *IntotoV001Schema) validateContent(formats strfmt.Registry) error { if err := validate.Required("content", "body", m.Content); err != nil { return err } if m.Content != nil { if err := m.Content.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content") } return err } } return nil } func (m *IntotoV001Schema) validatePublicKey(formats strfmt.Registry) error { if err := validate.Required("publicKey", "body", m.PublicKey); err != nil { return err } return nil } // ContextValidate validate this intoto v001 schema based on the context it is used func (m *IntotoV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateContent(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *IntotoV001Schema) contextValidateContent(ctx context.Context, formats strfmt.Registry) error { if m.Content != nil { if err := m.Content.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content") } return err } } return nil } // MarshalBinary interface implementation func (m *IntotoV001Schema) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *IntotoV001Schema) UnmarshalBinary(b []byte) error { var res IntotoV001Schema if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // IntotoV001SchemaContent intoto v001 schema content // // swagger:model IntotoV001SchemaContent type IntotoV001SchemaContent struct { // envelope Envelope string `json:"envelope,omitempty"` // hash Hash *IntotoV001SchemaContentHash `json:"hash,omitempty"` // payload hash PayloadHash *IntotoV001SchemaContentPayloadHash `json:"payloadHash,omitempty"` } // Validate validates this intoto v001 schema content func (m *IntotoV001SchemaContent) Validate(formats strfmt.Registry) error { var res []error if err := m.validateHash(formats); err != nil { res = append(res, err) } if err := m.validatePayloadHash(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *IntotoV001SchemaContent) validateHash(formats strfmt.Registry) error { if swag.IsZero(m.Hash) { // not required return nil } if m.Hash != nil { if err := m.Hash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content" + "." + "hash") } return err } } return nil } func (m *IntotoV001SchemaContent) validatePayloadHash(formats strfmt.Registry) error { if swag.IsZero(m.PayloadHash) { // not required return nil } if m.PayloadHash != nil { if err := m.PayloadHash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content" + "." + "payloadHash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content" + "." + "payloadHash") } return err } } return nil } // ContextValidate validate this intoto v001 schema content based on the context it is used func (m *IntotoV001SchemaContent) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateHash(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidatePayloadHash(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *IntotoV001SchemaContent) contextValidateHash(ctx context.Context, formats strfmt.Registry) error { if m.Hash != nil { if swag.IsZero(m.Hash) { // not required return nil } if err := m.Hash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content" + "." + "hash") } return err } } return nil } func (m *IntotoV001SchemaContent) contextValidatePayloadHash(ctx context.Context, formats strfmt.Registry) error { if m.PayloadHash != nil { if swag.IsZero(m.PayloadHash) { // not required return nil } if err := m.PayloadHash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content" + "." + "payloadHash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content" + "." + "payloadHash") } return err } } return nil } // MarshalBinary interface implementation func (m *IntotoV001SchemaContent) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *IntotoV001SchemaContent) UnmarshalBinary(b []byte) error { var res IntotoV001SchemaContent if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // IntotoV001SchemaContentHash Specifies the hash algorithm and value encompassing the entire signed envelope; this is computed by the rekor server, client-provided values are ignored // // swagger:model IntotoV001SchemaContentHash type IntotoV001SchemaContentHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The hash value for the archive // Required: true Value *string `json:"value"` } // Validate validates this intoto v001 schema content hash func (m *IntotoV001SchemaContentHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var intotoV001SchemaContentHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { intotoV001SchemaContentHashTypeAlgorithmPropEnum = append(intotoV001SchemaContentHashTypeAlgorithmPropEnum, v) } } const ( // IntotoV001SchemaContentHashAlgorithmSha256 captures enum value "sha256" IntotoV001SchemaContentHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *IntotoV001SchemaContentHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, intotoV001SchemaContentHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *IntotoV001SchemaContentHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("content"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("content"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *IntotoV001SchemaContentHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("content"+"."+"hash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validate this intoto v001 schema content hash based on the context it is used func (m *IntotoV001SchemaContentHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *IntotoV001SchemaContentHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *IntotoV001SchemaContentHash) UnmarshalBinary(b []byte) error { var res IntotoV001SchemaContentHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // IntotoV001SchemaContentPayloadHash Specifies the hash algorithm and value covering the payload within the DSSE envelope; this is computed by the rekor server, client-provided values are ignored // // swagger:model IntotoV001SchemaContentPayloadHash type IntotoV001SchemaContentPayloadHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The hash value for the envelope's payload // Required: true Value *string `json:"value"` } // Validate validates this intoto v001 schema content payload hash func (m *IntotoV001SchemaContentPayloadHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var intotoV001SchemaContentPayloadHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { intotoV001SchemaContentPayloadHashTypeAlgorithmPropEnum = append(intotoV001SchemaContentPayloadHashTypeAlgorithmPropEnum, v) } } const ( // IntotoV001SchemaContentPayloadHashAlgorithmSha256 captures enum value "sha256" IntotoV001SchemaContentPayloadHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *IntotoV001SchemaContentPayloadHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, intotoV001SchemaContentPayloadHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *IntotoV001SchemaContentPayloadHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("content"+"."+"payloadHash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("content"+"."+"payloadHash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *IntotoV001SchemaContentPayloadHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("content"+"."+"payloadHash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validate this intoto v001 schema content payload hash based on the context it is used func (m *IntotoV001SchemaContentPayloadHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *IntotoV001SchemaContentPayloadHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *IntotoV001SchemaContentPayloadHash) UnmarshalBinary(b []byte) error { var res IntotoV001SchemaContentPayloadHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/intoto_v002_schema.go000066400000000000000000000466471455727245600226400ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "encoding/json" "strconv" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // IntotoV002Schema intoto v0.0.2 Schema // // # Schema for intoto object // // swagger:model intotoV002Schema type IntotoV002Schema struct { // content // Required: true Content *IntotoV002SchemaContent `json:"content"` } // Validate validates this intoto v002 schema func (m *IntotoV002Schema) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *IntotoV002Schema) validateContent(formats strfmt.Registry) error { if err := validate.Required("content", "body", m.Content); err != nil { return err } if m.Content != nil { if err := m.Content.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content") } return err } } return nil } // ContextValidate validate this intoto v002 schema based on the context it is used func (m *IntotoV002Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateContent(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *IntotoV002Schema) contextValidateContent(ctx context.Context, formats strfmt.Registry) error { if m.Content != nil { if err := m.Content.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content") } return err } } return nil } // MarshalBinary interface implementation func (m *IntotoV002Schema) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *IntotoV002Schema) UnmarshalBinary(b []byte) error { var res IntotoV002Schema if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // IntotoV002SchemaContent intoto v002 schema content // // swagger:model IntotoV002SchemaContent type IntotoV002SchemaContent struct { // envelope // Required: true Envelope *IntotoV002SchemaContentEnvelope `json:"envelope"` // hash Hash *IntotoV002SchemaContentHash `json:"hash,omitempty"` // payload hash PayloadHash *IntotoV002SchemaContentPayloadHash `json:"payloadHash,omitempty"` } // Validate validates this intoto v002 schema content func (m *IntotoV002SchemaContent) Validate(formats strfmt.Registry) error { var res []error if err := m.validateEnvelope(formats); err != nil { res = append(res, err) } if err := m.validateHash(formats); err != nil { res = append(res, err) } if err := m.validatePayloadHash(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *IntotoV002SchemaContent) validateEnvelope(formats strfmt.Registry) error { if err := validate.Required("content"+"."+"envelope", "body", m.Envelope); err != nil { return err } if m.Envelope != nil { if err := m.Envelope.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content" + "." + "envelope") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content" + "." + "envelope") } return err } } return nil } func (m *IntotoV002SchemaContent) validateHash(formats strfmt.Registry) error { if swag.IsZero(m.Hash) { // not required return nil } if m.Hash != nil { if err := m.Hash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content" + "." + "hash") } return err } } return nil } func (m *IntotoV002SchemaContent) validatePayloadHash(formats strfmt.Registry) error { if swag.IsZero(m.PayloadHash) { // not required return nil } if m.PayloadHash != nil { if err := m.PayloadHash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content" + "." + "payloadHash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content" + "." + "payloadHash") } return err } } return nil } // ContextValidate validate this intoto v002 schema content based on the context it is used func (m *IntotoV002SchemaContent) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateEnvelope(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidateHash(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidatePayloadHash(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *IntotoV002SchemaContent) contextValidateEnvelope(ctx context.Context, formats strfmt.Registry) error { if m.Envelope != nil { if err := m.Envelope.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content" + "." + "envelope") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content" + "." + "envelope") } return err } } return nil } func (m *IntotoV002SchemaContent) contextValidateHash(ctx context.Context, formats strfmt.Registry) error { if m.Hash != nil { if swag.IsZero(m.Hash) { // not required return nil } if err := m.Hash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content" + "." + "hash") } return err } } return nil } func (m *IntotoV002SchemaContent) contextValidatePayloadHash(ctx context.Context, formats strfmt.Registry) error { if m.PayloadHash != nil { if swag.IsZero(m.PayloadHash) { // not required return nil } if err := m.PayloadHash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content" + "." + "payloadHash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content" + "." + "payloadHash") } return err } } return nil } // MarshalBinary interface implementation func (m *IntotoV002SchemaContent) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *IntotoV002SchemaContent) UnmarshalBinary(b []byte) error { var res IntotoV002SchemaContent if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // IntotoV002SchemaContentEnvelope dsse envelope // // swagger:model IntotoV002SchemaContentEnvelope type IntotoV002SchemaContentEnvelope struct { // payload of the envelope // Format: byte Payload strfmt.Base64 `json:"payload,omitempty"` // type describing the payload // Required: true PayloadType *string `json:"payloadType"` // collection of all signatures of the envelope's payload // Required: true // Min Items: 1 Signatures []*IntotoV002SchemaContentEnvelopeSignaturesItems0 `json:"signatures"` } // Validate validates this intoto v002 schema content envelope func (m *IntotoV002SchemaContentEnvelope) Validate(formats strfmt.Registry) error { var res []error if err := m.validatePayloadType(formats); err != nil { res = append(res, err) } if err := m.validateSignatures(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *IntotoV002SchemaContentEnvelope) validatePayloadType(formats strfmt.Registry) error { if err := validate.Required("content"+"."+"envelope"+"."+"payloadType", "body", m.PayloadType); err != nil { return err } return nil } func (m *IntotoV002SchemaContentEnvelope) validateSignatures(formats strfmt.Registry) error { if err := validate.Required("content"+"."+"envelope"+"."+"signatures", "body", m.Signatures); err != nil { return err } iSignaturesSize := int64(len(m.Signatures)) if err := validate.MinItems("content"+"."+"envelope"+"."+"signatures", "body", iSignaturesSize, 1); err != nil { return err } for i := 0; i < len(m.Signatures); i++ { if swag.IsZero(m.Signatures[i]) { // not required continue } if m.Signatures[i] != nil { if err := m.Signatures[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content" + "." + "envelope" + "." + "signatures" + "." + strconv.Itoa(i)) } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content" + "." + "envelope" + "." + "signatures" + "." + strconv.Itoa(i)) } return err } } } return nil } // ContextValidate validate this intoto v002 schema content envelope based on the context it is used func (m *IntotoV002SchemaContentEnvelope) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateSignatures(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *IntotoV002SchemaContentEnvelope) contextValidateSignatures(ctx context.Context, formats strfmt.Registry) error { for i := 0; i < len(m.Signatures); i++ { if m.Signatures[i] != nil { if swag.IsZero(m.Signatures[i]) { // not required return nil } if err := m.Signatures[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("content" + "." + "envelope" + "." + "signatures" + "." + strconv.Itoa(i)) } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("content" + "." + "envelope" + "." + "signatures" + "." + strconv.Itoa(i)) } return err } } } return nil } // MarshalBinary interface implementation func (m *IntotoV002SchemaContentEnvelope) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *IntotoV002SchemaContentEnvelope) UnmarshalBinary(b []byte) error { var res IntotoV002SchemaContentEnvelope if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // IntotoV002SchemaContentEnvelopeSignaturesItems0 a signature of the envelope's payload along with the public key for the signature // // swagger:model IntotoV002SchemaContentEnvelopeSignaturesItems0 type IntotoV002SchemaContentEnvelopeSignaturesItems0 struct { // optional id of the key used to create the signature Keyid string `json:"keyid,omitempty"` // public key that corresponds to this signature // Required: true // Format: byte PublicKey *strfmt.Base64 `json:"publicKey"` // signature of the payload // Required: true // Format: byte Sig *strfmt.Base64 `json:"sig"` } // Validate validates this intoto v002 schema content envelope signatures items0 func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) Validate(formats strfmt.Registry) error { var res []error if err := m.validatePublicKey(formats); err != nil { res = append(res, err) } if err := m.validateSig(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) validatePublicKey(formats strfmt.Registry) error { if err := validate.Required("publicKey", "body", m.PublicKey); err != nil { return err } return nil } func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) validateSig(formats strfmt.Registry) error { if err := validate.Required("sig", "body", m.Sig); err != nil { return err } return nil } // ContextValidate validates this intoto v002 schema content envelope signatures items0 based on context it is used func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) UnmarshalBinary(b []byte) error { var res IntotoV002SchemaContentEnvelopeSignaturesItems0 if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // IntotoV002SchemaContentHash Specifies the hash algorithm and value encompassing the entire signed envelope // // swagger:model IntotoV002SchemaContentHash type IntotoV002SchemaContentHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The hash value for the archive // Required: true Value *string `json:"value"` } // Validate validates this intoto v002 schema content hash func (m *IntotoV002SchemaContentHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var intotoV002SchemaContentHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { intotoV002SchemaContentHashTypeAlgorithmPropEnum = append(intotoV002SchemaContentHashTypeAlgorithmPropEnum, v) } } const ( // IntotoV002SchemaContentHashAlgorithmSha256 captures enum value "sha256" IntotoV002SchemaContentHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *IntotoV002SchemaContentHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, intotoV002SchemaContentHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *IntotoV002SchemaContentHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("content"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("content"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *IntotoV002SchemaContentHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("content"+"."+"hash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validate this intoto v002 schema content hash based on the context it is used func (m *IntotoV002SchemaContentHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *IntotoV002SchemaContentHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *IntotoV002SchemaContentHash) UnmarshalBinary(b []byte) error { var res IntotoV002SchemaContentHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // IntotoV002SchemaContentPayloadHash Specifies the hash algorithm and value covering the payload within the DSSE envelope // // swagger:model IntotoV002SchemaContentPayloadHash type IntotoV002SchemaContentPayloadHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The hash value of the payload // Required: true Value *string `json:"value"` } // Validate validates this intoto v002 schema content payload hash func (m *IntotoV002SchemaContentPayloadHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var intotoV002SchemaContentPayloadHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { intotoV002SchemaContentPayloadHashTypeAlgorithmPropEnum = append(intotoV002SchemaContentPayloadHashTypeAlgorithmPropEnum, v) } } const ( // IntotoV002SchemaContentPayloadHashAlgorithmSha256 captures enum value "sha256" IntotoV002SchemaContentPayloadHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *IntotoV002SchemaContentPayloadHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, intotoV002SchemaContentPayloadHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *IntotoV002SchemaContentPayloadHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("content"+"."+"payloadHash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("content"+"."+"payloadHash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *IntotoV002SchemaContentPayloadHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("content"+"."+"payloadHash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validate this intoto v002 schema content payload hash based on the context it is used func (m *IntotoV002SchemaContentPayloadHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *IntotoV002SchemaContentPayloadHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *IntotoV002SchemaContentPayloadHash) UnmarshalBinary(b []byte) error { var res IntotoV002SchemaContentPayloadHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/jar.go000066400000000000000000000116521455727245600177750ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // Jar Java Archive (JAR) // // swagger:model jar type Jar struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec JarSchema `json:"spec"` } // Kind gets the kind of this subtype func (m *Jar) Kind() string { return "jar" } // SetKind sets the kind of this subtype func (m *Jar) SetKind(val string) { } // UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure func (m *Jar) UnmarshalJSON(raw []byte) error { var data struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec JarSchema `json:"spec"` } buf := bytes.NewBuffer(raw) dec := json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&data); err != nil { return err } var base struct { /* Just the base type fields. Used for unmashalling polymorphic types.*/ Kind string `json:"kind"` } buf = bytes.NewBuffer(raw) dec = json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&base); err != nil { return err } var result Jar if base.Kind != result.Kind() { /* Not the type we're looking for. */ return errors.New(422, "invalid kind value: %q", base.Kind) } result.APIVersion = data.APIVersion result.Spec = data.Spec *m = result return nil } // MarshalJSON marshals this object with a polymorphic type to a JSON structure func (m Jar) MarshalJSON() ([]byte, error) { var b1, b2, b3 []byte var err error b1, err = json.Marshal(struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec JarSchema `json:"spec"` }{ APIVersion: m.APIVersion, Spec: m.Spec, }) if err != nil { return nil, err } b2, err = json.Marshal(struct { Kind string `json:"kind"` }{ Kind: m.Kind(), }) if err != nil { return nil, err } return swag.ConcatJSON(b1, b2, b3), nil } // Validate validates this jar func (m *Jar) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAPIVersion(formats); err != nil { res = append(res, err) } if err := m.validateSpec(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *Jar) validateAPIVersion(formats strfmt.Registry) error { if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil { return err } if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil { return err } return nil } func (m *Jar) validateSpec(formats strfmt.Registry) error { if m.Spec == nil { return errors.Required("spec", "body", nil) } return nil } // ContextValidate validate this jar based on the context it is used func (m *Jar) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *Jar) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *Jar) UnmarshalBinary(b []byte) error { var res Jar if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/jar_schema.go000066400000000000000000000016221455727245600213110ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command // JarSchema JAR Schema // // # Schema for JAR objects // // swagger:model jarSchema type JarSchema interface{} rekor-1.3.5/pkg/generated/models/jar_v001_schema.go000066400000000000000000000340331455727245600220610ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // JarV001Schema JAR v0.0.1 Schema // // # Schema for JAR entries // // swagger:model jarV001Schema type JarV001Schema struct { // archive // Required: true Archive *JarV001SchemaArchive `json:"archive"` // signature Signature *JarV001SchemaSignature `json:"signature,omitempty"` } // Validate validates this jar v001 schema func (m *JarV001Schema) Validate(formats strfmt.Registry) error { var res []error if err := m.validateArchive(formats); err != nil { res = append(res, err) } if err := m.validateSignature(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *JarV001Schema) validateArchive(formats strfmt.Registry) error { if err := validate.Required("archive", "body", m.Archive); err != nil { return err } if m.Archive != nil { if err := m.Archive.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("archive") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("archive") } return err } } return nil } func (m *JarV001Schema) validateSignature(formats strfmt.Registry) error { if swag.IsZero(m.Signature) { // not required return nil } if m.Signature != nil { if err := m.Signature.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signature") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signature") } return err } } return nil } // ContextValidate validate this jar v001 schema based on the context it is used func (m *JarV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateArchive(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidateSignature(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *JarV001Schema) contextValidateArchive(ctx context.Context, formats strfmt.Registry) error { if m.Archive != nil { if err := m.Archive.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("archive") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("archive") } return err } } return nil } func (m *JarV001Schema) contextValidateSignature(ctx context.Context, formats strfmt.Registry) error { if m.Signature != nil { if swag.IsZero(m.Signature) { // not required return nil } if err := m.Signature.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signature") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signature") } return err } } return nil } // MarshalBinary interface implementation func (m *JarV001Schema) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *JarV001Schema) UnmarshalBinary(b []byte) error { var res JarV001Schema if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // JarV001SchemaArchive Information about the archive associated with the entry // // swagger:model JarV001SchemaArchive type JarV001SchemaArchive struct { // Specifies the archive inline within the document // Format: byte Content strfmt.Base64 `json:"content,omitempty"` // hash Hash *JarV001SchemaArchiveHash `json:"hash,omitempty"` } // Validate validates this jar v001 schema archive func (m *JarV001SchemaArchive) Validate(formats strfmt.Registry) error { var res []error if err := m.validateHash(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *JarV001SchemaArchive) validateHash(formats strfmt.Registry) error { if swag.IsZero(m.Hash) { // not required return nil } if m.Hash != nil { if err := m.Hash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("archive" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("archive" + "." + "hash") } return err } } return nil } // ContextValidate validate this jar v001 schema archive based on the context it is used func (m *JarV001SchemaArchive) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateHash(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *JarV001SchemaArchive) contextValidateHash(ctx context.Context, formats strfmt.Registry) error { if m.Hash != nil { if swag.IsZero(m.Hash) { // not required return nil } if err := m.Hash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("archive" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("archive" + "." + "hash") } return err } } return nil } // MarshalBinary interface implementation func (m *JarV001SchemaArchive) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *JarV001SchemaArchive) UnmarshalBinary(b []byte) error { var res JarV001SchemaArchive if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // JarV001SchemaArchiveHash Specifies the hash algorithm and value encompassing the entire signed archive // // swagger:model JarV001SchemaArchiveHash type JarV001SchemaArchiveHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The hash value for the archive // Required: true Value *string `json:"value"` } // Validate validates this jar v001 schema archive hash func (m *JarV001SchemaArchiveHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var jarV001SchemaArchiveHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { jarV001SchemaArchiveHashTypeAlgorithmPropEnum = append(jarV001SchemaArchiveHashTypeAlgorithmPropEnum, v) } } const ( // JarV001SchemaArchiveHashAlgorithmSha256 captures enum value "sha256" JarV001SchemaArchiveHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *JarV001SchemaArchiveHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, jarV001SchemaArchiveHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *JarV001SchemaArchiveHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("archive"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("archive"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *JarV001SchemaArchiveHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("archive"+"."+"hash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validates this jar v001 schema archive hash based on context it is used func (m *JarV001SchemaArchiveHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *JarV001SchemaArchiveHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *JarV001SchemaArchiveHash) UnmarshalBinary(b []byte) error { var res JarV001SchemaArchiveHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // JarV001SchemaSignature Information about the included signature in the JAR file // // swagger:model JarV001SchemaSignature type JarV001SchemaSignature struct { // Specifies the PKCS7 signature embedded within the JAR file // Required: true // Read Only: true // Format: byte Content strfmt.Base64 `json:"content"` // public key // Required: true PublicKey *JarV001SchemaSignaturePublicKey `json:"publicKey"` } // Validate validates this jar v001 schema signature func (m *JarV001SchemaSignature) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if err := m.validatePublicKey(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *JarV001SchemaSignature) validateContent(formats strfmt.Registry) error { if err := validate.Required("signature"+"."+"content", "body", strfmt.Base64(m.Content)); err != nil { return err } return nil } func (m *JarV001SchemaSignature) validatePublicKey(formats strfmt.Registry) error { if err := validate.Required("signature"+"."+"publicKey", "body", m.PublicKey); err != nil { return err } if m.PublicKey != nil { if err := m.PublicKey.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signature" + "." + "publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signature" + "." + "publicKey") } return err } } return nil } // ContextValidate validate this jar v001 schema signature based on the context it is used func (m *JarV001SchemaSignature) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateContent(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidatePublicKey(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *JarV001SchemaSignature) contextValidateContent(ctx context.Context, formats strfmt.Registry) error { if err := validate.ReadOnly(ctx, "signature"+"."+"content", "body", strfmt.Base64(m.Content)); err != nil { return err } return nil } func (m *JarV001SchemaSignature) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error { if m.PublicKey != nil { if err := m.PublicKey.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signature" + "." + "publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signature" + "." + "publicKey") } return err } } return nil } // MarshalBinary interface implementation func (m *JarV001SchemaSignature) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *JarV001SchemaSignature) UnmarshalBinary(b []byte) error { var res JarV001SchemaSignature if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // JarV001SchemaSignaturePublicKey The X509 certificate containing the public key JAR which verifies the signature of the JAR // // swagger:model JarV001SchemaSignaturePublicKey type JarV001SchemaSignaturePublicKey struct { // Specifies the content of the X509 certificate containing the public key used to verify the signature // Required: true // Format: byte Content *strfmt.Base64 `json:"content"` } // Validate validates this jar v001 schema signature public key func (m *JarV001SchemaSignaturePublicKey) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *JarV001SchemaSignaturePublicKey) validateContent(formats strfmt.Registry) error { if err := validate.Required("signature"+"."+"publicKey"+"."+"content", "body", m.Content); err != nil { return err } return nil } // ContextValidate validate this jar v001 schema signature public key based on the context it is used func (m *JarV001SchemaSignaturePublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *JarV001SchemaSignaturePublicKey) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *JarV001SchemaSignaturePublicKey) UnmarshalBinary(b []byte) error { var res JarV001SchemaSignaturePublicKey if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/log_entry.go000066400000000000000000000250371455727245600212250ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // LogEntry log entry // // swagger:model LogEntry type LogEntry map[string]LogEntryAnon // Validate validates this log entry func (m LogEntry) Validate(formats strfmt.Registry) error { var res []error for k := range m { if swag.IsZero(m[k]) { // not required continue } if val, ok := m[k]; ok { if err := val.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName(k) } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName(k) } return err } } } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // ContextValidate validate this log entry based on the context it is used func (m LogEntry) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error for k := range m { if val, ok := m[k]; ok { if err := val.ContextValidate(ctx, formats); err != nil { return err } } } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // LogEntryAnon log entry anon // // swagger:model LogEntryAnon type LogEntryAnon struct { // attestation Attestation *LogEntryAnonAttestation `json:"attestation,omitempty"` // body // Required: true Body interface{} `json:"body"` // The time the entry was added to the log as a Unix timestamp in seconds // Required: true IntegratedTime *int64 `json:"integratedTime"` // This is the SHA256 hash of the DER-encoded public key for the log at the time the entry was included in the log // Required: true // Pattern: ^[0-9a-fA-F]{64}$ LogID *string `json:"logID"` // log index // Required: true // Minimum: 0 LogIndex *int64 `json:"logIndex"` // verification Verification *LogEntryAnonVerification `json:"verification,omitempty"` } // Validate validates this log entry anon func (m *LogEntryAnon) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAttestation(formats); err != nil { res = append(res, err) } if err := m.validateBody(formats); err != nil { res = append(res, err) } if err := m.validateIntegratedTime(formats); err != nil { res = append(res, err) } if err := m.validateLogID(formats); err != nil { res = append(res, err) } if err := m.validateLogIndex(formats); err != nil { res = append(res, err) } if err := m.validateVerification(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *LogEntryAnon) validateAttestation(formats strfmt.Registry) error { if swag.IsZero(m.Attestation) { // not required return nil } if m.Attestation != nil { if err := m.Attestation.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("attestation") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("attestation") } return err } } return nil } func (m *LogEntryAnon) validateBody(formats strfmt.Registry) error { if m.Body == nil { return errors.Required("body", "body", nil) } return nil } func (m *LogEntryAnon) validateIntegratedTime(formats strfmt.Registry) error { if err := validate.Required("integratedTime", "body", m.IntegratedTime); err != nil { return err } return nil } func (m *LogEntryAnon) validateLogID(formats strfmt.Registry) error { if err := validate.Required("logID", "body", m.LogID); err != nil { return err } if err := validate.Pattern("logID", "body", *m.LogID, `^[0-9a-fA-F]{64}$`); err != nil { return err } return nil } func (m *LogEntryAnon) validateLogIndex(formats strfmt.Registry) error { if err := validate.Required("logIndex", "body", m.LogIndex); err != nil { return err } if err := validate.MinimumInt("logIndex", "body", *m.LogIndex, 0, false); err != nil { return err } return nil } func (m *LogEntryAnon) validateVerification(formats strfmt.Registry) error { if swag.IsZero(m.Verification) { // not required return nil } if m.Verification != nil { if err := m.Verification.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("verification") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("verification") } return err } } return nil } // ContextValidate validate this log entry anon based on the context it is used func (m *LogEntryAnon) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateAttestation(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidateVerification(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *LogEntryAnon) contextValidateAttestation(ctx context.Context, formats strfmt.Registry) error { if m.Attestation != nil { if swag.IsZero(m.Attestation) { // not required return nil } if err := m.Attestation.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("attestation") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("attestation") } return err } } return nil } func (m *LogEntryAnon) contextValidateVerification(ctx context.Context, formats strfmt.Registry) error { if m.Verification != nil { if swag.IsZero(m.Verification) { // not required return nil } if err := m.Verification.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("verification") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("verification") } return err } } return nil } // MarshalBinary interface implementation func (m *LogEntryAnon) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *LogEntryAnon) UnmarshalBinary(b []byte) error { var res LogEntryAnon if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // LogEntryAnonAttestation log entry anon attestation // // swagger:model LogEntryAnonAttestation type LogEntryAnonAttestation struct { // data // Format: byte Data strfmt.Base64 `json:"data,omitempty"` } // Validate validates this log entry anon attestation func (m *LogEntryAnonAttestation) Validate(formats strfmt.Registry) error { return nil } // ContextValidate validates this log entry anon attestation based on context it is used func (m *LogEntryAnonAttestation) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *LogEntryAnonAttestation) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *LogEntryAnonAttestation) UnmarshalBinary(b []byte) error { var res LogEntryAnonAttestation if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // LogEntryAnonVerification log entry anon verification // // swagger:model LogEntryAnonVerification type LogEntryAnonVerification struct { // inclusion proof InclusionProof *InclusionProof `json:"inclusionProof,omitempty"` // Signature over the logID, logIndex, body and integratedTime. // Format: byte SignedEntryTimestamp strfmt.Base64 `json:"signedEntryTimestamp,omitempty"` } // Validate validates this log entry anon verification func (m *LogEntryAnonVerification) Validate(formats strfmt.Registry) error { var res []error if err := m.validateInclusionProof(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *LogEntryAnonVerification) validateInclusionProof(formats strfmt.Registry) error { if swag.IsZero(m.InclusionProof) { // not required return nil } if m.InclusionProof != nil { if err := m.InclusionProof.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("verification" + "." + "inclusionProof") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("verification" + "." + "inclusionProof") } return err } } return nil } // ContextValidate validate this log entry anon verification based on the context it is used func (m *LogEntryAnonVerification) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateInclusionProof(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *LogEntryAnonVerification) contextValidateInclusionProof(ctx context.Context, formats strfmt.Registry) error { if m.InclusionProof != nil { if swag.IsZero(m.InclusionProof) { // not required return nil } if err := m.InclusionProof.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("verification" + "." + "inclusionProof") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("verification" + "." + "inclusionProof") } return err } } return nil } // MarshalBinary interface implementation func (m *LogEntryAnonVerification) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *LogEntryAnonVerification) UnmarshalBinary(b []byte) error { var res LogEntryAnonVerification if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/log_info.go000066400000000000000000000122251455727245600210120ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "strconv" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // LogInfo log info // // swagger:model LogInfo type LogInfo struct { // inactive shards InactiveShards []*InactiveShardLogInfo `json:"inactiveShards"` // The current hash value stored at the root of the merkle tree // Required: true // Pattern: ^[0-9a-fA-F]{64}$ RootHash *string `json:"rootHash"` // The current signed tree head // Required: true SignedTreeHead *string `json:"signedTreeHead"` // The current treeID // Required: true // Pattern: ^[0-9]+$ TreeID *string `json:"treeID"` // The current number of nodes in the merkle tree // Required: true // Minimum: 1 TreeSize *int64 `json:"treeSize"` } // Validate validates this log info func (m *LogInfo) Validate(formats strfmt.Registry) error { var res []error if err := m.validateInactiveShards(formats); err != nil { res = append(res, err) } if err := m.validateRootHash(formats); err != nil { res = append(res, err) } if err := m.validateSignedTreeHead(formats); err != nil { res = append(res, err) } if err := m.validateTreeID(formats); err != nil { res = append(res, err) } if err := m.validateTreeSize(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *LogInfo) validateInactiveShards(formats strfmt.Registry) error { if swag.IsZero(m.InactiveShards) { // not required return nil } for i := 0; i < len(m.InactiveShards); i++ { if swag.IsZero(m.InactiveShards[i]) { // not required continue } if m.InactiveShards[i] != nil { if err := m.InactiveShards[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("inactiveShards" + "." + strconv.Itoa(i)) } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("inactiveShards" + "." + strconv.Itoa(i)) } return err } } } return nil } func (m *LogInfo) validateRootHash(formats strfmt.Registry) error { if err := validate.Required("rootHash", "body", m.RootHash); err != nil { return err } if err := validate.Pattern("rootHash", "body", *m.RootHash, `^[0-9a-fA-F]{64}$`); err != nil { return err } return nil } func (m *LogInfo) validateSignedTreeHead(formats strfmt.Registry) error { if err := validate.Required("signedTreeHead", "body", m.SignedTreeHead); err != nil { return err } return nil } func (m *LogInfo) validateTreeID(formats strfmt.Registry) error { if err := validate.Required("treeID", "body", m.TreeID); err != nil { return err } if err := validate.Pattern("treeID", "body", *m.TreeID, `^[0-9]+$`); err != nil { return err } return nil } func (m *LogInfo) validateTreeSize(formats strfmt.Registry) error { if err := validate.Required("treeSize", "body", m.TreeSize); err != nil { return err } if err := validate.MinimumInt("treeSize", "body", *m.TreeSize, 1, false); err != nil { return err } return nil } // ContextValidate validate this log info based on the context it is used func (m *LogInfo) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateInactiveShards(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *LogInfo) contextValidateInactiveShards(ctx context.Context, formats strfmt.Registry) error { for i := 0; i < len(m.InactiveShards); i++ { if m.InactiveShards[i] != nil { if swag.IsZero(m.InactiveShards[i]) { // not required return nil } if err := m.InactiveShards[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("inactiveShards" + "." + strconv.Itoa(i)) } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("inactiveShards" + "." + strconv.Itoa(i)) } return err } } } return nil } // MarshalBinary interface implementation func (m *LogInfo) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *LogInfo) UnmarshalBinary(b []byte) error { var res LogInfo if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/proposed_entry.go000066400000000000000000000117711455727245600222770ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "io" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/validate" ) // ProposedEntry proposed entry // // swagger:discriminator ProposedEntry kind type ProposedEntry interface { runtime.Validatable runtime.ContextValidatable // kind // Required: true Kind() string SetKind(string) // AdditionalProperties in base type shoud be handled just like regular properties // At this moment, the base type property is pushed down to the subtype } type proposedEntry struct { kindField string } // Kind gets the kind of this polymorphic type func (m *proposedEntry) Kind() string { return "ProposedEntry" } // SetKind sets the kind of this polymorphic type func (m *proposedEntry) SetKind(val string) { } // UnmarshalProposedEntrySlice unmarshals polymorphic slices of ProposedEntry func UnmarshalProposedEntrySlice(reader io.Reader, consumer runtime.Consumer) ([]ProposedEntry, error) { var elements []json.RawMessage if err := consumer.Consume(reader, &elements); err != nil { return nil, err } var result []ProposedEntry for _, element := range elements { obj, err := unmarshalProposedEntry(element, consumer) if err != nil { return nil, err } result = append(result, obj) } return result, nil } // UnmarshalProposedEntry unmarshals polymorphic ProposedEntry func UnmarshalProposedEntry(reader io.Reader, consumer runtime.Consumer) (ProposedEntry, error) { // we need to read this twice, so first into a buffer data, err := io.ReadAll(reader) if err != nil { return nil, err } return unmarshalProposedEntry(data, consumer) } func unmarshalProposedEntry(data []byte, consumer runtime.Consumer) (ProposedEntry, error) { buf := bytes.NewBuffer(data) buf2 := bytes.NewBuffer(data) // the first time this is read is to fetch the value of the kind property. var getType struct { Kind string `json:"kind"` } if err := consumer.Consume(buf, &getType); err != nil { return nil, err } if err := validate.RequiredString("kind", "body", getType.Kind); err != nil { return nil, err } // The value of kind is used to determine which type to create and unmarshal the data into switch getType.Kind { case "ProposedEntry": var result proposedEntry if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "alpine": var result Alpine if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "cose": var result Cose if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "dsse": var result DSSE if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "hashedrekord": var result Hashedrekord if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "helm": var result Helm if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "intoto": var result Intoto if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "jar": var result Jar if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "rekord": var result Rekord if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "rfc3161": var result Rfc3161 if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "rpm": var result Rpm if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "tuf": var result TUF if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil } return nil, errors.New(422, "invalid kind value: %q", getType.Kind) } // Validate validates this proposed entry func (m *proposedEntry) Validate(formats strfmt.Registry) error { return nil } // ContextValidate validates this proposed entry based on context it is used func (m *proposedEntry) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } rekor-1.3.5/pkg/generated/models/rekord.go000066400000000000000000000117441455727245600205110ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // Rekord Rekord object // // swagger:model rekord type Rekord struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec RekordSchema `json:"spec"` } // Kind gets the kind of this subtype func (m *Rekord) Kind() string { return "rekord" } // SetKind sets the kind of this subtype func (m *Rekord) SetKind(val string) { } // UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure func (m *Rekord) UnmarshalJSON(raw []byte) error { var data struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec RekordSchema `json:"spec"` } buf := bytes.NewBuffer(raw) dec := json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&data); err != nil { return err } var base struct { /* Just the base type fields. Used for unmashalling polymorphic types.*/ Kind string `json:"kind"` } buf = bytes.NewBuffer(raw) dec = json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&base); err != nil { return err } var result Rekord if base.Kind != result.Kind() { /* Not the type we're looking for. */ return errors.New(422, "invalid kind value: %q", base.Kind) } result.APIVersion = data.APIVersion result.Spec = data.Spec *m = result return nil } // MarshalJSON marshals this object with a polymorphic type to a JSON structure func (m Rekord) MarshalJSON() ([]byte, error) { var b1, b2, b3 []byte var err error b1, err = json.Marshal(struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec RekordSchema `json:"spec"` }{ APIVersion: m.APIVersion, Spec: m.Spec, }) if err != nil { return nil, err } b2, err = json.Marshal(struct { Kind string `json:"kind"` }{ Kind: m.Kind(), }) if err != nil { return nil, err } return swag.ConcatJSON(b1, b2, b3), nil } // Validate validates this rekord func (m *Rekord) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAPIVersion(formats); err != nil { res = append(res, err) } if err := m.validateSpec(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *Rekord) validateAPIVersion(formats strfmt.Registry) error { if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil { return err } if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil { return err } return nil } func (m *Rekord) validateSpec(formats strfmt.Registry) error { if m.Spec == nil { return errors.Required("spec", "body", nil) } return nil } // ContextValidate validate this rekord based on the context it is used func (m *Rekord) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *Rekord) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *Rekord) UnmarshalBinary(b []byte) error { var res Rekord if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/rekord_schema.go000066400000000000000000000016401455727245600220230ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command // RekordSchema Rekor Schema // // # Schema for Rekord objects // // swagger:model rekordSchema type RekordSchema interface{} rekor-1.3.5/pkg/generated/models/rekord_v001_schema.go000066400000000000000000000363121455727245600225750ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // RekordV001Schema Rekor v0.0.1 Schema // // # Schema for Rekord object // // swagger:model rekordV001Schema type RekordV001Schema struct { // data // Required: true Data *RekordV001SchemaData `json:"data"` // signature // Required: true Signature *RekordV001SchemaSignature `json:"signature"` } // Validate validates this rekord v001 schema func (m *RekordV001Schema) Validate(formats strfmt.Registry) error { var res []error if err := m.validateData(formats); err != nil { res = append(res, err) } if err := m.validateSignature(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *RekordV001Schema) validateData(formats strfmt.Registry) error { if err := validate.Required("data", "body", m.Data); err != nil { return err } if m.Data != nil { if err := m.Data.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data") } return err } } return nil } func (m *RekordV001Schema) validateSignature(formats strfmt.Registry) error { if err := validate.Required("signature", "body", m.Signature); err != nil { return err } if m.Signature != nil { if err := m.Signature.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signature") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signature") } return err } } return nil } // ContextValidate validate this rekord v001 schema based on the context it is used func (m *RekordV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateData(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidateSignature(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *RekordV001Schema) contextValidateData(ctx context.Context, formats strfmt.Registry) error { if m.Data != nil { if err := m.Data.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data") } return err } } return nil } func (m *RekordV001Schema) contextValidateSignature(ctx context.Context, formats strfmt.Registry) error { if m.Signature != nil { if err := m.Signature.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signature") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signature") } return err } } return nil } // MarshalBinary interface implementation func (m *RekordV001Schema) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *RekordV001Schema) UnmarshalBinary(b []byte) error { var res RekordV001Schema if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // RekordV001SchemaData Information about the content associated with the entry // // swagger:model RekordV001SchemaData type RekordV001SchemaData struct { // Specifies the content inline within the document // Format: byte Content strfmt.Base64 `json:"content,omitempty"` // hash Hash *RekordV001SchemaDataHash `json:"hash,omitempty"` } // Validate validates this rekord v001 schema data func (m *RekordV001SchemaData) Validate(formats strfmt.Registry) error { var res []error if err := m.validateHash(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *RekordV001SchemaData) validateHash(formats strfmt.Registry) error { if swag.IsZero(m.Hash) { // not required return nil } if m.Hash != nil { if err := m.Hash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data" + "." + "hash") } return err } } return nil } // ContextValidate validate this rekord v001 schema data based on the context it is used func (m *RekordV001SchemaData) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateHash(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *RekordV001SchemaData) contextValidateHash(ctx context.Context, formats strfmt.Registry) error { if m.Hash != nil { if swag.IsZero(m.Hash) { // not required return nil } if err := m.Hash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("data" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("data" + "." + "hash") } return err } } return nil } // MarshalBinary interface implementation func (m *RekordV001SchemaData) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *RekordV001SchemaData) UnmarshalBinary(b []byte) error { var res RekordV001SchemaData if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // RekordV001SchemaDataHash Specifies the hash algorithm and value for the content // // swagger:model RekordV001SchemaDataHash type RekordV001SchemaDataHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The hash value for the content // Required: true Value *string `json:"value"` } // Validate validates this rekord v001 schema data hash func (m *RekordV001SchemaDataHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var rekordV001SchemaDataHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { rekordV001SchemaDataHashTypeAlgorithmPropEnum = append(rekordV001SchemaDataHashTypeAlgorithmPropEnum, v) } } const ( // RekordV001SchemaDataHashAlgorithmSha256 captures enum value "sha256" RekordV001SchemaDataHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *RekordV001SchemaDataHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, rekordV001SchemaDataHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *RekordV001SchemaDataHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("data"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("data"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *RekordV001SchemaDataHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("data"+"."+"hash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validate this rekord v001 schema data hash based on the context it is used func (m *RekordV001SchemaDataHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *RekordV001SchemaDataHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *RekordV001SchemaDataHash) UnmarshalBinary(b []byte) error { var res RekordV001SchemaDataHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // RekordV001SchemaSignature Information about the detached signature associated with the entry // // swagger:model RekordV001SchemaSignature type RekordV001SchemaSignature struct { // Specifies the content of the signature inline within the document // Required: true // Format: byte Content *strfmt.Base64 `json:"content"` // Specifies the format of the signature // Required: true // Enum: [pgp minisign x509 ssh] Format *string `json:"format"` // public key // Required: true PublicKey *RekordV001SchemaSignaturePublicKey `json:"publicKey"` } // Validate validates this rekord v001 schema signature func (m *RekordV001SchemaSignature) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if err := m.validateFormat(formats); err != nil { res = append(res, err) } if err := m.validatePublicKey(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *RekordV001SchemaSignature) validateContent(formats strfmt.Registry) error { if err := validate.Required("signature"+"."+"content", "body", m.Content); err != nil { return err } return nil } var rekordV001SchemaSignatureTypeFormatPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["pgp","minisign","x509","ssh"]`), &res); err != nil { panic(err) } for _, v := range res { rekordV001SchemaSignatureTypeFormatPropEnum = append(rekordV001SchemaSignatureTypeFormatPropEnum, v) } } const ( // RekordV001SchemaSignatureFormatPgp captures enum value "pgp" RekordV001SchemaSignatureFormatPgp string = "pgp" // RekordV001SchemaSignatureFormatMinisign captures enum value "minisign" RekordV001SchemaSignatureFormatMinisign string = "minisign" // RekordV001SchemaSignatureFormatX509 captures enum value "x509" RekordV001SchemaSignatureFormatX509 string = "x509" // RekordV001SchemaSignatureFormatSSH captures enum value "ssh" RekordV001SchemaSignatureFormatSSH string = "ssh" ) // prop value enum func (m *RekordV001SchemaSignature) validateFormatEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, rekordV001SchemaSignatureTypeFormatPropEnum, true); err != nil { return err } return nil } func (m *RekordV001SchemaSignature) validateFormat(formats strfmt.Registry) error { if err := validate.Required("signature"+"."+"format", "body", m.Format); err != nil { return err } // value enum if err := m.validateFormatEnum("signature"+"."+"format", "body", *m.Format); err != nil { return err } return nil } func (m *RekordV001SchemaSignature) validatePublicKey(formats strfmt.Registry) error { if err := validate.Required("signature"+"."+"publicKey", "body", m.PublicKey); err != nil { return err } if m.PublicKey != nil { if err := m.PublicKey.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signature" + "." + "publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signature" + "." + "publicKey") } return err } } return nil } // ContextValidate validate this rekord v001 schema signature based on the context it is used func (m *RekordV001SchemaSignature) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidatePublicKey(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *RekordV001SchemaSignature) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error { if m.PublicKey != nil { if err := m.PublicKey.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("signature" + "." + "publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("signature" + "." + "publicKey") } return err } } return nil } // MarshalBinary interface implementation func (m *RekordV001SchemaSignature) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *RekordV001SchemaSignature) UnmarshalBinary(b []byte) error { var res RekordV001SchemaSignature if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // RekordV001SchemaSignaturePublicKey The public key that can verify the signature // // swagger:model RekordV001SchemaSignaturePublicKey type RekordV001SchemaSignaturePublicKey struct { // Specifies the content of the public key inline within the document // Required: true // Format: byte Content *strfmt.Base64 `json:"content"` } // Validate validates this rekord v001 schema signature public key func (m *RekordV001SchemaSignaturePublicKey) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *RekordV001SchemaSignaturePublicKey) validateContent(formats strfmt.Registry) error { if err := validate.Required("signature"+"."+"publicKey"+"."+"content", "body", m.Content); err != nil { return err } return nil } // ContextValidate validates this rekord v001 schema signature public key based on context it is used func (m *RekordV001SchemaSignaturePublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *RekordV001SchemaSignaturePublicKey) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *RekordV001SchemaSignaturePublicKey) UnmarshalBinary(b []byte) error { var res RekordV001SchemaSignaturePublicKey if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/rfc3161.go000066400000000000000000000117751455727245600203140ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // Rfc3161 RFC3161 Timestamp // // swagger:model rfc3161 type Rfc3161 struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec Rfc3161Schema `json:"spec"` } // Kind gets the kind of this subtype func (m *Rfc3161) Kind() string { return "rfc3161" } // SetKind sets the kind of this subtype func (m *Rfc3161) SetKind(val string) { } // UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure func (m *Rfc3161) UnmarshalJSON(raw []byte) error { var data struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec Rfc3161Schema `json:"spec"` } buf := bytes.NewBuffer(raw) dec := json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&data); err != nil { return err } var base struct { /* Just the base type fields. Used for unmashalling polymorphic types.*/ Kind string `json:"kind"` } buf = bytes.NewBuffer(raw) dec = json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&base); err != nil { return err } var result Rfc3161 if base.Kind != result.Kind() { /* Not the type we're looking for. */ return errors.New(422, "invalid kind value: %q", base.Kind) } result.APIVersion = data.APIVersion result.Spec = data.Spec *m = result return nil } // MarshalJSON marshals this object with a polymorphic type to a JSON structure func (m Rfc3161) MarshalJSON() ([]byte, error) { var b1, b2, b3 []byte var err error b1, err = json.Marshal(struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec Rfc3161Schema `json:"spec"` }{ APIVersion: m.APIVersion, Spec: m.Spec, }) if err != nil { return nil, err } b2, err = json.Marshal(struct { Kind string `json:"kind"` }{ Kind: m.Kind(), }) if err != nil { return nil, err } return swag.ConcatJSON(b1, b2, b3), nil } // Validate validates this rfc3161 func (m *Rfc3161) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAPIVersion(formats); err != nil { res = append(res, err) } if err := m.validateSpec(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *Rfc3161) validateAPIVersion(formats strfmt.Registry) error { if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil { return err } if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil { return err } return nil } func (m *Rfc3161) validateSpec(formats strfmt.Registry) error { if m.Spec == nil { return errors.Required("spec", "body", nil) } return nil } // ContextValidate validate this rfc3161 based on the context it is used func (m *Rfc3161) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *Rfc3161) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *Rfc3161) UnmarshalBinary(b []byte) error { var res Rfc3161 if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/rfc3161_schema.go000066400000000000000000000016631455727245600216270ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command // Rfc3161Schema Timestamp Schema // // # Schema for RFC 3161 timestamp objects // // swagger:model rfc3161Schema type Rfc3161Schema interface{} rekor-1.3.5/pkg/generated/models/rfc3161_v001_schema.go000066400000000000000000000106051455727245600223710ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // Rfc3161V001Schema Timestamp v0.0.1 Schema // // # Schema for RFC3161 entries // // swagger:model rfc3161V001Schema type Rfc3161V001Schema struct { // tsr // Required: true Tsr *Rfc3161V001SchemaTsr `json:"tsr"` } // Validate validates this rfc3161 v001 schema func (m *Rfc3161V001Schema) Validate(formats strfmt.Registry) error { var res []error if err := m.validateTsr(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *Rfc3161V001Schema) validateTsr(formats strfmt.Registry) error { if err := validate.Required("tsr", "body", m.Tsr); err != nil { return err } if m.Tsr != nil { if err := m.Tsr.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("tsr") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("tsr") } return err } } return nil } // ContextValidate validate this rfc3161 v001 schema based on the context it is used func (m *Rfc3161V001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateTsr(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *Rfc3161V001Schema) contextValidateTsr(ctx context.Context, formats strfmt.Registry) error { if m.Tsr != nil { if err := m.Tsr.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("tsr") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("tsr") } return err } } return nil } // MarshalBinary interface implementation func (m *Rfc3161V001Schema) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *Rfc3161V001Schema) UnmarshalBinary(b []byte) error { var res Rfc3161V001Schema if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // Rfc3161V001SchemaTsr Information about the tsr file associated with the entry // // swagger:model Rfc3161V001SchemaTsr type Rfc3161V001SchemaTsr struct { // Specifies the tsr file content inline within the document // Required: true // Format: byte Content *strfmt.Base64 `json:"content"` } // Validate validates this rfc3161 v001 schema tsr func (m *Rfc3161V001SchemaTsr) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *Rfc3161V001SchemaTsr) validateContent(formats strfmt.Registry) error { if err := validate.Required("tsr"+"."+"content", "body", m.Content); err != nil { return err } return nil } // ContextValidate validates this rfc3161 v001 schema tsr based on context it is used func (m *Rfc3161V001SchemaTsr) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *Rfc3161V001SchemaTsr) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *Rfc3161V001SchemaTsr) UnmarshalBinary(b []byte) error { var res Rfc3161V001SchemaTsr if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/rpm.go000066400000000000000000000116431455727245600200170ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // Rpm RPM package // // swagger:model rpm type Rpm struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec RpmSchema `json:"spec"` } // Kind gets the kind of this subtype func (m *Rpm) Kind() string { return "rpm" } // SetKind sets the kind of this subtype func (m *Rpm) SetKind(val string) { } // UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure func (m *Rpm) UnmarshalJSON(raw []byte) error { var data struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec RpmSchema `json:"spec"` } buf := bytes.NewBuffer(raw) dec := json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&data); err != nil { return err } var base struct { /* Just the base type fields. Used for unmashalling polymorphic types.*/ Kind string `json:"kind"` } buf = bytes.NewBuffer(raw) dec = json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&base); err != nil { return err } var result Rpm if base.Kind != result.Kind() { /* Not the type we're looking for. */ return errors.New(422, "invalid kind value: %q", base.Kind) } result.APIVersion = data.APIVersion result.Spec = data.Spec *m = result return nil } // MarshalJSON marshals this object with a polymorphic type to a JSON structure func (m Rpm) MarshalJSON() ([]byte, error) { var b1, b2, b3 []byte var err error b1, err = json.Marshal(struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec RpmSchema `json:"spec"` }{ APIVersion: m.APIVersion, Spec: m.Spec, }) if err != nil { return nil, err } b2, err = json.Marshal(struct { Kind string `json:"kind"` }{ Kind: m.Kind(), }) if err != nil { return nil, err } return swag.ConcatJSON(b1, b2, b3), nil } // Validate validates this rpm func (m *Rpm) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAPIVersion(formats); err != nil { res = append(res, err) } if err := m.validateSpec(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *Rpm) validateAPIVersion(formats strfmt.Registry) error { if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil { return err } if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil { return err } return nil } func (m *Rpm) validateSpec(formats strfmt.Registry) error { if m.Spec == nil { return errors.Required("spec", "body", nil) } return nil } // ContextValidate validate this rpm based on the context it is used func (m *Rpm) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *Rpm) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *Rpm) UnmarshalBinary(b []byte) error { var res Rpm if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/rpm_schema.go000066400000000000000000000016221455727245600213330ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command // RpmSchema RPM Schema // // # Schema for RPM objects // // swagger:model rpmSchema type RpmSchema interface{} rekor-1.3.5/pkg/generated/models/rpm_v001_schema.go000066400000000000000000000256231455727245600221100ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // RpmV001Schema RPM v0.0.1 Schema // // # Schema for RPM entries // // swagger:model rpmV001Schema type RpmV001Schema struct { // package // Required: true Package *RpmV001SchemaPackage `json:"package"` // public key // Required: true PublicKey *RpmV001SchemaPublicKey `json:"publicKey"` } // Validate validates this rpm v001 schema func (m *RpmV001Schema) Validate(formats strfmt.Registry) error { var res []error if err := m.validatePackage(formats); err != nil { res = append(res, err) } if err := m.validatePublicKey(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *RpmV001Schema) validatePackage(formats strfmt.Registry) error { if err := validate.Required("package", "body", m.Package); err != nil { return err } if m.Package != nil { if err := m.Package.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("package") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("package") } return err } } return nil } func (m *RpmV001Schema) validatePublicKey(formats strfmt.Registry) error { if err := validate.Required("publicKey", "body", m.PublicKey); err != nil { return err } if m.PublicKey != nil { if err := m.PublicKey.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("publicKey") } return err } } return nil } // ContextValidate validate this rpm v001 schema based on the context it is used func (m *RpmV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidatePackage(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidatePublicKey(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *RpmV001Schema) contextValidatePackage(ctx context.Context, formats strfmt.Registry) error { if m.Package != nil { if err := m.Package.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("package") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("package") } return err } } return nil } func (m *RpmV001Schema) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error { if m.PublicKey != nil { if err := m.PublicKey.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("publicKey") } return err } } return nil } // MarshalBinary interface implementation func (m *RpmV001Schema) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *RpmV001Schema) UnmarshalBinary(b []byte) error { var res RpmV001Schema if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // RpmV001SchemaPackage Information about the package associated with the entry // // swagger:model RpmV001SchemaPackage type RpmV001SchemaPackage struct { // Specifies the package inline within the document // Format: byte Content strfmt.Base64 `json:"content,omitempty"` // hash Hash *RpmV001SchemaPackageHash `json:"hash,omitempty"` // Values of the RPM headers // Read Only: true Headers map[string]string `json:"headers,omitempty"` } // Validate validates this rpm v001 schema package func (m *RpmV001SchemaPackage) Validate(formats strfmt.Registry) error { var res []error if err := m.validateHash(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *RpmV001SchemaPackage) validateHash(formats strfmt.Registry) error { if swag.IsZero(m.Hash) { // not required return nil } if m.Hash != nil { if err := m.Hash.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("package" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("package" + "." + "hash") } return err } } return nil } // ContextValidate validate this rpm v001 schema package based on the context it is used func (m *RpmV001SchemaPackage) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateHash(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidateHeaders(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *RpmV001SchemaPackage) contextValidateHash(ctx context.Context, formats strfmt.Registry) error { if m.Hash != nil { if swag.IsZero(m.Hash) { // not required return nil } if err := m.Hash.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("package" + "." + "hash") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("package" + "." + "hash") } return err } } return nil } func (m *RpmV001SchemaPackage) contextValidateHeaders(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *RpmV001SchemaPackage) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *RpmV001SchemaPackage) UnmarshalBinary(b []byte) error { var res RpmV001SchemaPackage if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // RpmV001SchemaPackageHash Specifies the hash algorithm and value for the package // // swagger:model RpmV001SchemaPackageHash type RpmV001SchemaPackageHash struct { // The hashing function used to compute the hash value // Required: true // Enum: [sha256] Algorithm *string `json:"algorithm"` // The hash value for the package // Required: true Value *string `json:"value"` } // Validate validates this rpm v001 schema package hash func (m *RpmV001SchemaPackageHash) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAlgorithm(formats); err != nil { res = append(res, err) } if err := m.validateValue(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var rpmV001SchemaPackageHashTypeAlgorithmPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["sha256"]`), &res); err != nil { panic(err) } for _, v := range res { rpmV001SchemaPackageHashTypeAlgorithmPropEnum = append(rpmV001SchemaPackageHashTypeAlgorithmPropEnum, v) } } const ( // RpmV001SchemaPackageHashAlgorithmSha256 captures enum value "sha256" RpmV001SchemaPackageHashAlgorithmSha256 string = "sha256" ) // prop value enum func (m *RpmV001SchemaPackageHash) validateAlgorithmEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, rpmV001SchemaPackageHashTypeAlgorithmPropEnum, true); err != nil { return err } return nil } func (m *RpmV001SchemaPackageHash) validateAlgorithm(formats strfmt.Registry) error { if err := validate.Required("package"+"."+"hash"+"."+"algorithm", "body", m.Algorithm); err != nil { return err } // value enum if err := m.validateAlgorithmEnum("package"+"."+"hash"+"."+"algorithm", "body", *m.Algorithm); err != nil { return err } return nil } func (m *RpmV001SchemaPackageHash) validateValue(formats strfmt.Registry) error { if err := validate.Required("package"+"."+"hash"+"."+"value", "body", m.Value); err != nil { return err } return nil } // ContextValidate validates this rpm v001 schema package hash based on context it is used func (m *RpmV001SchemaPackageHash) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *RpmV001SchemaPackageHash) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *RpmV001SchemaPackageHash) UnmarshalBinary(b []byte) error { var res RpmV001SchemaPackageHash if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // RpmV001SchemaPublicKey The PGP public key that can verify the RPM signature // // swagger:model RpmV001SchemaPublicKey type RpmV001SchemaPublicKey struct { // Specifies the content of the public key inline within the document // Required: true // Format: byte Content *strfmt.Base64 `json:"content"` } // Validate validates this rpm v001 schema public key func (m *RpmV001SchemaPublicKey) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *RpmV001SchemaPublicKey) validateContent(formats strfmt.Registry) error { if err := validate.Required("publicKey"+"."+"content", "body", m.Content); err != nil { return err } return nil } // ContextValidate validates this rpm v001 schema public key based on context it is used func (m *RpmV001SchemaPublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *RpmV001SchemaPublicKey) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *RpmV001SchemaPublicKey) UnmarshalBinary(b []byte) error { var res RpmV001SchemaPublicKey if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/search_index.go000066400000000000000000000201411455727245600216460ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // SearchIndex search index // // swagger:model SearchIndex type SearchIndex struct { // email // Format: email Email strfmt.Email `json:"email,omitempty"` // hash // Pattern: ^(sha512:)?[0-9a-fA-F]{128}$|^(sha256:)?[0-9a-fA-F]{64}$|^(sha1:)?[0-9a-fA-F]{40}$ Hash string `json:"hash,omitempty"` // operator // Enum: [and or] Operator string `json:"operator,omitempty"` // public key PublicKey *SearchIndexPublicKey `json:"publicKey,omitempty"` } // Validate validates this search index func (m *SearchIndex) Validate(formats strfmt.Registry) error { var res []error if err := m.validateEmail(formats); err != nil { res = append(res, err) } if err := m.validateHash(formats); err != nil { res = append(res, err) } if err := m.validateOperator(formats); err != nil { res = append(res, err) } if err := m.validatePublicKey(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *SearchIndex) validateEmail(formats strfmt.Registry) error { if swag.IsZero(m.Email) { // not required return nil } if err := validate.FormatOf("email", "body", "email", m.Email.String(), formats); err != nil { return err } return nil } func (m *SearchIndex) validateHash(formats strfmt.Registry) error { if swag.IsZero(m.Hash) { // not required return nil } if err := validate.Pattern("hash", "body", m.Hash, `^(sha512:)?[0-9a-fA-F]{128}$|^(sha256:)?[0-9a-fA-F]{64}$|^(sha1:)?[0-9a-fA-F]{40}$`); err != nil { return err } return nil } var searchIndexTypeOperatorPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["and","or"]`), &res); err != nil { panic(err) } for _, v := range res { searchIndexTypeOperatorPropEnum = append(searchIndexTypeOperatorPropEnum, v) } } const ( // SearchIndexOperatorAnd captures enum value "and" SearchIndexOperatorAnd string = "and" // SearchIndexOperatorOr captures enum value "or" SearchIndexOperatorOr string = "or" ) // prop value enum func (m *SearchIndex) validateOperatorEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, searchIndexTypeOperatorPropEnum, true); err != nil { return err } return nil } func (m *SearchIndex) validateOperator(formats strfmt.Registry) error { if swag.IsZero(m.Operator) { // not required return nil } // value enum if err := m.validateOperatorEnum("operator", "body", m.Operator); err != nil { return err } return nil } func (m *SearchIndex) validatePublicKey(formats strfmt.Registry) error { if swag.IsZero(m.PublicKey) { // not required return nil } if m.PublicKey != nil { if err := m.PublicKey.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("publicKey") } return err } } return nil } // ContextValidate validate this search index based on the context it is used func (m *SearchIndex) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidatePublicKey(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *SearchIndex) contextValidatePublicKey(ctx context.Context, formats strfmt.Registry) error { if m.PublicKey != nil { if swag.IsZero(m.PublicKey) { // not required return nil } if err := m.PublicKey.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("publicKey") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("publicKey") } return err } } return nil } // MarshalBinary interface implementation func (m *SearchIndex) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *SearchIndex) UnmarshalBinary(b []byte) error { var res SearchIndex if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // SearchIndexPublicKey search index public key // // swagger:model SearchIndexPublicKey type SearchIndexPublicKey struct { // content // Format: byte Content strfmt.Base64 `json:"content,omitempty"` // format // Required: true // Enum: [pgp x509 minisign ssh tuf] Format *string `json:"format"` // url // Format: uri URL strfmt.URI `json:"url,omitempty"` } // Validate validates this search index public key func (m *SearchIndexPublicKey) Validate(formats strfmt.Registry) error { var res []error if err := m.validateFormat(formats); err != nil { res = append(res, err) } if err := m.validateURL(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } var searchIndexPublicKeyTypeFormatPropEnum []interface{} func init() { var res []string if err := json.Unmarshal([]byte(`["pgp","x509","minisign","ssh","tuf"]`), &res); err != nil { panic(err) } for _, v := range res { searchIndexPublicKeyTypeFormatPropEnum = append(searchIndexPublicKeyTypeFormatPropEnum, v) } } const ( // SearchIndexPublicKeyFormatPgp captures enum value "pgp" SearchIndexPublicKeyFormatPgp string = "pgp" // SearchIndexPublicKeyFormatX509 captures enum value "x509" SearchIndexPublicKeyFormatX509 string = "x509" // SearchIndexPublicKeyFormatMinisign captures enum value "minisign" SearchIndexPublicKeyFormatMinisign string = "minisign" // SearchIndexPublicKeyFormatSSH captures enum value "ssh" SearchIndexPublicKeyFormatSSH string = "ssh" // SearchIndexPublicKeyFormatTUF captures enum value "tuf" SearchIndexPublicKeyFormatTUF string = "tuf" ) // prop value enum func (m *SearchIndexPublicKey) validateFormatEnum(path, location string, value string) error { if err := validate.EnumCase(path, location, value, searchIndexPublicKeyTypeFormatPropEnum, true); err != nil { return err } return nil } func (m *SearchIndexPublicKey) validateFormat(formats strfmt.Registry) error { if err := validate.Required("publicKey"+"."+"format", "body", m.Format); err != nil { return err } // value enum if err := m.validateFormatEnum("publicKey"+"."+"format", "body", *m.Format); err != nil { return err } return nil } func (m *SearchIndexPublicKey) validateURL(formats strfmt.Registry) error { if swag.IsZero(m.URL) { // not required return nil } if err := validate.FormatOf("publicKey"+"."+"url", "body", "uri", m.URL.String(), formats); err != nil { return err } return nil } // ContextValidate validates this search index public key based on context it is used func (m *SearchIndexPublicKey) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *SearchIndexPublicKey) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *SearchIndexPublicKey) UnmarshalBinary(b []byte) error { var res SearchIndexPublicKey if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/search_log_query.go000066400000000000000000000153631455727245600225570ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "io" "strconv" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // SearchLogQuery search log query // // swagger:model SearchLogQuery type SearchLogQuery struct { entriesField []ProposedEntry // entry u UI ds // Max Items: 10 // Min Items: 1 EntryUUIDs []string `json:"entryUUIDs"` // log indexes // Max Items: 10 // Min Items: 1 LogIndexes []*int64 `json:"logIndexes"` } // Entries gets the entries of this base type func (m *SearchLogQuery) Entries() []ProposedEntry { return m.entriesField } // SetEntries sets the entries of this base type func (m *SearchLogQuery) SetEntries(val []ProposedEntry) { m.entriesField = val } // UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure func (m *SearchLogQuery) UnmarshalJSON(raw []byte) error { var data struct { Entries json.RawMessage `json:"entries"` EntryUUIDs []string `json:"entryUUIDs"` LogIndexes []*int64 `json:"logIndexes"` } buf := bytes.NewBuffer(raw) dec := json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&data); err != nil { return err } var propEntries []ProposedEntry if string(data.Entries) != "null" { entries, err := UnmarshalProposedEntrySlice(bytes.NewBuffer(data.Entries), runtime.JSONConsumer()) if err != nil && err != io.EOF { return err } propEntries = entries } var result SearchLogQuery // entries result.entriesField = propEntries // entryUUIDs result.EntryUUIDs = data.EntryUUIDs // logIndexes result.LogIndexes = data.LogIndexes *m = result return nil } // MarshalJSON marshals this object with a polymorphic type to a JSON structure func (m SearchLogQuery) MarshalJSON() ([]byte, error) { var b1, b2, b3 []byte var err error b1, err = json.Marshal(struct { EntryUUIDs []string `json:"entryUUIDs"` LogIndexes []*int64 `json:"logIndexes"` }{ EntryUUIDs: m.EntryUUIDs, LogIndexes: m.LogIndexes, }) if err != nil { return nil, err } b2, err = json.Marshal(struct { Entries []ProposedEntry `json:"entries"` }{ Entries: m.entriesField, }) if err != nil { return nil, err } return swag.ConcatJSON(b1, b2, b3), nil } // Validate validates this search log query func (m *SearchLogQuery) Validate(formats strfmt.Registry) error { var res []error if err := m.validateEntries(formats); err != nil { res = append(res, err) } if err := m.validateEntryUUIDs(formats); err != nil { res = append(res, err) } if err := m.validateLogIndexes(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *SearchLogQuery) validateEntries(formats strfmt.Registry) error { if swag.IsZero(m.Entries()) { // not required return nil } iEntriesSize := int64(len(m.Entries())) if err := validate.MinItems("entries", "body", iEntriesSize, 1); err != nil { return err } if err := validate.MaxItems("entries", "body", iEntriesSize, 10); err != nil { return err } for i := 0; i < len(m.Entries()); i++ { if err := m.entriesField[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("entries" + "." + strconv.Itoa(i)) } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("entries" + "." + strconv.Itoa(i)) } return err } } return nil } func (m *SearchLogQuery) validateEntryUUIDs(formats strfmt.Registry) error { if swag.IsZero(m.EntryUUIDs) { // not required return nil } iEntryUUIDsSize := int64(len(m.EntryUUIDs)) if err := validate.MinItems("entryUUIDs", "body", iEntryUUIDsSize, 1); err != nil { return err } if err := validate.MaxItems("entryUUIDs", "body", iEntryUUIDsSize, 10); err != nil { return err } for i := 0; i < len(m.EntryUUIDs); i++ { if err := validate.Pattern("entryUUIDs"+"."+strconv.Itoa(i), "body", m.EntryUUIDs[i], `^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$`); err != nil { return err } } return nil } func (m *SearchLogQuery) validateLogIndexes(formats strfmt.Registry) error { if swag.IsZero(m.LogIndexes) { // not required return nil } iLogIndexesSize := int64(len(m.LogIndexes)) if err := validate.MinItems("logIndexes", "body", iLogIndexesSize, 1); err != nil { return err } if err := validate.MaxItems("logIndexes", "body", iLogIndexesSize, 10); err != nil { return err } for i := 0; i < len(m.LogIndexes); i++ { if swag.IsZero(m.LogIndexes[i]) { // not required continue } if err := validate.MinimumInt("logIndexes"+"."+strconv.Itoa(i), "body", *m.LogIndexes[i], 0, false); err != nil { return err } } return nil } // ContextValidate validate this search log query based on the context it is used func (m *SearchLogQuery) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateEntries(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *SearchLogQuery) contextValidateEntries(ctx context.Context, formats strfmt.Registry) error { for i := 0; i < len(m.Entries()); i++ { if swag.IsZero(m.entriesField[i]) { // not required return nil } if err := m.entriesField[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("entries" + "." + strconv.Itoa(i)) } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("entries" + "." + strconv.Itoa(i)) } return err } } return nil } // MarshalBinary interface implementation func (m *SearchLogQuery) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *SearchLogQuery) UnmarshalBinary(b []byte) error { var res SearchLogQuery if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/tuf.go000066400000000000000000000116441455727245600200200ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "bytes" "context" "encoding/json" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // TUF TUF metadata // // swagger:model tuf type TUF struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec TUFSchema `json:"spec"` } // Kind gets the kind of this subtype func (m *TUF) Kind() string { return "tuf" } // SetKind sets the kind of this subtype func (m *TUF) SetKind(val string) { } // UnmarshalJSON unmarshals this object with a polymorphic type from a JSON structure func (m *TUF) UnmarshalJSON(raw []byte) error { var data struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec TUFSchema `json:"spec"` } buf := bytes.NewBuffer(raw) dec := json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&data); err != nil { return err } var base struct { /* Just the base type fields. Used for unmashalling polymorphic types.*/ Kind string `json:"kind"` } buf = bytes.NewBuffer(raw) dec = json.NewDecoder(buf) dec.UseNumber() if err := dec.Decode(&base); err != nil { return err } var result TUF if base.Kind != result.Kind() { /* Not the type we're looking for. */ return errors.New(422, "invalid kind value: %q", base.Kind) } result.APIVersion = data.APIVersion result.Spec = data.Spec *m = result return nil } // MarshalJSON marshals this object with a polymorphic type to a JSON structure func (m TUF) MarshalJSON() ([]byte, error) { var b1, b2, b3 []byte var err error b1, err = json.Marshal(struct { // api version // Required: true // Pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ APIVersion *string `json:"apiVersion"` // spec // Required: true Spec TUFSchema `json:"spec"` }{ APIVersion: m.APIVersion, Spec: m.Spec, }) if err != nil { return nil, err } b2, err = json.Marshal(struct { Kind string `json:"kind"` }{ Kind: m.Kind(), }) if err != nil { return nil, err } return swag.ConcatJSON(b1, b2, b3), nil } // Validate validates this tuf func (m *TUF) Validate(formats strfmt.Registry) error { var res []error if err := m.validateAPIVersion(formats); err != nil { res = append(res, err) } if err := m.validateSpec(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *TUF) validateAPIVersion(formats strfmt.Registry) error { if err := validate.Required("apiVersion", "body", m.APIVersion); err != nil { return err } if err := validate.Pattern("apiVersion", "body", *m.APIVersion, `^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`); err != nil { return err } return nil } func (m *TUF) validateSpec(formats strfmt.Registry) error { if m.Spec == nil { return errors.Required("spec", "body", nil) } return nil } // ContextValidate validate this tuf based on the context it is used func (m *TUF) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // MarshalBinary interface implementation func (m *TUF) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *TUF) UnmarshalBinary(b []byte) error { var res TUF if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/models/tuf_schema.go000066400000000000000000000016331455727245600213350ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command // TUFSchema TUF Schema // // # Schema for TUF metadata objects // // swagger:model tufSchema type TUFSchema interface{} rekor-1.3.5/pkg/generated/models/tuf_v001_schema.go000066400000000000000000000162211455727245600221020ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package models // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "context" "github.com/go-openapi/errors" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // TUFV001Schema TUF v0.0.1 Schema // // # Schema for TUF metadata entries // // swagger:model tufV001Schema type TUFV001Schema struct { // metadata // Required: true Metadata *TUFV001SchemaMetadata `json:"metadata"` // root // Required: true Root *TUFV001SchemaRoot `json:"root"` // TUF specification version // Read Only: true SpecVersion string `json:"spec_version,omitempty"` } // Validate validates this tuf v001 schema func (m *TUFV001Schema) Validate(formats strfmt.Registry) error { var res []error if err := m.validateMetadata(formats); err != nil { res = append(res, err) } if err := m.validateRoot(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *TUFV001Schema) validateMetadata(formats strfmt.Registry) error { if err := validate.Required("metadata", "body", m.Metadata); err != nil { return err } if m.Metadata != nil { if err := m.Metadata.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("metadata") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("metadata") } return err } } return nil } func (m *TUFV001Schema) validateRoot(formats strfmt.Registry) error { if err := validate.Required("root", "body", m.Root); err != nil { return err } if m.Root != nil { if err := m.Root.Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("root") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("root") } return err } } return nil } // ContextValidate validate this tuf v001 schema based on the context it is used func (m *TUFV001Schema) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error if err := m.contextValidateMetadata(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidateRoot(ctx, formats); err != nil { res = append(res, err) } if err := m.contextValidateSpecVersion(ctx, formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *TUFV001Schema) contextValidateMetadata(ctx context.Context, formats strfmt.Registry) error { if m.Metadata != nil { if err := m.Metadata.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("metadata") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("metadata") } return err } } return nil } func (m *TUFV001Schema) contextValidateRoot(ctx context.Context, formats strfmt.Registry) error { if m.Root != nil { if err := m.Root.ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { return ve.ValidateName("root") } else if ce, ok := err.(*errors.CompositeError); ok { return ce.ValidateName("root") } return err } } return nil } func (m *TUFV001Schema) contextValidateSpecVersion(ctx context.Context, formats strfmt.Registry) error { if err := validate.ReadOnly(ctx, "spec_version", "body", string(m.SpecVersion)); err != nil { return err } return nil } // MarshalBinary interface implementation func (m *TUFV001Schema) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *TUFV001Schema) UnmarshalBinary(b []byte) error { var res TUFV001Schema if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // TUFV001SchemaMetadata TUF metadata // // swagger:model TUFV001SchemaMetadata type TUFV001SchemaMetadata struct { // Specifies the metadata inline within the document // Required: true Content interface{} `json:"content"` } // Validate validates this TUF v001 schema metadata func (m *TUFV001SchemaMetadata) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *TUFV001SchemaMetadata) validateContent(formats strfmt.Registry) error { if m.Content == nil { return errors.Required("metadata"+"."+"content", "body", nil) } return nil } // ContextValidate validates this TUF v001 schema metadata based on context it is used func (m *TUFV001SchemaMetadata) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *TUFV001SchemaMetadata) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *TUFV001SchemaMetadata) UnmarshalBinary(b []byte) error { var res TUFV001SchemaMetadata if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } // TUFV001SchemaRoot root metadata containing about the public keys used to sign the manifest // // swagger:model TUFV001SchemaRoot type TUFV001SchemaRoot struct { // Specifies the metadata inline within the document // Required: true Content interface{} `json:"content"` } // Validate validates this TUF v001 schema root func (m *TUFV001SchemaRoot) Validate(formats strfmt.Registry) error { var res []error if err := m.validateContent(formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } func (m *TUFV001SchemaRoot) validateContent(formats strfmt.Registry) error { if m.Content == nil { return errors.Required("root"+"."+"content", "body", nil) } return nil } // ContextValidate validates this TUF v001 schema root based on context it is used func (m *TUFV001SchemaRoot) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil } // MarshalBinary interface implementation func (m *TUFV001SchemaRoot) MarshalBinary() ([]byte, error) { if m == nil { return nil, nil } return swag.WriteJSON(m) } // UnmarshalBinary interface implementation func (m *TUFV001SchemaRoot) UnmarshalBinary(b []byte) error { var res TUFV001SchemaRoot if err := swag.ReadJSON(b, &res); err != nil { return err } *m = res return nil } rekor-1.3.5/pkg/generated/restapi/000077500000000000000000000000001455727245600170515ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/restapi/configure_rekor_server.go000066400000000000000000000351771455727245600241660ustar00rootroot00000000000000/* Copyright © 2020 The Sigstore Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // This file is safe to edit. Once it exists it will not be overwritten package restapi import ( "context" "crypto/tls" go_errors "errors" "fmt" "net/http" "net/http/httputil" "strconv" "time" // using embed to add the static html page duing build time _ "embed" "github.com/go-chi/chi/middleware" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/mitchellh/mapstructure" "github.com/rs/cors" "github.com/spf13/viper" "go.uber.org/zap" "go.uber.org/zap/zapcore" pkgapi "github.com/sigstore/rekor/pkg/api" "github.com/sigstore/rekor/pkg/generated/restapi/operations" "github.com/sigstore/rekor/pkg/generated/restapi/operations/entries" "github.com/sigstore/rekor/pkg/generated/restapi/operations/index" "github.com/sigstore/rekor/pkg/generated/restapi/operations/pubkey" "github.com/sigstore/rekor/pkg/generated/restapi/operations/tlog" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/util" "golang.org/x/exp/slices" ) //go:generate swagger generate server --target ../../generated --name RekorServer --spec ../../../openapi.yaml --principal interface{} --exclude-main type contextKey string var ( ctxKeyAPIToRecord = contextKey("apiToRecord") ) // Context payload for recording metrics. type apiToRecord struct { method *string // Method to record in metrics, if any. path *string // Path to record in metrics, if any. } func configureFlags(_ *operations.RekorServerAPI) { // api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{ ... } } func configureAPI(api *operations.RekorServerAPI) http.Handler { // configure the api here api.ServeError = logAndServeError // Set your custom logger if needed. Default one is log.Printf // Expected interface func(string, ...interface{}) // // Example: // api.Logger = log.Printf api.Logger = log.Logger.Infof // api.UseSwaggerUI() // To continue using redoc as your UI, uncomment the following line // api.UseRedoc() api.JSONConsumer = runtime.JSONConsumer() api.JSONProducer = runtime.JSONProducer() api.ApplicationXPemFileProducer = runtime.TextProducer() // disable all endpoints to start api.IndexSearchIndexHandler = index.SearchIndexHandlerFunc(pkgapi.SearchIndexNotImplementedHandler) api.EntriesCreateLogEntryHandler = entries.CreateLogEntryHandlerFunc(pkgapi.CreateLogEntryNotImplementedHandler) api.EntriesGetLogEntryByIndexHandler = entries.GetLogEntryByIndexHandlerFunc(pkgapi.GetLogEntryByIndexNotImplementedHandler) api.EntriesGetLogEntryByUUIDHandler = entries.GetLogEntryByUUIDHandlerFunc(pkgapi.GetLogEntryByUUIDNotImplementedHandler) api.EntriesSearchLogQueryHandler = entries.SearchLogQueryHandlerFunc(pkgapi.SearchLogQueryNotImplementedHandler) api.PubkeyGetPublicKeyHandler = pubkey.GetPublicKeyHandlerFunc(pkgapi.GetPublicKeyNotImplementedHandler) api.TlogGetLogProofHandler = tlog.GetLogProofHandlerFunc(pkgapi.GetLogProofNotImplementedHandler) enabledAPIEndpoints := viper.GetStringSlice("enabled_api_endpoints") if !slices.Contains(enabledAPIEndpoints, "searchIndex") && viper.GetBool("enable_retrieve_api") { enabledAPIEndpoints = append(enabledAPIEndpoints, "searchIndex") } for _, enabledAPI := range enabledAPIEndpoints { log.Logger.Infof("Enabling API endpoint: %s", enabledAPI) switch enabledAPI { case "searchIndex": api.IndexSearchIndexHandler = index.SearchIndexHandlerFunc(pkgapi.SearchIndexHandler) case "getLogInfo": api.TlogGetLogInfoHandler = tlog.GetLogInfoHandlerFunc(pkgapi.GetLogInfoHandler) case "getPublicKey": api.PubkeyGetPublicKeyHandler = pubkey.GetPublicKeyHandlerFunc(pkgapi.GetPublicKeyHandler) case "getLogProof": api.TlogGetLogProofHandler = tlog.GetLogProofHandlerFunc(pkgapi.GetLogProofHandler) case "createLogEntry": api.EntriesCreateLogEntryHandler = entries.CreateLogEntryHandlerFunc(pkgapi.CreateLogEntryHandler) case "getLogEntryByIndex": api.EntriesGetLogEntryByIndexHandler = entries.GetLogEntryByIndexHandlerFunc(pkgapi.GetLogEntryByIndexHandler) case "getLogEntryByUUID": api.EntriesGetLogEntryByUUIDHandler = entries.GetLogEntryByUUIDHandlerFunc(pkgapi.GetLogEntryByUUIDHandler) case "searchLogQuery": api.EntriesSearchLogQueryHandler = entries.SearchLogQueryHandlerFunc(pkgapi.SearchLogQueryHandler) default: log.Logger.Panicf("Unknown API endpoint requested: %s", enabledAPI) } } // all handlers need to be set before a call to api.AddMiddlewareFor for _, enabledAPI := range enabledAPIEndpoints { switch enabledAPI { case "searchIndex": recordMetricsForAPI(api, "POST", "/api/v1/index/retrieve") // add metrics case "getLogInfo": api.AddMiddlewareFor("GET", "/api/v1/log", middleware.NoCache) // not cacheable recordMetricsForAPI(api, "GET", "/api/v1/log") // add metrics case "getPublicKey": api.AddMiddlewareFor("GET", "/api/v1/log/publicKey", middleware.NoCache) // not cacheable recordMetricsForAPI(api, "GET", "/api/v1/log/publicKey") // add metrics case "getLogProof": api.AddMiddlewareFor("GET", "/api/v1/log/proof", middleware.NoCache) // not cacheable recordMetricsForAPI(api, "GET", "/api/v1/log/proof") // add metrics case "createLogEntry": recordMetricsForAPI(api, "POST", "/api/v1/log/entries") // add metrics case "getLogEntryByIndex": api.AddMiddlewareFor("GET", "/api/v1/log/entries", middleware.NoCache) // not cacheable recordMetricsForAPI(api, "GET", "/api/v1/log/entries") // add metrics case "getLogEntryByUUID": api.AddMiddlewareFor("GET", "/api/v1/log/entries/{entryUUID}", middleware.NoCache) // not cacheable recordMetricsForAPI(api, "GET", "/api/v1/log/entries/{entryUUID}") // add metrics case "searchLogQuery": recordMetricsForAPI(api, "POST", "/api/v1/log/entries/retrieve") // add metrics } } api.RegisterFormat("signedCheckpoint", &util.SignedNote{}, util.SignedCheckpointValidator) api.PreServerShutdown = func() {} api.ServerShutdown = func() { pkgapi.StopAPI() } return setupGlobalMiddleware(api.Serve(setupMiddlewares)) } // The TLS configuration before HTTPS server starts. func configureTLS(_ *tls.Config) { // Make all necessary changes to the TLS configuration here. } // As soon as server is initialized but not run yet, this function will be called. // If you need to modify a config, store server instance to stop it individually later, this is the place. // This function can be called multiple times, depending on the number of serving schemes. // scheme value will be set accordingly: "http", "https" or "unix" func configureServer(_ *http.Server, _, _ string) { } // The middleware configuration is for the handler executors. These do not apply to the swagger.json document. // The middleware executes after routing but before authentication, binding and validation func setupMiddlewares(handler http.Handler) http.Handler { return handler } type httpRequestFields struct { requestMethod string requestURL string requestSize int64 status int responseSize int userAgent string remoteIP string latency time.Duration protocol string } func (h *httpRequestFields) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("requestMethod", h.requestMethod) enc.AddString("requestUrl", h.requestURL) enc.AddString("requestSize", fmt.Sprintf("%d", h.requestSize)) enc.AddInt("status", h.status) enc.AddString("responseSize", fmt.Sprintf("%d", h.responseSize)) enc.AddString("userAgent", h.userAgent) enc.AddString("remoteIP", h.remoteIP) enc.AddString("latency", fmt.Sprintf("%.9fs", h.latency.Seconds())) // formatted per GCP expectations enc.AddString("protocol", h.protocol) return nil } // We need this type to act as an adapter between zap and the middleware request logger. type zapLogEntry struct { r *http.Request } func (z *zapLogEntry) Write(status, bytes int, _ http.Header, elapsed time.Duration, extra interface{}) { var fields []interface{} // follows https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry as a convention // append HTTP Request / Response Information scheme := "http" if z.r.TLS != nil { scheme = "https" } httpRequestObj := &httpRequestFields{ requestMethod: z.r.Method, requestURL: fmt.Sprintf("%s://%s%s", scheme, z.r.Host, z.r.RequestURI), requestSize: z.r.ContentLength, status: status, responseSize: bytes, userAgent: z.r.Header.Get("User-Agent"), remoteIP: z.r.RemoteAddr, latency: elapsed, protocol: z.r.Proto, } fields = append(fields, zap.Object("httpRequest", httpRequestObj)) if extra != nil { fields = append(fields, zap.Any("extra", extra)) } log.ContextLogger(z.r.Context()).With(fields...).Info("completed request") } func (z *zapLogEntry) Panic(v interface{}, stack []byte) { fields := []interface{}{zap.String("message", fmt.Sprintf("%v\n%v", v, string(stack)))} log.ContextLogger(z.r.Context()).With(fields...).Errorf("panic detected: %v", v) } type logFormatter struct{} func (l *logFormatter) NewLogEntry(r *http.Request) middleware.LogEntry { return &zapLogEntry{r} } // The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document. // So this is a good place to plug in a panic handling middleware, logging and metrics func setupGlobalMiddleware(handler http.Handler) http.Handler { returnHandler := recoverer(handler) maxReqBodySize := viper.GetInt64("max_request_body_size") if maxReqBodySize > 0 { returnHandler = maxBodySize(maxReqBodySize, returnHandler) } middleware.DefaultLogger = middleware.RequestLogger(&logFormatter{}) returnHandler = middleware.Logger(returnHandler) returnHandler = middleware.Heartbeat("/ping")(returnHandler) returnHandler = serveStaticContent(returnHandler) handleCORS := cors.Default().Handler returnHandler = handleCORS(returnHandler) returnHandler = wrapMetrics(returnHandler) return middleware.RequestID(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() r = r.WithContext(log.WithRequestID(ctx, middleware.GetReqID(ctx))) defer func() { _ = log.ContextLogger(ctx).Sync() }() returnHandler.ServeHTTP(w, r) })) } // Populates the the apiToRecord for this method/path so metrics are emitted. func recordMetricsForAPI(api *operations.RekorServerAPI, method string, path string) { metricsHandler := func(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() if apiInfo, ok := ctx.Value(ctxKeyAPIToRecord).(*apiToRecord); ok { apiInfo.method = &method apiInfo.path = &path } else { log.ContextLogger(ctx).Warn("Could not attach api info - endpoint may not be monitored.") } handler.ServeHTTP(w, r) }) } api.AddMiddlewareFor(method, path, metricsHandler) } func wrapMetrics(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() apiInfo := apiToRecord{} ctx = context.WithValue(ctx, ctxKeyAPIToRecord, &apiInfo) r = r.WithContext(ctx) start := time.Now() ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor) defer func() { // Only record metrics for APIs that need instrumentation. if apiInfo.path != nil && apiInfo.method != nil { code := strconv.Itoa(ww.Status()) labels := map[string]string{ "path": *apiInfo.path, "code": code, } // This logs latency broken down by URL path and response code // TODO(var-sdk): delete these metrics once the new metrics are safely rolled out. pkgapi.MetricLatency.With(labels).Observe(float64(time.Since(start))) pkgapi.MetricLatencySummary.With(labels).Observe(float64(time.Since(start))) pkgapi.MetricRequestLatency.With( map[string]string{ "path": *apiInfo.path, "method": *apiInfo.method, }).Observe(float64(time.Since(start))) pkgapi.MetricRequestCount.With( map[string]string{ "path": *apiInfo.path, "method": *apiInfo.method, "code": code, }).Inc() } }() handler.ServeHTTP(ww, r) }) } func logAndServeError(w http.ResponseWriter, r *http.Request, err error) { ctx := r.Context() if apiErr, ok := err.(errors.Error); ok && apiErr.Code() == http.StatusNotFound { log.ContextLogger(ctx).Warn(err) } else { log.ContextLogger(ctx).Error(err) } if compErr, ok := err.(*errors.CompositeError); ok { // iterate over composite error looking for something more specific for _, embeddedErr := range compErr.Errors { var maxBytesError *http.MaxBytesError if parseErr, ok := embeddedErr.(*errors.ParseError); ok && go_errors.As(parseErr.Reason, &maxBytesError) { err = errors.New(http.StatusRequestEntityTooLarge, http.StatusText(http.StatusRequestEntityTooLarge)) break } } } requestFields := map[string]interface{}{} if decodeErr := mapstructure.Decode(r, &requestFields); decodeErr == nil { log.ContextLogger(ctx).Debug(requestFields) } errors.ServeError(w, r, err) } //go:embed rekorHomePage.html var homePageBytes []byte func serveStaticContent(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { w.Header().Add("Content-Type", "text/html") w.WriteHeader(200) _, _ = w.Write(homePageBytes) return } handler.ServeHTTP(w, r) }) } // recoverer func recoverer(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { defer func() { if rvr := recover(); rvr != nil && rvr != http.ErrAbortHandler { var fields []interface{} // get context before dump request in case there is an error ctx := r.Context() request, err := httputil.DumpRequest(r, false) if err == nil { fields = append(fields, zap.ByteString("request_headers", request)) } log.ContextLogger(ctx).With(fields...).Errorf("panic detected: %v", rvr) errors.ServeError(w, r, nil) } }() next.ServeHTTP(w, r) } return http.HandlerFunc(fn) } // maxBodySize limits the request body func maxBodySize(maxLength int64, next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxLength) next.ServeHTTP(w, r) } return http.HandlerFunc(fn) } rekor-1.3.5/pkg/generated/restapi/doc.go000066400000000000000000000017541455727245600201540ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Package restapi Rekor // // Rekor is a cryptographically secure, immutable transparency log for signed software releases. // Schemes: // http // Host: rekor.sigstore.dev // BasePath: / // Version: 1.0.0 // // Consumes: // - application/json // // Produces: // - application/x-pem-file // - application/json // // swagger:meta package restapi rekor-1.3.5/pkg/generated/restapi/embedded_spec.go000066400000000000000000003640521455727245600221550ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package restapi // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "encoding/json" ) var ( // SwaggerJSON embedded version of the swagger document used at generation time SwaggerJSON json.RawMessage // FlatSwaggerJSON embedded flattened version of the swagger document used at generation time FlatSwaggerJSON json.RawMessage ) func init() { SwaggerJSON = json.RawMessage([]byte(`{ "consumes": [ "application/json" ], "produces": [ "application/json" ], "schemes": [ "http" ], "swagger": "2.0", "info": { "description": "Rekor is a cryptographically secure, immutable transparency log for signed software releases.", "title": "Rekor", "version": "1.0.0" }, "host": "rekor.sigstore.dev", "paths": { "/api/v1/index/retrieve": { "post": { "description": "EXPERIMENTAL - this endpoint is offered as best effort only and may be changed or removed in future releases.\nThe results returned from this endpoint may be incomplete.\n", "tags": [ "index" ], "summary": "Searches index by entry metadata", "operationId": "searchIndex", "deprecated": true, "parameters": [ { "name": "query", "in": "body", "required": true, "schema": { "$ref": "#/definitions/SearchIndex" } } ], "responses": { "200": { "description": "Returns zero or more entry UUIDs from the transparency log based on search query", "schema": { "type": "array", "items": { "description": "Entry UUID in transparency log", "type": "string", "pattern": "^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$" } } }, "400": { "$ref": "#/responses/BadContent" }, "default": { "$ref": "#/responses/InternalServerError" } } } }, "/api/v1/log": { "get": { "description": "Returns the current root hash and size of the merkle tree used to store the log entries.", "tags": [ "tlog" ], "summary": "Get information about the current state of the transparency log", "operationId": "getLogInfo", "parameters": [ { "type": "boolean", "default": false, "description": "Whether to return a stable checkpoint for the active shard", "name": "stable", "in": "query" } ], "responses": { "200": { "description": "A JSON object with the root hash and tree size as properties", "schema": { "$ref": "#/definitions/LogInfo" } }, "default": { "$ref": "#/responses/InternalServerError" } } } }, "/api/v1/log/entries": { "get": { "tags": [ "entries" ], "summary": "Retrieves an entry and inclusion proof from the transparency log (if it exists) by index", "operationId": "getLogEntryByIndex", "parameters": [ { "type": "integer", "description": "specifies the index of the entry in the transparency log to be retrieved", "name": "logIndex", "in": "query", "required": true } ], "responses": { "200": { "description": "the entry in the transparency log requested along with an inclusion proof", "schema": { "$ref": "#/definitions/LogEntry" } }, "404": { "$ref": "#/responses/NotFound" }, "default": { "$ref": "#/responses/InternalServerError" } } }, "post": { "description": "Creates an entry in the transparency log for a detached signature, public key, and content. Items can be included in the request or fetched by the server when URLs are specified.\n", "tags": [ "entries" ], "summary": "Creates an entry in the transparency log", "operationId": "createLogEntry", "parameters": [ { "name": "proposedEntry", "in": "body", "required": true, "schema": { "$ref": "#/definitions/ProposedEntry" } } ], "responses": { "201": { "description": "Returns the entry created in the transparency log", "schema": { "$ref": "#/definitions/LogEntry" }, "headers": { "ETag": { "type": "string", "description": "UUID of log entry" }, "Location": { "type": "string", "format": "uri", "description": "URI location of log entry" } } }, "400": { "$ref": "#/responses/BadContent" }, "409": { "$ref": "#/responses/Conflict" }, "default": { "$ref": "#/responses/InternalServerError" } } } }, "/api/v1/log/entries/retrieve": { "post": { "tags": [ "entries" ], "summary": "Searches transparency log for one or more log entries", "operationId": "searchLogQuery", "parameters": [ { "name": "entry", "in": "body", "required": true, "schema": { "$ref": "#/definitions/SearchLogQuery" } } ], "responses": { "200": { "description": "Returns zero or more entries from the transparency log, according to how many were included in request query", "schema": { "type": "array", "items": { "$ref": "#/definitions/LogEntry" } } }, "400": { "$ref": "#/responses/BadContent" }, "422": { "$ref": "#/responses/UnprocessableEntity" }, "default": { "$ref": "#/responses/InternalServerError" } } } }, "/api/v1/log/entries/{entryUUID}": { "get": { "description": "Returns the entry, root hash, tree size, and a list of hashes that can be used to calculate proof of an entry being included in the transparency log", "tags": [ "entries" ], "summary": "Get log entry and information required to generate an inclusion proof for the entry in the transparency log", "operationId": "getLogEntryByUUID", "parameters": [ { "pattern": "^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$", "type": "string", "description": "the UUID of the entry for which the inclusion proof information should be returned", "name": "entryUUID", "in": "path", "required": true } ], "responses": { "200": { "description": "Information needed for a client to compute the inclusion proof", "schema": { "$ref": "#/definitions/LogEntry" } }, "404": { "$ref": "#/responses/NotFound" }, "default": { "$ref": "#/responses/InternalServerError" } } } }, "/api/v1/log/proof": { "get": { "description": "Returns a list of hashes for specified tree sizes that can be used to confirm the consistency of the transparency log", "tags": [ "tlog" ], "summary": "Get information required to generate a consistency proof for the transparency log", "operationId": "getLogProof", "parameters": [ { "minimum": 1, "type": "integer", "default": 1, "description": "The size of the tree that you wish to prove consistency from (1 means the beginning of the log) Defaults to 1 if not specified\n", "name": "firstSize", "in": "query" }, { "minimum": 1, "type": "integer", "description": "The size of the tree that you wish to prove consistency to", "name": "lastSize", "in": "query", "required": true }, { "pattern": "^[0-9]+$", "type": "string", "description": "The tree ID of the tree that you wish to prove consistency for", "name": "treeID", "in": "query" } ], "responses": { "200": { "description": "All hashes required to compute the consistency proof", "schema": { "$ref": "#/definitions/ConsistencyProof" } }, "400": { "$ref": "#/responses/BadContent" }, "default": { "$ref": "#/responses/InternalServerError" } } } }, "/api/v1/log/publicKey": { "get": { "description": "Returns the public key that can be used to validate the signed tree head", "produces": [ "application/x-pem-file" ], "tags": [ "pubkey" ], "summary": "Retrieve the public key that can be used to validate the signed tree head", "operationId": "getPublicKey", "parameters": [ { "pattern": "^[0-9]+$", "type": "string", "description": "The tree ID of the tree you wish to get a public key for", "name": "treeID", "in": "query" } ], "responses": { "200": { "description": "The public key", "schema": { "type": "string" } }, "default": { "$ref": "#/responses/InternalServerError" } } } } }, "definitions": { "ConsistencyProof": { "type": "object", "required": [ "rootHash", "hashes" ], "properties": { "hashes": { "type": "array", "items": { "description": "SHA256 hash value expressed in hexadecimal format", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" } }, "rootHash": { "description": "The hash value stored at the root of the merkle tree at the time the proof was generated", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" } } }, "Error": { "type": "object", "properties": { "code": { "type": "integer" }, "message": { "type": "string" } } }, "InactiveShardLogInfo": { "type": "object", "required": [ "rootHash", "treeSize", "signedTreeHead", "treeID" ], "properties": { "rootHash": { "description": "The current hash value stored at the root of the merkle tree", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" }, "signedTreeHead": { "description": "The current signed tree head", "type": "string", "format": "signedCheckpoint" }, "treeID": { "description": "The current treeID", "type": "string", "pattern": "^[0-9]+$" }, "treeSize": { "description": "The current number of nodes in the merkle tree", "type": "integer", "minimum": 1 } } }, "InclusionProof": { "type": "object", "required": [ "logIndex", "rootHash", "treeSize", "hashes", "checkpoint" ], "properties": { "checkpoint": { "description": "The checkpoint (signed tree head) that the inclusion proof is based on", "type": "string", "format": "signedCheckpoint" }, "hashes": { "description": "A list of hashes required to compute the inclusion proof, sorted in order from leaf to root", "type": "array", "items": { "description": "SHA256 hash value expressed in hexadecimal format", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" } }, "logIndex": { "description": "The index of the entry in the transparency log", "type": "integer" }, "rootHash": { "description": "The hash value stored at the root of the merkle tree at the time the proof was generated", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" }, "treeSize": { "description": "The size of the merkle tree at the time the inclusion proof was generated", "type": "integer", "minimum": 1 } } }, "LogEntry": { "type": "object", "additionalProperties": { "type": "object", "required": [ "logID", "logIndex", "body", "integratedTime" ], "properties": { "attestation": { "type": "object", "format": "byte", "properties": { "data": { "format": "byte" } } }, "body": { "type": "object", "additionalProperties": true }, "integratedTime": { "description": "The time the entry was added to the log as a Unix timestamp in seconds", "type": "integer" }, "logID": { "description": "This is the SHA256 hash of the DER-encoded public key for the log at the time the entry was included in the log", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" }, "logIndex": { "type": "integer" }, "verification": { "type": "object", "properties": { "inclusionProof": { "$ref": "#/definitions/InclusionProof" }, "signedEntryTimestamp": { "description": "Signature over the logID, logIndex, body and integratedTime.", "type": "string", "format": "byte" } } } } } }, "LogInfo": { "type": "object", "required": [ "rootHash", "treeSize", "signedTreeHead", "treeID" ], "properties": { "inactiveShards": { "type": "array", "items": { "$ref": "#/definitions/InactiveShardLogInfo" } }, "rootHash": { "description": "The current hash value stored at the root of the merkle tree", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" }, "signedTreeHead": { "description": "The current signed tree head", "type": "string", "format": "signedCheckpoint" }, "treeID": { "description": "The current treeID", "type": "string", "pattern": "^[0-9]+$" }, "treeSize": { "description": "The current number of nodes in the merkle tree", "type": "integer", "minimum": 1 } } }, "ProposedEntry": { "type": "object", "required": [ "kind" ], "properties": { "kind": { "type": "string" } }, "discriminator": "kind" }, "SearchIndex": { "type": "object", "properties": { "email": { "type": "string", "format": "email" }, "hash": { "type": "string", "pattern": "^(sha512:)?[0-9a-fA-F]{128}$|^(sha256:)?[0-9a-fA-F]{64}$|^(sha1:)?[0-9a-fA-F]{40}$" }, "operator": { "type": "string", "enum": [ "and", "or" ] }, "publicKey": { "type": "object", "required": [ "format" ], "properties": { "content": { "type": "string", "format": "byte" }, "format": { "type": "string", "enum": [ "pgp", "x509", "minisign", "ssh", "tuf" ] }, "url": { "type": "string", "format": "uri" } } } } }, "SearchLogQuery": { "type": "object", "properties": { "entries": { "type": "array", "maxItems": 10, "minItems": 1, "items": { "$ref": "#/definitions/ProposedEntry" } }, "entryUUIDs": { "type": "array", "maxItems": 10, "minItems": 1, "items": { "type": "string", "pattern": "^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$" } }, "logIndexes": { "type": "array", "maxItems": 10, "minItems": 1, "items": { "type": "integer" } } } }, "alpine": { "description": "Alpine package", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "type": "object", "$ref": "pkg/types/alpine/alpine_schema.json" } }, "additionalProperties": false } ] }, "cose": { "description": "COSE object", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "type": "object", "$ref": "pkg/types/cose/cose_schema.json" } }, "additionalProperties": false } ] }, "dsse": { "description": "DSSE envelope", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "type": "object", "$ref": "pkg/types/dsse/dsse_schema.json" } }, "additionalProperties": false } ] }, "hashedrekord": { "description": "Hashed Rekord object", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "type": "object", "$ref": "pkg/types/hashedrekord/hashedrekord_schema.json" } }, "additionalProperties": false } ] }, "helm": { "description": "Helm chart", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "type": "object", "$ref": "pkg/types/helm/helm_schema.json" } } } ] }, "intoto": { "description": "Intoto object", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "type": "object", "$ref": "pkg/types/intoto/intoto_schema.json" } }, "additionalProperties": false } ] }, "jar": { "description": "Java Archive (JAR)", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "type": "object", "$ref": "pkg/types/jar/jar_schema.json" } }, "additionalProperties": false } ] }, "rekord": { "description": "Rekord object", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "type": "object", "$ref": "pkg/types/rekord/rekord_schema.json" } }, "additionalProperties": false } ] }, "rfc3161": { "description": "RFC3161 Timestamp", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "type": "object", "$ref": "pkg/types/rfc3161/rfc3161_schema.json" } }, "additionalProperties": false } ] }, "rpm": { "description": "RPM package", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "type": "object", "$ref": "pkg/types/rpm/rpm_schema.json" } }, "additionalProperties": false } ] }, "tuf": { "description": "TUF metadata", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "type": "object", "$ref": "pkg/types/tuf/tuf_schema.json" } }, "additionalProperties": false } ] } }, "responses": { "BadContent": { "description": "The content supplied to the server was invalid", "schema": { "$ref": "#/definitions/Error" } }, "Conflict": { "description": "The request conflicts with the current state of the transparency log", "schema": { "$ref": "#/definitions/Error" }, "headers": { "Location": { "type": "string", "format": "uri" } } }, "InternalServerError": { "description": "There was an internal error in the server while processing the request", "schema": { "$ref": "#/definitions/Error" } }, "NotFound": { "description": "The content requested could not be found" }, "UnprocessableEntity": { "description": "The server understood the request but is unable to process the contained instructions", "schema": { "$ref": "#/definitions/Error" } } } }`)) FlatSwaggerJSON = json.RawMessage([]byte(`{ "consumes": [ "application/json" ], "produces": [ "application/json" ], "schemes": [ "http" ], "swagger": "2.0", "info": { "description": "Rekor is a cryptographically secure, immutable transparency log for signed software releases.", "title": "Rekor", "version": "1.0.0" }, "host": "rekor.sigstore.dev", "paths": { "/api/v1/index/retrieve": { "post": { "description": "EXPERIMENTAL - this endpoint is offered as best effort only and may be changed or removed in future releases.\nThe results returned from this endpoint may be incomplete.\n", "tags": [ "index" ], "summary": "Searches index by entry metadata", "operationId": "searchIndex", "deprecated": true, "parameters": [ { "name": "query", "in": "body", "required": true, "schema": { "$ref": "#/definitions/SearchIndex" } } ], "responses": { "200": { "description": "Returns zero or more entry UUIDs from the transparency log based on search query", "schema": { "type": "array", "items": { "description": "Entry UUID in transparency log", "type": "string", "pattern": "^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$" } } }, "400": { "description": "The content supplied to the server was invalid", "schema": { "$ref": "#/definitions/Error" } }, "default": { "description": "There was an internal error in the server while processing the request", "schema": { "$ref": "#/definitions/Error" } } } } }, "/api/v1/log": { "get": { "description": "Returns the current root hash and size of the merkle tree used to store the log entries.", "tags": [ "tlog" ], "summary": "Get information about the current state of the transparency log", "operationId": "getLogInfo", "parameters": [ { "type": "boolean", "default": false, "description": "Whether to return a stable checkpoint for the active shard", "name": "stable", "in": "query" } ], "responses": { "200": { "description": "A JSON object with the root hash and tree size as properties", "schema": { "$ref": "#/definitions/LogInfo" } }, "default": { "description": "There was an internal error in the server while processing the request", "schema": { "$ref": "#/definitions/Error" } } } } }, "/api/v1/log/entries": { "get": { "tags": [ "entries" ], "summary": "Retrieves an entry and inclusion proof from the transparency log (if it exists) by index", "operationId": "getLogEntryByIndex", "parameters": [ { "minimum": 0, "type": "integer", "description": "specifies the index of the entry in the transparency log to be retrieved", "name": "logIndex", "in": "query", "required": true } ], "responses": { "200": { "description": "the entry in the transparency log requested along with an inclusion proof", "schema": { "$ref": "#/definitions/LogEntry" } }, "404": { "description": "The content requested could not be found" }, "default": { "description": "There was an internal error in the server while processing the request", "schema": { "$ref": "#/definitions/Error" } } } }, "post": { "description": "Creates an entry in the transparency log for a detached signature, public key, and content. Items can be included in the request or fetched by the server when URLs are specified.\n", "tags": [ "entries" ], "summary": "Creates an entry in the transparency log", "operationId": "createLogEntry", "parameters": [ { "name": "proposedEntry", "in": "body", "required": true, "schema": { "$ref": "#/definitions/ProposedEntry" } } ], "responses": { "201": { "description": "Returns the entry created in the transparency log", "schema": { "$ref": "#/definitions/LogEntry" }, "headers": { "ETag": { "type": "string", "description": "UUID of log entry" }, "Location": { "type": "string", "format": "uri", "description": "URI location of log entry" } } }, "400": { "description": "The content supplied to the server was invalid", "schema": { "$ref": "#/definitions/Error" } }, "409": { "description": "The request conflicts with the current state of the transparency log", "schema": { "$ref": "#/definitions/Error" }, "headers": { "Location": { "type": "string", "format": "uri" } } }, "default": { "description": "There was an internal error in the server while processing the request", "schema": { "$ref": "#/definitions/Error" } } } } }, "/api/v1/log/entries/retrieve": { "post": { "tags": [ "entries" ], "summary": "Searches transparency log for one or more log entries", "operationId": "searchLogQuery", "parameters": [ { "name": "entry", "in": "body", "required": true, "schema": { "$ref": "#/definitions/SearchLogQuery" } } ], "responses": { "200": { "description": "Returns zero or more entries from the transparency log, according to how many were included in request query", "schema": { "type": "array", "items": { "$ref": "#/definitions/LogEntry" } } }, "400": { "description": "The content supplied to the server was invalid", "schema": { "$ref": "#/definitions/Error" } }, "422": { "description": "The server understood the request but is unable to process the contained instructions", "schema": { "$ref": "#/definitions/Error" } }, "default": { "description": "There was an internal error in the server while processing the request", "schema": { "$ref": "#/definitions/Error" } } } } }, "/api/v1/log/entries/{entryUUID}": { "get": { "description": "Returns the entry, root hash, tree size, and a list of hashes that can be used to calculate proof of an entry being included in the transparency log", "tags": [ "entries" ], "summary": "Get log entry and information required to generate an inclusion proof for the entry in the transparency log", "operationId": "getLogEntryByUUID", "parameters": [ { "pattern": "^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$", "type": "string", "description": "the UUID of the entry for which the inclusion proof information should be returned", "name": "entryUUID", "in": "path", "required": true } ], "responses": { "200": { "description": "Information needed for a client to compute the inclusion proof", "schema": { "$ref": "#/definitions/LogEntry" } }, "404": { "description": "The content requested could not be found" }, "default": { "description": "There was an internal error in the server while processing the request", "schema": { "$ref": "#/definitions/Error" } } } } }, "/api/v1/log/proof": { "get": { "description": "Returns a list of hashes for specified tree sizes that can be used to confirm the consistency of the transparency log", "tags": [ "tlog" ], "summary": "Get information required to generate a consistency proof for the transparency log", "operationId": "getLogProof", "parameters": [ { "minimum": 1, "type": "integer", "default": 1, "description": "The size of the tree that you wish to prove consistency from (1 means the beginning of the log) Defaults to 1 if not specified\n", "name": "firstSize", "in": "query" }, { "minimum": 1, "type": "integer", "description": "The size of the tree that you wish to prove consistency to", "name": "lastSize", "in": "query", "required": true }, { "pattern": "^[0-9]+$", "type": "string", "description": "The tree ID of the tree that you wish to prove consistency for", "name": "treeID", "in": "query" } ], "responses": { "200": { "description": "All hashes required to compute the consistency proof", "schema": { "$ref": "#/definitions/ConsistencyProof" } }, "400": { "description": "The content supplied to the server was invalid", "schema": { "$ref": "#/definitions/Error" } }, "default": { "description": "There was an internal error in the server while processing the request", "schema": { "$ref": "#/definitions/Error" } } } } }, "/api/v1/log/publicKey": { "get": { "description": "Returns the public key that can be used to validate the signed tree head", "produces": [ "application/x-pem-file" ], "tags": [ "pubkey" ], "summary": "Retrieve the public key that can be used to validate the signed tree head", "operationId": "getPublicKey", "parameters": [ { "pattern": "^[0-9]+$", "type": "string", "description": "The tree ID of the tree you wish to get a public key for", "name": "treeID", "in": "query" } ], "responses": { "200": { "description": "The public key", "schema": { "type": "string" } }, "default": { "description": "There was an internal error in the server while processing the request", "schema": { "$ref": "#/definitions/Error" } } } } } }, "definitions": { "AlpineV001SchemaPackage": { "description": "Information about the package associated with the entry", "type": "object", "oneOf": [ { "required": [ "hash" ] }, { "required": [ "content" ] } ], "properties": { "content": { "description": "Specifies the package inline within the document", "type": "string", "format": "byte", "writeOnly": true }, "hash": { "description": "Specifies the hash algorithm and value for the package", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the package", "type": "string" } }, "readOnly": true }, "pkginfo": { "description": "Values of the .PKGINFO key / value pairs", "type": "object", "additionalProperties": { "type": "string" }, "readOnly": true } } }, "AlpineV001SchemaPackageHash": { "description": "Specifies the hash algorithm and value for the package", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the package", "type": "string" } }, "readOnly": true }, "AlpineV001SchemaPublicKey": { "description": "The public key that can verify the package signature", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } } }, "ConsistencyProof": { "type": "object", "required": [ "rootHash", "hashes" ], "properties": { "hashes": { "type": "array", "items": { "description": "SHA256 hash value expressed in hexadecimal format", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" } }, "rootHash": { "description": "The hash value stored at the root of the merkle tree at the time the proof was generated", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" } } }, "CoseV001SchemaData": { "description": "Information about the content associated with the entry", "type": "object", "properties": { "aad": { "description": "Specifies the additional authenticated data required to verify the signature", "type": "string", "format": "byte", "writeOnly": true }, "envelopeHash": { "description": "Specifies the hash algorithm and value for the COSE envelope", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the envelope", "type": "string" } }, "readOnly": true }, "payloadHash": { "description": "Specifies the hash algorithm and value for the content", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the content", "type": "string" } }, "readOnly": true } } }, "CoseV001SchemaDataEnvelopeHash": { "description": "Specifies the hash algorithm and value for the COSE envelope", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the envelope", "type": "string" } }, "readOnly": true }, "CoseV001SchemaDataPayloadHash": { "description": "Specifies the hash algorithm and value for the content", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the content", "type": "string" } }, "readOnly": true }, "DSSEV001SchemaEnvelopeHash": { "description": "Specifies the hash algorithm and value encompassing the entire envelope sent to Rekor", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The value of the computed digest over the entire envelope", "type": "string" } }, "readOnly": true }, "DSSEV001SchemaPayloadHash": { "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The value of the computed digest over the payload within the envelope", "type": "string" } }, "readOnly": true }, "DSSEV001SchemaProposedContent": { "type": "object", "required": [ "envelope", "verifiers" ], "properties": { "envelope": { "description": "DSSE envelope specified as a stringified JSON object", "type": "string", "writeOnly": true }, "verifiers": { "description": "collection of all verification material (e.g. public keys or certificates) used to verify signatures over envelope's payload, specified as base64-encoded strings", "type": "array", "minItems": 1, "items": { "type": "string", "format": "byte" }, "writeOnly": true } }, "writeOnly": true }, "DSSEV001SchemaSignaturesItems0": { "description": "a signature of the envelope's payload along with the verification material for the signature", "type": "object", "required": [ "signature", "verifier" ], "properties": { "signature": { "description": "base64 encoded signature of the payload", "type": "string", "pattern": "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" }, "verifier": { "description": "verification material that was used to verify the corresponding signature, specified as a base64 encoded string", "type": "string", "format": "byte" } } }, "Error": { "type": "object", "properties": { "code": { "type": "integer" }, "message": { "type": "string" } } }, "HashedrekordV001SchemaData": { "description": "Information about the content associated with the entry", "type": "object", "properties": { "hash": { "description": "Specifies the hash algorithm and value for the content", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256", "sha384", "sha512" ] }, "value": { "description": "The hash value for the content, as represented by a lower case hexadecimal string", "type": "string" } } } } }, "HashedrekordV001SchemaDataHash": { "description": "Specifies the hash algorithm and value for the content", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256", "sha384", "sha512" ] }, "value": { "description": "The hash value for the content, as represented by a lower case hexadecimal string", "type": "string" } } }, "HashedrekordV001SchemaSignature": { "description": "Information about the detached signature associated with the entry", "type": "object", "properties": { "content": { "description": "Specifies the content of the signature inline within the document", "type": "string", "format": "byte" }, "publicKey": { "description": "The public key that can verify the signature; this can also be an X509 code signing certificate that contains the raw public key information", "type": "object", "properties": { "content": { "description": "Specifies the content of the public key or code signing certificate inline within the document", "type": "string", "format": "byte" } } } } }, "HashedrekordV001SchemaSignaturePublicKey": { "description": "The public key that can verify the signature; this can also be an X509 code signing certificate that contains the raw public key information", "type": "object", "properties": { "content": { "description": "Specifies the content of the public key or code signing certificate inline within the document", "type": "string", "format": "byte" } } }, "HelmV001SchemaChart": { "description": "Information about the Helm chart associated with the entry", "type": "object", "required": [ "provenance" ], "properties": { "hash": { "description": "Specifies the hash algorithm and value for the chart", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the chart", "type": "string" } }, "readOnly": true }, "provenance": { "description": "The provenance entry associated with the signed Helm Chart", "type": "object", "oneOf": [ { "required": [ "signature" ] }, { "required": [ "content" ] } ], "properties": { "content": { "description": "Specifies the content of the provenance file inline within the document", "type": "string", "format": "byte", "writeOnly": true }, "signature": { "description": "Information about the included signature in the provenance file", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the signature embedded within the provenance file ", "type": "string", "format": "byte", "readOnly": true } }, "readOnly": true } } } } }, "HelmV001SchemaChartHash": { "description": "Specifies the hash algorithm and value for the chart", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the chart", "type": "string" } }, "readOnly": true }, "HelmV001SchemaChartProvenance": { "description": "The provenance entry associated with the signed Helm Chart", "type": "object", "oneOf": [ { "required": [ "signature" ] }, { "required": [ "content" ] } ], "properties": { "content": { "description": "Specifies the content of the provenance file inline within the document", "type": "string", "format": "byte", "writeOnly": true }, "signature": { "description": "Information about the included signature in the provenance file", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the signature embedded within the provenance file ", "type": "string", "format": "byte", "readOnly": true } }, "readOnly": true } } }, "HelmV001SchemaChartProvenanceSignature": { "description": "Information about the included signature in the provenance file", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the signature embedded within the provenance file ", "type": "string", "format": "byte", "readOnly": true } }, "readOnly": true }, "HelmV001SchemaPublicKey": { "description": "The public key that can verify the package signature", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } } }, "InactiveShardLogInfo": { "type": "object", "required": [ "rootHash", "treeSize", "signedTreeHead", "treeID" ], "properties": { "rootHash": { "description": "The current hash value stored at the root of the merkle tree", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" }, "signedTreeHead": { "description": "The current signed tree head", "type": "string", "format": "signedCheckpoint" }, "treeID": { "description": "The current treeID", "type": "string", "pattern": "^[0-9]+$" }, "treeSize": { "description": "The current number of nodes in the merkle tree", "type": "integer", "minimum": 1 } } }, "InclusionProof": { "type": "object", "required": [ "logIndex", "rootHash", "treeSize", "hashes", "checkpoint" ], "properties": { "checkpoint": { "description": "The checkpoint (signed tree head) that the inclusion proof is based on", "type": "string", "format": "signedCheckpoint" }, "hashes": { "description": "A list of hashes required to compute the inclusion proof, sorted in order from leaf to root", "type": "array", "items": { "description": "SHA256 hash value expressed in hexadecimal format", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" } }, "logIndex": { "description": "The index of the entry in the transparency log", "type": "integer", "minimum": 0 }, "rootHash": { "description": "The hash value stored at the root of the merkle tree at the time the proof was generated", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" }, "treeSize": { "description": "The size of the merkle tree at the time the inclusion proof was generated", "type": "integer", "minimum": 1 } } }, "IntotoV001SchemaContent": { "type": "object", "properties": { "envelope": { "description": "envelope", "type": "string", "writeOnly": true }, "hash": { "description": "Specifies the hash algorithm and value encompassing the entire signed envelope; this is computed by the rekor server, client-provided values are ignored", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the archive", "type": "string" } }, "readOnly": true }, "payloadHash": { "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope; this is computed by the rekor server, client-provided values are ignored", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the envelope's payload", "type": "string" } }, "readOnly": true } } }, "IntotoV001SchemaContentHash": { "description": "Specifies the hash algorithm and value encompassing the entire signed envelope; this is computed by the rekor server, client-provided values are ignored", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the archive", "type": "string" } }, "readOnly": true }, "IntotoV001SchemaContentPayloadHash": { "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope; this is computed by the rekor server, client-provided values are ignored", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the envelope's payload", "type": "string" } }, "readOnly": true }, "IntotoV002SchemaContent": { "type": "object", "required": [ "envelope" ], "properties": { "envelope": { "description": "dsse envelope", "type": "object", "required": [ "payloadType", "signatures" ], "properties": { "payload": { "description": "payload of the envelope", "type": "string", "format": "byte", "writeOnly": true }, "payloadType": { "description": "type describing the payload", "type": "string" }, "signatures": { "description": "collection of all signatures of the envelope's payload", "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/IntotoV002SchemaContentEnvelopeSignaturesItems0" } } } }, "hash": { "description": "Specifies the hash algorithm and value encompassing the entire signed envelope", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the archive", "type": "string" } }, "readOnly": true }, "payloadHash": { "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value of the payload", "type": "string" } }, "readOnly": true } } }, "IntotoV002SchemaContentEnvelope": { "description": "dsse envelope", "type": "object", "required": [ "payloadType", "signatures" ], "properties": { "payload": { "description": "payload of the envelope", "type": "string", "format": "byte", "writeOnly": true }, "payloadType": { "description": "type describing the payload", "type": "string" }, "signatures": { "description": "collection of all signatures of the envelope's payload", "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/IntotoV002SchemaContentEnvelopeSignaturesItems0" } } } }, "IntotoV002SchemaContentEnvelopeSignaturesItems0": { "description": "a signature of the envelope's payload along with the public key for the signature", "type": "object", "required": [ "sig", "publicKey" ], "properties": { "keyid": { "description": "optional id of the key used to create the signature", "type": "string" }, "publicKey": { "description": "public key that corresponds to this signature", "type": "string", "format": "byte" }, "sig": { "description": "signature of the payload", "type": "string", "format": "byte" } } }, "IntotoV002SchemaContentHash": { "description": "Specifies the hash algorithm and value encompassing the entire signed envelope", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the archive", "type": "string" } }, "readOnly": true }, "IntotoV002SchemaContentPayloadHash": { "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value of the payload", "type": "string" } }, "readOnly": true }, "JarV001SchemaArchive": { "description": "Information about the archive associated with the entry", "type": "object", "oneOf": [ { "required": [ "hash" ] }, { "required": [ "content" ] } ], "properties": { "content": { "description": "Specifies the archive inline within the document", "type": "string", "format": "byte", "writeOnly": true }, "hash": { "description": "Specifies the hash algorithm and value encompassing the entire signed archive", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the archive", "type": "string" } } } } }, "JarV001SchemaArchiveHash": { "description": "Specifies the hash algorithm and value encompassing the entire signed archive", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the archive", "type": "string" } } }, "JarV001SchemaSignature": { "description": "Information about the included signature in the JAR file", "type": "object", "required": [ "publicKey", "content" ], "properties": { "content": { "description": "Specifies the PKCS7 signature embedded within the JAR file ", "type": "string", "format": "byte", "readOnly": true }, "publicKey": { "description": "The X509 certificate containing the public key JAR which verifies the signature of the JAR", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the content of the X509 certificate containing the public key used to verify the signature", "type": "string", "format": "byte" } }, "readOnly": true } } }, "JarV001SchemaSignaturePublicKey": { "description": "The X509 certificate containing the public key JAR which verifies the signature of the JAR", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the content of the X509 certificate containing the public key used to verify the signature", "type": "string", "format": "byte" } }, "readOnly": true }, "LogEntry": { "type": "object", "additionalProperties": { "$ref": "#/definitions/LogEntryAnon" } }, "LogEntryAnon": { "type": "object", "required": [ "logID", "logIndex", "body", "integratedTime" ], "properties": { "attestation": { "type": "object", "format": "byte", "properties": { "data": { "format": "byte" } } }, "body": { "type": "object", "additionalProperties": true }, "integratedTime": { "description": "The time the entry was added to the log as a Unix timestamp in seconds", "type": "integer" }, "logID": { "description": "This is the SHA256 hash of the DER-encoded public key for the log at the time the entry was included in the log", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" }, "logIndex": { "type": "integer", "minimum": 0 }, "verification": { "type": "object", "properties": { "inclusionProof": { "$ref": "#/definitions/InclusionProof" }, "signedEntryTimestamp": { "description": "Signature over the logID, logIndex, body and integratedTime.", "type": "string", "format": "byte" } } } } }, "LogEntryAnonAttestation": { "type": "object", "format": "byte", "properties": { "data": { "format": "byte" } } }, "LogEntryAnonVerification": { "type": "object", "properties": { "inclusionProof": { "$ref": "#/definitions/InclusionProof" }, "signedEntryTimestamp": { "description": "Signature over the logID, logIndex, body and integratedTime.", "type": "string", "format": "byte" } } }, "LogInfo": { "type": "object", "required": [ "rootHash", "treeSize", "signedTreeHead", "treeID" ], "properties": { "inactiveShards": { "type": "array", "items": { "$ref": "#/definitions/InactiveShardLogInfo" } }, "rootHash": { "description": "The current hash value stored at the root of the merkle tree", "type": "string", "pattern": "^[0-9a-fA-F]{64}$" }, "signedTreeHead": { "description": "The current signed tree head", "type": "string", "format": "signedCheckpoint" }, "treeID": { "description": "The current treeID", "type": "string", "pattern": "^[0-9]+$" }, "treeSize": { "description": "The current number of nodes in the merkle tree", "type": "integer", "minimum": 1 } } }, "ProposedEntry": { "type": "object", "required": [ "kind" ], "properties": { "kind": { "type": "string" } }, "discriminator": "kind" }, "RekordV001SchemaData": { "description": "Information about the content associated with the entry", "type": "object", "oneOf": [ { "required": [ "hash" ] }, { "required": [ "content" ] } ], "properties": { "content": { "description": "Specifies the content inline within the document", "type": "string", "format": "byte", "writeOnly": true }, "hash": { "description": "Specifies the hash algorithm and value for the content", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the content", "type": "string" } }, "readOnly": true } } }, "RekordV001SchemaDataHash": { "description": "Specifies the hash algorithm and value for the content", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the content", "type": "string" } }, "readOnly": true }, "RekordV001SchemaSignature": { "description": "Information about the detached signature associated with the entry", "type": "object", "required": [ "format", "publicKey", "content" ], "properties": { "content": { "description": "Specifies the content of the signature inline within the document", "type": "string", "format": "byte" }, "format": { "description": "Specifies the format of the signature", "type": "string", "enum": [ "pgp", "minisign", "x509", "ssh" ] }, "publicKey": { "description": "The public key that can verify the signature", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } } } } }, "RekordV001SchemaSignaturePublicKey": { "description": "The public key that can verify the signature", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } } }, "Rfc3161V001SchemaTsr": { "description": "Information about the tsr file associated with the entry", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the tsr file content inline within the document", "type": "string", "format": "byte" } } }, "RpmV001SchemaPackage": { "description": "Information about the package associated with the entry", "type": "object", "oneOf": [ { "required": [ "hash" ] }, { "required": [ "content" ] } ], "properties": { "content": { "description": "Specifies the package inline within the document", "type": "string", "format": "byte", "writeOnly": true }, "hash": { "description": "Specifies the hash algorithm and value for the package", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the package", "type": "string" } } }, "headers": { "description": "Values of the RPM headers", "type": "object", "additionalProperties": { "type": "string" }, "readOnly": true } } }, "RpmV001SchemaPackageHash": { "description": "Specifies the hash algorithm and value for the package", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the package", "type": "string" } } }, "RpmV001SchemaPublicKey": { "description": "The PGP public key that can verify the RPM signature", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } } }, "SearchIndex": { "type": "object", "properties": { "email": { "type": "string", "format": "email" }, "hash": { "type": "string", "pattern": "^(sha512:)?[0-9a-fA-F]{128}$|^(sha256:)?[0-9a-fA-F]{64}$|^(sha1:)?[0-9a-fA-F]{40}$" }, "operator": { "type": "string", "enum": [ "and", "or" ] }, "publicKey": { "type": "object", "required": [ "format" ], "properties": { "content": { "type": "string", "format": "byte" }, "format": { "type": "string", "enum": [ "pgp", "x509", "minisign", "ssh", "tuf" ] }, "url": { "type": "string", "format": "uri" } } } } }, "SearchIndexPublicKey": { "type": "object", "required": [ "format" ], "properties": { "content": { "type": "string", "format": "byte" }, "format": { "type": "string", "enum": [ "pgp", "x509", "minisign", "ssh", "tuf" ] }, "url": { "type": "string", "format": "uri" } } }, "SearchLogQuery": { "type": "object", "properties": { "entries": { "type": "array", "maxItems": 10, "minItems": 1, "items": { "$ref": "#/definitions/ProposedEntry" } }, "entryUUIDs": { "type": "array", "maxItems": 10, "minItems": 1, "items": { "type": "string", "pattern": "^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$" } }, "logIndexes": { "type": "array", "maxItems": 10, "minItems": 1, "items": { "type": "integer", "minimum": 0 } } } }, "TUFV001SchemaMetadata": { "description": "TUF metadata", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the metadata inline within the document", "type": "object", "additionalProperties": true } } }, "TUFV001SchemaRoot": { "description": "root metadata containing about the public keys used to sign the manifest", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the metadata inline within the document", "type": "object", "additionalProperties": true } } }, "alpine": { "description": "Alpine package", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "$ref": "#/definitions/alpineSchema" } }, "additionalProperties": false } ] }, "alpineSchema": { "description": "Schema for Alpine package objects", "type": "object", "title": "Alpine Package Schema", "oneOf": [ { "$ref": "#/definitions/alpineV001Schema" } ], "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/alpine/alpine_schema.json" }, "alpineV001Schema": { "description": "Schema for Alpine Package entries", "type": "object", "title": "Alpine v0.0.1 Schema", "required": [ "publicKey", "package" ], "properties": { "package": { "description": "Information about the package associated with the entry", "type": "object", "oneOf": [ { "required": [ "hash" ] }, { "required": [ "content" ] } ], "properties": { "content": { "description": "Specifies the package inline within the document", "type": "string", "format": "byte", "writeOnly": true }, "hash": { "description": "Specifies the hash algorithm and value for the package", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the package", "type": "string" } }, "readOnly": true }, "pkginfo": { "description": "Values of the .PKGINFO key / value pairs", "type": "object", "additionalProperties": { "type": "string" }, "readOnly": true } } }, "publicKey": { "description": "The public key that can verify the package signature", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } } } }, "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/alpine/alpine_v0_0_1_schema.json" }, "cose": { "description": "COSE object", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "$ref": "#/definitions/coseSchema" } }, "additionalProperties": false } ] }, "coseSchema": { "description": "COSE for Rekord objects", "type": "object", "title": "COSE Schema", "oneOf": [ { "$ref": "#/definitions/coseV001Schema" } ], "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/cose/cose_schema.json" }, "coseV001Schema": { "description": "Schema for cose object", "type": "object", "title": "cose v0.0.1 Schema", "required": [ "publicKey" ], "properties": { "data": { "description": "Information about the content associated with the entry", "type": "object", "properties": { "aad": { "description": "Specifies the additional authenticated data required to verify the signature", "type": "string", "format": "byte", "writeOnly": true }, "envelopeHash": { "description": "Specifies the hash algorithm and value for the COSE envelope", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the envelope", "type": "string" } }, "readOnly": true }, "payloadHash": { "description": "Specifies the hash algorithm and value for the content", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the content", "type": "string" } }, "readOnly": true } } }, "message": { "description": "The COSE Sign1 Message", "type": "string", "format": "byte", "writeOnly": true }, "publicKey": { "description": "The public key that can verify the signature", "type": "string", "format": "byte" } }, "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/cose/cose_v0_0_1_schema.json" }, "dsse": { "description": "DSSE envelope", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "$ref": "#/definitions/dsseSchema" } }, "additionalProperties": false } ] }, "dsseSchema": { "description": "log entry schema for dsse envelopes", "type": "object", "title": "DSSE Schema", "oneOf": [ { "$ref": "#/definitions/dsseV001Schema" } ], "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/dsse/dsse_schema.json" }, "dsseV001Schema": { "description": "Schema for DSSE envelopes", "type": "object", "title": "DSSE v0.0.1 Schema", "oneOf": [ { "required": [ "proposedContent" ] }, { "required": [ "signatures", "envelopeHash", "payloadHash" ] } ], "properties": { "envelopeHash": { "description": "Specifies the hash algorithm and value encompassing the entire envelope sent to Rekor", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The value of the computed digest over the entire envelope", "type": "string" } }, "readOnly": true }, "payloadHash": { "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The value of the computed digest over the payload within the envelope", "type": "string" } }, "readOnly": true }, "proposedContent": { "type": "object", "required": [ "envelope", "verifiers" ], "properties": { "envelope": { "description": "DSSE envelope specified as a stringified JSON object", "type": "string", "writeOnly": true }, "verifiers": { "description": "collection of all verification material (e.g. public keys or certificates) used to verify signatures over envelope's payload, specified as base64-encoded strings", "type": "array", "minItems": 1, "items": { "type": "string", "format": "byte" }, "writeOnly": true } }, "writeOnly": true }, "signatures": { "description": "extracted collection of all signatures of the envelope's payload; elements will be sorted by lexicographical order of the base64 encoded signature strings", "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/DSSEV001SchemaSignaturesItems0" }, "readOnly": true } }, "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/dsse/dsse_v0_0_1_schema.json" }, "hashedrekord": { "description": "Hashed Rekord object", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "$ref": "#/definitions/hashedrekordSchema" } }, "additionalProperties": false } ] }, "hashedrekordSchema": { "description": "Schema for Rekord objects", "type": "object", "title": "Rekor Schema", "oneOf": [ { "$ref": "#/definitions/hashedrekordV001Schema" } ], "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/hashedrekord/hasehedrekord_schema.json" }, "hashedrekordV001Schema": { "description": "Schema for Hashed Rekord object", "type": "object", "title": "Hashed Rekor v0.0.1 Schema", "required": [ "signature", "data" ], "properties": { "data": { "description": "Information about the content associated with the entry", "type": "object", "properties": { "hash": { "description": "Specifies the hash algorithm and value for the content", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256", "sha384", "sha512" ] }, "value": { "description": "The hash value for the content, as represented by a lower case hexadecimal string", "type": "string" } } } } }, "signature": { "description": "Information about the detached signature associated with the entry", "type": "object", "properties": { "content": { "description": "Specifies the content of the signature inline within the document", "type": "string", "format": "byte" }, "publicKey": { "description": "The public key that can verify the signature; this can also be an X509 code signing certificate that contains the raw public key information", "type": "object", "properties": { "content": { "description": "Specifies the content of the public key or code signing certificate inline within the document", "type": "string", "format": "byte" } } } } } }, "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/rekord/rekord_v0_0_1_schema.json" }, "helm": { "description": "Helm chart", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "$ref": "#/definitions/helmSchema" } } } ] }, "helmSchema": { "description": "Schema for Helm objects", "type": "object", "title": "Helm Schema", "oneOf": [ { "$ref": "#/definitions/helmV001Schema" } ], "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/helm/helm_schema.json" }, "helmV001Schema": { "description": "Schema for Helm object", "type": "object", "title": "Helm v0.0.1 Schema", "required": [ "publicKey", "chart" ], "properties": { "chart": { "description": "Information about the Helm chart associated with the entry", "type": "object", "required": [ "provenance" ], "properties": { "hash": { "description": "Specifies the hash algorithm and value for the chart", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the chart", "type": "string" } }, "readOnly": true }, "provenance": { "description": "The provenance entry associated with the signed Helm Chart", "type": "object", "oneOf": [ { "required": [ "signature" ] }, { "required": [ "content" ] } ], "properties": { "content": { "description": "Specifies the content of the provenance file inline within the document", "type": "string", "format": "byte", "writeOnly": true }, "signature": { "description": "Information about the included signature in the provenance file", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the signature embedded within the provenance file ", "type": "string", "format": "byte", "readOnly": true } }, "readOnly": true } } } } }, "publicKey": { "description": "The public key that can verify the package signature", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } } } }, "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/helm/helm_v0_0_1_schema.json" }, "intoto": { "description": "Intoto object", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "$ref": "#/definitions/intotoSchema" } }, "additionalProperties": false } ] }, "intotoSchema": { "description": "Intoto for Rekord objects", "type": "object", "title": "Intoto Schema", "oneOf": [ { "$ref": "#/definitions/intotoV001Schema" }, { "$ref": "#/definitions/intotoV002Schema" } ], "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/intoto/intoto_schema.json" }, "intotoV001Schema": { "description": "Schema for intoto object", "type": "object", "title": "intoto v0.0.1 Schema", "required": [ "publicKey", "content" ], "properties": { "content": { "type": "object", "properties": { "envelope": { "description": "envelope", "type": "string", "writeOnly": true }, "hash": { "description": "Specifies the hash algorithm and value encompassing the entire signed envelope; this is computed by the rekor server, client-provided values are ignored", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the archive", "type": "string" } }, "readOnly": true }, "payloadHash": { "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope; this is computed by the rekor server, client-provided values are ignored", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the envelope's payload", "type": "string" } }, "readOnly": true } } }, "publicKey": { "description": "The public key that can verify the signature", "type": "string", "format": "byte" } }, "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/intoto/intoto_v0_0_1_schema.json" }, "intotoV002Schema": { "description": "Schema for intoto object", "type": "object", "title": "intoto v0.0.2 Schema", "required": [ "content" ], "properties": { "content": { "type": "object", "required": [ "envelope" ], "properties": { "envelope": { "description": "dsse envelope", "type": "object", "required": [ "payloadType", "signatures" ], "properties": { "payload": { "description": "payload of the envelope", "type": "string", "format": "byte", "writeOnly": true }, "payloadType": { "description": "type describing the payload", "type": "string" }, "signatures": { "description": "collection of all signatures of the envelope's payload", "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/IntotoV002SchemaContentEnvelopeSignaturesItems0" } } } }, "hash": { "description": "Specifies the hash algorithm and value encompassing the entire signed envelope", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the archive", "type": "string" } }, "readOnly": true }, "payloadHash": { "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value of the payload", "type": "string" } }, "readOnly": true } } } }, "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/intoto/intoto_v0_0_2_schema.json" }, "jar": { "description": "Java Archive (JAR)", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "$ref": "#/definitions/jarSchema" } }, "additionalProperties": false } ] }, "jarSchema": { "description": "Schema for JAR objects", "type": "object", "title": "JAR Schema", "oneOf": [ { "$ref": "#/definitions/jarV001Schema" } ], "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/jar/jar_schema.json" }, "jarV001Schema": { "description": "Schema for JAR entries", "type": "object", "title": "JAR v0.0.1 Schema", "required": [ "archive" ], "properties": { "archive": { "description": "Information about the archive associated with the entry", "type": "object", "oneOf": [ { "required": [ "hash" ] }, { "required": [ "content" ] } ], "properties": { "content": { "description": "Specifies the archive inline within the document", "type": "string", "format": "byte", "writeOnly": true }, "hash": { "description": "Specifies the hash algorithm and value encompassing the entire signed archive", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the archive", "type": "string" } } } } }, "signature": { "description": "Information about the included signature in the JAR file", "type": "object", "required": [ "publicKey", "content" ], "properties": { "content": { "description": "Specifies the PKCS7 signature embedded within the JAR file ", "type": "string", "format": "byte", "readOnly": true }, "publicKey": { "description": "The X509 certificate containing the public key JAR which verifies the signature of the JAR", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the content of the X509 certificate containing the public key used to verify the signature", "type": "string", "format": "byte" } }, "readOnly": true } } } }, "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/jar/jar_v0_0_1_schema.json" }, "rekord": { "description": "Rekord object", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "$ref": "#/definitions/rekordSchema" } }, "additionalProperties": false } ] }, "rekordSchema": { "description": "Schema for Rekord objects", "type": "object", "title": "Rekor Schema", "oneOf": [ { "$ref": "#/definitions/rekordV001Schema" } ], "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/rekord/rekord_schema.json" }, "rekordV001Schema": { "description": "Schema for Rekord object", "type": "object", "title": "Rekor v0.0.1 Schema", "required": [ "signature", "data" ], "properties": { "data": { "description": "Information about the content associated with the entry", "type": "object", "oneOf": [ { "required": [ "hash" ] }, { "required": [ "content" ] } ], "properties": { "content": { "description": "Specifies the content inline within the document", "type": "string", "format": "byte", "writeOnly": true }, "hash": { "description": "Specifies the hash algorithm and value for the content", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the content", "type": "string" } }, "readOnly": true } } }, "signature": { "description": "Information about the detached signature associated with the entry", "type": "object", "required": [ "format", "publicKey", "content" ], "properties": { "content": { "description": "Specifies the content of the signature inline within the document", "type": "string", "format": "byte" }, "format": { "description": "Specifies the format of the signature", "type": "string", "enum": [ "pgp", "minisign", "x509", "ssh" ] }, "publicKey": { "description": "The public key that can verify the signature", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } } } } } }, "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/rekord/rekord_v0_0_1_schema.json" }, "rfc3161": { "description": "RFC3161 Timestamp", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "$ref": "#/definitions/rfc3161Schema" } }, "additionalProperties": false } ] }, "rfc3161Schema": { "description": "Schema for RFC 3161 timestamp objects", "type": "object", "title": "Timestamp Schema", "oneOf": [ { "$ref": "#/definitions/rfc3161V001Schema" } ], "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/rfc3161/rfc3161_schema.json" }, "rfc3161V001Schema": { "description": "Schema for RFC3161 entries", "type": "object", "title": "Timestamp v0.0.1 Schema", "required": [ "tsr" ], "properties": { "tsr": { "description": "Information about the tsr file associated with the entry", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the tsr file content inline within the document", "type": "string", "format": "byte" } } } }, "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/timestamp/timestamp_v0_0_1_schema.json" }, "rpm": { "description": "RPM package", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "$ref": "#/definitions/rpmSchema" } }, "additionalProperties": false } ] }, "rpmSchema": { "description": "Schema for RPM objects", "type": "object", "title": "RPM Schema", "oneOf": [ { "$ref": "#/definitions/rpmV001Schema" } ], "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/rpm/rpm_schema.json" }, "rpmV001Schema": { "description": "Schema for RPM entries", "type": "object", "title": "RPM v0.0.1 Schema", "required": [ "publicKey", "package" ], "properties": { "package": { "description": "Information about the package associated with the entry", "type": "object", "oneOf": [ { "required": [ "hash" ] }, { "required": [ "content" ] } ], "properties": { "content": { "description": "Specifies the package inline within the document", "type": "string", "format": "byte", "writeOnly": true }, "hash": { "description": "Specifies the hash algorithm and value for the package", "type": "object", "required": [ "algorithm", "value" ], "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the package", "type": "string" } } }, "headers": { "description": "Values of the RPM headers", "type": "object", "additionalProperties": { "type": "string" }, "readOnly": true } } }, "publicKey": { "description": "The PGP public key that can verify the RPM signature", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } } } }, "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/rpm/rpm_v0_0_1_schema.json" }, "tuf": { "description": "TUF metadata", "type": "object", "allOf": [ { "$ref": "#/definitions/ProposedEntry" }, { "required": [ "apiVersion", "spec" ], "properties": { "apiVersion": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "spec": { "$ref": "#/definitions/tufSchema" } }, "additionalProperties": false } ] }, "tufSchema": { "description": "Schema for TUF metadata objects", "type": "object", "title": "TUF Schema", "oneOf": [ { "$ref": "#/definitions/tufV001Schema" } ], "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/tuf/tuf_schema.json" }, "tufV001Schema": { "description": "Schema for TUF metadata entries", "type": "object", "title": "TUF v0.0.1 Schema", "required": [ "metadata", "root" ], "properties": { "metadata": { "description": "TUF metadata", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the metadata inline within the document", "type": "object", "additionalProperties": true } } }, "root": { "description": "root metadata containing about the public keys used to sign the manifest", "type": "object", "required": [ "content" ], "properties": { "content": { "description": "Specifies the metadata inline within the document", "type": "object", "additionalProperties": true } } }, "spec_version": { "description": "TUF specification version", "type": "string", "readOnly": true } }, "$schema": "http://json-schema.org/draft-07/schema", "$id": "http://rekor.sigstore.dev/types/tuf/tuf_v0_0_1_schema.json" } }, "responses": { "BadContent": { "description": "The content supplied to the server was invalid", "schema": { "$ref": "#/definitions/Error" } }, "Conflict": { "description": "The request conflicts with the current state of the transparency log", "schema": { "$ref": "#/definitions/Error" }, "headers": { "Location": { "type": "string", "format": "uri" } } }, "InternalServerError": { "description": "There was an internal error in the server while processing the request", "schema": { "$ref": "#/definitions/Error" } }, "NotFound": { "description": "The content requested could not be found" }, "UnprocessableEntity": { "description": "The server understood the request but is unable to process the contained instructions", "schema": { "$ref": "#/definitions/Error" } } } }`)) } rekor-1.3.5/pkg/generated/restapi/operations/000077500000000000000000000000001455727245600212345ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/restapi/operations/entries/000077500000000000000000000000001455727245600227055ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/restapi/operations/entries/create_log_entry.go000066400000000000000000000047131455727245600265660ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "net/http" "github.com/go-openapi/runtime/middleware" ) // CreateLogEntryHandlerFunc turns a function with the right signature into a create log entry handler type CreateLogEntryHandlerFunc func(CreateLogEntryParams) middleware.Responder // Handle executing the request and returning a response func (fn CreateLogEntryHandlerFunc) Handle(params CreateLogEntryParams) middleware.Responder { return fn(params) } // CreateLogEntryHandler interface for that can handle valid create log entry params type CreateLogEntryHandler interface { Handle(CreateLogEntryParams) middleware.Responder } // NewCreateLogEntry creates a new http.Handler for the create log entry operation func NewCreateLogEntry(ctx *middleware.Context, handler CreateLogEntryHandler) *CreateLogEntry { return &CreateLogEntry{Context: ctx, Handler: handler} } /* CreateLogEntry swagger:route POST /api/v1/log/entries entries createLogEntry # Creates an entry in the transparency log Creates an entry in the transparency log for a detached signature, public key, and content. Items can be included in the request or fetched by the server when URLs are specified. */ type CreateLogEntry struct { Context *middleware.Context Handler CreateLogEntryHandler } func (o *CreateLogEntry) ServeHTTP(rw http.ResponseWriter, r *http.Request) { route, rCtx, _ := o.Context.RouteInfo(r) if rCtx != nil { *r = *rCtx } var Params = NewCreateLogEntryParams() if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params o.Context.Respond(rw, r, route.Produces, route, err) return } res := o.Handler.Handle(Params) // actually handle the request o.Context.Respond(rw, r, route.Produces, route, res) } rekor-1.3.5/pkg/generated/restapi/operations/entries/create_log_entry_parameters.go000066400000000000000000000053461455727245600310140ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "io" "net/http" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/validate" "github.com/sigstore/rekor/pkg/generated/models" ) // NewCreateLogEntryParams creates a new CreateLogEntryParams object // // There are no default values defined in the spec. func NewCreateLogEntryParams() CreateLogEntryParams { return CreateLogEntryParams{} } // CreateLogEntryParams contains all the bound params for the create log entry operation // typically these are obtained from a http.Request // // swagger:parameters createLogEntry type CreateLogEntryParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` /* Required: true In: body */ ProposedEntry models.ProposedEntry } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls. // // To ensure default values, the struct must have been initialized with NewCreateLogEntryParams() beforehand. func (o *CreateLogEntryParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error o.HTTPRequest = r if runtime.HasBody(r) { defer r.Body.Close() body, err := models.UnmarshalProposedEntry(r.Body, route.Consumer) if err != nil { if err == io.EOF { err = errors.Required("proposedEntry", "body", "") } res = append(res, err) } else { // validate body object if err := body.Validate(route.Formats); err != nil { res = append(res, err) } ctx := validate.WithOperationRequest(r.Context()) if err := body.ContextValidate(ctx, route.Formats); err != nil { res = append(res, err) } if len(res) == 0 { o.ProposedEntry = body } } } else { res = append(res, errors.Required("proposedEntry", "body", "")) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } rekor-1.3.5/pkg/generated/restapi/operations/entries/create_log_entry_responses.go000066400000000000000000000173111455727245600306650ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" ) // CreateLogEntryCreatedCode is the HTTP code returned for type CreateLogEntryCreated const CreateLogEntryCreatedCode int = 201 /* CreateLogEntryCreated Returns the entry created in the transparency log swagger:response createLogEntryCreated */ type CreateLogEntryCreated struct { /*UUID of log entry */ ETag string `json:"ETag"` /*URI location of log entry */ Location strfmt.URI `json:"Location"` /* In: Body */ Payload models.LogEntry `json:"body,omitempty"` } // NewCreateLogEntryCreated creates CreateLogEntryCreated with default headers values func NewCreateLogEntryCreated() *CreateLogEntryCreated { return &CreateLogEntryCreated{} } // WithETag adds the eTag to the create log entry created response func (o *CreateLogEntryCreated) WithETag(eTag string) *CreateLogEntryCreated { o.ETag = eTag return o } // SetETag sets the eTag to the create log entry created response func (o *CreateLogEntryCreated) SetETag(eTag string) { o.ETag = eTag } // WithLocation adds the location to the create log entry created response func (o *CreateLogEntryCreated) WithLocation(location strfmt.URI) *CreateLogEntryCreated { o.Location = location return o } // SetLocation sets the location to the create log entry created response func (o *CreateLogEntryCreated) SetLocation(location strfmt.URI) { o.Location = location } // WithPayload adds the payload to the create log entry created response func (o *CreateLogEntryCreated) WithPayload(payload models.LogEntry) *CreateLogEntryCreated { o.Payload = payload return o } // SetPayload sets the payload to the create log entry created response func (o *CreateLogEntryCreated) SetPayload(payload models.LogEntry) { o.Payload = payload } // WriteResponse to the client func (o *CreateLogEntryCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { // response header ETag eTag := o.ETag if eTag != "" { rw.Header().Set("ETag", eTag) } // response header Location location := o.Location.String() if location != "" { rw.Header().Set("Location", location) } rw.WriteHeader(201) payload := o.Payload if payload == nil { // return empty map payload = models.LogEntry{} } if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } // CreateLogEntryBadRequestCode is the HTTP code returned for type CreateLogEntryBadRequest const CreateLogEntryBadRequestCode int = 400 /* CreateLogEntryBadRequest The content supplied to the server was invalid swagger:response createLogEntryBadRequest */ type CreateLogEntryBadRequest struct { /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewCreateLogEntryBadRequest creates CreateLogEntryBadRequest with default headers values func NewCreateLogEntryBadRequest() *CreateLogEntryBadRequest { return &CreateLogEntryBadRequest{} } // WithPayload adds the payload to the create log entry bad request response func (o *CreateLogEntryBadRequest) WithPayload(payload *models.Error) *CreateLogEntryBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the create log entry bad request response func (o *CreateLogEntryBadRequest) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *CreateLogEntryBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } // CreateLogEntryConflictCode is the HTTP code returned for type CreateLogEntryConflict const CreateLogEntryConflictCode int = 409 /* CreateLogEntryConflict The request conflicts with the current state of the transparency log swagger:response createLogEntryConflict */ type CreateLogEntryConflict struct { /* */ Location strfmt.URI `json:"Location"` /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewCreateLogEntryConflict creates CreateLogEntryConflict with default headers values func NewCreateLogEntryConflict() *CreateLogEntryConflict { return &CreateLogEntryConflict{} } // WithLocation adds the location to the create log entry conflict response func (o *CreateLogEntryConflict) WithLocation(location strfmt.URI) *CreateLogEntryConflict { o.Location = location return o } // SetLocation sets the location to the create log entry conflict response func (o *CreateLogEntryConflict) SetLocation(location strfmt.URI) { o.Location = location } // WithPayload adds the payload to the create log entry conflict response func (o *CreateLogEntryConflict) WithPayload(payload *models.Error) *CreateLogEntryConflict { o.Payload = payload return o } // SetPayload sets the payload to the create log entry conflict response func (o *CreateLogEntryConflict) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *CreateLogEntryConflict) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { // response header Location location := o.Location.String() if location != "" { rw.Header().Set("Location", location) } rw.WriteHeader(409) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } /* CreateLogEntryDefault There was an internal error in the server while processing the request swagger:response createLogEntryDefault */ type CreateLogEntryDefault struct { _statusCode int /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewCreateLogEntryDefault creates CreateLogEntryDefault with default headers values func NewCreateLogEntryDefault(code int) *CreateLogEntryDefault { if code <= 0 { code = 500 } return &CreateLogEntryDefault{ _statusCode: code, } } // WithStatusCode adds the status to the create log entry default response func (o *CreateLogEntryDefault) WithStatusCode(code int) *CreateLogEntryDefault { o._statusCode = code return o } // SetStatusCode sets the status to the create log entry default response func (o *CreateLogEntryDefault) SetStatusCode(code int) { o._statusCode = code } // WithPayload adds the payload to the create log entry default response func (o *CreateLogEntryDefault) WithPayload(payload *models.Error) *CreateLogEntryDefault { o.Payload = payload return o } // SetPayload sets the payload to the create log entry default response func (o *CreateLogEntryDefault) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *CreateLogEntryDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(o._statusCode) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } rekor-1.3.5/pkg/generated/restapi/operations/entries/create_log_entry_urlbuilder.go000066400000000000000000000054771455727245600310270ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "errors" "net/url" golangswaggerpaths "path" ) // CreateLogEntryURL generates an URL for the create log entry operation type CreateLogEntryURL struct { _basePath string } // WithBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *CreateLogEntryURL) WithBasePath(bp string) *CreateLogEntryURL { o.SetBasePath(bp) return o } // SetBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *CreateLogEntryURL) SetBasePath(bp string) { o._basePath = bp } // Build a url path and query string func (o *CreateLogEntryURL) Build() (*url.URL, error) { var _result url.URL var _path = "/api/v1/log/entries" _basePath := o._basePath _result.Path = golangswaggerpaths.Join(_basePath, _path) return &_result, nil } // Must is a helper function to panic when the url builder returns an error func (o *CreateLogEntryURL) Must(u *url.URL, err error) *url.URL { if err != nil { panic(err) } if u == nil { panic("url can't be nil") } return u } // String returns the string representation of the path with query string func (o *CreateLogEntryURL) String() string { return o.Must(o.Build()).String() } // BuildFull builds a full url with scheme, host, path and query string func (o *CreateLogEntryURL) BuildFull(scheme, host string) (*url.URL, error) { if scheme == "" { return nil, errors.New("scheme is required for a full url on CreateLogEntryURL") } if host == "" { return nil, errors.New("host is required for a full url on CreateLogEntryURL") } base, err := o.Build() if err != nil { return nil, err } base.Scheme = scheme base.Host = host return base, nil } // StringFull returns the string representation of a complete url func (o *CreateLogEntryURL) StringFull(scheme, host string) string { return o.Must(o.BuildFull(scheme, host)).String() } rekor-1.3.5/pkg/generated/restapi/operations/entries/get_log_entry_by_index.go000066400000000000000000000046421455727245600277640ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "net/http" "github.com/go-openapi/runtime/middleware" ) // GetLogEntryByIndexHandlerFunc turns a function with the right signature into a get log entry by index handler type GetLogEntryByIndexHandlerFunc func(GetLogEntryByIndexParams) middleware.Responder // Handle executing the request and returning a response func (fn GetLogEntryByIndexHandlerFunc) Handle(params GetLogEntryByIndexParams) middleware.Responder { return fn(params) } // GetLogEntryByIndexHandler interface for that can handle valid get log entry by index params type GetLogEntryByIndexHandler interface { Handle(GetLogEntryByIndexParams) middleware.Responder } // NewGetLogEntryByIndex creates a new http.Handler for the get log entry by index operation func NewGetLogEntryByIndex(ctx *middleware.Context, handler GetLogEntryByIndexHandler) *GetLogEntryByIndex { return &GetLogEntryByIndex{Context: ctx, Handler: handler} } /* GetLogEntryByIndex swagger:route GET /api/v1/log/entries entries getLogEntryByIndex Retrieves an entry and inclusion proof from the transparency log (if it exists) by index */ type GetLogEntryByIndex struct { Context *middleware.Context Handler GetLogEntryByIndexHandler } func (o *GetLogEntryByIndex) ServeHTTP(rw http.ResponseWriter, r *http.Request) { route, rCtx, _ := o.Context.RouteInfo(r) if rCtx != nil { *r = *rCtx } var Params = NewGetLogEntryByIndexParams() if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params o.Context.Respond(rw, r, route.Produces, route, err) return } res := o.Handler.Handle(Params) // actually handle the request o.Context.Respond(rw, r, route.Produces, route, res) } rekor-1.3.5/pkg/generated/restapi/operations/entries/get_log_entry_by_index_parameters.go000066400000000000000000000065171455727245600322120ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // NewGetLogEntryByIndexParams creates a new GetLogEntryByIndexParams object // // There are no default values defined in the spec. func NewGetLogEntryByIndexParams() GetLogEntryByIndexParams { return GetLogEntryByIndexParams{} } // GetLogEntryByIndexParams contains all the bound params for the get log entry by index operation // typically these are obtained from a http.Request // // swagger:parameters getLogEntryByIndex type GetLogEntryByIndexParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` /*specifies the index of the entry in the transparency log to be retrieved Required: true Minimum: 0 In: query */ LogIndex int64 } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls. // // To ensure default values, the struct must have been initialized with NewGetLogEntryByIndexParams() beforehand. func (o *GetLogEntryByIndexParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error o.HTTPRequest = r qs := runtime.Values(r.URL.Query()) qLogIndex, qhkLogIndex, _ := qs.GetOK("logIndex") if err := o.bindLogIndex(qLogIndex, qhkLogIndex, route.Formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // bindLogIndex binds and validates parameter LogIndex from query. func (o *GetLogEntryByIndexParams) bindLogIndex(rawData []string, hasKey bool, formats strfmt.Registry) error { if !hasKey { return errors.Required("logIndex", "query", rawData) } var raw string if len(rawData) > 0 { raw = rawData[len(rawData)-1] } // Required: true // AllowEmptyValue: false if err := validate.RequiredString("logIndex", "query", raw); err != nil { return err } value, err := swag.ConvertInt64(raw) if err != nil { return errors.InvalidType("logIndex", "query", "int64", raw) } o.LogIndex = value if err := o.validateLogIndex(formats); err != nil { return err } return nil } // validateLogIndex carries on validations for parameter LogIndex func (o *GetLogEntryByIndexParams) validateLogIndex(formats strfmt.Registry) error { if err := validate.MinimumInt("logIndex", "query", o.LogIndex, 0, false); err != nil { return err } return nil } rekor-1.3.5/pkg/generated/restapi/operations/entries/get_log_entry_by_index_responses.go000066400000000000000000000107601455727245600320630ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/runtime" "github.com/sigstore/rekor/pkg/generated/models" ) // GetLogEntryByIndexOKCode is the HTTP code returned for type GetLogEntryByIndexOK const GetLogEntryByIndexOKCode int = 200 /* GetLogEntryByIndexOK the entry in the transparency log requested along with an inclusion proof swagger:response getLogEntryByIndexOK */ type GetLogEntryByIndexOK struct { /* In: Body */ Payload models.LogEntry `json:"body,omitempty"` } // NewGetLogEntryByIndexOK creates GetLogEntryByIndexOK with default headers values func NewGetLogEntryByIndexOK() *GetLogEntryByIndexOK { return &GetLogEntryByIndexOK{} } // WithPayload adds the payload to the get log entry by index o k response func (o *GetLogEntryByIndexOK) WithPayload(payload models.LogEntry) *GetLogEntryByIndexOK { o.Payload = payload return o } // SetPayload sets the payload to the get log entry by index o k response func (o *GetLogEntryByIndexOK) SetPayload(payload models.LogEntry) { o.Payload = payload } // WriteResponse to the client func (o *GetLogEntryByIndexOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(200) payload := o.Payload if payload == nil { // return empty map payload = models.LogEntry{} } if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } // GetLogEntryByIndexNotFoundCode is the HTTP code returned for type GetLogEntryByIndexNotFound const GetLogEntryByIndexNotFoundCode int = 404 /* GetLogEntryByIndexNotFound The content requested could not be found swagger:response getLogEntryByIndexNotFound */ type GetLogEntryByIndexNotFound struct { } // NewGetLogEntryByIndexNotFound creates GetLogEntryByIndexNotFound with default headers values func NewGetLogEntryByIndexNotFound() *GetLogEntryByIndexNotFound { return &GetLogEntryByIndexNotFound{} } // WriteResponse to the client func (o *GetLogEntryByIndexNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses rw.WriteHeader(404) } /* GetLogEntryByIndexDefault There was an internal error in the server while processing the request swagger:response getLogEntryByIndexDefault */ type GetLogEntryByIndexDefault struct { _statusCode int /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewGetLogEntryByIndexDefault creates GetLogEntryByIndexDefault with default headers values func NewGetLogEntryByIndexDefault(code int) *GetLogEntryByIndexDefault { if code <= 0 { code = 500 } return &GetLogEntryByIndexDefault{ _statusCode: code, } } // WithStatusCode adds the status to the get log entry by index default response func (o *GetLogEntryByIndexDefault) WithStatusCode(code int) *GetLogEntryByIndexDefault { o._statusCode = code return o } // SetStatusCode sets the status to the get log entry by index default response func (o *GetLogEntryByIndexDefault) SetStatusCode(code int) { o._statusCode = code } // WithPayload adds the payload to the get log entry by index default response func (o *GetLogEntryByIndexDefault) WithPayload(payload *models.Error) *GetLogEntryByIndexDefault { o.Payload = payload return o } // SetPayload sets the payload to the get log entry by index default response func (o *GetLogEntryByIndexDefault) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *GetLogEntryByIndexDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(o._statusCode) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } rekor-1.3.5/pkg/generated/restapi/operations/entries/get_log_entry_by_index_urlbuilder.go000066400000000000000000000061501455727245600322110ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "errors" "net/url" golangswaggerpaths "path" "github.com/go-openapi/swag" ) // GetLogEntryByIndexURL generates an URL for the get log entry by index operation type GetLogEntryByIndexURL struct { LogIndex int64 _basePath string // avoid unkeyed usage _ struct{} } // WithBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *GetLogEntryByIndexURL) WithBasePath(bp string) *GetLogEntryByIndexURL { o.SetBasePath(bp) return o } // SetBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *GetLogEntryByIndexURL) SetBasePath(bp string) { o._basePath = bp } // Build a url path and query string func (o *GetLogEntryByIndexURL) Build() (*url.URL, error) { var _result url.URL var _path = "/api/v1/log/entries" _basePath := o._basePath _result.Path = golangswaggerpaths.Join(_basePath, _path) qs := make(url.Values) logIndexQ := swag.FormatInt64(o.LogIndex) if logIndexQ != "" { qs.Set("logIndex", logIndexQ) } _result.RawQuery = qs.Encode() return &_result, nil } // Must is a helper function to panic when the url builder returns an error func (o *GetLogEntryByIndexURL) Must(u *url.URL, err error) *url.URL { if err != nil { panic(err) } if u == nil { panic("url can't be nil") } return u } // String returns the string representation of the path with query string func (o *GetLogEntryByIndexURL) String() string { return o.Must(o.Build()).String() } // BuildFull builds a full url with scheme, host, path and query string func (o *GetLogEntryByIndexURL) BuildFull(scheme, host string) (*url.URL, error) { if scheme == "" { return nil, errors.New("scheme is required for a full url on GetLogEntryByIndexURL") } if host == "" { return nil, errors.New("host is required for a full url on GetLogEntryByIndexURL") } base, err := o.Build() if err != nil { return nil, err } base.Scheme = scheme base.Host = host return base, nil } // StringFull returns the string representation of a complete url func (o *GetLogEntryByIndexURL) StringFull(scheme, host string) string { return o.Must(o.BuildFull(scheme, host)).String() } rekor-1.3.5/pkg/generated/restapi/operations/entries/get_log_entry_by_uuid.go000066400000000000000000000051031455727245600276140ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "net/http" "github.com/go-openapi/runtime/middleware" ) // GetLogEntryByUUIDHandlerFunc turns a function with the right signature into a get log entry by UUID handler type GetLogEntryByUUIDHandlerFunc func(GetLogEntryByUUIDParams) middleware.Responder // Handle executing the request and returning a response func (fn GetLogEntryByUUIDHandlerFunc) Handle(params GetLogEntryByUUIDParams) middleware.Responder { return fn(params) } // GetLogEntryByUUIDHandler interface for that can handle valid get log entry by UUID params type GetLogEntryByUUIDHandler interface { Handle(GetLogEntryByUUIDParams) middleware.Responder } // NewGetLogEntryByUUID creates a new http.Handler for the get log entry by UUID operation func NewGetLogEntryByUUID(ctx *middleware.Context, handler GetLogEntryByUUIDHandler) *GetLogEntryByUUID { return &GetLogEntryByUUID{Context: ctx, Handler: handler} } /* GetLogEntryByUUID swagger:route GET /api/v1/log/entries/{entryUUID} entries getLogEntryByUuid # Get log entry and information required to generate an inclusion proof for the entry in the transparency log Returns the entry, root hash, tree size, and a list of hashes that can be used to calculate proof of an entry being included in the transparency log */ type GetLogEntryByUUID struct { Context *middleware.Context Handler GetLogEntryByUUIDHandler } func (o *GetLogEntryByUUID) ServeHTTP(rw http.ResponseWriter, r *http.Request) { route, rCtx, _ := o.Context.RouteInfo(r) if rCtx != nil { *r = *rCtx } var Params = NewGetLogEntryByUUIDParams() if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params o.Context.Respond(rw, r, route.Produces, route, err) return } res := o.Handler.Handle(Params) // actually handle the request o.Context.Respond(rw, r, route.Produces, route, res) } rekor-1.3.5/pkg/generated/restapi/operations/entries/get_log_entry_by_uuid_parameters.go000066400000000000000000000060721455727245600320450ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/errors" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" "github.com/go-openapi/validate" ) // NewGetLogEntryByUUIDParams creates a new GetLogEntryByUUIDParams object // // There are no default values defined in the spec. func NewGetLogEntryByUUIDParams() GetLogEntryByUUIDParams { return GetLogEntryByUUIDParams{} } // GetLogEntryByUUIDParams contains all the bound params for the get log entry by UUID operation // typically these are obtained from a http.Request // // swagger:parameters getLogEntryByUUID type GetLogEntryByUUIDParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` /*the UUID of the entry for which the inclusion proof information should be returned Required: true Pattern: ^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$ In: path */ EntryUUID string } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls. // // To ensure default values, the struct must have been initialized with NewGetLogEntryByUUIDParams() beforehand. func (o *GetLogEntryByUUIDParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error o.HTTPRequest = r rEntryUUID, rhkEntryUUID, _ := route.Params.GetOK("entryUUID") if err := o.bindEntryUUID(rEntryUUID, rhkEntryUUID, route.Formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // bindEntryUUID binds and validates parameter EntryUUID from path. func (o *GetLogEntryByUUIDParams) bindEntryUUID(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string if len(rawData) > 0 { raw = rawData[len(rawData)-1] } // Required: true // Parameter is provided by construction from the route o.EntryUUID = raw if err := o.validateEntryUUID(formats); err != nil { return err } return nil } // validateEntryUUID carries on validations for parameter EntryUUID func (o *GetLogEntryByUUIDParams) validateEntryUUID(formats strfmt.Registry) error { if err := validate.Pattern("entryUUID", "path", o.EntryUUID, `^([0-9a-fA-F]{64}|[0-9a-fA-F]{80})$`); err != nil { return err } return nil } rekor-1.3.5/pkg/generated/restapi/operations/entries/get_log_entry_by_uuid_responses.go000066400000000000000000000106651455727245600317260ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/runtime" "github.com/sigstore/rekor/pkg/generated/models" ) // GetLogEntryByUUIDOKCode is the HTTP code returned for type GetLogEntryByUUIDOK const GetLogEntryByUUIDOKCode int = 200 /* GetLogEntryByUUIDOK Information needed for a client to compute the inclusion proof swagger:response getLogEntryByUuidOK */ type GetLogEntryByUUIDOK struct { /* In: Body */ Payload models.LogEntry `json:"body,omitempty"` } // NewGetLogEntryByUUIDOK creates GetLogEntryByUUIDOK with default headers values func NewGetLogEntryByUUIDOK() *GetLogEntryByUUIDOK { return &GetLogEntryByUUIDOK{} } // WithPayload adds the payload to the get log entry by Uuid o k response func (o *GetLogEntryByUUIDOK) WithPayload(payload models.LogEntry) *GetLogEntryByUUIDOK { o.Payload = payload return o } // SetPayload sets the payload to the get log entry by Uuid o k response func (o *GetLogEntryByUUIDOK) SetPayload(payload models.LogEntry) { o.Payload = payload } // WriteResponse to the client func (o *GetLogEntryByUUIDOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(200) payload := o.Payload if payload == nil { // return empty map payload = models.LogEntry{} } if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } // GetLogEntryByUUIDNotFoundCode is the HTTP code returned for type GetLogEntryByUUIDNotFound const GetLogEntryByUUIDNotFoundCode int = 404 /* GetLogEntryByUUIDNotFound The content requested could not be found swagger:response getLogEntryByUuidNotFound */ type GetLogEntryByUUIDNotFound struct { } // NewGetLogEntryByUUIDNotFound creates GetLogEntryByUUIDNotFound with default headers values func NewGetLogEntryByUUIDNotFound() *GetLogEntryByUUIDNotFound { return &GetLogEntryByUUIDNotFound{} } // WriteResponse to the client func (o *GetLogEntryByUUIDNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses rw.WriteHeader(404) } /* GetLogEntryByUUIDDefault There was an internal error in the server while processing the request swagger:response getLogEntryByUuidDefault */ type GetLogEntryByUUIDDefault struct { _statusCode int /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewGetLogEntryByUUIDDefault creates GetLogEntryByUUIDDefault with default headers values func NewGetLogEntryByUUIDDefault(code int) *GetLogEntryByUUIDDefault { if code <= 0 { code = 500 } return &GetLogEntryByUUIDDefault{ _statusCode: code, } } // WithStatusCode adds the status to the get log entry by UUID default response func (o *GetLogEntryByUUIDDefault) WithStatusCode(code int) *GetLogEntryByUUIDDefault { o._statusCode = code return o } // SetStatusCode sets the status to the get log entry by UUID default response func (o *GetLogEntryByUUIDDefault) SetStatusCode(code int) { o._statusCode = code } // WithPayload adds the payload to the get log entry by UUID default response func (o *GetLogEntryByUUIDDefault) WithPayload(payload *models.Error) *GetLogEntryByUUIDDefault { o.Payload = payload return o } // SetPayload sets the payload to the get log entry by UUID default response func (o *GetLogEntryByUUIDDefault) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *GetLogEntryByUUIDDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(o._statusCode) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } rekor-1.3.5/pkg/generated/restapi/operations/entries/get_log_entry_by_uuid_urlbuilder.go000066400000000000000000000061751455727245600320570ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "errors" "net/url" golangswaggerpaths "path" "strings" ) // GetLogEntryByUUIDURL generates an URL for the get log entry by UUID operation type GetLogEntryByUUIDURL struct { EntryUUID string _basePath string // avoid unkeyed usage _ struct{} } // WithBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *GetLogEntryByUUIDURL) WithBasePath(bp string) *GetLogEntryByUUIDURL { o.SetBasePath(bp) return o } // SetBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *GetLogEntryByUUIDURL) SetBasePath(bp string) { o._basePath = bp } // Build a url path and query string func (o *GetLogEntryByUUIDURL) Build() (*url.URL, error) { var _result url.URL var _path = "/api/v1/log/entries/{entryUUID}" entryUUID := o.EntryUUID if entryUUID != "" { _path = strings.Replace(_path, "{entryUUID}", entryUUID, -1) } else { return nil, errors.New("entryUuid is required on GetLogEntryByUUIDURL") } _basePath := o._basePath _result.Path = golangswaggerpaths.Join(_basePath, _path) return &_result, nil } // Must is a helper function to panic when the url builder returns an error func (o *GetLogEntryByUUIDURL) Must(u *url.URL, err error) *url.URL { if err != nil { panic(err) } if u == nil { panic("url can't be nil") } return u } // String returns the string representation of the path with query string func (o *GetLogEntryByUUIDURL) String() string { return o.Must(o.Build()).String() } // BuildFull builds a full url with scheme, host, path and query string func (o *GetLogEntryByUUIDURL) BuildFull(scheme, host string) (*url.URL, error) { if scheme == "" { return nil, errors.New("scheme is required for a full url on GetLogEntryByUUIDURL") } if host == "" { return nil, errors.New("host is required for a full url on GetLogEntryByUUIDURL") } base, err := o.Build() if err != nil { return nil, err } base.Scheme = scheme base.Host = host return base, nil } // StringFull returns the string representation of a complete url func (o *GetLogEntryByUUIDURL) StringFull(scheme, host string) string { return o.Must(o.BuildFull(scheme, host)).String() } rekor-1.3.5/pkg/generated/restapi/operations/entries/search_log_query.go000066400000000000000000000044531455727245600265750ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "net/http" "github.com/go-openapi/runtime/middleware" ) // SearchLogQueryHandlerFunc turns a function with the right signature into a search log query handler type SearchLogQueryHandlerFunc func(SearchLogQueryParams) middleware.Responder // Handle executing the request and returning a response func (fn SearchLogQueryHandlerFunc) Handle(params SearchLogQueryParams) middleware.Responder { return fn(params) } // SearchLogQueryHandler interface for that can handle valid search log query params type SearchLogQueryHandler interface { Handle(SearchLogQueryParams) middleware.Responder } // NewSearchLogQuery creates a new http.Handler for the search log query operation func NewSearchLogQuery(ctx *middleware.Context, handler SearchLogQueryHandler) *SearchLogQuery { return &SearchLogQuery{Context: ctx, Handler: handler} } /* SearchLogQuery swagger:route POST /api/v1/log/entries/retrieve entries searchLogQuery Searches transparency log for one or more log entries */ type SearchLogQuery struct { Context *middleware.Context Handler SearchLogQueryHandler } func (o *SearchLogQuery) ServeHTTP(rw http.ResponseWriter, r *http.Request) { route, rCtx, _ := o.Context.RouteInfo(r) if rCtx != nil { *r = *rCtx } var Params = NewSearchLogQueryParams() if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params o.Context.Respond(rw, r, route.Produces, route, err) return } res := o.Handler.Handle(Params) // actually handle the request o.Context.Respond(rw, r, route.Produces, route, res) } rekor-1.3.5/pkg/generated/restapi/operations/entries/search_log_query_parameters.go000066400000000000000000000054301455727245600310140ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "io" "net/http" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/validate" "github.com/sigstore/rekor/pkg/generated/models" ) // NewSearchLogQueryParams creates a new SearchLogQueryParams object // // There are no default values defined in the spec. func NewSearchLogQueryParams() SearchLogQueryParams { return SearchLogQueryParams{} } // SearchLogQueryParams contains all the bound params for the search log query operation // typically these are obtained from a http.Request // // swagger:parameters searchLogQuery type SearchLogQueryParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` /* Required: true In: body */ Entry *models.SearchLogQuery } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls. // // To ensure default values, the struct must have been initialized with NewSearchLogQueryParams() beforehand. func (o *SearchLogQueryParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error o.HTTPRequest = r if runtime.HasBody(r) { defer r.Body.Close() var body models.SearchLogQuery if err := route.Consumer.Consume(r.Body, &body); err != nil { if err == io.EOF { res = append(res, errors.Required("entry", "body", "")) } else { res = append(res, errors.NewParseError("entry", "body", "", err)) } } else { // validate body object if err := body.Validate(route.Formats); err != nil { res = append(res, err) } ctx := validate.WithOperationRequest(r.Context()) if err := body.ContextValidate(ctx, route.Formats); err != nil { res = append(res, err) } if len(res) == 0 { o.Entry = &body } } } else { res = append(res, errors.Required("entry", "body", "")) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } rekor-1.3.5/pkg/generated/restapi/operations/entries/search_log_query_responses.go000066400000000000000000000145041455727245600306740ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/runtime" "github.com/sigstore/rekor/pkg/generated/models" ) // SearchLogQueryOKCode is the HTTP code returned for type SearchLogQueryOK const SearchLogQueryOKCode int = 200 /* SearchLogQueryOK Returns zero or more entries from the transparency log, according to how many were included in request query swagger:response searchLogQueryOK */ type SearchLogQueryOK struct { /* In: Body */ Payload []models.LogEntry `json:"body,omitempty"` } // NewSearchLogQueryOK creates SearchLogQueryOK with default headers values func NewSearchLogQueryOK() *SearchLogQueryOK { return &SearchLogQueryOK{} } // WithPayload adds the payload to the search log query o k response func (o *SearchLogQueryOK) WithPayload(payload []models.LogEntry) *SearchLogQueryOK { o.Payload = payload return o } // SetPayload sets the payload to the search log query o k response func (o *SearchLogQueryOK) SetPayload(payload []models.LogEntry) { o.Payload = payload } // WriteResponse to the client func (o *SearchLogQueryOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(200) payload := o.Payload if payload == nil { // return empty array payload = make([]models.LogEntry, 0, 50) } if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } // SearchLogQueryBadRequestCode is the HTTP code returned for type SearchLogQueryBadRequest const SearchLogQueryBadRequestCode int = 400 /* SearchLogQueryBadRequest The content supplied to the server was invalid swagger:response searchLogQueryBadRequest */ type SearchLogQueryBadRequest struct { /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewSearchLogQueryBadRequest creates SearchLogQueryBadRequest with default headers values func NewSearchLogQueryBadRequest() *SearchLogQueryBadRequest { return &SearchLogQueryBadRequest{} } // WithPayload adds the payload to the search log query bad request response func (o *SearchLogQueryBadRequest) WithPayload(payload *models.Error) *SearchLogQueryBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the search log query bad request response func (o *SearchLogQueryBadRequest) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *SearchLogQueryBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } // SearchLogQueryUnprocessableEntityCode is the HTTP code returned for type SearchLogQueryUnprocessableEntity const SearchLogQueryUnprocessableEntityCode int = 422 /* SearchLogQueryUnprocessableEntity The server understood the request but is unable to process the contained instructions swagger:response searchLogQueryUnprocessableEntity */ type SearchLogQueryUnprocessableEntity struct { /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewSearchLogQueryUnprocessableEntity creates SearchLogQueryUnprocessableEntity with default headers values func NewSearchLogQueryUnprocessableEntity() *SearchLogQueryUnprocessableEntity { return &SearchLogQueryUnprocessableEntity{} } // WithPayload adds the payload to the search log query unprocessable entity response func (o *SearchLogQueryUnprocessableEntity) WithPayload(payload *models.Error) *SearchLogQueryUnprocessableEntity { o.Payload = payload return o } // SetPayload sets the payload to the search log query unprocessable entity response func (o *SearchLogQueryUnprocessableEntity) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *SearchLogQueryUnprocessableEntity) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(422) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } /* SearchLogQueryDefault There was an internal error in the server while processing the request swagger:response searchLogQueryDefault */ type SearchLogQueryDefault struct { _statusCode int /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewSearchLogQueryDefault creates SearchLogQueryDefault with default headers values func NewSearchLogQueryDefault(code int) *SearchLogQueryDefault { if code <= 0 { code = 500 } return &SearchLogQueryDefault{ _statusCode: code, } } // WithStatusCode adds the status to the search log query default response func (o *SearchLogQueryDefault) WithStatusCode(code int) *SearchLogQueryDefault { o._statusCode = code return o } // SetStatusCode sets the status to the search log query default response func (o *SearchLogQueryDefault) SetStatusCode(code int) { o._statusCode = code } // WithPayload adds the payload to the search log query default response func (o *SearchLogQueryDefault) WithPayload(payload *models.Error) *SearchLogQueryDefault { o.Payload = payload return o } // SetPayload sets the payload to the search log query default response func (o *SearchLogQueryDefault) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *SearchLogQueryDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(o._statusCode) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } rekor-1.3.5/pkg/generated/restapi/operations/entries/search_log_query_urlbuilder.go000066400000000000000000000055101455727245600310210ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package entries // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "errors" "net/url" golangswaggerpaths "path" ) // SearchLogQueryURL generates an URL for the search log query operation type SearchLogQueryURL struct { _basePath string } // WithBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *SearchLogQueryURL) WithBasePath(bp string) *SearchLogQueryURL { o.SetBasePath(bp) return o } // SetBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *SearchLogQueryURL) SetBasePath(bp string) { o._basePath = bp } // Build a url path and query string func (o *SearchLogQueryURL) Build() (*url.URL, error) { var _result url.URL var _path = "/api/v1/log/entries/retrieve" _basePath := o._basePath _result.Path = golangswaggerpaths.Join(_basePath, _path) return &_result, nil } // Must is a helper function to panic when the url builder returns an error func (o *SearchLogQueryURL) Must(u *url.URL, err error) *url.URL { if err != nil { panic(err) } if u == nil { panic("url can't be nil") } return u } // String returns the string representation of the path with query string func (o *SearchLogQueryURL) String() string { return o.Must(o.Build()).String() } // BuildFull builds a full url with scheme, host, path and query string func (o *SearchLogQueryURL) BuildFull(scheme, host string) (*url.URL, error) { if scheme == "" { return nil, errors.New("scheme is required for a full url on SearchLogQueryURL") } if host == "" { return nil, errors.New("host is required for a full url on SearchLogQueryURL") } base, err := o.Build() if err != nil { return nil, err } base.Scheme = scheme base.Host = host return base, nil } // StringFull returns the string representation of a complete url func (o *SearchLogQueryURL) StringFull(scheme, host string) string { return o.Must(o.BuildFull(scheme, host)).String() } rekor-1.3.5/pkg/generated/restapi/operations/index/000077500000000000000000000000001455727245600223435ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/restapi/operations/index/search_index.go000066400000000000000000000045631455727245600253360ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package index // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "net/http" "github.com/go-openapi/runtime/middleware" ) // SearchIndexHandlerFunc turns a function with the right signature into a search index handler type SearchIndexHandlerFunc func(SearchIndexParams) middleware.Responder // Handle executing the request and returning a response func (fn SearchIndexHandlerFunc) Handle(params SearchIndexParams) middleware.Responder { return fn(params) } // SearchIndexHandler interface for that can handle valid search index params type SearchIndexHandler interface { Handle(SearchIndexParams) middleware.Responder } // NewSearchIndex creates a new http.Handler for the search index operation func NewSearchIndex(ctx *middleware.Context, handler SearchIndexHandler) *SearchIndex { return &SearchIndex{Context: ctx, Handler: handler} } /* SearchIndex swagger:route POST /api/v1/index/retrieve index searchIndex # Searches index by entry metadata EXPERIMENTAL - this endpoint is offered as best effort only and may be changed or removed in future releases. The results returned from this endpoint may be incomplete. */ type SearchIndex struct { Context *middleware.Context Handler SearchIndexHandler } func (o *SearchIndex) ServeHTTP(rw http.ResponseWriter, r *http.Request) { route, rCtx, _ := o.Context.RouteInfo(r) if rCtx != nil { *r = *rCtx } var Params = NewSearchIndexParams() if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params o.Context.Respond(rw, r, route.Produces, route, err) return } res := o.Handler.Handle(Params) // actually handle the request o.Context.Respond(rw, r, route.Produces, route, res) } rekor-1.3.5/pkg/generated/restapi/operations/index/search_index_parameters.go000066400000000000000000000053561455727245600275620ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package index // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "io" "net/http" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/validate" "github.com/sigstore/rekor/pkg/generated/models" ) // NewSearchIndexParams creates a new SearchIndexParams object // // There are no default values defined in the spec. func NewSearchIndexParams() SearchIndexParams { return SearchIndexParams{} } // SearchIndexParams contains all the bound params for the search index operation // typically these are obtained from a http.Request // // swagger:parameters searchIndex type SearchIndexParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` /* Required: true In: body */ Query *models.SearchIndex } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls. // // To ensure default values, the struct must have been initialized with NewSearchIndexParams() beforehand. func (o *SearchIndexParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error o.HTTPRequest = r if runtime.HasBody(r) { defer r.Body.Close() var body models.SearchIndex if err := route.Consumer.Consume(r.Body, &body); err != nil { if err == io.EOF { res = append(res, errors.Required("query", "body", "")) } else { res = append(res, errors.NewParseError("query", "body", "", err)) } } else { // validate body object if err := body.Validate(route.Formats); err != nil { res = append(res, err) } ctx := validate.WithOperationRequest(r.Context()) if err := body.ContextValidate(ctx, route.Formats); err != nil { res = append(res, err) } if len(res) == 0 { o.Query = &body } } } else { res = append(res, errors.Required("query", "body", "")) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } rekor-1.3.5/pkg/generated/restapi/operations/index/search_index_responses.go000066400000000000000000000112441455727245600274310ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package index // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/runtime" "github.com/sigstore/rekor/pkg/generated/models" ) // SearchIndexOKCode is the HTTP code returned for type SearchIndexOK const SearchIndexOKCode int = 200 /* SearchIndexOK Returns zero or more entry UUIDs from the transparency log based on search query swagger:response searchIndexOK */ type SearchIndexOK struct { /* In: Body */ Payload []string `json:"body,omitempty"` } // NewSearchIndexOK creates SearchIndexOK with default headers values func NewSearchIndexOK() *SearchIndexOK { return &SearchIndexOK{} } // WithPayload adds the payload to the search index o k response func (o *SearchIndexOK) WithPayload(payload []string) *SearchIndexOK { o.Payload = payload return o } // SetPayload sets the payload to the search index o k response func (o *SearchIndexOK) SetPayload(payload []string) { o.Payload = payload } // WriteResponse to the client func (o *SearchIndexOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(200) payload := o.Payload if payload == nil { // return empty array payload = make([]string, 0, 50) } if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } // SearchIndexBadRequestCode is the HTTP code returned for type SearchIndexBadRequest const SearchIndexBadRequestCode int = 400 /* SearchIndexBadRequest The content supplied to the server was invalid swagger:response searchIndexBadRequest */ type SearchIndexBadRequest struct { /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewSearchIndexBadRequest creates SearchIndexBadRequest with default headers values func NewSearchIndexBadRequest() *SearchIndexBadRequest { return &SearchIndexBadRequest{} } // WithPayload adds the payload to the search index bad request response func (o *SearchIndexBadRequest) WithPayload(payload *models.Error) *SearchIndexBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the search index bad request response func (o *SearchIndexBadRequest) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *SearchIndexBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } /* SearchIndexDefault There was an internal error in the server while processing the request swagger:response searchIndexDefault */ type SearchIndexDefault struct { _statusCode int /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewSearchIndexDefault creates SearchIndexDefault with default headers values func NewSearchIndexDefault(code int) *SearchIndexDefault { if code <= 0 { code = 500 } return &SearchIndexDefault{ _statusCode: code, } } // WithStatusCode adds the status to the search index default response func (o *SearchIndexDefault) WithStatusCode(code int) *SearchIndexDefault { o._statusCode = code return o } // SetStatusCode sets the status to the search index default response func (o *SearchIndexDefault) SetStatusCode(code int) { o._statusCode = code } // WithPayload adds the payload to the search index default response func (o *SearchIndexDefault) WithPayload(payload *models.Error) *SearchIndexDefault { o.Payload = payload return o } // SetPayload sets the payload to the search index default response func (o *SearchIndexDefault) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *SearchIndexDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(o._statusCode) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } rekor-1.3.5/pkg/generated/restapi/operations/index/search_index_urlbuilder.go000066400000000000000000000054301455727245600275610ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package index // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "errors" "net/url" golangswaggerpaths "path" ) // SearchIndexURL generates an URL for the search index operation type SearchIndexURL struct { _basePath string } // WithBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *SearchIndexURL) WithBasePath(bp string) *SearchIndexURL { o.SetBasePath(bp) return o } // SetBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *SearchIndexURL) SetBasePath(bp string) { o._basePath = bp } // Build a url path and query string func (o *SearchIndexURL) Build() (*url.URL, error) { var _result url.URL var _path = "/api/v1/index/retrieve" _basePath := o._basePath _result.Path = golangswaggerpaths.Join(_basePath, _path) return &_result, nil } // Must is a helper function to panic when the url builder returns an error func (o *SearchIndexURL) Must(u *url.URL, err error) *url.URL { if err != nil { panic(err) } if u == nil { panic("url can't be nil") } return u } // String returns the string representation of the path with query string func (o *SearchIndexURL) String() string { return o.Must(o.Build()).String() } // BuildFull builds a full url with scheme, host, path and query string func (o *SearchIndexURL) BuildFull(scheme, host string) (*url.URL, error) { if scheme == "" { return nil, errors.New("scheme is required for a full url on SearchIndexURL") } if host == "" { return nil, errors.New("host is required for a full url on SearchIndexURL") } base, err := o.Build() if err != nil { return nil, err } base.Scheme = scheme base.Host = host return base, nil } // StringFull returns the string representation of a complete url func (o *SearchIndexURL) StringFull(scheme, host string) string { return o.Must(o.BuildFull(scheme, host)).String() } rekor-1.3.5/pkg/generated/restapi/operations/pubkey/000077500000000000000000000000001455727245600225335ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/restapi/operations/pubkey/get_public_key.go000066400000000000000000000045251455727245600260550ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pubkey // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "net/http" "github.com/go-openapi/runtime/middleware" ) // GetPublicKeyHandlerFunc turns a function with the right signature into a get public key handler type GetPublicKeyHandlerFunc func(GetPublicKeyParams) middleware.Responder // Handle executing the request and returning a response func (fn GetPublicKeyHandlerFunc) Handle(params GetPublicKeyParams) middleware.Responder { return fn(params) } // GetPublicKeyHandler interface for that can handle valid get public key params type GetPublicKeyHandler interface { Handle(GetPublicKeyParams) middleware.Responder } // NewGetPublicKey creates a new http.Handler for the get public key operation func NewGetPublicKey(ctx *middleware.Context, handler GetPublicKeyHandler) *GetPublicKey { return &GetPublicKey{Context: ctx, Handler: handler} } /* GetPublicKey swagger:route GET /api/v1/log/publicKey pubkey getPublicKey # Retrieve the public key that can be used to validate the signed tree head Returns the public key that can be used to validate the signed tree head */ type GetPublicKey struct { Context *middleware.Context Handler GetPublicKeyHandler } func (o *GetPublicKey) ServeHTTP(rw http.ResponseWriter, r *http.Request) { route, rCtx, _ := o.Context.RouteInfo(r) if rCtx != nil { *r = *rCtx } var Params = NewGetPublicKeyParams() if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params o.Context.Respond(rw, r, route.Produces, route, err) return } res := o.Handler.Handle(Params) // actually handle the request o.Context.Respond(rw, r, route.Produces, route, res) } rekor-1.3.5/pkg/generated/restapi/operations/pubkey/get_public_key_parameters.go000066400000000000000000000057231455727245600303010ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pubkey // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" "github.com/go-openapi/validate" ) // NewGetPublicKeyParams creates a new GetPublicKeyParams object // // There are no default values defined in the spec. func NewGetPublicKeyParams() GetPublicKeyParams { return GetPublicKeyParams{} } // GetPublicKeyParams contains all the bound params for the get public key operation // typically these are obtained from a http.Request // // swagger:parameters getPublicKey type GetPublicKeyParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` /*The tree ID of the tree you wish to get a public key for Pattern: ^[0-9]+$ In: query */ TreeID *string } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls. // // To ensure default values, the struct must have been initialized with NewGetPublicKeyParams() beforehand. func (o *GetPublicKeyParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error o.HTTPRequest = r qs := runtime.Values(r.URL.Query()) qTreeID, qhkTreeID, _ := qs.GetOK("treeID") if err := o.bindTreeID(qTreeID, qhkTreeID, route.Formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // bindTreeID binds and validates parameter TreeID from query. func (o *GetPublicKeyParams) bindTreeID(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string if len(rawData) > 0 { raw = rawData[len(rawData)-1] } // Required: false // AllowEmptyValue: false if raw == "" { // empty values pass all other validations return nil } o.TreeID = &raw if err := o.validateTreeID(formats); err != nil { return err } return nil } // validateTreeID carries on validations for parameter TreeID func (o *GetPublicKeyParams) validateTreeID(formats strfmt.Registry) error { if err := validate.Pattern("treeID", "query", *o.TreeID, `^[0-9]+$`); err != nil { return err } return nil } rekor-1.3.5/pkg/generated/restapi/operations/pubkey/get_public_key_responses.go000066400000000000000000000065621455727245600301610ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pubkey // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/runtime" "github.com/sigstore/rekor/pkg/generated/models" ) // GetPublicKeyOKCode is the HTTP code returned for type GetPublicKeyOK const GetPublicKeyOKCode int = 200 /* GetPublicKeyOK The public key swagger:response getPublicKeyOK */ type GetPublicKeyOK struct { /* In: Body */ Payload string `json:"body,omitempty"` } // NewGetPublicKeyOK creates GetPublicKeyOK with default headers values func NewGetPublicKeyOK() *GetPublicKeyOK { return &GetPublicKeyOK{} } // WithPayload adds the payload to the get public key o k response func (o *GetPublicKeyOK) WithPayload(payload string) *GetPublicKeyOK { o.Payload = payload return o } // SetPayload sets the payload to the get public key o k response func (o *GetPublicKeyOK) SetPayload(payload string) { o.Payload = payload } // WriteResponse to the client func (o *GetPublicKeyOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(200) payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } /* GetPublicKeyDefault There was an internal error in the server while processing the request swagger:response getPublicKeyDefault */ type GetPublicKeyDefault struct { _statusCode int /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewGetPublicKeyDefault creates GetPublicKeyDefault with default headers values func NewGetPublicKeyDefault(code int) *GetPublicKeyDefault { if code <= 0 { code = 500 } return &GetPublicKeyDefault{ _statusCode: code, } } // WithStatusCode adds the status to the get public key default response func (o *GetPublicKeyDefault) WithStatusCode(code int) *GetPublicKeyDefault { o._statusCode = code return o } // SetStatusCode sets the status to the get public key default response func (o *GetPublicKeyDefault) SetStatusCode(code int) { o._statusCode = code } // WithPayload adds the payload to the get public key default response func (o *GetPublicKeyDefault) WithPayload(payload *models.Error) *GetPublicKeyDefault { o.Payload = payload return o } // SetPayload sets the payload to the get public key default response func (o *GetPublicKeyDefault) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *GetPublicKeyDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(o._statusCode) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } rekor-1.3.5/pkg/generated/restapi/operations/pubkey/get_public_key_urlbuilder.go000066400000000000000000000060141455727245600303010ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package pubkey // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "errors" "net/url" golangswaggerpaths "path" ) // GetPublicKeyURL generates an URL for the get public key operation type GetPublicKeyURL struct { TreeID *string _basePath string // avoid unkeyed usage _ struct{} } // WithBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *GetPublicKeyURL) WithBasePath(bp string) *GetPublicKeyURL { o.SetBasePath(bp) return o } // SetBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *GetPublicKeyURL) SetBasePath(bp string) { o._basePath = bp } // Build a url path and query string func (o *GetPublicKeyURL) Build() (*url.URL, error) { var _result url.URL var _path = "/api/v1/log/publicKey" _basePath := o._basePath _result.Path = golangswaggerpaths.Join(_basePath, _path) qs := make(url.Values) var treeIDQ string if o.TreeID != nil { treeIDQ = *o.TreeID } if treeIDQ != "" { qs.Set("treeID", treeIDQ) } _result.RawQuery = qs.Encode() return &_result, nil } // Must is a helper function to panic when the url builder returns an error func (o *GetPublicKeyURL) Must(u *url.URL, err error) *url.URL { if err != nil { panic(err) } if u == nil { panic("url can't be nil") } return u } // String returns the string representation of the path with query string func (o *GetPublicKeyURL) String() string { return o.Must(o.Build()).String() } // BuildFull builds a full url with scheme, host, path and query string func (o *GetPublicKeyURL) BuildFull(scheme, host string) (*url.URL, error) { if scheme == "" { return nil, errors.New("scheme is required for a full url on GetPublicKeyURL") } if host == "" { return nil, errors.New("host is required for a full url on GetPublicKeyURL") } base, err := o.Build() if err != nil { return nil, err } base.Scheme = scheme base.Host = host return base, nil } // StringFull returns the string representation of a complete url func (o *GetPublicKeyURL) StringFull(scheme, host string) string { return o.Must(o.BuildFull(scheme, host)).String() } rekor-1.3.5/pkg/generated/restapi/operations/rekor_server_api.go000066400000000000000000000373671455727245600251440ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package operations // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "fmt" "io" "net/http" "strings" "github.com/go-openapi/errors" "github.com/go-openapi/loads" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/runtime/security" "github.com/go-openapi/spec" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/restapi/operations/entries" "github.com/sigstore/rekor/pkg/generated/restapi/operations/index" "github.com/sigstore/rekor/pkg/generated/restapi/operations/pubkey" "github.com/sigstore/rekor/pkg/generated/restapi/operations/tlog" ) // NewRekorServerAPI creates a new RekorServer instance func NewRekorServerAPI(spec *loads.Document) *RekorServerAPI { return &RekorServerAPI{ handlers: make(map[string]map[string]http.Handler), formats: strfmt.Default, defaultConsumes: "application/json", defaultProduces: "application/json", customConsumers: make(map[string]runtime.Consumer), customProducers: make(map[string]runtime.Producer), PreServerShutdown: func() {}, ServerShutdown: func() {}, spec: spec, useSwaggerUI: false, ServeError: errors.ServeError, BasicAuthenticator: security.BasicAuth, APIKeyAuthenticator: security.APIKeyAuth, BearerAuthenticator: security.BearerAuth, JSONConsumer: runtime.JSONConsumer(), ApplicationXPemFileProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error { return errors.NotImplemented("applicationXPemFile producer has not yet been implemented") }), JSONProducer: runtime.JSONProducer(), EntriesCreateLogEntryHandler: entries.CreateLogEntryHandlerFunc(func(params entries.CreateLogEntryParams) middleware.Responder { return middleware.NotImplemented("operation entries.CreateLogEntry has not yet been implemented") }), EntriesGetLogEntryByIndexHandler: entries.GetLogEntryByIndexHandlerFunc(func(params entries.GetLogEntryByIndexParams) middleware.Responder { return middleware.NotImplemented("operation entries.GetLogEntryByIndex has not yet been implemented") }), EntriesGetLogEntryByUUIDHandler: entries.GetLogEntryByUUIDHandlerFunc(func(params entries.GetLogEntryByUUIDParams) middleware.Responder { return middleware.NotImplemented("operation entries.GetLogEntryByUUID has not yet been implemented") }), TlogGetLogInfoHandler: tlog.GetLogInfoHandlerFunc(func(params tlog.GetLogInfoParams) middleware.Responder { return middleware.NotImplemented("operation tlog.GetLogInfo has not yet been implemented") }), TlogGetLogProofHandler: tlog.GetLogProofHandlerFunc(func(params tlog.GetLogProofParams) middleware.Responder { return middleware.NotImplemented("operation tlog.GetLogProof has not yet been implemented") }), PubkeyGetPublicKeyHandler: pubkey.GetPublicKeyHandlerFunc(func(params pubkey.GetPublicKeyParams) middleware.Responder { return middleware.NotImplemented("operation pubkey.GetPublicKey has not yet been implemented") }), IndexSearchIndexHandler: index.SearchIndexHandlerFunc(func(params index.SearchIndexParams) middleware.Responder { return middleware.NotImplemented("operation index.SearchIndex has not yet been implemented") }), EntriesSearchLogQueryHandler: entries.SearchLogQueryHandlerFunc(func(params entries.SearchLogQueryParams) middleware.Responder { return middleware.NotImplemented("operation entries.SearchLogQuery has not yet been implemented") }), } } /*RekorServerAPI Rekor is a cryptographically secure, immutable transparency log for signed software releases. */ type RekorServerAPI struct { spec *loads.Document context *middleware.Context handlers map[string]map[string]http.Handler formats strfmt.Registry customConsumers map[string]runtime.Consumer customProducers map[string]runtime.Producer defaultConsumes string defaultProduces string Middleware func(middleware.Builder) http.Handler useSwaggerUI bool // BasicAuthenticator generates a runtime.Authenticator from the supplied basic auth function. // It has a default implementation in the security package, however you can replace it for your particular usage. BasicAuthenticator func(security.UserPassAuthentication) runtime.Authenticator // APIKeyAuthenticator generates a runtime.Authenticator from the supplied token auth function. // It has a default implementation in the security package, however you can replace it for your particular usage. APIKeyAuthenticator func(string, string, security.TokenAuthentication) runtime.Authenticator // BearerAuthenticator generates a runtime.Authenticator from the supplied bearer token auth function. // It has a default implementation in the security package, however you can replace it for your particular usage. BearerAuthenticator func(string, security.ScopedTokenAuthentication) runtime.Authenticator // JSONConsumer registers a consumer for the following mime types: // - application/json JSONConsumer runtime.Consumer // ApplicationXPemFileProducer registers a producer for the following mime types: // - application/x-pem-file ApplicationXPemFileProducer runtime.Producer // JSONProducer registers a producer for the following mime types: // - application/json JSONProducer runtime.Producer // EntriesCreateLogEntryHandler sets the operation handler for the create log entry operation EntriesCreateLogEntryHandler entries.CreateLogEntryHandler // EntriesGetLogEntryByIndexHandler sets the operation handler for the get log entry by index operation EntriesGetLogEntryByIndexHandler entries.GetLogEntryByIndexHandler // EntriesGetLogEntryByUUIDHandler sets the operation handler for the get log entry by UUID operation EntriesGetLogEntryByUUIDHandler entries.GetLogEntryByUUIDHandler // TlogGetLogInfoHandler sets the operation handler for the get log info operation TlogGetLogInfoHandler tlog.GetLogInfoHandler // TlogGetLogProofHandler sets the operation handler for the get log proof operation TlogGetLogProofHandler tlog.GetLogProofHandler // PubkeyGetPublicKeyHandler sets the operation handler for the get public key operation PubkeyGetPublicKeyHandler pubkey.GetPublicKeyHandler // IndexSearchIndexHandler sets the operation handler for the search index operation IndexSearchIndexHandler index.SearchIndexHandler // EntriesSearchLogQueryHandler sets the operation handler for the search log query operation EntriesSearchLogQueryHandler entries.SearchLogQueryHandler // ServeError is called when an error is received, there is a default handler // but you can set your own with this ServeError func(http.ResponseWriter, *http.Request, error) // PreServerShutdown is called before the HTTP(S) server is shutdown // This allows for custom functions to get executed before the HTTP(S) server stops accepting traffic PreServerShutdown func() // ServerShutdown is called when the HTTP(S) server is shut down and done // handling all active connections and does not accept connections any more ServerShutdown func() // Custom command line argument groups with their descriptions CommandLineOptionsGroups []swag.CommandLineOptionsGroup // User defined logger function. Logger func(string, ...interface{}) } // UseRedoc for documentation at /docs func (o *RekorServerAPI) UseRedoc() { o.useSwaggerUI = false } // UseSwaggerUI for documentation at /docs func (o *RekorServerAPI) UseSwaggerUI() { o.useSwaggerUI = true } // SetDefaultProduces sets the default produces media type func (o *RekorServerAPI) SetDefaultProduces(mediaType string) { o.defaultProduces = mediaType } // SetDefaultConsumes returns the default consumes media type func (o *RekorServerAPI) SetDefaultConsumes(mediaType string) { o.defaultConsumes = mediaType } // SetSpec sets a spec that will be served for the clients. func (o *RekorServerAPI) SetSpec(spec *loads.Document) { o.spec = spec } // DefaultProduces returns the default produces media type func (o *RekorServerAPI) DefaultProduces() string { return o.defaultProduces } // DefaultConsumes returns the default consumes media type func (o *RekorServerAPI) DefaultConsumes() string { return o.defaultConsumes } // Formats returns the registered string formats func (o *RekorServerAPI) Formats() strfmt.Registry { return o.formats } // RegisterFormat registers a custom format validator func (o *RekorServerAPI) RegisterFormat(name string, format strfmt.Format, validator strfmt.Validator) { o.formats.Add(name, format, validator) } // Validate validates the registrations in the RekorServerAPI func (o *RekorServerAPI) Validate() error { var unregistered []string if o.JSONConsumer == nil { unregistered = append(unregistered, "JSONConsumer") } if o.ApplicationXPemFileProducer == nil { unregistered = append(unregistered, "ApplicationXPemFileProducer") } if o.JSONProducer == nil { unregistered = append(unregistered, "JSONProducer") } if o.EntriesCreateLogEntryHandler == nil { unregistered = append(unregistered, "entries.CreateLogEntryHandler") } if o.EntriesGetLogEntryByIndexHandler == nil { unregistered = append(unregistered, "entries.GetLogEntryByIndexHandler") } if o.EntriesGetLogEntryByUUIDHandler == nil { unregistered = append(unregistered, "entries.GetLogEntryByUUIDHandler") } if o.TlogGetLogInfoHandler == nil { unregistered = append(unregistered, "tlog.GetLogInfoHandler") } if o.TlogGetLogProofHandler == nil { unregistered = append(unregistered, "tlog.GetLogProofHandler") } if o.PubkeyGetPublicKeyHandler == nil { unregistered = append(unregistered, "pubkey.GetPublicKeyHandler") } if o.IndexSearchIndexHandler == nil { unregistered = append(unregistered, "index.SearchIndexHandler") } if o.EntriesSearchLogQueryHandler == nil { unregistered = append(unregistered, "entries.SearchLogQueryHandler") } if len(unregistered) > 0 { return fmt.Errorf("missing registration: %s", strings.Join(unregistered, ", ")) } return nil } // ServeErrorFor gets a error handler for a given operation id func (o *RekorServerAPI) ServeErrorFor(operationID string) func(http.ResponseWriter, *http.Request, error) { return o.ServeError } // AuthenticatorsFor gets the authenticators for the specified security schemes func (o *RekorServerAPI) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator { return nil } // Authorizer returns the registered authorizer func (o *RekorServerAPI) Authorizer() runtime.Authorizer { return nil } // ConsumersFor gets the consumers for the specified media types. // MIME type parameters are ignored here. func (o *RekorServerAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer { result := make(map[string]runtime.Consumer, len(mediaTypes)) for _, mt := range mediaTypes { switch mt { case "application/json": result["application/json"] = o.JSONConsumer } if c, ok := o.customConsumers[mt]; ok { result[mt] = c } } return result } // ProducersFor gets the producers for the specified media types. // MIME type parameters are ignored here. func (o *RekorServerAPI) ProducersFor(mediaTypes []string) map[string]runtime.Producer { result := make(map[string]runtime.Producer, len(mediaTypes)) for _, mt := range mediaTypes { switch mt { case "application/x-pem-file": result["application/x-pem-file"] = o.ApplicationXPemFileProducer case "application/json": result["application/json"] = o.JSONProducer } if p, ok := o.customProducers[mt]; ok { result[mt] = p } } return result } // HandlerFor gets a http.Handler for the provided operation method and path func (o *RekorServerAPI) HandlerFor(method, path string) (http.Handler, bool) { if o.handlers == nil { return nil, false } um := strings.ToUpper(method) if _, ok := o.handlers[um]; !ok { return nil, false } if path == "/" { path = "" } h, ok := o.handlers[um][path] return h, ok } // Context returns the middleware context for the rekor server API func (o *RekorServerAPI) Context() *middleware.Context { if o.context == nil { o.context = middleware.NewRoutableContext(o.spec, o, nil) } return o.context } func (o *RekorServerAPI) initHandlerCache() { o.Context() // don't care about the result, just that the initialization happened if o.handlers == nil { o.handlers = make(map[string]map[string]http.Handler) } if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) } o.handlers["POST"]["/api/v1/log/entries"] = entries.NewCreateLogEntry(o.context, o.EntriesCreateLogEntryHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } o.handlers["GET"]["/api/v1/log/entries"] = entries.NewGetLogEntryByIndex(o.context, o.EntriesGetLogEntryByIndexHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } o.handlers["GET"]["/api/v1/log/entries/{entryUUID}"] = entries.NewGetLogEntryByUUID(o.context, o.EntriesGetLogEntryByUUIDHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } o.handlers["GET"]["/api/v1/log"] = tlog.NewGetLogInfo(o.context, o.TlogGetLogInfoHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } o.handlers["GET"]["/api/v1/log/proof"] = tlog.NewGetLogProof(o.context, o.TlogGetLogProofHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } o.handlers["GET"]["/api/v1/log/publicKey"] = pubkey.NewGetPublicKey(o.context, o.PubkeyGetPublicKeyHandler) if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) } o.handlers["POST"]["/api/v1/index/retrieve"] = index.NewSearchIndex(o.context, o.IndexSearchIndexHandler) if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) } o.handlers["POST"]["/api/v1/log/entries/retrieve"] = entries.NewSearchLogQuery(o.context, o.EntriesSearchLogQueryHandler) } // Serve creates a http handler to serve the API over HTTP // can be used directly in http.ListenAndServe(":8000", api.Serve(nil)) func (o *RekorServerAPI) Serve(builder middleware.Builder) http.Handler { o.Init() if o.Middleware != nil { return o.Middleware(builder) } if o.useSwaggerUI { return o.context.APIHandlerSwaggerUI(builder) } return o.context.APIHandler(builder) } // Init allows you to just initialize the handler cache, you can then recompose the middleware as you see fit func (o *RekorServerAPI) Init() { if len(o.handlers) == 0 { o.initHandlerCache() } } // RegisterConsumer allows you to add (or override) a consumer for a media type. func (o *RekorServerAPI) RegisterConsumer(mediaType string, consumer runtime.Consumer) { o.customConsumers[mediaType] = consumer } // RegisterProducer allows you to add (or override) a producer for a media type. func (o *RekorServerAPI) RegisterProducer(mediaType string, producer runtime.Producer) { o.customProducers[mediaType] = producer } // AddMiddlewareFor adds a http middleware to existing handler func (o *RekorServerAPI) AddMiddlewareFor(method, path string, builder middleware.Builder) { um := strings.ToUpper(method) if path == "/" { path = "" } o.Init() if h, ok := o.handlers[um][path]; ok { o.handlers[um][path] = builder(h) } } rekor-1.3.5/pkg/generated/restapi/operations/tlog/000077500000000000000000000000001455727245600222015ustar00rootroot00000000000000rekor-1.3.5/pkg/generated/restapi/operations/tlog/get_log_info.go000066400000000000000000000044411455727245600251660ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "net/http" "github.com/go-openapi/runtime/middleware" ) // GetLogInfoHandlerFunc turns a function with the right signature into a get log info handler type GetLogInfoHandlerFunc func(GetLogInfoParams) middleware.Responder // Handle executing the request and returning a response func (fn GetLogInfoHandlerFunc) Handle(params GetLogInfoParams) middleware.Responder { return fn(params) } // GetLogInfoHandler interface for that can handle valid get log info params type GetLogInfoHandler interface { Handle(GetLogInfoParams) middleware.Responder } // NewGetLogInfo creates a new http.Handler for the get log info operation func NewGetLogInfo(ctx *middleware.Context, handler GetLogInfoHandler) *GetLogInfo { return &GetLogInfo{Context: ctx, Handler: handler} } /* GetLogInfo swagger:route GET /api/v1/log tlog getLogInfo # Get information about the current state of the transparency log Returns the current root hash and size of the merkle tree used to store the log entries. */ type GetLogInfo struct { Context *middleware.Context Handler GetLogInfoHandler } func (o *GetLogInfo) ServeHTTP(rw http.ResponseWriter, r *http.Request) { route, rCtx, _ := o.Context.RouteInfo(r) if rCtx != nil { *r = *rCtx } var Params = NewGetLogInfoParams() if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params o.Context.Respond(rw, r, route.Produces, route, err) return } res := o.Handler.Handle(Params) // actually handle the request o.Context.Respond(rw, r, route.Produces, route, res) } rekor-1.3.5/pkg/generated/restapi/operations/tlog/get_log_info_parameters.go000066400000000000000000000056321455727245600274140ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" ) // NewGetLogInfoParams creates a new GetLogInfoParams object // with the default values initialized. func NewGetLogInfoParams() GetLogInfoParams { var ( // initialize parameters with default values stableDefault = bool(false) ) return GetLogInfoParams{ Stable: &stableDefault, } } // GetLogInfoParams contains all the bound params for the get log info operation // typically these are obtained from a http.Request // // swagger:parameters getLogInfo type GetLogInfoParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` /*Whether to return a stable checkpoint for the active shard In: query Default: false */ Stable *bool } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls. // // To ensure default values, the struct must have been initialized with NewGetLogInfoParams() beforehand. func (o *GetLogInfoParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error o.HTTPRequest = r qs := runtime.Values(r.URL.Query()) qStable, qhkStable, _ := qs.GetOK("stable") if err := o.bindStable(qStable, qhkStable, route.Formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // bindStable binds and validates parameter Stable from query. func (o *GetLogInfoParams) bindStable(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string if len(rawData) > 0 { raw = rawData[len(rawData)-1] } // Required: false // AllowEmptyValue: false if raw == "" { // empty values pass all other validations // Default values have been previously initialized by NewGetLogInfoParams() return nil } value, err := swag.ConvertBool(raw) if err != nil { return errors.InvalidType("stable", "query", "bool", raw) } o.Stable = &value return nil } rekor-1.3.5/pkg/generated/restapi/operations/tlog/get_log_info_responses.go000066400000000000000000000066171455727245600272760ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/runtime" "github.com/sigstore/rekor/pkg/generated/models" ) // GetLogInfoOKCode is the HTTP code returned for type GetLogInfoOK const GetLogInfoOKCode int = 200 /* GetLogInfoOK A JSON object with the root hash and tree size as properties swagger:response getLogInfoOK */ type GetLogInfoOK struct { /* In: Body */ Payload *models.LogInfo `json:"body,omitempty"` } // NewGetLogInfoOK creates GetLogInfoOK with default headers values func NewGetLogInfoOK() *GetLogInfoOK { return &GetLogInfoOK{} } // WithPayload adds the payload to the get log info o k response func (o *GetLogInfoOK) WithPayload(payload *models.LogInfo) *GetLogInfoOK { o.Payload = payload return o } // SetPayload sets the payload to the get log info o k response func (o *GetLogInfoOK) SetPayload(payload *models.LogInfo) { o.Payload = payload } // WriteResponse to the client func (o *GetLogInfoOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(200) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } /* GetLogInfoDefault There was an internal error in the server while processing the request swagger:response getLogInfoDefault */ type GetLogInfoDefault struct { _statusCode int /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewGetLogInfoDefault creates GetLogInfoDefault with default headers values func NewGetLogInfoDefault(code int) *GetLogInfoDefault { if code <= 0 { code = 500 } return &GetLogInfoDefault{ _statusCode: code, } } // WithStatusCode adds the status to the get log info default response func (o *GetLogInfoDefault) WithStatusCode(code int) *GetLogInfoDefault { o._statusCode = code return o } // SetStatusCode sets the status to the get log info default response func (o *GetLogInfoDefault) SetStatusCode(code int) { o._statusCode = code } // WithPayload adds the payload to the get log info default response func (o *GetLogInfoDefault) WithPayload(payload *models.Error) *GetLogInfoDefault { o.Payload = payload return o } // SetPayload sets the payload to the get log info default response func (o *GetLogInfoDefault) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *GetLogInfoDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(o._statusCode) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } rekor-1.3.5/pkg/generated/restapi/operations/tlog/get_log_info_urlbuilder.go000066400000000000000000000060241455727245600274160ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "errors" "net/url" golangswaggerpaths "path" "github.com/go-openapi/swag" ) // GetLogInfoURL generates an URL for the get log info operation type GetLogInfoURL struct { Stable *bool _basePath string // avoid unkeyed usage _ struct{} } // WithBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *GetLogInfoURL) WithBasePath(bp string) *GetLogInfoURL { o.SetBasePath(bp) return o } // SetBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *GetLogInfoURL) SetBasePath(bp string) { o._basePath = bp } // Build a url path and query string func (o *GetLogInfoURL) Build() (*url.URL, error) { var _result url.URL var _path = "/api/v1/log" _basePath := o._basePath _result.Path = golangswaggerpaths.Join(_basePath, _path) qs := make(url.Values) var stableQ string if o.Stable != nil { stableQ = swag.FormatBool(*o.Stable) } if stableQ != "" { qs.Set("stable", stableQ) } _result.RawQuery = qs.Encode() return &_result, nil } // Must is a helper function to panic when the url builder returns an error func (o *GetLogInfoURL) Must(u *url.URL, err error) *url.URL { if err != nil { panic(err) } if u == nil { panic("url can't be nil") } return u } // String returns the string representation of the path with query string func (o *GetLogInfoURL) String() string { return o.Must(o.Build()).String() } // BuildFull builds a full url with scheme, host, path and query string func (o *GetLogInfoURL) BuildFull(scheme, host string) (*url.URL, error) { if scheme == "" { return nil, errors.New("scheme is required for a full url on GetLogInfoURL") } if host == "" { return nil, errors.New("host is required for a full url on GetLogInfoURL") } base, err := o.Build() if err != nil { return nil, err } base.Scheme = scheme base.Host = host return base, nil } // StringFull returns the string representation of a complete url func (o *GetLogInfoURL) StringFull(scheme, host string) string { return o.Must(o.BuildFull(scheme, host)).String() } rekor-1.3.5/pkg/generated/restapi/operations/tlog/get_log_proof.go000066400000000000000000000045541455727245600253650ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "net/http" "github.com/go-openapi/runtime/middleware" ) // GetLogProofHandlerFunc turns a function with the right signature into a get log proof handler type GetLogProofHandlerFunc func(GetLogProofParams) middleware.Responder // Handle executing the request and returning a response func (fn GetLogProofHandlerFunc) Handle(params GetLogProofParams) middleware.Responder { return fn(params) } // GetLogProofHandler interface for that can handle valid get log proof params type GetLogProofHandler interface { Handle(GetLogProofParams) middleware.Responder } // NewGetLogProof creates a new http.Handler for the get log proof operation func NewGetLogProof(ctx *middleware.Context, handler GetLogProofHandler) *GetLogProof { return &GetLogProof{Context: ctx, Handler: handler} } /* GetLogProof swagger:route GET /api/v1/log/proof tlog getLogProof # Get information required to generate a consistency proof for the transparency log Returns a list of hashes for specified tree sizes that can be used to confirm the consistency of the transparency log */ type GetLogProof struct { Context *middleware.Context Handler GetLogProofHandler } func (o *GetLogProof) ServeHTTP(rw http.ResponseWriter, r *http.Request) { route, rCtx, _ := o.Context.RouteInfo(r) if rCtx != nil { *r = *rCtx } var Params = NewGetLogProofParams() if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params o.Context.Respond(rw, r, route.Produces, route, err) return } res := o.Handler.Handle(Params) // actually handle the request o.Context.Respond(rw, r, route.Produces, route, res) } rekor-1.3.5/pkg/generated/restapi/operations/tlog/get_log_proof_parameters.go000066400000000000000000000131121455727245600275760ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" ) // NewGetLogProofParams creates a new GetLogProofParams object // with the default values initialized. func NewGetLogProofParams() GetLogProofParams { var ( // initialize parameters with default values firstSizeDefault = int64(1) ) return GetLogProofParams{ FirstSize: &firstSizeDefault, } } // GetLogProofParams contains all the bound params for the get log proof operation // typically these are obtained from a http.Request // // swagger:parameters getLogProof type GetLogProofParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` /*The size of the tree that you wish to prove consistency from (1 means the beginning of the log) Defaults to 1 if not specified Minimum: 1 In: query Default: 1 */ FirstSize *int64 /*The size of the tree that you wish to prove consistency to Required: true Minimum: 1 In: query */ LastSize int64 /*The tree ID of the tree that you wish to prove consistency for Pattern: ^[0-9]+$ In: query */ TreeID *string } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls. // // To ensure default values, the struct must have been initialized with NewGetLogProofParams() beforehand. func (o *GetLogProofParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error o.HTTPRequest = r qs := runtime.Values(r.URL.Query()) qFirstSize, qhkFirstSize, _ := qs.GetOK("firstSize") if err := o.bindFirstSize(qFirstSize, qhkFirstSize, route.Formats); err != nil { res = append(res, err) } qLastSize, qhkLastSize, _ := qs.GetOK("lastSize") if err := o.bindLastSize(qLastSize, qhkLastSize, route.Formats); err != nil { res = append(res, err) } qTreeID, qhkTreeID, _ := qs.GetOK("treeID") if err := o.bindTreeID(qTreeID, qhkTreeID, route.Formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } // bindFirstSize binds and validates parameter FirstSize from query. func (o *GetLogProofParams) bindFirstSize(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string if len(rawData) > 0 { raw = rawData[len(rawData)-1] } // Required: false // AllowEmptyValue: false if raw == "" { // empty values pass all other validations // Default values have been previously initialized by NewGetLogProofParams() return nil } value, err := swag.ConvertInt64(raw) if err != nil { return errors.InvalidType("firstSize", "query", "int64", raw) } o.FirstSize = &value if err := o.validateFirstSize(formats); err != nil { return err } return nil } // validateFirstSize carries on validations for parameter FirstSize func (o *GetLogProofParams) validateFirstSize(formats strfmt.Registry) error { if err := validate.MinimumInt("firstSize", "query", *o.FirstSize, 1, false); err != nil { return err } return nil } // bindLastSize binds and validates parameter LastSize from query. func (o *GetLogProofParams) bindLastSize(rawData []string, hasKey bool, formats strfmt.Registry) error { if !hasKey { return errors.Required("lastSize", "query", rawData) } var raw string if len(rawData) > 0 { raw = rawData[len(rawData)-1] } // Required: true // AllowEmptyValue: false if err := validate.RequiredString("lastSize", "query", raw); err != nil { return err } value, err := swag.ConvertInt64(raw) if err != nil { return errors.InvalidType("lastSize", "query", "int64", raw) } o.LastSize = value if err := o.validateLastSize(formats); err != nil { return err } return nil } // validateLastSize carries on validations for parameter LastSize func (o *GetLogProofParams) validateLastSize(formats strfmt.Registry) error { if err := validate.MinimumInt("lastSize", "query", o.LastSize, 1, false); err != nil { return err } return nil } // bindTreeID binds and validates parameter TreeID from query. func (o *GetLogProofParams) bindTreeID(rawData []string, hasKey bool, formats strfmt.Registry) error { var raw string if len(rawData) > 0 { raw = rawData[len(rawData)-1] } // Required: false // AllowEmptyValue: false if raw == "" { // empty values pass all other validations return nil } o.TreeID = &raw if err := o.validateTreeID(formats); err != nil { return err } return nil } // validateTreeID carries on validations for parameter TreeID func (o *GetLogProofParams) validateTreeID(formats strfmt.Registry) error { if err := validate.Pattern("treeID", "query", *o.TreeID, `^[0-9]+$`); err != nil { return err } return nil } rekor-1.3.5/pkg/generated/restapi/operations/tlog/get_log_proof_responses.go000066400000000000000000000112121455727245600274530ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the swagger generate command import ( "net/http" "github.com/go-openapi/runtime" "github.com/sigstore/rekor/pkg/generated/models" ) // GetLogProofOKCode is the HTTP code returned for type GetLogProofOK const GetLogProofOKCode int = 200 /* GetLogProofOK All hashes required to compute the consistency proof swagger:response getLogProofOK */ type GetLogProofOK struct { /* In: Body */ Payload *models.ConsistencyProof `json:"body,omitempty"` } // NewGetLogProofOK creates GetLogProofOK with default headers values func NewGetLogProofOK() *GetLogProofOK { return &GetLogProofOK{} } // WithPayload adds the payload to the get log proof o k response func (o *GetLogProofOK) WithPayload(payload *models.ConsistencyProof) *GetLogProofOK { o.Payload = payload return o } // SetPayload sets the payload to the get log proof o k response func (o *GetLogProofOK) SetPayload(payload *models.ConsistencyProof) { o.Payload = payload } // WriteResponse to the client func (o *GetLogProofOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(200) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } // GetLogProofBadRequestCode is the HTTP code returned for type GetLogProofBadRequest const GetLogProofBadRequestCode int = 400 /* GetLogProofBadRequest The content supplied to the server was invalid swagger:response getLogProofBadRequest */ type GetLogProofBadRequest struct { /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewGetLogProofBadRequest creates GetLogProofBadRequest with default headers values func NewGetLogProofBadRequest() *GetLogProofBadRequest { return &GetLogProofBadRequest{} } // WithPayload adds the payload to the get log proof bad request response func (o *GetLogProofBadRequest) WithPayload(payload *models.Error) *GetLogProofBadRequest { o.Payload = payload return o } // SetPayload sets the payload to the get log proof bad request response func (o *GetLogProofBadRequest) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *GetLogProofBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(400) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } /* GetLogProofDefault There was an internal error in the server while processing the request swagger:response getLogProofDefault */ type GetLogProofDefault struct { _statusCode int /* In: Body */ Payload *models.Error `json:"body,omitempty"` } // NewGetLogProofDefault creates GetLogProofDefault with default headers values func NewGetLogProofDefault(code int) *GetLogProofDefault { if code <= 0 { code = 500 } return &GetLogProofDefault{ _statusCode: code, } } // WithStatusCode adds the status to the get log proof default response func (o *GetLogProofDefault) WithStatusCode(code int) *GetLogProofDefault { o._statusCode = code return o } // SetStatusCode sets the status to the get log proof default response func (o *GetLogProofDefault) SetStatusCode(code int) { o._statusCode = code } // WithPayload adds the payload to the get log proof default response func (o *GetLogProofDefault) WithPayload(payload *models.Error) *GetLogProofDefault { o.Payload = payload return o } // SetPayload sets the payload to the get log proof default response func (o *GetLogProofDefault) SetPayload(payload *models.Error) { o.Payload = payload } // WriteResponse to the client func (o *GetLogProofDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { rw.WriteHeader(o._statusCode) if o.Payload != nil { payload := o.Payload if err := producer.Produce(rw, payload); err != nil { panic(err) // let the recovery middleware deal with this } } } rekor-1.3.5/pkg/generated/restapi/operations/tlog/get_log_proof_urlbuilder.go000066400000000000000000000065011455727245600276100ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package tlog // This file was generated by the swagger tool. // Editing this file might prove futile when you re-run the generate command import ( "errors" "net/url" golangswaggerpaths "path" "github.com/go-openapi/swag" ) // GetLogProofURL generates an URL for the get log proof operation type GetLogProofURL struct { FirstSize *int64 LastSize int64 TreeID *string _basePath string // avoid unkeyed usage _ struct{} } // WithBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *GetLogProofURL) WithBasePath(bp string) *GetLogProofURL { o.SetBasePath(bp) return o } // SetBasePath sets the base path for this url builder, only required when it's different from the // base path specified in the swagger spec. // When the value of the base path is an empty string func (o *GetLogProofURL) SetBasePath(bp string) { o._basePath = bp } // Build a url path and query string func (o *GetLogProofURL) Build() (*url.URL, error) { var _result url.URL var _path = "/api/v1/log/proof" _basePath := o._basePath _result.Path = golangswaggerpaths.Join(_basePath, _path) qs := make(url.Values) var firstSizeQ string if o.FirstSize != nil { firstSizeQ = swag.FormatInt64(*o.FirstSize) } if firstSizeQ != "" { qs.Set("firstSize", firstSizeQ) } lastSizeQ := swag.FormatInt64(o.LastSize) if lastSizeQ != "" { qs.Set("lastSize", lastSizeQ) } var treeIDQ string if o.TreeID != nil { treeIDQ = *o.TreeID } if treeIDQ != "" { qs.Set("treeID", treeIDQ) } _result.RawQuery = qs.Encode() return &_result, nil } // Must is a helper function to panic when the url builder returns an error func (o *GetLogProofURL) Must(u *url.URL, err error) *url.URL { if err != nil { panic(err) } if u == nil { panic("url can't be nil") } return u } // String returns the string representation of the path with query string func (o *GetLogProofURL) String() string { return o.Must(o.Build()).String() } // BuildFull builds a full url with scheme, host, path and query string func (o *GetLogProofURL) BuildFull(scheme, host string) (*url.URL, error) { if scheme == "" { return nil, errors.New("scheme is required for a full url on GetLogProofURL") } if host == "" { return nil, errors.New("host is required for a full url on GetLogProofURL") } base, err := o.Build() if err != nil { return nil, err } base.Scheme = scheme base.Host = host return base, nil } // StringFull returns the string representation of a complete url func (o *GetLogProofURL) StringFull(scheme, host string) string { return o.Must(o.BuildFull(scheme, host)).String() } rekor-1.3.5/pkg/generated/restapi/rekorHomePage.html000066400000000000000000000030221455727245600224640ustar00rootroot00000000000000 sigstore

Rekor Server

A non-profit, public good software signing & transparency service.

To learn more visit Sigstore project page

Currently storing some items.

Copyright © sigstore a Series of LF Projects, LLC For web site terms of use, trademark policy and general project policies please see https://lfprojects.org.

rekor-1.3.5/pkg/generated/restapi/server.go000066400000000000000000000435241455727245600207160ustar00rootroot00000000000000// Code generated by go-swagger; DO NOT EDIT. // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package restapi import ( "context" "crypto/tls" "crypto/x509" "errors" "fmt" "log" "net" "net/http" "os" "os/signal" "strconv" "sync" "sync/atomic" "syscall" "time" "github.com/go-openapi/runtime/flagext" "github.com/go-openapi/swag" flag "github.com/spf13/pflag" "golang.org/x/net/netutil" "github.com/sigstore/rekor/pkg/generated/restapi/operations" ) const ( schemeHTTP = "http" schemeHTTPS = "https" schemeUnix = "unix" ) var defaultSchemes []string func init() { defaultSchemes = []string{ schemeHTTP, } } var ( enabledListeners []string cleanupTimeout time.Duration gracefulTimeout time.Duration maxHeaderSize flagext.ByteSize socketPath string host string port int listenLimit int keepAlive time.Duration readTimeout time.Duration writeTimeout time.Duration tlsHost string tlsPort int tlsListenLimit int tlsKeepAlive time.Duration tlsReadTimeout time.Duration tlsWriteTimeout time.Duration tlsCertificate string tlsCertificateKey string tlsCACertificate string ) func init() { maxHeaderSize = flagext.ByteSize(1000000) flag.StringSliceVar(&enabledListeners, "scheme", defaultSchemes, "the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec") flag.DurationVar(&cleanupTimeout, "cleanup-timeout", 10*time.Second, "grace period for which to wait before killing idle connections") flag.DurationVar(&gracefulTimeout, "graceful-timeout", 15*time.Second, "grace period for which to wait before shutting down the server") flag.Var(&maxHeaderSize, "max-header-size", "controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body") flag.StringVar(&socketPath, "socket-path", "/var/run/todo-list.sock", "the unix socket to listen on") flag.StringVar(&host, "host", "localhost", "the IP to listen on") flag.IntVar(&port, "port", 0, "the port to listen on for insecure connections, defaults to a random value") flag.IntVar(&listenLimit, "listen-limit", 0, "limit the number of outstanding requests") flag.DurationVar(&keepAlive, "keep-alive", 3*time.Minute, "sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)") flag.DurationVar(&readTimeout, "read-timeout", 30*time.Second, "maximum duration before timing out read of the request") flag.DurationVar(&writeTimeout, "write-timeout", 30*time.Second, "maximum duration before timing out write of the response") flag.StringVar(&tlsHost, "tls-host", "localhost", "the IP to listen on") flag.IntVar(&tlsPort, "tls-port", 0, "the port to listen on for secure connections, defaults to a random value") flag.StringVar(&tlsCertificate, "tls-certificate", "", "the certificate file to use for secure connections") flag.StringVar(&tlsCertificateKey, "tls-key", "", "the private key file to use for secure connections (without passphrase)") flag.StringVar(&tlsCACertificate, "tls-ca", "", "the certificate authority certificate file to be used with mutual tls auth") flag.IntVar(&tlsListenLimit, "tls-listen-limit", 0, "limit the number of outstanding requests") flag.DurationVar(&tlsKeepAlive, "tls-keep-alive", 3*time.Minute, "sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)") flag.DurationVar(&tlsReadTimeout, "tls-read-timeout", 30*time.Second, "maximum duration before timing out read of the request") flag.DurationVar(&tlsWriteTimeout, "tls-write-timeout", 30*time.Second, "maximum duration before timing out write of the response") } func stringEnvOverride(orig string, def string, keys ...string) string { for _, k := range keys { if os.Getenv(k) != "" { return os.Getenv(k) } } if def != "" && orig == "" { return def } return orig } func intEnvOverride(orig int, def int, keys ...string) int { for _, k := range keys { if os.Getenv(k) != "" { v, err := strconv.Atoi(os.Getenv(k)) if err != nil { fmt.Fprintln(os.Stderr, k, "is not a valid number") os.Exit(1) } return v } } if def != 0 && orig == 0 { return def } return orig } // NewServer creates a new api rekor server server but does not configure it func NewServer(api *operations.RekorServerAPI) *Server { s := new(Server) s.EnabledListeners = enabledListeners s.CleanupTimeout = cleanupTimeout s.GracefulTimeout = gracefulTimeout s.MaxHeaderSize = maxHeaderSize s.SocketPath = socketPath s.Host = stringEnvOverride(host, "", "HOST") s.Port = intEnvOverride(port, 0, "PORT") s.ListenLimit = listenLimit s.KeepAlive = keepAlive s.ReadTimeout = readTimeout s.WriteTimeout = writeTimeout s.TLSHost = stringEnvOverride(tlsHost, s.Host, "TLS_HOST", "HOST") s.TLSPort = intEnvOverride(tlsPort, 0, "TLS_PORT") s.TLSCertificate = stringEnvOverride(tlsCertificate, "", "TLS_CERTIFICATE") s.TLSCertificateKey = stringEnvOverride(tlsCertificateKey, "", "TLS_PRIVATE_KEY") s.TLSCACertificate = stringEnvOverride(tlsCACertificate, "", "TLS_CA_CERTIFICATE") s.TLSListenLimit = tlsListenLimit s.TLSKeepAlive = tlsKeepAlive s.TLSReadTimeout = tlsReadTimeout s.TLSWriteTimeout = tlsWriteTimeout s.shutdown = make(chan struct{}) s.api = api s.interrupt = make(chan os.Signal, 1) return s } // ConfigureAPI configures the API and handlers. func (s *Server) ConfigureAPI() { if s.api != nil { s.handler = configureAPI(s.api) } } // ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse func (s *Server) ConfigureFlags() { if s.api != nil { configureFlags(s.api) } } // Server for the rekor server API type Server struct { EnabledListeners []string CleanupTimeout time.Duration GracefulTimeout time.Duration MaxHeaderSize flagext.ByteSize SocketPath string domainSocketL net.Listener Host string Port int ListenLimit int KeepAlive time.Duration ReadTimeout time.Duration WriteTimeout time.Duration httpServerL net.Listener TLSHost string TLSPort int TLSCertificate string TLSCertificateKey string TLSCACertificate string TLSListenLimit int TLSKeepAlive time.Duration TLSReadTimeout time.Duration TLSWriteTimeout time.Duration httpsServerL net.Listener api *operations.RekorServerAPI handler http.Handler hasListeners bool shutdown chan struct{} shuttingDown int32 interrupted bool interrupt chan os.Signal } // Logf logs message either via defined user logger or via system one if no user logger is defined. func (s *Server) Logf(f string, args ...interface{}) { if s.api != nil && s.api.Logger != nil { s.api.Logger(f, args...) } else { log.Printf(f, args...) } } // Fatalf logs message either via defined user logger or via system one if no user logger is defined. // Exits with non-zero status after printing func (s *Server) Fatalf(f string, args ...interface{}) { if s.api != nil && s.api.Logger != nil { s.api.Logger(f, args...) os.Exit(1) } else { log.Fatalf(f, args...) } } // SetAPI configures the server with the specified API. Needs to be called before Serve func (s *Server) SetAPI(api *operations.RekorServerAPI) { if api == nil { s.api = nil s.handler = nil return } s.api = api s.handler = configureAPI(api) } func (s *Server) hasScheme(scheme string) bool { schemes := s.EnabledListeners if len(schemes) == 0 { schemes = defaultSchemes } for _, v := range schemes { if v == scheme { return true } } return false } // Serve the api func (s *Server) Serve() (err error) { if !s.hasListeners { if err = s.Listen(); err != nil { return err } } // set default handler, if none is set if s.handler == nil { if s.api == nil { return errors.New("can't create the default handler, as no api is set") } s.SetHandler(s.api.Serve(nil)) } wg := new(sync.WaitGroup) once := new(sync.Once) signalNotify(s.interrupt) go handleInterrupt(once, s) servers := []*http.Server{} if s.hasScheme(schemeUnix) { domainSocket := new(http.Server) domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize) domainSocket.Handler = s.handler if int64(s.CleanupTimeout) > 0 { domainSocket.IdleTimeout = s.CleanupTimeout } configureServer(domainSocket, "unix", string(s.SocketPath)) servers = append(servers, domainSocket) wg.Add(1) s.Logf("Serving rekor server at unix://%s", s.SocketPath) go func(l net.Listener) { defer wg.Done() if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed { s.Fatalf("%v", err) } s.Logf("Stopped serving rekor server at unix://%s", s.SocketPath) }(s.domainSocketL) } if s.hasScheme(schemeHTTP) { httpServer := new(http.Server) httpServer.MaxHeaderBytes = int(s.MaxHeaderSize) httpServer.ReadTimeout = s.ReadTimeout httpServer.WriteTimeout = s.WriteTimeout httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0) if s.ListenLimit > 0 { s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit) } if int64(s.CleanupTimeout) > 0 { httpServer.IdleTimeout = s.CleanupTimeout } httpServer.Handler = s.handler configureServer(httpServer, "http", s.httpServerL.Addr().String()) servers = append(servers, httpServer) wg.Add(1) s.Logf("Serving rekor server at http://%s", s.httpServerL.Addr()) go func(l net.Listener) { defer wg.Done() if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed { s.Fatalf("%v", err) } s.Logf("Stopped serving rekor server at http://%s", l.Addr()) }(s.httpServerL) } if s.hasScheme(schemeHTTPS) { httpsServer := new(http.Server) httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize) httpsServer.ReadTimeout = s.TLSReadTimeout httpsServer.WriteTimeout = s.TLSWriteTimeout httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0) if s.TLSListenLimit > 0 { s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit) } if int64(s.CleanupTimeout) > 0 { httpsServer.IdleTimeout = s.CleanupTimeout } httpsServer.Handler = s.handler // Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go httpsServer.TLSConfig = &tls.Config{ // Causes servers to use Go's default ciphersuite preferences, // which are tuned to avoid attacks. Does nothing on clients. PreferServerCipherSuites: true, // Only use curves which have assembly implementations // https://github.com/golang/go/tree/master/src/crypto/elliptic CurvePreferences: []tls.CurveID{tls.CurveP256}, // Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility NextProtos: []string{"h2", "http/1.1"}, // https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols MinVersion: tls.VersionTLS12, // These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy CipherSuites: []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, }, } // build standard config from server options if s.TLSCertificate != "" && s.TLSCertificateKey != "" { httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1) httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(s.TLSCertificate, s.TLSCertificateKey) if err != nil { return err } } if s.TLSCACertificate != "" { // include specified CA certificate caCert, caCertErr := os.ReadFile(s.TLSCACertificate) if caCertErr != nil { return caCertErr } caCertPool := x509.NewCertPool() ok := caCertPool.AppendCertsFromPEM(caCert) if !ok { return fmt.Errorf("cannot parse CA certificate") } httpsServer.TLSConfig.ClientCAs = caCertPool httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert } // call custom TLS configurator configureTLS(httpsServer.TLSConfig) if len(httpsServer.TLSConfig.Certificates) == 0 && httpsServer.TLSConfig.GetCertificate == nil { // after standard and custom config are passed, this ends up with no certificate if s.TLSCertificate == "" { if s.TLSCertificateKey == "" { s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified") } s.Fatalf("the required flag `--tls-certificate` was not specified") } if s.TLSCertificateKey == "" { s.Fatalf("the required flag `--tls-key` was not specified") } // this happens with a wrong custom TLS configurator s.Fatalf("no certificate was configured for TLS") } configureServer(httpsServer, "https", s.httpsServerL.Addr().String()) servers = append(servers, httpsServer) wg.Add(1) s.Logf("Serving rekor server at https://%s", s.httpsServerL.Addr()) go func(l net.Listener) { defer wg.Done() if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed { s.Fatalf("%v", err) } s.Logf("Stopped serving rekor server at https://%s", l.Addr()) }(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig)) } wg.Add(1) go s.handleShutdown(wg, &servers) wg.Wait() return nil } // Listen creates the listeners for the server func (s *Server) Listen() error { if s.hasListeners { // already done this return nil } if s.hasScheme(schemeHTTPS) { // Use http host if https host wasn't defined if s.TLSHost == "" { s.TLSHost = s.Host } // Use http listen limit if https listen limit wasn't defined if s.TLSListenLimit == 0 { s.TLSListenLimit = s.ListenLimit } // Use http tcp keep alive if https tcp keep alive wasn't defined if int64(s.TLSKeepAlive) == 0 { s.TLSKeepAlive = s.KeepAlive } // Use http read timeout if https read timeout wasn't defined if int64(s.TLSReadTimeout) == 0 { s.TLSReadTimeout = s.ReadTimeout } // Use http write timeout if https write timeout wasn't defined if int64(s.TLSWriteTimeout) == 0 { s.TLSWriteTimeout = s.WriteTimeout } } if s.hasScheme(schemeUnix) { domSockListener, err := net.Listen("unix", string(s.SocketPath)) if err != nil { return err } s.domainSocketL = domSockListener } if s.hasScheme(schemeHTTP) { listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port))) if err != nil { return err } h, p, err := swag.SplitHostPort(listener.Addr().String()) if err != nil { return err } s.Host = h s.Port = p s.httpServerL = listener } if s.hasScheme(schemeHTTPS) { tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort))) if err != nil { return err } sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String()) if err != nil { return err } s.TLSHost = sh s.TLSPort = sp s.httpsServerL = tlsListener } s.hasListeners = true return nil } // Shutdown server and clean up resources func (s *Server) Shutdown() error { if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) { close(s.shutdown) } return nil } func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) { // wg.Done must occur last, after s.api.ServerShutdown() // (to preserve old behaviour) defer wg.Done() <-s.shutdown servers := *serversPtr ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout) defer cancel() // first execute the pre-shutdown hook s.api.PreServerShutdown() shutdownChan := make(chan bool) for i := range servers { server := servers[i] go func() { var success bool defer func() { shutdownChan <- success }() if err := server.Shutdown(ctx); err != nil { // Error from closing listeners, or context timeout: s.Logf("HTTP server Shutdown: %v", err) } else { success = true } }() } // Wait until all listeners have successfully shut down before calling ServerShutdown success := true for range servers { success = success && <-shutdownChan } if success { s.api.ServerShutdown() } } // GetHandler returns a handler useful for testing func (s *Server) GetHandler() http.Handler { return s.handler } // SetHandler allows for setting a http handler on this server func (s *Server) SetHandler(handler http.Handler) { s.handler = handler } // UnixListener returns the domain socket listener func (s *Server) UnixListener() (net.Listener, error) { if !s.hasListeners { if err := s.Listen(); err != nil { return nil, err } } return s.domainSocketL, nil } // HTTPListener returns the http listener func (s *Server) HTTPListener() (net.Listener, error) { if !s.hasListeners { if err := s.Listen(); err != nil { return nil, err } } return s.httpServerL, nil } // TLSListener returns the https listener func (s *Server) TLSListener() (net.Listener, error) { if !s.hasListeners { if err := s.Listen(); err != nil { return nil, err } } return s.httpsServerL, nil } func handleInterrupt(once *sync.Once, s *Server) { once.Do(func() { for range s.interrupt { if s.interrupted { s.Logf("Server already shutting down") continue } s.interrupted = true s.Logf("Shutting down... ") if err := s.Shutdown(); err != nil { s.Logf("HTTP server Shutdown: %v", err) } } }) } func signalNotify(interrupt chan<- os.Signal) { signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) } rekor-1.3.5/pkg/indexstorage/000077500000000000000000000000001455727245600161405ustar00rootroot00000000000000rekor-1.3.5/pkg/indexstorage/indexstorage.go000066400000000000000000000037031455727245600211660ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package indexstorage import ( "context" "fmt" "github.com/sigstore/rekor/pkg/indexstorage/mysql" "github.com/sigstore/rekor/pkg/indexstorage/redis" "github.com/spf13/viper" ) type IndexStorage interface { LookupIndices(context.Context, []string) ([]string, error) // Returns indices for specified keys WriteIndex(context.Context, []string, string) error // Writes index for specified keys Shutdown() error // Method to run on shutdown } // NewIndexStorage instantiates a new IndexStorage provider based on the requested type func NewIndexStorage(providerType string) (IndexStorage, error) { switch providerType { case redis.ProviderType: return redis.NewProvider(viper.GetString("redis_server.address"), viper.GetString("redis_server.port"), viper.GetString("redis_server.password")) case mysql.ProviderType: return mysql.NewProvider(viper.GetString("search_index.mysql.dsn"), mysql.WithConnMaxIdleTime(viper.GetDuration("search_index.mysql.conn_max_idletime")), mysql.WithConnMaxLifetime(viper.GetDuration("search_index.mysql.conn_max_lifetime")), mysql.WithMaxIdleConns(viper.GetInt("search_index.mysql.max_idle_connections")), mysql.WithMaxOpenConns(viper.GetInt("search_index.mysql.max_open_connections"))) default: return nil, fmt.Errorf("invalid index storage provider type: %v", providerType) } } rekor-1.3.5/pkg/indexstorage/mysql/000077500000000000000000000000001455727245600173055ustar00rootroot00000000000000rekor-1.3.5/pkg/indexstorage/mysql/mysql.go000066400000000000000000000100411455727245600207750ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package mysql import ( "context" "errors" "fmt" "github.com/jmoiron/sqlx" "github.com/sigstore/rekor/pkg/log" // this imports the mysql driver for go _ "github.com/go-sql-driver/mysql" ) const ( ProviderType = "mysql" lookupStmt = "SELECT EntryUUID FROM EntryIndex WHERE EntryKey IN (?)" writeStmt = "INSERT IGNORE INTO EntryIndex (EntryKey, EntryUUID) VALUES (:key, :uuid)" createTableStmt = `CREATE TABLE IF NOT EXISTS EntryIndex ( PK BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, EntryKey varchar(512) NOT NULL, EntryUUID char(80) NOT NULL, PRIMARY KEY(PK), UNIQUE(EntryKey, EntryUUID) )` ) // IndexStorageProvider implements indexstorage.IndexStorage type IndexStorageProvider struct { db *sqlx.DB } func NewProvider(dsn string, opts ...Options) (*IndexStorageProvider, error) { var err error provider := &IndexStorageProvider{} provider.db, err = sqlx.Open(ProviderType, dsn) if err != nil { return nil, err } if err = provider.db.Ping(); err != nil { return nil, err } for _, o := range opts { o.applyConnMaxIdleTime(provider.db) o.applyConnMaxLifetime(provider.db) o.applyMaxIdleConns(provider.db) o.applyMaxOpenConns(provider.db) } if _, err := provider.db.Exec(createTableStmt); err != nil { return nil, fmt.Errorf("create table if not exists failed: %w", err) } return provider, nil } // LookupIndices looks up and returns all indices for the specified keys. The key value(s) will be canonicalized // by converting all characters into a lowercase value before looking up in Redis func (isp *IndexStorageProvider) LookupIndices(ctx context.Context, keys []string) ([]string, error) { if isp.db == nil { return []string{}, errors.New("sql client has not been initialized") } query, args, err := sqlx.In(lookupStmt, keys) if err != nil { return []string{}, fmt.Errorf("error preparing statement: %w", err) } rows, err := isp.db.QueryContext(ctx, isp.db.Rebind(query), args...) if err != nil { return []string{}, fmt.Errorf("error looking up indices from mysql: %w", err) } defer rows.Close() var entryUUIDs []string for rows.Next() { var result string if err := rows.Scan(&result); err != nil { return []string{}, fmt.Errorf("error parsing results from mysql: %w", err) } entryUUIDs = append(entryUUIDs, result) } if err := rows.Err(); err != nil { return []string{}, fmt.Errorf("error processing results from mysql: %w", err) } return entryUUIDs, nil } // WriteIndex adds the index for the specified key. The key value will be canonicalized // by converting all characters into a lowercase value before appending the index in Redis func (isp *IndexStorageProvider) WriteIndex(ctx context.Context, keys []string, index string) error { if isp.db == nil { return errors.New("sql client has not been initialized") } var valueMaps []map[string]interface{} for _, key := range keys { valueMaps = append(valueMaps, map[string]interface{}{"key": key, "uuid": index}) } result, err := isp.db.NamedExecContext(ctx, writeStmt, valueMaps) if err != nil { return fmt.Errorf("mysql write error: %w", err) } rowsAffected, err := result.RowsAffected() if err != nil { return fmt.Errorf("mysql error getting rowsAffected: %w", err) } log.ContextLogger(ctx).Debugf("WriteIndex affected %d rows", rowsAffected) return nil } // Shutdown cleans up any client resources that may be held by the provider func (isp *IndexStorageProvider) Shutdown() error { if isp.db == nil { return nil } return isp.db.Close() } rekor-1.3.5/pkg/indexstorage/mysql/mysql_test.go000066400000000000000000000064031455727245600220430ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package mysql import ( "context" "errors" "regexp" "testing" "github.com/DATA-DOG/go-sqlmock" "github.com/jmoiron/sqlx" "go.uber.org/goleak" ) func TestLookupIndices(t *testing.T) { keys := []string{"87c1b129fbadd7b6e9abc0a9ef7695436d767aece042bec198a97e949fcbe14c"} value := []string{"1e1f2c881ae0608ec77ebf88a75c66d3099113a7343238f2f7a0ebb91a4ed335"} db, mock, err := sqlmock.New() if err != nil { t.Fatalf("unexpected error creating mock db: %v", err) } isp := IndexStorageProvider{sqlx.NewDb(db, "mysql")} defer isp.Shutdown() mock.ExpectQuery(lookupStmt).WithArgs(keys[0]).WillReturnRows(sqlmock.NewRows(value)) _, err = isp.LookupIndices(context.Background(), keys) if err != nil { t.Error(err) } if err := mock.ExpectationsWereMet(); err != nil { t.Error(err) } expectedErr := errors.New("badness") mock.ExpectQuery(lookupStmt).WillReturnError(expectedErr) if _, err := isp.LookupIndices(context.Background(), keys); err == nil { t.Error("unexpected success") } if err := mock.ExpectationsWereMet(); err != nil { t.Error(err) } } func TestWriteIndex(t *testing.T) { keys := []string{"87c1b129fbadd7b6e9abc0a9ef7695436d767aece042bec198a97e949fcbe14c"} value := "1e1f2c881ae0608ec77ebf88a75c66d3099113a7343238f2f7a0ebb91a4ed335" db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("unexpected error creating mock db: %v", err) } re := regexp.MustCompile(`:[a-z]*`) expectedStmt := string(re.ReplaceAll([]byte(writeStmt), []byte("?"))) isp := IndexStorageProvider{sqlx.NewDb(db, "mysql")} defer isp.Shutdown() if err := mock.ExpectationsWereMet(); err != nil { t.Fatalf("expectations not met: %v", err) } mock.ExpectExec(expectedStmt).WithArgs(keys[0], value).WillReturnResult(sqlmock.NewResult(1, 1)) if err := isp.WriteIndex(context.Background(), keys, value); err != nil { t.Errorf("%v, %v", expectedStmt, err) } if err := mock.ExpectationsWereMet(); err != nil { t.Error(err) } expectedErr := errors.New("badness") mock.ExpectExec(expectedStmt).WillReturnError(expectedErr) if err := isp.WriteIndex(context.Background(), keys, value); err == nil { t.Error("unexpected success") } if err := mock.ExpectationsWereMet(); err != nil { t.Error(err) } } func TestUninitializedClient(t *testing.T) { // this is not initialized with a real mysql client isp := IndexStorageProvider{} if _, err := isp.LookupIndices(context.Background(), []string{"key"}); err == nil { t.Error("unexpected success") } if err := isp.WriteIndex(context.Background(), []string{"key"}, "value"); err == nil { t.Error("unexpected success") } } func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } rekor-1.3.5/pkg/indexstorage/mysql/options.go000066400000000000000000000073331455727245600213350ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package mysql import ( "time" "github.com/jmoiron/sqlx" ) // Options configures connections to the MySQL index storage system type Options interface { applyConnMaxIdleTime(*sqlx.DB) applyConnMaxLifetime(*sqlx.DB) applyMaxIdleConns(*sqlx.DB) applyMaxOpenConns(*sqlx.DB) } // NoOpOptionImpl implements the MySQLOption interfaces as no-ops. type noOpOptionImpl struct{} // applyConnMaxIdleTime is a no-op required to fully implement the requisite interfaces func (noOpOptionImpl) applyConnMaxIdleTime(_ *sqlx.DB) {} // ApplyConnMaxLifetime is a no-op required to fully implement the requisite interfaces func (noOpOptionImpl) applyConnMaxLifetime(_ *sqlx.DB) {} // ApplyMaxOpenConns is a no-op required to fully implement the requisite interfaces func (noOpOptionImpl) applyMaxOpenConns(_ *sqlx.DB) {} // ApplyMaxIdleConns is a no-op required to fully implement the requisite interfaces func (noOpOptionImpl) applyMaxIdleConns(_ *sqlx.DB) {} // RequestConnMaxIdleTime implements the functional option pattern for specifying the maximum connection idle time type RequestConnMaxIdleTime struct { noOpOptionImpl idleTime time.Duration } // applyConnMaxIdleTime sets the maximum connection idle time func (r RequestConnMaxIdleTime) applyConnMaxIdleTime(db *sqlx.DB) { if db != nil { db.SetConnMaxIdleTime(r.idleTime) } } // WithConnMaxIdleTime specifies the maximum connection idle time func WithConnMaxIdleTime(idleTime time.Duration) RequestConnMaxIdleTime { return RequestConnMaxIdleTime{idleTime: idleTime} } // RequestConnMaxLifetime implements the functional option pattern for specifying the maximum connection lifetime type RequestConnMaxLifetime struct { noOpOptionImpl lifetime time.Duration } // ApplyConnMaxLifetime sets the maximum connection lifetime func (r RequestConnMaxLifetime) applyConnMaxLifetime(db *sqlx.DB) { if db != nil { db.SetConnMaxLifetime(r.lifetime) } } // WithConnMaxLifetime specifies the maximum connection lifetime func WithConnMaxLifetime(lifetime time.Duration) RequestConnMaxLifetime { return RequestConnMaxLifetime{lifetime: lifetime} } // RequestMaxIdleConns implements the functional option pattern for specifying the maximum number of idle connections type RequestMaxIdleConns struct { noOpOptionImpl idleConns int } // ApplyMaxIdleConns sets the maximum number of idle connections func (r RequestMaxIdleConns) applyMaxIdleConns(db *sqlx.DB) { if db != nil { db.SetMaxIdleConns(r.idleConns) } } // WithMaxIdleConns specifies the maximum number of idle connections func WithMaxIdleConns(idleConns int) RequestMaxIdleConns { return RequestMaxIdleConns{idleConns: idleConns} } // RequestMaxOpenConns implements the functional option pattern for specifying the maximum number of open connections type RequestMaxOpenConns struct { noOpOptionImpl openConns int } // applyMaxOpenConns sets the maximum number of open connections func (r RequestMaxOpenConns) applyMaxOpenConns(db *sqlx.DB) { if db != nil { db.SetMaxOpenConns(r.openConns) } } // WithMaxOpenConns specifies the maximum number of open connections func WithMaxOpenConns(openConns int) RequestMaxOpenConns { return RequestMaxOpenConns{openConns: openConns} } rekor-1.3.5/pkg/indexstorage/redis/000077500000000000000000000000001455727245600172465ustar00rootroot00000000000000rekor-1.3.5/pkg/indexstorage/redis/redis.go000066400000000000000000000053371455727245600207130ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package redis import ( "context" "errors" "fmt" "strings" redis "github.com/redis/go-redis/v9" ) const ProviderType = "redis" // IndexStorageProvider implements indexstorage.IndexStorage type IndexStorageProvider struct { client *redis.Client } func NewProvider(address, port, password string) (*IndexStorageProvider, error) { provider := &IndexStorageProvider{} provider.client = redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%v:%v", address, port), Network: "tcp", Password: password, DB: 0, // default DB }) return provider, nil } // LookupIndices looks up and returns all indices for the specified key(s). The key value(s) will be canonicalized // by converting all characters into a lowercase value before looking up in Redis func (isp *IndexStorageProvider) LookupIndices(ctx context.Context, keys []string) ([]string, error) { if isp.client == nil { return []string{}, errors.New("redis client has not been initialized") } cmds, err := isp.client.Pipelined(ctx, func(pipe redis.Pipeliner) error { for _, key := range keys { pipe.LRange(ctx, strings.ToLower(key), 0, -1) } return nil }) if err != nil { return []string{}, fmt.Errorf("redis client: %w", err) } var result []string for _, cmd := range cmds { result = append(result, cmd.(*redis.StringSliceCmd).Val()...) } return result, nil } // WriteIndex adds the index for the specified keys. The key value(s) will be canonicalized // by converting all characters into a lowercase value before appending the index in Redis func (isp *IndexStorageProvider) WriteIndex(ctx context.Context, keys []string, index string) error { if isp.client == nil { return errors.New("redis client has not been initialized") } _, err := isp.client.Pipelined(ctx, func(pipe redis.Pipeliner) error { for _, key := range keys { pipe.LPush(ctx, strings.ToLower(key), index) } return nil }) if err != nil { return fmt.Errorf("redis client: %w", err) } return nil } // Shutdown cleans up any client resources that may be held by the provider func (isp *IndexStorageProvider) Shutdown() error { if isp.client == nil { return nil } return isp.client.Close() } rekor-1.3.5/pkg/indexstorage/redis/redis_test.go000066400000000000000000000057501455727245600217510ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package redis import ( "context" "errors" "testing" "github.com/go-redis/redismock/v9" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "go.uber.org/goleak" ) func TestLookupIndices(t *testing.T) { keys := []string{"87c1b129fbadd7b6e9abc0a9ef7695436d767aece042bec198a97e949fcbe14c"} value := []string{"1e1f2c881ae0608ec77ebf88a75c66d3099113a7343238f2f7a0ebb91a4ed335"} redisClient, mock := redismock.NewClientMock() mock.Regexp().ExpectLRange(keys[0], 0, -1).SetVal(value) isp := IndexStorageProvider{redisClient} indices, err := isp.LookupIndices(context.Background(), keys) if err != nil { t.Error(err) } less := func(a, b string) bool { return a < b } if cmp.Diff(value, indices, cmpopts.SortSlices(less)) != "" { t.Errorf("expected %s, got %s", value, indices) } if err := mock.ExpectationsWereMet(); err != nil { t.Error(err) } mock.ClearExpect() errRedis := errors.New("redis error") mock.Regexp().ExpectLRange(keys[0], 0, -1).SetErr(errRedis) if _, err := isp.LookupIndices(context.Background(), keys); err == nil { t.Error("unexpected success") } if err := mock.ExpectationsWereMet(); err != nil { t.Error(err) } } func TestWriteIndex(t *testing.T) { keys := []string{"87c1b129fbadd7b6e9abc0a9ef7695436d767aece042bec198a97e949fcbe14c"} value := []string{"1e1f2c881ae0608ec77ebf88a75c66d3099113a7343238f2f7a0ebb91a4ed335"} redisClient, mock := redismock.NewClientMock() mock.Regexp().ExpectLPush(keys[0], value).SetVal(1) isp := IndexStorageProvider{redisClient} if err := isp.WriteIndex(context.Background(), keys, value[0]); err != nil { t.Error(err) } if err := mock.ExpectationsWereMet(); err != nil { t.Error(err) } mock.ClearExpect() errRedis := errors.New("redis error") mock.Regexp().ExpectLPush(keys[0], value).SetErr(errRedis) if err := isp.WriteIndex(context.Background(), keys, value[0]); err == nil { t.Error("unexpected success") } if err := mock.ExpectationsWereMet(); err != nil { t.Error(err) } } func TestUninitializedClient(t *testing.T) { // this is not initialized with a real Redis client isp := IndexStorageProvider{} if _, err := isp.LookupIndices(context.Background(), []string{"key"}); err == nil { t.Error("unexpected success") } if err := isp.WriteIndex(context.Background(), []string{"key"}, "value"); err == nil { t.Error("unexpected success") } } func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } rekor-1.3.5/pkg/log/000077500000000000000000000000001455727245600142255ustar00rootroot00000000000000rekor-1.3.5/pkg/log/log.go000066400000000000000000000063741455727245600153470ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package log import ( "context" "fmt" "log" "github.com/go-chi/chi/middleware" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) // Logger set the default logger to development mode var Logger *zap.SugaredLogger var traceStringPrefix string func init() { ConfigureLogger("dev", "") } func ConfigureLogger(logType, traceStrPrefix string) { var cfg zap.Config if logType == "prod" { cfg = zap.NewProductionConfig() cfg.EncoderConfig.LevelKey = "severity" cfg.EncoderConfig.MessageKey = "message" cfg.EncoderConfig.TimeKey = "time" cfg.EncoderConfig.EncodeLevel = encodeLevel() cfg.EncoderConfig.EncodeTime = zapcore.RFC3339NanoTimeEncoder cfg.EncoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder cfg.EncoderConfig.EncodeCaller = zapcore.FullCallerEncoder } else { cfg = zap.NewDevelopmentConfig() cfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder } logger, err := cfg.Build() if err != nil { log.Fatalln("createLogger", err) } Logger = logger.Sugar() if traceStrPrefix != "" { traceStringPrefix = traceStrPrefix } } func encodeLevel() zapcore.LevelEncoder { return func(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { switch l { case zapcore.DebugLevel: enc.AppendString("DEBUG") case zapcore.InfoLevel: enc.AppendString("INFO") case zapcore.WarnLevel: enc.AppendString("WARNING") case zapcore.ErrorLevel: enc.AppendString("ERROR") case zapcore.DPanicLevel: enc.AppendString("CRITICAL") case zapcore.PanicLevel: enc.AppendString("ALERT") case zapcore.FatalLevel: enc.AppendString("EMERGENCY") } } } var CliLogger = createCliLogger() func createCliLogger() *zap.SugaredLogger { cfg := zap.NewDevelopmentConfig() cfg.EncoderConfig.TimeKey = "" cfg.EncoderConfig.LevelKey = "" cfg.DisableCaller = true cfg.DisableStacktrace = true logger, err := cfg.Build() if err != nil { log.Fatalln("createLogger", err) } return logger.Sugar() } func WithRequestID(ctx context.Context, id string) context.Context { return context.WithValue(ctx, middleware.RequestIDKey, id) } type operation struct { id string } func (o operation) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("id", o.id) return nil } func ContextLogger(ctx context.Context) *zap.SugaredLogger { proposedLogger := Logger if ctx != nil { if ctxRequestID, ok := ctx.Value(middleware.RequestIDKey).(string); ok { requestID := operation{ctxRequestID} proposedLogger = proposedLogger.With(zap.Object("operation", requestID)) if traceStringPrefix != "" { proposedLogger = proposedLogger.With(zap.String("trace", fmt.Sprintf("%s/%s", traceStringPrefix, ctxRequestID))) } } } return proposedLogger } rekor-1.3.5/pkg/pki/000077500000000000000000000000001455727245600142275ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/factory.go000066400000000000000000000061311455727245600162260ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package pki import ( "fmt" "io" "github.com/sigstore/rekor/pkg/pki/minisign" "github.com/sigstore/rekor/pkg/pki/pgp" "github.com/sigstore/rekor/pkg/pki/pkcs7" "github.com/sigstore/rekor/pkg/pki/ssh" "github.com/sigstore/rekor/pkg/pki/tuf" "github.com/sigstore/rekor/pkg/pki/x509" ) type Format string const ( PGP Format = "pgp" Minisign Format = "minisign" SSH Format = "ssh" X509 Format = "x509" PKCS7 Format = "pkcs7" Tuf Format = "tuf" ) type ArtifactFactory struct { impl pkiImpl } func NewArtifactFactory(format Format) (*ArtifactFactory, error) { if impl, ok := artifactFactoryMap[format]; ok { return &ArtifactFactory{impl: impl}, nil } return nil, fmt.Errorf("%v is not a supported PKI format", format) } type pkiImpl struct { newPubKey func(io.Reader) (PublicKey, error) newSignature func(io.Reader) (Signature, error) } var artifactFactoryMap map[Format]pkiImpl func init() { artifactFactoryMap = map[Format]pkiImpl{ PGP: { newPubKey: func(r io.Reader) (PublicKey, error) { return pgp.NewPublicKey(r) }, newSignature: func(r io.Reader) (Signature, error) { return pgp.NewSignature(r) }, }, Minisign: { newPubKey: func(r io.Reader) (PublicKey, error) { return minisign.NewPublicKey(r) }, newSignature: func(r io.Reader) (Signature, error) { return minisign.NewSignature(r) }, }, SSH: { newPubKey: func(r io.Reader) (PublicKey, error) { return ssh.NewPublicKey(r) }, newSignature: func(r io.Reader) (Signature, error) { return ssh.NewSignature(r) }, }, X509: { newPubKey: func(r io.Reader) (PublicKey, error) { return x509.NewPublicKey(r) }, newSignature: func(r io.Reader) (Signature, error) { return x509.NewSignature(r) }, }, PKCS7: { newPubKey: func(r io.Reader) (PublicKey, error) { return pkcs7.NewPublicKey(r) }, newSignature: func(r io.Reader) (Signature, error) { return pkcs7.NewSignature(r) }, }, Tuf: { newPubKey: func(r io.Reader) (PublicKey, error) { return tuf.NewPublicKey(r) }, newSignature: func(r io.Reader) (Signature, error) { return tuf.NewSignature(r) }, }, } } func SupportedFormats() []string { var formats []string for f := range artifactFactoryMap { formats = append(formats, string(f)) } return formats } func (a ArtifactFactory) NewPublicKey(r io.Reader) (PublicKey, error) { return a.impl.newPubKey(r) } func (a ArtifactFactory) NewSignature(r io.Reader) (Signature, error) { return a.impl.newSignature(r) } rekor-1.3.5/pkg/pki/factory_test.go000066400000000000000000000064721455727245600172750ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package pki import ( "os" "testing" "go.uber.org/goleak" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestFactoryNewKey(t *testing.T) { type TestCase struct { name string format string keyFile string sigFile string expectSuccess bool expectValidFormat bool } testCases := []TestCase{ { name: "valid pgp", format: "pgp", keyFile: "pgp/testdata/valid_armored_public.pgp", sigFile: "pgp/testdata/hello_world.txt.asc.sig", expectSuccess: true, expectValidFormat: true, }, { name: "valid minisign", format: "minisign", keyFile: "minisign/testdata/minisign.pub", sigFile: "minisign/testdata/hello_world.txt.minisig", expectSuccess: true, expectValidFormat: true, }, { name: "valid x509", format: "x509", keyFile: "x509/testdata/ec.pub", sigFile: "x509/testdata/hello_world.txt.sig", expectSuccess: true, expectValidFormat: true, }, { name: "valid ssh", format: "ssh", keyFile: "ssh/testdata/id_rsa.pub", sigFile: "ssh/testdata/hello_world.txt.sig", expectSuccess: true, expectValidFormat: true, }, { name: "invalid ssh signature", format: "ssh", keyFile: "ssh/testdata/id_rsa.pub", sigFile: "ssh/testdata/hello_world.txt", expectSuccess: false, expectValidFormat: true, }, { name: "invalid ssh key", format: "ssh", keyFile: "ssh/testdata/hello_world.txt", sigFile: "ssh/testdata/hello_world.txt.sig", expectSuccess: false, expectValidFormat: true, }, { format: "bogus", expectSuccess: false, expectValidFormat: false, }, } for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { factory, err := NewArtifactFactory(Format(tc.format)) if tc.expectValidFormat != (err == nil) { t.Fatalf("unexpected error initializing factory for %v", tc.format) } if factory != nil { keyFile, _ := os.Open(tc.keyFile) _, newKeyErr := factory.NewPublicKey(keyFile) sigFile, _ := os.Open(tc.sigFile) _, newSigErr := factory.NewSignature(sigFile) if tc.expectSuccess { if newKeyErr != nil || newSigErr != nil { t.Errorf("unexpected error generating public key %v or signature %v", newKeyErr, newSigErr) } } else { // expect a failure{ if newKeyErr == nil && newSigErr == nil { t.Error("expected error generating public key and signature. got none") } } } }) } } rekor-1.3.5/pkg/pki/fuzz_test.go000066400000000000000000000053741455727245600166240ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package pki import ( "bytes" "io" "testing" "github.com/sigstore/rekor/pkg/pki/minisign" "github.com/sigstore/rekor/pkg/pki/pgp" "github.com/sigstore/rekor/pkg/pki/pkcs7" "github.com/sigstore/rekor/pkg/pki/ssh" "github.com/sigstore/rekor/pkg/pki/tuf" "github.com/sigstore/rekor/pkg/pki/x509" ) var ( fuzzArtifactFactoryMap = map[uint]pkiImpl{ 0: { newPubKey: func(r io.Reader) (PublicKey, error) { return pgp.NewPublicKey(r) }, newSignature: func(r io.Reader) (Signature, error) { return pgp.NewSignature(r) }, }, 1: { newPubKey: func(r io.Reader) (PublicKey, error) { return minisign.NewPublicKey(r) }, newSignature: func(r io.Reader) (Signature, error) { return minisign.NewSignature(r) }, }, 2: { newPubKey: func(r io.Reader) (PublicKey, error) { return ssh.NewPublicKey(r) }, newSignature: func(r io.Reader) (Signature, error) { return ssh.NewSignature(r) }, }, 3: { newPubKey: func(r io.Reader) (PublicKey, error) { return x509.NewPublicKey(r) }, newSignature: func(r io.Reader) (Signature, error) { return x509.NewSignature(r) }, }, 4: { newPubKey: func(r io.Reader) (PublicKey, error) { return pkcs7.NewPublicKey(r) }, newSignature: func(r io.Reader) (Signature, error) { return pkcs7.NewSignature(r) }, }, 5: { newPubKey: func(r io.Reader) (PublicKey, error) { return tuf.NewPublicKey(r) }, newSignature: func(r io.Reader) (Signature, error) { return tuf.NewSignature(r) }, }, } ) func FuzzKeys(f *testing.F) { f.Fuzz(func(t *testing.T, keyType uint, origSignatureData, verSignatureData, keyData []byte) { s, err := fuzzArtifactFactoryMap[keyType%6].newSignature(bytes.NewReader(origSignatureData)) if err == nil && s != nil { b, err := s.CanonicalValue() if err == nil { _, err = fuzzArtifactFactoryMap[keyType%6].newSignature(bytes.NewReader(b)) if err != nil { t.Fatal("Could not create a signature from valid key data") } } pub, err := fuzzArtifactFactoryMap[keyType%6].newPubKey(bytes.NewReader(keyData)) if err != nil { t.Skip() } s.Verify(bytes.NewReader(verSignatureData), pub) } }) } rekor-1.3.5/pkg/pki/identity/000077500000000000000000000000001455727245600160605ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/identity/identity.go000066400000000000000000000026321455727245600202430ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package identity type Identity struct { // Types include: // - *rsa.PublicKey // - *ecdsa.PublicKey // - ed25519.PublicKey // - *x509.Certificate // - openpgp.EntityList (golang.org/x/crypto/openpgp) // - *minisign.PublicKey (github.com/jedisct1/go-minisign) // - ssh.PublicKey (golang.org/x/crypto/ssh) 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 // For keys, certificates, and minisign, hex-encoded SHA-256 digest // of the public key or certificate // For SSH and PGP, Fingerprint is the standard for each ecosystem // For SSH, unpadded base-64 encoded SHA-256 digest of the key // For PGP, hex-encoded SHA-1 digest of a key, which can be either // a primary key or subkey Fingerprint string } rekor-1.3.5/pkg/pki/minisign/000077500000000000000000000000001455727245600160445ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/minisign/minisign.go000066400000000000000000000142721455727245600202160ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package minisign import ( "bytes" "crypto/ed25519" "crypto/sha256" "encoding/base64" "encoding/hex" "fmt" "io" "strings" minisign "github.com/jedisct1/go-minisign" "github.com/sigstore/rekor/pkg/pki/identity" "github.com/sigstore/sigstore/pkg/cryptoutils" sigsig "github.com/sigstore/sigstore/pkg/signature" "golang.org/x/crypto/blake2b" ) // Signature Signature that follows the minisign standard; supports both minisign and signify generated signatures type Signature struct { signature *minisign.Signature } // NewSignature creates and validates a minisign signature object func NewSignature(r io.Reader) (*Signature, error) { var s Signature var inputBuffer bytes.Buffer if _, err := io.Copy(&inputBuffer, r); err != nil { return nil, fmt.Errorf("unable to read minisign signature: %w", err) } inputString := inputBuffer.String() signature, err := minisign.DecodeSignature(inputString) if err != nil { // try to parse as signify lines := strings.Split(strings.TrimRight(inputString, "\n"), "\n") if len(lines) != 2 { return nil, fmt.Errorf("invalid signature provided: %v lines detected", len(lines)) } sigBytes, b64Err := base64.StdEncoding.DecodeString(lines[1]) if b64Err != nil { return nil, fmt.Errorf("invalid signature provided: base64 decoding failed") } if len(sigBytes) != len(signature.SignatureAlgorithm)+len(signature.KeyId)+len(signature.Signature) { return nil, fmt.Errorf("invalid signature provided: incorrect size %v detected", len(sigBytes)) } copy(signature.SignatureAlgorithm[:], sigBytes[0:2]) copy(signature.KeyId[:], sigBytes[2:10]) copy(signature.Signature[:], sigBytes[10:]) } s.signature = &signature return &s, nil } // CanonicalValue implements the pki.Signature interface func (s Signature) CanonicalValue() ([]byte, error) { if s.signature == nil { return nil, fmt.Errorf("minisign signature has not been initialized") } buf := bytes.NewBuffer([]byte("untrusted comment:\n")) b64Buf := bytes.NewBuffer(s.signature.SignatureAlgorithm[:]) if _, err := b64Buf.Write(s.signature.KeyId[:]); err != nil { return nil, fmt.Errorf("error canonicalizing minisign signature: %w", err) } if _, err := b64Buf.Write(s.signature.Signature[:]); err != nil { return nil, fmt.Errorf("error canonicalizing minisign signature: %w", err) } if _, err := buf.WriteString(base64.StdEncoding.EncodeToString(b64Buf.Bytes())); err != nil { return nil, fmt.Errorf("error canonicalizing minisign signature: %w", err) } return buf.Bytes(), nil } // Verify implements the pki.Signature interface func (s Signature) Verify(r io.Reader, k interface{}, opts ...sigsig.VerifyOption) error { if s.signature == nil { return fmt.Errorf("minisign signature has not been initialized") } key, ok := k.(*PublicKey) if !ok { return fmt.Errorf("cannot use Verify with a non-minisign key") } if key.key == nil { return fmt.Errorf("minisign public key has not been initialized") } verifier, err := sigsig.LoadED25519Verifier(key.key.PublicKey[:]) if err != nil { return err } prehashed := s.signature.SignatureAlgorithm[1] == 0x44 if prehashed { h, _ := blake2b.New512(nil) _, err := io.Copy(h, r) if err != nil { return fmt.Errorf("reading minisign data") } r = bytes.NewReader(h.Sum(nil)) } return verifier.VerifySignature(bytes.NewReader(s.signature.Signature[:]), r, opts...) } // PublicKey Public Key that follows the minisign standard; supports signify and minisign public keys type PublicKey struct { key *minisign.PublicKey } // NewPublicKey implements the pki.PublicKey interface func NewPublicKey(r io.Reader) (*PublicKey, error) { var k PublicKey var inputBuffer bytes.Buffer if _, err := io.Copy(&inputBuffer, r); err != nil { return nil, fmt.Errorf("unable to read minisign public key: %w", err) } inputString := inputBuffer.String() // There are three ways a minisign key can be stored. // 1. The entire text key // 2. A base64 encoded string // 3. A legacy format we stored of just the key material (no key ID or Algorithm) due to bug fixed in https://github.com/sigstore/rekor/pull/562 key, err := minisign.DecodePublicKey(inputString) if err == nil { k.key = &key return &k, nil } key, err = minisign.NewPublicKey(inputString) if err == nil { k.key = &key return &k, nil } if len(inputString) == 32 { k.key = &minisign.PublicKey{ SignatureAlgorithm: [2]byte{'E', 'd'}, KeyId: [8]byte{}, } copy(k.key.PublicKey[:], inputBuffer.Bytes()) return &k, nil } return nil, fmt.Errorf("unable to read minisign public key: %w", err) } // CanonicalValue implements the pki.PublicKey interface func (k PublicKey) CanonicalValue() ([]byte, error) { if k.key == nil { return nil, fmt.Errorf("minisign public key has not been initialized") } bin := []byte{} bin = append(bin, k.key.SignatureAlgorithm[:]...) bin = append(bin, k.key.KeyId[:]...) bin = append(bin, k.key.PublicKey[:]...) b64Key := base64.StdEncoding.EncodeToString(bin) return []byte(b64Key), nil } // EmailAddresses implements the pki.PublicKey interface func (k PublicKey) EmailAddresses() []string { return nil } // Subjects implements the pki.PublicKey interface func (k PublicKey) Subjects() []string { return nil } // Identities implements the pki.PublicKey interface func (k PublicKey) Identities() ([]identity.Identity, error) { // PKIX encode ed25519 public key pkixKey, err := cryptoutils.MarshalPublicKeyToDER(ed25519.PublicKey(k.key.PublicKey[:])) if err != nil { return nil, err } digest := sha256.Sum256(pkixKey) return []identity.Identity{{ Crypto: k.key, Raw: pkixKey, Fingerprint: hex.EncodeToString(digest[:]), }}, nil } rekor-1.3.5/pkg/pki/minisign/minisign_e2e_test.go000066400000000000000000000076301455727245600220100ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package minisign import ( "path/filepath" "reflect" "strings" "testing" "github.com/sigstore/rekor/pkg/util" ) func TestMinisign(t *testing.T) { // Create a keypair keyPath := filepath.Join(t.TempDir(), "minisign.key") pubPath := filepath.Join(t.TempDir(), "minisign.pub") // Set an empty password, we have to hit enter twice to confirm util.Run(t, "\n\n", "minisign", "-G", "-s", keyPath, "-p", pubPath) // Create a random artifact and sign it. artifactPath := filepath.Join(t.TempDir(), "artifact") sigPath := filepath.Join(t.TempDir(), "signature.asc") util.CreateArtifact(t, artifactPath) // Send in one empty password over stdin out := util.Run(t, "\n", "minisign", "-S", "-s", keyPath, "-m", artifactPath, "-x", sigPath) t.Log(out) // Now upload to the log! out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath, "--pki-format", "minisign") util.OutputContains(t, out, "Created entry at") uuidA := util.GetUUIDFromUploadOutput(t, out) out = util.RunCli(t, "verify", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath, "--pki-format", "minisign") util.OutputContains(t, out, "Inclusion Proof") out = util.RunCli(t, "search", "--public-key", pubPath, "--pki-format", "minisign") util.OutputContains(t, out, uuidA) // crease a second artifact and sign it artifactPath_B := filepath.Join(t.TempDir(), "artifact2") util.CreateArtifact(t, artifactPath_B) out = util.Run(t, "\n", "minisign", "-S", "-s", keyPath, "-m", artifactPath_B, "-x", sigPath) // Now upload to the log! out = util.RunCli(t, "upload", "--artifact", artifactPath_B, "--signature", sigPath, "--public-key", pubPath, "--pki-format", "minisign") util.OutputContains(t, out, "Created entry at") uuidB := util.GetUUIDFromUploadOutput(t, out) tests := []struct { name string expectedUuidACount int expectedUuidBCount int artifact string operator string }{ { name: "artifact A AND signature should return artifact A", expectedUuidACount: 1, expectedUuidBCount: 0, artifact: artifactPath, operator: "and", }, { name: "artifact A OR signature should return artifact A and B", expectedUuidACount: 1, expectedUuidBCount: 1, artifact: artifactPath, operator: "or", }, { name: "artifact B AND signature should return artifact B", expectedUuidACount: 0, expectedUuidBCount: 1, artifact: artifactPath_B, operator: "and", }, { name: "artifact B OR signature should return artifact A and B", expectedUuidACount: 1, expectedUuidBCount: 1, artifact: artifactPath_B, operator: "or", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { out = util.RunCli(t, "search", "--public-key", pubPath, "--pki-format", "minisign", "--operator", test.operator, "--artifact", test.artifact) expected := map[string]int{uuidA: test.expectedUuidACount, uuidB: test.expectedUuidBCount} actual := map[string]int{ uuidA: strings.Count(out, uuidA), uuidB: strings.Count(out, uuidB), } if !reflect.DeepEqual(expected, actual) { t.Errorf("expected to find %v, found %v", expected, actual) } }) } } rekor-1.3.5/pkg/pki/minisign/minisign_test.go000066400000000000000000000313611455727245600212530ustar00rootroot00000000000000// Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package minisign import ( "bytes" "crypto/ed25519" "crypto/sha256" "crypto/x509" "encoding/hex" "errors" "io" "os" "reflect" "testing" "github.com/google/go-cmp/cmp" minisign "github.com/jedisct1/go-minisign" "github.com/sigstore/sigstore/pkg/cryptoutils" "go.uber.org/goleak" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestReadPublicKey(t *testing.T) { type test struct { caseDesc string inputFile string } tests := []test{ {caseDesc: "Valid public key (minisign)", inputFile: "testdata/minisign.pub"}, {caseDesc: "Valid public key (signify)", inputFile: "testdata/signify.pub"}, } for _, tc := range tests { t.Run(tc.caseDesc, func(t *testing.T) { file, err := os.Open(tc.inputFile) if err != nil { t.Fatalf("%v: cannot open %v", tc.caseDesc, tc.inputFile) } got, err := NewPublicKey(file) if err != nil { t.Fatalf("unexpected error %v", err) } // Try to send just the raw public key bytes too rawBytes := got.key.PublicKey[:] rawGot, err := NewPublicKey(bytes.NewReader(rawBytes)) if err != nil { t.Fatalf("unexpected error re-parsing public key: %v", err) } if !bytes.Equal(rawGot.key.PublicKey[:], rawBytes) { t.Errorf("expected parsed keys to be equal, %v != %v", rawGot.key.PublicKey, rawBytes) } ids, err := rawGot.Identities() if err != nil { t.Fatalf("unexpected error getting identities: %v", err) } if _, ok := ids[0].Crypto.(*minisign.PublicKey); !ok { t.Fatalf("key is of unexpected type, expected *minisign.PublicKey, got %v", reflect.TypeOf(ids[0].Crypto)) } expectedDer, err := cryptoutils.MarshalPublicKeyToDER(ed25519.PublicKey(rawBytes)) if err != nil { t.Fatalf("unexpected error generating DER: %v", err) } if !reflect.DeepEqual(expectedDer, ids[0].Raw) { t.Errorf("raw keys are not equal") } edKey, _ := x509.ParsePKIXPublicKey(ids[0].Raw) if err := cryptoutils.EqualKeys(edKey, ed25519.PublicKey(rawBytes)); err != nil { t.Errorf("public keys did not match: %v", err) } if len(ids[0].Fingerprint) != 64 { t.Errorf("%v: fingerprint is not expected length of 64 (hex 32-byte sha256): %d", tc.caseDesc, len(ids[0].Fingerprint)) } digest := sha256.Sum256(expectedDer) if hex.EncodeToString(digest[:]) != ids[0].Fingerprint { t.Fatalf("fingerprints don't match") } }) } } func TestReadPublicKeyErr(t *testing.T) { type test struct { caseDesc string inputFile string } tests := []test{ {caseDesc: "Not a valid public key file", inputFile: "testdata/hello_world.txt.minisig"}, {caseDesc: "Wrong length", inputFile: "testdata/hello_world.txt"}, } for _, tc := range tests { t.Run(tc.caseDesc, func(t *testing.T) { file, err := os.Open(tc.inputFile) if err != nil { t.Fatalf("%v: cannot open %v", tc.caseDesc, tc.inputFile) } got, err := NewPublicKey(file) if err == nil { t.Errorf("error expected, got nil error and %v", got) } }) } } func TestReadSignature(t *testing.T) { type test struct { caseDesc string inputFile string errorFound bool } tests := []test{ {caseDesc: "Not a valid signature file", inputFile: "testdata/minisign.pub", errorFound: true}, {caseDesc: "Valid minisign signature", inputFile: "testdata/hello_world.txt.minisig", errorFound: false}, {caseDesc: "Valid signify signature", inputFile: "testdata/hello_world.txt.signify", errorFound: false}, } for _, tc := range tests { file, err := os.Open(tc.inputFile) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.inputFile) } if got, err := NewSignature(file); ((got != nil) == tc.errorFound) || ((err != nil) != tc.errorFound) { t.Error(err) t.Errorf("%v: unexpected result testing %v: %v", tc.caseDesc, tc.inputFile, got) } } } type BadReader struct { } func (br BadReader) Read(_ []byte) (n int, err error) { return 0, errors.New("test error") } func TestReadErrorPublicKey(t *testing.T) { br := new(BadReader) if _, err := NewPublicKey(br); err == nil { t.Errorf("KnownBadReader: unexpected success testing a broken reader for public key") } } func TestReadErrorSignature(t *testing.T) { br := new(BadReader) if _, err := NewSignature(br); err == nil { t.Errorf("KnownBadReader: unexpected success testing a broken reader for signature") } } func TestCanonicalValueSignature(t *testing.T) { type test struct { caseDesc string inputFile string keyFile string sigFile string expectSuccess bool } var s Signature if _, err := s.CanonicalValue(); err == nil { t.Errorf("CanonicalValue did not error out for uninitialized signature") } tests := []test{ { caseDesc: "Minisign key with comment and canonicalized signature both verify the same file", inputFile: "testdata/hello_world.txt", keyFile: "testdata/minisign.pub", sigFile: "testdata/hello_world.txt.minisig", expectSuccess: true, }, { caseDesc: "Standalone minisign key and canonicalized signature both verify the same file", inputFile: "testdata/hello_world.txt", keyFile: "testdata/minisign_key_only.pub", sigFile: "testdata/hello_world.txt.minisig", expectSuccess: true, }, { caseDesc: "Signify key and canonicalized signature both verify the same file", inputFile: "testdata/hello_world.txt", keyFile: "testdata/signify.pub", sigFile: "testdata/hello_world.txt.signify", expectSuccess: true, }, } for _, tc := range tests { var err error inputFile, err := os.Open(tc.inputFile) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.inputFile) } sigFile, err := os.Open(tc.sigFile) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.sigFile) } keyFile, err := os.Open(tc.keyFile) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.keyFile) } key, err := NewPublicKey(keyFile) if err != nil { t.Errorf("%v: Error reading public key for TestCanonicalValueSignature: %v", tc.caseDesc, err) } sig, err := NewSignature(sigFile) if err != nil { t.Errorf("%v: Error reading signature for TestCanonicalValueSignature: %v", tc.caseDesc, err) } if err := sig.Verify(inputFile, key); err != nil { t.Errorf("%v: Error verifying pre-canonicalized signature for TestCanonicalValueSignature: %v", tc.caseDesc, err) } canonicalSigBytes, err := sig.CanonicalValue() if err != nil { t.Errorf("%v: Error canonicalizing signature '%v': %v", tc.caseDesc, tc.sigFile, err) } canonicalSig, err := NewSignature(bytes.NewReader(canonicalSigBytes)) if err != nil { t.Errorf("%v: Error reading canonicalized signature for TestCanonicalValueSignature: %v", tc.caseDesc, err) } else { _, _ = inputFile.Seek(0, io.SeekStart) if err := canonicalSig.Verify(inputFile, key); (err == nil) != tc.expectSuccess { t.Errorf("%v: canonical signature was unable to be verified: %v", tc.caseDesc, err) } } } } func TestCanonicalValuePublicKey(t *testing.T) { type test struct { caseDesc string input string output string match bool } var k PublicKey if _, err := k.CanonicalValue(); err == nil { t.Errorf("CanonicalValue did not error out for uninitialized key") } tests := []test{ {caseDesc: "key", input: "testdata/minisign.pub", output: "testdata/minisign_key_only.pub", match: true}, } for _, tc := range tests { var inputFile, outputFile io.Reader var err error inputFile, err = os.Open(tc.input) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.input) } inputKey, err := NewPublicKey(inputFile) if err != nil { t.Errorf("%v: Error reading input for TestCanonicalValuePublicKey: %v", tc.caseDesc, err) } cvInput, err := inputKey.CanonicalValue() if err != nil { t.Errorf("%v: Error canonicalizing public key '%v': %v", tc.caseDesc, tc.input, err) } outputFile, err = os.Open(tc.output) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.output) } outputKey, err := NewPublicKey(outputFile) if err != nil { t.Errorf("%v: Error reading input for TestCanonicalValuePublicKey: %v", tc.caseDesc, err) } cvOutput, err := outputKey.CanonicalValue() if err != nil { t.Errorf("%v: Error canonicalizing public key '%v': %v", tc.caseDesc, tc.input, err) } if bytes.Equal(cvInput, cvOutput) != tc.match { t.Errorf("%v: %v equality of canonical values of %v and %v was expected but not generated", tc.caseDesc, tc.match, tc.input, tc.output) } // The canonical values should be round-trippable rt, err := NewPublicKey(bytes.NewReader(cvInput)) if err != nil { t.Fatalf("error parsing canonicalized key: %v", err) } if diff := cmp.Diff(rt.key, inputKey.key); diff != "" { t.Error(diff) } // Identities should be equal to the canonical value for minisign ids, err := outputKey.Identities() if err != nil { t.Fatalf("unexpected error getting identities: %v", err) } if _, ok := ids[0].Crypto.(*minisign.PublicKey); !ok { t.Fatalf("key is of unexpected type, expected *minisign.PublicKey, got %v", reflect.TypeOf(ids[0].Crypto)) } expectedDer, err := cryptoutils.MarshalPublicKeyToDER(ed25519.PublicKey(rt.key.PublicKey[:])) if err != nil { t.Fatalf("unexpected error generating DER: %v", err) } if !reflect.DeepEqual(expectedDer, ids[0].Raw) { t.Errorf("raw keys are not equal") } edKey, _ := x509.ParsePKIXPublicKey(ids[0].Raw) if err := cryptoutils.EqualKeys(edKey, ed25519.PublicKey(rt.key.PublicKey[:])); err != nil { t.Errorf("public keys did not match: %v", err) } if len(ids[0].Fingerprint) != 64 { t.Errorf("%v: fingerprint is not expected length of 64 (hex 32-byte sha256): %d", tc.caseDesc, len(ids[0].Fingerprint)) } digest := sha256.Sum256(expectedDer) if hex.EncodeToString(digest[:]) != ids[0].Fingerprint { t.Fatalf("fingerprints don't match") } } } func TestVerifySignature(t *testing.T) { type test struct { caseDesc string dataFile string sigFile string keyFile string verified bool } tests := []test{ {caseDesc: "Valid Signature (minisign), Valid Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.minisig", keyFile: "testdata/minisign.pub", verified: true}, {caseDesc: "Valid Signature (minisign, prehashed), Valid Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world_hashed.txt.minisig", keyFile: "testdata/minisign_hashed.pub", verified: true}, {caseDesc: "Valid Signature (signify), Valid Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.signify", keyFile: "testdata/signify.pub", verified: true}, {caseDesc: "Valid Signature, Incorrect Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.minisig", keyFile: "testdata/signify.pub", verified: false}, {caseDesc: "Data does not match Signature", dataFile: "testdata/signify.pub", sigFile: "testdata/hello_world.txt.minisig", keyFile: "testdata/minisign.pub", verified: false}, } for _, tc := range tests { keyFile, err := os.Open(tc.keyFile) if err != nil { t.Errorf("%v: error reading keyfile '%v': %v", tc.caseDesc, tc.keyFile, err) } k, err := NewPublicKey(keyFile) if err != nil { t.Errorf("%v: error reading keyfile '%v': %v", tc.caseDesc, tc.keyFile, err) } sigFile, err := os.Open(tc.sigFile) if err != nil { t.Errorf("%v: error reading sigfile '%v': %v", tc.caseDesc, tc.sigFile, err) } s, err := NewSignature(sigFile) if err != nil { t.Errorf("%v: error reading sigfile '%v': %v", tc.caseDesc, tc.sigFile, err) } dataFile, err := os.Open(tc.dataFile) if err != nil { t.Errorf("%v: error reading datafile '%v': %v", tc.caseDesc, tc.dataFile, err) } if err := s.Verify(dataFile, k); (err == nil) != tc.verified { t.Errorf("%v: unexpected result in verifying sigature: %v", tc.caseDesc, err) } } emptyKey := PublicKey{} emptySig := Signature{} if err := emptySig.Verify(bytes.NewReader([]byte("irrelevant")), emptyKey); err == nil { t.Errorf("expected error when using empty sig to verify") } sigFile, _ := os.Open("testdata/hello_world.txt.minisig") validSig, _ := NewSignature(sigFile) if err := validSig.Verify(bytes.NewReader([]byte("irrelevant")), &emptyKey); err == nil { t.Errorf("expected error when using empty key to verify") } } rekor-1.3.5/pkg/pki/minisign/testdata/000077500000000000000000000000001455727245600176555ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/minisign/testdata/hello_world.txt000066400000000000000000000000161455727245600227250ustar00rootroot00000000000000Hello, World! rekor-1.3.5/pkg/pki/minisign/testdata/hello_world.txt.minisig000066400000000000000000000004571455727245600243740ustar00rootroot00000000000000untrusted comment: signature from minisign secret key RWQ/VK4mEZOSnVsP2aVAcwlCDu0V5VUqqGeE6mndH9v7wY4++PrZdB0HBRyVpt4/h0VDzQIvLenPpmTRSx1604Bac6Joz08phg4= trusted comment: timestamp:1610131681 file:hello_world.txt SbpK5wWmI+aoiwXBitvDWszRT9dwH8ZMzVaGn+WHZQXz+xnnAWCTmikCYnVv67iffkmZr24wZmnMok6Fvv8HDQ== rekor-1.3.5/pkg/pki/minisign/testdata/hello_world.txt.signify000066400000000000000000000002201455727245600243710ustar00rootroot00000000000000untrusted comment: verify with signify.pub RWSZyj9wTc0QvMrf5en3xQSpQcAZCzNyW23BBPBPjQuFVek3KGzNtNCv60pob32eGBL9ZuuiG36GnvcOwFodj7l9dl1jbzNR6QE= rekor-1.3.5/pkg/pki/minisign/testdata/hello_world_hashed.txt.minisig000066400000000000000000000004661455727245600257100ustar00rootroot00000000000000untrusted comment: signature from minisign secret key RURIeCI9VBgUB9kPHyUwRtxZycb78g9wT6d+oRuXEKquv665OMM6CI64Z+hGcKiJg2ErfA50FCgmdiUw4EHErNMivjYajjO4EAQ= trusted comment: timestamp:1643685548 file:hello_world.txt hashed cueBI9ab3mX+ZGQoBFSq49wrxZMTrLjX1Q0LlNhUmnA7dIptKj/KrpbfDJDCPtbxd3lbeo0zKGVNwpW/EQo3Dw== rekor-1.3.5/pkg/pki/minisign/testdata/minisign.pub000066400000000000000000000001611455727245600222000ustar00rootroot00000000000000untrusted comment: minisign public key 9D92931126AE543F RWQ/VK4mEZOSnVFf2NhEt9WV8zE1RcN8mtKeOO7mVjj/MCDvb5tSV6RD rekor-1.3.5/pkg/pki/minisign/testdata/minisign_hashed.pub000066400000000000000000000001571455727245600235210ustar00rootroot00000000000000untrusted comment: minisign public key 71418543D227848 RWRIeCI9VBgUB0FAABABUrdfRVLBsRhOC63S9bDOAeWkCmnT38a1sUDbrekor-1.3.5/pkg/pki/minisign/testdata/minisign_key_only.pub000066400000000000000000000000711455727245600241110ustar00rootroot00000000000000RWQ/VK4mEZOSnVFf2NhEt9WV8zE1RcN8mtKeOO7mVjj/MCDvb5tSV6RD rekor-1.3.5/pkg/pki/minisign/testdata/signify.pub000066400000000000000000000001371455727245600220360ustar00rootroot00000000000000untrusted comment: signify public key RWSZyj9wTc0QvAfiUA2zFbdxSpPGyXLc/Mcxn+7hd9f6+VP+jHu0bu8b rekor-1.3.5/pkg/pki/pgp/000077500000000000000000000000001455727245600150155ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/pgp/pgp.go000066400000000000000000000222111455727245600161300ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package pgp import ( "bufio" "bytes" "context" "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" "encoding/hex" "errors" "fmt" "io" "net/http" "github.com/asaskevich/govalidator" //TODO: https://github.com/sigstore/rekor/issues/286 "golang.org/x/crypto/openpgp" //nolint:staticcheck "golang.org/x/crypto/openpgp/armor" //nolint:staticcheck "golang.org/x/crypto/openpgp/packet" //nolint:staticcheck "github.com/sigstore/rekor/pkg/pki/identity" "github.com/sigstore/sigstore/pkg/cryptoutils" sigsig "github.com/sigstore/sigstore/pkg/signature" ) // Signature that follows the PGP standard; supports both armored & binary detached signatures type Signature struct { isArmored bool signature []byte } // NewSignature creates and validates a PGP signature object func NewSignature(r io.Reader) (*Signature, error) { var s Signature var inputBuffer bytes.Buffer if _, err := io.Copy(&inputBuffer, r); err != nil { return nil, fmt.Errorf("unable to read PGP signature: %w", err) } sigByteReader := bytes.NewReader(inputBuffer.Bytes()) var sigReader io.Reader sigBlock, err := armor.Decode(sigByteReader) if err == nil { s.isArmored = true if sigBlock.Type != openpgp.SignatureType { return nil, fmt.Errorf("invalid PGP signature provided") } sigReader = sigBlock.Body } else { s.isArmored = false if _, err := sigByteReader.Seek(0, io.SeekStart); err != nil { return nil, fmt.Errorf("unable to read binary PGP signature: %w", err) } sigReader = sigByteReader } sigPktReader := packet.NewReader(sigReader) sigPkt, err := sigPktReader.Next() if err != nil { return nil, fmt.Errorf("invalid PGP signature: %w", err) } if _, ok := sigPkt.(*packet.Signature); !ok { if _, ok := sigPkt.(*packet.SignatureV3); !ok { return nil, fmt.Errorf("valid PGP signature was not detected") } } s.signature = inputBuffer.Bytes() return &s, nil } // FetchSignature implements pki.Signature interface func FetchSignature(ctx context.Context, url string) (*Signature, error) { req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, fmt.Errorf("error initializing fetch for PGP signature: %w", err) } client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("error fetching PGP signature: %w", err) } defer resp.Body.Close() sig, err := NewSignature(resp.Body) if err != nil { return nil, err } return sig, nil } // CanonicalValue implements the pki.Signature interface func (s Signature) CanonicalValue() ([]byte, error) { if len(s.signature) == 0 { return nil, fmt.Errorf("PGP signature has not been initialized") } if s.isArmored { return s.signature, nil } var canonicalBuffer bytes.Buffer // Use an inner function so we can defer the Close() if err := func() error { ew, err := armor.Encode(&canonicalBuffer, openpgp.SignatureType, nil) if err != nil { return fmt.Errorf("error encoding canonical value of PGP signature: %w", err) } defer ew.Close() if _, err := io.Copy(ew, bytes.NewReader(s.signature)); err != nil { return fmt.Errorf("error generating canonical value of PGP signature: %w", err) } return nil }(); err != nil { return nil, err } return canonicalBuffer.Bytes(), nil } // Verify implements the pki.Signature interface func (s Signature) Verify(r io.Reader, k interface{}, _ ...sigsig.VerifyOption) error { if len(s.signature) == 0 { return fmt.Errorf("PGP signature has not been initialized") } key, ok := k.(*PublicKey) if !ok { return fmt.Errorf("cannot use Verify with a non-PGP signature") } if len(key.key) == 0 { return fmt.Errorf("PGP public key has not been initialized") } verifyFn := openpgp.CheckDetachedSignature if s.isArmored { verifyFn = openpgp.CheckArmoredDetachedSignature } if _, err := verifyFn(key.key, r, bytes.NewReader(s.signature)); err != nil { return err } return nil } // PublicKey Public Key that follows the PGP standard; supports both armored & binary detached signatures type PublicKey struct { key openpgp.EntityList } // NewPublicKey implements the pki.PublicKey interface func NewPublicKey(r io.Reader) (*PublicKey, error) { var k PublicKey var inputBuffer bytes.Buffer startToken := []byte(`-----BEGIN PGP`) endToken := []byte(`-----END PGP`) bufferedReader := bufio.NewReader(r) armorCheck, err := bufferedReader.Peek(len(startToken)) if err != nil { return nil, fmt.Errorf("unable to read PGP public key: %w", err) } if bytes.Equal(startToken, armorCheck) { // looks like we have armored input scan := bufio.NewScanner(bufferedReader) scan.Split(bufio.ScanLines) for scan.Scan() { line := scan.Bytes() inputBuffer.Write(line) fmt.Fprintf(&inputBuffer, "\n") if bytes.HasPrefix(line, endToken) { // we have a complete armored message; process it keyBlock, err := armor.Decode(&inputBuffer) if err == nil { if keyBlock.Type != openpgp.PublicKeyType && keyBlock.Type != openpgp.PrivateKeyType { return nil, fmt.Errorf("invalid PGP type detected") } keys, err := openpgp.ReadKeyRing(keyBlock.Body) if err != nil { return nil, fmt.Errorf("error reading PGP public key: %w", err) } if k.key == nil { k.key = keys } else { k.key = append(k.key, keys...) } inputBuffer.Reset() } else { return nil, fmt.Errorf("invalid PGP public key provided: %w", err) } } } } else { // process as binary k.key, err = openpgp.ReadKeyRing(bufferedReader) if err != nil { return nil, fmt.Errorf("error reading binary PGP public key: %w", err) } } if len(k.key) == len(k.key.DecryptionKeys()) { return nil, fmt.Errorf("no PGP public keys could be read") } return &k, nil } // FetchPublicKey implements pki.PublicKey interface func FetchPublicKey(ctx context.Context, url string) (*PublicKey, error) { //TODO: detect if url is hkp and adjust accordingly req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, fmt.Errorf("error fetching PGP public key: %w", err) } client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("error fetching PGP public key: %w", err) } defer resp.Body.Close() key, err := NewPublicKey(resp.Body) if err != nil { return nil, err } return key, nil } // CanonicalValue implements the pki.PublicKey interface func (k PublicKey) CanonicalValue() ([]byte, error) { if k.key == nil { return nil, fmt.Errorf("PGP public key has not been initialized") } var canonicalBuffer bytes.Buffer // Use an inner function so we can defer the close() if err := func() error { armoredWriter, err := armor.Encode(&canonicalBuffer, openpgp.PublicKeyType, nil) if err != nil { return fmt.Errorf("error generating canonical value of PGP public key: %w", err) } defer armoredWriter.Close() for _, entity := range k.key { if err := entity.Serialize(armoredWriter); err != nil { return fmt.Errorf("error generating canonical value of PGP public key: %w", err) } } return nil }(); err != nil { return nil, err } return canonicalBuffer.Bytes(), nil } func (k PublicKey) KeyRing() (openpgp.KeyRing, error) { if k.key == nil { return nil, errors.New("PGP public key has not been initialized") } return k.key, nil } // EmailAddresses implements the pki.PublicKey interface func (k PublicKey) EmailAddresses() []string { var names []string // Extract from cert for _, entity := range k.key { for _, identity := range entity.Identities { if govalidator.IsEmail(identity.UserId.Email) { names = append(names, identity.UserId.Email) } } } return names } // Subjects implements the pki.PublicKey interface func (k PublicKey) Subjects() []string { return k.EmailAddresses() } // Identities implements the pki.PublicKey interface func (k PublicKey) Identities() ([]identity.Identity, error) { var ids []identity.Identity for _, entity := range k.key { var keys []*packet.PublicKey keys = append(keys, entity.PrimaryKey) for _, subKey := range entity.Subkeys { keys = append(keys, subKey.PublicKey) } for _, pk := range keys { pubKey := pk.PublicKey // Only process supported types. Will ignore DSA // and ElGamal keys. // TODO: For a V2 PGP type, enforce on upload switch pubKey.(type) { case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey: default: continue } pkixKey, err := cryptoutils.MarshalPublicKeyToDER(pubKey) if err != nil { return nil, err } ids = append(ids, identity.Identity{ Crypto: pubKey, Raw: pkixKey, Fingerprint: hex.EncodeToString(pk.Fingerprint[:]), }) } } return ids, nil } rekor-1.3.5/pkg/pki/pgp/pgp_test.go000066400000000000000000000414421455727245600171760ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package pgp import ( "bytes" "context" "errors" "io" "net/http" "net/http/httptest" "os" "reflect" "sort" "testing" "go.uber.org/goleak" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestReadPublicKey(t *testing.T) { type test struct { caseDesc string inputFile string errorFound bool } tests := []test{ {caseDesc: "Not a valid armored public key file", inputFile: "testdata/hello_world.txt.asc.sig", errorFound: true}, {caseDesc: "Armored private key (should fail)", inputFile: "testdata/armored_private.pgp", errorFound: true}, {caseDesc: "Valid armored public key", inputFile: "testdata/valid_armored_public.pgp", errorFound: false}, {caseDesc: "Valid armored public key with multiple subentries", inputFile: "testdata/valid_armored_complex_public.pgp", errorFound: false}, {caseDesc: "Not a valid binary public key", inputFile: "testdata/bogus_binary.pgp", errorFound: true}, {caseDesc: "Binary private key (should fail)", inputFile: "testdata/binary_private.pgp", errorFound: true}, {caseDesc: "Valid binary public key", inputFile: "testdata/valid_binary_public.pgp", errorFound: false}, {caseDesc: "Valid binary public key with multiple subentries", inputFile: "testdata/valid_binary_complex_public.pgp", errorFound: false}, } for _, tc := range tests { file, err := os.Open(tc.inputFile) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.inputFile) } if got, err := NewPublicKey(file); ((got != nil) == tc.errorFound) || ((err != nil) != tc.errorFound) { t.Errorf("%v: unexpected result testing %v: %v", tc.caseDesc, tc.inputFile, err) } } } func TestReadSignature(t *testing.T) { type test struct { caseDesc string inputFile string errorFound bool } tests := []test{ {caseDesc: "Not a valid signature file", inputFile: "testdata/bogus_armored.pgp", errorFound: true}, {caseDesc: "Invalid armored signature", inputFile: "testdata/valid_armored_public.pgp", errorFound: true}, {caseDesc: "Valid armored signature", inputFile: "testdata/hello_world.txt.asc.sig", errorFound: false}, {caseDesc: "Valid binary signature", inputFile: "testdata/hello_world.txt.sig", errorFound: false}, {caseDesc: "Valid armored V3 signature", inputFile: "testdata/hello_world.txt.asc.v3.sig", errorFound: false}, {caseDesc: "Valid binary V3 signature", inputFile: "testdata/hello_world.txt.v3.sig", errorFound: false}, } for _, tc := range tests { file, err := os.Open(tc.inputFile) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.inputFile) } if got, err := NewSignature(file); ((got != nil) == tc.errorFound) || ((err != nil) != tc.errorFound) { t.Errorf("%v: unexpected result testing %v: %v", tc.caseDesc, tc.inputFile, got) } } } type BadReader struct { } func (br BadReader) Read(_ []byte) (n int, err error) { return 0, errors.New("test error") } func TestReadErrorPublicKey(t *testing.T) { br := new(BadReader) if _, err := NewPublicKey(br); err == nil { t.Errorf("KnownBadReader: unexpected success testing a broken reader for public key") } } func TestReadErrorSignature(t *testing.T) { br := new(BadReader) if _, err := NewSignature(br); err == nil { t.Errorf("KnownBadReader: unexpected success testing a broken reader for signature") } } func TestFetchPublicKey(t *testing.T) { type test struct { caseDesc string inputFile string errorFound bool } testServer := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { if r.URL.Path[1:] == "premature_close" { return } file, err := os.ReadFile(r.URL.Path[1:]) if err != nil { w.WriteHeader(http.StatusNotFound) return } w.WriteHeader(http.StatusOK) _, _ = w.Write(file) })) defer testServer.Close() tests := []test{ {caseDesc: "Not a valid URL", inputFile: "%invalid_url%", errorFound: true}, {caseDesc: "HTTP server prematurely closes transaction", inputFile: "premature_close", errorFound: true}, {caseDesc: "404 error fetching content", inputFile: "not_a_file", errorFound: true}, {caseDesc: "Invalid public key", inputFile: "testdata/bogus_armored.pgp", errorFound: true}, {caseDesc: "Private key (should fail)", inputFile: "testdata/armored_private.pgp", errorFound: true}, {caseDesc: "Valid armored public key", inputFile: "testdata/valid_armored_public.pgp", errorFound: false}, {caseDesc: "Valid armored public key with multiple subentries", inputFile: "testdata/valid_armored_complex_public.pgp", errorFound: false}, } for _, tc := range tests { if got, err := FetchPublicKey(context.TODO(), testServer.URL+"/"+tc.inputFile); ((got != nil) == tc.errorFound) || ((err != nil) != tc.errorFound) { t.Errorf("%v: unexpected result testing %v: %v", tc.caseDesc, tc.inputFile, got) } } } func TestFetchSignature(t *testing.T) { type test struct { caseDesc string inputFile string errorFound bool } testServer := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { if r.URL.Path[1:] == "premature_close" { return } file, err := os.ReadFile(r.URL.Path[1:]) if err != nil { w.WriteHeader(http.StatusNotFound) return } w.WriteHeader(http.StatusOK) _, _ = w.Write(file) })) defer testServer.Close() tests := []test{ {caseDesc: "Not a valid URL", inputFile: "%invalid_url%", errorFound: true}, {caseDesc: "HTTP server prematurely closes transaction", inputFile: "premature_close", errorFound: true}, {caseDesc: "404 error fetching content", inputFile: "not_a_file", errorFound: true}, {caseDesc: "Invalid signature", inputFile: "testdata/bogus_armored.pgp", errorFound: true}, {caseDesc: "Valid armored signature", inputFile: "testdata/hello_world.txt.asc.sig", errorFound: false}, {caseDesc: "Valid binary signature", inputFile: "testdata/hello_world.txt.sig", errorFound: false}, } for _, tc := range tests { if got, err := FetchSignature(context.TODO(), testServer.URL+"/"+tc.inputFile); ((got != nil) == tc.errorFound) || ((err != nil) != tc.errorFound) { t.Errorf("%v: unexpected result testing %v: %v", tc.caseDesc, tc.inputFile, got) } } } func TestCanonicalValueSignature(t *testing.T) { type test struct { caseDesc string inputFile string keyFile string sigFile string expectSuccess bool } var s Signature if _, err := s.CanonicalValue(); err == nil { t.Errorf("CanonicalValue did not error out for uninitialized signature") } tests := []test{ { caseDesc: "Binary signature and canonicalized (armored) signature both verify the same file", inputFile: "testdata/hello_world.txt", keyFile: "testdata/valid_armored_public.pgp", sigFile: "testdata/hello_world.txt.sig", expectSuccess: true, }, { caseDesc: "Armored signature and canonicalized (armored) signature both verify the same file", inputFile: "testdata/hello_world.txt", keyFile: "testdata/valid_armored_public.pgp", sigFile: "testdata/hello_world.txt.asc.sig", expectSuccess: true, }, { caseDesc: "Binary V3 signature and canonicalized (armored) signature both verify the same file", inputFile: "testdata/hello_world.txt", keyFile: "testdata/valid_armored_public.pgp", sigFile: "testdata/hello_world.txt.v3.sig", expectSuccess: true, }, { caseDesc: "Armored V3 signature and canonicalized (armored) signature both verify the same file", inputFile: "testdata/hello_world.txt", keyFile: "testdata/valid_armored_public.pgp", sigFile: "testdata/hello_world.txt.asc.v3.sig", expectSuccess: true, }, } for _, tc := range tests { var err error inputFile, err := os.Open(tc.inputFile) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.inputFile) } sigFile, err := os.Open(tc.sigFile) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.sigFile) } keyFile, err := os.Open(tc.keyFile) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.keyFile) } key, err := NewPublicKey(keyFile) if err != nil { t.Errorf("%v: Error reading public key for TestCanonicalValueSignature: %v", tc.caseDesc, err) } sig, err := NewSignature(sigFile) if err != nil { t.Errorf("%v: Error reading signature for TestCanonicalValueSignature: %v", tc.caseDesc, err) } if err := sig.Verify(inputFile, key); err != nil { t.Errorf("%v: Error verifying pre-canonicalized signature for TestCanonicalValueSignature: %v", tc.caseDesc, err) } canonicalSigBytes, err := sig.CanonicalValue() if err != nil { t.Errorf("%v: Error canonicalizing signature '%v': %v", tc.caseDesc, tc.sigFile, err) } canonicalSig, err := NewSignature(bytes.NewReader(canonicalSigBytes)) if err != nil { t.Errorf("%v: Error reading canonicalized signature for TestCanonicalValueSignature: %v", tc.caseDesc, err) } _, _ = inputFile.Seek(0, io.SeekStart) if err := canonicalSig.Verify(inputFile, key); (err == nil) != tc.expectSuccess { t.Errorf("%v: canonical signature was unable to be verified: %v", tc.caseDesc, err) } } } func TestCanonicalValuePublicKey(t *testing.T) { type test struct { caseDesc string input string output string match bool } var k PublicKey if _, err := k.CanonicalValue(); err == nil { t.Errorf("CanonicalValue did not error out for uninitialized key") } tests := []test{ {caseDesc: "Binary and Armored versions of same key", input: "testdata/valid_binary_public.pgp", output: "testdata/valid_armored_public.pgp", match: true}, {caseDesc: "Complex binary and armored versions of same key", input: "testdata/valid_binary_complex_public.pgp", output: "testdata/valid_armored_complex_public.pgp", match: true}, } for _, tc := range tests { var inputFile, outputFile io.Reader var err error inputFile, err = os.Open(tc.input) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.input) } inputKey, err := NewPublicKey(inputFile) if err != nil { t.Errorf("%v: Error reading input for TestCanonicalValuePublicKey: %v", tc.caseDesc, err) } cvInput, err := inputKey.CanonicalValue() if err != nil { t.Errorf("%v: Error canonicalizing public key '%v': %v", tc.caseDesc, tc.input, err) } outputFile, err = os.Open(tc.output) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.output) } outputKey, err := NewPublicKey(outputFile) if err != nil { t.Errorf("%v: Error reading input for TestCanonicalValuePublicKey: %v", tc.caseDesc, err) } cvOutput, err := outputKey.CanonicalValue() if err != nil { t.Errorf("%v: Error canonicalizing public key '%v': %v", tc.caseDesc, tc.input, err) } if bytes.Equal(cvInput, cvOutput) != tc.match { t.Errorf("%v: %v equality of canonical values of %v and %v was expected but not generated", tc.caseDesc, tc.match, tc.input, tc.output) } } } func TestEmailAddresses(t *testing.T) { type test struct { caseDesc string inputFile string subjects []string // number of keys in key ring // verified with gpg, ignoring DSA/ElGamal keys keys int } var k PublicKey if len(k.Subjects()) != 0 { t.Errorf("Subjects for unitialized key should give empty slice") } tests := []test{ {caseDesc: "Valid armored public key", inputFile: "testdata/valid_armored_public.pgp", subjects: []string{}, keys: 2}, {caseDesc: "Valid armored public key with multiple subentries", inputFile: "testdata/valid_armored_complex_public.pgp", subjects: []string{"linux-packages-keymaster@google.com", "linux-packages-keymaster@google.com"}, keys: 4}, {caseDesc: "Valid binary public key", inputFile: "testdata/valid_binary_public.pgp", subjects: []string{}, keys: 2}, {caseDesc: "Valid binary public key with multiple subentries", inputFile: "testdata/valid_binary_complex_public.pgp", subjects: []string{"linux-packages-keymaster@google.com", "linux-packages-keymaster@google.com"}, keys: 4}, } for _, tc := range tests { var input io.Reader var err error input, err = os.Open(tc.inputFile) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.inputFile) } inputKey, err := NewPublicKey(input) if err != nil { t.Errorf("%v: Error reading input for TestEmailAddresses: %v", tc.caseDesc, err) } subjects := inputKey.Subjects() if len(subjects) == len(tc.subjects) { if len(subjects) > 0 { sort.Strings(subjects) sort.Strings(tc.subjects) if !reflect.DeepEqual(subjects, tc.subjects) { t.Errorf("%v: Error getting subjects from keys, got %v, expected %v", tc.caseDesc, subjects, tc.subjects) } } } else { t.Errorf("%v: Error getting subjects from keys length, got %v, expected %v", tc.caseDesc, len(subjects), len(tc.subjects)) } ids, err := inputKey.Identities() if err != nil { t.Fatalf("%v: unexpected error getting identities: %v", tc.caseDesc, err) } if len(ids) != tc.keys { t.Fatalf("%v: expected %d keys, got %d", tc.caseDesc, tc.keys, len(ids)) } } } func TestVerifySignature(t *testing.T) { type test struct { caseDesc string dataFile string sigFile string keyFile string verified bool } tests := []test{ {caseDesc: "Valid Armored Signature, Armored Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.asc.sig", keyFile: "testdata/valid_armored_public.pgp", verified: true}, {caseDesc: "Valid Armored Signature, Binary Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.asc.sig", keyFile: "testdata/valid_binary_public.pgp", verified: true}, {caseDesc: "Valid Binary Signature, Armored Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.sig", keyFile: "testdata/valid_armored_public.pgp", verified: true}, {caseDesc: "Valid Binary Signature, Binary Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.sig", keyFile: "testdata/valid_binary_public.pgp", verified: true}, {caseDesc: "Valid V3 Armored Signature, Armored Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.asc.v3.sig", keyFile: "testdata/valid_armored_public.pgp", verified: true}, {caseDesc: "Valid V3 Armored Signature, Binary Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.asc.v3.sig", keyFile: "testdata/valid_binary_public.pgp", verified: true}, {caseDesc: "Valid V3 Binary Signature, Armored Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.v3.sig", keyFile: "testdata/valid_armored_public.pgp", verified: true}, {caseDesc: "Valid V3 Binary Signature, Binary Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.v3.sig", keyFile: "testdata/valid_binary_public.pgp", verified: true}, {caseDesc: "Valid Signature, Incorrect Key", dataFile: "testdata/hello_world.txt", sigFile: "testdata/hello_world.txt.sig", keyFile: "testdata/valid_binary_complex_public.pgp", verified: false}, {caseDesc: "Data does not match Signature", dataFile: "testdata/armored_private.pgp", sigFile: "testdata/hello_world.txt.sig", keyFile: "testdata/valid_binary_complex_public.pgp", verified: false}, } for _, tc := range tests { keyFile, err := os.Open(tc.keyFile) if err != nil { t.Errorf("%v: error reading keyfile '%v': %v", tc.caseDesc, tc.keyFile, err) } k, err := NewPublicKey(keyFile) if err != nil { t.Errorf("%v: error reading keyfile '%v': %v", tc.caseDesc, tc.keyFile, err) } sigFile, err := os.Open(tc.sigFile) if err != nil { t.Errorf("%v: error reading sigfile '%v': %v", tc.caseDesc, tc.sigFile, err) } s, err := NewSignature(sigFile) if err != nil { t.Errorf("%v: error reading sigfile '%v': %v", tc.caseDesc, tc.sigFile, err) } dataFile, err := os.Open(tc.dataFile) if err != nil { t.Errorf("%v: error reading datafile '%v': %v", tc.caseDesc, tc.dataFile, err) } if err := s.Verify(dataFile, k); (err == nil) != tc.verified { t.Errorf("%v: unexpected result in verifying sigature: %v", tc.caseDesc, err) } } emptyKey := PublicKey{} emptySig := Signature{} if err := emptySig.Verify(bytes.NewReader([]byte("irrelevant")), emptyKey); err == nil { t.Errorf("expected error when using empty sig to verify") } sigFile, _ := os.Open("testdata/hello_world.txt.sig") validSig, _ := NewSignature(sigFile) if err := validSig.Verify(bytes.NewReader([]byte("irrelevant")), &emptyKey); err == nil { t.Errorf("expected error when using empty key to verify") } } rekor-1.3.5/pkg/pki/pgp/testdata/000077500000000000000000000000001455727245600166265ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/pgp/testdata/armored_private.pgp000066400000000000000000000066421455727245600225310ustar00rootroot00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- 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 bzrHSllcqKXPA7Q75DorBuM0+Ro= =g6El -----END PGP PRIVATE KEY BLOCK----- rekor-1.3.5/pkg/pki/pgp/testdata/binary_private.pgp000066400000000000000000000147321455727245600223630ustar00rootroot00000000000000lQOYBF+taPsBCADav3G36B5l+41rvf1nqyr7icxCMcjr1Jspxk5jCuQ6uKXqFfLkcCNURfR3AvOt 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 rekor-1.3.5/pkg/pki/pgp/testdata/bogus_armored.pgp000066400000000000000000000000121455727245600221570ustar00rootroot00000000000000not a key rekor-1.3.5/pkg/pki/pgp/testdata/bogus_binary.pgp000066400000000000000000000000211455727245600220120ustar00rootroot00000000000000bm90IGEga2V5Cg== rekor-1.3.5/pkg/pki/pgp/testdata/hello_world.txt000066400000000000000000000000161455727245600216760ustar00rootroot00000000000000Hello, World! rekor-1.3.5/pkg/pki/pgp/testdata/hello_world.txt.asc.sig000066400000000000000000000007701455727245600232330ustar00rootroot00000000000000-----BEGIN PGP SIGNATURE----- iQFBBAABCAArFiEEYbwpsb+sQzMSvoE6hvV1Up0Pn/QFAl+0DMkNHG5vdEByZWFs LmNvbQAKCRCG9XVSnQ+f9JpxCACn4k4wc8MDHGf0c0JdCtOAbU8YUesgosG/y4u0 iLebAN1H33fLRLLsdxHPCen7h9iW59Fx+p5QhAGV6v0WgR/niTx/pky0s6qX6jGd lNsbtJNXKQHXfG4thk5UGr3fcvSCrNpu7v+tONJ0fT25JClyhEL+HnAnMPICnFqS a0JfXpROzmfePWP9/L0uwU3ukT810FhfS0u7rImWJc3GnIWX9Zl6Jz7DdfzNwWTy yEltBtEWH4qYWOKvkFUvtjiYNcDlkdYRpU15RmWx2dSgwJt/YQoUkY1uTU42OWMq 36CTt+Vtoclj7HnAY7kBcIeFoVDvmoA1InTsyl/dJJQZUSVx =b7pM -----END PGP SIGNATURE----- rekor-1.3.5/pkg/pki/pgp/testdata/hello_world.txt.asc.v3.sig000066400000000000000000000007701455727245600235620ustar00rootroot00000000000000-----BEGIN PGP SIGNATURE----- iQFBBAABCAArFiEEYbwpsb+sQzMSvoE6hvV1Up0Pn/QFAl++kGUNHG5vdEByZWFs LmNvbQAKCRCG9XVSnQ+f9BslB/0ScA/dcsrJX5wQu1qRESV1Qqcjgl8x2BhNeOYu qNDVYIBjz5Rwe2P42I5DY1hNpMbMBSpyiavVHxBx5iHmig1iILHOsnG/DxVs8jNX MmtTwMDOaqw5kRbPeQKofr0i/ap+e5amE1qcpsPHpt7pcyhs2gclfDxfX9Csd52o y77XQ4Mg1chlvS10GGr3B789CXYcJxKAYeCBJD6sTk0J47kXK5x0QEtaHp9G+UdC oMcGifLy2g3kHuzzbSY7KMxfa5hKNlcpbopMaH/WMFCmEhgeZT3cINjvH1grYo9J Kg+SQ+Kk8lqoWpJJB2W5FN9RZY38Pj24QGT6FBV0D1mJlImp =ESQ0 -----END PGP SIGNATURE----- rekor-1.3.5/pkg/pki/pgp/testdata/hello_world.txt.sig000066400000000000000000000005041455727245600224610ustar00rootroot00000000000000A+!a)C3:uR_ not@real.com uRqN0sgsB] ӀmOQ ˋGwDw ؖqP<L1W)|n-NTrn8t}=$)rBp'0ZkB_^Ng=c.M?5X_KK%Ɯz'>udImX⯐U/85MyFeԠa nMN69c*ߠmcycpP5"t_$Q%qrekor-1.3.5/pkg/pki/pgp/testdata/hello_world.txt.v3.sig000066400000000000000000000005041455727245600230100ustar00rootroot00000000000000A+!a)C3:uR_e not@real.com uR%pr_Z%uB#_1Mx.`cϔp{c؎CcXM*rq! b βql3W2kSj9y~"~{ZǦs(l%|<__Ьw˾C e-tj= v'a$>NM +t@KZFGB m&;(_kJ6W)nLh0Pe= X+bI*CZZIeQe>=@dtYrekor-1.3.5/pkg/pki/pgp/testdata/valid_armored_complex_public.pgp000066400000000000000000000237521455727245600252440ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.2.2 (GNU/Linux) mQGiBEXwb0YRBADQva2NLpYXxgjNkbuP0LnPoEXruGmvi3XMIxjEUFuGNCP4Rj/a kv2E5VixBP1vcQFDRJ+p1puh8NU0XERlhpyZrVMzzS/RdWdyXf7E5S8oqNXsoD1z fvmI+i9b2EhHAA19Kgw7ifV8vMa4tkwslEmcTiwiw8lyUl28Wh4Et8SxzwCggDcA feGqtn3PP5YAdD0km4S4XeMEAJjlrqPoPv2Gf//tfznY2UyS9PUqFCPLHgFLe80u QhI2U5jt6jUKN4fHauvR6z3seSAsh1YyzyZCKxJFEKXCCqnrFSoh4WSJsbFNc4PN b0V0SqiTCkWADZyLT5wll8sWuQ5ylTf3z1ENoHf+G3um3/wk/+xmEHvj9HCTBEXP 78X0A/0Tqlhc2RBnEf+AqxWvM8sk8LzJI/XGjwBvKfXe+l3rnSR2kEAvGzj5Sg0X 4XmfTg4Jl8BNjWyvm2Wmjfet41LPmYJKsux3g0b8yzQxeOA4pQKKAU3Z4+rgzGmf HdwCG5MNT2A5XxD/eDd+L4fRx0HbFkIQoAi1J3YWQSiTk15fw7RMR29vZ2xlLCBJ bmMuIExpbnV4IFBhY2thZ2UgU2lnbmluZyBLZXkgPGxpbnV4LXBhY2thZ2VzLWtl eW1hc3RlckBnb29nbGUuY29tPohjBBMRAgAjAhsDBgsJCAcDAgQVAggDBBYCAwEC HgECF4AFAkYVdn8CGQEACgkQoECDD3+sWZHKSgCfdq3HtNYJLv+XZleb6HN4zOcF AJEAniSFbuv8V5FSHxeRimHx25671az+uQINBEXwb0sQCACuA8HT2nr+FM5y/kzI A51ZcC46KFtIDgjQJ31Q3OrkYP8LbxOpKMRIzvOZrsjOlFmDVqitiVc7qj3lYp6U rgNVaFv6Qu4bo2/ctjNHDDBdv6nufmusJUWq/9TwieepM/cwnXd+HMxu1XBKRVk9 XyAZ9SvfcW4EtxVgysI+XlptKFa5JCqFM3qJllVohMmr7lMwO8+sxTWTXqxsptJo pZeKz+UBEEqPyw7CUIVYGC9ENEtIMFvAvPqnhj1GS96REMpry+5s9WKuLEaclWpd K3krttbDlY1NaeQUCRvBYZ8iAG9YSLHUHMTuI2oea07Rh4dtIAqPwAX8xn36JAYG 2vgLAAMFB/wKqaycjWAZwIe98Yt0qHsdkpmIbarD9fGiA6kfkK/UxjL/k7tmS4Vm CljrrDZkPSQ/19mpdRcGXtb0NI9+nyM5trweTvtPw+HPkDiJlTaiCcx+izg79Fj9 KcofuNb3lPdXZb9tzf5oDnmm/B+4vkeTuEZJ//IFty8cmvCpzvY+DAz1Vo9rA+Zn cpWY1n6z6oSS9AsyT/IFlWWBZZ17SpMHu+h4Bxy62+AbPHKGSujEGQhWq8ZRoJAT G0KSObnmZ7FwFWu1e9XFoUCt0bSjiJWTIyaObMrWu/LvJ3e9I87HseSJStfw6fki 5og9qFEkMrIrBCp3QGuQWBq/rTdMuwNFiEkEGBECAAkFAkXwb0sCGwwACgkQoECD D3+sWZF/WACfeNAu1/1hwZtUo1bR+MWiCjpvHtwAnA1R3IHqFLQ2X3xJ40XPuAyY /FJG =Quqp -----END PGP PUBLIC KEY BLOCK----- -----BEGIN PGP PUBLIC KEY BLOCK----- mQINBFcMjNMBEAC6Wr5QuLIFgz1V1EFPlg8ty2TsjQEl4VWftUAqWlMevJFWvYEx BOsOZ6kNFfBfjAxgJNWTkxZrHzDl74R7KW/nUx6X57bpFjUyRaB8F3/NpWKSeIGS pJT+0m2SgUNhLAn1WY/iNJGNaMl7lgUnaP+/ZsSNT9hyTBiH3Ev5VvAtMGhVI/u8 P0EtTjXp4o2U+VqFTBGmZ6PJVhCFjZUeRByloHw8dGOshfXKgriebpioHvU8iQ2U GV3WNIirB2Rq1wkKxXJ/9Iw+4l5m4GmXMs7n3XaYQoBj28H86YA1cYWSm5LR5iU2 TneI1fJ3vwF2vpSXVBUUDk67PZhg6ZwGRT7GFWskC0z8PsWd5jwK20mA8EVKq0vN BFmMK6i4fJU+ux17Rgvnc9tDSCzFZ1/4f43EZ41uTmmNXIDsaPCqwjvSS5ICadt2 xeqTWDlzONUpOs5yBjF1cfJSdVxsfshvln2JXUwgIdKl4DLbZybuNFXnPffNLb2v PtRJHO48O2UbeXS8n27PcuMoLRd7+r7TsqG2vBH4t/cB/1vsvWMbqnQlaJ5VsjeW Tp8Gv9FJiKuU8PKiWsF4EGR/kAFyCB8QbJeQ6HrOT0CXLOaYHRu2TvJ4taY9doXn 98TgU03XTLcYoSp49cdkkis4K+9hd2dUqARVCG7UVd9PY60VVCKi47BVKQARAQAB tFRHb29nbGUgSW5jLiAoTGludXggUGFja2FnZXMgU2lnbmluZyBBdXRob3JpdHkp IDxsaW51eC1wYWNrYWdlcy1rZXltYXN0ZXJAZ29vZ2xlLmNvbT6JAjgEEwECACIF AlcMjNMCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEHch9jvTi0eW5CAP /RELE/OAoA4o1cMBxJsljWgCgDig2Ge91bFCN0vExLcP0iByra7qPWJowXDJ5sCj UBnCkrxGo5D15U7cW5FC0+qWU73q0AuG3OjKDQ49ecdRkYHwcvwWQvT5Lz3DwOGW 4armfEuzWXcUDeShR7AgfcTq+Pfoo3dHqdB8TmtNySu/AdJFmVH/xTiWYWrOSibh yLuaSW/0cTkHW0GDk06MlDkcdkTzhO5GMDO7PUxBgCysTXFR0T9TVWDo9VwvuMww 2pE5foleA0X6PD/6GQpy3aX2xry8rhFvYplEa5zwXhqsscdKXlp1ZPZ4PMvvwe49 5mY9n/1Rx1TmMvIcLHKP61sURMOve97Gipk/iD6oaeeT8I0khexHCQy7JMROoPMr z5onVOt2rAGZScIZsm5FYGSt9eDKBWI6qpJ/5QoVhkRWjOXOchZlJHo+kLdg6jq2 vOnIlFnXo0p6Rqf/IEq5PMh70vVZpk4tNYNy4zRx03ZTA9qXRLW+ftxSQIYMY5eC Z31lqSH4EjqgtUG+zn2A6juKayb1nkt2O3F1wWOm6oTzNsAP5LdReJRlw151Jp4U 4ftGtw7ygq+nvokXL7YLuu8sbFqfFXcTPrAZa5M9gnC7GCnIQyF/WvqUnrcaC1jp qBc+pkSJhROhN12QY8Po8AT8/UaUh/dPIiW5A4o8pOPEiEYEEBECAAYFAlcNtn8A CgkQoECDD3+sWZGy3wCfWTMZWsipX+yG/VB4Q1FunIfEVHYAnimEXCjZ3IVyy5F1 yU36PihDCjWqiEYEEBECAAYFAlcNtvEACgkQMUcsOzG36APnRwCeJ/bfGf8FBa4q 5TMw8p1GS1jWT5EAn2sc02481HHdTmZiW/CGWXmgE+OPuQINBFcMjcgBEACrL9gH hdr6gQX4ZMA5slp628xOrHCsdLO54WNdPRKeFHXJqSSJi3fs8FxBWI4FnejeKUGb F+MrOlFpKqELxaMje7bwZyap3izztZHszP3YmOoTBJvREGKdCkL82cLsChYD/Prg E8crvkhSnq9evcsKAnziMxg/wDCChUL3Evqo29BeoB81f+E9wkrUTMCT/kVxt3pG RalKX0UhrtKrpm8yRfjufJfwjkdwgvinkRGZ2GrWHj4LzMbi9/udYaJZ66Yw0hEU 4USxUB9vNtmSFrb4EB91T2rhc68dgQ4jYBI7K4Ebb8XaWAxb+IAq31l1UkiEA32F 4qUMoL6rChB4y6nHxOnTvs+XEb5TBwXVogjLRKTQs5U/HV9l7j+HAchk5y3im2N2 UKmMxHqotvPZZUZPdaCRxUedQf9gR0yLZV+U9BcDuwjzL/zjrthNZYlEGJ6HZ/TL STp4dDH+uXuLqMVWy5iquKtnbrnNTQtv5twD+Ajpgy60YLOJ9YaiJ4GjifOpzSk8 3e1rJ3p/pX6B5NWQinVLZJzxyeOoh3iMjdmCDSnEXLrCmYv5g6jyV/Wbd4GYFuMK 8TT7+PQdWLcbZ/Lxc5w0s+c7+f5OfmKXO5KPHnnUsrF5DBaKRPjScpwePQitxeIg lUgEMDkNruBhu1PzCxd3BtXgu++K3WdoH3VcgwARAQABiQREBBgBAgAPBQJXDI3I AhsCBQkFo5qAAikJEHch9jvTi0eWwV0gBBkBAgAGBQJXDI3IAAoJEBOXvFNkDbVR QSYP/0Ewr3T7e0soTz8g4QJLLVqZDZdX8Iez04idNHuvAu0AwdZ2wl0C+tMkD7l4 R2aI6BKe/9wPndk/NJe+ZYcD/uzyiKIJQD48PrifNnwvHu9A80rE4BppQnplENeh ibbWaGNJQONGFJx7QTYlFjS5LNlG1AX6mQjxvb423zOWSOmEamYXYBmYyMG6vkr/ XTPzsldky8XFuPrJUZslL/Wlx31XQ1IrtkHHOYqWwr0hTc50/2O8H0ewl/dBZLq3 EminZZ+tsTugof0j4SbxYhplw99nGwbN1uXy4L8/dWOUXnY5OgaTKZPF15zRMxXN 9FeylBVYpp5kzre/rRI6mQ2lafYHdbjvd7ryHF5JvYToSDXd0mzF2nLzm6jwsO84 7ZNd5GdTD6/vcef1IJta1nSwA/hhLtgtlz6/tNncp3lEdCjAMx29jYPDX+Lqs9JA xcJHufr82o6wM9TF24Q8ra8NbvB63odVidCfiHoOsIFDUrazH8XuaQzyZkI0bbzL mgMAvMO6u1zPfe/TK6LdJg7AeAKScOJS38D5mmwaD1bABr67ebA/X5HdaomSDKVd UYaewfTGBIsrWmCmKpdb+WfX4odFpNzXW/qskiBp5WSesKvN1QUkLJZDZD1kz2++ Xul5B97s5LxLTLRwvgLoNaUFr3lnejzNLgdBpf6FnkA59syRUuIP/jiAZ2uJzXVK PeRJqMGL+Ue2HiVEe8ima3SQIceqW8jKS7c7Nic6dMWxgnDpk5tJmVjrgfc0a9c1 FY4GomUBbZFj+j73+WRk3EaVKIsty+xz48+rlJjdYFVCJo0Jp67jjjXOt6EOHTni OA/ANtzRIzDMnWrwJZ7AxCGJ4YjLShkcRM9S30X0iuAkxNILX++SNOd8aqc2bFof yTCkcbk6CIc1W00vffv1QGTNjstNpVSl9+bRmlJDqJWnDGk5Nl4Ncqd8X51V0tYE g6WEK4OM83wx5Ew/TdTRq5jJkbCu2GYNaNNNgXW7bXSvT5VINbuP6dmbi1/8s0jK JQOEBI3RxxoB+01Dgx9YdNfjsCM3hvQvykaWMALeZIpzbXxV118Y9QQUIRe2L+4X ZACEAhWjj2K1wP7ODGTQrrM4q4sIw1l3l7yO9aXXN7likAAddT4WEpGV0CiorReO J1y/sKJRJSI/npN1UK7wMazZ+yzhxN0qzG8sqREKJQnNuuGQQ/qIGb/oe4dPO0Fi hAUGkWoa0bgtGVijN5fQSbMbV50kZYqaa9GnNQRnchmZb+pK2xLcK85hD1np37/A m5o2ggoONj3qI3JaRHsZaOs1qPQcyd46OyIFUpHJIfk4nezDCoQYd93bWUGqDwxI /n/CsdO0365yqDO/ADscehlVqdAupVv2uQINBFiGv8wBEACtrmK7c12DfxkPAJSD 12VanxLLvvjYW0KEWKxN6TMRQCawLhGwFf7FLNpab829DFMhBcNVgJ8aU0YIIu9f HroIaGi+bkBkDkSWEhSTlYa6ISfBn6Zk9AGBWB/SIelOncuAcI/Ik6BdDzIXnDN7 cXsMgV1ql7jIbdbsdX63wZEFwqbaiL1GWd4BUKhj0H46ZTEVBLl0MfHNlYl+X3ib 9WpRS6iBAGOWs8Kqw5xVE7oJm9DDXXWOdPUE8/FVti+bmOz+ICwQETY9I2EmyNXy UG3iaKs07VAf7SPHhgyBEkMngt5ZGcH4gs1m2l/HFQ0StNFNhXuzlHvQhDzd9M1n qpstEe+f8AZMgyNnM+uGHJq9VVtaNnwtMDastvNkUOs+auMXbNwsl5y/O6ZPX5I5 IvJmUhbSh0UOguGPJKUu/bl65theahz4HGBA0Q5nzgNLXVmU6aic143iixxMk+/q A59I6KelgWGj9QBPAHU68//J4dPFtlsRKZ7vI0vD14wnMvaJFv6tyTSgNdWsQOCW i+n16rGfMx1LNZTO1bO6TE6+ZLuvOchGJTYP4LbCeWLL8qDbdfz3oSKHUpyalELJ ljzin6r3qoA3TqvoGK5OWrFozuhWrWt3tIto53oJ34vJCsRZ0qvKDn9PQX9r3o56 hKhn8G9z/X5tNlfrzeSYikWQcQARAQABiQREBBgBAgAPBQJYhr/MAhsCBQkFo5qA AikJEHch9jvTi0eWwV0gBBkBAgAGBQJYhr/MAAoJEGSUxtaZfCFeW4kP/iZq+blR DzgRzOw16x80vyBjfPOUKd++dSUkcr4Khi5vjBygNdVSWcKZaBKVkdBmCvf+p9bY wzfL+RdxvGEv8WKNTNjdaWcJ2chU2O4H5Am3QsduQ/sSf+jTzlnMe7NpfF9n3uo3 4o+xEFOOcnyF3cHrhxWOCde9rX6kbnUQriIMXZteJY8e9Rs+Iv46DoL1eOlavAgD UJbIf/iLt219OdtWI7ZqopA0d+tcn7FL3fwuvyvn5WZRYHIerB4EYgBI6bCwl5JQ ejORlhuYx1oknyPjnzPJ9Los74chrf7OHOJ06iIQf1zlC9V/niA2xiM9NwePtTQO CTEJVB6IEoEtH6rozpAdriprH9fRnZkJxINNnCoYk1op9wVh3xfUHbOCvGQbB54c qN+amp9dEquCAe6Yt1WodTspL1zPXJ5Mv43Dud76TNEwQDywuebg4NFQnBTPXZGp LQYbUVhXSuMlVZXNEUx8xSz7vECm0S4x2h12RBKbK2RfI4oCq/wpD1dQRsZaKSYL FbZw5j2yk6nBBrtfahd7sWVX1F+YdisbTeT5iUhESAWqW9bCyCnNRFy6V34IgW9P e9yLu8WbVSJAFvnALxsc6hGyvs5dbXbruWKmi5mvk6tCFWdFlBVrrhx1QgqMtcS3 jv3S7GHyCA3CS1lEgsifYkeOARAgJ1hZ5BvUurUP+wb66lIhDB0U9NuFdJUTc6nO /1cy3i9mGCVoqwmTcB1BJ9E1hncMUP1/MvrAgkBBrAWJiD2Xj9QV/uBozA7nLxrV 7cf1de9OLgH4eNEfX25xj8BBPYnyVyHsyk5ZHDhjj9SaurfvlFWYi13i5ieMpyLV JV4+r2Wi1x1UgKVAlB78sHYnbDzSoHPLBcIxtIKp30LJ0PEkat8SG7G2wgtv1Rdh mcZEBV05vMnrGGO991e+pKzRNPYH8rD3VQKJlvaFwsJuBTW42gZ3KfpUNKI2ugCc nRNpoHFWNCrzlJ0CFI48LMlmUSs+7i/l+QGleaLKQxRTNNpAmevLrS7ga4Iq0IEq xey6VW6RSk/Z1Z37J8B7PISSR0rZn6TeyQgFWf/FOLw6OtwOquGmMeGSqj2Uzxyb ygtsvUZz0BxYymoWFd4F8sp43oL2TXU6Wp7QIpBaFgkSf/UQxfR6wcQ3ivafeS1l g8vUFuMfuMLto6T0JiZw8uKSuDWltSReF+FXVnhawz72BZMy8RIoshGdpWHn/YbN 6L+JOuxZnvkMAZvSLT3c0H4XCDYtEfK2mJMqD2ynX5tGR8Fy3GAaEjhx36TvzTjC XRmJ+FnlSW1p77x+UjFUFcpY8skv+f0Gip30iynAb1hoAdibIDab612OWi/4vX0D aM6t68Uq8rsabeJYsZG4uQINBF01/K4BEACskZL08crrKfX2aD2w8OUS3jVGSW7K 10Jr/dgl6ZB7Xx/y3c9lhBim7oRIsl6tpR/DBP50UnTIgBbvynbJ6tbWGptt64Az nI7el9pH0k63DOKcfqRUgJKTM4OUZSkcuqQ2qnkvn+g0oiJ3VhaVYOJdJfJF/pLj 5Oi3UEL2afoEd048/lZEaATRvEqLj+h2pSfETEl5wCWyRnuMSu6ay9NmVzRxiJhP DGW2ppQTxJuaKj+6Vqw5WISu9nsRxTPE1DW8f7LYyPBwgultuSYKZoCdfoYE8ff4 71oZIuCKcGSSBHQbR6MBTD6KJtqzBzpfJ8zZJmVO4lg0CJgp9xX2QZ8hPkpaBbnq 2JCMS1zriCMN8iGhW6ZHYmZQJtWuubuZt51VL9QmEUUhCF1t+3ld11SaowY4NFKI LUdYbC2zAOQIEEJkWRIHKleuc2zYSNSoXl06oGgwCKQb5l+LlcYHx4+/F3+KzyAq 0NqBC1rMnhbn3tcckdZyhLEpnx9/y33ypo6ZZ0s6dLGrmSpJpedEz6zr8siBa4uT 3IvVF4xjfpzSt3cMD/Lzhbnk5onUfkmoCmQ/pkuKpMr35hHtdDxshLcLPFkTncMj EVAOBToHDbKDSplueyJm48ELPi9ZmuyNu7WsB8TWVEAkUShxdeHALVpY1D+MjXK+ Z5ap6/tppj+fmwARAQABiQREBBgBCAAPBQJdNfyuAhsCBQkFo5qAAikJEHch9jvT i0eWwV0gBBkBCAAGBQJdNfyuAAoJEHi9ZUc8s70TzUAP/1Qq69M1CMd302TMnp1Y h1O06wkCPFGnMFMVwYRXH5ggoYUb3IoCOmIAHOEn6v9fho0rYImS+oRDFeE08dOx eI+Co0xVisVHJ1JJvdnu216BaXEsztZ0KGyUlFidXROrwndlpE3qlz4t1wh/EEaU H2TaQjRJ+O1mXJtF6vLB1+YvMTMz3+/3aeX/elDz9aatHSpjBVS2NzbHurb9g7mq D45nB80yTBsPYT7439O9m70OqsxjoDqe0bL/XlIXsM9w3ei/Us7rSfSY5zgIKf7/ iu+aJcMAQC9Zir7XASUVsbBZywfpo2v4/ACWCHJ63lFST2Qrlf4Rjj1PhF0ifvB2 XMR6SewNkDgVlQV+YRPO1XwTOmloFU8qepkt8nm0QM1lhdOQdKVe0QyNn6btyUCK I7p4pKc8/yfZm5j6EboXiGAb3XCcSFhR6pFrad12YMcKBhFYvLCaCN6g1q5sSDxv xqfRETvEFVwqOzlfiUH9KVY3WJcOZ3Cpbeu3QCpPkTiVZgbnR+WU9JSGQFEi7iZT rT8tct4hIg1Pa35B1lGZIlpYmzvdN5YoV9ohJoa1Bxj7qialTT/Su1Eb/toOOkOl qQ7B+1NBXzv9FmiBntC4afykHIeEIESNX9LdmvB+kQMW7d1d7Bs0aW2okPDt02vg wH2VEtQTtfq5B98jbwNW9mbXTvMQAKKCKl+H8T72WdueqgPKHEkXDZtJmTn6nyne YlETvdmHGEIb1ejxuJ5URlAYnciY+kvSQ/boKjVHNGmf6+JBexd+HqPhkeextV6J cnmi47HDvIU/TSynhuqZeK/3SZAV7ESqQl42q7wm7Pqw0dkv4jjFCRxDA+Qq2aH6 szJ7DZxTRWqfR3Zbe78NyFVXKxhFQO72zHzC3pFu/Ak59hmTU23yoXVo5t+5O+Q2 1kX2dbuLd6Px1bnT+EmyneoPP1Emea5jgsw2/ECqHnvNt6cbp+42XYldGh+PBHBm ucC3Mn7sALajHe5k2XkNlfbjSNlmutxQFH1qq9rh/JVyxJNHeGzV5G0timAwfdJF UzE1vNU5P0w4O8HrCsX5Ecfgcw2BQ9vPCE3OfG+11xp6oiNMRVsR5pTu7RiI1BQA yICWUW/wXuhhHkkwNTiwfciJfVA8ckOiRubik8geEH5boOxgeAaBu6yusQVHnRRy G4wjQ+qsWo+wDI9WMdtpNG1toJrSUL4OYa4oX3YogSv5hGrbYIaP4HwO6O2oTMnS 0lRIGJOqbEQcmKUa/nWT/3NipTnYzyMjMlEQe89YKjd+32tjMfOSdIOvwCGaTizd WnKPF77qB9D0v8C/7AdHmEFqf2ZX8vK31aaY+ZpPWG5IHlf6f/buIMBalJOxIBev eBqxcHwQ =4zaS -----END PGP PUBLIC KEY BLOCK----- rekor-1.3.5/pkg/pki/pgp/testdata/valid_armored_public.pgp000066400000000000000000000032751455727245600235130ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- mQENBF+taPsBCADav3G36B5l+41rvf1nqyr7icxCMcjr1Jspxk5jCuQ6uKXqFfLk cCNURfR3AvOtYeuavy5S6ryndTzwpkofDlqnSCHK6xVlfKhOKbVaZqD80wjoWBea 2GmiCkKxIy5fRvaCJ3cUg0gjsWF4PUXbxfXdFVKTjfGGV4RmFQ2vo1dTpnioCRLO /1k9qCbYzJQV8vijwwJdnHKroK670R66l14d8GtQCbeEOdFUArx8OkHHOKUFrT1e tnZL2EKacMtdOr7cdeBA1jQJm3XUfqVpqB4UUth24CNuzENvwhb4hMUMZapw8mIE t9gUosoe6K/2RCQSI0vhBMpnjxIsWBb6AmY3ABEBAAG0DG5vdEByZWFsLmNvbYkB VAQTAQgAPhYhBGG8KbG/rEMzEr6BOob1dVKdD5/0BQJfrWj7AhsDBQkDwmcABQsJ CAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEIb1dVKdD5/0HxMH/3/D2oc5sIcM/Apn 9HgQgnyUGP+VZ/MOozrRy3lPS+lvKEqpOEJxdwY5xZX37fCjoMjWRljfeKWePc4i Tr65Lo3gF5mW/mwHmc3HyHTFEizRgSSdAsYpniQ1ChpHLBdLLYqKJ0y86Hz2oZyx fH224J4r8ZrF7a3lOpvFHsSoOd3g4SXcSHbHM2AjWyDE92n/xlCNBH21EhSXnb5R SVCct8cLxP+BzfMu16iFWss4mVrmOAZBybFoj/9YTTZrAHLfqlFtNjclMny3Cytx S4FEy8ySKdjuH1ksy+eIj775THf0CEuuI8vAy3Z9d3v22gPbWQSmLwtqtDb2RLe2 v4d5MFu5AQ0EX61o+wEIANac8HSsMnB38y2+8x8moqtBBeZIL2xiss/hXjJXoxXu gOhif5MKx4T0dyQMRJ79nxsWvKFPC85poYKb2q8oWz5PZl7JGML7tvw/YozgP4xe GeM7MVmb+7I9jLDx9XM+LfYWnTYdumT8VQyxhhTUq7r+kXYGDJbUJpw/vP+tKV2r fv013b4bgJHqgF2q/UJTs7CEvcoPlazlxRuF0Zsiy4UTnB74/WhKEmZLRA2o2juA o+yq3ApD//PO8NUQvDZvIi+FNucDzUsmhYFTdlqLBzu5eQb74BHss3e3soaVBZBm yV3v2MJmxaZsdjfdSr3LR/Lza4C7XXZZM9qaP+EyqhMAEQEAAYkBNgQYAQgAIBYh BGG8KbG/rEMzEr6BOob1dVKdD5/0BQJfrWj7AhsMAAoJEIb1dVKdD5/0IqwH/0Z/ 3+pKZHB8y6XOqUzi3yW6xVIXnvcjhiCBwCUIJl4TwLGqO0f1OFVlrufg2CY7/Zcd zA0cHrmnjDhU9MbIeMQc90dr4A+HDNcCNKOzkfp6vYwSSmdGOsB3gHklez5Nz9ks PftodCpuyDpow4lfVTTVHtUYABN2K/eSl4dQtDuzfXCovtnpHLht84w2oTojYnqk 8JwAtdhk31jCTy2HVtHS9p3Fq9ueW3/vrHu/ePQq1HFx5cyknGOIb6Rbz+BT/46g FIh8k9s/IbNXM/pENPucinlJZju9QUkOUHt4ZfWsdPAjOs/tuM7yS+sdbzrHSllc qKXPA7Q75DorBuM0+Ro= =lHMC -----END PGP PUBLIC KEY BLOCK----- rekor-1.3.5/pkg/pki/pgp/testdata/valid_binary_complex_public.pgp000066400000000000000000000163511455727245600250740ustar00rootroot00000000000000EoFн.͑йϠEiu#P[4#F?ڒXoqCD֛4\DeS3/ugr]/(=s~/[HG }* ;|ƸL,IN,"rR]Zı7}᪶}?t=$]宣>9L*#K{.B6S5 7j=y ,V2&B+E *!dMsoEtJ E O%r7Q w{$f{pEX\g3$#Əo)]$v@/8J yN MleRϙJwF41x8Mi O`9_x7~/AB'vA(^_ôLGoogle, Inc. Linux Package Signing Key c# Fv @YJvǴ .fWsx$nWRa۞լ EoKzrLYp.:([H'}P` o(HΔYVW;=bUh[Boܶ3G 0]~k%E30w~npJEY=_ +qn`>^Zm(V$*3zUhɫS0;Ϭ5^lhJPX/D4KH0[=FKޑklb,Fj]+y+ÕMi a"oXH#jkNчm }$  `t{m2fKf X6d=$?٩u^4~#9NOϐ86 ~8;X)WemhyGFI/> Vkgr~ꄒ 2Oee{Jx^fi2vBc5q%6NwwvTN=`E>k$ L>ŝ< IEJKY+|>{F sCH,g_gnNi\h;KivX9s8):r1uqRu\l~o}]L !ҥ2g&4U=->I<;eytnr(-{Ӳ[ct%hU7NIZxdrlzO@,Nx=vSML*xd+8+awgTUnUOcT"U)TGoogle Inc. (Linux Packages Signing Authority) 8"W    w!;ӋG  (ě%h8gձB7Kķ r=bhpP’FN[BS =yQrB/=|KYw G }wG|NkM+EQ8ajJ&ȻIoq9[AN9vDF03=LA,MqQ?SU`\/0ڑ9~^Ei$G $N+Ϛ'TvInE`db: DVre$z>`:ȔYףJzF J<{YN-5r4qvSڗD~R@ cg}e!:A};k&Kv;quc6Qxe^u&F򂯧/ ,lZw>k=p)C!Z X>D7]cFO"%<ĈFW  @YY3Zȩ_PxCQnTv)\(܅rˑuM>(C 5FW 1G,;1G'*30FKXOkn aY0DPo6ْuOjs#`;+oX [*YuRH} x˩ӾϗSբDг?_e?d-cvPzeFOuGA`GLe_/MeDgI:xt1{V˘gnM o.`')<>6|/@JiBzeסhcI@F{A6%4,F63Hjf`J]3WdŸQ%/}WCR+A9½!MtcGAdhe;#&beg?uc^v9:)ל3WXdη: iuw^IH5lr8]gSq Zta.->ܧyDt(3_@Gڎ3ۄ< nzއUПzCRi fB4m˚ú\}+&xpRlVy?_j ]Q+Z`*[gE[ id$,Cd=do^yKLp5ygz<.A@9̑R8gkuJ=IG%D{Ȧkt!Ǫ[K;6':tűp铛IX4k5emc>ddF(-sϫ`UB& 5η986#0̝j%!JDRE$ _4|j6lZ0q:5[M/}@d͎MTњRC i96^ r|_U+|1L?Mѫɑf hMumtOH5ٛ_H%MCXt#7/F0dsm|U_!/db dЮ8Yw7bu>('\Q%"?uP1,*o, % ͺC{O;AbjѸ-X7IW$ekѧ5groJ+aY߿6 6=#rZD{h5:;"R!8 wYA H±Ӵ߮r3;zU.[ Xbs]eZ˾[BXM3@&.,Zoͽ S!USF"_hhn@dD!'dX!Nˀpȓ]23{q{ ]jmu~¦ڈFYPc~:e1t1͕~_xjQKcªÜU ]utU/ ,6=#a&Pmh4P#dž C'Yf_ M{{Єjl,;O_9"fR҇E$.z^j`@gK]Y騜׍LH觥aOu:Ŷ[)#K׌'245լ@걟3K5ճLNd9F%6ybu"RBɖ<⟪7NNZhVkwhz ߋ YҫOAkގzgos~m6W䘊EqDX ) w!;ӋG] X d֙|!^[&jQ854 c|)߾u%$r .o5RY™hf 7qa/bLig T BnCY{i|_g7⏱Sr| ׽~nu" ]^%>":xZPm}9V#j4w\K.+fQ`rbH鰰Pz3Z$#3,!t"\  6#=74 1 T-ΐ*kѝ ăM*Z)adߚ]Uu;)/\\LùL0@eT@v'l<Ҡs1B$j oaD]9cW4Un5w)T46iqV4*<,fQ+>/yCS4@˭.k*Ё*UnJO՝'{2(a迉:Y -=~6-򶘓*l_FGr`8qߤ8]YImi~R1TX/)oXh؛ 6]Z/}hέ*mX ]5)h=5FInBk%{_eH^tRtȀvm3ޗGN ~T3e)6y/4"wV`]%EPBiwN&ڳ:_'&eNX4)A!>JZؐK\# ![GbfP&ծU/&E!]my]T84R-GXl-BdY*WslHԨ^]:h0_Ǐ *ځ Z̞r)}򦎙gK:t*IDϬȁk܋c~ҷw ~I d?Kt/Y썻T@$Q(qu-ZX?rgi?D]5 ) w!;ӋG] ]5 xeG<@T*5wd̞XS -FdB4If\E/133izP*cT76Ǻg2La>ӽc:Ѳ^RpRI8)%@/Y%YkrzQROd+=O]"~v\zI 8~a|:ihO*z-y@eӐt^ @#x<'ٛ`pHXQkiv` Xޠ֮lHY۞I I9)bQهBTFPȘKC*5G4iA{~籵^ryü?M,xIDB^6&/8 C*١2{ SEjGv[{ UW+E@|ޑn 9Smuh߹;6EuwչI?Q&yc6@{ͷ6]]pf2~dy HfP}jrēGxlm-`0}ES159?L8; s CM|oz#LE[ȀQo^aI058}ȉ}P!a)C3:uR_h g    uRڇ9 gx|g:yOKo(J8Bqw9ŕFXx="N.lt,с$)$5 G,K-'L||}+:Ĩ9%Hv3`#[ iP}QIP .רZ8Z8AɱhXM6krߪQm67%2| +qKD̒)Y,爏LwK#v}w{Y/ j6Dy0[ _h֜t2pw-&AH/lb^2Wb DŽw$ DO iگ([>Of^?b?^;1Y=s>-6dU ԫv &?)]~5ݾ]BSћ"˅hJfKD ; C6o"/6K&SvZ;ywf]fŦlv7JGk]vY3ښ?26 !a)C3:uR_h uR"FJdp|˥ΩL%R# %&^;G8Ue&; 8TxGk 4zJgF:wy%{>M,=ht*n:hÉ_U4v+P;}pm6:#bzdXO-Vū۞[{x*qq̤co[S|?!W3D4yIf;AIP{xet#:Ko:JY\;:+4rekor-1.3.5/pkg/pki/pkcs7/000077500000000000000000000000001455727245600152565ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/pkcs7/pkcs7.go000066400000000000000000000142471455727245600166440ustar00rootroot00000000000000/* Copyright © 2021 The Sigstore Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package pkcs7 import ( "bytes" "crypto" "crypto/sha256" "crypto/x509" "encoding/asn1" "encoding/hex" "encoding/pem" "errors" "fmt" "io" "strings" "github.com/sassoftware/relic/lib/pkcs7" "github.com/sigstore/rekor/pkg/pki/identity" "github.com/sigstore/sigstore/pkg/cryptoutils" sigsig "github.com/sigstore/sigstore/pkg/signature" ) // EmailAddressOID defined by https://oidref.com/1.2.840.113549.1.9.1 var EmailAddressOID asn1.ObjectIdentifier = []int{1, 2, 840, 113549, 1, 9, 1} type Signature struct { signedData pkcs7.SignedData detached bool raw *[]byte } // NewSignature creates and validates an PKCS7 signature object func NewSignature(r io.Reader) (*Signature, error) { b, err := io.ReadAll(r) if err != nil { return nil, err } // try PEM decoding first var pkcsBytes *[]byte block, _ := pem.Decode(b) if block != nil { if block.Type != "PKCS7" { return nil, fmt.Errorf("unknown PEM block type %s found during PKCS7 parsing", block.Type) } pkcsBytes = &block.Bytes } else { // PEM decoding failed, it might just be raw ASN.1 data pkcsBytes = &b } psd, err := pkcs7.Unmarshal(*pkcsBytes) if err != nil { return nil, err } // we store the detached signature as the raw, canonical format if _, err := psd.Detach(); err != nil { return nil, err } detached, err := psd.Marshal() if err != nil { return nil, err } cb, err := psd.Content.ContentInfo.Bytes() if err != nil { return nil, err } return &Signature{ signedData: psd.Content, raw: &detached, detached: cb == nil, }, nil } // CanonicalValue implements the pki.Signature interface func (s Signature) CanonicalValue() ([]byte, error) { if s.raw == nil { return nil, fmt.Errorf("PKCS7 signature has not been initialized") } p := pem.Block{ Type: "PKCS7", Bytes: *s.raw, } var buf bytes.Buffer if err := pem.Encode(&buf, &p); err != nil { return nil, err } return buf.Bytes(), nil } // Verify implements the pki.Signature interface func (s Signature) Verify(r io.Reader, _ interface{}, _ ...sigsig.VerifyOption) error { if len(*s.raw) == 0 { return fmt.Errorf("PKCS7 signature has not been initialized") } // if content was passed to this, verify signature as if it were detached bb := bytes.Buffer{} var extContent []byte if r != nil { n, err := io.Copy(&bb, r) if err != nil { return err } if n > 0 { extContent = bb.Bytes() } else if s.detached { return errors.New("PKCS7 signature is detached and there is no external content to verify against") } } if _, err := s.signedData.Verify(extContent, false); err != nil { return err } return nil } // PublicKey Public Key contained in cert inside PKCS7 bundle type PublicKey struct { key crypto.PublicKey certs []*x509.Certificate rawCert []byte } // NewPublicKey implements the pki.PublicKey interface func NewPublicKey(r io.Reader) (*PublicKey, error) { rawPub, err := io.ReadAll(r) if err != nil { return nil, err } // try PEM decoding first var pkcsBytes *[]byte block, _ := pem.Decode(rawPub) if block != nil { if block.Type != "PKCS7" { return nil, fmt.Errorf("unknown PEM block type %s found during PKCS7 parsing", block.Type) } pkcsBytes = &block.Bytes } else { // PEM decoding failed, it might just be raw ASN.1 data pkcsBytes = &rawPub } pkcs7, err := pkcs7.Unmarshal(*pkcsBytes) if err != nil { return nil, err } certs, err := pkcs7.Content.Certificates.Parse() if err != nil { return nil, err } for _, cert := range certs { return &PublicKey{key: cert.PublicKey, certs: certs, rawCert: cert.Raw}, nil } return nil, errors.New("unable to extract public key from certificate inside PKCS7 bundle") } // CanonicalValue implements the pki.PublicKey interface func (k PublicKey) CanonicalValue() ([]byte, error) { if k.rawCert == nil { return nil, fmt.Errorf("PKCS7 public key has not been initialized") } //TODO: should we export the entire cert chain, not just the first one? p := pem.Block{ Type: "CERTIFICATE", Bytes: k.rawCert, } var buf bytes.Buffer if err := pem.Encode(&buf, &p); err != nil { return nil, err } return buf.Bytes(), nil } // EmailAddresses implements the pki.PublicKey interface func (k PublicKey) EmailAddresses() []string { var names []string // Get email address from Subject name in raw cert. cert, err := x509.ParseCertificate(k.rawCert) if err != nil { // This should not happen from a valid PublicKey, but fail gracefully. return names } for _, name := range cert.Subject.Names { if name.Type.Equal(EmailAddressOID) { names = append(names, strings.ToLower(name.Value.(string))) } } return names } // Subjects implements the pki.PublicKey interface func (k PublicKey) Subjects() []string { // combine identities in the subject and SANs identities := k.EmailAddresses() cert, err := x509.ParseCertificate(k.certs[0].Raw) if err != nil { // This should not happen from a valid PublicKey, but fail gracefully. return identities } identities = append(identities, cryptoutils.GetSubjectAlternateNames(cert)...) return identities } // Identities implements the pki.PublicKey interface func (k PublicKey) Identities() ([]identity.Identity, error) { // pkcs7 structure may contain both a key and certificate chain pkixKey, err := cryptoutils.MarshalPublicKeyToDER(k.key) if err != nil { return nil, err } keyDigest := sha256.Sum256(pkixKey) certDigest := sha256.Sum256(k.certs[0].Raw) return []identity.Identity{{ Crypto: k.certs[0], Raw: k.certs[0].Raw, Fingerprint: hex.EncodeToString(certDigest[:]), }, { Crypto: k.key, Raw: pkixKey, Fingerprint: hex.EncodeToString(keyDigest[:]), }, }, nil } rekor-1.3.5/pkg/pki/pkcs7/pkcs7_test.go000066400000000000000000000522551455727245600177040ustar00rootroot00000000000000/* Copyright © 2021 The Sigstore Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package pkcs7 import ( "bytes" "crypto" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "net/url" "reflect" "sort" "strings" "testing" "github.com/sassoftware/relic/lib/pkcs7" "github.com/sigstore/rekor/pkg/pki/identity" "github.com/sigstore/rekor/pkg/pki/x509/testutils" "github.com/sigstore/sigstore/pkg/cryptoutils" ) const pkcsECDSAPEM = `-----BEGIN PKCS7----- MIIW9QYJKoZIhvcNAQcCoIIW5jCCFuICAQExDzANBglghkgBZQMEAgEFADCCBAwG CSqGSIb3DQEHAaCCA/0EggP5U2lnbmF0dXJlLVZlcnNpb246IDEuMA0KQ3JlYXRl ZC1CeTogMTUgKEFkb3B0T3BlbkpESykNClNIQS0yNTYtRGlnZXN0LU1hbmlmZXN0 OiB6QzV4S3JxM1pIZS90UnNMMTR6bittM1lReWVaZFltbmxuNWJNdlJaZW5JPQ0K U0hBLTI1Ni1EaWdlc3QtTWFuaWZlc3QtTWFpbi1BdHRyaWJ1dGVzOiBBZW00ckh4 eTYycmx6QzJVU0NVbDcwSEFmYmV2NzhXDQogUkNhUWNKcXEwTE5nPQ0KDQpOYW1l OiBzaWdzdG9yZS9wbHVnaW4vU2lnbi5jbGFzcw0KU0hBLTI1Ni1EaWdlc3Q6IEZH UVZGbDlROEQ1ZTAzRE1RaGN2aTNtK0orZCtUc3A3TmFxKzBUUXpoSW89DQoNCk5h bWU6IE1FVEEtSU5GL21hdmVuL2Rldi5zaWdzdG9yZS9zaWdzdG9yZS1tYXZlbi1w bHVnaW4vcG9tLnhtbA0KU0hBLTI1Ni1EaWdlc3Q6IFlWRUFpeXZRMDZOVHRkRFRq cVJPYUZZbnQzcDY0QzFFa2NBbWlLNkpOcGM9DQoNCk5hbWU6IE1FVEEtSU5GL21h dmVuL2Rldi5zaWdzdG9yZS9zaWdzdG9yZS1tYXZlbi1wbHVnaW4vcG9tLnByb3Bl cnRpZXMNClNIQS0yNTYtRGlnZXN0OiA3aU1VWlpLeVI3cjdLelR1K2M2dVlsSWJ5 c0VuZE1wMVBacUVXR2pHU2lNPQ0KDQpOYW1lOiBNRVRBLUlORi9tYXZlbi9kZXYu c2lnc3RvcmUvc2lnc3RvcmUtbWF2ZW4tcGx1Z2luL3BsdWdpbi1oZWxwLnhtbA0K U0hBLTI1Ni1EaWdlc3Q6IG4yM1N4ZmlDcU43WW9FSnd5S0k3NUE3N3crRHREUmIr dFI0bVl6SnZlWnc9DQoNCk5hbWU6IE1FVEEtSU5GL21hdmVuL3BsdWdpbi54bWwN ClNIQS0yNTYtRGlnZXN0OiBTRktBeGVwMlErSzJNVmZVeUV2U1FvMFRBNDhDSitu QXNxbmhzRWRJOUVFPQ0KDQpOYW1lOiBzaWdzdG9yZS9wbHVnaW4vU2lnbiQxLmNs YXNzDQpTSEEtMjU2LURpZ2VzdDogNlEvQVExZW9QNE9hQVJwbnVSRklRb0tZUC9S bmJ0TGxqOGJhUEg3TkdMZz0NCg0KTmFtZTogc2lnc3RvcmUvcGx1Z2luL0hlbHBN b2pvLmNsYXNzDQpTSEEtMjU2LURpZ2VzdDogU3ZPNkhibVlBSzBMVEhyVCtYbmRB OExJdUptZU5ub1dyYmVHS3dvTE9Pdz0NCg0KoIIEsjCCAfgwggF+oAMCAQICEzVZ A2aAoqHzw7Mu3X5uoHi27ocwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3Rv cmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTAzMDcwMzIwMjlaFw0zMTAy MjMwMzIwMjlaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2ln c3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAS0sgOyIuZPqTTvGRFmNMpXplg6 MDpDWt5C/hmROWeRlnoS/fwPW0TQN0W67GeYtCXGrLWkS+0qeX6f4w+XcanP1HU1 Z5b0temp/tmH7MHv0Li6JUVAq3DhNvtogOfrc3ejZjBkMA4GA1UdDwEB/wQEAwIB BjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTIxR0AQZokKTJRJOsNrkrt SgbT7DAfBgNVHSMEGDAWgBTIxR0AQZokKTJRJOsNrkrtSgbT7DAKBggqhkjOPQQD AwNoADBlAjB/JYliXzLour11wYYw4GODMLJZjf0ycVXv/N1qxaJsJjX9OestV+PB fXOJt2t6M1wCMQCo0Wsuf2o/47CihiJJkGrYyPLrqR6//gsRb2iVpqWKjZCwxkVP vaK84eYSNka3LmkwggKyMIICN6ADAgECAhQA0hq1XjiwESgeFBdACLc/8dxN/jAK BggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNp Z3N0b3JlMB4XDTIxMDQwNTE3MzA0MVoXDTIxMDQwNTE3NTAzM1owPDEcMBoGA1UE CgwTYmNhbGxhd2FAcmVkaGF0LmNvbTEcMBoGA1UEAwwTYmNhbGxhd2FAcmVkaGF0 LmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOQ44IV3v9zK5zLUoPpqt4Wy snDT+OkgZQmPLq6PtNbqXOJnGtdi1crznvmlytJ1rsrNtobtG92Y3XtMSx+2fo6j ggEnMIIBIzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDAYD VR0TAQH/BAIwADAdBgNVHQ4EFgQU89SEXFUXE+hEwlqafHp6CayzjJcwHwYDVR0j BBgwFoAUyMUdAEGaJCkyUSTrDa5K7UoG0+wwgY0GCCsGAQUFBwEBBIGAMH4wfAYI KwYBBQUHMAKGcGh0dHA6Ly9wcml2YXRlY2EtY29udGVudC02MDNmZTdlNy0wMDAw LTIyMjctYmY3NS1mNGY1ZTgwZDI5NTQuc3RvcmFnZS5nb29nbGVhcGlzLmNvbS9j YTM2YTFlOTYyNDJiOWZjYjE0Ni9jYS5jcnQwHgYDVR0RBBcwFYETYmNhbGxhd2FA cmVkaGF0LmNvbTAKBggqhkjOPQQDAwNpADBmAjEAy3AOFlXTN7pMUyLyzsLk8tn8 v782Bo5hGSGYJMZn8eRHGktDSlx4bj51Gu+V1c4kAjEA9ISrLl83ZU6j1yP0emR1 FgAoHceF5dtx4KzSAi4B0Cghz7kBabfljWjCMy36Ce6rMYIOBDCCDgACAQEwQjAq MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlAhQA0hq1 XjiwESgeFBdACLc/8dxN/jANBglghkgBZQMEAgEFADALBgcqhkjOPQIBBQAERzBF AiAttO+bYBcMnsMBQlkTdXII2f8CREQVkl9ehakvihSjBgIhAKYic4Ycq3qYLoV9 4GZWm0NT0EFbzRG5BJaoEZgUL/lyoYINUDCCDUwGCyqGSIb3DQEJEAIOMYINOzCC DTcGCSqGSIb3DQEHAqCCDSgwgg0kAgEDMQ8wDQYJYIZIAWUDBAIBBQAwgYEGCyqG SIb3DQEJEAEEoHIEcDBuAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQg DpZGPZm1vvrjhj/lQAoCYWm+9GCixa/ySbShy9tPdjQCEBOsApMET86YdBu0FUV2 558YDzIwMjEwNDA1MTczMDQxWgIIMX79aENwnPqgggo3MIIE/jCCA+agAwIBAgIQ DUJK4L46iP9gQCHOFADw3TANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEV MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t MTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5n IENBMB4XDTIxMDEwMTAwMDAwMFoXDTMxMDEwNjAwMDAwMFowSDELMAkGA1UEBhMC VVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSAwHgYDVQQDExdEaWdpQ2VydCBU aW1lc3RhbXAgMjAyMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMLm YYRnxYr1DQikRcpja1HXOhFCvQp1dU2UtAxQtSYQ/h3Ib5FrDJbnGlxI70Tlv5th zRWRYlq4/2cLnGP9NmqB+in43Stwhd4CGPN4bbx9+cdtCT2+anaH6Yq9+IRdHnbJ 5MZ2djpT0dHTWjaPxqPhLxs6t2HWc+xObTOKfF1FLUuxUOZBOjdWhtyTI433UCXo ZObd048vV7WHIOsOjizVI9r0TXhG4wODMSlKXAwxikqMiMX3MFr5FK8VX2xDSQn9 JiNT9o1j6BqrW7EdMMKbaYK02/xWVLwfoYervnpbCiAvSwnJlaeNsvrWY4tOpXIc 7p96AXP4Gdb+DUmEvQECAwEAAaOCAbgwggG0MA4GA1UdDwEB/wQEAwIHgDAMBgNV HRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMEEGA1UdIAQ6MDgwNgYJ YIZIAYb9bAcBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29t L0NQUzAfBgNVHSMEGDAWgBT0tuEgHf4prtLkYaWyoiWyyBc1bjAdBgNVHQ4EFgQU NkSGjqS6sGa+vCgtHUQ23eNqerwwcQYDVR0fBGowaDAyoDCgLoYsaHR0cDovL2Ny bDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC10cy5jcmwwMqAwoC6GLGh0dHA6 Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtdHMuY3JsMIGFBggrBgEF BQcBAQR5MHcwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBP BggrBgEFBQcwAoZDaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0 U0hBMkFzc3VyZWRJRFRpbWVzdGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOC AQEASBzctemaI7znGucgDo5nRv1CclF0CiNHo6uS0iXEcFm+FKDlJ4GlTRQVGQd5 8NEEw4bZO73+RAJmTe1ppA/2uHDPYuj1UUp4eTZ6J7fz51Kfk6ftQ55757TdQSKJ +4eiRgNO/PT+t2R3Y18jUmmDgvoaU+2QzI2hF3MN9PNlOXBL85zWenvaDLw9MtAb y/Vh/HUIAHa8gQ74wOFcz8QRcucbZEnYIpp1FUL1LTI4gdr0YKK6tFL7XOBhJCVP st/JKahzQ1HavWPWH1ub9y4bTxMd90oNcX6Xt/Q/hOvB46NJofrOp79Wz7pZdmGJ X36ntI5nePk2mOHLKNpbh6aKLzCCBTEwggQZoAMCAQICEAqhJdbWMht+QeQF2jaX whUwDQYJKoZIhvcNAQELBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGln aUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTE2MDEwNzEyMDAwMFoXDTMxMDEw NzEyMDAwMFowcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hB MiBBc3N1cmVkIElEIFRpbWVzdGFtcGluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAL3QMu5LzY9/3am6gpnFOVQoV7YjSsQOB0UzURB90Pl9TWh+ 57ag9I2ziOSXv2MhkJi/E7xX08PhfgjWahQAOPcuHjvuzKb2Mln+X2U/4Jvr40ZH BhpVfgsnfsCi9aDg3iI/Dv9+lfvzo7oiPhisEeTwmQNtO4V8CdPuXciaC1TjqAlx a+DPIhAPdc9xck4Krd9AOly3UeGheRTGTSQjMF287DxgaqwvB8z98OpH2YhQXv1m blZhJymJhFHmgudGUP2UKiyn5HU+upgPhH+fMRTWrdXyZMt7HgXQhBlyF/EXBu89 zdZN7wZC/aJTKk+FHcQdPK/P2qwQ9d2srOlW/5MCAwEAAaOCAc4wggHKMB0GA1Ud DgQWBBT0tuEgHf4prtLkYaWyoiWyyBc1bjAfBgNVHSMEGDAWgBRF66Kv9JLLgjEt UYunpyGd823IDzASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAT BgNVHSUEDDAKBggrBgEFBQcDCDB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGG GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2Nh Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCB gQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lD ZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNl cnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBQBgNVHSAESTBHMDgG CmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQu Y29tL0NQUzALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggEBAHGVEulRh1Zp ze/d2nyqY3qzeM8GN0CE70uEv8rPAwL9xafDDiBCLK938ysfDCFaKrcFNB1qrpn4 J6JmvwmqYN92pDqTD/iy0dh8GWLoXoIlHsS6HHssIeLWWywUNUMEaLLbdQLgcseY 1jxk5R9IEBhfiThhTWJGJIdjjJFSLK8pieV4H9YLFKWA1xJHcLN11ZOFk362kmf7 U2GJqPVrlsD0WGkNfMgBsbkodbeZY4UijGHKeZR+WfyMD+NvtQEmtmyl7odRIeRY YJu6DC0rbaLEfrvEJStHAgh8Sa4TtuF8QkIoxhhWz0E0tmZdtnR79VYzIi8iNrJL okqV2PWmjlIxggJNMIICSQIBATCBhjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQD EyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBAhANQkrg vjqI/2BAIc4UAPDdMA0GCWCGSAFlAwQCAQUAoIGYMBoGCSqGSIb3DQEJAzENBgsq hkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjEwNDA1MTczMDQxWjArBgsqhkiG 9w0BCRACDDEcMBowGDAWBBTh14Ko4ZG+72vKFpG1qrSUpiSb8zAvBgkqhkiG9w0B CQQxIgQggGa+Xld/PXUrauHVUANZD8tGkn6b2ioPrjbYztVzPikwDQYJKoZIhvcN AQEBBQAEggEAj7sr/Yqkwqrm21IhIHPLXDDDhxBPfcv0DJhFsAOR77wlDzV52yg6 JrexTMuLWgPulVN0UyMoCISqMv22R9ELZGGxPjDYBu0jURFKZEryVEOoidA8U07x TBSkcGB6Vf4P6mNxzl2whkIg4bgob8ynD8O6eb7aF6sTXFN6GyZHtYhMlMuJw3Tt zNwtTy9bCZI4T4IlKscOhJ4hnVz0PO4mi/7C6Y/fLz/KoNXJR1q8LBTlHd5fNN5S NCy1JqXRQ/EFawlOicDB5IFL7TFpPTPEXsyTg1x5j1o0tAKErU3FJg30wiblro49 oNLw5vSDnA3bG/vDsgshFr03RYcLPUVAtA== -----END PKCS7-----` const signedContent = `U2lnbmF0dXJlLVZlcnNpb246IDEuMA0KQ3JlYXRlZC1CeTogMTUgKEFkb3B0T3Bl bkpESykNClNIQS0yNTYtRGlnZXN0LU1hbmlmZXN0OiB6QzV4S3JxM1pIZS90UnNM MTR6bittM1lReWVaZFltbmxuNWJNdlJaZW5JPQ0KU0hBLTI1Ni1EaWdlc3QtTWFu aWZlc3QtTWFpbi1BdHRyaWJ1dGVzOiBBZW00ckh4eTYycmx6QzJVU0NVbDcwSEFm YmV2NzhXDQogUkNhUWNKcXEwTE5nPQ0KDQpOYW1lOiBzaWdzdG9yZS9wbHVnaW4v U2lnbi5jbGFzcw0KU0hBLTI1Ni1EaWdlc3Q6IEZHUVZGbDlROEQ1ZTAzRE1RaGN2 aTNtK0orZCtUc3A3TmFxKzBUUXpoSW89DQoNCk5hbWU6IE1FVEEtSU5GL21hdmVu L2Rldi5zaWdzdG9yZS9zaWdzdG9yZS1tYXZlbi1wbHVnaW4vcG9tLnhtbA0KU0hB LTI1Ni1EaWdlc3Q6IFlWRUFpeXZRMDZOVHRkRFRqcVJPYUZZbnQzcDY0QzFFa2NB bWlLNkpOcGM9DQoNCk5hbWU6IE1FVEEtSU5GL21hdmVuL2Rldi5zaWdzdG9yZS9z aWdzdG9yZS1tYXZlbi1wbHVnaW4vcG9tLnByb3BlcnRpZXMNClNIQS0yNTYtRGln ZXN0OiA3aU1VWlpLeVI3cjdLelR1K2M2dVlsSWJ5c0VuZE1wMVBacUVXR2pHU2lN PQ0KDQpOYW1lOiBNRVRBLUlORi9tYXZlbi9kZXYuc2lnc3RvcmUvc2lnc3RvcmUt bWF2ZW4tcGx1Z2luL3BsdWdpbi1oZWxwLnhtbA0KU0hBLTI1Ni1EaWdlc3Q6IG4y M1N4ZmlDcU43WW9FSnd5S0k3NUE3N3crRHREUmIrdFI0bVl6SnZlWnc9DQoNCk5h bWU6IE1FVEEtSU5GL21hdmVuL3BsdWdpbi54bWwNClNIQS0yNTYtRGlnZXN0OiBT RktBeGVwMlErSzJNVmZVeUV2U1FvMFRBNDhDSituQXNxbmhzRWRJOUVFPQ0KDQpO YW1lOiBzaWdzdG9yZS9wbHVnaW4vU2lnbiQxLmNsYXNzDQpTSEEtMjU2LURpZ2Vz dDogNlEvQVExZW9QNE9hQVJwbnVSRklRb0tZUC9SbmJ0TGxqOGJhUEg3TkdMZz0N Cg0KTmFtZTogc2lnc3RvcmUvcGx1Z2luL0hlbHBNb2pvLmNsYXNzDQpTSEEtMjU2 LURpZ2VzdDogU3ZPNkhibVlBSzBMVEhyVCtYbmRBOExJdUptZU5ub1dyYmVHS3dv TE9Pdz0NCg0K` const pkcsPEMEmail = `-----BEGIN PKCS7----- MIIDCgYJKoZIhvcNAQcCoIIC+zCCAvcCAQExADALBgkqhkiG9w0BBwGgggLdMIIC 2TCCAjqgAwIBAgIUAL0Gw2SJvPW8PbXw+42XwmW8//owCgYIKoZIzj0EAwIwfTEL MAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1BMQ8wDQYDVQQHDAZCb3N0b24xITAfBgNV BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEOMAwGA1UEAwwFUmVrb3IxHTAb BgkqhkiG9w0BCQEWDnRlc3RAcmVrb3IuZGV2MCAXDTIxMDQxOTE0MTMyMFoYDzQ0 ODUwNTMxMTQxMzIwWjB9MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTUExDzANBgNV BAcMBkJvc3RvbjEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ4w DAYDVQQDDAVSZWtvcjEdMBsGCSqGSIb3DQEJARYOdGVzdEByZWtvci5kZXYwgZsw EAYHKoZIzj0CAQYFK4EEACMDgYYABABN0k2SaX5iK6Ahw8m+wXbQml4E8GEL0qLA lA0Gu8thlhvAcOLdPzNxPl2tsM7bBzTrD2H4iLM4myvpT4x2NgbjyAClvhXfJTOY m7oTFcKq0kNf8LEV2fjBpfdrw9yiS1DV6YWHwCzc3TUrZIChGhMYnfZPVu997wzy euVBSUMeO5Lmp6NTMFEwHQYDVR0OBBYEFJPLiMMFN5Cm6/rjOTPR2HWbbO5PMB8G A1UdIwQYMBaAFJPLiMMFN5Cm6/rjOTPR2HWbbO5PMA8GA1UdEwEB/wQFMAMBAf8w CgYIKoZIzj0EAwIDgYwAMIGIAkIBmRqxw8sStWknjeOgdyKkd+vFehNuVaiHAKGs z+6KG3jPG5xN5+/Ws+OMTAp7Hv6HH5ChDO3LJ6t/sCun1otdWmICQgCUqg1ke+Rj nVqVlz1rUR7CTL2SlG9Xg1kAkYH4vMn/otEuAhnKf+GWLNB1l/dTFNEyysvIA6yd FG8HXGWcnVVIVaEAMQA= -----END PKCS7-----` func TestSignature_Verify(t *testing.T) { tests := []struct { name string pkcs7 string }{ { name: "ec", pkcs7: pkcsECDSAPEM, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, err := NewSignature(strings.NewReader(tt.pkcs7)) if err != nil { t.Fatal(err) } pub, err := NewPublicKey(strings.NewReader(tt.pkcs7)) if err != nil { t.Fatal(err) } data, _ := base64.StdEncoding.DecodeString(signedContent) if err := s.Verify(bytes.NewReader(data), pub); err != nil { t.Fatalf("Signature.Verify() error = %v", err) } // Now try with the canonical value (this is a detached signature) cb, err := s.CanonicalValue() if err != nil { t.Fatal(err) } canonicalSig, err := NewSignature(bytes.NewReader(cb)) if err != nil { t.Fatal(err) } if err := canonicalSig.Verify(bytes.NewReader(data), pub); err != nil { t.Fatalf("CanonicalSignature.Verify() error = %v", err) } }) } } func TestSignature_VerifyFail(t *testing.T) { tests := []struct { name string pkcs7 string }{ { name: "ec", pkcs7: pkcsECDSAPEM, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Make some fake data, and tamper with the signature s, err := NewSignature(strings.NewReader(tt.pkcs7)) if err != nil { t.Fatal(err) } pub, err := NewPublicKey(strings.NewReader(tt.pkcs7)) if err != nil { t.Fatal(err) } data := []byte("something that shouldn't verify") if err := s.Verify(bytes.NewReader(data), pub); err == nil { t.Error("Signature.Verify() expected error!") } }) } } func TestEmailAddresses(t *testing.T) { tests := []struct { name string pkcs7 string emails []string }{ { name: "ec", pkcs7: pkcsECDSAPEM, emails: []string{}, }, { name: "email", pkcs7: pkcsPEMEmail, emails: []string{"test@rekor.dev"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pub, err := NewPublicKey(strings.NewReader(tt.pkcs7)) if err != nil { t.Fatal(err) } emails := pub.EmailAddresses() if len(emails) == len(tt.emails) { if len(emails) > 0 { sort.Strings(emails) sort.Strings(tt.emails) if !reflect.DeepEqual(emails, tt.emails) { t.Errorf("%v: Error getting email addresses from keys, got %v, expected %v", tt.name, emails, tt.emails) } } } else { t.Errorf("%v: Error getting email addresses from keys, got %v, expected %v", tt.name, emails, tt.emails) } }) } } func TestSubjects(t *testing.T) { // dynamically generate a PKCS7 structure with multiple subjects set url, _ := url.Parse("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.1.1") rootCert, rootKey, _ := testutils.GenerateRootCa() leafCert, leafKey, _ := testutils.GenerateLeafCert("subject@example.com", "oidc-issuer", url, rootCert, rootKey) b := pkcs7.NewBuilder(leafKey, []*x509.Certificate{leafCert}, crypto.SHA256) // set content to random data, only the certificate matters err := b.SetContentData([]byte{1, 2, 3, 4}) if err != nil { t.Fatalf("error setting content data in pkcs7: %v", err) } s, err := b.Sign() if err != nil { t.Fatalf("error signing pkcs7: %v", err) } pkcs7bytes, err := s.Marshal() if err != nil { t.Fatalf("error marshalling pkcs7: %v", err) } tests := []struct { name string pkcs7 string subs []string }{ { name: "ec", pkcs7: pkcsECDSAPEM, subs: []string{}, }, { name: "email in subject", pkcs7: pkcsPEMEmail, subs: []string{"test@rekor.dev"}, }, { name: "email and URI in subject alternative name", pkcs7: string(pkcs7bytes), subs: []string{"subject@example.com", "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.1.1"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pub, err := NewPublicKey(strings.NewReader(tt.pkcs7)) if err != nil { t.Fatal(err) } subs := pub.Subjects() if len(subs) == len(tt.subs) { if len(subs) > 0 { sort.Strings(subs) sort.Strings(tt.subs) if !reflect.DeepEqual(subs, tt.subs) { t.Errorf("%v: Error getting subjects from keys, got %v, expected %v", tt.name, subs, tt.subs) } } } else { t.Errorf("%v: Error getting subjects from keys, got %v, expected %v", tt.name, subs, tt.subs) } }) } } func TestIdentities(t *testing.T) { // dynamically generate a PKCS7 structure with multiple subjects set url, _ := url.Parse("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.1.1") rootCert, rootKey, _ := testutils.GenerateRootCa() leafCert, leafKey, _ := testutils.GenerateLeafCert("subject@example.com", "oidc-issuer", url, rootCert, rootKey) leafPEM, _ := cryptoutils.MarshalPublicKeyToPEM(leafKey.Public()) leafCertPEM, _ := cryptoutils.MarshalCertificateToPEM(leafCert) b := pkcs7.NewBuilder(leafKey, []*x509.Certificate{leafCert}, crypto.SHA256) // set content to random data, only the certificate matters err := b.SetContentData([]byte{1, 2, 3, 4}) if err != nil { t.Fatalf("error setting content data in pkcs7: %v", err) } s, err := b.Sign() if err != nil { t.Fatalf("error signing pkcs7: %v", err) } pkcs7bytes, err := s.Marshal() if err != nil { t.Fatalf("error marshalling pkcs7: %v", err) } pkcsECDSAPEMKey := `-----BEGIN PUBLIC KEY----- MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEtLIDsiLmT6k07xkRZjTKV6ZYOjA6Q1re Qv4ZkTlnkZZ6Ev38D1tE0DdFuuxnmLQlxqy1pEvtKnl+n+MPl3Gpz9R1NWeW9LXp qf7Zh+zB79C4uiVFQKtw4Tb7aIDn63N3 -----END PUBLIC KEY----- ` pkcsKeyCert := `-----BEGIN CERTIFICATE----- MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAq MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx MDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUu ZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSy A7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0Jcas taRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6Nm MGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE FMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2u Su1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJx Ve/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uup Hr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ== -----END CERTIFICATE----- ` pkcsPEMEmailKey := `-----BEGIN PUBLIC KEY----- MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQATdJNkml+YiugIcPJvsF20JpeBPBh C9KiwJQNBrvLYZYbwHDi3T8zcT5drbDO2wc06w9h+IizOJsr6U+MdjYG48gApb4V 3yUzmJu6ExXCqtJDX/CxFdn4waX3a8PcoktQ1emFh8As3N01K2SAoRoTGJ32T1bv fe8M8nrlQUlDHjuS5qc= -----END PUBLIC KEY----- ` pkcsEmailCert := `-----BEGIN CERTIFICATE----- MIIC2TCCAjqgAwIBAgIUAL0Gw2SJvPW8PbXw+42XwmW8//owCgYIKoZIzj0EAwIw fTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1BMQ8wDQYDVQQHDAZCb3N0b24xITAf BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEOMAwGA1UEAwwFUmVrb3Ix HTAbBgkqhkiG9w0BCQEWDnRlc3RAcmVrb3IuZGV2MCAXDTIxMDQxOTE0MTMyMFoY DzQ0ODUwNTMxMTQxMzIwWjB9MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTUExDzAN BgNVBAcMBkJvc3RvbjEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk MQ4wDAYDVQQDDAVSZWtvcjEdMBsGCSqGSIb3DQEJARYOdGVzdEByZWtvci5kZXYw gZswEAYHKoZIzj0CAQYFK4EEACMDgYYABABN0k2SaX5iK6Ahw8m+wXbQml4E8GEL 0qLAlA0Gu8thlhvAcOLdPzNxPl2tsM7bBzTrD2H4iLM4myvpT4x2NgbjyAClvhXf JTOYm7oTFcKq0kNf8LEV2fjBpfdrw9yiS1DV6YWHwCzc3TUrZIChGhMYnfZPVu99 7wzyeuVBSUMeO5Lmp6NTMFEwHQYDVR0OBBYEFJPLiMMFN5Cm6/rjOTPR2HWbbO5P MB8GA1UdIwQYMBaAFJPLiMMFN5Cm6/rjOTPR2HWbbO5PMA8GA1UdEwEB/wQFMAMB Af8wCgYIKoZIzj0EAwIDgYwAMIGIAkIBmRqxw8sStWknjeOgdyKkd+vFehNuVaiH AKGsz+6KG3jPG5xN5+/Ws+OMTAp7Hv6HH5ChDO3LJ6t/sCun1otdWmICQgCUqg1k e+RjnVqVlz1rUR7CTL2SlG9Xg1kAkYH4vMn/otEuAhnKf+GWLNB1l/dTFNEyysvI A6ydFG8HXGWcnVVIVQ== -----END CERTIFICATE----- ` tests := []struct { name string pkcs7 string identities []string }{ { name: "ec", pkcs7: pkcsECDSAPEM, identities: []string{pkcsKeyCert, pkcsECDSAPEMKey}, }, { name: "email in subject", pkcs7: pkcsPEMEmail, identities: []string{pkcsEmailCert, pkcsPEMEmailKey}, }, { name: "email and URI in subject alternative name", pkcs7: string(pkcs7bytes), identities: []string{string(leafCertPEM), string(leafPEM)}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { pub, err := NewPublicKey(strings.NewReader(tt.pkcs7)) if err != nil { t.Fatal(err) } ids, err := pub.Identities() if err != nil { t.Fatalf("unexpected error getting identities: %v", err) } if len(ids) != 2 { t.Fatalf("expected 2 identities, got %d", len(ids)) } // compare certificate cert, _ := cryptoutils.UnmarshalCertificatesFromPEM([]byte(tt.identities[0])) digest := sha256.Sum256(cert[0].Raw) expectedID := identity.Identity{Crypto: cert[0], Raw: cert[0].Raw, Fingerprint: hex.EncodeToString(digest[:])} if !ids[0].Crypto.(*x509.Certificate).Equal(expectedID.Crypto.(*x509.Certificate)) { t.Errorf("certificates did not match") } if !reflect.DeepEqual(ids[0].Raw, expectedID.Raw) { t.Errorf("raw identities did not match, expected %v, got %v", string(expectedID.Raw), ids[0].Raw) } if ids[0].Fingerprint != expectedID.Fingerprint { t.Fatalf("fingerprints did not match, expected %v, got %v", expectedID.Fingerprint, ids[0].Fingerprint) } // compare public key key, _ := cryptoutils.UnmarshalPEMToPublicKey([]byte(tt.identities[1])) pkixKey, _ := cryptoutils.MarshalPublicKeyToDER(key) digest = sha256.Sum256(pkixKey) expectedID = identity.Identity{Crypto: key, Raw: pkixKey, Fingerprint: hex.EncodeToString(digest[:])} if err := cryptoutils.EqualKeys(expectedID.Crypto, ids[1].Crypto); err != nil { t.Errorf("%v: public keys did not match: %v", tt.name, err) } if !reflect.DeepEqual(ids[1].Raw, expectedID.Raw) { t.Errorf("%v: raw key identities did not match", tt.name) } if ids[1].Fingerprint != expectedID.Fingerprint { t.Fatalf("key fingerprints did not match, expected %v, got %v", expectedID.Fingerprint, ids[1].Fingerprint) } }) } } rekor-1.3.5/pkg/pki/pki.go000066400000000000000000000025701455727245600153450ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package pki import ( "io" "github.com/sigstore/rekor/pkg/pki/identity" sigsig "github.com/sigstore/sigstore/pkg/signature" ) // PublicKey Generic object representing a public key (regardless of format & algorithm) type PublicKey interface { CanonicalValue() ([]byte, error) // Deprecated: EmailAddresses() will be deprecated in favor of Subjects() which will // also return Subject URIs present in public keys. EmailAddresses() []string Subjects() []string // Identities returns a list of typed keys and certificates. Identities() ([]identity.Identity, error) } // Signature Generic object representing a signature (regardless of format & algorithm) type Signature interface { CanonicalValue() ([]byte, error) Verify(r io.Reader, k interface{}, opts ...sigsig.VerifyOption) error } rekor-1.3.5/pkg/pki/ssh/000077500000000000000000000000001455727245600150245ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/ssh/README.md000066400000000000000000000102571455727245600163100ustar00rootroot00000000000000# SSH File Signatures SSH keys can be used to sign files! Unfortunately this is a pretty recent change to the openssh tooling, so it is not supported by golang.org/x/crypto/ssh yet. This document explains how it works at a high level. ## Keys SSH keys are usually split into public and private files, named `id_rsa.pub` and `id_rsa`, respectively. These files are encoded and formatted a little differently than other signing keys. ### Public Keys These are typically in the "known hosts" format. This looks something like: ``` ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDw0ZWP4zZLELSJVenQTQsrFJVBnoP64KTg/UWRU6qOb8HEOdtHJDOyTmo9dvN/yJoTFtWAfQEjaTsMVJzTD0gOk6ncTsp0BUtgXawSCfEUiv7v+2VgSVbUfAv/NL+HEGSCdcORnansIyrZaHwAjR3ei3O+pRWvgjRj3pOH1rWGrxaC5IbsELYzS/HvwAG/uwcxgBv4POvaq6eCEHVbqRjIYjjoYsC+c24sgSQxOyXvDS7j2z9TPHPvepDhVr9y6xnnqhLqZEWmidRrbb35aYkVLJxmGTFy/JW1cewyU2Jb3+sKQOiOwL7DAB39tRyec2ed+EHh6QLW4pcMnoXsWuPyi+G595HiUYmIlqXJ5JPo0Cv/rOJrmWSFceWiDjC/SeODp/AcK0EsN/p3wOp6ac7EzAz9Npri0vwSQX4MUYlya/olKiKCx5GIhTZtXioREPd8v4osx2VrVyDxKX99PVVbxw1FXSe4u+PuOawJzUA4vW41mxUY9zoAsb/fvoNPtrrT9HfC+7Pg6ryBdz+445M8Atc8YjjLeYXkTXWD6KMielRzBFFoIwIgi0bMotq3iQ9IwjQSXPMDQLb+UPg8xqsgRsX3wvyZzdBhxO4Bdomv7JYmySysaGgliHktU8qRse1lpDIXMovPtowywcKL4U3seDKrq7saVO0qdsLavy1o0w== lorenc.d@gmail.com ``` These can be parsed with [ParseKnownHosts](https://pkg.go.dev/golang.org/x/crypto/ssh#ParseKnownHosts) , NOT `ParsePublicKey`. In addition to the key material itself, this can contain the algorithm (`ssh-rsa` here) and a comment (lorenc.d@gmail.com) here. ### Private Keys These are stored in an "armored" PEM format, resembling PGP or x509 keys: ``` -----BEGIN SSH PRIVATE KEY----- -----END SSH PRIVATE KEY----- ``` These can be parsed correctly with [ParsePrivateKey](https://pkg.go.dev/golang.org/x/crypto/ssh#ParsePrivateKey). ## Wire Format The wire format is relatively standard. * Bytes are laid out in order. * Fixed-length fields are laid out at the proper offset with the specified length. * Strings are stored with the size as a prefix. ## Signature These can be generated and validated from the command line with the `ssh-keygen -Y` set of commands: `sign`, `verify`, and `check-novalidate`. To work with them in Go is a little tricker. The signature is stored using a struct packed using the `openssh` wire format. The data that is used in the signing function is also packed in another struct before it is signed. ### Signature Format Signatures are formatted on disk in a PEM-encoded format. The header is `-----BEGIN SSH SIGNATURE-----`, and the end is `-----END SSH SIGNATURE-----`. The signature contents are base64-encoded. The signature contents are wrapped with extra metadata, then encoded as a struct using the `openssh` wire format. That struct is defined [here](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig#L34). In Go: ``` type WrappedSig struct { MagicHeader [6]byte Version uint32 PublicKey string Namespace string Reserved string HashAlgorithm string Signature string } ``` The `PublicKey` and `Signature` fields are also stored as openssh-wire-formatted structs. The `MagicHeader` is `SSHSIG`. The `Version` is 1. The `Namespace` is `file` (for this use-case). `Reserved` must be empty. Go can already parse the `PublicKey` and `Signature` fields, and the `Signature` struct contains a `Blob` with the signature data. ### Signed Message In addition to these wrappers, the message to be signed is wrapped with some metadata before it is passed to the signing function. That wrapper is defined [here](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig#L81). And in Go: ``` type MessageWrapper struct { Namespace string Reserved string HashAlgorithm string Hash string } ```. So, the data must first be hashed, then packed in this struct and encoded in the openssh wire format. Then, this resulting data is signed using the desired signature function. The `Namespace` field must be `file` (for this usecase). The `Reserved` field must be empty. The output of this signature function (and the hash) becomes the `Signature.Blob` value, which gets wire-encoded, wrapped, wire-encoded and finally pem-encoded. rekor-1.3.5/pkg/pki/ssh/e2e_test.go000066400000000000000000000036501455727245600170710ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package ssh import ( "github.com/sigstore/rekor/pkg/util" "io/ioutil" "path/filepath" "strings" "testing" ) func TestSSH(t *testing.T) { td := t.TempDir() // Create a keypair keyPath := filepath.Join(td, "id_rsa") pubPath := filepath.Join(td, "id_rsa.pub") if err := ioutil.WriteFile(pubPath, []byte(publicKey), 0600); err != nil { t.Fatal(err) } if err := ioutil.WriteFile(keyPath, []byte(privateKey), 0600); err != nil { t.Fatal(err) } // Create a random artifact and sign it. artifactPath := filepath.Join(td, "artifact") sigPath := filepath.Join(td, "signature.sig") artifact := util.CreateArtifact(t, artifactPath) sig := SSHSign(t, strings.NewReader(artifact)) if err := ioutil.WriteFile(sigPath, []byte(sig), 0600); err != nil { t.Fatal(err) } // Now upload to the log! out := util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath, "--pki-format", "ssh") util.OutputContains(t, out, "Created entry at") uuid := util.GetUUIDFromUploadOutput(t, out) out = util.RunCli(t, "verify", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath, "--pki-format", "ssh") util.OutputContains(t, out, "Inclusion Proof") out = util.RunCli(t, "search", "--public-key", pubPath, "--pki-format", "ssh") util.OutputContains(t, out, uuid) } rekor-1.3.5/pkg/pki/ssh/encode.go000066400000000000000000000046521455727245600166170ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ssh import ( "encoding/pem" "errors" "fmt" "golang.org/x/crypto/ssh" ) const ( namespace = "file" pemType = "SSH SIGNATURE" ) func Armor(s *ssh.Signature, p ssh.PublicKey) []byte { sig := WrappedSig{ Version: 1, PublicKey: string(p.Marshal()), Namespace: namespace, HashAlgorithm: defaultHashAlgorithm, Signature: string(ssh.Marshal(s)), } copy(sig.MagicHeader[:], magicHeader) enc := pem.EncodeToMemory(&pem.Block{ Type: pemType, Bytes: ssh.Marshal(sig), }) return enc } func Decode(b []byte) (*Signature, error) { pemBlock, _ := pem.Decode(b) if pemBlock == nil { return nil, errors.New("unable to decode pem file") } if pemBlock.Type != pemType { return nil, fmt.Errorf("wrong pem block type: %s. Expected SSH-SIGNATURE", pemBlock.Type) } // Now we unmarshal it into the Signature block sig := WrappedSig{} if err := ssh.Unmarshal(pemBlock.Bytes, &sig); err != nil { return nil, err } if sig.Version != 1 { return nil, fmt.Errorf("unsupported signature version: %d", sig.Version) } if string(sig.MagicHeader[:]) != magicHeader { return nil, fmt.Errorf("invalid magic header: %s", sig.MagicHeader[:]) } if sig.Namespace != "file" { return nil, fmt.Errorf("invalid signature namespace: %s", sig.Namespace) } if _, ok := supportedHashAlgorithms[sig.HashAlgorithm]; !ok { return nil, fmt.Errorf("unsupported hash algorithm: %s", sig.HashAlgorithm) } // Now we can unpack the Signature and PublicKey blocks sshSig := ssh.Signature{} if err := ssh.Unmarshal([]byte(sig.Signature), &sshSig); err != nil { return nil, err } // TODO: check the format here (should be rsa-sha512) pk, err := ssh.ParsePublicKey([]byte(sig.PublicKey)) if err != nil { return nil, err } return &Signature{ signature: &sshSig, pk: pk, hashAlg: sig.HashAlgorithm, }, nil } rekor-1.3.5/pkg/pki/ssh/sign.go000066400000000000000000000047511455727245600163220ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ssh import ( "crypto/rand" "crypto/sha256" "crypto/sha512" "hash" "io" "golang.org/x/crypto/ssh" ) // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig#L81 type MessageWrapper struct { Namespace string Reserved string HashAlgorithm string Hash string } // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig#L34 type WrappedSig struct { MagicHeader [6]byte Version uint32 PublicKey string Namespace string Reserved string HashAlgorithm string Signature string } const ( magicHeader = "SSHSIG" defaultHashAlgorithm = "sha512" ) var supportedHashAlgorithms = map[string]func() hash.Hash{ "sha256": sha256.New, "sha512": sha512.New, } func sign(s ssh.AlgorithmSigner, m io.Reader) (*ssh.Signature, error) { hf := sha512.New() if _, err := io.Copy(hf, m); err != nil { return nil, err } mh := hf.Sum(nil) sp := MessageWrapper{ Namespace: "file", HashAlgorithm: defaultHashAlgorithm, Hash: string(mh), } dataMessageWrapper := ssh.Marshal(sp) dataMessageWrapper = append([]byte(magicHeader), dataMessageWrapper...) // ssh-rsa is not supported for RSA keys: // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig#L71 // We can use the default value of "" for other key types though. algo := "" if s.PublicKey().Type() == ssh.KeyAlgoRSA { algo = ssh.KeyAlgoRSASHA512 } sig, err := s.SignWithAlgorithm(rand.Reader, dataMessageWrapper, algo) if err != nil { return nil, err } return sig, nil } func Sign(sshPrivateKey string, data io.Reader) ([]byte, error) { s, err := ssh.ParsePrivateKey([]byte(sshPrivateKey)) if err != nil { return nil, err } as, ok := s.(ssh.AlgorithmSigner) if !ok { return nil, err } sig, err := sign(as, data) if err != nil { return nil, err } armored := Armor(sig, s.PublicKey()) return armored, nil } rekor-1.3.5/pkg/pki/ssh/sign_test.go000066400000000000000000000320711455727245600173550ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ssh import ( "bytes" "os" "os/exec" "path/filepath" "strings" "testing" ) var ( // Generated with "ssh-keygen -C test@rekor.dev -f id_rsa" sshPrivateKey = `-----BEGIN OPENSSH PRIVATE KEY----- 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 -----END OPENSSH PRIVATE KEY----- ` sshPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDXofkiahE7uavjWvxnwkUF27qMgz7pdTwzSv0XzVG6EtirOv3PDWct4YKoXE9c0EqbxnIfYEKwEextdvB7zkgwczdJSHxf/18jQumLn/FuoCmugVSk1H5Qli/qzwBpaTnOk3WuakGuoYUl8ZAokKKgOKLA0aZJ1WRQ2ZCZggA3EkwNZiY17y9Q6HqdgQcH6XN8aAMADNVJdMAJb33hSRJjjsAPTmzBTishP8lYDoGRSsSE7/8XRBCEV5E4I8mI9GElcZwV/1KJx98mpH8QvMzXM1idFcwPRtt1NTAOshwgUU0Fu1x8lU5RQIa6ZKW36qNQLvLxy/BscC7B/mdLptoDs/ot9NimUXZcgCR1a2Q3o7Wi6jIgcgJcyV10Nba81ol4RdN4qPHnVZIzuo+dBkqwG3CMtB4Rj84+Qi+7zyU01hIPreoxQDXaayiGPBUUIiAlW9gsiuRWJzNnu3cvuWDLVfQIkjh7Wug58z+v2NOJ7IMdyERillhzDcvVHaq14+U= test@rekor.dev ` // Generated with "ssh-keygen -C other-test@rekor.dev -f id_rsa" otherSSHPrivateKey = `-----BEGIN OPENSSH PRIVATE KEY----- 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 -----END OPENSSH PRIVATE KEY----- ` otherSSHPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDD9YJJYL1MS8JA7A75PrwS81rc5Ii/VjT6xPy5K/KM/IQ7T6nS3wKD/PLru87GJClTmM3owi2NcCMyZ4KbwYoy3qCT5wOhoB6/9l+O7NIvtFrITGmaEGV6HZfcYouSRcX0AEU1yGVOpIs5mISMOg2lsW/XopPWwToKpbwOPFdCRCT0krrmEsf4HF5Yw0IQlVoKZrhfThomYLvMkCLnIZ55PRIpWoyiFq8X3Q7peJgUJAe7Bc8/Id+hyqC52ZeejPP7oPprEkpkzBCw2ndYq6Y6OXNafEEIIHWXaM3pFqDxonbbvuIwVdHCNMv/yNoSxbgqTKwN/QaNXb+KpuvSrlvRqsNhu/sKsYFH64fTAbP9miDXHmJkA05uFlQukOstUmJ0QxzbsdcFvs8yw0PLsEZhEHXJzR3TLzenyZSq86VZICvGfVacBk7TikCBOtyWuESHhlc6SfZKfzZ67cOlDyKeSiVjgh+eEh9s4h56ahQ2rW05Sq6GjD0YtEzog2J4csE= other-test@rekor.dev ` // Generated with ssh-keygen -C test@rekor.dev -t ed25519 -f id_ed25519 ed25519PrivateKey = `-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACBB45zRHxPPFtabwS3Vd6Lb9vMe+tIHZj2qN5VQ+bgLfQAAAJgyRa3cMkWt 3AAAAAtzc2gtZWQyNTUxOQAAACBB45zRHxPPFtabwS3Vd6Lb9vMe+tIHZj2qN5VQ+bgLfQ AAAED7y4N/DsVnRQiBZNxEWdsJ9RmbranvtQ3X9jnb6gFed0HjnNEfE88W1pvBLdV3otv2 8x760gdmPao3lVD5uAt9AAAADnRlc3RAcmVrb3IuZGV2AQIDBAUGBw== -----END OPENSSH PRIVATE KEY----- ` ed25519PublicKey = `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEHjnNEfE88W1pvBLdV3otv28x760gdmPao3lVD5uAt9 test@rekor.dev ` ) func TestFromOpenSSH(t *testing.T) { for _, tt := range []struct { name string pub string priv string }{ { name: "rsa", pub: sshPublicKey, priv: sshPrivateKey, }, { name: "ed25519", pub: ed25519PublicKey, priv: ed25519PrivateKey, }, } { if _, err := exec.LookPath("ssh-keygen"); err != nil { t.Skip("skip TestFromOpenSSH: missing ssh-keygen in PATH") } t.Run(tt.name, func(t *testing.T) { tt := tt // Test that a signature from the cli can validate here. td := t.TempDir() data := []byte("hello, ssh world") dataPath := write(t, []byte(data), td, "data") privPath := write(t, []byte(tt.priv), td, "id") write(t, []byte(tt.pub), td, "id.pub") sigPath := dataPath + ".sig" run(t, nil, "ssh-keygen", "-Y", "sign", "-n", "file", "-f", privPath, dataPath) sigBytes, err := os.ReadFile(sigPath) if err != nil { t.Fatal(err) } if err := Verify(bytes.NewReader(data), sigBytes, []byte(tt.pub)); err != nil { t.Error(err) } // It should not verify if we check against another public key if err := Verify(bytes.NewReader(data), sigBytes, []byte(otherSSHPublicKey)); err == nil { t.Error("expected error with incorrect key") } // It should not verify if the data is tampered if err := Verify(strings.NewReader("bad data"), sigBytes, []byte(sshPublicKey)); err == nil { t.Error("expected error with incorrect data") } }) } } func TestToOpenSSH(t *testing.T) { for _, tt := range []struct { name string pub string priv string }{ { name: "rsa", pub: sshPublicKey, priv: sshPrivateKey, }, { name: "ed25519", pub: ed25519PublicKey, priv: ed25519PrivateKey, }, } { if _, err := exec.LookPath("ssh-keygen"); err != nil { t.Skip("skip TestToOpenSSH: missing ssh-keygen in PATH") } t.Run(tt.name, func(t *testing.T) { tt := tt // Test that a signature from here can validate in the CLI. td := t.TempDir() data := []byte("hello, ssh world") write(t, []byte(data), td, "data") armored, err := Sign(tt.priv, bytes.NewReader(data)) if err != nil { t.Fatal(err) } sigPath := write(t, []byte(armored), td, "oursig") // Create an allowed_signers file with two keys to check against. allowedSigner := "test@rekor.dev " + tt.pub + "\n" allowedSigner += "othertest@rekor.dev " + otherSSHPublicKey + "\n" allowedSigners := write(t, []byte(allowedSigner), td, "allowed_signer") // We use the correct principal here so it should work. run(t, data, "ssh-keygen", "-Y", "verify", "-f", allowedSigners, "-I", "test@rekor.dev", "-n", "file", "-s", sigPath) // Just to be sure, check against the other public key as well. runErr(t, data, "ssh-keygen", "-Y", "verify", "-f", allowedSigners, "-I", "othertest@rekor.dev", "-n", "file", "-s", sigPath) // It should error if we run it against other data data = []byte("other data!") runErr(t, data, "ssh-keygen", "-Y", "check-novalidate", "-n", "file", "-s", sigPath) }) } } func TestRoundTrip(t *testing.T) { data := []byte("my good data to be signed!") // Create one extra signature for all the tests. otherSig, err := Sign(otherSSHPrivateKey, bytes.NewReader(data)) if err != nil { t.Fatal(err) } for _, tt := range []struct { name string pub string priv string }{ { name: "rsa", pub: sshPublicKey, priv: sshPrivateKey, }, { name: "ed25519", pub: ed25519PublicKey, priv: ed25519PrivateKey, }, } { t.Run(tt.name, func(t *testing.T) { tt := tt sig, err := Sign(tt.priv, bytes.NewReader(data)) if err != nil { t.Fatal(err) } // Check the signature against that data and public key if err := Verify(bytes.NewReader(data), sig, []byte(tt.pub)); err != nil { t.Error(err) } // Now check it against invalid data. if err := Verify(strings.NewReader("invalid data!"), sig, []byte(tt.pub)); err == nil { t.Error("expected error!") } // Now check it against the wrong key. if err := Verify(bytes.NewReader(data), sig, []byte(otherSSHPublicKey)); err == nil { t.Error("expected error!") } // Now check it against an invalid signature data. if err := Verify(bytes.NewReader(data), []byte("invalid signature!"), []byte(tt.pub)); err == nil { t.Error("expected error!") } // Once more, use the wrong signature and check it against the original (wrong public key) if err := Verify(bytes.NewReader(data), otherSig, []byte(tt.pub)); err == nil { t.Error("expected error!") } // It should work against the correct public key. if err := Verify(bytes.NewReader(data), otherSig, []byte(otherSSHPublicKey)); err != nil { t.Error(err) } }) } } func write(t *testing.T, d []byte, fp ...string) string { p := filepath.Join(fp...) if err := os.WriteFile(p, d, 0600); err != nil { t.Fatal(err) } return p } func run(t *testing.T, stdin []byte, args ...string) { t.Helper() /* #nosec */ cmd := exec.Command(args[0], args[1:]...) cmd.Stdin = bytes.NewReader(stdin) out, err := cmd.CombinedOutput() t.Logf("cmd %v: %s", cmd, string(out)) if err != nil { t.Fatal(err) } } func runErr(t *testing.T, stdin []byte, args ...string) { t.Helper() /* #nosec */ cmd := exec.Command(args[0], args[1:]...) cmd.Stdin = bytes.NewReader(stdin) out, err := cmd.CombinedOutput() t.Logf("cmd %v: %s", cmd, string(out)) if err == nil { t.Fatal("expected error") } } rekor-1.3.5/pkg/pki/ssh/ssh.go000066400000000000000000000136331455727245600161560ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ssh import ( "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "encoding/binary" "errors" "fmt" "io" "net/http" "github.com/asaskevich/govalidator" "github.com/sigstore/rekor/pkg/pki/identity" "github.com/sigstore/sigstore/pkg/cryptoutils" sigsig "github.com/sigstore/sigstore/pkg/signature" "golang.org/x/crypto/ssh" ) type Signature struct { signature *ssh.Signature pk ssh.PublicKey hashAlg string } // NewSignature creates and Validates an ssh signature object func NewSignature(r io.Reader) (*Signature, error) { b, err := io.ReadAll(r) if err != nil { return nil, err } sig, err := Decode(b) if err != nil { return nil, err } return sig, nil } // CanonicalValue implements the pki.Signature interface func (s Signature) CanonicalValue() ([]byte, error) { return []byte(Armor(s.signature, s.pk)), nil } // Verify implements the pki.Signature interface func (s Signature) Verify(r io.Reader, k interface{}, _ ...sigsig.VerifyOption) error { if s.signature == nil { return fmt.Errorf("ssh signature has not been initialized") } key, ok := k.(*PublicKey) if !ok { return fmt.Errorf("invalid public key type for: %v", k) } ck, err := key.CanonicalValue() if err != nil { return err } cs, err := s.CanonicalValue() if err != nil { return err } return Verify(r, cs, ck) } // PublicKey contains an ssh PublicKey type PublicKey struct { key ssh.PublicKey comment string } // NewPublicKey implements the pki.PublicKey interface func NewPublicKey(r io.Reader) (*PublicKey, error) { // 64K seems generous as a limit for valid SSH keys // we use http.MaxBytesReader and pass nil for ResponseWriter to reuse stdlib // and not reimplement this; There is a proposal for this to be fixed in 1.20 // https://github.com/golang/go/issues/51115 // TODO: switch this to stdlib once golang 1.20 comes out rawPub, err := io.ReadAll(http.MaxBytesReader(nil, io.NopCloser(r), 65536)) if err != nil { return nil, err } key, comment, _, _, err := ssh.ParseAuthorizedKey(rawPub) if err != nil { return nil, err } return &PublicKey{key: key, comment: comment}, nil } // CanonicalValue implements the pki.PublicKey interface func (k PublicKey) CanonicalValue() ([]byte, error) { if k.key == nil { return nil, fmt.Errorf("ssh public key has not been initialized") } return ssh.MarshalAuthorizedKey(k.key), nil } // EmailAddresses implements the pki.PublicKey interface func (k PublicKey) EmailAddresses() []string { if govalidator.IsEmail(k.comment) { return []string{k.comment} } return nil } // Subjects implements the pki.PublicKey interface func (k PublicKey) Subjects() []string { return k.EmailAddresses() } // Identities implements the pki.PublicKey interface func (k PublicKey) Identities() ([]identity.Identity, error) { // extract key from SSH certificate if present var sshKey ssh.PublicKey switch v := k.key.(type) { case *ssh.Certificate: sshKey = v.Key default: sshKey = k.key } // Extract crypto.PublicKey from SSH key // Handle sk public keys which do not implement ssh.CryptoPublicKey // Inspired by x/ssh/keys.go // TODO: Simplify after https://github.com/golang/go/issues/62518 var cryptoPubKey crypto.PublicKey if v, ok := sshKey.(ssh.CryptoPublicKey); ok { cryptoPubKey = v.CryptoPublicKey() } else { switch sshKey.Type() { case ssh.KeyAlgoSKECDSA256: var w struct { Curve string KeyBytes []byte Application string Rest []byte `ssh:"rest"` } _, k, ok := parseString(sshKey.Marshal()) if !ok { return nil, fmt.Errorf("error parsing ssh.KeyAlgoSKED25519 key") } if err := ssh.Unmarshal(k, &w); err != nil { return nil, err } if w.Curve != "nistp256" { return nil, errors.New("ssh: unsupported curve") } ecdsaPubKey := new(ecdsa.PublicKey) ecdsaPubKey.Curve = elliptic.P256() //nolint:staticcheck // ignore SA1019 for old code ecdsaPubKey.X, ecdsaPubKey.Y = elliptic.Unmarshal(ecdsaPubKey.Curve, w.KeyBytes) if ecdsaPubKey.X == nil || ecdsaPubKey.Y == nil { return nil, errors.New("ssh: invalid curve point") } cryptoPubKey = ecdsaPubKey case ssh.KeyAlgoSKED25519: var w struct { KeyBytes []byte Application string Rest []byte `ssh:"rest"` } _, k, ok := parseString(sshKey.Marshal()) if !ok { return nil, fmt.Errorf("error parsing ssh.KeyAlgoSKED25519 key") } if err := ssh.Unmarshal(k, &w); err != nil { return nil, err } if l := len(w.KeyBytes); l != ed25519.PublicKeySize { return nil, fmt.Errorf("invalid size %d for Ed25519 public key", l) } cryptoPubKey = ed25519.PublicKey(w.KeyBytes) default: // Should not occur, as rsa, dsa, ecdsa, and ed25519 all implement ssh.CryptoPublicKey return nil, fmt.Errorf("unknown key type: %T", sshKey) } } pkixKey, err := cryptoutils.MarshalPublicKeyToDER(cryptoPubKey) if err != nil { return nil, err } fp := ssh.FingerprintSHA256(k.key) return []identity.Identity{{ Crypto: k.key, Raw: pkixKey, Fingerprint: fp, }}, nil } // Copied by x/ssh/keys.go // TODO: Remove after https://github.com/golang/go/issues/62518 func parseString(in []byte) (out, rest []byte, ok bool) { if len(in) < 4 { return } length := binary.BigEndian.Uint32(in) in = in[4:] if uint32(len(in)) < length { return } out = in[:length] rest = in[length:] ok = true return } rekor-1.3.5/pkg/pki/ssh/ssh_e2e_test.go000066400000000000000000000101401455727245600177360ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package ssh import ( "bytes" "io" "io/ioutil" "testing" ) var ( // Generated with "ssh-keygen -C test@rekor.dev -f id_rsa" privateKey = `-----BEGIN OPENSSH PRIVATE KEY----- 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 -----END OPENSSH PRIVATE KEY----- ` publicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDXofkiahE7uavjWvxnwkUF27qMgz7pdTwzSv0XzVG6EtirOv3PDWct4YKoXE9c0EqbxnIfYEKwEextdvB7zkgwczdJSHxf/18jQumLn/FuoCmugVSk1H5Qli/qzwBpaTnOk3WuakGuoYUl8ZAokKKgOKLA0aZJ1WRQ2ZCZggA3EkwNZiY17y9Q6HqdgQcH6XN8aAMADNVJdMAJb33hSRJjjsAPTmzBTishP8lYDoGRSsSE7/8XRBCEV5E4I8mI9GElcZwV/1KJx98mpH8QvMzXM1idFcwPRtt1NTAOshwgUU0Fu1x8lU5RQIa6ZKW36qNQLvLxy/BscC7B/mdLptoDs/ot9NimUXZcgCR1a2Q3o7Wi6jIgcgJcyV10Nba81ol4RdN4qPHnVZIzuo+dBkqwG3CMtB4Rj84+Qi+7zyU01hIPreoxQDXaayiGPBUUIiAlW9gsiuRWJzNnu3cvuWDLVfQIkjh7Wug58z+v2NOJ7IMdyERillhzDcvVHaq14+U= test@rekor.dev ` ) func SSHSign(t *testing.T, m io.Reader) []byte { t.Helper() data, err := ioutil.ReadAll(m) if err != nil { t.Fatal(err) } sig, err := Sign(privateKey, bytes.NewReader(data)) if err != nil { t.Fatal(err) } return []byte(sig) } rekor-1.3.5/pkg/pki/ssh/ssh_test.go000066400000000000000000000153631455727245600172170ustar00rootroot00000000000000// Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ssh import ( "encoding/base64" "math/rand" "reflect" "strings" "testing" "github.com/sigstore/sigstore/pkg/cryptoutils" "golang.org/x/crypto/ssh" ) func TestIdentities(t *testing.T) { // from ssh_e2e_test.go publicKey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDXofkiahE7uavjWvxnwkUF27qMgz7pdTwzSv0XzVG6EtirOv3PDWct4YKoXE9c0EqbxnIfYEKwEextdvB7zkgwczdJSHxf/18jQumLn/FuoCmugVSk1H5Qli/qzwBpaTnOk3WuakGuoYUl8ZAokKKgOKLA0aZJ1WRQ2ZCZggA3EkwNZiY17y9Q6HqdgQcH6XN8aAMADNVJdMAJb33hSRJjjsAPTmzBTishP8lYDoGRSsSE7/8XRBCEV5E4I8mI9GElcZwV/1KJx98mpH8QvMzXM1idFcwPRtt1NTAOshwgUU0Fu1x8lU5RQIa6ZKW36qNQLvLxy/BscC7B/mdLptoDs/ot9NimUXZcgCR1a2Q3o7Wi6jIgcgJcyV10Nba81ol4RdN4qPHnVZIzuo+dBkqwG3CMtB4Rj84+Qi+7zyU01hIPreoxQDXaayiGPBUUIiAlW9gsiuRWJzNnu3cvuWDLVfQIkjh7Wug58z+v2NOJ7IMdyERillhzDcvVHaq14+U= test@rekor.dev" expectedKey, _, _, _, _ := ssh.ParseAuthorizedKey([]byte(publicKey)) pub, err := NewPublicKey(strings.NewReader(publicKey)) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(pub.EmailAddresses(), []string{"test@rekor.dev"}) { t.Fatalf("expected email address, got %v", pub.EmailAddresses()) } if !reflect.DeepEqual(pub.Subjects(), []string{"test@rekor.dev"}) { t.Fatalf("expected email address as subject, got %v", pub.Subjects()) } keyVal := expectedKey.(ssh.CryptoPublicKey).CryptoPublicKey() pkixKey, err := cryptoutils.MarshalPublicKeyToDER(keyVal) if err != nil { t.Fatal(err) } ids, err := pub.Identities() if err != nil { t.Fatal(err) } if len(ids) != 1 { t.Errorf("too many identities, expected 1, got %v", len(ids)) } if !reflect.DeepEqual(ids[0].Crypto.(ssh.PublicKey).Marshal(), expectedKey.Marshal()) { t.Errorf("certificates did not match") } if !reflect.DeepEqual(ids[0].Raw, pkixKey) { t.Errorf("raw identities did not match, expected %v, got %v", string(pkixKey), string(ids[0].Raw)) } // removing "SHA256:" prefix fp, _ := base64.RawStdEncoding.DecodeString(ids[0].Fingerprint[7:]) if len(fp) != 32 { t.Errorf("fingerprint is not expected length of 32 (32-byte sha256): %d", len(fp)) } } func TestIdentitiesParsesAllKeyTypes(t *testing.T) { for _, k := range []string{ // DSA is not supported "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDXofkiahE7uavjWvxnwkUF27qMgz7pdTwzSv0XzVG6EtirOv3PDWct4YKoXE9c0EqbxnIfYEKwEextdvB7zkgwczdJSHxf/18jQumLn/FuoCmugVSk1H5Qli/qzwBpaTnOk3WuakGuoYUl8ZAokKKgOKLA0aZJ1WRQ2ZCZggA3EkwNZiY17y9Q6HqdgQcH6XN8aAMADNVJdMAJb33hSRJjjsAPTmzBTishP8lYDoGRSsSE7/8XRBCEV5E4I8mI9GElcZwV/1KJx98mpH8QvMzXM1idFcwPRtt1NTAOshwgUU0Fu1x8lU5RQIa6ZKW36qNQLvLxy/BscC7B/mdLptoDs/ot9NimUXZcgCR1a2Q3o7Wi6jIgcgJcyV10Nba81ol4RdN4qPHnVZIzuo+dBkqwG3CMtB4Rj84+Qi+7zyU01hIPreoxQDXaayiGPBUUIiAlW9gsiuRWJzNnu3cvuWDLVfQIkjh7Wug58z+v2NOJ7IMdyERillhzDcvVHaq14+U= test@rekor.dev", "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNLCu01+wpXe3xB5olXCN4SqU2rQu0qjSRKJO4Bg+JRCPU+ENcgdA5srTU8xYDz/GEa4dzK5ldPw4J/gZgSXCMs=", "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBLSsu/HNKiaALhQ26UDv+N0AFdMb26fMVrOKe866CGu6ajSf9HUOhJFdjhseihB2rTalMPr8MrcXNLufii4mL8u4l9fUQXFgwnM/ZpiVPSs6C+8i4u/ZDg7Nx2NXybNIgQ==", "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACB4WgRgGBRo6Uk+cRgg8tJPCbEtGURRWlUA7PDDerXR+P9O6mm3L99Etxsyh5XNYqXyaMNtH5c51ooMajrFwcayAHIhPPb8X3CsTwEfIUQ96aDyHQMotbRfnkn6uefeUTRrSNcqeAndUtVyAqBdqbsq2mgJYXHrz2NUKlPFYgauQi+WQ==", "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIXffBYeYL+WVzVru8npl5JHt2cjlr4ornFTWzoij9sx", "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGRNqlFgED/pf4zXz8IzqA6CALNwYcwgd4MQDmIS1GOtn1SySFObiuyJaOlpqkV5FeEifhxfIC2ejKKtNyO4CysAAAAEc3NoOg== user@host", "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJjzc2a20RjCvN/0ibH6UpGuN9F9hDvD7x182bOesNhHAAAABHNzaDo= user@host", "ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgb1srW/W3ZDjYAO45xLYAwzHBDLsJ4Ux6ICFIkTjb1LEAAAADAQABAAAAYQCkoR51poH0wE8w72cqSB8Sszx+vAhzcMdCO0wqHTj7UNENHWEXGrU0E0UQekD7U+yhkhtoyjbPOVIP7hNa6aRk/ezdh/iUnCIt4Jt1v3Z1h1P+hA4QuYFMHNB+rmjPwAcAAAAAAAAAAAAAAAEAAAAEdGVzdAAAAAAAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAHcAAAAHc3NoLXJzYQAAAAMBAAEAAABhANFS2kaktpSGc+CcmEKPyw9mJC4nZKxHKTgLVZeaGbFZOvJTNzBspQHdy7Q1uKSfktxpgjZnksiu/tFF9ngyY2KFoc+U88ya95IZUycBGCUbBQ8+bhDtw/icdDGQD5WnUwAAAG8AAAAHc3NoLXJzYQAAAGC8Y9Z2LQKhIhxf52773XaWrXdxP0t3GBVo4A10vUWiYoAGepr6rQIoGGXFxT4B9Gp+nEBJjOwKDXPrAevow0T9ca8gZN+0ykbhSrXLE5Ao48rqr3zP4O1/9P7e6gp0gw8=", } { pub, err := NewPublicKey(strings.NewReader(k)) if err != nil { t.Fatal(err) } ids, err := pub.Identities() if err != nil { t.Fatal(err) } if len(ids) == 0 { t.Fatal("expected identities to be found") } } } func randomSuffix(n int) string { const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" b := make([]byte, n) for i := range b { b[i] = letterBytes[rand.Intn(len(letterBytes))] } return string(b) } func TestPubKeyParsingLimit(t *testing.T) { // limit on NewPublicKey should be 65536 bytes, so let's generate a short one first and then extend it to ensure it fails publicKey := "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDXofkiahE7uavjWvxnwkUF27qMgz7pdTwzSv0XzVG6EtirOv3PDWct4YKoXE9c0EqbxnIfYEKwEextdvB7zkgwczdJSHxf/18jQumLn/FuoCmugVSk1H5Qli/qzwBpaTnOk3WuakGuoYUl8ZAokKKgOKLA0aZJ1WRQ2ZCZggA3EkwNZiY17y9Q6HqdgQcH6XN8aAMADNVJdMAJb33hSRJjjsAPTmzBTishP8lYDoGRSsSE7/8XRBCEV5E4I8mI9GElcZwV/1KJx98mpH8QvMzXM1idFcwPRtt1NTAOshwgUU0Fu1x8lU5RQIa6ZKW36qNQLvLxy/BscC7B/mdLptoDs/ot9NimUXZcgCR1a2Q3o7Wi6jIgcgJcyV10Nba81ol4RdN4qPHnVZIzuo+dBkqwG3CMtB4Rj84+Qi+7zyU01hIPreoxQDXaayiGPBUUIiAlW9gsiuRWJzNnu3cvuWDLVfQIkjh7Wug58z+v2NOJ7IMdyERillhzDcvVHaq14+U= " randomLongComment := randomSuffix(32768) validKey := publicKey + randomLongComment if _, err := NewPublicKey(strings.NewReader(validKey)); err != nil { t.Errorf("unexpected error parsing valid-length key: %v", err) } // now we should be exceeding the length validKey += randomLongComment if _, err := NewPublicKey(strings.NewReader(validKey)); err == nil { t.Errorf("expected an error parsing invalid-length key") } } rekor-1.3.5/pkg/pki/ssh/testdata/000077500000000000000000000000001455727245600166355ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/ssh/testdata/hello_world.txt000066400000000000000000000000221455727245600217020ustar00rootroot00000000000000Hello, ssh world! rekor-1.3.5/pkg/pki/ssh/testdata/hello_world.txt.sig000066400000000000000000000022731455727245600224750ustar00rootroot00000000000000-----BEGIN SSH SIGNATURE----- U1NIU0lHAAAAAQAAAZcAAAAHc3NoLXJzYQAAAAMBAAEAAAGBAM2JmobMfIz+/RFT8RWbF4 x69qm8kgPr9ly8cGLGMDzJn4WbTyk141VlXRdogpPWEj78nHsmhijgp14rfJ/tvgwCjl3B MkvTPQfCH5M0tlbpy2LUdo1JP79KOAyssKob93iGPPBnOvg8YlX3HhGi6XZuXovN9m1CG7 mons0VYyeaL7/1Vj4iQGBkXH41jVG+rUZC5xYY6lPMzgxHmsljzRiX99rQoAZOwPjVk8Hu Db7qNkjVlAaZ7kftG7/Q40NqLchsw1Q/OYlQqrdSb+rp43YTOdPpR9eOUbasOkG32/GDz7 ylS7Q7+7OltRNTakOkFSc/h91h1hcf7/YDreOUp9UKTX7EGdCmjArnHaXnKuZRZ6GgtGsZ aeOKJtgW6Xcaff8ydY4ZdJvQTzlhMPoXuOCKoyDkbKmbkwEAn8D1n9lMUsNmANZsCGi9al jQbxaWbFQ/HeUgiU5U813GwHSe5NPDqV3zYtVqEJqWMGIOhUriYIgp+wayFvx3DIXp7kEG awAAAARmaWxlAAAAAAAAAAZzaGE1MTIAAAGUAAAADHJzYS1zaGEyLTUxMgAAAYCKb6hW7f e+oN2hTje0ymbQbm6RCxvUID+qxA3uXpGaHXXOh2JDkjqTZE0aCbFIhrjLKsl5L4eu2Upn OmJf1og4Wi4qpMaFu1QS5j5OMzimJl/60iP0GklpQvfj/BaP2S2NoelTiHvZcbOUxtwVwX /ya5+MdB21mriZDXL8krEnx9XqHMga6ReCUJ1wN/IeA30xWg9k7acoR9q/UfaTauwbEa67 upCfAWLSlQxfTd5LZpw2Re695vXRZnKT3Xf7vXVPhLSLAZBn2zHm+Mk++oy5VGAxjf8+o1 yYSGv6xpiBynenZv50WffLyaZ1X1YtZ/WBiiz7lSdeulZAR3iDSWJYUDo7kREb68trygjV a68kEamlIMCDOo2wbWk8Z+yoYyT6qp9EbiWBuqexFonAnvabxOp3kQ4bGDeXkcjIyauhtZ Y92S+qadtwmi+4cLzo/yVEvIeNsA7NOGSpVI+fdOW45R8E1nQLnf8LczXRESVgv//9KMtx QM1YRBRG6BaBhg8= -----END SSH SIGNATURE----- rekor-1.3.5/pkg/pki/ssh/testdata/id_rsa000066400000000000000000000050521455727245600200230ustar00rootroot00000000000000-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn NhAAAAAwEAAQAAAYEAzYmahsx8jP79EVPxFZsXjHr2qbySA+v2XLxwYsYwPMmfhZtPKTXj VWVdF2iCk9YSPvyceyaGKOCnXit8n+2+DAKOXcEyS9M9B8IfkzS2VunLYtR2jUk/v0o4DK ywqhv3eIY88Gc6+DxiVfceEaLpdm5ei832bUIbuaiezRVjJ5ovv/VWPiJAYGRcfjWNUb6t RkLnFhjqU8zODEeayWPNGJf32tCgBk7A+NWTwe4Nvuo2SNWUBpnuR+0bv9DjQ2otyGzDVD 85iVCqt1Jv6unjdhM50+lH145Rtqw6Qbfb8YPPvKVLtDv7s6W1E1NqQ6QVJz+H3WHWFx/v 9gOt45Sn1QpNfsQZ0KaMCucdpecq5lFnoaC0axlp44om2Bbpdxp9/zJ1jhl0m9BPOWEw+h e44IqjIORsqZuTAQCfwPWf2UxSw2YA1mwIaL1qWNBvFpZsVD8d5SCJTlTzXcbAdJ7k08Op XfNi1WoQmpYwYg6FSuJgiCn7BrIW/HcMhenuQQZrAAAFiCaS9c8mkvXPAAAAB3NzaC1yc2 EAAAGBAM2JmobMfIz+/RFT8RWbF4x69qm8kgPr9ly8cGLGMDzJn4WbTyk141VlXRdogpPW Ej78nHsmhijgp14rfJ/tvgwCjl3BMkvTPQfCH5M0tlbpy2LUdo1JP79KOAyssKob93iGPP BnOvg8YlX3HhGi6XZuXovN9m1CG7mons0VYyeaL7/1Vj4iQGBkXH41jVG+rUZC5xYY6lPM zgxHmsljzRiX99rQoAZOwPjVk8HuDb7qNkjVlAaZ7kftG7/Q40NqLchsw1Q/OYlQqrdSb+ rp43YTOdPpR9eOUbasOkG32/GDz7ylS7Q7+7OltRNTakOkFSc/h91h1hcf7/YDreOUp9UK TX7EGdCmjArnHaXnKuZRZ6GgtGsZaeOKJtgW6Xcaff8ydY4ZdJvQTzlhMPoXuOCKoyDkbK mbkwEAn8D1n9lMUsNmANZsCGi9aljQbxaWbFQ/HeUgiU5U813GwHSe5NPDqV3zYtVqEJqW MGIOhUriYIgp+wayFvx3DIXp7kEGawAAAAMBAAEAAAGBAMOG2MrNctsKo6I9UYY1QSSxwT 9dlSZH7djwppVAZpkdUTTft2HD0tzlDbb8A+QxbLAgzZfV4SC3/l/2TJszpmx0bgzAgmFh tZhQ0orORXvO8120ModbnFoUd9eO3I0nB7fPM9+axJ1rjDytVhx+90tj2Wtz5q6vigKHZ3 I/m1EMO8qH0KBRIx7PurGRrjuKgfnqIT2DPD+2AHnsEFLvLyfrQa0WdHUrrCXLv8Fn/gmV c0i8bRIOk4A3DwPd6qSyNuEU6chOxg/ferh5xNtkaGvkAD/ZXuU1DIJ9itxv+k7/wcpJqx 0S+NEcX9n2TZ3Kf7ZMVOpl92gBRcwQ/ovjE+c4QSRjQiHnSJOQBhCxr2/8r+bwH/CmXS4f gTubD/Ilb8cMKwWyL9PN13iaCgKQRByS5ChBRbNcxplR2ekmPiLKdhoX3qAxeBBmMuX/7U 4/K8yEQZnJjSHio3vogf/ZlH+wWHfXQXzUZcroqXwYohzgeDaF/asAyPOgFXmdMHT0CQAA AMAsz1w28AL05ATW8MBbNsP74sYthQhoNmGTu80CE4KYKu4zN2G0Ku+0HMWzqrcSq63dlw RNGBlpI5NK9RY7xqPe9LUgtXp3Hv/w31XQ8/i5KB2B583Bn1KoAZq+GeoAgO+c+QbGXq6h uo6f0+fgdGvA3T68NMvo/u3LPHWS0PXHwlkh8m8ALfmmhC5AZmkhXtBzs5+TmXyymkLRkj OuW87/RetEEmGco7efViwBspleFNBaBLqMJnmobKLMBHNcSKcAAADBAOeN99nnsDpMhk// kNp8pxesNGN6WzBsRTQjMcaYWe8O07lwcUzOufzmaDUnDH6q43tp2Llc9IWTYp7xKUEqga Btz4UP+bsoDTR8Lbi9QHdQW6980c1mYI2eSaQDTud0pK41lvFuJPFY+yWFbumxM07OZGzt 0SjnieQtk9V30ZbNY8HKC/1UO7RIfJZTwXutJWFlXOphMf/oSz5aArwjJTdxB+Ar0rXZZN 8sBiSqpTLSeggfNvRnH99Y6XdlD5wD7QAAAMEA4zx+kGb1KcIwuwWrbJSn67kj5kQRuaf+ ZQ18Ho7cQWE+JYMBJZvBbI1GWIqBoef4eywRs9jKK/BRFrVx4LxZAYipbr+sg8fH2Jywcz kyzFhn+rKFr5Bp8JcaT6oxEG/MrPAQ80N9AaM9I3wGJGFJpwrUhlJB89a0SnAqJDJ7D2RV jlHsUKsyv4rYqUzn0mZ2UJLyQfKxfAuKNPt1yG78KuLQkFhAT6HwWnY08hQBUKWj5M+hyO vz1uTE39qHTBi3AAAADnRlc3RAcmVrb3IuZGV2AQIDBA== -----END OPENSSH PRIVATE KEY----- rekor-1.3.5/pkg/pki/ssh/testdata/id_rsa.pub000066400000000000000000000010701455727245600206040ustar00rootroot00000000000000ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDNiZqGzHyM/v0RU/EVmxeMevapvJID6/ZcvHBixjA8yZ+Fm08pNeNVZV0XaIKT1hI+/Jx7JoYo4KdeK3yf7b4MAo5dwTJL0z0Hwh+TNLZW6cti1HaNST+/SjgMrLCqG/d4hjzwZzr4PGJV9x4Roul2bl6LzfZtQhu5qJ7NFWMnmi+/9VY+IkBgZFx+NY1Rvq1GQucWGOpTzM4MR5rJY80Yl/fa0KAGTsD41ZPB7g2+6jZI1ZQGme5H7Ru/0ONDai3IbMNUPzmJUKq3Um/q6eN2EznT6UfXjlG2rDpBt9vxg8+8pUu0O/uzpbUTU2pDpBUnP4fdYdYXH+/2A63jlKfVCk1+xBnQpowK5x2l5yrmUWehoLRrGWnjiibYFul3Gn3/MnWOGXSb0E85YTD6F7jgiqMg5Gypm5MBAJ/A9Z/ZTFLDZgDWbAhovWpY0G8WlmxUPx3lIIlOVPNdxsB0nuTTw6ld82LVahCaljBiDoVK4mCIKfsGshb8dwyF6e5BBms= test@rekor.dev rekor-1.3.5/pkg/pki/ssh/verify.go000066400000000000000000000026271455727245600166660ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ssh import ( "io" "golang.org/x/crypto/ssh" ) func Verify(message io.Reader, armoredSignature []byte, publicKey []byte) error { decodedSignature, err := Decode(armoredSignature) if err != nil { return err } desiredPk, _, _, _, err := ssh.ParseAuthorizedKey(publicKey) if err != nil { return err } // Hash the message so we can verify it against the signature. h := supportedHashAlgorithms[decodedSignature.hashAlg]() if _, err := io.Copy(h, message); err != nil { return err } hm := h.Sum(nil) toVerify := MessageWrapper{ Namespace: "file", HashAlgorithm: decodedSignature.hashAlg, Hash: string(hm), } signedMessage := ssh.Marshal(toVerify) signedMessage = append([]byte(magicHeader), signedMessage...) return desiredPk.Verify(signedMessage, decodedSignature.signature) } rekor-1.3.5/pkg/pki/tuf/000077500000000000000000000000001455727245600150255ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/tuf/e2e_test.go000066400000000000000000000026251455727245600170730ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e // +build e2e package tuf import ( "github.com/sigstore/rekor/pkg/util" "path/filepath" "testing" ) func TestTufVerifyUpload(t *testing.T) { artifactPath := filepath.Join(t.TempDir(), "timestamp.json") rootPath := filepath.Join(t.TempDir(), "root.json") createTufSignedArtifact(t, artifactPath, rootPath) // Now upload to rekor! out := util.RunCli(t, "upload", "--artifact", artifactPath, "--public-key", rootPath, "--type", "tuf") util.OutputContains(t, out, "Created entry at") uuid := util.GetUUIDFromUploadOutput(t, out) out = util.RunCli(t, "verify", "--artifact", artifactPath, "--public-key", rootPath, "--type", "tuf") util.OutputContains(t, out, "Inclusion Proof") out = util.RunCli(t, "search", "--public-key", rootPath, "--pki-format", "tuf") util.OutputContains(t, out, uuid) } rekor-1.3.5/pkg/pki/tuf/testdata/000077500000000000000000000000001455727245600166365ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/tuf/testdata/1.root.json000066400000000000000000000143651455727245600206640ustar00rootroot00000000000000{ "signed": { "_type": "root", "spec_version": "1.0", "version": 5, "expires": "2023-04-18T18:13:43Z", "keys": { "25a0eb450fd3ee2bd79218c963dce3f1cc6118badf251bf149f0bd07d5cabe99": { "keytype": "ecdsa-sha2-nistp256", "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": [ "sha256", "sha512" ], "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEXsz3SZXFb8jMV42j6pJlyjbjR8K\nN3Bwocexq6LMIb5qsWKOQvLN16NUefLc4HswOoumRsVVaajSpQS6fobkRw==\n-----END PUBLIC KEY-----\n" } }, "2e61cd0cbf4a8f45809bda9f7f78c0d33ad11842ff94ae340873e2664dc843de": { "keytype": "ecdsa-sha2-nistp256", "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": [ "sha256", "sha512" ], "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0ghrh92Lw1Yr3idGV5WqCtMDB8Cx\n+D8hdC4w2ZLNIplVRoVGLskYa3gheMyOjiJ8kPi15aQ2//7P+oj7UvJPGw==\n-----END PUBLIC KEY-----\n" } }, "45b283825eb184cabd582eb17b74fc8ed404f68cf452acabdad2ed6f90ce216b": { "keytype": "ecdsa-sha2-nistp256", "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": [ "sha256", "sha512" ], "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELrWvNt94v4R085ELeeCMxHp7PldF\n0/T1GxukUh2ODuggLGJE0pc1e8CSBf6CS91Fwo9FUOuRsjBUld+VqSyCdQ==\n-----END PUBLIC KEY-----\n" } }, "7f7513b25429a64473e10ce3ad2f3da372bbdd14b65d07bbaf547e7c8bbbe62b": { "keytype": "ecdsa-sha2-nistp256", "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": [ "sha256", "sha512" ], "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEinikSsAQmYkNeH5eYq/CnIzLaacO\nxlSaawQDOwqKy/tCqxq5xxPSJc21K4WIhs9GyOkKfzueY3GILzcMJZ4cWw==\n-----END PUBLIC KEY-----\n" } }, "e1863ba02070322ebc626dcecf9d881a3a38c35c3b41a83765b6ad6c37eaec2a": { "keytype": "ecdsa-sha2-nistp256", "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": [ "sha256", "sha512" ], "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWRiGr5+j+3J5SsH+Ztr5nE2H2wO7\nBV+nO3s93gLca18qTOzHY1oWyAGDykMSsGTUBSt9D+An0KfKsD2mfSM42Q==\n-----END PUBLIC KEY-----\n" } }, "f5312f542c21273d9485a49394386c4575804770667f2ddb59b3bf0669fddd2f": { "keytype": "ecdsa-sha2-nistp256", "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": [ "sha256", "sha512" ], "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzBzVOmHCPojMVLSI364WiiV8NPrD\n6IgRxVliskz/v+y3JER5mcVGcONliDcWMC5J2lfHmjPNPhb4H7xm8LzfSA==\n-----END PUBLIC KEY-----\n" } }, "ff51e17fcf253119b7033f6f57512631da4a0969442afcf9fc8b141c7f2be99c": { "keytype": "ecdsa-sha2-nistp256", "scheme": "ecdsa-sha2-nistp256", "keyid_hash_algorithms": [ "sha256", "sha512" ], "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEy8XKsmhBYDI8Jc0GwzBxeKax0cm5\nSTKEU65HPFunUn41sT8pi0FjM4IkHz/YUmwmLUO0Wt7lxhj6BkLIK4qYAw==\n-----END PUBLIC KEY-----\n" } } }, "roles": { "root": { "keyids": [ "ff51e17fcf253119b7033f6f57512631da4a0969442afcf9fc8b141c7f2be99c", "25a0eb450fd3ee2bd79218c963dce3f1cc6118badf251bf149f0bd07d5cabe99", "f5312f542c21273d9485a49394386c4575804770667f2ddb59b3bf0669fddd2f", "7f7513b25429a64473e10ce3ad2f3da372bbdd14b65d07bbaf547e7c8bbbe62b", "2e61cd0cbf4a8f45809bda9f7f78c0d33ad11842ff94ae340873e2664dc843de" ], "threshold": 3 }, "snapshot": { "keyids": [ "45b283825eb184cabd582eb17b74fc8ed404f68cf452acabdad2ed6f90ce216b" ], "threshold": 1 }, "targets": { "keyids": [ "ff51e17fcf253119b7033f6f57512631da4a0969442afcf9fc8b141c7f2be99c", "25a0eb450fd3ee2bd79218c963dce3f1cc6118badf251bf149f0bd07d5cabe99", "f5312f542c21273d9485a49394386c4575804770667f2ddb59b3bf0669fddd2f", "7f7513b25429a64473e10ce3ad2f3da372bbdd14b65d07bbaf547e7c8bbbe62b", "2e61cd0cbf4a8f45809bda9f7f78c0d33ad11842ff94ae340873e2664dc843de" ], "threshold": 3 }, "timestamp": { "keyids": [ "e1863ba02070322ebc626dcecf9d881a3a38c35c3b41a83765b6ad6c37eaec2a" ], "threshold": 1 } }, "consistent_snapshot": true }, "signatures": [ { "keyid": "ff51e17fcf253119b7033f6f57512631da4a0969442afcf9fc8b141c7f2be99c", "sig": "3045022100fc1c2be509ce50ea917bbad1d9efe9d96c8c2ebea04af2717aa3d9c6fe617a75022012eef282a19f2d8bd4818aa333ef48a06489f49d4d34a20b8fe8fc867bb25a7a" }, { "keyid": "25a0eb450fd3ee2bd79218c963dce3f1cc6118badf251bf149f0bd07d5cabe99", "sig": "30450221008a4392ae5057fc00778b651e61fea244766a4ae58db84d9f1d3810720ab0f3b702207c49e59e8031318caf02252ecea1281cecc1e5986c309a9cef61f455ecf7165d" }, { "keyid": "7f7513b25429a64473e10ce3ad2f3da372bbdd14b65d07bbaf547e7c8bbbe62b", "sig": "3046022100da1b8dc5d53aaffbbfac98de3e23ee2d2ad3446a7bed09fac0f88bae19be2587022100b681c046afc3919097dfe794e0d819be891e2e850aade315bec06b0c4dea221b" }, { "keyid": "2e61cd0cbf4a8f45809bda9f7f78c0d33ad11842ff94ae340873e2664dc843de", "sig": "3046022100b534e0030e1b271133ecfbdf3ba9fbf3becb3689abea079a2150afbb63cdb7c70221008c39a718fd9495f249b4ab8788d5b9dc269f0868dbe38b272f48207359d3ded9" }, { "keyid": "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "sig": "3045022100fc1c2be509ce50ea917bbad1d9efe9d96c8c2ebea04af2717aa3d9c6fe617a75022012eef282a19f2d8bd4818aa333ef48a06489f49d4d34a20b8fe8fc867bb25a7a" }, { "keyid": "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", "sig": "30450221008a4392ae5057fc00778b651e61fea244766a4ae58db84d9f1d3810720ab0f3b702207c49e59e8031318caf02252ecea1281cecc1e5986c309a9cef61f455ecf7165d" }, { "keyid": "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209", "sig": "3046022100da1b8dc5d53aaffbbfac98de3e23ee2d2ad3446a7bed09fac0f88bae19be2587022100b681c046afc3919097dfe794e0d819be891e2e850aade315bec06b0c4dea221b" }, { "keyid": "75e867ab10e121fdef32094af634707f43ddd79c6bab8ad6c5ab9f03f4ea8c90", "sig": "3046022100b534e0030e1b271133ecfbdf3ba9fbf3becb3689abea079a2150afbb63cdb7c70221008c39a718fd9495f249b4ab8788d5b9dc269f0868dbe38b272f48207359d3ded9" } ] } rekor-1.3.5/pkg/pki/tuf/testdata/bogus.json000066400000000000000000000005631455727245600206540ustar00rootroot00000000000000{ "bogus": [ { "keyid": "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "sig": "3044022079252576532ed5ed4a19e4135997d89172101ed745a4489be6b20d04d483bbcc0220515119aab690033dc1e1650f08995dc839dcd161cab3898db0749063ca32dc86" } ], "can": { "anyone": "timestamp", "even": "2021-12-18T13:28:12.99008-06:00", "hear": "1.0", "me": 1 } } rekor-1.3.5/pkg/pki/tuf/testdata/other_root.json000066400000000000000000000042011455727245600217120ustar00rootroot00000000000000{ "signatures": [ { "keyid": "ce72db3f938914205461a415c9b7b91267a2079df991fd6283aa8461988c1add", "sig": "104eb78cba674adfc7ade1d4396c99466a7d4480e50aab89df53117faffd3d4d4ea87833fe6f83b8dc7f6e1e4a62c0329244c6e5f910627772df3c4d61a8900f" } ], "signed": { "_type": "root", "consistent_snapshot": true, "expires": "2100-01-01T00:00:00Z", "keys": { "289e5a9e71afd7909326aa4caea92f7557ee0e2283d8c31f0c3401ce67248a45": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ed25519", "keyval": { "public": "d482fa4805a50870aa1356ace6b764f7ab47ed4dc38f49b1a189afa25f179e94" }, "scheme": "ed25519" }, "808aa256a8172bb0cb961767c6768e55ccf732c99afccc6145752d7a328b7937": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ed25519", "keyval": { "public": "62f71f99c788f16bcdc8bb252455e3a690350e4ddea5a6aab1f9a3aaabcf369a" }, "scheme": "ed25519" }, "aa3255b4e8e17e566d2bdbea0e5842978f9fa1d2fa9ec76ae76b146164acbfc8": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ed25519", "keyval": { "public": "8656ad20568cd3534c405e4d9a84b0c6e6163f7f66434df77416502835b9b160" }, "scheme": "ed25519" }, "ce72db3f938914205461a415c9b7b91267a2079df991fd6283aa8461988c1add": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ed25519", "keyval": { "public": "bca152214dbfd3e7a1b7a7b3e2cc179fb00520fd7212c8a60ba99f14dfc0e1ca" }, "scheme": "ed25519" } }, "roles": { "root": { "keyids": [ "ce72db3f938914205461a415c9b7b91267a2079df991fd6283aa8461988c1add" ], "threshold": 1 }, "snapshot": { "keyids": [ "289e5a9e71afd7909326aa4caea92f7557ee0e2283d8c31f0c3401ce67248a45" ], "threshold": 1 }, "targets": { "keyids": [ "808aa256a8172bb0cb961767c6768e55ccf732c99afccc6145752d7a328b7937" ], "threshold": 1 }, "timestamp": { "keyids": [ "aa3255b4e8e17e566d2bdbea0e5842978f9fa1d2fa9ec76ae76b146164acbfc8" ], "threshold": 1 } }, "spec_version": "1.0", "version": 1 } }rekor-1.3.5/pkg/pki/tuf/testdata/reformat.1.root.json000066400000000000000000000127451455727245600225020ustar00rootroot00000000000000{"signed":{"_type":"root","spec_version":"1.0","version":5,"expires":"2023-04-18T18:13:43Z","keys":{"25a0eb450fd3ee2bd79218c963dce3f1cc6118badf251bf149f0bd07d5cabe99":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEXsz3SZXFb8jMV42j6pJlyjbjR8K\nN3Bwocexq6LMIb5qsWKOQvLN16NUefLc4HswOoumRsVVaajSpQS6fobkRw==\n-----END PUBLIC KEY-----\n"}},"2e61cd0cbf4a8f45809bda9f7f78c0d33ad11842ff94ae340873e2664dc843de":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0ghrh92Lw1Yr3idGV5WqCtMDB8Cx\n+D8hdC4w2ZLNIplVRoVGLskYa3gheMyOjiJ8kPi15aQ2//7P+oj7UvJPGw==\n-----END PUBLIC KEY-----\n"}},"45b283825eb184cabd582eb17b74fc8ed404f68cf452acabdad2ed6f90ce216b":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELrWvNt94v4R085ELeeCMxHp7PldF\n0/T1GxukUh2ODuggLGJE0pc1e8CSBf6CS91Fwo9FUOuRsjBUld+VqSyCdQ==\n-----END PUBLIC KEY-----\n"}},"7f7513b25429a64473e10ce3ad2f3da372bbdd14b65d07bbaf547e7c8bbbe62b":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEinikSsAQmYkNeH5eYq/CnIzLaacO\nxlSaawQDOwqKy/tCqxq5xxPSJc21K4WIhs9GyOkKfzueY3GILzcMJZ4cWw==\n-----END PUBLIC KEY-----\n"}},"e1863ba02070322ebc626dcecf9d881a3a38c35c3b41a83765b6ad6c37eaec2a":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWRiGr5+j+3J5SsH+Ztr5nE2H2wO7\nBV+nO3s93gLca18qTOzHY1oWyAGDykMSsGTUBSt9D+An0KfKsD2mfSM42Q==\n-----END PUBLIC KEY-----\n"}},"f5312f542c21273d9485a49394386c4575804770667f2ddb59b3bf0669fddd2f":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzBzVOmHCPojMVLSI364WiiV8NPrD\n6IgRxVliskz/v+y3JER5mcVGcONliDcWMC5J2lfHmjPNPhb4H7xm8LzfSA==\n-----END PUBLIC KEY-----\n"}},"ff51e17fcf253119b7033f6f57512631da4a0969442afcf9fc8b141c7f2be99c":{"keytype":"ecdsa-sha2-nistp256","scheme":"ecdsa-sha2-nistp256","keyid_hash_algorithms":["sha256","sha512"],"keyval":{"public":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEy8XKsmhBYDI8Jc0GwzBxeKax0cm5\nSTKEU65HPFunUn41sT8pi0FjM4IkHz/YUmwmLUO0Wt7lxhj6BkLIK4qYAw==\n-----END PUBLIC KEY-----\n"}}},"roles":{"root":{"keyids":["ff51e17fcf253119b7033f6f57512631da4a0969442afcf9fc8b141c7f2be99c","25a0eb450fd3ee2bd79218c963dce3f1cc6118badf251bf149f0bd07d5cabe99","f5312f542c21273d9485a49394386c4575804770667f2ddb59b3bf0669fddd2f","7f7513b25429a64473e10ce3ad2f3da372bbdd14b65d07bbaf547e7c8bbbe62b","2e61cd0cbf4a8f45809bda9f7f78c0d33ad11842ff94ae340873e2664dc843de"],"threshold":3},"snapshot":{"keyids":["45b283825eb184cabd582eb17b74fc8ed404f68cf452acabdad2ed6f90ce216b"],"threshold":1},"targets":{"keyids":["ff51e17fcf253119b7033f6f57512631da4a0969442afcf9fc8b141c7f2be99c","25a0eb450fd3ee2bd79218c963dce3f1cc6118badf251bf149f0bd07d5cabe99","f5312f542c21273d9485a49394386c4575804770667f2ddb59b3bf0669fddd2f","7f7513b25429a64473e10ce3ad2f3da372bbdd14b65d07bbaf547e7c8bbbe62b","2e61cd0cbf4a8f45809bda9f7f78c0d33ad11842ff94ae340873e2664dc843de"],"threshold":3},"timestamp":{"keyids":["e1863ba02070322ebc626dcecf9d881a3a38c35c3b41a83765b6ad6c37eaec2a"],"threshold":1}},"consistent_snapshot":true},"signatures":[{"keyid":"ff51e17fcf253119b7033f6f57512631da4a0969442afcf9fc8b141c7f2be99c","sig":"3045022100fc1c2be509ce50ea917bbad1d9efe9d96c8c2ebea04af2717aa3d9c6fe617a75022012eef282a19f2d8bd4818aa333ef48a06489f49d4d34a20b8fe8fc867bb25a7a"},{"keyid":"25a0eb450fd3ee2bd79218c963dce3f1cc6118badf251bf149f0bd07d5cabe99","sig":"30450221008a4392ae5057fc00778b651e61fea244766a4ae58db84d9f1d3810720ab0f3b702207c49e59e8031318caf02252ecea1281cecc1e5986c309a9cef61f455ecf7165d"},{"keyid":"7f7513b25429a64473e10ce3ad2f3da372bbdd14b65d07bbaf547e7c8bbbe62b","sig":"3046022100da1b8dc5d53aaffbbfac98de3e23ee2d2ad3446a7bed09fac0f88bae19be2587022100b681c046afc3919097dfe794e0d819be891e2e850aade315bec06b0c4dea221b"},{"keyid":"2e61cd0cbf4a8f45809bda9f7f78c0d33ad11842ff94ae340873e2664dc843de","sig":"3046022100b534e0030e1b271133ecfbdf3ba9fbf3becb3689abea079a2150afbb63cdb7c70221008c39a718fd9495f249b4ab8788d5b9dc269f0868dbe38b272f48207359d3ded9"},{"keyid":"2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97","sig":"3045022100fc1c2be509ce50ea917bbad1d9efe9d96c8c2ebea04af2717aa3d9c6fe617a75022012eef282a19f2d8bd4818aa333ef48a06489f49d4d34a20b8fe8fc867bb25a7a"},{"keyid":"eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b","sig":"30450221008a4392ae5057fc00778b651e61fea244766a4ae58db84d9f1d3810720ab0f3b702207c49e59e8031318caf02252ecea1281cecc1e5986c309a9cef61f455ecf7165d"},{"keyid":"f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209","sig":"3046022100da1b8dc5d53aaffbbfac98de3e23ee2d2ad3446a7bed09fac0f88bae19be2587022100b681c046afc3919097dfe794e0d819be891e2e850aade315bec06b0c4dea221b"},{"keyid":"75e867ab10e121fdef32094af634707f43ddd79c6bab8ad6c5ab9f03f4ea8c90","sig":"3046022100b534e0030e1b271133ecfbdf3ba9fbf3becb3689abea079a2150afbb63cdb7c70221008c39a718fd9495f249b4ab8788d5b9dc269f0868dbe38b272f48207359d3ded9"}]}rekor-1.3.5/pkg/pki/tuf/testdata/timestamp.json000066400000000000000000000013201455727245600215300ustar00rootroot00000000000000{ "signed": { "_type": "timestamp", "spec_version": "1.0", "version": 53, "expires": "2022-11-03T21:10:23Z", "meta": { "snapshot.json": { "length": 1973, "hashes": { "sha256": "d1eea940c4e3fff8a3ae5932a38ca35fdd789e1a85d4919f3316efd879c0e0ed", "sha512": "d0848c412728e5db9ad0713d3d1c319f7cc2bde67082da95f79f740ef26e1fee91a32b1eb948b47bb2c06b71557f60e61605e512e40dd4b25c907ac3f4ca6e91" }, "version": 53 } } }, "signatures": [ { "keyid": "e1863ba02070322ebc626dcecf9d881a3a38c35c3b41a83765b6ad6c37eaec2a", "sig": "304502202bb5ce4034ea4be68a9a6c9af77b48911294f1492d7add5a763a5a77bc863f27022100d4ab20ffa8790e6a28e7f1e66495e1fc5242bed0f5222312e76ec60235270df2" } ] } rekor-1.3.5/pkg/pki/tuf/testdata/unsigned_root.json000066400000000000000000000100331455727245600224050ustar00rootroot00000000000000{ "signatures": [ { "keyid": "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "sig": "30450221008a35d51da0f845301a5eac98ad0df00a934f59b709c1eaf81c86be734d9356f80220742942325599749800f52675f6efe124345980a2a636c0dc76f9caf9fc3123b0" } ], "signed": { "_type": "root", "consistent_snapshot": false, "expires": "2021-12-18T13:28:12.99008-06:00", "keys": { "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa-sha2-nistp256", "keyval": { "public": "04cbc5cab2684160323c25cd06c3307178a6b1d1c9b949328453ae473c5ba7527e35b13f298b41633382241f3fd8526c262d43b45adee5c618fa0642c82b8a9803" }, "scheme": "ecdsa-sha2-nistp256" }, "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa-sha2-nistp256", "keyval": { "public": "04a71aacd835dc170ba6db3fa33a1a33dee751d4f8b0217b805b9bd3242921ee93672fdcfd840576c5bb0dc0ed815edf394c1ee48c2b5e02485e59bfc512f3adc7" }, "scheme": "ecdsa-sha2-nistp256" }, "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa-sha2-nistp256", "keyval": { "public": "04117b33dd265715bf23315e368faa499728db8d1f0a377070a1c7b1aba2cc21be6ab1628e42f2cdd7a35479f2dce07b303a8ba646c55569a8d2a504ba7e86e447" }, "scheme": "ecdsa-sha2-nistp256" }, "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa-sha2-nistp256", "keyval": { "public": "04cc1cd53a61c23e88cc54b488dfae168a257c34fac3e88811c55962b24cffbfecb724447999c54670e365883716302e49da57c79a33cd3e16f81fbc66f0bcdf48" }, "scheme": "ecdsa-sha2-nistp256" }, "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa-sha2-nistp256", "keyval": { "public": "048a78a44ac01099890d787e5e62afc29c8ccb69a70ec6549a6b04033b0a8acbfb42ab1ab9c713d225cdb52b858886cf46c8e90a7f3b9e6371882f370c259e1c5b" }, "scheme": "ecdsa-sha2-nistp256" } }, "roles": { "root": { "keyids": [ "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" ], "threshold": 3 }, "snapshot": { "keyids": [ "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" ], "threshold": 3 }, "targets": { "keyids": [ "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" ], "threshold": 3 }, "timestamp": { "keyids": [ "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" ], "threshold": 3 } }, "spec_version": "1.0", "version": 1 } } rekor-1.3.5/pkg/pki/tuf/tuf.go000066400000000000000000000136441455727245600161620ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tuf import ( "crypto/ed25519" "crypto/sha256" "crypto/x509" "encoding/hex" "encoding/json" "fmt" "io" "time" "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer" "github.com/sigstore/rekor/pkg/pki/identity" "github.com/sigstore/sigstore/pkg/cryptoutils" sigsig "github.com/sigstore/sigstore/pkg/signature" "github.com/theupdateframework/go-tuf/data" "github.com/theupdateframework/go-tuf/pkg/keys" "github.com/theupdateframework/go-tuf/verify" ) type Signature struct { signed *data.Signed Role string Version int } type signedMeta struct { Type string `json:"_type"` Expires time.Time `json:"expires"` Version int `json:"version"` SpecVersion string `json:"spec_version"` } // NewSignature creates and validates a TUF signed manifest func NewSignature(r io.Reader) (*Signature, error) { b, err := io.ReadAll(r) if err != nil { return nil, err } s := &data.Signed{} if err := json.Unmarshal(b, s); err != nil { return nil, err } // extract role sm := &signedMeta{} if err := json.Unmarshal(s.Signed, sm); err != nil { return nil, err } return &Signature{ signed: s, Role: sm.Type, Version: sm.Version, }, nil } // CanonicalValue implements the pki.Signature interface func (s Signature) CanonicalValue() ([]byte, error) { if s.signed == nil { return nil, fmt.Errorf("tuf manifest has not been initialized") } marshalledBytes, err := json.Marshal(s.signed) if err != nil { return nil, fmt.Errorf("marshalling signature: %w", err) } return jsoncanonicalizer.Transform(marshalledBytes) } // Verify implements the pki.Signature interface func (s Signature) Verify(_ io.Reader, k interface{}, _ ...sigsig.VerifyOption) error { key, ok := k.(*PublicKey) if !ok { return fmt.Errorf("invalid public key type for: %v", k) } if key.db == nil { return fmt.Errorf("tuf root has not been initialized") } return key.db.Verify(s.signed, s.Role, 0) } // PublicKey Public Key database with verification keys type PublicKey struct { // we keep the signed root to retrieve the canonical value root *data.Signed db *verify.DB } // NewPublicKey implements the pki.PublicKey interface func NewPublicKey(r io.Reader) (*PublicKey, error) { rawRoot, err := io.ReadAll(r) if err != nil { return nil, err } // Unmarshal this to verify that this is a valid root.json s := &data.Signed{} if err := json.Unmarshal(rawRoot, s); err != nil { return nil, err } root := &data.Root{} if err := json.Unmarshal(s.Signed, root); err != nil { return nil, err } // Now create a verification db that trusts all the keys db := verify.NewDB() for id, k := range root.Keys { if err := db.AddKey(id, k); err != nil { return nil, err } } for name, role := range root.Roles { if err := db.AddRole(name, role); err != nil { return nil, err } } // Verify that this root.json was signed. if err := db.Verify(s, "root", 0); err != nil { return nil, err } return &PublicKey{root: s, db: db}, nil } // CanonicalValue implements the pki.PublicKey interface func (k PublicKey) CanonicalValue() (encoded []byte, err error) { if k.root == nil { return nil, fmt.Errorf("tuf root has not been initialized") } marshalledBytes, err := json.Marshal(k.root) if err != nil { return nil, fmt.Errorf("marshalling tuf root: %w", err) } return jsoncanonicalizer.Transform(marshalledBytes) } func (k PublicKey) SpecVersion() (string, error) { // extract role sm := &signedMeta{} if err := json.Unmarshal(k.root.Signed, sm); err != nil { return "", err } return sm.SpecVersion, nil } // EmailAddresses implements the pki.PublicKey interface func (k PublicKey) EmailAddresses() []string { return nil } // Subjects implements the pki.PublicKey interface func (k PublicKey) Subjects() []string { return nil } // Identities implements the pki.PublicKey interface func (k PublicKey) Identities() ([]identity.Identity, error) { root := &data.Root{} if err := json.Unmarshal(k.root.Signed, root); err != nil { return nil, err } var ids []identity.Identity for _, k := range root.Keys { verifier, err := keys.GetVerifier(k) if err != nil { return nil, err } switch k.Type { // RSA and ECDSA keys are PKIX-encoded without PEM header for the Verifier type case data.KeyTypeRSASSA_PSS_SHA256: fallthrough // TODO: Update to constants once go-tuf is updated to 0.6.0 (need PR #508) case "ecdsa-sha2-nistp256": fallthrough case "ecdsa": // parse and marshal to check format is correct pub, err := x509.ParsePKIXPublicKey([]byte(verifier.Public())) if err != nil { return nil, err } pkixKey, err := cryptoutils.MarshalPublicKeyToDER(pub) if err != nil { return nil, err } digest := sha256.Sum256(pkixKey) ids = append(ids, identity.Identity{ Crypto: pub, Raw: pkixKey, Fingerprint: hex.EncodeToString(digest[:]), }) case data.KeyTypeEd25519: // key is stored as a 32-byte string pub := ed25519.PublicKey(verifier.Public()) pkixKey, err := cryptoutils.MarshalPublicKeyToDER(pub) if err != nil { return nil, err } digest := sha256.Sum256(pkixKey) ids = append(ids, identity.Identity{ Crypto: pub, Raw: pkixKey, Fingerprint: hex.EncodeToString(digest[:]), }) default: return nil, fmt.Errorf("unsupported key type: %v", k.Type) } } return ids, nil } rekor-1.3.5/pkg/pki/tuf/tuf_e2e_test.go000066400000000000000000000036211455727245600177460ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e // +build e2e package tuf import ( "io/ioutil" "testing" "github.com/theupdateframework/go-tuf" ) func generateTestRepo(t *testing.T, files map[string][]byte) tuf.LocalStore { store := tuf.MemoryStore(nil, files) repo, err := tuf.NewRepo(store) if err := repo.Init(false); err != nil { t.Fatalf("unexpected error") } if err != nil { t.Fatalf("unexpected error") } for _, role := range []string{"root", "snapshot", "targets", "timestamp"} { _, err := repo.GenKey(role) if err != nil { t.Fatalf("unexpected error") } } for file := range files { repo.AddTarget(file, nil) } repo.Snapshot() repo.Timestamp() repo.Commit() return store } // createTufSignedArtifact gets the test dir setup correctly with some random artifacts and keys. func createTufSignedArtifact(t *testing.T, artifactPath, rootPath string) { t.Helper() store := generateTestRepo(t, map[string][]byte{ "foo.txt": []byte("foo")}) meta, err := store.GetMeta() if err != nil { t.Fatal(err) } rootJSON, ok := meta["root.json"] if !ok { t.Fatal(err) } timestampJSON, ok := meta["timestamp.json"] if !ok { t.Fatal(err) } if err := ioutil.WriteFile(artifactPath, timestampJSON, 0644); err != nil { t.Fatal(err) } if err := ioutil.WriteFile(rootPath, rootJSON, 0644); err != nil { t.Fatal(err) } } rekor-1.3.5/pkg/pki/tuf/tuf_test.go000066400000000000000000000172611455727245600172200ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tuf import ( "bytes" "crypto/ecdsa" "crypto/x509" "io" "os" "reflect" "testing" "time" "github.com/sigstore/sigstore/pkg/cryptoutils" _ "github.com/theupdateframework/go-tuf/pkg/deprecated/set_ecdsa" "github.com/theupdateframework/go-tuf/verify" ) func patchIsExpired() func() { // Patch out the IsExpired to make the tests stable :) old := verify.IsExpired verify.IsExpired = func(t time.Time) bool { return false } return func() { verify.IsExpired = old } } func TestReadPublicKey(t *testing.T) { // Tests reading a valid public key (root.json) type test struct { caseDesc string inputFile string errorFound bool specVersion string } tests := []test{ {caseDesc: "Unsigned root manifest", inputFile: "testdata/unsigned_root.json", errorFound: true}, {caseDesc: "Invalid TUF root.json (invalid type)", inputFile: "testdata/timestamp.json", errorFound: true, specVersion: "1.0"}, {caseDesc: "Valid TUF root.json", inputFile: "testdata/1.root.json", errorFound: false, specVersion: "1.0"}, } // Patch out the expired function to make tests stable :) defer patchIsExpired()() for _, tc := range tests { file, err := os.Open(tc.inputFile) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.inputFile) } got, err := NewPublicKey(file) if ((got != nil) == tc.errorFound) || ((err != nil) != tc.errorFound) { t.Errorf("%v: unexpected result testing %v: %v", tc.caseDesc, tc.inputFile, err) } if !tc.errorFound { specVersion, err := got.SpecVersion() if err != nil { t.Errorf("%v: unexpected result testing %v: %v", tc.caseDesc, tc.inputFile, err) } if specVersion != tc.specVersion { t.Errorf("%v: unexpected spec version expected %v, got %v", tc.caseDesc, tc.specVersion, specVersion) } identities, err := got.Identities() if err != nil { t.Errorf("%v: error getting identities for %v: %v", tc.caseDesc, tc.inputFile, err) } if len(identities) != 7 { t.Errorf("%v: expected 7 identities, got: %d", tc.caseDesc, len(identities)) } for _, i := range identities { if _, ok := i.Crypto.(*ecdsa.PublicKey); !ok { t.Errorf("%v: key was not of type *ecdsa.PublicKey: %v", tc.caseDesc, reflect.TypeOf(i.Crypto)) } key, err := x509.ParsePKIXPublicKey(i.Raw) if err != nil { t.Fatalf("%v: Raw is not in PKIX format: %v", tc.caseDesc, err) } if err := cryptoutils.EqualKeys(key, i.Crypto); err != nil { t.Errorf("%v: raw key and crypto key not equal: %v", tc.caseDesc, err) } if len(i.Fingerprint) != 64 { t.Errorf("%v: fingerprint is not expected length of 64 (hex 32-byte sha256): %d", tc.caseDesc, len(i.Fingerprint)) } } } } } func TestReadSignature(t *testing.T) { // Tests reading a valid signature (manifest) type test struct { caseDesc string inputFile string errorFound bool } tests := []test{ {caseDesc: "Not a valid TUF manifest", inputFile: "testdata/bogus.json", errorFound: true}, {caseDesc: "Valid root.json manifest", inputFile: "testdata/timestamp.json", errorFound: false}, {caseDesc: "Valid timestamp.json manifest", inputFile: "testdata/1.root.json", errorFound: false}, {caseDesc: "Valid unsigned root.json manifest", inputFile: "testdata/unsigned_root.json", errorFound: false}, } for _, tc := range tests { file, err := os.Open(tc.inputFile) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.inputFile) } if got, err := NewSignature(file); ((got != nil) == tc.errorFound) || ((err != nil) != tc.errorFound) { t.Errorf("%v: unexpected result testing %v: %v", tc.caseDesc, tc.inputFile, got) } } } func TestCanonicalValue(t *testing.T) { // Tests equivalence even with different JSON encodings type test struct { caseDesc string input string output string match bool } var k PublicKey if _, err := k.CanonicalValue(); err == nil { t.Errorf("CanonicalValue did not error out for uninitialized key") } // Patch out the expired function to make tests stable :) defer patchIsExpired()() tests := []test{ {caseDesc: "root", input: "testdata/1.root.json", output: "testdata/reformat.1.root.json", match: true}, } for _, tc := range tests { var inputFile, outputFile io.Reader var err error inputFile, err = os.Open(tc.input) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.input) } inputKey, err := NewPublicKey(inputFile) if err != nil { t.Errorf("%v: Error reading input for TestCanonicalValue: %v", tc.caseDesc, err) } cvInput, err := inputKey.CanonicalValue() if err != nil { t.Errorf("%v: Error canonicalizing public key '%v': %v", tc.caseDesc, tc.input, err) } outputFile, err = os.Open(tc.output) if err != nil { t.Errorf("%v: cannot open %v", tc.caseDesc, tc.output) } outputKey, err := NewPublicKey(outputFile) if err != nil { t.Fatalf("%v: Error reading input for TestCanonicalValue: %v", tc.caseDesc, err) } cvOutput, err := outputKey.CanonicalValue() if err != nil { t.Fatalf("%v: Error canonicalizing public key '%v': %v", tc.caseDesc, tc.input, err) } if bytes.Equal(cvInput, cvOutput) != tc.match { t.Errorf("%v: %v equality of canonical values of %v and %v was expected but not generated", tc.caseDesc, tc.match, tc.input, tc.output) } } } func TestVerifySignature(t *testing.T) { type test struct { caseDesc string sigFile string keyFile string verified bool } tests := []test{ {caseDesc: "Valid root.json, valid signed timestamp.json", keyFile: "testdata/1.root.json", sigFile: "testdata/timestamp.json", verified: true}, {caseDesc: "Valid root.json, valid signed root.json", keyFile: "testdata/1.root.json", sigFile: "testdata/1.root.json", verified: true}, {caseDesc: "Valid root.json, mismatched timestamp.json", keyFile: "testdata/other_root.json", sigFile: "testdata/timestamp.json", verified: false}, {caseDesc: "Valid root.json, unsigned root.json", keyFile: "testdata/1.root.json", sigFile: "testdata/unsigned_root.json", verified: false}, } defer patchIsExpired()() for _, tc := range tests { keyFile, err := os.Open(tc.keyFile) if err != nil { t.Errorf("%v: error reading keyfile '%v': %v", tc.caseDesc, tc.keyFile, err) } k, err := NewPublicKey(keyFile) if err != nil { t.Errorf("%v: error reading keyfile '%v': %v", tc.caseDesc, tc.keyFile, err) } sigFile, err := os.Open(tc.sigFile) if err != nil { t.Errorf("%v: error reading sigfile '%v': %v", tc.caseDesc, tc.sigFile, err) } s, err := NewSignature(sigFile) if err != nil { t.Errorf("%v: error reading sigfile '%v': %v", tc.caseDesc, tc.sigFile, err) } if err := s.Verify(nil, k); (err == nil) != tc.verified { t.Errorf("%v: unexpected result in verifying sigature: %v", tc.caseDesc, err) } } emptyKey := PublicKey{} emptySig := Signature{} if err := emptySig.Verify(nil, emptyKey); err == nil { t.Errorf("expected error when using empty sig to verify") } sigFile, _ := os.Open("testdata/timestamp.json") validSig, _ := NewSignature(sigFile) if err := validSig.Verify(bytes.NewReader([]byte("irrelevant")), &emptyKey); err == nil { t.Errorf("expected error when using empty key to verify") } } rekor-1.3.5/pkg/pki/x509/000077500000000000000000000000001455727245600147345ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/x509/e2e.go000066400000000000000000000154531455727245600157460ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e // +build e2e package x509 import ( "bytes" "context" "crypto" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/pem" "errors" "io/ioutil" "testing" "github.com/sigstore/rekor/pkg/util" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" ) // Generated with: // openssl ecparam -genkey -name prime256v1 > ec_private.pem // openssl pkcs8 -topk8 -in ec_private.pem -nocrypt const ECDSAPriv = `-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmrLtCpBdXgXLUr7o nSUPfo3oXMjmvuwTOjpTulIBKlKhRANCAATH6KSpTFe6uXFmW1qNEFXaO7fWPfZt pPZrHZ1cFykidZoURKoYXfkohJ+U/USYy8Sd8b4DMd5xDRZCnlDM0h37 -----END PRIVATE KEY-----` // Extracted from above with: // openssl ec -in ec_private.pem -pubout const ECDSAPub = `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEx+ikqUxXurlxZltajRBV2ju31j32 baT2ax2dXBcpInWaFESqGF35KISflP1EmMvEnfG+AzHecQ0WQp5QzNId+w== -----END PUBLIC KEY-----` // Generated with: // openssl req -newkey rsa:2048 -nodes -keyout test.key -x509 -out test.crt const RSACert = `-----BEGIN CERTIFICATE----- MIIDOjCCAiKgAwIBAgIUEP925shVBKERFCsymdSqESLZFyMwDQYJKoZIhvcNAQEL BQAwHzEdMBsGCSqGSIb3DQEJARYOdGVzdEByZWtvci5kZXYwHhcNMjEwNDIxMjAy ODAzWhcNMjEwNTIxMjAyODAzWjAfMR0wGwYJKoZIhvcNAQkBFg50ZXN0QHJla29y LmRldjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN8KiP08rFIik4GN W8/sHSXxDopeDBLEQEihsyXXWesfYW/q59lFaCZrsTetlyNEzKDJ+JrpIHwoGOo4 EwefFfvy2nkgPFs9aeIDsYZNZnIGxeB8sUfsZUYGHx+Ikm18vhM//GYzNjjuvHyq +CWRAOS12ZISa99iah/lIhcP8IEj1gPGldAH0QFx3XpCePAdQocSU6ziVkj054/x NJXy1bKySrVw7gvE9LxZlVO9urSOnzg7BBOla0mob8NRDVB8yN+LG365q4IMDzuI jAEL6sLtoJ9pcemo1rIfNOhSLYlzfg7oszJ8eCjASNCCcp6EKVjhW7LRoldC8oGZ EOrKM78CAwEAAaNuMGwwHQYDVR0OBBYEFGjs8EHKT3x1itwwptJLuQQg/hQcMB8G A1UdIwQYMBaAFGjs8EHKT3x1itwwptJLuQQg/hQcMA8GA1UdEwEB/wQFMAMBAf8w GQYDVR0RBBIwEIEOdGVzdEByZWtvci5kZXYwDQYJKoZIhvcNAQELBQADggEBAAHE bYuePN3XpM7pHoCz6g4uTHu0VrezqJyK1ohysgWJmSJzzazUeISXk0xWnHPk1Zxi kzoEuysI8b0P7yodMA8e16zbIOL6QbGe3lNXYqRIg+bl+4OPFGVMX8xHNZmeh0kD vX1JVS+y9uyo4/z/pm0JhaSCn85ft/Y5uXMQYn1wFR5DAcJH+iWjNX4fipGxGRE9 Cy0DjFnYJ3SRY4HPQ0oUSQmyhrwe2DiYzeqtbL2KJBXPcFQKWhkf/fupdYFljvcH d9NNfRb0p2oFGG/J0ROg9pEcP1/aZP5k8P2pRdt3y7h1MAtmg2bgEdugZgXwAUmM BmU8k2FeTuqV15piPCE= -----END CERTIFICATE-----` const RSAKey = `-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDfCoj9PKxSIpOB jVvP7B0l8Q6KXgwSxEBIobMl11nrH2Fv6ufZRWgma7E3rZcjRMygyfia6SB8KBjq OBMHnxX78tp5IDxbPWniA7GGTWZyBsXgfLFH7GVGBh8fiJJtfL4TP/xmMzY47rx8 qvglkQDktdmSEmvfYmof5SIXD/CBI9YDxpXQB9EBcd16QnjwHUKHElOs4lZI9OeP 8TSV8tWyskq1cO4LxPS8WZVTvbq0jp84OwQTpWtJqG/DUQ1QfMjfixt+uauCDA87 iIwBC+rC7aCfaXHpqNayHzToUi2Jc34O6LMyfHgowEjQgnKehClY4Vuy0aJXQvKB mRDqyjO/AgMBAAECggEBAIHOAs3Gis8+WjRSjXVjh882DG1QsJwXZQYgPT+vpiAl YjKdNpOHRkbd9ARgXY5kEuccxDd7p7E6MM3XFpQf7M51ltpZfWboRgAIgD+WOiHw eSbdytr95C6tj11twTJBH+naGk1sTokxv7aaVdKfIjL49oeBexBFmVe4pW9gkmrE 1z1y1a0RohqbZ0kprYPWjz5UhsNqbCzgkdDqS7IrcOwVg6zvKYFjHnqIHqaJXVif FgIfoNt7tz+12FTHI+6OkKoN3YCJueaxneBhITXm6RLOpQWa9qhdUPbkJ9vQNfph Qqke4faaxKY9UDma+GpEHR016AWufZp92pd9wQkDn0kCgYEA7w/ZizAkefHoZhZ8 Isn/fYu4fdtUaVgrnGUVZobiGxWrHRU9ikbAwR7UwbgRSfppGiJdAMq1lyH2irmb 4OHU64rjuYSlIqUWHLQHWmqUbLUvlDojH/vdmH/Zn0AbrLZaimC5UCjK3Eb7sAMq G0tGeDX2JraQvx7KrbC6peTaaaMCgYEA7tgZBiRCQJ7+mNu+gX9x6OXtjsDCh516 vToRLkxWc7LAbC9LKsuEHl4e3vy1PY/nyuv12Ng2dBq4WDXozAmVgz0ok7rRlIFp w8Yj8o/9KuGZkD/7tw/pLsVc9Q3Wf0ACrnAAh7+3dAvn3yg+WHwXzqWIbrseDPt9 ILCfUoNDpzUCgYAKFCX8y0PObFd67lm/cbq2xUw66iNN6ay1BEH5t5gSwkAbksis ar03pyAbJrJ75vXFZ0t6fBFZ1NG7GYYr3fmHEKz3JlN7+W/MN/7TXgjx6FWgLy9J 6ul1w3YeU6qXBn0ctmU5ru6WiNuVmRyOWAcZjFTbXvkNRbQPzJKh6dsXdwKBgA1D FIihxMf/zBVCxl48bF/JPJqbm3GaTfFp4wBWHsrH1yVqrtrOeCSTh1VMZOfpMK60 0W7b+pIR1cCYJbgGpDWoVLN3QSHk2bGUM/TJB/60jilTVC/DA2ikbtfwj8N7E2sK Lw1amN4ptxNOEcAqC8xepqe3XiDMahNBm2cigMQtAoGBAKwrXvss2BKz+/6poJQU A0c7jhMN8M9Y5S2Ockw07lrQeAgfu4q+/8ztm0NeHJbk01IJvJY5Nt7bSgwgNVlo j7vR2BMAc9U73Ju9aeTl/L6GqmZyA+Ojhl5gA5DPZYqNiqi93ydgRaI6n4+o3dI7 5wnr40AmbuKCDvMOvN7nMybL -----END PRIVATE KEY-----` // Extracted from the certificate using: // openssl x509 -pubkey -noout -in test.crt const PubKey = `-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3wqI/TysUiKTgY1bz+wd JfEOil4MEsRASKGzJddZ6x9hb+rn2UVoJmuxN62XI0TMoMn4mukgfCgY6jgTB58V +/LaeSA8Wz1p4gOxhk1mcgbF4HyxR+xlRgYfH4iSbXy+Ez/8ZjM2OO68fKr4JZEA 5LXZkhJr32JqH+UiFw/wgSPWA8aV0AfRAXHdekJ48B1ChxJTrOJWSPTnj/E0lfLV srJKtXDuC8T0vFmVU726tI6fODsEE6VrSahvw1ENUHzI34sbfrmrggwPO4iMAQvq wu2gn2lx6ajWsh806FItiXN+DuizMnx4KMBI0IJynoQpWOFbstGiV0LygZkQ6soz vwIDAQAB -----END PUBLIC KEY-----` var ( CertPrivateKey *rsa.PrivateKey Certificate *x509.Certificate ) func init() { p, _ := pem.Decode([]byte(RSAKey)) priv, err := x509.ParsePKCS8PrivateKey(p.Bytes) if err != nil { panic(err) } cpk, ok := priv.(*rsa.PrivateKey) if !ok { panic("unsuccessful conversion") } CertPrivateKey = cpk p, _ = pem.Decode([]byte(RSACert)) Certificate, err = x509.ParseCertificate(p.Bytes) if err != nil { panic(err) } } func SignX509Cert(b []byte) ([]byte, error) { dgst := sha256.Sum256(b) signature, err := CertPrivateKey.Sign(rand.Reader, dgst[:], crypto.SHA256) return signature, err } // CreatedX509SignedArtifact gets the test dir setup correctly with some random artifacts and keys. func CreatedX509SignedArtifact(t *testing.T, artifactPath, sigPath string) { t.Helper() artifact := util.CreateArtifact(t, artifactPath) // Sign it with our key and write that to a file signature, err := SignX509Cert([]byte(artifact)) if err != nil { t.Fatal(err) } if err := ioutil.WriteFile(sigPath, []byte(signature), 0644); err != nil { t.Fatal(err) } } type Verifier struct { S signature.Signer v signature.Verifier } func (v *Verifier) KeyID() (string, error) { return "", nil } func (v *Verifier) Public() crypto.PublicKey { return v.v.PublicKey } func (v *Verifier) Sign(_ context.Context, data []byte) (sig []byte, err error) { if v.S == nil { return nil, errors.New("nil signer") } sig, err = v.S.SignMessage(bytes.NewReader(data), options.WithCryptoSignerOpts(crypto.SHA256)) if err != nil { return nil, err } return sig, nil } func (v *Verifier) Verify(_ context.Context, data, sig []byte) error { if v.v == nil { return errors.New("nil Verifier") } return v.v.VerifySignature(bytes.NewReader(sig), bytes.NewReader(data)) } rekor-1.3.5/pkg/pki/x509/e2e_test.go000066400000000000000000000053431455727245600170020ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e // +build e2e package x509 import ( "os" "path/filepath" "testing" "github.com/sigstore/rekor/pkg/util" ) func TestX509(t *testing.T) { td := t.TempDir() artifactPath := filepath.Join(td, "artifact") sigPath := filepath.Join(td, "signature") certPath := filepath.Join(td, "cert.pem") pubKeyPath := filepath.Join(td, "key.pem") CreatedX509SignedArtifact(t, artifactPath, sigPath) if err := os.WriteFile(certPath, []byte(RSACert), 0o644); err != nil { t.Fatal(err) } if err := os.WriteFile(pubKeyPath, []byte(PubKey), 0o644); err != nil { t.Fatal(err) } // If we do it twice, it should already exist out := util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", certPath, "--pki-format", "x509") util.OutputContains(t, out, "Created entry at") out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", certPath, "--pki-format", "x509") util.OutputContains(t, out, "Entry already exists") // Now upload with the public key rather than the cert. They should NOT be deduped. out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubKeyPath, "--pki-format", "x509") util.OutputContains(t, out, "Created entry at") // Now let's go the other order to be sure. New artifact, key first then cert. CreatedX509SignedArtifact(t, artifactPath, sigPath) out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubKeyPath, "--pki-format", "x509") util.OutputContains(t, out, "Created entry at") out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubKeyPath, "--pki-format", "x509") util.OutputContains(t, out, "Entry already exists") // This should NOT already exist out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", certPath, "--pki-format", "x509") util.OutputContains(t, out, "Created entry at") uuid := util.GetUUIDFromUploadOutput(t, out) // Search via email out = util.RunCli(t, "search", "--email", "test@rekor.dev") util.OutputContains(t, out, uuid) } rekor-1.3.5/pkg/pki/x509/testdata/000077500000000000000000000000001455727245600165455ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/x509/testdata/ec.pub000066400000000000000000000002621455727245600176440ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIxaAc+TaxYoQU0X9NNUJgWffbn6h juoEDPQQn80nX/Eus9I/t00ccNpcSrUw1+IPyGp1p9fgmL0DlBf05BNFNQ== -----END PUBLIC KEY----- rekor-1.3.5/pkg/pki/x509/testdata/hello_world.txt000066400000000000000000000000161455727245600216150ustar00rootroot00000000000000Hello, World! rekor-1.3.5/pkg/pki/x509/testdata/hello_world.txt.sig000066400000000000000000000001101455727245600223710ustar00rootroot000000000000000F!Z}h]<+H-H! Xo*#/ o)M!Կ$rekor-1.3.5/pkg/pki/x509/testutils/000077500000000000000000000000001455727245600167745ustar00rootroot00000000000000rekor-1.3.5/pkg/pki/x509/testutils/cert_test_utils.go000066400000000000000000000145521455727245600225460ustar00rootroot00000000000000// Copyright 2022 The Sigstore Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package testutils import ( "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "math/big" "net" "net/url" "time" ) /* To use: rootCert, rootKey, _ := GenerateRootCa() subCert, subKey, _ := GenerateSubordinateCa(rootCert, rootKey) leafCert, _, _ := GenerateLeafCert("subject", "oidc-issuer", subCert, subKey) roots := x509.NewCertPool() subs := x509.NewCertPool() roots.AddCert(rootCert) subs.AddCert(subCert) opts := x509.VerifyOptions{ Roots: roots, Intermediates: subs, KeyUsages: []x509.ExtKeyUsage{ x509.ExtKeyUsageCodeSigning, }, } _, err := leafCert.Verify(opts) */ func createCertificate(template *x509.Certificate, parent *x509.Certificate, pub interface{}, priv crypto.Signer) (*x509.Certificate, error) { certBytes, err := x509.CreateCertificate(rand.Reader, template, parent, pub, priv) if err != nil { return nil, err } cert, err := x509.ParseCertificate(certBytes) if err != nil { return nil, err } return cert, nil } func GenerateRootCa() (*x509.Certificate, *ecdsa.PrivateKey, error) { rootTemplate := &x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ CommonName: "sigstore", Organization: []string{"sigstore.dev"}, }, NotBefore: time.Now().Add(-10 * time.Minute), NotAfter: time.Now().Add(5 * time.Hour), KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, BasicConstraintsValid: true, IsCA: true, } priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, err } cert, err := createCertificate(rootTemplate, rootTemplate, &priv.PublicKey, priv) if err != nil { return nil, nil, err } return cert, priv, nil } func GenerateSubordinateCa(rootTemplate *x509.Certificate, rootPriv crypto.Signer) (*x509.Certificate, *ecdsa.PrivateKey, error) { subTemplate := &x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ CommonName: "sigstore-sub", Organization: []string{"sigstore.dev"}, }, NotBefore: time.Now().Add(-9 * time.Minute), NotAfter: time.Now().Add(2 * time.Hour), KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}, BasicConstraintsValid: true, IsCA: true, } priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, err } cert, err := createCertificate(subTemplate, rootTemplate, &priv.PublicKey, rootPriv) if err != nil { return nil, nil, err } return cert, priv, nil } func GenerateLeafCert(subject, oidcIssuer string, uri *url.URL, parentTemplate *x509.Certificate, parentPriv crypto.Signer, exts ...pkix.Extension) (*x509.Certificate, *ecdsa.PrivateKey, error) { exts = append(exts, pkix.Extension{ // OID for OIDC Issuer extension Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 1}, Critical: false, Value: []byte(oidcIssuer), }) certTemplate := &x509.Certificate{ SerialNumber: big.NewInt(1), EmailAddresses: []string{subject}, NotBefore: time.Now().Add(-1 * time.Minute), NotAfter: time.Now().Add(time.Hour), KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}, IsCA: false, ExtraExtensions: exts, } if uri != nil { certTemplate.URIs = []*url.URL{uri} } priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, err } cert, err := createCertificate(certTemplate, parentTemplate, &priv.PublicKey, parentPriv) if err != nil { return nil, nil, err } return cert, priv, nil } func GenerateExpiredLeafCert(subject string, oidcIssuer string, parentTemplate *x509.Certificate, parentPriv crypto.Signer) (*x509.Certificate, *ecdsa.PrivateKey, error) { certTemplate := &x509.Certificate{ SerialNumber: big.NewInt(1), EmailAddresses: []string{subject}, NotBefore: time.Now().Add(-5 * time.Minute), NotAfter: time.Now().Add(-2 * time.Minute), KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}, IsCA: false, ExtraExtensions: []pkix.Extension{{ // OID for OIDC Issuer extension Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 1}, Critical: false, Value: []byte(oidcIssuer), }}, } priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, err } cert, err := createCertificate(certTemplate, parentTemplate, &priv.PublicKey, parentPriv) if err != nil { return nil, nil, err } return cert, priv, nil } func GenerateLeafCertWithSubjectAlternateNames(dnsNames []string, emailAddresses []string, ipAddresses []net.IP, uris []*url.URL, oidcIssuer string, parentTemplate *x509.Certificate, parentPriv crypto.Signer) (*x509.Certificate, *ecdsa.PrivateKey, error) { certTemplate := &x509.Certificate{ SerialNumber: big.NewInt(1), EmailAddresses: emailAddresses, DNSNames: dnsNames, IPAddresses: ipAddresses, URIs: uris, NotBefore: time.Now().Add(-1 * time.Minute), NotAfter: time.Now().Add(time.Hour), KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}, IsCA: false, ExtraExtensions: []pkix.Extension{{ // OID for OIDC Issuer extension Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 1}, Critical: false, Value: []byte(oidcIssuer), }}, } priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, err } cert, err := createCertificate(certTemplate, parentTemplate, &priv.PublicKey, parentPriv) if err != nil { return nil, nil, err } return cert, priv, nil } rekor-1.3.5/pkg/pki/x509/x509.go000066400000000000000000000150671455727245600160010ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package x509 import ( "bytes" "crypto" "crypto/sha256" "crypto/x509" "encoding/asn1" "encoding/hex" "encoding/pem" "errors" "fmt" "io" "strings" "github.com/asaskevich/govalidator" "github.com/sigstore/rekor/pkg/pki/identity" "github.com/sigstore/sigstore/pkg/cryptoutils" sigsig "github.com/sigstore/sigstore/pkg/signature" ) // EmailAddressOID defined by https://oidref.com/1.2.840.113549.1.9.1 var EmailAddressOID asn1.ObjectIdentifier = []int{1, 2, 840, 113549, 1, 9, 1} type Signature struct { signature []byte } // NewSignature creates and validates an x509 signature object func NewSignature(r io.Reader) (*Signature, error) { b, err := io.ReadAll(r) if err != nil { return nil, err } return &Signature{ signature: b, }, nil } // CanonicalValue implements the pki.Signature interface func (s Signature) CanonicalValue() ([]byte, error) { return s.signature, nil } // Verify implements the pki.Signature interface func (s Signature) Verify(r io.Reader, k interface{}, opts ...sigsig.VerifyOption) error { if len(s.signature) == 0 { //lint:ignore ST1005 X509 is proper use of term return fmt.Errorf("X509 signature has not been initialized") } key, ok := k.(*PublicKey) if !ok { return fmt.Errorf("invalid public key type for: %v", k) } p := key.key if p == nil { switch { case key.cert != nil: p = key.cert.c.PublicKey case len(key.certs) > 0: if err := verifyCertChain(key.certs); err != nil { return err } p = key.certs[0].PublicKey default: return errors.New("no public key found") } } verifier, err := sigsig.LoadVerifier(p, crypto.SHA256) if err != nil { return err } return verifier.VerifySignature(bytes.NewReader(s.signature), r, opts...) } // PublicKey Public Key that follows the x509 standard type PublicKey struct { key interface{} cert *cert certs []*x509.Certificate } type cert struct { c *x509.Certificate b []byte } // NewPublicKey implements the pki.PublicKey interface func NewPublicKey(r io.Reader) (*PublicKey, error) { rawPub, err := io.ReadAll(r) if err != nil { return nil, err } trimmedRawPub := bytes.TrimSpace(rawPub) block, rest := pem.Decode(trimmedRawPub) if block == nil { return nil, errors.New("invalid public key: failure decoding PEM") } // Handle certificate chain, concatenated PEM-encoded certificates if len(rest) > 0 { // Support up to 10 certificates in a chain, to avoid parsing extremely long chains certs, err := cryptoutils.UnmarshalCertificatesFromPEMLimited(trimmedRawPub, 10) if err != nil { return nil, err } return &PublicKey{certs: certs}, nil } switch block.Type { case string(cryptoutils.PublicKeyPEMType): key, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, err } return &PublicKey{key: key}, nil case string(cryptoutils.CertificatePEMType): c, err := x509.ParseCertificate(block.Bytes) if err != nil { return nil, err } return &PublicKey{ cert: &cert{ c: c, b: block.Bytes, }}, nil } return nil, fmt.Errorf("invalid public key: cannot handle type %v", block.Type) } // CanonicalValue implements the pki.PublicKey interface func (k PublicKey) CanonicalValue() (encoded []byte, err error) { switch { case k.key != nil: encoded, err = cryptoutils.MarshalPublicKeyToPEM(k.key) case k.cert != nil: encoded, err = cryptoutils.MarshalCertificateToPEM(k.cert.c) case k.certs != nil: encoded, err = cryptoutils.MarshalCertificatesToPEM(k.certs) default: err = fmt.Errorf("x509 public key has not been initialized") } return } func (k PublicKey) CryptoPubKey() crypto.PublicKey { if k.cert != nil { return k.cert.c.PublicKey } if len(k.certs) > 0 { return k.certs[0].PublicKey } return k.key } // EmailAddresses implements the pki.PublicKey interface func (k PublicKey) EmailAddresses() []string { var names []string var cert *x509.Certificate if k.cert != nil { cert = k.cert.c } else if len(k.certs) > 0 { cert = k.certs[0] } if cert != nil { for _, name := range cert.EmailAddresses { if govalidator.IsEmail(name) { names = append(names, strings.ToLower(name)) } } } return names } // Subjects implements the pki.PublicKey interface func (k PublicKey) Subjects() []string { var subjects []string var cert *x509.Certificate if k.cert != nil { cert = k.cert.c } else if len(k.certs) > 0 { cert = k.certs[0] } if cert != nil { subjects = cryptoutils.GetSubjectAlternateNames(cert) } return subjects } // Identities implements the pki.PublicKey interface func (k PublicKey) Identities() ([]identity.Identity, error) { // k contains either a key, a cert, or a list of certs if k.key != nil { pkixKey, err := cryptoutils.MarshalPublicKeyToDER(k.key) if err != nil { return nil, err } digest := sha256.Sum256(pkixKey) return []identity.Identity{{ Crypto: k.key, Raw: pkixKey, Fingerprint: hex.EncodeToString(digest[:]), }}, nil } var cert *x509.Certificate switch { case k.cert != nil: cert = k.cert.c case len(k.certs) > 0: cert = k.certs[0] default: return nil, errors.New("no key, certificate or certificate chain provided") } digest := sha256.Sum256(cert.Raw) return []identity.Identity{{ Crypto: cert, Raw: cert.Raw, Fingerprint: hex.EncodeToString(digest[:]), }}, nil } func verifyCertChain(certChain []*x509.Certificate) error { if len(certChain) == 0 { return errors.New("no certificate chain provided") } // No certificate chain to verify if len(certChain) == 1 { return nil } rootPool := x509.NewCertPool() rootPool.AddCert(certChain[len(certChain)-1]) subPool := x509.NewCertPool() for _, c := range certChain[1 : len(certChain)-1] { subPool.AddCert(c) } if _, err := certChain[0].Verify(x509.VerifyOptions{ Roots: rootPool, Intermediates: subPool, // Allow any key usage KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, // Expired certificates can be uploaded and should be verifiable CurrentTime: certChain[0].NotBefore, }); err != nil { return err } return nil } rekor-1.3.5/pkg/pki/x509/x509_test.go000066400000000000000000000316341455727245600170360ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package x509 import ( "bytes" "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/hex" "net" "net/url" "reflect" "strings" "testing" "github.com/sigstore/rekor/pkg/pki/identity" "github.com/sigstore/rekor/pkg/pki/x509/testutils" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" ) // Generated with: // openssl genrsa -out myprivate.pem 512 // openssl pkcs8 -topk8 -in myprivate.pem -nocrypt' const pkcs1v15Priv = `-----BEGIN PRIVATE KEY----- MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAoLEL57Kd5w8b5LCl SM+5mJbVYj4GoFXP/Gynfk6mDj7aANYWAkU74xkjz0BX2Nq0IT9DyxWI8aXZ8B6R YtbsPwIDAQABAkA2WgwTz5eXKsYdgR421YQKN6JvO1mUa9IQqFOy5jlGgbR+W5HG JfQVJKhCGMYYmByHgR0QDk/6gvJjhuszTHuJAiEA0siY/vE20zC1UHpPgDXXVSNN dKtM6YKBKSo47oTKQHsCIQDDKZgal50Cd3W+lOWpNO23QGZgBhJrJ70TpcPWGEsS DQIhAIDIMLnq1G1Z4B2IbRRPUP3icMtscbRlmNZ2xovsM8oLAiBluZh+w+gjEQFe hV3wBJajnf2+r2uKTvxO8WhSf/chQQIhAKzYjX2chfvPN6hRqeGeoPpRLXS8cdxC A4hZJRvZgkO3 -----END PRIVATE KEY----- ` // Extracted from above with: // openssl rsa -in myprivate.pem -pubout const pkcs1v15Pub = `-----BEGIN PUBLIC KEY----- MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKCxC+eynecPG+SwpUjPuZiW1WI+BqBV z/xsp35Opg4+2gDWFgJFO+MZI89AV9jatCE/Q8sViPGl2fAekWLW7D8CAwEAAQ== -----END PUBLIC KEY----- ` // Generated with: // openssl ecparam -genkey -name prime256v1 > ec_private.pem // openssl pkcs8 -topk8 -in ec_private.pem -nocrypt const priv = `-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmrLtCpBdXgXLUr7o nSUPfo3oXMjmvuwTOjpTulIBKlKhRANCAATH6KSpTFe6uXFmW1qNEFXaO7fWPfZt pPZrHZ1cFykidZoURKoYXfkohJ+U/USYy8Sd8b4DMd5xDRZCnlDM0h37 -----END PRIVATE KEY----- ` // Extracted from above with: // openssl ec -in ec_private.pem -pubout const pubStr = `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEx+ikqUxXurlxZltajRBV2ju31j32 baT2ax2dXBcpInWaFESqGF35KISflP1EmMvEnfG+AzHecQ0WQp5QzNId+w== -----END PUBLIC KEY----- ` // Generated with: // openssl genpkey -algorithm ED25519 -out edprivate.pem const ed25519Priv = `-----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIKjlXfR/VFvO9qM9+CG2qbuSM54k8ciKWHhgNwKTgqpG -----END PRIVATE KEY----- ` // Extracted from above with: // openssl pkey -in edprivate.pem -pubout const ed25519Pub = `-----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEAizWek2gKgMM+bad4rVJ5nc9NsbNOba0A0BNfzOgklRs= -----END PUBLIC KEY----- ` const pubWithTrailingNewLine = `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEx+ikqUxXurlxZltajRBV2ju31j32 baT2ax2dXBcpInWaFESqGF35KISflP1EmMvEnfG+AzHecQ0WQp5QzNId+w== -----END PUBLIC KEY----- ` func signData(t *testing.T, b []byte, pkey string) []byte { priv, err := cryptoutils.UnmarshalPEMToPrivateKey([]byte(pkey), cryptoutils.SkipPassword) if err != nil { t.Fatal(err) } signer, err := signature.LoadSigner(priv, crypto.SHA256) if err != nil { t.Fatal(err) } signature, err := signer.SignMessage(bytes.NewReader(b)) if err != nil { t.Fatal(err) } return signature } func TestSignature_Verify(t *testing.T) { tests := []struct { name string priv string pub string }{ { name: "rsa", priv: pkcs1v15Priv, pub: pkcs1v15Pub, }, { name: "ec", priv: priv, pub: pubStr, }, { name: "ed25519", priv: ed25519Priv, pub: ed25519Pub, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { data := []byte("hey! this is my test data") sigBytes := signData(t, data, tt.priv) s, err := NewSignature(bytes.NewReader(sigBytes)) if err != nil { t.Fatal(err) } pub, err := NewPublicKey(strings.NewReader(tt.pub)) if err != nil { t.Fatal(err) } if err := s.Verify(bytes.NewReader(data), pub); err != nil { t.Errorf("Signature.Verify() error = %v", err) } // Now try with the canonical value cb, err := s.CanonicalValue() if err != nil { t.Error(err) } canonicalSig, err := NewSignature(bytes.NewReader(cb)) if err != nil { t.Error(err) } if err := canonicalSig.Verify(bytes.NewReader(data), pub); err != nil { t.Errorf("Signature.Verify() error = %v", err) } pubKey, _ := cryptoutils.UnmarshalPEMToPublicKey([]byte(tt.pub)) derKey, _ := cryptoutils.MarshalPublicKeyToDER(pubKey) digest := sha256.Sum256(derKey) expectedID := identity.Identity{Crypto: pubKey, Raw: derKey, Fingerprint: hex.EncodeToString(digest[:])} ids, err := pub.Identities() if err != nil { t.Fatal(err) } if len(ids) != 1 { t.Errorf("%v: too many identities, expected 1, got %v", tt.name, len(ids)) } switch v := ids[0].Crypto.(type) { case *rsa.PublicKey: if tt.name != "rsa" { t.Fatalf("unexpected key, expected RSA, got %v", reflect.TypeOf(v)) } case *ecdsa.PublicKey: if tt.name != "ec" { t.Fatalf("unexpected key, expected RSA, got %v", reflect.TypeOf(v)) } case ed25519.PublicKey: if tt.name != "ed25519" { t.Fatalf("unexpected key, expected RSA, got %v", reflect.TypeOf(v)) } default: t.Fatalf("unexpected key type, got %v", reflect.TypeOf(v)) } if err := cryptoutils.EqualKeys(expectedID.Crypto, ids[0].Crypto); err != nil { t.Errorf("%v: public keys did not match: %v", tt.name, err) } if !reflect.DeepEqual(expectedID.Raw, ids[0].Raw) { t.Errorf("%v: raw identities did not match, expected %v, got %v", tt.name, expectedID.Raw, ids[0].Raw) } if expectedID.Fingerprint != ids[0].Fingerprint { t.Errorf("%v: fingerprints did not match, expected %v, got %v", tt.name, expectedID.Fingerprint, ids[0].Fingerprint) } }) } } func TestSignature_VerifyFail(t *testing.T) { tests := []struct { name string priv string pub string }{ { name: "rsa", priv: pkcs1v15Priv, pub: pkcs1v15Pub, }, { name: "ec", priv: priv, pub: pubStr, }, { name: "ed25519", priv: ed25519Priv, pub: ed25519Pub, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Make some fake data, and tamper with the signature data := []byte("hey! this is my test data") sigBytes := signData(t, data, tt.priv) sigBytes[0]-- s, err := NewSignature(bytes.NewReader(sigBytes)) if err != nil { t.Fatal(err) } pub, err := NewPublicKey(strings.NewReader(tt.pub)) if err != nil { t.Fatal(err) } if err := s.Verify(bytes.NewReader(data), pub); err == nil { t.Error("Signature.Verify() expected error!") } }) } } func TestPublicKeyWithCertChain(t *testing.T) { rootCert, rootKey, _ := testutils.GenerateRootCa() subCert, subKey, _ := testutils.GenerateSubordinateCa(rootCert, rootKey) subjectURL, _ := url.Parse("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.1.1") leafCert, leafKey, _ := testutils.GenerateLeafCertWithSubjectAlternateNames( []string{"example.com"}, []string{"subject@example.com"}, []net.IP{{1, 1, 1, 1}}, []*url.URL{subjectURL}, "oidc-issuer", subCert, subKey) pemCertChain, err := cryptoutils.MarshalCertificatesToPEM([]*x509.Certificate{leafCert, subCert, rootCert}) if err != nil { t.Fatalf("unexpected error marshalling certificate chain: %v", err) } pub, err := NewPublicKey(bytes.NewReader(pemCertChain)) if err != nil { t.Fatalf("unexpected error generating public key: %v", err) } if pub.certs == nil || !pub.certs[0].Equal(leafCert) || !pub.certs[1].Equal(subCert) || !pub.certs[2].Equal(rootCert) { t.Fatal("expected certificate chain to match provided certificate chain") } if !pub.CryptoPubKey().(*ecdsa.PublicKey).Equal(leafKey.Public()) { t.Fatal("expected public keys to match") } if !reflect.DeepEqual(pub.EmailAddresses(), leafCert.EmailAddresses) { t.Fatalf("expected matching subjects, expected %v, got %v", leafCert.EmailAddresses, pub.EmailAddresses()) } var expectedSubjects []string expectedSubjects = append(expectedSubjects, leafCert.DNSNames...) expectedSubjects = append(expectedSubjects, leafCert.EmailAddresses...) expectedSubjects = append(expectedSubjects, leafCert.IPAddresses[0].String()) expectedSubjects = append(expectedSubjects, leafCert.URIs[0].String()) if !reflect.DeepEqual(pub.Subjects(), expectedSubjects) { t.Fatalf("expected matching subjects, expected %v, got %v", expectedSubjects, pub.Subjects()) } digest := sha256.Sum256(leafCert.Raw) expectedID := identity.Identity{Crypto: leafCert, Raw: leafCert.Raw, Fingerprint: hex.EncodeToString((digest[:]))} ids, err := pub.Identities() if err != nil { t.Fatal(err) } if len(ids) != 1 { t.Errorf("too many identities, expected 1, got %v", len(ids)) } if !ids[0].Crypto.(*x509.Certificate).Equal(expectedID.Crypto.(*x509.Certificate)) { t.Errorf("certificates did not match") } if !reflect.DeepEqual(expectedID.Raw, ids[0].Raw) { t.Errorf("raw identities did not match, expected %v, got %v", expectedID.Raw, ids[0].Raw) } if expectedID.Fingerprint != ids[0].Fingerprint { t.Errorf("fingerprints did not match, expected %v, got %v", expectedID.Fingerprint, ids[0].Fingerprint) } canonicalValue, err := pub.CanonicalValue() if err != nil { t.Fatalf("unexpected error fetching canonical value: %v", err) } if !reflect.DeepEqual(canonicalValue, pemCertChain) { t.Fatalf("expected canonical value %v, got %v", pemCertChain, canonicalValue) } // Generate signature to verify data := []byte("test") signer, err := signature.LoadSigner(leafKey, crypto.SHA256) if err != nil { t.Fatal(err) } sigBytes, err := signer.SignMessage(bytes.NewReader(data)) if err != nil { t.Fatal(err) } s, err := NewSignature(bytes.NewReader(sigBytes)) if err != nil { t.Fatalf("unexpected error generating signature: %v", err) } err = s.Verify(bytes.NewReader(data), pub) if err != nil { t.Fatalf("unexpected error verifying signature, %v", err) } // Verify works with expired certificate leafCert, leafKey, _ = testutils.GenerateExpiredLeafCert("subject@example.com", "oidc-issuer", subCert, subKey) pemCertChain, _ = cryptoutils.MarshalCertificatesToPEM([]*x509.Certificate{leafCert, subCert, rootCert}) pub, _ = NewPublicKey(bytes.NewReader(pemCertChain)) signer, _ = signature.LoadSigner(leafKey, crypto.SHA256) sigBytes, _ = signer.SignMessage(bytes.NewReader(data)) s, _ = NewSignature(bytes.NewReader(sigBytes)) err = s.Verify(bytes.NewReader(data), pub) if err != nil { t.Fatalf("unexpected error verifying signature with expired certificate: %v", err) } // Verify error with invalid chain pemCertChain, _ = cryptoutils.MarshalCertificatesToPEM([]*x509.Certificate{leafCert, rootCert}) pub, _ = NewPublicKey(bytes.NewReader(pemCertChain)) signer, _ = signature.LoadSigner(leafKey, crypto.SHA256) sigBytes, _ = signer.SignMessage(bytes.NewReader(data)) s, _ = NewSignature(bytes.NewReader(sigBytes)) err = s.Verify(bytes.NewReader(data), pub) if err == nil || !strings.Contains(err.Error(), "x509: certificate signed by unknown authority") { t.Fatalf("expected error verifying signature, got %v", err) } // Verify works with chain without intermediate leafCert, leafKey, _ = testutils.GenerateLeafCert("subject@example.com", "oidc-issuer", nil, rootCert, rootKey) pemCertChain, _ = cryptoutils.MarshalCertificatesToPEM([]*x509.Certificate{leafCert, rootCert}) pub, _ = NewPublicKey(bytes.NewReader(pemCertChain)) signer, _ = signature.LoadSigner(leafKey, crypto.SHA256) sigBytes, _ = signer.SignMessage(bytes.NewReader(data)) s, _ = NewSignature(bytes.NewReader(sigBytes)) err = s.Verify(bytes.NewReader(data), pub) if err != nil { t.Fatalf("unexpected error verifying signature, %v", err) } // Verify error with long chain chain := []*x509.Certificate{} for i := 0; i < 11; i++ { chain = append(chain, leafCert) } pemCertChain, _ = cryptoutils.MarshalCertificatesToPEM(chain) _, err = NewPublicKey(bytes.NewReader(pemCertChain)) if err == nil || !strings.Contains(err.Error(), "too many certificates specified in PEM block") { t.Fatalf("expected error with long certificate chain, got %v", err) } // Verify public key with extra trailing newline is parsed OK key, err := NewPublicKey(strings.NewReader(pubWithTrailingNewLine)) if err != nil { t.Fatalf("unexpected error parsing public key with extra trailing newline: %v", err) } canonicalKeyBytes, err := key.CanonicalValue() if err != nil { t.Fatalf("unexpected error canonicalizing public key with extra trailing newline: %v", err) } if !bytes.Equal([]byte(pubStr), canonicalKeyBytes) { t.Fatalf("expected canonical value to match original without extra trailing new line") } } rekor-1.3.5/pkg/pubsub/000077500000000000000000000000001455727245600147445ustar00rootroot00000000000000rekor-1.3.5/pkg/pubsub/doc.go000066400000000000000000000013511455727245600160400ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package pubsub provides an interface and implementations for publishing // notifications for Rekor updates to a Pub/Sub system. package pubsub rekor-1.3.5/pkg/pubsub/gcp/000077500000000000000000000000001455727245600155155ustar00rootroot00000000000000rekor-1.3.5/pkg/pubsub/gcp/publisher.go000066400000000000000000000107021455727245600200410ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package gcp implements the pubsub.Publisher with Google Cloud Pub/Sub. package gcp import ( "context" "encoding/base64" "fmt" "os" "regexp" "sync" "time" "github.com/sigstore/rekor/pkg/events" sigpubsub "github.com/sigstore/rekor/pkg/pubsub" "cloud.google.com/go/pubsub" "google.golang.org/api/option" ) func init() { sigpubsub.AddProvider(URIIdentifier, func(ctx context.Context, topicResourceID string) (sigpubsub.Publisher, error) { return New(ctx, topicResourceID) }) } const URIIdentifier = "gcppubsub://" var ( // Copied from https://github.com/google/go-cloud/blob/master/pubsub/gcppubsub/gcppubsub.go re = regexp.MustCompile(`^gcppubsub://projects/([^/]+)/topics/([^/]+)$`) // Minimal set of permissions needed to check if the server can publish to the configured topic. // https://cloud.google.com/pubsub/docs/access-control#required_permissions requiredIAMPermissions = []string{ "pubsub.topics.publish", } ) type Publisher struct { client *pubsub.Client topic string wg *sync.WaitGroup } func New(ctx context.Context, topicResourceID string, opts ...option.ClientOption) (*Publisher, error) { projectID, topic, err := parseRef(topicResourceID) if err != nil { return nil, fmt.Errorf("parse ref: %w", err) } client, err := pubsub.NewClient(ctx, projectID, opts...) if err != nil { return nil, fmt.Errorf("create pubsub client for project %q: %w", projectID, err) } // The PubSub emulator does not support IAM methods, and will block the // server start up if they are called. If the environment variable is set, // skip this check. if os.Getenv("PUBSUB_EMULATOR_HOST") == "" { if _, err := client.Topic(topic).IAM().TestPermissions(ctx, requiredIAMPermissions); err != nil { return nil, fmt.Errorf("insufficient permissions for topic %q: %w", topic, err) } } return &Publisher{ client: client, topic: topic, wg: new(sync.WaitGroup), }, nil } func (p *Publisher) Publish(ctx context.Context, event *events.Event, encoding events.EventContentType) error { p.wg.Add(1) defer p.wg.Done() var data []byte var err error switch encoding { case events.ContentTypeProtobuf: data, err = event.MarshalProto() case events.ContentTypeJSON: data, err = event.MarshalJSON() default: err = fmt.Errorf("unsupported encoding: %s", encoding) } if err != nil { return fmt.Errorf("marshal event: %w", err) } msg := &pubsub.Message{ Data: data, Attributes: gcpAttrs(event, encoding), } // The Publish call does not block. res := p.client.Topic(p.topic).Publish(ctx, msg) // TODO: Consider making the timeout configurable. cctx, cancel := context.WithTimeout(ctx, pubsub.DefaultPublishSettings.Timeout) defer cancel() // This Get call blocks until a response occurs, or the deadline is reached. if _, err := res.Get(cctx); err != nil { return fmt.Errorf("publish event %s to topic %q: %w", event.ID(), p.topic, err) } return nil } func (p *Publisher) Close() error { p.wg.Wait() return p.client.Close() } func parseRef(ref string) (projectID, topic string, err error) { v := re.FindStringSubmatch(ref) if len(v) != 3 { err = fmt.Errorf("invalid gcppubsub format %q", ref) return } projectID, topic = v[1], v[2] return } // GCP Pub/Sub attributes can be used to filter events server-side, reducing // the processing for the client and reducing GCP costs for egress fees. func gcpAttrs(event *events.Event, dataType events.EventContentType) map[string]string { attrs := map[string]string{ "source": event.Type().Source(), "type": event.Type().Name(), "datacontenttype": string(dataType), } for name, value := range event.Attributes() { switch v := value.(type) { case string: attrs[name] = v case time.Time: attrs[name] = v.Format(time.RFC3339) case []byte: attrs[name] = base64.StdEncoding.EncodeToString(v) default: attrs[name] = fmt.Sprint(v) } } return attrs } rekor-1.3.5/pkg/pubsub/gcp/publisher_test.go000066400000000000000000000067601455727245600211110ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package gcp import ( "testing" "time" "github.com/google/go-cmp/cmp" "github.com/sigstore/rekor/pkg/events" "google.golang.org/protobuf/types/known/emptypb" ) func TestParseRef(t *testing.T) { t.Parallel() testCases := []struct { desc string ref string wantProject string wantTopic string wantErr bool }{ { desc: "Valid example", ref: "gcppubsub://projects/project-foo/topics/topic-bar", wantProject: "project-foo", wantTopic: "topic-bar", }, { desc: "Empty ref", wantErr: true, }, { desc: "Missing topic", ref: "gcppubsub://projects/project-foo/topics/", wantErr: true, }, { desc: "Wrong scheme", ref: "foo://projects/project-foo/topics/topic-bar", wantErr: true, }, } for _, tc := range testCases { tc := tc t.Run(tc.desc, func(t *testing.T) { t.Parallel() project, topic, err := parseRef(tc.ref) gotErr := err != nil if gotErr != tc.wantErr { t.Errorf("parseRef(%s) error = %v, wantErr %v", tc.ref, gotErr, tc.wantErr) return } if project != tc.wantProject { t.Errorf("parseRef(%s) project = %s, want %s", tc.ref, project, tc.wantProject) } if topic != tc.wantTopic { t.Errorf("parseRef(%s) topic = %s, want %s", tc.ref, topic, tc.wantTopic) } }) } } func TestGCPAttrs(t *testing.T) { t.Parallel() empty := &emptypb.Empty{} ty := events.RegisterType("gcpAttrsTestEvent", "/source", empty.ProtoReflect().Descriptor()) coreEvent, err := ty.New("A123-456", &emptypb.Empty{}, nil) if err != nil { t.Fatal(err) } attrs := map[string]any{ "attr_string": "string", "attr_bool": true, "attr_int": 123, "attr_bytes": []byte("hello"), "attr_timestamp": time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Second), } attrsEvent, err := ty.New("A456-789", &emptypb.Empty{}, attrs) if err != nil { t.Fatal(err) } testCases := []struct { desc string event *events.Event want map[string]string }{ { desc: "Core attrs only", event: coreEvent, want: map[string]string{ "datacontenttype": "application/fake-test-mime", "source": "/source", "type": "gcpAttrsTestEvent", }, }, { desc: "With optional attrs", event: attrsEvent, want: map[string]string{ "datacontenttype": "application/fake-test-mime", "source": "/source", "type": "gcpAttrsTestEvent", "attr_string": "string", "attr_int": "123", "attr_bool": "true", "attr_bytes": "aGVsbG8=", "attr_timestamp": time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Second).Format(time.RFC3339), }, }, } for _, tc := range testCases { tc := tc t.Run(tc.desc, func(t *testing.T) { t.Parallel() got := gcpAttrs(tc.event, "application/fake-test-mime") if diff := cmp.Diff(got, tc.want); diff != "" { t.Errorf("unexpected diff:\n%s", diff) } }) } } rekor-1.3.5/pkg/pubsub/publisher.go000066400000000000000000000046241455727245600172760ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package pubsub import ( "context" "fmt" "strings" "github.com/sigstore/rekor/pkg/events" "golang.org/x/exp/maps" "golang.org/x/exp/slices" ) // Publisher provides methods for publishing events to a Pub/Sub topic. type Publisher interface { // Publish publishes a CloudEvent to the configured Pub/Sub topic serialized // using the specified encoding type. Publish(ctx context.Context, event *events.Event, encoding events.EventContentType) error // Close safely closes any active connections. Close() error } // ProviderNotFoundError indicates that no matching PubSub provider was found. type ProviderNotFoundError struct { ref string } func (e *ProviderNotFoundError) Error() string { return fmt.Sprintf("no pubsub provider found for key reference: %s", e.ref) } // ProviderInit is a function that initializes provider-specific Publisher. type ProviderInit func(ctx context.Context, topicResourceID string) (Publisher, error) // AddProvider adds the provider implementation into the local cache func AddProvider(uri string, init ProviderInit) { providersMap[uri] = init } var providersMap = map[string]ProviderInit{} // Get returns a Publisher for the given resource string and hash function. // If no matching provider is found, Get returns a ProviderNotFoundError. It // also returns an error if initializing the Publisher fails. If no resource // is supplied, it returns a nil Publisher and no error. func Get(ctx context.Context, topicResourceID string) (Publisher, error) { for ref, pi := range providersMap { if strings.HasPrefix(topicResourceID, ref) { return pi(ctx, topicResourceID) } } return nil, &ProviderNotFoundError{ref: topicResourceID} } // SupportedProviders returns list of initialized providers func SupportedProviders() []string { names := maps.Keys(providersMap) slices.Sort(names) return names } rekor-1.3.5/pkg/sharding/000077500000000000000000000000001455727245600152435ustar00rootroot00000000000000rekor-1.3.5/pkg/sharding/log_index.go000066400000000000000000000025221455727245600175430ustar00rootroot00000000000000// Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sharding // VirtualLogIndex returns the virtual log index for a given leaf index func VirtualLogIndex(leafIndex int64, tid int64, ranges LogRanges) int64 { // if we have no inactive ranges, we have just one log! return the leafIndex as is // as long as it matches the active tree ID if ranges.NoInactive() { if ranges.GetActive() == tid { return leafIndex } return -1 } var virtualIndex int64 for _, r := range ranges.GetInactive() { if r.TreeID == tid { return virtualIndex + leafIndex } virtualIndex += r.TreeLength } // If no TreeID in Inactive matches the tid, the virtual index should be the active tree if ranges.GetActive() == tid { return virtualIndex + leafIndex } // Otherwise, the tid is invalid return -1 } rekor-1.3.5/pkg/sharding/log_index_test.go000066400000000000000000000041351455727245600206040ustar00rootroot00000000000000// Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sharding import ( "testing" ) func TestVirtualLogIndex(t *testing.T) { tests := []struct { description string leafIndex int64 tid int64 ranges LogRanges expectedIndex int64 }{ { description: "no ranges", leafIndex: 5, ranges: LogRanges{}, expectedIndex: 5, }, // Log 100: 0 1 2 3 4 // Log 300: 5 6 7... { description: "two shards", leafIndex: 2, tid: 300, ranges: LogRanges{ inactive: []LogRange{ { TreeID: 100, TreeLength: 5, }}, active: 300, }, expectedIndex: 7, }, // Log 100: 0 1 2 3 4 // Log 300: 5 6 7 8 // Log 400: ... { description: "three shards", leafIndex: 1, tid: 300, ranges: LogRanges{ inactive: []LogRange{ { TreeID: 100, TreeLength: 5, }, { TreeID: 300, TreeLength: 4, }}, active: 400, }, expectedIndex: 6, }, // Log 30: 1 2 3... { description: "only active tree", leafIndex: 2, tid: 30, ranges: LogRanges{ active: 30, }, expectedIndex: 2, }, { description: "invalid tid passed in", leafIndex: 2, tid: 4, ranges: LogRanges{ active: 30, }, expectedIndex: -1, }, } for _, test := range tests { t.Run(test.description, func(t *testing.T) { got := VirtualLogIndex(test.leafIndex, test.tid, test.ranges) if got != test.expectedIndex { t.Fatalf("expected %v got %v", test.expectedIndex, got) } }) } } rekor-1.3.5/pkg/sharding/ranges.go000066400000000000000000000134301455727245600170520ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sharding import ( "context" "encoding/base64" "encoding/json" "errors" "fmt" "os" "strconv" "strings" "github.com/google/trillian" "github.com/google/trillian/types" "github.com/sigstore/rekor/pkg/log" "sigs.k8s.io/yaml" ) type LogRanges struct { inactive Ranges active int64 } type Ranges []LogRange type LogRange struct { TreeID int64 `json:"treeID" yaml:"treeID"` TreeLength int64 `json:"treeLength" yaml:"treeLength"` EncodedPublicKey string `json:"encodedPublicKey" yaml:"encodedPublicKey"` decodedPublicKey string } func NewLogRanges(ctx context.Context, logClient trillian.TrillianLogClient, path string, treeID uint) (LogRanges, error) { if path == "" { log.Logger.Info("No config file specified, skipping init of logRange map") return LogRanges{}, nil } if treeID == 0 { return LogRanges{}, errors.New("non-zero tlog_id required when passing in shard config filepath; please set the active tree ID via the `--trillian_log_server.tlog_id` flag") } // otherwise, try to read contents of the sharding config ranges, err := logRangesFromPath(path) if err != nil { return LogRanges{}, fmt.Errorf("log ranges from path: %w", err) } for i, r := range ranges { r, err := updateRange(ctx, logClient, r) if err != nil { return LogRanges{}, fmt.Errorf("updating range for tree id %d: %w", r.TreeID, err) } ranges[i] = r } log.Logger.Info("Ranges: %v", ranges) return LogRanges{ inactive: ranges, active: int64(treeID), }, nil } func logRangesFromPath(path string) (Ranges, error) { var ranges Ranges contents, err := os.ReadFile(path) if err != nil { return Ranges{}, err } if string(contents) == "" { log.Logger.Info("Sharding config file contents empty, skipping init of logRange map") return Ranges{}, nil } if err := yaml.Unmarshal(contents, &ranges); err != nil { // Try to use JSON if jerr := json.Unmarshal(contents, &ranges); jerr == nil { return ranges, nil } return Ranges{}, err } return ranges, nil } // updateRange fills in any missing information about the range func updateRange(ctx context.Context, logClient trillian.TrillianLogClient, r LogRange) (LogRange, error) { // If a tree length wasn't passed in, get it ourselves if r.TreeLength == 0 { resp, err := logClient.GetLatestSignedLogRoot(ctx, &trillian.GetLatestSignedLogRootRequest{LogId: r.TreeID}) if err != nil { return LogRange{}, fmt.Errorf("getting signed log root for tree %d: %w", r.TreeID, err) } var root types.LogRootV1 if err := root.UnmarshalBinary(resp.SignedLogRoot.LogRoot); err != nil { return LogRange{}, err } r.TreeLength = int64(root.TreeSize) } // If a public key was provided, decode it if r.EncodedPublicKey != "" { decoded, err := base64.StdEncoding.DecodeString(r.EncodedPublicKey) if err != nil { return LogRange{}, err } r.decodedPublicKey = string(decoded) } return r, nil } func (l *LogRanges) ResolveVirtualIndex(index int) (int64, int64) { indexLeft := index for _, l := range l.inactive { if indexLeft < int(l.TreeLength) { return l.TreeID, int64(indexLeft) } indexLeft -= int(l.TreeLength) } // If index not found in inactive trees, return the active tree return l.active, int64(indexLeft) } func (l *LogRanges) ActiveTreeID() int64 { return l.active } func (l *LogRanges) NoInactive() bool { return l.inactive == nil } // AllShards returns all shards, starting with the active shard and then the inactive shards func (l *LogRanges) AllShards() []int64 { shards := []int64{l.ActiveTreeID()} for _, in := range l.GetInactive() { shards = append(shards, in.TreeID) } return shards } // TotalInactiveLength returns the total length across all inactive shards; // we don't know the length of the active shard. func (l *LogRanges) TotalInactiveLength() int64 { var total int64 for _, r := range l.inactive { total += r.TreeLength } return total } func (l *LogRanges) SetInactive(r []LogRange) { l.inactive = r } func (l *LogRanges) GetInactive() []LogRange { return l.inactive } func (l *LogRanges) AppendInactive(r LogRange) { l.inactive = append(l.inactive, r) } func (l *LogRanges) SetActive(i int64) { l.active = i } func (l *LogRanges) GetActive() int64 { return l.active } func (l *LogRanges) String() string { ranges := []string{} for _, r := range l.inactive { ranges = append(ranges, fmt.Sprintf("%d=%d", r.TreeID, r.TreeLength)) } ranges = append(ranges, fmt.Sprintf("active=%d", l.active)) return strings.Join(ranges, ",") } // PublicKey returns the associated public key for the given Tree ID // and returns the active public key by default func (l *LogRanges) PublicKey(activePublicKey, treeID string) (string, error) { // if no tree ID is specified, assume the active tree if treeID == "" { return activePublicKey, nil } tid, err := strconv.Atoi(treeID) if err != nil { return "", err } for _, i := range l.inactive { if int(i.TreeID) == tid { if i.decodedPublicKey != "" { return i.decodedPublicKey, nil } // assume the active public key if one wasn't provided return activePublicKey, nil } } if tid == int(l.active) { return activePublicKey, nil } return "", fmt.Errorf("%d is not a valid tree ID and doesn't have an associated public key", tid) } rekor-1.3.5/pkg/sharding/ranges_test.go000066400000000000000000000267151455727245600201230ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sharding import ( "context" "encoding/json" "errors" "os" "path/filepath" "reflect" "testing" "github.com/golang/mock/gomock" "github.com/google/trillian/testonly" "github.com/google/trillian" "google.golang.org/grpc" "gopkg.in/yaml.v2" ) func TestNewLogRanges(t *testing.T) { contents := ` - treeID: 0001 treeLength: 3 encodedPublicKey: c2hhcmRpbmcK - treeID: 0002 treeLength: 4` file := filepath.Join(t.TempDir(), "sharding-config") if err := os.WriteFile(file, []byte(contents), 0o644); err != nil { t.Fatal(err) } treeID := uint(45) expected := LogRanges{ inactive: []LogRange{ { TreeID: 1, TreeLength: 3, EncodedPublicKey: "c2hhcmRpbmcK", decodedPublicKey: "sharding\n", }, { TreeID: 2, TreeLength: 4, }, }, active: int64(45), } ctx := context.Background() tc := trillian.NewTrillianLogClient(&grpc.ClientConn{}) got, err := NewLogRanges(ctx, tc, file, treeID) if err != nil { t.Fatal(err) } if expected.ActiveTreeID() != got.ActiveTreeID() { t.Fatalf("expected tree id %d got %d", expected.ActiveTreeID(), got.ActiveTreeID()) } if !reflect.DeepEqual(expected.GetInactive(), got.GetInactive()) { t.Fatalf("expected %v got %v", expected.GetInactive(), got.GetInactive()) } } func TestLogRanges_ResolveVirtualIndex(t *testing.T) { lrs := LogRanges{ inactive: []LogRange{ {TreeID: 1, TreeLength: 17}, {TreeID: 2, TreeLength: 1}, {TreeID: 3, TreeLength: 100}, }, active: 4, } for _, tt := range []struct { Index int WantTreeID int64 WantIndex int64 }{ { Index: 3, WantTreeID: 1, WantIndex: 3, }, // This is the first (0th) entry in the next tree { Index: 17, WantTreeID: 2, WantIndex: 0, }, // Overflow { Index: 3000, WantTreeID: 4, WantIndex: 2882, }, } { tree, index := lrs.ResolveVirtualIndex(tt.Index) if tree != tt.WantTreeID { t.Errorf("LogRanges.ResolveVirtualIndex() tree = %v, want %v", tree, tt.WantTreeID) } if index != tt.WantIndex { t.Errorf("LogRanges.ResolveVirtualIndex() index = %v, want %v", index, tt.WantIndex) } } } func TestPublicKey(t *testing.T) { ranges := LogRanges{ active: 45, inactive: []LogRange{ { TreeID: 10, TreeLength: 10, decodedPublicKey: "sharding", }, { TreeID: 20, TreeLength: 20, }, }, } activePubKey := "activekey" tests := []struct { description string treeID string expectedPubKey string shouldErr bool }{ { description: "empty tree ID", expectedPubKey: "activekey", }, { description: "tree id with decoded public key", treeID: "10", expectedPubKey: "sharding", }, { description: "tree id without decoded public key", treeID: "20", expectedPubKey: "activekey", }, { description: "invalid tree id", treeID: "34", shouldErr: true, }, { description: "pass in active tree id", treeID: "45", expectedPubKey: "activekey", }, } for _, test := range tests { t.Run(test.description, func(t *testing.T) { got, err := ranges.PublicKey(activePubKey, test.treeID) if err != nil && !test.shouldErr { t.Fatal(err) } if test.shouldErr { return } if got != test.expectedPubKey { t.Fatalf("got %s doesn't match expected %s", got, test.expectedPubKey) } }) } } func TestLogRanges_String(t *testing.T) { type fields struct { inactive Ranges active int64 } tests := []struct { name string fields fields want string }{ { name: "empty", fields: fields{ inactive: Ranges{}, active: 0, }, want: "active=0", }, { name: "one", fields: fields{ inactive: Ranges{ { TreeID: 1, TreeLength: 2, }, }, active: 3, }, want: "1=2,active=3", }, { name: "two", fields: fields{ inactive: Ranges{ { TreeID: 1, TreeLength: 2, }, { TreeID: 2, TreeLength: 3, }, }, active: 4, }, want: "1=2,2=3,active=4", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { l := &LogRanges{ inactive: tt.fields.inactive, active: tt.fields.active, } if got := l.String(); got != tt.want { t.Errorf("String() = %v, want %v", got, tt.want) } }) } } func TestLogRanges_TotalInactiveLength(t *testing.T) { type fields struct { inactive Ranges active int64 } tests := []struct { name string fields fields want int64 }{ { name: "empty", fields: fields{ inactive: Ranges{}, active: 0, }, want: 0, }, { name: "one", fields: fields{ inactive: Ranges{ { TreeID: 1, TreeLength: 2, }, }, active: 3, }, want: 2, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { l := &LogRanges{ inactive: tt.fields.inactive, active: tt.fields.active, } if got := l.TotalInactiveLength(); got != tt.want { t.Errorf("TotalInactiveLength() = %v, want %v", got, tt.want) } }) } } func TestLogRanges_AllShards(t *testing.T) { type fields struct { inactive Ranges active int64 } tests := []struct { name string fields fields want []int64 }{ { name: "empty", fields: fields{ inactive: Ranges{}, active: 0, }, want: []int64{0}, }, { name: "one", fields: fields{ inactive: Ranges{ { TreeID: 1, TreeLength: 2, }, }, active: 3, }, want: []int64{3, 1}, }, { name: "two", fields: fields{ inactive: Ranges{ { TreeID: 1, TreeLength: 2, }, { TreeID: 2, TreeLength: 3, }, }, active: 4, }, want: []int64{4, 1, 2}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { l := &LogRanges{ inactive: tt.fields.inactive, active: tt.fields.active, } if got := l.AllShards(); !reflect.DeepEqual(got, tt.want) { t.Errorf("AllShards() = %v, want %v", got, tt.want) } }) } } func TestLogRangesFromPath(t *testing.T) { type args struct { path string } tests := []struct { name string args args want Ranges content string wantJSON bool wantYaml bool wantInvalidJSON bool wantErr bool }{ { name: "empty", args: args{ path: "", }, want: Ranges{}, wantErr: true, }, { name: "empty file", args: args{ path: "one", }, want: Ranges{}, wantErr: false, }, { name: "valid json", args: args{ path: "one", }, want: Ranges{ { TreeID: 1, TreeLength: 2, }, }, wantJSON: true, wantErr: false, }, { name: "valid yaml", args: args{ path: "one", }, want: Ranges{ { TreeID: 1, TreeLength: 2, }, }, wantYaml: true, wantErr: false, }, { name: "invalid json", args: args{ path: "one", }, want: Ranges{}, wantInvalidJSON: true, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.args.path != "" { f, err := os.CreateTemp("", tt.args.path) if err != nil { t.Fatalf("Failed to create temp file: %v", err) } switch { case tt.wantJSON: if err := json.NewEncoder(f).Encode(tt.want); err != nil { t.Fatalf("Failed to encode json: %v", err) } case tt.wantYaml: if err := yaml.NewEncoder(f).Encode(tt.want); err != nil { t.Fatalf("Failed to encode yaml: %v", err) } case tt.wantInvalidJSON: if _, err := f.WriteString("invalid json"); err != nil { t.Fatalf("Failed to write invalid json: %v", err) } } if _, err := f.Write([]byte(tt.content)); err != nil { t.Fatalf("Failed to write to temp file: %v", err) } defer f.Close() defer os.Remove(f.Name()) tt.args.path = f.Name() } got, err := logRangesFromPath(tt.args.path) if (err != nil) != tt.wantErr { t.Errorf("logRangesFromPath() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { t.Errorf("logRangesFromPath() got = %v, want %v", got, tt.want) } }) } } func TestUpdateRange(t *testing.T) { type args struct { ctx context.Context r LogRange } tests := []struct { name string args args want LogRange wantErr bool rootResponse *trillian.GetLatestSignedLogRootResponse signedLogError error }{ { name: "empty", args: args{ ctx: context.Background(), r: LogRange{}, }, want: LogRange{}, wantErr: true, rootResponse: &trillian.GetLatestSignedLogRootResponse{ SignedLogRoot: &trillian.SignedLogRoot{}, }, signedLogError: nil, }, { name: "error in GetLatestSignedLogRoot", args: args{ ctx: context.Background(), r: LogRange{}, }, want: LogRange{}, wantErr: true, rootResponse: &trillian.GetLatestSignedLogRootResponse{ SignedLogRoot: &trillian.SignedLogRoot{}, }, signedLogError: errors.New("error"), }, } mockCtl := gomock.NewController(t) defer mockCtl.Finish() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, fakeServer, err := testonly.NewMockServer(mockCtl) if err != nil { t.Fatalf("Failed to create mock server: %v", err) } defer fakeServer() s.Log.EXPECT().GetLatestSignedLogRoot( gomock.Any(), gomock.Any()).Return(tt.rootResponse, tt.signedLogError).AnyTimes() got, err := updateRange(tt.args.ctx, s.LogClient, tt.args.r) if (err != nil) != tt.wantErr { t.Errorf("updateRange() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { t.Errorf("updateRange() got = %v, want %v", got, tt.want) } }) } } func TestNewLogRangesWithMock(t *testing.T) { type args struct { ctx context.Context path string treeID uint } tests := []struct { name string args args want LogRanges wantErr bool }{ { name: "empty path", args: args{ ctx: context.Background(), path: "", treeID: 1, }, want: LogRanges{}, wantErr: false, }, { name: "treeID 0", args: args{ ctx: context.Background(), path: "x", treeID: 0, }, want: LogRanges{}, wantErr: true, }, } mockCtl := gomock.NewController(t) defer mockCtl.Finish() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s, fakeServer, err := testonly.NewMockServer(mockCtl) if err != nil { t.Fatalf("Failed to create mock server: %v", err) } defer fakeServer() got, err := NewLogRanges(tt.args.ctx, s.LogClient, tt.args.path, tt.args.treeID) if (err != nil) != tt.wantErr { t.Errorf("NewLogRanges() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { t.Errorf("NewLogRanges() got = %v, want %v", got, tt.want) } }) } } rekor-1.3.5/pkg/sharding/shard_fuzz_test.go000066400000000000000000000047661455727245600210250ustar00rootroot00000000000000// Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sharding import "testing" func FuzzCreateEntryIDFromParts(f *testing.F) { f.Fuzz(func(t *testing.T, treeID, uuid string) { if _, err := CreateEntryIDFromParts(treeID, uuid); err != nil { t.Skipf("failed to create entryID from %v + %v: %v", treeID, uuid, err) } }) } func FuzzGetUUIDFromIDString(f *testing.F) { f.Fuzz(func(t *testing.T, entryID string) { if _, err := GetUUIDFromIDString(entryID); err != nil { t.Skipf("error getting UUID from %v: %v", entryID, err) } }) } func FuzzGetTreeIDFromIDString(f *testing.F) { f.Fuzz(func(t *testing.T, entryID string) { if _, err := GetTreeIDFromIDString(entryID); err != nil { t.Skipf("error getting treeID from %v: %v", entryID, err) } }) } func FuzzPadToTreeIDLen(f *testing.F) { f.Fuzz(func(t *testing.T, treeID string) { if _, err := PadToTreeIDLen(treeID); err != nil { t.Skipf("error padding %v: %v", treeID, err) } }) } func FuzzReturnEntryIDString(f *testing.F) { f.Fuzz(func(t *testing.T, treeID, uuid string) { if _, err := CreateEntryIDFromParts(treeID, uuid); err != nil { t.Skipf("failed to create entryID from %s + %s: %s", treeID, uuid, err) } }) } func FuzzTreeID(f *testing.F) { f.Fuzz(func(t *testing.T, treeID string) { if _, err := TreeID(treeID); err != nil { t.Skipf("error creating treeID %v: %v", treeID, err) } }) } func FuzzValidateUUID(f *testing.F) { f.Fuzz(func(t *testing.T, uuid string) { if err := ValidateUUID(uuid); err != nil { t.Skipf("error validating UUID %v: %v", uuid, err) } }) } func FuzzValidateTreeID(f *testing.F) { f.Fuzz(func(t *testing.T, treeID string) { if err := ValidateTreeID(treeID); err != nil { t.Skipf("error validating treeID %v: %v", treeID, err) } }) } func FuzzValidateEntryID(f *testing.F) { f.Fuzz(func(t *testing.T, entryID string) { if err := ValidateEntryID(entryID); err != nil { t.Skipf("error validating entryID %v: %v", entryID, err) } }) } rekor-1.3.5/pkg/sharding/sharding.go000066400000000000000000000136721455727245600174020ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sharding import ( "encoding/hex" "errors" "fmt" "strconv" ) // An EntryID refers to a specific artifact's ID and is made of two components, // the TreeID and the UUID. The TreeID is a hex-encoded uint64 (8 bytes) // referring to the specific trillian tree (also known as log or shard) where // the artifact can be found. The UUID is a hex-encoded 32-byte number // referring to the artifact's merkle leaf hash from trillian. Artifact lookup // by UUID occurs by finding the UUID within the tree specified by the TreeID. // // An EntryID is 40 bytes long and looks like this: // FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF // |_______ ________| |_____________________________________ ______________________________________| // \/ \/ // TreeID (8 bytes, hex) UUID (32 bytes, hex) const TreeIDHexStringLen = 16 const UUIDHexStringLen = 64 const EntryIDHexStringLen = TreeIDHexStringLen + UUIDHexStringLen type EntryID struct { TreeID string UUID string } // CreateEntryIDFromParts This function can take a TreeID of equal or lesser length than TreeIDHexStringLen. In // case the TreeID length is less than TreeIDHexStringLen, it will be padded to the correct // length. func CreateEntryIDFromParts(treeid string, uuid string) (EntryID, error) { if len(treeid) > TreeIDHexStringLen { err := fmt.Errorf("invalid treeid len: %v", len(treeid)) return createEmptyEntryID(), err } if len(uuid) != UUIDHexStringLen { err := fmt.Errorf("invalid uuid len: %v", len(uuid)) return createEmptyEntryID(), err } treeidFormatted, err := PadToTreeIDLen(treeid) if err != nil { return createEmptyEntryID(), err } if err := ValidateEntryID(treeidFormatted + uuid); err != nil { return createEmptyEntryID(), err } return EntryID{ TreeID: treeidFormatted, UUID: uuid}, nil } func createEmptyEntryID() EntryID { return EntryID{ TreeID: "", UUID: ""} } func (e EntryID) ReturnEntryIDString() string { return e.TreeID + e.UUID } func PadToTreeIDLen(t string) (string, error) { switch { case len(t) == TreeIDHexStringLen: return t, nil case len(t) > TreeIDHexStringLen: return "", fmt.Errorf("invalid treeID %v: too long", t) default: return fmt.Sprintf("%016s", t), nil } } // GetUUIDFromIDString Returns UUID (with no prepended TreeID) from a UUID or EntryID string. // Validates UUID and also TreeID if present. func GetUUIDFromIDString(id string) (string, error) { switch len(id) { case UUIDHexStringLen: if err := ValidateUUID(id); err != nil { return "", err } return id, nil case EntryIDHexStringLen: if err := ValidateEntryID(id); err != nil { if err.Error() == "0 is not a valid TreeID" { return id[len(id)-UUIDHexStringLen:], nil } return "", err } return id[len(id)-UUIDHexStringLen:], nil default: return "", fmt.Errorf("invalid ID len %v for %v", len(id), id) } } // ValidateUUID This is permissive in that if passed an EntryID, it will find the UUID and validate it. func ValidateUUID(u string) error { switch len(u) { // If u is an EntryID, call validate on just the UUID case EntryIDHexStringLen: uid := u[len(u)-UUIDHexStringLen:] if err := ValidateUUID(uid); err != nil { return err } return nil case UUIDHexStringLen: if _, err := hex.DecodeString(u); err != nil { return fmt.Errorf("id %v is not a valid hex string: %v", u, err) } return nil default: return fmt.Errorf("invalid ID len %v for %v", len(u), u) } } // ValidateTreeID This is permissive in that if passed an EntryID, it will find the TreeID and validate it. func ValidateTreeID(t string) error { switch len(t) { // If t is an EntryID, call validate on just the TreeID case EntryIDHexStringLen: tid := t[:TreeIDHexStringLen] err := ValidateTreeID(tid) if err != nil { return err } return nil case TreeIDHexStringLen: // Check that it's a valid int64 in hex (base 16) i, err := strconv.ParseInt(t, 16, 64) if err != nil { return fmt.Errorf("could not convert treeID %v to int64: %v", t, err) } // Check for invalid TreeID values // TODO: test for more of these if i == 0 { return fmt.Errorf("0 is not a valid TreeID") } return nil default: return fmt.Errorf("TreeID len expected to be %v but got %v", TreeIDHexStringLen, len(t)) } } func ValidateEntryID(id string) error { UUIDErr := ValidateUUID(id) if UUIDErr != nil { return UUIDErr } treeIDErr := ValidateTreeID(id) if treeIDErr != nil { return treeIDErr } return nil } var ErrPlainUUID = errors.New("cannot get treeID from plain UUID") // GetTreeIDFromIDString Returns TreeID (with no appended UUID) from a TreeID or EntryID string. // Validates TreeID and also UUID if present. func GetTreeIDFromIDString(id string) (string, error) { switch len(id) { case UUIDHexStringLen: return "", ErrPlainUUID case EntryIDHexStringLen, TreeIDHexStringLen: if err := ValidateEntryID(id); err != nil { return "", err } return id[:TreeIDHexStringLen], nil default: return "", fmt.Errorf("invalid ID len %v for %v", len(id), id) } } func TreeID(entryID string) (int64, error) { tid, err := GetTreeIDFromIDString(entryID) if err != nil { return 0, err } i, err := strconv.ParseInt(tid, 16, 64) if err != nil { return 0, fmt.Errorf("could not convert treeID %v to int64: %w", tid, err) } return i, nil } rekor-1.3.5/pkg/sharding/sharding_test.go000066400000000000000000000222541455727245600204350ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sharding import "testing" // Create some test data // Good data const validTreeID1 = "0FFFFFFFFFFFFFFF" const ( validTreeID2 = "3315648d077a9f02" validTreeID3 = "7241b7903737211c" shortTreeID = "12345" ) const validUUID = "f794467401d57241b7903737211c721cb3315648d077a9f02ceefb6e404a05de" const ( validEntryID1 = validTreeID1 + validUUID validEntryID2 = validTreeID2 + validUUID validEntryID3 = validTreeID3 + validUUID ) var ( validTreeIDs = []string{validTreeID1, validTreeID2, validTreeID3, shortTreeID} validEntryIDs = []string{validEntryID1, validEntryID2, validEntryID3} ) // Bad data // Unknown TreeID const invalidTreeID = "0000000000000000" const invalidEntryID = invalidTreeID + validUUID // Wrong length const tooLongTreeID = validTreeID1 + "e" const tooLongUUID = validUUID + "e" var tooShortUUID = validUUID[:len(validUUID)-1] const tooLongEntryID = validEntryID1 + "e" var tooShortEntryID = validEntryID1[:len(validEntryID1)-1] var ( wrongLengthTreeIDs = []string{tooLongTreeID, validEntryID3, validUUID} wrongLengthUUIDs = []string{tooShortUUID, tooLongUUID, validEntryID3, validTreeID1} wrongLengthEntryandUUIDs = []string{tooLongEntryID, tooShortEntryID, tooLongUUID, tooShortUUID, validTreeID3} ) // Not valid hex const notHexTreeID1 = "ZZZZZZZZZZZZZZZZ" const ( notHexTreeID2 = "FFFFFFF_FFFFFFFF" notHexTreeID3 = "xxFFFFFFFFFFFFFF" ) const ( notHexUUID1 = "94467401d57241b7903737211c721cb3315648d077a9f02ceefb6e404a05dezq" notHexUUID2 = "y794467401d57241b7903737211c721cb3315648d077a9f02ceefb6e404a05de" notHexUUID3 = "f794467401d57241b7903737211c721cbp3315648d077a9f02ceefb6e404a05d" ) const ( notHexEntryID1 = notHexTreeID1 + validUUID notHexEntryID2 = validTreeID2 + notHexUUID1 notHexEntryID3 = notHexTreeID2 + notHexUUID3 ) var ( notHexTreeIDs = []string{notHexTreeID1, notHexTreeID2, notHexTreeID3} notHexUUIDs = []string{notHexUUID1, notHexUUID2, notHexUUID3} notHexEntryandUUIDs = []string{notHexEntryID1, notHexEntryID2, notHexEntryID3, notHexUUID1, notHexUUID2, notHexUUID3} ) // Test functions func TestCreateEntryIDFromParts(t *testing.T) { for _, s := range wrongLengthTreeIDs { if _, err := CreateEntryIDFromParts(s, validUUID); err == nil { t.Errorf("expected length error for wrong TreeID of invalid len: %v", s) } } for _, s := range wrongLengthUUIDs { if _, err := CreateEntryIDFromParts(validTreeID1, s); err == nil { t.Errorf("expected length error for wrong UUID of invalid len: %v", s) } } for _, s := range notHexTreeIDs { if _, err := CreateEntryIDFromParts(s, validUUID); err == nil { t.Errorf("expected hex error for TreeID: %v", s) } } for _, s := range notHexUUIDs { if _, err := CreateEntryIDFromParts(validTreeID3, s); err == nil { t.Errorf("expected hex error for UUID: %v", s) } } for _, tid := range validTreeIDs { entryID, err := CreateEntryIDFromParts(tid, validUUID) if err != nil { t.Errorf("failed to create entryID from %v + %v: %v", tid, validUUID, err) } expectedTid, _ := PadToTreeIDLen(tid) if entryID.TreeID != expectedTid { t.Errorf("created entryID with incorrect treeID: expected %v, got %v", tid, entryID.TreeID) } if entryID.UUID != validUUID { t.Errorf("created entryID with incorrect UUID: expected %v, got %v", validUUID, entryID.UUID) } } } func TestCreateEmptyEntryID(t *testing.T) { emptyEntryID := createEmptyEntryID() if emptyEntryID.TreeID != "" { t.Errorf("expected empty EntryID.TreeID but got %v", emptyEntryID.TreeID) } if emptyEntryID.UUID != "" { t.Errorf("expected empty EntryID.UUID but got %v", emptyEntryID.UUID) } } func TestPadToTreeIDLen(t *testing.T) { short := "12345678" shortPadded := "0000000012345678" medium := "1234567812345678" long := "12345678901234567890" result1, err1 := PadToTreeIDLen(short) if result1 != shortPadded || err1 != nil { t.Errorf("error padding %v: expected (%v, nil), got (%v, %v)", short, shortPadded, result1, err1) } result2, err2 := PadToTreeIDLen(medium) if result2 != medium || err2 != nil { t.Errorf("error padding %v: expected (%v, nil), got (%v, %v)", medium, medium, result2, err2) } result3, err3 := PadToTreeIDLen(long) if result3 != "" || err3 == nil { t.Errorf("expected error in padding %v, but got %v", long, result3) } } func TestReturnEntryIDString(t *testing.T) { entryID, _ := CreateEntryIDFromParts(validTreeID1, validUUID) IDString := entryID.ReturnEntryIDString() if IDString != validEntryID1 { t.Errorf("expected entryID string %v but got %v", validEntryID1, IDString) } } func TestGetUUIDFromIDString(t *testing.T) { for _, s := range wrongLengthEntryandUUIDs { // TODO: check for correct error if _, err := GetUUIDFromIDString(s); err == nil { t.Errorf("expected length error for GetUUIDFromIDString(%v) but no error was found", s) } } for _, s := range notHexEntryandUUIDs { // TODO: check for correct error if _, err := GetUUIDFromIDString(s); err == nil { t.Errorf("expected invalid hex error for GetUUIDFromIDString(%v) but no error was found", s) } } // Return entire UUID res, err := GetUUIDFromIDString(validUUID) if err != nil { t.Errorf("unexpected error for GetUUIDFromIDString(%v): %v", validUUID, err) } if res != validUUID { t.Errorf("expected result %v for GetUUIDFromIDString(%v) but got %v", validUUID, validUUID, res) } // Return UUID from EntryID for _, s := range validEntryIDs { res, err := GetUUIDFromIDString(s) if err != nil { t.Errorf("unexpected error for GetUUIDFromIDString(%v): %v", s, err) } if res != validUUID { t.Errorf("expected result %v for GetUUIDFromIDString(%v) but got %v", validUUID, s, res) } } } func TestGetTreeIDFromIDString(t *testing.T) { valid1, err := GetTreeIDFromIDString(validEntryID1) if valid1 != validTreeID1 || err != nil { t.Errorf("expected TreeID %v with nil error, got: %v with error %v", validTreeID1, valid1, err) } valid2, err := GetTreeIDFromIDString(validEntryID2) if valid2 != validTreeID2 || err != nil { t.Errorf("expected TreeID %v with nil error, got: %v with error %v", validTreeID2, valid2, err) } valid3, err := GetTreeIDFromIDString(validEntryID3) if valid3 != validTreeID3 || err != nil { t.Errorf("expected TreeID %v with nil error, got: %v with error %v", validTreeID3, valid3, err) } // tree IDs of zero should return an error invalid, err := GetTreeIDFromIDString(invalidEntryID) if invalid != "" || err.Error() != "0 is not a valid TreeID" { t.Errorf("expected err 'unknown treeID', got: %v with error %v", invalid, err) } // invalid UUID should also return an error because we test inclusively for // malformed parts of EntryID _, e := GetTreeIDFromIDString(notHexEntryID2) if e == nil { t.Errorf("expected error for invalid UUID, but got none") } // uuid length error uuidHexStringLen := "qlALqZuNP9Iqpg2WVAOkJUntCXQtOOQpOfqox5JbJK4jw5xBs53Wqu1WQ5vTfvqr" _, e = GetTreeIDFromIDString(uuidHexStringLen) if e == nil { t.Errorf("expected error for invalid TreeID, but got none") } invalidStringLength := "FWQfOtwd7I4BcCZ5OU7Hbmmp" _, e = GetTreeIDFromIDString(invalidStringLength) if e == nil { t.Errorf("expected error for invalid TreeID, but got none") } } func TestTreeID(t *testing.T) { type args struct { entryID string } tests := []struct { name string args args want int64 wantErr bool }{ { name: "valid entryID", args: args{ entryID: validEntryID1, }, want: 1152921504606846975, wantErr: false, }, { name: "invalid entryID", args: args{ entryID: invalidEntryID, }, want: 0, wantErr: true, }, { name: "invalid UUID", args: args{ entryID: notHexEntryID2, }, want: 0, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := TreeID(tt.args.entryID) if (err != nil) != tt.wantErr { t.Errorf("TreeID() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { t.Errorf("TreeID() got = %v, want %v", got, tt.want) } }) } } func TestValidateTreeID(t *testing.T) { type args struct { t string } tests := []struct { name string args args wantErr bool }{ { name: "valid treeID", args: args{ t: validTreeID1, }, wantErr: false, }, { name: "invalid treeID", args: args{ t: invalidTreeID, }, wantErr: true, }, { name: "invalid UUID", args: args{ t: notHexTreeID2, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if err := ValidateTreeID(tt.args.t); (err != nil) != tt.wantErr { t.Errorf("ValidateTreeID() error = %v, wantErr %v", err, tt.wantErr) } }) } } rekor-1.3.5/pkg/signer/000077500000000000000000000000001455727245600147335ustar00rootroot00000000000000rekor-1.3.5/pkg/signer/file.go000066400000000000000000000024001455727245600161750ustar00rootroot00000000000000/* Copyright The Rekor 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 signer import ( "crypto" "fmt" "github.com/sigstore/sigstore/pkg/signature" "go.step.sm/crypto/pemutil" ) // returns an file based signer and verify, used for spinning up local instances type File struct { signature.SignerVerifier } func NewFile(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) } signer, err := signature.LoadSignerVerifier(opaqueKey, crypto.SHA256) if err != nil { return nil, fmt.Errorf(`file: loaded private key from %s can't be used to sign: %w`, keyPath, err) } return &File{signer}, nil } rekor-1.3.5/pkg/signer/file_test.go000066400000000000000000000033611455727245600172430ustar00rootroot00000000000000/* Copyright The Rekor 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 signer 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 := NewFile(tc.keyPath, tc.keyPass) if tc.wantErr != (err != nil) { t.Errorf("NewFile() expected %t, got err %s", tc.wantErr, err) } }) } } rekor-1.3.5/pkg/signer/fuzz_test.go000066400000000000000000000017221455727245600173210ustar00rootroot00000000000000/* Copyright The Rekor 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 signer import ( "os" "testing" ) func FuzzNewFile(f *testing.F) { f.Fuzz(func(t *testing.T, keyData []byte, keyPass string) { keyPath, err := os.CreateTemp(t.TempDir(), "keyfile") if err != nil { t.Skip() } defer func() { keyPath.Close() os.Remove(keyPath.Name()) }() _, err = keyPath.Write(keyData) if err != nil { t.Skip() } _, _ = NewFile(keyPath.Name(), keyPass) }) } rekor-1.3.5/pkg/signer/memory.go000066400000000000000000000021031455727245600165660ustar00rootroot00000000000000/* Copyright The Rekor 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 signer import ( "crypto" "crypto/elliptic" "crypto/rand" "github.com/sigstore/sigstore/pkg/signature" ) const MemoryScheme = "memory" // returns an in-memory signer and verify, used for spinning up local instances type Memory struct { signature.ECDSASignerVerifier } func NewMemory() (*Memory, error) { // generate a keypair sv, _, err := signature.NewECDSASignerVerifier(elliptic.P256(), rand.Reader, crypto.SHA256) if err != nil { return nil, err } return &Memory{ ECDSASignerVerifier: *sv, }, nil } rekor-1.3.5/pkg/signer/memory_test.go000066400000000000000000000015321455727245600176320ustar00rootroot00000000000000/* Copyright The Rekor 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 signer import ( "context" "testing" ) func TestMemory(t *testing.T) { ctx := context.Background() m, err := New(ctx, "memory", "") if err != nil { t.Fatalf("new memory: %v", err) } _, err = m.PublicKey() if err != nil { t.Fatalf("unexpected error creating public key") } } rekor-1.3.5/pkg/signer/signer.go000066400000000000000000000026001455727245600165470ustar00rootroot00000000000000/* Copyright 2021 The Sigstore Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package signer import ( "context" "crypto" "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" ) func New(ctx context.Context, signer string, pass string) (signature.Signer, error) { switch { case slices.ContainsFunc(kms.SupportedProviders(), func(s string) bool { return strings.HasPrefix(signer, s) }): return kms.Get(ctx, signer, crypto.SHA256) case signer == MemoryScheme: return NewMemory() default: return NewFile(signer, pass) } } rekor-1.3.5/pkg/storage/000077500000000000000000000000001455727245600151105ustar00rootroot00000000000000rekor-1.3.5/pkg/storage/storage.go000066400000000000000000000043111455727245600171020ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package storage import ( "context" "errors" "fmt" "github.com/sigstore/rekor/pkg/log" "github.com/spf13/viper" "gocloud.dev/blob" // Blank imports to register storage _ "gocloud.dev/blob/fileblob" _ "gocloud.dev/blob/gcsblob" _ "gocloud.dev/blob/memblob" _ "gocloud.dev/blob/s3blob" ) type AttestationStorage interface { StoreAttestation(ctx context.Context, key string, attestation []byte) error FetchAttestation(ctx context.Context, key string) ([]byte, error) } func NewAttestationStorage() (AttestationStorage, error) { if url := viper.GetString("attestation_storage_bucket"); url != "" { log.Logger.Infof("Configuring attestation storage at %s", url) bucket, err := blob.OpenBucket(context.Background(), url) if err != nil { return nil, err } return &Blob{ bucket: bucket, }, nil } return nil, errors.New("no storage configured") } type Blob struct { bucket *blob.Bucket } func (b *Blob) StoreAttestation(ctx context.Context, key string, attestation []byte) error { log.ContextLogger(ctx).Infof("storing attestation at %s", key) w, err := b.bucket.NewWriter(ctx, key, nil) if err != nil { return err } if _, err := w.Write(attestation); err != nil { return err } return w.Close() } func (b *Blob) FetchAttestation(ctx context.Context, key string) ([]byte, error) { log.ContextLogger(ctx).Infof("fetching attestation %s", key) exists, err := b.bucket.Exists(ctx, key) if err != nil { return nil, err } if !exists { return nil, fmt.Errorf("attestation %v does not exist", key) } data, err := b.bucket.ReadAll(ctx, key) if err != nil { return nil, err } return data, nil } rekor-1.3.5/pkg/tle/000077500000000000000000000000001455727245600142305ustar00rootroot00000000000000rekor-1.3.5/pkg/tle/tle.go000066400000000000000000000071761455727245600153560ustar00rootroot00000000000000// Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tle import ( "bytes" "encoding/base64" "encoding/hex" "fmt" "github.com/go-openapi/runtime" rekor_pb_common "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" rekor_pb "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "google.golang.org/protobuf/encoding/protojson" ) // GenerateTransparencyLogEntry returns a sigstore/protobuf-specs compliant message containing a // TransparencyLogEntry as defined at https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_rekor.proto func GenerateTransparencyLogEntry(anon models.LogEntryAnon) (*rekor_pb.TransparencyLogEntry, error) { logIDHash, err := hex.DecodeString(*anon.LogID) if err != nil { return nil, fmt.Errorf("decoding logID string: %w", err) } rootHash, err := hex.DecodeString(*anon.Verification.InclusionProof.RootHash) if err != nil { return nil, fmt.Errorf("decoding inclusion proof root hash: %w", err) } inclusionProofHashes := make([][]byte, len(anon.Verification.InclusionProof.Hashes)) for i, hash := range anon.Verification.InclusionProof.Hashes { hashBytes, err := hex.DecodeString(hash) if err != nil { return nil, fmt.Errorf("decoding inclusion proof hash: %w", err) } inclusionProofHashes[i] = hashBytes } // Different call paths may supply string or []byte. If string, it is base64 encoded. var body []byte switch v := anon.Body.(type) { case string: b, err := base64.StdEncoding.DecodeString(v) if err != nil { return nil, fmt.Errorf("base64 decoding body: %w", err) } body = b case []byte: body = v default: return nil, fmt.Errorf("body is not string or []byte: (%T)%v", v, v) } pe, err := models.UnmarshalProposedEntry(bytes.NewReader(body), runtime.JSONConsumer()) if err != nil { return nil, err } eimpl, err := types.UnmarshalEntry(pe) if err != nil { return nil, err } return &rekor_pb.TransparencyLogEntry{ LogIndex: *anon.LogIndex, // the global log index LogId: &rekor_pb_common.LogId{ KeyId: logIDHash, }, KindVersion: &rekor_pb.KindVersion{ Kind: pe.Kind(), Version: eimpl.APIVersion(), }, IntegratedTime: *anon.IntegratedTime, InclusionPromise: &rekor_pb.InclusionPromise{ SignedEntryTimestamp: anon.Verification.SignedEntryTimestamp, }, InclusionProof: &rekor_pb.InclusionProof{ LogIndex: *anon.Verification.InclusionProof.LogIndex, // relative to the specific tree the entry is found in RootHash: rootHash, TreeSize: *anon.Verification.InclusionProof.TreeSize, Hashes: inclusionProofHashes, Checkpoint: &rekor_pb.Checkpoint{ Envelope: *anon.Verification.InclusionProof.Checkpoint, }, }, CanonicalizedBody: body, // we don't call eimpl.Canonicalize in the case that the logic is different in this caller vs when it was persisted in the log }, nil } // MarshalTLEToJSON marshals a TransparencyLogEntry message to JSON according to the protobuf JSON encoding rules func MarshalTLEToJSON(tle *rekor_pb.TransparencyLogEntry) ([]byte, error) { return protojson.Marshal(tle) } rekor-1.3.5/pkg/tle/tle_test.go000066400000000000000000000526431455727245600164140ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tle import ( "encoding/base64" "encoding/hex" "reflect" "testing" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" rekor_pb_common "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" rekor_pb "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "github.com/sigstore/rekor/pkg/generated/models" _ "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1" ) func TestGenerateTransparencyLogEntry(t *testing.T) { type TestCase struct { description string expectSuccess bool proposedEntry models.LogEntryAnon want *rekor_pb.TransparencyLogEntry } b64Body := "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2MmQwOGYyOGM2OWNhZGE3YjQyYTQ1Nzk0YjQ3ZWU2YzgxYTdkZmE3MTY4NDZiMzljODhmMGFkMTljMjA2OTk3In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJQm14U0N1TW1HSzhNQWRMd1FWZ21TZjVXKzlkdU5iQXN1cUNQNlNucUxCUkFpRUFvNGtGRVdDTG9HcTVUaysrUEhtTEgrb3N1emVTRjN4OTdBbmVicTRlbVRvPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUk1ha05EUVhKVFowRjNTVUpCWjBsVVRWQkRlVXdyYmxOb2MycHdaa2hZYUZkYVRVWkNUVUZIUlVSQlMwSm5aM0ZvYTJwUFVGRlJSRUY2UVhFS1RWSlZkMFYzV1VSV1VWRkxSWGQ0ZW1GWFpIcGtSemw1V2xNMWExcFlXWGhGVkVGUVFtZE9Wa0pCVFZSRFNFNXdXak5PTUdJelNteE5RalJZUkZSSmVRcE5SRWwzVFdwRmQwNUVXWGhOVm05WVJGUkplVTFFU1hkTmFrVjNUbFJaZUUxR2IzZEZla1ZTVFVFNFIwRXhWVVZEYUUxSll6SnNibU16VW5aamJWVjNDbGRVUVZSQ1oyTnhhR3RxVDFCUlNVSkNaMmR4YUd0cVQxQlJUVUpDZDA1RFFVRlVVMVJ2VEhWS2N5OTFSV05IU2tRME5VWmFiVE5wWmxKTU4yOXVRVWNLWlZaNWJuWkhVbmN6WnpKMU0wbFhTREZuU2tSamNERjRSWFI2UVZCUWJYQmhlVGRtTmxCNE1XeFpNa0ZyWnpsMGEyb3dRa1J2UTNkdk5FbENlbXBEUXdwQlkyOTNSR2RaUkZaU01GQkJVVWd2UWtGUlJFRm5aVUZOUWsxSFFURlZaRXBSVVUxTlFXOUhRME56UjBGUlZVWkNkMDFFVFVGM1IwRXhWV1JGZDBWQ0NpOTNVVU5OUVVGM1NGRlpSRlpTTUU5Q1FsbEZSazlSYTNZNVoyMVpXVFpWU0doQ1pWSnJMMWx4VlVsaU1WRldiMDFDT0VkQk1WVmtTWGRSV1UxQ1lVRUtSa1pxUVVoc0sxSlNZVlp0Y1ZoeVRXdExSMVJKZEVGeGVHTllOazFIVFVkQk1WVmtSVkZTWTAxR2NVZFhSMmd3WkVoQ2VrOXBPSFphTW13d1lVaFdhUXBNYlU1MllsTTVjbGx1VGpCTU0xSnNZMjVLYUZwdE9YbGlVekZ5WkZkS2JHTXpVbWhaTW5OMlRHMWtjR1JIYURGWmFUa3pZak5LY2xwdGVIWmtNMDEyQ21KWFJuQmlhVFUxWWxkNFFXTnRWbTFqZVRsdldsZEdhMk41T1hSWldFNHdXbGhKZDBWbldVdExkMWxDUWtGSFJIWjZRVUpCWjFGRlkwaFdlbUZFUVcwS1FtZHZja0puUlVWQldVOHZUVUZGUmtKQ2FISlpiazR3VEROU2JHTnVTbWhhYlRsNVlsTXhjbVJYU214ak0xSm9XVEp6ZDA1bldVdExkMWxDUWtGSFJBcDJla0ZDUVhkUmIxcHFSVEZPVjFGNVdXMUplRTU2V210TlZFVTFXV3BHYlU5VVNUSk9WRlV6V1hwTk5WbDZTbWxQVkdzMVdtcFNhRnBxWjNkWmVrRTFDa0puYjNKQ1owVkZRVmxQTDAxQlJVSkNRM1J2WkVoU2QyTjZiM1pNTTFKMllUSldkVXh0Um1wa1IyeDJZbTVOZFZveWJEQmhTRlpwWkZoT2JHTnRUbllLWW01U2JHSnVVWFZaTWpsMFRVSTRSME5wYzBkQlVWRkNaemM0ZDBGUldVVkZXRXBzV201TmRtRkhWbWhhU0UxMllsZEdlbVJIVm5sTlEwRkhRMmx6UndwQlVWRkNaemM0ZDBGUlVVVkZhMG94WVZkNGEwbEdVbXhqTTFGblZVaFdhV0pIYkhwaFJFRkxRbWRuY1docmFrOVFVVkZFUVhkT2IwRkVRbXhCYWtWQkNtdDJORTFLYUdGRGFFMUJaMHBWVTNWWll6bFBWRWt3WTB0bU9XTnlObU14Y1RreVYyOXFMM1ZsV0RKRFR6Z3JMMDQyU25SM1FVNTRVSElyTjNWNlpGQUtRV3BDYVhwR2NHZEVMelJzWW5aa1NuRnplWE5HYlVSeU1TdFNNSGhKWjI1S1N5c3JaWGROYmtKaVMxQkVMemd3VTNJelFYTTVMMWxxV1U5M05EVjRkUXA2ZVdzOUNpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19" bodyBytes, _ := base64.RawStdEncoding.DecodeString(b64Body) deadbeefBytes, _ := hex.DecodeString("deadbeef") abcdefaaBytes, _ := hex.DecodeString("abcdefaa") testCases := []TestCase{ { description: "valid entry", expectSuccess: true, proposedEntry: models.LogEntryAnon{ Body: "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2MmQwOGYyOGM2OWNhZGE3YjQyYTQ1Nzk0YjQ3ZWU2YzgxYTdkZmE3MTY4NDZiMzljODhmMGFkMTljMjA2OTk3In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJQm14U0N1TW1HSzhNQWRMd1FWZ21TZjVXKzlkdU5iQXN1cUNQNlNucUxCUkFpRUFvNGtGRVdDTG9HcTVUaysrUEhtTEgrb3N1emVTRjN4OTdBbmVicTRlbVRvPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUk1ha05EUVhKVFowRjNTVUpCWjBsVVRWQkRlVXdyYmxOb2MycHdaa2hZYUZkYVRVWkNUVUZIUlVSQlMwSm5aM0ZvYTJwUFVGRlJSRUY2UVhFS1RWSlZkMFYzV1VSV1VWRkxSWGQ0ZW1GWFpIcGtSemw1V2xNMWExcFlXWGhGVkVGUVFtZE9Wa0pCVFZSRFNFNXdXak5PTUdJelNteE5RalJZUkZSSmVRcE5SRWwzVFdwRmQwNUVXWGhOVm05WVJGUkplVTFFU1hkTmFrVjNUbFJaZUUxR2IzZEZla1ZTVFVFNFIwRXhWVVZEYUUxSll6SnNibU16VW5aamJWVjNDbGRVUVZSQ1oyTnhhR3RxVDFCUlNVSkNaMmR4YUd0cVQxQlJUVUpDZDA1RFFVRlVVMVJ2VEhWS2N5OTFSV05IU2tRME5VWmFiVE5wWmxKTU4yOXVRVWNLWlZaNWJuWkhVbmN6WnpKMU0wbFhTREZuU2tSamNERjRSWFI2UVZCUWJYQmhlVGRtTmxCNE1XeFpNa0ZyWnpsMGEyb3dRa1J2UTNkdk5FbENlbXBEUXdwQlkyOTNSR2RaUkZaU01GQkJVVWd2UWtGUlJFRm5aVUZOUWsxSFFURlZaRXBSVVUxTlFXOUhRME56UjBGUlZVWkNkMDFFVFVGM1IwRXhWV1JGZDBWQ0NpOTNVVU5OUVVGM1NGRlpSRlpTTUU5Q1FsbEZSazlSYTNZNVoyMVpXVFpWU0doQ1pWSnJMMWx4VlVsaU1WRldiMDFDT0VkQk1WVmtTWGRSV1UxQ1lVRUtSa1pxUVVoc0sxSlNZVlp0Y1ZoeVRXdExSMVJKZEVGeGVHTllOazFIVFVkQk1WVmtSVkZTWTAxR2NVZFhSMmd3WkVoQ2VrOXBPSFphTW13d1lVaFdhUXBNYlU1MllsTTVjbGx1VGpCTU0xSnNZMjVLYUZwdE9YbGlVekZ5WkZkS2JHTXpVbWhaTW5OMlRHMWtjR1JIYURGWmFUa3pZak5LY2xwdGVIWmtNMDEyQ21KWFJuQmlhVFUxWWxkNFFXTnRWbTFqZVRsdldsZEdhMk41T1hSWldFNHdXbGhKZDBWbldVdExkMWxDUWtGSFJIWjZRVUpCWjFGRlkwaFdlbUZFUVcwS1FtZHZja0puUlVWQldVOHZUVUZGUmtKQ2FISlpiazR3VEROU2JHTnVTbWhhYlRsNVlsTXhjbVJYU214ak0xSm9XVEp6ZDA1bldVdExkMWxDUWtGSFJBcDJla0ZDUVhkUmIxcHFSVEZPVjFGNVdXMUplRTU2V210TlZFVTFXV3BHYlU5VVNUSk9WRlV6V1hwTk5WbDZTbWxQVkdzMVdtcFNhRnBxWjNkWmVrRTFDa0puYjNKQ1owVkZRVmxQTDAxQlJVSkNRM1J2WkVoU2QyTjZiM1pNTTFKMllUSldkVXh0Um1wa1IyeDJZbTVOZFZveWJEQmhTRlpwWkZoT2JHTnRUbllLWW01U2JHSnVVWFZaTWpsMFRVSTRSME5wYzBkQlVWRkNaemM0ZDBGUldVVkZXRXBzV201TmRtRkhWbWhhU0UxMllsZEdlbVJIVm5sTlEwRkhRMmx6UndwQlVWRkNaemM0ZDBGUlVVVkZhMG94WVZkNGEwbEdVbXhqTTFGblZVaFdhV0pIYkhwaFJFRkxRbWRuY1docmFrOVFVVkZFUVhkT2IwRkVRbXhCYWtWQkNtdDJORTFLYUdGRGFFMUJaMHBWVTNWWll6bFBWRWt3WTB0bU9XTnlObU14Y1RreVYyOXFMM1ZsV0RKRFR6Z3JMMDQyU25SM1FVNTRVSElyTjNWNlpGQUtRV3BDYVhwR2NHZEVMelJzWW5aa1NuRnplWE5HYlVSeU1TdFNNSGhKWjI1S1N5c3JaWGROYmtKaVMxQkVMemd3VTNJelFYTTVMMWxxV1U5M05EVjRkUXA2ZVdzOUNpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19", IntegratedTime: swag.Int64(123), LogID: swag.String("deadbeef"), LogIndex: swag.Int64(2), Verification: &models.LogEntryAnonVerification{ InclusionProof: &models.InclusionProof{ Checkpoint: swag.String("checkpoint"), Hashes: []string{"deadbeef", "abcdefaa"}, LogIndex: swag.Int64(1), RootHash: swag.String("abcdefaa"), TreeSize: swag.Int64(2), }, SignedEntryTimestamp: strfmt.Base64("set"), }, }, want: &rekor_pb.TransparencyLogEntry{ LogIndex: 2, LogId: &rekor_pb_common.LogId{ KeyId: deadbeefBytes, }, KindVersion: &rekor_pb.KindVersion{ Kind: "hashedrekord", Version: "0.0.1", }, IntegratedTime: 123, InclusionPromise: &rekor_pb.InclusionPromise{ SignedEntryTimestamp: []byte("set"), }, InclusionProof: &rekor_pb.InclusionProof{ Checkpoint: &rekor_pb.Checkpoint{ Envelope: "checkpoint", }, Hashes: [][]byte{deadbeefBytes, abcdefaaBytes}, LogIndex: 1, RootHash: abcdefaaBytes, TreeSize: 2, }, CanonicalizedBody: bodyBytes, }, }, { description: "body is not valid base64", expectSuccess: false, proposedEntry: models.LogEntryAnon{ Body: "not_base_64", IntegratedTime: swag.Int64(123), LogID: swag.String("deadbeef"), LogIndex: swag.Int64(1), Verification: &models.LogEntryAnonVerification{ InclusionProof: &models.InclusionProof{ Checkpoint: swag.String("checkpoint"), Hashes: []string{"deadbeef", "abcdefaa"}, LogIndex: swag.Int64(1), RootHash: swag.String("abcdefaa"), TreeSize: swag.Int64(2), }, SignedEntryTimestamp: strfmt.Base64("set"), }, }, }, { description: "logID is not valid hex", expectSuccess: false, proposedEntry: models.LogEntryAnon{ Body: "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2MmQwOGYyOGM2OWNhZGE3YjQyYTQ1Nzk0YjQ3ZWU2YzgxYTdkZmE3MTY4NDZiMzljODhmMGFkMTljMjA2OTk3In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJQm14U0N1TW1HSzhNQWRMd1FWZ21TZjVXKzlkdU5iQXN1cUNQNlNucUxCUkFpRUFvNGtGRVdDTG9HcTVUaysrUEhtTEgrb3N1emVTRjN4OTdBbmVicTRlbVRvPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUk1ha05EUVhKVFowRjNTVUpCWjBsVVRWQkRlVXdyYmxOb2MycHdaa2hZYUZkYVRVWkNUVUZIUlVSQlMwSm5aM0ZvYTJwUFVGRlJSRUY2UVhFS1RWSlZkMFYzV1VSV1VWRkxSWGQ0ZW1GWFpIcGtSemw1V2xNMWExcFlXWGhGVkVGUVFtZE9Wa0pCVFZSRFNFNXdXak5PTUdJelNteE5RalJZUkZSSmVRcE5SRWwzVFdwRmQwNUVXWGhOVm05WVJGUkplVTFFU1hkTmFrVjNUbFJaZUUxR2IzZEZla1ZTVFVFNFIwRXhWVVZEYUUxSll6SnNibU16VW5aamJWVjNDbGRVUVZSQ1oyTnhhR3RxVDFCUlNVSkNaMmR4YUd0cVQxQlJUVUpDZDA1RFFVRlVVMVJ2VEhWS2N5OTFSV05IU2tRME5VWmFiVE5wWmxKTU4yOXVRVWNLWlZaNWJuWkhVbmN6WnpKMU0wbFhTREZuU2tSamNERjRSWFI2UVZCUWJYQmhlVGRtTmxCNE1XeFpNa0ZyWnpsMGEyb3dRa1J2UTNkdk5FbENlbXBEUXdwQlkyOTNSR2RaUkZaU01GQkJVVWd2UWtGUlJFRm5aVUZOUWsxSFFURlZaRXBSVVUxTlFXOUhRME56UjBGUlZVWkNkMDFFVFVGM1IwRXhWV1JGZDBWQ0NpOTNVVU5OUVVGM1NGRlpSRlpTTUU5Q1FsbEZSazlSYTNZNVoyMVpXVFpWU0doQ1pWSnJMMWx4VlVsaU1WRldiMDFDT0VkQk1WVmtTWGRSV1UxQ1lVRUtSa1pxUVVoc0sxSlNZVlp0Y1ZoeVRXdExSMVJKZEVGeGVHTllOazFIVFVkQk1WVmtSVkZTWTAxR2NVZFhSMmd3WkVoQ2VrOXBPSFphTW13d1lVaFdhUXBNYlU1MllsTTVjbGx1VGpCTU0xSnNZMjVLYUZwdE9YbGlVekZ5WkZkS2JHTXpVbWhaTW5OMlRHMWtjR1JIYURGWmFUa3pZak5LY2xwdGVIWmtNMDEyQ21KWFJuQmlhVFUxWWxkNFFXTnRWbTFqZVRsdldsZEdhMk41T1hSWldFNHdXbGhKZDBWbldVdExkMWxDUWtGSFJIWjZRVUpCWjFGRlkwaFdlbUZFUVcwS1FtZHZja0puUlVWQldVOHZUVUZGUmtKQ2FISlpiazR3VEROU2JHTnVTbWhhYlRsNVlsTXhjbVJYU214ak0xSm9XVEp6ZDA1bldVdExkMWxDUWtGSFJBcDJla0ZDUVhkUmIxcHFSVEZPVjFGNVdXMUplRTU2V210TlZFVTFXV3BHYlU5VVNUSk9WRlV6V1hwTk5WbDZTbWxQVkdzMVdtcFNhRnBxWjNkWmVrRTFDa0puYjNKQ1owVkZRVmxQTDAxQlJVSkNRM1J2WkVoU2QyTjZiM1pNTTFKMllUSldkVXh0Um1wa1IyeDJZbTVOZFZveWJEQmhTRlpwWkZoT2JHTnRUbllLWW01U2JHSnVVWFZaTWpsMFRVSTRSME5wYzBkQlVWRkNaemM0ZDBGUldVVkZXRXBzV201TmRtRkhWbWhhU0UxMllsZEdlbVJIVm5sTlEwRkhRMmx6UndwQlVWRkNaemM0ZDBGUlVVVkZhMG94WVZkNGEwbEdVbXhqTTFGblZVaFdhV0pIYkhwaFJFRkxRbWRuY1docmFrOVFVVkZFUVhkT2IwRkVRbXhCYWtWQkNtdDJORTFLYUdGRGFFMUJaMHBWVTNWWll6bFBWRWt3WTB0bU9XTnlObU14Y1RreVYyOXFMM1ZsV0RKRFR6Z3JMMDQyU25SM1FVNTRVSElyTjNWNlpGQUtRV3BDYVhwR2NHZEVMelJzWW5aa1NuRnplWE5HYlVSeU1TdFNNSGhKWjI1S1N5c3JaWGROYmtKaVMxQkVMemd3VTNJelFYTTVMMWxxV1U5M05EVjRkUXA2ZVdzOUNpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19", IntegratedTime: swag.Int64(123), LogID: swag.String("not_valid_hex"), LogIndex: swag.Int64(1), Verification: &models.LogEntryAnonVerification{ InclusionProof: &models.InclusionProof{ Checkpoint: swag.String("checkpoint"), Hashes: []string{"deadbeef", "abcdefaa"}, LogIndex: swag.Int64(1), RootHash: swag.String("abcdefaa"), TreeSize: swag.Int64(2), }, SignedEntryTimestamp: strfmt.Base64("set"), }, }, }, { description: "rootHash is not valid hex", expectSuccess: false, proposedEntry: models.LogEntryAnon{ Body: "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2MmQwOGYyOGM2OWNhZGE3YjQyYTQ1Nzk0YjQ3ZWU2YzgxYTdkZmE3MTY4NDZiMzljODhmMGFkMTljMjA2OTk3In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJQm14U0N1TW1HSzhNQWRMd1FWZ21TZjVXKzlkdU5iQXN1cUNQNlNucUxCUkFpRUFvNGtGRVdDTG9HcTVUaysrUEhtTEgrb3N1emVTRjN4OTdBbmVicTRlbVRvPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUk1ha05EUVhKVFowRjNTVUpCWjBsVVRWQkRlVXdyYmxOb2MycHdaa2hZYUZkYVRVWkNUVUZIUlVSQlMwSm5aM0ZvYTJwUFVGRlJSRUY2UVhFS1RWSlZkMFYzV1VSV1VWRkxSWGQ0ZW1GWFpIcGtSemw1V2xNMWExcFlXWGhGVkVGUVFtZE9Wa0pCVFZSRFNFNXdXak5PTUdJelNteE5RalJZUkZSSmVRcE5SRWwzVFdwRmQwNUVXWGhOVm05WVJGUkplVTFFU1hkTmFrVjNUbFJaZUUxR2IzZEZla1ZTVFVFNFIwRXhWVVZEYUUxSll6SnNibU16VW5aamJWVjNDbGRVUVZSQ1oyTnhhR3RxVDFCUlNVSkNaMmR4YUd0cVQxQlJUVUpDZDA1RFFVRlVVMVJ2VEhWS2N5OTFSV05IU2tRME5VWmFiVE5wWmxKTU4yOXVRVWNLWlZaNWJuWkhVbmN6WnpKMU0wbFhTREZuU2tSamNERjRSWFI2UVZCUWJYQmhlVGRtTmxCNE1XeFpNa0ZyWnpsMGEyb3dRa1J2UTNkdk5FbENlbXBEUXdwQlkyOTNSR2RaUkZaU01GQkJVVWd2UWtGUlJFRm5aVUZOUWsxSFFURlZaRXBSVVUxTlFXOUhRME56UjBGUlZVWkNkMDFFVFVGM1IwRXhWV1JGZDBWQ0NpOTNVVU5OUVVGM1NGRlpSRlpTTUU5Q1FsbEZSazlSYTNZNVoyMVpXVFpWU0doQ1pWSnJMMWx4VlVsaU1WRldiMDFDT0VkQk1WVmtTWGRSV1UxQ1lVRUtSa1pxUVVoc0sxSlNZVlp0Y1ZoeVRXdExSMVJKZEVGeGVHTllOazFIVFVkQk1WVmtSVkZTWTAxR2NVZFhSMmd3WkVoQ2VrOXBPSFphTW13d1lVaFdhUXBNYlU1MllsTTVjbGx1VGpCTU0xSnNZMjVLYUZwdE9YbGlVekZ5WkZkS2JHTXpVbWhaTW5OMlRHMWtjR1JIYURGWmFUa3pZak5LY2xwdGVIWmtNMDEyQ21KWFJuQmlhVFUxWWxkNFFXTnRWbTFqZVRsdldsZEdhMk41T1hSWldFNHdXbGhKZDBWbldVdExkMWxDUWtGSFJIWjZRVUpCWjFGRlkwaFdlbUZFUVcwS1FtZHZja0puUlVWQldVOHZUVUZGUmtKQ2FISlpiazR3VEROU2JHTnVTbWhhYlRsNVlsTXhjbVJYU214ak0xSm9XVEp6ZDA1bldVdExkMWxDUWtGSFJBcDJla0ZDUVhkUmIxcHFSVEZPVjFGNVdXMUplRTU2V210TlZFVTFXV3BHYlU5VVNUSk9WRlV6V1hwTk5WbDZTbWxQVkdzMVdtcFNhRnBxWjNkWmVrRTFDa0puYjNKQ1owVkZRVmxQTDAxQlJVSkNRM1J2WkVoU2QyTjZiM1pNTTFKMllUSldkVXh0Um1wa1IyeDJZbTVOZFZveWJEQmhTRlpwWkZoT2JHTnRUbllLWW01U2JHSnVVWFZaTWpsMFRVSTRSME5wYzBkQlVWRkNaemM0ZDBGUldVVkZXRXBzV201TmRtRkhWbWhhU0UxMllsZEdlbVJIVm5sTlEwRkhRMmx6UndwQlVWRkNaemM0ZDBGUlVVVkZhMG94WVZkNGEwbEdVbXhqTTFGblZVaFdhV0pIYkhwaFJFRkxRbWRuY1docmFrOVFVVkZFUVhkT2IwRkVRbXhCYWtWQkNtdDJORTFLYUdGRGFFMUJaMHBWVTNWWll6bFBWRWt3WTB0bU9XTnlObU14Y1RreVYyOXFMM1ZsV0RKRFR6Z3JMMDQyU25SM1FVNTRVSElyTjNWNlpGQUtRV3BDYVhwR2NHZEVMelJzWW5aa1NuRnplWE5HYlVSeU1TdFNNSGhKWjI1S1N5c3JaWGROYmtKaVMxQkVMemd3VTNJelFYTTVMMWxxV1U5M05EVjRkUXA2ZVdzOUNpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19", IntegratedTime: swag.Int64(123), LogID: swag.String("deadbeef"), LogIndex: swag.Int64(1), Verification: &models.LogEntryAnonVerification{ InclusionProof: &models.InclusionProof{ Checkpoint: swag.String("checkpoint"), Hashes: []string{"deadbeef", "abcdefaa"}, LogIndex: swag.Int64(1), RootHash: swag.String("not_hex_string"), TreeSize: swag.Int64(2), }, SignedEntryTimestamp: strfmt.Base64("set"), }, }, }, { description: "one inclusion proof hash is not valid hex", expectSuccess: false, proposedEntry: models.LogEntryAnon{ Body: "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI2MmQwOGYyOGM2OWNhZGE3YjQyYTQ1Nzk0YjQ3ZWU2YzgxYTdkZmE3MTY4NDZiMzljODhmMGFkMTljMjA2OTk3In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJQm14U0N1TW1HSzhNQWRMd1FWZ21TZjVXKzlkdU5iQXN1cUNQNlNucUxCUkFpRUFvNGtGRVdDTG9HcTVUaysrUEhtTEgrb3N1emVTRjN4OTdBbmVicTRlbVRvPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUk1ha05EUVhKVFowRjNTVUpCWjBsVVRWQkRlVXdyYmxOb2MycHdaa2hZYUZkYVRVWkNUVUZIUlVSQlMwSm5aM0ZvYTJwUFVGRlJSRUY2UVhFS1RWSlZkMFYzV1VSV1VWRkxSWGQ0ZW1GWFpIcGtSemw1V2xNMWExcFlXWGhGVkVGUVFtZE9Wa0pCVFZSRFNFNXdXak5PTUdJelNteE5RalJZUkZSSmVRcE5SRWwzVFdwRmQwNUVXWGhOVm05WVJGUkplVTFFU1hkTmFrVjNUbFJaZUUxR2IzZEZla1ZTVFVFNFIwRXhWVVZEYUUxSll6SnNibU16VW5aamJWVjNDbGRVUVZSQ1oyTnhhR3RxVDFCUlNVSkNaMmR4YUd0cVQxQlJUVUpDZDA1RFFVRlVVMVJ2VEhWS2N5OTFSV05IU2tRME5VWmFiVE5wWmxKTU4yOXVRVWNLWlZaNWJuWkhVbmN6WnpKMU0wbFhTREZuU2tSamNERjRSWFI2UVZCUWJYQmhlVGRtTmxCNE1XeFpNa0ZyWnpsMGEyb3dRa1J2UTNkdk5FbENlbXBEUXdwQlkyOTNSR2RaUkZaU01GQkJVVWd2UWtGUlJFRm5aVUZOUWsxSFFURlZaRXBSVVUxTlFXOUhRME56UjBGUlZVWkNkMDFFVFVGM1IwRXhWV1JGZDBWQ0NpOTNVVU5OUVVGM1NGRlpSRlpTTUU5Q1FsbEZSazlSYTNZNVoyMVpXVFpWU0doQ1pWSnJMMWx4VlVsaU1WRldiMDFDT0VkQk1WVmtTWGRSV1UxQ1lVRUtSa1pxUVVoc0sxSlNZVlp0Y1ZoeVRXdExSMVJKZEVGeGVHTllOazFIVFVkQk1WVmtSVkZTWTAxR2NVZFhSMmd3WkVoQ2VrOXBPSFphTW13d1lVaFdhUXBNYlU1MllsTTVjbGx1VGpCTU0xSnNZMjVLYUZwdE9YbGlVekZ5WkZkS2JHTXpVbWhaTW5OMlRHMWtjR1JIYURGWmFUa3pZak5LY2xwdGVIWmtNMDEyQ21KWFJuQmlhVFUxWWxkNFFXTnRWbTFqZVRsdldsZEdhMk41T1hSWldFNHdXbGhKZDBWbldVdExkMWxDUWtGSFJIWjZRVUpCWjFGRlkwaFdlbUZFUVcwS1FtZHZja0puUlVWQldVOHZUVUZGUmtKQ2FISlpiazR3VEROU2JHTnVTbWhhYlRsNVlsTXhjbVJYU214ak0xSm9XVEp6ZDA1bldVdExkMWxDUWtGSFJBcDJla0ZDUVhkUmIxcHFSVEZPVjFGNVdXMUplRTU2V210TlZFVTFXV3BHYlU5VVNUSk9WRlV6V1hwTk5WbDZTbWxQVkdzMVdtcFNhRnBxWjNkWmVrRTFDa0puYjNKQ1owVkZRVmxQTDAxQlJVSkNRM1J2WkVoU2QyTjZiM1pNTTFKMllUSldkVXh0Um1wa1IyeDJZbTVOZFZveWJEQmhTRlpwWkZoT2JHTnRUbllLWW01U2JHSnVVWFZaTWpsMFRVSTRSME5wYzBkQlVWRkNaemM0ZDBGUldVVkZXRXBzV201TmRtRkhWbWhhU0UxMllsZEdlbVJIVm5sTlEwRkhRMmx6UndwQlVWRkNaemM0ZDBGUlVVVkZhMG94WVZkNGEwbEdVbXhqTTFGblZVaFdhV0pIYkhwaFJFRkxRbWRuY1docmFrOVFVVkZFUVhkT2IwRkVRbXhCYWtWQkNtdDJORTFLYUdGRGFFMUJaMHBWVTNWWll6bFBWRWt3WTB0bU9XTnlObU14Y1RreVYyOXFMM1ZsV0RKRFR6Z3JMMDQyU25SM1FVNTRVSElyTjNWNlpGQUtRV3BDYVhwR2NHZEVMelJzWW5aa1NuRnplWE5HYlVSeU1TdFNNSGhKWjI1S1N5c3JaWGROYmtKaVMxQkVMemd3VTNJelFYTTVMMWxxV1U5M05EVjRkUXA2ZVdzOUNpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9fX19", IntegratedTime: swag.Int64(123), LogID: swag.String("deadbeef"), LogIndex: swag.Int64(1), Verification: &models.LogEntryAnonVerification{ InclusionProof: &models.InclusionProof{ Checkpoint: swag.String("checkpoint"), Hashes: []string{"invalid_hex", "abcdefaa"}, LogIndex: swag.Int64(1), RootHash: swag.String("abcdefaa"), TreeSize: swag.Int64(2), }, SignedEntryTimestamp: strfmt.Base64("set"), }, }, }, { description: "body is valid base64 but not valid entry", expectSuccess: false, proposedEntry: models.LogEntryAnon{ Body: "aW52YWxpZF9lbnRyeQo=", // "invalid_entry" IntegratedTime: swag.Int64(123), LogID: swag.String("deadbeef"), LogIndex: swag.Int64(1), Verification: &models.LogEntryAnonVerification{ InclusionProof: &models.InclusionProof{ Checkpoint: swag.String("checkpoint"), Hashes: []string{"deadbeef", "abcdefaa"}, LogIndex: swag.Int64(1), RootHash: swag.String("abcdefaa"), TreeSize: swag.Int64(2), }, SignedEntryTimestamp: strfmt.Base64("set"), }, }, }, { description: "kind/version are valid but spec is not schema valid", expectSuccess: false, proposedEntry: models.LogEntryAnon{ Body: "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7ImhhcyI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjYyZDA4ZjI4YzY5Y2FkYTdiNDJhNDU3OTRiNDdlZTZjODFhN2RmYTcxNjg0NmIzOWM4OGYwYWQxOWMyMDY5OTcifX0sInNpZ25hdHVyZSI6eyJjb250ZW50IjoiTUVVQ0lCbXhTQ3VNbUdLOE1BZEx3UVZnbVNmNVcrOWR1TmJBc3VxQ1A2U25xTEJSQWlFQW80a0ZFV0NMb0dxNVRrKytQSG1MSCtvc3V6ZVNGM3g5N0FuZWJxNGVtVG89IiwicHVibGljS2V5Ijp7ImNvbnRlbnQiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VSTWFrTkRRWEpUWjBGM1NVSkJaMGxVVFZCRGVVd3JibE5vYzJwd1praFlhRmRhVFVaQ1RVRkhSVVJCUzBKblozRm9hMnBQVUZGUlJFRjZRWEVLVFZKVmQwVjNXVVJXVVZGTFJYZDRlbUZYWkhwa1J6bDVXbE0xYTFwWVdYaEZWRUZRUW1kT1ZrSkJUVlJEU0U1d1dqTk9NR0l6U214TlFqUllSRlJKZVFwTlJFbDNUV3BGZDA1RVdYaE5WbTlZUkZSSmVVMUVTWGROYWtWM1RsUlplRTFHYjNkRmVrVlNUVUU0UjBFeFZVVkRhRTFKWXpKc2JtTXpVblpqYlZWM0NsZFVRVlJDWjJOeGFHdHFUMUJSU1VKQ1oyZHhhR3RxVDFCUlRVSkNkMDVEUVVGVVUxUnZUSFZLY3k5MVJXTkhTa1EwTlVaYWJUTnBabEpNTjI5dVFVY0taVlo1Ym5aSFVuY3paekoxTTBsWFNERm5Ta1JqY0RGNFJYUjZRVkJRYlhCaGVUZG1ObEI0TVd4Wk1rRnJaemwwYTJvd1FrUnZRM2R2TkVsQ2VtcERRd3BCWTI5M1JHZFpSRlpTTUZCQlVVZ3ZRa0ZSUkVGblpVRk5RazFIUVRGVlpFcFJVVTFOUVc5SFEwTnpSMEZSVlVaQ2QwMUVUVUYzUjBFeFZXUkZkMFZDQ2k5M1VVTk5RVUYzU0ZGWlJGWlNNRTlDUWxsRlJrOVJhM1k1WjIxWldUWlZTR2hDWlZKckwxbHhWVWxpTVZGV2IwMUNPRWRCTVZWa1NYZFJXVTFDWVVFS1JrWnFRVWhzSzFKU1lWWnRjVmh5VFd0TFIxUkpkRUZ4ZUdOWU5rMUhUVWRCTVZWa1JWRlNZMDFHY1VkWFIyZ3daRWhDZWs5cE9IWmFNbXd3WVVoV2FRcE1iVTUyWWxNNWNsbHVUakJNTTFKc1kyNUthRnB0T1hsaVV6RnlaRmRLYkdNelVtaFpNbk4yVEcxa2NHUkhhREZaYVRrellqTktjbHB0ZUhaa00wMTJDbUpYUm5CaWFUVTFZbGQ0UVdOdFZtMWplVGx2V2xkR2EyTjVPWFJaV0U0d1dsaEpkMFZuV1V0TGQxbENRa0ZIUkhaNlFVSkJaMUZGWTBoV2VtRkVRVzBLUW1kdmNrSm5SVVZCV1U4dlRVRkZSa0pDYUhKWmJrNHdURE5TYkdOdVNtaGFiVGw1WWxNeGNtUlhTbXhqTTFKb1dUSnpkMDVuV1V0TGQxbENRa0ZIUkFwMmVrRkNRWGRSYjFwcVJURk9WMUY1V1cxSmVFNTZXbXROVkVVMVdXcEdiVTlVU1RKT1ZGVXpXWHBOTlZsNlNtbFBWR3MxV21wU2FGcHFaM2RaZWtFMUNrSm5iM0pDWjBWRlFWbFBMMDFCUlVKQ1EzUnZaRWhTZDJONmIzWk1NMUoyWVRKV2RVeHRSbXBrUjJ4MlltNU5kVm95YkRCaFNGWnBaRmhPYkdOdFRuWUtZbTVTYkdKdVVYVlpNamwwVFVJNFIwTnBjMGRCVVZGQ1p6YzRkMEZSV1VWRldFcHNXbTVOZG1GSFZtaGFTRTEyWWxkR2VtUkhWbmxOUTBGSFEybHpSd3BCVVZGQ1p6YzRkMEZSVVVWRmEwb3hZVmQ0YTBsR1VteGpNMUZuVlVoV2FXSkhiSHBoUkVGTFFtZG5jV2hyYWs5UVVWRkVRWGRPYjBGRVFteEJha1ZCQ210Mk5FMUthR0ZEYUUxQlowcFZVM1ZaWXpsUFZFa3dZMHRtT1dOeU5tTXhjVGt5VjI5cUwzVmxXREpEVHpnckwwNDJTblIzUVU1NFVISXJOM1Y2WkZBS1FXcENhWHBHY0dkRUx6UnNZblprU25GemVYTkdiVVJ5TVN0U01IaEpaMjVLU3lzclpYZE5ia0ppUzFCRUx6Z3dVM0l6UVhNNUwxbHFXVTkzTkRWNGRRcDZlV3M5Q2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn19fX0K", IntegratedTime: swag.Int64(123), LogID: swag.String("deadbeef"), LogIndex: swag.Int64(1), Verification: &models.LogEntryAnonVerification{ InclusionProof: &models.InclusionProof{ Checkpoint: swag.String("checkpoint"), Hashes: []string{"deadbeef", "abcdefaa"}, LogIndex: swag.Int64(1), RootHash: swag.String("abcdefaa"), TreeSize: swag.Int64(2), }, SignedEntryTimestamp: strfmt.Base64("set"), }, }, }, } for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { e, err := GenerateTransparencyLogEntry(tc.proposedEntry) if (err == nil) != tc.expectSuccess { t.Fatalf("unexpected result: %v", err) } if tc.expectSuccess && !reflect.DeepEqual(e, tc.want) { t.Errorf("unexpected value returned; got %v, wanted %v", e, tc.want) } }) } } rekor-1.3.5/pkg/trillianclient/000077500000000000000000000000001455727245600164615ustar00rootroot00000000000000rekor-1.3.5/pkg/trillianclient/trillian_client.go000066400000000000000000000245321455727245600221720ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package trillianclient import ( "context" "encoding/hex" "fmt" "time" "github.com/sigstore/rekor/pkg/log" "github.com/transparency-dev/merkle/proof" "github.com/transparency-dev/merkle/rfc6962" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/durationpb" "github.com/google/trillian" "github.com/google/trillian/client" "github.com/google/trillian/types" ) // TrillianClient provides a wrapper around the Trillian client type TrillianClient struct { client trillian.TrillianLogClient logID int64 context context.Context } // NewTrillianClient creates a TrillianClient with the given Trillian client and log/tree ID. func NewTrillianClient(ctx context.Context, logClient trillian.TrillianLogClient, logID int64) TrillianClient { return TrillianClient{ client: logClient, logID: logID, context: ctx, } } // Response includes a status code, an optional error message, and one of the results based on the API call type Response struct { // Status is the status code of the response Status codes.Code // Error contains an error on request or client failure Err error // GetAddResult contains the response from queueing a leaf in Trillian GetAddResult *trillian.QueueLeafResponse // GetLeafAndProofResult contains the response for fetching an inclusion proof and leaf GetLeafAndProofResult *trillian.GetEntryAndProofResponse // GetLatestResult contains the response for the latest checkpoint GetLatestResult *trillian.GetLatestSignedLogRootResponse // GetConsistencyProofResult contains the response for a consistency proof between two log sizes GetConsistencyProofResult *trillian.GetConsistencyProofResponse // getProofResult contains the response for an inclusion proof fetched by leaf hash getProofResult *trillian.GetInclusionProofByHashResponse } func unmarshalLogRoot(logRoot []byte) (types.LogRootV1, error) { var root types.LogRootV1 if err := root.UnmarshalBinary(logRoot); err != nil { return types.LogRootV1{}, err } return root, nil } func (t *TrillianClient) root() (types.LogRootV1, error) { rqst := &trillian.GetLatestSignedLogRootRequest{ LogId: t.logID, } resp, err := t.client.GetLatestSignedLogRoot(t.context, rqst) if err != nil { return types.LogRootV1{}, err } return unmarshalLogRoot(resp.SignedLogRoot.LogRoot) } func (t *TrillianClient) AddLeaf(byteValue []byte) *Response { leaf := &trillian.LogLeaf{ LeafValue: byteValue, } rqst := &trillian.QueueLeafRequest{ LogId: t.logID, Leaf: leaf, } resp, err := t.client.QueueLeaf(t.context, rqst) // check for error if err != nil || (resp.QueuedLeaf.Status != nil && resp.QueuedLeaf.Status.Code != int32(codes.OK)) { return &Response{ Status: status.Code(err), Err: err, GetAddResult: resp, } } root, err := t.root() if err != nil { return &Response{ Status: status.Code(err), Err: err, GetAddResult: resp, } } v := client.NewLogVerifier(rfc6962.DefaultHasher) logClient := client.New(t.logID, t.client, v, root) waitForInclusion := func(ctx context.Context, leafHash []byte) *Response { if logClient.MinMergeDelay > 0 { select { case <-ctx.Done(): return &Response{ Status: codes.DeadlineExceeded, Err: ctx.Err(), } case <-time.After(logClient.MinMergeDelay): } } for { root = *logClient.GetRoot() if root.TreeSize >= 1 { proofResp := t.getProofByHash(resp.QueuedLeaf.Leaf.MerkleLeafHash) // if this call succeeds or returns an error other than "not found", return if proofResp.Err == nil || (proofResp.Err != nil && status.Code(proofResp.Err) != codes.NotFound) { return proofResp } // otherwise wait for a root update before trying again } if _, err := logClient.WaitForRootUpdate(ctx); err != nil { return &Response{ Status: codes.Unknown, Err: err, } } } } proofResp := waitForInclusion(t.context, resp.QueuedLeaf.Leaf.MerkleLeafHash) if proofResp.Err != nil { return &Response{ Status: status.Code(proofResp.Err), Err: proofResp.Err, GetAddResult: resp, } } proofs := proofResp.getProofResult.Proof if len(proofs) != 1 { err := fmt.Errorf("expected 1 proof from getProofByHash for %v, found %v", hex.EncodeToString(resp.QueuedLeaf.Leaf.MerkleLeafHash), len(proofs)) return &Response{ Status: status.Code(err), Err: err, GetAddResult: resp, } } leafIndex := proofs[0].LeafIndex leafResp := t.GetLeafAndProofByIndex(leafIndex) if leafResp.Err != nil { return &Response{ Status: status.Code(leafResp.Err), Err: leafResp.Err, GetAddResult: resp, } } // overwrite queued leaf that doesn't have index set resp.QueuedLeaf.Leaf = leafResp.GetLeafAndProofResult.Leaf return &Response{ Status: status.Code(err), Err: err, GetAddResult: resp, // include getLeafAndProofResult for inclusion proof GetLeafAndProofResult: leafResp.GetLeafAndProofResult, } } func (t *TrillianClient) GetLeafAndProofByHash(hash []byte) *Response { // get inclusion proof for hash, extract index, then fetch leaf using index proofResp := t.getProofByHash(hash) if proofResp.Err != nil { return &Response{ Status: status.Code(proofResp.Err), Err: proofResp.Err, } } proofs := proofResp.getProofResult.Proof if len(proofs) != 1 { err := fmt.Errorf("expected 1 proof from getProofByHash for %v, found %v", hex.EncodeToString(hash), len(proofs)) return &Response{ Status: status.Code(err), Err: err, } } return t.GetLeafAndProofByIndex(proofs[0].LeafIndex) } func (t *TrillianClient) GetLeafAndProofByIndex(index int64) *Response { ctx, cancel := context.WithTimeout(t.context, 20*time.Second) defer cancel() rootResp := t.GetLatest(0) if rootResp.Err != nil { return &Response{ Status: status.Code(rootResp.Err), Err: rootResp.Err, } } root, err := unmarshalLogRoot(rootResp.GetLatestResult.SignedLogRoot.LogRoot) if err != nil { return &Response{ Status: status.Code(rootResp.Err), Err: rootResp.Err, } } resp, err := t.client.GetEntryAndProof(ctx, &trillian.GetEntryAndProofRequest{ LogId: t.logID, LeafIndex: index, TreeSize: int64(root.TreeSize), }) if resp != nil && resp.Proof != nil { if err := proof.VerifyInclusion(rfc6962.DefaultHasher, uint64(index), root.TreeSize, resp.GetLeaf().MerkleLeafHash, resp.Proof.Hashes, root.RootHash); err != nil { return &Response{ Status: status.Code(err), Err: err, } } return &Response{ Status: status.Code(err), Err: err, GetLeafAndProofResult: &trillian.GetEntryAndProofResponse{ Proof: resp.Proof, Leaf: resp.Leaf, SignedLogRoot: rootResp.GetLatestResult.SignedLogRoot, }, } } return &Response{ Status: status.Code(err), Err: err, } } func (t *TrillianClient) GetLatest(leafSizeInt int64) *Response { ctx, cancel := context.WithTimeout(t.context, 20*time.Second) defer cancel() resp, err := t.client.GetLatestSignedLogRoot(ctx, &trillian.GetLatestSignedLogRootRequest{ LogId: t.logID, FirstTreeSize: leafSizeInt, }) return &Response{ Status: status.Code(err), Err: err, GetLatestResult: resp, } } func (t *TrillianClient) GetConsistencyProof(firstSize, lastSize int64) *Response { ctx, cancel := context.WithTimeout(t.context, 20*time.Second) defer cancel() resp, err := t.client.GetConsistencyProof(ctx, &trillian.GetConsistencyProofRequest{ LogId: t.logID, FirstTreeSize: firstSize, SecondTreeSize: lastSize, }) return &Response{ Status: status.Code(err), Err: err, GetConsistencyProofResult: resp, } } func (t *TrillianClient) getProofByHash(hashValue []byte) *Response { ctx, cancel := context.WithTimeout(t.context, 20*time.Second) defer cancel() rootResp := t.GetLatest(0) if rootResp.Err != nil { return &Response{ Status: status.Code(rootResp.Err), Err: rootResp.Err, } } root, err := unmarshalLogRoot(rootResp.GetLatestResult.SignedLogRoot.LogRoot) if err != nil { return &Response{ Status: status.Code(rootResp.Err), Err: rootResp.Err, } } // issue 1308: if the tree is empty, there's no way we can return a proof if root.TreeSize == 0 { return &Response{ Status: codes.NotFound, Err: status.Error(codes.NotFound, "tree is empty"), } } resp, err := t.client.GetInclusionProofByHash(ctx, &trillian.GetInclusionProofByHashRequest{ LogId: t.logID, LeafHash: hashValue, TreeSize: int64(root.TreeSize), }) if resp != nil { v := client.NewLogVerifier(rfc6962.DefaultHasher) for _, proof := range resp.Proof { if err := v.VerifyInclusionByHash(&root, hashValue, proof); err != nil { return &Response{ Status: status.Code(err), Err: err, } } } // Return an inclusion proof response with the requested return &Response{ Status: status.Code(err), Err: err, getProofResult: &trillian.GetInclusionProofByHashResponse{ Proof: resp.Proof, SignedLogRoot: rootResp.GetLatestResult.SignedLogRoot, }, } } return &Response{ Status: status.Code(err), Err: err, } } func CreateAndInitTree(ctx context.Context, adminClient trillian.TrillianAdminClient, logClient trillian.TrillianLogClient) (*trillian.Tree, error) { t, err := adminClient.CreateTree(ctx, &trillian.CreateTreeRequest{ Tree: &trillian.Tree{ TreeType: trillian.TreeType_LOG, TreeState: trillian.TreeState_ACTIVE, MaxRootDuration: durationpb.New(time.Hour), }, }) if err != nil { return nil, fmt.Errorf("create tree: %w", err) } if err := client.InitLog(ctx, t, logClient); err != nil { return nil, fmt.Errorf("init log: %w", err) } log.Logger.Infof("Created new tree with ID: %v", t.TreeId) return t, nil } rekor-1.3.5/pkg/types/000077500000000000000000000000001455727245600146105ustar00rootroot00000000000000rekor-1.3.5/pkg/types/README.md000066400000000000000000000021121455727245600160630ustar00rootroot00000000000000# Pluggable Types ## Description Rekor supports pluggable types (aka different schemas) for entries stored in the transparency log. ### Currently supported types - Alpine Packages [schema](alpine/alpine_schema.json) - Versions: 0.0.1 - COSE Envelopes [schema](cose/cose_schema.json) - Versions: 0.0.1 - DSSE Envelopes [schema](dsse/dsse_schema.json) - Versions: 0.0.1 - HashedRekord [schema](hashedrekord/hashedrekord_schema.json) - Versions: 0.0.1 - Helm Provenance Files [schema](helm/helm_schema.json) - Versions: 0.0.1 - In-Toto Attestations [schema](intoto/intoto_schema.json) - Versions: 0.0.1, 0.0.2 - Java Archives (JAR Files) [schema](jar/jar_schema.json) - Versions: 0.0.1 - Rekord *(default type)* [schema](rekord/rekord_schema.json) - Versions: 0.0.1 - RFC3161 Timestamps [schema](rfc3161/rfc3161_schema.json) - Versions: 0.0.1 - RPM Packages [schema](rpm/rpm_schema.json) - Versions: 0.0.1 - TUF Metadata [schema](tuf/tuf_schema.json) - Versions: 0.0.1 Refer to [Rekor docs](https://docs.sigstore.dev/rekor/pluggable-types) for adding support for new types. rekor-1.3.5/pkg/types/alpine/000077500000000000000000000000001455727245600160605ustar00rootroot00000000000000rekor-1.3.5/pkg/types/alpine/README.md000066400000000000000000000011451455727245600173400ustar00rootroot00000000000000**Alpine Type Data Documentation** This document notes additional information (beyond that found in the Alpine schema) about the values associated with each field such as the format in which the data is stored and any necessary transformations. Further information about pkginfo fields and the .PKGINFO file can be found on the [Alpine documentation website](https://wiki.alpinelinux.org/wiki/Alpine_package_format#.PKGINFO). The value of the `publicKey` `content` field ought to be Base64-encoded. **How do you identify an object as an Alpine object?** The "Body" field will include an "AlpineModel" field. rekor-1.3.5/pkg/types/alpine/alpine.go000066400000000000000000000034711455727245600176640ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alpine import ( "context" "errors" "fmt" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" ) const ( KIND = "alpine" ) type BaseAlpineType struct { types.RekorType } func init() { types.TypeMap.Store(KIND, New) } func New() types.TypeImpl { bat := BaseAlpineType{} bat.Kind = KIND bat.VersionMap = VersionMap return &bat } var VersionMap = types.NewSemVerEntryFactoryMap() func (bat *BaseAlpineType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) { if pe == nil { return nil, errors.New("proposed entry cannot be nil") } apk, ok := pe.(*models.Alpine) if !ok { return nil, errors.New("cannot unmarshal non-Alpine types") } return bat.VersionedUnmarshal(apk, *apk.APIVersion) } func (bat *BaseAlpineType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) { if version == "" { version = bat.DefaultVersion() } ei, err := bat.VersionedUnmarshal(nil, version) if err != nil { return nil, fmt.Errorf("fetching Intoto version implementation: %w", err) } return ei.CreateFromArtifactProperties(ctx, props) } func (bat BaseAlpineType) DefaultVersion() string { return "0.0.1" } rekor-1.3.5/pkg/types/alpine/alpine_schema.json000066400000000000000000000005321455727245600215430ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/alpine/alpine_schema.json", "title": "Alpine Package Schema", "description": "Schema for Alpine package objects", "type": "object", "oneOf": [ { "$ref": "v0.0.1/alpine_v0_0_1_schema.json" } ] } rekor-1.3.5/pkg/types/alpine/alpine_test.go000066400000000000000000000056541455727245600207300ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alpine import ( "errors" "testing" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" ) type UnmarshalTester struct { models.Alpine types.BaseUnmarshalTester } type UnmarshalFailsTester struct { types.BaseUnmarshalTester } func (u UnmarshalFailsTester) NewEntry() types.EntryImpl { return &UnmarshalFailsTester{} } func (u UnmarshalFailsTester) Unmarshal(_ models.ProposedEntry) error { return errors.New("error") } func (u UnmarshalFailsTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } func (u UnmarshalFailsTester) Insertable() (bool, error) { return false, nil } func TestAlpineType(t *testing.T) { // empty to start if VersionMap.Count() != 0 { t.Error("semver range was not blank at start of test") } u := UnmarshalTester{} // ensure semver range parser is working invalidSemVerRange := "not a valid semver range" err := VersionMap.SetEntryFactory(invalidSemVerRange, u.NewEntry) if err == nil || VersionMap.Count() > 0 { t.Error("invalid semver range was incorrectly added to SemVerToFacFnMap") } // valid semver range can be parsed err = VersionMap.SetEntryFactory(">= 1.2.3", u.NewEntry) if err != nil || VersionMap.Count() != 1 { t.Error("valid semver range was not added to SemVerToFacFnMap") } u.Alpine.APIVersion = swag.String("2.0.1") brt := New() // version requested matches implementation in map if _, err := brt.UnmarshalEntry(&u.Alpine); err != nil { t.Errorf("unexpected error in Unmarshal: %v", err) } // version requested fails to match implementation in map u.Alpine.APIVersion = swag.String("1.2.2") if _, err := brt.UnmarshalEntry(&u.Alpine); err == nil { t.Error("unexpected success in Unmarshal for non-matching version") } // error in Unmarshal call is raised appropriately u.Alpine.APIVersion = swag.String("2.2.0") u2 := UnmarshalFailsTester{} _ = VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry) if _, err := brt.UnmarshalEntry(&u.Alpine); err == nil { t.Error("unexpected success in Unmarshal when error is thrown") } // version requested fails to match implementation in map u.Alpine.APIVersion = swag.String("not_a_version") if _, err := brt.UnmarshalEntry(&u.Alpine); err == nil { t.Error("unexpected success in Unmarshal for invalid version") } } rekor-1.3.5/pkg/types/alpine/apk.go000066400000000000000000000166231455727245600171720ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alpine import ( "archive/tar" "bufio" "bytes" "compress/gzip" "crypto" "crypto/sha1" // #nosec G505 "crypto/sha256" "encoding/hex" "encoding/pem" "errors" "fmt" "hash" "io" "strings" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" "github.com/spf13/viper" "gopkg.in/ini.v1" ) type Package struct { Pkginfo map[string]string // KVP pairs Signature []byte Datahash []byte controlSHA1Digest []byte } type sha1Reader struct { r *bufio.Reader addToHash bool hasher hash.Hash } func newSHA1Reader(b *bufio.Reader) *sha1Reader { // #nosec G401 c := sha1Reader{ r: b, hasher: sha1.New(), } return &c } func (s *sha1Reader) Read(p []byte) (int, error) { n, err := s.r.Read(p) if err == nil && n > 0 && s.addToHash { s.hasher.Write(p) } return n, err } func (s *sha1Reader) ReadByte() (byte, error) { b, err := s.r.ReadByte() if err == nil && s.addToHash { s.hasher.Write([]byte{b}) } return b, err } func (s sha1Reader) Sum() []byte { return s.hasher.Sum(nil) } func (s *sha1Reader) StartHashing() { s.hasher.Reset() s.addToHash = true } func (s *sha1Reader) StopHashing() { s.addToHash = false } func (p *Package) Unmarshal(pkgReader io.Reader) error { pkg := Package{} // bufio.Reader is required if Multistream(false) is used bufReader := bufio.NewReader(pkgReader) sha1BufReader := newSHA1Reader(bufReader) gzipReader, err := gzip.NewReader(sha1BufReader) if err != nil { return fmt.Errorf("create gzip reader: %w", err) } defer func() { _ = gzipReader.Close() }() // APKs are concatenated gzip files so we want to know where the boundary is gzipReader.Multistream(false) // GZIP headers/footers are left unmodified; Tar footers are removed on first two archives // signature.tar.gz | control.tar.gz | data.tar.gz sigBuf := bytes.Buffer{} // #nosec G110 if _, err := io.Copy(&sigBuf, gzipReader); err != nil { return fmt.Errorf("reading signature.tar.gz: %w", err) } // the SHA1 sum used in the signature is over the entire file control.tar.gz so we need to // intercept the buffered reading to compute the hash correctly // // we start sha1 hashing now since the Reset() call will begin reading control.tar.gz headers sha1BufReader.StartHashing() // we reset the reader since we've found the end of signature.tar.gz if err := gzipReader.Reset(sha1BufReader); err != nil && err != io.EOF { return fmt.Errorf("resetting to control.tar.gz: %w", err) } gzipReader.Multistream(false) controlTar := bytes.Buffer{} // #nosec G110 if _, err = io.Copy(&controlTar, gzipReader); err != nil { return fmt.Errorf("reading control.tar.gz: %w", err) } // signature uses sha1 digest hardcoded in abuild-sign tool pkg.controlSHA1Digest = sha1BufReader.Sum() sha1BufReader.StopHashing() // the gzip reader is NOT reset again since that advances the underlying reader // by reading the next GZIP header, which affects the datahash computation below sigReader := tar.NewReader(&sigBuf) for { header, err := sigReader.Next() if err == io.EOF { if pkg.Signature == nil { return errors.New("no signature detected in alpine package") } break } else if err != nil { return fmt.Errorf("getting next entry in tar archive: %w", err) } if strings.HasPrefix(header.Name, ".SIGN") && pkg.Signature == nil { if header.Size < 0 { return errors.New("negative header size for .SIGN file") } if uint64(header.Size) > viper.GetUint64("max_apk_metadata_size") && viper.GetUint64("max_apk_metadata_size") > 0 { return fmt.Errorf("uncompressed .SIGN file size %d exceeds max allowed size %d", header.Size, viper.GetUint64("max_apk_metadata_size")) } sigBytes := make([]byte, header.Size) if _, err = sigReader.Read(sigBytes); err != nil && err != io.EOF { return fmt.Errorf("reading signature: %w", err) } // we're not sure whether this is PEM encoded or not, so handle both cases block, _ := pem.Decode(sigBytes) if block == nil { pkg.Signature = sigBytes } else { pkg.Signature = block.Bytes } } } ctlReader := tar.NewReader(&controlTar) for { header, err := ctlReader.Next() if err == io.EOF { if pkg.Pkginfo == nil { return errors.New(".PKGINFO file was not located") } break } else if err != nil { return fmt.Errorf("getting next entry in tar archive: %w", err) } if header.Name == ".PKGINFO" { if header.Size < 0 { return errors.New("negative header size for .PKGINFO file") } if uint64(header.Size) > viper.GetUint64("max_apk_metadata_size") && viper.GetUint64("max_apk_metadata_size") > 0 { return fmt.Errorf("uncompressed .PKGINFO file size %d exceeds max allowed size %d", header.Size, viper.GetUint64("max_apk_metadata_size")) } pkginfoContent := make([]byte, header.Size) if _, err = ctlReader.Read(pkginfoContent); err != nil && err != io.EOF { return fmt.Errorf("reading .PKGINFO: %w", err) } pkg.Pkginfo, err = parsePkginfo(pkginfoContent) if err != nil { return fmt.Errorf("parsing .PKGINFO: %w", err) } pkg.Datahash, err = hex.DecodeString(pkg.Pkginfo["datahash"]) if err != nil { return fmt.Errorf("parsing datahash: %w", err) } } } // at this point, bufReader should point to first byte of data.tar.gz // datahash value from .PKGINFO is sha256 sum of data.tar.gz sha256 := sha256.New() if _, err := io.Copy(sha256, bufReader); err != nil { return fmt.Errorf("computing SHA256 sum of data.tar.gz: %w", err) } computedSum := sha256.Sum(nil) if !bytes.Equal(computedSum, pkg.Datahash) { return fmt.Errorf("checksum for data.tar.gz (%v) does not match value from .PKGINFO (%v)", hex.EncodeToString(computedSum), hex.EncodeToString(pkg.Datahash)) } *p = pkg return nil } // VerifySignature verifies the signature of the alpine package using the provided // public key. It returns an error if verification fails, or nil if it is successful. func (p Package) VerifySignature(pub crypto.PublicKey) error { if p.Signature == nil { return errors.New("no signature in alpine package object") } if p.controlSHA1Digest == nil { return errors.New("no digest value for data.tar.gz known") } verifier, err := signature.LoadUnsafeVerifier(pub) if err != nil { return err } return verifier.VerifySignature(bytes.NewReader(p.Signature), nil, options.WithDigest(p.controlSHA1Digest), options.WithCryptoSignerOpts(crypto.SHA1)) } // parsePkginfo parses the .PKGINFO file which is in a // key[space]=[space]value\n // format. it returns a map[string]string of the key/value pairs, or // an error if parsing could not be completed successfully. func parsePkginfo(input []byte) (map[string]string, error) { cfg, err := ini.Load(input) if err != nil { return nil, err } // .PKGINFO does not use sections, so using "" grabs the default values return cfg.Section("").KeysHash(), nil } rekor-1.3.5/pkg/types/alpine/apk_test.go000066400000000000000000000033601455727245600202230ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alpine import ( "os" "strings" "testing" "github.com/sigstore/rekor/pkg/pki/x509" "github.com/spf13/viper" ) func TestAlpinePackage(t *testing.T) { inputArchive, err := os.Open("tests/test_alpine.apk") if err != nil { t.Fatalf("could not open archive %v", err) } p := Package{} err = p.Unmarshal(inputArchive) if err != nil { t.Fatalf("unmarshal error: %v", err) } pubKey, err := os.Open("tests/test_alpine.pub") if err != nil { t.Fatalf("could not open archive %v", err) } pub, err := x509.NewPublicKey(pubKey) if err != nil { t.Fatalf("failed to parse public key: %v", err) } if err = p.VerifySignature(pub.CryptoPubKey()); err != nil { t.Fatalf("signature verification failed: %v", err) } } func TestAlpineMetadataSize(t *testing.T) { viper.Set("max_apk_metadata_size", 10) inputArchive, err := os.Open("tests/test_alpine.apk") if err != nil { t.Fatalf("could not open archive %v", err) } p := Package{} err = p.Unmarshal(inputArchive) if err == nil { t.Fatal("expecting metadata too large err") } if !strings.Contains(err.Error(), "exceeds max allowed size 10") { t.Fatalf("unexpected error %v", err) } } rekor-1.3.5/pkg/types/alpine/e2e.go000066400000000000000000000053771455727245600170760ustar00rootroot00000000000000//go:build e2e // +build e2e // // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alpine import ( "archive/tar" "bytes" "compress/gzip" "crypto" crand "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/sha256" "encoding/hex" "io" "io/ioutil" "strings" "testing" "github.com/sigstore/rekor/pkg/util" sigx509 "github.com/sigstore/rekor/pkg/pki/x509" ) func CreateSignedApk(t *testing.T, artifactPath string) { t.Helper() data := util.RandomData(t, 100) dataTarBuf := bytes.Buffer{} dataTar := tar.NewWriter(&dataTarBuf) dataTar.WriteHeader(&tar.Header{Name: "random.txt", Size: int64(len(data))}) dataTar.Write(data) dataTar.Close() dataTGZBuf := bytes.Buffer{} dataGZ, _ := gzip.NewWriterLevel(&dataTGZBuf, gzip.BestCompression) dataGZ.Write(dataTarBuf.Bytes()) dataGZ.Close() datahash := sha256.Sum256(dataTGZBuf.Bytes()) ctlData := strings.Builder{} ctlData.WriteString("name = " + util.RandomSuffix(16)) ctlData.WriteRune('\n') ctlData.WriteString("datahash = " + hex.EncodeToString(datahash[:])) ctlData.WriteRune('\n') ctlTarBuf := bytes.Buffer{} ctlTar := tar.NewWriter(&ctlTarBuf) ctlTar.WriteHeader(&tar.Header{Name: ".PKGINFO", Size: int64(ctlData.Len())}) ctlTar.Write([]byte(ctlData.String())) ctlTar.Flush() // do not close so uncompressed stream appears as contiguous tar archive ctlTGZBuf := bytes.Buffer{} ctlGZ, _ := gzip.NewWriterLevel(&ctlTGZBuf, gzip.BestCompression) ctlGZ.Write(ctlTarBuf.Bytes()) ctlGZ.Close() sha1sum := sha1.Sum(ctlTGZBuf.Bytes()) sig, _ := rsa.SignPKCS1v15(crand.Reader, sigx509.CertPrivateKey, crypto.SHA1, sha1sum[:]) sigTarBuf := bytes.Buffer{} sigTar := tar.NewWriter(&sigTarBuf) sigTar.WriteHeader(&tar.Header{Name: ".SIGN.RSA.fixed.pub", Size: int64(len(sig))}) sigTar.Write(sig) sigTar.Flush() // do not close so uncompressed stream appears as contiguous tar archive sigTGZBuf := bytes.Buffer{} sigGZ, _ := gzip.NewWriterLevel(&sigTGZBuf, gzip.BestCompression) sigGZ.Write(sigTarBuf.Bytes()) sigGZ.Close() apkBuf := bytes.Buffer{} if _, err := io.Copy(&apkBuf, io.MultiReader(&sigTGZBuf, &ctlTGZBuf, &dataTGZBuf)); err != nil { t.Fatal(err) } if err := ioutil.WriteFile(artifactPath, apkBuf.Bytes(), 777); err != nil { t.Fatal(err) } } rekor-1.3.5/pkg/types/alpine/e2e_test.go000066400000000000000000000031771455727245600201310ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package alpine import ( "io/ioutil" "path/filepath" "testing" sigx509 "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/util" ) func TestAPK(t *testing.T) { td := t.TempDir() artifactPath := filepath.Join(td, "artifact.apk") CreateSignedApk(t, artifactPath) pubPath := filepath.Join(t.TempDir(), "pubKey.asc") if err := ioutil.WriteFile(pubPath, []byte(sigx509.PubKey), 0644); err != nil { t.Fatal(err) } // If we do it twice, it should already exist out := util.RunCli(t, "upload", "--artifact", artifactPath, "--type", "alpine", "--public-key", pubPath) util.OutputContains(t, out, "Created entry at") out = util.RunCli(t, "upload", "--artifact", artifactPath, "--type", "alpine", "--public-key", pubPath) util.OutputContains(t, out, "Entry already exists") // pass invalid public key, ensure we see an error with helpful message out = util.RunCliErr(t, "upload", "--artifact", artifactPath, "--type", "alpine", "--public-key", artifactPath) util.OutputContains(t, out, "invalid public key") } rekor-1.3.5/pkg/types/alpine/fuzz_test.go000066400000000000000000000024511455727245600204460ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alpine import ( "bytes" "fmt" "testing" "github.com/spf13/viper" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" utils "github.com/sigstore/rekor/pkg/fuzz" ) func init() { viper.Set("max_apk_metadata_size", 60000) if viper.GetUint64("max_apk_metadata_size") != 60000 { panic(fmt.Sprintf("max metadata size is not defined: %d", viper.GetUint64("max_apk_metadata_size"))) } } // FuzzPackageUnmarshal implements the fuzz test func FuzzPackageUnmarshal(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { ff := fuzz.NewConsumer(data) artifactBytes, err := utils.AlpineArtifactBytes(ff) if err != nil { t.Skip() } p := &Package{} p.Unmarshal(bytes.NewReader(artifactBytes)) }) } rekor-1.3.5/pkg/types/alpine/tests/000077500000000000000000000000001455727245600172225ustar00rootroot00000000000000rekor-1.3.5/pkg/types/alpine/tests/test_alpine.apk000066400000000000000000000054721455727245600222360ustar00rootroot00000000000000 t vK)KMI-Kq,.) dV$%X'&1 L:m`d3(0$)(0SGH~gW_*.yf2{[`iAM|eJٴX&OY%N Z=sN[^d2Q+>.Z;G.2))~f#妪OoeΆvCrmY*Bʬ,qZ q>{>Ħ)wHi7s٥d5[.mDZ馮)Z/g3}߀\#;4fʻ0悷ltK-UA4ڜJGw'fsϸ[ ie$D+ư\H=DKGMYNܑ/hжSs;aZӤ%ΟÉS;H΀gn1oVtvs0e>쳪q[zFֹw AQXk (4JW Pl/u%Xneç< NxeKZKIZHms443=IDN>`kzԍ[6`ϭNթʕlqل+]vZVhh+תSz8f䪎Ӭ6r=Ja ]zB#Bufk5itvG_-Sk=ۮ/`aW>fja7Cr?ap_&Y!:~3&'w?A=e/?xGCg F]Gf}t@w-S\$,=;FFZ7=U-錫N=Vk4Z7HOH?"kԓXa׹rW_x"?w_[׋+//` <i>#z(S3gÑL {+ OFFqbU6S<2 rnoa*S%|. "B\+%6ژ "|n(>cj"n/<1拸VF{Df{d1 7BG3I\EKߐH!XxKT"i exlQ*"߆s>!dž8aC_U*vlYYe\e =uKcT95fωPXylnlNYp,\aYt:Z mVaw0_,Bυ[ܾ[D QЅ!9;=gt]sEtRP ]*z1TxW4K،L^sQu\Bݛ@"ҏ*SP([2)N(M 99K;Dh[BD)(( ȝ.mxD0JkWJY0hwG@M-'#H" f'Bb7s`i}㚠^OKAcAZ2Tt_V3-mNiKsP /5$`JIoB$#nhO 8ZI( (4|f3S/}GT̒,QzXezG$0ɞ%mgGE`iZ0U2m *XKXJn=A\@ޜt ;N!~=޽.Xn6,pGሚG {ݨJ|pmH"}mU N,E K2N WL!T=<=VgkÍwI] %#BĒ:F )BQc(? [ӂm?8!!cTP/pld<:@ڮh$QAZDr- +N"ăe(/3q)Ŭ$kjӋn4I8|8X]svQUN.M!X\h?p"[ЂϮ9i&h%LMds7y,ѹv ٜ _jSNROVhYJolG?"LBԍpLٙf'T:%ueL Iw94)={ӍLR=O9FX|ԥqRd/wqyBbg\(E~(|N  k(< !Ì./`ݗĨ/ٰ[]"6,}u)O`X +oIM6nV#a}S/SW,%A ~]~jR%FK{XIfw$&íۀ8S&-S٦?bX=`Pu[Kisa R|'*$\MIGΚ vUNՁg3Lf+{rͼ|c_خڮڮڮ/hU(rekor-1.3.5/pkg/types/alpine/tests/test_alpine.pub000066400000000000000000000007031455727245600222410ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1yHJxQgsHQREclQu4Ohe qxTxd1tHcNnvnQTu/UrTky8wWvgXT+jpveroeWWnzmsYlDI93eLI2ORakxb3gA2O Q0Ry4ws8vhaxLQGC74uQR5+/yYrLuTKydFzuPaS1dK19qJPXB8GMdmFOijnXX4SA jixuHLe1WW7kZVtjL7nufvpXkWBGjsfrvskdNA/5MfxAeBbqPgaq0QMEfxMAn6/R L5kNepi/Vr4S39Xvf2DzWkTLEK8pcnjNkt9/aafhWqFVW7m3HCAII6h/qlQNQKSo GuH34Q8GsFG30izUENV9avY7hSLq7nggsvknlNBZtFUcmGoQrtx3FmyYsIC8/R+B ywIDAQAB -----END PUBLIC KEY----- rekor-1.3.5/pkg/types/alpine/v0.0.1/000077500000000000000000000000001455727245600167025ustar00rootroot00000000000000rekor-1.3.5/pkg/types/alpine/v0.0.1/alpine_v0_0_1_schema.json000066400000000000000000000047071455727245600234410ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/alpine/alpine_v0_0_1_schema.json", "title": "Alpine v0.0.1 Schema", "description": "Schema for Alpine Package entries", "type": "object", "properties": { "publicKey" : { "description": "The public key that can verify the package signature", "type": "object", "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } }, "required": [ "content" ] }, "package": { "description": "Information about the package associated with the entry", "type": "object", "properties": { "pkginfo": { "description": "Values of the .PKGINFO key / value pairs", "type": "object", "additionalProperties": { "type": "string" }, "readOnly": true }, "hash": { "description": "Specifies the hash algorithm and value for the package", "type": "object", "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the package", "type": "string" } }, "readOnly": true, "required": [ "algorithm", "value" ] }, "content": { "description": "Specifies the package inline within the document", "type": "string", "format": "byte", "writeOnly": true } }, "oneOf": [ { "required": [ "hash" ] }, { "required": [ "content" ] } ] } }, "required": [ "publicKey", "package" ] } rekor-1.3.5/pkg/types/alpine/v0.0.1/entry.go000066400000000000000000000243301455727245600203740ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alpine import ( "bytes" "context" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "io" "os" "path/filepath" "strings" "github.com/asaskevich/govalidator" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "golang.org/x/sync/errgroup" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/alpine" "github.com/sigstore/rekor/pkg/util" ) const ( APIVERSION = "0.0.1" ) func init() { if err := alpine.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil { log.Logger.Panic(err) } } type V001Entry struct { AlpineModel models.AlpineV001Schema } func (v V001Entry) APIVersion() string { return APIVERSION } func NewEntry() types.EntryImpl { return &V001Entry{} } func (v V001Entry) IndexKeys() ([]string, error) { var result []string keyObj, err := x509.NewPublicKey(bytes.NewReader(*v.AlpineModel.PublicKey.Content)) if err != nil { return nil, err } key, err := keyObj.CanonicalValue() if err != nil { return nil, err } keyHash := sha256.Sum256(key) result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:]))) result = append(result, keyObj.Subjects()...) if v.AlpineModel.Package.Hash != nil { hashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.AlpineModel.Package.Hash.Algorithm, *v.AlpineModel.Package.Hash.Value)) result = append(result, hashKey) } return result, nil } func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error { apk, ok := pe.(*models.Alpine) if !ok { return errors.New("cannot unmarshal non Alpine v0.0.1 type") } if err := types.DecodeEntry(apk.Spec, &v.AlpineModel); err != nil { return err } // field validation if err := v.AlpineModel.Validate(strfmt.Default); err != nil { return err } return v.validate() } func (v *V001Entry) fetchExternalEntities(ctx context.Context) (*x509.PublicKey, *alpine.Package, error) { if err := v.validate(); err != nil { return nil, nil, types.ValidationError(err) } g, ctx := errgroup.WithContext(ctx) hashR, hashW := io.Pipe() apkR, apkW := io.Pipe() defer hashR.Close() defer apkR.Close() closePipesOnError := types.PipeCloser(hashR, hashW, apkR, apkW) oldSHA := "" if v.AlpineModel.Package.Hash != nil && v.AlpineModel.Package.Hash.Value != nil { oldSHA = swag.StringValue(v.AlpineModel.Package.Hash.Value) } g.Go(func() error { defer hashW.Close() defer apkW.Close() dataReadCloser := bytes.NewReader(v.AlpineModel.Package.Content) /* #nosec G110 */ if _, err := io.Copy(io.MultiWriter(hashW, apkW), dataReadCloser); err != nil { return closePipesOnError(err) } return nil }) hashResult := make(chan string) g.Go(func() error { defer close(hashResult) hasher := sha256.New() if _, err := io.Copy(hasher, hashR); err != nil { return closePipesOnError(err) } computedSHA := hex.EncodeToString(hasher.Sum(nil)) if oldSHA != "" && computedSHA != oldSHA { return closePipesOnError(types.ValidationError(fmt.Errorf("SHA mismatch: %s != %s", computedSHA, oldSHA))) } select { case <-ctx.Done(): return ctx.Err() case hashResult <- computedSHA: return nil } }) keyResult := make(chan *x509.PublicKey) g.Go(func() error { defer close(keyResult) keyReadCloser := bytes.NewReader(*v.AlpineModel.PublicKey.Content) keyObj, err := x509.NewPublicKey(keyReadCloser) if err != nil { return closePipesOnError(types.ValidationError(err)) } select { case <-ctx.Done(): return ctx.Err() case keyResult <- keyObj: return nil } }) var apkObj *alpine.Package var key *x509.PublicKey g.Go(func() error { apk := alpine.Package{} if err := apk.Unmarshal(apkR); err != nil { return closePipesOnError(types.ValidationError(err)) } key = <-keyResult if key == nil { return closePipesOnError(errors.New("error processing public key")) } if err := apk.VerifySignature(key.CryptoPubKey()); err != nil { return closePipesOnError(types.ValidationError(err)) } apkObj = &apk select { case <-ctx.Done(): return ctx.Err() default: return nil } }) computedSHA := <-hashResult if err := g.Wait(); err != nil { return nil, nil, err } // if we get here, all goroutines succeeded without error if oldSHA == "" { v.AlpineModel.Package.Hash = &models.AlpineV001SchemaPackageHash{} v.AlpineModel.Package.Hash.Algorithm = swag.String(models.AlpineV001SchemaPackageHashAlgorithmSha256) v.AlpineModel.Package.Hash.Value = swag.String(computedSHA) } return key, apkObj, nil } func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) { key, apkObj, err := v.fetchExternalEntities(ctx) if err != nil { return nil, err } canonicalEntry := models.AlpineV001Schema{} var content []byte // need to canonicalize key content canonicalEntry.PublicKey = &models.AlpineV001SchemaPublicKey{} content, err = key.CanonicalValue() if err != nil { return nil, err } canonicalEntry.PublicKey.Content = (*strfmt.Base64)(&content) canonicalEntry.Package = &models.AlpineV001SchemaPackage{} canonicalEntry.Package.Hash = &models.AlpineV001SchemaPackageHash{} canonicalEntry.Package.Hash.Algorithm = v.AlpineModel.Package.Hash.Algorithm canonicalEntry.Package.Hash.Value = v.AlpineModel.Package.Hash.Value // data content is not set deliberately // set .PKGINFO headers canonicalEntry.Package.Pkginfo = apkObj.Pkginfo // wrap in valid object with kind and apiVersion set apk := models.Alpine{} apk.APIVersion = swag.String(APIVERSION) apk.Spec = &canonicalEntry v.AlpineModel = canonicalEntry return json.Marshal(&apk) } // validate performs cross-field validation for fields in object func (v V001Entry) validate() error { key := v.AlpineModel.PublicKey if key == nil { return errors.New("missing public key") } if key.Content == nil || len(*key.Content) == 0 { return errors.New("'content' must be specified for publicKey") } pkg := v.AlpineModel.Package if pkg == nil { return errors.New("missing package") } hash := pkg.Hash if hash != nil { if !govalidator.IsHash(swag.StringValue(hash.Value), swag.StringValue(hash.Algorithm)) { return errors.New("invalid value for hash") } } else if len(pkg.Content) == 0 { return errors.New("'content' must be specified for package") } return nil } func (v V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) { returnVal := models.Alpine{} re := V001Entry{} // we will need artifact, public-key, signature re.AlpineModel = models.AlpineV001Schema{} re.AlpineModel.Package = &models.AlpineV001SchemaPackage{} var err error artifactBytes := props.ArtifactBytes if artifactBytes == nil { var artifactReader io.ReadCloser if props.ArtifactPath == nil { return nil, errors.New("path to artifact file must be specified") } if props.ArtifactPath.IsAbs() { artifactReader, err = util.FileOrURLReadCloser(ctx, props.ArtifactPath.String(), nil) if err != nil { return nil, fmt.Errorf("error reading artifact file: %w", err) } } else { artifactReader, err = os.Open(filepath.Clean(props.ArtifactPath.Path)) if err != nil { return nil, fmt.Errorf("error opening artifact file: %w", err) } } artifactBytes, err = io.ReadAll(artifactReader) if err != nil { return nil, fmt.Errorf("error reading artifact file: %w", err) } } re.AlpineModel.Package.Content = strfmt.Base64(artifactBytes) re.AlpineModel.PublicKey = &models.AlpineV001SchemaPublicKey{} publicKeyBytes := props.PublicKeyBytes if len(publicKeyBytes) == 0 { if len(props.PublicKeyPaths) != 1 { return nil, errors.New("only one public key must be provided") } keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path)) if err != nil { return nil, fmt.Errorf("error reading public key file: %w", err) } publicKeyBytes = append(publicKeyBytes, keyBytes) } else if len(publicKeyBytes) != 1 { return nil, errors.New("only one public key must be provided") } re.AlpineModel.PublicKey.Content = (*strfmt.Base64)(&publicKeyBytes[0]) if err := re.validate(); err != nil { return nil, err } if _, _, err := re.fetchExternalEntities(ctx); err != nil { return nil, fmt.Errorf("error retrieving external entities: %v", err) } returnVal.APIVersion = swag.String(re.APIVersion()) returnVal.Spec = re.AlpineModel return &returnVal, nil } func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { if v.AlpineModel.PublicKey == nil || v.AlpineModel.PublicKey.Content == nil { return nil, errors.New("alpine v0.0.1 entry not initialized") } key, err := x509.NewPublicKey(bytes.NewReader(*v.AlpineModel.PublicKey.Content)) if err != nil { return nil, err } return []pki.PublicKey{key}, nil } func (v V001Entry) ArtifactHash() (string, error) { if v.AlpineModel.Package == nil || v.AlpineModel.Package.Hash == nil || v.AlpineModel.Package.Hash.Value == nil || v.AlpineModel.Package.Hash.Algorithm == nil { return "", errors.New("alpine v0.0.1 entry not initialized") } return strings.ToLower(fmt.Sprintf("%s:%s", *v.AlpineModel.Package.Hash.Algorithm, *v.AlpineModel.Package.Hash.Value)), nil } func (v V001Entry) Insertable() (bool, error) { if v.AlpineModel.Package == nil { return false, fmt.Errorf("missing package entry") } if len(v.AlpineModel.Package.Content) == 0 { return false, fmt.Errorf("missing package content") } if v.AlpineModel.PublicKey == nil { return false, fmt.Errorf("missing public key") } if v.AlpineModel.PublicKey.Content == nil || len(*v.AlpineModel.PublicKey.Content) == 0 { return false, fmt.Errorf("missing public key content") } return true, nil } rekor-1.3.5/pkg/types/alpine/v0.0.1/entry_test.go000066400000000000000000000204451455727245600214360ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alpine import ( "bytes" "context" "crypto/sha256" "encoding/hex" "os" "reflect" "testing" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "go.uber.org/goleak" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestNewEntryReturnType(t *testing.T) { entry := NewEntry() if reflect.TypeOf(entry) != reflect.ValueOf(&V001Entry{}).Type() { t.Errorf("invalid type returned from NewEntry: %T", entry) } } func TestCrossFieldValidation(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectUnmarshalSuccess bool expectCanonicalizeSuccess bool expectedVerifierSuccess bool } keyBytes, _ := os.ReadFile("../tests/test_alpine.pub") dataBytes, _ := os.ReadFile("../tests/test_alpine.apk") testCases := []TestCase{ { caseDesc: "empty obj", entry: V001Entry{}, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "public key without content", entry: V001Entry{ AlpineModel: models.AlpineV001Schema{ PublicKey: &models.AlpineV001SchemaPublicKey{}, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "public key without package", entry: V001Entry{ AlpineModel: models.AlpineV001Schema{ PublicKey: &models.AlpineV001SchemaPublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "public key with empty package", entry: V001Entry{ AlpineModel: models.AlpineV001Schema{ PublicKey: &models.AlpineV001SchemaPublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, Package: &models.AlpineV001SchemaPackage{}, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "public key with invalid key content & with data with content", entry: V001Entry{ AlpineModel: models.AlpineV001Schema{ PublicKey: &models.AlpineV001SchemaPublicKey{ Content: (*strfmt.Base64)(&dataBytes), }, Package: &models.AlpineV001SchemaPackage{ Content: strfmt.Base64(dataBytes), }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "public key with key content & with data with content", entry: V001Entry{ AlpineModel: models.AlpineV001Schema{ PublicKey: &models.AlpineV001SchemaPublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, Package: &models.AlpineV001SchemaPackage{ Content: strfmt.Base64(dataBytes), }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: true, expectedVerifierSuccess: true, }, } for _, tc := range testCases { if err := tc.entry.validate(); (err == nil) != tc.expectUnmarshalSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } v := &V001Entry{} r := models.Alpine{ APIVersion: swag.String(tc.entry.APIVersion()), Spec: tc.entry.AlpineModel, } unmarshalAndValidate := func() error { if err := v.Unmarshal(&r); err != nil { return err } return v.validate() } if err := unmarshalAndValidate(); (err == nil) != tc.expectUnmarshalSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } if tc.expectUnmarshalSuccess { if ok, err := v.Insertable(); !ok || err != nil { t.Errorf("unexpected result in calling Insertable on valid proposed entry: %v", err) } } b, err := v.Canonicalize(context.TODO()) if (err == nil) != tc.expectCanonicalizeSuccess { t.Errorf("unexpected result from Canonicalize for '%v': %v", tc.caseDesc, err) } else if err != nil { if _, ok := err.(types.ValidationError); !ok { t.Errorf("canonicalize returned an unexpected error that isn't of type types.ValidationError: %v", err) } } if b != nil { pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tc.caseDesc, err) } ei, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tc.caseDesc, err) } if ok, err := ei.Insertable(); ok || err == nil { t.Errorf("unexpected success calling Insertable on entry created from canonicalized content") } hash, err := ei.ArtifactHash() expectedHash := sha256.Sum256(dataBytes) if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != "sha256:"+hex.EncodeToString(expectedHash[:]) { t.Errorf("unexpected match with ArtifactHash: %s", hash) } } verifiers, err := v.Verifiers() if tc.expectedVerifierSuccess { if err != nil { t.Errorf("%v: unexpected error, got %v", tc.caseDesc, err) } else { pub, _ := verifiers[0].CanonicalValue() if !reflect.DeepEqual(pub, keyBytes) { t.Errorf("%v: verifier and public keys do not match: %v, %v", tc.caseDesc, string(pub), string(keyBytes)) } } } else { if err == nil { s, _ := verifiers[0].CanonicalValue() t.Errorf("%v: expected error for %v, got %v", tc.caseDesc, string(s), err) } } } } func TestInsertable(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectSuccess bool } pub := strfmt.Base64([]byte("pub")) testCases := []TestCase{ { caseDesc: "valid entry", entry: V001Entry{ AlpineModel: models.AlpineV001Schema{ Package: &models.AlpineV001SchemaPackage{ Content: strfmt.Base64("package"), }, PublicKey: &models.AlpineV001SchemaPublicKey{ Content: &pub, }, }, }, expectSuccess: true, }, { caseDesc: "missing key content", entry: V001Entry{ AlpineModel: models.AlpineV001Schema{ Package: &models.AlpineV001SchemaPackage{ Content: strfmt.Base64("package"), }, PublicKey: &models.AlpineV001SchemaPublicKey{ //Content: &pub, }, }, }, expectSuccess: false, }, { caseDesc: "missing public key", entry: V001Entry{ AlpineModel: models.AlpineV001Schema{ Package: &models.AlpineV001SchemaPackage{ Content: strfmt.Base64("package"), }, /* PublicKey: &models.AlpineV001SchemaPublicKey{ Content: &pub, }, */ }, }, expectSuccess: false, }, { caseDesc: "missing package content", entry: V001Entry{ AlpineModel: models.AlpineV001Schema{ Package: &models.AlpineV001SchemaPackage{ //Content: strfmt.Base64("package"), }, PublicKey: &models.AlpineV001SchemaPublicKey{ Content: &pub, }, }, }, expectSuccess: false, }, { caseDesc: "missing package", entry: V001Entry{ AlpineModel: models.AlpineV001Schema{ /* Package: &models.AlpineV001SchemaPackage{ Content: strfmt.Base64("package"), }, */ PublicKey: &models.AlpineV001SchemaPublicKey{ Content: &pub, }, }, }, expectSuccess: false, }, { caseDesc: "empty model", entry: V001Entry{ AlpineModel: models.AlpineV001Schema{ /* Package: &models.AlpineV001SchemaPackage{ Content: strfmt.Base64("package"), }, PublicKey: &models.AlpineV001SchemaPublicKey{ Content: &pub, }, */ }, }, expectSuccess: false, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess { t.Errorf("unexpected result calling Insertable: %v", err) } }) } } rekor-1.3.5/pkg/types/alpine/v0.0.1/fuzz_test.go000066400000000000000000000045241455727245600212730ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alpine import ( "context" "sync" "testing" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/go-openapi/swag" fuzzUtils "github.com/sigstore/rekor/pkg/fuzz" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/alpine" ) var initter sync.Once func FuzzAlpineCreateProposedEntry(f *testing.F) { f.Fuzz(func(t *testing.T, propsData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) version := "0.0.1" ff := fuzz.NewConsumer(propsData) props, cleanup, err := fuzzUtils.CreateAlpineProps(ff) if err != nil { t.Skip() } defer cleanup() it := alpine.New() entry, err := it.CreateProposedEntry(context.Background(), version, props) if err != nil { t.Skip() } ei, err := types.CreateVersionedEntry(entry) if err != nil { t.Skip() } if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err) } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("valid insertable entry should be able to be canonicalized: %v", err) } _, _ = ei.IndexKeys() }) } func FuzzAlpineUnmarshalAndCanonicalize(f *testing.F) { f.Fuzz(func(t *testing.T, entryData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(entryData) targetV001 := &models.AlpineV001Schema{} if err := ff.GenerateStruct(targetV001); err != nil { t.Skip() } targetEntry := &models.Alpine{ APIVersion: swag.String(APIVERSION), Spec: targetV001, } ei, err := types.UnmarshalEntry(targetEntry) if err != nil { t.Skip() } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Skip() } }) } rekor-1.3.5/pkg/types/cose/000077500000000000000000000000001455727245600155415ustar00rootroot00000000000000rekor-1.3.5/pkg/types/cose/README.md000066400000000000000000000025561455727245600170300ustar00rootroot00000000000000**COSE Type Data Documentation** This document provides a definition for each field that is not otherwise described in the [cose schema](https://github.com/sigstore/rekor/blob/main/pkg/types/cose/v0.0.1/cose_v0_0_1_schema.json). This document also notes any additional information about the values associated with each field such as the format in which the data is stored and any necessary transformations. **AAD** Additional Authenticated Data. If the COSE envelope is signed using AAD, the same data must be provided during upload, otherwise the signature verification will fail. This data is not stored in Rekor. **How do you identify an object as an cose object?** The "Body" field will include an "coseObj" field. **Recognized content types** - [in-toto statements](https://github.com/in-toto/attestation/tree/main/spec#statement) are recognized and parsed. The found subject hashes are indexed so they can be searched for. **What data about the envelope is stored in Rekor** Only the hash of the payload, the hash of the COSE envelope and the public key is stored. If Rekor is configured to use attestation storage, the entire envelope is also stored. If attestation storage is enabled, the COSE envelope is stored as an attestation, which means that during retrieval of the record, the complete envelope is returned in the `attestation` field, not within the `body`. rekor-1.3.5/pkg/types/cose/cose.go000066400000000000000000000034331455727245600170240ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cose import ( "context" "errors" "fmt" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" ) const ( KIND = "cose" ) type BaseCOSEType struct { types.RekorType } func init() { types.TypeMap.Store(KIND, New) } func New() types.TypeImpl { bit := BaseCOSEType{} bit.Kind = KIND bit.VersionMap = VersionMap return &bit } var VersionMap = types.NewSemVerEntryFactoryMap() func (it BaseCOSEType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) { if pe == nil { return nil, errors.New("proposed entry cannot be nil") } in, ok := pe.(*models.Cose) if !ok { return nil, errors.New("cannot unmarshal non-COSE types") } return it.VersionedUnmarshal(in, *in.APIVersion) } func (it *BaseCOSEType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) { if version == "" { version = it.DefaultVersion() } ei, err := it.VersionedUnmarshal(nil, version) if err != nil { return nil, fmt.Errorf("fetching COSE version implementation: %w", err) } return ei.CreateFromArtifactProperties(ctx, props) } func (it BaseCOSEType) DefaultVersion() string { return "0.0.1" } rekor-1.3.5/pkg/types/cose/cose_schema.json000066400000000000000000000005001455727245600207000ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/cose/cose_schema.json", "title": "COSE Schema", "description": "COSE for Rekord objects", "type": "object", "oneOf": [ { "$ref": "v0.0.1/cose_v0_0_1_schema.json" } ] } rekor-1.3.5/pkg/types/cose/cose_test.go000066400000000000000000000112431455727245600200610ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cose import ( "context" "errors" "testing" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" ) type UnmarshalTester struct { models.Cose types.BaseUnmarshalTester } type UnmarshalFailsTester struct { types.BaseUnmarshalTester } func (u UnmarshalFailsTester) NewEntry() types.EntryImpl { return &UnmarshalFailsTester{} } func (u UnmarshalFailsTester) Unmarshal(_ models.ProposedEntry) error { return errors.New("error") } func (u UnmarshalFailsTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } func (u UnmarshalFailsTester) Insertable() (bool, error) { return false, nil } func TestCOSEType(t *testing.T) { // empty to start if VersionMap.Count() != 0 { t.Error("semver range was not blank at start of test") } u := UnmarshalTester{} // ensure semver range parser is working invalidSemVerRange := "not a valid semver range" err := VersionMap.SetEntryFactory(invalidSemVerRange, u.NewEntry) if err == nil || VersionMap.Count() > 0 { t.Error("invalid semver range was incorrectly added to SemVerToFacFnMap") } // valid semver range can be parsed err = VersionMap.SetEntryFactory(">= 1.2.3", u.NewEntry) if err != nil || VersionMap.Count() != 1 { t.Error("valid semver range was not added to SemVerToFacFnMap") } u.Cose.APIVersion = swag.String("2.0.1") brt := New() // version requested matches implementation in map if _, err := brt.UnmarshalEntry(&u.Cose); err != nil { t.Errorf("unexpected error in Unmarshal: %v", err) } // version requested fails to match implementation in map u.Cose.APIVersion = swag.String("1.2.2") if _, err := brt.UnmarshalEntry(&u.Cose); err == nil { t.Error("unexpected success in Unmarshal for non-matching version") } // error in Unmarshal call is raised appropriately u.Cose.APIVersion = swag.String("2.2.0") u2 := UnmarshalFailsTester{} _ = VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry) if _, err := brt.UnmarshalEntry(&u.Cose); err == nil { t.Error("unexpected success in Unmarshal when error is thrown") } // version requested fails to match implementation in map u.Cose.APIVersion = swag.String("not_a_version") if _, err := brt.UnmarshalEntry(&u.Cose); err == nil { t.Error("unexpected success in Unmarshal for invalid version") } ti, err := brt.UnmarshalEntry(nil) if ti != nil { t.Error("unexpected success in unmarshal for nil") } if err == nil { t.Error("expected error") } ti, err = brt.UnmarshalEntry(types.BaseProposedEntryTester{}) if ti != nil { t.Error("unexpected success in unmarshal for nil") } if err == nil { t.Error("expected error") } } func TestCOSEDefaultVersion(t *testing.T) { brt := New() ver := brt.DefaultVersion() if ver != "0.0.1" { t.Errorf("unexpected default version %s", ver) } } func TestCOSECreateProposedEntry(t *testing.T) { // Reset semver map VersionMap = types.NewSemVerEntryFactoryMap() u := UnmarshalTester{} VersionMap.SetEntryFactory("0.0.3", u.NewEntry) VersionMap.SetEntryFactory(New().DefaultVersion(), u.NewEntry) t.Run("unknown version", func(t *testing.T) { ctx := context.Background() brt := New() props := types.ArtifactProperties{} pe, err := brt.CreateProposedEntry(ctx, "1.2.3", props) if pe != nil { t.Error("unexpected propsed entry") } if err == nil { t.Error("expected error") } }) t.Run("valid version", func(t *testing.T) { ctx := context.Background() brt := New() props := types.ArtifactProperties{} pe, err := brt.CreateProposedEntry(ctx, "0.0.3", props) // BaseUnmarshalTester returns nil for the proposed entry if pe != nil { t.Error("unexpected proposed entry") } if err != nil { t.Error("unexpected error") } }) t.Run("default version", func(t *testing.T) { ctx := context.Background() brt := New() props := types.ArtifactProperties{} pe, err := brt.CreateProposedEntry(ctx, "", props) // BaseUnmarshalTester returns nil for the proposed entry if pe != nil { t.Error("unexpected proposed entry") } if err != nil { t.Error("unexpected error") } }) } rekor-1.3.5/pkg/types/cose/v0.0.1/000077500000000000000000000000001455727245600163635ustar00rootroot00000000000000rekor-1.3.5/pkg/types/cose/v0.0.1/cose_v0_0_1_schema.json000066400000000000000000000056211455727245600225770ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/cose/cose_v0_0_1_schema.json", "title": "cose v0.0.1 Schema", "description": "Schema for cose object", "type": "object", "properties": { "message": { "description": "The COSE Sign1 Message", "type": "string", "format": "byte", "writeOnly": true }, "publicKey": { "description": "The public key that can verify the signature", "type": "string", "format": "byte" }, "data": { "description": "Information about the content associated with the entry", "type": "object", "properties": { "payloadHash": { "description": "Specifies the hash algorithm and value for the content", "type": "object", "readOnly": true, "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the content", "type": "string" } }, "required": [ "algorithm", "value" ] }, "envelopeHash": { "description": "Specifies the hash algorithm and value for the COSE envelope", "type": "object", "readOnly": true, "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the envelope", "type": "string" } }, "required": [ "algorithm", "value" ] }, "aad": { "description": "Specifies the additional authenticated data required to verify the signature", "type": "string", "format": "byte", "writeOnly": true } }, "required": [] } }, "required": [ "publicKey" ] } rekor-1.3.5/pkg/types/cose/v0.0.1/entry.go000066400000000000000000000254671455727245600200710ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cose import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/rsa" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "os" "path/filepath" "strings" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/in-toto/in-toto-golang/in_toto" "github.com/spf13/viper" gocose "github.com/veraison/go-cose" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/cose" ) const ( APIVERSION = "0.0.1" ) const ( CurveP256 = "P-256" ) func init() { if err := cose.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil { log.Logger.Panic(err) } } type V001Entry struct { CoseObj models.CoseV001Schema keyObj pki.PublicKey sign1Msg *gocose.Sign1Message envelopeHash []byte } func (v V001Entry) APIVersion() string { return APIVERSION } func NewEntry() types.EntryImpl { return &V001Entry{} } func (v V001Entry) IndexKeys() ([]string, error) { var result []string // We add the key, the hash of the overall cose envelope, and the hash of the payload itself as keys. keyObj, err := x509.NewPublicKey(bytes.NewReader(*v.CoseObj.PublicKey)) if err != nil { return nil, err } // 1. Key key, err := keyObj.CanonicalValue() if err != nil { log.Logger.Error(err) } else { keyHash := sha256.Sum256(key) result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:]))) } result = append(result, keyObj.Subjects()...) // 2. Overall envelope result = append(result, formatKey(v.CoseObj.Message)) // 3. Payload if v.sign1Msg != nil { result = append(result, formatKey(v.sign1Msg.Payload)) } else { // If no payload exists (it's unpacked in validate() method) // return now, as we will not be able to extract any headers return result, nil } // If payload is an in-toto statement, let's grab the subjects. if rawContentType, ok := v.sign1Msg.Headers.Protected[gocose.HeaderLabelContentType]; ok { contentType, ok := rawContentType.(string) // Integers as defined by CoAP content format are valid too, // but in-intoto payload type is not defined there, so only // proceed if content type is a string. // See list of CoAP content formats here: // https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats if ok && contentType == in_toto.PayloadType { stmt, err := getIntotoStatement(v.sign1Msg.Payload) if err != nil { // ContentType header says intoto statement, but // parsing failed, continue with a warning. log.Logger.Warnf("Failed to parse intoto statement") } else { for _, sub := range stmt.Subject { for alg, digest := range sub.Digest { index := alg + ":" + digest result = append(result, index) } } } } } return result, nil } func getIntotoStatement(b []byte) (*in_toto.Statement, error) { var stmt in_toto.Statement if err := json.Unmarshal(b, &stmt); err != nil { return nil, err } return &stmt, nil } func formatKey(b []byte) string { h := sha256.Sum256(b) hash := hex.EncodeToString(h[:]) return strings.ToLower(fmt.Sprintf("%s:%s", models.CoseV001SchemaDataPayloadHashAlgorithmSha256, hash)) } func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error { it, ok := pe.(*models.Cose) if !ok { return errors.New("cannot unmarshal non Cose v0.0.1 type") } var err error if err := types.DecodeEntry(it.Spec, &v.CoseObj); err != nil { return err } // field validation if err := v.CoseObj.Validate(strfmt.Default); err != nil { return err } v.keyObj, err = x509.NewPublicKey(bytes.NewReader(*v.CoseObj.PublicKey)) if err != nil { return err } // Store the envelope hash. // The CoseObj.Message is only populated during entry creation. // When marshalling from the database (retrieval) the envelope // hash must be decoded from the stored hex string. // The envelope hash is used to create the attestation key during // retrieval of a record. if len(v.CoseObj.Message) == 0 { if v.CoseObj.Data == nil || v.CoseObj.Data.EnvelopeHash == nil || v.CoseObj.Data.EnvelopeHash.Value == nil { return errors.New("envelope hash should have been previously computed") } b, err := hex.DecodeString(*v.CoseObj.Data.EnvelopeHash.Value) if err != nil { return err } v.envelopeHash = b } else { h := sha256.Sum256(v.CoseObj.Message) v.envelopeHash = h[:] } return v.validate() } func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) { if v.keyObj == nil { return nil, errors.New("cannot canonicalze empty key") } pk, err := v.keyObj.CanonicalValue() if err != nil { return nil, err } pkb := strfmt.Base64(pk) h := sha256.Sum256([]byte(v.sign1Msg.Payload)) canonicalEntry := models.CoseV001Schema{ PublicKey: &pkb, Data: &models.CoseV001SchemaData{ PayloadHash: &models.CoseV001SchemaDataPayloadHash{ Algorithm: swag.String(models.CoseV001SchemaDataPayloadHashAlgorithmSha256), Value: swag.String(hex.EncodeToString(h[:])), }, EnvelopeHash: &models.CoseV001SchemaDataEnvelopeHash{ Algorithm: swag.String(models.CoseV001SchemaDataEnvelopeHashAlgorithmSha256), Value: swag.String(hex.EncodeToString(v.envelopeHash)), }, }, } itObj := models.Cose{} itObj.APIVersion = swag.String(APIVERSION) itObj.Spec = &canonicalEntry return json.Marshal(&itObj) } // validate performs cross-field validation for fields in object func (v *V001Entry) validate() error { // This also gets called in the CLI, where we won't have this data // or during record retrieval (message is the raw COSE object) which // is only stored as an attestation. if len(v.CoseObj.Message) == 0 { return nil } alg, pk, err := getPublicKey(v.keyObj) if err != nil { return err } bv, err := gocose.NewVerifier(alg, pk) if err != nil { return err } sign1Msg := gocose.NewSign1Message() if err := sign1Msg.UnmarshalCBOR(v.CoseObj.Message); err != nil { return err } if err := sign1Msg.Verify(v.CoseObj.Data.Aad, bv); err != nil { return err } v.sign1Msg = sign1Msg return nil } func getPublicKey(pk pki.PublicKey) (gocose.Algorithm, crypto.PublicKey, error) { invAlg := gocose.Algorithm(0) x5pk, ok := pk.(*x509.PublicKey) if !ok { return invAlg, nil, errors.New("invalid public key type") } cryptoPub := x5pk.CryptoPubKey() var alg gocose.Algorithm switch t := cryptoPub.(type) { case *rsa.PublicKey: alg = gocose.AlgorithmPS256 case *ecdsa.PublicKey: alg = gocose.AlgorithmES256 if t.Params().Name != CurveP256 { return invAlg, nil, fmt.Errorf("unsupported elliptic curve %s", t.Params().Name) } default: return invAlg, nil, fmt.Errorf("unsupported algorithm type %T", t) } return alg, cryptoPub, nil } // AttestationKey returns the digest of the COSE envelope that was uploaded, // to be used to lookup the attestation from storage. func (v *V001Entry) AttestationKey() string { return fmt.Sprintf("%s:%s", models.CoseV001SchemaDataEnvelopeHashAlgorithmSha256, hex.EncodeToString(v.envelopeHash)) } // AttestationKeyValue returns both the key and value to be persisted // into attestation storage func (v *V001Entry) AttestationKeyValue() (string, []byte) { storageSize := len(v.CoseObj.Message) if storageSize > viper.GetInt("max_attestation_size") { log.Logger.Infof("Skipping attestation storage, size %d is greater than max %d", storageSize, viper.GetInt("max_attestation_size")) return "", nil } return v.AttestationKey(), v.CoseObj.Message } func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) { returnVal := models.Cose{} var err error messageBytes := props.ArtifactBytes if messageBytes == nil { if props.ArtifactPath == nil { return nil, errors.New("path to artifact file must be specified") } if props.ArtifactPath.IsAbs() { return nil, errors.New("cose envelopes cannot be fetched over HTTP(S)") } messageBytes, err = os.ReadFile(filepath.Clean(props.ArtifactPath.Path)) if err != nil { return nil, err } } publicKeyBytes := props.PublicKeyBytes if len(publicKeyBytes) == 0 { if len(props.PublicKeyPaths) != 1 { return nil, errors.New("only one public key must be provided to verify signature") } keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path)) if err != nil { return nil, fmt.Errorf("error reading public key file: %w", err) } publicKeyBytes = append(publicKeyBytes, keyBytes) } else if len(publicKeyBytes) != 1 { return nil, errors.New("only one public key must be provided") } kb := strfmt.Base64(publicKeyBytes[0]) mb := strfmt.Base64(messageBytes) re := V001Entry{ CoseObj: models.CoseV001Schema{ Data: &models.CoseV001SchemaData{ Aad: props.AdditionalAuthenticatedData, }, PublicKey: &kb, Message: mb, }, } returnVal.Spec = re.CoseObj returnVal.APIVersion = swag.String(re.APIVersion()) return &returnVal, nil } func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { if v.CoseObj.PublicKey == nil { return nil, errors.New("cose v0.0.1 entry not initialized") } key, err := x509.NewPublicKey(bytes.NewReader(*v.CoseObj.PublicKey)) if err != nil { return nil, err } return []pki.PublicKey{key}, nil } func (v V001Entry) ArtifactHash() (string, error) { if v.CoseObj.Data == nil || v.CoseObj.Data.PayloadHash == nil || v.CoseObj.Data.PayloadHash.Value == nil || v.CoseObj.Data.PayloadHash.Algorithm == nil { return "", errors.New("cose v0.0.1 entry not initialized") } return strings.ToLower(fmt.Sprintf("%s:%s", *v.CoseObj.Data.PayloadHash.Algorithm, *v.CoseObj.Data.PayloadHash.Value)), nil } func (v V001Entry) Insertable() (bool, error) { if len(v.CoseObj.Message) == 0 { return false, errors.New("missing COSE Sign1 message") } if v.CoseObj.PublicKey == nil || len(*v.CoseObj.PublicKey) == 0 { return false, errors.New("missing public key") } if v.CoseObj.Data == nil { return false, errors.New("missing COSE data property") } if len(v.envelopeHash) == 0 { return false, errors.New("envelope hash has not been computed") } if v.keyObj == nil { return false, errors.New("public key has not been parsed") } if v.sign1Msg == nil { return false, errors.New("signature has not been validated") } return true, nil } rekor-1.3.5/pkg/types/cose/v0.0.1/entry_test.go000066400000000000000000000542451455727245600211240ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cose import ( "bytes" "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/x509" "encoding/hex" "encoding/pem" "fmt" "math/big" "os" "reflect" "testing" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki/identity" sigx509 "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/types" "github.com/spf13/viper" gocose "github.com/veraison/go-cose" "go.uber.org/goleak" ) const ( pubKeyP256 = `-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5P3tzcNDA11znnCFF3DHLwiHNCl3 OXbUFakqff3cSRd4OTH1hiJgi15VIGSKZALlqjdWpf+fs87uRpiI6Yp59A== -----END PUBLIC KEY----- ` pubKeyP384 = `-----BEGIN PUBLIC KEY----- MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEPx88tPXP1ggkZHXnvg0vQAQ3vBlpKhF0 hVt3kEn4ug3o72Wa1JnJALuOALGn4tY5Xuv9jx4BG+DzbAcyMbC3ueuw6ppQcNEu YJtZ/ty5vUBCekso165mLmAK+l5UXWTq -----END PUBLIC KEY----- ` pubKeyP521 = `-----BEGIN PUBLIC KEY----- MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBRuRK30vNm09kt7AqbEtyZ4csZ943 5zgNvcYlqO9GOPA5rUu8lvjbwiELR4WPr9lzofDJY/I7gq8Hzdnl6snlyycBabpQ Ndanm2XueC84SStD3ElF6JzjsD9QGljaVYWek6to/8luw5+1niH3hNDEw5jsqa2W /r+0gL0QOCKvVsThqp4= -----END PUBLIC KEY----- ` pubKeyRSA2048 = `-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuqO4gwscYmCE3P8eM9eg yIiElLNAzjWapPn99/uFAFKqkinGr/DAejP2zxgdXk+ESd8bO0Rqob1WZL8/HqQN 8kRkf2KfR7d6jFe06V7N/Fmh+3YCcNNS6K9eW86u31sjnszgdtmWDrXhsH+M0W8g Q7rmo+7BUJAcU39iApN2GNsji6vrRLRiEnMP/fpnsLa8qYpPToSE0YVfWrKOvY2q Qhg/LceADsJzdYP0Yp+Q2jdC1J5OvUC4Mq08YdD7EawWJ5JI2qEkcPgPn5SqPomS ihKHDVzm+FqHEbgx0P57ZdKnk8kALNz5FFdwq46mbY8FRqGD56r4sB5rRcxy0cbB EQIDAQAB -----END PUBLIC KEY----- ` pubKeyEd25519 = `-----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEABhjHE6AOa33q2JGlVk9OjICRp2S6d9nUJh0Xr6PUego= -----END PUBLIC KEY----- ` ) type testPublicKey int func (t testPublicKey) CanonicalValue() ([]byte, error) { return nil, nil } func (t testPublicKey) EmailAddresses() []string { return nil } func (t testPublicKey) Subjects() []string { return nil } func (t testPublicKey) Identities() ([]identity.Identity, error) { return nil, nil } func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestNewEntryReturnType(t *testing.T) { entry := NewEntry() if reflect.TypeOf(entry) != reflect.ValueOf(&V001Entry{}).Type() { t.Errorf("invalid type returned from NewEntry: %T", entry) } } func p(b []byte) *strfmt.Base64 { b64 := strfmt.Base64(b) return &b64 } func makeSignedCose(t *testing.T, priv *ecdsa.PrivateKey, payload, aad []byte, contentType interface{}) []byte { m := gocose.NewSign1Message() m.Payload = payload m.Headers.Protected[gocose.HeaderLabelAlgorithm] = gocose.AlgorithmES256 if contentType != "" { m.Headers.Protected[gocose.HeaderLabelContentType] = contentType } signer, err := gocose.NewSigner(gocose.AlgorithmES256, priv) if err != nil { t.Fatal(err) } if err := m.Sign(rand.Reader, aad, signer); err != nil { t.Fatal(err) } msg, err := m.MarshalCBOR() if err != nil { t.Fatal(err) } return msg } func TestV001Entry_Unmarshal(t *testing.T) { priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&priv.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) ca := &x509.Certificate{ SerialNumber: big.NewInt(1), } caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &priv.PublicKey, priv) if err != nil { t.Fatal(err) } pemBytes := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: caBytes, }) msg := makeSignedCose(t, priv, []byte("hello"), nil, "") msgWithAAD := makeSignedCose(t, priv, []byte("hello"), []byte("external aad"), "") tests := []struct { name string want models.CoseV001Schema it *models.CoseV001Schema wantErr bool wantVerifierErr bool }{ { name: "empty", it: &models.CoseV001Schema{}, wantErr: true, wantVerifierErr: true, }, { name: "missing data", it: &models.CoseV001Schema{ PublicKey: p(pub), }, wantErr: true, wantVerifierErr: false, }, { name: "missing envelope", it: &models.CoseV001Schema{ Data: &models.CoseV001SchemaData{}, PublicKey: p([]byte("hello")), }, wantErr: true, wantVerifierErr: true, }, { name: "valid", it: &models.CoseV001Schema{ Data: &models.CoseV001SchemaData{}, PublicKey: p(pub), Message: msg, }, wantErr: false, wantVerifierErr: false, }, { name: "valid with aad", it: &models.CoseV001Schema{ Data: &models.CoseV001SchemaData{ Aad: strfmt.Base64("external aad"), }, PublicKey: p(pub), Message: msgWithAAD, }, wantErr: false, wantVerifierErr: false, }, { name: "extra aad", it: &models.CoseV001Schema{ Data: &models.CoseV001SchemaData{ Aad: strfmt.Base64("aad"), }, PublicKey: p(pub), Message: msg, }, wantErr: true, wantVerifierErr: false, }, { name: "invalid envelope", it: &models.CoseV001Schema{ Data: &models.CoseV001SchemaData{}, PublicKey: p([]byte(pemBytes)), Message: []byte("hello"), }, wantErr: true, wantVerifierErr: false, }, { name: "cert", it: &models.CoseV001Schema{ Data: &models.CoseV001SchemaData{}, PublicKey: p([]byte(pemBytes)), Message: msg, }, wantErr: false, wantVerifierErr: false, }, { name: "invalid key", it: &models.CoseV001Schema{ Data: &models.CoseV001SchemaData{}, PublicKey: p([]byte("notavalidkey")), Message: []byte("hello"), }, wantErr: true, wantVerifierErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { v := &V001Entry{ CoseObj: models.CoseV001Schema{}, } it := &models.Cose{ Spec: tt.it, } if err := v.Unmarshal(it); (err != nil) != tt.wantErr { t.Errorf("V001Entry.Unmarshal() error = %v, wantErr %v", err, tt.wantErr) } if !tt.wantErr { if ok, err := v.Insertable(); !ok || err != nil { t.Errorf("unexpected error calling Insertable on valid proposed entry: %v", err) } } verifiers, err := v.Verifiers() if !tt.wantVerifierErr { if err != nil { s, _ := verifiers[0].CanonicalValue() t.Errorf("%v: unexpected error for %v, got %v", tt.name, string(s), err) } if !tt.wantErr { b, err := v.Canonicalize(context.Background()) if err != nil { t.Errorf("unexpected error canonicalizing %v", tt.name) } if len(b) != 0 { pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tt.name, err) } ei, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tt.name, err) } if ok, err := ei.Insertable(); ok || err == nil { t.Errorf("entry created from canonicalized entry should not also be insertable") } hash, err := ei.ArtifactHash() expectedHash := sha256.Sum256([]byte("hello")) if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != "sha256:"+hex.EncodeToString(expectedHash[:]) { t.Errorf("unexpected match with ArtifactHash: %s", hash) } } } pubV, _ := verifiers[0].CanonicalValue() if !reflect.DeepEqual(pubV, pub) && !reflect.DeepEqual(pubV, pemBytes) { t.Errorf("verifier and public keys do not match: %v, %v", string(pubV), string(pub)) } } else if err == nil { s, _ := verifiers[0].CanonicalValue() t.Errorf("%v: expected error for %v, got %v", tt.name, string(s), err) } }) } t.Run("invalid type", func(t *testing.T) { want := "cannot unmarshal non Cose v0.0.1 type" v := V001Entry{} if err := v.Unmarshal(&types.BaseProposedEntryTester{}); err == nil { t.Error("expected error") } else if err.Error() != want { t.Errorf("wrong error: %s", err.Error()) } }) } func TestV001Entry_IndexKeys(t *testing.T) { payloadType := "application/vnd.in-toto+json" attestation := ` { "_type": "https://in-toto.io/Statement/v0.1", "predicateType": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "foo", "digest": { "sha256": "ad92c12d7947cc04000948248ccf305682f395af3e109ed044081dbb40182e6c" } } ], "predicate": { "builder": { "id": "https://example.com/test-builder" }, "buildType": "test" } } ` priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&priv.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) rawMsg := []byte(attestation) msg := makeSignedCose(t, priv, rawMsg, nil, payloadType) pk, err := sigx509.NewPublicKey(bytes.NewReader(pub)) if err != nil { t.Errorf("unexpected error: %v", err) return } v := V001Entry{ CoseObj: models.CoseV001Schema{ Message: msg, Data: &models.CoseV001SchemaData{}, PublicKey: p(pub), }, keyObj: pk, } err = v.validate() if err != nil { t.Errorf("unexpected error: %v", err) return } got, err := v.IndexKeys() if err != nil { t.Errorf("unexpected error: %v", err) } // Envelope digest sha := sha256.Sum256(msg) envDigest := "sha256:" + hex.EncodeToString(sha[:]) mustContain(t, envDigest, got) // Message digest in envelope sha = sha256.Sum256(rawMsg) rawDigest := "sha256:" + hex.EncodeToString(sha[:]) mustContain(t, rawDigest, got) // Subject from in-toto statement mustContain(t, "sha256:ad92c12d7947cc04000948248ccf305682f395af3e109ed044081dbb40182e6c", got) } func TestV001Entry_IndexKeysWrongContentType(t *testing.T) { payloadType := "application/vnd.in-toto+json" priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&priv.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) rawMsg := []byte("this is not an intoto statement") msg := makeSignedCose(t, priv, rawMsg, nil, payloadType) pk, err := sigx509.NewPublicKey(bytes.NewReader(pub)) if err != nil { t.Errorf("unexpected error: %v", err) return } v := V001Entry{ CoseObj: models.CoseV001Schema{ Message: msg, Data: &models.CoseV001SchemaData{}, PublicKey: p(pub), }, keyObj: pk, } err = v.validate() if err != nil { t.Errorf("unexpected error: %v", err) return } got, err := v.IndexKeys() if err != nil { t.Errorf("unexpected error: %v", err) } // Envelope digest sha := sha256.Sum256(msg) envDigest := "sha256:" + hex.EncodeToString(sha[:]) mustContain(t, envDigest, got) // Message digest in envelope sha = sha256.Sum256(rawMsg) rawDigest := "sha256:" + hex.EncodeToString(sha[:]) mustContain(t, rawDigest, got) } func TestV001Entry_IndexKeysIntegerContentType(t *testing.T) { priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&priv.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) rawMsg := []byte("hello") msg := makeSignedCose(t, priv, rawMsg, nil, 12345) pk, err := sigx509.NewPublicKey(bytes.NewReader(pub)) if err != nil { t.Errorf("unexpected error: %v", err) return } v := V001Entry{ CoseObj: models.CoseV001Schema{ Message: msg, Data: &models.CoseV001SchemaData{}, PublicKey: p(pub), }, keyObj: pk, } err = v.validate() if err != nil { t.Errorf("unexpected error: %v", err) return } got, err := v.IndexKeys() if err != nil { t.Errorf("unexpected error: %v", err) } // Envelope digest sha := sha256.Sum256(msg) envDigest := "sha256:" + hex.EncodeToString(sha[:]) mustContain(t, envDigest, got) // Message digest in envelope sha = sha256.Sum256(rawMsg) rawDigest := "sha256:" + hex.EncodeToString(sha[:]) mustContain(t, rawDigest, got) } func TestV001Entry_Attestation(t *testing.T) { priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&priv.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) msg := makeSignedCose(t, priv, []byte("hello"), nil, "") it := &models.Cose{ Spec: &models.CoseV001Schema{ Data: &models.CoseV001SchemaData{}, PublicKey: p(pub), Message: msg, }, } t.Run("no storage", func(t *testing.T) { v := &V001Entry{ CoseObj: models.CoseV001Schema{}, } if err := v.Unmarshal(it); err != nil { t.Errorf("V001Entry.Unmarshal() error = %v", err) } key, att := v.AttestationKeyValue() if key != "" { t.Errorf("Unexpected key returned") } if len(att) != 0 { t.Errorf("Attestation returned") } }) t.Run("with storage", func(t *testing.T) { // Need to trick viper to update config so we can return // an attestation os.Setenv("MAX_ATTESTATION_SIZE", "1048576") viper.AutomaticEnv() msgHash := sha256.Sum256(msg) wantKey := fmt.Sprintf("sha256:%s", hex.EncodeToString(msgHash[:])) v := &V001Entry{ CoseObj: models.CoseV001Schema{}, } if err := v.Unmarshal(it); err != nil { t.Errorf("V001Entry.Unmarshal() error = %v", err) } key, att := v.AttestationKeyValue() if key != wantKey { t.Errorf("unexpected attestation key: %s want: %s", key, wantKey) } if len(att) != len(msg) { t.Error("Wrong attestation returned") } for i := range att { if att[i] != msg[i] { t.Error("Wrong attestation returned") return } } }) } func TestGetPublicKey(t *testing.T) { t.Run("P256", func(t *testing.T) { pk, err := sigx509.NewPublicKey(bytes.NewBufferString(pubKeyP256)) if err != nil { t.Error("failed to load public key") } alg, cpk, err := getPublicKey(pk) if alg != gocose.AlgorithmES256 { t.Error("wrong algorithm") } if cpk == nil { t.Error("no public key returned") } if err != nil { t.Errorf("Unexpected error %s", err.Error()) } }) t.Run("P384", func(t *testing.T) { pk, err := sigx509.NewPublicKey(bytes.NewBufferString(pubKeyP384)) if err != nil { t.Error("failed to load public key") } alg, cpk, err := getPublicKey(pk) if alg != gocose.Algorithm(0) { t.Error("unexpected algorithm returned") } if cpk != nil { t.Error("unexpected key returned") } if err == nil { t.Error("expected error") } }) t.Run("P521", func(t *testing.T) { pk, err := sigx509.NewPublicKey(bytes.NewBufferString(pubKeyP521)) if err != nil { t.Error("failed to load public key") } alg, cpk, err := getPublicKey(pk) if alg != gocose.Algorithm(0) { t.Error("unexpected algorithm returned") } if cpk != nil { t.Error("unexpected key returned") } if err == nil { t.Error("expected error") } }) t.Run("RSA2048", func(t *testing.T) { pk, err := sigx509.NewPublicKey(bytes.NewBufferString(pubKeyRSA2048)) if err != nil { t.Error("failed to load public key") } alg, cpk, err := getPublicKey(pk) if alg != gocose.AlgorithmPS256 { t.Error("unexpected algorithm returned") } if cpk == nil { t.Error("no public key returned") } if err != nil { t.Error("unexpected error") } }) t.Run("Invalid key", func(t *testing.T) { alg, cpk, err := getPublicKey(testPublicKey(0)) if alg != gocose.Algorithm(0) { t.Error("unexpected algorithm returned") } if cpk != nil { t.Error("unexpected key returned") } if err == nil { t.Error("expected error") } }) t.Run("Ed25519", func(t *testing.T) { pk, err := sigx509.NewPublicKey(bytes.NewBufferString(pubKeyEd25519)) if err != nil { t.Error("failed to load public key") } alg, cpk, err := getPublicKey(pk) if alg != gocose.Algorithm(0) { t.Error("unexpected algorithm returned") } if cpk != nil { t.Error("unexpected key returned") } if err == nil { t.Error("expected error") } if err.Error() != "unsupported algorithm type ed25519.PublicKey" { t.Error("expected error") } }) } func TestV001Entry_Validate(t *testing.T) { t.Run("missing message", func(t *testing.T) { v := V001Entry{} err := v.validate() if err != nil { t.Error("unexpected error") } }) t.Run("invalid public key", func(t *testing.T) { v := V001Entry{} v.CoseObj.Message = []byte("string") v.keyObj, _ = sigx509.NewPublicKey(bytes.NewBufferString(pubKeyEd25519)) err := v.validate() if err == nil { t.Error("expected error") return } if err.Error() != "unsupported algorithm type ed25519.PublicKey" { t.Error("wrong error returned") } }) } func mustContain(t *testing.T, want string, l []string) { for _, s := range l { if s == want { return } } t.Fatalf("list %v does not contain %s", l, want) } func TestInsertable(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectSuccess bool } key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) keyObj, err := sigx509.NewPublicKey(bytes.NewReader(pub)) if err != nil { t.Fatal(err) } pubKey := strfmt.Base64(pub) testCases := []TestCase{ { caseDesc: "valid entry", entry: V001Entry{ CoseObj: models.CoseV001Schema{ Data: &models.CoseV001SchemaData{ Aad: strfmt.Base64([]byte("aad")), }, Message: strfmt.Base64([]byte("message")), PublicKey: &pubKey, }, keyObj: keyObj, sign1Msg: &gocose.Sign1Message{}, envelopeHash: []byte("hash"), }, expectSuccess: true, }, { caseDesc: "no aad", entry: V001Entry{ CoseObj: models.CoseV001Schema{ Data: &models.CoseV001SchemaData{}, Message: strfmt.Base64([]byte("message")), PublicKey: &pubKey, }, keyObj: keyObj, sign1Msg: &gocose.Sign1Message{}, envelopeHash: []byte("hash"), }, expectSuccess: true, }, { caseDesc: "missing hash", entry: V001Entry{ CoseObj: models.CoseV001Schema{ Data: &models.CoseV001SchemaData{}, Message: strfmt.Base64([]byte("message")), PublicKey: &pubKey, }, keyObj: keyObj, sign1Msg: &gocose.Sign1Message{}, //envelopeHash: []byte("hash"), }, expectSuccess: false, }, { caseDesc: "unparsed message", entry: V001Entry{ CoseObj: models.CoseV001Schema{ Data: &models.CoseV001SchemaData{ Aad: strfmt.Base64([]byte("aad")), }, Message: strfmt.Base64([]byte("message")), PublicKey: &pubKey, }, keyObj: keyObj, //sign1Msg: &gocose.Sign1Message{}, envelopeHash: []byte("hash"), }, expectSuccess: false, }, { caseDesc: "unparsed public key", entry: V001Entry{ CoseObj: models.CoseV001Schema{ Data: &models.CoseV001SchemaData{ Aad: strfmt.Base64([]byte("aad")), }, Message: strfmt.Base64([]byte("message")), PublicKey: &pubKey, }, //keyObj: keyObj, sign1Msg: &gocose.Sign1Message{}, envelopeHash: []byte("hash"), }, expectSuccess: false, }, { caseDesc: "missing public key", entry: V001Entry{ CoseObj: models.CoseV001Schema{ Data: &models.CoseV001SchemaData{ Aad: strfmt.Base64([]byte("aad")), }, Message: strfmt.Base64([]byte("message")), //PublicKey: &pubKey, }, keyObj: keyObj, sign1Msg: &gocose.Sign1Message{}, envelopeHash: []byte("hash"), }, expectSuccess: false, }, { caseDesc: "missing unparsed message", entry: V001Entry{ CoseObj: models.CoseV001Schema{ Data: &models.CoseV001SchemaData{ Aad: strfmt.Base64([]byte("aad")), }, //Message: strfmt.Base64([]byte("message")), PublicKey: &pubKey, }, keyObj: keyObj, sign1Msg: &gocose.Sign1Message{}, envelopeHash: []byte("hash"), }, expectSuccess: false, }, { caseDesc: "missing data", entry: V001Entry{ CoseObj: models.CoseV001Schema{ /* Data: &models.CoseV001SchemaData{ Aad: strfmt.Base64([]byte("aad")), }, */ Message: strfmt.Base64([]byte("message")), PublicKey: &pubKey, }, keyObj: keyObj, sign1Msg: &gocose.Sign1Message{}, envelopeHash: []byte("hash"), }, expectSuccess: false, }, { caseDesc: "missing cose obj", entry: V001Entry{ /* CoseObj: models.CoseV001Schema{ Data: &models.CoseV001SchemaData{ Aad: strfmt.Base64([]byte("aad")), }, Message: strfmt.Base64([]byte("message")), PublicKey: &pubKey, }, */ keyObj: keyObj, sign1Msg: &gocose.Sign1Message{}, envelopeHash: []byte("hash"), }, expectSuccess: false, }, { caseDesc: "empty obj", entry: V001Entry{ /* CoseObj: models.CoseV001Schema{ Data: &models.CoseV001SchemaData{ Aad: strfmt.Base64([]byte("aad")), }, Message: strfmt.Base64([]byte("message")), PublicKey: &pubKey, }, keyObj: keyObj, sign1Msg: &gocose.Sign1Message{}, envelopeHash: []byte("hash"), */ }, expectSuccess: false, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess { t.Errorf("unexpected result calling Insertable: %v", err) } }) } } rekor-1.3.5/pkg/types/cose/v0.0.1/fuzz_test.go000066400000000000000000000046611455727245600207560ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cose import ( "context" "sync" "testing" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/go-openapi/swag" fuzzUtils "github.com/sigstore/rekor/pkg/fuzz" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/cose" ) var initter sync.Once func FuzzCoseCreateProposedEntry(f *testing.F) { f.Fuzz(func(t *testing.T, propsData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) version := "0.0.1" ff := fuzz.NewConsumer(propsData) props, cleanup, err := fuzzUtils.CreateProps(ff, "coseV001") if err != nil { t.Skip() } defer func() { for _, c := range cleanup { c() } }() it := cose.New() entry, err := it.CreateProposedEntry(context.Background(), version, props) if err != nil { t.Skip() } ei, err := types.CreateVersionedEntry(entry) if err != nil { t.Skip() } if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err) } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("valid insertable entry should be able to be canonicalized: %v", err) } _, _ = ei.IndexKeys() }) } func FuzzCoseUnmarshalAndCanonicalize(f *testing.F) { f.Fuzz(func(t *testing.T, entryData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(entryData) targetV001 := &models.CoseV001Schema{} if err := ff.GenerateStruct(targetV001); err != nil { t.Skip() } targetEntry := &models.Cose{ APIVersion: swag.String(APIVERSION), Spec: targetV001, } ei, err := types.UnmarshalEntry(targetEntry) if err != nil { t.Skip() } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("error canonicalizing unmarshalled entry: %v", err) } }) } rekor-1.3.5/pkg/types/dsse/000077500000000000000000000000001455727245600155465ustar00rootroot00000000000000rekor-1.3.5/pkg/types/dsse/README.md000066400000000000000000000022031455727245600170220ustar00rootroot00000000000000**DSSE Type Data Documentation** This document provides a definition for each field that is not otherwise described in the [dsse schema](https://github.com/sigstore/rekor/blob/main/pkg/types/dsse/v0.0.1/dsse_v0_0_1_schema.json). This document also notes any additional information about the values associated with each field such as the format in which the data is stored and any necessary transformations. **How do you identify an object as an DSSE object?** The "Body" field will include an "dsseObj" field. **Recognized content types** - [in-toto statements](https://github.com/in-toto/attestation/tree/main/spec#statement) are recognized and parsed. The found subject hashes are indexed so they can be searched for. **What data about the envelope is stored in Rekor** Only the hash of the payload (the content covered by the digital signature inside the envelope), the hash of the entire DSSE envelope (including signatures), the signature(s) and their corresponding verifying materials (e.g. public key(s) or certificates) are stored. Even if Rekor is configured to use attestation storage, the entire DSSE envelope will not be stored. rekor-1.3.5/pkg/types/dsse/dsse.go000066400000000000000000000034331455727245600170360ustar00rootroot00000000000000// // Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package dsse import ( "context" "errors" "fmt" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" ) const ( KIND = "dsse" ) type BaseDSSEType struct { types.RekorType } func init() { types.TypeMap.Store(KIND, New) } func New() types.TypeImpl { bit := BaseDSSEType{} bit.Kind = KIND bit.VersionMap = VersionMap return &bit } var VersionMap = types.NewSemVerEntryFactoryMap() func (it BaseDSSEType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) { if pe == nil { return nil, errors.New("proposed entry cannot be nil") } in, ok := pe.(*models.DSSE) if !ok { return nil, errors.New("cannot unmarshal non-DSSE types") } return it.VersionedUnmarshal(in, *in.APIVersion) } func (it *BaseDSSEType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) { if version == "" { version = it.DefaultVersion() } ei, err := it.VersionedUnmarshal(nil, version) if err != nil { return nil, fmt.Errorf("fetching DSSE version implementation: %w", err) } return ei.CreateFromArtifactProperties(ctx, props) } func (it BaseDSSEType) DefaultVersion() string { return "0.0.1" } rekor-1.3.5/pkg/types/dsse/dsse_schema.json000066400000000000000000000005141455727245600207170ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/dsse/dsse_schema.json", "title": "DSSE Schema", "description": "log entry schema for dsse envelopes", "type": "object", "oneOf": [ { "$ref": "v0.0.1/dsse_v0_0_1_schema.json" } ] } rekor-1.3.5/pkg/types/dsse/dsse_test.go000066400000000000000000000111231455727245600200700ustar00rootroot00000000000000// // Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package dsse import ( "context" "errors" "testing" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" ) type UnmarshalTester struct { models.DSSE types.BaseUnmarshalTester } type UnmarshalFailsTester struct { types.BaseUnmarshalTester } func (u UnmarshalFailsTester) NewEntry() types.EntryImpl { return &UnmarshalFailsTester{} } func (u UnmarshalFailsTester) Unmarshal(_ models.ProposedEntry) error { return errors.New("error") } func (u UnmarshalFailsTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } func TestDSSEType(t *testing.T) { // empty to start if VersionMap.Count() != 0 { t.Error("semver range was not blank at start of test") } u := UnmarshalTester{} // ensure semver range parser is working invalidSemVerRange := "not a valid semver range" err := VersionMap.SetEntryFactory(invalidSemVerRange, u.NewEntry) if err == nil || VersionMap.Count() > 0 { t.Error("invalid semver range was incorrectly added to SemVerToFacFnMap") } // valid semver range can be parsed err = VersionMap.SetEntryFactory(">= 1.2.3", u.NewEntry) if err != nil || VersionMap.Count() != 1 { t.Error("valid semver range was not added to SemVerToFacFnMap") } u.DSSE.APIVersion = swag.String("2.0.1") brt := New() // version requested matches implementation in map if _, err := brt.UnmarshalEntry(&u.DSSE); err != nil { t.Errorf("unexpected error in Unmarshal: %v", err) } // version requested fails to match implementation in map u.DSSE.APIVersion = swag.String("1.2.2") if _, err := brt.UnmarshalEntry(&u.DSSE); err == nil { t.Error("unexpected success in Unmarshal for non-matching version") } // error in Unmarshal call is raised appropriately u.DSSE.APIVersion = swag.String("2.2.0") u2 := UnmarshalFailsTester{} _ = VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry) if _, err := brt.UnmarshalEntry(&u.DSSE); err == nil { t.Error("unexpected success in Unmarshal when error is thrown") } // version requested fails to match implementation in map u.DSSE.APIVersion = swag.String("not_a_version") if _, err := brt.UnmarshalEntry(&u.DSSE); err == nil { t.Error("unexpected success in Unmarshal for invalid version") } ti, err := brt.UnmarshalEntry(nil) if ti != nil { t.Error("unexpected success in unmarshal for nil") } if err == nil { t.Error("expected error") } ti, err = brt.UnmarshalEntry(types.BaseProposedEntryTester{}) if ti != nil { t.Error("unexpected success in unmarshal for nil") } if err == nil { t.Error("expected error") } } func TestDSSEDefaultVersion(t *testing.T) { brt := New() ver := brt.DefaultVersion() if ver != "0.0.1" { t.Errorf("unexpected default version %s", ver) } } func TestDSSECreateProposedEntry(t *testing.T) { // Reset semver map VersionMap = types.NewSemVerEntryFactoryMap() u := UnmarshalTester{} VersionMap.SetEntryFactory("0.0.1", u.NewEntry) VersionMap.SetEntryFactory(New().DefaultVersion(), u.NewEntry) t.Run("unknown version", func(t *testing.T) { ctx := context.Background() brt := New() props := types.ArtifactProperties{} pe, err := brt.CreateProposedEntry(ctx, "1.2.3", props) if pe != nil { t.Error("unexpected proposed entry") } if err == nil { t.Error("expected error") } }) t.Run("valid version", func(t *testing.T) { ctx := context.Background() brt := New() props := types.ArtifactProperties{} pe, err := brt.CreateProposedEntry(ctx, "0.0.1", props) // BaseUnmarshalTester returns nil for the proposed entry if pe != nil { t.Error("unexpected proposed entry") } if err != nil { t.Error("unexpected error") } }) t.Run("default version", func(t *testing.T) { ctx := context.Background() brt := New() props := types.ArtifactProperties{} pe, err := brt.CreateProposedEntry(ctx, "", props) // BaseUnmarshalTester returns nil for the proposed entry if pe != nil { t.Error("unexpected proposed entry") } if err != nil { t.Error("unexpected error") } }) } rekor-1.3.5/pkg/types/dsse/v0.0.1/000077500000000000000000000000001455727245600163705ustar00rootroot00000000000000rekor-1.3.5/pkg/types/dsse/v0.0.1/dsse_v0_0_1_schema.json000066400000000000000000000076161455727245600226170ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/dsse/dsse_v0_0_1_schema.json", "title": "DSSE v0.0.1 Schema", "description": "Schema for DSSE envelopes", "type": "object", "properties": { "proposedContent": { "type": "object", "properties": { "envelope": { "description": "DSSE envelope specified as a stringified JSON object", "type": "string", "writeOnly": true }, "verifiers": { "description": "collection of all verification material (e.g. public keys or certificates) used to verify signatures over envelope's payload, specified as base64-encoded strings", "type": "array", "minItems": 1, "items": { "type": "string", "format": "byte" }, "writeOnly": true } }, "writeOnly": true, "required": [ "envelope", "verifiers" ] }, "signatures": { "description": "extracted collection of all signatures of the envelope's payload; elements will be sorted by lexicographical order of the base64 encoded signature strings", "type": "array", "minItems": 1, "items": { "description": "a signature of the envelope's payload along with the verification material for the signature", "type": "object", "properties": { "signature": { "description": "base64 encoded signature of the payload", "type": "string", "pattern": "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" }, "verifier": { "description": "verification material that was used to verify the corresponding signature, specified as a base64 encoded string", "type": "string", "format": "byte" } }, "required": [ "signature", "verifier" ] }, "readOnly": true }, "envelopeHash": { "description": "Specifies the hash algorithm and value encompassing the entire envelope sent to Rekor", "type": "object", "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The value of the computed digest over the entire envelope", "type": "string" } }, "required": [ "algorithm", "value" ], "readOnly": true }, "payloadHash": { "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope", "type": "object", "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The value of the computed digest over the payload within the envelope", "type": "string" } }, "required": [ "algorithm", "value" ], "readOnly": true } }, "oneOf": [ { "required": [ "proposedContent" ] }, { "required": [ "signatures", "envelopeHash", "payloadHash" ] } ] } rekor-1.3.5/pkg/types/dsse/v0.0.1/e2e_test.go000066400000000000000000000522751455727245600204440ustar00rootroot00000000000000// // Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package dsse import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/json" "encoding/pem" "fmt" "os" "path/filepath" "strings" "testing" "time" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/in-toto/in-toto-golang/in_toto" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/rekor/pkg/client" "github.com/sigstore/rekor/pkg/generated/client/entries" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/sigstore/pkg/signature" sigx509 "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/util" ) func rekorServer() string { if s := os.Getenv("REKOR_SERVER"); s != "" { return s } return "http://localhost:3000" } func GenerateSingleSignedDSSE(t *testing.T) ([]byte, []byte) { t.Helper() // Get some random data so it's unique each run d := util.RandomData(t, 10) id := base64.StdEncoding.EncodeToString(d) it := in_toto.ProvenanceStatement{ StatementHeader: in_toto.StatementHeader{ Type: in_toto.StatementInTotoV01, PredicateType: slsa.PredicateSLSAProvenance, Subject: []in_toto.Subject{ { Name: "foobar", Digest: common.DigestSet{ "foo": "bar", }, }, }, }, Predicate: slsa.ProvenancePredicate{ Builder: common.ProvenanceBuilder{ ID: "foo" + id, }, }, } b, err := json.Marshal(it) if err != nil { t.Fatal(err) } pb, _ := pem.Decode([]byte(sigx509.ECDSAPriv)) priv, err := x509.ParsePKCS8PrivateKey(pb.Bytes) if err != nil { t.Fatal(err) } s, err := signature.LoadECDSASigner(priv.(*ecdsa.PrivateKey), crypto.SHA256) if err != nil { t.Fatal(err) } signer, err := dsse.NewEnvelopeSigner(&sigx509.Verifier{ S: s, }) if err != nil { t.Fatal(err) } env, err := signer.SignPayload(context.Background(), in_toto.PayloadType, b) if err != nil { t.Fatal(err) } eb, err := json.Marshal(env) if err != nil { t.Fatal(err) } return b, eb } func GenerateDoubleSignedDSSE(t *testing.T) ([]byte, []byte) { t.Helper() // Get some random data so it's unique each run d := util.RandomData(t, 10) id := base64.StdEncoding.EncodeToString(d) it := in_toto.ProvenanceStatement{ StatementHeader: in_toto.StatementHeader{ Type: in_toto.StatementInTotoV01, PredicateType: slsa.PredicateSLSAProvenance, Subject: []in_toto.Subject{ { Name: "foobar", Digest: common.DigestSet{ "foo": "bar", }, }, }, }, Predicate: slsa.ProvenancePredicate{ Builder: common.ProvenanceBuilder{ ID: "foo" + id, }, }, } b, err := json.Marshal(it) if err != nil { t.Fatal(err) } evps := []*sigx509.Verifier{} pb, _ := pem.Decode([]byte(sigx509.ECDSAPriv)) priv, err := x509.ParsePKCS8PrivateKey(pb.Bytes) if err != nil { t.Fatal(err) } signECDSA, err := signature.LoadECDSASigner(priv.(*ecdsa.PrivateKey), crypto.SHA256) if err != nil { t.Fatal(err) } evps = append(evps, &sigx509.Verifier{ S: signECDSA, }) pbRSA, _ := pem.Decode([]byte(sigx509.RSAKey)) rsaPriv, err := x509.ParsePKCS8PrivateKey(pbRSA.Bytes) if err != nil { t.Fatal(err) } signRSA, err := signature.LoadRSAPKCS1v15Signer(rsaPriv.(*rsa.PrivateKey), crypto.SHA256) if err != nil { t.Fatal(err) } evps = append(evps, &sigx509.Verifier{ S: signRSA, }) signer, err := dsse.NewEnvelopeSigner(evps[0], evps[1]) if err != nil { t.Fatal(err) } env, err := signer.SignPayload(context.Background(), in_toto.PayloadType, b) if err != nil { t.Fatal(err) } eb, err := json.Marshal(env) if err != nil { t.Fatal(err) } return b, eb } func TestDsse(t *testing.T) { td := t.TempDir() attestationPath := filepath.Join(td, "attestation.json") pubKeyPath := filepath.Join(td, "pub.pem") b, eb := GenerateSingleSignedDSSE(t) util.Write(t, string(eb), attestationPath) util.Write(t, sigx509.ECDSAPub, pubKeyPath) out := util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "dsse", "--public-key", pubKeyPath) util.OutputContains(t, out, "Created entry at") uuid := util.GetUUIDFromUploadOutput(t, out) out = util.RunCli(t, "get", "--uuid", uuid, "--format=json") g := util.GetOut{} if err := json.Unmarshal([]byte(out), &g); err != nil { t.Fatal(err) } // The attestation should not be stored if len(g.Attestation) > 0 { t.Fatal("unexpected attestation present in response") } payloadHash := sha256.Sum256(b) envelopeHash := sha256.Sum256(eb) dsseModel := &models.DSSEV001Schema{} if err := types.DecodeEntry(g.Body.(map[string]interface{})["DSSEObj"], dsseModel); err != nil { t.Errorf("could not convert body into dsse type: %v", err) } if dsseModel.PayloadHash == nil { t.Errorf("could not find hash over payload %v", dsseModel) } recordedPayloadHash, err := hex.DecodeString(*dsseModel.PayloadHash.Value) if err != nil { t.Errorf("error converting payload hash to []byte: %v", err) } if !bytes.Equal(payloadHash[:], recordedPayloadHash) { t.Fatal(fmt.Errorf("payload hash %v doesnt match the payload we sent %v", hex.EncodeToString(payloadHash[:]), *dsseModel.PayloadHash.Value)) } if dsseModel.EnvelopeHash == nil { t.Errorf("could not find hash over entire envelope %v", dsseModel) } recordedEnvelopeHash, err := hex.DecodeString(*dsseModel.EnvelopeHash.Value) if err != nil { t.Errorf("error converting Envelope hash to []byte: %v", err) } if !bytes.Equal(envelopeHash[:], recordedEnvelopeHash) { t.Fatal(fmt.Errorf("envelope hash %v doesnt match the payload we sent %v", hex.EncodeToString(envelopeHash[:]), *dsseModel.EnvelopeHash.Value)) } if len(dsseModel.Signatures) != 1 { t.Fatalf("expected one signatures but got %d instead", len(dsseModel.Signatures)) } out = util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "dsse", "--public-key", pubKeyPath) util.OutputContains(t, out, "Entry already exists") } func TestDsseMultiSig(t *testing.T) { td := t.TempDir() attestationPath := filepath.Join(td, "attestation.json") ecdsapubKeyPath := filepath.Join(td, "ecdsapub.pem") rsapubKeyPath := filepath.Join(td, "rsapub.pem") b, eb := GenerateDoubleSignedDSSE(t) util.Write(t, string(eb), attestationPath) util.Write(t, sigx509.ECDSAPub, ecdsapubKeyPath) util.Write(t, sigx509.PubKey, rsapubKeyPath) out := util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "dsse", "--public-key", ecdsapubKeyPath, "--public-key", rsapubKeyPath) util.OutputContains(t, out, "Created entry at") uuid := util.GetUUIDFromUploadOutput(t, out) out = util.RunCli(t, "get", "--uuid", uuid, "--format=json") g := util.GetOut{} if err := json.Unmarshal([]byte(out), &g); err != nil { t.Fatal(err) } // The attestation should not be stored if len(g.Attestation) > 0 { t.Fatal("unexpected attestation present in response") } payloadHash := sha256.Sum256(b) envelopeHash := sha256.Sum256(eb) dsseModel := &models.DSSEV001Schema{} if err := types.DecodeEntry(g.Body.(map[string]interface{})["DSSEObj"], dsseModel); err != nil { t.Errorf("could not convert body into dsse type: %v", err) } if dsseModel.PayloadHash == nil { t.Errorf("could not find hash over payload %v", dsseModel) } recordedPayloadHash, err := hex.DecodeString(*dsseModel.PayloadHash.Value) if err != nil { t.Errorf("error converting payload hash to []byte: %v", err) } if !bytes.Equal(payloadHash[:], recordedPayloadHash) { t.Fatal(fmt.Errorf("payload hash %v doesnt match the payload we sent %v", hex.EncodeToString(payloadHash[:]), *dsseModel.PayloadHash.Value)) } if dsseModel.EnvelopeHash == nil { t.Errorf("could not find hash over envelope %v", dsseModel) } recordedEnvelopeHash, err := hex.DecodeString(*dsseModel.EnvelopeHash.Value) if err != nil { t.Errorf("error converting envelope hash to []byte: %v", err) } if !bytes.Equal(envelopeHash[:], recordedEnvelopeHash) { t.Fatal(fmt.Errorf("envelope hash %v doesnt match the payload we sent %v", hex.EncodeToString(envelopeHash[:]), *dsseModel.EnvelopeHash.Value)) } if len(dsseModel.Signatures) != 2 { t.Fatalf("expected two signatures but got %d instead", len(dsseModel.Signatures)) } out = util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "dsse", "--public-key", ecdsapubKeyPath, "--public-key", rsapubKeyPath) util.OutputContains(t, out, "Entry already exists") } func DecodeV001FromRekorResponse(t *testing.T, resp *entries.CreateLogEntryCreated) *V001Entry { t.Helper() for _, e := range resp.Payload { b, err := base64.StdEncoding.DecodeString(e.Body.(string)) if err != nil { t.Errorf("could not decode body into dsse type: %v", err) } pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { t.Errorf("could not unmarshal body into dsse type: %v", err) } eimpl, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("could not unmarshal body into dsse v0.0.1 type: %v", err) } return eimpl.(*V001Entry) } return nil } // TestSendingCanonicalizedDSSE tests uploading a valid canonicalized entry. This should fail because the type requires // the ProposedContent fields to be submitted on upload, and canonicalized entries will not have those fields persisted. func TestSendingCanonicalizedDSSE(t *testing.T) { b, eb := GenerateSingleSignedDSSE(t) sha := sha256.New() sha.Write(b) payloadHashBytes := sha.Sum(nil) payloadHashStr := hex.EncodeToString(payloadHashBytes) sha.Reset() sha.Write(eb) envelopeHashBytes := sha.Sum(nil) envelopeHashStr := hex.EncodeToString(envelopeHashBytes) ap := types.ArtifactProperties{ ArtifactBytes: eb, PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub)}, } ei := NewEntry() entry, err := ei.CreateFromArtifactProperties(context.Background(), ap) if err != nil { t.Fatalf("error creating entry: %v", err) } dsse_entry := entry.(*models.DSSE) v001 := dsse_entry.Spec.(models.DSSEV001Schema) v001.EnvelopeHash = &models.DSSEV001SchemaEnvelopeHash{ Algorithm: swag.String("sha256"), Value: swag.String(envelopeHashStr), } v001.PayloadHash = &models.DSSEV001SchemaPayloadHash{ Algorithm: swag.String("sha256"), Value: swag.String(payloadHashStr), } env := &dsse.Envelope{} if err := json.Unmarshal([]byte(*v001.ProposedContent.Envelope), env); err != nil { t.Fatalf("error extracting DSSE envelope") } pk := v001.ProposedContent.Verifiers[0] v001.Signatures = []*models.DSSEV001SchemaSignaturesItems0{ { Signature: &env.Signatures[0].Sig, Verifier: &pk, }, } // erase proposed content and overwrite previous v001.ProposedContent = nil dsse_entry.Spec = v001 rc, err := client.GetRekorClient(rekorServer()) if err != nil { t.Errorf("error getting client: %v", err) } params := &entries.CreateLogEntryParams{} params.SetProposedEntry(dsse_entry) params.SetContext(context.Background()) params.SetTimeout(5 * time.Second) if _, err = rc.Entries.CreateLogEntry(params); err == nil { t.Fatalf("expected error submitting canonicalized entry to rekor") } e, ok := err.(*entries.CreateLogEntryBadRequest) if !ok { t.Errorf("unexpected error returned from rekor: %v", err.Error()) } if !strings.Contains(e.Payload.Message, "missing proposed content") { t.Errorf("expected error message to include 'missing proposed content': %v", e.Payload.Message) } } // TestSendingEntryWithClientComputedHashes tests uploading a valid entry with incorrect client-computed digests // over the entire envelope and payload. The hashes should be computed server side, and the request should be rejected func TestSendingEntryWithClientComputedHashes(t *testing.T) { b, eb := GenerateSingleSignedDSSE(t) sha := sha256.New() sha.Write(b) payloadHashBytes := sha.Sum(nil) payloadHashStr := hex.EncodeToString(payloadHashBytes) t.Logf(payloadHashStr) sha.Reset() sha.Write(eb) envelopeHashBytes := sha.Sum(nil) envelopeHashStr := hex.EncodeToString(envelopeHashBytes) t.Logf(envelopeHashStr) ap := types.ArtifactProperties{ ArtifactBytes: eb, PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub)}, } ei := NewEntry() entry, err := ei.CreateFromArtifactProperties(context.Background(), ap) if err != nil { t.Fatalf("error creating entry: %v", err) } dsse_entry := entry.(*models.DSSE) v001 := dsse_entry.Spec.(models.DSSEV001Schema) v001.EnvelopeHash = &models.DSSEV001SchemaEnvelopeHash{ Algorithm: swag.String("sha256"), Value: swag.String("8810ad581e59f2bc3928b261707a71308f7e139eb04820366dc4d5c18d980225"), } v001.PayloadHash = &models.DSSEV001SchemaPayloadHash{ Algorithm: swag.String("sha256"), Value: swag.String("8810ad581e59f2bc3928b261707a71308f7e139eb04820366dc4d5c18d980225"), } dsse_entry.Spec = v001 rc, err := client.GetRekorClient(rekorServer()) if err != nil { t.Errorf("error getting client: %v", err) } params := &entries.CreateLogEntryParams{} params.SetProposedEntry(dsse_entry) params.SetContext(context.Background()) params.SetTimeout(5 * time.Second) _, err = rc.Entries.CreateLogEntry(params) if err == nil { t.Error("expected error uploading bad entry to Rekor") } e, ok := err.(*entries.CreateLogEntryBadRequest) if !ok { t.Errorf("unexpected error returned from rekor: %v", err.Error()) } if !strings.Contains(e.Payload.Message, "either proposedContent or envelopeHash, payloadHash, and signatures must be present but not both") { t.Errorf("unexpected error message returned: %v", e.Payload.Message) } } // TestMismatchedKeySingleSigner tests uploading a valid entry with the incorrect public key; this should be rejected by Rekor func TestMismatchedKeySingleSigner(t *testing.T) { _, eb := GenerateSingleSignedDSSE(t) ap := types.ArtifactProperties{ ArtifactBytes: eb, PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub)}, // this is the matching key, we will swap it out momentarily } ei := NewEntry() entry, err := ei.CreateFromArtifactProperties(context.Background(), ap) if err != nil { t.Fatalf("error creating entry: %v", err) } // swap out good public key for mismatched one dsse_entry := entry.(*models.DSSE) v001 := dsse_entry.Spec.(models.DSSEV001Schema) v001.ProposedContent.Verifiers[0] = strfmt.Base64(sigx509.RSACert) dsse_entry.Spec = v001 rc, err := client.GetRekorClient(rekorServer()) if err != nil { t.Errorf("error getting client: %v", err) } params := &entries.CreateLogEntryParams{} params.SetProposedEntry(dsse_entry) params.SetContext(context.Background()) params.SetTimeout(5 * time.Second) if _, err = rc.Entries.CreateLogEntry(params); err == nil { t.Fatalf("expected error submitting canonicalized entry to rekor") } e, ok := err.(*entries.CreateLogEntryBadRequest) if !ok { t.Errorf("unexpected error returned from rekor: %v", err.Error()) } if !strings.Contains(e.Payload.Message, "could not verify envelope") { t.Errorf("expected error message to include 'could not verify envelope': %v", e.Payload.Message) } } // TestNoSignature tests sending a valid JSON object as the DSSE envelope, but one that omits the // signature. This should not be accepted by Rekor. func TestNoSignature(t *testing.T) { _, eb := GenerateSingleSignedDSSE(t) ap := types.ArtifactProperties{ ArtifactBytes: eb, PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub)}, //, []byte(sigx509.ECDSAPub)}, } ei := NewEntry() entry, err := ei.CreateFromArtifactProperties(context.Background(), ap) if err != nil { t.Fatalf("error creating entry: %v", err) } dsse_entry := entry.(*models.DSSE) v001 := dsse_entry.Spec.(models.DSSEV001Schema) env := &dsse.Envelope{} if err := json.Unmarshal([]byte(*v001.ProposedContent.Envelope), env); err != nil { t.Fatalf("error extracting DSSE envelope") } // remove signature env.Signatures = []dsse.Signature{} noSigEnv, err := json.Marshal(env) if err != nil { t.Fatalf("error marshalling sorted DSSE envelope") } v001.ProposedContent.Envelope = swag.String(string(noSigEnv)) dsse_entry.Spec = v001 rc, err := client.GetRekorClient(rekorServer()) if err != nil { t.Errorf("error getting client: %v", err) } params := &entries.CreateLogEntryParams{} params.SetProposedEntry(dsse_entry) params.SetContext(context.Background()) params.SetTimeout(5 * time.Second) if _, err := rc.Entries.CreateLogEntry(params); err == nil { t.Errorf("expected error to be returned from rekor given lack of signature in envelope") } } // TestTwoPublicKeysOneSignature tests uploading a valid entry with the both the correct and an incorrect public key; // this should be accepted by Rekor, but with only the key that successfully verifies the signature. func TestTwoPublicKeysOneSignature(t *testing.T) { _, eb := GenerateSingleSignedDSSE(t) ap := types.ArtifactProperties{ ArtifactBytes: eb, PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub), []byte(sigx509.ECDSAPub)}, } ei := NewEntry() entry, err := ei.CreateFromArtifactProperties(context.Background(), ap) if err != nil { t.Fatalf("error creating entry: %v", err) } rc, err := client.GetRekorClient(rekorServer()) if err != nil { t.Errorf("error getting client: %v", err) } params := &entries.CreateLogEntryParams{} params.SetProposedEntry(entry) params.SetContext(context.Background()) params.SetTimeout(5 * time.Second) resp, err := rc.Entries.CreateLogEntry(params) if err != nil { t.Errorf("unexpected error returned from rekor: %v", err.Error()) } v001 := DecodeV001FromRekorResponse(t, resp) if len(v001.DSSEObj.Signatures) != 1 { t.Errorf("incorrect number of signatures returned in response: expected 1, got %d", len(v001.DSSEObj.Signatures)) } } // TestTwoPublicKeysTwoSignatures tests uploading a valid entry with the both the correct and an incorrect public key; // this should be rejected by Rekor, as both signatures were not successfully verified func TestTwoPublicKeysTwoSignatures(t *testing.T) { _, eb := GenerateDoubleSignedDSSE(t) ap := types.ArtifactProperties{ ArtifactBytes: eb, PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub), []byte(sigx509.RSACert)}, // missing RSA pub key } ei := NewEntry() entry, err := ei.CreateFromArtifactProperties(context.Background(), ap) if err != nil { t.Fatalf("error creating entry: %v", err) } // swap out one of the good public keys for a mismatched one dsse_entry := entry.(*models.DSSE) v001 := dsse_entry.Spec.(models.DSSEV001Schema) v001.ProposedContent.Verifiers[0] = strfmt.Base64(sigx509.RSACert) v001.ProposedContent.Verifiers[1] = strfmt.Base64(sigx509.RSACert) dsse_entry.Spec = v001 rc, err := client.GetRekorClient(rekorServer()) if err != nil { t.Errorf("error getting client: %v", err) } params := &entries.CreateLogEntryParams{} params.SetProposedEntry(dsse_entry) params.SetContext(context.Background()) params.SetTimeout(5 * time.Second) if _, err := rc.Entries.CreateLogEntry(params); err == nil { t.Errorf("expected error to be returned from rekor given incorrect public keys provided") } } // TestThreePublicKeysTwoSignatures tests uploading a valid entry with the both the correct keys and an additional // incorrect public key; this should be accepted by Rekor, but with only the two keys that successfully verified the signatures func TestThreePublicKeysTwoSignatures(t *testing.T) { _, eb := GenerateDoubleSignedDSSE(t) ap := types.ArtifactProperties{ ArtifactBytes: eb, PublicKeyBytes: [][]byte{[]byte(sigx509.ECDSAPub), []byte(sigx509.ECDSAPub), []byte(sigx509.RSACert)}, } ei := NewEntry() entry, err := ei.CreateFromArtifactProperties(context.Background(), ap) if err != nil { t.Fatalf("error creating entry: %v", err) } rc, err := client.GetRekorClient(rekorServer()) if err != nil { t.Errorf("error getting client: %v", err) } params := &entries.CreateLogEntryParams{} params.SetProposedEntry(entry) params.SetContext(context.Background()) params.SetTimeout(5 * time.Second) resp, err := rc.Entries.CreateLogEntry(params) if err != nil { t.Errorf("unexpected error returned from rekor: %v", err.Error()) } for _, k := range resp.Payload { b, err := base64.StdEncoding.DecodeString(k.Body.(string)) if err != nil { t.Errorf("unexpected error returned from rekor: %v", err.Error()) } pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected error returned from rekor: %v", err.Error()) } eimpl, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected error returned from rekor: %v", err.Error()) } dsse_eimpl := eimpl.(*V001Entry) if len(dsse_eimpl.DSSEObj.Signatures) != 2 { t.Errorf("incorrect number of signatures returned in response: expected 2, got %d", len(dsse_eimpl.DSSEObj.Signatures)) } } } rekor-1.3.5/pkg/types/dsse/v0.0.1/entry.go000066400000000000000000000331221455727245600200610ustar00rootroot00000000000000// // Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package dsse import ( "bytes" "context" "crypto" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "os" "path/filepath" "sort" "strings" "github.com/in-toto/in-toto-golang/in_toto" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/types" dsseType "github.com/sigstore/rekor/pkg/types/dsse" "github.com/sigstore/sigstore/pkg/signature" sigdsse "github.com/sigstore/sigstore/pkg/signature/dsse" ) const ( APIVERSION = "0.0.1" ) func init() { if err := dsseType.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil { log.Logger.Panic(err) } } type V001Entry struct { DSSEObj models.DSSEV001Schema env *dsse.Envelope } func (v V001Entry) APIVersion() string { return APIVERSION } func NewEntry() types.EntryImpl { return &V001Entry{} } // IndexKeys computes the list of keys that should map back to this entry. // It should *never* reference v.DSSEObj.ProposedContent as those values would only // be present at the time of insertion func (v V001Entry) IndexKeys() ([]string, error) { var result []string for _, sig := range v.DSSEObj.Signatures { if sig == nil || sig.Verifier == nil { return result, errors.New("missing or malformed public key") } keyObj, err := x509.NewPublicKey(bytes.NewReader(*sig.Verifier)) if err != nil { return result, err } canonKey, err := keyObj.CanonicalValue() if err != nil { return result, fmt.Errorf("could not canonicalize key: %w", err) } keyHash := sha256.Sum256(canonKey) result = append(result, "sha256:"+hex.EncodeToString(keyHash[:])) result = append(result, keyObj.Subjects()...) } if v.DSSEObj.PayloadHash != nil { payloadHashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.DSSEObj.PayloadHash.Algorithm, *v.DSSEObj.PayloadHash.Value)) result = append(result, payloadHashKey) } if v.DSSEObj.EnvelopeHash != nil { envelopeHashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.DSSEObj.EnvelopeHash.Algorithm, *v.DSSEObj.EnvelopeHash.Value)) result = append(result, envelopeHashKey) } if v.env == nil { log.Logger.Info("DSSEObj content or DSSE envelope is nil, returning partial set of keys") return result, nil } switch v.env.PayloadType { case in_toto.PayloadType: if v.env.Payload == "" { log.Logger.Info("DSSEObj DSSE payload is empty") return result, nil } decodedPayload, err := v.env.DecodeB64Payload() if err != nil { return result, fmt.Errorf("could not decode envelope payload: %w", err) } statement, err := parseStatement(decodedPayload) if err != nil { return result, err } for _, s := range statement.Subject { for alg, ds := range s.Digest { result = append(result, alg+":"+ds) } } // Not all in-toto statements will contain a SLSA provenance predicate. // See https://github.com/in-toto/attestation/blob/main/spec/README.md#predicate // for other predicates. if predicate, err := parseSlsaPredicate(decodedPayload); err == nil { if predicate.Predicate.Materials != nil { for _, s := range predicate.Predicate.Materials { for alg, ds := range s.Digest { result = append(result, alg+":"+ds) } } } } default: log.Logger.Infof("Unknown DSSE envelope payloadType: %s", v.env.PayloadType) } return result, nil } func parseStatement(p []byte) (*in_toto.Statement, error) { ps := in_toto.Statement{} if err := json.Unmarshal(p, &ps); err != nil { return nil, err } return &ps, nil } func parseSlsaPredicate(p []byte) (*in_toto.ProvenanceStatement, error) { predicate := in_toto.ProvenanceStatement{} if err := json.Unmarshal(p, &predicate); err != nil { return nil, err } return &predicate, nil } func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error { it, ok := pe.(*models.DSSE) if !ok { return errors.New("cannot unmarshal non DSSE v0.0.1 type") } dsseObj := &models.DSSEV001Schema{} if err := types.DecodeEntry(it.Spec, dsseObj); err != nil { return err } // field validation if err := dsseObj.Validate(strfmt.Default); err != nil { return err } // either we have just proposed content or the canonicalized fields if dsseObj.ProposedContent == nil { // then we need canonicalized fields, and all must be present (if present, they would have been validated in the above call to Validate()) if dsseObj.EnvelopeHash == nil || dsseObj.PayloadHash == nil || len(dsseObj.Signatures) == 0 { return errors.New("either proposedContent or envelopeHash, payloadHash, and signatures must be present") } v.DSSEObj = *dsseObj return nil } // if we're here, then we're trying to propose a new entry so we check to ensure client's aren't setting server-side computed fields if dsseObj.EnvelopeHash != nil || dsseObj.PayloadHash != nil || len(dsseObj.Signatures) != 0 { return errors.New("either proposedContent or envelopeHash, payloadHash, and signatures must be present but not both") } env := &dsse.Envelope{} if err := json.Unmarshal([]byte(*dsseObj.ProposedContent.Envelope), env); err != nil { return err } if len(env.Signatures) == 0 { return errors.New("DSSE envelope must contain 1 or more signatures") } allPubKeyBytes := make([][]byte, 0) for _, publicKey := range dsseObj.ProposedContent.Verifiers { if publicKey == nil { return errors.New("an invalid null verifier was provided in ProposedContent") } allPubKeyBytes = append(allPubKeyBytes, publicKey) } sigToKeyMap, err := verifyEnvelope(allPubKeyBytes, env) if err != nil { return err } // we need to ensure we canonicalize the ordering of signatures sortedSigs := make([]string, 0, len(sigToKeyMap)) for sig := range sigToKeyMap { sortedSigs = append(sortedSigs, sig) } sort.Strings(sortedSigs) for i, sig := range sortedSigs { key := sigToKeyMap[sig] canonicalizedKey, err := key.CanonicalValue() if err != nil { return err } b64CanonicalizedKey := strfmt.Base64(canonicalizedKey) dsseObj.Signatures = append(dsseObj.Signatures, &models.DSSEV001SchemaSignaturesItems0{ Signature: &sortedSigs[i], Verifier: &b64CanonicalizedKey, }) } decodedPayload, err := env.DecodeB64Payload() if err != nil { // this shouldn't happen because failure would have occurred in verifyEnvelope call above return err } payloadHash := sha256.Sum256(decodedPayload) dsseObj.PayloadHash = &models.DSSEV001SchemaPayloadHash{ Algorithm: swag.String(models.DSSEV001SchemaPayloadHashAlgorithmSha256), Value: swag.String(hex.EncodeToString(payloadHash[:])), } envelopeHash := sha256.Sum256([]byte(*dsseObj.ProposedContent.Envelope)) dsseObj.EnvelopeHash = &models.DSSEV001SchemaEnvelopeHash{ Algorithm: swag.String(models.DSSEV001SchemaEnvelopeHashAlgorithmSha256), Value: swag.String(hex.EncodeToString(envelopeHash[:])), } // we've gotten through all processing without error, now update the object we're unmarshalling into v.DSSEObj = *dsseObj v.env = env return nil } // Canonicalize returns a JSON representation of the entry to be persisted into the log. This // will be further canonicalized by JSON Canonicalization Scheme (JCS) before being written. // // This function should not use v.DSSEObj.ProposedContent fields as they are client provided and // should not be trusted; the other fields at the top level are only set server side. func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) { canonicalEntry := models.DSSEV001Schema{ Signatures: v.DSSEObj.Signatures, EnvelopeHash: v.DSSEObj.EnvelopeHash, PayloadHash: v.DSSEObj.PayloadHash, ProposedContent: nil, // this is explicitly done as we don't want to canonicalize the envelope } for _, s := range canonicalEntry.Signatures { if s.Signature == nil { return nil, errors.New("canonical entry missing required signature") } } sort.Slice(canonicalEntry.Signatures, func(i, j int) bool { return *canonicalEntry.Signatures[i].Signature < *canonicalEntry.Signatures[j].Signature }) itObj := models.DSSE{} itObj.APIVersion = swag.String(APIVERSION) itObj.Spec = &canonicalEntry return json.Marshal(&itObj) } // AttestationKey and AttestationKeyValue are not implemented so the envelopes will not be persisted in Rekor func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) { returnVal := models.DSSE{} re := V001Entry{ DSSEObj: models.DSSEV001Schema{ ProposedContent: &models.DSSEV001SchemaProposedContent{}, }, } var err error artifactBytes := props.ArtifactBytes if artifactBytes == nil { if props.ArtifactPath == nil { return nil, errors.New("path to artifact file must be specified") } if props.ArtifactPath.IsAbs() { return nil, errors.New("dsse envelopes cannot be fetched over HTTP(S)") } artifactBytes, err = os.ReadFile(filepath.Clean(props.ArtifactPath.Path)) if err != nil { return nil, err } } env := &dsse.Envelope{} if err := json.Unmarshal(artifactBytes, env); err != nil { return nil, fmt.Errorf("payload must be a valid DSSE envelope: %w", err) } allPubKeyBytes := make([][]byte, 0) if len(props.PublicKeyBytes) > 0 { allPubKeyBytes = append(allPubKeyBytes, props.PublicKeyBytes...) } if len(props.PublicKeyPaths) > 0 { for _, path := range props.PublicKeyPaths { if path.IsAbs() { return nil, errors.New("dsse public keys cannot be fetched over HTTP(S)") } publicKeyBytes, err := os.ReadFile(filepath.Clean(path.Path)) if err != nil { return nil, fmt.Errorf("error reading public key file: %w", err) } allPubKeyBytes = append(allPubKeyBytes, publicKeyBytes) } } keysBySig, err := verifyEnvelope(allPubKeyBytes, env) if err != nil { return nil, err } for _, key := range keysBySig { canonicalKey, err := key.CanonicalValue() if err != nil { return nil, err } re.DSSEObj.ProposedContent.Verifiers = append(re.DSSEObj.ProposedContent.Verifiers, strfmt.Base64(canonicalKey)) } re.DSSEObj.ProposedContent.Envelope = swag.String(string(artifactBytes)) returnVal.Spec = re.DSSEObj returnVal.APIVersion = swag.String(re.APIVersion()) return &returnVal, nil } // verifyEnvelope takes in an array of possible key bytes and attempts to parse them as x509 public keys. // it then uses these to verify the envelope and makes sure that every signature on the envelope is verified. // it returns a map of verifiers indexed by the signature the verifier corresponds to. func verifyEnvelope(allPubKeyBytes [][]byte, env *dsse.Envelope) (map[string]*x509.PublicKey, error) { // generate a fake id for these keys so we can get back to the key bytes and match them to their corresponding signature verifierBySig := make(map[string]*x509.PublicKey) allSigs := make(map[string]struct{}) for _, sig := range env.Signatures { allSigs[sig.Sig] = struct{}{} } for _, pubKeyBytes := range allPubKeyBytes { if len(allSigs) == 0 { break // if all signatures have been verified, do not attempt anymore } key, err := x509.NewPublicKey(bytes.NewReader(pubKeyBytes)) if err != nil { return nil, fmt.Errorf("could not parse public key as x509: %w", err) } vfr, err := signature.LoadVerifier(key.CryptoPubKey(), crypto.SHA256) 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) verifierBySig[accept.Sig.Sig] = key } } if len(allSigs) > 0 { return nil, errors.New("all signatures must have a key that verifies it") } return verifierBySig, nil } func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { if len(v.DSSEObj.Signatures) == 0 { return nil, errors.New("dsse v0.0.1 entry not initialized") } var keys []pki.PublicKey for _, s := range v.DSSEObj.Signatures { key, err := x509.NewPublicKey(bytes.NewReader(*s.Verifier)) if err != nil { return nil, err } keys = append(keys, key) } return keys, nil } func (v V001Entry) ArtifactHash() (string, error) { if v.DSSEObj.PayloadHash == nil || v.DSSEObj.PayloadHash.Algorithm == nil || v.DSSEObj.PayloadHash.Value == nil { return "", errors.New("dsse v0.0.1 entry not initialized") } return strings.ToLower(fmt.Sprintf("%s:%s", *v.DSSEObj.PayloadHash.Algorithm, *v.DSSEObj.PayloadHash.Value)), nil } func (v V001Entry) Insertable() (bool, error) { if v.DSSEObj.ProposedContent == nil { return false, errors.New("missing proposed content") } if v.DSSEObj.ProposedContent.Envelope == nil || len(*v.DSSEObj.ProposedContent.Envelope) == 0 { return false, errors.New("missing proposed DSSE envelope") } if len(v.DSSEObj.ProposedContent.Verifiers) == 0 { return false, errors.New("missing proposed verifiers") } return true, nil } rekor-1.3.5/pkg/types/dsse/v0.0.1/entry_test.go000066400000000000000000000337141455727245600211270ustar00rootroot00000000000000// // Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package dsse import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/json" "encoding/pem" "fmt" "math/big" "reflect" "sort" "strings" "testing" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/in-toto/in-toto-golang/in_toto" slsaCommon "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/sigstore/pkg/signature" sigdsse "github.com/sigstore/sigstore/pkg/signature/dsse" "go.uber.org/goleak" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestNewEntryReturnType(t *testing.T) { entry := NewEntry() if reflect.TypeOf(entry) != reflect.ValueOf(&V001Entry{}).Type() { t.Errorf("invalid type returned from NewEntry: %T", entry) } } func envelope(t *testing.T, k *ecdsa.PrivateKey, payload []byte) *dsse.Envelope { s, err := signature.LoadECDSASigner(k, crypto.SHA256) if err != nil { t.Fatal(err) } signer, err := dsse.NewEnvelopeSigner(&sigdsse.SignerAdapter{ SignatureSigner: s, }) if err != nil { t.Fatal(err) } dsseEnv, err := signer.SignPayload(context.Background(), "application/vnd.in-toto+json", payload) if err != nil { t.Fatal(err) } return dsseEnv } func multiSignEnvelope(t *testing.T, k []*ecdsa.PrivateKey, payload []byte) *dsse.Envelope { evps := []*sigdsse.SignerAdapter{} for _, key := range k { s, err := signature.LoadECDSASigner(key, crypto.SHA256) if err != nil { t.Fatal(err) } evps = append(evps, &sigdsse.SignerAdapter{ SignatureSigner: s, }) } signer, err := dsse.NewEnvelopeSigner(evps[0], evps[1]) if err != nil { t.Fatal(err) } dsseEnv, err := signer.SignPayload(context.Background(), in_toto.PayloadType, payload) if err != nil { t.Fatal(err) } return dsseEnv } func createRekorEnvelope(dsseEnv *dsse.Envelope, pub [][]byte) *models.DSSEV001SchemaProposedContent { envelopeBytes, _ := json.Marshal(dsseEnv) proposedContent := &models.DSSEV001SchemaProposedContent{ Envelope: swag.String(string(envelopeBytes)), } for _, key := range pub { proposedContent.Verifiers = append(proposedContent.Verifiers, strfmt.Base64(key)) } return proposedContent } func TestV001Entry_Unmarshal(t *testing.T) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } ca := &x509.Certificate{ SerialNumber: big.NewInt(1), } caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &priv.PublicKey, priv) if err != nil { t.Fatal(err) } pemBytes := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: caBytes, }) invalid := dsse.Envelope{ Payload: "hello", Signatures: []dsse.Signature{ { Sig: string(strfmt.Base64("foobar")), }, }, } validEnv := envelope(t, key, []byte("payload")) validEnvBytes, _ := json.Marshal(validEnv) validPayload := "hellothispayloadisvalid" tests := []struct { env *dsse.Envelope name string it *models.DSSEV001Schema wantErr bool }{ { name: "empty", it: &models.DSSEV001Schema{}, wantErr: true, }, { name: "missing envelope", it: &models.DSSEV001Schema{ ProposedContent: &models.DSSEV001SchemaProposedContent{}, }, wantErr: true, }, { env: envelope(t, key, []byte(validPayload)), name: "valid", it: &models.DSSEV001Schema{ ProposedContent: createRekorEnvelope(envelope(t, key, []byte(validPayload)), [][]byte{pub}), }, wantErr: false, }, { env: envelope(t, priv, []byte(validPayload)), name: "cert", it: &models.DSSEV001Schema{ ProposedContent: createRekorEnvelope(envelope(t, priv, []byte(validPayload)), [][]byte{pemBytes}), }, wantErr: false, }, { env: &invalid, name: "invalid", it: &models.DSSEV001Schema{ ProposedContent: createRekorEnvelope(&invalid, [][]byte{pub}), }, wantErr: true, }, { env: envelope(t, key, []byte(validPayload)), name: "invalid key", it: &models.DSSEV001Schema{ ProposedContent: createRekorEnvelope(envelope(t, key, []byte(validPayload)), [][]byte{[]byte("notavalidkey")}), }, wantErr: true, }, { env: multiSignEnvelope(t, []*ecdsa.PrivateKey{key, priv}, []byte(validPayload)), name: "multi-key", it: &models.DSSEV001Schema{ ProposedContent: createRekorEnvelope(multiSignEnvelope(t, []*ecdsa.PrivateKey{key, priv}, []byte(validPayload)), [][]byte{pub, pemBytes}), }, wantErr: false, }, { env: validEnv, name: "null verifier in array", it: &models.DSSEV001Schema{ ProposedContent: &models.DSSEV001SchemaProposedContent{ Envelope: swag.String(string(validEnvBytes)), Verifiers: []strfmt.Base64{pub, nil}, }, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { v := &V001Entry{} it := &models.DSSE{ Spec: tt.it, } var uv = func() error { if err := v.Unmarshal(it); err != nil { return err } if !tt.wantErr { if ok, err := v.Insertable(); !ok || err != nil { return fmt.Errorf("unexpected error calling Insertable: %w", err) } } want := []string{} for _, sig := range v.DSSEObj.Signatures { keyHash := sha256.Sum256(*sig.Verifier) want = append(want, "sha256:"+hex.EncodeToString(keyHash[:])) } decodedPayload, err := base64.StdEncoding.DecodeString(tt.env.Payload) if err != nil { return fmt.Errorf("could not decode envelope payload: %w", err) } h := sha256.Sum256(decodedPayload) want = append(want, "sha256:"+hex.EncodeToString(h[:])) envHashBytes := sha256.Sum256([]byte(*tt.it.ProposedContent.Envelope)) envHash := hex.EncodeToString(envHashBytes[:]) hashkey := strings.ToLower(fmt.Sprintf("sha256:%s", envHash)) want = append(want, hashkey) got, _ := v.IndexKeys() sort.Strings(got) sort.Strings(want) if !reflect.DeepEqual(got, want) { t.Errorf("V001Entry.IndexKeys() = %v, want %v", got, want) } payloadBytes, _ := v.env.DecodeB64Payload() payloadSha := sha256.Sum256(payloadBytes) payloadHash := hex.EncodeToString(payloadSha[:]) canonicalBytes, err := v.Canonicalize(context.Background()) if err != nil { t.Errorf("error canonicalizing entry: %v", err) } pe, err := models.UnmarshalProposedEntry(bytes.NewReader(canonicalBytes), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tt.name, err) } canonicalEntry, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tt.name, err) } canonicalV001 := canonicalEntry.(*V001Entry) if ok, err := canonicalV001.Insertable(); ok || err == nil { t.Errorf("unexpected success testing Insertable against entry created from canonicalized content") } if *canonicalV001.DSSEObj.EnvelopeHash.Value != envHash { t.Errorf("envelope hashes do not match post canonicalization: %v %v", *canonicalV001.DSSEObj.EnvelopeHash.Value, envHash) } if *canonicalV001.DSSEObj.PayloadHash.Value != payloadHash { t.Errorf("payload hashes do not match post canonicalization: %v %v", canonicalV001.DSSEObj.PayloadHash.Value, payloadHash) } canonicalIndexKeys, _ := canonicalV001.IndexKeys() if !cmp.Equal(got, canonicalIndexKeys, cmpopts.SortSlices(func(x, y string) bool { return x < y })) { t.Errorf("index keys from hydrated object do not match those generated from canonicalized (and re-hydrated) object: %v %v", got, canonicalIndexKeys) } hash, err := canonicalV001.ArtifactHash() expectedHash := sha256.Sum256([]byte(validPayload)) if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != "sha256:"+hex.EncodeToString(expectedHash[:]) { t.Errorf("unexpected match with ArtifactHash: %s", hash) } return nil } if err := uv(); (err != nil) != tt.wantErr { t.Errorf("V001Entry.Unmarshal() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestV001Entry_IndexKeys(t *testing.T) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) tests := []struct { name string statement in_toto.Statement want []string }{ { name: "standard", want: []string{}, statement: in_toto.Statement{ Predicate: "hello", }, }, { name: "subject", want: []string{"sha256:foo"}, statement: in_toto.Statement{ StatementHeader: in_toto.StatementHeader{ Subject: []in_toto.Subject{ { Name: "foo", Digest: map[string]string{ "sha256": "foo", }, }, }, }, Predicate: "hello", }, }, { name: "slsa", want: []string{"sha256:bar"}, statement: in_toto.Statement{ Predicate: slsa.ProvenancePredicate{ Materials: []slsaCommon.ProvenanceMaterial{ { URI: "foo", Digest: map[string]string{ "sha256": "bar", }}, }, }, }, }, { name: "slsa wit header", want: []string{"sha256:foo", "sha256:bar"}, statement: in_toto.Statement{ StatementHeader: in_toto.StatementHeader{ Subject: []in_toto.Subject{ { Name: "foo", Digest: map[string]string{ "sha256": "foo", }, }, }, }, Predicate: slsa.ProvenancePredicate{ Materials: []slsaCommon.ProvenanceMaterial{ { URI: "foo", Digest: map[string]string{ "sha256": "bar", }}, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { b, err := json.Marshal(tt.statement) if err != nil { t.Fatal(err) } pe := &models.DSSE{ APIVersion: swag.String(APIVERSION), Spec: &models.DSSEV001Schema{ ProposedContent: createRekorEnvelope(envelope(t, key, b), [][]byte{pub}), }, } v := V001Entry{} if err := v.Unmarshal(pe); err != nil { t.Error(err) } want := []string{} for _, sig := range v.DSSEObj.Signatures { keyHash := sha256.Sum256(*sig.Verifier) want = append(want, "sha256:"+hex.EncodeToString(keyHash[:])) } decodedPayload, _ := base64.StdEncoding.DecodeString(v.env.Payload) h := sha256.Sum256(decodedPayload) want = append(want, "sha256:"+hex.EncodeToString(h[:])) envHashBytes := sha256.Sum256([]byte(*v.DSSEObj.ProposedContent.Envelope)) envHash := hex.EncodeToString(envHashBytes[:]) hashkey := strings.ToLower(fmt.Sprintf("sha256:%s", envHash)) want = append(want, hashkey) want = append(want, tt.want...) got, err := v.IndexKeys() if err != nil { t.Error(err) } sort.Strings(got) sort.Strings(want) if !cmp.Equal(got, want, cmpopts.EquateEmpty()) { t.Errorf("V001Entry.IndexKeys() = %v, want %v", got, want) } }) } } func TestInsertable(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectSuccess bool } testCases := []TestCase{ { caseDesc: "valid entry", entry: V001Entry{ DSSEObj: models.DSSEV001Schema{ ProposedContent: &models.DSSEV001SchemaProposedContent{ Envelope: swag.String("envelope"), Verifiers: []strfmt.Base64{ []byte("keys"), }, }, }, }, expectSuccess: true, }, { caseDesc: "missing public keys", entry: V001Entry{ DSSEObj: models.DSSEV001Schema{ ProposedContent: &models.DSSEV001SchemaProposedContent{ Envelope: swag.String("envelope"), /* Verifiers: []strfmt.Base64{ []byte("keys"), }, */ }, }, }, expectSuccess: false, }, { caseDesc: "missing envelope", entry: V001Entry{ DSSEObj: models.DSSEV001Schema{ ProposedContent: &models.DSSEV001SchemaProposedContent{ //Envelope: swag.String("envelope"), Verifiers: []strfmt.Base64{ []byte("keys"), }, }, }, }, expectSuccess: false, }, { caseDesc: "missing proposed content obj", entry: V001Entry{ DSSEObj: models.DSSEV001Schema{ /* ProposedContent: &models.DSSEV001SchemaProposedContent{ Envelope: swag.String("envelope"), Verifiers: []strfmt.Base64{ []byte("keys"), }, }, */ }, }, expectSuccess: false, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess { t.Errorf("unexpected result calling Insertable: %v", err) } }) } } func TestCanonicalizeHandlesInvalidInput(t *testing.T) { v := &V001Entry{} v.DSSEObj.Signatures = []*models.DSSEV001SchemaSignaturesItems0{{Signature: nil}, {Signature: nil}} _, err := v.Canonicalize(context.TODO()) if err == nil { t.Fatalf("expected error canonicalizing invalid input") } } rekor-1.3.5/pkg/types/dsse/v0.0.1/fuzz_test.go000066400000000000000000000046361455727245600207650ustar00rootroot00000000000000// // Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package dsse import ( "context" "sync" "testing" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/go-openapi/swag" fuzzUtils "github.com/sigstore/rekor/pkg/fuzz" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/dsse" ) var initter sync.Once func FuzzDSSECreateProposedEntry(f *testing.F) { f.Fuzz(func(t *testing.T, propsData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(propsData) props, cleanup, err := fuzzUtils.CreateProps(ff, "dssev001") if err != nil { t.Skip() } defer func() { for _, c := range cleanup { c() } }() it := dsse.New() entry, err := it.CreateProposedEntry(context.Background(), APIVERSION, props) if err != nil { t.Skip() } ei, err := types.CreateVersionedEntry(entry) if err != nil { t.Skip() } if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err) } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("valid insertable entry should be able to be canonicalized: %v", err) } _, _ = ei.IndexKeys() }) } func FuzzDSSEUnmarshalAndCanonicalize(f *testing.F) { f.Fuzz(func(t *testing.T, entryData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(entryData) targetV001 := &models.DSSEV001Schema{} if err := ff.GenerateStruct(targetV001); err != nil { t.Skip() } targetEntry := &models.DSSE{ APIVersion: swag.String(APIVERSION), Spec: targetV001, } ei, err := types.UnmarshalEntry(targetEntry) if err != nil { t.Skip() } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("error canonicalizing unmarshalled entry: %v", err) } }) } rekor-1.3.5/pkg/types/entries.go000066400000000000000000000152451455727245600166170ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package types import ( "context" "encoding/base64" "errors" "fmt" "net/url" "reflect" "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer" "github.com/go-openapi/strfmt" "github.com/mitchellh/mapstructure" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" ) // EntryImpl specifies the behavior of a versioned type type EntryImpl interface { APIVersion() string // the supported versions for this implementation IndexKeys() ([]string, error) // the keys that should be added to the external index for this entry Canonicalize(ctx context.Context) ([]byte, error) // marshal the canonical entry to be put into the tlog Unmarshal(e models.ProposedEntry) error // unmarshal the abstract entry into the specific struct for this versioned type CreateFromArtifactProperties(context.Context, ArtifactProperties) (models.ProposedEntry, error) Verifiers() ([]pki.PublicKey, error) // list of keys or certificates that can verify an entry's signature ArtifactHash() (string, error) // hex-encoded artifact hash prefixed with hash name, e.g. sha256:abcdef Insertable() (bool, error) // denotes whether the entry that was unmarshalled has the writeOnly fields required to validate and insert into the log } // EntryWithAttestationImpl specifies the behavior of a versioned type that also stores attestations type EntryWithAttestationImpl interface { EntryImpl AttestationKey() string // returns the key used to look up the attestation from storage (should be sha256:digest) AttestationKeyValue() (string, []byte) // returns the key to be used when storing the attestation as well as the attestation itself } // ProposedEntryIterator is an iterator over a list of proposed entries type ProposedEntryIterator interface { models.ProposedEntry HasNext() bool Get() models.ProposedEntry GetNext() models.ProposedEntry } // EntryFactory describes a factory function that can generate structs for a specific versioned type type EntryFactory func() EntryImpl func NewProposedEntry(ctx context.Context, kind, version string, props ArtifactProperties) (models.ProposedEntry, error) { if tf, found := TypeMap.Load(kind); found { t := tf.(func() TypeImpl)() if t == nil { return nil, fmt.Errorf("error generating object for kind '%v'", kind) } return t.CreateProposedEntry(ctx, version, props) } return nil, fmt.Errorf("could not create entry for kind '%v'", kind) } // CreateVersionedEntry returns the specific instance for the type and version specified in the doc // This method should be used on the insertion flow, which validates that the specific version proposed // is permitted to be entered into the log. func CreateVersionedEntry(pe models.ProposedEntry) (EntryImpl, error) { ei, err := UnmarshalEntry(pe) if err != nil { return nil, err } kind := pe.Kind() if tf, found := TypeMap.Load(kind); found { if !tf.(func() TypeImpl)().IsSupportedVersion(ei.APIVersion()) { return nil, fmt.Errorf("entry kind '%v' does not support inserting entries of version '%v'", kind, ei.APIVersion()) } } else { return nil, fmt.Errorf("unknown kind '%v' specified", kind) } if ok, err := ei.Insertable(); !ok { return nil, fmt.Errorf("entry not insertable into log: %w", err) } return ei, nil } // UnmarshalEntry returns the specific instance for the type and version specified in the doc // This method does not check for whether the version of the entry could be currently inserted into the log, // and is useful when dealing with entries that have been persisted to the log. func UnmarshalEntry(pe models.ProposedEntry) (EntryImpl, error) { if pe == nil { return nil, errors.New("proposed entry cannot be nil") } kind := pe.Kind() if tf, found := TypeMap.Load(kind); found { t := tf.(func() TypeImpl)() if t == nil { return nil, fmt.Errorf("error generating object for kind '%v'", kind) } return t.UnmarshalEntry(pe) } return nil, fmt.Errorf("could not unmarshal entry for kind '%v'", kind) } // DecodeEntry maps the (abstract) input structure into the specific entry implementation class; // while doing so, it detects the case where we need to convert from string to []byte and does // the base64 decoding required to make that happen. // This also detects converting from string to strfmt.DateTime func DecodeEntry(input, output interface{}) error { cfg := mapstructure.DecoderConfig{ DecodeHook: func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { if f.Kind() != reflect.String || t.Kind() != reflect.Slice && t != reflect.TypeOf(strfmt.DateTime{}) { return data, nil } if data == nil { return nil, errors.New("attempted to decode nil data") } if t == reflect.TypeOf(strfmt.DateTime{}) { return strfmt.ParseDateTime(data.(string)) } bytes, err := base64.StdEncoding.DecodeString(data.(string)) if err != nil { return []byte{}, fmt.Errorf("failed parsing base64 data: %v", err) } return bytes, nil }, Result: output, } dec, err := mapstructure.NewDecoder(&cfg) if err != nil { return fmt.Errorf("error initializing decoder: %w", err) } return dec.Decode(input) } // CanonicalizeEntry returns the entry marshalled in JSON according to the // canonicalization rules of RFC8785 to protect against any changes in golang's JSON // marshalling logic that may reorder elements func CanonicalizeEntry(ctx context.Context, entry EntryImpl) ([]byte, error) { canonicalEntry, err := entry.Canonicalize(ctx) if err != nil { return nil, err } return jsoncanonicalizer.Transform(canonicalEntry) } // ArtifactProperties provide a consistent struct for passing values from // CLI flags to the type+version specific CreateProposeEntry() methods type ArtifactProperties struct { AdditionalAuthenticatedData []byte ArtifactPath *url.URL ArtifactHash string ArtifactBytes []byte SignaturePath *url.URL SignatureBytes []byte PublicKeyPaths []*url.URL PublicKeyBytes [][]byte PKIFormat string } rekor-1.3.5/pkg/types/error.go000066400000000000000000000014611455727245600162720ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package types // ValidationError indicates that there is an issue with the content in the HTTP Request that // should result in an HTTP 400 Bad Request error being returned to the client type ValidationError error rekor-1.3.5/pkg/types/hashedrekord/000077500000000000000000000000001455727245600172535ustar00rootroot00000000000000rekor-1.3.5/pkg/types/hashedrekord/fuzz_test.go000066400000000000000000000026631455727245600216460ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package hashedrekord import ( "context" "net/url" "testing" "github.com/sigstore/rekor/pkg/types" ) func FuzzHashedRekord(f *testing.F) { f.Fuzz(func(t *testing.T, version, artifact, pkiFormat, scheme, host, path string, signature, authentication []byte) { ctx := context.Background() hrd := New() u := url.URL{Scheme: scheme, Host: host, Path: path} props := types.ArtifactProperties{ArtifactHash: artifact, PKIFormat: pkiFormat, SignatureBytes: signature, AdditionalAuthenticatedData: authentication, ArtifactPath: &u, SignaturePath: &u, PublicKeyPaths: []*url.URL{&u}} entry, err := hrd.CreateProposedEntry(ctx, version, props) if err != nil { t.Skip("skipping fuzz test due to error: ", err) } _, err = hrd.UnmarshalEntry(entry) if err != nil { t.Skip("skipping fuzz test due to unmarshal error: ", err) } }) } rekor-1.3.5/pkg/types/hashedrekord/hashedrekord.go000066400000000000000000000035531455727245600222530ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package hashedrekord import ( "context" "errors" "fmt" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" ) const ( KIND = "hashedrekord" ) type BaseRekordType struct { types.RekorType } func init() { types.TypeMap.Store(KIND, New) } func New() types.TypeImpl { brt := BaseRekordType{} brt.Kind = KIND brt.VersionMap = VersionMap return &brt } var VersionMap = types.NewSemVerEntryFactoryMap() func (rt BaseRekordType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) { if pe == nil { return nil, errors.New("proposed entry cannot be nil") } rekord, ok := pe.(*models.Hashedrekord) if !ok { return nil, fmt.Errorf("cannot unmarshal non-hashed Rekord types: %s", pe.Kind()) } return rt.VersionedUnmarshal(rekord, *rekord.APIVersion) } func (rt *BaseRekordType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) { if version == "" { version = rt.DefaultVersion() } ei, err := rt.VersionedUnmarshal(nil, version) if err != nil { return nil, fmt.Errorf("fetching hashed Rekord version implementation: %w", err) } return ei.CreateFromArtifactProperties(ctx, props) } func (rt BaseRekordType) DefaultVersion() string { return "0.0.1" } rekor-1.3.5/pkg/types/hashedrekord/hashedrekord_schema.json000066400000000000000000000005341455727245600241330ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/hashedrekord/hasehedrekord_schema.json", "title": "Rekor Schema", "description": "Schema for Rekord objects", "type": "object", "oneOf": [ { "$ref": "v0.0.1/hashedrekord_v0_0_1_schema.json" } ] } rekor-1.3.5/pkg/types/hashedrekord/hashedrekord_test.go000066400000000000000000000057511455727245600233140ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package hashedrekord import ( "errors" "testing" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" ) type UnmarshalTester struct { models.Hashedrekord types.BaseUnmarshalTester } type UnmarshalFailsTester struct { types.BaseUnmarshalTester } func (u UnmarshalFailsTester) NewEntry() types.EntryImpl { return &UnmarshalFailsTester{} } func (u UnmarshalFailsTester) Unmarshal(_ models.ProposedEntry) error { return errors.New("error") } func (u UnmarshalFailsTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } func (u UnmarshalFailsTester) ArtifactHash() (string, error) { return "", nil } func TestRekordType(t *testing.T) { // empty to start if VersionMap.Count() != 0 { t.Error("semver range was not blank at start of test") } u := UnmarshalTester{} // ensure semver range parser is working invalidSemVerRange := "not a valid semver range" err := VersionMap.SetEntryFactory(invalidSemVerRange, u.NewEntry) if err == nil || VersionMap.Count() > 0 { t.Error("invalid semver range was incorrectly added to SemVerToFacFnMap") } // valid semver range can be parsed err = VersionMap.SetEntryFactory(">= 1.2.3", u.NewEntry) if err != nil || VersionMap.Count() != 1 { t.Error("valid semver range was not added to SemVerToFacFnMap") } u.Hashedrekord.APIVersion = swag.String("2.0.1") brt := New() // version requested matches implementation in map if _, err := brt.UnmarshalEntry(&u.Hashedrekord); err != nil { t.Errorf("unexpected error in Unmarshal: %v", err) } // version requested fails to match implementation in map u.Hashedrekord.APIVersion = swag.String("1.2.2") if _, err := brt.UnmarshalEntry(&u.Hashedrekord); err == nil { t.Error("unexpected success in Unmarshal for non-matching version") } // error in Unmarshal call is raised appropriately u.Hashedrekord.APIVersion = swag.String("2.2.0") u2 := UnmarshalFailsTester{} _ = VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry) if _, err := brt.UnmarshalEntry(&u.Hashedrekord); err == nil { t.Error("unexpected success in Unmarshal when error is thrown") } // version requested fails to match implementation in map u.Hashedrekord.APIVersion = swag.String("not_a_version") if _, err := brt.UnmarshalEntry(&u.Hashedrekord); err == nil { t.Error("unexpected success in Unmarshal for invalid version") } } rekor-1.3.5/pkg/types/hashedrekord/v0.0.1/000077500000000000000000000000001455727245600200755ustar00rootroot00000000000000rekor-1.3.5/pkg/types/hashedrekord/v0.0.1/e2e_test.go000066400000000000000000000051141455727245600221370ustar00rootroot00000000000000// // Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package hashedrekord import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "os" "testing" "time" "github.com/sigstore/rekor/pkg/client" "github.com/sigstore/rekor/pkg/generated/client/entries" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" ) func rekorServer() string { if s := os.Getenv("REKOR_SERVER"); s != "" { return s } return "http://localhost:3000" } // TestSHA256HashedRekordEntry tests sending a valid HashedRekord proposed entry. func TestSHA256HashedRekordEntry(t *testing.T) { privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatalf("error generating key: %v", err) } pubBytes, err := cryptoutils.MarshalPublicKeyToPEM(privKey.Public()) if err != nil { t.Fatalf("error marshaling public key: %v", err) } data := []byte("data") signer, err := signature.LoadSigner(privKey, crypto.SHA256) if err != nil { t.Fatalf("error loading verifier: %v", err) } signature, err := signer.SignMessage(bytes.NewReader(data)) if err != nil { t.Fatalf("error signing message: %v", err) } ap := types.ArtifactProperties{ ArtifactBytes: []byte("data"), ArtifactHash: "sha256:3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7", PublicKeyBytes: [][]byte{pubBytes}, PKIFormat: "x509", SignatureBytes: signature, } ei := NewEntry() entry, err := ei.CreateFromArtifactProperties(context.Background(), ap) if err != nil { t.Fatalf("error creating entry: %v", err) } rc, err := client.GetRekorClient(rekorServer()) if err != nil { t.Errorf("error getting client: %v", err) } params := &entries.CreateLogEntryParams{} params.SetProposedEntry(entry) params.SetContext(context.Background()) params.SetTimeout(5 * time.Second) if _, err = rc.Entries.CreateLogEntry(params); err != nil { t.Fatalf("expected no errors when submitting hashedrekord entry with sha256 to rekor %s", err) } } rekor-1.3.5/pkg/types/hashedrekord/v0.0.1/entry.go000066400000000000000000000231061455727245600215670ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package hashedrekord import ( "bytes" "context" "crypto" "crypto/ed25519" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "os" "path/filepath" "strings" "github.com/asaskevich/govalidator" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/types" hashedrekord "github.com/sigstore/rekor/pkg/types/hashedrekord" "github.com/sigstore/rekor/pkg/util" "github.com/sigstore/sigstore/pkg/signature/options" ) const ( APIVERSION = "0.0.1" ) func init() { if err := hashedrekord.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil { log.Logger.Panic(err) } } type V001Entry struct { HashedRekordObj models.HashedrekordV001Schema } func (v V001Entry) APIVersion() string { return APIVERSION } func NewEntry() types.EntryImpl { return &V001Entry{} } func (v V001Entry) IndexKeys() ([]string, error) { var result []string key := v.HashedRekordObj.Signature.PublicKey.Content keyHash := sha256.Sum256(key) result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:]))) pub, err := x509.NewPublicKey(bytes.NewReader(key)) if err != nil { return nil, err } result = append(result, pub.Subjects()...) if v.HashedRekordObj.Data.Hash != nil { hashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.HashedRekordObj.Data.Hash.Algorithm, *v.HashedRekordObj.Data.Hash.Value)) result = append(result, hashKey) } return result, nil } func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error { rekord, ok := pe.(*models.Hashedrekord) if !ok { return errors.New("cannot unmarshal non Rekord v0.0.1 type") } if err := types.DecodeEntry(rekord.Spec, &v.HashedRekordObj); err != nil { return err } // field validation if err := v.HashedRekordObj.Validate(strfmt.Default); err != nil { return err } // cross field validation _, _, err := v.validate() return err } func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) { sigObj, keyObj, err := v.validate() if err != nil { return nil, types.ValidationError(err) } canonicalEntry := models.HashedrekordV001Schema{} // need to canonicalize signature & key content canonicalEntry.Signature = &models.HashedrekordV001SchemaSignature{} canonicalEntry.Signature.Content, err = sigObj.CanonicalValue() if err != nil { return nil, err } // key URL (if known) is not set deliberately canonicalEntry.Signature.PublicKey = &models.HashedrekordV001SchemaSignaturePublicKey{} canonicalEntry.Signature.PublicKey.Content, err = keyObj.CanonicalValue() if err != nil { return nil, err } canonicalEntry.Data = &models.HashedrekordV001SchemaData{} canonicalEntry.Data.Hash = v.HashedRekordObj.Data.Hash // data content is not set deliberately v.HashedRekordObj = canonicalEntry // wrap in valid object with kind and apiVersion set rekordObj := models.Hashedrekord{} rekordObj.APIVersion = swag.String(APIVERSION) rekordObj.Spec = &canonicalEntry return json.Marshal(&rekordObj) } // validate performs cross-field validation for fields in object func (v *V001Entry) validate() (pki.Signature, pki.PublicKey, error) { sig := v.HashedRekordObj.Signature if sig == nil { return nil, nil, types.ValidationError(errors.New("missing signature")) } // Hashed rekord type only works for x509 signature types sigObj, err := x509.NewSignature(bytes.NewReader(sig.Content)) if err != nil { return nil, nil, types.ValidationError(err) } key := sig.PublicKey if key == nil { return nil, nil, types.ValidationError(errors.New("missing public key")) } keyObj, err := x509.NewPublicKey(bytes.NewReader(key.Content)) if err != nil { return nil, nil, types.ValidationError(err) } _, isEd25519 := keyObj.CryptoPubKey().(ed25519.PublicKey) if isEd25519 { return nil, nil, types.ValidationError(errors.New("ed25519 unsupported for hashedrekord")) } data := v.HashedRekordObj.Data if data == nil { return nil, nil, types.ValidationError(errors.New("missing data")) } hash := data.Hash if hash == nil { return nil, nil, types.ValidationError(errors.New("missing hash")) } if !govalidator.IsHash(swag.StringValue(hash.Value), swag.StringValue(hash.Algorithm)) { return nil, nil, types.ValidationError(errors.New("invalid value for hash")) } var alg crypto.Hash switch swag.StringValue(hash.Algorithm) { case models.HashedrekordV001SchemaDataHashAlgorithmSha384: alg = crypto.SHA384 case models.HashedrekordV001SchemaDataHashAlgorithmSha512: alg = crypto.SHA512 default: alg = crypto.SHA256 } decoded, err := hex.DecodeString(*hash.Value) if err != nil { return nil, nil, err } if err := sigObj.Verify(nil, keyObj, options.WithDigest(decoded), options.WithCryptoSignerOpts(alg)); err != nil { return nil, nil, types.ValidationError(fmt.Errorf("verifying signature: %w", err)) } return sigObj, keyObj, nil } func getDataHashAlgorithm(hashAlgorithm crypto.Hash) string { switch hashAlgorithm { case crypto.SHA384: return models.HashedrekordV001SchemaDataHashAlgorithmSha384 case crypto.SHA512: return models.HashedrekordV001SchemaDataHashAlgorithmSha512 default: return models.HashedrekordV001SchemaDataHashAlgorithmSha256 } } func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) { returnVal := models.Hashedrekord{} re := V001Entry{} // we will need artifact, public-key, signature re.HashedRekordObj.Data = &models.HashedrekordV001SchemaData{} var err error if props.PKIFormat != string(pki.X509) { return nil, errors.New("hashedrekord entries can only be created for artifacts signed with x509-based PKI") } re.HashedRekordObj.Signature = &models.HashedrekordV001SchemaSignature{} sigBytes := props.SignatureBytes if sigBytes == nil { if props.SignaturePath == nil { return nil, errors.New("a detached signature must be provided") } sigBytes, err = os.ReadFile(filepath.Clean(props.SignaturePath.Path)) if err != nil { return nil, fmt.Errorf("error reading signature file: %w", err) } } re.HashedRekordObj.Signature.Content = strfmt.Base64(sigBytes) re.HashedRekordObj.Signature.PublicKey = &models.HashedrekordV001SchemaSignaturePublicKey{} publicKeyBytes := props.PublicKeyBytes if len(publicKeyBytes) == 0 { if len(props.PublicKeyPaths) != 1 { return nil, errors.New("only one public key must be provided to verify detached signature") } keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path)) if err != nil { return nil, fmt.Errorf("error reading public key file: %w", err) } publicKeyBytes = append(publicKeyBytes, keyBytes) } else if len(publicKeyBytes) != 1 { return nil, errors.New("only one public key must be provided") } hashAlgorithm, hashValue := util.UnprefixSHA(props.ArtifactHash) re.HashedRekordObj.Signature.PublicKey.Content = strfmt.Base64(publicKeyBytes[0]) re.HashedRekordObj.Data.Hash = &models.HashedrekordV001SchemaDataHash{ Algorithm: swag.String(getDataHashAlgorithm(hashAlgorithm)), Value: swag.String(hashValue), } if _, _, err := re.validate(); err != nil { return nil, err } returnVal.APIVersion = swag.String(re.APIVersion()) returnVal.Spec = re.HashedRekordObj return &returnVal, nil } func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { if v.HashedRekordObj.Signature == nil || v.HashedRekordObj.Signature.PublicKey == nil || v.HashedRekordObj.Signature.PublicKey.Content == nil { return nil, errors.New("hashedrekord v0.0.1 entry not initialized") } key, err := x509.NewPublicKey(bytes.NewReader(v.HashedRekordObj.Signature.PublicKey.Content)) if err != nil { return nil, err } return []pki.PublicKey{key}, nil } func (v V001Entry) ArtifactHash() (string, error) { if v.HashedRekordObj.Data == nil || v.HashedRekordObj.Data.Hash == nil || v.HashedRekordObj.Data.Hash.Value == nil || v.HashedRekordObj.Data.Hash.Algorithm == nil { return "", errors.New("hashedrekord v0.0.1 entry not initialized") } return strings.ToLower(fmt.Sprintf("%s:%s", *v.HashedRekordObj.Data.Hash.Algorithm, *v.HashedRekordObj.Data.Hash.Value)), nil } func (v V001Entry) Insertable() (bool, error) { if v.HashedRekordObj.Signature == nil { return false, errors.New("missing signature property") } if len(v.HashedRekordObj.Signature.Content) == 0 { return false, errors.New("missing signature content") } if v.HashedRekordObj.Signature.PublicKey == nil { return false, errors.New("missing publicKey property") } if len(v.HashedRekordObj.Signature.PublicKey.Content) == 0 { return false, errors.New("missing publicKey content") } if v.HashedRekordObj.Data == nil { return false, errors.New("missing data property") } if v.HashedRekordObj.Data.Hash == nil { return false, errors.New("missing hash property") } if v.HashedRekordObj.Data.Hash.Algorithm == nil { return false, errors.New("missing hash algorithm") } if v.HashedRekordObj.Data.Hash.Value == nil { return false, errors.New("missing hash value") } return true, nil } rekor-1.3.5/pkg/types/hashedrekord/v0.0.1/entry_test.go000066400000000000000000000546551455727245600226430ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package hashedrekord import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/sha512" "crypto/x509" "crypto/x509/pkix" "encoding/hex" "encoding/pem" "math/big" "reflect" "testing" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" x509r "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" "go.uber.org/goleak" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestNewEntryReturnType(t *testing.T) { entry := NewEntry() if reflect.TypeOf(entry) != reflect.ValueOf(&V001Entry{}).Type() { t.Errorf("invalid type returned from NewEntry: %T", entry) } } func TestRejectsSHA1(t *testing.T) { privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatalf("error generating key: %v", err) } pubBytes, err := cryptoutils.MarshalPublicKeyToPEM(privKey.Public()) if err != nil { t.Fatalf("error marshaling public key: %v", err) } data := []byte("data") signer, err := signature.LoadSigner(privKey, crypto.SHA256) if err != nil { t.Fatalf("error loading verifier: %v", err) } signature, err := signer.SignMessage(bytes.NewReader(data)) if err != nil { t.Fatalf("error signing message: %v", err) } ap := types.ArtifactProperties{ ArtifactBytes: data, ArtifactHash: "sha1:a17c9aaa61e80a1bf71d0d850af4e5baa9800bbd", PublicKeyBytes: [][]byte{pubBytes}, PKIFormat: "x509", SignatureBytes: signature, } ei := NewEntry() _, err = ei.CreateFromArtifactProperties(context.Background(), ap) if err == nil { t.Fatalf("expected error creating entry") } } func TestCrossFieldValidation(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectedHashValue string expectUnmarshalSuccess bool expectCanonicalizeSuccess bool expectedVerifierSuccess bool } key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { t.Fatal(err) } keyBytes := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) // testing lack of support for ed25519 invalidEdPubKey, _, err := ed25519.GenerateKey(rand.Reader) if err != nil { t.Fatal(err) } invalidDer, err := x509.MarshalPKIXPublicKey(invalidEdPubKey) if err != nil { t.Fatal(err) } invalidKeyBytes := pem.EncodeToMemory(&pem.Block{ Bytes: invalidDer, Type: "PUBLIC KEY", }) dataBytes := []byte("sign me!") sha256Sum := sha256.Sum256(dataBytes) sha384Sum := sha512.Sum384(dataBytes) sha512Sum := sha512.Sum512(dataBytes) dataSHA256 := hex.EncodeToString(sha256Sum[:]) dataSHA384 := hex.EncodeToString(sha384Sum[:]) dataSHA512 := hex.EncodeToString(sha512Sum[:]) sha256Signer, _ := signature.LoadSigner(key, crypto.SHA256) sha256SigBytes, _ := sha256Signer.SignMessage(bytes.NewReader(dataBytes)) sha384Signer, _ := signature.LoadSigner(key, crypto.SHA384) sha384SigBytes, _ := sha384Signer.SignMessage(bytes.NewReader(dataBytes)) sha512Signer, _ := signature.LoadSigner(key, crypto.SHA512) sha512SigBytes, _ := sha512Signer.SignMessage(bytes.NewReader(dataBytes)) incorrectLengthHash := sha256.Sum224(dataBytes) incorrectLengthSHA := hex.EncodeToString(incorrectLengthHash[:]) badHash := sha256.Sum256(keyBytes) badDataSHA := hex.EncodeToString(badHash[:]) testCases := []TestCase{ { caseDesc: "empty obj", entry: V001Entry{}, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "signature without url or content", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{}, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "signature without public key", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ Content: sha256SigBytes, }, }, }, expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "signature with empty public key", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{}, }, }, }, expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "signature with ed25519 public key", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: invalidKeyBytes, }, }, }, }, expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, // successful even if unmarshalling fails, because the ed25519 key is valid expectedVerifierSuccess: true, }, { caseDesc: "signature without data", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, }, }, }, expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "signature with empty data", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, }, Data: &models.HashedrekordV001SchemaData{}, }, }, expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "signature with sha256 hash", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, }, Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Value: swag.String(dataSHA256), Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), }, }, }, }, expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: true, expectedVerifierSuccess: true, }, { caseDesc: "signature with sha384 hash", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ Content: sha384SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, }, Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Value: swag.String(dataSHA384), Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha384), }, }, }, }, expectedHashValue: "sha384:" + dataSHA384, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: true, expectedVerifierSuccess: true, }, { caseDesc: "signature with sha512 hash", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ Content: sha512SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, }, Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Value: swag.String(dataSHA512), Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha512), }, }, }, }, expectedHashValue: "sha512:" + dataSHA512, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: true, expectedVerifierSuccess: true, }, { caseDesc: "signature with invalid sha length", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, }, Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Value: swag.String(incorrectLengthSHA), Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), }, }, }, }, expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectCanonicalizeSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "signature with hash & invalid signature", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ Content: sha256SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, }, Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Value: swag.String(badDataSHA), Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), }, }, }, }, expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectCanonicalizeSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "signature with mismatched hash & invalid signature", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ Content: sha512SigBytes, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: keyBytes, }, }, Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Value: swag.String(dataSHA256), Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), }, }, }, }, expectedHashValue: "sha256:" + dataSHA256, expectUnmarshalSuccess: false, expectCanonicalizeSuccess: false, expectedVerifierSuccess: true, }, } for _, tc := range testCases { if _, _, err := tc.entry.validate(); (err == nil) != tc.expectUnmarshalSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } v := &V001Entry{} r := models.Hashedrekord{ APIVersion: swag.String(tc.entry.APIVersion()), Spec: tc.entry.HashedRekordObj, } unmarshalAndValidate := func() error { if err := v.Unmarshal(&r); err != nil { return err } if _, _, err := v.validate(); err != nil { return err } return nil } if err := unmarshalAndValidate(); (err == nil) != tc.expectUnmarshalSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } if tc.expectUnmarshalSuccess { ok, err := v.Insertable() if !ok || err != nil { t.Errorf("unexpected failure in testing insertable on valid entry: %v", err) } hash, err := v.ArtifactHash() if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != tc.expectedHashValue { t.Errorf("unexpected match with ArtifactHash: %s", hash) } } b, err := v.Canonicalize(context.TODO()) if (err == nil) != tc.expectCanonicalizeSuccess { t.Errorf("unexpected result from Canonicalize for '%v': %v", tc.caseDesc, err) } else if err != nil { if _, ok := err.(types.ValidationError); !ok { t.Errorf("canonicalize returned an unexpected error that isn't of type types.ValidationError: %v", err) } } if b != nil { pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tc.caseDesc, err) } ei, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tc.caseDesc, err) } // hashedrekord is one of two types (rfc3161, hashedrekord) in that what is persisted is also insertable ok, err := ei.Insertable() if !ok || err != nil { t.Errorf("unexpected failure in testing insertable on entry created from canonicalized content: %v", err) } hash, err := ei.ArtifactHash() if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != tc.expectedHashValue { t.Errorf("unexpected match with ArtifactHash: %s", hash) } } verifiers, err := v.Verifiers() if tc.expectedVerifierSuccess { if err != nil { t.Errorf("%v: unexpected error, got %v", tc.caseDesc, err) } else { pub, _ := verifiers[0].CanonicalValue() // invalidKeyBytes is a valid ed25519 key if !reflect.DeepEqual(pub, keyBytes) && !reflect.DeepEqual(pub, invalidKeyBytes) { t.Errorf("verifier and public keys do not match: %v, %v", string(pub), string(keyBytes)) } } } else { if err == nil { s, _ := verifiers[0].CanonicalValue() t.Errorf("%v: expected error for %v, got %v", tc.caseDesc, string(s), err) } } } } func hexHash(b []byte) string { h := sha256.Sum256([]byte(b)) return hex.EncodeToString(h[:]) } func TestV001Entry_IndexKeys(t *testing.T) { pub, cert, priv := testKeyAndCert(t) data := "my random data" h := sha256.Sum256([]byte(data)) sig, err := ecdsa.SignASN1(rand.Reader, priv, h[:]) if err != nil { t.Fatal(err) } hashStr := hex.EncodeToString(h[:]) hashIndexKey := "sha256:" + hashStr // Base entry template v := V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Algorithm: swag.String("sha256"), Value: swag.String(hashStr), }, }, Signature: &models.HashedrekordV001SchemaSignature{ Content: strfmt.Base64(sig), PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{}, }, }, } // Test with a public key and a cert // For the public key, we should have the key and the hash. t.Run("public key", func(t *testing.T) { v.HashedRekordObj.Signature.PublicKey.Content = strfmt.Base64(pub) k, err := v.IndexKeys() if err != nil { t.Fatal(err) } keys := map[string]struct{}{} for _, key := range k { keys[key] = struct{}{} } if _, ok := keys[hashIndexKey]; !ok { t.Errorf("missing hash index entry %s, got %v", hashIndexKey, keys) } want := hexHash(pub) if _, ok := keys[want]; !ok { t.Errorf("missing key index entry %s, got %v", want, keys) } }) // For the public key, we should have the key and the hash. t.Run("cert", func(t *testing.T) { v.HashedRekordObj.Signature.PublicKey.Content = strfmt.Base64(cert) k, err := v.IndexKeys() if err != nil { t.Fatal(err) } keys := map[string]struct{}{} for _, key := range k { keys[key] = struct{}{} } if _, ok := keys[hashIndexKey]; !ok { t.Errorf("missing hash index entry for public key test, got %v", keys) } if _, ok := keys[hexHash(cert)]; !ok { t.Errorf("missing key index entry for public key test, got %v", keys) } }) } func testKeyAndCert(t *testing.T) ([]byte, []byte, *ecdsa.PrivateKey) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } ca := &x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ Names: []pkix.AttributeTypeAndValue{ { Type: x509r.EmailAddressOID, Value: "foo@bar.com", }, }, }, } cb, err := x509.CreateCertificate(rand.Reader, ca, ca, &priv.PublicKey, priv) if err != nil { t.Fatal(err) } certPem := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: cb, }) return pub, certPem, priv } func TestInsertable(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectSuccess bool } testCases := []TestCase{ { caseDesc: "valid entry", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), Value: swag.String("deadbeef"), }, }, Signature: &models.HashedrekordV001SchemaSignature{ Content: strfmt.Base64("sig"), PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: strfmt.Base64("key"), }, }, }, }, expectSuccess: true, }, { caseDesc: "missing key", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), Value: swag.String("deadbeef"), }, }, Signature: &models.HashedrekordV001SchemaSignature{ Content: strfmt.Base64("sig"), }, }, }, expectSuccess: false, }, { caseDesc: "missing key content", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), Value: swag.String("deadbeef"), }, }, Signature: &models.HashedrekordV001SchemaSignature{ Content: strfmt.Base64("sig"), PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{}, }, }, }, expectSuccess: false, }, { caseDesc: "missing key content", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), Value: swag.String("deadbeef"), }, }, Signature: &models.HashedrekordV001SchemaSignature{ Content: strfmt.Base64("sig"), PublicKey: nil, }, }, }, expectSuccess: false, }, { caseDesc: "missing sig content", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), Value: swag.String("deadbeef"), }, }, Signature: &models.HashedrekordV001SchemaSignature{ Content: nil, PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: strfmt.Base64("key"), }, }, }, }, expectSuccess: false, }, { caseDesc: "missing hash value", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), }, }, Signature: &models.HashedrekordV001SchemaSignature{ Content: strfmt.Base64("sig"), PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: strfmt.Base64("key"), }, }, }, }, expectSuccess: false, }, { caseDesc: "missing hash algorithm", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Value: swag.String("deadbeef"), }, }, Signature: &models.HashedrekordV001SchemaSignature{ Content: strfmt.Base64("sig"), PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: strfmt.Base64("key"), }, }, }, }, expectSuccess: false, }, { caseDesc: "missing hash object", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Data: &models.HashedrekordV001SchemaData{}, Signature: &models.HashedrekordV001SchemaSignature{ Content: strfmt.Base64("sig"), PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: strfmt.Base64("key"), }, }, }, }, expectSuccess: false, }, { caseDesc: "missing data object", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Signature: &models.HashedrekordV001SchemaSignature{ Content: strfmt.Base64("sig"), PublicKey: &models.HashedrekordV001SchemaSignaturePublicKey{ Content: strfmt.Base64("key"), }, }, }, }, expectSuccess: false, }, { caseDesc: "missing sig object", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{ Data: &models.HashedrekordV001SchemaData{ Hash: &models.HashedrekordV001SchemaDataHash{ Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256), Value: swag.String("deadbeef"), }, }, }, }, expectSuccess: false, }, { caseDesc: "empty object", entry: V001Entry{ HashedRekordObj: models.HashedrekordV001Schema{}, }, expectSuccess: false, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess { t.Errorf("unexpected result calling Insertable: %v", err) } }) } } rekor-1.3.5/pkg/types/hashedrekord/v0.0.1/fuzz_test.go000066400000000000000000000047611455727245600224710ustar00rootroot00000000000000// // Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package hashedrekord import ( "context" "sync" "testing" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/go-openapi/swag" fuzzUtils "github.com/sigstore/rekor/pkg/fuzz" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/hashedrekord" ) var initter sync.Once func FuzzHashedRekordCreateProposedEntry(f *testing.F) { f.Fuzz(func(t *testing.T, propsData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) version := "0.0.1" ff := fuzz.NewConsumer(propsData) props, cleanup, err := fuzzUtils.CreateProps(ff, "hashedrekordV001") if err != nil { t.Skip() } defer func() { for _, c := range cleanup { c() } }() it := hashedrekord.New() entry, err := it.CreateProposedEntry(context.Background(), version, props) if err != nil { t.Skip() } ei, err := types.CreateVersionedEntry(entry) if err != nil { t.Skip() } if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err) } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("valid insertable entry should be able to be canonicalized: %v", err) } _, _ = ei.IndexKeys() }) } func FuzzHashedRekordUnmarshalAndCanonicalize(f *testing.F) { f.Fuzz(func(t *testing.T, entryData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(entryData) targetV001 := &models.HashedrekordV001Schema{} if err := ff.GenerateStruct(targetV001); err != nil { t.Skip() } targetEntry := &models.Hashedrekord{ APIVersion: swag.String(APIVERSION), Spec: targetV001, } ei, err := types.UnmarshalEntry(targetEntry) if err != nil { t.Skip() } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("error canonicalizing unmarshalled entry: %v", err) } }) } rekor-1.3.5/pkg/types/hashedrekord/v0.0.1/hashedrekord_v0_0_1_schema.json000066400000000000000000000045071455727245600260250ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/rekord/rekord_v0_0_1_schema.json", "title": "Hashed Rekor v0.0.1 Schema", "description": "Schema for Hashed Rekord object", "type": "object", "properties": { "signature": { "description": "Information about the detached signature associated with the entry", "type": "object", "properties": { "content": { "description": "Specifies the content of the signature inline within the document", "type": "string", "format": "byte" }, "publicKey" : { "description": "The public key that can verify the signature; this can also be an X509 code signing certificate that contains the raw public key information", "type": "object", "properties": { "content": { "description": "Specifies the content of the public key or code signing certificate inline within the document", "type": "string", "format": "byte" } } } } }, "data": { "description": "Information about the content associated with the entry", "type": "object", "properties": { "hash": { "description": "Specifies the hash algorithm and value for the content", "type": "object", "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256", "sha384", "sha512" ] }, "value": { "description": "The hash value for the content, as represented by a lower case hexadecimal string", "type": "string" } }, "required": [ "algorithm", "value" ] } } } }, "required": [ "signature", "data" ] } rekor-1.3.5/pkg/types/helm/000077500000000000000000000000001455727245600155355ustar00rootroot00000000000000rekor-1.3.5/pkg/types/helm/helm.go000066400000000000000000000034371455727245600170200ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package helm import ( "context" "errors" "fmt" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" ) const ( KIND = "helm" ) type BaseHelmType struct { types.RekorType } func init() { types.TypeMap.Store(KIND, New) } func New() types.TypeImpl { bit := BaseHelmType{} bit.Kind = KIND bit.VersionMap = VersionMap return &bit } var VersionMap = types.NewSemVerEntryFactoryMap() func (it BaseHelmType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) { if pe == nil { return nil, errors.New("proposed entry cannot be nil") } in, ok := pe.(*models.Helm) if !ok { return nil, errors.New("cannot unmarshal non-Rekord types") } return it.VersionedUnmarshal(in, *in.APIVersion) } func (it *BaseHelmType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) { if version == "" { version = it.DefaultVersion() } ei, err := it.VersionedUnmarshal(nil, version) if err != nil { return nil, fmt.Errorf("fetching Rekord version implementation: %w", err) } return ei.CreateFromArtifactProperties(ctx, props) } func (it BaseHelmType) DefaultVersion() string { return "0.0.1" } rekor-1.3.5/pkg/types/helm/helm_schema.json000066400000000000000000000004771455727245600207050ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/helm/helm_schema.json", "title": "Helm Schema", "description": "Schema for Helm objects", "type": "object", "oneOf": [ { "$ref": "v0.0.1/helm_v0_0_1_schema.json" } ] }rekor-1.3.5/pkg/types/helm/helm_test.go000066400000000000000000000055051455727245600200550ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package helm import ( "errors" "testing" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" ) type UnmarshalTester struct { models.Helm types.BaseUnmarshalTester } type UnmarshalFailsTester struct { types.BaseUnmarshalTester } func (u UnmarshalFailsTester) NewEntry() types.EntryImpl { return &UnmarshalFailsTester{} } func (u UnmarshalFailsTester) Unmarshal(_ models.ProposedEntry) error { return errors.New("error") } func (u UnmarshalFailsTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } func TestHelmType(t *testing.T) { // empty to start if VersionMap.Count() != 0 { t.Error("semver range was not blank at start of test") } u := UnmarshalTester{} // ensure semver range parser is working invalidSemVerRange := "not a valid semver range" err := VersionMap.SetEntryFactory(invalidSemVerRange, u.NewEntry) if err == nil || VersionMap.Count() > 0 { t.Error("invalid semver range was incorrectly added to SemVerToFacFnMap") } // valid semver range can be parsed err = VersionMap.SetEntryFactory(">= 1.2.3", u.NewEntry) if err != nil || VersionMap.Count() != 1 { t.Error("valid semver range was not added to SemVerToFacFnMap") } u.Helm.APIVersion = swag.String("2.0.1") brt := New() // version requested matches implementation in map if _, err := brt.UnmarshalEntry(&u.Helm); err != nil { t.Errorf("unexpected error in Unmarshal: %v", err) } // version requested fails to match implementation in map u.Helm.APIVersion = swag.String("1.2.2") if _, err := brt.UnmarshalEntry(&u.Helm); err == nil { t.Error("unexpected success in Unmarshal for non-matching version") } // error in Unmarshal call is raised appropriately u.Helm.APIVersion = swag.String("2.2.0") u2 := UnmarshalFailsTester{} _ = VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry) if _, err := brt.UnmarshalEntry(&u.Helm); err == nil { t.Error("unexpected success in Unmarshal when error is thrown") } // version requested fails to match implementation in map u.Helm.APIVersion = swag.String("not_a_version") if _, err := brt.UnmarshalEntry(&u.Helm); err == nil { t.Error("unexpected success in Unmarshal for invalid version") } } rekor-1.3.5/pkg/types/helm/provenance.go000066400000000000000000000050741455727245600202320ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package helm import ( "bytes" "errors" "fmt" "io" "strings" "sigs.k8s.io/yaml" //TODO: https://github.com/sigstore/rekor/issues/286 "golang.org/x/crypto/openpgp/clearsign" //nolint:staticcheck ) type Provenance struct { ChartMetadata map[string]string SumCollection *SumCollection Block *clearsign.Block } type SumCollection struct { Files map[string]string `json:"files"` Images map[string]string `json:"images,omitempty"` } func (p *Provenance) Unmarshal(reader io.Reader) error { buf := &bytes.Buffer{} read, err := buf.ReadFrom(reader) if err != nil { return errors.New("Failed to read from buffer") } else if read == 0 { return errors.New("Provenance file contains no content") } block, _ := clearsign.Decode(buf.Bytes()) if block == nil { return errors.New("Unable to decode provenance file") } if block.ArmoredSignature == nil { return errors.New("Unable to locate armored signature in provenance file") } if err = p.parseMessageBlock(block.Plaintext); err != nil { return fmt.Errorf("Error occurred parsing message block: %w", err) } p.Block = block return nil } func (p *Provenance) parseMessageBlock(data []byte) error { parts := bytes.Split(data, []byte("\n...\n")) if len(parts) < 2 { return errors.New("message block must have at least two parts") } sc := &SumCollection{} if err := yaml.Unmarshal(parts[1], sc); err != nil { return fmt.Errorf("Error occurred parsing SumCollection: %w", err) } p.SumCollection = sc return nil } func (p *Provenance) GetChartAlgorithmHash() (string, string, error) { if p.SumCollection == nil || p.SumCollection.Files == nil { return "", "", errors.New("Unable to locate chart hash") } for _, value := range p.SumCollection.Files { parts := strings.Split(value, ":") if len(parts) != 2 { return "", "", errors.New("Invalid hash found in Provenance file") } return parts[0], parts[1], nil } // Return error if no keys found return "", "", errors.New("No checksums found") } rekor-1.3.5/pkg/types/helm/provenance_test.go000066400000000000000000000036661455727245600212760ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package helm import ( "bytes" "os" "testing" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki/pgp" ) func TestProvenance(t *testing.T) { inputProvenance, err := os.Open("tests/test-0.1.0.tgz.prov") if err != nil { t.Fatalf("could not open provenance file %v", err) } provenance := Provenance{} err = provenance.Unmarshal(inputProvenance) if err != nil { t.Fatalf("unmarshal error: %v", err) } algorithm, checksum, err := provenance.GetChartAlgorithmHash() if err != nil { t.Fatalf("Error retrieving chart hash: %v", err) } if models.HelmV001SchemaChartHashAlgorithmSha256 != algorithm { t.Fatalf("Unexpected checksum algorithm. Expected %s, found %s", models.HelmV001SchemaChartHashAlgorithmSha256, algorithm) } if len(checksum) == 0 { t.Fatal("Empty checksum") } publickeyFile, err := os.Open("tests/test_helm_armor.pub") if err != nil { t.Fatalf("could not open public key %v", err) } publicKey, err := pgp.NewPublicKey(publickeyFile) if err != nil { t.Fatalf("failed to parse public key: %v", err) } sig, err := pgp.NewSignature(provenance.Block.ArmoredSignature.Body) if err != nil { t.Fatalf("Failed to create signature %v", err) } err = sig.Verify(bytes.NewBuffer(provenance.Block.Bytes), publicKey) if err != nil { t.Fatalf("Failed to verify signature %v", err) } } rekor-1.3.5/pkg/types/helm/tests/000077500000000000000000000000001455727245600166775ustar00rootroot00000000000000rekor-1.3.5/pkg/types/helm/tests/test-0.1.0.tgz000066400000000000000000000061731455727245600210450ustar00rootroot00000000000000)+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=Helm;o۸bNe9B@?npmj$m(hilH-I9񺾿-viL E΃CJHHaxgavv`p`AVSI#9RQ#x X\PiYvxdY1sFcba% :Aݷ\۶Y?$@ [NO - iM<»* uu/dF80$CZ@!'Jak#QYΈFxD5Euϣ`H̅ZQ/򂱮`4Ep?+Q!Cx;D)ibH,BdWPI)"uc͌*=_KfcK*HX~BPiGqle]>EW)%(8@P(XbK$ʧ,":\hkIF:`#Ow)aD(PX>pA“>Ys U0@p |`WNBQ.wBR=:\]w_.EG*G=@"E^=k,$o9 2T#1ZH n:%3VǬPi,X!u/B,y% ;JvD(TAXdj] Pvь w:N Ob3\Ti"ff) 3aJfB*c 0#OD'Le;u# 5ek]1rm|YE{c鯆HZHgaZ0c>~ƁDdb!$GP Ц_Xtxl=jLԜLv{6ޞ}{'&Ś5Z&Q9 L Φ |Qi_`UO/U &cV$ϫ4˷b>mumpepj,[J]$}叉oSag-jlDzZׂ$CDMj"8Րhr@W&ڼup-w%dH(3+4p\KWDǩˌ&Oob5un&%sg1mBr -h*Wxǥ="2|6 }{ͫrl$3Zr4S*f=t|qJ]l2*ZiMjM,TQ]sAtደܤ=u_@d74+"xT EXwF9ETo.]ow3un ޳vS>[WP$*^Wв1G]>73ϲvJOdJD]F-Ӂ':^ F=qJd7N>E( ]˪U7j890& 0i;t^!0WYt1 W1wLR0Vr\x]if7VD&Ϗ,gsTI3 :>:\RZ)|m-[q4QoiY H٪) lQW{.$l}?n<"w칗YmYRkTeu0Riݼ[Lg&oY.ƆX2Vo%@e{jͺjZJպoH!Rh!ei6JagIE\/k/  U,l"&m۶m۶' Frekor-1.3.5/pkg/types/helm/tests/test-0.1.0.tgz.prov000066400000000000000000000012651455727245600220270ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 apiVersion: v2 description: Test Helm Chart name: test type: application version: 0.1.0 ... files: test-0.1.0.tgz: sha256:6dec7ea21e655d5796c1e214cfb75b73428b2abfa2e66c8f7bc64ff4a7b3b29f -----BEGIN PGP SIGNATURE----- wsBcBAEBCgAQBQJg1/71CRDDyCivtmgcFwAAMEcIABaRFqHhb6ibQqIC7xkn0U7j SDEKoDPt7lKJlBWe/9Q1hE1L7AslczkCBBdLI67lHJnEQH6doT2n6ww3yoJJw7gw w4ZkNqhdYAYbYDE8FlwgMdi3VqPrCABZA2G9zh9PcmjrS1S+GrznujK26OLA3ooc giBTLJ5wsVuP2aQpr+b3czK4pLmWjs1lDywoEtptxgK+Ooyt2XqTzkmWDZvSiYEQ vrbb71muPbJVn0+1mW88EyACoUiqNZLu15kyNgSZC6By/C58RPatYz9DieyZoSIQ kJeTX/NeHfwBHbpy7fenDfnMbj4LqjvLxeyod7Nc/57dUesimR++2ZC5A4bkq+c= =BNTk -----END PGP SIGNATURE-----rekor-1.3.5/pkg/types/helm/tests/test_helm_armor.pub000066400000000000000000000033051455727245600225740ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- mQENBGDX7rcBCACdHDAoYc4ZxrxaVNfkPhHwNjUDKMTkQIC/laU64yUaR4ZyHu9W 5OH1dU6+yCFeysyNpwa0v1701Q6goJkd4BLVNK7JEKTp7gHPznSJkdFsFAN7P7gq UB+ZJgVRr9g9F83fNJXfzS5DVxC691yn1O5fm9sGlXRYsqhktuhOX8CKgQpdvYNw aO8gfdK2tB5ebsk7yJUvWj0h12fnQgxRlBtCihN8Hf+tmXVzKZZb27tN0SfdXmhX aSuHNMUunS/hz3xjc3rXkaWZ4lSqqZOX00C0hSRIey4L6vNBHi75oFzKFCesZchB VSGs//iPllQMHiVGGyXRuOvoC1BjnDT84WIdABEBAAG0DG5vdEByZWFsLmNvbYkB VAQTAQgAPhYhBI1o/WP1Lc9+qnJWW8PIKK+2aBwXBQJg1+63AhsDBQkDwmcABQsJ CAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEMPIKK+2aBwXlQ8IAJPpkjiACt9aaJIF Dn5DYVu2WEp96Ft9SwV4Ege2cPKQy8JVtyweSF+lgll0N0r/G3uajmuaAGxGo9x4 iuhRgGeDWheO9TFKSMLb0Fj7FyRg1XOHIXujh7WlnnYVnG3hg8LYhzJenfyNLAsO 6CtER2naadhJMbKwhNl6X86ILtjC+LKyQPNK9ECMVUwZHULzEskoJLsOFW6Y/I2F q8qoASQK7dmM0ZJHAkwZTeJgAxQvBQtHkTA7LxBXNYXbE4IaMAUWMyL9rnlkUn5e iAr9W+OAyUPwS8Wn5s/as0YG7uoFvfAYjx5DLsL1Lf3UBu1FXNNvhg30WakTEDYp kyTO3QG5AQ0EYNfutwEIAMpplZyyHhcI+Mw8MWyKovRqHoaEUeG754XS5c/YEi+T h55g4BkWf0Tq+ZZ1C0QGdL/h6uzKeD/hwqRJk6tSEdWEV11s6aglS7ps9Z3kady8 7DiwDC7GOPueVRtLfyjQCGfpuoKriDEV155W13BOTGiMin9evWfUTeZ5XFO5UI5S CHDY8u/zkepVFHE/vM/GX5eo2NTkeZWBwzDvAd0pmhLyCKxfJdLASIKjSmF2aPnu WCXT732Aj3JY2vL6Z3k1rFT3gbScTFFL+HxytP5QAoR0IwVF/Q5Fw2wKd/1PHt8I uwDd2eqBrt32JPYSugmeZnEZFDSIYXyui4jFFipf0vEAEQEAAYkBPAQYAQgAJhYh BI1o/WP1Lc9+qnJWW8PIKK+2aBwXBQJg1+63AhsMBQkDwmcAAAoJEMPIKK+2aBwX Y6gH/2p6EKEA0XnnIcSae2umq1Xz39IvQHOcvRPfgybtMd2uVqhup17ONhOXkzpa qxlTF3djDzu4eisK5XpeC/5dHnozHPiey3AqOvVpdPPzvfb8O8pASu+uo+eE0/9G dww401OjMKi3nVfvLitcttccbszWoMpCGybJvoKBh4bsjSfej1OXorEJNATXazlN CVUhBNDz8mB+e3Dmz/3vzQryfksi0LxU1YpX1f2sWxMq8kLp3yJWnjRIYJQ9A7Vs HMv65ebKlfpoUGCtWiKD2bF5lysFoiL+gDNzsbb9ylWfljmYOFw+wNCFvf/yHYxX xfmrMdZ0nbvRfjuMr27eOY/vO+M= =IqHu -----END PGP PUBLIC KEY BLOCK----- rekor-1.3.5/pkg/types/helm/v0.0.1/000077500000000000000000000000001455727245600163575ustar00rootroot00000000000000rekor-1.3.5/pkg/types/helm/v0.0.1/entry.go000066400000000000000000000237341455727245600200600ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package helm import ( "bytes" "context" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "io" "os" "path/filepath" "strings" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/pki/pgp" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/helm" "github.com/sigstore/rekor/pkg/util" "golang.org/x/sync/errgroup" ) const ( APIVERSION = "0.0.1" ) func init() { if err := helm.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil { log.Logger.Panic(err) } } type V001Entry struct { HelmObj models.HelmV001Schema } func (v V001Entry) APIVersion() string { return APIVERSION } func NewEntry() types.EntryImpl { return &V001Entry{} } func (v V001Entry) IndexKeys() ([]string, error) { var result []string keyObj, err := pgp.NewPublicKey(bytes.NewReader(*v.HelmObj.PublicKey.Content)) if err != nil { return nil, err } provenance := helm.Provenance{} if err := provenance.Unmarshal(bytes.NewReader(v.HelmObj.Chart.Provenance.Content)); err != nil { return nil, err } key, err := keyObj.CanonicalValue() if err != nil { return nil, err } keyHash := sha256.Sum256(key) result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:]))) result = append(result, keyObj.Subjects()...) algorithm, chartHash, err := provenance.GetChartAlgorithmHash() if err != nil { log.Logger.Error(err) } else { hashKey := strings.ToLower(fmt.Sprintf("%s:%s", algorithm, chartHash)) result = append(result, hashKey) } return result, nil } func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error { helm, ok := pe.(*models.Helm) if !ok { return errors.New("cannot unmarshal non Helm v0.0.1 type") } if err := types.DecodeEntry(helm.Spec, &v.HelmObj); err != nil { return err } // field validation if err := v.HelmObj.Validate(strfmt.Default); err != nil { return err } // cross field validation return v.validate() } func (v *V001Entry) fetchExternalEntities(ctx context.Context) (*helm.Provenance, *pgp.PublicKey, *pgp.Signature, error) { if err := v.validate(); err != nil { return nil, nil, nil, types.ValidationError(err) } g, ctx := errgroup.WithContext(ctx) provenanceR, provenanceW := io.Pipe() defer provenanceR.Close() closePipesOnError := types.PipeCloser(provenanceR, provenanceW) g.Go(func() error { defer provenanceW.Close() dataReadCloser := bytes.NewReader(v.HelmObj.Chart.Provenance.Content) /* #nosec G110 */ if _, err := io.Copy(provenanceW, dataReadCloser); err != nil { return closePipesOnError(err) } return nil }) keyResult := make(chan *pgp.PublicKey) g.Go(func() error { defer close(keyResult) keyReadCloser := bytes.NewReader(*v.HelmObj.PublicKey.Content) keyObj, err := pgp.NewPublicKey(keyReadCloser) if err != nil { return closePipesOnError(types.ValidationError(err)) } select { case <-ctx.Done(): return ctx.Err() case keyResult <- keyObj: return nil } }) var key *pgp.PublicKey provenance := &helm.Provenance{} var sig *pgp.Signature g.Go(func() error { if err := provenance.Unmarshal(provenanceR); err != nil { return closePipesOnError(types.ValidationError(err)) } key = <-keyResult if key == nil { return closePipesOnError(errors.New("error processing public key")) } // Set signature var err error sig, err = pgp.NewSignature(provenance.Block.ArmoredSignature.Body) if err != nil { return closePipesOnError(types.ValidationError(err)) } // Verify signature if err := sig.Verify(bytes.NewReader(provenance.Block.Bytes), key); err != nil { return closePipesOnError(types.ValidationError(err)) } select { case <-ctx.Done(): return ctx.Err() default: return nil } }) if err := g.Wait(); err != nil { return nil, nil, nil, err } return provenance, key, sig, nil } func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) { provenanceObj, keyObj, sigObj, err := v.fetchExternalEntities(ctx) if err != nil { return nil, err } if keyObj == nil { return nil, errors.New("key object not initialized before canonicalization") } canonicalEntry := models.HelmV001Schema{} canonicalEntry.PublicKey = &models.HelmV001SchemaPublicKey{} keyContent, err := keyObj.CanonicalValue() if err != nil { return nil, err } canonicalEntry.PublicKey.Content = (*strfmt.Base64)(&keyContent) canonicalEntry.Chart = &models.HelmV001SchemaChart{} algorithm, chartHash, err := provenanceObj.GetChartAlgorithmHash() if err != nil { return nil, err } canonicalEntry.Chart.Hash = &models.HelmV001SchemaChartHash{} canonicalEntry.Chart.Hash.Algorithm = &algorithm canonicalEntry.Chart.Hash.Value = &chartHash canonicalEntry.Chart.Provenance = &models.HelmV001SchemaChartProvenance{} canonicalEntry.Chart.Provenance.Signature = &models.HelmV001SchemaChartProvenanceSignature{} sigContent, err := sigObj.CanonicalValue() if err != nil { return nil, err } canonicalEntry.Chart.Provenance.Signature.Content = sigContent // wrap in valid object with kind and apiVersion set helmObj := models.Helm{} helmObj.APIVersion = swag.String(APIVERSION) helmObj.Spec = &canonicalEntry return json.Marshal(&helmObj) } // validate performs cross-field validation for fields in object func (v V001Entry) validate() error { key := v.HelmObj.PublicKey if key == nil { return errors.New("missing public key") } if key.Content == nil || len(*key.Content) == 0 { return errors.New("'content' must be specified for publicKey") } chart := v.HelmObj.Chart if chart == nil { return errors.New("missing chart") } provenance := chart.Provenance if provenance == nil { return errors.New("missing provenance") } if provenance.Signature == nil || provenance.Signature.Content == nil { if len(provenance.Content) == 0 { return errors.New("'content' must be specified for provenance") } } return nil } func (v V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) { //TODO: how to select version of item to create returnVal := models.Helm{} re := V001Entry{} // we will need provenance file and public-key re.HelmObj = models.HelmV001Schema{} re.HelmObj.Chart = &models.HelmV001SchemaChart{} re.HelmObj.Chart.Provenance = &models.HelmV001SchemaChartProvenance{} var err error artifactBytes := props.ArtifactBytes if artifactBytes == nil { var artifactReader io.ReadCloser if props.ArtifactPath == nil { return nil, errors.New("path to artifact file must be specified") } if props.ArtifactPath.IsAbs() { artifactReader, err = util.FileOrURLReadCloser(ctx, props.ArtifactPath.String(), nil) if err != nil { return nil, fmt.Errorf("error reading chart file: %w", err) } } else { artifactReader, err = os.Open(filepath.Clean(props.ArtifactPath.Path)) if err != nil { return nil, fmt.Errorf("error opening chart file: %w", err) } } artifactBytes, err = io.ReadAll(artifactReader) if err != nil { return nil, fmt.Errorf("error reading chart file: %w", err) } } re.HelmObj.Chart.Provenance.Content = strfmt.Base64(artifactBytes) re.HelmObj.PublicKey = &models.HelmV001SchemaPublicKey{} publicKeyBytes := props.PublicKeyBytes if len(publicKeyBytes) == 0 { if len(props.PublicKeyPaths) != 1 { return nil, errors.New("only one public key must be provided") } keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path)) if err != nil { return nil, fmt.Errorf("error reading public key file: %w", err) } publicKeyBytes = append(publicKeyBytes, keyBytes) } else if len(publicKeyBytes) != 1 { return nil, errors.New("only one public key must be provided") } re.HelmObj.PublicKey.Content = (*strfmt.Base64)(&publicKeyBytes[0]) if err := re.validate(); err != nil { return nil, err } if _, _, _, err := re.fetchExternalEntities(ctx); err != nil { return nil, fmt.Errorf("error retrieving external entities: %v", err) } returnVal.APIVersion = swag.String(re.APIVersion()) returnVal.Spec = re.HelmObj return &returnVal, nil } func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { if v.HelmObj.PublicKey == nil || v.HelmObj.PublicKey.Content == nil { return nil, errors.New("helm v0.0.1 entry not initialized") } key, err := pgp.NewPublicKey(bytes.NewReader(*v.HelmObj.PublicKey.Content)) if err != nil { return nil, err } return []pki.PublicKey{key}, nil } func (v V001Entry) ArtifactHash() (string, error) { if v.HelmObj.Chart == nil || v.HelmObj.Chart.Hash == nil || v.HelmObj.Chart.Hash.Algorithm == nil || v.HelmObj.Chart.Hash.Value == nil { return "", errors.New("helm v0.0.1 entry not initialized") } return strings.ToLower(fmt.Sprintf("%s:%s", *v.HelmObj.Chart.Hash.Algorithm, *v.HelmObj.Chart.Hash.Value)), nil } func (v V001Entry) Insertable() (bool, error) { if v.HelmObj.PublicKey == nil { return false, errors.New("missing public key property") } if v.HelmObj.PublicKey.Content == nil || len(*v.HelmObj.PublicKey.Content) == 0 { return false, errors.New("missing public key content") } if v.HelmObj.Chart == nil { return false, errors.New("missing chart property") } if v.HelmObj.Chart.Provenance == nil { return false, errors.New("missing provenance property") } if len(v.HelmObj.Chart.Provenance.Content) == 0 { return false, errors.New("missing provenance content") } return true, nil } rekor-1.3.5/pkg/types/helm/v0.0.1/entry_test.go000066400000000000000000000232131455727245600211070ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package helm import ( "bytes" "context" "os" "reflect" "testing" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "go.uber.org/goleak" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestNewEntryReturnType(t *testing.T) { entry := NewEntry() if reflect.TypeOf(entry) != reflect.ValueOf(&V001Entry{}).Type() { t.Errorf("invalid type returned from NewEntry: %T", entry) } } func TestCrossFieldValidation(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectUnmarshalSuccess bool expectCanonicalizeSuccess bool expectedVerifierSuccess bool } keyBytes, _ := os.ReadFile("../tests/test_helm_armor.pub") provenanceBytes, _ := os.ReadFile("../tests/test-0.1.0.tgz.prov") testCases := []TestCase{ { caseDesc: "empty obj", entry: V001Entry{}, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "provenance file without public key", entry: V001Entry{ HelmObj: models.HelmV001Schema{ Chart: &models.HelmV001SchemaChart{ Provenance: &models.HelmV001SchemaChartProvenance{ Content: strfmt.Base64(provenanceBytes), }, }, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "public key without provenance file", entry: V001Entry{ HelmObj: models.HelmV001Schema{ PublicKey: &models.HelmV001SchemaPublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "public key with empty provenance file", entry: V001Entry{ HelmObj: models.HelmV001Schema{ PublicKey: &models.HelmV001SchemaPublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, Chart: &models.HelmV001SchemaChart{ Provenance: &models.HelmV001SchemaChartProvenance{}, }, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "public key and invalid provenance content", entry: V001Entry{ HelmObj: models.HelmV001Schema{ PublicKey: &models.HelmV001SchemaPublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, Chart: &models.HelmV001SchemaChart{ Provenance: &models.HelmV001SchemaChartProvenance{ Content: strfmt.Base64(keyBytes), }, }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "provenance content with invalid public key", entry: V001Entry{ HelmObj: models.HelmV001Schema{ PublicKey: &models.HelmV001SchemaPublicKey{ Content: (*strfmt.Base64)(&provenanceBytes), }, Chart: &models.HelmV001SchemaChart{ Provenance: &models.HelmV001SchemaChartProvenance{ Content: strfmt.Base64(provenanceBytes), }, }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "provenance content with valid public key", entry: V001Entry{ HelmObj: models.HelmV001Schema{ PublicKey: &models.HelmV001SchemaPublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, Chart: &models.HelmV001SchemaChart{ Provenance: &models.HelmV001SchemaChartProvenance{ Content: strfmt.Base64(provenanceBytes), }, }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: true, expectedVerifierSuccess: true, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if err := tc.entry.validate(); (err == nil) != tc.expectUnmarshalSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } v := &V001Entry{} r := models.Helm{ APIVersion: swag.String(tc.entry.APIVersion()), Spec: tc.entry.HelmObj, } unmarshalAndValidate := func() error { if err := v.Unmarshal(&r); err != nil { return err } return v.validate() } if err := unmarshalAndValidate(); (err == nil) != tc.expectUnmarshalSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } if tc.expectUnmarshalSuccess { ok, err := v.Insertable() if !ok || err != nil { t.Errorf("unexpected error calling Insertable on valid proposed entry: %v", err) } } b, err := v.Canonicalize(context.TODO()) if (err == nil) != tc.expectCanonicalizeSuccess { t.Errorf("unexpected result from Canonicalize for '%v': %v", tc.caseDesc, err) } else if err != nil { if _, ok := err.(types.ValidationError); !ok { t.Errorf("canonicalize returned an unexpected error that isn't of type types.ValidationError: %v", err) } } if b != nil { pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tc.caseDesc, err) } ei, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tc.caseDesc, err) } if ok, err := ei.Insertable(); ok || err == nil { t.Errorf("unexpected success calling Insertable on entry created from canonicalized content") } hash, err := ei.ArtifactHash() if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != "sha256:6dec7ea21e655d5796c1e214cfb75b73428b2abfa2e66c8f7bc64ff4a7b3b29f" { t.Errorf("unexpected match with ArtifactHash: %s", hash) } } verifiers, err := v.Verifiers() if tc.expectedVerifierSuccess { if err != nil { t.Errorf("%v: unexpected error, got %v", tc.caseDesc, err) } else { // TODO: Improve this test once CanonicalValue returns same result as input for PGP keys _, err := verifiers[0].CanonicalValue() if err != nil { t.Errorf("%v: unexpected error getting canonical value, got %v", tc.caseDesc, err) } } } else { if err == nil { s, _ := verifiers[0].CanonicalValue() t.Errorf("%v: expected error for %v, got %v", tc.caseDesc, string(s), err) } } }) } } func TestInsertable(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectSuccess bool } pubKey := strfmt.Base64([]byte("pubKey")) testCases := []TestCase{ { caseDesc: "valid entry", entry: V001Entry{ HelmObj: models.HelmV001Schema{ Chart: &models.HelmV001SchemaChart{ Provenance: &models.HelmV001SchemaChartProvenance{ Content: strfmt.Base64([]byte("content")), }, }, PublicKey: &models.HelmV001SchemaPublicKey{ Content: &pubKey, }, }, }, expectSuccess: true, }, { caseDesc: "missing key content", entry: V001Entry{ HelmObj: models.HelmV001Schema{ Chart: &models.HelmV001SchemaChart{ Provenance: &models.HelmV001SchemaChartProvenance{ Content: strfmt.Base64([]byte("content")), }, }, PublicKey: &models.HelmV001SchemaPublicKey{ //Content: &pubKey, }, }, }, expectSuccess: false, }, { caseDesc: "missing key", entry: V001Entry{ HelmObj: models.HelmV001Schema{ Chart: &models.HelmV001SchemaChart{ Provenance: &models.HelmV001SchemaChartProvenance{ Content: strfmt.Base64([]byte("content")), }, }, /* PublicKey: &models.HelmV001SchemaPublicKey{ Content: &pubKey, }, */ }, }, expectSuccess: false, }, { caseDesc: "missing provenance content", entry: V001Entry{ HelmObj: models.HelmV001Schema{ Chart: &models.HelmV001SchemaChart{ Provenance: &models.HelmV001SchemaChartProvenance{ //Content: strfmt.Base64([]byte("content")), }, }, PublicKey: &models.HelmV001SchemaPublicKey{ Content: &pubKey, }, }, }, expectSuccess: false, }, { caseDesc: "missing provenance obj", entry: V001Entry{ HelmObj: models.HelmV001Schema{ Chart: &models.HelmV001SchemaChart{ /* Provenance: &models.HelmV001SchemaChartProvenance{ Content: strfmt.Base64([]byte("content")), }, */ }, PublicKey: &models.HelmV001SchemaPublicKey{ Content: &pubKey, }, }, }, expectSuccess: false, }, { caseDesc: "missing chart obj", entry: V001Entry{ HelmObj: models.HelmV001Schema{ /* Chart: &models.HelmV001SchemaChart{ Provenance: &models.HelmV001SchemaChartProvenance{ Content: strfmt.Base64([]byte("content")), }, }, */ PublicKey: &models.HelmV001SchemaPublicKey{ Content: &pubKey, }, }, }, expectSuccess: false, }, { caseDesc: "empty obj", entry: V001Entry{}, expectSuccess: false, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess { t.Errorf("unexpected result calling Insertable: %v", err) } }) } } rekor-1.3.5/pkg/types/helm/v0.0.1/fuzz_test.go000066400000000000000000000051031455727245600207420ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package helm import ( "bytes" "context" "sync" "testing" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/go-openapi/swag" fuzzUtils "github.com/sigstore/rekor/pkg/fuzz" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/helm" ) var initter sync.Once func FuzzHelmCreateProposedEntry(f *testing.F) { f.Fuzz(func(t *testing.T, propsData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) version := "0.0.1" ff := fuzz.NewConsumer(propsData) props, cleanup, err := fuzzUtils.CreateProps(ff, "helmV001") if err != nil { t.Skip() } defer func() { for _, c := range cleanup { c() } }() it := helm.New() entry, err := it.CreateProposedEntry(context.Background(), version, props) if err != nil { t.Skip() } ei, err := types.CreateVersionedEntry(entry) if err != nil { t.Skip() } if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err) } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("valid insertable entry should be able to be canonicalized: %v", err) } _, _ = ei.IndexKeys() }) } func FuzzHelmUnmarshalAndCanonicalize(f *testing.F) { f.Fuzz(func(t *testing.T, entryData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(entryData) targetV001 := &models.HelmV001Schema{} if err := ff.GenerateStruct(targetV001); err != nil { t.Skip() } targetEntry := &models.Helm{ APIVersion: swag.String(APIVERSION), Spec: targetV001, } ei, err := types.UnmarshalEntry(targetEntry) if err != nil { t.Skip() } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Skip() } }) } func FuzzHelmProvenanceUnmarshal(f *testing.F) { f.Fuzz(func(t *testing.T, provenanceData []byte) { p := &helm.Provenance{} r := bytes.NewReader(provenanceData) p.Unmarshal(r) }) } rekor-1.3.5/pkg/types/helm/v0.0.1/helm_v0_0_1_schema.json000066400000000000000000000065151455727245600225720ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/helm/helm_v0_0_1_schema.json", "title": "Helm v0.0.1 Schema", "description": "Schema for Helm object", "type": "object", "properties": { "publicKey": { "description": "The public key that can verify the package signature", "type": "object", "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } }, "required": [ "content" ] }, "chart": { "description": "Information about the Helm chart associated with the entry", "type": "object", "properties": { "hash": { "description": "Specifies the hash algorithm and value for the chart", "type": "object", "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the chart", "type": "string" } }, "required": [ "algorithm", "value" ], "readOnly": true }, "provenance": { "description": "The provenance entry associated with the signed Helm Chart", "type": "object", "properties": { "signature": { "description": "Information about the included signature in the provenance file", "type": "object", "properties": { "content": { "description": "Specifies the signature embedded within the provenance file ", "type": "string", "format": "byte", "readOnly": true } }, "required": [ "content" ], "readOnly": true }, "content": { "description": "Specifies the content of the provenance file inline within the document", "type": "string", "format": "byte", "writeOnly": true } }, "oneOf": [ { "required": [ "signature" ] }, { "required": [ "content" ] } ] } }, "required": [ "provenance" ] } }, "required": [ "publicKey", "chart" ] } rekor-1.3.5/pkg/types/intoto/000077500000000000000000000000001455727245600161245ustar00rootroot00000000000000rekor-1.3.5/pkg/types/intoto/README.md000066400000000000000000000022661455727245600174110ustar00rootroot00000000000000**in-toto Type Data Documentation** This document provides a definition for each field that is not otherwise described in the [in-toto schema](https://github.com/sigstore/rekor/blob/main/pkg/types/intoto/v0.0.1/intoto_v0_0_1_schema.json). This document also notes any additional information about the values associated with each field such as the format in which the data is stored and any necessary transformations. **Attestation:** authenticated, machine-readable metadata about one or more software artifacts. [SLSA definition](https://github.com/slsa-framework/slsa/blob/main/controls/attestations.md) - The Attestation value ought to be a Base64-encoded JSON object. - The [in-toto Attestation specification](https://github.com/in-toto/attestation/blob/main/spec/README.md#statement) provides detailed guidance on understanding and parsing this JSON object. **AttestationType:** Identifies the type of attestation being made, such as a provenance attestation or a vulnerability scan attestation. AttestationType's value, even when prefixed with an http, is not necessarily a working URL. **How do you identify an object as an in-toto object?** The "Body" field will include an "IntotoObj" field. rekor-1.3.5/pkg/types/intoto/e2e_test.go000066400000000000000000000404401455727245600201670ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package intoto import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/json" "encoding/pem" "fmt" "io/ioutil" "net/http" "os" "path/filepath" "testing" "github.com/google/go-cmp/cmp" "github.com/in-toto/in-toto-golang/in_toto" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/sharding" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/sigstore/pkg/signature" sigx509 "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/util" ) func rekorServer() string { if s := os.Getenv("REKOR_SERVER"); s != "" { return s } return "http://localhost:3000" } func TestIntoto(t *testing.T) { td := t.TempDir() attestationPath := filepath.Join(td, "attestation.json") pubKeyPath := filepath.Join(td, "pub.pem") // Get some random data so it's unique each run d := util.RandomData(t, 10) id := base64.StdEncoding.EncodeToString(d) it := in_toto.ProvenanceStatement{ StatementHeader: in_toto.StatementHeader{ Type: in_toto.StatementInTotoV01, PredicateType: slsa.PredicateSLSAProvenance, Subject: []in_toto.Subject{ { Name: "foobar", Digest: common.DigestSet{ "foo": "bar", }, }, }, }, Predicate: slsa.ProvenancePredicate{ Builder: common.ProvenanceBuilder{ ID: "foo" + id, }, }, } b, err := json.Marshal(it) if err != nil { t.Fatal(err) } pb, _ := pem.Decode([]byte(sigx509.ECDSAPriv)) priv, err := x509.ParsePKCS8PrivateKey(pb.Bytes) if err != nil { t.Fatal(err) } s, err := signature.LoadECDSASigner(priv.(*ecdsa.PrivateKey), crypto.SHA256) if err != nil { t.Fatal(err) } signer, err := dsse.NewEnvelopeSigner(&sigx509.Verifier{ S: s, }) if err != nil { t.Fatal(err) } env, err := signer.SignPayload(context.Background(), in_toto.PayloadType, b) if err != nil { t.Fatal(err) } eb, err := json.Marshal(env) if err != nil { t.Fatal(err) } util.Write(t, string(eb), attestationPath) util.Write(t, sigx509.ECDSAPub, pubKeyPath) out := util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", pubKeyPath) util.OutputContains(t, out, "Created entry at") uuid := util.GetUUIDFromUploadOutput(t, out) out = util.RunCli(t, "get", "--uuid", uuid, "--format=json") g := util.GetOut{} if err := json.Unmarshal([]byte(out), &g); err != nil { t.Fatal(err) } // The attestation should be stored at /var/run/attestations/sha256:digest got := in_toto.ProvenanceStatement{} if err := json.Unmarshal([]byte(g.Attestation), &got); err != nil { t.Fatal(err) } if diff := cmp.Diff(it, got); diff != "" { t.Errorf("diff: %s", diff) } attHash := sha256.Sum256(b) intotoModel := &models.IntotoV002Schema{} if err := types.DecodeEntry(g.Body.(map[string]interface{})["IntotoObj"], intotoModel); err != nil { t.Errorf("could not convert body into intoto type: %v", err) } if intotoModel.Content == nil || intotoModel.Content.PayloadHash == nil { t.Errorf("could not find hash over attestation %v", intotoModel) } recordedPayloadHash, err := hex.DecodeString(*intotoModel.Content.PayloadHash.Value) if err != nil { t.Errorf("error converting attestation hash to []byte: %v", err) } if !bytes.Equal(attHash[:], recordedPayloadHash) { t.Fatal(fmt.Errorf("attestation hash %v doesnt match the payload we sent %v", hex.EncodeToString(attHash[:]), *intotoModel.Content.PayloadHash.Value)) } out = util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", pubKeyPath) util.OutputContains(t, out, "Entry already exists") // issue1649 check for full UUID in printed Location value from 409 response header if len(uuid) != sharding.EntryIDHexStringLen { t.Fatal("UUID returned instead of entry ID (includes treeID)") } util.OutputContains(t, out, uuid) } func TestIntotoMultiSig(t *testing.T) { td := t.TempDir() attestationPath := filepath.Join(td, "attestation.json") ecdsapubKeyPath := filepath.Join(td, "ecdsapub.pem") rsapubKeyPath := filepath.Join(td, "rsapub.pem") // Get some random data so it's unique each run d := util.RandomData(t, 10) id := base64.StdEncoding.EncodeToString(d) it := in_toto.ProvenanceStatement{ StatementHeader: in_toto.StatementHeader{ Type: in_toto.StatementInTotoV01, PredicateType: slsa.PredicateSLSAProvenance, Subject: []in_toto.Subject{ { Name: "foobar", Digest: common.DigestSet{ "foo": "bar", }, }, }, }, Predicate: slsa.ProvenancePredicate{ Builder: common.ProvenanceBuilder{ ID: "foo" + id, }, }, } b, err := json.Marshal(it) if err != nil { t.Fatal(err) } evps := []*sigx509.Verifier{} pb, _ := pem.Decode([]byte(sigx509.ECDSAPriv)) priv, err := x509.ParsePKCS8PrivateKey(pb.Bytes) if err != nil { t.Fatal(err) } signECDSA, err := signature.LoadECDSASigner(priv.(*ecdsa.PrivateKey), crypto.SHA256) if err != nil { t.Fatal(err) } evps = append(evps, &sigx509.Verifier{ S: signECDSA, }) pbRSA, _ := pem.Decode([]byte(sigx509.RSAKey)) rsaPriv, err := x509.ParsePKCS8PrivateKey(pbRSA.Bytes) if err != nil { t.Fatal(err) } signRSA, err := signature.LoadRSAPKCS1v15Signer(rsaPriv.(*rsa.PrivateKey), crypto.SHA256) if err != nil { t.Fatal(err) } evps = append(evps, &sigx509.Verifier{ S: signRSA, }) signer, err := dsse.NewEnvelopeSigner(evps[0], evps[1]) if err != nil { t.Fatal(err) } env, err := signer.SignPayload(context.Background(), in_toto.PayloadType, b) if err != nil { t.Fatal(err) } eb, err := json.Marshal(env) if err != nil { t.Fatal(err) } util.Write(t, string(eb), attestationPath) util.Write(t, sigx509.ECDSAPub, ecdsapubKeyPath) util.Write(t, sigx509.PubKey, rsapubKeyPath) out := util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", ecdsapubKeyPath, "--public-key", rsapubKeyPath) util.OutputContains(t, out, "Created entry at") uuid := util.GetUUIDFromUploadOutput(t, out) out = util.RunCli(t, "get", "--uuid", uuid, "--format=json") g := util.GetOut{} if err := json.Unmarshal([]byte(out), &g); err != nil { t.Fatal(err) } // The attestation should be stored at /var/run/attestations/$uuid got := in_toto.ProvenanceStatement{} if err := json.Unmarshal([]byte(g.Attestation), &got); err != nil { t.Fatal(err) } if diff := cmp.Diff(it, got); diff != "" { t.Errorf("diff: %s", diff) } attHash := sha256.Sum256([]byte(g.Attestation)) intotoV002Model := &models.IntotoV002Schema{} if err := types.DecodeEntry(g.Body.(map[string]interface{})["IntotoObj"], intotoV002Model); err != nil { t.Errorf("could not convert body into intoto type: %v", err) } if intotoV002Model.Content.Hash == nil { t.Errorf("could not find hash over attestation %v", intotoV002Model) } recordedPayloadHash, err := hex.DecodeString(*intotoV002Model.Content.PayloadHash.Value) if err != nil { t.Errorf("error converting attestation hash to []byte: %v", err) } if !bytes.Equal(attHash[:], recordedPayloadHash) { t.Fatal(fmt.Errorf("attestation hash %v doesnt match the payload we sent %v", hex.EncodeToString(attHash[:]), *intotoV002Model.Content.PayloadHash.Value)) } out = util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", ecdsapubKeyPath, "--public-key", rsapubKeyPath) util.OutputContains(t, out, "Entry already exists") } func TestValidationSearchIntotoV002MissingEnvelope(t *testing.T) { body := `{ "entries":[ { "kind":"intoto", "apiVersion": "0.0.2", "spec":{ "content":{ "hash":{ "algorithm":"sha256", "value":"782143e39f1e7a04e3f6da2d88b1c057e5657363c4f90679f3e8a071b7619e02" }, "payloadHash":{ "algorithm":"sha256", "value":"ebbfddda6277af199e93c5bb5cf5998a79311de238e49bcc8ac24102698761bb" } }, "publicKey":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNwRENDQWlxZ0F3SUJBZ0lVYWhsOEFRd1lZV05ZbnV6dlFvOEVrN1dMTURvd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpJd09ESTJNREV4TnpFM1doY05Nakl3T0RJMk1ERXlOekUzV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVLWmZEQzlpalVyclpBWE9jWFYrQXFHRUlTSlEzVHRqSndJdEEKdTE3Rml2aWpnSk1hYUhGNDcrT3Z2OVR1ekFDQ3lpSUV5UDUyZXI2ZmF5bmZKYVVqOEtPQ0FVa3dnZ0ZGTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVHQldUCkMwdUU3dTRQcURVRjFYV0c0QlVWVUpBd0h3WURWUjBqQkJnd0ZvQVUzOVBwejFZa0VaYjVxTmpwS0ZXaXhpNFkKWkQ4d0pRWURWUjBSQVFIL0JCc3dHWUVYYzJGemIyRnJhWEpoTmpFeE5FQm5iV0ZwYkM1amIyMHdLUVlLS3dZQgpCQUdEdnpBQkFRUWJhSFIwY0hNNkx5OWhZMk52ZFc1MGN5NW5iMjluYkdVdVkyOXRNSUdMQmdvckJnRUVBZFo1CkFnUUNCSDBFZXdCNUFIY0FDR0NTOENoUy8yaEYwZEZySjRTY1JXY1lyQlk5d3pqU2JlYThJZ1kyYjNJQUFBR0MKMTdtSmhnQUFCQU1BU0RCR0FpRUFoS09BSkdWVlhCb1cxTDR4alk5eWJWOGZUUXN5TStvUEpIeDk5S29LYUpVQwpJUURCZDllc1Q0Mk1STng3Vm9BM1paKzV4akhNZWR6amVxQ2ZoZTcvd1pxYTlUQUtCZ2dxaGtqT1BRUURBd05vCkFEQmxBakVBcnBkeXlFRjc3b2JyTENMUXpzYmIxM2lsNjd3dzM4Y050amdNQml6Y2VUakRiY2VLeVFSN1RKNHMKZENsclkxY1BBakE4aXB6SUQ4VU1CaGxkSmUvZXJGcGdtN2swNWFic2lPN3V5dVZuS29VNk0rTXJ6VVUrZTlGdwpJRGhCanVRa1dRYz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=" } } ] } ` resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewBuffer([]byte(body))) if err != nil { t.Fatal(err) } c, _ := ioutil.ReadAll(resp.Body) if resp.StatusCode != 400 { t.Fatalf("expected status 400, got %d instead: %v", resp.StatusCode, string(c)) } } func TestValidationSearchIntotoV002MissingEnvelopeHash(t *testing.T) { body := `{ "entries":[ { "kind":"intoto", "apiVersion": "0.0.2", "spec":{ "content":{ "envelope":{ "payloadType":"asd", "signatures":[ {} ] }, "payloadHash":{ "algorithm":"sha256", "value":"ebbfddda6277af199e93c5bb5cf5998a79311de238e49bcc8ac24102698761bb" } }, "publicKey":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNwRENDQWlxZ0F3SUJBZ0lVYWhsOEFRd1lZV05ZbnV6dlFvOEVrN1dMTURvd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpJd09ESTJNREV4TnpFM1doY05Nakl3T0RJMk1ERXlOekUzV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVLWmZEQzlpalVyclpBWE9jWFYrQXFHRUlTSlEzVHRqSndJdEEKdTE3Rml2aWpnSk1hYUhGNDcrT3Z2OVR1ekFDQ3lpSUV5UDUyZXI2ZmF5bmZKYVVqOEtPQ0FVa3dnZ0ZGTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVHQldUCkMwdUU3dTRQcURVRjFYV0c0QlVWVUpBd0h3WURWUjBqQkJnd0ZvQVUzOVBwejFZa0VaYjVxTmpwS0ZXaXhpNFkKWkQ4d0pRWURWUjBSQVFIL0JCc3dHWUVYYzJGemIyRnJhWEpoTmpFeE5FQm5iV0ZwYkM1amIyMHdLUVlLS3dZQgpCQUdEdnpBQkFRUWJhSFIwY0hNNkx5OWhZMk52ZFc1MGN5NW5iMjluYkdVdVkyOXRNSUdMQmdvckJnRUVBZFo1CkFnUUNCSDBFZXdCNUFIY0FDR0NTOENoUy8yaEYwZEZySjRTY1JXY1lyQlk5d3pqU2JlYThJZ1kyYjNJQUFBR0MKMTdtSmhnQUFCQU1BU0RCR0FpRUFoS09BSkdWVlhCb1cxTDR4alk5eWJWOGZUUXN5TStvUEpIeDk5S29LYUpVQwpJUURCZDllc1Q0Mk1STng3Vm9BM1paKzV4akhNZWR6amVxQ2ZoZTcvd1pxYTlUQUtCZ2dxaGtqT1BRUURBd05vCkFEQmxBakVBcnBkeXlFRjc3b2JyTENMUXpzYmIxM2lsNjd3dzM4Y050amdNQml6Y2VUakRiY2VLeVFSN1RKNHMKZENsclkxY1BBakE4aXB6SUQ4VU1CaGxkSmUvZXJGcGdtN2swNWFic2lPN3V5dVZuS29VNk0rTXJ6VVUrZTlGdwpJRGhCanVRa1dRYz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=" } } ] } ` resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewBuffer([]byte(body))) if err != nil { t.Fatal(err) } c, _ := ioutil.ReadAll(resp.Body) if resp.StatusCode != 400 { t.Fatalf("expected status 400, got %d instead: %v", resp.StatusCode, string(c)) } } func TestValidationSearchIntotoV002MissingPayloadHash(t *testing.T) { body := `{ "entries":[ { "kind":"intoto", "apiVersion": "0.0.2", "spec":{ "content":{ "envelope":{ "payloadType":"asd", "signatures":[ {} ] }, "hash":{ "algorithm":"sha256", "value":"782143e39f1e7a04e3f6da2d88b1c057e5657363c4f90679f3e8a071b7619e02" }, }, "publicKey":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNwRENDQWlxZ0F3SUJBZ0lVYWhsOEFRd1lZV05ZbnV6dlFvOEVrN1dMTURvd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpJd09ESTJNREV4TnpFM1doY05Nakl3T0RJMk1ERXlOekUzV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVLWmZEQzlpalVyclpBWE9jWFYrQXFHRUlTSlEzVHRqSndJdEEKdTE3Rml2aWpnSk1hYUhGNDcrT3Z2OVR1ekFDQ3lpSUV5UDUyZXI2ZmF5bmZKYVVqOEtPQ0FVa3dnZ0ZGTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVHQldUCkMwdUU3dTRQcURVRjFYV0c0QlVWVUpBd0h3WURWUjBqQkJnd0ZvQVUzOVBwejFZa0VaYjVxTmpwS0ZXaXhpNFkKWkQ4d0pRWURWUjBSQVFIL0JCc3dHWUVYYzJGemIyRnJhWEpoTmpFeE5FQm5iV0ZwYkM1amIyMHdLUVlLS3dZQgpCQUdEdnpBQkFRUWJhSFIwY0hNNkx5OWhZMk52ZFc1MGN5NW5iMjluYkdVdVkyOXRNSUdMQmdvckJnRUVBZFo1CkFnUUNCSDBFZXdCNUFIY0FDR0NTOENoUy8yaEYwZEZySjRTY1JXY1lyQlk5d3pqU2JlYThJZ1kyYjNJQUFBR0MKMTdtSmhnQUFCQU1BU0RCR0FpRUFoS09BSkdWVlhCb1cxTDR4alk5eWJWOGZUUXN5TStvUEpIeDk5S29LYUpVQwpJUURCZDllc1Q0Mk1STng3Vm9BM1paKzV4akhNZWR6amVxQ2ZoZTcvd1pxYTlUQUtCZ2dxaGtqT1BRUURBd05vCkFEQmxBakVBcnBkeXlFRjc3b2JyTENMUXpzYmIxM2lsNjd3dzM4Y050amdNQml6Y2VUakRiY2VLeVFSN1RKNHMKZENsclkxY1BBakE4aXB6SUQ4VU1CaGxkSmUvZXJGcGdtN2swNWFic2lPN3V5dVZuS29VNk0rTXJ6VVUrZTlGdwpJRGhCanVRa1dRYz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=" } } ] } ` resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewBuffer([]byte(body))) if err != nil { t.Fatal(err) } c, _ := ioutil.ReadAll(resp.Body) if resp.StatusCode != 400 { t.Fatalf("expected status 400, got %d instead: %v", resp.StatusCode, string(c)) } } func TestValidationSearchIntotoV001MissingPayloadHash(t *testing.T) { body := `{ "entries":[ { "kind":"intoto", "apiVersion": "0.0.1", "spec":{ "content":{ "hash":{ "algorithm":"sha256", "value":"ebbfddda6277af199e93c5bb5cf5998a79311de238e49bcc8ac24102698761bb" } }, "publicKey":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNwRENDQWlxZ0F3SUJBZ0lVYWhsOEFRd1lZV05ZbnV6dlFvOEVrN1dMTURvd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpJd09ESTJNREV4TnpFM1doY05Nakl3T0RJMk1ERXlOekUzV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVLWmZEQzlpalVyclpBWE9jWFYrQXFHRUlTSlEzVHRqSndJdEEKdTE3Rml2aWpnSk1hYUhGNDcrT3Z2OVR1ekFDQ3lpSUV5UDUyZXI2ZmF5bmZKYVVqOEtPQ0FVa3dnZ0ZGTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVHQldUCkMwdUU3dTRQcURVRjFYV0c0QlVWVUpBd0h3WURWUjBqQkJnd0ZvQVUzOVBwejFZa0VaYjVxTmpwS0ZXaXhpNFkKWkQ4d0pRWURWUjBSQVFIL0JCc3dHWUVYYzJGemIyRnJhWEpoTmpFeE5FQm5iV0ZwYkM1amIyMHdLUVlLS3dZQgpCQUdEdnpBQkFRUWJhSFIwY0hNNkx5OWhZMk52ZFc1MGN5NW5iMjluYkdVdVkyOXRNSUdMQmdvckJnRUVBZFo1CkFnUUNCSDBFZXdCNUFIY0FDR0NTOENoUy8yaEYwZEZySjRTY1JXY1lyQlk5d3pqU2JlYThJZ1kyYjNJQUFBR0MKMTdtSmhnQUFCQU1BU0RCR0FpRUFoS09BSkdWVlhCb1cxTDR4alk5eWJWOGZUUXN5TStvUEpIeDk5S29LYUpVQwpJUURCZDllc1Q0Mk1STng3Vm9BM1paKzV4akhNZWR6amVxQ2ZoZTcvd1pxYTlUQUtCZ2dxaGtqT1BRUURBd05vCkFEQmxBakVBcnBkeXlFRjc3b2JyTENMUXpzYmIxM2lsNjd3dzM4Y050amdNQml6Y2VUakRiY2VLeVFSN1RKNHMKZENsclkxY1BBakE4aXB6SUQ4VU1CaGxkSmUvZXJGcGdtN2swNWFic2lPN3V5dVZuS29VNk0rTXJ6VVUrZTlGdwpJRGhCanVRa1dRYz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=" } } ] } ` resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewBuffer([]byte(body))) if err != nil { t.Fatal(err) } c, _ := ioutil.ReadAll(resp.Body) // No failure when payload hash is not present for canonicalization if resp.StatusCode != 200 { t.Fatalf("expected status 200, got %d instead: %v", resp.StatusCode, string(c)) } } rekor-1.3.5/pkg/types/intoto/intoto.go000066400000000000000000000072471455727245600200010ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package intoto import ( "context" "errors" "fmt" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/types" "golang.org/x/exp/slices" ) const ( KIND = "intoto" ) type BaseIntotoType struct { types.RekorType } func init() { types.TypeMap.Store(KIND, New) } func New() types.TypeImpl { bit := BaseIntotoType{} bit.Kind = KIND bit.VersionMap = VersionMap return &bit } var VersionMap = types.NewSemVerEntryFactoryMap() func (it BaseIntotoType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) { if pe == nil { return nil, errors.New("proposed entry cannot be nil") } in, ok := pe.(*models.Intoto) if !ok { return nil, errors.New("cannot unmarshal non-Rekord types") } return it.VersionedUnmarshal(in, *in.APIVersion) } func (it *BaseIntotoType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) { var head ProposedIntotoEntryIterator var next *ProposedIntotoEntryIterator if version == "" { // get default version as head of list version = it.DefaultVersion() ei, err := it.VersionedUnmarshal(nil, version) if err != nil { return nil, fmt.Errorf("fetching default Intoto version implementation: %w", err) } pe, err := ei.CreateFromArtifactProperties(ctx, props) if err != nil { return nil, fmt.Errorf("creating default Intoto entry: %w", err) } head.ProposedEntry = pe next = &head for _, v := range it.SupportedVersions() { if v == it.DefaultVersion() { continue } ei, err := it.VersionedUnmarshal(nil, v) if err != nil { log.ContextLogger(ctx).Errorf("fetching Intoto version (%v) implementation: %w", v, err) continue } versionedPE, err := ei.CreateFromArtifactProperties(ctx, props) if err != nil { log.ContextLogger(ctx).Errorf("error creating Intoto entry of version (%v): %w", v, err) continue } next.next = &ProposedIntotoEntryIterator{versionedPE, nil} next = next.next.(*ProposedIntotoEntryIterator) } return head, nil } ei, err := it.VersionedUnmarshal(nil, version) if err != nil { return nil, fmt.Errorf("fetching Intoto version implementation: %w", err) } return ei.CreateFromArtifactProperties(ctx, props) } func (it BaseIntotoType) DefaultVersion() string { return "0.0.2" } // SupportedVersions returns the supported versions for this type in the order of preference func (it BaseIntotoType) SupportedVersions() []string { return []string{"0.0.2", "0.0.1"} } // IsSupportedVersion returns true if the version can be inserted into the log, and false if not func (it *BaseIntotoType) IsSupportedVersion(proposedVersion string) bool { return slices.Contains(it.SupportedVersions(), proposedVersion) } type ProposedIntotoEntryIterator struct { models.ProposedEntry next models.ProposedEntry } func (p ProposedIntotoEntryIterator) HasNext() bool { return p.next != nil } func (p ProposedIntotoEntryIterator) GetNext() models.ProposedEntry { return p.next } func (p ProposedIntotoEntryIterator) Get() models.ProposedEntry { return p.ProposedEntry } rekor-1.3.5/pkg/types/intoto/intoto_schema.json000066400000000000000000000006261455727245600216570ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/intoto/intoto_schema.json", "title": "Intoto Schema", "description": "Intoto for Rekord objects", "type": "object", "oneOf": [ { "$ref": "v0.0.1/intoto_v0_0_1_schema.json" }, { "$ref": "v0.0.2/intoto_v0_0_2_schema.json" } ] } rekor-1.3.5/pkg/types/intoto/intoto_test.go000066400000000000000000000055331455727245600210340ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package intoto import ( "errors" "testing" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" ) type UnmarshalTester struct { models.Intoto types.BaseUnmarshalTester } type UnmarshalFailsTester struct { types.BaseUnmarshalTester } func (u UnmarshalFailsTester) NewEntry() types.EntryImpl { return &UnmarshalFailsTester{} } func (u UnmarshalFailsTester) Unmarshal(_ models.ProposedEntry) error { return errors.New("error") } func (u UnmarshalFailsTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } func TestIntotoType(t *testing.T) { // empty to start if VersionMap.Count() != 0 { t.Error("semver range was not blank at start of test") } u := UnmarshalTester{} // ensure semver range parser is working invalidSemVerRange := "not a valid semver range" err := VersionMap.SetEntryFactory(invalidSemVerRange, u.NewEntry) if err == nil || VersionMap.Count() > 0 { t.Error("invalid semver range was incorrectly added to SemVerToFacFnMap") } // valid semver range can be parsed err = VersionMap.SetEntryFactory(">= 1.2.3", u.NewEntry) if err != nil || VersionMap.Count() != 1 { t.Error("valid semver range was not added to SemVerToFacFnMap") } u.Intoto.APIVersion = swag.String("2.0.1") brt := New() // version requested matches implementation in map if _, err := brt.UnmarshalEntry(&u.Intoto); err != nil { t.Errorf("unexpected error in Unmarshal: %v", err) } // version requested fails to match implementation in map u.Intoto.APIVersion = swag.String("1.2.2") if _, err := brt.UnmarshalEntry(&u.Intoto); err == nil { t.Error("unexpected success in Unmarshal for non-matching version") } // error in Unmarshal call is raised appropriately u.Intoto.APIVersion = swag.String("2.2.0") u2 := UnmarshalFailsTester{} _ = VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry) if _, err := brt.UnmarshalEntry(&u.Intoto); err == nil { t.Error("unexpected success in Unmarshal when error is thrown") } // version requested fails to match implementation in map u.Intoto.APIVersion = swag.String("not_a_version") if _, err := brt.UnmarshalEntry(&u.Intoto); err == nil { t.Error("unexpected success in Unmarshal for invalid version") } } rekor-1.3.5/pkg/types/intoto/v0.0.1/000077500000000000000000000000001455727245600167465ustar00rootroot00000000000000rekor-1.3.5/pkg/types/intoto/v0.0.1/entry.go000066400000000000000000000302601455727245600204370ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package intoto import ( "bytes" "context" "crypto" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "errors" "fmt" "os" "path/filepath" "strings" "github.com/in-toto/in-toto-golang/in_toto" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/spf13/viper" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/intoto" "github.com/sigstore/sigstore/pkg/signature" dsse_verifier "github.com/sigstore/sigstore/pkg/signature/dsse" ) const ( APIVERSION = "0.0.1" ) func init() { if err := intoto.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil { log.Logger.Panic(err) } } type V001Entry struct { IntotoObj models.IntotoV001Schema keyObj pki.PublicKey env dsse.Envelope } func (v V001Entry) APIVersion() string { return APIVERSION } func NewEntry() types.EntryImpl { return &V001Entry{} } func (v V001Entry) IndexKeys() ([]string, error) { var result []string // add digest over entire DSSE envelope if v.IntotoObj.Content != nil && v.IntotoObj.Content.Hash != nil { hashkey := strings.ToLower(fmt.Sprintf("%s:%s", swag.StringValue(v.IntotoObj.Content.Hash.Algorithm), swag.StringValue(v.IntotoObj.Content.Hash.Value))) result = append(result, hashkey) } else { log.Logger.Error("could not find content digest to include in index keys") } // add digest over public key if v.keyObj != nil { key, err := v.keyObj.CanonicalValue() if err == nil { keyHash := sha256.Sum256(key) result = append(result, fmt.Sprintf("sha256:%s", strings.ToLower(hex.EncodeToString(keyHash[:])))) // add digest over any subjects within signing certificate result = append(result, v.keyObj.Subjects()...) } else { log.Logger.Errorf("could not canonicalize public key to include in index keys: %w", err) } } else { log.Logger.Error("could not find public key to include in index keys") } // add digest base64-decoded payload inside of DSSE envelope if v.IntotoObj.Content != nil && v.IntotoObj.Content.PayloadHash != nil { payloadHash := strings.ToLower(fmt.Sprintf("%s:%s", swag.StringValue(v.IntotoObj.Content.PayloadHash.Algorithm), swag.StringValue(v.IntotoObj.Content.PayloadHash.Value))) result = append(result, payloadHash) } else { log.Logger.Error("could not find payload digest to include in index keys") } switch v.env.PayloadType { case in_toto.PayloadType: statement, err := parseStatement(v.env.Payload) if err != nil { log.Logger.Errorf("error parsing payload as intoto statement: %w", err) break } for _, s := range statement.Subject { for alg, ds := range s.Digest { result = append(result, alg+":"+ds) } } // Not all in-toto statements will contain a SLSA provenance predicate. // See https://github.com/in-toto/attestation/blob/main/spec/README.md#predicate // for other predicates. if predicate, err := parseSlsaPredicate(v.env.Payload); err == nil { if predicate.Predicate.Materials != nil { for _, s := range predicate.Predicate.Materials { for alg, ds := range s.Digest { result = append(result, alg+":"+ds) } } } } default: log.Logger.Infof("unknown in_toto statement type (%s), cannot extract additional index keys", v.env.PayloadType) } return result, nil } func parseStatement(p string) (*in_toto.Statement, error) { ps := in_toto.Statement{} payload, err := base64.StdEncoding.DecodeString(p) if err != nil { return nil, err } if err := json.Unmarshal(payload, &ps); err != nil { return nil, err } return &ps, nil } func parseSlsaPredicate(p string) (*in_toto.ProvenanceStatement, error) { predicate := in_toto.ProvenanceStatement{} payload, err := base64.StdEncoding.DecodeString(p) if err != nil { return nil, err } if err := json.Unmarshal(payload, &predicate); err != nil { return nil, err } return &predicate, nil } func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error { it, ok := pe.(*models.Intoto) if !ok { return errors.New("cannot unmarshal non Intoto v0.0.1 type") } var err error if err := types.DecodeEntry(it.Spec, &v.IntotoObj); err != nil { return err } // field validation if err := v.IntotoObj.Validate(strfmt.Default); err != nil { return err } v.keyObj, err = x509.NewPublicKey(bytes.NewReader(*v.IntotoObj.PublicKey)) if err != nil { return err } return v.validate() } func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) { if v.keyObj == nil { return nil, errors.New("cannot canonicalize empty key") } if v.IntotoObj.Content == nil { return nil, errors.New("missing content") } if v.IntotoObj.Content.Hash == nil { return nil, errors.New("missing envelope hash") } // PayloadHash is not present for old entries pk, err := v.keyObj.CanonicalValue() if err != nil { return nil, err } pkb := strfmt.Base64(pk) canonicalEntry := models.IntotoV001Schema{ PublicKey: &pkb, Content: &models.IntotoV001SchemaContent{ Hash: &models.IntotoV001SchemaContentHash{ Algorithm: v.IntotoObj.Content.Hash.Algorithm, Value: v.IntotoObj.Content.Hash.Value, }, }, } // Set PayloadHash if present if v.IntotoObj.Content.PayloadHash != nil { canonicalEntry.Content.PayloadHash = &models.IntotoV001SchemaContentPayloadHash{ Algorithm: v.IntotoObj.Content.PayloadHash.Algorithm, Value: v.IntotoObj.Content.PayloadHash.Value, } } itObj := models.Intoto{} itObj.APIVersion = swag.String(APIVERSION) itObj.Spec = &canonicalEntry return json.Marshal(&itObj) } // validate performs cross-field validation for fields in object func (v *V001Entry) validate() error { // TODO handle multiple pk := v.keyObj.(*x509.PublicKey) // one of two cases must be true: // - ProposedEntry: client gives an envelope; (client provided hash/payloadhash are ignored as they are computed server-side) OR // - CommittedEntry: NO envelope and hash/payloadHash must be present if v.IntotoObj.Content.Envelope == "" { if v.IntotoObj.Content.Hash == nil { return fmt.Errorf("missing hash value for envelope") } else if err := v.IntotoObj.Content.Hash.Validate(strfmt.Default); err != nil { return fmt.Errorf("validation error on envelope hash: %w", err) } // PayloadHash is not present for old entries if v.IntotoObj.Content.PayloadHash != nil { if err := v.IntotoObj.Content.PayloadHash.Validate(strfmt.Default); err != nil { return fmt.Errorf("validation error on payload hash: %w", err) } } // if there is no envelope, and hash/payloadHash are valid, then there's nothing else to do here return nil } vfr, err := signature.LoadVerifier(pk.CryptoPubKey(), crypto.SHA256) if err != nil { return err } dsseVerifier := dsse_verifier.WrapVerifier(vfr) if err := dsseVerifier.VerifySignature(strings.NewReader(v.IntotoObj.Content.Envelope), nil); err != nil { return err } if err := json.Unmarshal([]byte(v.IntotoObj.Content.Envelope), &v.env); err != nil { return err } attBytes, err := base64.StdEncoding.DecodeString(v.env.Payload) if err != nil { return err } // validation logic complete without errors, hydrate local object attHash := sha256.Sum256(attBytes) v.IntotoObj.Content.PayloadHash = &models.IntotoV001SchemaContentPayloadHash{ Algorithm: swag.String(models.IntotoV001SchemaContentPayloadHashAlgorithmSha256), Value: swag.String(hex.EncodeToString(attHash[:])), } h := sha256.Sum256([]byte(v.IntotoObj.Content.Envelope)) v.IntotoObj.Content.Hash = &models.IntotoV001SchemaContentHash{ Algorithm: swag.String(models.IntotoV001SchemaContentHashAlgorithmSha256), Value: swag.String(hex.EncodeToString(h[:])), } return nil } // AttestationKey returns the digest of the attestation that was uploaded, to be used to lookup the attestation from storage func (v *V001Entry) AttestationKey() string { if v.IntotoObj.Content != nil && v.IntotoObj.Content.PayloadHash != nil { return fmt.Sprintf("%s:%s", *v.IntotoObj.Content.PayloadHash.Algorithm, *v.IntotoObj.Content.PayloadHash.Value) } return "" } // AttestationKeyValue returns both the key and value to be persisted into attestation storage func (v *V001Entry) AttestationKeyValue() (string, []byte) { storageSize := base64.StdEncoding.DecodedLen(len(v.env.Payload)) if storageSize > viper.GetInt("max_attestation_size") { log.Logger.Infof("Skipping attestation storage, size %d is greater than max %d", storageSize, viper.GetInt("max_attestation_size")) return "", nil } attBytes, _ := base64.StdEncoding.DecodeString(v.env.Payload) return v.AttestationKey(), attBytes } func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) { returnVal := models.Intoto{} var err error artifactBytes := props.ArtifactBytes if artifactBytes == nil { if props.ArtifactPath == nil { return nil, errors.New("path to artifact file must be specified") } if props.ArtifactPath.IsAbs() { return nil, errors.New("intoto envelopes cannot be fetched over HTTP(S)") } artifactBytes, err = os.ReadFile(filepath.Clean(props.ArtifactPath.Path)) if err != nil { return nil, err } } publicKeyBytes := props.PublicKeyBytes if len(publicKeyBytes) == 0 { if len(props.PublicKeyPaths) != 1 { return nil, errors.New("only one public key must be provided to verify signature") } keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path)) if err != nil { return nil, fmt.Errorf("error reading public key file: %w", err) } publicKeyBytes = append(publicKeyBytes, keyBytes) } else if len(publicKeyBytes) != 1 { return nil, errors.New("only one public key must be provided") } kb := strfmt.Base64(publicKeyBytes[0]) re := V001Entry{ IntotoObj: models.IntotoV001Schema{ Content: &models.IntotoV001SchemaContent{ Envelope: string(artifactBytes), }, PublicKey: &kb, }, } h := sha256.Sum256([]byte(re.IntotoObj.Content.Envelope)) re.IntotoObj.Content.Hash = &models.IntotoV001SchemaContentHash{ Algorithm: swag.String(models.IntotoV001SchemaContentHashAlgorithmSha256), Value: swag.String(hex.EncodeToString(h[:])), } returnVal.Spec = re.IntotoObj returnVal.APIVersion = swag.String(re.APIVersion()) return &returnVal, nil } func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { if v.IntotoObj.PublicKey == nil { return nil, errors.New("intoto v0.0.1 entry not initialized") } key, err := x509.NewPublicKey(bytes.NewReader(*v.IntotoObj.PublicKey)) if err != nil { return nil, err } return []pki.PublicKey{key}, nil } func (v V001Entry) ArtifactHash() (string, error) { if v.IntotoObj.Content == nil || v.IntotoObj.Content.PayloadHash == nil || v.IntotoObj.Content.PayloadHash.Algorithm == nil || v.IntotoObj.Content.PayloadHash.Value == nil { return "", errors.New("hashedrekord v0.0.1 entry not initialized") } return strings.ToLower(fmt.Sprintf("%s:%s", *v.IntotoObj.Content.PayloadHash.Algorithm, *v.IntotoObj.Content.PayloadHash.Value)), nil } func (v V001Entry) Insertable() (bool, error) { if v.IntotoObj.Content == nil { return false, errors.New("missing content property") } if len(v.IntotoObj.Content.Envelope) == 0 { return false, errors.New("missing envelope content") } if v.IntotoObj.PublicKey == nil || len(*v.IntotoObj.PublicKey) == 0 { return false, errors.New("missing publicKey content") } if v.keyObj == nil { return false, errors.New("failed to parse public key") } if v.env.Payload == "" || v.env.PayloadType == "" || len(v.env.Signatures) == 0 { return false, errors.New("invalid DSSE envelope") } return true, nil } rekor-1.3.5/pkg/types/intoto/v0.0.1/entry_test.go000066400000000000000000000422671455727245600215100ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package intoto import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/json" "encoding/pem" "errors" "fmt" "math/big" "reflect" "sort" "strings" "testing" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/in-toto/in-toto-golang/in_toto" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/rekor/pkg/generated/models" pkix509 "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/sigstore/pkg/signature" dsse_signer "github.com/sigstore/sigstore/pkg/signature/dsse" "go.uber.org/goleak" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestNewEntryReturnType(t *testing.T) { entry := NewEntry() if reflect.TypeOf(entry) != reflect.ValueOf(&V001Entry{}).Type() { t.Errorf("invalid type returned from NewEntry: %T", entry) } } func p(b []byte) *strfmt.Base64 { b64 := strfmt.Base64(b) return &b64 } func envelope(t *testing.T, k *ecdsa.PrivateKey, payload, payloadType string) string { s, err := signature.LoadECDSASigner(k, crypto.SHA256) if err != nil { t.Fatal(err) } wrappedSigner := dsse_signer.WrapSigner(s, string(payloadType)) dsseEnv, err := wrappedSigner.SignMessage(strings.NewReader(payload)) if err != nil { t.Fatal(err) } return string(dsseEnv) } func TestV001Entry_Unmarshal(t *testing.T) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } ca := &x509.Certificate{ SerialNumber: big.NewInt(1), EmailAddresses: []string{"joe@schmoe.com"}, } caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &priv.PublicKey, priv) if err != nil { t.Fatal(err) } pemBytes := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: caBytes, }) invalid, err := json.Marshal(dsse.Envelope{ Payload: "hello", Signatures: []dsse.Signature{ { Sig: string(strfmt.Base64("foobar")), }, }, }) if err != nil { t.Fatal(err) } validPayload := "hellothispayloadisvalid" tests := []struct { name string want models.IntotoV001Schema it *models.IntotoV001Schema wantErr bool additionalIndexKeys []string wantVerifierErr bool }{ { name: "empty", it: &models.IntotoV001Schema{}, wantErr: true, wantVerifierErr: true, }, { name: "missing envelope", it: &models.IntotoV001Schema{ PublicKey: p(pub), }, wantErr: true, wantVerifierErr: false, }, { name: "invalid key", it: &models.IntotoV001Schema{ PublicKey: p([]byte("hello")), }, wantErr: true, wantVerifierErr: true, }, { name: "valid intoto", it: &models.IntotoV001Schema{ PublicKey: p(pub), Content: &models.IntotoV001SchemaContent{ Envelope: envelope(t, key, validPayload, "application/vnd.in-toto+json"), }, }, wantErr: false, wantVerifierErr: false, }, { name: "valid intoto but hash specified by client (should be ignored)", it: &models.IntotoV001Schema{ PublicKey: p(pub), Content: &models.IntotoV001SchemaContent{ Envelope: envelope(t, key, validPayload, "application/vnd.in-toto+json"), Hash: &models.IntotoV001SchemaContentHash{ Algorithm: swag.String(models.IntotoV001SchemaContentHashAlgorithmSha256), Value: swag.String("1a1707bb54e5fb4deddd19f07adcb4f1e022ca7879e3c8348da8d4fa496ae8e2"), }, }, }, wantErr: false, wantVerifierErr: false, }, { name: "valid intoto but payloadhash specified by client (should be ignored)", it: &models.IntotoV001Schema{ PublicKey: p(pub), Content: &models.IntotoV001SchemaContent{ Envelope: envelope(t, key, validPayload, "application/vnd.in-toto+json"), PayloadHash: &models.IntotoV001SchemaContentPayloadHash{ Algorithm: swag.String(models.IntotoV001SchemaContentPayloadHashAlgorithmSha256), Value: swag.String("1a1707bb54e5fb4deddd19f07adcb4f1e022ca7879e3c8348da8d4fa496ae8e2"), }, }, }, wantErr: false, wantVerifierErr: false, }, { name: "valid intoto but envelope and payloadhash specified by client (hash values should be ignored)", it: &models.IntotoV001Schema{ PublicKey: p(pub), Content: &models.IntotoV001SchemaContent{ Envelope: envelope(t, key, validPayload, "application/vnd.in-toto+json"), Hash: &models.IntotoV001SchemaContentHash{ Algorithm: swag.String(models.IntotoV001SchemaContentHashAlgorithmSha256), Value: swag.String("1a1707bb54e5fb4deddd19f07adcb4f1e022ca7879e3c8348da8d4fa496ae8e2"), }, PayloadHash: &models.IntotoV001SchemaContentPayloadHash{ Algorithm: swag.String(models.IntotoV001SchemaContentPayloadHashAlgorithmSha256), Value: swag.String("1a1707bb54e5fb4deddd19f07adcb4f1e022ca7879e3c8348da8d4fa496ae8e2"), }, }, }, wantErr: false, wantVerifierErr: false, }, { name: "valid dsse but invalid intoto", it: &models.IntotoV001Schema{ PublicKey: p(pub), Content: &models.IntotoV001SchemaContent{ Envelope: envelope(t, key, validPayload, "text"), }, }, wantErr: false, wantVerifierErr: false, }, { name: "cert", it: &models.IntotoV001Schema{ PublicKey: p([]byte(pemBytes)), Content: &models.IntotoV001SchemaContent{ Envelope: envelope(t, priv, validPayload, "text"), }, }, additionalIndexKeys: []string{"joe@schmoe.com"}, wantErr: false, wantVerifierErr: false, }, { name: "invalid", it: &models.IntotoV001Schema{ PublicKey: p(pub), Content: &models.IntotoV001SchemaContent{ Envelope: string(invalid), }, }, wantErr: true, wantVerifierErr: false, }, { name: "invalid key", it: &models.IntotoV001Schema{ PublicKey: p([]byte("notavalidkey")), Content: &models.IntotoV001SchemaContent{ Envelope: envelope(t, key, validPayload, "text"), }, }, wantErr: true, wantVerifierErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { v := &V001Entry{} it := &models.Intoto{ Spec: tt.it, } var uv = func() error { if err := v.Unmarshal(it); err != nil { return err } if !tt.wantErr { if ok, err := v.Insertable(); !ok || err != nil { t.Errorf("unexpected error calling Insertable on valid proposed entry: %v", err) } } if v.IntotoObj.Content.Hash == nil || v.IntotoObj.Content.Hash.Algorithm != tt.it.Content.Hash.Algorithm || v.IntotoObj.Content.Hash.Value != tt.it.Content.Hash.Value { return errors.New("missing envelope hash in validated object") } keysWanted := tt.additionalIndexKeys if tt.it.PublicKey != nil { h := sha256.Sum256(*tt.it.PublicKey) keysWanted = append(keysWanted, fmt.Sprintf("sha256:%s", hex.EncodeToString(h[:]))) } payloadBytes, _ := v.env.DecodeB64Payload() payloadSha := sha256.Sum256(payloadBytes) payloadHash := hex.EncodeToString(payloadSha[:]) // Always start with the hash keysWanted = append(keysWanted, "sha256:"+payloadHash) hashkey := strings.ToLower(fmt.Sprintf("%s:%s", *tt.it.Content.Hash.Algorithm, *tt.it.Content.Hash.Value)) keysWanted = append(keysWanted, hashkey) got, _ := v.IndexKeys() if !cmp.Equal(got, keysWanted, cmpopts.SortSlices(func(x, y string) bool { return x < y })) { t.Errorf("V001Entry.IndexKeys() = %v, want %v", got, keysWanted) } canonicalBytes, err := v.Canonicalize(context.Background()) if err != nil { t.Errorf("error canonicalizing entry: %v", err) } pe, err := models.UnmarshalProposedEntry(bytes.NewReader(canonicalBytes), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tt.name, err) } canonicalEntry, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tt.name, err) } if ok, err := canonicalEntry.Insertable(); ok || err == nil { t.Errorf("unexpected success calling Insertable on entry created from canonicalized content") } canonicalV001 := canonicalEntry.(*V001Entry) fmt.Printf("%v", canonicalV001.IntotoObj.Content) if *canonicalV001.IntotoObj.Content.Hash.Value != *tt.it.Content.Hash.Value { t.Errorf("envelope hashes do not match post canonicalization: %v %v", *canonicalV001.IntotoObj.Content.Hash.Value, *tt.it.Content.Hash.Value) } if canonicalV001.AttestationKey() != "" && *canonicalV001.IntotoObj.Content.PayloadHash.Value != payloadHash { t.Errorf("payload hashes do not match post canonicalization: %v %v", canonicalV001.IntotoObj.Content.PayloadHash.Value, payloadHash) } canonicalIndexKeys, _ := canonicalV001.IndexKeys() if !cmp.Equal(got, canonicalIndexKeys, cmpopts.SortSlices(func(x, y string) bool { return x < y })) { t.Errorf("index keys from hydrated object do not match those generated from canonicalized (and re-hydrated) object: %v %v", got, canonicalIndexKeys) } hash, err := canonicalV001.ArtifactHash() expectedHash := sha256.Sum256([]byte(validPayload)) if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != "sha256:"+hex.EncodeToString(expectedHash[:]) { t.Errorf("unexpected match with ArtifactHash: %s", hash) } verifiers, err := v.Verifiers() if !tt.wantVerifierErr { if err != nil { t.Errorf("%v: unexpected error, got %v", tt.name, err) } else { pubV, _ := verifiers[0].CanonicalValue() if !reflect.DeepEqual(pubV, pub) && !reflect.DeepEqual(pubV, pemBytes) { t.Errorf("verifier and public keys do not match: %v, %v", string(pubV), string(pub)) } } } else { if err == nil { s, _ := verifiers[0].CanonicalValue() t.Errorf("%v: expected error for %v, got %v", tt.name, string(s), err) } } return nil } if err := uv(); (err != nil) != tt.wantErr { t.Errorf("V001Entry.Unmarshal() error = %v, wantErr %v", err, tt.wantErr) } }) } } // Demonstrates that Unmarshal and Canonicalize will succeed with only a hash, // since committed entries will have no envelope and may have no payload hash func TestV001EntryWithoutEnvelopeOrPayloadHash(t *testing.T) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) m := &models.IntotoV001Schema{ PublicKey: p(pub), Content: &models.IntotoV001SchemaContent{ Hash: &models.IntotoV001SchemaContentHash{ Algorithm: swag.String(models.IntotoV001SchemaContentHashAlgorithmSha256), Value: swag.String("1a1707bb54e5fb4deddd19f07adcb4f1e022ca7879e3c8348da8d4fa496ae8e2"), }, }, } v := &V001Entry{} it := &models.Intoto{ Spec: m, } if err := v.Unmarshal(it); err != nil { t.Fatalf("error umarshalling intoto without envelope: %v", err) } _, err = v.Canonicalize(context.TODO()) if err != nil { t.Fatalf("error canonicalizing intoto without envelope: %v", err) } } func TestV001Entry_IndexKeys(t *testing.T) { h := sha256.Sum256([]byte("foo")) dataSHA := hex.EncodeToString(h[:]) hashkey := strings.ToLower(fmt.Sprintf("%s:%s", "sha256", dataSHA)) tests := []struct { name string statement in_toto.Statement want []string }{ { name: "standard", want: []string{hashkey}, statement: in_toto.Statement{ Predicate: "hello", }, }, { name: "subject", want: []string{"sha256:foo", hashkey}, statement: in_toto.Statement{ StatementHeader: in_toto.StatementHeader{ Subject: []in_toto.Subject{ { Name: "foo", Digest: map[string]string{ "sha256": "foo", }, }, }, }, Predicate: "hello", }, }, { name: "slsa", want: []string{"sha256:bar", hashkey}, statement: in_toto.Statement{ Predicate: slsa.ProvenancePredicate{ Materials: []common.ProvenanceMaterial{ { URI: "foo", Digest: map[string]string{ "sha256": "bar", }}, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { b, err := json.Marshal(tt.statement) if err != nil { t.Fatal(err) } payload := base64.StdEncoding.EncodeToString(b) payloadHash := sha256.Sum256(b) v := V001Entry{ IntotoObj: models.IntotoV001Schema{ Content: &models.IntotoV001SchemaContent{ Hash: &models.IntotoV001SchemaContentHash{ Algorithm: swag.String(models.IntotoV001SchemaContentHashAlgorithmSha256), Value: swag.String(dataSHA), }, PayloadHash: &models.IntotoV001SchemaContentPayloadHash{ Algorithm: swag.String(models.IntotoV001SchemaContentPayloadHashAlgorithmSha256), Value: swag.String(hex.EncodeToString(payloadHash[:])), }, }, }, env: dsse.Envelope{ Payload: payload, PayloadType: in_toto.PayloadType, }, } sha := sha256.Sum256(b) // Always start with the hash want := []string{"sha256:" + hex.EncodeToString(sha[:])} want = append(want, tt.want...) got, _ := v.IndexKeys() sort.Strings(got) sort.Strings(want) if !cmp.Equal(got, want) { t.Errorf("V001Entry.IndexKeys() = %v, want %v", got, want) } }) } } func TestInsertable(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectSuccess bool } key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) keyObj, err := pkix509.NewPublicKey(bytes.NewReader(pub)) if err != nil { t.Fatal(err) } envStr := envelope(t, key, "payload", "payloadType") env := dsse.Envelope{} if err := json.Unmarshal([]byte(envStr), &env); err != nil { t.Fatal(err) } testCases := []TestCase{ { caseDesc: "valid entry", entry: V001Entry{ IntotoObj: models.IntotoV001Schema{ Content: &models.IntotoV001SchemaContent{ Envelope: "envelope", }, PublicKey: p(pub), }, keyObj: keyObj, env: env, }, expectSuccess: true, }, { caseDesc: "missing parsed keyObj", entry: V001Entry{ IntotoObj: models.IntotoV001Schema{ Content: &models.IntotoV001SchemaContent{ Envelope: "envelope", }, PublicKey: p(pub), }, env: env, }, expectSuccess: false, }, { caseDesc: "missing parsed DSSE envelope", entry: V001Entry{ IntotoObj: models.IntotoV001Schema{ Content: &models.IntotoV001SchemaContent{ Envelope: "envelope", }, PublicKey: p(pub), }, keyObj: keyObj, }, expectSuccess: false, }, { caseDesc: "missing content", entry: V001Entry{ IntotoObj: models.IntotoV001Schema{ PublicKey: p(pub), }, keyObj: keyObj, env: env, }, expectSuccess: false, }, { caseDesc: "missing envelope string", entry: V001Entry{ IntotoObj: models.IntotoV001Schema{ Content: &models.IntotoV001SchemaContent{}, PublicKey: p(pub), }, keyObj: keyObj, env: env, }, expectSuccess: false, }, { caseDesc: "missing unparsed public key", entry: V001Entry{ IntotoObj: models.IntotoV001Schema{ Content: &models.IntotoV001SchemaContent{ Envelope: "envelope", }, }, keyObj: keyObj, env: env, }, expectSuccess: false, }, { caseDesc: "empty parsed DSSE envelope", entry: V001Entry{ IntotoObj: models.IntotoV001Schema{ Content: &models.IntotoV001SchemaContent{ Envelope: "envelope", }, PublicKey: p(pub), }, keyObj: keyObj, env: dsse.Envelope{}, }, expectSuccess: false, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess { t.Errorf("unexpected result calling Insertable: %v", err) } }) } } rekor-1.3.5/pkg/types/intoto/v0.0.1/fuzz_test.go000066400000000000000000000047001455727245600213330ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package intoto import ( "context" "sync" "testing" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/go-openapi/swag" fuzzUtils "github.com/sigstore/rekor/pkg/fuzz" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/intoto" ) var initter sync.Once func FuzzIntotoCreateProposedEntry(f *testing.F) { f.Fuzz(func(t *testing.T, propsData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) version := "0.0.1" ff := fuzz.NewConsumer(propsData) props, cleanup, err := fuzzUtils.CreateProps(ff, "intotoV001") if err != nil { t.Skip() } defer func() { for _, c := range cleanup { c() } }() it := intoto.New() entry, err := it.CreateProposedEntry(context.Background(), version, props) if err != nil { t.Skip() } ei, err := types.CreateVersionedEntry(entry) if err != nil { t.Skip() } if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err) } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("valid insertable entry should be able to be canonicalized: %v", err) } _, _ = ei.IndexKeys() }) } func FuzzIntotoUnmarshalAndCanonicalize(f *testing.F) { f.Fuzz(func(t *testing.T, entryData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(entryData) targetV001 := &models.IntotoV001Schema{} if err := ff.GenerateStruct(targetV001); err != nil { t.Skip() } targetEntry := &models.Intoto{ APIVersion: swag.String(APIVERSION), Spec: targetV001, } ei, err := types.UnmarshalEntry(targetEntry) if err != nil { t.Skip() } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("error canonicalizing unmarshalled entry: %v", err) } }) } rekor-1.3.5/pkg/types/intoto/v0.0.1/intoto_v0_0_1_schema.json000066400000000000000000000052621455727245600235460ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/intoto/intoto_v0_0_1_schema.json", "title": "intoto v0.0.1 Schema", "description": "Schema for intoto object", "type": "object", "properties": { "content": { "type": "object", "properties": { "envelope": { "description": "envelope", "type": "string", "writeOnly": true }, "hash": { "description": "Specifies the hash algorithm and value encompassing the entire signed envelope; this is computed by the rekor server, client-provided values are ignored", "type": "object", "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the archive", "type": "string" } }, "required": [ "algorithm", "value" ], "readOnly": true }, "payloadHash": { "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope; this is computed by the rekor server, client-provided values are ignored", "type": "object", "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the envelope's payload", "type": "string" } }, "required": [ "algorithm", "value" ], "readOnly": true } } }, "publicKey": { "description": "The public key that can verify the signature", "type": "string", "format": "byte" } }, "required": [ "publicKey", "content" ] }rekor-1.3.5/pkg/types/intoto/v0.0.2/000077500000000000000000000000001455727245600167475ustar00rootroot00000000000000rekor-1.3.5/pkg/types/intoto/v0.0.2/entry.go000066400000000000000000000374511455727245600204510ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package intoto import ( "bytes" "context" "crypto" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "errors" "fmt" "os" "path/filepath" "strings" "github.com/in-toto/in-toto-golang/in_toto" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/spf13/viper" "golang.org/x/exp/slices" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/intoto" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" ) const ( APIVERSION = "0.0.2" ) func init() { if err := intoto.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil { log.Logger.Panic(err) } } type V002Entry struct { IntotoObj models.IntotoV002Schema env dsse.Envelope } func (v V002Entry) APIVersion() string { return APIVERSION } func NewEntry() types.EntryImpl { return &V002Entry{} } func (v V002Entry) IndexKeys() ([]string, error) { var result []string if v.IntotoObj.Content == nil || v.IntotoObj.Content.Envelope == nil { log.Logger.Info("IntotoObj content or dsse envelope is nil") return result, nil } for _, sig := range v.IntotoObj.Content.Envelope.Signatures { if sig == nil || sig.PublicKey == nil { return result, errors.New("malformed or missing signature") } keyObj, err := x509.NewPublicKey(bytes.NewReader(*sig.PublicKey)) if err != nil { return result, err } canonKey, err := keyObj.CanonicalValue() if err != nil { return result, fmt.Errorf("could not canonicize key: %w", err) } keyHash := sha256.Sum256(canonKey) result = append(result, "sha256:"+hex.EncodeToString(keyHash[:])) result = append(result, keyObj.Subjects()...) } payloadKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.IntotoObj.Content.PayloadHash.Algorithm, *v.IntotoObj.Content.PayloadHash.Value)) result = append(result, payloadKey) // since we can't deterministically calculate this server-side (due to public keys being added inline, and also canonicalization being potentially different), // we'll just skip adding this index key // hashkey := strings.ToLower(fmt.Sprintf("%s:%s", *v.IntotoObj.Content.Hash.Algorithm, *v.IntotoObj.Content.Hash.Value)) // result = append(result, hashkey) switch *v.IntotoObj.Content.Envelope.PayloadType { case in_toto.PayloadType: if v.IntotoObj.Content.Envelope.Payload == nil { log.Logger.Info("IntotoObj DSSE payload is empty") return result, nil } decodedPayload, err := base64.StdEncoding.DecodeString(string(v.IntotoObj.Content.Envelope.Payload)) if err != nil { return result, fmt.Errorf("could not decode envelope payload: %w", err) } statement, err := parseStatement(decodedPayload) if err != nil { return result, err } for _, s := range statement.Subject { for alg, ds := range s.Digest { result = append(result, alg+":"+ds) } } // Not all in-toto statements will contain a SLSA provenance predicate. // See https://github.com/in-toto/attestation/blob/main/spec/README.md#predicate // for other predicates. if predicate, err := parseSlsaPredicate(decodedPayload); err == nil { if predicate.Predicate.Materials != nil { for _, s := range predicate.Predicate.Materials { for alg, ds := range s.Digest { result = append(result, alg+":"+ds) } } } } default: log.Logger.Infof("Unknown in_toto DSSE envelope Type: %s", *v.IntotoObj.Content.Envelope.PayloadType) } return result, nil } func parseStatement(p []byte) (*in_toto.Statement, error) { ps := in_toto.Statement{} if err := json.Unmarshal(p, &ps); err != nil { return nil, err } return &ps, nil } func parseSlsaPredicate(p []byte) (*in_toto.ProvenanceStatement, error) { predicate := in_toto.ProvenanceStatement{} if err := json.Unmarshal(p, &predicate); err != nil { return nil, err } return &predicate, nil } func (v *V002Entry) Unmarshal(pe models.ProposedEntry) error { it, ok := pe.(*models.Intoto) if !ok { return errors.New("cannot unmarshal non Intoto v0.0.2 type") } var err error if err := types.DecodeEntry(it.Spec, &v.IntotoObj); err != nil { return err } // field validation if err := v.IntotoObj.Validate(strfmt.Default); err != nil { return err } if string(v.IntotoObj.Content.Envelope.Payload) == "" { return nil } env := &dsse.Envelope{ Payload: string(v.IntotoObj.Content.Envelope.Payload), PayloadType: *v.IntotoObj.Content.Envelope.PayloadType, } allPubKeyBytes := make([][]byte, 0) for i, sig := range v.IntotoObj.Content.Envelope.Signatures { if sig == nil { v.IntotoObj.Content.Envelope.Signatures = slices.Delete(v.IntotoObj.Content.Envelope.Signatures, i, i) continue } env.Signatures = append(env.Signatures, dsse.Signature{ KeyID: sig.Keyid, Sig: string(*sig.Sig), }) allPubKeyBytes = append(allPubKeyBytes, *sig.PublicKey) } if _, err := verifyEnvelope(allPubKeyBytes, env); err != nil { return err } v.env = *env decodedPayload, err := base64.StdEncoding.DecodeString(string(v.IntotoObj.Content.Envelope.Payload)) if err != nil { return fmt.Errorf("could not decode envelope payload: %w", err) } h := sha256.Sum256(decodedPayload) v.IntotoObj.Content.PayloadHash = &models.IntotoV002SchemaContentPayloadHash{ Algorithm: swag.String(models.IntotoV002SchemaContentPayloadHashAlgorithmSha256), Value: swag.String(hex.EncodeToString(h[:])), } return nil } func (v *V002Entry) Canonicalize(_ context.Context) ([]byte, error) { if err := v.IntotoObj.Validate(strfmt.Default); err != nil { return nil, err } if v.IntotoObj.Content.Hash == nil { return nil, errors.New("missing envelope digest") } if err := v.IntotoObj.Content.Hash.Validate(strfmt.Default); err != nil { return nil, fmt.Errorf("error validating envelope digest: %w", err) } if v.IntotoObj.Content.PayloadHash == nil { return nil, errors.New("missing payload digest") } if err := v.IntotoObj.Content.PayloadHash.Validate(strfmt.Default); err != nil { return nil, fmt.Errorf("error validating payload digest: %w", err) } if len(v.IntotoObj.Content.Envelope.Signatures) == 0 { return nil, errors.New("missing signatures") } canonicalEntry := models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: &models.IntotoV002SchemaContentEnvelope{ PayloadType: v.IntotoObj.Content.Envelope.PayloadType, Signatures: v.IntotoObj.Content.Envelope.Signatures, }, Hash: v.IntotoObj.Content.Hash, PayloadHash: v.IntotoObj.Content.PayloadHash, }, } itObj := models.Intoto{} itObj.APIVersion = swag.String(APIVERSION) itObj.Spec = &canonicalEntry return json.Marshal(&itObj) } // AttestationKey returns the digest of the attestation that was uploaded, to be used to lookup the attestation from storage func (v *V002Entry) AttestationKey() string { if v.IntotoObj.Content != nil && v.IntotoObj.Content.PayloadHash != nil { return fmt.Sprintf("%s:%s", *v.IntotoObj.Content.PayloadHash.Algorithm, *v.IntotoObj.Content.PayloadHash.Value) } return "" } // AttestationKeyValue returns both the key and value to be persisted into attestation storage func (v *V002Entry) AttestationKeyValue() (string, []byte) { storageSize := base64.StdEncoding.DecodedLen(len(v.env.Payload)) if storageSize > viper.GetInt("max_attestation_size") { log.Logger.Infof("Skipping attestation storage, size %d is greater than max %d", storageSize, viper.GetInt("max_attestation_size")) return "", nil } attBytes, err := base64.StdEncoding.DecodeString(v.env.Payload) if err != nil { log.Logger.Infof("could not decode envelope payload: %w", err) return "", nil } return v.AttestationKey(), attBytes } type verifier struct { s signature.Signer v signature.Verifier } func (v *verifier) KeyID() (string, error) { return "", nil } func (v *verifier) Public() crypto.PublicKey { // the dsse library uses this to generate a key ID if the KeyID function returns an empty string // as well for the AcceptedKey return value. Unfortunately since key ids can be arbitrary, we don't // know how to generate a matching id for the key id on the envelope's signature... // dsse verify will skip verifiers whose key id doesn't match the signature's key id, unless it fails // to generate one from the public key... so we trick it by returning nil ¯\_(ツ)_/¯ return nil } func (v *verifier) Sign(_ context.Context, data []byte) (sig []byte, err error) { if v.s == nil { return nil, errors.New("nil signer") } sig, err = v.s.SignMessage(bytes.NewReader(data), options.WithCryptoSignerOpts(crypto.SHA256)) if err != nil { return nil, err } return sig, nil } func (v *verifier) Verify(_ context.Context, data, sig []byte) error { if v.v == nil { return errors.New("nil verifier") } return v.v.VerifySignature(bytes.NewReader(sig), bytes.NewReader(data)) } func (v V002Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) { returnVal := models.Intoto{} re := V002Entry{ IntotoObj: models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: &models.IntotoV002SchemaContentEnvelope{}, }, }} var err error artifactBytes := props.ArtifactBytes if artifactBytes == nil { if props.ArtifactPath == nil { return nil, errors.New("path to artifact file must be specified") } if props.ArtifactPath.IsAbs() { return nil, errors.New("intoto envelopes cannot be fetched over HTTP(S)") } artifactBytes, err = os.ReadFile(filepath.Clean(props.ArtifactPath.Path)) if err != nil { return nil, err } } env := dsse.Envelope{} if err := json.Unmarshal(artifactBytes, &env); err != nil { return nil, fmt.Errorf("payload must be a valid dsse envelope: %w", err) } allPubKeyBytes := make([][]byte, 0) if len(props.PublicKeyBytes) > 0 { allPubKeyBytes = append(allPubKeyBytes, props.PublicKeyBytes...) } if len(props.PublicKeyPaths) > 0 { for _, path := range props.PublicKeyPaths { if path.IsAbs() { return nil, errors.New("dsse public keys cannot be fetched over HTTP(S)") } publicKeyBytes, err := os.ReadFile(filepath.Clean(path.Path)) if err != nil { return nil, fmt.Errorf("error reading public key file: %w", err) } allPubKeyBytes = append(allPubKeyBytes, publicKeyBytes) } } keysBySig, err := verifyEnvelope(allPubKeyBytes, &env) if err != nil { return nil, err } b64 := strfmt.Base64([]byte(env.Payload)) re.IntotoObj.Content.Envelope.Payload = b64 re.IntotoObj.Content.Envelope.PayloadType = &env.PayloadType for _, sig := range env.Signatures { key, ok := keysBySig[sig.Sig] if !ok { return nil, errors.New("all signatures must have a key that verifies it") } canonKey, err := key.CanonicalValue() if err != nil { return nil, fmt.Errorf("could not canonicize key: %w", err) } keyBytes := strfmt.Base64(canonKey) sigBytes := strfmt.Base64([]byte(sig.Sig)) re.IntotoObj.Content.Envelope.Signatures = append(re.IntotoObj.Content.Envelope.Signatures, &models.IntotoV002SchemaContentEnvelopeSignaturesItems0{ Keyid: sig.KeyID, Sig: &sigBytes, PublicKey: &keyBytes, }) } h := sha256.Sum256([]byte(artifactBytes)) re.IntotoObj.Content.Hash = &models.IntotoV002SchemaContentHash{ Algorithm: swag.String(models.IntotoV001SchemaContentHashAlgorithmSha256), Value: swag.String(hex.EncodeToString(h[:])), } returnVal.Spec = re.IntotoObj returnVal.APIVersion = swag.String(re.APIVersion()) return &returnVal, nil } // verifyEnvelope takes in an array of possible key bytes and attempts to parse them as x509 public keys. // it then uses these to verify the envelope and makes sure that every signature on the envelope is verified. // it returns a map of verifiers indexed by the signature the verifier corresponds to. func verifyEnvelope(allPubKeyBytes [][]byte, env *dsse.Envelope) (map[string]*x509.PublicKey, error) { // generate a fake id for these keys so we can get back to the key bytes and match them to their corresponding signature verifierBySig := make(map[string]*x509.PublicKey) allSigs := make(map[string]struct{}) for _, sig := range env.Signatures { allSigs[sig.Sig] = struct{}{} } for _, pubKeyBytes := range allPubKeyBytes { key, err := x509.NewPublicKey(bytes.NewReader(pubKeyBytes)) if err != nil { return nil, fmt.Errorf("could not parse public key as x509: %w", err) } vfr, err := signature.LoadVerifier(key.CryptoPubKey(), crypto.SHA256) if err != nil { return nil, fmt.Errorf("could not load verifier: %w", err) } dsseVfr, err := dsse.NewEnvelopeVerifier(&verifier{ v: 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) verifierBySig[accept.Sig.Sig] = key } } if len(allSigs) > 0 { return nil, errors.New("all signatures must have a key that verifies it") } return verifierBySig, nil } func (v V002Entry) Verifiers() ([]pki.PublicKey, error) { if v.IntotoObj.Content == nil || v.IntotoObj.Content.Envelope == nil { return nil, errors.New("intoto v0.0.2 entry not initialized") } sigs := v.IntotoObj.Content.Envelope.Signatures if len(sigs) == 0 { return nil, errors.New("no signatures found on intoto entry") } var keys []pki.PublicKey for _, s := range v.IntotoObj.Content.Envelope.Signatures { key, err := x509.NewPublicKey(bytes.NewReader(*s.PublicKey)) if err != nil { return nil, err } keys = append(keys, key) } return keys, nil } func (v V002Entry) ArtifactHash() (string, error) { if v.IntotoObj.Content == nil || v.IntotoObj.Content.PayloadHash == nil || v.IntotoObj.Content.PayloadHash.Algorithm == nil || v.IntotoObj.Content.PayloadHash.Value == nil { return "", errors.New("intoto v0.0.2 entry not initialized") } return strings.ToLower(fmt.Sprintf("%s:%s", *v.IntotoObj.Content.PayloadHash.Algorithm, *v.IntotoObj.Content.PayloadHash.Value)), nil } func (v V002Entry) Insertable() (bool, error) { if v.IntotoObj.Content == nil { return false, errors.New("missing content property") } if v.IntotoObj.Content.Envelope == nil { return false, errors.New("missing envelope property") } if len(v.IntotoObj.Content.Envelope.Payload) == 0 { return false, errors.New("missing envelope content") } if v.IntotoObj.Content.Envelope.PayloadType == nil || len(*v.IntotoObj.Content.Envelope.PayloadType) == 0 { return false, errors.New("missing payloadType content") } if len(v.IntotoObj.Content.Envelope.Signatures) == 0 { return false, errors.New("missing signatures content") } for _, sig := range v.IntotoObj.Content.Envelope.Signatures { if sig == nil { return false, errors.New("missing signature entry") } if sig.Sig == nil || len(*sig.Sig) == 0 { return false, errors.New("missing signature content") } if sig.PublicKey == nil || len(*sig.PublicKey) == 0 { return false, errors.New("missing publicKey content") } } if v.env.Payload == "" || v.env.PayloadType == "" || len(v.env.Signatures) == 0 { return false, errors.New("invalid DSSE envelope") } return true, nil } rekor-1.3.5/pkg/types/intoto/v0.0.2/entry_test.go000066400000000000000000000516641455727245600215120ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package intoto import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/json" "encoding/pem" "fmt" "math/big" "reflect" "sort" "testing" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/in-toto/in-toto-golang/in_toto" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/sigstore/pkg/signature" "go.uber.org/goleak" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestNewEntryReturnType(t *testing.T) { entry := NewEntry() if reflect.TypeOf(entry) != reflect.ValueOf(&V002Entry{}).Type() { t.Errorf("invalid type returned from NewEntry: %T", entry) } } func envelope(t *testing.T, k *ecdsa.PrivateKey, payload []byte) *dsse.Envelope { s, err := signature.LoadECDSASigner(k, crypto.SHA256) if err != nil { t.Fatal(err) } signer, err := dsse.NewEnvelopeSigner( &verifier{ s: s, }) if err != nil { t.Fatal(err) } dsseEnv, err := signer.SignPayload(context.Background(), "application/vnd.in-toto+json", payload) if err != nil { t.Fatal(err) } return dsseEnv } func multiSignEnvelope(t *testing.T, k []*ecdsa.PrivateKey, payload []byte) *dsse.Envelope { evps := []*verifier{} for _, key := range k { s, err := signature.LoadECDSASigner(key, crypto.SHA256) if err != nil { t.Fatal(err) } evps = append(evps, &verifier{ s: s, }) } signer, err := dsse.NewEnvelopeSigner(evps[0], evps[1]) if err != nil { t.Fatal(err) } dsseEnv, err := signer.SignPayload(context.Background(), in_toto.PayloadType, payload) if err != nil { t.Fatal(err) } return dsseEnv } func createRekorEnvelope(dsseEnv *dsse.Envelope, pub [][]byte) *models.IntotoV002SchemaContentEnvelope { env := &models.IntotoV002SchemaContentEnvelope{} b64 := strfmt.Base64([]byte(dsseEnv.Payload)) env.Payload = b64 env.PayloadType = &dsseEnv.PayloadType for i, sig := range dsseEnv.Signatures { keyBytes := strfmt.Base64(pub[i]) sigBytes := strfmt.Base64([]byte(sig.Sig)) env.Signatures = append(env.Signatures, &models.IntotoV002SchemaContentEnvelopeSignaturesItems0{ Keyid: sig.KeyID, Sig: &sigBytes, PublicKey: &keyBytes, }) } return env } func envelopeHash(t *testing.T, dsseEnv *dsse.Envelope) string { val, err := json.Marshal(dsseEnv) if err != nil { t.Fatal(err) } h := sha256.Sum256(val) return hex.EncodeToString(h[:]) } func TestV002Entry_Unmarshal(t *testing.T) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } ca := &x509.Certificate{ SerialNumber: big.NewInt(1), } caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &priv.PublicKey, priv) if err != nil { t.Fatal(err) } pemBytes := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: caBytes, }) invalid := dsse.Envelope{ Payload: "hello", Signatures: []dsse.Signature{ { Sig: string(strfmt.Base64("foobar")), }, }, } validPayload := "hellothispayloadisvalid" keyBytes := strfmt.Base64("key") sigBytes := strfmt.Base64("sig") tests := []struct { env *dsse.Envelope name string it *models.IntotoV002Schema wantErr bool wantVerifierErr bool }{ { name: "empty", it: &models.IntotoV002Schema{}, wantErr: true, wantVerifierErr: true, }, { name: "missing envelope", it: &models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Hash: &models.IntotoV002SchemaContentHash{ Algorithm: swag.String(models.IntotoV002SchemaContentHashAlgorithmSha256), }, }, }, wantErr: true, wantVerifierErr: true, }, { env: envelope(t, key, []byte(validPayload)), name: "valid", it: &models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: createRekorEnvelope(envelope(t, key, []byte(validPayload)), [][]byte{pub}), Hash: &models.IntotoV002SchemaContentHash{ Algorithm: swag.String(models.IntotoV002SchemaContentHashAlgorithmSha256), Value: swag.String(envelopeHash(t, envelope(t, key, []byte(validPayload)))), }, }, }, wantErr: false, wantVerifierErr: false, }, { env: envelope(t, priv, []byte(validPayload)), name: "cert", it: &models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: createRekorEnvelope(envelope(t, priv, []byte(validPayload)), [][]byte{pemBytes}), Hash: &models.IntotoV002SchemaContentHash{ Algorithm: swag.String(models.IntotoV002SchemaContentHashAlgorithmSha256), Value: swag.String(envelopeHash(t, envelope(t, priv, []byte(validPayload)))), }, }, }, wantErr: false, wantVerifierErr: false, }, { env: &invalid, name: "invalid", it: &models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: createRekorEnvelope(&invalid, [][]byte{pub}), Hash: &models.IntotoV002SchemaContentHash{ Algorithm: swag.String(models.IntotoV002SchemaContentHashAlgorithmSha256), Value: swag.String(envelopeHash(t, &invalid)), }, }, }, wantErr: true, wantVerifierErr: false, }, { env: envelope(t, key, []byte(validPayload)), name: "invalid key", it: &models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: createRekorEnvelope(envelope(t, key, []byte(validPayload)), [][]byte{[]byte("notavalidkey")}), Hash: &models.IntotoV002SchemaContentHash{ Algorithm: swag.String(models.IntotoV002SchemaContentHashAlgorithmSha256), Value: swag.String(envelopeHash(t, envelope(t, key, []byte(validPayload)))), }, }, }, wantErr: true, wantVerifierErr: true, }, { env: multiSignEnvelope(t, []*ecdsa.PrivateKey{key, priv}, []byte(validPayload)), name: "multi-key", it: &models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: createRekorEnvelope(multiSignEnvelope(t, []*ecdsa.PrivateKey{key, priv}, []byte(validPayload)), [][]byte{pub, pemBytes}), Hash: &models.IntotoV002SchemaContentHash{ Algorithm: swag.String(models.IntotoV002SchemaContentHashAlgorithmSha256), Value: swag.String(envelopeHash(t, multiSignEnvelope(t, []*ecdsa.PrivateKey{key, priv}, []byte(validPayload)))), }, }, }, wantErr: false, wantVerifierErr: false, }, { env: envelope(t, key, []byte(validPayload)), name: "null array entry", it: &models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: &models.IntotoV002SchemaContentEnvelope{ Payload: strfmt.Base64("cGF5bG9hZAo="), PayloadType: swag.String("payloadType"), Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{ { PublicKey: &keyBytes, Sig: &sigBytes, }, nil, }, }, Hash: &models.IntotoV002SchemaContentHash{ Algorithm: swag.String(models.IntotoV002SchemaContentHashAlgorithmSha256), Value: swag.String(envelopeHash(t, envelope(t, key, []byte(validPayload)))), }, }, }, wantErr: true, wantVerifierErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { v := &V002Entry{} it := &models.Intoto{ Spec: tt.it, } var uv = func() error { if err := v.Unmarshal(it); err != nil { return err } if !tt.wantErr { if ok, err := v.Insertable(); !ok || err != nil { t.Errorf("unexpected error calling insertable on valid proposed entry: %v", err) } } want := []string{} for _, sig := range v.IntotoObj.Content.Envelope.Signatures { keyHash := sha256.Sum256(*sig.PublicKey) want = append(want, "sha256:"+hex.EncodeToString(keyHash[:])) } decodedPayload, err := base64.StdEncoding.DecodeString(tt.env.Payload) if err != nil { return fmt.Errorf("could not decode envelope payload: %w", err) } h := sha256.Sum256(decodedPayload) want = append(want, "sha256:"+hex.EncodeToString(h[:])) if !reflect.DeepEqual(v.AttestationKey(), "sha256:"+hex.EncodeToString(h[:])) { t.Errorf("V002Entry.AttestationKey() = %v, want %v", v.AttestationKey(), "sha256:"+hex.EncodeToString(h[:])) } got, _ := v.IndexKeys() sort.Strings(got) sort.Strings(want) if !reflect.DeepEqual(got, want) { t.Errorf("V002Entry.IndexKeys() = %v, want %v", got, want) } payloadBytes, _ := v.env.DecodeB64Payload() payloadSha := sha256.Sum256(payloadBytes) payloadHash := hex.EncodeToString(payloadSha[:]) canonicalBytes, err := v.Canonicalize(context.Background()) if err != nil { t.Errorf("error canonicalizing entry: %v", err) } pe, err := models.UnmarshalProposedEntry(bytes.NewReader(canonicalBytes), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tt.name, err) } canonicalEntry, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tt.name, err) } if ok, err := canonicalEntry.Insertable(); ok || err == nil { t.Errorf("unexpected success calling Insertable on entry created from canonicalized content") } canonicalV002 := canonicalEntry.(*V002Entry) fmt.Printf("%v", canonicalV002.IntotoObj.Content) if *canonicalV002.IntotoObj.Content.Hash.Value != *tt.it.Content.Hash.Value { t.Errorf("envelope hashes do not match post canonicalization: %v %v", *canonicalV002.IntotoObj.Content.Hash.Value, *tt.it.Content.Hash.Value) } if canonicalV002.AttestationKey() != "" && *canonicalV002.IntotoObj.Content.PayloadHash.Value != payloadHash { t.Errorf("payload hashes do not match post canonicalization: %v %v", canonicalV002.IntotoObj.Content.PayloadHash.Value, payloadHash) } canonicalIndexKeys, _ := canonicalV002.IndexKeys() if !cmp.Equal(got, canonicalIndexKeys, cmpopts.SortSlices(func(x, y string) bool { return x < y })) { t.Errorf("index keys from hydrated object do not match those generated from canonicalized (and re-hydrated) object: %v %v", got, canonicalIndexKeys) } hash, err := canonicalV002.ArtifactHash() expectedHash := sha256.Sum256([]byte(validPayload)) if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != "sha256:"+hex.EncodeToString(expectedHash[:]) { t.Errorf("unexpected match with ArtifactHash: %s", hash) } verifiers, err := v.Verifiers() if !tt.wantVerifierErr { if err != nil { t.Errorf("%v: unexpected error, got %v", tt.name, err) } else { pubV, _ := verifiers[0].CanonicalValue() if !reflect.DeepEqual(pubV, pub) && !reflect.DeepEqual(pubV, pemBytes) { t.Errorf("verifier and public keys do not match: %v, %v", string(pubV), string(pub)) } } } else { if err == nil { s, _ := verifiers[0].CanonicalValue() t.Errorf("%v: expected error for %v, got %v", tt.name, string(s), err) } } return nil } if err := uv(); (err != nil) != tt.wantErr { t.Errorf("V002Entry.Unmarshal() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestV002Entry_IndexKeys(t *testing.T) { key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { t.Fatal(err) } pub := pem.EncodeToMemory(&pem.Block{ Bytes: der, Type: "PUBLIC KEY", }) tests := []struct { name string statement in_toto.Statement want []string }{ { name: "standard", want: []string{}, statement: in_toto.Statement{ Predicate: "hello", }, }, { name: "subject", want: []string{"sha256:foo"}, statement: in_toto.Statement{ StatementHeader: in_toto.StatementHeader{ Subject: []in_toto.Subject{ { Name: "foo", Digest: map[string]string{ "sha256": "foo", }, }, }, }, Predicate: "hello", }, }, { name: "slsa", want: []string{"sha256:bar"}, statement: in_toto.Statement{ Predicate: slsa.ProvenancePredicate{ Materials: []common.ProvenanceMaterial{ { URI: "foo", Digest: map[string]string{ "sha256": "bar", }}, }, }, }, }, { name: "slsa wit header", want: []string{"sha256:foo", "sha256:bar"}, statement: in_toto.Statement{ StatementHeader: in_toto.StatementHeader{ Subject: []in_toto.Subject{ { Name: "foo", Digest: map[string]string{ "sha256": "foo", }, }, }, }, Predicate: slsa.ProvenancePredicate{ Materials: []common.ProvenanceMaterial{ { URI: "foo", Digest: map[string]string{ "sha256": "bar", }}, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { b, err := json.Marshal(tt.statement) if err != nil { t.Fatal(err) } payloadHash := sha256.Sum256(b) v := V002Entry{ IntotoObj: models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: createRekorEnvelope(envelope(t, key, b), [][]byte{pub}), Hash: &models.IntotoV002SchemaContentHash{ Algorithm: swag.String(models.IntotoV001SchemaContentHashAlgorithmSha256), Value: swag.String(envelopeHash(t, envelope(t, key, b))), }, PayloadHash: &models.IntotoV002SchemaContentPayloadHash{ Algorithm: swag.String(models.IntotoV001SchemaContentHashAlgorithmSha256), Value: swag.String(hex.EncodeToString(payloadHash[:])), }, }, }, env: *envelope(t, key, b), } want := []string{} for _, sig := range v.IntotoObj.Content.Envelope.Signatures { keyHash := sha256.Sum256(*sig.PublicKey) want = append(want, "sha256:"+hex.EncodeToString(keyHash[:])) } want = append(want, "sha256:"+hex.EncodeToString(payloadHash[:])) want = append(want, tt.want...) got, _ := v.IndexKeys() sort.Strings(got) sort.Strings(want) if !cmp.Equal(got, want) { t.Errorf("V001Entry.IndexKeys() = %v, want %v", got, want) } }) } } func TestInsertable(t *testing.T) { type TestCase struct { caseDesc string entry V002Entry expectSuccess bool } key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } env := envelope(t, key, []byte("payload")) keyBytes := strfmt.Base64([]byte("key")) sigBytes := strfmt.Base64([]byte("sig")) testCases := []TestCase{ { caseDesc: "valid entry", entry: V002Entry{ IntotoObj: models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: &models.IntotoV002SchemaContentEnvelope{ Payload: strfmt.Base64("payload"), PayloadType: swag.String("payloadType"), Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{ { PublicKey: &keyBytes, Sig: &sigBytes, }, }, }, }, }, env: *env, }, expectSuccess: true, }, { caseDesc: "valid entry but hasn't been parsed", entry: V002Entry{ IntotoObj: models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: &models.IntotoV002SchemaContentEnvelope{ Payload: strfmt.Base64("payload"), PayloadType: swag.String("payloadType"), Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{ { PublicKey: &keyBytes, Sig: &sigBytes, }, }, }, }, }, env: dsse.Envelope{}, }, expectSuccess: false, }, { caseDesc: "missing sig", entry: V002Entry{ IntotoObj: models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: &models.IntotoV002SchemaContentEnvelope{ Payload: strfmt.Base64("payload"), PayloadType: swag.String("payloadType"), Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{ { PublicKey: &keyBytes, //Sig: strfmt.Base64([]byte("sig")), }, }, }, }, }, env: *env, }, expectSuccess: false, }, { caseDesc: "missing key", entry: V002Entry{ IntotoObj: models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: &models.IntotoV002SchemaContentEnvelope{ Payload: strfmt.Base64("payload"), PayloadType: swag.String("payloadType"), Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{ { //PublicKey: strfmt.Base64([]byte("key")), Sig: &sigBytes, }, }, }, }, }, env: *env, }, expectSuccess: false, }, { caseDesc: "empty signatures", entry: V002Entry{ IntotoObj: models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: &models.IntotoV002SchemaContentEnvelope{ Payload: strfmt.Base64("payload"), PayloadType: swag.String("payloadType"), Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{}, /* Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{ { PublicKey: strfmt.Base64([]byte("key")), Sig: strfmt.Base64([]byte("sig")), }, }, */ }, }, }, env: *env, }, expectSuccess: false, }, { caseDesc: "missing payloadType", entry: V002Entry{ IntotoObj: models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: &models.IntotoV002SchemaContentEnvelope{ Payload: strfmt.Base64("payload"), //PayloadType: swag.String("payloadType"), Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{ { PublicKey: &keyBytes, Sig: &sigBytes, }, }, }, }, }, env: *env, }, expectSuccess: false, }, { caseDesc: "missing payload", entry: V002Entry{ IntotoObj: models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ Envelope: &models.IntotoV002SchemaContentEnvelope{ //Payload: strfmt.Base64("payload"), PayloadType: swag.String("payloadType"), Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{ { PublicKey: &keyBytes, Sig: &sigBytes, }, }, }, }, }, env: *env, }, expectSuccess: false, }, { caseDesc: "missing envelope", entry: V002Entry{ IntotoObj: models.IntotoV002Schema{ Content: &models.IntotoV002SchemaContent{ /* Envelope: &models.IntotoV002SchemaContentEnvelope{ Payload: strfmt.Base64("payload"), PayloadType: swag.String("payloadType"), Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{ { PublicKey: strfmt.Base64([]byte("key")), Sig: strfmt.Base64([]byte("sig")), }, }, }, */ }, }, env: *env, }, expectSuccess: false, }, { caseDesc: "missing content", entry: V002Entry{ IntotoObj: models.IntotoV002Schema{ /* Content: &models.IntotoV002SchemaContent{ Envelope: &models.IntotoV002SchemaContentEnvelope{ Payload: strfmt.Base64("payload"), PayloadType: swag.String("payloadType"), Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{ { PublicKey: strfmt.Base64([]byte("key")), Sig: strfmt.Base64([]byte("sig")), }, }, }, }, */ }, env: *env, }, expectSuccess: false, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess { t.Errorf("unexpected result calling Insertable: %v", err) } }) } } rekor-1.3.5/pkg/types/intoto/v0.0.2/fuzz_test.go000066400000000000000000000047001455727245600213340ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package intoto import ( "context" "sync" "testing" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/go-openapi/swag" fuzzUtils "github.com/sigstore/rekor/pkg/fuzz" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/intoto" ) var initter sync.Once func FuzzIntotoCreateProposedEntry(f *testing.F) { f.Fuzz(func(t *testing.T, propsData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) version := "0.0.2" ff := fuzz.NewConsumer(propsData) props, cleanup, err := fuzzUtils.CreateProps(ff, "intotoV002") if err != nil { t.Skip() } defer func() { for _, c := range cleanup { c() } }() it := intoto.New() entry, err := it.CreateProposedEntry(context.Background(), version, props) if err != nil { t.Skip() } ei, err := types.CreateVersionedEntry(entry) if err != nil { t.Skip() } if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err) } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("valid insertable entry should be able to be canonicalized: %v", err) } _, _ = ei.IndexKeys() }) } func FuzzIntotoUnmarshalAndCanonicalize(f *testing.F) { f.Fuzz(func(t *testing.T, entryData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(entryData) targetV002 := &models.IntotoV002Schema{} if err := ff.GenerateStruct(targetV002); err != nil { t.Skip() } targetEntry := &models.Intoto{ APIVersion: swag.String(APIVERSION), Spec: targetV002, } ei, err := types.UnmarshalEntry(targetEntry) if err != nil { t.Skip() } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("error canonicalizing unmarshalled entry: %v", err) } }) } rekor-1.3.5/pkg/types/intoto/v0.0.2/intoto_v0_0_2_schema.json000066400000000000000000000106261455727245600235500ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/intoto/intoto_v0_0_2_schema.json", "title": "intoto v0.0.2 Schema", "description": "Schema for intoto object", "type": "object", "properties": { "content": { "type": "object", "properties": { "envelope": { "description": "dsse envelope", "type": "object", "properties": { "payload": { "description": "payload of the envelope", "type": "string", "format": "byte", "writeOnly": true }, "payloadType": { "description": "type describing the payload", "type": "string" }, "signatures": { "description": "collection of all signatures of the envelope's payload", "type": "array", "minItems": 1, "items": { "description": "a signature of the envelope's payload along with the public key for the signature", "type": "object", "properties": { "keyid": { "description": "optional id of the key used to create the signature", "type": "string" }, "sig": { "description": "signature of the payload", "type": "string", "format": "byte" }, "publicKey": { "description": "public key that corresponds to this signature", "type": "string", "format": "byte" } }, "required": ["sig", "publicKey"] } } }, "required": ["payloadType", "signatures"] }, "hash": { "description": "Specifies the hash algorithm and value encompassing the entire signed envelope", "type": "object", "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the archive", "type": "string" } }, "required": [ "algorithm", "value" ], "readOnly": true }, "payloadHash": { "description": "Specifies the hash algorithm and value covering the payload within the DSSE envelope", "type": "object", "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value of the payload", "type": "string" } }, "required": [ "algorithm", "value" ], "readOnly": true } }, "required": [ "envelope" ] } }, "required": [ "content" ] } rekor-1.3.5/pkg/types/jar/000077500000000000000000000000001455727245600153645ustar00rootroot00000000000000rekor-1.3.5/pkg/types/jar/e2e.go000066400000000000000000000043231455727245600163700ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package jar import ( "archive/zip" "bytes" "context" "crypto" "github.com/sigstore/rekor/pkg/util" "os" "strings" "testing" "github.com/sassoftware/relic/lib/certloader" "github.com/sassoftware/relic/lib/signjar" "github.com/sassoftware/relic/lib/zipslicer" sigx509 "github.com/sigstore/rekor/pkg/pki/x509" ) //note: reuses PKI artifacts from x509 tests const manifest = `Manifest-Version: 1.0 Created-By: REPLACE Name: src/some/java/HelloWorld.class SHA-256-Digest: cp40SgHlLIIr397GHijW7aAmWNLn0rgKm5Ap9B4hLd4= ` // CreateSignedJar creates a signed JAR file with a single file inside func CreateSignedJar(t *testing.T, artifactPath string) { t.Helper() //create a ZIP file with a single file inside f, err := os.Create(artifactPath) if err != nil { t.Fatal(err) } zw := zip.NewWriter(f) jw, err := zw.Create("src/some/java/HelloWorld.class") if err != nil { t.Fatal(err) } jw.Write([]byte("HelloWorld!")) mf, err := zw.Create("META-INF/MANIFEST.MF") if err != nil { t.Fatal(err) } randManifest := strings.Replace(manifest, "REPLACE", util.RandomSuffix(16), 1) mf.Write([]byte(randManifest)) if err := zw.Close(); err != nil { t.Fatal(err) } f.Sync() buf := bytes.Buffer{} zipslicer.ZipToTar(f, &buf) jd, err := signjar.DigestJarStream(&buf, crypto.SHA256) if err != nil { t.Fatal(err) } c := certloader.Certificate{ PrivateKey: sigx509.CertPrivateKey, Leaf: sigx509.Certificate, } patch, _, err := jd.Sign(context.Background(), &c, "rekor", false, true, false) if err != nil { t.Fatal(err) } if err := patch.Apply(f, artifactPath); err != nil { t.Fatal(err) } } rekor-1.3.5/pkg/types/jar/e2e_test.go000066400000000000000000000021761455727245600174330ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package jar import ( "github.com/sigstore/rekor/pkg/util" "path/filepath" "testing" ) func TestJAR(t *testing.T) { td := t.TempDir() artifactPath := filepath.Join(td, "artifact.jar") CreateSignedJar(t, artifactPath) // If we do it twice, it should already exist out := util.RunCli(t, "upload", "--artifact", artifactPath, "--type", "jar") util.OutputContains(t, out, "Created entry at") out = util.RunCli(t, "upload", "--artifact", artifactPath, "--type", "jar") util.OutputContains(t, out, "Entry already exists") } rekor-1.3.5/pkg/types/jar/fuzz_test.go000066400000000000000000000016021455727245600177470ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package jar import ( "testing" "github.com/sigstore/rekor/pkg/generated/models" ) func FuzzJarUnmarshal(f *testing.F) { f.Fuzz(func(t *testing.T, raw []byte) { m := &models.Jar{} err := m.UnmarshalJSON(raw) if err != nil { t.Skip() } brt := New() brt.UnmarshalEntry(m) }) } rekor-1.3.5/pkg/types/jar/jar.go000066400000000000000000000034331455727245600164720ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package jar import ( "context" "errors" "fmt" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" ) const ( KIND = "jar" ) type BaseJARType struct { types.RekorType } func init() { types.TypeMap.Store(KIND, New) } func New() types.TypeImpl { bjt := BaseJARType{} bjt.Kind = KIND bjt.VersionMap = VersionMap return &bjt } var VersionMap = types.NewSemVerEntryFactoryMap() func (bjt *BaseJARType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) { if pe == nil { return nil, errors.New("proposed entry cannot be nil") } jar, ok := pe.(*models.Jar) if !ok { return nil, errors.New("cannot unmarshal non-JAR types") } return bjt.VersionedUnmarshal(jar, *jar.APIVersion) } func (bjt *BaseJARType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) { if version == "" { version = bjt.DefaultVersion() } ei, err := bjt.VersionedUnmarshal(nil, version) if err != nil { return nil, fmt.Errorf("fetching JAR version implementation: %w", err) } return ei.CreateFromArtifactProperties(ctx, props) } func (bjt BaseJARType) DefaultVersion() string { return "0.0.1" } rekor-1.3.5/pkg/types/jar/jar_schema.json000066400000000000000000000004731455727245600203570ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/jar/jar_schema.json", "title": "JAR Schema", "description": "Schema for JAR objects", "type": "object", "oneOf": [ { "$ref": "v0.0.1/jar_v0_0_1_schema.json" } ] } rekor-1.3.5/pkg/types/jar/jar_test.go000066400000000000000000000054141455727245600175320ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package jar import ( "errors" "testing" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" ) type UnmarshalTester struct { models.Jar types.BaseUnmarshalTester } type UnmarshalFailsTester struct { types.BaseUnmarshalTester } func (u UnmarshalFailsTester) NewEntry() types.EntryImpl { return &UnmarshalFailsTester{} } func (u UnmarshalFailsTester) Unmarshal(_ models.ProposedEntry) error { return errors.New("error") } func (u UnmarshalFailsTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } func TestJARType(t *testing.T) { // empty to start if VersionMap.Count() != 0 { t.Error("semver range was not blank at start of test") } u := UnmarshalTester{} // ensure semver range parser is working invalidSemVerRange := "not a valid semver range" VersionMap.SetEntryFactory(invalidSemVerRange, u.NewEntry) if VersionMap.Count() > 0 { t.Error("invalid semver range was incorrectly added to SemVerToFacFnMap") } // valid semver range can be parsed VersionMap.SetEntryFactory(">= 1.2.3", u.NewEntry) if VersionMap.Count() != 1 { t.Error("valid semver range was not added to SemVerToFacFnMap") } u.Jar.APIVersion = swag.String("2.0.1") brt := New() // version requested matches implementation in map if _, err := brt.UnmarshalEntry(&u.Jar); err != nil { t.Errorf("unexpected error in Unmarshal: %v", err) } // version requested fails to match implementation in map u.Jar.APIVersion = swag.String("1.2.2") if _, err := brt.UnmarshalEntry(&u.Jar); err == nil { t.Error("unexpected success in Unmarshal for non-matching version") } // error in Unmarshal call is raised appropriately u.Jar.APIVersion = swag.String("2.2.0") u2 := UnmarshalFailsTester{} VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry) if _, err := brt.UnmarshalEntry(&u.Jar); err == nil { t.Error("unexpected success in Unmarshal when error is thrown") } // version requested fails to match implementation in map u.Jar.APIVersion = swag.String("not_a_version") if _, err := brt.UnmarshalEntry(&u.Jar); err == nil { t.Error("unexpected success in Unmarshal for invalid version") } } rekor-1.3.5/pkg/types/jar/v0.0.1/000077500000000000000000000000001455727245600162065ustar00rootroot00000000000000rekor-1.3.5/pkg/types/jar/v0.0.1/entry.go000066400000000000000000000245271455727245600177100ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package jar import ( "archive/zip" "bytes" "context" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "io" "os" "path" "path/filepath" "strings" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/pki/pkcs7" "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/jar" "github.com/sigstore/rekor/pkg/util" "github.com/asaskevich/govalidator" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" jarutils "github.com/sassoftware/relic/lib/signjar" "github.com/sigstore/rekor/pkg/generated/models" "github.com/spf13/viper" ) const ( APIVERSION = "0.0.1" ) func init() { if err := jar.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil { log.Logger.Panic(err) } } type V001Entry struct { JARModel models.JarV001Schema } func (v V001Entry) APIVersion() string { return APIVERSION } func NewEntry() types.EntryImpl { return &V001Entry{} } func (v *V001Entry) IndexKeys() ([]string, error) { var result []string keyObj, err := pkcs7.NewSignature(bytes.NewReader(v.JARModel.Signature.Content)) if err != nil { return nil, err } key, err := keyObj.CanonicalValue() if err != nil { return nil, err } keyHash := sha256.Sum256(key) result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:]))) if v.JARModel.Archive.Hash != nil { hashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.JARModel.Archive.Hash.Algorithm, *v.JARModel.Archive.Hash.Value)) result = append(result, hashKey) } return result, nil } func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error { jar, ok := pe.(*models.Jar) if !ok { return errors.New("cannot unmarshal non JAR v0.0.1 type") } if err := types.DecodeEntry(jar.Spec, &v.JARModel); err != nil { return err } // field validation if err := v.JARModel.Validate(strfmt.Default); err != nil { return err } return v.validate() } func (v *V001Entry) fetchExternalEntities(_ context.Context) (*pkcs7.PublicKey, *pkcs7.Signature, error) { if err := v.validate(); err != nil { return nil, nil, types.ValidationError(err) } oldSHA := "" if v.JARModel.Archive.Hash != nil && v.JARModel.Archive.Hash.Value != nil { oldSHA = swag.StringValue(v.JARModel.Archive.Hash.Value) } dataReadCloser := bytes.NewReader(v.JARModel.Archive.Content) hasher := sha256.New() b := &bytes.Buffer{} n, err := io.Copy(io.MultiWriter(hasher, b), dataReadCloser) if err != nil { return nil, nil, err } computedSHA := hex.EncodeToString(hasher.Sum(nil)) if oldSHA != "" && computedSHA != oldSHA { return nil, nil, types.ValidationError(fmt.Errorf("SHA mismatch: %s != %s", computedSHA, oldSHA)) } zipReader, err := zip.NewReader(bytes.NewReader(b.Bytes()), n) if err != nil { return nil, nil, types.ValidationError(err) } // Checking that uncompressed metadata files are within acceptable bounds before reading into memory. // Checks match those performed by the relic library in the jarutils.Verify method below. For example, // the META-INF/MANIFEST.MF is read into memory by the relic lib, but a META-INF/LICENSE file is not. for _, f := range zipReader.File { dir, name := path.Split(strings.ToUpper(f.Name)) if dir != "META-INF/" || name == "" || strings.LastIndex(name, ".") < 0 { continue } if f.UncompressedSize64 > viper.GetUint64("max_jar_metadata_size") && viper.GetUint64("max_jar_metadata_size") > 0 { return nil, nil, types.ValidationError( fmt.Errorf("uncompressed jar metadata of size %d exceeds max allowed size %d", f.UncompressedSize64, viper.GetUint64("max_jar_metadata_size"))) } } // this ensures that the JAR is signed and the signature verifies, as // well as checks that the hashes in the signed manifest are all valid jarObjs, err := jarutils.Verify(zipReader, false) if err != nil { return nil, nil, types.ValidationError(err) } switch len(jarObjs) { case 0: return nil, nil, types.ValidationError(errors.New("no signatures detected in JAR archive")) case 1: default: return nil, nil, types.ValidationError(errors.New("multiple signatures detected in JAR; unable to process")) } // we need to find and extract the PKCS7 bundle from the JAR file manually sigPKCS7, err := extractPKCS7SignatureFromJAR(zipReader) if err != nil { return nil, nil, types.ValidationError(err) } keyObj, err := pkcs7.NewPublicKey(bytes.NewReader(sigPKCS7)) if err != nil { return nil, nil, types.ValidationError(err) } sigObj, err := pkcs7.NewSignature(bytes.NewReader(sigPKCS7)) if err != nil { return nil, nil, types.ValidationError(err) } // if we get here, all goroutines succeeded without error if oldSHA == "" { v.JARModel.Archive.Hash = &models.JarV001SchemaArchiveHash{ Algorithm: swag.String(models.JarV001SchemaArchiveHashAlgorithmSha256), Value: swag.String(computedSHA), } } return keyObj, sigObj, nil } func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) { keyObj, sigObj, err := v.fetchExternalEntities(ctx) if err != nil { return nil, err } // need to canonicalize key content keyContent, err := keyObj.CanonicalValue() if err != nil { return nil, err } sigContent, err := sigObj.CanonicalValue() if err != nil { return nil, err } canonicalEntry := models.JarV001Schema{ Signature: &models.JarV001SchemaSignature{ PublicKey: &models.JarV001SchemaSignaturePublicKey{ Content: (*strfmt.Base64)(&keyContent), }, Content: sigContent, }, Archive: &models.JarV001SchemaArchive{ Hash: &models.JarV001SchemaArchiveHash{ Algorithm: v.JARModel.Archive.Hash.Algorithm, Value: v.JARModel.Archive.Hash.Value, }, }, } // archive content is not set deliberately v.JARModel = canonicalEntry // wrap in valid object with kind and apiVersion set jar := models.Jar{} jar.APIVersion = swag.String(APIVERSION) jar.Spec = &canonicalEntry return json.Marshal(&jar) } // validate performs cross-field validation for fields in object func (v *V001Entry) validate() error { archive := v.JARModel.Archive if archive == nil { return errors.New("missing package") } // if the signature isn't present, then we need content to extract if v.JARModel.Signature == nil || v.JARModel.Signature.Content == nil { if len(archive.Content) == 0 { return errors.New("'content' must be specified for package") } } hash := archive.Hash if hash != nil { if !govalidator.IsHash(swag.StringValue(hash.Value), swag.StringValue(hash.Algorithm)) { return errors.New("invalid value for hash") } } return nil } // extractPKCS7SignatureFromJAR extracts the first signature file from the JAR and returns it func extractPKCS7SignatureFromJAR(inz *zip.Reader) ([]byte, error) { for _, f := range inz.File { dir, name := path.Split(strings.ToUpper(f.Name)) if dir != "META-INF/" || name == "" { continue } i := strings.LastIndex(name, ".") if i < 0 { continue } fileExt := name[i:] if fileExt == ".RSA" || fileExt == ".DSA" || fileExt == ".EC" || strings.HasPrefix(name, "SIG-") { fileReader, err := f.Open() if err != nil { return nil, err } contents, err := io.ReadAll(fileReader) if err != nil { return nil, err } if err = fileReader.Close(); err != nil { return nil, err } return contents, nil } } return nil, errors.New("unable to locate signature in JAR file") } func (v *V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) { returnVal := models.Jar{} re := V001Entry{} // we will need only the artifact; public-key & signature are embedded in JAR re.JARModel = models.JarV001Schema{} re.JARModel.Archive = &models.JarV001SchemaArchive{} var err error artifactBytes := props.ArtifactBytes if artifactBytes == nil { var artifactReader io.ReadCloser if props.ArtifactPath == nil { return nil, errors.New("path to artifact file must be specified") } if props.ArtifactPath.IsAbs() { artifactReader, err = util.FileOrURLReadCloser(ctx, props.ArtifactPath.String(), nil) if err != nil { return nil, fmt.Errorf("error reading JAR file: %w", err) } } else { artifactReader, err = os.Open(filepath.Clean(props.ArtifactPath.Path)) if err != nil { return nil, fmt.Errorf("error opening JAR file: %w", err) } } artifactBytes, err = io.ReadAll(artifactReader) if err != nil { return nil, fmt.Errorf("error reading JAR file: %w", err) } } re.JARModel.Archive.Content = (strfmt.Base64)(artifactBytes) if err := re.validate(); err != nil { return nil, err } if _, _, err := re.fetchExternalEntities(ctx); err != nil { return nil, fmt.Errorf("error retrieving external entities: %v", err) } returnVal.APIVersion = swag.String(re.APIVersion()) returnVal.Spec = re.JARModel return &returnVal, nil } func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { if v.JARModel.Signature == nil || v.JARModel.Signature.PublicKey == nil || v.JARModel.Signature.PublicKey.Content == nil { return nil, errors.New("jar v0.0.1 entry not initialized") } key, err := x509.NewPublicKey(bytes.NewReader(*v.JARModel.Signature.PublicKey.Content)) if err != nil { return nil, err } return []pki.PublicKey{key}, nil } func (v V001Entry) ArtifactHash() (string, error) { if v.JARModel.Archive == nil || v.JARModel.Archive.Hash == nil || v.JARModel.Archive.Hash.Value == nil || v.JARModel.Archive.Hash.Algorithm == nil { return "", errors.New("jar v0.0.1 entry not initialized") } return strings.ToLower(fmt.Sprintf("%s:%s", *v.JARModel.Archive.Hash.Algorithm, *v.JARModel.Archive.Hash.Value)), nil } func (v V001Entry) Insertable() (bool, error) { if v.JARModel.Archive == nil { return false, errors.New("missing archive property") } if len(v.JARModel.Archive.Content) == 0 { return false, errors.New("missing archive content") } return true, nil } rekor-1.3.5/pkg/types/jar/v0.0.1/entry_test.go000066400000000000000000000156601455727245600207450ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package jar import ( "bytes" "context" "os" "reflect" "strings" "testing" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/spf13/viper" "go.uber.org/goleak" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestNewEntryReturnType(t *testing.T) { entry := NewEntry() if reflect.TypeOf(entry) != reflect.ValueOf(&V001Entry{}).Type() { t.Errorf("invalid type returned from NewEntry: %T", entry) } } func TestCrossFieldValidation(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectUnmarshalSuccess bool expectCanonicalizeSuccess bool expectedVerifierSuccess bool } jarBytes, _ := os.ReadFile("tests/test.jar") // extracted from jar certificate := `-----BEGIN CERTIFICATE----- MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAq MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx MDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUu ZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSy A7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0Jcas taRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6Nm MGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE FMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2u Su1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJx Ve/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uup Hr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ== -----END CERTIFICATE----- ` testCases := []TestCase{ { caseDesc: "empty obj", entry: V001Entry{}, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "empty archive", entry: V001Entry{ JARModel: models.JarV001Schema{ Archive: &models.JarV001SchemaArchive{}, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "archive with inline content", entry: V001Entry{ JARModel: models.JarV001Schema{ Archive: &models.JarV001SchemaArchive{ Content: strfmt.Base64(jarBytes), }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: true, expectedVerifierSuccess: true, }, } for _, tc := range testCases { v := &V001Entry{} r := models.Jar{ APIVersion: swag.String(tc.entry.APIVersion()), Spec: tc.entry.JARModel, } if err := v.Unmarshal(&r); (err == nil) != tc.expectUnmarshalSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } // No need to continue here if unmarshal failed if !tc.expectUnmarshalSuccess { continue } if ok, err := v.Insertable(); !ok || err != nil { t.Errorf("unexpected error calling Insertable on valid proposed entry: %v", err) } b, err := v.Canonicalize(context.TODO()) if (err == nil) != tc.expectCanonicalizeSuccess { t.Errorf("unexpected result from Canonicalize for '%v': %v", tc.caseDesc, err) } else if err != nil { if _, ok := err.(types.ValidationError); !ok { t.Errorf("canonicalize returned an unexpected error that isn't of type types.ValidationError: %v", err) } } if b != nil { pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tc.caseDesc, err) } ei, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tc.caseDesc, err) } if ok, err := ei.Insertable(); ok || err == nil { t.Errorf("unexpected err from calling Insertable on entry created from canonicalized content") } hash, err := ei.ArtifactHash() if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != "sha256:4a9d0ab4e10597497ed6c4617c983c35fa9e964e75cdd6f9fae3a0da1929acc6" { t.Errorf("unexpected match with ArtifactHash: %s", hash) } } verifiers, err := v.Verifiers() if tc.expectedVerifierSuccess { if err != nil { t.Errorf("%v: unexpected error, got %v", tc.caseDesc, err) } else { pub, _ := verifiers[0].CanonicalValue() if !reflect.DeepEqual(pub, []byte(certificate)) { t.Errorf("verifier and public keys do not match: %v, %v", string(pub), certificate) } } } else { if err == nil { s, _ := verifiers[0].CanonicalValue() t.Errorf("%v: expected error for %v, got %v", tc.caseDesc, string(s), err) } } } } func TestJarMetadataSize(t *testing.T) { jarBytes, _ := os.ReadFile("tests/test.jar") os.Setenv("MAX_JAR_METADATA_SIZE", "10") viper.AutomaticEnv() v := V001Entry{ JARModel: models.JarV001Schema{ Archive: &models.JarV001SchemaArchive{ Content: strfmt.Base64(jarBytes), }, }, } r := models.Jar{ APIVersion: swag.String(v.APIVersion()), Spec: v.JARModel, } if err := v.Unmarshal(&r); err != nil { t.Errorf("unexpected unmarshal failure: %v", err) } _, err := v.Canonicalize(context.TODO()) if err == nil { t.Fatal("expecting metadata too large err") } if !strings.Contains(err.Error(), "exceeds max allowed size 10") { t.Fatalf("unexpected error %v", err) } } func TestInsertable(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectSuccess bool } testCases := []TestCase{ { caseDesc: "valid entry", entry: V001Entry{ JARModel: models.JarV001Schema{ Archive: &models.JarV001SchemaArchive{ Content: strfmt.Base64([]byte("content")), }, }, }, expectSuccess: true, }, { caseDesc: "missing archive content", entry: V001Entry{ JARModel: models.JarV001Schema{ Archive: &models.JarV001SchemaArchive{ //Content: strfmt.Base64([]byte("content")), }, }, }, expectSuccess: false, }, { caseDesc: "missing archive obj", entry: V001Entry{ JARModel: models.JarV001Schema{ /* Archive: &models.JarV001SchemaArchive{ Content: strfmt.Base64([]byte("content")), }, */ }, }, expectSuccess: false, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess { t.Errorf("unexpected result calling Insertable: %v", err) } }) } } rekor-1.3.5/pkg/types/jar/v0.0.1/fuzz_test.go000066400000000000000000000067601455727245600206030ustar00rootroot00000000000000// // Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package jar import ( "archive/zip" "bytes" "context" "sync" "testing" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/go-openapi/swag" jarutils "github.com/sassoftware/relic/lib/signjar" fuzzUtils "github.com/sigstore/rekor/pkg/fuzz" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/jar" ) var initter sync.Once func FuzzJarCreateProposedEntry(f *testing.F) { f.Fuzz(func(t *testing.T, propsData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) version := "0.0.1" ff := fuzz.NewConsumer(propsData) props, cleanup, err := fuzzUtils.CreateProps(ff, "jarV001") if err != nil { t.Skip() } defer func() { for _, c := range cleanup { c() } }() it := jar.New() entry, err := it.CreateProposedEntry(context.Background(), version, props) if err != nil { t.Skip() } ei, err := types.CreateVersionedEntry(entry) if err != nil { t.Skip() } if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err) } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("valid insertable entry should be able to be canonicalized: %v", err) } _, _ = ei.IndexKeys() }) } func FuzzJarUnmarshalAndCanonicalize(f *testing.F) { f.Fuzz(func(t *testing.T, entryData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(entryData) targetV001 := &models.JarV001Schema{} if err := ff.GenerateStruct(targetV001); err != nil { t.Skip() } targetEntry := &models.Jar{ APIVersion: swag.String(APIVERSION), Spec: targetV001, } ei, err := types.UnmarshalEntry(targetEntry) if err != nil { t.Skip() } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Skip() } }) } type zipFile struct { fileName string fileBody []byte } func FuzzJarutilsVerify(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { ff := fuzz.NewConsumer(data) noOfFiles, err := ff.GetInt() if err != nil { return } zipFiles := make([]*zipFile, 0) for i := 0; i < noOfFiles%20; i++ { fileName, err := ff.GetString() if err != nil { return } fileBody, err := ff.GetBytes() if err != nil { return } zf := &zipFile{ fileName: fileName, fileBody: fileBody, } zipFiles = append(zipFiles, zf) } if len(zipFiles) == 0 { return } buf := new(bytes.Buffer) w := zip.NewWriter(buf) for _, file := range zipFiles { f, err := w.Create(file.fileName) if err != nil { w.Close() return } _, err = f.Write([]byte(file.fileBody)) if err != nil { w.Close() return } } w.Close() zipData := buf.Bytes() zipReader, err := zip.NewReader(bytes.NewReader(zipData), int64(len(zipData))) if err != nil { return } _, _ = jarutils.Verify(zipReader, false) }) } rekor-1.3.5/pkg/types/jar/v0.0.1/jar_v0_0_1_schema.json000066400000000000000000000054431455727245600222470ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/jar/jar_v0_0_1_schema.json", "title": "JAR v0.0.1 Schema", "description": "Schema for JAR entries", "type": "object", "properties": { "signature": { "description": "Information about the included signature in the JAR file", "type": "object", "properties": { "content": { "description": "Specifies the PKCS7 signature embedded within the JAR file ", "type": "string", "format": "byte", "readOnly": true }, "publicKey" : { "description": "The X509 certificate containing the public key JAR which verifies the signature of the JAR", "type": "object", "properties": { "content": { "description": "Specifies the content of the X509 certificate containing the public key used to verify the signature", "type": "string", "format": "byte" } }, "required": [ "content" ], "readOnly": true } }, "required": [ "publicKey", "content" ] }, "archive": { "description": "Information about the archive associated with the entry", "type": "object", "properties": { "hash": { "description": "Specifies the hash algorithm and value encompassing the entire signed archive", "type": "object", "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the archive", "type": "string" } }, "required": [ "algorithm", "value" ] }, "content": { "description": "Specifies the archive inline within the document", "type": "string", "format": "byte", "writeOnly": true } }, "oneOf": [ { "required": [ "hash" ] }, { "required": [ "content" ] } ] } }, "required": [ "archive" ] }rekor-1.3.5/pkg/types/jar/v0.0.1/tests/000077500000000000000000000000001455727245600173505ustar00rootroot00000000000000rekor-1.3.5/pkg/types/jar/v0.0.1/tests/test.jar000066400000000000000000000621041455727245600210300ustar00rootroot00000000000000PKkRMETA-INF/MANIFEST.MFK@F&#WY[[tB˫y x界殺ҕ9tTV @EtN[Vcݜ2aRjNQC\=gb c/x=&hN_V@\k?L]觬aY{ &*s*}6)&0ܑ@U oh lH\CP*G鐑m3 ;/:67aP.u4P[Ph:6䶰sf8=1)/G FSS֔cw1gnjV8 ONnǡ( sib^陻@V(i( Ү8<& J]xr,x9qL5%䋬9"Y\bnً;SP+{ڷhA]HN\BN:`tgq 1v Spo<@UZWJp.U1D Gh8>BB&Γ*ɼ 0ӓVqjuTy#}>^wY8<KG j\:S@.B/a7Û1_ ˚#{{tjDd&,uO/R}>BbKp{8Vv%Zjlr&(~$)XVW7FLvUX[TPK3֯PKkRMETA-INF/SIGSTORE.ECŗw\0AA4LAHr3HJ5B8B @(!Q7j_EܣdXB⨨( ց _X }‡O~=s\z6^T (Xuo8h 0hAp!e Aኒ$-h}}QH$N,hyx(Ds`FZO) Ig!DO2z܀8Z=s@&Pe2I扔{>{~z'ݤtqRqCXOglo7ݜgҦފo>>}D3sAY]ҮG̝@L cL($4[h }Av99;}s8Ic0U숚0 D%E IONV2P%\ig#SEUjr<w5$efoRn]xh~#si=WyfҚԀ*hP[®T:j:z~w0Iq`q2el祥2yBA2OKë1WA,dH: CP^uwHM/+]+4X?ߒln&ܨk* ض, eMYSQt9qGA1I$A/yOm]V:r_'Aap8*w)'@T|\e>ǗJBGD!MH#H4\B"HIzۓ׆$'IIiBLz瑩<ЛJ D J0Ek9L3 I+D(d&Viա\Ow^͛d_t>yG %Reӽe'hKoA|0lM^@es)CxWࢪOqo $jLP@c;%'?HF#C&+޶3pX \u'܉F8( &T&lcp\2iy%|4PPe`0!j0_@QrBvo:9J$*Kď6&|TԾm/y!']Pzȋ w3(3CJ ̌M!>]>1/!mƐu@/g/[žh nY-'m$hN%b@6X'Ni# LZCK$ ` ]Gv@+P*cDb2$ ˠG7hɰQx"v@GT8IwǑ)i 8vtnK.f=?s嵐JPnQbowA6xSM|PmK&ĝd-u,bbOf)W|7wJ32|W^9`SkR<W,ıEt}h1w;z@r} avE!jͽ쬀v2[H1qE^7G&Oc8rX`sp1;pzHS~}LTks *{:l|j~LpI8 !a|P4Jɟ)Zyk}U;mzaq/mK{>,&mg][[U^H^#ŷ~mi>o:g+mݫLzޙyN ޺ ^嫓P\YA=& psS6ޡL|zS:A َЁ_T QF$Box_S֥5KZWkr]rd1[|bvrNWeI;6)}L[~S%~O/?_8uۣ@MtѴK_si!g㻞_NUٖN`n R]{{%1inEyNMW@D߆{;T_V-Nl A)zW&ɭ[}/5~}vgfil>dС12z(]1]נ!8_k1.&./c [5b7QA f3ڔ U OW YL~^Nڧ{o`  m8T``\u*dpJni?ƈ.u4/:WjDfus{Ϙ`0L'^&/ÃOA^-| y Xr~F* =i<5DZVSr1|gIk+Yz|֓ &V|8sx~!ag7I2PQ3Zg2Q$4pJ PJi/TC /ٛ s!ahBf?7 Hxې@,:EC֩6=-s.ӊ^5a\ZV*0݄% W*)}^!V  lWkLW<ބiS5nηu߮M 96Mo A,,?}/?˄8 XJ(+0P<Aӳ=y߆ :S:SY"b oѥ ԕ\SUҔD@G ] <,ģ&de&脴VUc7EL#<,xhXesXCp@)Va4L`RkQTNA61 ;Mu7Bd"Jzqv>4fנNk},, <5uw6,u:Ajwd2)n;{ށs9 XJà0d@$Eh\HEԔtXUR"#PD_oNY"sa2tFVAkkT ZD{IuXRq!6K6XnxG rGO #'(1;lkQhױjGu%n"x%ة%8)+[j =%ic&f;Q"*=\%]nv굈QJO5> R95"ǹ@:,\~tƄL]N xQY)#41.ё)&^xRj)]W u-q+lC,p"dWmMr#"ѿ V##B>uz`)ٝN!h>zvw~ ;/IĝLM'P17SuU(ע(E]t{qgXPCCˉn’ WiЄ4k>S3 u K1zPyS3mOG%8Jkd {_S&OvdI=ѓ% Ko<뉚_;a"\*bfL&/}HMnvŸdcornݎ%eg$L&{ k9/9%DP6ttD8ϵȨt? mЫ} nT@'։{1}8kWe\/ iL#Y Į[2~9G)O=RWd_~dnYARjw]O]7l ]^WAR}YHj,"T?~yxG_)e:*_PKsB-PK@tR9META-INF/maven/dev.sigstore/sigstore-maven-plugin/pom.xmlXo6_y}XHdKUņ5h5l[HD5rlw>L}XowߝGqd%ollgM·;o4)gėv2B(+888h;gʎF^Lggͧ[PM!qȲ@Jh'c=څX z)n{=d# 9ҏ#h($ET!0t} `QEB) ܹ* zuC[TgZ7 j ӨG `qѐNͨ@G^}Z,QC-RVfq2`"<_N`,NiDx&OkSKb٥UZ93M`~$dII@lD\`$fv-4H3 lfNi|I )|` H|SFM'v/߱ *i[wE H&M!zݚ9.} 2FpL.m?$xa&δa4*=4ΝI8>),ӢP'DKxCFZ@> )LtoJL.a,Pּ٩xG"f?ODxr59YM.~/\hjf< Ϋ^q&{jsb9_R I g{mEX(8’+F+9 ERX?xc=+f+(l9XRl0[+VyFzTZێ4Hzl]|q@~ƕ'h;k&jq_ ߛmzN\ YA9 95} (vtɚ~έĦBfyh)D]VH=SO ꚨN*}$Jme0jJMbP4S"QDg/+5&RKi^G v*-!~]EC]mz-D9Q=OovV!(@H6l6}^N.Ϸa2kvij+N;[mwWk5/ndԪ`PKG-lXPKkR@META-INF/maven/dev.sigstore/sigstore-maven-plugin/pom.propertiesSv.JM,IMQHTp,HLHUM,KS033J//-LMI-+L/./JJ,*LKL.tsAt rJ3R3l t=CPKqfbjPK kR sigstore/PK kRsigstore/plugin/PKkRsigstore/plugin/HelpMojo.classZ |Օ?g^d20I!CABu5 &I+N2_83 `}Rmo-؊U VVl־nmkۮR7$J[CfwϹ_|tӤT;'D;mңqif<]J:{P̪Hδ1TY7PyP,Oґ8ڲb -!Le}Z_wP\鞤 tz^P6ok[tO<zjfs uk75[657lokFbYL֪94NqftO$4y,@2{G_$(n96]q2o,ĉzgakFsrb$^پPgyN {K(sӕƱMS]aZ1b\&:. FlrT-\餑ȂF퐄0I710) rF0B1j>G$`135n_DIw;KN ''LG5yqҽF&d3RZohnt{K5CLkaD!ll9QF"iq;U1{ :AuɣRZބ]7}vKeXN35zhPRM}E{(؇ U|(cwhi-!(, m`Hk4SɈ'`"'}S꤃e/ꬔ^#HI/uȷ9+آ͑h&W0:jֺ()n&Wl;|Fo@hMߧ׊0C R'?B~nGAjV[0#P?-xq*qdUf~~Σ5zBV}kZY ާP_2ȇ^oF^-rޖ$T\.v HTw{pxCLoM%j/?-!M-I}["1_3 +@8 gu :c!0t(@[E# )%H185 lp1 ]8AVuW")6n.fz q)ɉYie88f&eO@-ȳDTcrD`!IxLnP8<_k˓z ႗;LXP\O*)h R* { mn1Iun^"„<䐜 뉤C; KWd!* nP["[+|"}vpX](2on!T4}tql4GZ^'vvDWd?AeN̲_G' vKC)y. AyR\SB6,heT9zdY6F4L:Y >F-Pe2}J6g3ZL(ͥs ncU%O՘tv9sζ73}mO51`>Dh]Ts>@]^>Hw͕ӥÞ6#_Di"JSI%4v;4t9]FJg+% 1,kdh^:֨`1FT44s Xځ@ DlV΅6&h!ZHTFjtt?Rdzoa{ u]DS\%&2(Y2EVH,QHVNCjs&5_B۱Gx5 $)0X˱?C?@ W @Lgr_~t-oh>@;.GmW{n[Z5[KwJÎhk'! Qv^1@OKhc4@w4s81tH?@z<:k?>8j,J e>SB4o C iG>:?W)dU;y̵sSzfNRH ,D A8~ r Z ܮkki]r=wwl=JE/ыtJ^z롇x23QnpvzCH/r T17DGpL y/Kq}}įE&̳R W@k/ZA4hr+xm"[lҥуwV\dFu'҃vDd<2V d|XJUHeL{G3@ <\Kym.;X9fu'[{(MNRZNDD:l"|JsVu$D,X=rͷe:dZg(x]On86b{k*]TUyZ;OIyZl59J : #>R9U|~3^ñxAgak*cN90с*b!*Ypg L>% cL\ rG7 3F{GRψ  2x%{y!]hQ֢EِGTzFw e }J5e|GxP9i8 *T T[| J:(4v:]*6gu'-:Q S%eS BKufgL)5F.ɣ3J尌R-F>^.k,#'Qڄ 2AP7)eav2*Q ѯAjA[ԞRo?#J? Rn?@On@^FL~6zON.?|E/JPayBiD/dP:A io[y"r/Z;t#3ŗp[}}.|n)sUsQsk6<< ӽ2xC|[`C7* y!ܯjrN9MTgr|T{\QNI r2"veOyxpOe)"ã4 Ș^* <\NH^>NAÕR3LG:O- e4E!:@ FH?Ԁs9l]Y>kf`Z]bk[Tk2aRS%Əyc|f:aM ߯Ϗ- &05aGg| ? .P_1 :OzeTmJ/X5k/ :+]uUҢpd%~J"]<=~e(ynin/FhW$SÙ(%Gᠣ4=HfePЕU5uvZ7z>KMt1L!nt#߁ZN羚N}b } q{PЭ@C䮠6ޏx򠉌UId2"}%8pZsÐaH{ d|*g5*R)~)Dƽy>vɲ)%_,ovVfƧL|2woHkQtM˔~]I:,ΆG㹉d ͆쟁DpU;riod']U2ka!oh}{_Aު'r_mask5Ga/d~LmRoI*d6)\o0gJUzV~k)B+9owva>| L}6sgPKEP-PKkRsigstore/plugin/Sign$1.classUmOA~-Ԋ(*Z-Q|A-*XZ%)6RSs\7e%w[~~3 ~W,hm3=;sX#>Q` D3P֖j Ϫ3 Mg(-Lđ`8v+zmZ?aRqs)e喽%,SVK jg]]Vvh{]HĐC&dH>_ؖk{M; O; }z )h+ Fpc8ΐ4eXTה/f!CKN0D3%p N8 {"6-YkM[G. åyMVQqMn妶a>aCh:@iutp@hϯI^:aGû8Ajl*5;8 }x_&~Gs}Xj}Sj_ ܤ[dGSvRY@Y?(@;PK.v|PKkRsigstore/plugin/Sign.class; |Sܤ}iJ!+`T>JPDRRX R>,kZi^{-ө:?sNEU[snNtN͹n**sKҴMڢ޽{瞯{y=\8px_i]ԣX-WV\ ZˌMBqh:Q>u\P[BPF62z )E(nӣz\+p[>a֩M=[[ɮ*!ȾmםcV؈*0aF.Ӊq\ASq_0kV8۬oH7H(!8U5daj L 6hNdQ?c̈ouh8ccmXjRm 5kQ3f-7xL04oYjaj%p$`g`3d$IcXP +p GDʾA#fh%iEv}y@W}cӪ:ĭrd@ S"0ԭ,d/n|2bNP 3ZA$]1L]k=xgB\_\}ǗvCmNP&NlVcduX?IighFH_1LL <*TqVѵ_ele_kD:MsN$$Ltd&  W#dNPjT# JWl@foyVZu ,Ra1,!NR|˰ 54nKyODż.C8aM+8M^(BP \pRr>6xBO6Z0e`GXZX! 0:a_arʅesAJHG(z}gP6hNQT[Xq"![_'7lh82IzBtZUh̒-i"¦ Vc2|y&=hUoIsIA(`M Z#!l&$OLv❆Pސ (f̾2l t 4{=`AP>sRm ŠTiL?/1F X.[P5¢-35A-Ҥ;J=;# 7t7YAKH.eP`v0LJEj&3 V|M5!YЪB.V.(;d}'wѦ[VrET6dC~1`K-+*VB܂8'MS:h)ɸf}ax_hubFT2\Rac M*|nFy:ǴIsu#۸U*~H΢D+>g=u.aB8 /jXi +R֝*~Lnp5bh76$6nG"%sbYM.tRS=55}T$Fu( ` a_?C}$އ*Jd_8_I_M)v ni6uӃ/~O+H}`bq>|:rϞnj:kf&&ߣWwó*<xTivt */lG2DD0_!^+ ^_)Tfmv]4h<<3\A&&{k+^W 5)@V&왭h=LFo;xaLLIS׸xW?G\,˷Uft,rRϩ-!,̍4  GPh# P/'u:{(i\#m[.7GcJX=PSˆgR='7/88(-{jBkD)VҫqmBֵv7{T1I[hB\IK;L ;mChÆA7bQYyQ0DHo fF:wJm`1Wp,A(j.Gi,QtZnZdVN_&XX,N .+:+ J l͵A&%ͥ<< oUL/ ['p:\x$Z,IƵ^Z8I8Y)*YLs09kCY^a+YgC&߅3UI$#*|~(1i#%L+f8!B<*x0pG[ v nM<mcXI]Sq>OFAӆhFM01{*0oy!͍ NE*.FrSHւdiVYROd'^$jӖVp9M;sЅc7/)3_Ɵ#P6iY+e^Q| / 8~Ƹ+nn|_W ,XSG,ƹc3)!?T-҈SB*ƛCtɨDQ}SH]TOenRabb{}uj\F*z+OgY3yS!RY:m\SPBope>Kj'Ys1I=~W$Žq}P8,(\:>U8TdgrV/(dЅB뵜"4Z * l4O Eb*Yt#H 1Î-ggؤs˸14>6>^h|/>FUĨ~#uॏ[%s$I jݢLWEaV+YYlh=/V"(K_Z4DfQLT$1Yz%zϪfej_9 `QnQ!)b:ϻ_))(„UL֯b4a˾B4~\UTq4o j:,&ҕQv'ΰ&STig̕~ΟR1Ki+7q0CAS֭Ү@0%m$ ] 7zUR&9A[g<錐I$TWH}WteZڼmBų {Dac뛍xFt6ELvG6G WjZN#4vUMv aN6:aBPⶤ QFiT_oRK?Dvc} .=5r#48yM=E Z$iQ% -_ټiuU/%G}CwDkؒU9V~StQ>SJƋý )=䃮Tq$>;}u ZDIa${S%"Pɓ,^R9٤t<%d.Lc O#f g D4ߔb^Z&gpɔ!qIixCdj8FzsYZ wٗwFeH:]lbKY2CH̹t`+(z\6nHcxd %%5t͡hBƦY&K9򸡔'UD|\6)H&K n$&ga9VZMɘ9*9SYd|E\7gE4nj g#LZYmO\bfʥpkEuՋȘ4[s@N1&Y/pNESSN߳h'ä]iy}f.7 csGlt"dAؓ$j4WݴPjP,ٓ*OFl}Qآ$ jW3vɁzrT}iJ_+it'i`l,$._MF_mUkWVDu v~W-4{aA> pO3"?$Mj']_v&#)l=yxmDcEyzx6(qy_z"Y5ώNMɎ Ч/( B*l)TN ^ (p=gZ'ӽw>u Z${G^X`: NIFA@1Ȣ$X d ԧ$ҼdLfC'R~*!/׊eTm(v> F.=0  Pv@n(n%{ats&o.( %`RufSg7̡ιӻa~sŃ ]P Ո]ղlh p77Wx]`$ fo^N3Y]pv ߛ97Zuz  \(l (ppM*`{$P$gr[ܱ[vx8n>3Pus&k;$NwpnjvrW.+&0Y=bZ ?% ~F/ {_R/b_EoXKz>Jǫ?p98a]y`x7Bsp:@eh9z82P-I 6Y%T@ HnLx5[3ꆢf߃y]l(Kp.\Ս'q}s7*Q6ta [cc}w@wl^ ۚ.{^")OOG~s펃yG'0ϗ6/ eb$KxIwݸyZK oK%+ |DpWI`w8.|&0K@-M ʒoj`;>Ko42{K&wInPow.k`8JHf2݃%a!TjǺrn/ۺ+0; q˘Ee,KSq?Y0dċ ՎSUGF L=q||F:'9ˡll@W6¾0n27L 0np+ nv8 .Z]p?<;a?<4IO=8"~.8p3<6xox‹^—e|~+9R DJ Y_FxSD Nl9.YO_3x &ހw_cs cߎG|쨄OS8hsǩm{w :h;:9|r|N ^T>,rNg9wNΣ\#?L&; ANVPM!.bX cbX ^a8Y4C)qRa9 )Tr[3 yׅF͢EiIWv9E+8sDHkqM4y$7pThB\'QjSq# z+BK/b4g;Eފqh`8 #m[\U%H!ę0R,q6q&hq.ֈooXZA q9)bKuhWe`;09hJMw|H (Nt%_E}6Z@gPkapѫ"VX@N" |!> ` .1K&OVpg0L0s΃H;MV(+,$NO@,NF( _Qĵ6tRǰ `^@RLS/KI./'bIG.yX9{~|@>*][>ÄTpxD 0 { t.Error("invalid semver range was incorrectly added to SemVerToFacFnMap") } // valid semver range can be parsed err = VersionMap.SetEntryFactory(">= 1.2.3", u.NewEntry) if err != nil || VersionMap.Count() != 1 { t.Error("valid semver range was not added to SemVerToFacFnMap") } u.Rekord.APIVersion = swag.String("2.0.1") brt := New() // version requested matches implementation in map if _, err := brt.UnmarshalEntry(&u.Rekord); err != nil { t.Errorf("unexpected error in Unmarshal: %v", err) } // version requested fails to match implementation in map u.Rekord.APIVersion = swag.String("1.2.2") if _, err := brt.UnmarshalEntry(&u.Rekord); err == nil { t.Error("unexpected success in Unmarshal for non-matching version") } // error in Unmarshal call is raised appropriately u.Rekord.APIVersion = swag.String("2.2.0") u2 := UnmarshalFailsTester{} _ = VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry) if _, err := brt.UnmarshalEntry(&u.Rekord); err == nil { t.Error("unexpected success in Unmarshal when error is thrown") } // version requested fails to match implementation in map u.Rekord.APIVersion = swag.String("not_a_version") if _, err := brt.UnmarshalEntry(&u.Rekord); err == nil { t.Error("unexpected success in Unmarshal for invalid version") } } rekor-1.3.5/pkg/types/rekord/tests/000077500000000000000000000000001455727245600172405ustar00rootroot00000000000000rekor-1.3.5/pkg/types/rekord/tests/test_file.sig000066400000000000000000000007151455727245600217250ustar00rootroot000000000000004!r\nie &8z_lhinds@protonmail.com &8z7 ra`2sQ0 :`. F^ Hˣ.!l#9*2 rquN'K7~G·#'0յ7C) tP#TE]O't]p^Ul_9u_E̮ʢ4GԨAU4R X8T>wc5;QUutCf0W^k !׹,I@JڊBQ"]!@ v)n[Hr*~0EKȺPA$rekor-1.3.5/pkg/types/rekord/tests/test_file.txt000066400000000000000000000000141455727245600217520ustar00rootroot00000000000000hello rekor rekor-1.3.5/pkg/types/rekord/tests/test_public_key.key000066400000000000000000000046301455727245600231420ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- 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 n14pbAaTdaKEPcvtufbUuW0IjYd2kpIT/tg= =Oghr -----END PGP PUBLIC KEY BLOCK----- rekor-1.3.5/pkg/types/rekord/v0.0.1/000077500000000000000000000000001455727245600167205ustar00rootroot00000000000000rekor-1.3.5/pkg/types/rekord/v0.0.1/entry.go000066400000000000000000000322641455727245600204170ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rekord import ( "bytes" "context" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "io" "os" "path/filepath" "strings" "github.com/asaskevich/govalidator" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "golang.org/x/sync/errgroup" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/pki/minisign" "github.com/sigstore/rekor/pkg/pki/pgp" "github.com/sigstore/rekor/pkg/pki/ssh" "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/rekord" "github.com/sigstore/rekor/pkg/util" ) const ( APIVERSION = "0.0.1" ) func init() { if err := rekord.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil { log.Logger.Panic(err) } } type V001Entry struct { RekordObj models.RekordV001Schema } func (v V001Entry) APIVersion() string { return APIVERSION } func NewEntry() types.EntryImpl { return &V001Entry{} } func (v V001Entry) IndexKeys() ([]string, error) { var result []string af, err := pki.NewArtifactFactory(pki.Format(*v.RekordObj.Signature.Format)) if err != nil { return nil, err } keyObj, err := af.NewPublicKey(bytes.NewReader(*v.RekordObj.Signature.PublicKey.Content)) if err != nil { return nil, err } key, err := keyObj.CanonicalValue() if err != nil { log.Logger.Error(err) } else { keyHash := sha256.Sum256(key) result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:]))) } result = append(result, keyObj.Subjects()...) if v.RekordObj.Data.Hash != nil { hashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.RekordObj.Data.Hash.Algorithm, *v.RekordObj.Data.Hash.Value)) result = append(result, hashKey) } return result, nil } func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error { rekord, ok := pe.(*models.Rekord) if !ok { return errors.New("cannot unmarshal non Rekord v0.0.1 type") } if err := types.DecodeEntry(rekord.Spec, &v.RekordObj); err != nil { return err } // field validation if err := v.RekordObj.Validate(strfmt.Default); err != nil { return err } // cross field validation return v.validate() } func (v *V001Entry) fetchExternalEntities(ctx context.Context) (pki.PublicKey, pki.Signature, error) { g, ctx := errgroup.WithContext(ctx) af, err := pki.NewArtifactFactory(pki.Format(*v.RekordObj.Signature.Format)) if err != nil { return nil, nil, err } hashR, hashW := io.Pipe() sigR, sigW := io.Pipe() defer hashR.Close() defer sigR.Close() closePipesOnError := types.PipeCloser(hashR, hashW, sigR, sigW) oldSHA := "" if v.RekordObj.Data.Hash != nil && v.RekordObj.Data.Hash.Value != nil { oldSHA = swag.StringValue(v.RekordObj.Data.Hash.Value) } g.Go(func() error { defer hashW.Close() defer sigW.Close() dataReadCloser := bytes.NewReader(v.RekordObj.Data.Content) /* #nosec G110 */ if _, err := io.Copy(io.MultiWriter(hashW, sigW), dataReadCloser); err != nil { return closePipesOnError(err) } return nil }) hashResult := make(chan string) g.Go(func() error { defer close(hashResult) hasher := sha256.New() if _, err := io.Copy(hasher, hashR); err != nil { return closePipesOnError(err) } computedSHA := hex.EncodeToString(hasher.Sum(nil)) if oldSHA != "" && computedSHA != oldSHA { return closePipesOnError(types.ValidationError(fmt.Errorf("SHA mismatch: %s != %s", computedSHA, oldSHA))) } select { case <-ctx.Done(): return ctx.Err() case hashResult <- computedSHA: return nil } }) sigResult := make(chan pki.Signature) g.Go(func() error { defer close(sigResult) sigReadCloser := bytes.NewReader(*v.RekordObj.Signature.Content) signature, err := af.NewSignature(sigReadCloser) if err != nil { return closePipesOnError(types.ValidationError(err)) } select { case <-ctx.Done(): return ctx.Err() case sigResult <- signature: return nil } }) keyResult := make(chan pki.PublicKey) g.Go(func() error { defer close(keyResult) keyReadCloser := bytes.NewReader(*v.RekordObj.Signature.PublicKey.Content) key, err := af.NewPublicKey(keyReadCloser) if err != nil { return closePipesOnError(types.ValidationError(err)) } select { case <-ctx.Done(): return ctx.Err() case keyResult <- key: return nil } }) var ( keyObj pki.PublicKey sigObj pki.Signature ) g.Go(func() error { keyObj, sigObj = <-keyResult, <-sigResult if keyObj == nil || sigObj == nil { return closePipesOnError(errors.New("failed to read signature or public key")) } var err error if err = sigObj.Verify(sigR, keyObj); err != nil { return closePipesOnError(types.ValidationError(err)) } select { case <-ctx.Done(): return ctx.Err() default: return nil } }) computedSHA := <-hashResult if err := g.Wait(); err != nil { return nil, nil, err } // if we get here, all goroutines succeeded without error if oldSHA == "" { v.RekordObj.Data.Hash = &models.RekordV001SchemaDataHash{} v.RekordObj.Data.Hash.Algorithm = swag.String(models.RekordV001SchemaDataHashAlgorithmSha256) v.RekordObj.Data.Hash.Value = swag.String(computedSHA) } return keyObj, sigObj, nil } func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) { keyObj, sigObj, err := v.fetchExternalEntities(ctx) if err != nil { return nil, err } canonicalEntry := models.RekordV001Schema{} // need to canonicalize signature & key content canonicalEntry.Signature = &models.RekordV001SchemaSignature{} // signature URL (if known) is not set deliberately canonicalEntry.Signature.Format = v.RekordObj.Signature.Format var sigContent []byte sigContent, err = sigObj.CanonicalValue() if err != nil { return nil, err } canonicalEntry.Signature.Content = (*strfmt.Base64)(&sigContent) var pubKeyContent []byte canonicalEntry.Signature.PublicKey = &models.RekordV001SchemaSignaturePublicKey{} pubKeyContent, err = keyObj.CanonicalValue() if err != nil { return nil, err } canonicalEntry.Signature.PublicKey.Content = (*strfmt.Base64)(&pubKeyContent) canonicalEntry.Data = &models.RekordV001SchemaData{} canonicalEntry.Data.Hash = v.RekordObj.Data.Hash // data content is not set deliberately // wrap in valid object with kind and apiVersion set rekordObj := models.Rekord{} rekordObj.APIVersion = swag.String(APIVERSION) rekordObj.Spec = &canonicalEntry v.RekordObj = canonicalEntry bytes, err := json.Marshal(&rekordObj) if err != nil { return nil, err } return bytes, nil } // validate performs cross-field validation for fields in object func (v V001Entry) validate() error { sig := v.RekordObj.Signature if v.RekordObj.Signature == nil { return errors.New("missing signature") } if sig.Content == nil || len(*sig.Content) == 0 { return errors.New("'content' must be specified for signature") } key := sig.PublicKey if key == nil { return errors.New("missing public key") } if key.Content == nil || len(*key.Content) == 0 { return errors.New("'content' must be specified for publicKey") } data := v.RekordObj.Data if data == nil { return errors.New("missing data") } hash := data.Hash if hash != nil { if !govalidator.IsHash(swag.StringValue(hash.Value), swag.StringValue(hash.Algorithm)) { return errors.New("invalid value for hash") } } else if len(data.Content) == 0 { return errors.New("'content' must be specified for data") } return nil } func (v V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) { returnVal := models.Rekord{} re := V001Entry{} // we will need artifact, public-key, signature re.RekordObj.Data = &models.RekordV001SchemaData{} var err error artifactBytes := props.ArtifactBytes if len(artifactBytes) == 0 { var artifactReader io.ReadCloser if props.ArtifactPath == nil { return nil, errors.New("path to artifact file must be specified") } if props.ArtifactPath.IsAbs() { artifactReader, err = util.FileOrURLReadCloser(ctx, props.ArtifactPath.String(), nil) if err != nil { return nil, fmt.Errorf("error reading artifact file: %w", err) } } else { artifactReader, err = os.Open(filepath.Clean(props.ArtifactPath.Path)) if err != nil { return nil, fmt.Errorf("error opening artifact file: %w", err) } } artifactBytes, err = io.ReadAll(artifactReader) if err != nil { return nil, fmt.Errorf("error reading artifact file: %w", err) } } re.RekordObj.Data.Content = strfmt.Base64(artifactBytes) re.RekordObj.Signature = &models.RekordV001SchemaSignature{} switch props.PKIFormat { case "pgp": re.RekordObj.Signature.Format = swag.String(models.RekordV001SchemaSignatureFormatPgp) case "minisign": re.RekordObj.Signature.Format = swag.String(models.RekordV001SchemaSignatureFormatMinisign) case "x509": re.RekordObj.Signature.Format = swag.String(models.RekordV001SchemaSignatureFormatX509) case "ssh": re.RekordObj.Signature.Format = swag.String(models.RekordV001SchemaSignatureFormatSSH) default: return nil, fmt.Errorf("unexpected format of public key: %s", props.PKIFormat) } sigBytes := props.SignatureBytes if len(sigBytes) == 0 { if props.SignaturePath == nil { return nil, errors.New("a detached signature must be provided") } sigBytes, err = os.ReadFile(filepath.Clean(props.SignaturePath.Path)) if err != nil { return nil, fmt.Errorf("error reading signature file: %w", err) } re.RekordObj.Signature.Content = (*strfmt.Base64)(&sigBytes) } else { re.RekordObj.Signature.Content = (*strfmt.Base64)(&sigBytes) } re.RekordObj.Signature.PublicKey = &models.RekordV001SchemaSignaturePublicKey{} publicKeyBytes := props.PublicKeyBytes if len(publicKeyBytes) == 0 { if len(props.PublicKeyPaths) != 1 { return nil, errors.New("only one public key must be provided to verify detached signature") } keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path)) if err != nil { return nil, fmt.Errorf("error reading public key file: %w", err) } publicKeyBytes = append(publicKeyBytes, keyBytes) } else if len(publicKeyBytes) != 1 { return nil, errors.New("only one public key must be provided") } re.RekordObj.Signature.PublicKey.Content = (*strfmt.Base64)(&publicKeyBytes[0]) if err := re.validate(); err != nil { return nil, err } if _, _, err := re.fetchExternalEntities(ctx); err != nil { return nil, fmt.Errorf("error retrieving external entities: %v", err) } returnVal.APIVersion = swag.String(re.APIVersion()) returnVal.Spec = re.RekordObj return &returnVal, nil } func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { if v.RekordObj.Signature == nil || v.RekordObj.Signature.PublicKey == nil || v.RekordObj.Signature.PublicKey.Content == nil { return nil, errors.New("rekord v0.0.1 entry not initialized") } var key pki.PublicKey var err error switch f := *v.RekordObj.Signature.Format; f { case "x509": key, err = x509.NewPublicKey(bytes.NewReader(*v.RekordObj.Signature.PublicKey.Content)) case "ssh": key, err = ssh.NewPublicKey(bytes.NewReader(*v.RekordObj.Signature.PublicKey.Content)) case "pgp": key, err = pgp.NewPublicKey(bytes.NewReader(*v.RekordObj.Signature.PublicKey.Content)) case "minisign": key, err = minisign.NewPublicKey(bytes.NewReader(*v.RekordObj.Signature.PublicKey.Content)) default: return nil, fmt.Errorf("unexpected format of public key: %s", f) } if err != nil { return nil, err } return []pki.PublicKey{key}, nil } func (v V001Entry) ArtifactHash() (string, error) { if v.RekordObj.Data == nil || v.RekordObj.Data.Hash == nil || v.RekordObj.Data.Hash.Value == nil || v.RekordObj.Data.Hash.Algorithm == nil { return "", errors.New("rekord v0.0.1 entry not initialized") } return strings.ToLower(fmt.Sprintf("%s:%s", *v.RekordObj.Data.Hash.Algorithm, *v.RekordObj.Data.Hash.Value)), nil } func (v V001Entry) Insertable() (bool, error) { if v.RekordObj.Signature == nil { return false, errors.New("missing signature property") } if v.RekordObj.Signature.Content == nil || len(*v.RekordObj.Signature.Content) == 0 { return false, errors.New("missing signature content") } if v.RekordObj.Signature.PublicKey == nil { return false, errors.New("missing publicKey property") } if v.RekordObj.Signature.PublicKey.Content == nil || len(*v.RekordObj.Signature.PublicKey.Content) == 0 { return false, errors.New("missing publicKey content") } if v.RekordObj.Signature.Format == nil || len(*v.RekordObj.Signature.Format) == 0 { return false, errors.New("missing signature format") } if v.RekordObj.Data == nil { return false, errors.New("missing data property") } if len(v.RekordObj.Data.Content) == 0 { return false, errors.New("missing data content") } return true, nil } rekor-1.3.5/pkg/types/rekord/v0.0.1/entry_test.go000066400000000000000000000331211455727245600214470ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rekord import ( "bytes" "context" "crypto/sha256" "encoding/hex" "os" "reflect" "testing" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "go.uber.org/goleak" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/rekord" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestNewEntryReturnType(t *testing.T) { entry := NewEntry() if reflect.TypeOf(entry) != reflect.ValueOf(&V001Entry{}).Type() { t.Errorf("invalid type returned from NewEntry: %T", entry) } } func TestCrossFieldValidation(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectUnmarshalSuccess bool expectCanonicalizeSuccess bool expectedVerifierSuccess bool } sigBytes, _ := os.ReadFile("../tests/test_file.sig") keyBytes, _ := os.ReadFile("../tests/test_public_key.key") dataBytes, _ := os.ReadFile("../tests/test_file.txt") testCases := []TestCase{ { caseDesc: "empty obj", entry: V001Entry{}, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "signature without url or content", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Signature: &models.RekordV001SchemaSignature{ Format: swag.String("pgp"), }, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "signature without public key", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Signature: &models.RekordV001SchemaSignature{ Format: swag.String("pgp"), Content: (*strfmt.Base64)(&sigBytes), }, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "signature with empty public key", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Signature: &models.RekordV001SchemaSignature{ Format: swag.String("pgp"), Content: (*strfmt.Base64)(&sigBytes), PublicKey: &models.RekordV001SchemaSignaturePublicKey{}, }, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "signature without data", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Signature: &models.RekordV001SchemaSignature{ Format: swag.String("pgp"), Content: (*strfmt.Base64)(&sigBytes), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, }, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "signature with empty data", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Signature: &models.RekordV001SchemaSignature{ Format: swag.String("pgp"), Content: (*strfmt.Base64)(&sigBytes), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, }, Data: &models.RekordV001SchemaData{}, }, }, expectUnmarshalSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "signature with invalid sig content, key content & with data with content", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Signature: &models.RekordV001SchemaSignature{ Format: swag.String("pgp"), Content: (*strfmt.Base64)(&dataBytes), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, }, Data: &models.RekordV001SchemaData{ Content: strfmt.Base64(dataBytes), }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "signature with sig content, invalid key content & with data with content", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Signature: &models.RekordV001SchemaSignature{ Format: swag.String("pgp"), Content: (*strfmt.Base64)(&sigBytes), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: (*strfmt.Base64)(&dataBytes), }, }, Data: &models.RekordV001SchemaData{ Content: strfmt.Base64(dataBytes), }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: false, expectedVerifierSuccess: false, }, { caseDesc: "signature with sig content, key content & with data with content", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Signature: &models.RekordV001SchemaSignature{ Format: swag.String("pgp"), Content: (*strfmt.Base64)(&sigBytes), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, }, Data: &models.RekordV001SchemaData{ Content: strfmt.Base64(keyBytes), }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: false, expectedVerifierSuccess: true, }, { caseDesc: "signature with sig content, key content & with data with content", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Signature: &models.RekordV001SchemaSignature{ Format: swag.String("pgp"), Content: (*strfmt.Base64)(&sigBytes), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, }, Data: &models.RekordV001SchemaData{ Content: strfmt.Base64(dataBytes), }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: true, expectedVerifierSuccess: true, }, } for _, tc := range testCases { v := &V001Entry{} r := models.Rekord{ APIVersion: swag.String(tc.entry.APIVersion()), Spec: tc.entry.RekordObj, } if err := v.Unmarshal(&r); (err == nil) != tc.expectUnmarshalSuccess { t.Fatalf("unexpected result in '%v': %v", tc.caseDesc, err) } // No need to continue here if we didn't unmarshal if !tc.expectUnmarshalSuccess { continue } if ok, err := v.Insertable(); !ok || err != nil { t.Errorf("unexpected error calling Insertable on valid proposed entry: %v", err) } b, err := v.Canonicalize(context.TODO()) if (err == nil) != tc.expectCanonicalizeSuccess { t.Errorf("unexpected result from Canonicalize for '%v': %v", tc.caseDesc, err) } else if err != nil { if _, ok := err.(types.ValidationError); !ok { t.Errorf("canonicalize returned an unexpected error that isn't of type types.ValidationError: %v", err) } } if b != nil { pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tc.caseDesc, err) } ei, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tc.caseDesc, err) } if ok, err := ei.Insertable(); ok || err == nil { t.Errorf("unexpected success calling Insertable on entry created from canonicalized content") } hash, err := ei.ArtifactHash() expectedHash := sha256.Sum256(dataBytes) if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != "sha256:"+hex.EncodeToString(expectedHash[:]) { t.Errorf("unexpected match with ArtifactHash: %s", hash) } } verifiers, err := v.Verifiers() if tc.expectedVerifierSuccess { if err != nil { t.Errorf("%v: unexpected error, got %v", tc.caseDesc, err) } else { // TODO: Improve this test once CanonicalValue returns same result as input for PGP keys _, err := verifiers[0].CanonicalValue() if err != nil { t.Errorf("%v: unexpected error getting canonical value, got %v", tc.caseDesc, err) } } } else { if err == nil { s, _ := verifiers[0].CanonicalValue() t.Errorf("%v: expected error for %v, got %v", tc.caseDesc, string(s), err) } } } } func TestUnspecifiedPKIFormat(t *testing.T) { props := types.ArtifactProperties{ ArtifactBytes: []byte("something"), SignatureBytes: []byte("signature"), PublicKeyBytes: [][]byte{[]byte("public_key")}, // PKIFormat is deliberately unspecified } rek := rekord.New() if _, err := rek.CreateProposedEntry(context.Background(), APIVERSION, props); err == nil { t.Errorf("no signature, public key or format should not create a valid entry") } props.PKIFormat = "invalid_format" if _, err := rek.CreateProposedEntry(context.Background(), APIVERSION, props); err == nil { t.Errorf("invalid pki format should not create a valid entry") } } func TestInsertable(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectSuccess bool } sig := strfmt.Base64([]byte("sig")) pub := strfmt.Base64([]byte("pub")) testCases := []TestCase{ { caseDesc: "valid entry", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Data: &models.RekordV001SchemaData{ Content: strfmt.Base64([]byte("content")), }, Signature: &models.RekordV001SchemaSignature{ Content: &sig, Format: swag.String("format"), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: &pub, }, }, }, }, expectSuccess: true, }, { caseDesc: "missing public key content", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Data: &models.RekordV001SchemaData{ Content: strfmt.Base64([]byte("content")), }, Signature: &models.RekordV001SchemaSignature{ Content: &sig, Format: swag.String("format"), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ //Content: &pub, }, }, }, }, expectSuccess: false, }, { caseDesc: "missing public key obj", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Data: &models.RekordV001SchemaData{ Content: strfmt.Base64([]byte("content")), }, Signature: &models.RekordV001SchemaSignature{ Content: &sig, Format: swag.String("format"), /* PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: &pub, }, */ }, }, }, expectSuccess: false, }, { caseDesc: "missing format string", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Data: &models.RekordV001SchemaData{ Content: strfmt.Base64([]byte("content")), }, Signature: &models.RekordV001SchemaSignature{ Content: &sig, //Format: swag.String("format"), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: &pub, }, }, }, }, expectSuccess: false, }, { caseDesc: "missing signature content", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Data: &models.RekordV001SchemaData{ Content: strfmt.Base64([]byte("content")), }, Signature: &models.RekordV001SchemaSignature{ //Content: &sig, Format: swag.String("format"), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: &pub, }, }, }, }, expectSuccess: false, }, { caseDesc: "missing signature obj", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Data: &models.RekordV001SchemaData{ Content: strfmt.Base64([]byte("content")), }, /* Signature: &models.RekordV001SchemaSignature{ Content: &sig, Format: swag.String("format"), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: &pub, }, }, */ }, }, expectSuccess: false, }, { caseDesc: "missing data content", entry: V001Entry{ RekordObj: models.RekordV001Schema{ Data: &models.RekordV001SchemaData{ //Content: strfmt.Base64([]byte("content")), }, Signature: &models.RekordV001SchemaSignature{ Content: &sig, Format: swag.String("format"), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: &pub, }, }, }, }, expectSuccess: false, }, { caseDesc: "missing data obj", entry: V001Entry{ RekordObj: models.RekordV001Schema{ /* Data: &models.RekordV001SchemaData{ Content: strfmt.Base64([]byte("content")), }, */ Signature: &models.RekordV001SchemaSignature{ Content: &sig, Format: swag.String("format"), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: &pub, }, }, }, }, expectSuccess: false, }, { caseDesc: "empty obj", entry: V001Entry{ RekordObj: models.RekordV001Schema{ /* Data: &models.RekordV001SchemaData{ Content: strfmt.Base64([]byte("content")), }, Signature: &models.RekordV001SchemaSignature{ Content: &sig, Format: swag.String("format"), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: &pub, }, }, */ }, }, expectSuccess: false, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess { t.Errorf("unexpected result calling Insertable: %v", err) } }) } } rekor-1.3.5/pkg/types/rekord/v0.0.1/fuzz_test.go000066400000000000000000000046141455727245600213110ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rekord import ( "context" "sync" "testing" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/go-openapi/swag" fuzzUtils "github.com/sigstore/rekor/pkg/fuzz" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/rekord" ) var initter sync.Once func FuzzRekordCreateProposedEntry(f *testing.F) { f.Fuzz(func(t *testing.T, propsData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) version := "0.0.1" ff := fuzz.NewConsumer(propsData) props, cleanup, err := fuzzUtils.CreateProps(ff, "rekordV001") if err != nil { t.Skip() } defer func() { for _, c := range cleanup { c() } }() it := rekord.New() entry, err := it.CreateProposedEntry(context.Background(), version, props) if err != nil { t.Skip() } ei, err := types.CreateVersionedEntry(entry) if err != nil { t.Skip() } if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err) } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("valid insertable entry should be able to be canonicalized: %v", err) } _, _ = ei.IndexKeys() }) } func FuzzRekordUnmarshalAndCanonicalize(f *testing.F) { f.Fuzz(func(t *testing.T, entryData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(entryData) targetV001 := &models.RekordV001Schema{} if err := ff.GenerateStruct(targetV001); err != nil { t.Skip() } targetEntry := &models.Rekord{ APIVersion: swag.String(APIVERSION), Spec: targetV001, } ei, err := types.UnmarshalEntry(targetEntry) if err != nil { t.Skip() } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Skip() } }) } rekor-1.3.5/pkg/types/rekord/v0.0.1/rekord_v0_0_1_schema.json000066400000000000000000000056441455727245600234760ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/rekord/rekord_v0_0_1_schema.json", "title": "Rekor v0.0.1 Schema", "description": "Schema for Rekord object", "type": "object", "properties": { "signature": { "description": "Information about the detached signature associated with the entry", "type": "object", "properties": { "format": { "description": "Specifies the format of the signature", "type": "string", "enum": [ "pgp", "minisign", "x509", "ssh" ] }, "content": { "description": "Specifies the content of the signature inline within the document", "type": "string", "format": "byte" }, "publicKey" : { "description": "The public key that can verify the signature", "type": "object", "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } }, "required": [ "content" ] } }, "required": [ "format", "publicKey", "content" ] }, "data": { "description": "Information about the content associated with the entry", "type": "object", "properties": { "hash": { "description": "Specifies the hash algorithm and value for the content", "type": "object", "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the content", "type": "string" } }, "required": [ "algorithm", "value" ], "readOnly": true }, "content": { "description": "Specifies the content inline within the document", "type": "string", "format": "byte", "writeOnly": true } }, "oneOf": [ { "required": [ "hash" ] }, { "required": [ "content" ] } ] } }, "required": [ "signature", "data" ] } rekor-1.3.5/pkg/types/rfc3161/000077500000000000000000000000001455727245600156755ustar00rootroot00000000000000rekor-1.3.5/pkg/types/rfc3161/e2e_test.go000066400000000000000000000027231455727245600177420ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package rfc3161 import ( "crypto/sha256" "encoding/hex" "fmt" "io/ioutil" "testing" "github.com/sigstore/rekor/pkg/util" ) func TestTimestampArtifact(t *testing.T) { var out string out = util.RunCli(t, "upload", "--type", "rfc3161", "--artifact", "tests/test.tsr") util.OutputContains(t, out, "Created entry at") uuid := util.GetUUIDFromUploadOutput(t, out) artifactBytes, err := ioutil.ReadFile("tests/test.tsr") if err != nil { t.Error(err) } sha := sha256.Sum256(artifactBytes) out = util.RunCli(t, "upload", "--type", "rfc3161", "--artifact", "tests/test.tsr") util.OutputContains(t, out, "Entry already exists") out = util.RunCli(t, "search", "--artifact", "tests/test.tsr") util.OutputContains(t, out, uuid) out = util.RunCli(t, "search", "--sha", fmt.Sprintf("sha256:%s", hex.EncodeToString(sha[:]))) util.OutputContains(t, out, uuid) } rekor-1.3.5/pkg/types/rfc3161/rfc3161.go000066400000000000000000000035321455727245600173140ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rfc3161 import ( "context" "errors" "fmt" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" ) const ( KIND = "rfc3161" ) type BaseTimestampType struct { types.RekorType } func init() { types.TypeMap.Store(KIND, New) } func New() types.TypeImpl { btt := BaseTimestampType{} btt.Kind = KIND btt.VersionMap = VersionMap return &btt } var VersionMap = types.NewSemVerEntryFactoryMap() func (btt BaseTimestampType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) { if pe == nil { return nil, errors.New("proposed entry cannot be nil") } rfc3161, ok := pe.(*models.Rfc3161) if !ok { return nil, errors.New("cannot unmarshal non-Timestamp types") } return btt.VersionedUnmarshal(rfc3161, *rfc3161.APIVersion) } func (btt *BaseTimestampType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) { if version == "" { version = btt.DefaultVersion() } ei, err := btt.VersionedUnmarshal(nil, version) if err != nil { return nil, fmt.Errorf("fetching RFC3161 version implementation: %w", err) } return ei.CreateFromArtifactProperties(ctx, props) } func (btt BaseTimestampType) DefaultVersion() string { return "0.0.1" } rekor-1.3.5/pkg/types/rfc3161/rfc3161_schema.json000066400000000000000000000005341455727245600211770ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/rfc3161/rfc3161_schema.json", "title": "Timestamp Schema", "description": "Schema for RFC 3161 timestamp objects", "type": "object", "oneOf": [ { "$ref": "v0.0.1/rfc3161_v0_0_1_schema.json" } ] } rekor-1.3.5/pkg/types/rfc3161/rfc3161_test.go000066400000000000000000000055461455727245600203620ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rfc3161 import ( "errors" "testing" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" ) type UnmarshalTester struct { models.Rfc3161 types.BaseUnmarshalTester } type UnmarshalFailsTester struct { types.BaseUnmarshalTester } func (u UnmarshalFailsTester) NewEntry() types.EntryImpl { return &UnmarshalFailsTester{} } func (u UnmarshalFailsTester) Unmarshal(_ models.ProposedEntry) error { return errors.New("error") } func (u UnmarshalFailsTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } func TestRfc3161Type(t *testing.T) { // empty to start if VersionMap.Count() != 0 { t.Error("semver range was not blank at start of test") } u := UnmarshalTester{} // ensure semver range parser is working invalidSemVerRange := "not a valid semver range" err := VersionMap.SetEntryFactory(invalidSemVerRange, u.NewEntry) if err == nil || VersionMap.Count() > 0 { t.Error("invalid semver range was incorrectly added to SemVerToFacFnMap") } // valid semver range can be parsed err = VersionMap.SetEntryFactory(">= 1.2.3", u.NewEntry) if err != nil || VersionMap.Count() != 1 { t.Error("valid semver range was not added to SemVerToFacFnMap") } u.Rfc3161.APIVersion = swag.String("2.0.1") brt := New() // version requested matches implementation in map if _, err := brt.UnmarshalEntry(&u.Rfc3161); err != nil { t.Errorf("unexpected error in Unmarshal: %v", err) } // version requested fails to match implementation in map u.Rfc3161.APIVersion = swag.String("1.2.2") if _, err := brt.UnmarshalEntry(&u.Rfc3161); err == nil { t.Error("unexpected success in Unmarshal for non-matching version") } // error in Unmarshal call is raised appropriately u.Rfc3161.APIVersion = swag.String("2.2.0") u2 := UnmarshalFailsTester{} _ = VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry) if _, err := brt.UnmarshalEntry(&u.Rfc3161); err == nil { t.Error("unexpected success in Unmarshal when error is thrown") } // version requested fails to match implementation in map u.Rfc3161.APIVersion = swag.String("not_a_version") if _, err := brt.UnmarshalEntry(&u.Rfc3161); err == nil { t.Error("unexpected success in Unmarshal for invalid version") } } rekor-1.3.5/pkg/types/rfc3161/tests/000077500000000000000000000000001455727245600170375ustar00rootroot00000000000000rekor-1.3.5/pkg/types/rfc3161/tests/test.tsr000066400000000000000000000036251455727245600205560ustar00rootroot00000000000000000 *H y0u10  `He0 *H  0*010  `He ='9gNTG[Zp! >fb& ac20210527180054Z D kx/gbvt0r1 0 UUS1 0U10U San Francisco10U Golden Gate Bridge10 U9401610U  Rekor Testd0B0z0 *H=0t1 0 UUS1 0U10U San Francisco10U Golden Gate Bridge10 U9401610U  Root CA Test0 210527162211Z 310527162211Z0r1 0 UUS1 0U10U San Francisco10U Golden Gate Bridge10 U9401610U  Rekor Test0Y0*H=*H=BZH5h- ūh xk0i0U@0 U00U0!U00U% 0 +0 *H=I0F!T< "ݡ/Vq!k̃V& DEPW000 *H=0t1 0 UUS1 0U10U San Francisco10U Golden Gate Bridge10 U9401610U  Root CA Test0 210527162211Z 310527162211Z0t1 0 UUS1 0U10U San Francisco10U Golden Gate Bridge10 U9401610U  Root CA Test0Y0*H=*H=B,)SZ p0{ (10 * 1024) { return fmt.Errorf("tsr exceeds maximum allowed size (10kB)") } var tsr pkcs9.TimeStampResp _, err = asn1.Unmarshal(b, &tsr) if err != nil { return err } if tsr.Status.Status != pkcs9.StatusGranted && tsr.Status.Status != pkcs9.StatusGrantedWithMods { return fmt.Errorf("tsr status not granted: %v", tsr.Status.Status) } if !tsr.TimeStampToken.ContentType.Equal(asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}) { return fmt.Errorf("tsr wrong content type: %v", tsr.TimeStampToken.ContentType) } _, err = tsr.TimeStampToken.Content.Verify(nil, false) if err != nil { return fmt.Errorf("tsr verification error: %v", err) } return nil } func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) { returnVal := models.Rfc3161{} var err error artifactBytes := props.ArtifactBytes if artifactBytes == nil { if props.ArtifactPath == nil { return nil, errors.New("path to artifact file must be specified") } if props.ArtifactPath.IsAbs() { return nil, errors.New("RFC3161 timestamps cannot be fetched over HTTP(S)") } artifactBytes, err = os.ReadFile(filepath.Clean(props.ArtifactPath.Path)) if err != nil { return nil, fmt.Errorf("error reading artifact file: %w", err) } } b64 := strfmt.Base64(artifactBytes) re := V001Entry{ Rfc3161Obj: models.Rfc3161V001Schema{ Tsr: &models.Rfc3161V001SchemaTsr{ Content: &b64, }, }, } returnVal.Spec = re.Rfc3161Obj returnVal.APIVersion = swag.String(re.APIVersion()) return &returnVal, nil } func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { return nil, errors.New("Verifiers() does not support rfc3161 entry type") } func (v V001Entry) ArtifactHash() (string, error) { if v.Rfc3161Obj.Tsr == nil || v.Rfc3161Obj.Tsr.Content == nil { return "", errors.New("rfc3161 v0.0.1 entry not initialized") } tsrDecoded, err := base64.StdEncoding.DecodeString(v.Rfc3161Obj.Tsr.Content.String()) if err != nil { return "", err } h := sha256.Sum256(tsrDecoded) return strings.ToLower(fmt.Sprintf("sha256:%s", hex.EncodeToString(h[:]))), nil } func (v V001Entry) Insertable() (bool, error) { if v.Rfc3161Obj.Tsr == nil { return false, errors.New("missing tsr property") } if v.Rfc3161Obj.Tsr.Content == nil || len(*v.Rfc3161Obj.Tsr.Content) == 0 { return false, errors.New("missing tsr content") } if v.tsrContent == nil || len(*v.tsrContent) == 0 { return false, errors.New("timestamp response has not been parsed") } return true, nil } rekor-1.3.5/pkg/types/rfc3161/v0.0.1/entry_test.go000066400000000000000000000212721455727245600212520ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rfc3161 import ( "bytes" "context" "encoding/asn1" "errors" "net/http" "net/http/httptest" "os" "reflect" "strings" "testing" "github.com/sassoftware/relic/lib/pkcs7" "github.com/sassoftware/relic/lib/pkcs9" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "go.uber.org/goleak" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestNewEntryReturnType(t *testing.T) { entry := NewEntry() if reflect.TypeOf(entry) != reflect.ValueOf(&V001Entry{}).Type() { t.Errorf("invalid type returned from NewEntry: %T", entry) } } func TestCrossFieldValidation(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectUnmarshalSuccess bool expectCanonicalizeSuccess bool expectValidationErrorMessage string } tsrBytes, _ := os.ReadFile("../tests/test.tsr") testServer := httptest.NewServer(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { var file *[]byte var err error switch r.URL.Path { case "/data": file = &tsrBytes default: err = errors.New("unknown URL") } if err != nil { w.WriteHeader(http.StatusNotFound) return } w.WriteHeader(http.StatusOK) _, _ = w.Write(*file) })) defer testServer.Close() testCases := []TestCase{ { caseDesc: "empty obj", entry: V001Entry{}, expectUnmarshalSuccess: false, expectCanonicalizeSuccess: false, expectValidationErrorMessage: "validation failure", }, { caseDesc: "valid obj", entry: V001Entry{ Rfc3161Obj: models.Rfc3161V001Schema{ Tsr: &models.Rfc3161V001SchemaTsr{ Content: p(tsrBytes), }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: true, }, { caseDesc: "invalid obj - too big", entry: V001Entry{ Rfc3161Obj: models.Rfc3161V001Schema{ Tsr: &models.Rfc3161V001SchemaTsr{ Content: p(tTooBig()), }, }, }, expectUnmarshalSuccess: false, expectCanonicalizeSuccess: false, expectValidationErrorMessage: "tsr exceeds maximum allowed size (10kB)", }, { caseDesc: "invalid obj - bad status", entry: V001Entry{ Rfc3161Obj: models.Rfc3161V001Schema{ Tsr: &models.Rfc3161V001SchemaTsr{ Content: p(tBadStatus(t, tsrBytes)), }, }, }, expectUnmarshalSuccess: false, expectCanonicalizeSuccess: false, expectValidationErrorMessage: "tsr status not granted: 2", }, { caseDesc: "invalid obj - bad content type", entry: V001Entry{ Rfc3161Obj: models.Rfc3161V001Schema{ Tsr: &models.Rfc3161V001SchemaTsr{ Content: p(tBadContentType(t, tsrBytes)), }, }, }, expectUnmarshalSuccess: false, expectCanonicalizeSuccess: false, expectValidationErrorMessage: "tsr wrong content type: 0.0.0.0.42", }, { caseDesc: "invalid obj - bad content", entry: V001Entry{ Rfc3161Obj: models.Rfc3161V001Schema{ Tsr: &models.Rfc3161V001SchemaTsr{ Content: p(tBadContent(t, tsrBytes)), }, }, }, expectUnmarshalSuccess: false, expectCanonicalizeSuccess: false, expectValidationErrorMessage: "tsr verification error", }, } for _, tc := range testCases { v := &V001Entry{} ts := models.Rfc3161{ APIVersion: swag.String(tc.entry.APIVersion()), Spec: tc.entry.Rfc3161Obj, } if err := v.Unmarshal(&ts); (err == nil) != tc.expectUnmarshalSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } else if err != nil { if !strings.HasPrefix(err.Error(), tc.expectValidationErrorMessage) { t.Errorf("unexpected error message from Validate for '%v': %v", tc.caseDesc, err) } } if tc.expectUnmarshalSuccess { if ok, err := v.Insertable(); !ok || err != nil { t.Errorf("unexpected error calling Insertable on valid proposed entry: %v", err) } } b, err := v.Canonicalize(context.TODO()) if (err == nil) != tc.expectCanonicalizeSuccess { t.Errorf("unexpected result from Canonicalize for '%v': %v", tc.caseDesc, err) } else if err != nil { if _, ok := err.(types.ValidationError); !ok { t.Errorf("canonicalize returned an unexpected error that isn't of type types.ValidationError: %v", err) } } if b != nil { pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tc.caseDesc, err) } ei, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tc.caseDesc, err) } // rfc3161 is one of two types that Insertable() should return true from canonicalized entries if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("unexpected error calling insertable on entry created from canonicalized content") } hash, err := ei.ArtifactHash() if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != "sha256:6dbda791c778454d0baa96e1dec5fa2c50867fd22d119ebe75c60c513b254c91" { t.Errorf("unexpected match with ArtifactHash: %s", hash) } } } } func tTooBig() []byte { lotsOfBytes := make([]byte, 10*1024+1) for i := 0; i < len(lotsOfBytes); i++ { lotsOfBytes[i] = 1 } return lotsOfBytes } func tBadStatus(t *testing.T, bytes []byte) []byte { var tsr pkcs9.TimeStampResp if _, err := asn1.Unmarshal(bytes, &tsr); err != nil { t.Fatal(err) } tsr.Status.Status = pkcs9.StatusRejection if b, err := asn1.Marshal(tsr); err != nil { t.Fatal(err) } else { return b } return nil } func tBadContentType(t *testing.T, bytes []byte) []byte { var tsr pkcs9.TimeStampResp if _, err := asn1.Unmarshal(bytes, &tsr); err != nil { t.Fatal(err) } tsr.TimeStampToken.ContentType = asn1.ObjectIdentifier{0, 0, 0, 0, 42} if b, err := asn1.Marshal(tsr); err != nil { t.Fatal(err) } else { return b } return nil } func tBadContent(t *testing.T, bytes []byte) []byte { var tsr pkcs9.TimeStampResp if _, err := asn1.Unmarshal(bytes, &tsr); err != nil { t.Fatal(err) } tsr.TimeStampToken.Content.Certificates = pkcs7.RawCertificates{} if b, err := asn1.Marshal(tsr); err != nil { t.Fatal(err) } else { return b } return nil } func p(b []byte) *strfmt.Base64 { b64 := strfmt.Base64(b) return &b64 } func TestInsertable(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectSuccess bool } tsr := strfmt.Base64([]byte("tsr")) testCases := []TestCase{ { caseDesc: "valid entry", entry: V001Entry{ Rfc3161Obj: models.Rfc3161V001Schema{ Tsr: &models.Rfc3161V001SchemaTsr{ Content: &tsr, }, }, tsrContent: &tsr, }, expectSuccess: true, }, { caseDesc: "unparsed tsr", entry: V001Entry{ Rfc3161Obj: models.Rfc3161V001Schema{ Tsr: &models.Rfc3161V001SchemaTsr{ Content: &tsr, }, }, //tsrContent: &tsr, }, expectSuccess: false, }, { caseDesc: "missing tsr content", entry: V001Entry{ Rfc3161Obj: models.Rfc3161V001Schema{ Tsr: &models.Rfc3161V001SchemaTsr{ //Content: &tsr, }, }, tsrContent: &tsr, }, expectSuccess: false, }, { caseDesc: "missing tsr obj", entry: V001Entry{ Rfc3161Obj: models.Rfc3161V001Schema{ /* Tsr: &models.Rfc3161V001SchemaTsr{ Content: &tsr, }, */ }, tsrContent: &tsr, }, expectSuccess: false, }, { caseDesc: "missing Rfc3161 obj", entry: V001Entry{ /* Rfc3161Obj: models.Rfc3161V001Schema{ Tsr: &models.Rfc3161V001SchemaTsr{ Content: &tsr, }, }, */ tsrContent: &tsr, }, expectSuccess: false, }, { caseDesc: "empty obj", entry: V001Entry{}, expectSuccess: false, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess { t.Errorf("unexpected result calling Insertable: %v", err) } }) } } rekor-1.3.5/pkg/types/rfc3161/v0.0.1/fuzz_test.go000066400000000000000000000047101455727245600211050ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rfc3161 import ( "context" "sync" "testing" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/go-openapi/swag" fuzzUtils "github.com/sigstore/rekor/pkg/fuzz" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/rfc3161" ) var initter sync.Once func FuzzRfc3161CreateProposedEntry(f *testing.F) { f.Fuzz(func(t *testing.T, propsData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) version := "0.0.1" ff := fuzz.NewConsumer(propsData) props, cleanup, err := fuzzUtils.CreateProps(ff, "rfc3161V001") if err != nil { t.Skip() } defer func() { for _, c := range cleanup { c() } }() it := rfc3161.New() entry, err := it.CreateProposedEntry(context.Background(), version, props) if err != nil { t.Skip() } ei, err := types.CreateVersionedEntry(entry) if err != nil { t.Skip() } if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err) } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("valid insertable entry should be able to be canonicalized: %v", err) } _, _ = ei.IndexKeys() }) } func FuzzRfc3161UnmarshalAndCanonicalize(f *testing.F) { f.Fuzz(func(t *testing.T, entryData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(entryData) targetV001 := &models.Rfc3161V001Schema{} if err := ff.GenerateStruct(targetV001); err != nil { t.Skip() } targetEntry := &models.Rfc3161{ APIVersion: swag.String(APIVERSION), Spec: targetV001, } ei, err := types.UnmarshalEntry(targetEntry) if err != nil { t.Skip() } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("error canonicalizing unmarshalled entry: %v", err) } }) } rekor-1.3.5/pkg/types/rfc3161/v0.0.1/rfc3161_v0_0_1_schema.json000066400000000000000000000013631455727245600230660ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/timestamp/timestamp_v0_0_1_schema.json", "title": "Timestamp v0.0.1 Schema", "description": "Schema for RFC3161 entries", "type": "object", "properties": { "tsr": { "description": "Information about the tsr file associated with the entry", "type": "object", "properties" : { "content": { "description": "Specifies the tsr file content inline within the document", "type": "string", "format": "byte" } }, "required": [ "content" ] } }, "required": [ "tsr" ] } rekor-1.3.5/pkg/types/rpm/000077500000000000000000000000001455727245600154065ustar00rootroot00000000000000rekor-1.3.5/pkg/types/rpm/e2e.go000066400000000000000000000026751455727245600164220ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package rpm import ( "bytes" "github.com/sigstore/rekor/pkg/util" "io/ioutil" "os" "testing" "github.com/google/rpmpack" ) func CreateSignedRpm(t *testing.T, artifactPath string) { t.Helper() rpmMetadata := rpmpack.RPMMetaData{ Name: "test-rpm-" + util.RandomSuffix(16), Epoch: 0, Version: "1", Release: "2", Arch: "x86_64", } rpm, err := rpmpack.NewRPM(rpmMetadata) if err != nil { t.Error(err) } rpm.SetPGPSigner(util.SignPGP) data := util.RandomData(t, 100) rpm.AddFile(rpmpack.RPMFile{ Name: util.RandomSuffix(16), Body: data, Type: rpmpack.GenericFile, Owner: "testOwner", Group: "testGroup", }) rpmBuf := bytes.Buffer{} if err := rpm.Write(&rpmBuf); err != nil { t.Fatal(err) } if err := ioutil.WriteFile(artifactPath, rpmBuf.Bytes(), os.ModePerm); err != nil { t.Fatal(err) } } rekor-1.3.5/pkg/types/rpm/e2e_test.go000066400000000000000000000076731455727245600174640ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package rpm import ( "github.com/sigstore/rekor/pkg/util" "io/ioutil" "path/filepath" "testing" ) var publicKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- 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 WR6Fv7ySHTl7cQ13jGmSR1N8hg== =Fen+ -----END PGP PUBLIC KEY BLOCK-----` func TestUploadVerifyRpm(t *testing.T) { // Create a random rpm and sign it. td := t.TempDir() rpmPath := filepath.Join(td, "rpm") CreateSignedRpm(t, rpmPath) // Write the public key to a file pubPath := filepath.Join(t.TempDir(), "pubKey.asc") if err := ioutil.WriteFile(pubPath, []byte(publicKey), 0644); err != nil { t.Fatal(err) } // Verify should fail initially util.RunCliErr(t, "verify", "--type=rpm", "--artifact", rpmPath, "--public-key", pubPath) // It should upload successfully. out := util.RunCli(t, "upload", "--type=rpm", "--artifact", rpmPath, "--public-key", pubPath) util.OutputContains(t, out, "Created entry at") // Now we should be able to verify it. out = util.RunCli(t, "verify", "--type=rpm", "--artifact", rpmPath, "--public-key", pubPath) util.OutputContains(t, out, "Inclusion Proof:") } rekor-1.3.5/pkg/types/rpm/rpm.go000066400000000000000000000034331455727245600165360ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rpm import ( "context" "errors" "fmt" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" ) const ( KIND = "rpm" ) type BaseRPMType struct { types.RekorType } func init() { types.TypeMap.Store(KIND, New) } func New() types.TypeImpl { brt := BaseRPMType{} brt.Kind = KIND brt.VersionMap = VersionMap return &brt } var VersionMap = types.NewSemVerEntryFactoryMap() func (brt *BaseRPMType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) { if pe == nil { return nil, errors.New("proposed entry cannot be nil") } rpm, ok := pe.(*models.Rpm) if !ok { return nil, errors.New("cannot unmarshal non-RPM types") } return brt.VersionedUnmarshal(rpm, *rpm.APIVersion) } func (brt *BaseRPMType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) { if version == "" { version = brt.DefaultVersion() } ei, err := brt.VersionedUnmarshal(nil, version) if err != nil { return nil, fmt.Errorf("fetching RPM version implementation: %w", err) } return ei.CreateFromArtifactProperties(ctx, props) } func (brt BaseRPMType) DefaultVersion() string { return "0.0.1" } rekor-1.3.5/pkg/types/rpm/rpm_schema.json000066400000000000000000000004731455727245600204230ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/rpm/rpm_schema.json", "title": "RPM Schema", "description": "Schema for RPM objects", "type": "object", "oneOf": [ { "$ref": "v0.0.1/rpm_v0_0_1_schema.json" } ] } rekor-1.3.5/pkg/types/rpm/rpm_test.go000066400000000000000000000054721455727245600176020ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rpm import ( "errors" "testing" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" ) type UnmarshalTester struct { models.Rpm types.BaseUnmarshalTester } type UnmarshalFailsTester struct { types.BaseUnmarshalTester } func (u UnmarshalFailsTester) NewEntry() types.EntryImpl { return &UnmarshalFailsTester{} } func (u UnmarshalFailsTester) Unmarshal(_ models.ProposedEntry) error { return errors.New("error") } func (u UnmarshalFailsTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } func TestRPMType(t *testing.T) { // empty to start if VersionMap.Count() != 0 { t.Error("semver range was not blank at start of test") } u := UnmarshalTester{} // ensure semver range parser is working invalidSemVerRange := "not a valid semver range" err := VersionMap.SetEntryFactory(invalidSemVerRange, u.NewEntry) if err == nil || VersionMap.Count() > 0 { t.Error("invalid semver range was incorrectly added to SemVerToFacFnMap") } // valid semver range can be parsed err = VersionMap.SetEntryFactory(">= 1.2.3", u.NewEntry) if err != nil || VersionMap.Count() != 1 { t.Error("valid semver range was not added to SemVerToFacFnMap") } u.Rpm.APIVersion = swag.String("2.0.1") brt := New() // version requested matches implementation in map if _, err := brt.UnmarshalEntry(&u.Rpm); err != nil { t.Errorf("unexpected error in Unmarshal: %v", err) } // version requested fails to match implementation in map u.Rpm.APIVersion = swag.String("1.2.2") if _, err := brt.UnmarshalEntry(&u.Rpm); err == nil { t.Error("unexpected success in Unmarshal for non-matching version") } // error in Unmarshal call is raised appropriately u.Rpm.APIVersion = swag.String("2.2.0") u2 := UnmarshalFailsTester{} _ = VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry) if _, err := brt.UnmarshalEntry(&u.Rpm); err == nil { t.Error("unexpected success in Unmarshal when error is thrown") } // version requested fails to match implementation in map u.Rpm.APIVersion = swag.String("not_a_version") if _, err := brt.UnmarshalEntry(&u.Rpm); err == nil { t.Error("unexpected success in Unmarshal for invalid version") } } rekor-1.3.5/pkg/types/rpm/tests/000077500000000000000000000000001455727245600165505ustar00rootroot00000000000000rekor-1.3.5/pkg/types/rpm/tests/test.rpm000066400000000000000000005057501455727245600202630ustar00rootroot00000000000000NetworkManager-bluetooth-1:1.26.0-8.el8 >  A _U]q3Q8D GXŋ#׳( ~ a(yy|)DZ)x/hz\fTTqNM%˃p1m9sLj9=p:d>l_206%sHO$gǻ!S({:xn=‰ddb^JIĎkǶ(U'Mу=FTK6k,7=⛣7oVޢ5j߯NͦenVm 6 MDxhCb]l i1x( гUW |0& 3-5Y #& n!RU,3!*Ib)=pdLwbf <8@/ʁG^fUTzF&qB:vHh&}11 m|R f7fd4b5809a81d6bb7d009dcbb1b49c5d37e912c79773482caec9c2f5a9a80ce1a7b22fa8fb5b87db6c31f6d1bd8712fcb57ac8fv_U]M$/P h~]`Z` Vmtiy,IB5A,Mά"X YŁ- X ,Dc49_vr[IV?z2y ,Yi^F2#p\@i{m;v!DL/sϢO;gf+"A#r7F:F߶M_x̥ `])xC2}Z*\(.'d.Y*dќhzFx*vJV$қg "Ah ՄA>jDkw2ЊѬ4_3j0:%TblZ ΪmBؿ;p6wofA߁ʰ@Q< 7>,2#9m8%TV4B+'n㐎 g}֊D Z $ x k9%˲fӱW oJ&-r# ^gd?t盝kX[Zh>Jp X>p@h?Xd"( , W EKTd l t    0DX|X(188'9':$'B"GDHTIdXhYtZԔ[Ԝ\Ԭ]Լ^bnd־efltuvwטxרy׸ TCNetworkManager-bluetooth1.26.08.el8Bluetooth device plugin for NetworkManagerThis package contains NetworkManager support for Bluetooth devices._x86-02.mbox.centos.orgCentOSCentOSGPLv2+ and LGPLv2+CentOS Buildsys System Environment/Basehttp://www.gnome.org/projects/NetworkManager/linuxx86_64RAA____5fd94a8d397f9ca8c4df09d88216d94f192fefad28fa90f058e0fb126d3ea15c../../../../usr/lib64/NetworkManager/1.26.0-8.el8/libnm-device-plugin-bluetooth.sorootrootrootrootrootrootrootrootNetworkManager-1.26.0-8.el8.src.rpmNetworkManager-bluetoothNetworkManager-bluetooth(x86-64)libnm-device-plugin-bluetooth.so()(64bit) @@@@@@@@@@@@@@@@@@    @NetworkManager(x86-64)NetworkManager-wwanbluezlibbluetooth.so.3()(64bit)libc.so.6()(64bit)libc.so.6(GLIBC_2.14)(64bit)libc.so.6(GLIBC_2.2.5)(64bit)libc.so.6(GLIBC_2.4)(64bit)libdl.so.2()(64bit)libgcc_s.so.1()(64bit)libgcc_s.so.1(GCC_3.0)(64bit)libgcc_s.so.1(GCC_3.3.1)(64bit)libgio-2.0.so.0()(64bit)libglib-2.0.so.0()(64bit)libgmodule-2.0.so.0()(64bit)libgobject-2.0.so.0()(64bit)libmm-glib.so.0()(64bit)libnm-wwan.so()(64bit)libpthread.so.0()(64bit)libpthread.so.0(GLIBC_2.2.5)(64bit)libsystemd.so.0()(64bit)rpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rpmlib(PayloadIsXz)rtld(GNU_HASH)1:1.26.0-8.el81:1.26.0-8.el84.101-53.0.4-14.6.0-14.0-15.2-14.14.2_l@_N7_:q@_5+@_2@_+__ L_@^@^b^@^I^b;@^S^M#@^K^4^g@^C]@]@]@]@]?]e@]UI@]Ik]:@] u@]@\\]o@\73\5@\4\@\I\U@[[@[[F[[[[s[qr[Y[(@[ZZZZ3@Z@YYdYY5Y˒YX@YYx@YqYp@YlYK@Y:Y3@Y"Y /YXX-X @X@X@X@XX~@XwoXwoXRXF@X8'X5X@WW@WRWu@W@WhW@W@WPW3Wo@W@WVVn@V3V@VՄ@VVV%@VVD@V @V @UU6@UM@UU@ŬUUUU@U@U@UUUUHU@TE@TE@TE@TT TTTTsT[bTPTPTJ?@THT?@T2@T TSS@S@S@SR@S@SS@S@SG@S)S&S"@S@Sz@S@R3@R@R@R&RʚRR@Rv@R@R@R@RiR|@RM\@RJ@R<8R2@R)R6Q@QQQQɆ@Q@QxQ'@QQR@Q-@Q:@QP@PZP7@P7@P@P@P{P{Pp@PnPmz@P3x@PP - 1:1.26.0-8Antonio Cardace - 1:1.26.0-7Thomas Haller - 1:1.26.0-6Thomas Haller - 1:1.26.0-5Antonio Cardace - 1:1.26.0-4Thomas Haller - 1:1.26.0-3Antonio Cardace - 1:1.26.0-2Thomas Haller - 1:1.26.0-1Beniamino Galvani - 1:1.26.0-0.2.1Beniamino Galvani - 1:1.26.0-0.2Thomas Haller - 1:1.26.0-0.1Beniamino Galvani - 1:1.25.2-1Thomas Haller - 1:1.25.1-1Thomas Haller - 1:1.22.8-4Beniamino Galvani - 1:1.22.8-3Thomas Haller - 1:1.22.8-2Antonio Cardace - 1:1.22.8-1Antonio Cardace - 1:1.22.6-1Beniamino Galvani - 1:1.22.4-1Thomas Haller - 1:1.22.2-1Thomas Haller - 1:1.22.0-2Thomas Haller - 1:1.22.0-1Thomas Haller - 1:1.22.0-0.2Beniamino Galvani - 1:1.22.0-0.1Lubomir Rintel - 1:1.20.0-4Thomas Haller - 1:1.20.0-3Lubomir Rintel - 1:1.20.0-2Thomas Haller - 1:1.20.0-1Thomas Haller - 1:1.20.0-0.4Lubomir Rintel - 1:1.20.0-0.3Lubomir Rintel - 1:1.20.0-0.2Lubomir Rintel - 1:1.20.0-0.1Beniamino Galvani - 1:1.14.0-14Francesco Giudici - 1:1.14.0-13Thomas Haller - 1:1.14.0-12Beniamino Galvani - 1:1.14.0-11Beniamino Galvani - 1:1.14.0-10Thomas Haller - 1:1.14.0-9Lubomir Rintel - 1:1.14.0-8Thomas Haller - 1:1.14.0-7Thomas Haller - 1:1.14.0-6Lubomir Rintel - 1:1.14.0-5Beniamino Galvani - 1:1.14.0-4Thomas Haller - 1:1.14.0-3Thomas Haller - 1:1.14.0-2Thomas Haller - 1:1.14.0-1Thomas Haller - 1:1.14.0-0.4Thomas Haller - 1:1.14.0-0.3Lubomir Rintel - 1:1.14.0-0.2Thomas Haller - 1:1.14.0-0.1Lubomir Rintel - 1:1.12.0-0.4Thomas Haller - 1:1.12.0-0.3Thomas Haller - 1:1.12.0-0.2Thomas Haller - 1:1.12.0-0.1Thomas Haller - 1:1.10.2-1Björn Esser - 1:1.8.4-7Thomas Haller - 1:1.8.4-6Lubomir Rintel - 1:1.8.4-5Thomas Haller - 1:1.8.4-4Thomas Haller - 1:1.8.4-3Thomas Haller - 1:1.8.4-2Thomas Haller - 1:1.8.4-1Fedora Release Engineering - 1:1.8.2-3.2Fedora Release Engineering - 1:1.8.2-3.1Lubomir Rintel - 1:1.8.2-3Stephen Gallagher - 1:1.8.2-2Beniamino Galvani - 1:1.8.2-1Lubomir Rintel - 1:1.8.0-6Lubomir Rintel - 1:1.8.0-5Thomas Haller - 1:1.8.0-4Thomas Haller - 1:1.8.0-3Thomas Haller - 1:1.8.0-2Thomas Haller - 1:1.8.0-1Lubomir Rintel - 1:1.8.0-0.2.rc3Lubomir Rintel - 1:1.8.0-0.2.rc2Lubomir Rintel - 1:1.8.0-0.1Lubomir Rintel - 1:1.6.2-1Fedora Release Engineering - 1:1.6.0-1.1Lubomir Rintel - 1:1.6.0-1Thomas Haller - 1:1.6-0.2.rc1Lubomir Rintel - 1:1.6-0.1.rc1Thomas Haller - 1:1.5.3-5Igor Gnatenko - 1:1.5.3-4.1Lubomir Rintel - 1:1.5.3-4Thomas Haller - 1:1.5.2-4Thomas Haller - 1:1.5.2-3Thomas Haller - 1:1.5.2-2Lubomir Rintel - 1:1.5.2-1Lubomir Rintel - 1:1.4.2-1Thomas Haller - 1:1.4.0-4Thoams Haller - 1:1.4.0-3Thomas Haller - 1:1.4.0-2Lubomir Rintel - 1:1.4.0-1Thomas Haller - 1:1.4.0-0.5.git20160621.072358daMatthias Clasen - 1:1.4.0-0.4.git20160621.072358daLubomir Rintel - 1:1.4.0-0.3.git20160621.072358daThomas Haller - 1:1.2.2-2Lubomir Rintel - 1:1.2.2-1Lubomir Rintel - 1:1.2.0-1Lubomir Rintel - 1:1.2.0-0.7.rc2Lubomir Rintel - 1:1.2.0-0.7.rc1Lubomir Rintel - 1:1.2.0-0.8.beta3Lubomir Rintel - 1:1.2.0-0.7.beta3Lubomir Rintel - 1:1.2.0-0.7.beta2Dan Williams - 1:1.2.0-0.6.beta2.1Lubomir Rintel - 1:1.2.0-0.6.beta2Thomas Haller - 1:1.2.0-0.6.beta1Fedora Release Engineering - 1:1.2.0-0.5.beta1.1Lubomir Rintel - 1:1.2.0-0.5.beta1David King - 1:1.2.0-0.4.20151007gite73e55cLubomir Rintel - 1:1.2.0-0.3.20151112gitec4d653Lubomir Rintel - 1:1.2.0-0.3.20151023gite01c175Lubomir Rintel - 1:1.2.0-0.2.20151007gite73e55cLubomir Rintel - 1:1.2.0-0.2.20150903gitde5d981Lubomir Rintel - 1:1.2.0-0.1.20150903gitde5d981Lubomir Rintel - 1:1.0.6-2Lubomir Rintel - 1:1.0.6-1Thomas Haller - 1:1.0.6-0.2.20150813git7e2caa2Lubomir Rintel - 1:1.0.6-0.1.20150813git7e2caa2Lubomir Rintel - 1:1.0.4-2Lubomir Rintel - 1:1.0.4-1Dan Horák - 1:1.0.4-0.5.git20150713.38bf2cb0Lubomir Rintel - 1:1.0.4-0.4.git20150713.38bf2cb0Lubomir Rintel - 1:1.0.4-0.3.git20150707.e3bd4e1Jiří Klimeš - 1:1.0.4-0.2.git20150707.cf15f2aLubomir Rintel - 1:1.0.4-0.1.git20160624.f245b49aLubomir Rintel - 1:1.0.4-0.1.git20150618.8cffaf3bf5Fedora Release Engineering - 1:1.0.2-1.1Lubomir Rintel - 1:1.0.2-1Jiří Klimeš - 1:1.0.1-2.git20150429Dan Williams - 1:1.0.1-1.git20150305Dan Williams - 1:1.0.0-7Dan Williams - 1:1.0.0-6Dan Williams - 1:1.0.0-5Adam Williamson - 1:1.0.0-4Thomas Haller - 1:1.0.0-3Dan Winship - 1:1.0.0-2Dan Williams - 1:1.0.0-1Jiří Klimeš - 1:0.9.10.0-14.git20140704Jiří Klimeš - 1:0.9.10.0-13.git20140704Dan Winship - 1:0.9.10.0-12.git20140704Lubomir Rintel 1:0.9.10.0-11.git20140704Lubomir Rintel 1:0.9.10.0-10.git20140704Adam Williamson - 1:0.9.10.0-9.git20140704Lubomir Rintel 1:0.9.10.0-8.git20140704Stef Walter - 1:0.9.10.0-7.git20140704Jiří Klimeš - 1:0.9.10.0-6.git20140704Peter Robinson 1:0.9.10.0-5.git20140704Dan Horák - 1:0.9.10.0-4.git20140704Fedora Release Engineering - 1:0.9.10.0-3.git20140704.1Kalev Lember - 1:0.9.10.0-3.git20140704Dan Williams - 1:0.9.10.0-2.git20140704Kalev Lember - 1:0.9.10.0-1.git20140704.1Thomas Haller - 0.9.10.0-1.git20140704Thomas Haller - 0.9.9.98-1.git20140620Dan Williams - 0.9.9.95-1.git20140609Fedora Release Engineering - 1:0.9.9.1-6.git20140319Dan Williams - 0.9.9.1-5.git20140319Dan Winship - 0.9.9.1-4.git20140319Jiří Klimeš - 0.9.9.1-3.git20140317Jiří Klimeš - 0.9.9.1-2.git20140314Jiří Klimeš - 0.9.9.1-1.git20140310Thomas Haller - 0.9.9.1-0.git20140228Thomas Haller - 0.9.9.0-28.git20140131Thomas Haller - 0.9.9.0-27.git20140131Jiří Klimeš - 0.9.9.0-26.git20140131Jiří Klimeš - 0.9.9.0-25.git20140117Jiří Klimeš - 0.9.9.0-24.git20140114Dan Winship - 0.9.9.0-23.git20131003Dan Williams - 0.9.9.0-22.git20131003Dan Williams - 0.9.9.0-21.git20131003Dan Winship - 0.9.9.0-20.git20131003Jiří Klimeš - 0.9.9.0-19.git20131003Dan Winship - 0.9.9.0-18.git20131003Dan Williams - 0.9.9.0-17.git20131003Dan Williams - 0.9.9.0-16.git20131003Jiří Klimeš - 0.9.9.0-15.git20131003Dan Williams - 0.9.9.0-14.git20131003Dan Winship - 0.9.9.0-13.git20131001Bill Nottingham - 0.9.9.0-12.git20130913Dan Williams - 0.9.9.0-11.git20130913Dan Williams - 0.9.9.0-10.git20130906Dan Williams - 0.9.9.0-9.git20130807Dan Winship - 0.9.9.0-8.git20130724Dan Williams - 0.9.9.0-7.git20130724Dan Winship - 0.9.9.0-6Dan Winship - 0.9.9.0-5Jiří Klimeš - 0.9.9.0-4.git20130603Dan Williams - 0.9.9.0-3.git20130603Dan Williams - 0.9.9.0-2.git20130515Dan Williams - 0.9.9.0-1.git20130514Dan Williams - 0.9.8.1-2.git20130507Dan Williams - 0.9.8.1-1.git20130327Jiří Klimeš - 0.9.8.0-1Dan Williams - 0.9.7.997-2Dan Williams - 0.9.7.997-1Dan Winship - 0.9.7.0-12.git20121004Jiří Klimeš - 0.9.7.0-11.git20121004Dan Winship - 0.9.7.0-10.git20121004Dan Winship - 0.9.7.0-9.git20121004Jiří Klimeš - 0.9.7.0-8.git20121004Daniel Drake - 0.9.7.0-7.git20121004Dan Winship - 0.9.7.0-6.git20121004Dan Winship - 0.9.7.0-5.git20121004Dan Winship - 0.9.7.0-4.git20121004Dan Williams - 0.9.7.0-3.git20121004Dan Winship - 0.9.7.0-2.git20121004Dan Winship - 0.9.7.0-1.git20120820Fedora Release Engineering - 1:0.9.5.96-2Dan Williams - 0.9.5.96-1Jiří Klimeš - 0.9.5.95-1.git20120713Jiří Klimeš - 0.9.4-5.git20120521Dan Winship - 0.9.4-4.git20120502Jiří Klimeš - 0.9.4-3.git20120502Colin Walters - 1:0.9.4-2.git20120328_2Jiří Klimeš - 0.9.4-1.git20120328_2Dan Williams - 0.9.3.997-2Dan Williams - 0.9.3.997-1Dan Williams - 0.9.3.997-0.7Dan Williams - 0.9.3.995-0.6Dan Williams - 0.9.3.995-0.5Dan Williams - 0.9.3.995-0.4Dan Horák - 0.9.3-0.3Dan Williams - 0.9.3-0.2Dan Williams - 0.9.3-0.1Matthias Clasen - 0.9.2-4Fedora Release Engineering - 1:0.9.2-3Daniel Drake - 0.9.2-2Dan Williams - 0.9.2-1Adam Williamson - 1:0.9.1.90-5.git20110927Dan Williams - 0.9.1.90-3.git20110927Jiří Klimeš - 0.9.1.90-2.git20110927Dan Williams - 0.9.1.90-1Tom Callaway - 0.9.0-2Dan Williams - 0.9.0-1Ray Strode 0.8.9997-7.git20110721Dan Williams - 0.8.9997-6.git20110721Dan Williams - 0.8.9997-5.git20110702Dan Williams - 0.8.9997-4.git20110620Dan Williams - 0.8.9997-3.git20110613Dan Williams - 0.8.9997-2.git20110531Dan Williams - 0.8.9997-1.git20110531Dan Williams - 0.8.999-3.git20110526Dan Williams - 0.8.999-2.git20110509Dan Williams - 0.8.999-1Dan Williams - 0.8.998-4.git20110427Dan Williams - 0.8.998-3.git20110419Dan Williams - 0.8.998-2.git20110406Dan Williams - 0.8.998-1Dan Williams - 0.8.997-8.git20110331Dan Williams - 0.8.997-7.git20110330Christopher Aillon - 0.8.997-6.git20110328Dan Williams - 0.8.997-5.git20110328Dan Williams - 0.8.997-4.git20110325Dan Williams - 0.8.997-3.git20110324Dan Williams - 0.8.997-2.git20110324Dan Williams - 0.8.997-1Dan Williams - 0.8.996-1Dan Williams - 0.8.995-4.git20110308Dan Williams - 0.8.995-3.git20110308Matthias Clasen - 0.8.995-2.git20110308Dan Williams - 0.8.995-1.git20110308Matthias Clasen - 0.8.2-8.git20101117Fedora Release Engineering - 1:0.8.2-7.git20101117Matthias Clasen - 0.8.2-6.git20101117Dan Williams - 0.8.2-5.git20101117Matthias Clasen - 0.8.2-4.git20101117Dan Horák - 0.8.2-3.git20101117Matthias Clasen - 0.8.2-2.git20101117Dan Williams - 0.8.2-1.git20101117Matthias Clasen - 0.8.1-10.1Dan Williams - 0.8.1-10Dan Williams - 0.8.1-9Dan Williams - 0.8.1-8Dan Williams - 0.8.1-7Dan Williams - 0.8.1-6Dan Williams - 0.8.1-5Dan Williams - 0.8.1-4Dan Williams - 0.8.1-3Dan Williams - 0.8.1-2Dan Williams - 0.8.1-1Matthias Clasen - 0.8.1-0.5Dan Williams - 0.8.1-0.4Dan Williams - 0.8.1-0.3Dan Williams - 0.8.1-0.2.git20100519Dan Williams - 0.8.1-0.1.git20100510Dan Williams - 0.8-13.git20100509Dan Williams - 0.8-12.git20100504Dan Williams - 0.8-11.git20100503Dan Williams - 0.8-10.git20100502Dan Williams - 0.8-9.git20100429Dan Williams - 0.8-8.git20100426Dan Williams - 0.8-7.git20100422Dan Williams - 0.8-6.git20100408Dan Williams - 0.8-5.git20100408Dan Williams - 0.8-4.git20100325Dan Williams - 0.8-3.git20100323Dan Williams - 0.8-2.git20100317Dan Williams - 0.8-1.git20100219Dan Williams - 0.8-0.4.git20100211Kevin Kofler - 0.8-0.3.git20100129Dan Williams - 0.8-0.2.git20100129Dan Williams - 0.8-0.1.git20100122Dan Williams - 0.7.999-2.git20100120Dan Williams - 0.7.999-1.git20100120Dan Williams - 0.7.998-1.git20100106Dan Williams - 0.7.997-2.git20091214Dan Williams - 0.7.997-1Dan Williams - 0.7.996-7.git20091113Dan Williams - 0.7.996-6.git20091021Dan Williams - 0.7.996-5.git20091021Dan Williams - 0.7.996-4.git20091002Dan Williams - 0.7.996-3.git20090928Matthias Clasen - 0.7.996-3.git20090921Dan Williams - 0.7.996-2.git20090921Dan Williams - 0.7.996-1.git20090826Dan Williams - 0.7.995-3.git20090813Bastien Nocera 0.7.995-2.git20090804Dan Williams - 0.7.995-1.git20090804Matthias Clasen - 0.7.995-1.git20090728Dan Williams - 0.7.995-0.git20090728Fedora Release Engineering - 1:0.7.1-9.git20090708Dan Williams - 0.7.1-8.git20090708Dan Williams - 0.7.1-7.git20090708Dan Williams - 0.7.1-6.git20090617Dan Williams - 0.7.1-5.git20090617Karsten Hopp 0.7.1-4.git20090414.1Adam Jackson 1:0.7.1-4.git20090414Dan Williams - 1:0.7.1-3.git20090414Dan Williams - 1:0.7.1-2.git20090414Dan Williams - 1:0.7.1-1Dan Williams - 1:0.7.0.100-2.git20090408Dan Williams - 1:0.7.0.100-1Dan Williams - 1:0.7.0.99-5Dan Williams - 1:0.7.0.99-4Dan Williams - 1:0.7.0.99-3.5Dan Williams - 1:0.7.0.99-3Dan Williams - 1:0.7.0.99-2Dan Williams - 1:0.7.0.99-1Dan Williams - 1:0.7.0.98-1.git20090225Fedora Release Engineering - 1:0.7.0.97-6.git20090220Dan Williams - 1:0.7.0.97-5.git20090220Dan Williams - 1:0.7.0.97-4.git20090219Dan Williams - 1:0.7.0.97-2Dan Williams - 1:0.7.0.97-1Dan Williams - 1:0.7.0-2.git20090207Dan Williams - 1:0.7.0-1.git20090102Dan Williams - 1:0.7.0-0.12.svn4326Dan Williams - 1:0.7.0-0.12.svn4296Dan Williams - 1:0.7.0-0.12.svn4295Dan Williams - 1:0.7.0-0.12.svn4293Dan Williams - 1:0.7.0-0.11.svn4229Dan Williams - 1:0.7.0-0.11.svn4201Dan Williams - 1:0.7.0-0.11.svn4175Dan Williams - 1:0.7.0-0.11.svn4174Dan Williams - 1:0.7.0-0.11.svn4022.4Dan Williams - 1:0.7.0-0.11.svn4022.3Dan Williams - 1:0.7.0-0.11.svn4022.2Dan Williams - 1:0.7.0-0.11.svn4022.1Dan Williams - 1:0.7.0-0.11.svn4022Dan Williams - 1:0.7.0-0.11.svn3930Dan Williams - 1:0.7.0-0.11.svn3927Dan Williams - 1:0.7.0-0.11.svn3846Dan Williams - 1:0.7.0-0.11.svn3830Matthias Clasen - 1:0.7.0-0.10.svn3801Dan Williams - 1:0.7.0-0.10.svn3801Dan Williams - 1:0.7.0-0.10.svn3747Dan Williams - 1:0.7.0-0.9.4.svn3675Dan Williams - 1:0.7.0-0.9.3.svn3675Dan Williams - 1:0.7.0-0.9.3.svn3669Dan Williams - 1:0.7.0-0.9.3.svn3667Dan Williams - 1:0.7.0-0.9.3.svn3665Dan Williams - 1:0.7.0-0.9.3.svn3623Dan Williams - 1:0.7.0-0.9.2.svn3623Dan Williams - 1:0.7.0-0.9.2.svn3622Dan Williams - 1:0.7.0-0.9.2.svn3620Dan Williams - 1:0.7.0-0.9.2.svn3619Dan Williams - 1:0.7.0-0.9.2.svn3614Dan Williams - 1:0.7.0-0.9.2.svn3590Dan Williams - 1:0.7.0-0.9.2.svn3578Dan Williams - 1:0.7.0-0.9.2.svn3571Dan Williams - 1:0.7.0-0.9.2.svn3570Dan Williams - 1:0.7.0-0.9.2.svn3566Dan Williams - 1:0.7.0-0.9.1.svn3566Dan Williams - 1:0.7.0-0.9.1.svn3549Dan Williams - 1:0.7.0-0.9.1.svn3548Dan Williams - 1:0.7.0-0.9.1.svn3547Dan Williams - 1:0.7.0-0.9.1.svn3527Dan Williams - 1:0.7.0-0.9.1.svn3521Dan Williams - 1:0.7.0-0.9.1.svn3476Dan Williams - 1:0.7.0-0.9.1.svn3473Dan Williams - 1:0.7.0-0.9.1.svn3472Dan Williams - 1:0.7.0-0.9.1.svn3440Dan Williams - 1:0.7.0-0.9.1.svn3417Dan Williams - 1:0.7.0-0.8.svn3417Dan Williams - 1:0.7.0-0.8.svn3370Dan Williams - 1:0.7.0-0.8.svn3369Dan Williams - 1:0.7.0-0.8.svn3319Dan Williams - 1:0.7.0-0.8.svn3312Dan Williams - 1:0.7.0-0.8.svn3302Dan Williams - 1:0.7.0-0.8.svn3261Dan Williams - 1:0.7.0-0.8.svn3235Dan Williams - 1:0.7.0-0.8.svn3204Dan Williams - 1:0.7.0-0.8.svn3181Dan Williams - 1:0.7.0-0.8.svn3180Dan Williams - 1:0.7.0-0.8.svn3138Dan Williams - 1:0.7.0-0.8.svn3134Dan Williams - 1:0.7.0-0.8.svn3133Jeremy Katz - 1:0.7.0-0.8.svn3109Dan Williams - 1:0.7.0-0.6.6.svn3109Dan Williams - 1:0.7.0-0.6.5.svn3109Dan Williams - 1:0.7.0-0.6.5.svn3096Dan Williams - 1:0.7.0-0.6.4.svn3096Dan Williams - 1:0.7.0-0.6.3.svn3096Dan Williams - 1:0.7.0-0.6.3.svn3094Dan Williams - 1:0.7.0-0.6.2.svn3080Dan Williams - 1:0.7.0-0.6.1.svn3030Dan Williams - 1:0.7.0-0.5.svn3030Dan Williams - 1:0.7.0-0.4.svn3030Dan Williams - 1:0.7.0-0.4.svn3020Dan Williams - 1:0.7.0-0.3.svn3020Dan Williams - 1:0.7.0-0.3.svn3016Dan Williams - 1:0.7.0-0.3.svn3014Dan Williams - 1:0.7.0-0.3.svn3008Dan Williams - 1:0.7.0-0.3.svn2995Dan Williams - 1:0.7.0-0.3.svn2994Dan Williams - 1:0.7.0-0.3.svn2983Dan Williams - 1:0.7.0-0.3.svn2970Dan Williams - 1:0.7.0-0.3.svn2962Dan Williams - 1:0.7.0-0.3.svn2961Dan Williams - 1:0.7.0-0.3.svn2914Dan Williams - 1:0.7.0-0.3.svn2907Dan Williams - 1:0.7.0-0.3.svn2886Dan Williams - 1:0.7.0-0.3.svn2880Dan Williams - 1:0.7.0-0.3.svn2852Dan Williams - 1:0.7.0-0.3.svn2849Dan Williams - 1:0.7.0-0.3.svn2844Dan Williams - 1:0.7.0-0.2.svn2833Dan Williams - 1:0.7.0-0.1.svn2736Christopher Aillon 1:0.6.5-9Christopher Aillon 1:0.6.5-8Dan Williams 1:0.6.5-7Dan Williams 1:0.6.5-6Dan Williams 1:0.6.5-5Dan Williams 1:0.6.5-4Dan Williams 1:0.6.5-3Christopher Aillon 1:0.6.5-2Christopher Aillon 1:0.6.5-1Dan Williams - 1:0.6.5-0.7.svn2547Matthew Barnes 1:0.6.5-0.6.svn2474Matthias Clasen 1:0.6.5-0.5.svn2474Dan Williams - 1:0.6.5-0.4.svn2474Christopher Aillon - 1:0.6.5-0.3.cvs20061025Christopher Aillon - 1:0.6.5-0.2.cvs20061025Matthias Clasen Dan Williams - 1:0.6.5-0.cvs20061025Christopher Aillon - 1:0.6.4-6Christopher Aillon - 1:0.6.4-5Bill Nottingham - 1:0.6.4-4Dan Williams - 1:0.6.4-3Dan Williams - 1:0.6.4-2Christopher Aillon - 0.7.0-0.cvs20060529.7Ray Strode - 0.7.0-0.cvs20060529.6Ray Strode - 0.7.0-0.cvs20060529.5Ray Strode - 0.7.0-0.cvs20060529.4John (J5) Palmieri - 0.7.0-0.cvs20060529.3John (J5) Palmieri - 0.7.0-0.cvs20060529.2Jesse Keating - 0.7.0-0.cvs20060529.1.1Dan Williams - 0.7.0-0.cvs20060529Dan Williams - 0.7.0-0.cvs20060521Bill Nottingham - 0.6.2-3.fc6Jeremy Katz - 0.6.2-2.fc6Dan Williams - 0.6.2-1Peter Jones - 0.6.0-3Dan Williams 0.6.0-2Dan Williams 0.6.0-1Jeremy Katz - 0.5.1-18.cvs20060302Christopher Aillon Dan Williams 0.5.1-18.cvs20060301Christopher Aillon 0.5.1-17.cvs20060228Christopher Aillon 0.5.1-16.cvs20060227Christopher Aillon 0.5.1-15.cvs20060227Dan Williams 0.5.1-14.cvs20060221Dan Williams 0.5.1-13.cvs20060221Dan Williams 0.5.1-12.cvs20060213Christopher Aillon 0.5.1-11.cvs20060205Jesse Keating 0.5.1-10.cvs20060205.1Dan Williams 0.5.1-10.cvs20060205Dan Williams 0.5.1-9.cvs20060202Dan Williams 0.5.1-8.cvs20060131Dan Williams 0.5.1-7.cvs20060131Dan Williams 0.5.1-6.cvs20060127Jesse Keating John (J5) Palmieri - 0.5.1-5Peter Jones - 0.5.1-4Christopher Aillon - 0.5.1-3Christopher Aillon - 0.5.1-2Christopher Aillon - 0.5.0-2Dan Williams - 0.4.1-5.cvs20051010Dan Williams - 0.4.1-4.cvs20051009Dan Williams - 0.4.1-3.cvs20050922Jeremy Katz - 0.4.1-2.cvs20050912Dan Williams - 0.4.1-2.cvs20050819Dan Williams - 0.4.1Dan Williams - 0.4-36.cvs20050811Dan Williams - 0.4-35.cvs20050811Ray Strode - 0.4-34.cvs20050729Dan Williams - 0.4-34.cvs20050629David Zeuthen - 0.4-33.cvs20050629Dan Williams - 0.4-32.cvs20050617Dan Williams - 0.4-31.cvs20050616Dan Williams - 0.4-30.cvs20050615Dan Williams - 0.4-15.cvs30050404Dan Williams - 0.4-14.cvs30050404Dan Williams - 0.4-13.cvs30050404Dan Williams - 0.4-12.cvs20050404Dan Williams - 0.4-11.cvs20050404Dan Williams - 0.4-10.cvs20050404Dan Williams - 0.4-9.cvs20050404Jeremy Katz - 0.4-8.cvs20050404Jeremy Katz - 0.4-7.cvs20050404Dan Williams 0.4-6.cvs20050404Dan Williams 0.4-5.cvs20050402Christopher Aillon 0.4-4.cvs20050315Ray Strode 0.4-3.cvs20050315Ray Strode 0.4-2.cvs20050315Ray Strode 0.4-1.cvs20050315Ray Strode 0.4-1.cvs20050307Dan Williams 0.3.4-1.cvs20050304Dan Williams 0.3.3-2.cvs20050222Dan Williams 0.3.3-2.cvs20050214.x.1Dan Williams 0.3.3-2.cvs20050214Dan Williams 0.3.3-1.cvs20050202Dan Williams 0.3.3-1.cvs20050125Dan Williams 0.3.3-1.cvs20050124Than Ngo 0.3.3-1.cvs20050112.4 - 0.3.3-1.cvs20050118 - 0.3.3-1.cvs20050112 - 0.3.2-4.3.cvs20041208 - 0.3.2-3.cvs20041117 - 0.3.2-2.cvs20041115 - 0.3.2-2.cvs20041029 - 0.3.1-2 - 0.3.1-1Dan Williams 0.3-1Dan Williams 0.2-4Dan Williams 0.2-3Dan Williams 0.2-2Dan Williams 0.2-1Florian La Roche Dan Williams 0.1-3- initrd: accept mac address as interface specifier (rh #1879795) - initrd: fix parsing IPv6 prefix length (rh #1879795)- dhcp: add dhcp-vendor-class-identifier option (rh #1871042) - initrd: parse 'rd.net.dhcp.vendor-class' kernel cmdline arg (rh #1872299)- core: fix handling of local routes as default route and on D-Bus (rh #1868982)- core: fix wait-device-timeout race and support general device matches (rh #1853348)- bond: fix Reapply does not update bond options (rh #1847814) - dhcp: support DHCPv6 fqdn_fqdn option for hostname (rh #1858344)- core: fix managing devices after resuming from sleep (rh #1855563) - dhcp: fix BPF filter for internal client on big endian arch (rh #1861488) - core: support warning log setting IPv6 MTU with IPv6 disabled (rh #1840989) - wifi: fix crash parsing incomplete BSS info (rh #1866395)- core: fix generation of local routes for VRF devices (rh #1857133) - team: fix crash on failure to connect to teamd (rh #1856723) - core: fix detecting failure of master active-connection (rh #1845018) - core: fix warning about setting active_slave of bond when activating master (rh #1858326) - import translations (rh #1820552)- update to 1.26.0 - device: reset SR-IOV parameters on activation failure (rh #1819587) - initrd: enable ipv6.method=auto with ip=dhcp6 (rh #1854323) - core: add "nm-shared" zone for firewalld for shared mode (rh #1834907) - ppp: fix taking control of link (rh #1849386)- device: restart DHCP only for devices that are active or activating (rh #1852612) - initrd: fix generating default BOOTIF= connection (rh #1853277) - ovs: fix race condition when setting MAC address for ovs interfaces (rh #1852106)- update to 1.26-rc2 (1.25.91) - initrd: set ipv6.method=auto when using IPv4 static configuration (rh #1848943) - cloud-setup: add support for Google Cloud load-balancing routes (rh #1821787)- update to 1.26-rc1 (1.25.90) - core: support more tc qdiscs (tbf and sfq) (rh #1546802) - core: support match devices for connection profile by PCI address (ID_PATH) (rh #1673321) - ovs: fix peer property for OVS patch interface (rh #1845216) - doc: add manual pages nm-settings-dbus and nm-settings-nmcli (rh #1614726) - wifi: don't block autoconnect for profiles that never succeeded to connect (rh #1781253) - dbus,nmcli: highlight externally managed devices (rh #1816202)- update to 1.25.2 (development) - support ethtool coalesce and ring options (rh #1614700) - core: improve synchronization of qdiscs with kernel (rh #1815875) - team: support running without D-Bus (rh #1784363) - core: fix potential crash when autoactivating child connections (rh #1778073) - ethernet: reset original autonegotiation/speed/duplex settings on deactivation (rh #1807171) - core: fix setting IPv6 token in kernel (rh #1819680)- update to 1.25.1 (development) - improve documentation (rh #1651594, rh #1819259) - vrf: add support (rh #1773908) - bond: improve setting default options for miimon and updelay (rh #1805184, rh #1806549) - bluetooth: fix crash handling DUN modem (rh #1826635) - core: fix potential infinite loop with prefix delegation (rh #1488030) - initrd: fixes for running NetworkManager in initrd (rh #1627820, #1710935, #1744935, #1771792) - core: prevent multiple attempts to create default wired connection (rh #1687937) - bridge: support more options (rh #1755768) - libnm,dbus: expose HwAddress for all device types (rh #1786937) - core: fix route priority for IPv6 (rh #1814557) - core: fix crash during reapply (rh #1816067) - core: clear IP address from bridge slave (rh #1816517) - ovs: support changing MTU of OVS interfaces (rh #1820052) - nm-online: support setting timeout for NetworkManager-wait-online (rh #1828458)- core: fix leaking device state files in /run (rh #1810153) - dhcp: fix crash in nettools client when leaking GSource (rh #1810188)- dhcp: keep trying after a send failure (rh #1806516) - ovs: fail port enslavement when the bridge is not found (rh #1797696)- bond: fix setting arp_validate option for other bonding modes (rh #1789437)- Update to 1.22.8 - Added configuration option to customize IPv6 RA timeout (rh #1801158) - Removed length limitation for OVS Bridge, Patches and Interfaces (only Patch types) names (rh #1788432) - Reworked asynchronous deactivation of OVS interfaces (rh #1787989, rh #1782701) - Fixed failure when creating team interfaces (rh #1798947) - ifcfg-rh: fix clearing ovs slave type from ifcfg-rh file (rh #1804167) - Fixed bug causing virtual devices to not be available after AddConnection()/Update() (rh #1804350)- Update to 1.22.6 - nm-device: add new pending action to keep the device busy when in between states (rh #1759956) - cloud-setup: avoid unsupported settings in systemd service unit (rh #1791758) - do not create virtual device if master is not present (rh #1795919) - allow IPv6 RA timeout to be set to a value higher than 120 seconds (rh #1795957) - fix behaviour when 'ipv4.dhcp-timeout' option is set to 'infinity' (rh #1791378)- Update to 1.22.4 - dhcp: fix behavior of internal DHCP client when the server sends a NAK (rh #1787219)- Update to 1.22.2 - core,libnm: expose capability for OVS support (rh #1785147) - dhcp: various bugfixes for nettools n-dhcp4 plugin- dhcp: fix parsing of DNS search domain with nettools plugin (rh #1783981)- Update to 1.22.0 - support main.auth-polkit=root-only setting to allow root only (rh #1762011)- Update to 1.22-rc1 (1.21.90) - large internal rework of libnm's NMClient - dhcp: switch implementation of "internal" DHCP to nettools' n-dhcp4 - add support for carrier state of devices on D-Bus/libnm (rh #1722024) - cloud-setup: add initial and experimental tool for configuring in cloud (rh #1642461) - dhcp: support configuring FQDN hostname flags (rh #1649368)- Update to 1.21.3, a development snapshot of NetworkManager 1.22 - support configuring default route as a regular, static route (rh #1714438)- initrd: re-enable the generator (rh #1626348)- wifi: detect FT support per device to fix issues with driver support (rh #1743730) - doc: fix default values in pre-generated documentation (rh #1737945)- Import translations (rh #1689999)- Update to 1.20.0 release - fix license comments for RPM package (rh #1723395) - dhcp: disable experimental nettools DHCP plugin- Update to 1.20-rc1 snapshot - settings: support read-only directory for keyfile profiles (rh #1674545) - settings: add AddConnection2 D-Bus API to suppress autoconnect (rh #1677068) - settings: add no-reapply flat to Update2 D-Bus API (rh #1677070) - openvswitch: don't release slaves on quit (rh #1733709) - dhcp: expose private options for internal DHCP plugin (rh #1663253) - device: fix route table setting when re-activating device (rh #1719318) - man: clarify example in nm-openvswitch manual page (rh #1638038) - man: various improvements of manual pages (rh #1612554)- initrd: disable the generator again- Update to a newer 1.20 snapshot - ovs: support dpdk interfaces (rh #1612503) - libnm-core: change unsupported modes for arp_ip_targets bond option (rh #1718173) - ipv6: add 'disabled' method (rh #1643841) - device: fix matching parent device by connection UUID (rh #1716438) - cli: fix default value for team.runner-min-ports (rh #1716987) - initrd: re-enable the generator (rh #1626348)- Update to a 1.20 snapshot - core: fix a possible crash on device removal (rh #1659790) - core: fix automatic activation of software deviecs (rh #1667874) - team: use strict JSON parsing for configuration (rh #1691619) - team: don't kill teamd for external devices (rh #1693142) - logging: don't misuse SYSLOG_FACILITY field in journal (rh #1709741)- clients: fix string list setter (rh #1671200)- device: improve assuming bridges on startup (rh #1593939)- dhcp: fix client-id and DUID for infiniband (2) (rh #1658057)- device: ensure IP configuration is restored when link goes up (rh #1636715) - dhcp: fix client-id and DUID for infiniband (rh #1658057) - dhcp: change internal DHCP plugin's ipv4.dhcp-client-id setting to "mac" (rh #1661165)- ifcfg-rh: fix reading SR-IOV settings - dhcp: support client-id and DUID for infiniband (rh #1658057)- dhcp: fix default client-id for NetworkManager-config-server (rh #1658057) - connectivity: fix crash and portal detection (rh #1658217) - core: combine secret-key with machine-id for host identity (rh #1642023) - SR-IOV related fixes (rh #1651578, rh #1651576, rh #1651979) - core: fix updating agent-owned secrets (rh #1658771) - core: no longer set rp_filter sysctl (rh #1651097) - device: don't take device down when changing MAC address (rh #1659063) - doc: use pregenerated manual pages and gtk-doc from source tarball- Update translations (rh #1608323)- device: improve auto selection of device when activating profile (rh #1639254)- dhcp: fix out-of-bounds heap write for DHCPv6 with internal plugin (CVE-2018-15688) - dhcp: revert letting internal DHCP generate default client-id based on MAC address (rh #1640464) - dhcp: support "duid" setting for ipv4.dhcp-client-id - dhcp: support "${MAC}" identifier for connection.stable-id - dhcp: support dhcp-plugin device spec for matching devices in NetworkManager.conf - dhcp: install configuration snippet in config-server package for ipv4.dhcp-client-id=mac (rh #1640494) - dns: remove limitation for six DNS search entries (rh #1649704) - libnm: fix crash cancelling activation from within callback (rh #1643085)- Update translations (rh #1608323)- Don't depend on openvswitch (rh #1629178) - device: don't remove routes when the interface is down (rh #1636715)- dhcp: let internal DHCP generate default client-id based on MAC address (2)- dhcp: let internal DHCP generate default client-id based on MAC address- Update to 1.14.0 release- dhcp: switch default DHCP plugin from dhclient to internal (rh #1571655)- Update to 1.13.3, a development snapshot of NetworkManager 1.14- Update to 1.13.2, a development snapshot of NetworkManager 1.14- Update to 1.13.0, a development snapshot of NetworkManager 1.14- Update to 1.11.4, a development snapshot of NetworkManager 1.12 - Switch to Python 3-only build root- core: use gnutls crypto library instead of nss (rh #1581693)- core: fix error destroying checkpoints (rh#1574565)- Update to 1.11.3 release- Update to 1.10.2 release- Apply patch from previous commit- systemd: let NM-w-o.service require NetworkManager service (rh #1452866) - platform: really treat dsa devices as regular wired ethernet (rh #1371289) - libnm: fix accessing enabled and metered properties- platform: treat dsa devices as regular wired ethernet (rh #1371289)- device: fix frozen notify signals on unrealize error path - device: fix delay startup complete for unrealized devices - keyfile: fix handling routes with metric zero- cli: fix crash in interactive mode for "describe ." - libnm/{vpn,remote}-connection: disconnect signal handlers when disposed - libnm/manager: disconnect from signals on the proxy when we're disposed- enable NetworkManager-wait-online.service on package upgrade (rh#1455704)- Update to 1.8.4 release - don't install NetworkManager-wait-online in network-online.target.wants (rh#1455704)- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild- provide NetworkManager-devel- NetworkManager-wifi and NetworkManager-glib-devel should require NetworkManager, not provide it.- Update to 1.8.2 release - dhcp/dhclient: improve "interface" statement parsing - dns: fix public suffix check on search domains (rh #1404350)- device: don't change MTU unless explicitly configured (rh #1460760) - core: don't remove external IPv4 addresses (rh #1459813) - cli: fix output of iface in overview output (rh#1460219) - ppp: unexport NMPPPManager instance on dispose (rh#1459579) - cli: remove spurious device names from wifi subcommands output (rh#1460527)- bond: fix crash comparing mode while generating bond connection (rh #1459580) - connectivity: fix route penalty if WWAN and BT device using ip-ifindex (rh #1459932) - device: persist nm-owned in run state (rh #1376199) - device: fix assuming master device on restart (rh #1452062) - device: apply route metric penality only when the default route exists (rh #1459604) - connectivity: fix periodic connectivity check (rh #1458399) - bond: improve option matching on daemon restart (rh #1457909) - device: fix touching device after external activation (rh #1457242)- ifcfg-rh: fix writing legacy NETMASK value (rh #1445414) - tui: fix crash during connect (rh #1456826) - libnm: fix libnm rejecting VLAN ID 4095 (rh #1456911) - bluetooth: fix crash on connecting to a NAP (rh #1454385) - device: release removed devices from master on cleanup (rh #1448907) - nmcli: fix crash when setting 802-1x.password-raw (rh #1456362)- device: update external configuration before commit (fix bug) (rh #1449873)- dhcp: don't add route to DHCP4 server (rh #1448987) - device: update external configuration before commit (rh #1449873) - libnm: fix NUL termination of device's description (rh #1443114) - libnm, core: ensure valid UTF-8 in device properties (rh #1443114) - core: fix device's UDI property on D-Bus (rh #1443114) - ifcfg-rh: omit empty next hop for routes in legacy format (rh #1452648) - core: fix persisting managed state of device (rh #1440171) - proxy: fix use-after-free (rh #1450459) - device: don't wrongly delay startup complete waiting for carrier (rh #1450444)- Update to 1.8.0 release- Update to third Release Candidate of NetworkManager 1.8- Update to second Release Candidate of NetworkManager 1.8- Update to a snapshot of 1.8.x series- Update to a 1.6.2 release- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild- Update to a 1.6.0 release- Update with fixes from upstream nm-1-6 branch - build: let libnm and glib package conflict (rh #1406454)- Update to a 1.6-rc1- fix build failure due to clash of bitwise defines- Rebuild for readline 7.x- Update to a newer development snapshot- Rebuild package for vala generation error (rh#1398738)- fix enabling ifcfg-rh plugin by default for +=/-= operations (rh#1397938) - fix missing symbol _nm_device_factory_no_default_settings- fix enabling ifcfg-rh plugin by default (rh#1397938) - move translation files from core to libnm/glib subpackages- Update to a development snapshot- Update to 1.4.2- wifi: fix another activation failure when changing MAC address (rh#1371478, bgo#770456, bgo#770504)- dhcp: fix race to miss DHCP lease event (rh#1372854)- wifi: fix activation failure due to error changing MAC address (rh#1371478, bgo#770456)- Update to NetworkManager 1.4.0 release- fix stale Wi-Fi after resume from suspend (rh#1362165)- Rebuild against newer GLib to overcome logging problems on i686- Update to a later Git snapshot- dns: clear cache of dnsmasq when updating DNS configuration (rh#1338731) - dns: fix restarting dnsmasq instance - spec: depend bluetooth subpackage on exact wwan version - all: fix some memleaks- Update to NetworkManager 1.2.2 release- Update to NetworkManager 1.2.0 release- Update to NetworkManager 1.2-rc2- Update to NetworkManager 1.2-rc1- Fix link detection on 4.5 when build with 4.6 kernel- Update to NetworkManager 1.2-beta3- Fix obtaining the hostname from DNS (rh #1308974)- Fix activating connections in some cases (rh #1316488)- Update to NetworkManager 1.2-beta2 - Resync with contrib/rpm- specfile: remove no longer needed 10-ibft-plugin.conf and sync with contrib/rpm - core: backport fix for missing braces bug in platform- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild- Update to NetworkManager 1.2-beta1- Add upstream fix for AP list hash function (#1288867)- Update to a later snapshot - Enables RFC7217 addressing for new IPv6 connections- Drop the NetworkManager-devel subpackage (folded into libnm-glib-devel) - Update to a later snapshot- Import a newer 1.2 git snapshot- Fix test run- Import a 1.2 git snapshot- Fix command line parsing- Update to 1.0.6 release- fix crash when deactivating assumed device (rh #1253949) - backport wifi scan options for ssid - use plain HTTP URI for connectivity check- Update to a Git snapshot- Fix an assertion failure in nmcli (rh #1244048) - Fix default route handling on assumed connections (rh #1245648)- Update to 1.0.4 release- WEXT depends on enabled wifi- A bit more recent Git snapshot- A bit more recent Git snapshot - This one fixes a regression with default route management- Update to a new 1.0.3 development snapshot (git20150707) - core: fix handling of ignore-auto-* properties (rh #1239184)- A bit more recent Git snapshot- Update to a recent Git snapshot- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild- Update to 1.0.2 release- Update to 1.0.2 development snapshot (git20150429)- Update to 1.0.2 development snapshot- dns: revert resolv.conf symlink stuff (should only be in F23+, not F22)- connectivity: fix checking when no valid DNS servers are present (rh #1199098)- core: flush IPv6LL address when deconfiguring managed devices (rh #1193127) (rh #1184997)- core: resume bridged connections properly (rh #1162636, backport from master)- dns: manage resolv.conf as symlink to private file in /run directory (rh #1116999)- build: fix NetworkManager-bluetooth dep on NetworkManager-wwan - build: re-enable hardware plugins on s390- Update to 1.0- vpn: propagate daemon exec error correctly (bgo #739436) - core: do not assert when a device is enslaved externally (rh #1167345)- cli: fix crash in `nmcli device wifi` with multiple wifi devices (rh #1159408)- platform: fix a routing-related bug that could cause NM and other apps to spin (rh #1151665)- Fix IPv6 next hop default setting- Avoid unowned /etc/NetworkManager in config-connectivity-fedora- connectivity-fedora: don't require NetworkManager (#1156198)- bluetooth: Restore DUN support (rh #1055628)- Allow non-local users network control after PolicyKit authentication (rh #1145646)- connectivity: use HTTPS for connectivity checking (rh #113577)- adsl plugin needs rp-pppoe to work- always include ModemManager-glib-devel (#1129632)- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild- Rebuilt for ppp 2.4.7- connectivity: ensure interval is set to enable connectivity checking (rh #1123772)- Rebuilt for gobject-introspection 1.41.4- Update to upstream 0.9.10.0 release snapshot- Update to upstream 0.9.9.98 (0.9.10-rc1) release snapshot- Update to upstream 0.9.9.95 (0.9.10-beta1) release snapshot- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild- Rebuild against pppd 2.4.6- Update to a git snapshot (git20140319 git:3980806) - Rename NetworkManager-atm package to NetworkManager-adsl - Rename NetworkManager-bt package to NetworkManager-bluetooth- Update to a git snapshot (git20140317 git:a1e89b4) - platform: fix NM crash if link has no name (e.g. for failed VPN connection) - libnm-util/cli: fix bridge priority default value (rh #1073664)- Update to a git snapshot (git20140314 git:45a326d) - Fix Obsoletes and Requires to perform updates correctly- Update to a git snapshot (git20140310 git:350b6d6)- new upstream snapshot with development version 0.9.9.1- add nmtui package - bugfix caching of libnl objects (caused error with new libnl3 version when activating bridges) (rh #1063290) - fix NMManager:startup tracking (pending action) (rh #1030583)- core: fix crash getting secrets in libnm-glib- Update to a git snapshot (git20140131)- Update to a git snapshot (git20140117)- Update to a git snapshot (git20140114)- bluez-manager: fix a crash (rh #1048711)- core: fix IPv6 router solicitation loop (rh #1044757)- core: wait for link before declaring startup complete (rh #1034921) - core: ignore RA-provided IPv6 default routes (rh #1029213) - core: set IPv4 broadcast address correctly (rh #1032819)- core: Fix PtP/peer address support, for OpenVPN (rh #1018317)- dispatcher: fix crash on exit while logging from signal handler (rh #1017884) - core: workaround crash when connecting to wifi (rh #1025371) - ethernet: don't crash if device doesn't have a MAC address (rh #1029053) - libnm-glib: fix crash by taking additional ref in result_cb() (rh #1030403) - ifcfg-rh: fix ignoring updates that don't change anything- nmcli: add "con load" to manually load an ifcfg file - vpn: fix logging to help debug rh #1018317 - bridge: fix crash with bridge ports with empty settings (rh #1031170)- core: fix detection of non-mac80211 devices that do not set DEVTYPE (rh #1015598)- core: add some debugging to help diagnose netlink errors (rh #1029213)- ifcfg-rh: fix crash in ifcfg-rh plugin when reloading connections (rh #1023571) - ifcfg-rh: fix crash when having connections with NEVER_DEFAULT (rh #1021112) - core: fix segfault in nm-policy when setting default route for vpn (rh #1019021) - ifcfg-rh: fix crash when reading connection (assert) (rh #1025007) - core: allow IPv4 to proceed if IPv6 is globally disabled but set to "auto" (rh #1012151)- core: fix DHCPv6 address prefix length (rh #1013583) - cli: enhance bonding questionaire (rh #1007355) - core: fix crash with Bluez5 if PAN connection is not defined (rh #1014770) - libnm-glib: fix various memory leaks that could cause UIs to mis-report state - core: fix issues with mis-configured IPv6 router advertisements (rh #1008104) - cli: fix potential crash editing connections (rh #1011942)- core: fix bridge device creation (#1012532) - core,settings: do not call functions with connection==NULL (rh #1008151) - cli: accept gateway in the IP questionnaire of 'nmcli -a con add' (rh #1007368) - cli: always print success message (not only in --pretty mode) (rh #1006444) - cli: fix bond questionnaire to be able to set miimon (rh #1007355) - ifcfg-rh: if IPv4 is disabled put DNS domains (DOMAIN) into IPv6 (rh #1004866) - platform: fix a crash when nm_platform_sysctl_get() returns NULL (rh #1010522) - platform: fix InfiniBand partition handling (rh #1008568) - infiniband: only check the last 8 bytes when doing hwaddr matches (rh #1008566) - bluez: merge adding support for BlueZ 5 (bgo #701078) - api: clarify lifetime and behavior of ActiveConnection's SpecificObject property (rh #1012309) - vpn: fix connecting to VPN (bgo #708255) (rh #1014716) - rdisc: do not crash on NDP init failures (rh #1012151) - cli: be more verbose when adding IP addresses in questionnaire (rh #1006450) - team: chain up parent dispose() in NMDeviceTeam dispose() (rh #1013593) - translation updates- drop wimax subpackage- core: actually enable ModemManager 1.0 support - libnm-glib: fix nm_remote_connection_delete() not calling callback (rh #997568) - cli: ensure terminal is reset after quitting - cli: set wep-key-type properly when editing (rh #1003945) - man: fix typo in nmcli examples manpage (rh #1004117) - core: fix setting VLAN ingress/egress mappings - core: allow creating VLANs from interfaces other than Ethernet (rh #1003180) - cli: fix input/output format conversion (rh #998929)- core: fix bug which disallowed deleting connections (rh #997568) - core: add support for Team devices - core: enable NetworkManager-wait-online by default (rh #816655) - core: fix crash when 'gre' and 'macvlan' links change (rh #997396) - core: fail activation when invalid static routes are configured (rh #999544) - core: enhance connectivity checking to include portal detection - core: allow hyphens for MAC addresses (rh #1002553) - core: remove NetworkManager-created software devices when they are deactivated (rh #953300) - core: fix handling of some DHCP client identifiers (rh #999503) - core: correctly handle Open vSwitch interfaces as generic interfaces (rh #1004356) - core: better handle Layer-2-only connections (rh #979288) - cli: enhanced bash completion - cli: make the 'describe' command more visible (rh #998002) - cli: fix bug rejecting changes to Wi-Fi channels (rh #999999) - cli: update bash completion to suggest connection names (rh #997997) - cli: fix tab completion for aliases in edit mode - cli: ask whether to switch IP method to 'auto' when all addresses are deleted (rh #998137) - cli: request missing information when --ask is passed (rh #953291) - cli: add 'remove' command to edit mode - cli: fix creation of secure Wi-Fi connections (rh #997969) (rh #997555) - cli: default autoconnect to no and ask whether to activate on save (rh #953296) - man: clarify manpage text (rh #960071) (rh #953299) - man: fix errors in the nmcli help output and manpage (rh #997566) - ifcfg-rh: only write IPV6_DEFAULTGW when there's actually a default gateway (rh #997759) - ifcfg-rh: fix handling of legacy-format routes file with missing gateway- core: fix assert on multi-hop routes (rh #989022) - core: fix dispatcher systemd unit enabling (rh #948433) - ifcfg-rh: ignore emacs temporary lockfiles (rh #987629) - core: fix various routing issues and interaction with kernel events - cli: confirm saving connections when autoconnect is enabled (rh #953296) - cli: automatically change method when static IP addresses are added - core: preserve externally added IPv4 routes and addresses- Create NetworkManager-config-server package- Update to git snapshot- Belatedly update udev directory for UsrMove - Fix incorrect dates in old changelog entries to avoid rpm warnings- build support for connectivity checking (rh #810457)- disable building WiMax for RHEL- Update to new 0.9.10 snapshot- Update for systemd network-online.target (rh #787314) - Add system service for the script dispatcher (rh #948433)- Enable hardened build - Update to 0.9.10 snapshot - cli: new capabilities and somewhat re-arranged syntax - core: generic interface support - core: split config support; new "server mode" options - core: allow locking connections to interface names- core: fix issue with UI not showing disconnected on rfkill - core: memory leak fixes - core: silence warning about failure reading permanent MAC address (rh #907912) - core: wait up to 120s for slow-connecting modems - core: don't crash on PPPoE connections without a wired setting - core: ensure the AvailableConnections property is always correct - keyfile: ensure all-default VLAN connections are read correctly - core: suppress kernel's automatic creation of bond0 (rh #953466) - libnm-glib: make NMSecretAgent usable with GObject Introspection - libnm-util: fix GObject Introspection annotations of nm_connection_need_secrets() - core: documentation updates- Update to 0.9.8.2 snapshot - core: fix VLAN parent handling when identified by UUID - core: quiet warning about invalid interface index (rh #920145) - core: request 'static-routes' from DHCP servers (rh #922558) - core: fix crash when dbus-daemon is restarted (rh #918273) - core: copy leasefiles from /var/lib/dhclient to fix netboot (rh #916233) - core: memory leak and potential crash fixes - ifcfg-rh: ensure missing STP property is interpreted as off (rh #922702)- Update to the 0.9.8.0 release - cli: fix a possible crash- core: use systemd for suspend/resume, not upower- Update to 0.9.8-beta2 - core: ignore bridges managed by other tools (rh #905035) - core: fix libnl assert (rh #894653) - wifi: always use Proactive Key Caching with WPA Enterprise (rh #834444) - core: don't crash when Internet connection sharing fails to start (rh #883142)- Set correct systemd KillMode to fix anaconda shutdown hangs (rh #876218)- ifcfg-rh: write missing IPv6 setting as IPv6 with "auto" method (rh #830434)- Build vapi files and add them to the devel package- Apply patch from master to read hostname from /etc/hostname (rh #831735)- Apply patch from master to update hostname (rh #875085) - spec: create /etc/NetworkManager/dnsmasq.d (rh #873621)- Don't bring up uninitialized devices (fd #56929)- Actually apply the patch from the previous commit...- Apply patch from master to fix a crash (rh #865009)- Apply patch from master so connections finish connecting properly (bgo #685581)- Forward-port some forgotten fixes from F17 - Fix networked-filesystem systemd dependencies (rh #787314) - Don't restart NM on upgrade, don't stop NM on uninstall (rh #811200)- Update to git snapshot- Update to 0.9.7.0 snapshot- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild- Update to 0.9.6-rc2 - core: fix race between parallel DHCP client invocations - core: suppress a useless warning (rh #840580) - ifcfg-rh: fix segfault with malformed values (rh #841391) - ifcfg-rh: ignore IP config on bond slave configurations (rh #838907)- Update to 0.9.5.95 (0.9.6-rc1) snapshot - core: add autoconnect, driver-versioni and firmware-version properties to NMDevice - core: various IPv6 improvements - core: reduce number of changes made to DNS information during connection setup - core: add Vala language bindings - vpn: support IPv6 over VPNs - wifi: add on-demand WiFi scan support- Update to git snapshot- NM no longer uses /var/run/NetworkManager, so don't claim to own it. (rh #656638)- Update to git snapshot- Add _isa for internal requires; otherwise depsolving may pull in an arbitrary architecture.- Update to 0.9.4- libnm-glib: updated for new symbols the applet wants- applet: move to network-manager-applet RPM - editor: move to nm-connection-editor RPM - libnm-gtk: move to libnm-gtk RPM- Update to 0.9.3.997 (0.9.4-rc1) - core: fix possible WiFi hang when connecting to Ad-Hoc networks - core: enhanced IPv6 compatibility - core: proxy DNSSEC data when using the 'dnsmasq' caching nameserver plugin - core: allow VPNs to specify multiple domain names given by the server - core: fix an issue creating new InfiniBand connections - core/applet/editor: disable WiFi Ad-Hoc WPA connections until kernel bugs are fixed- core: fix issue with carrier changes not being recognized (rh #800690) - editor: warn user if CA certificate is left blank- core: fix a crash with ipw2200 devices and adhoc networks - core: fix IPv6 addressing on newer kernels - core: fix issue with VPN plugin passwords (rh #802540) - cli: enhancements for Bonding, VLAN, and OLPC mesh devices - ifcfg-rh: fix quoting WPA passphrases that include quotes (rh #798102) - libnm-glib: fix some issues with duplicate devices shown in menus- Update to 0.9.3.995 (0.9.4-beta1) - core: add support for bonding and VLAN interfaces - core: add support for Internet connectivity detection - core: add support for IPv6 Privacy Extensions - core: fix interaction with firewalld restarts- disable WiMAX plugin on s390(x)- Put WiMAX plugin files in the right subpackage- Update to 0.9.4 snapshot - wimax: enable optional support for Intel WiMAX devices - core: use nl80211 for WiFi device control - core: add basic support for Infiniband IP interfaces - core: add basic support for bonded interfaces - core: in-process IP configuration no longer blocks connected state- Rebuild- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild- Rebuild for libgnome-bluetooth.so.9- core: fix possible crash when talking to ModemManager - core: improve handling of rfkill on some machines (eeepc 1005HA and others) - ifcfg-rh: don't use spaces in ifcfg file names (rh #742273) - core: accept IPv6 Router Advertisements when forwarding is on - core: bump dnsmasq cache size to 400 entries - core: ensure IPv6 static routes are flushed when device is deactivated - ifcfg-rh: fix changing WPA connections to WEP - core: fix setting hostname from DHCP (rh #719100) - libnm-glib: fix various GObject introspection issues (rh #747302) - core: don't change routing or DNS if no devices are managed - core: ensure IPv6 RA-provided routes are honored- Rebuilt for glibc (rh #747377) - core: fix setting hostname from DHCP options (rh #719100) - skip a release to keep up with F16- core: fix location of wifi.ui (rh #741448)- core: ifcfg-rh: remove newlines when writing to ifcfg files (CVE-2011-3364) (rh #737338) - core: change iscsiadm path to /sbin/iscsiadm in ifcfg-rh plugin (rh #740753) - core: fix refcounting when deleting a default wired connection (lp:797868)- Update to 0.9.1.90 (0.9.2-beta1) - core: fix IPv6 link-local DNS servers in the dnsmasq DNS plugin - cli: add ability to delete connections - keyfile: fix an issue with duplicated keyfile connections - core: ensure the 'novj' option is passed through to pppd - core: store timestamps for VPN connections too (rh #725353)- fix systemd scriptlets and trigger- Update to 0.9 release - core: fix issue where scan results could be ignored - core: ensure agent secrets are preserved when updating connections - core: don't autoconnect disabled modems - core: fix race when checking modem enabled/disabled status after disabling - core: ensure newly installed VPN plugins can actually talk to NM - core: add support for 802.1X certificate subject matching - libnm-glib: various introspection fixes - applet/editor: updated translations- Add some patches for some blocker (rh #727501)- core: updated Russian translation (rh #652904) - core: fix possible crash if secrets are missing - core: append interface name for IPv6 link-local DNS server addresses (rh #720001) - core: fix setting hostname from DHCP options (rh #719100) - libnm-util: GObject introspection annotation fixes - libnm-util: ensure IP address/route prefixes are valid - ifcfg-rh: read anonymous identity for 802.1x PEAP connections (rh #708436) - applet: show notifications on CDMA home/roaming changes - applet: fix various issues saving VPN secrets - editor: allow exporting VPN secrets - editor: default to IPv6 "automatic" addressing mode- core: ensure users are authorized for shared wifi connections (CVE-2011-2176) (rh #715492) - core: retry failed connections after 5 minute timeout - core: immediately request new 802.1x 'always ask' passwords if they fail - core: add MAC blacklisting capability for WiFi and Wired connections - core: retry failed connections when new users log in (rh #706204) - applet: updated translations - core: drop compat interface now that KDE bits are updated to NM 0.9 API- core: don't cache "(none)" hostname at startup (rh #706094) - core: fix handling of VPN connections with only system-owned secrets - core: fix optional waiting for networking at startup behavior (rh #710502) - ifcfg-rh: fix possible crashes in error cases - ifcfg-rh: fix various IPv4 and IPv6 handling issues - applet: add notifications of GSM mobile broadband registration status - editor: move secrets when making connections available to all users or private - applet: don't show irrelevant options when asking for passwords- keyfile: better handling of missing certificates/private keys - core: fix issues handling "always-ask" wired and WiFi 802.1x connections (rh #703785) - core: fix automatic handling of hidden WiFi networks (rh #707406) - editor: fix possible crash after reading network connections (rh #706906) - editor: make Enter/Return key close WiFi password dialogs (rh #708666)- Bump for CVE-2011-1943 (no changes, only a rebuild)- editor: fix resizing of UI elements (rh #707269) - core: retry wired connections when cable is replugged - core: fix a few warnings and remove some left-over debugging code- compat: fix activation/deactivation of VPN connections (rh #699786) - core: fix autodetection of previously-used hidden wifi networks - core: silence error if ConsoleKit database does not yet exist (rh #695617) - core: fix Ad-Hoc frequency handling (rh #699203) - core: fixes for migrated OpenConnect VPN plugin connections - core: various fixes for VPN connection secrets handling - core: send only short hostname to DHCP servers (rh #694758) - core: better handling of PKCS#8 private keys - core: fix dispatcher script interface name handling - editor: fix potential crash when connection is invalid (rh #704848) - editor: allow _ as a valid character for GSM APNs- core: fix possible crash when connections are deleted - core: fix exported symbols in libnm-util and libnm-glib - core/applet: updated translations- core: ensure DER format certificates are correctly recognized (rh #699591) - core: fix WINS server handling in client helper libraries - core: enhance dispatcher script environment to include IPv6 and VPN details - applet: migrate openswan connections to 0.9 - editor: improve usability of editing IP addresses (rh #698199)- core: enable optimized background roaming for WPA Enterprise configs - core: better handling of WiFi and WiMAX rfkill (rh #599002) - applet: fix crash detecting Bluetooth DUN devices a second time - ifcfg-rh: fix managed/unmanaged changes when removing connections (rh #698202)- core: systemd and startup enhancements for NFS mounts - core: more efficient startup process - core: fix handling of multiple logins when one is inactive - core: fix handling of S390/Hercules CTC network interfaces (rh #641986) - core: support Easytether interfaces for Android phones - core: fix handling of WWAN enable/disable states - ifcfg-rh: harmonize handling if IPADDR/PREFIX/NETMASK with initscripts (rh #658907) - applet: fix connection to WPA Enterprise networks (rh #694765)- core: fix handling of infinite IPv6 RDNSS timeouts (rh #689291)- Update to 0.8.998 (0.9.0-rc1) - core: fix near-infinite requests for passwords (rh #692783) - core: fix handling of wired 802.1x connections - core: ignore Nokia PC-Suite ethernet devices we can't use yet - applet: migrate 0.8 OpenVPN passwords to 0.9 formats- core: resurrect default VPN username - core: don't stomp on crypto library users by de-initing the crypto library- core: fix creation of default wired connections - core: fix requesting new secrets when old ones fail (ex changing WEP keys) - editor: ensure all pages are sensitive after retrieving secrets - editor: fix crash when scrolling through connection lists (rh #693446) - applet: fix crash after using the wifi or wired secrets dialogs (rh #693446)- Fix trigger to enable the systemd service for upgrades (rh #678553)- core: fix connection deactivation on the compat interface - core: give default wired connections a more friendly name - core: fix base type of newly created wired connections - applet: many updated translations- core: fix possible libnm-glib crash when activating connections - applet: fix various naming and dialog title issues- nm-version.h should be in NetworkManager-devel, not -glib-devel (rh #685442)- core: add compatibility layer for KDE Plasma network infrastructure- Update to 0.8.997 (0.9-beta3) - ifcfg-rh: fix reading and writing of Dynamic WEP connections using LEAP as the eap method - wifi: fix signal strength for scanned access points with some drivers - applet: translation updates- Update to 0.8.996 (0.9-beta2)- applet: fix bus name more- applet: fix bus name- Fix systemd requires- Update to NetworkManager 0.9-beta1 - core: consolidate user and system settings services into NM itself - core: add WiMAX support - applet: support Fast User Switching- Rebuild against newer gtk- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild- Rebuild against new gtk- Handle modem IP interface changes after device is recognized- Rebuild against new gtk3- use --force in autoreconf to fix FTBFS- Rebuild against newer gtk- Update to 0.8.2- Rebuild against libnotify 0.7 - misc gtk build fixes- core: preserve WiFi Enabled state across reboot and suspend/resume- core: fix suspend/resume regression (rh #638640) - core: fix issue causing some nmcli requests to be ignored- core: preserve custom local-mapped hostnames in /etc/hosts (rh #627269)- core: remove stale /etc/hosts mappings (rh #630146)- core: add dispatcher events on DHCPv4 and DHCPv6 lease changes - core: enforce access permissions when enabling/disabling WiFi and WWAN (rh #626337) - core: listen for UPower suspend/resume signals - applet: fix disabled Enable Networking and Enable Wireless menu items (rh #627365) - applet: updated translations - applet: obscure Mobile Broadband PIN in secondary unlock dialog- core: fix some systemd interaction issues- core: rebuild to fix polkit 0.97 build issue - applet: updated translations- core: rebuild to fix dbus-glib security issue (CVE-2010-1172) (rh #585394)- core: quiet annoying warnings (rh #612991) - core: fix retrieval of various IP options in libnm-glib (rh #611141) - core: ship NetworkManager.conf instead of deprecated nm-system-settings.conf (rh #606160) - core: add short hostname to /etc/hosts too (rh #621910) - core: recheck autoactivation when new system connections appear - core: enable DHCPv6-only configurations (rh #612445) - core: don't fail connection immediately if DHCP lease expires (rh #616084) (rh #590874) - core: fix editing of PPPoE system connections - core: work around twitchy frequency reporting of various wifi drivers - core: don't tear down user connections on console changes (rh #614556) - cli: wait a bit for NM's permissions check to complete (rh #614866) - ifcfg-rh: ignore BRIDGE and VLAN configs and treat as unmanaged (rh #619863) - man: add manpage for nm-online - applet: fix crash saving ignore-missing-CA-cert preference (rh #619775) - applet: hide PIN/PUK by default in the mobile PIN/PUK dialog (rh #615085) - applet: ensure Enter closes the PIN/PUK dialog (rh #611831) - applet: fix another crash in ignore-CA-certificate handling (rh #557495) - editor: fix handling of Wired/s390 connections (rh #618620) - editor: fix crash when canceling editing in IP address pages (rh #610891) - editor: fix handling of s390-specific options - editor: really fix crash when changing system connections (rh #603566)- core: read nm-system-settings.conf before NetworkManager.conf (rh #606160) - core: fix editing system DSL connections when using keyfile plugin - core: work around inconsistent proprietary driver associated AP reporting - core: ensure empty VPN secrets are not used (rh #587784) - core: don't request WiFi scans when connection is locked to a specific BSSID - cli: show IPv6 settings and configuration - applet: updated translations - editor: fix a PolicyKit-related crash editing connections (rh #603566) - applet: fix saving the ignore-missing-CA-cert preference (rh #610084) - editor: fix listing connections on PPC64 (rh #608663) - editor: ensure editor windows are destroyed when closed (rh #572466)- Rebuild against new gnome-bluetooth- Update to 0.8.1 release candidate - core: fix WWAN hardware enable state tracking (rh #591622) - core: fix Red Hat initscript return value on double-start (rh #584321) - core: add multicast route entry for IPv4 link-local connections - core: fix connection sharing in cases where a dnsmasq config file exists - core: fix handling of Ad-Hoc wifi connections to indicate correct network - core: ensure VPN interface name is passed to dispatcher when VPN goes down - ifcfg-rh: fix handling of ASCII WEP keys - ifcfg-rh: fix double-quoting of some SSIDs (rh #606518) - applet: ensure deleted connections are actually forgotten (rh #618973) - applet: don't crash if the AP's BSSID isn't availabe (rh #603236) - editor: don't crash on PolicyKit events after windows are closed (rh #572466)- core: fix nm-online crash (rh #593677) - core: fix failed suspend disables network (rh #589108) - core: print out missing firmware errors (rh #594578) - applet: fix device descriptions for some mobile broadband devices - keyfile: bluetooth fixes - applet: updated translations (rh #589230)- core: use GIO in local mode only (rh #588745) - core: updated translations (rh #589230) - core: be more lenient in IPv6 RDNSS server expiry (rh #590202) - core: fix headers to be C++ compatible (rh #592783) - applet: updated translations (rh #589230) - applet: lock connections with well-known SSIDs to their specific AP- core: fix handling of IPv6 RA flags when router goes away (rh #588560) - bluetooth: fix crash configuring DUN connections from the wizard (rh #590666)- core: restore initial accept_ra value for IPv6 ignored connections (rh #588619) - bluetooth: fix bad timeout on PAN connections (rh #586961) - applet: updated translations- core: treat missing IPv6 configuration as ignored (rh #588814) - core: don't flush IPv6 link-local routes (rh #587836) - cli: update output formatting- core: allow IP configuration as long as one method completes (rh #567978) - core: don't prematurely remove IPv6 RDNSS nameservers (rh #588192) - core: ensure router advertisements are only used when needed (rh #588613) - editor: add IPv6 gateway editing capability- core: IPv6 autoconf, DHCP, link-local, and manual mode fixes - editor: fix saving IPv6 address in user connections- core: fix crash when IPv6 is enabled and interface is deactivated- core: fix issues with IPv6 router advertisement mishandling (rh #530670) - core: many fixes for IPv6 RA and DHCP handling (rh #538499) - core: ignore WWAN ethernet devices until usable (rh #585214) - ifcfg-rh: fix handling of WEP passphrases (rh #581718) - applet: fix crashes (rh #582938) (rh #582428) - applet: fix crash with multiple concurrent authorization requests (rh #585405) - editor: allow disabling IPv4 on a per-connection basis - editor: add support for IPv6 DHCP-only configurations- core: fix crash during install (rh #581794) - wifi: fix crash when supplicant segfaults after resume (rh #538717) - ifcfg-rh: fix MTU handling for wired connections (rh #569319) - applet: fix display of disabled mobile broadband devices- core: fix automatic WiFi connections on resume (rh #578141)- core: more flexible logging - core: fix crash with OLPC mesh devices after suspend - applet: updated translations - applet: show mobile broadband signal strength and technology in the icon - applet: fix continuous password requests for 802.1x connections (rh #576925) - applet: many updated translations- core: fix modem enable/disable - core: fix modem default route handling- core: don't exit early on non-fatal state file errors - core: fix Bluetooth connection issues (rh #572340) - applet: fix some translations (rh #576056) - applet: better feedback when wrong PIN/PUK is entered - applet: many updated translations - applet: PIN2 unlock not required for normal modem functionality - applet: fix wireless secrets dialog display- man: many manpage updates - core: determine classful prefix if non is given via DHCP - core: ensure /etc/hosts is always up-to-date and correct (rh #569914) - core: support GSM network and roaming preferences - applet: startup speed enhancements - applet: better support for OTP/token-based WiFi connections (rh #526383) - applet: show GSM and CDMA registration status and signal strength when available - applet: fix zombie GSM and CDMA devices in the menu - applet: remove 4-character GSM PIN/PUK code limit - applet: fix insensitive WiFi Create... button (rh #541163) - applet: allow unlocking of mobile devices immediately when plugged in- core: update to final 0.8 release - core: fix Bluetooth DUN connections when secrets are needed - ifcfg-rh: add helper for initscripts to determine ifcfg connection UUIDs - applet: fix Bluetooth connection secrets requests - applet: fix rare conflict with other gnome-bluetooth plugins- core: fix mobile broadband PIN handling (rh #543088) (rh #560742) - core: better handling of /etc/hosts if hostname was already added by the user - applet: crash less on D-Bus property errors (rh #557007) - applet: fix crash entering wired 802.1x connection details (rh #556763)- core: validate the autostart .desktop file - build: fix nmcli for the stricter ld (fixes FTBFS) - build: fix nm-connection-editor for the stricter ld (fixes FTBFS) - applet: don't autostart in KDE on F13+ (#541353)- core: add Bluetooth Dial-Up Networking (DUN) support (rh #136663) - core: start DHCPv6 on receipt of RA 'otherconf'/'managed' bits - nmcli: allow enable/disable of WiFi and WWAN- ifcfg-rh: read and write DHCPv6 enabled connections (rh #429710) - nmcli: update- core: clean NSS up later to preserve errors from crypto_init()- core: support for managed-mode DHCPv6 (rh #429710) - ifcfg-rh: gracefully handle missing PREFIX/NETMASK - cli: initial preview of command-line client - applet: add --help to explain what the applet is (rh #494641)- build: fix for new pppd (rh #548520) - core: add WWAN enable/disable functionality - ifcfg-rh: IPv6 addressing and routes support (rh #523288) - ifcfg-rh: ensure connection is updated when route/key files change - applet: fix crash when active AP isn't found (rh #546901) - editor: fix crash when editing connections (rh #549579)- core: fix recognition of standalone 802.1x private keys - applet: clean notification text to ensure it passes libnotify validation- core: remove haldaemon from initscript dependencies (rh #542078) - core: handle PEM certificates without an ending newline (rh #507315) - core: fix rfkill reporting for ipw2x00 devices - core: increase PPPoE timeout to 30 seconds - core: fix re-activating system connections with secrets - core: fix crash when deleting automatically created wired connections - core: ensure that a VPN's DNS servers are used when sharing the VPN connection - ifcfg-rh: support routes files (rh #507307) - ifcfg-rh: warn when device will be managed due to missing HWADDR (rh #545003) - ifcfg-rh: interpret DEFROUTE as never-default (rh #528281) - ifcfg-rh: handle MODE=Auto correctly - rpm: fix rpmlint errors - applet: don't crash on various D-Bus and other errors (rh #545011) (rh #542617) - editor: fix various PolicyKit-related crashes (rh #462944) - applet+editor: notify user that private keys must be protected- nm: better pidfile handing (rh #517362) - nm: save WiFi and Networking enabled/disabled states across reboot - nm: fix crash with missing VPN secrets (rh #532084) - applet: fix system connection usage from the "Connect to hidden..." dialog - applet: show Bluetooth connections when no other devices are available (rh #532049) - applet: don't die when autoconfigured connections can't be made (rh #532680) - applet: allow system administrators to disable the "Create new wireless network..." menu item - applet: fix missing username connecting to VPNs the second time - applet: really fix animation stuttering - editor: fix IP config widget tooltips - editor: allow unlisted countries in the mobile broadband wizard (rh #530981) - ifcfg-rh: ignore .rpmnew files (rh #509621)- nm: fix PPPoE connection authentication (rh #532862)- install: better fix for (rh #526519) - install: don't build Bluetooth bits on s390 (rh #529854) - nm: wired 802.1x connection activation fixes - nm: fix crash after modifying default wired connections like "Auto eth0" - nm: ensure VPN secrets are requested again after connection failure - nm: reset 'accept_ra' to previous value after deactivating IPv6 connections - nm: ensure random netlink events don't interfere with IPv6 connection activation - ifcfg-rh: fix writing out LEAP connections - ifcfg-rh: recognize 'static' as a valid BOOTPROTO (rh #528068) - applet: fix "could not find required resources" error (rh #529766)- install: fix -gnome package pre script failures (rh #526519) - nm: fix failures validating private keys when using the NSS crypto backend - applet: fix crashes when clicking on menu but not associated (rh #526535) - editor: fix crash editing wired 802.1x settings - editor: fix secrets retrieval when editing connections- nm: fix connection takeover when carrier is not on - nm: handle certificate paths (CA chain PEM files are now fully usable) - nm: defer action for 4 seconds when wired carrier drops - ifcfg-rh: fix writing WPA passphrases with odd characters - editor: fix editing of IPv4 settings with new connections (rh #525819) - editor: fix random crashes when editing due to bad widget refcounting - applet: debut reworked menu layout (not final yet...)- Install GConf schemas- nm: allow disconnection of all device types - nm: ensure that wired connections are torn down when their hardware goes away - nm: fix crash when canceling a VPN's request for secrets - editor: fix issues changing connections between system and user scopes - editor: ensure changes are thrown away when editing is canceled - applet: ensure connection changes are noticed by NetworkManager - applet: fix crash when creating new connections - applet: actually use wired 802.1x secrets after they are requested- nm: IPv6 zeroconf support and fixes - nm: port to polkit (rh #499965) - nm: fixes for ehea devices (rh #511304) (rh #516591) - nm: work around PPP bug causing bogus nameservers for mobile broadband connections - editor: fix segfault with "Unlisted" plans in the mobile broadband assistant- nm: add iSCSI support - nm: add connection assume/takeover support for ethernet (rh #517333) - nm: IPv6 fixes - nm: re-add OLPC XO-1 mesh device support (removed with 0.7.0) - applet: better WiFi dialog focus handling- Add patch to fix service detection on phones- nm: IPv6 support for manual & router-advertisement modes- Move some big docs to -devel to save space- Update to upstream 'master' branch - Use modem-manager for better 3G modem support - Integrated system settings with NetworkManager itself - Use udev instead of HAL- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild- applet: fix certificate validation in hidden wifi networks dialog (rh #508207)- nm: fixes for ZTE/Onda modem detection - nm: prevent re-opening serial port when the SIM has a PIN - applet: updated translations - editor: show list column headers- nm: fix serial port settings- nm: fix AT&T Quicksilver modem connections (rh #502002) - nm: fix support for s390 bus types (rh #496820) - nm: fix detection of some CMOtech modems - nm: handle unsolicited wifi scans better - nm: resolv.conf fixes when using DHCP and overriding search domains - nm: handle WEP and WPA passphrases (rh #441070) - nm: fix removal of old APs when none are scanned - nm: fix Huawei EC121 and EC168C detection and handling (rh #496426) - applet: save WEP and WPA passphrases instead of hashed keys (rh #441070) - applet: fix broken notification bubble actions - applet: default to WEP encryption for Ad-Hoc network creation - applet: fix crash when connection editor dialogs are canceled - applet: add a mobile broadband provider wizard- drop ExcludeArch s390 s390x, we need at least the header files- nm-save-the-leases.patch: Use per-connection lease files, and don't delete them on interface deactivate.- ifcfg-rh: fix problems noticing changes via inotify (rh #495884)- ifcfg-rh: enable write support for wired and wifi connections- nm: update to 0.7.1 - nm: fix startup race with HAL causing unmanaged devices to sometimes be managed (rh #494527)- nm: fix recognition of Option GT Fusion and Option GT HSDPA (nozomi) devices (rh #494069) - nm: fix handling of spaces in DHCP 'domain-search' option - nm: fix detection of newer Option 'hso' devices - nm: ignore low MTUs returned by broken DHCP servers- Update to 0.7.1-rc4 - nm: use PolicyKit for system connection secrets retrieval - nm: correctly interpret errors returned from chmod(2) when saving keyfile system connections - editor: use PolicyKit to get system connection secrets- nm: fix crashes with out-of-tree modules that provide no driver link (rh #492246) - nm: fix USB modem probing on recent udev versions- nm: fix communication with Option GT Max 3.6 mobile broadband cards - nm: fix communication with Huawei mobile broadband cards (rh #487663) - nm: don't look up hostname when HOSTNAME=localhost unless asked (rh #490184) - nm: fix crash during IP4 configuration (rh #491620) - nm: ignore ONBOOT=no for minimal ifcfg files (f9 & f10 only) (rh #489398) - applet: updated translations- nm: work around unhandled device removals due to missing HAL events (rh #484530) - nm: improve handling of multiple modem ports - nm: support for Sony Ericsson F3507g / MD300 and Dell 5530 - applet: updated translations- Missing ONBOOT should actually mean ONBOOT=yes (rh #489422)- Fix conflict with NetworkManager-openconnect (rh #489271) - Fix possible crash when resynchronizing devices if HAL restarts- nm: make default wired "Auto ethX" connection modifiable if an enabled system settings plugin supports modifying connections (rh #485555) - nm: manpage fixes (rh #447233) - nm: CVE-2009-0365 - GetSecrets disclosure - applet: CVE-2009-0578 - local users can modify the connection settings - applet: fix inability to choose WPA Ad-Hoc networks from the menu - ifcfg-rh: add read-only support for WPA-PSK connections- Fix getting secrets for system connections (rh #486696) - More compatible modem autodetection - Better handle minimal ifcfg files- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild- Use IFF_LOWER_UP for carrier detect instead of IFF_RUNNING - Add small delay before probing cdc-acm driven mobile broadband devices- Fix PEAP version selection in the applet (rh #468844) - Match hostname behavior to 'network' service when hostname is localhost (rh #441453)- Fix 'noreplace' for nm-system-settings.conf- Update to 0.7.1rc1 - nm: support for Huawei E160G mobile broadband devices (rh #466177) - nm: fix misleading routing error message (rh #477916) - nm: fix issues with 32-character SSIDs (rh #485312) - nm: allow root to activate user connections - nm: automatic modem detection with udev-extras - nm: massive manpage rewrite - applet: fix crash when showing the CA certificate ignore dialog a second time - applet: clear keyring items when deleting a connection - applet: fix max signal strength calculation in menu (rh #475123) - applet: fix VPN export (rh #480496)- applet: fix blank VPN connection message bubbles - applet: better handling of VPN routing on update - applet: silence pointless warning (rh #484136) - applet: desensitize devices in the menu until they are ready (rh #483879) - nm: Expose WINS servers in the IP4Config over D-Bus - nm: Better handling of GSM Mobile Broadband modem initialization - nm: Handle DHCP Classless Static Routes (RFC 3442) - nm: Fix Mobile Broadband and PPPoE to always use 'noauth' - nm: Better compatibility with older dual-SSID AP configurations (rh #445369) - nm: Mark nm-system-settings.conf as config (rh #465633) - nm-tool: Show VPN connection information - ifcfg-rh: Silence message about ignoring loopback config (rh #484060) - ifcfg-rh: Fix issue with wrong gateway for system connections (rh #476089)- Update to 0.7.1 pre-release - Allow connections to be ignored when determining the default route (rh #476089) - Own /usr/share/gnome-vpn-properties (rh #477155) - Fix log flooding due to netlink errors (rh #459205) - Pass connection UUID to dispatcher scripts via the environment - Fix possible crash after deactivating a VPN connection - Fix issues with editing wired 802.1x connections - Fix issues when using PKCS#12 certificates with 802.1x connections- API and documentation updates - Fix PIN handling on 'hso' mobile broadband devices- Fix PIN/PUK issues with high-speed Option HSDPA mobile broadband cards - Fix desensitized OK button when asking for wireless keys- Fix issues reading ifcfg files - Previously fixed: - Doesn't send DHCP hostname (rh #469336) - 'Auto eth0' forgets settings (rh #468612) - DHCP renewal sometimes breaks VPN (rh #471852) - Connection editor menu item in the wrong place (rh #471495) - Cannot make system-wide connections (rh #471308)- Update to NetworkManager 0.7.0 RC2 - Handle gateways on a different subnet from the interface - Clear VPN secrets on connection failure to ensure they are requested again (rh #429287) - Add support for PKCS#12 private keys (rh #462705) - Fix mangling of VPN's default route on DHCP renew - Fix type detection of qemu/kvm network devices (rh #466340) - Clear up netmask/prefix confusion in the connection editor - Make the secrets dialog go away when it's not needed - Fix inability to add system connections (rh #471308)- More reliable mobile broadband card initialization - Handle mobile broadband PINs correctly when PPP passwords are also used - Additional PolicyKit integration for editing system connections - Close the applet menu if a keyring password is needed (rh #353451)- Fix issues with hostname during anaconda installation (rh #461933) - Fix Ad-Hoc WPA connections (rh #461197) - Don't require gnome-panel or gnome-panel-devel (rh #427834) - Fix determination of WPA encryption capabilities on some cards - Fix conflicts with PPTP and vpnc plugins - Allow .cer file extensions when choosing certificates- Fix conflicts for older PPTP VPN plugins- Ensure that mobile broadband cards are powered up before trying to use them - Hostname changing support (rh #441453) - Fix mobile broadband secret requests to happen less often - Better handling of default devices and default routes - Better information in tooltips and notifications - Various UI cleanups; hide widgets that aren't used (rh #465397, rh #465395) - Accept different separators for DNS servers and searches - Make applet's icon accurately reflect signal strength of the current AP- Fix connection comparison that could cause changes to get overwritten (rh #464417)- Fix handling of VPN settings on upgrade (rh #460730, bgo #553465)- Fix hang when reading system connections from ifcfg files- Fix WPA Ad-Hoc connections- Fix parsing of DOMAIN in ifcfg files (rh #459370) - Fix reconnection to mobile broadband networks after an auth failure - Fix recognition of timeouts of PPP during mobile broadband connection - More compatible connection sharing (rh #458625) - Fix DHCP in minimal environments without glibc locale information installed - Add support for Option mobile broadband devices (like iCON 225 and iCON 7.2) - Add IP4 config information to dispatcher script environment - Merge WEP ASCII and Hex key types for cleaner UI - Pre-fill PPPoE password when authentication fails - Fixed some changes not getting saved in the connection editor - Accept both prefix and netmask in the conection editor's IPv4 page- Fix issue with mobile broadband connections that don't require authentication- Expose DHCP-returned options over D-Bus and to dispatcher scripts - Add support for customized static routes - Handle multiple concurrent 3G or PPPoE connections - Fix GSM/CDMA username and password issues - Better handling of unmanaged devices from ifcfg files - Fix timeout handling of errors during 3G connections - Fix some routing issues (rh #456685) - Fix applet crashes after removing a device (rh #457380)- Convert stored IPv4 static IP addresses to new prefix-based scheme automatically - Fix pppd connections to some 3G providers (rh #455348) - Make PPPoE "Show Password" option work - Hide IPv4 config options that don't make sense in certain configurations- Expose server-returned DHCP options via D-Bus - Use avahi-autoipd rather than old built-in IPv4LL implementation - Send hostname to DHCP server if provided (DHCP_HOSTNAME ifcfg option) - Support sending DHCP Client Identifier to DHCP server - Allow forcing 802.1x PEAP Label to '0' - Make connection sharing more robust - Show status for shared and Ad-Hoc connections if no other connection is active- Drop explicit hal dep in -gnome- Move VPN configuration into connection editor - Fix mobile broadband username/password issues - Fix issues with broken rfkill setups (rh #448889) - Honor APN setting for GSM mobile broadband configurations - Fix adding CDMA connections in the connection editor- Update to latest SVN - Enable connection sharing - Respect VPN-provided routes- Move NM later in the shutdown process (rh #449070) - Move libnm-util into a subpackage to allow NM to be removed more easily (rh #351101)- Read global gateway from /etc/sysconfig/network if missing (rh #446527) - nm-system-settings now terminates when dbus goes away (rh #444976)- Fix initial carrier state detection on devices that are already up (rh #134886)- Restore behavior of marking wifi devices as "down" when disabling wireless - Fix a crash on resume when a VPN was active when going to sleep- Fix issues with the Fedora plugin not noticing changes made by system-config-network (rh #444502) - Allow autoconnection of GSM and CDMA connections - Multiple IP address support for user connections - Fixes for Mobile Broadband cards that return line speed on connect - Implement PIN entry for GSM mobile broadband connections - Fix crash when editing unencrypted WiFi connections in the connection editor- Clean up the dispatcher now that it's service is gone (rh #444798)- Fix asking applets for the GSM PIN/PUK- Guess WEP key type in applet when asking for new keys - Correct OK button sensitivity in applet when asking for new WEP keys- Fix issues with Mobile Broadband connections caused by device init race patch- Fix device initialization race that caused ethernet devices to get stuck on startup - Fix PPPoE connections not showing up in the applet - Fix disabled OK button in connection editor some wireless and IP4 settings - Don't exit if HAL isn't up yet; wait for it - Fix a suspend/resume crash- Don't ask for wireless keys when the driver sends disconnect events during association; wait until the entire assocation times out - Replace dispatcher daemon with D-Bus activated callout - Fix parsing of DNS2 and DNS3 ifcfg file items - Execute dispatcher scripts in alphabetical order - Be active at runlevel 2 - Hook up MAC address widgets for wired & wireless; and BSSID widget for wireless - Pre-populate anonymous identity and phase2 widgets correctly - Clear out unused connection keys from GConf- Don't select devices without a default gateway as the default route (rh #437338) - Fill in broadcast address if not specified (rh #443474) - Respect manual VPN IPv4 configuration options - Show Connection Information for the device with the default route only- Add dbus-glib-devel BuildRequires for NetworkManager-glib-devel (rh #442978) - Add PPP settings page to connection editor - Fix a few crashes with PPPoE - Fix active connection state changes that confused clients- Fix build in pppd-plugin- PPPoE authentication fixes - More robust handing of mobile broadband device communications- Honor options from /etc/sysconfig/network for blocking until network is up- Turn on Add/Edit in the connection editor - Don't flush or change IPv6 addresses or routes - Enhance nm-online tool - Some serial communication fixes for mobile broadband- Fix issues with VPN passwords not getting found- Fix builds due to glib2 breakage of GStaticMutex with gcc 4.3- Fix WEP key index handling in UI - Fix handling of NM_CONTROLLED in ifcfg files - Show device managed state in applet menu - Show wireless enabled state in applet menu - Better handling of default DHCP connections for wired devices - Fix loading of connection editor on KDE (rh #435344)- Honor MAC address locking for wired & wireless devices- Show VPN failures - Support Static WEP key indexes - Fix parsing of WEP keys from ifcfg files - Pre-fill wireless security UI bits in connection editor and applet- Grab system settings from /etc/sysconfig/network-scripts, not from profiles- Fix crashes when returning VPN secrets from the applet to NM- Fix crashes on suspend/resume and exit (rh #437426) - Ensure there's always an option to chose the wired device - Never set default route via an IPv4 link-local addressed device (rh #437338)- Fix DHCP rebind behavior - Preliminary PPPoE support- Fix gnome-icon-theme Requires, should be on gnome subpackage- Honor DHCP rebinds - Multiple active device support - Better error handling of mobile broadband connection failures - Allow use of interface-specific dhclient config files - Recognize system settings which have no TYPE item- Fix crash of nm-system-settings on malformed ifcfg files (rh #434919) - Require gnome-icon-theme to pick up lock.png (rh #435344) - Fix applet segfault after connection removal via connection editor or GConf- Don't create multiple connections for hidden access points - Fix scanning behavior- Rework connection editor connection list- Better handling of changes in the profile directory by the system settings serivce- Enable system settings service - Allow explicit disconnection of mobile broadband devices - Fix applet memory leaks (rh #430178) - Applet Connection Information dialog tweaks (gnome.org #505899) - Filter input characters to passphrase/key entry (gnome.org #332951) - Fix applet focus stealing prevention behavior- Add CDMA mobile broadband support (if supported by HAL) - Rework applet connection and icon handling - Enable connection editor (only for deleting connections)- Fix crash when activating a mobile broadband connection - Better handling of non-SSID-broadcasting APs on kernels that support it (gnome.org #464215) (rh #373841) - Honor DHCP-server provided MTU if present (gnome.org #332953) - Use previous DNS settings if the VPN concentrator doesn't provide any (gnome.org #346833)- Fix WPA passphrase hashing on big endian (PPC, Sparc, etc) (rh #426233)- Fixes to work better with new libnl (rh #401761)- Fix WPA/WPA2 Enterprise Phase2 connections (rh #388471)- Fix applet connection comparison which failed to send connection updated signals to NM in some cases - Make VPN connection applet more robust against plugin failures- 64-bit -Wall compile fixes- Fix applet crash when choosing to ignore the CA certificate (rh #359001) - Fix applet crash when editing VPN properties and VPN connection failures (rh #409351) - Add file filter name in certificate file picker dialog (rh #410201) - No longer start named when starting NM (rh #381571)- Fix upgrading from an earlier rawhide snap- Fix device descriptions shown in applet menu- Fix crash when deactivating VPN connections- Fix crash and potential infinite nag dialog loop when ignoring CA certificates- Fix crash when ignoring CA certificate for EAP-TLS, EAP-TTLS, and EAP-PEAP- Fix connections when picking a WPA Enterprise AP from the menu - Fix issue where applet would provide multiple same connections to NM- Add support for EAP-PEAP (rh #362251) - Fix EAP-TLS private key handling- Clarify naming of WPA & WPA2 Personal encryption options (rh #374861, rh #373831) - Don't require a CA certificate for applicable EAP methods (rh #359001) - Fix certificate and private key handling for EAP-TTLS and EAP-TLS (rh #323371) - Fix applet crash with USB devices (rh #337191) - Support upgrades from NM 0.6.x GConf settings- Fix applet crash with USB devices that don't advertise a product or vendor (rh #337191)- Fix crash when getting WPA secrets (rh #355041)- Bring up ethernet devices by default if no connections are defined (rh #339201) - Fix crash when switching networks or bringing up secrets dialog (rh #353091) - Fix crash when editing VPN connection properties a second time - Fix crash when cancelling the secrets dialog if another connection was activated in the mean time - Fix disembodied notification bubbles (rh #333391)- Handle PEM certificates - Hide WPA-PSK Type combo since it's as yet unused - Fix applet crash when AP security options changed and old secrets are still in the keyring - Fix applet crash connecting to unencrypted APs via the other network dialog- Fix WPA Enterprise connections that use certificates - Better display of SSIDs in the menu- Fix getting current access point - Fix WPA Enterprise connections - Wireless dialog now defaults to sensible choices based on the connection - Tell nscd to restart if needed, don't silently kill it- Suppress excessive GConf updates which sometimes caused secrets to be cleared at the wrong times, causing connections to fail - Various EAP and LEAP related fixes- Make WPA-EAP and Dynamic WEP options connect successfully - Static IPs are now handled correctly in NM itself- Add Dynamic WEP as a supported authentication/security option- Re-enable "Connect to other network" - Switch to new GUI bits for wireless security config and password entry- Add rfkill functionality - Fix applet crash when choosing wired networks from the menu- Fix segfault with deferred connections - Fix default username with vpnc VPN plugin - Hidden SSID fixes- Fix merging of non-SSID-broadcasting APs into a device's scan list - Speed up opening of the applet menu- New snapshot - Add timestamps to networks to connect to last used wireless network - Turn autoconnect on in the applet - Hidden SSID support - Invalidate failed or cancelled connections again - Fix issues with reactivation of the same device - Handle connection updates in the applet (ex. find new VPN connections) - Fix vertical sizing of menu items - Fix AP list on wireless devices other than the first device in the applet - Fix matching of current AP with the right menu item- New snapshot - Add WPA passphrase support to password dialog - Applet now reflects actual VPN behavior of one active connection - Applet now notices VPN active connections on startup - Fix connections with some WPA and WEP keys- New snapshot - VPN support (only vpnc plugin ported at this time)- New snapshot - Make wired device carrier state work in the applet - Fix handling of errors with unencrypted APs - Fix "frozen" applet icon by reporting NM state better - Fix output of AP frequency in nm-tool- New snapshot - Fix applet icon sizing on start (mclasen) - Fix nm-tool installation (mclasen) - Fix 'state' method call return (#303271) - Fix 40-bit WEP keys (again) - Fix loop when secrets were wrong/invalid - Fix applet crash when clicking Cancel in the password dialog - Ensure NM doesn't get stuck waiting for the supplicant to re-appear if it crashes or goes away - Make VPN properties applet work again - Increase timeout for network password entry- New snapshot (fix unencrypted & 40 bit WEP)- New snapshot- New snapshot- New SVN snapshot of 0.7 that sucks less- Update to SVN snapshot of 0.7- Update the license tag- Own /etc/NetworkManager/dispatcher.d and /etc/NetworkManager/VPN (#234004)- Fix Wireless Enabled checkbox when no killswitches are present- Update to stable branch snapshot: - More fixes for ethernet link detection (gnome #354565, rh #194124) - Support for HAL-detected rfkill switches- Fix applet crash on 64-bit platforms when choosing "Connect to other wireless network..." (gnome.org #435036) - Add debug output for ethernet device link changes- Fix ethernet link detection (gnome #354565, rh #194124) - Fix perpetual credentials request with private key passwords in the applet - Sleep a bit before activating wireless cards to work around driver bugs- Don't spawn wpa_supplicant with -o- Fix requires macro (237806)- Update to 0.6.5 final - Don't lose scanned security information- Update from trunk * Updated translations * Cleaned-up VPN properties dialogs * Fix 64-bit kernel leakage issues in WEXT * Don't capture and redirect wpa_supplicant log output- Close private D-Bus connections. (#232691)- Fix a directory ownership issue. (#233763)- Update to pre-0.6.5 snapshot- Guard against D-Bus LimitExceeded messages- Move .so file to -devel package- Own the /etc/NetworkManager/dispatcher.d directory - Require pkgconfig for the -devel packages - Fix compilation with dbus 1.0- Update to a stable branch snapshot - Gnome applet timeout/redraw suppression when idle - Backport of LEAP patch from HEAD (from Thiago Bauermann) - Backport of asynchronous scanning patch from HEAD - Make renaming of VPN connections work (from Tambet Ingo) - Dial down wpa_supplicant debug spew - Cleanup of key/passphrase request scenarios (from Valentine Sinitsyn) - Shut down VPN connections on logout (from Robert Love) - Fix WPA passphrase hashing on PPC- Own /usr/share/NetworkManager and /usr/include/NetworkManager- Don't wake up to redraw if NM is inactive (#204850)- add epochs in requirements- Fix FC-5 buildreqs- Revert FC6 to latest stable NM - Update to stable snapshot - Remove bind/caching-nameserver hard requirement- BuildRequire wireless-tools-devel and perl-XML-Parser - Update the BuildRoot tag- add patch to make networkmanager less verbose (bug 202832)- actually make the patch in 0.7.0-0.cvs20060529.4 apply- Don't ever elect inactive wired devices (bug 194124).- Add patch to fix deprecated dbus functions- Add BR for dbus-glib-devel- rebuild- Update to latest CVS o Gnome.org #333420: dialog do not have window icons o Gnome.org #336913: HIG tweaks for vpn properties pages o Gnome.org #336846: HIG tweaks for nm-vpn-properties o Gnome.org #336847: some bugs in nm-vpn-properties args parsing o Gnome.org #341306: nm-vpn-properties crashes on startup o Gnome.org #341263: Version 0.6.2-0ubuntu5 crashes on nm_device_802_11_wireless_get_type o Gnome.org #341297: displays repeated keyring dialogs on resume from suspend o Gnome.org #342400: Building libnm-util --without-gcrypt results in linker error o Gnome.org #342398: Eleminate Gnome dependency for NetworkManager o Gnome.org #336532: declaration of 'link' shadows a global declaration - Specfile fixes (#rh187489#)- Update to latest CVS - Drop special-case-madwifi.patch, since WEXT code is in madwifi-ng trunk now- use the same 0.6.2 tarball as FC5, so we have the same VPN interface (did he fire ten args, or only nine?)- use the hal device type instead of poking via ioctl so that wireless devices are properly detected even if the kill switch has been used- Update to 0.6.2: * Fix various WPA-related bugs * Clean up leaks * Increased DHCP timeout to account for slow DHCP servers, or STP-enabled switches * Allow applet to reconnect on dbus restarts * Add "Dynamic WEP" support * Allow hiding of password/key entry text * More responsive connection switching- Fix device bringup on resume- Don't let wpa_supplicant perform scanning with non-WPA drivers- Update to 0.6.0 release - Move autostart file to /usr/share/gnome/autostart- updated cvs snapshot. seems to make airo much less neurotic- Move the unversioned libnm_glib.so to the -devel package- Fix VPN-related crash - Fix issue where NM would refuse to activate a VPN connection once it had timed out - Log wpa_supplicant output for better debugging- Tweak three-scan-prune.patch- Don't prune networks until they've gone MIA for three scans, not one.- Update snapshot, which fixes up the libnotify stuff.- Move libnotify requires to NetworkManager-gnome, not core NM package- Add BuildRequires: libnl-devel (#rh179438#) - Fix libnm_glib to not clobber an application's existing dbus connection (#rh177546#, gnome.org #326572) - libnotify support - AP compatibility fixes- Minor bug fixes - Update to VPN dbus API for passing user-defined routes to vpn service- Rebuild- rebuilt for new gcc4.1 snapshot and glibc changes- Workarounds for madwifi/Atheros cards - Do better with non-SSID-broadcasting access points - Fix hangs when access points change settings- Own /var/run/NetworkManager, fix SELinux issues- Switch to autostarting the applet instead of having it be session-managed - Work better with non-broadcasting access points - Add more manufacturer default SSIDs to the blacklist- Longer association timeout - Fix some SELinux issues - General bug and cosmetic fixes- Snapshot from CVS - WPA Support! Woohoo!- rebuilt- rebuild for new dbus- Don't kill the network connection when you upgrade the package.- Split out the -glib subpackage to have a -glib-devel package as well - Add epoch to version requirements for bind and wireless-tools - Update URL of project- NetworkManager 0.5.1- NetworkManager 0.5.0- Fix automatic wireless connections - Remove usage of NMLoadModules callout, no longer needed - Try to fix deadlock when menu is down and keyring dialog pops up- Update to latest CVS o Integrate connection progress with applet icon (Chris Aillon) o More information in "Connection Information" dialog (Robert Love) o Shorten time taken to sleep o Make applet icon wireless strength levels a bit more realistic o Talk to named using DBUS rather than spawning our own - You need to add "-D" to the OPTIONS line in /etc/sysconfig/named - You need to set named to start as a service on startup- Update to current CVS to fix issues with routing table and /sbin/ip- update to current CVS and rebuild (workaround for #168120)- Fix occasional hang in NM caused by the applet- Update to NetworkManager 0.4.1- Rebuild against new cairo/gtk- Update to latest CVS o Use DHCP server address as gateway address if the DHCP server doesn't give us a gateway address #rh165698# o Fixes to the applet (Robert Love) o Better caching of information in the applet (Bill Moss) o Generate automatic suggested Ad-Hoc network name from machine's hostname (Robert Love) o Update all network information on successfull connect, not just authentication method- Update to latest CVS to get fix for bug 165683.- Move pkgconfig file to devel package (#162316, thanks to Michael Schwendt)- Update to latest CVS to get latest VPN interface settings to satisfy BuildReq for NetworkManager-vpnc in Fedora Extras Development - Latest CVS also contains various bug- and UI-fixes- Update to latest CVS o VPN connection import/export capability o Fix up some menu item names - Move nm-vpn-properties.glade to the gnome subpackage- Update to latest CVS o Clean up wording in Wireless Network Discovery menu o Robert Love's applet beautify patch- Update to latest CVS- Fix dispatcher and applet CFLAGS so they gets compiled with FORTIFY_SOURCE- Fix segfault in NetworkManagerDispatcher, add an initscript for it- Fix condition that may have resulted in DHCP client returning success when it really timed out- Enable OK button correctly in Passphrase and Other Networks dialogs when using ASCII or Hex WEP keys- #rh154391# NetworkManager dies on startup (don't force-kill nifd)- Fix leak of a socket in DHCP code- Fix some memory leaks (Tom Parker) - Join to threads rather than spinning for their completion (Tom Parker) - Fix misuse of a g_assert() (Colin Walters) - Fix return checking of an ioctl() (Bill Moss) - Better detection and matching of hidden access points (Bill Moss) - Don't use varargs, and therefore don't crash on PPC (Peter Jones)- fix build with newer dbus- silence %post- #rh153234# NetworkManager quits/cores just as a connection is made- Update from latest CVS HEAD- Update the GTK+ theme icon cache on (un)install- Pull from latest CVS HEAD- Upload new source tarball (woops)- Pull from latest CVS HEAD (hopefully works again)- Pull from latest CVS HEAD - Commit broken NetworkManager to satisfy to dbus dependency- Pull from latest CVS HEAD - Rebuild for gcc 4.0- Update from CVS- Fix free of invalid pointer for multiple search domains- Never automatically choose a device that doesn't support carrier detection - Add right-click menu to applet, can now "Pause/Resume" scanning through it - Fix DHCP Renew/Rebind timeouts - Fix frequency cycling problem on some cards, even when scanning was off - Play better with IPv6 - Don't send kernel version in DHCP packets, and ensure DHCP packets are at least 300 bytes in length to work around broken router - New DHCP options D-BUS API by Dan Reed - Handle multiple domain search options in DHCP responses- Display wireless network name in applet tooltip - Hopefully fix double-default-route problem - Write out valid resolv.conf when we exit - Make multi-domain search options work - Rework signal strength code to be WEXT conformant, if strength is still wierd then its 95% surely a driver problem - Fix annoying instances of suddenly dropping and reactivating a wireless device (Cisco cards were worst offenders here) - Fix some instances of NetworkManager not remembering your WEP key - Fix some races between NetworkManager and NetworkManagerInfo where NetworkManager wouldn't recognize changes in the allowed list - Don't shove Ad-Hoc Access Point MAC addresses into GConf- Play nice with dbus 0.23 - Update our list of Allowed Wireless Networks more quickly- Update to latest CVS - Make sure we start as late as possible so that we ensure dbus & HAL are already around - Fix race in initial device activation- rebuilt against new wireless tool- Fix issue where NM wouldn't recognize that access points were encrypted, and then would try to connect without encryption - Refine packaging to put client library in separate package - Remove bind+caching-nameserver dep for FC-3, use 'nscd -i hosts' instead. DNS queries may timeout now right after device activation due to this change.- Update to latest CVS - Fixes to DHCP code - Link-Local (ZeroConf/Rendezvous) support - Use bind in "caching-nameserver" mode to work around stupidity in glibc's resolver library not recognizing resolv.conf changes - #rh144818# Clean up the specfile (Patch from Matthias Saou) - Ad-Hoc mode support with Link-Local addressing only (for now) - Fixes for device activation race conditions - Wireless scanning in separate thread- Update to CVS - Updates to link detection, DHCP code - Remove NMLaunchHelper so we start up faster and don't block for a connection. This means services that depend on the network may fail if they start right after NM - Make sure DHCP renew/rebinding works- Update to CVS - Fixes to link detection - Better detection of non-ESSID-broadcasting access points - Don't dialog-spam the user if a connection fails- Update to CVS - Much better link detection, works with Open System authentication - Blacklist wireless cards rather than whitelisting them- #rh134893# NetworkManagerInfo and the panel-icon life-cycle - #rh134895# Status icon should hide when in Wired-only mode - #rh134896# Icon code needs rewrite - #rh134897# "Other Networks..." dialog needs implementing - #rh135055# Menu highlights incorrectly in NM - #rh135648# segfault with cipsec0 - #rh135722# NetworkManager will not allow zaurus to sync via usb0 - #rh135999# NetworkManager-0.3.1 will not connect to 128 wep - #rh136866# applet needs tooltips - #rh137047# lots of applets, yay! - #rh137341# Network Manager dies after disconnecting from wired network second time - Better checking for wireless devices - Fix some memleaks - Fix issues with dhclient declining an offered address - Fix an activation thread deadlock - More accurately detect "Other wireless networks" that are encrypted - Don't bring devices down as much, won't hotplug-spam as much anymore about firmware - Add a "network not found" dialog when the user chooses a network that could not be connected to- Fix escaping of ESSIDs in gconf- minor point release to improve error handling and translations- Update from CVS, version 0.3- Update from CVS - Improvements: o Better link checking on wireless cards o Panel applet now a Notification Area icon o Static IP configuration support- Update from CVS- Require gnome-panel, not gnome-panel-devel - Turn off by default- Update to 0.2- spec-changes to req glib2 instead of glib- First public releaseNetworkManagerNetworkManager-bt1:1.26.0-8.el81:1.26.0-8.el81:0.9.9.95-1.build-id8702925be4f0737ec8739f22fdd47ceff5c28200libnm-device-plugin-bluetooth.so/usr/lib//usr/lib/.build-id//usr/lib/.build-id/87//usr/lib64/NetworkManager/1.26.0-8.el8/-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protectioncpioxz2x86_64-redhat-linux-gnudirectoryELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=8702925be4f0737ec8739f22fdd47ceff5c28200, strippedPRRRRR R RRRRR RRR RR RRRutf-8919f7a9ccffab9b7cf4b617adc64cdae87aeab1472e1cd71906d13aacbc7d962?7zXZ !#,W/] b2u jӫ`(xy0QxVjCԎFg>噧<@N@4$D&l6}M(#ڥX  56B2 H2ɿ`RuWᄴ̯b)̠_ָ Z"8ܩ먛Y*Y pCqMq)^ uZ͙%M64tԵtZghg gQ^$fqPo`g|]ur;Y*L#ЉC֚Tɢ=XnsÅM},pt$h#VICu*)'k̼TKFCK$\^-b{U_V [>hǭh Z%Z$FˢT4 XS?_r22FX6%Ei zh$Cv1n=yruJA P~HBs.s6oMMV $ԣs}1tݒys'O[S[=]Xm՘s$TY96Fdom_vItE3du{ˁl @*1(+JHa#`XXnڝ0,E?Y .*-׉'Hl:,Wa/'$2QӝuRB[e`_]L=? ވErI4 r5Ilk 颣ͩك1wߴyћ!,w>P%iݱ }Ng&`7~SwC$K_bZ+nD]]zF*iGYKSʽE_wE3,n3shnz[(x`]"Q-#^OGh i~vL^(BngR/t7@z8i)AFL~$' C;hSqM&Ԑ`%=QOkُY%}JpJhLlLs2ē$vjS+K`Ҽ+pp&һ 5lMW0nfQ@押sڴN;W`䅽/MYVD/"wvp@juIsaE#g *BcgzS3 k414(T!/]Z)}ƘLr 'fM6ϕs1B-L4V//Qg1Ga߰܉ bvVBܠ GR_nr za8tgͽ!wJ~6C^|* cM>L_m ŖKne%Oo6֘\Dܣ΋ʘ*)&"D,$76|Ӷ"<C\ dæ2~Ⱥ(xWbi -$B fxg^ [6*Ʉ@˶A!RKZ)On3Кg8t񀁶%T>Beok N#\p,j=У)x\O(:"OGM*} Ha8'tmLxL9ɷ`Pa @_ kI3j`t73z <>9`SY2!0I=6~ۣřrj$r2bC΁W ʝh{LJ*+oMw[/-/޻|hA#e!=?fudlg *hEB k9ߊT!ǁ[o6E_o->hZT# ^~P&d#Mz%^#uRd{m时hT,$kE8~hn n p,!c ;$v41n$d2-C-]EBj2v='!?)XX^p]i:}ZGSӒ'Jyl3p<ܤ;oH)w{VjsLa*sׅk j嘳։ͦ޾UUQs}㌣n"ҪS!V /+F" EFpd~$ߑ0I-<K}+x#b 3UA1[< ku V;&Qz>sdYoExE|yqn9Mte3D;_Y h}>kYST~t`dXjm3 lAyoq4YkˏϜGI\*.DA .Dhϗ>H%6N^Z= y8'#h5?lGS.c⽗Q٢K1Up|Z[@U_W|u VBöǩA;BcY { c0PAnK+׭"WHr-!tp%g/:? jPj$QؾW*TɊ G02juWFDb~-޶ouɗSW 'lm-tDCZb!Ջ_Tߪ@[!򦾦%a=_1[qSޕ4䏾@4Zn&gV6"L;0uHqtV z4<Ry~$R>nJ^Z,#'г=25Т"R~UHDNt`b@$c?:f cUN82y:s({.wV#LS=znWM;z-5} 2묻 -Q/c6}3e ;?~Q4@ bwȱ¼p.Ԙ67O~pͷtBםf@\dq.tRk7= Peyr=#Gn f1cܺQh"e?<\[ <:fμ1a 6z~Vf8bZ4in!:Vjzuez/΂~_[6+UxӆĽRX*y*;7!NH2e im(H`ς;|_#A ;ApJ[Khc uMx~:5RC~/s^UC^W [QƊ fwLzk^ߝmn&f}U=/{3\R$Nz􌳊2Db$eQ8< G*ks3I/ bxYf0w~$;5²kŸB νʹjgְҺU66lwWDT=?$v^7Ygbȷ@!jCs%[C#~s A|"m[,V(-;nB"%vo[hϢy"?A |; 2ͦ|ssDݹz c, YL =%, wgDc5І)T!~Жz+wIk ُ4G~O2,eQB6ui>J$wt\6[ 3;b|FaWE#YFKU+㢣Jc`ƴ)vTP~ЁEs+'1Оĝn??~?221x՚"4OM{Ҏ45c1Ҍ_Q0C Tl؜9wt9kvזx6E?`JBP\ D[(OOLgS2x6r!beĘV'*-j~."nbxpfsm( ikxzg.\cj mǍ&d4be18%1TWci;=VeݱLl$d?[f]S2 R/Uq,J9~Lǿ/" Od5c3*|U!P2j{3ko^W;a0Z o6OtX E[ ,GR*c|ӹZ87+EOp8ͬYo!m nUfM\(HyEI`yx׎=3f5m%!=O|᭍Sdk ' ,:@+NBE}rlix Ky11Sr”"J Evv)< #,zEV|mZչLAh9'zk BmaO9gSV% } &KFSyԇ=_@X!T=èʩ1_* u`2zhkW6x cq.W*)~f{vmv /Ou='jlǸ\]g;<=X$砲 >Q^(_0@PcV IoĊ˖1A<rJZל^f=C\`:bt9\~ "ryI؆[I &F+Y=uNR:km|y!`nLˌ"bcæd,i3ٍ6p{B*d @׆jB픪 6$6F+8ՕL],-Q3׶DX sb["`Nj٤KYq$X&dMIs$n%^b,rd;k|^ؕјAɡ}q N>+1 7NIivI$00LDX[.,;B%̱y^oO6,Wl9gYϼD*'qwb㵍rvxSCd"A]:/ o@Ղl:2{RC w_&̒~\vP[H^>HūɡRTdhgv ')7mu.ݾkǒ`V<0w?GRn Z55E7zw2iזm$29ͬ;eVQB´k఺?Jaaw<X8IW}~iz6w.qEI:;;z*-r4Tؕަ\] i~S4` Ve[my+f&&kk@ޥM'eeܦAsO,QŽr}DQ#zCMɍ}GQӏJ(>hUwDƩ> (m2a~'Xp)棠ZSIzL[IpV*ӡA[b#BMd{7L T8Nޅ=kڸQ= ضZϟl{ȫ K'/)]/fnk?x@ p$[j d{#TnJzZӿj43#M!i2ƗP0sA?>o[U` 3 N\Kd'v2,mCPYdl ·+{قt Q~/ <]co֠^e E̩{MՌt9X|9_p}z5wjTʣ@-L_ʮnXֽzdg 6hG]y @ a5O{nX2!}?Eq9).O.w۲ȍN nIlAL#-./}h] Ne>p`?V8CWh+h&*F_AF䭬NLYb9p3k˶ߣ=_4ؗ)ïqMAĨN5٪^͵1":70ix-X toMh+t4h7I$- hۈ5R|s{GrO3)l*P-,{f0 YO 4-ʮ/kG݇nvxcr5vpԼv=%\EGc{|/, KO / ̉;um~#c+{= m\l;b?]ޜA$z24,v$m> 4YJtIdLarDz+>F_5LoAbD`m2#;L,l@):'G0)q1W0҂ܱ_k"\J(]f)r#M;j0UՏ [M~n _v͍4p`$Q je-8 )|Q-^8@V-}p |`LWTPA۸[bIAv4ܟ7TL+gX>p=.Qq4bJGIRvdMWt]{Rؓ%s ny'/lesɨ89)guEi3dO7? B3GNeUPN_g9nj@Zm'uS;6oMa1eSjӸGIE^aɑ~_j)5(N˽cѥ8LT|;y]#Qzҹ=g?ǯr3-%d{U7he7ȎJ`~&{U>QPc;HS>̀>V3 s~c勞Bb0A&=r=V QQWR 5? P#uܝ&scn_o}gxQ-vIgG8J} g3hclI5\%.x&H+%U~-2oabLדա]XM@ cݲ(2Qm͡ekgYwn6L@8tҊVU\꺿X5'nX8 L_N_h8.HH+7ff}k9ht hqSA[&(@)&VyA°z!ɐrwT [uw% ўuaG^@#ȗ@T}E{}Zrp+'~F<]i$pa"-A/n4__ $Wf̶6~)Ɲs 7&D,`>،Ng.*:cHbQ%P1lC" 2/ rC.\eӄ :[R [T0U%M-ۢE ?Y'Ìd@ұLeV E?G-Dk)do$g]7a \"JQ'6C>~?;-Q@lC:(15guU lHtfEHl0VI7-E̡!dN^3B}eODGOe1il} =\ oL^c}.[tb%j"`Q^Pʰ"I砖 K ť:a4ClFst=6;/?۩C@ҟF״1up0kG#AR܍1:.G]3dve%7~GDzT/]D,/BvY ڊ}[#ge"wK]20<[nh=&6ʍ`7pLlA;"buS}eOtĭڟ&9ZIk8-뒾o}p +9Z 9 @ X f AHB ƁuzP&m@5A4AP#+'yeu3Ķ9CҚA01k ,fVfY2N~=V0IR@n)S-y/C^p4GsƟWkS'?jwe&J6 ;ܫ$@ N}cȹIC CA_Y_ǧ9DGi#*HBRc `:(+[ppx)`|jfcvuAdzJ֘9o|$M;:)aʪq of.z R0} yw*;D3#a=Xz ~]# -yq9n,qm.Z &I\(㣣WW zى^e;I=~RS&\?ț :Y #gc]I w#.O!K,o%X!( qIZ/a? &LuKzacb[..yC^)7B9/ 8ԭ?];I[) B,.Dʑ6l4RN[UQO- #~O2$]4OO 9rG2[S8&ݍm.oVk~I%ȿd}lԦ8]f$& Ym#9>cX0MӉrode{i+YȤ g~TQW3D㺊mȚ/9ub@kFHRB}u8>}"jT6&~30(۱imWts`CRkF&(8NYGj xiWz;&- Ӕ?,d[Ù!Z$>M"Goېrɽ/|; VA\U6av;E{a)b߻z Zy=it##@n6,)ă uB:AimZړU,~LX~`wD#]hǦN[Nb~d" 6 O^ `pMy 麧dB81̲e%8l,57Μ{Aװy96v㓡u]azBP&P+\,P ;2\8uq!'tҿBI2W).wheGmv"KQAwvbt> 2kbnx_lt뾦P{wiOe!bQMM)r0HQϡ7%EX*3f{4::-]e^KZ1}fˠ>ls 8 _ɺ>zg0]S_Y mA$&`p}{* YzvآuA܈wm-XGiAfa `e 0inW웗8a3rL!C^ ?=:Bdn}Nys@ןꫬ)@<^F]lwRVÖHfZ۾1[T6bv&-I~'~յȭI \dPVa;LNIRV5bRY.}CF\ю$S>gzVбxrϐ>/ז+h4ǵ4o.֡4䂟d.elL/'w_~ . ~bx[d֪vS 4w2 -j!%+K^جnyT|C A ,OԬ|PhȔU6"gݎ8溒 *#-*"ۛ368&rϹ WO{[*s;W>®'&æg]!#2VJ3~ֶ\j}mv\N"_ n_c5%'w⫕Mi]w+ {@J^/MѦ`[T)QÉvdyOM a+ۨg6_sjQɂӔhij_ YNFN<$[LJCڧƟ1Dk '\o?ߐfmScX<:ibƍj#!?8Q!#>Uf$sQ T_) xl#\v!eYƾ2z2~Afv|[ʆRa-ߌQX Y4^2"@#0Utko2~?,33u?HB"=2'xU83!ŻWYQn(wSN{ѣ Dԁvg+Lbۂ0(:ᤛ*L~| |gL'Xz/Wp\ [I,l`*ה ߮VSqZx*=u!` Gԯ/U:al Lf =\-*jW_ATD2D Lי`#tx9Dwr0~)$HgKYw.ZY/e7ކ#$RUljs0G;@ 6kj RO*J2TeehЄtEq ϭNW]Z@Ҩ7_w]33v3J-]Bǝ Ma'勮ǥ)׊ͫXA2nK`[ɹ Ry239~ vlryccHRJWyYpmU;ʁ=V?<+e4Џ[>է'll)#h6o1wWU .BdtP$p?#1RtG_< .e䞾,*[^,=ǻdӏzϐ V6]sr^oG% -QΐɛkŢ8娊W'!?t07@$aO+k'#7Y(s< &L7U m3ǭl+@ YoxZ3mIh[5Ⱥ Ol.b.[. `g17͊fcKWm"Q:Ga5e7d+f{= 37+J}mKAoncOh9UpDP3ԼxT1U"YtLs!UςԭGGT,iwfLg RmUTrjQbz]Ţ`^rPe\pT*k>DL=ڭx> AYxh&faY}3Y %\ca4rTVMg/!.aR鐧TOr~p% 'Irgbn D _q NKyҢqdDp#*Ƨ% (@zL Qq Fh{_ ʐTן%< >,Eѷ9)_U5vStjq_'z=[C c0Xd{Iai+YĵSnK.hc<-s0^* /PFCfIvqT*U1L;rz]{DI_YCftSu8e}My J:,۷ X{wFG;=y݁Cp+w=kʹԎ||TYS]Ψa`-#Y]lSD1c9~&i 0@0 ᦷĎTim_Uh.S(&qۇǂ#N )=MqMꞒ5U\G{rb+Eߣ6DRxJ[EiA[~f~1jW =iY0@lP`| !'b6~&\!'Ioc胴\h[V'" 2xwP$].'-lS:Ʃ h!=O0]@8 a!uoKJ9۸`Dt86o>8h] ƍiz25Q2넧fcʹXm6-p?6r0;S}\k6|0bFTЋ(,K>QU()_"|4@DE84H*~ 0C@q ƛ[AͼuA?W䫭L#/2q =-\oi UfOV1`]!i~7鰰UFRh o53>Ӊ/dcI;h82f0/T-D2ە6s饽ƭVvPG6%R;DeN%c}zQ$EB4M A?8Jy=/ ׾r -mF CItj"heSs,TJgFʸ4~H!:N?2u%,!$M57k: o|M!aJ]^>ylPM`euuQ [E\2H{ snA^Ilv-l&ӨQD5k3VױgM"z3PX/r CITQA">.N`t=]Z$:Jv~6,l~ߒKJ;ќ1ji"aBX7t*{_ W3"l.3e A5vQH$|)Wd}Mo"e=~¹+%<Hr&okxy<2ȯV&oJ!d7mKj)jҭ|355wC<P9U#2ё O Z@`X6;F5 bx5&n o4t-h59v F0uNG[5R""Jqҽ $,vEʓZ xo$b!,9eުwCoN" @^g5RK K@Q+L^m*ҵXC\OjN*:?}^B\s 2G5̠:yrJ+,TN`2;y Iٌ|NA$vBX[EKө'kW)7/[6ta#mts׈trӝU\Mrł٦M#{|?ĊG#,+π$ o;?W#̕+^Hf—JthU* <~>x"8gn* 6]$z 4#| bWֶ4vp}= &hnژO~_.$e L:J,Pī%a*!qVge!aIyc&Zka{5sE[K?R<5P"8IjLJ "HHʡmߌ53F"@}nQvDRg2. 툷;iV{ `P㏭L]&ťL<؀s _K@JH/׳Tν,N"_P%?&g46Jze`1e{SĠЊ0Qs&)@өNY0L~wXLϵ'健}b:#5#8BPd bp660^a;UoN!Eg}:5\U}Q. qeV i\%kw1^e{r"EZT;:Dq*;&\ib.F¥X2]F/[kAauDZ^FRX8roA#cZMc~Ɨ8tP\pg?MxBU 螕i412 D^E 3^Gb[GS9Q L$̢NJ/8@[iDn8C@0=TQEjpWƎ߿v?^ȅ-c7o3 p,5]%`+. yxw̪ZZ)+'|/tzVݴ{i8fCgCC{CᇭA _S6xPY2/QCSiNzN0CdyxȪ&a0+H}ׁht{QEoi%1 WSY<6w}Y XA8"=$U5$#h6qxz]&'1CI)fku lt6si6M}k/ %RDa8W,xGrSEq5\DO^bŎID6nc45e^S{ܨT1h!tI6#Li"wNյG+u4WrdC=w-r#N̲N(6KbSeEy18;zvo6jfϝ| Hdo1.+s:50}I xGhfb4`,ҥJjUr?RR}LƧ7k<} R{VZnt8Ex))ZCᅳˑcǔ ;?_k93VImo=и,06R!δ0"XhQud' a+ޣ\G˵I)əƮuhCQfDE"S⟳Rv:75'f'0VA~\zdW'Kwp[Xj)JQo5.5 ƤDV]ͳa׺+wnxxoT T!EG`C? ! %@v_/D8{I`/RFU$*BxSƒv*,WuOo<0'L';PV ڜes.SǕ O! tv(6b_iVݹ6 ֧bV}Nø;1\C6) JAr i;)D{fn<ꨤ@ŕ4Aд1EnrSGoǕObyeg:5@m)_"`K<~9'HLhSZ*BIO,J&8]CRqۄ;gkPZhYw LR!  ju:t㉘Oy;\^|8Du/t@w} { z!3Zt~΋:ZHT'Kp7FheJCĐm_Itۊ|]|aC+'fDSQ,%5QabeRx#UG;iuے@f 5.HVtzc @a '( sq>SGhF1J L{|8;)Fa]&{UؼDo,Ͷ#|v.8,ut mmsYVnvP9VyGwm֖`?/̡khq=7UC"7nh6K*=&qa|:m%R0GL0La?^Ը6$J ZT||"yR^w7C;$*Q{^Δ _n!Bϲ>Z$X\2䩡eV*l8d\{ m:Mj_lL)R)bJ`8^W6OC=3[yӿt‹s\*h\gA~s&ETs %O>&Qg&,%k)c?!Xlbb,D;qi~x,P %? TS\bJdwV|ٱ'R,s.X$eڨLN[jgfnslH 36XDxhmN{^||^zqS*= OĬcbIz3N2'7Ghcf*"ԱA"lsPh`~C_<j[۷Gr*ˆhz%_%Hwq/MDמ]-$c*Y]@6@TCWCz.S'edMyX2tr$tqY"uiM?.' b!{f̷q_V*oYit%+ U4g+^6"9u2I%,:E9W7F[$W9O#d#8LZrbb]bfnetc&$.EA\Nr*+s?x 68=AjE䬭}~;mÅ4F'wO.Fe2r"DȋCh!•OHoOsjog_4.L+ֳ;[J($콲M|U9⟥]<6 ] ׬KBܕ+ ,+)Q"TeRJ5S|n8+,%h{MRQI&.~y] @E݄QY 9[AVH%bpmO`q̒G4ed$d\WZ]*ٚ הEɖvz784Xyx 8{F?8kW ?i W7ulBHDCD%ø_ouWT~WGN8 ylG0R[z=ͣeaX!t\slX5~X Wcuɶ]oÂMiA4WuæX.ՔZ+_Cs:_%n8/* _[.>B{wϵCj} ,cz >#҃^|n$֘HQum!(|p)]00U1 K}"Fm>!B,㦒pZ]ʵ@ݟ/v |LQΣVA)7gcsB2p8`-uMW\\I [đ!"&dm7ts]j-vYS,HXU}3Dotگ[)*D9`Jk Yl:zzH1.&8CδB*D ڇbD־v`Z? :hSF]("Y#\Ji\af/2k.3*2M-nQH e_`d{A\lPp&j&GVK],@DhN3gm ALj7UMpߦA:M*oq-DP* 3sT" ~|/ϬO|jHX4kcJd0T~궚4J  GMPWL]t\Ƿ3k=>z,|:޹Z;d=$*$| sW1ơ;frb}]£mJ v;Ph0ņcXkioP:Q1`m[kTKB3:w̒8QPeP`_So~:cIqGl " ҲVnj팥 aBIlY9+;%6 Bp^1 Xq=Qw8_v֞G'u/vŝ .24| -Z6N?Ay+03~Tt0][h&v郳QrS 5.yqXZ@fxgduPb#?;w$BН_B9#&[1}'tTC^A%ɩ.pv?^J5۽О+c-Uz4L aϺb'q9SaCս 5 o;s`X#nKi"C XiwԋRq0eTB(D ŞL3AgJ,vPR}kV0ӫ5ZmKѲ'!d5J"RX)YZMtovn$oBFčb{/L&kUp#ehTh0rȸq$&Uύ37]s[&bTċM(?ۺC̬h5 `gلxlts_O ŽZ nyx$n_XR}ڼxǩ4_, aZcY9uBH7XOP*C;%jNcmKǣg># NBH57F;;EU lx joqCiѮzYD`U e{v430VA5pwQB݇<>qdHJ'ۢ?S6|ys0j )۾[ *0Z7YpS$&-_ |60W̝mC yW;qhԆAr&6BbA#A[kW^I&Md'g4r l0|X2 /95 %SP6=x)/.Oy - w5Ŀ tMVh\@S wTz)S* XzٷblHn*r9` {ː^f#=*?Tފl d,1r*d֘˿̉"M\L^' } Aܡ4=e{I56#Ȝ8ѱk%5ВqDl{@`pДbo8 puN0)| 9]vv^oT`*$E_:? aI/P?[tv(1 <_^P*ljufNfq1S s^GHҧ? ٧U}<Ӽ7-R$6t\Ɇ_j,A$7^eFrQ߾`s2)G=A6vg/8Qya Y|5Dj8UP$9Q*J )Dm)n\syH9zEUrD7B ~|0Hpj\9±^yb>֞~HNϙL;/+&`L,h6^J̓\q}ؓ˺[WܨHdžAH8b\%bwnL6muJ|ދZܟE^jJx&A uU WiU/&ZM)O ٢ߞ@XL G:1 QtɬqrU{lAd3@RNZ3GiK؊-jɻXlDT^<h)s2Y~ݱb,{J?":9|${`$`3B}0RϹ`F~ ǖWЇ[tj*$ZcwA-5=ݛz(Ugc fx}M _ 暪. X+֛9SYvyL1+}d Q~(S?\hOhp9 po{fɉU~83:V@)u'ٓq~}dl].,DP$ȓ[ի*¨zz@oRHPBkR#=DyN#VHubJr:f2׭ )mZ9gD;Lބč&| ̠:!Q ޿\&'}BZ3,4:śl_>4NY8Ooŗݿ-"|&8:|$w_4F'#I,.&"0iPhݳ6_5'ŏ}C'!]iZw&'KRkё,<_YޡĻ;ḣK rHA@> V~ Xml}2mCr:b!h'HL{9oΔ5/+fM~: /[|P/Ua6)X+8tTrf!BeiO!T, ]u I!O)5Oe/ߞq"} \Idf-z1lvV@8]IC{ (}FWgU]7C9Xϼ2u?3&֬iZsl`-H1am8jAF!œ1) YDthgr*>75$59@ěqVlmdPD>jg%$) =Vʜj+?1,n.=Hf'eͿ]3a'_!pu ڏ׬p etdːTD~ҁ#dp(Hni9C9+i;x!!QI4 l ,5s8SOSYJ<*wD7b_y͛¥H7~R?Q \l胟0:Pd紉QgDf|tϷ!Z ͗4/<ᚸ ?fփ@m۳Ihdajg;^ya&j<kUNiTIILUnt,Fqaaa۶fԩ|&MYeI)a_Ġ0nf*/wqCm.nI(#owbBs%NOq:hY>,S~,j*X2r" qDC~=wh}XPP$j.sXkeKN0LgZ#LeN1ߡ]h*?LtZ^.6{;׼6Ф9ea=ȾhjVQ}i &G pх<܈W `76,,=6ic .5<\:LE2ۯ rIc\=] $RfaݟK)4OĿ}&$"x' M*6Y[:5(W\d+#v+vdnP.CV~HpӢdE19zc C7W~DR@z-ЪЅb 6ZmE~+;_޽xHŗ*G$WQ-~4:v"rm<] #ʅ}$I9JW#Im69"q ;1FϵW-~F0 /(S"kW׸FI-Q{4]A>}qvO)|tO/t }lS]8/ r*" ,b:qJTv !A-Ͳ,zHwȇ Hx?Oס7'G TOEU6H5+ȮW PFFlG-Qe ?@3T[?Tc&\1^#UiGpi9fyrL ʲG;oXe:\FU-he~EWqKZM;^5Rb9a}2P1n2:>?s+<9pco߀]Ջ 6lCT0עY'8|rJi>|q!\y^tA_zZ,R >^Zُ}^#|.io#w3j12(Q"sϛۂk_,G-*3wr[oZ0"dvNi]_j' ,S0OG /GTK|m-M7 xa8'=:x;+/>ݮF?Kj ~cnN3jzn»Ґ~LO(JDP2%}m2*Nr~UԊ)pa*mQExjy\y;xA*kA$U]%I Unpk_=MMeA.;݉ySSQ;PErCyl7 H%Oq^'F'\[?@B0Q!Ȍoۘ«HQsb~Zl+k 7*r^Vs8ѓ]b^? O\[Z2PjSN;ieцI%^Uf6LBgXG!-1# c ]pP{FB(`V!-0qų˙Xvr/A~r!.e},h/̭q'j 1 %s01?7\Wbb;SK5 >?0XDbBrU%KxGZauC>q7# ,[*}-t,O/;] v>s[îO=2W`iFPGI 1nw)P Rl@aJ=K^qi"r(FT4̮kHW~2/T;c/]ޫ ~#i١FbgqdU{I $wTTEQ{2=o|-"MuN-5Vx]7LI&$ Q0?;|;+>%1xx`b Ʉ)RUL$JؖD7(\b˰!Υ]2,B.b6C$9p 5.0hDl`.lR%[=q hi2уsvd)[ǝߘ|7t FYXLxH)VqfT+FlAyzsiwjjXtgDP C1Y~r|:Pu^*:zL d~gQ=虩ľ!ھB3??:6(g[Xk(|hCh[̘u5UF7](9 XÓg"ª 'xOb<|c~{7,Zf!sqsDR;e@XP݆#NFsֳDtQ_uU<+˃@UZUGL@j +"<.ұ˗Vn+, >g=9e抬Oy{JHmI:J> ǘkRxU*/' 1`6SBcoIB ezFb[ce:2&ef`K5 xCQ98*)P[u[:~W*9 fYM/yN\H̟]L xmcFf?dabjܠ2eмE:OPBi[g4ZBoOXkIw/yK5qO&]Nr\:~m+q g-p)bkG?A{SӱZfϧkx鉱D׀L[hix}]ՂЙzǕ% |tWP5jj&jJX/y[^VlK!*3ҹl=c] v5 UpFIzxC}+ߑ0X@b}w #@h2opA4G5 V!+kN']1 ={ usBKf*j0ȡ"ހs>a}F0 FcS@T ,`s1gLŒ>?~fZVcAI~=0+!ߋ'KbfHja!V7s#.k'p`sZk<|%#ϓwU $\vZQitjRAgGK:}bj_6_N'@@#jc(8F}fh/(0!HV Dq=!H. k.0u.BdK+kf_Y u05x"%^|6,?7TRbky-VuRMhwL(`[az6#)ֈncE-Va b ϪcLȶ#;H0TO!f(` ҀKT^ۣ4k3D-*6!SWTV5 oI+a[gcDZEBރ, cbem@P\cN`F짾b10hn] ;)c D{I F ~cB,ү>DY`GZs$6E&iH>|vYK#o /}ׂfW:7mn$6j4’#2V`j_"O-)r$E X%91vY4 ٬IbE:F yh{1,˜#\6O $;dZiiky>'go >CpwJ3᎖A̶}0d:Th9%g*p,8^R3AY?ػ 8`1ypmv&]m+bҙ 9̩/GZ#Fm!0L .ޕӕbNE-IC,aZIKWa5^MEI~f\ڰh$3@PVo*k㴵XɎ&az(O6gk b-E*zZx.EKHiONpY/>deٸ"^ >`\AAиLW}6b:oD  hwtx ZT[Mz^%#cW|{B;Y+0aN<"^x')r$ƖĜfs!qYgٰrJ@i]BY8m|`7ͲM(VLݏwq [. ]s h[%n9h\µFB2o#znȱ_hX"(>qeZUFl]VB`,);c~t_6N$[jG,O3+K 䘽{g@-5KR+p?I辢tTԨ ־&دJB|H(?74iepj H" T&s_@TݖҰyrwOt: xgɌ^ߑ^6ao$B})<"V çZ8NMݘK(R cuBj0K:RȯtarRVи P$+q8<԰0}r3|H]F4) NM#yYX؆"' >vh|kTF`~H/D?dD6>=~oYW<\wrHw1s& =Jc41KJ5w #¸(>ܨ^Yd#O9)ȫa*>=A"Eb@'[{ғ` ΂;Uů<'Mc({QU֨L>1Bjo9tLxSwã)b )9|~V0G p5)G_SvF-d4B}!~4Aʠ<7Z2wD`b:/ J?m'LM35X"^+Jjy75cIQF NGB@^$aR^eeFJfiPY]Yc٬! bP;d>̀l RÀgԚ< #q1 ]KBZJm-+Q9)$+ i@iXTdmXk`\8Y~ñ=I%3j.-)x+vDP!sJHk هkaSk$JiB~ȷDhP%{|.Oȼe9:IB#OAZzHWg)F1.FbL8IPйy|;h6B^8kN2׿O}a bY:63>~͘@D$5-".Ƽː&&FSW~hM&)i.хCBcwm+n2*<ŔD`HVfiY jR wo4 _HjG6 .]?k'Vn vY7yt 5!_TBW)Xqαn 3)ԧDS\Xy&Ѷ ,J}j>#5}DvMe=6A[;/^!x0ċYIX^5:`*OS3BT;Z I-j2ZNZAJIwXONXY")&_{e!t/H%~3w/5O/GjRy1(OX@7L Lݝ.}5ĥ9ח㰇 m~ ]os~#"%.]@`hc6Z+U.e? `YS _Y-KAA:b fH|l{'#mhtI Snߒb<ǎL65Ƕ prkg@ ~.QjR-yfW좡8^¼})gꞪ VHX]ѧKHV3txx6ΩJg 8E:Rfl pM=&ѩgyղmX}9n6T.u|(\Ω"cL_|؋qK&qZ^F86hqd~QVY)} R5mp~)Ani2 {i4J"Pb{5-ytV iN8C|n"$Qm}]\UIoEU\\"h'8 kxo2Mzc/r~h~%{ӭ*?w4N0jыEO6^_es5}2}`wh~W p *~$7G2F')w~<ҏ6Q¾e N0ZOvR;.tĹv:l1'8hJ'30{O|@g[{! _&ۙh {f'(M]BRJ/>9hw P!Vmᆢci7-;> CL7bP8墦SͣBE"ـLi3T `x'bv/|W[nTD3}42gwQe~yE1Fpa5:Vz/8/ˡrAgID kP$_c=M |&z'㳿óD6IMf3-;7p) Z Q飇شy YZrekor-1.3.5/pkg/types/rpm/tests/test_rpm_public_key.key000066400000000000000000000032231455727245600233250ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v2.0.22 (GNU/Linux) mQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn rIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ 8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X 5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c aevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e f+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7 JINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m vufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk nHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry Gat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y m4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB tDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5 QGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB Ah4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl Gy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs N3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD vOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq a0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw byaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg q4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X 407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z V6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG rCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32 o8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy yy+mHmSv =kkH7 -----END PGP PUBLIC KEY BLOCK----- rekor-1.3.5/pkg/types/rpm/v0.0.1/000077500000000000000000000000001455727245600162305ustar00rootroot00000000000000rekor-1.3.5/pkg/types/rpm/v0.0.1/entry.go000066400000000000000000000260661455727245600177320ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rpm import ( "bytes" "context" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "io" "os" "path/filepath" "strconv" "strings" "github.com/asaskevich/govalidator" rpmutils "github.com/cavaliercoder/go-rpm" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "golang.org/x/sync/errgroup" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/pki/pgp" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/rpm" "github.com/sigstore/rekor/pkg/util" ) const ( APIVERSION = "0.0.1" ) func init() { if err := rpm.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil { log.Logger.Panic(err) } } type V001Entry struct { RPMModel models.RpmV001Schema } func (v V001Entry) APIVersion() string { return APIVERSION } func NewEntry() types.EntryImpl { return &V001Entry{} } func (v V001Entry) IndexKeys() ([]string, error) { var result []string keyObj, err := pgp.NewPublicKey(bytes.NewReader(*v.RPMModel.PublicKey.Content)) if err != nil { return nil, err } key, err := keyObj.CanonicalValue() if err != nil { return nil, err } keyHash := sha256.Sum256(key) result = append(result, strings.ToLower(hex.EncodeToString(keyHash[:]))) result = append(result, keyObj.Subjects()...) if v.RPMModel.Package.Hash != nil { hashKey := strings.ToLower(fmt.Sprintf("%s:%s", *v.RPMModel.Package.Hash.Algorithm, *v.RPMModel.Package.Hash.Value)) result = append(result, hashKey) } return result, nil } func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error { rpm, ok := pe.(*models.Rpm) if !ok { return errors.New("cannot unmarshal non RPM v0.0.1 type") } if err := types.DecodeEntry(rpm.Spec, &v.RPMModel); err != nil { return err } // field validation if err := v.RPMModel.Validate(strfmt.Default); err != nil { return err } return v.validate() } func (v *V001Entry) fetchExternalEntities(ctx context.Context) (*pgp.PublicKey, *rpmutils.PackageFile, error) { if err := v.validate(); err != nil { return nil, nil, types.ValidationError(err) } g, ctx := errgroup.WithContext(ctx) hashR, hashW := io.Pipe() sigR, sigW := io.Pipe() rpmR, rpmW := io.Pipe() defer hashR.Close() defer sigR.Close() defer rpmR.Close() closePipesOnError := types.PipeCloser(hashR, hashW, sigR, sigW, rpmR, rpmW) oldSHA := "" if v.RPMModel.Package.Hash != nil && v.RPMModel.Package.Hash.Value != nil { oldSHA = swag.StringValue(v.RPMModel.Package.Hash.Value) } g.Go(func() error { defer hashW.Close() defer sigW.Close() defer rpmW.Close() dataReadCloser := bytes.NewReader(v.RPMModel.Package.Content) /* #nosec G110 */ if _, err := io.Copy(io.MultiWriter(hashW, sigW, rpmW), dataReadCloser); err != nil { return closePipesOnError(err) } return nil }) hashResult := make(chan string) g.Go(func() error { defer close(hashResult) hasher := sha256.New() if _, err := io.Copy(hasher, hashR); err != nil { return closePipesOnError(err) } computedSHA := hex.EncodeToString(hasher.Sum(nil)) if oldSHA != "" && computedSHA != oldSHA { return closePipesOnError(types.ValidationError(fmt.Errorf("SHA mismatch: %s != %s", computedSHA, oldSHA))) } select { case <-ctx.Done(): return ctx.Err() case hashResult <- computedSHA: return nil } }) var keyObj *pgp.PublicKey g.Go(func() error { keyReadCloser := bytes.NewReader(*v.RPMModel.PublicKey.Content) var err error keyObj, err = pgp.NewPublicKey(keyReadCloser) if err != nil { return closePipesOnError(types.ValidationError(err)) } keyring, err := keyObj.KeyRing() if err != nil { return closePipesOnError(types.ValidationError(err)) } if _, err := rpmutils.GPGCheck(sigR, keyring); err != nil { return closePipesOnError(types.ValidationError(err)) } select { case <-ctx.Done(): return ctx.Err() default: return nil } }) var rpmObj *rpmutils.PackageFile g.Go(func() error { var err error rpmObj, err = rpmutils.ReadPackageFile(rpmR) if err != nil { return closePipesOnError(types.ValidationError(err)) } // ReadPackageFile does not drain the entire reader so we need to discard the rest if _, err = io.Copy(io.Discard, rpmR); err != nil { return closePipesOnError(err) } select { case <-ctx.Done(): return ctx.Err() default: return nil } }) computedSHA := <-hashResult if err := g.Wait(); err != nil { return nil, nil, err } // if we get here, all goroutines succeeded without error if oldSHA == "" { v.RPMModel.Package.Hash = &models.RpmV001SchemaPackageHash{} v.RPMModel.Package.Hash.Algorithm = swag.String(models.RpmV001SchemaPackageHashAlgorithmSha256) v.RPMModel.Package.Hash.Value = swag.String(computedSHA) } return keyObj, rpmObj, nil } func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) { keyObj, rpmObj, err := v.fetchExternalEntities(ctx) if err != nil { return nil, err } canonicalEntry := models.RpmV001Schema{} // need to canonicalize key content var pubKeyContent []byte canonicalEntry.PublicKey = &models.RpmV001SchemaPublicKey{} pubKeyContent, err = keyObj.CanonicalValue() if err != nil { return nil, err } canonicalEntry.PublicKey.Content = (*strfmt.Base64)(&pubKeyContent) canonicalEntry.Package = &models.RpmV001SchemaPackage{} canonicalEntry.Package.Hash = &models.RpmV001SchemaPackageHash{} canonicalEntry.Package.Hash.Algorithm = v.RPMModel.Package.Hash.Algorithm canonicalEntry.Package.Hash.Value = v.RPMModel.Package.Hash.Value // data content is not set deliberately // set NEVRA headers canonicalEntry.Package.Headers = make(map[string]string) canonicalEntry.Package.Headers["Name"] = rpmObj.Name() canonicalEntry.Package.Headers["Epoch"] = strconv.Itoa(rpmObj.Epoch()) canonicalEntry.Package.Headers["Version"] = rpmObj.Version() canonicalEntry.Package.Headers["Release"] = rpmObj.Release() canonicalEntry.Package.Headers["Architecture"] = rpmObj.Architecture() if md5sum := rpmObj.GetBytes(0, 1004); md5sum != nil { canonicalEntry.Package.Headers["RPMSIGTAG_MD5"] = hex.EncodeToString(md5sum) } if sha1sum := rpmObj.GetBytes(0, 1012); sha1sum != nil { canonicalEntry.Package.Headers["RPMSIGTAG_SHA1"] = hex.EncodeToString(sha1sum) } if sha256sum := rpmObj.GetBytes(0, 1016); sha256sum != nil { canonicalEntry.Package.Headers["RPMSIGTAG_SHA256"] = hex.EncodeToString(sha256sum) } // wrap in valid object with kind and apiVersion set rpm := models.Rpm{} rpm.APIVersion = swag.String(APIVERSION) rpm.Spec = &canonicalEntry return json.Marshal(&rpm) } // validate performs cross-field validation for fields in object func (v V001Entry) validate() error { key := v.RPMModel.PublicKey if key == nil { return errors.New("missing public key") } if key.Content == nil || len(*key.Content) == 0 { return errors.New("'content' must be specified for publicKey") } pkg := v.RPMModel.Package if pkg == nil { return errors.New("missing package") } hash := pkg.Hash if hash != nil { if !govalidator.IsHash(swag.StringValue(hash.Value), swag.StringValue(hash.Algorithm)) { return errors.New("invalid value for hash") } } else if len(pkg.Content) == 0 { return errors.New("'content' must be specified for package") } return nil } func (v V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) { returnVal := models.Rpm{} re := V001Entry{} // we will need artifact, public-key, signature re.RPMModel = models.RpmV001Schema{} re.RPMModel.Package = &models.RpmV001SchemaPackage{} var err error artifactBytes := props.ArtifactBytes if artifactBytes == nil { var artifactReader io.ReadCloser if props.ArtifactPath == nil { return nil, errors.New("path to artifact file must be specified") } if props.ArtifactPath.IsAbs() { artifactReader, err = util.FileOrURLReadCloser(ctx, props.ArtifactPath.String(), nil) if err != nil { return nil, fmt.Errorf("error reading RPM file: %w", err) } } else { artifactReader, err = os.Open(filepath.Clean(props.ArtifactPath.Path)) if err != nil { return nil, fmt.Errorf("error opening RPM file: %w", err) } } artifactBytes, err = io.ReadAll(artifactReader) if err != nil { return nil, fmt.Errorf("error reading RPM file: %w", err) } } re.RPMModel.Package.Content = strfmt.Base64(artifactBytes) re.RPMModel.PublicKey = &models.RpmV001SchemaPublicKey{} publicKeyBytes := props.PublicKeyBytes if len(publicKeyBytes) == 0 { if len(props.PublicKeyPaths) != 1 { return nil, errors.New("only one public key must be provided to verify RPM signature") } keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path)) if err != nil { return nil, fmt.Errorf("error reading public key file: %w", err) } publicKeyBytes = append(publicKeyBytes, keyBytes) } else if len(publicKeyBytes) != 1 { return nil, errors.New("only one public key must be provided") } re.RPMModel.PublicKey.Content = (*strfmt.Base64)(&publicKeyBytes[0]) if err := re.validate(); err != nil { return nil, err } if _, _, err := re.fetchExternalEntities(context.Background()); err != nil { return nil, fmt.Errorf("error retrieving external entities: %v", err) } returnVal.APIVersion = swag.String(re.APIVersion()) returnVal.Spec = re.RPMModel return &returnVal, nil } func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { if v.RPMModel.PublicKey == nil || v.RPMModel.PublicKey.Content == nil { return nil, errors.New("rpm v0.0.1 entry not initialized") } key, err := pgp.NewPublicKey(bytes.NewReader(*v.RPMModel.PublicKey.Content)) if err != nil { return nil, err } return []pki.PublicKey{key}, nil } func (v V001Entry) ArtifactHash() (string, error) { if v.RPMModel.Package == nil || v.RPMModel.Package.Hash == nil || v.RPMModel.Package.Hash.Value == nil || v.RPMModel.Package.Hash.Algorithm == nil { return "", errors.New("rpm v0.0.1 entry not initialized") } return strings.ToLower(fmt.Sprintf("%s:%s", *v.RPMModel.Package.Hash.Algorithm, *v.RPMModel.Package.Hash.Value)), nil } func (v V001Entry) Insertable() (bool, error) { if v.RPMModel.PublicKey == nil { return false, errors.New("missing publicKey property") } if v.RPMModel.PublicKey.Content == nil || len(*v.RPMModel.PublicKey.Content) == 0 { return false, errors.New("missing publicKey content") } if v.RPMModel.Package == nil { return false, errors.New("missing package property") } if len(v.RPMModel.Package.Content) == 0 { return false, errors.New("missing package content") } return true, nil } rekor-1.3.5/pkg/types/rpm/v0.0.1/entry_test.go000066400000000000000000000206521455727245600207640ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rpm import ( "bytes" "context" "os" "reflect" "testing" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "go.uber.org/goleak" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestNewEntryReturnType(t *testing.T) { entry := NewEntry() if reflect.TypeOf(entry) != reflect.ValueOf(&V001Entry{}).Type() { t.Errorf("invalid type returned from NewEntry: %T", entry) } } func TestCrossFieldValidation(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectUnmarshalSuccess bool expectCanonicalizeSuccess bool expectVerifierSuccess bool } keyBytes, _ := os.ReadFile("../tests/test_rpm_public_key.key") dataBytes, _ := os.ReadFile("../tests/test.rpm") testCases := []TestCase{ { caseDesc: "empty obj", entry: V001Entry{}, expectUnmarshalSuccess: false, expectVerifierSuccess: false, }, { caseDesc: "public key without content", entry: V001Entry{ RPMModel: models.RpmV001Schema{ PublicKey: &models.RpmV001SchemaPublicKey{}, }, }, expectUnmarshalSuccess: false, expectVerifierSuccess: false, }, { caseDesc: "public key without package", entry: V001Entry{ RPMModel: models.RpmV001Schema{ PublicKey: &models.RpmV001SchemaPublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, }, }, expectUnmarshalSuccess: false, expectVerifierSuccess: true, }, { caseDesc: "public key with empty package", entry: V001Entry{ RPMModel: models.RpmV001Schema{ PublicKey: &models.RpmV001SchemaPublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, Package: &models.RpmV001SchemaPackage{}, }, }, expectUnmarshalSuccess: false, expectVerifierSuccess: true, }, { caseDesc: "public key with invalid key content & with data with content", entry: V001Entry{ RPMModel: models.RpmV001Schema{ PublicKey: &models.RpmV001SchemaPublicKey{ Content: (*strfmt.Base64)(&dataBytes), }, Package: &models.RpmV001SchemaPackage{ Content: strfmt.Base64(dataBytes), }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: false, expectVerifierSuccess: false, }, { caseDesc: "public key with key content & with data with content", entry: V001Entry{ RPMModel: models.RpmV001Schema{ PublicKey: &models.RpmV001SchemaPublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, Package: &models.RpmV001SchemaPackage{ Content: strfmt.Base64(dataBytes), }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: true, expectVerifierSuccess: true, }, { caseDesc: "public key with key content & with invalid data with content", entry: V001Entry{ RPMModel: models.RpmV001Schema{ PublicKey: &models.RpmV001SchemaPublicKey{ Content: (*strfmt.Base64)(&keyBytes), }, Package: &models.RpmV001SchemaPackage{ Content: strfmt.Base64(keyBytes), }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: false, expectVerifierSuccess: true, }, } for _, tc := range testCases { if err := tc.entry.validate(); (err == nil) != tc.expectUnmarshalSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } v := &V001Entry{} r := models.Rpm{ APIVersion: swag.String(tc.entry.APIVersion()), Spec: tc.entry.RPMModel, } unmarshalAndValidate := func() error { if err := v.Unmarshal(&r); err != nil { return err } return v.validate() } if err := unmarshalAndValidate(); (err == nil) != tc.expectUnmarshalSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } if tc.expectUnmarshalSuccess { if ok, err := v.Insertable(); !ok || err != nil { t.Errorf("unexpected error calling Insertable on valid proposed entry: %v", err) } } b, err := v.Canonicalize(context.TODO()) if (err == nil) != tc.expectCanonicalizeSuccess { t.Errorf("unexpected result from Canonicalize for '%v': %v", tc.caseDesc, err) } else if err != nil { if _, ok := err.(types.ValidationError); !ok { t.Errorf("canonicalize returned an unexpected error that isn't of type types.ValidationError: %v", err) } } if b != nil { pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tc.caseDesc, err) } ei, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tc.caseDesc, err) } if ok, err := ei.Insertable(); ok || err == nil { t.Errorf("unexpected success calling Insertable on entry created from canonicalized content") } hash, err := ei.ArtifactHash() if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != "sha256:c8b0bc59708d74f53aab0089ac587d5c348d6ead143dab9f6d9c4b48c973bfd8" { t.Errorf("unexpected match with ArtifactHash: %s", hash) } } verifiers, err := v.Verifiers() if tc.expectVerifierSuccess { if err != nil { t.Errorf("%v: unexpected error, got %v", tc.caseDesc, err) } else { // TODO: Improve this test once CanonicalValue returns same result as input for PGP keys _, err := verifiers[0].CanonicalValue() if err != nil { t.Errorf("%v: unexpected error getting canonical value, got %v", tc.caseDesc, err) } } } else { if err == nil { s, _ := verifiers[0].CanonicalValue() t.Errorf("%v: expected error for %v, got %v", tc.caseDesc, string(s), err) } } } } func TestInsertable(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectSuccess bool } pub := strfmt.Base64([]byte("pub")) testCases := []TestCase{ { caseDesc: "valid entry", entry: V001Entry{ RPMModel: models.RpmV001Schema{ Package: &models.RpmV001SchemaPackage{ Content: strfmt.Base64([]byte("content")), }, PublicKey: &models.RpmV001SchemaPublicKey{ Content: &pub, }, }, }, expectSuccess: true, }, { caseDesc: "missing public key content", entry: V001Entry{ RPMModel: models.RpmV001Schema{ Package: &models.RpmV001SchemaPackage{ Content: strfmt.Base64([]byte("content")), }, PublicKey: &models.RpmV001SchemaPublicKey{ //Content: &pub, }, }, }, expectSuccess: false, }, { caseDesc: "missing public key obj", entry: V001Entry{ RPMModel: models.RpmV001Schema{ Package: &models.RpmV001SchemaPackage{ Content: strfmt.Base64([]byte("content")), }, /* PublicKey: &models.RpmV001SchemaPublicKey{ Content: &pub, }, */ }, }, expectSuccess: false, }, { caseDesc: "missing package content", entry: V001Entry{ RPMModel: models.RpmV001Schema{ Package: &models.RpmV001SchemaPackage{ //Content: strfmt.Base64([]byte("content")), }, PublicKey: &models.RpmV001SchemaPublicKey{ Content: &pub, }, }, }, expectSuccess: false, }, { caseDesc: "missing package obj", entry: V001Entry{ RPMModel: models.RpmV001Schema{ /* Package: &models.RpmV001SchemaPackage{ Content: strfmt.Base64([]byte("content")), }, */ PublicKey: &models.RpmV001SchemaPublicKey{ Content: &pub, }, }, }, expectSuccess: false, }, { caseDesc: "empty obj", entry: V001Entry{}, expectSuccess: false, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess { t.Errorf("unexpected result calling Insertable: %v", err) } }) } } rekor-1.3.5/pkg/types/rpm/v0.0.1/fuzz_test.go000066400000000000000000000045641455727245600206250ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package rpm import ( "context" "sync" "testing" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/go-openapi/swag" fuzzUtils "github.com/sigstore/rekor/pkg/fuzz" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/rpm" ) var initter sync.Once func FuzzRpmCreateProposedEntry(f *testing.F) { f.Fuzz(func(t *testing.T, propsData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) version := "0.0.1" ff := fuzz.NewConsumer(propsData) props, cleanup, err := fuzzUtils.CreateProps(ff, "rpmV001") if err != nil { t.Skip() } defer func() { for _, c := range cleanup { c() } }() it := rpm.New() entry, err := it.CreateProposedEntry(context.Background(), version, props) if err != nil { t.Skip() } ei, err := types.CreateVersionedEntry(entry) if err != nil { t.Skip() } if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err) } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("valid insertable entry should be able to be canonicalized: %v", err) } _, _ = ei.IndexKeys() }) } func FuzzRpmUnmarshalAndCanonicalize(f *testing.F) { f.Fuzz(func(t *testing.T, entryData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(entryData) targetV001 := &models.RpmV001Schema{} if err := ff.GenerateStruct(targetV001); err != nil { t.Skip() } targetEntry := &models.Rpm{ APIVersion: swag.String(APIVERSION), Spec: targetV001, } ei, err := types.UnmarshalEntry(targetEntry) if err != nil { t.Skip() } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Skip() } }) } rekor-1.3.5/pkg/types/rpm/v0.0.1/rpm_v0_0_1_schema.json000066400000000000000000000045761455727245600223210ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/rpm/rpm_v0_0_1_schema.json", "title": "RPM v0.0.1 Schema", "description": "Schema for RPM entries", "type": "object", "properties": { "publicKey" : { "description": "The PGP public key that can verify the RPM signature", "type": "object", "properties": { "content": { "description": "Specifies the content of the public key inline within the document", "type": "string", "format": "byte" } }, "required": [ "content" ] }, "package": { "description": "Information about the package associated with the entry", "type": "object", "properties": { "headers": { "description": "Values of the RPM headers", "type": "object", "additionalProperties": { "type": "string" }, "readOnly": true }, "hash": { "description": "Specifies the hash algorithm and value for the package", "type": "object", "properties": { "algorithm": { "description": "The hashing function used to compute the hash value", "type": "string", "enum": [ "sha256" ] }, "value": { "description": "The hash value for the package", "type": "string" } }, "required": [ "algorithm", "value" ] }, "content": { "description": "Specifies the package inline within the document", "type": "string", "format": "byte", "writeOnly": true } }, "oneOf": [ { "required": [ "hash" ] }, { "required": [ "content" ] } ] } }, "required": [ "publicKey", "package" ] } rekor-1.3.5/pkg/types/test_util.go000066400000000000000000000041731455727245600171600ustar00rootroot00000000000000/* Copyright © 2021 The Sigstore Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package types import ( "context" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" ) type BaseUnmarshalTester struct{} func (u BaseUnmarshalTester) NewEntry() EntryImpl { return &BaseUnmarshalTester{} } func (u BaseUnmarshalTester) ArtifactHash() (string, error) { return "", nil } func (u BaseUnmarshalTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } func (u BaseUnmarshalTester) APIVersion() string { return "2.0.1" } func (u BaseUnmarshalTester) IndexKeys() ([]string, error) { return []string{}, nil } func (u BaseUnmarshalTester) Canonicalize(_ context.Context) ([]byte, error) { return nil, nil } func (u BaseUnmarshalTester) Unmarshal(_ models.ProposedEntry) error { return nil } func (u BaseUnmarshalTester) Validate() error { return nil } func (u BaseUnmarshalTester) AttestationKey() string { return "" } func (u BaseUnmarshalTester) AttestationKeyValue() (string, []byte) { return "", nil } func (u BaseUnmarshalTester) CreateFromArtifactProperties(_ context.Context, _ ArtifactProperties) (models.ProposedEntry, error) { return nil, nil } func (u BaseUnmarshalTester) Insertable() (bool, error) { return false, nil } type BaseProposedEntryTester struct{} func (b BaseProposedEntryTester) Kind() string { return "nil" } func (b BaseProposedEntryTester) SetKind(_ string) { } func (b BaseProposedEntryTester) Validate(_ strfmt.Registry) error { return nil } func (b BaseProposedEntryTester) ContextValidate(_ context.Context, _ strfmt.Registry) error { return nil } rekor-1.3.5/pkg/types/tuf/000077500000000000000000000000001455727245600154065ustar00rootroot00000000000000rekor-1.3.5/pkg/types/tuf/tuf.go000066400000000000000000000034011455727245600165310ustar00rootroot00000000000000/* Copyright © 2021 The Sigstore Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package tuf import ( "context" "errors" "fmt" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/generated/models" ) const ( KIND = "tuf" ) type BaseTufType struct { types.RekorType } func init() { types.TypeMap.Store(KIND, New) } func New() types.TypeImpl { btt := BaseTufType{} btt.Kind = KIND btt.VersionMap = VersionMap return &btt } var VersionMap = types.NewSemVerEntryFactoryMap() func (btt BaseTufType) UnmarshalEntry(pe models.ProposedEntry) (types.EntryImpl, error) { if pe == nil { return nil, errors.New("proposed entry cannot be nil") } tuf, ok := pe.(*models.TUF) if !ok { return nil, fmt.Errorf("cannot unmarshal non-tuf types %+v", pe) } return btt.VersionedUnmarshal(tuf, *tuf.APIVersion) } func (btt *BaseTufType) CreateProposedEntry(ctx context.Context, version string, props types.ArtifactProperties) (models.ProposedEntry, error) { if version == "" { version = btt.DefaultVersion() } ei, err := btt.VersionedUnmarshal(nil, version) if err != nil { return nil, fmt.Errorf("fetching TUF version implementation: %w", err) } return ei.CreateFromArtifactProperties(ctx, props) } func (btt BaseTufType) DefaultVersion() string { return "0.0.1" } rekor-1.3.5/pkg/types/tuf/tuf_schema.json000066400000000000000000000005041455727245600204160ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/tuf/tuf_schema.json", "title": "TUF Schema", "description": "Schema for TUF metadata objects", "type": "object", "oneOf": [ { "$ref": "v0.0.1/tuf_v0_0_1_schema.json" } ] } rekor-1.3.5/pkg/types/tuf/tuf_test.go000066400000000000000000000054331455727245600175770ustar00rootroot00000000000000/* Copyright © 2021 The Sigstore Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package tuf import ( "errors" "testing" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" ) type UnmarshalTester struct { models.TUF types.BaseUnmarshalTester } type UnmarshalFailsTester struct { types.BaseUnmarshalTester } func (u UnmarshalFailsTester) NewEntry() types.EntryImpl { return &UnmarshalFailsTester{} } func (u UnmarshalFailsTester) Verifiers() ([]pki.PublicKey, error) { return nil, nil } func (u UnmarshalFailsTester) Unmarshal(_ models.ProposedEntry) error { return errors.New("error") } func TestTufType(t *testing.T) { // empty to start if VersionMap.Count() != 0 { t.Error("semver range was not blank at start of test") } u := UnmarshalTester{} // ensure semver range parser is working invalidSemVerRange := "not a valid semver range" err := VersionMap.SetEntryFactory(invalidSemVerRange, u.NewEntry) if err == nil || VersionMap.Count() > 0 { t.Error("invalid semver range was incorrectly added to SemVerToFacFnMap") } // valid semver range can be parsed err = VersionMap.SetEntryFactory(">= 1.2.3", u.NewEntry) if err != nil || VersionMap.Count() != 1 { t.Error("valid semver range was not added to SemVerToFacFnMap") } u.TUF.APIVersion = swag.String("2.0.1") brt := New() // version requested matches implementation in map if _, err := brt.UnmarshalEntry(&u.TUF); err != nil { t.Errorf("unexpected error in Unmarshal: %v", err) } // version requested fails to match implementation in map u.TUF.APIVersion = swag.String("1.2.2") if _, err := brt.UnmarshalEntry(&u.TUF); err == nil { t.Error("unexpected success in Unmarshal for non-matching version") } // error in Unmarshal call is raised appropriately u.TUF.APIVersion = swag.String("2.2.0") u2 := UnmarshalFailsTester{} _ = VersionMap.SetEntryFactory(">= 1.2.3", u2.NewEntry) if _, err := brt.UnmarshalEntry(&u.TUF); err == nil { t.Error("unexpected success in Unmarshal when error is thrown") } // version requested fails to match implementation in map u.TUF.APIVersion = swag.String("not_a_version") if _, err := brt.UnmarshalEntry(&u.TUF); err == nil { t.Error("unexpected success in Unmarshal for invalid version") } } rekor-1.3.5/pkg/types/tuf/v0.0.1/000077500000000000000000000000001455727245600162305ustar00rootroot00000000000000rekor-1.3.5/pkg/types/tuf/v0.0.1/entry.go000066400000000000000000000261051455727245600177240ustar00rootroot00000000000000/* Copyright © 2021 The Sigstore Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package tuf import ( "bytes" "context" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "errors" "fmt" "io" "os" "path/filepath" "strconv" "strings" "time" "github.com/theupdateframework/go-tuf/data" // This will support deprecated ECDSA hex-encoded keys in TUF metadata. // Will be removed when sigstore migrates entirely off hex-encoded. _ "github.com/theupdateframework/go-tuf/pkg/deprecated/set_ecdsa" "golang.org/x/sync/errgroup" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/tuf" "github.com/sigstore/rekor/pkg/util" "github.com/go-openapi/strfmt" "github.com/sigstore/rekor/pkg/pki" ptuf "github.com/sigstore/rekor/pkg/pki/tuf" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" ) const ( APIVERSION = "0.0.1" ) func init() { if err := tuf.VersionMap.SetEntryFactory(APIVERSION, NewEntry); err != nil { log.Logger.Panic(err) } } type BaseSigned struct { Type string `json:"_type"` Expires time.Time `json:"expires"` Version int `json:"version"` } type V001Entry struct { TufObj models.TUFV001Schema } func (v V001Entry) APIVersion() string { return APIVERSION } func NewEntry() types.EntryImpl { return &V001Entry{} } func (v V001Entry) IndexKeys() ([]string, error) { var result []string keyBytes, err := v.parseRootContent() if err != nil { return nil, err } sigBytes, err := v.parseMetadataContent() if err != nil { return nil, err } key, err := ptuf.NewPublicKey(bytes.NewReader(keyBytes)) if err != nil { return nil, err } sig, err := ptuf.NewSignature(bytes.NewReader(sigBytes)) if err != nil { return nil, err } // Index metadata hash, type, and version. metadata, err := sig.CanonicalValue() if err != nil { return nil, err } metadataHash := sha256.Sum256(metadata) result = append(result, strings.ToLower(hex.EncodeToString(metadataHash[:]))) result = append(result, sig.Role) result = append(result, strconv.Itoa(sig.Version)) // Index root.json hash. root, err := key.CanonicalValue() if err != nil { log.Logger.Error(err) } else { rootHash := sha256.Sum256(root) result = append(result, strings.ToLower(hex.EncodeToString(rootHash[:]))) } // TODO: Index individual key IDs return result, nil } func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error { tuf, ok := pe.(*models.TUF) if !ok { return errors.New("cannot unmarshal non tuf v0.0.1 type") } if err := types.DecodeEntry(tuf.Spec, &v.TufObj); err != nil { return err } // field validation if err := v.TufObj.Validate(strfmt.Default); err != nil { return err } // cross field validation return v.Validate() } func (v *V001Entry) fetchExternalEntities(ctx context.Context) (pki.PublicKey, pki.Signature, error) { g, ctx := errgroup.WithContext(ctx) metaR, metaW := io.Pipe() rootR, rootW := io.Pipe() defer metaR.Close() defer rootR.Close() closePipesOnError := types.PipeCloser(metaR, metaW, rootR, rootW) // verify artifact signature sigResult := make(chan pki.Signature) g.Go(func() error { defer close(sigResult) var contentBytes []byte if v.TufObj.Metadata.Content != nil { var err error contentBytes, err = v.parseMetadataContent() if err != nil { return closePipesOnError(err) } } sigReadCloser := bytes.NewReader(contentBytes) signature, err := ptuf.NewSignature(sigReadCloser) if err != nil { return closePipesOnError(types.ValidationError(err)) } select { case <-ctx.Done(): return ctx.Err() case sigResult <- signature: return nil } }) keyResult := make(chan pki.PublicKey) g.Go(func() error { defer close(keyResult) var contentBytes []byte if v.TufObj.Root.Content != nil { var err error contentBytes, err = v.parseRootContent() if err != nil { return closePipesOnError(err) } } keyReadCloser := bytes.NewReader(contentBytes) key, err := ptuf.NewPublicKey(keyReadCloser) if err != nil { return closePipesOnError(types.ValidationError(err)) } select { case <-ctx.Done(): return ctx.Err() case keyResult <- key: return nil } }) var ( keyObj pki.PublicKey sigObj pki.Signature ) // the sigObj contains the signed content. g.Go(func() error { keyObj, sigObj = <-keyResult, <-sigResult if keyObj == nil || sigObj == nil { return closePipesOnError(errors.New("failed to read signature or public key")) } var err error if err = sigObj.Verify(nil, keyObj); err != nil { return closePipesOnError(types.ValidationError(err)) } select { case <-ctx.Done(): return ctx.Err() default: return nil } }) if err := g.Wait(); err != nil { return nil, nil, err } return keyObj, sigObj, nil } func (v *V001Entry) Canonicalize(ctx context.Context) ([]byte, error) { key, sig, err := v.fetchExternalEntities(ctx) if err != nil { return nil, err } canonicalEntry := models.TUFV001Schema{} canonicalEntry.SpecVersion, err = key.(*ptuf.PublicKey).SpecVersion() if err != nil { return nil, err } // need to canonicalize manifest (canonicalize JSON) canonicalEntry.Root = &models.TUFV001SchemaRoot{} canonicalEntry.Root.Content, err = key.CanonicalValue() if err != nil { return nil, err } canonicalEntry.Metadata = &models.TUFV001SchemaMetadata{} canonicalEntry.Metadata.Content, err = sig.CanonicalValue() if err != nil { return nil, err } // wrap in valid object with kind and apiVersion set tuf := models.TUF{} tuf.APIVersion = swag.String(APIVERSION) tuf.Spec = &canonicalEntry return json.Marshal(&tuf) } // Validate performs cross-field validation for fields in object // FIXME: we can probably export ValidateMetablock on in-toto.go func (v V001Entry) Validate() error { root := v.TufObj.Root if root == nil { return errors.New("missing root") } if root.Content == nil { return errors.New("root must be specified") } tufManifest := v.TufObj.Metadata if tufManifest == nil { return errors.New("missing TUF metadata") } if tufManifest.Content == nil { return errors.New("TUF metadata must be specified") } return nil } func (v V001Entry) CreateFromArtifactProperties(ctx context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) { // This will do only syntactic checks of the metablock, not signature verification. // Signature verification occurs in FetchExternalEntries() returnVal := models.TUF{} re := V001Entry{} // we will need the manifest and root var err error artifactBytes := props.ArtifactBytes re.TufObj.Metadata = &models.TUFV001SchemaMetadata{} if artifactBytes == nil { var artifactReader io.ReadCloser if props.ArtifactPath == nil { return nil, errors.New("path to artifact file must be specified") } if props.ArtifactPath.IsAbs() { artifactReader, err = util.FileOrURLReadCloser(ctx, props.ArtifactPath.String(), nil) if err != nil { return nil, fmt.Errorf("error reading RPM file: %w", err) } } else { artifactReader, err = os.Open(filepath.Clean(props.ArtifactPath.Path)) if err != nil { return nil, fmt.Errorf("error opening RPM file: %w", err) } } artifactBytes, err = io.ReadAll(artifactReader) if err != nil { return nil, fmt.Errorf("error reading RPM file: %w", err) } } s := &data.Signed{} if err := json.Unmarshal(artifactBytes, s); err != nil { return nil, err } re.TufObj.Metadata.Content = s rootBytes := props.PublicKeyBytes re.TufObj.Root = &models.TUFV001SchemaRoot{} if len(rootBytes) == 0 { if len(props.PublicKeyPaths) != 1 { return nil, errors.New("only one path to root file must be specified") } keyBytes, err := os.ReadFile(filepath.Clean(props.PublicKeyPaths[0].Path)) if err != nil { return nil, fmt.Errorf("error reading root file: %w", err) } rootBytes = append(rootBytes, keyBytes) } else if len(rootBytes) != 1 { return nil, errors.New("only one root key must be provided") } root := &data.Signed{} if err := json.Unmarshal(rootBytes[0], root); err != nil { return nil, err } re.TufObj.Root.Content = root if err := re.Validate(); err != nil { return nil, err } if _, _, err := re.fetchExternalEntities(ctx); err != nil { return nil, fmt.Errorf("error retrieving external entities: %v", err) } returnVal.APIVersion = swag.String(re.APIVersion()) returnVal.Spec = re.TufObj return &returnVal, nil } func (v V001Entry) Verifiers() ([]pki.PublicKey, error) { if v.TufObj.Root == nil { return nil, errors.New("tuf v0.0.1 entry not initialized") } keyBytes, err := v.parseRootContent() if err != nil { return nil, err } key, err := ptuf.NewPublicKey(bytes.NewReader(keyBytes)) if err != nil { return nil, err } return []pki.PublicKey{key}, nil } func (v V001Entry) ArtifactHash() (string, error) { if v.TufObj.Metadata == nil || v.TufObj.Metadata.Content == nil { return "", errors.New("tuf v0.0.1 entry not initialized") } sigBytes, err := v.parseMetadataContent() if err != nil { return "", err } sig, err := ptuf.NewSignature(bytes.NewReader(sigBytes)) if err != nil { return "", err } metadata, err := sig.CanonicalValue() if err != nil { return "", err } metadataHash := sha256.Sum256(metadata) return strings.ToLower(fmt.Sprintf("sha256:%s", hex.EncodeToString(metadataHash[:]))), nil } func (v V001Entry) Insertable() (bool, error) { if v.TufObj.Metadata == nil { return false, errors.New("missing metadata property") } if v.TufObj.Metadata.Content == nil { return false, errors.New("missing metadata content") } if v.TufObj.Root == nil { return false, errors.New("missing root property") } if v.TufObj.Root.Content == nil { return false, errors.New("missing root content") } return true, nil } func (v V001Entry) parseRootContent() ([]byte, error) { var keyBytes []byte // Root.Content can either be a base64-encoded string or object switch v := v.TufObj.Root.Content.(type) { case string: b, err := base64.StdEncoding.DecodeString(v) if err != nil { return nil, fmt.Errorf("base64 decoding TUF root content: %w", err) } keyBytes = b default: var err error keyBytes, err = json.Marshal(v) if err != nil { return nil, err } } return keyBytes, nil } func (v V001Entry) parseMetadataContent() ([]byte, error) { var sigBytes []byte // Metadata.Content can either be a base64-encoded string or object switch v := v.TufObj.Metadata.Content.(type) { case string: b, err := base64.StdEncoding.DecodeString(v) if err != nil { return nil, fmt.Errorf("base64 decoding TUF metadata content: %w", err) } sigBytes = b default: var err error sigBytes, err = json.Marshal(v) if err != nil { return nil, err } } return sigBytes, nil } rekor-1.3.5/pkg/types/tuf/v0.0.1/entry_test.go000066400000000000000000000227351455727245600207700ustar00rootroot00000000000000/* Copyright © 2021 The Sigstore Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package tuf import ( "bytes" "context" "encoding/base64" "encoding/json" "os" "reflect" "testing" "time" "github.com/go-openapi/runtime" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/theupdateframework/go-tuf/data" "github.com/theupdateframework/go-tuf/verify" "go.uber.org/goleak" ) func patchIsExpired() func() { // Patch out the IsExpired to make the tests stable :) old := verify.IsExpired verify.IsExpired = func(t time.Time) bool { return false } return func() { verify.IsExpired = old } } func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } func TestNewEntryReturnType(t *testing.T) { entry := NewEntry() if reflect.TypeOf(entry) != reflect.ValueOf(&V001Entry{}).Type() { t.Errorf("invalid type returned from NewEntry: %T", entry) } } func TestCrossFieldValidation(t *testing.T) { defer patchIsExpired()() type TestCase struct { caseDesc string entry V001Entry expectUnmarshalSuccess bool expectCanonicalizeSuccess bool expectVerifierSuccess bool } keyBytes, _ := os.ReadFile("tests/test_root.json") dataBytes, _ := os.ReadFile("tests/test_timestamp.json") anyBytes, _ := os.ReadFile("tests/test_any.json") keyContent := &data.Signed{} if err := json.Unmarshal(keyBytes, keyContent); err != nil { t.Errorf("unexpected error") } dataContent := &data.Signed{} if err := json.Unmarshal(dataBytes, dataContent); err != nil { t.Errorf("unexpected error") } anyContent := &data.Signed{} if err := json.Unmarshal(anyBytes, anyContent); err != nil { t.Errorf("unexpected error") } testCases := []TestCase{ { caseDesc: "root without content", entry: V001Entry{ TufObj: models.TUFV001Schema{ Root: &models.TUFV001SchemaRoot{}, Metadata: &models.TUFV001SchemaMetadata{}, }, }, expectUnmarshalSuccess: false, expectVerifierSuccess: false, }, { caseDesc: "root without manifest", entry: V001Entry{ TufObj: models.TUFV001Schema{ Root: &models.TUFV001SchemaRoot{ Content: keyContent, }, Metadata: &models.TUFV001SchemaMetadata{}, }, }, expectUnmarshalSuccess: false, expectVerifierSuccess: true, }, { caseDesc: "root with invalid manifest & valid metadata", entry: V001Entry{ TufObj: models.TUFV001Schema{ Root: &models.TUFV001SchemaRoot{ Content: anyContent, }, Metadata: &models.TUFV001SchemaMetadata{ Content: dataContent, }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: false, expectVerifierSuccess: false, }, { caseDesc: "root with manifest & content", entry: V001Entry{ TufObj: models.TUFV001Schema{ Root: &models.TUFV001SchemaRoot{ Content: keyContent, }, Metadata: &models.TUFV001SchemaMetadata{ Content: dataContent, }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: true, expectVerifierSuccess: true, }, { caseDesc: "root with manifest & content base64-encoded", entry: V001Entry{ TufObj: models.TUFV001Schema{ Root: &models.TUFV001SchemaRoot{ Content: base64.StdEncoding.EncodeToString(keyBytes), }, Metadata: &models.TUFV001SchemaMetadata{ Content: base64.StdEncoding.EncodeToString(dataBytes), }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: true, expectVerifierSuccess: true, }, { caseDesc: "root with invalid key content & with manifest with content", entry: V001Entry{ TufObj: models.TUFV001Schema{ Root: &models.TUFV001SchemaRoot{ Content: dataContent, }, Metadata: &models.TUFV001SchemaMetadata{ Content: dataContent, }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: false, expectVerifierSuccess: false, }, { caseDesc: "public key with key content & with invalid data with content", entry: V001Entry{ TufObj: models.TUFV001Schema{ Root: &models.TUFV001SchemaRoot{ Content: keyContent, }, Metadata: &models.TUFV001SchemaMetadata{ Content: anyContent, }, }, }, expectUnmarshalSuccess: true, expectCanonicalizeSuccess: false, expectVerifierSuccess: true, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if err := tc.entry.Validate(); (err == nil) != tc.expectUnmarshalSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } // No need to continue here if we failed at unmarshal if !tc.expectUnmarshalSuccess { return } v := &V001Entry{} r := models.TUF{ APIVersion: swag.String(tc.entry.APIVersion()), Spec: tc.entry.TufObj, } unmarshalAndValidate := func() error { if err := v.Unmarshal(&r); err != nil { return err } return v.Validate() } if err := unmarshalAndValidate(); (err == nil) != tc.expectUnmarshalSuccess { t.Errorf("unexpected result in '%v': %v", tc.caseDesc, err) } if tc.expectUnmarshalSuccess { if ok, err := v.Insertable(); !ok || err != nil { t.Errorf("unexpected error calling Insertable on valid proposed entry: %v", err) } } b, err := v.Canonicalize(context.TODO()) if (err == nil) != tc.expectCanonicalizeSuccess { t.Errorf("unexpected result from Canonicalize for '%v': %v", tc.caseDesc, err) } else if err != nil { if _, ok := err.(types.ValidationError); !ok { t.Errorf("canonicalize returned an unexpected error that isn't of type types.ValidationError: %v", err) } } if b != nil { pe, err := models.UnmarshalProposedEntry(bytes.NewReader(b), runtime.JSONConsumer()) if err != nil { t.Errorf("unexpected err from Unmarshalling canonicalized entry for '%v': %v", tc.caseDesc, err) } ei, err := types.UnmarshalEntry(pe) if err != nil { t.Errorf("unexpected err from type-specific unmarshalling for '%v': %v", tc.caseDesc, err) } // Insertable on canonicalized content is variable so we skip testing it here hash, err := ei.ArtifactHash() if err != nil { t.Errorf("unexpected failure with ArtifactHash: %v", err) } else if hash != "sha256:c170ae288c93f56031639bac1ad085fc47918346f733b3d76b07a8124ebd24f9" { t.Errorf("unexpected match with ArtifactHash: %s", hash) } } verifiers, err := v.Verifiers() if tc.expectVerifierSuccess { if err != nil { t.Errorf("%v: unexpected error, got %v", tc.caseDesc, err) } else { pub, _ := verifiers[0].CanonicalValue() rootBytes := new(bytes.Buffer) if err := json.Compact(rootBytes, keyBytes); err != nil { t.Fatal(err) } if !reflect.DeepEqual(pub, rootBytes.Bytes()) { t.Errorf("%v: verifier and public keys do not match: %v, %v", tc.caseDesc, string(pub), rootBytes) } } } else { if err == nil { s, _ := verifiers[0].CanonicalValue() t.Errorf("%v: expected error for %v, got %v", tc.caseDesc, string(s), err) } } }) } } func TestInsertable(t *testing.T) { type TestCase struct { caseDesc string entry V001Entry expectSuccess bool } testCases := []TestCase{ { caseDesc: "valid entry", entry: V001Entry{ TufObj: models.TUFV001Schema{ Metadata: &models.TUFV001SchemaMetadata{ Content: struct{}{}, }, Root: &models.TUFV001SchemaRoot{ Content: struct{}{}, }, }, }, expectSuccess: true, }, { caseDesc: "missing root content", entry: V001Entry{ TufObj: models.TUFV001Schema{ Metadata: &models.TUFV001SchemaMetadata{ Content: struct{}{}, }, Root: &models.TUFV001SchemaRoot{ //Content: struct{}{}, }, }, }, expectSuccess: false, }, { caseDesc: "missing root obj", entry: V001Entry{ TufObj: models.TUFV001Schema{ Metadata: &models.TUFV001SchemaMetadata{ Content: struct{}{}, }, /* Root: &models.TUFV001SchemaRoot{ Content: struct{}{}, }, */ }, }, expectSuccess: false, }, { caseDesc: "missing metadata content", entry: V001Entry{ TufObj: models.TUFV001Schema{ Metadata: &models.TUFV001SchemaMetadata{ //Content: struct{}{}, }, Root: &models.TUFV001SchemaRoot{ Content: struct{}{}, }, }, }, expectSuccess: false, }, { caseDesc: "missing metadata content", entry: V001Entry{ TufObj: models.TUFV001Schema{ /* Metadata: &models.TUFV001SchemaMetadata{ Content: struct{}{}, }, */ Root: &models.TUFV001SchemaRoot{ Content: struct{}{}, }, }, }, expectSuccess: false, }, { caseDesc: "empty obj", entry: V001Entry{}, expectSuccess: false, }, } for _, tc := range testCases { t.Run(tc.caseDesc, func(t *testing.T) { if ok, err := tc.entry.Insertable(); ok != tc.expectSuccess { t.Errorf("unexpected result calling Insertable: %v", err) } }) } } rekor-1.3.5/pkg/types/tuf/v0.0.1/fuzz_test.go000066400000000000000000000045641455727245600206250ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tuf import ( "context" "sync" "testing" fuzz "github.com/AdamKorcz/go-fuzz-headers-1" "github.com/go-openapi/swag" fuzzUtils "github.com/sigstore/rekor/pkg/fuzz" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/tuf" ) var initter sync.Once func FuzzTufCreateProposedEntry(f *testing.F) { f.Fuzz(func(t *testing.T, propsData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) version := "0.0.1" ff := fuzz.NewConsumer(propsData) props, cleanup, err := fuzzUtils.CreateProps(ff, "tufV001") if err != nil { t.Skip() } defer func() { for _, c := range cleanup { c() } }() it := tuf.New() entry, err := it.CreateProposedEntry(context.Background(), version, props) if err != nil { t.Skip() } ei, err := types.CreateVersionedEntry(entry) if err != nil { t.Skip() } if ok, err := ei.Insertable(); !ok || err != nil { t.Errorf("entry created via CreateProposedEntry should be insertable: %v", err) } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Errorf("valid insertable entry should be able to be canonicalized: %v", err) } _, _ = ei.IndexKeys() }) } func FuzzTufUnmarshalAndCanonicalize(f *testing.F) { f.Fuzz(func(t *testing.T, entryData []byte) { initter.Do(fuzzUtils.SetFuzzLogger) ff := fuzz.NewConsumer(entryData) targetV001 := &models.TUFV001Schema{} if err := ff.GenerateStruct(targetV001); err != nil { t.Skip() } targetEntry := &models.TUF{ APIVersion: swag.String(APIVERSION), Spec: targetV001, } ei, err := types.UnmarshalEntry(targetEntry) if err != nil { t.Skip() } if _, err := types.CanonicalizeEntry(context.Background(), ei); err != nil { t.Skip() } }) } rekor-1.3.5/pkg/types/tuf/v0.0.1/tests/000077500000000000000000000000001455727245600173725ustar00rootroot00000000000000rekor-1.3.5/pkg/types/tuf/v0.0.1/tests/test_any.json000066400000000000000000000000221455727245600221050ustar00rootroot00000000000000{ "foo": "bar" } rekor-1.3.5/pkg/types/tuf/v0.0.1/tests/test_root.json000066400000000000000000000117511455727245600223140ustar00rootroot00000000000000{ "signatures": [ { "keyid": "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "sig": "30450221008a35d51da0f845301a5eac98ad0df00a934f59b709c1eaf81c86be734d9356f80220742942325599749800f52675f6efe124345980a2a636c0dc76f9caf9fc3123b0" }, { "keyid": "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", "sig": "3045022100ef9157ece2a09baec1eab80adfc00b04da20b1f9a0d1b47c5dabc4506719ef2c022074f72acd57398e4ddc8c2a5040df902961e9615dca48f3fbe38cbb506e500066" }, { "keyid": "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", "sig": "30450220420fdc9a09cd069b8b15fd8db9cedf7d0dee75871bd1cfee77c926d4120a770002210097553b5ad0d6b4a13902ed37509638bb63a9009f78230cd56c802909ffbfead7" }, { "keyid": "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", "sig": "304502202aaf32e66f90752f658672b085ecfe45cc1ad31ee6cf5c9ad05f3267685f8d88022100b5df02acdaa371123db9d7a42219553fe079b230b168833e951be7ee56ded347" }, { "keyid": "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209", "sig": "304402205d420c7d05c58980c1c9f7d221f53b5334aae27a447d2a91c2ceddd685269749022039ec83e51f8e1779d7f0142dfa4a5bbecfe327fc0b91b7416090fea2416fd53a" } ], "signed": { "_type": "root", "consistent_snapshot": false, "expires": "2021-12-18T13:28:12.99008-06:00", "keys": { "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa-sha2-nistp256", "keyval": { "public": "04cbc5cab2684160323c25cd06c3307178a6b1d1c9b949328453ae473c5ba7527e35b13f298b41633382241f3fd8526c262d43b45adee5c618fa0642c82b8a9803" }, "scheme": "ecdsa-sha2-nistp256" }, "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa-sha2-nistp256", "keyval": { "public": "04a71aacd835dc170ba6db3fa33a1a33dee751d4f8b0217b805b9bd3242921ee93672fdcfd840576c5bb0dc0ed815edf394c1ee48c2b5e02485e59bfc512f3adc7" }, "scheme": "ecdsa-sha2-nistp256" }, "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa-sha2-nistp256", "keyval": { "public": "04117b33dd265715bf23315e368faa499728db8d1f0a377070a1c7b1aba2cc21be6ab1628e42f2cdd7a35479f2dce07b303a8ba646c55569a8d2a504ba7e86e447" }, "scheme": "ecdsa-sha2-nistp256" }, "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa-sha2-nistp256", "keyval": { "public": "04cc1cd53a61c23e88cc54b488dfae168a257c34fac3e88811c55962b24cffbfecb724447999c54670e365883716302e49da57c79a33cd3e16f81fbc66f0bcdf48" }, "scheme": "ecdsa-sha2-nistp256" }, "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa-sha2-nistp256", "keyval": { "public": "048a78a44ac01099890d787e5e62afc29c8ccb69a70ec6549a6b04033b0a8acbfb42ab1ab9c713d225cdb52b858886cf46c8e90a7f3b9e6371882f370c259e1c5b" }, "scheme": "ecdsa-sha2-nistp256" } }, "roles": { "root": { "keyids": [ "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" ], "threshold": 3 }, "snapshot": { "keyids": [ "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" ], "threshold": 3 }, "targets": { "keyids": [ "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" ], "threshold": 3 }, "timestamp": { "keyids": [ "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209" ], "threshold": 3 } }, "spec_version": "1.0", "version": 1 } } rekor-1.3.5/pkg/types/tuf/v0.0.1/tests/test_timestamp.json000066400000000000000000000031261455727245600233310ustar00rootroot00000000000000{ "signatures": [ { "keyid": "2f64fb5eac0cf94dd39bb45308b98920055e9a0d8e012a7220787834c60aef97", "sig": "3044022079252576532ed5ed4a19e4135997d89172101ed745a4489be6b20d04d483bbcc0220515119aab690033dc1e1650f08995dc839dcd161cab3898db0749063ca32dc86" }, { "keyid": "bdde902f5ec668179ff5ca0dabf7657109287d690bf97e230c21d65f99155c62", "sig": "3045022100c5216dd17d381c951b5174f8ee2157b315d1f26247e7f9e49c42cf975dfcf49b022048fb1751a86fddedc21129e94a3e7e0efeeb93f1238fad6636bbf0c0d39543e8" }, { "keyid": "eaf22372f417dd618a46f6c627dbc276e9fd30a004fc94f9be946e73f8bd090b", "sig": "3045022042c6b4003deee27db7db6f5aebb29ac89625fd7389dfff434fa93c65cf8aed5f022100fe6cbd036b5fce1169d7392ecfaf76e01f05fdc6c81cf9bae8c9227fc09c65d9" }, { "keyid": "f40f32044071a9365505da3d1e3be6561f6f22d0e60cf51df783999f6c3429cb", "sig": "3045022100bc431b7315c2aa657418835005692021de7496bbc7c1a2fedf2aafe8d861ca5402200cbca80a4555d8236265e1b746743532894b46257c450ff8706d0e50e659978c" }, { "keyid": "f505595165a177a41750a8e864ed1719b1edfccd5a426fd2c0ffda33ce7ff209", "sig": "3046022100c09aea2f05e94a656bd70379340c7b5f09b24bbd20adf4855be3783d7ce39482022100da93a8a1577599979d38bfc44016bde5838d9548797fc9e960780276855bbcf9" } ], "signed": { "_type": "timestamp", "expires": "2021-12-18T13:28:12.99008-06:00", "meta": { "snapshot.json": { "hashes": { "sha512": "9103503c18f7da2098dce04892948ad240c1b9965048c4ab4da0c32248f3491652d91d76fe9022be2cf15a99e68b3a3ddd1034e5293c8aac86d0446c4354716d" }, "length": 1849, "version": 1 } }, "spec_version": "1.0", "version": 1 } } rekor-1.3.5/pkg/types/tuf/v0.0.1/tuf_v0_0_1_schema.json000066400000000000000000000024711455727245600223110ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://rekor.sigstore.dev/types/tuf/tuf_v0_0_1_schema.json", "title": "TUF v0.0.1 Schema", "description": "Schema for TUF metadata entries", "type": "object", "properties": { "spec_version": { "description": "TUF specification version", "type": "string", "readOnly": true }, "metadata": { "description": "TUF metadata", "type": "object", "properties": { "content": { "description": "Specifies the metadata inline within the document", "type": "object", "additionalProperties": true } }, "required": [ "content" ] }, "root" : { "description": "root metadata containing about the public keys used to sign the manifest", "type": "object", "properties": { "content": { "description": "Specifies the metadata inline within the document", "type": "object", "additionalProperties": true } }, "required": [ "content" ] } }, "required": [ "metadata" , "root"] } rekor-1.3.5/pkg/types/types.go000066400000000000000000000067471455727245600163210ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package types import ( "context" "errors" "fmt" "sync" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/log" "golang.org/x/exp/slices" ) // TypeMap stores mapping between type strings and entry constructors // entries are written once at process initialization and read for each transaction, so we use // sync.Map which is optimized for this case var TypeMap sync.Map // RekorType is the base struct that is embedded in all type implementations type RekorType struct { Kind string // this is the unique string that identifies the type VersionMap VersionEntryFactoryMap // this maps the supported versions to implementation } // TypeImpl is implemented by all types to support the polymorphic conversion of abstract // proposed entry to a working implementation for the versioned type requested, if supported type TypeImpl interface { CreateProposedEntry(context.Context, string, ArtifactProperties) (models.ProposedEntry, error) DefaultVersion() string SupportedVersions() []string IsSupportedVersion(string) bool UnmarshalEntry(pe models.ProposedEntry) (EntryImpl, error) } // VersionedUnmarshal extracts the correct implementing factory function from the type's version map, // creates an entry of that versioned type and then calls that versioned type's unmarshal method func (rt *RekorType) VersionedUnmarshal(pe models.ProposedEntry, version string) (EntryImpl, error) { ef, err := rt.VersionMap.GetEntryFactory(version) if err != nil { return nil, fmt.Errorf("%s implementation for version '%v' not found: %w", rt.Kind, version, err) } entry := ef() if entry == nil { return nil, errors.New("failure generating object") } if pe == nil { return entry, nil } return entry, entry.Unmarshal(pe) } // SupportedVersions returns a list of versions of this type that can be currently entered into the log func (rt *RekorType) SupportedVersions() []string { return rt.VersionMap.SupportedVersions() } // IsSupportedVersion returns true if the version can be inserted into the log, and false if not func (rt *RekorType) IsSupportedVersion(proposedVersion string) bool { return slices.Contains(rt.SupportedVersions(), proposedVersion) } // ListImplementedTypes returns a list of all type strings currently known to // be implemented func ListImplementedTypes() []string { retVal := []string{} TypeMap.Range(func(k interface{}, v interface{}) bool { tf := v.(func() TypeImpl) for _, verStr := range tf().SupportedVersions() { retVal = append(retVal, fmt.Sprintf("%v:%v", k.(string), verStr)) } return true }) return retVal } type errCloser interface { CloseWithError(error) error } func PipeCloser(errClosers ...errCloser) func(err error) error { return func(err error) error { for _, p := range errClosers { if err := p.CloseWithError(err); err != nil { log.Logger.Error(fmt.Errorf("error closing pipe: %w", err)) } } return err } } rekor-1.3.5/pkg/types/types_test.go000066400000000000000000000040431455727245600173430ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package types import ( "context" "errors" "testing" "github.com/go-openapi/strfmt" "go.uber.org/goleak" "github.com/sigstore/rekor/pkg/generated/models" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } type InvalidEntry struct{} func (e InvalidEntry) Kind() string { return "invalid" } func (e InvalidEntry) SetKind(string) {} func (e InvalidEntry) Validate(_ strfmt.Registry) error { return nil } func (e InvalidEntry) ContextValidate(_ context.Context, _ strfmt.Registry) error { return nil } type UnmarshalErrorValidEntry struct{} func (e UnmarshalErrorValidEntry) Kind() string { if _, found := TypeMap.Load("rekord"); found { return "rekord" } return "" } func (e UnmarshalErrorValidEntry) SetKind(string) {} func (e UnmarshalErrorValidEntry) Validate(_ strfmt.Registry) error { return errors.New("invalid content") } func (e UnmarshalErrorValidEntry) ContextValidate(_ context.Context, _ strfmt.Registry) error { return nil } func TestUnmarshalEntry(t *testing.T) { type TestCase struct { entry models.ProposedEntry expectSuccess bool } testCases := []TestCase{ { entry: InvalidEntry{}, expectSuccess: false, }, { entry: UnmarshalErrorValidEntry{}, expectSuccess: false, }, } for _, tc := range testCases { if _, err := UnmarshalEntry(tc.entry); (err == nil) != tc.expectSuccess { t.Errorf("unexpected error creating entry of type '%v': %v", tc.entry.Kind(), err) } } } rekor-1.3.5/pkg/types/versionmap.go000066400000000000000000000053661455727245600173340ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package types import ( "fmt" "sync" "github.com/blang/semver" "github.com/sigstore/rekor/pkg/log" ) // VersionEntryFactoryMap defines a map-like interface to find the correct implementation for a version string // This could be a simple map[string][EntryFactory], or something more elegant (e.g. semver) type VersionEntryFactoryMap interface { GetEntryFactory(string) (EntryFactory, error) // return the entry factory for the specified version SetEntryFactory(string, EntryFactory) error // set the entry factory for the specified version Count() int // return the count of entry factories currently in the map SupportedVersions() []string // return a list of versions currently stored in the map } // SemVerEntryFactoryMap implements a map that allows implementations to specify their supported versions using // semver-compliant strings type SemVerEntryFactoryMap struct { factoryMap map[string]EntryFactory sync.RWMutex } func NewSemVerEntryFactoryMap() VersionEntryFactoryMap { s := SemVerEntryFactoryMap{} s.factoryMap = make(map[string]EntryFactory) return &s } func (s *SemVerEntryFactoryMap) Count() int { return len(s.factoryMap) } func (s *SemVerEntryFactoryMap) GetEntryFactory(version string) (EntryFactory, error) { s.RLock() defer s.RUnlock() semverToMatch, err := semver.Parse(version) if err != nil { log.Logger.Error(err) return nil, err } // will return first function that matches for k, v := range s.factoryMap { semverRange, err := semver.ParseRange(k) if err != nil { log.Logger.Error(err) return nil, err } if semverRange(semverToMatch) { return v, nil } } return nil, fmt.Errorf("unable to locate entry for version %s", version) } func (s *SemVerEntryFactoryMap) SetEntryFactory(constraint string, ef EntryFactory) error { s.Lock() defer s.Unlock() if _, err := semver.ParseRange(constraint); err != nil { log.Logger.Error(err) return err } s.factoryMap[constraint] = ef return nil } func (s *SemVerEntryFactoryMap) SupportedVersions() []string { var versions []string for k := range s.factoryMap { versions = append(versions, k) } return versions } rekor-1.3.5/pkg/util/000077500000000000000000000000001455727245600144215ustar00rootroot00000000000000rekor-1.3.5/pkg/util/checkpoint.go000066400000000000000000000115501455727245600171010ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package util import ( "bytes" "context" "encoding/base64" "errors" "fmt" "strconv" "strings" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" ) // heavily borrowed from https://github.com/transparency-dev/formats/blob/main/log/checkpoint.go type Checkpoint struct { // Origin is the unique identifier/version string Origin string // Size is the number of entries in the log at this checkpoint. Size uint64 // Hash is the hash which commits to the contents of the entire log. Hash []byte // OtherContent is any additional data to be included in the signed payload; each element is assumed to be one line OtherContent []string } // String returns the String representation of the Checkpoint func (c Checkpoint) String() string { var b strings.Builder fmt.Fprintf(&b, "%s\n%d\n%s\n", c.Origin, c.Size, base64.StdEncoding.EncodeToString(c.Hash)) for _, line := range c.OtherContent { fmt.Fprintf(&b, "%s\n", line) } return b.String() } // MarshalText returns the common format representation of this Checkpoint. func (c Checkpoint) MarshalCheckpoint() ([]byte, error) { return []byte(c.String()), nil } // UnmarshalText parses the common formatted checkpoint data and stores the result // in the Checkpoint. // // The supplied data is expected to begin with the following 3 lines of text, // each followed by a newline: // // // // ... // ... // // This will discard any content found after the checkpoint (including signatures) func (c *Checkpoint) UnmarshalCheckpoint(data []byte) error { l := bytes.Split(data, []byte("\n")) if len(l) < 4 { return errors.New("invalid checkpoint - too few newlines") } origin := string(l[0]) if len(origin) == 0 { return errors.New("invalid checkpoint - empty ecosystem") } size, err := strconv.ParseUint(string(l[1]), 10, 64) if err != nil { return fmt.Errorf("invalid checkpoint - size invalid: %w", err) } h, err := base64.StdEncoding.DecodeString(string(l[2])) if err != nil { return fmt.Errorf("invalid checkpoint - invalid hash: %w", err) } *c = Checkpoint{ Origin: origin, Size: size, Hash: h, } if len(l) >= 3 { for _, line := range l[3:] { if len(line) == 0 { break } c.OtherContent = append(c.OtherContent, string(line)) } } return nil } type SignedCheckpoint struct { Checkpoint SignedNote } func CreateSignedCheckpoint(c Checkpoint) (*SignedCheckpoint, error) { text, err := c.MarshalCheckpoint() if err != nil { return nil, err } return &SignedCheckpoint{ Checkpoint: c, SignedNote: SignedNote{Note: string(text)}, }, nil } func SignedCheckpointValidator(strToValidate string) bool { s := SignedNote{} if err := s.UnmarshalText([]byte(strToValidate)); err != nil { return false } c := &Checkpoint{} return c.UnmarshalCheckpoint([]byte(s.Note)) == nil } func CheckpointValidator(strToValidate string) bool { c := &Checkpoint{} return c.UnmarshalCheckpoint([]byte(strToValidate)) == nil } func (r *SignedCheckpoint) UnmarshalText(data []byte) error { s := SignedNote{} if err := s.UnmarshalText([]byte(data)); err != nil { return fmt.Errorf("unmarshalling signed note: %w", err) } c := Checkpoint{} if err := c.UnmarshalCheckpoint([]byte(s.Note)); err != nil { return fmt.Errorf("unmarshalling checkpoint: %w", err) } *r = SignedCheckpoint{Checkpoint: c, SignedNote: s} return nil } // CreateAndSignCheckpoint creates a signed checkpoint as a commitment to the current root hash func CreateAndSignCheckpoint(ctx context.Context, hostname string, treeID int64, treeSize uint64, rootHash []byte, signer signature.Signer) ([]byte, error) { sth, err := CreateSignedCheckpoint(Checkpoint{ Origin: fmt.Sprintf("%s - %d", hostname, treeID), Size: treeSize, Hash: rootHash, }) if err != nil { return nil, fmt.Errorf("error creating checkpoint: %v", err) } if _, err := sth.Sign(hostname, signer, options.WithContext(ctx)); err != nil { return nil, fmt.Errorf("error signing checkpoint: %v", err) } scBytes, err := sth.SignedNote.MarshalText() if err != nil { return nil, fmt.Errorf("error marshalling checkpoint: %v", err) } return scBytes, nil } rekor-1.3.5/pkg/util/checkpoint_test.go000066400000000000000000000361721455727245600201470ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package util import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/sha256" "fmt" "testing" "github.com/google/go-cmp/cmp" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" "golang.org/x/mod/sumdb/note" ) // heavily borrowed from https://github.com/google/trillian-examples/blob/master/formats/log/checkpoint_test.go func TestMarshalCheckpoint(t *testing.T) { for _, test := range []struct { c Checkpoint want string }{ { c: Checkpoint{ Origin: "Log Checkpoint v0", Size: 123, Hash: []byte("bananas"), }, want: "Log Checkpoint v0\n123\nYmFuYW5hcw==\n", }, { c: Checkpoint{ Origin: "Banana Checkpoint v5", Size: 9944, Hash: []byte("the view from the tree tops is great!"), }, want: "Banana Checkpoint v5\n9944\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n", }, { c: Checkpoint{ Origin: "Banana Checkpoint v7", Size: 9943, Hash: []byte("the view from the tree tops is great!"), OtherContent: []string{"foo", "bar"}, }, want: "Banana Checkpoint v7\n9943\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\nfoo\nbar\n", }, } { t.Run(string(test.c.Hash), func(t *testing.T) { got, err := test.c.MarshalCheckpoint() if err != nil { t.Fatalf("unexpected error marshalling: %v", err) } if string(got) != test.want { t.Fatalf("Marshal = %q, want %q", got, test.want) } }) } } func TestUnmarshalCheckpoint(t *testing.T) { for _, test := range []struct { desc string m string want Checkpoint wantErr bool }{ { desc: "valid one", m: "Log Checkpoint v0\n123\nYmFuYW5hcw==\n", want: Checkpoint{ Origin: "Log Checkpoint v0", Size: 123, Hash: []byte("bananas"), }, }, { desc: "valid with different ecosystem", m: "Banana Checkpoint v1\n9944\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n", want: Checkpoint{ Origin: "Banana Checkpoint v1", Size: 9944, Hash: []byte("the view from the tree tops is great!"), }, }, { desc: "valid with trailing data", m: "Log Checkpoint v0\n9944\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\nHere's some associated data.\n", want: Checkpoint{ Origin: "Log Checkpoint v0", Size: 9944, Hash: []byte("the view from the tree tops is great!"), OtherContent: []string{"Here's some associated data."}, }, }, { desc: "valid with multiple trailing data lines", m: "Log Checkpoint v0\n9944\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\nlots\nof\nlines\n", want: Checkpoint{ Origin: "Log Checkpoint v0", Size: 9944, Hash: []byte("the view from the tree tops is great!"), OtherContent: []string{"lots", "of", "lines"}, }, }, { desc: "valid with trailing newlines", m: "Log Checkpoint v0\n9944\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n\n\n\n", want: Checkpoint{ Origin: "Log Checkpoint v0", Size: 9944, Hash: []byte("the view from the tree tops is great!"), }, }, { desc: "invalid - insufficient lines", m: "Head\n9944\n", wantErr: true, }, { desc: "invalid - empty header", m: "\n9944\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n", wantErr: true, }, { desc: "invalid - missing newline on roothash", m: "Log Checkpoint v0\n123\nYmFuYW5hcw==", wantErr: true, }, { desc: "invalid size - not a number", m: "Log Checkpoint v0\nbananas\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n", wantErr: true, }, { desc: "invalid size - negative", m: "Log Checkpoint v0\n-34\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n", wantErr: true, }, { desc: "invalid size - too large", m: "Log Checkpoint v0\n3438945738945739845734895735\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n", wantErr: true, }, { desc: "invalid roothash - not base64", m: "Log Checkpoint v0\n123\nThisIsn'tBase64\n", wantErr: true, }, } { t.Run(string(test.desc), func(t *testing.T) { var got Checkpoint var gotErr error if gotErr = got.UnmarshalCheckpoint([]byte(test.m)); (gotErr != nil) != test.wantErr { t.Fatalf("Unmarshal = %q, wantErr: %T", gotErr, test.wantErr) } if diff := cmp.Diff(test.want, got); len(diff) != 0 { t.Fatalf("Unmarshalled Checkpoint with diff %s", diff) } if !test.wantErr != CheckpointValidator(test.m) { t.Fatalf("Validator failed for %s", test.desc) } }) } } func TestSigningRoundtripCheckpoint(t *testing.T) { rsaKey, _ := rsa.GenerateKey(rand.Reader, 2048) ecdsaKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) edPubKey, edPrivKey, _ := ed25519.GenerateKey(rand.Reader) for _, test := range []struct { c Checkpoint identity string signer crypto.Signer pubKey crypto.PublicKey opts crypto.SignerOpts wantSignErr bool wantVerifyErr bool }{ { c: Checkpoint{ Origin: "Log Checkpoint RSA v0", Size: 123, Hash: []byte("bananas"), }, identity: "someone", signer: rsaKey, pubKey: rsaKey.Public(), opts: &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthAuto, Hash: crypto.SHA256}, wantSignErr: false, wantVerifyErr: false, }, { c: Checkpoint{ Origin: "Log Checkpoint ECDSA v0", Size: 123, Hash: []byte("bananas"), }, identity: "someone", signer: ecdsaKey, pubKey: ecdsaKey.Public(), opts: nil, wantSignErr: false, wantVerifyErr: false, }, { c: Checkpoint{ Origin: "Log Checkpoint Ed25519 v0", Size: 123, Hash: []byte("bananas"), }, identity: "someone", signer: edPrivKey, pubKey: edPubKey, opts: crypto.Hash(0), wantSignErr: false, wantVerifyErr: false, }, { c: Checkpoint{ Origin: "Log Checkpoint With Timestamp", Size: 123, Hash: []byte("bananas"), OtherContent: []string{"Timestamp: 12345"}, }, identity: "someone", signer: edPrivKey, pubKey: edPubKey, opts: crypto.Hash(0), wantSignErr: false, wantVerifyErr: false, }, { c: Checkpoint{ Origin: "Log Checkpoint With Multiple Other Contents", Size: 123, Hash: []byte("bananas"), OtherContent: []string{"Timestamp: 12345", "Extra: Foo Bar"}, }, identity: "someone", signer: edPrivKey, pubKey: edPubKey, opts: crypto.Hash(0), wantSignErr: false, wantVerifyErr: false, }, { c: Checkpoint{ Origin: "Log Checkpoint Mismatch v0", Size: 123, Hash: []byte("bananas"), }, identity: "someone", signer: edPrivKey, pubKey: ecdsaKey.Public(), opts: crypto.Hash(0), wantSignErr: false, wantVerifyErr: true, }, { c: Checkpoint{ Origin: "Log Checkpoint Mismatch v1", Size: 123, Hash: []byte("bananas"), }, identity: "someone", signer: ecdsaKey, pubKey: rsaKey.Public(), opts: &rsa.PSSOptions{Hash: crypto.SHA256}, wantSignErr: false, wantVerifyErr: true, }, { c: Checkpoint{ Origin: "Log Checkpoint Mismatch v2", Size: 123, Hash: []byte("bananas"), }, identity: "someone", signer: edPrivKey, pubKey: rsaKey.Public(), opts: &rsa.PSSOptions{Hash: crypto.SHA256}, wantSignErr: false, wantVerifyErr: true, }, { c: Checkpoint{ Origin: "Log Checkpoint Mismatch v3", Size: 123, Hash: []byte("bananas"), }, identity: "someone", signer: ecdsaKey, pubKey: edPubKey, opts: nil, wantSignErr: false, wantVerifyErr: true, }, } { t.Run(string(test.c.Origin), func(t *testing.T) { sth, err := CreateSignedCheckpoint(test.c) if err != nil { t.Fatalf("error creating signed checkpoint") } signer, _ := signature.LoadSigner(test.signer, crypto.SHA256) if _, ok := test.signer.(*rsa.PrivateKey); ok { signer, _ = signature.LoadRSAPSSSigner(test.signer.(*rsa.PrivateKey), crypto.SHA256, test.opts.(*rsa.PSSOptions)) } _, err = sth.Sign(test.identity, signer, options.WithCryptoSignerOpts(test.opts)) if (err != nil) != test.wantSignErr { t.Fatalf("signing test failed: wantSignErr %v, err %v", test.wantSignErr, err) } if !test.wantSignErr { verifier, _ := signature.LoadVerifier(test.pubKey, crypto.SHA256) if _, ok := test.pubKey.(*rsa.PublicKey); ok { verifier, _ = signature.LoadRSAPSSVerifier(test.pubKey.(*rsa.PublicKey), crypto.SHA256, test.opts.(*rsa.PSSOptions)) } if !sth.Verify(verifier) != test.wantVerifyErr { t.Fatalf("verification test failed %v", sth.Verify(verifier)) } if _, err := sth.Sign("second", signer, options.WithCryptoSignerOpts(test.opts)); err != nil { t.Fatalf("adding second signature failed: %v", err) } if len(sth.Signatures) != 2 { t.Fatalf("expected two signatures on checkpoint, only found %v", len(sth.Signatures)) } // finally, test marshalling object and unmarshalling marshalledSc, err := sth.MarshalText() if err != nil { t.Fatalf("error during marshalling: %v", err) } sth2, _ := CreateSignedCheckpoint(test.c) if err := sth2.UnmarshalText(marshalledSc); err != nil { t.Fatalf("error unmarshalling just marshalled object %v\n%v", err, string(marshalledSc)) } if diff := cmp.Diff(sth, sth2); len(diff) != 0 { t.Fatalf("UnmarshalText = diff %s", diff) } } }) } } func TestInvalidSigVerification(t *testing.T) { ecdsaKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) for _, test := range []struct { checkpoint Checkpoint s []note.Signature pubKey crypto.PublicKey expectedResult bool }{ { checkpoint: Checkpoint{ Origin: "Log Checkpoint v0", Size: 123, Hash: []byte("bananas"), }, s: []note.Signature{}, pubKey: ecdsaKey.Public(), expectedResult: false, }, { checkpoint: Checkpoint{ Origin: "Log Checkpoint v0 not base64", Size: 123, Hash: []byte("bananas"), }, pubKey: ecdsaKey.Public(), s: []note.Signature{ { Name: "something", Hash: 1234, Base64: "not_base 64 string", }, }, expectedResult: false, }, { checkpoint: Checkpoint{ Origin: "Log Checkpoint v0 invalid signature", Size: 123, Hash: []byte("bananas"), }, pubKey: ecdsaKey.Public(), s: []note.Signature{ { Name: "someone", Hash: 142, Base64: "bm90IGEgc2ln", // valid base64, not a valid signature }, }, expectedResult: false, }, } { t.Run(string(test.checkpoint.Origin), func(t *testing.T) { text, _ := test.checkpoint.MarshalCheckpoint() sc := SignedNote{ Note: string(text), Signatures: test.s, } verifier, _ := signature.LoadVerifier(test.pubKey, crypto.SHA256) result := sc.Verify(verifier) if result != test.expectedResult { t.Fatal("verification test generated unexpected result") } }) } } // does not test validity of signatures but merely parsing logic func TestUnmarshalSignedCheckpoint(t *testing.T) { for _, test := range []struct { desc string m string wantErr bool }{ { desc: "invalid checkpoint, no signatures", m: "Log Checkpoint v0\n\nYmFuYW5hcw==\n\n", wantErr: true, }, { desc: "valid checkpoint, no signatures", m: "Log Checkpoint v0\n123\nYmFuYW5hcw==\n\n", wantErr: true, }, { desc: "incorrect signature line format", m: "Banana Checkpoint v1\n9944\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n\n* name not-a-sig\n", wantErr: true, }, { desc: "signature not base64 encoded", m: "Banana Checkpoint v1\n9944\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n\n\u2014 name not-b64\n", wantErr: true, }, { desc: "missing identity", m: "Banana Checkpoint v1\n9944\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n\n\u2014 YQ==\n", wantErr: true, }, { desc: "signature base64 encoded but too short", m: "Banana Checkpoint v1\n9944\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n\n\u2014 name YQ==\n", wantErr: true, }, { desc: "valid signed checkpoint - single signature", m: "Banana Checkpoint v1\n9944\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n\n\u2014 name pOhM+S/mYjEYtQsOF4lL8o/dR+nbjoz5Cvg/n486KIismpVq0s4wxBaakmryI7zThjWAqRUyECPL3WSEcVDEBQ==\n", wantErr: false, }, { desc: "valid signed checkpoint - two signatures", m: "Banana Checkpoint v1\n9944\ndGhlIHZpZXcgZnJvbSB0aGUgdHJlZSB0b3BzIGlzIGdyZWF0IQ==\n\n\u2014 name pOhM+S/mYjEYtQsOF4lL8o/dR+nbjoz5Cvg/n486KIismpVq0s4wxBaakmryI7zThjWAqRUyECPL3WSEcVDEBQ==\n\u2014 another_name pOhM+S/mYjEYtQsOF4lL8o/dR+nbjoz5Cvg/n486KIismpVq0s4wxBaakmryI7zThjWAqRUyECPL3WSEcVDEBQ==\n", wantErr: false, }, } { t.Run(string(test.desc), func(t *testing.T) { var got SignedNote var gotErr error if gotErr = got.UnmarshalText([]byte(test.m)); (gotErr != nil) != test.wantErr { t.Fatalf("UnmarshalText(%s) = %q, wantErr: %v", test.desc, gotErr, test.wantErr) } if !test.wantErr != SignedCheckpointValidator(test.m) { t.Fatalf("Validator failed for %s", test.desc) } }) } } func TestSignCheckpoint(t *testing.T) { hostname := "rekor.localhost" treeID := int64(123) rootHash := sha256.Sum256([]byte{1, 2, 3}) treeSize := uint64(42) signer, _, err := signature.NewDefaultECDSASignerVerifier() if err != nil { t.Fatalf("error generating signer: %v", err) } ctx := context.Background() scBytes, err := CreateAndSignCheckpoint(ctx, hostname, treeID, treeSize, rootHash[:], signer) if err != nil { t.Fatalf("error creating signed checkpoint: %v", err) } sth := SignedCheckpoint{} if err := sth.UnmarshalText(scBytes); err != nil { t.Fatalf("error unmarshalling signed checkpoint: %v", err) } if !sth.Verify(signer) { t.Fatalf("checkpoint signature invalid") } expectedOrigin := fmt.Sprintf("%s - %d", hostname, treeID) if sth.Origin != fmt.Sprintf("%s - %d", hostname, treeID) { t.Fatalf("unexpected origin: got %s, expected %s", expectedOrigin, sth.Origin) } if !bytes.Equal(sth.Hash, rootHash[:]) { t.Fatalf("unexpected mismatch of root hash") } if sth.Size != treeSize { t.Fatalf("unexpected tree size: got %d, expected %d", sth.Size, treeSize) } } rekor-1.3.5/pkg/util/fetch.go000066400000000000000000000026261455727245600160470ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package util import ( "bytes" "context" "fmt" "io" "net/http" ) // FileOrURLReadCloser Note: caller is responsible for closing ReadCloser returned from method! func FileOrURLReadCloser(ctx context.Context, url string, content []byte) (io.ReadCloser, error) { var dataReader io.ReadCloser if url != "" { //TODO: set timeout here, SSL settings? client := &http.Client{} req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, err } resp, err := client.Do(req) if err != nil { return nil, err } if resp.StatusCode < 200 || resp.StatusCode > 299 { return nil, fmt.Errorf("error received while fetching artifact '%v': %v", url, resp.Status) } dataReader = resp.Body } else { dataReader = io.NopCloser(bytes.NewReader(content)) } return dataReader, nil } rekor-1.3.5/pkg/util/sha.go000066400000000000000000000032351455727245600155260ustar00rootroot00000000000000// Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package util import ( "crypto" "fmt" "strings" ) // PrefixSHA sets the prefix of a sha hash to match how it is stored based on the length. func PrefixSHA(sha string) string { var prefix string var components = strings.Split(sha, ":") if len(components) == 2 { return sha } switch len(sha) { case 40: prefix = "sha1:" case 64: prefix = "sha256:" case 96: prefix = "sha384:" case 128: prefix = "sha512:" } return fmt.Sprintf("%v%v", prefix, sha) } func UnprefixSHA(sha string) (crypto.Hash, string) { components := strings.Split(sha, ":") if len(components) == 2 { prefix := components[0] sha = components[1] switch prefix { case "sha1": return crypto.SHA1, sha case "sha256": return crypto.SHA256, sha case "sha384": return crypto.SHA384, sha case "sha512": return crypto.SHA512, sha default: return crypto.Hash(0), "" } } switch len(sha) { case 40: return crypto.SHA1, sha case 64: return crypto.SHA256, sha case 96: return crypto.SHA384, sha case 128: return crypto.SHA512, sha } return crypto.Hash(0), "" } rekor-1.3.5/pkg/util/sha_test.go000066400000000000000000000073001455727245600165620ustar00rootroot00000000000000// Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package util import ( "crypto" "testing" ) func TestPrefixSHA(t *testing.T) { var testCases = []struct { input string want string }{ { input: "123", want: "123", }, { input: "sha512:abc", want: "sha512:abc", }, { input: "09b80428c53912d4174162fd5b7c7d485bdcc3ab", want: "sha1:09b80428c53912d4174162fd5b7c7d485bdcc3ab", }, { input: "b9869be95b24001702120dd5dd673a9bd8447446fb57220388d8d0a48c738808", want: "sha256:b9869be95b24001702120dd5dd673a9bd8447446fb57220388d8d0a48c738808", }, { input: "cfd356237e261871e8f92ae6710a75a65a925ae121d94d28533f008bd3e00b5472d261b5d0e1ab4082e3078dd1ad2af57876ed3c1c797c4097dbed870f458408", want: "sha512:cfd356237e261871e8f92ae6710a75a65a925ae121d94d28533f008bd3e00b5472d261b5d0e1ab4082e3078dd1ad2af57876ed3c1c797c4097dbed870f458408", }, { input: "78674b244bc9cba8ecb6dcb660b059728236e36b2f30fbcd6e17b1b64255f3ac596fbe5c84d1cc9d2a0979513260de09", want: "sha384:78674b244bc9cba8ecb6dcb660b059728236e36b2f30fbcd6e17b1b64255f3ac596fbe5c84d1cc9d2a0979513260de09", }, } for _, tr := range testCases { got := PrefixSHA(tr.input) if got != tr.want { t.Errorf("Got '%s' expected '%s'", got, tr.want) } } } func TestUnprefixSHA(t *testing.T) { type prefixedSHA struct { crypto.Hash string } var testCases = []struct { input string want prefixedSHA }{ { input: "87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7", want: prefixedSHA{ crypto.SHA256, "87428fc522803d31065e7bce3cf03fe475096631e5e07bbd7a0fde60c4cf25c7", }, }, { input: "sha512:162b0b32f02482d5aca0a7c93dd03ceac3acd7e410a5f18f3fb990fc958ae0df6f32233b91831eaf99ca581a8c4ddf9c8ba315ac482db6d4ea01cc7884a635be", want: prefixedSHA{ crypto.SHA512, "162b0b32f02482d5aca0a7c93dd03ceac3acd7e410a5f18f3fb990fc958ae0df6f32233b91831eaf99ca581a8c4ddf9c8ba315ac482db6d4ea01cc7884a635be", }, }, { input: "09b80428c53912d4174162fd5b7c7d485bdcc3ab", want: prefixedSHA{ crypto.SHA1, "09b80428c53912d4174162fd5b7c7d485bdcc3ab", }, }, { input: "cfd356237e261871e8f92ae6710a75a65a925ae121d94d28533f008bd3e00b5472d261b5d0e1ab4082e3078dd1ad2af57876ed3c1c797c4097dbed870f458408", want: prefixedSHA{ crypto.SHA512, "cfd356237e261871e8f92ae6710a75a65a925ae121d94d28533f008bd3e00b5472d261b5d0e1ab4082e3078dd1ad2af57876ed3c1c797c4097dbed870f458408", }, }, { input: "78674b244bc9cba8ecb6dcb660b059728236e36b2f30fbcd6e17b1b64255f3ac596fbe5c84d1cc9d2a0979513260de09", want: prefixedSHA{ crypto.SHA384, "78674b244bc9cba8ecb6dcb660b059728236e36b2f30fbcd6e17b1b64255f3ac596fbe5c84d1cc9d2a0979513260de09", }, }, { input: "sha384:78674b244bc9cba8ecb6dcb660b059728236e36b2f30fbcd6e17b1b64255f3ac596fbe5c84d1cc9d2a0979513260de09", want: prefixedSHA{ crypto.SHA384, "78674b244bc9cba8ecb6dcb660b059728236e36b2f30fbcd6e17b1b64255f3ac596fbe5c84d1cc9d2a0979513260de09", }, }, } for _, tr := range testCases { algo, value := UnprefixSHA(tr.input) got := prefixedSHA{algo, value} if got != tr.want { t.Errorf("Got '%v' expected '%v' (input %s)", got, tr.want, tr.input) } } } rekor-1.3.5/pkg/util/signed_note.go000066400000000000000000000126031455727245600172500ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package util import ( "bufio" "bytes" "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/binary" "errors" "fmt" "strings" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" "golang.org/x/mod/sumdb/note" ) type SignedNote struct { // Textual representation of a note to sign. Note string // Signatures are one or more signature lines covering the payload Signatures []note.Signature } // Sign adds a signature to a SignedCheckpoint object // The signature is added to the signature array as well as being directly returned to the caller func (s *SignedNote) Sign(identity string, signer signature.Signer, opts signature.SignOption) (*note.Signature, error) { sig, err := signer.SignMessage(bytes.NewReader([]byte(s.Note)), opts) if err != nil { return nil, fmt.Errorf("signing note: %w", err) } pk, err := signer.PublicKey() if err != nil { return nil, fmt.Errorf("retrieving public key: %w", err) } pubKeyBytes, err := x509.MarshalPKIXPublicKey(pk) if err != nil { return nil, fmt.Errorf("marshalling public key: %w", err) } pkSha := sha256.Sum256(pubKeyBytes) signature := note.Signature{ Name: identity, Hash: binary.BigEndian.Uint32(pkSha[:]), Base64: base64.StdEncoding.EncodeToString(sig), } s.Signatures = append(s.Signatures, signature) return &signature, nil } // Verify checks that one of the signatures can be successfully verified using // the supplied public key func (s SignedNote) Verify(verifier signature.Verifier) bool { if len(s.Signatures) == 0 { return false } msg := []byte(s.Note) digest := sha256.Sum256(msg) for _, s := range s.Signatures { sigBytes, err := base64.StdEncoding.DecodeString(s.Base64) if err != nil { return false } pk, err := verifier.PublicKey() if err != nil { return false } opts := []signature.VerifyOption{} switch pk.(type) { case *rsa.PublicKey, *ecdsa.PublicKey: opts = append(opts, options.WithDigest(digest[:])) case ed25519.PublicKey: break default: return false } if err := verifier.VerifySignature(bytes.NewReader(sigBytes), bytes.NewReader(msg), opts...); err != nil { return false } } return true } // MarshalText returns the common format representation of this SignedNote. func (s SignedNote) MarshalText() ([]byte, error) { return []byte(s.String()), nil } // String returns the String representation of the SignedNote func (s SignedNote) String() string { var b strings.Builder b.WriteString(s.Note) b.WriteRune('\n') for _, sig := range s.Signatures { var hbuf [4]byte binary.BigEndian.PutUint32(hbuf[:], sig.Hash) sigBytes, _ := base64.StdEncoding.DecodeString(sig.Base64) b64 := base64.StdEncoding.EncodeToString(append(hbuf[:], sigBytes...)) fmt.Fprintf(&b, "%c %s %s\n", '\u2014', sig.Name, b64) } return b.String() } // UnmarshalText parses the common formatted signed note data and stores the result // in the SignedNote. THIS DOES NOT VERIFY SIGNATURES INSIDE THE CONTENT! // // The supplied data is expected to contain a single Note, followed by a single // line with no comment, followed by one or more lines with the following format: // // \u2014 name signature // // - name is the string associated with the signer // - signature is a base64 encoded string; the first 4 bytes of the decoded value is a // hint to the public key; it is a big-endian encoded uint32 representing the first // 4 bytes of the SHA256 hash of the public key func (s *SignedNote) UnmarshalText(data []byte) error { sigSplit := []byte("\n\n") // Must end with signature block preceded by blank line. split := bytes.LastIndex(data, sigSplit) if split < 0 { return errors.New("malformed note") } text, data := data[:split+1], data[split+2:] if len(data) == 0 || data[len(data)-1] != '\n' { return errors.New("malformed note") } sn := SignedNote{ Note: string(text), } b := bufio.NewScanner(bytes.NewReader(data)) for b.Scan() { var name, signature string if _, err := fmt.Fscanf(strings.NewReader(b.Text()), "\u2014 %s %s\n", &name, &signature); err != nil { return fmt.Errorf("parsing signature: %w", err) } sigBytes, err := base64.StdEncoding.DecodeString(signature) if err != nil { return fmt.Errorf("decoding signature: %w", err) } if len(sigBytes) < 5 { return errors.New("signature is too small") } sig := note.Signature{ Name: name, Hash: binary.BigEndian.Uint32(sigBytes[0:4]), Base64: base64.StdEncoding.EncodeToString(sigBytes[4:]), } sn.Signatures = append(sn.Signatures, sig) } if len(sn.Signatures) == 0 { return errors.New("no signatures found in input") } // copy sc to s *s = sn return nil } func SignedNoteValidator(strToValidate string) bool { s := SignedNote{} return s.UnmarshalText([]byte(strToValidate)) == nil } rekor-1.3.5/pkg/util/util.go000066400000000000000000000454041455727245600157340ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package util import ( "bytes" "encoding/base64" "fmt" "io/ioutil" "math/rand" "os" "os/exec" "path" "path/filepath" "strings" "testing" "time" "golang.org/x/crypto/openpgp" ) var ( cli = "rekor-cli" server = "rekor-server" keys openpgp.EntityList ) type GetOut struct { Attestation string AttestationType string Body interface{} LogIndex int IntegratedTime int64 } // This was generated with gpg --gen-key, and all defaults. // The email is "test@rekor.dev", and the name is Rekor Test. // It should only be used for test purposes. var secretKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- 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 =CwjQ -----END PGP PRIVATE KEY BLOCK-----` var PrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- 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 =CwjQ -----END PGP PRIVATE KEY BLOCK-----` var PubKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- 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 WR6Fv7ySHTl7cQ13jGmSR1N8hg== =Fen+ -----END PGP PUBLIC KEY BLOCK-----` func init() { p := os.Getenv("REKORTMPDIR") if p != "" { cli = path.Join(p, cli) server = path.Join(p, server) } var err error keys, err = openpgp.ReadArmoredKeyRing(strings.NewReader(secretKey)) if err != nil { panic(err) } } func OutputContains(t *testing.T, output, sub string) { t.Helper() if !strings.Contains(output, sub) { t.Errorf("Expected [%s] in response, got %s", sub, output) } } func Run(t *testing.T, stdin, cmd string, arg ...string) string { t.Helper() arg = append([]string{coverageFlag()}, arg...) c := exec.Command(cmd, arg...) if stdin != "" { c.Stdin = strings.NewReader(stdin) } if os.Getenv("REKORTMPDIR") != "" { // ensure that we use a clean state.json file for each Run c.Env = append(c.Env, "HOME="+os.Getenv("REKORTMPDIR")) } b, err := c.CombinedOutput() if err != nil { t.Log(string(b)) t.Fatal(err) } return stripCoverageOutput(string(b)) } func RunCli(t *testing.T, arg ...string) string { t.Helper() arg = append(arg, rekorServerFlag()) // use a blank config file to ensure no collision if os.Getenv("REKORTMPDIR") != "" { arg = append(arg, "--config="+os.Getenv("REKORTMPDIR")+".rekor.yaml") } return Run(t, "", cli, arg...) } func RunCliErr(t *testing.T, arg ...string) string { t.Helper() arg = append([]string{coverageFlag()}, arg...) arg = append(arg, rekorServerFlag()) // use a blank config file to ensure no collision if os.Getenv("REKORTMPDIR") != "" { arg = append(arg, "--config="+os.Getenv("REKORTMPDIR")+".rekor.yaml") } cmd := exec.Command(cli, arg...) b, err := cmd.CombinedOutput() if err == nil { t.Log(string(b)) t.Fatalf("expected error, got %s", string(b)) } return stripCoverageOutput(string(b)) } func rekorServerFlag() string { return fmt.Sprintf("--rekor_server=%s", rekorServer()) } func rekorServer() string { if s := os.Getenv("REKOR_SERVER"); s != "" { return s } return "http://localhost:3000" } func coverageFlag() string { return "-test.coverprofile=/tmp/pkg-rekor-cli." + RandomSuffix(8) + ".cov" } func stripCoverageOutput(out string) string { return strings.Split(strings.Split(out, "PASS")[0], "FAIL")[0] } // RandomSuffix returns a random string of the given length. func RandomSuffix(n int) string { const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" b := make([]byte, n) for i := range b { b[i] = letterBytes[rand.Intn(len(letterBytes))] } return string(b) } // RandomData returns a random byte slice of the given size. func RandomData(t *testing.T, n int) []byte { t.Helper() rand.Seed(time.Now().UnixNano()) data := make([]byte, n) if _, err := rand.Read(data[:]); err != nil { t.Fatal(err) } return data } func CreateArtifact(t *testing.T, artifactPath string) string { t.Helper() // First let's generate some random data so we don't have to worry about dupes. data := RandomData(t, 100) artifact := base64.StdEncoding.EncodeToString(data[:]) // Write this to a file write(t, artifact, artifactPath) return artifact } func write(t *testing.T, data string, path string) { t.Helper() if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil { t.Fatal(err) } } func GetUUIDFromUploadOutput(t *testing.T, out string) string { t.Helper() // Output looks like "Artifact timestamped at ...\m Wrote response \n Created entry at index X, available at $URL/UUID", so grab the UUID: urlTokens := strings.Split(strings.TrimSpace(out), " ") url := urlTokens[len(urlTokens)-1] splitUrl := strings.Split(url, "/") return splitUrl[len(splitUrl)-1] } func SignPGP(b []byte) ([]byte, error) { var buf bytes.Buffer if err := openpgp.DetachSign(&buf, keys[0], bytes.NewReader(b), nil); err != nil { return nil, err } return buf.Bytes(), nil } func Write(t *testing.T, data string, path string) { t.Helper() if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil { t.Fatal(err) } } // CreatedPGPSignedArtifact gets the test dir setup correctly with some random artifacts and keys. func CreatedPGPSignedArtifact(t *testing.T, artifactPath, sigPath string) { t.Helper() artifact := CreateArtifact(t, artifactPath) // Sign it with our key and write that to a file signature, err := SignPGP([]byte(artifact)) if err != nil { t.Fatal(err) } if err := ioutil.WriteFile(sigPath, signature, 0644); err != nil { t.Fatal(err) } } func GetUUIDFromTimestampOutput(t *testing.T, out string) string { t.Helper() // Output looks like "Created entry at index X, available at $URL/UUID", so grab the UUID: urlTokens := strings.Split(strings.TrimSpace(out), "\n") return GetUUIDFromUploadOutput(t, urlTokens[len(urlTokens)-1]) } // SetupTestData is a helper function to setups the test data func SetupTestData(t *testing.T) { // create a temp directory artifactPath := filepath.Join(t.TempDir(), "artifact") // create a temp file sigPath := filepath.Join(t.TempDir(), "signature.asc") CreatedPGPSignedArtifact(t, artifactPath, sigPath) // Write the public key to a file pubPath := filepath.Join(t.TempDir(), "pubKey.asc") if err := ioutil.WriteFile(pubPath, []byte(PubKey), 0644); err != nil { //nolint:gosec t.Fatal(err) } // Now upload to rekor! out := RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) OutputContains(t, out, "Created entry at") } rekor-1.3.5/pkg/verify/000077500000000000000000000000001455727245600147505ustar00rootroot00000000000000rekor-1.3.5/pkg/verify/verify.go000066400000000000000000000174631455727245600166160ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package verify import ( "bytes" "context" "encoding/base64" "encoding/hex" "encoding/json" "errors" "fmt" "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer" "github.com/sigstore/rekor/pkg/generated/client" "github.com/sigstore/rekor/pkg/generated/client/tlog" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/util" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" "github.com/transparency-dev/merkle/proof" "github.com/transparency-dev/merkle/rfc6962" ) // ProveConsistency verifies consistency between an initial, trusted STH // and a second new STH. Callers MUST verify signature on the STHs'. func ProveConsistency(ctx context.Context, rClient *client.Rekor, oldSTH *util.SignedCheckpoint, newSTH *util.SignedCheckpoint, treeID string) error { oldTreeSize := int64(oldSTH.Size) switch { case oldTreeSize == 0: return errors.New("consistency proofs can not be computed starting from an empty log") case oldTreeSize == int64(newSTH.Size): if !bytes.Equal(oldSTH.Hash, newSTH.Hash) { return errors.New("old root hash does not match STH hash") } case oldTreeSize < int64(newSTH.Size): consistencyParams := tlog.NewGetLogProofParamsWithContext(ctx) consistencyParams.FirstSize = &oldTreeSize // Root size at the old, or trusted state. consistencyParams.LastSize = int64(newSTH.Size) // Root size at the new state to verify against. consistencyParams.TreeID = &treeID consistencyProof, err := rClient.Tlog.GetLogProof(consistencyParams) if err != nil { return err } var hashes [][]byte for _, h := range consistencyProof.Payload.Hashes { b, err := hex.DecodeString(h) if err != nil { return errors.New("error decoding consistency proof hashes") } hashes = append(hashes, b) } if err := proof.VerifyConsistency(rfc6962.DefaultHasher, oldSTH.Size, newSTH.Size, hashes, oldSTH.Hash, newSTH.Hash); err != nil { return err } case oldTreeSize > int64(newSTH.Size): return errors.New("inclusion proof returned a tree size larger than the verified tree size") } return nil } // VerifyCurrentCheckpoint verifies the provided checkpoint by verifying consistency // against a newly fetched Checkpoint. // nolint func VerifyCurrentCheckpoint(ctx context.Context, rClient *client.Rekor, verifier signature.Verifier, oldSTH *util.SignedCheckpoint) (*util.SignedCheckpoint, error) { // The oldSTH should already be verified, but check for robustness. if !oldSTH.Verify(verifier) { return nil, errors.New("signature on old tree head did not verify") } // Get and verify against the current STH. infoParams := tlog.NewGetLogInfoParamsWithContext(ctx) result, err := rClient.Tlog.GetLogInfo(infoParams) if err != nil { return nil, err } logInfo := result.GetPayload() sth := util.SignedCheckpoint{} if err := sth.UnmarshalText([]byte(*logInfo.SignedTreeHead)); err != nil { return nil, err } // Verify the signature on the SignedCheckpoint. if !sth.Verify(verifier) { return nil, errors.New("signature on tree head did not verify") } // Now verify consistency up to the STH. if err := ProveConsistency(ctx, rClient, oldSTH, &sth, *logInfo.TreeID); err != nil { return nil, err } return &sth, nil } // VerifyCheckpointSignature verifies the signature on a checkpoint (signed tree head). It does // not verify consistency against other checkpoints. // nolint func VerifyCheckpointSignature(e *models.LogEntryAnon, verifier signature.Verifier) error { sth := &util.SignedCheckpoint{} if err := sth.UnmarshalText([]byte(*e.Verification.InclusionProof.Checkpoint)); err != nil { return fmt.Errorf("unmarshalling log entry checkpoint to SignedCheckpoint: %w", err) } if !sth.Verify(verifier) { return errors.New("signature on checkpoint did not verify") } rootHash, err := hex.DecodeString(*e.Verification.InclusionProof.RootHash) if err != nil { return errors.New("decoding inclusion proof root has") } if !bytes.EqualFold(rootHash, sth.Hash) { return fmt.Errorf("proof root hash does not match signed tree head, expected %s got %s", *e.Verification.InclusionProof.RootHash, hex.EncodeToString(sth.Hash)) } return nil } // VerifyInclusion verifies an entry's inclusion proof. Clients MUST either verify // the root hash against a new STH (via VerifyCurrentCheckpoint) or against a // trusted, existing STH (via ProveConsistency). // nolint func VerifyInclusion(ctx context.Context, e *models.LogEntryAnon) error { if e.Verification == nil || e.Verification.InclusionProof == nil { return errors.New("inclusion proof not provided") } hashes := [][]byte{} for _, h := range e.Verification.InclusionProof.Hashes { hb, _ := hex.DecodeString(h) hashes = append(hashes, hb) } rootHash, err := hex.DecodeString(*e.Verification.InclusionProof.RootHash) if err != nil { return err } // Verify the inclusion proof. entryBytes, err := base64.StdEncoding.DecodeString(e.Body.(string)) if err != nil { return err } leafHash := rfc6962.DefaultHasher.HashLeaf(entryBytes) if err := proof.VerifyInclusion(rfc6962.DefaultHasher, uint64(*e.Verification.InclusionProof.LogIndex), uint64(*e.Verification.InclusionProof.TreeSize), leafHash, hashes, rootHash); err != nil { return err } return nil } // VerifySignedEntryTimestamp verifies the entry's SET against the provided // public key. // nolint func VerifySignedEntryTimestamp(ctx context.Context, e *models.LogEntryAnon, verifier signature.Verifier) error { if e.Verification == nil { return fmt.Errorf("missing verification") } if e.Verification.SignedEntryTimestamp == nil { return fmt.Errorf("signature missing") } type bundle struct { Body interface{} `json:"body"` IntegratedTime int64 `json:"integratedTime"` // Note that this is the virtual index. LogIndex int64 `json:"logIndex"` LogID string `json:"logID"` } bundlePayload := bundle{ Body: e.Body, IntegratedTime: *e.IntegratedTime, LogIndex: *e.LogIndex, LogID: *e.LogID, } contents, err := json.Marshal(bundlePayload) if err != nil { return fmt.Errorf("marshaling bundle: %w", err) } canonicalized, err := jsoncanonicalizer.Transform(contents) if err != nil { return fmt.Errorf("canonicalizing bundle: %w", err) } // verify the SET against the public key if err := verifier.VerifySignature(bytes.NewReader(e.Verification.SignedEntryTimestamp), bytes.NewReader(canonicalized), options.WithContext(ctx)); err != nil { return fmt.Errorf("unable to verify bundle: %w", err) } return nil } // VerifyLogEntry performs verification of a LogEntry given a Rekor verifier. // Performs inclusion proof verification up to a verified root hash, // SignedEntryTimestamp verification, and checkpoint verification. // nolint func VerifyLogEntry(ctx context.Context, e *models.LogEntryAnon, verifier signature.Verifier) error { // Verify the inclusion proof using the body's leaf hash. if err := VerifyInclusion(ctx, e); err != nil { return err } // Verify checkpoint, which includes a signed root hash. if err := VerifyCheckpointSignature(e, verifier); err != nil { return err } // Verify the Signed Entry Timestamp. if err := VerifySignedEntryTimestamp(ctx, e, verifier); err != nil { return err } return nil } rekor-1.3.5/pkg/verify/verify_test.go000066400000000000000000000271111455727245600176440ustar00rootroot00000000000000// // Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package verify import ( "context" "crypto/sha256" "encoding/hex" "testing" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/sigstore/rekor/pkg/generated/client" "github.com/sigstore/rekor/pkg/generated/client/tlog" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/util" "github.com/sigstore/sigstore/pkg/signature" ) type TlogClient struct { Proof []string Root string LogInfo models.LogInfo } func (m *TlogClient) GetLogProof(_ *tlog.GetLogProofParams, _ ...tlog.ClientOption) (*tlog.GetLogProofOK, error) { return &tlog.GetLogProofOK{ Payload: &models.ConsistencyProof{ Hashes: m.Proof, RootHash: &m.Root, }}, nil } func (m *TlogClient) GetLogInfo(_ *tlog.GetLogInfoParams, _ ...tlog.ClientOption) (*tlog.GetLogInfoOK, error) { return &tlog.GetLogInfoOK{ Payload: &m.LogInfo, }, nil } // TODO: Implement mock func (m *TlogClient) SetTransport(_ runtime.ClientTransport) { } func TestConsistency(t *testing.T) { root2String := "5be1758dd2228acfaf2546b4b6ce8aa40c82a3748f3dcb550e0d67ba34f02a45" root2, _ := hex.DecodeString(root2String) root1, _ := hex.DecodeString("59a575f157274702c38de3ab1e1784226f391fb79500ebf9f02b4439fb77574c") root0, _ := hex.DecodeString("1a341bc342ff4e567387de9789ab14000b147124317841489172419874198147") hashes := []string{"d3be742c8d73e2dd3c5635843e987ad3dfb3837616f412a07bf730c3ad73f5cb"} for _, test := range []struct { name string oldC util.Checkpoint newC util.Checkpoint Proof []string wantErr bool }{ { name: "zero length proof", oldC: util.Checkpoint{ Origin: "test", Size: uint64(2), Hash: root2, }, newC: util.Checkpoint{ Origin: "test", Size: uint64(2), Hash: root2, }, wantErr: false, }, { name: "valid consistency proof", oldC: util.Checkpoint{ Origin: "test", Size: uint64(1), Hash: root1, }, newC: util.Checkpoint{ Origin: "test", Size: uint64(2), Hash: root2, }, wantErr: false, }, { name: "invalid new sth request", oldC: util.Checkpoint{ Origin: "test", Size: uint64(2), Hash: root1, }, newC: util.Checkpoint{ Origin: "test", Size: uint64(1), Hash: root2, }, wantErr: true, }, { name: "invalid consistency proof", oldC: util.Checkpoint{ Origin: "test", Size: uint64(1), Hash: root2, }, newC: util.Checkpoint{ Origin: "test", Size: uint64(2), Hash: root1, }, wantErr: true, }, { name: "invalid consistency - same size", oldC: util.Checkpoint{ Origin: "test", Size: uint64(1), Hash: root1, }, newC: util.Checkpoint{ Origin: "test", Size: uint64(1), Hash: root2, }, wantErr: true, }, { name: "invalid consistency - empty log", oldC: util.Checkpoint{ Origin: "test", Size: uint64(0), Hash: root0, }, newC: util.Checkpoint{ Origin: "test", Size: uint64(2), Hash: root2, }, wantErr: true, }, } { var mClient client.Rekor mClient.Tlog = &TlogClient{Proof: hashes, Root: root2String} t.Run(string(test.name), func(t *testing.T) { ctx := context.Background() treeID := "123" oldSTH, err := util.CreateSignedCheckpoint(test.oldC) if err != nil { t.Fatalf("creating old checkpoint") } newSTH, err := util.CreateSignedCheckpoint(test.newC) if err != nil { t.Fatalf("creating new checkpoint") } gotErr := ProveConsistency(ctx, &mClient, oldSTH, newSTH, treeID) if (gotErr != nil) != test.wantErr { t.Fatalf("ProveConsistency = %t, wantErr %t", gotErr, test.wantErr) } }) } } func TestInclusion(t *testing.T) { time := int64(1661794812) logID := "1701474e8cb504dbb853a5887bc2cf66936b0f36d2641bfb61f1abae80088e6a" for _, test := range []struct { name string e models.LogEntryAnon wantErr bool }{ { name: "valid inclusion", e: models.LogEntryAnon{ Body: "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoicmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlY2RjNTUzNmY3M2JkYWU4ODE2ZjBlYTQwNzI2ZWY1ZTliODEwZDkxNDQ5MzA3NTkwM2JiOTA2MjNkOTdiMWQ4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUQvUGRQUW1LV0MxKzBCTkVkNWdLdlFHcjF4eGwzaWVVZmZ2M2prMXp6Skt3SWhBTEJqM3hmQXlXeGx6NGpwb0lFSVYxVWZLOXZua1VVT1NvZVp4QlpQSEtQQyIsImZvcm1hdCI6Ing1MDkiLCJwdWJsaWNLZXkiOnsiY29udGVudCI6IkxTMHRMUzFDUlVkSlRpQlFWVUpNU1VNZ1MwVlpMUzB0TFMwS1RVWnJkMFYzV1VoTGIxcEplbW93UTBGUldVbExiMXBKZW1vd1JFRlJZMFJSWjBGRlRVOWpWR1pTUWxNNWFtbFlUVGd4UmxvNFoyMHZNU3R2YldWTmR3cHRiaTh6TkRjdk5UVTJaeTlzY21sVE56SjFUV2haT1V4alZDczFWVW8yWmtkQ1oyeHlOVm80VERCS1RsTjFZWE41WldRNVQzUmhVblozUFQwS0xTMHRMUzFGVGtRZ1VGVkNURWxESUV0RldTMHRMUzB0Q2c9PSJ9fX19", IntegratedTime: &time, LogID: &logID, LogIndex: swag.Int64(1), Verification: &models.LogEntryAnonVerification{ InclusionProof: &models.InclusionProof{ TreeSize: swag.Int64(int64(2)), RootHash: swag.String("5be1758dd2228acfaf2546b4b6ce8aa40c82a3748f3dcb550e0d67ba34f02a45"), LogIndex: swag.Int64(1), Hashes: []string{ "59a575f157274702c38de3ab1e1784226f391fb79500ebf9f02b4439fb77574c", }, }, SignedEntryTimestamp: strfmt.Base64("MEUCIHJj8xP+oPTd4BAXhO2lcbRplnKW2FafMiFo0gIDGUcYAiEA80BJ8QikiupGAv3R3dtSvZ1ICsAOQat10cFKPqBkLBM="), }, }, wantErr: false, }, { name: "invalid inclusion - bad body hash", e: models.LogEntryAnon{ Body: "ayJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoicmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlY2RjNTUzNmY3M2JkYWU4ODE2ZjBlYTQwNzI2ZWY1ZTliODEwZDkxNDQ5MzA3NTkwM2JiOTA2MjNkOTdiMWQ4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUQvUGRQUW1LV0MxKzBCTkVkNWdLdlFHcjF4eGwzaWVVZmZ2M2prMXp6Skt3SWhBTEJqM3hmQXlXeGx6NGpwb0lFSVYxVWZLOXZua1VVT1NvZVp4QlpQSEtQQyIsImZvcm1hdCI6Ing1MDkiLCJwdWJsaWNLZXkiOnsiY29udGVudCI6IkxTMHRMUzFDUlVkSlRpQlFWVUpNU1VNZ1MwVlpMUzB0TFMwS1RVWnJkMFYzV1VoTGIxcEplbW93UTBGUldVbExiMXBKZW1vd1JFRlJZMFJSWjBGRlRVOWpWR1pTUWxNNWFtbFlUVGd4UmxvNFoyMHZNU3R2YldWTmR3cHRiaTh6TkRjdk5UVTJaeTlzY21sVE56SjFUV2haT1V4alZDczFWVW8yWmtkQ1oyeHlOVm80VERCS1RsTjFZWE41WldRNVQzUmhVblozUFQwS0xTMHRMUzFGVGtRZ1VGVkNURWxESUV0RldTMHRMUzB0Q2c9PSJ9fX19", IntegratedTime: &time, LogID: &logID, LogIndex: swag.Int64(1), Verification: &models.LogEntryAnonVerification{ InclusionProof: &models.InclusionProof{ TreeSize: swag.Int64(int64(2)), RootHash: swag.String("5be1758dd2228acfaf2546b4b6ce8aa40c82a3748f3dcb550e0d67ba34f02a45"), LogIndex: swag.Int64(1), Hashes: []string{ "59a575f157274702c38de3ab1e1784226f391fb79500ebf9f02b4439fb77574c", }, }, SignedEntryTimestamp: strfmt.Base64("MEUCIHJj8xP+oPTd4BAXhO2lcbRplnKW2FafMiFo0gIDGUcYAiEA80BJ8QikiupGAv3R3dtSvZ1ICsAOQat10cFKPqBkLBM="), }, }, wantErr: true, }, } { t.Run(string(test.name), func(t *testing.T) { ctx := context.Background() gotErr := VerifyInclusion(ctx, &test.e) if (gotErr != nil) != test.wantErr { t.Fatalf("VerifyInclusion = %t, wantErr %t", gotErr, test.wantErr) } }) } } func TestCheckpoint(t *testing.T) { hostname := "rekor.localhost" treeID := int64(123) rootHash := sha256.Sum256([]byte{1, 2, 3}) rootHashString := hex.EncodeToString(rootHash[:]) treeSize := uint64(42) signer, _, err := signature.NewDefaultECDSASignerVerifier() if err != nil { t.Fatalf("error generating signer: %v", err) } ctx := context.Background() scBytes, err := util.CreateAndSignCheckpoint(ctx, hostname, treeID, treeSize, rootHash[:], signer) if err != nil { t.Fatalf("error creating signed checkpoint: %v", err) } time := int64(1661794812) logID := "1701474e8cb504dbb853a5887bc2cf66936b0f36d2641bfb61f1abae80088e6a" for _, test := range []struct { name string e models.LogEntryAnon wantErr bool }{ { name: "valid checkpoint", e: models.LogEntryAnon{ Body: "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoicmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlY2RjNTUzNmY3M2JkYWU4ODE2ZjBlYTQwNzI2ZWY1ZTliODEwZDkxNDQ5MzA3NTkwM2JiOTA2MjNkOTdiMWQ4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUQvUGRQUW1LV0MxKzBCTkVkNWdLdlFHcjF4eGwzaWVVZmZ2M2prMXp6Skt3SWhBTEJqM3hmQXlXeGx6NGpwb0lFSVYxVWZLOXZua1VVT1NvZVp4QlpQSEtQQyIsImZvcm1hdCI6Ing1MDkiLCJwdWJsaWNLZXkiOnsiY29udGVudCI6IkxTMHRMUzFDUlVkSlRpQlFWVUpNU1VNZ1MwVlpMUzB0TFMwS1RVWnJkMFYzV1VoTGIxcEplbW93UTBGUldVbExiMXBKZW1vd1JFRlJZMFJSWjBGRlRVOWpWR1pTUWxNNWFtbFlUVGd4UmxvNFoyMHZNU3R2YldWTmR3cHRiaTh6TkRjdk5UVTJaeTlzY21sVE56SjFUV2haT1V4alZDczFWVW8yWmtkQ1oyeHlOVm80VERCS1RsTjFZWE41WldRNVQzUmhVblozUFQwS0xTMHRMUzFGVGtRZ1VGVkNURWxESUV0RldTMHRMUzB0Q2c9PSJ9fX19", IntegratedTime: &time, LogID: &logID, LogIndex: swag.Int64(1), Verification: &models.LogEntryAnonVerification{ InclusionProof: &models.InclusionProof{ TreeSize: swag.Int64(int64(2)), RootHash: swag.String(rootHashString), LogIndex: swag.Int64(1), Hashes: []string{ "59a575f157274702c38de3ab1e1784226f391fb79500ebf9f02b4439fb77574c", }, Checkpoint: swag.String(string(scBytes)), }, SignedEntryTimestamp: strfmt.Base64("MEUCIHJj8xP+oPTd4BAXhO2lcbRplnKW2FafMiFo0gIDGUcYAiEA80BJ8QikiupGAv3R3dtSvZ1ICsAOQat10cFKPqBkLBM="), }, }, wantErr: false, }, { name: "root hash mismatch", e: models.LogEntryAnon{ Body: "ayJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoicmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJlY2RjNTUzNmY3M2JkYWU4ODE2ZjBlYTQwNzI2ZWY1ZTliODEwZDkxNDQ5MzA3NTkwM2JiOTA2MjNkOTdiMWQ4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUQvUGRQUW1LV0MxKzBCTkVkNWdLdlFHcjF4eGwzaWVVZmZ2M2prMXp6Skt3SWhBTEJqM3hmQXlXeGx6NGpwb0lFSVYxVWZLOXZua1VVT1NvZVp4QlpQSEtQQyIsImZvcm1hdCI6Ing1MDkiLCJwdWJsaWNLZXkiOnsiY29udGVudCI6IkxTMHRMUzFDUlVkSlRpQlFWVUpNU1VNZ1MwVlpMUzB0TFMwS1RVWnJkMFYzV1VoTGIxcEplbW93UTBGUldVbExiMXBKZW1vd1JFRlJZMFJSWjBGRlRVOWpWR1pTUWxNNWFtbFlUVGd4UmxvNFoyMHZNU3R2YldWTmR3cHRiaTh6TkRjdk5UVTJaeTlzY21sVE56SjFUV2haT1V4alZDczFWVW8yWmtkQ1oyeHlOVm80VERCS1RsTjFZWE41WldRNVQzUmhVblozUFQwS0xTMHRMUzFGVGtRZ1VGVkNURWxESUV0RldTMHRMUzB0Q2c9PSJ9fX19", IntegratedTime: &time, LogID: &logID, LogIndex: swag.Int64(1), Verification: &models.LogEntryAnonVerification{ InclusionProof: &models.InclusionProof{ TreeSize: swag.Int64(int64(2)), RootHash: swag.String("5be1758dd2228acfaf2546b4b6ce8aa40c82a3748f3dcb550e0d67ba34f02a45"), LogIndex: swag.Int64(1), Hashes: []string{ "59a575f157274702c38de3ab1e1784226f391fb79500ebf9f02b4439fb77574c", }, Checkpoint: swag.String(string(scBytes)), }, SignedEntryTimestamp: strfmt.Base64("MEUCIHJj8xP+oPTd4BAXhO2lcbRplnKW2FafMiFo0gIDGUcYAiEA80BJ8QikiupGAv3R3dtSvZ1ICsAOQat10cFKPqBkLBM="), }, }, wantErr: true, }, } { t.Run(string(test.name), func(t *testing.T) { gotErr := VerifyCheckpointSignature(&test.e, signer) if (gotErr != nil) != test.wantErr { t.Fatalf("VerifyCheckpointSignature = %t, wantErr %t", gotErr, test.wantErr) } }) } } rekor-1.3.5/pkg/witness/000077500000000000000000000000001455727245600151405ustar00rootroot00000000000000rekor-1.3.5/pkg/witness/mockclient/000077500000000000000000000000001455727245600172705ustar00rootroot00000000000000rekor-1.3.5/pkg/witness/mockclient/generate.go000066400000000000000000000014641455727245600214160ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package mockclient provides a mockable version of the Trillian log client API. package mockclient //go:generate mockgen -package mockclient -destination mock_log_client.go github.com/google/trillian TrillianLogClient rekor-1.3.5/pkg/witness/mockclient/mock_log_client.go000066400000000000000000000223571455727245600227600ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/google/trillian (interfaces: TrillianLogClient) // Package mockclient is a generated GoMock package. package mockclient import ( context "context" reflect "reflect" gomock "github.com/golang/mock/gomock" trillian "github.com/google/trillian" grpc "google.golang.org/grpc" ) // MockTrillianLogClient is a mock of TrillianLogClient interface. type MockTrillianLogClient struct { ctrl *gomock.Controller recorder *MockTrillianLogClientMockRecorder } // MockTrillianLogClientMockRecorder is the mock recorder for MockTrillianLogClient. type MockTrillianLogClientMockRecorder struct { mock *MockTrillianLogClient } // NewMockTrillianLogClient creates a new mock instance. func NewMockTrillianLogClient(ctrl *gomock.Controller) *MockTrillianLogClient { mock := &MockTrillianLogClient{ctrl: ctrl} mock.recorder = &MockTrillianLogClientMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTrillianLogClient) EXPECT() *MockTrillianLogClientMockRecorder { return m.recorder } // AddSequencedLeaves mocks base method. func (m *MockTrillianLogClient) AddSequencedLeaves(arg0 context.Context, arg1 *trillian.AddSequencedLeavesRequest, arg2 ...grpc.CallOption) (*trillian.AddSequencedLeavesResponse, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "AddSequencedLeaves", varargs...) ret0, _ := ret[0].(*trillian.AddSequencedLeavesResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // AddSequencedLeaves indicates an expected call of AddSequencedLeaves. func (mr *MockTrillianLogClientMockRecorder) AddSequencedLeaves(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddSequencedLeaves", reflect.TypeOf((*MockTrillianLogClient)(nil).AddSequencedLeaves), varargs...) } // GetConsistencyProof mocks base method. func (m *MockTrillianLogClient) GetConsistencyProof(arg0 context.Context, arg1 *trillian.GetConsistencyProofRequest, arg2 ...grpc.CallOption) (*trillian.GetConsistencyProofResponse, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetConsistencyProof", varargs...) ret0, _ := ret[0].(*trillian.GetConsistencyProofResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetConsistencyProof indicates an expected call of GetConsistencyProof. func (mr *MockTrillianLogClientMockRecorder) GetConsistencyProof(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConsistencyProof", reflect.TypeOf((*MockTrillianLogClient)(nil).GetConsistencyProof), varargs...) } // GetEntryAndProof mocks base method. func (m *MockTrillianLogClient) GetEntryAndProof(arg0 context.Context, arg1 *trillian.GetEntryAndProofRequest, arg2 ...grpc.CallOption) (*trillian.GetEntryAndProofResponse, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetEntryAndProof", varargs...) ret0, _ := ret[0].(*trillian.GetEntryAndProofResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetEntryAndProof indicates an expected call of GetEntryAndProof. func (mr *MockTrillianLogClientMockRecorder) GetEntryAndProof(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEntryAndProof", reflect.TypeOf((*MockTrillianLogClient)(nil).GetEntryAndProof), varargs...) } // GetInclusionProof mocks base method. func (m *MockTrillianLogClient) GetInclusionProof(arg0 context.Context, arg1 *trillian.GetInclusionProofRequest, arg2 ...grpc.CallOption) (*trillian.GetInclusionProofResponse, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetInclusionProof", varargs...) ret0, _ := ret[0].(*trillian.GetInclusionProofResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetInclusionProof indicates an expected call of GetInclusionProof. func (mr *MockTrillianLogClientMockRecorder) GetInclusionProof(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInclusionProof", reflect.TypeOf((*MockTrillianLogClient)(nil).GetInclusionProof), varargs...) } // GetInclusionProofByHash mocks base method. func (m *MockTrillianLogClient) GetInclusionProofByHash(arg0 context.Context, arg1 *trillian.GetInclusionProofByHashRequest, arg2 ...grpc.CallOption) (*trillian.GetInclusionProofByHashResponse, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetInclusionProofByHash", varargs...) ret0, _ := ret[0].(*trillian.GetInclusionProofByHashResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetInclusionProofByHash indicates an expected call of GetInclusionProofByHash. func (mr *MockTrillianLogClientMockRecorder) GetInclusionProofByHash(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInclusionProofByHash", reflect.TypeOf((*MockTrillianLogClient)(nil).GetInclusionProofByHash), varargs...) } // GetLatestSignedLogRoot mocks base method. func (m *MockTrillianLogClient) GetLatestSignedLogRoot(arg0 context.Context, arg1 *trillian.GetLatestSignedLogRootRequest, arg2 ...grpc.CallOption) (*trillian.GetLatestSignedLogRootResponse, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetLatestSignedLogRoot", varargs...) ret0, _ := ret[0].(*trillian.GetLatestSignedLogRootResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLatestSignedLogRoot indicates an expected call of GetLatestSignedLogRoot. func (mr *MockTrillianLogClientMockRecorder) GetLatestSignedLogRoot(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestSignedLogRoot", reflect.TypeOf((*MockTrillianLogClient)(nil).GetLatestSignedLogRoot), varargs...) } // GetLeavesByRange mocks base method. func (m *MockTrillianLogClient) GetLeavesByRange(arg0 context.Context, arg1 *trillian.GetLeavesByRangeRequest, arg2 ...grpc.CallOption) (*trillian.GetLeavesByRangeResponse, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "GetLeavesByRange", varargs...) ret0, _ := ret[0].(*trillian.GetLeavesByRangeResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLeavesByRange indicates an expected call of GetLeavesByRange. func (mr *MockTrillianLogClientMockRecorder) GetLeavesByRange(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLeavesByRange", reflect.TypeOf((*MockTrillianLogClient)(nil).GetLeavesByRange), varargs...) } // InitLog mocks base method. func (m *MockTrillianLogClient) InitLog(arg0 context.Context, arg1 *trillian.InitLogRequest, arg2 ...grpc.CallOption) (*trillian.InitLogResponse, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "InitLog", varargs...) ret0, _ := ret[0].(*trillian.InitLogResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // InitLog indicates an expected call of InitLog. func (mr *MockTrillianLogClientMockRecorder) InitLog(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitLog", reflect.TypeOf((*MockTrillianLogClient)(nil).InitLog), varargs...) } // QueueLeaf mocks base method. func (m *MockTrillianLogClient) QueueLeaf(arg0 context.Context, arg1 *trillian.QueueLeafRequest, arg2 ...grpc.CallOption) (*trillian.QueueLeafResponse, error) { m.ctrl.T.Helper() varargs := []interface{}{arg0, arg1} for _, a := range arg2 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "QueueLeaf", varargs...) ret0, _ := ret[0].(*trillian.QueueLeafResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // QueueLeaf indicates an expected call of QueueLeaf. func (mr *MockTrillianLogClientMockRecorder) QueueLeaf(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]interface{}{arg0, arg1}, arg2...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueueLeaf", reflect.TypeOf((*MockTrillianLogClient)(nil).QueueLeaf), varargs...) } rekor-1.3.5/pkg/witness/publish_checkpoint.go000066400000000000000000000151111455727245600213430ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package witness import ( "context" "encoding/hex" "fmt" "strconv" "time" "github.com/google/trillian" "github.com/google/trillian/types" "github.com/prometheus/client_golang/prometheus" "github.com/redis/go-redis/v9" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/trillianclient" "github.com/sigstore/rekor/pkg/util" "github.com/sigstore/sigstore/pkg/signature" "google.golang.org/grpc/codes" ) // CheckpointPublisher is a long-running job to periodically publish signed checkpoints to etc.d type CheckpointPublisher struct { ctx context.Context // logClient is the client for Trillian logClient trillian.TrillianLogClient // treeID is used to construct the origin and configure the Trillian client treeID int64 // hostname is used to construct the origin ("hostname - treeID") hostname string // signer signs the checkpoint signer signature.Signer // publishFreq is how often a new checkpoint is published to Rekor, in minutes checkpointFreq uint // redisClient to upload signed checkpoints redisClient *redis.Client // reqCounter tracks successes and failures for publishing reqCounter *prometheus.CounterVec } // Constant values used with metrics const ( Success = iota SuccessObtainLock GetCheckpoint UnmarshalCheckpoint SignCheckpoint RedisFailure RedisLatestFailure ) // NewCheckpointPublisher creates a CheckpointPublisher to write stable checkpoints to Redis func NewCheckpointPublisher(ctx context.Context, logClient trillian.TrillianLogClient, treeID int64, hostname string, signer signature.Signer, redisClient *redis.Client, checkpointFreq uint, reqCounter *prometheus.CounterVec) CheckpointPublisher { return CheckpointPublisher{ctx: ctx, logClient: logClient, treeID: treeID, hostname: hostname, signer: signer, checkpointFreq: checkpointFreq, redisClient: redisClient, reqCounter: reqCounter} } // StartPublisher creates a long-running task that publishes the latest checkpoint every X minutes // Writing to Redis is best effort. Failure will be detected either through metrics or by witnesses // or Verifiers monitoring for fresh checkpoints. Failure can occur after a lock is obtained but // before publishing the latest checkpoint. If this occurs due to a sporadic failure, this simply // means that a witness will not see a fresh checkpoint for an additional period. func (c *CheckpointPublisher) StartPublisher(ctx context.Context) { tc := trillianclient.NewTrillianClient(context.Background(), c.logClient, c.treeID) sTreeID := strconv.FormatInt(c.treeID, 10) // publish on startup to ensure a checkpoint is available the first time Rekor starts up c.publish(&tc, sTreeID) ticker := time.NewTicker(time.Duration(c.checkpointFreq) * time.Minute) go func() { for { select { case <-ctx.Done(): return case <-ticker.C: c.publish(&tc, sTreeID) } } }() } // publish publishes the latest checkpoint to Redis once func (c *CheckpointPublisher) publish(tc *trillianclient.TrillianClient, sTreeID string) { // get latest checkpoint resp := tc.GetLatest(0) if resp.Status != codes.OK { c.reqCounter.With( map[string]string{ "shard": sTreeID, "code": strconv.Itoa(GetCheckpoint), }).Inc() log.Logger.Errorf("error getting latest checkpoint to publish: %v", resp.Status) return } // unmarshal checkpoint root := &types.LogRootV1{} if err := root.UnmarshalBinary(resp.GetLatestResult.SignedLogRoot.LogRoot); err != nil { c.reqCounter.With( map[string]string{ "shard": sTreeID, "code": strconv.Itoa(UnmarshalCheckpoint), }).Inc() log.Logger.Errorf("error unmarshalling latest checkpoint to publish: %v", err) return } // sign checkpoint with Rekor private key checkpoint, err := util.CreateAndSignCheckpoint(context.Background(), c.hostname, c.treeID, root.TreeSize, root.RootHash, c.signer) if err != nil { c.reqCounter.With( map[string]string{ "shard": sTreeID, "code": strconv.Itoa(SignCheckpoint), }).Inc() log.Logger.Errorf("error signing checkpoint to publish: %v", err) return } // encode checkpoint as hex to write to redis hexCP := hex.EncodeToString(checkpoint) // write checkpoint to Redis if key does not yet exist // this prevents multiple instances of Rekor from writing different checkpoints in the same time window ts := time.Now().Truncate(time.Duration(c.checkpointFreq) * time.Minute).UnixNano() // key is treeID/timestamp, where timestamp is rounded down to the nearest X minutes key := fmt.Sprintf("%d/%d", c.treeID, ts) ctx, cancel := context.WithTimeout(c.ctx, 10*time.Second) defer cancel() // return value ignored, which is whether or not the entry was set // no error is thrown if the key already exists // use smallest value as it's unused value := true successNX, err := c.redisClient.SetNX(ctx, key, value, 0).Result() if err != nil { c.reqCounter.With( map[string]string{ "shard": sTreeID, "code": strconv.Itoa(RedisFailure), }).Inc() log.Logger.Errorf("error with client publishing checkpoint: %v", err) return } // if the key was not set, then the key already exists for this time period if !successNX { return } // successful obtaining of lock for time period c.reqCounter.With( map[string]string{ "shard": sTreeID, "code": strconv.Itoa(SuccessObtainLock), }).Inc() // on successfully obtaining the "lock" for the time window, update latest checkpoint latestKey := fmt.Sprintf("%d/latest", c.treeID) latestCtx, latestCancel := context.WithTimeout(c.ctx, 10*time.Second) defer latestCancel() // return value ignored, which is whether or not the entry was set // no error is thrown if the key already exists if _, err = c.redisClient.Set(latestCtx, latestKey, hexCP, 0).Result(); err != nil { c.reqCounter.With( map[string]string{ "shard": sTreeID, "code": strconv.Itoa(RedisLatestFailure), }).Inc() log.Logger.Errorf("error with client publishing latest checkpoint: %v", err) return } // successful publish c.reqCounter.With( map[string]string{ "shard": sTreeID, "code": strconv.Itoa(Success), }).Inc() } rekor-1.3.5/pkg/witness/publish_checkpoint_test.go000066400000000000000000000276341455727245600224170ustar00rootroot00000000000000// Copyright 2023 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package witness import ( "context" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "errors" "fmt" "testing" "time" "github.com/go-redis/redismock/v9" "github.com/golang/mock/gomock" "github.com/google/trillian" "github.com/google/trillian/types" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/sigstore/rekor/pkg/witness/mockclient" "github.com/sigstore/sigstore/pkg/signature" "go.uber.org/goleak" ) func TestPublishCheckpoint(t *testing.T) { treeID := 1234 hostname := "rekor-test" freq := 1 counter := prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "rekor_checkpoint_publish", Help: "Checkpoint publishing by shard and code", }, []string{"shard", "code"}) priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) signer, _ := signature.LoadSigner(priv, crypto.SHA256) ctrl := gomock.NewController(t) defer ctrl.Finish() root := &types.LogRootV1{TreeSize: 10, RootHash: []byte{1}, TimestampNanos: 123, Revision: 0} mRoot, err := root.MarshalBinary() if err != nil { t.Fatalf("error marshalling log root: %v", err) } mockTrillianLogClient := mockclient.NewMockTrillianLogClient(ctrl) mockTrillianLogClient.EXPECT().GetLatestSignedLogRoot(gomock.Any(), &trillian.GetLatestSignedLogRootRequest{ LogId: int64(treeID), FirstTreeSize: 0, }).Return(&trillian.GetLatestSignedLogRootResponse{SignedLogRoot: &trillian.SignedLogRoot{LogRoot: mRoot}}, nil) redisClient, mock := redismock.NewClientMock() ts := time.Now().Truncate(time.Duration(freq) * time.Minute).UnixNano() mock.Regexp().ExpectSetNX(fmt.Sprintf("%d/%d", treeID, ts), true, 0).SetVal(true) mock.Regexp().ExpectSet(fmt.Sprintf("%d/latest", treeID), "[0-9a-fA-F]+", 0).SetVal("OK") publisher := NewCheckpointPublisher(context.Background(), mockTrillianLogClient, int64(treeID), hostname, signer, redisClient, uint(freq), counter) ctx, cancel := context.WithCancel(context.Background()) publisher.StartPublisher(ctx) defer cancel() // wait for initial publish time.Sleep(1 * time.Second) if err := mock.ExpectationsWereMet(); err != nil { t.Error(err) } if res := testutil.CollectAndCount(counter); res != 2 { t.Fatalf("unexpected number of metrics: %d", res) } if res := testutil.ToFloat64(counter.WithLabelValues(fmt.Sprint(treeID), fmt.Sprint(Success))); res != 1.0 { t.Fatalf("unexpected number of metrics: %2f", res) } if res := testutil.ToFloat64(counter.WithLabelValues(fmt.Sprint(treeID), fmt.Sprint(SuccessObtainLock))); res != 1.0 { t.Fatalf("unexpected number of metrics: %2f", res) } } func TestPublishCheckpointMultiple(t *testing.T) { treeID := 1234 hostname := "rekor-test" freq := 1 counter := prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "rekor_checkpoint_publish", Help: "Checkpoint publishing by shard and code", }, []string{"shard", "code"}) priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) signer, _ := signature.LoadSigner(priv, crypto.SHA256) ctrl := gomock.NewController(t) defer ctrl.Finish() root := &types.LogRootV1{TreeSize: 10, RootHash: []byte{1}, TimestampNanos: 123, Revision: 0} mRoot, err := root.MarshalBinary() if err != nil { t.Fatalf("error marshalling log root: %v", err) } mockTrillianLogClient := mockclient.NewMockTrillianLogClient(ctrl) mockTrillianLogClient.EXPECT().GetLatestSignedLogRoot(gomock.Any(), &trillian.GetLatestSignedLogRootRequest{ LogId: int64(treeID), FirstTreeSize: 0, }).Return(&trillian.GetLatestSignedLogRootResponse{SignedLogRoot: &trillian.SignedLogRoot{LogRoot: mRoot}}, nil).MaxTimes(2) redisClient, mock := redismock.NewClientMock() ts := time.Now().Truncate(time.Duration(freq) * time.Minute).UnixNano() mock.Regexp().ExpectSetNX(fmt.Sprintf("%d/%d", treeID, ts), true, 0).SetVal(true) mock.Regexp().ExpectSet(fmt.Sprintf("%d/latest", treeID), "[0-9a-fA-F]+", 0).SetVal("OK") publisher := NewCheckpointPublisher(context.Background(), mockTrillianLogClient, int64(treeID), hostname, signer, redisClient, uint(freq), counter) ctx, cancel := context.WithCancel(context.Background()) publisher.StartPublisher(ctx) defer cancel() redisClientEx, mockEx := redismock.NewClientMock() mockEx.Regexp().ExpectSetNX(fmt.Sprintf("%d/%d", treeID, ts), true, 0).SetVal(false) publisherEx := NewCheckpointPublisher(context.Background(), mockTrillianLogClient, int64(treeID), hostname, signer, redisClientEx, uint(freq), counter) ctxEx, cancelEx := context.WithCancel(context.Background()) publisherEx.StartPublisher(ctxEx) defer cancelEx() // wait for initial publish time.Sleep(1 * time.Second) if err := mock.ExpectationsWereMet(); err != nil { t.Error(err) } if err := mockEx.ExpectationsWereMet(); err != nil { t.Error(err) } // only publishes once if res := testutil.CollectAndCount(counter); res != 2 { t.Fatalf("unexpected number of metrics: %d", res) } if res := testutil.ToFloat64(counter.WithLabelValues(fmt.Sprint(treeID), fmt.Sprint(Success))); res != 1.0 { t.Fatalf("unexpected number of metrics: %2f", res) } if res := testutil.ToFloat64(counter.WithLabelValues(fmt.Sprint(treeID), fmt.Sprint(SuccessObtainLock))); res != 1.0 { t.Fatalf("unexpected number of metrics: %2f", res) } } func TestPublishCheckpointTrillianError(t *testing.T) { treeID := 1234 hostname := "rekor-test" freq := 1 counter := prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "rekor_checkpoint_publish", Help: "Checkpoint publishing by shard and code", }, []string{"shard", "code"}) priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) signer, _ := signature.LoadSigner(priv, crypto.SHA256) ctrl := gomock.NewController(t) defer ctrl.Finish() // return error mockTrillianLogClient := mockclient.NewMockTrillianLogClient(ctrl) mockTrillianLogClient.EXPECT().GetLatestSignedLogRoot(gomock.Any(), gomock.Any()).Return(nil, errors.New("error: LatestSLR")) redisClient, _ := redismock.NewClientMock() publisher := NewCheckpointPublisher(context.Background(), mockTrillianLogClient, int64(treeID), hostname, signer, redisClient, uint(freq), counter) ctx, cancel := context.WithCancel(context.Background()) publisher.StartPublisher(ctx) defer cancel() // wait for initial publish time.Sleep(1 * time.Second) if res := testutil.CollectAndCount(counter); res != 1 { t.Fatalf("unexpected number of metrics: %d", res) } if res := testutil.ToFloat64(counter.WithLabelValues(fmt.Sprint(treeID), fmt.Sprint(GetCheckpoint))); res != 1.0 { t.Fatalf("unexpected number of metrics: %2f", res) } } func TestPublishCheckpointInvalidTrillianResponse(t *testing.T) { treeID := 1234 hostname := "rekor-test" freq := 1 counter := prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "rekor_checkpoint_publish", Help: "Checkpoint publishing by shard and code", }, []string{"shard", "code"}) priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) signer, _ := signature.LoadSigner(priv, crypto.SHA256) ctrl := gomock.NewController(t) defer ctrl.Finish() // set no log root in response mockTrillianLogClient := mockclient.NewMockTrillianLogClient(ctrl) mockTrillianLogClient.EXPECT().GetLatestSignedLogRoot(gomock.Any(), gomock.Any()). Return(&trillian.GetLatestSignedLogRootResponse{SignedLogRoot: &trillian.SignedLogRoot{LogRoot: []byte{}}}, nil) redisClient, _ := redismock.NewClientMock() publisher := NewCheckpointPublisher(context.Background(), mockTrillianLogClient, int64(treeID), hostname, signer, redisClient, uint(freq), counter) ctx, cancel := context.WithCancel(context.Background()) publisher.StartPublisher(ctx) defer cancel() // wait for initial publish time.Sleep(1 * time.Second) if res := testutil.CollectAndCount(counter); res != 1 { t.Fatalf("unexpected number of metrics: %d", res) } if res := testutil.ToFloat64(counter.WithLabelValues(fmt.Sprint(treeID), fmt.Sprint(UnmarshalCheckpoint))); res != 1.0 { t.Fatalf("unexpected number of metrics: %2f", res) } } func TestPublishCheckpointRedisFailure(t *testing.T) { treeID := 1234 hostname := "rekor-test" freq := 1 counter := prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "rekor_checkpoint_publish", Help: "Checkpoint publishing by shard and code", }, []string{"shard", "code"}) priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) signer, _ := signature.LoadSigner(priv, crypto.SHA256) ctrl := gomock.NewController(t) defer ctrl.Finish() root := &types.LogRootV1{TreeSize: 10, RootHash: []byte{1}, TimestampNanos: 123, Revision: 0} mRoot, err := root.MarshalBinary() if err != nil { t.Fatalf("error marshalling log root: %v", err) } mockTrillianLogClient := mockclient.NewMockTrillianLogClient(ctrl) mockTrillianLogClient.EXPECT().GetLatestSignedLogRoot(gomock.Any(), gomock.Any()). Return(&trillian.GetLatestSignedLogRootResponse{SignedLogRoot: &trillian.SignedLogRoot{LogRoot: mRoot}}, nil) redisClient, mock := redismock.NewClientMock() // error on first redis call mock.Regexp().ExpectSetNX(".+", true, 0).SetErr(errors.New("redis error")) publisher := NewCheckpointPublisher(context.Background(), mockTrillianLogClient, int64(treeID), hostname, signer, redisClient, uint(freq), counter) ctx, cancel := context.WithCancel(context.Background()) publisher.StartPublisher(ctx) defer cancel() // wait for initial publish time.Sleep(1 * time.Second) if res := testutil.CollectAndCount(counter); res != 1 { t.Fatalf("unexpected number of metrics: %d", res) } if res := testutil.ToFloat64(counter.WithLabelValues(fmt.Sprint(treeID), fmt.Sprint(RedisFailure))); res != 1.0 { t.Fatalf("unexpected number of metrics: %2f", res) } } func TestPublishCheckpointRedisLatestFailure(t *testing.T) { treeID := 1234 hostname := "rekor-test" freq := 1 counter := prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "rekor_checkpoint_publish", Help: "Checkpoint publishing by shard and code", }, []string{"shard", "code"}) priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) signer, _ := signature.LoadSigner(priv, crypto.SHA256) ctrl := gomock.NewController(t) defer ctrl.Finish() root := &types.LogRootV1{TreeSize: 10, RootHash: []byte{1}, TimestampNanos: 123, Revision: 0} mRoot, err := root.MarshalBinary() if err != nil { t.Fatalf("error marshalling log root: %v", err) } mockTrillianLogClient := mockclient.NewMockTrillianLogClient(ctrl) mockTrillianLogClient.EXPECT().GetLatestSignedLogRoot(gomock.Any(), gomock.Any()). Return(&trillian.GetLatestSignedLogRootResponse{SignedLogRoot: &trillian.SignedLogRoot{LogRoot: mRoot}}, nil) redisClient, mock := redismock.NewClientMock() mock.Regexp().ExpectSetNX(".+", true, 0).SetVal(true) // error on second redis call mock.Regexp().ExpectSet(".*", "[0-9a-fA-F]+", 0).SetErr(errors.New("error")) publisher := NewCheckpointPublisher(context.Background(), mockTrillianLogClient, int64(treeID), hostname, signer, redisClient, uint(freq), counter) ctx, cancel := context.WithCancel(context.Background()) publisher.StartPublisher(ctx) defer cancel() // wait for initial publish time.Sleep(1 * time.Second) // two metrics, one success for initial redis and one failure for latest if res := testutil.CollectAndCount(counter); res != 2 { t.Fatalf("unexpected number of metrics: %d", res) } if res := testutil.ToFloat64(counter.WithLabelValues(fmt.Sprint(treeID), fmt.Sprint(RedisLatestFailure))); res != 1.0 { t.Fatalf("unexpected number of metrics: %2f", res) } } func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } rekor-1.3.5/rekor-server.yaml000066400000000000000000000012701455727245600161750ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. trillian_log_server: address: "127.0.0.1" port: 8090 rekor_server: address: "127.0.0.1" port: 3000 rekor-1.3.5/release/000077500000000000000000000000001455727245600143035ustar00rootroot00000000000000rekor-1.3.5/release/README.md000066400000000000000000000052061455727245600155650ustar00rootroot00000000000000# Release This directory contain the files and scripts to run a Rekor release. # Cutting a Rekor Release 1. Release notes: Create a PR to update and review release notes in CHANGELOG.md. Check merged pull requests since the last release and make sure enhancements, bug fixes, and authors are reflected in the notes. You can get a list of pull requests since the last release by substituting in the date of the last release and running: ``` git log --pretty="* %s" --after="YYYY-MM-DD" ``` and a list of authors by running: ``` git log --pretty="* %an" --after="YYYY-MM-DD" | sort -u ``` 2. Open a Pull Request to update CHANGELOG.md 3. Tag the repository **WARNING**: Tags should not be updated to a new ref or deleted/recreated after creation. Go provides a [checksum database](https://sum.golang.org/) that persists an immutable mapping between version and ref, and updating the tag will break clients that have already downloaded the release. ```shell $ export RELEASE_TAG= $ git tag -s ${RELEASE_TAG} -m "${RELEASE_TAG}" $ git push upstream ${RELEASE_TAG} ``` Note that `upstream` should be the upstream `sigstore/rekor` repository. You may have to change this if you've configured remotes. 4. Then go to the `Actions` tab and click on the [Cut Release workflow](https://github.com/sigstore/rekor/actions/workflows/cut-release.yml). Note you need to be in [this list](https://github.com/sigstore/sigstore/blob/main/.github/workflows/reusable-release.yml#L45) to trigger this workflow. Click to run a workflow and insert the following parameters ("Cosign" is correct, this refers to the artifact signing key): - `Release tag`: the tag that use pushed for the release - `Key ring for cosign key`: the value is `release-cosign` - `Key name for cosign key`: the value is `cosign` That will trigger a CloudBuild job and will run the release using `goreleaser`, which will publish images to `gcr.io` and `ghcr.io`, and the binaries will be available in the GitHub release. If you have permissions to access the project, you can follow the CloudBuild job in the `projectsigstore`(https://console.cloud.google.com/cloud-build/builds?project=projectsigstore) GCP Project. As the last step of the CloudBuild job, `goreleaser` will create a `draft release` in GitHub. 5. Navigate to the `Draft Release` in the Github repository. Click the `Publish Release` button to make the Release available. You might want/need to add any extra notes/breaking changes notices, upgrade paths. 6. Post on the `#general` and `#rekor` Slack channels. 7. If it's a significant release, send an announcement email to sigstore-dev@googlegroups.com mailing list. rekor-1.3.5/release/cloudbuild.yaml000066400000000000000000000070751455727245600173260ustar00rootroot00000000000000# # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. timeout: 3600s steps: - name: gcr.io/cloud-builders/git dir: "go/src/sigstore" args: - "clone" - "https://github.com/${_TOOL_ORG}/${_TOOL_REPO}" - name: gcr.io/cloud-builders/git entrypoint: "bash" dir: "go/src/sigstore/rekor" args: - '-c' - | git fetch echo "Checking out ${_GIT_TAG}" git checkout ${_GIT_TAG} - name: 'gcr.io/projectsigstore/cosign:v2.2.2-dev@sha256:1a49e2f6cf3580935863d9d8d46066db9aad3dbd673ca24cb83d143221c6e64b' dir: "go/src/sigstore/rekor" env: - TUF_ROOT=/tmp args: - 'verify' - 'ghcr.io/gythialy/golang-cross:v1.21.6-0@sha256:c00bdb060aff03e8042f41ed0c11a0bbbb01e2ea3f65733ce037497fcb83d5d7' - '--certificate-oidc-issuer' - "https://token.actions.githubusercontent.com" - '--certificate-identity' - "https://github.com/gythialy/golang-cross/.github/workflows/release-golang-cross.yml@refs/tags/v1.21.6-0" - name: ghcr.io/gythialy/golang-cross:v1.21.6-0@sha256:c00bdb060aff03e8042f41ed0c11a0bbbb01e2ea3f65733ce037497fcb83d5d7 entrypoint: /bin/sh dir: "go/src/sigstore/rekor" env: - "GOPATH=/workspace/go" - "GOBIN=/workspace/bin" - PROJECT_ID=${PROJECT_ID} - KEY_LOCATION=${_KEY_LOCATION} - KEY_RING=${_KEY_RING} - KEY_NAME=${_KEY_NAME} - KEY_VERSION=${_KEY_VERSION} - GIT_TAG=${_GIT_TAG} - GOOGLE_SERVICE_ACCOUNT_NAME=keyless@${PROJECT_ID}.iam.gserviceaccount.com - COSIGN_YES=true - KO_PREFIX=gcr.io/${PROJECT_ID} secretEnv: - GITHUB_TOKEN args: - '-c' - | gcloud auth configure-docker \ && make release - name: ghcr.io/gythialy/golang-cross:v1.21.6-0@sha256:c00bdb060aff03e8042f41ed0c11a0bbbb01e2ea3f65733ce037497fcb83d5d7 entrypoint: 'bash' dir: "go/src/sigstore/rekor" env: - "GOPATH=/workspace/go" - "GOBIN=/workspace/bin" - PROJECT_ID=${PROJECT_ID} - KEY_LOCATION=${_KEY_LOCATION} - KEY_RING=${_KEY_RING} - KEY_NAME=${_KEY_NAME} - KEY_VERSION=${_KEY_VERSION} - GIT_TAG=${_GIT_TAG} - KO_PREFIX=gcr.io/${PROJECT_ID} - COSIGN_EXPERIMENTAL=true - GOOGLE_SERVICE_ACCOUNT_NAME=keyless@${PROJECT_ID}.iam.gserviceaccount.com - GITHUB_USER=${_GITHUB_USER} - KO_PREFIX=gcr.io/${PROJECT_ID} secretEnv: - GITHUB_TOKEN args: - '-c' - | echo $$GITHUB_TOKEN | docker login ghcr.io -u $$GITHUB_USER --password-stdin \ && make copy-signed-release-to-ghcr || true availableSecrets: secretManager: - versionName: projects/${PROJECT_NUMBER}/secrets/GITHUB_TOKEN/versions/latest env: GITHUB_TOKEN artifacts: objects: location: 'gs://${_STORAGE_LOCATION}/${_GIT_TAG}' paths: - "go/src/sigstore/rekor/dist/rekor*" - "go/src/sigstore/rekor/release/release-cosign.pub" - "go/src/sigstore/rekor/rekor*.yaml" options: machineType: E2_HIGHCPU_8 tags: - rekor-release - ${_GIT_TAG} - ${_TOOL_ORG} - ${_TOOL_REPO} substitutions: _GIT_TAG: 'v0.0.0' _TOOL_ORG: 'honk' _TOOL_REPO: 'honk-repo' _STORAGE_LOCATION: 'honk' _KEY_RING: 'honk-ring' _KEY_NAME: 'honk-crypto' _KEY_VERSION: '1' _KEY_LOCATION: 'global' _GITHUB_USER: 'placeholder' rekor-1.3.5/release/ko-sign-release-images.sh000077500000000000000000000045421455727245600210770ustar00rootroot00000000000000#!/usr/bin/env bash # Copyright 2022 The Sigstore Authors # # Licensed under the Apache License, Version 2.0 (the "License""; # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -o errexit set -o nounset set -o pipefail : "${GIT_HASH:?Environment variable empty or not defined.}" : "${GIT_VERSION:?Environment variable empty or not defined.}" : "${PROJECT_ID:?Environment variable empty or not defined.}" : "${KEY_LOCATION:?Environment variable empty or not defined.}" : "${KEY_RING:?Environment variable empty or not defined.}" : "${KEY_NAME:?Environment variable empty or not defined.}" : "${KEY_VERSION:?Environment variable empty or not defined.}" if [[ ! -f rekorServerImagerefs ]]; then echo "rekorServerImagerefs not found" exit 1 fi if [[ ! -f rekorCliImagerefs ]]; then echo "rekorCliImagerefs not found" exit 1 fi if [[ ! -f bRedisImagerefs ]]; then echo "bRedisImagerefs not found" exit 1 fi echo "Signing images with GCP KMS Key..." cosign sign --yes --key "gcpkms://projects/$PROJECT_ID/locations/$KEY_LOCATION/keyRings/$KEY_RING/cryptoKeys/$KEY_NAME/versions/$KEY_VERSION" -a GIT_HASH="$GIT_HASH" -a GIT_VERSION="$GIT_VERSION" $(cat rekorServerImagerefs) cosign sign --yes --key "gcpkms://projects/$PROJECT_ID/locations/$KEY_LOCATION/keyRings/$KEY_RING/cryptoKeys/$KEY_NAME/versions/$KEY_VERSION" -a GIT_HASH="$GIT_HASH" -a GIT_VERSION="$GIT_VERSION" $(cat rekorCliImagerefs) cosign sign --yes --key "gcpkms://projects/$PROJECT_ID/locations/$KEY_LOCATION/keyRings/$KEY_RING/cryptoKeys/$KEY_NAME/versions/$KEY_VERSION" -a GIT_HASH="$GIT_HASH" -a GIT_VERSION="$GIT_VERSION" $(cat bRedisImagerefs) echo "Signing images with Keyless..." cosign sign --yes -a GIT_HASH="$GIT_HASH" -a GIT_VERSION="$GIT_VERSION" $(cat rekorServerImagerefs) cosign sign --yes -a GIT_HASH="$GIT_HASH" -a GIT_VERSION="$GIT_VERSION" $(cat rekorCliImagerefs) cosign sign --yes -a GIT_HASH="$GIT_HASH" -a GIT_VERSION="$GIT_VERSION" $(cat bRedisImagerefs) rekor-1.3.5/release/release-cosign.pub000066400000000000000000000002621455727245600177130ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhyQCx0E9wQWSFI9ULGwy3BuRklnt IqozONbbdbqz11hlRJy9c7SG+hdcFl9jE9uE/dwtuwU2MqU9T/cN0YkWww== -----END PUBLIC KEY----- rekor-1.3.5/release/release.mk000066400000000000000000000047451455727245600162660ustar00rootroot00000000000000################## # release section ################## # used when releasing together with GCP CloudBuild .PHONY: release release: CLI_LDFLAGS="$(CLI_LDFLAGS)" SERVER_LDFLAGS="$(SERVER_LDFLAGS)" goreleaser release --clean --timeout 120m # used when need to validate the goreleaser .PHONY: snapshot snapshot: CLI_LDFLAGS="$(CLI_LDFLAGS)" SERVER_LDFLAGS="$(SERVER_LDFLAGS)" goreleaser release --skip-sign --skip-publish --snapshot --clean --timeout 120m ########################### # sign section ########################### .PHONY: sign-container-release sign-container-release: ko GIT_HASH=$(GIT_HASH) GIT_VERSION=$(GIT_VERSION) \ ./release/ko-sign-release-images.sh #################### # copy image to GHCR #################### .PHONY: copy-rekor-server-signed-release-to-ghcr copy-rekor-server-signed-release-to-ghcr: cosign copy $(KO_PREFIX)/rekor-server:$(GIT_VERSION) $(GHCR_PREFIX)/rekor-server:$(GIT_VERSION) .PHONY: copy-rekor-cli-signed-release-to-ghcr copy-rekor-cli-signed-release-to-ghcr: cosign copy $(KO_PREFIX)/rekor-cli:$(GIT_VERSION) $(GHCR_PREFIX)/rekor-cli:$(GIT_VERSION) .PHONY: copy-backfill-redis-signed-release-to-ghcr copy-backfill-redis-signed-release-to-ghcr: cosign copy $(KO_PREFIX)/backfill-redis:$(GIT_VERSION) $(GHCR_PREFIX)/backfill-redis:$(GIT_VERSION) .PHONY: copy-signed-release-to-ghcr copy-signed-release-to-ghcr: copy-rekor-server-signed-release-to-ghcr copy-rekor-cli-signed-release-to-ghcr copy-backfill-redis-signed-release-to-ghcr ## -------------------------------------- ## Dist / maybe we can deprecate ## -------------------------------------- .PHONY: dist-cli dist-cli: mkdir -p dist/ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags $(CLI_LDFLAGS) -o dist/rekor-cli-linux-amd64 ./cmd/rekor-cli CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -ldflags $(CLI_LDFLAGS) -o dist/rekor-cli-linux-arm64 ./cmd/rekor-cli CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags $(CLI_LDFLAGS) -o dist/rekor-cli-darwin-amd64 ./cmd/rekor-cli CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -trimpath -ldflags $(CLI_LDFLAGS) -o dist/rekor-cli-darwin-arm64 ./cmd/rekor-cli CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags $(CLI_LDFLAGS) -o dist/rekor-cli-windows-amd64.exe ./cmd/rekor-cli .PHONY: dist-server dist-server: mkdir -p dist/ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags $(SERVER_LDFLAGS) -o dist/rekor-server-linux-amd64 ./cmd/rekor-server .PHONY: dist dist: dist-server dist-cli rekor-1.3.5/scripts/000077500000000000000000000000001455727245600143525ustar00rootroot00000000000000rekor-1.3.5/scripts/createdb.sh000077500000000000000000000017641455727245600164720ustar00rootroot00000000000000#!/bin/bash # # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. DB="test" USER="test" PASS="zaphod" ROOTPASS="" echo -e "Creating $DB database and $USER user account" mysql </dev/null 2>&1; then docker_compose="docker-compose -f docker-compose.yml -f docker-compose.test.yml" fi rm -f /tmp/rekor-*.cov echo "installing gocovmerge" make gocovmerge echo "building test-only containers" docker build -t gcp-pubsub-emulator -f Dockerfile.pubsub-emulator . echo "starting services" ${docker_compose} up -d --build echo "building CLI and server" go test -c ./cmd/rekor-cli -o rekor-cli -cover -covermode=count -coverpkg=./... go test -c ./cmd/rekor-server -o rekor-server -covermode=count -coverpkg=./... count=0 echo "waiting up to 2 min for system to start" until [ $(${docker_compose} ps | \ grep -E "(rekor[-_]mysql|rekor[-_]redis|rekor[-_]rekor-server|rekor[-_]gcp-pubsub-emulator)" | \ grep -c "(healthy)" ) == 4 ]; do if [ $count -eq 24 ]; then echo "! timeout reached" exit 1 else echo -n "." sleep 5 let 'count+=1' fi done echo echo "running tests" REKORTMPDIR="$(mktemp -d -t rekor_test.XXXXXX)" touch $REKORTMPDIR.rekor.yaml trap "rm -rf $REKORTMPDIR" EXIT if ! REKORTMPDIR=$REKORTMPDIR go test -tags=e2e ./tests/ -run TestIssue1308; then ${docker_compose} logs --no-color > /tmp/docker-compose.log exit 1 fi if ! REKORTMPDIR=$REKORTMPDIR PUBSUB_EMULATOR_HOST=localhost:8085 go test -tags=e2e ./tests/; then ${docker_compose} logs --no-color > /tmp/docker-compose.log exit 1 fi if ${docker_compose} logs --no-color | grep -q "panic: runtime error:" ; then # if we're here, we found a panic echo "Failing due to panics detected in logs" ${docker_compose} logs --no-color > /tmp/docker-compose.log exit 1 fi echo "generating code coverage" ${docker_compose} restart rekor-server if ! docker cp $(docker ps -aqf "name=rekor_rekor-server" -f "name=rekor-rekor-server"):/go/rekor-server.cov /tmp/rekor-server.cov ; then # failed to copy code coverage report from server echo "Failed to retrieve server code coverage report" ${docker_compose} logs --no-color > /tmp/docker-compose.log exit 1 fi # merging coverage reports and filtering out /pkg/generated from final report hack/tools/bin/gocovmerge /tmp/rekor-*.cov | grep -v "/pkg/generated/" > /tmp/rekor-merged.cov echo "code coverage $(go tool cover -func=/tmp/rekor-merged.cov | grep -E '^total\:' | sed -E 's/\s+/ /g')" rekor-1.3.5/tests/e2e_test.go000066400000000000000000001074021455727245600160720ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e package e2e import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "os" "path/filepath" "strconv" "strings" "testing" "time" "golang.org/x/sync/errgroup" "cloud.google.com/go/pubsub" "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/sigstore/rekor/pkg/client" generatedClient "github.com/sigstore/rekor/pkg/generated/client" "github.com/sigstore/rekor/pkg/generated/client/entries" "github.com/sigstore/rekor/pkg/generated/client/pubkey" "github.com/sigstore/rekor/pkg/generated/models" sigx509 "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/sharding" "github.com/sigstore/rekor/pkg/signer" _ "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1" rekord "github.com/sigstore/rekor/pkg/types/rekord/v0.0.1" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore/pkg/signature/options" ) func getUUIDFromUploadOutput(t *testing.T, out string) string { t.Helper() // Output looks like "Artifact timestamped at ...\m Wrote response \n Created entry at index X, available at $URL/UUID", so grab the UUID: urlTokens := strings.Split(strings.TrimSpace(out), " ") url := urlTokens[len(urlTokens)-1] splitUrl := strings.Split(url, "/") return splitUrl[len(splitUrl)-1] } func getLogIndexFromUploadOutput(t *testing.T, out string) int { t.Helper() t.Log(out) // Output looks like "Created entry at index X, available at $URL/UUID", so grab the index X: split := strings.Split(strings.TrimSpace(out), ",") ss := strings.Split(split[0], " ") i, err := strconv.Atoi(ss[len(ss)-1]) if err != nil { t.Fatal(err) } return i } func TestEnvVariableValidation(t *testing.T) { os.Setenv("REKOR_FORMAT", "bogus") defer os.Unsetenv("REKOR_FORMAT") runCliErr(t, "loginfo") } func TestDuplicates(t *testing.T) { artifactPath := filepath.Join(t.TempDir(), "artifact") sigPath := filepath.Join(t.TempDir(), "signature.asc") createdPGPSignedArtifact(t, artifactPath, sigPath) // Write the public key to a file pubPath := filepath.Join(t.TempDir(), "pubKey.asc") if err := ioutil.WriteFile(pubPath, []byte(publicKey), 0644); err != nil { t.Fatal(err) } // Now upload to rekor! out := runCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) outputContains(t, out, "Created entry at") // Now upload the same one again, we should get a dupe entry. out = runCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) outputContains(t, out, "Entry already exists") // Now do a new one, we should get a new entry createdPGPSignedArtifact(t, artifactPath, sigPath) out = runCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) outputContains(t, out, "Created entry at") } type getOut struct { Attestation string AttestationType string Body interface{} LogIndex int IntegratedTime int64 } func TestGetCLI(t *testing.T) { // Create something and add it to the log artifactPath := filepath.Join(t.TempDir(), "artifact") sigPath := filepath.Join(t.TempDir(), "signature.asc") createdPGPSignedArtifact(t, artifactPath, sigPath) // Write the public key to a file pubPath := filepath.Join(t.TempDir(), "pubKey.asc") if err := ioutil.WriteFile(pubPath, []byte(publicKey), 0644); err != nil { t.Fatal(err) } out := runCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) outputContains(t, out, "Created entry at") uuid, err := sharding.GetUUIDFromIDString(getUUIDFromUploadOutput(t, out)) if err != nil { t.Error(err) } // since we at least have 1 valid entry, check the log at index 0 runCli(t, "get", "--log-index", "0") out = runCli(t, "get", "--format=json", "--uuid", uuid) // The output here should be in JSON with this structure: g := getOut{} if err := json.Unmarshal([]byte(out), &g); err != nil { t.Error(err) } if g.IntegratedTime == 0 { t.Errorf("Expected IntegratedTime to be set. Got %s", out) } // Get it with the logindex as well runCli(t, "get", "--format=json", "--log-index", strconv.Itoa(g.LogIndex)) // check index via the file and public key to ensure that the index has updated correctly out = runCli(t, "search", "--artifact", artifactPath) outputContains(t, out, uuid) out = runCli(t, "search", "--public-key", pubPath) outputContains(t, out, uuid) artifactBytes, err := ioutil.ReadFile(artifactPath) if err != nil { t.Error(err) } sha := sha256.Sum256(artifactBytes) out = runCli(t, "search", "--sha", fmt.Sprintf("sha256:%s", hex.EncodeToString(sha[:]))) outputContains(t, out, uuid) // Exercise GET with the new EntryID (TreeID + UUID) tid := getTreeID(t) entryID, err := sharding.CreateEntryIDFromParts(fmt.Sprintf("%x", tid), uuid) if err != nil { t.Error(err) } runCli(t, "get", "--format=json", "--uuid", entryID.ReturnEntryIDString()) } func publicKeyFromRekorClient(ctx context.Context, c *generatedClient.Rekor) (*ecdsa.PublicKey, error) { resp, err := c.Pubkey.GetPublicKey(&pubkey.GetPublicKeyParams{Context: ctx}) if err != nil { return nil, err } // marshal the pubkey pubKey, err := cryptoutils.UnmarshalPEMToPublicKey([]byte(resp.GetPayload())) if err != nil { return nil, err } ed, ok := pubKey.(*ecdsa.PublicKey) if !ok { return nil, errors.New("public key retrieved from Rekor is not an ECDSA key") } return ed, nil } func TestSignedEntryTimestamp(t *testing.T) { // Create a random payload and sign it ctx := context.Background() payload := []byte("payload") s, err := signer.NewMemory() if err != nil { t.Fatal(err) } sig, err := s.SignMessage(bytes.NewReader(payload), options.WithContext(ctx)) if err != nil { t.Fatal(err) } pubkey, err := s.PublicKey(options.WithContext(ctx)) if err != nil { t.Fatal(err) } pemBytes, err := cryptoutils.MarshalPublicKeyToPEM(pubkey) if err != nil { t.Fatal(err) } // submit our newly signed payload to rekor rekorClient, err := client.GetRekorClient(rekorServer()) if err != nil { t.Fatal(err) } re := rekord.V001Entry{ RekordObj: models.RekordV001Schema{ Data: &models.RekordV001SchemaData{ Content: strfmt.Base64(payload), }, Signature: &models.RekordV001SchemaSignature{ Content: (*strfmt.Base64)(&sig), Format: swag.String(models.RekordV001SchemaSignatureFormatX509), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: (*strfmt.Base64)(&pemBytes), }, }, }, } returnVal := models.Rekord{ APIVersion: swag.String(re.APIVersion()), Spec: re.RekordObj, } params := entries.NewCreateLogEntryParams() params.SetProposedEntry(&returnVal) resp, err := rekorClient.Entries.CreateLogEntry(params) if err != nil { t.Fatal(err) } logEntry := extractLogEntry(t, resp.GetPayload()) // verify the signature against the log entry (without the signature) timestampSig := logEntry.Verification.SignedEntryTimestamp logEntry.Verification = nil payload, err = logEntry.MarshalBinary() if err != nil { t.Fatal(err) } canonicalized, err := jsoncanonicalizer.Transform(payload) if err != nil { t.Fatal(err) } // get rekor's public key rekorPubKey, err := publicKeyFromRekorClient(ctx, rekorClient) if err != nil { t.Fatal(err) } verifier, err := signature.LoadVerifier(rekorPubKey, crypto.SHA256) if err != nil { t.Fatal(err) } if err := verifier.VerifySignature(bytes.NewReader(timestampSig), bytes.NewReader(canonicalized), options.WithContext(ctx)); err != nil { t.Fatal("unable to verify") } } func TestEntryUpload(t *testing.T) { artifactPath := filepath.Join(t.TempDir(), "artifact") sigPath := filepath.Join(t.TempDir(), "signature.asc") // Create the entry file createdPGPSignedArtifact(t, artifactPath, sigPath) payload, err := ioutil.ReadFile(artifactPath) if err != nil { t.Fatal(err) } sig, err := ioutil.ReadFile(sigPath) if err != nil { t.Fatal(err) } entryPath := filepath.Join(t.TempDir(), "entry.json") pubKeyBytes := []byte(publicKey) re := rekord.V001Entry{ RekordObj: models.RekordV001Schema{ Data: &models.RekordV001SchemaData{ Content: strfmt.Base64(payload), }, Signature: &models.RekordV001SchemaSignature{ Content: (*strfmt.Base64)(&sig), Format: swag.String(models.RekordV001SchemaSignatureFormatPgp), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: (*strfmt.Base64)(&pubKeyBytes), }, }, }, } returnVal := models.Rekord{ APIVersion: swag.String(re.APIVersion()), Spec: re.RekordObj, } entryBytes, err := json.Marshal(returnVal) if err != nil { t.Fatal(err) } if err := ioutil.WriteFile(entryPath, entryBytes, 0644); err != nil { t.Fatal(err) } // Start pubsub client to capture notifications. Values match those in // docker-compose.test.yml. ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() psc, err := pubsub.NewClient(ctx, "test-project") if err != nil { t.Fatalf("Create pubsub client: %v", err) } topic, err := psc.CreateTopic(ctx, "new-entry") if err != nil { // Assume error is AlreadyExists if one occurrs unless it is context timeout. // If the error was not AlreadyExists, it will be caught in later error // checks in this test. if errors.Is(err, os.ErrDeadlineExceeded) { t.Fatalf("Create pubsub topic: %v", err) } topic = psc.Topic("new-entry") } filters := []string{ `attributes:rekor_entry_kind`, // Ignore any messages that do not have this attribute `attributes.rekor_signing_subjects = "test@rekor.dev"`, // This is the email in the hard-coded PGP test key `attributes.datacontenttype = "application/json"`, // Only fetch the JSON formatted events } cfg := pubsub.SubscriptionConfig{ Topic: topic, Filter: strings.Join(filters, " AND "), } sub, err := psc.CreateSubscription(ctx, "new-entry-sub", cfg) if err != nil { if errors.Is(err, os.ErrDeadlineExceeded) { t.Fatalf("Create pubsub subscription: %v", err) } sub = psc.Subscription("new-entry-sub") } ch := make(chan []byte, 1) go func() { if err := sub.Receive(ctx, func(_ context.Context, m *pubsub.Message) { ch <- m.Data }); err != nil { t.Errorf("Receive pubusub msg: %v", err) } }() // Now upload to rekor! out := runCli(t, "upload", "--entry", entryPath) outputContains(t, out, "Created entry at") // Await pubsub select { case msg := <-ch: t.Logf("Got pubsub message!\n%s", string(msg)) case <-ctx.Done(): t.Errorf("Did not receive pubsub message: %v", ctx.Err()) } } // Regression test for https://github.com/sigstore/rekor/pull/956 // Requesting an inclusion proof concurrently with an entry write triggers // a race where the inclusion proof returned does not verify because the // tree head changes. func TestInclusionProofRace(t *testing.T) { // Create a random artifact and sign it. artifactPath := filepath.Join(t.TempDir(), "artifact") sigPath := filepath.Join(t.TempDir(), "signature.asc") sigx509.CreatedX509SignedArtifact(t, artifactPath, sigPath) dataBytes, _ := ioutil.ReadFile(artifactPath) h := sha256.Sum256(dataBytes) dataSHA := hex.EncodeToString(h[:]) // Write the public key to a file pubPath := filepath.Join(t.TempDir(), "pubKey.asc") if err := ioutil.WriteFile(pubPath, []byte(sigx509.RSACert), 0644); err != nil { t.Fatal(err) } // Upload an entry runCli(t, "upload", "--type=hashedrekord", "--pki-format=x509", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath) // Constantly uploads new signatures on an entry. uploadRoutine := func(pubPath string) error { // Create a random artifact and sign it. artifactPath := filepath.Join(t.TempDir(), "artifact") sigPath := filepath.Join(t.TempDir(), "signature.asc") sigx509.CreatedX509SignedArtifact(t, artifactPath, sigPath) dataBytes, _ := ioutil.ReadFile(artifactPath) h := sha256.Sum256(dataBytes) dataSHA := hex.EncodeToString(h[:]) // Upload an entry out := runCli(t, "upload", "--type=hashedrekord", "--pki-format=x509", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath) outputContains(t, out, "Created entry at") return nil } // Attempts to verify the original entry. verifyRoutine := func(dataSHA, sigPath, pubPath string) error { out := runCli(t, "verify", "--type=hashedrekord", "--pki-format=x509", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath) if strings.Contains(out, "calculated root") || strings.Contains(out, "wrong") { return fmt.Errorf(out) } return nil } var g errgroup.Group for i := 0; i < 50; i++ { g.Go(func() error { return uploadRoutine(pubPath) }) g.Go(func() error { return verifyRoutine(dataSHA, sigPath, pubPath) }) } if err := g.Wait(); err != nil { t.Fatal(err) } } // TestIssue1308 should be run once before any other tests (against an empty log) func TestIssue1308(t *testing.T) { // we run this to validate issue 1308 which needs to be tested against an empty log if getTotalTreeSize(t) == 0 { TestSearchQueryNonExistentEntry(t) } else { t.Skip("skipping because log is not empty") } } func TestSearchQueryNonExistentEntry(t *testing.T) { // Nonexistent but well-formed entry results in 200 with empty array as body wd, err := os.Getwd() if err != nil { t.Fatal(err) } b, err := ioutil.ReadFile(filepath.Join(wd, "canonical_rekor.json")) if err != nil { t.Fatal(err) } body := fmt.Sprintf("{\"entries\":[%s]}", b) resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewBuffer([]byte(body))) if err != nil { t.Fatal(err) } c, _ := ioutil.ReadAll(resp.Body) if resp.StatusCode != 200 { t.Fatalf("expected status 200, got %d instead: %v", resp.StatusCode, string(c)) } if strings.TrimSpace(string(c)) != "[]" { t.Fatalf("expected empty JSON array as response, got %s instead", string(c)) } } func getTreeID(t *testing.T) int64 { out := runCli(t, "loginfo") tidStr := strings.TrimSpace(strings.Split(out, "TreeID: ")[1]) tid, err := strconv.ParseInt(tidStr, 10, 64) if err != nil { t.Errorf(err.Error()) } t.Log("Tree ID:", tid) return tid } func getTotalTreeSize(t *testing.T) int64 { out := runCli(t, "loginfo") sizeStr := strings.Fields(strings.Split(out, "Total Tree Size: ")[1])[0] size, err := strconv.ParseInt(sizeStr, 10, 64) if err != nil { t.Errorf(err.Error()) } t.Log("Total Tree Size:", size) return size } // This test confirms that we validate tree ID when using the /api/v1/log/entries/retrieve endpoint // https://github.com/sigstore/rekor/issues/1014 func TestSearchValidateTreeID(t *testing.T) { // Create something and add it to the log artifactPath := filepath.Join(t.TempDir(), "artifact") sigPath := filepath.Join(t.TempDir(), "signature.asc") createdPGPSignedArtifact(t, artifactPath, sigPath) // Write the public key to a file pubPath := filepath.Join(t.TempDir(), "pubKey.asc") if err := ioutil.WriteFile(pubPath, []byte(publicKey), 0644); err != nil { t.Fatal(err) } out := runCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) outputContains(t, out, "Created entry at") uuid, err := sharding.GetUUIDFromIDString(getUUIDFromUploadOutput(t, out)) if err != nil { t.Error(err) } // Make sure we can get by Entry ID tid := getTreeID(t) entryID, err := sharding.CreateEntryIDFromParts(fmt.Sprintf("%x", tid), uuid) if err != nil { t.Fatal(err) } body := "{\"entryUUIDs\":[\"%s\"]}" resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewBuffer([]byte(fmt.Sprintf(body, entryID.ReturnEntryIDString())))) if err != nil { t.Fatal(err) } if resp.StatusCode != 200 { t.Fatalf("expected 200 status code but got %d", resp.StatusCode) } // Make sure we fail with a random tree ID fakeTID := tid + 1 entryID, err = sharding.CreateEntryIDFromParts(fmt.Sprintf("%x", fakeTID), uuid) if err != nil { t.Fatal(err) } resp, err = http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", bytes.NewBuffer([]byte(fmt.Sprintf(body, entryID.ReturnEntryIDString())))) if err != nil { t.Fatal(err) } // Not Found because currently we don't detect that an unused random tree ID is invalid. c, _ := ioutil.ReadAll(resp.Body) if resp.StatusCode != 200 { t.Fatalf("expected status 200, got %d instead", resp.StatusCode) } if strings.TrimSpace(string(c)) != "[]" { t.Fatalf("expected empty JSON array as response, got %s instead", string(c)) } } // TestSearchLogQuerySingleShard provides coverage testing on the searchLogQuery endpoint within a single shard func TestSearchLogQuerySingleShard(t *testing.T) { // Write the shared public key to a file pubPath := filepath.Join(t.TempDir(), "logQuery_pubKey.asc") pubKeyBytes := []byte(publicKey) if err := ioutil.WriteFile(pubPath, pubKeyBytes, 0644); err != nil { t.Fatal(err) } // Create two valid log entries to use for the test cases firstArtifactPath := filepath.Join(t.TempDir(), "artifact1") firstSigPath := filepath.Join(t.TempDir(), "signature1.asc") createdPGPSignedArtifact(t, firstArtifactPath, firstSigPath) firstArtifactBytes, _ := ioutil.ReadFile(firstArtifactPath) firstSigBytes, _ := ioutil.ReadFile(firstSigPath) firstRekord := rekord.V001Entry{ RekordObj: models.RekordV001Schema{ Data: &models.RekordV001SchemaData{ Content: strfmt.Base64(firstArtifactBytes), }, Signature: &models.RekordV001SchemaSignature{ Content: (*strfmt.Base64)(&firstSigBytes), Format: swag.String(models.RekordV001SchemaSignatureFormatPgp), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: (*strfmt.Base64)(&pubKeyBytes), }, }, }, } firstEntry := &models.Rekord{ APIVersion: swag.String(firstRekord.APIVersion()), Spec: firstRekord.RekordObj, } secondArtifactPath := filepath.Join(t.TempDir(), "artifact2") secondSigPath := filepath.Join(t.TempDir(), "signature2.asc") createdPGPSignedArtifact(t, secondArtifactPath, secondSigPath) secondArtifactBytes, _ := ioutil.ReadFile(secondArtifactPath) secondSigBytes, _ := ioutil.ReadFile(secondSigPath) secondRekord := rekord.V001Entry{ RekordObj: models.RekordV001Schema{ Data: &models.RekordV001SchemaData{ Content: strfmt.Base64(secondArtifactBytes), }, Signature: &models.RekordV001SchemaSignature{ Content: (*strfmt.Base64)(&secondSigBytes), Format: swag.String(models.RekordV001SchemaSignatureFormatPgp), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: (*strfmt.Base64)(&pubKeyBytes), }, }, }, } secondEntry := &models.Rekord{ APIVersion: swag.String(secondRekord.APIVersion()), Spec: secondRekord.RekordObj, } // Now upload them to rekor! firstOut := runCli(t, "upload", "--artifact", firstArtifactPath, "--signature", firstSigPath, "--public-key", pubPath) secondOut := runCli(t, "upload", "--artifact", secondArtifactPath, "--signature", secondSigPath, "--public-key", pubPath) firstEntryID := getUUIDFromUploadOutput(t, firstOut) firstUUID, _ := sharding.GetUUIDFromIDString(firstEntryID) firstIndex := int64(getLogIndexFromUploadOutput(t, firstOut)) secondEntryID := getUUIDFromUploadOutput(t, secondOut) secondUUID, _ := sharding.GetUUIDFromIDString(secondEntryID) secondIndex := int64(getLogIndexFromUploadOutput(t, secondOut)) // this is invalid because treeID is > int64 invalidEntryID := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeeefff" invalidIndex := int64(-1) invalidEntry := &models.Rekord{ APIVersion: swag.String(secondRekord.APIVersion()), } nonexistentArtifactPath := filepath.Join(t.TempDir(), "artifact3") nonexistentSigPath := filepath.Join(t.TempDir(), "signature3.asc") createdPGPSignedArtifact(t, nonexistentArtifactPath, nonexistentSigPath) nonexistentArtifactBytes, _ := ioutil.ReadFile(nonexistentArtifactPath) nonexistentSigBytes, _ := ioutil.ReadFile(nonexistentSigPath) nonexistentEntryID := "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeeefff" nonexistentUUID := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeeefff" nonexistentIndex := int64(999999999) // assuming we don't put that many entries in the log nonexistentRekord := rekord.V001Entry{ RekordObj: models.RekordV001Schema{ Data: &models.RekordV001SchemaData{ Content: strfmt.Base64(nonexistentArtifactBytes), }, Signature: &models.RekordV001SchemaSignature{ Content: (*strfmt.Base64)(&nonexistentSigBytes), Format: swag.String(models.RekordV001SchemaSignatureFormatPgp), PublicKey: &models.RekordV001SchemaSignaturePublicKey{ Content: (*strfmt.Base64)(&pubKeyBytes), }, }, }, } nonexistentEntry := &models.Rekord{ APIVersion: swag.String("0.0.1"), Spec: nonexistentRekord.RekordObj, } type testCase struct { name string expectSuccess bool expectedErrorResponseCode int64 expectedEntryIDs []string entryUUIDs []string logIndexes []*int64 entries []models.ProposedEntry } testCases := []testCase{ { name: "empty entryUUIDs", expectSuccess: true, expectedEntryIDs: []string{}, entryUUIDs: []string{}, }, { name: "first in log (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID}, entryUUIDs: []string{firstEntryID}, }, { name: "first in log (using UUID in entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID}, entryUUIDs: []string{firstUUID}, }, { name: "second in log (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{secondEntryID}, entryUUIDs: []string{secondEntryID}, }, { name: "invalid entryID (using entryUUIDs)", expectSuccess: false, expectedErrorResponseCode: http.StatusBadRequest, entryUUIDs: []string{invalidEntryID}, }, { name: "valid entryID not in log (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{}, entryUUIDs: []string{nonexistentEntryID}, }, { name: "valid UUID not in log (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{}, entryUUIDs: []string{nonexistentUUID}, }, { name: "both valid entries in log (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entryUUIDs: []string{firstEntryID, secondEntryID}, }, { name: "both valid entries in log (one with UUID, other with entryID) (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entryUUIDs: []string{firstEntryID, secondUUID}, }, { name: "one valid entry in log, one malformed (using entryUUIDs)", expectSuccess: false, expectedErrorResponseCode: http.StatusBadRequest, entryUUIDs: []string{firstEntryID, invalidEntryID}, }, { name: "one existing, one valid entryID but not in log (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID}, entryUUIDs: []string{firstEntryID, nonexistentEntryID}, }, { name: "two existing, one valid entryID but not in log (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entryUUIDs: []string{firstEntryID, secondEntryID, nonexistentEntryID}, }, { name: "two existing, one valid entryID but not in log (different ordering 1) (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entryUUIDs: []string{firstEntryID, nonexistentEntryID, secondEntryID}, }, { name: "two existing, one valid entryID but not in log (different ordering 2) (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entryUUIDs: []string{nonexistentEntryID, firstEntryID, secondEntryID}, }, { name: "two existing, one valid entryID but not in log (different ordering 3) (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entryUUIDs: []string{nonexistentUUID, firstEntryID, secondEntryID}, }, { name: "two existing, one valid entryID but not in log (different ordering 4) (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entryUUIDs: []string{nonexistentEntryID, firstUUID, secondEntryID}, }, { name: "two existing, one valid entryID but not in log (different ordering 5) (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entryUUIDs: []string{nonexistentEntryID, firstEntryID, secondUUID}, }, { name: "two existing, one valid entryID but not in log (different ordering 6) (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entryUUIDs: []string{nonexistentUUID, firstEntryID, secondUUID}, }, { name: "two existing, one valid entryID but not in log (different ordering 7) (using entryUUIDs)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entryUUIDs: []string{nonexistentEntryID, firstUUID, secondUUID}, }, { name: "request more than 10 entries (using entryUUIDs)", expectSuccess: false, expectedErrorResponseCode: http.StatusUnprocessableEntity, entryUUIDs: []string{firstEntryID, firstEntryID, firstEntryID, firstEntryID, firstEntryID, firstEntryID, firstEntryID, firstEntryID, firstEntryID, firstEntryID, firstEntryID}, }, { name: "empty logIndexes", expectSuccess: true, expectedEntryIDs: []string{}, logIndexes: []*int64{}, }, { name: "first in log (using logIndexes)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID}, logIndexes: []*int64{&firstIndex}, }, { name: "second in log (using logIndexes)", expectSuccess: true, expectedEntryIDs: []string{secondEntryID}, logIndexes: []*int64{&secondIndex}, }, { name: "invalid logIndex (using logIndexes)", expectSuccess: false, expectedErrorResponseCode: http.StatusUnprocessableEntity, logIndexes: []*int64{&invalidIndex}, }, { name: "valid index not in log (using logIndexes)", expectSuccess: true, expectedEntryIDs: []string{}, logIndexes: []*int64{&nonexistentIndex}, }, { name: "both valid entries in log (using logIndexes)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, logIndexes: []*int64{&firstIndex, &secondIndex}, }, { name: "one valid entry in log, one malformed (using logIndexes)", expectSuccess: false, expectedErrorResponseCode: http.StatusUnprocessableEntity, logIndexes: []*int64{&firstIndex, &invalidIndex}, }, { name: "one existing, one valid Index but not in log (using logIndexes)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID}, logIndexes: []*int64{&firstIndex, &nonexistentIndex}, }, { name: "two existing, one valid Index but not in log (using logIndexes)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, logIndexes: []*int64{&firstIndex, &secondIndex, &nonexistentIndex}, }, { name: "two existing, one valid Index but not in log (different ordering 1) (using logIndexes)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, logIndexes: []*int64{&firstIndex, &nonexistentIndex, &secondIndex}, }, { name: "two existing, one valid Index but not in log (different ordering 2) (using logIndexes)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, logIndexes: []*int64{&nonexistentIndex, &firstIndex, &secondIndex}, }, { name: "request more than 10 entries (using logIndexes)", expectSuccess: false, expectedErrorResponseCode: http.StatusUnprocessableEntity, logIndexes: []*int64{&firstIndex, &firstIndex, &firstIndex, &firstIndex, &firstIndex, &firstIndex, &firstIndex, &firstIndex, &firstIndex, &firstIndex, &firstIndex}, }, { name: "empty entries", expectSuccess: true, expectedEntryIDs: []string{}, entries: []models.ProposedEntry{}, }, { name: "first in log (using entries)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID}, entries: []models.ProposedEntry{firstEntry}, }, { name: "second in log (using entries)", expectSuccess: true, expectedEntryIDs: []string{secondEntryID}, entries: []models.ProposedEntry{secondEntry}, }, { name: "invalid entry (using entries)", expectSuccess: false, expectedErrorResponseCode: http.StatusUnprocessableEntity, entries: []models.ProposedEntry{invalidEntry}, }, { name: "valid entry not in log (using entries)", expectSuccess: true, expectedEntryIDs: []string{}, entries: []models.ProposedEntry{nonexistentEntry}, }, { name: "both valid entries in log (using entries)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entries: []models.ProposedEntry{firstEntry, secondEntry}, }, { name: "one valid entry in log, one malformed (using entries)", expectSuccess: false, expectedErrorResponseCode: http.StatusUnprocessableEntity, entries: []models.ProposedEntry{firstEntry, invalidEntry}, }, { name: "one existing, one valid Index but not in log (using entries)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID}, entries: []models.ProposedEntry{firstEntry, nonexistentEntry}, }, { name: "two existing, one valid Index but not in log (using entries)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entries: []models.ProposedEntry{firstEntry, secondEntry, nonexistentEntry}, }, { name: "two existing, one valid Index but not in log (different ordering 1) (using entries)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entries: []models.ProposedEntry{firstEntry, nonexistentEntry, secondEntry}, }, { name: "two existing, one valid Index but not in log (different ordering 2) (using entries)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID}, entries: []models.ProposedEntry{nonexistentEntry, firstEntry, secondEntry}, }, { name: "request more than 10 entries (using entries)", expectSuccess: false, expectedErrorResponseCode: http.StatusUnprocessableEntity, entries: []models.ProposedEntry{firstEntry, firstEntry, firstEntry, firstEntry, firstEntry, firstEntry, firstEntry, firstEntry, firstEntry, firstEntry, firstEntry}, }, { name: "request more than 10 entries (using mixture)", expectSuccess: false, expectedErrorResponseCode: http.StatusUnprocessableEntity, entryUUIDs: []string{firstEntryID, firstEntryID, firstEntryID, firstEntryID}, logIndexes: []*int64{&firstIndex, &firstIndex, &firstIndex}, entries: []models.ProposedEntry{firstEntry, firstEntry, firstEntry, firstEntry}, }, { name: "request valid and invalid (using mixture)", expectSuccess: false, expectedErrorResponseCode: http.StatusUnprocessableEntity, entryUUIDs: []string{firstEntryID, firstEntryID, firstEntryID, firstEntryID}, logIndexes: []*int64{&invalidIndex, &invalidIndex, &invalidIndex}, entries: []models.ProposedEntry{firstEntry, firstEntry, firstEntry}, }, { name: "request valid and nonexistent (using mixture)", expectSuccess: true, expectedEntryIDs: []string{firstEntryID, secondEntryID, firstEntryID, secondEntryID, firstEntryID, secondEntryID}, entryUUIDs: []string{firstEntryID, secondEntryID, nonexistentEntryID}, logIndexes: []*int64{&firstIndex, &secondIndex, &nonexistentIndex}, entries: []models.ProposedEntry{firstEntry, secondEntry, nonexistentEntry}, }, } for _, test := range testCases { rekorClient, err := client.GetRekorClient(rekorServer(), client.WithRetryCount(0)) if err != nil { t.Fatal(err) } t.Run(test.name, func(t *testing.T) { params := entries.NewSearchLogQueryParams() entry := &models.SearchLogQuery{} if len(test.entryUUIDs) > 0 { t.Log("trying with entryUUIDs: ", test.entryUUIDs) entry.EntryUUIDs = test.entryUUIDs } if len(test.logIndexes) > 0 { entry.LogIndexes = test.logIndexes } if len(test.entries) > 0 { entry.SetEntries(test.entries) } params.SetEntry(entry) resp, err := rekorClient.Entries.SearchLogQuery(params) if err != nil { if !test.expectSuccess { if _, ok := err.(*entries.SearchLogQueryBadRequest); ok { if test.expectedErrorResponseCode != http.StatusBadRequest { t.Fatalf("unexpected error code received: expected %d, got %d: %v", test.expectedErrorResponseCode, http.StatusBadRequest, err) } } else if _, ok := err.(*entries.SearchLogQueryUnprocessableEntity); ok { if test.expectedErrorResponseCode != http.StatusUnprocessableEntity { t.Fatalf("unexpected error code received: expected %d, got %d: %v", test.expectedErrorResponseCode, http.StatusUnprocessableEntity, err) } } else if e, ok := err.(*entries.SearchLogQueryDefault); ok { t.Fatalf("unexpected error: %v", e) } } else { t.Fatalf("unexpected error: %v", err) } } else { if len(resp.Payload) != len(test.expectedEntryIDs) { t.Fatalf("unexpected number of responses received: expected %d, got %d", len(test.expectedEntryIDs), len(resp.Payload)) } // walk responses, build up list of returned entry IDs returnedEntryIDs := []string{} for _, entry := range resp.Payload { // do this for dynamic keyed entries for entryID := range entry { t.Log("received entry: ", entryID) returnedEntryIDs = append(returnedEntryIDs, entryID) } } // we have the expected number of responses, let's ensure they're the ones we expected if out := cmp.Diff(returnedEntryIDs, test.expectedEntryIDs, cmpopts.SortSlices(func(a, b string) bool { return a < b })); out != "" { t.Fatalf("unexpected responses: %v", out) } } }) } } rekor-1.3.5/tests/fuzz-testdata/000077500000000000000000000000001455727245600166325ustar00rootroot00000000000000rekor-1.3.5/tests/fuzz-testdata/FuzzHelmProvenanceUnmarshal.options000066400000000000000000000000561455727245600257100ustar00rootroot00000000000000[libfuzzer] max_len = 1500000 len_control = 0 rekor-1.3.5/tests/fuzz-testdata/FuzzPackageUnmarshal.options000066400000000000000000000000551455727245600243340ustar00rootroot00000000000000[libfuzzer] max_len = 300000 len_control = 0 rekor-1.3.5/tests/fuzz-testdata/seeds/000077500000000000000000000000001455727245600177355ustar00rootroot00000000000000rekor-1.3.5/tests/fuzz-testdata/seeds/alpine/000077500000000000000000000000001455727245600212055ustar00rootroot00000000000000rekor-1.3.5/tests/fuzz-testdata/seeds/alpine/FuzzPackageUnmarshal/000077500000000000000000000000001455727245600252725ustar00rootroot00000000000000rekor-1.3.5/tests/fuzz-testdata/seeds/alpine/FuzzPackageUnmarshal/FuzzPackageUnmarshal_seed1000066400000000000000000004374001455727245600323730ustar00rootroot00000000000000 t///Ž t///`t t t`t t t u 4/ ( --  .U ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp/] .U ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp/] /6   3 !Ret  .3ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp0ppppppppppppppppppppp/] ] UnmarshalJSV  t~ /3 ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp/]"  ]   ppz!Ret  5!Ret  /3 4!Ret yUp p*ppppWWWWW;WWWGWWWWWW:! p*ppppWW:p*ppppWW ppppppWpppWWWppppppppppppppppppppppppp0ppppppppp `  dRetryableS@? /3 /3%PPPWW5YWWWWWWWWWWWWWWWWWWWWWWWWWpppppppppppppppppppppppp +.4*ppppppppppppppppppppppppppppppppppppppppppppppppppplppppppppppp0ppppppppppppppppppppp/]WWWWWppppppppppppppppppppppppppppppppppppppppppppppp0ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp1pppppppppOpppp/ppppppppp]/]  ( -- [ /3 ] UnmarshalJSV  t /3 ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp/]"       -HealthCheckConfigcingConf%i.WWWWWWWU-WWWGWWW;WWWWWWWWWW@ :!!dp*ppppWW WWWW====================================Call]   ppz!Ret  4!Ret   /3 4!Ret yUp p*ppppWWWWW;WWWGWWWWWW:! p*ppppWW:p*ppppWW ppppppWpppWWWppppppppppppppppppppppppp0ppppppppp `  dRetryableS@? /3 /3%PPPWW5YWWWWWWWWWWWWWWWWWWWWWWWWWpppppppppppppppppppppppp +.4*ppppppppppppppppppppppppppppppppppppppppppppppppppplppppppppppp0ppppppppppppppppppppp/]WWWWWppppppppppppppppppppppppppppppppppppppppppppppp0ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp1pppppppppOpppp/ppppppppp]/]  ( -- [ /3  H[ /3 5!Ret  /3 4!Ret yUp p*ppppWWWWW;WWWGWWWWWW:! p*ppppWW:p*ppppWW ppppppWpppWWWppppppppppppppppppppppppp0ppppppppp `  dRetryableS@? /3 /3%PPPWW5YWWWWWWWWWWWWWWWWWWWWWWWWWpppppppppppppppppppppppp +.4*ppppppppppppppppppppppppppppppppppppppppppppppppppplppppppppppp0ppppppppppppppppppppp/]WWWWWppppppppppppppppppppppppppppppppppppppppppppppp0ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp1pppppppppOpppp/ppppppppp]/]  ( -- [ /3 ] UnmarshalJSV  t /3 /3 4!Ret yUp p*ppppWWWWW;WWWGWWWWWW:! p*ppppWW:p*ppppWW ppppppWpppWWWppppppppppppppppppppppppp0ppppppppp `  dRetryableS@? /3 /3%PPPWW5YWWWWWWWWWWWWWWWWWWWWWWWWWpppppppppppppppppppppppp +.4*ppppppppppppppppppppppppppppppppppppppppppppppppppplppppppppppp0ppppppppppppppppppppp/]WWWWWppppppppppppppppppppppppppppppppppppppppppppppp0ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp1pppppppppOpppp/ppppppppp]/]  ( -- [ /3  H[ /3 e *[]code _#* ( -rekor-1.3.5/tests/harness_test.go000066400000000000000000000245471455727245600170720ustar00rootroot00000000000000// Copyright 2022 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e // +build e2e package e2e import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/json" "encoding/pem" "errors" "fmt" "io/ioutil" "os" "path/filepath" "strconv" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/in-toto/in-toto-golang/in_toto" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/rekor/pkg/generated/models" sigx509 "github.com/sigstore/rekor/pkg/pki/x509" "github.com/sigstore/rekor/pkg/sharding" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/sigstore/pkg/signature" ) type StoredEntry struct { Attestation string UUID string } // Make sure we can add an entry func TestHarnessAddEntry(t *testing.T) { // Create a random artifact and sign it. artifactPath := filepath.Join(t.TempDir(), "artifact") sigPath := filepath.Join(t.TempDir(), "signature.asc") sigx509.CreatedX509SignedArtifact(t, artifactPath, sigPath) dataBytes, _ := ioutil.ReadFile(artifactPath) h := sha256.Sum256(dataBytes) dataSHA := hex.EncodeToString(h[:]) // Write the public key to a file pubPath := filepath.Join(t.TempDir(), "pubKey.asc") if err := ioutil.WriteFile(pubPath, []byte(sigx509.RSACert), 0644); err != nil { t.Fatal(err) } // Verify should fail initially runCliErr(t, "verify", "--type=hashedrekord", "--pki-format=x509", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath) // It should upload successfully. out := runCli(t, "upload", "--type=hashedrekord", "--pki-format=x509", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath) outputContains(t, out, "Created entry at") uuid := getUUIDFromUploadOutput(t, out) logIndex := getLogIndexFromUploadOutput(t, out) if !rekorCLIIncompatible() { // Now we should be able to verify it. out = runCli(t, "verify", "--type=hashedrekord", "--pki-format=x509", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath) outputContains(t, out, "Inclusion Proof:") } saveEntry(t, logIndex, StoredEntry{UUID: uuid}) } // Make sure we can add an intoto entry func TestHarnessAddIntoto(t *testing.T) { td := t.TempDir() attestationPath := filepath.Join(td, "attestation.json") pubKeyPath := filepath.Join(td, "pub.pem") // Get some random data so it's unique each run d := randomData(t, 10) id := base64.StdEncoding.EncodeToString(d) it := in_toto.ProvenanceStatement{ StatementHeader: in_toto.StatementHeader{ Type: in_toto.StatementInTotoV01, PredicateType: slsa.PredicateSLSAProvenance, Subject: []in_toto.Subject{ { Name: "foobar", Digest: common.DigestSet{ "foo": "bar", }, }, }, }, Predicate: slsa.ProvenancePredicate{ Builder: common.ProvenanceBuilder{ ID: "foo" + id, }, }, } b, err := json.Marshal(it) if err != nil { t.Fatal(err) } pb, _ := pem.Decode([]byte(sigx509.ECDSAPriv)) priv, err := x509.ParsePKCS8PrivateKey(pb.Bytes) if err != nil { t.Fatal(err) } s, err := signature.LoadECDSASigner(priv.(*ecdsa.PrivateKey), crypto.SHA256) if err != nil { t.Fatal(err) } signer, err := dsse.NewEnvelopeSigner(&sigx509.Verifier{ S: s, }) if err != nil { t.Fatal(err) } env, err := signer.SignPayload(context.Background(), "application/vnd.in-toto+json", b) if err != nil { t.Fatal(err) } eb, err := json.Marshal(env) if err != nil { t.Fatal(err) } write(t, string(eb), attestationPath) write(t, sigx509.ECDSAPub, pubKeyPath) // If we do it twice, it should already exist out := runCliStdout(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", pubKeyPath) outputContains(t, out, "Created entry at") uuid := getUUIDFromUploadOutput(t, out) logIndex := getLogIndexFromUploadOutput(t, out) out = runCli(t, "get", "--log-index", fmt.Sprintf("%d", logIndex), "--format=json") g := getOut{} if err := json.Unmarshal([]byte(out), &g); err != nil { t.Fatal(err) } // The attestation should be stored at /var/run/attestations/sha256:digest got := in_toto.ProvenanceStatement{} if err := json.Unmarshal([]byte(g.Attestation), &got); err != nil { t.Fatal(err) } if diff := cmp.Diff(it, got); diff != "" { t.Errorf("diff: %s", diff) } attHash := sha256.Sum256(b) intotoModel := &models.IntotoV002Schema{} if err := types.DecodeEntry(g.Body.(map[string]interface{})["IntotoObj"], intotoModel); err != nil { t.Errorf("could not convert body into intoto type: %v", err) } if intotoModel.Content == nil || intotoModel.Content.PayloadHash == nil { t.Errorf("could not find hash over attestation %v", intotoModel) } recordedPayloadHash, err := hex.DecodeString(*intotoModel.Content.PayloadHash.Value) if err != nil { t.Errorf("error converting attestation hash to []byte: %v", err) } if !bytes.Equal(attHash[:], recordedPayloadHash) { t.Fatal(fmt.Errorf("attestation hash %v doesnt match the payload we sent %v", hex.EncodeToString(attHash[:]), *intotoModel.Content.PayloadHash.Value)) } out = runCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", pubKeyPath) outputContains(t, out, "Entry already exists") saveEntry(t, logIndex, StoredEntry{Attestation: g.Attestation, UUID: uuid}) } func getEntries(t *testing.T) (string, map[int]StoredEntry) { tmpDir := os.Getenv("REKOR_HARNESS_TMPDIR") if tmpDir == "" { t.Skip("Skipping test, REKOR_HARNESS_TMPDIR is not set") } file := filepath.Join(tmpDir, "attestations") t.Log("Reading", file) attestations := map[int]StoredEntry{} contents, err := os.ReadFile(file) if errors.Is(err, os.ErrNotExist) || contents == nil { return file, attestations } if err != nil { t.Fatal(err) } if err := json.Unmarshal(contents, &attestations); err != nil { t.Fatal(err) } return file, attestations } func saveEntry(t *testing.T, logIndex int, entry StoredEntry) { file, attestations := getEntries(t) t.Logf("Storing entry for logIndex %d", logIndex) attestations[logIndex] = entry contents, err := json.Marshal(attestations) if err != nil { t.Fatal(err) } if err := os.WriteFile(file, contents, 0777); err != nil { t.Fatal(err) } } func compareAttestation(t *testing.T, logIndex int, got string) { _, entries := getEntries(t) expected, ok := entries[logIndex] if !ok { t.Fatalf("expected to find persisted entries with logIndex %d but none existed: %v", logIndex, entries) } if got != expected.Attestation { t.Fatalf("attestations don't match, got %v expected %v", got, expected) } } // Make sure we can get and verify all entries // For attestations, make sure we can see the attestation // Older versions of the CLI may not be able to parse the retrieved entry. func TestHarnessGetAllEntriesLogIndex(t *testing.T) { if rekorCLIIncompatible() { t.Skipf("Skipping getting entries by UUID, old rekor-cli version %s is incompatible with server version %s", os.Getenv("CLI_VERSION"), os.Getenv("SERVER_VERSION")) } treeSize := activeTreeSize(t) if treeSize == 0 { t.Fatal("There are 0 entries in the log, there should be at least 2") } for i := 0; i < treeSize; i++ { out := runCli(t, "get", "--log-index", fmt.Sprintf("%d", i), "--format", "json") if !strings.Contains(out, "IntotoObj") { continue } var intotoObj struct { Attestation string } if err := json.Unmarshal([]byte(out), &intotoObj); err != nil { t.Fatal(err) } compareAttestation(t, i, intotoObj.Attestation) t.Log("IntotoObj matches stored attestation") } } func TestHarnessGetAllEntriesUUID(t *testing.T) { if rekorCLIIncompatible() { t.Skipf("Skipping getting entries by UUID, old rekor-cli version %s is incompatible with server version %s", os.Getenv("CLI_VERSION"), os.Getenv("SERVER_VERSION")) } treeSize := activeTreeSize(t) if treeSize == 0 { t.Fatal("There are 0 entries in the log, there should be at least 2") } _, entries := getEntries(t) for _, e := range entries { outUUID := runCli(t, "get", "--uuid", e.UUID, "--format", "json") outEntryID := runCli(t, "get", "--uuid", entryID(t, e.UUID), "--format", "json") if outUUID != outEntryID { t.Fatalf("Getting by uuid %s and entryID %s gave different outputs:\nuuid: %v\nentryID:%v\n", e.UUID, entryID(t, e.UUID), outUUID, outEntryID) } if !strings.Contains(outUUID, "IntotoObj") { continue } var intotoObj struct { Attestation string } if err := json.Unmarshal([]byte(outUUID), &intotoObj); err != nil { t.Fatal(err) } if intotoObj.Attestation != e.Attestation { t.Fatalf("attestations don't match, got %v expected %v", intotoObj.Attestation, e.Attestation) } } } func entryID(t *testing.T, uuid string) string { if sharding.ValidateEntryID(uuid) == nil { return uuid } treeID, err := strconv.Atoi(os.Getenv("TREE_ID")) if err != nil { t.Fatal(err) } tid := strconv.FormatInt(int64(treeID), 16) ts, err := sharding.PadToTreeIDLen(tid) if err != nil { t.Fatal(err) } return ts + uuid } func activeTreeSize(t *testing.T) int { out := runCliStdout(t, "loginfo", "--format", "json", "--store_tree_state", "false") t.Log(string(out)) var s struct { ActiveTreeSize int } if err := json.Unmarshal([]byte(out), &s); err != nil { t.Fatal(err) } return s.ActiveTreeSize } // Check if we have a new server version and an old CLI version // since the new server returns an EntryID but the old CLI version expects a UUID // Also, new rekor server allows upload of intoto v0.0.2, and old rekor cli versions // don't understand how to parse these entries. // TODO: use semver comparisons. func rekorCLIIncompatible() bool { if sv := os.Getenv("SERVER_VERSION"); sv != "v0.10.0" && sv != "v0.11.0" { if cv := os.Getenv("CLI_VERSION"); cv == "v0.10.0" || cv == "v0.11.0" { return true } } return false } rekor-1.3.5/tests/intoto_dsse.json000066400000000000000000000010201455727245600172430ustar00rootroot00000000000000{"payloadType":"application/vnd.in-toto+json","payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsInN1YmplY3QiOlt7Im5hbWUiOiJmb29iYXIiLCJkaWdlc3QiOnsiZm9vIjoiYmFyIn19XSwicHJlZGljYXRlIjp7ImJ1aWxkZXIiOnsiaWQiOiJmb29ISzFiZ2Y1WC8xckNxZz09In0sImJ1aWxkVHlwZSI6IiIsImludm9jYXRpb24iOnsiY29uZmlnU291cmNlIjp7fX19fQ==","signatures":[{"keyid":"","sig":"MEQCIAIlnxHC3eU4jmUsqJExxfzqyy8bk+61btgnRiGcRDxgAiBwmdnJ/GX1yCYhYAvwAtkuYN0yFlVPQVAx9R6JpUUBiA=="}]}rekor-1.3.5/tests/intoto_dsse.pem000066400000000000000000000002611455727245600170610ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEx+ikqUxXurlxZltajRBV2ju31j32 baT2ax2dXBcpInWaFESqGF35KISflP1EmMvEnfG+AzHecQ0WQp5QzNId+w== -----END PUBLIC KEY-----rekor-1.3.5/tests/issue-872-e2e-test.sh000077500000000000000000000166361455727245600174740ustar00rootroot00000000000000#!/bin/bash # # Copyright 2022 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -e testdir=$(dirname "$0") echo "* starting services" docker-compose up -d echo "* building CLI" go build -o rekor-cli ./cmd/rekor-cli REKOR_CLI=$(pwd)/rekor-cli function waitForRekorServer () { echo -n "* waiting up to 60 sec for system to start" count=0 until [ $(docker ps -a | grep -c "(healthy)") == 3 ]; do if [ $count -eq 6 ]; then echo "! timeout reached" exit 1 else echo -n "." sleep 10 let 'count+=1' fi done echo } REKORTMPDIR="$(mktemp -d -t rekor_test.XXXXXX)" touch $REKORTMPDIR.rekor.yaml trap "rm -rf $REKORTMPDIR" EXIT waitForRekorServer echo "* stopping rekor to test issue #872" docker-compose stop rekor-server docker volume rm -f issue872_attestations || true ATT_VOLUME=$(docker volume create --name issue872_attestations) # set permissions on docker volume to be friendly to non-root since v0.6.0 container is based on distroless docker run --rm -v $ATT_VOLUME:/att:z busybox /bin/sh -c 'touch /att/.initialized && chown -R 65532:65532 /att && chmod 777 /att' V060_COMPOSE_FILE=$REKORTMPDIR/docker-compose-issue872-v060.yaml cat << EOF > $V060_COMPOSE_FILE version: '3.4' services: rekor-server-issue-872-v060: # this container image is built on v0.6.0 with the fix for issue #800 image: gcr.io/projectsigstore/rekor/ci/rekor/rekor-server@sha256:568aee99574e6d796d70b7b1fd59438bd54b3b9f44cc2c9a086629597c66d324 user: "65532:65532" command: [ "serve", "--trillian_log_server.address=trillian-log-server", "--trillian_log_server.port=8090", "--redis_server.address=redis-server", "--redis_server.port=6379", "--rekor_server.address=0.0.0.0", "--rekor_server.signer=memory", "--enable_attestation_storage", "--attestation_storage_bucket=file:///ko-app/attestations", # Uncomment this for production logging # "--log_type=prod", ] volumes: - "$ATT_VOLUME:/ko-app/attestations:z" restart: always # keep the server running ports: - "0.0.0.0:3000:3000" - "0.0.0.0:2112:2112" volumes: $ATT_VOLUME: external: true EOF echo "* starting rekor v0.6.0 to test issue #872" docker-compose -f $V060_COMPOSE_FILE --project-directory=$PWD up -d rekor-server-issue-872-v060 sleep 5 # this rekor-cli image is based on v0.6.0 and has the fix for issue #800 ISSUE800_CONTAINER=gcr.io/projectsigstore/rekor/ci/rekor/rekor-cli@sha256:34f6ec6324a6f32f118dc14d33e5cc081fb8b49a5026d388f782a3566afa2ca8 ISSUE800_CONTAINER_ID=$(docker create $ISSUE800_CONTAINER) ISSUE800_CLI=$REKORTMPDIR/rekor-cli-issue-800 docker cp "$ISSUE800_CONTAINER_ID:/ko-app/rekor-cli" $ISSUE800_CLI docker rm $ISSUE800_CONTAINER_ID >/dev/null V060_UPLOAD_OUTPUT=$REKORTMPDIR/issue-872-upload-output echo "* inserting intoto entry into Rekor v0.6.0" if ! $ISSUE800_CLI upload --type intoto --artifact tests/intoto_dsse.json --public-key tests/intoto_dsse.pem --format=json --rekor_server=http://localhost:3000 > $V060_UPLOAD_OUTPUT; then echo "* failed to insert intoto entry to test issue #872, exiting" docker-compose logs --no-color > /tmp/docker-compose.log docker-compose -f $V060_COMPOSE_FILE --project-directory=$PWD logs rekor-server-issue-872-v060 > /tmp/post-insert-docker-compose.log exit 1 fi ISSUE872_UPLOAD_INDEX=$(jq -r .Index $V060_UPLOAD_OUTPUT) V060_GET_OUTPUT=$REKORTMPDIR/issue-872-get-output echo "* read back entry from Rekor v0.6.0" if ! $ISSUE800_CLI get --log-index=$ISSUE872_UPLOAD_INDEX --format=json --rekor_server=http://localhost:3000 > $V060_GET_OUTPUT; then echo "* failed to retrieve entry from rekor v0.6.0 to test issue #872, exiting" docker-compose logs --no-color > /tmp/docker-compose.log docker-compose -f $V060_COMPOSE_FILE --project-directory=$PWD logs rekor-server-issue-872-v060 > /tmp/post-insert-docker-compose.log exit 1 fi echo "* checking to ensure attestation is successfully returned from rekor v0.6.0" V060_ATT_LENGTH=$(jq -r '.Attestation | length' $V060_GET_OUTPUT) if [ $V060_ATT_LENGTH -eq 0 ]; then echo "* failed to read back attestation while testing issue #872 against rekor v0.6.0, exiting" cat $V060_GET_OUTPUT docker-compose logs --no-color > /tmp/docker-compose.log docker-compose -f $V060_COMPOSE_FILE --project-directory=$PWD logs rekor-server-issue-872-v060 > /tmp/post-insert-docker-compose.log exit 1 fi echo "* grabbing TreeID to use when starting older version" REKOR_TRILLIAN_LOG_SERVER_TLOG_ID=$($ISSUE800_CLI loginfo --rekor_server=http://localhost:3000 --format=json | jq -r .TreeID) echo "* stopping rekor v0.6.0 to test issue #872" docker-compose -f $V060_COMPOSE_FILE --project-directory=$PWD logs rekor-server-issue-872-v060 > /tmp/post-insert-docker-compose.log docker-compose -f $V060_COMPOSE_FILE --project-directory=$PWD stop rekor-server-issue-872-v060 COMPOSE_FILE=$REKORTMPDIR/docker-compose-issue872.yaml cat << EOF > $COMPOSE_FILE version: '3.4' services: rekor-server: build: context: . target: "deploy" command: [ "rekor-server", "serve", "--trillian_log_server.address=trillian-log-server", "--trillian_log_server.port=8090", "--redis_server.address=redis-server", "--redis_server.port=6379", "--rekor_server.address=0.0.0.0", "--rekor_server.signer=memory", "--enable_attestation_storage", "--attestation_storage_bucket=file:///var/run/attestations", "--trillian_log_server.tlog_id=$REKOR_TRILLIAN_LOG_SERVER_TLOG_ID", # Uncomment this for production logging # "--log_type=prod", ] volumes: - "$ATT_VOLUME:/var/run/attestations:z" restart: always # keep the server running ports: - "3000:3000" - "2112:2112" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/ping"] interval: 10s timeout: 3s retries: 3 start_period: 5s volumes: $ATT_VOLUME: external: true EOF docker network prune -f echo "* starting rekor under test to ensure attestation inserted in old version is successfully returned" docker-compose -f $COMPOSE_FILE --project-directory=$PWD up -d waitForRekorServer ISSUE872_GET_ENTRY=$REKORTMPDIR/issue-872-get-entry echo "* fetching previous entry made under v0.6.0" if ! $REKOR_CLI get --log-index=$ISSUE872_UPLOAD_INDEX --rekor_server=http://localhost:3000 --format=json > $ISSUE872_GET_ENTRY; then echo "* failed to read back intoto entry while testing issue #872, exiting" docker-compose logs --no-color > /tmp/docker-compose.log exit 1 fi #ensure attestation of len() > 0 returned echo "* checking to ensure attestation is successfully returned" ATT_LENGTH=$(jq -r '.Attestation | length' $ISSUE872_GET_ENTRY) if [ $ATT_LENGTH -eq 0 ]; then echo "* failed to read back attestation while testing issue #872, exiting" cat $ISSUE872_GET_ENTRY docker-compose logs --no-color > /tmp/docker-compose.log exit 1 else echo "* tests succeeded!" firekor-1.3.5/tests/node/000077500000000000000000000000001455727245600147525ustar00rootroot00000000000000rekor-1.3.5/tests/node/SHASUMS256.txt000066400000000000000000000055611455727245600171020ustar00rootroot00000000000000e9630d01ee1ff74445e12ab2f14581b2aea9201eca05b39f8174697afdba74b2 node-v15.4.0-aix-ppc64.tar.gz 503d160016f6a61ec25a9462499721ef96b9ed08e232a420d17541774e079dda node-v15.4.0-darwin-x64.tar.gz db5bfc430f865061bde1f6afe6d75fae98fc04b647050e9e7ad7444a44d790e0 node-v15.4.0-darwin-x64.tar.xz 2e0efc6931f30e6d8ba1cdde018ae234bd6186b3f38ce8a94a8304f4340fbd2f node-v15.4.0-headers.tar.gz cf1f16dbda084ac8102b2faeb2608bd3d3541311281ba8869da06649f8099dba node-v15.4.0-headers.tar.xz 0dad2932f7f7e0fc21bca0690d31f065080dbbf448527e982447355ff4bb91bd node-v15.4.0-linux-arm64.tar.gz 1b2b601167f6e07bb9b2bb86774bc386c5c60fac6ab28e49ad6d62f491be2b7f node-v15.4.0-linux-arm64.tar.xz a36ecfa85ca2b6cfbb864190147a26c1fd2e04e15ab4b31b3a398663dc91223a node-v15.4.0-linux-armv7l.tar.gz 8912e375b96ef964db5c1ba100e3afedc13730d53570cbe7c0dd71e055afb1b4 node-v15.4.0-linux-armv7l.tar.xz ebcc8c6819032fc02dee3214d61032c11cd9ecfc764613e8f7ed2cbe5afaabff node-v15.4.0-linux-ppc64le.tar.gz 6948bad4bcdac9b8f4f8b0a29b8eb89c0b09c39ded0bdd67e4a55a4c89c0af80 node-v15.4.0-linux-ppc64le.tar.xz 93ca0bf22bd0ee7a5f261a8235b0e8305873228e63c174bd15a77ce2d222c69f node-v15.4.0-linux-s390x.tar.gz 417ade49c5d9b15391fb47e5d95bc91b0da90977472f062905b22f1a4a027f4c node-v15.4.0-linux-s390x.tar.xz 96b801f51bf73330c65e6ee4d17c5b223fded16d8020af3b3550a548d271b1e2 node-v15.4.0-linux-x64.tar.gz a1fc57a8aeeb2a175ca62718fdc0f896efc0f78695a7d4f56d5e8653a24b1a11 node-v15.4.0-linux-x64.tar.xz 4ee293f815395b1774895da79f02ae6bc5c158b93985a315db36b54f25f62c8c node-v15.4.0.pkg b199796544d988b4bb61e38584cd097744e073fa0559cbec772858d91ce4649f node-v15.4.0.tar.gz ba726955316c8190adc8e8c892d8782e7167643e41b22bb7c7aa4e092783e04e node-v15.4.0.tar.xz 343f603329de39f4019df9ef8ff7610d3e079bc397afd15560abf7bd9f60fafe node-v15.4.0-win-x64.7z 9bfd174d15cf38885740e3b2308e701f33d064bb1100afd59f8114298a89e4a7 node-v15.4.0-win-x64.zip ac97daba712c7e5ae58d8cdd9e7e7a3103092efdaef0328b14e80bc046b21d86 node-v15.4.0-win-x86.7z 37d852eade26bfd262f62190f848265409e0de5d510753c158c619ed4897dd63 node-v15.4.0-win-x86.zip 0be3e8050feecc0546d81fc5884626e7ea1ed99a2c1fdcc96e246e59774d4809 node-v15.4.0-x64.msi cee5d80302f0a4d29816cae05f80058e578c72d08caa36e19bd766a8bcd4a9cb node-v15.4.0-x86.msi 1a76edb3b10a006ea6108f318c9fa959fe1e692586bad90b3edfda4918f5fb69 win-x64/node.exe 8730c9880ba0237ec978c6bfb224e84390112980dc39daabb89f1148e9f9f81d win-x64/node.lib b7e88f414378a776282586a0de4a6aa61869c647426e57466ee6fac3059cc760 win-x64/node_pdb.7z c2618d56cd3a1cbf6b267c44b42f260082b3eb33b83944084a8e50425ba02fb2 win-x64/node_pdb.zip f170feef25cf2f77be3f9c67c123f03999f913cfb6a04267df827ed0320fa1d1 win-x86/node.exe 5d6b151f6deaa4ca42b52e5333f467f2ae8f12dfa52715bf9f16562723fc996e win-x86/node.lib 60de0dc131697829439de285beef8dd31ad366cf6b183e5aac7467b098c38c21 win-x86/node_pdb.7z fc102b969efdd8a335d039d13d39c4a174947395fa0a89569e7b7381b278238f win-x86/node_pdb.zip rekor-1.3.5/tests/node/key000066400000000000000000000031341455727245600154660ustar00rootroot00000000000000 [X b_w ?V O2KJޞ3dLa獎@JHw(6IhA;Hy.*˚+Z7}9؄, ,)z(K=Z1*hjl<37 O.~ 5a.@"Hޮ" }[lZZhT@(iiwiB!ğCy#91K3~Ao䜯5r)yo|q:$%H[ϫmoø(Danielle Adams T>   !3BDu7f{_j _ f f{H'-luh6U,.ТJ??WqPzNq*"͸A~?r]q(*% ЅM{0.k̋)*356:Y9J.h8fWfIܞ]v'ЉbVZkj_j^ 7cOt>n VfwN)p 7]S @0>ZU e!޶k\3 $V8I{A)danielleadams T>   !3BDu7f{_j ` f f{$:dTu;!TAc78HQycX_Ij{]0U[6 !3BDu7f{[X f{nu9qwX(OmMmN޺P I]`fNBn*|5 ~;{ &e+ J aT|xDcC$mrekor-1.3.5/tests/node/sha000066400000000000000000000001011455727245600154400ustar00rootroot00000000000000bc51ec78f8f290860c35cdd93ef3886b464ba5a535b2e6f2a80623439026bcd5 rekor-1.3.5/tests/node/sig000066400000000000000000000004661455727245600154650ustar00rootroot000000000000003!3BDu7f{_ f{;#5̽!T.wDi(WeW9q;S"Azt4DTKsu[0: Gm08}13 G.wj"y}oj{Ɖ!7pt{C7S /tmp/docker-compose.log exit 1 else echo -n "." sleep 10 let 'count+=1' fi done git checkout $server_version . git checkout $current_branch echo } function build_cli () { echo "Building CLI at version $cli_version" cli_version=$1 current_branch=$(git rev-parse --abbrev-ref HEAD) git checkout $cli_version make rekor-cli git checkout $cli_version . git checkout $current_branch } function run_tests () { REKORTMPDIR="$(mktemp -d -t rekor_test.XXXXXX)" touch $REKORTMPDIR.rekor.yaml trap "rm -rf $REKORTMPDIR" EXIT go clean -testcache if ! REKORTMPDIR=$REKORTMPDIR SERVER_VERSION=$1 CLI_VERSION=$2 go test -run TestHarness -v -tags=e2e ./tests/ ; then docker-compose logs --no-color > /tmp/docker-compose.log exit 1 fi if docker-compose logs --no-color | grep -q "panic: runtime error:" ; then # if we're here, we found a panic echo "Failing due to panics detected in logs" docker-compose logs --no-color > /tmp/docker-compose.log exit 1 fi } # Get last 2 server versions git fetch --all --tags NUM_VERSIONS_TO_TEST=2 # don't explicitly fetch RC builds; they'll be included below when we test at HEAD VERSIONS=$(git tag --sort=-version:refname | grep -v "rc" | head -n $NUM_VERSIONS_TO_TEST | tac) # Also add the commit @ HEAD HEAD=$(git log --pretty="%H" -n 1 ) echo "Also testing at HEAD at commit $HEAD" VERSIONS="$VERSIONS $HEAD" echo $VERSIONS export REKOR_HARNESS_TMPDIR="$(mktemp -d -t rekor_test_harness.XXXXXX)" docker-compose down for server_version in $VERSIONS do start_server $server_version for cli_version in $VERSIONS do echo "=======================================================" echo "Running tests with server version $server_version and CLI version $cli_version" build_cli $cli_version run_tests $server_version $cli_version echo "Tests passed successfully." echo "=======================================================" done done # Since we add two entries to the log for every test, once all tests are run we should have 2*(($NUM_VERSIONS_TO_TEST+1)^2) entries make rekor-cli actual=$(./rekor-cli loginfo --rekor_server http://localhost:3000 --format json --store_tree_state=false | jq -r .ActiveTreeSize) expected=$((2*(1+$NUM_VERSIONS_TO_TEST)*(1+$NUM_VERSIONS_TO_TEST))) if [[ ! "$expected" == "$actual" ]]; then echo "ERROR: Log had $actual entries instead of expected $expected" exit 1 fi echo "Harness testing successful :)" rekor-1.3.5/tests/rekor.json000066400000000000000000000105011455727245600160370ustar00rootroot00000000000000{ "kind": "rekord", "apiVersion": "0.0.1", "spec": { "signature": { "format": "pgp", "content": "iQHKBAABCAA0FiEEcgCUXG78adj6hGUJJrfBoJ04pHoFAl+86RwWHGxoaW5kc0Bwcm90b25tYWlsLmNvbQAKCRAmt8GgnTikejcHC/9yyGEPh2D+MnNR8I8w0sfWChc6pGAQoS6qk/sfC/9GvF4OC7RIy6OwLr/lxyEZbOP2ngYjh/s5KjKxhZyApwwg13LmcbazGnXc3E76J55LoTfwoRa9fupH/M6HI56VFKwnu+AbMNW1s+DM47r7i5nIN6IX9kMpDe3B9XTUULff/yNUv0XtXU+VAf8ndF1w117YVWxf8TnU/HWvX74URQPN+syuyqK/NO1H1KhBVTzcIYd5H6kJu300jgkDypyyqQpd/pJYVwfeY8fCOaeCpfIPjKQ/4enCsAeBgKsAwfIbor8WiE86KoANYqROaW7uqiN+VPadbWVeN6bMpRIdEq8+NKQGlepSCRqbkVg4VKGOPgB3h5WbY9U1O1FVDnXyt7kWdEPEZjBX+V4DawshvNe5LIyqH5hJ1QNAFd0UStqKQt8EUZ/gAtQiXSGbxM1ACoYL9HblKW5b+kj/onKghekFoCoAfhMwRRqR5g/TS/Pc2/ztwYTIuhpQQfMXziTm64g=", "publicKey": { "content":"LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tCgptUUdOQkYrY0lNMEJEQUNhOEc3UkQydjNtaXdNdHhWYVppM0pCVnVlVkFxSEtDNGVLb01TNUhNQ1JvK0haVlJBCjcwWG1zVHBYMVoxZ1pRdXVDMEdEWTI2aEJoZWpBcTNoeDJydjYvOHE5MEJ2V0dIOXRWZUdwTDFzYUltNTJnRVIKWHlWZ2d6NWtBQzBTNnZNbjdkcjJldEJrV1dQK09qMDVTMDJZWkJUWWd4cE9ieWVjVVNjcUtOVGpzbFpRQkgyZApTSHVrM28yWjdoTTQ5VTBsN3piV3c0b0lUK2xBUmNzYWpRVHdXamxpYVBEL0hSalQyblJPaEloaXRlZC93Z3l6CnlkSXE1ZTZzMThWTGNUNzVxV25yWlhOUFdGd2YyNVJYWTN1dGtXK0dXNW5RZU44MFEya1JFZ2t4RnM1QWQ1V1oKdkU3dDgvaHg1em1zbFo0dGZGNHNpM1FaZUlRQmFjWWJ3eE1QU0RmOW9GR3hkR0ZUODg5d0pMR2dXbXIxVGtQTQpjTjA2d3hBUkd0eE4wejYxRkpUaWpMV1JialczdW5JOWhjUWNVbE4vUSsxNm90SHBlS1ZnNG9XMDBDcnZXT0Q2CnFrTGNNRDQ5eVVEOGZTR0IrUkVuaUJhODlDOWtRVTRTS2Rnc0xML1ErSksrU3k5S21JRHJtMWE0RGZQMXBzZmUKTGphcnpzVlpmS1VIZndjQUVRRUFBYlFpVEhWclpTQklhVzVrY3lBOGJHaHBibVJ6UUhCeWIzUnZibTFoYVd3dQpZMjl0UG9rQjFBUVRBUWdBUGhZaEJISUFsRnh1L0duWStvUmxDU2Ezd2FDZE9LUjZCUUpmbkNETkFoc0RCUWtECndtY0FCUXNKQ0FjQ0JoVUtDUWdMQWdRV0FnTUJBaDRCQWhlQUFBb0pFQ2Ezd2FDZE9LUjZaMWtMLzFJSzB2ZGUKWlg1cjVTZWJOeFRJTlNBQXZZa3JLUnlKNWY3bE9NOWdMR0l1YzJGb05VbmpWUVQwcklHOTAxOWg0OHBDeTkxZgpYakREUk1ZOWd6RldXQ2dHblhoMWhXSTNNN0JKRjZZRTZ1NkRYR3N2dVVwR3JOZVpBRzZra2F6QXVBbm5WMGtDCjA4em9SckFaQ3ZscGFacnlkOGl0YityVitRS3A3QXcybEFJSDFlNmR3TTRSTEZqdmZrOExKWHhqSkFvUG13NmwKTHcxOGM3b1c2UkxPOVFYUThlTTZyMnZISHBtMFR1ZHZaeWFmTnVDMzJHRGxNWTR1MFYxRGI4THN5bVBzQWh1QQoySno0L0tQcTZ1S3dJdG1WSzRwbmRmRUR1NkQxVG9vRFlYaXB0WWFmZHZVMzNwVVF4d0hvZlRUZkU1elp3MlBlCmxIM25aZHNnSFhHUHhKTExNcU9wVzRDL2NNNlpRVmdZU3RWcjBudlU2NitRalF2c2tVWlIwNmRkRXpuQnBHSnMKdHBtajlBZS9HUlk4RU5uTjkvMkdmRXVydHozZEtOVVpvak15MTUzamNHMFUxenpoMTE1V0o3dDh3SEJ1NFM0cAowZ0UrUkFxeXRBY0laRGQyTlNOcno4VnI5RkU5eCtmYXQ5RVJsYm5kQUJFNWlWOHNLMCtGYW5Xd2dia0JqUVJmCm5DRE5BUXdBdEJvdGhmY1J6cjN4cjNQOXA3UUNNd0t1aW9udk1DbThXZ3dOUzRDcGhxbzVOT3IyaU1qa0xQMEoKb21nSkxWWDVOK2Jydjh5NEg4cllQd0tCMTZvL2hBOEliR2JwWXltM0ZjeWtUd2NiV2J0UFRMRXRkQ1VQTFlURApOQzVMR0pwZzNlODZZZlF0QU42L01uWnlZT21sRHgyV0d0dExkbXNBU0dWdXg2QVZKcUl2K3gwNlVLSkVtSzN0CmpsRVZLeWcxMlJFenllNUlUNnFFU0dwT3pvMllsV1VxSVR3L0FhUFEyWnhVYXh2WUZvVU9jd2djZG5Ia2dzaEkKT245aC9OSFVtUDMyV1F2cWtRTXVVYVBJTlJzQzgzS3ZUREdseWZTSFZGek1hNGhETWhFY1h6NGFjaW5kNVdUZQp6eUxnWmhPYjdjTmVDeDR4Y3J0UEI2VTdCUi9GVkx6TEJsQXp1emppRWhZd0pvM0FPTXFGb1I1bUFxaGx1dE5PCnNzeW9mYnFUZ0diU0xkamJYUC9hRXRnejJNVjluL29jMVNCOEhlWk8vMTdKeWduenJ1SUt5Ky9sT1dPenQralYKVkZwVnloMXVlOGxGN3ltS1I0dHNsK2lJVmJxblB2cE1oTE9JQnFYRm4yZ01Da0dvSkx5N09IbzJXQUVKR2x0MwpTd3BicmpqMUFCRUJBQUdKQWJ3RUdBRUlBQ1lXSVFSeUFKUmNidnhwMlBxRVpRa210OEdnblRpa2VnVUNYNXdnCnpRSWJEQVVKQThKbkFBQUtDUkFtdDhHZ25UaWtlaW5pREFDRUFma1pxLzRScDJhTkE0ZGJvSjdVRlhET2FSa1YKOU1Lb0VaRnFUTU5vdkRMNXhoTWxnbFBQdS9sK2RoVGd4ZGVKOUVWSG9lenRiODk2VS9wT3VCUnNuOVZ0VzRZLwpqZWlXN0V5TlhBZC9PcnZuRmJ4KzdpWExxdXBaSkpGVGkvajlSaFZZTnNtbDdzZWJUUGVCbkdEQTkxcWJDNHhICnBRVkRDdWp4NjlWeE81RTFMU29oQ00rTy81dkxCbThpMW8vbmJGbWJ5N1ZDeUtlUkRmaHRmOW5DODRxc0U5R3EKVTcvTFNpazliZnhNV2JwcTh5a250bVMzYTBzemM0YlZGcGV6QnBtTmIwQVZjQitUbTlnV21FemhpTHM2RktBTgpJbnFOdVh1Qkw5UENhYzcrbVUrYzJtQmdHT1JHZDFkWk8zUkM4OXpGM3hCQlluQ09lNWNBTUZsYzFYR3NsbHNJCmR6ZHJkWHZiTkJ6L2o3MXB1TjhvRlltL1hiVmNpZU8wVGZRaURjVHQ4S2lpUjlUQUQ5L1A1OTNSTWxMT0dTOHAKaHZKYmlGb1pmWEhjbHNaRkhtOERRUWE5NElad1RCOG00Z0JWME0yWFN2ZEhvMzBsc3FqdFphWmlTclJoNHJzaApuMTRwYkFhVGRhS0VQY3Z0dWZiVXVXMElqWWQya3BJVC90Zz0KPU9naHIKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQo=" } }, "data": { "url": "https://raw.githubusercontent.com/sigstore/rekor/main/tests/test_file.txt", "hash": { "algorithm": "sha256", "value": "45c7b11fcbf07dec1694adecd8c5b85770a12a6c8dfdcf2580a2db0c47c31779" } } } } rekor-1.3.5/tests/sharding-e2e-test.sh000077500000000000000000000237571455727245600176270ustar00rootroot00000000000000#!/bin/bash # # Copyright 2021 The Sigstore Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -ex # Things to install first: # - jq, createtree # Spin up services as usual echo "Installing createtree..." go install github.com/google/trillian/cmd/createtree@latest echo "starting services" docker-compose up -d rm ~/.rekor/state.json || true echo "building CLI and server" go build -o rekor-cli ./cmd/rekor-cli REKOR_CLI=$(pwd)/rekor-cli go build -o rekor-server ./cmd/rekor-server function check_log_index () { logIndex=$1 # make sure we can get this log index from rekor $REKOR_CLI get --log-index $logIndex --rekor_server http://localhost:3000 # make sure the entry index matches the log index gotIndex=$($REKOR_CLI get --log-index $logIndex --rekor_server http://localhost:3000 --format json | jq -r .LogIndex) if [[ "$gotIndex" == $logIndex ]]; then echo "New entry has expected virtual log index $gotIndex" else echo "FAIL: expected virtual log index $logIndex, got $gotIndex" exit 1 fi } function stringsMatch () { one=$1 two=$2 if [[ "$one" == "$two" ]]; then echo "Strings match" else echo "$one and $two don't match but should" exit 1 fi } function waitForRekorServer () { count=0 echo -n "waiting up to 60 sec for system to start" until [ $(docker-compose ps | grep -c "(healthy)") == 3 ]; do if [ $count -eq 6 ]; then echo "! timeout reached" REKOR_CONTAINER_ID=$(docker ps --filter name=rekor-server --format {{.ID}}) docker logs $REKOR_CONTAINER_ID exit 1 else echo -n "." sleep 10 let 'count+=1' fi done echo } function collectLogsOnFailure () { if [[ "$1" -ne "0" ]]; then echo "failure detected, collecting docker-compose logs" docker-compose logs --no-color > /tmp/docker-compose.log exit $1 elif docker-compose logs --no-color | grep -q "panic: runtime error:" ; then # if we're here, we found a panic echo "failing due to panics detected in logs" docker-compose logs --no-color > /tmp/docker-compose.log exit 1 fi exit 0 } trap "collectLogsOnFailure $?" EXIT echo "Waiting for rekor server to come up..." waitForRekorServer # Add some things to the tlog :) pushd tests $REKOR_CLI upload --artifact test_file.txt --signature test_file.sig --public-key test_public_key.key --rekor_server http://localhost:3000 popd # Make sure we can prove consistency $REKOR_CLI loginfo --rekor_server http://localhost:3000 # Add 2 more entries to the log pushd tests/sharding-testdata $REKOR_CLI upload --artifact file1 --signature file1.sig --pki-format=x509 --public-key=ec_public.pem --rekor_server http://localhost:3000 $REKOR_CLI upload --artifact file2 --signature file2.sig --pki-format=x509 --public-key=ec_public.pem --rekor_server http://localhost:3000 popd INITIAL_TREE_ID=$($REKOR_CLI loginfo --rekor_server http://localhost:3000 --format json | jq -r .TreeID) echo "Initial Tree ID is $INITIAL_TREE_ID" # Make sure we have three entries in the log check_log_index 2 $REKOR_CLI logproof --rekor_server http://localhost:3000 --last-size 2 # Now, we want to shard the log. # Create a new tree echo "creating a new Tree ID...." SHARD_TREE_ID=$(createtree --admin_server localhost:8090) echo "the new shard ID is $SHARD_TREE_ID" # Once more $REKOR_CLI loginfo --rekor_server http://localhost:3000 # Get the public key for the active tree for later ENCODED_PUBLIC_KEY=$(curl http://localhost:3000/api/v1/log/publicKey | base64 -w 0) # Spin down the rekor server echo "stopping the rekor server..." REKOR_CONTAINER_ID=$(docker ps --filter name=rekor-server --format {{.ID}}) docker stop $REKOR_CONTAINER_ID # Now we want to spin up the Rekor server again, but this time point # to the new tree SHARDING_CONFIG=sharding-config.yaml cat << EOF > $SHARDING_CONFIG - treeID: $INITIAL_TREE_ID encodedPublicKey: $ENCODED_PUBLIC_KEY EOF cat $SHARDING_CONFIG COMPOSE_FILE=docker-compose-sharding.yaml cat << EOF > $COMPOSE_FILE version: '3.4' services: rekor-server: build: context: . target: "deploy" command: [ "rekor-server", "serve", "--trillian_log_server.address=trillian-log-server", "--trillian_log_server.port=8090", "--redis_server.address=redis-server", "--redis_server.port=6379", "--rekor_server.address=0.0.0.0", "--rekor_server.signer=memory", "--enable_attestation_storage", "--attestation_storage_bucket=file:///var/run/attestations", "--trillian_log_server.tlog_id=$SHARD_TREE_ID", "--trillian_log_server.sharding_config=/$SHARDING_CONFIG" # Uncomment this for production logging # "--log_type=prod", ] volumes: - "/var/run/attestations:/var/run/attestations:z" - "./$SHARDING_CONFIG:/$SHARDING_CONFIG:z" restart: always # keep the server running ports: - "3000:3000" - "2112:2112" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/ping"] interval: 10s timeout: 3s retries: 3 start_period: 5s EOF # Spin up the new Rekor docker-compose -f $COMPOSE_FILE up -d waitForRekorServer $REKOR_CLI loginfo --rekor_server http://localhost:3000 # Make sure we are pointing to the new tree now TREE_ID=$($REKOR_CLI loginfo --rekor_server http://localhost:3000 --format json | jq -r .TreeID) # Check that the SHARD_TREE_ID is a substring of the `$REKOR_CLI loginfo` output stringsMatch $TREE_ID $SHARD_TREE_ID # Now, if we run $REKOR_CLI get --log_index 2 again, it should grab the log index # from Shard 0 check_log_index 2 # Add in a new entry to this shard pushd tests/sharding-testdata $REKOR_CLI upload --artifact file2 --signature file2.sig --pki-format=x509 --public-key=ec_public.pem --rekor_server http://localhost:3000 popd # Pass in the universal log_index & make sure it resolves check_log_index 3 # Make sure the shard tree size is 1 and the total tree size is 4 rm $HOME/.rekor/state.json # We have to remove this since we can't prove consistency between entry 0 and entry 1 TREE_SIZE=$($REKOR_CLI loginfo --rekor_server http://localhost:3000 --format json | jq -r .ActiveTreeSize) stringsMatch $TREE_SIZE "1" TOTAL_TREE_SIZE=$($REKOR_CLI loginfo --rekor_server http://localhost:3000 --format json | jq -r .TotalTreeSize) stringsMatch $TOTAL_TREE_SIZE "4" # Make sure we can still get logproof for the now-inactive shard $REKOR_CLI logproof --last-size 2 --tree-id $INITIAL_TREE_ID --rekor_server http://localhost:3000 # And the logproof for the now active shard $REKOR_CLI logproof --last-size 1 --rekor_server http://localhost:3000 echo "Getting public key for inactive shard..." GOT_PUB_KEY=$(curl "http://localhost:3000/api/v1/log/publicKey?treeID=$INITIAL_TREE_ID" | base64 -w 0) echo "Got encoded public key $GOT_PUB_KEY, making sure this matches the public key we got earlier..." stringsMatch $ENCODED_PUBLIC_KEY $GOT_PUB_KEY echo "Getting the public key for the active tree..." NEW_PUB_KEY=$(curl "http://localhost:3000/api/v1/log/publicKey" | base64 -w 0) echo "Making sure the public key for the active shard is different from the inactive shard..." if [[ "$ENCODED_PUBLIC_KEY" == "$NEW_PUB_KEY" ]]; then echo echo "Active tree public key should be different from inactive shard public key but isn't..." echo "Inactive Shard Public Key: $ENCODED_PUBLIC_KEY" echo "Active Shard Public Key: $NEW_PUB_KEY" exit 1 fi # TODO: Try to get the entry via Entry ID (Tree ID in hex + UUID) echo echo "Testing /api/v1/log/entries/retrieve endpoint..." ENTRY_ID_1=$($REKOR_CLI get --log-index 1 --rekor_server http://localhost:3000 --format json | jq -r .UUID) ENTRY_ID_2=$($REKOR_CLI get --log-index 3 --rekor_server http://localhost:3000 --format json | jq -r .UUID) # Make sure retrieve by UUID in the inactive shard works NUM_ELEMENTS=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"entryUUIDs\": [\"$ENTRY_ID_1\"]}" | jq '. | length') stringsMatch $NUM_ELEMENTS "1" # Make sure we can verify the entry we entered into the now-inactive shard pushd tests $REKOR_CLI verify --artifact test_file.txt --signature test_file.sig --public-key test_public_key.key --rekor_server http://localhost:3000 popd # -f makes sure we exit on failure NUM_ELEMENTS=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"entryUUIDs\": [\"$ENTRY_ID_1\", \"$ENTRY_ID_2\"]}" | jq '. | length') stringsMatch $NUM_ELEMENTS "2" # Make sure the /api/v1/log/entries/retrieve endpoint is resolving virtual indexes correctly NUM_ELEMENTS=$(curl -f -H "Content-Type: application/json" --data '{"logIndexes": [0,3]}' "http://localhost:3000/api/v1/log/entries/retrieve" | jq '. | length') stringsMatch $NUM_ELEMENTS "2" # Make sure we get the expected LogIndex in the response when calling /retrieve endpoint RETRIEVE_LOGINDEX1=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"logIndexes\": [1]}" | jq '.[0]' | jq -r .$UUID1.logIndex) stringsMatch $RETRIEVE_LOGINDEX1 "1" # Make sure that verification succeeds via UUID echo echo "Testing rekor-cli verification via UUID..." $REKOR_CLI verify --uuid $UUID1 --rekor_server http://localhost:3000 # Make sure that verification succeeds via Entry ID (Tree ID in hex + UUID) echo echo "Testing rekor-cli verification via Entry ID..." DEBUG=1 $REKOR_CLI verify --uuid $ENTRY_ID_1 --rekor_server http://localhost:3000 echo "Test passed successfully :)" rekor-1.3.5/tests/sharding-testdata/000077500000000000000000000000001455727245600174335ustar00rootroot00000000000000rekor-1.3.5/tests/sharding-testdata/ec_public.pem000066400000000000000000000002621455727245600220630ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMOcTfRBS9jiXM81FZ8gm/1+omeMw mn/347/556g/lriS72uMhY9LcT+5UJ6fGBglr5Z8L0JNSuasyed9OtaRvw== -----END PUBLIC KEY----- rekor-1.3.5/tests/sharding-testdata/file1000066400000000000000000000000061455727245600203520ustar00rootroot00000000000000file1 rekor-1.3.5/tests/sharding-testdata/file1.sig000066400000000000000000000001101455727245600211270ustar00rootroot000000000000000F!=И4Gyq]yG߿x<+!cleψ頁WU+EJOrekor-1.3.5/tests/sharding-testdata/file2000066400000000000000000000000071455727245600203540ustar00rootroot00000000000000file2 rekor-1.3.5/tests/sharding-testdata/file2.sig000066400000000000000000000001061455727245600211350ustar00rootroot000000000000000D F9/;O19!|+ɭ*b)03 7)t!Õ" ѷ3I7}Fǟ2rekor-1.3.5/tests/util.go000066400000000000000000000115131455727245600153320ustar00rootroot00000000000000// // Copyright 2021 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build e2e // +build e2e package e2e import ( "encoding/base64" "fmt" "io/ioutil" "math/rand" "os" "os/exec" "strings" "testing" "time" "github.com/sigstore/rekor/pkg/generated/models" ) const ( cli = "../rekor-cli" server = "../rekor-server" nodeDataDir = "node" ) func outputContains(t *testing.T, output, sub string) { t.Helper() if !strings.Contains(output, sub) { t.Errorf("Expected [%s] in response, got %s", sub, output) } } func run(t *testing.T, stdin, cmd string, arg ...string) string { t.Helper() // Coverage flag must be the first arg passed to coverage binary // No impact when running with regular binary arg = append([]string{coverageFlag()}, arg...) c := exec.Command(cmd, arg...) if stdin != "" { c.Stdin = strings.NewReader(stdin) } if os.Getenv("REKORTMPDIR") != "" { // ensure that we use a clean state.json file for each run c.Env = append(c.Env, "HOME="+os.Getenv("REKORTMPDIR")) } b, err := c.CombinedOutput() if err != nil { t.Log(string(b)) t.Fatal(err) } return stripCoverageOutput(string(b)) } func runCli(t *testing.T, arg ...string) string { t.Helper() arg = append(arg, rekorServerFlag()) // use a blank config file to ensure no collision if os.Getenv("REKORTMPDIR") != "" { arg = append(arg, "--config="+os.Getenv("REKORTMPDIR")+".rekor.yaml") } return run(t, "", cli, arg...) } func runCliStdout(t *testing.T, arg ...string) string { t.Helper() // Coverage flag must be the first arg passed to coverage binary // No impact when running with regular binary arg = append([]string{coverageFlag()}, arg...) arg = append(arg, rekorServerFlag()) c := exec.Command(cli, arg...) if os.Getenv("REKORTMPDIR") != "" { // ensure that we use a clean state.json file for each run c.Env = append(c.Env, "HOME="+os.Getenv("REKORTMPDIR")) } b, err := c.Output() if err != nil { t.Log(string(b)) t.Fatal(err) } return stripCoverageOutput(string(b)) } func runCliErr(t *testing.T, arg ...string) string { t.Helper() // Coverage flag must be the first arg passed to coverage binary // No impact when running with regular binary arg = append([]string{coverageFlag()}, arg...) arg = append(arg, rekorServerFlag()) // use a blank config file to ensure no collision if os.Getenv("REKORTMPDIR") != "" { arg = append(arg, "--config="+os.Getenv("REKORTMPDIR")+".rekor.yaml") } cmd := exec.Command(cli, arg...) b, err := cmd.CombinedOutput() if err == nil { t.Log(string(b)) t.Fatalf("expected error, got %s", string(b)) } return stripCoverageOutput(string(b)) } func rekorServerFlag() string { return fmt.Sprintf("--rekor_server=%s", rekorServer()) } func rekorServer() string { if s := os.Getenv("REKOR_SERVER"); s != "" { return s } return "http://localhost:3000" } func coverageFlag() string { return "-test.coverprofile=/tmp/rekor-cli." + randomSuffix(8) + ".cov" } func stripCoverageOutput(out string) string { return strings.Split(strings.Split(out, "PASS")[0], "FAIL")[0] } func readFile(t *testing.T, p string) string { b, err := ioutil.ReadFile(p) if err != nil { t.Fatal(err) } return strings.TrimSpace(string(b)) } func randomSuffix(n int) string { const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" b := make([]byte, n) for i := range b { b[i] = letterBytes[rand.Intn(len(letterBytes))] } return string(b) } func randomData(t *testing.T, n int) []byte { t.Helper() rand.Seed(time.Now().UnixNano()) data := make([]byte, n) if _, err := rand.Read(data[:]); err != nil { t.Fatal(err) } return data } func createArtifact(t *testing.T, artifactPath string) string { t.Helper() // First let's generate some random data so we don't have to worry about dupes. data := randomData(t, 100) artifact := base64.StdEncoding.EncodeToString(data[:]) // Write this to a file write(t, artifact, artifactPath) return artifact } func extractLogEntry(t *testing.T, le models.LogEntry) models.LogEntryAnon { t.Helper() if len(le) != 1 { t.Fatal("expected length to be 1, is actually", len(le)) } for _, v := range le { return v } // this should never happen return models.LogEntryAnon{} } func write(t *testing.T, data string, path string) { t.Helper() if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil { t.Fatal(err) } } rekor-1.3.5/types.md000066400000000000000000000231131455727245600143510ustar00rootroot00000000000000# Signing and Uploading Other Types This documentation contains information on how to sign and upload data in different pluggable types. The following are covered: - [Minisign](#minisign) - [SSH](#ssh) - [PKIX/X509](#pkixx509) - OpenPGP / GPG (TODO) - RPM (TODO) - TSR (TODO) - [TUF](#tuf) ## Minisign Create a keypair with something like: ```console $ minisign -G Please enter a password to protect the secret key. Password: Password (one more time): Deriving a key from the password in order to encrypt the secret key... done The secret key was saved as /Users/dlorenc/.minisign/minisign.key - Keep it secret! The public key was saved as minisign.pub - That one can be public. Files signed using this key pair can be verified with the following command: minisign -Vm -P RWSzQI7+S6M0c4yReOwcDZ2petL8pAZsrNfkdyqr0V7j/HGafpjdKZQm ``` Sign a file: ```console $ minisign -S -m README.md Password: Deriving a key from the password and decrypting the secret key... done ``` Upload to rekor: ```console $ rekor-cli upload --artifact README.md --signature README.md.minisig --pki-format=minisign --public-key=minisign.pub Created entry at index 5895, available at: https://rekor.sigstore.dev/api/v1/log/entries/008bfbbaa8f473a0b17cba5f8078d2c08410bca55f01d2ec71860795ef823b36 ``` Look at the entry with: ```console $ ./rekor-cli get --uuid=008bfbbaa8f473a0b17cba5f8078d2c08410bca55f01d2ec71860795ef823b36 LogID: c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d Index: 5895 IntegratedTime: 2021-07-14T01:39:50Z UUID: 008bfbbaa8f473a0b17cba5f8078d2c08410bca55f01d2ec71860795ef823b36 Body: { "RekordObj": { "data": { "hash": { "algorithm": "sha256", "value": "3d80236772ca7c5405e398a4d685e715859260a8733070b86de7322e233c68d2" } }, "signature": { "content": "dW50cnVzdGVkIGNvbW1lbnQ6ClJXU3pRSTcrUzZNMGMrNUcxbVZzcmw2dmgvYi91VjlxclJySWpxd21abDFKYjZhTGJ2U1NWUzdObDNvUmpVTUdHUWpLVlEyd2JnMnJxNDZxdDdPTHE3L1c3Z2liMlo5Rzh3az0=", "format": "minisign", "publicKey": { "content": "akpGNDdCd05uYWw2MHZ5a0JteXMxK1IzS3F2Ulh1UDhjWnArbU4wcGxDWT0=" } } } } ``` ## SSH Generate a keypair with: ```console $ ssh-keygen -C test@rekor.dev -t ed25519 -f id_ed25519 Generating public/private ed25519 key pair. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in id_ed25519. Your public key has been saved in id_ed25519.pub. The key fingerprint is: SHA256:73u0etmm2h7BehcLbjrwXqXe193k5R5Uz0Lnl83nTt4 test@rekor.dev The key's randomart image is: +--[ED25519 256]--+ | | | | | . o| | . . ==| | S + +oO| | .. o.=.==| | oo.B+o=B| | .oB=+o+X| | .BO=o.oE| +----[SHA256]-----+ ``` Sign a file with: ```console $ ssh-keygen -Y sign -n file -f id_ed25519 README.md Enter passphrase: Signing file README.md Write signature to README.md.sig ``` Upload it to rekor with: ```console $ rekor-cli upload --artifact README.md --signature README.md.sig --pki-format=ssh --public-key=id_ed25519.pub Created entry at index 5896, available at: https://rekor.sigstore.dev/api/v1/log/entries/0e81b4d9299e2609e45b5c453a4c0e7820ac74e02c4935a8b830d104632fd2d ``` Look at the entry with: ```console $ rekor-cli get --uuid=0e81b4d9299e2609e45b5c453a4c0e7820ac74e02c4935a8b830d104632fd2d1 LogID: c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d Index: 5896 IntegratedTime: 2021-07-14T01:45:06Z UUID: 0e81b4d9299e2609e45b5c453a4c0e7820ac74e02c4935a8b830d104632fd2d1 Body: { "RekordObj": { "data": { "hash": { "algorithm": "sha256", "value": "3d80236772ca7c5405e398a4d685e715859260a8733070b86de7322e233c68d2" } }, "signature": { "content": "LS0tLS1CRUdJTiBTU0ggU0lHTkFUVVJFLS0tLS0KVTFOSVUwbEhBQUFBQVFBQUFETUFBQUFMYzNOb0xXVmtNalUxTVRrQUFBQWdqNnhOWHFWdFJQb2JOaHg5TXNnbQp4Q2lYMlo3VFh5QXcyRHZpN0k1Nzdia0FBQUFFWm1sc1pRQUFBQUFBQUFBR2MyaGhOVEV5QUFBQVV3QUFBQXR6CmMyZ3RaV1F5TlRVeE9RQUFBRUM1N2xCUGtjWlF2K2RDOG1HMEd4ajZoeUVXOUtPZVVtN21WdFVicURSTDdramoKS1pTakYxaVFVcWVpUVQ4Z2ZKbGVyZVhhUmVMamZoR2FUN0llRENrRQotLS0tLUVORCBTU0ggU0lHTkFUVVJFLS0tLS0K", "format": "ssh", "publicKey": { "content": "c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSUkrc1RWNmxiVVQ2R3pZY2ZUTElKc1FvbDltZTAxOGdNTmc3NHV5T2UrMjUK" } } } } ``` ## PKIX/X509 Generate a keypair with: ```console $ openssl ecparam -genkey -name prime256v1 > ec_private.pem $ openssl ec -in ec_private.pem -pubout > ec_public.pem read EC key writing EC key ``` Sign the file with: ```console $ openssl dgst -sha256 -sign ec_private.pem -out README.md.sig README.md ``` Upload it to rekor with: ```console $ ./rekor-cli upload --artifact README.md --signature README.md.sig --pki-format=x509 --public-key=ec_public.pem Created entry at index 5897, available at: https://rekor.sigstore.dev/api/v1/log/entries/31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d ``` View the entry with: ```console $ rekor-cli get --uuid=31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d LogID: c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d Index: 5897 IntegratedTime: 2021-07-14T01:49:54Z UUID: 31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d Body: { "RekordObj": { "data": { "hash": { "algorithm": "sha256", "value": "3d80236772ca7c5405e398a4d685e715859260a8733070b86de7322e233c68d2" } }, "signature": { "content": "MEUCICwZpVU/3fnWSZkejA8R2j/t5futtl5Co3CDj7k6J6PwAiEA75Cn2txgpg/KjsOitSKsydL3D6cQIf7NQJtsmvsRTRQ=", "format": "x509", "publicKey": { "content": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFYzJKUkJZbS9OQVo5ZHhhUnNWV05mdTcxV3B5TAo2cGx4L1hsZnNVTlM2SmcrWEhEVmpsaVNBNHV2ZEQ4ZW5XdUhNdWQybS9WdEVQaDZYT0M3bjR0aCtnPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==" } } } } ``` ## OpenPGP / GPG TODO ## RPM TODO ## Alpine TODO ## RPM TODO ## TSR TODO ## TUF Generate a TUF repository (for example, with the [Python reference implementation](https://pypi.org/project/tuf/) or [go-tuf](https://github.com/theupdateframework/go-tuf)). With go-tuf: ```console $ tuf init $ tuf gen-key root $ tuf gen-key targets $ tuf gen-key snapshot $ tuf gen-key timestamp $ tuf add path/to/some/target.txt $ tuf snapshot $ tuf timestamp $ tuf commit ``` You will find the signed metadata in your TUF `repository/` directory: ```console $ tree . . ├── keys │ ├── snapshot.json │ ├── targets.json │ └── timestamp.json ├── repository │ ├── root.json │ ├── snapshot.json │ ├── targets │ │ └── foo │ │ └── bar │ │ └── baz.txt │ ├── targets.json │ └── timestamp.json └── staged ``` Upload any TUF manifest to rekor by using the `root.json` as a the public key: ```console $ ./rekor-cli upload --artifact repository/timestamp.json --type tuf --public-key repository/root.json Created entry at index 0, available at: https://rekor.sigstore.dev/api/v1/log/entries/6ed8fa5e9f0aa31b6cdfd2cc6877692f5afba52edd7ff5774eebfb22228e8847 ``` View the entry with: ```console $ rekor-cli get --uuid=31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d LogID: 5c4ceffb024e0d0b50bb9e03bc308ce83a76353f1003f8e57a21c51f74cc1e0e Index: 0 IntegratedTime: 2021-08-13T19:17:33Z UUID: 6ed8fa5e9f0aa31b6cdfd2cc6877692f5afba52edd7ff5774eebfb22228e8847 Body: { "TufObj": { "manifest": { "expires": "2021-12-18 13:28:12.99008 -0600 CST", "signed": { "content": [...] }, "version": 1 }, "root": { "expires": "2021-12-18 13:28:12.99008 -0600 CST", "signed": { "content": [...] }, "version": 1 } } } ``` ## Hashed rekord This is similar to a rekord type, but allows hashed data instead of supplying the full content that was signed. This is suitable for uploading signatures on large payloads. This is only compatible with x509 / PKIX signature types. Generate a keypair with: ```console $ openssl ecparam -genkey -name prime256v1 > ec_private.pem $ openssl ec -in ec_private.pem -pubout > ec_public.pem read EC key writing EC key ``` Sign the file with: ```console $ openssl dgst -sha256 -sign ec_private.pem -out README.md.sig README.md ``` Upload it to rekor with: ```console $ ./rekor-cli upload --type hashedrekord:0.0.1 --artifact-hash $(sha256sum README.md | awk '{print $1}') --signature README.md.sig --pki-format=x509 --public-key=ec_public.pem Created entry at index 12, available at: https://rekor.sigstore.dev/api/v1/log/entries/31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d ``` View the entry with: ```console $ rekor-cli get --uuid=31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d LogID: b3e217db795022552080ed8b22596131c63f3aa198e83450f3dba9e686633641 Index: 12 IntegratedTime: 2021-11-17T21:59:49Z UUID: 31a51c1bc20da83b66b2f24899184b85dbf8261c2de8571479165619ad87cd5d Body: { "HashedRekordObj": { "data": { "hash": { "algorithm": "sha256", "value": "9249e5dfa2ede1c5bd89c49bf9beaf3e9afda2d961dea7cda7f639210179cd16" } }, "signature": { "content": "MEQCIG9s7GVWH67OkeXPQvM/XAcLW7N0xiFZWez95uR+GlXyAiBW+DPRaYvgtpQglQLtqujwb+xQBd8I70Vk/2vDB+G3uQ==", "publicKey": { "content": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFR0JPTVdDanViVTVldkJ0OGcxWTZTR1ZoZ29OVwpjY2lrbHlpTEJQajQ5Um40WVFhTjRJS0xySi9nQlROU2tOREdQbHFvNHVjTVg3L21PZmlBNkVHS09BPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==" } } } } ```