pax_global_header00006660000000000000000000000064147747752170014537gustar00rootroot0000000000000052 comment=9699214f329055f01c1c685556eb725120e4a32f sigstore-go-0.7.1/000077500000000000000000000000001477477521700140065ustar00rootroot00000000000000sigstore-go-0.7.1/.github/000077500000000000000000000000001477477521700153465ustar00rootroot00000000000000sigstore-go-0.7.1/.github/dependabot.yml000066400000000000000000000017051477477521700202010ustar00rootroot00000000000000# # 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. version: 2 updates: - package-ecosystem: gomod directories: - "**/*" schedule: interval: "weekly" open-pull-requests-limit: 10 groups: minor-patch: update-types: - "minor" - "patch" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" open-pull-requests-limit: 10 sigstore-go-0.7.1/.github/workflows/000077500000000000000000000000001477477521700174035ustar00rootroot00000000000000sigstore-go-0.7.1/.github/workflows/build.yml000066400000000000000000000024611477477521700212300ustar00rootroot00000000000000# 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. name: "Build and test" on: push: branches: [main] pull_request: {} permissions: contents: read jobs: build: strategy: fail-fast: false matrix: os: [macos-latest, ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 fetch-tags: true persist-credentials: false - name: Install Go uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: go.mod - name: Build binaries run: make all - name: Run tests run: make test sigstore-go-0.7.1/.github/workflows/codeql.yml000066400000000000000000000037011477477521700213760ustar00rootroot00000000000000# 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. # https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#changing-the-languages-that-are-analyzed name: CodeQL on: push: branches: [ main ] pull_request: # The branches below must be a subset of the branches above branches: [ main ] schedule: - cron: '45 10 * * 1' permissions: {} jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'go', 'actions' ] steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - name: Setup Go uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: ./go.mod # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@0701025a8b1600e416be4f3bb5a830b1aa6af01e # v3.28.6 with: languages: ${{ matrix.language }} - name: Autobuild uses: github/codeql-action/autobuild@0701025a8b1600e416be4f3bb5a830b1aa6af01e # v3.28.6 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@0701025a8b1600e416be4f3bb5a830b1aa6af01e # v3.28.6 sigstore-go-0.7.1/.github/workflows/conformance.yml000066400000000000000000000027671477477521700224340ustar00rootroot00000000000000# 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. name: Conformance Tests on: workflow_dispatch: push: branches: - main pull_request: branches: - main permissions: contents: read jobs: conformance: runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: go.mod - run: go mod download - run: go build -o conformance test/conformance/main.go - uses: sigstore/sigstore-conformance@640e7dfb715518eeeb492910c6d244cedcc6cfea # v0.0.17 with: entrypoint: ${{ github.workspace }}/conformance - uses: sigstore/sigstore-conformance@640e7dfb715518eeeb492910c6d244cedcc6cfea # v0.0.17 with: entrypoint: ${{ github.workspace }}/conformance environment: staging sigstore-go-0.7.1/.github/workflows/depsreview.yml000066400000000000000000000014571477477521700223120ustar00rootroot00000000000000# # 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. 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@main sigstore-go-0.7.1/.github/workflows/golangci-lint.yml000066400000000000000000000026151477477521700226610ustar00rootroot00000000000000# 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. name: golangci-lint on: push: branches: - main pull_request: permissions: contents: read jobs: golangci: name: lint runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: './go.mod' check-latest: true - name: golangci-lint uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v7.0.0 with: version: v2.0 args: --verbose # sometimes the pkg cache gets corrupted, skipping cache avoids this # https://github.com/golangci/golangci-lint-action/issues/23 skip-cache: true sigstore-go-0.7.1/.github/workflows/scorecard.yml000066400000000000000000000031621477477521700220750ustar00rootroot00000000000000# 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. name: Scorecards supply-chain security on: # (Optional) For Branch-Protection check. Only the default branch is supported. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection # branch_protection_rule: # To guarantee Maintained check is occasionally updated. See # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained schedule: # Weekly on Saturdays. - cron: '30 1 * * 6' push: branches: [ main ] # Disable all default permissions. permissions: {} jobs: analysis: name: Scorecards analysis permissions: # Needed to upload the results to code-scanning dashboard. security-events: write # Needed to publish results and get a badge (see publish_results below). id-token: write uses: sigstore/community/.github/workflows/reusable-scorecard.yml@main # (Optional) Disable publish results: # with: # publish_results: false # (Optional) Enable Branch-Protection check: # secrets: # scorecard_token: ${{ secrets.SCORECARD_TOKEN }} sigstore-go-0.7.1/.github/workflows/verify_license.yml000066400000000000000000000024271477477521700231410ustar00rootroot00000000000000# 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. name: Verify on: [push, pull_request] permissions: contents: read jobs: license-check: name: license boilerplate check runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version-file: './go.mod' check-latest: true - name: Install addlicense run: go install github.com/google/addlicense@v1.1.1 - name: Check license headers run: | set -e addlicense -check -l apache -c 'The Sigstore Authors' -ignore "third_party/**" -v * sigstore-go-0.7.1/.gitignore000066400000000000000000000003701477477521700157760ustar00rootroot00000000000000.idea .DS_Store *~ /sigstore-go /tufdata /conformance /pkg/tuf/testing.local.json /examples/oci-image-verification/oci-image-verification /examples/sigstore-go-signing/sigstore-go-signing /examples/sigstore-go-verification/sigstore-go-verification sigstore-go-0.7.1/.golangci.yaml000066400000000000000000000021521477477521700165330ustar00rootroot00000000000000# 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. version: "2" linters: enable: - errorlint - goconst - gocritic - gosec - misspell - revive - tagliatelle - unused - whitespace disable: - tagalign exclusions: generated: lax presets: - comments - common-false-positives - legacy - std-error-handling paths: - third_party$ - builtin$ - examples$ formatters: enable: - gofmt - goimports exclusions: generated: lax paths: - third_party$ - builtin$ - examples$ sigstore-go-0.7.1/CODEOWNERS000066400000000000000000000000431477477521700153760ustar00rootroot00000000000000* @sigstore/sigstore-go-codeowners sigstore-go-0.7.1/CODE_OF_CONDUCT.md000066400000000000000000000062071477477521700166120ustar00rootroot00000000000000# 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/ sigstore-go-0.7.1/CONTRIBUTING.md000066400000000000000000000103661477477521700162450ustar00rootroot00000000000000# Contributing When contributing to a repository in the Sigstore organization, please first discuss the change you wish to make via an issue in the repository. ## Pull Request Process 1. Create an issue in the repository outlining the fix or feature. 2. Fork the repository to your own GitHub account and clone it locally. 3. Complete and test the change. 4. If relevant, update documentation with details of the change. This includes updates to an API, 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. Sign off your commit. 7. Ensure that CI passes. If it fails, fix the failures. 8. Every pull request requires a review from the Sigstore subprojects MAINTAINERS. 9. If your pull request consists of more than one commit, please squash your commits as described in [Squash Commits](#Squash Commits), or the commits will be squashed on merge. ## 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 Sigstore 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/community/blob/main/CODE_OF_CONDUCT.md) document. sigstore-go-0.7.1/COPYRIGHT.txt000066400000000000000000000010621477477521700161160ustar00rootroot00000000000000Copyright 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. sigstore-go-0.7.1/LICENSE000066400000000000000000000261351477477521700150220ustar00rootroot00000000000000 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. sigstore-go-0.7.1/Makefile000066400000000000000000000017341477477521700154530ustar00rootroot00000000000000# 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. .PHONY: all all: build-examples .PHONY: build-examples build-examples: go build -C ./examples/oci-image-verification -o oci-image-verification . go build -C ./examples/sigstore-go-signing -o sigstore-go-signing . go build -C ./examples/sigstore-go-verification -o sigstore-go-verification . .PHONY: test test: go test ./... .PHONY: tidy tidy: go mod tidy .PHONY: lint lint: golangci-lint run sigstore-go-0.7.1/README.md000066400000000000000000000070241477477521700152700ustar00rootroot00000000000000# sigstore-go A client library for [Sigstore](https://www.sigstore.dev/), written in Go. [![Go Reference](https://pkg.go.dev/badge/github.com/sigstore/sigstore-go.svg)](https://pkg.go.dev/github.com/sigstore/sigstore-go) [![Go Report Card](https://goreportcard.com/badge/github.com/sigstore/sigstore-go)](https://goreportcard.com/report/github.com/sigstore/sigstore-go) [![e2e-tests](https://github.com/sigstore/sigstore-go/actions/workflows/build.yml/badge.svg)](https://github.com/sigstore/sigstore-go/actions/workflows/build.yml) Features: - Signing and verification of [Sigstore bundles](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto) compliant with Sigstore Client Spec - Verification of raw Sigstore signatures by creating bundles for them (see [conformance tests](test/conformance/main.go) for example) - Signing and verifying with a Timestamp Authority (TSA) - Signing and verifying (offline or online) with Rekor (Artifact Transparency Log) - Structured verification results including certificate metadata - TUF support - Verification support for custom [trusted root](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_trustroot.proto) - Examples for signing and verifying artifacts There is not built-in support for signing with a KMS or other bring-your-own-key; however you can easily add support by implementing your own version of the interface `pkg/sign/keys.go:Keypair`. ## Background Sigstore already has a canonical Go client implementation, [cosign](https://github.com/sigstore/cosign), which was developed with a focus on container image signing/verification. It has a rich CLI and a long legacy of features and development. `sigstore-go` is a more minimal and friendly API for integrating Go code with Sigstore, with a focus on the newly specified data structures in [sigstore/protobuf-specs](https://github.com/sigstore/protobuf-specs). `sigstore-go` attempts to minimize the dependency tree for simple signing and verification tasks, omitting KMS support and container image verification, and we intend to refactor parts of `cosign` to depend on `sigstore-go`. ## Status `sigstore-go` is currently beta, and may have minor API changes before the 1.0.0 release. It does however pass the [`sigstore-conformance`](https://github.com/sigstore/sigstore-conformance) signing and verification test suite, and correctness is taken very seriously. ## Documentation and examples Documentation is found in the [`docs`](./docs) subdirectory and on [pkg.go.dev](https://pkg.go.dev/github.com/sigstore/sigstore-go). See the [examples directory](./examples/README.md) for examples of how to use this library. Note that the CLI examples are to demonstrate how to use the library, and not intended as a fully-featured Sigstore CLI like [cosign](https://github.com/sigstore/cosign). ## Requirements Tested with: - Unix-compatible OS and Windows - [Go 1.23](https://go.dev/doc/install) Note that we do not provide built versions of this library, but you can see what architectures your version of `go` supports with `go tool dist list`. ## Testing Tests are invoked using the standard Go testing framework. A helper exists in the Makefile also. ```shell $ make test ``` ## Example bundles ### examples/bundle-provenance.json This came from https://www.npmjs.com/package/sigstore/v/1.3.0/provenance, with the outermost "bundle" key stripped off. ## Support Bug reports are welcome via issues and questions are welcome via discussion. Please refer to [SUPPORT.md](./SUPPORT.md) for details. This project is provided as-is. sigstore-go-0.7.1/SUPPORT.md000066400000000000000000000010131477477521700154770ustar00rootroot00000000000000# Support ## How to file issues and get help This project uses GitHub issues to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new issue. For help or questions about using this project, please use discussions. `sigstore-go` is under active development and maintained by the Sigstore community. We will do our best to respond to support, feature requests, and community questions in a timely manner. sigstore-go-0.7.1/docs/000077500000000000000000000000001477477521700147365ustar00rootroot00000000000000sigstore-go-0.7.1/docs/oci-image-verification.md000066400000000000000000000303331477477521700215740ustar00rootroot00000000000000# Example of OCI image verification using `sigstore-go` This document will walk through using the `sigstore-go` library to verify an OCI image reference. ## Disclaimer This is an example of how to use the `sigstore-go` library to verify an OCI image reference and is not intended to replace cosign and/or be reused in production. The `sigstore-go` library is still in development and is subject to change. The following issue tracks the cosign support of the protobuf bundle format - [#3139](https://github.com/sigstore/cosign/issues/3139). ## Overview In this example, we'll use the `sigstore-go` library to verify an OCI image reference. This simple CLI is a wrapper around the Go API with the difference that it constructs a [Sigstore bundle](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto) from the OCI image reference metadata as a prerequisite to the verification process. ## Requirements - Unix-compatible OS - [Go 1.22](https://go.dev/doc/install) ## Getting started Clone this repository and navigate to the `examples/oci-image-verification` directory: ```shell $ go build . ``` ## Trusted Root The verifier allows you to use the Sigstore Public Good TUF root or your own custom [trusted root](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_trustroot.proto) containing the root/intermediate certificates of the Fulcio/TSA/Rekor instances used to sign the bundle, in order to verify common open source bundles or bundles signed by your own private Sigstore instance. ## Verification Process The image we are going to verify is `ghcr.io/stacklok/minder/server:latest` ```shell $ ./oci-image-verification --ociImage="ghcr.io/stacklok/minder/server:latest" \ --tufRootURL="tuf-repo-cdn.sigstore.dev" \ --expectedSANRegex="^https://github.com/stacklok/minder/" \ --expectedIssuer="https://token.actions.githubusercontent.com" ``` Upon successful verification, the CLI will print the verification result in JSON format along with a `Verification successful!` message. Below is an example of the bundle that was constructed for this image(at that point of time) followed by the successful verification result, serialized as JSON: ```json { "mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1", "verificationMaterial": { "x509CertificateChain": { "certificates": [ { "rawBytes": "MIIGtDCCBjugAwIBAgIUbd4ghtzt4FI69XTWFG2jdRhQgxcwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjMxMTI4MjEwNzA4WhcNMjMxMTI4MjExNzA4WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYPWRO615alYr3u0gSiiL056ZykQQBOe3OUmyoIpOwMpXoPN8zTn5T6OAqFirfg2n9zSwQBD4eXqFXemzR7I/oaOCBVowggVWMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUllhfm6BoyqDLHMEBgQrVW4pFnUwwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wZAYDVR0RAQH/BFowWIZWaHR0cHM6Ly9naXRodWIuY29tL3N0YWNrbG9rL21pbmRlci8uZ2l0aHViL3dvcmtmbG93cy9jaGFydC1wdWJsaXNoLnltbEByZWZzL2hlYWRzL21haW4wOQYKKwYBBAGDvzABAQQraHR0cHM6Ly90b2tlbi5hY3Rpb25zLmdpdGh1YnVzZXJjb250ZW50LmNvbTASBgorBgEEAYO/MAECBARwdXNoMDYGCisGAQQBg78wAQMEKDZkYzZjNmMyNzE4NGY5MTliYTZjYTI1OGUwNjRiZDdkZDE4ZTkyMDAwIAYKKwYBBAGDvzABBAQSUHVibGlzaCBIZWxtIENoYXJ0MB0GCisGAQQBg78wAQUED3N0YWNrbG9rL21pbmRlcjAdBgorBgEEAYO/MAEGBA9yZWZzL2hlYWRzL21haW4wOwYKKwYBBAGDvzABCAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMGYGCisGAQQBg78wAQkEWAxWaHR0cHM6Ly9naXRodWIuY29tL3N0YWNrbG9rL21pbmRlci8uZ2l0aHViL3dvcmtmbG93cy9jaGFydC1wdWJsaXNoLnltbEByZWZzL2hlYWRzL21haW4wOAYKKwYBBAGDvzABCgQqDCg2ZGM2YzZjMjcxODRmOTE5YmE2Y2EyNThlMDY0YmQ3ZGQxOGU5MjAwMB0GCisGAQQBg78wAQsEDwwNZ2l0aHViLWhvc3RlZDAyBgorBgEEAYO/MAEMBCQMImh0dHBzOi8vZ2l0aHViLmNvbS9zdGFja2xvay9taW5kZXIwOAYKKwYBBAGDvzABDQQqDCg2ZGM2YzZjMjcxODRmOTE5YmE2Y2EyNThlMDY0YmQ3ZGQxOGU5MjAwMB8GCisGAQQBg78wAQ4EEQwPcmVmcy9oZWFkcy9tYWluMBkGCisGAQQBg78wAQ8ECwwJNjI0MDU2NTU4MCsGCisGAQQBg78wARAEHQwbaHR0cHM6Ly9naXRodWIuY29tL3N0YWNrbG9rMBkGCisGAQQBg78wAREECwwJMTEwMjM3NzQ2MGYGCisGAQQBg78wARIEWAxWaHR0cHM6Ly9naXRodWIuY29tL3N0YWNrbG9rL21pbmRlci8uZ2l0aHViL3dvcmtmbG93cy9jaGFydC1wdWJsaXNoLnltbEByZWZzL2hlYWRzL21haW4wOAYKKwYBBAGDvzABEwQqDCg2ZGM2YzZjMjcxODRmOTE5YmE2Y2EyNThlMDY0YmQ3ZGQxOGU5MjAwMBQGCisGAQQBg78wARQEBgwEcHVzaDBVBgorBgEEAYO/MAEVBEcMRWh0dHBzOi8vZ2l0aHViLmNvbS9zdGFja2xvay9taW5kZXIvYWN0aW9ucy9ydW5zLzcwMjQ1MDI0ODAvYXR0ZW1wdHMvMTAWBgorBgEEAYO/MAEWBAgMBnB1YmxpYzCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABjBfB1dwAAAQDAEcwRQIhAPOQe9Jb1gH0c5q/lpjztpyrN2P4Zm+xHExfH/mHUoOHAiBgClsjZq4aRMwu8N7bQp07bdir+skM9pMTuxQ/fbkMCTAKBggqhkjOPQQDAwNnADBkAjA7yRocfz9xNVKNXadkL5pXc453uaerc/Y7hrtVkJvI2mXFXl42BWCck3sVPHqvvtACMD/vOb3bWqGT5yTLSbtpXxBNncrp2o0KR12c1c7v5mhtf9UdPo1E3LIAYlqpOoj3QQ==" } ] }, "tlogEntries": [ { "logIndex": "53194260", "logId": { "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" }, "kindVersion": { "kind": "hashedrekord", "version": "0.0.1" }, "integratedTime": "1701205628", "inclusionPromise": { "signedEntryTimestamp": "MEYCIQCOvYr8ezbNfJMGw0CIT4krwy2fSnIVMUfWJ4Xjn7ZsOQIhAMROYirqcbs76Y4B4I/wDlqdDavbx3OB6/YPezB46npD" }, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJhOWQzZWE4Mzk3OWJmYTM1MjJkNTkwNjRjMzI0NzNlZDNiYWM4OGY2MmMzNmIzZGQyNDNmZWZlOTVkZWM2ZGEwIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJR1RkZnIwbytoQWlMTEZzTkNNR1ZoRnF2aTRYNm52V290amZwc1NPaThxakFpQmloMC9FVjhYckRnMWpiUTVyK2R5Y080Wi94a0hWbGg1VUZSWThnQ2ZzdGc9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVZDBSRU5EUW1wMVowRjNTVUpCWjBsVlltUTBaMmgwZW5RMFJrazJPVmhVVjBaSE1tcGtVbWhSWjNoamQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcE5lRTFVU1RSTmFrVjNUbnBCTkZkb1kwNU5hazE0VFZSSk5FMXFSWGhPZWtFMFYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZaVUZkU1R6WXhOV0ZzV1hJemRUQm5VMmxwVERBMU5scDVhMUZSUWs5bE0wOVZiWGtLYjBsd1QzZE5jRmh2VUU0NGVsUnVOVlEyVDBGeFJtbHlabWN5YmpsNlUzZFJRa1EwWlZoeFJsaGxiWHBTTjBrdmIyRlBRMEpXYjNkbloxWlhUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZzYkdobUNtMDJRbTk1Y1VSTVNFMUZRbWRSY2xaWE5IQkdibFYzZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDFwQldVUldVakJTUVZGSUwwSkdiM2RYU1ZwWFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVEROT01GbFhUbkppUnpseVRESXhjQXBpYlZKc1kyazRkVm95YkRCaFNGWnBURE5rZG1OdGRHMWlSemt6WTNrNWFtRkhSbmxrUXpGM1pGZEtjMkZZVG05TWJteDBZa1ZDZVZwWFducE1NbWhzQ2xsWFVucE1NakZvWVZjMGQwOVJXVXRMZDFsQ1FrRkhSSFo2UVVKQlVWRnlZVWhTTUdOSVRUWk1lVGt3WWpKMGJHSnBOV2haTTFKd1lqSTFla3h0WkhBS1pFZG9NVmx1Vm5wYVdFcHFZakkxTUZwWE5UQk1iVTUyWWxSQlUwSm5iM0pDWjBWRlFWbFBMMDFCUlVOQ1FWSjNaRmhPYjAxRVdVZERhWE5IUVZGUlFncG5OemgzUVZGTlJVdEVXbXRaZWxwcVRtMU5lVTU2UlRST1IxazFUVlJzYVZsVVdtcFpWRWt4VDBkVmQwNXFVbWxhUkdScldrUkZORnBVYTNsTlJFRjNDa2xCV1V0TGQxbENRa0ZIUkhaNlFVSkNRVkZUVlVoV2FXSkhiSHBoUTBKSldsZDRkRWxGVG05WldFb3dUVUl3UjBOcGMwZEJVVkZDWnpjNGQwRlJWVVVLUkROT01GbFhUbkppUnpseVRESXhjR0p0VW14amFrRmtRbWR2Y2tKblJVVkJXVTh2VFVGRlIwSkJPWGxhVjFwNlRESm9iRmxYVW5wTU1qRm9ZVmMwZHdwUGQxbExTM2RaUWtKQlIwUjJla0ZDUTBGUmRFUkRkRzlrU0ZKM1kzcHZka3d6VW5aaE1sWjFURzFHYW1SSGJIWmliazExV2pKc01HRklWbWxrV0U1c0NtTnRUblppYmxKc1ltNVJkVmt5T1hSTlIxbEhRMmx6UjBGUlVVSm5OemgzUVZGclJWZEJlRmRoU0ZJd1kwaE5Oa3g1T1c1aFdGSnZaRmRKZFZreU9YUUtURE5PTUZsWFRuSmlSemx5VERJeGNHSnRVbXhqYVRoMVdqSnNNR0ZJVm1sTU0yUjJZMjEwYldKSE9UTmplVGxxWVVkR2VXUkRNWGRrVjBwellWaE9id3BNYm14MFlrVkNlVnBYV25wTU1taHNXVmRTZWt3eU1XaGhWelIzVDBGWlMwdDNXVUpDUVVkRWRucEJRa05uVVhGRVEyY3lXa2ROTWxsNldtcE5hbU40Q2s5RVVtMVBWRVUxV1cxRk1sa3lSWGxPVkdoc1RVUlpNRmx0VVROYVIxRjRUMGRWTlUxcVFYZE5RakJIUTJselIwRlJVVUpuTnpoM1FWRnpSVVIzZDA0S1dqSnNNR0ZJVm1sTVYyaDJZek5TYkZwRVFYbENaMjl5UW1kRlJVRlpUeTlOUVVWTlFrTlJUVWx0YURCa1NFSjZUMms0ZGxveWJEQmhTRlpwVEcxT2RncGlVemw2WkVkR2FtRXllSFpoZVRsMFlWYzFhMXBZU1hkUFFWbExTM2RaUWtKQlIwUjJla0ZDUkZGUmNVUkRaekphUjAweVdYcGFhazFxWTNoUFJGSnRDazlVUlRWWmJVVXlXVEpGZVU1VWFHeE5SRmt3V1cxUk0xcEhVWGhQUjFVMVRXcEJkMDFDT0VkRGFYTkhRVkZSUW1jM09IZEJVVFJGUlZGM1VHTnRWbTBLWTNrNWIxcFhSbXRqZVRsMFdWZHNkVTFDYTBkRGFYTkhRVkZSUW1jM09IZEJVVGhGUTNkM1NrNXFTVEJOUkZVeVRsUlZORTFEYzBkRGFYTkhRVkZSUWdwbk56aDNRVkpCUlVoUmQySmhTRkl3WTBoTk5reDVPVzVoV0ZKdlpGZEpkVmt5T1hSTU0wNHdXVmRPY21KSE9YSk5RbXRIUTJselIwRlJVVUpuTnpoM0NrRlNSVVZEZDNkS1RWUkZkMDFxVFROT2VsRXlUVWRaUjBOcGMwZEJVVkZDWnpjNGQwRlNTVVZYUVhoWFlVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVUtXVEk1ZEV3elRqQlpWMDV5WWtjNWNrd3lNWEJpYlZKc1kyazRkVm95YkRCaFNGWnBURE5rZG1OdGRHMWlSemt6WTNrNWFtRkhSbmxrUXpGM1pGZEtjd3BoV0U1dlRHNXNkR0pGUW5sYVYxcDZUREpvYkZsWFVucE1NakZvWVZjMGQwOUJXVXRMZDFsQ1FrRkhSSFo2UVVKRmQxRnhSRU5uTWxwSFRUSlplbHBxQ2sxcVkzaFBSRkp0VDFSRk5WbHRSVEpaTWtWNVRsUm9iRTFFV1RCWmJWRXpXa2RSZUU5SFZUVk5ha0YzVFVKUlIwTnBjMGRCVVZGQ1p6YzRkMEZTVVVVS1FtZDNSV05JVm5waFJFSldRbWR2Y2tKblJVVkJXVTh2VFVGRlZrSkZZMDFTVjJnd1pFaENlazlwT0haYU1td3dZVWhXYVV4dFRuWmlVemw2WkVkR2FncGhNbmgyWVhrNWRHRlhOV3RhV0VsMldWZE9NR0ZYT1hWamVUbDVaRmMxZWt4NlkzZE5hbEV4VFVSSk1FOUVRWFpaV0ZJd1dsY3hkMlJJVFhaTlZFRlhDa0puYjNKQ1owVkZRVmxQTDAxQlJWZENRV2ROUW01Q01WbHRlSEJaZWtOQ2FXZFpTMHQzV1VKQ1FVaFhaVkZKUlVGblVqaENTRzlCWlVGQ01rRk9NRGtLVFVkeVIzaDRSWGxaZUd0bFNFcHNiazUzUzJsVGJEWTBNMnA1ZEM4MFpVdGpiMEYyUzJVMlQwRkJRVUpxUW1aQ01XUjNRVUZCVVVSQlJXTjNVbEZKYUFwQlVFOVJaVGxLWWpGblNEQmpOWEV2YkhCcWVuUndlWEpPTWxBMFdtMHJlRWhGZUdaSUwyMUlWVzlQU0VGcFFtZERiSE5xV25FMFlWSk5kM1U0VGpkaUNsRndNRGRpWkdseUszTnJUVGx3VFZSMWVGRXZabUpyVFVOVVFVdENaMmR4YUd0cVQxQlJVVVJCZDA1dVFVUkNhMEZxUVRkNVVtOWpabm81ZUU1V1MwNEtXR0ZrYTB3MWNGaGpORFV6ZFdGbGNtTXZXVGRvY25SV2EwcDJTVEp0V0VaWWJEUXlRbGREWTJzemMxWlFTSEYyZG5SQlEwMUVMM1pQWWpOaVYzRkhWQW8xZVZSTVUySjBjRmg0UWs1dVkzSndNbTh3UzFJeE1tTXhZemQyTlcxb2RHWTVWV1JRYnpGRk0weEpRVmxzY1hCUGIyb3pVVkU5UFFvdExTMHRMVVZPUkNCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2c9PSJ9fX19" } ] }, "messageSignature": { "messageDigest": { "algorithm": "SHA2_256", "digest": "qdPqg5eb+jUi1ZBkwyRz7TusiPYsNrPdJD/v6V3sbaA=" }, "signature": "MEQCIGTdfr0o+hAiLLFsNCMGVhFqvi4X6nvWotjfpsSOi8qjAiBih0/EV8XrDg1jbQ5r+dycO4Z/xkHVlh5UFRY8gCfstg==" } } ``` and the verification result: ```json { "mediaType": "application/vnd.dev.sigstore.verificationresult+json;version=0.1", "signature": { "certificate": { "certificateIssuer": "CN=sigstore-intermediate,O=sigstore.dev", "subjectAlternativeName": { "type": "URI", "value": "https://github.com/stacklok/minder/.github/workflows/chart-publish.yml@refs/heads/main" }, "issuer": "https://token.actions.githubusercontent.com", "githubWorkflowTrigger": "push", "githubWorkflowSHA": "6dc6c6c27184f919ba6ca258e064bd7dd18e9200", "githubWorkflowName": "Publish Helm Chart", "githubWorkflowRepository": "stacklok/minder", "githubWorkflowRef": "refs/heads/main", "buildSignerURI": "https://github.com/stacklok/minder/.github/workflows/chart-publish.yml@refs/heads/main", "buildSignerDigest": "6dc6c6c27184f919ba6ca258e064bd7dd18e9200", "runnerEnvironment": "github-hosted", "sourceRepositoryURI": "https://github.com/stacklok/minder", "sourceRepositoryDigest": "6dc6c6c27184f919ba6ca258e064bd7dd18e9200", "sourceRepositoryRef": "refs/heads/main", "sourceRepositoryIdentifier": "624056558", "sourceRepositoryOwnerURI": "https://github.com/stacklok", "sourceRepositoryOwnerIdentifier": "110237746", "buildConfigURI": "https://github.com/stacklok/minder/.github/workflows/chart-publish.yml@refs/heads/main", "buildConfigDigest": "6dc6c6c27184f919ba6ca258e064bd7dd18e9200", "buildTrigger": "push", "runInvocationURI": "https://github.com/stacklok/minder/actions/runs/7024502480/attempts/1", "sourceRepositoryVisibilityAtSigning": "public" } }, "verifiedTimestamps": [ { "type": "Tlog", "uri": "TODO", "timestamp": "2023-11-28T23:07:08+02:00" } ], "verifiedIdentity": { "subjectAlternativeName": { "regexp": "^https://github.com/stacklok/minder/" }, "issuer": "https://token.actions.githubusercontent.com" } } ``` To explore a more advanced/configurable verification process, see the CLI implementation in [`examples/sigstore-go-verification/main.go`](../examples/sigstore-go-verification/main.go). sigstore-go-0.7.1/docs/signing.md000066400000000000000000000106641477477521700167250ustar00rootroot00000000000000# Signing using `sigstore-go` This document will walk you through using `sigstore-go` to generate a Sigstore bundle. ## Requirements - Unix-compatible OS - [Go 1.21](https://go.dev/doc/install) ## Installation Clone this repository and run the following command: ```shell $ go install ./examples/sigstore-go-signing ``` ## Bundle This library generates [Sigstore bundles](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto) encoded as JSON, which are composed of raw message signatures or attestations, combined with certificates, transparency log data, signed timestamps, and other metadata to form a single, verifiable artifact. ## Trusted Root You can optionally provide a [trusted root](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_trustroot.proto), containing root/intermediate certificate of the Fulcio/TSA/Rekor instances used to sign the bundle, which the signer will use to verify the bundle before returning it. Because the trusted root content changes as key material is rotated, the example uses TUF to fetch an up-to-date trusted root. ## Abstractions The library provides enough implementation to sign content with the Sigstore public good instance. It also provides interfaces so that in can be used in a wide variety of private deployments or other use-cases: - `Content` to represent what it is you want to be signed. Implementations are provided for `PlainData` or `DSSE` encoded content. - `Keypair` for how the content should be signed. A default implementation of `EphemeralKeypair` is provided, intended to be used with Sigstore Fulcio, with a ECDSA key using the P-256 curve. If you need a different key type, or a durable key, or support for a KMS, you can implement the `Keypair` interface. - `CertificateProvider` for obtaining a code signing certificate. A default `Fulcio` implementation is provided. - `Transparency` for obtaining a transparency log entry. A default `Rekor` implementation is provided. Although not an interface, there is also a `TimestampAuthority` that you can use to corroborate when the signing certificate was obtained. ## Interface Looking at the `Bundle()` function and its associated `BundleOptions`, you can see what is required to generate a bundle with signed content: - `Content` that represents what it is you want to be signed - `Keypair` for what to use to sign the content And then optionally: - A `CertificateProvider` - One or more `TimestampAuthorities` - One or more `Transparency` log entry providers - `TrustedRoot` material to verify the bundle before returning it See [sigstore-go-signing](../examples/sigstore-go-signing/main.go) for an example of how to use this interface. ## Examples A very basic example of signing with a provided keypair with a signed timestamp: ```bash $ sigstore-go-signing -tsa examples/sigstore-go-signing/hello_world.txt Using public key: -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY51jsbL5o8ge+FAcxpYjQeDEe1n5 WK+8DzwCkLLPJHISIvsiS93PTVDPpmbAASOl2Y4ZHqRsxb3aPMaQmN4sew== -----END PUBLIC KEY----- {"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial":{"publicKey":{"hint":"WpMWlwBZxXlzAjt/fxK4Nd9VYm7PH3cnr3TTVmdQ5SQ="}, "timestampVerificationData":{"rfc3161Timestamps":[{"signedTimestamp":"MIIC0jADAgEAMIICyQYJKoZIhvcNAQcCoIICujCCArYCAQMxDTALBglghkgBZQMEAgIwgbwGCyqGSIb3DQEJEAEEoIGsBIGpMIGmAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQgkaG6xajtAtsOoLy40vPZp+nDZRIWKnES2RqHDU7rmf4CFQC3ipDzPRUatDNLHxecg+XOCLSvWRgPMjAyNDA2MDcxNDExNTNaMAMCAQGgNqQ0MDIxFTATBgNVBAoTDEdpdEh1YiwgSW5jLjEZMBcGA1UEAxMQVFNBIFRpbWVzdGFtcGluZ6AAMYIB3zCCAdsCAQEwSjAyMRUwEwYDVQQKEwxHaXRIdWIsIEluYy4xGTAXBgNVBAMTEFRTQSBpbnRlcm1lZGlhdGUCFDz4J+p3q9T1P++QkPutn3Qbms2gMAsGCWCGSAFlAwQCAqCCAQUwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNDA2MDcxNDExNTNaMD8GCSqGSIb3DQEJBDEyBDCBYMVKhy7Mh+uUo0ycmn+Cl4swv4Z2t0TVuI+v0iNFJ4KTxB94bEALa2aaJZhNURswgYcGCyqGSIb3DQEJEAIvMXgwdjB0MHIEIHlI8iapfsPTNvwCoQbp1RaqmufUNHq7MbJ0CRlZCRsHME4wNqQ0MDIxFTATBgNVBAoTDEdpdEh1YiwgSW5jLjEZMBcGA1UEAxMQVFNBIGludGVybWVkaWF0ZQIUPPgn6ner1PU/75CQ+62fdBuazaAwCgYIKoZIzj0EAwMEaDBmAjEA3LlfE26IGKXCWgfGOxohAcz7/IlnRntEOI0lmknn5TgPa+VWfs1SqUBOKrYXPZutAjEAoNgsnlcSraDhAqdnv0llxvQLVEvZDBny1I1UgrtsAEnks9LWtH67bdwYrHRsCBAW"}]}}, "messageSignature":{"messageDigest":{"algorithm":"SHA2_256", "digest":"uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek="}, "signature":"MEUCIQDlK0ZyYsGeh1NC7MiAL+mT54jdQakhelpy5Vz5MmWEbgIgRn51DlDW6rgIY7KMUq+7sC8BjZYzh4QtmcPjJiF4RSA="}} ``` sigstore-go-0.7.1/docs/verification.md000066400000000000000000000275211477477521700177510ustar00rootroot00000000000000# Verification using `sigstore-go` This document will walk through using `sigstore-go` to verify a Sigstore Bundle. ## Requirements - Unix-compatible OS - [Go 1.21](https://go.dev/doc/install) ## Installation Clone this repository and use the `go` tool to install the `sigstore-go` CLI: ```shell go install ./examples/sigstore-go-verification ``` ## Bundle This library supports verifying [Sigstore bundles](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto) encoded as JSON, which are composed of raw message signatures or attestations, combined with certificates, transparency log data, signed timestamps, and other metadata to form a single, verifiable artifact. See the [signing documentation](signing.md) for how to generate/sign a bundle. An example Sigstore bundle is included in this distribution at [`examples/bundle-provenance.json`](../examples/bundle-provenance.json). ## Trusted Root The verifier allows you to use the Sigstore Public Good TUF root or your own custom [trusted root](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_trustroot.proto) containing the root/intermediate certificates of the Fulcio/TSA/Rekor instances used to sign the bundle, in order to verify common open source bundles or bundles signed by your own private Sigstore instance. ## Abstractions This library includes a few abstractions to support different use cases, testing, and extensibility: - `SignedEntity` - an interface type respresenting a signed message or attestation, with a signature and metadata, implemented by `Bundle`, a type which wraps the `Bundle` type from `protobuf-specs`. - `TrustedMaterial` - an interface type representing a trusted set of keys or certificates for verifying certificates, timestamps, and artifact transparency logs, implemented by `TrustedRoot` ## Verifier The main entrypoint for verification is called `SignedEntityVerifier`, which takes a `TrustedMaterial` and a set of `VerifierOption`s to configure the verification process. A `SignedEntityVerifier` has a single method, `Verify`, which accepts a `SignedEntity` (generally, a Sigstore bundle) and a `Policy` and returns a `VerificationResult`. As you can see, there are two places you can provide configuration for the verifier: - `NewSignedEntityVerifier` - "global options", such as the trusted material, and options for verifying the bundle's signatures, such as thresholds and whether to perform online verification, whether to check for SCTs, etc. - `Verify` - the bundle to be verified, and options for verifying the bundle's contents, such as asserting a specific subject digest or certificate issuer or SAN This is compatible with batch workflows where a single verifier is used to verify many bundles, and the bundles themselves may be verified against different identities/artifacts. ## Go API To verify a bundle with the Go API, you'll need to: - establish a trusted root - create a verifier using the required options - set up a policy containing the expected identity and digest to verify - verify the bundle Going through this step-by-step, we'll start by loading the trusted root from the Sigstore TUF repo: ```go opts := tuf.DefaultOptions() client, err := tuf.New(opts) if err != nil { panic(err) } trustedMaterial, err := root.GetTrustedRoot(client) if err != nil { panic(err) } ``` Next, we'll create a verifier with some options, which will enable SCT verification, ensure a single transparency log entry, and perform online verification: ```go sev, err := verify.NewSignedEntityVerifier(trustedMaterial, verify.WithSignedCertificateTimestamps(1), verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) if err != nil { panic(err) } ``` Then, we need to prepare the expected artifact digest. Note that this option has an alternative option `WithoutArtifactUnsafe`. This is a failsafe to ensure that the caller is aware that simply verifying the bundle is not enough, you must also verify the contents of the bundle against a specific artifact. ```go digest, err := hex.DecodeString("76176ffa33808b54602c7c35de5c6e9a4deb96066dba6533f50ac234f4f1f4c6b3527515dc17c06fbe2860030f410eee69ea20079bd3a2c6f3dcf3b329b10751") if err != nil { panic(err) } ``` In this case, we also need to prepare the expected certificate identity. Note that this option has an alternative option `WithoutIdentitiesUnsafe`. This is a failsafe to ensure that the caller is aware that simply verifying the bundle is not enough, you must also verify the contents of the bundle against a specific identity. If your bundle was signed with a key, and thus does not have a certificate identity, a better choice is to use the `WithKey` option. ```go certID, err := verify.NewShortCertificateIdentity("https://token.actions.githubusercontent.com", "", "", "^https://github.com/sigstore/sigstore-js/") if err != nil { panic(err) } ``` Then, we load the bundle and perform the verification: ```go b, err := bundle.LoadJSONFromPath("./examples/bundle-provenance.json") if err != nil { panic(err) } result, err := sev.Verify(b, verify.NewPolicy(verify.WithArtifactDigest("sha512", digest), verify.WithCertificateIdentity(certID))) if err != nil { panic(err) } ``` If the value of `err` is nil, the verification is successful and the `result` will contain details about the verification result. Below is an example of a successful verification result, serialized as JSON: ```json { "mediaType": "application/vnd.dev.sigstore.verificationresult+json;version=0.1", "statement": { "_type": "https://in-toto.io/Statement/v0.1", "predicateType": "https://slsa.dev/provenance/v0.2", "subject": [ { "name": "pkg:npm/sigstore@1.3.0", "digest": { "sha512": "76176ffa33808b54602c7c35de5c6e9a4deb96066dba6533f50ac234f4f1f4c6b3527515dc17c06fbe2860030f410eee69ea20079bd3a2c6f3dcf3b329b10751" } } ], "predicate": "omitted for brevity" }, "signature": { "certificate": { "certificateIssuer": "CN=sigstore-intermediate,O=sigstore.dev", "subjectAlternativeName": { "type": "URI", "value": "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main" }, "issuer": "https://token.actions.githubusercontent.com", "githubWorkflowTrigger": "push", "githubWorkflowSHA": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1", "githubWorkflowName": "Release", "githubWorkflowRepository": "sigstore/sigstore-js", "githubWorkflowRef": "refs/heads/main", "buildSignerURI": "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main", "buildSignerDigest": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1", "runnerEnvironment": "github-hosted", "sourceRepositoryURI": "https://github.com/sigstore/sigstore-js", "sourceRepositoryDigest": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1", "sourceRepositoryRef": "refs/heads/main", "sourceRepositoryIdentifier": "495574555", "sourceRepositoryOwnerURI": "https://github.com/sigstore", "sourceRepositoryOwnerIdentifier": "71096353", "buildConfigURI": "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main", "buildConfigDigest": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1", "buildTrigger": "push", "runInvocationURI": "https://github.com/sigstore/sigstore-js/actions/runs/4735384265/attempts/1" } }, "verifiedTimestamps": [ { "type": "Tlog", "uri": "TODO", "timestamp": "2023-04-18T13:45:12-04:00" } ], "verifiedIdentity": { "subjectAlternativeName": { "regexp": "^https://github.com/sigstore/sigstore-js/" }, "issuer": "https://token.actions.githubusercontent.com" } } ``` Putting it together, the following script will verify the example bundle and print the result. This can be run against the example bundle in this repository, if you paste it into `main.go` and use `go run main.go` to run it. ```go package main import ( "encoding/hex" "encoding/json" "fmt" "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/tuf" "github.com/sigstore/sigstore-go/pkg/verify" ) func main() { opts := tuf.DefaultOptions() client, err := tuf.New(opts) if err != nil { panic(err) } trustedMaterial, err := root.GetTrustedRoot(client) if err != nil { panic(err) } sev, err := verify.NewSignedEntityVerifier(trustedMaterial, verify.WithSignedCertificateTimestamps(1), verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) if err != nil { panic(err) } digest, err := hex.DecodeString("76176ffa33808b54602c7c35de5c6e9a4deb96066dba6533f50ac234f4f1f4c6b3527515dc17c06fbe2860030f410eee69ea20079bd3a2c6f3dcf3b329b10751") if err != nil { panic(err) } certID, err := verify.NewShortCertificateIdentity("https://token.actions.githubusercontent.com", "", "", "^https://github.com/sigstore/sigstore-js/") if err != nil { panic(err) } b, err := bundle.LoadJSONFromPath("./examples/bundle-provenance.json") if err != nil { panic(err) } result, err := sev.Verify(b, verify.NewPolicy(verify.WithArtifactDigest("sha512", digest), verify.WithCertificateIdentity(certID))) if err != nil { panic(err) } marshaled, err := json.MarshalIndent(result, "", " ") if err != nil { panic(err) } fmt.Println(string(marshaled)) } ``` And here is a complete example of verifying a bundle signed with a key: ```go package main import ( "crypto" _ "crypto/sha256" "crypto/x509" "encoding/hex" "encoding/json" "encoding/pem" "fmt" "os" "time" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/verify" ) type verifyTrustedMaterial struct { root.TrustedMaterial keyTrustedMaterial root.TrustedMaterial } func (v *verifyTrustedMaterial) PublicKeyVerifier(hint string) (root.TimeConstrainedVerifier, error) { return v.keyTrustedMaterial.PublicKeyVerifier(hint) } func main() { b, err := bundle.LoadJSONFromPath("./examples/bundle-publish.json") if err != nil { panic(err) } // This bundle uses public good instance with an added signing key trustedRoot, err := root.FetchTrustedRoot() if err != nil { panic(err) } keyData, err := os.ReadFile("examples/publish_key.pub") if err != nil { panic(err) } block, _ := pem.Decode(keyData) if block == nil { panic("unable to PEM decode provided key") } pubKey, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { panic(err) } verifier, err := signature.LoadVerifier(pubKey, crypto.SHA256) if err != nil { panic(err) } newExpiringKey := root.NewExpiringKey(verifier, time.Time{}, time.Time{}) trustedMaterial := &verifyTrustedMaterial{ TrustedMaterial: trustedRoot, keyTrustedMaterial: root.NewTrustedPublicKeyMaterial(func(_ string) (root.TimeConstrainedVerifier, error) { return newExpiringKey, nil }), } sev, err := verify.NewSignedEntityVerifier(trustedMaterial, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) if err != nil { panic(err) } digest, err := hex.DecodeString("76176ffa33808b54602c7c35de5c6e9a4deb96066dba6533f50ac234f4f1f4c6b3527515dc17c06fbe2860030f410eee69ea20079bd3a2c6f3dcf3b329b10751") if err != nil { panic(err) } result, err := sev.Verify(b, verify.NewPolicy(verify.WithArtifactDigest("sha512", digest), verify.WithKey())) if err != nil { panic(err) } fmt.Println("Verification successful!\n") marshaled, err := json.MarshalIndent(result, "", " ") if err != nil { panic(err) } fmt.Println(string(marshaled)) } ``` To explore a more advanced/configurable verification process, see the CLI implementation in [`examples/sigstore-go-verification/main.go`](../examples/sigstore-go-verification/main.go). sigstore-go-0.7.1/examples/000077500000000000000000000000001477477521700156245ustar00rootroot00000000000000sigstore-go-0.7.1/examples/README.md000066400000000000000000000010221477477521700170760ustar00rootroot00000000000000# sigstore-go examples These examples show how to use the library. They are not intended to be fully- supported CLI tools, so stability is not guaranteed. - [sigstore-go-signing](./sigstore-go-signing): a CLI for signing artifacts - [sigstore-go-verification](./sigstore-go-verification/README.md): a CLI for verifying Sigstore bundles - [custom-certificate-validator](./custom-certificate-validator/README.md): a custom certificate validator - [oci-image-verification](./oci-image-verification): a CLI for verifying OCI images sigstore-go-0.7.1/examples/bundle-provenance.json000066400000000000000000000256261477477521700221410ustar00rootroot00000000000000{"mediaType":"application/vnd.dev.sigstore.bundle+json;version=0.1","verificationMaterial":{"x509CertificateChain":{"certificates":[{"rawBytes":"MIIGnTCCBiKgAwIBAgIUAY4nsTCcZGNQgKt26IDI5lbzU/IwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjMwNDE4MTc0NTExWhcNMjMwNDE4MTc1NTExWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwEOO0UfhGUq2rXxy7jLTHY5VQXgNN5DmXXONKmoskPBECLY3l25HnymyzNpgMZyOnFJDvcDbi5+HjL5Yto6gKaOCBUEwggU9MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUoVwtgKpSjSIsfmaolzLXjxFY0yYwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wYwYDVR0RAQH/BFkwV4ZVaHR0cHM6Ly9naXRodWIuY29tL3NpZ3N0b3JlL3NpZ3N0b3JlLWpzLy5naXRodWIvd29ya2Zsb3dzL3JlbGVhc2UueW1sQHJlZnMvaGVhZHMvbWFpbjA5BgorBgEEAYO/MAEBBCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMBIGCisGAQQBg78wAQIEBHB1c2gwNgYKKwYBBAGDvzABAwQoZGFlOGJkOGViNDMzYTQxNDdiNDY1NWMwMGZlNzNlMGYyMmJjMGZiMTAVBgorBgEEAYO/MAEEBAdSZWxlYXNlMCIGCisGAQQBg78wAQUEFHNpZ3N0b3JlL3NpZ3N0b3JlLWpzMB0GCisGAQQBg78wAQYED3JlZnMvaGVhZHMvbWFpbjA7BgorBgEEAYO/MAEIBC0MK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wZQYKKwYBBAGDvzABCQRXDFVodHRwczovL2dpdGh1Yi5jb20vc2lnc3RvcmUvc2lnc3RvcmUtanMvLmdpdGh1Yi93b3JrZmxvd3MvcmVsZWFzZS55bWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wAQoEKgwoZGFlOGJkOGViNDMzYTQxNDdiNDY1NWMwMGZlNzNlMGYyMmJjMGZiMTAdBgorBgEEAYO/MAELBA8MDWdpdGh1Yi1ob3N0ZWQwNwYKKwYBBAGDvzABDAQpDCdodHRwczovL2dpdGh1Yi5jb20vc2lnc3RvcmUvc2lnc3RvcmUtanMwOAYKKwYBBAGDvzABDQQqDChkYWU4YmQ4ZWI0MzNhNDE0N2I0NjU1YzAwZmU3M2UwZjIyYmMwZmIxMB8GCisGAQQBg78wAQ4EEQwPcmVmcy9oZWFkcy9tYWluMBkGCisGAQQBg78wAQ8ECwwJNDk1NTc0NTU1MCsGCisGAQQBg78wARAEHQwbaHR0cHM6Ly9naXRodWIuY29tL3NpZ3N0b3JlMBgGCisGAQQBg78wAREECgwINzEwOTYzNTMwZQYKKwYBBAGDvzABEgRXDFVodHRwczovL2dpdGh1Yi5jb20vc2lnc3RvcmUvc2lnc3RvcmUtanMvLmdpdGh1Yi93b3JrZmxvd3MvcmVsZWFzZS55bWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wARMEKgwoZGFlOGJkOGViNDMzYTQxNDdiNDY1NWMwMGZlNzNlMGYyMmJjMGZiMTAUBgorBgEEAYO/MAEUBAYMBHB1c2gwWgYKKwYBBAGDvzABFQRMDEpodHRwczovL2dpdGh1Yi5jb20vc2lnc3RvcmUvc2lnc3RvcmUtanMvYWN0aW9ucy9ydW5zLzQ3MzUzODQyNjUvYXR0ZW1wdHMvMTCBiQYKKwYBBAHWeQIEAgR7BHkAdwB1AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABh5V4dEoAAAQDAEYwRAIgB9iqF/FYavg0QB87JLcRU/8m6SbN3ysYOxhk85VkRnoCIGemfDKeS1OaoFOu28SoQBohJaB0GozyyIIWgp3T6CRsMAoGCCqGSM49BAMDA2kAMGYCMQDyU//yA/5DuynXytqwHeF5aorTT2l83z1v1/eHoKtlw5eC0Id8jLUN2UzAA1D9IR0CMQDhltxC40MxjanEj1BSK/DWz2IVTt/VMOAkdMu/1qbhAMnMm6SG6N6KbYF4s2yYwT0="},{"rawBytes":"MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow="},{"rawBytes":"MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ"}]},"tlogEntries":[{"logIndex":"18300934","logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"kindVersion":{"kind":"intoto","version":"0.0.2"},"integratedTime":"1681839912","inclusionPromise":{"signedEntryTimestamp":"MEYCIQCQxXRPzxtA3rie/Gg8vErjJNfGRBwWtfyJZWekPepLIwIhAKCP6p9llDiaqkuOzjlGNfqWqHESGEiAGvS7RSNc6mLr"},"inclusionProof":null,"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoiYXBwbGljYXRpb24vdm5kLmluLXRvdG8ranNvbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVWR1VkVORFFtbExaMEYzU1VKQlowbFZRVmswYm5OVVEyTmFSMDVSWjB0ME1qWkpSRWsxYkdKNlZTOUpkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BOZDA1RVJUUk5WR013VGxSRmVGZG9ZMDVOYWsxM1RrUkZORTFVWXpGT1ZFVjRWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWM1JVOVBNRlZtYUVkVmNUSnlXSGg1TjJwTVZFaFpOVlpSV0dkT1RqVkViVmhZVDA0S1MyMXZjMnRRUWtWRFRGa3piREkxU0c1NWJYbDZUbkJuVFZwNVQyNUdTa1IyWTBSaWFUVXJTR3BNTlZsMGJ6Wm5TMkZQUTBKVlJYZG5aMVU1VFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZWdlZuZDBDbWRMY0ZOcVUwbHpabTFoYjJ4NlRGaHFlRVpaTUhsWmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQxbDNXVVJXVWpCU1FWRklMMEpHYTNkV05GcFdZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRETk9jRm96VGpCaU0wcHNURE5PY0FwYU0wNHdZak5LYkV4WGNIcE1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU0wcHNZa2RXYUdNeVZYVmxWekZ6VVVoS2JGcHVUWFpoUjFab0NscElUWFppVjBad1ltcEJOVUpuYjNKQ1owVkZRVmxQTDAxQlJVSkNRM1J2WkVoU2QyTjZiM1pNTTFKMllUSldkVXh0Um1wa1IyeDJZbTVOZFZveWJEQUtZVWhXYVdSWVRteGpiVTUyWW01U2JHSnVVWFZaTWpsMFRVSkpSME5wYzBkQlVWRkNaemM0ZDBGUlNVVkNTRUl4WXpKbmQwNW5XVXRMZDFsQ1FrRkhSQXAyZWtGQ1FYZFJiMXBIUm14UFIwcHJUMGRXYVU1RVRYcFpWRkY0VGtSa2FVNUVXVEZPVjAxM1RVZGFiRTU2VG14TlIxbDVUVzFLYWsxSFdtbE5WRUZXQ2tKbmIzSkNaMFZGUVZsUEwwMUJSVVZDUVdSVFdsZDRiRmxZVG14TlEwbEhRMmx6UjBGUlVVSm5OemgzUVZGVlJVWklUbkJhTTA0d1lqTktiRXd6VG5BS1dqTk9NR0l6U214TVYzQjZUVUl3UjBOcGMwZEJVVkZDWnpjNGQwRlJXVVZFTTBwc1dtNU5kbUZIVm1oYVNFMTJZbGRHY0dKcVFUZENaMjl5UW1kRlJRcEJXVTh2VFVGRlNVSkRNRTFMTW1nd1pFaENlazlwT0haa1J6bHlXbGMwZFZsWFRqQmhWemwxWTNrMWJtRllVbTlrVjBveFl6SldlVmt5T1hWa1IxWjFDbVJETldwaU1qQjNXbEZaUzB0M1dVSkNRVWRFZG5wQlFrTlJVbGhFUmxadlpFaFNkMk42YjNaTU1tUndaRWRvTVZscE5XcGlNakIyWXpKc2JtTXpVbllLWTIxVmRtTXliRzVqTTFKMlkyMVZkR0Z1VFhaTWJXUndaRWRvTVZscE9UTmlNMHB5V20xNGRtUXpUWFpqYlZaeldsZEdlbHBUTlRWaVYzaEJZMjFXYlFwamVUbHZXbGRHYTJONU9YUlpWMngxVFVSblIwTnBjMGRCVVZGQ1p6YzRkMEZSYjBWTFozZHZXa2RHYkU5SFNtdFBSMVpwVGtSTmVsbFVVWGhPUkdScENrNUVXVEZPVjAxM1RVZGFiRTU2VG14TlIxbDVUVzFLYWsxSFdtbE5WRUZrUW1kdmNrSm5SVVZCV1U4dlRVRkZURUpCT0UxRVYyUndaRWRvTVZscE1XOEtZak5PTUZwWFVYZE9kMWxMUzNkWlFrSkJSMFIyZWtGQ1JFRlJjRVJEWkc5a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFpqTW14dVl6TlNkZ3BqYlZWMll6SnNibU16VW5aamJWVjBZVzVOZDA5QldVdExkMWxDUWtGSFJIWjZRVUpFVVZGeFJFTm9hMWxYVlRSWmJWRTBXbGRKTUUxNlRtaE9SRVV3Q2s0eVNUQk9hbFV4V1hwQmQxcHRWVE5OTWxWM1dtcEplVmx0VFhkYWJVbDRUVUk0UjBOcGMwZEJVVkZDWnpjNGQwRlJORVZGVVhkUVkyMVdiV041T1c4S1dsZEdhMk41T1hSWlYyeDFUVUpyUjBOcGMwZEJVVkZDWnpjNGQwRlJPRVZEZDNkS1RrUnJNVTVVWXpCT1ZGVXhUVU56UjBOcGMwZEJVVkZDWnpjNGR3cEJVa0ZGU0ZGM1ltRklVakJqU0UwMlRIazVibUZZVW05a1YwbDFXVEk1ZEV3elRuQmFNMDR3WWpOS2JFMUNaMGREYVhOSFFWRlJRbWMzT0hkQlVrVkZDa05uZDBsT2VrVjNUMVJaZWs1VVRYZGFVVmxMUzNkWlFrSkJSMFIyZWtGQ1JXZFNXRVJHVm05a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFlLWXpKc2JtTXpVblpqYlZWMll6SnNibU16VW5aamJWVjBZVzVOZGt4dFpIQmtSMmd4V1drNU0ySXpTbkphYlhoMlpETk5kbU50Vm5OYVYwWjZXbE0xTlFwaVYzaEJZMjFXYldONU9XOWFWMFpyWTNrNWRGbFhiSFZOUkdkSFEybHpSMEZSVVVKbk56aDNRVkpOUlV0bmQyOWFSMFpzVDBkS2EwOUhWbWxPUkUxNkNsbFVVWGhPUkdScFRrUlpNVTVYVFhkTlIxcHNUbnBPYkUxSFdYbE5iVXBxVFVkYWFVMVVRVlZDWjI5eVFtZEZSVUZaVHk5TlFVVlZRa0ZaVFVKSVFqRUtZekpuZDFkbldVdExkMWxDUWtGSFJIWjZRVUpHVVZKTlJFVndiMlJJVW5kamVtOTJUREprY0dSSGFERlphVFZxWWpJd2RtTXliRzVqTTFKMlkyMVZkZ3BqTW14dVl6TlNkbU50VlhSaGJrMTJXVmRPTUdGWE9YVmplVGw1WkZjMWVreDZVVE5OZWxWNlQwUlJlVTVxVlhaWldGSXdXbGN4ZDJSSVRYWk5WRU5DQ21sUldVdExkMWxDUWtGSVYyVlJTVVZCWjFJM1FraHJRV1IzUWpGQlRqQTVUVWR5UjNoNFJYbFplR3RsU0Vwc2JrNTNTMmxUYkRZME0ycDVkQzgwWlVzS1kyOUJka3RsTms5QlFVRkNhRFZXTkdSRmIwRkJRVkZFUVVWWmQxSkJTV2RDT1dseFJpOUdXV0YyWnpCUlFqZzNTa3hqVWxVdk9HMDJVMkpPTTNseldRcFBlR2hyT0RWV2ExSnViME5KUjJWdFprUkxaVk14VDJGdlJrOTFNamhUYjFGQ2IyaEtZVUl3UjI5NmVYbEpTVmRuY0ROVU5rTlNjMDFCYjBkRFEzRkhDbE5OTkRsQ1FVMUVRVEpyUVUxSFdVTk5VVVI1VlM4dmVVRXZOVVIxZVc1WWVYUnhkMGhsUmpWaGIzSlVWREpzT0RONk1YWXhMMlZJYjB0MGJIYzFaVU1LTUVsa09HcE1WVTR5VlhwQlFURkVPVWxTTUVOTlVVUm9iSFI0UXpRd1RYaHFZVzVGYWpGQ1Uwc3ZSRmQ2TWtsV1ZIUXZWazFQUVd0a1RYVXZNWEZpYUFwQlRXNU5iVFpUUnpaT05rdGlXVVkwY3pKNVdYZFVNRDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUT09Iiwic2lnIjoiVFVWUlEwbEJXVkkwY0dKbVIwVjZjR0pDYWtwak9XMDRMMVpsUlRkeGRXUklPV1k1VFhGbmRHNTVhVTlWZUUxV1FXbENVM1puZVhWS2NFZE9UakZHY0ZoUlFqZEtZa1YyTUVwbmNVMTNaMVpUZFVGSk1saGlSRmRSUVcxbVFUMDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiYzUyZWYzOGFlMjE5NzMyMGRhZDdkNjc3YzBhYzExMjFjYjQ1MTkwYjZiYjIzMzljNTI5YjVkNGZhZGFkOGE3NSJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjJjOTNlOTk2Mjc0ZWRiOTVjYzQxMzk1MzAwMDk3NjYyOGYxM2YxZWRmYmUyMDM4ZmZkZDgxZjA3ZmY3YWE0ODMifX19fQ=="}],"timestampVerificationData":null},"dsseEnvelope":{"payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInN1YmplY3QiOlt7Im5hbWUiOiJwa2c6bnBtL3NpZ3N0b3JlQDEuMy4wIiwiZGlnZXN0Ijp7InNoYTUxMiI6Ijc2MTc2ZmZhMzM4MDhiNTQ2MDJjN2MzNWRlNWM2ZTlhNGRlYjk2MDY2ZGJhNjUzM2Y1MGFjMjM0ZjRmMWY0YzZiMzUyNzUxNWRjMTdjMDZmYmUyODYwMDMwZjQxMGVlZTY5ZWEyMDA3OWJkM2EyYzZmM2RjZjNiMzI5YjEwNzUxIn19XSwicHJlZGljYXRlVHlwZSI6Imh0dHBzOi8vc2xzYS5kZXYvcHJvdmVuYW5jZS92MC4yIiwicHJlZGljYXRlIjp7ImJ1aWxkVHlwZSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9ucG0vY2xpL2doYS92MiIsImJ1aWxkZXIiOnsiaWQiOiJodHRwczovL2dpdGh1Yi5jb20vYWN0aW9ucy9ydW5uZXIifSwiaW52b2NhdGlvbiI6eyJjb25maWdTb3VyY2UiOnsidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS9zaWdzdG9yZS9zaWdzdG9yZS1qc0ByZWZzL2hlYWRzL21haW4iLCJkaWdlc3QiOnsic2hhMSI6ImRhZThiZDhlYjQzM2E0MTQ3YjQ2NTVjMDBmZTczZTBmMjJiYzBmYjEifSwiZW50cnlQb2ludCI6Ii5naXRodWIvd29ya2Zsb3dzL3JlbGVhc2UueW1sIn0sInBhcmFtZXRlcnMiOnt9LCJlbnZpcm9ubWVudCI6eyJHSVRIVUJfRVZFTlRfTkFNRSI6InB1c2giLCJHSVRIVUJfUkVGIjoicmVmcy9oZWFkcy9tYWluIiwiR0lUSFVCX1JFUE9TSVRPUlkiOiJzaWdzdG9yZS9zaWdzdG9yZS1qcyIsIkdJVEhVQl9SRVBPU0lUT1JZX0lEIjoiNDk1NTc0NTU1IiwiR0lUSFVCX1JFUE9TSVRPUllfT1dORVJfSUQiOiI3MTA5NjM1MyIsIkdJVEhVQl9SVU5fQVRURU1QVCI6IjEiLCJHSVRIVUJfUlVOX0lEIjoiNDczNTM4NDI2NSIsIkdJVEhVQl9TSEEiOiJkYWU4YmQ4ZWI0MzNhNDE0N2I0NjU1YzAwZmU3M2UwZjIyYmMwZmIxIiwiR0lUSFVCX1dPUktGTE9XX1JFRiI6InNpZ3N0b3JlL3NpZ3N0b3JlLWpzLy5naXRodWIvd29ya2Zsb3dzL3JlbGVhc2UueW1sQHJlZnMvaGVhZHMvbWFpbiIsIkdJVEhVQl9XT1JLRkxPV19TSEEiOiJkYWU4YmQ4ZWI0MzNhNDE0N2I0NjU1YzAwZmU3M2UwZjIyYmMwZmIxIn19LCJtZXRhZGF0YSI6eyJidWlsZEludm9jYXRpb25JZCI6IjQ3MzUzODQyNjUtMSIsImNvbXBsZXRlbmVzcyI6eyJwYXJhbWV0ZXJzIjpmYWxzZSwiZW52aXJvbm1lbnQiOmZhbHNlLCJtYXRlcmlhbHMiOmZhbHNlfSwicmVwcm9kdWNpYmxlIjpmYWxzZX0sIm1hdGVyaWFscyI6W3sidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS9zaWdzdG9yZS9zaWdzdG9yZS1qc0ByZWZzL2hlYWRzL21haW4iLCJkaWdlc3QiOnsic2hhMSI6ImRhZThiZDhlYjQzM2E0MTQ3YjQ2NTVjMDBmZTczZTBmMjJiYzBmYjEifX1dfX0=","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MEQCIAYR4pbfGEzpbBjJc9m8/VeE7qudH9f9MqgtnyiOUxMVAiBSvgyuJpGNN1FpXQB7JbEv0JgqMwgVSuAI2XbDWQAmfA==","keyid":""}]}} sigstore-go-0.7.1/examples/bundle-publish.json000066400000000000000000000046241477477521700214420ustar00rootroot00000000000000{"mediaType":"application/vnd.dev.sigstore.bundle+json;version=0.1","verificationMaterial":{"publicKey":{"hint":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"},"tlogEntries":[{"logIndex":"18300940","logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="},"kindVersion":{"kind":"intoto","version":"0.0.2"},"integratedTime":"1681839916","inclusionPromise":{"signedEntryTimestamp":"MEYCIQDhoULWkbm7KZ4P4qAWHLw7d9X66AM/ZHNRvKgRahZg1gIhAILdjWLhlzSAy3XoP7sSFJKLwobemh2dtglhAXjSfEvA"},"inclusionProof":null,"canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoiYXBwbGljYXRpb24vdm5kLmluLXRvdG8ranNvbiIsInNpZ25hdHVyZXMiOlt7ImtleWlkIjoiU0hBMjU2OmpsM2J3c3d1ODBQampva0NnaDBvMnc1YzJVNExoUUFFNTdnajljejFrekEiLCJwdWJsaWNLZXkiOiJMUzB0TFMxQ1JVZEpUaUJRVlVKTVNVTWdTMFZaTFMwdExTMEtUVVpyZDBWM1dVaExiMXBKZW1vd1EwRlJXVWxMYjFwSmVtb3dSRUZSWTBSUlowRkZNVTlzWWpONlRVRkdSbmhZUzBocFNXdFJUelZqU2pOWmFHdzFhVFpWVUhBclNXaDFkR1ZDU21KMVNHTkJOVlZ2WjB0dk1FVlhkR3hYZDFjMlMxTmhTMjlVVGtWWlREZEtiRU5SYVZadWEyaENhM1JWWjJjOVBRb3RMUzB0TFVWT1JDQlFWVUpNU1VNZ1MwVlpMUzB0TFMwPSIsInNpZyI6IlRVVlZRMGxDYm10bldWcFFTM3BWY21RNFEyeFJXVlZJTTJNM1FXSk9aVFkxVkVGMU1GVXZTMk5FWmxKQmFWTnlRV2xGUVhWelRua3lXVFZGU2pjM1MzRnhlVzB4SzFCdFdsRXlkMGhRT0hoc05EWTNkMmxDWmxBME9UQXliRVU5In1dfSwiaGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6ImJjNWFlNjgxZTQ4Yjc1ZTAxN2MyNDdjNjRlY2Y0N2NkNDVjODVlNmNiNzY4ZjQzY2M0OGZhNmM0ZGVlMmFkYWMifSwicGF5bG9hZEhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIyNDViZDg2ODA0ZTQzM2M2MjEyYWUyYmQ4MGVjNzUwYmE0MWNjOWE0YTlkMTY3YWYyNzM4YzQ1MzI2MDgxOGE4In19fX0="}],"timestampVerificationData":null},"dsseEnvelope":{"payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInN1YmplY3QiOlt7Im5hbWUiOiJwa2c6bnBtL3NpZ3N0b3JlQDEuMy4wIiwiZGlnZXN0Ijp7InNoYTUxMiI6Ijc2MTc2ZmZhMzM4MDhiNTQ2MDJjN2MzNWRlNWM2ZTlhNGRlYjk2MDY2ZGJhNjUzM2Y1MGFjMjM0ZjRmMWY0YzZiMzUyNzUxNWRjMTdjMDZmYmUyODYwMDMwZjQxMGVlZTY5ZWEyMDA3OWJkM2EyYzZmM2RjZjNiMzI5YjEwNzUxIn19XSwicHJlZGljYXRlVHlwZSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9ucG0vYXR0ZXN0YXRpb24vdHJlZS9tYWluL3NwZWNzL3B1Ymxpc2gvdjAuMSIsInByZWRpY2F0ZSI6eyJuYW1lIjoic2lnc3RvcmUiLCJ2ZXJzaW9uIjoiMS4zLjAiLCJyZWdpc3RyeSI6Imh0dHBzOi8vcmVnaXN0cnkubnBtanMub3JnIn19","payloadType":"application/vnd.in-toto+json","signatures":[{"sig":"MEUCIBnkgYZPKzUrd8ClQYUH3c7AbNe65TAu0U/KcDfRAiSrAiEAusNy2Y5EJ77Kqqym1+PmZQ2wHP8xl467wiBfP4902lE=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}]}} sigstore-go-0.7.1/examples/custom-certificate-validator/000077500000000000000000000000001477477521700234015ustar00rootroot00000000000000sigstore-go-0.7.1/examples/custom-certificate-validator/README.md000066400000000000000000000013511477477521700246600ustar00rootroot00000000000000# Custom Certificate Validation example This example demonstrates how to use a custom TrustedMaterial that implements a custom certificate validator. This can be used by organizations running private PKI infrastructure to validate certificates issued by that infrastructure, or to implement a custom certificate revocation list (CRL). This custom TrustedMaterial type wraps any other TrustedMaterial (such as that provided by the Public Good Instance) and acts as a middleware that checks the CRL before the leaf certificate is verified by the wrapped TrustedMaterial. The code is implemented in `NewValidatingTrustedMaterial`, in `certificate_validator.go`. The unit test in `certificate_validator_test.go` demonstrates how it can be used. sigstore-go-0.7.1/examples/custom-certificate-validator/certificate_validator.go000066400000000000000000000047741477477521700302730ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "crypto/x509" "fmt" "math/big" "time" "github.com/sigstore/sigstore-go/pkg/root" ) type CertificateValidator func(cert *x509.Certificate) error type ValidatingCertificateAuthority struct { root.CertificateAuthority validator CertificateValidator } func (ca *ValidatingCertificateAuthority) Verify(leafCert *x509.Certificate, observerTimestamp time.Time) ([][]*x509.Certificate, error) { if err := ca.validator(leafCert); err != nil { return nil, fmt.Errorf("certificate validation failed: %w", err) } return ca.CertificateAuthority.Verify(leafCert, observerTimestamp) } type TrustedMaterialWithCertificateValidator struct { root.TrustedMaterial validator CertificateValidator } func NewTrustedMaterialWithCertificateValidation(tm root.TrustedMaterial, validator CertificateValidator) *TrustedMaterialWithCertificateValidator { return &TrustedMaterialWithCertificateValidator{ TrustedMaterial: tm, validator: validator, } } func (tm *TrustedMaterialWithCertificateValidator) FulcioCertificateAuthorities() []root.CertificateAuthority { cas := make([]root.CertificateAuthority, len(tm.TrustedMaterial.FulcioCertificateAuthorities())) for i, ca := range tm.TrustedMaterial.FulcioCertificateAuthorities() { cas[i] = &ValidatingCertificateAuthority{ca, tm.validator} } return cas } // NewValidatingTrustedMaterial creates a TrustedMaterial that validates certificates against a list of revoked serial numbers. func NewValidatingTrustedMaterial(trustedMaterial root.TrustedMaterial, revokedSerialNumbers []*big.Int) root.TrustedMaterial { return &TrustedMaterialWithCertificateValidator{ TrustedMaterial: trustedMaterial, validator: func(cert *x509.Certificate) error { for _, serialNumber := range revokedSerialNumbers { if cert.SerialNumber.Cmp(serialNumber) == 0 { return fmt.Errorf("certificate with serial number %v is revoked", serialNumber) } } return nil }, } } sigstore-go-0.7.1/examples/custom-certificate-validator/certificate_validator_test.go000066400000000000000000000025741477477521700313260ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "math/big" "testing" "time" "github.com/sigstore/sigstore-go/pkg/testing/ca" "github.com/sigstore/sigstore-go/pkg/verify" "github.com/stretchr/testify/assert" ) func TestCRLWrapper(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) leaf, _, err := virtualSigstore.GenerateLeafCert("example@example.com", "issuer") assert.NoError(t, err) trustedMaterial := NewValidatingTrustedMaterial(virtualSigstore, []*big.Int{}) validatingTrustedMaterial := NewValidatingTrustedMaterial(virtualSigstore, []*big.Int{leaf.SerialNumber}) _, err = verify.VerifyLeafCertificate(time.Now(), leaf, trustedMaterial) assert.NoError(t, err) _, err = verify.VerifyLeafCertificate(time.Now(), leaf, validatingTrustedMaterial) assert.Error(t, err) } sigstore-go-0.7.1/examples/oci-image-verification/000077500000000000000000000000001477477521700221365ustar00rootroot00000000000000sigstore-go-0.7.1/examples/oci-image-verification/go.mod000066400000000000000000000110671477477521700232510ustar00rootroot00000000000000module github.com/sigstore/sigstore-go/examples/oci-image-verification go 1.23.0 toolchain go1.24.1 replace github.com/sigstore/sigstore-go => ../../ require ( github.com/google/go-containerregistry v0.20.3 github.com/sigstore/protobuf-specs v0.4.1 github.com/sigstore/sigstore v1.9.1 github.com/sigstore/sigstore-go v0.6.2 ) require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 // indirect github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect github.com/docker/cli v27.5.0+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-chi/chi v4.1.2+incompatible // indirect github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.23.0 // indirect github.com/go-openapi/errors v0.22.1 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/loads v0.22.0 // indirect github.com/go-openapi/runtime v0.28.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect github.com/go-openapi/strfmt v0.23.0 // indirect github.com/go-openapi/swag v0.23.1 // indirect github.com/go-openapi/validate v0.24.0 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/google/certificate-transparency-go v1.3.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/in-toto/attestation v1.1.1 // indirect github.com/in-toto/in-toto-golang v0.9.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b // indirect github.com/josharian/intern v1.0.0 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sassoftware/relic v7.2.1+incompatible // indirect github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/sigstore/rekor v1.3.9 // indirect github.com/sigstore/timestamp-authority v1.2.5 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/viper v1.20.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/theupdateframework/go-tuf v0.7.0 // indirect github.com/theupdateframework/go-tuf/v2 v2.0.2 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/transparency-dev/merkle v0.0.2 // indirect github.com/vbatts/tar-split v0.11.6 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.34.0 // indirect go.opentelemetry.io/otel/metric v1.34.0 // indirect go.opentelemetry.io/otel/trace v1.34.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.37.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect golang.org/x/mod v0.24.0 // indirect golang.org/x/net v0.38.0 // indirect golang.org/x/sync v0.13.0 // indirect golang.org/x/sys v0.32.0 // indirect golang.org/x/term v0.31.0 // indirect golang.org/x/text v0.24.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect ) sigstore-go-0.7.1/examples/oci-image-verification/go.sum000066400000000000000000001114431477477521700232750ustar00rootroot00000000000000cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME= cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc= cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM= cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM= cloud.google.com/go/kms v1.21.1 h1:r1Auo+jlfJSf8B7mUnVw5K0fI7jWyoUy65bV53VjKyk= cloud.google.com/go/kms v1.21.1/go.mod h1:s0wCyByc9LjTdCjG88toVs70U9W+cc6RKFc8zAqX7nE= cloud.google.com/go/longrunning v0.6.5 h1:sD+t8DO8j4HKW4QfouCklg7ZC1qC4uzVZt8iz3uTW+Q= cloud.google.com/go/longrunning v0.6.5/go.mod h1:Et04XK+0TTLKa5IPYryKf5DkpwImy6TluQ1QTLwlKmY= 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.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2/config v1.29.10 h1:yNjgjiGBp4GgaJrGythyBXg2wAs+Im9fSWIUwvi1CAc= github.com/aws/aws-sdk-go-v2/config v1.29.10/go.mod h1:A0mbLXSdtob/2t59n1X0iMkPQ5d+YzYZB4rwu7SZ7aA= github.com/aws/aws-sdk-go-v2/credentials v1.17.63 h1:rv1V3kIJ14pdmTu01hwcMJ0WAERensSiD9rEWEBb1Tk= github.com/aws/aws-sdk-go-v2/credentials v1.17.63/go.mod h1:EJj+yDf0txT26Ulo0VWTavBl31hOsaeuMxIHu2m0suY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= github.com/aws/aws-sdk-go-v2/service/kms v1.38.1 h1:tecq7+mAav5byF+Mr+iONJnCBf4B4gon8RSp4BrweSc= github.com/aws/aws-sdk-go-v2/service/kms v1.38.1/go.mod h1:cQn6tAF77Di6m4huxovNM7NVAozWTZLsDRp9t8Z/WYk= github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0= github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.2 h1:wK8O+j2dOolmpNVY1EWIbLgxrGCHJKVPm08Hv/u80M8= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.2/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc= github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= 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/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/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8= github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 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.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs= github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1GUYL7P0MlNa00M67axePTq+9nBSGddR8I= github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y= github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM= github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= 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.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 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/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.22.1 h1:kslMRRnK7NCb/CvR1q1VWuEQCEIsBGn5GgKD9e+HYhU= github.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= 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/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/certificate-transparency-go v1.3.1 h1:akbcTfQg0iZlANZLn0L9xOeWtyCIdeoYhKrqi5iH3Go= github.com/google/certificate-transparency-go v1.3.1/go.mod h1:gg+UQlx6caKEDQ9EElFOujyxEQEfOiQzAt6782Bvi8k= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI= github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI= 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/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM= github.com/google/trillian v1.7.1 h1:+zX8jLM3524bAMPS+VxaDIDgsMv3/ty6DuLWerHXcek= github.com/google/trillian v1.7.1/go.mod h1:E1UMAHqpZCA8AQdrKdWmHmtUfSeiD0sDWD1cv00Xa+c= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= 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-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.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= 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.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4= github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/in-toto/attestation v1.1.1 h1:QD3d+oATQ0dFsWoNh5oT0udQ3tUrOsZZ0Fc3tSgWbzI= github.com/in-toto/attestation v1.1.1/go.mod h1:Dcq1zVwA2V7Qin8I7rgOi+i837wEf/mOZwRm047Sjys= github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0= github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b h1:ZGiXF8sz7PDk6RgkP+A/SFfUD0ZR/AgG6SpRNEDKZy8= github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b/go.mod h1:hQmNrgofl+IY/8L+n20H6E6PWBBTokdsv+q49j0QhsU= github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc= github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= 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-20240620165639-de9c06129bec h1:2tTW6cDth2TSgRbAhD7yjZzTQmcN25sDRPEeinR51yQ= github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec/go.mod h1:TmwEoGCwIti7BCeJ9hescZgRtatxRE+A72pCoPfmcfk= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/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/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= 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/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 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.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4= github.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k= github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= github.com/sigstore/protobuf-specs v0.4.1 h1:5SsMqZbdkcO/DNHudaxuCUEjj6x29tS2Xby1BxGU7Zc= github.com/sigstore/protobuf-specs v0.4.1/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= github.com/sigstore/rekor v1.3.9 h1:sUjRpKVh/hhgqGMs0t+TubgYsksArZ6poLEC3MsGAzU= github.com/sigstore/rekor v1.3.9/go.mod h1:xThNUhm6eNEmkJ/SiU/FVU7pLY2f380fSDZFsdDWlcM= github.com/sigstore/sigstore v1.9.1 h1:bNMsfFATsMPaagcf+uppLk4C9rQZ2dh5ysmCxQBYWaw= github.com/sigstore/sigstore v1.9.1/go.mod h1:zUoATYzR1J3rLNp3jmp4fzIJtWdhC3ZM6MnpcBtnsE4= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.1 h1:/YcNq687WnXpIRXl04nLfJX741G4iW+w+7Nem2Zy0f4= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.1/go.mod h1:ApL9RpKsi7gkSYN0bMNdm/3jZ9EefxMmfYHfUmq2ZYM= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.1 h1:FnusXyTIInnwfIOzzl5PFilRm1I97dxMSOcCkZBu9Kc= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.1/go.mod h1:d5m5LOa/69a+t2YC9pDPwS1n2i/PhqB4cUKbpVDlKKE= github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.1 h1:LFiYK1DEWQ6Hf/nroFzBMM+s5rVSjVL45Alpb5Ctl5A= github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.1/go.mod h1:GFyFmDsE2wDuIHZD+4+JErGpA0S4zJsKNz5l2JVJd8s= github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.1 h1:sIW6xe4yU5eIMH8fve2C78d+r29KmHnIb+7po+80bsY= github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.1/go.mod h1:3pNf99GnK9eu3XUa5ebHzgEQSVYf9hqAoPFwbwD6O6M= github.com/sigstore/timestamp-authority v1.2.5 h1:W22JmwRv1Salr/NFFuP7iJuhytcZszQjldoB8GiEdnw= github.com/sigstore/timestamp-authority v1.2.5/go.mod h1:gWPKWq4HMWgPCETre0AakgBzcr9DRqHrsgbrRqsigOs= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 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.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI= github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug= github.com/theupdateframework/go-tuf/v2 v2.0.2 h1:PyNnjV9BJNzN1ZE6BcWK+5JbF+if370jjzO84SS+Ebo= github.com/theupdateframework/go-tuf/v2 v2.0.2/go.mod h1:baB22nBHeHBCeuGZcIlctNq4P61PcOdyARlplg5xmLA= github.com/tink-crypto/tink-go-awskms/v2 v2.1.0 h1:N9UxlsOzu5mttdjhxkDLbzwtEecuXmlxZVo/ds7JKJI= github.com/tink-crypto/tink-go-awskms/v2 v2.1.0/go.mod h1:PxSp9GlOkKL9rlybW804uspnHuO9nbD98V/fDX4uSis= github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0 h1:3B9i6XBXNTRspfkTC0asN5W0K6GhOSgcujNiECNRNb0= github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0/go.mod h1:jY5YN2BqD/KSCHM9SqZPIpJNG/u3zwfLXHgws4x2IRw= github.com/tink-crypto/tink-go/v2 v2.3.0 h1:4/TA0lw0lA/iVKBL9f8R5eP7397bfc4antAMXF5JRhs= github.com/tink-crypto/tink-go/v2 v2.3.0/go.mod h1:kfPOtXIadHlekBTeBtJrHWqoGL+Fm3JQg0wtltPuxLU= 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/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.step.sm/crypto v0.60.0 h1:UgSw8DFG5xUOGB3GUID17UA32G4j1iNQ4qoMhBmsVFw= go.step.sm/crypto v0.60.0/go.mod h1:Ep83Lv818L4gV0vhFTdPWRKnL6/5fRMpi8SaoP5ArSw= 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.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= google.golang.org/api v0.227.0 h1:QvIHF9IuyG6d6ReE+BNd11kIB8hZvjN8Z5xY5t21zYc= google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ= google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 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/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= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= 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.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= sigstore-go-0.7.1/examples/oci-image-verification/main.go000066400000000000000000000425231477477521700234170ustar00rootroot00000000000000// 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 main import ( "bytes" "crypto" "crypto/ecdsa" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/json" "encoding/pem" "errors" "flag" "fmt" "os" "time" "github.com/google/go-containerregistry/pkg/crane" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" protorekor "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/tuf" "github.com/sigstore/sigstore-go/pkg/verify" "github.com/sigstore/sigstore/pkg/signature" ) var artifact *string var artifactDigest *string var artifactDigestAlgorithm *string var expectedOIDIssuer *string var expectedOIDIssuerRegex *string var expectedSAN *string var expectedSANRegex *string var ignoreSCT *bool var requireTimestamp *bool var requireTlog *bool var minBundleVersion *string var trustedPublicKey *string var trustedrootJSONpath *string var tufRootURL *string var tufDirectory *string var ociImage *string func init() { artifact = flag.String("artifact", "", "Path to artifact to verify") artifactDigest = flag.String("artifact-digest", "", "Hex-encoded digest of artifact to verify") artifactDigestAlgorithm = flag.String("artifact-digest-algorithm", "sha256", "Digest algorithm") expectedOIDIssuer = flag.String("expectedIssuer", "", "The expected OIDC issuer for the signing certificate") expectedOIDIssuerRegex = flag.String("expectedIssuerRegex", "", "The expected OIDC issuer for the signing certificate") expectedSAN = flag.String("expectedSAN", "", "The expected identity in the signing certificate's SAN extension") expectedSANRegex = flag.String("expectedSANRegex", "", "The expected identity in the signing certificate's SAN extension") ignoreSCT = flag.Bool("ignore-sct", false, "Ignore SCT verification - do not check that a certificate contains an embedded SCT, a proof of inclusion in a certificate transparency log") requireTimestamp = flag.Bool("requireTimestamp", true, "Require either an RFC3161 signed timestamp or log entry integrated timestamp") requireTlog = flag.Bool("requireTlog", true, "Require Artifact Transparency log entry (Rekor)") minBundleVersion = flag.String("minBundleVersion", "", "Minimum acceptable bundle version (e.g. '0.1')") trustedPublicKey = flag.String("publicKey", "", "Path to trusted public key") trustedrootJSONpath = flag.String("trustedrootJSONpath", "examples/trusted-root-public-good.json", "Path to trustedroot JSON file") tufRootURL = flag.String("tufRootURL", "", "URL of TUF root containing trusted root JSON file") tufDirectory = flag.String("tufDirectory", "tufdata", "Directory to store TUF metadata") ociImage = flag.String("ociImage", "", "OCI image to verify") flag.Parse() if flag.NArg() == 0 && *ociImage == "" { usage() os.Exit(1) } } func usage() { fmt.Printf("Usage: %s [OPTIONS] BUNDLE_FILE ...\n", os.Args[0]) flag.PrintDefaults() } func main() { if err := run(); err != nil { os.Stderr.WriteString(err.Error() + "\n") os.Exit(1) } } func run() error { var b *bundle.Bundle var err error if *ociImage != "" { // Build a bundle from OCI image reference and get its digest b, artifactDigest, err = bundleFromOCIImage(*ociImage, *requireTlog, *requireTimestamp) } else { // Load the bundle from file b, err = bundle.LoadJSONFromPath(flag.Arg(0)) } if err != nil { return err } if *minBundleVersion != "" { if !b.MinVersion(*minBundleVersion) { return fmt.Errorf("bundle is not of minimum version %s", *minBundleVersion) } } verifierConfig := []verify.VerifierOption{} identityPolicies := []verify.PolicyOption{} var artifactPolicy verify.ArtifactPolicyOption if !*ignoreSCT { verifierConfig = append(verifierConfig, verify.WithSignedCertificateTimestamps(1)) } if *requireTimestamp { verifierConfig = append(verifierConfig, verify.WithObserverTimestamps(1)) } if *requireTlog { verifierConfig = append(verifierConfig, verify.WithTransparencyLog(1)) } if *expectedOIDIssuer != "" || *expectedOIDIssuerRegex != "" || *expectedSAN != "" || *expectedSANRegex != "" { certID, err := verify.NewShortCertificateIdentity(*expectedOIDIssuer, *expectedOIDIssuerRegex, *expectedSAN, *expectedSANRegex) if err != nil { return err } identityPolicies = append(identityPolicies, verify.WithCertificateIdentity(certID)) } var trustedMaterial = make(root.TrustedMaterialCollection, 0) var trustedRootJSON []byte if *tufRootURL != "" { opts := tuf.DefaultOptions() opts.RepositoryBaseURL = *tufRootURL client, err := tuf.New(opts) if err != nil { return err } trustedRootJSON, err = client.GetTarget("trusted_root.json") if err != nil { return err } } else if *trustedrootJSONpath != "" { trustedRootJSON, err = os.ReadFile(*trustedrootJSONpath) } if err != nil { return err } if len(trustedRootJSON) > 0 { var trustedRoot *root.TrustedRoot trustedRoot, err = root.NewTrustedRootFromJSON(trustedRootJSON) if err != nil { return err } trustedMaterial = append(trustedMaterial, trustedRoot) } if *trustedPublicKey != "" { pemBytes, err := os.ReadFile(*trustedPublicKey) if err != nil { return err } pemBlock, _ := pem.Decode(pemBytes) if pemBlock == nil { return errors.New("failed to decode pem block") } pubKey, err := x509.ParsePKIXPublicKey(pemBlock.Bytes) if err != nil { return err } trustedMaterial = append(trustedMaterial, trustedPublicKeyMaterial(pubKey)) } if len(trustedMaterial) == 0 { return errors.New("no trusted material provided") } sev, err := verify.NewSignedEntityVerifier(trustedMaterial, verifierConfig...) if err != nil { return err } if *artifactDigest != "" { //nolint:gocritic artifactDigestBytes, err := hex.DecodeString(*artifactDigest) if err != nil { return err } artifactPolicy = verify.WithArtifactDigest(*artifactDigestAlgorithm, artifactDigestBytes) } else if *artifact != "" { file, err := os.Open(*artifact) if err != nil { return err } artifactPolicy = verify.WithArtifact(file) } else { artifactPolicy = verify.WithoutArtifactUnsafe() fmt.Fprintf(os.Stderr, "No artifact provided, skipping artifact verification. This is unsafe!\n") } res, err := sev.Verify(b, verify.NewPolicy(artifactPolicy, identityPolicies...)) if err != nil { return err } fmt.Fprintf(os.Stderr, "Verification successful!\n") marshaled, err := json.MarshalIndent(res, "", " ") if err != nil { return err } fmt.Println(string(marshaled)) return nil } type nonExpiringVerifier struct { signature.Verifier } func (*nonExpiringVerifier) ValidAtTime(_ time.Time) bool { return true } func trustedPublicKeyMaterial(pk crypto.PublicKey) *root.TrustedPublicKeyMaterial { return root.NewTrustedPublicKeyMaterial(func(string) (root.TimeConstrainedVerifier, error) { verifier, err := signature.LoadECDSAVerifier(pk.(*ecdsa.PublicKey), crypto.SHA256) if err != nil { return nil, err } return &nonExpiringVerifier{verifier}, nil }) } // bundleFromOCIImage returns a Bundle based on OCI image reference. func bundleFromOCIImage(imageRef string, hasTlog, hasTimestamp bool) (*bundle.Bundle, *string, error) { // 1. Get the simple signing layer simpleSigning, err := simpleSigningLayerFromOCIImage(imageRef) if err != nil { return nil, nil, fmt.Errorf("error getting simple signing layer: %w", err) } // 2. Build the verification material for the bundle verificationMaterial, err := getBundleVerificationMaterial(simpleSigning, hasTlog, hasTimestamp) if err != nil { return nil, nil, fmt.Errorf("error getting verification material: %w", err) } // 3. Build the message signature for the bundle msgSignature, err := getBundleMsgSignature(simpleSigning) if err != nil { return nil, nil, fmt.Errorf("error getting message signature: %w", err) } // 4. Construct and verify the bundle bundleMediaType, err := bundle.MediaTypeString("0.1") if err != nil { return nil, nil, fmt.Errorf("error getting bundle media type: %w", err) } pb := protobundle.Bundle{ MediaType: bundleMediaType, VerificationMaterial: verificationMaterial, Content: msgSignature, } bun, err := bundle.NewBundle(&pb) if err != nil { return nil, nil, fmt.Errorf("error creating bundle: %w", err) } // 5. Return the bundle and the digest of the simple signing layer (this is what is signed) return bun, &simpleSigning.Digest.Hex, nil } // simpleSigningLayerFromOCIImage returns the simple signing layer from the OCI image reference func simpleSigningLayerFromOCIImage(imageRef string) (*v1.Descriptor, error) { // 1. Get the image reference ref, err := name.ParseReference(imageRef) if err != nil { return nil, fmt.Errorf("error parsing image reference: %w", err) } // 2. Get the image descriptor desc, err := remote.Get(ref) if err != nil { return nil, fmt.Errorf("error getting image descriptor: %w", err) } // 3. Get the digest digest := ref.Context().Digest(desc.Digest.String()) h, err := v1.NewHash(digest.Identifier()) if err != nil { return nil, fmt.Errorf("error getting hash: %w", err) } // 4. Construct the signature reference - sha256-.sig sigTag := digest.Context().Tag(fmt.Sprint(h.Algorithm, "-", h.Hex, ".sig")) // 5. Get the manifest of the signature mf, err := crane.Manifest(sigTag.Name()) if err != nil { return nil, fmt.Errorf("error getting signature manifest: %w", err) } sigManifest, err := v1.ParseManifest(bytes.NewReader(mf)) if err != nil { return nil, fmt.Errorf("error parsing signature manifest: %w", err) } // 6. Ensure there is at least one layer and it is a simple signing layer if len(sigManifest.Layers) == 0 || sigManifest.Layers[0].MediaType != "application/vnd.dev.cosign.simplesigning.v1+json" { return nil, fmt.Errorf("no suitable layers found in signature manifest") } // 7. Return the layer - most probably there are more layers (one for each signature) but verifying one is enough return &sigManifest.Layers[0], nil } // getBundleVerificationMaterial returns the bundle verification material from the simple signing layer func getBundleVerificationMaterial(manifestLayer *v1.Descriptor, hasTlog, hasTimestamp bool) (*protobundle.VerificationMaterial, error) { // 1. Get the signing certificate chain signingCert, err := getVerificationMaterialX509CertificateChain(manifestLayer) if err != nil { return nil, fmt.Errorf("error getting signing certificate: %w", err) } // 2. Get the transparency log entries var tlogEntries []*protorekor.TransparencyLogEntry if hasTlog { tlogEntries, err = getVerificationMaterialTlogEntries(manifestLayer) if err != nil { return nil, fmt.Errorf("error getting tlog entries: %w", err) } } var timestampEntries *protobundle.TimestampVerificationData if hasTimestamp { timestampEntries, err = getVerificationMaterialTimestampEntries(manifestLayer) if err != nil { return nil, fmt.Errorf("error getting timestamp entries: %w", err) } } // 3. Construct the verification material return &protobundle.VerificationMaterial{ Content: signingCert, TlogEntries: tlogEntries, TimestampVerificationData: timestampEntries, }, nil } // getVerificationMaterialTlogEntries returns the verification material transparency log entries from the simple signing layer func getVerificationMaterialTlogEntries(manifestLayer *v1.Descriptor) ([]*protorekor.TransparencyLogEntry, error) { // 1. Get the bundle annotation bun := manifestLayer.Annotations["dev.sigstore.cosign/bundle"] var jsonData map[string]interface{} err := json.Unmarshal([]byte(bun), &jsonData) if err != nil { return nil, fmt.Errorf("error unmarshaling json: %w", err) } // 2. Get the log index, log ID, integrated time, signed entry timestamp and body logIndex, ok := jsonData["Payload"].(map[string]interface{})["logIndex"].(float64) if !ok { return nil, fmt.Errorf("error getting logIndex") } li, ok := jsonData["Payload"].(map[string]interface{})["logID"].(string) if !ok { return nil, fmt.Errorf("error getting logID") } logID, err := hex.DecodeString(li) if err != nil { return nil, fmt.Errorf("error decoding logID: %w", err) } integratedTime, ok := jsonData["Payload"].(map[string]interface{})["integratedTime"].(float64) if !ok { return nil, fmt.Errorf("error getting integratedTime") } set, ok := jsonData["SignedEntryTimestamp"].(string) if !ok { return nil, fmt.Errorf("error getting SignedEntryTimestamp") } signedEntryTimestamp, err := base64.StdEncoding.DecodeString(set) if err != nil { return nil, fmt.Errorf("error decoding signedEntryTimestamp: %w", err) } // 3. Unmarshal the body and extract the rekor KindVersion details body, ok := jsonData["Payload"].(map[string]interface{})["body"].(string) if !ok { return nil, fmt.Errorf("error getting body") } bodyBytes, err := base64.StdEncoding.DecodeString(body) if err != nil { return nil, fmt.Errorf("error decoding body: %w", err) } err = json.Unmarshal(bodyBytes, &jsonData) if err != nil { return nil, fmt.Errorf("error unmarshaling json: %w", err) } apiVersion := jsonData["apiVersion"].(string) kind := jsonData["kind"].(string) // 4. Construct the transparency log entry list return []*protorekor.TransparencyLogEntry{ { LogIndex: int64(logIndex), LogId: &protocommon.LogId{ KeyId: logID, }, KindVersion: &protorekor.KindVersion{ Kind: kind, Version: apiVersion, }, IntegratedTime: int64(integratedTime), InclusionPromise: &protorekor.InclusionPromise{ SignedEntryTimestamp: signedEntryTimestamp, }, InclusionProof: nil, CanonicalizedBody: bodyBytes, }, }, nil } func getVerificationMaterialTimestampEntries(manifestLayer *v1.Descriptor) (*protobundle.TimestampVerificationData, error) { // 1. Get the bundle annotation ts := manifestLayer.Annotations["dev.sigstore.cosign/rfc3161timestamp"] // 2. Get the key/value pairs maps var keyValPairs map[string]string err := json.Unmarshal([]byte(ts), &keyValPairs) if err != nil { return nil, fmt.Errorf("error unmarshaling JSON blob into key/val map: %w", err) } // 3. Verify the key "SignedRFC3161Timestamp" is present if _, ok := keyValPairs["SignedRFC3161Timestamp"]; !ok { return nil, errors.New("error getting SignedRFC3161Timestamp from key/value pairs") } // 4. Decode the base64 encoded timestamp der, err := base64.StdEncoding.DecodeString(keyValPairs["SignedRFC3161Timestamp"]) if err != nil { return nil, fmt.Errorf("error decoding base64 encoded timestamp: %w", err) } // 4. Construct the timestamp entry list return &protobundle.TimestampVerificationData{ Rfc3161Timestamps: []*protocommon.RFC3161SignedTimestamp{ { SignedTimestamp: der, }, }, }, nil } // getVerificationMaterialX509CertificateChain returns the verification material X509 certificate chain from the simple signing layer func getVerificationMaterialX509CertificateChain(manifestLayer *v1.Descriptor) (*protobundle.VerificationMaterial_X509CertificateChain, error) { // 1. Get the PEM certificate from the simple signing layer pemCert := manifestLayer.Annotations["dev.sigstore.cosign/certificate"] // 2. Construct the DER encoded version of the PEM certificate block, _ := pem.Decode([]byte(pemCert)) if block == nil { return nil, errors.New("failed to decode PEM block") } signingCert := protocommon.X509Certificate{ RawBytes: block.Bytes, } // 3. Construct the X509 certificate chain return &protobundle.VerificationMaterial_X509CertificateChain{ X509CertificateChain: &protocommon.X509CertificateChain{ Certificates: []*protocommon.X509Certificate{&signingCert}, }, }, nil } // getBundleMsgSignature returns the bundle message signature from the simple signing layer func getBundleMsgSignature(simpleSigningLayer *v1.Descriptor) (*protobundle.Bundle_MessageSignature, error) { // 1. Get the message digest algorithm var msgHashAlg protocommon.HashAlgorithm switch simpleSigningLayer.Digest.Algorithm { case "sha256": msgHashAlg = protocommon.HashAlgorithm_SHA2_256 default: return nil, fmt.Errorf("unknown digest algorithm: %s", simpleSigningLayer.Digest.Algorithm) } // 2. Get the message digest digest, err := hex.DecodeString(simpleSigningLayer.Digest.Hex) if err != nil { return nil, fmt.Errorf("error decoding digest: %w", err) } // 3. Get the signature s := simpleSigningLayer.Annotations["dev.cosignproject.cosign/signature"] sig, err := base64.StdEncoding.DecodeString(s) if err != nil { return nil, fmt.Errorf("error decoding manSig: %w", err) } // Construct the bundle message signature return &protobundle.Bundle_MessageSignature{ MessageSignature: &protocommon.MessageSignature{ MessageDigest: &protocommon.HashOutput{ Algorithm: msgHashAlg, Digest: digest, }, Signature: sig, }, }, nil } sigstore-go-0.7.1/examples/publish_key.pub000066400000000000000000000002611477477521700206510ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Olb3zMAFFxXKHiIkQO5cJ3Yhl5i6UPp+IhuteBJbuHcA5UogKo0EWtlWwW6KSaKoTNEYL7JlCQiVnkhBktUgg== -----END PUBLIC KEY----- sigstore-go-0.7.1/examples/sigstore-go-signing/000077500000000000000000000000001477477521700215225ustar00rootroot00000000000000sigstore-go-0.7.1/examples/sigstore-go-signing/hello_world.txt000066400000000000000000000000131477477521700245670ustar00rootroot00000000000000hello worldsigstore-go-0.7.1/examples/sigstore-go-signing/intoto.txt000066400000000000000000000003261477477521700236000ustar00rootroot00000000000000{"_type":"https://in-toto.io/Statement/v0.1","subject":[{"name":"hello_world.txt","digest":{"sha256":"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"}}],"predicateType":"something","predicate":{}}sigstore-go-0.7.1/examples/sigstore-go-signing/main.go000066400000000000000000000126651477477521700230070ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "flag" "fmt" "log" "os" "time" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/trustroot/v1" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/sign" "github.com/sigstore/sigstore-go/pkg/tuf" "github.com/sigstore/sigstore-go/pkg/util" "github.com/theupdateframework/go-tuf/v2/metadata/fetcher" "google.golang.org/protobuf/encoding/protojson" ) var idToken *string var intoto *bool var tsa *bool var rekor *bool func init() { idToken = flag.String("id-token", "", "OIDC token to send to Fulcio") intoto = flag.Bool("in-toto", false, "Content to sign is in-toto document") tsa = flag.Bool("tsa", false, "Include signed timestamp from timestamp authority") rekor = flag.Bool("rekor", false, "Including transparency log entry from Rekor") flag.Parse() if flag.NArg() == 0 { usage() os.Exit(1) } } func usage() { fmt.Printf("Usage: %s [OPTIONS] FILE_TO_SIGN\n", os.Args[0]) flag.PrintDefaults() } func main() { var content sign.Content data, err := os.ReadFile(flag.Arg(0)) if err != nil { log.Fatal(err) } if *intoto { content = &sign.DSSEData{ Data: data, PayloadType: "application/vnd.in-toto+json", } } else { content = &sign.PlainData{ Data: data, } } keypair, err := sign.NewEphemeralKeypair(nil) if err != nil { log.Fatal(err) } publicKeyPem, err := keypair.GetPublicKeyPem() if err != nil { log.Fatal(err) } fmt.Printf("Using public key:\n\n%s\n\n", publicKeyPem) opts := sign.BundleOptions{} // Get trusted_root.json fetcher := fetcher.DefaultFetcher{} fetcher.SetHTTPUserAgent(util.ConstructUserAgent()) tufOptions := &tuf.Options{ Root: tuf.StagingRoot(), RepositoryBaseURL: tuf.StagingMirror, Fetcher: &fetcher, } tufClient, err := tuf.New(tufOptions) if err != nil { log.Fatal(err) } trustedRoot, err := root.GetTrustedRoot(tufClient) if err != nil { log.Fatal(err) } // TODO: Uncomment once an updated v0.2 SigningConfig is distributed // via TUF // signingConfigPGI, err := root.GetSigningConfig(tufClient) signingConfig, err := root.NewSigningConfig( root.SigningConfigMediaType02, // Fulcio URLs []root.Service{ { URL: "https://fulcio.sigstage.dev", MajorAPIVersion: 1, ValidityPeriodStart: time.Now().Add(-time.Hour), ValidityPeriodEnd: time.Now().Add(time.Hour), }, }, // OIDC Provider URLs []root.Service{ { URL: "https://oauth2.sigstage.dev/auth", MajorAPIVersion: 1, ValidityPeriodStart: time.Now().Add(-time.Hour), ValidityPeriodEnd: time.Now().Add(time.Hour), }, }, // Rekor URLs []root.Service{ { URL: "https://rekor.sigstage.dev", MajorAPIVersion: 1, ValidityPeriodStart: time.Now().Add(-time.Hour), ValidityPeriodEnd: time.Now().Add(time.Hour), }, }, root.ServiceConfiguration{ Selector: v1.ServiceSelector_ANY, }, []root.Service{ { URL: "https://timestamp.githubapp.com/api/v1/timestamp", MajorAPIVersion: 1, ValidityPeriodStart: time.Now().Add(-time.Hour), ValidityPeriodEnd: time.Now().Add(time.Hour), }, }, root.ServiceConfiguration{ Selector: v1.ServiceSelector_ANY, }, ) if err != nil { log.Fatal(err) } opts.TrustedRoot = trustedRoot if *idToken != "" { fulcioURL, err := root.SelectService(signingConfig.FulcioCertificateAuthorityURLs(), []uint32{1}, time.Now()) if err != nil { log.Fatal(err) } fulcioOpts := &sign.FulcioOptions{ BaseURL: fulcioURL, Timeout: time.Duration(30 * time.Second), Retries: 1, } opts.CertificateProvider = sign.NewFulcio(fulcioOpts) opts.CertificateProviderOptions = &sign.CertificateProviderOptions{ IDToken: *idToken, } } if *tsa { tsaURLs, err := root.SelectServices(signingConfig.TimestampAuthorityURLs(), signingConfig.TimestampAuthorityURLsConfig(), []uint32{1}, time.Now()) if err != nil { log.Fatal(err) } for _, tsaURL := range tsaURLs { tsaOpts := &sign.TimestampAuthorityOptions{ URL: tsaURL, Timeout: time.Duration(30 * time.Second), Retries: 1, } opts.TimestampAuthorities = append(opts.TimestampAuthorities, sign.NewTimestampAuthority(tsaOpts)) } } if *rekor { rekorURLs, err := root.SelectServices(signingConfig.RekorLogURLs(), signingConfig.RekorLogURLsConfig(), []uint32{1}, time.Now()) if err != nil { log.Fatal(err) } for _, rekorURL := range rekorURLs { rekorOpts := &sign.RekorOptions{ BaseURL: rekorURL, Timeout: time.Duration(90 * time.Second), Retries: 1, } opts.TransparencyLogs = append(opts.TransparencyLogs, sign.NewRekor(rekorOpts)) } } bundle, err := sign.Bundle(content, keypair, opts) if err != nil { log.Fatal(err) } bundleJSON, err := protojson.Marshal(bundle) if err != nil { log.Fatal(err) } fmt.Println(string(bundleJSON)) } sigstore-go-0.7.1/examples/sigstore-go-verification/000077500000000000000000000000001477477521700225465ustar00rootroot00000000000000sigstore-go-0.7.1/examples/sigstore-go-verification/README.md000066400000000000000000000066501477477521700240340ustar00rootroot00000000000000# Building examples To build example programs, run `make build-examples` in the repo root. The built executables will be in the `examples/` subdirectory: ```shell $ make build-examples go build -C ./examples/oci-image-verification -o oci-image-verification . go build -C ./examples/sigstore-go-signing -o sigstore-go-signing . go build -C ./examples/sigstore-go-verification -o sigstore-go-verification . $ find examples -type f -perm -u+x | sort examples/oci-image-verification/oci-image-verification examples/sigstore-go-signing/sigstore-go-signing examples/sigstore-go-verification/sigstore-go-verification ``` # oci-image-verification This is a CLI for verifying signatures on the OCI images. View the help text with `-h` or `--help` for all the options. ```shell $ ./oci-image-verification -h Usage of ./oci-image-verification: -artifact string Path to artifact to verify -artifact-digest string Hex-encoded digest of artifact to verify -artifact-digest-algorithm string Digest algorithm (default "sha256") -expectedIssuer string The expected OIDC issuer for the signing certificate -expectedIssuerRegex string The expected OIDC issuer for the signing certificate -expectedSAN string The expected identity in the signing certificate's SAN extension -expectedSANRegex string The expected identity in the signing certificate's SAN extension -ignore-sct Ignore SCT verification - do not check that a certificate contains an embedded SCT, a proof of inclusion in a certificate transparency log -minBundleVersion string Minimum acceptable bundle version (e.g. '0.1') -ociImage string OCI image to verify -publicKey string Path to trusted public key -requireTimestamp Require either an RFC3161 signed timestamp or log entry integrated timestamp (default true) -requireTlog Require Artifact Transparency log entry (Rekor) (default true) -trustedrootJSONpath string Path to trustedroot JSON file (default "examples/trusted-root-public-good.json") -tufDirectory string Directory to store TUF metadata (default "tufdata") -tufRootURL string URL of TUF root containing trusted root JSON file ``` # sigstore-go-signing This is a test CLI for signing sigstore bundles. ```shell $ ./sigstore-go-signing -h Usage of ./sigstore-go-signing: -id-token string OIDC token to send to Fulcio -in-toto Content to sign is in-toto document -rekor Including transparency log entry from Rekor -tsa Include signed timestamp from timestamp authority ``` # sigstore-go-verification This is a CLI for verifying Sigstore bundles. View the help text with `-h` or `--help` for all the options. ```shell $ ./sigstore-go-verification \ -artifact-digest 76176ffa33808b54602c7c35de5c6e9a4deb96066dba6533f50ac234f4f1f4c6b3527515dc17c06fbe2860030f410eee69ea20079bd3a2c6f3dcf3b329b10751 \ -artifact-digest-algorithm sha512 \ -expectedIssuer https://token.actions.githubusercontent.com \ -expectedSAN https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main \ ../bundle-provenance.json Verification successful! { "version": 20230823, "statement": { "_type": "https://in-toto.io/Statement/v0.1", "predicateType": "https://slsa.dev/provenance/v0.2", "subject": ... }, ... } ``` You can also specify a TUF root with something like `-tufRootURL tuf-repo-cdn.sigstore.dev`. sigstore-go-0.7.1/examples/sigstore-go-verification/main.go000066400000000000000000000164451477477521700240330ustar00rootroot00000000000000// 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 main import ( "crypto" "crypto/ecdsa" "crypto/x509" "encoding/hex" "encoding/json" "encoding/pem" "errors" "flag" "fmt" "os" "time" "github.com/sigstore/sigstore/pkg/signature" "github.com/theupdateframework/go-tuf/v2/metadata/fetcher" "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/tuf" "github.com/sigstore/sigstore-go/pkg/util" "github.com/sigstore/sigstore-go/pkg/verify" ) var artifact *string var artifactDigest *string var artifactDigestAlgorithm *string var expectedOIDIssuer *string var expectedOIDIssuerRegex *string var expectedSAN *string var expectedSANRegex *string var requireTimestamp *bool var requireCTlog *bool var requireTlog *bool var minBundleVersion *string var trustedPublicKey *string var trustedrootJSONpath *string var tufRootURL *string var tufTrustedRoot *string func init() { artifact = flag.String("artifact", "", "Path to artifact to verify") artifactDigest = flag.String("artifact-digest", "", "Hex-encoded digest of artifact to verify") artifactDigestAlgorithm = flag.String("artifact-digest-algorithm", "sha256", "Digest algorithm") expectedOIDIssuer = flag.String("expectedIssuer", "", "The expected OIDC issuer for the signing certificate") expectedOIDIssuerRegex = flag.String("expectedIssuerRegex", "", "The expected OIDC issuer for the signing certificate") expectedSAN = flag.String("expectedSAN", "", "The expected identity in the signing certificate's SAN extension") expectedSANRegex = flag.String("expectedSANRegex", "", "The expected identity in the signing certificate's SAN extension") requireTimestamp = flag.Bool("requireTimestamp", true, "Require either an RFC3161 signed timestamp or log entry integrated timestamp") requireCTlog = flag.Bool("requireCTlog", true, "Require Certificate Transparency log entry") requireTlog = flag.Bool("requireTlog", true, "Require Artifact Transparency log entry (Rekor)") minBundleVersion = flag.String("minBundleVersion", "", "Minimum acceptable bundle version (e.g. '0.1')") trustedPublicKey = flag.String("publicKey", "", "Path to trusted public key") trustedrootJSONpath = flag.String("trustedrootJSONpath", "../trusted-root-public-good.json", "Path to trustedroot JSON file") tufRootURL = flag.String("tufRootURL", "", "URL of TUF root containing trusted root JSON file") tufTrustedRoot = flag.String("tufTrustedRoot", "", "Path to the trusted TUF root.json to bootstrap trust in the remote TUF repository") flag.Parse() if flag.NArg() == 0 { usage() os.Exit(1) } } func usage() { fmt.Printf("Usage: %s [OPTIONS] BUNDLE_FILE ...\n", os.Args[0]) flag.PrintDefaults() } func main() { if err := run(); err != nil { os.Stderr.WriteString(err.Error() + "\n") os.Exit(1) } } func run() error { b, err := bundle.LoadJSONFromPath(flag.Arg(0)) if err != nil { return err } if *minBundleVersion != "" { if !b.MinVersion(*minBundleVersion) { return fmt.Errorf("bundle is not of minimum version %s", *minBundleVersion) } } verifierConfig := []verify.VerifierOption{} identityPolicies := []verify.PolicyOption{} var artifactPolicy verify.ArtifactPolicyOption if *requireCTlog { verifierConfig = append(verifierConfig, verify.WithSignedCertificateTimestamps(1)) } if *requireTimestamp { verifierConfig = append(verifierConfig, verify.WithObserverTimestamps(1)) } if *requireTlog { verifierConfig = append(verifierConfig, verify.WithTransparencyLog(1)) } certID, err := verify.NewShortCertificateIdentity(*expectedOIDIssuer, *expectedOIDIssuerRegex, *expectedSAN, *expectedSANRegex) if err != nil { return err } identityPolicies = append(identityPolicies, verify.WithCertificateIdentity(certID)) var trustedMaterial = make(root.TrustedMaterialCollection, 0) var trustedRootJSON []byte if *tufRootURL != "" { opts := tuf.DefaultOptions() opts.RepositoryBaseURL = *tufRootURL fetcher := fetcher.DefaultFetcher{} fetcher.SetHTTPUserAgent(util.ConstructUserAgent()) opts.Fetcher = &fetcher // Load the tuf root.json if provided, if not use public good if *tufTrustedRoot != "" { rb, err := os.ReadFile(*tufTrustedRoot) if err != nil { return fmt.Errorf("failed to read %s: %w", *tufTrustedRoot, err) } opts.Root = rb } client, err := tuf.New(opts) if err != nil { return err } trustedRootJSON, err = client.GetTarget("trusted_root.json") if err != nil { return err } } else if *trustedrootJSONpath != "" { trustedRootJSON, err = os.ReadFile(*trustedrootJSONpath) if err != nil { return fmt.Errorf("failed to read %s: %w", *trustedrootJSONpath, err) } } if len(trustedRootJSON) > 0 { var trustedRoot *root.TrustedRoot trustedRoot, err = root.NewTrustedRootFromJSON(trustedRootJSON) if err != nil { return err } trustedMaterial = append(trustedMaterial, trustedRoot) } if *trustedPublicKey != "" { pemBytes, err := os.ReadFile(*trustedPublicKey) if err != nil { return err } pemBlock, _ := pem.Decode(pemBytes) if pemBlock == nil { return errors.New("failed to decode pem block") } pubKey, err := x509.ParsePKIXPublicKey(pemBlock.Bytes) if err != nil { return err } trustedMaterial = append(trustedMaterial, trustedPublicKeyMaterial(pubKey)) } if len(trustedMaterial) == 0 { return errors.New("no trusted material provided") } sev, err := verify.NewSignedEntityVerifier(trustedMaterial, verifierConfig...) if err != nil { return err } if *artifactDigest != "" { //nolint:gocritic artifactDigestBytes, err := hex.DecodeString(*artifactDigest) if err != nil { return err } artifactPolicy = verify.WithArtifactDigest(*artifactDigestAlgorithm, artifactDigestBytes) } else if *artifact != "" { file, err := os.Open(*artifact) if err != nil { return err } artifactPolicy = verify.WithArtifact(file) } else { artifactPolicy = verify.WithoutArtifactUnsafe() fmt.Fprintf(os.Stderr, "No artifact provided, skipping artifact verification. This is unsafe!\n") } res, err := sev.Verify(b, verify.NewPolicy(artifactPolicy, identityPolicies...)) if err != nil { return err } fmt.Fprintf(os.Stderr, "Verification successful!\n") marshaled, err := json.MarshalIndent(res, "", " ") if err != nil { return err } fmt.Println(string(marshaled)) return nil } type nonExpiringVerifier struct { signature.Verifier } func (*nonExpiringVerifier) ValidAtTime(_ time.Time) bool { return true } func trustedPublicKeyMaterial(pk crypto.PublicKey) *root.TrustedPublicKeyMaterial { return root.NewTrustedPublicKeyMaterial(func(string) (root.TimeConstrainedVerifier, error) { verifier, err := signature.LoadECDSAVerifier(pk.(*ecdsa.PublicKey), crypto.SHA256) if err != nil { return nil, err } return &nonExpiringVerifier{verifier}, nil }) } sigstore-go-0.7.1/examples/statement-provenance-sigstorejs1.3.0.json000066400000000000000000000034161477477521700254370ustar00rootroot00000000000000{ "_type": "https://in-toto.io/Statement/v0.1", "subject": [ { "name": "pkg:npm/sigstore@1.3.0", "digest": { "sha512": "76176ffa33808b54602c7c35de5c6e9a4deb96066dba6533f50ac234f4f1f4c6b3527515dc17c06fbe2860030f410eee69ea20079bd3a2c6f3dcf3b329b10751" } } ], "predicateType": "https://slsa.dev/provenance/v0.2", "predicate": { "buildType": "https://github.com/npm/cli/gha/v2", "builder": { "id": "https://github.com/actions/runner" }, "invocation": { "configSource": { "uri": "git+https://github.com/sigstore/sigstore-js@refs/heads/main", "digest": { "sha1": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1" }, "entryPoint": ".github/workflows/release.yml" }, "parameters": {}, "environment": { "GITHUB_EVENT_NAME": "push", "GITHUB_REF": "refs/heads/main", "GITHUB_REPOSITORY": "sigstore/sigstore-js", "GITHUB_REPOSITORY_ID": "495574555", "GITHUB_REPOSITORY_OWNER_ID": "71096353", "GITHUB_RUN_ATTEMPT": "1", "GITHUB_RUN_ID": "4735384265", "GITHUB_SHA": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1", "GITHUB_WORKFLOW_REF": "sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main", "GITHUB_WORKFLOW_SHA": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1" } }, "metadata": { "buildInvocationId": "4735384265-1", "completeness": { "parameters": false, "environment": false, "materials": false }, "reproducible": false }, "materials": [ { "uri": "git+https://github.com/sigstore/sigstore-js@refs/heads/main", "digest": { "sha1": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1" } } ] } } sigstore-go-0.7.1/examples/trusted-root-public-good.json000066400000000000000000000155461477477521700234070ustar00rootroot00000000000000{ "mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1", "tlogs": [ { "baseUrl": "https://rekor.sigstore.dev", "hashAlgorithm": "SHA2_256", "publicKey": { "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==", "keyDetails": "PKIX_ECDSA_P256_SHA_256", "validFor": { "start": "2021-01-12T11:53:27.000Z" } }, "logId": { "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" } } ], "certificateAuthorities": [ { "subject": { "organization": "sigstore.dev", "commonName": "sigstore" }, "uri": "https://fulcio.sigstore.dev", "certChain": { "certificates": [ { "rawBytes": "MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIxMDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSyA7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0JcastaRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6NmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJxVe/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uupHr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==" } ] }, "validFor": { "start": "2021-03-07T03:20:29.000Z", "end": "2022-12-31T23:59:59.999Z" } }, { "subject": { "organization": "sigstore.dev", "commonName": "sigstore" }, "uri": "https://fulcio.sigstore.dev", "certChain": { "certificates": [ { "rawBytes": "MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=" }, { "rawBytes": "MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ" } ] }, "validFor": { "start": "2022-04-13T20:06:15.000Z" } } ], "ctlogs": [ { "baseUrl": "https://ctfe.sigstore.dev/test", "hashAlgorithm": "SHA2_256", "publicKey": { "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbfwR+RJudXscgRBRpKX1XFDy3PyudDxz/SfnRi1fT8ekpfBd2O1uoz7jr3Z8nKzxA69EUQ+eFCFI3zeubPWU7w==", "keyDetails": "PKIX_ECDSA_P256_SHA_256", "validFor": { "start": "2021-03-14T00:00:00.000Z", "end": "2022-10-31T23:59:59.999Z" } }, "logId": { "keyId": "CGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2b3I=" } }, { "baseUrl": "https://ctfe.sigstore.dev/2022", "hashAlgorithm": "SHA2_256", "publicKey": { "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiPSlFi0CmFTfEjCUqF9HuCEcYXNKAaYalIJmBZ8yyezPjTqhxrKBpMnaocVtLJBI1eM3uXnQzQGAJdJ4gs9Fyw==", "keyDetails": "PKIX_ECDSA_P256_SHA_256", "validFor": { "start": "2022-10-20T00:00:00.000Z" } }, "logId": { "keyId": "3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4=" } } ], "timestampAuthorities": [ { "subject": { "organization": "GitHub, Inc.", "commonName": "Internal Services Root" }, "certChain": { "certificates": [ { "rawBytes": "MIIB3DCCAWKgAwIBAgIUchkNsH36Xa04b1LqIc+qr9DVecMwCgYIKoZIzj0EAwMwMjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRkwFwYDVQQDExBUU0EgaW50ZXJtZWRpYXRlMB4XDTIzMDQxNDAwMDAwMFoXDTI0MDQxMzAwMDAwMFowMjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRkwFwYDVQQDExBUU0EgVGltZXN0YW1waW5nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUD5ZNbSqYMd6r8qpOOEX9ibGnZT9GsuXOhr/f8U9FJugBGExKYp40OULS0erjZW7xV9xV52NnJf5OeDq4e5ZKqNWMFQwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUaW1RudOgVt0leqY0WKYbuPr47wAwCgYIKoZIzj0EAwMDaAAwZQIwbUH9HvD4ejCZJOWQnqAlkqURllvu9M8+VqLbiRK+zSfZCZwsiljRn8MQQRSkXEE5AjEAg+VxqtojfVfu8DhzzhCx9GKETbJHb19iV72mMKUbDAFmzZ6bQ8b54Zb8tidy5aWe" }, { "rawBytes": "MIICEDCCAZWgAwIBAgIUX8ZO5QXP7vN4dMQ5e9sU3nub8OgwCgYIKoZIzj0EAwMwODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MB4XDTIzMDQxNDAwMDAwMFoXDTI4MDQxMjAwMDAwMFowMjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRkwFwYDVQQDExBUU0EgaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEvMLY/dTVbvIJYANAuszEwJnQE1llftynyMKIMhh48HmqbVr5ygybzsLRLVKbBWOdZ21aeJz+gZiytZetqcyF9WlER5NEMf6JV7ZNojQpxHq4RHGoGSceQv/qvTiZxEDKo2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaW1RudOgVt0leqY0WKYbuPr47wAwHwYDVR0jBBgwFoAU9NYYlobnAG4c0/qjxyH/lq/wz+QwCgYIKoZIzj0EAwMDaQAwZgIxAK1B185ygCrIYFlIs3GjswjnwSMG6LY8woLVdakKDZxVa8f8cqMs1DhcxJ0+09w95QIxAO+tBzZk7vjUJ9iJgD4R6ZWTxQWKqNm74jO99o+o9sv4FI/SZTZTFyMn0IJEHdNmyA==" }, { "rawBytes": "MIIB9DCCAXqgAwIBAgIUa/JAkdUjK4JUwsqtaiRJGWhqLSowCgYIKoZIzj0EAwMwODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MB4XDTIzMDQxNDAwMDAwMFoXDTMzMDQxMTAwMDAwMFowODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEf9jFAXxz4kx68AHRMOkFBhflDcMTvzaXz4x/FCcXjJ/1qEKon/qPIGnaURskDtyNbNDOpeJTDDFqt48iMPrnzpx6IZwqemfUJN4xBEZfza+pYt/iyod+9tZr20RRWSv/o0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBAjAdBgNVHQ4EFgQU9NYYlobnAG4c0/qjxyH/lq/wz+QwCgYIKoZIzj0EAwMDaAAwZQIxALZLZ8BgRXzKxLMMN9VIlO+e4hrBnNBgF7tz7Hnrowv2NetZErIACKFymBlvWDvtMAIwZO+ki6ssQ1bsZo98O8mEAf2NZ7iiCgDDU0Vwjeco6zyeh0zBTs9/7gV6AHNQ53xD" } ] }, "validFor": { "start": "2023-04-14T00:00:00.000Z" } } ] } sigstore-go-0.7.1/go.mod000066400000000000000000000116231477477521700151170ustar00rootroot00000000000000module github.com/sigstore/sigstore-go go 1.23.0 toolchain go1.24.1 require ( github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 github.com/go-openapi/runtime v0.28.0 github.com/go-openapi/strfmt v0.23.0 github.com/go-openapi/swag v0.23.1 github.com/google/certificate-transparency-go v1.3.1 github.com/in-toto/attestation v1.1.1 github.com/in-toto/in-toto-golang v0.9.0 github.com/secure-systems-lab/go-securesystemslib v0.9.0 github.com/sigstore/protobuf-specs v0.4.1 github.com/sigstore/rekor v1.3.9 github.com/sigstore/sigstore v1.9.1 github.com/sigstore/timestamp-authority v1.2.5 github.com/stretchr/testify v1.10.0 github.com/theupdateframework/go-tuf/v2 v2.0.2 golang.org/x/crypto v0.37.0 golang.org/x/mod v0.24.0 google.golang.org/protobuf v1.36.6 ) require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/coreos/go-oidc/v3 v3.12.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-chi/chi v4.1.2+incompatible // indirect github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.23.0 // indirect github.com/go-openapi/errors v0.22.1 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/loads v0.22.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect github.com/go-openapi/validate v0.24.0 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/google/go-containerregistry v0.20.3 // indirect github.com/google/trillian v1.7.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.7.2 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b // indirect github.com/josharian/intern v1.0.0 // indirect github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sassoftware/relic v7.2.1+incompatible // indirect github.com/segmentio/ksuid v1.0.4 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.6 // indirect github.com/spf13/viper v1.20.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/theupdateframework/go-tuf v0.7.0 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/transparency-dev/merkle v0.0.2 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.34.0 // indirect go.opentelemetry.io/otel/metric v1.34.0 // indirect go.opentelemetry.io/otel/trace v1.34.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect golang.org/x/net v0.38.0 // indirect golang.org/x/oauth2 v0.28.0 // indirect golang.org/x/sync v0.13.0 // indirect golang.org/x/sys v0.32.0 // indirect golang.org/x/term v0.31.0 // indirect golang.org/x/text v0.24.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect google.golang.org/grpc v1.71.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect ) sigstore-go-0.7.1/go.sum000066400000000000000000001133061477477521700151450ustar00rootroot00000000000000cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME= cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc= cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM= cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM= cloud.google.com/go/kms v1.21.1 h1:r1Auo+jlfJSf8B7mUnVw5K0fI7jWyoUy65bV53VjKyk= cloud.google.com/go/kms v1.21.1/go.mod h1:s0wCyByc9LjTdCjG88toVs70U9W+cc6RKFc8zAqX7nE= cloud.google.com/go/longrunning v0.6.5 h1:sD+t8DO8j4HKW4QfouCklg7ZC1qC4uzVZt8iz3uTW+Q= cloud.google.com/go/longrunning v0.6.5/go.mod h1:Et04XK+0TTLKa5IPYryKf5DkpwImy6TluQ1QTLwlKmY= 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.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= 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/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2/config v1.29.10 h1:yNjgjiGBp4GgaJrGythyBXg2wAs+Im9fSWIUwvi1CAc= github.com/aws/aws-sdk-go-v2/config v1.29.10/go.mod h1:A0mbLXSdtob/2t59n1X0iMkPQ5d+YzYZB4rwu7SZ7aA= github.com/aws/aws-sdk-go-v2/credentials v1.17.63 h1:rv1V3kIJ14pdmTu01hwcMJ0WAERensSiD9rEWEBb1Tk= github.com/aws/aws-sdk-go-v2/credentials v1.17.63/go.mod h1:EJj+yDf0txT26Ulo0VWTavBl31hOsaeuMxIHu2m0suY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= github.com/aws/aws-sdk-go-v2/service/kms v1.38.1 h1:tecq7+mAav5byF+Mr+iONJnCBf4B4gon8RSp4BrweSc= github.com/aws/aws-sdk-go-v2/service/kms v1.38.1/go.mod h1:cQn6tAF77Di6m4huxovNM7NVAozWTZLsDRp9t8Z/WYk= github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0= github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.2 h1:wK8O+j2dOolmpNVY1EWIbLgxrGCHJKVPm08Hv/u80M8= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.2/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc= github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= 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/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/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo= github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 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.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1GUYL7P0MlNa00M67axePTq+9nBSGddR8I= github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 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.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 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/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.22.1 h1:kslMRRnK7NCb/CvR1q1VWuEQCEIsBGn5GgKD9e+HYhU= github.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco= github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs= github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ= github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc= github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA= github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= 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/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 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.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/certificate-transparency-go v1.3.1 h1:akbcTfQg0iZlANZLn0L9xOeWtyCIdeoYhKrqi5iH3Go= github.com/google/certificate-transparency-go v1.3.1/go.mod h1:gg+UQlx6caKEDQ9EElFOujyxEQEfOiQzAt6782Bvi8k= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI= github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI= 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/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM= github.com/google/trillian v1.7.1 h1:+zX8jLM3524bAMPS+VxaDIDgsMv3/ty6DuLWerHXcek= github.com/google/trillian v1.7.1/go.mod h1:E1UMAHqpZCA8AQdrKdWmHmtUfSeiD0sDWD1cv00Xa+c= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.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.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= 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.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4= github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/in-toto/attestation v1.1.1 h1:QD3d+oATQ0dFsWoNh5oT0udQ3tUrOsZZ0Fc3tSgWbzI= github.com/in-toto/attestation v1.1.1/go.mod h1:Dcq1zVwA2V7Qin8I7rgOi+i837wEf/mOZwRm047Sjys= github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0= github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b h1:ZGiXF8sz7PDk6RgkP+A/SFfUD0ZR/AgG6SpRNEDKZy8= github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b/go.mod h1:hQmNrgofl+IY/8L+n20H6E6PWBBTokdsv+q49j0QhsU= github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc= github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/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-20240620165639-de9c06129bec h1:2tTW6cDth2TSgRbAhD7yjZzTQmcN25sDRPEeinR51yQ= github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec/go.mod h1:TmwEoGCwIti7BCeJ9hescZgRtatxRE+A72pCoPfmcfk= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/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/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= 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/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 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.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4= github.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k= github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= github.com/sigstore/protobuf-specs v0.4.1 h1:5SsMqZbdkcO/DNHudaxuCUEjj6x29tS2Xby1BxGU7Zc= github.com/sigstore/protobuf-specs v0.4.1/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= github.com/sigstore/rekor v1.3.9 h1:sUjRpKVh/hhgqGMs0t+TubgYsksArZ6poLEC3MsGAzU= github.com/sigstore/rekor v1.3.9/go.mod h1:xThNUhm6eNEmkJ/SiU/FVU7pLY2f380fSDZFsdDWlcM= github.com/sigstore/sigstore v1.9.1 h1:bNMsfFATsMPaagcf+uppLk4C9rQZ2dh5ysmCxQBYWaw= github.com/sigstore/sigstore v1.9.1/go.mod h1:zUoATYzR1J3rLNp3jmp4fzIJtWdhC3ZM6MnpcBtnsE4= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.1 h1:/YcNq687WnXpIRXl04nLfJX741G4iW+w+7Nem2Zy0f4= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.1/go.mod h1:ApL9RpKsi7gkSYN0bMNdm/3jZ9EefxMmfYHfUmq2ZYM= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.1 h1:FnusXyTIInnwfIOzzl5PFilRm1I97dxMSOcCkZBu9Kc= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.1/go.mod h1:d5m5LOa/69a+t2YC9pDPwS1n2i/PhqB4cUKbpVDlKKE= github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.1 h1:LFiYK1DEWQ6Hf/nroFzBMM+s5rVSjVL45Alpb5Ctl5A= github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.1/go.mod h1:GFyFmDsE2wDuIHZD+4+JErGpA0S4zJsKNz5l2JVJd8s= github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.1 h1:sIW6xe4yU5eIMH8fve2C78d+r29KmHnIb+7po+80bsY= github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.1/go.mod h1:3pNf99GnK9eu3XUa5ebHzgEQSVYf9hqAoPFwbwD6O6M= github.com/sigstore/timestamp-authority v1.2.5 h1:W22JmwRv1Salr/NFFuP7iJuhytcZszQjldoB8GiEdnw= github.com/sigstore/timestamp-authority v1.2.5/go.mod h1:gWPKWq4HMWgPCETre0AakgBzcr9DRqHrsgbrRqsigOs= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI= github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug= github.com/theupdateframework/go-tuf/v2 v2.0.2 h1:PyNnjV9BJNzN1ZE6BcWK+5JbF+if370jjzO84SS+Ebo= github.com/theupdateframework/go-tuf/v2 v2.0.2/go.mod h1:baB22nBHeHBCeuGZcIlctNq4P61PcOdyARlplg5xmLA= github.com/tink-crypto/tink-go-awskms/v2 v2.1.0 h1:N9UxlsOzu5mttdjhxkDLbzwtEecuXmlxZVo/ds7JKJI= github.com/tink-crypto/tink-go-awskms/v2 v2.1.0/go.mod h1:PxSp9GlOkKL9rlybW804uspnHuO9nbD98V/fDX4uSis= github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0 h1:3B9i6XBXNTRspfkTC0asN5W0K6GhOSgcujNiECNRNb0= github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0/go.mod h1:jY5YN2BqD/KSCHM9SqZPIpJNG/u3zwfLXHgws4x2IRw= github.com/tink-crypto/tink-go/v2 v2.3.0 h1:4/TA0lw0lA/iVKBL9f8R5eP7397bfc4antAMXF5JRhs= github.com/tink-crypto/tink-go/v2 v2.3.0/go.mod h1:kfPOtXIadHlekBTeBtJrHWqoGL+Fm3JQg0wtltPuxLU= 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/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q= github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg= github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU= github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.step.sm/crypto v0.60.0 h1:UgSw8DFG5xUOGB3GUID17UA32G4j1iNQ4qoMhBmsVFw= go.step.sm/crypto v0.60.0/go.mod h1:Ep83Lv818L4gV0vhFTdPWRKnL6/5fRMpi8SaoP5ArSw= 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.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= google.golang.org/api v0.227.0 h1:QvIHF9IuyG6d6ReE+BNd11kIB8hZvjN8Z5xY5t21zYc= google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ= google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 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/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= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= 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.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= sigstore-go-0.7.1/pkg/000077500000000000000000000000001477477521700145675ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/bundle/000077500000000000000000000000001477477521700160405ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/bundle/bundle.go000066400000000000000000000273501477477521700176470ustar00rootroot00000000000000// 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 bundle import ( "crypto/x509" "encoding/base64" "errors" "fmt" "os" "strings" "github.com/secure-systems-lab/go-securesystemslib/dsse" protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" protodsse "github.com/sigstore/protobuf-specs/gen/pb-go/dsse" "golang.org/x/mod/semver" "google.golang.org/protobuf/encoding/protojson" "github.com/sigstore/sigstore-go/pkg/tlog" "github.com/sigstore/sigstore-go/pkg/verify" ) var ErrValidation = errors.New("validation error") var ErrUnsupportedMediaType = fmt.Errorf("%w: unsupported media type", ErrValidation) var ErrEmptyBundle = fmt.Errorf("%w: empty protobuf bundle", ErrValidation) var ErrMissingVerificationMaterial = fmt.Errorf("%w: missing verification material", ErrValidation) var ErrMissingBundleContent = fmt.Errorf("%w: missing bundle content", ErrValidation) var ErrUnimplemented = errors.New("unimplemented") var ErrInvalidAttestation = fmt.Errorf("%w: invalid attestation", ErrValidation) var ErrMissingEnvelope = fmt.Errorf("%w: missing valid envelope", ErrInvalidAttestation) var ErrDecodingJSON = fmt.Errorf("%w: decoding json", ErrInvalidAttestation) var ErrDecodingB64 = fmt.Errorf("%w: decoding base64", ErrInvalidAttestation) const mediaTypeBase = "application/vnd.dev.sigstore.bundle" func ErrValidationError(err error) error { return fmt.Errorf("%w: %w", ErrValidation, err) } type Bundle struct { *protobundle.Bundle hasInclusionPromise bool hasInclusionProof bool } func NewBundle(pbundle *protobundle.Bundle) (*Bundle, error) { bundle := &Bundle{ Bundle: pbundle, hasInclusionPromise: false, hasInclusionProof: false, } err := bundle.validate() if err != nil { return nil, err } return bundle, nil } // Deprecated: use Bundle instead type ProtobufBundle = Bundle // Deprecated: use NewBundle instead func NewProtobufBundle(b *protobundle.Bundle) (*ProtobufBundle, error) { return NewBundle(b) } func (b *Bundle) validate() error { bundleVersion, err := getBundleVersion(b.MediaType) if err != nil { return fmt.Errorf("error getting bundle version: %w", err) } // if bundle version is < 0.1, return error if semver.Compare(bundleVersion, "v0.1") < 0 { return fmt.Errorf("%w: bundle version %s is not supported", ErrUnsupportedMediaType, bundleVersion) } // fetch tlog entries, as next check needs to check them for inclusion proof/promise entries, err := b.TlogEntries() if err != nil { return err } // if bundle version == v0.1, require inclusion promise if semver.Compare(bundleVersion, "v0.1") == 0 { if len(entries) > 0 && !b.hasInclusionPromise { return errors.New("inclusion promises missing in bundle (required for bundle v0.1)") } } else { // if bundle version >= v0.2, require inclusion proof if len(entries) > 0 && !b.hasInclusionProof { return errors.New("inclusion proof missing in bundle (required for bundle v0.2)") } } // if bundle version >= v0.3, require verification material to not be X.509 certificate chain (only single certificate is allowed) if semver.Compare(bundleVersion, "v0.3") >= 0 { certs := b.VerificationMaterial.GetX509CertificateChain() if certs != nil { return errors.New("verification material cannot be X.509 certificate chain (for bundle v0.3)") } } // if bundle version is >= v0.4, return error as this version is not supported if semver.Compare(bundleVersion, "v0.4") >= 0 { return fmt.Errorf("%w: bundle version %s is not yet supported", ErrUnsupportedMediaType, bundleVersion) } err = validateBundle(b.Bundle) if err != nil { return fmt.Errorf("invalid bundle: %w", err) } return nil } // MediaTypeString returns a mediatype string for the specified bundle version. // The function returns an error if the resulting string does validate. func MediaTypeString(version string) (string, error) { if version == "" { return "", fmt.Errorf("unable to build media type string, no version defined") } var mtString string version = strings.TrimPrefix(version, "v") mtString = fmt.Sprintf("%s.v%s+json", mediaTypeBase, strings.TrimPrefix(version, "v")) if version == "0.1" || version == "0.2" { mtString = fmt.Sprintf("%s+json;version=%s", mediaTypeBase, strings.TrimPrefix(version, "v")) } if _, err := getBundleVersion(mtString); err != nil { return "", fmt.Errorf("unable to build mediatype: %w", err) } return mtString, nil } func getBundleVersion(mediaType string) (string, error) { switch mediaType { case mediaTypeBase + "+json;version=0.1": return "v0.1", nil case mediaTypeBase + "+json;version=0.2": return "v0.2", nil case mediaTypeBase + "+json;version=0.3": return "v0.3", nil } if strings.HasPrefix(mediaType, mediaTypeBase+".v") && strings.HasSuffix(mediaType, "+json") { version := strings.TrimPrefix(mediaType, mediaTypeBase+".") version = strings.TrimSuffix(version, "+json") if semver.IsValid(version) { return version, nil } return "", fmt.Errorf("%w: invalid bundle version: %s", ErrUnsupportedMediaType, version) } return "", fmt.Errorf("%w: %s", ErrUnsupportedMediaType, mediaType) } func validateBundle(b *protobundle.Bundle) error { if b == nil { return ErrEmptyBundle } if b.Content == nil { return ErrMissingBundleContent } switch b.Content.(type) { case *protobundle.Bundle_DsseEnvelope, *protobundle.Bundle_MessageSignature: default: return fmt.Errorf("invalid bundle content: bundle content must be either a message signature or dsse envelope") } if b.VerificationMaterial == nil || b.VerificationMaterial.Content == nil { return ErrMissingVerificationMaterial } switch b.VerificationMaterial.Content.(type) { case *protobundle.VerificationMaterial_PublicKey, *protobundle.VerificationMaterial_Certificate, *protobundle.VerificationMaterial_X509CertificateChain: default: return fmt.Errorf("invalid verification material content: verification material must be one of public key, x509 certificate and x509 certificate chain") } return nil } func LoadJSONFromPath(path string) (*Bundle, error) { var bundle Bundle bundle.Bundle = new(protobundle.Bundle) contents, err := os.ReadFile(path) if err != nil { return nil, err } err = bundle.UnmarshalJSON(contents) if err != nil { return nil, err } return &bundle, nil } func (b *Bundle) MarshalJSON() ([]byte, error) { return protojson.Marshal(b.Bundle) } func (b *Bundle) UnmarshalJSON(data []byte) error { b.Bundle = new(protobundle.Bundle) err := protojson.Unmarshal(data, b.Bundle) if err != nil { return err } err = b.validate() if err != nil { return err } return nil } func (b *Bundle) VerificationContent() (verify.VerificationContent, error) { if b.VerificationMaterial == nil { return nil, ErrMissingVerificationMaterial } switch content := b.VerificationMaterial.GetContent().(type) { case *protobundle.VerificationMaterial_X509CertificateChain: if content.X509CertificateChain == nil { return nil, ErrMissingVerificationMaterial } certs := content.X509CertificateChain.GetCertificates() if len(certs) == 0 || certs[0].RawBytes == nil { return nil, ErrMissingVerificationMaterial } parsedCert, err := x509.ParseCertificate(certs[0].RawBytes) if err != nil { return nil, ErrValidationError(err) } cert := &Certificate{ certificate: parsedCert, } return cert, nil case *protobundle.VerificationMaterial_Certificate: if content.Certificate == nil || content.Certificate.RawBytes == nil { return nil, ErrMissingVerificationMaterial } parsedCert, err := x509.ParseCertificate(content.Certificate.RawBytes) if err != nil { return nil, ErrValidationError(err) } cert := &Certificate{ certificate: parsedCert, } return cert, nil case *protobundle.VerificationMaterial_PublicKey: if content.PublicKey == nil { return nil, ErrMissingVerificationMaterial } pk := &PublicKey{ hint: content.PublicKey.Hint, } return pk, nil default: return nil, ErrMissingVerificationMaterial } } func (b *Bundle) HasInclusionPromise() bool { return b.hasInclusionPromise } func (b *Bundle) HasInclusionProof() bool { return b.hasInclusionProof } func (b *Bundle) TlogEntries() ([]*tlog.Entry, error) { if b.VerificationMaterial == nil { return nil, nil } tlogEntries := make([]*tlog.Entry, len(b.VerificationMaterial.TlogEntries)) var err error for i, entry := range b.VerificationMaterial.TlogEntries { tlogEntries[i], err = tlog.ParseEntry(entry) if err != nil { return nil, ErrValidationError(err) } if tlogEntries[i].HasInclusionPromise() { b.hasInclusionPromise = true } if tlogEntries[i].HasInclusionProof() { b.hasInclusionProof = true } } return tlogEntries, nil } func (b *Bundle) SignatureContent() (verify.SignatureContent, error) { switch content := b.Content.(type) { //nolint:gocritic case *protobundle.Bundle_DsseEnvelope: envelope, err := parseEnvelope(content.DsseEnvelope) if err != nil { return nil, err } return envelope, nil case *protobundle.Bundle_MessageSignature: if content.MessageSignature == nil || content.MessageSignature.MessageDigest == nil { return nil, ErrMissingVerificationMaterial } return NewMessageSignature( content.MessageSignature.MessageDigest.Digest, protocommon.HashAlgorithm_name[int32(content.MessageSignature.MessageDigest.Algorithm)], content.MessageSignature.Signature, ), nil } return nil, ErrMissingVerificationMaterial } func (b *Bundle) Envelope() (*Envelope, error) { switch content := b.Content.(type) { //nolint:gocritic case *protobundle.Bundle_DsseEnvelope: envelope, err := parseEnvelope(content.DsseEnvelope) if err != nil { return nil, err } return envelope, nil } return nil, ErrMissingVerificationMaterial } func (b *Bundle) Timestamps() ([][]byte, error) { if b.VerificationMaterial == nil { return nil, ErrMissingVerificationMaterial } signedTimestamps := make([][]byte, 0) if b.VerificationMaterial.TimestampVerificationData == nil { return signedTimestamps, nil } for _, timestamp := range b.VerificationMaterial.TimestampVerificationData.Rfc3161Timestamps { signedTimestamps = append(signedTimestamps, timestamp.SignedTimestamp) } return signedTimestamps, nil } // MinVersion returns true if the bundle version is greater than or equal to the expected version. func (b *Bundle) MinVersion(expectVersion string) bool { version, err := getBundleVersion(b.MediaType) if err != nil { return false } if !strings.HasPrefix(expectVersion, "v") { expectVersion = "v" + expectVersion } return semver.Compare(version, expectVersion) >= 0 } func parseEnvelope(input *protodsse.Envelope) (*Envelope, error) { if input == nil { return nil, ErrMissingEnvelope } output := &dsse.Envelope{} payload := input.GetPayload() if payload == nil { return nil, ErrMissingEnvelope } output.Payload = base64.StdEncoding.EncodeToString([]byte(payload)) output.PayloadType = string(input.GetPayloadType()) output.Signatures = make([]dsse.Signature, len(input.GetSignatures())) for i, sig := range input.GetSignatures() { if sig == nil { return nil, ErrMissingEnvelope } output.Signatures[i].KeyID = sig.GetKeyid() output.Signatures[i].Sig = base64.StdEncoding.EncodeToString(sig.GetSig()) } return &Envelope{Envelope: output}, nil } sigstore-go-0.7.1/pkg/bundle/bundle_test.go000066400000000000000000000726651477477521700207170ustar00rootroot00000000000000// 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 bundle import ( "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/json" "fmt" "math/big" "testing" protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" protodsse "github.com/sigstore/protobuf-specs/gen/pb-go/dsse" rekorv1 "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" _ "github.com/sigstore/rekor/pkg/types/hashedrekord" "github.com/stretchr/testify/require" ) func Test_getBundleVersion(t *testing.T) { tests := []struct { mediaType string want string wantErr bool }{ { mediaType: "application/vnd.dev.sigstore.bundle+json;version=0.1", want: "v0.1", wantErr: false, }, { mediaType: "application/vnd.dev.sigstore.bundle+json;version=0.2", want: "v0.2", wantErr: false, }, { mediaType: "application/vnd.dev.sigstore.bundle+json;version=0.3", want: "v0.3", wantErr: false, }, { mediaType: "application/vnd.dev.sigstore.bundle.v0.3+json", want: "v0.3", wantErr: false, }, { mediaType: "application/vnd.dev.sigstore.bundle.v0.3.1+json", want: "v0.3.1", wantErr: false, }, { mediaType: "application/vnd.dev.sigstore.bundle.v0.4+json", want: "v0.4", wantErr: false, }, { mediaType: "application/vnd.dev.sigstore.bundle+json", want: "", wantErr: true, }, { mediaType: "garbage", want: "", wantErr: true, }, { mediaType: "application/vnd.dev.sigstore.bundle.vgarbage+json", want: "", wantErr: true, }, { mediaType: "application/vnd.dev.sigstore.bundle.v0.3.1.1.1.1+json", want: "", wantErr: true, }, { mediaType: "", want: "", wantErr: true, }, } for _, tt := range tests { t.Run(fmt.Sprintf("mediatype:%s", tt.mediaType), func(t *testing.T) { got, err := getBundleVersion(tt.mediaType) if (err != nil) != tt.wantErr { t.Errorf("getBundleVersion() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { t.Errorf("getBundleVersion() = %v, want %v", got, tt.want) } }) } } func TestMinVersion(t *testing.T) { t.Parallel() for _, tc := range []struct { name string mediaType string expectedVersion string ret bool }{ {"old-format", "application/vnd.dev.sigstore.bundle+json;version=0.1", "v0.1", true}, {"old-format-unexpected", "application/vnd.dev.sigstore.bundle+json;version=0.1", "v0.2", false}, {"old-format-without-v", "application/vnd.dev.sigstore.bundle+json;version=0.1", "0.1", true}, {"new-format", "application/vnd.dev.sigstore.bundle.v0.3+json", "v0.1", true}, {"new-format-exact", "application/vnd.dev.sigstore.bundle.v0.3+json", "v0.3", true}, {"new-format-unexpected", "application/vnd.dev.sigstore.bundle.v0.2+json", "v0.3", false}, {"new-format-without-v", "application/vnd.dev.sigstore.bundle.v0.3+json", "0.3", true}, {"new-format-without-v-unexpected", "application/vnd.dev.sigstore.bundle.v0.2+json", "0.3", false}, {"blank", "", "", false}, {"invalid", "garbage", "v0.1", false}, } { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() b := &Bundle{Bundle: &protobundle.Bundle{ MediaType: tc.mediaType, }} ret := b.MinVersion(tc.expectedVersion) if tc.ret != ret { t.Fatalf("expected %v, got %v", tc.ret, ret) } }) } } func TestMediaTypeString(t *testing.T) { t.Parallel() for _, tc := range []struct { name string ver string expected string mustErr bool }{ {"normal-semver", "v0.3", "application/vnd.dev.sigstore.bundle.v0.3+json", false}, {"old-semver1", "v0.1", "application/vnd.dev.sigstore.bundle+json;version=0.1", false}, {"old-semver2", "v0.2", "application/vnd.dev.sigstore.bundle+json;version=0.2", false}, {"blank", "", "", true}, {"invalid", "garbage", "", true}, } { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() res, err := MediaTypeString(tc.ver) if tc.mustErr { require.Error(t, err) return } require.NoError(t, err) require.Equal(t, tc.expected, res) }) } } func Test_validate(t *testing.T) { t.Parallel() tlogBody := map[string]any{ "kind": "hashedrekord", "apiVersion": "0.0.1", "spec": map[string]any{ "signature": map[string]any{ "content": "sn/VqLMqWjDeYt93XTb6LzWIsKIn5bOvEsZQyF1elkvpur85LoDk5q/ExGWBB0Y+v8q0B04Bg2xGMOVMNyD/LQ==", "publicKey": map[string]any{ "content": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJnekNDQVMyZ0F3SUJBZ0lVS2cxZHN1OTBoS0daVW5WN1RRWFZPRjdOZCtrd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xhblZ6ZEhSeWRYTjBiV1V3SGhjTk1qUXdOakkwTWpJMU5USXpXaGNOTXpRdwpOakl5TWpJMU5USXpXakFXTVJRd0VnWURWUVFEREF0cWRYTjBkSEoxYzNSdFpUQmNNQTBHQ1NxR1NJYjNEUUVCCkFRVUFBMHNBTUVnQ1FRRGIwNjhSMkpYNStZSE5nZWVyeDlzM1k2eEp2ZVdPRGl3YnROZWtKaytTWUlDUjNYQlQKaDErNUJ1SStwTGNyTXNyQTZlOThaNkNxUkJjNDdEL05LdWgvQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXQkJTbgpKbExuNWZjeXYzNnlibHBKYTVkcmdhQlNBREFmQmdOVkhTTUVHREFXZ0JTbkpsTG41ZmN5djM2eWJscEphNWRyCmdhQlNBREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQTBHQ1NxR1NJYjNEUUVCQ3dVQUEwRUFaaTNCMTF4VDY5TjQKNnl4ODg5Rkl2Z0xIdjQvaUROR2JTUkpHanlXMXY1RFpscXBBT0dYWjc5V3d2TFJZQlAxbFhid0tGaGlzTlNsUwpNRk84c0FHZ1hRPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=", }, }, "data": map[string]any{ "hash": map[string]any{ "algorithm": "sha256", "value": "bc103b4a84971ef6459b294a2b98568a2bfb72cded09d4acd1e16366a401f95b", }, }, }, } canonicalTlogBody, err := json.Marshal(tlogBody) require.NoError(t, err) tests := []struct { name string pb Bundle wantErr bool }{ { name: "invalid media type", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "", }, }, wantErr: true, }, { name: "version too low", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle.v0.0.1+json", }, }, wantErr: true, }, { name: "version too high", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.4", }, }, wantErr: true, }, { name: "no verification material", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.1", }, }, wantErr: true, }, { name: "v0.1 with no inclusion promise", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.1", VerificationMaterial: &protobundle.VerificationMaterial{ TlogEntries: []*rekorv1.TransparencyLogEntry{ { LogIndex: 42, LogId: &protocommon.LogId{ KeyId: []byte("deadbeef"), }, KindVersion: &rekorv1.KindVersion{ Kind: "hashedrekord", Version: "0.0.1", }, IntegratedTime: 1, CanonicalizedBody: canonicalTlogBody, }, }, }, }, }, wantErr: true, }, { name: "v0.1 with inclusion promise", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.1", VerificationMaterial: &protobundle.VerificationMaterial{ TlogEntries: []*rekorv1.TransparencyLogEntry{ { LogIndex: 42, LogId: &protocommon.LogId{ KeyId: []byte("deadbeef"), }, KindVersion: &rekorv1.KindVersion{ Kind: "hashedrekord", Version: "0.0.1", }, IntegratedTime: 1, CanonicalizedBody: canonicalTlogBody, InclusionPromise: &rekorv1.InclusionPromise{ SignedEntryTimestamp: []byte("1"), }, }, }, Content: &protobundle.VerificationMaterial_PublicKey{ PublicKey: &protocommon.PublicKeyIdentifier{}, }, }, Content: &protobundle.Bundle_MessageSignature{}, }, }, }, { name: "v0.1 with inclusion promise & proof without checkpoint", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.1", VerificationMaterial: &protobundle.VerificationMaterial{ TlogEntries: []*rekorv1.TransparencyLogEntry{ { LogIndex: 42, LogId: &protocommon.LogId{ KeyId: []byte("deadbeef"), }, KindVersion: &rekorv1.KindVersion{ Kind: "hashedrekord", Version: "0.0.1", }, IntegratedTime: 1, CanonicalizedBody: canonicalTlogBody, InclusionProof: &rekorv1.InclusionProof{ LogIndex: 42, RootHash: []byte("b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"), }, InclusionPromise: &rekorv1.InclusionPromise{ SignedEntryTimestamp: []byte("1"), }, }, }, Content: &protobundle.VerificationMaterial_PublicKey{ PublicKey: &protocommon.PublicKeyIdentifier{}, }, }, Content: &protobundle.Bundle_MessageSignature{}, }, }, wantErr: true, }, { name: "v0.1 with inclusion proof & promise", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.1", VerificationMaterial: &protobundle.VerificationMaterial{ TlogEntries: []*rekorv1.TransparencyLogEntry{ { LogIndex: 42, LogId: &protocommon.LogId{ KeyId: []byte("deadbeef"), }, KindVersion: &rekorv1.KindVersion{ Kind: "hashedrekord", Version: "0.0.1", }, IntegratedTime: 1, CanonicalizedBody: canonicalTlogBody, InclusionProof: &rekorv1.InclusionProof{ LogIndex: 42, RootHash: []byte("b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"), Checkpoint: &rekorv1.Checkpoint{Envelope: "checkpoint"}, }, InclusionPromise: &rekorv1.InclusionPromise{ SignedEntryTimestamp: []byte("1"), }, }, }, Content: &protobundle.VerificationMaterial_PublicKey{ PublicKey: &protocommon.PublicKeyIdentifier{}, }, }, Content: &protobundle.Bundle_MessageSignature{}, }, }, }, { name: "v0.2 with no inclusion proof", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.2", VerificationMaterial: &protobundle.VerificationMaterial{ TlogEntries: []*rekorv1.TransparencyLogEntry{ { LogIndex: 42, LogId: &protocommon.LogId{ KeyId: []byte("deadbeef"), }, KindVersion: &rekorv1.KindVersion{ Kind: "hashedrekord", Version: "0.0.1", }, IntegratedTime: 1, CanonicalizedBody: canonicalTlogBody, }, }, Content: &protobundle.VerificationMaterial_PublicKey{ PublicKey: &protocommon.PublicKeyIdentifier{}, }, }, Content: &protobundle.Bundle_MessageSignature{}, }, }, wantErr: true, }, { name: "v0.2 with inclusion proof without checkpoint", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.2", VerificationMaterial: &protobundle.VerificationMaterial{ TlogEntries: []*rekorv1.TransparencyLogEntry{ { LogIndex: 42, LogId: &protocommon.LogId{ KeyId: []byte("deadbeef"), }, KindVersion: &rekorv1.KindVersion{ Kind: "hashedrekord", Version: "0.0.1", }, IntegratedTime: 1, CanonicalizedBody: canonicalTlogBody, InclusionProof: &rekorv1.InclusionProof{ LogIndex: 42, RootHash: []byte("b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"), }, }, }, Content: &protobundle.VerificationMaterial_PublicKey{ PublicKey: &protocommon.PublicKeyIdentifier{}, }, }, Content: &protobundle.Bundle_MessageSignature{}, }, }, wantErr: true, }, { name: "v0.2 with inclusion proof with empty checkpoint", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.2", VerificationMaterial: &protobundle.VerificationMaterial{ TlogEntries: []*rekorv1.TransparencyLogEntry{ { LogIndex: 42, LogId: &protocommon.LogId{ KeyId: []byte("deadbeef"), }, KindVersion: &rekorv1.KindVersion{ Kind: "hashedrekord", Version: "0.0.1", }, IntegratedTime: 1, CanonicalizedBody: canonicalTlogBody, InclusionProof: &rekorv1.InclusionProof{ LogIndex: 42, RootHash: []byte("b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"), Checkpoint: &rekorv1.Checkpoint{}, }, }, }, Content: &protobundle.VerificationMaterial_PublicKey{ PublicKey: &protocommon.PublicKeyIdentifier{}, }, }, Content: &protobundle.Bundle_MessageSignature{}, }, }, wantErr: true, }, { name: "v0.2 with inclusion proof", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.2", VerificationMaterial: &protobundle.VerificationMaterial{ TlogEntries: []*rekorv1.TransparencyLogEntry{ { LogIndex: 42, LogId: &protocommon.LogId{ KeyId: []byte("deadbeef"), }, KindVersion: &rekorv1.KindVersion{ Kind: "hashedrekord", Version: "0.0.1", }, IntegratedTime: 1, CanonicalizedBody: canonicalTlogBody, InclusionProof: &rekorv1.InclusionProof{ LogIndex: 42, RootHash: []byte("b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"), Checkpoint: &rekorv1.Checkpoint{Envelope: "checkpoint"}, }, }, }, Content: &protobundle.VerificationMaterial_PublicKey{ PublicKey: &protocommon.PublicKeyIdentifier{}, }, }, Content: &protobundle.Bundle_MessageSignature{}, }, }, }, { name: "v0.3 with x.509 certificate chain", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.3", VerificationMaterial: &protobundle.VerificationMaterial{ TlogEntries: []*rekorv1.TransparencyLogEntry{ { LogIndex: 42, LogId: &protocommon.LogId{ KeyId: []byte("deadbeef"), }, KindVersion: &rekorv1.KindVersion{ Kind: "hashedrekord", Version: "0.0.1", }, IntegratedTime: 1, CanonicalizedBody: canonicalTlogBody, InclusionProof: &rekorv1.InclusionProof{ LogIndex: 42, RootHash: []byte("b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"), Checkpoint: &rekorv1.Checkpoint{Envelope: "checkpoint"}, }, }, }, Content: &protobundle.VerificationMaterial_X509CertificateChain{ X509CertificateChain: &protocommon.X509CertificateChain{}, }, }, Content: &protobundle.Bundle_MessageSignature{}, }, }, wantErr: true, }, { name: "v0.3 without x.509 certificate chain", pb: Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.3", VerificationMaterial: &protobundle.VerificationMaterial{ TlogEntries: []*rekorv1.TransparencyLogEntry{ { LogIndex: 42, LogId: &protocommon.LogId{ KeyId: []byte("deadbeef"), }, KindVersion: &rekorv1.KindVersion{ Kind: "hashedrekord", Version: "0.0.1", }, IntegratedTime: 1, CanonicalizedBody: canonicalTlogBody, InclusionProof: &rekorv1.InclusionProof{ LogIndex: 42, RootHash: []byte("b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"), Checkpoint: &rekorv1.Checkpoint{Envelope: "checkpoint"}, }, }, }, Content: &protobundle.VerificationMaterial_Certificate{ Certificate: &protocommon.X509Certificate{}, }, }, Content: &protobundle.Bundle_MessageSignature{}, }, }, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { got := tt.pb.validate() if (got != nil) != tt.wantErr { t.Errorf("validate() error = %v, wantErr = %v", got, tt.wantErr) } }) } } func TestVerificationContent(t *testing.T) { t.Parallel() caCert := &x509.Certificate{ SerialNumber: big.NewInt(1), } leafCert := &x509.Certificate{ SerialNumber: big.NewInt(2), } caKey, err := rsa.GenerateKey(rand.Reader, 512) //nolint:gosec require.NoError(t, err) leafKey, err := rsa.GenerateKey(rand.Reader, 512) //nolint:gosec require.NoError(t, err) caDer, err := x509.CreateCertificate(rand.Reader, caCert, caCert, &caKey.PublicKey, caKey) require.NoError(t, err) leafDer, err := x509.CreateCertificate(rand.Reader, leafCert, caCert, &leafKey.PublicKey, caKey) require.NoError(t, err) tests := []struct { name string pb Bundle wantCertificate bool wantPublicKey bool wantErr bool }{ { name: "no verification material", pb: Bundle{ Bundle: &protobundle.Bundle{}, }, wantErr: true, }, { name: "certificate chain with zero certs", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_X509CertificateChain{ X509CertificateChain: &protocommon.X509CertificateChain{}, }, }, }, }, wantErr: true, }, { name: "certificate chain with self-signed cert", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_X509CertificateChain{ X509CertificateChain: &protocommon.X509CertificateChain{ Certificates: []*protocommon.X509Certificate{ { RawBytes: caDer, }, }, }, }, }, }, }, wantCertificate: true, }, { name: "certificate chain", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_X509CertificateChain{ X509CertificateChain: &protocommon.X509CertificateChain{ Certificates: []*protocommon.X509Certificate{ { RawBytes: leafDer, }, { RawBytes: caDer, }, }, }, }, }, }, }, wantCertificate: true, }, { name: "certificate chain with invalid cert", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_X509CertificateChain{ X509CertificateChain: &protocommon.X509CertificateChain{ Certificates: []*protocommon.X509Certificate{ { RawBytes: []byte("hello"), }, }, }, }, }, }, }, wantErr: true, }, { name: "certificate chain with nil bytes", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_X509CertificateChain{ X509CertificateChain: &protocommon.X509CertificateChain{ Certificates: []*protocommon.X509Certificate{ { RawBytes: nil, }, }, }, }, }, }, }, wantErr: true, }, { name: "certificate chain with nil cert", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_X509CertificateChain{ X509CertificateChain: &protocommon.X509CertificateChain{ Certificates: nil, }, }, }, }, }, wantErr: true, }, { name: "certificate chain with nil chain", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_X509CertificateChain{ X509CertificateChain: nil, }, }, }, }, wantErr: true, }, { name: "certificate", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_Certificate{ Certificate: &protocommon.X509Certificate{ RawBytes: leafDer, }, }, }, }, }, wantCertificate: true, }, { name: "invalid certificate", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_Certificate{ Certificate: &protocommon.X509Certificate{ RawBytes: []byte("hello"), }, }, }, }, }, wantErr: true, }, { name: "certificate with nil bytes", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_Certificate{ Certificate: &protocommon.X509Certificate{ RawBytes: nil, }, }, }, }, }, wantErr: true, }, { name: "empty certificate", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_Certificate{ Certificate: &protocommon.X509Certificate{ RawBytes: nil, }, }, }, }, }, wantErr: true, }, { name: "public key", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_PublicKey{ PublicKey: &protocommon.PublicKeyIdentifier{}, }, }, }, }, wantPublicKey: true, }, { name: "nil public key", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_PublicKey{ PublicKey: nil, }, }, }, }, wantErr: true, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { got, gotErr := tt.pb.VerificationContent() if tt.wantErr { require.Error(t, gotErr) return } require.NoError(t, gotErr) if tt.wantCertificate { require.NotNil(t, got.Certificate()) return } if tt.wantPublicKey { k := got.PublicKey() require.NotNil(t, k) return } }) } } func TestSignatureContent(t *testing.T) { t.Parallel() tests := []struct { name string pb Bundle wantEnvelope bool wantSignature bool wantErr bool }{ { name: "dsse envelope", pb: Bundle{ Bundle: &protobundle.Bundle{ Content: &protobundle.Bundle_DsseEnvelope{ DsseEnvelope: &protodsse.Envelope{ Payload: []byte{}, Signatures: []*protodsse.Signature{{Sig: []byte{}, Keyid: ""}}, }, }, }, }, wantEnvelope: true, }, { name: "dsse envelope with nil signature", pb: Bundle{ Bundle: &protobundle.Bundle{ Content: &protobundle.Bundle_DsseEnvelope{ DsseEnvelope: &protodsse.Envelope{ Payload: []byte{}, Signatures: []*protodsse.Signature{nil}, }, }, }, }, wantErr: true, }, { name: "dsse envelope with nil payload", pb: Bundle{ Bundle: &protobundle.Bundle{ Content: &protobundle.Bundle_DsseEnvelope{ DsseEnvelope: &protodsse.Envelope{ Payload: nil, Signatures: []*protodsse.Signature{{Sig: []byte{}, Keyid: ""}}, }, }, }, }, wantErr: true, }, { name: "message signature", pb: Bundle{ Bundle: &protobundle.Bundle{ Content: &protobundle.Bundle_MessageSignature{ MessageSignature: &protocommon.MessageSignature{ MessageDigest: &protocommon.HashOutput{}, }, }, }, }, wantSignature: true, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { got, gotErr := tt.pb.SignatureContent() if tt.wantErr { require.Error(t, gotErr) return } require.NoError(t, gotErr) if tt.wantEnvelope { require.NotNil(t, got.EnvelopeContent()) return } if tt.wantSignature { require.NotNil(t, got.MessageSignatureContent()) return } }) } } func TestEnvelope(t *testing.T) { t.Parallel() tests := []struct { name string pb Bundle wantErr bool }{ { name: "dsse envelope", pb: Bundle{ Bundle: &protobundle.Bundle{ Content: &protobundle.Bundle_DsseEnvelope{ DsseEnvelope: &protodsse.Envelope{ Payload: []byte{}, Signatures: []*protodsse.Signature{{Sig: []byte{}, Keyid: ""}}, }, }, }, }, }, { name: "message signature", pb: Bundle{ Bundle: &protobundle.Bundle{ Content: &protobundle.Bundle_MessageSignature{ MessageSignature: &protocommon.MessageSignature{ MessageDigest: &protocommon.HashOutput{}, }, }, }, }, wantErr: true, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { _, gotErr := tt.pb.Envelope() if tt.wantErr { require.Error(t, gotErr) return } require.NoError(t, gotErr) }) } } func TestTimestamps(t *testing.T) { t.Parallel() tests := []struct { name string pb Bundle wantTimestamps [][]byte wantErr bool }{ { name: "missing verification material", pb: Bundle{Bundle: &protobundle.Bundle{}}, wantErr: true, }, { name: "empty timestamp data", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{}, }, }, wantTimestamps: make([][]byte, 0), }, { name: "one timestamp", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ TimestampVerificationData: &protobundle.TimestampVerificationData{ Rfc3161Timestamps: []*protocommon.RFC3161SignedTimestamp{ { SignedTimestamp: []byte("sometime yesterday"), }, }, }, }, }, }, wantTimestamps: [][]byte{ []byte("sometime yesterday"), }, }, { name: "multiple timestamps", pb: Bundle{ Bundle: &protobundle.Bundle{ VerificationMaterial: &protobundle.VerificationMaterial{ TimestampVerificationData: &protobundle.TimestampVerificationData{ Rfc3161Timestamps: []*protocommon.RFC3161SignedTimestamp{ { SignedTimestamp: []byte("sometime yesterday"), }, { SignedTimestamp: []byte("last week"), }, }, }, }, }, }, wantTimestamps: [][]byte{ []byte("sometime yesterday"), []byte("last week"), }, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { got, gotErr := tt.pb.Timestamps() if tt.wantErr { require.Error(t, gotErr) return } require.NoError(t, gotErr) require.Equal(t, tt.wantTimestamps, got) }) } } func Test_BundleValidation(t *testing.T) { tests := []struct { name string bundle *Bundle errMsg string wantErr bool }{ { name: "Empty verification material", bundle: &Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.3", VerificationMaterial: &protobundle.VerificationMaterial{ Content: nil, }, Content: &protobundle.Bundle_MessageSignature{}, }, }, errMsg: "invalid bundle: validation error: missing verification material", wantErr: true, }, { name: "No bundle content", bundle: &Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.3", Content: nil, }, }, errMsg: "invalid bundle: validation error: missing bundle content", wantErr: true, }, { name: "Nil verification material", bundle: &Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.3", Content: &protobundle.Bundle_MessageSignature{}, VerificationMaterial: nil, }, }, errMsg: "invalid bundle: validation error: missing verification material", wantErr: true, }, { name: "Valid protobuf bundle", bundle: &Bundle{ Bundle: &protobundle.Bundle{ MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.3", Content: &protobundle.Bundle_DsseEnvelope{}, VerificationMaterial: &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_PublicKey{ PublicKey: &protocommon.PublicKeyIdentifier{}, }, TimestampVerificationData: &protobundle.TimestampVerificationData{}, }, }, }, errMsg: "", wantErr: false, }, } for _, tt := range tests { t.Run(fmt.Sprintf("name:%s", tt.name), func(t *testing.T) { err := tt.bundle.validate() if (err != nil) != tt.wantErr || (err != nil && tt.errMsg != err.Error()) { t.Errorf("Protobuf.Bundle() error = %v, wantErr %v", err, tt.errMsg) return } }) } } sigstore-go-0.7.1/pkg/bundle/fuzz_test.go000066400000000000000000000025611477477521700204300ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package bundle import ( "testing" ) /* FuzzBundle creates a randomized bundle and invokes three of its methods */ //nolint:errcheck func FuzzBundle(f *testing.F) { f.Fuzz(func(t *testing.T, bundleData []byte, call1, call2, call3 int, expectVersion string) { var bundle Bundle err := bundle.UnmarshalJSON(bundleData) if err != nil { t.Skip() } calls := []int{call1, call2, call3} for _, call := range calls { switch call % 8 { case 0: bundle.VerificationContent() case 1: bundle.HasInclusionPromise() case 2: bundle.HasInclusionProof() case 3: bundle.TlogEntries() case 4: bundle.SignatureContent() case 5: bundle.Envelope() case 6: bundle.Timestamps() case 7: bundle.MinVersion(expectVersion) } } }) } sigstore-go-0.7.1/pkg/bundle/signature_content.go000066400000000000000000000047321477477521700221300ustar00rootroot00000000000000// 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 bundle import ( "encoding/base64" in_toto "github.com/in-toto/attestation/go/v1" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/sigstore-go/pkg/verify" "google.golang.org/protobuf/encoding/protojson" ) const IntotoMediaType = "application/vnd.in-toto+json" type MessageSignature struct { digest []byte digestAlgorithm string signature []byte } func (m *MessageSignature) Digest() []byte { return m.digest } func (m *MessageSignature) DigestAlgorithm() string { return m.digestAlgorithm } func NewMessageSignature(digest []byte, digestAlgorithm string, signature []byte) *MessageSignature { return &MessageSignature{ digest: digest, digestAlgorithm: digestAlgorithm, signature: signature, } } type Envelope struct { *dsse.Envelope } func (e *Envelope) Statement() (*in_toto.Statement, error) { if e.PayloadType != IntotoMediaType { return nil, ErrUnsupportedMediaType } var statement in_toto.Statement raw, err := e.DecodeB64Payload() if err != nil { return nil, ErrDecodingB64 } err = protojson.Unmarshal(raw, &statement) if err != nil { return nil, ErrDecodingJSON } return &statement, nil } func (e *Envelope) EnvelopeContent() verify.EnvelopeContent { return e } func (e *Envelope) RawEnvelope() *dsse.Envelope { return e.Envelope } func (m *MessageSignature) EnvelopeContent() verify.EnvelopeContent { return nil } func (e *Envelope) MessageSignatureContent() verify.MessageSignatureContent { return nil } func (m *MessageSignature) MessageSignatureContent() verify.MessageSignatureContent { return m } func (m *MessageSignature) Signature() []byte { return m.signature } func (e *Envelope) Signature() []byte { if len(e.Signatures) == 0 { return []byte{} } sigBytes, err := base64.StdEncoding.DecodeString(e.Signatures[0].Sig) if err != nil { return []byte{} } return sigBytes } sigstore-go-0.7.1/pkg/bundle/verification_content.go000066400000000000000000000042301477477521700226020ustar00rootroot00000000000000// 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 bundle import ( "crypto" "crypto/x509" "time" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/verify" ) type Certificate struct { certificate *x509.Certificate } func NewCertificate(cert *x509.Certificate) *Certificate { return &Certificate{certificate: cert} } type PublicKey struct { hint string } func (pk PublicKey) Hint() string { return pk.hint } func (c *Certificate) CompareKey(key any, _ root.TrustedMaterial) bool { x509Key, ok := key.(*x509.Certificate) if !ok { return false } return c.certificate.Equal(x509Key) } func (c *Certificate) ValidAtTime(t time.Time, _ root.TrustedMaterial) bool { return !c.certificate.NotAfter.Before(t) && !c.certificate.NotBefore.After(t) } func (c *Certificate) Certificate() *x509.Certificate { return c.certificate } func (c *Certificate) PublicKey() verify.PublicKeyProvider { return nil } func (pk *PublicKey) CompareKey(key any, tm root.TrustedMaterial) bool { verifier, err := tm.PublicKeyVerifier(pk.hint) if err != nil { return false } pubKey, err := verifier.PublicKey() if err != nil { return false } if equaler, ok := key.(interface{ Equal(x crypto.PublicKey) bool }); ok { return equaler.Equal(pubKey) } return false } func (pk *PublicKey) ValidAtTime(t time.Time, tm root.TrustedMaterial) bool { verifier, err := tm.PublicKeyVerifier(pk.hint) if err != nil { return false } return verifier.ValidAtTime(t) } func (pk *PublicKey) Certificate() *x509.Certificate { return nil } func (pk *PublicKey) PublicKey() verify.PublicKeyProvider { return pk } sigstore-go-0.7.1/pkg/fulcio/000077500000000000000000000000001477477521700160505ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/fulcio/certificate/000077500000000000000000000000001477477521700203325ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/fulcio/certificate/extensions.go000066400000000000000000000255111477477521700230640ustar00rootroot00000000000000// 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. // This file is a verbatim copy of https://github.com/sigstore/fulcio/blob/3707d80bb25330bc7ffbd9702fb401cd643e36fa/pkg/certificate/extensions.go , // EXCEPT: // - the parseExtensions func has been renamed ParseExtensions package certificate import ( "crypto/x509/pkix" "encoding/asn1" "errors" "fmt" ) var ( // Deprecated: Use OIDIssuerV2 OIDIssuer = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 1} // Deprecated: Use OIDBuildTrigger OIDGitHubWorkflowTrigger = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 2} // Deprecated: Use OIDSourceRepositoryDigest OIDGitHubWorkflowSHA = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 3} // Deprecated: Use OIDBuildConfigURI or OIDBuildConfigDigest OIDGitHubWorkflowName = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 4} // Deprecated: Use SourceRepositoryURI OIDGitHubWorkflowRepository = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 5} // Deprecated: Use OIDSourceRepositoryRef OIDGitHubWorkflowRef = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 6} OIDOtherName = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 7} OIDIssuerV2 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 8} // CI extensions OIDBuildSignerURI = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 9} OIDBuildSignerDigest = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 10} OIDRunnerEnvironment = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 11} OIDSourceRepositoryURI = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 12} OIDSourceRepositoryDigest = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 13} OIDSourceRepositoryRef = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 14} OIDSourceRepositoryIdentifier = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 15} OIDSourceRepositoryOwnerURI = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 16} OIDSourceRepositoryOwnerIdentifier = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 17} OIDBuildConfigURI = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 18} OIDBuildConfigDigest = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 19} OIDBuildTrigger = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 20} OIDRunInvocationURI = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 21} OIDSourceRepositoryVisibilityAtSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 22} ) // Extensions contains all custom x509 extensions defined by Fulcio type Extensions struct { // NB: New extensions must be added here and documented // at docs/oidc-info.md // The OIDC issuer. Should match `iss` claim of ID token or, in the case of // a federated login like Dex it should match the issuer URL of the // upstream issuer. The issuer is not set the extensions are invalid and // will fail to render. Issuer string `json:"issuer,omitempty"` // OID 1.3.6.1.4.1.57264.1.8 and 1.3.6.1.4.1.57264.1.1 (Deprecated) // Deprecated // Triggering event of the Github Workflow. Matches the `event_name` claim of ID // tokens from Github Actions GithubWorkflowTrigger string `json:"githubWorkflowTrigger,omitempty"` // OID 1.3.6.1.4.1.57264.1.2 // Deprecated // SHA of git commit being built in Github Actions. Matches the `sha` claim of ID // tokens from Github Actions GithubWorkflowSHA string `json:"githubWorkflowSHA,omitempty"` //nolint:tagliatelle // OID 1.3.6.1.4.1.57264.1.3 // Deprecated // Name of Github Actions Workflow. Matches the `workflow` claim of the ID // tokens from Github Actions GithubWorkflowName string `json:"githubWorkflowName,omitempty"` // OID 1.3.6.1.4.1.57264.1.4 // Deprecated // Repository of the Github Actions Workflow. Matches the `repository` claim of the ID // tokens from Github Actions GithubWorkflowRepository string `json:"githubWorkflowRepository,omitempty"` // OID 1.3.6.1.4.1.57264.1.5 // Deprecated // Git Ref of the Github Actions Workflow. Matches the `ref` claim of the ID tokens // from Github Actions GithubWorkflowRef string `json:"githubWorkflowRef,omitempty"` // 1.3.6.1.4.1.57264.1.6 // Reference to specific build instructions that are responsible for signing. BuildSignerURI string `json:"buildSignerURI,omitempty"` //nolint:tagliatelle // 1.3.6.1.4.1.57264.1.9 // Immutable reference to the specific version of the build instructions that is responsible for signing. BuildSignerDigest string `json:"buildSignerDigest,omitempty"` // 1.3.6.1.4.1.57264.1.10 // Specifies whether the build took place in platform-hosted cloud infrastructure or customer/self-hosted infrastructure. RunnerEnvironment string `json:"runnerEnvironment,omitempty"` // 1.3.6.1.4.1.57264.1.11 // Source repository URL that the build was based on. SourceRepositoryURI string `json:"sourceRepositoryURI,omitempty"` //nolint:tagliatelle // 1.3.6.1.4.1.57264.1.12 // Immutable reference to a specific version of the source code that the build was based upon. SourceRepositoryDigest string `json:"sourceRepositoryDigest,omitempty"` // 1.3.6.1.4.1.57264.1.13 // Source Repository Ref that the build run was based upon. SourceRepositoryRef string `json:"sourceRepositoryRef,omitempty"` // 1.3.6.1.4.1.57264.1.14 // Immutable identifier for the source repository the workflow was based upon. SourceRepositoryIdentifier string `json:"sourceRepositoryIdentifier,omitempty"` // 1.3.6.1.4.1.57264.1.15 // Source repository owner URL of the owner of the source repository that the build was based on. SourceRepositoryOwnerURI string `json:"sourceRepositoryOwnerURI,omitempty"` //nolint:tagliatelle // 1.3.6.1.4.1.57264.1.16 // Immutable identifier for the owner of the source repository that the workflow was based upon. SourceRepositoryOwnerIdentifier string `json:"sourceRepositoryOwnerIdentifier,omitempty"` // 1.3.6.1.4.1.57264.1.17 // Build Config URL to the top-level/initiating build instructions. BuildConfigURI string `json:"buildConfigURI,omitempty"` //nolint:tagliatelle // 1.3.6.1.4.1.57264.1.18 // Immutable reference to the specific version of the top-level/initiating build instructions. BuildConfigDigest string `json:"buildConfigDigest,omitempty"` // 1.3.6.1.4.1.57264.1.19 // Event or action that initiated the build. BuildTrigger string `json:"buildTrigger,omitempty"` // 1.3.6.1.4.1.57264.1.20 // Run Invocation URL to uniquely identify the build execution. RunInvocationURI string `json:"runInvocationURI,omitempty"` //nolint:tagliatelle // 1.3.6.1.4.1.57264.1.21 // Source repository visibility at the time of signing the certificate. SourceRepositoryVisibilityAtSigning string `json:"sourceRepositoryVisibilityAtSigning,omitempty"` // 1.3.6.1.4.1.57264.1.22 } func ParseExtensions(ext []pkix.Extension) (Extensions, error) { out := Extensions{} for _, e := range ext { switch { // BEGIN: Deprecated case e.Id.Equal(OIDIssuer): out.Issuer = string(e.Value) case e.Id.Equal(OIDGitHubWorkflowTrigger): out.GithubWorkflowTrigger = string(e.Value) case e.Id.Equal(OIDGitHubWorkflowSHA): out.GithubWorkflowSHA = string(e.Value) case e.Id.Equal(OIDGitHubWorkflowName): out.GithubWorkflowName = string(e.Value) case e.Id.Equal(OIDGitHubWorkflowRepository): out.GithubWorkflowRepository = string(e.Value) case e.Id.Equal(OIDGitHubWorkflowRef): out.GithubWorkflowRef = string(e.Value) // END: Deprecated case e.Id.Equal(OIDIssuerV2): if err := ParseDERString(e.Value, &out.Issuer); err != nil { return Extensions{}, err } case e.Id.Equal(OIDBuildSignerURI): if err := ParseDERString(e.Value, &out.BuildSignerURI); err != nil { return Extensions{}, err } case e.Id.Equal(OIDBuildSignerDigest): if err := ParseDERString(e.Value, &out.BuildSignerDigest); err != nil { return Extensions{}, err } case e.Id.Equal(OIDRunnerEnvironment): if err := ParseDERString(e.Value, &out.RunnerEnvironment); err != nil { return Extensions{}, err } case e.Id.Equal(OIDSourceRepositoryURI): if err := ParseDERString(e.Value, &out.SourceRepositoryURI); err != nil { return Extensions{}, err } case e.Id.Equal(OIDSourceRepositoryDigest): if err := ParseDERString(e.Value, &out.SourceRepositoryDigest); err != nil { return Extensions{}, err } case e.Id.Equal(OIDSourceRepositoryRef): if err := ParseDERString(e.Value, &out.SourceRepositoryRef); err != nil { return Extensions{}, err } case e.Id.Equal(OIDSourceRepositoryIdentifier): if err := ParseDERString(e.Value, &out.SourceRepositoryIdentifier); err != nil { return Extensions{}, err } case e.Id.Equal(OIDSourceRepositoryOwnerURI): if err := ParseDERString(e.Value, &out.SourceRepositoryOwnerURI); err != nil { return Extensions{}, err } case e.Id.Equal(OIDSourceRepositoryOwnerIdentifier): if err := ParseDERString(e.Value, &out.SourceRepositoryOwnerIdentifier); err != nil { return Extensions{}, err } case e.Id.Equal(OIDBuildConfigURI): if err := ParseDERString(e.Value, &out.BuildConfigURI); err != nil { return Extensions{}, err } case e.Id.Equal(OIDBuildConfigDigest): if err := ParseDERString(e.Value, &out.BuildConfigDigest); err != nil { return Extensions{}, err } case e.Id.Equal(OIDBuildTrigger): if err := ParseDERString(e.Value, &out.BuildTrigger); err != nil { return Extensions{}, err } case e.Id.Equal(OIDRunInvocationURI): if err := ParseDERString(e.Value, &out.RunInvocationURI); err != nil { return Extensions{}, err } case e.Id.Equal(OIDSourceRepositoryVisibilityAtSigning): if err := ParseDERString(e.Value, &out.SourceRepositoryVisibilityAtSigning); err != nil { return Extensions{}, err } } } // We only ever return nil, but leaving error in place so that we can add // more complex parsing of fields in a backwards compatible way if needed. return out, nil } // ParseDERString decodes a DER-encoded string and puts the value in parsedVal. // Returns an error if the unmarshalling fails or if there are trailing bytes in the encoding. func ParseDERString(val []byte, parsedVal *string) error { rest, err := asn1.Unmarshal(val, parsedVal) if err != nil { return fmt.Errorf("unexpected error unmarshalling DER-encoded string: %w", err) } if len(rest) != 0 { return errors.New("unexpected trailing bytes in DER-encoded string") } return nil } sigstore-go-0.7.1/pkg/fulcio/certificate/summarize.go000066400000000000000000000051261477477521700227010ustar00rootroot00000000000000// 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 certificate import ( "crypto/x509" "errors" "fmt" "reflect" "github.com/sigstore/sigstore/pkg/cryptoutils" ) type Summary struct { CertificateIssuer string `json:"certificateIssuer"` SubjectAlternativeName string `json:"subjectAlternativeName"` Extensions } type ErrCompareExtensions struct { field string expected string actual string } func (e *ErrCompareExtensions) Error() string { return fmt.Sprintf("expected %s to be \"%s\", got \"%s\"", e.field, e.expected, e.actual) } func SummarizeCertificate(cert *x509.Certificate) (Summary, error) { extensions, err := ParseExtensions(cert.Extensions) if err != nil { return Summary{}, err } var san string switch { case len(cert.URIs) > 0: san = cert.URIs[0].String() case len(cert.EmailAddresses) > 0: san = cert.EmailAddresses[0] } if san == "" { san, _ = cryptoutils.UnmarshalOtherNameSAN(cert.Extensions) } if san == "" { return Summary{}, errors.New("no Subject Alternative Name found") } return Summary{CertificateIssuer: cert.Issuer.String(), SubjectAlternativeName: san, Extensions: extensions}, nil } // CompareExtensions compares two Extensions structs and returns an error if // any set values in the expected struct not equal. Empty fields in the // expectedExt struct are ignored. func CompareExtensions(expectedExt, actualExt Extensions) error { expExtValue := reflect.ValueOf(expectedExt) actExtValue := reflect.ValueOf(actualExt) fields := reflect.VisibleFields(expExtValue.Type()) for _, field := range fields { expectedFieldVal := expExtValue.FieldByName(field.Name) // if the expected field is empty, skip it if expectedFieldVal.IsValid() && !expectedFieldVal.IsZero() { actualFieldVal := actExtValue.FieldByName(field.Name) if actualFieldVal.IsValid() { if expectedFieldVal.Interface() != actualFieldVal.Interface() { return &ErrCompareExtensions{field.Name, fmt.Sprintf("%v", expectedFieldVal.Interface()), fmt.Sprintf("%v", actualFieldVal.Interface())} } } } } return nil } sigstore-go-0.7.1/pkg/fulcio/certificate/summarize_test.go000066400000000000000000000135071477477521700237420ustar00rootroot00000000000000// 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 certificate_test import ( "testing" "github.com/sigstore/sigstore-go/pkg/fulcio/certificate" "github.com/sigstore/sigstore-go/pkg/testing/data" "github.com/stretchr/testify/assert" ) func TestSummarizeCertificateWithActionsBundle(t *testing.T) { entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") vc, err := entity.VerificationContent() if err != nil { t.Fatalf("failed to get verification content: %v", err) } leaf := vc.Certificate() if leaf == nil { t.Fatalf("expected verification content to be a certificate chain") } cs, err := certificate.SummarizeCertificate(leaf) if err != nil { t.Fatalf("failed to summarize: %v", err) } expected := certificate.Summary{ CertificateIssuer: "CN=sigstore-intermediate,O=sigstore.dev", SubjectAlternativeName: "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main", Extensions: certificate.Extensions{ Issuer: "https://token.actions.githubusercontent.com", GithubWorkflowTrigger: "push", GithubWorkflowSHA: "f0b49a04e5a62250e0f60fb128004a73110fe311", GithubWorkflowName: "Release", GithubWorkflowRepository: "sigstore/sigstore-js", GithubWorkflowRef: "refs/heads/main", BuildSignerURI: "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main", BuildSignerDigest: "f0b49a04e5a62250e0f60fb128004a73110fe311", RunnerEnvironment: "github-hosted", SourceRepositoryURI: "https://github.com/sigstore/sigstore-js", SourceRepositoryDigest: "f0b49a04e5a62250e0f60fb128004a73110fe311", SourceRepositoryRef: "refs/heads/main", SourceRepositoryIdentifier: "495574555", SourceRepositoryOwnerURI: "https://github.com/sigstore", SourceRepositoryOwnerIdentifier: "71096353", BuildConfigURI: "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main", BuildConfigDigest: "f0b49a04e5a62250e0f60fb128004a73110fe311", BuildTrigger: "push", RunInvocationURI: "https://github.com/sigstore/sigstore-js/actions/runs/5904696764/attempts/1", SourceRepositoryVisibilityAtSigning: "public", }, } assert.Equal(t, expected, cs) } func TestSummarizeCertificateWithOauthBundle(t *testing.T) { entity := data.Bundle(t, "dsse.sigstore.json") vc, err := entity.VerificationContent() if err != nil { t.Fatalf("failed to get verification content: %v", err) } leaf := vc.Certificate() if leaf == nil { t.Fatalf("expected verification content to be a certificate chain") } cs, err := certificate.SummarizeCertificate(leaf) if err != nil { t.Fatalf("failed to summarize: %v", err) } expected := certificate.Summary{ CertificateIssuer: "CN=sigstore-intermediate,O=sigstore.dev", SubjectAlternativeName: "brian@dehamer.com", Extensions: certificate.Extensions{ Issuer: "https://github.com/login/oauth", }, } assert.Equal(t, expected, cs) } func TestSummarizeCertificateWithOtherNameSAN(t *testing.T) { entity := data.Bundle(t, "othername.sigstore.json") vc, err := entity.VerificationContent() if err != nil { t.Fatalf("failed to get verification content: %v", err) } leaf := vc.Certificate() if leaf == nil { t.Fatalf("expected verification content to be a certificate chain") } cs, err := certificate.SummarizeCertificate(leaf) assert.NoError(t, err) expected := certificate.Summary{ CertificateIssuer: "O=Linux Foundation,POSTALCODE=57274,STREET=548 Market St,L=San Francisco,ST=California,C=USA", SubjectAlternativeName: "foo!oidc.local", Extensions: certificate.Extensions{ Issuer: "http://oidc.local:8080", }, } assert.Equal(t, expected, cs) } func TestCompareExtensions(t *testing.T) { // Test that the extensions are equal actualExt := certificate.Extensions{ Issuer: "https://token.actions.githubusercontent.com", GithubWorkflowTrigger: "push", GithubWorkflowSHA: "f0b49a04e5a62250e0f60fb128004a73110fe311", GithubWorkflowName: "Release", GithubWorkflowRepository: "sigstore/sigstore-js", GithubWorkflowRef: "refs/heads/main", } expectedExt := certificate.Extensions{ Issuer: "https://token.actions.githubusercontent.com", } // Only the specified fields are expected to match assert.NoError(t, certificate.CompareExtensions(expectedExt, actualExt)) // Blank fields are ignored expectedExt = certificate.Extensions{ Issuer: "https://token.actions.githubusercontent.com", GithubWorkflowName: "", } assert.NoError(t, certificate.CompareExtensions(expectedExt, actualExt)) // but if any of the fields don't match, it should return false expectedExt = certificate.Extensions{ Issuer: "https://token.actions.githubusercontent.com", GithubWorkflowName: "Final", } errCompareExtensions := &certificate.ErrCompareExtensions{} assert.ErrorAs(t, certificate.CompareExtensions(expectedExt, actualExt), &errCompareExtensions) assert.Equal(t, errCompareExtensions.Error(), "expected GithubWorkflowName to be \"Final\", got \"Release\"") } sigstore-go-0.7.1/pkg/root/000077500000000000000000000000001477477521700155525ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/root/certificate_authority.go000066400000000000000000000042401477477521700224730ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package root import ( "crypto/x509" "errors" "time" ) type CertificateAuthority interface { Verify(cert *x509.Certificate, observerTimestamp time.Time) ([][]*x509.Certificate, error) } type FulcioCertificateAuthority struct { Root *x509.Certificate Intermediates []*x509.Certificate ValidityPeriodStart time.Time ValidityPeriodEnd time.Time URI string } var _ CertificateAuthority = &FulcioCertificateAuthority{} func (ca *FulcioCertificateAuthority) Verify(cert *x509.Certificate, observerTimestamp time.Time) ([][]*x509.Certificate, error) { if !ca.ValidityPeriodStart.IsZero() && observerTimestamp.Before(ca.ValidityPeriodStart) { return nil, errors.New("certificate is not valid yet") } if !ca.ValidityPeriodEnd.IsZero() && observerTimestamp.After(ca.ValidityPeriodEnd) { return nil, errors.New("certificate is no longer valid") } rootCertPool := x509.NewCertPool() rootCertPool.AddCert(ca.Root) intermediateCertPool := x509.NewCertPool() for _, cert := range ca.Intermediates { intermediateCertPool.AddCert(cert) } // From spec: // > ## Certificate // > For a signature with a given certificate to be considered valid, it must have a timestamp while every certificate in the chain up to the root is valid (the so-called “hybrid model” of certificate verification per Braun et al. (2013)). opts := x509.VerifyOptions{ CurrentTime: observerTimestamp, Roots: rootCertPool, Intermediates: intermediateCertPool, KeyUsages: []x509.ExtKeyUsage{ x509.ExtKeyUsageCodeSigning, }, } return cert.Verify(opts) } sigstore-go-0.7.1/pkg/root/certificate_authority_test.go000066400000000000000000000142571477477521700235430ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package root_test import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "math/big" "testing" "time" "github.com/sigstore/sigstore-go/pkg/root" tsx509 "github.com/sigstore/timestamp-authority/pkg/x509" "github.com/stretchr/testify/require" ) func TestCertificateAuthority(t *testing.T) { _, rootCert, intermediateCert, leafCert, now := genChain(t, false) _, rootCert2, intermediateCert2, leafCert2, _ := genChain(t, false) for _, test := range []struct { name string ca *root.FulcioCertificateAuthority expectError bool leafCert *x509.Certificate }{ { name: "normal", ca: &root.FulcioCertificateAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert, }, ValidityPeriodStart: now.Add(-time.Hour), ValidityPeriodEnd: now.Add(time.Hour), }, leafCert: leafCert, expectError: false, }, { name: "no validity period defined", ca: &root.FulcioCertificateAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert, }, }, leafCert: leafCert, expectError: false, }, { name: "before validity period", ca: &root.FulcioCertificateAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert, }, ValidityPeriodStart: now.Add(time.Hour), }, leafCert: leafCert, expectError: true, }, { name: "after validity period", ca: &root.FulcioCertificateAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert, }, ValidityPeriodEnd: now.Add(-time.Hour), }, leafCert: leafCert, expectError: true, }, { name: "missing intermediate", ca: &root.FulcioCertificateAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{}, ValidityPeriodStart: now.Add(-time.Hour), ValidityPeriodEnd: now.Add(time.Hour), }, leafCert: leafCert, expectError: true, }, { name: "bad leaf", ca: &root.FulcioCertificateAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert, }, ValidityPeriodStart: now.Add(-time.Hour), ValidityPeriodEnd: now.Add(time.Hour), }, leafCert: leafCert2, expectError: true, }, { name: "bad intermediate", ca: &root.FulcioCertificateAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert2, }, ValidityPeriodStart: now.Add(-time.Hour), ValidityPeriodEnd: now.Add(time.Hour), }, leafCert: leafCert, expectError: true, }, { name: "bad root", ca: &root.FulcioCertificateAuthority{ Root: rootCert2, Intermediates: []*x509.Certificate{ intermediateCert, }, ValidityPeriodStart: now.Add(-time.Hour), ValidityPeriodEnd: now.Add(time.Hour), }, leafCert: leafCert, expectError: true, }, } { t.Run(test.name, func(t *testing.T) { chains, err := test.ca.Verify(test.leafCert, now) if test.expectError { require.Error(t, err) require.Nil(t, chains) } else { require.NoError(t, err) require.NotNil(t, chains) } }) } } func genChain(t *testing.T, tsa bool) (*ecdsa.PrivateKey, *x509.Certificate, *x509.Certificate, *x509.Certificate, time.Time) { now := time.Now() rootCertTemplate := &x509.Certificate{ SerialNumber: big.NewInt(1), NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour), IsCA: true, BasicConstraintsValid: true, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}, } intermediateCertTemplate := &x509.Certificate{ SerialNumber: big.NewInt(2), NotBefore: now.Add(-time.Hour), NotAfter: now.Add(time.Hour), IsCA: true, BasicConstraintsValid: true, } leafCertTemplate := &x509.Certificate{ SerialNumber: big.NewInt(3), NotBefore: now, NotAfter: now.Add(10 * time.Minute), } if tsa { rootCertTemplate.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping} intermediateCertTemplate.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping} leafCertTemplate.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping} timestampExt, err := asn1.Marshal([]asn1.ObjectIdentifier{tsx509.EKUTimestampingOID}) if err != nil { t.Fatal(err) } leafCertTemplate.ExtraExtensions = []pkix.Extension{ { Id: asn1.ObjectIdentifier{2, 5, 29, 37}, Critical: true, Value: timestampExt, }, } } caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) //nolint:gosec require.NoError(t, err) intermediateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) //nolint:gosec require.NoError(t, err) leafKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) //nolint:gosec require.NoError(t, err) rootDer, err := x509.CreateCertificate(rand.Reader, rootCertTemplate, rootCertTemplate, &caKey.PublicKey, caKey) require.NoError(t, err) intermediateDer, err := x509.CreateCertificate(rand.Reader, intermediateCertTemplate, rootCertTemplate, &intermediateKey.PublicKey, caKey) require.NoError(t, err) leafDer, err := x509.CreateCertificate(rand.Reader, leafCertTemplate, intermediateCertTemplate, &leafKey.PublicKey, intermediateKey) require.NoError(t, err) rootCert, err := x509.ParseCertificate(rootDer) require.NoError(t, err) intermediateCert, err := x509.ParseCertificate(intermediateDer) require.NoError(t, err) leafCert, err := x509.ParseCertificate(leafDer) require.NoError(t, err) return leafKey, rootCert, intermediateCert, leafCert, now } sigstore-go-0.7.1/pkg/root/signing_config.go000066400000000000000000000313571477477521700210750ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package root import ( "fmt" "math/rand" "os" "slices" "time" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" prototrustroot "github.com/sigstore/protobuf-specs/gen/pb-go/trustroot/v1" "github.com/sigstore/sigstore-go/pkg/tuf" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/timestamppb" ) const SigningConfigMediaType02 = "application/vnd.dev.sigstore.signingconfig.v0.2+json" type SigningConfig struct { signingConfig *prototrustroot.SigningConfig } type Service struct { URL string MajorAPIVersion uint32 ValidityPeriodStart time.Time ValidityPeriodEnd time.Time } type ServiceConfiguration struct { Selector prototrustroot.ServiceSelector Count uint32 } // SelectService returns which service endpoint should be used based on supported API versions // and current time. It will select the first service that matches the criteria. Services should // be sorted from newest to oldest validity period start time, to minimize how far clients // need to search to find a matching service. func SelectService(services []Service, supportedAPIVersions []uint32, currentTime time.Time) (string, error) { for _, s := range services { if slices.Contains(supportedAPIVersions, s.MajorAPIVersion) && s.ValidAtTime(currentTime) { return s.URL, nil } } return "", fmt.Errorf("no matching service found for API versions %v and current time %v", supportedAPIVersions, currentTime) } // SelectServices returns which service endpoints should be used based on supported API versions // and current time. It will use the configuration's selector to pick a set of services. // ALL will return all service endpoints, ANY will return a random endpoint, and // EXACT will return a random selection of a specified number of endpoints. func SelectServices(services []Service, config ServiceConfiguration, supportedAPIVersions []uint32, currentTime time.Time) ([]string, error) { var urls []string for _, s := range services { if slices.Contains(supportedAPIVersions, s.MajorAPIVersion) && s.ValidAtTime(currentTime) { urls = append(urls, s.URL) } } if len(urls) == 0 { return nil, fmt.Errorf("no matching services found for API versions %v and current time %v", supportedAPIVersions, currentTime) } switch config.Selector { case prototrustroot.ServiceSelector_ALL: return urls, nil case prototrustroot.ServiceSelector_ANY: i := rand.Intn(len(urls)) // #nosec G404 return []string{urls[i]}, nil case prototrustroot.ServiceSelector_EXACT: matchedUrls, err := selectExact(urls, config.Count) if err != nil { return nil, err } return matchedUrls, nil default: return nil, fmt.Errorf("invalid service selector") } } func selectExact[T any](slice []T, count uint32) ([]T, error) { if count == 0 { return nil, fmt.Errorf("service selector count must be greater than 0") } if int(count) > len(slice) { return nil, fmt.Errorf("service selector count %d must be less than or equal to the slice length %d", count, len(slice)) } sliceCopy := make([]T, len(slice)) copy(sliceCopy, slice) var result []T for range count { i := rand.Intn(len(sliceCopy)) // #nosec G404 result = append(result, sliceCopy[i]) // Remove element from slice sliceCopy[i], sliceCopy[len(sliceCopy)-1] = sliceCopy[len(sliceCopy)-1], sliceCopy[i] sliceCopy = sliceCopy[:len(sliceCopy)-1] } return result, nil } func mapFunc[T, V any](ts []T, fn func(T) V) []V { result := make([]V, len(ts)) for i, t := range ts { result[i] = fn(t) } return result } func (s Service) ValidAtTime(t time.Time) bool { if !s.ValidityPeriodStart.IsZero() && t.Before(s.ValidityPeriodStart) { return false } if !s.ValidityPeriodEnd.IsZero() && t.After(s.ValidityPeriodEnd) { return false } return true } func (s Service) ToServiceProtobuf() *prototrustroot.Service { return &prototrustroot.Service{ Url: s.URL, MajorApiVersion: s.MajorAPIVersion, ValidFor: &v1.TimeRange{ Start: timestamppb.New(s.ValidityPeriodStart), End: timestamppb.New(s.ValidityPeriodEnd), }, } } func (sc ServiceConfiguration) ToConfigProtobuf() *prototrustroot.ServiceConfiguration { return &prototrustroot.ServiceConfiguration{ Selector: sc.Selector, Count: sc.Count, } } func (sc *SigningConfig) FulcioCertificateAuthorityURLs() []Service { var services []Service for _, s := range sc.signingConfig.GetCaUrls() { services = append(services, Service{ URL: s.GetUrl(), MajorAPIVersion: s.GetMajorApiVersion(), ValidityPeriodStart: s.GetValidFor().GetStart().AsTime(), ValidityPeriodEnd: s.GetValidFor().GetEnd().AsTime(), }) } return services } func (sc *SigningConfig) OIDCProviderURLs() []Service { var services []Service for _, s := range sc.signingConfig.GetOidcUrls() { services = append(services, Service{ URL: s.GetUrl(), MajorAPIVersion: s.GetMajorApiVersion(), ValidityPeriodStart: s.GetValidFor().GetStart().AsTime(), ValidityPeriodEnd: s.GetValidFor().GetEnd().AsTime(), }) } return services } func (sc *SigningConfig) RekorLogURLs() []Service { var services []Service for _, s := range sc.signingConfig.GetRekorTlogUrls() { services = append(services, Service{ URL: s.GetUrl(), MajorAPIVersion: s.GetMajorApiVersion(), ValidityPeriodStart: s.GetValidFor().GetStart().AsTime(), ValidityPeriodEnd: s.GetValidFor().GetEnd().AsTime(), }) } return services } func (sc *SigningConfig) RekorLogURLsConfig() ServiceConfiguration { c := sc.signingConfig.GetRekorTlogConfig() return ServiceConfiguration{ Selector: c.Selector, Count: c.Count, } } func (sc *SigningConfig) TimestampAuthorityURLs() []Service { var services []Service for _, s := range sc.signingConfig.GetTsaUrls() { services = append(services, Service{ URL: s.GetUrl(), MajorAPIVersion: s.GetMajorApiVersion(), ValidityPeriodStart: s.GetValidFor().GetStart().AsTime(), ValidityPeriodEnd: s.GetValidFor().GetEnd().AsTime(), }) } return services } func (sc *SigningConfig) TimestampAuthorityURLsConfig() ServiceConfiguration { c := sc.signingConfig.GetTsaConfig() return ServiceConfiguration{ Selector: c.Selector, Count: c.Count, } } func (sc *SigningConfig) WithFulcioCertificateAuthorityURLs(fulcioURLs ...Service) *SigningConfig { var services []*prototrustroot.Service for _, u := range fulcioURLs { services = append(services, &prototrustroot.Service{ Url: u.URL, MajorApiVersion: u.MajorAPIVersion, ValidFor: &v1.TimeRange{ Start: timestamppb.New(u.ValidityPeriodStart), End: timestamppb.New(u.ValidityPeriodEnd), }, }) } sc.signingConfig.CaUrls = services return sc } func (sc *SigningConfig) AddFulcioCertificateAuthorityURLs(fulcioURLs ...Service) *SigningConfig { for _, u := range fulcioURLs { sc.signingConfig.CaUrls = append(sc.signingConfig.CaUrls, u.ToServiceProtobuf()) } return sc } func (sc *SigningConfig) WithOIDCProviderURLs(oidcURLs ...Service) *SigningConfig { var services []*prototrustroot.Service for _, u := range oidcURLs { services = append(services, u.ToServiceProtobuf()) } sc.signingConfig.OidcUrls = services return sc } func (sc *SigningConfig) AddOIDCProviderURLs(oidcURLs ...Service) *SigningConfig { for _, u := range oidcURLs { sc.signingConfig.OidcUrls = append(sc.signingConfig.OidcUrls, u.ToServiceProtobuf()) } return sc } func (sc *SigningConfig) WithRekorLogURLs(logURLs ...Service) *SigningConfig { var services []*prototrustroot.Service for _, u := range logURLs { services = append(services, u.ToServiceProtobuf()) } sc.signingConfig.RekorTlogUrls = services return sc } func (sc *SigningConfig) AddRekorLogURLs(logURLs ...Service) *SigningConfig { for _, u := range logURLs { sc.signingConfig.RekorTlogUrls = append(sc.signingConfig.RekorTlogUrls, u.ToServiceProtobuf()) } return sc } func (sc *SigningConfig) WithRekorTlogConfig(selector prototrustroot.ServiceSelector, count uint32) *SigningConfig { sc.signingConfig.RekorTlogConfig.Selector = selector sc.signingConfig.RekorTlogConfig.Count = count return sc } func (sc *SigningConfig) WithTimestampAuthorityURLs(tsaURLs ...Service) *SigningConfig { var services []*prototrustroot.Service for _, u := range tsaURLs { services = append(services, u.ToServiceProtobuf()) } sc.signingConfig.TsaUrls = services return sc } func (sc *SigningConfig) AddTimestampAuthorityURLs(tsaURLs ...Service) *SigningConfig { for _, u := range tsaURLs { sc.signingConfig.TsaUrls = append(sc.signingConfig.TsaUrls, u.ToServiceProtobuf()) } return sc } func (sc *SigningConfig) WithTsaConfig(selector prototrustroot.ServiceSelector, count uint32) *SigningConfig { sc.signingConfig.TsaConfig.Selector = selector sc.signingConfig.TsaConfig.Count = count return sc } func (sc SigningConfig) String() string { return fmt.Sprintf("{CA: %v, OIDC: %v, RekorLogs: %v, TSAs: %v, MediaType: %s}", sc.FulcioCertificateAuthorityURLs(), sc.OIDCProviderURLs(), sc.RekorLogURLs(), sc.TimestampAuthorityURLs(), SigningConfigMediaType02) } // NewSigningConfig initializes a SigningConfig object from a mediaType string, Fulcio certificate // authority URLs, OIDC provider URLs, Rekor transparency log URLs, timestamp authorities URLs, // selection criteria for Rekor logs and TSAs. func NewSigningConfig(mediaType string, fulcioCertificateAuthorities []Service, oidcProviders []Service, rekorLogs []Service, rekorLogsConfig ServiceConfiguration, timestampAuthorities []Service, timestampAuthoritiesConfig ServiceConfiguration) (*SigningConfig, error) { if mediaType != SigningConfigMediaType02 { return nil, fmt.Errorf("unsupported SigningConfig media type, must be: %s", SigningConfigMediaType02) } sc := &SigningConfig{ signingConfig: &prototrustroot.SigningConfig{ MediaType: mediaType, CaUrls: mapFunc(fulcioCertificateAuthorities, Service.ToServiceProtobuf), OidcUrls: mapFunc(oidcProviders, Service.ToServiceProtobuf), RekorTlogUrls: mapFunc(rekorLogs, Service.ToServiceProtobuf), RekorTlogConfig: rekorLogsConfig.ToConfigProtobuf(), TsaUrls: mapFunc(timestampAuthorities, Service.ToServiceProtobuf), TsaConfig: timestampAuthoritiesConfig.ToConfigProtobuf(), }, } return sc, nil } // NewSigningConfigFromProtobuf returns a Sigstore signing configuration. func NewSigningConfigFromProtobuf(sc *prototrustroot.SigningConfig) (*SigningConfig, error) { if sc.GetMediaType() != SigningConfigMediaType02 { return nil, fmt.Errorf("unsupported SigningConfig media type: %s", sc.GetMediaType()) } return &SigningConfig{signingConfig: sc}, nil } // NewSigningConfigFromPath returns a Sigstore signing configuration from a file. func NewSigningConfigFromPath(path string) (*SigningConfig, error) { scJSON, err := os.ReadFile(path) if err != nil { return nil, err } return NewSigningConfigFromJSON(scJSON) } // NewSigningConfigFromJSON returns a Sigstore signing configuration from JSON. func NewSigningConfigFromJSON(rootJSON []byte) (*SigningConfig, error) { pbSC, err := NewSigningConfigProtobuf(rootJSON) if err != nil { return nil, err } return NewSigningConfigFromProtobuf(pbSC) } // NewSigningConfigProtobuf returns a Sigstore signing configuration as a protobuf. func NewSigningConfigProtobuf(scJSON []byte) (*prototrustroot.SigningConfig, error) { pbSC := &prototrustroot.SigningConfig{} err := protojson.Unmarshal(scJSON, pbSC) if err != nil { return nil, err } return pbSC, nil } // FetchSigningConfig fetches the public-good Sigstore signing configuration from TUF. func FetchSigningConfig() (*SigningConfig, error) { return FetchSigningConfigWithOptions(tuf.DefaultOptions()) } // FetchSigningConfig fetches the public-good Sigstore signing configuration with the given options from TUF. func FetchSigningConfigWithOptions(opts *tuf.Options) (*SigningConfig, error) { client, err := tuf.New(opts) if err != nil { return nil, err } return GetSigningConfig(client) } // FetchSigningConfig fetches the public-good Sigstore signing configuration target from TUF. func GetSigningConfig(c *tuf.Client) (*SigningConfig, error) { jsonBytes, err := c.GetTarget("signing_config.json") if err != nil { return nil, err } return NewSigningConfigFromJSON(jsonBytes) } sigstore-go-0.7.1/pkg/root/signing_config_test.go000066400000000000000000000662531477477521700221370ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package root import ( "fmt" "reflect" "testing" "time" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" prototrustroot "github.com/sigstore/protobuf-specs/gen/pb-go/trustroot/v1" "google.golang.org/protobuf/types/known/timestamppb" ) // Assume services are sorted such that they should match func servicesEqual(got []Service, want []Service) bool { if len(got) != len(want) { return false } for i, g := range got { w := want[i] if g.URL != w.URL || g.MajorAPIVersion != w.MajorAPIVersion || !g.ValidityPeriodStart.Equal(w.ValidityPeriodStart) || !g.ValidityPeriodEnd.Equal(w.ValidityPeriodEnd) { return false } } return true } func newService(url string, now time.Time) Service { return Service{ URL: url, MajorAPIVersion: 1, ValidityPeriodStart: now.Add(-time.Hour), ValidityPeriodEnd: now.Add(time.Hour), } } func TestSelectService(t *testing.T) { now := time.Now() past := now.Add(-time.Hour) future := now.Add(time.Hour) farFuture := now.Add(2 * time.Hour) services := []Service{ { URL: "url1", MajorAPIVersion: 1, ValidityPeriodStart: past, ValidityPeriodEnd: future, }, { URL: "url2", MajorAPIVersion: 2, ValidityPeriodStart: past, ValidityPeriodEnd: future, }, { URL: "url3_new", MajorAPIVersion: 2, ValidityPeriodStart: now, ValidityPeriodEnd: future, }, { URL: "url_no_end", MajorAPIVersion: 2, ValidityPeriodStart: farFuture, ValidityPeriodEnd: time.Time{}, }, } tests := []struct { name string services []Service supportedVersions uint32 currentTime time.Time expectedURL string expectedErr bool expectedErrMessage string }{ { name: "single matching service", services: services, supportedVersions: 1, currentTime: now, expectedURL: "url1", expectedErr: false, }, { name: "multiple matching service, first selected", services: services, supportedVersions: 2, currentTime: now, expectedURL: "url2", expectedErr: false, }, { name: "no matching version", services: services, supportedVersions: 3, currentTime: now, expectedErr: true, expectedErrMessage: "no matching service found for API versions [3] and current time", }, { name: "valid with no end time", services: services, supportedVersions: 2, currentTime: farFuture, expectedURL: "url_no_end", expectedErr: false, }, { name: "no matching service at all", services: []Service{}, supportedVersions: 1, currentTime: now, expectedErr: true, expectedErrMessage: "no matching service found for API versions [1] and current time", }, { name: "first service selected when multiple match", services: services, supportedVersions: 2, currentTime: now.Add(time.Minute), // In the validity period of url3_new expectedURL: "url2", expectedErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { url, err := SelectService(tt.services, []uint32{tt.supportedVersions}, tt.currentTime) if (err != nil) != tt.expectedErr { t.Errorf("SelectService() error = %v, expectedErr %v", err, tt.expectedErr) return } if tt.expectedErr { if err.Error()[:len(tt.expectedErrMessage)] != tt.expectedErrMessage { t.Errorf("SelectService() error message = %v, expected %v", err.Error(), tt.expectedErrMessage) } } else if url != tt.expectedURL { t.Errorf("SelectService() got = %v, want %v", url, tt.expectedURL) } }) } } func TestSelectServices(t *testing.T) { now := time.Now() past := now.Add(-time.Hour) future := now.Add(time.Hour) services := []Service{ { URL: "url1", MajorAPIVersion: 1, ValidityPeriodStart: past, ValidityPeriodEnd: future, }, { URL: "url2", MajorAPIVersion: 2, ValidityPeriodStart: past, ValidityPeriodEnd: future, }, { URL: "url3", MajorAPIVersion: 2, ValidityPeriodStart: past, ValidityPeriodEnd: future, }, } tests := []struct { name string services []Service config ServiceConfiguration supportedVersions uint32 currentTime time.Time expectedURLs []string possibleURLs [][]string expectedErr bool expectedErrMessage string }{ { name: "ALL selector, multiple matches", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_ALL, }, supportedVersions: 2, currentTime: now, expectedURLs: []string{"url2", "url3"}, expectedErr: false, }, { name: "ALL selector, single match", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_ALL, }, supportedVersions: 1, currentTime: now, expectedURLs: []string{"url1"}, expectedErr: false, }, { name: "ALL selector, no match", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_ALL, }, supportedVersions: 3, currentTime: now, expectedErr: true, expectedErrMessage: "no matching services found for API versions [3] and current time", }, { name: "ANY selector, multiple matches", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_ANY, }, supportedVersions: 2, currentTime: now, possibleURLs: [][]string{{"url2"}, {"url3"}}, expectedErr: false, }, { name: "ANY selector, single match", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_ANY, }, supportedVersions: 1, currentTime: now, expectedURLs: []string{"url1"}, expectedErr: false, }, { name: "ANY selector, no match", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_ANY, }, supportedVersions: 3, currentTime: now, expectedErr: true, expectedErrMessage: "no matching services found for API versions [3] and current time", }, { name: "EXACT selector, count 1, multiple matches", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_EXACT, Count: 1, }, supportedVersions: 2, currentTime: now, possibleURLs: [][]string{{"url2"}, {"url3"}}, expectedErr: false, }, { name: "EXACT selector, count 2, multiple matches", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_EXACT, Count: 2, }, supportedVersions: 2, currentTime: now, possibleURLs: [][]string{{"url2", "url3"}, {"url3", "url2"}}, expectedErr: false, }, { name: "EXACT selector, count 1, single match", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_EXACT, Count: 1, }, supportedVersions: 1, currentTime: now, expectedURLs: []string{"url1"}, expectedErr: false, }, { name: "EXACT selector, count 0", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_EXACT, Count: 0, }, supportedVersions: 2, currentTime: now, expectedErr: true, expectedErrMessage: "service selector count must be greater than 0", }, { name: "EXACT selector, count greater than matches", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_EXACT, Count: 3, }, supportedVersions: 2, currentTime: now, expectedErr: true, expectedErrMessage: "service selector count 3 must be less than or equal to the slice length 2", }, { name: "EXACT selector, count too hight", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_EXACT, Count: 100, }, supportedVersions: 2, currentTime: now, expectedErr: true, expectedErrMessage: "service selector count 100 must be less than or equal to the slice length 2", }, { name: "EXACT selector, no matches", services: services, config: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_EXACT, Count: 1, }, supportedVersions: 3, currentTime: now, expectedErr: true, expectedErrMessage: "no matching services found for API versions [3] and current time", }, { name: "Invalid selector", services: services, config: ServiceConfiguration{ Selector: 99, // Invalid }, supportedVersions: 2, currentTime: now, expectedErr: true, expectedErrMessage: "invalid service selector", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { urls, err := SelectServices(tt.services, tt.config, []uint32{tt.supportedVersions}, tt.currentTime) if (err != nil) != tt.expectedErr { t.Errorf("SelectServices() error = %v, expectedErr %v", err, tt.expectedErr) return } if tt.expectedErr { //nolint:gocritic if err.Error()[:len(tt.expectedErrMessage)] != tt.expectedErrMessage { t.Errorf("SelectServices() error message = %v, expected %v", err.Error(), tt.expectedErrMessage) } } else if tt.possibleURLs != nil { // Handle EXACT and ANY tests, where results are random found := false for _, t := range tt.possibleURLs { if reflect.DeepEqual(urls, t) { found = true break } } if !found { t.Errorf("SelectServices() got = %v, wanted one of %v", urls, tt.possibleURLs) } } else if !reflect.DeepEqual(urls, tt.expectedURLs) { t.Errorf("SelectServices() got = %v, want %v", urls, tt.expectedURLs) } }) } } func Test_selectExact(t *testing.T) { tests := []struct { name string slice []string count uint32 expectedErr bool }{ { name: "count 1", slice: []string{"a", "b", "c"}, count: 1, expectedErr: false, }, { name: "count 2", slice: []string{"a", "b", "c"}, count: 2, expectedErr: false, }, { name: "count equal to length", slice: []string{"a", "b", "c"}, count: 3, expectedErr: false, }, { name: "count 0", slice: []string{"a", "b", "c"}, count: 0, expectedErr: true, }, { name: "count greater than length", slice: []string{"a", "b", "c"}, count: 4, expectedErr: true, }, { name: "empty slice", slice: []string{}, count: 1, expectedErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := selectExact(tt.slice, tt.count) if (err != nil) != tt.expectedErr { t.Errorf("selectExact() error = %v, wantErr %v", err, tt.expectedErr) return } if !tt.expectedErr && len(got) != int(tt.count) { t.Errorf("selectExact() got = %v", got) } }) } } func Test_mapFunc(t *testing.T) { tests := []struct { name string input []int mapFn func(int) string expected []string }{ { name: "simple mapping", input: []int{1, 2, 3}, mapFn: func(i int) string { return fmt.Sprintf("num_%d", i) }, expected: []string{"num_1", "num_2", "num_3"}, }, { name: "empty slice", input: []int{}, mapFn: func(i int) string { return fmt.Sprintf("num_%d", i) }, expected: []string{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := mapFunc(tt.input, tt.mapFn) if !reflect.DeepEqual(got, tt.expected) { t.Errorf("mapFunc() got = %v, want %v", got, tt.expected) } }) } } func TestSigningConfig_FulcioCertificateAuthorityURLs(t *testing.T) { now := time.Now() tests := []struct { name string signingConfig *prototrustroot.SigningConfig want []Service }{ { name: "valid", signingConfig: &prototrustroot.SigningConfig{ CaUrls: []*prototrustroot.Service{ { Url: "https://fulcio.sigstore.dev", MajorApiVersion: 1, ValidFor: &v1.TimeRange{Start: timestamppb.New(now), End: timestamppb.New(now)}, }, }, }, want: []Service{ { URL: "https://fulcio.sigstore.dev", MajorAPIVersion: 1, ValidityPeriodStart: now, ValidityPeriodEnd: now, }, }, }, { name: "empty", signingConfig: &prototrustroot.SigningConfig{ CaUrls: []*prototrustroot.Service{}, }, want: []Service{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sc := &SigningConfig{ signingConfig: tt.signingConfig, } if got := sc.FulcioCertificateAuthorityURLs(); !servicesEqual(got, tt.want) { t.Errorf("SigningConfig.FulcioCertificateAuthorityURLs() = %v, want %v", got, tt.want) } }) } } func TestSigningConfig_OIDCProviderURLs(t *testing.T) { now := time.Now() tests := []struct { name string signingConfig *prototrustroot.SigningConfig want []Service }{ { name: "valid", signingConfig: &prototrustroot.SigningConfig{ OidcUrls: []*prototrustroot.Service{ { Url: "https://oauth2.sigstore.dev/auth", MajorApiVersion: 1, ValidFor: &v1.TimeRange{Start: timestamppb.New(now), End: timestamppb.New(now)}, }, }, }, want: []Service{ { URL: "https://oauth2.sigstore.dev/auth", MajorAPIVersion: 1, ValidityPeriodStart: now, ValidityPeriodEnd: now, }, }, }, { name: "empty", signingConfig: &prototrustroot.SigningConfig{ OidcUrls: []*prototrustroot.Service{}, }, want: []Service{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sc := &SigningConfig{ signingConfig: tt.signingConfig, } if got := sc.OIDCProviderURLs(); !servicesEqual(got, tt.want) { t.Errorf("SigningConfig.OIDCProviderURLs() = %v, want %v", got, tt.want) } }) } } func TestSigningConfig_RekorLogURLs(t *testing.T) { now := time.Now() tests := []struct { name string signingConfig *prototrustroot.SigningConfig want []Service }{ { name: "valid", signingConfig: &prototrustroot.SigningConfig{ RekorTlogUrls: []*prototrustroot.Service{ { Url: "https://rekor.sigstore.dev", MajorApiVersion: 1, ValidFor: &v1.TimeRange{Start: timestamppb.New(now), End: timestamppb.New(now)}, }, }, }, want: []Service{ { URL: "https://rekor.sigstore.dev", MajorAPIVersion: 1, ValidityPeriodStart: now, ValidityPeriodEnd: now, }, }, }, { name: "empty", signingConfig: &prototrustroot.SigningConfig{ RekorTlogUrls: []*prototrustroot.Service{}, }, want: []Service{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sc := &SigningConfig{ signingConfig: tt.signingConfig, } if got := sc.RekorLogURLs(); !servicesEqual(got, tt.want) { t.Errorf("SigningConfig.RekorLogURLs() = %v, want %v", got, tt.want) } }) } } func TestSigningConfig_RekorLogURLsConfig(t *testing.T) { tests := []struct { name string signingConfig *prototrustroot.SigningConfig want ServiceConfiguration }{ { name: "valid", signingConfig: &prototrustroot.SigningConfig{ RekorTlogConfig: &prototrustroot.ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_EXACT, Count: 1, }, }, want: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_EXACT, Count: 1, }, }, { name: "empty", signingConfig: &prototrustroot.SigningConfig{RekorTlogConfig: &prototrustroot.ServiceConfiguration{}}, want: ServiceConfiguration{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sc := &SigningConfig{ signingConfig: tt.signingConfig, } if got := sc.RekorLogURLsConfig(); !reflect.DeepEqual(got, tt.want) { t.Errorf("SigningConfig.RekorLogURLs() = %v, want %v", got, tt.want) } }) } } func TestSigningConfig_TimestampAuthorityURLs(t *testing.T) { now := time.Now() tests := []struct { name string signingConfig *prototrustroot.SigningConfig want []Service }{ { name: "valid", signingConfig: &prototrustroot.SigningConfig{ TsaUrls: []*prototrustroot.Service{ { Url: "https://timestamp.sigstore.dev", MajorApiVersion: 1, ValidFor: &v1.TimeRange{Start: timestamppb.New(now), End: timestamppb.New(now)}, }, }, }, want: []Service{ { URL: "https://timestamp.sigstore.dev", MajorAPIVersion: 1, ValidityPeriodStart: now, ValidityPeriodEnd: now, }, }, }, { name: "empty", signingConfig: &prototrustroot.SigningConfig{ TsaUrls: []*prototrustroot.Service{}, }, want: []Service{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sc := &SigningConfig{ signingConfig: tt.signingConfig, } if got := sc.TimestampAuthorityURLs(); !servicesEqual(got, tt.want) { t.Errorf("SigningConfig.TimestampAuthorityURLs() = %v, want %v", got, tt.want) } }) } } func TestSigningConfig_TimestampAuthorityURLsConfig(t *testing.T) { tests := []struct { name string signingConfig *prototrustroot.SigningConfig want ServiceConfiguration }{ { name: "valid", signingConfig: &prototrustroot.SigningConfig{ TsaConfig: &prototrustroot.ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_EXACT, Count: 1, }, }, want: ServiceConfiguration{ Selector: prototrustroot.ServiceSelector_EXACT, Count: 1, }, }, { name: "empty", signingConfig: &prototrustroot.SigningConfig{TsaConfig: &prototrustroot.ServiceConfiguration{}}, want: ServiceConfiguration{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sc := &SigningConfig{ signingConfig: tt.signingConfig, } if got := sc.TimestampAuthorityURLsConfig(); !reflect.DeepEqual(got, tt.want) { t.Errorf("SigningConfig.TimestampAuthorityURLsConfig() = %v, want %v", got, tt.want) } }) } } func TestNewSigningConfig(t *testing.T) { now := time.Now() type args struct { mediaType string fulcioCertificateAuthorities []Service oidcProviders []Service rekorLogs []Service timestampAuthorities []Service config ServiceConfiguration } tests := []struct { name string args args want *SigningConfig wantErr bool }{ { name: "valid", args: args{ mediaType: SigningConfigMediaType02, fulcioCertificateAuthorities: []Service{{URL: "https://fulcio.sigstore.dev", ValidityPeriodStart: now, ValidityPeriodEnd: now.Add(time.Hour)}}, oidcProviders: []Service{{URL: "https://oauth2.sigstore.dev/auth", ValidityPeriodStart: now, ValidityPeriodEnd: now.Add(time.Hour)}}, rekorLogs: []Service{{URL: "https://rekor.sigstore.dev", ValidityPeriodStart: now, ValidityPeriodEnd: now.Add(time.Hour)}}, timestampAuthorities: []Service{{URL: "https://timestamp.sigstore.dev", ValidityPeriodStart: now, ValidityPeriodEnd: now.Add(time.Hour)}}, config: ServiceConfiguration{Selector: prototrustroot.ServiceSelector_ANY}, }, want: &SigningConfig{ signingConfig: &prototrustroot.SigningConfig{ MediaType: SigningConfigMediaType02, CaUrls: []*prototrustroot.Service{{Url: "https://fulcio.sigstore.dev", ValidFor: &v1.TimeRange{Start: timestamppb.New(now), End: timestamppb.New(now.Add(time.Hour))}}}, OidcUrls: []*prototrustroot.Service{{Url: "https://oauth2.sigstore.dev/auth", ValidFor: &v1.TimeRange{Start: timestamppb.New(now), End: timestamppb.New(now.Add(time.Hour))}}}, RekorTlogUrls: []*prototrustroot.Service{{Url: "https://rekor.sigstore.dev", ValidFor: &v1.TimeRange{Start: timestamppb.New(now), End: timestamppb.New(now.Add(time.Hour))}}}, RekorTlogConfig: &prototrustroot.ServiceConfiguration{Selector: prototrustroot.ServiceSelector_ANY}, TsaUrls: []*prototrustroot.Service{{Url: "https://timestamp.sigstore.dev", ValidFor: &v1.TimeRange{Start: timestamppb.New(now), End: timestamppb.New(now.Add(time.Hour))}}}, TsaConfig: &prototrustroot.ServiceConfiguration{Selector: prototrustroot.ServiceSelector_ANY}, }, }, wantErr: false, }, { name: "invalid media type", args: args{ mediaType: "application/json", fulcioCertificateAuthorities: []Service{{URL: "https://fulcio.sigstore.dev", ValidityPeriodStart: now, ValidityPeriodEnd: now.Add(time.Hour)}}, oidcProviders: []Service{{URL: "https://oauth2.sigstore.dev/auth", ValidityPeriodStart: now, ValidityPeriodEnd: now.Add(time.Hour)}}, rekorLogs: []Service{{URL: "https://rekor.sigstore.dev", ValidityPeriodStart: now, ValidityPeriodEnd: now.Add(time.Hour)}}, timestampAuthorities: []Service{{URL: "https://timestamp.sigstore.dev", ValidityPeriodStart: now, ValidityPeriodEnd: now.Add(time.Hour)}}, config: ServiceConfiguration{Selector: prototrustroot.ServiceSelector_ANY}, }, want: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := NewSigningConfig(tt.args.mediaType, tt.args.fulcioCertificateAuthorities, tt.args.oidcProviders, tt.args.rekorLogs, tt.args.config, tt.args.timestampAuthorities, tt.args.config) if (err != nil) != tt.wantErr { t.Errorf("NewSigningConfig() error = %v, wantErr %v", err, tt.wantErr) return } if tt.want != nil { if !servicesEqual(got.FulcioCertificateAuthorityURLs(), tt.want.FulcioCertificateAuthorityURLs()) { t.Errorf("NewSigningConfig.FulcioCertificateAuthorityURLs() = %v, want %v", got.FulcioCertificateAuthorityURLs(), tt.want.FulcioCertificateAuthorityURLs()) } if !servicesEqual(got.OIDCProviderURLs(), tt.want.OIDCProviderURLs()) { t.Errorf("NewSigningConfig.OIDCProviderURLs() = %v, want %v", got.OIDCProviderURLs(), tt.want.OIDCProviderURLs()) } if !servicesEqual(got.RekorLogURLs(), tt.want.RekorLogURLs()) { t.Errorf("NewSigningConfig.RekorLogURLs() = %v, want %v", got.RekorLogURLs(), tt.want.RekorLogURLs()) } if !reflect.DeepEqual(got.RekorLogURLsConfig(), tt.want.RekorLogURLsConfig()) { t.Errorf("NewSigningConfig.RekorLogURLsConfig() = %v, want %v", got.RekorLogURLsConfig(), tt.want.RekorLogURLsConfig()) } if !servicesEqual(got.TimestampAuthorityURLs(), tt.want.TimestampAuthorityURLs()) { t.Errorf("NewSigningConfig.TimestampAuthorityURLs() = %v, want %v", got.TimestampAuthorityURLs(), tt.want.TimestampAuthorityURLs()) } if !reflect.DeepEqual(got.TimestampAuthorityURLsConfig(), tt.want.TimestampAuthorityURLsConfig()) { t.Errorf("NewSigningConfig.TimestampAuthorityURLsConfig() = %v, want %v", got.TimestampAuthorityURLsConfig(), tt.want.TimestampAuthorityURLsConfig()) } } }) } } func TestNewSigningConfigWithOptions(t *testing.T) { now := time.Now() expectedCAService := newService("ca-url", now) expectedOIDCService := newService("oidc-url", now) expectedRekorLogService := newService("rekor-url", now) expectedTSAService := newService("tsa-url", now) sc, err := NewSigningConfig(SigningConfigMediaType02, nil, nil, nil, ServiceConfiguration{}, nil, ServiceConfiguration{}) sc = sc.WithFulcioCertificateAuthorityURLs(expectedCAService). WithOIDCProviderURLs(expectedOIDCService). WithRekorLogURLs(expectedRekorLogService). WithTimestampAuthorityURLs(expectedTSAService). WithRekorTlogConfig(prototrustroot.ServiceSelector_EXACT, 1). WithTsaConfig(prototrustroot.ServiceSelector_EXACT, 1) if err != nil { t.Errorf("NewSigningConfig() error = %v", err) } if !servicesEqual(sc.FulcioCertificateAuthorityURLs(), []Service{expectedCAService}) { t.Errorf("unexpected CA service, expected %v, got %v", expectedCAService, sc.FulcioCertificateAuthorityURLs()) } if !servicesEqual(sc.OIDCProviderURLs(), []Service{expectedOIDCService}) { t.Errorf("unexpected OIDC service, expected %v, got %v", expectedOIDCService, sc.OIDCProviderURLs()) } if !servicesEqual(sc.RekorLogURLs(), []Service{expectedRekorLogService}) { t.Errorf("unexpected Rekor service, expected %v, got %v", expectedRekorLogService, sc.RekorLogURLs()) } if !servicesEqual(sc.TimestampAuthorityURLs(), []Service{expectedTSAService}) { t.Errorf("unexpected TSA service, expected %v, got %v", expectedTSAService, sc.TimestampAuthorityURLs()) } if !reflect.DeepEqual(sc.RekorLogURLsConfig(), ServiceConfiguration{Selector: prototrustroot.ServiceSelector_EXACT, Count: 1}) { t.Errorf("unexpected Rekor config, expected %v", sc.RekorLogURLsConfig()) } if !reflect.DeepEqual(sc.TimestampAuthorityURLsConfig(), ServiceConfiguration{Selector: prototrustroot.ServiceSelector_EXACT, Count: 1}) { t.Errorf("unexpected TSA config, expected %v", sc.TimestampAuthorityURLsConfig()) } expectedAddedCAService := newService("ca-url2", now) expectedAddedOIDCService := newService("oidc-url2", now) expectedAddedRekorLogService := newService("rekor-url2", now) expectedAddedTSAService := newService("tsa-url2", now) sc = sc.AddFulcioCertificateAuthorityURLs(expectedAddedCAService).AddOIDCProviderURLs(expectedAddedOIDCService). AddRekorLogURLs(expectedAddedRekorLogService).AddTimestampAuthorityURLs(expectedAddedTSAService) if !servicesEqual(sc.FulcioCertificateAuthorityURLs(), []Service{expectedCAService, expectedAddedCAService}) { t.Errorf("unexpected CA service, expected %v, got %v", expectedCAService, sc.FulcioCertificateAuthorityURLs()) } if !servicesEqual(sc.OIDCProviderURLs(), []Service{expectedOIDCService, expectedAddedOIDCService}) { t.Errorf("unexpected OIDC service, expected %v, got %v", expectedOIDCService, sc.OIDCProviderURLs()) } if !servicesEqual(sc.RekorLogURLs(), []Service{expectedRekorLogService, expectedAddedRekorLogService}) { t.Errorf("unexpected Rekor service, expected %v, got %v", expectedRekorLogService, sc.RekorLogURLs()) } if !servicesEqual(sc.TimestampAuthorityURLs(), []Service{expectedTSAService, expectedAddedTSAService}) { t.Errorf("unexpected TSA service, expected %v, got %v", expectedTSAService, sc.TimestampAuthorityURLs()) } } sigstore-go-0.7.1/pkg/root/timestamping_authority.go000066400000000000000000000042461477477521700227200ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package root import ( "bytes" "crypto/x509" "errors" "time" tsaverification "github.com/sigstore/timestamp-authority/pkg/verification" ) type Timestamp struct { Time time.Time URI string } type TimestampingAuthority interface { Verify(signedTimestamp []byte, signatureBytes []byte) (*Timestamp, error) } type SigstoreTimestampingAuthority struct { Root *x509.Certificate Intermediates []*x509.Certificate Leaf *x509.Certificate ValidityPeriodStart time.Time ValidityPeriodEnd time.Time URI string } var _ TimestampingAuthority = &SigstoreTimestampingAuthority{} func (tsa *SigstoreTimestampingAuthority) Verify(signedTimestamp []byte, signatureBytes []byte) (*Timestamp, error) { trustedRootVerificationOptions := tsaverification.VerifyOpts{ Roots: []*x509.Certificate{tsa.Root}, Intermediates: tsa.Intermediates, TSACertificate: tsa.Leaf, } // Ensure timestamp responses are from trusted sources timestamp, err := tsaverification.VerifyTimestampResponse(signedTimestamp, bytes.NewReader(signatureBytes), trustedRootVerificationOptions) if err != nil { return nil, err } if !tsa.ValidityPeriodStart.IsZero() && timestamp.Time.Before(tsa.ValidityPeriodStart) { return nil, errors.New("timestamp is before the validity period start") } if !tsa.ValidityPeriodEnd.IsZero() && timestamp.Time.After(tsa.ValidityPeriodEnd) { return nil, errors.New("timestamp is after the validity period end") } // All above verification successful, so return nil return &Timestamp{Time: timestamp.Time, URI: tsa.URI}, nil } sigstore-go-0.7.1/pkg/root/timestamping_authority_test.go000066400000000000000000000122751477477521700237600ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package root_test import ( "bytes" "crypto" "crypto/ecdsa" "crypto/x509" "encoding/asn1" "testing" "time" "github.com/digitorus/timestamp" "github.com/sigstore/sigstore-go/pkg/root" "github.com/stretchr/testify/require" ) func TestTimestampingAuthority(t *testing.T) { privKey, rootCert, intermediateCert, leafCert, now := genChain(t, true) _, rootCert2, intermediateCert2, leafCert2, _ := genChain(t, true) artifactBytes := []byte("artifact") // generate a timestamping response tsrBytes, err := generateTimestampingResponse(artifactBytes, leafCert, privKey) if err != nil { t.Fatal(err) } for _, test := range []struct { name string tsa *root.SigstoreTimestampingAuthority expectError bool tsrBytes []byte artifactBytes []byte }{ { name: "normal", tsa: &root.SigstoreTimestampingAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert, }, Leaf: leafCert, ValidityPeriodStart: now.Add(-time.Hour), ValidityPeriodEnd: now.Add(time.Hour), }, tsrBytes: tsrBytes, artifactBytes: artifactBytes, expectError: false, }, { name: "no validity period defined", tsa: &root.SigstoreTimestampingAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert, }, Leaf: leafCert, }, tsrBytes: tsrBytes, artifactBytes: artifactBytes, expectError: false, }, { name: "before validity period", tsa: &root.SigstoreTimestampingAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert, }, Leaf: leafCert, ValidityPeriodStart: now.Add(time.Hour), ValidityPeriodEnd: now.Add(2 * time.Hour), }, tsrBytes: tsrBytes, artifactBytes: artifactBytes, expectError: true, }, { name: "after validity period", tsa: &root.SigstoreTimestampingAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert, }, Leaf: leafCert, ValidityPeriodStart: now.Add(-2 * time.Hour), ValidityPeriodEnd: now.Add(-time.Hour), }, tsrBytes: tsrBytes, artifactBytes: artifactBytes, expectError: true, }, { name: "missing intermediate", tsa: &root.SigstoreTimestampingAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{}, Leaf: leafCert, }, tsrBytes: tsrBytes, artifactBytes: artifactBytes, expectError: true, }, { name: "bad leaf", tsa: &root.SigstoreTimestampingAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert, }, Leaf: leafCert2, }, tsrBytes: tsrBytes, artifactBytes: artifactBytes, expectError: true, }, { name: "bad intermediate", tsa: &root.SigstoreTimestampingAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert2, }, Leaf: leafCert, }, tsrBytes: tsrBytes, artifactBytes: artifactBytes, expectError: true, }, { name: "bad root", tsa: &root.SigstoreTimestampingAuthority{ Root: rootCert2, Intermediates: []*x509.Certificate{ intermediateCert, }, Leaf: leafCert, }, tsrBytes: tsrBytes, artifactBytes: artifactBytes, expectError: true, }, { name: "signature over wrong artifact", tsa: &root.SigstoreTimestampingAuthority{ Root: rootCert, Intermediates: []*x509.Certificate{ intermediateCert, }, Leaf: leafCert, }, tsrBytes: tsrBytes, artifactBytes: []byte("wrong artifact"), expectError: true, }, } { t.Run(test.name, func(t *testing.T) { chains, err := test.tsa.Verify(test.tsrBytes, test.artifactBytes) if test.expectError { require.Error(t, err) require.Nil(t, chains) } else { require.NoError(t, err) require.NotNil(t, chains) } }) } } func generateTimestampingResponse(sig []byte, tsaCert *x509.Certificate, tsaKey *ecdsa.PrivateKey) ([]byte, error) { tsq, err := timestamp.CreateRequest(bytes.NewReader(sig), ×tamp.RequestOptions{ Hash: crypto.SHA256, }) if err != nil { return nil, err } req, err := timestamp.ParseRequest([]byte(tsq)) if err != nil { return nil, err } tsTemplate := timestamp.Timestamp{ HashAlgorithm: req.HashAlgorithm, HashedMessage: req.HashedMessage, Time: time.Now(), Policy: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 2}, Ordering: false, Qualified: false, ExtraExtensions: req.Extensions, } return tsTemplate.CreateResponseWithOpts(tsaCert, tsaKey, crypto.SHA256) } sigstore-go-0.7.1/pkg/root/trusted_material.go000066400000000000000000000125251477477521700214560ustar00rootroot00000000000000// 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 root import ( "fmt" "time" "github.com/sigstore/sigstore/pkg/signature" ) type TrustedMaterial interface { TimestampingAuthorities() []TimestampingAuthority FulcioCertificateAuthorities() []CertificateAuthority RekorLogs() map[string]*TransparencyLog CTLogs() map[string]*TransparencyLog PublicKeyVerifier(string) (TimeConstrainedVerifier, error) } type BaseTrustedMaterial struct{} func (b *BaseTrustedMaterial) TimestampingAuthorities() []TimestampingAuthority { return []TimestampingAuthority{} } func (b *BaseTrustedMaterial) FulcioCertificateAuthorities() []CertificateAuthority { return []CertificateAuthority{} } func (b *BaseTrustedMaterial) RekorLogs() map[string]*TransparencyLog { return map[string]*TransparencyLog{} } func (b *BaseTrustedMaterial) CTLogs() map[string]*TransparencyLog { return map[string]*TransparencyLog{} } func (b *BaseTrustedMaterial) PublicKeyVerifier(_ string) (TimeConstrainedVerifier, error) { return nil, fmt.Errorf("public key verifier not found") } type TrustedMaterialCollection []TrustedMaterial // Ensure types implement interfaces var _ TrustedMaterial = &BaseTrustedMaterial{} var _ TrustedMaterial = TrustedMaterialCollection{} func (tmc TrustedMaterialCollection) PublicKeyVerifier(keyID string) (TimeConstrainedVerifier, error) { for _, tm := range tmc { verifier, err := tm.PublicKeyVerifier(keyID) if err == nil { return verifier, nil } } return nil, fmt.Errorf("public key verifier not found for keyID: %s", keyID) } func (tmc TrustedMaterialCollection) TimestampingAuthorities() []TimestampingAuthority { var timestampingAuthorities []TimestampingAuthority for _, tm := range tmc { timestampingAuthorities = append(timestampingAuthorities, tm.TimestampingAuthorities()...) } return timestampingAuthorities } func (tmc TrustedMaterialCollection) FulcioCertificateAuthorities() []CertificateAuthority { var certAuthorities []CertificateAuthority for _, tm := range tmc { certAuthorities = append(certAuthorities, tm.FulcioCertificateAuthorities()...) } return certAuthorities } func (tmc TrustedMaterialCollection) RekorLogs() map[string]*TransparencyLog { rekorLogs := make(map[string]*TransparencyLog) for _, tm := range tmc { for keyID, tlogVerifier := range tm.RekorLogs() { rekorLogs[keyID] = tlogVerifier } } return rekorLogs } func (tmc TrustedMaterialCollection) CTLogs() map[string]*TransparencyLog { rekorLogs := make(map[string]*TransparencyLog) for _, tm := range tmc { for keyID, tlogVerifier := range tm.CTLogs() { rekorLogs[keyID] = tlogVerifier } } return rekorLogs } type ValidityPeriodChecker interface { ValidAtTime(time.Time) bool } type TimeConstrainedVerifier interface { ValidityPeriodChecker signature.Verifier } type TrustedPublicKeyMaterial struct { BaseTrustedMaterial publicKeyVerifier func(string) (TimeConstrainedVerifier, error) } func (tr *TrustedPublicKeyMaterial) PublicKeyVerifier(keyID string) (TimeConstrainedVerifier, error) { return tr.publicKeyVerifier(keyID) } func NewTrustedPublicKeyMaterial(publicKeyVerifier func(string) (TimeConstrainedVerifier, error)) *TrustedPublicKeyMaterial { return &TrustedPublicKeyMaterial{ publicKeyVerifier: publicKeyVerifier, } } // ExpiringKey is a TimeConstrainedVerifier with a static validity period. type ExpiringKey struct { signature.Verifier validityPeriodStart time.Time validityPeriodEnd time.Time } var _ TimeConstrainedVerifier = &ExpiringKey{} // ValidAtTime returns true if the key is valid at the given time. If the // validity period start time is not set, the key is considered valid for all // times before the end time. Likewise, if the validity period end time is not // set, the key is considered valid for all times after the start time. func (k *ExpiringKey) ValidAtTime(t time.Time) bool { if !k.validityPeriodStart.IsZero() && t.Before(k.validityPeriodStart) { return false } if !k.validityPeriodEnd.IsZero() && t.After(k.validityPeriodEnd) { return false } return true } // NewExpiringKey returns a new ExpiringKey with the given validity period func NewExpiringKey(verifier signature.Verifier, validityPeriodStart, validityPeriodEnd time.Time) *ExpiringKey { return &ExpiringKey{ Verifier: verifier, validityPeriodStart: validityPeriodStart, validityPeriodEnd: validityPeriodEnd, } } // NewTrustedPublicKeyMaterialFromMapping returns a TrustedPublicKeyMaterial from a map of key IDs to // ExpiringKeys. func NewTrustedPublicKeyMaterialFromMapping(trustedPublicKeys map[string]*ExpiringKey) *TrustedPublicKeyMaterial { return NewTrustedPublicKeyMaterial(func(keyID string) (TimeConstrainedVerifier, error) { expiringKey, ok := trustedPublicKeys[keyID] if !ok { return nil, fmt.Errorf("public key not found for keyID: %s", keyID) } return expiringKey, nil }) } sigstore-go-0.7.1/pkg/root/trusted_root.go000066400000000000000000000376161477477521700206530ustar00rootroot00000000000000// 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 root import ( "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rsa" "crypto/x509" "encoding/hex" "fmt" "log" "os" "sync" "time" protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" prototrustroot "github.com/sigstore/protobuf-specs/gen/pb-go/trustroot/v1" "github.com/sigstore/sigstore-go/pkg/tuf" "google.golang.org/protobuf/encoding/protojson" ) const TrustedRootMediaType01 = "application/vnd.dev.sigstore.trustedroot+json;version=0.1" type TrustedRoot struct { BaseTrustedMaterial trustedRoot *prototrustroot.TrustedRoot rekorLogs map[string]*TransparencyLog certificateAuthorities []CertificateAuthority ctLogs map[string]*TransparencyLog timestampingAuthorities []TimestampingAuthority } type TransparencyLog struct { BaseURL string ID []byte ValidityPeriodStart time.Time ValidityPeriodEnd time.Time // This is the hash algorithm used by the Merkle tree HashFunc crypto.Hash PublicKey crypto.PublicKey // The hash algorithm used during signature creation SignatureHashFunc crypto.Hash } const ( defaultTrustedRoot = "trusted_root.json" ) func (tr *TrustedRoot) TimestampingAuthorities() []TimestampingAuthority { return tr.timestampingAuthorities } func (tr *TrustedRoot) FulcioCertificateAuthorities() []CertificateAuthority { return tr.certificateAuthorities } func (tr *TrustedRoot) RekorLogs() map[string]*TransparencyLog { return tr.rekorLogs } func (tr *TrustedRoot) CTLogs() map[string]*TransparencyLog { return tr.ctLogs } func (tr *TrustedRoot) MarshalJSON() ([]byte, error) { err := tr.constructProtoTrustRoot() if err != nil { return nil, fmt.Errorf("failed constructing protobuf TrustRoot representation: %w", err) } return protojson.Marshal(tr.trustedRoot) } func NewTrustedRootFromProtobuf(protobufTrustedRoot *prototrustroot.TrustedRoot) (trustedRoot *TrustedRoot, err error) { if protobufTrustedRoot.GetMediaType() != TrustedRootMediaType01 { return nil, fmt.Errorf("unsupported TrustedRoot media type: %s", protobufTrustedRoot.GetMediaType()) } trustedRoot = &TrustedRoot{trustedRoot: protobufTrustedRoot} trustedRoot.rekorLogs, err = ParseTransparencyLogs(protobufTrustedRoot.GetTlogs()) if err != nil { return nil, err } trustedRoot.certificateAuthorities, err = ParseCertificateAuthorities(protobufTrustedRoot.GetCertificateAuthorities()) if err != nil { return nil, err } trustedRoot.timestampingAuthorities, err = ParseTimestampingAuthorities(protobufTrustedRoot.GetTimestampAuthorities()) if err != nil { return nil, err } trustedRoot.ctLogs, err = ParseTransparencyLogs(protobufTrustedRoot.GetCtlogs()) if err != nil { return nil, err } return trustedRoot, nil } func ParseTransparencyLogs(tlogs []*prototrustroot.TransparencyLogInstance) (transparencyLogs map[string]*TransparencyLog, err error) { transparencyLogs = make(map[string]*TransparencyLog) for _, tlog := range tlogs { if tlog.GetHashAlgorithm() != protocommon.HashAlgorithm_SHA2_256 { return nil, fmt.Errorf("unsupported tlog hash algorithm: %s", tlog.GetHashAlgorithm()) } if tlog.GetLogId() == nil { return nil, fmt.Errorf("tlog missing log ID") } if tlog.GetLogId().GetKeyId() == nil { return nil, fmt.Errorf("tlog missing log ID key ID") } encodedKeyID := hex.EncodeToString(tlog.GetLogId().GetKeyId()) if tlog.GetPublicKey() == nil { return nil, fmt.Errorf("tlog missing public key") } if tlog.GetPublicKey().GetRawBytes() == nil { return nil, fmt.Errorf("tlog missing public key raw bytes") } var hashFunc crypto.Hash switch tlog.GetHashAlgorithm() { case protocommon.HashAlgorithm_SHA2_256: hashFunc = crypto.SHA256 default: return nil, fmt.Errorf("unsupported hash function for the tlog") } tlogEntry := &TransparencyLog{ BaseURL: tlog.GetBaseUrl(), ID: tlog.GetLogId().GetKeyId(), HashFunc: hashFunc, SignatureHashFunc: crypto.SHA256, } switch tlog.GetPublicKey().GetKeyDetails() { case protocommon.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256, protocommon.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384, protocommon.PublicKeyDetails_PKIX_ECDSA_P521_SHA_512: key, err := x509.ParsePKIXPublicKey(tlog.GetPublicKey().GetRawBytes()) if err != nil { return nil, err } var ecKey *ecdsa.PublicKey var ok bool if ecKey, ok = key.(*ecdsa.PublicKey); !ok { return nil, fmt.Errorf("tlog public key is not ECDSA: %s", tlog.GetPublicKey().GetKeyDetails()) } tlogEntry.PublicKey = ecKey // This key format has public key in PKIX RSA format and PKCS1#1v1.5 or RSASSA-PSS signature case protocommon.PublicKeyDetails_PKIX_RSA_PKCS1V15_2048_SHA256, protocommon.PublicKeyDetails_PKIX_RSA_PKCS1V15_3072_SHA256, protocommon.PublicKeyDetails_PKIX_RSA_PKCS1V15_4096_SHA256: key, err := x509.ParsePKIXPublicKey(tlog.GetPublicKey().GetRawBytes()) if err != nil { return nil, err } var rsaKey *rsa.PublicKey var ok bool if rsaKey, ok = key.(*rsa.PublicKey); !ok { return nil, fmt.Errorf("tlog public key is not RSA: %s", tlog.GetPublicKey().GetKeyDetails()) } tlogEntry.PublicKey = rsaKey case protocommon.PublicKeyDetails_PKIX_ED25519: //nolint:staticcheck key, err := x509.ParsePKIXPublicKey(tlog.GetPublicKey().GetRawBytes()) if err != nil { return nil, err } var edKey ed25519.PublicKey var ok bool if edKey, ok = key.(ed25519.PublicKey); !ok { return nil, fmt.Errorf("tlog public key is not RSA: %s", tlog.GetPublicKey().GetKeyDetails()) } tlogEntry.PublicKey = edKey // This key format is deprecated, but currently in use for Sigstore staging instance case protocommon.PublicKeyDetails_PKCS1_RSA_PKCS1V5: //nolint:staticcheck key, err := x509.ParsePKCS1PublicKey(tlog.GetPublicKey().GetRawBytes()) if err != nil { return nil, err } tlogEntry.PublicKey = key default: return nil, fmt.Errorf("unsupported tlog public key type: %s", tlog.GetPublicKey().GetKeyDetails()) } tlogEntry.SignatureHashFunc = getSignatureHashAlgo(tlogEntry.PublicKey) transparencyLogs[encodedKeyID] = tlogEntry if validFor := tlog.GetPublicKey().GetValidFor(); validFor != nil { if validFor.GetStart() != nil { transparencyLogs[encodedKeyID].ValidityPeriodStart = validFor.GetStart().AsTime() } else { return nil, fmt.Errorf("tlog missing public key validity period start time") } if validFor.GetEnd() != nil { transparencyLogs[encodedKeyID].ValidityPeriodEnd = validFor.GetEnd().AsTime() } } else { return nil, fmt.Errorf("tlog missing public key validity period") } } return transparencyLogs, nil } func ParseCertificateAuthorities(certAuthorities []*prototrustroot.CertificateAuthority) (certificateAuthorities []CertificateAuthority, err error) { certificateAuthorities = make([]CertificateAuthority, len(certAuthorities)) for i, certAuthority := range certAuthorities { certificateAuthority, err := ParseCertificateAuthority(certAuthority) if err != nil { return nil, err } certificateAuthorities[i] = certificateAuthority } return certificateAuthorities, nil } func ParseCertificateAuthority(certAuthority *prototrustroot.CertificateAuthority) (*FulcioCertificateAuthority, error) { if certAuthority == nil { return nil, fmt.Errorf("CertificateAuthority is nil") } certChain := certAuthority.GetCertChain() if certChain == nil { return nil, fmt.Errorf("CertificateAuthority missing cert chain") } chainLen := len(certChain.GetCertificates()) if chainLen < 1 { return nil, fmt.Errorf("CertificateAuthority cert chain is empty") } certificateAuthority := &FulcioCertificateAuthority{ URI: certAuthority.Uri, } for i, cert := range certChain.GetCertificates() { parsedCert, err := x509.ParseCertificate(cert.RawBytes) if err != nil { return nil, err } if i < chainLen-1 { certificateAuthority.Intermediates = append(certificateAuthority.Intermediates, parsedCert) } else { certificateAuthority.Root = parsedCert } } validFor := certAuthority.GetValidFor() if validFor != nil { start := validFor.GetStart() if start != nil { certificateAuthority.ValidityPeriodStart = start.AsTime() } end := validFor.GetEnd() if end != nil { certificateAuthority.ValidityPeriodEnd = end.AsTime() } } certificateAuthority.URI = certAuthority.Uri return certificateAuthority, nil } func ParseTimestampingAuthorities(certAuthorities []*prototrustroot.CertificateAuthority) (timestampingAuthorities []TimestampingAuthority, err error) { timestampingAuthorities = make([]TimestampingAuthority, len(certAuthorities)) for i, certAuthority := range certAuthorities { timestampingAuthority, err := ParseTimestampingAuthority(certAuthority) if err != nil { return nil, err } timestampingAuthorities[i] = timestampingAuthority } return timestampingAuthorities, nil } func ParseTimestampingAuthority(certAuthority *prototrustroot.CertificateAuthority) (TimestampingAuthority, error) { if certAuthority == nil { return nil, fmt.Errorf("CertificateAuthority is nil") } certChain := certAuthority.GetCertChain() if certChain == nil { return nil, fmt.Errorf("CertificateAuthority missing cert chain") } chainLen := len(certChain.GetCertificates()) if chainLen < 1 { return nil, fmt.Errorf("CertificateAuthority cert chain is empty") } timestampingAuthority := &SigstoreTimestampingAuthority{ URI: certAuthority.Uri, } for i, cert := range certChain.GetCertificates() { parsedCert, err := x509.ParseCertificate(cert.RawBytes) if err != nil { return nil, err } switch { case i == 0 && !parsedCert.IsCA: timestampingAuthority.Leaf = parsedCert case i < chainLen-1: timestampingAuthority.Intermediates = append(timestampingAuthority.Intermediates, parsedCert) case i == chainLen-1: timestampingAuthority.Root = parsedCert } } validFor := certAuthority.GetValidFor() if validFor != nil { start := validFor.GetStart() if start != nil { timestampingAuthority.ValidityPeriodStart = start.AsTime() } end := validFor.GetEnd() if end != nil { timestampingAuthority.ValidityPeriodEnd = end.AsTime() } } timestampingAuthority.URI = certAuthority.Uri return timestampingAuthority, nil } func NewTrustedRootFromPath(path string) (*TrustedRoot, error) { trustedrootJSON, err := os.ReadFile(path) if err != nil { return nil, err } return NewTrustedRootFromJSON(trustedrootJSON) } // NewTrustedRootFromJSON returns the Sigstore trusted root. func NewTrustedRootFromJSON(rootJSON []byte) (*TrustedRoot, error) { pbTrustedRoot, err := NewTrustedRootProtobuf(rootJSON) if err != nil { return nil, err } return NewTrustedRootFromProtobuf(pbTrustedRoot) } // NewTrustedRootProtobuf returns the Sigstore trusted root as a protobuf. func NewTrustedRootProtobuf(rootJSON []byte) (*prototrustroot.TrustedRoot, error) { pbTrustedRoot := &prototrustroot.TrustedRoot{} err := protojson.Unmarshal(rootJSON, pbTrustedRoot) if err != nil { return nil, err } return pbTrustedRoot, nil } // NewTrustedRoot initializes a TrustedRoot object from a mediaType string, list of Fulcio // certificate authorities, list of timestamp authorities and maps of ctlogs and rekor // transparency log instances. func NewTrustedRoot(mediaType string, certificateAuthorities []CertificateAuthority, certificateTransparencyLogs map[string]*TransparencyLog, timestampAuthorities []TimestampingAuthority, transparencyLogs map[string]*TransparencyLog) (*TrustedRoot, error) { // document that we assume 1 cert chain per target and with certs already ordered from leaf to root if mediaType != TrustedRootMediaType01 { return nil, fmt.Errorf("unsupported TrustedRoot media type: %s", TrustedRootMediaType01) } tr := &TrustedRoot{ certificateAuthorities: certificateAuthorities, ctLogs: certificateTransparencyLogs, timestampingAuthorities: timestampAuthorities, rekorLogs: transparencyLogs, } return tr, nil } // FetchTrustedRoot fetches the Sigstore trusted root from TUF and returns it. func FetchTrustedRoot() (*TrustedRoot, error) { return FetchTrustedRootWithOptions(tuf.DefaultOptions()) } // FetchTrustedRootWithOptions fetches the trusted root from TUF with the given options and returns it. func FetchTrustedRootWithOptions(opts *tuf.Options) (*TrustedRoot, error) { client, err := tuf.New(opts) if err != nil { return nil, err } return GetTrustedRoot(client) } // GetTrustedRoot returns the trusted root func GetTrustedRoot(c *tuf.Client) (*TrustedRoot, error) { jsonBytes, err := c.GetTarget(defaultTrustedRoot) if err != nil { return nil, err } return NewTrustedRootFromJSON(jsonBytes) } func getSignatureHashAlgo(pubKey crypto.PublicKey) crypto.Hash { var h crypto.Hash switch pk := pubKey.(type) { case *rsa.PublicKey: h = crypto.SHA256 case *ecdsa.PublicKey: switch pk.Curve { case elliptic.P256(): h = crypto.SHA256 case elliptic.P384(): h = crypto.SHA384 case elliptic.P521(): h = crypto.SHA512 default: h = crypto.SHA256 } case ed25519.PublicKey: h = crypto.SHA512 default: h = crypto.SHA256 } return h } // LiveTrustedRoot is a wrapper around TrustedRoot that periodically // refreshes the trusted root from TUF. This is needed for long-running // processes to ensure that the trusted root does not expire. type LiveTrustedRoot struct { *TrustedRoot mu sync.RWMutex } // NewLiveTrustedRoot returns a LiveTrustedRoot that will periodically // refresh the trusted root from TUF. func NewLiveTrustedRoot(opts *tuf.Options) (*LiveTrustedRoot, error) { return NewLiveTrustedRootFromTarget(opts, defaultTrustedRoot) } // NewLiveTrustedRootFromTarget returns a LiveTrustedRoot that will // periodically refresh the trusted root from TUF using the provided target. func NewLiveTrustedRootFromTarget(opts *tuf.Options, target string) (*LiveTrustedRoot, error) { client, err := tuf.New(opts) if err != nil { return nil, err } b, err := client.GetTarget(target) if err != nil { return nil, err } tr, err := NewTrustedRootFromJSON(b) if err != nil { return nil, err } ltr := &LiveTrustedRoot{ TrustedRoot: tr, mu: sync.RWMutex{}, } ticker := time.NewTicker(time.Hour * 24) go func() { for range ticker.C { client, err = tuf.New(opts) if err != nil { log.Printf("error creating TUF client: %v", err) } b, err := client.GetTarget(target) if err != nil { log.Printf("error fetching trusted root: %v", err) } newTr, err := NewTrustedRootFromJSON(b) if err != nil { log.Printf("error fetching trusted root: %v", err) continue } ltr.mu.Lock() ltr.TrustedRoot = newTr ltr.mu.Unlock() } }() return ltr, nil } func (l *LiveTrustedRoot) TimestampingAuthorities() []TimestampingAuthority { l.mu.RLock() defer l.mu.RUnlock() return l.TrustedRoot.TimestampingAuthorities() } func (l *LiveTrustedRoot) FulcioCertificateAuthorities() []CertificateAuthority { l.mu.RLock() defer l.mu.RUnlock() return l.TrustedRoot.FulcioCertificateAuthorities() } func (l *LiveTrustedRoot) RekorLogs() map[string]*TransparencyLog { l.mu.RLock() defer l.mu.RUnlock() return l.TrustedRoot.RekorLogs() } func (l *LiveTrustedRoot) CTLogs() map[string]*TransparencyLog { l.mu.RLock() defer l.mu.RUnlock() return l.TrustedRoot.CTLogs() } func (l *LiveTrustedRoot) PublicKeyVerifier(keyID string) (TimeConstrainedVerifier, error) { l.mu.RLock() defer l.mu.RUnlock() return l.TrustedRoot.PublicKeyVerifier(keyID) } sigstore-go-0.7.1/pkg/root/trusted_root_create.go000066400000000000000000000203611477477521700221630ustar00rootroot00000000000000// 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 root import ( "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rsa" "crypto/x509" "fmt" "sort" "time" protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" prototrustroot "github.com/sigstore/protobuf-specs/gen/pb-go/trustroot/v1" timestamppb "google.golang.org/protobuf/types/known/timestamppb" ) func (tr *TrustedRoot) constructProtoTrustRoot() error { tr.trustedRoot = &prototrustroot.TrustedRoot{} tr.trustedRoot.MediaType = TrustedRootMediaType01 for logID, transparencyLog := range tr.rekorLogs { tlProto, err := transparencyLogToProtobufTL(transparencyLog) if err != nil { return fmt.Errorf("failed converting rekor log %s to protobuf: %w", logID, err) } tr.trustedRoot.Tlogs = append(tr.trustedRoot.Tlogs, tlProto) } // ensure stable sorting of the slice sortTlogSlice(tr.trustedRoot.Tlogs) for logID, ctLog := range tr.ctLogs { ctProto, err := transparencyLogToProtobufTL(ctLog) if err != nil { return fmt.Errorf("failed converting ctlog %s to protobuf: %w", logID, err) } tr.trustedRoot.Ctlogs = append(tr.trustedRoot.Ctlogs, ctProto) } // ensure stable sorting of the slice sortTlogSlice(tr.trustedRoot.Ctlogs) for _, ca := range tr.certificateAuthorities { caProto, err := certificateAuthorityToProtobufCA(ca.(*FulcioCertificateAuthority)) if err != nil { return fmt.Errorf("failed converting fulcio cert chain to protobuf: %w", err) } tr.trustedRoot.CertificateAuthorities = append(tr.trustedRoot.CertificateAuthorities, caProto) } // ensure stable sorting of the slice sortCASlice(tr.trustedRoot.CertificateAuthorities) for _, ca := range tr.timestampingAuthorities { caProto, err := timestampingAuthorityToProtobufCA(ca.(*SigstoreTimestampingAuthority)) if err != nil { return fmt.Errorf("failed converting TSA cert chain to protobuf: %w", err) } tr.trustedRoot.TimestampAuthorities = append(tr.trustedRoot.TimestampAuthorities, caProto) } // ensure stable sorting of the slice sortCASlice(tr.trustedRoot.TimestampAuthorities) return nil } func sortCASlice(slc []*prototrustroot.CertificateAuthority) { sort.Slice(slc, func(i, j int) bool { iTime := time.Unix(0, 0) jTime := time.Unix(0, 0) if slc[i].ValidFor.Start != nil { iTime = slc[i].ValidFor.Start.AsTime() } if slc[j].ValidFor.Start != nil { jTime = slc[j].ValidFor.Start.AsTime() } return iTime.Before(jTime) }) } func sortTlogSlice(slc []*prototrustroot.TransparencyLogInstance) { sort.Slice(slc, func(i, j int) bool { iTime := time.Unix(0, 0) jTime := time.Unix(0, 0) if slc[i].PublicKey.ValidFor.Start != nil { iTime = slc[i].PublicKey.ValidFor.Start.AsTime() } if slc[j].PublicKey.ValidFor.Start != nil { jTime = slc[j].PublicKey.ValidFor.Start.AsTime() } return iTime.Before(jTime) }) } func certificateAuthorityToProtobufCA(ca *FulcioCertificateAuthority) (*prototrustroot.CertificateAuthority, error) { org := "" if len(ca.Root.Subject.Organization) > 0 { org = ca.Root.Subject.Organization[0] } var allCerts []*protocommon.X509Certificate for _, intermed := range ca.Intermediates { allCerts = append(allCerts, &protocommon.X509Certificate{RawBytes: intermed.Raw}) } if ca.Root == nil { return nil, fmt.Errorf("root certificate is nil") } allCerts = append(allCerts, &protocommon.X509Certificate{RawBytes: ca.Root.Raw}) caProto := prototrustroot.CertificateAuthority{ Uri: ca.URI, Subject: &protocommon.DistinguishedName{ Organization: org, CommonName: ca.Root.Subject.CommonName, }, ValidFor: &protocommon.TimeRange{ Start: timestamppb.New(ca.ValidityPeriodStart), }, CertChain: &protocommon.X509CertificateChain{ Certificates: allCerts, }, } if !ca.ValidityPeriodEnd.IsZero() { caProto.ValidFor.End = timestamppb.New(ca.ValidityPeriodEnd) } return &caProto, nil } func timestampingAuthorityToProtobufCA(ca *SigstoreTimestampingAuthority) (*prototrustroot.CertificateAuthority, error) { org := "" if len(ca.Root.Subject.Organization) > 0 { org = ca.Root.Subject.Organization[0] } var allCerts []*protocommon.X509Certificate if ca.Leaf != nil { allCerts = append(allCerts, &protocommon.X509Certificate{RawBytes: ca.Leaf.Raw}) } for _, intermed := range ca.Intermediates { allCerts = append(allCerts, &protocommon.X509Certificate{RawBytes: intermed.Raw}) } if ca.Root == nil { return nil, fmt.Errorf("root certificate is nil") } allCerts = append(allCerts, &protocommon.X509Certificate{RawBytes: ca.Root.Raw}) caProto := prototrustroot.CertificateAuthority{ Uri: ca.URI, Subject: &protocommon.DistinguishedName{ Organization: org, CommonName: ca.Root.Subject.CommonName, }, ValidFor: &protocommon.TimeRange{ Start: timestamppb.New(ca.ValidityPeriodStart), }, CertChain: &protocommon.X509CertificateChain{ Certificates: allCerts, }, } if !ca.ValidityPeriodEnd.IsZero() { caProto.ValidFor.End = timestamppb.New(ca.ValidityPeriodEnd) } return &caProto, nil } func transparencyLogToProtobufTL(tl *TransparencyLog) (*prototrustroot.TransparencyLogInstance, error) { hashAlgo, err := hashAlgorithmToProtobufHashAlgorithm(tl.HashFunc) if err != nil { return nil, fmt.Errorf("failed converting hash algorithm to protobuf: %w", err) } publicKey, err := publicKeyToProtobufPublicKey(tl.PublicKey, tl.ValidityPeriodStart, tl.ValidityPeriodEnd) if err != nil { return nil, fmt.Errorf("failed converting public key to protobuf: %w", err) } trProto := prototrustroot.TransparencyLogInstance{ BaseUrl: tl.BaseURL, HashAlgorithm: hashAlgo, PublicKey: publicKey, LogId: &protocommon.LogId{ KeyId: tl.ID, }, } return &trProto, nil } func hashAlgorithmToProtobufHashAlgorithm(hashAlgorithm crypto.Hash) (protocommon.HashAlgorithm, error) { switch hashAlgorithm { case crypto.SHA256: return protocommon.HashAlgorithm_SHA2_256, nil case crypto.SHA384: return protocommon.HashAlgorithm_SHA2_384, nil case crypto.SHA512: return protocommon.HashAlgorithm_SHA2_512, nil case crypto.SHA3_256: return protocommon.HashAlgorithm_SHA3_256, nil case crypto.SHA3_384: return protocommon.HashAlgorithm_SHA3_384, nil default: return 0, fmt.Errorf("unsupported hash algorithm for Merkle tree: %v", hashAlgorithm) } } func publicKeyToProtobufPublicKey(publicKey crypto.PublicKey, start time.Time, end time.Time) (*protocommon.PublicKey, error) { pkd := protocommon.PublicKey{ ValidFor: &protocommon.TimeRange{ Start: timestamppb.New(start), }, } if !end.IsZero() { pkd.ValidFor.End = timestamppb.New(end) } rawBytes, err := x509.MarshalPKIXPublicKey(publicKey) if err != nil { return nil, fmt.Errorf("failed marshalling public key: %w", err) } pkd.RawBytes = rawBytes switch p := publicKey.(type) { case *ecdsa.PublicKey: switch p.Curve { case elliptic.P256(): pkd.KeyDetails = protocommon.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256 case elliptic.P384(): pkd.KeyDetails = protocommon.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384 case elliptic.P521(): pkd.KeyDetails = protocommon.PublicKeyDetails_PKIX_ECDSA_P521_SHA_512 default: return nil, fmt.Errorf("unsupported curve for ecdsa key: %T", p.Curve) } case *rsa.PublicKey: switch p.Size() * 8 { case 2048: pkd.KeyDetails = protocommon.PublicKeyDetails_PKIX_RSA_PKCS1V15_2048_SHA256 case 3072: pkd.KeyDetails = protocommon.PublicKeyDetails_PKIX_RSA_PKCS1V15_3072_SHA256 case 4096: pkd.KeyDetails = protocommon.PublicKeyDetails_PKIX_RSA_PKCS1V15_4096_SHA256 default: return nil, fmt.Errorf("unsupported public modulus for RSA key: %d", p.Size()) } case ed25519.PublicKey: pkd.KeyDetails = protocommon.PublicKeyDetails_PKIX_ED25519 default: return nil, fmt.Errorf("unknown public key type: %T", p) } return &pkd, nil } sigstore-go-0.7.1/pkg/root/trusted_root_test.go000066400000000000000000000151661477477521700217060ustar00rootroot00000000000000// 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 root import ( "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" "encoding/json" "encoding/pem" "os" "strings" "testing" "time" protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" "github.com/sigstore/sigstore/pkg/signature" "github.com/stretchr/testify/assert" ) const pkixRsa = `-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3wqI/TysUiKTgY1bz+wd JfEOil4MEsRASKGzJddZ6x9hb+rn2UVoJmuxN62XI0TMoMn4mukgfCgY6jgTB58V +/LaeSA8Wz1p4gOxhk1mcgbF4HyxR+xlRgYfH4iSbXy+Ez/8ZjM2OO68fKr4JZEA 5LXZkhJr32JqH+UiFw/wgSPWA8aV0AfRAXHdekJ48B1ChxJTrOJWSPTnj/E0lfLV srJKtXDuC8T0vFmVU726tI6fODsEE6VrSahvw1ENUHzI34sbfrmrggwPO4iMAQvq wu2gn2lx6ajWsh806FItiXN+DuizMnx4KMBI0IJynoQpWOFbstGiV0LygZkQ6soz vwIDAQAB -----END PUBLIC KEY-----` const pkixEd25519 = `-----BEGIN PUBLIC KEY----- MCowBQYDK2VwAyEA9wy4umF4RHQ8UQXo8fzEQNBWE4GsBMkCzQPAfHvkf/s= -----END PUBLIC KEY-----` func TestGetSigstoreTrustedRoot(t *testing.T) { trustedrootJSON, err := os.ReadFile("../../examples/trusted-root-public-good.json") assert.Nil(t, err) trustedRoot, err := NewTrustedRootFromJSON(trustedrootJSON) assert.Nil(t, err) assert.NotNil(t, trustedRoot) } type singleKeyVerifier struct { BaseTrustedMaterial verifier TimeConstrainedVerifier } func (f *singleKeyVerifier) PublicKeyVerifier(_ string) (TimeConstrainedVerifier, error) { return f.verifier, nil } type nonExpiringVerifier struct { signature.Verifier } func (*nonExpiringVerifier) ValidAtTime(_ time.Time) bool { return true } func TestTrustedMaterialCollectionECDSA(t *testing.T) { trustedrootJSON, err := os.ReadFile("../../examples/trusted-root-public-good.json") assert.NoError(t, err) trustedRoot, err := NewTrustedRootFromJSON(trustedrootJSON) assert.NoError(t, err) key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) assert.NoError(t, err) ecVerifier, err := signature.LoadECDSAVerifier(key.Public().(*ecdsa.PublicKey), crypto.SHA256) assert.NoError(t, err) verifier := &nonExpiringVerifier{ecVerifier} trustedMaterialCollection := TrustedMaterialCollection{trustedRoot, &singleKeyVerifier{verifier: verifier}} verifier2, err := trustedMaterialCollection.PublicKeyVerifier("foo") assert.NoError(t, err) assert.Equal(t, verifier, verifier2) // verify that a JSON round trip works jsonBytes, err := json.Marshal(trustedRoot) assert.NoError(t, err) _, err = NewTrustedRootFromJSON(jsonBytes) assert.NoError(t, err) } func TestTrustedMaterialCollectionED25519(t *testing.T) { trustedrootJSON, err := os.ReadFile("../../examples/trusted-root-public-good.json") assert.NoError(t, err) trustedRootProto, err := NewTrustedRootProtobuf(trustedrootJSON) assert.NoError(t, err) for _, ctlog := range trustedRootProto.Ctlogs { ctlog.PublicKey.KeyDetails = protocommon.PublicKeyDetails_PKIX_ED25519 derBytes, _ := pem.Decode([]byte(pkixEd25519)) ctlog.PublicKey.RawBytes = derBytes.Bytes } for _, tlog := range trustedRootProto.Tlogs { tlog.PublicKey.KeyDetails = protocommon.PublicKeyDetails_PKIX_ED25519 derBytes, _ := pem.Decode([]byte(pkixEd25519)) tlog.PublicKey.RawBytes = derBytes.Bytes } trustedRoot, err := NewTrustedRootFromProtobuf(trustedRootProto) assert.NoError(t, err) for _, tlog := range trustedRoot.rekorLogs { assert.Equal(t, tlog.SignatureHashFunc, crypto.SHA512) } key, _, err := ed25519.GenerateKey(rand.Reader) assert.NoError(t, err) ecVerifier, err := signature.LoadED25519Verifier(key) assert.NoError(t, err) verifier := &nonExpiringVerifier{ecVerifier} trustedMaterialCollection := TrustedMaterialCollection{trustedRoot, &singleKeyVerifier{verifier: verifier}} verifier2, err := trustedMaterialCollection.PublicKeyVerifier("foo") assert.NoError(t, err) assert.Equal(t, verifier, verifier2) // verify that a JSON round trip works jsonBytes, err := json.Marshal(trustedRoot) assert.NoError(t, err) _, err = NewTrustedRootFromJSON(jsonBytes) assert.NoError(t, err) } func TestTrustedMaterialCollectionRSA(t *testing.T) { trustedrootJSON, err := os.ReadFile("../../examples/trusted-root-public-good.json") assert.NoError(t, err) trustedRootProto, err := NewTrustedRootProtobuf(trustedrootJSON) assert.NoError(t, err) for _, ctlog := range trustedRootProto.Ctlogs { ctlog.PublicKey.KeyDetails = protocommon.PublicKeyDetails_PKIX_RSA_PKCS1V15_2048_SHA256 derBytes, _ := pem.Decode([]byte(pkixRsa)) ctlog.PublicKey.RawBytes = derBytes.Bytes } for _, tlog := range trustedRootProto.Tlogs { tlog.PublicKey.KeyDetails = protocommon.PublicKeyDetails_PKIX_RSA_PKCS1V15_2048_SHA256 derBytes, _ := pem.Decode([]byte(pkixRsa)) tlog.PublicKey.RawBytes = derBytes.Bytes } trustedRoot, err := NewTrustedRootFromProtobuf(trustedRootProto) assert.NoError(t, err) key, err := rsa.GenerateKey(rand.Reader, 2048) assert.NoError(t, err) ecVerifier, err := signature.LoadRSAPKCS1v15Verifier(key.Public().(*rsa.PublicKey), crypto.SHA256) assert.NoError(t, err) verifier := &nonExpiringVerifier{ecVerifier} trustedMaterialCollection := TrustedMaterialCollection{trustedRoot, &singleKeyVerifier{verifier: verifier}} verifier2, err := trustedMaterialCollection.PublicKeyVerifier("foo") assert.NoError(t, err) assert.Equal(t, verifier, verifier2) // verify that a JSON round trip works jsonBytes, err := json.Marshal(trustedRoot) assert.NoError(t, err) _, err = NewTrustedRootFromJSON(jsonBytes) assert.NoError(t, err) } func TestFromJSONToJSON(t *testing.T) { trustedrootJSON, err := os.ReadFile("../../examples/trusted-root-public-good.json") assert.NoError(t, err) trustedRoot, err := NewTrustedRootFromJSON(trustedrootJSON) assert.NoError(t, err) jsonBytes, err := json.Marshal(trustedRoot) assert.NoError(t, err) // Protobuf JSON serialization intentionally strips second fraction from time, if // the fraction is 0. We do the same to the expected result: // https://github.com/golang/protobuf/blob/b7697bb698b1c56643249ef6179c7cae1478881d/jsonpb/encode.go#L207 trJSONTrimmedTime := strings.ReplaceAll(string(trustedrootJSON), ".000Z\"", "Z\"") assert.JSONEq(t, trJSONTrimmedTime, string(jsonBytes)) } sigstore-go-0.7.1/pkg/sign/000077500000000000000000000000001477477521700155275ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/sign/certificate.go000066400000000000000000000135071477477521700203460ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sign import ( "bytes" "context" "encoding/base64" "encoding/json" "encoding/pem" "errors" "fmt" "io" "math" "net/http" "strings" "time" "github.com/sigstore/sigstore-go/pkg/util" "github.com/sigstore/sigstore/pkg/oauthflow" ) type CertificateProviderOptions struct { // Optional OIDC JWT to send to certificate provider; required for Fulcio IDToken string } type CertificateProvider interface { GetCertificate(context.Context, Keypair, *CertificateProviderOptions) ([]byte, error) } type Fulcio struct { options *FulcioOptions client *http.Client } type FulcioOptions struct { // URL of Fulcio instance BaseURL string // Optional timeout for network requests (default 30s; use negative value for no timeout) Timeout time.Duration // Optional number of times to retry on HTTP 5XX Retries uint // Optional Transport (for dependency injection) Transport http.RoundTripper } type fulcioCertRequest struct { PublicKeyRequest publicKeyRequest `json:"publicKeyRequest"` } type publicKeyRequest struct { PublicKey publicKey `json:"publicKey"` ProofOfPossession string `json:"proofOfPossession"` } type publicKey struct { Algorithm string `json:"algorithm"` Content string `json:"content"` } type fulcioResponse struct { SignedCertificateEmbeddedSct signedCertificateEmbeddedSct `json:"signedCertificateEmbeddedSct"` SignedCertificateDetachedSct signedCertificateDetachedSct `json:"signedCertificateDetachedSct"` } type signedCertificateEmbeddedSct struct { Chain chain `json:"chain"` } type signedCertificateDetachedSct struct { Chain chain `json:"chain"` } type chain struct { Certificates []string `json:"certificates"` } func NewFulcio(opts *FulcioOptions) *Fulcio { fulcio := &Fulcio{options: opts} fulcio.client = &http.Client{ Transport: opts.Transport, } if opts.Timeout >= 0 { if opts.Timeout == 0 { opts.Timeout = 30 * time.Second } fulcio.client.Timeout = opts.Timeout } return fulcio } // Returns DER-encoded code signing certificate func (f *Fulcio) GetCertificate(ctx context.Context, keypair Keypair, opts *CertificateProviderOptions) ([]byte, error) { if opts.IDToken == "" { return nil, errors.New("fetching certificate from Fulcio requires IDToken to be set") } // Get JWT from identity token // // Note that the contents of this token are untrusted. Fulcio will perform // the token verification. tokenParts := strings.Split(opts.IDToken, ".") if len(tokenParts) < 2 { return nil, errors.New("unable to get subject from identity token") } jwtString, err := base64.RawURLEncoding.DecodeString(tokenParts[1]) if err != nil { return nil, err } subject, err := oauthflow.SubjectFromUnverifiedToken(jwtString) if err != nil { return nil, err } // Sign JWT subject for proof of possession subjectSignature, _, err := keypair.SignData(ctx, []byte(subject)) if err != nil { return nil, err } // Make Fulcio certificate request keypairPem, err := keypair.GetPublicKeyPem() if err != nil { return nil, err } certRequest := fulcioCertRequest{ PublicKeyRequest: publicKeyRequest{ PublicKey: publicKey{ Algorithm: keypair.GetKeyAlgorithm(), Content: keypairPem, }, ProofOfPossession: base64.StdEncoding.EncodeToString(subjectSignature), }, } requestJSON, err := json.Marshal(&certRequest) if err != nil { return nil, err } // TODO: For now we are using our own HTTP client // // https://github.com/sigstore/fulcio/pkg/api's client could be used in the // future, when it supports the v2 API attempts := uint(0) var response *http.Response for attempts <= f.options.Retries { request, err := http.NewRequest("POST", f.options.BaseURL+"/api/v2/signingCert", bytes.NewBuffer(requestJSON)) if err != nil { return nil, err } request.Header.Add("Authorization", "Bearer "+opts.IDToken) request.Header.Add("Content-Type", "application/json") request.Header.Add("User-Agent", util.ConstructUserAgent()) response, err = f.client.Do(request) if err != nil { return nil, err } if (response.StatusCode < 500 || response.StatusCode >= 600) && response.StatusCode != 429 { // Not a retryable HTTP status code, so don't retry break } delay := time.Duration(math.Pow(2, float64(attempts))) timer := time.NewTimer(delay * time.Second) select { case <-ctx.Done(): timer.Stop() return nil, ctx.Err() case <-timer.C: } attempts++ } body, err := io.ReadAll(response.Body) if err != nil { return nil, err } if response.StatusCode != 200 { return nil, fmt.Errorf("Fulcio returned %d: %s", response.StatusCode, string(body)) } // Assemble bundle from Fulcio response var fulcioResp fulcioResponse err = json.Unmarshal(body, &fulcioResp) if err != nil { return nil, err } var cert []byte switch { case len(fulcioResp.SignedCertificateEmbeddedSct.Chain.Certificates) > 0: cert = []byte(fulcioResp.SignedCertificateEmbeddedSct.Chain.Certificates[0]) case len(fulcioResp.SignedCertificateDetachedSct.Chain.Certificates) > 0: cert = []byte(fulcioResp.SignedCertificateDetachedSct.Chain.Certificates[0]) default: return nil, errors.New("Fulcio returned no certificates") } certBlock, _ := pem.Decode(cert) if certBlock == nil { return nil, errors.New("unable to parse Fulcio certificate") } return certBlock.Bytes, nil } sigstore-go-0.7.1/pkg/sign/certificate_test.go000066400000000000000000000075501477477521700214060ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sign import ( "bytes" "context" "encoding/json" "encoding/pem" "io" "net/http" "sync" "testing" "github.com/stretchr/testify/assert" "github.com/sigstore/sigstore-go/pkg/testing/ca" ) var virtualSigstore *ca.VirtualSigstore var virtualSigstoreOnce sync.Once var virtualSigstoreErr error func setupVirtualSigstore() { if virtualSigstore == nil { virtualSigstore, virtualSigstoreErr = ca.NewVirtualSigstore() } } func getFulcioResponse(detachedSct bool) (*http.Response, error) { virtualSigstoreOnce.Do(setupVirtualSigstore) if virtualSigstoreErr != nil { return nil, virtualSigstoreErr } leafCert, _, err := virtualSigstore.GenerateLeafCert("identity", "issuer") if err != nil { return nil, err } certPEM := string(pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: leafCert.Raw, })) var responseStruct fulcioResponse if detachedSct { responseStruct = fulcioResponse{ SignedCertificateDetachedSct: signedCertificateDetachedSct{ Chain: chain{ Certificates: []string{certPEM}, }, }, } } else { responseStruct = fulcioResponse{ SignedCertificateEmbeddedSct: signedCertificateEmbeddedSct{ Chain: chain{ Certificates: []string{certPEM}, }, }, } } fulcioJSON, err := json.Marshal(responseStruct) if err != nil { return nil, err } response := &http.Response{ StatusCode: 200, Body: io.NopCloser(bytes.NewReader(fulcioJSON)), } return response, nil } type mockFulcio struct { detachedSct bool } func (m *mockFulcio) RoundTrip(_ *http.Request) (*http.Response, error) { return getFulcioResponse(m.detachedSct) } type failFirstFulcio struct { Count int detachedSct bool } func (f *failFirstFulcio) RoundTrip(_ *http.Request) (*http.Response, error) { if f.Count <= 0 { f.Count++ response := &http.Response{ StatusCode: 500, Body: io.NopCloser(bytes.NewReader([]byte(""))), } return response, nil } return getFulcioResponse(f.detachedSct) } func Test_GetCertificate(t *testing.T) { opts := &FulcioOptions{Retries: 1, Transport: &mockFulcio{}} fulcio := NewFulcio(opts) ctx := context.TODO() keypair, err := NewEphemeralKeypair(nil) assert.Nil(t, err) // Test malformed idtoken certOpts := &CertificateProviderOptions{ IDToken: "idtoken.notbase64.stuff", } cert, err := fulcio.GetCertificate(ctx, keypair, certOpts) assert.Nil(t, cert) assert.NotNil(t, err) // Test happy path certOpts.IDToken = "idtoken.eyJzdWIiOiJzdWJqZWN0In0K.stuff" // #nosec G101 cert, err = fulcio.GetCertificate(ctx, keypair, certOpts) assert.NotNil(t, cert) assert.Nil(t, err) // Test successful retry roundTripper := &failFirstFulcio{} retryFulcioOpts := &FulcioOptions{Retries: 1, Transport: roundTripper} retryFulcio := NewFulcio(retryFulcioOpts) cert, err = retryFulcio.GetCertificate(ctx, keypair, certOpts) assert.NotNil(t, cert) assert.Nil(t, err) // Test unsuccessful retry roundTripper.Count = -1 cert, err = retryFulcio.GetCertificate(ctx, keypair, certOpts) assert.Nil(t, cert) assert.NotNil(t, err) // Test detached SCT detachedOpts := &FulcioOptions{Retries: 1, Transport: &mockFulcio{detachedSct: true}} detachedFulcio := NewFulcio(detachedOpts) cert, err = detachedFulcio.GetCertificate(ctx, keypair, certOpts) assert.NotNil(t, cert) assert.NoError(t, err) } sigstore-go-0.7.1/pkg/sign/content.go000066400000000000000000000041751477477521700175370ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sign import ( "fmt" protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" protodsse "github.com/sigstore/protobuf-specs/gen/pb-go/dsse" ) type Content interface { // Return the data to be signed PreAuthEncoding() []byte // Add something that satisfies protobundle.isBundle_Content to bundle Bundle(bundle *protobundle.Bundle, signature, digest []byte, hashAlgorithm protocommon.HashAlgorithm) } type PlainData struct { Data []byte } func (pd *PlainData) PreAuthEncoding() []byte { return pd.Data } func (pd *PlainData) Bundle(bundle *protobundle.Bundle, signature, digest []byte, hashAlgorithm protocommon.HashAlgorithm) { bundle.Content = &protobundle.Bundle_MessageSignature{ MessageSignature: &protocommon.MessageSignature{ MessageDigest: &protocommon.HashOutput{ Algorithm: hashAlgorithm, Digest: digest, }, Signature: signature, }, } } type DSSEData struct { Data []byte PayloadType string } func (d *DSSEData) PreAuthEncoding() []byte { pae := fmt.Sprintf("DSSEv1 %d %s %d %s", len(d.PayloadType), d.PayloadType, len(d.Data), d.Data) return []byte(pae) } func (d *DSSEData) Bundle(bundle *protobundle.Bundle, signature, _ []byte, _ protocommon.HashAlgorithm) { sig := &protodsse.Signature{ Sig: signature, } bundle.Content = &protobundle.Bundle_DsseEnvelope{ DsseEnvelope: &protodsse.Envelope{ Payload: []byte(d.Data), PayloadType: d.PayloadType, Signatures: []*protodsse.Signature{sig}, }, } } sigstore-go-0.7.1/pkg/sign/content_test.go000066400000000000000000000030241477477521700205660ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sign import ( "strings" "testing" protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" "github.com/stretchr/testify/assert" ) var data = []byte("qwerty") func Test_PlainData(t *testing.T) { pd := PlainData{Data: data} pae := pd.PreAuthEncoding() assert.Equal(t, pae, data) bundle := &protobundle.Bundle{} pd.Bundle(bundle, data, data, protocommon.HashAlgorithm_SHA2_256) assert.NotNil(t, bundle.GetMessageSignature()) assert.Nil(t, bundle.GetDsseEnvelope()) } func Test_DSSEData(t *testing.T) { dsseData := DSSEData{Data: data, PayloadType: "something"} pae := dsseData.PreAuthEncoding() assert.True(t, strings.HasPrefix(string(pae), "DSSE")) bundle := &protobundle.Bundle{} dsseData.Bundle(bundle, data, data, protocommon.HashAlgorithm_SHA2_256) assert.Nil(t, bundle.GetMessageSignature()) assert.NotNil(t, bundle.GetDsseEnvelope()) } sigstore-go-0.7.1/pkg/sign/keys.go000066400000000000000000000065671477477521700170470ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sign import ( "context" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" _ "crypto/sha512" // if user chooses SHA2-384 or SHA2-512 for hash "crypto/x509" "encoding/base64" "errors" protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" "github.com/sigstore/sigstore/pkg/cryptoutils" ) type Keypair interface { GetHashAlgorithm() protocommon.HashAlgorithm GetHint() []byte GetKeyAlgorithm() string GetPublicKeyPem() (string, error) SignData(ctx context.Context, data []byte) ([]byte, []byte, error) } type EphemeralKeypairOptions struct { // Optional hint of for signing key Hint []byte // TODO: support additional key algorithms } type EphemeralKeypair struct { options *EphemeralKeypairOptions privateKey *ecdsa.PrivateKey hashAlgorithm protocommon.HashAlgorithm } func NewEphemeralKeypair(opts *EphemeralKeypairOptions) (*EphemeralKeypair, error) { if opts == nil { opts = &EphemeralKeypairOptions{} } privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, err } if opts.Hint == nil { pubKeyBytes, err := x509.MarshalPKIXPublicKey(privateKey.Public()) if err != nil { return nil, err } hashedBytes := sha256.Sum256(pubKeyBytes) opts.Hint = []byte(base64.StdEncoding.EncodeToString(hashedBytes[:])) } ephemeralKeypair := EphemeralKeypair{ options: opts, privateKey: privateKey, hashAlgorithm: protocommon.HashAlgorithm_SHA2_256, } return &ephemeralKeypair, nil } func (e *EphemeralKeypair) GetHashAlgorithm() protocommon.HashAlgorithm { return e.hashAlgorithm } func (e *EphemeralKeypair) GetHint() []byte { return e.options.Hint } func (e *EphemeralKeypair) GetKeyAlgorithm() string { return "ECDSA" } func (e *EphemeralKeypair) GetPublicKeyPem() (string, error) { pubKeyBytes, err := cryptoutils.MarshalPublicKeyToPEM(e.privateKey.Public()) if err != nil { return "", err } return string(pubKeyBytes), nil } func getHashFunc(hashAlgorithm protocommon.HashAlgorithm) (crypto.Hash, error) { switch hashAlgorithm { case protocommon.HashAlgorithm_SHA2_256: return crypto.Hash(crypto.SHA256), nil case protocommon.HashAlgorithm_SHA2_384: return crypto.Hash(crypto.SHA384), nil case protocommon.HashAlgorithm_SHA2_512: return crypto.Hash(crypto.SHA512), nil default: var hash crypto.Hash return hash, errors.New("unsupported hash algorithm") } } func (e *EphemeralKeypair) SignData(_ context.Context, data []byte) ([]byte, []byte, error) { hashFunc, err := getHashFunc(e.hashAlgorithm) if err != nil { return nil, nil, err } hasher := hashFunc.New() hasher.Write(data) digest := hasher.Sum(nil) signature, err := e.privateKey.Sign(rand.Reader, digest, hashFunc) if err != nil { return nil, nil, err } return signature, digest, nil } sigstore-go-0.7.1/pkg/sign/keys_test.go000066400000000000000000000032611477477521700200720ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sign import ( "context" "testing" protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" "github.com/stretchr/testify/assert" ) func Test_EphemeralKeypair(t *testing.T) { opts := &EphemeralKeypairOptions{ Hint: []byte("asdf"), } ctx := context.TODO() ephemeralKeypair, err := NewEphemeralKeypair(opts) assert.NotNil(t, ephemeralKeypair) assert.Nil(t, err) hashAlgorithm := ephemeralKeypair.GetHashAlgorithm() assert.Equal(t, hashAlgorithm, protocommon.HashAlgorithm_SHA2_256) hint := ephemeralKeypair.GetHint() assert.Equal(t, hint, []byte("asdf")) keyAlgorithm := ephemeralKeypair.GetKeyAlgorithm() assert.Equal(t, keyAlgorithm, "ECDSA") pem, err := ephemeralKeypair.GetPublicKeyPem() assert.NotEqual(t, pem, "") assert.Nil(t, err) signature, digest, err := ephemeralKeypair.SignData(ctx, []byte("hello world")) assert.NotEqual(t, signature, "") assert.NotEqual(t, digest, "") assert.Nil(t, err) defaultEphemeralKeypair, err := NewEphemeralKeypair(nil) assert.Nil(t, err) hint = defaultEphemeralKeypair.GetHint() assert.NotEqual(t, hint, []byte("")) } sigstore-go-0.7.1/pkg/sign/signer.go000066400000000000000000000124771477477521700173600ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sign import ( "bytes" "context" "encoding/pem" "errors" protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" verifyBundle "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/verify" ) const bundleV03MediaType = "application/vnd.dev.sigstore.bundle.v0.3+json" type BundleOptions struct { // Optional certificate provider to get code signing certificate from. // // Typically a Fulcio instance; resulting bundle will contain a certificate // for its verification material content instead of a public key. CertificateProvider CertificateProvider // Optional options for certificate provider // // Some certificate authorities may require options to be set CertificateProviderOptions *CertificateProviderOptions // Optional list of timestamp authorities to contact for inclusion in bundle TimestampAuthorities []*TimestampAuthority // Optional list of Rekor instances to get transparency log entry from. // // Supports hashedrekord and dsse entry types. TransparencyLogs []Transparency // Optional context for retrying network requests Context context.Context // Optional trusted root to verify signed bundle TrustedRoot root.TrustedMaterial } func Bundle(content Content, keypair Keypair, opts BundleOptions) (*protobundle.Bundle, error) { if keypair == nil { return nil, errors.New("must provide a keypair for signing, like EphemeralKeypair") } if opts.Context == nil { opts.Context = context.TODO() } bundle := &protobundle.Bundle{MediaType: bundleV03MediaType} verifierOptions := []verify.VerifierOption{} // Sign content and add to bundle signature, digest, err := keypair.SignData(opts.Context, content.PreAuthEncoding()) if err != nil { return nil, err } content.Bundle(bundle, signature, digest, keypair.GetHashAlgorithm()) // Add verification information to bundle var verifierPEM []byte if opts.CertificateProvider != nil { pubKeyBytes, err := opts.CertificateProvider.GetCertificate(opts.Context, keypair, opts.CertificateProviderOptions) if err != nil { return nil, err } bundle.VerificationMaterial = &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_Certificate{ Certificate: &protocommon.X509Certificate{ RawBytes: pubKeyBytes, }, }, } verifierPEM = pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: pubKeyBytes, }) } else { bundle.VerificationMaterial = &protobundle.VerificationMaterial{ Content: &protobundle.VerificationMaterial_PublicKey{ PublicKey: &protocommon.PublicKeyIdentifier{ Hint: string(keypair.GetHint()), }, }, } pubKeyStr, err := keypair.GetPublicKeyPem() if err != nil { return nil, err } verifierPEM = []byte(pubKeyStr) } for _, timestampAuthority := range opts.TimestampAuthorities { timestampBytes, err := timestampAuthority.GetTimestamp(opts.Context, signature) if err != nil { return nil, err } signedTimestamp := &protocommon.RFC3161SignedTimestamp{ SignedTimestamp: timestampBytes, } if bundle.VerificationMaterial.TimestampVerificationData == nil { bundle.VerificationMaterial.TimestampVerificationData = &protobundle.TimestampVerificationData{} } bundle.VerificationMaterial.TimestampVerificationData.Rfc3161Timestamps = append(bundle.VerificationMaterial.TimestampVerificationData.Rfc3161Timestamps, signedTimestamp) verifierOptions = append(verifierOptions, verify.WithSignedTimestamps(len(opts.TimestampAuthorities))) } if len(opts.TransparencyLogs) > 0 { for _, transparency := range opts.TransparencyLogs { err = transparency.GetTransparencyLogEntry(verifierPEM, bundle) if err != nil { return nil, err } } verifierOptions = append(verifierOptions, verify.WithTransparencyLog(len(opts.TransparencyLogs)), verify.WithIntegratedTimestamps(len(opts.TransparencyLogs))) } if opts.TrustedRoot != nil && len(verifierOptions) > 0 { sev, err := verify.NewSignedEntityVerifier(opts.TrustedRoot, verifierOptions...) if err != nil { return nil, err } protobundle, err := verifyBundle.NewBundle(bundle) if err != nil { return nil, err } // Generally, you should provide an artifact when verifying. // // However, we just signed the DSSE object trusting the user has // referenced the artifact(s) they intended. artifactOpts := verify.WithoutArtifactUnsafe() if bundle.GetMessageSignature() != nil { artifactOpts = verify.WithArtifact(bytes.NewReader(content.PreAuthEncoding())) } policy := verify.NewPolicy(artifactOpts, verify.WithoutIdentitiesUnsafe()) _, err = sev.Verify(protobundle, policy) if err != nil { return nil, err } } return bundle, nil } sigstore-go-0.7.1/pkg/sign/signer_test.go000066400000000000000000000020721477477521700204050ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sign import ( "testing" "github.com/stretchr/testify/assert" ) func Test_Bundle(t *testing.T) { content := &PlainData{Data: []byte("qwerty")} opts := BundleOptions{} // Test requiring Keypair bundle, err := Bundle(content, nil, opts) assert.Nil(t, bundle) assert.NotNil(t, err) // Test minimal happy path keypair, err := NewEphemeralKeypair(nil) assert.Nil(t, err) bundle, err = Bundle(content, keypair, opts) assert.NotNil(t, bundle) assert.Nil(t, err) } sigstore-go-0.7.1/pkg/sign/timestamping.go000066400000000000000000000057451477477521700205720ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sign import ( "bytes" "context" "crypto" "crypto/sha256" "fmt" "io" "math" "net/http" "time" "github.com/digitorus/timestamp" "github.com/sigstore/sigstore-go/pkg/util" ) type TimestampAuthorityOptions struct { // Full URL (with path) of Timestamp Authority endpoint URL string // Optional timeout for network requests (default 30s; use negative value for no timeout) Timeout time.Duration // Optional number of times to retry on HTTP 5XX Retries uint // Optional Transport (for dependency injection) Transport http.RoundTripper } type TimestampAuthority struct { options *TimestampAuthorityOptions client *http.Client } func NewTimestampAuthority(opts *TimestampAuthorityOptions) *TimestampAuthority { ta := &TimestampAuthority{options: opts} ta.client = &http.Client{ Transport: opts.Transport, } if opts.Timeout >= 0 { if opts.Timeout == 0 { opts.Timeout = 30 * time.Second } ta.client.Timeout = opts.Timeout } return ta } func (ta *TimestampAuthority) GetTimestamp(ctx context.Context, signature []byte) ([]byte, error) { signatureHash := sha256.Sum256(signature) req := ×tamp.Request{ HashAlgorithm: crypto.SHA256, HashedMessage: signatureHash[:], } reqBytes, err := req.Marshal() if err != nil { return nil, err } attempts := uint(0) var response *http.Response for attempts <= ta.options.Retries { request, err := http.NewRequest("POST", ta.options.URL, bytes.NewReader(reqBytes)) if err != nil { return nil, err } request.Header.Add("Content-Type", "application/timestamp-query") request.Header.Add("User-Agent", util.ConstructUserAgent()) response, err = ta.client.Do(request) if err != nil { return nil, err } if (response.StatusCode < 500 || response.StatusCode >= 600) && response.StatusCode != 429 { // Not a retryable HTTP status code, so don't retry break } delay := time.Duration(math.Pow(2, float64(attempts))) timer := time.NewTimer(delay * time.Second) select { case <-ctx.Done(): timer.Stop() return nil, ctx.Err() case <-timer.C: } attempts++ } body, err := io.ReadAll(response.Body) if err != nil { return nil, err } if response.StatusCode != 200 && response.StatusCode != 201 { return nil, fmt.Errorf("timestamp authority returned %d: %s", response.StatusCode, string(body)) } _, err = timestamp.ParseResponse(body) if err != nil { return nil, err } return body, nil } sigstore-go-0.7.1/pkg/sign/timestamping_test.go000066400000000000000000000046771477477521700216340ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sign import ( "bytes" "context" "io" "net/http" "testing" "github.com/stretchr/testify/assert" ) func getTSAResponse(req []byte) (*http.Response, error) { virtualSigstoreOnce.Do(setupVirtualSigstore) if virtualSigstoreErr != nil { return nil, virtualSigstoreErr } tsBytes, err := virtualSigstore.TimestampResponse(req) if err != nil { return nil, err } response := &http.Response{ StatusCode: 200, Body: io.NopCloser(bytes.NewReader(tsBytes)), } return response, nil } type mockTSAClient struct{} func (m *mockTSAClient) RoundTrip(req *http.Request) (*http.Response, error) { reqBytes, err := io.ReadAll(req.Body) if err != nil { return nil, err } return getTSAResponse(reqBytes) } type failFirstTSA struct { Count int } func (f *failFirstTSA) RoundTrip(req *http.Request) (*http.Response, error) { if f.Count <= 0 { f.Count++ response := &http.Response{ StatusCode: 500, Body: io.NopCloser(bytes.NewReader([]byte(""))), } return response, nil } reqBytes, err := io.ReadAll(req.Body) if err != nil { return nil, err } return getTSAResponse(reqBytes) } func Test_GetTimestamp(t *testing.T) { // Test happy path opts := &TimestampAuthorityOptions{Retries: 1, Transport: &mockTSAClient{}} tsa := NewTimestampAuthority(opts) ctx := context.TODO() signature := []byte("somestuff") resp, err := tsa.GetTimestamp(ctx, signature) assert.NotNil(t, resp) assert.Nil(t, err) // Test successful retry failFirstClient := &failFirstTSA{} retryOpts := &TimestampAuthorityOptions{Retries: 1, Transport: failFirstClient} retryTSA := NewTimestampAuthority(retryOpts) resp, err = retryTSA.GetTimestamp(ctx, signature) assert.NotNil(t, resp) assert.Nil(t, err) // Test unsuccessful retry failFirstClient.Count = -1 resp, err = retryTSA.GetTimestamp(ctx, signature) assert.Nil(t, resp) assert.NotNil(t, err) } sigstore-go-0.7.1/pkg/sign/transparency.go000066400000000000000000000105261477477521700205730ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sign import ( "context" "encoding/hex" "encoding/json" "errors" "time" protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" protorekor "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "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/pki" "github.com/sigstore/rekor/pkg/tle" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/dsse" "github.com/sigstore/rekor/pkg/types/hashedrekord" rekorUtil "github.com/sigstore/rekor/pkg/util" // To initialize rekor types _ "github.com/sigstore/rekor/pkg/types/dsse/v0.0.1" _ "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1" "github.com/sigstore/sigstore-go/pkg/util" ) type RekorClient interface { CreateLogEntry(params *entries.CreateLogEntryParams, opts ...entries.ClientOption) (*entries.CreateLogEntryCreated, error) } type Transparency interface { GetTransparencyLogEntry([]byte, *protobundle.Bundle) error } type Rekor struct { options *RekorOptions } type RekorOptions struct { // URL of Fulcio instance BaseURL string // Optional timeout for network requests (default 30s; use negative value for no timeout) Timeout time.Duration // Optional number of times to retry Retries uint // Optional client (for dependency injection) Client RekorClient } func NewRekor(opts *RekorOptions) *Rekor { return &Rekor{options: opts} } func (r *Rekor) GetTransparencyLogEntry(pubKeyPEM []byte, b *protobundle.Bundle) error { artifactProperties := types.ArtifactProperties{ PublicKeyBytes: [][]byte{pubKeyPEM}, } dsseEnvelope := b.GetDsseEnvelope() messageSignature := b.GetMessageSignature() verificationMaterial := b.GetVerificationMaterial() bundleCertificate := verificationMaterial.GetCertificate() var proposedEntry models.ProposedEntry switch { case dsseEnvelope != nil: dsseType := dsse.New() artifactBytes, err := json.Marshal(dsseEnvelope) if err != nil { return err } artifactProperties.ArtifactBytes = artifactBytes proposedEntry, err = dsseType.CreateProposedEntry(context.TODO(), "", artifactProperties) if err != nil { return err } case messageSignature != nil: hashedrekordType := hashedrekord.New() if bundleCertificate == nil { return errors.New("hashedrekord requires X.509 certificate") } hexDigest := hex.EncodeToString(messageSignature.MessageDigest.Digest) artifactProperties.PKIFormat = string(pki.X509) artifactProperties.SignatureBytes = messageSignature.Signature artifactProperties.ArtifactHash = rekorUtil.PrefixSHA(hexDigest) var err error proposedEntry, err = hashedrekordType.CreateProposedEntry(context.TODO(), "", artifactProperties) if err != nil { return err } default: return errors.New("unable to find signature in bundle") } params := entries.NewCreateLogEntryParams() if r.options.Timeout >= 0 { if r.options.Timeout == 0 { r.options.Timeout = 30 * time.Second } params.SetTimeout(r.options.Timeout) } params.SetProposedEntry(proposedEntry) if r.options.Client == nil { client, err := client.GetRekorClient(r.options.BaseURL, client.WithUserAgent(util.ConstructUserAgent()), client.WithRetryCount(r.options.Retries)) if err != nil { return err } r.options.Client = client.Entries } resp, err := r.options.Client.CreateLogEntry(params) if err != nil { return err } entry := resp.Payload[resp.ETag] tlogEntry, err := tle.GenerateTransparencyLogEntry(entry) if err != nil { return err } if b.VerificationMaterial.TlogEntries == nil { b.VerificationMaterial.TlogEntries = []*protorekor.TransparencyLogEntry{} } b.VerificationMaterial.TlogEntries = append(b.VerificationMaterial.TlogEntries, tlogEntry) return nil } sigstore-go-0.7.1/pkg/sign/transparency_test.go000066400000000000000000000074331477477521700216350ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sign import ( "context" "crypto" "crypto/ecdsa" "encoding/base64" "encoding/hex" "testing" "time" "github.com/secure-systems-lab/go-securesystemslib/dsse" protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" "github.com/sigstore/rekor/pkg/generated/client/entries" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/sigstore/pkg/signature" sigdsse "github.com/sigstore/sigstore/pkg/signature/dsse" "github.com/stretchr/testify/assert" ) var envelopeBody []byte type mockRekor struct{} func (m *mockRekor) CreateLogEntry(_ *entries.CreateLogEntryParams, _ ...entries.ClientOption) (*entries.CreateLogEntryCreated, error) { virtualSigstoreOnce.Do(setupVirtualSigstore) if virtualSigstoreErr != nil { return nil, virtualSigstoreErr } leafCert, leafPrivKey, err := virtualSigstore.GenerateLeafCert("identity", "issuer") if err != nil { return nil, err } signer, err := signature.LoadECDSASignerVerifier(leafPrivKey, crypto.SHA256) if err != nil { return nil, err } dsseSigner, err := dsse.NewEnvelopeSigner(&sigdsse.SignerAdapter{ SignatureSigner: signer, Pub: leafCert.PublicKey.(*ecdsa.PublicKey), }) if err != nil { return nil, err } envelope, err := dsseSigner.SignPayload(context.TODO(), "application/vnd.in-toto+json", envelopeBody) if err != nil { return nil, err } signature, err := base64.StdEncoding.DecodeString(envelope.Signatures[0].Sig) if err != nil { return nil, err } entry, err := virtualSigstore.GenerateTlogEntry(leafCert, envelope, signature, time.Now().Unix(), false) if err != nil { return nil, err } integratedTime := entry.IntegratedTime().Unix() logID := hex.EncodeToString([]byte(entry.LogKeyID())) logIndex := entry.LogIndex() rootHash := "deadbeef" treeSize := int64(1) payload := map[string]models.LogEntryAnon{ "asdf": { Body: entry.Body(), IntegratedTime: &integratedTime, LogID: &logID, LogIndex: &logIndex, Verification: &models.LogEntryAnonVerification{ InclusionProof: &models.InclusionProof{ Checkpoint: &rootHash, Hashes: []string{}, LogIndex: &logIndex, RootHash: &rootHash, TreeSize: &treeSize, }, }, }, } created := &entries.CreateLogEntryCreated{ETag: "asdf", Payload: payload} return created, nil } func Test_GetTransparencyLogEntry(t *testing.T) { ctx := context.TODO() // First create a bundle with DSSE content keypair, err := NewEphemeralKeypair(nil) assert.Nil(t, err) bundle := &protobundle.Bundle{MediaType: bundleV03MediaType} content := DSSEData{Data: []byte("hello world"), PayloadType: "something"} envelopeBody = content.PreAuthEncoding() signature, digest, err := keypair.SignData(ctx, content.PreAuthEncoding()) assert.Nil(t, err) content.Bundle(bundle, signature, digest, keypair.GetHashAlgorithm()) bundle.VerificationMaterial = &protobundle.VerificationMaterial{} // Test the happy path opts := &RekorOptions{Retries: 1, Client: &mockRekor{}} rekor := NewRekor(opts) pubkey, err := keypair.GetPublicKeyPem() assert.Nil(t, err) err = rekor.GetTransparencyLogEntry([]byte(pubkey), bundle) assert.Nil(t, err) assert.NotNil(t, bundle.VerificationMaterial.TlogEntries) } sigstore-go-0.7.1/pkg/testing/000077500000000000000000000000001477477521700162445ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/testing/ca/000077500000000000000000000000001477477521700166275ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/testing/ca/ca.go000066400000000000000000000510321477477521700175420ustar00rootroot00000000000000// 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 ca import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "math/big" "strings" "time" "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer" "github.com/digitorus/timestamp" "github.com/go-openapi/runtime" "github.com/go-openapi/swag" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/pki" "github.com/sigstore/rekor/pkg/types" "github.com/sigstore/rekor/pkg/types/hashedrekord" "github.com/sigstore/rekor/pkg/types/intoto" "github.com/sigstore/rekor/pkg/types/rekord" "github.com/sigstore/rekor/pkg/util" "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/tlog" "github.com/sigstore/sigstore-go/pkg/verify" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/sigstore/sigstore/pkg/signature" sigdsse "github.com/sigstore/sigstore/pkg/signature/dsse" tsx509 "github.com/sigstore/timestamp-authority/pkg/x509" ) type VirtualSigstore struct { fulcioCA *root.FulcioCertificateAuthority fulcioIntermediateKey *ecdsa.PrivateKey tsaCA *root.SigstoreTimestampingAuthority tsaLeafKey *ecdsa.PrivateKey rekorKey *ecdsa.PrivateKey ctlogKey *ecdsa.PrivateKey publicKeyVerifier map[string]root.TimeConstrainedVerifier } func NewVirtualSigstore() (*VirtualSigstore, error) { ss := &VirtualSigstore{fulcioCA: &root.FulcioCertificateAuthority{}, tsaCA: &root.SigstoreTimestampingAuthority{}} rootCert, rootKey, err := GenerateRootCa() if err != nil { return nil, err } ss.fulcioCA.Root = rootCert ss.tsaCA.Root = rootCert intermediateCert, intermediateKey, _ := GenerateFulcioIntermediate(rootCert, rootKey) ss.fulcioCA.Intermediates = []*x509.Certificate{intermediateCert} ss.fulcioIntermediateKey = intermediateKey tsaIntermediateCert, tsaIntermediateKey, err := GenerateTSAIntermediate(rootCert, rootKey) if err != nil { return nil, err } ss.tsaCA.Intermediates = []*x509.Certificate{tsaIntermediateCert} tsaLeafKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, err } tsaLeafCert, err := GenerateTSALeafCert(time.Now().Add(-5*time.Minute), tsaLeafKey, tsaIntermediateCert, tsaIntermediateKey) if err != nil { return nil, err } ss.tsaCA.Leaf = tsaLeafCert ss.tsaLeafKey = tsaLeafKey ss.fulcioCA.ValidityPeriodStart = time.Now().Add(-5 * time.Hour) ss.fulcioCA.ValidityPeriodEnd = time.Now().Add(time.Hour) ss.tsaCA.ValidityPeriodStart = time.Now().Add(-5 * time.Hour) ss.tsaCA.ValidityPeriodEnd = time.Now().Add(time.Hour) ss.rekorKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, err } ss.ctlogKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, err } return ss, nil } // getLogID calculates the digest of a PKIX-encoded public key func getLogID(pub crypto.PublicKey) (string, error) { pubBytes, err := x509.MarshalPKIXPublicKey(pub) if err != nil { return "", err } digest := sha256.Sum256(pubBytes) return hex.EncodeToString(digest[:]), nil } func (ca *VirtualSigstore) RekorLogID() (string, error) { return getLogID(ca.rekorKey.Public()) } func (ca *VirtualSigstore) RekorSignPayload(payload tlog.RekorPayload) ([]byte, error) { jsonPayload, err := json.Marshal(payload) if err != nil { return nil, err } canonicalized, err := jsoncanonicalizer.Transform(jsonPayload) if err != nil { return nil, err } signer, err := signature.LoadECDSASignerVerifier(ca.rekorKey, crypto.SHA256) if err != nil { return nil, err } bundleSig, err := signer.SignMessage(bytes.NewReader(canonicalized)) if err != nil { return nil, err } return bundleSig, nil } func (ca *VirtualSigstore) GenerateLeafCert(identity, issuer string) (*x509.Certificate, *ecdsa.PrivateKey, error) { privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, err } leafCert, err := GenerateLeafCert(identity, issuer, time.Now(), privKey, ca.fulcioCA.Intermediates[0], ca.fulcioIntermediateKey) if err != nil { return nil, nil, err } return leafCert, privKey, nil } func (ca *VirtualSigstore) Attest(identity, issuer string, envelopeBody []byte) (*TestEntity, error) { // The timing here is important. We need to attest at a time when the leaf // certificate is valid, so we match what GenerateLeafCert() does, above return ca.AttestAtTime(identity, issuer, envelopeBody, time.Now().Add(5*time.Minute), false) } func (ca *VirtualSigstore) AttestAtTime(identity, issuer string, envelopeBody []byte, integratedTime time.Time, generateInclusionProof bool) (*TestEntity, error) { leafCert, leafPrivKey, err := ca.GenerateLeafCert(identity, issuer) if err != nil { return nil, err } signer, err := signature.LoadECDSASignerVerifier(leafPrivKey, crypto.SHA256) if err != nil { return nil, err } dsseSigner, err := dsse.NewEnvelopeSigner(&sigdsse.SignerAdapter{ SignatureSigner: signer, Pub: leafCert.PublicKey.(*ecdsa.PublicKey), }) if err != nil { return nil, err } envelope, err := dsseSigner.SignPayload(context.TODO(), "application/vnd.in-toto+json", envelopeBody) if err != nil { return nil, err } sig, err := base64.StdEncoding.DecodeString(envelope.Signatures[0].Sig) if err != nil { return nil, err } tsr, err := generateTimestampingResponse(sig, ca.tsaCA.Leaf, ca.tsaLeafKey) if err != nil { return nil, err } entry, err := ca.GenerateTlogEntry(leafCert, envelope, sig, integratedTime.Unix(), generateInclusionProof) if err != nil { return nil, err } return &TestEntity{ certChain: []*x509.Certificate{leafCert, ca.fulcioCA.Intermediates[0], ca.fulcioCA.Root}, timestamps: [][]byte{tsr}, envelope: envelope, tlogEntries: []*tlog.Entry{entry}, }, nil } func (ca *VirtualSigstore) Sign(identity, issuer string, artifact []byte) (*TestEntity, error) { return ca.SignAtTime(identity, issuer, artifact, time.Now().Add(5*time.Minute)) } func (ca *VirtualSigstore) SignAtTime(identity, issuer string, artifact []byte, integratedTime time.Time) (*TestEntity, error) { leafCert, leafPrivKey, err := ca.GenerateLeafCert(identity, issuer) if err != nil { return nil, err } signer, err := signature.LoadECDSASignerVerifier(leafPrivKey, crypto.SHA256) if err != nil { return nil, err } digest := sha256.Sum256(artifact) sig, err := signer.SignMessage(bytes.NewReader(artifact)) if err != nil { return nil, err } tsr, err := generateTimestampingResponse(sig, ca.tsaCA.Leaf, ca.tsaLeafKey) if err != nil { return nil, err } entry, err := ca.generateTlogEntryHashedRekord(leafCert, artifact, sig, integratedTime.Unix()) if err != nil { return nil, err } return &TestEntity{ certChain: []*x509.Certificate{leafCert, ca.fulcioCA.Intermediates[0], ca.fulcioCA.Root}, timestamps: [][]byte{tsr}, messageSignature: bundle.NewMessageSignature(digest[:], "SHA2_256", sig), tlogEntries: []*tlog.Entry{entry}, }, nil } func (ca *VirtualSigstore) GenerateTlogEntry(leafCert *x509.Certificate, envelope *dsse.Envelope, sig []byte, integratedTime int64, generateInclusionProof bool) (*tlog.Entry, error) { leafCertPem, err := cryptoutils.MarshalCertificateToPEM(leafCert) if err != nil { return nil, err } envelopeBytes, err := json.Marshal(envelope) if err != nil { return nil, err } rekorBody, err := generateRekorEntry(intoto.KIND, intoto.New().DefaultVersion(), envelopeBytes, leafCertPem, sig) if err != nil { return nil, err } rekorLogID, err := getLogID(ca.rekorKey.Public()) if err != nil { return nil, err } rekorLogIDRaw, err := hex.DecodeString(rekorLogID) if err != nil { return nil, err } logIndex := int64(0) b := createRekorBundle(rekorLogID, integratedTime, logIndex, rekorBody) set, err := ca.RekorSignPayload(*b) if err != nil { return nil, err } rekorBodyRaw, err := base64.StdEncoding.DecodeString(rekorBody) if err != nil { return nil, err } var inclusionProof *models.InclusionProof if generateInclusionProof { inclusionProof, err = ca.GetInclusionProof(rekorBodyRaw) if err != nil { return nil, err } } return tlog.NewEntry(rekorBodyRaw, integratedTime, logIndex, rekorLogIDRaw, set, inclusionProof) } func (ca *VirtualSigstore) GetInclusionProof(rekorBodyRaw []byte) (*models.InclusionProof, error) { signer, err := signature.LoadECDSASignerVerifier(ca.rekorKey, crypto.SHA256) if err != nil { return nil, err } rootHash := sha256.Sum256(append([]byte("\000"), rekorBodyRaw...)) encodedRootHash := hex.EncodeToString(rootHash[:]) scBytes, err := util.CreateAndSignCheckpoint(context.TODO(), "rekor.localhost", int64(123), uint64(42), rootHash[:], signer) if err != nil { return nil, err } return &models.InclusionProof{ TreeSize: swag.Int64(int64(1)), RootHash: &encodedRootHash, LogIndex: swag.Int64(0), Hashes: nil, Checkpoint: swag.String(string(scBytes)), }, nil } func (ca *VirtualSigstore) generateTlogEntryHashedRekord(leafCert *x509.Certificate, artifact []byte, sig []byte, integratedTime int64) (*tlog.Entry, error) { leafCertPem, err := cryptoutils.MarshalCertificateToPEM(leafCert) if err != nil { return nil, err } rekorBody, err := generateRekorEntry(hashedrekord.KIND, hashedrekord.New().DefaultVersion(), artifact, leafCertPem, sig) if err != nil { return nil, err } rekorLogID, err := getLogID(ca.rekorKey.Public()) if err != nil { return nil, err } rekorLogIDRaw, err := hex.DecodeString(rekorLogID) if err != nil { return nil, err } logIndex := int64(1000) b := createRekorBundle(rekorLogID, integratedTime, logIndex, rekorBody) set, err := ca.RekorSignPayload(*b) if err != nil { return nil, err } rekorBodyRaw, err := base64.StdEncoding.DecodeString(rekorBody) if err != nil { return nil, err } return tlog.NewEntry(rekorBodyRaw, integratedTime, logIndex, rekorLogIDRaw, set, nil) } func (ca *VirtualSigstore) PublicKeyVerifier(keyID string) (root.TimeConstrainedVerifier, error) { v, ok := ca.publicKeyVerifier[keyID] if !ok { return nil, fmt.Errorf("public key not found for keyID: %s", keyID) } return v, nil } func generateRekorEntry(kind, version string, artifact []byte, cert []byte, sig []byte) (string, error) { // Generate the Rekor Entry entryImpl, err := createEntry(context.Background(), kind, version, artifact, cert, sig) if err != nil { return "", err } entryBytes, err := entryImpl.Canonicalize(context.Background()) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(entryBytes), nil } func createEntry(ctx context.Context, kind, apiVersion string, blobBytes, certBytes, sigBytes []byte) (types.EntryImpl, error) { props := types.ArtifactProperties{ PublicKeyBytes: [][]byte{certBytes}, PKIFormat: string(pki.X509), } switch kind { case rekord.KIND, intoto.KIND: props.ArtifactBytes = blobBytes props.SignatureBytes = sigBytes case hashedrekord.KIND: blobHash := sha256.Sum256(blobBytes) props.ArtifactHash = strings.ToLower(hex.EncodeToString(blobHash[:])) props.SignatureBytes = sigBytes default: return nil, fmt.Errorf("unexpected entry kind: %s", kind) } proposedEntry, err := types.NewProposedEntry(ctx, kind, apiVersion, props) if err != nil { return nil, err } eimpl, err := types.CreateVersionedEntry(proposedEntry) if err != nil { return nil, err } can, err := types.CanonicalizeEntry(ctx, eimpl) if err != nil { return nil, err } proposedEntryCan, err := models.UnmarshalProposedEntry(bytes.NewReader(can), runtime.JSONConsumer()) if err != nil { return nil, err } return types.UnmarshalEntry(proposedEntryCan) } func createRekorBundle(logID string, integratedTime int64, logIndex int64, rekorEntry string) *tlog.RekorPayload { return &tlog.RekorPayload{ LogID: logID, IntegratedTime: integratedTime, LogIndex: logIndex, Body: rekorEntry, } } func (ca *VirtualSigstore) TimestampResponse(sig []byte) ([]byte, error) { return generateTimestampingResponse(sig, ca.tsaCA.Leaf, ca.tsaLeafKey) } func generateTimestampingResponse(sig []byte, tsaCert *x509.Certificate, tsaKey *ecdsa.PrivateKey) ([]byte, error) { var hash crypto.Hash switch tsaKey.Curve { case elliptic.P256(): hash = crypto.SHA256 case elliptic.P384(): hash = crypto.SHA384 case elliptic.P521(): hash = crypto.SHA512 } tsq, err := timestamp.CreateRequest(bytes.NewReader(sig), ×tamp.RequestOptions{ Hash: hash, }) if err != nil { return nil, err } req, err := timestamp.ParseRequest([]byte(tsq)) if err != nil { return nil, err } tsTemplate := timestamp.Timestamp{ HashAlgorithm: req.HashAlgorithm, HashedMessage: req.HashedMessage, Time: time.Now(), Policy: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 2}, Ordering: false, Qualified: false, ExtraExtensions: req.Extensions, } return tsTemplate.CreateResponseWithOpts(tsaCert, tsaKey, hash) } func (ca *VirtualSigstore) TimestampingAuthorities() []root.TimestampingAuthority { return []root.TimestampingAuthority{ca.tsaCA} } func (ca *VirtualSigstore) FulcioCertificateAuthorities() []root.CertificateAuthority { return []root.CertificateAuthority{ca.fulcioCA} } func (ca *VirtualSigstore) RekorLogs() map[string]*root.TransparencyLog { verifiers := make(map[string]*root.TransparencyLog) logID, err := getLogID(ca.rekorKey.Public()) if err != nil { panic(err) } verifiers[logID] = &root.TransparencyLog{ BaseURL: "test", ID: []byte(logID), ValidityPeriodStart: time.Now().Add(-time.Hour), ValidityPeriodEnd: time.Now().Add(time.Hour), HashFunc: crypto.SHA256, PublicKey: ca.rekorKey.Public(), SignatureHashFunc: crypto.SHA256, } return verifiers } func (ca *VirtualSigstore) CTLogs() map[string]*root.TransparencyLog { verifiers := make(map[string]*root.TransparencyLog) logID, err := getLogID(ca.ctlogKey.Public()) if err != nil { panic(err) } verifiers[logID] = &root.TransparencyLog{ BaseURL: "test", ID: []byte(logID), ValidityPeriodStart: time.Now().Add(-time.Hour), ValidityPeriodEnd: time.Now().Add(time.Hour), HashFunc: crypto.SHA256, PublicKey: ca.ctlogKey.Public(), } return verifiers } type TestEntity struct { certChain []*x509.Certificate envelope *dsse.Envelope messageSignature *bundle.MessageSignature timestamps [][]byte tlogEntries []*tlog.Entry } func (e *TestEntity) VerificationContent() (verify.VerificationContent, error) { return bundle.NewCertificate(e.certChain[0]), nil } func (e *TestEntity) HasInclusionPromise() bool { return true } func (e *TestEntity) HasInclusionProof() bool { for _, tlog := range e.tlogEntries { if tlog.HasInclusionProof() { return true } } return false } func (e *TestEntity) SignatureContent() (verify.SignatureContent, error) { if e.envelope != nil { return &bundle.Envelope{Envelope: e.envelope}, nil } return e.messageSignature, nil } func (e *TestEntity) Timestamps() ([][]byte, error) { return e.timestamps, nil } func (e *TestEntity) TlogEntries() ([]*tlog.Entry, error) { return e.tlogEntries, nil } // Much of the following code is adapted from cosign/test/cert_utils.go 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(-5 * time.Hour), 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 GenerateFulcioIntermediate(rootTemplate *x509.Certificate, rootPriv crypto.Signer) (*x509.Certificate, *ecdsa.PrivateKey, error) { subTemplate := &x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ CommonName: "sigstore-intermediate", Organization: []string{"sigstore.dev"}, }, NotBefore: time.Now().Add(-2 * 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 GenerateTSAIntermediate(rootTemplate *x509.Certificate, rootPriv crypto.Signer) (*x509.Certificate, *ecdsa.PrivateKey, error) { subTemplate := &x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ CommonName: "sigstore-tsa-intermediate", Organization: []string{"sigstore.dev"}, }, NotBefore: time.Now().Add(-2 * time.Minute), NotAfter: time.Now().Add(2 * time.Hour), KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}, 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 string, oidcIssuer string, expiration time.Time, priv *ecdsa.PrivateKey, parentTemplate *x509.Certificate, parentPriv crypto.Signer) (*x509.Certificate, error) { certTemplate := &x509.Certificate{ SerialNumber: big.NewInt(1), EmailAddresses: []string{subject}, NotBefore: expiration, NotAfter: expiration.Add(10 * 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), }, }, } cert, err := createCertificate(certTemplate, parentTemplate, &priv.PublicKey, parentPriv) if err != nil { return nil, err } return cert, nil } func GenerateTSALeafCert(expiration time.Time, priv *ecdsa.PrivateKey, parentTemplate *x509.Certificate, parentPriv crypto.Signer) (*x509.Certificate, error) { timestampExt, err := asn1.Marshal([]asn1.ObjectIdentifier{tsx509.EKUTimestampingOID}) if err != nil { return nil, err } certTemplate := &x509.Certificate{ SerialNumber: big.NewInt(1), NotBefore: expiration, NotAfter: expiration.Add(10 * time.Minute), KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}, IsCA: false, // set EKU to x509.ExtKeyUsageTimeStamping but with a critical bit ExtraExtensions: []pkix.Extension{ { Id: asn1.ObjectIdentifier{2, 5, 29, 37}, Critical: true, Value: timestampExt, }, }, } cert, err := createCertificate(certTemplate, parentTemplate, &priv.PublicKey, parentPriv) if err != nil { return nil, err } return cert, nil } sigstore-go-0.7.1/pkg/testing/data/000077500000000000000000000000001477477521700171555ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/testing/data/bundles/000077500000000000000000000000001477477521700206115ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/testing/data/bundles/dsse-2sigs.sigstore.json000066400000000000000000000202771477477521700253350ustar00rootroot00000000000000{ "mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1", "verificationMaterial": { "tlogEntries": [ { "logIndex": "6800908", "logId": { "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" }, "kindVersion": { "kind": "intoto", "version": "0.0.2" }, "integratedTime": "1668034836", "inclusionPromise": { "signedEntryTimestamp": "MEYCIQCEx8HKsx9hobZjrNqHCSEJvjMEhc2wU2mUwkI7ButQHAIhAPevmw7piNjE2N1OWHmp9S5kBvlVIg93qu4i9yRaswur" }, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoiYXBwbGljYXRpb24vdm5kLmluLXRvdG8ranNvbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU51ZWtORFFXbGhaMEYzU1VKQlowbFZRbTV0V2xKMFpHdFBkR1pQTDB4NVp6UXpOVU5TSzFaSmFTdEJkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUVk5hazEzVFVSTk1WZG9ZMDVOYWtsNFRWUkJOVTFxVFhoTlJFMHhWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWbFZVVTJUM2d2TkRFMFN6QmtRbmd6WXpOWEszUlJOMDVVVTJ4SlZsWXlORmxUWWtJS2JEWldlWFZKVmk5cE1UVkxRMnhUYWxWdk1uRlJkVXRUVlRSRmVtMUlaaklyUlUxcUwxbElXVWhsUWtGRWEwUjRhalpQUTBGVlZYZG5aMFpDVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZVdlZrZERDbFZJVmxnMVlsaG9aWFF5TVdsMFltbzFWM1pvV25GQmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwdENaMjl5UW1kRlJVRmtXalZCWjFGRENrSklkMFZsWjBJMFFVaFpRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZod0t6RUtXRkZCUVVKQlRVRlNla0pHUVdsRlFXdGtTVFk1TkM4NFFqSnlUMlJwZVZsUWJFVnVZMlpTZDJ0MVltOWtUMW8wYW14dE5HYzFNamcxVEd0RFNVaFNjQXB0UjJnMGNEVlBZeXRXYXl0QlMwaE5aSFF3Vm5SRU1pOHJZMkZJVjNsbE1WWnhSRFJ5UjJORFRVRnZSME5EY1VkVFRUUTVRa0ZOUkVFeVkwRk5SMUZEQ2sxQmVVczRkRkUxYlZCRFEybE1hbWxKYzFaelNWcFFXWFpvZGtSU00wUkdiR2sxUVZGNmFsTkRlbU5GVXk5b05XWkNNMGR3WlVSa1FrOTFPWFIxYTJFS2IzZEpkMDVyYjBWWldraFBkR3RoWTB4bGNrdzFNbVZaVTNCV2FtWlljMEV3WjBKTmNVRnViVXhIZDFoMGVtdFdTM0Z4ZWxWdFlscG9RM1k0YlRnMVlncG1kVWxTQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIiwic2lnIjoiVFVWVlEwbEdZeTlDZVV4b1EydFNNbGwwUVUxaWJVcHdNakF5V20xYU5GaFdSMWhHUzJrM2NpdHhOMnh6VGtSUVFXbEZRUzlLZDFneVVHbHNRMHgyYTNGRk9VNUtUVVpMVG00eVF6SnFPR05JTDNwNVJtaFJOalYzY21reVNGazkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiNjZiM2RjZmNhNjU5ZTVkNTA1NjAyYzNjOWFmOGZkMmJmNmE0YWZhY2FjMzNiMTc1ZTFkN2UwZWNhYjEwZjg5MCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjhhNzVmNmM4ZGM0ZDlmNDA3Mjg1YmI0OWQzZTAxOWE1ZTY2NmY0MzQ5OWMyNzA3ZDkyODlhYTI3YzNjMmE2N2UifX19fQ==" } ], "timestampVerificationData": { "rfc3161Timestamps": [] }, "x509CertificateChain": { "certificates": [ { "rawBytes": "MIICnzCCAiagAwIBAgIUBnmZRtdkOtfO/Lyg435CR+VIi+AwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MjMwMDM1WhcNMjIxMTA5MjMxMDM1WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeUE6Ox/414K0dBx3c3W+tQ7NTSlIVV24YSbBl6VyuIV/i15KClSjUo2qQuKSU4EzmHf2+EMj/YHYHeBADkDxj6OCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU/VGCUHVX5bXhet21itbj5WvhZqAwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEXp+1XQAABAMARzBFAiEAkdI694/8B2rOdiyYPlEncfRwkubodOZ4jlm4g5285LkCIHRpmGh4p5Oc+Vk+AKHMdt0VtD2/+caHWye1VqD4rGcCMAoGCCqGSM49BAMDA2cAMGQCMAyK8tQ5mPCCiLjiIsVsIZPYvhvDR3DFli5AQzjSCzcES/h5fB3GpeDdBOu9tukaowIwNkoEYZHOtkacLerL52eYSpVjfXsA0gBMqAnmLGwXtzkVKqqzUmbZhCv8m85bfuIR" }, { "rawBytes": "MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=" }, { "rawBytes": "MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ" } ] } }, "dsseEnvelope": { "payload": "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJzbHNhLXByb3ZlbmFuY2UtMC4wLjcudGd6IiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhNTEyIjogImJiZmQzNzJmYzliZWViNzc3ZmUzNWQwNWJiMTBjMGM3MGE5NzMzZDM2NmE3NWZlZDdkZDE4ODY2YzczOTFkZTNlZTdlODhiYTc0ZGQ0N2FiZjNlMTVjODQ1ZTU0N2ZjZjBlNWMzZGE4MDg1NGM3NTE1NTQyMjRkM2E2ZDRlNTVmIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9zbHNhLXByb3ZlbmFuY2UvZ2hhQHYwIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9zbHNhLXByb3ZlbmFuY2VAMC4wLjEiCiAgICB9LAogICAgImludm9jYXRpb24iOiB7CiAgICAgICJjb25maWdTb3VyY2UiOiB7CiAgICAgICAgInVyaSI6ICJnaXQraHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9zbHNhLXByb3ZlbmFuY2VAcmVmcy9oZWFkcy9kZW1vIiwKICAgICAgICAiZGlnZXN0IjogewogICAgICAgICAgInNoYTEiOiAiMjljZmYzZGQ2NWY3ODBjMzYwMWJkNDU3YWNiNmZlNGU1OTMxYzgyNSIKICAgICAgICB9LAogICAgICAgICJlbnRyeVBvaW50IjogImRlbW8iCiAgICAgIH0sCiAgICAgICJwYXJhbWV0ZXJzIjoge30sCiAgICAgICJlbnZpcm9ubWVudCI6IHsKICAgICAgICAiR0lUSFVCX0VWRU5UX05BTUUiOiAicHVzaCIsCiAgICAgICAgIkdJVEhVQl9KT0IiOiAicnVuLXByb3ZlbmFuY2UtZGVtbyIsCiAgICAgICAgIkdJVEhVQl9SRUYiOiAicmVmcy9oZWFkcy9kZW1vIiwKICAgICAgICAiR0lUSFVCX1JFRl9UWVBFIjogImJyYW5jaCIsCiAgICAgICAgIkdJVEhVQl9SRVBPU0lUT1JZIjogImdpdGh1Yi9zbHNhLXByb3ZlbmFuY2UiLAogICAgICAgICJHSVRIVUJfUkVQT1NJVE9SWV9PV05FUiI6ICJnaXRodWIiLAogICAgICAgICJHSVRIVUJfUlVOX0FUVEVNUFQiOiAiNCIsCiAgICAgICAgIkdJVEhVQl9SVU5fSUQiOiAiMzAyNDA5MTU0NiIsCiAgICAgICAgIkdJVEhVQl9SVU5fTlVNQkVSIjogIjE3IiwKICAgICAgICAiR0lUSFVCX1NIQSI6ICIyOWNmZjNkZDY1Zjc4MGMzNjAxYmQ0NTdhY2I2ZmU0ZTU5MzFjODI1IiwKICAgICAgICAiR0lUSFVCX1dPUktGTE9XIjogImRlbW8iLAogICAgICAgICJJTUFHRV9PUyI6ICJ1YnVudHUyMCIsCiAgICAgICAgIklNQUdFX1ZFUlNJT04iOiAiMjAyMjA5MDUuMSIsCiAgICAgICAgIlJVTk5FUl9BUkNIIjogIlg2NCIsCiAgICAgICAgIlJVTk5FUl9OQU1FIjogIkdpdEh1YiBBY3Rpb25zIDUwIiwKICAgICAgICAiUlVOTkVSX09TIjogIkxpbnV4IgogICAgICB9CiAgICB9LAogICAgIm1ldGFkYXRhIjogewogICAgICAiYnVpbGRJbnZvY2F0aW9uSWQiOiAiMzAyNDA5MTU0Ni00IiwKICAgICAgImNvbXBsZXRlbmVzcyI6IHsKICAgICAgICAicGFyYW1ldGVycyI6IGZhbHNlLAogICAgICAgICJlbnZpcm9ubWVudCI6IGZhbHNlLAogICAgICAgICJtYXRlcmlhbHMiOiBmYWxzZQogICAgICB9LAogICAgICAicmVwcm9kdWNpYmxlIjogZmFsc2UKICAgIH0sCiAgICAibWF0ZXJpYWxzIjogWwogICAgICB7CiAgICAgICAgInVyaSI6ICJnaXQraHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9zbHNhLXByb3ZlbmFuY2UiLAogICAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgICAic2hhMSI6ICIyOWNmZjNkZDY1Zjc4MGMzNjAxYmQ0NTdhY2I2ZmU0ZTU5MzFjODI1IgogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQo=", "payloadType": "application/vnd.in-toto+json", "signatures": [ { "sig": "MEUCIFc/ByLhCkR2YtAMbmJp202ZmZ4XVGXFKi7r+q7lsNDPAiEA/JwX2PilCLvkqE9NJMFKNn2C2j8cH/zyFhQ65wri2HY=", "keyid": "" }, { "sig": "MEUCIFc/ByLhCkR2YtAMbmJp202ZmZ4XVGXFKi7r+q7lsNDPAiEA/JwX2PilCLvkqE9NJMFKNn2C2j8cH/zyFhAAAAAAAHY=", "keyid": "" } ] } } sigstore-go-0.7.1/pkg/testing/data/bundles/dsse.sigstore.json000066400000000000000000000200651477477521700243030ustar00rootroot00000000000000{ "mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1", "verificationMaterial": { "tlogEntries": [ { "logIndex": "6800908", "logId": { "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" }, "kindVersion": { "kind": "intoto", "version": "0.0.2" }, "integratedTime": "1668034836", "inclusionPromise": { "signedEntryTimestamp": "MEYCIQCEx8HKsx9hobZjrNqHCSEJvjMEhc2wU2mUwkI7ButQHAIhAPevmw7piNjE2N1OWHmp9S5kBvlVIg93qu4i9yRaswur" }, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoiYXBwbGljYXRpb24vdm5kLmluLXRvdG8ranNvbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVU51ZWtORFFXbGhaMEYzU1VKQlowbFZRbTV0V2xKMFpHdFBkR1pQTDB4NVp6UXpOVU5TSzFaSmFTdEJkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BKZUUxVVFUVk5hazEzVFVSTk1WZG9ZMDVOYWtsNFRWUkJOVTFxVFhoTlJFMHhWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVWbFZVVTJUM2d2TkRFMFN6QmtRbmd6WXpOWEszUlJOMDVVVTJ4SlZsWXlORmxUWWtJS2JEWldlWFZKVmk5cE1UVkxRMnhUYWxWdk1uRlJkVXRUVlRSRmVtMUlaaklyUlUxcUwxbElXVWhsUWtGRWEwUjRhalpQUTBGVlZYZG5aMFpDVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZVdlZrZERDbFZJVmxnMVlsaG9aWFF5TVdsMFltbzFWM1pvV25GQmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQwaDNXVVJXVWpCU1FWRklMMEpDVlhkRk5FVlNXVzVLY0ZsWE5VRmFSMVp2V1ZjeGJHTnBOV3BpTWpCM1RFRlpTMHQzV1VKQ1FVZEVkbnBCUWdwQlVWRmxZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRESjRkbG95YkhWTU1qbG9aRmhTYjAxSlIwdENaMjl5UW1kRlJVRmtXalZCWjFGRENrSklkMFZsWjBJMFFVaFpRVE5VTUhkaGMySklSVlJLYWtkU05HTnRWMk16UVhGS1MxaHlhbVZRU3pNdmFEUndlV2RET0hBM2J6UkJRVUZIUlZod0t6RUtXRkZCUVVKQlRVRlNla0pHUVdsRlFXdGtTVFk1TkM4NFFqSnlUMlJwZVZsUWJFVnVZMlpTZDJ0MVltOWtUMW8wYW14dE5HYzFNamcxVEd0RFNVaFNjQXB0UjJnMGNEVlBZeXRXYXl0QlMwaE5aSFF3Vm5SRU1pOHJZMkZJVjNsbE1WWnhSRFJ5UjJORFRVRnZSME5EY1VkVFRUUTVRa0ZOUkVFeVkwRk5SMUZEQ2sxQmVVczRkRkUxYlZCRFEybE1hbWxKYzFaelNWcFFXWFpvZGtSU00wUkdiR2sxUVZGNmFsTkRlbU5GVXk5b05XWkNNMGR3WlVSa1FrOTFPWFIxYTJFS2IzZEpkMDVyYjBWWldraFBkR3RoWTB4bGNrdzFNbVZaVTNCV2FtWlljMEV3WjBKTmNVRnViVXhIZDFoMGVtdFdTM0Z4ZWxWdFlscG9RM1k0YlRnMVlncG1kVWxTQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIiwic2lnIjoiVFVWVlEwbEdZeTlDZVV4b1EydFNNbGwwUVUxaWJVcHdNakF5V20xYU5GaFdSMWhHUzJrM2NpdHhOMnh6VGtSUVFXbEZRUzlLZDFneVVHbHNRMHgyYTNGRk9VNUtUVVpMVG00eVF6SnFPR05JTDNwNVJtaFJOalYzY21reVNGazkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiNjZiM2RjZmNhNjU5ZTVkNTA1NjAyYzNjOWFmOGZkMmJmNmE0YWZhY2FjMzNiMTc1ZTFkN2UwZWNhYjEwZjg5MCJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjhhNzVmNmM4ZGM0ZDlmNDA3Mjg1YmI0OWQzZTAxOWE1ZTY2NmY0MzQ5OWMyNzA3ZDkyODlhYTI3YzNjMmE2N2UifX19fQ==" } ], "timestampVerificationData": { "rfc3161Timestamps": [] }, "x509CertificateChain": { "certificates": [ { "rawBytes": "MIICnzCCAiagAwIBAgIUBnmZRtdkOtfO/Lyg435CR+VIi+AwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjIxMTA5MjMwMDM1WhcNMjIxMTA5MjMxMDM1WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeUE6Ox/414K0dBx3c3W+tQ7NTSlIVV24YSbBl6VyuIV/i15KClSjUo2qQuKSU4EzmHf2+EMj/YHYHeBADkDxj6OCAUUwggFBMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU/VGCUHVX5bXhet21itbj5WvhZqAwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0RAQH/BBUwE4ERYnJpYW5AZGVoYW1lci5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGKBgorBgEEAdZ5AgQCBHwEegB4AHYA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGEXp+1XQAABAMARzBFAiEAkdI694/8B2rOdiyYPlEncfRwkubodOZ4jlm4g5285LkCIHRpmGh4p5Oc+Vk+AKHMdt0VtD2/+caHWye1VqD4rGcCMAoGCCqGSM49BAMDA2cAMGQCMAyK8tQ5mPCCiLjiIsVsIZPYvhvDR3DFli5AQzjSCzcES/h5fB3GpeDdBOu9tukaowIwNkoEYZHOtkacLerL52eYSpVjfXsA0gBMqAnmLGwXtzkVKqqzUmbZhCv8m85bfuIR" }, { "rawBytes": "MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=" }, { "rawBytes": "MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ" } ] } }, "dsseEnvelope": { "payload": "ewogICJfdHlwZSI6ICJodHRwczovL2luLXRvdG8uaW8vU3RhdGVtZW50L3YwLjEiLAogICJzdWJqZWN0IjogWwogICAgewogICAgICAibmFtZSI6ICJzbHNhLXByb3ZlbmFuY2UtMC4wLjcudGd6IiwKICAgICAgImRpZ2VzdCI6IHsKICAgICAgICAic2hhNTEyIjogImJiZmQzNzJmYzliZWViNzc3ZmUzNWQwNWJiMTBjMGM3MGE5NzMzZDM2NmE3NWZlZDdkZDE4ODY2YzczOTFkZTNlZTdlODhiYTc0ZGQ0N2FiZjNlMTVjODQ1ZTU0N2ZjZjBlNWMzZGE4MDg1NGM3NTE1NTQyMjRkM2E2ZDRlNTVmIgogICAgICB9CiAgICB9CiAgXSwKICAicHJlZGljYXRlVHlwZSI6ICJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsCiAgInByZWRpY2F0ZSI6IHsKICAgICJidWlsZFR5cGUiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9zbHNhLXByb3ZlbmFuY2UvZ2hhQHYwIiwKICAgICJidWlsZGVyIjogewogICAgICAiaWQiOiAiaHR0cHM6Ly9naXRodWIuY29tL25wbS9zbHNhLXByb3ZlbmFuY2VAMC4wLjEiCiAgICB9LAogICAgImludm9jYXRpb24iOiB7CiAgICAgICJjb25maWdTb3VyY2UiOiB7CiAgICAgICAgInVyaSI6ICJnaXQraHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9zbHNhLXByb3ZlbmFuY2VAcmVmcy9oZWFkcy9kZW1vIiwKICAgICAgICAiZGlnZXN0IjogewogICAgICAgICAgInNoYTEiOiAiMjljZmYzZGQ2NWY3ODBjMzYwMWJkNDU3YWNiNmZlNGU1OTMxYzgyNSIKICAgICAgICB9LAogICAgICAgICJlbnRyeVBvaW50IjogImRlbW8iCiAgICAgIH0sCiAgICAgICJwYXJhbWV0ZXJzIjoge30sCiAgICAgICJlbnZpcm9ubWVudCI6IHsKICAgICAgICAiR0lUSFVCX0VWRU5UX05BTUUiOiAicHVzaCIsCiAgICAgICAgIkdJVEhVQl9KT0IiOiAicnVuLXByb3ZlbmFuY2UtZGVtbyIsCiAgICAgICAgIkdJVEhVQl9SRUYiOiAicmVmcy9oZWFkcy9kZW1vIiwKICAgICAgICAiR0lUSFVCX1JFRl9UWVBFIjogImJyYW5jaCIsCiAgICAgICAgIkdJVEhVQl9SRVBPU0lUT1JZIjogImdpdGh1Yi9zbHNhLXByb3ZlbmFuY2UiLAogICAgICAgICJHSVRIVUJfUkVQT1NJVE9SWV9PV05FUiI6ICJnaXRodWIiLAogICAgICAgICJHSVRIVUJfUlVOX0FUVEVNUFQiOiAiNCIsCiAgICAgICAgIkdJVEhVQl9SVU5fSUQiOiAiMzAyNDA5MTU0NiIsCiAgICAgICAgIkdJVEhVQl9SVU5fTlVNQkVSIjogIjE3IiwKICAgICAgICAiR0lUSFVCX1NIQSI6ICIyOWNmZjNkZDY1Zjc4MGMzNjAxYmQ0NTdhY2I2ZmU0ZTU5MzFjODI1IiwKICAgICAgICAiR0lUSFVCX1dPUktGTE9XIjogImRlbW8iLAogICAgICAgICJJTUFHRV9PUyI6ICJ1YnVudHUyMCIsCiAgICAgICAgIklNQUdFX1ZFUlNJT04iOiAiMjAyMjA5MDUuMSIsCiAgICAgICAgIlJVTk5FUl9BUkNIIjogIlg2NCIsCiAgICAgICAgIlJVTk5FUl9OQU1FIjogIkdpdEh1YiBBY3Rpb25zIDUwIiwKICAgICAgICAiUlVOTkVSX09TIjogIkxpbnV4IgogICAgICB9CiAgICB9LAogICAgIm1ldGFkYXRhIjogewogICAgICAiYnVpbGRJbnZvY2F0aW9uSWQiOiAiMzAyNDA5MTU0Ni00IiwKICAgICAgImNvbXBsZXRlbmVzcyI6IHsKICAgICAgICAicGFyYW1ldGVycyI6IGZhbHNlLAogICAgICAgICJlbnZpcm9ubWVudCI6IGZhbHNlLAogICAgICAgICJtYXRlcmlhbHMiOiBmYWxzZQogICAgICB9LAogICAgICAicmVwcm9kdWNpYmxlIjogZmFsc2UKICAgIH0sCiAgICAibWF0ZXJpYWxzIjogWwogICAgICB7CiAgICAgICAgInVyaSI6ICJnaXQraHR0cHM6Ly9naXRodWIuY29tL2dpdGh1Yi9zbHNhLXByb3ZlbmFuY2UiLAogICAgICAgICJkaWdlc3QiOiB7CiAgICAgICAgICAic2hhMSI6ICIyOWNmZjNkZDY1Zjc4MGMzNjAxYmQ0NTdhY2I2ZmU0ZTU5MzFjODI1IgogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQo=", "payloadType": "application/vnd.in-toto+json", "signatures": [ { "sig": "MEUCIFc/ByLhCkR2YtAMbmJp202ZmZ4XVGXFKi7r+q7lsNDPAiEA/JwX2PilCLvkqE9NJMFKNn2C2j8cH/zyFhQ65wri2HY=", "keyid": "" } ] } } sigstore-go-0.7.1/pkg/testing/data/bundles/othername.sigstore.json000066400000000000000000000147251477477521700253350ustar00rootroot00000000000000{ "mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial": { "certificate": { "rawBytes": "MIIEtTCCAp2gAwIBAgIUQo007zs0OhGOK8/Acik+axa7ve0wDQYJKoZIhvcNAQELBQAwfjEMMAoGA1UEBhMDVVNBMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRYwFAYDVQQJEw01NDggTWFya2V0IFN0MQ4wDAYDVQQREwU1NzI3NDEZMBcGA1UEChMQTGludXggRm91bmRhdGlvbjAeFw0yNDA3MTIxOTA2MjhaFw0yNDA3MTIxOTE2MjhaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ2fasaLzAQ6NW1DeN47ahLQ+4B/yykTNrlPN1L4/Fd2n7+Khk2Np0sCOzn1q1J3A9ctTaLwhmaWx98VXVax9uNo4IBcjCCAW4wDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBQav7zimj6IhRI/bEru7UNoUd2MMDAfBgNVHSMEGDAWgBSPD5vlHaXVMRD4Ul0X+y/OAJEl7TAsBgNVHREBAf8EIjAgoB4GCisGAQQBg78wAQegEAwOZm9vIW9pZGMubG9jYWwwJAYKKwYBBAGDvzABAQQWaHR0cDovL29pZGMubG9jYWw6ODA4MDAmBgorBgEEAYO/MAEIBBgMFmh0dHA6Ly9vaWRjLmxvY2FsOjgwODAwgYoGCisGAQQB1nkCBAIEfAR6AHgAdgDesHDYHzkyPSGM4zeGpsPji0+Fkuo5K601DwRJUWQDXAAAAZCoVvGxAAAEAwBHMEUCIF8KATnGR/A0M00weGYISnKlMHu+/PQPLXu7yO0G2itfAiEA2k2BG9Hzdp2AcgverhnsegnXxjKNO5FNtnwW/jnOIo4wDQYJKoZIhvcNAQELBQADggIBAGODe/vPPzDxaroHlIm/2uGoAl7a/aWJZvjobg7a9QqSM43nFhprRF3C518jATPxmzr0xzmDMOcI6+aT1ezK6pBRK5U/vY+mLzYHxBg9CcBDd6A8mOl89Qn1x6awSXoq+3D950Eww3vHfEJUS5gAFfD0SE91Y9L6fN1u9VzfcB27sTHfnfCk78iQf+sA0KWaTFgekCTkWetP9839efcQo5xY5JkxHzCWxKDsZrZqH3goGHCqdIL93g06QLJIHqOH3ztMvfkYbLmVuTV2RiysdYVhD6sJRlEKyiXtaXwthqdbsgbiKD8gRmQRJir961PoxTKkSvHhdafVmVUYtkWO6wQ98PwmOY0Poj+3zWoOAsnzqr0jwFn8QVNdeWKlDmzXqdXn5aBoXBphlQy/j2u1TWsl8Hc7JL+HhmV3GhqRbhD31WxVAQqi0poK7ig3ZB+q36TXvesmLEWenICplXscUy2Lr39C5sBeiLwLse3aaXse95YHqJkYgP44cS33/mmTmy2C1Fc4Pu01akUhLx69/sgLHS/3G2+UqgG8nslz2N7l7SUXat4Djqec1XQvoWG/f7kUbn3+dt0N8vv4YHVqVyaW7QkXcP6hyjnT8chmjsqCSCy8KWsgxr0pqpLCrrumlSke1BJGL4EZm0hSDvrh0dhqTgros8GZsYq8AJBAAmqj" }, "tlogEntries": [ { "logIndex": "3", "logId": { "keyId": "9vs1fkgdlblPyMuWiLRAQbEg0hmDHE6UwC92VxyLS8g=" }, "kindVersion": { "kind": "hashedrekord", "version": "0.0.1" }, "integratedTime": "1720811189", "inclusionPromise": { "signedEntryTimestamp": "MEUCIQDlRe4vCqGTap9Bko4TN9scDU7E7ideUfC51cEwxJJVJwIgBhimuSEUEUTuJ8rISl9UyMZvZp2hi1m7SSDIZM/ZkAA=" }, "inclusionProof": { "logIndex": "3", "rootHash": "uZYUY33ENx3NVSOphL2yVZLM+fjGXvOvRoQ15T82jp8=", "treeSize": "4", "hashes": [ "7KJPHdqkyM0JutlXYl4X0P0KU4VrWQKzjU6khYDdypw=", "t2F/5pUpEDAGCLrNbBywFrpk6eTM03yRmqxCkwO8nd0=" ], "checkpoint": { "envelope": "rekor-00001-deployment-56bf7777c9-jds5x - 6364419738405537866\n4\nuZYUY33ENx3NVSOphL2yVZLM+fjGXvOvRoQ15T82jp8=\n\n— rekor-00001-deployment-56bf7777c9-jds5x 9vs1fjBFAiBU8kwsoJjjEntsK485B35Sa4xhVryfMnnsv+V3fjujFgIhAOe8Okg1uwIH0no5NG3YvR57Fq0rwdxTxLqrsj2Ox1aj\n" } }, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJiYzEwM2I0YTg0OTcxZWY2NDU5YjI5NGEyYjk4NTY4YTJiZmI3MmNkZWQwOWQ0YWNkMWUxNjM2NmE0MDFmOTViIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJQ2pKYmY1ZXZRRzBjZUN1SHEvZ1VWeWI4dFU5OHBaaVFudTcxYkRuT2drbUFpRUF0bzZLeTJYQjhPeitab1NQRzRQSjg3cnNUejFkR1h0V3V5LzU4OXZXZlB3PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVVjBWRU5EUVhBeVowRjNTVUpCWjBsVlVXOHdNRGQ2Y3pCUGFFZFBTemd2UVdOcGF5dGhlR0UzZG1Vd2QwUlJXVXBMYjFwSmFIWmpUa0ZSUlV3S1FsRkJkMlpxUlUxTlFXOUhRVEZWUlVKb1RVUldWazVDVFZKTmQwVlJXVVJXVVZGSlJYZHdSRmxYZUhCYWJUbDVZbTFzYUUxU1dYZEdRVmxFVmxGUlNBcEZkekZVV1ZjMFoxSnVTbWhpYlU1d1l6Sk9kazFTV1hkR1FWbEVWbEZSU2tWM01ERk9SR2RuVkZkR2VXRXlWakJKUms0d1RWRTBkMFJCV1VSV1VWRlNDa1YzVlRGT2Vra3pUa1JGV2sxQ1kwZEJNVlZGUTJoTlVWUkhiSFZrV0dkblVtMDVNV0p0VW1oa1IyeDJZbXBCWlVaM01IbE9SRUV6VFZSSmVFOVVRVElLVFdwb1lVWjNNSGxPUkVFelRWUkplRTlVUlRKTmFtaGhUVUZCZDFkVVFWUkNaMk54YUd0cVQxQlJTVUpDWjJkeGFHdHFUMUJSVFVKQ2QwNURRVUZSTWdwbVlYTmhUSHBCVVRaT1Z6RkVaVTQwTjJGb1RGRXJORUl2ZVhsclZFNXliRkJPTVV3MEwwWmtNbTQzSzB0b2F6Sk9jREJ6UTA5NmJqRnhNVW96UVRsakNuUlVZVXgzYUcxaFYzZzVPRlpZVm1GNE9YVk9ielJKUW1OcVEwTkJWelIzUkdkWlJGWlNNRkJCVVVndlFrRlJSRUZuWlVGTlFrMUhRVEZWWkVwUlVVMEtUVUZ2UjBORGMwZEJVVlZHUW5kTlJFMUNNRWRCTVZWa1JHZFJWMEpDVVdGMk4zcHBiV28yU1doU1NTOWlSWEoxTjFWT2IxVmtNazFOUkVGbVFtZE9WZ3BJVTAxRlIwUkJWMmRDVTFCRU5YWnNTR0ZZVmsxU1JEUlZiREJZSzNrdlQwRktSV3czVkVGelFtZE9Wa2hTUlVKQlpqaEZTV3BCWjI5Q05FZERhWE5IQ2tGUlVVSm5OemgzUVZGbFowVkJkMDlhYlRsMlNWYzVjRnBIVFhWaVJ6bHFXVmQzZDBwQldVdExkMWxDUWtGSFJIWjZRVUpCVVZGWFlVaFNNR05FYjNZS1RESTVjRnBIVFhWaVJ6bHFXVmQzTms5RVFUUk5SRUZ0UW1kdmNrSm5SVVZCV1U4dlRVRkZTVUpDWjAxR2JXZ3daRWhCTmt4NU9YWmhWMUpxVEcxNGRncFpNa1p6VDJwbmQwOUVRWGRuV1c5SFEybHpSMEZSVVVJeGJtdERRa0ZKUldaQlVqWkJTR2RCWkdkRVpYTklSRmxJZW10NVVGTkhUVFI2WlVkd2MxQnFDbWt3SzBacmRXODFTell3TVVSM1VrcFZWMUZFV0VGQlFVRmFRMjlXZGtkNFFVRkJSVUYzUWtoTlJWVkRTVVk0UzBGVWJrZFNMMEV3VFRBd2QyVkhXVWtLVTI1TGJFMUlkU3N2VUZGUVRGaDFOM2xQTUVjeWFYUm1RV2xGUVRKck1rSkhPVWg2WkhBeVFXTm5kbVZ5YUc1elpXZHVXSGhxUzA1UE5VWk9kRzUzVndvdmFtNVBTVzgwZDBSUldVcExiMXBKYUhaalRrRlJSVXhDVVVGRVoyZEpRa0ZIVDBSbEwzWlFVSHBFZUdGeWIwaHNTVzB2TW5WSGIwRnNOMkV2WVZkS0NscDJhbTlpWnpkaE9WRnhVMDAwTTI1R2FIQnlVa1l6UXpVeE9HcEJWRkI0YlhweU1IaDZiVVJOVDJOSk5pdGhWREZsZWtzMmNFSlNTelZWTDNaWksyMEtUSHBaU0hoQ1p6bERZMEpFWkRaQk9HMVBiRGc1VVc0eGVEWmhkMU5ZYjNFck0wUTVOVEJGZDNjemRraG1SVXBWVXpWblFVWm1SREJUUlRreFdUbE1OZ3BtVGpGMU9WWjZabU5DTWpkelZFaG1ibVpEYXpjNGFWRm1LM05CTUV0WFlWUkdaMlZyUTFSclYyVjBVRGs0TXpsbFptTlJielY0V1RWS2EzaElla05YQ25oTFJITmFjbHB4U0RObmIwZElRM0ZrU1V3NU0yY3dObEZNU2tsSWNVOUlNM3AwVFhabWExbGlURzFXZFZSV01sSnBlWE5rV1Zab1JEWnpTbEpzUlVzS2VXbFlkR0ZZZDNSb2NXUmljMmRpYVV0RU9HZFNiVkZTU21seU9UWXhVRzk0VkV0clUzWklhR1JoWmxadFZsVlpkR3RYVHpaM1VUazRVSGR0VDFrd1VBcHZhaXN6ZWxkdlQwRnpibnB4Y2pCcWQwWnVPRkZXVG1SbFYwdHNSRzE2V0hGa1dHNDFZVUp2V0VKd2FHeFJlUzlxTW5VeFZGZHpiRGhJWXpkS1RDdElDbWh0VmpOSGFIRlNZbWhFTXpGWGVGWkJVWEZwTUhCdlN6ZHBaek5hUWl0eE16WlVXSFpsYzIxTVJWZGxia2xEY0d4WWMyTlZlVEpNY2pNNVF6VnpRbVVLYVV4M1RITmxNMkZoV0hObE9UVlpTSEZLYTFsblVEUTBZMU16TXk5dGJWUnRlVEpETVVaak5GQjFNREZoYTFWb1RIZzJPUzl6WjB4SVV5OHpSeklyVlFweFowYzRibk5zZWpKT04ydzNVMVZZWVhRMFJHcHhaV014V0ZGMmIxZEhMMlkzYTFWaWJqTXJaSFF3VGpoMmRqUlpTRlp4Vm5saFZ6ZFJhMWhqVURab0NubHFibFE0WTJodGFuTnhRMU5EZVRoTFYzTm5lSEl3Y0hGd1RFTnljblZ0YkZOclpURkNTa2RNTkVWYWJUQm9VMFIyY21nd1pHaHhWR2R5YjNNNFIxb0tjMWx4T0VGS1FrRkJiWEZxQ2kwdExTMHRSVTVFSUVORlVsUkpSa2xEUVZSRkxTMHRMUzBLIn19fX0=" } ] }, "messageSignature": { "messageDigest": { "algorithm": "SHA2_256", "digest": "vBA7SoSXHvZFmylKK5hWiiv7cs3tCdSs0eFjZqQB+Vs=" }, "signature": "MEUCICjJbf5evQG0ceCuHq/gUVyb8tU98pZiQnu71bDnOgkmAiEAto6Ky2XB8Oz+ZoSPG4PJ87rsTz1dGXtWuy/589vWfPw=" } } sigstore-go-0.7.1/pkg/testing/data/bundles/sigstore.js@2.0.0-provenance.sigstore.json000066400000000000000000000250171477477521700304350ustar00rootroot00000000000000{ "mediaType": "application/vnd.dev.sigstore.bundle+json;version=0.1", "verificationMaterial": { "x509CertificateChain": { "certificates": [ { "rawBytes": "MIIGtzCCBjygAwIBAgIUfd/5FN88EX4bwp7c7Q5ZrOXgRw4wCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjMwODE4MTYwNTM1WhcNMjMwODE4MTYxNTM1WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2CZZ4gTXAq4i5mYEl36bdw+RUVA1IaC5uw6IsBwiyfE/DLsMnbPpb/0vwXEh0d1FDWeel5RZd19wT+I0eD8sLKOCBVswggVXMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUIHAeQbQZz9vBuCr+LkarZTn38CkwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wYwYDVR0RAQH/BFkwV4ZVaHR0cHM6Ly9naXRodWIuY29tL3NpZ3N0b3JlL3NpZ3N0b3JlLWpzLy5naXRodWIvd29ya2Zsb3dzL3JlbGVhc2UueW1sQHJlZnMvaGVhZHMvbWFpbjA5BgorBgEEAYO/MAEBBCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMBIGCisGAQQBg78wAQIEBHB1c2gwNgYKKwYBBAGDvzABAwQoZjBiNDlhMDRlNWE2MjI1MGUwZjYwZmIxMjgwMDRhNzMxMTBmZTMxMTAVBgorBgEEAYO/MAEEBAdSZWxlYXNlMCIGCisGAQQBg78wAQUEFHNpZ3N0b3JlL3NpZ3N0b3JlLWpzMB0GCisGAQQBg78wAQYED3JlZnMvaGVhZHMvbWFpbjA7BgorBgEEAYO/MAEIBC0MK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wZQYKKwYBBAGDvzABCQRXDFVodHRwczovL2dpdGh1Yi5jb20vc2lnc3RvcmUvc2lnc3RvcmUtanMvLmdpdGh1Yi93b3JrZmxvd3MvcmVsZWFzZS55bWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wAQoEKgwoZjBiNDlhMDRlNWE2MjI1MGUwZjYwZmIxMjgwMDRhNzMxMTBmZTMxMTAdBgorBgEEAYO/MAELBA8MDWdpdGh1Yi1ob3N0ZWQwNwYKKwYBBAGDvzABDAQpDCdodHRwczovL2dpdGh1Yi5jb20vc2lnc3RvcmUvc2lnc3RvcmUtanMwOAYKKwYBBAGDvzABDQQqDChmMGI0OWEwNGU1YTYyMjUwZTBmNjBmYjEyODAwNGE3MzExMGZlMzExMB8GCisGAQQBg78wAQ4EEQwPcmVmcy9oZWFkcy9tYWluMBkGCisGAQQBg78wAQ8ECwwJNDk1NTc0NTU1MCsGCisGAQQBg78wARAEHQwbaHR0cHM6Ly9naXRodWIuY29tL3NpZ3N0b3JlMBgGCisGAQQBg78wAREECgwINzEwOTYzNTMwZQYKKwYBBAGDvzABEgRXDFVodHRwczovL2dpdGh1Yi5jb20vc2lnc3RvcmUvc2lnc3RvcmUtanMvLmdpdGh1Yi93b3JrZmxvd3MvcmVsZWFzZS55bWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wARMEKgwoZjBiNDlhMDRlNWE2MjI1MGUwZjYwZmIxMjgwMDRhNzMxMTBmZTMxMTAUBgorBgEEAYO/MAEUBAYMBHB1c2gwWgYKKwYBBAGDvzABFQRMDEpodHRwczovL2dpdGh1Yi5jb20vc2lnc3RvcmUvc2lnc3RvcmUtanMvYWN0aW9ucy9ydW5zLzU5MDQ2OTY3NjQvYXR0ZW1wdHMvMTAWBgorBgEEAYO/MAEWBAgMBnB1YmxpYzCBiwYKKwYBBAHWeQIEAgR9BHsAeQB3AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABigllGRAAAAQDAEgwRgIhAI+83BJd9c8hMU3oN33BSGow7UM4bs9jBGjoPZKu1SJSAiEAocFiN6CQF8tl+Ys1A39ctFFxOFn2Cr5NaO89QzbGVNUwCgYIKoZIzj0EAwMDaQAwZgIxAMCitzMG8PVXCibkqAYHOEcirlSuNdqLOGSxjvQvZq+n/LQDAXPGovz//vUH3HUZLAIxAJ8PpZWpESht+wC/n1+2TEGBB7aEIAJbcFYJ2AqFQIIjjsTcBLmNJT3EDAgtJCHFHA==" } ] }, "tlogEntries": [ { "logIndex": "31821305", "logId": { "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" }, "kindVersion": { "kind": "intoto", "version": "0.0.2" }, "integratedTime": "1692374735", "inclusionPromise": { "signedEntryTimestamp": "MEQCIBIG9TnhANgIZKrx20e1YQ0V7rnVs4/cKTf9tn3Y+NVIAiB8A0UwYu+Mc+E9pcP9ju7QOQYvLk8NajSeLp6sPLB1aA==" }, "inclusionProof": { "logIndex": "27657874", "rootHash": "v+7gOn1wovHHKBEVizJ5FFgTKUBCN9UxLo5KQ1Jz8cw=", "treeSize": "27657875", "hashes": [ "/pZbqoFwAGIZaonQ2KdQj3HSGP7/4yfdZBUxKadw9Z8=", "xZNrgfzUc8Ys5AKdeIpQ91hqM3mgCVdekTXsrM3GeBk=", "0vtqRSUOxFOmLkErow/DJ4p9SYw2PsjCgIRfKa7/twg=", "KXsEVwvzXH3v7vszv53J+jiAoKq1S9NCESUsKPStlUE=", "NTFwGNVKjiF6zpAaoug3Zdn4bcdMPFje53W1Nq5UgEI=", "aOgwCE1YnPdqr2RqEQElhpXvw1/6v+l9KuwI8pDg/j8=", "ZW26eQRJVw4L+5bsecao28mT5P+mmfOQkz1yVnnLHOY=", "uLuBRins5nkqq2rqd17R27pQTUF+xetttC6MsmlUzd0=", "jRUq4D8O+FI47Wbw96s7yHCu4qzWUxpIVfxQEeprDmc=", "rXEsmEJN4PEoTU8US4qVtdIsGB1MCiRlGOepoiC99kM=" ], "checkpoint": { "envelope": "rekor.sigstore.dev - 2605736670972794746\n27657875\nv+7gOn1wovHHKBEVizJ5FFgTKUBCN9UxLo5KQ1Jz8cw=\nTimestamp: 1692374735595899989\n\n— rekor.sigstore.dev wNI9ajBEAiAzHmfHSCMNTSzP9h0Pzzdg95z3uaFP2n1992qoazwr5AIgPdgJIrzOe2CRYLLZTjMWFe9pBIg0r2hAevmsWrnXSyk=\n" } }, "canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjIiLCJraW5kIjoiaW50b3RvIiwic3BlYyI6eyJjb250ZW50Ijp7ImVudmVsb3BlIjp7InBheWxvYWRUeXBlIjoiYXBwbGljYXRpb24vdm5kLmluLXRvdG8ranNvbiIsInNpZ25hdHVyZXMiOlt7InB1YmxpY0tleSI6IkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVWQwZWtORFFtcDVaMEYzU1VKQlowbFZabVF2TlVaT09EaEZXRFJpZDNBM1l6ZFJOVnB5VDFoblVuYzBkME5uV1VsTGIxcEplbW93UlVGM1RYY0tUbnBGVmsxQ1RVZEJNVlZGUTJoTlRXTXliRzVqTTFKMlkyMVZkVnBIVmpKTlVqUjNTRUZaUkZaUlVVUkZlRlo2WVZka2VtUkhPWGxhVXpGd1ltNVNiQXBqYlRGc1drZHNhR1JIVlhkSWFHTk9UV3BOZDA5RVJUUk5WRmwzVGxSTk1WZG9ZMDVOYWsxM1QwUkZORTFVV1hoT1ZFMHhWMnBCUVUxR2EzZEZkMWxJQ2t0dldrbDZhakJEUVZGWlNVdHZXa2w2YWpCRVFWRmpSRkZuUVVVeVExcGFOR2RVV0VGeE5HazFiVmxGYkRNMlltUjNLMUpWVmtFeFNXRkROWFYzTmtrS2MwSjNhWGxtUlM5RVRITk5ibUpRY0dJdk1IWjNXRVZvTUdReFJrUlhaV1ZzTlZKYVpERTVkMVFyU1RCbFJEaHpURXRQUTBKV2MzZG5aMVpZVFVFMFJ3cEJNVlZrUkhkRlFpOTNVVVZCZDBsSVowUkJWRUpuVGxaSVUxVkZSRVJCUzBKblozSkNaMFZHUWxGalJFRjZRV1JDWjA1V1NGRTBSVVpuVVZWSlNFRmxDbEZpVVZwNk9YWkNkVU55SzB4cllYSmFWRzR6T0VOcmQwaDNXVVJXVWpCcVFrSm5kMFp2UVZVek9WQndlakZaYTBWYVlqVnhUbXB3UzBaWGFYaHBORmtLV2tRNGQxbDNXVVJXVWpCU1FWRklMMEpHYTNkV05GcFdZVWhTTUdOSVRUWk1lVGx1WVZoU2IyUlhTWFZaTWpsMFRETk9jRm96VGpCaU0wcHNURE5PY0FwYU0wNHdZak5LYkV4WGNIcE1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU0wcHNZa2RXYUdNeVZYVmxWekZ6VVVoS2JGcHVUWFpoUjFab0NscElUWFppVjBad1ltcEJOVUpuYjNKQ1owVkZRVmxQTDAxQlJVSkNRM1J2WkVoU2QyTjZiM1pNTTFKMllUSldkVXh0Um1wa1IyeDJZbTVOZFZveWJEQUtZVWhXYVdSWVRteGpiVTUyWW01U2JHSnVVWFZaTWpsMFRVSkpSME5wYzBkQlVWRkNaemM0ZDBGUlNVVkNTRUl4WXpKbmQwNW5XVXRMZDFsQ1FrRkhSQXAyZWtGQ1FYZFJiMXBxUW1sT1JHeG9UVVJTYkU1WFJUSk5ha2t4VFVkVmQxcHFXWGRhYlVsNFRXcG5kMDFFVW1oT2VrMTRUVlJDYlZwVVRYaE5WRUZXQ2tKbmIzSkNaMFZGUVZsUEwwMUJSVVZDUVdSVFdsZDRiRmxZVG14TlEwbEhRMmx6UjBGUlVVSm5OemgzUVZGVlJVWklUbkJhTTA0d1lqTktiRXd6VG5BS1dqTk9NR0l6U214TVYzQjZUVUl3UjBOcGMwZEJVVkZDWnpjNGQwRlJXVVZFTTBwc1dtNU5kbUZIVm1oYVNFMTJZbGRHY0dKcVFUZENaMjl5UW1kRlJRcEJXVTh2VFVGRlNVSkRNRTFMTW1nd1pFaENlazlwT0haa1J6bHlXbGMwZFZsWFRqQmhWemwxWTNrMWJtRllVbTlrVjBveFl6SldlVmt5T1hWa1IxWjFDbVJETldwaU1qQjNXbEZaUzB0M1dVSkNRVWRFZG5wQlFrTlJVbGhFUmxadlpFaFNkMk42YjNaTU1tUndaRWRvTVZscE5XcGlNakIyWXpKc2JtTXpVbllLWTIxVmRtTXliRzVqTTFKMlkyMVZkR0Z1VFhaTWJXUndaRWRvTVZscE9UTmlNMHB5V20xNGRtUXpUWFpqYlZaeldsZEdlbHBUTlRWaVYzaEJZMjFXYlFwamVUbHZXbGRHYTJONU9YUlpWMngxVFVSblIwTnBjMGRCVVZGQ1p6YzRkMEZSYjBWTFozZHZXbXBDYVU1RWJHaE5SRkpzVGxkRk1rMXFTVEZOUjFWM0NscHFXWGRhYlVsNFRXcG5kMDFFVW1oT2VrMTRUVlJDYlZwVVRYaE5WRUZrUW1kdmNrSm5SVVZCV1U4dlRVRkZURUpCT0UxRVYyUndaRWRvTVZscE1XOEtZak5PTUZwWFVYZE9kMWxMUzNkWlFrSkJSMFIyZWtGQ1JFRlJjRVJEWkc5a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFpqTW14dVl6TlNkZ3BqYlZWMll6SnNibU16VW5aamJWVjBZVzVOZDA5QldVdExkMWxDUWtGSFJIWjZRVUpFVVZGeFJFTm9iVTFIU1RCUFYwVjNUa2RWTVZsVVdYbE5hbFYzQ2xwVVFtMU9ha0p0V1dwRmVVOUVRWGRPUjBVelRYcEZlRTFIV214TmVrVjRUVUk0UjBOcGMwZEJVVkZDWnpjNGQwRlJORVZGVVhkUVkyMVdiV041T1c4S1dsZEdhMk41T1hSWlYyeDFUVUpyUjBOcGMwZEJVVkZDWnpjNGQwRlJPRVZEZDNkS1RrUnJNVTVVWXpCT1ZGVXhUVU56UjBOcGMwZEJVVkZDWnpjNGR3cEJVa0ZGU0ZGM1ltRklVakJqU0UwMlRIazVibUZZVW05a1YwbDFXVEk1ZEV3elRuQmFNMDR3WWpOS2JFMUNaMGREYVhOSFFWRlJRbWMzT0hkQlVrVkZDa05uZDBsT2VrVjNUMVJaZWs1VVRYZGFVVmxMUzNkWlFrSkJSMFIyZWtGQ1JXZFNXRVJHVm05a1NGSjNZM3B2ZGt3eVpIQmtSMmd4V1drMWFtSXlNSFlLWXpKc2JtTXpVblpqYlZWMll6SnNibU16VW5aamJWVjBZVzVOZGt4dFpIQmtSMmd4V1drNU0ySXpTbkphYlhoMlpETk5kbU50Vm5OYVYwWjZXbE0xTlFwaVYzaEJZMjFXYldONU9XOWFWMFpyWTNrNWRGbFhiSFZOUkdkSFEybHpSMEZSVVVKbk56aDNRVkpOUlV0bmQyOWFha0pwVGtSc2FFMUVVbXhPVjBVeUNrMXFTVEZOUjFWM1dtcFpkMXB0U1hoTmFtZDNUVVJTYUU1NlRYaE5WRUp0V2xSTmVFMVVRVlZDWjI5eVFtZEZSVUZaVHk5TlFVVlZRa0ZaVFVKSVFqRUtZekpuZDFkbldVdExkMWxDUWtGSFJIWjZRVUpHVVZKTlJFVndiMlJJVW5kamVtOTJUREprY0dSSGFERlphVFZxWWpJd2RtTXliRzVqTTFKMlkyMVZkZ3BqTW14dVl6TlNkbU50VlhSaGJrMTJXVmRPTUdGWE9YVmplVGw1WkZjMWVreDZWVFZOUkZFeVQxUlpNMDVxVVhaWldGSXdXbGN4ZDJSSVRYWk5WRUZYQ2tKbmIzSkNaMFZGUVZsUEwwMUJSVmRDUVdkTlFtNUNNVmx0ZUhCWmVrTkNhWGRaUzB0M1dVSkNRVWhYWlZGSlJVRm5VamxDU0hOQlpWRkNNMEZPTURrS1RVZHlSM2g0UlhsWmVHdGxTRXBzYms1M1MybFRiRFkwTTJwNWRDODBaVXRqYjBGMlMyVTJUMEZCUVVKcFoyeHNSMUpCUVVGQlVVUkJSV2QzVW1kSmFBcEJTU3M0TTBKS1pEbGpPR2hOVlROdlRqTXpRbE5IYjNjM1ZVMDBZbk01YWtKSGFtOVFXa3QxTVZOS1UwRnBSVUZ2WTBacFRqWkRVVVk0ZEd3cldYTXhDa0V6T1dOMFJrWjRUMFp1TWtOeU5VNWhUemc1VVhwaVIxWk9WWGREWjFsSlMyOWFTWHBxTUVWQmQwMUVZVkZCZDFwblNYaEJUVU5wZEhwTlJ6aFFWbGdLUTJsaWEzRkJXVWhQUldOcGNteFRkVTVrY1V4UFIxTjRhblpSZGxweEsyNHZURkZFUVZoUVIyOTJlaTh2ZGxWSU0waFZXa3hCU1hoQlNqaFFjRnBYY0FwRlUyaDBLM2RETDI0eEt6SlVSVWRDUWpkaFJVbEJTbUpqUmxsS01rRnhSbEZKU1dwcWMxUmpRa3h0VGtwVU0wVkVRV2QwU2tOSVJraEJQVDBLTFMwdExTMUZUa1FnUTBWU1ZFbEdTVU5CVkVVdExTMHRMUT09Iiwic2lnIjoiVFVWUlEwbEdWM0pRY0ROcE5UaHpibFZKYXpsSU5UbG9lbmxZU0hwUVJuTXpLMGRhUkhBclEzcGtUa3RZWTBKRlFXbENVVkZxZGxWaFZFZDRTMmxQUjJ4SE1VZFJlRXRzT1RGWldrVTRhMFZZTW5kaFVYQnpNRTVPVTFORlp6MDkifV19LCJoYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZTBjZjg1NDI4MzQ0ZDRmZjE3N2E4ZWRjNDMxZTNmOTJiNDQ4Nzc1YTJiMDBiN2ZjZDdhN2FiM2QyZjk4ZWNhYyJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjA3NDJhNmZlMmE5MWViN2UyYzI3NDE0NGY2MTIzZjU5YTc5OTczMmM5ZDliZmQzYjdmZWFjNDg3ZjcyZWI0NGMifX19fQ==" } ], "timestampVerificationData": null }, "dsseEnvelope": { "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoicGtnOm5wbS9zaWdzdG9yZUAyLjAuMCIsImRpZ2VzdCI6eyJzaGE1MTIiOiI0NmQ0ZTJmNzRjNDg3NzMxNjY0MDAwMGE2ZmRmOGE4YjU5ZjFlMDg0NzY2Nzk3M2U5ODU5Zjc3NGRkMzFiOGYxZTA5Mzc4MTNiNzc3ZmI2NmEyYWM2N2Q1MDU0MGZlMzQ2NDA5NjZlZWU5ZmMyY2NjYTM4NzA4MmI0Yzg1Y2QzYyJ9fV0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjEiLCJwcmVkaWNhdGUiOnsiYnVpbGREZWZpbml0aW9uIjp7ImJ1aWxkVHlwZSI6Imh0dHBzOi8vc2xzYS1mcmFtZXdvcmsuZ2l0aHViLmlvL2dpdGh1Yi1hY3Rpb25zLWJ1aWxkdHlwZXMvd29ya2Zsb3cvdjEiLCJleHRlcm5hbFBhcmFtZXRlcnMiOnsid29ya2Zsb3ciOnsicmVmIjoicmVmcy9oZWFkcy9tYWluIiwicmVwb3NpdG9yeSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9zaWdzdG9yZS9zaWdzdG9yZS1qcyIsInBhdGgiOiIuZ2l0aHViL3dvcmtmbG93cy9yZWxlYXNlLnltbCJ9fSwiaW50ZXJuYWxQYXJhbWV0ZXJzIjp7ImdpdGh1YiI6eyJldmVudF9uYW1lIjoicHVzaCIsInJlcG9zaXRvcnlfaWQiOiI0OTU1NzQ1NTUiLCJyZXBvc2l0b3J5X293bmVyX2lkIjoiNzEwOTYzNTMifX0sInJlc29sdmVkRGVwZW5kZW5jaWVzIjpbeyJ1cmkiOiJnaXQraHR0cHM6Ly9naXRodWIuY29tL3NpZ3N0b3JlL3NpZ3N0b3JlLWpzQHJlZnMvaGVhZHMvbWFpbiIsImRpZ2VzdCI6eyJnaXRDb21taXQiOiJmMGI0OWEwNGU1YTYyMjUwZTBmNjBmYjEyODAwNGE3MzExMGZlMzExIn19XX0sInJ1bkRldGFpbHMiOnsiYnVpbGRlciI6eyJpZCI6Imh0dHBzOi8vZ2l0aHViLmNvbS9hY3Rpb25zL3J1bm5lci9naXRodWItaG9zdGVkIn0sIm1ldGFkYXRhIjp7Imludm9jYXRpb25JZCI6Imh0dHBzOi8vZ2l0aHViLmNvbS9zaWdzdG9yZS9zaWdzdG9yZS1qcy9hY3Rpb25zL3J1bnMvNTkwNDY5Njc2NC9hdHRlbXB0cy8xIn19fX0=", "payloadType": "application/vnd.in-toto+json", "signatures": [ { "sig": "MEQCIFWrPp3i58snUIk9H59hzyXHzPFs3+GZDp+CzdNKXcBEAiBQQjvUaTGxKiOGlG1GQxKl91YZE8kEX2waQps0NNSSEg==", "keyid": "" } ] } } sigstore-go-0.7.1/pkg/testing/data/data.go000066400000000000000000000027711477477521700204240ustar00rootroot00000000000000// 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 data import ( "embed" "path" "testing" "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/root" "github.com/stretchr/testify/assert" ) //go:embed bundles/*.json trusted-roots/*.json var embedded embed.FS // Bundle reads a file from the embedded file system and returns a *bundle.Bundle func Bundle(t *testing.T, filename string) (b *bundle.Bundle) { b = &bundle.Bundle{} data, err := embedded.ReadFile(path.Join("bundles", filename)) assert.NoError(t, err) err = b.UnmarshalJSON(data) assert.NoError(t, err) return b } // TrustedRoot reads a file from the embedded file system and returns a *root.TrustedRoot func TrustedRoot(t *testing.T, filename string) *root.TrustedRoot { data, err := embedded.ReadFile(path.Join("trusted-roots", filename)) assert.NoError(t, err) trustedRoot, _ := root.NewTrustedRootFromJSON(data) assert.NoError(t, err) return trustedRoot } sigstore-go-0.7.1/pkg/testing/data/trusted-roots/000077500000000000000000000000001477477521700220135ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/testing/data/trusted-roots/public-good.json000066400000000000000000000155461477477521700251250ustar00rootroot00000000000000{ "mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1", "tlogs": [ { "baseUrl": "https://rekor.sigstore.dev", "hashAlgorithm": "SHA2_256", "publicKey": { "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2G2Y+2tabdTV5BcGiBIx0a9fAFwrkBbmLSGtks4L3qX6yYY0zufBnhC8Ur/iy55GhWP/9A/bY2LhC30M9+RYtw==", "keyDetails": "PKIX_ECDSA_P256_SHA_256", "validFor": { "start": "2021-01-12T11:53:27.000Z" } }, "logId": { "keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0=" } } ], "certificateAuthorities": [ { "subject": { "organization": "sigstore.dev", "commonName": "sigstore" }, "uri": "https://fulcio.sigstore.dev", "certChain": { "certificates": [ { "rawBytes": "MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAqMRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIxMDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSyA7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0JcastaRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6NmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJxVe/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uupHr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==" } ] }, "validFor": { "start": "2021-03-07T03:20:29.000Z", "end": "2022-12-31T23:59:59.999Z" } }, { "subject": { "organization": "sigstore.dev", "commonName": "sigstore" }, "uri": "https://fulcio.sigstore.dev", "certChain": { "certificates": [ { "rawBytes": "MIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV77LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjpKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZIzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJRnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsPmygUY7Ii2zbdCdliiow=" }, { "rawBytes": "MIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMwKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0yMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7XeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxexX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92jYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRYwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCMWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9TNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ" } ] }, "validFor": { "start": "2022-04-13T20:06:15.000Z" } } ], "ctlogs": [ { "baseUrl": "https://ctfe.sigstore.dev/test", "hashAlgorithm": "SHA2_256", "publicKey": { "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbfwR+RJudXscgRBRpKX1XFDy3PyudDxz/SfnRi1fT8ekpfBd2O1uoz7jr3Z8nKzxA69EUQ+eFCFI3zeubPWU7w==", "keyDetails": "PKIX_ECDSA_P256_SHA_256", "validFor": { "start": "2021-03-14T00:00:00.000Z", "end": "2022-10-31T23:59:59.999Z" } }, "logId": { "keyId": "CGCS8ChS/2hF0dFrJ4ScRWcYrBY9wzjSbea8IgY2b3I=" } }, { "baseUrl": "https://ctfe.sigstore.dev/2022", "hashAlgorithm": "SHA2_256", "publicKey": { "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiPSlFi0CmFTfEjCUqF9HuCEcYXNKAaYalIJmBZ8yyezPjTqhxrKBpMnaocVtLJBI1eM3uXnQzQGAJdJ4gs9Fyw==", "keyDetails": "PKIX_ECDSA_P256_SHA_256", "validFor": { "start": "2022-10-20T00:00:00.000Z" } }, "logId": { "keyId": "3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4=" } } ], "timestampAuthorities": [ { "subject": { "organization": "GitHub, Inc.", "commonName": "Internal Services Root" }, "certChain": { "certificates": [ { "rawBytes": "MIIB3DCCAWKgAwIBAgIUchkNsH36Xa04b1LqIc+qr9DVecMwCgYIKoZIzj0EAwMwMjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRkwFwYDVQQDExBUU0EgaW50ZXJtZWRpYXRlMB4XDTIzMDQxNDAwMDAwMFoXDTI0MDQxMzAwMDAwMFowMjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRkwFwYDVQQDExBUU0EgVGltZXN0YW1waW5nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUD5ZNbSqYMd6r8qpOOEX9ibGnZT9GsuXOhr/f8U9FJugBGExKYp40OULS0erjZW7xV9xV52NnJf5OeDq4e5ZKqNWMFQwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUaW1RudOgVt0leqY0WKYbuPr47wAwCgYIKoZIzj0EAwMDaAAwZQIwbUH9HvD4ejCZJOWQnqAlkqURllvu9M8+VqLbiRK+zSfZCZwsiljRn8MQQRSkXEE5AjEAg+VxqtojfVfu8DhzzhCx9GKETbJHb19iV72mMKUbDAFmzZ6bQ8b54Zb8tidy5aWe" }, { "rawBytes": "MIICEDCCAZWgAwIBAgIUX8ZO5QXP7vN4dMQ5e9sU3nub8OgwCgYIKoZIzj0EAwMwODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MB4XDTIzMDQxNDAwMDAwMFoXDTI4MDQxMjAwMDAwMFowMjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMRkwFwYDVQQDExBUU0EgaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEvMLY/dTVbvIJYANAuszEwJnQE1llftynyMKIMhh48HmqbVr5ygybzsLRLVKbBWOdZ21aeJz+gZiytZetqcyF9WlER5NEMf6JV7ZNojQpxHq4RHGoGSceQv/qvTiZxEDKo2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaW1RudOgVt0leqY0WKYbuPr47wAwHwYDVR0jBBgwFoAU9NYYlobnAG4c0/qjxyH/lq/wz+QwCgYIKoZIzj0EAwMDaQAwZgIxAK1B185ygCrIYFlIs3GjswjnwSMG6LY8woLVdakKDZxVa8f8cqMs1DhcxJ0+09w95QIxAO+tBzZk7vjUJ9iJgD4R6ZWTxQWKqNm74jO99o+o9sv4FI/SZTZTFyMn0IJEHdNmyA==" }, { "rawBytes": "MIIB9DCCAXqgAwIBAgIUa/JAkdUjK4JUwsqtaiRJGWhqLSowCgYIKoZIzj0EAwMwODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MB4XDTIzMDQxNDAwMDAwMFoXDTMzMDQxMTAwMDAwMFowODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZJbnRlcm5hbCBTZXJ2aWNlcyBSb290MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEf9jFAXxz4kx68AHRMOkFBhflDcMTvzaXz4x/FCcXjJ/1qEKon/qPIGnaURskDtyNbNDOpeJTDDFqt48iMPrnzpx6IZwqemfUJN4xBEZfza+pYt/iyod+9tZr20RRWSv/o0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBAjAdBgNVHQ4EFgQU9NYYlobnAG4c0/qjxyH/lq/wz+QwCgYIKoZIzj0EAwMDaAAwZQIxALZLZ8BgRXzKxLMMN9VIlO+e4hrBnNBgF7tz7Hnrowv2NetZErIACKFymBlvWDvtMAIwZO+ki6ssQ1bsZo98O8mEAf2NZ7iiCgDDU0Vwjeco6zyeh0zBTs9/7gV6AHNQ53xD" } ] }, "validFor": { "start": "2023-04-14T00:00:00.000Z" } } ] } sigstore-go-0.7.1/pkg/testing/data/trusted-roots/scaffolding.json000066400000000000000000000066131477477521700251730ustar00rootroot00000000000000{ "mediaType": "application/vnd.dev.sigstore.trustedroot+json;version=0.1", "tlogs": [ { "baseUrl": "http://rekor.rekor-system.172.18.255.1.sslip.io", "hashAlgorithm": "SHA2_256", "publicKey": { "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnPyeVMLRWPJQpCHcUdG41k+oJiQEjX4uGSX7ujPH7Iv5zQD3VYiHhyQ/oMJvc1vx+2Zk2DBcBhN9IT0eZjB2RQ==", "keyDetails": "PKIX_ECDSA_P256_SHA_256", "validFor": { "start": "2024-07-12T18:35:53Z" } }, "logId": { "keyId": "9vs1fkgdlblPyMuWiLRAQbEg0hmDHE6UwC92VxyLS8g=" } } ], "certificateAuthorities": [ { "subject": { "organization": "Linux Foundation" }, "uri": "http://fulcio.fulcio-system.172.18.255.1.sslip.io", "certChain": { "certificates": [ { "rawBytes": "MIIFwzCCA6ugAwIBAgIIGOK4JTIvAnQwDQYJKoZIhvcNAQELBQAwfjEMMAoGA1UEBhMDVVNBMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRYwFAYDVQQJEw01NDggTWFya2V0IFN0MQ4wDAYDVQQREwU1NzI3NDEZMBcGA1UEChMQTGludXggRm91bmRhdGlvbjAeFw0yNDA3MTEyMjI4NDFaFw0yNTA3MTEyMjI4NDFaMH4xDDAKBgNVBAYTA1VTQTETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEWMBQGA1UECRMNNTQ4IE1hcmtldCBTdDEOMAwGA1UEERMFNTcyNzQxGTAXBgNVBAoTEExpbnV4IEZvdW5kYXRpb24wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCrq2z5byNpomZGJsrEloYzae0zU6bZK2x+9C16DdocsLavJNX2MaxQ28imb5YYp4z6M52SDPW4NZKCtJRSOp4Z+jK6194z6r08SCbU4JdU6qhBWhzb5PqDN8JYImnWAsUAg2MHu8DWDHsNVfyivxkqeeyTf/c4aAJX0YqVv8WnvEnI6rstV6CO3/Q7VqZrK3vfUH4rFuiIBwCO1TLnVh9RHARM43oDdeKAQLKh2p4PD6VoOVPNEw8uxuokG8qyJZOUVgUETovR8E3puTVn3iopea2BvMADZQA1u6MT4MCjY/Hqv+RdQ6W4c2eyey/ZZSoiQUZmkO2YTqtYPH2B+ucDmIOJ07MtraFeB1CXfRlPa5sv02N6NzZN/iD66GQ/fV2PiuMyJVmhnYJp0Yf3onVmmpxIEOkUDnWudUtMJHZuLy0rhu/hAid6l0KEGjXlBvXu7txZHw1AMerQbvn5VJdPgm4PT/5xK5f1PpPGxVZwGkjmBMZmj9+hRt0OHH59aK31vqGqPbQtIXguAlF89O1UaZv4JGnpdaJl4K3huXnahcI16+8s+Vu9sJ4dfZT/NlFV26a4aU7q+E7yH3n8+zmsk3+l06BWxz7R6SSp6Fx4yPB/3SBs2c5SJ5k6a+/3SssqVHWwgSZD6cXDt1ByYDMjkHFExV0oLDr0Q057l/ainQIDAQABo0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUjw+b5R2l1TEQ+FJdF/svzgCRJe0wDQYJKoZIhvcNAQELBQADggIBAECAX4HbC+MWJS5+D6aZmu7P85ZDzHMpIk5LJiAJwLUIOZwF4K0z9AOHE/nqg5+PnZGWWI3a9UheuzsZauerz/jaP8thBWjVDJCROJZpMMvALAjJfgIFJw3YLNPUup0EL4UohZ7iWoD6e/vfY64DKzCpdfGDRfcBCnWqBIYeSSPNqH+i0L059oR9kXv3jwR4os0CWk8TUMBYGeDADeE27QuZ4qafLkmOaqp//yWXwOoe4MZBxettZz/Nib5RRhCxRQ88hbs/zH3T5bBgp+DZ0anjy2iVhOj2x02mdD6Zcb32JgEJLQHCTAdGamcdulQDXC+YS9N2U0ap8J3tZCrEPQkdkeRzJ2EzQx38NIiY16BPlAqnnRpOZiXqee4O7bni4qdyVAYpkArSRNvKQbTyLHYLiQ+TEMs0SboajbQtC38I4ztZXr2ozM2b1MU0d3rBLsozmAhqT99od8wiBValo0EEi2mSxArRHy0puIOMs1i4kIz2yTbyeEI5pnkq/2uaX+RPmS2UB83SmbZ7Ex9eNe6QjnMhCv5fU0wcjtwwPp0GMMRulErGvnZ39PRMjEH79C8Nfhx9nZZoEN5VCG9qrM1KMlDLwNc09W5RJTYRQ7d41sC2hdMgwmxVJ08Ai3XMn7xiJ9JwnaypClc14XsQERoy2afgBUME9CL00G20nVYb" } ] }, "validFor": { "start": "2024-07-12T18:35:53Z" } } ], "ctlogs": [ { "baseUrl": "http://ctlog.ctlog-system.172.18.255.1.sslip.io", "hashAlgorithm": "SHA2_256", "publicKey": { "rawBytes": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJ7v1OnMWwYi4O5oaycBsWKom3McZBDzNqXsIOq9AXc3z2HOeWVbaDd1V/9c91WRFyAv77Ao9hS9D9MEboT7lZg==", "keyDetails": "PKIX_ECDSA_P256_SHA_256", "validFor": { "start": "2024-07-12T18:35:53Z" } }, "logId": { "keyId": "3rBw2B85Mj0hjOM3hqbD44tPhZLqOSutNQ8ESVFkA1w=" } } ] } sigstore-go-0.7.1/pkg/tlog/000077500000000000000000000000001477477521700155345ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/tlog/entry.go000066400000000000000000000212401477477521700172230ustar00rootroot00000000000000// 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 tlog import ( "bytes" "context" "crypto/ecdsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/json" "encoding/pem" "errors" "fmt" "time" "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer" "github.com/go-openapi/runtime" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/rekor/pkg/types" dsse_v001 "github.com/sigstore/rekor/pkg/types/dsse/v0.0.1" hashedrekord_v001 "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1" intoto_v002 "github.com/sigstore/rekor/pkg/types/intoto/v0.0.2" rekorVerify "github.com/sigstore/rekor/pkg/verify" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore-go/pkg/root" ) type Entry struct { kind string version string rekorEntry types.EntryImpl logEntryAnon models.LogEntryAnon signedEntryTimestamp []byte } type RekorPayload struct { Body interface{} `json:"body"` IntegratedTime int64 `json:"integratedTime"` LogIndex int64 `json:"logIndex"` LogID string `json:"logID"` //nolint:tagliatelle } var ErrNilValue = errors.New("validation error: nil value in transaction log entry") func NewEntry(body []byte, integratedTime int64, logIndex int64, logID []byte, signedEntryTimestamp []byte, inclusionProof *models.InclusionProof) (*Entry, error) { pe, err := models.UnmarshalProposedEntry(bytes.NewReader(body), runtime.JSONConsumer()) if err != nil { return nil, err } rekorEntry, err := types.UnmarshalEntry(pe) if err != nil { return nil, err } entry := &Entry{ rekorEntry: rekorEntry, logEntryAnon: models.LogEntryAnon{ Body: base64.StdEncoding.EncodeToString(body), IntegratedTime: swag.Int64(integratedTime), LogIndex: swag.Int64(logIndex), LogID: swag.String(string(logID)), }, kind: pe.Kind(), version: rekorEntry.APIVersion(), } if len(signedEntryTimestamp) > 0 { entry.signedEntryTimestamp = signedEntryTimestamp } if inclusionProof != nil { entry.logEntryAnon.Verification = &models.LogEntryAnonVerification{ InclusionProof: inclusionProof, } } return entry, nil } // ParseEntry decodes the entry bytes to a specific entry type (types.EntryImpl). func ParseEntry(protoEntry *v1.TransparencyLogEntry) (entry *Entry, err error) { if protoEntry == nil || protoEntry.CanonicalizedBody == nil || protoEntry.IntegratedTime == 0 || protoEntry.LogIndex == 0 || protoEntry.LogId == nil || protoEntry.LogId.KeyId == nil || protoEntry.KindVersion == nil { return nil, ErrNilValue } signedEntryTimestamp := []byte{} if protoEntry.InclusionPromise != nil && protoEntry.InclusionPromise.SignedEntryTimestamp != nil { signedEntryTimestamp = protoEntry.InclusionPromise.SignedEntryTimestamp } var inclusionProof *models.InclusionProof if protoEntry.InclusionProof != nil { var hashes []string for _, v := range protoEntry.InclusionProof.Hashes { hashes = append(hashes, hex.EncodeToString(v)) } rootHash := hex.EncodeToString(protoEntry.InclusionProof.RootHash) if protoEntry.InclusionProof.Checkpoint == nil { return nil, fmt.Errorf("inclusion proof missing required checkpoint") } if protoEntry.InclusionProof.Checkpoint.Envelope == "" { return nil, fmt.Errorf("inclusion proof checkpoint empty") } inclusionProof = &models.InclusionProof{ LogIndex: swag.Int64(protoEntry.InclusionProof.LogIndex), RootHash: &rootHash, TreeSize: swag.Int64(protoEntry.InclusionProof.TreeSize), Hashes: hashes, Checkpoint: swag.String(protoEntry.InclusionProof.Checkpoint.Envelope), } } entry, err = NewEntry(protoEntry.CanonicalizedBody, protoEntry.IntegratedTime, protoEntry.LogIndex, protoEntry.LogId.KeyId, signedEntryTimestamp, inclusionProof) if err != nil { return nil, err } if entry.kind != protoEntry.KindVersion.Kind || entry.version != protoEntry.KindVersion.Version { return nil, fmt.Errorf("kind and version mismatch: %s/%s != %s/%s", entry.kind, entry.version, protoEntry.KindVersion.Kind, protoEntry.KindVersion.Version) } return entry, nil } func ValidateEntry(entry *Entry) error { switch e := entry.rekorEntry.(type) { case *dsse_v001.V001Entry: err := e.DSSEObj.Validate(strfmt.Default) if err != nil { return err } case *hashedrekord_v001.V001Entry: err := e.HashedRekordObj.Validate(strfmt.Default) if err != nil { return err } case *intoto_v002.V002Entry: err := e.IntotoObj.Validate(strfmt.Default) if err != nil { return err } default: return fmt.Errorf("unsupported entry type: %T", e) } return nil } func (entry *Entry) IntegratedTime() time.Time { return time.Unix(*entry.logEntryAnon.IntegratedTime, 0) } func (entry *Entry) Signature() []byte { switch e := entry.rekorEntry.(type) { case *dsse_v001.V001Entry: sigBytes, err := base64.StdEncoding.DecodeString(*e.DSSEObj.Signatures[0].Signature) if err != nil { return []byte{} } return sigBytes case *hashedrekord_v001.V001Entry: return e.HashedRekordObj.Signature.Content case *intoto_v002.V002Entry: sigBytes, err := base64.StdEncoding.DecodeString(string(*e.IntotoObj.Content.Envelope.Signatures[0].Sig)) if err != nil { return []byte{} } return sigBytes } return []byte{} } func (entry *Entry) PublicKey() any { var pemString []byte switch e := entry.rekorEntry.(type) { case *dsse_v001.V001Entry: pemString = []byte(*e.DSSEObj.Signatures[0].Verifier) case *hashedrekord_v001.V001Entry: pemString = []byte(e.HashedRekordObj.Signature.PublicKey.Content) case *intoto_v002.V002Entry: pemString = []byte(*e.IntotoObj.Content.Envelope.Signatures[0].PublicKey) } certBlock, _ := pem.Decode(pemString) var pk any var err error pk, err = x509.ParseCertificate(certBlock.Bytes) if err != nil { pk, err = x509.ParsePKIXPublicKey(certBlock.Bytes) if err != nil { return nil } } return pk } func (entry *Entry) LogKeyID() string { return *entry.logEntryAnon.LogID } func (entry *Entry) LogIndex() int64 { return *entry.logEntryAnon.LogIndex } func (entry *Entry) Body() any { return entry.logEntryAnon.Body } func (entry *Entry) HasInclusionPromise() bool { return entry.signedEntryTimestamp != nil } func (entry *Entry) HasInclusionProof() bool { return entry.logEntryAnon.Verification != nil } func VerifyInclusion(entry *Entry, verifier signature.Verifier) error { err := rekorVerify.VerifyInclusion(context.TODO(), &entry.logEntryAnon) if err != nil { return err } err = rekorVerify.VerifyCheckpointSignature(&entry.logEntryAnon, verifier) if err != nil { return err } return nil } func VerifySET(entry *Entry, verifiers map[string]*root.TransparencyLog) error { rekorPayload := RekorPayload{ Body: entry.logEntryAnon.Body, IntegratedTime: *entry.logEntryAnon.IntegratedTime, LogIndex: *entry.logEntryAnon.LogIndex, LogID: hex.EncodeToString([]byte(*entry.logEntryAnon.LogID)), } verifier, ok := verifiers[hex.EncodeToString([]byte(*entry.logEntryAnon.LogID))] if !ok { return errors.New("rekor log public key not found for payload") } if verifier.ValidityPeriodStart.IsZero() { return errors.New("rekor validity period start time not set") } if (verifier.ValidityPeriodStart.After(entry.IntegratedTime())) || (!verifier.ValidityPeriodEnd.IsZero() && verifier.ValidityPeriodEnd.Before(entry.IntegratedTime())) { return errors.New("rekor log public key not valid at payload integrated time") } contents, err := json.Marshal(rekorPayload) if err != nil { return fmt.Errorf("marshaling: %w", err) } canonicalized, err := jsoncanonicalizer.Transform(contents) if err != nil { return fmt.Errorf("canonicalizing: %w", err) } hash := sha256.Sum256(canonicalized) if ecdsaPublicKey, ok := verifier.PublicKey.(*ecdsa.PublicKey); !ok { return fmt.Errorf("unsupported public key type: %T", verifier.PublicKey) } else if !ecdsa.VerifyASN1(ecdsaPublicKey, hash[:], entry.signedEntryTimestamp) { return errors.New("unable to verify SET") } return nil } sigstore-go-0.7.1/pkg/tlog/fuzz_test.go000066400000000000000000000035071477477521700201250ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tlog import ( "testing" commonV1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" v1 "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1" ) /* FuzzParseEntry creates a randomized TransparencyLogEntry and parses it */ func FuzzParseEntry(f *testing.F) { f.Fuzz(func(_ *testing.T, proofTreeSize, proofLogIndex, tlEntryIntegratedTime, tlEntryIndex int64, proofRootHash, proofHash1, proofHash2, proofHash3, promiseSETimestamp, tlEntryCanonicalizedBody, logIdKeyId []byte, kindVersion, kindKind, checkpointEnvelope string) { //nolint:errcheck ParseEntry(&v1.TransparencyLogEntry{ LogIndex: tlEntryIndex, LogId: &commonV1.LogId{ KeyId: logIdKeyId, }, KindVersion: &v1.KindVersion{ Kind: kindKind, Version: kindVersion, }, IntegratedTime: tlEntryIntegratedTime, InclusionPromise: &v1.InclusionPromise{ SignedEntryTimestamp: promiseSETimestamp, }, InclusionProof: &v1.InclusionProof{ LogIndex: proofLogIndex, RootHash: proofRootHash, TreeSize: proofTreeSize, Hashes: [][]byte{proofHash1, proofHash2, proofHash3}, Checkpoint: &v1.Checkpoint{ Envelope: checkpointEnvelope, }, }, CanonicalizedBody: tlEntryCanonicalizedBody, }) }) } sigstore-go-0.7.1/pkg/tuf/000077500000000000000000000000001477477521700153655ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/tuf/client.go000066400000000000000000000135421477477521700171770ustar00rootroot00000000000000// 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 tuf import ( "fmt" "path/filepath" "strings" "time" "github.com/theupdateframework/go-tuf/v2/metadata/config" "github.com/theupdateframework/go-tuf/v2/metadata/fetcher" "github.com/theupdateframework/go-tuf/v2/metadata/updater" "github.com/sigstore/sigstore-go/pkg/util" ) // Client is a Sigstore TUF client type Client struct { cfg *config.UpdaterConfig up *updater.Updater opts *Options } // New returns a new client with custom options func New(opts *Options) (*Client, error) { var c = Client{ opts: opts, } dir := filepath.Join(opts.CachePath, URLToPath(opts.RepositoryBaseURL)) var err error if c.cfg, err = config.New(opts.RepositoryBaseURL, opts.Root); err != nil { return nil, fmt.Errorf("failed to create TUF client: %w", err) } c.cfg.LocalMetadataDir = dir c.cfg.LocalTargetsDir = filepath.Join(dir, "targets") c.cfg.DisableLocalCache = c.opts.DisableLocalCache c.cfg.PrefixTargetsWithHash = !c.opts.DisableConsistentSnapshot if c.cfg.DisableLocalCache { c.opts.CachePath = "" c.opts.CacheValidity = 0 c.opts.ForceCache = false } if opts.Fetcher != nil { c.cfg.Fetcher = opts.Fetcher } else { fetcher := fetcher.DefaultFetcher{} fetcher.SetHTTPUserAgent(util.ConstructUserAgent()) c.cfg.Fetcher = &fetcher } // Upon client creation, we may not perform a full TUF update, // based on the cache control configuration. Start with a local // client (only reads content on disk) and then decide if we // must perform a full TUF update. tmpCfg := *c.cfg // Create a temporary config for the first use where UnsafeLocalMode // is true. This means that when we first initialize the client, // we are guaranteed to only read the metadata on disk. // Based on that metadata we take a decision if a full TUF // refresh should be done or not. As so, the tmpCfg is only needed // here and not in future invocations. tmpCfg.UnsafeLocalMode = true c.up, err = updater.New(&tmpCfg) if err != nil { return nil, err } if err = c.loadMetadata(); err != nil { return nil, err } return &c, nil } // DefaultClient returns a Sigstore TUF client for the public good instance func DefaultClient() (*Client, error) { opts := DefaultOptions() return New(opts) } // loadMetadata controls if the client actually should perform a TUF refresh. // The TUF specification mandates so, but for certain Sigstore clients, it // may be beneficial to rely on the cache, or in air-gapped deployments it // it may not even be possible. func (c *Client) loadMetadata() error { // Load the metadata into memory and verify it if err := c.up.Refresh(); err != nil { // this is most likely due to the lack of metadata files // on disk. Perform a full update and return. return c.Refresh() } if c.opts.ForceCache { return nil } else if c.opts.CacheValidity > 0 { cfg, err := LoadConfig(c.configPath()) if err != nil { // Config may not exist, don't error // create a new empty config cfg = &Config{} } cacheValidUntil := cfg.LastTimestamp.AddDate(0, 0, c.opts.CacheValidity) if time.Now().Before(cacheValidUntil) { // No need to update return nil } } return c.Refresh() } func (c *Client) configPath() string { var p = filepath.Join( c.opts.CachePath, fmt.Sprintf("%s.json", URLToPath(c.opts.RepositoryBaseURL)), ) return p } // Refresh forces a refresh of the underlying TUF client. // As the tuf client updater does not support multiple refreshes during // its life-time, this will replace the TUF client updater with a new one. func (c *Client) Refresh() error { var err error c.up, err = updater.New(c.cfg) if err != nil { return fmt.Errorf("failed to create tuf updater: %w", err) } err = c.up.Refresh() if err != nil { return fmt.Errorf("tuf refresh failed: %w", err) } // Update config with last update cfg, err := LoadConfig(c.configPath()) if err != nil { // Likely config file did not exit, create it cfg = &Config{} } cfg.LastTimestamp = time.Now() // ignore error writing update config file _ = cfg.Persist(c.configPath()) return nil } // GetTarget returns a target file from the TUF repository func (c *Client) GetTarget(target string) ([]byte, error) { // Set filepath to the empty string. When we get targets, // we rely in the target info struct instead. const filePath = "" ti, err := c.up.GetTargetInfo(target) if err != nil { return nil, fmt.Errorf("getting info for target \"%s\": %w", target, err) } path, tb, err := c.up.FindCachedTarget(ti, filePath) if err != nil { return nil, fmt.Errorf("getting target cache: %w", err) } if path != "" { // Cached version found return tb, nil } // Download of target is needed // Ignore targetsBaseURL, set to empty string const targetsBaseURL = "" _, tb, err = c.up.DownloadTarget(ti, filePath, targetsBaseURL) if err != nil { return nil, fmt.Errorf("failed to download target file %s - %w", target, err) } return tb, nil } // URLToPath converts a URL to a filename-compatible string func URLToPath(url string) string { // Strip scheme, replace slashes with dashes // e.g. https://github.github.com/prod-tuf-root -> github.github.com-prod-tuf-root fn := url fn, _ = strings.CutPrefix(fn, "https://") fn, _ = strings.CutPrefix(fn, "http://") fn = strings.ReplaceAll(fn, "/", "-") fn = strings.ReplaceAll(fn, ":", "-") return strings.ToLower(fn) } sigstore-go-0.7.1/pkg/tuf/client_test.go000066400000000000000000000335011477477521700202330ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tuf import ( "crypto" "crypto/sha256" "fmt" "net/url" "os" "path/filepath" "regexp" "strconv" "strings" "testing" "time" "github.com/sigstore/sigstore/pkg/signature" "github.com/stretchr/testify/assert" "github.com/theupdateframework/go-tuf/v2/metadata" "github.com/theupdateframework/go-tuf/v2/metadata/repository" "golang.org/x/crypto/ed25519" ) func TestNewOfflineClientFail(t *testing.T) { var opt = DefaultOptions() opt.WithForceCache().WithCachePath(t.TempDir()) opt.WithRepositoryBaseURL("http://localhost:12345") // create a client, it should fail as it's set to forced cache, // and there is no metadata on disk, and the repository url is // invalid. c, err := New(opt) assert.Nil(t, c) assert.Error(t, err) } func TestRefresh(t *testing.T) { r := newTestRepo(t) r.AddTarget("foo", []byte("foo version 1")) rootJSON, err := r.roles.Root().ToBytes(false) if err != nil { t.Fatal(err) } var opt = DefaultOptions(). WithRepositoryBaseURL("https://testing.local"). WithRoot(rootJSON). WithCachePath(t.TempDir()). WithFetcher(r). WithDisableLocalCache() c, err := New(opt) assert.NotNil(t, c) assert.NoError(t, err) target, err := c.GetTarget("foo") assert.NoError(t, err) assert.NotNil(t, target) assert.Equal(t, target, []byte("foo version 1")) r.AddTarget("foo", []byte("foo version 2")) assert.NoError(t, c.Refresh()) target, err = c.GetTarget("foo") assert.NoError(t, err) assert.NotNil(t, target) assert.Equal(t, target, []byte("foo version 2")) } func TestInvalidRoot(t *testing.T) { r := newTestRepo(t) r2 := newTestRepo(t) rootJSON, err := r.roles.Root().ToBytes(false) if err != nil { t.Fatal(err) } // Create a client with a root that is not signed by the given repository fetcher var opt = DefaultOptions(). WithRepositoryBaseURL("https://testing.local"). WithRoot(rootJSON). WithCachePath(t.TempDir()). WithFetcher(r2). WithDisableLocalCache() c, err := New(opt) assert.Nil(t, c) assert.Error(t, err) } func TestInvalidRepositoryURL(t *testing.T) { var opt = DefaultOptions(). WithRepositoryBaseURL(string(byte(0x7f))). WithCachePath(t.TempDir()) c, err := New(opt) assert.Nil(t, c) assert.Error(t, err) } func TestCache(t *testing.T) { r := newTestRepo(t) r.AddTarget("foo", []byte("foo version 1")) rootJSON, err := r.roles.Root().ToBytes(false) if err != nil { t.Fatal(err) } var opt = DefaultOptions(). WithRepositoryBaseURL("https://testing.local"). WithRoot(rootJSON). WithCachePath(t.TempDir()). WithFetcher(r). WithCacheValidity(1) c, err := New(opt) assert.NotNil(t, c) assert.NoError(t, err) target, err := c.GetTarget("foo") assert.NoError(t, err) assert.NotNil(t, target) assert.Equal(t, target, []byte("foo version 1")) r.AddTarget("foo", []byte("foo version 2")) // Create new client with the same cache path c, err = New(opt) assert.NotNil(t, c) assert.NoError(t, err) target, err = c.GetTarget("foo") assert.NoError(t, err) assert.NotNil(t, target) // Cache is still valid, so we should get the old version assert.Equal(t, target, []byte("foo version 1")) // Set last updated time to 2 days ago, to trigger cache refresh cfg, err := LoadConfig(c.configPath()) if err != nil { t.Fatal(err) } cfg.LastTimestamp = time.Now().Add(-48 * time.Hour) err = cfg.Persist(c.configPath()) if err != nil { t.Fatal(err) } // Create new client with the same cache path c, err = New(opt) assert.NotNil(t, c) assert.NoError(t, err) // Now we should get the new version target, err = c.GetTarget("foo") assert.NoError(t, err) assert.Equal(t, target, []byte("foo version 2")) r.AddTarget("foo", []byte("foo version 3")) // Delete config to show that client fetches fresh metadata when no config is present if err = os.Remove(c.configPath()); err != nil { t.Fatal(err) } // Create another new client with the same cache path c, err = New(opt) assert.NotNil(t, c) assert.NoError(t, err) // Cache contains new version target, err = c.GetTarget("foo") assert.NoError(t, err) assert.Equal(t, target, []byte("foo version 3")) } func TestExpiredTimestamp(t *testing.T) { r := newTestRepo(t) r.AddTarget("foo", []byte("foo version 1")) rootJSON, err := r.roles.Root().ToBytes(false) if err != nil { t.Fatal(err) } var opt = DefaultOptions(). WithRepositoryBaseURL("https://testing.local"). WithRoot(rootJSON). WithCachePath(t.TempDir()). WithFetcher(r). WithCacheValidity(1) c, err := New(opt) assert.NotNil(t, c) assert.NoError(t, err) target, err := c.GetTarget("foo") assert.NoError(t, err) assert.Equal(t, target, []byte("foo version 1")) r.AddTarget("foo", []byte("foo version 2")) opt.ForceCache = true c, err = New(opt) assert.NotNil(t, c) assert.NoError(t, err) target, err = c.GetTarget("foo") assert.NoError(t, err) // Using ForceCache, so we should get the old version assert.Equal(t, target, []byte("foo version 1")) r.SetTimestamp(time.Now().Add(-1 * time.Second)) // Manually write timestamp to disk, as Refresh() will fail err = r.roles.Timestamp().ToFile(filepath.Join(opt.CachePath, "testing.local", "timestamp.json"), false) if err != nil { t.Fatal(err) } // Client creation should fail as the timestamp is expired and the repository has an expired timestamp c, err = New(opt) assert.Nil(t, c) assert.Error(t, err) // Update repo with unexpired timestamp r.SetTimestamp(time.Now().AddDate(0, 0, 1)) c, err = New(opt) assert.NotNil(t, c) assert.NoError(t, err) target, err = c.GetTarget("foo") assert.NoError(t, err) // Even though ForceCache is set, we should get the new version since the cached timestamp is expired assert.Equal(t, target, []byte("foo version 2")) } // repo represents repositoryType from // github.com/theupdateframework/go-tuf/v2/metadata/repository, which is // unexported. type repo interface { Root() *metadata.Metadata[metadata.RootType] SetRoot(meta *metadata.Metadata[metadata.RootType]) Snapshot() *metadata.Metadata[metadata.SnapshotType] SetSnapshot(meta *metadata.Metadata[metadata.SnapshotType]) Timestamp() *metadata.Metadata[metadata.TimestampType] SetTimestamp(meta *metadata.Metadata[metadata.TimestampType]) Targets(name string) *metadata.Metadata[metadata.TargetsType] SetTargets(name string, meta *metadata.Metadata[metadata.TargetsType]) } // testRepo is a basic implementation of a TUF repository for testing purposes. // It does not support delegates, multiple signers, thresholds, or other // advanced TUF features, but it is sufficient for testing the sigstore-go // client. Those other features should be covered by the go-tuf tests. This is // primarily intended to test the caching and fetching behavior of the client. type testRepo struct { keys map[string]ed25519.PrivateKey roles repo dir string t *testing.T } func newTestRepo(t *testing.T) *testRepo { var err error r := &testRepo{ keys: make(map[string]ed25519.PrivateKey), roles: repository.New(), t: t, } tomorrow := time.Now().AddDate(0, 0, 1).UTC() targets := metadata.Targets(tomorrow) r.roles.SetTargets(metadata.TARGETS, targets) r.dir, err = os.MkdirTemp("", "tuf-test-repo") if err != nil { t.Fatal(err) } err = os.Mkdir(filepath.Join(r.dir, metadata.TARGETS), 0700) if err != nil { t.Fatal(err) } snapshot := metadata.Snapshot(tomorrow) r.roles.SetSnapshot(snapshot) timestamp := metadata.Timestamp(tomorrow) r.roles.SetTimestamp(timestamp) root := metadata.Root(tomorrow) r.roles.SetRoot(root) for _, name := range []string{metadata.TARGETS, metadata.SNAPSHOT, metadata.TIMESTAMP, metadata.ROOT} { _, private, err := ed25519.GenerateKey(nil) if err != nil { t.Fatal(err) } r.keys[name] = private key, err := metadata.KeyFromPublicKey(private.Public()) if err != nil { t.Fatal(err) } err = r.roles.Root().Signed.AddKey(key, name) if err != nil { t.Fatal(err) } } for _, name := range metadata.TOP_LEVEL_ROLE_NAMES { key := r.keys[name] signer, err := signature.LoadSigner(key, crypto.Hash(0)) if err != nil { t.Fatal(err) } switch name { case metadata.TARGETS: _, err = r.roles.Targets(metadata.TARGETS).Sign(signer) case metadata.SNAPSHOT: _, err = r.roles.Snapshot().Sign(signer) case metadata.TIMESTAMP: _, err = r.roles.Timestamp().Sign(signer) case metadata.ROOT: _, err = r.roles.Root().Sign(signer) } if err != nil { t.Fatal(err) } } return r } // DownloadFile is a test implementation of the Fetcher interface, which the // client may use to avoid making real HTTP requests. It returns the contents // of the metadata files and target files in the test repository. func (r *testRepo) DownloadFile(urlPath string, _ int64, _ time.Duration) ([]byte, error) { u, err := url.Parse(urlPath) if err != nil { return []byte{}, err } if strings.HasPrefix(u.Path, "/targets/") { re := regexp.MustCompile(`/targets/[0-9a-f]{64}\.(.*)$`) matches := re.FindStringSubmatch(u.Path) if len(matches) != 2 { return nil, &metadata.ErrDownloadHTTP{StatusCode: 404} } targetFile, ok := r.roles.Targets(metadata.TARGETS).Signed.Targets[matches[1]] if !ok { return nil, &metadata.ErrDownloadHTTP{StatusCode: 404} } data, err := os.ReadFile(targetFile.Path) if err != nil { return nil, &metadata.ErrDownloadHTTP{StatusCode: 404} } return data, nil } if u.Path == "/timestamp.json" { meta := r.roles.Timestamp() return meta.ToBytes(false) } re := regexp.MustCompile(`/(\d+)\.(root|snapshot|targets)\.json$`) matches := re.FindStringSubmatch(u.Path) if len(matches) != 3 { return nil, &metadata.ErrDownloadHTTP{StatusCode: 404} } role := matches[2] version, err := strconv.Atoi(matches[1]) if err != nil { return []byte{}, &metadata.ErrDownload{} } switch role { case metadata.ROOT: // TODO: handle all versions of signed root meta := r.roles.Root() if meta.Signed.Version != int64(version) { return []byte{}, &metadata.ErrDownloadHTTP{StatusCode: 404} } return meta.ToBytes(false) case metadata.SNAPSHOT: meta := r.roles.Snapshot() if meta.Signed.Version != int64(version) { return []byte{}, &metadata.ErrDownloadHTTP{StatusCode: 404} } return meta.ToBytes(false) case metadata.TARGETS: meta := r.roles.Targets(metadata.TARGETS) if meta.Signed.Version != int64(version) { return []byte{}, &metadata.ErrDownloadHTTP{StatusCode: 404} } return meta.ToBytes(false) } return []byte{}, nil } // AddTarget adds a target file to the repository. It also creates a new // snapshot and timestamp metadata file, and signs them with the appropriate // key. func (r *testRepo) AddTarget(name string, content []byte) { targetHash := sha256.Sum256(content) localPath := filepath.Join(r.dir, metadata.TARGETS, fmt.Sprintf("%x.%s", targetHash, name)) err := os.WriteFile(localPath, content, 0600) if err != nil { r.t.Fatal(err) } targetFileInfo, err := metadata.TargetFile().FromFile(localPath, "sha256") if err != nil { r.t.Fatal(err) } r.roles.Targets(metadata.TARGETS).Signed.Targets[name] = targetFileInfo r.roles.Targets("targets").Signed.Version++ r.roles.Snapshot().Signed.Meta["targets.json"] = metadata.MetaFile(r.roles.Targets(metadata.TARGETS).Signed.Version) r.roles.Snapshot().Signed.Version++ r.roles.Timestamp().Signed.Meta["snapshot.json"] = metadata.MetaFile(r.roles.Snapshot().Signed.Version) r.roles.Timestamp().Signed.Version++ for _, name := range []string{metadata.TARGETS, metadata.SNAPSHOT, metadata.TIMESTAMP} { signer, err := signature.LoadSigner(r.keys[name], crypto.Hash(0)) if err != nil { r.t.Fatal(err) } switch name { case metadata.TARGETS: r.roles.Targets(metadata.TARGETS).ClearSignatures() _, err = r.roles.Targets(metadata.TARGETS).Sign(signer) case metadata.SNAPSHOT: r.roles.Snapshot().ClearSignatures() _, err = r.roles.Snapshot().Sign(signer) case metadata.TIMESTAMP: r.roles.Timestamp().ClearSignatures() _, err = r.roles.Timestamp().Sign(signer) } if err != nil { r.t.Fatal(err) } } } // SetTimestamp sets the expiration date of the timestamp metadata file to the // given date, and increments the version number. It then signs the metadata // file with the appropriate key. func (r *testRepo) SetTimestamp(date time.Time) { r.roles.Timestamp().Signed.Expires = date r.roles.Timestamp().Signed.Version++ signer, err := signature.LoadSigner(r.keys[metadata.TIMESTAMP], crypto.Hash(0)) if err != nil { r.t.Fatal(err) } r.roles.Timestamp().ClearSignatures() _, err = r.roles.Timestamp().Sign(signer) if err != nil { r.t.Fatal(err) } } func TestURLToPath(t *testing.T) { tests := []struct { name string url string want string }{ { name: "no-change", url: "example.com", want: "example.com", }, { name: "simple", url: "https://example.com", want: "example.com", }, { name: "simple with path", url: "https://example.com/foo/bar", want: "example.com-foo-bar", }, { name: "http scheme", url: "http://example.com/foo/bar", want: "example.com-foo-bar", }, { name: "different port", url: "http://example.com:8080/foo/bar", want: "example.com-8080-foo-bar", }, { name: "lowercase", url: "http://EXAMPLE.COM:8080/foo/bar", want: "example.com-8080-foo-bar", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := URLToPath(tt.url); got != tt.want { t.Errorf("URLToPath() = %v, want %v", got, tt.want) } }) } } sigstore-go-0.7.1/pkg/tuf/config.go000066400000000000000000000024251477477521700171640ustar00rootroot00000000000000// 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 tuf import ( "encoding/json" "fmt" "os" "time" ) type Config struct { LastTimestamp time.Time `json:"lastTimestamp"` } func LoadConfig(p string) (*Config, error) { var c Config b, err := os.ReadFile(p) if err != nil { return nil, fmt.Errorf("failed to read config: %w", err) } err = json.Unmarshal(b, &c) if err != nil { return nil, fmt.Errorf("malformed config file: %w", err) } return &c, nil } func (c *Config) Persist(p string) error { b, err := json.Marshal(c) if err != nil { return fmt.Errorf("failed to JSON marshal config: %w", err) } err = os.WriteFile(p, b, 0600) if err != nil { return fmt.Errorf("failed to write config: %w", err) } return nil } sigstore-go-0.7.1/pkg/tuf/config_test.go000066400000000000000000000022551477477521700202240ustar00rootroot00000000000000// 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 tuf import ( "path/filepath" "testing" "time" ) func TestConfig(t *testing.T) { var p = filepath.Join(t.TempDir(), "cfg.json") var ts = time.Now() var c = Config{ LastTimestamp: ts, } err := c.Persist(p) if err != nil { t.Error(err.Error()) } cp, err := LoadConfig(p) if err != nil { t.Error(err.Error()) } delta := ts.Sub(cp.LastTimestamp) if delta < 0 { delta = -delta } // make sure the delta is less than one second. During JSON // serializion precision up to a second may be lost if delta > time.Second { t.Error("wrong date received after load") } } sigstore-go-0.7.1/pkg/tuf/options.go000066400000000000000000000123121477477521700174060ustar00rootroot00000000000000// 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 tuf import ( "embed" "math" "os" "path/filepath" "github.com/theupdateframework/go-tuf/v2/metadata/fetcher" ) //go:embed repository var embeddedRepo embed.FS const ( DefaultMirror = "https://tuf-repo-cdn.sigstore.dev" StagingMirror = "https://tuf-repo-cdn.sigstage.dev" // The following caching values can be used for the CacheValidity option NoCache = 0 MaxCache = math.MaxInt ) // Options represent the various options for a Sigstore TUF Client type Options struct { // CacheValidity period in days (default 0). The client will persist a // timestamp with the cache after refresh. Note that the client will // always refresh the cache if the metadata is expired or if the client is // unable to find a persisted timestamp, so this is not an optimal control // for air-gapped environments. Use const MaxCache to update the cache when // the metadata is expired, though the first initialization will still // refresh the cache. CacheValidity int // ForceCache controls if the cache should be used without update // as long as the metadata is valid. Use ForceCache over CacheValidity // if you want to always use the cache up until its expiration. Note that // the client will refresh the cache once the metadata has expired, so this // is not an optimal control for air-gapped environments. Clients instead // should provide a trust root file directly to the client to bypass TUF. ForceCache bool // Root is the TUF trust anchor Root []byte // CachePath is the location on disk for TUF cache // (default $HOME/.sigstore/tuf) CachePath string // RepositoryBaseURL is the TUF repository location URL // (default https://tuf-repo-cdn.sigstore.dev) RepositoryBaseURL string // DisableLocalCache mode allows a client to work on a read-only // files system if this is set, cache path is ignored. DisableLocalCache bool // DisableConsistentSnapshot DisableConsistentSnapshot bool // Fetcher is the metadata fetcher Fetcher fetcher.Fetcher } // WithCacheValidity sets the cache validity period in days func (o *Options) WithCacheValidity(days int) *Options { o.CacheValidity = days return o } // WithForceCache forces the client to use the cache without updating func (o *Options) WithForceCache() *Options { o.ForceCache = true return o } // WithRoot sets the TUF trust anchor func (o *Options) WithRoot(root []byte) *Options { o.Root = root return o } // WithCachePath sets the location on disk for TUF cache func (o *Options) WithCachePath(path string) *Options { o.CachePath = path return o } // WithRepositoryBaseURL sets the TUF repository location URL func (o *Options) WithRepositoryBaseURL(url string) *Options { o.RepositoryBaseURL = url return o } // WithDisableLocalCache sets the client to work on a read-only file system func (o *Options) WithDisableLocalCache() *Options { o.DisableLocalCache = true return o } // WithDisableConsistentSnapshot sets the client to disable consistent snapshot func (o *Options) WithDisableConsistentSnapshot() *Options { o.DisableConsistentSnapshot = true return o } // WithFetcher sets the metadata fetcher func (o *Options) WithFetcher(f fetcher.Fetcher) *Options { o.Fetcher = f return o } // DefaultOptions returns an options struct for the public good instance func DefaultOptions() *Options { var opts Options var err error opts.Root = DefaultRoot() home, err := os.UserHomeDir() if err != nil { // Fall back to using a TUF repository in the temp location home = os.TempDir() } opts.CachePath = filepath.Join(home, ".sigstore", "root") opts.RepositoryBaseURL = DefaultMirror return &opts } // DefaultRoot returns the root.json for the public good instance func DefaultRoot() []byte { // The embed file system always uses forward slashes as path separators, // even on Windows p := "repository/root.json" b, err := embeddedRepo.ReadFile(p) if err != nil { // This should never happen. // ReadFile from an embedded FS will never fail as long as // the path is correct. If it fails, it would mean // that the binary is not assembled as it should, and there // is no way to recover from that. panic(err) } return b } // StagingRoot returns the root.json for the staging instance func StagingRoot() []byte { // The embed file system always uses forward slashes as path separators, // even on Windows p := "repository/staging_root.json" b, err := embeddedRepo.ReadFile(p) if err != nil { // This should never happen. // ReadFile from an embedded FS will never fail as long as // the path is correct. If it fails, it would mean // that the binary is not assembled as it should, and there // is no way to recover from that. panic(err) } return b } sigstore-go-0.7.1/pkg/tuf/repository/000077500000000000000000000000001477477521700176045ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/tuf/repository/root.json000066400000000000000000000124461477477521700214710ustar00rootroot00000000000000{ "signatures": [ { "keyid": "6f260089d5923daf20166ca657c543af618346ab971884a99962b01988bbe0c3", "sig": "" }, { "keyid": "e71a54d543835ba86adad9460379c7641fb8726d164ea766801a1c522aba7ea2", "sig": "3045022100b0bcf189ce1b93e7db9649d5be512a1880c0e358870e3933e426c5afb8a4061002206d214bd79b09f458ccc521a290aa960c417014fc16e606f82091b5e31814886a" }, { "keyid": "22f4caec6d8e6f9555af66b3d4c3cb06a3bb23fdc7e39c916c61f462e6f52b06", "sig": "" }, { "keyid": "61643838125b440b40db6942f5cb5a31c0dc04368316eb2aaa58b95904a58222", "sig": "3045022100a9b9e294ec21b62dfca6a16a19d084182c12572e33d9c4dcab5317fa1e8a459d022069f68e55ea1f95c5a367aac7a61a65757f93da5a006a5f4d1cf995be812d7602" }, { "keyid": "a687e5bf4fab82b0ee58d46e05c9535145a2c9afb458f43d42b45ca0fdce2a70", "sig": "30440220781178ec3915cb16aca757d40e28435ac5378d6b487acb111d1eeb339397f79a0220781cce48ae46f9e47b97a8414fcf466a986726a5896c72a0e4aba3162cb826dd" } ], "signed": { "_type": "root", "consistent_snapshot": true, "expires": "2025-08-19T14:33:09Z", "keys": { "0c87432c3bf09fd99189fdc32fa5eaedf4e4a5fac7bab73fa04a2e0fc64af6f5": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa", "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWRiGr5+j+3J5SsH+Ztr5nE2H2wO7\nBV+nO3s93gLca18qTOzHY1oWyAGDykMSsGTUBSt9D+An0KfKsD2mfSM42Q==\n-----END PUBLIC KEY-----\n" }, "scheme": "ecdsa-sha2-nistp256", "x-tuf-on-ci-online-uri": "gcpkms:projects/sigstore-root-signing/locations/global/keyRings/root/cryptoKeys/timestamp/cryptoKeyVersions/1" }, "22f4caec6d8e6f9555af66b3d4c3cb06a3bb23fdc7e39c916c61f462e6f52b06": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa", "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzBzVOmHCPojMVLSI364WiiV8NPrD\n6IgRxVliskz/v+y3JER5mcVGcONliDcWMC5J2lfHmjPNPhb4H7xm8LzfSA==\n-----END PUBLIC KEY-----\n" }, "scheme": "ecdsa-sha2-nistp256", "x-tuf-on-ci-keyowner": "@santiagotorres" }, "61643838125b440b40db6942f5cb5a31c0dc04368316eb2aaa58b95904a58222": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa", "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEinikSsAQmYkNeH5eYq/CnIzLaacO\nxlSaawQDOwqKy/tCqxq5xxPSJc21K4WIhs9GyOkKfzueY3GILzcMJZ4cWw==\n-----END PUBLIC KEY-----\n" }, "scheme": "ecdsa-sha2-nistp256", "x-tuf-on-ci-keyowner": "@bobcallaway" }, "6f260089d5923daf20166ca657c543af618346ab971884a99962b01988bbe0c3": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa", "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEy8XKsmhBYDI8Jc0GwzBxeKax0cm5\nSTKEU65HPFunUn41sT8pi0FjM4IkHz/YUmwmLUO0Wt7lxhj6BkLIK4qYAw==\n-----END PUBLIC KEY-----\n" }, "scheme": "ecdsa-sha2-nistp256", "x-tuf-on-ci-keyowner": "@dlorenc" }, "a687e5bf4fab82b0ee58d46e05c9535145a2c9afb458f43d42b45ca0fdce2a70": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa", "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0ghrh92Lw1Yr3idGV5WqCtMDB8Cx\n+D8hdC4w2ZLNIplVRoVGLskYa3gheMyOjiJ8kPi15aQ2//7P+oj7UvJPGw==\n-----END PUBLIC KEY-----\n" }, "scheme": "ecdsa-sha2-nistp256", "x-tuf-on-ci-keyowner": "@joshuagl" }, "e71a54d543835ba86adad9460379c7641fb8726d164ea766801a1c522aba7ea2": { "keyid_hash_algorithms": [ "sha256", "sha512" ], "keytype": "ecdsa", "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEXsz3SZXFb8jMV42j6pJlyjbjR8K\nN3Bwocexq6LMIb5qsWKOQvLN16NUefLc4HswOoumRsVVaajSpQS6fobkRw==\n-----END PUBLIC KEY-----\n" }, "scheme": "ecdsa-sha2-nistp256", "x-tuf-on-ci-keyowner": "@mnm678" } }, "roles": { "root": { "keyids": [ "6f260089d5923daf20166ca657c543af618346ab971884a99962b01988bbe0c3", "e71a54d543835ba86adad9460379c7641fb8726d164ea766801a1c522aba7ea2", "22f4caec6d8e6f9555af66b3d4c3cb06a3bb23fdc7e39c916c61f462e6f52b06", "61643838125b440b40db6942f5cb5a31c0dc04368316eb2aaa58b95904a58222", "a687e5bf4fab82b0ee58d46e05c9535145a2c9afb458f43d42b45ca0fdce2a70" ], "threshold": 3 }, "snapshot": { "keyids": [ "0c87432c3bf09fd99189fdc32fa5eaedf4e4a5fac7bab73fa04a2e0fc64af6f5" ], "threshold": 1, "x-tuf-on-ci-expiry-period": 3650, "x-tuf-on-ci-signing-period": 365 }, "targets": { "keyids": [ "6f260089d5923daf20166ca657c543af618346ab971884a99962b01988bbe0c3", "e71a54d543835ba86adad9460379c7641fb8726d164ea766801a1c522aba7ea2", "22f4caec6d8e6f9555af66b3d4c3cb06a3bb23fdc7e39c916c61f462e6f52b06", "61643838125b440b40db6942f5cb5a31c0dc04368316eb2aaa58b95904a58222", "a687e5bf4fab82b0ee58d46e05c9535145a2c9afb458f43d42b45ca0fdce2a70" ], "threshold": 3 }, "timestamp": { "keyids": [ "0c87432c3bf09fd99189fdc32fa5eaedf4e4a5fac7bab73fa04a2e0fc64af6f5" ], "threshold": 1, "x-tuf-on-ci-expiry-period": 7, "x-tuf-on-ci-signing-period": 6 } }, "spec_version": "1.0", "version": 12, "x-tuf-on-ci-expiry-period": 197, "x-tuf-on-ci-signing-period": 46 } } sigstore-go-0.7.1/pkg/tuf/repository/staging_root.json000066400000000000000000000101761477477521700232030ustar00rootroot00000000000000{ "signatures": [ { "keyid": "aa61e09f6af7662ac686cf0c6364079f63d3e7a86836684eeced93eace3acd81", "sig": "3044022064ac6af7f922e3bc8ac095d1fb59c5e65b52c8b378d3777b9223fc63b65c1f05022022a3722f464b3cfb985cdd76b76790533c5ac81613dade8f3a1136d4473dc466" }, { "keyid": "61f9609d2655b346fcebccd66b509d5828168d5e447110e261f0bcc8553624bc", "sig": "3046022100ef742d08c803a87e4eabbefbad528e40bdbe7aa9dcdcdcc024aa256315c8bcf202210089e444aebb431f743fad85cecbb16a3cfd62b624dbd37a9bfdce21135659bd8b" }, { "keyid": "9471fbda95411d10109e467ad526082d15f14a38de54ea2ada9687ab39d8e237", "sig": "" }, { "keyid": "0374a9e18a20a2103736cb4277e2fdd7f8453642c7d9eaf4ad8aee9cf2d47bb5", "sig": "" } ], "signed": { "_type": "root", "consistent_snapshot": true, "expires": "2025-08-01T13:24:50Z", "keys": { "0374a9e18a20a2103736cb4277e2fdd7f8453642c7d9eaf4ad8aee9cf2d47bb5": { "keytype": "ecdsa", "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoxkvDOmtGEknB3M+ZkPts8joDM0X\nIH5JZwPlgC2CXs/eqOuNF8AcEWwGYRiDhV/IMlQw5bg8PLICQcgsbrDiKg==\n-----END PUBLIC KEY-----\n" }, "scheme": "ecdsa-sha2-nistp256", "x-tuf-on-ci-keyowner": "@mnm678" }, "61f9609d2655b346fcebccd66b509d5828168d5e447110e261f0bcc8553624bc": { "keytype": "ecdsa", "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE++Wv+DcLRk+mfkmlpCwl1GUi9EMh\npBUTz8K0fH7bE4mQuViGSyWA/eyMc0HvzZi6Xr0diHw0/lUPBvok214YQw==\n-----END PUBLIC KEY-----\n" }, "scheme": "ecdsa-sha2-nistp256", "x-tuf-on-ci-keyowner": "@kommendorkapten" }, "9471fbda95411d10109e467ad526082d15f14a38de54ea2ada9687ab39d8e237": { "keytype": "ecdsa", "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFHDb85JH+JYR1LQmxiz4UMokVMnP\nxKoWpaEnFCKXH8W4Fc/DfIxMnkpjCuvWUBdJXkO0aDIxwsij8TOFh2R7dw==\n-----END PUBLIC KEY-----\n" }, "scheme": "ecdsa-sha2-nistp256", "x-tuf-on-ci-keyowner": "@joshuagl" }, "aa61e09f6af7662ac686cf0c6364079f63d3e7a86836684eeced93eace3acd81": { "keytype": "ecdsa", "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEohqIdE+yTl4OxpX8ZxNUPrg3SL9H\nBDnhZuceKkxy2oMhUOxhWweZeG3bfM1T4ZLnJimC6CAYVU5+F5jZCoftRw==\n-----END PUBLIC KEY-----\n" }, "scheme": "ecdsa-sha2-nistp256", "x-tuf-on-ci-keyowner": "@jku" }, "c3479007e861445ce5dc109d9661ed77b35bbc0e3f161852c46114266fc2daa4": { "keytype": "ecdsa", "keyval": { "public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExxmEtmhF5U+i+v/6he4BcSLzCgMx\n/0qSrvDg6bUWwUrkSKS2vDpcJrhGy5fmmhRrGawjPp1ALpC3y1kqFTpXDg==\n-----END PUBLIC KEY-----\n" }, "scheme": "ecdsa-sha2-nistp256", "x-tuf-on-ci-online-uri": "gcpkms:projects/projectsigstore-staging/locations/global/keyRings/tuf-keyring/cryptoKeys/tuf-key/cryptoKeyVersions/2" } }, "roles": { "root": { "keyids": [ "aa61e09f6af7662ac686cf0c6364079f63d3e7a86836684eeced93eace3acd81", "61f9609d2655b346fcebccd66b509d5828168d5e447110e261f0bcc8553624bc", "9471fbda95411d10109e467ad526082d15f14a38de54ea2ada9687ab39d8e237", "0374a9e18a20a2103736cb4277e2fdd7f8453642c7d9eaf4ad8aee9cf2d47bb5" ], "threshold": 2 }, "snapshot": { "keyids": [ "c3479007e861445ce5dc109d9661ed77b35bbc0e3f161852c46114266fc2daa4" ], "threshold": 1, "x-tuf-on-ci-expiry-period": 3650, "x-tuf-on-ci-signing-period": 365 }, "targets": { "keyids": [ "aa61e09f6af7662ac686cf0c6364079f63d3e7a86836684eeced93eace3acd81", "61f9609d2655b346fcebccd66b509d5828168d5e447110e261f0bcc8553624bc", "9471fbda95411d10109e467ad526082d15f14a38de54ea2ada9687ab39d8e237", "0374a9e18a20a2103736cb4277e2fdd7f8453642c7d9eaf4ad8aee9cf2d47bb5" ], "threshold": 1 }, "timestamp": { "keyids": [ "c3479007e861445ce5dc109d9661ed77b35bbc0e3f161852c46114266fc2daa4" ], "threshold": 1, "x-tuf-on-ci-expiry-period": 7, "x-tuf-on-ci-signing-period": 6 } }, "spec_version": "1.0", "version": 11, "x-tuf-on-ci-expiry-period": 182, "x-tuf-on-ci-signing-period": 35 } } sigstore-go-0.7.1/pkg/util/000077500000000000000000000000001477477521700155445ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/util/util.go000066400000000000000000000016761477477521700170620ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package util import ( "runtime/debug" ) func ConstructUserAgent() string { userAgent := "sigstore-go" buildInfo, ok := debug.ReadBuildInfo() if !ok { return userAgent } for _, eachDep := range buildInfo.Deps { if eachDep.Path == "github.com/sigstore/sigstore-go" { userAgent += "/" userAgent += eachDep.Version } } return userAgent } sigstore-go-0.7.1/pkg/verify/000077500000000000000000000000001477477521700160735ustar00rootroot00000000000000sigstore-go-0.7.1/pkg/verify/certificate.go000066400000000000000000000021351477477521700207050ustar00rootroot00000000000000// 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 verify import ( "crypto/x509" "errors" "time" "github.com/sigstore/sigstore-go/pkg/root" ) func VerifyLeafCertificate(observerTimestamp time.Time, leafCert *x509.Certificate, trustedMaterial root.TrustedMaterial) ([][]*x509.Certificate, error) { // nolint: revive for _, ca := range trustedMaterial.FulcioCertificateAuthorities() { chains, err := ca.Verify(leafCert, observerTimestamp) if err == nil { return chains, nil } } return nil, errors.New("leaf certificate verification failed") } sigstore-go-0.7.1/pkg/verify/certificate_identity.go000066400000000000000000000155671477477521700226330ustar00rootroot00000000000000// 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 verify import ( "encoding/json" "errors" "fmt" "regexp" "github.com/sigstore/sigstore-go/pkg/fulcio/certificate" ) type SubjectAlternativeNameMatcher struct { SubjectAlternativeName string `json:"subjectAlternativeName"` Regexp regexp.Regexp `json:"regexp,omitempty"` } type IssuerMatcher struct { Issuer string `json:"issuer"` Regexp regexp.Regexp `json:"regexp,omitempty"` } type CertificateIdentity struct { SubjectAlternativeName SubjectAlternativeNameMatcher `json:"subjectAlternativeName"` Issuer IssuerMatcher `json:"issuer"` certificate.Extensions } type CertificateIdentities []CertificateIdentity type ErrValueMismatch struct { object string expected string actual string } func (e *ErrValueMismatch) Error() string { return fmt.Sprintf("expected %s value \"%s\", got \"%s\"", e.object, e.expected, e.actual) } type ErrValueRegexMismatch struct { object string regex string value string } func (e *ErrValueRegexMismatch) Error() string { return fmt.Sprintf("expected %s value to match regex \"%s\", got \"%s\"", e.object, e.regex, e.value) } type ErrNoMatchingCertificateIdentity struct { errors []error } func (e *ErrNoMatchingCertificateIdentity) Error() string { if len(e.errors) > 0 { return fmt.Sprintf("no matching CertificateIdentity found, last error: %v", e.errors[len(e.errors)-1]) } return "no matching CertificateIdentity found" } func (e *ErrNoMatchingCertificateIdentity) Unwrap() []error { return e.errors } // NewSANMatcher provides an easier way to create a SubjectAlternativeNameMatcher. // If the regexpStr fails to compile into a Regexp, an error is returned. func NewSANMatcher(sanValue string, regexpStr string) (SubjectAlternativeNameMatcher, error) { r, err := regexp.Compile(regexpStr) if err != nil { return SubjectAlternativeNameMatcher{}, err } return SubjectAlternativeNameMatcher{ SubjectAlternativeName: sanValue, Regexp: *r}, nil } // The default Regexp json marshal is quite ugly, so we override it here. func (s *SubjectAlternativeNameMatcher) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { SubjectAlternativeName string `json:"subjectAlternativeName"` Regexp string `json:"regexp,omitempty"` }{ SubjectAlternativeName: s.SubjectAlternativeName, Regexp: s.Regexp.String(), }) } // Verify checks if the actualCert matches the SANMatcher's Value and // Regexp – if those values have been provided. func (s SubjectAlternativeNameMatcher) Verify(actualCert certificate.Summary) error { if s.SubjectAlternativeName != "" && actualCert.SubjectAlternativeName != s.SubjectAlternativeName { return &ErrValueMismatch{"SAN", string(s.SubjectAlternativeName), string(actualCert.SubjectAlternativeName)} } if s.Regexp.String() != "" && !s.Regexp.MatchString(actualCert.SubjectAlternativeName) { return &ErrValueRegexMismatch{"SAN", string(s.Regexp.String()), string(actualCert.SubjectAlternativeName)} } return nil } func NewIssuerMatcher(issuerValue, regexpStr string) (IssuerMatcher, error) { r, err := regexp.Compile(regexpStr) if err != nil { return IssuerMatcher{}, err } return IssuerMatcher{Issuer: issuerValue, Regexp: *r}, nil } func (i *IssuerMatcher) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { Issuer string `json:"issuer"` Regexp string `json:"regexp,omitempty"` }{ Issuer: i.Issuer, Regexp: i.Regexp.String(), }) } func (i IssuerMatcher) Verify(actualCert certificate.Summary) error { if i.Issuer != "" && actualCert.Issuer != i.Issuer { return &ErrValueMismatch{"issuer", string(i.Issuer), string(actualCert.Issuer)} } if i.Regexp.String() != "" && !i.Regexp.MatchString(actualCert.Issuer) { return &ErrValueRegexMismatch{"issuer", string(i.Regexp.String()), string(actualCert.Issuer)} } return nil } func NewCertificateIdentity(sanMatcher SubjectAlternativeNameMatcher, issuerMatcher IssuerMatcher, extensions certificate.Extensions) (CertificateIdentity, error) { if sanMatcher.SubjectAlternativeName == "" && sanMatcher.Regexp.String() == "" { return CertificateIdentity{}, errors.New("when verifying a certificate identity, there must be subject alternative name criteria") } if issuerMatcher.Issuer == "" && issuerMatcher.Regexp.String() == "" { return CertificateIdentity{}, errors.New("when verifying a certificate identity, must specify Issuer criteria") } if extensions.Issuer != "" { return CertificateIdentity{}, errors.New("please specify issuer in IssuerMatcher, not Extensions") } certID := CertificateIdentity{ SubjectAlternativeName: sanMatcher, Issuer: issuerMatcher, Extensions: extensions, } return certID, nil } // NewShortCertificateIdentity provides a more convenient way of initializing // a CertificiateIdentity with a SAN and the Issuer OID extension. If you need // to check more OID extensions, use NewCertificateIdentity instead. func NewShortCertificateIdentity(issuer, issuerRegex, sanValue, sanRegex string) (CertificateIdentity, error) { sanMatcher, err := NewSANMatcher(sanValue, sanRegex) if err != nil { return CertificateIdentity{}, err } issuerMatcher, err := NewIssuerMatcher(issuer, issuerRegex) if err != nil { return CertificateIdentity{}, err } return NewCertificateIdentity(sanMatcher, issuerMatcher, certificate.Extensions{}) } // Verify verifies the CertificateIdentities, and if ANY of them match the cert, // it returns the CertificateIdentity that matched. If none match, it returns an // error. func (i CertificateIdentities) Verify(cert certificate.Summary) (*CertificateIdentity, error) { multierr := &ErrNoMatchingCertificateIdentity{} var err error for _, ci := range i { if err = ci.Verify(cert); err == nil { return &ci, nil } multierr.errors = append(multierr.errors, err) } return nil, multierr } // Verify checks if the actualCert matches the CertificateIdentity's SAN and // any of the provided OID extension values. Any empty values are ignored. func (c CertificateIdentity) Verify(actualCert certificate.Summary) error { var err error if err = c.SubjectAlternativeName.Verify(actualCert); err != nil { return err } if err = c.Issuer.Verify(actualCert); err != nil { return err } return certificate.CompareExtensions(c.Extensions, actualCert.Extensions) } sigstore-go-0.7.1/pkg/verify/certificate_identity_test.go000066400000000000000000000146761477477521700236720ustar00rootroot00000000000000// 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 verify import ( "testing" "github.com/sigstore/sigstore-go/pkg/fulcio/certificate" "github.com/stretchr/testify/assert" ) const ( ActionsIssuerValue = "https://token.actions.githubusercontent.com" ActionsIssuerRegex = "githubusercontent.com$" SigstoreSanValue = "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main" SigstoreSanRegex = "^https://github.com/sigstore/sigstore-js/" ) func TestCertificateIdentityVerify(t *testing.T) { // given a certificate summary, it does what we expect actualCert := certificate.Summary{ SubjectAlternativeName: SigstoreSanValue, Extensions: certificate.Extensions{ Issuer: ActionsIssuerValue, GithubWorkflowTrigger: "push", GithubWorkflowSHA: "f0b49a04e5a62250e0f60fb128004a73110fe311", GithubWorkflowName: "Release", GithubWorkflowRepository: "sigstore/sigstore-js", GithubWorkflowRef: "refs/heads/main", BuildSignerURI: "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main", BuildSignerDigest: "f0b49a04e5a62250e0f60fb128004a73110fe311", RunnerEnvironment: "github-hosted", SourceRepositoryURI: "https://github.com/sigstore/sigstore-js", SourceRepositoryDigest: "f0b49a04e5a62250e0f60fb128004a73110fe311", SourceRepositoryRef: "refs/heads/main", SourceRepositoryIdentifier: "495574555", SourceRepositoryOwnerURI: "https://github.com/sigstore", SourceRepositoryOwnerIdentifier: "71096353", BuildConfigURI: "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main", BuildConfigDigest: "f0b49a04e5a62250e0f60fb128004a73110fe311", BuildTrigger: "push", RunInvocationURI: "https://github.com/sigstore/sigstore-js/actions/runs/5904696764/attempts/1", SourceRepositoryVisibilityAtSigning: "public", }, } // First, let's test happy paths: issuerOnlyID, _ := certIDForTesting("", "", ActionsIssuerValue, "", "") assert.NoError(t, issuerOnlyID.Verify(actualCert)) issuerOnlyRegex, _ := certIDForTesting("", "", "", ActionsIssuerRegex, "") assert.NoError(t, issuerOnlyRegex.Verify(actualCert)) sanValueOnly, _ := certIDForTesting(SigstoreSanValue, "", "", "", "") assert.NoError(t, sanValueOnly.Verify(actualCert)) sanRegexOnly, _ := certIDForTesting("", SigstoreSanRegex, "", "", "") assert.NoError(t, sanRegexOnly.Verify(actualCert)) // multiple values can be specified sanRegexAndIssuer, _ := certIDForTesting("", SigstoreSanRegex, ActionsIssuerValue, "", "github-hosted") assert.NoError(t, sanRegexAndIssuer.Verify(actualCert)) // unhappy paths: // wrong issuer sanRegexAndWrongIssuer, _ := certIDForTesting("", SigstoreSanRegex, "https://token.actions.example.com", "", "") errValueMismatch := &ErrValueMismatch{} assert.ErrorAs(t, sanRegexAndWrongIssuer.Verify(actualCert), &errValueMismatch) assert.Equal(t, "expected issuer value \"https://token.actions.example.com\", got \"https://token.actions.githubusercontent.com\"", errValueMismatch.Error()) // bad san regex badRegex, _ := certIDForTesting("", "^badregex.*", "", "", "") errValueRegexMismatch := &ErrValueRegexMismatch{} assert.ErrorAs(t, badRegex.Verify(actualCert), &errValueRegexMismatch) assert.Equal(t, "expected SAN value to match regex \"^badregex.*\", got \"https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main\"", errValueRegexMismatch.Error()) // bad issuer regex badIssuerRegex, _ := certIDForTesting("", "", "", "^badregex$", "") assert.Error(t, badIssuerRegex.Verify(actualCert)) // if we have an array of certIDs, only one needs to match ci, err := CertificateIdentities{sanRegexAndWrongIssuer, sanRegexAndIssuer}.Verify(actualCert) assert.NoError(t, err) assert.Equal(t, *ci, sanRegexAndIssuer) // if none match, we fail ci, err = CertificateIdentities{badRegex, sanRegexAndWrongIssuer}.Verify(actualCert) assert.Error(t, err) assert.Equal(t, "no matching CertificateIdentity found, last error: expected issuer value \"https://token.actions.example.com\", got \"https://token.actions.githubusercontent.com\"", err.Error()) assert.Nil(t, ci) // test err unwrap for previous error errValueMismatch = &ErrValueMismatch{} assert.ErrorAs(t, err, &errValueMismatch) assert.Equal(t, "expected issuer value \"https://token.actions.example.com\", got \"https://token.actions.githubusercontent.com\"", errValueMismatch.Error()) // if no certIDs are specified, we fail _, err = CertificateIdentities{}.Verify(actualCert) assert.Error(t, err) assert.Equal(t, "no matching CertificateIdentity found", err.Error()) } func TestThatCertIDsAreFullySpecified(t *testing.T) { _, err := NewShortCertificateIdentity("", "", "", "") assert.Error(t, err) _, err = NewShortCertificateIdentity("foobar", "", "", "") assert.Error(t, err) _, err = NewShortCertificateIdentity("", ActionsIssuerRegex, "", "") assert.Error(t, err) _, err = NewShortCertificateIdentity("", "", "", SigstoreSanRegex) assert.Error(t, err) _, err = NewShortCertificateIdentity("foobar", "", "", SigstoreSanRegex) assert.Nil(t, err) _, err = NewShortCertificateIdentity("", ActionsIssuerRegex, "", SigstoreSanRegex) assert.Nil(t, err) } func certIDForTesting(sanValue, sanRegex, issuer, issuerRegex, runnerEnv string) (CertificateIdentity, error) { san, err := NewSANMatcher(sanValue, sanRegex) if err != nil { return CertificateIdentity{}, err } issuerMatcher, err := NewIssuerMatcher(issuer, issuerRegex) if err != nil { return CertificateIdentity{}, err } return CertificateIdentity{SubjectAlternativeName: san, Issuer: issuerMatcher, Extensions: certificate.Extensions{RunnerEnvironment: runnerEnv}}, nil } sigstore-go-0.7.1/pkg/verify/certificate_test.go000066400000000000000000000034131477477521700217440ustar00rootroot00000000000000// 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 verify_test import ( "testing" "time" "github.com/sigstore/sigstore-go/pkg/testing/ca" "github.com/sigstore/sigstore-go/pkg/verify" "github.com/stretchr/testify/assert" ) func TestVerifyValidityPeriod(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) leaf, _, err := virtualSigstore.GenerateLeafCert("example@example.com", "issuer") assert.NoError(t, err) tests := []struct { name string observerTimestamp time.Time wantErr bool }{ { name: "before validity period", observerTimestamp: time.Now().Add(time.Hour * -24), wantErr: true, }, { name: "inside validity period", observerTimestamp: time.Now(), wantErr: false, }, { name: "after validity period", observerTimestamp: time.Now().Add(time.Hour * 24), wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if _, err := verify.VerifyLeafCertificate(tt.observerTimestamp, leaf, virtualSigstore); (err != nil) != tt.wantErr { t.Errorf("VerifyLeafCertificate() error = %v, wantErr %v", err, tt.wantErr) } }) } } sigstore-go-0.7.1/pkg/verify/dsse_test.go000066400000000000000000000052411477477521700204210ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package verify import ( "context" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "errors" "fmt" "testing" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/sigstore/pkg/signature" "github.com/stretchr/testify/assert" ) type dsseSigner struct { pk *ecdsa.PrivateKey } func (d *dsseSigner) Sign(_ context.Context, data []byte) ([]byte, error) { digest := sha256.Sum256(data) return d.pk.Sign(rand.Reader, digest[:], nil) } func (d *dsseSigner) KeyID() (string, error) { return "", nil } type envelopeContent struct { EnvelopeContent e *dsse.Envelope } func (e *envelopeContent) RawEnvelope() *dsse.Envelope { return e.e } func TestVerifyEnvelopeSignatureCount(t *testing.T) { privk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) pubk := privk.Public().(*ecdsa.PublicKey) assert.NoError(t, err) var s = dsseSigner{ pk: privk, } testcases := []struct { count int fail bool }{ { count: 0, fail: true, }, { count: 1, fail: false, }, { count: 2, fail: true, }, } for _, tc := range testcases { t.Run(fmt.Sprintf("Test DSSE verify with %d signatures", tc.count), func(t *testing.T) { var e *dsse.Envelope if tc.count == 0 { // Need to create the envelope manually e = &dsse.Envelope{ PayloadType: "test-payload-type", // b64(test-payload) Payload: "dGVzdC1wYXlsb2Fk", } } else { var signers []dsse.Signer for i := 0; i < tc.count; i++ { signers = append(signers, &s) } es, err := dsse.NewEnvelopeSigner(signers...) assert.NoError(t, err) e, err = es.SignPayload(context.Background(), "test-payload-type", []byte("test-payload")) assert.NoError(t, err) } sigver, err := signature.LoadECDSAVerifier(pubk, crypto.SHA256) assert.NoError(t, err) err = verifyEnvelope( sigver, &envelopeContent{ e: e, }, ) if tc.fail { assert.True(t, errors.Is(err, ErrDSSEInvalidSignatureCount)) } else { assert.NoError(t, err) } }) } } sigstore-go-0.7.1/pkg/verify/errors.go000066400000000000000000000017361477477521700177450ustar00rootroot00000000000000// 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 verify import ( "fmt" ) type ErrVerification struct { err error } func NewVerificationError(e error) ErrVerification { return ErrVerification{e} } func (e ErrVerification) Unwrap() error { return e.err } func (e ErrVerification) String() string { return fmt.Sprintf("verification error: %s", e.err.Error()) } func (e ErrVerification) Error() string { return e.String() } sigstore-go-0.7.1/pkg/verify/fuzz_test.go000066400000000000000000000142331477477521700204620ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package verify_test import ( "bytes" "io" "testing" protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/testing/ca" "github.com/sigstore/sigstore-go/pkg/verify" "google.golang.org/protobuf/encoding/protojson" ) var FuzzSkipArtifactAndIdentitiesPolicy = verify.NewPolicy(verify.WithoutArtifactUnsafe(), verify.WithoutIdentitiesUnsafe()) /* Tests VerifyTimestampAuthority with an entity that contains a randomized email and statement */ func FuzzVerifyTimestampAuthorityWithoutThreshold(f *testing.F) { f.Fuzz(func(t *testing.T, email string, statement []byte) { virtualSigstore, err := ca.NewVirtualSigstore() if err != nil { t.Fatal(err) } entity, err := virtualSigstore.Attest(email, "issuer", statement) if err != nil { t.Skip() } //nolint:errcheck verify.VerifyTimestampAuthority(entity, virtualSigstore) }) } /* Tests VerifyTimestampAuthorityWithThreshold with an entity that contains a randomized email, statement and a randomized threshold */ func FuzzVerifyTimestampAuthorityWithThreshold(f *testing.F) { f.Fuzz(func(t *testing.T, email string, statement []byte, threshold int) { virtualSigstore, err := ca.NewVirtualSigstore() if err != nil { t.Fatal(err) } entity, err := virtualSigstore.Attest(email, "issuer", statement) if err != nil { t.Skip() } //nolint:errcheck verify.VerifyTimestampAuthorityWithThreshold(entity, virtualSigstore, threshold) }) } /* Tests VerifyArtifactTransparencyLog with an entity that contains a randomized email and statement and a randomized log threshold and integrated time */ func FuzzVerifyArtifactTransparencyLog(f *testing.F) { f.Fuzz(func(t *testing.T, email string, statement []byte, logThreshold int, trustIntegratedTime bool) { virtualSigstore, err := ca.NewVirtualSigstore() if err != nil { t.Fatal(err) } entity, err := virtualSigstore.Attest(email, "issuer", statement) if err != nil { t.Skip() } //nolint:errcheck verify.VerifyArtifactTransparencyLog(entity, virtualSigstore, logThreshold, trustIntegratedTime) }) } /* Tests Verify with an entity that contains a randomized email and statement and a randomized root */ func FuzzSignedEntityVerifier(f *testing.F) { f.Fuzz(func(t *testing.T, trustedrootJSON, bundleBytes []byte) { trustedRoot, err := root.NewTrustedRootFromJSON(trustedrootJSON) if err != nil || trustedRoot == nil { t.Skip() } var b protobundle.Bundle err = protojson.Unmarshal(bundleBytes, &b) if err != nil { t.Skip() } entity, err := bundle.NewBundle(&b) if err != nil { t.Skip() } v, err := verify.NewSignedEntityVerifier(trustedRoot, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) if err != nil { t.Fatal(err) } //nolint:errcheck v.Verify(entity, FuzzSkipArtifactAndIdentitiesPolicy) }) } /* Tests VerifySignature with a sigContent and verificationsContent from an entity that contains a randomized email and statement. */ func FuzzVerifySignatureWithoutArtifactOrDigest(f *testing.F) { f.Fuzz(func(t *testing.T, email string, statement []byte) { virtualSigstore, err := ca.NewVirtualSigstore() if err != nil { t.Fatal(err) } entity, err := virtualSigstore.Attest(email, "issuer", statement) if err != nil { t.Skip() } sigContent, err := entity.SignatureContent() if err != nil { t.Fatal(err) } verificationContent, err := entity.VerificationContent() if err != nil { t.Fatal(err) } //nolint:errcheck verify.VerifySignature(sigContent, verificationContent, virtualSigstore) }) } /* Tests VerifySignatureWithArtifact with a sigContent and verificationsContent from an entity that contains a randomized email and statement. The fuzzer also creates an artifact from random bytes */ func FuzzVerifySignatureWithArtifactWithoutDigest(f *testing.F) { f.Fuzz(func(t *testing.T, email string, statement, artifactBytes []byte) { virtualSigstore, err := ca.NewVirtualSigstore() if err != nil { t.Fatal(err) } entity, err := virtualSigstore.Attest(email, "issuer", statement) if err != nil { t.Skip() } sigContent, err := entity.SignatureContent() if err != nil { t.Fatal(err) } verificationContent, err := entity.VerificationContent() if err != nil { t.Fatal(err) } artifacts := []io.Reader{bytes.NewReader(artifactBytes)} //nolint:errcheck verify.VerifySignatureWithArtifacts(sigContent, verificationContent, virtualSigstore, artifacts) }) } /* Tests VerifySignatureWithArtifactDigest with a sigContent and verificationsContent from an entity that contains a randomized email and statement. The fuzzer also passes a digest from random bytes and a random string for the algorithm */ func FuzzVerifySignatureWithArtifactDigest(f *testing.F) { f.Fuzz(func(t *testing.T, email, artifactDigestAlgorithm string, statement, artifactDigest []byte) { virtualSigstore, err := ca.NewVirtualSigstore() if err != nil { t.Fatal(err) } entity, err := virtualSigstore.Attest(email, "issuer", statement) if err != nil { t.Skip() } sigContent, err := entity.SignatureContent() if err != nil { t.Fatal(err) } verificationContent, err := entity.VerificationContent() if err != nil { t.Fatal(err) } artifactDigests := []verify.ArtifactDigest{{ Algorithm: artifactDigestAlgorithm, Digest: artifactDigest, }} //nolint:errcheck verify.VerifySignatureWithArtifactDigests(sigContent, verificationContent, virtualSigstore, artifactDigests) }) } sigstore-go-0.7.1/pkg/verify/interface.go000066400000000000000000000057631477477521700203750ustar00rootroot00000000000000// 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 verify import ( "crypto/x509" "errors" "time" in_toto "github.com/in-toto/attestation/go/v1" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/tlog" ) var errNotImplemented = errors.New("not implemented") type HasInclusionPromise interface { HasInclusionPromise() bool } type HasInclusionProof interface { HasInclusionProof() bool } type SignatureProvider interface { SignatureContent() (SignatureContent, error) } type SignedTimestampProvider interface { Timestamps() ([][]byte, error) } type TlogEntryProvider interface { TlogEntries() ([]*tlog.Entry, error) } type VerificationProvider interface { VerificationContent() (VerificationContent, error) } type SignedEntity interface { HasInclusionPromise HasInclusionProof SignatureProvider SignedTimestampProvider TlogEntryProvider VerificationProvider } type VerificationContent interface { CompareKey(any, root.TrustedMaterial) bool ValidAtTime(time.Time, root.TrustedMaterial) bool Certificate() *x509.Certificate PublicKey() PublicKeyProvider } type SignatureContent interface { Signature() []byte EnvelopeContent() EnvelopeContent MessageSignatureContent() MessageSignatureContent } type PublicKeyProvider interface { Hint() string } type MessageSignatureContent interface { Digest() []byte DigestAlgorithm() string Signature() []byte } type EnvelopeContent interface { RawEnvelope() *dsse.Envelope Statement() (*in_toto.Statement, error) } // BaseSignedEntity is a helper struct that implements all the interfaces // of SignedEntity. It can be embedded in a struct to implement the SignedEntity // interface. This may be useful for testing, or for implementing a SignedEntity // that only implements a subset of the interfaces. type BaseSignedEntity struct{} var _ SignedEntity = &BaseSignedEntity{} func (b *BaseSignedEntity) HasInclusionPromise() bool { return false } func (b *BaseSignedEntity) HasInclusionProof() bool { return false } func (b *BaseSignedEntity) VerificationContent() (VerificationContent, error) { return nil, errNotImplemented } func (b *BaseSignedEntity) SignatureContent() (SignatureContent, error) { return nil, errNotImplemented } func (b *BaseSignedEntity) Timestamps() ([][]byte, error) { return nil, errNotImplemented } func (b *BaseSignedEntity) TlogEntries() ([]*tlog.Entry, error) { return nil, errNotImplemented } sigstore-go-0.7.1/pkg/verify/sct.go000066400000000000000000000056721477477521700172250ustar00rootroot00000000000000// 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 verify import ( "crypto/x509" "encoding/hex" "errors" "fmt" ct "github.com/google/certificate-transparency-go" "github.com/google/certificate-transparency-go/ctutil" ctx509 "github.com/google/certificate-transparency-go/x509" "github.com/google/certificate-transparency-go/x509util" "github.com/sigstore/sigstore-go/pkg/root" ) // VerifySignedCertificateTimestamp, given a threshold, TrustedMaterial, and a // leaf certificate, will extract SCTs from the leaf certificate and verify the // timestamps using the TrustedMaterial's FulcioCertificateAuthorities() and // CTLogs() func VerifySignedCertificateTimestamp(chains [][]*x509.Certificate, threshold int, trustedMaterial root.TrustedMaterial) error { // nolint: revive if len(chains) == 0 || len(chains[0]) == 0 || chains[0][0] == nil { return errors.New("no chains provided") } // The first certificate in the chain is always the leaf certificate leaf := chains[0][0] ctlogs := trustedMaterial.CTLogs() scts, err := x509util.ParseSCTsFromCertificate(leaf.Raw) if err != nil { return err } leafCTCert, err := ctx509.ParseCertificates(leaf.Raw) if err != nil { return err } verified := 0 for _, sct := range scts { encodedKeyID := hex.EncodeToString(sct.LogID.KeyID[:]) key, ok := ctlogs[encodedKeyID] if !ok { // skip entries the trust root cannot verify continue } // Ensure sct is within ctlog validity window sctTime := ct.TimestampToTime(sct.Timestamp) if !key.ValidityPeriodStart.IsZero() && sctTime.Before(key.ValidityPeriodStart) { // skip entries that were before ctlog key start time continue } if !key.ValidityPeriodEnd.IsZero() && sctTime.After(key.ValidityPeriodEnd) { // skip entries that were after ctlog key end time continue } for _, chain := range chains { fulcioChain := make([]*ctx509.Certificate, len(leafCTCert)) copy(fulcioChain, leafCTCert) if len(chain) < 2 { continue } parentCert := chain[1].Raw fulcioIssuer, err := ctx509.ParseCertificates(parentCert) if err != nil { continue } fulcioChain = append(fulcioChain, fulcioIssuer...) err = ctutil.VerifySCT(key.PublicKey, fulcioChain, sct, true) if err == nil { verified++ } } } if verified < threshold { return fmt.Errorf("only able to verify %d SCT entries; unable to meet threshold of %d", verified, threshold) } return nil } sigstore-go-0.7.1/pkg/verify/sct_test.go000066400000000000000000000340121477477521700202520ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package verify import ( "crypto" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "encoding/hex" "math/big" "testing" "time" ct "github.com/google/certificate-transparency-go" "github.com/google/certificate-transparency-go/tls" "github.com/google/certificate-transparency-go/trillian/ctfe" ctx509 "github.com/google/certificate-transparency-go/x509" ctx509util "github.com/google/certificate-transparency-go/x509util" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore/pkg/cryptoutils" "github.com/stretchr/testify/assert" ) func TestVerifySignedCertificateTimestamp(t *testing.T) { privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatal(err) } anotherPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatal(err) } skid, err := cryptoutils.SKID(&privateKey.PublicKey) if err != nil { t.Fatal(err) } anotherSKID, err := cryptoutils.SKID(&anotherPrivateKey.PublicKey) if err != nil { t.Fatal(err) } caCert := createBaseCert(t, privateKey, skid, big.NewInt(1)) anotherCACert := createBaseCert(t, anotherPrivateKey, anotherSKID, big.NewInt(99)) logID, err := ctfe.GetCTLogID(&privateKey.PublicKey) if err != nil { t.Fatal(err) } tests := []struct { name string getCertFn func() *x509.Certificate chain []*x509.Certificate // only intermediate and root threshold int trustedMaterial root.TrustedMaterial wantErr bool }{ { name: "missing sct in cert", getCertFn: func() *x509.Certificate { return createBaseCert(t, privateKey, skid, big.NewInt(1)) }, chain: []*x509.Certificate{}, threshold: 1, trustedMaterial: &fakeTrustedMaterial{}, wantErr: true, }, { name: "sct missing from ct logs", getCertFn: func() *x509.Certificate { return embedSCTs(t, privateKey, skid, createBaseCert(t, privateKey, skid, big.NewInt(1)), []ct.SignedCertificateTimestamp{{ SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: logID}, }}) }, chain: []*x509.Certificate{}, threshold: 1, trustedMaterial: &fakeTrustedMaterial{ transparencyLog: map[string]*root.TransparencyLog{}, }, wantErr: true, }, { name: "missing fulcio CAs", getCertFn: func() *x509.Certificate { return embedSCTs(t, privateKey, skid, createBaseCert(t, privateKey, skid, big.NewInt(1)), []ct.SignedCertificateTimestamp{{ SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: logID}, }}) }, chain: []*x509.Certificate{}, threshold: 1, trustedMaterial: &fakeTrustedMaterial{ transparencyLog: map[string]*root.TransparencyLog{ hex.EncodeToString(logID[:]): {}, }, }, wantErr: true, }, { name: "one valid sct", getCertFn: func() *x509.Certificate { return embedSCTs(t, privateKey, skid, createBaseCert(t, privateKey, skid, big.NewInt(1)), []ct.SignedCertificateTimestamp{{ SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: logID}, }}) }, chain: []*x509.Certificate{caCert}, threshold: 1, trustedMaterial: &fakeTrustedMaterial{ transparencyLog: map[string]*root.TransparencyLog{ hex.EncodeToString(logID[:]): { PublicKey: &privateKey.PublicKey, ValidityPeriodStart: time.UnixMilli(12344), ValidityPeriodEnd: time.UnixMilli(12346), }, }, cas: []root.CertificateAuthority{ &root.FulcioCertificateAuthority{ Root: caCert, }, }, }, }, { name: "one invalid sct", getCertFn: func() *x509.Certificate { return embedSCTs(t, privateKey, skid, createBaseCert(t, privateKey, skid, big.NewInt(1)), []ct.SignedCertificateTimestamp{ { SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: [32]byte{1, 2, 3, 4}}, }, }) }, chain: []*x509.Certificate{caCert}, threshold: 1, trustedMaterial: &fakeTrustedMaterial{ transparencyLog: map[string]*root.TransparencyLog{ hex.EncodeToString(logID[:]): { PublicKey: &privateKey.PublicKey, }, }, cas: []root.CertificateAuthority{ &root.FulcioCertificateAuthority{ Root: caCert, }, }, }, wantErr: true, }, { name: "one valid sct out of multiple invalid scts", getCertFn: func() *x509.Certificate { return embedSCTs(t, privateKey, skid, createBaseCert(t, privateKey, skid, big.NewInt(1)), []ct.SignedCertificateTimestamp{ { SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: logID}, }, { SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: [32]byte{1, 2, 3, 4}}, }, }) }, chain: []*x509.Certificate{caCert}, threshold: 1, trustedMaterial: &fakeTrustedMaterial{ transparencyLog: map[string]*root.TransparencyLog{ hex.EncodeToString(logID[:]): { PublicKey: &privateKey.PublicKey, }, }, cas: []root.CertificateAuthority{ &root.FulcioCertificateAuthority{ Root: caCert, }, }, }, }, { name: "threshold of 2 with only 1 valid sct", getCertFn: func() *x509.Certificate { return embedSCTs(t, privateKey, skid, createBaseCert(t, privateKey, skid, big.NewInt(1)), []ct.SignedCertificateTimestamp{ { SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: logID}, }, { SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: [32]byte{1, 2, 3, 4}}, }, }) }, chain: []*x509.Certificate{caCert}, threshold: 2, trustedMaterial: &fakeTrustedMaterial{ transparencyLog: map[string]*root.TransparencyLog{ hex.EncodeToString(logID[:]): { PublicKey: &privateKey.PublicKey, }, }, cas: []root.CertificateAuthority{ &root.FulcioCertificateAuthority{ Root: caCert, }, }, }, wantErr: true, }, { name: "no valid scts out of multiple", getCertFn: func() *x509.Certificate { return embedSCTs(t, privateKey, skid, createBaseCert(t, privateKey, skid, big.NewInt(1)), []ct.SignedCertificateTimestamp{ { SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: [32]byte{0, 1, 2, 3}}, }, { SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: [32]byte{4, 5, 6, 7}}, }, }) }, chain: []*x509.Certificate{caCert}, threshold: 1, trustedMaterial: &fakeTrustedMaterial{ transparencyLog: map[string]*root.TransparencyLog{ hex.EncodeToString(logID[:]): { PublicKey: &privateKey.PublicKey, }, }, cas: []root.CertificateAuthority{ &root.FulcioCertificateAuthority{ Root: caCert, }, }, }, wantErr: true, }, { name: "fulcio CA has intermediates", getCertFn: func() *x509.Certificate { return embedSCTs(t, privateKey, skid, createBaseCert(t, privateKey, skid, big.NewInt(1)), []ct.SignedCertificateTimestamp{{ SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: logID}, }}) }, chain: []*x509.Certificate{caCert, caCert}, threshold: 1, trustedMaterial: &fakeTrustedMaterial{ transparencyLog: map[string]*root.TransparencyLog{ hex.EncodeToString(logID[:]): { PublicKey: &privateKey.PublicKey, }, }, cas: []root.CertificateAuthority{ &root.FulcioCertificateAuthority{ Root: caCert, Intermediates: []*x509.Certificate{ caCert, }, }, }, }, }, { name: "no valid fulcio CAs", getCertFn: func() *x509.Certificate { return embedSCTs(t, privateKey, skid, createBaseCert(t, privateKey, skid, big.NewInt(1)), []ct.SignedCertificateTimestamp{{ SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: logID}, }}) }, chain: []*x509.Certificate{anotherCACert}, threshold: 1, trustedMaterial: &fakeTrustedMaterial{ transparencyLog: map[string]*root.TransparencyLog{ hex.EncodeToString(logID[:]): { PublicKey: &privateKey.PublicKey, }, }, cas: []root.CertificateAuthority{ &root.FulcioCertificateAuthority{ Root: anotherCACert, }, }, }, wantErr: true, }, { name: "threshold of 0", getCertFn: func() *x509.Certificate { return createBaseCert(t, privateKey, skid, big.NewInt(1)) }, chain: []*x509.Certificate{}, threshold: 0, trustedMaterial: &fakeTrustedMaterial{}, }, { name: "sct not valid for ctlog key time range", getCertFn: func() *x509.Certificate { return embedSCTs(t, privateKey, skid, createBaseCert(t, privateKey, skid, big.NewInt(1)), []ct.SignedCertificateTimestamp{ { SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: logID}, }, }) }, chain: []*x509.Certificate{caCert}, threshold: 1, trustedMaterial: &fakeTrustedMaterial{ transparencyLog: map[string]*root.TransparencyLog{ hex.EncodeToString(logID[:]): { PublicKey: &privateKey.PublicKey, ValidityPeriodStart: time.UnixMilli(10), ValidityPeriodEnd: time.UnixMilli(10000), }, }, cas: []root.CertificateAuthority{ &root.FulcioCertificateAuthority{ Root: caCert, }, }, }, wantErr: true, }, { name: "threshold of 2 with 2 valid scts", getCertFn: func() *x509.Certificate { return embedSCTs(t, privateKey, skid, createBaseCert(t, privateKey, skid, big.NewInt(1)), []ct.SignedCertificateTimestamp{ { SCTVersion: ct.V1, Timestamp: 12345, LogID: ct.LogID{KeyID: logID}, }, { SCTVersion: ct.V1, Timestamp: 99, LogID: ct.LogID{KeyID: logID}, }, }) }, chain: []*x509.Certificate{caCert}, threshold: 2, trustedMaterial: &fakeTrustedMaterial{ transparencyLog: map[string]*root.TransparencyLog{ hex.EncodeToString(logID[:]): { PublicKey: &privateKey.PublicKey, }, }, cas: []root.CertificateAuthority{ &root.FulcioCertificateAuthority{ Root: caCert, }, }, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { chain := []*x509.Certificate{test.getCertFn()} chain = append(chain, test.chain...) err = VerifySignedCertificateTimestamp([][]*x509.Certificate{chain}, test.threshold, test.trustedMaterial) if test.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } func createBaseCert(t *testing.T, privateKey *rsa.PrivateKey, skid []byte, serialNumber *big.Int) *x509.Certificate { cert := &x509.Certificate{ SerialNumber: serialNumber, SubjectKeyId: skid, } certDERBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &privateKey.PublicKey, privateKey) if err != nil { t.Fatal(err) } parsedCert, err := x509.ParseCertificate(certDERBytes) if err != nil { t.Fatal(err) } return parsedCert } func embedSCTs(t *testing.T, privateKey *rsa.PrivateKey, skid []byte, preCert *x509.Certificate, sctInput []ct.SignedCertificateTimestamp) *x509.Certificate { scts := make([]*ct.SignedCertificateTimestamp, 0) for _, s := range sctInput { logEntry := ct.LogEntry{ Leaf: ct.MerkleTreeLeaf{ Version: ct.V1, LeafType: ct.TimestampedEntryLeafType, TimestampedEntry: &ct.TimestampedEntry{ Timestamp: s.Timestamp, EntryType: ct.PrecertLogEntryType, PrecertEntry: &ct.PreCert{ IssuerKeyHash: sha256.Sum256(preCert.RawSubjectPublicKeyInfo), TBSCertificate: preCert.RawTBSCertificate, }, }, }, } data, err := ct.SerializeSCTSignatureInput(s, logEntry) if err != nil { t.Fatal(err) } h := sha256.Sum256(data) signature, err := privateKey.Sign(rand.Reader, h[:], crypto.SHA256) if err != nil { t.Fatal(err) } sct := ct.SignedCertificateTimestamp{ SCTVersion: s.SCTVersion, LogID: s.LogID, Timestamp: s.Timestamp, Signature: ct.DigitallySigned{ Algorithm: tls.SignatureAndHashAlgorithm{ Hash: tls.SHA256, Signature: tls.RSA, }, Signature: signature, }, } scts = append(scts, &sct) } sctList, err := ctx509util.MarshalSCTsIntoSCTList(scts) if err != nil { t.Fatal(err) } sctBytes, err := tls.Marshal(*sctList) if err != nil { t.Fatal(err) } asnSCT, err := asn1.Marshal(sctBytes) if err != nil { t.Fatal(err) } cert := &x509.Certificate{ SerialNumber: preCert.SerialNumber, SubjectKeyId: skid, ExtraExtensions: []pkix.Extension{ { Id: asn1.ObjectIdentifier(ctx509.OIDExtensionCTSCT), Value: asnSCT, }, }, } certDERBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &privateKey.PublicKey, privateKey) if err != nil { t.Fatal(err) } parsedCert, err := x509.ParseCertificate(certDERBytes) if err != nil { t.Fatal(err) } return parsedCert } type fakeTrustedMaterial struct { transparencyLog map[string]*root.TransparencyLog cas []root.CertificateAuthority } func (t *fakeTrustedMaterial) CTLogs() map[string]*root.TransparencyLog { return t.transparencyLog } func (t *fakeTrustedMaterial) FulcioCertificateAuthorities() []root.CertificateAuthority { return t.cas } func (t *fakeTrustedMaterial) TimestampingAuthorities() []root.TimestampingAuthority { panic("not implemented") } func (t *fakeTrustedMaterial) RekorLogs() map[string]*root.TransparencyLog { panic("not implemented") } func (t *fakeTrustedMaterial) PublicKeyVerifier(string) (root.TimeConstrainedVerifier, error) { panic("not implemented") } sigstore-go-0.7.1/pkg/verify/signature.go000066400000000000000000000325021477477521700204250ustar00rootroot00000000000000// 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 verify import ( "bytes" "context" "crypto" "encoding/hex" "errors" "fmt" "hash" "io" "slices" in_toto "github.com/in-toto/attestation/go/v1" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore/pkg/signature" sigdsse "github.com/sigstore/sigstore/pkg/signature/dsse" "github.com/sigstore/sigstore/pkg/signature/options" ) const maxAllowedSubjects = 1024 const maxAllowedSubjectDigests = 32 var ErrDSSEInvalidSignatureCount = errors.New("exactly one signature is required") func VerifySignature(sigContent SignatureContent, verificationContent VerificationContent, trustedMaterial root.TrustedMaterial) error { // nolint: revive var verifier signature.Verifier var err error verifier, err = getSignatureVerifier(verificationContent, trustedMaterial) if err != nil { return fmt.Errorf("could not load signature verifier: %w", err) } if envelope := sigContent.EnvelopeContent(); envelope != nil { return verifyEnvelope(verifier, envelope) } else if msg := sigContent.MessageSignatureContent(); msg != nil { return errors.New("artifact must be provided to verify message signature") } // handle an invalid signature content message return fmt.Errorf("signature content has neither an envelope or a message") } func VerifySignatureWithArtifacts(sigContent SignatureContent, verificationContent VerificationContent, trustedMaterial root.TrustedMaterial, artifacts []io.Reader) error { // nolint: revive verifier, err := getSignatureVerifier(verificationContent, trustedMaterial) if err != nil { return fmt.Errorf("could not load signature verifier: %w", err) } envelope := sigContent.EnvelopeContent() msg := sigContent.MessageSignatureContent() if envelope == nil && msg == nil { return fmt.Errorf("signature content has neither an envelope or a message") } // If there is only one artifact and no envelope, // attempt to verify the message signature with the artifact. if envelope == nil { if len(artifacts) != 1 { return fmt.Errorf("only one artifact can be verified with a message signature") } return verifyMessageSignature(verifier, msg, artifacts[0]) } // Otherwise, verify the envelope with the provided artifacts return verifyEnvelopeWithArtifacts(verifier, envelope, artifacts) } func VerifySignatureWithArtifactDigests(sigContent SignatureContent, verificationContent VerificationContent, trustedMaterial root.TrustedMaterial, digests []ArtifactDigest) error { // nolint: revive verifier, err := getSignatureVerifier(verificationContent, trustedMaterial) if err != nil { return fmt.Errorf("could not load signature verifier: %w", err) } envelope := sigContent.EnvelopeContent() msg := sigContent.MessageSignatureContent() if envelope == nil && msg == nil { return fmt.Errorf("signature content has neither an envelope or a message") } // If there is only one artifact and no envelope, // attempt to verify the message signature with the artifact. if envelope == nil { if len(digests) != 1 { return fmt.Errorf("only one artifact can be verified with a message signature") } return verifyMessageSignatureWithArtifactDigest(verifier, msg, digests[0].Digest) } return verifyEnvelopeWithArtifactDigests(verifier, envelope, digests) } func getSignatureVerifier(verificationContent VerificationContent, tm root.TrustedMaterial) (signature.Verifier, error) { if leafCert := verificationContent.Certificate(); leafCert != nil { // TODO: Inspect certificate's SignatureAlgorithm to determine hash function return signature.LoadVerifier(leafCert.PublicKey, crypto.SHA256) } else if pk := verificationContent.PublicKey(); pk != nil { return tm.PublicKeyVerifier(pk.Hint()) } return nil, fmt.Errorf("no public key or certificate found") } func verifyEnvelope(verifier signature.Verifier, envelope EnvelopeContent) error { dsseEnv := envelope.RawEnvelope() // A DSSE envelope in a Sigstore bundle MUST only contain one // signature, even though DSSE is more permissive. if len(dsseEnv.Signatures) != 1 { return ErrDSSEInvalidSignatureCount } pub, err := verifier.PublicKey() if err != nil { return fmt.Errorf("could not fetch verifier public key: %w", err) } envVerifier, err := dsse.NewEnvelopeVerifier(&sigdsse.VerifierAdapter{ SignatureVerifier: verifier, Pub: pub, }) if err != nil { return fmt.Errorf("could not load envelope verifier: %w", err) } _, err = envVerifier.Verify(context.TODO(), dsseEnv) if err != nil { return fmt.Errorf("could not verify envelope: %w", err) } return nil } func verifyEnvelopeWithArtifacts(verifier signature.Verifier, envelope EnvelopeContent, artifacts []io.Reader) error { if err := verifyEnvelope(verifier, envelope); err != nil { return err } statement, err := envelope.Statement() if err != nil { return fmt.Errorf("could not verify artifact: unable to extract statement from envelope: %w", err) } if err = limitSubjects(statement); err != nil { return err } // Sanity check (no subjects) if len(statement.Subject) == 0 { return errors.New("no subjects found in statement") } // determine which hash functions to use hashFuncs, err := getHashFunctions(statement) if err != nil { return fmt.Errorf("unable to determine hash functions: %w", err) } hashedArtifacts := make([]map[crypto.Hash][]byte, len(artifacts)) for i, artifact := range artifacts { // Compute digest of the artifact. hasher, err := newMultihasher(hashFuncs) if err != nil { return fmt.Errorf("could not verify artifact: unable to create hasher: %w", err) } if _, err = io.Copy(hasher, artifact); err != nil { return fmt.Errorf("could not verify artifact: unable to calculate digest: %w", err) } hashedArtifacts[i] = hasher.Sum(nil) } // create a map based on the digests present in the statement // the map key is the hash algorithm and the field is a slice of digests // created using that hash algorithm subjectDigests := make(map[crypto.Hash][][]byte) for _, subject := range statement.Subject { for alg, hexdigest := range subject.Digest { hf, err := algStringToHashFunc(alg) if err != nil { continue } if _, ok := subjectDigests[hf]; !ok { subjectDigests[hf] = make([][]byte, 0) } digest, err := hex.DecodeString(hexdigest) if err != nil { continue } subjectDigests[hf] = append(subjectDigests[hf], digest) } } // now loop over the provided artifact digests and try to compare them // to the mapped subject digests // if we cannot find a match, exit with an error for _, ha := range hashedArtifacts { matchFound := false for key, value := range ha { statementDigests, ok := subjectDigests[key] if !ok { return fmt.Errorf("no matching artifact hash algorithm found in subject digests") } if ok := isDigestInSlice(value, statementDigests); ok { matchFound = true break } } if !matchFound { return fmt.Errorf("provided artifact digests do not match digests in statement") } } return nil } func verifyEnvelopeWithArtifactDigests(verifier signature.Verifier, envelope EnvelopeContent, digests []ArtifactDigest) error { if err := verifyEnvelope(verifier, envelope); err != nil { return err } statement, err := envelope.Statement() if err != nil { return fmt.Errorf("could not verify artifact: unable to extract statement from envelope: %w", err) } if err = limitSubjects(statement); err != nil { return err } // create a map based on the digests present in the statement // the map key is the hash algorithm and the field is a slice of digests // created using that hash algorithm subjectDigests := make(map[string][][]byte) for _, subject := range statement.Subject { for alg, digest := range subject.Digest { if _, ok := subjectDigests[alg]; !ok { subjectDigests[alg] = make([][]byte, 0) } hexdigest, err := hex.DecodeString(digest) if err != nil { return fmt.Errorf("could not verify artifact: unable to decode subject digest: %w", err) } subjectDigests[alg] = append(subjectDigests[alg], hexdigest) } } // now loop over the provided artifact digests and compare them to the mapped subject digests // if we cannot find a match, exit with an error for _, artifactDigest := range digests { statementDigests, ok := subjectDigests[artifactDigest.Algorithm] if !ok { return fmt.Errorf("provided artifact digests does not match digests in statement") } if ok := isDigestInSlice(artifactDigest.Digest, statementDigests); !ok { return fmt.Errorf("provided artifact digest does not match any digest in statement") } } return nil } func isDigestInSlice(digest []byte, digestSlice [][]byte) bool { for _, el := range digestSlice { if bytes.Equal(digest, el) { return true } } return false } func verifyMessageSignature(verifier signature.Verifier, msg MessageSignatureContent, artifact io.Reader) error { err := verifier.VerifySignature(bytes.NewReader(msg.Signature()), artifact) if err != nil { return fmt.Errorf("could not verify message: %w", err) } return nil } func verifyMessageSignatureWithArtifactDigest(verifier signature.Verifier, msg MessageSignatureContent, artifactDigest []byte) error { if !bytes.Equal(artifactDigest, msg.Digest()) { return errors.New("artifact does not match digest") } if _, ok := verifier.(*signature.ED25519Verifier); ok { return errors.New("message signatures with ed25519 signatures can only be verified with artifacts, and not just their digest") } err := verifier.VerifySignature(bytes.NewReader(msg.Signature()), bytes.NewReader([]byte{}), options.WithDigest(artifactDigest)) if err != nil { return fmt.Errorf("could not verify message: %w", err) } return nil } // limitSubjects limits the number of subjects and digests in a statement to prevent DoS. func limitSubjects(statement *in_toto.Statement) error { if len(statement.Subject) > maxAllowedSubjects { return fmt.Errorf("too many subjects: %d > %d", len(statement.Subject), maxAllowedSubjects) } for _, subject := range statement.Subject { // limit the number of digests too if len(subject.Digest) > maxAllowedSubjectDigests { return fmt.Errorf("too many digests: %d > %d", len(subject.Digest), maxAllowedSubjectDigests) } } return nil } type multihasher struct { io.Writer hashfuncs []crypto.Hash hashes []io.Writer } func newMultihasher(hashfuncs []crypto.Hash) (*multihasher, error) { if len(hashfuncs) == 0 { return nil, errors.New("no hash functions specified") } hashes := make([]io.Writer, len(hashfuncs)) for i := range hashfuncs { hashes[i] = hashfuncs[i].New() } return &multihasher{ Writer: io.MultiWriter(hashes...), hashfuncs: hashfuncs, hashes: hashes, }, nil } func (m *multihasher) Sum(b []byte) map[crypto.Hash][]byte { sums := make(map[crypto.Hash][]byte, len(m.hashes)) for i := range m.hashes { sums[m.hashfuncs[i]] = m.hashes[i].(hash.Hash).Sum(b) } return sums } func algStringToHashFunc(alg string) (crypto.Hash, error) { switch alg { case "sha256": return crypto.SHA256, nil case "sha384": return crypto.SHA384, nil case "sha512": return crypto.SHA512, nil default: return 0, errors.New("unsupported digest algorithm") } } // getHashFunctions returns the smallest subset of supported hash functions // that are needed to verify all subjects in a statement. func getHashFunctions(statement *in_toto.Statement) ([]crypto.Hash, error) { if len(statement.Subject) == 0 { return nil, errors.New("no subjects found in statement") } supportedHashFuncs := []crypto.Hash{crypto.SHA512, crypto.SHA384, crypto.SHA256} chosenHashFuncs := make([]crypto.Hash, 0, len(supportedHashFuncs)) subjectHashFuncs := make([][]crypto.Hash, len(statement.Subject)) // go through the statement and make a simple data structure to hold the // list of hash funcs for each subject (subjectHashFuncs) for i, subject := range statement.Subject { for alg := range subject.Digest { hf, err := algStringToHashFunc(alg) if err != nil { continue } subjectHashFuncs[i] = append(subjectHashFuncs[i], hf) } } // for each subject, see if we have chosen a compatible hash func, and if // not, add the first one that is supported for _, hfs := range subjectHashFuncs { // if any of the hash funcs are already in chosenHashFuncs, skip if len(intersection(hfs, chosenHashFuncs)) > 0 { continue } // check each supported hash func and add it if the subject // has a digest for it for _, hf := range supportedHashFuncs { if slices.Contains(hfs, hf) { chosenHashFuncs = append(chosenHashFuncs, hf) break } } } if len(chosenHashFuncs) == 0 { return nil, errors.New("no supported digest algorithms found") } return chosenHashFuncs, nil } func intersection(a, b []crypto.Hash) []crypto.Hash { var result []crypto.Hash for _, x := range a { if slices.Contains(b, x) { result = append(result, x) } } return result } sigstore-go-0.7.1/pkg/verify/signature_internal_test.go000066400000000000000000000100271477477521700233560ustar00rootroot00000000000000// Copyright 2024 The Sigstore Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package verify import ( "crypto" "crypto/sha256" "crypto/sha512" "testing" in_toto "github.com/in-toto/attestation/go/v1" "github.com/stretchr/testify/assert" ) func TestMultiHasher(t *testing.T) { testBytes := []byte("Hello, world!") hash256 := sha256.Sum256(testBytes) hash384 := sha512.Sum384(testBytes) hash512 := sha512.Sum512(testBytes) for _, tc := range []struct { name string hashes []crypto.Hash output map[crypto.Hash][]byte err bool }{ { name: "one hash", hashes: []crypto.Hash{crypto.SHA256}, output: map[crypto.Hash][]byte{ crypto.SHA256: hash256[:], }, }, { name: "two hashes", hashes: []crypto.Hash{crypto.SHA256, crypto.SHA512}, output: map[crypto.Hash][]byte{ crypto.SHA256: hash256[:], crypto.SHA512: hash512[:], }, }, { name: "three hashes", hashes: []crypto.Hash{crypto.SHA256, crypto.SHA384, crypto.SHA512}, output: map[crypto.Hash][]byte{ crypto.SHA256: hash256[:], crypto.SHA384: hash384[:], crypto.SHA512: hash512[:], }, }, { name: "no hashes", hashes: []crypto.Hash{}, output: nil, err: true, }, } { t.Run(tc.name, func(t *testing.T) { hasher, err := newMultihasher(tc.hashes) if tc.err { assert.Error(t, err) return } assert.NoError(t, err) _, err = hasher.Write(testBytes) assert.NoError(t, err) hashes := hasher.Sum(nil) assert.EqualValues(t, tc.output, hashes) assert.Equal(t, len(tc.hashes), len(hashes)) for _, hash := range tc.hashes { assert.EqualValues(t, tc.output[hash], hashes[hash]) } }) } } func makeStatement(subjectalgs [][]string) *in_toto.Statement { statement := &in_toto.Statement{ Subject: make([]*in_toto.ResourceDescriptor, len(subjectalgs)), } for i, subjectAlg := range subjectalgs { statement.Subject[i] = &in_toto.ResourceDescriptor{ Digest: make(map[string]string), } for _, digest := range subjectAlg { // content of digest doesn't matter for this test statement.Subject[i].Digest[digest] = "foobar" } } return statement } func TestGetHashFunctions(t *testing.T) { for _, test := range []struct { name string algs [][]string expectOutput []crypto.Hash expectError bool }{ { name: "choose strongest algorithm", algs: [][]string{{"sha256", "sha512"}}, expectOutput: []crypto.Hash{crypto.SHA512}, }, { name: "choose both algorithms", algs: [][]string{{"sha256"}, {"sha512"}}, expectOutput: []crypto.Hash{crypto.SHA256, crypto.SHA512}, }, { name: "choose one algorithm", algs: [][]string{{"sha512"}, {"sha256", "sha512"}}, expectOutput: []crypto.Hash{crypto.SHA512}, }, { name: "choose two algorithms", algs: [][]string{{"sha256", "sha512"}, {"sha384", "sha512"}, {"sha256", "sha384"}}, expectOutput: []crypto.Hash{crypto.SHA512, crypto.SHA384}, }, { name: "ignore unknown algorithm", algs: [][]string{{"md5", "sha512"}, {"sha256", "sha512"}}, expectOutput: []crypto.Hash{crypto.SHA512}, }, { name: "no recognized algorithms", algs: [][]string{{"md5"}, {"sha1"}}, expectError: true, }, } { t.Run(test.name, func(t *testing.T) { statement := makeStatement(test.algs) hfs, err := getHashFunctions(statement) if test.expectError { assert.Error(t, err) } else { assert.NoError(t, err) } assert.Equal(t, test.expectOutput, hfs) }) } } sigstore-go-0.7.1/pkg/verify/signature_test.go000066400000000000000000000227041477477521700214670ustar00rootroot00000000000000// 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 verify_test import ( "bytes" "crypto/sha256" "crypto/sha512" "encoding/hex" "encoding/json" "fmt" "io" "strings" "testing" "github.com/in-toto/in-toto-golang/in_toto" "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" "github.com/sigstore/sigstore-go/pkg/testing/ca" "github.com/sigstore/sigstore-go/pkg/verify" "github.com/stretchr/testify/assert" ) var SkipArtifactAndIdentitiesPolicy = verify.NewPolicy(verify.WithoutArtifactUnsafe(), verify.WithoutIdentitiesUnsafe()) func TestSignatureVerifier(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) statement := []byte(`{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"customFoo","subject":[{"name":"subject","digest":{"sha256":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}}],"predicate":{}}`) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", statement) assert.NoError(t, err) sigContent, err := entity.SignatureContent() assert.NoError(t, err) verificationContent, err := entity.VerificationContent() assert.NoError(t, err) err = verify.VerifySignature(sigContent, verificationContent, virtualSigstore) assert.NoError(t, err) // should fail to verify with a different signature entity2, err := virtualSigstore.Attest("foo@example.com", "issuer", statement) assert.NoError(t, err) sigContent2, err := entity2.SignatureContent() assert.NoError(t, err) err = verify.VerifySignature(sigContent2, verificationContent, virtualSigstore) assert.Error(t, err) } func TestEnvelopeSubject(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) subjectBody := "Hi, I am a subject!" digest256 := sha256.Sum256([]byte(subjectBody)) digest := digest256[:] digest256hex := hex.EncodeToString(digest) statement := []byte(fmt.Sprintf(`{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"customFoo","subject":[{"name":"subject","digest":{"sha256":"%s"}}],"predicate":{}}`, digest256hex)) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", statement) assert.NoError(t, err) verifier, err := verify.NewSignedEntityVerifier(virtualSigstore, verify.WithTransparencyLog(1), verify.WithSignedTimestamps(1)) assert.NoError(t, err) _, err = verifier.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.NoError(t, err) _, err = verifier.Verify(entity, verify.NewPolicy(verify.WithArtifact(bytes.NewBufferString(subjectBody)), verify.WithoutIdentitiesUnsafe())) assert.NoError(t, err) _, err = verifier.Verify(entity, verify.NewPolicy(verify.WithArtifactDigest("sha256", digest), verify.WithoutIdentitiesUnsafe())) assert.NoError(t, err) // Error: incorrect artifact _, err = verifier.Verify(entity, verify.NewPolicy(verify.WithArtifact(bytes.NewBufferString("Hi, I am a different subject!")), verify.WithoutIdentitiesUnsafe())) assert.Error(t, err) // Error: incorrect digest algorithm _, err = verifier.Verify(entity, verify.NewPolicy(verify.WithArtifactDigest("sha512", digest), verify.WithoutIdentitiesUnsafe())) assert.Error(t, err) } func TestSignatureVerifierMessageSignature(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) artifact := "Hi, I am an artifact!" //nolint:goconst entity, err := virtualSigstore.Sign("foo@example.com", "issuer", []byte(artifact)) assert.NoError(t, err) verifier, err := verify.NewSignedEntityVerifier(virtualSigstore, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) assert.NoError(t, err) result, err := verifier.Verify(entity, verify.NewPolicy(verify.WithArtifact(bytes.NewBufferString(artifact)), verify.WithoutIdentitiesUnsafe())) assert.NoError(t, err) assert.Equal(t, result.Signature.Certificate.SubjectAlternativeName, "foo@example.com") assert.Equal(t, result.VerifiedTimestamps[0].Type, "Tlog") // should fail to verify with a different artifact artifact2 := "Hi, I am a different artifact!" result, err = verifier.Verify(entity, verify.NewPolicy(verify.WithArtifact(bytes.NewBufferString(artifact2)), verify.WithoutIdentitiesUnsafe())) assert.Error(t, err) assert.Nil(t, result) } func TestTooManySubjects(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) tooManySubjectsStatement := in_toto.Statement{} for i := 0; i < 1025; i++ { tooManySubjectsStatement.Subject = append(tooManySubjectsStatement.Subject, in_toto.Subject{ Name: fmt.Sprintf("subject-%d", i), Digest: map[string]string{ "sha256": "", // actual content of digest does not matter for this test }, }) } tooManySubjectsStatementBytes, err := json.Marshal(tooManySubjectsStatement) assert.NoError(t, err) tooManySubjectsEntity, err := virtualSigstore.Attest("foo@example.com", "issuer", tooManySubjectsStatementBytes) assert.NoError(t, err) verifier, err := verify.NewSignedEntityVerifier(virtualSigstore, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) assert.NoError(t, err) artifact := "Hi, I am an artifact!" //nolint:goconst _, err = verifier.Verify(tooManySubjectsEntity, verify.NewPolicy(verify.WithArtifact(bytes.NewBufferString(artifact)), verify.WithoutIdentitiesUnsafe())) assert.ErrorContains(t, err, "too many subjects") } func TestTooManyDigests(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) tooManyDigestsStatement := in_toto.Statement{} tooManyDigestsStatement.Subject = []in_toto.Subject{ { Name: "subject", Digest: make(common.DigestSet), }, } tooManyDigestsStatement.Subject[0].Digest["sha512"] = "" // verifier requires that at least one known hash algorithm is present in the digest map for i := 0; i < 32; i++ { tooManyDigestsStatement.Subject[0].Digest[fmt.Sprintf("digest-%d", i)] = "" } tooManySubjectsStatementBytes, err := json.Marshal(tooManyDigestsStatement) assert.NoError(t, err) tooManySubjectsEntity, err := virtualSigstore.Attest("foo@example.com", "issuer", tooManySubjectsStatementBytes) assert.NoError(t, err) verifier, err := verify.NewSignedEntityVerifier(virtualSigstore, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) assert.NoError(t, err) artifact := "Hi, I am an artifact!" //nolint:goconst _, err = verifier.Verify(tooManySubjectsEntity, verify.NewPolicy(verify.WithArtifact(bytes.NewBufferString(artifact)), verify.WithoutIdentitiesUnsafe())) assert.ErrorContains(t, err, "too many digests") } func TestVerifyEnvelopeWithMultipleArtifactsAndArtifactDigests(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) subjects := make([]in_toto.Subject, 10) artifacts := make([]io.Reader, 10) artifactDigests := make([]verify.ArtifactDigest, 10) // Create ten test subjects for i := range 10 { s := in_toto.Subject{ Name: fmt.Sprintf("subject-%d", i), } subjectBody := fmt.Sprintf("Hi, I am a subject! #%d", i) artifacts[i] = strings.NewReader(subjectBody) // alternate between sha256 and sha512 when creating the digests // so that we can test that the verifier can handle digests created // with different algorithms if i%2 == 0 { digest256 := sha256.Sum256([]byte(subjectBody)) digest := digest256[:] s.Digest = common.DigestSet{ "sha256": hex.EncodeToString(digest), } a := verify.ArtifactDigest{ Algorithm: "sha256", Digest: digest, } artifactDigests[i] = a } else { digest512 := sha512.Sum512([]byte(subjectBody)) digest := digest512[:] s.Digest = common.DigestSet{ "sha512": hex.EncodeToString(digest), } a := verify.ArtifactDigest{ Algorithm: "sha512", Digest: digest, } artifactDigests[i] = a } subjects[i] = s } jsonSubjects, err := json.Marshal(subjects) assert.NoError(t, err) statement := []byte(fmt.Sprintf(`{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"customFoo","subject":%s,"predicate":{}}`, string(jsonSubjects))) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", statement) assert.NoError(t, err) verifier, err := verify.NewSignedEntityVerifier(virtualSigstore, verify.WithTransparencyLog(1), verify.WithSignedTimestamps(1)) assert.NoError(t, err) _, err = verifier.Verify(entity, verify.NewPolicy(verify.WithArtifacts(artifacts), verify.WithoutIdentitiesUnsafe())) assert.NoError(t, err) _, err = verifier.Verify(entity, verify.NewPolicy(verify.WithArtifactDigests(artifactDigests), verify.WithoutIdentitiesUnsafe())) assert.NoError(t, err) noMatchingArtifacts := []io.Reader{strings.NewReader("some other artifact")} _, err = verifier.Verify(entity, verify.NewPolicy(verify.WithArtifacts(noMatchingArtifacts), verify.WithoutIdentitiesUnsafe())) assert.Error(t, err) noMatchingArtifactDigests := []verify.ArtifactDigest{ { Algorithm: "sha256", Digest: []byte("some other artifact"), }, } _, err = verifier.Verify(entity, verify.NewPolicy(verify.WithArtifactDigests(noMatchingArtifactDigests), verify.WithoutIdentitiesUnsafe())) assert.Error(t, err) } sigstore-go-0.7.1/pkg/verify/signed_entity.go000066400000000000000000000742231477477521700212770ustar00rootroot00000000000000// 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 verify import ( "crypto/x509" "encoding/asn1" "encoding/json" "errors" "fmt" "io" "time" in_toto "github.com/in-toto/attestation/go/v1" "github.com/sigstore/sigstore-go/pkg/fulcio/certificate" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore/pkg/cryptoutils" "google.golang.org/protobuf/encoding/protojson" ) const ( VerificationResultMediaType01 = "application/vnd.dev.sigstore.verificationresult+json;version=0.1" ) type SignedEntityVerifier struct { trustedMaterial root.TrustedMaterial config VerifierConfig } type VerifierConfig struct { // nolint: revive // requireSignedTimestamps requires RFC3161 timestamps to verify // short-lived certificates requireSignedTimestamps bool // signedTimestampThreshold is the minimum number of verified // RFC3161 timestamps in a bundle signedTimestampThreshold int // requireIntegratedTimestamps requires log entry integrated timestamps to // verify short-lived certificates requireIntegratedTimestamps bool // integratedTimeThreshold is the minimum number of log entry // integrated timestamps in a bundle integratedTimeThreshold int // requireObserverTimestamps requires RFC3161 timestamps and/or log // integrated timestamps to verify short-lived certificates requireObserverTimestamps bool // observerTimestampThreshold is the minimum number of verified // RFC3161 timestamps and/or log integrated timestamps in a bundle observerTimestampThreshold int // requireTlogEntries requires log inclusion proofs in a bundle requireTlogEntries bool // tlogEntriesThreshold is the minimum number of verified inclusion // proofs in a bundle tlogEntriesThreshold int // requireSCTs requires SCTs in Fulcio certificates requireSCTs bool // ctlogEntriesTreshold is the minimum number of verified SCTs in // a Fulcio certificate ctlogEntriesThreshold int // useCurrentTime uses the current time rather than a provided signed // or log timestamp. Most workflows will not use this option useCurrentTime bool } type VerifierOption func(*VerifierConfig) error // NewSignedEntityVerifier creates a new SignedEntityVerifier. It takes a // root.TrustedMaterial, which contains a set of trusted public keys and // certificates, and a set of VerifierConfigurators, which set the config // that determines the behaviour of the Verify function. // // VerifierConfig's set of options should match the properties of a given // Sigstore deployment, i.e. whether to expect SCTs, Tlog entries, or signed // timestamps. func NewSignedEntityVerifier(trustedMaterial root.TrustedMaterial, options ...VerifierOption) (*SignedEntityVerifier, error) { var err error c := VerifierConfig{} for _, opt := range options { err = opt(&c) if err != nil { return nil, fmt.Errorf("failed to configure verifier: %w", err) } } err = c.Validate() if err != nil { return nil, err } v := &SignedEntityVerifier{ trustedMaterial: trustedMaterial, config: c, } return v, nil } // WithSignedTimestamps configures the SignedEntityVerifier to expect RFC 3161 // timestamps from a Timestamp Authority, verify them using the TrustedMaterial's // TimestampingAuthorities(), and, if it exists, use the resulting timestamp(s) // to verify the Fulcio certificate. func WithSignedTimestamps(threshold int) VerifierOption { return func(c *VerifierConfig) error { if threshold < 1 { return errors.New("signed timestamp threshold must be at least 1") } c.requireSignedTimestamps = true c.signedTimestampThreshold = threshold return nil } } // WithObserverTimestamps configures the SignedEntityVerifier to expect // timestamps from either an RFC3161 timestamp authority or a log's // SignedEntryTimestamp. These are verified using the TrustedMaterial's // TimestampingAuthorities() or RekorLogs(), and used to verify // the Fulcio certificate. func WithObserverTimestamps(threshold int) VerifierOption { return func(c *VerifierConfig) error { if threshold < 1 { return errors.New("observer timestamp threshold must be at least 1") } c.requireObserverTimestamps = true c.observerTimestampThreshold = threshold return nil } } // WithTransparencyLog configures the SignedEntityVerifier to expect // Transparency Log inclusion proofs or SignedEntryTimestamps, verifying them // using the TrustedMaterial's RekorLogs(). func WithTransparencyLog(threshold int) VerifierOption { return func(c *VerifierConfig) error { if threshold < 1 { return errors.New("transparency log entry threshold must be at least 1") } c.requireTlogEntries = true c.tlogEntriesThreshold = threshold return nil } } // WithIntegratedTimestamps configures the SignedEntityVerifier to // expect log entry integrated timestamps from either SignedEntryTimestamps // or live log lookups. func WithIntegratedTimestamps(threshold int) VerifierOption { return func(c *VerifierConfig) error { c.requireIntegratedTimestamps = true c.integratedTimeThreshold = threshold return nil } } // WithSignedCertificateTimestamps configures the SignedEntityVerifier to // expect the Fulcio certificate to have a SignedCertificateTimestamp, and // verify it using the TrustedMaterial's CTLogAuthorities(). func WithSignedCertificateTimestamps(threshold int) VerifierOption { return func(c *VerifierConfig) error { if threshold < 1 { return errors.New("ctlog entry threshold must be at least 1") } c.requireSCTs = true c.ctlogEntriesThreshold = threshold return nil } } // WithCurrentTime configures the SignedEntityVerifier to not expect // any timestamps from either a Timestamp Authority or a Transparency Log. // This option should not be enabled when verifying short-lived certificates, // as an observer timestamp is needed. This option is useful primarily for // private deployments with long-lived code signing certificates. func WithCurrentTime() VerifierOption { return func(c *VerifierConfig) error { c.useCurrentTime = true return nil } } func (c *VerifierConfig) Validate() error { if !c.requireObserverTimestamps && !c.requireSignedTimestamps && !c.requireIntegratedTimestamps && !c.useCurrentTime { return errors.New("when initializing a new SignedEntityVerifier, you must specify at least one of " + "WithObserverTimestamps(), WithSignedTimestamps(), or WithIntegratedTimestamps()") } return nil } type VerificationResult struct { MediaType string `json:"mediaType"` Statement *in_toto.Statement `json:"statement,omitempty"` Signature *SignatureVerificationResult `json:"signature,omitempty"` VerifiedTimestamps []TimestampVerificationResult `json:"verifiedTimestamps"` VerifiedIdentity *CertificateIdentity `json:"verifiedIdentity,omitempty"` } type SignatureVerificationResult struct { PublicKeyID *[]byte `json:"publicKeyId,omitempty"` Certificate *certificate.Summary `json:"certificate,omitempty"` } type TimestampVerificationResult struct { Type string `json:"type"` URI string `json:"uri"` Timestamp time.Time `json:"timestamp"` } func NewVerificationResult() *VerificationResult { return &VerificationResult{ MediaType: VerificationResultMediaType01, } } // MarshalJSON deals with protojson needed for the Statement. // Can be removed when https://github.com/in-toto/attestation/pull/403 is merged. func (b *VerificationResult) MarshalJSON() ([]byte, error) { statement, err := protojson.Marshal(b.Statement) if err != nil { return nil, err } // creating a type alias to avoid infinite recursion, as MarshalJSON is // not copied into the alias. type Alias VerificationResult return json.Marshal(struct { Alias Statement json.RawMessage `json:"statement,omitempty"` }{ Alias: Alias(*b), Statement: statement, }) } func (b *VerificationResult) UnmarshalJSON(data []byte) error { b.Statement = &in_toto.Statement{} type Alias VerificationResult aux := &struct { Alias Statement json.RawMessage `json:"statement,omitempty"` }{ Alias: Alias(*b), } if err := json.Unmarshal(data, aux); err != nil { return err } return protojson.Unmarshal(aux.Statement, b.Statement) } type PolicyOption func(*PolicyConfig) error type ArtifactPolicyOption func(*PolicyConfig) error // PolicyBuilder is responsible for building & validating a PolicyConfig type PolicyBuilder struct { artifactPolicy ArtifactPolicyOption policyOptions []PolicyOption } func (pc PolicyBuilder) options() []PolicyOption { arr := []PolicyOption{PolicyOption(pc.artifactPolicy)} return append(arr, pc.policyOptions...) } func (pc PolicyBuilder) BuildConfig() (*PolicyConfig, error) { var err error policy := &PolicyConfig{} for _, applyOption := range pc.options() { err = applyOption(policy) if err != nil { return nil, err } } if err := policy.validate(); err != nil { return nil, err } return policy, nil } type ArtifactDigest struct { Algorithm string Digest []byte } type PolicyConfig struct { ignoreArtifact bool ignoreIdentities bool requireSigningKey bool certificateIdentities CertificateIdentities verifyArtifacts bool artifacts []io.Reader verifyArtifactDigests bool artifactDigests []ArtifactDigest } func (p *PolicyConfig) withVerifyAlreadyConfigured() error { if p.verifyArtifacts || p.verifyArtifactDigests { return errors.New("only one invocation of WithArtifact/WithArtifacts/WithArtifactDigest/WithArtifactDigests is allowed") } return nil } func (p *PolicyConfig) validate() error { if p.RequireIdentities() && len(p.certificateIdentities) == 0 { return errors.New("can't verify identities without providing at least one identity") } return nil } // RequireArtifact returns true if the Verify algorithm should perform // signature verification with an an artifact provided by either the // WithArtifact or the WithArtifactDigest functions. // // By default, unless explicitly turned off, we should always expect to verify // a SignedEntity's signature using an artifact. Bools are initialized to false, // so this behaviour is therefore controlled by the ignoreArtifact field. // // Double negatives are confusing, though. To aid with comprehension of the // main Verify loop, this function therefore just wraps the double negative. func (p *PolicyConfig) RequireArtifact() bool { return !p.ignoreArtifact } // RequireIdentities returns true if the Verify algorithm should check // whether the SignedEntity's certificate was created by one of the identities // provided by the WithCertificateIdentity function. // // By default, unless explicitly turned off, we should always expect to enforce // that a SignedEntity's certificate was created by an Identity we trust. Bools // are initialized to false, so this behaviour is therefore controlled by the // ignoreIdentities field. // // Double negatives are confusing, though. To aid with comprehension of the // main Verify loop, this function therefore just wraps the double negative. func (p *PolicyConfig) RequireIdentities() bool { return !p.ignoreIdentities } // RequireSigningKey returns true if we expect the SignedEntity to be signed // with a key and not a certificate. func (p *PolicyConfig) RequireSigningKey() bool { return p.requireSigningKey } func NewPolicy(artifactOpt ArtifactPolicyOption, options ...PolicyOption) PolicyBuilder { return PolicyBuilder{artifactPolicy: artifactOpt, policyOptions: options} } // WithoutIdentitiesUnsafe allows the caller of Verify to skip enforcing any // checks on the identity that created the SignedEntity being verified. // // Do not use this option unless you know what you are doing! // // As the name implies, using WithoutIdentitiesUnsafe is not safe: outside of // exceptional circumstances, we should always enforce that the SignedEntity // being verified was signed by a trusted CertificateIdentity. // // For more information, consult WithCertificateIdentity. func WithoutIdentitiesUnsafe() PolicyOption { return func(p *PolicyConfig) error { if len(p.certificateIdentities) > 0 { return errors.New("can't use WithoutIdentitiesUnsafe while specifying CertificateIdentities") } p.ignoreIdentities = true return nil } } // WithCertificateIdentity allows the caller of Verify to enforce that the // SignedEntity being verified was signed by the given identity, as defined by // the Fulcio certificate embedded in the entity. If this policy is enabled, // but the SignedEntity does not have a certificate, verification will fail. // // Providing this function multiple times will concatenate the provided // CertificateIdentity to the list of identities being checked. // // If all of the provided CertificateIdentities fail to match the Fulcio // certificate, then verification will fail. If *any* CertificateIdentity // matches, then verification will succeed. Therefore, each CertificateIdentity // provided to this function must define a "sufficient" identity to trust. // // The CertificateIdentity struct allows callers to specify: // - The exact value, or Regexp, of the SubjectAlternativeName // - The exact value of any Fulcio OID X.509 extension, i.e. Issuer // // For convenience, consult the NewShortCertificateIdentity function. func WithCertificateIdentity(identity CertificateIdentity) PolicyOption { return func(p *PolicyConfig) error { if p.ignoreIdentities { return errors.New("can't use WithCertificateIdentity while using WithoutIdentitiesUnsafe") } if p.requireSigningKey { return errors.New("can't use WithCertificateIdentity while using WithKey") } p.certificateIdentities = append(p.certificateIdentities, identity) return nil } } // WithKey allows the caller of Verify to require the SignedEntity being // verified was signed with a key and not a certificate. func WithKey() PolicyOption { return func(p *PolicyConfig) error { if len(p.certificateIdentities) > 0 { return errors.New("can't use WithKey while using WithCertificateIdentity") } p.requireSigningKey = true p.ignoreIdentities = true return nil } } // WithoutArtifactUnsafe allows the caller of Verify to skip checking whether // the SignedEntity was created from, or references, an artifact. // // WithoutArtifactUnsafe can only be used with SignedEntities that contain a // DSSE envelope. If the the SignedEntity has a MessageSignature, providing // this policy option will cause verification to always fail, since // MessageSignatures can only be verified in the presence of an Artifact or // artifact digest. See WithArtifact/WithArtifactDigest for more informaiton. // // Do not use this function unless you know what you are doing! // // As the name implies, using WithoutArtifactUnsafe is not safe: outside of // exceptional circumstances, SignedEntities should always be verified with // an artifact. func WithoutArtifactUnsafe() ArtifactPolicyOption { return func(p *PolicyConfig) error { if err := p.withVerifyAlreadyConfigured(); err != nil { return err } p.ignoreArtifact = true return nil } } // WithArtifact allows the caller of Verify to enforce that the SignedEntity // being verified was created from, or references, a given artifact. // // If the SignedEntity contains a DSSE envelope, then the artifact digest is // calculated from the given artifact, and compared to the digest in the // envelope's statement. func WithArtifact(artifact io.Reader) ArtifactPolicyOption { return func(p *PolicyConfig) error { if err := p.withVerifyAlreadyConfigured(); err != nil { return err } if p.ignoreArtifact { return errors.New("can't use WithArtifact while using WithoutArtifactUnsafe") } p.verifyArtifacts = true p.artifacts = []io.Reader{artifact} return nil } } // WithArtifacts allows the caller of Verify to enforce that the SignedEntity // being verified was created from, or references, a slice of artifacts. // // If the SignedEntity contains a DSSE envelope, then the artifact digest is // calculated from the given artifact, and compared to the digest in the // envelope's statement. func WithArtifacts(artifacts []io.Reader) ArtifactPolicyOption { return func(p *PolicyConfig) error { if err := p.withVerifyAlreadyConfigured(); err != nil { return err } if p.ignoreArtifact { return errors.New("can't use WithArtifacts while using WithoutArtifactUnsafe") } p.verifyArtifacts = true p.artifacts = artifacts return nil } } // WithArtifactDigest allows the caller of Verify to enforce that the // SignedEntity being verified was created for a given artifact digest. // // If the SignedEntity contains a MessageSignature that was signed using the // ED25519 algorithm, then providing only an artifactDigest will fail; the // whole artifact must be provided. Use WithArtifact instead. // // If the SignedEntity contains a DSSE envelope, then the artifact digest is // compared to the digest in the envelope's statement. func WithArtifactDigest(algorithm string, artifactDigest []byte) ArtifactPolicyOption { return func(p *PolicyConfig) error { if err := p.withVerifyAlreadyConfigured(); err != nil { return err } if p.ignoreArtifact { return errors.New("can't use WithArtifactDigest while using WithoutArtifactUnsafe") } p.verifyArtifactDigests = true p.artifactDigests = []ArtifactDigest{{ Algorithm: algorithm, Digest: artifactDigest, }} return nil } } // WithArtifactDigests allows the caller of Verify to enforce that the // SignedEntity being verified was created for a given array of artifact digests. // // If the SignedEntity contains a DSSE envelope, then the artifact digests // are compared to the digests in the envelope's statement. // // If the SignedEntity does not contain a DSSE envelope, verification fails. func WithArtifactDigests(digests []ArtifactDigest) ArtifactPolicyOption { return func(p *PolicyConfig) error { if err := p.withVerifyAlreadyConfigured(); err != nil { return err } if p.ignoreArtifact { return errors.New("can't use WithArtifactDigests while using WithoutArtifactUnsafe") } p.verifyArtifactDigests = true p.artifactDigests = digests return nil } } // Verify checks the cryptographic integrity of a given SignedEntity according // to the options configured in the NewSignedEntityVerifier. Its purpose is to // determine whether the SignedEntity was created by a Sigstore deployment we // trust, as defined by keys in our TrustedMaterial. // // If the SignedEntity contains a MessageSignature, then the artifact or its // digest must be provided to the Verify function, as it is required to verify // the signature. See WithArtifact and WithArtifactDigest for more details. // // If and only if verification is successful, Verify will return a // VerificationResult struct whose contents' integrity have been verified. // Verify may then verify the contents of the VerificationResults using supplied // PolicyOptions. See WithCertificateIdentity for more details. // // Callers of this function SHOULD ALWAYS: // - (if the signed entity has a certificate) verify that its Subject Alternate // Name matches a trusted identity, and that its OID Issuer field matches an // expected value // - (if the signed entity has a dsse envelope) verify that the envelope's // statement's subject matches the artifact being verified func (v *SignedEntityVerifier) Verify(entity SignedEntity, pb PolicyBuilder) (*VerificationResult, error) { policy, err := pb.BuildConfig() if err != nil { return nil, fmt.Errorf("failed to build policy: %w", err) } // Let's go by the spec: https://docs.google.com/document/d/1kbhK2qyPPk8SLavHzYSDM8-Ueul9_oxIMVFuWMWKz0E/edit#heading=h.g11ovq2s1jxh // > ## Transparency Log Entry verifiedTlogTimestamps, err := v.VerifyTransparencyLogInclusion(entity) if err != nil { return nil, fmt.Errorf("failed to verify log inclusion: %w", err) } // > ## Establishing a Time for the Signature // > First, establish a time for the signature. This timestamp is required to validate the certificate chain, so this step comes first. verifiedTimestamps, err := v.VerifyObserverTimestamps(entity, verifiedTlogTimestamps) if err != nil { return nil, fmt.Errorf("failed to verify timestamps: %w", err) } verificationContent, err := entity.VerificationContent() if err != nil { return nil, fmt.Errorf("failed to fetch verification content: %w", err) } var signedWithCertificate bool var certSummary certificate.Summary // If the bundle was signed with a long-lived key, and does not have a Fulcio certificate, // then skip the certificate verification steps if leafCert := verificationContent.Certificate(); leafCert != nil { if policy.RequireSigningKey() { return nil, errors.New("expected key signature, not certificate") } signedWithCertificate = true // Get the summary before modifying the cert extensions certSummary, err = certificate.SummarizeCertificate(leafCert) if err != nil { return nil, fmt.Errorf("failed to summarize certificate: %w", err) } // From spec: // > ## Certificate // > … // > The Verifier MUST perform certification path validation (RFC 5280 §6) of the certificate chain with the pre-distributed Fulcio root certificate(s) as a trust anchor, but with a fake “current time.” If a timestamp from the timestamping service is available, the Verifier MUST perform path validation using the timestamp from the Timestamping Service. If a timestamp from the Transparency Service is available, the Verifier MUST perform path validation using the timestamp from the Transparency Service. If both are available, the Verifier performs path validation twice. If either fails, verification fails. // Go does not support the OtherName GeneralName SAN extension. If // Fulcio issued the certificate with an OtherName SAN, it will be // handled by SummarizeCertificate above, and it must be removed here // or the X.509 verification will fail. if len(leafCert.UnhandledCriticalExtensions) > 0 { var unhandledExts []asn1.ObjectIdentifier for _, oid := range leafCert.UnhandledCriticalExtensions { if !oid.Equal(cryptoutils.SANOID) { unhandledExts = append(unhandledExts, oid) } } leafCert.UnhandledCriticalExtensions = unhandledExts } var chains [][]*x509.Certificate for _, verifiedTs := range verifiedTimestamps { // verify the leaf certificate against the root chains, err = VerifyLeafCertificate(verifiedTs.Timestamp, leafCert, v.trustedMaterial) if err != nil { return nil, fmt.Errorf("failed to verify leaf certificate: %w", err) } } // From spec: // > Unless performing online verification (see §Alternative Workflows), the Verifier MUST extract the SignedCertificateTimestamp embedded in the leaf certificate, and verify it as in RFC 9162 §8.1.3, using the verification key from the Certificate Transparency Log. if v.config.requireSCTs { err = VerifySignedCertificateTimestamp(chains, v.config.ctlogEntriesThreshold, v.trustedMaterial) if err != nil { return nil, fmt.Errorf("failed to verify signed certificate timestamp: %w", err) } } } // If SCTs are required, ensure the bundle is certificate-signed not public key-signed if v.config.requireSCTs { if verificationContent.PublicKey() != nil { return nil, errors.New("SCTs required but bundle is signed with a public key, which cannot contain SCTs") } } // From spec: // > ## Signature Verification // > The Verifier MUST verify the provided signature for the constructed payload against the key in the leaf of the certificate chain. sigContent, err := entity.SignatureContent() if err != nil { return nil, fmt.Errorf("failed to fetch signature content: %w", err) } if policy.RequireArtifact() { switch { case policy.verifyArtifacts: err = VerifySignatureWithArtifacts(sigContent, verificationContent, v.trustedMaterial, policy.artifacts) case policy.verifyArtifactDigests: err = VerifySignatureWithArtifactDigests(sigContent, verificationContent, v.trustedMaterial, policy.artifactDigests) default: // should never happen, but just in case: err = errors.New("no artifact or artifact digest provided") } } else { // verifying with artifact has been explicitly turned off, so just check // the signature on the dsse envelope: err = VerifySignature(sigContent, verificationContent, v.trustedMaterial) } if err != nil { return nil, fmt.Errorf("failed to verify signature: %w", err) } // Hooray! We've verified all of the entity's constituent parts! 🎉 🥳 // Now we can construct the results object accordingly. result := NewVerificationResult() if signedWithCertificate { result.Signature = &SignatureVerificationResult{ Certificate: &certSummary, } } // SignatureContent can be either an Envelope or a MessageSignature. // If it's an Envelope, let's pop the Statement for our results: if envelope := sigContent.EnvelopeContent(); envelope != nil { stmt, err := envelope.Statement() if err != nil { return nil, fmt.Errorf("failed to fetch envelope statement: %w", err) } result.Statement = stmt } result.VerifiedTimestamps = verifiedTimestamps // Now that the signed entity's crypto material has been verified, and the // result struct has been constructed, we can optionally enforce some // additional policies: // -------------------- // From ## Certificate section, // >The Verifier MUST then check the certificate against the verification policy. Details on how to do this depend on the verification policy, but the Verifier SHOULD check the Issuer X.509 extension (OID 1.3.6.1.4.1.57264.1.1) at a minimum, and will in most cases check the SubjectAlternativeName as well. See Spec: Fulcio §TODO for example checks on the certificate. if policy.RequireIdentities() { if !signedWithCertificate { // We got asked to verify identities, but the entity was not signed with // a certificate. That's a problem! return nil, errors.New("can't verify certificate identities: entity was not signed with a certificate") } if len(policy.certificateIdentities) == 0 { return nil, errors.New("can't verify certificate identities: no identities provided") } matchingCertID, err := policy.certificateIdentities.Verify(certSummary) if err != nil { return nil, fmt.Errorf("failed to verify certificate identity: %w", err) } result.VerifiedIdentity = matchingCertID } return result, nil } // VerifyTransparencyLogInclusion verifies TlogEntries if expected. Optionally returns // a list of verified timestamps from the log integrated timestamps when verifying // with observer timestamps. // TODO: Return a different verification result for logs specifically (also for #48) func (v *SignedEntityVerifier) VerifyTransparencyLogInclusion(entity SignedEntity) ([]TimestampVerificationResult, error) { verifiedTimestamps := []TimestampVerificationResult{} if v.config.requireTlogEntries { // log timestamps should be verified if with WithIntegratedTimestamps or WithObserverTimestamps is used verifiedTlogTimestamps, err := VerifyArtifactTransparencyLog(entity, v.trustedMaterial, v.config.tlogEntriesThreshold, v.config.requireIntegratedTimestamps || v.config.requireObserverTimestamps) if err != nil { return nil, err } for _, vts := range verifiedTlogTimestamps { verifiedTimestamps = append(verifiedTimestamps, TimestampVerificationResult{Type: "Tlog", URI: vts.URI, Timestamp: vts.Time}) } } return verifiedTimestamps, nil } // VerifyObserverTimestamps verifies RFC3161 signed timestamps, and verifies // that timestamp thresholds are met with log entry integrated timestamps, // signed timestamps, or a combination of both. The returned timestamps // can be used to verify short-lived certificates. // logTimestamps may be populated with verified log entry integrated timestamps // In order to be verifiable, a SignedEntity must have at least one verified // "observer timestamp". func (v *SignedEntityVerifier) VerifyObserverTimestamps(entity SignedEntity, logTimestamps []TimestampVerificationResult) ([]TimestampVerificationResult, error) { verifiedTimestamps := []TimestampVerificationResult{} // From spec: // > … if verification or timestamp parsing fails, the Verifier MUST abort if v.config.requireSignedTimestamps { verifiedSignedTimestamps, err := VerifyTimestampAuthorityWithThreshold(entity, v.trustedMaterial, v.config.signedTimestampThreshold) if err != nil { return nil, err } for _, vts := range verifiedSignedTimestamps { verifiedTimestamps = append(verifiedTimestamps, TimestampVerificationResult{Type: "TimestampAuthority", URI: vts.URI, Timestamp: vts.Time}) } } if v.config.requireIntegratedTimestamps { if len(logTimestamps) < v.config.integratedTimeThreshold { return nil, fmt.Errorf("threshold not met for verified log entry integrated timestamps: %d < %d", len(logTimestamps), v.config.integratedTimeThreshold) } verifiedTimestamps = append(verifiedTimestamps, logTimestamps...) } if v.config.requireObserverTimestamps { verifiedSignedTimestamps, err := VerifyTimestampAuthority(entity, v.trustedMaterial) if err != nil { return nil, err } // check threshold for both RFC3161 and log timestamps tsCount := len(verifiedSignedTimestamps) + len(logTimestamps) if tsCount < v.config.observerTimestampThreshold { return nil, fmt.Errorf("threshold not met for verified signed & log entry integrated timestamps: %d < %d", tsCount, v.config.observerTimestampThreshold) } // append all timestamps verifiedTimestamps = append(verifiedTimestamps, logTimestamps...) for _, vts := range verifiedSignedTimestamps { verifiedTimestamps = append(verifiedTimestamps, TimestampVerificationResult{Type: "TimestampAuthority", URI: vts.URI, Timestamp: vts.Time}) } } if v.config.useCurrentTime { // use current time to verify certificate if no signed timestamps are provided verifiedTimestamps = append(verifiedTimestamps, TimestampVerificationResult{Type: "CurrentTime", URI: "", Timestamp: time.Now()}) } if len(verifiedTimestamps) == 0 { return nil, fmt.Errorf("no valid observer timestamps found") } return verifiedTimestamps, nil } sigstore-go-0.7.1/pkg/verify/signed_entity_test.go000066400000000000000000000477711477477521700223460ustar00rootroot00000000000000// 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 verify_test import ( "crypto/x509" "errors" "strings" "testing" "unicode" "encoding/hex" "encoding/json" in_toto "github.com/in-toto/attestation/go/v1" "github.com/sigstore/sigstore-go/pkg/testing/data" "github.com/sigstore/sigstore-go/pkg/verify" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/types/known/structpb" ) func TestSignedEntityVerifierInitialization(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") // can't create a verifier without specifying either tlog or tsa _, err := verify.NewSignedEntityVerifier(tr) assert.NotNil(t, err) // can create a verifier with both of them _, err = verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithSignedTimestamps(1)) assert.Nil(t, err) // unless we are really sure we want a verifier without either tlog or tsa _, err = verify.NewSignedEntityVerifier(tr, verify.WithCurrentTime()) assert.Nil(t, err) // can configure the verifiers with thresholds _, err = verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(2), verify.WithSignedTimestamps(10)) assert.Nil(t, err) // can't configure them with < 1 thresholds _, err = verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(0), verify.WithSignedTimestamps(-10)) assert.Error(t, err) } func TestSignedEntityVerifierInitRequiresTimestamp(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") _, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1)) assert.Error(t, err) if !strings.Contains(err.Error(), "you must specify at least one of") { t.Errorf("expected error missing timestamp verifier, got: %v", err) } _, err = verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithIntegratedTimestamps(1)) assert.NoError(t, err) _, err = verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithSignedTimestamps(1)) assert.NoError(t, err) _, err = verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) assert.NoError(t, err) _, err = verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithCurrentTime()) assert.NoError(t, err) } // Testing a bundle: // - signed by public good // - one tlog entry // - zero tsa entries func TestEntitySignedByPublicGoodWithTlogVerifiesSuccessfully(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") v, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) assert.NoError(t, err) res, err := v.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.NoError(t, err) assert.NotNil(t, res) assert.NotNil(t, res.Statement) assert.Equal(t, "https://slsa.dev/provenance/v1", res.Statement.PredicateType) assert.NotNil(t, res.Signature) assert.NotNil(t, res.Signature.Certificate) assert.Equal(t, "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main", res.Signature.Certificate.SubjectAlternativeName) assert.NotEmpty(t, res.VerifiedTimestamps) assert.Equal(t, "https://rekor.sigstore.dev", res.VerifiedTimestamps[0].URI) // verifies with integrated timestamp threshold too v, err = verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithIntegratedTimestamps(1)) assert.NoError(t, err) res, err = v.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.NoError(t, err) assert.NotNil(t, res) } func TestEntitySignedByPublicGoodWithoutTimestampsVerifiesSuccessfully(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") v, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithIntegratedTimestamps(1)) assert.NoError(t, err) res, err := v.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.NoError(t, err) assert.NotNil(t, res) } func TestEntitySignedByPublicGoodWithHighTlogThresholdFails(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") v, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(2), verify.WithObserverTimestamps(1)) assert.NoError(t, err) res, err := v.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.Error(t, err) assert.Nil(t, res) if !strings.Contains(err.Error(), "not enough verified log entries from transparency log") { t.Errorf("expected error not meeting log entry threshold, got: %v", err) } } func TestEntitySignedByPublicGoodWithoutVerifyingLogEntryFails(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") v, err := verify.NewSignedEntityVerifier(tr, verify.WithObserverTimestamps(1)) assert.NoError(t, err) res, err := v.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.Error(t, err) assert.Nil(t, res) if !strings.Contains(err.Error(), "threshold not met for verified signed & log entry integrated timestamps") { t.Errorf("expected error not meeting timestamp threshold without entry verification, got: %v", err) } // also fails trying to use integrated timestamps without verifying the log v, err = verify.NewSignedEntityVerifier(tr, verify.WithIntegratedTimestamps(1)) assert.NoError(t, err) res, err = v.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.Error(t, err) assert.Nil(t, res) if !strings.Contains(err.Error(), "threshold not met for verified log entry integrated timestamps") { t.Errorf("expected error not meeting integrated timestamp threshold without entry verification, got: %v", err) } } func TestEntitySignedByPublicGoodWithHighLogTimestampThresholdFails(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") v, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithIntegratedTimestamps(2)) assert.NoError(t, err) res, err := v.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.Error(t, err) assert.Nil(t, res) if !strings.Contains(err.Error(), "threshold not met for verified log entry integrated timestamps") { t.Errorf("expected error not meeting log entry integrated timestamp threshold, got: %v", err) } } func TestEntitySignedByPublicGoodExpectingTSAFails(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") v, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithSignedTimestamps(1)) assert.NoError(t, err) res, err := v.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.Error(t, err) assert.Nil(t, res) if !strings.Contains(err.Error(), "threshold not met for verified signed timestamps") { t.Errorf("expected error not meeting signed timestamp threshold, got: %v", err) } } func TestEntitySignedByPublicGoodWithHighObserverTimestampThresholdFails(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") v, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(2)) assert.NoError(t, err) res, err := v.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.Error(t, err) assert.Nil(t, res) if !strings.Contains(err.Error(), "threshold not met for verified signed & log entry integrated timestamps") { t.Errorf("expected error not meeting observer timestamp threshold, got: %v", err) } } func TestEntityWithOthernameSan(t *testing.T) { tr := data.TrustedRoot(t, "scaffolding.json") entity := data.Bundle(t, "othername.sigstore.json") v, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithIntegratedTimestamps(1)) assert.NoError(t, err) digest, err := hex.DecodeString("bc103b4a84971ef6459b294a2b98568a2bfb72cded09d4acd1e16366a401f95b") assert.NoError(t, err) certID, err := verify.NewShortCertificateIdentity("http://oidc.local:8080", "", "foo!oidc.local", "") assert.NoError(t, err) res, err := v.Verify(entity, verify.NewPolicy(verify.WithArtifactDigest("sha256", digest), verify.WithCertificateIdentity(certID))) assert.NoError(t, err) assert.NotNil(t, res) assert.Equal(t, res.VerifiedIdentity.Issuer.Issuer, "http://oidc.local:8080") assert.Equal(t, res.VerifiedIdentity.SubjectAlternativeName.SubjectAlternativeName, "foo!oidc.local") // an email address doesn't verify certID, err = verify.NewShortCertificateIdentity("http://oidc.local:8080", "", "foo@oidc.local", "") assert.NoError(t, err) _, err = v.Verify(entity, verify.NewPolicy(verify.WithArtifactDigest("sha256", digest), verify.WithCertificateIdentity(certID))) assert.Error(t, err) } // Now we test policy: func TestVerifyPolicyOptionErors(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") verifier, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) assert.Nil(t, err) goodCertID, err := verify.NewShortCertificateIdentity(verify.ActionsIssuerValue, "", "", verify.SigstoreSanRegex) assert.Nil(t, err) digest, _ := hex.DecodeString("46d4e2f74c4877316640000a6fdf8a8b59f1e0847667973e9859f774dd31b8f1e0937813b777fb66a2ac67d50540fe34640966eee9fc2ccca387082b4c85cd3c") // first, we demonstrate a happy path combination: noArtifactHappyPath := verify.NewPolicy(verify.WithoutArtifactUnsafe(), verify.WithCertificateIdentity(goodCertID)) p, err := noArtifactHappyPath.BuildConfig() assert.Nil(t, err) assert.NotNil(t, p) assert.False(t, p.RequireArtifact()) assert.True(t, p.RequireIdentities()) // --- noArtifactNoCertHappyPath := verify.NewPolicy(verify.WithoutArtifactUnsafe(), verify.WithoutIdentitiesUnsafe()) p, err = noArtifactNoCertHappyPath.BuildConfig() assert.Nil(t, err) assert.NotNil(t, p) assert.False(t, p.RequireArtifact()) assert.False(t, p.RequireIdentities()) // --- yesArtifactNoCertHappyPath := verify.NewPolicy(verify.WithArtifactDigest("sha512", digest), verify.WithoutIdentitiesUnsafe()) p, err = yesArtifactNoCertHappyPath.BuildConfig() assert.Nil(t, err) assert.NotNil(t, p) assert.True(t, p.RequireArtifact()) assert.False(t, p.RequireIdentities()) // --- noArtifactKeyHappyPath := verify.NewPolicy(verify.WithoutArtifactUnsafe(), verify.WithKey()) p, err = noArtifactKeyHappyPath.BuildConfig() assert.Nil(t, err) assert.NotNil(t, p) assert.True(t, p.RequireSigningKey()) assert.False(t, p.RequireIdentities()) // let's exercise the different error cases! // 1. can't combine WithoutArtifactUnsafe with other Artifact options // technically a hack that requires casting but better safe than sorry: badArtifactComboPolicy1 := verify.NewPolicy(verify.WithoutArtifactUnsafe(), verify.PolicyOption(verify.WithArtifactDigest("sha512", digest)), verify.WithCertificateIdentity(goodCertID)) _, err = badArtifactComboPolicy1.BuildConfig() assert.NotNil(t, err) // imho good to check that the verify func also fails _, err = verifier.Verify(entity, badArtifactComboPolicy1) assert.NotNil(t, err) // 2. can't combine several artifact policies badArtifactComboPolicy2 := verify.NewPolicy(verify.WithArtifact(strings.NewReader("")), verify.PolicyOption(verify.WithArtifactDigest("sha512", digest)), verify.WithCertificateIdentity(goodCertID)) _, err = badArtifactComboPolicy2.BuildConfig() assert.NotNil(t, err) _, err = verifier.Verify(entity, badArtifactComboPolicy2) assert.NotNil(t, err) // 3. always have to provide _an_ identity option, even tho it will compile: badIdentityPolicyOpts := verify.NewPolicy(verify.WithoutArtifactUnsafe()) _, err = badIdentityPolicyOpts.BuildConfig() assert.NotNil(t, err) _, err = verifier.Verify(entity, badIdentityPolicyOpts) assert.NotNil(t, err) // 4. can't combine incompatible identity options badIdentityPolicyCombo := verify.NewPolicy(verify.WithoutArtifactUnsafe(), verify.WithoutIdentitiesUnsafe(), verify.WithCertificateIdentity(goodCertID)) _, err = badIdentityPolicyCombo.BuildConfig() assert.NotNil(t, err) _, err = verifier.Verify(entity, badIdentityPolicyCombo) assert.NotNil(t, err) // 5. can't expect certificate and key signature badIdentityPolicyCombo2 := verify.NewPolicy(verify.WithoutArtifactUnsafe(), verify.WithCertificateIdentity(goodCertID), verify.WithKey()) _, err = badIdentityPolicyCombo2.BuildConfig() assert.NotNil(t, err) } func TestEntitySignedByPublicGoodWithCertificateIdentityVerifiesSuccessfully(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") goodCI, _ := verify.NewShortCertificateIdentity(verify.ActionsIssuerValue, "", "", verify.SigstoreSanRegex) badCI, _ := verify.NewShortCertificateIdentity(verify.ActionsIssuerValue, "", "BadSANValue", "") verifier, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) assert.Nil(t, err) digest, err := hex.DecodeString("46d4e2f74c4877316640000a6fdf8a8b59f1e0847667973e9859f774dd31b8f1e0937813b777fb66a2ac67d50540fe34640966eee9fc2ccca387082b4c85cd3c") assert.Nil(t, err) res, err := verifier.Verify(entity, verify.NewPolicy(verify.WithArtifactDigest("sha512", digest), verify.WithCertificateIdentity(badCI), verify.WithCertificateIdentity(goodCI))) assert.Nil(t, err) assert.Equal(t, res.VerifiedIdentity.Issuer.Issuer, verify.ActionsIssuerValue) // but if only pass in the bad CI, it will fail: res, err = verifier.Verify(entity, verify.NewPolicy( verify.WithArtifactDigest("sha512", digest), verify.WithCertificateIdentity(badCI))) assert.NotNil(t, err) assert.Nil(t, res) // and if the digest is off, verification fails badDigest, err := hex.DecodeString("56d4e2f74c4877316640000a6fdf8a8b59f1e0847667973e9859f774dd31b8f1e0937813b777fb66a2ac67d50540fe34640966eee9fc2ccca387082b4c85cd3c") assert.Nil(t, err) res, err = verifier.Verify(entity, verify.NewPolicy( verify.WithArtifactDigest("sha512", badDigest), verify.WithCertificateIdentity(goodCI))) assert.NotNil(t, err) assert.Nil(t, res) } // TODO test bundles: // - signed with a key, not a fulcio cert, i.e. npm // - with duplicate tlog entries // - with duplicate tsa entries // - with tlog entries that do not refer to the verification content // - with tsa entries that do not refer to the verification content // - with an artifact to be verified // - with a messagesignature (and artifact) func TestThatAllTheJSONKeysStartWithALowerCase(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") verifier, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) assert.Nil(t, err) res, err := verifier.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.Nil(t, err) rawJSON, err := json.Marshal(res) assert.Nil(t, err) var unmarshaledJSON interface{} err = json.Unmarshal(rawJSON, &unmarshaledJSON) assert.Nil(t, err) ensureKeysBeginWithLowercase(t, unmarshaledJSON) } func ensureKeysBeginWithLowercase(t *testing.T, obj interface{}) { switch v := obj.(type) { case map[string]interface{}: for key, val := range v { r := []rune(key) assert.Equal(t, string(unicode.ToLower(r[0]))+string(r[1:]), key) ensureKeysBeginWithLowercase(t, val) } case []interface{}: for _, val := range v { ensureKeysBeginWithLowercase(t, val) } } } func TestSigstoreBundle2Sig(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "dsse-2sigs.sigstore.json") v, err := verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1)) assert.NoError(t, err) res, err := v.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.True(t, errors.Is(err, verify.ErrDSSEInvalidSignatureCount)) assert.Nil(t, res) } func TestStatementSerializesToValidInTotoStatement(t *testing.T) { statement := in_toto.Statement{} statement.Type = "https://in-toto.io/Statement/v0.1" statement.PredicateType = "https://example.org/predicate" statement.Subject = []*in_toto.ResourceDescriptor{ { Name: "artifact-name", Digest: map[string]string{ "sha256": "artifact-digest", }, }, } statement.Predicate = &structpb.Struct{ Fields: map[string]*structpb.Value{}, } result := verify.NewVerificationResult() result.Statement = &statement // marshal the statement to JSON resultJSON, err := json.Marshal(result) assert.NoError(t, err) want := ` { "mediaType": "application/vnd.dev.sigstore.verificationresult+json;version=0.1", "verifiedTimestamps": null, "statement": { "_type": "https://in-toto.io/Statement/v0.1", "predicateType": "https://example.org/predicate", "subject": [ { "name": "artifact-name", "digest": { "sha256": "artifact-digest" } } ], "predicate": {} } }` assert.JSONEq(t, want, string(resultJSON)) // unmarshal the JSON back to a VerificationResult result2 := verify.NewVerificationResult() err = json.Unmarshal(resultJSON, result2) assert.NoError(t, err) assert.Equal(t, result.MediaType, result2.MediaType) assert.Equal(t, result.Statement, result2.Statement) } func TestCertificateSignedEntityWithSCTsRequiredVerifiesSuccessfully(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") v, err := verify.NewSignedEntityVerifier( tr, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1), verify.WithSignedCertificateTimestamps(1), ) assert.NoError(t, err) res, err := v.Verify(entity, SkipArtifactAndIdentitiesPolicy) assert.NoError(t, err) assert.NotNil(t, res) assert.NotNil(t, res.Signature) assert.NotNil(t, res.Signature.Certificate) } func TestForcedKeySignedEntityWithSCTsRequiredFails(t *testing.T) { tr := data.TrustedRoot(t, "public-good.json") entity := data.Bundle(t, "sigstore.js@2.0.0-provenance.sigstore.json") v, err := verify.NewSignedEntityVerifier( tr, verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1), verify.WithSignedCertificateTimestamps(1), ) assert.NoError(t, err) // Wrap existing data to force key-signed behavior as current data is signed by a certificate keySignedEntity := &keySignedEntityWrapper{ SignedEntity: entity, forceKey: true, } res, err := v.Verify(keySignedEntity, SkipArtifactAndIdentitiesPolicy) assert.Error(t, err) assert.Nil(t, res) assert.Equal(t, "SCTs required but bundle is signed with a public key, which cannot contain SCTs", err.Error(), ) } type keySignedEntityWrapper struct { verify.SignedEntity forceKey bool } func (w *keySignedEntityWrapper) VerificationContent() (verify.VerificationContent, error) { vc, err := w.SignedEntity.VerificationContent() if err != nil { return nil, err } return &keySignedVerificationContent{ VerificationContent: vc, forceKey: w.forceKey, }, nil } type keySignedVerificationContent struct { verify.VerificationContent forceKey bool } func (k *keySignedVerificationContent) PublicKey() verify.PublicKeyProvider { if k.forceKey { return &mockPublicKeyProvider{keyBytes: []byte("mock-public-key")} } return k.VerificationContent.PublicKey() } func (k *keySignedVerificationContent) Certificate() *x509.Certificate { if k.forceKey { return nil } return k.VerificationContent.Certificate() } type mockPublicKeyProvider struct { keyBytes []byte } func (p *mockPublicKeyProvider) PublicKey() ([]byte, error) { return p.keyBytes, nil } func (p *mockPublicKeyProvider) Hint() string { return "mock" } sigstore-go-0.7.1/pkg/verify/tlog.go000066400000000000000000000107441477477521700173750ustar00rootroot00000000000000// 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 verify import ( "bytes" "crypto" "encoding/hex" "errors" "fmt" "github.com/sigstore/sigstore/pkg/signature" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/tlog" ) const maxAllowedTlogEntries = 32 // VerifyArtifactTransparencyLog verifies that the given entity has been logged // in the transparency log and that the log entry is valid. // // The threshold parameter is the number of unique transparency log entries // that must be verified. func VerifyArtifactTransparencyLog(entity SignedEntity, trustedMaterial root.TrustedMaterial, logThreshold int, trustIntegratedTime bool) ([]root.Timestamp, error) { //nolint:revive entries, err := entity.TlogEntries() if err != nil { return nil, err } // limit the number of tlog entries to prevent DoS if len(entries) > maxAllowedTlogEntries { return nil, fmt.Errorf("too many tlog entries: %d > %d", len(entries), maxAllowedTlogEntries) } // disallow duplicate entries, as a malicious actor could use duplicates to bypass the threshold for i := 0; i < len(entries); i++ { for j := i + 1; j < len(entries); j++ { if entries[i].LogKeyID() == entries[j].LogKeyID() && entries[i].LogIndex() == entries[j].LogIndex() { return nil, errors.New("duplicate tlog entries found") } } } sigContent, err := entity.SignatureContent() if err != nil { return nil, err } entitySignature := sigContent.Signature() verificationContent, err := entity.VerificationContent() if err != nil { return nil, err } verifiedTimestamps := []root.Timestamp{} logEntriesVerified := 0 for _, entry := range entries { err := tlog.ValidateEntry(entry) if err != nil { return nil, err } rekorLogs := trustedMaterial.RekorLogs() keyID := entry.LogKeyID() hex64Key := hex.EncodeToString([]byte(keyID)) tlogVerifier, ok := trustedMaterial.RekorLogs()[hex64Key] if !ok { // skip entries the trust root cannot verify continue } if !entry.HasInclusionPromise() && !entry.HasInclusionProof() { return nil, fmt.Errorf("entry must contain an inclusion proof and/or promise") } if entry.HasInclusionPromise() { err = tlog.VerifySET(entry, rekorLogs) if err != nil { // skip entries the trust root cannot verify continue } if trustIntegratedTime { verifiedTimestamps = append(verifiedTimestamps, root.Timestamp{Time: entry.IntegratedTime(), URI: tlogVerifier.BaseURL}) } } if entry.HasInclusionProof() { verifier, err := getVerifier(tlogVerifier.PublicKey, tlogVerifier.SignatureHashFunc) if err != nil { return nil, err } err = tlog.VerifyInclusion(entry, *verifier) if err != nil { return nil, err } // DO NOT use timestamp with only an inclusion proof, because it is not signed metadata } // Ensure entry signature matches signature from bundle if !bytes.Equal(entry.Signature(), entitySignature) { return nil, errors.New("transparency log signature does not match") } // Ensure entry certificate matches bundle certificate if !verificationContent.CompareKey(entry.PublicKey(), trustedMaterial) { return nil, errors.New("transparency log certificate does not match") } // TODO: if you have access to artifact, check that it matches body subject // Check tlog entry time against bundle certificates if !verificationContent.ValidAtTime(entry.IntegratedTime(), trustedMaterial) { return nil, errors.New("integrated time outside certificate validity") } // successful log entry verification logEntriesVerified++ } if logEntriesVerified < logThreshold { return nil, fmt.Errorf("not enough verified log entries from transparency log: %d < %d", logEntriesVerified, logThreshold) } return verifiedTimestamps, nil } func getVerifier(publicKey crypto.PublicKey, hashFunc crypto.Hash) (*signature.Verifier, error) { verifier, err := signature.LoadVerifier(publicKey, hashFunc) if err != nil { return nil, err } return &verifier, nil } sigstore-go-0.7.1/pkg/verify/tlog_test.go000066400000000000000000000215041477477521700204300ustar00rootroot00000000000000// 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 verify_test import ( "encoding/base64" "strings" "testing" "time" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/testing/ca" "github.com/sigstore/sigstore-go/pkg/tlog" "github.com/sigstore/sigstore-go/pkg/verify" "github.com/stretchr/testify/assert" ) // TODO(issue#53): Add unit tests for online log verification and inclusion proofs func TestTlogVerifier(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) statement := []byte(`{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"customFoo","subject":[{"name":"subject","digest":{"sha256":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}}],"predicate":{}}`) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", statement) assert.NoError(t, err) var ts []root.Timestamp ts, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore, 1, true) assert.NoError(t, err) // 1 verified timestamp assert.Len(t, ts, 1) ts, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore, 1, false) assert.NoError(t, err) // 0 verified timestamps, since integrated timestamps are ignored assert.Len(t, ts, 0) virtualSigstore2, err := ca.NewVirtualSigstore() assert.NoError(t, err) _, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore2, 1, true) assert.Error(t, err) // different sigstore instance should fail to verify // Attempt to use tlog with integrated time outside certificate validity. // // This time was chosen assuming the Fulcio signing certificate expires // after 5 minutes, but while the TSA intermediate is still valid (2 hours). entity, err = virtualSigstore.AttestAtTime("foo@example.com", "issuer", statement, time.Now().Add(30*time.Minute), false) assert.NoError(t, err) _, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore, 1, true) assert.Error(t, err) } type oneTrustedOneUntrustedLogEntry struct { *ca.TestEntity UntrustedTestEntity *ca.TestEntity } func (e *oneTrustedOneUntrustedLogEntry) TlogEntries() ([]*tlog.Entry, error) { entries, err := e.TestEntity.TlogEntries() if err != nil { return nil, err } otherEntries, err := e.UntrustedTestEntity.TlogEntries() if err != nil { return nil, err } return append(entries, otherEntries...), nil } func TestIgnoredTLogEntries(t *testing.T) { statement := []byte(`{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"customFoo","subject":[{"name":"subject","digest":{"sha256":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}}],"predicate":{}}`) virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", statement) assert.NoError(t, err) untrustedSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) untrustedEntity, err := untrustedSigstore.Attest("foo@example.com", "issuer", statement) assert.NoError(t, err) // success: entry that cannot be verified is ignored _, err = verify.VerifyArtifactTransparencyLog(&oneTrustedOneUntrustedLogEntry{entity, untrustedEntity}, virtualSigstore, 1, true) assert.NoError(t, err) // failure: threshold of 2 is not met since 1 untrusted entry is ignored _, err = verify.VerifyArtifactTransparencyLog(&oneTrustedOneUntrustedLogEntry{entity, untrustedEntity}, virtualSigstore, 2, true) assert.Error(t, err) } // invalidTLogEntity constructs a bundle with a Rekor response, but without an inclusion proof or promise type invalidTLogEntity struct { *ca.TestEntity } func (e *invalidTLogEntity) TlogEntries() ([]*tlog.Entry, error) { entries, err := e.TestEntity.TlogEntries() if err != nil { return nil, err } var invalidEntries []*tlog.Entry for _, entry := range entries { body, err := base64.StdEncoding.DecodeString(entry.Body().(string)) if err != nil { return nil, err } invalidEntry, err := tlog.NewEntry(body, entry.IntegratedTime().Unix(), entry.LogIndex(), []byte(entry.LogKeyID()), nil, nil) if err != nil { return nil, err } invalidEntries = append(invalidEntries, invalidEntry) } return invalidEntries, nil } func TestInvalidTLogEntries(t *testing.T) { statement := []byte(`{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"customFoo","subject":[{"name":"subject","digest":{"sha256":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}}],"predicate":{}}`) virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", statement) assert.NoError(t, err) // failure: threshold of 1 is not met with invalid entry _, err = verify.VerifyArtifactTransparencyLog(&invalidTLogEntity{entity}, virtualSigstore, 1, true) assert.Error(t, err) if err.Error() != "entry must contain an inclusion proof and/or promise" { t.Errorf("expected error with missing proof/promises, got: %v", err.Error()) } } type noTLogEntity struct { *ca.TestEntity } func (e *noTLogEntity) TlogEntries() ([]*tlog.Entry, error) { return []*tlog.Entry{}, nil } func TestNoTLogEntries(t *testing.T) { statement := []byte(`{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"customFoo","subject":[{"name":"subject","digest":{"sha256":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}}],"predicate":{}}`) virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", statement) assert.NoError(t, err) // failure: threshold of 1 is not met with no entries _, err = verify.VerifyArtifactTransparencyLog(&noTLogEntity{entity}, virtualSigstore, 1, true) assert.Error(t, err) if !strings.Contains(err.Error(), "not enough verified log entries from transparency log") { t.Errorf("expected error with timestamp threshold, got: %v", err.Error()) } } type dupTlogEntity struct { *ca.TestEntity } func (e *dupTlogEntity) TlogEntries() ([]*tlog.Entry, error) { entries, err := e.TestEntity.TlogEntries() if err != nil { return nil, err } return append(entries, entries[0]), nil } func TestDuplicateTlogEntries(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) statement := []byte(`{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"customFoo","subject":[{"name":"subject","digest":{"sha256":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}}],"predicate":{}}`) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", statement) assert.NoError(t, err) _, err = verify.VerifyArtifactTransparencyLog(&dupTlogEntity{entity}, virtualSigstore, 1, true) assert.ErrorContains(t, err, "duplicate tlog entries found") // duplicate tlog entries should fail to verify } type tooManyTlogEntriesEntity struct { *ca.TestEntity } func (e *tooManyTlogEntriesEntity) TlogEntries() ([]*tlog.Entry, error) { entries, err := e.TestEntity.TlogEntries() if err != nil { return nil, err } for i := 0; i < 32; i++ { entries = append(entries, entries[0]) } return entries, nil } func TestMaxAllowedTlogEntries(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) statement := []byte(`{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"customFoo","subject":[{"name":"subject","digest":{"sha256":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}}],"predicate":{}}`) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", statement) assert.NoError(t, err) _, err = verify.VerifyArtifactTransparencyLog(&tooManyTlogEntriesEntity{entity}, virtualSigstore, 1, true) assert.ErrorContains(t, err, "too many tlog entries") // too many tlog entries should fail to verify } func TestOfflineInclusionProofVerification(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) statement := []byte(`{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"customFoo","subject":[{"name":"subject","digest":{"sha256":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"}}],"predicate":{}}`) integratedTime := time.Now().Add(5 * time.Minute) entity, err := virtualSigstore.AttestAtTime("foo@example.com", "issuer", statement, integratedTime, true) assert.NoError(t, err) _, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore, 1, true) assert.NoError(t, err) } sigstore-go-0.7.1/pkg/verify/tsa.go000066400000000000000000000065021477477521700172140ustar00rootroot00000000000000// 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 verify import ( "bytes" "errors" "fmt" "github.com/sigstore/sigstore-go/pkg/root" ) const maxAllowedTimestamps = 32 // VerifyTimestampAuthority verifies that the given entity has been timestamped // by a trusted timestamp authority and that the timestamp is valid. func VerifyTimestampAuthority(entity SignedEntity, trustedMaterial root.TrustedMaterial) ([]*root.Timestamp, error) { //nolint:revive signedTimestamps, err := entity.Timestamps() if err != nil { return nil, err } // limit the number of timestamps to prevent DoS if len(signedTimestamps) > maxAllowedTimestamps { return nil, fmt.Errorf("too many signed timestamps: %d > %d", len(signedTimestamps), maxAllowedTimestamps) } // disallow duplicate timestamps, as a malicious actor could use duplicates to bypass the threshold for i := 0; i < len(signedTimestamps); i++ { for j := i + 1; j < len(signedTimestamps); j++ { if bytes.Equal(signedTimestamps[i], signedTimestamps[j]) { return nil, errors.New("duplicate timestamps found") } } } sigContent, err := entity.SignatureContent() if err != nil { return nil, err } signatureBytes := sigContent.Signature() verifiedTimestamps := []*root.Timestamp{} for _, timestamp := range signedTimestamps { verifiedSignedTimestamp, err := verifySignedTimestamp(timestamp, signatureBytes, trustedMaterial) // Timestamps from unknown source are okay, but don't count as verified if err != nil { continue } verifiedTimestamps = append(verifiedTimestamps, verifiedSignedTimestamp) } return verifiedTimestamps, nil } // VerifyTimestampAuthority verifies that the given entity has been timestamped // by a trusted timestamp authority and that the timestamp is valid. // // The threshold parameter is the number of unique timestamps that must be // verified. func VerifyTimestampAuthorityWithThreshold(entity SignedEntity, trustedMaterial root.TrustedMaterial, threshold int) ([]*root.Timestamp, error) { //nolint:revive verifiedTimestamps, err := VerifyTimestampAuthority(entity, trustedMaterial) if err != nil { return nil, err } if len(verifiedTimestamps) < threshold { return nil, fmt.Errorf("threshold not met for verified signed timestamps: %d < %d", len(verifiedTimestamps), threshold) } return verifiedTimestamps, nil } func verifySignedTimestamp(signedTimestamp []byte, signatureBytes []byte, trustedMaterial root.TrustedMaterial) (*root.Timestamp, error) { timestampAuthorities := trustedMaterial.TimestampingAuthorities() // Iterate through TSA certificate authorities to find one that verifies for _, tsa := range timestampAuthorities { ts, err := tsa.Verify(signedTimestamp, signatureBytes) if err == nil { return ts, nil } } return nil, errors.New("unable to verify signed timestamps") } sigstore-go-0.7.1/pkg/verify/tsa_test.go000066400000000000000000000170311477477521700202520ustar00rootroot00000000000000// 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 verify_test import ( "testing" "time" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/testing/ca" "github.com/sigstore/sigstore-go/pkg/verify" "github.com/stretchr/testify/assert" ) func TestTimestampAuthorityVerifier(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", []byte("statement")) assert.NoError(t, err) _, err = verify.VerifyTimestampAuthorityWithThreshold(entity, virtualSigstore, 1) assert.NoError(t, err) virtualSigstore2, err := ca.NewVirtualSigstore() assert.NoError(t, err) _, err = verify.VerifyTimestampAuthorityWithThreshold(entity, virtualSigstore2, 1) assert.Error(t, err) // different sigstore instance should fail to verify untrustedEntity, err := virtualSigstore2.Attest("foo@example.com", "issuer", []byte("statement")) assert.NoError(t, err) _, err = verify.VerifyTimestampAuthorityWithThreshold(&oneTrustedOneUntrustedTimestampEntity{entity, untrustedEntity}, virtualSigstore, 1) assert.NoError(t, err) _, err = verify.VerifyTimestampAuthorityWithThreshold(&oneTrustedOneUntrustedTimestampEntity{entity, untrustedEntity}, virtualSigstore, 2) assert.Error(t, err) // only 1 trusted should not meet threshold of 2 } func TestTimestampAuthorityVerifierWithoutThreshold(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", []byte("statement")) assert.NoError(t, err) virtualSigstore2, err := ca.NewVirtualSigstore() assert.NoError(t, err) var ts []*root.Timestamp // expect one verified timestamp ts, err = verify.VerifyTimestampAuthority(entity, virtualSigstore) assert.NoError(t, err) assert.Len(t, ts, 1) // no failure, but also no verified timestamps ts, err = verify.VerifyTimestampAuthority(entity, virtualSigstore2) assert.NoError(t, err) assert.Empty(t, ts) } type oneTrustedOneUntrustedTimestampEntity struct { *ca.TestEntity UntrustedTestEntity *ca.TestEntity } func (e *oneTrustedOneUntrustedTimestampEntity) Timestamps() ([][]byte, error) { timestamps, err := e.TestEntity.Timestamps() if err != nil { return nil, err } untrustedTimestamps, err := e.UntrustedTestEntity.Timestamps() if err != nil { return nil, err } return append(timestamps, untrustedTimestamps...), nil } type dupTimestampEntity struct { *ca.TestEntity } func (e *dupTimestampEntity) Timestamps() ([][]byte, error) { timestamps, err := e.TestEntity.Timestamps() if err != nil { return nil, err } return append(timestamps, timestamps[0]), nil } func TestDuplicateTimestamps(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", []byte("statement")) assert.NoError(t, err) _, err = verify.VerifyTimestampAuthorityWithThreshold(&dupTimestampEntity{entity}, virtualSigstore, 1) assert.ErrorContains(t, err, "duplicate timestamps found") } type badTSASignatureEntity struct { *ca.TestEntity } func (e *badTSASignatureEntity) Timestamps() ([][]byte, error) { return [][]byte{[]byte("bad signature")}, nil } func TestBadTSASignature(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", []byte("statement")) assert.NoError(t, err) _, err = verify.VerifyTimestampAuthorityWithThreshold(&badTSASignatureEntity{entity}, virtualSigstore, 1) assert.Error(t, err) } type customTSAChainTrustedMaterial struct { *ca.VirtualSigstore tsaChain []root.TimestampingAuthority } func (i *customTSAChainTrustedMaterial) TimestampingAuthorities() []root.TimestampingAuthority { return i.tsaChain } func TestBadTSACertificateChain(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) virtualSigstore2, err := ca.NewVirtualSigstore() assert.NoError(t, err) ca1 := virtualSigstore.TimestampingAuthorities()[0].(*root.SigstoreTimestampingAuthority) ca2 := virtualSigstore2.TimestampingAuthorities()[0].(*root.SigstoreTimestampingAuthority) badChain := &root.SigstoreTimestampingAuthority{ Root: ca2.Root, Intermediates: ca2.Intermediates, Leaf: ca1.Leaf, ValidityPeriodStart: ca1.ValidityPeriodStart, ValidityPeriodEnd: ca1.ValidityPeriodEnd, } entity, err := virtualSigstore.Attest("foo@example.com", "issuer", []byte("statement")) assert.NoError(t, err) _, err = verify.VerifyTimestampAuthorityWithThreshold(entity, &customTSAChainTrustedMaterial{VirtualSigstore: virtualSigstore, tsaChain: []root.TimestampingAuthority{badChain}}, 1) assert.Error(t, err) } func TestBadTSACertificateChainOutsideValidityPeriod(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) ca := virtualSigstore.TimestampingAuthorities()[0].(*root.SigstoreTimestampingAuthority) for _, test := range []struct { name string err bool ca *root.SigstoreTimestampingAuthority }{ { name: "valid", err: false, ca: &root.SigstoreTimestampingAuthority{ Root: ca.Root, Intermediates: ca.Intermediates, Leaf: ca.Leaf, // ValidityPeriod is not set, so it should always be valid }, }, { name: "invalid: start time in the future", err: true, ca: &root.SigstoreTimestampingAuthority{ Root: ca.Root, Intermediates: ca.Intermediates, Leaf: ca.Leaf, ValidityPeriodStart: time.Now().Add(10 * time.Minute), }, }, { name: "invalid: end time in the past", err: true, ca: &root.SigstoreTimestampingAuthority{ Root: ca.Root, Intermediates: ca.Intermediates, Leaf: ca.Leaf, ValidityPeriodEnd: time.Now().Add(-10 * time.Minute), }, }, } { t.Run(test.name, func(t *testing.T) { entity, err := virtualSigstore.Attest("foo@example.com", "issuer", []byte("statement")) assert.NoError(t, err) _, err = verify.VerifyTimestampAuthorityWithThreshold(entity, &customTSAChainTrustedMaterial{VirtualSigstore: virtualSigstore, tsaChain: []root.TimestampingAuthority{test.ca}}, 1) if test.err { assert.Error(t, err) } else { assert.NoError(t, err) } }) } } type tooManyTimestampsEntity struct { *ca.TestEntity } func (e *tooManyTimestampsEntity) Timestamps() ([][]byte, error) { timestamps, err := e.TestEntity.Timestamps() if err != nil { return nil, err } for i := 0; i < 32; i++ { timestamps = append(timestamps, timestamps[0]) } return timestamps, nil } func TestTooManyTimestamps(t *testing.T) { virtualSigstore, err := ca.NewVirtualSigstore() assert.NoError(t, err) entity, err := virtualSigstore.Attest("foo@example.com", "issuer", []byte("statement")) assert.NoError(t, err) _, err = verify.VerifyTimestampAuthorityWithThreshold(&tooManyTimestampsEntity{entity}, virtualSigstore, 1) assert.ErrorContains(t, err, "too many signed timestamps") } sigstore-go-0.7.1/test/000077500000000000000000000000001477477521700147655ustar00rootroot00000000000000sigstore-go-0.7.1/test/conformance/000077500000000000000000000000001477477521700172575ustar00rootroot00000000000000sigstore-go-0.7.1/test/conformance/main.go000066400000000000000000000146031477477521700205360ustar00rootroot00000000000000// 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 main import ( "encoding/hex" "fmt" "log" "os" "strings" "time" protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1" "github.com/theupdateframework/go-tuf/v2/metadata/fetcher" "google.golang.org/protobuf/encoding/protojson" "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore-go/pkg/sign" "github.com/sigstore/sigstore-go/pkg/tuf" "github.com/sigstore/sigstore-go/pkg/util" "github.com/sigstore/sigstore-go/pkg/verify" ) var bundlePath *string var certOIDC *string var certSAN *string var identityToken *string var staging = false var trustedRootPath *string func usage() { fmt.Println("Usage:") fmt.Printf("\t%s sign-bundle --identity-token TOKEN --bundle FILE [--staging] FILE", os.Args[0]) fmt.Printf("\t%s verify-bundle --bundle FILE --certificate-identity IDENTITY --certificate-oidc-issuer URL [--trusted-root FILE] [--staging] FILE\n", os.Args[0]) } func getTrustedRoot(staging bool) root.TrustedMaterial { var trustedRootJSON []byte var err error if trustedRootPath != nil { trustedRootJSON, err = os.ReadFile(*trustedRootPath) } else { opts := tuf.DefaultOptions() fetcher := fetcher.DefaultFetcher{} fetcher.SetHTTPUserAgent(util.ConstructUserAgent()) opts.Fetcher = &fetcher if staging { opts.Root = tuf.StagingRoot() opts.RepositoryBaseURL = tuf.StagingMirror } client, err := tuf.New(opts) if err != nil { log.Fatal(err) } trustedRootJSON, err = client.GetTarget("trusted_root.json") if err != nil { log.Fatal(err) } } if err != nil { log.Fatal(err) } tr, err := root.NewTrustedRootFromJSON(trustedRootJSON) if err != nil { log.Fatal(err) } return tr } func parseArgs() { for i := 2; i < len(os.Args); { switch os.Args[i] { case "--bundle": bundlePath = &os.Args[i+1] i += 2 case "--certificate-oidc-issuer": certOIDC = &os.Args[i+1] i += 2 case "--certificate-identity": certSAN = &os.Args[i+1] i += 2 case "--identity-token": identityToken = &os.Args[i+1] i += 2 case "--staging": staging = true i++ case "--trusted-root": trustedRootPath = &os.Args[i+1] i += 2 default: i++ } } } func signBundle(withRekor bool) (*protobundle.Bundle, error) { timeout := time.Duration(60 * time.Second) signingOptions := sign.BundleOptions{} instance := "sigstore" if staging { instance = "sigstage" } fulcioOpts := &sign.FulcioOptions{ BaseURL: fmt.Sprintf("https://fulcio.%s.dev", instance), Timeout: timeout, } signingOptions.CertificateProvider = sign.NewFulcio(fulcioOpts) signingOptions.CertificateProviderOptions = &sign.CertificateProviderOptions{ IDToken: *identityToken, } if withRekor { rekorOpts := &sign.RekorOptions{ BaseURL: fmt.Sprintf("https://rekor.%s.dev", instance), Timeout: timeout, } signingOptions.TransparencyLogs = append(signingOptions.TransparencyLogs, sign.NewRekor(rekorOpts)) } if withRekor { // Verification will only work with Rekor signingOptions.TrustedRoot = getTrustedRoot(staging) } fileBytes, err := os.ReadFile(os.Args[len(os.Args)-1]) if err != nil { return nil, err } content := &sign.PlainData{Data: fileBytes} keypair, err := sign.NewEphemeralKeypair(nil) if err != nil { return nil, err } bundle, err := sign.Bundle(content, keypair, signingOptions) if err != nil { return nil, err } return bundle, nil } func main() { if len(os.Args) < 2 { usage() os.Exit(1) } parseArgs() switch os.Args[1] { case "sign-bundle": bundle, err := signBundle(true) if err != nil { log.Fatal(err) } bundleBytes, err := protojson.Marshal(bundle) if err != nil { log.Fatal(err) } err = os.WriteFile(*bundlePath, bundleBytes, 0600) if err != nil { log.Fatal(err) } case "verify-bundle": // Load bundle b, err := bundle.LoadJSONFromPath(*bundlePath) if err != nil { log.Fatal(err) } var artifactPolicyOption verify.ArtifactPolicyOption fileOrDigest := os.Args[len(os.Args)-1] // Load digest or file if strings.Contains(fileOrDigest, ":") { algDigest := strings.Split(fileOrDigest, ":") alg, hexDigest := algDigest[0], algDigest[1] digest, err := hex.DecodeString(hexDigest) if err != nil { log.Fatal(err) } artifactPolicyOption = verify.WithArtifactDigest(alg, digest) } else { file, err := os.Open(fileOrDigest) if err != nil { log.Fatal(err) } artifactPolicyOption = verify.WithArtifact(file) } // Configure verification options identityPolicies := []verify.PolicyOption{} if *certOIDC != "" || *certSAN != "" { certID, err := verify.NewShortCertificateIdentity(*certOIDC, "", *certSAN, "") if err != nil { fmt.Println(err) os.Exit(1) } identityPolicies = append(identityPolicies, verify.WithCertificateIdentity(certID)) } // Load trust root tr := getTrustedRoot(staging) verifierConfig := []verify.VerifierOption{} verifierConfig = append(verifierConfig, verify.WithSignedCertificateTimestamps(1)) // Check bundle and trusted root for signed timestamp information bundleTimestamps, err := b.Timestamps() if err != nil { fmt.Println(err) os.Exit(1) } if len(tr.TimestampingAuthorities()) > 0 && len(bundleTimestamps) > 0 { verifierConfig = append(verifierConfig, verify.WithSignedTimestamps(1)) } // Check bundle and trusted root for Tlog information if len(tr.RekorLogs()) > 0 && b.HasInclusionPromise() { verifierConfig = append(verifierConfig, verify.WithTransparencyLog(1), verify.WithIntegratedTimestamps(1)) } sev, err := verify.NewSignedEntityVerifier(tr, verifierConfig...) if err != nil { log.Fatal(err) } // Verify bundle _, err = sev.Verify(b, verify.NewPolicy(artifactPolicyOption, identityPolicies...)) if err != nil { log.Fatal(err) } default: log.Fatalf("Unsupported command %s", os.Args[1]) } } sigstore-go-0.7.1/test/fuzz/000077500000000000000000000000001477477521700157635ustar00rootroot00000000000000sigstore-go-0.7.1/test/fuzz/dictionaries/000077500000000000000000000000001477477521700204405ustar00rootroot00000000000000sigstore-go-0.7.1/test/fuzz/dictionaries/intoto_json.dict000066400000000000000000000007371477477521700236610ustar00rootroot00000000000000"https://in-toto.io/Statement/v0.1" "_type" "predicateType" "subject" "sha256" "sha512" "https://slsa.dev/provenance/v0.2" # Below is from https://github.com/rc0r/afl-fuzz/blob/master/dictionaries/json.dict "0" ",0" ":0" "0:" "-1.2e+3" "true" "false" "null" "\"\"" ",\"\"" ":\"\"" "\"\":" "{}" ",{}" ":{}" "{\"\":0}" "{{}}" "[]" ",[]" ":[]" "[0]" "[[]]" "''" "\\" "\\b" "\\f" "\\n" "\\r" "\\t" "\\u0000" "\\x00" "\\0" "\\uD800\\uDC00" "\\uDBFF\\uDFFF" "\"\":0" "//" "/**/" sigstore-go-0.7.1/test/fuzz/oss_fuzz_build.sh000077500000000000000000000046541477477521700213740ustar00rootroot00000000000000#!/bin/bash -eu # 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 get github.com/AdamKorcz/go-118-fuzz-build/testing compile_native_go_fuzzer github.com/sigstore/sigstore-go/pkg/bundle FuzzBundle FuzzBundle compile_native_go_fuzzer github.com/sigstore/sigstore-go/pkg/tlog FuzzParseEntry FuzzParseEntry mkdir pkg/verify/fuzz && mv pkg/verify/fuzz_test.go pkg/verify/fuzz/ compile_native_go_fuzzer github.com/sigstore/sigstore-go/pkg/verify/fuzz FuzzVerifyTimestampAuthorityWithoutThreshold FuzzVerifyTimestampAuthorityWithoutThreshold compile_native_go_fuzzer github.com/sigstore/sigstore-go/pkg/verify/fuzz FuzzVerifyTimestampAuthorityWithThreshold FuzzVerifyTimestampAuthorityWithThreshold compile_native_go_fuzzer github.com/sigstore/sigstore-go/pkg/verify/fuzz FuzzVerifyArtifactTransparencyLog FuzzVerifyArtifactTransparencyLog compile_native_go_fuzzer github.com/sigstore/sigstore-go/pkg/verify/fuzz FuzzSignedEntityVerifier FuzzSignedEntityVerifier compile_native_go_fuzzer github.com/sigstore/sigstore-go/pkg/verify/fuzz FuzzVerifySignatureWithoutArtifactOrDigest FuzzVerifySignatureWithoutArtifactOrDigest compile_native_go_fuzzer github.com/sigstore/sigstore-go/pkg/verify/fuzz FuzzVerifySignatureWithArtifactWithoutDigest FuzzVerifySignatureWithArtifactWithoutDigest compile_native_go_fuzzer github.com/sigstore/sigstore-go/pkg/verify/fuzz FuzzVerifySignatureWithArtifactDigest FuzzVerifySignatureWithArtifactDigest zip -j $OUT/FuzzSignedEntityVerifier_seed_corpus.zip examples/trusted-root-public-good.json for fuzzer in FuzzVerifyTimestampAuthorityWithoutThreshold FuzzVerifyTimestampAuthorityWithThreshold FuzzVerifyArtifactTransparencyLog FuzzVerifySignatureWithoutArtifactOrDigest FuzzVerifySignatureWithArtifactWithoutDigest FuzzVerifySignatureWithArtifactDigest; do cp test/fuzz/dictionaries/intoto_json.dict $OUT/$fuzzer.dict zip -j $OUT/"$fuzzer"_seed_corpus.zip examples/sigstore-go-signing/intoto.txt done