diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f23ecf7cb..52f6881a3 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -56,9 +56,6 @@ jobs: - name: creds run: gcloud auth configure-docker --quiet - - name: container - run: echo -n "${{secrets.COSIGN_PASSWORD}}" | KO_PREFIX=gcr.io/projectsigstore/cosign/ci make sign-container + - name: policy-controller run: echo -n "${{secrets.COSIGN_PASSWORD}}" | KO_PREFIX=gcr.io/projectsigstore/cosign/ci make sign-policy-controller - - name: sget - run: echo -n "${{secrets.COSIGN_PASSWORD}}" | KO_PREFIX=gcr.io/projectsigstore/cosign/ci make sign-sget diff --git a/.github/workflows/cross.yaml b/.github/workflows/cross.yaml deleted file mode 100644 index 84cc33ee4..000000000 --- a/.github/workflows/cross.yaml +++ /dev/null @@ -1,74 +0,0 @@ -on: - push: - branches: - - main - - release-* - pull_request: - -permissions: read-all - -name: Cross -jobs: - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [macos-latest, ubuntu-latest, windows-latest] - include: - - os: macos-latest - COSIGN_TARGET: cosign-darwin-amd64 - SGET_TARGET: sget-darwin-amd64 - COSIGN_PASSWORD: COSIGN_PASSWORD - - os: ubuntu-latest - COSIGN_TARGET: cosign-linux-amd64 - SGET_TARGET: sget-linux-amd64 - COSIGN_PASSWORD: COSIGN_PASSWORD - - os: windows-latest - COSIGN_TARGET: cosign-windows-amd64.exe - SGET_TARGET: sget-windows-amd64.exe - COSIGN_PASSWORD: COSIGN_PASSWORD - steps: - - name: Install Go - uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923 # v2.2.0 - with: - go-version: '1.17' - check-latest: true - - name: Checkout code - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v2.4.0 - - name: build cosign - run: | - make cosign && mv ./cosign ./${{matrix.COSIGN_TARGET}} - make sget && mv ./sget ./${{matrix.SGET_TARGET}} - - name: Create checksum file - shell: pwsh - run: | - $hash=Get-FileHash -Path ./${{matrix.COSIGN_TARGET}} - Write-Output $($hash.Hash + " " + $(([io.fileinfo]$hash.path).basename)) | Tee-Object -Path ${{matrix.COSIGN_TARGET}}.sha256 - $hash=Get-FileHash -Path ./${{matrix.SGET_TARGET}} - Write-Output $($hash.Hash + " " + $(([io.fileinfo]$hash.path).basename)) | Tee-Object -Path ${{matrix.SGET_TARGET}}.sha256 - - name: sign - shell: bash - env: - COSIGN_PASSWORD: ${{secrets[matrix.COSIGN_PASSWORD]}} - if: github.event_name != 'pull_request' - run: | - ./${{matrix.COSIGN_TARGET}} sign-blob --key ./.github/workflows/cosign-test.key ./${{matrix.COSIGN_TARGET}} > ${{matrix.COSIGN_TARGET}}.sig - ./${{matrix.COSIGN_TARGET}} sign-blob --key ./.github/workflows/cosign-test.key ./${{matrix.SGET_TARGET}} > ${{matrix.SGET_TARGET}}.sig - - name: verify - if: github.event_name != 'pull_request' - run: | - ./${{matrix.COSIGN_TARGET}} verify-blob --key ./.github/workflows/cosign-test.pub --signature ${{matrix.COSIGN_TARGET}}.sig ./${{matrix.COSIGN_TARGET}} - ./${{matrix.COSIGN_TARGET}} verify-blob --key ./.github/workflows/cosign-test.pub --signature ${{matrix.SGET_TARGET}}.sig ./${{matrix.SGET_TARGET}} - - name: Upload artifacts - if: github.event_name != 'pull_request' - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v2.3.1 - with: - name: artifacts - path: | - cosign-* - cosign.-*sha256 - cosign-*.sig - sget-* - sget.-*sha256 - sget-*.sig - diff --git a/.github/workflows/cut-release.yml b/.github/workflows/cut-release.yml index b00ac50bf..e1acbb8e8 100644 --- a/.github/workflows/cut-release.yml +++ b/.github/workflows/cut-release.yml @@ -31,5 +31,5 @@ jobs: key_name: ${{ github.event.inputs.key_name }} workload_identity_provider: 'projects/498091336538/locations/global/workloadIdentityPools/githubactions/providers/sigstore-cosign' service_account: 'github-actions-cosign@projectsigstore.iam.gserviceaccount.com' - repo: 'cosign' + repo: 'policy-controller' diff --git a/.github/workflows/e2e-with-binary.yml b/.github/workflows/e2e-with-binary.yml deleted file mode 100644 index e29db64ca..000000000 --- a/.github/workflows/e2e-with-binary.yml +++ /dev/null @@ -1,67 +0,0 @@ -# -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: e2e-with-binary - -# Run on every push, and allow it to be run manually. -on: - push: - branches: [ 'main' ] - workflow_dispatch: - -jobs: - e2e-tests-with-binary: - # Skip if running in a fork that might not have secrets configured. - if: ${{ github.repository == 'sigstore/cosign' }} - name: Run tests - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [macos-latest, ubuntu-latest, windows-latest] - - permissions: - id-token: write - contents: read - env: - COSIGN_EXPERIMENTAL: "true" - - steps: - - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v2.4.0 - - uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923 # v2.2.0 - with: - go-version: '1.17' - check-latest: true - - name: build cosign and check - shell: bash - run: | - set -e - make cosign - ./cosign sign-blob --output-certificate key.pem --output-signature README.md.sig README.md - - if [ -s key.pem ] - then - echo "all good for key.pem" - else - echo "file does not exist, or is empty" - exit 1 - fi - - if [ -s README.md.sig ] - then - exit 0 - else - echo "file does not exist, or is empty" - exit 1 - fi diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_tests.yml deleted file mode 100644 index 3bd4e9113..000000000 --- a/.github/workflows/e2e_tests.yml +++ /dev/null @@ -1,51 +0,0 @@ -# -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: e2e-tests - -# Run on every push, and allow it to be run manually. -on: [push, workflow_dispatch] - -permissions: read-all - -jobs: - e2e-tests: - # Skip if running in a fork that might not have secrets configured. - if: ${{ github.repository == 'sigstore/cosign' }} - name: Run tests - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [macos-latest, ubuntu-latest, windows-latest] - - steps: - - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v2.4.0 - - uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923 # v2.2.0 - with: - go-version: '1.17' - check-latest: true - - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@877d4953d2c70a0ba7ef3290ae968eb24af233bb # v0.5.1 - with: - project_id: projectsigstore - service_account_key: ${{ secrets.GCP_CI_SERVICE_ACCOUNT }} - export_default_credentials: true - - name: Set up `crane` - run: go install github.com/google/go-containerregistry/cmd/crane@v0.8.0 - - name: gcloud auth configure-docker - run: gcloud auth configure-docker us-central1-docker.pkg.dev --quiet - - name: Run e2e_test_secrets.sh - shell: bash - run: ./test/e2e_test_secrets.sh diff --git a/.github/workflows/github-oidc.yaml b/.github/workflows/github-oidc.yaml deleted file mode 100644 index 81f31cc4b..000000000 --- a/.github/workflows/github-oidc.yaml +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: Test GitHub OIDC -on: - push: - branches: [ 'main', 'release-*' ] - schedule: - - cron: '0 1 * * *' # 1AM UTC - workflow_dispatch: - -jobs: - build: - permissions: - id-token: write - packages: write - contents: read - env: - COSIGN_EXPERIMENTAL: "true" - GIT_HASH: ${{ github.sha }} - GIT_VERSION: unstable - GITHUB_RUN_ID: ${{ github.run_id }} - GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }} - KO_PREFIX: ghcr.io/${{ github.repository }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v2.4.0 - - uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923 # v2.2.0 - with: - go-version: '1.17' - check-latest: true - - # Install tools. - - uses: sigstore/cosign-installer@536b37ec5d5b543420bdfd9b744c5965bd4d8730 # v2.3.0 - - uses: imjasonh/setup-ko@2c3450ca27f6e6f2b02e72a40f2163c281a1f675 # v0.4 - - - name: Build and sign a container image - run: | - set -e - # Build and publish an image. - make sign-keyless-container - - - name: Build and sign a blob - run: | - set -e - make cosign - make sign-blob-experimental diff --git a/.github/workflows/kind-cluster-image-policy-with-attestations.yaml b/.github/workflows/kind-cluster-image-policy-with-attestations.yaml index c8695553b..86106b1bd 100644 --- a/.github/workflows/kind-cluster-image-policy-with-attestations.yaml +++ b/.github/workflows/kind-cluster-image-policy-with-attestations.yaml @@ -67,9 +67,9 @@ jobs: with: mirror: mirror.gcr.io - - name: build cosign + - name: install cosign run: | - make cosign + go install ./cmd/cosign - name: Install cluster + cosign uses: sigstore/scaffolding/actions/setup@main diff --git a/.github/workflows/kind-cluster-image-policy.yaml b/.github/workflows/kind-cluster-image-policy.yaml index 274381ed9..622d08322 100644 --- a/.github/workflows/kind-cluster-image-policy.yaml +++ b/.github/workflows/kind-cluster-image-policy.yaml @@ -67,9 +67,9 @@ jobs: with: mirror: mirror.gcr.io - - name: build cosign + - name: install cosign run: | - make cosign + go install ./cmd/cosign - name: Install cluster + cosign uses: sigstore/scaffolding/actions/setup@main diff --git a/.github/workflows/kind-e2e-cosigned.yaml b/.github/workflows/kind-e2e-cosigned.yaml index 378f5aab6..c048eca12 100644 --- a/.github/workflows/kind-e2e-cosigned.yaml +++ b/.github/workflows/kind-e2e-cosigned.yaml @@ -112,11 +112,6 @@ jobs: # Wait for the webhook to come up and become Ready kubectl rollout status --timeout 5m --namespace cosign-system deployments/webhook - - name: Run Insecure Registry Tests - run: | - go install github.com/google/go-containerregistry/cmd/crane - ./test/e2e_test_insecure_registry.sh - - name: Run Image Policy Tests run: | ./test/e2e_test_policy_crd.sh diff --git a/.github/workflows/kind-verify-attestation.yaml b/.github/workflows/kind-verify-attestation.yaml deleted file mode 100644 index 889422eb9..000000000 --- a/.github/workflows/kind-verify-attestation.yaml +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright 2022 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: Test attest / verify-attestation - -on: - pull_request: - branches: [ 'main', 'release-*' ] - -defaults: - run: - shell: bash - -permissions: read-all - -jobs: - cip-test: - name: attest / verify-attestation test - runs-on: ubuntu-latest - - strategy: - matrix: - k8s-version: - - v1.21.x - - v1.22.x - # Try without this one now, might have problems with job restartings - # may require upstream changes. - #- v1.23.x - - env: - KNATIVE_VERSION: "1.1.0" - KO_DOCKER_REPO: "registry.local:5000/policy-controller" - SCAFFOLDING_RELEASE_VERSION: "v0.2.2" - GO111MODULE: on - GOFLAGS: -ldflags=-s -ldflags=-w - KOCACHE: ~/ko - # Trust the custom Rekor API endpoint for fetching the Public Key from it. - SIGSTORE_TRUST_REKOR_API_PUBLIC_KEY: "true" - # We are only testing keyless here, so set it. - COSIGN_EXPERIMENTAL: "true" - - steps: - - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v2.4.0 - - uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923 # v2.2.0 - with: - go-version: '1.17' - check-latest: true - - # will use the latest release available for ko - - uses: imjasonh/setup-ko@2c3450ca27f6e6f2b02e72a40f2163c281a1f675 # v0.4 - - - name: Install yq - uses: mikefarah/yq@70403375d7b96075bd68b40c434807cff1593568 # v4.25.1 - - - name: build cosign - run: | - make cosign - - - name: Install cluster + cosign - uses: sigstore/scaffolding/actions/setup@main - - - name: Create sample image - demoimage - run: | - pushd $(mktemp -d) - go mod init example.com/demo - cat < main.go - package main - import "fmt" - func main() { - fmt.Println("hello world") - } - EOF - demoimage=`ko publish -B example.com/demo` - echo "demoimage=$demoimage" >> $GITHUB_ENV - echo Created image $demoimage - popd - - - name: Sign demoimage with cosign - run: | - ./cosign sign --rekor-url ${{ env.REKOR_URL }} --fulcio-url ${{ env.FULCIO_URL }} --force --allow-insecure-registry ${{ env.demoimage }} --identity-token ${{ env.OIDC_TOKEN }} - - - name: Create attestation for it - run: | - echo -n 'foobar e2e test' > ./predicate-file - ./cosign attest --predicate ./predicate-file --fulcio-url ${{ env.FULCIO_URL }} --rekor-url ${{ env.REKOR_URL }} --allow-insecure-registry --force ${{ env.demoimage }} --identity-token ${{ env.OIDC_TOKEN }} - - - name: Verify with cosign - run: | - ./cosign verify --rekor-url ${{ env.REKOR_URL }} --allow-insecure-registry ${{ env.demoimage }} - - - name: Verify custom attestation with cosign, works - run: | - echo '::group:: test custom verify-attestation success' - if ! ./cosign verify-attestation --policy ./test/testdata/policies/cue-works.cue --rekor-url ${{ env.REKOR_URL }} --allow-insecure-registry ${{ env.demoimage }} ; then - echo Failed to verify attestation with a valid policy - exit 1 - else - echo Successfully validated custom attestation with a valid policy - fi - echo '::endgroup::' - - - name: Verify custom attestation with cosign, fails - run: | - echo '::group:: test custom verify-attestation success' - if ./cosign verify-attestation --policy ./test/testdata/policies/cue-fails.cue --rekor-url ${{ env.REKOR_URL }} --allow-insecure-registry ${{ env.demoimage }} ; then - echo custom verify-attestation succeeded with cue policy that should not work - exit 1 - else - echo Successfully failed a policy that should not work - fi - echo '::endgroup::' - - - name: Collect diagnostics - if: ${{ failure() }} - uses: chainguard-dev/actions/kind-diag@84c993eaf02da1c325854fb272a4df9184bd80fc # main - - - name: Create vuln attestation for it - run: | - ./cosign attest --predicate ./test/testdata/attestations/vuln-predicate.json --type vuln --fulcio-url ${{ env.FULCIO_URL }} --rekor-url ${{ env.REKOR_URL }} --allow-insecure-registry --force ${{ env.demoimage }} --identity-token ${{ env.OIDC_TOKEN }} - - - name: Verify vuln attestation with cosign, works - run: | - echo '::group:: test vuln verify-attestation success' - if ! ./cosign verify-attestation --type vuln --policy ./test/testdata/policies/cue-vuln-works.cue --rekor-url ${{ env.REKOR_URL }} --allow-insecure-registry ${{ env.demoimage }} ; then - echo Failed to verify attestation with a valid policy - exit 1 - else - echo Successfully validated vuln attestation with a valid policy - fi - echo '::endgroup::' - - - name: Verify vuln attestation with cosign, fails - run: | - echo '::group:: test vuln verify-attestation success' - if ./cosign verify-attestation --type vuln --policy ./test/testdata/policies/cue-vuln-fails.cue --rekor-url ${{ env.REKOR_URL }} --allow-insecure-registry ${{ env.demoimage }} ; then - echo verify-attestation succeeded with cue policy that should not work - exit 1 - else - echo Successfully failed a policy that should not work - fi - echo '::endgroup::' diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 41beb330a..fd7225918 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -33,7 +33,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-latest, ubuntu-latest, windows-latest] + os: [ubuntu-latest] env: OS: ${{ matrix.os }} @@ -45,8 +45,6 @@ jobs: # In order: # * Module download cache # * Build cache (Linux) - # * Build cache (Mac) - # * Build cache (Windows) path: | ~/go/pkg/mod ~/.cache/go-build @@ -69,72 +67,6 @@ jobs: if: ${{ runner.os == 'Linux' }} run: go test -race $(go list ./... | grep -v third_party/) - e2e-tests: - name: Run e2e tests - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v2.4.0 - # https://github.com/mvdan/github-actions-golang#how-do-i-set-up-caching-between-builds - - uses: actions/cache@c3f1317a9e7b1ef106c153ac8c0f00fed3ddbc0d # v3.0.3 - with: - # In order: - # * Module download cache - # * Build cache (Linux) - # * Build cache (Mac) - # * Build cache (Windows) - path: | - ~/go/pkg/mod - ~/.cache/go-build - ~/Library/Caches/go-build - %LocalAppData%\go-build - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923 # v2.1.5 - with: - go-version: ${{ env.GO_VERSION }} - check-latest: true - - uses: imjasonh/setup-ko@2c3450ca27f6e6f2b02e72a40f2163c281a1f675 # v0.4 - - name: setup kind cluster - run: | - # Used to test: cosign generate-key-pair k8s://... - go install sigs.k8s.io/kind@v0.11.1 - kind create cluster - - - name: Run end-to-end tests - run: ./test/e2e_test.sh - - - name: Collect diagnostics - if: ${{ failure() }} - uses: chainguard-dev/actions/kind-diag@84c993eaf02da1c325854fb272a4df9184bd80fc # main - - e2e-windows-powershell-tests: - name: Run PowerShell E2E tests - runs-on: windows-latest - steps: - - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v2.4.0 - - uses: actions/setup-go@b22fbbc2921299758641fab08929b4ac52b32923 # v2.1.5 - with: - go-version: ${{ env.GO_VERSION }} - check-latest: true - - # https://github.com/mvdan/github-actions-golang#how-do-i-set-up-caching-between-builds - - uses: actions/cache@c3f1317a9e7b1ef106c153ac8c0f00fed3ddbc0d # v3.0.3 - with: - # In order: - # * Module download cache - # * Build cache (Windows) - path: | - ~/go/pkg/mod - %LocalAppData%\go-build - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Run e2e_test.ps1 - run: ./test/e2e_test.ps1 - license-check: name: license boilerplate check runs-on: ubuntu-latest diff --git a/.github/workflows/validate-release.yml b/.github/workflows/validate-release.yml deleted file mode 100644 index ebcd11fcf..000000000 --- a/.github/workflows/validate-release.yml +++ /dev/null @@ -1,73 +0,0 @@ -# -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: CI-Validate-Release-Job - -on: - push: - branches: - - main - - release-* - pull_request: - -jobs: - validate-release-job: - runs-on: ubuntu-latest - - permissions: - actions: none - checks: none - contents: none - deployments: none - issues: none - packages: none - pull-requests: none - repository-projects: none - security-events: none - statuses: none - - env: - CROSS_BUILDER_IMAGE: ghcr.io/gythialy/golang-cross:v1.17.11-0@sha256:b0c3083b0a420ff649d5a5bfd7e0a96719ab0a7b3f99a049a4950d836a72637a - COSIGN_IMAGE: gcr.io/projectsigstore/cosign:v1.8.0@sha256:12b4d428529654c95a7550a936cbb5c6fe93a046ea7454676cb6fb0ce566d78c - - steps: - - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b #v2.4.0 - - - name: Check Signature - run: | - docker run --rm \ - -e COSIGN_EXPERIMENTAL=true \ - -e TUF_ROOT=/tmp \ - $COSIGN_IMAGE \ - verify \ - $CROSS_BUILDER_IMAGE - - - name: goreleaser snapshot - run: | - docker run --rm --privileged \ - -e PROJECT_ID=honk-fake-project \ - -e CI=$CI \ - -e RUNTIME_IMAGE=gcr.io/distroless/static:debug-nonroot \ - -v ${PWD}:/go/src/sigstore/cosign \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -w /go/src/sigstore/cosign \ - --entrypoint="" \ - $CROSS_BUILDER_IMAGE \ - make snapshot - - - name: check binaries - run: | - ./dist/cosign-linux-amd64 version - ./dist/sget-linux-amd64 version diff --git a/.goreleaser.yml b/.goreleaser.yml deleted file mode 100644 index 8e30cde0a..000000000 --- a/.goreleaser.yml +++ /dev/null @@ -1,248 +0,0 @@ -project_name: cosign - -env: - - GO111MODULE=on - - CGO_ENABLED=1 - - DOCKER_CLI_EXPERIMENTAL=enabled - - COSIGN_EXPERIMENTAL=true - - LATEST_TAG=,latest - -# Prevents parallel builds from stepping on each others toes downloading modules -before: - hooks: - - go mod tidy - - /bin/bash -c 'if [ -n "$(git --no-pager diff --exit-code go.mod go.sum)" ]; then exit 1; fi' -# if running a release we will generate the images in this step -# if running in the CI the CI env va is set and we dont run the ko steps -# this is needed because we are generating files that goreleaser was not aware to push to GH project release - - /bin/bash -c 'if [ -z "$CI" ]; then make sign-release-images; fi' - -gomod: - proxy: true - -sboms: -- artifacts: binary - -builds: -- id: linux - binary: cosign-linux-{{ .Arch }} - no_unique_dist_dir: true - main: ./cmd/cosign - flags: - - -trimpath - mod_timestamp: '{{ .CommitTimestamp }}' - goos: - - linux - goarch: - - amd64 - - arm64 - - arm - - s390x - - ppc64le - goarm: - - '7' - ldflags: - - "{{ .Env.LDFLAGS }}" - env: - - CGO_ENABLED=0 - -- id: linux-pivkey-pkcs11key-amd64 - binary: cosign-linux-pivkey-pkcs11key-amd64 - no_unique_dist_dir: true - main: ./cmd/cosign - flags: - - -trimpath - mod_timestamp: '{{ .CommitTimestamp }}' - goos: - - linux - goarch: - - amd64 - ldflags: - - "{{ .Env.LDFLAGS }}" - tags: - - pivkey - - pkcs11key - hooks: - pre: - - apt-get update - - apt-get -y install libpcsclite-dev - env: - - PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig/" - -- id: darwin-amd64 - binary: cosign-darwin-amd64 - no_unique_dist_dir: true - env: - - CC=o64-clang - - CXX=o64-clang++ - main: ./cmd/cosign - flags: - - -trimpath - mod_timestamp: '{{ .CommitTimestamp }}' - goos: - - darwin - goarch: - - amd64 - ldflags: - - "{{ .Env.LDFLAGS }}" - tags: - - pivkey - - pkcs11key - -- id: darwin-arm64 - binary: cosign-darwin-arm64 - no_unique_dist_dir: true - env: - - CC=aarch64-apple-darwin20.2-clang - - CXX=aarch64-apple-darwin20.2-clang++ - main: ./cmd/cosign - flags: - - -trimpath - goos: - - darwin - goarch: - - arm64 - tags: - - pivkey - - pkcs11key - ldflags: - - "{{.Env.LDFLAGS}}" - -- id: windows-amd64 - binary: cosign-windows-amd64 - no_unique_dist_dir: true - env: - - CC=x86_64-w64-mingw32-gcc - - CXX=x86_64-w64-mingw32-g++ - main: ./cmd/cosign - mod_timestamp: '{{ .CommitTimestamp }}' - flags: - - -trimpath - goos: - - windows - goarch: - - amd64 - ldflags: - - -buildmode=exe - - "{{ .Env.LDFLAGS }}" - tags: - - pivkey - - pkcs11key - -- id: sget - binary: sget-{{ .Os }}-{{ .Arch }} - no_unique_dist_dir: true - mod_timestamp: '{{ .CommitTimestamp }}' - main: ./cmd/sget - flags: - - -trimpath - goos: - - linux - - darwin - - windows - goarch: - - amd64 - - arm64 - - arm - - s390x - - ppc64le - goarm: - - 7 - ignore: - - goos: windows - goarch: arm64 - - goos: windows - goarch: arm - - goos: windows - goarch: s390x - - goos: windows - goarch: ppc64le - ldflags: - - "{{ .Env.LDFLAGS }}" - env: - - CGO_ENABLED=0 - -signs: - - id: cosign - signature: "${artifact}.sig" - cmd: ./dist/cosign-linux-amd64 - args: ["sign-blob", "--output-signature", "${artifact}.sig", "--key", "gcpkms://projects/{{ .Env.PROJECT_ID }}/locations/{{ .Env.KEY_LOCATION }}/keyRings/{{ .Env.KEY_RING }}/cryptoKeys/{{ .Env.KEY_NAME }}/versions/{{ .Env.KEY_VERSION }}", "${artifact}"] - artifacts: binary - - id: sget - signature: "${artifact}.sig" - cmd: ./dist/cosign-linux-amd64 - args: ["sign-blob", "--output-signature", "${artifact}.sig", "--key", "gcpkms://projects/{{ .Env.PROJECT_ID }}/locations/{{ .Env.KEY_LOCATION }}/keyRings/{{ .Env.KEY_RING }}/cryptoKeys/{{ .Env.KEY_NAME }}/versions/{{ .Env.KEY_VERSION }}", "${artifact}"] - artifacts: binary - ids: - - sget - # Keyless - - id: cosign-keyless - signature: "${artifact}-keyless.sig" - certificate: "${artifact}-keyless.pem" - cmd: ./dist/cosign-linux-amd64 - args: ["sign-blob", "--output-signature", "${artifact}-keyless.sig", "--output-certificate", "${artifact}-keyless.pem", "${artifact}"] - artifacts: binary - - id: sget-keyless - signature: "${artifact}-keyless.sig" - certificate: "${artifact}-keyless.pem" - cmd: ./dist/cosign-linux-amd64 - args: ["sign-blob", "--output-signature", "${artifact}-keyless.sig", "--output-certificate", "${artifact}-keyless.pem", "${artifact}"] - artifacts: binary - ids: - - sget - - id: checksum-keyless - signature: "${artifact}-keyless.sig" - certificate: "${artifact}-keyless.pem" - cmd: ./dist/cosign-linux-amd64 - args: ["sign-blob", "--output-signature", "${artifact}-keyless.sig", "--output-certificate", "${artifact}-keyless.pem", "${artifact}"] - artifacts: checksum - - id: packages-keyless - signature: "${artifact}-keyless.sig" - certificate: "${artifact}-keyless.pem" - cmd: ./dist/cosign-linux-amd64 - args: ["sign-blob", "--output-signature", "${artifact}-keyless.sig", "--output-certificate", "${artifact}-keyless.pem", "${artifact}"] - artifacts: package - -nfpms: - - id: cosign - package_name: cosign - file_name_template: "{{ .ConventionalFileName }}" - vendor: Sigstore - homepage: https://sigstore.dev - maintainer: Sigstore Authors 86837369+sigstore-bot@users.noreply.github.com - builds: - - linux - description: Container Signing, Verification and Storage in an OCI registry. - license: "Apache License 2.0" - formats: - - apk - - deb - - rpm - contents: - - src: /usr/bin/cosign-linux-{{ .Arch }} - dst: /usr/bin/cosign - type: "symlink" - -archives: -- format: binary - name_template: "{{ .Binary }}" - allow_different_binary_count: true - -checksum: - name_template: "{{ .ProjectName }}_checksums.txt" - -snapshot: - name_template: SNAPSHOT-{{ .ShortCommit }} - -release: - prerelease: allow # remove this when we start publishing non-prerelease or set to auto - draft: true # allow for manual edits - github: - owner: sigstore - name: cosign - footer: | - ### Thanks to all contributors! - - extra_files: - - glob: "./release/release-cosign.pub" - - glob: "./cosign*.yaml" diff --git a/test/ci.mk b/test/ci.mk index 5e144d121..030cedd03 100644 --- a/test/ci.mk +++ b/test/ci.mk @@ -2,36 +2,15 @@ # signing ci ############ -.PHONY: sign-container -sign-container: ko - cosign sign --key .github/workflows/cosign-test.key -a GIT_HASH=$(GIT_HASH) ${KO_PREFIX}/cosign:$(GIT_HASH) - .PHONY: sign-policy-controller sign-policy-controller: cosign sign --key .github/workflows/cosign-test.key -a GIT_HASH=$(GIT_HASH) ${KO_PREFIX}/policy-controller:$(GIT_HASH) -.PHONY: sign-sget -sign-sget: - cosign sign --key .github/workflows/cosign-test.key -a GIT_HASH=$(GIT_HASH) ${KO_PREFIX}/sget:$(GIT_HASH) - -.PHONY: sign-keyless-cosign -sign-keyless-cosign: - cosign sign -a sha=$(GIT_HASH) -a run_id=${GITHUB_RUN_ID} -a run_attempt=${GITHUB_RUN_ATTEMPT} ${KO_PREFIX}/cosign:$(GIT_HASH) - cosign sign -a sha=$(GIT_HASH) -a run_id=${GITHUB_RUN_ID} -a run_attempt=${GITHUB_RUN_ATTEMPT} ${KO_PREFIX}/cosign:$(GIT_VERSION) - .PHONY: sign-keyless-policy-controller sign-keyless-policy-controller: cosign sign -a sha=$(GIT_HASH) -a run_id=${GITHUB_RUN_ID} -a run_attempt=${GITHUB_RUN_ATTEMPT} ${KO_PREFIX}/policy-controller:$(GIT_HASH) cosign sign -a sha=$(GIT_HASH) -a run_id=${GITHUB_RUN_ID} -a run_attempt=${GITHUB_RUN_ATTEMPT} ${KO_PREFIX}/policy-controller:$(GIT_VERSION) -.PHONY: sign-keyless-sget -sign-keyless-sget: - cosign sign -a sha=$(GIT_HASH) -a run_id=${GITHUB_RUN_ID} -a run_attempt=${GITHUB_RUN_ATTEMPT} ${KO_PREFIX}/sget:$(GIT_HASH) - cosign sign -a sha=$(GIT_HASH) -a run_id=${GITHUB_RUN_ID} -a run_attempt=${GITHUB_RUN_ATTEMPT} ${KO_PREFIX}/sget:$(GIT_VERSION) .PHONY: sign-keyless-container -sign-keyless-container: ko sign-keyless-cosign sign-keyless-policy-controller sign-keyless-sget - -.PHONY: sign-blob-experimental -sign-blob-experimental: - ./test/sign_blob_test.sh +sign-keyless-container: ko sign-keyless-policy-controller diff --git a/test/e2e_test.go b/test/e2e_test.go deleted file mode 100644 index 75c7701f8..000000000 --- a/test/e2e_test.go +++ /dev/null @@ -1,1250 +0,0 @@ -// -// Copyright 2021 The Sigstore Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build e2e -// +build e2e - -package test - -import ( - "bytes" - "context" - "crypto" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "net/http/httptest" - "net/url" - "os" - "path" - "path/filepath" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-containerregistry/pkg/authn" - "github.com/google/go-containerregistry/pkg/name" - "github.com/google/go-containerregistry/pkg/registry" - "github.com/google/go-containerregistry/pkg/v1/random" - "github.com/google/go-containerregistry/pkg/v1/remote" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/sigstore/cosign/cmd/cosign/cli" - "github.com/sigstore/cosign/cmd/cosign/cli/attach" - "github.com/sigstore/cosign/cmd/cosign/cli/attest" - "github.com/sigstore/cosign/cmd/cosign/cli/download" - "github.com/sigstore/cosign/cmd/cosign/cli/generate" - "github.com/sigstore/cosign/cmd/cosign/cli/options" - "github.com/sigstore/cosign/cmd/cosign/cli/publickey" - "github.com/sigstore/cosign/cmd/cosign/cli/sign" - "github.com/sigstore/cosign/cmd/cosign/cli/upload" - cliverify "github.com/sigstore/cosign/cmd/cosign/cli/verify" - "github.com/sigstore/cosign/pkg/cosign" - "github.com/sigstore/cosign/pkg/cosign/kubernetes" - cremote "github.com/sigstore/cosign/pkg/cosign/remote" - "github.com/sigstore/cosign/pkg/oci/mutate" - ociremote "github.com/sigstore/cosign/pkg/oci/remote" - "github.com/sigstore/cosign/pkg/sget" - sigs "github.com/sigstore/cosign/pkg/signature" - "github.com/sigstore/sigstore/pkg/signature/payload" -) - -const ( - serverEnv = "REKOR_SERVER" - rekorURL = "https://rekor.sigstore.dev" -) - -var keyPass = []byte("hello") - -var passFunc = func(_ bool) ([]byte, error) { - return keyPass, nil -} - -var verify = func(keyRef, imageRef string, checkClaims bool, annotations map[string]interface{}, attachment string) error { - cmd := cliverify.VerifyCommand{ - KeyRef: keyRef, - RekorURL: rekorURL, - CheckClaims: checkClaims, - Annotations: sigs.AnnotationsMap{Annotations: annotations}, - Attachment: attachment, - HashAlgorithm: crypto.SHA256, - } - - args := []string{imageRef} - - return cmd.Exec(context.Background(), args) -} - -// Used to verify local images stored on disk -var verifyLocal = func(keyRef, path string, checkClaims bool, annotations map[string]interface{}, attachment string) error { - cmd := cliverify.VerifyCommand{ - KeyRef: keyRef, - CheckClaims: checkClaims, - Annotations: sigs.AnnotationsMap{Annotations: annotations}, - Attachment: attachment, - HashAlgorithm: crypto.SHA256, - LocalImage: true, - } - - args := []string{path} - - return cmd.Exec(context.Background(), args) -} - -var ro = &options.RootOptions{Timeout: options.DefaultTimeout} - -func TestSignVerify(t *testing.T) { - repo, stop := reg(t) - defer stop() - td := t.TempDir() - - imgName := path.Join(repo, "cosign-e2e") - - _, _, cleanup := mkimage(t, imgName) - defer cleanup() - - _, privKeyPath, pubKeyPath := keypair(t, td) - - ctx := context.Background() - // Verify should fail at first - mustErr(verify(pubKeyPath, imgName, true, nil, ""), t) - // So should download - mustErr(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t) - - // Now sign the image - ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - - // Now verify and download should work! - must(verify(pubKeyPath, imgName, true, nil, ""), t) - must(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t) - - // Look for a specific annotation - mustErr(verify(pubKeyPath, imgName, true, map[string]interface{}{"foo": "bar"}, ""), t) - - // Sign the image with an annotation - annotations := map[string]interface{}{"foo": "bar"} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, annotations, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - - // It should match this time. - must(verify(pubKeyPath, imgName, true, map[string]interface{}{"foo": "bar"}, ""), t) - - // But two doesn't work - mustErr(verify(pubKeyPath, imgName, true, map[string]interface{}{"foo": "bar", "baz": "bat"}, ""), t) -} - -func TestSignVerifyClean(t *testing.T) { - repo, stop := reg(t) - defer stop() - td := t.TempDir() - - imgName := path.Join(repo, "cosign-e2e") - - _, _, _ = mkimage(t, imgName) - - _, privKeyPath, pubKeyPath := keypair(t, td) - - ctx := context.Background() - - // Now sign the image - ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - - // Now verify and download should work! - must(verify(pubKeyPath, imgName, true, nil, ""), t) - must(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t) - - // Now clean signature from the given image - must(cli.CleanCmd(ctx, options.RegistryOptions{}, "all", imgName, true), t) - - // It doesn't work - mustErr(verify(pubKeyPath, imgName, true, nil, ""), t) -} - -func TestImportSignVerifyClean(t *testing.T) { - - repo, stop := reg(t) - defer stop() - td := t.TempDir() - - imgName := path.Join(repo, "cosign-e2e") - - _, _, _ = mkimage(t, imgName) - - _, privKeyPath, pubKeyPath := importKeyPair(t, td) - - ctx := context.Background() - - // Now sign the image - ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - - // Now verify and download should work! - must(verify(pubKeyPath, imgName, true, nil, ""), t) - must(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t) - - // Now clean signature from the given image - must(cli.CleanCmd(ctx, options.RegistryOptions{}, "all", imgName, true), t) - - // It doesn't work - mustErr(verify(pubKeyPath, imgName, true, nil, ""), t) -} - -func TestAttestVerify(t *testing.T) { - repo, stop := reg(t) - defer stop() - td := t.TempDir() - - imgName := path.Join(repo, "cosign-attest-e2e") - - _, _, cleanup := mkimage(t, imgName) - defer cleanup() - - _, privKeyPath, pubKeyPath := keypair(t, td) - - ctx := context.Background() - - // Verify should fail at first - verifyAttestation := cliverify.VerifyAttestationCommand{ - KeyRef: pubKeyPath, - } - - // Fail case when using without type and policy flag - mustErr(verifyAttestation.Exec(ctx, []string{imgName}), t) - - slsaAttestation := `{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }` - slsaAttestationPath := filepath.Join(td, "attestation.slsa.json") - if err := os.WriteFile(slsaAttestationPath, []byte(slsaAttestation), 0600); err != nil { - t.Fatal(err) - } - - // Now attest the image - ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false, - "slsaprovenance", false, 30*time.Second), t) - - // Use cue to verify attestation - policyPath := filepath.Join(td, "policy.cue") - verifyAttestation.PredicateType = "slsaprovenance" - verifyAttestation.Policies = []string{policyPath} - - // Fail case - cuePolicy := `builder: id: "1"` - if err := os.WriteFile(policyPath, []byte(cuePolicy), 0600); err != nil { - t.Fatal(err) - } - - // Success case - cuePolicy = `builder: id: "2"` - if err := os.WriteFile(policyPath, []byte(cuePolicy), 0600); err != nil { - t.Fatal(err) - } - must(verifyAttestation.Exec(ctx, []string{imgName}), t) - - // Look for a specific annotation - mustErr(verify(pubKeyPath, imgName, true, map[string]interface{}{"foo": "bar"}, ""), t) -} - -func TestAttestationReplace(t *testing.T) { - // This test is currently failing, see https://github.com/sigstore/cosign/issues/1378 - // The replace option for attest does not appear to work on random.Image() generated images - t.Skip() - - repo, stop := reg(t) - defer stop() - td := t.TempDir() - - imgName := path.Join(repo, "cosign-attest-replace-e2e") - - _, _, cleanup := mkimage(t, imgName) - defer cleanup() - - _, privKeyPath, _ := keypair(t, td) - ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - - ctx := context.Background() - - slsaAttestation := `{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }` - slsaAttestationPath := filepath.Join(td, "attestation.slsa.json") - if err := os.WriteFile(slsaAttestationPath, []byte(slsaAttestation), 0600); err != nil { - t.Fatal(err) - } - - // Attest once with with replace=false creating an attestation - must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false, - "slsaprovenance", false, 30*time.Second), t) - // Attest again with replace=true, replacing the previous attestation - must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false, - "slsaprovenance", true, 30*time.Second), t) - // Attest once more replace=true using a different predicate, to ensure it adds a new attestation - must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false, - "custom", true, 30*time.Second), t) - - // Download and count the attestations - ref, err := name.ParseReference(imgName) - if err != nil { - t.Fatal(err) - } - regOpts := options.RegistryOptions{} - ociremoteOpts, err := regOpts.ClientOpts(ctx) - if err != nil { - t.Fatal(err) - } - attestations, err := cosign.FetchAttestationsForReference(ctx, ref, ociremoteOpts...) - if err != nil { - t.Fatal(err) - } - if len(attestations) != 2 { - t.Fatal(fmt.Errorf("Expected len(attestations) == 2, got %d", len(attestations))) - } -} - -func TestRekorBundle(t *testing.T) { - // turn on the tlog - defer setenv(t, options.ExperimentalEnv, "1")() - - repo, stop := reg(t) - defer stop() - td := t.TempDir() - - imgName := path.Join(repo, "cosign-e2e") - - _, _, cleanup := mkimage(t, imgName) - defer cleanup() - - _, privKeyPath, pubKeyPath := keypair(t, td) - - ko := options.KeyOpts{ - KeyRef: privKeyPath, - PassFunc: passFunc, - RekorURL: rekorURL, - } - - // Sign the image - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - // Make sure verify works - must(verify(pubKeyPath, imgName, true, nil, ""), t) - - // Make sure offline verification works with bundling - // use rekor prod since we have hardcoded the public key - os.Setenv(serverEnv, "notreal") - must(verify(pubKeyPath, imgName, true, nil, ""), t) -} - -func TestDuplicateSign(t *testing.T) { - repo, stop := reg(t) - defer stop() - td := t.TempDir() - - imgName := path.Join(repo, "cosign-e2e") - - ref, _, cleanup := mkimage(t, imgName) - defer cleanup() - - _, privKeyPath, pubKeyPath := keypair(t, td) - - ctx := context.Background() - // Verify should fail at first - mustErr(verify(pubKeyPath, imgName, true, nil, ""), t) - // So should download - mustErr(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t) - - // Now sign the image - ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - - // Now verify and download should work! - must(verify(pubKeyPath, imgName, true, nil, ""), t) - must(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t) - - // Signing again should work just fine... - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - - se, err := ociremote.SignedEntity(ref, ociremote.WithRemoteOptions(registryClientOpts(ctx)...)) - must(err, t) - sigs, err := se.Signatures() - must(err, t) - signatures, err := sigs.Get() - must(err, t) - - if len(signatures) > 1 { - t.Errorf("expected there to only be one signature, got %v", signatures) - } -} - -func TestKeyURLVerify(t *testing.T) { - // TODO: re-enable once distroless images are being signed by the new client - t.Skip() - // Verify that an image can be verified via key url - keyRef := "https://raw.githubusercontent.com/GoogleContainerTools/distroless/main/cosign.pub" - img := "gcr.io/distroless/base:latest" - - must(verify(keyRef, img, true, nil, ""), t) -} - -func TestGenerateKeyPairEnvVar(t *testing.T) { - defer setenv(t, "COSIGN_PASSWORD", "foo")() - keys, err := cosign.GenerateKeyPair(generate.GetPass) - if err != nil { - t.Fatal(err) - } - if _, err := cosign.LoadPrivateKey(keys.PrivateBytes, []byte("foo")); err != nil { - t.Fatal(err) - } -} - -func TestGenerateKeyPairK8s(t *testing.T) { - td := t.TempDir() - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - if err := os.Chdir(td); err != nil { - t.Fatal(err) - } - defer func() { - os.Chdir(wd) - }() - password := "foo" - defer setenv(t, "COSIGN_PASSWORD", password)() - ctx := context.Background() - name := "cosign-secret" - namespace := "default" - if err := kubernetes.KeyPairSecret(ctx, fmt.Sprintf("k8s://%s/%s", namespace, name), generate.GetPass); err != nil { - t.Fatal(err) - } - // make sure the secret actually exists - client, err := kubernetes.Client() - if err != nil { - t.Fatal(err) - } - s, err := client.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{}) - if err != nil { - t.Fatal(err) - } - if v, ok := s.Data["cosign.password"]; !ok || string(v) != password { - t.Fatalf("password is incorrect, got %v expected %v", v, "foo") - } -} - -func TestMultipleSignatures(t *testing.T) { - repo, stop := reg(t) - defer stop() - - td1 := t.TempDir() - td2 := t.TempDir() - - imgName := path.Join(repo, "cosign-e2e") - - _, _, cleanup := mkimage(t, imgName) - defer cleanup() - - _, priv1, pub1 := keypair(t, td1) - _, priv2, pub2 := keypair(t, td2) - - // Verify should fail at first for both keys - mustErr(verify(pub1, imgName, true, nil, ""), t) - mustErr(verify(pub2, imgName, true, nil, ""), t) - - // Now sign the image with one key - ko := options.KeyOpts{KeyRef: priv1, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - // Now verify should work with that one, but not the other - must(verify(pub1, imgName, true, nil, ""), t) - mustErr(verify(pub2, imgName, true, nil, ""), t) - - // Now sign with the other key too - ko.KeyRef = priv2 - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - - // Now verify should work with both - must(verify(pub1, imgName, true, nil, ""), t) - must(verify(pub2, imgName, true, nil, ""), t) -} - -func TestSignBlob(t *testing.T) { - blob := "someblob" - td1 := t.TempDir() - td2 := t.TempDir() - t.Cleanup(func() { - os.RemoveAll(td1) - os.RemoveAll(td2) - }) - bp := filepath.Join(td1, blob) - - if err := os.WriteFile(bp, []byte(blob), 0644); err != nil { - t.Fatal(err) - } - - _, privKeyPath1, pubKeyPath1 := keypair(t, td1) - _, _, pubKeyPath2 := keypair(t, td2) - - ctx := context.Background() - - ko1 := options.KeyOpts{ - KeyRef: pubKeyPath1, - } - ko2 := options.KeyOpts{ - KeyRef: pubKeyPath2, - } - // Verify should fail on a bad input - mustErr(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certOidcIssuer*/, "" /*certChain*/, "badsig", blob, false), t) - mustErr(cliverify.VerifyBlobCmd(ctx, ko2, "" /*certRef*/, "" /*certEmail*/, "" /*certOidcIssuer*/, "" /*certChain*/, "badsig", blob, false), t) - - // Now sign the blob with one key - ko := options.KeyOpts{ - KeyRef: privKeyPath1, - PassFunc: passFunc, - } - sig, err := sign.SignBlobCmd(ro, ko, options.RegistryOptions{}, bp, true, "", "") - if err != nil { - t.Fatal(err) - } - // Now verify should work with that one, but not the other - must(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certOidcIssuer*/, "" /*certChain*/, string(sig), bp, false), t) - mustErr(cliverify.VerifyBlobCmd(ctx, ko2, "" /*certRef*/, "" /*certEmail*/, "" /*certOidcIssuer*/, "" /*certChain*/, string(sig), bp, false), t) -} - -func TestSignBlobBundle(t *testing.T) { - blob := "someblob" - td1 := t.TempDir() - t.Cleanup(func() { - os.RemoveAll(td1) - }) - bp := filepath.Join(td1, blob) - bundlePath := filepath.Join(td1, "bundle.sig") - - if err := os.WriteFile(bp, []byte(blob), 0644); err != nil { - t.Fatal(err) - } - - _, privKeyPath1, pubKeyPath1 := keypair(t, td1) - - ctx := context.Background() - - ko1 := options.KeyOpts{ - KeyRef: pubKeyPath1, - BundlePath: bundlePath, - } - // Verify should fail on a bad input - mustErr(cliverify.VerifyBlobCmd(ctx, ko1, "", "", "", "", "", blob, false), t) - - // Now sign the blob with one key - ko := options.KeyOpts{ - KeyRef: privKeyPath1, - PassFunc: passFunc, - BundlePath: bundlePath, - RekorURL: rekorURL, - } - if _, err := sign.SignBlobCmd(ro, ko, options.RegistryOptions{}, bp, true, "", ""); err != nil { - t.Fatal(err) - } - // Now verify should work - must(cliverify.VerifyBlobCmd(ctx, ko1, "", "", "", "", "", bp, false), t) - - // Now we turn on the tlog and sign again - defer setenv(t, options.ExperimentalEnv, "1")() - if _, err := sign.SignBlobCmd(ro, ko, options.RegistryOptions{}, bp, true, "", ""); err != nil { - t.Fatal(err) - } - - // Point to a fake rekor server to make sure offline verification of the tlog entry works - os.Setenv(serverEnv, "notreal") - must(cliverify.VerifyBlobCmd(ctx, ko1, "", "", "", "", "", bp, false), t) -} - -func TestGenerate(t *testing.T) { - repo, stop := reg(t) - defer stop() - - imgName := path.Join(repo, "cosign-e2e") - _, desc, cleanup := mkimage(t, imgName) - defer cleanup() - - // Generate the payload for the image, and check the digest. - b := bytes.Buffer{} - must(generate.GenerateCmd(context.Background(), options.RegistryOptions{}, imgName, nil, &b), t) - ss := payload.SimpleContainerImage{} - must(json.Unmarshal(b.Bytes(), &ss), t) - - equals(desc.Digest.String(), ss.Critical.Image.DockerManifestDigest, t) - - // Now try with some annotations. - b.Reset() - a := map[string]interface{}{"foo": "bar"} - must(generate.GenerateCmd(context.Background(), options.RegistryOptions{}, imgName, a, &b), t) - must(json.Unmarshal(b.Bytes(), &ss), t) - - equals(desc.Digest.String(), ss.Critical.Image.DockerManifestDigest, t) - equals(ss.Optional["foo"], "bar", t) -} - -func keypair(t *testing.T, td string) (*cosign.KeysBytes, string, string) { - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - if err := os.Chdir(td); err != nil { - t.Fatal(err) - } - defer func() { - os.Chdir(wd) - }() - keys, err := cosign.GenerateKeyPair(passFunc) - if err != nil { - t.Fatal(err) - } - - privKeyPath := filepath.Join(td, "cosign.key") - if err := os.WriteFile(privKeyPath, keys.PrivateBytes, 0600); err != nil { - t.Fatal(err) - } - - pubKeyPath := filepath.Join(td, "cosign.pub") - if err := os.WriteFile(pubKeyPath, keys.PublicBytes, 0600); err != nil { - t.Fatal(err) - } - return keys, privKeyPath, pubKeyPath -} - -func importKeyPair(t *testing.T, td string) (*cosign.KeysBytes, string, string) { - - const validrsa1 = `-----BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAx5piWVlE62NnZ0UzJ8Z6oKiKOC4dbOZ1HsNhIRtqkM+Oq4G+ -25yq6P+0JU/Qvr9veOGEb3R/J9u8JBo+hv2i5X8OtgvP2V2pi6f1s6vK7L0+6uRb -4YTT/UdMshaVf97MgEqbq41Jf/cuvh+3AV0tZ1BpixZg4aXMKpY6HUP69lbsu27o -SUN1myMv7TSgZiV4CYs3l/gkEfpysBptWlcHRuw5RsB+C0RbjRtbJ/5VxmE/vd3M -lafd5t1WSpMb8yf0a84u5NFaXwZ7CweMfXeOddS0yb19ShSuW3PPRadruBM1mq15 -js9GfagPxDS75Imcs+fA62lWvHxEujTGjYHxawIDAQABAoIBAH+sgLwmHa9zJfEo -klAe5NFe/QpydN/ziXbkAnzqzH9URC3wD+TpkWj4JoK3Sw635NWtasjf+3XDV9S/ -9L7j/g5N91r6sziWcJykEsWaXXKQmm4lI6BdFjwsHyLKz1W7bZOiJXDWLu1rbrqu -DqEQuLoc9WXCKrYrFy0maoXNtfla/1p05kKN0bMigcnnyAQ+xBTwoyco4tkIz5se -IYxorz7qzXrkHQI+knz5BawmNe3ekoSaXUPoLoOR7TRTGsLteL5yukvWAi8S/0rE -gftC+PZCQpoQhSUYq7wXe7RowJ1f+kXb7HsSedOTfTSW1D/pUb/uW+CcRKig42ZI -I9H9TAECgYEA5XGBML6fJyWVqx64sHbUAjQsmQ0RwU6Zo7sqHIEPf6tYVYp7KtzK -KOfi8seOOL5FSy4pjCo11Dzyrh9bn45RNmtjSYTgOnVPSoCfuRNfOcpG+/wCHjYf -EjDvdrCpbg59kVUeaMeBDiyWAlM48HJAn8O7ez2U/iKQCyJmOIwFhSkCgYEA3rSz -Fi1NzqYWxWos4NBmg8iKcQ9SMkmPdgRLAs/WNnZJ8fdgJZwihevkXGytRGJEmav2 -GMKRx1g6ey8fjXTQH9WM8X/kJC5fv8wLHnUCH/K3Mcp9CYwn7PFvSnBr4kQoc/el -bURhcF1+/opEC8vNX/Wk3zAG7Xs1PREXlH2SIHMCgYBV/3kgwBH/JkM25EjtO1yz -hsLAivmAruk/SUO7c1RP0fVF+qW3pxHOyztxLALOmeJ3D1JbSubqKf377Zz17O3b -q9yHDdrNjnKtxhAX2n7ytjJs+EQC9t4mf1kB761RpvTBqFnBhCWHHocLUA4jcW9v -cnmu86IIrwO2aKpPv4vCIQKBgHU9gY3qOazRSOmSlJ+hdmZn+2G7pBTvHsQNTIPl -cCrpqNHl3crO4GnKHkT9vVVjuiOAIKU2QNJFwzu4Og8Y8LvhizpTjoHxm9x3iV72 -UDELcJ+YrqyJCTe2flUcy96o7Pbn50GXnwgtYD6WAW6IUszyn2ITgYIhu4wzZEt6 -s6O7AoGAPTKbRA87L34LMlXyUBJma+etMARIP1zu8bXJ7hSJeMcog8zaLczN7ruT -pGAaLxggvtvuncMuTrG+cdmsR9SafSFKRS92NCxhOUonQ+NP6mLskIGzJZoQ5JvQ -qGzRVIDGbNkrVHM0IsAtHRpC0rYrtZY+9OwiraGcsqUMLwwQdCA= ------END RSA PRIVATE KEY-----` - - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - if err := os.Chdir(td); err != nil { - t.Fatal(err) - } - defer func() { - os.Chdir(wd) - }() - - err = os.WriteFile("validrsa1.key", []byte(validrsa1), 0600) - if err != nil { - t.Fatal(err) - } - - keys, err := cosign.ImportKeyPair("validrsa1.key", passFunc) - if err != nil { - t.Fatal(err) - } - - privKeyPath := filepath.Join(td, "import-cosign.key") - if err := os.WriteFile(privKeyPath, keys.PrivateBytes, 0600); err != nil { - t.Fatal(err) - } - - pubKeyPath := filepath.Join(td, "import-cosign.pub") - if err := os.WriteFile(pubKeyPath, keys.PublicBytes, 0600); err != nil { - t.Fatal(err) - } - return keys, privKeyPath, pubKeyPath - -} - -func TestUploadDownload(t *testing.T) { - repo, stop := reg(t) - defer stop() - td := t.TempDir() - ctx := context.Background() - - testCases := map[string]struct { - signature string - signatureType attach.SignatureArgType - expectedErr bool - }{ - "file containing signature": { - signature: "testsignaturefile", - signatureType: attach.FileSignature, - expectedErr: false, - }, - "raw signature as argument": { - signature: "testsignatureraw", - signatureType: attach.RawSignature, - expectedErr: false, - }, - "empty signature as argument": { - signature: "", - signatureType: attach.RawSignature, - expectedErr: true, - }, - } - - imgName := path.Join(repo, "cosign-e2e") - for testName, testCase := range testCases { - t.Run(testName, func(t *testing.T) { - ref, _, cleanup := mkimage(t, imgName) - payload := "testpayload" - payloadPath := mkfile(payload, td, t) - signature := base64.StdEncoding.EncodeToString([]byte(testCase.signature)) - - var sigRef string - if testCase.signatureType == attach.FileSignature { - sigRef = mkfile(signature, td, t) - } else { - sigRef = signature - } - - // Upload it! - err := attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadPath, imgName) - if testCase.expectedErr { - mustErr(err, t) - } else { - must(err, t) - } - - // Now download it! - se, err := ociremote.SignedEntity(ref, ociremote.WithRemoteOptions(registryClientOpts(ctx)...)) - must(err, t) - sigs, err := se.Signatures() - must(err, t) - signatures, err := sigs.Get() - must(err, t) - - if testCase.expectedErr { - if len(signatures) != 0 { - t.Fatalf("unexpected signatures %d, wanted 0", len(signatures)) - } - } else { - if len(signatures) != 1 { - t.Fatalf("unexpected signatures %d, wanted 1", len(signatures)) - } - - if b64sig, err := signatures[0].Base64Signature(); err != nil { - t.Fatalf("Base64Signature() = %v", err) - } else if diff := cmp.Diff(b64sig, signature); diff != "" { - t.Error(diff) - } - - if p, err := signatures[0].Payload(); err != nil { - t.Fatalf("Payload() = %v", err) - } else if diff := cmp.Diff(p, []byte(payload)); diff != "" { - t.Error(diff) - } - } - - // Now delete it! - cleanup() - }) - } -} - -func TestUploadBlob(t *testing.T) { - repo, stop := reg(t) - defer stop() - td := t.TempDir() - ctx := context.Background() - - imgName := path.Join(repo, "/cosign-upload-e2e") - payload := "testpayload" - payloadPath := mkfile(payload, td, t) - - // Upload it! - files := []cremote.File{cremote.FileFromFlag(payloadPath)} - must(upload.BlobCmd(ctx, options.RegistryOptions{}, files, "", imgName), t) - - // Check it - ref, err := name.ParseReference(imgName) - if err != nil { - t.Fatal(err) - } - - // Now download it with sget (this should fail by tag) - if err := sget.New(imgName, "", os.Stdout).Do(ctx); err == nil { - t.Error("expected download to fail") - } - - img, err := remote.Image(ref) - if err != nil { - t.Fatal(err) - } - dgst, err := img.Digest() - if err != nil { - t.Fatal(err) - } - - result := &bytes.Buffer{} - - // But pass by digest - if err := sget.New(imgName+"@"+dgst.String(), "", result).Do(ctx); err != nil { - t.Fatal(err) - } - b, err := io.ReadAll(result) - if err != nil { - t.Fatal(err) - } - if string(b) != payload { - t.Errorf("expected contents to be %s, got %s", payload, string(b)) - } -} - -func TestSaveLoad(t *testing.T) { - tests := []struct { - description string - getSignedEntity func(t *testing.T, n string) (name.Reference, *remote.Descriptor, func()) - }{ - { - description: "save and load an image", - getSignedEntity: mkimage, - }, - { - description: "save and load an image index", - getSignedEntity: mkimageindex, - }, - } - for i, test := range tests { - t.Run(test.description, func(t *testing.T) { - repo, stop := reg(t) - defer stop() - keysDir := t.TempDir() - - imgName := path.Join(repo, fmt.Sprintf("save-load-%d", i)) - - _, _, cleanup := test.getSignedEntity(t, imgName) - defer cleanup() - - _, privKeyPath, pubKeyPath := keypair(t, keysDir) - - ctx := context.Background() - // Now sign the image and verify it - ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - must(verify(pubKeyPath, imgName, true, nil, ""), t) - - // save the image to a temp dir - imageDir := t.TempDir() - must(cli.SaveCmd(ctx, options.SaveOptions{Directory: imageDir}, imgName), t) - - // verify the local image using a local key - must(verifyLocal(pubKeyPath, imageDir, true, nil, ""), t) - - // load the image from the temp dir into a new image and verify the new image - imgName2 := path.Join(repo, fmt.Sprintf("save-load-%d-2", i)) - must(cli.LoadCmd(ctx, options.LoadOptions{Directory: imageDir}, imgName2), t) - must(verify(pubKeyPath, imgName2, true, nil, ""), t) - }) - } -} - -func TestSaveLoadAttestation(t *testing.T) { - repo, stop := reg(t) - defer stop() - td := t.TempDir() - - imgName := path.Join(repo, "save-load") - - _, _, cleanup := mkimage(t, imgName) - defer cleanup() - - _, privKeyPath, pubKeyPath := keypair(t, td) - - ctx := context.Background() - // Now sign the image and verify it - ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - must(verify(pubKeyPath, imgName, true, nil, ""), t) - - // now, append an attestation to the image - slsaAttestation := `{ "builder": { "id": "2" }, "recipe": {} }` - slsaAttestationPath := filepath.Join(td, "attestation.slsa.json") - if err := os.WriteFile(slsaAttestationPath, []byte(slsaAttestation), 0600); err != nil { - t.Fatal(err) - } - - // Now attest the image - ko = options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc} - must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false, - "custom", false, 30*time.Second), t) - - // save the image to a temp dir - imageDir := t.TempDir() - must(cli.SaveCmd(ctx, options.SaveOptions{Directory: imageDir}, imgName), t) - - // load the image from the temp dir into a new image and verify the new image - imgName2 := path.Join(repo, "save-load-2") - must(cli.LoadCmd(ctx, options.LoadOptions{Directory: imageDir}, imgName2), t) - must(verify(pubKeyPath, imgName2, true, nil, ""), t) - // Use cue to verify attestation on the new image - policyPath := filepath.Join(td, "policy.cue") - verifyAttestation := cliverify.VerifyAttestationCommand{ - KeyRef: pubKeyPath, - } - verifyAttestation.PredicateType = "slsaprovenance" - verifyAttestation.Policies = []string{policyPath} - // Success case (remote) - cuePolicy := `builder: id: "2"` - if err := os.WriteFile(policyPath, []byte(cuePolicy), 0600); err != nil { - t.Fatal(err) - } - must(verifyAttestation.Exec(ctx, []string{imgName2}), t) - // Success case (local) - verifyAttestation.LocalImage = true - must(verifyAttestation.Exec(ctx, []string{imageDir}), t) -} - -func TestAttachSBOM(t *testing.T) { - repo, stop := reg(t) - defer stop() - ctx := context.Background() - - imgName := path.Join(repo, "sbom-image") - img, _, cleanup := mkimage(t, imgName) - defer cleanup() - - out := bytes.Buffer{} - _, err := download.SBOMCmd(ctx, options.RegistryOptions{}, img.Name(), &out) - if err == nil { - t.Fatal("Expected error") - } - t.Log(out.String()) - out.Reset() - - // Upload it! - must(attach.SBOMCmd(ctx, options.RegistryOptions{}, "./testdata/bom-go-mod.spdx", "spdx", imgName), t) - - sboms, err := download.SBOMCmd(ctx, options.RegistryOptions{}, imgName, &out) - if err != nil { - t.Fatal(err) - } - t.Log(out.String()) - if len(sboms) != 1 { - t.Fatalf("Expected one sbom, got %d", len(sboms)) - } - want, err := os.ReadFile("./testdata/bom-go-mod.spdx") - if err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(string(want), sboms[0]); diff != "" { - t.Errorf("diff: %s", diff) - } - - // Generate key pairs to sign the sbom - td1 := t.TempDir() - td2 := t.TempDir() - _, privKeyPath1, pubKeyPath1 := keypair(t, td1) - _, _, pubKeyPath2 := keypair(t, td2) - - // Verify should fail on a bad input - mustErr(verify(pubKeyPath1, imgName, true, nil, "sbom"), t) - mustErr(verify(pubKeyPath2, imgName, true, nil, "sbom"), t) - - // Now sign the sbom with one key - ko1 := options.KeyOpts{KeyRef: privKeyPath1, PassFunc: passFunc} - must(sign.SignCmd(ro, ko1, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, "sbom"), t) - - // Now verify should work with that one, but not the other - must(verify(pubKeyPath1, imgName, true, nil, "sbom"), t) - mustErr(verify(pubKeyPath2, imgName, true, nil, "sbom"), t) -} - -func setenv(t *testing.T, k, v string) func() { - if err := os.Setenv(k, v); err != nil { - t.Fatalf("error setting env: %v", err) - } - return func() { - os.Unsetenv(k) - } -} - -func TestTlog(t *testing.T) { - repo, stop := reg(t) - defer stop() - td := t.TempDir() - - imgName := path.Join(repo, "cosign-e2e") - - _, _, cleanup := mkimage(t, imgName) - defer cleanup() - - _, privKeyPath, pubKeyPath := keypair(t, td) - - // Verify should fail at first - mustErr(verify(pubKeyPath, imgName, true, nil, ""), t) - - // Now sign the image without the tlog - ko := options.KeyOpts{ - KeyRef: privKeyPath, - PassFunc: passFunc, - RekorURL: rekorURL, - } - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - - // Now verify should work! - must(verify(pubKeyPath, imgName, true, nil, ""), t) - - // Now we turn on the tlog! - defer setenv(t, options.ExperimentalEnv, "1")() - - // Verify shouldn't work since we haven't put anything in it yet. - mustErr(verify(pubKeyPath, imgName, true, nil, ""), t) - - // Sign again with the tlog env var on - must(sign.SignCmd(ro, ko, options.RegistryOptions{}, nil, []string{imgName}, "", "", true, "", "", "", false, false, ""), t) - // And now verify works! - must(verify(pubKeyPath, imgName, true, nil, ""), t) -} - -func TestGetPublicKeyCustomOut(t *testing.T) { - td := t.TempDir() - keys, privKeyPath, _ := keypair(t, td) - ctx := context.Background() - - outFile := "output.pub" - outPath := filepath.Join(td, outFile) - outWriter, err := os.OpenFile(outPath, os.O_WRONLY|os.O_CREATE, 0600) - must(err, t) - - pk := publickey.Pkopts{ - KeyRef: privKeyPath, - } - must(publickey.GetPublicKey(ctx, pk, publickey.NamedWriter{Name: outPath, Writer: outWriter}, passFunc), t) - - output, err := os.ReadFile(outPath) - must(err, t) - equals(keys.PublicBytes, output, t) -} - -func mkfile(contents, td string, t *testing.T) string { - f, err := os.CreateTemp(td, "") - if err != nil { - t.Fatal(err) - } - defer f.Close() - if _, err := f.Write([]byte(contents)); err != nil { - t.Fatal(err) - } - return f.Name() -} - -func mkimage(t *testing.T, n string) (name.Reference, *remote.Descriptor, func()) { - ref, err := name.ParseReference(n, name.WeakValidation) - if err != nil { - t.Fatal(err) - } - img, err := random.Image(512, 5) - if err != nil { - t.Fatal(err) - } - - regClientOpts := registryClientOpts(context.Background()) - - if err := remote.Write(ref, img, regClientOpts...); err != nil { - t.Fatal(err) - } - - remoteImage, err := remote.Get(ref, regClientOpts...) - if err != nil { - t.Fatal(err) - } - - cleanup := func() { - _ = remote.Delete(ref, regClientOpts...) - ref, _ := ociremote.SignatureTag(ref.Context().Digest(remoteImage.Descriptor.Digest.String()), ociremote.WithRemoteOptions(regClientOpts...)) - _ = remote.Delete(ref, regClientOpts...) - } - return ref, remoteImage, cleanup -} - -func mkimageindex(t *testing.T, n string) (name.Reference, *remote.Descriptor, func()) { - ref, err := name.ParseReference(n, name.WeakValidation) - if err != nil { - t.Fatal(err) - } - ii, err := random.Index(512, 5, 4) - if err != nil { - t.Fatal(err) - } - - regClientOpts := registryClientOpts(context.Background()) - - if err := remote.WriteIndex(ref, ii, regClientOpts...); err != nil { - t.Fatal(err) - } - - remoteIndex, err := remote.Get(ref, regClientOpts...) - if err != nil { - t.Fatal(err) - } - - cleanup := func() { - _ = remote.Delete(ref, regClientOpts...) - ref, _ := ociremote.SignatureTag(ref.Context().Digest(remoteIndex.Descriptor.Digest.String()), ociremote.WithRemoteOptions(regClientOpts...)) - _ = remote.Delete(ref, regClientOpts...) - } - return ref, remoteIndex, cleanup -} - -func must(err error, t *testing.T) { - t.Helper() - if err != nil { - t.Fatal(err) - } -} - -func mustErr(err error, t *testing.T) { - t.Helper() - if err == nil { - t.Fatal("expected error") - } -} - -func equals(v1, v2 interface{}, t *testing.T) { - if diff := cmp.Diff(v1, v2); diff != "" { - t.Error(diff) - } -} - -func reg(t *testing.T) (string, func()) { - repo := os.Getenv("COSIGN_TEST_REPO") - if repo != "" { - return repo, func() {} - } - - t.Log("COSIGN_TEST_REPO unset, using fake registry") - r := httptest.NewServer(registry.New()) - u, err := url.Parse(r.URL) - if err != nil { - t.Fatal(err) - } - return u.Host, r.Close -} - -func registryClientOpts(ctx context.Context) []remote.Option { - return []remote.Option{ - remote.WithAuthFromKeychain(authn.DefaultKeychain), - remote.WithContext(ctx), - } -} - -// If a signature has a bundle, but *not for that signature*, cosign verification should fail -// This test is pretty long, so here are the basic points: -// 1. Sign image1 with a keypair, store entry in rekor -// 2. Sign image2 with keypair, DO NOT store entry in rekor -// 3. Take the bundle from image1 and store it on the signature in image2 -// 4. Verification of image2 should now fail, since the bundle is for a different signature -func TestInvalidBundle(t *testing.T) { - regName, stop := reg(t) - defer stop() - td := t.TempDir() - - img1 := path.Join(regName, "cosign-e2e") - - imgRef, _, cleanup := mkimage(t, img1) - defer cleanup() - - _, privKeyPath, pubKeyPath := keypair(t, td) - - ctx := context.Background() - - // Sign image1 and store the entry in rekor - // (we're just using it for its bundle) - defer setenv(t, options.ExperimentalEnv, "1")() - remoteOpts := ociremote.WithRemoteOptions(registryClientOpts(ctx)...) - ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc, RekorURL: rekorURL} - regOpts := options.RegistryOptions{} - - must(sign.SignCmd(ro, ko, regOpts, nil, []string{img1}, "", "", true, "", "", "", true, false, ""), t) - // verify image1 - must(verify(pubKeyPath, img1, true, nil, ""), t) - // extract the bundle from image1 - si, err := ociremote.SignedImage(imgRef, remoteOpts) - must(err, t) - imgSigs, err := si.Signatures() - must(err, t) - sigs, err := imgSigs.Get() - must(err, t) - if l := len(sigs); l != 1 { - t.Error("expected one signature") - } - bund, err := sigs[0].Bundle() - must(err, t) - if bund == nil { - t.Fail() - } - - // Now, we move on to image2 - // Sign image2 and DO NOT store the entry in rekor - defer setenv(t, options.ExperimentalEnv, "0")() - img2 := path.Join(regName, "unrelated") - imgRef2, _, cleanup := mkimage(t, img2) - defer cleanup() - must(sign.SignCmd(ro, ko, regOpts, nil, []string{img2}, "", "", true, "", "", "", false, false, ""), t) - must(verify(pubKeyPath, img2, true, nil, ""), t) - - si2, err := ociremote.SignedEntity(imgRef2, remoteOpts) - must(err, t) - sigs2, err := si2.Signatures() - must(err, t) - gottenSigs2, err := sigs2.Get() - must(err, t) - if len(gottenSigs2) != 1 { - t.Fatal("there should be one signature") - } - sigsTag, err := ociremote.SignatureTag(imgRef2) - if err != nil { - t.Fatal(err) - } - - // At this point, we would mutate the signature to add the bundle annotation - // since we don't have a function for it at the moment, mock this by deleting the signature - // and pushing a new signature with the additional bundle annotation - if err := remote.Delete(sigsTag); err != nil { - t.Fatal(err) - } - mustErr(verify(pubKeyPath, img2, true, nil, ""), t) - - newSig, err := mutate.Signature(gottenSigs2[0], mutate.WithBundle(bund)) - must(err, t) - si2, err = ociremote.SignedEntity(imgRef2, remoteOpts) - must(err, t) - newImage, err := mutate.AttachSignatureToEntity(si2, newSig) - must(err, t) - if err := ociremote.WriteSignatures(sigsTag.Repository, newImage); err != nil { - t.Fatal(err) - } - - // veriyfing image2 now should fail - mustErr(verify(pubKeyPath, img2, true, nil, ""), t) -} diff --git a/test/e2e_test.ps1 b/test/e2e_test.ps1 deleted file mode 100644 index 76d1a85fb..000000000 --- a/test/e2e_test.ps1 +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -function New-TmpDir { - $parent = [System.IO.Path]::GetTempPath() - $name = [System.IO.Path]::GetRandomFileName() - New-Item -ItemType Directory -Path (Join-Path $parent $name) -} - -make cosign -$TmpDir = New-TmpDir -Copy-Item -Path .\cosign -Destination (Join-Path $TmpDir cosign.exe) - -Push-Location $TmpDir - -# See if things blow up immediately -.\cosign.exe version - -# Generate a random alphanumeric password for the private key -$pass = Get-Random - -Write-Output $pass | .\cosign.exe generate-key-pair -$signing_key = "cosign.key" -$verification_key = "cosign.pub" - -$test_img = "ghcr.io/distroless/static" -Write-Output $pass | .\cosign.exe sign --key $signing_key --output-signature interactive.sig $test_img -.\cosign.exe verify --key $verification_key --signature interactive.sig $test_img - -Pop-Location - -Write-Output "Success" diff --git a/test/e2e_test.sh b/test/e2e_test.sh deleted file mode 100755 index 594eba650..000000000 --- a/test/e2e_test.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -ex - -echo "copying rekor repo" -pushd $HOME -git clone https://github.com/sigstore/rekor.git -cd rekor - -echo "starting services" -docker-compose up -d - -count=0 - -echo -n "waiting up to 60 sec for system to start" -until [ $(docker-compose ps | grep -c "(healthy)") == 3 ]; -do - if [ $count -eq 6 ]; then - echo "! timeout reached" - exit 1 - else - echo -n "." - sleep 10 - let 'count+=1' - fi -done - -echo -echo "running tests" - -popd -go build -o cosign ./cmd/cosign -go test -tags=e2e -race $(go list ./... | grep -v third_party/) - -# Test `cosign dockerfile verify` -export COSIGN_EXPERIMENTAL=true -./cosign dockerfile verify ./test/testdata/single_stage.Dockerfile -if (./cosign dockerfile verify ./test/testdata/unsigned_build_stage.Dockerfile); then false; fi -./cosign dockerfile verify --base-image-only ./test/testdata/unsigned_build_stage.Dockerfile -./cosign dockerfile verify ./test/testdata/fancy_from.Dockerfile -test_image="ghcr.io/distroless/alpine-base" ./cosign dockerfile verify ./test/testdata/with_arg.Dockerfile -# Image exists, but is unsigned -if (test_image="ubuntu" ./cosign dockerfile verify ./test/testdata/with_arg.Dockerfile); then false; fi -./cosign dockerfile verify ./test/testdata/with_lowercase.Dockerfile - -# Test `cosign manifest verify` -./cosign manifest verify ./test/testdata/signed_manifest.yaml -if (./cosign manifest verify ./test/testdata/unsigned_manifest.yaml); then false; fi - -# Run the built container to make sure it doesn't crash -make ko-local -img="ko.local/cosign:$(git rev-parse HEAD)" -docker run $img version - -echo "cleanup" -cd $HOME/rekor -docker-compose down diff --git a/test/e2e_test_cluster_image_policy.sh b/test/e2e_test_cluster_image_policy.sh index 49c9d02c9..7d8f2ada7 100755 --- a/test/e2e_test_cluster_image_policy.sh +++ b/test/e2e_test_cluster_image_policy.sh @@ -117,11 +117,11 @@ kubectl apply -f ./test/testdata/policy-controller/e2e/cip-keyless.yaml echo '::endgroup::' echo '::group:: Sign demo image' -COSIGN_EXPERIMENTAL=1 ./cosign sign --rekor-url ${REKOR_URL} --fulcio-url ${FULCIO_URL} --force --allow-insecure-registry ${demoimage} --identity-token ${OIDC_TOKEN} +COSIGN_EXPERIMENTAL=1 cosign sign --rekor-url ${REKOR_URL} --fulcio-url ${FULCIO_URL} --force --allow-insecure-registry ${demoimage} --identity-token ${OIDC_TOKEN} echo '::endgroup::' echo '::group:: Verify demo image' -COSIGN_EXPERIMENTAL=1 ./cosign verify --rekor-url ${REKOR_URL} --allow-insecure-registry ${demoimage} +COSIGN_EXPERIMENTAL=1 cosign verify --rekor-url ${REKOR_URL} --allow-insecure-registry ${demoimage} echo '::endgroup::' echo '::group:: Create test namespace and label for verification' @@ -184,7 +184,7 @@ sleep 5 echo '::endgroup::' echo '::group:: Generate New Signing Key For Colocated Signature' -COSIGN_PASSWORD="" ./cosign generate-key-pair +COSIGN_PASSWORD="" cosign generate-key-pair mv cosign.key cosign-colocated-signing.key mv cosign.pub cosign-colocated-signing.pub echo '::endgroup::' @@ -207,11 +207,11 @@ fi echo '::endgroup::' echo '::group:: Sign demoimage with cosign key' -COSIGN_PASSWORD="" ./cosign sign --key cosign-colocated-signing.key --force --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage} +COSIGN_PASSWORD="" cosign sign --key cosign-colocated-signing.key --force --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage} echo '::endgroup::' echo '::group:: Verify demoimage with cosign key' -./cosign verify --key cosign-colocated-signing.pub --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage} +cosign verify --key cosign-colocated-signing.pub --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage} echo '::endgroup::' echo '::group:: test job success' @@ -235,7 +235,7 @@ fi echo '::endgroup::' echo '::group:: Generate New Signing Key For Remote Signature' -COSIGN_PASSWORD="" ./cosign generate-key-pair +COSIGN_PASSWORD="" cosign generate-key-pair mv cosign.key cosign-remote-signing.key mv cosign.pub cosign-remote-signing.pub echo '::endgroup::' @@ -248,16 +248,16 @@ yq '. | .metadata.name = "image-policy-remote-source" echo '::endgroup::' echo '::group:: Sign demoimage with cosign remote key' -COSIGN_PASSWORD="" COSIGN_REPOSITORY="${KO_DOCKER_REPO}/remote-signature" ./cosign sign --key cosign-remote-signing.key --force --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage} +COSIGN_PASSWORD="" COSIGN_REPOSITORY="${KO_DOCKER_REPO}/remote-signature" cosign sign --key cosign-remote-signing.key --force --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage} echo '::endgroup::' echo '::group:: Verify demoimage with cosign remote key' -if ./cosign verify --key cosign-remote-signing.pub --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage}; then +if cosign verify --key cosign-remote-signing.pub --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage}; then echo "Signature should not have been verified unless COSIGN_REPOSITORY was defined" exit 1 fi -if ! COSIGN_REPOSITORY="${KO_DOCKER_REPO}/remote-signature" ./cosign verify --key cosign-remote-signing.pub --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage}; then +if ! COSIGN_REPOSITORY="${KO_DOCKER_REPO}/remote-signature" cosign verify --key cosign-remote-signing.pub --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage}; then echo "Signature should have been verified when COSIGN_REPOSITORY was defined" exit 1 fi diff --git a/test/e2e_test_cluster_image_policy_with_attestations.sh b/test/e2e_test_cluster_image_policy_with_attestations.sh index c68d3413f..66b8dc75d 100755 --- a/test/e2e_test_cluster_image_policy_with_attestations.sh +++ b/test/e2e_test_cluster_image_policy_with_attestations.sh @@ -114,7 +114,7 @@ assert_error ${expected_error} echo '::endgroup::' echo '::group:: Sign demoimage with keyless' -COSIGN_EXPERIMENTAL=1 ./cosign sign --rekor-url ${REKOR_URL} --fulcio-url ${FULCIO_URL} --force --allow-insecure-registry ${demoimage} --identity-token ${OIDC_TOKEN} +COSIGN_EXPERIMENTAL=1 cosign sign --rekor-url ${REKOR_URL} --fulcio-url ${FULCIO_URL} --force --allow-insecure-registry ${demoimage} --identity-token ${OIDC_TOKEN} echo '::endgroup::' # This image has been signed, but does not have an attestation, so should fail. @@ -126,9 +126,9 @@ echo '::endgroup::' # Ok, cool. So attest and it should pass. echo '::group:: Create one keyless attestation and verify it' echo -n 'foobar e2e test' > ./predicate-file-custom -COSIGN_EXPERIMENTAL=1 ./cosign attest --predicate ./predicate-file-custom --fulcio-url ${FULCIO_URL} --rekor-url ${REKOR_URL} --allow-insecure-registry --force ${demoimage} --identity-token ${OIDC_TOKEN} +COSIGN_EXPERIMENTAL=1 cosign attest --predicate ./predicate-file-custom --fulcio-url ${FULCIO_URL} --rekor-url ${REKOR_URL} --allow-insecure-registry --force ${demoimage} --identity-token ${OIDC_TOKEN} -COSIGN_EXPERIMENTAL=1 ./cosign verify-attestation --type=custom --rekor-url ${REKOR_URL} --allow-insecure-registry ${demoimage} +COSIGN_EXPERIMENTAL=1 cosign verify-attestation --type=custom --rekor-url ${REKOR_URL} --allow-insecure-registry ${demoimage} echo '::endgroup::' echo '::group:: test job success' @@ -145,7 +145,7 @@ fi echo '::endgroup::' echo '::group:: Generate New Signing Key that we use for key-ful signing' -COSIGN_PASSWORD="" ./cosign generate-key-pair +COSIGN_PASSWORD="" cosign generate-key-pair echo '::endgroup::' # Ok, so now we have satisfied the keyless requirements, one signature, one @@ -165,11 +165,11 @@ echo '::endgroup::' # Sign it with key echo '::group:: Sign demoimage with key, and add to rekor' -COSIGN_EXPERIMENTAL=1 COSIGN_PASSWORD="" ./cosign sign --key cosign.key --force --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage} +COSIGN_EXPERIMENTAL=1 COSIGN_PASSWORD="" cosign sign --key cosign.key --force --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage} echo '::endgroup::' echo '::group:: Verify demoimage with cosign key' -COSIGN_EXPERIMENTAL=1 ./cosign verify --key cosign.pub --rekor-url ${REKOR_URL} --allow-insecure-registry ${demoimage} +COSIGN_EXPERIMENTAL=1 cosign verify --key cosign.pub --rekor-url ${REKOR_URL} --allow-insecure-registry ${demoimage} echo '::endgroup::' # This image has been signed with key, but does not have a key attestation @@ -182,9 +182,9 @@ echo '::endgroup::' # Fine, so create an attestation for it that's different from the keyless one echo '::group:: create keyful attestation, add add to rekor' echo -n 'foobar key e2e test' > ./predicate-file-key-custom -COSIGN_EXPERIMENTAL=1 COSIGN_PASSWORD="" ./cosign attest --predicate ./predicate-file-key-custom --rekor-url ${REKOR_URL} --key ./cosign.key --allow-insecure-registry --force ${demoimage} +COSIGN_EXPERIMENTAL=1 COSIGN_PASSWORD="" cosign attest --predicate ./predicate-file-key-custom --rekor-url ${REKOR_URL} --key ./cosign.key --allow-insecure-registry --force ${demoimage} -COSIGN_EXPERIMENTAL=1 ./cosign verify-attestation --key ./cosign.pub --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage} +COSIGN_EXPERIMENTAL=1 cosign verify-attestation --key ./cosign.pub --allow-insecure-registry --rekor-url ${REKOR_URL} ${demoimage} echo '::endgroup::' echo '::group:: test job success with key / keyless' @@ -219,9 +219,9 @@ assert_error ${expected_error} echo '::endgroup::' echo '::group:: Create vuln keyless attestation and verify it' -COSIGN_EXPERIMENTAL=1 ./cosign attest --predicate ./test/testdata/attestations/vuln-predicate.json --type=vuln --fulcio-url ${FULCIO_URL} --rekor-url ${REKOR_URL} --allow-insecure-registry --force ${demoimage} --identity-token ${OIDC_TOKEN} +COSIGN_EXPERIMENTAL=1 cosign attest --predicate ./test/testdata/attestations/vuln-predicate.json --type=vuln --fulcio-url ${FULCIO_URL} --rekor-url ${REKOR_URL} --allow-insecure-registry --force ${demoimage} --identity-token ${OIDC_TOKEN} -COSIGN_EXPERIMENTAL=1 ./cosign verify-attestation --type=vuln --rekor-url ${REKOR_URL} --allow-insecure-registry ${demoimage} +COSIGN_EXPERIMENTAL=1 cosign verify-attestation --type=vuln --rekor-url ${REKOR_URL} --allow-insecure-registry ${demoimage} echo '::endgroup::' echo '::group:: test job success' diff --git a/test/e2e_test_insecure_registry.sh b/test/e2e_test_insecure_registry.sh deleted file mode 100755 index 185ad6fc4..000000000 --- a/test/e2e_test_insecure_registry.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -ex - -go build -o cosign ./cmd/cosign -tmp=$(mktemp -d) -cp cosign $tmp/ - -INSECURE_REGISTRY_NAME=${INSECURE_REGISTRY_NAME:-insecure-registry.notlocal} -INSECURE_REGISTRY_PORT=${INSECURE_REGISTRY_PORT:-5001} - -pushd $tmp - -pass="$RANDOM" -export COSIGN_PASSWORD=$pass - -./cosign generate-key-pair -signing_key=cosign.key -verification_key=cosign.pub - -img="${INSECURE_REGISTRY_NAME}:${INSECURE_REGISTRY_PORT}/test" -(crane delete $(./cosign triangulate $img)) || true -crane cp ghcr.io/distroless/static $img --insecure - -# Operations with insecure registries should fail by default, then succeed -# with `--allow-insecure-registry` -if (./cosign sign --key ${signing_key} $img); then false; fi -./cosign sign --allow-insecure-registry --key ${signing_key} $img -if (./cosign verify --key ${verification_key} $img); then false; fi -./cosign verify --allow-insecure-registry --key ${verification_key} $img - -echo "SUCCESS" diff --git a/test/e2e_test_secrets.sh b/test/e2e_test_secrets.sh deleted file mode 100755 index 6b7aad8a6..000000000 --- a/test/e2e_test_secrets.sh +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -ex - -go build -o cosign ./cmd/cosign -go build -o sget ./cmd/sget -tmp=$(mktemp -d -t cosign-e2e-secrets.XXXX) -cp cosign $tmp/ -cp sget $tmp/ - -pushd $tmp - -pass="$RANDOM" -export COSIGN_PASSWORD=$pass - -BASE_TEST_REPO=${BASE_TEST_REPO:-us-central1-docker.pkg.dev/projectsigstore/cosign-ci} -TEST_INSTANCE_REPO="${BASE_TEST_REPO}/$(date +'%Y/%m/%d')/$RANDOM" - -# setup -./cosign generate-key-pair -signing_key=cosign.key -verification_key=cosign.pub -img="${TEST_INSTANCE_REPO}/test" -img2="${TEST_INSTANCE_REPO}/test-2" -legacy_img="${TEST_INSTANCE_REPO}/legacy-test" -for image in $img $img2 $legacy_img -do - (crane delete $(./cosign triangulate $image)) || true - crane cp busybox $image -done -img_copy="${img}/copy" -crane ls $img_copy | while read tag ; do crane delete "${img_copy}:${tag}" ; done -multiarch_img="${TEST_INSTANCE_REPO}/multiarch-test" -crane ls $multiarch_img | while read tag ; do crane delete "${multiarch_img}:${tag}" ; done -crane cp ghcr.io/distroless/alpine-base $multiarch_img - -# `initialize` -./cosign initialize - -## Generate (also test output redirection -./cosign generate $img > payload1 -./cosign generate --output-file=payload2 $img -diff payload1 payload2 - -## sign/verify -./cosign sign --key ${signing_key} $img -./cosign verify --key ${verification_key} $img - -# copy -./cosign copy $img $img_copy -./cosign verify --key ${verification_key} $img_copy - -# sign recursively -./cosign sign --key ${signing_key} -r $multiarch_img -./cosign verify --key ${verification_key} $multiarch_img # verify image index -for arch in "linux/amd64" "linux/arm64" "linux/s390x" -do - # verify sigs on discrete images - ./cosign verify --key ${verification_key} "${multiarch_img}@$(crane digest --platform=$arch ${multiarch_img})" -done - -## confirm use of OCI media type in signature image -crane manifest $(./cosign triangulate $img) | grep -q "application/vnd.oci.image.config.v1+json" - -## sign/verify multiple images -./cosign sign --key ${signing_key} -a multiple=true $img $img2 -./cosign verify --key ${verification_key} -a multiple=true $img $img2 - -# annotations -if (./cosign verify --key ${verification_key} -a foo=bar $img); then false; fi -./cosign sign --key ${signing_key} -a foo=bar $img -./cosign verify --key ${verification_key} -a foo=bar $img - -if (./cosign verify --key ${verification_key} -a foo=bar -a bar=baz $img); then false; fi -./cosign sign --key ${signing_key} -a foo=bar -a bar=baz $img -./cosign verify --key ${verification_key} -a foo=bar -a bar=baz $img -./cosign verify --key ${verification_key} -a bar=baz $img - -# confirm the use of legacy (Docker) media types -COSIGN_DOCKER_MEDIA_TYPES=1 ./cosign sign --key ${signing_key} $legacy_img -./cosign verify --key ${verification_key} $legacy_img -legacy_manifest=$(crane manifest $(./cosign triangulate $legacy_img)) -echo $legacy_manifest | grep -q "application/vnd.docker.distribution.manifest.v2+json" -echo $legacy_manifest | grep -q "application/vnd.docker.container.image.v1+json" - -# wrong keys -mkdir wrong && pushd wrong -../cosign generate-key-pair -if (../cosign verify --key ${verification_key} $img); then false; fi -popd - -## sign-blob -echo "myblob" > myblob -echo "myblob2" > myblob2 -./cosign sign-blob --key ${signing_key} myblob > myblob.sig -./cosign sign-blob --key ${signing_key} myblob2 > myblob2.sig - -./cosign verify-blob --key ${verification_key} --signature myblob.sig myblob -if (./cosign verify-blob --key ${verification_key} --signature myblob.sig myblob2); then false; fi - -if (./cosign verify-blob --key ${verification_key} --signature myblob2.sig myblob); then false; fi -./cosign verify-blob --key ${verification_key} --signature myblob2.sig myblob2 - -./cosign sign-blob --key ${signing_key} --bundle bundle.sig myblob -./cosign verify-blob --key ${verification_key} --bundle bundle.sig myblob - -## sign and verify multiple blobs -./cosign sign-blob --key ${signing_key} myblob myblob2 > sigs -head -n 1 sigs > car.sig -tail -n 1 sigs > cdr.sig -./cosign verify-blob --key ${verification_key} --signature car.sig myblob -./cosign verify-blob --key ${verification_key} --signature cdr.sig myblob2 - -## upload blob/sget -blobimg="${TEST_INSTANCE_REPO}/blob" -crane ls ${blobimg} | while read tag ; do crane delete "${blobimg}:${tag}" ; done - -# make a random blob -cat /dev/urandom | head -n 10 | base64 > randomblob - -# upload blob and sign it -dgst=$(./cosign upload blob -f randomblob ${blobimg}) -./cosign sign --key ${signing_key} ${dgst} -./cosign verify --key ${verification_key} ${dgst} # For sanity - -# sget w/ signature verification should work via tag or digest -./sget --key ${verification_key} -o verified_randomblob_from_digest $dgst -./sget --key ${verification_key} -o verified_randomblob_from_tag $blobimg - -# sget w/o signature verification should only work for ref by digest -./sget --key ${verification_key} -o randomblob_from_digest $dgst -if (./sget -o randomblob_from_tag $blobimg); then false; fi - -# clean up a bit -crane delete $blobimg || true -crane delete $dgst || true - -# Make sure they're the same -if ( ! cmp -s randomblob verified_randomblob_from_digest ); then false; fi -if ( ! cmp -s randomblob verified_randomblob_from_tag ); then false; fi -if ( ! cmp -s randomblob randomblob_from_digest ); then false; fi - -# TODO: tlog - -## KMS! -TEST_KMS=${TEST_KMS:-gcpkms://projects/projectsigstore/locations/global/keyRings/e2e-test/cryptoKeys/test} -(crane delete $(./cosign triangulate $img)) || true -./cosign generate-key-pair --kms $TEST_KMS -signing_key=$TEST_KMS - -if (./cosign verify --key ${verification_key} $img); then false; fi -./cosign sign --key ${signing_key} $img -./cosign verify --key ${verification_key} $img - -if (./cosign verify -a foo=bar --key ${verification_key} $img); then false; fi -./cosign sign --key ${signing_key} -a foo=bar $img -./cosign verify --key ${verification_key} -a foo=bar $img - -# store signatures in a different repo -export COSIGN_REPOSITORY=${TEST_INSTANCE_REPO}/subbedrepo -(crane delete $(./cosign triangulate $img)) || true -./cosign sign --key ${signing_key} $img -./cosign verify --key ${verification_key} $img -unset COSIGN_REPOSITORY - -# test stdin interaction for private key password -stdin_password=${COSIGN_PASSWORD} -unset COSIGN_PASSWORD -(crane delete $(./cosign triangulate $img)) || true -echo $stdin_password | ./cosign sign --key ${signing_key} --output-signature interactive.sig $img -./cosign verify --key ${verification_key} --signature interactive.sig $img -export COSIGN_PASSWORD=${stdin_password} - -# What else needs auth? -echo "SUCCESS" diff --git a/test/piv_test.go b/test/piv_test.go deleted file mode 100644 index f0876c4b6..000000000 --- a/test/piv_test.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2021 The Sigstore Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build resetyubikey && e2e && !pivkeydisabled -// +build resetyubikey,e2e,!pivkeydisabled - -// DANGER -// This test requires a yubikey to be present. It WILL reset the yubikey to exercise functionality. -// DO NOT RUN THIS TEST IF YOU DO NOT WANT TO RESET YOUR YUBIKEY -// This requires the "resetyubikey" tag to be passed to go test. - -package test - -import ( - "context" - "crypto/x509" - "testing" - - // Import the functions directly for testing. - . "github.com/sigstore/cosign/cmd/cosign/cli/pivcli" -) - -func TestSetManagementKeyCmd(t *testing.T) { - ctx := context.Background() - - Confirm = func(_ string) bool { return true } - must(ResetKeyCmd(ctx), t) - - // The key should be the default management key so this should fail - mustErr(SetManagementKeyCmd(ctx, "foobar", "newkey", false), t) - must(SetManagementKeyCmd(ctx, "", "newkey", false), t) - - // Now it should fail with the wrong key - mustErr(SetManagementKeyCmd(ctx, "", "otherkey", false), t) - // But pass if we use the right key - must(SetManagementKeyCmd(ctx, "newkey", "otherkey", false), t) - - // Reset and try a random key too! - must(ResetKeyCmd(ctx), t) - must(SetManagementKeyCmd(ctx, "", "", true), t) - mustErr(SetManagementKeyCmd(ctx, "", "", true), t) -} - -func TestSetPUKCmd(t *testing.T) { - ctx := context.Background() - - Confirm = func(_ string) bool { return true } - must(ResetKeyCmd(ctx), t) - - // The PUK should be the default key so this should fail - mustErr(SetPukCmd(ctx, "11111111", "12121212"), t) - must(SetPukCmd(ctx, "", "12121212"), t) - - // Now it should fail with the wrong key - mustErr(SetPukCmd(ctx, "", "43214321"), t) - // But pass if we use the right key - must(SetPukCmd(ctx, "12121212", "43214321"), t) -} - -func TestSetPinCmd(t *testing.T) { - ctx := context.Background() - - Confirm = func(_ string) bool { return true } - must(ResetKeyCmd(ctx), t) - - // The PIN should be the default PIN so this should fail - mustErr(SetPinCmd(ctx, "111111", "222222"), t) - must(SetPinCmd(ctx, "", "222222"), t) - - // Now it should fail with the wrong key - mustErr(SetPinCmd(ctx, "333333", "444444"), t) - // But pass if we use the right key - must(SetPinCmd(ctx, "222222", "111111"), t) -} - -func TestUnblockCmd(t *testing.T) { - ctx := context.Background() - - Confirm = func(_ string) bool { return true } - must(ResetKeyCmd(ctx), t) - - // Set a PUK - must(SetPukCmd(ctx, "", "43214321"), t) - // Set the pin to something, then lock the device by trying the wrong one too many times. - must(SetPinCmd(ctx, "", "111111"), t) - - for i := 0; i < 5; i++ { - mustErr(SetPinCmd(ctx, "222222", "333333"), t) - } - - // Now even with the right PIN it should be stuck - mustErr(SetPinCmd(ctx, "111111", "222222"), t) - - // But we can unblock it - must(UnblockCmd(ctx, "43214321", "222222"), t) - must(SetPinCmd(ctx, "222222", "333333"), t) -} - -func TestGenerateKeyCmd(t *testing.T) { - ctx := context.Background() - - Confirm = func(_ string) bool { return true } - must(ResetKeyCmd(ctx), t) - - // This should work with the default key - must(GenerateKeyCmd(ctx, "", false, "", "", ""), t) - - // Set the key to something other than the default - must(SetManagementKeyCmd(ctx, "", "mynewkey", false), t) - // Now this should fail - mustErr(GenerateKeyCmd(ctx, "", false, "", "", ""), t) - // Unless we use the right key - must(GenerateKeyCmd(ctx, "mynewkey", false, "", "", ""), t) - - // Now if we use a random key it should set a new one - must(GenerateKeyCmd(ctx, "mynewkey", true, "", "", ""), t) - // The old one shouldn't work. - mustErr(GenerateKeyCmd(ctx, "mynewkey", false, "", "", ""), t) -} - -func TestAttestationCmd(t *testing.T) { - ctx := context.Background() - - Confirm = func(_ string) bool { return true } - must(ResetKeyCmd(ctx), t) - must(GenerateKeyCmd(ctx, "", false, "", "", ""), t) - - attestations, err := AttestationCmd(ctx, "") - if err != nil { - t.Fatal(err) - } - - root := x509.NewCertPool() - if !root.AppendCertsFromPEM([]byte(yubicoCert)) { - t.Fatal("error adding roots") - } - - // Check the device against the manufacturer - if _, err := attestations.DeviceCert.Verify(x509.VerifyOptions{ - Roots: root, - }); err != nil { - t.Fatal(err) - } - - intermediate := x509.NewCertPool() - intermediate.AddCert(attestations.DeviceCert) - // Now check the key, with the device as a chain - if _, err := attestations.KeyCert.Verify(x509.VerifyOptions{ - Roots: root, - Intermediates: intermediate, - }); err != nil { - // This is known to fail on YubiKey firmware 4.3 - // See https://labanskoller.se/blog/2019/12/30/pki-is-hard-how-yubico-trusted-openssl-and-got-it-wrong/ - // - if attestations.KeyAttestation.Version.Major == 4 && - attestations.KeyAttestation.Version.Minor == 3 { - t.Skipf("key attestation cert chain verification is known to be broken on firmware 4.3") - } else { - t.Fatal(err) - } - - } - -} - -const yubicoCert = `-----BEGIN CERTIFICATE----- -MIIDFzCCAf+gAwIBAgIDBAZHMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNVBAMMIFl1 -YmljbyBQSVYgUm9vdCBDQSBTZXJpYWwgMjYzNzUxMCAXDTE2MDMxNDAwMDAwMFoY -DzIwNTIwNDE3MDAwMDAwWjArMSkwJwYDVQQDDCBZdWJpY28gUElWIFJvb3QgQ0Eg -U2VyaWFsIDI2Mzc1MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMN2 -cMTNR6YCdcTFRxuPy31PabRn5m6pJ+nSE0HRWpoaM8fc8wHC+Tmb98jmNvhWNE2E -ilU85uYKfEFP9d6Q2GmytqBnxZsAa3KqZiCCx2LwQ4iYEOb1llgotVr/whEpdVOq -joU0P5e1j1y7OfwOvky/+AXIN/9Xp0VFlYRk2tQ9GcdYKDmqU+db9iKwpAzid4oH -BVLIhmD3pvkWaRA2H3DA9t7H/HNq5v3OiO1jyLZeKqZoMbPObrxqDg+9fOdShzgf -wCqgT3XVmTeiwvBSTctyi9mHQfYd2DwkaqxRnLbNVyK9zl+DzjSGp9IhVPiVtGet -X02dxhQnGS7K6BO0Qe8CAwEAAaNCMEAwHQYDVR0OBBYEFMpfyvLEojGc6SJf8ez0 -1d8Cv4O/MA8GA1UdEwQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 -DQEBCwUAA4IBAQBc7Ih8Bc1fkC+FyN1fhjWioBCMr3vjneh7MLbA6kSoyWF70N3s -XhbXvT4eRh0hvxqvMZNjPU/VlRn6gLVtoEikDLrYFXN6Hh6Wmyy1GTnspnOvMvz2 -lLKuym9KYdYLDgnj3BeAvzIhVzzYSeU77/Cupofj093OuAswW0jYvXsGTyix6B3d -bW5yWvyS9zNXaqGaUmP3U9/b6DlHdDogMLu3VLpBB9bm5bjaKWWJYgWltCVgUbFq -Fqyi4+JE014cSgR57Jcu3dZiehB6UtAPgad9L5cNvua/IWRmm+ANy3O2LH++Pyl8 -SREzU8onbBsjMg9QDiSf5oJLKvd/Ren+zGY7 ------END CERTIFICATE-----` diff --git a/test/pkcs11_test.go b/test/pkcs11_test.go deleted file mode 100644 index 578757011..000000000 --- a/test/pkcs11_test.go +++ /dev/null @@ -1,545 +0,0 @@ -// Copyright 2021 The Sigstore Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !pkcs11keydisabled && softhsm -// +build !pkcs11keydisabled,softhsm - -// DANGER -// This test requires SoftHSMv2 to be installed. An initialized token should already exist. -// This test will import an RSA key pair, using the specified token label. -// By default, the test assumes the following : -// - The SoftHSMv2 library is located at "/usr/local/lib/softhsm/libsofthsm2.so" -// - The initialized token has the label "My Token" -// - The initialized token has the pin "1234" -// - The test will import the key pair using the key label "My Key" -// These values can be overriden using the following environment variable : -// - SOFTHSM_LIB -// - SOFTHSM_TOKENLABEL -// - SOFTHSM_PIN -// - SOFTHSM_KEYLABEL -// By default, the test makes use of the following SoftHSMv2 configuration files : -// - /etc/softhsm2.conf -// - /etc/softhsm.conf -// These values can be overriden using the following environment variable : -// - SOFTHSM2_CONF -// - SOFTHSM_CONF - -package test - -import ( - "bytes" - "context" - "crypto/rsa" - "crypto/x509" - "encoding/hex" - "encoding/pem" - "fmt" - "math/big" - "os" - "strings" - "testing" - - // Import the functions directly for testing. - - "github.com/miekg/pkcs11" - . "github.com/sigstore/cosign/cmd/cosign/cli/pkcs11cli" - "github.com/sigstore/cosign/pkg/cosign/pkcs11key" - "github.com/stretchr/testify/require" -) - -var ( - modulePath = "/usr/local/lib/softhsm/libsofthsm2.so" - tokenLabel = "My Token" - pin = "1234" - keyLabel = "My Key" - - keyID = "355d2d0b569a2a0169e46b82e172cf99aca41400" - uri = "" -) - -func init() { - if x := os.Getenv("SOFTHSM_LIB"); x != "" { - modulePath = x - } - if x := os.Getenv("SOFTHSM_TOKENLABEL"); x != "" { - tokenLabel = x - } - if x := os.Getenv("SOFTHSM_PIN"); x != "" { - pin = x - } - if x := os.Getenv("SOFTHSM_KEYLABEL"); x != "" { - keyLabel = x - } - if x := os.Getenv("SOFTHSM_CONF"); x == "" { - os.Setenv("SOFTHSM_CONF", "/etc/softhsm.conf") - } - if x := os.Getenv("SOFTHSM2_CONF"); x == "" { - os.Setenv("SOFTHSM2_CONF", "/etc/softhsm2.conf") - } - - keyIDBytes, _ := hex.DecodeString(keyID) - pkcs11UriConfig := pkcs11key.NewPkcs11UriConfigFromInput(modulePath, nil, tokenLabel, []byte(keyLabel), keyIDBytes, pin) - uri, _ = pkcs11UriConfig.Construct() -} - -func TestParsePKCS11URI(t *testing.T) { - _ = context.Background() - - uriString := "pkcs11:" - uriString += "library-manufacturer=manufacturer;library-description=description;library-version=1;" - uriString += "slot-manufacturer=manufacturer;slot-description=description;slot-id=1;" - uriString += "manufacturer=manufacturer;model=model;serial=12345678;token=token%20label;" - uriString += "type=private;object=key%20label;id=%6b%65%79%5f%69%64" - uriString += "?" - uriString += "module-path=/path/to/some%20folder/libmodule.so&module-name=libmodule.so&" - uriString += "pin-value=1234&pin-source=/path/to/pinfile" - - pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() - must(pkcs11UriConfig.Parse(uriString), t) - require.Equal(t, pkcs11UriConfig.KeyID, []byte("key_id")) - require.Equal(t, pkcs11UriConfig.KeyLabel, []byte("key label")) - require.Equal(t, pkcs11UriConfig.ModulePath, "/path/to/some folder/libmodule.so") - require.Equal(t, pkcs11UriConfig.Pin, "1234") - require.Equal(t, *pkcs11UriConfig.SlotID, 1) - require.Equal(t, pkcs11UriConfig.TokenLabel, "token label") -} - -func TestConstructPKCS11URI(t *testing.T) { - _ = context.Background() - - uri := "pkcs11:token=token%20label;slot-id=1;id=%6b%65%79%5f%69%64;object=key%20label" - uri += "?" - uri += "module-path=/path/to/some%20folder/libmodule.so&pin-value=1234" - - slotID := 1 - pkcs11UriConfig := pkcs11key.NewPkcs11UriConfigFromInput("/path/to/some folder/libmodule.so", &slotID, "token label", []byte("key label"), []byte("key_id"), "1234") - uriString, err := pkcs11UriConfig.Construct() - require.NoError(t, err) - require.Equal(t, uri, uriString) -} - -func TestListTokensCmd(t *testing.T) { - ctx := context.Background() - - tokens, err := GetTokens(ctx, modulePath) - if err != nil { - t.Fatal(err) - } - - bTokenFound := false - for _, token := range tokens { - if token.TokenInfo.Label == tokenLabel { - bTokenFound = true - break - } - } - - if !bTokenFound { - t.Fatalf("token with label '%s' not found", tokenLabel) - } -} - -func TestListKeysUrisCmd(t *testing.T) { - ctx := context.Background() - - tokens, err := GetTokens(ctx, modulePath) - if err != nil { - t.Fatal(err) - } - - bTokenFound := false - var slotID uint - for _, token := range tokens { - if token.TokenInfo.Label == tokenLabel { - bTokenFound = true - slotID = token.Slot - break - } - } - if !bTokenFound { - t.Fatalf("token with label '%s' not found", tokenLabel) - } - - err = importKey(slotID) - if err != nil { - t.Fatal(err) - } - defer deleteKey(slotID) - - keysInfo, err := GetKeysInfo(ctx, modulePath, slotID, pin) - if err != nil { - t.Fatal(err) - } - - bKeyFound := false - for _, keyInfo := range keysInfo { - if hex.EncodeToString(keyInfo.KeyID) == keyID && string(keyInfo.KeyLabel) == keyLabel { - foundUriConfig := pkcs11key.NewPkcs11UriConfig() - err = foundUriConfig.Parse(keyInfo.KeyURI) - if err != nil { - t.Fatal(err) - } - - uriConfig := pkcs11key.NewPkcs11UriConfig() - err = uriConfig.Parse(uri) - if err != nil { - t.Fatal(err) - } - - if foundUriConfig.TokenLabel == uriConfig.TokenLabel && - bytes.Compare(foundUriConfig.KeyID, uriConfig.KeyID) == 0 && - bytes.Compare(foundUriConfig.KeyLabel, uriConfig.KeyLabel) == 0 && - foundUriConfig.ModulePath == uriConfig.ModulePath && - foundUriConfig.Pin == uriConfig.Pin { - bKeyFound = true - } - - break - } - } - - if !bKeyFound { - t.Fatalf("key not found") - } -} - -func TestSignAndVerify(t *testing.T) { - ctx := context.Background() - - tokens, err := GetTokens(ctx, modulePath) - if err != nil { - t.Fatal(err) - } - - bTokenFound := false - var slotID uint - for _, token := range tokens { - if token.TokenInfo.Label == tokenLabel { - bTokenFound = true - slotID = token.Slot - break - } - } - if !bTokenFound { - t.Fatalf("token with label '%s' not found", tokenLabel) - } - - err = importKey(slotID) - if err != nil { - t.Fatal(err) - } - defer deleteKey(slotID) - - pkcs11UriConfig := pkcs11key.NewPkcs11UriConfig() - err = pkcs11UriConfig.Parse(uri) - if err != nil { - t.Fatal(err) - } - - sk, err := pkcs11key.GetKeyWithURIConfig(pkcs11UriConfig, true) - if err != nil { - t.Fatal(err) - } - defer sk.Close() - - sv, err := sk.SignerVerifier() - if err != nil { - t.Fatal(err) - } - - v, err := sk.Verifier() - if err != nil { - t.Fatal(err) - } - - sig, err := sv.SignMessage(bytes.NewReader([]byte("hello, world!"))) - if err != nil { - t.Fatal(err) - } - - err = v.VerifySignature(bytes.NewReader(sig), bytes.NewReader([]byte("hello, world!"))) - if err != nil { - t.Fatal(err) - } -} - -var newPublicKeyAttrs = []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), - pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), - pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, false), - pkcs11.NewAttribute(pkcs11.CKA_VERIFY, true), -} - -var newPrivateKeyAttrs = []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY), - pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true), - pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, true), - pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, true), - pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, false), - pkcs11.NewAttribute(pkcs11.CKA_SIGN, true), -} - -func rsaImportAttrs(priv *rsa.PrivateKey) (pubAttrs, privAttrs []*pkcs11.Attribute) { - pubAttrs = []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, big.NewInt(int64(priv.E)).Bytes()), - pkcs11.NewAttribute(pkcs11.CKA_MODULUS, priv.N.Bytes()), - } - privAttrs = []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, big.NewInt(int64(priv.E)).Bytes()), - pkcs11.NewAttribute(pkcs11.CKA_MODULUS, priv.N.Bytes()), - pkcs11.NewAttribute(pkcs11.CKA_PRIVATE_EXPONENT, priv.D.Bytes()), - pkcs11.NewAttribute(pkcs11.CKA_PRIME_1, priv.Primes[0].Bytes()), - pkcs11.NewAttribute(pkcs11.CKA_PRIME_2, priv.Primes[1].Bytes()), - pkcs11.NewAttribute(pkcs11.CKA_EXPONENT_1, priv.Precomputed.Dp.Bytes()), - pkcs11.NewAttribute(pkcs11.CKA_EXPONENT_2, priv.Precomputed.Dq.Bytes()), - pkcs11.NewAttribute(pkcs11.CKA_COEFFICIENT, priv.Precomputed.Qinv.Bytes()), - } - return -} - -func attrConcat(attrSets ...[]*pkcs11.Attribute) []*pkcs11.Attribute { - ret := make([]*pkcs11.Attribute, 0) - for _, attrs := range attrSets { - ret = append(ret, attrs...) - } - return ret -} - -func initPKCS11(modulePath string) (*pkcs11.Ctx, error) { - ctx := pkcs11.New(modulePath) - if ctx == nil { - return nil, fmt.Errorf("unable to load PKCS#11 module") - } - - err := ctx.Initialize() - if err != nil { - return nil, fmt.Errorf("unable to initialize PKCS#11 module") - } - - return ctx, nil -} - -func importKey(slotID uint) error { - var pemBytes []byte - var priv interface{} - - ctx, err := initPKCS11(modulePath) - if err != nil { - return err - } - defer func() { - ctx.Finalize() - ctx.Destroy() - }() - - keyIDBytes, err := hex.DecodeString(keyID) - if err != nil { - return err - } - keyLabelBytes := []byte(keyLabel) - - r := strings.NewReader(rsaPrivKey) - pemBytes, err = os.ReadAll(r) - if err != nil { - return fmt.Errorf("unable to read pem") - } - block, _ := pem.Decode(pemBytes) - if block == nil { - return fmt.Errorf("unable to decode pem") - } - priv, err = x509.ParsePKCS8PrivateKey(block.Bytes) - if err != nil { - return fmt.Errorf("unable to parse pem") - } - privKey, ok := priv.(*rsa.PrivateKey) - if !ok { - return fmt.Errorf("unable to load key") - } - - session, err := ctx.OpenSession(slotID, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) - if err != nil { - return fmt.Errorf("unable to open session") - } - defer ctx.CloseSession(session) - err = ctx.Login(session, pkcs11.CKU_USER, pin) - if err != nil { - return fmt.Errorf("unable to login") - } - defer ctx.Logout(session) - - keyType := pkcs11.CKK_RSA - pubTypeAttrs, privTypeAttrs := rsaImportAttrs(privKey) - commonAttrs := []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, keyType), - pkcs11.NewAttribute(pkcs11.CKA_ID, keyIDBytes), - pkcs11.NewAttribute(pkcs11.CKA_LABEL, keyLabelBytes), - } - pubAttrs := attrConcat(commonAttrs, newPublicKeyAttrs, pubTypeAttrs) - privAttrs := attrConcat(commonAttrs, newPrivateKeyAttrs, privTypeAttrs) - pubHandle, err := ctx.CreateObject(session, pubAttrs) - if err != nil { - return fmt.Errorf("unable to create public key") - } - _, err = ctx.CreateObject(session, privAttrs) - if err != nil { - ctx.DestroyObject(session, pubHandle) - return fmt.Errorf("unable to create private key") - } - - return nil -} - -func deleteKey(slotID uint) error { - var handles []pkcs11.ObjectHandle - - ctx, err := initPKCS11(modulePath) - if err != nil { - return err - } - defer func() { - ctx.Finalize() - ctx.Destroy() - }() - - keyIDBytes, err := hex.DecodeString(keyID) - if err != nil { - return err - } - keyLabelBytes := []byte(keyLabel) - - session, err := ctx.OpenSession(slotID, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION) - if err != nil { - return fmt.Errorf("unable to open session") - } - defer ctx.CloseSession(session) - err = ctx.Login(session, pkcs11.CKU_USER, pin) - if err != nil { - return fmt.Errorf("unable to login") - } - defer ctx.Logout(session) - - maxHandlePerFind := 20 - publicAttrs := []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY), - pkcs11.NewAttribute(pkcs11.CKA_ID, keyIDBytes), - pkcs11.NewAttribute(pkcs11.CKA_LABEL, keyLabelBytes), - } - if err = ctx.FindObjectsInit(session, publicAttrs); err != nil { - return fmt.Errorf("unable to initialize find objects") - } - handles, _, err = ctx.FindObjects(session, maxHandlePerFind) - if err != nil { - return fmt.Errorf("unable to find objects") - } - err = ctx.FindObjectsFinal(session) - if err != nil { - return fmt.Errorf("unable to finalize find objects") - } - if len(handles) == 1 { - ctx.DestroyObject(session, handles[0]) - if err != nil { - return fmt.Errorf("unable to destroy public key") - } - } - - privAttrs := []*pkcs11.Attribute{ - pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY), - pkcs11.NewAttribute(pkcs11.CKA_ID, keyIDBytes), - pkcs11.NewAttribute(pkcs11.CKA_LABEL, keyLabelBytes), - } - if err = ctx.FindObjectsInit(session, privAttrs); err != nil { - return fmt.Errorf("unable to initialize find objects") - } - handles, _, err = ctx.FindObjects(session, maxHandlePerFind) - if err != nil { - return fmt.Errorf("unable to find objects") - } - err = ctx.FindObjectsFinal(session) - if err != nil { - return fmt.Errorf("unable to finalize find objects") - } - if len(handles) == 1 { - ctx.DestroyObject(session, handles[0]) - if err != nil { - return fmt.Errorf("unable to destroy private key") - } - } - - return nil -} - -func must(err error, t *testing.T) { - t.Helper() - if err != nil { - t.Fatal(err) - } -} - -func mustErr(err error, t *testing.T) { - t.Helper() - if err == nil { - t.Fatal("expected error") - } -} - -const rsaPrivKey = `-----BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDZJZ44vB04D2wm -xz+3upmuWelrTWcceVC2v6fkBo9dIR9IejolFY+CsMF1Rc5LGXG3XStQHRrbmq1w -UxC8jsIOK7gI2xI9IOwCgyaQun3J+1VQc6eZxHLGQfTTNq7Vx67VOG8V8d3RhN7L -BvAMT5U55254bUgH0KVx5C1ybLcX6BdGaABCunh7tV+thgwEZSbr2/t0Tf8QneMr -eHojTKZp7/d90TH8KF+/FiPWJWWv5OVhjpZPwTWqUgL+6pgrMKUSWD/92JSDIZe6 -UjxASE4JgnJWMQUhkerJ7j5P16gjdAwJAAt5m3L6wdfVQG2aZ9CJzUowk12ly4Dc -Dx73/UufAgMBAAECggEAF8iA/eHMqXk29UBZgDwV3PzIDhKaOoonBv0S3GzDgwW/ -sWaBu9ISt9O4PKn6oEsXI2g2+D1X1bmpSWYvrRdNtdOgAohMBRn3/4Zx0OQ8JsU6 -YOdp8fOMRp6uu/t/RrbqNTxLHnIxQ2N0K3SFEjQdOgxZEyOVAhYeKM0/FQtHOnzj -WoyZHT8pV3mr6WnxBw/4u/1Ahfau7fs6aVJLECc9jGF/6e7aQeb+yEeLrHayml8e -sbBx4l/1LqU/2S7SQrWtQ+fi+/MlgxvLh0XC7tTPP6I3cTetyMZime9EwwDiPebX -PLUgo8Kf/sHzd/25G9M3Yz+UCLemcPSMUjBUQTPtYQKBgQD8lnpjekyeOjNCdRVP -5w6h1wGN4aC4bCksZ89HKpHc44+3AjDT/aVviory+CyOj05qbXDdpNnNh+jl5llM -yDw15WIvSsXFx3UQ467VVrBKm7vr+k1LGgLJ2fSFbZUTyLvwW4NpP26KDW6SitZ8 -B9lkepTZ0G4Eao51VgidHsulKQKBgQDcFJNAIctqUWDli4tA5L0G5tiypcAA7iIZ -0h2YK+7eOU2f3r8aaywbPhcRn+cKlrf3iV4BCZAv59WEJqq1HOlzU92jkmZspYPq -8kSZLaaiDIBw+vwV4prHDSdZFEY+hHq5eULPIgVm/M474JcghetkVt8pG3ee+Dml -o6zUrZr7hwKBgQDCiXbrpObbuoF+PsTSTGfFl923k74ALDWt4KoQ6qV61bz7O3G1 -5BYFiVOo/CD9Dzxa1b1mx6+ED5f9cOL4MwPEks2DFPircgoknucpomGWpMkgXyAm -pnrdUcN0/Egj+6db4G+eoN8W7m9p6Ap3bmgtbge0lkYVmqfrkP6DXJOFuQKBgHA/ -hkMFeYyGaRdqruGwSMEGaKvlYiKXUok8459DeReavn61y16cHujeKEHy/pImATqd -s3Zv/DyS0BIQ7qxlTKRnt/m/p8HuQXRJkLdX009/dNsrB/vZkfvIN7N1ZcZpJ3cF -5A9lWMAIXN+pUythYofQzw1WVxKbpDtZWcM3sH5tAoGBAMHgZdtmIyllx/1BbYSg -Emxj3LekvZL0e7afeod9f977ZETt/imaejnJNnGOPeSbtLSPfhwonLEp+5XmICzt -lJZAF8iP2m1n9h8sZga5rZQ0JgiwVNFNwde4sp1pD5UcFrYepHRxKPo50eJi3rhR -SwNAKWa96qm5o8BaQu/aRMRu ------END PRIVATE KEY-----` - -const rsaCert = `-----BEGIN CERTIFICATE----- -MIIDazCCAlOgAwIBAgIUL7BdF7HSUwEAdqElJjVLQYd2OekwDQYJKoZIhvcNAQEL -BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM -GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTExMDIxNzM3MzJaFw0zMTEw -MzExNzM3MzJaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw -HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQDZJZ44vB04D2wmxz+3upmuWelrTWcceVC2v6fkBo9d -IR9IejolFY+CsMF1Rc5LGXG3XStQHRrbmq1wUxC8jsIOK7gI2xI9IOwCgyaQun3J -+1VQc6eZxHLGQfTTNq7Vx67VOG8V8d3RhN7LBvAMT5U55254bUgH0KVx5C1ybLcX -6BdGaABCunh7tV+thgwEZSbr2/t0Tf8QneMreHojTKZp7/d90TH8KF+/FiPWJWWv -5OVhjpZPwTWqUgL+6pgrMKUSWD/92JSDIZe6UjxASE4JgnJWMQUhkerJ7j5P16gj -dAwJAAt5m3L6wdfVQG2aZ9CJzUowk12ly4DcDx73/UufAgMBAAGjUzBRMB0GA1Ud -DgQWBBRokgD44sdsSGQEQcbJ3vrCrXTIcTAfBgNVHSMEGDAWgBRokgD44sdsSGQE -QcbJ3vrCrXTIcTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA8 -+CpbIGi4ycCcSeomBzGVXsTFgFutqqvh3BFQ1u6bPlV7hIlFd11zzgWBeKKxREJn -z3SipT1qGX+uP4iVhUux94f2rQCV25mJNRKft2phAUylMr+laiO7IkHFB1zzJTfz -Bi9gm55HGvGCIdSWFkLZ/MUNCMj3WtPrUYl5jqFgDDmCpLctmPoN4vxSa0of3apv -ILH8jSsN5XbL8G1hsT/IGlRRbzoiLCKgCp6e6TjZSq/Y+JWGyw/+sZJMI8Mg4Mje -054uJhD29xmbfxdYxrMWLAFb6yoWVbDJPdECFf9uwOXyDZ8bGd48frTdUU3Rb+m3 -5Hue2g5US98p2jnJiv75 ------END CERTIFICATE-----` diff --git a/test/sign_blob_test.sh b/test/sign_blob_test.sh deleted file mode 100755 index 5698bd915..000000000 --- a/test/sign_blob_test.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright 2021 The Sigstore Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This test checks that verify-blob will iterate over all entries and check for at least one valid entry before erroring out -# This is to prevent verify-blob from only checking the most recent entry, which could result -# in a "denial of service" type attack if someone signs a piece of software -# with their own certificate which doesn't chain up to Sigstore - -set -ex - -export COSIGN_EXPERIMENTAL=1 -COSIGN_CLI=./cosign - -echo "Creating a unique blob" -BLOB=verify-experimental-blob -date > $BLOB -cat $BLOB - -echo "Sign the blob with cosign first and upload to rekor" -SIG=$($COSIGN_CLI sign-blob $BLOB) - -echo "Verifying ..." -$COSIGN_CLI verify-blob -signature $SIG $BLOB - -# Now, sign the blob with a self-signed certificate and upload to rekor -SIG_FILE=verify-experimental-signature -PRIV_KEY=./test/testdata/test_blob_private_key -PUB_KEY=./test/testdata/test_blob_public_key -CERT_FILE=./test/testdata/test_blob_cert.pem - -openssl dgst -sha256 -sign $PRIV_KEY -out $SIG_FILE $BLOB -openssl dgst -sha256 -verify $PUB_KEY -signature $SIG_FILE $BLOB - -SHA256HASH=$(sha256sum $BLOB | cut -f1 -d' ') - -SIGNATURE=$(cat $SIG_FILE | base64) -echo "Signature: $SIGNATURE" - -CERT=$(cat $CERT_FILE | base64) -echo "Cert: $CERT" - -JSON_BODY_FILE=verify-experimental-blob-http-body.json -cat < $JSON_BODY_FILE -{ - "apiVersion": "0.0.1", - "spec": { - "data": { - "hash": { - "algorithm": "sha256", - "value": "$SHA256HASH" - } - }, - "signature": { - "content": "$SIGNATURE", - "publicKey": { - "content": "$CERT" - } - } - }, - "kind": "hashedrekord" -} -EOF - -curl -X POST https://rekor.sigstore.dev/api/v1/log/entries -H 'Content-Type: application/json' -d @$JSON_BODY_FILE - -# Verifying should still work -echo "Verifying ..." -$COSIGN_CLI verify-blob --signature $SIG $BLOB